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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
9 A1 b1 [. n% p

" m: q* i k# e1 E/ _6 @+ |

7 V- c4 Q; F1 t* x3 R

/ K) U# Y6 D8 m5 \! \1 L 前言 : \! R/ R: ?. Y5 H: i

& y/ B$ n, d9 L1 n

$ U S/ P+ d! A) x$ P8 w' m 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。) Y% k2 K1 N) I

" o' C$ v7 @" @- O7 x

- [# ^+ L4 w$ h, }/ u+ C   7 l% Z6 P4 N/ e/ _2 Q8 R A

# Q; t" Z! g% o- e7 L: o

5 M, J" N, n4 M8 n& m 漏洞分析1 R+ V6 g0 |3 Q" U8 z6 {/ q d

% `( I0 K, W7 s, t

2 |4 @9 K: j/ y# e; P 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: 5 I" ]8 Y5 Y5 R

1 x; Y" g: _& N# R% L

8 ~/ x: x6 a+ s   8 v5 @5 x1 m9 L4 M$ }0 c

, k1 K( K+ G% b0 B& O9 x! Y

6 j2 w9 b( ^1 [ 对应着avatar.inc.php代码如下: 1 Z/ `" T& A$ \0 s8 P2 [

6 s3 ?! V* ^; }" a0 |' q2 v$ o9 u

/ u: \) H; n0 {, r) N <?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) { : J( h# ~( P" G$ ]( o

" N7 P$ S* [2 K, h) X! ~) _# b& k

# V! k6 [8 D9 Z* C! s1 [8 N     case 'upload':! s) G" k( e0 I

# ?# G3 H, R3 e: t# p- m- ?

' X5 Z4 m! R' L" a7 r0 {# R+ |         if(!$_FILES['file']['size']) { ; r( j7 z& o" S- E( ^! @

7 O' d4 D3 t1 z# P3 Z, Z! ?

5 C4 o- o; Z" [# U             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); - A8 ]" p, }) w* b: G4 m. I

+ c9 q4 i6 \' b

* i8 X/ W" U3 ~9 } ?% G1 g             exit('{"error":1,"message":"Error FILE"}');1 U# Q: A* E* \; s, x

' @9 n" F+ ]' s& h

# ?7 f x4 l% y6 j. @- s         } 4 r/ T- U1 m2 q- [) C2 q' I

% O$ @4 [. K+ [6 r2 N! C

9 r& q. |4 D" `) \3 v3 r. U( Q         require DT_ROOT.'/include/upload.class.php';! r5 k4 R! A2 \9 y. q6 {: M% |7 B

5 [7 `9 b' t$ g5 O0 T

- b6 ~6 Y$ ?/ I; a) ~6 U  , g, E) ^( \* h+ E7 X9 P* q

' j8 o1 A R; c: P5 I. z7 F. S( l

; R! V' p% W% j5 q( F         $ext = file_ext($_FILES['file']['name']); 8 L2 {3 F( J X

! ~ ?7 g+ s( V4 Y9 t9 H

5 W$ U: M% R/ g) E/ V0 A         $name = 'avatar'.$_userid.'.'.$ext; 2 [/ ]- W) T3 J/ y/ Z- z. Z

! @# L5 {3 T' F m) Q( I

! \# j/ l8 J3 p" m3 D# w9 g2 i         $file = DT_ROOT.'/file/temp/'.$name;9 O, E; B, u8 W3 h/ X- R$ C

& c: o8 q1 g C. ~/ V' B+ u7 m

7 ~! |# K( g( E# M7 m. s. d& R   ; X) B: S% L- j( G

5 t7 E# W0 t6 L- A- h

7 [% C$ \* O. h2 O; t         if(is_file($file)) file_del($file);4 J/ Z3 V! F) D4 b

) o) J; Q6 k. m) N& e' o" H' \

% t. [( a7 W6 j8 w7 Y' M) V% n3 ]* q         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); : X7 n4 s ~# b+ N; ^

# }+ C j `1 r" \/ o- L. y

7 D2 ~ _9 d- v2 b' j   8 | ]& V8 H7 t ]) R) z9 E

. E4 h4 G# P; e5 `( U8 p& K

0 b h. F# _) l& Y& C' c         $upload->adduserid = false;: m. {2 N8 v- L' h; @, @% M

. Y3 G. H4 j8 o2 R0 m" B+ N

3 E: N* A! S- U6 e   6 R ]) w7 {) L% G

# ]% Z5 l1 b }3 [

9 W" G: W3 A9 E4 C- T         if($upload->save()) { * O/ b/ U9 N% Q% h8 Z3 X

1 w' I; I: A+ }& S; l. e8 a+ v

# H7 p+ J6 Z0 t9 ?1 S8 Z/ q; e             ... # T) j: r. a; ]+ j' t

* r! d4 |3 `3 B

" J# Z% _; M, \% o         } else {! s$ K9 o3 B* B% }, A

9 v5 ^( Z$ R! O2 H

% v( Q- V1 n: Z& y' s' g# G3 L" W             ... 2 e$ k, Z2 a; D# V9 }& ^( x

( j6 _. L5 ?3 i

5 J* M; g9 g% d8 x, D0 ?         }& V1 l$ t0 ^# }' a4 @: a

, o3 E) W5 r* X! Q& B3 r& T4 s

: p4 x/ z/ F$ I6 q9 p% d) Q$ g     break;2 r. ^7 K. I8 ?3 D' g+ ?+ Q9 B }

; Z, C2 f2 f: m+ o! U/ L

& b* {* b# i5 }3 J6 } 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。& s8 A7 L# p7 Y' l

5 _8 t1 j3 _$ b! E

2 |- l4 Y- x* x$ F+ t upload对象构造函数如下,include/upload.class.php:25: 4 k$ l8 `* I: Q8 ^1 V b; H- E ]

+ n$ w1 n; |6 N3 V: N6 }& Q# { S

