中国网络渗透测试联盟

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

作者: admin    时间: 2018-10-20 20:13
标题: Destoon cms前台getwebshell
i' H L5 @$ X# ]# E, K, [, d, f% A

, |; k% a {: j: |5 y; g8 k9 G

! q! o. I; \: F

% f$ x, \$ v: p2 q) Q 前言' c8 W3 c" B" \% B6 y8 Z! x

. I3 i. m% z N a: v; h

+ ~* E' Z; g% l( v4 f, Q8 j 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 & W* w. s+ o3 ?. v

/ X2 S- v) l3 v; I) B' k, N

9 o3 u" j2 ?+ Q' t   / L+ f2 C9 S& {" A, w4 L$ ^4 j5 a9 o3 e

; G! |4 b+ ~6 z( l

" Y/ M; P- v; ~1 J) f0 r+ f 漏洞分析. d8 j0 b( p5 r

& c. M! L$ Z3 z2 J% V

. W9 N3 ?" o. z1 t3 } F2 U9 M0 B' _ 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: & `" B& t @' [

- B( T7 G; |0 O

( M% V1 @1 @; s3 G/ R" X  6 }5 L2 B g: e& A6 x) `6 L

& b* P. I9 P' q' A$ F. J3 E

& u% T. M2 s7 v 对应着avatar.inc.php代码如下:. ?3 _' ]8 c* T: {, |

! i( N K) e5 E% _7 ~7 N u2 z2 W

! p* i7 I4 ]6 P. u U; H <?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) { . j1 C1 W% {' {3 g+ G

& U! _* ^* X7 n& U" w

. @+ b+ V j. Q7 F     case 'upload': " @5 G9 [ n' v. e/ W

6 @# B( j5 o4 v- A$ L

& D* ~1 c2 F" v2 C8 O         if(!$_FILES['file']['size']) { * b# ]% U. M9 n; e$ K: P

7 {9 s" y& w! H) A5 N$ j5 S; {) g( V

4 `* I2 F8 W0 y/ |             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);. `0 B. L+ |3 Q! T! e

' l: G6 C* n- B0 ?1 C

7 F& N5 S' }/ G) z, t* E5 \( E% s% T             exit('{"error":1,"message":"Error FILE"}'); . F1 J* i1 Z6 p, q) q1 |8 f, i

4 P, D7 d, |) Z+ \2 w0 u- J

+ U8 a( ?3 K z" i; H k% w         }3 d7 v- L1 |( O( z1 M/ L

) J' C" a k1 x" `: w" L+ y7 h

' c4 N4 E( V9 ?# E2 e         require DT_ROOT.'/include/upload.class.php';2 Z4 a3 e [/ M* f1 ?. `( A

3 ]' l2 b& k2 e6 @$ G3 b f

, C2 l: E6 k) K- Y6 b  ' [ B. B3 R' F# [4 m. n; ]3 y; i

% \% _: r# u r$ z. _

0 ~( f, c, o+ `* D- s         $ext = file_ext($_FILES['file']['name']); # C5 O4 ^1 T" y8 D. H$ T3 B: _( B/ n, L

' E7 _+ M: u0 J

2 l& `% O5 I, N% C         $name = 'avatar'.$_userid.'.'.$ext; 3 Y: o$ d. s( ]

( c$ F1 D/ _. r r

# L$ e( p7 ^4 j% \: p' S7 i         $file = DT_ROOT.'/file/temp/'.$name;) Z& s! u9 c7 C

/ j* X) x# D! X8 ^1 @

/ e4 l7 O6 w4 z0 U! T# y   ]7 Y) P% h' _: }3 N

4 a/ Z% X' m! D# }+ F% p

/ a# c- u' G1 P8 t6 g9 O         if(is_file($file)) file_del($file); 8 B" }2 P# Z! L' G3 b7 q

8 a' ?7 C4 p/ _4 y9 Z% w

- O. X* q$ k9 X1 v) t         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');4 U2 R/ P3 _4 J5 q T$ [1 _

- o; t( h2 C/ I/ h' s7 X

) Z+ |4 i5 l/ q( E# b T3 v   i* r. D- V2 o* `6 S9 ^# R1 Q

% S# ]9 h7 W! p1 M- Y. t

- T7 K( ?! Y7 T x         $upload->adduserid = false;6 c4 @% S7 ]. ?& {/ L0 C! N1 N

! A' w% @3 c3 b2 }

# U! x; F$ h. j) o  8 i' N! q5 c* n

1 G' ~0 X/ f S, r3 i3 e

7 v N/ h; Z5 q, Z. Y5 A& Z         if($upload->save()) {3 r! k$ o8 x6 g) j [5 N

/ G- k3 G3 b9 [+ ?5 m; x. }1 s

1 ~ ]% O' F* C8 }             ... ) _6 r! ?1 \/ F- q+ g

5 d+ ]( K+ o! K- u y: y7 o7 Y9 P1 h

4 f" H6 P" M& J! Y: r/ s& @         } else { ; r: @ b- g/ }: G+ X* e

8 Z+ T$ _7 a: h9 @

; L, Z' S) x2 o             ... 5 \6 [. Y1 L# N5 b& d

' \) B) U) u) L/ Z

1 a2 a4 T" D: w( N. ?         }" e0 k! g( t9 D4 Z. v, Q

. f- r8 U8 y# \. H( s

0 k% `3 r/ r" b7 c     break; + t5 \( `. m6 D

. P4 m1 N8 u3 ~( @# `! ~9 D

$ F1 k- h( @7 ^ q, A9 z 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。; c" p5 U' v$ j& |3 e* B) a

, B" K3 _; k5 N' w' J1 i1 a* Q

. {8 h/ w8 r/ W1 ? upload对象构造函数如下,include/upload.class.php:25: N+ s, M( Y! F& R7 v( H

5 C4 g8 t+ V# k u+ I

" ^1 Q( l& Q1 `( A4 ?/ q- @* v* [ <?phpclass upload {5 Q9 g, l+ K4 Z4 D1 y! A/ ? ?5 V! W0 O

' v1 A' B, |) k. T: w+ Z

0 s* i3 X; N! z1 g1 m) ~9 t     function __construct($_file, $savepath, $savename = '', $fileformat = '') {8 N, K9 }8 }7 X

a2 {( W% G9 B0 P' C' |

1 P2 H0 D6 d& K6 q1 O) x$ h         global $DT, $_userid;5 v: D! l5 d, b2 V3 c6 J

' L# l6 ^3 ?+ s) U/ r

* S6 Z9 z" {$ Q/ j N3 p         foreach($_file as $file) { * o8 {0 b9 ]/ x. p3 R5 h0 C z" {

, ^4 A3 _, \4 j* I

- m2 z' Y, i. z) ^7 Q             $this->file = $file['tmp_name'];0 k5 h4 t' ?3 ]3 \' C. R& h; H M$ j

- E: F8 t9 o g/ I

) F& b9 q! j' Z& _ U0 K             $this->file_name = $file['name']; 1 O1 E! M' z/ W* `

6 K/ Q% q* F" n: d' ?

8 Q1 q3 m* t7 K: W( X7 M             $this->file_size = $file['size']; 2 K6 A! h3 C2 E0 J

8 f: j E u& u/ t

) P' W) X% E$ U6 E; ~2 ]             $this->file_type = $file['type'];' @0 `5 i0 U+ ]' @9 D. C+ b+ J, g

" |( z1 q5 q& K) b

8 t' o& c+ O' U" M. @* o/ a! Q             $this->file_error = $file['error'];9 F5 M4 m9 I7 E

" j9 n7 l/ }' V3 I" s! q

# R1 W2 S1 R7 K- Y! P  7 c! L3 t9 b( b

- q2 t' L1 P' S# u' }% e( p' l. ~, V

5 o3 U& \$ H- r, ^; y0 Y         } # y5 c6 ?) l& S0 f1 r

7 W6 Q5 d5 F& R+ Y5 m

& i8 m5 @7 U' _- D         $this->userid = $_userid; 1 c3 i3 s. _* C: z

2 {* O. c6 C7 v; Y

' @, ^" V/ H. z; `4 m6 C. f         $this->ext = file_ext($this->file_name);5 o& s+ ~9 h- p& _3 f# @

; Z# q* z& l3 M% X" _' f; S

: s0 p$ {& R L, u) I4 x         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; 6 H% o0 y4 u6 P9 ~& d# s

# w1 _0 w+ z! }1 Y, T4 e4 j

; X( U6 }% d% u, w' z         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; % U# x6 T- j. ^7 [0 m: ^

1 E5 B, V( e6 S0 r- H

1 a, n# h5 C% f* b         $this->savepath = $savepath;& g( @2 [1 E6 W4 A

7 {* E# b: m$ I, b; L

- X* M) E# a1 @         $this->savename = $savename;. i& o$ z8 d9 b" A" ]

3 H* ]4 h7 l# y4 x8 ?4 Z* \6 q

# A( U% _/ X2 t1 D8 O: ]" U! A     }}4 s$ U2 v1 A6 z0 {# u

/ R# M. r9 k0 {1 h

/ E$ w+ S: M; ^4 E' N- n4 j( @) h 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。9 K! W0 Q& B) g. b

1 C% V8 B& t7 i/ Z

E2 ~6 p2 N' R9 ^ 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 ( e+ ~9 W' e5 @2 J

3 h* a9 N' N. Y7 V4 E+ u1 a- U0 {

1 @! c: b- U4 O$ C& }8 e $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.php0 I3 Q5 {$ l4 f: g( p# M

4 ~+ _0 b5 t( Y8 j v& o- \

% P' U6 p1 C' h1 k M0 l$ L" O2 { 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:; F8 W3 ]/ A! A! S. ?! p

# g/ w+ G+ W( X0 O, ~

0 Q# d1 o. a3 |3 Q' E1 w3 B   ( ^# q2 o9 r: @: _% m- ?3 a& j9 D$ p

/ s" ]4 }# y3 |3 N$ P" C! m* g

8 g( m8 g' c1 e# N2 t 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:* O+ j; a! a1 k, z

" ~5 ]- M6 t: t8 N- O

! z8 y; C8 I# A! M$ f+ Z3 w2 I& N <?phpclass upload {- V1 A" n6 T7 G7 g9 R' J$ b

0 N1 k/ O1 ^ f' j8 X+ Z

2 W! [7 S; Y4 s0 g     function save() { ( y: m7 C' _3 k; d5 S) N

t0 ^4 l: C7 H# m/ J

) ^# D3 X5 I/ \) F) Y         include load('include.lang'); , ~) a+ _8 I$ Q1 n* H2 c, p

8 t! Y# Z( o. j- M3 E7 V& n/ e$ c

& e& I4 u! _' K4 X' M; O9 ?         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');& D& Q9 X5 \3 [

" d3 ]" |; E7 O) [7 |

2 S0 |& I9 h1 X   0 ]0 O/ D2 I( Z

. k7 d' j' `' S" u! s6 G

! z+ x/ m% b6 t. M0 N         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');; Q0 T. F, n& @9 i8 I" f

7 c2 H+ q' U9 }3 c7 ?

0 |( W* e* I$ p* q8 {  ) u4 z& _, C: i3 _7 [

: H8 R+ P* q" X7 {4 \

) L4 L H9 ^3 v8 P" a4 H" K         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); : T6 a+ N2 ~9 e: [$ R% T

8 u( _7 ]/ {+ Z$ o/ V0 r- Q5 w

# R G4 `! L! n, k: }   , V# a# T9 U/ E) ^$ e

0 w& I$ j+ D a/ f7 B$ S4 m

4 d7 x" x) r# Y1 T& y         $this->set_savepath($this->savepath); % ^- I* t o. q1 K: a

& N" U& m5 A2 {0 x4 J+ s6 i( X* P) G

( N" Z1 ^6 g4 R# Y         $this->set_savename($this->savename); $ P5 U2 r( l. R* T+ w( `

6 G6 Q, {. E- \% j

' G" G8 @* k5 r. ] Y J  : u' @* Z( q; G, J4 J! t! f# j& R

7 c6 N6 n3 ?+ g! d- z9 M6 r K

! M2 I6 L, E0 u. f         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); , n$ e# Z$ Z4 T6 c

5 L# b+ }$ F W* m& \8 E+ H. L. s3 x

$ }" r0 p5 l- g7 ], c         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); % N/ p' R- }7 d) U: Q$ ]

4 K+ b. t6 B; ?- U- |3 g. u2 E

: r M, j( H# e9 l( k         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); , T/ ]! e0 A# E) e9 C) J

% s; w1 c3 N5 V2 m: t7 q

% C. Y l+ T/ K( n   8 p% r+ x9 `, ], _

6 |4 Z- d1 B- A l1 i

6 i4 Q1 I# W, F2 N. p, R         $this->image = $this->is_image();9 C2 y% X- R1 t: D

- Y$ C, R, R) }1 n1 g

% R) n8 T% Z+ K# w, ~! R         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);. [+ j& \( E: q- j( f4 M

! V9 v9 D' r) H8 z$ y5 _

* K) L. Z6 h. s! G& z% [         return true;/ h7 Q) M& K. d- q2 Y

3 ~. N& h# d0 F' |" S9 o% t4 B$ y

& S) Q. c: c1 x4 O1 V* O6 B     }} + x2 P# C, P& n9 l) j2 S

) [- i& r- y0 S% t" c% H' `2 c/ t

+ ]* [) f4 f5 a# k& n 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:3 s. i4 C( G: f. `

9 ]2 Z' H; {1 ?' ^# E

1 @$ o k3 L7 E6 Q8 r. A6 m <?php 7 J, V' x( i" }) M9 x9 z! I. }0 {- q

5 k. `6 a m0 e8 V O6 j

1 o- \/ j M$ t) d     function is_allow() { 8 h, @! W, z6 P' {( }$ m

+ {2 F5 b" f/ i! F" [

7 N0 V; Y3 `0 r4 l/ d* y$ c. J$ n! @         if(!$this->fileformat) return false;/ y+ _% U0 u4 ]8 }1 R7 x. w

# v2 |2 }' R/ f: }% c f C/ Q' X

+ t" b. a/ l' _9 |         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;& S+ U. k6 l7 a. U

) |& ]1 S( D8 W+ w( a9 a

9 L9 n& q8 E5 v% U- u+ c* T4 P5 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;, ~) _. |8 j6 F5 G9 ~! S* a

( q; I' b5 S* X1 ?. M0 _

0 j; S% ~7 ^+ O: A1 ]. N         return true;7 ]+ X) Y; [+ j3 p7 Z

. z6 }$ Q; K2 ^" C

4 L& e7 z" F2 ]. o4 a     } 4 T S+ j* e$ \; c A

. x9 f% O- q( X: j' P1 P1 D

7 E" i* y3 e; N9 { 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 1 f7 m; {8 z+ k3 }

- x4 a4 ^2 T3 p! n/ l3 u1 x g7 D

8 }8 v0 Z/ r6 k3 M 接着会进行真正的保存。通过$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文件。 3 r, u4 J5 z/ F& ~+ s

0 \/ F+ q: Z( A% h1 r

# K! h7 u8 B6 w* B 漏洞利用 4 l4 M4 h9 `" S& w

! N2 d: G% h4 F8 R ` X

. m$ k9 P0 M! e% } 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。5 L5 W0 y2 ~$ _4 Q9 D! a% t7 i* w2 s% ?

! a: v# C3 a4 Q6 y2 T

) H% m- e4 v9 K# U   + k( f. g* U! W% \/ e% g

R6 b/ m+ Q" K- i

1 z' r }" M/ ?' J 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid 3 q+ A, K% |0 N& B5 U

/ M# i6 S0 |. X$ K3 L

- z$ Y; b, T+ x' W! r 不过实际利用上会有一定的限制。 , h0 r# {1 Q: d% u9 W# s

2 K* s% G5 L% N2 J( p

# i: ^1 H; @. N 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。+ Y* n+ H C" x; [

`: ]& O# o7 M0 H2 o+ E: X6 G

/ e& U* K5 `* Z1 `# X8 M! @2 i0 b  ' z8 h' r- v6 u

$ @! b8 k! u0 B

\9 S9 ?! R' p2 W$ x, _3 B/ f 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: / _ Y1 z2 V ~1 }0 Y% D9 i; K

% m" H+ `% O7 l

) Q8 P& m! v, u7 u" N 省略...$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]);省略... * ]8 E+ F- B- }6 ]# u' _

( N0 ~& B& f! Q" @$ e. C) V

; Q" w# s5 P% ^: A6 \4 o 因此要利用成功就需要条件竞争了。' B5 _4 v& W: n% J {

7 `% ?" Y- t0 G5 n% x$ c; F

! C! Z! y% ~5 }) p* g C1 [+ Z 补丁分析 ^8 h. K$ `6 x* @

2 h/ ]+ a5 D) v( c0 ~8 @& t. I: G

( k9 X9 J5 y' u$ u  " D( `% J8 K( g4 W9 h

3 k" r6 D: i5 t: T. D4 q4 l! ~( n

; t+ [9 D) s ^" k# C S* s 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:3 I' C5 I/ k8 _4 [7 L1 i9 [

; y+ Q/ [, E% T# x/ w3 t8 D W2 A

" B6 d4 N L) e/ [- e, g0 Z function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} 4 Y; D+ T! K i% k' T

( E, K0 Z b* j) X' n" q

. Y% ~$ l; E7 S' Y' X& q1 y   8 T( r: s9 `+ o5 m

& {: a! {6 J$ ]/ h( m/ p1 U

' n+ I9 u5 n) |/ k 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。! i# l' K- Z9 p7 c- f+ d; L1 [

w! B+ ~8 k/ }

8 f0 t" M( P" ?1 T Z% s 在is_allow()中增加对$this->savename的二次检查。1 S" s! H% t5 _$ d7 g: {

/ _8 a$ d( Y) C$ D- R9 W+ {

' l4 D2 ~- f8 y. ?/ U/ g2 m 最后 2 H3 Y6 V" {$ ~) F4 T5 Y& w/ G

, w- W) f. Y" f" i0 @) l

/ C8 m% Q7 G* E5 ~5 W 嘛,祝各位大师傅中秋快乐!# x: \6 G0 E! j n& W! f

$ J" B+ L7 u& d

6 H% y; P3 ]( ]7 H# L x   C2 x. e h$ ^, ^6 o& f0 D

% a" P2 z+ e. g3 ~





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