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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
4 s. w3 G; U- @$ ~$ S

% Z& m8 n: k0 [& D; o/ p m

+ y$ y ? O+ _' s% X

. r& Z" K+ n0 l( a 前言 ' D2 ~1 r, F% V. s

; e P1 ~& D" Y

5 `9 p- I: F7 p! s+ d M2 M. |1 a 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。# d- @, E. h4 j2 U: s! v- `

% ^2 _, A# C' Y

& D, h) K r u$ E+ O! `   8 i" b/ r5 Y3 H; Z' p. o

/ V5 }7 r. e* D8 G c5 a

9 d8 C4 t1 D$ M7 J 漏洞分析" M1 ?3 k+ x3 M' f

3 T7 C ]" d7 c1 k, a7 ~, t+ q

5 |! {% n; }. e 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:9 H9 ~5 i$ \5 W

& I" v, F; }! V5 ?) G9 n% [+ f' Q

" t( i1 n0 s( v   - A: U" K5 M, A7 m1 A

% l0 D" c. M1 D" W4 U+ ~

* t9 R0 M& Z& k; U 对应着avatar.inc.php代码如下:1 c, r5 }4 e2 j1 u' k

+ _8 W& H( h4 T( ^) Z* }

/ J: e1 l5 e2 M <?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) { 8 b; U: G! G, W; G& T3 `. ?. Y4 V0 J

/ f% m2 n T3 h) E5 E5 r8 B5 I/ S

9 e( S N+ S3 e4 S- [4 G     case 'upload': 0 o8 k- ~: |! q8 b, y) s7 {

- E$ _' R$ S9 k

6 u! V( w6 b# ?6 w         if(!$_FILES['file']['size']) { ( z" }3 U, r6 Q

. P& c: ^% |9 O

( }. f; z3 c6 G" H             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);' Q9 {) N0 r& [! R8 ~- s

# z( C" h5 W' [

* \4 R% g1 B0 }             exit('{"error":1,"message":"Error FILE"}'); ) x4 O4 {2 g i, v

& I1 ~) ]) I; D! ]. M

4 B1 V' H* Q; e; a0 o& H9 W         } / G, [" }% Y6 q3 x# f

, A) H4 k- l k: N$ c0 ^

' B% N3 B L# ~$ X9 N8 N         require DT_ROOT.'/include/upload.class.php';+ q2 ^" b, {- [1 [2 w6 M

8 b( e; ?% k. @0 M7 B7 k6 V

" {7 |( }+ A3 o7 e3 K8 u' P  * H' U# ?0 ]( f) e% ^

4 {, x8 r/ L3 I/ Y% Y

) `4 J' O4 E% Q5 Y2 s         $ext = file_ext($_FILES['file']['name']);3 ^6 Y. r [8 z+ N

" I4 G9 O0 u2 r+ C; s: n

% J; V6 o& J9 V+ i! M% m         $name = 'avatar'.$_userid.'.'.$ext;% V. ~6 S9 ? G2 V2 @( L

* g7 r$ D! y+ A7 O, T

2 }% Q1 s- l) K* X         $file = DT_ROOT.'/file/temp/'.$name; ! c1 v2 J* N' [, B5 l

5 H) G. m. E- d" v

2 }6 c1 R( ?1 w b8 b' a, B  / l& F( H5 c. S' u. D, u6 n e

9 u4 [0 f1 w, t' C* U, B& y

, W, q% Q6 i, W; H& c! }: D         if(is_file($file)) file_del($file); L" e. s5 V% d& C( h- M7 Y1 @2 D

& q7 \. W& L& L: n# M/ f/ L

9 `1 K! J& a* r7 I2 g         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');7 j9 O$ H* o$ P5 U8 N! V6 @: S

k$ S/ [# ^2 V! r

9 f/ h: q3 |) y% o6 R  + j+ k2 {+ ^0 P! A& E) ]

