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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
5 j; {1 \5 w6 A! U

4 @1 _& R# J" Q! I; p- D/ H

2 ?7 }6 b' ~4 m: G6 p

! B0 B7 P/ b$ F5 j( J- p/ g 前言 ; v! s' D) a! ]( r/ {" _. L8 a. n

* x7 J% S. M* u5 S

- C- O2 `- K; {& r3 |* |/ ~1 f* O 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 % K" H% }' f4 [ w" p9 K9 F, ?

+ Q( p1 O# m& T! }2 L5 i: B

( G% u$ B7 S; y" Z( s# ]  & o w. c; C+ o3 ^

6 ?; K' X- ^/ H3 ^! J# o- {

8 b- L( q5 d2 I6 k/ M# m 漏洞分析 , i( r% g3 J# p' Y

% R! \$ C3 f* n1 e' `

4 }& G- q/ T! ]' K! e( \3 N6 O4 f$ Q 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: % Z' `; E9 L/ v- ~& S

" |" @; V. l2 ~9 I

9 e0 ?1 E( I' D0 ~4 f2 j# Z3 `8 ?   $ I3 M0 S8 H& a

$ o# D2 Z# C' G g" K0 ?& ?

' d% N5 E# _$ F& d, s 对应着avatar.inc.php代码如下:( d) h$ i; S2 q: ~

& ?5 ~' \' s$ ^0 s% H8 y% I; I

6 `3 N$ f! ^; E5 K6 Z <?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) {' i. k6 s' h7 ~

' q/ o& f2 R; B- q0 A

& h: V/ r4 H3 L9 K* G; c* k- H     case 'upload':* R! L, B+ ~8 o- |& [$ {

: e& Y- L8 `7 Z8 Q0 O; D

% W5 G% V% ~+ o5 V- h4 n8 @; r5 u         if(!$_FILES['file']['size']) { 8 s& x" {2 f8 b7 y6 b, U

! P4 }& i4 W4 h! t/ P

. z |$ M& q- W+ D' {* J             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); - U6 R" ]! d) H( x' A, R

