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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
8 [9 s( q. t6 [0 G8 n6 z

5 p1 @8 ?& U2 S6 U v" Z

7 s& F( \9 A, }/ A g @! k ~ q

) H% R2 ]) P2 h$ M 前言/ z$ m; O+ Q t

. ^9 H- a( x% ]6 y; d+ I/ \

& O5 _, o G) p0 E* N% g 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 : I: C$ b& c9 b

, ~7 h8 ~. U4 Y' Z

9 r. r1 w$ Z U( T   / W' P- C- _' J0 G) h

X5 {, V7 O8 I" E. u8 A. ]

% j7 c2 Y: Z4 W6 ^ 漏洞分析 2 R; B3 z0 _) H" @1 e

3 p; C! n5 c- E' W; d9 R

9 J2 K# g0 _7 g6 F 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: * U& w, @& L- C- v

! u+ N% m; U- }$ J3 N

0 ^. R* `) ~3 r/ d* [; |5 s( {, [& p   - p% u+ h" [: h8 `+ W5 }$ g

! N3 u0 C+ Z& B

* |' f+ j: r* f6 [# v" i9 [& t 对应着avatar.inc.php代码如下:, ]2 N+ x# O8 Z2 V

: T& i+ t2 s. h9 u$ f( {

8 ^. h, I& j( c; F" A( {3 Y e <?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) { . f4 v& R2 f) d& z* K5 O

; U1 P0 A6 E- M: G! m

* q# ]6 F* Q. b9 C     case 'upload': 0 e5 U. C8 m/ t, T3 r5 C/ J/ Y: Z

/ h+ ?% _! c$ O9 M

" `3 K1 ]0 W% l3 z7 [* x6 m2 j         if(!$_FILES['file']['size']) {6 ~# U2 Q5 B& x% S, ]4 i# P0 S

9 P4 z7 @& S4 N: T; J

9 A; k3 _6 u8 E             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);- I9 M) M, M3 i9 w

: |5 O) `8 B3 `# n% `

, T9 p' N9 |6 O: g j! k: c; O7 B& n             exit('{"error":1,"message":"Error FILE"}');' m4 z" Q4 T/ w9 a Q5 Y7 C

9 c) L0 X" U4 m9 i# w6 s

+ @' f/ {/ [6 I/ x7 N% ]         } 3 U4 w0 G& o2 g+ ^

8 M" I# F& W" y0 @

- C2 [- H9 P4 v         require DT_ROOT.'/include/upload.class.php'; ; F) T& ~; j& V5 X! v2 t8 \

8 W4 Y% `; C3 S) q# u: Q" y

9 W7 K2 V2 D+ T  $ ~7 D; b8 L# d

& u1 Q5 F6 J% g+ o8 m8 \/ Y" p

v$ ^9 {1 j2 J" w% ^% u! V& h         $ext = file_ext($_FILES['file']['name']); # x; M1 R7 X# v

% i8 u% E* z7 _3 a7 [

) W, h3 M4 l/ o8 h& f. h) }2 j0 Z         $name = 'avatar'.$_userid.'.'.$ext;1 D: ^1 C3 h3 Y4 W" e/ ^# F

$ Z' |; o. C' n

6 [4 ^) n6 p! ~% q. J         $file = DT_ROOT.'/file/temp/'.$name; & z/ T6 @5 t6 H

( Y: T) C$ g6 M S' `' t: e

' P$ G4 @ l# B8 G! S0 y   0 ~/ o( I9 h: ~( L$ F' P

& D" U1 @- }) U! v: b

8 ^3 ]6 |) I( \1 a% V! ~+ U         if(is_file($file)) file_del($file);9 ]/ d. S G7 ?* t: ?

; x, z6 Z9 y1 B2 a; b: t

' ~' @# `% {# e7 \6 Z: I; h         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); , d0 g: S1 w6 {! y" y* S

7 G' q& P# a" X$ E/ [# Q4 {

& C5 t/ _1 u- }  & q' ^, N6 E, o }+ F+ E+ S' ^: h

" w1 j) H- W; P

, [8 T& j4 s/ \, U1 X- ~0 L7 J2 Z         $upload->adduserid = false; # z! n- }- F+ Z6 S/ O* R, s* q2 Z

0 J( t+ x! t) \7 ^# K

0 l7 E9 D. s. O' s  . P3 Q. U, X0 R& v$ p

* R5 a/ t4 H6 V+ K0 n, K5 d

5 e. r- e* i1 I9 y         if($upload->save()) { + ]( T9 Q) c0 [1 P1 p) Q& X6 K/ ~) I

