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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
8 H: q# R. b" L% Q

* U9 N; n, ]/ ^! H" y, D9 k

/ H5 K& \# j% t1 c- H

, [5 A3 S& c8 ] 前言; l) b3 }4 }& ~8 s; }

9 X1 @4 O* i- W5 ?

2 [8 V9 ^1 J7 d6 W' k 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。" X* a6 ^* Z' \ \% y5 p; }5 c' y

1 q8 Q8 |. k# K( p" _" J4 m q

- _3 B/ D; H+ v! z0 s6 O  5 [+ c: v- n% K, l d

: ]0 u+ K. K$ s1 Y9 s4 B

2 `" I+ a. C; G 漏洞分析 5 m( h* m3 T& T% x0 d- x& ?

6 y3 J/ \' a8 r0 @ x

( T" M# P0 R6 I' e 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: 7 z" B( l4 L, m9 |+ r0 K

) l. V5 A) K! m x

: v8 S* f3 G S( t' o   ; u2 m# t% T9 }" A9 Y1 [

0 u# X) K L' T# |& i$ P

' k' {; I b1 e1 T# K' J9 N 对应着avatar.inc.php代码如下:7 j( Y! q) X0 p/ r( A: U% J% x

0 r5 q$ ^( Q/ \5 m8 F) @

5 A) H6 C* ^4 K% _5 |) q8 K <?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) { & a/ } z3 ~- v& v' ?+ K; A8 D

& z/ B% N+ |6 H" @

1 x0 f; J, A6 Z% e' v     case 'upload':4 z7 k9 d5 V& i2 @+ {. Q- e

/ W! F( Q) r: _9 l

9 F2 O0 g) N% H# [         if(!$_FILES['file']['size']) {0 M8 p8 V) @- e

0 e/ G2 E! w6 ?, O, A" b

: \/ r) Q% j8 R a; l8 j! A0 d             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); ) n% t( s7 H8 ]! {

* I a1 i, |& {- D. a+ z

4 I9 t. j# m* n             exit('{"error":1,"message":"Error FILE"}');) q! D: v d# W1 }& ^

) g9 @% j; V3 x5 V4 r9 }

, g' L: Q& c6 `+ c         }- {9 x5 [- _; v* p; D3 V6 \2 x" \

" q% e) C" I+ c5 O* y" l3 F5 A7 G

1 v( ]1 I5 v( h, G         require DT_ROOT.'/include/upload.class.php'; 7 Z+ \8 h x u% X9 A, H

6 o. p4 P) p9 A8 a+ v0 s

3 e5 K. Z/ h0 h# F- v B   ; h7 {, S x8 K9 ^5 F$ f; r

9 @/ M/ U2 L0 @, D

( m s0 ~/ Z5 T6 z3 i. ~; H: }         $ext = file_ext($_FILES['file']['name']);1 l" ~3 e6 j5 z/ h

5 e2 f5 P1 G$ h: ?" w- w

* W: i! X" ` X! v1 p! n; Q) C0 E) `         $name = 'avatar'.$_userid.'.'.$ext; 7 O+ b+ T4 m1 K8 N6 c5 u

! B0 e9 M! A0 c

7 V8 w- {4 w C; B         $file = DT_ROOT.'/file/temp/'.$name;% F8 ]! n0 C! k

/ u1 m: U4 u R

4 [( W, w( B7 O5 ] Y' v1 s  0 f0 r; F7 ?& J) v9 F5 N$ x

0 u/ ]8 w P5 k0 `" z. v1 t

" H' m- K, z$ L3 ^* d: U! r7 o         if(is_file($file)) file_del($file);4 L) Z% e$ ~3 P$ Q6 D& C* O4 R

& t2 n; k- y; i @ I1 R5 u

. C6 D- b: y' ]- R- Y% q         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');; ~ u+ j. q. x. K( S$ k0 K

+ f: q4 {% ]( f9 ~& ~/ P9 }5 Y

. Z5 \+ M. X, v0 Z  8 u4 R8 I& V- P

$ d6 m- O! X0 n |! Y P

R* G# i$ J) ?2 F; V1 V/ s         $upload->adduserid = false; % ?5 @ G& @, y! [

) X1 G' r6 A" g" ~; \

b- T8 d! Z' M! \  6 p0 s1 C. ^- P+ l8 h

+ I% r6 q6 b: E0 _( w

% n3 P' A# X8 D E         if($upload->save()) {) f# {: O T8 u* `! N i% }

8 I1 V, Z! I. Z1 S h9 @

0 N+ ~; A0 x! X0 R) I' L, H             ...& A7 D: O8 k& o; X7 k! R3 _: \. i

3 i; \3 l0 p. r H7 v1 ?

3 @( N q" P$ R: ]+ l         } else { 6 H/ U0 v2 G3 S: X6 O6 q

: N6 V- q5 @) h( c W! p! b) A7 B

4 R @5 B. p" y( o             ... + d3 S, n8 ^. P- {7 p! ^) e% O6 w7 ^4 z' _

5 i, l4 Y1 _, z o7 h) Z7 u2 u