0 m/ g* b1 D2 O9 E `) m

7 ^0 Y: y. o* R" X+ e             exit('{"error":1,"message":"Error FILE"}');5 t' }* p" n& P+ D2 c

9 n8 G2 w$ j$ @) \" L

( ~( {% v: n% a1 w) E         } , w7 l) Q/ m h) }4 X

" Q- E; ~$ w8 n+ s3 o

9 p2 \- B8 }) f; ~/ S! r         require DT_ROOT.'/include/upload.class.php'; 8 x, a+ g4 `: D, U1 H& {! V9 L

" k* w o C: g1 x) Q* T

; o7 b# ~9 p8 x9 g- o  " D% G9 M9 a C! _3 q2 l8 e4 L

! X2 i( l* I# b6 V

8 x0 u4 v& p3 W- e         $ext = file_ext($_FILES['file']['name']); {, `( `% u% {, B& g) n

2 U6 z$ w/ H! P, a

2 _7 g+ {2 F8 K2 x         $name = 'avatar'.$_userid.'.'.$ext;9 S& `) Z/ Z @# o k5 R# K$ t

3 V1 s. `, p0 v1 y

2 X0 a5 U5 \( P7 q         $file = DT_ROOT.'/file/temp/'.$name; . g9 f+ @& R; }; B' |/ U( k' A

7 F, G2 p/ R! N4 e9 [9 j

* p- v, T4 M5 m* g& h) v   / h/ q0 F. G! S

6 F& @4 b9 m2 }. u3 `4 J

! U5 e( K" p' ~( q& s+ Y         if(is_file($file)) file_del($file);# {& [8 T1 n/ {$ U

3 [* A0 d2 d" D% c

5 q( Y1 d2 G: H5 `! C' M5 m/ x         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); 4 K) i M2 F7 `" F

+ G& ^! ]- J! I1 W% |2 ]( p0 J9 Y

: j+ Z% D6 K r$ ~; ^  + I3 [% p- C( g- p3 e* `

. R# C% L" l9 ]

, M" P+ n' ]. H# R9 N         $upload->adduserid = false; 7 W. W4 G3 E1 ^4 Y

0 W+ g, P; w8 K5 p

! w% G; u" `* \* @/ ?   # d! v6 u7 O3 p) }7 X! k5 o

% L. n4 _- l% J

; w6 T+ F$ s/ p/ r3 c         if($upload->save()) {* _" t2 N: g1 g) X9 ?

2 X9 g3 v4 d. K& Z4 U+ W

. m; i/ n" O: p1 @" B             ... * k7 O8 X" O4 K5 v+ b

/ d( U* F" r+ n' P

* z7 P) s3 A: R* @         } else { ! j# c6 ]' i; p

F5 W) Y" C/ f; k- n- z: H

( G3 i& A, ^0 j             ...) n! U$ ]6 D1 b. W3 _

2 s6 b% k3 T0 u [; Y; Z1 l

- k4 U3 E7 x! z: Y         } & F. v0 D( G: x# }' n# [) \" O8 w2 H% ^

' B, M, j2 u% t. q! }( Z

7 D4 q" o" Y" U, D- R" v     break; O, n2 t5 {$ @/ h" }% n/ Y: e3 `

: e' ?% X" ~0 D% a, Z

! U+ L1 ]# m: ^3 Q1 w; z3 w9 ^ U" g 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 ( S: Y- _! X+ Z6 @/ M5 e+ j, Z

- I! A1 T. I' q/ E

8 R, `. ?5 e. \3 p upload对象构造函数如下,include/upload.class.php:25:) Y8 H1 ]5 }% P# [5 D

" q) F# G! Y) D7 |. ~

7 f4 n0 k6 I) V3 M6 d: l <?phpclass upload { % c! J6 m/ r* A8 v& g; Y0 z" F7 Z+ K

- r' g' c$ i6 M" n1 y$ t' z( W

9 |+ R4 B+ r& ]9 v- o     function __construct($_file, $savepath, $savename = '', $fileformat = '') { 6 N! `+ H/ `5 J( N7 z

" U! n8 o% i- N/ u% T8 V

: A& ^1 Q! T- F         global $DT, $_userid; . {) h* z! Z9 i% V# ~( l

, Y( k4 m4 B' ]* L! l! h

% e! A, x% z' f9 ~' B         foreach($_file as $file) { 1 |" `: I# `6 x( Z; q' w

2 p$ M5 g1 p, Q! s% \& [6 @4 E

( K* {, I) Y! H" h. r# |             $this->file = $file['tmp_name']; 4 y# z' n; e. b5 ~7 q2 O) Y7 k& R. t

. ~+ {. |' t5 R& B+ p2 L

8 I5 f5 J9 Y! `% r" n0 S             $this->file_name = $file['name'];' J' p+ y$ w# N- c( ~; _

% }, A/ b. u- E# J4 g2 M: b

: E! r- f) U, u' K0 L$ X             $this->file_size = $file['size']; 6 Q# t3 r' u' L' {+ n

; R- W( A! E6 Z7 K- T: }" `' ]

( v& O1 P2 R! c, s2 m             $this->file_type = $file['type'];$ F0 t G5 j& G1 {1 Z: F

8 x) G3 ?, H# @# P# E: q b" H Z7 {

! m. N. |2 ]8 ?             $this->file_error = $file['error'];8 `. a3 O* G& X6 ^

1 `- P% O, f, P

3 ~$ a8 c# w" _  $ t* ~' [6 y) j; l3 L& t" W

$ A& |5 Y/ I& q) Q9 q k0 h; Y* o

& J1 o. n7 _9 |. G! o" z' F         }5 i8 G- v8 G4 T! j( s/ S& X6 M3 O

. @/ K8 l$ ^; E1 f3 s8 ?

1 z. Z) c# I8 y0 W/ |0 d$ p1 W' D         $this->userid = $_userid; % `0 }4 S3 r- V- {% K+ g- b9 e" y

/ S8 r; L: |* _7 \

+ x+ o7 s: [1 f" ^: v         $this->ext = file_ext($this->file_name); ) d" r) [% f5 u5 q. A

' p* L% U' \0 @' S5 V$ u

; ^$ i" B! @# \4 E7 c         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; . I% u) j& [" X* o- P: n

$ n3 C L2 z: A% f& D# i- u, K u: b

/ w% F9 ]8 Q4 @. {5 z) h         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;6 `7 p- _/ y$ Y: V* s

* c7 X0 R6 L5 G( ~, |* H6 f

' d2 }8 i4 J! `: e. B2 C$ k         $this->savepath = $savepath;! P! v. r/ H* E- [% R9 L, F

( d; x1 e3 p( T

- b' G/ k% H' U9 |% W         $this->savename = $savename;% {7 h7 |4 a0 o* `

. }5 k. r, z+ r, [4 |/ Z5 x& y" t

9 V C" ]% ~4 h4 K, ?. f4 `' {" z     }} 8 y% [* ?, S( v% f! N8 I

0 N |0 {4 g2 ^* C: b5 m4 J$ g. _

& ~( W" u: x' d; z" X) Q P; t4 m7 w 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。& ^' b# {0 Y1 B6 X0 }# ]

' z% M6 x; ]- a4 r K, g# l @

7 O( B* M: w5 D, h1 V% d# c$ V& G 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 2 W+ C% }8 q; E+ C& I; e

5 t1 E3 U7 s; X! F" Z }8 r% [& l

' C4 J& d# p" ^ $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.php5 R9 Y; A& _/ M9 f7 F' @

E2 y! B( O7 ~, e( R$ `! S

7 R( ]* k, r5 Q: s+ C H5 O 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: / {: Z1 W9 R, o6 t2 t8 x9 ~

7 ^# n) S1 d# ]- N/ W9 h

3 U! N/ d' W1 j0 H   2 h3 f' L2 H9 H

: ~" }3 j7 Z. H- {+ C; ]

& y& U. M/ O3 V! X# i1 R1 G 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: 8 y. C( e2 `1 a9 q) E

- R- N/ G: F$ h) U0 u

8 t3 B6 G0 ~) N <?phpclass upload { : L* z; E$ J3 r: g! t9 j( Q3 x

) g5 l. P! ^$ j6 X0 u }5 }, g

+ K) U8 q1 T% k- @5 D     function save() {% z1 W# l; ~' R7 J+ x

+ c- U! z& m4 Z/ d

# j9 w( Z/ n, E% P/ b; u% [         include load('include.lang'); u1 {/ l: h4 l1 D$ Q* ]

6 u& I8 h9 [6 A2 V8 Z: A$ q+ X

& ~3 z; z: @" m1 v8 D3 ^8 b         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); / a8 R* P4 l6 [* g8 p( q: t

! Y& Q4 X7 E" |0 P; J

4 S: O/ L" {2 P! x  , z3 a& G- o% Q. X; s

. O! `, m9 U7 ~% h! R0 P

8 y0 u) W4 m7 w+ J2 W+ @* A         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); ' \+ N5 p, @; c8 h8 N+ I" m) L

- @2 [4 E( d0 U# S/ n+ k

5 A: [: f+ h; K' a- D   0 J$ C8 p3 m+ \) I3 x; }

" l5 Q' M+ G# }. _0 g7 k

1 Y5 c3 q% h9 r6 u0 B- R         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); 1 e' x5 w. J' a; k- R

- x9 p& x3 c' O0 Y( Y, A

6 ~0 G) q" ?/ W   0 }+ ~% S5 E8 C3 c( P

" K3 G+ n. y. \2 n/ x5 _% Z

2 x& \- q t8 }. `# v& B         $this->set_savepath($this->savepath); ( B; x5 K3 a: n- h. w4 q: n

4 {; O8 \6 G% N# W& T% E

0 S, M1 h* f- Z% O8 c/ n& B5 ]. T         $this->set_savename($this->savename); : r% C) n5 K' q4 A$ t6 u4 ~

* }2 X6 p' d) [& @6 N. h4 w

% f$ s/ [9 \3 b+ G$ |9 _  $ H; M: \6 I8 s* V" a" p, b

& o% J D+ G5 d& r

5 T! Y6 q3 H1 a1 v% O# {9 r         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);; `/ j& n/ F/ _4 E3 }) U% D# X

% X. ^* y# i" b3 X" e6 m

5 G6 G# m. u( _4 V! o W1 F" S         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);9 c/ Q$ `! S3 o1 b$ d. j b

% s& H9 O5 m& j2 ?

% D( M! G7 w$ {7 e         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);2 F3 h5 [% B) g/ d4 K

; `4 d% A: x( {1 Q0 E4 m

( o! X& M! L, }$ O! v/ I, B# u   ) _3 D! A. C6 j% X# ~+ J2 V

$ Z) [; d9 l+ o0 t& o! ^ l, H

8 F' I% I9 N4 s: P/ i         $this->image = $this->is_image();" ?. V! b2 t" h7 F

7 n3 V* s% x5 h2 S1 N! N$ Q* \! N/ r6 J

3 A7 i4 I2 |( Z5 N% d- p8 k4 y         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);( s9 N: p$ n x+ }# |

: c- x. ] G! W3 b, j9 @

' @* y% A3 n8 P5 d- }% m         return true;( t) N; B% ~& m% f

! p4 D5 T* X( d# g0 A$ z$ D* }

) P. m; s! L7 [2 f& ]     }} & h \' G5 `" j$ x

* |( s& a+ Q( |" z: H, J- v

; F) N2 @# |6 ~ 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: . o. z2 D1 g5 ?' X# e1 l

) e: d3 L7 i+ S$ Y5 S+ C$ Q( c) ]

( o( z1 ^- }$ @* k/ a7 Q" l4 ? <?php- O; a( M8 N; p; o' K1 P

Q5 H4 T: Z2 K9 n

6 e3 N; M% A C6 w5 M( @: c# z# V     function is_allow() {7 B) D4 `' H! k2 o' @

6 V' }/ Y+ ?, q

' K/ L6 g! o3 A7 z2 t- M2 K         if(!$this->fileformat) return false; 2 b p' y2 C6 q7 K; y

! i: t0 T0 a9 u$ Z' l4 L: z3 X

: C/ b$ `; F- l) ?( s9 y         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;9 o3 {7 K! H, M4 o8 J8 t& s

) S/ D6 z' p6 s d" [7 }

2 i4 [- K. ]: p" 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;9 k0 k9 q# P2 u- w( [- y

/ N- B' q0 n% U

3 [, u2 ]1 W. i& t" B+ V9 o         return true; ) f) Y0 V" p/ _' x8 ~

& k1 a, i, k- x0 V' P

' d) ^1 O9 W' O     } + |6 p" u1 ?% O" ?% h

4 ?; X1 |( O5 `2 R \+ `- [* [0 R

+ ~ [& m- V, n: m8 Y2 B2 I. z 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。4 O. E4 V1 Z; m2 w3 N; W- }/ @

. C' N( V8 |! e1 Y- h& M

7 J: z& b" V+ s' C. L) C% ^- p 接着会进行真正的保存。通过$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文件。" [3 S. m' B6 P" y/ h

( X. A; l' M8 F% T

, x( h5 y' R5 ^- R1 i7 c( {5 L 漏洞利用 / H% H# E; {+ T& p9 u

3 S4 t% n9 R, Q8 W: Y

; ^- Z$ S4 p) f. B, n( ^ 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。1 \% @% |/ p* R2 Q

8 s7 R" K9 [$ t) H+ C

- u$ Z8 k8 `+ Y; F3 ]% [, `   " l3 t0 K+ D8 d& S

, h$ u8 n" L0 S% V

+ J4 s( I5 D8 A5 ?/ u' Q# L 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid A$ S& m4 e) q( t0 V; f

( u0 u; R/ A* h; @1 m7 ?$ Q6 ~

0 \- u: w! L# `' W4 A 不过实际利用上会有一定的限制。 w L; p1 l% Y' p0 ^7 Q& T2 j5 w

$ ]. P9 Z* D6 ~7 ]6 Y5 |

. v( ~$ W: t8 g' Q* x" j 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 % k4 j; `4 A& m, H1 { T1 a: Y# z

; T4 }* a; t- v: T

% s8 E- j0 ^7 f: `/ L# q+ C+ @   - {& J3 Z' v! U

: R" v1 R* S7 x, N, L& s3 t$ |

0 h' ~" f+ j" Q) ^- q* B+ h5 D 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: # e9 f% c( M5 V

7 } q, s! K f$ Z( Q7 M$ W9 K1 F

5 |: E* k! m) A7 W. S% [3 v 省略...$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]);省略... % \& E. X& h8 W: N- s2 y5 ^

2 t6 r8 R; p# {0 d. b4 M5 X# ~

: J( s% N+ z" ?/ `+ h 因此要利用成功就需要条件竞争了。 $ J3 d$ r" i+ \9 d0 K9 q

$ ?, z( M5 @6 `4 M

7 K/ F. W3 f+ g; { u 补丁分析$ ]5 A$ d8 w" { N9 ^, x

8 Z- d0 S, V: b; I' c. m' K, [

5 i% S( R! ]9 k; X   8 a& H* Y& x: |5 m

1 |$ v/ I, q- d1 ?. z

9 `: T+ o# G7 ?1 k 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: 5 W% o7 Y& O3 F# H: |

5 q4 ~. Q/ L. b3 x/ H, u

) g" l# S& p' p5 q$ ^ a function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}9 p) d* C8 J, N$ \ _9 i, O2 f

& b7 [ ]+ N! x) w" u

7 q& L8 Z& L1 G   $ L6 b4 e; I% U

; i- l3 Z6 J6 F3 w7 {' X

- O9 x( \; i- j Z5 t: q h: P 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。: X9 f$ ]0 n, ^" N) z# t

- W( Z7 e2 W! Z6 p/ e

* Q7 A, ?0 t G d; J 在is_allow()中增加对$this->savename的二次检查。 + E r7 ]6 K2 z* x" J# @/ a( y

9 _: f5 @/ C W: n# Q8 y

9 d( B! L6 R6 k 最后6 [% L& \0 F: u

3 j! t- e; H. w- R4 {% j

x! U# e- L5 g/ R: M 嘛,祝各位大师傅中秋快乐! ) h, e; \" z: i! X+ j

/ T% Y( g" e7 L4 g

) c' q& V' | W4 p; I8 a6 R; D3 A   0 b$ d6 _# S" ~ r! ^1 Q

1 P/ ], d3 p6 A3 A: a$ |/ ^/ ?
回复

使用道具 举报

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

本版积分规则

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