" S/ m& E9 r3 p

) b/ ]- j8 n8 \6 A# Y2 L) s K             ... 6 l8 L. @, w, @, F5 i- ~

" J* Z3 q# d1 p& w4 T0 y

4 J; |5 Z1 a+ }" L% l& r' L: I         } else {0 z2 g7 N# i( N

/ ~' x5 ]4 |$ Y9 K" s9 K6 m

1 c$ Z* k$ `. n: {3 j0 T             ...: k1 ?$ m8 b7 ~6 g

7 t& O. t+ [; ^5 \# Q( u5 C! B

- o2 F, N, M3 C         }# r! E M, D/ D2 q- [

& s$ e* j6 N. K2 ?9 A6 c1 |7 N E

5 b7 n9 _3 @3 u+ z     break;6 V8 T& X8 t3 k2 {

& G+ `5 U8 E L, ^

/ \( }# `& `7 e" ] 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。4 P7 s9 Z6 E4 J9 Y6 g

b a: Y4 p+ I; l* e

: t7 u, h# t* p( P upload对象构造函数如下,include/upload.class.php:25: ' ~$ \! W' c j6 n

! j7 L" M9 M9 v: ?

: E7 h) J' p: t% ~, E <?phpclass upload {! |8 ? o: G+ x; q- W

# i; J. P; L4 v

% w+ D" A) k( c' ]* n     function __construct($_file, $savepath, $savename = '', $fileformat = '') { * p9 t7 L ?! l8 X0 ]6 B5 M

) t# h3 s" T8 b y6 E" A/ p$ A" e

7 g# o! O' R9 Q         global $DT, $_userid; 3 a7 c1 D4 ^- B8 }% f2 Z

8 M+ M" d4 f* M, {% d- d. ~

& c; S( c' v2 ]# K# `- p4 s         foreach($_file as $file) { 6 t% s" o) l% k5 l0 b) l) t5 Y

. N% y ^2 h3 s2 E, u+ ^

% N* w* {* n9 B$ W             $this->file = $file['tmp_name'];$ m% T/ e- w( M! Q

/ G- N# b- |: m% b& S

* m5 u, I5 j2 X: p3 c0 T9 o w `             $this->file_name = $file['name']; : W: H5 E* H6 E4 c( A

2 Y# Z. b0 a' G8 o. t1 H5 k

# p: ~6 m' p$ w- y4 ~9 t M/ ~3 D" N             $this->file_size = $file['size']; + y* v6 ? @" h' L

8 u8 j8 A0 B+ o! \8 ?/ @# c

! W! N7 f! U& B1 ]             $this->file_type = $file['type'];3 Y" Q8 l) w0 H* X" W& [ H- |" g

8 [ y2 M% s6 t& C7 u8 A# q+ o9 o

1 z: W; @0 r3 g- X6 t# M             $this->file_error = $file['error']; d- K G: H3 C; p: @

1 \) R$ a3 a' `; Z% ^9 |1 L0 D

( H/ p K; T, H, L8 X  ! f. U* W, k: V' ^3 n0 M% f

0 [- x) u; C. m/ L: q' n4 q

5 f+ ^: c4 V; B3 u+ a3 V9 R         } : \. c. S1 \6 N5 \

: j$ d/ p+ a) u

! a, d2 `( G3 y& O2 E# Z/ }         $this->userid = $_userid; ! n0 q5 }( R% I$ h. x6 _6 p

! r( s% \+ k8 b% N& o( Q

9 V& F' l0 J" D         $this->ext = file_ext($this->file_name);" W; U: Z9 Y1 d* C9 d

7 }% ]* P4 W; F0 M. e

, ^; {: O; l: ~, t( Y8 F         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; : L% h. W# h- A9 n9 x) q

( z( O! Z5 @3 |

" L, I/ i: L! ~         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; ' T2 ] d( a, A9 x- s

! k4 `6 ]" C3 [# A0 t

; B& C7 v$ x8 w6 w( C6 B         $this->savepath = $savepath; 3 Y) `3 d, X0 n$ f7 C% d

g$ d; {) B/ |2 ]

9 N6 y! S% P0 U3 e7 j         $this->savename = $savename;$ J, q: f6 x- V& [5 T6 Y* j) U3 X

# b5 K' x4 d, i& a5 G

- r: q7 D7 D; n! k* ?8 b9 W     }} " h# J; ]) L. R# Y1 k2 G- Z8 i

$ c. T' o- q, D; W7 B+ z! z, D( |4 G

+ S' b# G8 \+ G1 t$ B 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。; h* B7 ]! u7 W

f3 a0 q4 s1 c1 L2 Z# X

$ v4 i- l5 n, z 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 + [, U7 u: L9 h- a( P

$ D! \5 I% Q9 ]: n' f

R# @5 z$ v% y' D1 U" _2 u" H7 n $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 r7 E0 k) Z3 E T, d/ U& Z4 f

3 _& ^# f* {! Y2 y( X7 g

% \2 }( a8 T: A1 a7 i4 ]" L! N8 ?0 L 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:" }: d( f" A% `7 @ E) P6 P

