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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 显示全部楼层 回帖奖励 |倒序浏览 |阅读模式
+ [: O# {3 ?9 l- x

+ F/ S# Y: w1 r1 L& @: @* u

- a0 E: W, |0 V i" u( F( T" ^

8 g; U8 o* U% L. [( B# v/ G' p 前言7 x: e4 a: u+ J7 g( j

2 L% A' M4 e2 x& h3 R

) w1 e0 u/ @" }' `3 W9 O 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。1 X) q* L4 b* L o9 q% r

! k' L4 @, E4 t$ R

; a7 D/ A( a- \, Z) i% {   & L# i7 _/ W0 c; F3 _8 `

0 B O+ Q0 T% ]( l1 o T- [: Z/ z& R3 c

) r$ n' A ~& Y8 h 漏洞分析4 \( d$ }/ W2 A

q$ E7 _1 i1 S% `+ y7 |

% c6 T* _& s& ~/ f0 m 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:9 k# b, u, I/ B! B7 q/ a

1 g; \' K9 D: F9 Q# G6 j

0 B1 ^% g5 \5 X, l8 z' W( d   ' ^3 j3 l6 U* g+ Y$ J! O

6 l' B* A8 U: W

) Z0 \' W0 g, A7 K3 p7 Y5 l7 M 对应着avatar.inc.php代码如下: , k; r0 H9 Z& h" O8 t

8 N8 T, v) S3 Z- v

2 _% m( {; u/ D O' A; j <?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) { ( T- k/ m% u2 V

5 [# c, h5 f7 \8 w& `

! F& U8 d. x, A     case 'upload': 7 y, ? |. j- R+ \" L& [

+ j" R9 V# Q+ D n! B" R

& r9 e5 D9 ?# p) {         if(!$_FILES['file']['size']) { & @& j( M3 k& H& c

* ?+ f) [2 A9 w2 U# |& r" H( o

* q1 t+ @) @3 W! i) r             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); ( u- }# [ @7 Z6 K1 F

j3 c8 M$ x( d% [: I

) P% f7 ]6 k7 k! w9 v) k$ S; @( g" T             exit('{"error":1,"message":"Error FILE"}'); 0 ]1 ]/ p+ C% [) w, U7 `. j

! }7 P E8 ?& I* C. T

3 M5 `! H- s, h* h% q7 h         } 0 v5 X* j8 k2 t: w* S

' @0 M0 _! F# C7 Q" ~

# }, b% T0 l. h# ?5 a         require DT_ROOT.'/include/upload.class.php';2 o, |) C; I% Z& h( Q: M

" r- o- o( |# N/ ^+ Q- |

( h+ R6 [4 ?8 ~- S8 B8 C/ m   9 `1 i4 ]/ F" e9 ?/ k, f

- q1 h, _+ y/ @3 B+ C) W

+ j. x0 K8 h8 |- J( \         $ext = file_ext($_FILES['file']['name']);, o6 a2 F( j2 ]! e% r( f: l5 a" K

x: d$ j; z* F: V

o& b/ t) j6 H9 p c         $name = 'avatar'.$_userid.'.'.$ext;$ @6 W, I& u1 X7 f$ k

; s0 j8 @5 p/ ]# p& H8 A) A

% E6 M! {: v) L6 R9 y; r         $file = DT_ROOT.'/file/temp/'.$name; ; m e* m' [4 v3 C4 w

7 R$ U; m, i; Q" @

( M/ L, B, d8 j( }4 E  ) F4 ]# [$ |2 Z1 I$ z; b( q) x; I

0 ~: m9 o1 `+ K- I0 j

. |% ^# l( ?1 m6 B+ l" I' J         if(is_file($file)) file_del($file);4 Q* b* H5 H: {

! R8 z$ T0 ~5 v8 C, h

* @* Q, t8 Q9 ]         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); 2 M4 h4 A- K3 h# o/ @( l

* J; c! b$ K: [

1 F m8 I( G7 d! _$ t  & t: i0 h, F- L9 J" R

" S% \# t! t5 `# f% [' N

