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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
0 F; Z3 i/ h( S8 f5 z, N4 u6 m9 c

, t+ t) j, s, T' E! M8 Q

$ Z- P( T m( F& J/ y# |. j

2 l( C7 W8 x3 d/ y0 c' I 前言/ {: t# @+ M! I/ Q+ Y

# G2 H* m6 b2 K4 o5 q5 A

7 ^& G7 M! l; G 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 0 P% d' g+ i) z p$ H+ E

# m9 }1 q; T0 Q1 h1 A9 N2 h$ a" e

$ R# A+ F3 U7 }- x3 o1 |* l/ A  ) W: r8 ~, h7 `: [' Q

& e2 X+ l8 Y0 V3 g- G

8 |5 P" O6 u1 L' |" j& c 漏洞分析$ r- s Q) s s/ N: I) d

' @% q* q6 f7 l6 m+ E- b

5 J1 L0 g* I2 F- j8 n 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:- J! b7 {1 ~) s

7 ?; j# f& w8 p

4 v4 j0 A: |3 P. e   : T8 |! G" `$ I: }) q- p1 {) Q

) ~( `& F; M) g z! a4 g6 G

3 ^2 l' u* \2 [3 t 对应着avatar.inc.php代码如下:; _+ n6 n( `6 C, _ J0 o

; U4 s. V7 A/ H* L# N

7 M& W }; v* Z/ v <?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 r$ d' ?' W0 ]" w

' q: }. a6 R7 ]) O

+ T7 i6 J& W- r+ @" B. U     case 'upload': $ Z6 c$ r1 @* G5 c5 I2 j

/ {: i+ n4 e, D- n* M

, n- p7 ]! y# R7 ?2 U         if(!$_FILES['file']['size']) { " z2 Y. c) `5 _; ]' ]% s

\9 c) F, _% ]' D

8 l4 v* D6 D- L/ j! N             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);' ~! K- m( g& \5 _

! L6 }7 Z/ ^: ]# v/ t4 F' z# z

# x. i% N" E. z/ ]. d             exit('{"error":1,"message":"Error FILE"}'); ' {# Q- |2 j4 K- k+ E! r+ H

% c) {# i6 D! ?) e( V

0 e) E& O y/ s0 b" f$ @* t) J         } % g+ B7 D. ], B2 g+ A

/ w, C4 n/ m8 Y: w) l9 h1 Q( b

- v! p# V' l, j9 U2 I         require DT_ROOT.'/include/upload.class.php'; 6 m8 q- g4 k9 R0 h3 g: L

7 L: d. K- {! m6 d2 e0 |* l

- q) d; a7 f( R/ ~ {0 _% a  6 A, Y, J+ u# x. f, r

A/ o# i) a7 {0 w4 x/ v

8 S+ B, z2 G8 }! U6 n         $ext = file_ext($_FILES['file']['name']);" b3 S' Q( l0 ] ?+ J% V

. D6 N' ~' A, K

0 t( f P% d% m5 a [7 j         $name = 'avatar'.$_userid.'.'.$ext;$ f: \. X$ M, H' D6 Z3 O

# e# Z/ X+ E& z) `; `- n1 e4 O

! W3 K! W& {" R. E6 W! D/ v/ _9 c         $file = DT_ROOT.'/file/temp/'.$name; 8 g; v- Q9 V5 E' `5 j% _6 F1 l% D3 F4 g3 t% i

8 C, y) o; l3 i9 N

6 h. ^3 R5 r5 l9 x   ; e7 T; k- v( P8 N1 w! s$ E; X' P% \! M- \

) ^$ c4 _- o2 s7 V; Y# H$ S* F8 G

+ B! X6 b' D5 x, S, \' |         if(is_file($file)) file_del($file); ' b8 B4 A; R0 S

- p- A* Z. I6 U+ @- d

/ l6 Y# ~# J- {7 B' e. p         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); 7 F- M7 J3 J! L& P' b3 c6 l2 ^0 n1 a- \

. s$ y) M( @3 n ?0 O' j

& H8 W9 d6 U( Z8 ^" b   ( ?1 l4 W* K2 M5 k7 g; n

" o; ~% r% S, A- Z% r; [

6 M P3 x5 N! @6 C% F         $upload->adduserid = false; , s4 w5 P7 e6 m- `9 d$ L

8 @* c* ]: {5 X7 J

0 Q# E, }1 f9 C# L7 Z& N   : K0 ~" Z8 `) v7 `

6 a7 g8 E$ w' y. L' ]3 R2 e

% e$ M1 j- h$ u% K/ W         if($upload->save()) {! l# J3 Y* X, e1 o. H* _

7 g' f5 l( T. J/ Y7 V, u( U

3 u* J. Y. ` C7 E5 D- j0 T; D! B             ... ) m& Y& o4 f$ Y6 Y& s- v9 ?* q6 P

6 x& \7 {7 d. S: R/ z

" F; h4 Q$ n1 e; l6 _; A+ k$ P0 h         } else {3 g! f4 ~* s' S) C) a- z' H

3 ^( e( \9 S1 D- G! P3 d8 ^9 m7 Z

m% \/ p% G+ H% D" m4 b A             ... & ~6 F6 {# ~) ^- g' t% g- ~

: ^ ~: W2 ~1 k' S

P( U; }$ m4 d/ X% Y$ [: j2 d         } 4 ~. ^# G- y: ]) M% I. l, Z

* N1 z3 ]: T; i/ @5 s

* l1 C0 f( f) z     break; $ f: V) L# r7 l& r$ C

# T! u; T! ~9 s- N

) T5 F' m$ ?9 N p 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 5 r' N: c, L" z; c, D# S

. m% ^; w3 ~) D" y$ R) p. E

" m$ z9 [. f/ T9 M: V. S- q upload对象构造函数如下,include/upload.class.php:25:+ W4 h8 M/ d8 A! \, B* C& j

) z! w/ a7 B8 J7 J0 X

5 J6 v9 m) _: ~. s Z1 r <?phpclass upload {/ w E& r0 a: N1 Z

! h# F& U9 l: L9 t) A! t: R Y) ~; [

6 S. A$ o C! A) n0 W/ p     function __construct($_file, $savepath, $savename = '', $fileformat = '') {0 }6 k! C2 `& a4 u( O

& Y9 c) i5 G: z

+ B$ M- r2 u% t5 y         global $DT, $_userid; 8 `* _9 m8 B7 {4 u4 W, v

) i9 O0 n' Z" X# Q- S H2 C, o

