找回密码
 立即注册
查看: 1820|回复: 0
打印 上一主题 下一主题

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
' o/ ], \" I! G5 X6 }- s* I1 _

5 h& i. ^% m0 B" o! S

1 h. }& j( U* W

; x7 Z9 o6 j, ?* a& r1 Z 前言 ' s: L! Z% j m0 Z. G; w

. k; K) _9 u% a) @5 h( P

7 K+ a" n0 s* k0 W" g( c 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 : ?4 |/ k0 l/ {: R, p! m

. t4 n. T, B- {/ z. L N

# x: r1 h; ^6 o4 ~' C# \3 `1 p/ m  ( t! A! X$ a2 |( q

4 K2 c+ G4 N1 F) w0 H" `6 |

; @1 d* n7 J) I0 `. c* f6 f 漏洞分析 2 o8 P$ b1 y/ D R: w" F1 F

* r$ ^5 d* D h+ K, L! ?& ~! f' {# L

- I/ ?- b W( y" t 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:6 x) K/ L9 _! I( a7 ~8 a4 u

# K3 b7 c% t! |

/ ~$ M/ S0 E. j5 u- U. n5 K3 C   , ]5 v$ G' D% z8 r( e# ]

$ X I$ [' q) l/ y" \& M

' ], K+ E' I& J) X 对应着avatar.inc.php代码如下:9 J% m. `! i+ Q$ v; H/ h) _

& v" k2 ]( m- E M2 l, X5 e5 a

! G9 c2 ?4 o0 T2 I: e/ C <?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) { t! R. c h+ _* a9 \0 G8 W

5 k% e! z1 S9 \. B9 {

' o6 C$ ?. [/ h! ?     case 'upload':& E; ?3 h, ]' |9 ~

+ I" Q1 R0 x0 f8 y2 B5 {% a

' [# {+ {1 W/ S- P s         if(!$_FILES['file']['size']) {( [& z0 E o9 L0 b# b& o1 Y

9 K! a. s6 f# R

) c! A' Z1 F! Y3 U1 L5 @, h% F( g             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); u- u) W8 I& _3 P

; |4 D" C% m) y5 m7 h& ?

/ r i" D4 O6 }( k& N7 ^ w             exit('{"error":1,"message":"Error FILE"}'); ) W$ ~" G+ w5 @; {+ p+ |

4 Q2 i. H8 R7 u, O# [- S; n8 x

! U% v1 q5 r5 L& @) U; M         } 8 y+ P* h0 ?3 \) K+ l" M

% k; ~) n3 Y. g7 C- E5 `

7 V) Y3 z" ~" Y8 i6 Q8 _         require DT_ROOT.'/include/upload.class.php'; 4 A. I* A. l+ R8 V% w& Q

! Q6 W, l2 o; i! y0 [0 ]+ q2 _

6 q1 ^/ ]$ \; e" e. D E  7 N7 I: F5 Q- _% j" C* x' l

/ @7 w0 Y+ b4 O2 U7 }

. t3 K( T% \* T! o# O8 z         $ext = file_ext($_FILES['file']['name']); % m% R' c, \2 W E% h, |. a$ ?

5 r" \4 @0 |( b u3 }

: [! s8 h0 c+ i7 H1 |2 |9 G         $name = 'avatar'.$_userid.'.'.$ext; % X# a6 d3 s3 U" i6 d, s

' a6 J3 ~. d( T+ t

- {. m& B+ S) B' A' J         $file = DT_ROOT.'/file/temp/'.$name; ; m8 c T' G) e% Q5 ~! J/ {

: Y8 q& r9 c, {: ^

8 p/ j& @- h! x& G+ g. F   . s6 ~ Z9 q0 Q; v" }" G

4 Z& u0 I N" h# B8 W; g2 E

8 ~/ e2 g# q2 G/ t( T! {         if(is_file($file)) file_del($file); $ h7 U8 i, t" g; a) k$ F4 P% v1 y) n

4 Y& G; e+ h- c5 p

! H' }5 W5 t& a, Y; f) i5 R         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); & M* j' m* c/ E: X' r

" c3 @. Q$ l) F: L' P0 L

* K+ `% a! ^! q: H. t  $ n7 M" ?% |$ l0 d

, O7 y2 @; k# W6 V" r1 [9 t/ {

0 _, G' s9 ?+ \+ h         $upload->adduserid = false; $ {& W$ W# M$ X& K; o0 u- R7 o, i9 y

) A, q" { r, h% M' U. c

