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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
0 a ]/ i7 t! I3 w9 k- d4 z

4 I& V z0 K4 O9 A3 `+ N4 c

" G( w& Z7 d6 d% g0 @2 M# V6 }

5 O, h6 l( f1 N3 }$ b4 o 前言( ?6 W1 a- G; @) f, {, H3 T

# J! e) O/ R0 \

6 E& s7 s8 j+ `, F" A9 t$ ^ 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。7 h! h; I4 n% W. h* p8 v

/ J' b! x4 [$ c' F- W

7 l1 Y% L9 ? ]; Z' b. M! j: r! h   8 C3 {% r9 r1 L( h. \$ h/ j

' m& r0 D- G( Q

+ f& S, z, i0 }0 l) c 漏洞分析 $ o4 W8 T. q* G! E% f) K

{* h* F# I: g4 G

' R6 M# N+ f, ~ 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:$ q* T+ S5 P3 b3 Z5 c4 g2 `% Y

0 |' f, s. p5 C+ }6 b

2 @# F9 t5 s$ I  / Q$ O5 f* C" Z- C! u3 \

/ @; z* L' y1 t, C v8 q

- R8 }! {9 j# m 对应着avatar.inc.php代码如下:) m% q7 i8 A- k6 X# X! @) J

. U6 Z8 {6 w% Q* h& K

0 z2 s1 h4 U: Z, u: ]+ p <?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 j$ n1 v5 L$ Q; P) j( @7 b; |7 H

8 j/ L. A* I. M5 l3 p

+ B u! Y2 Q7 z, f: I     case 'upload': 4 m( X5 F* f. X7 {& [9 Y2 V

0 Z; ]9 M: |/ d+ t6 z7 b& o: ]

8 M. {0 V) w# W$ o1 ^( q         if(!$_FILES['file']['size']) { % l. a+ |9 j) f! w! J

! B% ~9 ? q! M/ ~

! T h" e2 Z' `2 m) ~$ a             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); & `! ?! ?4 C9 m- p; d

1 a! k4 x( |1 r! Z* A

8 o! f- s/ h/ J6 ?( U" f7 X- ? H             exit('{"error":1,"message":"Error FILE"}'); 5 B. L4 N) I, y/ E% V0 Z" ]

7 T- A) A! L/ J

W! Q9 j) H* H7 B/ ]: X, r         }. _2 k! r$ z% f$ r

& [) p( d1 {3 J$ H+ y/ x2 u

7 W' k. p& e: ~" G; `% u# e         require DT_ROOT.'/include/upload.class.php';: G" h$ W( W: J1 K! l

. v& V. l) Z' C0 u7 B

0 N' V6 A) A$ |   " k. n7 x8 d0 ~9 `" }4 t

2 ^5 h$ X6 d4 a+ u

9 I0 ~; a R4 S* g. \4 C         $ext = file_ext($_FILES['file']['name']);* @4 j- V, V' T& r4 {; D3 p& d$ v4 f

" r8 F( z; P, M1 e' H" _, _, A B

. c9 r: J' O5 B7 H: O% _         $name = 'avatar'.$_userid.'.'.$ext; " q& p; g. [5 e

: Q5 C+ W9 t5 i' M2 Q) ~( F( X+ \

8 f5 k0 o) x9 { s         $file = DT_ROOT.'/file/temp/'.$name;4 ^* W! D5 Y. R) P+ P

, B% a4 o: d0 s3 t" Z

9 J# @" ]1 A0 M( m! E  4 e; z, f' o& P; t; r0 t- @

% V; f: B$ E' G# O# Z

/ ~; ^3 H# p& J: V; i8 {- H         if(is_file($file)) file_del($file); ' y& q1 C/ j+ S$ Z) J

0 u1 V: T4 t( V+ E* ?

! ~! ~. p& d+ p. ` |# T' I         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');! T! w! n2 s- V# j

* i0 i- \" O6 j, e. q/ I

: S7 ^4 U5 [& s/ q  " @' Y! l# g# V7 Q1 l

; \) A) v% C" @" C

# _( K- _4 n* ]6 J         $upload->adduserid = false; ! \* C; [& I0 D2 I0 m' [

! J C( Q2 {9 r0 w4 O; L

( `: l# N2 O* ~7 p5 d: X" F& ]) [  % o, b$ s- b7 `8 }

5 l1 G7 ]2 b9 ~" g) D( g4 c

) `" l4 o0 F& C/ T% m% ]         if($upload->save()) { 2 O. N6 @6 b$ v \; r, {8 [

l( C$ h* q, n0 T% x

. h( E/ ?# {5 o1 U; i5 v r3 u/ Y& S8 R             ... ! N: m, g& _9 s8 b

# N5 P: E% c. s3 Q

/ ?5 D+ X$ }7 |0 m, f) x: |         } else { 1 ]/ m9 [ Y0 M3 P, u

' K3 u/ j* _# T: r/ `; X

