找回密码
 立即注册
欢迎中测联盟老会员回家,1997年注册的域名
查看: 1507|回复: 0
打印 上一主题 下一主题

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
( i! N# w! Z% r* R% P2 w; j6 b, D

' Z- Z; {+ A0 k. |5 e/ [

& Z0 M7 x! R* A% q @

! S$ h \" j% C- X 前言 + K# G# y* z& T$ i. D0 Q/ z

5 K/ y! Z* }8 @0 f* `9 I

# |2 R% S0 O5 J$ p1 o7 C 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。6 R+ a0 K3 Q! V o

+ L$ A/ }- R6 \' D; w; b

. U& g* I1 l& ^9 m   * C. `+ M8 N& z: D( Z0 n' f w

6 K' b* _/ z+ U3 P; g0 w

% k' I2 u" p* W) Z* v" T+ ` 漏洞分析# r1 j( ?1 @: i: m; k$ I3 e! Y

4 [2 Y+ g( Y/ G9 A

: C) z+ i+ x: n) f9 j) S) D% P 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: * U* ]0 p3 q7 _3 p7 i$ z) m

. b% C$ r# R8 z+ s3 N4 e7 b& i

: }0 @: g/ o0 p9 z5 h" ?1 s: i   + [$ u. l, q! ^) f* M

, Q$ y: w } c! i

( M8 Z$ l+ Z8 r# E/ ]$ F0 o' H 对应着avatar.inc.php代码如下: / `3 R% H% C' p$ h3 h1 l* t/ O: Y

) ~. a7 k9 C9 i+ g/ M: D2 H

7 G! @! t Q) l" I8 Q+ O <?php defined('IN_DESTOON') or exit('Access Denied');login();require DT_ROOT.'/module/'.$module.'/common.inc.php';require DT_ROOT.'/include/post.func.php';$avatar = useravatar($_userid, 'large', 0, 2);switch($action) { ( L5 f" |4 {1 v! w3 t) F" F- g

. ^- O, @+ M F6 I X: a: c

/ M: g4 Y* d* J7 B     case 'upload':% B' {3 Z. B6 F* j/ e" C- C

) C# l; K7 q) N5 `% T9 t

