找回密码
 立即注册
查看: 1605|回复: 0
打印 上一主题 下一主题

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
/ W, H$ z* ~! ^

. c/ B! J& [6 N! j, _6 L

% ` w* X# h3 [. l g

7 W$ F j! \% {; B 前言 + n: ] X% ~% z5 e5 v7 t

7 |. Q8 p: z; H

c0 b+ f" Z2 E 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。* a$ `9 n; Y# Y* _: X' E8 K

% G- I Q4 d6 ]3 l

4 N0 [5 |2 B- l' N0 g   ! O) F) `1 Q/ l4 ^

' L5 r4 `3 a. G/ x

3 @ d, p) d0 O; u, Z/ A8 m2 C V. p 漏洞分析 : n: r6 \$ c6 u& [; P

, ~: t3 i G+ |5 x

% ?/ l% s7 A3 x- k6 a0 O 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:3 Z8 f4 g" z: Y

( ]* P( A/ \# v& W- } \

2 e) O2 t8 V9 W& q. E) J  3 e6 _; V- s! ]' Z! s3 S

- f m6 r8 H, i' \% g

0 G. Q+ m3 ^, M6 j. O 对应着avatar.inc.php代码如下:7 o% A1 E% r* Z+ f

2 m! \9 f2 E5 X d5 }3 m1 ]( N$ k

8 t+ M% U0 d1 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) {) k3 ~! E0 e: B

' e: c' ` z! H6 @5 S0 e! D2 ]

& L Z6 |3 P$ v, s- |7 J     case 'upload':) Y3 }- I; G) i' S. d! ]7 _! h

j+ j+ a6 A" O: V

- f! D) l5 z) G         if(!$_FILES['file']['size']) {6 ~" `. ]8 L! d- N9 f

4 U4 ^1 r6 n# t& o% J, |

1 D" [2 X% n5 s! i- \; |6 j             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); * V) A& Q. k9 {9 y" b6 R

4 X5 a6 j7 w: |7 ?' q

5 R6 g1 i I; l/ b5 S4 O/ h             exit('{"error":1,"message":"Error FILE"}');; g$ V, o( u& R; V

9 e- i2 u2 Y1 o. S/ M

: O) k+ n/ d/ D' _         } ' F! m) [* _: T' e2 \6 s3 o- b2 }# |

; [2 e X7 |7 o. a

* z0 s- j7 f0 G         require DT_ROOT.'/include/upload.class.php';& S6 M) [/ H+ |7 ?, ?# Z

- N0 {4 X4 M+ {' A9 w; }

0 y; \; H6 x# c3 U0 A4 ]8 Z  ( \9 t6 m- W! ~& ?+ I# j7 Y( g

# s5 u$ d6 r8 S, V' y3 y8 a# B

; Q+ O7 H' _3 c' e( g         $ext = file_ext($_FILES['file']['name']);9 ]1 S4 r0 H& ]6 E* ]3 {3 z

6 b p- {9 t. Q% x$ M4 A

0 e' w* K4 Q- y2 A8 q+ d( X2 R         $name = 'avatar'.$_userid.'.'.$ext; E# H% n3 J$ o& P" P9 J

9 h* B9 l- ^) t2 c7 z: z

+ }9 }* N3 _! X2 z$ J4 b         $file = DT_ROOT.'/file/temp/'.$name; % T7 |2 b$ e# q; i' `! z$ M

+ k& y1 F$ D6 T8 R/ f2 \

5 n; H& u$ I4 c- H+ {) @   Y/ S( Z) D h G9 q" Q( H

9 Q" j- `8 d* e

# k( j2 M, I" k, Z3 q3 i- w         if(is_file($file)) file_del($file);# M3 B- w# v6 n( c0 ^

" F- @2 h& H; |

- M, Q# @4 f7 }) w5 D         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); - i# Q( k' V f* n! n5 ]2 S

1 q5 A3 S9 [) o

" P7 r7 o$ _' K ^' Y5 ]8 N. N   * @ \ a. U# V

- W* l0 c0 ]2 F% ]/ u6 F

4 q+ f* U& a6 Z# K9 e+ A, ~         $upload->adduserid = false; 4 M2 t0 I% N3 Q& p3 I

& |3 I& ~, j) j; O, k+ t. k

; t) N6 w/ M, X8 y* W3 A2 U   1 W, O2 Y" w1 Z+ k4 K7 }

% W( l# J4 z$ ` e2 F. N

8 s* `. b' U9 [: Y+ j% W         if($upload->save()) { % I$ t+ G: [: W& t

" ]5 {) u K# c( s! Y

3 p9 o) O6 w& [ i0 Z& C& G3 E             ... : ?9 s7 q8 a" D8 I: C X) B2 H

* t( o' _% P( L9 _6 A

) R. u0 c3 o4 G# X* h# J         } else {6 k9 Z' m9 }+ I) c# C

0 T0 k6 n2 d% h

* L, `* |: a% L2 f/ p, ]; d. W             ...9 b! i; ]# U3 M/ @ R

