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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
" [2 J8 k, C1 C( e3 ~, U

2 ]! [( H, U5 d) T. R! A; A, N

" r& @* `& U; l$ }; h: h9 H

; D9 u2 Q m7 |. T 前言 ' T; p( L3 O- i* h2 z8 Y4 ]

4 ~& _9 S% G2 n$ N n

% ^8 \6 ?6 q2 z) p8 @, Q' ^ 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。" M8 Z, w# `; h( k0 E* z: A1 ]1 ~

+ G- a q1 ^; G. o

* q" @/ n0 O9 ]: e* \   ; U! [0 R6 w2 I3 \; s

) |* f' B6 Q6 S1 j, J1 A

% k6 Q9 z/ o: l+ o) [* Q 漏洞分析 , G) G# k+ [) v c

2 M* Q: t* R: l" j8 [" L( e0 A

' b3 j; X4 V/ ~/ K/ D( E C 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: - ]: j" H( I: q9 Y

. T T0 c* _+ C* s

4 T* E7 F: K' y4 L6 ~   # n% H3 ~) w' C% G& z

1 l$ R% [' ?! T$ y% f+ h

+ Q+ y4 x: U0 M8 ~0 w' R 对应着avatar.inc.php代码如下: ' h$ o3 Z) F* I2 |2 M

' i9 P: V9 L: r1 K4 _

/ @* N" f7 y4 S: q, D, _) I9 P <?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) { 2 N, ?' q r0 H0 j3 M J3 v

1 h; m9 m+ o! `7 W1 ], _

. Y; I8 Z o6 h; X5 j     case 'upload': 8 i4 X. P' M# c0 \

7 o) ` B! I/ @8 M' h

6 `6 y) r: V v/ b9 K( }( ~         if(!$_FILES['file']['size']) { " Y ~4 |9 U6 O; |

# r0 P+ }5 P. t- w6 C! |0 h

( b, }# V! }; D5 s0 k- M             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);) M. A! ?( |' j! t. Q, J

/ J7 _, p$ f, s* W) x! d w

4 h/ M5 O& |0 \: _) L             exit('{"error":1,"message":"Error FILE"}'); 4 Z; D* {% X4 C& X

/ G. L$ U6 V. a9 O$ H6 i0 ?, o) Z

- I" _5 L! Q' _2 }2 ^         }2 k0 w6 W ?( n; t0 }' W; a0 r

7 L1 X/ e' x, O

# l) B! Y. G _+ c% m$ `         require DT_ROOT.'/include/upload.class.php';* K9 V1 ~$ R K, w: b; y

# |+ v+ M+ }, w- c

/ P. x+ a! K/ R) r$ u   9 f& a" @3 }/ i A

) S/ o- w* m+ ^

