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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
" @( _% P% J! F, {, r: @7 a

! n# S K" q2 V

6 J) P7 C6 P5 N( y3 B3 z: V( `

- q* S+ Y7 M" w6 N1 y# V$ U8 D9 o- i' } 前言 ; q* q2 I& p) { v. N

6 R6 o, A# z' q' p$ [/ ]+ o

/ a/ N, ~3 g1 Y0 X0 H- k* k6 q 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。0 u% K9 D4 m% [ H' K7 \

' H0 y1 n y. j" f. [& G

6 X+ s% f6 S9 y$ I' @6 B3 p   7 O9 u0 _* n1 E# N1 Y5 s, w

- c) C1 t$ p% Q3 ~+ _

. L* p5 `1 p3 A5 A# } 漏洞分析5 {: [' n4 w! ?/ f

: C# c+ d1 a0 J. x+ C

. [: q8 I# G6 Q4 l 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:4 y7 K3 |/ O( e9 w

! i6 C2 |2 I1 \9 E5 f$ ]

, W# G# H7 Y( @+ q: H; x   9 O, R5 s( z# W4 ]# y1 f

& J- H1 m" u0 H$ O$ ~

. q/ A# c9 \9 D. D4 l4 F" } 对应着avatar.inc.php代码如下:2 r/ I, c( |& P# L3 u

# r4 o! C4 ~- G) j4 x2 Z

5 W' [4 l1 q( s4 f( V <?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) { 5 \# |) T( e& g8 Z# _+ t

# t4 A, v* o* t+ V9 q

/ N' ~ y0 \: u/ S/ m     case 'upload': $ w8 T- a/ ^( l/ K5 S5 h

# c4 R& D8 \9 {* K. Q* [% i

1 s. u+ b: C) N         if(!$_FILES['file']['size']) {+ R) N( m1 ~( @$ x: c& s

* S& M; _/ X2 s# }

4 p# G" ? i, e% Y5 f. W0 R             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); 5 ^! }0 ]# E+ X- X/ F6 p

+ p( s5 d! q4 l% X9 Y

5 q8 \5 ~* D8 u- \' J* ], R             exit('{"error":1,"message":"Error FILE"}');/ G, w# z3 K9 [% }4 C* V

8 F5 O$ [4 a. D7 \* s

3 ?' r- i+ v7 m$ I         } ' z4 L! a) k F' h

4 o0 e2 M' { W8 z; d0 V

2 s/ I0 v3 }% S1 ?0 U# f1 |         require DT_ROOT.'/include/upload.class.php';/ [/ t5 w' w) B8 z

' J2 e) w* x/ y2 y8 [/ b" o+ l

2 H- c6 Z% D4 X# B" [/ V6 I   7 y, f. @9 Z) R# o' J

7 K! W. |1 ?/ w3 e" T+ V$ \

8 k/ j9 C4 e: n3 e1 r) O         $ext = file_ext($_FILES['file']['name']);% k7 U M" f3 h, T* P

( r4 k7 W5 u9 H

* I9 B& R3 ~ N9 R3 A         $name = 'avatar'.$_userid.'.'.$ext; 0 h# }% v5 G% u! e" k

# a7 N( b( [: R5 F1 V2 g

8 y- {8 E/ y9 e$ d& A+ M4 _         $file = DT_ROOT.'/file/temp/'.$name; 6 t5 g7 O. u8 T1 o5 Z: S

1 [: s3 R6 O7 V3 K1 }

0 |) l0 d" l$ N2 q, N% _4 c   9 P, ^9 m" d0 I- D

U4 a# n9 @1 E- c8 u

v% N# P- r& n' y         if(is_file($file)) file_del($file); 5 q- b8 P& w( i0 {2 B1 @

: t2 X, u/ Q( g8 c! Z: T

& k3 T3 x; T' [1 i: k3 @         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); 2 X. s T0 Y" m- t

" H: U3 }* Y! Y* X9 m. G f# l

$ e; E$ g$ L1 h1 m& Y+ J2 X1 }  ' N, E/ L$ J( O' K& ?/ }% }! K

) n. f2 M6 N8 E' N

