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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
% Q, F, k* ^' C

9 U& g% x& x8 d; k+ B/ |

* I# ~4 g1 x7 b p& L

u. x6 R9 {8 g 前言 ( B" L, c+ k5 {( j6 D: y. j' L

' a; i3 e/ e; W% m; q

$ F2 u' V O. U+ s0 n4 }/ R 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。$ V6 ?- \6 Y3 ~( W; _. p% F

( y% f. H7 Q# `$ Z

, ]* j: G$ H' x7 E# [2 E$ b6 V  % ]( g( I& ]0 G

+ n( b* K* Q; i9 y; O( L

& V2 `; |; N2 [. w% A% P 漏洞分析 / P7 J: l' s8 d

2 a$ |1 s- p- C l

, s4 |: K; w0 d2 W' r; d0 O 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:5 S1 s! c- z# K

4 ]# G6 _% R6 z+ D

/ G' x- _, [- d* C   - d$ o. }* ?9 G. s

/ l `- r0 c7 z- M9 O/ R. k t' T

& v0 L5 f' c [: r: \5 Q( y1 } 对应着avatar.inc.php代码如下: 6 L! ?7 j% d* f6 o" D

) b7 A0 h0 s& l. f. T' L

" Q. S2 O3 \ `+ W7 z3 m N <?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) { / n6 z$ z% o2 A" V$ W' H( f0 ~

( [3 y- c7 O; p+ U

. J! d$ B+ r2 E1 {) }* \     case 'upload': 2 j ?8 C( A. e% O N4 z

& n' T1 G `$ P7 Q9 T5 y

$ j) T5 o: X; F* F2 f) |# z1 n( S" [) m         if(!$_FILES['file']['size']) { $ J: H1 q3 [4 ] B

; ]% B7 I2 s. W. a

3 x& [4 v3 o5 F% N: x. u; {3 u             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); 6 S8 h) C; c8 ^8 H( m4 c

% x7 }9 {3 g, }$ _+ n N2 P

; g* o c0 M' h N             exit('{"error":1,"message":"Error FILE"}'); 7 {* f. D4 {+ Y, J" ~" s

7 \* Z* w- D- C" P2 b) v; z. T

; i1 V. O( i7 D4 x! I2 |0 \# i, Q         } ) n( m- M+ Q e' u* _

. R( f4 j" T* c

/ v% D7 _- }- z, f) {3 r7 j         require DT_ROOT.'/include/upload.class.php'; # o- T& Z6 f& E# K

9 g# B) J' r$ t3 T( ~. `

3 S6 s# X, M& n   8 a7 [3 K, ? S, |' H* @* D6 @

6 U7 y ^" f) l) z

) {* d+ }: ~3 _- Y         $ext = file_ext($_FILES['file']['name']);+ |2 L- k# ?3 E) T- q

9 ~: P7 R7 t' B3 L" l }0 e% a6 M

0 r) L/ ^% I. o8 O! c9 b         $name = 'avatar'.$_userid.'.'.$ext;; w# J. T4 h% K; e% m4 N5 T

* o' @1 `1 [" b. O

" g% B4 L8 a2 n& v% C$ U' ^         $file = DT_ROOT.'/file/temp/'.$name; 8 w$ a' ?+ P6 L3 u/ ]* q" M

) v) \, D' I5 W& w% Q

" C% T/ E) {$ w   l5 F9 z( k9 v9 i$ d+ `

( p: U! }" g$ r: s% q( i

3 g% p1 Q6 G. B5 n         if(is_file($file)) file_del($file); * H' z) w0 O. `

2 y) g. j1 X' I F