4 @7 }' r3 M" [ I7 a$ z         } 6 q& |# [+ A/ q

$ H# l# z! P$ \, r; t G ^

! g( r% f$ S4 ^ T+ B( P. M" @- A     break; % n# e& Y0 R) x! \2 f

; h$ a& B% p$ \" N; M; W

+ w- B$ ?$ e1 G$ T) P( }: U 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。! h0 G' l8 E) Q6 b& {

. D3 b, F5 M! ~0 T

# |0 y x7 ~6 J0 ]8 T5 H) i upload对象构造函数如下,include/upload.class.php:25:" ^; j7 y+ O3 M( `

( K7 b, Y1 a- f5 R

6 ~( g% A9 I M <?phpclass upload {$ l' ~* p T: T" z0 k# c D

/ s5 n% b3 @ O4 [. W

* ?/ N" Q4 J2 ~5 \/ ?4 Y* z: d     function __construct($_file, $savepath, $savename = '', $fileformat = '') { ; [$ c( W' |/ _) H7 U# B3 U

1 M& E/ z4 |* l0 @, u' i

5 Y; K* B' E ?: _2 p3 S- i" ~         global $DT, $_userid; $ i4 A, f# r2 u3 v

8 q+ Q P& ^, z, ~6 I

$ y+ s. N2 @; k p+ W         foreach($_file as $file) {- O; E' A( R: h' x; m

& r* l9 p, B1 w" V. Y

0 V- w1 z2 O; e$ y             $this->file = $file['tmp_name'];( l5 p3 |7 B: y" G& h$ }

7 n# L1 S3 h0 f3 r' x, E

' C# R0 a" F, P3 Q6 }: y             $this->file_name = $file['name']; 9 ?0 `: r, v6 L) O

2 H1 p0 g0 R3 }- @# l! [7 V# ^

( X/ M3 X" a+ H5 d5 C- X             $this->file_size = $file['size']; 0 O. E- L5 \9 H! X, a/ ]

7 j r* \; l8 M# T7 I* d9 o

2 Q, s+ ~& I/ A7 r* A8 c* F             $this->file_type = $file['type']; $ Q3 b0 C" F- b

" o ~' ]2 M- b+ u" c" T( x

5 P% w6 {( ]7 b, W& v* A9 ^             $this->file_error = $file['error'];$ T# n7 s- @% G# ~" b) M

/ H5 I" w" T3 L: T0 i1 q# H

0 h2 r5 ?; j3 J! ~  % p: L) Z! j- ~1 ^

, v8 A; w5 C) M* [. U9 c

4 b4 t) [- r: K# w- |0 X- k, N9 ~3 d         } 7 @5 c; i# b1 a( H# h

0 H) w& l4 E" t+ ^) l/ r& r

8 O: k( S0 H! y) e         $this->userid = $_userid;' e6 Q$ ]% S1 h

6 {) @: ~. T6 y9 N

) C/ Z- J6 c0 `( \- q9 M         $this->ext = file_ext($this->file_name);& o l* Q. X" i/ C& d( N7 H; Y

$ e4 _* a: i6 ^+ T

. G! ?+ k0 | k2 f" A: O         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];/ X; u |# Z" A' q' v: Z+ d

. X& ^. W' {; q, T# b* N8 U

4 f o7 s3 D! u- L+ E: D- O0 u9 H5 W         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;) Z, `4 ]+ x U

- k- I' r& s% F; A* f7 {$ {" Z1 A

+ \, E8 R; i( \0 \         $this->savepath = $savepath;2 P+ m% f7 T9 X% B5 h7 t

6 n2 ]- ` S$ U5 ~% {

1 j4 f1 ~- ^% z, |! L         $this->savename = $savename;! h! D& G+ I6 C2 \: s4 S$ h

' P" L2 C \$ G y. H

3 S+ a5 w8 }/ ^8 W0 `     }} 7 S# _% {3 u0 Y

3 r# y. @5 W4 ^/ k* b0 O# R

. ]/ _) h' r7 g4 O 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 8 x5 U3 Z8 O4 M6 \: h. ~/ N7 ^

7 H2 p; g2 c9 h3 R7 @

; W% S2 L! h/ \/ \% x 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 / d& g$ \0 Z: _0 x3 P8 }, a

