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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
2 y0 \ v* L6 {% L* g

$ d6 j( v" d- r7 R; \

7 Z+ U) L) y% b2 e; x6 O

" |% {* M! ~6 Y0 v4 `" h 前言5 D5 i9 F: |* h! B0 z+ e

# a7 |, F6 p0 n: O

' `( Z' Q8 i# A! k- A1 l% t( N 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。3 Z) t3 C- X A

Z# b6 `1 H4 B/ r. o

! E" P) t" T7 u2 h( x( L  7 l* ?- p* p/ K5 `5 X7 [

1 v) f7 Y7 t5 m

( @9 a/ `$ I) }8 Y5 s% ]5 f 漏洞分析9 q; H1 W; d; e- I, m+ }0 ]4 j5 w

; \8 s \* x/ m3 t/ a* y; S; n

( k- o w# A. Z 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:) a! H. p5 h% Y) ^1 b8 a

# n8 H( R2 V5 ?. l

0 h& _. n% ^' t% h8 A  ' S+ Y1 t4 j: b9 w" N

8 n5 H6 Q7 v/ i

4 q6 D) m3 o6 a6 ^8 h 对应着avatar.inc.php代码如下: 1 N0 H+ {! c/ [1 P/ s s

s9 W( n3 }: D- [4 f$ f

: S7 M7 w$ a# u <?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) { 7 a/ w: y9 F1 G, t$ b4 ]

0 D4 f( m1 |5 P, D# q6 b

2 O- M6 Z$ a3 }% [% `     case 'upload':" c, @ p+ H* Y1 i) K* Q. ~

5 }/ J1 l- s. j2 y; J% h

3 }! H5 [# d- B% Y         if(!$_FILES['file']['size']) { # Q0 H( F ]+ @8 G/ e

2 N+ u: f. {* I3 R+ v) Q& f6 b

/ Q, h8 }6 B( ^9 Z             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);; m, Y( v2 E; w- z9 R, D

/ v0 T" c4 G6 w: j/ L( b7 Y

! N% L2 }* Y+ p2 t# ?             exit('{"error":1,"message":"Error FILE"}');7 U+ y/ ~8 m/ V% N! z K) d' x

7 c4 k5 J; U( }5 L

. E8 Q6 v3 l. ~7 j/ ~+ h0 D         } 4 [& e. ^, y: A

) r0 m6 a) ?+ \+ x- k' b

' j# |) J" r% a& E$ ^! |0 N         require DT_ROOT.'/include/upload.class.php'; - H6 L, @3 O* k* O' c: J

6 F0 b: Q* |5 h2 l, {+ q

5 f' @$ I: c; V3 Q$ M% l   - Q9 b; V: S3 j* ^, T( K. L+ \1 P% X

3 r8 k6 n# C2 C" { d ^6 M( Q

1 S. n" T3 u2 K         $ext = file_ext($_FILES['file']['name']); 7 V0 [4 T2 t) Z+ D6 _( s7 E2 r6 D

& ~& C: `) `4 [: j8 n- U

7 [7 o9 G1 ^. d/ j         $name = 'avatar'.$_userid.'.'.$ext; 6 ^% O/ n$ f: ]7 F/ n, e

& q4 P/ a- D# ~

% T9 f3 ]0 a7 D, t% P6 M) f         $file = DT_ROOT.'/file/temp/'.$name;0 _: T) }/ T/ e( I& f

* q9 n: r0 p; U6 L1 ]

& [* u) F7 K0 U* g   ( S9 z( T$ n" C4 U& O f

8 r$ U+ T2 ?) Y9 [% l3 m

8 d: f& E( Q2 b& ^0 ?/ A         if(is_file($file)) file_del($file); 2 L# C( |' `9 Q% r8 U2 Y$ S; v

* S% {% X0 q* W; B

4 y; y3 X* ]/ r) U- i% G         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); 8 H9 E' \7 n8 h) W2 A& s3 j

$ O( z, L6 s, ?

8 m# P6 v0 c! t' I   * h) w2 n" @" w1 o

" B" r$ Y, t" f# `1 J& y3 q3 ~( A+ C

8 \: f! }4 h1 B. E9 _/ Y8 @: J         $upload->adduserid = false;# h3 K Y$ ^& `

( q0 O2 f9 i7 `9 R8 a# h+ q0 q

/ U3 Q8 F) O5 J+ `8 R+ P  ) W' x9 [% l4 n3 ~/ l+ J

3 q* E. @" _! p6 E' l8 I

5 w: Q( l: H& o         if($upload->save()) { z8 m7 [+ Q, p4 k6 z! B7 u4 w8 c

" ]- [( F# I; M9 ?

* o, c; L3 Q) y: f             ... ( [! f4 a& v4 c( _4 f) `

/ i8 B& a; \2 j6 H/ R, e( N1 I

4 |3 s! l" d# D i9 K/ X( F! O9 U         } else { 9 }% ~1 v4 Z( ]3 I0 T& Q6 |! n

1 c9 V+ m. _5 x7 b7 S& z

$ A$ v3 e+ @0 A& p2 Z% e" @/ s" O: ?             ...* J, c6 w* j$ C, b; H+ k5 F

# A$ A# F& p! x" h& W8 K' n7 e

; f! ?* d, d3 [         } 8 ^4 R8 B6 V: f

/ u! [; E% Z) X' K

- @5 F( m/ \' _" x0 h5 [* `: Z     break; % q5 k" P9 f2 c* L: m

2 v! k/ x5 L8 l$ s2 n; G

, O F: [- t q6 Y) W3 M 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。+ r$ O7 ` q. V9 W5 q

, ?! i, @4 b [: v9 N& ]+ Y

5 @9 Z% ^# x/ w2 } upload对象构造函数如下,include/upload.class.php:25:1 T0 h4 K# _% i

4 C2 ?, I! A8 D) a# x4 z, \- a/ y

5 E1 I7 k) U" J8 e x8 ?. B9 Z) U <?phpclass upload {6 @1 g3 E; R* v% f9 j

1 e$ w1 |4 N; D+ H

9 |! p8 `* l, b1 Z3 b, k; W5 q0 o: Q     function __construct($_file, $savepath, $savename = '', $fileformat = '') {: U2 |- a, P9 O/ o

8 n( f; E% q# x3 |9 f

) x* @) E0 ^3 O2 n& a7 m" j9 ~         global $DT, $_userid; 9 R, f6 a2 N1 A1 s3 L1 h

" j# h) Q' L: Z4 K, ?

. V9 T6 t6 p$ y& V3 n% k         foreach($_file as $file) {, o0 u2 ]2 N! \% E' ]; w

2 e& f ]6 H8 x3 g9 _8 k

8 z" ^( y9 |2 ~6 D/ x             $this->file = $file['tmp_name']; * h$ U4 O* n' o$ i0 K& j' X

2 J: j" U: E% g, p

- S5 ~3 O( Q" t2 _$ K2 Z* k             $this->file_name = $file['name']; 4 _# L; w4 c k" j9 Q$ a1 ]

4 m& n* u( |4 M: |! E. k% `

' o2 e! R4 A6 i! r3 G             $this->file_size = $file['size'];& \- @( a3 s" ]+ p z

$ d" q" X/ w$ H

- I) E* t9 u/ |; F" u2 j             $this->file_type = $file['type']; & E1 J! C- z$ ~" u) Q/ `

; p) B Q* O( l z# j, b0 T) U2 C

) r2 X$ g) Y. L! N5 U. m             $this->file_error = $file['error'];: w+ _; I2 e$ b; ~

/ X* ^% E6 o* \5 M. D/ t9 F& P

4 G K* u2 O( n5 ^: [& H   # Z7 N; m) Q# Q* E

4 C K/ f, E; u% l- j6 }

* b( u1 _+ l4 R. v% n; d6 u- |7 p9 M3 Y         } 9 d& z& Y6 K& T; {

; B2 H, n& K) u! C

9 ^9 _; r$ A! t# i& {9 h" f         $this->userid = $_userid;7 A+ v8 K& `9 r5 o! e* d k

. D) n$ U- x( Z, G& i- [/ R

8 m+ q* S/ y$ q) C         $this->ext = file_ext($this->file_name);: ~+ U9 c- J7 j8 ?2 X6 Y

% C/ ?) S% c: }4 H! a& S) v" v

) a3 P. r5 J P! { e0 r; Y5 K5 [         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];! l9 s# V' a/ Y+ S( Q T& W$ t

M1 T- \* n; ~

& a7 y1 w, x9 {3 J. [6 \1 y. l- X         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;; C0 l9 |; p. Z, O

- V6 {; w" Z, w, R3 k) O; q

! O5 @! r+ N8 x* G9 g. k% q         $this->savepath = $savepath;4 x- W/ d# \/ R3 M* g# Y1 r, t- Z( H

' t/ v0 C; M( c1 R) v7 C

9 u+ Z/ o, u0 U4 o) C         $this->savename = $savename; 2 h4 F% w! h, [$ n3 s" Y5 f; |

( y* k3 F3 N1 b

1 |! p4 k5 D ?2 P @; s1 ]0 B- h     }} . T k1 o' e) t* g

- e% C3 T" b+ v; }# Y' Q5 `

' e8 l$ z! g* k 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。* @1 R' r7 t9 Y" n0 m" w6 r

! i d" Y' G) Q7 q

* a# b3 A6 t @# f" r 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 $ `! ]3 y4 `# i2 i, i

/ P7 T; a) O: f# ^- \) k

2 |6 T7 h) P. D" D3 e( Y $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 8 T! b, p% ~0 p) y' ]

