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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
8 ^0 H8 \% J4 @) K4 b/ T

/ {7 v. d2 A3 L- D" B% c9 {

z, I' T& V- h1 i# }

8 v$ ^- L2 Z$ E" W 前言3 J5 J( G; z. X

3 ^2 J$ D% f# P" e1 @" Z* C

; L0 }# J. G4 c; ~ 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。) H9 n( K. F' D* U$ d _2 U

/ R/ G/ n) C" e

+ o N" X) @, A+ _- W/ B  ' y; Z: K8 c1 Y5 C& J! h

) S# T9 q) T) H# U: L8 Z

+ e" S& a4 `/ i1 d& l" @9 G 漏洞分析 5 [* `- _8 ?$ F: G4 Q

* B9 F7 G2 V3 s9 K$ z# s+ Z

/ [1 m& o" F P0 r0 T1 C/ } 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: 3 y$ O* }6 D5 M% v4 y4 \

0 [+ ^! F4 F, L d3 g3 `* Q8 n

v5 ]. }; f. [9 {% w  5 i& R6 N6 g- w% R8 I5 t

0 W9 @4 a2 N, j' F' r/ Y

6 Q: E$ u1 w0 t. Y+ v/ q/ j 对应着avatar.inc.php代码如下:3 X0 ?& A3 P# L# k1 K

; |) ^! W. t' ^0 r( l* }

v# \( b2 m/ I" t1 d, t |$ ^ <?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) { % T# O/ b' m* u1 `& o) ]5 H

0 d) {0 ^0 g0 [* m6 ?

6 G, H; E& L; u' Q+ r. a     case 'upload':6 s* @' o4 r/ w

6 ^9 M3 E) V. s

9 c& S5 B9 Y1 j3 e4 M         if(!$_FILES['file']['size']) {- @0 t* j+ ^/ C9 g- _# q, r. S2 N

' _+ b# F. o" V0 v! p3 N

; x% o( l* {8 X7 ?             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); & R( M+ @: I) F

; ~( n% u2 i6 ^

+ _" u% @- t0 P             exit('{"error":1,"message":"Error FILE"}'); # ]" b3 w, m0 x) l& r4 P

7 } C* G i4 e$ N/ {& g4 T

, v) Z' \+ ~# \7 e! N+ |4 ~         }3 V& e7 k F8 E7 g( F3 V

8 @* k9 W) G5 \ E9 J

2 n) A0 F" ]% Z8 f         require DT_ROOT.'/include/upload.class.php';: F9 [% F% a* Q9 o7 ^/ @9 ?) e

/ O! U3 u$ m4 j; Z! ]2 V# U

+ E4 ?5 M3 \( u$ v4 r {& A8 c   ; N* X' V0 Q" Z6 @4 G

% @0 U E$ N- \/ e5 e- g

9 T6 M) S; E/ U         $ext = file_ext($_FILES['file']['name']);: O/ v) B7 }: R1 O ], G/ S: |

4 ^" y6 V% s& B8 j3 K- A

* e/ n$ H* r) e C. @5 w/ }         $name = 'avatar'.$_userid.'.'.$ext; 0 u8 [! g# C1 X, f+ G

9 s& m5 Q& f7 G) G7 Y2 }

9 Z! y. J. ~% R* C9 K! [         $file = DT_ROOT.'/file/temp/'.$name; ; |, g2 [5 T$ p0 D/ b; f' _, y

7 ?6 E- a/ I7 O) I! m2 H& D

$ f) }6 R0 D `7 }4 ]! [, q" k   9 x3 d) v3 Z) Q, B# q

# C- F! z! e/ l j9 N% x

6 ~8 Q& Z2 P' s0 W         if(is_file($file)) file_del($file); # t+ b" q) T8 ?9 P3 H

* [8 x3 _1 i2 |# F, o$ z! N9 B

2 Z0 W! X C, ]: E# o         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');9 z' B5 K/ q4 C4 R; x3 H! `) ^

. J6 L; @& _0 k* k

7 }4 u2 j' i8 z9 H- a- f6 A- r4 ?' \  , S# M9 H, \. X. `' Y' z

$ _6 r" P& ?! \( N

, \! K4 J& A. g1 c q7 x$ G8 v         $upload->adduserid = false; ; e7 J2 O$ n: H8 t6 D6 b

1 ?9 B5 e0 U2 x) Z( X* t) x

* {* i7 @, p' _( }: j   # J& g A4 v$ q: ?

- v) u; Y) c* }! k8 p

