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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
" E7 D" o2 }8 A; n

6 Q! A1 `- P6 N7 i2 W1 L0 N

8 G! Z& h# o: [" C: y

- ?! a) N* K. V 前言 " ~8 p' a: y% `' G1 k3 O4 H+ [

" b4 `( ]! Q7 Q& u

1 o5 T, B! g2 O3 w# q5 A 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。+ |3 C s, ? w7 M4 {- e

' ~* @. {; G$ n; Y- q2 ~

" f' @* c9 ~" @. ~9 {5 J/ x   . x: S+ ~) [8 [6 x

) `; C7 U4 r- W, E

, y9 M" z6 r2 l$ Q' b 漏洞分析 6 y3 S( u" Y/ h1 c% r5 S- Z

& R, e4 [$ |1 |+ H0 r

' P( b1 {, \' ]1 G6 y1 Z 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: ( l3 Y8 i5 {8 M* M& W

' v5 g: J) q) [/ O3 P2 q3 c

: t6 m1 w7 D C) F7 [+ s% d  % n" Q; u7 b' Z" u) S. c

- J, v! ~. u; ?6 v+ s

" u9 `% {$ R1 K* I; U% o 对应着avatar.inc.php代码如下:0 W) |* Z5 _, L

- Z& ~& x3 B# q/ `* q' ?

4 r1 q& P+ y2 D' A0 D/ L <?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) {) u1 u: m# k, p5 D% u3 }$ S

4 w8 J' m; F3 \. \

. T& M6 a( A& Z; ` ~     case 'upload': : ^ ^' a. }& h9 M4 j

- K0 C4 e1 |" E' Y8 q& O

, o) c1 T! {: u6 s4 i/ \6 E         if(!$_FILES['file']['size']) {. R' [. ^5 U6 R: C t% `

4 I2 E" K- G! P" k4 C Z5 g( s

5 }- D# E" n: v; w$ j+ ^# M9 p             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); ! y6 R( x! K: l5 Q3 q2 V# R

! c- N$ Q& O8 W1 \2 G+ t; A; c$ P" ~3 y

: B* x5 Z. C2 v, Z" y9 b" `: v             exit('{"error":1,"message":"Error FILE"}');! m1 H7 b1 R5 e5 V- S

4 W. e! t3 B. S; R8 y% }* p4 v4 e

9 |% v% w! W |8 L) {# s. f8 `         } 5 P/ ^! J* z( t8 g/ M$ Q7 }: H% v

' }& {& P8 U+ P* R* z3 G" a

: d! l C$ _ S% X3 s5 H         require DT_ROOT.'/include/upload.class.php'; U/ U& F3 N; h0 |( g

$ m% R3 L- D- P/ |6 Z* E1 b( y; x

7 }' O9 i# | q7 s4 g0 C; O   " w# h- [7 m" P& h

# Z6 \7 W; N5 V4 @7 a9 J7 T

( ~% g# L# g5 [3 j0 ~0 p7 q/ l* O) f         $ext = file_ext($_FILES['file']['name']);/ E( b J4 m, b8 P* ?3 b$ m

2 J+ i5 R! S+ N( z5 F) P! U

4 G& ?2 G$ w9 @) b/ B         $name = 'avatar'.$_userid.'.'.$ext; 1 {7 M& _7 r, K' i6 ]& F

8 D% d R8 d7 l

! @. z) T+ ~; p6 S9 Z# O* Z% S         $file = DT_ROOT.'/file/temp/'.$name; 9 i2 v6 m! F5 I9 G' {& G

* k/ i6 Y" K% {# g9 B. D4 b% f' ~- F! X

, N0 R6 P0 N; }5 P/ l   ! b# z$ C- _- u9 H- o

/ e1 q+ l5 P9 F2 o! _) }! e

w' U0 ?- R0 u4 V         if(is_file($file)) file_del($file); 7 Z% ?$ F; d) \" e/ \

: y1 R* [0 w2 |4 u$ p

) k6 R" D' o3 i% H         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); Q3 t8 k8 p6 ^2 p f

" r! J$ t4 E0 O3 r! y) o9 u

4 O* ]1 A: K U9 B9 i  * \/ J0 I4 p M3 I. g

6 S' L9 W* ~) i3 e u A4 d5 P* H

& e/ E( Z, e& e         $upload->adduserid = false; 9 G% t# E* z9 b ^; i& T$ d" U. j$ p

& B" S, o4 D" M/ }9 w

3 k4 m" S( P) W8 |8 ^3 s   1 S! g Z( O1 ?# l6 o- q0 f