6 [- N! O) U" f% R2 N

6 f9 N# j0 K# l) a6 N! N) l8 ^ 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: ; G$ {: \1 d2 G9 l9 x6 v7 k t

1 @% M% i1 c3 A- c- u

+ i) l1 v! y* n3 @ z( W  ; O$ z7 {& p* U" r, h

N) a, i" L$ n: B

& I7 O3 Y$ N6 x6 f, j, Q* W4 C' x 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: " E3 C3 B" G5 o* Z

6 H( j& V5 g: N% V; D/ x. I

' S( a. J! P. O& g$ t5 k2 D. m! Q <?phpclass upload {, a% j) `2 f6 A" P s! Z0 a

* w% k( C" M# ?4 Y/ `. g

# t- h) z. {* A, l% f! k     function save() {9 a m6 [! A1 b a( p( a3 ]% Z

2 a% x3 R0 l: t: o8 R' T& D* X

. e5 B7 K+ h6 V, B. M! |4 r* c         include load('include.lang'); C" Q9 ^. z4 v* [! N! k4 _( f: c

- _, W! e9 G- i

1 ?2 Z0 y _" J4 |         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); 3 J, ~5 e) F. Q& `

7 j% L4 I2 o( R2 n1 T6 D' N

% l; r! H8 G! Z+ n4 T( _  $ i6 s4 F/ z( Z+ U2 ?

' I" m6 Y+ p# l6 w* m' Y$ Q9 i

) B, I: q& }* X8 E1 E/ w         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');6 n$ p/ j' Q3 }3 R8 C! u# T

, k" I# r: ^8 C% a) P5 G

# Q4 ~# u: q5 Y/ K# p: e' b   - J, r$ U$ ~* u* @

2 k( V& G3 ?' Z7 R/ w H

; v V, D2 T5 G5 u         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); 5 y% D# n& w9 `

0 X, F- R4 X/ p P

& J; O b" R# K4 [7 T   ~8 ?; K; ?! `

B, D% H3 T6 v c. g, p# c

4 c0 h5 H# I# |1 C F         $this->set_savepath($this->savepath); % T3 I7 ` r: J

" ?9 l( e3 S; T* l- [

0 I) p/ h! h+ h+ Y( O         $this->set_savename($this->savename); Z7 R5 O! A7 ~2 V

% P- B) K* t' J/ J

2 o- {6 ]. I' d x0 D6 ~3 {  8 G0 A! r) f% k+ }, P

?1 ?7 ]9 n) _! m