- _! X q6 \/ @         if($upload->save()) {( B' i/ I7 ^ }$ [

" D! Z& B+ O* X$ W, F* r' Z

5 ], [. F- A! ?: _" c! D# D e. s0 _             ...: v! N& e6 Q% V. W. V6 p

' ~2 v8 b/ w7 j3 m/ A5 f* g

1 c# L2 e. q& o3 [& a* T         } else { p8 T0 T- ~: v% A2 [8 O+ h

( n- B. y5 ?5 s) O- ?) I: |0 \+ F

( |# h5 y, S/ s7 ^; F. ]' o             ... ( t9 o, ~9 D0 A) e

+ R) X& m) f: ~

( `: _- h& d* l( h' B9 C5 r3 O3 @         }( v) l) C4 H% u: h

- B$ P3 M5 ]1 |! ?% J% h

+ |& @ G+ p: ~     break; # I" z! T L+ R h# _

' E$ l8 A$ [$ h+ b& ]3 w

3 ~$ w+ r$ n7 P# y+ l 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。) t8 @. M* ^0 p4 F

; ^+ E* x) P" g

4 L0 B, j) m' d' \ upload对象构造函数如下,include/upload.class.php:25:. i, @) }) e. {$ `% h s

% A, a( R8 Y* t; U- |) u" v

% [1 n5 j2 w# S+ W' |0 g <?phpclass upload { P, ^$ o% S) N9 j

# q6 z7 O3 O2 v1 }6 E% h8 t

+ u$ Z5 A1 X1 h% u     function __construct($_file, $savepath, $savename = '', $fileformat = '') {1 D* X2 Q! s$ N0 E- G0 r. o- O

# S9 V* {, h5 J: p' d. ~: l4 i3 a

8 R' Y; I I3 S' a& [2 b( ~) V         global $DT, $_userid;# r8 ]' S$ r7 [. O% A9 _

% L: K8 i* V$ E

/ n4 A a! B W         foreach($_file as $file) {$ b$ _4 |* F& N+ ]2 M! I

& d( l N: l! T" }& ?& i6 ]

/ c0 Q, g9 w: f             $this->file = $file['tmp_name'];9 m) j; {6 S% e9 S& U( E: m

" \, [- I! N2 r" v

& ^9 M y# ^3 y# M7 S1 P) O             $this->file_name = $file['name'];; G* M2 q' p) `3 j

5 |) k/ {: W% c+ P2 Z

8 q1 ]# D6 Q% L8 D9 a' h             $this->file_size = $file['size']; 4 l& S% c/ M! I3 w

1 q* k1 V9 i. N0 c5 B

. `% ?" x" X) m3 {+ g             $this->file_type = $file['type']; / ^1 }0 z n6 m& l+ q" B- u

0 h5 ^. E& l% G1 B/ D0 S& Y

! |8 T& O: B8 i+ Y3 D r) J8 g             $this->file_error = $file['error']; 9 l/ k' X7 w* h4 [% H5 C

2 D! o. f+ e- d. C: ]' A: y

* V7 N! P$ Q+ L# }   - {8 p7 u4 P/ K1 Y$ l

6 { h0 ?2 ~+ ?" V: w

4 t$ `/ W* L2 b: M         }, N& D- n/ C, V9 G3 N5 C

. o( u/ B. |9 R# C

. g* e* N( x) L0 J2 ^6 L3 P         $this->userid = $_userid; ' e8 f; k' P, P9 U- d# Q, U

0 U8 O. Y+ y! O, _* a: J4 \+ h

7 P t; k7 |& N0 Y         $this->ext = file_ext($this->file_name);4 s. a9 s# P5 s# D2 ^ F( S

7 G1 P& x: L/ C% e$ L2 J2 ?- F

" C$ f- e, D% g" b7 D: o6 U         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];6 Y! \( {) ]) I3 @2 s) j

- X, h+ A% e. E5 Z% A! F: m

: ?( r' Q/ L/ u l         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;' l( Y6 Y* P' A

5 C- i( _; W# p' w

' `5 }9 C: H2 h& o9 T5 h( a) e         $this->savepath = $savepath; & C$ ^3 X; J) W

# J4 E7 C0 ]+ T' e* t6 G

7 A, r" N- [) K5 T' Z J/ H         $this->savename = $savename;3 z" h' \7 \+ v4 F. K4 u& E+ r

6 n& n ^* B; L0 Z* t& x

% t* t+ S- Z0 o: g) o A     }}" @# z9 x5 k6 {6 m2 }

- I2 B( A/ {7 M5 @- _

2 C5 M; T# }# A9 b" ~9 T 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。: ]2 B' h/ c" G# b2 o0 R

1 L" d/ v7 ~( V: C+ M% c1 N

' c2 ]. y8 x; z; a. V 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 " E. n1 s J/ R7 h: r

3 p( }' g' v2 X: q

7 d V5 N1 u" K* D# N( ?2 z $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( n* a0 _: O# J0 p0 ~

A$ u0 N1 I4 q& K x7 Y

7 Q p2 Y# T& Q7 ^; W 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:- _, q/ t+ |% x- q6 n C+ h

' s3 \3 z7 Y* }) S1 p

4 w- s) e* o" G$ u4 W# ^4 a$ x   q$ Q/ a! D3 |" d7 t2 N) a/ r

9 G0 u% ]/ t; Z9 Q/ ]; Y; ?* n