/ f! \% c8 ?, w- W7 [) v e* W8 c         foreach($_file as $file) {- B( R6 v/ o1 L9 F6 a; ]/ s

6 N/ |# z1 ]( H9 U, e: f* O

5 A- V& X* A! s7 F             $this->file = $file['tmp_name'];" Z2 m- D- o; ~, j$ Z8 E

1 L- ^0 I. v. ^2 R

1 I- M! ~, d- C& y# Q             $this->file_name = $file['name']; ; B3 p; l7 k8 j- {. M7 [

! [8 |8 \, s/ b

% x# N" y4 s) j) t             $this->file_size = $file['size']; ; ~# }) B8 x* ~4 _

) n0 R+ v8 ]" t9 P3 I% P

4 O i8 ?& W( | J0 K             $this->file_type = $file['type']; . V: W; G d% M$ t+ g$ g

+ \8 m* Q0 t8 U( U

- G& _' q8 o5 @8 H1 f* F             $this->file_error = $file['error']; ]! E& T! w3 k. |+ s( N4 R' U

( y2 v' j0 V' k% f/ f0 @

( i7 Q% W- G6 G7 p   ! U- m! U8 Q2 r* i6 r6 ^' B) k

9 s% {+ J- s2 I1 B/ V

7 B9 h3 ^5 |, O: o" s c         } 7 f6 O/ I1 V7 g

( S9 [+ P6 q3 f9 o$ j* @# V

; L# O M$ b7 A) M: x* O         $this->userid = $_userid;: X8 t# e/ R: T; g

: K# w# X5 w+ W4 N) q, O

- E8 t! M _$ H0 o4 d' g! l# D         $this->ext = file_ext($this->file_name); 2 n5 V- N* V5 _: S/ Q! g9 l

2 \0 l, h7 M+ X$ @

, ~) Q9 q6 h& w1 h         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; . l5 o* d6 O/ Y2 I* \* R3 J

. Z) c9 x; Z! `8 s$ `6 U

1 E# _; Z, u) q! z8 v* F         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;9 Q; x. _6 I1 n1 e( E! T

+ G' t% X' F" F% ~- j3 e

, B( l- d3 q4 C         $this->savepath = $savepath;+ s& ?+ W6 v: Z/ J9 K

4 A- k& i6 ?. }: J( r# ~

% v! H+ {& l4 w5 a! Z         $this->savename = $savename; U/ I8 @4 ]' z; S; J4 K

]' W6 z1 E& A2 y1 ] q

) k/ I/ I% H8 f6 u4 h) P4 m. H/ k a     }}1 k# a$ {) s% n1 W2 z4 p) r

& @* {4 _$ h# [% Q; C" c; j9 D( u

9 o2 k9 j2 [9 v 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。2 @2 W3 Z; a* k

. J+ i c- G+ M$ E/ { m( h

6 o: P+ S1 e. v5 l- P$ k( s0 h 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 ; `8 t6 Q- x8 ~6 O% r, Z

( l, ~( R! L* v

& y& Q0 F% P+ u/ s $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$ C& Q7 Y: I, Z6 B

! s( i6 O. C% c

/ Z+ g4 h" X$ c. g 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: E& N5 s2 [% A

, r- W& O G W4 U+ V1 ~% k

0 \* W, x) J2 A5 ]7 m. y   : q4 ]# @9 @7 l0 ^

' N p) j' n4 F2 O( [, Y, K

- E1 @5 A) L, ~' |5 C 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:/ c2 Q# Y- [9 O# Y Q+ N! q2 Y

% r; `% `. l) J8 m- R

# ?: e, ~% C% Z& m <?phpclass upload {' B! q; W1 ]% \1 D) p' a" h e

, R9 j/ s$ Z5 u% M% S. N& I$ [5 x

4 Q3 F( D' i6 Z+ } `$ }! w     function save() {+ m* y+ x' H1 V

1 I1 L4 f& l7 |

7 T( q1 K$ p. y         include load('include.lang'); 8 p: }( d! D4 ^+ n6 B

+ c/ d/ ]0 x1 Y% p& V H

; ~: u+ T6 P( w4 x5 C- f         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); * q6 k" z3 O4 Z* u* ^2 z

, l L* s. J7 V0 j9 L

: O6 ^ ]7 w+ u6 V4 l `! U5 Z. f   0 c1 F4 Q4 r9 S; S- u

, ]8 [' K" j, [% T

/ E# T' {4 Y# ~9 M% g         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');: v4 v& ?! p6 E$ A& D

! o; [) }6 l6 m0 A' f6 Y

2 v# \& s: p) }7 ^# c: N0 j* Q& k  1 o2 B% x7 p% T9 a! k0 Y

' w* u6 O7 q3 Q6 {% u

9 F- K8 ^- i2 F         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);# w9 B& b- o& m, {1 Q6 W

* c) a ~/ ?. o( v" y4 P" N

, X+ B' Q, C6 k5 ^  6 f% `7 }- b% F6 |; c

' r" s& [6 q" ?& Z9 i l: |0 f" \ ?

+ s# I- Q* N0 K+ g8 M         $this->set_savepath($this->savepath);" D! B" C8 G* e) F$ b

" o3 a0 Z, u8 c4 C' |

7 C, C: F' O0 O9 }8 V8 c         $this->set_savename($this->savename); ( j6 j2 F, Y% ?& R F

( M# [3 a: {$ m5 w! y9 U

# j/ a% {/ \! {( c$ ~" ]# l' n/ N  2 H! r: {) I# a! w. {5 _8 v7 T

. Z4 u' S$ r; j: G, W" e/ [1 Q

4 a3 q, \2 Z, I$ P! M         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); + U. Y* v8 V7 A* s( u4 \

, p$ x: f& N c) S" O

+ E. j7 J: L+ `# ?9 j# F         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); + o T5 M z/ R) n- V1 ~( P

$ V1 X; T7 L; q: u

- W( `) N( \) P, R6 a         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); 6 A! w* P: v8 @

& Q3 e& s! q- H

$ {' Q, ?' W O! G9 ~4 t1 w3 e# x   9 n1 \& x& Z. V

8 q. E7 C2 e, j& I

* Q) H5 V5 ~, J+ j ]. X         $this->image = $this->is_image(); 9 ? B* H2 q; t) S