: J& H2 L% `2 L; g ^/ v+ E: J7 K( y

8 q# { B7 x& P" U! j) f T         $upload->adduserid = false; 5 Q, F, Q! E8 x7 U

% }& r3 t4 Q0 B' u( q5 o0 |

' ^8 C$ l" \# @1 q# ^& K   6 n! e! C! A6 o1 @

8 w* ^) S5 ~# g/ {9 I/ Z' q

1 u5 g4 ]! Q' n; ?         if($upload->save()) {9 ~) P$ X3 ~1 o( B& }5 ^% Z+ u

O! _ P2 E* K( K. ?2 y5 d" F

/ J2 e5 g& I5 U3 E" r: z& m7 ~, Z% m             ... , {4 N' C# L& P8 D+ J

9 o: F' E/ y) _- P- G h: l& L

( Y- Q0 X9 J- q& s- Z         } else { - O" W* h# o4 X/ D+ c p

6 e! g3 P1 s4 ^! Z6 Z" W+ j0 \

! [! m2 C6 t2 m0 \* e             ... , o6 x' ]' I: d/ q

6 y u- U4 q" P# O. \: ]% P2 @

/ N% ~$ A" [3 F; b         } ) {) H U( e/ Y+ `( O# w0 a

: O# E7 A& `2 N# U$ C- n

. d2 i: {9 t# |6 M+ \& V     break;8 e$ C9 }8 h- C9 ~. T

m% @$ E& {2 X- Q: F

; n% p- I# ^/ R# W, w5 e8 f; ^/ T 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。, g! i$ @" g" C; A* _* ]$ L

8 ]& R* ?: F6 v; T5 |+ D' B) m' t: u

6 ?5 x1 s6 L: U" F+ V z upload对象构造函数如下,include/upload.class.php:25:* L6 C" J: M5 F, p" v

1 N& M: V2 G5 }2 z

7 D$ ]! k5 \* g" ^' F <?phpclass upload {1 q3 E. o9 W& C% N7 S% M; h, M

6 i a3 ^# g- |7 T- X

( ^1 f3 B2 t5 c8 U+ v     function __construct($_file, $savepath, $savename = '', $fileformat = '') { ; R6 R3 s; {$ h1 U' D/ Z

7 S2 @& S N( Z) f( U

9 {; ?: r3 m) t& y0 Y$ \7 }- E         global $DT, $_userid;5 H/ l: x! J+ D4 W7 l% |

1 c( F( t! r+ H7 ?

7 @+ p4 J1 a% B         foreach($_file as $file) {9 u1 w1 J# o$ f5 s' _# V

% h1 `; m) E. `6 l- ? {! U3 Z

* [: D: `% L8 y% O             $this->file = $file['tmp_name'];* F4 b, F" p" x* G

% Y' t2 L. {+ b+ t

+ \5 Y& o* T8 K Q. l             $this->file_name = $file['name'];$ O0 s9 N+ D5 V+ O5 S; U# o6 W

1 U( d5 O: R5 j

2 W# q0 v+ j4 p2 A p! d" f             $this->file_size = $file['size']; 3 a$ O% @! a1 A2 I o& e) U, l1 S

% ~# |$ j3 |3 C. [

* O5 A S9 Q8 H* t3 S. P             $this->file_type = $file['type'];6 g4 [* V5 z3 \

% `+ J; y$ f, p8 c7 S1 s+ ~

) ]. H* h+ N$ N0 r8 u/ L* ]% k             $this->file_error = $file['error'];" v8 b1 l; p- \5 M1 ~$ s; Z

0 Y9 }! E. D* \/ W2 o- n

+ R0 ^, }' ~* x, p  9 Z U- L# s ?1 ^1 P. H

- _2 k: X7 O- d B

" S2 x7 W" z% P0 l5 v9 n% u         } / a. H) y$ W4 V( S( I

: ?2 x. I; O) J* s* Q

! m, N1 O" d1 G* v% Q& o- a3 _+ l         $this->userid = $_userid;+ y% B* P, _; M

; j, }0 Z2 s2 H; N8 S/ T* L: Y

1 d6 s: [. Q, ^, F         $this->ext = file_ext($this->file_name);( Q& E$ M3 p. F

4 J0 T: L# k# z) B6 _$ p3 p

9 |+ `7 B2 f. c' w6 m* I         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; ' X. V9 X2 B" S7 ?' f# ~+ `0 ~8 r, @

* O' R" c( t% V' y4 X/ d

/ h, E& O# {' h* ?! R+ l: o3 k         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;, n2 y, I9 k3 Q

1 p+ I2 ~$ N! h! T) @# C

, _. m, f! J, Y3 C/ ^) O         $this->savepath = $savepath; 7 p- ]6 A) |* h9 A" }+ @1 y' I% M; o/ O

* _/ M6 V7 j* ~* ]

/ w) i# n+ J- E( Q         $this->savename = $savename;/ N3 d* b. A( B' b' \ V1 N

( A, l: G9 v0 B/ ?" V* M8 U6 u

4 T+ J1 y: ~4 J% m2 ^     }}4 j+ k$ |6 S% N |$ C9 [ h: J* y

# y5 `- I( b# F& N

: s. r$ r7 l/ G; L6 k9 I 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。* e/ f+ t2 V4 }5 v6 }6 e

: O' f* b. j D/ S

) C8 C% c" L; @! Q% E) M 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 ) T" w' p3 n+ O0 G- d

