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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
! I# b7 u. [2 Y. }% u3 O) E: ]

* P$ N! P H1 a2 c( R3 k

1 y$ \5 H" o! q2 U

* k6 e+ k2 Q* B1 [0 |! ? 前言: H& |+ c- L, I6 ?+ ^( ^- R

; p/ S/ W8 D! c! k8 o# X+ K

& i9 y. }$ i/ B' H, b% y' K" \ 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。- C- @% ^1 \$ S5 [- I" ^

& [- i8 c" Q. X ~; `

& Q$ \8 y. ~- M3 A8 h& a   2 o/ l( [ i( [ M

5 D; A/ ]" u* {, Q0 T

% k2 L. N8 S2 Z. W* M3 x7 a 漏洞分析- \. k$ G$ ]7 O

5 U& ], y& ? W+ N5 U9 ]* ~

5 |) ^4 t, F# M5 y& `2 Q, i$ s5 V; F 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: . H6 D6 I: A5 L5 H8 R7 l' p' m

- N3 T& q( b7 b/ E# W

8 P" p# q& D8 w, m4 H8 q' A/ B1 O   ) Z8 v0 A( \5 x0 r

8 z4 l. c5 A6 K2 p5 b, v/ b8 r

" m: _2 s! a! J6 O 对应着avatar.inc.php代码如下:/ G; c( P! a& }; Y8 R4 g

9 `- w) g0 O; W0 q2 j0 b

) [1 v: D& ?0 O, q/ `; U, S3 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) {+ l( k2 C: L) s

4 ~) @" K% [( u5 u

1 P5 O# W% v& n+ R; k     case 'upload':8 u3 B. J- V+ Y' e( S

. Q6 r4 l, e- J0 }8 \

3 I, J# H) J+ n9 G         if(!$_FILES['file']['size']) {- M! Q+ M' t- f: B! t! \4 b, P% U

' b3 A* w' ^4 q' y* }' u

- r9 C* Y. _8 `. r2 s             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);/ u _) ^9 i8 ]' p

% `( B, n- f1 ?

5 S n3 h O' \& k             exit('{"error":1,"message":"Error FILE"}');6 ~* l/ m( y- O# K* {0 m/ l1 @

6 f9 P7 q6 y- e& `: h

$ a `' E5 G* j& E- U1 z6 m         }( ^- Z) F+ g/ _

! `* |, @: F4 b% y

) |5 s" [% f0 u9 u) ]+ O$ u9 _         require DT_ROOT.'/include/upload.class.php';# F) N2 O5 s- d" U- J

7 Z" P* q: q, l$ a

" l, ~( k- Q& F! z9 p! i6 v' S  * [5 _7 |9 R. o! D& A

6 Q- y7 m8 P. V

6 P- w; J% o1 {+ C# I( o         $ext = file_ext($_FILES['file']['name']);7 e! ?0 w) e% V/ W- z0 c' d

. b% u8 Y6 e6 k/ M% h

+ r3 l: l, R4 |1 f- D6 O' D         $name = 'avatar'.$_userid.'.'.$ext; y' G2 B! d/ q& V& f' q

9 p" f; f+ x/ R# a" |, t. t

' _2 G* J) R6 C         $file = DT_ROOT.'/file/temp/'.$name;4 F( g7 `" ~# \8 D) t9 B8 o( o

7 l7 f7 C, a# O0 f v5 ~6 z8 `4 F9 Z

, v$ t* _# B; q  & P# M `+ }5 W. h! Z

" @, v3 q _8 f! s

* }- g6 C' M& P         if(is_file($file)) file_del($file); % ^% f% g ~. Q

# V+ K- u! O" D* i, ?

3 M `. V* v& m; A         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');2 l& Z( X5 ]: i( ]7 a- r

, c" [1 f1 x$ \# I, O. b3 E0 f

# e ?& _' l1 _2 A4 _/ q  6 t& E4 q& z7 S4 }- a% d" u+ {

9 B* O( D0 P$ l/ I6 U

" D7 a1 L6 n8 x/ D" ^         $upload->adduserid = false; ! H8 |% x8 K: a3 _2 i

0 }8 U ^7 I0 Z" n. J+ o- h& e

8 D2 ~+ a& }' o$ S) N% r   . o$ {; f0 Z* L8 y5 n- l

1 U& Q0 M& `2 E

# u& P* N6 T6 W& l         if($upload->save()) {$ Z$ u# H c+ ~5 y

* j. ^7 e ^' _) w" t# M+ K7 l

8 B7 Z# s2 d$ _             ... - i9 N- N. q |) G1 C \

3 H9 e1 I9 N5 D- r `0 Z" J4 S) L3 {- E

& y8 T1 D$ E: }; D s: Z         } else {/ U0 X/ \( k$ R

5 {9 r t8 L2 ?( z, _' t

! i/ B' j" d! o; \             ...4 p3 K; S7 E5 Y$ q

" x8 k9 V; j0 b3 M

( j- F, b) ~! Z9 A \* i! V6 {         } 6 {6 l C; O' s1 a$ b e/ P9 z

9 ~ N$ W0 r9 Y

/ [+ K) W# a- W8 u% I$ B! Z     break; 3 e% V7 ^$ a, ?

# n0 \/ r9 A# p3 @9 s+ \

1 ]+ B0 D8 X' w1 f& W/ @% H/ I 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 $ q' {" L* B1 j/ a9 g8 u$ ]

$ {7 G. u' ~) Y+ C/ f/ }: N

* t/ I4 V0 `+ H) [; V1 w6 Q upload对象构造函数如下,include/upload.class.php:25:' z" _; G. m7 R+ n% ?

! Z; `, Z Z7 a( M7 @5 @

- m& }! |9 F: I5 G [0 l0 U( N4 [; O <?phpclass upload { 8 R- L. \% c4 ^( r" v/ G

/ s* `1 K P1 b7 Y& p) I! j* r/ Z! n6 Q

