找回密码
 立即注册
查看: 2022|回复: 0
打印 上一主题 下一主题

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
~1 @- Q& V/ d" @1 \* W! z

: s4 {7 y* y) r" Z$ C+ M

9 ]9 j/ K* ]6 y+ Y5 j

/ l$ U1 j4 @: U% X 前言 & D3 A7 o% [# |. b

* W) j' Z/ }0 _1 f

F1 E1 E8 y l% k/ G" r# C 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 : P: V' q _( \; s; g$ A. p2 J

5 D2 O4 G9 K# B1 r9 r8 V6 P

% D4 K+ F _ T, c' O  : i4 N; k( r j( Y

3 c9 [" H# S. ]$ j% f

( y0 G8 a0 b, @6 E: G) f 漏洞分析 % B: D O4 ]7 ], B" o

; O0 S9 B: q) u! L4 B- Q1 |# _, `- s

! h; F$ a. U6 T4 i/ f$ O d 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: 3 Q$ i: c! d& _

! n n/ _( D5 ~6 e& a- w4 p

7 y3 n! G( W$ ~, C   + x8 O) a9 Y6 W& u/ o

# N$ [% z+ R) N0 c9 u0 M

, F. {4 t0 r) P. w; m4 z3 O 对应着avatar.inc.php代码如下: 6 Q% T1 H- T+ S# v1 O. p

* c/ {6 E- @6 ^5 z- y# `1 M

" u" }, x* ^( ~% k0 o L <?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) { - \7 f. t0 h# Y i) X

7 O. A" H& Z, z4 `0 ~6 n1 s( Y

9 r; C) i3 M3 L# X) l' Q     case 'upload': \) v m- U! `9 ?4 S+ {4 U

4 d1 U2 d/ `& i: n* |% X

5 ^2 k% B, T0 A0 ^% b% `' C% V         if(!$_FILES['file']['size']) { 1 g! G' G' E0 v1 v5 ~: `; @

( ]! @& a) |; w/ j+ `7 b2 } F6 ?

_; {8 ~$ C" Q& t             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); : L" S$ L2 f3 ?$ `* g

( p8 h0 G# g9 Q# V- x

" h1 }; w' L: N! H$ n             exit('{"error":1,"message":"Error FILE"}'); / D& H: p$ T) E" b; m' m* H+ S8 T

$ D' m: a3 O: B* |9 M$ r6 i' h. ^5 w& I

6 r. E7 H3 Y1 F         } {* D) R6 C! A2 t

: @* f% R# N0 h# G9 d

$ p7 }# e5 `( R6 G, L         require DT_ROOT.'/include/upload.class.php'; 5 ^( e6 M9 t+ Q8 n6 L9 v9 d5 B

3 X1 v+ ? i. G. |& D

. L9 b3 P9 }! D$ ~9 g8 m  ! D& y9 l$ F4 j ^: p2 C, n

/ ~1 o; i9 v, b- l+ v0 H5 b" ~

3 g) K' R& Z$ U1 e1 |; r         $ext = file_ext($_FILES['file']['name']); 2 a W: y6 A6 }% C; A! P

6 \/ W2 h2 h- F k' Z+ C2 }6 {8 E

$ \% L9 O4 b" ~         $name = 'avatar'.$_userid.'.'.$ext;1 w. f8 v. e, R( q1 I! ]" C+ n

7 P2 N$ i N" {- V) _% I

+ G' V9 C" @0 m4 n) n6 ?" _5 _) n         $file = DT_ROOT.'/file/temp/'.$name;* e$ P7 C& f- S8 e0 h

4 P2 F7 e. r& H0 K

3 O& c1 w% T- P# \! n  5 E L: c1 f4 o& f, T" b. O

! m/ S+ d* i- ?

+ e7 h+ Z' Y; l0 U3 Y f$ ~7 H         if(is_file($file)) file_del($file);5 j( Q6 w3 X; X' B2 k6 g

# [" }" S- f! J. [* n

: M0 F1 w0 f& s1 V         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); Y, C0 w1 Z6 B5 f3 ?

