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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
; x6 r4 S8 [! |+ W+ h/ G1 X/ u2 N; j5 K

4 c: e" m. M0 |: \4 {

" Z1 |1 \. ^! y `6 f v4 q

& y1 b( Y/ ~- A+ I$ R 前言! ]0 L5 d; f& l% d/ O$ y

7 R4 g9 h/ [- J1 C; r2 G

5 f2 @. Q" I+ x2 y. N k- ^ 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。. _% }3 T1 ~" j6 N3 b; t7 t

0 D8 \$ V7 f* [5 l: h

. e, W2 J6 c# |/ s7 |' s   + f S. B& H. k3 x& ~& c3 _! d

! [! k3 t+ q/ h+ f$ P% v

! r8 S) G; Q4 ?- s! E# o9 G 漏洞分析 3 \( _8 A8 Q& u. A! i8 b

4 E: i e% u/ z/ m5 I z6 k

4 Y6 P# E+ ^! A0 _6 M5 \ 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: 3 Z \8 Z( u, p3 r, t1 M

3 r# v7 K7 E% `# V2 d8 P9 K9 A" O

& o7 d% x+ x) B1 {  4 i* H8 P+ N+ x( Y, a

8 ]4 p4 d% r, c. [. l

6 p# t1 A" w: S 对应着avatar.inc.php代码如下: 7 S2 h1 p" _ O

3 M, l! }! N. |" S+ c

% P9 l+ y, _4 L$ i. B9 k/ w <?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) {# V1 T! R4 }( y! r7 S/ a. d

1 Q& p$ H8 [: X/ E

/ o/ p. i! y- C: N2 l6 F     case 'upload': 1 `9 y. Q4 Z+ }7 x# ^2 v

, P" l. I3 L" M3 ?$ A; Q, N

4 }8 o. [% }1 y( j; z" a7 |         if(!$_FILES['file']['size']) {9 P6 T& Q' E$ Q* [

' `6 [! F B0 [+ I" L# }' O

. ^; ^( G% O) r- h             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);( l1 R# c4 j( L x. W

2 j* D! @+ v/ Q' P2 w; a1 M" n

# }; z+ C2 a- ]. G3 D5 _             exit('{"error":1,"message":"Error FILE"}'); ) Q2 z7 q! j, b, @" |1 Z1 `( Y# Q

5 ~* p( U, s# @( M& {5 i) B3 r

8 i% n& b4 ^8 ?' w2 Y# K7 D8 e' O         } , `& m' J6 Y) S3 |0 s) I! |

h/ g/ J; B" _

( B& K: Z' I2 ~+ s2 K! {         require DT_ROOT.'/include/upload.class.php'; 1 o4 I3 E/ a+ u0 E

- w* X+ F7 {( }4 g4 ~7 `0 V3 Y4 C% V

5 {2 b0 \6 e) f, z   : v6 ~/ K: w" e) i2 U0 I4 Q

/ q9 R6 ~; a# W# }2 X* B, W

7 l) t' C! k2 i. K2 u2 r         $ext = file_ext($_FILES['file']['name']);" v9 v; _* w( P

% Q8 Y7 U" R5 b0 b9 w8 r

0 S$ J* z1 P- Q         $name = 'avatar'.$_userid.'.'.$ext; 9 F M, s& U1 U

: |! `1 v, h8 g

0 F+ c' [# U+ R/ I: D         $file = DT_ROOT.'/file/temp/'.$name; 8 u* _7 n3 |# d" e( ~/ \1 |( y

5 f3 e% L* \3 Z5 p' x

: h% T: K; b9 R( x9 M7 j  $ H$ V$ L6 k2 ^) \9 p6 e

' _, ^! l: {! |) Y! ~0 T) L9 h

) D+ E7 _- g/ V         if(is_file($file)) file_del($file); $ w. h( t: Y; p8 A! D6 P' z

) _9 _; b. o5 [5 w

% Z- `3 ~( Q9 X( t; j$ W$ p" X         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');$ x( y& P& q9 `+ c+ _, R

! a+ E9 `, i$ C8 o! F. z1 L

3 _$ y7 m% a. e4 @ }+ k  " o6 F1 x; i) M+ H4 c' o! P4 K

5 P! f, V: y, q: |" W+ ?9 O

' v% V. ]4 `/ @0 L# j3 }; [! m) k         $upload->adduserid = false;; Z7 S% J! ]* W" t' h7 j7 @% D

/ C( v |2 j! F* K4 q

$ u. x! O* i' k; V   2 b5 A- D/ a H) j+ I

$ |$ S- H3 z1 p0 h: C- |

9 L+ V) h7 g' O3 M0 W7 m         if($upload->save()) {7 `5 T7 @9 F, z8 I

% `/ M# s3 [9 C" B. r e r1 Q

# k6 {# }5 E2 M- |             ... 4 v6 b4 @- z# x1 @

; `3 l8 Q& Q9 g3 G+ h

! M+ S( L d: K         } else { 8 ]3 c( y6 o) Q% f6 ~+ n

9 m1 X; Q5 d$ W1 @8 D

: J- f4 i$ D+ p5 w/ R( ^! _             ...7 {8 | }( I$ |8 ^* R

, i" h% z/ N @

5 r" ]. I5 h. S& A4 R, ~         } - S6 ?8 ~8 ^% Z7 n' ?

, ?% ?. U5 A8 o

7 a) ~: U' a! d/ M5 I& S# G     break;" o! o+ K; [2 `4 A# T, j

" \- v; x. i* |8 T7 S& [

/ u' ]/ F) [* V, _ 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 ' W/ L" H# F @9 ]' L+ _. F% F

! d c( {6 E8 K5 ?* \

5 ^5 K3 y4 L/ u$ }1 V X8 M upload对象构造函数如下,include/upload.class.php:25: / k% N7 S9 W' `1 b1 k0 o+ A

/ D& e- b8 ~" ?* q

) E! L; Q: U0 X- c <?phpclass upload {& I( {, _7 h* n8 M4 P1 A" s' w5 m

- ^, U3 ]6 K- ~5 h5 ]

! u B3 r1 x- t8 W8 J9 H# C8 k9 t6 s     function __construct($_file, $savepath, $savename = '', $fileformat = '') {$ C; `6 z2 ~; n( n: I

' c# J( \/ b2 M5 R

1 g) c9 ]$ `" ?* T; ?% {# B         global $DT, $_userid;5 U- l1 ~7 @$ k( u6 G

v. X. D# ?3 y! r

. M- R! r$ t5 G         foreach($_file as $file) {" t/ x% Q6 C4 ?7 q5 t

/ C+ R0 P' W* U- w* \9 L

1 |; U) I: l% ~$ }             $this->file = $file['tmp_name'];5 L3 K7 c5 H6 _- K5 y

$ K8 U4 o5 ?; x0 f

! D3 L) s C) ~             $this->file_name = $file['name']; : M) Y! C( Z5 r

. I, c, w2 c1 G) ?

. B, x$ W( k6 e             $this->file_size = $file['size'];6 k2 _' [; U, l( `: T6 N

% r- v* X3 r' d* z7 x! K

$ V v' T/ C" `# s$ @             $this->file_type = $file['type'];0 b- U; O% q5 c# g

- O1 q2 A% n6 d3 B3 m

& o! l+ c' }! F+ A6 _             $this->file_error = $file['error'];0 ?9 D& d8 D$ g& s }

. n# l+ W2 \' d' t( \; J

4 I9 X1 J6 V2 ?. _$ i6 B6 A z7 r  , W: r& H2 \; E$ G0 ?

s* l: z' Z# r' M `: I

. H$ u2 ?: c3 V4 q4 C" r         }* E. \2 f. V; O2 w4 D* S1 Y

2 p8 b/ _) s7 {3 i2 `8 M

' M5 T3 D4 n- D' N- ~1 S j4 H         $this->userid = $_userid; 8 F1 M: W- L% W4 F9 \' k

+ U8 l4 K% e* `* ]# s6 s: F

* W f% _5 \& i# o         $this->ext = file_ext($this->file_name);& V/ a. N4 P* V. K6 c& n

0 D3 |' Q1 V3 \) p; h; J' @ n, G

8 F, w S! _9 P         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; , q# X5 R- t& E0 f( B3 Q

5 Q# x5 d: h$ v. Y; ]: W: t, {

( s, Q- L/ P) d5 y         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;& Z$ }3 Q3 c6 E! s

8 c" J N9 w! ]3 E* G

! p* s( `% {- _, s6 D6 t         $this->savepath = $savepath;- b& z h- G) z6 }: L! q

( q1 N' e: K& c

, k$ \* m Q+ x# x9 J1 D! t         $this->savename = $savename;1 v; J" W3 |/ \8 q2 E A9 R

" b+ o5 Q e! g j; J% Q4 h

$ u' o* J5 @& r     }} ; Z# J, G$ J3 r9 B* ^' o

~4 `4 z$ [- i6 j, P! [/ [

. j- T# O- c- ` 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 ' Q) {. ~) d; ^# v4 E# \( E5 @

4 b, |9 @" g: Q+ Q6 U. `

, k: l0 t" I5 w4 ~8 p2 P8 N$ C 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 # Q q! W) U7 r* K

- C, r6 c* U7 K. e) s4 ^

O& I+ ?" x8 L4 ` $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 5 w0 V0 J5 l: T" d) |1 G/ D: b" U

) p/ I. \' G+ j. h+ @1 i

2 n) l! ~0 E' o3 o1 O! | 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: 2 z9 ?: J1 c3 ` R" F5 g

6 L- [* m+ q% k1 l& x1 _6 r- N: ?

4 x* K' B& i) ^8 j/ j   4 }5 _1 o3 b3 |4 N

* V# M, ~- _, v( ^

+ Q0 D% d* D9 @+ j# L# j" W6 L 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: 7 }9 @! {( v7 T5 p, q- z

' D0 ^8 ?0 ?/ L4 L, A

2 w: \& R7 A. }3 \5 l8 k( M <?phpclass upload {6 D: N+ }: K: ~

/ T" E& I- Q, z" a6 X

: H8 i$ L0 v7 C1 W. G9 n$ P- G- e     function save() {* p1 L& B7 O; K

" z( K1 e9 }! D. n- B6 p- v

/ I9 Q$ n" D1 V9 k3 p         include load('include.lang');& `+ P1 m6 N. B, u

5 ~3 A8 w1 H, D0 _2 o6 l- Q5 B

. m8 }/ w* X8 B6 r9 a* b E- `         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');5 i# E( T( K! V( n- L6 |

, Q" z8 a: ], e4 d: O! U

' c( F! X6 |& f% c9 P" n+ P C7 b   . F, ~' W0 a+ c! p& O \& Z7 z7 e

2 ]& _9 m/ J1 T5 n

8 y/ h3 u& S' [% S) T         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');4 n( {; N: h0 d; X) u$ H# |

, A. d$ |1 r: J* x9 u- C

* e1 R1 V8 i/ u4 @   5 g) R& }- B; B# w5 ~+ R

6 k1 z! |& Y0 L& u( M5 M) t* |# Q

( {4 @; _ @( d: R1 |* x+ S         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);5 n1 U! v, M* x9 n2 U

/ W% j. M* w3 B1 l

4 W* p+ o: R. f" O3 t* Y/ U  ) Z% I. |4 i* @* Q

3 r1 |7 i7 x6 G+ X" j" S

$ Z0 R% \3 [: X7 X! s. \+ ~         $this->set_savepath($this->savepath); 6 w, n" t% I6 \; p2 Z9 \) t4 M

, l j( V" w# ~3 X# y

: }- `* j' l8 f         $this->set_savename($this->savename);1 q b z4 g: d0 E j

# Z# k2 o$ Z0 e8 ^

; j# M- P4 L5 k; P: d   ; r2 E# [, J$ G% q; i! T+ A

) z4 K* ]: D+ U* j k' `( i5 G

; v! a, d& O( k7 j         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); - |1 m# Q. @0 A8 ^6 T5 _* K, x

' ]6 Z2 F5 C$ R5 z

; e& f1 G- p1 c2 x         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); 7 R' j0 t; k8 Y/ r7 q# R9 d8 ^

: k( ?7 r/ [3 m& t4 r# g

+ x% n0 P5 r4 |( F1 v/ V         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);! D* {7 q, r; I" J: l

( y* P) f4 w' b. _ B7 I, ?

7 m2 l9 e* b) O) g* X  - D9 d/ Q2 [$ K- q7 H* c

" Y: f! e' J; D/ x9 y

% Q7 M6 v! R) V         $this->image = $this->is_image();# Y ?3 P3 c0 U

" u' X% E8 e8 _3 ?3 |) C

& K4 y& B) C- w1 r Y7 c# l% l         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);1 |$ u! X3 \8 i; |3 |

7 m! N# \" e0 X7 m! i9 L

- M* [/ H6 P* f& p% D         return true; R3 e- w# f S5 P7 i9 t

8 W* g7 b% @2 `9 {4 i2 S' \/ O: J

" z: j4 }7 S8 I$ t     }} 0 s' M) S8 N/ W$ M0 j2 G

8 I8 N( ^0 E$ }2 n' H9 c r0 [

5 h# A' b$ ~5 J8 S" W; U7 ?/ M 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: + i: M, G* m5 j" G( F& R% c- C# K6 I

+ I8 X7 V0 z& @4 [

+ M9 m0 \: n5 _" q/ }& N <?php 5 d- ]5 B" \2 T* k# Y

, l1 r( C2 M9 J# P9 n- e

* L- T. w/ V6 ~% u6 k     function is_allow() { $ k2 T! i! d4 n/ i6 U8 i) Q* d

/ K$ O/ F. Y/ I8 N7 d9 D

5 x2 |) V% i' G1 x. U1 ^         if(!$this->fileformat) return false; : U# ]7 G8 ?3 J$ g8 v+ F

+ p1 ]' m1 p) j4 ]! C' |

9 S) I+ W. i3 F7 ]! u3 Q! F. P         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; ' |6 V- u! d# p* z6 u; Q* N

. a9 [2 v* `7 J- Q5 g

. Y$ G2 M7 U7 g8 y& P" 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; : v W4 [/ h0 C

6 B$ b9 q- q* }' O

+ F5 h9 M) d2 V+ K3 ?/ G' _* C9 i         return true;$ H5 c, M1 x7 V2 h; ]

