中国网络渗透测试联盟

标题: Destoon cms前台getwebshell [打印本页]

作者: admin    时间: 2018-10-20 20:13
标题: Destoon cms前台getwebshell
! `" Z0 A/ R* m8 \

) v: E1 A8 `0 r1 W, t/ o9 D. d

9 ^8 `; P* o8 R6 ^

: G$ X7 ?- h. @ O9 I 前言 $ A' A' B; A& B" m

& c. v5 x, G" f( p* F

1 y4 A. c4 o% b2 I 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 , G) q9 H0 c( ^% _9 L7 a0 d

\* R* {7 M- x; U" [

$ R! e/ z2 }' Q+ \   3 z$ `4 ?" P3 a

3 z) C: b) a5 C7 R% V# X

/ s7 b+ }- `! B0 N 漏洞分析 # _" M: S9 f q0 b$ J1 T1 ?

& V( c8 J1 H9 w6 x3 Y

* N' J2 W: Z5 P. h# \6 J4 P' t 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: ' p. D1 L2 W6 O" H$ _. i i) F

1 G0 F! |2 y- c6 r

. H9 j3 s# u) s* i& H$ K  % t) ]0 T9 j, S. K

) Y1 s: X* V1 J0 U8 X

- q' X9 Q8 z/ S) Z) { 对应着avatar.inc.php代码如下:+ x/ O* u6 s6 r# |5 P% I* X/ }

6 V0 p0 f2 l' a" g; a# t

5 X; C; ?: \3 m1 X- 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) { u. a* l5 C% ^0 ~! \8 R/ B

: Z7 S* K8 e! P

3 r" G, p9 q! B4 q6 ?* k! o' B: a     case 'upload': 3 ]( C2 a& c0 X% I5 o

7 C) q0 ^5 t: S: q' b o0 {) K. }

4 ]9 d2 @' h" m: q" ?         if(!$_FILES['file']['size']) {8 x9 ?0 b# |$ x8 {/ V- c

7 g& ^( _; V/ O1 S$ F0 T

9 J4 z- e! B2 J( ^             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);3 D# z4 z7 V g4 H/ D

# I% ~. ~7 ?5 ?& o0 U( o/ s

* g1 ]6 N3 K, ` E3 s" B; A$ @% c `             exit('{"error":1,"message":"Error FILE"}');6 Y8 Y. {4 \& D3 l- [' P

/ D2 r/ N$ P, @' c' i

, o1 u3 ?# G8 a8 P" G         } 8 r7 `9 M$ K* [ Z+ Z6 X& C

3 }, R( ~4 Z/ p5 q% U

1 u+ h6 ]5 @0 r, Z9 |; N         require DT_ROOT.'/include/upload.class.php';% H, g; [$ o) L" v4 q2 L

* h" T' D8 X; G2 b/ v7 o2 g) e

$ L3 ?6 n! _3 k   ; c" m/ p1 a" X A2 r) \' k

5 ?1 M* i/ r& m4 }4 \5 w0 }

8 K3 [$ ?0 Z+ Y' z; a         $ext = file_ext($_FILES['file']['name']);- B* w0 p& ~2 l0 @1 i4 P6 ?% ]" w

1 A. V7 A/ j7 [6 W. S

0 Z3 X7 O% h' Z$ ?( ?. c         $name = 'avatar'.$_userid.'.'.$ext; w y* D: R ?$ _% _" D# l6 }

; \1 Z: I1 C+ ~# }6 O) M: C

; W, h- j$ L' d* {' B. o; Q# F         $file = DT_ROOT.'/file/temp/'.$name; * z5 L' I) Y4 ]

8 w$ v( ?: c1 Z; w' m

# }+ G0 G1 R1 `+ i. Y( W: c   ) g; u5 T9 A! C6 B% I5 X

+ a2 p8 c, u) M3 G+ ~

+ D/ x: F& Z* {1 w) E         if(is_file($file)) file_del($file);" o, P: _* N Z2 Z& m7 Q; J4 @

6 p- ~9 ]% _5 A) T

& F2 H! |* S* Q3 F+ M% _         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');# G2 y; Y5 n! J

7 [# p0 S i- e3 @2 o# d+ b

4 E C, n3 n6 x" o& U8 s( w   Q$ C: k* N- M% Y/ `

. I4 r ]/ O) o+ y$ L

3 B: |3 P$ a3 R         $upload->adduserid = false;2 |* N& W* h0 a. y& {3 k" [2 x

. w h4 v- c4 @( f3 Y7 r# n

; m7 i1 E) \8 q8 @   & I4 @; c! I% Z% ]

4 n$ S3 t" v+ z

