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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
4 H, f. h/ O9 ?- d: ^

+ Q Q0 B3 Y. I/ P

% G9 j3 _* g0 D4 _: q

3 ]- h; o- y5 I* [* X$ ? 前言 + D6 i7 p- `3 }$ e) U

; {2 F- d4 V+ I- d3 ^

1 u- A! k0 U8 S; e2 O 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 * e0 t, q( H- i w3 H& V+ H

" R9 q9 a q n1 ]

* J' y6 U5 o8 p2 s* ^/ q6 o! {0 L  % Q5 B+ _9 j1 D$ A

- b9 _/ n/ z5 {! m9 {2 S1 J" y7 @

$ R' e7 \' i2 j( `3 n. }6 w0 ]) A/ u 漏洞分析 8 j1 I8 b6 x8 y* o

5 E% s! r6 R' N) e/ Q& J) k" L

$ t! j' v6 G$ s# C4 M5 A 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: $ L: S: W3 U) }" h# L

$ C9 p9 M x" g- K

1 C% ]1 D- L w' Z   / Q+ X7 X2 Y# Q

! ~2 T5 W. c, {, ]% c' R( P

1 t2 y: Y2 J, q( {/ t3 w 对应着avatar.inc.php代码如下: ( `, g4 H& g! q$ X+ |

- E9 { }" b+ E3 l Q

+ u/ s! Y# _: c! q5 I <?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) {5 i4 t$ J |- n4 O1 @) A/ p: s

1 {. v9 `) k/ e5 w2 i

3 P% R, Y" D6 U8 }' R     case 'upload':" v, j3 v# [% _3 }2 N

1 R& u* Q$ |! J# ^1 t, ?

- U+ N1 H8 Y! O3 _1 ^         if(!$_FILES['file']['size']) {$ h {9 m6 e! W$ F7 K, ^! s

0 x, B9 @: X0 T) X9 e

3 W4 D* D# r J! n; B             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); 3 j+ L K- F- J! O$ B

6 z" c( }& ^( ]) h$ \/ L N2 _

* j* [/ U; l/ G: ^             exit('{"error":1,"message":"Error FILE"}');* E: C/ }/ d' K+ u& w0 q, h

/ h3 w# T' p1 Z) G" n" Z

/ y( r* b' n! n8 L# ~% s; H5 P         } # D( H7 D4 K2 ^$ M9 z) }

) [6 N$ `5 R$ V- ]) ?3 F

; X( a, o9 ]: N z( r( c         require DT_ROOT.'/include/upload.class.php'; a0 {8 H1 E; H- e' z- Q

% Q8 {& D* c# s' a

7 x, |( f2 Z+ O/ I  6 ], r1 N+ H# w4 z* Y$ B* _1 V

0 ^4 Z" r* h3 i, C) v

% n" s& ?& q( v         $ext = file_ext($_FILES['file']['name']);5 o$ h; }9 h2 K- B. q Z

4 x( K" f7 d! ~+ C

! j+ B% m! Q6 M4 l4 _6 E; N7 U         $name = 'avatar'.$_userid.'.'.$ext;2 n+ A7 t. I& n/ R S4 n0 L* u

8 v, b, h6 G5 S6 F# \" V' a+ ]9 Q

) Q) t, D0 D4 w; v9 ?         $file = DT_ROOT.'/file/temp/'.$name; 9 v2 |/ K6 e- e

' d- n3 l u8 d* U& d* a, v7 S

2 V8 D- @' d8 ?( L# _+ L   3 P6 l/ l" Q1 I# g0 L5 K W1 C- e2 ]5 ~/ H

& E' @8 x4 V" i4 Z# L

$ a: ^. f+ ?1 D         if(is_file($file)) file_del($file); ( B, v- [: S% K) \2 A1 x

+ ~* x, V3 D4 E2 w% [( h5 V

9 F3 S! \- Z q: _7 I         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');: n4 ~* j a9 v0 f$ U' c7 D

) {4 d7 Z _. z$ F& }

; ^3 V: g2 t U( W* f7 W. b  ; e, Z0 e' O& f" v

0 G4 _6 {( }1 h9 n* x

9 d5 o5 O! `% Y, M$ ?         $upload->adduserid = false;% A2 \* q p6 `- t2 d w

) S* ^$ ^: d8 s2 m

# [5 l+ v! Y! m V   8 {+ F) a* y- e% m( n

; P. x) z" i9 @& Y, X6 g3 x3 C

" p Y7 C: T: b" |1 A8 c         if($upload->save()) { x+ }' z2 _* l1 h! Y3 n5 x* q

" q: e& x: p, P5 z3 L

1 o6 Q6 O+ \7 z" r             ... . e3 x3 W8 d5 G1 G+ W$ M+ D

. W: R& p6 x" E0 Z- x$ g1 B

7 @' X \ c8 R1 ?; Z         } else { ! }, z' o; m _0 r

: B) J; N3 K5 ~* r

8 V: E4 V1 J% p5 C7 u6 X- S7 i7 u             ... / x, X$ m& i8 S5 ]: j, o

. o J- f/ ]1 v9 s& y. Y# N

H; `0 W3 m3 K3 x; t! A# K5 o         } % c- }' r6 N; ?

' l, l/ ^* C1 V" H0 l

" g+ A8 h2 y8 F& Z, N     break;9 c" p2 q7 X3 F* I* G0 ~! f5 N0 {

1 j) r) W \' {/ q! W& p2 G

" z9 x- I( J. T8 E" Q6 h9 z 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。# |3 y0 l! F8 T% F: W' k1 s: [

7 o: n5 z1 d. Z/ n

3 y; Z% n. L3 ?; w7 k( t upload对象构造函数如下,include/upload.class.php:25: . s+ U4 ^, O( v8 |

0 n- \" h8 p: L4 Q% r

& V1 \8 D% `& D5 b8 _ <?phpclass upload { K9 k5 @$ d- ~- {

O+ Q- e, _& t4 r

0 N% e3 I9 s! b     function __construct($_file, $savepath, $savename = '', $fileformat = '') { ; }+ n- {& e! f) B

* _, R7 W/ Z' C/ _3 l5 |

4 ^2 y6 z6 h: h, Q7 z3 T1 e. i e! e, O         global $DT, $_userid; r7 n8 f8 S/ ]1 V: s

: L7 B8 q) v$ F

/ ~$ F( B. p" F* E7 n( @         foreach($_file as $file) { * Y$ w. H! W0 `1 w0 t) ?, W9 }) h8 y+ m

6 [ j) U. D! X$ H7 Z; @# E5 d6 ^

4 U v2 j* \+ ?, u             $this->file = $file['tmp_name'];( m. M# R. w6 D' N E! O

0 |7 Z1 y$ g4 E) M

* K3 A* A8 `" O* N- Z6 k             $this->file_name = $file['name']; 8 E- {6 ~/ R9 ]- o% c0 s9 Z

! p0 v" Q$ i, `0 B3 g$ k! c

% {, F! X, M/ w+ C, i2 V! Z5 c3 x. l             $this->file_size = $file['size']; : m) J0 ~$ N- o5 r; s" A6 u: i0 V

9 I9 S6 ^2 |9 h U% \$ A

' `7 i1 j' s* H             $this->file_type = $file['type'];0 V. O: k! s& g4 S5 L

* P/ z. `8 T* W. H' ~* m4 q; r

: v4 J( H: }8 z/ k" J             $this->file_error = $file['error'];, D' l u; M+ s) ?% n, d

5 g* B: A; m( e7 J

4 @1 W+ d& B/ ]+ @2 {( S: ~   , Y" c8 N0 G% ?& j; s% s. R# {

5 v: k; c* n! Z4 c F

( z+ l$ r! J6 h2 o8 _9 X2 Z         }, {6 e9 S8 O! P$ Q! C% F8 `

* ~( A& @# T4 t; {

8 h o' w4 Y- Z/ T+ j# z: P         $this->userid = $_userid; / b3 m0 }7 v" a, ~+ L- L

6 @0 ]5 o, C9 w, t$ S& z( w

! R8 `5 O/ W7 `5 T         $this->ext = file_ext($this->file_name);8 _ m. c' f& D9 y- C9 Z- s" V

7 n7 P4 d- E9 M4 Q8 Q' H- L

# `! ~% J* n% R" Z+ t         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; $ y7 ^$ g3 f8 @* f8 t

- }( h) j8 ~, K$ ]

( m3 m% z M9 @/ k0 o/ ]$ x         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; ' U, }. ~( F# j: o

# M) G! N& m* `3 A) ]% U9 U

9 Q$ y+ R- J# R1 c5 F% O         $this->savepath = $savepath;' J6 \8 }. S9 c( Q5 O6 J( N

. @9 p- k2 A k7 P

" _! z% n9 D2 n( b* [8 J$ q         $this->savename = $savename;1 i% z: u: S$ Y% I. z; }

4 W- }+ F- p1 n7 w/ z+ h: x- @; Y