5 W- q' p# U+ J" p" D L5 w& y <?phpclass upload { ( l7 |' ~& L9 n K2 Q

5 f& A( A7 W8 m( x% X; k2 l

. z8 p' H- D. c* T- k# Y8 ^+ M     function __construct($_file, $savepath, $savename = '', $fileformat = '') {* m; \! {" L& R1 p, i H3 n* F

" n; F0 s4 }6 o) K

$ D/ P: P! e5 W7 d5 s% {         global $DT, $_userid; ( }. u# Q) n7 |) B D2 R* q& z8 i

2 u* Q1 t3 h3 H2 n2 f7 c8 M+ U

5 z4 {; c7 ^9 ~& l" V5 I6 C( S         foreach($_file as $file) {4 j0 [1 e; P( m! V5 F R

6 I7 j% _: _5 L& L4 t! n' |

) E1 u |" ]2 V' w* R( n2 G, M             $this->file = $file['tmp_name'];1 Q/ U5 B! F) e( H, c; ^! ^

& ]+ k2 g2 y+ b0 W

1 t1 |- E2 T& M' _7 E             $this->file_name = $file['name']; " s/ e7 v4 T% b' u# X, E; N1 `( L

0 J% y1 c4 k3 \7 p

4 h+ ~) [8 G x& C) J# d2 p- M             $this->file_size = $file['size']; * u; v: o5 R3 h

3 B }0 |% p) b, C# d( ~

* K6 g$ N5 X5 s( u- C             $this->file_type = $file['type']; H$ J$ V) R7 b7 s

" c; B, v, J6 a- X% b# {" @* R

5 Z+ ?' s2 o( f2 V             $this->file_error = $file['error']; " b& S& k: b7 R" r

- r. \0 J# F' a! ]

- i7 A" O* @* q9 C/ @! n   , s$ N6 r! s- _1 w

Y; c9 a2 H0 g. _% h: L! U. F

; [. g1 F% k& s" ? ~& ~" @, r7 v         }2 |6 f" X2 b: v% Q0 D9 P

4 Z! n: {. e( r+ w7 |

- }% N9 _* c; |- z         $this->userid = $_userid;$ q/ @1 s q: X; I

_2 m8 d; H e3 G+ Z

4 i! {( K& I8 I- u" `( n- x @         $this->ext = file_ext($this->file_name);' b/ a" h) {" o6 v# h- D0 }% U- ~! x

% U8 y$ C4 ?6 f3 s

: g" c$ S3 W0 S         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];- \$ L7 ?9 r- H8 i

f$ }4 a+ v. ^ }6 l# i

6 m/ h' p' h; E# k         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;7 s# [3 |; l" C9 H0 m! c& E

$ E' \1 @, m' o* k. N

; J; O% |9 `+ p' c. E* E         $this->savepath = $savepath;3 I. z9 l/ c2 P; U

; g) P2 \: K4 q

, W, i x5 c) X2 m f* K8 c         $this->savename = $savename; 6 A3 {5 I# V# J$ \

5 T7 m+ Z6 X! j

2 `, g' Q; n7 i     }} * T6 }0 ?+ d3 `- e0 q+ i

$ o L% d0 y5 x1 a

6 \) H8 }: P. w5 U7 a& P7 @. V( v 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。6 \) R2 y9 o; d1 u

