中国网络渗透测试联盟

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

作者: admin    时间: 2018-10-20 20:13
标题: Destoon cms前台getwebshell
) v# L$ w( U4 E. s, H! {, v: Q

( Q/ W( [; Z% _

& Z1 B! L2 P; i7 X

; `+ ]3 o4 D2 V ? 前言 ! j. _; O; |; j/ p! W% G

, e# ^$ G- a/ W( D" y

- \! G: H9 O. ` 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。! v5 D% D& z0 b! u! b. I+ p

7 y- p, u9 b, F$ }7 i' D t5 o: E

. u3 `3 g) ^7 n1 R4 ?  ; ]& {$ p- ]6 E4 H: \1 { J

5 n, z3 _0 W Q3 k* y* v% M

8 j2 m/ _: L6 p7 L3 X 漏洞分析# P* N. W2 d8 j

7 g8 P8 {) p+ d

5 y: N! f6 t2 f R# i5 {9 B: K* h$ M 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:; D/ I4 K- B5 U5 V9 w

; L- N0 g7 Q/ U) x% k+ M' L

3 O7 o7 M6 |% S   6 J% J5 ^( M9 F2 W$ b6 h

$ _5 |0 r; [$ s/ h! o- v- b3 q2 Z* Z

3 K/ }% `1 A( m! S7 q 对应着avatar.inc.php代码如下:' m& z1 E" _6 j! ~$ W* U

1 O$ _ {2 g1 U2 ]% f! J2 G% ]

& z8 Q# ~5 S/ z# 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) { & u [+ S, {, V O

! T* c) K' t* F4 O. M! ^. W& h

+ b, l, q: f, F+ F d0 ~, |     case 'upload':5 D4 ?9 V7 W. i. I

1 e3 C3 {0 W3 W$ i5 Q$ r7 Z

2 W; ?3 i0 J" [ V2 A2 f         if(!$_FILES['file']['size']) { 0 L- o! h4 r/ {* q# i

8 h; N* n9 ]4 c: E2 E+ s0 P

j7 H0 @0 y M: B6 D; H$ b- Y             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);) q8 ^( ~ d6 O' E( }/ G; H

2 K/ x/ ]% G% ]) h

) ?1 `' i. q# y1 M             exit('{"error":1,"message":"Error FILE"}');% c- g$ r! A' c

1 u+ T B& u4 K+ w

3 O( a" X3 K9 k: N) L# r5 \; Y# h         } 4 o3 j- U9 [& H+ J: D4 Q( ~

: W9 e/ V3 L% ~) S

6 i" J7 A) U" A, G         require DT_ROOT.'/include/upload.class.php'; . t) ~9 F0 V- N; K

. l; e0 N! F- [

0 ?, o2 S" h# `5 U- |0 ~- D   6 p& j. Q s' V. ]

" N* o0 B! @$ ?+ R

' C7 i S; N" k& \9 q         $ext = file_ext($_FILES['file']['name']); . f( j. P! @, ?9 ^7 j/ Y2 ~- h

* {: j. `6 h5 q! _; O+ M& f

. V C& |0 s+ P         $name = 'avatar'.$_userid.'.'.$ext; 3 T( u% |; G$ G, x

' q; O2 F9 v4 P0 d

9 s0 m+ m/ w4 \6 s         $file = DT_ROOT.'/file/temp/'.$name;! A/ _/ n" b3 r: H/ R/ y

2 S/ s, @* K/ d( F, g* a9 S$ j( Q

& V6 N, H* D- U  6 @3 M) `) y. i9 l3 t# [

& M4 Q+ b# @, E+ _% l- t

+ V3 G3 q6 m; Y |3 V         if(is_file($file)) file_del($file);7 L G$ b( o- G) p& H6 [* a

* H0 P# i' _; a( C. g# @6 c$ k

/ v# g! R' }! Q/ u7 I& j! E. G         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); # s0 b4 k, p, \

2 f: W/ N. j/ b* l. ], j) n& H$ A

; ~6 Y. @! p, G* F; [   8 }, |+ }8 b: A2 s0 E& |! o

6 b( j4 }! S Y. [( @1 Y

/ h7 l9 Q/ I2 X+ w E         $upload->adduserid = false;( x) a* Y. F; i, c" w( V B

" k$ b9 L0 c5 P+ b

0 L) g2 p; B/ |5 n, T   " B* p& @( L8 a7 Y

7 V ]# X: D6 R Y, ?4 E) p( \