* U4 G: R2 B2 v4 M1 ]. [5 e! d. ^

8 p+ q6 f4 V+ @$ |  ; Z+ {# M# F( [

- t. E! D" [: C

0 X5 c. s$ A" k2 _. z! |6 ^# W         $upload->adduserid = false; % h( {2 b; O) B

& f8 B8 N6 R: i5 Z

2 J$ J+ |4 K1 P   ) C0 q" e0 Z" V' r

9 I1 r9 H; U% m! f" U0 r

2 n/ A' O" ]$ m9 d. s         if($upload->save()) { T4 i6 Y; J) I9 r' ~

4 n( q0 y$ i. Y2 t1 J0 M

/ c$ |3 z$ v. U- P; _! Q \             ... ' u( }9 y, G5 F4 I0 H; Z6 M

0 n4 a& o. A, d' o

4 K. Z- H: S" W0 v9 K         } else { / M+ c0 c# K8 P

; z5 @1 Q1 U7 j8 v

; e! E- H, p8 f9 ^) w2 |             ...; Y+ b; c' E( n2 v9 u6 x

5 Y- D$ h- c6 M( i* x) z

$ v: i1 V! D- E. ]         } ) w' d7 C% M& ^' A: J0 k `2 ?

% ~3 T" O: E" H, h

" d/ H# }1 b/ G; D1 i3 w7 U$ ?9 k     break;' z5 N( M& S8 P, ~

& p! Z# ^9 |# ] B! i* D% A) _

1 J) h* T' Y( u- I2 _ 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。, A0 U) ~! B/ ], f3 y

" G' z! o. S/ x: o+ \

! b7 z6 X$ J: P+ u/ R6 i upload对象构造函数如下,include/upload.class.php:25:6 n* x* N9 e( }# @9 U

0 T; p: c+ Q% ?5 r1 d

( g3 y/ B$ N5 \3 r6 S- Q <?phpclass upload {) Y( K9 [1 K; B+ c a2 |3 D: s" R

( k& Y' E5 D1 [: f! Q! c5 U

' _" G0 @9 O) X     function __construct($_file, $savepath, $savename = '', $fileformat = '') { ( Y0 d) ?# {' s- r9 e. u

7 N/ J" A1 {7 Z" W

- O2 b/ Y; N# H4 x+ c: c         global $DT, $_userid;8 I2 }3 K t5 }' F7 Z. w

O8 m' l s6 P3 l V1 b

% d m( e8 ?0 f& Y         foreach($_file as $file) {% z6 I4 J6 {" l. c* n6 x5 ]" D* M8 W

# r4 r9 |! B! p

" L- V; \* Q5 X2 X$ v6 S# z             $this->file = $file['tmp_name'];6 a) o, R+ m. A2 P

+ b, z# S$ Q% \7 C

0 ], G4 p5 d+ u& J/ L9 m9 O             $this->file_name = $file['name'];) _6 ~3 B4 K! _7 P* W

9 i# \' D H) s3 B. X7 y

% S* D* H; z: y3 Y3 ?5 A             $this->file_size = $file['size'];! e8 `1 N! w% ]8 }0 F

2 t1 P5 Y, K& m- F7 Y

( T4 z8 b1 C: t3 o             $this->file_type = $file['type'];: I4 G! J+ o# d9 t! `/ [

4 {! h2 K' g* w9 c7 m' Z

4 Q$ V8 e" w4 P8 _- j             $this->file_error = $file['error'];4 e6 k5 p0 e7 F% N

( s9 Z8 ?! X' w# ]

7 B, Z, _4 X! T5 T0 ^$ L* Q9 a) F   \. z, o4 X& d: L4 ~

6 g8 X' I9 M/ K& h# K/ o

1 b( ~' E! ]7 K5 T2 \2 J0 }6 C         } 1 F5 J, q% B4 U$ i9 q0 s# ]

- q4 Y9 R6 C+ _6 [: U' q* p: N; [! f

3 I8 j6 }! w" v8 g; O% p         $this->userid = $_userid; 4 T8 E; z" ?: D" J( r; L4 a0 y: u0 ]! u

3 z6 Y5 C, V, t! g2 h& \/ [, {

% b) I; H0 A) ^1 T- ~9 @8 q         $this->ext = file_ext($this->file_name);3 m! d3 u+ J9 A, a* ^2 C6 w

# {2 a$ L/ ?1 H% X% r: U. T, V

" q: s( M" c4 L! J1 c+ b8 D         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; * C. i8 {) o2 z! |0 |

$ d! r5 Q1 {+ N

/ a q9 D9 P8 F, b' r3 _         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;; T( k8 [/ u- v! x+ g

$ Z9 ~7 W3 P( ?

( l% `4 c6 [2 V9 O: F         $this->savepath = $savepath; 7 c! @9 O% A9 I! L/ a# s

" [& k! V* p% w( J" K8 L

* N; `% y- S% p" F         $this->savename = $savename;' }: K, C/ x* b' ~8 ]

$ H( k& w: L6 K

( _2 F3 L. A$ w2 I+ f$ a$ } f     }} 7 b* f, s( P6 z8 |* O7 V7 g& c

. N3 t0 h5 [+ N, y( H

& |* m7 N" H' G5 |( N: F! g) J" o 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 7 p* h$ Y$ J! @3 I: Q

: N0 }8 r/ B) e+ V

7 D4 ? V |; J& A' p 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 4 X! O5 s5 Q; i: x) g) l$ v

6 ~- J2 u! b! n# v

& Y7 `7 S5 e6 X# \, y $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 % L6 I/ X% V8 K$ d+ p8 n

8 p- Y( e1 {# W/ b

7 Q8 @& n! W& K- h8 G+ Z 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: - T" e6 b2 X/ Y) `

' i0 j0 }$ ]/ e* {; ?/ D9 e# W

& N3 a) T. u! H   . q7 m8 q' d, O6 z) u+ I

0 n& d2 R, S) U2 e7 @! Q8 P# c S

" G. m+ C. N5 r$ s& s7 f4 n1 f 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:: j E+ o5 X. f1 ]. L' T

! D" F! b$ U4 E+ B) F0 U: p. u

9 u6 K1 q. ^0 X/ Q1 @" l& E4 B- H <?phpclass upload { ( ]! Q' N2 z2 ]. I& d' T$ V

, o- D; p& a* V- }! ^3 R ^

# o2 q: D, U) b' s% q" v     function save() {) Y9 |5 I9 x; d; A5 J& s* A. K

( k$ @$ b Q5 W$ a) n% H6 c) m# Z; g

% w6 P9 _9 O3 X% V9 c. \. I         include load('include.lang'); 9 o7 C& ?: z. _

, g* C t# u+ g2 @

, Q0 U. t& t0 X0 t& F% F         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');. m* Y% \$ d- b2 h

' o E! ~: s' m6 I& `9 V

U) A% T( d4 \. w( F% s- R   . e4 H2 p. j4 d' o. _) t

- t6 w$ }2 V' D! p* J

+ B+ P- c* ^3 m8 J         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');4 g# L/ G! f: ~9 m6 A* E6 n e" `

! ]0 R/ J F0 f* T/ |1 g! N

. N9 ]9 B6 K/ i3 N* T$ X  7 w _( d8 E& k) P