3 K1 O0 M s4 b/ m1 l         if($upload->save()) { ! k" L" Y, A1 Q( W. x

$ p7 d# D- }3 w" E; U2 y- L% {

8 Z+ j" c0 K0 l; E             ... ; q& n* U; W" ^0 s" M: E; ]

2 a9 e& b0 W# n& z( ^) l

% C" A }; q9 B* ~/ |( o2 n         } else { 3 e# z" B+ E" f! I$ D, [9 K+ [+ R

; D- V. y8 q v1 X

5 p$ Q, f1 A4 w( C0 L; F, H             ... & M' z( B I* p! Z! Q

+ I& s# N: d/ j0 y* x I! h, o' R K

$ l; k# w% Q, N2 _/ E; F         }/ d( E J6 S' y/ e, [

- q0 q5 p/ U' K

; j2 L5 y- \: D6 z7 P# T V% e     break;& G' l- c, F( s" c

2 P1 @- V: R3 A0 y; f$ [0 @( `" I! |

# Z1 l' z2 z7 N; H" p7 b 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。* q$ c0 [# l4 m& _ V5 d7 `

+ [; u E1 A4 ?5 Q" W$ g5 p+ x7 C9 l

0 G+ T$ T$ Y: ]. A( }3 c upload对象构造函数如下,include/upload.class.php:25: 3 M8 ^* t4 A! L# i/ G- K0 F

( `$ G. X: x N" Y" D" X( \

5 [3 b5 j; y; \' r* I8 P8 q, J& ] <?phpclass upload {4 V$ ]) D0 i1 Y+ ]( X

% E$ a/ O3 O/ p! H

8 c7 @) D6 s* s d( y     function __construct($_file, $savepath, $savename = '', $fileformat = '') { % A" ^, B: F$ \6 \ h) @6 s- V

4 y6 H; F" i9 [1 W5 r9 q: d6 n( C

_% I" E- h2 Y. _         global $DT, $_userid;, k7 N! N# x0 x- r! m6 U

7 _$ R/ X' Q% @. A

' ?4 Z' P4 i- o5 M+ s! `% Y         foreach($_file as $file) { : Q" O- Z( j2 ?$ \" W. q8 z

& X5 C5 ]2 z9 b- y3 w, U4 f. U

7 H# X: k" K6 Y! W: b) Q7 P             $this->file = $file['tmp_name'];& {0 {6 B8 V; C/ d: p; z0 u+ E4 ]+ u

. T6 d7 `) F7 ]8 `' e; b

8 W" `2 n' v% h, z$ f             $this->file_name = $file['name']; + P' G# g4 a1 R- `4 y! B! a

) k6 l; W# m1 D) N

9 D+ G( e J/ e. U( Q8 ? V             $this->file_size = $file['size'];5 U! ], z1 `$ c, A3 c+ i! {: o

$ N# @* z0 A5 c) i# D, {6 C- b

' d8 S& ^& H* o- S) C             $this->file_type = $file['type']; l" k7 o( s2 o0 x

) j, w' F6 Y* J

) j" ]( S2 _* V             $this->file_error = $file['error'];. ~/ y$ \! P. R$ `/ f

# f- E7 v/ r3 a9 ?

* s- ]7 H( L3 G5 @  2 x% t1 i) I3 o4 D# L) S, R: g! R

. N( }4 K+ |: K5 g: B

( e; U! d; L! L" L% B9 P% {# e         }6 e8 a% J, _3 C, d

7 Z6 ~7 P% [$ L- i

3 t! ^/ e' M l, R1 I         $this->userid = $_userid; : d* w9 m8 a: k

/ t3 z/ z! ?+ p

3 n5 ?$ E9 a' s- Y& \& f" ]         $this->ext = file_ext($this->file_name); 9 e8 i: d- H1 G! `0 A$ }0 \

0 H: }% p, C! [ F7 t

( ~& W9 ?# c1 _* @+ w2 G         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; ( s N. n7 U% ~) F

8 N& J/ I+ H- ], Q+ [

1 s; d# o% b* Z         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; W3 ~; b. H) U8 C5 @

~. {2 G- ^! X% P# d

3 Z' ~, ]! ^ s$ a         $this->savepath = $savepath;, {% `6 f5 C3 x$ h: Y" g

# Q E, k; t8 B- a! ~" r3 o

7 u f( s" r6 {+ C% d4 t! c. @( U         $this->savename = $savename;) V% n+ w o" N4 \

+ Q- v, k; H. V

8 z/ P% e3 C! B! T     }} & o) M- j) l1 H

; J& X- q( ]; I$ i( h

9 e- B$ a- t) p) C" Y# c0 u 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 2 v/ o* g1 {3 X7 _. J( v

% t! d# l* @6 e7 k

_) K" u. ^% ^3 @6 `) o/ N 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 - D4 _0 v2 [: e9 t* T# F; e% x

" I+ E* M& f3 S3 S8 D$ _9 c5 A6 ]

6 C. D9 o9 }2 Z $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.php3 o4 h1 ~( `9 X! u

% N. @' K$ F: S: W/ Z

( h% Z: k# W/ g, x1 ]9 ? 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:$ T7 Q' O0 G% j% O: [

3 |% D- o5 G. |$ G) f8 E" {

$ E6 i/ f0 z; x- ], U8 a! {* Z; i- h  & d# S, z$ X' D& I! l

/ M: j& c% ~& Z: V

( A H2 Y1 C8 U% _7 F2 H 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:! g, ^1 i) h5 \4 n

' h: A4 b, ?: P2 r

0 d+ Y2 H' j* I8 H' v* Y1 [ <?phpclass upload { 6 f6 e2 S/ g1 L1 [7 V' Q

8 ~2 ^5 w. ~/ [

/ n9 X9 Q `9 f( k+ q" L6 o: j, F! g" ]     function save() { 3 } e8 J1 E9 x/ W

1 v( c/ a5 o/ }" H; @; g* v

1 P, l' Z3 N# h% y         include load('include.lang'); 6 `9 ?8 x$ b, f3 u7 [/ h

" z8 v2 G( o6 h

6 S) @! H' q+ W% r0 u' q- `# B5 O         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');: O) w: _- n5 \2 T( ~

# \. l3 N6 O1 x$ Z9 r. L

+ W1 H; q# d7 f: q   6 }. {2 f& e4 ~4 I- Q6 A

\& y3 [1 E; q

6 {4 b% ^1 R' I% ?" Y7 x         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); 8 U; B6 c- _( v7 J, ~! Q3 A

' R# H! S5 \2 l( u( v9 s6 A0 }6 B

- \& F1 ]8 u5 G9 O7 x/ |- n# v* z   9 K- z& L6 S6 M5 Z8 `4 i- @( Q5 X& J

! `( U; H( u U9 w; }2 c

" n1 N! y( v8 E& i D         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);) i3 \: T$ b- ?) a& B

1 g( l2 B9 {* @3 _0 Q" c

0 i+ R- R, v% y a, ?' p   5 a9 r- L2 ?, d3 V- _

& m# { C; m3 e9 l2 c; Z

% G! h0 w" `1 r! t9 u         $this->set_savepath($this->savepath);5 j/ H; J- k! s' T; Z

* L9 \& c5 L5 x- a% p2 A

7 c0 l5 T1 Z8 j6 j0 _: ~         $this->set_savename($this->savename);- I9 d8 G& [- J* }, F' W$ i

% v3 H" w% a" v4 }' q

) p% z9 T# X: t( k   & X! \7 s9 P5 t- r

" S! P( F; E( C0 E& w

) M1 _5 d! Q6 U: ]2 G         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);1 `! W z" H! j6 [

# ^* r* k/ |6 D

; U8 X$ [; r: ^! M# r         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); 2 M, R7 C" I( p

$ d U, q" C, v( Z$ v

3 l; i+ ?6 x8 {5 C/ a- o+ E- J         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); 7 x$ j/ M' P6 h' |& R# N

" D( J" ?7 H. ~! p

1 T" w. V b1 a$ V   # t0 l1 U3 y B3 y1 _6 W

2 z4 A0 ]: w7 y3 \: m* |

! O* b# G& d: I. n. ~) ~( {         $this->image = $this->is_image();9 v0 T) q, b- }

% j1 ^( ~7 k0 A2 R7 n% A, X

) q; Z$ n2 y7 @         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);& J$ ~* V9 L( f& v" P1 H( x

B+ W7 [: y5 {4 H9 C

, k* c& j9 ?' ~- E% y9 J3 l+ I         return true;& E' O5 u$ K/ R

& G9 R# ^ v2 u* k. a" L

! D3 w# U& c" X6 W' r     }} 9 K- K8 x" R% H' X! g( a7 L

9 _2 Q6 `3 z( I4 q" A

. y% {* j0 [- G( B2 R( f 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:3 ~# S7 B; e; n) M% h ~9 d

( L, Z* L+ p8 k1 n' }

! U e. s$ v+ m2 L+ n3 W6 \ <?php 1 N7 x( e7 `1 J* O

9 }$ L+ K! }% G( ^0 Q: I) `9 W$ m, D' @

( S: p8 p& w6 h/ I: R2 ~* O     function is_allow() {4 i8 t6 W7 ^' o/ w- j% ~5 e

3 n$ E! i X9 ^

( Q1 j4 ~1 Y6 y; `         if(!$this->fileformat) return false; 1 ]5 p7 n% w+ N: j, K# Y; v

2 r N0 i: E( H0 F; K

) v6 L" V1 P. b         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;# x! K& D- G: i5 a4 @ A: G e

- U* l5 j$ t0 _' x$ J- r

' l7 `9 v0 Z& D         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; ' T# H* Z h9 F

8 ?9 h2 M0 j4 P6 i

2 O! u- Y$ D. R7 p# `3 z1 f         return true; 3 ` R/ s O) V* H3 i

2 h6 s# c* |! s; T* K$ o( v7 j

( w8 @2 I5 e& f4 i; _* O     }" ^8 b" _0 h/ _6 A' m

2 V6 T9 N6 Z# e& w

2 C; |$ S/ t$ V, z/ W7 m 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。; u3 L& ]9 t) h* |; Y

8 U4 {. y& N5 d

9 P6 I+ [% Q4 z) {) V1 \' | 接着会进行真正的保存。通过$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文件。 6 H* R% z, |% e u t

: Y2 [# U) a9 W/ N

% U" c' Y& H V8 B. ~4 h1 u 漏洞利用 % @$ \7 v6 D, o! r3 Y$ |( h% C

, h: o+ m6 J9 B: f) I5 X7 o

) J2 Q) [; d* s) b1 V b 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 1 e8 f7 l0 Y7 M) S

( k* ^; B' t, {( ~8 n" A# O

6 `0 i2 [% m; n1 e) }   & _! H( _2 A4 N, J

: |7 u" p5 Q9 S( `1 v5 s7 k! M

/ m0 |, |7 z8 O$ h 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid " [4 ]9 c/ m+ G5 f1 \

* e1 i7 L4 O" w

; j9 r- L5 n- g! z# F 不过实际利用上会有一定的限制。 * E9 B' \- l! @; n& k/ E2 f

3 V4 D3 c4 J" V

) b( H' e2 k1 z8 s5 D+ a- Y 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 + r8 }. y8 e+ ~+ u5 r- W' C6 u

0 B7 t+ X8 y$ y" y' |% [0 u. D

$ z4 X5 @; s: N! g: R* _   8 G0 h3 ^7 @8 d; L, L% o/ Q

7 a( a3 B F3 N+ s# r

" _' W2 P, _% X, |4 P( |! p- @ 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: ; j- W1 n) u& k) g; e

$ n X- }( q6 c6 E% E

& }4 X, w$ i; b' a' z' t! | 省略...$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]);省略...( ]% I3 j, a: h) w# w/ V

* J e. V" u; k- U7 _ O" E T7 \

9 ^6 B5 M) |2 |9 N0 d6 H+ }( ^8 ^7 ~ 因此要利用成功就需要条件竞争了。" D. j( V1 i {- v0 g( n; \2 ?

" J+ u) W$ G# M/ u( K' @

/ \9 W# G% A+ r6 Z6 P# X0 ~ 补丁分析/ o7 X9 Q; K) w* v7 m2 I3 I

$ B+ E4 d' J& B! C

: V* w0 ]; ]' X' _3 C  " A$ X- D( ?) W! k: ?6 v

% u4 X6 Y( C0 X/ t' l3 e

: k: d. ^# V6 z' n1 d* ` 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: $ ]! @* X2 y5 _. c* Z8 {, t' h

' H3 X* }) w9 [4 D P

3 S6 R% S( ^ [9 a1 a: W7 m function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}* h/ f1 b# ^' o$ c) Z: q; I/ _

4 B0 f, U. q9 z6 G9 n

( D4 S2 d3 X! {; }   0 e: ^0 B7 m7 x# z1 O

( s, n2 {3 t, R1 {

$ X7 f5 }* V+ ?* W6 J 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 ! v6 ?" ?$ N& D' a* P

: S! A, [, n# {, K1 A6 X

, q4 i0 ]+ h' t$ Y) @& h 在is_allow()中增加对$this->savename的二次检查。0 }2 ]( t H- [, f

m% H! a! f5 l3 s. m1 z' _

" y( g* A5 X e" y1 B' k 最后: g& ]$ T7 m1 U+ Z

' p k$ e& @: v, c( j* i) u2 p. B

# @$ N+ ~# h; ]" j$ Z3 T, g$ Z 嘛,祝各位大师傅中秋快乐! " b7 S6 K& H7 Y6 ^& s5 \" C! f3 j

9 @5 d. _* k0 V5 r1 `# J- s

- l* p9 P* F8 v# x2 H' j   ; r! M; @* e+ L+ _/ O3 ~

- L0 ^5 h3 F8 m& X* d5 t. b0 [





欢迎光临 中国网络渗透测试联盟 (https://cobjon.com/) Powered by Discuz! X3.2