! a$ }3 x; m: V/ Y0 i* N$ G$ e

, m2 e( S! s: A; P. m( W $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.php0 D$ C8 q& j# `; K8 V& o

& T" [" \$ b0 ^9 q" w6 I

/ d0 f" d, \7 g7 y# u6 u2 S 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:5 ~" K7 @, }+ ^

+ i; }, a6 p* }7 Z

B5 z5 B; }4 Q0 V4 ?: K3 \   9 I! O4 [2 m6 ?

: |# W/ r% N+ p, @9 _: S

' V) P8 q3 c6 f 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: . ]2 s( B5 O% r6 d0 ?( T1 |

+ I0 B( G' W# y2 V# ?# I

, J1 R: U5 ?$ _4 H" B( M& h <?phpclass upload {4 h1 z0 n4 i5 ]' ^4 N( J5 a3 {

5 b& q5 c2 [) A- }% `1 L; u8 u

, J* \- H& m* W4 k3 ^# `8 C     function save() {+ _& }5 l' D) L. e* T4 m9 X1 W! y+ [

% Y/ z) w# G, Q* z& z% W

. Q1 C+ \5 f" I$ ]         include load('include.lang'); 5 r _( G) W7 J' c, ? G2 k

, b9 c8 P! W2 g7 g+ N; }+ w

: q# P9 t$ B3 n7 R* q         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); ; D1 H# M8 ?' f5 J+ m

8 _% v4 V2 \, ]9 H0 i4 E8 v, D

2 N+ C) Q$ z0 w* t* E   " K6 {9 d; U! I [& z5 ]

5 {7 {* l) X( @5 t

- n& _0 \4 u) C$ B2 x" J q         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');3 v4 K; z$ E; D1 j; g

' Z7 t& A$ F; f/ [9 v0 ^! F

) a5 U7 H; V+ ]1 i3 z  9 f5 H$ q6 ~8 P3 W- J) v9 u

- s) V- m6 K0 ]5 u, e0 q3 a

/ |' C" k$ ]2 T( t         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); . L8 L" i0 }! M' u- W

- u" n& n" y5 ]# ~9 l# W% o" z

5 g! Y, W5 B/ f \ ~5 @: H" A  1 k" S4 J) [8 U9 a6 a/ y

% d4 s8 O4 H' K- N9 y3 G( L& u4 r

- X" s2 P8 E: B) N         $this->set_savepath($this->savepath); / ?- | T) e. V; K

. T8 R Z- L$ s( E; B+ g6 U

5 s4 s0 Q$ \+ A5 W         $this->set_savename($this->savename);6 k. o3 b, J* }" y

! s4 z+ ^0 F, ^/ f I

K$ V& q/ J+ }2 `) J9 K- @  . x' C) V4 ^$ c3 e$ A5 \

' P5 \* C2 g1 u6 h; Q4 M9 ]

8 P4 C1 Z: V, Z         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); * l9 E" X+ T# F6 _& M/ g4 Y- a2 ?

' _3 ]7 h$ g0 ^

0 S2 a; g* o: e" E; F         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);4 v' R2 \7 H$ x: C- b6 J

7 e# ]* }9 f1 d2 a

( E) ?+ ^3 i6 r1 E         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);% ~* p" C' L- t3 y ]

" D- T1 O5 {( l

! W( C% Q% ^6 D4 [% F  ) I/ ?0 _- ^5 k1 Y# m$ B! u

" y6 w' J a% S4 l; [; V' M: l8 E

! Y0 h9 b$ ], F( U; ?         $this->image = $this->is_image();" d% \" Y! H F% S

e) A7 g% a" m2 ?9 {1 |$ N

0 V3 g5 Y0 G: K         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);! [; R% t# A4 z! S9 h2 x2 m% W0 E

7 z- X$ P2 m6 A0 z

. z% m0 ^2 a+ j0 m# B" Y5 V         return true;" f$ g4 f' j7 J% G

2 M. [9 m, R+ i) a2 X- O

; y2 O. Y3 o' V. \% J; W     }} : G; C" [- b+ b6 s; d

, i, _0 i0 v- t0 z1 a" v* P* F% c

. w% M8 k7 ^; [0 }0 ]$ r 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:1 p( z u3 v3 G" S0 U9 K E

# e6 _! L- K* v! R- Q5 i

/ l% @" t( @- U$ r# P' h <?php* b. A5 O* V& w' J+ I, E

+ W$ I+ |. ?; `2 Z+ C4 \

. L9 a+ T" A2 w4 l5 I K2 g T     function is_allow() { 3 Q3 B# v3 o+ E) w3 A8 x

3 t @+ |3 l# A+ i

