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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
+ ]. @2 |" l6 r" p Y; E

) h- \. ?/ d: ~0 d% _

: \. H* o. d1 G* J Y% O

" B5 U' Z: _% \7 A8 p8 y+ W 前言 3 Z9 Z8 o$ c$ w( q1 a; E4 w3 g9 n

& V4 V; |8 b3 W% _% Y* L9 |

( u& E, S/ }% a 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 1 v* O, s. t% T/ a* a+ |

9 U' u+ A0 S" S

. H8 E4 F! X7 k. m   & q7 L" V y; |2 h$ O

+ V, i" I- c0 i6 C" M, f, o

6 J0 n! c D5 k, S/ ]5 e! d. W. W 漏洞分析) L( J) r* w" B& q" c' l) W

4 o u- ~ U' e9 c

. ~. P0 T0 x/ }, j 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:" G6 L+ g" y: m$ P

, S6 j, O! e3 f

4 C, w9 M; m& s& p7 e  $ j+ O0 v" a& U2 T, M

5 h( @3 O. l6 D& Z. h% a& T, T

: f5 p6 m2 u _, U 对应着avatar.inc.php代码如下:3 x2 @6 D: Q4 c% J2 ]

, ]6 `2 C8 K% R( q

1 {6 U$ g( y/ Y4 u0 d7 ~) y <?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) {( j d: e& B0 O2 W3 a/ A1 X

! k# x8 ?; r7 u

+ A# U- V/ n! @: M7 M' d( @     case 'upload':' T% ]( x) ~1 l; j: x8 z, q% t( D

2 f6 X, _! W/ n

# T3 w: U/ y+ ?0 y         if(!$_FILES['file']['size']) { * ]. \7 W8 i8 f) [

6 a/ \3 |: ~' l

" s) ~6 E7 ~8 l             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);" Q% p3 o# p0 v9 K) P7 s/ _( ^4 `2 [

, x, ^+ g3 l: D! u/ [6 h

) y) C- ^* |0 ?6 d             exit('{"error":1,"message":"Error FILE"}'); / F: k+ J3 v; _7 x. J$ o5 J* [" Q5 d

- t, C4 P! Z, l

$ ^( S6 h( p! y* E) T! F+ V8 s2 W         }0 r; O4 k1 ?; {0 J) s

7 K7 L; S, [# N1 Q

+ H* p: s6 x* G. f. X: w$ D         require DT_ROOT.'/include/upload.class.php'; ( r1 a* [3 q; V- X/ R

5 A9 d N" w+ u

+ _( W- T" c" a  2 a' b. I7 C4 U. I- v1 {) h

9 g9 x1 m2 P0 _. V

% x$ }$ s! u% k7 P& }& C% x; H         $ext = file_ext($_FILES['file']['name']); , S4 |) S, L2 p, R* N+ q

" A! Q4 H) d- }! H( _

2 N5 B# e6 P, m: m- [0 T- @% T         $name = 'avatar'.$_userid.'.'.$ext;4 U, m8 ]7 d/ M. o2 h

5 K" R' [# K4 W# y" R

1 _; y# L6 f. @         $file = DT_ROOT.'/file/temp/'.$name; ! h# C9 X& m. e# @( r: C0 ]

& k. @. R0 i: }/ T+ B. K7 n' s2 w

- s8 k3 a# l i. U2 U   9 @; o0 D4 |. K$ g' f4 V7 m/ o- u

7 L6 l( ]( ^, g& x

# f* P% G$ h6 }! X( O( y7 X         if(is_file($file)) file_del($file); 4 x% ?4 J' V3 M" }! ^

2 U6 o2 b4 \2 T4 k7 `& k

8 z" @1 y9 K" }8 |* U$ s         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');9 n/ U6 e( ?/ b2 j) }( n

' d6 B% b7 N5 S+ @$ ]0 W8 |& M6 p

u* B1 D2 I$ A* [' y   4 x" z$ f# Q A$ V' `

' l5 x' Z9 C2 y' @

3 N" [: R9 d! y9 Q         $upload->adduserid = false;4 X: g0 t3 U' {* W9 _: ]- Y: g

, S* r( ?% x3 w. K# e

2 @5 S O$ T: U; N+ B9 B' p   3 t4 {. z, U+ D# P* h

& ]5 b5 x0 i D- y' B

& j- o5 ]" o" T2 u         if($upload->save()) { & c0 z" q$ B" ~

. e( N0 P, l0 P0 x

H) ?3 u# J3 {% h             ... # d) R" b# \" a; N

* O5 q$ `+ d, N

8 K2 B. Y+ C- `4 J3 k* K9 R! a0 {         } else { 7 z4 y0 v% R, p W6 q! d2 {

* z" Z0 K; M+ m0 _

- S a" r: E( p1 r             ... - U3 r" z p7 V) L; v' K p

7 ~9 w: K1 }: y9 ^

7 p7 e6 S" Z) G- F: Y. E: w) K0 x" T         }' b" ?- O+ R8 S% d5 @, z

" t6 D/ k( N$ ^8 @& e! s

' W& I% L% Z* f- b/ [6 r     break;& e7 I- x0 }) A" u- Y. s. Z! w+ C

! E2 ]$ i! q# m2 U: P: i/ \