$ U* b9 C$ d _0 `6 m# o

/ D/ B; S8 }$ R% I6 p         if($upload->save()) {. E2 }& h( x$ V3 i- F9 w% ~

) T% L# p) A% p/ \

1 m1 }' f, X H6 }" q9 k             ... # z: U; t' G+ R e% R F

* c! a, M7 ?) Q! G( a

. C& _$ E* f/ K- @6 c         } else {! d, L% r9 G1 ]! P9 D# Y. G3 _

^ @7 r8 F! {2 f3 s. B8 A

% {' P$ O5 f5 z- Y" j# Q             ... / ~/ f4 j. k G x1 w

) l) b# b0 x' K( ]% G

0 R2 H1 Z" Y/ c: \2 d, T         } ; l, d7 v+ d9 v( A: J, s6 B9 B

& Z8 K* [4 d- i1 U; A

( x% A! Q' N" ~# B1 x" m; @     break; / p0 W0 \4 I4 ] w; s

9 S: B1 l; `. b4 `

& V. G# W$ T2 Q: } 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 5 m4 R* x2 u3 N) R* {2 N$ f

* z3 m% [# `- n* `7 R

6 J7 z+ A/ o) \- r0 r5 S. I upload对象构造函数如下,include/upload.class.php:25:5 K ]: N- ~7 i I' d

6 X, A- P* a( r' v" h; R

7 N& w& l! L" {9 \ <?phpclass upload { ' o- d8 |* \$ E5 x S* [; V3 H

9 P M" P% u8 f- b" A

& m3 C0 F7 y/ e3 a& M# c. h     function __construct($_file, $savepath, $savename = '', $fileformat = '') { 3 Y v" z5 r* O% m8 s

8 p: Z6 Q9 f7 U# B/ l% S

" [. T) O3 h0 ^1 r+ m4 p         global $DT, $_userid; + c7 w, {$ v% z$ `: g2 H2 @

9 r# s9 |% g$ U: {* c/ q

9 f D) P' e4 f$ b. {         foreach($_file as $file) { % l& u* u T; i6 B

$ N( S1 {% Q1 b6 \' O, M

' d7 w2 I, {6 v2 o. c             $this->file = $file['tmp_name']; ! z0 v ?& _0 j3 v; C4 F, ?

) \7 U' I4 m M; M+ k0 w' i

4 ~- O2 P# y7 S$ |8 Y+ I: X             $this->file_name = $file['name'];% O* Q8 @5 r9 ?3 |" z _, A

) a9 P* V9 ^! c

7 S* o9 a* l1 R- {/ P             $this->file_size = $file['size'];& v( ~2 `' T% ?

; W! P2 ^& v" ~' v+ V% H

5 {; k. Y8 P4 ^6 y             $this->file_type = $file['type'];/ v' Q& O. V4 b, N; ?. ^

' l8 k0 x4 P* L& [" t n

% a) @1 Z+ B; p. y K! H8 X- E             $this->file_error = $file['error']; + O ^0 R+ ~! U+ z8 \$ g; E8 {) [

" \& e: F/ [: J# \! `6 T

) j! M7 H0 X( F3 Q- ^% @   : X6 B9 @ y/ ~+ O$ y8 f6 V

1 c$ w) H3 s+ n2 I3 R

2 L* U2 E& Z- c: h         }$ o" r( g! d2 B- D" }: Y. z$ {

& |: H! Z4 D& v2 i3 g) V

! L$ V7 g& k D# ^* D8 ~8 D         $this->userid = $_userid; 4 ^: } L& w1 g0 x, r

; l9 D0 @/ f+ o# ~9 C! ^' ~; g

* `- P' n- y+ z         $this->ext = file_ext($this->file_name); U. u2 P* P0 G \

& O1 k6 q8 y% s# @ h$ ~

5 n# @7 L7 z$ T3 e8 B' B9 J/ N ~5 g# F         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; ' x5 w+ w; V0 G4 F' p( u7 A

R& p1 u) v% q) f5 t% d. Q

$ H! Y2 E$ L4 Q6 s, s; H$ i         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;' P& _( ~' j1 e9 V7 z

% ^0 @: Q4 n1 m6 R. ]

) ]! y2 Q! j0 a4 {+ z% n: ?: y! o7 P         $this->savepath = $savepath; % `1 h' @; g# b+ l

9 Q" f4 j& t( C7 t" k7 `

* ]; P9 C$ m+ H8 s, e4 p, v         $this->savename = $savename; 6 I* n' q. p4 E9 |/ K; e

* ^) ^ Q* I+ D* Z( V2 Y U

/ q0 [4 Q% h. |- B4 I1 `' ~     }} 3 s, j7 Z# z% s, B) R, F. m: h

8 E2 ^6 M, i1 A3 M

2 \7 M' r9 v/ A5 p 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 - c/ E) e& W7 B7 x3 v& P- N