( n+ M ~9 p9 T( N         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); 2 e# e; p7 I" [3 l% c

1 H4 e( H# T g W9 s( z s: p

# J& ~: O$ \/ E: O   ' p4 D5 }5 Z7 |& w1 g4 r

$ g' F; Z, P) \) l& A$ f7 y6 O- I

8 \- P; z1 X/ D9 D m h: b$ V         $upload->adduserid = false; 0 x' a0 p; j: D2 I! a

1 _, ?: {6 [% V" T( F

" ^5 {# P4 i' x' _! J' h+ I1 { {   9 l3 g: h2 f- H+ f/ _: \

5 S* ?. I/ I3 L! k: z+ d$ _

( h( b( g2 y" {5 y. \         if($upload->save()) { 5 [7 d! W& r% c3 F* ~3 u

$ c4 ^5 ^" V# P) y, [' h0 @

3 A# t k4 R7 w9 k6 ]' g             ...1 {+ w. L# B o, ^; I( Z

% f8 ]% _7 |4 N

9 y# b; Y a+ v1 @5 s! U7 K         } else {" s! W: ^3 e* I5 ^+ i% {- ?' n8 A

; V& T7 ^) F) |' u

& l3 ]9 `4 c: E- v8 `2 o             ... 9 N& \! @) q4 F0 O1 `

5 b; ^( L$ B$ |5 i. F

( ?6 [ A, ^5 ?) z* t6 D$ V         } $ L# S" O! p5 S" I* \

5 K) I8 u4 e Y# m

/ @7 Z# o- C5 u4 D! n     break; 2 h5 M& F G( y2 u: f$ ?0 X- ]

4 j0 T# l: L* o5 [) r6 f) ^

5 w% I9 Z5 m: N 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。. Q: w. u) u4 Y7 H5 T Y, w" n" H

; W! \+ E* u2 _

( v; ?6 s6 r; @+ P! `. G- l+ Q upload对象构造函数如下,include/upload.class.php:25:& T7 M2 W+ E% `' n

: v' X- P+ j. ] N& @; ?% B

% c2 `$ `. B- m7 `9 g) x <?phpclass upload { 3 _* G6 y/ x) E( v* j

0 ~3 x1 G8 ]" f) N6 X5 R

! V' d' v- S G0 ~$ b     function __construct($_file, $savepath, $savename = '', $fileformat = '') { ) }0 c! e' l+ w7 T

. H7 X0 j$ T$ w: y

. |: @# A1 z, ^) ^3 J \         global $DT, $_userid; # X+ T7 \# |6 S7 `. p9 F

6 A7 ~2 B7 Q' g- B% G& g- u6 g& I

, w4 k2 b; r& t3 w' P0 H- a         foreach($_file as $file) {% _# j' Y5 B# G9 N4 v

7 |8 f, T+ r6 {; Q4 g: M

7 I% A. H r+ l4 L             $this->file = $file['tmp_name']; / [ @8 I5 x, M+ g7 d2 z7 ?3 Y; @+ F

+ r0 `' }4 q2 ^6 ?+ _

: @4 ?) x0 [0 s- |             $this->file_name = $file['name'];: \- I$ R p1 J! `5 ]

" w* y! c/ O1 J/ t" K( k: X

# O j6 n* `6 r5 q+ U             $this->file_size = $file['size'];8 l# \+ K, E+ m! }8 R

' H4 |- Q1 j9 Y2 o8 m2 V7 v+ ]* s

) t4 K% ]0 g& c             $this->file_type = $file['type'];; M C7 a% T. s. d; g) G

' O, y: O9 x( k8 S( q

7 O" n3 |. Z" {             $this->file_error = $file['error'];. q5 \7 |6 x. \2 W/ ?3 ? Q

8 F3 }7 z7 |) ~0 }! T

1 u( ]9 w& E" o" m8 p2 }+ I! o   7 Y3 y) i1 `2 w, k* Z; ]

7 k1 ~6 ~/ _) S3 Q1 ~. j5 s6 G

- A/ z- Q+ R9 t$ h: L/ c         } ) a3 i, k: Z0 q* z+ \' ^+ W7 O

4 ^0 e" j/ Z/ [ @2 a

3 P$ l7 ~& G/ {8 J4 Z' |5 a         $this->userid = $_userid; ) d- ~: N* _: b8 K3 U# k: ~2 v

- ]9 }( h& i! r) V; f4 T6 U

9 ?% ^. ]# J2 ^$ K0 r3 w         $this->ext = file_ext($this->file_name); . |2 C: i! F$ X; i/ c2 Y# s

* [' \& ~, b1 \5 R9 ^

0 @& O! b3 T! W" d; Q. h( C3 v         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; # f7 }) [! }2 X* v2 Y

8 r: v( g' H( I! N7 t

+ B/ T4 l8 f+ w1 S5 k         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;+ v5 D9 U/ X$ L! P w3 j- |3 y

, B# v( g3 F$ B5 j

6 ~2 E5 M/ ] f+ I+ Q) F# Q9 Z) {         $this->savepath = $savepath;. ^. u3 S. j5 Q- x0 p

7 Z, b' w! N( G1 ]" G* @" F

( Z: M1 o; j! H( ^; i2 i         $this->savename = $savename; 7 P* S0 ]9 Y6 l9 F: L# P& ^

+ Q5 X# f7 @. ~

" ]' u9 o/ h) i6 }; V. Y     }}9 K- T4 p* ^/ e$ p( e; n

+ G" _# v7 b# z- @9 N

5 q* I3 Y8 ?9 M+ t; j. q 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 & i6 O- s6 d& O8 y

* ]# w& C; F1 y$ A' |

$ t+ d- u! J$ f8 H 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 $ V8 A+ t+ ]) N* a- Y& W1 e

6 ]# H* c3 G8 e8 v! J7 |' s

3 T- G* {. a( X! R5 q6 d3 x3 X6 K3 D $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: D+ S9 `4 t2 g0 a& m9 v7 _! s

