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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
z% K2 i4 ^( M) _1 R x

( P( P6 a* F7 f2 {

, b8 n& o8 @/ z4 J+ t( I" u; M

1 Z1 | t2 d- t# s) b 前言 " i8 w) |5 e- M

% A/ @ u" Z/ z6 q

6 i3 l4 W- P1 z$ y7 F- ]* n 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 C: i+ F( _/ t$ T/ q5 s

" s2 t% n4 B3 P9 j3 U4 |

* h/ x# i; ]* y' M) m9 \   h: i1 Q" r0 j4 q R1 X1 `# l

: ]( L) j r I T: _

$ j) ?4 ~) N7 c1 k+ w, C! ]$ @8 _ 漏洞分析* x' G) e ^) G, F5 E

- y! t; ~: P( e( l- A$ o

. a# z& N1 A! T8 r: G 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:. X/ S6 D6 h2 R

3 s! V. [% K. ~1 v8 c/ I

) C; c' T) K, v! U' ^  - y+ }" r# N# r) _: y

7 q. G+ q0 v5 \! D& X0 `

- ]6 J- W: K% o; U7 T 对应着avatar.inc.php代码如下: o5 A# T8 O" s* s1 N2 `+ ~

' _' f# G: J( Q2 Z: u

/ F& Z$ p2 X9 y" t <?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) {8 ` I3 I, U7 M6 u, b; Q+ ^7 u1 C% P

% H5 u0 x9 Z/ N7 O; @

# T! X9 J1 [' K! N     case 'upload': - ]$ s! }$ ?4 _3 Z! j

; o! Y0 g0 r% x! N7 J

+ c4 |* m$ \5 s5 M. }# ]         if(!$_FILES['file']['size']) {6 K* _8 l+ }2 x/ I# v0 f

! w6 W2 |& f- L) {; c j

9 h) M( ~7 E1 G0 t, f             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); ! B0 \0 `- R6 s

: a' D0 d+ E- n' D+ z& O$ U+ S" V

- R: E! V5 t+ \6 z# {             exit('{"error":1,"message":"Error FILE"}'); / ]; J. S7 y. h" s8 C: t* `2 Y: C

4 U) h) E' p; e! g

. E8 f+ O( x* N; g, @4 t( ^' q         } ; ^3 ^1 _% @7 C

3 X2 K0 T0 h8 c& c/ ^' U

8 ~ ?8 ^2 O4 F+ o4 [# K$ G         require DT_ROOT.'/include/upload.class.php'; 1 ~/ U+ ^& w. C# n4 w

- B) R6 J5 k7 F# K

5 u0 d# Z+ q, Z8 C$ J  6 @4 u5 H! l* N* k: p4 a% ^

! ?2 d, g8 t' S1 R

4 H/ ?! O1 ]6 _$ ]& N% k$ \         $ext = file_ext($_FILES['file']['name']); ; Y$ @# `# u! b6 z7 E0 n

6 G- B+ V( G! C7 {& @: v

- h- Z; [" `+ X9 c2 p3 {         $name = 'avatar'.$_userid.'.'.$ext; " f. r( E$ V3 @% N% H. L

9 P8 A7 A6 u1 L" C1 ]

' n1 g0 o# e6 x; h4 d8 N         $file = DT_ROOT.'/file/temp/'.$name;) h# a; `6 j1 K4 a( v e# o

1 |/ ~- g) q6 f- X! u8 T, R

6 Q$ z7 Q5 r, x% w) j   ' p* D! K7 I- {) p8 M+ N

7 y% i6 }7 u( ]) }" a5 P( G

: Q# ?! I$ j% L         if(is_file($file)) file_del($file);; W5 ~; X" @2 R* Y# L

! p/ ~+ I% s# h$ @

: b" C2 Y3 z: E8 f" k         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); ! M. V% u D4 I7 D

& W" y, c$ ~5 J5 t" S

9 e0 E) N, E2 O. z# z$ G   / x, t* {: M3 D