7 a; h& I( i7 [/ Q( r- {6 b2 A         $ext = file_ext($_FILES['file']['name']);. x7 S7 d% g$ z, d) _% R

0 E' T' n; v& w

# a* I' B# n% g% _% w5 C         $name = 'avatar'.$_userid.'.'.$ext; N/ O4 f7 [. O% r! p' Z- ?/ p- H

- N: M( ?( o0 N. n. ]8 ^' C$ e! |

& ~( j4 ~! }# r' \ Y2 b& x1 [$ k         $file = DT_ROOT.'/file/temp/'.$name;; L& S2 k) A) @) w0 O. ]! ?) c& X. ~

& [% e2 g7 i: `6 r8 l- n' s

7 X9 k1 h- H4 `0 f% C3 X  : U* g$ N5 l1 C+ V* b! s

( f/ D( b# S O' @7 U6 J

+ j& Z: I) Q6 q B9 a         if(is_file($file)) file_del($file); 9 M1 u: i' @% Q% n# }+ s

+ O0 h! ^3 {8 Z5 T5 V

. z9 T: ^4 Z0 `5 ]1 V" U1 T+ D) g         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); ) h+ I8 H7 i+ @; J5 g& k

/ i7 i' f/ K5 n) U+ E) i) h8 {

; N( a$ P: } H ?' B3 ^  9 k% z( g7 ^( o8 t

) A+ i6 }- h" b2 E. u

% d; r5 D3 t) O3 I: n% k. h         $upload->adduserid = false;$ g. [ G* f) ~: X/ }

) }9 p: Y- V! J/ B

/ R, \$ m m3 o4 m+ f' R/ N   2 R( y# \4 R$ }! Z7 p% ~) v

0 c! }% w" H( K8 P3 `1 r9 ]

' o5 F2 I) ~/ U( N         if($upload->save()) { 0 {! N u, {) a3 y- U5 T- z

4 X% |; N% u& k; r

8 t K7 L( E, O' l             ...2 Y6 U1 f0 q7 B5 f' [

7 d' e( E# d1 h8 d

* r4 H3 } ?5 p4 @) y         } else {8 r4 C. X! {; {/ m/ [) y1 |

^: b8 T" H$ w

1 n$ f+ Z! @9 A7 B& L& Y# d             ...: x# @4 d( P* {* r' t; o

5 m2 y; i. E# N s; N

. T+ E- W/ W) W5 @& ^) N, Y% Z8 A! M2 v         }7 ?; p4 E8 [: P9 S4 x1 n% ^+ D$ b

, g, w5 u& L. p5 @

! B9 \: r4 L1 [     break; 1 d2 O$ v2 m! ^! x

: ]+ r0 T9 g. F# T% L+ H

& J7 b( n ?2 J' p 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 ; d1 [6 P u2 ]5 r% [ o

- L; m# b0 b: d& y; z, n

6 A) Q0 P N; W( n% u upload对象构造函数如下,include/upload.class.php:25: - t( j" M$ w8 W3 ^) |- H* I A

t- Z9 I, G: }! r1 ?3 u

$ ? ~( {! R4 J, p9 i# b <?phpclass upload {! c% m3 k" [" o# N( X( i1 ^* J

2 n4 X* @8 C. D) R5 ^

8 f. c0 s2 I: S* b3 h     function __construct($_file, $savepath, $savename = '', $fileformat = '') {- W# H' j$ S E2 B' T

' K/ d1 |, {5 s3 {" V5 x# o+ Z

4 N! o; x% S6 s) A7 l         global $DT, $_userid;: _4 Z# [& [% r4 p! S. B( m

7 N) ?+ |& }, p# B9 Q

3 W# t R- J4 C" Y* Y% L$ Y, C8 t         foreach($_file as $file) {9 a! V9 F m0 f( ?) [0 d

& N# W. E; {% B5 x- M8 r& T

$ y. ~% c% t/ a X0 J: N( A8 d             $this->file = $file['tmp_name']; * G' L1 ^: K, | ?5 ?! i/ j

3 z3 Z+ `8 t9 n: A& c

2 L3 G/ s1 t p             $this->file_name = $file['name'];$ s- w- x+ V2 h

2 }3 G5 X5 N3 }+ B# C

4 z5 N+ P# @9 a5 B" J             $this->file_size = $file['size']; * U( u* V/ d/ e

* v- O& |( H: D+ t! ~! v0 T

1 b6 U6 J8 p3 |3 x# H             $this->file_type = $file['type'];$ X* O. h- c U& x' `) ]

* Y& C' u9 D, Y7 W3 W% V! \0 Q% y

4 j# W& U/ ~; j; X, }             $this->file_error = $file['error']; 7 w S1 W4 s3 o' H0 G# x

8 o2 e. _! c& Z* _4 P, l

' j& A8 I1 x3 k0 _   ; L( A4 @% u% A, K/ v) r4 I+ q: E

( `; {3 c/ U! T$ y$ K! y# j

' n! i1 k1 }: B1 a, C& z& l         }6 p/ }, ~5 {" R, d

: R" `' I& _! [, X

$ v9 j8 l z4 d# |9 m         $this->userid = $_userid; 2 v. s. f7 Z2 B% z

: k+ O; y; ~: Q9 w5 E

! {! V& i. d- P+ K# h         $this->ext = file_ext($this->file_name); * o! [# c9 T$ Y9 y

: S8 G) c- h$ k; q3 r: O

9 Z9 ]3 k% Z7 H6 t. v D; p         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];. Y& v. K* D( o& e* R

9 }2 i0 m/ N" Z

9 s* W/ ]& c8 x* t$ _- D         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; + ] L0 h5 w9 N' u$ u6 _! L- d8 Z

; ~% g, O; f" J9 P1 d

# m2 a+ E4 X9 d3 a         $this->savepath = $savepath;8 D2 |) e3 Q+ c: R$ n" Q6 o$ C

9 Z$ b) R' b) s% O

% F7 K6 ]* ?) {         $this->savename = $savename; 6 G! z, ~" V7 a8 i5 _% p+ x

- G) M2 v D, L8 A! r$ a

w# D6 J8 Z4 n) M. P     }} $ s" t1 ? z, S0 T9 S# w# `; J6 J5 y& p

: R( m3 |& F) J

' T7 G7 c5 o* f3 L6 n, {9 i 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 9 ~0 i* c& @% ^