$ R+ X5 w( |" G3 n         $upload->adduserid = false; # k( f1 N! Q7 H8 A

, I$ j+ Z5 y" X C

7 p( i3 k( O$ `+ b   4 ?) |: t# A' c5 F4 K$ _: \

, Z" Q/ {7 S( z: \

. ^8 \) b/ ?3 Z6 B         if($upload->save()) {, \9 Q4 h+ k4 F

9 @3 O6 L Y, @& E3 f: i2 Z, |( k* P

# U# l* B9 C6 c! s+ h! w7 h: z$ V; Z             ... - O& M! z0 A$ r& W" _

+ Y$ Q6 n$ m% A8 @) I* l: m

1 O5 D; y; C. s" o* }         } else {* \; X; O8 ^( O6 J

Q6 P8 F# j3 G' p$ I8 t+ }

& A) _+ P* y1 ^) ^ U0 x$ q             ...* ` u+ g4 L: k/ U$ {+ l! D/ _

% h) S8 x) b7 y+ E3 D

u7 T) ^# _" i         }/ b( r/ s' d+ Q B b) k8 F

6 o# Q% H( E9 \) P* q8 a

, v5 [6 C. N* w! `     break; 6 @( B; a; v. _5 C: ?' Y

( F7 n. K7 E9 }) Z0 Y

* [# S% O( ]2 G# E 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 2 P, X5 i- A5 k

) _" o8 X& `% g$ C1 i

" S0 ]6 O3 X0 c- Y upload对象构造函数如下,include/upload.class.php:25: ) p6 l7 Q7 X+ |/ ~& ^& a

: s5 ?3 g+ l: I

5 l+ ^$ O! X* n( d6 m, c: U6 g, d7 ~% W <?phpclass upload { 4 e* m% ]* g" |! d! w5 v, E6 H

2 e7 q7 D$ E6 ~# Y

+ P+ [- l5 m+ H+ G     function __construct($_file, $savepath, $savename = '', $fileformat = '') {" Z( x$ t# x$ G4 T( w! }. a) }

2 X; C9 j# ?# B

' X! r1 q* s0 q3 g2 c' h- i         global $DT, $_userid;# u8 l7 b9 Q4 ]

! w& \5 M O: e* h: n ^

, o9 j4 H& L* k         foreach($_file as $file) {) r! [7 n( D) ]* A. k

, p1 ^8 ?- t. A

) k% h, @0 v; b; n9 `             $this->file = $file['tmp_name']; k5 o2 B3 l0 S% C! S3 O/ `6 g4 `

; u0 U: ^- Q8 w, g

* `& i# w" x# U8 e. M6 Z             $this->file_name = $file['name'];* r2 F' p0 y3 ^* X1 W" A3 M8 k7 |

! T! u. ^1 y) y

1 t. e1 U$ O6 H' F% G# O4 J             $this->file_size = $file['size'];: t1 T' s8 I9 b( L6 y

0 s; Q) n+ \6 K, ~1 Q

. g8 q: L1 T& ]: O             $this->file_type = $file['type'];8 J5 j8 \7 D: q/ _

8 k; f& N W6 f, N3 u: G+ z

) ~3 L v5 |) l. A- |) f             $this->file_error = $file['error'];2 V% b5 n( y4 a* M5 J& w7 |# [6 |

( Y. o# H! T2 e+ j

2 \; v" f; v% G6 u' |  1 e z) u% ]1 r! W8 ^

1 {; n# b- C% \ K

; N* d, g2 P: @$ o' S, O8 K         } 5 }3 ^: m, [3 u7 k7 ^

2 C# L6 _( j+ b+ c/ [) d2 c

1 K) t( ~3 ^9 [3 ~         $this->userid = $_userid; : T% L7 P, t) e3 k! V' D3 d5 L& F

3 a6 s1 T# K, F+ _% [

3 T. H& H* Y$ G         $this->ext = file_ext($this->file_name); 1 y% b! [7 S* y1 o: L

% R- a6 h4 S& m+ V

+ }+ K9 X5 ]# c I+ W3 }$ l         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];0 N7 Q( j0 F2 _. r( {# l

6 C0 x' ]1 {% q9 S0 R' D9 p5 Z7 }

* E+ }4 _* e( K8 K         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;% f' {8 ~8 G8 U

- K2 s4 {8 L' n7 `2 G) ~

R3 ]& e( t+ E' [) H/ Q) N& n. z         $this->savepath = $savepath;$ W3 @4 v( u/ _8 Q

8 p: G* i5 S! z1 Q9 g

7 z6 p* F0 h; F         $this->savename = $savename; ' `* U7 j7 t7 T- E6 G) L

. A: T: S: @3 `; c7 m

7 ?9 L: i( L; B+ ], B     }} $ D2 m, ` ^$ o1 |

/ L$ E0 G' i3 w ^

5 @( O. {" }2 W3 t. z- I9 p+ { 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 / d9 h' d! J, b* ~. ?! m) u& F

, i) C% @8 x5 s8 F' ^

7 C% B8 ]1 M+ r0 x1 k) b% G( B" u 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 . N, W5 T' D5 e A# m5 T5 T

. v, n- {1 S0 o4 o3 A

8 ]5 ^3 Z: U9 O8 } 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 ; F d& u3 p6 T z- U9 I

9 p# ]: H6 ^ w

5 `/ ?, _ c1 K4 U$ N$ l 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:, U: c1 P( \9 z K. t

# U) N6 L9 R1 j

4 g( T: i6 }( J, h6 g  1 x8 o$ F; ?. J: K8 e

/ Q3 c; R( g* S

. _( v. v$ s6 _1 x/ h2 [ 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: J- c4 Z. T6 z$ ~

2 ` x' M D: a6 \8 l' a

5 U* u6 X: Z7 d7 M; ]! G: @ <?phpclass upload {3 n3 q2 R, z. g- L

* ]4 a. F0 U G8 K. z; E4 e/ _

