中国网络渗透测试联盟

标题: Destoon cms前台getwebshell [打印本页]

作者: admin    时间: 2018-10-20 20:13
标题: Destoon cms前台getwebshell
+ w; Z/ N1 E) v; b+ t5 B

. o/ P7 l5 [& _

' @) T1 J2 I) D

0 D6 r4 u" X& K 前言- {9 m* }5 ^) {* Y. K

+ D; r. W" P c

. Q' z1 D* ~" m4 b$ a3 P4 b7 w 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。% }$ D! `: C I9 n- M9 ]% G

% Z. A" I/ J8 A Y6 `

# Z9 a( k0 N: E- q) G   9 _& |! q" }, J4 A- W- K6 \1 o; K

2 P# z8 S. ^8 M* j% c

; N9 f7 d. j! j" _4 w9 @5 b 漏洞分析) V/ T$ J( R+ a3 ?/ W. Q

$ s4 O/ \; }! v u$ l; F1 ]

2 o y: ?' {/ A+ v 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:" s/ t% ^, i& n

- m/ e _3 b, {8 t7 y0 _

% o3 \$ c: Q& N t' B  + Z8 [( Y6 M2 n( I

5 t0 r: M$ {' x9 P

) v( n Z4 g& k+ q$ `9 y 对应着avatar.inc.php代码如下:: Y3 o' u# _, N: ~9 G

7 R( ^* W; e+ g% n+ s, J

- T- z8 p- V% w6 ~ <?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) { 7 h) W$ A/ Q: X) T

" Q* B9 i3 n/ O& p- x

1 V) \ D, `- T# d7 H, j% S5 @     case 'upload': 7 n7 ^+ c$ _. B; F

4 U3 [5 k2 F# ~9 S; |+ k

. k& S* ^" X5 M1 j+ L         if(!$_FILES['file']['size']) {4 C' ^0 a j* C" o; j; g0 J

F6 |3 N1 U4 V) E

0 y" _# g* H0 Z5 L5 L. @! P             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);) |; s6 E& d" k, b6 A8 f- W

7 N& s8 i$ `9 F' v

$ b) [6 P& D5 ?8 ~2 K; j             exit('{"error":1,"message":"Error FILE"}'); % S9 ~9 j4 c" X1 x. Z1 |

, J' }$ Z w% [+ k7 Q, d% |

) h6 u& l6 u! I0 w$ R         }" L- ?+ A% Z5 N

) g: |- k% [8 j9 h5 S

( f: G, t% ^0 W, ^2 A! n _0 p( a         require DT_ROOT.'/include/upload.class.php'; " a; f: Y/ p0 w: X

2 D( g& M, G; o9 s" n

! X; J! s* J2 ?. u. _  5 [# b/ r1 m$ |! e d2 H

- b- R2 S/ M8 `1 h

, A/ |$ g. X* Y& p: G( f3 ~! j         $ext = file_ext($_FILES['file']['name']);- M$ a9 \3 k, G* } C# C

. Q+ m! t) @6 h* @6 @

) P9 L+ E0 k8 k. H         $name = 'avatar'.$_userid.'.'.$ext; + S# S6 f* |* p6 d* _& P! a

M, m# [) [# }9 @* m A9 B

/ G) q( q: b! b; `' i" q* D         $file = DT_ROOT.'/file/temp/'.$name;+ f) b# {+ j4 t2 Q7 P) F9 w, u# L

& \' X: K$ W" a/ p3 S0 V

- a* [$ S8 ?7 L2 f0 T   + X. c, i: O/ c. h

! [5 M8 Y+ W2 Z" K5 |; J1 N5 W

8 s. J1 j5 Z; O% ]- ?, c; X         if(is_file($file)) file_del($file); ! G: A, _8 M$ M' z% D2 T% f

; W% U# n; B- |6 h9 P

' O ?8 g3 b2 X( }         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');1 g/ S: j% r$ M9 |8 W

5 x. R& q8 J+ q; b9 k7 ]2 j

: e. h- @; \% a A( w" n/ X  9 V# l7 o% ~$ @7 x$ a# b

9 I/ h% g; S6 q- w

0 Y) x p( h3 B$ U         $upload->adduserid = false; 0 U- u' e( v9 a B6 H9 ]& U2 C

) b7 P+ |; [: Y S% y* `

- h2 O( J3 |" R- D+ g& l" y! k' Z   4 Z& e7 U3 `' v7 s: K f1 V: ]

2 ~) _: b7 q, J2 e" ` l! a/ f# A