' s, g ^& J2 t8 I' K4 C

4 K# L% Y# j) ^6 X) a7 a 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:+ T: Z b3 V6 d& h" ]: u% z' [

% K9 d# i5 g# b: C- J! h

3 ?6 h; x5 l2 ] k4 C1 M6 m7 l  7 l. x9 G( W C- n( |2 K& o

) ~3 j1 ?3 Z! q! m: R* l) ~4 }

' b; g& {5 S" U# F1 A 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: 3 m( n# |* c( [0 `

( Z& R* m, \4 q E- ?

8 A8 a1 m7 [/ r* l% j <?phpclass upload { : J2 h E+ |* v: B8 J- j

- q2 N7 A- i' `2 o

9 _0 D! i4 x# `     function save() { / w( o1 @. T, S5 M

" a; o' r0 C& M7 U0 c( k

0 f, g0 G. i- c+ u         include load('include.lang');/ |$ H; z8 ?% g9 p

( C0 f# B$ Y# x2 w, R5 O

' v( Q! |& X. m         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');; Y2 o% X1 Q$ s1 z

0 C, w4 K0 h5 u) k X

0 U! l J# j* L; \% L  5 \ B8 \. w3 M4 X" j C, ~

& y; {! t/ O5 H" v

5 [& p; m2 U5 |! n" K+ b w         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); : o) v: c+ ]& _1 g

; k/ V6 i; X5 k

& N3 i) O8 s+ Q3 i' z  ( E" R+ U; \% ?9 `: ~. a& h

! M0 @" g5 ]* E3 M: P

x9 j7 s3 ?& C! S7 m9 y- O/ }         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); " v1 q! d# K0 k! Z

1 {" l0 A7 y0 n

9 j6 y9 b* D9 b( ?7 z  ! v/ e7 A5 z) A$ p

: s. a& V* j5 [" h3 X

; p* P: B* H9 c* w         $this->set_savepath($this->savepath); 3 `# q# `) L/ X6 b2 `

' y, h$ l' l5 u+ M* F& l! `" Q

, P( j2 ^0 W, {8 k4 o7 a' O [         $this->set_savename($this->savename);) e# p2 {) y3 Z& `4 G u

7 g p' [6 M( @+ ~' M# x

5 U: W- \5 A, s; h. l+ z1 T   5 y+ z- l5 b6 z, l

; Y& u& F* C& d" ^0 r! H$ C$ Q

7 N' r/ C' p7 p$ q$ N& T& n+ E         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);8 Q/ f3 f& m/ V

% j, N, n) t& `; H: e/ z0 \

7 T2 o. u' [ F         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);1 t, F" [6 x' H' m

8 q g! U9 _& T' | T0 {) J! P

3 e$ |. L( x' e: ~! c# f2 e         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);3 w+ J* d: |8 g) j, s. u

, B2 F* Y8 [* T4 i$ u" S

' E' |0 I% g! }8 f8 n4 ?3 o) r  " ~" G* @! u, B! i

0 n+ [- a8 w" O

# F; D: g7 n1 K1 N- y% V7 T         $this->image = $this->is_image(); 3 _. s( E6 R, s/ Z9 a, x

: L0 V( O9 U" T

8 t# r: h a/ c         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);, K# [$ Z9 K: o( N9 q+ }( p

9 k, U$ m/ G+ G( Y; X" S0 ^ F

- @% l1 H( i4 \         return true;1 ]7 T% d! g+ O9 L) r& k

& Q4 f: v8 x7 e- o6 e

4 l5 Z- w: n+ @, r; {+ }% y. G1 [     }} h) s# w, ]- s* ~2 L E( N4 p

7 \& K+ {" J4 S! B( \

* ~, i1 s& F1 b# ]4 d( q 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: " P! U/ H2 u4 D4 r0 r: M" T

- {- x& j" N! M) w4 X- ?; O

p. z4 Q# i; l' f2 T; G8 g <?php2 k2 l1 B5 l& K

) Q4 V j' M0 R7 F& U0 ~/ g, N% z

/ g& O$ s B# W1 O! R/ B+ J' a     function is_allow() {; ?" e1 y `) I, K7 z

* W) Y5 q `' ^" ]4 z

3 a9 D& o# h4 p4 r L7 \( O6 U         if(!$this->fileformat) return false;2 w5 e& U. ?" U4 Q. b( b; @

9 j' p+ u" J( u$ g+ H* o# r

: t" Y+ N) {. j- h O, \         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; 8 b8 f& B7 o% ?$ n1 L

0 L' k8 ?6 @' N# A8 y5 q4 s9 z

& N5 F+ W0 D e1 |5 e) Z' s8 t7 o         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;6 f$ O/ D& @+ h: @7 F

& l6 U% ~$ I8 G+ N. a/ X$ U+ E

& @' F+ k B. N5 K) X2 X% l         return true; , M( o8 ?- y9 I7 M! @) }

' ~9 D' q+ k1 @7 u5 H

, |5 M/ U9 E$ ~" g5 k     }1 F- o( X8 F. ]$ r/ S* u3 q* K/ o

" v4 v9 o" j) d/ i

, Y0 y7 c. ~" y+ W, |) h, [: @ 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。* m; i9 N4 Q9 n' A6 z

5 ^! _6 ?- y' `, [ B* u3 W

6 q1 O- V n$ H 接着会进行真正的保存。通过$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文件。 2 j N7 H$ p5 n: `1 @( e( x

4 ?, M- \! w5 D& |9 {

; D8 n' e$ f1 b2 V% v1 e9 H 漏洞利用9 ~7 S: O4 T* Z$ l

9 f* ]4 W; b9 I- m! p* o

9 C$ f/ |$ F% j3 x 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。& [& t. X) w+ W+ ]: y4 r' I

5 ~0 C( j$ S( w' C. D( q

0 R% Y$ B- R0 _. D) T( ]2 i2 V   . a0 U9 F* M0 d. a {2 ^: o

@. W& ]' o* I/ w# z, `

2 w5 E8 j/ V8 ~" N( [ w! W 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid 0 y& \, w. @: |7 d( M; G

1 l& ]9 @5 S6 D3 C+ S3 \8 @

