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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
. q% C, G6 q2 u! r k! U) V

6 D; r& O5 h- Q' |- Y

' w$ _( P+ K$ w5 ~0 I0 }' u

. X2 H8 t8 J2 I! F; n/ x 前言 t, Z( R2 y6 e7 A0 J" v: b

8 b$ e U: b" U6 c' Q5 t

% i, a2 j) U+ o; t& D- M- ~3 S6 b; H 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。8 f" F& w& p- D

5 [5 [2 m2 a9 Z" C

6 A6 ?6 o( w- q% @) d   4 _; n ]; a( e* A2 E; U" [

6 d5 R( I; _: O) r( n4 j/ M2 B" m

7 V8 ~& d' F6 \; K& t 漏洞分析 2 n( V$ T" n0 @7 H, v

8 `" Z6 N4 R9 I! O; m. D/ P. f

0 d; G+ X. e4 M! U! A: L 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: + w1 y# {; w8 h8 M# V7 Y

- }6 L1 ^& m9 q$ K% H: R8 _

$ I/ i: J3 v @$ f" |  . G: w' c+ X l2 N% M

4 q; l* V; H. b# X! t2 a3 v3 [

* |+ o0 Q* w/ l$ V/ P% c' z 对应着avatar.inc.php代码如下: ! z4 ]: E' J- E. Q% M% n9 }& H

8 n8 F, P: f8 L& q2 |, n1 h* T. ]

1 ~) P9 T6 x. i# f <?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) {% `) B4 \3 V& }& S {9 f" R. R

( L+ g% p U7 L$ _- G D

1 G) ^, Q! R" X9 i c8 D8 z+ `     case 'upload':' L6 J# d6 {! H+ m, ?+ R

I" L! |. Y: g8 [7 F* Y! b7 \

- Y- N4 _" ?% T1 x: r. W: W* O. E) c         if(!$_FILES['file']['size']) {( |+ y4 W0 M( n$ l, s) s- [

4 r! }4 Y2 a, k5 A

$ H) N# R: W3 N; c7 ? J' }             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); 9 \) I* f# F7 s) }0 M; A2 \

/ X% {. n# |, z( t8 j

; {$ M: Q; }9 {6 w/ p5 j             exit('{"error":1,"message":"Error FILE"}');' w' [3 z; O0 V

& U) _$ ~" h* Y

o; Q8 e/ I; }& n+ ]         } 1 e; V0 p [, R3 w! \& c

4 h" c# g& L/ L2 L

" [) @$ G- M) N         require DT_ROOT.'/include/upload.class.php'; " x& m( }! V! S+ L6 S/ C

7 C/ x, l, Q/ i) h2 f3 a) W( C) P

8 O6 g$ e" L4 `   9 q4 I1 k: S9 D9 Z: L

5 P) M. a% H# p e* l0 C

4 b& \/ O* g+ g9 ~1 L' \; r5 h         $ext = file_ext($_FILES['file']['name']); 5 t$ C4 [# J! l4 N% x- E

0 @9 w6 T+ t0 ]3 ~