$ C3 c# F' u. i         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);$ R! S# J( a. w( n3 B

5 V1 u; C! i* y4 w. V) g4 F

; q) U$ {: w# z. u2 w2 u         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); ! z: u* G) b1 D, \

5 B" a, M7 q7 V( }' f; c- V

# W+ Q! @' M* H9 z& [9 M9 @         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); 6 l& ~3 C9 r0 p; W) D

8 x9 F# R: g6 m P

7 P) ~* e& T2 M  , I% Y6 ]4 q8 z" A. ]! s

& T/ ?, m! K6 F7 i- J

# O* z' ]. t. F$ n9 T9 ^         $this->image = $this->is_image();! p( j! V- M: f

# l) G' t; C1 E" z& H" \0 {

! a4 M$ h4 f8 N8 s2 b9 [6 [0 u3 Z         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); J: ~. E8 b+ N( Y

# ?# n" Q5 \, r \% G/ f" }

( \" n7 l/ Z* P" p8 p2 l3 e         return true; % k4 \9 w% }) m/ W

- A1 J( E) D! P0 @3 j6 e

/ O! X, M, h* Y4 K     }}! a# j/ r# J* _3 A

/ G# d& l# v) @; H: u# F, L( u

0 J7 J& K+ Q5 m$ c( S 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:/ F2 i; V, J7 n6 N7 _3 k

& _3 H0 _0 A. y2 W! |# d

2 L) F( p) ]: s4 ~( @ <?php; t, R% j& p/ m) d

# B1 k0 Y9 c0 m! z% ]. K

/ m; [; t8 D) r7 M# x# @1 s; Z     function is_allow() { f+ P) [: v5 y0 d" w4 `" u; e

9 e& H8 g! N% |* \/ d& Z

7 ?' V9 P4 ^1 s6 y4 _: c7 j         if(!$this->fileformat) return false; % ] }/ F) W" d. V, s6 \6 `* f