9 ^: {/ H8 q; X, Z7 T         if($upload->save()) { - F3 M9 [! ] r5 R7 D6 w" m( q% \. V

( J" v x r5 o& Y, o8 B! V

- q- v! L% w% ~: a i             ...8 ?: e5 P; V! p n

/ J1 F: A; M9 L+ R5 R

( h6 N5 z, \/ ]; ^" A# e2 k& z         } else {# X) s8 G& a$ `) i+ U. [

j% K7 R2 N; U& A" @8 V0 F

& Y8 N, V; X. G" {$ J             ..." P; p: j# l% {. ~- p+ H

3 ~+ n2 f1 Z* R) [8 f2 K

6 q: S- {! s. e9 ]0 A6 U w# H* q% O J         } $ m( \, i* J7 Q' o Q

1 V0 Z5 m# i, {7 L

2 X0 X) W3 j6 c: N% p/ f     break; X/ y3 w8 ?6 |$ ~: c# [# C

8 Q0 O5 y. v( R& O* q$ c0 b" F

8 `" d; Q, x) E- ^: z/ _9 ^+ d 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。% j4 W/ S t1 Q6 s J

- t, a) W( G' t2 i( x& y8 D

# P$ R- b7 N9 ?1 q2 ^. } upload对象构造函数如下,include/upload.class.php:25:. K! c) c# ~/ w7 f; i: U" k

" [# ]9 _! E5 L! A" g6 D

$ [) q8 L/ q$ L( A" y <?phpclass upload {# O/ Z2 `2 e- s

5 e3 p4 y8 g' F4 A

, S1 \: D1 h9 x, n     function __construct($_file, $savepath, $savename = '', $fileformat = '') {& I6 l4 J6 H9 |/ Z+ a- O

0 R5 _7 J, F% z) Q+ H' b' ^ q

" _7 G5 K8 M4 o2 ~         global $DT, $_userid; r9 ^- P" Z9 `2 S

% i& h, r: D# p( `- s R

; Q, ^' E4 Q! j% y; r" @" a8 }         foreach($_file as $file) { 2 f0 ?/ c8 h% {

" d$ n. J T4 r) {+ c7 O

% J. z/ ?1 i8 F             $this->file = $file['tmp_name']; 4 ]% K/ H$ L6 H1 _0 [" d# E

$ N! b0 @. U4 U

: C; m/ ]' z4 M# u' E. p             $this->file_name = $file['name'];# y. k; Y2 s- ]' E" ~! n

4 r; _" z$ g3 K/ M. q$ `: L0 t

) A+ V# Z2 F6 ?             $this->file_size = $file['size'];- V" B" v, G& J1 x; k0 u

8 d2 v0 _+ m; i6 B( D0 U$ f O

# d2 F" l3 \6 E, o+ N4 G, g             $this->file_type = $file['type'];+ f7 c0 c' n* J( i, y! C- l/ V* M

2 k3 i0 J9 g/ o! F& s6 A4 D u

, a" E# S, f( j! ^0 r- @! H             $this->file_error = $file['error']; % C" o6 e: c7 N' j B! G9 P

; M: X) P3 R, V" ]

5 Y+ t" E$ I& g, z4 Q0 b   + t; s$ L* c* l" \

- B% {, ?* N1 P: W3 O0 P1 g$ {# N1 W

3 C4 C' C% L* K9 J         } % A3 i, @( D$ p" G1 {

+ }1 @* c: |6 P# i F. l g" e S

9 v3 n4 ^" j" ^0 ]$ x b3 g         $this->userid = $_userid; / U8 W1 {: w2 j/ Y5 F

) M! w3 Y0 X# h l( J3 l$ P

! u6 x E# [' U) U3 x% m         $this->ext = file_ext($this->file_name);3 ]# V$ ] f' R/ m; H; D* R

- X: U1 m) N( T. Z3 y* A* g

% f/ L! c' _2 ~0 w9 F7 _         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];% \+ w7 x8 i" Q2 x/ [

1 J' H4 Z- s$ _$ o. S- e

+ ?% r0 `7 q+ N) i( y1 k% T" r         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;% n j( z; m' S. G* Q

( A: c! M) Q5 j8 E" y: V3 Z/ o

6 ?2 g1 b* G% k- H         $this->savepath = $savepath; 0 D& ~9 T/ k% ]9 Z, e

8 t4 e( W1 t2 i- n1 O, a

$ E+ Y( H4 D7 a+ r         $this->savename = $savename;- t* w& o& A% ~6 X

0 V3 M" A; [( w4 @9 S- w' a+ {

1 [0 V- w3 H2 A     }} 3 m9 V8 n, k/ o7 _" R' Z

5 z8 y) p2 \7 x5 w

4 e( C' G1 W( Q. T( C: Z' k 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。& A: T( b! ~ [' b; D1 S# J

* P, D* m* g: c7 v# L; ]) N* v9 T

6 n8 r* z0 z8 k" ]$ O% k 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 6 H. @! e- @) ~, F% z$ q! E

! r6 x, U' u4 q2 `' u; V( b

0 g5 ]+ ^4 j) X# A" K `. G $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) Y% N; j7 b8 _; f# f; Q: N