' i# S: v* n. T; M( ?" T

7 d/ N; V) U, A O Q   3 U& T7 A* e0 G" p& d/ o' {! n

9 i% ?# I6 t k# ^

0 I, r4 E% Q9 N+ p& B 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: 6 [1 V$ U9 w0 @" M k

9 c9 Y2 T5 L9 e: r% F9 O

# r9 O! c7 `! {. y k <?phpclass upload {( G, F/ b: q1 Y5 ]& B1 @

$ T1 |: A( c9 c9 ?( G

" l. |" v1 ~; E! \* Y, [3 U Q     function save() { 0 _) D' H1 a: z* y6 G1 K3 W0 S- {

$ e" I* z; ^3 R) K4 [

3 ?! \: H& I+ U         include load('include.lang'); , P0 \) p) W- M$ {" A* H' k# s

* Y) l; O e& k: `( ?3 z

- o1 h7 n; S$ k' J         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');% o' _; G# U: p! C

" _0 c5 h$ t& w

% C+ P2 r, U# N/ k. f; ^2 j" k  ( D+ y1 h. M! d: z" h5 V

, ~" A) z2 J5 p/ h: M s( _

" |! e- f8 `$ |; b         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); / P6 A# e% r! d

: z& L; Z' @$ M, a" L

4 K6 h: ^% E- q7 ^ [   0 A8 \% t0 L* j; t/ Z$ R; ]6 N. x. s

5 W3 B5 S' O4 _7 Z: T8 [* ?- t& L

' M y! ?/ g. t3 ]5 g4 Z         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);" w* ^9 v$ n. W7 x9 z# d

: P/ p. ]0 Q% k& h6 `+ t/ g$ u

2 O) h, |, o9 p  . x% M2 @5 l1 {/ M, B" m

0 c# G. F$ @1 D

8 E& | W/ G) K/ T         $this->set_savepath($this->savepath); 1 G4 K4 a( E+ |; v4 u- M) _

0 _0 W' b1 I9 y! b: Z

+ L4 c' k/ U O* G7 o% ?         $this->set_savename($this->savename);% I& b4 P* y( v

% |- a- o/ R3 k6 a0 j& W

4 v ^# F' q+ k! [   9 l7 }, J0 J6 q" f% ?# w5 o1 Q

f0 G! S8 [2 J( q7 J

( X I2 }& ~# E# u- b# J: Q/ N         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);* ?/ n9 c: z9 O0 W% S5 K$ e

& O- F$ ?3 q; X" P. O) A

9 p4 k# Y8 ]- a3 c' P2 R9 X         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);) _ I, }+ v: M- c$ S5 I

$ n5 X! Z$ e, K) ^ K0 H

+ k `8 u* M4 h/ w- j. u! ^         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); ( {2 y9 C0 \# w0 ? D8 X

( k$ {8 c6 r3 ~6 U& K9 }/ f

1 K" l! r4 j: |5 N1 v  0 C8 x u/ _& y6 @4 w

9 J* \; s) F2 T% t/ F+ m; t

1 M6 d0 c) j" O: y3 \& i$ V         $this->image = $this->is_image();7 i. ?6 g2 A8 L% C5 s1 O! i

" K# Q4 X9 `9 u6 d5 G8 j

+ c% P% n& f, ~) f; v) X4 i' j         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); 8 C. c8 h' R Y! k) m

% i+ @+ H: ?; B; C4 n