2 [3 r! E, v( c8 S4 A' | 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 4 }7 T6 G' P& T- t3 X' a

( m6 }6 p3 L" ?

3 h6 r; m$ G; o! f4 c) k upload对象构造函数如下,include/upload.class.php:25: + ^: w$ M1 O2 F! u7 o3 j7 E

+ H( c; B$ b$ s: Y

6 F C8 l' `& ~, R: `# F/ O! Z <?phpclass upload {9 A" ?! b7 L W7 ~

" X5 f- @/ D- Z

5 i* k* r8 Y1 R: o, @5 J$ Y     function __construct($_file, $savepath, $savename = '', $fileformat = '') { # V! y- b1 r \! Q0 q# S

& N2 y' a) _- ~* F! } H) [

! {2 i3 v# [' G. A4 |- j9 [         global $DT, $_userid; & Z% Q2 h* F' O

4 s& j9 `; x3 V1 E% ^" g

8 D; t J/ \5 B/ ]. X1 b8 R         foreach($_file as $file) {7 z# @! k N4 Q7 W4 L% A Y1 P; T+ l

; K( C$ E" J ^7 @: w h5 e

" i! K, p& {( e5 l, Y ?5 H             $this->file = $file['tmp_name'];3 ?2 w6 y. v7 x3 i

+ `( s) j, K# H: @- J( j

+ @$ S' D, v& U; O6 F3 M( O             $this->file_name = $file['name'];1 z/ ]3 G; F5 U a2 ]$ w7 Y0 y6 K

+ I4 J$ F+ D6 y+ ~

0 }6 f9 }$ c/ A2 h             $this->file_size = $file['size'];. E' [# B) ~7 B2 x. x. z

1 s- F7 o4 n5 b8 v; m, O' @ x

& N& S* d: [' L             $this->file_type = $file['type'];2 x$ P& ~/ s+ N! g L& y

1 X# \, J% R- j; s( `

S+ J) I! Y' l, m6 n             $this->file_error = $file['error']; - ?( N2 B" Q; Y

! V! \3 B" M/ h5 c! S' m# v2 y3 A

' [) u( h# C1 L) E. N  : w/ l/ W2 p6 f$ z9 o' ^

$ }% f" x$ }( y3 `& i' l: B* }

3 B# u2 K5 i& K         } + ?; W! [& Z; g( X/ N6 ]" n

+ N5 j2 s+ i# M7 F( Y* b3 V* P

}8 x I: a# `+ k/ n         $this->userid = $_userid; ! T) L, P& ?" o$ d6 c: T! T; q

; W/ b2 N3 `; Y% V* @

0 x8 u+ j0 f( {( ~         $this->ext = file_ext($this->file_name);9 |8 L9 s+ W. v. H/ _

) w; E. o% Q; r2 {- {

! g& V/ C8 c' l: }; }+ A         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];1 A. H4 D* H$ p! N2 S+ F7 |

9 \0 h3 [! j. E, ]; U

% x. E; k( K4 H/ Y% v6 F7 e         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; ' N3 N/ ~. x _) }

8 b0 E# H$ |+ d: o. u& d1 W1 v

. {" e1 x3 k5 n, [ P: d0 x$ I, _         $this->savepath = $savepath;$ ]! o9 {+ N4 k7 A1 k; f5 Y5 s

, f0 r. V/ U6 s2 I* a( x

) @* j, E* A6 T r/ T         $this->savename = $savename;* Q7 x8 F0 W' m* ~

. L V# a& w9 K+ U

. T2 t! l, x: X$ G     }}/ ^/ e) m+ W2 a9 X

( `: \/ _# ~7 A: [

* l. l8 e% Z& N: @+ p 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。( M+ _4 }! V4 l7 B% E

" r9 ~" A' p% r/ b7 L