& H4 n$ j5 p( p8 C1 G; h     function save() { / j" s$ u3 f2 E3 S! R) C+ ^

9 o0 M3 t" E7 T6 ]" ^' P! b9 V

% o: p) n0 Z2 s6 T8 _& g: t         include load('include.lang'); / }- O) W `1 `4 R4 K/ x) x7 \

7 B4 e8 P5 d1 V( m: ^

4 a- }4 D* q) k' Q         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');( F6 E' Z& e& q6 S! l

# r X1 r0 j; U$ @9 m% p7 ^

% D2 p& x4 p* z1 l f6 r  / t* Q1 G2 ?9 D

! I* \. M9 J. O

& ~ N" f6 \8 {( K% a* I         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); : k0 R* W* t1 G2 m; _

0 ~! A6 J8 ?+ @) }; Q6 {0 i; z

* q* S0 y5 v7 g7 T   ( Q9 A3 U$ X+ t7 A2 s

6 P- Q+ J3 [4 I' a" [: b

. @. K+ _5 Y/ ?( @- y         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); $ F3 Q) t6 U+ A" |

1 |- p- \. Y$ {

/ d5 W/ Q: t9 U5 S, D5 U( ^  5 ^8 ?7 s0 E1 |5 l, x$ O

; y( y& G: h/ B3 S

: \8 n) Z0 c; U         $this->set_savepath($this->savepath); 2 g3 h1 o) z: x' L7 F& k

: {% N! o! ?/ D9 L

5 _8 ]$ p' O) J. }8 L- y         $this->set_savename($this->savename); $ u+ r" f" l' s& G+ i4 r* A# Q

; x; i# D M0 I& ~8 @% S2 \

( y9 m6 ~9 g( s6 r' q7 M/ M* M   0 E$ M3 i8 Y0 Y2 H0 v' P

" H6 e$ o7 s, _' ?

; r2 o: w. t9 \, G0 v+ |* w         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); . B/ ]( A) ]: R! x% Z+ K% J6 d

: k, o7 \: U$ y5 n( ^2 O C

1 g$ ]7 }' c% Q }' j         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); 1 @; o1 |+ `! y A; O+ Y$ U. X/ h

0 r3 t$ i E! U9 ]

$ J% S6 n6 X, g$ ^& H5 Z& s0 f         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);) j; E; o" Z( R5 z( [0 w. g

% `9 [8 O/ @* }) v* X

: j& h# s) r3 [. x! v+ M  9 l/ d- _4 |7 u5 i

