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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
6 Y; D2 ]7 L" |% q3 [

, m! m/ M3 W4 g! z# W# X7 c" `

$ c5 s" |) r* b) s3 m! [

* z2 Y9 p3 R: l, R 前言* J& J' V/ v# b

4 O8 E4 ?3 a. }

$ Q8 T+ B; A3 u) T3 B' Y 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 " e3 y9 F! ?) |4 e# f( q- n

: H% U9 d& W6 B6 F$ V* Q) ^# m) y4 Y

2 L0 X% F* u1 b# f, F5 n% @  : p, r1 V# \* k" R# n/ l0 c9 z

( g5 e; f2 Q4 }3 D) z; o( b2 g; S0 c

# q! g: f0 v8 Y' Q, U; Z8 R 漏洞分析4 N$ u+ c: S; S# G4 g- p2 B

* [1 x k- _$ o: t

$ S% d0 o3 e, w4 [, a5 J" i g 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: C/ f7 b' r0 S5 f- b6 ?

2 m9 [$ ^/ |7 d3 F! h* ?

9 i! x* p1 k3 M: X8 u* @   7 ^5 {4 P/ t' }7 t) v, D& S2 F

- Q4 l8 N% ~6 u7 A( y8 U

- X& i5 `. Z" @# n: w 对应着avatar.inc.php代码如下: : Y+ y) O/ A8 w! L" Q

+ ^0 N$ R$ g0 ]% p% K

/ y3 i5 r# A( 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) { 0 n, T1 ^9 Y3 z/ b# y3 q% t5 f: c, x7 p

^. e+ q! a* Y$ O0 q" ^" Q

0 A7 T P$ K( V+ w( _. d     case 'upload': & \& p& L9 c' ?+ q

: J$ t; l1 F& Q! {

9 S& }* g0 m; ]6 _( B         if(!$_FILES['file']['size']) {2 U5 S4 w, M1 X

: g0 e" r* {8 j3 n$ E' d

+ ~% {5 X! c r9 j6 m' p             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);/ Q- n4 K& G/ `

8 x# x0 X3 v4 i T& ]" V

" |7 r7 I8 X& F6 S; U             exit('{"error":1,"message":"Error FILE"}'); ; d) f& O) j: j& x# o- ?

, @: L( ~! ], x L

1 |2 q2 f( p! w" O         }4 u: l5 `% P, I, R$ N

+ t+ m& X1 p. u5 X/ w# i2 k' `+ q

: P1 W7 c$ W% `         require DT_ROOT.'/include/upload.class.php';+ @5 H: C. g) @& t. V

8 K1 ~; i4 Q4 P* w

4 p0 B4 W9 t' R/ b& l7 ~2 ]. [  & z; T& R4 ?: C& m

8 E$ }! U3 `: _2 g+ J

0 z" N c0 n" j: p' n ]! a         $ext = file_ext($_FILES['file']['name']); & _% W% k, F9 u: u2 v! u

0 C% ?3 t) a8 |( w

5 f: T. a) j+ O         $name = 'avatar'.$_userid.'.'.$ext; + M) }0 }. l% Q( L- o9 e

8 c9 I$ V9 _* ?' k L+ _

% }, n j. B7 e) }         $file = DT_ROOT.'/file/temp/'.$name; # B; w2 ~5 k& t: b

4 Z# n* i/ s* ]( D7 d

6 h2 W) Z: q# G! Z  - b( w. v9 c7 k. w4 r/ a

8 [, L* K6 b1 w

! h t5 K% _+ I |4 o         if(is_file($file)) file_del($file); 3 r' m8 }: A9 B6 u6 T3 F! U

: R$ P( Z p) a- b1 \0 M2 z

. h( k$ x. n4 a1 h: \0 o- m         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); ; a) p6 E- M* c2 d% p8 [5 ^9 w

S' s( @% C# v' Y6 g

8 S' B8 s+ i+ A- b4 s   2 W& y/ r; N' a6 @) C1 n6 J9 U

; j- @/ o" q1 W% d9 t