& D( C& P% D7 J. X2 a         return true; 0 [) h( o9 L+ b6 S& @7 d" w

8 g9 I/ j; h$ M. Z

, o0 S0 b9 t$ Q$ o6 C     }}. s& |- C7 i( W! N- Q b

+ o( }( d% q) `8 A) P5 Q

! e0 T/ ?$ _( V: E 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: 7 q! b1 ]5 c# J% x! E

L9 h% ^( N# N, t7 r; x( R: H

$ E- u% f3 p) c3 u9 \ <?php6 L! J2 c4 Y% K+ f5 o4 g8 z1 k! R3 P( m

" {4 c; d1 c6 r4 X7 W

1 ?8 R' j* {. Z2 \' ?- q     function is_allow() { L) p& z% t* d4 L+ Z* n! [0 F9 W/ r) Y

/ O- N& b% Q' ]% i9 b

1 J/ Z3 a5 J& E. _5 l2 C' i         if(!$this->fileformat) return false; # [; b5 C( l3 y! F

$ j. B7 l& U* b6 X

% d! N) B6 b3 w6 O- X1 a5 k# f. ]         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; , z8 H0 ?8 f3 t

w; j- u- M' x }# Q

, g0 ]' q# M2 K4 t         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; ! h, P1 y( K! A( G& u

$ m) j1 }9 u+ O

! m6 K& A/ u6 ]         return true; 1 [8 [0 w+ r) x( s

) K9 d! A2 ~5 N/ t8 i+ k

( L" \% Q" I3 c* y     } K$ O3 K# F. Q9 T

" }2 [% n( k2 J" `8 G! {8 A

. O9 q8 ]: V8 z8 Y! y& U 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 4 @6 l1 u" N+ U9 h& b

9 R* t" w; Y, V% X/ d2 @

) |& [3 d+ w8 {+ b! f 接着会进行真正的保存。通过$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文件。 5 s- B- D* V& A; b$ f3 l, J

- o9 ?) m4 I. L* \: G# J5 R

( x' x- I1 w! p' O; G# U 漏洞利用$ |# @2 L; {% O% P' I

3 J: S4 h( J- N- C9 v

0 i' B$ @! e! I f+ f) X/ ` 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 / y1 @% r0 T: C% v7 ]

& p6 @, T7 { \8 q

& q# W! z' n! K- ]" e   1 Z7 { y- v# f% T

+ ?, e/ b# d5 T/ P( `8 E" t

. G6 m: o: M: ?9 [& G 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid# [( `2 y8 c3 g; O, M C

1 j Q5 X- S" Q4 y3 l8 r

. o% k2 I$ @# m' H. J 不过实际利用上会有一定的限制。 5 j6 n; i3 Y4 _! u4 Y

* r# z, C( s- [; A7 g$ k7 s+ C$ t

9 A4 G! K. u6 y" f' C8 G7 z6 ~. f 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。; A% N7 F! ^! o2 B8 A( D

3 P3 j1 j1 o6 F: Q% k9 s

4 m3 [; e0 V; N. Y6 z; G  9 ?$ H/ }( U) d( {% V( N

* _$ b4 j* H7 F( T! D

& U( g* j1 `# \: |) A$ _& {# l 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: * e* q# D6 X$ t0 w

; B9 p$ C. P9 d7 A3 h# q- N

& T/ G4 A1 E7 Q- k! W) h( f 省略...$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 M# y2 Q5 q; O; B1 I' N) {

& W9 n8 Q& R' g1 @5 [6 Z. z& p

4 j: C. I# J. F9 |1 w$ o 因此要利用成功就需要条件竞争了。 0 @, \' e) M" l2 i, {

4 r4 ?( _/ Y: M2 m

' b4 X, b5 ], }$ @+ Q; N 补丁分析 ( X) y' ^; u2 K0 `. G. j o

6 Q& L1 a a1 E1 [

1 ]; V5 t. `. C& U  8 t% J) ~& n3 W

" n) p% L" [/ Q+ J; I0 t0 g

2 I) J6 R. Z2 |9 j 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: 0 u" ]: X9 L# W# m0 s

8 J$ d7 m0 w4 }3 @1 E

$ j& f2 K( x* o" U/ Y# e' ~ function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} 2 K, Z* U# X; n+ o! b n* b

8 }/ O. }! S" t; N x' c2 y

3 R4 C* ^1 `9 u: r% A   0 O, H+ @2 V/ @% {

1 K( x6 G5 S0 u$ G4 |' G4 _% z! x/ [

: l2 u: C8 R& X$ V2 Q2 J: r6 ` 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 4 y' n+ w9 @8 W/ r

; B( A: ~+ V; z& m! K% s' k6 W

$ V+ l3 x& I8 Z E" ]% k 在is_allow()中增加对$this->savename的二次检查。 1 T! r4 A+ A1 B1 ?3 S

0 m/ @% g7 \2 r3 P

* V) g# S5 L4 |2 }% ] 最后 4 D* X K0 J' o0 {

9 M; a8 D _+ W+ A9 E/ W- l. ?

( n5 u6 ?9 y; T w& h; G/ m 嘛,祝各位大师傅中秋快乐! ; f* T8 O3 ^( {; n5 T a# b

w; E' E; Q# R: ?- E: }

% ?7 I5 W6 p3 q! ` h' ]+ r7 C# T   * l c6 E- p( ?- l* S Y4 D0 \

3 N" s/ E' h9 E+ h. v D3 n
回复

使用道具 举报

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

本版积分规则

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