3 j8 B5 U' I% F# E5 W

% k$ Z8 f0 T: \! I     }) e) b+ Z2 P; ?: F( y, E! l

0 l& Q! e% ]1 J" l

, h. N+ C( H r2 E" T6 g+ F# S 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 7 {! r, a5 _: X- G8 X' o, N8 l

1 ]$ h3 J+ S5 H! h7 `' l

, E1 g; I3 n+ V$ v 接着会进行真正的保存。通过$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文件。- q& U. U8 R6 ^% i

; o5 _* g" ~1 u b8 W) c' m6 W m

; ?! N' ]* V# h 漏洞利用 k2 b9 \& h' M. Z& [

& Y" P& J% v( j x) Q8 j6 _( f

u& y8 D: B& X' a& T 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 . a ^/ f# v7 j9 a: n$ ~& g

% `6 H& F) C& C i4 J3 O

1 I3 w# ?9 _, u4 r+ W# R/ j   ! Y. N" T5 S$ t+ Y4 U

; ~. T' i* T* n

: o$ G! Q9 Z' {' Y( p$ ?' Q 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid : Z- C* i, `3 h- {( T- w: G

6 ^3 d; S- P }$ x$ k' j. [! `

- C& @: |, Y7 I5 H9 ` 不过实际利用上会有一定的限制。6 A5 U1 {4 W4 d

# a5 Y" E* \. x+ H9 ^

6 ^% y; M/ ]7 K 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 9 A" @# i1 q6 A5 t

. Z4 f/ I* {3 y

: n. v! L8 L$ _& { B- J, V   : y3 e! f9 O# v' J$ a! R

5 [. t& |/ L, i {; a$ w$ V

3 ?5 E/ k" K( `! x q* { 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: 0 ^+ B. Z$ M4 t$ z2 W

2 C3 c5 \1 g; y+ J

: X0 M/ _" C% Z 省略...$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]);省略... 3 B' `1 ^) v2 x# ?! h% k

% O' f V6 R$ C4 n

* x+ { {5 d$ b9 Q/ o: z 因此要利用成功就需要条件竞争了。 ]1 r, E: o' m" s

2 q0 ]( Z8 M+ I" M0 q

$ q" `1 Z+ _4 B3 ^1 D4 `# e 补丁分析- u" m) B, ^+ \. p2 v. z

! M6 R3 f; L8 L

3 l# ?- @. X1 E) i$ F# X4 o: z, K' M  3 B/ q# `, w0 F! D. o: u1 v7 G

1 G2 |, S8 E& e" c, D4 c

% s7 L1 W" I. C% L- C0 }2 v# c: @ 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:! v8 x3 G5 }3 N! R9 [

9 G @ V/ w& B) V