* q0 r; Y2 p: P }

- X u' v1 x8 N3 A' Y# L Z, W         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);9 c3 g& I' ~7 ? `

5 L- Y* h8 `0 @; s

1 p, ~, q" t2 a3 V. Z% f         return true; 9 c7 S$ h0 [. M

5 T- b9 x+ V+ F7 P/ A* ~6 y

; q+ K3 q' A4 `4 Y2 g     }} 7 x v2 S% I/ ~" R4 S1 T! Z7 c

6 p, t# d3 w, G$ X2 g; ?

& F9 m: }) M# e" r' r. } 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:" Z: X/ e, K [6 w

; q! [) |; b9 S" y( {9 }

: U. k+ m9 K7 e7 C' i% D% i+ D <?php3 a# G3 M1 V/ f+ Q0 ^

. z! l* y% L+ i1 l/ e: Z1 |! e

! @7 ?( y' m5 \1 B* J9 I" \6 k4 E+ s' D& {     function is_allow() { $ M" E: a# \9 a9 y/ \& e& ~

0 ]$ r; k; \% }: ?/ n7 a4 W* Y+ \

3 Y2 ~, f% q: M) X         if(!$this->fileformat) return false;# s$ e! x% A- K3 D

) s1 C% x* ?/ B5 }3 ^" n

