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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
" t% x1 B+ p& p0 z% i

: u: ]( e* {4 T& e, B) X* ~5 J- t3 Q

4 Y1 ^% \, n2 M! G$ S/ Z& ^& ~0 n

8 H1 H5 n' V9 p& a6 R- ] 前言 8 {2 ~; e8 q2 U! Y

- o$ m' f& i3 h' I9 d8 S) n

$ H" |1 ~3 n* L- F8 e0 c 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。, N7 k; B4 ]" M$ e* o ^

' V& C$ L- \8 I

" P' k# k- M) d9 U   0 U3 o" ?: N7 x4 @" j% }, [

8 q7 H1 J) s: R$ D$ J: m# Y, A

3 |& B' j/ l7 L1 I4 Y$ H 漏洞分析 ! N: u8 `" `- ~% B, @6 ^; f

# Z5 J2 T) u X2 L; m

+ D# O4 V2 a2 u% K; E 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: # E2 V, M, v* C, q9 q7 u$ U) m

% ^ P, ?3 ^' I

& G% B0 R5 N3 s9 R; s6 O( ~  ; o9 _ O- A' C- a+ l9 j

; J3 d: `( V! a- L5 ]" N4 k& z7 w- Y7 v

' L5 S$ x: ] M6 [( w: D 对应着avatar.inc.php代码如下:( D, g( e! F6 |+ ^) s

. Z; N+ p1 Q7 C2 G) o

7 h3 _. o9 r8 J+ r <?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) {- l0 c4 `" q. q# @* D

. v3 J/ j$ O7 {" t4 b

" R$ E! g. g6 A" v6 o     case 'upload': & ~( M3 [( ^! X" q4 Z

: x H5 o* D3 ~) Z% P( n5 Y

5 f4 A u) U+ t2 Q3 H& G         if(!$_FILES['file']['size']) { 8 P. ?, f- N0 x+ \0 k) E

7 u: N0 n5 P' _! R) |

3 J$ D3 R+ O8 k+ I             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);) @# A8 l9 x1 I! M

$ o, X. l- y/ J; ?# }7 y7 ~

8 l0 x- K( m3 C- _ S             exit('{"error":1,"message":"Error FILE"}');& s" u. B& E) S

" Z1 \5 z+ u6 \* d; E; w

1 i3 B$ b+ `" [( Y3 k6 ?         }$ P q& |; a' A: C$ ]0 q1 N. s0 r

/ D6 g. l$ Q* y

; c& b+ S) N! \9 D6 i         require DT_ROOT.'/include/upload.class.php';, }0 D0 @9 y* h8 ^; b

) I, p% y7 ~5 i

5 y9 B! d+ r h+ Q; n0 w; Z3 N   5 J% O( F+ T6 d* q H' i+ o3 O

1 L& J: g1 e0 p- {% u3 f

0 y7 b+ t$ P9 t         $ext = file_ext($_FILES['file']['name']);- o/ E+ `! N; P1 T: K8 r& m! e/ x

8 X, r! W) }/ ?! x6 ?! O8 A

2 m* o& Q) \8 E7 @         $name = 'avatar'.$_userid.'.'.$ext;( ^3 `7 w& s% Q; F

r- K: I2 a' H/ m; J

) L3 O# a( i& `: i7 o" `7 C         $file = DT_ROOT.'/file/temp/'.$name; 0 P* v% X! ]3 ^* ~

( N7 T1 ^2 y v; b p0 l; e8 v

$ f7 e: [. e! s7 l" ~- A5 W   " f0 G' L+ u# j2 N

; d* q1 G, n$ k/ w9 ^. w# E

) G3 N7 v' {1 i2 F6 O         if(is_file($file)) file_del($file); 5 X* ?- ?" i% U- j3 x* G* t x

2 }2 z2 _$ F, q6 }/ B% j; j