' u2 v/ o5 Z' h3 x         if($upload->save()) { ! B: g7 q( \8 k+ b" N& _

3 h8 z E' e2 {$ a2 A) d

+ k# A$ s8 ?" d             ...2 s% K7 V# j+ H! Z# @1 }' |% K

8 g4 \0 Q/ G$ D

Z! h; ^) R6 ^9 F5 C         } else { 8 u5 Q2 X3 d z. N# S2 e- a

& V/ {$ F4 q. f6 G+ T$ z. u

* a( C$ S+ [' S* b, L: V& ?             ...* s1 [$ R" o) e- b! |/ N/ Y% J

6 a6 z. m. b6 `) R5 P( N& q0 V

) I* g9 f+ p- _         }2 p3 D; x2 `3 v4 Z, |$ [/ I

7 m) u1 |( T/ K3 t: C x5 r+ Y

$ i( [2 Z& ?% K9 H x$ R     break; + x6 v7 Q& v5 J5 [ t

& \0 E, d1 m& L

6 B6 M. Y! M" u+ P, J 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 9 D8 c$ H( g1 S6 _) r

H8 j, [% i( T3 d2 ?

: U a( n* J& {( w2 x, m# ] upload对象构造函数如下,include/upload.class.php:25: 3 v+ y! f0 ~) m( ?

( |. \! J" l" Y+ q$ h1 z0 N8 N4 x

) |. K& @" Q' L V8 u <?phpclass upload { ! q9 O$ R4 U$ e, `+ E% M4 C

7 Z. O' k- G* n3 r8 i7 `

* {: H6 Z5 ]1 P+ I     function __construct($_file, $savepath, $savename = '', $fileformat = '') { 8 g! O$ N1 }# a4 W2 F- G4 r M

4 J- N- @$ S( @+ T9 K' r6 n

: e2 t3 H& e7 k2 w0 [; {         global $DT, $_userid; ! c( c! @- Z4 k( Q

/ O' @2 ^" B! Z* W

: i: B% s* {5 L% e         foreach($_file as $file) { # l8 y& J/ m; `7 E0 R9 L# w0 S

% ]: J' i7 n* B4 c3 j/ ?+ n. U( p

5 n4 R' y3 A8 E4 ]& Q! d             $this->file = $file['tmp_name']; 7 |7 [' \) I \; V2 l$ H

3 h8 Y1 }3 \- P

7 m* I7 t3 ~; H Z+ W9 e( e9 q             $this->file_name = $file['name']; ( @% K3 v( H ~

4 M& l+ U4 G. b8 ^3 V1 t5 I, Q1 I

3 T. }$ h* y3 X- _, O             $this->file_size = $file['size'];- A [! m. l3 r* g% ?9 d

4 H6 b2 t" }) s6 H. K' {# v, P+ m( q% V

, R* k! W6 J' ?& b2 a9 a             $this->file_type = $file['type']; " Z1 N, S) [. ~* ~

/ {8 M" y/ P; Q. z

W7 \9 H) B; l+ \* ?             $this->file_error = $file['error']; / U& `' R% F+ A: Y

, i" `3 p% m, i; X$ C: \1 ~2 p

( r6 m0 w0 Z+ R. p: k- I   ' v9 C' z( |/ W9 ~6 }

# Z, o4 ]2 b) \7 a. i

& K# W1 A( a8 y0 c/ O         } 6 x8 K9 w: d- Q7 z# X" W

3 X$ f9 e6 n$ b2 V' x7 @

+ d- z0 m+ v6 `         $this->userid = $_userid;- f* G9 G& [) W' W } v

6 \& Z/ N$ B5 n6 p3 B

. _) u+ k' _6 x/ A9 @         $this->ext = file_ext($this->file_name);1 l* |0 y( m( i* v x

7 t5 a6 e4 c& V: a

/ O5 v# [4 O3 ?, I# e8 o: L         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];4 a1 G' J1 R8 i& K

1 } T% d- w; y% i, B6 \5 t2 C

0 \9 y& ]' Q1 N3 l/ `         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;. z3 D& k: H6 V6 z' h& ~

% ~+ I8 R6 T$ k" o8 \/ ?( h

$ k0 M$ q% j' N2 R0 @% }         $this->savepath = $savepath;5 y2 j: V+ O. k' A+ ` p

# D2 e+ G. \8 x+ X& b. I$ T

! ?2 m) T" |6 z; V1 p+ S% R         $this->savename = $savename; 9 k i* J7 p* ], }$ n4 \

2 m5 \& ~6 S2 W$ O% I) h6 [# ]

; d+ B5 r% }1 x& h) d! E     }} / _2 S8 m% T" Z

+ K# p5 l1 H, r; Q2 l

" E( a6 P& \4 E7 r1 x* N 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。; n6 ]7 q& v( |. G' |! _

- D1 X( z& T& U+ b# X4 w5 {$ m$ ~4 Y& h

1 u; V/ k8 q. K5 K1 R' u$ F) k' e4 _& a 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 ' o) G8 d& u" Q+ b- l& B

& b7 C4 |* x8 e! b4 g

5 X2 Y4 S6 S4 V+ g0 q9 q $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.php2 |4 O8 p# y& N" Q/ t. c8 b

: i; `* H9 ]0 ~& t/ V

) A: a$ T! N% q 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: ( |1 x% K' h6 T% y, _

+ B( ^0 K# l1 v! n3 A

0 a/ S+ c+ ?3 L3 W3 c! B  " o9 c3 y* b' n" r" V' y

: g# u2 ~) p" ]- ?1 w

. Y5 Y" I) q! {6 e8 k& Q5 L% ^2 w 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: 9 c5 I# Q+ L+ D* D; z* J

3 S% `- P2 k7 C: q- }4 ~

3 T& h# I1 N) b2 f <?phpclass upload { ; j8 F5 [$ s. n6 `0 t" u$ b

* I3 @9 O; _, K" C+ n

! e+ q# @, m. C) _     function save() {" l' w2 {. i6 C; ^" n S- p# c

3 F2 U, J: Z; A6 \4 I& g

% Q4 H3 O9 j3 ^) d1 w1 O+ g         include load('include.lang'); % F3 G" J3 [* g0 s8 L

3 ^/ \6 s$ ]1 a8 d# V$ [$ a

9 u% W. K( \0 ]0 t% `         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); 4 O- ~1 ~! }& ?' q8 M# s3 s

9 r: f- A6 `( V9 Z- m/ n1 @7 d

- O" e- s7 ?- _# B) W4 u   * u& U, V2 |" `7 x, J, ]

8 |" p! a6 J& t2 m

! U. u8 t' z, G: W         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); 5 q6 ^2 c0 s5 T- t8 D# L

* v# v9 ^& t$ V! c2 u& N9 E! y. _

6 L4 o. `6 K/ U7 J1 \  5 X8 r+ o7 b a4 d8 r' s% }# F4 a- Q

5 }" F: C& E, A5 C$ @

' K) H. Y- E9 G5 w3 M& k8 ~/ Z) K         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); ) Q" i" C0 t7 C+ t. j, O, n+ Y1 i

) i/ [( V4 F, ^* {- g1 U

! l1 h$ y9 [2 M9 [: Z+ \; S   ^& _/ S+ }8 ~4 g F/ Q

6 F' ^/ U! l( V5 x3 |: Y2 m

8 ~( G1 G- T. S& {+ c$ g3 U W         $this->set_savepath($this->savepath); 0 `0 O) I8 c8 s" p" |- e

/ r" m4 C# I6 j2 o$ L

# m" S) b8 F" F- g9 o0 P! E ^         $this->set_savename($this->savename); ; s1 T! v- F0 P9 k! G7 A) ?) j) l8 {7 B

1 e4 j6 M3 W" ]4 W1 Q$ v M( Z

5 S6 w8 l# i2 P  5 q$ B5 D& c) u6 {- l$ S9 I

K" \$ z* C$ r: x" j

: {& O1 P/ L0 j         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);# d% Y& W X. z A

+ }7 v+ z, ~0 y" f

% M' k+ j* q4 ]         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); - s! `$ n8 O# q& q

% v" l. J4 n- M9 d: l# w) s

, N% `% R+ O2 B; n         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);, [' w; O( T' \, T3 \% H* `

, a( l* E8 [9 S/ B7 Y3 ^

/ i/ L( v2 l3 K! Y   ( F+ N/ A' K" q A

1 L7 m3 \) `' m. \8 J L* {

4 ~* \7 Z0 J+ X# X7 H, G9 G         $this->image = $this->is_image(); 5 b" t7 S) \$ {( {5 e( M0 a9 T" B

0 f9 H$ q# z% S; J6 F; ~. b

- z- M( b# h) ^) T }         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); ; O7 J1 `1 ?. Y3 s

: a6 C V% m k5 U7 `. e3 {% `

! F% i, h t6 U( k2 Y8 Z         return true;3 R7 l( i- o* O$ d6 f8 q

8 c1 M7 h' P0 S

; c4 J8 d; \9 W     }} % P5 e% C9 ]" S3 s% \ g2 h

$ o+ o" l6 a$ C! o$ d

4 b& g1 T+ l1 b8 f, _9 A 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:" U) r5 a' d3 ]9 |

* B, M, \. k* A" T

- Z$ a, L3 e/ H( n0 T& T* w# r0 d' J <?php 7 Y0 n( l$ L* D. x6 c* {

/ e, s. Q- Q$ H- B+ l

0 @. z; Y2 d/ M G     function is_allow() { N% P" \/ ?, p

; U. f& }, t+ O( j

* ]4 L! d0 }6 ~. y# G: i: R$ `4 S% M7 T         if(!$this->fileformat) return false;; t/ P7 R+ z' \" O5 D

! I4 u5 k7 i; Z) f( k8 `/ l, O

, l& c3 t! g0 A" W         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; " ]. [. R/ L& \4 e5 N4 N; R' @

3 _& R* [6 o4 U1 O* j2 T8 r

- l1 e: x$ R6 m         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; - L5 I& j$ A1 I) ?( C+ N

3 ?7 ~ P4 `- g6 i* G( R: l# Q( L- ?0 e

% s/ t$ x( k7 n" D! e         return true;) o! i R) d* p2 I

( \, [/ c$ W' ?. c7 u9 y1 G

- y5 O) N, V4 a% E' ?) d' b& E( N     } E; ?1 F$ S8 K. u: j$ D0 A

/ S% f, y0 ^$ \1 z1 T

+ c* X+ j1 j3 ~ 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 2 R* i- c4 D+ D* r0 z9 u# L

7 h$ A8 Z5 |" K5 w

* O; T) g! Y6 ^ 接着会进行真正的保存。通过$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文件。* W2 ~6 ]: o3 w. _ S; |

3 D* e: i$ y$ k* h! x; T+ ]+ m8 V

# b9 J; Z' _8 E" ~ 漏洞利用 - E f& g. Q8 v, D- n

' S! D' W+ m! E# O) j( v

1 F! `1 H% V- Y 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。, M7 J2 B9 ^' U. }& Y5 P4 b/ O4 Y9 ^$ t* \

4 T# N0 V7 u8 s5 X8 ?. T; [

& q2 `8 X( G6 J! E% @7 c7 ]   9 j/ j3 I( i- R0 j. J0 m& j% R

* @: v5 W2 a% _' V

2 E2 ], _- x" M8 y" J5 v4 O3 h 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid ! C: _; }: ^( v$ C9 I! @4 \) t0 A

/ A% V! [* ?8 k, T+ E

; y# i6 v4 Z! I6 v- L! L 不过实际利用上会有一定的限制。: |6 I2 R2 V# I( y- Q( H5 b) {( u

4 h* n! \+ h) @# C

, ?0 l4 O5 S9 x 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。6 x* L+ M) L. K

! ^0 U( g' a' ~: l' H

) l) K& B. M7 r   0 h6 {5 |2 Y6 d& l/ }2 B

& Q) k1 h% R1 ?

; z5 `5 R; y; Z2 G 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:) Q( P$ H: F: \" L& C

3 e; I* `* A! `

7 ?6 H9 r% A2 z( \; l8 _" T0 N% C 省略...$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]);省略..., X% u4 k+ u0 M: f

/ i% m Q( u" a5 I/ L

2 q# j. K0 k( @& p. j 因此要利用成功就需要条件竞争了。 1 w: {+ W( d; p

$ W0 f8 l# ~* H: H$ _2 {

" e7 r' q" S7 w: r 补丁分析4 |, Y- t5 j7 I% Y9 L& m

+ r! w0 R+ I9 s0 Y

3 z7 K6 ?) J- }' H& ]( u  ' m Q% k2 j) `3 O2 L, A6 Y

3 ], V: Y# d I) A) m" w

# u6 y# o# [! Q! F; r 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:/ L9 s9 H) d) `- C F$ P1 ?

" R( Y. H; y/ I# Q

) K3 c( k" e W: N( _ function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}8 f9 q; i8 s4 m2 X* w6 a2 ?# V

- q! w7 V" N( b

; U% D5 O$ k9 @: h: x& n  / Y. x: s5 x) l" Y/ _+ J

' H* w8 k& V3 ?2 t/ j5 ~* f

# C! L4 J" N1 ^" d N) v 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 % h1 k, Z2 x6 ?" f( J9 d

$ @) [0 c+ X+ f3 f. A: ?

' l/ ]+ \4 J( H8 d+ c 在is_allow()中增加对$this->savename的二次检查。; k; f; U" L- U' d- L

! @2 B: X0 s" J9 K' }

! W" h/ o2 Z: `* t' N 最后 . [7 m {9 N3 D6 m( }6 S9 R+ [8 [- \

8 j% l, {# ]( K2 \ V

% y. K1 C) l! ? 嘛,祝各位大师傅中秋快乐!2 S4 Q* d* x' }4 h

U/ |% v& [" E; g6 _" d

- H2 f" M4 I8 m+ S# A; |   2 p8 s& J1 ^! i( {3 r# S

# {2 a5 C+ o k# R* D9 B





欢迎光临 中国网络渗透测试联盟 (https://www.cobjon.com/) Powered by Discuz! X3.2