5 i, w: K- d! [( q   , _8 n' V) _, y" y l4 K$ \- Z- ^

5 ^1 T9 H/ |- u. h/ ~6 B5 O3 A

* D4 g1 Q7 w4 X; L         if($upload->save()) {! i2 a- x* A! v; \9 W) B

7 E* v& X+ V! c* Q5 G3 n' c

2 D! ^( S: x6 N/ h: \( |& d             ...! d- d6 a: k; t w/ n5 G( R* l0 C/ v" d

* F. H/ z& s8 X( d! [

8 x' w, v2 N0 T6 K& D# C& T         } else { 7 q+ ^' l K. J" Z+ Y' ]

9 c+ Z8 s( V7 F( L# ~( j

. g" y/ C& ~1 ]6 `: R' c1 u/ O3 I             ...' ~1 a! ?- r! @- _. `* ~2 G

) a9 u" t2 ]1 Y1 X, f7 p7 k

. u# @5 e: ~3 p% A* |5 @& n3 r/ j3 n5 K         } 8 d9 l9 P" D4 Z9 K, j

$ ?# h0 T2 z$ d% y, [

* r4 D3 a2 }; S4 e     break; " t4 w: O& T F+ v

" `5 V* j b" s5 |

. y% @6 t9 P! ?* B$ s# ~! v 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 + Y9 `6 ?) ?; o; L a( H

# x. y0 p! v; P9 a6 L

$ [1 q; f' |6 L5 Q. i8 d9 m upload对象构造函数如下,include/upload.class.php:25:4 r9 W0 {* @: y, u

4 r0 {' O* u* z4 G0 T& {! A% l

% z7 K6 t, Z% c4 q4 x) s( R# m <?phpclass upload { 2 g" Y8 h+ q+ h' n

; |! t: d |* k9 O! }# Z

6 }/ w! i; ^, m; N     function __construct($_file, $savepath, $savename = '', $fileformat = '') { ; q. `0 |# m$ j6 ]! u+ ]+ z

0 }+ n& N. t% b% ]: w

5 Y; Y' S' @9 |" a         global $DT, $_userid;, z2 |( E8 F) ]; Q, P. g

$ Y% o1 y) I# r$ q" f

" c3 h7 Q1 A# ]5 w. c         foreach($_file as $file) { , i# m( Z5 r& D3 q7 G- L

2 r3 y+ z g/ }+ g( k+ q

* ]+ ?7 H e& c$ c             $this->file = $file['tmp_name'];. @; @) |" Y" ]6 i3 d; M( Y

x1 I: E) r3 V. z. Q& S

2 S7 u+ V) C5 k9 G- n' ?' y, [             $this->file_name = $file['name'];, P6 A t+ V' j& T! F0 k

- g, f% h6 \6 D* q

m2 E& @% @% f Q             $this->file_size = $file['size'];7 i* T/ s- D3 G

6 Z! r `6 {. R' q4 ?

- \0 S+ z2 R$ A( Y( J B5 z* Q N' {             $this->file_type = $file['type']; . d( p n! A: j6 H! |0 ?

' T& a, c$ t: ~: @

) o8 `0 V. K) q5 c. `4 e             $this->file_error = $file['error'];8 h/ Z# t9 z7 {" M* w8 T

/ Q" w" H+ t6 ^. o$ X

& E. H% W4 I0 g! N- u* Y   e+ k; n) A$ q

# p% h2 ~ j4 Q+ ~. p

, ], R0 r; h3 Z1 r. p5 q q1 z         } $ O. I$ b) N' ^2 Z' E( G8 v' E

1 j0 ?$ Z% m+ R5 F

! X0 b' q+ ]6 K! x         $this->userid = $_userid; ) g: } I( J7 T0 \9 Y3 O

8 R$ q+ D# R: e& n5 a8 e6 Q2 F

, R6 L' {; [( d- d* o; ^         $this->ext = file_ext($this->file_name); " c, N- n# ~+ v" B3 b) t- y' R

6 ?! I) R6 q4 N9 g0 s/ W5 c1 r, W) m

9 @% N" t0 G' }+ y5 u' X         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; 5 F$ q- \ P' W5 V

' i5 D" V& R) M4 W. u

0 s, `$ t# B2 q5 d. v& Z* m         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;: t( ~. r4 | u& a, v; Z4 Z

- [0 P# v) S, F4 n3 a" z

; }7 C8 O! n1 b         $this->savepath = $savepath; / L: B5 ?& B. ]: U; ^% w+ ]) `; W/ H

t7 a/ Q+ C# Z9 T0 f. Y* P3 ]

' g' v, A L) z/ Y6 ?         $this->savename = $savename; 7 k0 T9 U+ H5 D- V/ v2 h3 Q

4 _& u8 w3 l6 G% z+ h2 U

5 f$ |& z ?# Z: v _+ h     }} 3 L4 X. R9 }- `4 p7 K

6 x W* V4 R) g! t) q% S$ s

- g6 w$ M, @0 e7 Z6 p 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 1 s/ g8 l6 R% ?- V

) ]9 w1 [+ g3 g/ F( R/ v& O, h

7 e6 y0 Z) J4 F& g0 v 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 L+ f& S1 K1 Q1 {/ {

7 \5 {8 R, ]0 j

B) i3 @. l& x/ x/ ? $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 4 [( Z. L; a0 |6 ?

) O9 Z1 v% D6 n/ H$ S" h

* ]+ i/ a T! M 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: 2 {! m3 [4 u6 s5 `; m0 i

4 K+ u! x; ?* e

" F% g6 n0 v. w: E$ e' O% c   % n1 Q$ C2 o/ m1 e+ D, f

2 N6 D8 u# X& x" Y9 z9 p% T

9 T- N# o/ e4 r) h 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:2 h8 p! N# l2 i6 s* Z, e

5 y. L8 c! z" o* O

: ?9 ~6 d7 ] ^5 p' }/ L <?phpclass upload { 1 \# Z# f* ~% b4 ^3 a; B+ w! A

6 E5 k3 W( \# C$ e: c) a

2 @6 J- H# o0 ]) G9 x- y     function save() { ' p+ I( K* S2 H: N" ?, E3 b; r

5 @. \$ U t( ^+ W/ c

; `0 a) y* @3 x( y: T! n! U: K         include load('include.lang'); ! o0 k1 h! B$ ]

' A/ F( [+ s& X0 ]) Q

& P C }9 [8 ?0 k# B/ b5 a         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');' Y3 R. {3 x7 {4 g6 Q+ O) g; c

- y7 a5 E% |: G- ~

3 _" U; n8 i' ~& { {; N3 i   ' ^$ h, I( R `. w- _+ |* h

* ? k& }/ i1 e7 X; O

# I# G; O4 G4 j# {         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');. X- w2 n0 E5 K7 I

6 b3 \2 b! M% U; H' x

2 x, M2 ]6 y- W1 M/ j% Z ^4 v   ( m# i# R6 W) N' V$ J# q

D6 F, s8 e$ x4 A, q

A; ?. ], L9 ]! o, R         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);: p' I. c& r" Y