$ L2 b1 d. J% T% F

+ R' P1 ?9 u& r- p         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);4 P5 Z) J: X+ a. q

( w1 S3 p% S9 E, w( r

/ r8 u" I) b$ J0 c3 v" T% s   ; g* {, P7 S: A% a8 Z/ H

) i2 S" \8 J: g; O

* J. o$ I+ C/ z! _! `8 i( J1 Z         $this->set_savepath($this->savepath); , e+ M( G/ I( l- M w

7 P8 [( E! T! Z2 Z

# C2 P2 q$ }! Q! P4 V         $this->set_savename($this->savename); . R( ?# s9 K u. o

2 n1 |+ i9 {. Y/ s( o9 H g) ]

% S. O$ I! a0 T0 a! k# P3 L   + d) _' V5 c9 r, e/ b& E

' s6 g8 J$ G, S

; l+ A# @* z' `* }9 D- b         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);0 B% ]. d# M9 O M% v7 @% i$ T

: f9 n8 O: v+ I+ r

- r, t- ]! f3 r2 b5 Z" a7 h9 ]+ p         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);5 ^) t3 Y6 Z1 z+ T! V6 b

. S9 J( |( }2 j$ p

/ e7 T+ X8 R; Q# Y         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);: j4 z; e4 S8 A3 B( p: @

2 I+ ]! {7 s5 F( m% t

+ J* j+ O$ e# b8 Y }' e4 ^% W   7 t6 g+ j1 q) s4 m0 o7 X/ c/ z

6 A4 y7 Q; V: B" S; y& u

& ^. u2 a) x m# T l         $this->image = $this->is_image(); / v6 \+ V5 V% t/ D

% N/ j. D; S& G" v

: F; a1 U2 I" J6 a         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); . x- `* { Y$ ]

3 O6 M) U7 O) a4 k: Q1 Y

# I! y$ T. q6 r# [0 \         return true; ! _$ K( X. H+ ~* C) {7 C5 X3 X

: u3 l, d5 C4 h6 c' R$ Y1 D/ x

V* }8 A& }+ T q% q; F4 ?/ Z" ^, O! b     }}9 y- x6 D, p% o& l- o

0 E- E- q7 d2 e. U, ]$ `& E; y

- s: ^. b6 z* x0 T 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: ) G( W: ^0 ~* e0 s: a7 i

! W+ x0 P2 i( t' C

+ A7 U. e( Z$ `2 W! v6 D6 Z. Q <?php ; {8 t- r$ B& f, h/ R8 B8 ?

6 d# a4 N% p! y; f' S8 \2 v, R! }/ i

0 v) y* a4 R) J# ^ I9 \     function is_allow() {6 }4 M0 `3 y% V0 r3 q

# W' @% W& z* u