8 U/ Q9 v& M: I/ `$ { 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: # o6 m$ a8 x/ }

# c( X. Q9 {) w3 C) g

) H6 _8 f$ C" w0 O4 N8 M# W <?phpclass upload {4 g- }# G$ o" ?7 n7 \, w& s

5 z2 J/ F9 l" x

; X V, D: X, U     function save() {4 Z( e% ?2 O* L7 M3 v

$ ?: ~* D! r. [5 D' j) ^

) c* |) v1 \1 N& O         include load('include.lang'); $ Q* \3 v2 x/ I" S5 @$ I$ _

! c4 h9 [! M2 @- @& [3 U

. g8 c$ F7 t; F" T3 e. p' L         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); + i5 a& w- P9 _' z1 O( s( W. v+ ^

8 y4 W) B; C/ i# w* U! \

- F/ b# o; c' ]2 w9 r( M" v0 s  $ ~' t% H& F/ S1 I j

9 W+ y/ S" d% o* c5 v. F8 x% d7 s

" W0 t- Y7 T* x5 @/ G         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); 6 U6 f. k8 K, ~& O" J- f4 J

: F* \0 N5 x( u0 t: I

; l2 x5 p' [8 T& ~$ s: L! v  ! s3 e4 T. |4 ?* `3 R% ]

+ u0 D5 ~ {$ }, z4 s4 D; P

3 j5 J! s# w% z7 o$ x4 q% M         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);0 _. m8 v& s7 J8 W6 @