; u3 y7 P% C, Z- t: a

8 [; c1 y/ K1 d* J  7 N- p1 T t4 S

' f+ ]. T7 y6 P8 ^' X3 L' ?$ s

6 f& Z9 G. u* B m/ N* E! G         $this->set_savepath($this->savepath);9 X% Q- f \; T

2 I6 [2 E K/ f, R" E! g4 q

5 x, |$ E* _6 g         $this->set_savename($this->savename); 1 @0 @4 |3 g' K, ?( b, q/ E

8 q' a8 Q2 `$ h9 w2 ~+ `1 z

" d7 N: ~3 Y. k$ |; T% h  ; Y6 Z5 t. u! F5 X" e( v

. W, v* O* u9 m3 [! Q7 y9 x

6 }) q# G @+ K8 N) j* `* n         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); 8 B3 j! z# `# ~0 \

, p& _6 e" T& O; I% P0 s1 c

- P5 N" P5 c6 J* ?         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); ' K* s \4 Y. B' G$ G: G1 `

( }+ T' c1 c0 Q9 |: f) q

; G: P' C! h7 U; }- \& ~         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);" J* D4 F1 M% b- X7 @: Z! u4 `+ D

* B9 Y* |- f( S2 G, G

9 J% D8 Y) a, ^" s* K  , k8 F% g. V4 a. x

, P/ a8 y: Y, c% k) z( V

, y l+ a8 Z: Z& e2 A0 B         $this->image = $this->is_image();9 V# y+ L# _& v$ O) {# {% m

+ t* \- y2 f* f6 @ {+ y

1 b2 Z5 a. `$ o         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); + O: {2 x l( `4 {

1 O5 q; k6 f0 w

! N/ e1 _0 C* f% S         return true; ^8 k* E# P' T& G5 a

" J2 j4 h: }7 z# z9 g% ~

/ \4 d d, v$ h S     }}( {2 J& b' q; |

$ p6 `6 |" W& h: _

, x/ t" T* F s( f b2 A- p( Y) a, A0 N 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:) s4 ^. M0 F9 n3 X& m+ K) {3 ?1 `

+ s4 F, e4 J0 m! h) t

+ }1 B9 R! Z- S& R/ v4 X! C4 h/ b# h2 o <?php . p$ W& P. g; O/ Y& m

- r: C; u! C, }/ [

" f4 U+ n f# k3 r) w     function is_allow() {- m% H7 U4 Y2 \0 a

( M3 i. U r7 P. }6 ?

/ A+ j& |# I% H8 i; u8 v         if(!$this->fileformat) return false; 0 W+ S- W/ j! q U$ d- K

5 I0 ]# ?# J+ z* p, ^ w, H4 v0 X) Y

) k( L# z+ I8 i( b& ?         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;/ W' @( o5 R2 J. U- I/ _

# c4 ?$ l, S; B. Y' Y& o# G4 u

7 c+ b% H( E" [* `# Z) ], ]         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+ _( ~4 |2 b g

# O! ~5 f4 M8 C& @

0 h0 r; G! E" E( C1 ?         return true;6 D5 |. \6 F$ q! y3 G

4 S9 z% U& e* W7 x/ O8 Y

6 f9 Y, g- l+ r9 D+ v" c2 e     }2 ?! f$ C/ n( q4 E2 E

% g/ ~5 c6 d; ^' t

# M) w8 R7 r$ X& p1 l 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。9 S1 K% t) e: R" }4 x Y

$ n4 H8 S$ q7 r' F, v$ n; `% `

; M+ o: }3 @+ l/ G4 Z 接着会进行真正的保存。通过$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文件。% ^* ^! X( Z2 D$ o* V: O4 O

, R4 \, D( m3 |

: t& d5 g5 v) S% Y* R, |. x- f 漏洞利用 2 y9 [: p1 F) t0 b# ?! M

- X4 o0 I2 A$ O. F% V" V

. T4 t3 \! e- Y4 y9 B ?6 ` 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。1 `4 S- |' d* O% ^$ ~3 I

2 ?& s3 c0 x2 @% l/ U) f

; O. z, X) {: Y( D6 n# L, [4 g* a: r   + w) \9 j4 T6 d) |8 o& E* C

/ A+ ]' S, K- `3 k

6 m+ C+ v- `6 ]+ O9 u 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid' A3 s% G0 Q( D

/ C7 W4 J3 v. d9 z: O6 i

" J; T! w1 Y: u: P# ^! V 不过实际利用上会有一定的限制。2 n' i+ q6 O% U- T, ^

$ c; p0 @; |5 f+ n0 w

" V& O8 v; C0 u w3 r: t0 c 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 3 z s0 `! l/ V# O# b+ ?. Z1 {% K" t

8 c* s/ ?# y+ O, C$ V* Z

% P" p |2 i. L& T! V: n. M+ x; ?  8 p- i1 n) W, [& L5 z/ K% r6 Y2 R

8 l! m2 B! }. V