: _9 ~; }- b0 o& H' t( E" z% N             ... ' N4 o# J/ u- n9 f0 C) E

/ w1 T9 \% M5 c- |9 D! }- d! m

; [& [2 v1 d/ C' w$ }6 o         } $ i7 j& ^. `: l" F

( e% Q+ `: ^ x# L

1 S2 m( J( Y! j( Z s; f* U, U9 w! ?; Z     break;1 b8 M- X' ?) @7 P ^) G

2 V0 f: V% Y* b/ n, [: N2 n

2 B6 {! z, y. i2 b$ X 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。0 e2 v$ ~3 `5 ?, q. u' T: i, j# Y! X' ^

, z" b6 A% d7 r% p4 {

! _( i4 @+ Z0 F: v6 Y upload对象构造函数如下,include/upload.class.php:25: " X3 T. C( R0 s( M5 |2 d

8 {2 I( ^, F5 A8 a. E

6 ]6 g# d; ?0 D( }2 Z1 U <?phpclass upload {4 g+ o" E$ A, E( c

+ r! c/ ^; U6 g

' k- c0 W" k* A( G+ V+ D8 L/ g     function __construct($_file, $savepath, $savename = '', $fileformat = '') { : E I: w% U h# Y: [

+ @ p6 Q- w+ O7 \3 l; j( S

2 D& H# L7 U0 j( q1 s8 F         global $DT, $_userid; $ A( A: {* L, I% f% z- R; h% y/ ^

7 A, r5 r- R6 w5 ^! @

3 J8 V. \5 {. ^4 v! K+ |         foreach($_file as $file) {8 Z7 z2 A5 n, w' \8 y

3 C, H3 z% r& N% J5 N2 j

2 q* n: }2 O ]$ `6 O1 j' W             $this->file = $file['tmp_name']; + m+ v: c6 n, X3 O4 w0 Z' q! j

) t; s: P$ D G

4 B K; R6 [( y             $this->file_name = $file['name'];. v( H9 f' V+ M+ D, c& m# d! A5 D

* L- G, ]. D! k) r; f2 t$ Y& z

) p6 E8 x o+ n' u! V; K9 z6 s1 }             $this->file_size = $file['size'];6 S; w. a$ o4 @4 C- y

- O; J D B+ L( [: B

: U( ?0 {" x- u! ^1 M, Z( @' ^             $this->file_type = $file['type'];$ n: }3 |; t! S

7 j: m" e% z. ^! @, |

' A4 Y& A, q7 s" N: J- ~             $this->file_error = $file['error']; % V3 F A: R# g+ |9 c4 E

; U& z1 x! N: z X h- H- {' X

6 c7 ^4 I- E w  ! B3 o; Z% J% P o2 p) `( H

0 D) A, I& C( i; |2 m

3 E% J, b( H" a0 J         }5 n' _2 a7 S- s: C

6 @: j* f# d) u( \( l; C% u- A

! ~: w7 B+ B( k' T! U         $this->userid = $_userid; ( s9 @, L6 U7 ^0 Y

4 o- i' x4 g. }2 G

! h8 \! S3 H* A f5 i         $this->ext = file_ext($this->file_name); 5 h) x4 g6 O- M$ \

# V( H+ S( ]9 R

/ U5 ]4 ?1 E5 z         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; & _$ b# a, b! I6 x1 y

. h) r+ d$ Y5 d& Q3 p0 g# Q

' U8 H- s/ Q k3 B, i) s7 l9 O         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; - a3 ^! F% _1 x' \) g2 k9 v

- g) H3 x- }, H+ c% J8 k$ ?1 f

* ^- A0 U% T3 }, S) Y         $this->savepath = $savepath;* I5 p+ P" `3 P' O- P* J1 d

( F: i3 V, G9 ]0 q- ~

_# q8 i% P7 Z" Z! R         $this->savename = $savename; 3 R" D% a1 o Y0 K/ S

9 Y: |1 L1 U, \( J! Q' v

2 f2 o! W8 Q% }' X5 D# k' K     }} ' H2 x! }" Y" H4 [3 S; o# P! x

) p' e6 t+ l( a6 u