5 c/ `4 k. k) j         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); * f+ B, y1 f* J

G+ J$ J* H! P4 l" k8 B3 ]# E: k

& ^# r+ k ^9 v; b   , }0 k3 E7 z, G$ t3 y

9 p! B$ u# Q; S N

: l+ u/ e3 B5 H' I7 d8 H4 J         $upload->adduserid = false; 8 Q( F. ]! [6 M9 b# Z1 ]% @

6 _* Q: a3 I0 W" w x: h C/ j

0 S- z" m1 J+ [2 }: }" b' D  , c2 r: e( J5 o3 x* v* L

2 d2 N; V k- X2 b/ W O

- H% S3 @# ^) W, g- {& p         if($upload->save()) {! F# L0 x- J9 [* J

) E& O9 E+ Z" i; F) ^$ J4 _

! `+ [5 C& B3 q* `3 F) q( A; v4 u             ...1 A/ j; c5 V0 r( _2 n9 w

7 T( u; O9 X7 @2 F7 l

: R/ @0 z4 i: a2 b+ P! `         } else { , Q/ a e: Y2 M* x

: `( [" ~6 u- [8 A" q* s4 h

N- k5 _6 H! `$ ~             ... 7 h8 F# e3 d2 b7 g6 t E. ]

: x" x3 [6 v! N7 z8 u* P" P; f

, G% G- D) {- f# M0 ?         }3 T/ q; V; B# L

+ k3 L5 h" L( {

' {7 ]5 t6 x. z     break; % n( T. Y8 {3 G- S0 i% g

& P' g$ ?. Q) \/ I& ?% M

0 p% B) |! |4 q( G' d+ p 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 # A) b: h$ i% I& i- n

$ D6 Z1 d) H$ e" C

, Z/ X" H9 t6 ~- e) E& D: K5 B) | upload对象构造函数如下,include/upload.class.php:25:* q6 v( F0 [4 d( F' ~

* g0 P/ S7 }5 r! L/ U. J( g$ m

8 E$ I% F/ x- w3 ^7 a& n6 {5 ?( y <?phpclass upload { % k7 n v3 u1 Q! w) T

& Q y8 L$ B$ q5 F

, T8 j1 [% D9 e" ?# q) K$ b     function __construct($_file, $savepath, $savename = '', $fileformat = '') {# d2 q% n. ]( a) a) L2 L9 e% `) Q, }

) S6 z0 c& x0 m+ n: u

( J M4 K! z0 p \. S# _         global $DT, $_userid;* Q( i9 F8 N9 C# W8 p3 x

% b6 L4 \8 f5 d# h- k* C) |

1 z, I* F/ o- q" F# c# X) g! t/ }         foreach($_file as $file) { 6 [1 C X& s7 a6 Q% K) ~+ B

8 [" F0 n* T" G8 ?4 `

; l7 ~+ @: }5 f1 T; c) Q             $this->file = $file['tmp_name'];& ^7 M. w* ]4 z+ v

2 p1 @8 w% o+ c& |

, S. C2 Y( H8 u* B$ x" H( @             $this->file_name = $file['name']; ' e+ k$ x2 |9 } z! \1 h& M4 P6 k

) @% u2 V x: v9 d7 J8 J

5 R' l. S4 D& F( Z* B" H) k             $this->file_size = $file['size']; 2 b5 e% g' O3 }& N2 u# L

' Q2 Z2 Z# [3 y' R. ]4 J0 {

* f& |; t1 Q1 s3 [; L+ {8 j* l) E. [1 u             $this->file_type = $file['type'];$ Z' G/ q: Q- j& T4 e9 d2 R

% ~( c3 F- K& N! g; W5 \

8 ]1 u) H& a) r. z& A             $this->file_error = $file['error'];5 V! X6 h/ q) D& v% @0 N5 L" Q* l

' h# v! w( b) B! Z

8 H6 v* @, ^6 [   _3 d9 q& i$ l% k* v2 q

4 t$ u# j& I X* d& {- u3 Y& @

' x. Y* q$ A5 z" D1 K9 I0 s         }! u$ r5 y; i( G- ]9 J

7 t/ J0 r1 o5 V$ w4 ?: z

2 H/ F% Z _) I. Q( c1 }9 ^         $this->userid = $_userid; ' ^/ c8 @+ Q% d: ?) x

6 o! J7 K% |3 H/ b

6 Z9 {' x+ Z$ \! g% P         $this->ext = file_ext($this->file_name);9 L' K( O3 \. `. ]+ a* z

% _1 J2 L9 ? A9 M" @; |5 ^

, Q9 i6 C7 B* i) U         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];& t0 y. |% y, K" V7 a# M

6 u- J7 V# M2 L

9 @9 I% f2 _* i* l0 |$ @0 R. r, {$ P/ d         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; . b: L4 G! T6 \) G% c" |

2 ~$ U! B4 `( ?. M

8 M% v. a# }( {$ P         $this->savepath = $savepath;5 p! y& ]/ m; H7 V

1 x0 O) Y' L. E2 F& g, ]

# c, t" i$ o2 x, X4 I' h         $this->savename = $savename;, [0 U1 F. k8 ^# [5 M

$ W: H0 c" h3 Z5 w

( m j7 f) `% Z     }} % M Z p! ]: m- Z* z* ~$ [3 ~. j

1 k4 z+ y9 i8 M+ }! F H

+ _& e6 N6 U7 p 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 / p' h7 b+ t6 s

+ ^9 I8 J$ G0 i% U a9 K