& q" C9 A2 U0 Z

: z2 _2 ?8 F8 s $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 ! z$ P4 q, y6 x. q

1 d$ h9 [2 K& ~4 O! P% Q, Z P

0 _/ Z& s. b0 \ K/ `# L6 k' M 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:7 O/ ?7 s) r3 h6 h/ O3 b, s

6 z/ d% ~* N% H1 ~- ^% v# z) D/ j; B! s% a

3 r* R2 w/ r4 j! N   0 ^( m! ~! ^4 O8 z

& l/ U6 d/ f3 z4 w% n# S* ^

, y) y$ ~ p: s" r8 \( }3 Z+ [ 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: : ~) u' N& t. f [! c2 _' b. b

6 R3 x& s: o1 V8 a) G- S

s/ L( z* H0 p: o U <?phpclass upload {1 d) u0 ~3 j- i

- t" X$ @/ W+ Y' ~8 @" J

4 O* F" h c6 m9 |' I     function save() { ; c- S* `' B) z( X/ x' B

: H2 m9 k+ X/ a/ R. l, v( J; m

* ?4 W0 k, z: p/ U! `         include load('include.lang'); 9 J' t7 R. j1 v& M5 ?9 w

/ i2 ~% W5 V3 E! D% Q7 Q

5 `: W3 q% a: c% r9 H c         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');' \( ^8 t7 d6 q' Z; m

. o# c& `( r' \- @9 a

# W0 b9 d: {7 }: {  ; t' H; _7 o8 _, {: g

4 K( B9 ~% ]$ l$ g" E

% ?* E, ~+ Q+ N$ D& f; g3 p2 T2 }         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');+ E- k5 ?& J; U5 X* O S' |0 o

% W/ P8 B4 N. n. b, H

+ c4 c' n* K4 C: M* t/ L8 `  % \. }8 O) g$ X* }( p

" f+ Z' V+ O$ \+ x3 V

( g a/ N9 ]- E         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); \! B! B% q% m% t T5 l/ R+ h# w

& P# E9 h9 }, O2 c+ F- E* ^( X1 A

) z0 x G! a/ ]. K* S   2 v! T) m7 m6 i

5 r: S5 K8 |9 |, `. G9 K$ m4 V

8 w" I9 |; a; u1 i* Z$ Y/ h: |$ h0 g         $this->set_savepath($this->savepath); ' ^4 m C$ s+ o8 g3 m

# n" B: U8 D; q, l& Y

/ V; \* ?, K, a p+ z# e5 \$ c! b         $this->set_savename($this->savename); 3 H2 `( ]3 ]; C+ k

7 C) R0 c, U( s0 n& \ H4 ?2 v

( j& b, A* B; G) j2 A  ! Z) Y [4 i2 p! B" D1 z

. Z5 O$ k8 _' b7 i. x; S/ }4 T$ p* f

, z' C7 p0 W7 x. }3 a) q% y         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);+ x6 x5 `' I# `9 {" b3 Q5 ?

% U( a! p& Z. t+ [( X- ]; E

0 a; ^' y1 \2 c" w! v! C q# ?         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); / o' s& \$ e# L, N1 m6 w. \- [

0 Z+ g" i- R0 C

& p' F7 ^, x7 W( m: g! m         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);0 x. O4 G% F1 A

) m6 L- Y$ d8 O$ K

6 Q. w) S5 V# R6 w   7 ^$ _6 D; F$ ^" [2 d! _

9 W4 {- C+ P0 }, k U8 }) }. Y% E5 r

9 p; r# b. i3 Q1 b         $this->image = $this->is_image();/ J/ {, ?& i% v

5 j0 w R/ q% U6 o5 |* Q+ @1 _$ q

. X# ?6 @0 U T8 z" c" p         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);' ?9 ?6 N: X' [, j* b

# \ X8 `- |1 |" q4 z1 r: Z

4 t7 a$ [- o$ R- [& I         return true;- H) h5 B' A/ C% ]

" Q# M+ k3 E/ q# ]

9 M1 {* L2 P0 t     }}! T; T5 s; |; E: M6 }8 l

+ ^! |4 o+ X" |+ t5 Q h

# g% l* f5 J3 y( Z% [, ~' o/ q5 j 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:# x0 |7 v/ q( `5 v+ l

2 }+ P& D$ w: |2 I

- `4 N8 `! v5 m& J8 \; O <?php ! l* Y; b+ P- M: ~7 _9 r

: f; |! X, \9 }

7 m3 n8 l3 Q) B7 m' h. D- l     function is_allow() {5 W6 E' V) }2 o, k

' A( u: v$ V0 Q8 p2 g