6 } c, V; J: `, g

: N9 x N! |# x# h1 g4 [ 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 * i/ r2 ` K2 x

- _$ y- N' @& s4 O4 h" `

) ?, t& ]$ \8 \* S8 l $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 ' d5 }! ?# q1 R8 p) F% `: y! H

1 i3 W: N0 p$ J

+ c, [/ g# Z% K3 t 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:7 u# S: ]; o9 y }& c

9 e/ f) p/ E c. Z+ _" w! ~9 d, g8 O

( A1 N: U, r- [* u   / X* C1 Y% w ~# v

6 d' J; d4 `3 H

. t3 c; t* L y- A7 P& R+ B* I( g% i3 m% N 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:1 a5 b$ p3 ]! _0 k

% S/ Z: p% d3 X7 ^" o

. q2 g, B/ Z4 ^: C9 S& m <?phpclass upload { % x( e: |& J3 B! S

0 s0 O' v! s( Q; e% L

0 H+ {& v( P% r2 l5 X% g* T0 K8 K% ~     function save() {6 I- r# g& ]. |! U, `. Y

) |. e" a" H1 N

' @% d5 W* S5 y9 |( ?. e- D         include load('include.lang'); ; N" Z$ {( W2 J

7 c7 S7 I: |6 {1 v# y: a

6 o% y. k+ B |0 }1 A Y         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); ; l3 @ v+ t! R6 @

- z: S9 g) Z& I5 o& A1 [

. A9 I s' V! j/ E) z' p% _, T! A& D  # K, o/ T. _7 v/ s

5 \% j" R0 ~7 }/ r3 T* [

8 ~' x. p _7 b8 k; K         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); ; B @# B0 \4 c

, A8 Y5 @; M5 ?4 d

/ F; J( }9 f0 J, B3 V   7 r) Q+ o6 {4 K3 K6 h, L

; H. }7 X/ c5 z5 Y

' v; p9 W# [, Z( ^         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);2 E. B7 o$ d; j, O2 Y, Z% k

+ c/ j# c: S( D F! A

! m" [7 O% J* O4 p# E" }  ! e. A. }; |9 z8 u$ k

! w; P* d- s/ y

- c! u; H4 @. ?( A: u; p         $this->set_savepath($this->savepath);5 ]% w' P, G. [

1 n; U& q3 ?6 a4 }- M9 }0 V& C! }

* I* }* K$ S) F1 r         $this->set_savename($this->savename); 7 M6 S4 P" Z- \* b# Y7 U

4 ~" c5 A& q" H' p

4 z8 M9 a3 m" l" B8 d/ l- O  3 T' E) d% c3 S# h, R! V+ w; ~# s

5 g4 a, X, g. S, ]! ^9 g" r( b

$ O5 L% q/ q' q* O7 s. S         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); ; i4 B2 z5 G( s9 x3 Z

0 ?! q1 G6 T+ j2 \0 n* C

( }5 U4 J( g8 _+ B         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); . _* g4 h' x: d

1 a3 D+ r) i" F5 a+ R: i

7 b- S u* ]: ?5 R4 m1 [         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); 7 G9 O, ]; I$ d( s5 G

% v' g$ I$ O; Z$ [

& T* U% D" T" I& Q! Z  9 E* q+ Z1 j5 v/ F u! s9 ^

7 U% H9 X4 t7 E& H6 o. x" |

/ G/ f5 P: _7 F! T$ k         $this->image = $this->is_image();0 }1 z* j4 Q6 ?' f8 J8 `

- G# [8 B6 R( I; n# {

$ w0 i- k& x I4 ^: q$ c& E( R         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);" Q4 H; {' O7 P; z+ s7 q# ]

) U6 `( v2 c6 S8 X G, u

! r# h7 o% o. Y( o7 {5 d ~         return true; * c( R/ ~2 V$ l( _

& J1 H2 ]- g7 }6 J+ z6 F6 v `

# o9 f: j0 J6 r0 l8 s+ H" m     }}' r V% l# T8 Q6 R5 I

! k5 x0 t" y5 N

1 W9 Z# P- r+ C# A( `, d 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: 4 t" ^: d) K+ c1 L. J, b$ _

/ W# f2 d, B: u- q1 Z* t# h

. R5 Q/ s! j; y! l7 I <?php& d. V; Z) J' P8 t' p