9 X0 A B5 ~& u' J4 }- D

B( i+ }% q& T: q+ O+ y: }         }8 B O5 p- |2 T, @- x

* I+ m. u2 {' C; V

1 }) [, ?4 |) W% t1 P1 ?- T     break; M! u0 k) ~$ N9 `( M0 j0 y A

% ?: m: |! w Y. n" F% N

( y$ J1 C) x: c/ @, d7 t 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 . \ x' U. }+ G; E0 b+ b

! {4 W4 b8 K1 `, V% F+ Y

% T3 ~ f1 o; c+ r$ k9 Y2 | upload对象构造函数如下,include/upload.class.php:25: , q+ N: S' }: x; ^) W* |

7 _) t B# W% p

8 X; C5 Y" ~. ^( l <?phpclass upload {. z2 C) g, y6 W: T9 x2 @; ]

; u3 {8 Q7 A- l0 W5 a

3 C: S# ^, l5 r: Y* G w4 ~0 v" l% F     function __construct($_file, $savepath, $savename = '', $fileformat = '') { M3 C% r. q2 O1 V9 Y

# A0 ^/ y4 p+ J( t

2 E1 V8 Y P" c         global $DT, $_userid;7 E0 L3 y- q5 M' m9 ?* i2 H. d$ n6 ^: z; j

+ ~+ w2 F5 j7 ~' @+ M% M

8 B& U8 C" S% W; ]         foreach($_file as $file) {* [4 M. V. w' ~! ?/ `: A. L

; _8 t3 M# P- A# q

7 B! f4 b* O" P             $this->file = $file['tmp_name'];1 r/ u4 W. |7 Y

7 c1 |/ J2 _' v

# w* s+ j$ k. Z" h' Q0 p             $this->file_name = $file['name']; 6 g2 T+ E2 W. w. y- q4 H

- d# E( M) Q" D) m- z$ E* U

; J" c0 y% ~8 X A& f             $this->file_size = $file['size']; 9 {) f( e5 r1 U. d* q( _4 f8 M; v( v

- r! Z! D- C' s& o) ]" Q3 x; ~

0 a7 S; X1 g [             $this->file_type = $file['type']; ; f5 ^6 T: B" d7 p

2 m. _/ B0 |; G( B' a& U0 N! H

! g0 h* V1 e5 i0 n9 }8 ^! b/ J             $this->file_error = $file['error'];# F6 V) A) k' C$ K1 ?, p6 h

' w* q+ \8 f) t0 v; S% ~

9 V N) k2 ?" n: I. |  * I% J- Q( V+ k) j& X

, _1 S1 j+ i( d; u- W

8 o( b, b2 X7 H$ ~1 o         }" B+ f( W4 y- u4 @& Y. N9 |# b

/ t) B% Y! }% r9 A

; O& F0 u9 _7 \/ A         $this->userid = $_userid; c1 S" V8 B, f- w2 m

& y0 b% m& N! K; B2 N: y% ?" K2 \

" ?7 T8 K8 j. \9 }6 N, }: p         $this->ext = file_ext($this->file_name); . {9 t8 B2 ?$ T

/ P9 L! }$ n. \1 m: f6 d

2 @6 ]7 ^" Q- g u, Z! @& @: ^         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; . N# A5 V, V4 c* ?9 L

; \# ~. T+ z. u( {( T& B3 A

9 _8 @/ G( W; t- K$ v$ U8 y1 j @         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;! C6 B% j" k/ z

8 l: S! u' {6 M0 b0 V2 k7 b2 c9 ]

* `* n, G* G2 F6 k         $this->savepath = $savepath;8 a/ B' \$ b5 q

# i6 ~- \& t* l

, x, S7 K0 @0 Y L& \8 i, {         $this->savename = $savename;1 M7 _) O! x7 y& B# G

. {* x# j0 s9 l4 c" D

# `* @2 j7 y4 W7 \ Z$ |$ h: Z     }} G0 U7 s! z6 ]) p

3 w8 V$ Z+ d" \: H. s; k

q/ @4 D u: {, S/ J7 L' C# J 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 2 u9 W4 l& k2 B7 C5 y

9 [! `/ R; G r

+ c4 U! N/ U6 n( w1 e 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 3 a: r- }, r* P/ V) v; g! f: _6 S4 E

& S; a) f/ j# I @

# Y, w3 D- A3 A2 B) [ $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 6 ~: P7 ]4 J& a: B: g; o3 ]

, Q1 Z( h& G! C3 j6 r8 \; w

' f& }4 `4 S+ a; g Q 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: & w* O$ h! g* I2 Z# e

w/ L$ {+ M) o4 y

! k+ ]- }% u% K* G5 o; d8 a! o   9 ^3 T7 k' R2 \: @