@/ W1 q; W* [ j3 Y         $name = 'avatar'.$_userid.'.'.$ext;/ i1 u( B* Y9 `3 a: H4 H- d6 E: T

/ w$ W5 x2 ^0 q8 N# n7 O

" }1 F/ j5 v4 ]1 l7 {8 n         $file = DT_ROOT.'/file/temp/'.$name; ' ?5 G) M' s. G8 f. `

g( P! L2 N6 ?2 m! G* {: g

0 i! [7 P4 a: ?5 P. f   / I( a& C; W4 z2 J

! x3 Q: |- J* n: ?1 Y- g# d0 n. D

& k6 ]8 f; t# c; q6 R# S2 c         if(is_file($file)) file_del($file);) h9 `& Q8 R8 p1 y; J

1 ~% h& y* f0 H' ^5 V3 g

* b4 d( Q' _, J, r! S; y V8 T         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); s3 D& _4 [7 W0 K' E

0 F" Y3 b4 o Q

$ P+ L9 x7 b' I! [" H$ {0 O   + W0 V6 r2 T% h1 l7 T' O

. }6 v9 R6 t% P8 f1 o7 i

2 _) n- ^8 N# s         $upload->adduserid = false;' J x6 Y% d& W+ m, m! k& @+ Y' y

8 r8 p2 a$ X6 n1 ~

1 C+ f1 ?# _9 L6 r   1 r3 m$ v1 d+ n4 e& C7 J+ W+ j

9 Z3 v) W I' e H1 J

; D4 n; e& ^) ^9 r         if($upload->save()) { h% o% R1 l$ ^7 w' s4 C# H

1 z. m5 \& X$ o5 t

, N4 ?) |4 h$ _3 n; |8 Y             ... $ w% W" O$ B$ q9 c8 D

: i# y. j5 L e* Y# b

$ z0 B3 ~9 f. F% w9 `         } else {# ^8 h$ P+ _5 P4 N2 U2 Y

# Q6 U) h, K4 q1 _+ \ F

, k' O! j" m1 W2 Z0 K; y+ J8 d             ...+ Q# x6 S" b/ Q) W6 \4 I% ?6 t

, w# `- F1 j8 B+ d( ]

# d6 j2 m- m8 c N/ y: ]9 a# S         }* I- q% ~' g7 r) Y( I/ J

0 T4 M0 |, a# x H% } R1 @

- }# E9 O4 T7 g. d     break;4 y6 R- Z( m1 s5 V4 g

3 f+ z. H) q$ C7 q! u {, U

. v4 H! V+ [. p3 B" _9 ] 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 2 o" M# H) ]% `, ~1 p

; F9 y$ H4 X2 O- _

9 z4 S- p4 o" Y5 j* {1 H upload对象构造函数如下,include/upload.class.php:25: # ^( w/ l7 K6 |

$ a: w( H% V6 }' l; V! L

. W! \" ]4 _8 P7 Y <?phpclass upload { 2 k4 H9 E1 S) _& x# L f2 z, V. h ?2 \

: Q# T# d v+ l" s* m

, U( C1 z( y8 V" b$ C/ A) E6 C$ b     function __construct($_file, $savepath, $savename = '', $fileformat = '') {& _ ?$ e* J0 Q

' }% E) X u5 v K: |

' D# G0 g3 p5 d         global $DT, $_userid; : W9 n2 ]3 c4 N4 h. j

2 j% _ ~) k& ^( h6 ^ V7 E% o

$ a$ s- t5 N. \3 w* f$ }9 Q         foreach($_file as $file) { 4 v& A" m0 J0 G4 h( k+ l

1 I/ ]' P" w4 H; o

7 {7 a4 Q' }2 u5 a! h! p+ i1 t             $this->file = $file['tmp_name'];; v& `6 s, J: `0 {3 j# X

5 J" y( W# n g( _7 r

0 E- K Z4 a! j0 k7 a% s& M             $this->file_name = $file['name'];) E2 Z" b' i' p+ q4 H) X7 b

" n: Z8 R! Y Y: A! q

5 n) R8 M/ I& L8 s+ l7 I0 g' j             $this->file_size = $file['size'];: }- M9 Y& o# M- h

7 \6 a7 \% t$ r# z( [* \! s

' z' K' |% ]+ ], x/ l             $this->file_type = $file['type']; ) h: @7 y1 [, L' _4 v

/ F4 x# b D& ~

# Z0 p" b' M* A9 t- r             $this->file_error = $file['error'];/ N# h* ^. T+ v7 f

. G d% r, o+ U+ k

M/ S- d6 T$ B V. n5 x Y   " J: ~( s) ~" X Q3 ^, ^$ }+ l

3 u' ^" \9 x( l$ J+ J9 [2 F/ W

3 D- o# ^5 H! N) v5 F: _* [8 |         } $ H' V# ?6 w- d8 f% [* Y

" m2 |9 N1 s$ c$ R

, ?/ U: n2 G5 v }4 w' g! _" _         $this->userid = $_userid; ( o$ A" R) X; K, o- I$ F# `4 E, R

5 t( n5 u0 Q" t2 L

/ E" q4 m x% `6 N, P         $this->ext = file_ext($this->file_name); ( K+ r) a: V+ a+ j, j

- _9 x" W# Q% i( G

# `# f7 u6 `. Z1 [( e* f w# a         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; % l# f' ?1 ^. p3 j ~8 X' q* E

/ K& }4 D: N3 Z* G5 f$ }& f

0 j N" ~4 S' r, y+ X+ ^1 g" @. I( Z         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; ' S' [- ?9 c6 v( W' ]6 g( T

) P8 M$ V7 i; H

' D' ]+ j5 {! o         $this->savepath = $savepath; $ f* z4 X1 i9 p" V

8 [) k" O2 K4 O% l8 v

- w' ], _ }* z7 \/ z) o         $this->savename = $savename;. B3 H$ x) E& Y! ~7 H6 u! {- M

; E# J$ |8 ^7 m

. q _1 x; w) }) J9 h& R n     }} # @3 `8 k. P! C

# Z4 a) u0 r0 i. x N

1 W- I6 v3 |3 v V8 o) J 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。( x* y+ w: K1 l2 R9 H

' K, R# r- N% K* j% Q1 t6 }

1 M( @- x e& B6 ` `( b 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 & \! V4 j9 ]) m) @, x& s