1 p y1 `1 n o& ^' ]         if(!$this->fileformat) return false; $ D6 C2 u+ _1 H# m) ~- G1 { E5 u

D( Y: ?& [' _7 L" T

/ G; K4 T; `2 f" Z8 m/ t         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;% }0 Z0 Z. l6 J% q& I" h

9 w( s, b5 W$ }0 N. \! i

, \2 w! V& |7 F; i5 \& {* k         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;5 T) A% g ^3 v. g

' e! @5 B1 R. j8 W

M! S$ _8 C1 h9 [         return true;- S# Q' l& [! o8 G2 K7 B3 d5 t2 ]

8 Z9 B% F+ r0 m1 A/ C- [& E& m+ L

; u0 E7 Q% A4 N     }% A# J6 C" D% ^& u `' {3 [+ N

F& Q6 z5 ^! V, W4 H& V% c

( m6 T7 P& }& m% _8 Z* ` 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 # m; o9 u" a% u9 M( h

& t* w& ~9 E- |

9 V' n0 `" p- n2 X+ _. u) w 接着会进行真正的保存。通过$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文件。 ( \8 O3 V6 m. ^0 D

6 `3 ^& }- u& v& Y$ P

- w& S, F( P4 J( d6 o# G. M/ E; [ 漏洞利用8 h; W3 A% ?( O3 O" M* [

9 g% S3 B8 n5 t4 p) V

! `6 e. p3 T8 T5 H( D ]) }, Y t 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 7 C& j+ S. P& A( v" B( M% P

, b6 b+ I, N8 i# p& V# Y

3 H% F2 V0 v4 o   9 u4 D6 o2 h7 a; W9 w6 G E/ o- W

. `! ^9 |; O. G( x

6 J' S. R& d3 ]5 w9 R% M5 m 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid9 s+ g7 g, v( p" C3 C! C& E4 _

" T1 B* ]- G1 M0 R: n7 h4 l

6 @) z' x$ @+ p9 F/ m 不过实际利用上会有一定的限制。 % a" o5 @4 Q# E$ Y0 r9 I2 x% i

, G/ Z# L( f* k- J; Y7 \

" `$ \3 @, m6 P: w" z$ n0 o 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。1 W7 s" f3 ?4 x- H6 w( X+ {

* d; u* h [. |* t: P

7 o. u Q3 d% T   0 V2 t4 j* { ~# h7 [& x+ M: v

2 u: ~1 s! ]2 k+ i

) b. G: |- @3 B' I6 M$ ?4 i# F- }4 p 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:8 h9 D) ?7 ~) Z: a

" m c# K8 s" }' }* W b; N! G0 D3 f

4 u p' r& q# J$ T& b) Q 省略...$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 E# U. o4 q- B/ z* l% p R

" @8 ?# C6 M. M$ T9 i! m

9 }! N$ X: j% i% Z 因此要利用成功就需要条件竞争了。 @$ Z' {/ A, |, b1 `

4 P+ E$ t, D; B0 H

/ _; [$ w8 Q D' { 补丁分析 3 Y7 o" I% T3 V% `5 ]5 q8 ]

# l$ `3 C T5 B- ~- g3 l

3 G4 N0 [* |: E& A4 J8 J  " X3 {0 a* z5 \) a6 G

" i: e! k% l1 _+ k* b f

s$ V5 @1 Q! r! n" m: o0 L! N 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:6 M2 L2 m i3 `* Q* p Z" U

" ]" {6 [& ]0 ~, ^3 p5 y

1 }) B T: E/ W; l function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}' V' v8 @1 W6 M7 c

( Q( _1 K5 H: {2 c* Z

% ^" [# z: A& f" M+ c   0 E7 q) B+ e& }. d- u

- U4 n; p2 _8 D+ K( M9 Y

- m5 ~1 d6 A" |: L2 v 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 4 B/ t. n$ v( l" B0 u, B. X1 a6 T

# u, J+ h) M4 v( z7 N( t

& \* ~7 l9 z/ R# y- ]0 V 在is_allow()中增加对$this->savename的二次检查。# A7 v$ R( w: [0 Y( M

+ x# S( H+ A! B4 ]$ r

7 k7 h3 Y. v1 n0 q' u: _- r) R 最后0 h: l6 A9 l7 I. l

3 B: I N3 A* X2 ]" A0 ?

/ R0 F* ~8 s) R9 y 嘛,祝各位大师傅中秋快乐! v R0 p8 k7 @6 ?

$ c/ G" D' N+ c5 p3 X: p/ v# D

3 H& p" t5 g2 d# W6 E2 e   2 W9 e9 Q4 p) f' W& r0 p1 x

" b: E( u1 @0 ^1 ?/ M4 R) @
回复

使用道具 举报

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

本版积分规则

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