9 o( l( o/ v" l" V/ b

1 @ F( I A. r$ G     function is_allow() { / _4 V6 E7 D; ]+ k; T

7 ]4 u- P% v( l9 T) J6 s$ p

& }1 u& D( S# {. s3 q& K- @         if(!$this->fileformat) return false; - T. ^ l- T. b5 A

9 `& B6 T' _7 d

+ i! b: _7 g+ k( ?: c2 P         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; - m* E" W) u$ q0 ~

* Z1 @; B( u2 h" z

5 W! g3 H& ?# T         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; - p8 P6 ^# W0 Q t% f7 ?6 }! o

/ v' {' _: q( D) ?6 {9 T2 l; @

O5 q, p! E) z0 U: R* w) Y- @         return true;$ F3 T* z6 R4 N" z" P: g

& _8 o4 _) W" Q/ W! g, h

5 d9 n: `, g W2 g% I     }. W/ r' P! V% a' t, e' }

- {$ ^6 c' [+ Z4 k

% k! e" L/ j4 Z8 p 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。5 P% g) T! M7 C8 }! ~6 f" ]9 E9 S4 Z

5 D/ H/ U$ W4 ^+ J" A/ N6 v, n( \

0 ^( B( p2 _0 C1 R9 V6 K& Q2 r 接着会进行真正的保存。通过$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文件。 ; ]9 N, ^4 s# U

% j' Y$ Z4 l" ^& j5 T6 w

: s- f" T- H) @7 s T 漏洞利用 $ \/ ~% x1 J- u4 q/ o

9 D5 B& j1 w) C. c

5 d( O+ E4 }5 f9 u- c* x 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 $ w% k T; H) D X8 @8 \0 X5 x

$ p% u( e$ m8 V3 G6 Z

4 S: w# G5 z0 t* W   9 f% |: p7 o, {* K

+ _8 T" S. N8 C+ A: ~

; e# H( J& W/ c; Q 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid; Z7 a r2 v0 J c# \ V

6 @$ F& T) n# u* b$ k; U

# i I, L8 o! b 不过实际利用上会有一定的限制。# {3 F+ I2 ~5 c# Y/ V

2 G' v9 G |( B8 X

5 A) K6 G) i% s$ a' [4 u9 I; { 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。* p7 e: x1 u% R

( I$ ~' _1 M0 |( e

+ T# t& b# C2 c   3 Y% z% ~+ a9 x$ M6 b4 I$ K. X

# Z. c$ |. j. o) G0 ^

) X6 b9 _: f" l' o. a, b V 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:, o4 q# j& P2 P3 n, v4 |, Y9 L

$ M+ A3 U$ v" P0 B0 s

D6 r8 V' r5 U3 b5 D* V T; M 省略...$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]);省略... . r# y' H3 D" I7 U0 d2 Z

k+ E* e5 l/ |: z; @

1 W4 |, a7 a1 L+ C 因此要利用成功就需要条件竞争了。 9 D7 d0 m2 D, ~

- o3 y5 F; P8 z

5 V5 ?/ o9 j9 `$ q$ Z$ h( j3 v 补丁分析5 ]5 {7 |& L. g8 @; p) ^ Q1 u, L, W

5 | j0 r. n" w3 S; C2 ?, g3 o7 [

0 X, e! \: o. g+ Y  & Q$ C6 t1 |4 y! O+ {3 M

/ ~+ q$ t$ H+ O% }- j* x

n/ D! K3 H8 P% U9 E' v \ 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: & \4 a' C# N" u+ e

, ~" A/ ~4 ?( T# z, w) Z

! o' P- Y% F3 M1 M function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} 6 R% Z$ U( ]: @* e \

6 R4 U; V! t. v# Y0 T

! H. [7 g6 J4 h9 E. c: k  ; [! s r% {/ ?; C8 O8 m

; T8 g& s+ r) O5 o) j( I: h# k

. M5 U- m# u/ z9 w 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 * T3 p$ O* c# d) o( G

: Z9 m, t: w( N, B8 M

% |, p( F# L* Q _ 在is_allow()中增加对$this->savename的二次检查。 " Z8 h: @& F2 y# J

: O0 k3 B- c; E: d, I) ^% ?1 Z' U: I

/ | _3 _" ]8 [5 w! Q5 n ~ 最后; h) j Z. Z- G9 S

" O7 ~# B5 A7 Q q- u* Q0 D4 w. v

) k! _$ v* v, s1 l 嘛,祝各位大师傅中秋快乐! - L/ ?0 b# s" L' r

: \) l" Q' X6 a6 B$ l q% R

- P: `0 b; \) h8 S# f/ |  ' _( O) C/ A0 Z' N3 R

; A; z7 ~4 q* {
回复

使用道具 举报

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

本版积分规则

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