; c: P* C Z& B I f( h

: j8 z% F3 R( L% w T! y: A- E h         $upload->adduserid = false;: j: Z/ S+ s5 U6 {9 H

/ K' M" I" \4 f$ a; Q

# V$ l* B: Y& I  9 ?2 L% |0 b0 e2 q& I* }& F

4 r& d4 U0 @0 r' T% f* n" P. ~2 j

$ t+ t$ e7 ?$ L         if($upload->save()) { / k2 _3 w, N& w( J% H } | D

/ b9 A0 S* e8 d! l2 V! t

+ `" D' s, ?7 T) m( f0 A, \& N             ... 8 T& E& v6 l h" g$ c" `6 e; X8 k

# a+ e3 i8 I" H" z8 Q' X/ h0 |1 K+ ? |

' g; O, Y* E9 I# F         } else {1 Z! o6 G& q- e- q

5 J, _- ~$ X$ U9 S3 N9 t3 _) j

% `4 |% \! Y* g# n: e5 i5 Z             ...+ V, E5 X3 t% E/ x( z

+ |1 m; B% N# l* T0 y$ V

4 f7 H, U$ E- @2 B0 j, V         } 5 o, d; Z. L1 ^' g2 \+ Z& W

& n8 ]9 L9 w7 ]% @2 r9 h% R6 h. I7 l

4 d( M* H5 N/ p     break;% C3 l* x' `' s) E

2 u' g& _/ }* y' v! S$ P

& J9 {! k5 A) L6 F6 j 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。, d% \: Q3 c( A& K3 E9 J% r

" M! i, R- }; d0 Y) s6 F7 \

8 Z: W- j, t2 w: ]: N% B upload对象构造函数如下,include/upload.class.php:25: ! m& o# x; L/ T; n, {4 K& L

, b% @1 b |7 ^( n7 [% L

W+ s$ N' F7 @- {, n1 h4 _+ y <?phpclass upload { ; Z2 |5 J% l8 s4 o4 _' W

: @9 J: s7 d2 x* M

7 x; w5 K$ i+ w7 {     function __construct($_file, $savepath, $savename = '', $fileformat = '') { , {8 N- \# |& i8 F

8 J0 m: d6 P" y1 g

' K |- |- u) b1 C# ^         global $DT, $_userid; g# K: I1 [3 I( p6 u$ ]

: n( `3 D' F w* R4 f

C x3 h$ }) N$ b( i6 g% z# g) o         foreach($_file as $file) { 3 g- }8 d* U3 q5 ?% t

% I& e# R% B3 C

: ~$ N2 j; E5 S! J7 D             $this->file = $file['tmp_name']; 5 z' H) A J/ b5 K

9 _" w9 A" f0 G, i/ t2 X" c, T

5 D9 ~8 a% Q: l             $this->file_name = $file['name']; & z+ o& U% ~7 c1 o

) W! R- d1 I( ]/ _+ t1 c! [' q% w

# C* s; Y. q, V6 i3 k7 a4 M7 j             $this->file_size = $file['size']; / Y1 X4 e2 f; B* x

& ^9 F# ?/ a; G( w5 f

" q* e% R C, h; Q             $this->file_type = $file['type']; ) r2 _: U2 F) R% {" i, u5 P

) \0 t5 F# R7 ~' I% |# v# t

8 Q) [$ {; a* p3 A. m8 P             $this->file_error = $file['error']; W+ p, U8 d Y

2 Y G" z0 g* T" x

) [; [2 L- j T* y   8 t- L0 r8 {! Q! \/ p

8 J$ V) h# J$ P/ P8 a7 c6 |

# [- R* E. e7 G- [2 x         } 4 N! U3 X8 j3 R& D/ U6 h

- f5 |2 J" s8 b- D

6 V7 i5 T2 ~, L0 X. y% _         $this->userid = $_userid; & j& L4 e4 Y) K K5 _# j* ^2 o) x

?! A& j5 ~& b& I

- O, x, m+ j) y. _, X+ R2 H8 ~         $this->ext = file_ext($this->file_name);" i- j: o/ j4 \) h& w

& r& F# i) a' W5 U( [% A9 b" h. k

; w% G% L7 r% O4 z4 z         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];/ K9 P6 y4 b' a" M7 X( b9 ?

9 _! l; l9 M( q7 B0 G

4 @& I* @! _- o, V         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;* ]* A F+ R, }5 L1 m- j

" V$ w) `) f7 G8 Q/ Z

$ x5 R) {: _0 V/ E" A, s8 P" \         $this->savepath = $savepath; : A Q/ {& o/ P0 h& J

4 Y9 Y- m$ k- i7 s }

; n5 g/ P! h5 Y3 T4 K/ o: N4 @         $this->savename = $savename;; P! L# F" `' p( Z( P6 H& ~

' c9 \2 k' R7 ~! x

* Q/ m/ B& K0 l5 |& A     }}0 L6 l# n. r* p0 }2 J/ y# U

% D" `$ Q! H$ k- B( T8 M% o3 r' @

: H6 L+ m" p5 H 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 , w/ p& u0 x6 ?( \6 Q4 j

! C, S$ }3 B$ [

; R( c0 }) ^: a. H0 w 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 2 [, j0 G) H( k$ o6 d2 w) c

# y- ]; o& ^& I# n

! |+ |9 D! D. V' A$ R$ p$ X $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( R$ S5 M# C3 e. z+ n

' I# ^7 }$ j5 r7 v- X

2 s2 _: M6 D w+ l* p/ z! d 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:6 w* n9 b ?8 m& D% f* R, @

, r* B( k; R C- ]& d

, y8 S. n5 Y* ^# b( Z   3 c* D: i" H3 d1 g4 e3 E0 R2 K) u

7 D0 T6 a/ X T) B7 I

3 K: J; k0 E* D7 b" s 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: ! J1 |3 @: A6 W# Q2 @3 U5 b

; ], F+ R0 j* X, f- G

" ]' c6 u7 I7 ]( o% d f <?phpclass upload { / ]& F: @" |" l$ `9 U/ r! a0 A

8 L) ]" M: C1 z. c1 j. Y, ?! E

$ ?: O& U% N8 D/ @6 s" I* b/ J0 e4 o     function save() { " X9 Y( h; c) n+ O3 s( J

9 A7 l5 {- N! F9 k( r* a6 j9 L: r9 h

! S7 @8 ^' K0 K1 K         include load('include.lang'); ( b* L% u, \; K( }4 Z4 O m

! ~1 g- J6 ^( ^* {0 K$ @8 P6 h

: L+ R6 p5 x; {% v e D% x6 @0 S. ^         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');- J9 F7 e; z2 R# \# [2 _

' v, R* u9 j7 v$ G- p0 J

7 |/ m5 o1 A5 s, ^6 A" x+ Y# [  2 w; B7 h% O- P+ x j4 U

/ |- `: j5 y4 m( k& y

- Q( `9 ^, {# _+ U         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');3 L7 ~; A# p5 A5 D& u w

! P& v) ^& x$ n

4 D9 o' i% o% l+ M, V+ ^  + \4 D0 H' v$ {

+ H. _! z' w/ e& p

. r V1 R# [9 | g         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);/ N" C4 b# V# `% o4 f+ H% T- W2 W

S; S+ ~ A1 M: c

, R+ ]* z1 e, _" f6 y* e5 P! t e3 J% P  : Q0 E/ h2 j% U

8 }8 M9 j7 q' I8 ~7 Z4 `8 l

0 C, L; B8 k% o         $this->set_savepath($this->savepath); 2 J- ~+ |# d- X

/ g: b: b; B. a$ T$ S0 l0 Z. X

6 l. U2 a V8 K5 G         $this->set_savename($this->savename); " ]+ j* ]" z+ }* U3 w; B8 ]

4 {9 g, B. q6 O! E; e/ \

. X5 F7 |! k( [% @   + l! i( x* e T/ z! O: ~. ^

4 [; s" q0 S, j' G2 J( k, _

; W4 W! a* `# T+ [         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); 5 N# T5 e& D. R( Z' r4 Y! C

+ {8 B I; I+ V9 Z6 K

) \/ L* ?+ Y* l- E: ]5 q5 L) {! o         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);' W9 u7 b6 r, D

4 L$ ?, s" R8 `% O- z- B9 q

* L' g0 [( M' F! ]         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);* V% s$ E3 {$ @- f1 h

: O# A% l9 X1 U& z

^; Y% O% a9 } F  & F* B0 m9 N* l8 [) M1 w

. M- q5 {& x% |4 Z' g

+ [/ u8 p$ w' j3 i: l- W; `9 D; e6 a         $this->image = $this->is_image(); ; w: ^2 q0 q5 J6 }+ R

( R2 I: a6 V8 {# @6 A p' o; F: V% z

. b4 y& k. n _5 T         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); , ^6 [! j+ n& [. U; I0 r

$ s4 ]% }) Z* Q

1 g8 m) e3 q. t. s' \         return true; 9 I, V# x: F; | Y Z

5 n# k- C Q* n% _& F. {8 u

8 Z/ ]9 g6 _( a8 ?! j/ W     }} ) O' E" Y6 q6 c

& f) f/ p5 C7 M) n r, ?

( H2 D( B8 W% K 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:0 m% y k. H! o C3 ~

: U: z/ E* I" b. @: b" N

& ^: W0 P6 U- \1 I) x$ z/ S2 v <?php 1 N, z2 G1 o% C4 i

! t% U+ C" M8 I2 Q( d

& ^& u7 Z# L6 S1 M( i+ \     function is_allow() { 1 `, b& J, ]& W8 e* i' U+ q8 b$ k

- i. k! N# @* A% ~7 Q6 K- R' K/ q

, ~1 `/ K2 \5 I3 D5 w, x         if(!$this->fileformat) return false; $ v$ N/ {; x" z7 \6 N; R

& t5 X5 P: y5 C1 ^

! y$ w1 F8 u) j         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;: H+ }7 b! `% G2 U2 h

; u& u8 [. H2 D. h! l

+ |( L( i/ ?: x& u% ~         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; 0 N, d, H( x8 ^6 R

5 D, L) Y2 W6 e: d% _$ k6 r

! N3 H+ w s. L         return true;& m1 A$ C& N$ Z7 r, v

) `8 d2 U, \& b; k' h2 H; ~

3 e" T+ t; s; l/ P5 `+ l/ n     }& j: ]! ~0 g# @( q: J; h% Y* m7 k* z

' m9 M' f9 ~" K1 B

3 D- @ L z$ a$ h2 p( E% Q7 h' E 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。( b5 q" L/ s2 C+ f3 C$ A% M% P

% @$ M( [5 z! p! B! [

1 U2 ]. y; W3 t0 R% F. a4 s 接着会进行真正的保存。通过$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文件。 4 t Y4 c8 C/ C2 O# |6 r4 Y5 F9 k

: ^$ `0 E- v$ x3 ?! s/ V1 R& ?

- g. \ A$ B/ k0 k A 漏洞利用4 _. f5 C" q& c7 T; {

\6 }& u: d; B; P# j' [

% v& h" Z, [; l% Q& r' P7 E 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 : N. g5 W* c" I7 G/ Q

1 S0 t0 B" Q( [2 T" s P2 t! h

: X( \9 _! j( w6 q   ( w9 y$ J0 _8 R- [

* z" v' n8 d- ]' z5 w

) ~ c# w% d& \7 ` 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid ( y) M* N0 W) L5 L" _3 S; K

" [1 [* u+ [* I1 Y0 g J* W

& y( N+ c0 o: _: M9 a2 ` 不过实际利用上会有一定的限制。 , N/ x8 C7 V( N' g2 v

" F: U" H! \1 c" o

, M+ I" j5 f8 ^) N+ U 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。. V$ a6 K8 m7 n$ Q; l

+ i+ M; K# s) f, A( E# B. M

' T$ u! M z& m6 W  # Y3 C( B0 v3 W- i9 p! s* d

2 X! m+ d6 s- t; I3 X S# e

4 y' }9 N2 K3 `; O) |) S- N3 }' T! D 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: h. o3 A g* K5 m; S

. [4 v$ ]: b3 ^' x i4 e

/ X) b, ~" z3 m9 j* e7 J! T. X 省略...$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]);省略... % ~9 h7 K/ I3 r t6 z8 N; x3 Z

! x0 Q* |" _& q3 ~" C

% I: G8 C( F, [% n4 U* C l 因此要利用成功就需要条件竞争了。 7 V! i' f& f8 E: I. H

5 o) W5 l) n8 W

, _5 [2 j0 x0 g% f9 E 补丁分析, w) v2 G# N6 t9 r3 x& c

) C4 K$ f3 j9 }/ Z4 G5 |

4 _4 M0 L! K, h9 G  , |# _) e( U3 \. K6 a! }1 d$ @# l

. l3 L5 z' {$ \3 G

3 r5 L4 Y. N$ a+ m1 g6 h7 H% {) ?/ s 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:, k9 J; w1 O. w; s$ U

1 ` S8 B9 b, X% ^, G

4 z2 {0 y6 z7 n1 ?: P9 G( l function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}) _* Y/ e: p: H! O( U4 x

# g [2 L( O; Q& l9 |& T

6 X5 ^5 O# t0 @7 o( E; v   ( v% u: k" R, |2 _4 C6 o

8 ^/ o ~+ O7 V+ C2 Q" e/ z

% W% F! L* P, {' q 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 . a6 g- l" q, D: a4 c. s7 s9 _. ~/ [. P

1 n R; K. U) U

$ S3 ]4 `; a6 @ n 在is_allow()中增加对$this->savename的二次检查。 $ W0 d4 g) J$ j3 b

& H M& P/ e1 }0 Y5 [

) e3 `# Y8 @+ W4 u7 F& S 最后 % Q1 `* r9 O' R' u U+ d

5 ]: q* M, z( ~3 r

# Z) X; b9 t5 }& ?7 b4 h 嘛,祝各位大师傅中秋快乐!: t$ d& u+ s+ x' l/ c/ J

- o9 \! v2 V* J

z9 d; r) m: s6 `8 S1 j  - f6 L1 I, N8 E) A2 n% @2 S

: v: z; i" c1 u# z% K% o
回复

使用道具 举报

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

本版积分规则

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