中国网络渗透测试联盟

标题: Destoon cms前台getwebshell [打印本页]

作者: admin    时间: 2018-10-20 20:13
标题: Destoon cms前台getwebshell
- B: z; V( |* J7 o. \# f- D- P x; X

1 _( `! h/ B; k; L4 e! l

8 ^2 `; l j) p" T) j

4 S3 Q2 E8 f5 _% [ 前言* B/ u6 @1 N% k! R! o# w( M

) S& p; q' Q4 \" A+ R: h/ T) \

& k/ B8 W, H: d. X2 O 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。% \" U' L; m! a) ?) X* B8 ]6 ^

" f" o' x# T- z+ J' c

2 W8 U2 p' S, w) J: @- W   6 L! F6 @) p! Q$ o: U

0 j! j' M5 s- z" L9 Z" P+ W$ s

' ^8 P/ i3 k, M- h4 Y% I 漏洞分析# ?$ y8 `9 w; a6 b7 Q& N

3 {" J/ a3 `) G c& e) o+ ]

5 r, d8 v8 c; o, W 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: & \+ z# q' |: p1 _; i

/ t6 X2 y+ z% g4 j( H$ i

! y" ] p1 r' W* M2 ^. Y6 p/ v7 q  7 D5 x1 g3 C. N1 A$ @% _! E u& l

7 k5 [# `8 d7 X' i

$ z+ e1 w) Z0 ?: r" ? 对应着avatar.inc.php代码如下:2 j% P: D2 l7 H' J* n

" @' T5 V, y9 A- y) B

+ p* Q& h* w$ v- Z3 o$ j8 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) {5 d% e% a5 P; L: y' l

' Y" f7 ]4 d5 B$ I1 s) c

2 P$ E: h# j, W! P/ U% i0 A     case 'upload': 7 F, z# K5 F e& z. n% _

# o$ d' v% A8 |, j C! W0 l

! h& u* ~3 {- T         if(!$_FILES['file']['size']) { - p4 S. |/ a; Q% \! f1 J

# n9 {" \, k. c0 J

, Y1 r, R: p7 @# D- K0 A' A             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);. ^ K! } G8 S' d

3 N& ^5 d% z( m: N% m

/ M4 B2 Z$ ?5 A% S             exit('{"error":1,"message":"Error FILE"}'); ) L" a: x! G2 Z6 b n7 K) D1 s

. j w: {& J1 \! W* |4 Y

" x z6 L9 v5 {) L* D6 W& k         } a* r3 R7 g" D( ?% G" @2 x

, E9 [: f$ `5 j, W$ d3 p h0 g

% a' D1 z( _6 ?; O; _) q         require DT_ROOT.'/include/upload.class.php';# C% A: b2 G" s/ x P" o& i# Q. p

2 V, k/ L7 P$ l+ ^

y+ s* F' J6 L/ m" ~  5 m0 G* W2 i6 C d- @+ F2 o

/ v( S* }5 h: p/ S' j4 l, X1 _

2 o/ g! H/ H. M) q: Q3 v1 t         $ext = file_ext($_FILES['file']['name']);2 p, G6 r/ q7 ^6 ~; Z

8 i! p& D) T( s+ t4 s- T" z6 [

. c e- o5 I5 M0 W         $name = 'avatar'.$_userid.'.'.$ext;. x# w9 |) v3 ^& U& f6 \2 ~

+ h0 U" @9 p& k

0 A$ \7 x* r3 o* _1 f         $file = DT_ROOT.'/file/temp/'.$name;; {0 I q/ Z" f" T

$ f. [ h* { Y

& j% p" f! U3 z  & }' y. x3 z, H- {+ ?6 s6 c

. ~! W0 r# [: U! D! X/ A9 D& L% M& h

0 A3 t1 I/ l' P( e, j         if(is_file($file)) file_del($file);" ?: g% p& [: _& j. x/ @

3 F& E' ~2 x3 m1 |; f# _' E* O

0 ^% h( ?( ?4 q6 X, L0 s3 d         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); X5 J' t# d* P. ~) M- Q

5 F& R& J/ h- S) p0 _3 F3 V* }$ p

+ q1 w: G- w% e5 Q   X! F* _/ C: ^- m4 ?

- v0 Q$ [8 T1 I" m& H

9 D# a8 T, O. g         $upload->adduserid = false; 5 d8 L' Q- y3 W k

: J7 A, }% c7 I9 N/ l

+ x) X7 b# h4 |( C   % v5 Z9 M# x' u, I3 J8 X

/ ~' Y4 j5 [8 b3 b

: f) Z2 m3 S0 d         if($upload->save()) {3 Q) ]% r. n' l4 P. A3 W0 h

; |" D0 G7 u/ s4 g; F5 I! v% ^

4 r% V( l$ y$ a- H% W             ... / W: }8 g; E% p7 r

% r# y8 w t3 z; a4 H+ \ F" Q

' ^- r! L2 g* l* `$ i9 M: w         } else { 0 u5 n" c' C: g: _

1 }5 L! f @* q V

2 f* r0 _/ f! D) B, ?             ... + j$ C. l& C0 h% v i: j) ^- w& B

3 ]4 q" u3 M" F$ y& n1 O Y% Y7 z

+ f, M! U0 `/ a, k9 Y+ w9 X         } / Q+ J1 p' D& C( b o5 Q

; {* e8 T/ W, f: z2 J" l2 y) H

: q _1 o0 w2 R! k     break; " H; j. k% k9 Y' A

* B) U' u( ?$ R/ `$ n9 x# q

9 l% k v' t2 p0 h: ]: f7 ^ 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 * {* l) [9 Y0 ^! K

. j' }8 j! B$ N6 y4 P5 f

3 C+ `& N* T" r4 w upload对象构造函数如下,include/upload.class.php:25: 2 I; W0 N4 b7 e6 B* a% S

3 k' [) Q# X% }+ z( t3 O( |5 M, [

4 ]) T! l; g; c$ h7 M! v$ s# { <?phpclass upload { & o8 e, I# i8 L9 e# Y' m. S

* h0 g$ t9 P# S7 K0 b

' m/ e: W: R! k+ s; q     function __construct($_file, $savepath, $savename = '', $fileformat = '') {! f9 o; x3 P& |: _* S+ Y2 Y

, H4 Q9 \2 ?) U% i' E

' ^. j8 ?. B, d5 }. M         global $DT, $_userid;' ~7 e! |5 V2 y% V

. |8 \' a% `0 {( g& f$ K+ R

# D5 x5 q# E% M, ?) L3 v         foreach($_file as $file) { S) j# S4 z$ z% ]" n8 {" n

. r; y5 u, b4 p+ Z! R/ x

' T9 \* T5 u: L             $this->file = $file['tmp_name'];5 H$ z5 |4 H$ ]' Y# Z% N# C6 v3 n

0 @7 \: x- x3 z1 H. V2 \

: H" D0 {% [8 t- z! B             $this->file_name = $file['name']; 6 N) _% _! q4 g

2 v) d3 O1 p! A" P, U

' M0 \0 u9 p1 @; O: v- \; G) ?             $this->file_size = $file['size']; # E! A, h( o G* T2 e0 q

* V% D- \5 z% Y

. _ u0 K% m& f. e             $this->file_type = $file['type'];% ?( F% q0 I4 P: Z8 x( N1 D0 x: ?3 n

% t3 F5 l2 r* [

- x& `. ~! r/ ^) r" b+ S, _             $this->file_error = $file['error']; 6 |7 Q; F6 z- w* b k4 R8 ^9 `( o4 Y6 Z

2 \! ? w B; d7 {3 _$ p

+ e- U/ u8 ^: a7 f6 @' k7 U1 Z D/ g  3 Q6 c' x# S# a1 ~

3 U% Z. a) `- T

+ k5 \, g! U/ ^( D% F+ o         } , v7 p/ [8 t3 r6 m

/ l. @, c7 W# ^: z, u

Y9 b0 w J" [4 m         $this->userid = $_userid; , k) x( I e7 N( D3 b4 \; M* Y

) A2 O/ |0 h7 y- c

" {( [0 [4 {$ t7 ]1 t1 m( R% n         $this->ext = file_ext($this->file_name);3 W& N* p0 ]* J$ v1 v

e: y) }. u" ^( D: u) F

1 p# C/ z* |- }* V( r' W         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; ; R/ }; R/ g( c7 g: V! z& F. A" `

3 A/ g7 Y4 ]% B4 p

6 v& U* s' A% \$ l" B         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;! E) i4 L& G- f9 D( i

0 T# d* w) I: Q8 i! A# W+ T0 Q

4 _! l2 K! ]( D7 s, A         $this->savepath = $savepath; ' H& J& s+ y7 g& U

& I. b; r# p @" }0 B

' k, f8 v5 C" g9 w- L7 b" N k# N         $this->savename = $savename;7 I& Q; B2 R5 g) c- p

/ [ j+ a* e0 s$ ^+ y; ]

8 A# I9 t% G/ `# O' w     }} B$ {3 C: U" x7 ^( D) |( G

! f) j9 w+ ]. |7 a3 Y" T

7 c. g$ m w$ C8 _5 j7 Q 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。) ?% o: U* z1 N M$ ~# J9 q' A9 Q

1 r6 D' Y. B" `! y6 E. q

5 u5 I6 `& c0 {1 g 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 + z3 `7 P( u. z+ w/ F: P

+ l: i9 F. F5 H, Z* R0 a

& ?- [1 t% \2 r+ z7 h: Z $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 1 r) `5 h4 Z" C

2 B8 x; H3 l' W2 q9 e( o: l6 Y- y" c

9 L; f; T* c" s1 |$ a7 t 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: & y' [2 i3 [1 c) x4 z

7 q3 |+ a$ {8 _& X% J; \3 r" k

; I' J, J$ w, S- r7 Q& E; J% S   , g, k; {7 F5 w" N

% c% c/ p) L7 [- i1 m; z% P& S) Q& `( K

# n( P- ]0 ]+ w" O# R! N% X 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:( }6 s: e2 Y4 v# Y+ l

6 ?$ i/ `# w Q5 V

( g H2 F6 V& k* {* N7 n7 G <?phpclass upload { 6 _+ h1 Z- Z. v" V- s$ t7 F

* w* [/ Y+ P* q6 ^* u) M, ~4 Q( O

; d, \ z% z; Z1 j' z4 k     function save() {' b$ \% p& h0 z/ S+ b. D

" m9 ^; A9 m4 t/ n. Y

8 ` ]. q: P% z         include load('include.lang');- {3 s5 d$ B$ }& T1 A

3 J1 G3 d& k' Y

( w) \" I B% {. y, g         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); - S; `% R Z1 t' o& t0 _

% }6 H: N/ }8 |& F! G% i+ ~$ ?

: P0 |$ |; M9 e# O+ t   ) P; O% P" d+ U4 g4 y+ q8 Y& S

7 X. b+ c8 x: b

+ \' O e; p% l4 O4 Q         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); ) m. k( Z3 q, r$ t+ S8 P+ F: g2 ^

! Y' ~) d1 P6 w7 r+ }0 i3 R+ `0 }) B

% E ]2 m" P* n5 m8 v   8 b5 K- D5 ~- `8 Y5 Z- X( m1 O

% T/ B8 a4 M% } W' J* u

) G& _" d) o) j m3 j @6 s. ]         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);2 O. Z2 f8 ^- Z# E7 ?

. v) B% e1 C3 d% R6 Z* e