* }% W0 Y5 G$ N9 [/ Y0 D4 O3 @         if(!$_FILES['file']['size']) { / R# o0 [+ s8 H/ P4 _, s/ {

( K( Z7 U9 V- H6 j% ?7 M7 A# @

8 w; H/ n: c+ i+ F, X( _" d$ U             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); * |( v+ d5 q$ J2 g7 C

8 m1 J( ~1 t2 O! v' V

- r" c3 T/ ~# o; |6 t             exit('{"error":1,"message":"Error FILE"}');4 N9 d5 ^7 c7 u3 e4 ~! V8 I# d

/ a* n& N. b8 E& l* F& n' V

5 b/ k- ?+ e& t5 H' c) D4 @         }- A0 ?7 _) ~1 E( J

1 \3 V: f, y. u$ C, s+ j, _

% d0 h: `+ h" O3 U% m         require DT_ROOT.'/include/upload.class.php';$ n; g& y/ [+ X# H

; V8 m- [% ?' e( Y

( s e2 ^; B/ I7 G* {9 r7 z u C: H   ; m9 `& l. D+ w/ N

. U+ I; }* {" E. k: U* s

- X+ ^# e0 Q. ]3 K         $ext = file_ext($_FILES['file']['name']); 6 L, o X( a' Y

4 p* C( d& U Z. \" C7 ?6 ^" Q2 I$ f

; C/ J* E& d; Y1 G         $name = 'avatar'.$_userid.'.'.$ext; 8 }6 F- g) A, {5 @6 Y

, m; z# M$ M( u X9 @6 l0 v, |

, j4 Z% l6 s+ o2 t         $file = DT_ROOT.'/file/temp/'.$name; ~3 r, t) v/ u* b

& G" L7 b4 H1 U' O! |& Z

$ k s% y+ ` T% T8 Z% k   " T( X" m d3 e) o0 d

) Q7 { u( I3 g* ?4 C) ?8 d

9 @8 ^2 b# A7 o: P- M" a) }/ ?         if(is_file($file)) file_del($file);1 r* L% l# V: n& E; W' W$ j

- u, a" Z& ^ O3 _* J3 A

0 a9 ]% A! x" {5 l         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');& Z* f7 x5 M8 L4 b8 o

: P5 y& | t: `5 u* t

* V+ V y3 h, ]( b3 e  , S4 B* O0 \4 g* o. y5 ?: s

/ V. W! v+ X% m% [$ a' T0 l

% u* `5 S+ D* m; D. g- T5 n( q N         $upload->adduserid = false; - ~3 Z/ v1 T; V) G, e5 d

" p; |' f. e5 }& X7 }

( g1 v d! B g% g   2 H2 M2 C* I5 j9 I6 q

3 e2 T5 U* U$ {8 j' l% h

% R- F) A( f1 K0 j9 S, _         if($upload->save()) { ; m- O2 n% d3 E3 l" ~$ p0 s

" \+ W4 T/ X* z1 J

: u3 M( j0 F5 l             ...- B' B, E$ ~: h* b

/ g8 m/ G1 Z/ t# Z# N

9 I$ e6 r! g% t8 F         } else {* a' [( c5 h& ?4 T

& t6 z: e; ]: J$ O

4 Q* o' F0 q5 Q% I3 P7 A% c             .... Q% _# v9 `) `9 N* Y

+ _8 H3 D, E5 I! Y7 y7 A3 m

0 A+ X8 \2 r, G0 a* H. T         } ! C( J) L# \0 i+ Y, j* |/ Y

Q* {) h# F, h: t

* O& R0 N# h# v7 v+ Y     break;4 q- | H; K2 ^. g" j, V

/ p3 {* Q0 ^+ L1 o+ ]5 [

1 Q5 [& M" r: ~ 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。' x9 W1 |$ x1 r- o8 X( ^; l

2 q5 U7 Z1 ~7 k! k" v

: T4 K4 u E% \ upload对象构造函数如下,include/upload.class.php:25:' H; n& M9 W5 _+ |2 a- }# m

2 S) K) Q% V# S4 l" w) L

* b2 e9 I4 z; b' X/ t8 B1 }& v! U <?phpclass upload { 9 ?" Q3 E- g: h$ l2 C

& O, Y/ X6 f, c. i- ~6 r

$ K8 `& |) c9 x- Q     function __construct($_file, $savepath, $savename = '', $fileformat = '') {, w; S! K/ I' I: Y# T3 ?

9 O# m# ]4 P. }2 l- M

! R I/ r1 z" |8 r8 x# [         global $DT, $_userid; ' _1 y$ x3 m% m1 |

" `: c# {4 Z6 G" x* k5 D; z

4 c0 [- E j% D9 K         foreach($_file as $file) { 6 m, A- l3 K) `9 D$ f

$ c# j# \" X" R' `

& w$ a0 p+ V# ]5 z/ n; i1 r             $this->file = $file['tmp_name']; 4 C- X& k7 _7 M. \

5 v% z% i8 E3 A7 {" K$ B

: }" m% w5 `6 t: Z+ R4 I             $this->file_name = $file['name'];7 y( g7 R9 V$ ?0 K! l2 Y7 A

0 h" i8 ]& e8 }4 T$ p; b

( _0 m2 L- Q1 }; F             $this->file_size = $file['size'];$ \- \$ ]* p+ G

* y9 h0 ]) W; X3 e7 J( v o

" C5 r/ [) u! @. t: R2 a" w             $this->file_type = $file['type']; [# @! P! Y8 G

2 `5 y% t4 v3 |) c# I; X5 o h

* s" ^4 p! n* v             $this->file_error = $file['error'];7 ~8 A4 A% N$ T1 B# P, [$ O: U

% }- M# x- B# j) R- R, F

' C0 p, e% S# J4 ~, q+ \0 m2 @/ H) u! |   h9 x0 t% n# U

( ^/ B# n7 Y; j( P+ t$ K+ ?

2 n" E% x4 r/ s4 d! R# Z5 E         }' v0 x4 u" b( n! ^9 N; B% Z- K

/ E! O- z" G: ~( D Y ^ q

8 L* K4 K' s L+ `6 U$ }         $this->userid = $_userid;! {! h) z m! ^8 A; g; D d1 i

* k( R( j2 D/ m* X

$ Q4 ^$ Y4 h0 t+ ^% c9 V         $this->ext = file_ext($this->file_name); $ ^, I) D5 j# c! ], t4 i! \1 b% v6 A

, S& N/ j- V' A2 F

& d! @- Z, Z: p" E& }; l( }* W         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];8 y) g* B! K; F7 W6 Q

7 d+ Y, i: ~* P5 R, q {

+ s, M2 y) {- q8 A T         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;# r9 E3 D& ?' o

& J- \- ~' ~$ c9 x; q5 n" e" L

" z, z9 q# F! A4 @* a1 Q, H5 W4 R+ B         $this->savepath = $savepath; 4 |! U& V8 n+ j4 a+ v W9 A

/ @* u e8 x+ u

9 e. i [) E/ U* Q$ y8 q         $this->savename = $savename; $ f3 B( B% Q7 S' ?# {$ D

% g6 c; e" ^. C0 J O/ t7 _3 V# f

3 _5 V1 L0 W+ S1 ?& ]     }} ) @7 x; H) w: \% R X3 ?; \- v

|4 k# t2 L! j" c; n( w+ @9 V

$ A5 m% I6 m9 U. g7 a& c0 G: v$ K6 l 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。0 k. `2 u- A8 i7 d

& _. M: V2 ]* T4 _. N

9 M. b) s# K. {$ c% n: X! a 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 . h4 n& n4 f0 F% q: Q* V6 g

6 Y$ g2 q( j' p

- z: r8 g: c: @6 W $ext = file_ext($_FILES['file']['name']); // `$ext`即为`php` $name = 'avatar'.$_userid.'.'.$ext; // $name 为 'avatar'.$_userid.'.'php'$file = DT_ROOT.'/file/temp/'.$name; // $file 即为 xx/xx/xx/xx.php) E" i' u! i* w/ z) W: c

* H. x6 u6 g5 C5 J6 X

, q& Z: i+ I; y7 B! n3 Y 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: - k Y" J( B0 W" C3 q2 }, W i$ o

0 [ c, O( [0 j" q5 G

: A2 N4 _- \' U: U) X: {  3 ?9 ?8 D/ E+ k, I( r# h

+ a$ R3 U) U/ Q, D# G9 x

( d& ~# {9 l* O, q# V9 |. f 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:* M" Q" {7 W- u

( l6 z6 |" z4 [- j4 }

+ ?5 _% H+ c5 f# ]/ O' d- p; S <?phpclass upload { f% ~0 J4 t# x% }! Z3 i% G' V

$ ~* c+ K6 ~/ T

; X2 F7 W7 @( |/ ]5 e* n7 ?4 Q     function save() {6 n5 O R$ j3 y; \# h2 I: c9 p

6 t! d. O( K& {

; C# C1 C6 s! O9 K         include load('include.lang');: K) `; Z( X" B1 t

! z: }2 b0 O# }3 |

; L4 E$ g* m4 V3 ] B& u5 l         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');2 E. k! x: B, b% l1 G

% T' C6 p+ |# P# k- }

, u @7 @9 h, C0 v   * Q' y8 M; G& B/ j3 J( @3 t

) @) ~( _1 U6 M6 x6 t/ p [

, ?/ r2 T2 g( Y* @) G         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');, Y% W' u9 k, \) Z; q

9 ?/ \7 h0 {: g i

$ @- ]( U9 o: u6 P; y8 f# V0 e   # m; A7 h$ W" b0 Y3 J

/ Z/ U5 b& a* x8 P, @( w1 p& j

* [8 S# _2 L d. W         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); 6 ]& P5 ]6 c# d ~$ y% ]8 y& j5 O

2 k( N1 f9 V* N; g

5 S9 P) ~2 [( N- N3 k   % C: g2 R ]- q7 U' M+ G% C4 h

* A1 T; [# N+ w4 L

/ O) m% u0 V2 F4 `$ f; y" ~         $this->set_savepath($this->savepath);) p3 I; f W, a* w. r

0 t: [- M* N. \$ O: s2 b+ I

. e8 y" G; R* N! v3 Z$ R* P         $this->set_savename($this->savename);+ z" C% E5 Z+ P, E1 a3 ]% l

& o9 x$ v r0 V4 |8 M

! ^( {- o/ R7 K4 T6 ]# g   ! @9 {. |- q0 M( ?

5 M4 g! E9 m) k6 |" R

% Q; g+ u$ H( y: g         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);8 F. d) h6 s- m% ]

" L1 l- z4 ~! T$ b

. A' y% `8 Z4 @- b         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); 1 d$ _: Y* ^, e

2 X* b5 z7 W2 a2 s! U. }

4 a% P0 X$ J9 Y, g/ T$ D0 f         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); " u( @4 q! o7 x7 T2 `& d9 y0 h2 s4 L

1 W4 g' x1 }+ n+ [* p+ r

# |4 r7 c6 h& S7 {, k5 u  $ J4 x) r, \" r2 |' c

; f6 v A) }1 i/ V: [

3 e# z1 y3 O: H! v+ I         $this->image = $this->is_image(); + K1 j; }2 v4 C$ i1 ~: q& z$ q' p! m; b

7 B4 k0 H$ O+ r

& x# O2 h: Q: O         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); 5 T% d& u8 H. ?6 e8 J# {

; s" r2 f. Y+ a% p1 I

+ E' w4 _! \ h         return true;1 @4 j( Z5 x; [

: f& O1 X2 d7 o9 Z' m3 h7 E3 Y0 |: `1 N

: H* r! @8 e0 s0 \. Q( G     }}1 P. P; P& y- b5 E3 z8 y9 ]