, n. g2 V( O5 N         $upload->adduserid = false;3 U7 O8 S* ^$ M& b" x6 C0 D

! U9 e$ S! Q- u( n2 m- W

4 {- `# D/ |# I8 @6 ]+ e  : g; w; q% v9 Q5 m; ^7 U

. p: d* K4 P: @; f7 W+ V

: j/ q. U- M- S, O' p" Y2 z# e" j8 p         if($upload->save()) {9 S' s- x/ q% M, y0 r

- U. _9 X1 a. ?8 d7 N2 E$ x

! A* O" M+ C3 |( a             ... 7 b: m! C* H: V6 N' v2 o' k$ k$ Z P- L2 k

- l: ~. ^0 b' V/ I% V4 N

/ {3 [. R$ W8 J6 P# g/ O8 z5 j$ F/ R         } else {4 S( m2 O' B; J0 V6 Z5 p; l

- c; e' z0 F; j9 F+ Q! v* x5 U

9 H1 F$ T% w% _5 A" O$ k' q' E             ...' j1 W" i; [7 d u- T5 `) N) t5 r& g

" \8 p9 z) c" [0 Z- I( I

6 J. U$ h% L: X+ Y# ?7 t3 J         }& x w( W7 k4 y: v7 G: s

% r& a0 Y* L, e- T+ P

u7 f; M3 {' P7 |8 \     break; % S9 X, }! p. A: U1 G8 Q" `+ ^

- a9 C+ C8 l0 H! J

3 Q" `5 H6 v4 f 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。7 q: ^- i# B4 k( B

4 u7 m% E, v: c3 {# E

' r, y+ i7 A, Z( }$ {) y4 j upload对象构造函数如下,include/upload.class.php:25: 1 Q( O( L, V6 v- `) `6 i: @& z, v

, D8 B% A) U$ a

/ E8 f$ R! Z$ U7 F; ]+ L <?phpclass upload {! S* }; ]/ [# L! R5 a9 X

. T+ b, p* N% \- l. P

1 g/ Q5 a. w4 n/ j     function __construct($_file, $savepath, $savename = '', $fileformat = '') {% L1 \- D- p$ A/ ]! B

0 @- {* Z% ]" } B

# j' x( f' }& v z# g$ c         global $DT, $_userid; . d, d, c1 W. P* q& g" q5 o# S

3 l6 w+ S$ C7 k' a6 p

- J# t' M- {" G$ S) p A- p         foreach($_file as $file) { # O$ R% l3 }) F

3 @$ l- H( e! V% e: F F; [

/ u* c1 B* @3 N- L' y             $this->file = $file['tmp_name'];' `" |% g5 ?8 p) g7 e* `3 K! ?4 z: t

, \& T% Y+ b6 \6 ]1 T

' X6 L" T5 T7 b: V* S             $this->file_name = $file['name']; # M% t" f; k# o5 s7 N# G1 y

9 `5 z8 i V2 {7 ~

4 o N2 ]* U8 `+ [5 k+ ^             $this->file_size = $file['size'];+ A; o1 g5 l3 H6 P

: i, }& H4 H) y \! n9 ]( M; G. Z: J

, U& e& B) E7 g" Y             $this->file_type = $file['type']; {/ J8 k" t1 K2 D" i

1 ^; @2 ^7 w2 N1 s+ e# A4 Z3 t

e3 [) ]% _5 ]             $this->file_error = $file['error'];2 z# m% v) W* O% t, e# I

3 ?' p" B# B" U0 v% ?/ s$ ]7 ^

0 \) m% B! _) f6 n  - E+ `6 B% a2 f' U d( F' {( m5 A, W

6 ]5 _& ~3 {! m( ^5 G8 M. _

8 q3 Z0 A$ m, \1 Q- {( @7 i         } + ~! M* }& S9 {

# b, z+ T m, a( `

+ ]1 @% C J" X         $this->userid = $_userid; + v$ Q; \) A. U X+ m# N

( K: W8 T1 v) G) t+ s/ V0 p

4 I3 B9 D5 T5 w+ ~# N/ C         $this->ext = file_ext($this->file_name); 2 _0 l; ^' t: D3 O3 A- R# O d9 T

2 E: ^" W6 C( s) N' R: [' Q

/ }: J- A7 k/ O W7 ^         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; ) ], X6 c8 b1 D

\) B8 B9 s' g" ?: q! e2 [; e* s7 e

' O/ g1 {/ C; D, [) H, v- R& q$ r         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;; Y7 r, k @) g3 L6 ~) p

3 O- F: o4 N& B: S/ f, s0 _

6 D2 E" ~2 J3 U' G         $this->savepath = $savepath; ) H# m+ v' F) ^( M5 o