% f$ z, I) [8 f6 X( i/ M  # F! T8 u8 c' h( D* j* m9 r# ~; }

5 y: R0 x7 S5 t: x, f' ~6 a

! D( g7 p# w$ s) Z3 M7 P         $this->set_savepath($this->savepath);" h! S% k: ~6 [: e7 Y

% s7 ^4 R; R- R

0 m* f" t* Z9 ?" j8 r0 b         $this->set_savename($this->savename); 5 O! `! i8 q9 W# \

& p- S$ ]! E2 j! T

9 H$ a: V( k/ y! l V% T8 X" V, g  $ M: s& q4 w+ ~

' A+ A; A5 m% I, |. |$ O P# D5 ^2 E

2 N/ ^$ Z; E8 `2 g7 G. d" o K         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); - V g# K3 v$ t0 g

. X d8 f! O: j9 G# H

9 p, E. |: q: u4 k         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); 9 S; ]2 C8 s4 J0 O0 {

; s& |* l* @; D. @* d3 H# J

: f1 F( e4 T" o8 [: f         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); 5 g) |$ L7 F' R. @7 }) T

$ I0 Y2 I* E5 H: P- k( \0 ]4 C

( \: E& [- F6 r X$ L0 p  0 b0 T6 z5 r. U$ k0 g

" j$ k2 G6 \$ f, K( ]1 k) K

* o+ H- l" \6 ]) V         $this->image = $this->is_image(); - p: R; h, E1 |6 a

: Q, R# v6 X7 w; |/ x

) m5 K1 |+ b) z9 {         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);& G2 r, p1 v9 x5 {* P+ P

+ w, C0 m& |2 q

, w5 t/ a- }9 m5 }9 b1 S1 ^7 h         return true;2 c( l, q8 P9 _# @5 J0 x. e

" p1 x5 ]# y1 |" H

: _ [# X0 _% N- ~+ ]& p% ~     }} * f j5 y8 g- f) o3 ]+ ]7 R

9 Q6 E! S, t2 n' f4 L

, g# F( I6 [/ V( j4 W 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: 5 Y5 ]! Q; v4 s; s" ?/ z, r& A

3 K8 Y$ R0 y2 M/ B8 Q8 a" W

+ c9 p" V( b! t& n( m- I2 ?; m+ M" ^ <?php+ y t) R7 N! x$ b& b

. G" w3 e6 @$ r& P8 ]' w

8 ~% C k4 q2 y1 z2 e     function is_allow() {: M. u# F1 J9 k, V2 s8 n3 E2 o/ l

& F) W4 o/ w! d

; O) b4 r/ I6 _) I( p0 x         if(!$this->fileformat) return false; t6 y; z0 N; t) ?! J. A0 }