- q/ ^* y L! s- M( @

% S) X6 v" v2 b& E6 P( q   1 K" J6 ?( Q4 ~, @

& f- d3 @5 e @" ]# g" \

2 g: Z9 `! G* W% C4 m3 _         $this->set_savepath($this->savepath); . p' ~2 c' z r

! _3 @( r/ r* S6 n) M- n S- ]$ h

! k" Y U8 C3 V, V0 x         $this->set_savename($this->savename); 0 g# w/ J, z: n& p

# T4 Y! L" H+ K8 D

" q T+ ]4 t9 s4 B   % U9 D- m+ R* f

3 [ u( n( f! t' G# K' l' C/ |

9 S- j: q+ ]9 q' L/ x& {: \2 X         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); . [" Q4 D9 S( j: f

u6 f& F* J5 m2 O- E' g

* V; h! ~5 p! U- z/ I0 ~         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); & P+ x4 V( S! \6 }

- s% X* e& ?. J& X2 N8 N% V

! T$ k3 A7 l g: A- c( d4 a T         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); 6 p8 g0 A* i4 h) c3 I6 w% i0 [, R

7 R3 I6 g3 f! y! B5 @ t

# v+ A* O4 ]3 Q+ L- h   " C3 E7 A" e4 E0 J

' M" [7 [. M* o. D

/ Q8 C9 a1 N- l         $this->image = $this->is_image(); % b8 L7 y8 N. F6 x. Q4 W

3 ?* x3 L8 B6 K3 e4 E

. `- [2 f! U' Y+ [( O         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); " Z9 M9 x. ]! Z) C- b- l7 j

$ _6 s+ Q& x/ q" o3 l. v2 m

9 i. o, s& Y2 \         return true;8 {2 e/ [0 L0 Q/ V

: r9 S2 Y3 Z4 [

6 D+ J2 v' ?% V! c1 ^3 m     }}; y+ s& g! @. V( L7 U; [0 q3 W

/ E! V, |* T. p- K' `

: v8 h5 s- s' c: z) ?" O1 P 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:+ u0 O. Y" k, n/ X; k4 W

2 U; t( j: F2 I4 |5 ?4 r$ b% F. {

# R6 i6 I) h- J. g9 U0 h# g <?php * B$ T5 l5 g W& ?: o* v

9 P+ @: W0 Q: J+ Q' |

% k$ p9 X6 ]6 N6 ~6 f: [     function is_allow() {" J3 E2 Q& u5 ~9 F

. u' E# P! d7 c; |" `

: e0 k. ?' I9 p, }4 O         if(!$this->fileformat) return false; 2 e+ Y, j! g* N) g6 g- F5 t) d5 h: Y

; b4 e4 ?* z- f7 Q( M

/ x% V3 S" W4 V0 Y+ E0 \         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;1 Q: V* j+ A7 ]3 O* l' S- U7 G( T

0 e/ K; \! p9 r- H, b& o

1 d+ `& t- M+ B, \8 ^" Z1 d         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;! b' s* N0 e3 R

. q+ J& t; d% L4 L, K8 v0 W

$ U5 S" R. g3 Q9 l         return true; " h, U, B- G& I1 `* H

! \/ o3 T( C# u* F

( i4 E5 P) r* { c0 Q, s     }' @+ {. d" l* }* N/ E3 \2 g3 A5 U9 R

_: S6 N, P2 o9 P. n

: |) v- R1 J$ D3 l 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 ( V3 ~( M: T7 Y, L+ ~

/ i" r+ [+ r) L% a+ A

2 u' u$ |8 H( v) _: O 接着会进行真正的保存。通过$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文件。 $ X# D! e9 L6 T6 W

. z1 }. k2 g) c

8 \& K5 a- {5 E3 N/ v8 F U 漏洞利用0 q& d8 I `6 ^- T: ^7 }$ Y; Q

1 v" t/ `: x& M

4 R8 k( |% Y3 k! j8 y! `2 A 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。0 M6 l9 i, H) T

3 }/ y4 t, j, l

% k% D8 C- Q4 Y* P2 T   : M0 X* u( e+ {. ^% {

! J7 R6 \" v9 S. Q

1 E4 K5 b; ^: g3 |3 z6 [ s! X 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid% _: E) w" ^4 |% {+ Q8 B

, G* L, T. `; B6 }7 b# M! @. G

/ t( X3 U* C1 H) v, R: F* g 不过实际利用上会有一定的限制。9 Y1 S4 @" B* P" Q; M& |! Y

1 J1 V( i# k0 \/ y) e

% T$ ~ s9 U1 W' b8 C 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 * S) E5 S0 Y7 f) Y

) z6 u) D! W2 F/ f: _

0 S% t5 U1 A$ z/ C8 e9 C: |4 N2 `  3 F; _: w" E5 h- M

. U& `) z" h A: |( f( i- G

* v6 w5 |+ B$ B9 P2 L 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:8 N7 A7 y" N( U4 y2 d T, u' a/ Z

7 R2 z3 r$ M3 T; w

# T/ r$ }$ |0 _2 i% w& J 省略...$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 Y$ v; ~& Q' Y, A2 `1 m" t

; e$ F0 n" D8 Q- Z

9 A. u/ U, Q, P( \9 C 因此要利用成功就需要条件竞争了。4 p9 w8 c3 T; y7 V3 W/ b# Q

, Z7 c0 s% Q* V9 `

! a! S; @$ ^- G 补丁分析 0 g$ [0 c* a, w- P+ d

( a5 `+ x2 f8 f6 o" u1 N5 w: Z( W

- D. R; S. w7 ~1 O! t, o$ m' g9 ]  9 W' J/ C3 E* k: ]! H6 ~

0 z4 X6 q3 K* w2 [! s$ H/ _! k

* X! Y ?$ m9 r0 ]: c 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:7 V) u" {+ u! S- ^7 L0 }; t; \

! W. x8 M6 V! s7 |4 c4 a( o

. Y! `/ S6 y+ e- Y7 r function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}; Z- z7 E" z* g/ S

' T( ~! J, S* \" |

0 M3 Q) v# K, i! F$ e  6 Z/ Q" \% h8 |8 A# {

: s5 n$ e* q {5 Q- a% X+ V

# K# [ D. ^; k& a: d2 M0 X. O. E 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 3 k- q) S L/ ^1 f/ h0 I4 R L

* ^0 }8 y4 l8 ]* l' d, F

7 e; @7 b- ]7 Z0 i Q7 m: ^ 在is_allow()中增加对$this->savename的二次检查。 / y6 b( n3 M" z3 ]

, I+ |9 ~* a, f1 @

* ^# ?1 g, t0 W- s, c 最后9 ]( y/ E" w( x6 J( @

! p" Q6 K7 j P' X; O% l. }) P* I

+ m& G# R, h/ E/ ? 嘛,祝各位大师傅中秋快乐!( j) m! B; Q b

|7 E" d, W1 Z2 Z

- M# B- {7 s9 \) ~   ; g6 k. Q7 \6 |5 x9 d

8 S0 n6 d* `1 @4 v8 y* z, U$ n+ h" n9 {. L
回复

使用道具 举报

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

本版积分规则

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