* T0 I- H/ C" l8 b+ f6 ~) ]; p 不过实际利用上会有一定的限制。 2 x% C6 e/ L5 ~" P) h! [7 M( V

5 p, w9 }/ L# X- P2 H* ~2 T8 z

$ P/ g0 y9 U1 e3 v" }% L- c 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。. r& v3 z: t: P0 W

- L% ]/ N! i" O+ b" Z2 i

6 _3 r( c' q; E   ! {5 D( s2 b0 l* ]6 k

; ^1 y3 _3 N9 F/ f2 d8 B

8 X5 L. I, a8 t 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:# L) t3 v6 a/ s( _- `" [/ W9 M

5 ?, n3 X0 v: ]

: ]6 P$ {& W+ 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]);省略... ( P# S+ U$ M; P2 G

( @2 L% h! q8 @

" a* e/ S0 }9 T' X# k 因此要利用成功就需要条件竞争了。 . ^, T, @/ `; n; ^3 _$ ^

* Q- ^) u: P' e W

4 `! w- ?: ? u! | 补丁分析4 C; ?, Q& X9 `! f. Z4 t- l

4 o% Z" A- [4 a% D

, u* ]/ U0 X) \( P! R+ o4 X   + g: `4 c/ T1 C$ X6 T% ~% g' z4 Q

4 Q1 f' y- a% \; i: N9 h% y" U

% c: Y# W" D5 {) ]0 C: G" x 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: % @4 j+ r7 ]: |- ^; g+ x" f

/ M6 E+ }/ @* Y- D( N

& F5 j$ {5 N5 t3 _( O7 @: L( T$ q function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}7 R8 e1 O3 ~- `

) G% U7 k) t3 i

+ }& L( b. f S( s7 Z4 }   F' r/ j' U' E5 k4 |! @

9 h' ]! b- |) A! O% A' q! Q" k

- G# }) _* h2 S1 N 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 {5 z( Y9 ]1 E. D- Y: X- b

" `) J2 C1 \; O5 v

7 y; u% v0 |5 O8 w) L2 r 在is_allow()中增加对$this->savename的二次检查。& A/ f* r0 c' ]+ `3 F. @& u

: v% t q: b& p5 g3 n6 g& _3 K

S1 B8 \. T/ l! }. [ 最后 # K m f1 Y1 t9 |9 \% g) P, v! V

* J: h4 B6 i$ B8 ?; I8 B

' `/ w) s, [! E, G) `) C- Q 嘛,祝各位大师傅中秋快乐! a# E, I1 Q- j; z! q+ d

: q; f# d, B% V& F$ Y- |, n0 E

0 D* V2 H3 N1 a0 y* P! G0 W   + y( i5 ?5 \7 n3 g2 o; v$ V

! Q/ |+ f h2 Y+ h) g5 ]
回复

使用道具 举报

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

本版积分规则

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