中国网络渗透测试联盟

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

作者: admin    时间: 2018-10-20 20:13
标题: Destoon cms前台getwebshell
- n6 r2 b; e- l- b' x

& _1 E* {8 `3 `9 k1 K! K' K* P; i W

) S0 e+ z8 ]) ~

# G7 x+ v3 L* Y9 v2 s$ Q' V 前言 4 b! x& ~# v4 n3 w) e; G J

5 |( P$ E) }; b: X6 I4 ]

! v) u |& C1 @ B j 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 `) W4 r' |) S X

# c3 |, z9 v* U$ j& x$ X, A X2 o

, g5 H& J; o9 X8 G6 _4 }, D' s   , [6 J0 r, d, I. N

5 d7 y; O! g8 G |* ]+ i! p( D! r

1 a+ S' L. T7 w; t* f; D8 j: |: Q+ d 漏洞分析8 o1 G5 T5 f A4 F s

" g6 M* o0 ^5 Y% E

& i) e. n: z) p2 f/ f 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:6 z# F$ j% a c; e

8 P7 A1 a0 ?' c' Z

& K9 r0 Y4 j0 h$ L4 d$ U  " U, x5 {& E; E# e& |

9 E" I5 K. n' h3 G! s: t- g

& C' E8 H4 a! N. v! J: Z% { 对应着avatar.inc.php代码如下: $ q: X2 e- d3 J# i3 G" F+ @

5 \5 I5 R1 n% Q0 q

0 f3 n O9 a7 I; [* ?! y# z <?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) {# M; c. o! B% d- b2 P& M) Y

/ ^6 \# o2 Q* D2 z1 k. b

8 P9 \5 \8 r0 z/ r; L; D     case 'upload': 9 {* }6 C$ p1 ^7 G5 T4 q( }- e% N

$ M& Z9 S; u: \5 W

+ s7 n/ u7 Q% e% K' Q% X         if(!$_FILES['file']['size']) { # H. A/ M5 }2 ^4 |7 x' \6 a

, I- Z1 I$ a- I x

' C z& C! d) A8 k             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);, b9 W4 D6 l) x0 `: p, t: t

0 i; t" \4 i, P4 l. I% c