3 Q) j c7 O* x/ {

2 g8 ~2 z, m" m0 T         $this->savename = $savename;1 @3 N- d. k) Y' r4 c; W. C! Z0 m

* j' H' F3 [. b1 t

. @/ } S9 c% h: _! R1 G& @$ J     }}& w+ B6 n6 [. ] I( L# l6 n% ?4 Y; W8 O

. p O$ a( G6 n5 v( Q" \" G

0 R5 R- g$ x+ K, H6 S- ] 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 + o* e, g( w t: T& Z

2 H" f' M; b) t3 H" C- I+ V+ N1 V

7 x: z" x Y4 D: D 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 5 W w0 ^. q; D: b3 I6 Z

2 N/ O" T" L n

' Y9 z B- T: |1 I& ? $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 * j# c3 M4 s, e" M6 F

5 D# P5 y% w: z& p4 c

0 Q3 N3 Q5 n4 t3 o4 [2 M 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: & F5 g w) X# p( W9 L6 { e \; t; K

. t7 C$ w8 i$ x* C" `6 X6 l' N

) V- @* u1 c2 _! O3 W) A, N |& Y0 z  6 m7 \0 ~. T. }9 z8 E, o7 k$ g0 q

" j. b* m* J6 i! s6 p

4 g$ w+ A+ l; l" k+ ~: @ 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: 5 q8 A1 E% A% G* [8 Z& y

& M, I u0 K. q# g8 \6 x

; Q- t6 a2 m3 R1 n# D: {9 N <?phpclass upload { ( j: f4 `6 z# N. K6 m" I1 z

- x1 @/ x! X( e8 s

g2 e* N- j6 i# b0 y V' x+ N     function save() { % D9 U- R! ? Z- g, N+ m c

% W B5 `( v! f7 k, z

c1 j* |0 w: \- S         include load('include.lang'); D8 g6 J2 Y7 F$ t8 |. N7 ]

6 x3 Q% C& L5 `5 K

/ a/ ~' {; h# ^         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');9 r4 W. h7 a9 P: S" V

4 M2 R) }; d& \' A2 `

1 T) _( N' R5 Y# w  ' w' H7 F J1 U! S: p

5 `- G! n \! e g1 K6 N

, Q( ^7 E) a# `; g9 o1 G1 m         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');3 [0 N- i" r/ h

; d, d! k, Q* T6 A) \( c3 Q! r

) n; }) e7 }( u& D+ ~  # c1 t1 w% I) m' c( W. d

* b, g$ p, R, p" I: @

' ^" t' f+ w+ P8 O         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); + Y1 j. F- A% o' n+ f

3 z: H$ l5 }& r" \7 Z( Z

2 m: i- w+ F0 J2 p0 Z3 Q  " W9 K1 n% M- b- `( Z! C. A1 ]

* k5 k9 x J7 v8 |& L6 P

% D' r( r3 f/ G         $this->set_savepath($this->savepath); ( q9 \; s+ c( F: f9 c( j* ^

" p. }% A7 [; Z

! K5 n7 T1 g, ^ P, S: T         $this->set_savename($this->savename);6 R& o" P. {; q5 q: l

8 R4 f4 i. ~* D/ Z

* ?+ ~2 E% }8 c" N   % ^1 ?3 L* [/ o+ V, F

/ E# J! \: u: v

% P$ D9 M; U- {8 O         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); B0 K1 G( I4 P

/ S: l, E4 n2 \* ?

& u5 Q4 m: \4 r# g: u         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);- y5 `; \. p, ~# M

: I9 y+ ^+ L: N) x' @; k; F8 I

! c4 I6 q7 S& X2 R1 W" Y1 ?: O         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); 2 L+ a3 M2 e- Y1 w) s* C

1 i5 {* s" F+ L4 U. k- F

/ ~. p! x( k0 y4 Z/ |  8 d4 N1 ]$ Z& J M$ [

* j# U; l1 s4 z4 Z

/ C! @; p% y% i; ]* t5 t* J         $this->image = $this->is_image(); & j, f' w. M, _. D* d3 X

- I, z7 y8 @2 l3 T

+ @* r+ {1 S0 R7 n) j) T         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);7 N& b! C1 h% S

7 S0 K& V/ m3 L4 d

, K. r ~ o# }2 N/ T1 K         return true; 3 x7 I1 N- y! [# f2 K+ Q$ V& L9 J* U3 g

0 y% V$ ? d& a8 j3 [

6 X# D: O$ I* Z Y     }} ; W; D7 |& a6 L- _" @- }: D I

) G+ E8 C9 s6 m1 O8 R

: {& i+ z+ U% b7 B1 } 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: % g7 _, V" X5 f7 K. o. H+ _/ O

7 o6 z' R* q2 J, Y

1 U9 }1 |; c2 L# S <?php, E5 \9 v. ^4 y8 O. K9 H+ E9 O

2 N. }; K8 e9 x! U7 a

# E( F+ |+ X1 K0 l; T) Q0 o. C     function is_allow() { ; Q* u) ~% k O% ~! j0 W8 v: a( U

