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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
2 S1 C' t, f: r3 {8 l" u

7 w6 x7 l6 |0 A

$ y4 }' |. W7 [ u( z, T! d5 X# ^

y8 E; A/ c0 Q) W2 Q% \+ l 前言 . n( B) {3 A" M% m0 G& k

% i8 w& t" k3 g) L

4 s2 z h/ G, h8 d' j 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 % p4 B9 x3 `% }% ?

- \& U& g d' @6 |6 f

" Q" F8 |, L% \+ o4 n3 [  0 \$ e; \. ~, D* n

) j* M, N+ _/ S% W8 @' I$ ]8 `2 W

5 h" I7 i3 {# R1 o; p8 } 漏洞分析 9 V7 B% g4 s6 e- S* {9 M

" o* Y' o( A% @# l9 ]

) t; n, A5 e( |# O- j- S 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: 9 P+ }9 m2 y0 a! \9 ]. T

0 B3 p& N7 |0 q8 |2 z' D

- }8 B9 f* c! R1 m  % k& U4 F; L# ^- l9 U. h7 ~

8 A) C! |1 q8 n" J

5 k% P% {4 Q5 i, [0 G8 V3 Q 对应着avatar.inc.php代码如下: ) W) p" m0 k' X4 o

. ^8 Z: y" O4 d8 G1 b0 `1 X) x9 S/ H

& C$ D1 w3 w& u# h2 a% Z/ x <?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) {6 @5 q, y5 q4 K1 E6 W/ ?; j0 O

& ^, s# D; g6 s4 Q6 Y4 k3 @. S g1 c3 s

g% u- s! i' f     case 'upload':5 J, |( Q+ S ?) E2 @6 [

' z9 Z3 L. \2 a& \

! u2 A4 [4 c' y6 C) f9 i$ B         if(!$_FILES['file']['size']) { * u% |, [4 H# g+ g

) m0 j3 c8 n/ o. D# y9 T* Y

+ o# s n8 K6 v4 K+ ^" @2 e             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); & B- \: A: v& p B: s

* h2 i6 E3 O% z2 G- o

+ t5 v2 T+ Y2 W' W             exit('{"error":1,"message":"Error FILE"}');2 `- k3 }) Y: A4 \3 x

7 l. \. L6 L% t! Q! v$ @5 u% T

$ C% o) t! [0 h: _, Y0 z         } 0 D- c/ z5 x7 W8 g+ X

" {- y- |/ a1 x- R+ N) _

3 `& m) G0 j+ h" K6 _         require DT_ROOT.'/include/upload.class.php'; . K! L/ D. [/ k

0 Z- v+ H3 c, w- r4 ^, O

. w" G. _& x' ] F   # T7 D4 a2 E$ Z8 j" E5 W

# K6 {- j6 L6 @ @) v, c- M! M

) l# K0 a6 t D0 d         $ext = file_ext($_FILES['file']['name']); ( x$ }1 Z3 `3 N, Z

- Z) `; k8 k% m& Y2 a# e0 \2 X

+ o# @7 }/ ~1 Y8 @% G         $name = 'avatar'.$_userid.'.'.$ext; % g7 q3 F4 E! E! D

" x" {" m+ @! j' e

/ p2 Q; ?2 v) M Q6 z: ]         $file = DT_ROOT.'/file/temp/'.$name;8 k& O" O8 _* j! O0 [0 A; W

4 Q* F* Y, i W. C

: b! o+ u" ?. p0 C3 @$ ?/ p6 A   q n) O; V. {# b* |

- _5 C: v' C# A) V. _1 l7 f

. ~3 B1 `7 F( m         if(is_file($file)) file_del($file); : S% a4 T6 U/ l2 q* i8 i

4 @' R; o$ B# m

) y) n3 b2 H$ ^: e- m3 o$ C( n& e$ K3 h         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); 7 s- y8 l# ]5 i( d, ]6 s4 S

2 e& O, ]# M4 |8 d& v; b

3 m9 J2 q6 e' b% u, d  4 m7 I# C5 D9 X9 W3 z: c3 |

) K3 u# _1 G1 b9 k- o6 N+ X

6 T \! O3 F" e6 _. [4 p         $upload->adduserid = false; 0 x4 P4 Y% ], Q0 C- _

: h2 ^, P& Z9 h: ?

& Z7 L- B4 I! c; J! @0 c9 e" ~   ( m! P# C% e3 K8 {: S; {9 ^9 h3 S