: p3 \/ O. @, Y         if(!$this->fileformat) return false;# x* x* y! T) i! ?* K

, `) w! R4 V3 v' G8 @8 V

" h1 a3 j z) i, y' X: ~         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;1 |/ G8 c8 O' W' o* C% Q- }! @

, n/ w9 U {) V: x; f8 E. n

$ F0 E/ \. z$ U         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;! F+ p/ |" x6 S. G2 B% w8 \4 u

' c" q. k+ c- ~( ~8 {

) D7 c& i) L7 l- x         return true;, }: H1 k4 S1 z, Z1 M

3 F6 o3 F0 G. ?( t

$ f5 ^1 B7 f! ~$ \/ e6 r     } 9 D0 \* A2 W3 w: w& A

5 j# I) @ C- g1 I" Q: L6 ?

9 L9 {9 [6 h+ s! X. A1 ~ 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。$ O1 m6 ~3 f7 C+ P+ b- ]2 x

p* S- N, i" V

3 l/ |' k7 \1 C3 L# n1 _ 接着会进行真正的保存。通过$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 s3 {5 w, {0 J

0 B+ f6 R8 u& e

6 `* |! D# t3 N) w0 a& Z 漏洞利用# M1 y: P" b# k' ]6 @& ?0 l: r; L

: s' z! l7 \" E- u# U

v) [/ T+ d! x r) z: x+ j. H 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 & E' R- N& p& j H# ~+ e, }/ R% y

) {' y% ~- N7 [' B

' [# q# F% W2 w+ Z$ ?) i   4 ~ y5 Q8 ?) U$ {; N" H9 E% Q! l

0 ?5 W; o, X" \: {

, D9 K% @1 z# Q G8 ]" q 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid& l, `+ ~$ }7 z' N3 b0 H# Q

6 E0 k) D y8 f6 w

0 W0 R Z5 j: e) j, J+ g' |$ N 不过实际利用上会有一定的限制。) n# |' x' G: E' j. B% k

9 i& }8 [9 c, Q

8 Z5 ~- |; D/ u 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。5 Y/ F8 U5 l; z% |

$ K# I3 d7 `2 D. D! a K) k

1 T6 H) g7 I1 @0 l! j   7 w" V7 V" m0 S/ z, |& W; m; O# O {

' h2 g. j4 Y" ~& p0 a4 A! w

6 f6 g/ D( e. p3 d5 P9 `" l 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:& D5 z5 O5 C) A- |$ v3 K; s

) m7 o1 i1 x- Y; l1 R/ i) C; d9 @# T. t. s

8 M8 _3 P$ Q. ~- Q* 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]);省略... , p& G! @- n6 }" w F* q

6 k3 Q2 q5 p( g$ T" K

/ r0 P' N% O5 x* f% t6 F 因此要利用成功就需要条件竞争了。3 }/ t1 m; p% S/ I9 ]# J/ p

3 m; R% S$ {: @! J' v+ _( w; o

; t& R: O, [$ j# m* Q 补丁分析6 P7 B# F1 b) G2 n3 A' w

# p) ]* Y" |. R; U* A; B! M

* H* N% C7 q' ?5 L9 \   + y' R2 W; p, v# z0 H5 D

0 E+ h m9 p" P- `4 v! e: u* ]

1 M. W! a9 m8 c# ~7 s8 B 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:. i! {5 b; w; m6 j

+ R# o2 o+ T" i' r# N4 K- v

- O$ e1 o" Y1 a function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} ; x8 H2 u/ M; }

4 m9 X+ u0 X5 `+ ~

; c. m; p' z' \: {' ?9 X   8 ~. [% [) a5 Y( l7 v9 g! @1 F

+ y& B1 B/ _/ J8 m3 D& U1 u" A

" c, a' J6 i, H b4 W3 a 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 ) K: i4 T, K8 B% m" v& b) i; t5 i n

j. w. V' ~5 y# L

6 o9 l3 t1 I# j 在is_allow()中增加对$this->savename的二次检查。 / a2 p! {! x) V( k

5 c/ N1 b* u% Q7 I5 h0 `" @

% M3 e v0 V5 h0 s2 n: J1 e3 ^ 最后4 V: k1 V" L! a; r1 D r

6 Y; d9 p0 a- Z6 s2 F9 N/ N: u7 z' ~

5 F( ]& h* v/ ?3 y+ w 嘛,祝各位大师傅中秋快乐!, {' {& F! q$ C- e

5 G0 }' b0 n% N9 H+ p# k( t

+ j! c# H B" l& s   5 `; m, h0 t: j4 z

: N' h0 X8 V8 g5 A) Y' ~
回复

使用道具 举报

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

本版积分规则

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