" [3 }+ Z" j7 {             exit('{"error":1,"message":"Error FILE"}');' b; C/ `8 W2 j6 Y

p5 J6 o" {7 ~2 a. w5 S) M2 @0 x

$ ?9 L( {7 i2 m+ b( ~* l8 R         } j: ^% j5 v1 [9 @. ~/ p% w

X4 f+ p" `9 W% W. B5 U1 n

1 ]4 Y' w3 o8 _; ]         require DT_ROOT.'/include/upload.class.php'; 4 v/ e' B3 h" V( a6 k: E

5 L4 e4 b7 f. M; {7 u) v2 s J

' Q& E! B* y0 D4 w7 n( _  - n0 g7 n* d2 Y, ?) i& [

. z6 c4 K3 U, E

6 m! x2 g; v) L2 B         $ext = file_ext($_FILES['file']['name']); & j& c7 _6 Q4 ?! }0 z, b6 J

4 O+ b7 j4 Z+ v4 ~

: k( [) N$ `1 n$ W         $name = 'avatar'.$_userid.'.'.$ext; 9 J3 g3 H- h) T) b

5 {& u: k. b5 l& |# x& m) B

% n* w! G, X$ V         $file = DT_ROOT.'/file/temp/'.$name;+ E0 F5 M8 [7 g3 E4 B: _- f

9 b t# y3 ]( |, ]1 X7 c4 E$ W

+ u. G: c) _6 E& V- }   & c% }9 |8 z: _

; H) T% M3 s$ g- o7 c4 @6 b* H

5 t% ~" l) j$ {, Q1 ^8 R         if(is_file($file)) file_del($file);% T" X3 J4 E# O, t

% {3 _ k' m5 \4 }

" z0 d) b& v( M1 f6 { i0 b         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');2 h8 ~% I/ w" O' J; L, ~( e

3 L$ {3 H/ A* X; m

; j9 r* {; S7 b* l, N. o% C% A  , z L0 x/ @ P' ?/ q9 k/ c+ y

4 S* \5 d/ J. {* L

6 Z1 `0 m d3 ]# Z3 R$ j. d9 t- f; Z         $upload->adduserid = false; / O8 [; m; q+ A

+ W( p8 N: ^, ?; K

* Z% @% f+ M: K  9 k5 u' \# f5 f3 R6 w& o2 m: ?

0 a$ l+ [2 _/ o$ b% B/ ?2 y1 B2 w

! v# f2 H/ x- A; [7 l3 r$ [! w( |         if($upload->save()) { 8 q) I Y3 g+ J( L& o

: e. q& A2 k ~& r+ }2 N- ?, G3 A

. o( I$ M! b9 A8 e& {" j             ...& s& `3 y/ e7 ]5 ^

% _0 Z) r/ ~( h8 s8 @, F _( v8 G

7 P& O- G9 ], K( l, u9 {         } else {; i5 p0 @ j9 E

* J5 q( @7 O, o9 \

p4 w; I2 L1 n! w9 ^             ...( J, @, w+ h# G/ H( e/ k$ |

X- I7 o6 m7 p7 t7 M

7 ]% f/ x: b% _" x: d, \- ?         } 9 s$ ]! R9 N) d2 a

" Z# W% R# M, m" L

$ e6 [* G: o \* b     break;, x$ ~* Y6 Z& P6 {# s; O6 P3 P) V

# C6 X8 p2 {- A7 H8 j# E$ _: V

0 J6 J* u: l- O' B: @; e; G e 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 7 H1 |1 h o, m5 [- s

# n# M' u0 O+ V: o0 L( P

4 ?# N- k0 Y& W/ ~; I3 O upload对象构造函数如下,include/upload.class.php:25:6 z! p. E7 n. ]% O; y

; b! B+ G3 c% L) K* o+ k$ C( I

0 N4 E6 a. X8 z F" h <?phpclass upload { ' L. ]: e( D6 ]* o9 x2 D, A7 J

5 ~4 o6 B# ^9 g2 D% P

& b" U) K! g3 {! H     function __construct($_file, $savepath, $savename = '', $fileformat = '') {3 {: f% O, c3 s& D

- D( M. }; B+ \: @; i

: `6 D1 l. V( f6 d. I7 u& R* Z         global $DT, $_userid; & u v9 ~9 g* L

, x4 u3 \+ v- T; ] \8 Q: ]

8 V7 ], u$ `- T8 o         foreach($_file as $file) { $ J2 F3 m' F, a6 ^- Q; u; t" R! a

6 N# x- O9 f. b3 e8 F% A! @+ d

% V$ q" s; F/ e' ]7 h* q% ?' C             $this->file = $file['tmp_name'];' L8 l, h& H2 t3 X

/ P8 E, d7 X; X0 r6 N! J& M1 \

" V- u3 E! }2 S6 d             $this->file_name = $file['name']; / M% D- t! r1 _) p

) w" O$ K# \( I; f

- c( Z" q7 P7 F8 @2 ^             $this->file_size = $file['size'];7 x9 q; C e# d5 D

* g8 G" E3 ?& T7 j. w

& _; F' t' z O( A             $this->file_type = $file['type']; " |8 \% \8 p/ B9 x

% J4 N3 r6 @6 w6 [; s

5 f; `) f9 w: P4 _% d6 \             $this->file_error = $file['error']; 7 W# \% H0 ?+ E0 s$ b

6 |$ k% s5 O# u5 B; F H& |9 H

, N3 k# [2 {- k4 P) v1 h   ; h; r- g5 D |

5 ? `. C( E, a, g O$ V# S6 j

8 G A6 ]% `1 s$ E4 q! j! y         }0 _7 ^+ e |* s, @* L4 ]$ W

6 Z/ e! H0 D* g) @9 W: H, n

- }8 o8 c* l H: ]) ?         $this->userid = $_userid;0 r# X3 t4 o! v8 F$ w5 B

0 Y& e" E% B# L& P; ?) E- L+ _. q

; t, Z2 P) a8 H% Y: A8 P7 j8 f i         $this->ext = file_ext($this->file_name); 1 C8 k* p d3 O* W0 n9 n8 d

- H; z( ?6 Z0 e0 L; l$ `9 p% r

2 V( }$ A# f; ]; o! e# O         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];7 ^# {" _" b& u W- m

0 D5 h3 N' w; W

6 Y6 J2 F4 h r! t         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;0 ^+ g. q2 T) u' o/ s$ Z# M

P; ~6 \+ C$ ^& h' T

^3 L4 e q5 e& P         $this->savepath = $savepath;$ c( S# n) K! ^" Z3 m- o

/ x& \1 J+ U5 r& k: S

' C+ G# \$ h0 _# y1 D9 }         $this->savename = $savename;/ E! e# Q% `# g

8 o' {: w4 y6 N: J! y, k" l

9 r1 j( r ]* n+ w1 }     }} # N0 J3 A x! q. ]

; g* \4 z/ T3 V. c |1 @

. y3 `( {$ B6 o# V& @ 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 3 }" S: W0 }5 t

6 R% `" T1 b, ]# \+ r4 u

" N8 o/ X* ~7 @1 @+ I1 s2 q$ \5 k 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 6 y2 V* N Q) i

& F+ [* Y" ]; V7 ^2 [

5 a; T. W& X( ]9 f! ?: [! | $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.php2 l+ c4 ^5 a" h u" v, f

4 @7 o7 E: r, p0 `2 r

! Z- L2 f% a. Q5 ?! ~3 B# o 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:$ c2 Q8 `% n1 [- U+ F+ [7 y

e+ i8 W2 v" P: N* ^8 Y

0 D& y0 q: e: [8 M0 [  5 Y- Z( }/ a4 q& o: J

& M6 {9 R, S. _% y0 a c

" e, T8 o: }) h* N- Q l 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:' t7 E3 L1 p- r6 e. f

Z; ^9 l. _3 @9 ~9 \9 s

. j5 ?" d# s3 v& z9 C <?phpclass upload { G. b! w+ s2 D; A) @

M* O" ^4 n# e7 R" H2 I8 c

1 q4 ~8 L% |5 ^( P* `     function save() {& p7 H! R* A( s. J' r* f

& _ j( w& l) Y$ n$ Z3 X8 K, G8 E6 P

' b% `. Y. ?9 \5 v' U9 Y: q. Q         include load('include.lang');* b: x# _3 c8 K8 `1 C

2 J' i J& k4 k/ s

1 ^7 S& u3 f/ p, a7 q5 J& H% ^: N         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');# X, T$ z) a1 {& F

6 `* Z1 u$ {: b& c# M, d* L. d/ M

6 l/ n/ m2 o f3 m5 `/ Q   + @+ @7 g1 {+ i! N [7 N3 j/ m( e

& e0 E5 i. q' }9 R. j7 h- R

* s x9 F3 U: r# k, I         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');: J$ p4 Q- A r& h

: Z; I% T3 f* h* [1 ]' u

3 N( y% a/ U' W0 `1 f* O   , _4 Y5 i- Z6 Q7 Q1 Q

7 q5 w* R5 {. y! b0 p- P

: Z8 B+ i" @8 R2 F( f6 Q* o         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); ) E, b# C3 k# X9 A8 \

) g) J' o* l3 \1 c4 K9 l I

. x* G' A6 H8 j/ }! q5 D   ) e8 ?4 n" `5 d8 v4 y- }) Z( D3 d

0 p! v$ ~4 z/ z2 G: P. {: U0 Q

d' ~- O# [! x8 R8 k& N         $this->set_savepath($this->savepath); ( q- ?6 a! S0 b9 z$ u/ m: R% J

: W8 r! k) g2 N0 g, I7 ^6 Y$ x; l- G2 \

) \& O$ j- J9 t         $this->set_savename($this->savename); / z& c1 {( H: [8 z: c9 ]

M' X, l/ E4 J

; I& z" t9 v4 G4 A$ S9 r- D# u; ^0 i  9 n- @; _2 y: G& i

/ `# y$ ?/ K; F. v

2 o+ d- V* w8 n8 N$ _* P9 ]' l9 Y/ C         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); E6 Y. X0 v8 Q$ B2 {

! E5 J# G4 A7 H; _8 Z

. o) f& k8 Z2 b# H, U4 @         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);' y: N# T4 ~* U

, S* p6 o3 X _+ r) R

1 r# N: N# r0 @, r' q/ Y9 Y         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);+ @2 K6 K- w3 c8 N! `4 f

$ P! J, g- h7 H) r9 n

% {$ C) F: w( n; Q( u4 ^; l  + x; O7 w. ?$ c" V$ H

- P6 ~$ @, h$ q: q/ B ^$ o: J

. q9 g; ?; T; k         $this->image = $this->is_image();; O4 H% F# h1 z/ _+ _3 |$ X$ c

' K: Z0 O0 f) v) ]' y# \

, s9 D7 m4 f4 s) k3 V( X' q         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);5 k0 b8 [( K* f0 l7 V5 W

) o- ~& \0 l8 r9 n! Y5 D

8 x; y& P4 b5 }         return true;" K, M! w i* Q* z

- S: h6 O* V( k2 R' X/ |0 l7 R

6 A4 k. o- M0 L9 S5 f     }} T A5 R: Y5 J: `

; t$ {% U. u+ h& C

9 H i! c% b5 M" l O 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: ' X6 K& ^3 U( d# Y$ |

1 f" |7 A8 q& X9 U0 |

8 }+ E" w5 b. `% b5 D <?php - R1 x, @/ Z6 s! O! h/ W

: [0 g4 N/ _, H3 c# ^

4 ^, K/ j! L7 h+ {7 J3 L' n     function is_allow() {* B! z% w' G# c2 ~! u

: ^2 Q k, H6 j# E3 f; B6 e9 e

% b+ y+ l' O0 M% X         if(!$this->fileformat) return false;, b4 w; P3 l6 T/ b# L

2 l U* Q E8 O5 M7 k

* _' I0 e6 x$ C+ I7 z2 o9 ~' I         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; % f+ {9 }' h4 A* I& L. V. p' Q: e

5 C6 E. v K: Y! ?5 N

' t) L8 z7 u t" a O& \/ x         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; 2 Q- a1 _1 m" q2 A5 U1 M

/ c, T2 T# g( \6 g9 J, _

d/ m4 O l+ w7 h: j         return true; - [. E6 t& C0 K( \$ {# z

0 L' Z" [0 i8 B$ R' _

- F& r2 l: M- p3 u; v Z     } ' a- z4 R4 \+ K

! S5 z. K$ E' l" }7 m

& H" N9 @2 }2 V. }; {$ q" E u" E 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。& o% o' ^: c. ^

# N* r7 k! p* `! D& k; k! f

! y3 ~4 z& G: D" 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文件。( O# R {/ H7 l; }5 n

$ {7 n1 l3 f g; A3 h; m

3 P2 P9 Q2 j. h2 \" O" K8 j 漏洞利用0 d" v; j# b2 D2 D2 R

, Y) m1 s& J$ ^, ?

3 w Y8 R5 |4 i 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 ; U/ \$ ?5 R5 ?1 X+ [! p

7 n& D i7 x3 E8 a

8 E7 `) R' A& v; @3 B; W& e2 ]   # J# \# C4 t7 h

& \% S0 M, U/ s; j

) m1 g+ o7 [" ^9 `4 v 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid * a9 f- i; W! Q( D, h: b' x! N+ \0 J

# s2 V$ C. n0 M7 R

6 J2 r8 [8 B4 D1 p. ` 不过实际利用上会有一定的限制。 ! V+ t2 {% N0 N( i: E

! x% j# ]+ Z7 E F

2 \$ q9 S# \( u# x 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。6 f7 c# o* B: N: k! d$ j- t9 N5 ^5 S

8 `! x9 D! q5 l1 e/ w* i2 R3 c

7 d6 i$ @8 P) n# [1 n6 A5 F  " F3 r. `& A* R

7 o3 x; M6 A7 s2 I

! Y: f$ m$ J a. I7 u7 J 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:! k& @9 L+ J7 T: g1 _* o2 W

2 \. K' b9 A* m: U. K! P+ n% ~" S

: S- M, D8 G; Z$ }2 \- 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]);省略... & K2 j8 u/ o( g* M% Z5 N+ u! z

, _8 G& Q" u! C) R: [ X

" o; `+ {1 e& ~5 \ 因此要利用成功就需要条件竞争了。 . ^9 A8 a( { S1 J1 D3 Z# R5 x

3 |7 @/ h- M \: o" P1 Y

- U0 k1 g9 |8 _2 X5 A2 h 补丁分析2 L2 T' f v" r' r& U

; z5 r7 J) i7 a0 O

8 @0 J) D1 z6 x6 H, x# w   ( G. e$ V$ Q5 d# x2 }2 q; f" h( X

$ M/ C0 e3 ^ t1 C3 } ~

- h' O* B! l8 Z7 @# ~; K- ~ 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:3 M9 S+ ~3 p# U# g: d f; o

6 w" l- b, o* M2 p

& w8 q# ]1 `( ?3 ^$ h function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} - M3 E! Y; r# k* w5 _

6 y5 E8 k% p3 ~+ i1 q

) k# ?, W( f) @# L0 K   . Y5 e9 J# y6 D& j; n0 m

( n0 R- m) u5 \( b- {" ~

( L+ j) ]' K: d4 S% M 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 0 ~7 M4 ^* H. R

% }( E: B) D% T4 G! k4 d9 k9 `- |

1 c; N8 e+ H: a! K' L5 g8 B4 Z 在is_allow()中增加对$this->savename的二次检查。$ s$ M, j1 g- N: S

, O- |( \ A0 T. z

! } ]/ T" `0 n 最后 - Q7 H7 K' Z& A/ T4 {5 h% Y

! W a1 v' {* K) s/ t0 m

! Q! g7 H8 j2 c/ j# Q 嘛,祝各位大师傅中秋快乐!# w+ Y# l! R# w

7 q& x2 a) t' g3 F4 L5 [8 y

- i5 Q1 A- Y+ G  # A5 X9 w; o/ p) V. P% o8 C

. k; y. h% q$ E H' p6 a/ I2 j





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