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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
2 D; E# A& C* E1 q# ]

1 G$ O& h9 Z4 b% W8 k

6 M e. T1 w" c8 W) u

0 s7 n0 l4 Q: d6 i 前言) o0 N" j* S1 q+ p. b9 V

/ l, I5 a& Y) W* t/ b0 i8 g4 B! [

; P1 h( R. b% \2 N! ^; T8 n 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 5 }/ R: B3 z7 {" [3 M0 Q4 U* d

4 E( P7 h0 j* E' Y2 O) C

7 z* S1 ^ n& T   1 m3 u& ]& h5 X9 k7 q/ [) E8 G \; b

. F% b! {5 u( t1 I

( y, b, P4 Y# f$ A( \, k 漏洞分析, F, W+ ~- j; Z+ H: k2 G. N

9 u; n1 z9 N0 y9 j5 _$ l' }! e! w

4 A% |2 ?6 i, R: { 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: ! K' F( X$ z; | S" s1 @- M% D* J! A) B

7 a0 O, f& m7 ?5 q' i: U: |, H

3 m* i4 J. f3 u  * M. ~, j) _- P; y

& k4 P8 i' _% K! ^& d5 j. G4 M4 y

/ m; [6 ]6 d+ e' o 对应着avatar.inc.php代码如下:0 y- d, F" W* S2 y3 P4 p6 [" Q; ^

]3 V3 X! V9 v9 |. T% |

- D c* ^6 W! [- E0 V6 A8 Q <?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) {6 |. f+ P6 T( Q

3 \6 p2 H$ ]) ^

% {. [# L- L1 @ V6 F/ A     case 'upload':7 C7 z# k+ _2 x1 e- \* K

# }7 H, n& _& E/ |

* o) M% l* a! e: m1 R         if(!$_FILES['file']['size']) {5 u# z4 C7 a4 N( v: M; {

6 m7 K* E9 q) p& E* W

/ D. E& t. o; N1 `! M             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); ( j' f) U6 z- t1 a

5 |; K9 e/ @; y, A) ]% X' a, `# l

) Q* Q1 V9 c" f             exit('{"error":1,"message":"Error FILE"}'); / V; v1 r4 [" ~8 `! G# g: Q# o

3 l' ~4 q" w5 f# c r! O, O

8 _9 t i8 I- L, E# c         }% a) G. X5 L# g3 O" E- t+ o

5 j5 Q+ h; r- B* B# J. U1 ?% f

( h1 B$ S5 A1 }9 U$ h. \* r$ U         require DT_ROOT.'/include/upload.class.php'; ) P2 O/ m( J# p- k0 O5 a4 q

% i4 ^6 M$ q0 g& W# W. v

$ M+ T& \9 a4 c# W/ ~6 o   7 R# D$ o* K, F1 H

7 W; K, |! r& {) d% U k

; d% a4 Z8 ~1 d0 M         $ext = file_ext($_FILES['file']['name']);: _0 P! Q5 h4 V" @( Q

; b' M9 [" Y+ W, V

* l0 X1 y# i# _$ Q# ^* u2 i         $name = 'avatar'.$_userid.'.'.$ext; . U$ y/ F% ^4 A# @

* G- r9 I+ ?/ M5 Q

3 T0 }2 E( c$ V2 _9 v. X: u1 F5 Z         $file = DT_ROOT.'/file/temp/'.$name; , M" H7 d0 `9 ^* F6 K; P% {/ `# C( `

# w) u' U E6 N/ n0 ~( L" c

& A9 Z) b# H' r  : G3 K9 ~$ [0 |

5 E. [" H* m- X5 g& }' r5 {* v c

6 f4 Z I$ ^; Q1 d* k! r         if(is_file($file)) file_del($file); 4 G$ d7 w d) ?( e6 s2 |

' ?6 I4 V/ b2 n: Z+ M9 e7 e

1 U/ Y( {/ q, l2 {; v, A         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');' z+ A/ X9 l; D3 p3 F% J

" c& `( c% I- s8 p7 p( w

) D a7 ^/ w$ s, x( B' R, S   # F x' L# a( c J& A

7 s9 m, H7 g% k1 A' R

( ^* [" N6 A8 `- k. j+ ?6 _& |         $upload->adduserid = false; : x; s/ t$ a1 U* e) ?1 k: ~

1 c$ ?6 N1 z2 U# a z+ V3 j: m# ^

7 V3 W% j) S2 S8 y5 J" P4 P& R   - z+ l: {0 m5 f. U- i) }- f" L7 c; Q0 s% t

6 p4 |; d! n5 ~ i

. P6 c1 `$ y0 o) l         if($upload->save()) { ; a. \0 F. @% ?1 t- f

3 I" k% J3 q' f

) X9 J9 _! B3 \: H9 [7 u             ... ; Z1 |% T) ?% M4 P" B& F

0 J. `$ q, B/ ]7 A0 Q6 r$ w

( O. v* w2 g+ T" y, s& v' `5 `3 `         } else { ( j7 j2 o7 ?% D* Z0 j+ B

! S2 |6 n( T# _3 n' Q; f

$ K8 g9 B8 e" {; g' D* `( Q             ... * v7 d! s/ C: {+ h# R3 @- D

! O( a! _0 [: L& m

- \: e" C& G9 [0 R+ `2 w7 a0 g u         }5 {( z& j- u/ }: I1 j/ X0 w

% |7 c0 P9 ?( S- ]8 B7 F( h

9 J% w0 ?: [1 X! L/ M2 A     break; ( q7 O3 `% A8 F7 p9 Y5 D

+ i. @1 z: U$ ~% C6 U- R* O. Q

: o5 S0 \; Q' K! d; F" s 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。2 V! ^+ K0 e5 m) O( ^

" r( H: L: I$ C8 @2 b( V) Z( z" a

" q2 j( `( p- j# l: q upload对象构造函数如下,include/upload.class.php:25:# \ x3 R7 _. T: e7 D

4 C' T* z1 E, q/ j7 {3 K, P* J4 \

. H. l- g! G2 s, Q# Z* s1 K2 g <?phpclass upload { $ g1 Y% l2 A4 w& Y E y

- o1 |+ P7 k0 V$ {9 c

9 I, t; ^, U& i: m" d1 Y     function __construct($_file, $savepath, $savename = '', $fileformat = '') { ; s# R }8 I" ~4 s' ?1 ^% y4 u

) V, `% @# \ J8 y6 U

7 \, B m. b0 C" _5 B         global $DT, $_userid;/ L. N7 Z2 ?2 I

* j, z: n# s0 ?4 W7 h

3 g( t" _; x% h         foreach($_file as $file) { 1 V, v. |; k/ J; J, o6 r

/ Y+ d/ I7 P0 P3 P; |7 F

2 U) ^, l" U( k9 a             $this->file = $file['tmp_name'];! V2 ^' b" D5 f; |. d/ q- N3 ~

. ^3 B! V: Z1 H( s) a" g% k' y

% V' o9 w2 w, L. G             $this->file_name = $file['name']; $ Z1 M, e& S: j, p+ A

* h* e; B- B0 L4 C0 R. \

) X$ H' L& h; U" F' ?             $this->file_size = $file['size'];# P/ |+ \! ?/ i8 {: }

* f5 Z# w$ D2 \' K" S

. {. O$ Y; A5 Z9 i {             $this->file_type = $file['type']; ) e+ j) C# o. t) T

6 f5 @ R% W( I6 u

+ k% R5 E1 |% S# _( W8 Z6 j4 Y             $this->file_error = $file['error'];. K& f$ i, S8 }( H: F0 D+ I" A% ]* `

2 z3 i8 @% K. R

% l3 o7 G$ X# k1 o, m- W4 Q, A  : p6 e& \ t9 W4 e) j

% n( O8 Z. H" V( |

2 W! i& n+ l$ ^# n         } + T' x2 v7 j+ {2 B1 Y

! @; A: m/ u0 r6 I9 S/ W

4 n9 R" A4 ~% i* h0 _         $this->userid = $_userid;5 d) X9 H3 q4 Y1 z

+ v' }! v/ N6 D

5 s2 I- ?" k6 u5 k1 ^: O         $this->ext = file_ext($this->file_name); 2 I) h, r( ^/ e

( E" J$ V$ n+ ]

$ d# y4 b) R/ i. q1 C- [7 f         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];2 c8 X! x6 \2 U

/ f2 c9 o& W! g

- I' d* g" J) b! C* y! w0 o. ^         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;- @! b# `) v8 O' M" z5 e2 e

. j3 b7 T. a* J* h

8 c# j4 j0 o/ D         $this->savepath = $savepath;6 W6 t! ]! v3 P* {: J" Q, r% W

! X, y' K5 u! S. K: _# |2 j

+ D! L/ v8 C' i4 R* s         $this->savename = $savename; ( l- C! t, M& Y1 C2 I

6 h1 x0 X; @" J2 ?

, y: U: k3 Q) B$ y8 ?+ _8 g     }} 7 h8 e* w ]8 i2 |

% f. S% h: W* V

+ O/ O5 x3 ~! k ^) I 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。0 T( T: l; `2 h# F# f

1 B( O! C' U: |7 y$ H" ]. j M

; C7 j: Z* G2 ]; W 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 5 ]1 L+ [* I! s2 p

7 M& F& b2 k, P) d6 W/ S5 K) {

6 D, U' Y( e5 A: y% A $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+ I7 L# ~* k8 i8 A) a

^" F" ^4 ?. K8 p6 X/ K% V+ q/ Z

' D6 o( U& b- b& b" H, K 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:! P0 V& O2 F9 q4 T9 N- s

( e+ @" c0 x' ~. _

: w$ ~5 F, G. @+ J7 @" J$ d   % b' M/ ?! T3 I' r& s

: a$ \2 ~& D) @2 S- U, @" Q

& P! e% o$ s& N 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: 7 E! X2 J, s% L5 p2 S8 T& J

: Q0 G# v1 z' D- t; [

% A; q. k7 Z1 b5 q, }2 q# b9 { <?phpclass upload { " f# h) F J: }) b

- i, n" E( h z; X2 `* i6 n* H

3 @3 i- {% p$ j3 O2 [     function save() { & c7 j% f" O9 X6 Y. M) n% |; i

6 {, [5 F0 V8 |! h. _

+ r/ x0 [: ^( c' P6 h- U) g         include load('include.lang');, C. Z& c$ I7 m' m8 t

6 |- V }# w1 M m

; a+ o! d, c% n; ]3 h         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); ; `0 S7 x' H, b6 J# W

/ [1 p* I/ M* a1 l

t* z( O0 q% I+ o: W- Z" H0 \  / s2 R' d- a) q; O3 O: ?5 o

) t, z; m$ ]2 S- @

$ W; i" j+ u) I3 ?* f9 N         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); Y5 E2 v: d2 Z

* Z) E5 W/ P4 L- K

$ I# U# U5 K2 y  6 u% q$ s G6 o2 a9 [5 K/ O# d

, [- E% u8 ?, o7 T/ L

- v5 D. z5 I, T" P( @( {) T/ w         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);4 E& J9 \) ]* L4 P7 ~5 s

- U! R) `( Q9 l* q& y& v T

, T, T3 R1 {! E# j/ f   ( x- X% r2 m( G% l

. k& J) K, {) n( x3 K; g

7 Y: b) L5 a/ P5 C0 p( `         $this->set_savepath($this->savepath); 8 L+ J9 W# k8 y& E. F

9 G1 z* h* p* `' D: o

/ F4 V. |1 [: A0 Y# R( ]& R7 m1 V         $this->set_savename($this->savename);, \) ~" M( f+ h, E, i

% |& {% ~3 ^6 W: p' J6 f& Q

3 o! `* S- c4 N. L) M0 M- l  & r- g+ o0 c$ ?7 w' S3 ~3 f

, ~7 {; T/ H& c5 X

8 K: q, O! q# Z% T" }. S- C         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); ' X+ l% k7 w! o0 O

" m4 B# o7 u8 I9 M6 {5 L1 {

$ T6 y$ S0 n3 h3 q- @         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);3 q0 l7 c2 G F7 I* n

$ m3 O& u" |9 {

; V9 u/ V3 h; i. H, K         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); / v7 ]. F: X* ]! L; {# t- \+ L

! S: o' r @0 V" U* H, r# n2 a

1 w/ a6 n' W8 N- w  / V2 x5 K& x/ k! {5 q1 S( a

9 p+ m6 x/ r( E) v: C1 Y+ v

, |' J$ q8 j% ]: i6 W! B# t         $this->image = $this->is_image(); , P7 J( B+ f# ?$ I. v

% T% k1 X$ M4 {

8 L% p h, F$ @# |- U         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); ' u7 Q1 z: A+ y6 @3 G& V

( s5 c( O& A% B i$ o+ A- S6 X& O/ H

2 i4 P# x- ~- U1 h- x; m( d7 |         return true; , x- J( D9 Z7 X5 c4 @9 N, j

0 Q1 J, }7 c) Q2 s$ w

1 y% A/ _. b' z" Y' d& R. G) h0 A7 \# L     }} l0 t* o, I3 v) q+ g- o! l1 _

$ Y/ X( n6 O& ^: m

1 K( G& D: l* r" w 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: 8 g# d4 ?4 h3 e0 F9 j! y

& A* x: v* ? E3 L, K# J L* j

/ z' G2 i$ ?0 d! P, U2 d" O8 J <?php6 \: [1 }% n% a" ^

% m% }" V6 x0 z8 r( ]

- |1 I" g, Y: S; K     function is_allow() {( D i# X! {# ~

7 [0 y4 K( F3 y

: ~9 i p% x) U, v9 d `0 e         if(!$this->fileformat) return false; 4 I2 |4 q4 N; r& c

* y, L* Z, |6 r2 }4 \+ q& x" x+ V

- W1 g2 n. `" r         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;0 S' z' X. U+ r' D

+ T/ L0 n0 s& O7 j% L0 L

6 R! o6 ?/ U# [. W         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; O1 m \4 E1 g! [6 \6 Q1 k) K# _

9 U6 _& L4 Q, y6 _

( z) g2 m9 o3 m* J0 N( G; ?         return true; ) U1 j6 ~7 D$ w7 y

" K: _4 o H' C' L ~

( d- a E' C# L3 t4 @# j7 }( l0 s2 E     }8 i6 @, E. p7 n* ^* N( O

+ f: z: W& j2 H! H- ~

0 I8 O* ~. \* ?/ m/ k5 j 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 + T2 C4 l- M" l; T( T0 k+ y. a

# R" `7 J! Q, H6 w: o @

6 ~+ i T# M9 d7 S* q. { 接着会进行真正的保存。通过$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文件。 ' z3 \( e4 E% [7 M: t

3 |" B, M/ m* P+ b5 m, M+ F* b

: d2 n( L" P7 `+ u" R 漏洞利用* y& B0 @" p' ]3 f" }: c4 e

. ^; I# S! K. \. y( ^: u# S( Y' p

+ R6 c- R$ d* I 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 9 `2 a8 |. Z; O% `2 O

+ k1 z+ A* R W: z; s1 e3 R" l+ Z

( O- }! S0 w. t6 q5 Q( j   1 I6 R6 q0 r& y- L: B7 ~

% j) |. g. _" q; c( R

4 b! B& |3 N/ k" Q 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid) v* f1 W% {0 o

/ u, v; c% f! d

$ ]) X) p/ }* O3 L: L3 } 不过实际利用上会有一定的限制。 ( r: v5 w, P% a7 H0 U; M

5 p* U; q# n2 d9 D* s# H1 {

$ t6 D/ }0 `3 N/ } 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 4 b( B: d/ n6 S4 h

( V2 H7 i5 a4 q# G: \* B* A& ^

$ g) r* Y. M' ?( A2 X0 I. f, j  / |5 X1 q: o2 m' d

0 _8 U7 L1 c0 C |: o

' ?( ?' H% S# I( u5 e 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:1 c: p) q) Q/ v7 P+ Y+ h3 X

9 ?, O N: j. |6 B" o

9 e; A: M. m) ~/ x: p 省略...$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]);省略... & @9 `6 L& r$ S

! `) R; V9 r. E; N

& _! @) T/ H# c3 F 因此要利用成功就需要条件竞争了。! ]. w+ L' Z! ^2 ~9 W. ^- Y# Q$ x0 s

5 Z+ X) c4 p$ j( t& W2 f R

1 ]' Y0 O- b z- O# \. E 补丁分析 + N$ b: l1 l3 |1 ^" Q4 R, l# d

( r6 e8 |/ I: N! t; n O! I

" i8 `9 t# |6 [0 k  1 `! t* U2 c- Z' |- |

2 i" d2 J% Z! p! S, d* O

' u0 B# j9 w9 G9 z3 c 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:+ l' R; g, m, j. `! I

( [9 a+ ~6 X2 W2 E0 S! M

8 p+ E# H ?& W1 W function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}/ m: P) V7 P# l7 [

- N- G& J! }; N( i$ F3 x

4 U- @. n7 x: u5 V7 \( @1 a   1 q0 o5 G8 [4 }' |+ [; W2 x, n

4 B$ x, E4 M) t/ C6 b

4 Q# ~3 C; |! O) O/ R$ s 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 ( a8 E; f4 h8 A# e: G8 ~# O

/ A% x3 k& u9 m; m+ q) t# A

" w9 }' ]6 v5 J+ u, o/ p8 p) J 在is_allow()中增加对$this->savename的二次检查。 + I5 L) r& R$ Q! t S9 [1 |1 X4 g# r

0 N0 \5 D' h$ Y3 X; Y. n2 z" h, U

8 Z7 ]! Y6 Y! X& ~- Z9 Q. ^ 最后 4 a2 i3 e; X: m1 S- l0 h9 H9 C

4 N% Y. I8 v# ~9 y# A4 }

7 ]1 S# t2 `$ Z! i 嘛,祝各位大师傅中秋快乐!. I* p( C# g$ N' e/ Z# c3 J

: L4 j9 c( |# B! G

" v4 A5 L' t, W& `4 c. ~   4 L" j, h% E: l9 P0 A1 B L

5 s7 U3 P% v' Y6 k; v1 y# o
回复

使用道具 举报

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

本版积分规则

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