7 \" u& b2 f1 M$ ]% c

( Z* @$ E4 j- i $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 , V9 C" k @* _4 d2 A) I" V. f

1 ^1 A! Z6 @8 {5 c

2 ]$ D( @+ n1 S) W" ]3 z 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:( ?2 y$ {+ ]" l- S

* I1 B' [0 V% d% u

! k) c; [5 j; |, r4 I2 \* `. U) B# A  " z* x2 N/ R9 ?; E

. V5 ^: j& n4 \" O+ X2 s$ O

1 t1 Z5 D% g" t1 E( f 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:4 N5 N5 f* K/ Q2 o, K7 L/ D, F

6 s8 s" K0 {' w* x9 j1 H0 C4 t! b& a

5 D M, f! A% K( l <?phpclass upload {% ]* u5 r" E5 y4 @7 ]( R5 F: P6 v; \: ]

7 a* j% W" `* ?* T

: D' `, e" j1 o0 e     function save() { ! R/ G3 @' S) X3 ^

; X+ E: U* ~& n- v' N# R# l$ b

2 W U9 K" u/ V z3 [: c         include load('include.lang');! p& N( Z6 b3 [1 |3 D0 z' x5 p

4 m3 o3 z A) S

3 x$ S: V: D& G" Y2 ^         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); 2 R2 ^1 t2 \$ l, `

4 ?9 R6 D7 T( A+ X& c3 e6 }

$ k; [9 v" i, S; Y9 ^7 X9 }( X) A   2 N h6 u9 u' b1 O5 T

& J3 W' w6 B+ v/ z6 A( r

8 P: F, A$ c; ~; H7 _5 O         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');9 J" B7 w# u3 r( k) a

5 L5 O; o! F; K2 g

/ h" K% b( ?3 J4 `- j  9 p1 J3 d- C9 y# B! M

6 F1 O0 m) O- i6 u

& ?$ n, E3 ]0 ~2 c% F4 g& v4 Q         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);; j2 K# ~$ P; |0 L

/ z: V* J4 M3 h

! W u3 ^% E- m0 b) o3 R   0 `. D" j7 v( j/ q- R! n6 t4 a5 D

( w8 Q: ?7 n# n( ~" n/ I

2 T, r+ b& D8 N D; I         $this->set_savepath($this->savepath);3 f5 ~3 ~/ R* i$ P1 q3 c" k, n: D

; z! |3 z8 O9 |9 v

% s$ U% i* ]: v         $this->set_savename($this->savename);9 m9 A- O" j, `) p5 }

7 o8 B) f# p+ K/ y8 K

5 [% V# T# F0 a+ r2 q   : i% G0 \1 e3 X$ M n- g- J

# I6 C) p- V- @ _1 h7 K

' V3 N- V6 U% t) S         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);5 j3 R8 Q. J1 [$ i* M0 V1 k; w) R

$ D$ H& o! S6 P# J: m5 H% i

2 ]1 u' n6 J& ]0 m4 ^" i& J         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); 3 h: d \3 }$ n+ q2 w% C' Y

9 t% l/ m3 O- Z. @6 d5 V

8 K/ N3 i9 q( ^/ U* ?! [# ^/ f         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); + S0 l7 B& q% Z, u8 L

& i1 H2 T# e/ g" ?2 ^

$ q. |) U$ S& w   + l- ]# c7 f4 B; i

. Z2 _" u |9 r

/ T1 g. U$ c6 g. b         $this->image = $this->is_image();- t% w" u4 x ?

' f$ U" T8 i0 F: g+ r

* S+ r) _6 `& M# D1 t+ W) P         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);; B3 \( u9 V/ E

" w Y+ z3 J9 I: R

% Q `. n& ]7 k! s: x         return true; , X, o$ ^' u: \8 E# ]2 B

" f, Z; L, O7 P% T2 ]9 y3 h

" @% i4 x) Q. }" o     }} " p, e* H2 h: f+ C' U9 s7 I6 ?

7 G; `+ K& ]. z! x' D' H5 {! y

: z7 q# x+ Y9 ]4 `6 B 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:' X. }# U. c2 p. X e$ P

9 l) @& q! Y5 i

. A' O' X% x& N' ~! n2 M <?php 7 E( ?) w+ ^/ E; v

, p8 B/ f- T' s: O# z5 {