% ?( d3 l. W3 I

1 j& g/ O. j7 k, N1 `% q 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:; C/ D0 `3 E2 \6 n* W9 l; U

5 r3 K2 m# P# e9 J/ a0 M, {

4 w1 c$ ]- R6 E' G+ } <?php# J8 x/ f, U' z, @) Q

' L; d: D/ `) [' Q1 ~

[ k& j7 [. t3 V$ C& H' h3 d) M     function is_allow() { & {" ?7 c' o- Y

M! p" [3 t& X$ p/ W* D3 y

( F+ r6 d) L* n+ W) r; M1 u( P         if(!$this->fileformat) return false; 6 M7 [8 A7 r0 g: b* q* N

4 a1 {8 P: U: U& @6 v$ ^

# O1 t) G: w, `3 y* [: U         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; 2 w) Q& }8 \2 ~- z4 ]' ], }. k4 x

2 N8 \; q0 q7 F- y

: |$ Q5 @- U+ d         if(preg_match("/^(php|phtml|php3|php4|jsp|exe|dll|cer|shtml|shtm|asp|asa|aspx|asax|ashx|cgi|fcgi|pl)$/i", $this->ext)) return false; * K- z$ I: t; q: `: s7 t

6 P+ @/ D/ h0 X, b6 ^/ b$ a

, h+ K: t, L+ J2 b% P, t: f% W- C         return true; , t) ?4 g7 q3 l( `8 r5 T, ?2 T* u

/ M; P+ |: A8 W" I6 U

* E* J0 l& Z5 Z( ]% w+ {$ f' x1 |( Z     }( @2 @) J3 D7 g, w

/ B0 u/ M- x3 e! H

% V% b1 j7 Z0 L* J- [ 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 + ]2 D. G* {6 U& C9 h

9 n) G7 q" `/ ?7 W

- c/ B' e; v! w9 w( m 接着会进行真正的保存。通过$this->set_savepath($this->savepath); $this->set_savename($this->savename);设置了$this->saveto,然后通过move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)将file保存到$this->saveto ,注意此时的savepath、savename、saveto均以php为后缀,而$this->file实际指的是第二个jpg文件。 ; E% {0 I. M: b( i! l! [. |

6 q3 [6 C+ f: M( C8 m/ m

" m" ^( j9 u6 [ 漏洞利用 / E3 a. n {3 V9 c, Y9 p, w

$ y2 L+ x$ [9 y+ O( n

7 B6 W4 y; w0 v1 {4 A5 W 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。6 K7 j4 b# F! b7 f* \- {

2 R: p7 b \! G4 b. [

! f9 J( g. D/ Y; n! I/ o: W" }   H2 _9 A$ p( g4 b6 d9 ^+ E

4 @7 K% ?* @3 z% g: p" v o' h

4 N) ~$ o/ Z' d8 n& J6 e- C, O8 y 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid 4 @7 `& R; d7 a! \0 U0 M6 h& o

; z( w! I% a% z2 E) v% }* N

; X' J; c: l& s U# h 不过实际利用上会有一定的限制。8 l; [0 M5 o+ h; ^; l! D H8 ~

( I1 O, J* G2 P' p" M$ ?! h P

- p' R) p0 d( G 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。. A, W/ ?: x" \* P

; {( f! e7 k2 t+ m. h' v

( b; z- o6 Q& M% G! s0 d0 Y* ]  $ _1 B* N: F) C. k! c. T4 A9 Y

; U0 Y1 V$ n3 e4 I. t6 {. ^+ z2 O

2 u& F1 I& ]. @8 O3 n/ F1 C 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: 3 ?8 N8 V. p( R

$ O. ]! Z; g, x* e* Y

5 l- n1 E4 i8 k# N+ h1 c! b 省略...$img = array();$img[1] = $dir.'.jpg';$img[2] = $dir.'x48.jpg';$img[3] = $dir.'x20.jpg';$md5 = md5($_username);$dir = DT_ROOT.'/file/avatar/'.substr($md5, 0, 2).'/'.substr($md5, 2, 2).'/_'.$_username;$img[4] = $dir.'.jpg';$img[5] = $dir.'x48.jpg';$img[6] = $dir.'x20.jpg';file_copy($file, $img[1]);file_copy($file, $img[4]);省略... ! o( k5 N( e) y2 X' @

# F/ ^- c5 v6 a/ k2 A. o" J

& L. _5 d9 S5 a; u6 H: c. M 因此要利用成功就需要条件竞争了。 1 W: i1 H; y' o( B

3 b$ O6 U) n& c# l" ?* _

' @+ f3 R" h, X" Z R 补丁分析 ' j- R$ H+ a" m' m

/ N1 V# i% _8 d0 e/ R

3 ]6 G: O e( t: _7 n" s  + N8 p, ]* v& D0 Q2 i

# w. E, z! {: f. l. _

; G" f% g& K0 H8 I6 b, U6 W 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: Q3 c+ \. o# L* t! {7 ?1 [

9 [0 |. H: z1 b* K# z' ^* E4 c

# Q, d- S/ Y5 E0 w0 G+ e/ ]$ t0 g function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} 4 u, w! `- g3 b5 b5 u

, W8 n% u$ |2 `% i

, G3 k% z8 ]2 ~' b  * N4 D9 f. _, {9 G- O. h

0 q! G N- V' V

2 u9 }# g8 U% H- { 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 F" m4 M) {- y8 e' E9 \! X

3 S1 y2 Y) o! x7 ~, X# g

, N) N' m/ k( [$ ?7 H 在is_allow()中增加对$this->savename的二次检查。9 N' N% w/ F2 `4 a: |

) ?: T" }+ B* F; ~* z8 c

! D, o- o7 O9 W0 I7 D1 c) H 最后 : I8 d3 N6 _* R" a# i

3 Z+ _ p% Y `2 G) N) p

2 P5 ?9 f" s& c 嘛,祝各位大师傅中秋快乐!5 u6 M" c; T: ]5 p, W

8 F% [9 L _+ m! G" w& `2 m8 T

* g& O) K3 [0 a9 V6 z9 \2 c7 [8 S   1 s- ~) u( T0 t. x3 O

$ J7 f1 K# c. ]- t
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表