. c1 A# w/ A0 t' o" D) H' S

# ^* }8 u. Q8 z8 P& j( \ 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:5 b% I) b# `7 W: f

. b2 ]5 W- K& O3 q o. j5 h

, ]6 ]2 N; b% L+ k) m7 p  ! S3 n/ M( N4 K; e

. A$ h( T* E9 f" B

( ?& z; c1 |% C2 ?( h! C 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:7 D3 d, Y3 M2 [( f

9 J: [: Z& o- H0 Y8 f) T) S+ ?

0 ]) W% Z, I: @$ D <?phpclass upload {7 u0 A5 o" M0 E+ g5 {9 f; d! r

: i! W6 o% r# E9 M

* g1 H: M$ M$ j2 {- X/ H     function save() { ! x- C; Q0 J3 B, a: C

# _- u+ r! w4 D2 R" H6 G

( S5 l+ q: P3 Y& F- c7 q+ k         include load('include.lang'); 1 h' Q) C! T- m" B* J0 l. {, {; o

' M* i8 P! \) R. c& s6 |

8 g8 h% x4 w$ H0 B9 l         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); ! B) T3 p3 W8 G1 I7 d) M

; ]% [+ T" X7 S7 G, F

: h6 f! P y- }$ k! b' a* H  6 Q! k, l0 z& Q1 j

, h. M2 A/ y& ?6 S3 R7 K

% _+ A' \# C+ t' e3 A+ b8 m         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); % @4 c2 g& a- C+ n

3 s& V# M8 S# U- O" {! Z0 \

& e' B8 `4 _; F: _$ r0 ]1 x% L& I  , w9 W( t# |& Y7 S. o1 U3 ?- E

W: V& X9 ^% B3 e. q

6 `0 ?/ y8 d8 _& x" t         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); & \' V* d# C4 _4 \1 |

6 v! p; Y, N: s7 M& H

8 u8 ^+ x# O# v/ K5 o& M; b4 [$ f  . F4 d5 U3 Y6 `

7 J# J; K/ Y- b( M& r0 I

' u6 W, ]! V5 c6 C% V2 ]/ W         $this->set_savepath($this->savepath); : ~) o. @. |7 k- G' L* J3 H

& g' o1 v0 w: A% i/ R5 O

8 D' k9 |1 p( F1 c" |9 X         $this->set_savename($this->savename); * B# ^+ n$ o+ y! i5 C& \* A: f

( F. M/ X9 D" g% F* m- V

3 P% C0 g; @1 q2 A+ l  3 h+ C) z. \; D* Q1 L: _

9 H0 Y4 B8 C$ s( Y

1 s& s! E* c; o: A6 x         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);' S7 ~/ @' G' `

2 p# G9 t- j% h

3 M4 [7 q9 l: y, P7 a         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); / @6 X C$ W! n( q

- l( K5 t3 F% D$ [- P( f8 c. R8 i* H

9 a: L6 s* ]6 c: X* Q2 X7 l6 _' g         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); 3 C0 W3 Q: @, }* J& R

2 C1 D1 X+ W, g! v: H; a% z

6 |6 V) M: n- b) V" ]4 [   ( P' k' w3 Q) N" L9 _/ j2 {! k

) G8 `# E+ j" O+ S- X: q2 v

6 c4 y7 e/ n3 ?6 O6 e e9 F2 |9 a( c         $this->image = $this->is_image(); ) n$ I' C" y0 Y5 v' k

: y8 D8 u1 J4 l: f

6 {; S* Z) T2 |0 Z e         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); / [' N: R3 P/ I4 L2 U5 Z# i

' d8 U. E0 n5 e& C ]* d

7 z; I8 t0 w; Y         return true;- |! I9 q i: q& B" H. t

* T; a4 D: s, J8 g

a" [/ }! a5 Y7 M" u     }}/ E. b( g3 I2 M4 N- P, W q5 u

& n. z1 h2 X3 u- j: K* @" {! P

2 x3 m( L+ c* f: F1 M 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:- m' h7 [8 I% V

* @5 d4 m) n5 s; k

5 D& R( c3 O9 B; f8 l4 S9 K) ?8 N$ N$ Y <?php9 G. S: K( n9 F1 A) B5 m# }

0 K* W! I- Q5 d8 F) o* _

1 C2 R1 h2 P& G/ M! `7 F     function is_allow() { 9 S4 J* X5 p0 S! p3 y* p

: E% \0 P% Q9 o! O% K" Y& j

; o0 ?1 j" c+ I7 R1 T& D: N* O) t- t! T" v         if(!$this->fileformat) return false; # c$ i) [* k$ M

' Y+ w: l% P! z; l. o/ P6 z

( C) R& Q6 ]" m8 z' Z         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;' F1 k R7 C4 b

& Y$ a( t( H: N U0 ]

4 J% d3 ?+ g# ] p7 m3 h         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 |0 L% S2 v6 v# }7 h6 ~

+ Y* [6 ^# n, H

; H0 ^4 |* ^- |         return true;) ^" Z2 N" {) k

5 { _. R+ k, _) [2 N, L

]6 T8 I# @8 T3 h, b     }" B3 n1 r5 r( e

" a4 u% E' O) D/ n5 H1 V! g! s

4 K: t" r( @' D6 ? 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 # |7 r, g% Y& b

( j# C6 r j7 _. ]5 h; `* K

9 _; v+ T" Z& x- `6 }: w 接着会进行真正的保存。通过$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 |, H5 T5 j7 @( j" A

1 n$ v- K6 M0 ]9 ]

! ^3 w# _. D5 a3 h 漏洞利用 1 Z+ o1 Z' {: Z" N ~( s

6 d8 s/ O+ u+ [" T; x. R- y

6 @6 U9 I. h3 R4 p2 T 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。6 w# `, V- S P x7 W5 l

7 B" U8 Q8 ~( C. U

1 W( P9 w; L2 y8 m   1 j6 S- x; O+ k7 H6 f2 f

. ]! Y3 p) p" M$ }7 k6 X- i

6 \% x4 T7 \* T1 `/ g5 t% b5 o 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid& H7 ?- q: ?) N1 B/ e D8 _

. [* U& T) V& F. Q8 m0 n& {# G

/ k4 _( o: O: C. s) g2 n) _: i: I 不过实际利用上会有一定的限制。3 s, p, S8 p6 l& t1 t8 H& y

2 _& G, A" D- ?' _

6 y: R1 q f- A! H( O 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 ) _% B' P2 e0 K. _# d& i* j- O

& R6 L% y$ q/ b! q, M

9 M* w2 u0 s- v! s& X: n2 ^   ; Z* r/ q0 A3 U) I; b1 a' S* g

, l, U& ^6 T! Q- f: S2 |2 G

; H( ~, ]) D( b1 |, c 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: $ k) d' u, K) E% t% J, X

^- s# g5 g% E& W; t

$ W) J" \/ P( g0 r- t: 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]);省略...7 a. {+ M C& H. P

$ F' v; e$ e* U( f

$ l) ?% a' M* b3 b$ @ 因此要利用成功就需要条件竞争了。4 j. b6 E. v+ o/ S0 b

( b& \; _& g9 _- r

" B# U V' g8 |6 Q+ a 补丁分析" ~) O; [5 F" b, ?; t& u) j

) Q2 {* n* r) f& Z' O

! T1 Q. g3 [. n1 U. J& x% H, t2 v  9 b; i. n' h1 r1 V$ G- {1 w

0 j/ C b2 g4 ]% L( w+ A% A% v

4 [% ^, k& h Z. U! j 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: ' |9 [) m& b6 u8 \; y

1 J& |; c- G0 s+ d( j; J

& j; ?( D* g" P function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}7 g6 I7 D- U" c# z" B

1 _6 }) S2 y2 Q& b

4 [3 I: D0 w/ Q1 H H6 v5 W6 @  3 _: N/ @" q3 L3 S: J

; v, B5 u8 H7 y; e

* U/ \7 b1 T1 P D7 d2 y: ? 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。! g x- _: D* B1 N; ~4 \

; J0 C0 ^1 |9 F' c4 Z

. T5 S( [/ }/ ^ Z 在is_allow()中增加对$this->savename的二次检查。 # q- v& \2 ?& C3 @* h4 i2 f: c' j' z

$ I* b, a1 E2 b3 c8 K

' N8 ^3 A! d# L; C0 D/ `( S 最后; M% [% U/ D0 g, ~3 ?

0 s& w5 p3 S7 T9 ^9 W/ ]

) D% [" Z; K% d$ B% E& K 嘛,祝各位大师傅中秋快乐! / m1 K1 S4 Y: I% n/ d3 k- l& ~# L

5 l/ r9 i2 F2 T: i4 S+ j

/ D9 G3 z: ^$ g   + b9 w: l( o6 P. o! S3 E* p8 r

7 x4 B0 ^1 ?! s/ W9 u1 R9 {( z





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