5 |% x2 E4 |$ [2 e, u

. E; Q3 r. Y( R+ P' [/ V. T# P) R- w6 U0 k         $this->image = $this->is_image(); 5 Z$ I/ N+ A4 j/ x! g' v

/ ]9 P8 w \6 @% Y4 F! K1 [

+ l5 D6 A1 x7 Z# c0 e. ]         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);+ s \5 x0 v% T$ Z- r" ~+ Y2 b

/ p. [' n5 }/ \! B: {1 {2 H- p

7 g# n) S* R4 [) m8 o         return true; 8 H3 e* F9 a* s2 h- e: P: C8 D

# N2 X8 T% i. E4 A2 a" ?7 l

8 y9 k5 `0 V0 P/ u; A6 r$ ^     }}: A. {1 z+ z# o C

9 I( A. F1 {1 p7 Q( ~! Z, h3 r# j

6 |- k! a, J; C$ R1 H 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:( Q/ m' I8 J$ u/ @7 \$ z- j: H( Q

5 R. x7 b& s! F" [$ }8 v2 I' u& [

- O! z' s4 y0 h; }: O8 X; k: u g: J <?php k; o# p' }6 ?

& N1 b) t( y. Y0 i* w4 V

5 v5 P V! h4 X6 r4 B     function is_allow() {! ]: @8 ~2 U& h+ `6 [3 g0 p

+ ^7 L5 {1 O7 O9 S

; | s1 q$ N2 l. R2 v# M _         if(!$this->fileformat) return false; ( j$ w6 X5 d5 v5 P1 M

6 c% e; n+ L" M, P

, r" k( i6 S% s6 n; E# f% h         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; & N& R. u- q) l+ ]

- R* c+ L/ s- r: o

" ?" l; a7 ^: ]% ]; Z( L' `4 x3 b         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;* a+ i9 M2 c9 C4 M

0 v, j) F% u% a0 e; X

; x9 h$ H5 M2 H         return true; G @3 i. @, P. h6 x* S" s8 J

+ S* G) ^ m! x: [. G

2 B2 C# s" L9 k8 ~. i! ~: _     }4 k! H9 g; _, a: I! r

$ x- A+ g# s& k; j8 n

! F5 [2 N% }0 K/ Q) Z s 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。- X: _# g p* `9 C+ V( Z

( W4 `/ V" U. j% K, q* r. o

N% |" U% H- 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文件。: |, ]3 t5 j# Q

) N: b3 D& w: A, a# B

D1 i" Q& ]0 u6 m* E9 G 漏洞利用* S6 _# R* ^ o: ], A/ a

- y, @, K! z1 l

, ?; G$ }# W( A8 D: ?0 |2 ^+ B 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 ! J* V/ U" V* s6 R6 O+ l

+ f: {+ e5 s2 G" t& M( u% {

9 j4 a" ^* d' b. q' W' R! O4 ~   3 ]2 Y; m- g4 ~6 P X5 P

( Q& C' l6 A5 a& H2 ?

6 L+ k0 r! E2 c: P1 { 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid& U3 v3 Y, V4 r9 b! I& L* X7 d( m( |

# D j) ~; j8 ~( A: l; b4 D5 f8 A

1 \! U6 ?2 ]; A# q+ d3 q9 c5 D) c5 a5 p 不过实际利用上会有一定的限制。 6 X+ s. B7 |7 ^2 N

: N, y; Q/ C: ]! e( k

- w! k `5 [6 B& _ 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。+ ~( V& `" ~* i3 s

) m6 w; `8 j8 A, [4 K

2 k. Z/ q8 N$ ^: C) L6 G) s   4 k$ U3 @% Q Q- w3 T7 O c

9 w0 _& f6 l# O. G& Q3 Y* t

% I6 V" H3 [, T" ]% |* K 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: ; u% b, e, ?7 p9 T

, ]5 r7 I- Z+ _; R0 L# K0 b, h

2 S9 f( [! L+ N" H, M# i: { 省略...$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]);省略...# \0 ~. e; v8 e3 t+ m2 q2 C1 Z

/ Y: y; K3 {! p: j! d7 `

# f) j$ {9 ?; ^" G5 ~, J; Z1 d" U 因此要利用成功就需要条件竞争了。" i% ^3 D" M% y6 [6 R" I4 E

, n f+ ?$ ~5 @3 h+ N

, I1 E9 }( ?# B+ }/ _! c. Y, i 补丁分析" V f) L3 L; p( n) n6 P, r

3 h# H9 w( M Y7 O2 D7 ]; j

# N k8 N/ \- C! P% B   6 U4 Z8 T' G/ h$ e! n$ f

9 v+ r7 u8 K, x% w0 H+ F5 X0 y: ]

) U) b, u% l5 B% C, g6 o 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:7 t& c- t( P3 i- T! C( a% o

4 l: B$ p) {# z: M2 ~6 L5 @4 d

8 y0 H n% Y5 v u8 u0 F' Y function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}6 P: j4 D& w& f5 ^( o

6 @1 y5 R4 M7 Q

( o$ `# e: Q: c- ]# L+ O   ! j, y; j( J0 Z0 W. N y( p6 [$ j

8 }8 a. W) ~9 n6 [$ C5 `

# d" }' ?3 K: k% M" H" |/ ^9 @4 J 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。& i$ A) m* q9 d8 l, ?# C

3 c0 i2 m, {, Y

* @0 R, n. H7 g# N d9 o9 t 在is_allow()中增加对$this->savename的二次检查。 # f/ p9 {8 t9 ]. n3 q

/ k+ Y% ?/ J4 l$ J# \$ b- [; E

8 \6 J7 B2 N+ N& W- ]& ^ 最后 , b, V; q% o% q

' E2 ~" V. {; S9 r

# |2 x/ {2 d# M' w 嘛,祝各位大师傅中秋快乐! 0 v i0 ]+ x0 W) q

$ K8 C. A* P7 H$ ^% }2 Q+ _

2 o6 i g1 D4 t; g' ~7 B0 ~0 T  . G: N: v3 }2 o( P( q/ Q4 F8 t

: `$ Q4 I* p' W3 L/ m3 S8 i
回复

使用道具 举报

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

本版积分规则

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