7 V* P; }+ D) k 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 ) L( @! k# v8 h

: r$ \; Q( r/ |" M) k2 j

/ g5 B+ V+ i) K2 X; u e; X8 R8 o/ n 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 0 ?, ]+ k( N( f

) M1 z0 p9 A! h9 I! C6 b' u3 P

6 w8 ~! \% w. E $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 . g$ |2 _% H# X

2 z: I) {. ?2 n" \

% @9 k/ ^: h. [( s) b# }, [ 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:, ]3 X# U& }! H3 s

( M9 H/ Y, Y6 ?/ Y7 j/ V. G5 |# y

" f& B0 e0 D" @6 D# r  0 {" {; M, W, e

6 k3 h; T1 A1 ?$ V7 ~; `

0 ^# b) c" l2 J( W1 |+ G 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:, w, i$ y# f* T! r

" t. u/ c/ K. e1 p6 J7 \

1 I- p8 R, T4 }- Q <?phpclass upload {3 v9 n# Q) q( d% N& M7 O

. z; B5 G5 ~0 g( M& _# a! g* `

+ L) I: X; q( K F; f7 n( y, B     function save() {% E' N6 u- r8 q) E# I$ f. g! |

! p4 ?+ l) x: v4 c/ k6 R

0 |/ S9 c3 W M: `8 ~0 N' l8 }         include load('include.lang');9 b3 D" l3 p, f. B# V, }

5 W o. c5 F/ n! _) ~# _0 r

, W, w# V0 m q6 j) k         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');2 f, T* @8 x- r4 b

8 b* G, ?9 _' h! _

4 f8 b+ K1 ?- q! u- \+ e; a  2 M5 I4 t7 O& J2 ?: }' D

7 m" {( Q) {; d7 M

4 l) i" N7 L& R         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); ) G# v. T5 Q" i- m7 u) I

4 V1 h+ ?# x( A' Z

, T# W4 k5 b& P# j2 x- b6 Y   8 T" Q5 @* N* ~& {

+ w; w) N; K, D# G5 |1 D

+ a5 g, N& v) o$ i- ~0 @; d9 Y         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);* ?5 j% r' |' P

% G# p( l+ `8 v" X G

" k4 E) L+ \# A   ) C4 j8 N, y8 o: N; F

& ]) E- |( X" F. N

* |: y% M/ z' q- O! p1 v0 d2 Z         $this->set_savepath($this->savepath); ! e% o' c0 J( o8 G

6 M& D9 ?+ S* w0 m/ d- i, @

`3 R% x% v. `3 _* m S5 T         $this->set_savename($this->savename);4 _0 U& g/ ?" o6 C6 v

' Z, {$ U, l/ d

& H; O; U6 }! s3 {" O  3 {* y3 Y0 j% V' Z1 w! ]

( g/ k. z1 x1 d0 U7 ?. v# h& \9 `

# X. R. V8 E/ |! U         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);9 l& g0 U; V2 g

! q ~7 X2 M, h$ B

9 H. M$ e9 A. B, Q3 x- a3 ^1 c         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); ; V5 \. T E3 K& y

- W1 f; r+ l3 p0 O# C

; R& O( Z& U5 i- u         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);/ k" ~5 @& O% \/ Z

* w% P8 d; L: a+ E) o* n7 ?

" f0 D; X$ L$ j3 T* Q: f E7 i   * q( b- c. @, x k$ A6 A

4 F0 t" X: L6 r4 z [4 Z

- ~, @- p+ m8 N) g: M         $this->image = $this->is_image();, x, j3 T( `) ~) k/ j7 ~' o

7 P8 Q( d: R, p1 {9 q M

% x- z/ U) s% k! X) o) z         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);/ b9 u4 w3 \7 b( `8 ^% }9 v {

: ]( P+ Z$ M0 Y0 t% T' B& M

& M* b2 V4 t6 F         return true;- o5 V& Y0 u! i" g5 C

& c$ T: t6 x- N. J3 z" w8 h& k

+ p6 u( U9 K7 ?1 K! H% K9 P- T     }}1 k1 p3 t" I8 K5 T

" ]( v9 n+ K2 C- b' h2 Z* u

# A4 _: ~0 R" a" L6 J* e5 c 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:/ u0 P1 n5 P/ J$ F# ~3 T5 ^

, F; z; }7 n% V$ N

2 e+ Z3 M0 Y6 D% X$ L <?php - x% v. C5 g- b, Q- `( g( e' t& c

" {& O/ x# g- w0 ^$ B/ x" n# W

0 K3 I3 |* ~, x1 L* P/ ^% C1 R; G1 J I     function is_allow() { : ~* o$ z4 f5 C0 `

& k: ]/ \* @# w5 }& f$ K