1 L& d5 R& }1 n4 r% ?9 g4 [         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; 2 W# v/ I; X# n* U1 C) P& s* d

* _' v, v8 a o

9 c" c6 E& C2 n% Q1 I         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;/ }7 B+ z( M9 M

( B( X G$ S$ i

6 {5 V$ X( U4 X* o9 T9 n& a# ^         return true; " [* `0 ^# A3 D8 ?5 m5 K! v/ T

- z) ~# h5 O6 L2 j7 z8 u R

- _+ o @$ \6 T0 v) N     } * {/ w+ ~2 E0 B; Z4 g

* l/ M1 s% t: w w7 Z

( Y" G2 S+ u4 o3 A O& u, H2 D 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。$ m' G* Z$ z S2 g" q5 V

% L, r/ x( q+ e7 X' \ _5 p

" y! H5 r5 Z) d0 x( D" I7 Z* T1 w 接着会进行真正的保存。通过$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文件。9 Q6 V$ g; Z, i3 N- ]

. t& s4 d% N2 O* c) C# d

+ d, ], k2 Y, p 漏洞利用0 u; o# X1 [3 N0 h. T

3 C1 Z8 U4 Y- j9 X( C1 S

& y( u) J5 r H 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 : j8 a# ^. y6 [0 p- L

6 A7 y/ S3 E0 m7 a

5 i" p' ^( {/ o, z$ F2 C   ; q8 _- J7 g, \% R

3 s9 Q8 p/ n |- X- l |* u

0 G# B& ]9 n: T/ o' b 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid ) F( k+ I n& I; J+ ]

# p0 |6 @4 }3 z

9 G+ c* K5 s9 F7 j& g" d 不过实际利用上会有一定的限制。 ' t- G9 J0 |4 C: \1 a6 ~% ]

% X9 [& O' H- O X

0 `. [/ S2 X! M _7 j( n& g/ V! @ 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。4 Y9 j7 y% q- R6 {8 L p4 p

/ Q0 z9 \2 h9 v

5 E5 `& {9 J8 ]  + ]5 _- _7 i* S3 [9 \4 {' e B

5 B5 [3 f( U+ Z3 d4 a, `

* x: w' e: j- n$ T! d 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: 3 i8 N8 Z0 K1 ~. i

: H3 X$ D5 } u2 b

, d/ c W9 J; _1 c9 x5 Z 省略...$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]);省略...( l/ {! e& M9 @/ z9 K

7 j5 H: `9 B2 K" G4 z2 u1 i

9 x9 `# D' q, B2 @+ A 因此要利用成功就需要条件竞争了。 * f1 R0 b& K! v( V( ^# {# m _; z

4 g8 {# [5 l' w A2 F2 w* f

/ x2 P& K: N! M* a- a% e 补丁分析 . Q3 Q- N; G) q) F; V

' O# f8 I) L; U9 g& O" Q

0 P8 z- } H; f- s1 a   $ c9 M! ^1 e) g" C

; E/ w. x( T. J% t

# t7 {1 m0 q: h5 ~" z3 J l' Q7 N 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:% d3 a" `& j3 G O

+ Z. a7 W1 k; d) u" Q& \

4 v1 ~6 C0 S8 F function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}/ R" r: [# Q6 P- D

7 K0 p1 L2 J: v

7 Z: e6 \. K! M/ ~) a" A  8 Z6 D7 J. _4 K' ?5 i

$ V. r9 q6 @& D/ R7 w) J

2 [( h& N" g8 \# m/ f/ @ 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 1 j5 Y$ J. b& L4 a3 X' U

# p; d/ y3 {6 A% k

d4 v4 I! g' \) U7 A& I$ m# B 在is_allow()中增加对$this->savename的二次检查。2 q* U" J l- d+ m, X6 D

9 ?( D$ L4 T+ d- F# k0 u0 D, t; e) Q4 C

* b5 c( {$ n" I `6 X8 w* S 最后. W/ E6 N& `$ l* T

; J9 B% E1 b9 c( I, Y/ {9 g4 I- K

' s5 E( R5 d) ]$ X 嘛,祝各位大师傅中秋快乐! / v- k4 w8 R0 C# B9 G, ~/ a& }

+ |1 W6 l7 l2 H4 J, A T

8 j9 F* H+ k' L F   1 @$ @ R# g7 T- j0 ]

1 I- T4 F; u& v2 Q( |$ ?7 o
回复

使用道具 举报

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

本版积分规则

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