( z h, D: Z. `1 l4 y 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 * z/ n ]- e" x* p. g# f/ \ B+ B# ?

$ [) \' ^0 ]+ y. s5 e8 l

- @% I) S5 l2 `: K: Z6 I' I1 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; U! M7 N7 P5 i4 X

5 n$ @% n2 a4 R" m

: |& R. @" m' g s* @ 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: 0 u0 O: X0 f8 f" \9 ^8 S4 J' A

( H Q2 I, D' h; X$ Q4 i

( H$ M2 o5 v, a/ Y   0 L6 d% f, p2 v: L, o; K

+ j5 r! S5 ]0 g1 }5 s: s

( r U/ W* m3 Z/ O$ o 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:9 Q, A/ D3 m" Q7 S

$ D& o9 ^" t/ O* G

7 b8 D) |4 Y4 o* n <?phpclass upload {5 O; s5 J) @- p5 a5 \6 h. H

$ h' Y5 l" V0 }, [

3 G9 }0 Z4 p" F' U( b6 j7 C) x$ w     function save() { . m0 G( I8 L |

) Q- g6 I, {8 g% f( y6 [# v

. J5 {$ u- I8 _         include load('include.lang');; p E# }% T9 y

: ~: a% G0 o$ ^$ R

4 T7 k# T) G4 n' a" c8 w: v5 D0 o         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');) \- M( z0 N X+ {4 U& d* [0 q* Z1 s

2 C: _3 @8 e% z

3 N9 \3 g$ ?7 _! B7 I. I9 j% {   * c. h. r- t J! m+ u

5 W. F+ o; ^+ x/ y9 G1 B( {

# p2 @( O3 p% \' M. F# r! M         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); # W+ d( q! l) x- l7 a

) y( v* k2 K4 D7 j1 ~8 X

: J" I6 [6 g8 @# z  7 J! L+ U9 d& z5 ]( q" C2 J3 w

' e F. W3 G5 }7 f; t

; @4 \$ i! A, b1 @3 l( r l         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); , i0 W- H6 A9 l+ F' ~2 Q

6 n; g E8 |) M$ \! f) @

1 A( T+ q# I2 @ [   7 j2 C8 Z3 k/ j: `& @" p. {

- H6 \1 q& v0 e$ j4 m7 Y

6 M$ Z- u" @! f# Y* g3 {         $this->set_savepath($this->savepath); 1 y' j q0 \) F( b! w! |6 y- P

' Y+ }: f6 Z9 @8 G/ F

7 P" ?$ Z7 d6 ~5 y9 Y         $this->set_savename($this->savename); 7 V1 C! a5 i1 f" S9 y, T$ R

' d7 k9 j9 o1 S. _

9 |! x: C7 C! g! d  ; J- d0 i0 ]: U6 S$ i" n# p8 ]

1 ]( E. y4 z/ M( X$ W

) r5 E1 s" M6 e# c* e         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); 7 k2 x: O( m- R0 c4 `

% z' S6 V$ o# _4 j" ^

" S1 M W& o; e3 [9 m* C7 m         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);( S1 L1 q! G1 ^$ c9 r$ e. Q

5 M6 {% Y/ s% X9 m) J' D3 G( u" k5 x

7 A% s3 W0 @# K. {3 y' r) Y) N: i         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); / U2 a" L3 O% s6 o

7 v) ]3 s! Z* u# v! n) V' O! ?

% }" v0 ?% g1 x6 k @, u4 y   . V- O( }% V; d% U( D: ?

- T5 W% G- [ B

* p6 ~4 H/ ~# U$ a. |         $this->image = $this->is_image(); ; Q2 W% @* j, s4 P5 {$ G

" ?; k B" R" p- u: r* _

J4 T# e1 t0 T0 n J) Z         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); ( q! l, n& M/ c

. R8 W4 H' T2 w

8 U+ @+ W$ s7 V0 p% \; E; t/ c         return true; $ ]* W7 U+ Q5 i8 ?- P

0 t1 X% H, }. Z5 j

2 P3 T1 M1 i/ O# i     }} ) ^8 [7 H4 Y. H6 I% H* j9 U

; B; q5 n8 S$ e4 O

+ I, \8 }, U" e; D 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:$ ]' B1 y) V7 X: |8 c