8 k9 V9 y+ H% v! Q; g! T# z" w

+ A, k l" _0 ?8 y# @ 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 3 t) c: g4 j4 j4 u0 h

1 e2 m. U& d) }9 p# S4 z

, w/ a9 Y+ E+ s0 Z' D $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 & b% \, a4 r$ |3 F5 r

1 k1 e* T# Q/ t) w3 a

3 A, p, b: Y' O7 h 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:2 J- V: I+ H4 D; V, ~

/ n6 E3 _* Z% g* `

% T l9 j6 w% c  ! Y0 A/ I( z$ [

, S, `% @- Y" p$ |+ Q

% j. d" y5 x: Q: Q% A& P' e& j- o0 r 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:7 ]8 y, ]+ }6 V& b! o

) l+ ]5 q8 S% F- G" p* F

( u4 S$ P, J8 e4 J <?phpclass upload {) u+ x( t; J# t( C% u

! u/ X' r, P8 \- k5 V+ I

6 c0 @6 G, `: l8 k7 V     function save() {" L4 I# @* R2 _

3 A" V4 V& g Y$ P

6 F* D% o7 j- K& L: O+ Y' K         include load('include.lang'); ( N% W+ M/ ^" G, c

- e+ V/ f3 L3 [: ]4 U

) K s* }* I) P         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');) o7 o+ Y$ q3 P& H

( i4 p! k& |6 v3 P( x1 S2 R; x

) n9 `7 m9 C$ V0 n$ z" [: \& s6 {   4 _3 O; ?) |3 a J. ^

7 V+ { L6 Q" j# u( j4 k. V

( }; ?, F0 _7 o7 q         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); 5 p; R0 C# `/ O( A' n

1 A) z0 x4 A' m4 ^8 m1 A

; n! Y: |" A [- l( n  9 ^& v4 i8 x& q7 n- K

- H8 W' U7 f, R+ M/ D

' H9 t" r% }( {5 l2 e" G         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);3 e+ E" J+ n/ u1 u- X( l

- l, X7 p& m, _, c$ e6 m# X/ j

6 }! @( H8 Y* K" s8 P  3 ^( Q) b' S2 O

! D& N7 o! L, q6 S; h

& z2 B% e. A9 g2 P         $this->set_savepath($this->savepath); , Q' k0 Y) S0 k: l( ^) L" S2 C" p

( u" d) V6 ], U& P! g$ U

0 e3 x# d& r f/ x# A         $this->set_savename($this->savename);! n1 r# F# r" ~' B3 Q9 A5 Y

8 K% e" u, g2 L s

2 D! O+ n! \& }+ W" P9 E y6 ^   c" |4 B; d$ x

. M2 L( S* g9 q

+ @4 ]4 h; p5 Z         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); / p" V f5 Q5 ] L* H

+ N% C5 p, ^# U$ O9 _0 |+ \

! v/ b' o- [0 r* ]; A7 T         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);6 Z/ F5 G) U/ e ?

* @0 [3 r& m8 `, x" j9 _

4 j5 H6 m/ \, `* f) p. i0 E         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);' L7 O X8 f: S% r: [

, u2 D- L, s# U4 y2 a3 h( b6 D1 G

' `7 G- o S+ U7 ~4 b; B   7 r$ K: I! c* p4 E

7 y1 h+ O! u u5 Z4 Z! A+ |. O

& c) k$ f/ J* v T2 N         $this->image = $this->is_image(); q% ^ n5 C4 Y9 ]9 a

! y9 s+ l2 d, K4 I/ Z* X4 c

! G- q8 Y8 l; M' w2 g) H3 Y U% P( A% W         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); ! ?8 r! @7 t; k1 Y. T2 W