5 p3 {. N' ~. f     function is_allow() {7 w3 T+ r& _$ R( L

- A$ I& x. T+ Z4 V' T

8 J3 a+ v W9 n) O( s         if(!$this->fileformat) return false; ^2 R; |! C* a* L: @0 K

7 q" ~# Z8 G5 Y( g, c" G

( t8 m9 a. O. H/ y+ C4 H$ K {         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;0 ?8 B& b; W# W/ t" V" n6 W

N$ B; ~: Y2 d6 j0 p

, T8 t d4 o! T2 W$ K# U         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; " K/ z8 a7 c0 u7 \

9 u' N0 u5 ]8 c( |

1 t+ ~, E( z' o7 T {5 e& w# N5 ~         return true; % y9 l, h* Q B! v& x

1 _9 I4 Y7 O$ o' f2 t! A

2 {. r6 X/ |' l7 C' o3 ^4 f! A     } ( U# _' T0 E1 l9 D5 W- {: M& Y

, T6 f. `% Q8 c- K' l- {

: b+ T* X# v, u' W 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。* i% I4 Z1 n, g0 T* j7 B' x- ~" X

( f4 K! ~) K1 S, H6 f% L

/ L1 F8 P4 p9 E 接着会进行真正的保存。通过$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文件。8 i$ z/ O7 U3 J, N8 Y) f

0 \/ P. C7 M* a- j/ u

: ^0 I3 ], L9 X. f 漏洞利用 ; f: f" x5 F- N' k1 \" v' Z

3 g4 u+ _ u; J" v- D) P" E

9 ^9 S. n7 Z- c0 k 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。2 d" z# z/ c. _2 e4 }7 P) r

) r, J7 }+ o" J

5 K; \+ }+ b. J% F" ]; j+ }   ! W) A2 l* ^* S3 M' R @* \

1 \+ ]0 p/ U! R4 g/ X

0 k' u9 z2 }( t3 H" e 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid ( {3 p% J7 {/ a0 \

, ?9 m6 Q6 \6 c5 s) \# h9 g

! g4 d/ [2 t8 y* d 不过实际利用上会有一定的限制。 0 m! i9 L+ q8 B( f

. r$ t: S2 T0 P9 m; Y

6 e( r0 }4 D) y. | 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。$ N# P2 _3 b7 ~0 |% C& u6 B

* D. @6 c1 [5 ?7 a

% m4 N8 }! E7 u' P7 Q! g& p   2 C0 K8 U& _# G9 t3 _. D

( T) q' T7 ?# r( B8 Z2 |! j

0 k8 M7 C* @2 ]2 b3 `8 M1 }, h3 y 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: 7 ^# j/ Q( G/ m- h! ]$ m; N

. W) v" w2 F8 w+ a) p1 k

r {( ]0 H1 `% J- u, @! y: Y8 q 省略...$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]);省略... ; e- C# r% ^' B1 [4 I5 j# X9 z

2 x3 l1 C0 u5 ^+ f) V P: g

/ y- y' {# u3 a* U6 A 因此要利用成功就需要条件竞争了。 * R; X2 R- Z- u B% m: ]5 a4 }

* b% C- E$ p7 @: w9 v

/ D* s5 J7 m0 y* ^0 ]6 ~ 补丁分析 ) W9 g: ^# d7 A5 W, @

3 }* x8 e2 T) `: U" Q5 S0 L4 K! e

! ]9 Y) |: Z* y, E$ R9 F! D  # n( m8 m1 z% g. Q9 b

, @$ O# ^+ Q. Q& k6 V0 e, s4 e+ A

0 @2 g' V) E& [) L; [0 e- ?8 z1 v 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: , W# H! ~1 T* ^" |

* @- b: H y: u6 U ~% I

% m5 |9 h, W5 N9 V8 ]" w. R function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} 0 n$ J0 G$ r- g

3 h. q. C. g: f( {% W: W

[" |! b$ l: \0 `+ j' `/ U   * ]. W2 s0 M; r: p

3 Y- [3 @' ]* g+ _$ c

) D [0 _. V9 J: q/ X7 ]! D4 v 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。) M/ N3 u6 v: J, {, y/ r3 V3 v

2 e. C9 c {$ r& U3 n% x+ d+ W9 |' I

3 D' h: r: E. Z" ^; t 在is_allow()中增加对$this->savename的二次检查。 " t. k' }0 U% m9 g+ }" N9 m; `. y" y

9 s6 n$ s# U: {/ ~7 V8 T5 z

3 U1 Y5 P" q& `, C, E 最后5 i1 W3 l$ }7 u0 \& s# b9 n

8 m- i* L; U6 S

* ~% ?6 g0 A9 w7 v2 m% t" X3 o 嘛,祝各位大师傅中秋快乐! & e9 w8 y! V- R$ U- U, d2 B% Q

% ?! v7 T$ o- W2 [+ n! i

) l; h8 S3 n$ _, b   . q+ u$ j* y4 g) _

: G! F4 {' M+ q; Z9 w
回复

使用道具 举报

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

本版积分规则

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