. m9 t: K( H% Z- \0 e$ O& e* S* } 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 % O% S; _2 W9 z- C0 d% l4 s. c, y5 z: I

9 Q7 z3 I" `7 }8 [8 W( E) o' r! y. Z

2 y( c$ Z5 M, ~4 Z" @9 ^2 t $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 / _, T( r- D) Z) b' f) `" H% z

0 A# V9 z( @, k- X' L6 o

1 t9 n- ]: M: B! h 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: ! I) b7 }2 a" k2 N

& r, S8 v; s0 `5 S Q

4 b) j! y- j- \$ b) w" U4 ~0 d: W  4 k' l4 h! g1 \. V" z# k& W

9 h# _- K {: m* M$ Z( Y

+ B m" j. X2 E$ |- T* A8 s4 { 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:4 I L L( K" i {! N+ i

2 D! B# y+ n# M& {* f" W% ?

3 [2 W, e- P, T1 h; K/ ^ <?phpclass upload { ! j/ Z, j1 B/ a; k, t

/ h; o( d. T v9 h0 f0 e. V7 K

6 v5 s$ L6 L' I0 {# }/ y% p. A9 i     function save() { " F: i' U3 t5 o

6 m, t( | w8 @& v- B4 ? a! d6 F

7 x% D F# E3 ^" V# |: S         include load('include.lang');- b& E) M' O M. }

0 ~. g- s+ q% ?9 O+ L

* g! Y2 Q* B( T         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');1 a& j% b3 g! @: S5 G

( B) a V e: \

" G, f6 |* D% Z) o( P3 j2 I, c  7 ^) {+ C0 B' k" M8 w

S5 E- g* \; y. ~& t3 n

& N6 H' |* @, L. t/ a# F) N         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); $ g( _, k% w% Z( m2 F& M$ W# i5 o! T

( ^! g6 |- p6 j/ f8 {: ?

! x# |( d; H2 ?% G  " J0 T9 V9 }' e- L( M

7 K1 X- c0 I2 M! `* Z. B2 @

& N; E! D8 Z1 ?5 M3 \: \         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);% ?9 L4 T p9 h4 J' z

$ n5 G3 D. j5 v: B0 n2 L2 o

* V9 H) o) k* ~/ E8 E) Z   " w$ T1 N. ^; V+ ~ V2 K1 W$ q+ I7 ]+ ~

" ~' |/ \5 E v0 b" f6 {: u

3 q Z$ e% s# v9 c         $this->set_savepath($this->savepath); 4 A0 O% n( A4 D

, [& l& _0 D i+ w" L% Z

0 }/ J% C, e9 ?: ?         $this->set_savename($this->savename); ! c6 t7 D9 t* n% B' A+ |# a

: ]* W3 ?1 x( A! V

. ~% y+ k# V& X' s4 O6 q+ E' P   / [1 i- b' m7 T7 W9 D

, Y2 @) a4 {2 s# L8 \7 C/ G

6 I* f- c2 W% U         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);0 T. c$ ^, p5 _3 i# d- _

; y8 |1 i$ }) N" G* e g: X& r& c

, v$ [) a4 W& R0 [; V( k) A         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); ) m% l5 x3 X5 A5 I- t- B! @

* a9 Z- ]' j g" r# ~* @) x

% J- R1 N5 P4 y& F$ r1 L% k: |         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);. S- a' ]/ E# I( k- G* y5 a" W

( ]1 N' V- Y6 q( k

( y( K& U: L) p- F( ]  * j5 F( u( C% V

6 ], k& T- \2 w$ o# h3 E, D

/ l4 `: K7 b. }' Q. S3 s         $this->image = $this->is_image(); / _% f/ k( L; K8 k, h* M! X7 d2 i! k

) E8 F, p/ ?- U* ?

/ r. c/ {- ~7 ` j/ F' V5 o         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); 6 r: ?7 Z! m1 ]3 }

' d) U, ~( m4 D' K& c4 H

' N8 E5 |7 E: S" G8 |         return true;* x& ~: k3 }. ?3 F

7 w! c- }& Z% k, X+ `6 v

) C: a3 A5 F6 s T6 `$ y, H     }}2 A- ]( a* R$ K/ W0 W9 k

+ i7 n3 S; j" j' E+ d6 ^' D& j

: F8 d7 J& D( x# B3 D# o 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: * D/ S1 ?' p' \5 L: t, s1 c

+ P$ T" C5 ?5 ]6 k% Y

: i: Q- ~& p+ E$ x <?php ' G2 f$ c' J% m' Q- w2 {

$ K( Z4 m% Q$ _/ e9 @8 M# d

3 t$ |0 ]) U$ G     function is_allow() { ) C! K4 m& S5 e0 c% O: S2 L

. k* B/ E& r4 k# G

8 D- V( C& }" A# d" ^         if(!$this->fileformat) return false;2 M. |/ E7 k# ^

4 k/ c, Y# D3 b6 {8 M; o+ h- h

- U# [6 W" q( R         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;0 D; V6 Z# ~; Q# j# |; {3 w) d

8 G3 B6 O& }4 P! ]# r3 K: c0 w6 h