; `- `( Y! a9 U3 b) V( X9 p

0 Q) D! k+ {4 p* T& H5 H 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: : E; P$ E. f$ Z/ Z" X3 Q, O0 P% b

! F) g3 C& X/ C* q3 C7 f' G5 K

. V, Y- u7 T% J+ C0 `. W1 u <?phpclass upload {6 d+ p% B2 q4 [2 ^0 q# |

. v" y1 B5 B6 z5 B3 w

+ G" n' V4 Z; |+ K( Q/ _) L9 K5 m     function save() { ; {9 w: U- P* q

2 N8 z; X, M5 W& o0 Q5 V

6 t4 F* W: S# a. W1 m9 r1 @0 a" C         include load('include.lang'); 0 l) J' `' Z2 {& q) C, T

7 @' P5 ~* o u) W, j% R+ l

& W. x3 b; g" t, K0 l8 n/ A         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');- i0 }. ]( l4 w4 F

]8 x$ l$ J z7 h8 d+ v9 e4 v4 h

$ _3 l1 y; _' [: z8 J  7 y3 [3 H$ c7 u; [5 B: H! a

- {6 G9 j7 D/ |% ?/ g. g" V$ K1 g

: r6 i/ R1 r! m- ^$ e6 b4 K0 n         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');0 j/ j: \% E2 P( s

2 n' j. |' t' U" S

8 a u9 P9 g+ A& L   8 Q& F' F/ G, h3 N: j- k# {

" ~) k. V n( K( i. q

- b& I. ^8 C b- D/ I) D3 C         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);, |1 K- @4 h' D0 z6 \" Z

: M7 @5 o3 n/ R# i# M& ~( ]

# s' Z/ s+ ]+ m2 {' K. g! c! Y  ) O. p I' G/ Q) U7 _

5 B$ T$ u# B/ I. B, M4 u

* O5 L/ r* S- l% u0 P' x         $this->set_savepath($this->savepath);2 m# j) m/ A/ y6 C% a* k

l- R& B, |1 U4 n& E

5 J0 r6 X$ f& p4 m% \ q+ _' Q         $this->set_savename($this->savename);( v8 Y8 B" u7 q2 s6 n5 w/ Y