. ]! \$ n. T( U) v+ d- T6 P! f/ I     }} . j# X) q* I; s O5 Y/ ?) g/ L( D6 Y

9 V; k4 W# Q$ [$ y

, a8 d; C p5 F9 {# Q* H- g 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 . [" W2 z) x9 w4 _9 R( I

7 H6 c0 M* _+ g5 {' Z0 K) s* }

/ S/ o! b j% D4 |+ g i; \6 x* s 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 i. @* P q; i; q5 q" ~7 {4 ~

+ [- I- |6 Q0 n8 _, \/ Q; \3 _ T

' z( [- b& r* a7 N' H $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 - b3 [) |3 R; L& Q5 e/ M

4 R4 X# v2 X! N6 |

, E* a0 ~3 x( _ _ 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: + j0 D$ M' a0 Z/ Q/ t

& c0 [% w2 ~5 f, ]* a4 H9 C" V

8 X: Y$ y; Z" h& J4 e5 d3 W3 E  . X! I$ E3 ^$ T' W4 z6 d

! c& H4 d9 {, V" N$ b. Y* i% d

3 O% X6 {0 ~& U1 D# _1 P 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: 1 ]7 f- a/ x3 K" i0 T9 H; Z

* M+ p* z4 _( R9 i! z: e

4 I! T2 `' v) L7 K: D* r <?phpclass upload {$ y6 U: V! T. N2 {0 Z- F

( F: x$ _1 c3 i$ S

8 A2 M) S# P5 F8 C Z     function save() { % S: n! Y" \* ~0 b( \/ c+ R

4 _" m1 C: W( K

& L& e, |# h) k         include load('include.lang');- K! r0 V& y' t+ V$ V$ M9 ]8 C

& }+ {% [$ V4 N

7 _, B3 a/ g& X: U. t5 b/ M         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); : e$ S; ~( e8 H1 k c! g2 R+ ]$ N

, S0 W' V8 Q' {; C

! l3 m5 y4 s) p  - J8 o' Z9 [% {- E+ @

4 {# k% S {' t- [$ ?+ q

! b, q" ~, o( o         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); % t2 J: R/ q8 v) R8 u, ?4 W( ~* H

, _0 V$ z% P7 X8 Z7 L2 h' [& x/ \1 X

! x, }% f: ]7 s8 g   / u$ E# g/ ]# o' o4 e8 \

6 v$ n, W0 s8 D% J, R6 X) i$ y

/ ~# J7 v2 ?- ~         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);6 {+ F; B" W/ s2 g2 U( v+ _

* n1 V0 ^) l+ |; H& q) C$ s

[3 v6 Q( d+ S |! T   # M/ H! ^' p' [

3 a. P: w6 Q4 t$ s

2 x( U$ W' `6 N1 V- S+ m         $this->set_savepath($this->savepath); , g3 l5 V" @/ Z" y5 \& y

?* ]2 l+ C& ~9 Q" U4 m

3 Z% r7 N4 m) ^2 \         $this->set_savename($this->savename);7 v+ Z4 x2 G- D) k$ o! A

( e ?, B9 Y' Y) W8 e: j

4 m6 s4 G( O; d; u4 _, Y  ; _7 ~3 o: o f4 {* c

) @3 H. M+ e9 x- o) i

! Q& ^' |5 r; J4 U7 Y9 h         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);0 D- q5 q, G7 [

( @& G* _6 r% I, Y: I( m$ `

- c' J4 N. J2 a; O# i. L7 f         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);9 v! I1 s9 Z( [5 t9 @& @

) z+ F8 h! H5 q' O7 r0 p% U

) D( M4 {$ d5 g         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);! ^6 H" F3 c3 _

; c7 P! j6 V+ i7 H6 t; z. _ ^8 A

( L& ` X9 W5 Z' ]   $ A8 i: X1 _5 h: `# u' F9 K P

+ ]9 Y. |0 O$ e8 n5 |" @& R

9 T+ f+ i8 |' F3 a+ H2 Q         $this->image = $this->is_image();- |9 s4 y: n8 P

# E& N; V/ p4 ]6 H# L2 [6 l2 b7 ~

4 A, z! d: f/ u2 g- _5 r         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);# ?6 C& p) x" i& d; ` g* S

3 M8 P5 J' B# f+ M$ `8 \4 g

4 S8 @, e! o8 ?7 Z' g, D) F         return true;1 U( ~( ^& B4 h6 v/ w* y, x+ _0 m

, Y' e2 R/ N: X$ {# D+ t8 U9 {- ^

+ `% R% V& h) Q, S- M     }} # Q' S( y( x# q& h# ^9 r: Q; b

$ n W: Z4 A* m- b: N

, y) \ t6 D% C1 T6 ^$ z' M 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:6 A5 r) ?" o0 Y" }' X% U9 r3 b0 y

" _& R9 P* S1 H

& D) }3 M: }+ }& z: D \% { <?php; c4 n k. g2 Q+ O/ V# x

$ w- a& g5 G* w- \

4 t X4 w' v, z$ H" \# c     function is_allow() { ' {( _7 M9 m$ ?0 J. C

' J: n% ~% N" G" `. `) `1 B

1 b+ t+ ^8 U/ i: C         if(!$this->fileformat) return false; ! p& D# F2 ]1 ?# d; a4 |. J

; Z1 P5 j6 u: V: k/ O) K8 D& {

3 J5 ~8 E; h0 q" y* @2 e ]9 t" A         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; 2 J2 J7 N0 ]% J' S3 m ^

) D" q( c1 X1 a

3 c$ l7 f2 T. z# s# t: |" 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;6 m) M- T8 r9 u @$ }6 l/ b

2 l7 q- |6 ]- c U# s0 w

z1 D Z; R8 W1 o' y% I         return true;# g% c7 I* X% B8 I$ Y

9 Y5 d8 J2 g+ `6 m* O

! J7 h& [9 r7 c. k: a+ P, v( B$ j     }9 @5 P( e+ O. b4 u4 u

9 L+ d3 X4 X' i/ d) d/ {2 S; J: \

! `8 O8 g" d$ L% _* Z' J6 D 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 5 W2 V1 Y" g$ v* c

: i/ P3 {! ^" P5 d u

) L4 v! ?! K5 }! z& o8 O 接着会进行真正的保存。通过$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 }- Q% f1 T0 }0 X. k. t- R$ d( Y

3 o* j5 w0 r- M

4 Z+ O6 \8 e/ K 漏洞利用7 P' H% c; G& O; s9 E: M; b0 T

: h( E6 O6 L" `# H1 I5 a M5 L

7 T2 }3 J! s5 U" A 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。+ h% x% P# y% V8 B

3 G; J8 P% V5 \+ l

5 h* Z5 `4 @* t2 e6 R. k- v   & b" @& R5 }5 u4 W/ T, P! T+ S o

6 [# W$ [8 y" R o, D9 p

! A) A3 T `5 @) h$ G 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid: U( z9 {, x" k. C* o# z

# v f( A9 @+ b# x

" e! G# [% v) F. c# B 不过实际利用上会有一定的限制。- s9 ]3 c7 J, V, N2 c; P

' M# w2 r! | Z, t' `

$ b) W9 F- ?, P0 } 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。6 C3 _" p" a9 ]+ Y. I( o

# q, x: E4 l, g) w) C

" T! F7 A: \6 z( Q* s   ( f+ }. `/ {% Y* {, r$ |. s

, }4 R. ]1 \1 R q0 m4 w

" K& t6 |. C0 N6 s* K( x% i 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: ( p" [1 Y" z3 k2 a% f% b Y

3 u# R2 ]4 @, S/ C+ X

6 F4 s4 U' r y( \ 省略...$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]);省略...( F0 `- s% H0 g; n) R

0 b( O- k; E% ?0 s

# }- u# g$ d' Z0 `+ d+ x$ z/ k 因此要利用成功就需要条件竞争了。 9 K) i: X, S O8 l# Q# l

6 b: |. \* T4 [4 O4 t6 l

3 K$ O7 ]- \& P2 }$ M7 V, m 补丁分析 , t) Q* ~3 T( O: R2 ^+ K0 W

" U! n1 u- R1 v, C. G) T

4 ~5 v+ `7 [' b& W7 B   * l9 m1 j5 C/ A$ \- J. m/ r

3 r" r5 J8 [6 ~0 @1 d" @, ^

( ]6 [8 H) M, O' w O 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: ) E9 A# O* m( H9 c0 O

+ s3 t4 a3 O C- ~

( w$ N; z G1 g t2 K3 b2 X& A function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}* e- o7 }1 G# V$ n* ?8 }

4 F; s7 s- } z

, {/ g3 n% y: R& A  3 T; w3 m$ X) x: M8 A

8 X$ o6 f" J& ^4 g2 t' }

- Z( n/ C$ A# @& |7 t6 N 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 ) ]: P' ?7 J- N6 L2 b' Y3 _( W- \) f2 |

8 }2 ^: f0 t, X4 P4 K. G

' h3 D4 u3 M# s6 V7 A2 H# | 在is_allow()中增加对$this->savename的二次检查。; C- K$ s' Y0 c" D1 @

L9 H* D5 E6 V1 l' ?2 r

. B- P9 K+ q# ^2 o' i 最后* W! O; a0 l, X* v3 Y) x

0 c; J/ P. R: c$ A0 x

! f9 _/ X3 H* I# a 嘛,祝各位大师傅中秋快乐! % S @0 A/ F( r- f9 k

: w9 \3 e: d0 S% x7 R/ P) |

% U; `- P4 w* K; Q   ) l J1 O9 G, J/ i% g9 k2 |# S

( x7 j3 b* m }, t3 v* Q. I$ S
回复

使用道具 举报

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

本版积分规则

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