8 @0 {: o" ^& @

* P- m& a$ \5 X" x         if($upload->save()) { ) _4 L& p5 K) E$ P

+ j H3 [& a/ w/ [& b# v# z

7 y# p' \2 c9 j# D7 K: D6 `             ...9 c6 Q9 z' t* f- G2 V% o3 l# V

7 ]; S- F- F* c3 M9 _

( @/ \8 D! z( j         } else {. r: s! Q% N5 t

( [2 _! j' h. e

! }0 M8 w2 I% p* L9 t9 @ V             ...7 [# R0 @ {% E2 F5 M

4 E j1 U( `$ ~, _

5 F/ a# c6 J) l% R$ T         }' m+ s1 a) @6 x X; ?8 u

$ V& b7 e. b# q8 S2 q

( Z8 P( y1 L2 g; m+ Z/ T     break; , ^2 a# E% V' j: O" n5 R9 H

8 T- K) Y# v2 G1 b2 h& N

8 C. Y& Z; @" K 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 ( C$ Y8 Y* X1 x* F7 V$ U8 [$ r+ }

, ~ x% J' N$ k, `9 c) j/ e

6 l: K5 V' i% I# b upload对象构造函数如下,include/upload.class.php:25:* q, u/ ~" A# @6 }0 J- ?; m8 v( H# I& i

m2 V4 t9 n; L4 t" E0 Q) \

! Q! W& E4 K, r <?phpclass upload {2 A: c0 ~6 I7 V8 V

! y1 c/ N, E6 X: g' n

- l# n2 P" X. i" [" d! x     function __construct($_file, $savepath, $savename = '', $fileformat = '') { , T- ^5 a+ L0 M$ j# E

# |+ U) y9 J4 z. j% M z

! k3 ^* G: E1 s; x6 d6 [0 N! R3 F         global $DT, $_userid; 6 o. q9 Q" a3 c

) L4 A. t# T$ k' k

8 H' b) e& D6 p8 V5 P' r! Z         foreach($_file as $file) { ' m" Q0 w1 i3 O9 v: ]+ z

# `9 c7 X& E3 i7 d1 `

; g# h1 T5 \; r `% [8 @             $this->file = $file['tmp_name'];9 b9 |3 x! q- W$ _% |* t

: v) F- J+ B* O2 J# a

7 D% I) Z6 `$ Y) t+ W+ S$ Q; ?             $this->file_name = $file['name']; + g9 z: {4 H7 y) w

& G# ]7 i9 i! S' |1 B

x8 f- p, q1 r7 m2 W7 X& J+ }             $this->file_size = $file['size']; T( [/ Z1 K) M$ P9 Y3 z" N

! b4 C9 Q* R$ @1 l g5 b4 ~2 n

# j) ~1 x; [" b1 W# g2 f; {' b$ b, y             $this->file_type = $file['type'];% T$ I8 Y9 u; N9 u% G0 _& W

$ |) h" N5 W1 Z6 L# p8 x! _2 O

: V" S; e6 @ D, Z" K" f6 {, c r             $this->file_error = $file['error']; $ A& S) u3 \4 e

0 R! L% p/ K O' k

" q6 B- q& V: W. v1 [   3 r1 M3 J6 F) X: v. I, P

# h* ?/ U' u* B) Z( H( C; r

: ~% [ n+ J+ V% a$ v5 Z- I: d$ |         }/ Z4 b/ ?' N7 N

9 c, W; r; U: X6 f

8 P+ ^3 C, ?; F# z( @' ]         $this->userid = $_userid; $ g" o7 c' A2 `( z. i5 |5 ~! C

$ P1 V% S2 |% d3 m, M

# o5 g; i C( t7 J         $this->ext = file_ext($this->file_name); ' ~, c' ?4 C8 k

. T2 H. a2 l* d: ?

% ?- ~8 B! P0 E* J) t         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; : g5 o; R' k. E M

/ x7 K& S& U8 ?' ^8 P: d9 S3 P! Z3 A

3 Y6 G- G: @" E5 c5 @4 p( e6 m         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; : B0 f! ]; Z2 x0 A3 L

( N: D Z. e1 s% X2 G" m

3 t( P# H3 U# H) q$ g         $this->savepath = $savepath; + O t% N: L- ^% T/ G+ ?/ [

' _& K1 d/ r& e2 k

' B: o, f7 f g5 O7 g! z3 C         $this->savename = $savename;, _ w L' T/ Y& i

% z9 B& h7 i& ~( [( K# N* _5 r0 _

3 k. @! V) {3 Y4 H9 a N' y     }} + W- F8 w! n6 p0 T4 [; `4 v+ n6 ~) [

* s1 X2 T% R: C" O- c

G8 _( W5 y& c0 f+ M, L( Q 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。# T2 p* o) ]* m# L o4 a$ o# s

1 r3 Y7 M& M- u( H6 D

w0 F& u8 j: K/ G8 R 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 7 d3 p2 o( E4 p8 i$ i

- e7 d9 t. i. i. f+ G. J- [

- R$ ~( q/ \6 \( E' l5 r $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 1 z. j$ {* I8 X, ^, E, x

% m0 o6 H' [, x' |9 i9 i$ j: s

% F; M5 M* k% B A! h" ]8 W. | 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: I5 O) F% }* k2 o

. N1 N" l' C' F/ f7 H

& I; T2 o" _: b6 j+ J* Z3 ]  : Z0 @' g% z; p3 I

" k9 p/ I2 w4 U, w" U1 B, n. J. X C

" m8 N: w# P1 b+ i2 }# O 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:0 G. D+ e6 H2 s- o f7 A0 f' R1 {

& O C) n2 Z E: i

: j9 v: [" Q a5 p) \ <?phpclass upload {3 b4 S9 E9 P. Z/ }

; {8 Q& g# f6 k& [$ H

M/ G1 @ M, w2 w& \/ q v     function save() { # _/ `3 i( ^: M" X4 r. f

7 Q% v* a9 s% S4 a

+ |3 P0 w. r* j9 S7 H         include load('include.lang'); ) v4 I* A" n% t

N+ C8 ?/ E) F4 v! g

( x) C& [& E h7 E* p- |         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');6 k' B7 ]9 w9 X

) F5 z: z5 Z5 B" I7 h3 u) P' H

5 Q& A4 `" t' H8 v! W/ x   . C" L- Y0 P8 ?* @, C

: z$ W. E4 N1 H

8 _2 q. g/ Z/ m) r. u+ _5 J- r3 a         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');- E8 `9 j- O; T' p! \

+ D) E# ^. R$ s" @& [( h

6 v) s& }% D5 e. v! E6 {2 o8 q  / d, c. z! Y3 b1 u

W. U! s- \/ w8 T6 s# ]( c

0 ?, }+ y, i! g. f# j         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);0 g% n; x; ~; i: E9 D( y ?

0 t" ^( D, M G

8 z @3 F/ [4 S$ `* g1 [  ( C. x. _7 ]6 \2 V$ j8 {. F: t

% O# {2 c0 w! I0 r8 h- u" s

$ |" A" l7 i: Z8 F$ `. F7 u         $this->set_savepath($this->savepath); ( Z) d7 ?: n' W/ W9 b

! `6 E D' [$ l; y, N

3 T" t5 I t/ ^8 j$ ^9 n         $this->set_savename($this->savename);) w! h" D8 @3 t# ^& |

; ^' a2 b' U& Y

+ b- |/ V$ }/ G% _3 k  2 S i5 r* w/ A5 J5 j

1 i* l' b" O7 q7 Y, ~3 |" A" c: I

# D1 L* K, P; g8 \8 n6 A4 Z0 g         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);8 m# W! n# D8 e+ c

" a+ C, P$ k- H) p) N5 F2 R

9 b! k( q- n" s4 m6 v7 L) C6 J         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);- ~9 u# N1 ^1 k6 U9 n

" z! I0 l0 V; B: X) g9 R& n* P

. |* p c" D7 a6 b& x$ w         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);+ E% j0 D }8 C' c2 }

8 O* K) z* ~7 Q: @

3 y( n( k4 G! S  # o- d& G4 b2 d C4 ]$ [. J

, H8 y q% o9 q4 T

4 N( @! H! [7 \         $this->image = $this->is_image(); - M' A% S& H0 k3 w6 |

* o/ m6 n" X5 Y6 t* W+ Q+ {9 K

" D2 ?, x- p" m, m) `, O         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);" k+ A$ ]0 X! ?5 l% x( {

) d7 t- ^9 t5 B" M* g

' T) \6 M' ~! E& K$ z         return true; R5 W: h' m8 l

' V" o9 x, O, b ]( j

, g3 P$ A; z1 z3 E6 X; b3 M     }} 4 b! F6 A/ H: ~) ^. S6 v

) g k: v3 s* m! g

! ]- k- X& ] b' Y0 i. p! r 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:9 @2 A9 O2 O. G1 T. n8 m1 z, h

) \+ l+ T7 h- D2 A8 p; J' M

0 D; i" d2 P, w- j3 n, D1 J2 { <?php+ H( @. G( B% s( d. S6 W; c- _4 C

% i, ?0 G/ i9 f3 W

2 Y. t0 i! }& n     function is_allow() { ; c: C1 _0 [7 a6 T4 r

" f( P& l& @7 o2 ?7 G

: _: z* n' L: N* v e1 N: m         if(!$this->fileformat) return false;' h, q- S+ B" z8 L

# }; B6 s$ N6 n2 \7 _' v, |

% _" p+ a) q% b1 U+ F8 A, _( e         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;% z' M( E* c/ g% @

6 {) r( } Z0 k" n! M

: \5 b0 G: d& p* o" F" e5 a2 ]         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; $ l+ x% h* E3 I4 a$ x3 \8 e

! C7 S1 e, L" l, h; V

/ u3 a" Q' R4 v% J, L8 b         return true;/ X2 F1 I2 D: W/ D& b

3 z; M [# U5 i0 G7 Q! d; N

$ H' B# H1 [. ^$ E3 Y; u     } $ S: t- o6 r% }

5 z: d+ O' D2 a1 Q

. a5 O* k1 A4 _) R7 \) u 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 - p' |- M. E( J* P* @

. w0 j0 D4 Q# Z0 C, P7 q, y n8 W

; E3 [, d# R6 C 接着会进行真正的保存。通过$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文件。 : P& r+ z* }9 y

c4 p7 ^+ P U3 [. J2 C

* m" q ^) L7 q1 ~ 漏洞利用 : }# y7 J$ H! [6 r3 U9 r2 }/ E5 S

% e, Y, F, z0 R3 K$ S

" h; ?% T( S, L- `& d 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 2 w$ Q2 C( O1 [8 d

' H f! p$ W# t) k

7 S$ k/ | Q, h" n# w   : ~+ c/ l+ B5 I, g( _2 _$ ]

. Y$ V8 p) }. i

. }3 N2 ?, j5 @+ ]$ n3 |6 y 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid7 Z3 E0 H! a! h; P! Q9 J- G

N5 v0 M6 D; ?0 b! o! t

3 B" z) \+ `7 B9 T 不过实际利用上会有一定的限制。0 \* x. o2 M' X3 U

# {2 [' g/ m( Z( u

% b8 j8 v# T/ y: `# z) _ 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 & N( F2 u2 o( @7 c7 W9 q

, E4 d7 A& O! \. `5 S, U

. W8 _2 v- H- [2 C# [) h3 A, w. i  * W4 S1 ^" F% h7 q( z3 _

0 ?1 w5 ]& i' I& k. ?4 o& z( D

6 j" }, j' B$ I0 |' W/ C 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:) ~0 n/ {, T" ]) d1 [

: R$ J5 w% Y# t0 O* ]

" d$ Y/ ~! C3 ?4 R) q2 b2 g6 t% 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]);省略...4 O, m. P, s1 V; S6 X; f' a

. x) S! u8 {! Y3 e, n% X* D- k+ F

) ^8 G- u$ l; g# F1 t! P 因此要利用成功就需要条件竞争了。 + w; N( i5 X! D8 ]3 r

+ P' i; K# _" M3 F5 ^

# Z; v9 H! f7 B ?$ y% E 补丁分析 1 U' S d* s( u! D+ }

' J& ^& V( O: ~' q

$ F+ y8 ~+ j* R- f3 p   / @! F6 z1 O( I8 p1 s% l4 L! G) t8 G

& `8 e8 n, C, G. `: O

/ U) l) P. u% ^& P. y( ]( @ 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:( E* V& [) B! S+ R

! T3 h; c" X1 ?

9 v4 h9 T8 B& R* y% H' d n8 U function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} . b8 @& @; Y3 ~# V

6 `/ r) l _' j/ v, m' H

8 g/ e( w, l" Y7 u  & E% M( }. D9 @7 i) c; h1 Q

% V9 n4 f+ T/ J

/ `7 X' R2 U) y' I) X0 M 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。7 p/ h/ I( J" g5 R1 b

0 s- c z" V( Z. P5 L9 ^, H. H

{7 }. e% g0 d. h7 x6 d 在is_allow()中增加对$this->savename的二次检查。 0 T. ^# k4 w3 r6 L

. L5 Y- [, i2 R* Y* J

. n6 V# m5 V/ [3 g" T4 M4 ^/ J* @ 最后! g8 \, ~/ C7 b; H9 z/ X. O

% k1 M6 R* V% v5 o0 H. E0 C5 W$ h

& J# A0 e7 v6 r3 W$ J& p0 H( `0 _! Z 嘛,祝各位大师傅中秋快乐!0 j6 E: u' N4 C6 S

. S- J, a! d+ I$ X8 \$ r

. R/ k3 [. ?/ N9 E. Z  8 z" S# ?3 G% ~7 U1 @8 |8 q

$ d- A! e( m7 M& f- ~
回复

使用道具 举报

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

本版积分规则

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