3 m) G1 Y. _ Q p2 Y" f

8 K8 [2 e C o' i! `         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;3 H t# W: Z) D& S2 M

, V, V/ d# \2 c0 \- \

1 G1 p! S. |2 e- V4 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 y3 T3 D9 S* f

; y3 g( D- O0 }3 e

% N( {4 R3 j* i I         return true;4 {: ^7 D7 l( s* I" j1 g+ D8 A

# A) i/ K6 d8 k. V# h

3 X) v k3 O* Z     } 8 W* R, e9 I( M( q2 E* z" i

4 c5 {: L- ~1 L7 z, ^' W

7 d2 z9 m) Y1 C, Z s5 s 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 , m5 u4 u, n+ E! x6 N4 ^

8 @- ^/ M7 w" U3 L! j( |

, z5 F( [1 U& y 接着会进行真正的保存。通过$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文件。 " U$ `- E6 ]" g+ w

' _$ L2 @# E! u1 t, [6 y3 D

* U& J0 m9 r9 V. k* u$ a4 q 漏洞利用, d: I* o9 i8 f4 q( w+ V" [: M" ]

# ~- d% X, J+ ?# o! o' Z" q% y5 o

- N5 c& v/ U6 {7 q3 j7 j7 m 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 6 w# e5 o2 x! V7 K* Z" m3 V3 V

+ Z) M9 Z4 s: r/ M& |! s1 J5 ^8 p

0 O6 O/ {0 Z. i. m6 I   , G6 }: \) }! k2 y( C% n7 ?

* t: N# D6 Z4 I! B, F

" i* J7 K7 V A9 `! o 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid 0 n# d: ~+ U' p8 l% y1 A( t/ S1 N9 A

( O3 y' u! n# l/ J1 j

% P+ D- d# b( g0 H 不过实际利用上会有一定的限制。 8 r4 @' b& D* l+ N7 _8 J

, [) z! t1 O, S3 b1 Q

9 j% v0 k8 {3 G3 {! ^ 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 8 m! c1 s# a: f9 f" q" V7 |1 U4 Q/ ?

4 m( F) r7 B# @2 F1 ?

; x: p6 g4 F K& a5 {- n   2 E+ B$ a) E; K/ D% \

/ g7 y- Q) X: {; Y* T) b+ D+ ]

; M$ b" O- r0 Q. n. l: w, { 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: 0 }! C2 X, h0 B

8 Y& O1 X' ^9 Y* Y: ~3 ]- P5 e4 p

5 L4 `& W1 c' d8 q# g" 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]);省略...4 S0 |, D T4 M

7 b4 v6 g2 T3 a. m3 ]( H

5 @/ z7 X+ n& x3 e3 \ 因此要利用成功就需要条件竞争了。 \2 h- U" V1 _- F* }

, ^' x7 W+ p [. P8 e/ A5 u9 U4 k

* [6 f& Q4 r7 U0 b: m6 Y' y 补丁分析. c& p, @- r8 V: e2 @

6 [+ G1 X# L! G

. j" L* `: {' T7 q$ O  ; X( g# l/ E( G

, W( ?7 d5 A! F- @ G

# w8 G" p& D/ \; D) |: t 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: * ?, C, ? }9 [9 T( N. w5 a; d

3 d0 S s2 H: L b

/ J) z- P* A- Q! k9 I function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} ( G6 g/ L o E- w4 y# \

3 P3 a( i+ y& M) o5 c- i

2 i, a6 @8 Q6 ] a" o  , l6 d; L! l: y! n

! @4 B" Y) w. N1 F, H7 J

: f8 D% Y3 K' Q. y& w! _! T4 h 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。4 u; T+ A8 W- h' U! S% r0 h7 c

! w7 T3 ]( Q3 c4 f9 M2 ?" P

2 ~! X% c9 C: p; e 在is_allow()中增加对$this->savename的二次检查。 # j% w' D( h6 |: g$ p& p

8 K+ ~. @. p7 L! V6 ^* V. ^

2 a/ d' w2 u0 C 最后( m3 k( b/ R8 I5 B; M# y

6 P' s" i; B E8 B' K+ b

/ X3 K! k% ]1 ?3 C1 Z5 q 嘛,祝各位大师傅中秋快乐!$ x- v e& Y; H- b! y! i) z. ^

{7 d' T( j& V: @

/ @: C8 n9 y4 |0 c) `" v, H   . w0 V9 t) G# E. Y: K# S8 U7 A

) z( N0 e2 d+ x2 ~6 @7 y: y4 g





欢迎光临 中国网络渗透测试联盟 (https://cobjon.com/) Powered by Discuz! X3.2