T8 k" w5 c: z3 E K/ ^: S( {1 L9 d     function __construct($_file, $savepath, $savename = '', $fileformat = '') {- | m; }. `3 A

6 |' e6 s/ _" x+ `7 U8 w: G% M

* }6 N5 l8 r& b& ^6 Q         global $DT, $_userid;7 y1 R/ q5 Q5 K: M3 z, n

+ k1 U) Q7 @( g' g2 |# e

- y( N$ d, @3 l. \- L2 ]7 ^7 m         foreach($_file as $file) { $ S C' }* {: S% d& f# n2 [4 p! p

1 m# Q3 P/ H4 k

: H' ~0 R) j& j) ^$ g! H( C4 x' N             $this->file = $file['tmp_name']; , y' T$ h1 M5 U

4 B% Y- F( L$ ]# u4 ]% H5 A

5 b1 ] W4 h1 U2 J5 I3 i! V             $this->file_name = $file['name'];& ]1 }' F* d# x) ?: d

# _3 o) u' {6 T+ S3 I; m

& G( _% M7 j" R& ?             $this->file_size = $file['size']; 0 C) D7 g+ G! I9 O1 R, N" C1 e

! E" _2 ?; A& g+ ~8 t

: _* F: B& b0 s             $this->file_type = $file['type']; 8 z7 g6 x& P- G# s6 u+ ^( E

6 Z+ N; g% ~- W# [

2 C0 _' W! k$ b; l) H6 I7 ^             $this->file_error = $file['error']; 6 B" @7 z/ W( Q% f5 E

* w- M' ?( a1 H

# } d' u- E+ D) F   " X7 {7 U4 g: o w$ y/ k

! S7 m4 C0 v. U7 g+ Y9 N

* v% ~9 S Y2 w6 x0 |% G K         } 6 ^$ v& P; I4 b5 T9 M/ a. _

6 o/ A7 p P+ k+ h