# N4 C. }3 V% n* U* k

1 A/ [1 |! ?3 W9 Q         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;2 g: W6 i w" K' A5 s) \

( K5 j! Y* J" A3 _, e2 Z; `- H2 q

5 p# T4 ` R) {2 D( k4 I         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; * W8 k2 ^8 i* Z4 |1 a3 Q

: y/ W9 r* S3 _3 E( ?1 }6 ?: k

. ~, Z. y, q* F$ M         return true;8 `" H& z' |( F) V* a7 [- L. x

' \7 }3 L3 o6 k, h$ B0 z; Y

1 J- ?7 Q' `7 u: L! R     } 1 N0 {) u6 P' l$ ?: Y- H! C

7 w5 |) n l9 o" Y4 Z% C. J

+ W7 E9 R' T) g: W l 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。0 B/ o6 w7 M9 k5 ~* t H g- j

, n9 H8 C% b" I# x( Q% S. e

7 a$ K N" c% @. x, 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文件。 % r2 A, _' h3 J* e( i5 }5 u

% X4 V* S$ s* I2 a

$ {& @& z7 u) s" U* X t 漏洞利用: c% ~, T* v6 m# F* _4 U

/ N5 d+ [ k- V

" i1 ^% W- W' X+ |7 c9 W 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 / R6 ?( `6 }- F$ l

& I" d' V' C, K# U S: U

0 u5 ~0 H4 l9 m" d   7 R5 e+ w+ q+ U1 g' ]& {1 j5 d" v, |5 w

. D+ ~; p" F. _4 M) n

/ z' b% n$ o& N) Z" o 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid 8 V9 [. n1 J1 t u. f+ y

6 F' y7 A/ C$ Q# x

0 n* l, s1 E+ e6 W- q7 L 不过实际利用上会有一定的限制。" B: {3 n' p5 \/ w6 V+ `4 w/ K( ?

: ?+ o: w) u* d9 }5 ^, I3 Y$ z

$ z" m: v8 a% `& Y7 R' u* M7 m/ i 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 4 Q; ~- k* i! T- F; g

, x7 ~' H5 N1 z2 Q% k# E+ b) X

# W1 M, q/ X" c, C8 S   ! o) Y2 z* J' Z0 `+ ]

* w: }) ?5 L. i: o# o4 B, E

2 _, V. q# a4 Z/ u 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: % d& n( k8 \$ X: E! n( f$ L

6 H8 [& n5 w7 H& X# Q# J7 `

* }' ^- `) H3 i" w9 \7 g+ q 省略...$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]);省略... * h) J% |) _9 G1 \

2 z( g& E4 \( d4 Y# f/ P

6 H2 H& ]% P% e; K 因此要利用成功就需要条件竞争了。/ W5 A! F6 a* F

( E1 S3 d' i% L3 ]8 a# W) Y

6 Q L8 l; {$ n7 K/ D2 f 补丁分析/ Q) A$ E! H& G! k

& x- R2 {0 R& Q1 h' E/ Z

) }: y0 m" t& O" V% B) e6 H3 i: k   1 a! ?$ B( m. [6 U

8 W; @: i4 r `, S

) ^2 y2 Z4 ~6 |. E+ ^' U% _$ F 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:, N: S! P. u. Z7 V) K& v4 x. c

" m, C/ ~$ D, O5 M' X

r0 X" k8 K7 r3 N: n function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}5 \8 O% J3 k, f7 F

8 ?' F! n/ R. i4 L( t5 N8 g e% \$ M

. }) z% V$ w0 }1 h- f& U% l- Q  9 p/ n- L5 W; l6 N+ }4 S

, `# Y b- s: q4 e

9 C: Y( `; u7 m1 Y 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。: F* a! m- \( y; ^

) u+ X# z% ~* A. H8 ^

! x1 E3 Q4 W! _- O* N 在is_allow()中增加对$this->savename的二次检查。 : ~: S8 S0 r% j9 X5 P- h0 F

5 w: z3 |0 a' y! t& M1 h

: @' W7 s7 B" w2 D6 R) X9 Q3 k 最后7 X) [. V! ]# K1 Z

' i2 ]: f4 j" n5 J4 J! P

5 G4 [6 g% c; U# l T9 l3 Q 嘛,祝各位大师傅中秋快乐! # \* [4 F/ J6 D/ `2 s# N' Y0 P# H

7 L+ `" g) E* _8 a# i( y6 `

$ S* ]4 A; g! n. \  3 h! M3 n; e# ^

! t8 d6 n7 k8 F9 A0 ]* z! T
回复

使用道具 举报

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

本版积分规则

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