- m4 `6 x! E4 b7 x! e 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:! l4 g, R) @3 x2 p) E

3 W2 g2 C, T# G9 c1 O+ m0 z

& v9 W4 n: I8 n# F$ m! e( d, \7 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]);省略... 8 l9 U/ Y4 p( f2 `

& Z- n" h, v* I4 B2 A

6 o- c1 u" q5 _0 W6 G 因此要利用成功就需要条件竞争了。 * D& }, B5 z3 o

: J6 s( l3 V/ i: q8 f K. c

: f/ i7 |' Q/ O9 f" ]" I 补丁分析* s) L6 F: b; c# F

) _' _1 r4 Y! x

- v& i6 c1 G" D9 }' O0 ^) D" u% ]   1 q# ^, g7 Y9 j3 l' q& T

: F0 v4 M- \3 c

_) J3 w" j7 j0 E0 Y) Y 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:) Q2 i" c! @+ x

, t0 P" [' B) Q7 O( O5 e6 P

9 O; l4 s4 Z* A) D; H' Q function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}9 R6 \* m3 n' j

, X9 I" E' j7 `! n( P

) B, T( Y- q$ {" m: F  6 {( Q9 X0 q8 Y

, k# }) v* a$ \5 F- ~3 S+ v

5 @- d8 A9 W) A. {4 G$ k8 ~ 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 9 {* r2 M( c4 {7 o

% ~9 z- D$ t+ v

I. [7 P! S, q/ L t5 C4 `6 J! c' \ 在is_allow()中增加对$this->savename的二次检查。' f( @4 f5 O; U9 v) c1 w

% i* w" k! r# B6 F# g

" j8 x* v' o/ |1 j, b& H 最后 1 F! s0 J" y# L+ O* _# G

7 ^' i0 ^4 t A

/ Y/ z) r/ ?/ w% O6 ~* _ 嘛,祝各位大师傅中秋快乐! @4 m' K' K, H: N2 x

$ w$ r2 K9 i; `

+ S! T. t1 l. K) l( b+ Q- n  : a- b2 q0 O0 S

% q/ X8 {/ M: {* _4 m9 E8 P
回复

使用道具 举报

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

本版积分规则

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