( f2 X1 \' j- \- Q

( R8 c7 [7 W" I! h; Q <?php8 l6 P9 a. C, T$ F+ k0 ~+ Z( G$ F* r

, G$ M6 T. ]" a/ |4 X7 i( F& r

# ?$ C8 ^* C/ N" t! J2 N     function is_allow() { 8 L8 l+ B; i( g2 }

0 c1 Y. o7 S2 x( `5 W' N

2 F( U) c) k9 z q7 j0 V2 O         if(!$this->fileformat) return false;6 |8 F6 ?! @+ @3 r( B3 p

) }- q7 y$ n" r, u6 c p

3 U0 e0 i! G5 }, k5 U5 @         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; 1 E" s) q/ {$ l- ]4 n, k

: r1 ~0 S) S3 a% _

1 [) N+ k" R9 B' O4 d" W6 r" 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;, v! v" n2 i0 a2 ^

; E2 _- a1 m: c2 T& {9 ^

. @6 y: Z3 o6 x7 X- x5 s         return true;& Z) ?$ d2 K4 I5 l/ f& h

" \& x& O, q/ U! o) l7 I; `

7 X" |: h' Q# S- Q9 X: b# m     } - k# R. J' W5 l. s8 u' @' I

( j* G, o4 [' ~, ~" H F* o

" e/ ^, t4 R2 Z' p 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。/ o( R4 t* U& }9 U5 o

- M6 b) ]( _) Z9 a6 O( k9 V8 ]

9 r* S' v/ \. T6 f8 {* n. }* Y. C! F2 z 接着会进行真正的保存。通过$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文件。' S2 m" W: m6 \! y. K6 ]! v1 m2 `

8 B* M, Z e$ x

. O4 s) [, E, [" S& ~- A* M- }* C$ B 漏洞利用 ) G! [6 T) k# V1 I' u2 {4 P

0 z& K) F {9 G" E' C7 a: f# T

& F$ j3 B! t( ~. ~! y } 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。5 M2 [+ D' }: r0 m& t; T

9 g U7 Q( F1 q% R% ~: D l

6 b6 e# ~. X/ h   % ?, U: Y8 Y) \9 `) i1 b! l

2 _, c0 o: x7 r1 P& K- I

' d! G6 i$ Q2 G2 ~" b 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid. R& E( h4 |5 I. k( Y/ L

# m% k' e8 O$ W* w+ `0 T0 W- c7 D: s

2 r4 ]* V/ d# k1 c3 K& Y [ 不过实际利用上会有一定的限制。 7 x9 |# X6 Z% [7 f. M

" A7 z" O: v f$ r

3 x; n! B3 J, i: y 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。6 H* M8 q4 t/ y( |

; a. k# @: i" Z. m; ^9 D

1 Y$ L. d6 S( Y  1 i- Y6 O' i. f5 \' v B6 ?

( C7 n1 k% ]% d

, g/ S; x3 m! |, Z# k 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:, F @6 i$ e9 J/ o6 N0 w/ g

X* B( X1 x; i) ?/ \# m

9 ^: M) [& F( G; \' y" F3 U 省略...$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]);省略...+ T F* n* W% K) M" a8 F

1 E! D+ P! u& Y6 S- n- S/ |! L5 n

4 G3 Z" u5 P% R9 W8 p+ p3 F 因此要利用成功就需要条件竞争了。8 d/ A6 ]" A2 ]$ F" V, y) c" z: t1 s

$ }* g. \8 B8 B5 W/ f

; \" V; j7 @: }7 A5 k 补丁分析 8 ^, |9 v: q3 f/ ]

: t7 [( A2 ], f4 u7 t

9 ~9 G0 f( K. u' U% h   - S1 i$ J# P, Z: z# h/ ]

" c0 _4 M' m! r2 }3 p' T

6 z, d' h" B, V 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: + _& |. B x0 e. |: \0 `! a

/ R: {, N0 H, ~3 Z$ p& [2 F

) h& ]8 r% m* A% r function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} . @: E* _; @& o

4 N* K# v" I6 q- r& f* {

$ Q& \# q' {' m6 M% z7 f   6 ^9 c \" \* r4 a

6 @% ~ {. _' O$ m! Q* S/ x z) K

1 D2 w# i5 p* k5 \" L2 Q% { 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。+ A% {/ D6 q5 d

% g- T) i7 Y! N0 f0 M

7 \; R% ?& }& X 在is_allow()中增加对$this->savename的二次检查。 % ^2 k4 k4 C: M+ L0 t

% h- k2 L! b# k2 W$ W

- o6 |/ O/ A$ r& {8 B! G+ X ~. v. | 最后4 [) ~& I" ^8 S8 i8 d

! a, K. G( ?" ]) h- \) X& q

3 w9 _0 y+ \7 m: r 嘛,祝各位大师傅中秋快乐! 2 C6 v3 }" `+ i+ G; e

7 n9 v y3 Q) W( o" N

+ y- R% m0 h8 m, c2 y; |( l/ N, _, \  3 y: k, f% B4 E3 }; [2 s

# ^) Y8 N, T. K2 j4 z, A
回复

使用道具 举报

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

本版积分规则

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