2 E. e. F, Y! a8 I- k& k+ A

6 Q/ K' C& Y3 k8 R/ F, [4 u! u! m         if(!$this->fileformat) return false; p, a* l; R/ J U! J" A

+ `$ R6 u0 M3 |, i% l+ ^2 \0 z( R

. a: P* k/ \/ d. `         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;" [! _, ~: c$ W& B

, y. r! C9 M; N5 r j& E7 ]

; c: {: e% Q: d6 e         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;7 l- h* J0 W7 E) L7 `' J' D$ b* D- q5 z

: y- A! q3 f' E+ ?

7 \' G; E0 e+ F5 g9 d9 j G         return true;' o5 {- Q$ L, m# g; a3 i8 l

- N: L* H7 T1 N$ O

. f* ^, O1 Y) e     }/ [7 f1 f8 E& m& n/ N5 y

" u' K( _ [" N( `3 \6 z

" C3 @! h S& H4 h! V" U, e 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 # O& q; ]3 v {7 I

9 ?* }7 ^* J* C7 A; p( ~

! N3 { r0 m+ m' s) \ 接着会进行真正的保存。通过$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文件。 : w2 d! E( b3 q) B* Y

# q9 G; ]" E) h

# C9 t# ]4 C0 c( F* `' F: j 漏洞利用- F {+ @* w$ k4 @2 i$ H. ^( ^9 i

/ A1 G6 `% f. o* a ?3 l

0 p8 p- @+ Q/ A 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。6 M; J( C/ N; D" v; c- y* ? z: [

! Y6 L( {; V2 T- x: k% ?$ K

& q) ^2 e, }! T0 m9 P3 V+ G) b r   8 k4 V# t6 S {) t" y, \1 U

- |5 f# _, A" G/ w N% r" ]7 {

/ U+ W; p4 }0 f$ V" V 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid ( R( w% a8 s8 j2 c

; C& b, A* k9 v$ P7 s

7 e7 Z3 ^3 G# f8 R! I 不过实际利用上会有一定的限制。* V# u% b* i9 L' q8 `: y2 I

7 y8 {0 f: C7 R T( p

* ?* p$ d( r+ p$ \; }: j! |9 r: R3 s; N 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。+ |- k+ F+ l5 ]% C! @

* S9 U e) Y5 b" f

3 |! ^+ L" G$ I: M  ( v9 y# t' z: v- D ]7 @% l% E0 h

7 L& k1 {/ |0 z8 Q; {

; y$ [' ~: [; m Q6 H 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:! g) l+ O. u r; }2 Y* N

# q, v0 G' T4 |; B

2 ?. f8 @. X0 y: I3 D7 f+ d3 ] 省略...$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 h2 {8 C- c5 H0 D4 ]" M+ P$ C

+ o! k4 O2 r( q0 ]& I; o. A

# v% }' L* E* y* H" h 因此要利用成功就需要条件竞争了。 7 Q. P* r+ M- `6 p) N1 D; S" y

# e/ `( m9 u& B" G- z

& d, h; j9 Q/ g O 补丁分析! L3 U$ n g# z3 d

% B6 L8 C5 @- {8 Y. u- G

" D C" z1 n, ~3 ^+ h: Z   9 s4 b: o* Y, y

- V5 Q9 N. b' F: ~

q. L4 [$ R" ` 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: " k. Z) s3 y, b n

8 m" _/ B+ }. W+ U

/ n% E9 {; ^( U# S function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}) r! [) p3 I6 \5 i0 Z. H9 R2 n

# Z2 Q: d! {5 H

& X5 A# d& g& s, \1 A  ' J% h2 }) f0 C$ p2 X

+ ?) t3 e8 H7 ?, P4 Z/ c5 s

# e+ ~( K" y7 ~! U" C# J! T- j 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。/ @0 `, T; s+ R# }. L/ q

. I& C, P2 F% p: H

/ z `1 B3 O* {/ @ 在is_allow()中增加对$this->savename的二次检查。5 P# r- N4 x: z3 f7 r

- {# A5 |- `8 w; ^) S- b

2 b4 t( e+ ]1 R3 z1 {$ i# X 最后 + J- e$ L6 d5 |5 _6 V: u

! f- M6 t" }/ \' I

1 M) a9 _* q; A1 ] 嘛,祝各位大师傅中秋快乐! # q. t; W0 ~- K+ O& F

9 s# g f* X0 Q/ z

6 w1 k y' K* x- q* ]: h( |  0 i' g8 Y3 L" S# J( z

$ R1 d. q1 [+ Z0 o* d
回复

使用道具 举报

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

本版积分规则

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