( m3 h* R, z$ d' W8 D         if(!$this->fileformat) return false;9 V( L% b4 M" G. r4 j s

2 h1 @) }% J( a. O

/ R% ~5 L! S2 P( [, i3 p         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;" l& o6 i( I- v, @1 H: i6 p

* b2 M! @5 L3 u" c

4 _( h% X. |4 `! n2 V3 x$ D* G         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; ( y) T" u& d0 h( ?1 h( z9 i

- O1 r# Q/ j( F8 W" w0 F4 L

; `) a4 b, C; d2 ~* y         return true; * M& t: l t, M1 z5 K) r1 e

4 n3 k) I: k) \/ p

$ ~" v0 z- g, k2 M& ~* L5 m     }) B9 g) q+ F- Z# }

: c+ L% p3 M- G3 Z* l! i$ C' _ R" h; q

* L! L" P# \/ I) d9 |( U 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 ! s5 I0 `' D% q; L. b0 F

1 O4 n3 M# J) d7 z! Z" [) q

6 a2 e, x! x% n5 I; ^ 接着会进行真正的保存。通过$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文件。 4 g( M5 Y" ^8 X- F

4 m7 L9 f$ P1 t% T5 Q( E$ {! X4 x

R- m2 u! B1 E$ H% }) y; E 漏洞利用. |/ y6 [4 w5 w: q

4 _0 V# s. u. L- S3 F% [

% _4 C1 m; e* _# C I6 q. u) l, X. W 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 2 H; `( s/ A9 a+ [1 ]4 A9 v

6 \ L+ V5 A0 G; j9 V# S3 r

/ L% K$ W; O" ~( F4 M   ; T, a4 r; l6 R( v: q

$ ?# B+ x1 a. I

) e [- ^7 J" X& x 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid " }! r' {' |1 q: v- D- a1 E

4 O- p' o1 ]2 |

+ D7 e1 p" G: r4 R' O( Q! f 不过实际利用上会有一定的限制。- D5 |- ~. N1 m

% `8 w# O% N& p0 e

# z+ i& C; j4 M+ ^4 L0 o 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 / N( H4 S" Z3 u

9 s4 @ w/ a4 n* x o# ]

7 m8 b2 R! _( p- d/ u, l6 z0 L! s   1 L0 T. ^. c) [

. e. G9 f4 v2 c ^3 N

5 o0 [6 p) Z% k& Y W2 [ 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: / O3 I1 W7 n* n" D8 s f3 K; [9 ~5 [- B

, A, X. `' N$ y k% @4 x P

1 D9 \9 x0 P, F- }0 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]);省略.../ Q* N, _; e' H, f! d

, k3 b k( b5 _" Z/ l. d1 U

0 j( R- F, C' X/ l! u: b 因此要利用成功就需要条件竞争了。- f3 A3 J7 n0 J7 M

/ F: V2 x" {- W

9 S! _9 Q1 ^* v7 X' W R: y 补丁分析 7 @0 E! W2 W9 L4 A

( i% M1 ^' B: p

- b1 ~$ _1 D8 j7 {9 \. W/ S   9 t/ B% ?/ b3 x

8 F, R1 Y# P& T# W: F2 U$ k

: ], E: o8 t8 k" g 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:4 _* L! T- O o+ {% s8 G( i

( f' t* o6 \7 x( R9 b9 e! g

" T2 k/ `) s' k/ Y$ ?, Y) o& W function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}! p# k' H4 Y8 a+ Y

) u, D6 L0 a9 b- V3 q6 l- i* i1 U8 x

, z( m" X7 V5 ^9 C( x   3 B3 W8 W1 p$ ~. T/ f0 j L% I

" `3 S2 s5 e% d% v- f

! u p Z+ M5 [3 d9 W8 n 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。' b: z; t1 G5 Q% C- O

/ R4 D; C9 H+ n8 t. A# \

8 z" o/ u V+ E1 ]0 Z 在is_allow()中增加对$this->savename的二次检查。 5 X1 P! P3 S6 D% f) z7 [

4 d( E- P6 \' H3 ]2 c

/ Q, P/ W1 f ?" p 最后- X s) d2 F' S, B4 N6 M

0 _: d% j2 o( p% i8 l9 [( d; I# q

5 N4 Z, C. p! f 嘛,祝各位大师傅中秋快乐!/ }% v% L; S! Y( W6 }4 R: w4 Z

) d4 d( u# C4 O; J0 t5 a

% t0 [3 ~, _& J; N1 z+ W   # E. |& b1 w) d ^6 B+ F# Q

1 S# u. o$ h, Z+ t2 ?; v+ O% ]( X
回复

使用道具 举报

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

本版积分规则

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