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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
# l q4 [6 K! |

" B2 Z. z3 ]0 M; r' _

4 @$ G3 d2 { p7 s( q

$ h: _3 R+ r+ k. G 前言 " q* {; V$ ^* d4 N& G

6 X1 J# g. L3 m" s P o

* c0 ~- X" c2 ~4 b 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。2 w/ |, k- y# C( r' ^

$ e2 h. g. i8 j' i

) u& w0 l/ A4 d5 \, `- K4 F/ _   ( N) G: J* B3 _6 O# W3 @( ]8 ?

9 u2 N3 w' M7 t

, K: V' L3 _9 @" N; |+ _( r; f* c 漏洞分析 2 ~' h' y5 O3 X' z1 }* a7 c; t1 M

7 s- S2 E, d, ?& h3 n* o$ H7 U2 I

5 x2 K1 Z; H% Q4 a2 M2 Z& n8 | 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: / K" {4 t3 X# ?1 g6 Q' T

! ]$ Q0 a8 u2 [/ a& U

$ y9 K. F5 y! H5 t- I  . D: J& ]$ y" f h: n$ Q: v

% p3 [7 F: D$ n; I

; Z9 O7 a$ H: u 对应着avatar.inc.php代码如下: , i3 v7 p1 a- E; |$ w3 h

, I! M# e: k* l1 c: v

+ b0 G' J _4 M2 ]4 L6 W5 i <?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 }' f0 D4 _. S8 i3 e

; c4 d7 m. n+ V: D2 g

- }8 h+ L' n( z8 C8 @% N& x     case 'upload': $ J H8 r! U2 L) B( f

1 u. j9 g: M# j& `1 D

% v' B3 z6 V( N' m         if(!$_FILES['file']['size']) {) v0 S5 `3 V' o- q. g% E8 H6 m

# `+ Y4 E1 U; I& y% n

1 V( t8 ^( Z6 w# c' ^             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); , @" n3 s9 u8 y. [2 [0 ^

4 v, R5 }: U5 ~. _; n4 ^' r$ H! ~

* Q+ n, g4 { k. [; Q0 s             exit('{"error":1,"message":"Error FILE"}');6 a3 }, {8 x: u W

/ W% u4 K. T: K' y, n

0 I6 L- |: m. l7 O9 ?2 k& |+ E         } 5 Z# z8 I) U/ W$ K

! L! ]* \5 T+ P% _. Y( N2 i

1 O: C( n# y9 r' k2 i4 x. z2 o& X         require DT_ROOT.'/include/upload.class.php';7 ]8 e* @+ J* E

- ?. G1 s/ J6 O- q& K: s

, ~5 a+ ?3 ^* W  ; V& e( q$ r+ A

: t: S0 a9 O( _: q2 B

+ O& \4 R' N; J         $ext = file_ext($_FILES['file']['name']);0 N: m F- e6 ?2 b

; Q% a9 R# K1 H, C3 e

+ S" h( S' D/ L" k; C: y" Q. l         $name = 'avatar'.$_userid.'.'.$ext; . j& U& n) l; A/ X/ ?0 p# v

7 P( ]( ~* S6 I# w

; O r" K7 x% B2 l2 S         $file = DT_ROOT.'/file/temp/'.$name;1 h/ ^/ n8 F- X' C; I$ y

% L {# K+ j0 Z* ?- x& t* Q! g1 h

8 \( k* q) Z, m% h  3 g+ T" R1 j6 @2 v8 n6 ~( a

: J' t9 R" c! F4 k

) Y; f( ]3 @( \" t: ^" }         if(is_file($file)) file_del($file);: ?' b: |9 [; ?' n7 i& q

! Z- C& c! |- S! J

/ ]5 N$ t$ V" H1 [( ?. ^         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');7 q+ O5 N; Y" E2 d' m) t- s3 M

: [7 [. R- J& E: ]# ^7 u4 D

* z8 Q) Y) W$ d, m( i, |   + c. u( W2 @6 b

6 F/ Q9 j1 Z N2 \( I6 S

$ Y8 L7 a/ | {$ p: C4 [         $upload->adduserid = false;2 N( a' ~. O# g$ Y

4 b; `0 m( j( N4 L9 @" ~3 X

) w& g9 W4 v9 t   V1 D: U! D# q: @+ S% m7 z

# A; _1 C% g( Q; P

" V, o! M" c3 w+ X* N$ g, z( b         if($upload->save()) { ! @, a0 t" a4 w$ I6 D

* y# ?: C1 V$ `6 s3 V$ H

, s7 R* `, L r. f             ... % h6 ]) i R5 ^" ~( u6 H+ I, D; D* d

& Q% r6 A. ` X j! Z

8 O6 d( {7 @) H4 G         } else {( T. K N, e7 K

6 w4 V( I( k6 w7 h: x: N/ B

3 C9 c: }6 M1 k* G             ...0 B% ^1 F2 O- e2 Q" W

6 z# h7 [# _3 L) a

+ D! j' I/ g' C. w; f) C         } 9 i4 ]# W8 b0 A$ u' \

6 n$ M$ q, P' Y1 n. q% w& c( f

* q% [- v. T3 U3 X ?" @     break;! t7 H+ J- z7 m5 g0 O' s8 ^2 ]

2 E1 D& @: b( C: w# |7 N/ l

/ |$ |9 e1 ?- @. g7 \ H& {/ Y0 \1 z 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 # t5 ?( ?5 ~6 v* ~

# Y$ l, m8 l& w' u x0 ], _ j

6 r) T3 B% r) ?( |$ C9 |6 { upload对象构造函数如下,include/upload.class.php:25: # U; C# n' v- L

3 @0 F& }3 [1 o3 e+ i( p6 q

( U* g) A/ N, C+ o2 b <?phpclass upload { ' H( o) _9 V" e3 y* a) O

. x V0 o* b: i3 o" W

; h4 W" K* C& f     function __construct($_file, $savepath, $savename = '', $fileformat = '') {4 K9 q, b- {+ U1 ?9 K0 Q2 ] X$ D

& {( B/ l5 O' O% g9 r. k

1 T6 `; z) a; x! D4 t; R8 R9 r X# |         global $DT, $_userid; ( ]; U, {; l2 b# x# O) o' J

/ }5 P, |! f o; B/ E* y

$ N0 I. C( w$ z A1 k1 N         foreach($_file as $file) { }" N, T8 K; D* o: u# u

; p+ g# E Q. s/ G

! U4 B- X0 @8 h" u( j/ i, l             $this->file = $file['tmp_name']; & _ y7 X+ D; M

& s0 S0 P% b' y0 F# C1 w

9 [. u# P. I3 I* j             $this->file_name = $file['name'];- j5 p: Z: i8 T7 T# ~' ~- D

5 l" Q3 e2 j" N5 T' {0 g; b) g5 X

# N& J* ~) O D) I             $this->file_size = $file['size'];4 C& E# \! c( K! v7 e! F

# J# B9 P- o+ z3 y

1 c. P, E W6 ~4 S- H             $this->file_type = $file['type']; ' }- F O9 _2 Z$ t0 ^

- R; s4 e+ b" i% p

- Q' E# S7 [/ {; B- q. ?             $this->file_error = $file['error'];% F* h& u3 |" o' e& r; \4 C

- k0 {4 Z5 W1 ]/ e) G6 E

8 W1 G# P6 z. L% _$ G* _' @7 N  & H# N1 h3 x" _0 h; s/ F1 k

/ r' } V5 M4 G& o: \; b! T( c

0 a- o0 M; x" M$ v         } : P& h& F+ C% W+ A2 {( l

# ~) p, \. D+ u, H$ t' w8 ~8 i

" ?7 m( P6 E/ u g         $this->userid = $_userid; & i4 s' |7 j* }% G1 Z6 b- ?

. K+ _& w3 L& w7 M- H* P

+ W0 C5 {& z1 x$ f         $this->ext = file_ext($this->file_name);& _- p' G0 g# _- |

4 d, d3 l0 D1 Y$ }" d+ R

5 K2 }! x8 s+ {' C- d         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; . a/ R7 _% ` }( ^6 _ {. K

/ n' D. p/ p" m: O' k

3 \! X* Z. N" F" K( x         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; + a4 u; I2 b: ]/ F$ t

# b) ^2 R0 |8 j4 I; V+ M* p

; W% Z, x/ M& U0 L0 [4 |         $this->savepath = $savepath; ( M8 o$ l' }2 S0 J j s1 n0 X: L

. e3 n6 @1 f. m! h- b

, I: I K5 t$ n8 p: v& W3 s         $this->savename = $savename; " d, Q1 ~) A" [6 l( I% h

8 [ s! D6 I! c& R/ n

) e4 J/ r# ~6 o     }} + G5 P" q3 g8 a$ t4 M" z: m8 O

! P2 M7 ]' I* n7 I2 G

1 y6 n- }/ Z. G# S; V) u* `- w: e 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 " p/ r0 [+ a, ?8 y

4 C' {: ]1 M+ N% V

! r1 U! B* V) `8 K: r 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 ; d/ H# X1 }3 R: w- t( i

. Z: U- A2 o. z+ d& y# z

0 E E) r e: S' \. y8 S# p' `. S+ c $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.php1 s: _" i( u4 w4 x

3 \* P7 i \1 Q/ u" ^( Z) o4 U

5 o4 K& [: }2 Q) _ 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:' C+ D& }' e% D/ K5 S& R( i+ o

" Q3 _5 ~- o n6 g! O0 e9 S

A, [& f/ b# G  , \4 ~. x2 B& e m3 Z3 j

9 o' ~7 e- [* D8 j" t6 G* K: _9 X4 `' O+ c

- t: P. @. {# i7 x- o- ]4 p9 w 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: 0 ^- a. Q# p7 l2 h9 |/ L o

/ F+ ]: b3 w' n' W$ K# a' C

" g2 O2 E- r! \5 P <?phpclass upload { $ q5 @* G K, ~7 f0 D

; {, q# R$ i% }! [' q% o, f8 p) B

: Z- D& C; c6 D& U2 x! X     function save() { " v! ^; C: w% v* K1 I" j# y

. s' j0 U# w y% ^1 p+ C0 a3 Q

, m% I% ] g* `6 p+ [1 X         include load('include.lang');3 h) l7 p# _; m* S

* ~3 o+ k. C- j; z- [5 h: u5 V

9 }( A1 c. k7 Y         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');3 x3 f; A9 R( L' x; l: @+ m, k

& I. O/ A3 x- Q% c* }

- I6 V9 J) e0 _: R9 W: R0 G, g  ; l" t4 W. D1 N

7 _& F; T5 y" t6 A x0 f

7 x6 i! f& V% _ W6 w( d         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); , Q& h7 } R) W! E& T7 A% h

! n5 g. E- y4 s# Q: {

: \6 g; C, |% U/ Q: h   # D b6 b* j$ T+ w5 b, v8 O3 T

$ R: o- e" X8 X u

' x4 v4 K) @9 {( [" ^' `3 Q         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); 5 R7 j4 B8 b( E* V& m

6 a7 V- S. `% K7 \

4 t/ d/ U" P! g5 L7 r  4 A& X, G" q& }9 \( Q

. ^1 k' |9 S# b1 t

1 D- E9 m) K+ _0 M7 d; `         $this->set_savepath($this->savepath); - w, [' K9 a5 q+ P# C

8 W7 k, l* I! [. `" X

, B+ p' q( V- q% S/ V! e' N         $this->set_savename($this->savename); # W+ n! e% t0 {& \

! Q4 _' f6 v5 f# j0 W' y8 X

7 w6 m% v8 h% [% B) p' X7 Y  ) ^2 v2 ^2 U9 ~7 W, B% m# [! ~

' _ o+ y) {" E

# O. }$ O3 I5 u: |* [         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);) M; O6 i$ B+ w5 g/ h6 l

6 \' V+ \2 Y& ?

, V2 m/ D: r* Z! |" u. P( L         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); $ }1 b: V( H3 h; j

3 e& E" A1 T9 q; o

( k# w8 d' L! |1 s; X& i" K) |         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); / F8 P! i7 `, Y7 l/ F& N2 v

5 U2 V% d3 ?; {$ ~8 s# ?& ~7 q

4 B7 f6 b; [( F8 P# r* q  8 \" K6 n& Q' L* H

4 N4 V: l0 j, _2 [

) q* z9 @1 x/ ~- \: @7 C         $this->image = $this->is_image();$ I7 C. Z9 n' v9 t9 X, R+ Q1 l

0 X8 Q6 S8 V7 E" n. a+ M. N+ u

9 X; e' u6 V: G+ c6 X- H1 O         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);. j5 o6 P6 R% E! I

- T3 R( t2 Y( h7 P8 K1 p8 Z

3 O) ^/ e! m: ~' N* D9 z         return true;, Z; Z' f% _ @ h) u

5 p7 h5 @! g7 v! n

3 [1 l& n2 O4 O2 K; ?% Z" I     }}3 F5 G* ^& f6 w& y$ b

" K) c5 T3 R. r: Z) g0 G

8 ^9 m7 W. l$ @. T. o# @: q: o 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:8 S2 t+ g$ J$ s6 ~' m

3 R; y6 i9 ]7 ~) e

; }8 E% k9 j, ^6 \5 k <?php * t* \- P, x* i6 A C/ L& B8 r

* J9 }" f6 U" S p% h

0 Z( k! L5 _# z% N; `6 R     function is_allow() {! X3 w0 D" U8 O

3 p1 e, u! D. w: n0 A4 Z2 ]

' g; W% [4 ^3 M. y9 }8 E( k1 K         if(!$this->fileformat) return false;, Q' r/ \5 T* x

: S5 O" v* D5 y9 E

3 I; E% b; c8 b4 V         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; / O$ g1 ?# N3 I h4 I9 t1 H

) ?5 }6 X& w5 r

7 x/ b3 ~' S8 o3 y2 J         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;+ r0 R( `2 V8 k2 i+ X; Q

% t5 R H. S' |3 y& j, }' |

, P, Y: m% V/ b5 s' o7 G6 G         return true;# }2 | l5 P x+ l# F

/ k, L7 j9 ~* T

6 y2 U* Q; k9 ?2 L+ |, q- a4 I     } 4 `2 F4 M/ ?: N/ y) ?6 e& N" I

. D& c8 z; q' E3 {; L

5 X! O' V* g4 f- f' r( R" M 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 6 A. Z& |# L/ \& p

c( D1 I+ N2 c" l) f

: O6 J2 F; I$ G0 [' w4 i' P1 j1 n 接着会进行真正的保存。通过$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文件。& B1 B5 t$ g7 \# e& c% c; }

8 |: ~2 @( Y+ h, `: S- ~

' J- m) K+ U3 l7 b: g 漏洞利用: g* J# G) O* Y* m) B

& K) i+ ^. f0 H6 ~ C3 Z3 } c1 D

6 w; j) y1 j6 X 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。3 |) k* |: _: q, N; }

9 u4 ^3 O+ I6 u1 w% @

# _6 {. [7 R- u- ~, ^! k   1 G7 b5 q- |, F1 {" C* Z) K

/ M- g4 } c8 T5 ?

& A7 {; k" E \) i, c* g 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid 2 L: C' _2 i( G: A. U+ {

6 ~! I) c" N5 H$ J* B

. N$ |* ?) Y5 o 不过实际利用上会有一定的限制。- f5 d; n2 v- o) }

5 D" R/ y* t8 k# S7 u

" L7 o7 y1 A3 d- u' F 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。0 G6 a& ]7 e- F1 C: w( c! }: C1 R

8 a9 F& j- A' s- e; M9 Y- U, F+ \

' A, s. Y+ y/ W& ?  6 v6 X( v! U2 w/ X* X

3 u' i, ~) r7 \. q0 H7 E4 C

0 I( M6 l# y$ @7 e' P" _7 Z 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: 9 `3 L3 N# z, G0 ^

( T+ s6 x! D S) [3 `! S

1 j1 e# e0 q: ]* O 省略...$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]);省略... 2 C5 }" A* [4 E

7 I7 A! N/ `2 E8 b/ q) P

1 ~3 |8 P: Y% A5 E 因此要利用成功就需要条件竞争了。, b$ {: J- @9 d, h7 D6 T4 e, G

; K: ]6 i! I. b5 `# k8 `9 P

: [+ k1 b! f: v1 x 补丁分析 7 H+ Z* L& b9 D) O! y

7 `; Y2 f$ h6 J6 B* E' p

# T7 @. o, K) T0 H- d# }4 t   0 R. |; |; `$ ]' Q

) s- U2 E( f& I- K1 F0 E

. p+ i) Y, Q6 s9 [, w& _9 J4 r) ? 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:# F4 K3 T ? Y& Q

8 Y+ j& v. J3 [5 G4 w0 ^/ [* n- w

2 U2 P) h$ g4 g5 S! T& m function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} 8 c5 Q7 {1 r* a& u; ]+ {4 e

( t# M1 z% x* ?. d- s+ s

2 r- c% d1 y. p( p& v0 W: m) x. C   " ^! x- |3 S* B$ h. I

: T; S7 h% C' j' D5 e* Z8 {& d4 S

( Z# f) I% W) i7 f" f3 B 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 % ^ E9 i5 Z3 V/ n" D* K

' X; g/ i I" z6 B6 C% l9 [! y

5 O }+ W' c2 n9 _* i& o% q 在is_allow()中增加对$this->savename的二次检查。 2 @) C4 s- A/ r Q, |# V0 D

) ?6 @- i- {' B! {( p9 U$ K

9 X4 F6 K: i$ c# r! w9 K 最后7 L$ ~9 N( U* U$ }3 d D

( C* x) }& u" o0 B5 V# S

& _1 x( U+ {0 i1 l( o; V 嘛,祝各位大师傅中秋快乐!# z$ a/ O2 o3 m3 S8 f% ]2 Y

+ L1 R1 `; I' H4 e& I

/ ]- ^; g3 M/ g   L. `9 b* ~& e& D

: j: r2 j' ]- k2 t0 S
回复

使用道具 举报

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

本版积分规则

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