s6 `% F* p# J& l* Y. f5 E# h% T         $upload->adduserid = false; % B4 O# x' _3 X

% w/ ]. W- u+ y7 o6 S

$ C1 x8 n3 G7 U9 w# ]" j   7 i6 E4 D E9 L+ J+ K4 o

7 ^4 B7 N, q1 `+ a- {

6 e: ]; |) T( l( J; M5 V) [         if($upload->save()) {: O2 H8 ?6 A8 H& h+ a& {1 f0 M/ Q

7 l* F( O( ~6 \! ^1 y! a. I

# f1 t" P( k& V3 `6 u             ... 4 b0 r, S5 J6 O; c) |3 p7 @

* p, k- {5 ]8 u6 c5 ^" C2 S0 `

* c# z. T" v) b4 w/ U" P5 N         } else {! Q. i8 M- A% Y5 F8 ^9 J/ ~

& Z- M7 V1 y, t' O; D. c

- s. g' D& P, @& {$ S             ...: B5 v9 L; v* H F$ B) B+ D

* i) F1 J8 O2 w( M$ F6 {

' u# j7 s, m \+ y         } 9 x$ O" q' X; r

# Q- j% G# v) T1 p4 n

$ k" k+ o7 Q2 b- K     break;. W$ O* `3 y+ T5 _( t

& w7 `) _8 O2 e! s# u1 _# M

1 g* H6 O6 _( ]+ J, e0 a1 c 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。# y$ }* P$ J% X$ n! `& H3 i

/ c6 c9 q1 V4 x3 Z8 V

' W- G4 Q, N9 J upload对象构造函数如下,include/upload.class.php:25: - T" T3 [% p; b+ U. [. a$ b

! {6 \, }9 D/ ?/ x/ W

5 L' _/ s) V e <?phpclass upload {, s0 z3 r b. A* N& l2 k6 J

8 n9 ^- M& P& a) I/ ~' o1 r

- z% ^( o7 u* h Z     function __construct($_file, $savepath, $savename = '', $fileformat = '') {4 Q' U$ d- f" C: p

' ^; w+ Y: q- S" w4 z' k$ R1 F

& w+ D, T1 M) ?. K0 ?         global $DT, $_userid; . i0 Q4 o5 m0 `

5 M+ x: a2 H, i) u1 @

7 q" y |+ D4 h* C! L' W& F' j* j         foreach($_file as $file) {& j1 e, s" X, f8 N) J9 P, V2 X

- R* \/ h; `5 L7 _ b

5 i. c' F+ D) d7 R             $this->file = $file['tmp_name'];1 `, K4 m, D! V0 `8 o

. n" ^+ [6 c; Q6 ]

4 }$ i+ A; |; ^* y" O N! @7 I# t             $this->file_name = $file['name'];& c+ ?; r* F$ B& M* ]

- x/ }, k& ~9 ~ g$ a# ~

* O. S' l9 @# o3 Z3 f1 N/ }             $this->file_size = $file['size']; + V* m, M# o7 F% @

# D* `: w4 W6 C& |3 b) r# a

- J u% T; {+ h: |' |! V             $this->file_type = $file['type'];7 b8 v" K$ H0 J% ?. t6 s# A

/ U* [) l3 F" q2 C; j

( _: u" b0 e, J( @) e. c             $this->file_error = $file['error']; $ Q5 s: z, f& a( V7 J: t9 {

5 Q! p4 S& g. g5 ]/ K8 u

, `# R* W& [2 H6 d) r   " f7 A. C+ |7 _

6 h# g: l B& d2 s# T

: n9 \, n( G2 B. h8 z         } 2 U: u9 y1 o2 ?/ G/ Y* J+ T

$ o5 k: D/ M, R0 n1 S/ ?

/ d% W3 a- M, u+ ]4 a1 K* V         $this->userid = $_userid; , O T6 G& n+ j! T' D

c6 k, X7 N: V

9 {. \; i7 ^" g0 s% r6 o1 b" f         $this->ext = file_ext($this->file_name); + }. N% a& a# v N+ F- e

# @$ A L. x& B! e( W; p0 |8 R

, A" n: A8 Q0 |" P2 _         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; , s$ P! O8 l/ j0 {- e$ @9 u0 {# C

0 @0 w: X V5 q2 k

* I+ T1 I4 W. l) k1 Q) ?2 M         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;- E+ o. j. D. k) s7 x# v R6 D

% J! {4 a7 L8 j) v4 X- M. Q

6 x9 p/ s+ ~, `7 ?3 j         $this->savepath = $savepath;9 K0 A2 r ^3 C+ |

$ D* E: q1 k: |( k

6 o- ~ e" t" d% n+ _. F         $this->savename = $savename; " ?& a( n2 g2 g2 m+ b8 L4 r. i! F8 I

# k" g; a Q* A& t

- ?4 Q# H: a5 C1 t     }}) f' Z5 i; O( M

3 ?7 E3 l+ I6 E' N& F9 _( G0 z4 n/ U

2 l Y9 m4 @' f! x. P8 n 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 1 c/ f& L' g7 K5 W) v7 O9 \

) f( R( E& F s$ w

) N8 T6 g2 n2 i* n- y8 V 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 5 }' X6 d/ |; h5 q, h

. J& l* E- a7 d4 l9 A8 w

8 }8 o& {5 J6 [5 e, I S( O. W $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.php7 `, u; t! v) z. \: k

7 c" t# _* M$ k6 G4 c

8 _! v- l" Z [( {3 v- ~# L 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: ! j& {8 J% ^, ~ e

! G: F A% x2 h# \9 }

! h4 z* o! _" t   Q- x3 l" y) V9 E8 `9 u2 q

) j+ A' X" H4 U! i1 r' e6 }: {

7 L4 s( a+ P) l M$ I$ r5 P' { 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: ! v2 s5 _) }0 \* x

. A' q7 q0 c# s2 A& v' t7 c `

( h' }! c8 s( X$ a4 A <?phpclass upload { ( ]# ^/ h1 |6 d# b: i% c

6 R* w8 [$ W+ U% K; M& c0 u8 f$ `

) @1 N* h7 l/ d; W8 y9 v     function save() { b; j4 r1 V4 G9 h$ w

, [ b- M' G! b

" ?- G1 u- Z% H0 D% t         include load('include.lang');+ l9 O4 S) h0 K& \+ }2 F" j4 y

& [8 g' q7 Y# R3 Z4 _1 @

% g: p. n; k, b$ r5 i' e& E         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); 6 `0 G- u- m, {" \& ]

, h, A1 X# P+ K4 {3 X) @

1 V$ B( l" n( F K I' Z  & f8 d* H6 X8 }) b" P

5 \+ E9 ]( f3 p

% w$ L2 g5 q" n' h- _! n$ h         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');, W7 ~) K( r8 E6 P3 U, N* c

1 \1 s. ~6 f4 r+ Y8 g5 ^

: s, J! r0 a+ f   9 P1 M g% C2 N: Y* g

3 U4 e- J1 O' N" ]

6 |) ]/ c3 D0 |5 A3 b. M* C! ?         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);8 v6 T4 [& x0 k" ?$ Y4 F8 [9 I

4 e1 D. P1 l7 a8 _* l& c# I; y5 J, ~

, ]$ ?; @) t7 y( j* |9 \  ; M' _4 p E& {! h5 N- H0 L7 A

* Q5 W9 P( e$ r& P; P% b; N

; {8 v0 u7 ?0 r2 c$ w         $this->set_savepath($this->savepath); % i5 G( q- @2 A/ `3 K

5 [1 U v* v0 Z/ t( j; M4 L* c

- B, {5 B6 f8 p# H; b" r- s4 d         $this->set_savename($this->savename);, r" M3 r# D5 w( v7 _/ Q

3 A( W0 J( _ V1 s! }

8 }/ T, {) g0 B7 k b! M& {; J  $ x% ~3 U) K0 o4 T# q- r' a

9 L. x: |# h: z. J6 Z0 ~2 D

. r% w. ?, {. V         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); 6 Q( D! w' d; {3 U, L. R

2 {4 S" ]* R. ?

7 t7 r- L% |7 j% N5 a7 _         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);0 g4 W2 o' k6 f' J2 ^; ~

% w6 k1 @/ b4 @" b% t

6 f+ W$ S' B, ~) `" j' M         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); + N8 s9 g r6 u3 `4 p8 f- {7 D; L

* ?+ i4 y5 l* | a; F

% ~$ I/ K% s d% ^' B% p( }   0 g8 a+ G( W9 S6 @

: A( q. A. Z) Y, d$ n e; M

' W' ~7 `: {3 W9 Y4 [' k         $this->image = $this->is_image();" |0 Q: ?/ m. i4 O4 _% M

2 v' S4 a3 Q2 O6 t

5 X' {$ `. F, h1 I+ r2 ]1 S         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); ' o3 M4 r& k, z. C

- X: d5 I9 b$ g: ]+ l |8 D

3 {- }, }/ Z; E4 n         return true; " @1 ^* A2 e+ G. X! D% c4 y

" w( s' T3 ^; H! D' j, Q" m

, ]& ?6 L" B( ~* W$ V3 Y* u     }}3 }' C% v2 Y) V7 _( t

" X" s% Q# \1 M1 {

9 D5 t1 r% [, C0 R 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:3 F0 `1 N) b7 z* y' W1 O

8 o( u5 ~4 R' k; W" ?

p7 R" G- Z6 U; E( \3 n <?php 9 A8 j6 B$ j! x" Q

7 Z2 x9 B6 W: ~, p

3 O- D+ J$ b( c- I: j1 y/ p     function is_allow() { 8 k: U ^0 Q" X$ \" ?

8 o) T& v3 M2 s" W7 D4 W

- j5 ?! [' m2 l* l         if(!$this->fileformat) return false;9 E3 h6 |! `% I0 ?( W( w

$ k( r6 u: G7 d, y5 w) I

; l" [! P: G" m$ m         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;$ j& S# U5 e2 S# `- a& f

4 U) G% H! \- X# v J: X" v

9 D {3 g/ c4 T! b/ Z) ^. ?, R         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;8 L8 L- m6 q0 Y) `

2 y C# k% n0 e2 ^3 `- e

6 A; H* x, C8 s; w5 \         return true; % N# q( U: h0 B+ b9 L, Z. _

' E5 e6 Z1 O. A8 P4 S9 K

8 V" B4 ~! M+ x2 N0 M; r     } * \2 G! R3 A7 d& s! ^+ e; {, w

1 N9 D- I+ R$ ~; o

+ I/ N& o0 j8 X1 V8 f0 _" X% l8 t 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 0 r! H5 ?( A, E0 ~* r

9 z5 g' D, {: [( ]$ ^' ?

B) C) m3 n0 V3 h 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文件。 7 b" O- j4 _; w. u

s5 ~6 b9 V& a

- X+ `, g$ ]' A4 | 漏洞利用 - J+ U$ Q' j) E* S4 N/ H

3 l" g( ~/ ^* _$ h Q* s* E4 \5 I

; w, I1 T7 b3 W 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 + x) M9 w+ b# f$ w3 d e$ q

1 p- G) Z; H8 @7 ?+ K

- k z2 g. d& y( D; t9 E- d( @1 R   L* @; U' ]* D" f- c4 e

8 L/ c# O t( D! p/ k& q- |

( a& H/ p4 ?' H# O0 A 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid o4 Y) d. c9 ]& H% s# g/ A

9 v5 n) S2 j m2 U1 y$ `

# A) V' U6 R5 i! q) i 不过实际利用上会有一定的限制。 7 M" r% ]* @5 ~. ^9 Y

. u: |7 r# g" u' |( m

! v: t' ?. y/ u9 c$ W 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 ) ^( ]! D( |5 P- F+ Z

2 u, M/ s3 z7 V1 c4 m+ w" W; m2 D

6 S9 L2 `" ?& B' b9 q  + B/ g! `7 P9 O/ K& P8 l0 h1 m6 S

) b; v |* C* f7 G" `; b

" \0 p$ w/ _7 J 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: 9 S9 @% ? w' w; K4 y) m

8 D, h. {& d% T7 ^# v

1 b% X) D1 U% {4 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]);省略...5 e5 N' U. I# u' E! f# \( U3 U

* |( ]" j3 e& p. T" z0 y4 B' v( X

" o9 \% x) W1 `9 ^ 因此要利用成功就需要条件竞争了。 7 p- M/ d9 a \/ A

6 A% r% R0 L- E6 E! M

" X2 |/ w# l1 T( M% k) F+ U 补丁分析 2 l) G) T" B- a' I

& f) a6 [- l) u O* C