- L2 G8 G* u% R. T) [         $this->userid = $_userid; 3 N" e$ [. }& J) ~' P, b- Q6 t

& x8 h% {! b. t+ i

) f7 }( o/ V, d9 C+ e         $this->ext = file_ext($this->file_name); A$ V4 t9 ~ D" C% k- l s9 u

* l; Z% d1 ?' L# q; Z5 v) [8 C

; e( q( l( i9 E/ ~7 z6 J         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; ) N/ t( Z [( p0 ~/ e5 L( \. n" B

3 @1 C4 _5 ^+ }6 R; {; t

+ w- _- x) t+ B% K8 C! e         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;6 k; @( n5 t: V' v: ?% r3 |+ [( p

: ?4 f2 _. c( a& S% o( o; S/ ?

4 a4 J2 y4 @" E         $this->savepath = $savepath; - Z$ V! M4 L' g

. q. l" |- j( a! X: P& E ~

3 j" ^$ w+ G+ ]- x         $this->savename = $savename;8 X$ S4 s6 t/ {0 N

) u. o a7 K b) G o6 d! z

$ R; i w. J! p/ o% [) c4 z7 G     }} , \( p/ Y, ~( ]/ R# W

$ `8 r! i! `( u+ _. i! K

7 `/ P% \1 b7 ? 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 $ u+ {% Y) _- Y" r4 L/ e6 ]; a

) g1 w8 M3 ^0 ] s( |% f4 @

8 i. D2 Q6 t8 B. T) h* ] 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 2 s9 {/ @ T3 x

) E! @$ L7 ?9 B) S1 l

( \0 a7 J. Z% k# d- v( N, k) t* { $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 $ n( V, _9 q' G0 P8 T

8 }" F( O6 b0 `: _$ K4 V0 L

( o( H8 U# {- S3 C! R' K 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:. J- k0 M+ z4 X/ z+ V3 u R* Z$ |

* B7 \, ?8 r9 _( t- S. D5 M

% M5 O8 n9 [: J* n7 q' N   ( l) _+ d; C$ l4 Z* _) B1 c: E

) w4 u0 n J5 B5 f2 ~

k; @: [ W; F; H9 T 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: L* ?5 {1 [9 e, p/ r

/ M u* |- o1 ?2 K) \1 v

; d9 z* i8 U# [/ V# N7 _4 ` <?phpclass upload {9 m: I; O1 ~* x( c( G4 ?" h, I

9 N, |8 p8 X0 y& o

/ d! }" a0 t k: j3 G     function save() { ! a7 b! d! m( v) N t# g

; _: E; @/ i9 ~

" v5 i5 p9 q" ?1 ?( Y7 T# c9 n8 b         include load('include.lang');" D" m# W2 u" J, e" V8 C

3 m; w q: ?8 c; p! g* ?

* j1 D l/ z% |% a- W) h         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); " b* K! a& }' d9 |: S

7 D+ r3 c# ~4 w8 O$ s

+ o, g" s9 a1 k% q0 o9 H   & A" a" b7 q6 `! u

; y, X+ `$ r. a8 }" Y- R, {

. X3 v+ X2 ~% E V) s         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); $ F$ W8 u2 [) q3 S. O' Z

0 B/ g! f+ b5 q9 @8 n" E

; m; h, Z5 D( ~4 V8 a) Z   ( n$ G6 I& B/ X* W$ u8 P

, v# s7 f; k5 v0 U" A/ H

3 W, L8 ]7 j+ j- K( d, r2 }' Z3 p0 Y         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);8 j5 k" x& s2 X2 }/ B6 C- \

; X5 n: o/ y+ e* ]9 {

" e: c8 R8 V% _- _  6 T( {/ G7 j; a7 o1 p/ x! X

) A/ m9 Q, z$ g8 Q7 I1 e* O

: {& }; t( J$ ?6 C         $this->set_savepath($this->savepath); 5 H. ?$ m6 _3 [

' Q* J a- g# Y: i

4 Z6 \- O9 d1 L6 C7 B         $this->set_savename($this->savename);/ y, G( O) Q" r; r

! q/ Z2 {" D% V- O/ E, B% k7 ?# p

* b; t/ R4 X5 X# b+ s* p2 E Y   * m9 |8 B6 v: R' z4 C

! C3 v8 W' g2 W/ t

4 W" W' A, v$ Z G5 Q7 A- Z( C         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); M3 P K, H* d1 W$ D0 [$ }+ x8 r

3 T: d! T% O9 x; b" P

) b2 `; b9 b s# Y4 `) _         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); 6 D! P8 w1 n/ W) X# |

. U2 x% D; b3 ~9 k* M

9 G3 X% o1 f# M6 o         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); - J( V3 @7 ~6 \0 c

) ]& W9 R7 @, y; m3 ^5 G

. y# O3 ^/ T7 O( o' V0 z4 ~' r5 O  7 i# Z* |! \ q* L% D$ }& N( r

& v# V. f N. \! D

7 V8 V' n: ]' ?2 {4 S3 j         $this->image = $this->is_image();! Q1 J- M! Q) A+ X; F

' P) A" ]4 w" d: Q* t2 y

. N, b3 v% p* V+ ?6 K         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); ) d, j _4 l1 E# e6 N. }! |

# J1 M0 ?& W6 w1 m; Z

' B j, l" R# Q- _         return true;3 d: y# w2 S1 q% J, e$ Z/ v

. X# k6 ]9 W8 i. ? `: E9 n6 C

; o2 R" m5 g. W6 L# N     }}8 t) s) ^# b0 ]( g8 |. A& i1 R

8 J, k$ y2 d8 e/ d0 Z* A9 p

2 H$ W; G2 H( n( \2 e( P4 o 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:# P+ v4 G; r! V P

* Z! k$ }, ^3 J9 m6 G/ |6 s

+ R: m7 C) t4 e' m( N0 x* y$ m <?php* W b0 ?9 Z; O! U, D5 q4 E

/ v ^# J. O L- b8 T

' Q" }4 b- v8 _; E% z     function is_allow() { 6 v8 G, S! h1 |/ E% P

( J& G) p& z( @% k

% f% u; h# `. V) @* k) K1 Y         if(!$this->fileformat) return false;6 h% s8 C. a/ G/ P% \+ z

9 i9 J' A, ^% d* z# H

6 N1 w; X5 U {2 s; ^4 }         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;- ?) w% A* \' s K+ Q

1 B" D9 v3 E( Z* b" R. p

9 f- L+ ^. N1 M( [/ B$ x! _         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; 6 \. V% U# |+ j, u3 d

8 J! y7 P. w% X6 z$ Q6 a! E

4 m4 v$ N: z3 D* @" y8 o         return true;3 j6 a6 o! q! Q, U6 Q! z! l9 n

& Y$ ~% ?4 H$ r

! C t7 }, J! ` }& Q- F3 \% \2 p! `     } / n# z* x# B. K! r% g

! C8 t# T! j4 w, E$ Z

# o5 ?% c1 _) ~, r2 y# O, a9 V 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。4 U. ~, F3 Z5 `2 a. \

- ^! u t! @2 y) {3 D3 P, x

8 j: l: F/ w( G+ h# 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文件。; |7 {# L: q- q% o; i0 t

' D+ M3 i5 V- f: l, K o/ V

8 N; H" O0 Z- T" ^ 漏洞利用 2 b2 u6 w/ r, B9 M2 {8 ^

! ]$ G0 d' H& s. m, H- s9 [

3 b7 Q- U: Z" f2 `5 L/ y 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。$ I( W+ T8 d) u1 b- G3 X5 J

3 }* K, t2 q( ]) h6 m4 y5 b2 l- S

1 Z% ? C+ u! j( A' w8 d   # a& Y6 p9 w+ y1 ~

1 ]2 _7 g: O; j: }, i; p

, G/ g1 s7 U3 W. u 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid3 j! K* V. [/ Y" S

, v) D0 w1 A' @8 G# X

; m! p$ z' Q6 B) P5 K 不过实际利用上会有一定的限制。 # H4 B, q+ p3 a8 U8 q4 ^: F# P3 m

0 v" s9 J: L$ A3 |. t

2 c. E; j) b9 e6 R+ [# W' ~ 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。' z, D: K' w1 R* D$ b: \9 Z W

9 x8 I2 w$ B( D9 o X2 |

4 R' ~1 Z# p }. B- N& q/ S) i( l6 w   I9 f5 O: R$ W- G- n% F; \

^# [" K5 Y) f0 j9 m

2 Q/ L0 K ^" O. g 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:8 d$ F" U# R8 H9 T/ \

+ L0 D- ^) d/ C/ |$ I" E, b+ f' V" c

3 F1 _5 k5 k7 V" D* `- S( 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]);省略...! o6 A, X" L$ J1 s

& I4 [! n0 g9 M7 G8 v

' @9 {& o2 S% F; f 因此要利用成功就需要条件竞争了。 - o% j' B2 N4 m& E

1 U4 J0 B. Z$ j! Q6 ~# k! P* T

- y) a* a# I$ S( G- X* Z 补丁分析* g1 w6 K* \5 ^5 w& |/ R7 Y0 h

) { S& i2 ^5 \" H, b, a. O- x

3 Y( Q7 R( O( f& ^, g1 \   2 Z0 ?9 i6 o" Q6 v* b& ?8 `: f+ X& \

4 X+ t/ }9 A1 g0 F! k) U, Y

0 \+ I& c% t& v; s# V; l# L4 i6 J 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:* D' ~0 e: b) v

. Z3 G( c* ?) w/ M+ D6 f! {) e

$ I, n0 I" n) J# {- H function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} 7 ^( c. E' a8 p* O R* M p

% ]. h+ u/ Y6 J. v" G* i$ M- m+ a

+ u7 j/ F+ ]$ }' E+ u$ D   0 X; m' S. q, }9 D9 t% T

5 e! D2 O( t/ ], h5 g1 {

; D' C, E g% p" \ 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 2 u1 q2 C$ t! `

w2 o2 V d: u1 P# J9 v0 {8 B

% G/ i" O9 l3 z0 t" M4 ^ 在is_allow()中增加对$this->savename的二次检查。 N/ C( M: n9 p. U( T

8 p9 S: v; M {! q7 w4 g6 x7 ?+ y U

( ]* L; e+ p6 f- t0 }( a 最后8 \, }- r4 W/ ]: ~- c7 G- |. N

! P+ C" g8 n# @& H

( f+ P# P. j; g* y6 Z( j- f 嘛,祝各位大师傅中秋快乐!2 Q6 d/ c3 @0 B7 J4 O) ~

6 W4 D: C/ U4 M+ _; q, m

' ?( O/ V4 Y4 y" w  % Q4 N5 d& j1 @/ Z

6 l7 D8 K, m! \# J6 ~
回复

使用道具 举报

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

本版积分规则

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