9 P( n4 Q: `7 z3 n3 \4 b' a8 G+ H

+ }# f w2 _" D6 [) _ Z+ T 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 + e3 Z2 P0 g0 t8 R# K5 m

4 k1 I' S) B5 W7 F6 t! u0 i

* h5 \9 i5 d7 `; J $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: G# l- n/ Y1 [1 g; g, b' y! p1 n

$ R; ^) [: S9 L* d a0 {

9 z2 d* ?+ Q/ b3 e# o- h 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:% t% [. ^# e) J3 h* n$ O

3 n, Q9 d& P$ T/ C5 l# l4 R2 y5 m

6 F8 ^" N) b3 w0 J9 O- K" H" R" H   7 c$ m, w& z- m( y7 ?. T0 a

3 V. Q# C( @( Y+ [- T$ J8 K: D* V

+ x3 l1 z! z1 W+ b" c 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:$ f! {$ i9 c# U& Q; t% r8 Q4 U" u

! g3 @" p; S$ k# X9 k" W

( N3 X$ @# `" Q# i <?phpclass upload { / v1 F- y- ~) g0 h

. L7 o& _5 Z2 q# L* M& T- A! [+ ^

6 `" J9 B: g, v7 N5 B     function save() { * t! C+ Y$ P. q

0 H7 x6 p8 d! _$ }2 {

+ g. R& [# b8 n         include load('include.lang'); & ?5 O& X/ g" g, R0 C& h: R

# _1 [- w8 C5 s( V5 C- \% `

$ b& L" a6 X& N9 k: J         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); 1 _( k3 Q+ o. @2 k6 L# w7 Y

R7 A8 o. E2 K- G5 T

8 O; E* g2 h% F0 F: f  9 N" W0 L1 F7 s; @

# u: D6 x' D! e+ J+ K, [

8 t0 ~& z" I _         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); - V, _9 [' N4 |6 c: p

& M$ y3 }: J4 Q! {8 i( Q1 t

" Z1 x$ k0 t. z' f* f; q+ X) k  * a U" J$ S# a3 A9 B6 ]

1 s; m+ ]7 P$ T& Q9 `$ S

7 ^# ~' I% j8 \         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); / o% P; V; p- y, a, k1 Z

7 J* j; T6 `- p* D, E

( ?/ W5 l% O! H* u   - w" z! L, N3 A

# R3 I6 S/ O' H0 \( G

. O. p3 L% i" u9 L( M3 t) |         $this->set_savepath($this->savepath);4 F9 N8 M7 O r4 B# f8 W) K

9 [+ q0 W- F7 c- \

- L( v( S7 H6 u$ Y0 f         $this->set_savename($this->savename); l) v# k4 v1 U% A3 g- Q, N/ |

/ _% z& ]. d/ S" C8 n+ G

+ L4 @' x$ O; O9 s, N3 d  3 X- N1 f1 {! o4 G! j

7 K/ ^. o0 P6 k; {2 j) T

9 L2 Z8 y- f9 q: f2 L& c' S; `+ R         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);( {7 W/ p7 M: D& B; \/ l# ^

! E6 }1 P$ d$ t2 ~( Q

" g/ Q. o( I& B3 `         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);0 M% I( M) s1 u% ]# M

3 `7 j9 G/ W- o* e Q

' @( v6 r6 c& T! {+ Z! n& c         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); ) l% r' c- c3 g# D4 {

: }6 M0 c/ n8 z/ B1 }; y \. d& A

& X9 U2 }' w1 y+ {0 k   3 l/ J" ` u3 Z- D/ X5 m7 X

* @ w) ^$ H1 u0 Y) M; M

0 a, C- P+ |! n7 P         $this->image = $this->is_image();# @5 Y+ I8 T% Q8 _) N

D& b9 E8 ~/ e8 e5 z; ~

( ^; m# \5 }# O; ^         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); 5 E1 N5 d, p; t

( e! V/ F- ]/ B! x y- @

8 G9 b# K% v# ?0 J8 E8 H' I         return true;8 }5 ^( G. p9 P5 a+ n' K) p

' H9 U9 u' ?9 D# ]

8 ^, n* G' L+ q y     }}' g- \; k) e" X

( A$ ~, [8 y: P$ n5 ]3 Y

( j' k8 p) U6 \ a t 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:1 K2 S+ C% v4 }' E

/ ?0 r0 i) H5 k* o( z' X

0 F+ b/ z9 Q( \6 f5 s/ x <?php% Q: ?. F2 B# {8 ]3 X s

* g) w% ?- i. Y2 e! z. y7 s

1 H$ Q" u) e3 [" f* h! l6 v, o     function is_allow() {) G) _6 U6 f' L5 O7 ~- h

" n: ]# r% h/ y: D6 e. W) M( d

& s1 [$ b2 I' Z) R$ Q) L4 D6 S         if(!$this->fileformat) return false; / t# L5 p# J1 a) O7 Y, }/ o+ [

/ {, _, w) B! s. q

; a& b' ~( o' b7 W& T+ K+ g         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;" e, y& L# a. R9 N d, N

- Y/ E8 [. @& t

* C6 ?/ \% @: m8 t- N$ `' {         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;( h8 F! z4 T' S9 F8 e' i

6 ]) r2 t0 Z' E3 T

% x7 H! b9 ^6 @! f/ q" S         return true;! p4 O& M' R- Z, e6 ^& q

. q/ B) z* v! c2 h

8 W6 N& }2 T+ J6 A     } ! z" ]) H! v5 J5 c! d0 V4 l7 Z

, g3 b% V5 L; f3 v- M

* g9 K2 Z7 U& {0 j 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。0 l1 d0 Y# \+ ^3 |& `

A: z( D" m& X7 N8 b6 F6 O

( W7 j4 B# ^8 u. c6 @4 t. R 接着会进行真正的保存。通过$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文件。' m7 s* I; P2 a% |+ z

2 Y$ V/ g, o/ \% A1 |" } d) H

1 X1 q, Q0 V4 o8 u; P# o4 B* S 漏洞利用 i4 T9 D+ O/ k! w+ j# N- n

+ f' c9 p7 E8 G7 h& }/ I; G8 y' N

1 U4 c6 ~: @- P" T 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。, a g6 I2 Z8 |

+ }3 b' R' ]5 N6 [. @% \- h- b, x% s

V" a9 h% v. o5 V) D) `9 I   : c ?6 ^. t$ n( T- G( c5 a

6 B+ y7 [3 Y' d/ F. \9 T

. p& `/ H3 V/ M: |" x 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid ( e4 D. a( R t" S. d+ |

: R! K( w w4 ^, j

2 t2 ^& {" n' g0 a; r 不过实际利用上会有一定的限制。 , V( ~+ \% B) d, E% h2 ~

9 J% `8 k9 V" A% H* [9 q

$ ~! y8 D+ E( [1 U- u9 n: I 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。+ E8 _" |! c. [, {- [4 k3 e$ P

8 v! e" A( e/ u2 F( p

5 @9 Y9 |2 U# b# R   % c3 n4 I; \( u1 x( ?

! v' e# H" P0 ]3 G

. Y7 |- |6 y8 H4 {3 @* }8 a 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:& h! m* }+ K# Y h+ E2 s3 G% \

9 i* a7 ~2 @! @& ^

( A" u9 n/ J; Q& e 省略...$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]);省略.../ y7 b" j* ^' t+ l3 U1 J' }' ?

) g. Y5 e8 n' i$ d8 a1 s: ~

! G, p4 S" I$ q6 ]" e6 X 因此要利用成功就需要条件竞争了。* D0 |# I2 B, I8 P+ O/ a% f t

( s( c1 U- L) L( m [# [& `& R

% G7 g. ?( S8 a, E! f! g, v 补丁分析% Y) v' N; e( w( k

; }, [1 S( o- H) l) [, u g# g

6 S, w. G8 V0 ^) r& F  . Z) U) r2 A% a8 G+ E) [

1 w" F/ p0 T1 s g$ M& g: h: d

3 \/ c1 M/ k& k3 G2 ?7 l 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: ! M+ f) Q+ _2 l- ?7 G. I% [6 E$ ]/ n

# U# n) e$ a: d- b% T

. Q: R6 |; |0 r8 K% o function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}0 G2 a6 M" S \% l* C7 u

# n# [& L$ o/ Q( R

( U' h3 H1 T# Y4 `  8 |7 `; ^" c7 r, h

8 n( r8 q. P* F) y

! N1 Z% Z; h5 S. r5 W" M3 [ 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 1 v( {: H4 J7 _6 ]; ^2 U' t

) K) I, I* D: m# m

; v$ l/ g# ]9 ~5 s3 c6 Q3 k 在is_allow()中增加对$this->savename的二次检查。' e) i7 s! k0 ]3 i

# J' u5 Q. P! X: l

' |9 A7 L' e' S7 [+ C6 d! Y; F 最后) y1 j+ x5 F& z' j& G% h7 H* v

, G8 e. _: {/ @# Q

* a1 T+ B* [: ?. ^" A$ c: V 嘛,祝各位大师傅中秋快乐!5 V4 t( K( v7 ]

2 L3 |, m; S8 t" r

+ O( h4 Z* w! k2 M, I) b0 o( m   ( P. `' r' P. F; F- {

% c G2 B; z! V. g
回复

使用道具 举报

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

本版积分规则

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