/ X! e8 b" ^2 q( Q/ y( ^         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; ; Y5 {* e& t2 V! q

" u3 a& |1 a3 E' Y% Y, ~3 t7 x( Q

# q, X1 d" q- s9 Z/ X3 B+ J         return true;+ e- ^4 A/ n- W5 v& c1 c, z

5 L# u, n, T' C# _

) O3 p; L! f* X5 @' b5 K; I     }3 C# ^# A! f! f# }( O) D

" w; G8 d3 E* K: o

5 _) k! z# D; C! G 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。. y* u. ^$ j+ l* P% Z

6 A( G- O8 Y/ W$ ]" e- B; m3 K

" e Z4 a: v2 Y" B# 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文件。& s. P( I; B7 n6 Z( ~4 e

5 k* }1 V, Z( g" T6 i

$ M6 u& k( ~# ]3 Y! B, p) { 漏洞利用 & q' `" H" ]! w# E

! f: q3 L) l5 [0 P0 b; d

& `( D! M) V% j8 ]* R. w 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。. U1 b% R2 p( c' F# ~

% }% {* g( V% u- {; I

- @9 e4 }' F) B   ! L. @* q. [$ P8 N. v

2 S# b% S: } {

, V7 W- v8 n. Q" m 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid4 l- E) D0 u U7 M

2 M2 c) k% t7 C$ s

" \. e0 N. Z* K0 Y. E8 L( x- T 不过实际利用上会有一定的限制。 Z4 v/ X4 ?: T; ~) [

* L F+ F) \ s5 b7 P+ J

, I5 v# k! y6 P 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 , R4 \# n- H; [) d1 i5 d

, I' ^) [ S; ~. g' s: ?

$ b1 w# t9 W- H) y   ) p9 [. ]: T2 e! _

3 J2 {) ]! G8 u; B, ~

/ j( n0 L9 o: s& ] 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: # B* A: H1 D" Q" m* V2 S

5 o) T% @, _4 B# w% l# ^

+ R& i/ ^. a3 i5 c4 ?0 F& l. h 省略...$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]);省略... $ x$ E, k( `0 Z9 U3 ~5 u( o% s

8 h4 u2 m( G7 }6 g3 ^6 `

1 U% `5 ?4 D0 ?+ C% }- ~ p 因此要利用成功就需要条件竞争了。 & u/ j$ G/ w d: I# d5 _2 Q- v

c& \/ d- D& r! B

4 G) [: g1 A1 v6 L* C 补丁分析 5 ~5 A8 y* R9 x0 }# P B

7 X9 l7 @; n, a

# N" S3 I! T1 W/ E! E i: _) V. Z   * p N8 `: f6 O9 V! \" ^. h& E- v0 {" ]

; X/ y" i" n7 L6 R

' I Y {0 O+ X6 a7 Y 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: " o& x1 S4 F2 V1 E1 o6 L

5 R' \) z; ?9 ]+ w6 H. C

9 A0 a4 ^) `/ H7 V. q A1 |1 x function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} 4 }7 k$ `1 x6 r4 ]) p7 N- K

9 N; y8 g8 y/ U

9 N; X) _. d; h   5 Y6 W) I7 @ ~

; L7 M1 r- D, [4 A/ y+ X9 d

t( g5 C4 g* S5 f' J5 v( U 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 8 d- q) w9 B" i. x; t' [0 [1 u6 Z% F

( v; i- N7 @, ~7 R

! P7 o6 C+ m& n4 L2 i% \3 ^ 在is_allow()中增加对$this->savename的二次检查。 ; \4 B' r% v" r/ R9 U6 g2 W# v; u. G

- Z5 h$ W! ?2 L9 e7 K5 E

' E0 c& f- D4 p& P1 F( M 最后 / i0 {3 d7 R- Y/ U

7 n( P, u) L( K- q- h

' G9 }4 `$ F( s# W* } 嘛,祝各位大师傅中秋快乐!* X) i+ [( }! L2 [% S5 r# d$ ?

) o% }; V2 g# J1 O9 Y* \# W

; ^2 n" Z/ ~+ w: a' |$ E   8 z% x" M1 q% |) S- [

/ U7 D ?6 x' I% U
回复

使用道具 举报

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

本版积分规则

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