/ J, D. M2 t0 c+ [0 D4 d: U6 F2 U; C

+ X7 P! w5 c( @  7 g( X0 T' g5 l- u; R- O

% h& H/ z5 T1 `! x2 E2 a

l3 O) Y2 j; u% Y# }+ o0 M1 B         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); % v' J3 V$ N; {) S4 ]& Z

$ U1 n6 i3 [) O' C& ]6 l

7 `/ l' z9 P; h& t, ]         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);! z6 w X8 ]" m) A

1 v d& S, Z% k. ^& m

+ ^- X% s @; m) e% `- \" d: ?         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); ^: j) ]' Y+ v+ R0 x) \. T" N

: C1 G, s0 U1 S

$ w( t, B% m' S8 Z' J   3 F/ r9 a: A( Y% K. v

0 y. `; {7 X- m' c) E: j

7 ?! j# {2 a3 ]         $this->image = $this->is_image(); ! u/ o! ]' Z6 W9 B' U- J

4 F- H. ?. R2 O% G1 G0 h7 @

7 y; t b7 ^' ~3 Q4 ~2 M3 J+ F         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); # f7 }: V/ k' R6 p' e) G) _* N

! ~6 V7 l. O( @+ \3 U5 _! T

& R( d$ v: s+ P- A1 X S         return true;; m& C. U( J9 H& A: ] D) u5 E$ H

: a) D% R5 Q8 D" G6 @

C6 g6 L+ f; ?9 c' ~: {     }}4 D6 I: Y; l1 L1 y7 [/ _, k

. D6 ]" w( t/ E+ Z, T

6 [+ a6 L6 n& |- [ 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: , h) Q& x0 x M# m

8 y$ t6 t- ]+ Z6 L, a* g* q0 [

' _2 u( U) }+ c <?php6 e, Q2 f: X4 F, ^

$ v. i4 e {' d$ k

1 s6 G' }. W" S' {" t2 k     function is_allow() { ! C9 W& \$ h2 t( Y; ^4 T. w

% E: ]' u' l& E

& X- v- C) Z) N% f8 A; V& y; _; t- N7 m         if(!$this->fileformat) return false; 7 p7 T' y0 c; e2 H, _: J

7 }4 y0 N7 P/ r

5 w' O1 z8 `( h% o% ]) a         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;* T% v. Q+ y; {% \) s

1 x0 [: D4 [% q4 r1 G

. Y$ E7 X5 X+ W7 `         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;4 V% `% X+ `( c; O

5 v2 b" E& k o/ m4 A7 _/ q

. D- d6 u. U9 {. O" D; j         return true; 9 H2 ~! U6 p: l* B+ P& o

- p2 _- j# I& A( m' N, m$ n3 |

9 \2 o) N+ K, B8 `9 b     } 5 b( j( S5 i/ ]! w5 b4 N

: j! h, i! ^# h2 D& Z

, K6 ]" {5 E! I$ n) ]2 ^( { 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 0 M5 O# F B: `: N2 ?1 C# d" O

5 W V& _( u( M5 t% w ]6 i' {

+ H( _7 ~ k+ ~; ] 接着会进行真正的保存。通过$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文件。 " {& L1 u/ Z9 _5 D

K4 `, w5 S. A$ N! y' L

1 d/ h! f6 I+ j 漏洞利用) O6 P5 l! [/ F+ ]' e# E& U

: E7 m7 t/ o+ g9 ^

% P3 @; e& u' m4 S2 x/ w9 o 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。( N' \& S; ?+ `0 z* |0 [

% \4 h* }2 X% R/ @: i% P

+ c' l! l( P: E5 Z6 O   5 Q2 Q2 `0 P. f: [! Y0 m. S

; M- }2 Y. \+ u* [ a( q6 W" w, O

, w& P+ V9 k' a6 p9 l2 `5 v. I+ K 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid 9 O/ H) ~: D x( [$ X3 B

4 \" \' a& t% v8 S

* c- M1 q" U1 P; W8 i6 h# U 不过实际利用上会有一定的限制。- I& H4 u2 k- m* \# \

2 m/ {9 r U. \: d

* e: e& V. `- ]8 X3 ]; u: B 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 5 M5 j$ n; s" Z8 S g

0 m4 n, k7 I4 B& g) V: W$ `

+ ^$ n8 _1 u9 o! {   ; ]7 q$ X! Y0 |$ ?1 b, E, M3 P0 v! ]

0 C" Y+ I) l6 f N/ K

* g0 D2 t+ |3 z/ @9 k6 @+ k 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: " {7 u8 a4 p7 i+ w/ x

3 y8 f* y: \0 z7 O! d2 C0 V8 i, h

/ K9 M. T- q: e 省略...$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]);省略...8 U4 n8 u+ {8 C1 E- s% |

0 O2 O, q0 i' }2 W1 Q# K# z

. @- W" s8 C. @0 V 因此要利用成功就需要条件竞争了。. T. O5 |9 C. ~% x

3 I6 M' a/ P4 m# e8 i4 _

3 U# o+ l+ ^5 [& T0 n9 N 补丁分析% G/ ^; l! e" L2 g; x/ I1 l1 E, A

0 w4 B1 }9 D& o, S g

( C) c, a. P2 x' G+ R# k+ d; O( J1 j   5 ]" }; _7 G7 A+ U# L

& E5 \0 J0 `8 M7 M# }3 U+ V3 P

3 e$ `0 y& C4 D# B 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:1 S& l. v) R! D6 Z- Z) m

1 q4 a1 Z0 S( [' }$ |' G

) |2 Q9 Z2 F, y: d/ r- a# v9 a function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}8 v4 _2 n8 h5 s. P; A$ a

( Z2 p# W: ]2 d( R

, E' N1 a, Y) F# }   $ c/ O) w2 D2 p4 s1 z+ p

* |& }# |) k% G! d$ |9 b" v

2 o5 R6 `# F% p# W4 ?6 z, \ 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。) @' a$ a0 x# W, s

8 j6 z' O" E3 K I2 ^8 Y! `

+ k7 f9 k+ o3 i" o8 x* A; H: r3 Y 在is_allow()中增加对$this->savename的二次检查。0 ~6 `) ~5 v% _. d

. ~# V Y+ Y+ e% E0 B' v1 M$ D

4 t7 w- v6 V4 R4 N( c& z* ?) M1 Y 最后 8 x; [( k8 ^/ w) T

; g' g- @& }. v7 N' Z% A/ }0 {) I2 M

! Y9 H! T( s5 R; ]; ?4 p, C 嘛,祝各位大师傅中秋快乐! ) Q' Q9 m) B. O; N

2 e5 V' V! }, e! y

" t8 S z& S- O) R& R( g* p   ) {) f% w, c1 M. G4 R* H

2 P* C% E* L& f( {7 \- x# I6 B) ]
回复

使用道具 举报

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

本版积分规则

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