0 m$ r* D$ O0 P; \( _- E  ; s5 }8 l8 U& E

2 O& H# T& \7 @7 ]

( U2 b# a) ~: H 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: # K' ?' k7 B- F# f* d' f

4 e& p2 U, I( ]5 g! \$ S

, B" Z/ J: K" ^ h6 U; D function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} - U3 B* c& U9 j# g

+ U7 @% h0 f* D& b* i$ c- U5 @" @

4 t5 Z1 I1 } |3 T   # P& Z' b( M q% ~% S

& P1 @+ H `* }& i$ w# C) p# O) O

) V9 Y( w# H; U 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。* X8 q) z- p$ E! o

, U1 P$ z T7 U

- t; s, W3 `0 q2 O8 Q+ z6 ~3 s 在is_allow()中增加对$this->savename的二次检查。4 v1 q5 ?; ?* [ K

7 n L$ K$ P3 ]

% x, X- t$ p- K- L- G 最后+ `) {- V# m- M8 [% X$ v% C

! ^, D" R* p& s. p$ P( Y! u6 r+ I

- C3 r$ ]& a% H) R+ V/ i1 g* N 嘛,祝各位大师傅中秋快乐!2 n( ]# @4 ^/ r$ P! G

D H( Y) X: n6 E: u

, [5 ]8 F# a) ^' D  $ G0 ~5 I+ U; \0 d' l

" M/ N$ W2 k8 j( e+ C
回复

使用道具 举报

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

本版积分规则

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