# ]3 N7 A4 u# S! B- q: P, D( H, h' t function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}! @+ W& y- O! U! ~2 j& P

1 G$ ?2 [- [2 B

" j+ h9 t0 w- S  $ h, r1 r9 b- V, I* ~" Q

, S0 P3 ]4 E; ?& a4 e4 X' o& I

. k2 U& l% {' W 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。4 m; Z2 v/ l7 Q8 S# ]

: w) G( f0 L8 V* v8 H7 S- k0 }& o

4 v3 Q; i/ P7 c) q3 H3 b 在is_allow()中增加对$this->savename的二次检查。 : c" B p1 E1 D3 ^4 a- K

# o: ~8 O4 o* V- b* `) `

( H: t" a. {( T; ^/ R9 b 最后% H# Z2 b* E9 `- F

: _; e2 X) Y! R/ T

Y; l. C* U$ U4 `" C ]( m' P2 O 嘛,祝各位大师傅中秋快乐!# j) s( {' _- u. p9 I

0 }( q; E. W# H. M; k2 N k- l

) h- Q, c2 r% E# e! m8 C, c; ]- T   6 T! {; d" X0 [) p

' G: |# p) d( Y! j4 |1 z# k6 j8 ]. x% X
回复

使用道具 举报

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

本版积分规则

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