; A3 l; O% v: y         if(!$this->fileformat) return false; 2 z9 O8 n. X; X! e

7 D9 |, x( e( I# r; F

) H; | `; w1 m7 g! L. s* Y' J         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;5 A0 W4 O- C% p' k4 E

$ a" M+ V( U y( ?( S9 g7 v: j

* A, }6 q) D" M         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; . w4 }+ ?; C: J# w' z1 Z; c

- E& K3 b$ Q3 x3 c9 x7 W5 e- k

( t. J) N( [ t" A7 P! f         return true;- S3 b( i7 ^1 e! A) V' X8 |: T8 f

% C# d/ F4 z! @% ?

$ v1 w+ ^! r5 G$ P( O& R3 q     } # H2 P5 F9 g. T' \

/ q, d. e' `' l# `: V

. K7 m6 h5 V! v1 {" O' G! q 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。7 d- \- r( X- S @' u& x* j/ F+ g

% H" ]( S5 H& A

! v p6 Q; L( m. A; k2 U" } 接着会进行真正的保存。通过$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文件。 $ \ l) |: Z* Z: Q

, ]. w; O* {9 o4 w, x

0 Z' ^9 i+ @$ a 漏洞利用1 f& D4 }) e" |" K

- J/ t& P: l" i: c; b

1 r0 M: `0 u5 g( P2 _ 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。7 _1 ^( y/ l0 x6 A

7 u6 h/ K: f8 X

. P% w0 w0 ~( z9 }* j" t3 m; h   # e* R5 }1 z! w3 w3 t' e6 y( W9 e

' f% B3 h/ f$ B7 j' M

5 Q# i- e3 q5 [' m# p/ \ 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid ' m+ l3 l1 K ]& F5 o

( r$ ~8 o3 F) g6 f

, ?2 e2 W8 b$ |8 z- @ 不过实际利用上会有一定的限制。 9 X. b9 ~8 q6 T& [# x' K3 J @1 ~

1 ]5 m! J3 r9 C7 m

" [' V$ A# z! k' d; Y+ |& z 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 " s) d- B, l3 g6 j# G

! j$ W6 H) _- U O6 ]1 b

) j7 e: v% H( _   4 J. \1 X) @+ i4 h5 T9 {

9 s" P) x. M* U4 N

t! T ]0 b4 C% e0 x 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:/ I' N# _# n6 G; s+ U

# P1 c5 u9 t1 M9 ^4 o0 `

: e7 X7 l9 N: f! }) P, F" z# {* z 省略...$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]);省略... . I$ l% r$ R' U2 b

% z. `8 J9 E4 D% a8 a0 T+ O( a

! \9 Q% J: |6 p1 { 因此要利用成功就需要条件竞争了。 9 L, c& P2 @% _5 [' K

" f! @7 X k; i: w/ C! ?

2 _" t" H* c& R5 B4 R 补丁分析% B! H2 H3 o, V4 ^8 ?# d

, b- T% g1 I% p+ D( W0 d6 n' r

# D6 e4 K: H; |9 B& |  . y$ d' o+ w3 N. N, ?& j' h

. o: D+ \* Y9 J: z3 b0 u4 e

8 ~: |: x( |5 g! R$ T- S 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:5 |- A: V: _8 |' D/ k) _

( a' f$ N w0 \4 I5 {

% Q& j& [3 G5 N: Z7 Y. E function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}. [( F. w* K7 b5 R

z3 D& s$ m! t' h! k+ Y' `

% Q0 X& u2 c/ P- m$ ]   8 l. J* ]8 l# q4 i" `. }1 R8 A

8 Z) b' }6 Y! ?/ o5 p( L% x8 v ]1 x- E

" k5 c* ]* m" D3 \& H v4 c 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。! c+ K, G( F/ L l9 c- [1 N, _! {

6 t2 l& [8 A" {4 d* q

6 v' D& N; Z" a% B: p 在is_allow()中增加对$this->savename的二次检查。* L: O2 H- I+ `5 Z) {1 r4 c5 X1 t* |

7 c+ `+ }- V. c9 E9 c& o/ N

/ V6 h! }- A; t# J5 W1 `% q 最后 * k/ `/ g/ C1 v4 s- F6 g

. H3 }+ Y$ I; ^( g" g4 r

M, h5 |( ?) I( p0 q5 j 嘛,祝各位大师傅中秋快乐! 1 T: B' d2 \5 v9 Q" N y

; L0 I1 d: c8 \( i4 B

3 c4 ?' @5 z5 E) x4 O7 z* e! k# G   6 L% \9 \0 w% h9 ^' ]

8 [8 i3 P( X+ ^
回复

使用道具 举报

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

本版积分规则

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