( o ^: [ o9 _4 P; ^* c7 F

7 |2 I4 W: ~0 d6 r         return true;, h {: ^3 q, U, g7 W4 q! E

& ^& ]$ Z' M7 o* [7 J

* x9 H( n7 E' U! m/ w+ u     }} ; ` }. H# J. K3 {- ?3 A

3 c1 _, _. s2 X& Q* l j

. H: Z6 r2 y7 i* g! K( v- r7 U 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: * P8 Q- h5 m% U. S* U0 R/ U

* `) |# L8 Y6 f6 W* i2 |" Q: ~

: f" |9 _/ c2 w, ? `$ w9 u <?php0 [0 i/ `% W- E" N X& F$ N! u$ j

- d2 [, G1 L% m; s, u

' _7 Z" R; H0 {- E/ o- C8 A     function is_allow() { , K3 |" f: m2 a8 z4 M" ?0 N

) p# K" p! x" ?6 q

: e8 o5 w3 v* |: x$ q I% U2 I9 K         if(!$this->fileformat) return false;% |- A1 q. J$ Q+ H& Q1 j6 r

$ p% o: X" \ }1 X. j% B, R1 s& {

/ v. t8 u+ ?% r. U0 R) Y         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; Q+ T" t% P( U: p% U

9 f, W6 l7 x8 E2 s8 G

, u1 w% V! B% H0 I) g( |         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;3 R6 Q1 r D% K5 r L7 `" r

7 J9 z/ Q. Q8 U$ t

1 ]4 s, q- `) r5 J2 A+ }# S6 m         return true; - U, J3 A m+ C# _

. h1 ?# T0 m3 ~5 \+ q0 z# z

. e% x& a. o, J! C8 r' w3 l     }% h& G7 S; h5 q5 U

3 M6 j+ @- x+ E

/ ]" I. p+ Z5 a$ m& z, d 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。2 W; G/ ?" B4 Z" v. _

" e4 x3 G$ Q! g0 k3 s4 G

$ i4 T$ r4 H8 f( X1 e3 l' X 接着会进行真正的保存。通过$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文件。 * T5 @6 F5 f& I

' Y3 H- C0 l r7 i

; {2 o: g, t! t+ x 漏洞利用" b6 ] j. U- w) u

$ f6 l9 i- N, Y% \% g; H

& c; J0 ]' s! X$ ?- y 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 . [& b ]$ z: W1 B1 s5 n

# U6 d# A: m( f& J5 e

, ?: ~3 L) I! a4 j9 o: H9 L6 ^   3 r8 Q7 o- a7 v5 S" l* d8 O1 @1 a

7 P) i' j6 c1 @/ D2 |: b* {

& R) E: v; m( Z" R' X 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid+ Z% a! r/ M4 d7 C

: ]6 Z* {; F( r+ w" s' o- O/ P+ o7 Q) l

) W, n- B: ?. m+ ~: ]$ h 不过实际利用上会有一定的限制。 $ F* e7 ^6 H2 z# x4 L! ~

$ N' F4 \) U9 h& P) V' y, H9 ?* U# B

, W9 O; m1 b2 ] 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。7 r: w9 e4 Q; k3 F$ e# g

: R8 P9 H! A, W+ {1 l! j

. g! A& H: j( }   9 I! m' q M3 Q$ Z: u2 {

# l4 S2 c* U g9 z

( I& P& A/ e# W6 M6 { 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:( [+ \/ G7 b3 }8 c

/ o2 u9 E, G/ I, v+ r

; L0 t, a7 y5 Z; H' N2 a. a" o7 D 省略...$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]);省略...& u* H M. D/ U+ s3 P

q* @9 N# A3 a" l$ D0 h7 k3 ^4 [; T

$ y) {* o6 t" W 因此要利用成功就需要条件竞争了。 , J) u& ]- {. T8 ^$ d+ [/ q+ N

5 d1 w `/ E. [* g z1 E9 i" h4 E

" @& n/ O3 i U- L2 b C7 P 补丁分析! t- i$ Q; e, {

9 O" \# Q% H' K0 \( j. p

, B0 @( R3 u9 A" \   * e6 O6 R9 h. G% p

3 Y4 k* f8 M; l: X

! ~! V7 @5 H. z& v [1 x! F! W/ l 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: 7 l4 G' E3 ~7 [4 d+ ~

7 u! x/ x7 v& }# c. {

5 S9 K' J1 Y! p! ?) Y) [; k function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}2 ^: @6 \5 O, m, {( A) ]! z+ D

0 J1 B$ o; S& {0 n3 T' O

7 s* v/ d/ m$ H   0 N [$ b8 L5 h2 J5 A

* e4 s7 i- N/ w. s( y

$ z* I( O8 X2 ?* e! F# s3 B 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 8 c: B1 R2 J/ o7 \4 _* S7 `7 o* x( r

: b( \7 A( C' m4 o' k& Y

- f) d$ o9 X- ] 在is_allow()中增加对$this->savename的二次检查。2 `( e' Y% S' A

) {3 w* l2 W) V8 a! w

3 H4 `/ E; S& {: P/ P, l# z8 Z 最后# d+ H% I- F9 B5 f1 }& A( q

6 Z5 S2 t6 x2 [/ u

( A4 g: x9 ?- B) N5 l1 @5 @ 嘛,祝各位大师傅中秋快乐! 7 Y/ k. Y9 ?7 ]0 }

! F3 [; j# Q8 X5 s

4 a+ q1 V8 H- G \& w2 |4 X  ' t2 j6 S/ U( Z% l. i6 ]% V

* g8 E ]- M3 W* O
回复

使用道具 举报

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

本版积分规则

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