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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
1 i# y! q9 b4 Q% p

$ J9 h8 z+ [" T7 l

( w r2 w* E; l( u! B7 ~

2 {' n- m+ m; d7 B: [0 i# T 前言 % P6 c8 F0 j3 j# ?' |' V/ w9 r8 ~

# o5 U2 W% y% h( y! W) y* }: E& y

0 A& `1 S( ?/ ~5 F1 n! X 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 R. T2 V. N" K1 R7 ?, }+ ~2 C

" m/ Z. _0 [, f! c8 ^5 `' x$ k

9 w) }4 f ~& [; F& p  % U, ^8 G% g# K5 t! T0 _# e7 Y! ^5 O

4 m3 c# S8 R9 u& }

4 t; x) y# j1 G 漏洞分析 % Y+ Z" l# ? C

# b- C8 D% z' d1 M

% R1 R2 ~+ k# K% C' l/ t$ @ 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:- W3 G, W! k$ ?5 Q9 O

o7 E- M) F( V$ _6 [

, x) I$ G- ^6 @, m4 L# ]2 Z   7 {; g q+ }5 b8 f! r, D( L

9 e7 Z4 R+ G( Z$ ^: a

" V; y. b! t, K+ S y% T 对应着avatar.inc.php代码如下: r0 k# R0 f2 k

- _8 W q0 w3 e6 P `9 k" O

* S$ [- _2 d8 h3 _6 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) { ; i3 Y4 ^0 W! Q

/ k+ u! L$ E$ t) b

! Q( L7 P1 f* @, p- p     case 'upload':/ c5 H& g' y) V4 C. K9 e5 b

+ f6 H8 S* l2 D7 D! d3 }

- a8 V7 q, Q7 O) N! l/ D         if(!$_FILES['file']['size']) {. |. S' `1 h. J$ H( E8 {

. a5 t2 a3 @3 N0 _# P

# {! j3 w5 ?) u             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); 0 \7 m+ l9 S6 i

4 X9 r+ Z" M4 P U) i, j& j: X

- X( v! E7 b8 K, K% B, U3 Z             exit('{"error":1,"message":"Error FILE"}'); " E& n/ G. ]/ c. o8 s' G1 _; W

- @! ~% J) s9 }. R0 _; P

, a+ U* O. G7 x! I6 {+ }         } - j' f# ? z; i8 G: g

: U. E* x9 [1 t

& }4 t% g2 Y3 P4 V! q# j         require DT_ROOT.'/include/upload.class.php';8 K) t7 |4 D l( R3 f4 h }

) E8 k: F. z8 \

4 S6 f `9 q. W5 n4 f, b n W- D   2 x* F& V2 s8 w& C

* z# T0 e. ]- B

% _6 o- c+ F: S$ V: q4 \         $ext = file_ext($_FILES['file']['name']); . x0 X% Z. [0 B+ Z4 _- B/ W

' P+ i& t+ Q5 w% d$ J" ?$ e, t

# J; c) K$ Y7 \* K5 [         $name = 'avatar'.$_userid.'.'.$ext; 7 s( L- Z0 }$ V7 b

8 T5 B4 Y$ T8 _7 p$ D! a3 S

0 T9 G! f. I% l/ C% X         $file = DT_ROOT.'/file/temp/'.$name; 8 h$ R1 G1 V3 U* f5 O* C$ Y

/ R3 B. n3 [& ^5 y" f% {; o

' I: J2 P5 _/ A0 x. j9 o# Z1 o3 a  ; u, R! W6 D- F3 f% @3 r

* Y' \2 \7 u7 h2 p9 i# o; t

9 m% j) A- q* q/ L) @3 e3 Z4 U         if(is_file($file)) file_del($file); # z; b6 |; t* t6 C

& N% H$ ]9 {; X. q. W

0 `2 W; E' C% \+ X* J- F         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); 3 B4 F7 w4 e2 J [& I# q) A* j$ _

- }3 g! q/ d; u) L% y6 [

* f7 d+ z! u' V1 l   ; y/ k' [: G( t

2 s3 b% B1 J6 o! o

8 V/ l2 C' X1 {" D- K ^         $upload->adduserid = false;# b' U4 Q Z9 _9 A9 D2 B

8 t0 C+ K: G" S! K

! l G& ~7 s3 Y  9 P" {+ |, |4 }6 w4 W

& Q& i% ]* x% N- Y- U b+ Z

* i, ^ Q; k+ e. v         if($upload->save()) { # F( v4 \) `# _3 T

; U& H( b/ L3 p+ _ N

# p! L; v0 l) X4 e             ... % n1 B) j& K' t2 j4 h2 w

: m$ ~2 m) M0 [* F

* C5 [( y6 L0 a" L4 u% N2 c         } else { ; Y ?' |/ }$ ~; N" l. b

m, l: \ k4 Z- `& u# O2 [

/ _- s) q: t; h8 j5 |4 T! v; ~             ...5 o; Q2 |5 h( M J# Q

. d; r! |3 o; ^7 E3 \" z- _

& T! K1 a! }3 y7 h; W         }0 ] e$ x) r& ^. c9 b0 U4 h; @

- b D, [0 c* D, E5 d( ^% |2 Q

: S4 p1 d. E1 [! t     break;3 r2 @1 V2 [2 k: ]

8 X" D- L& y( H5 Z9 Y6 n T

* o; p4 q; \+ Q! } q/ G 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 : X0 L8 o$ m/ x

9 s; x0 m& _9 Y. r

0 R% ^2 k( q; {# F upload对象构造函数如下,include/upload.class.php:25:( M# Y; } ] l- P

& ^2 o# p# R. c0 e q

. J; Q5 u3 ]# n) h, ` <?phpclass upload {# f: W" ~4 e; b7 q; h* k' [

, k. ]9 m, D* o N# A$ K$ Z

2 c7 A; w: T' o4 u     function __construct($_file, $savepath, $savename = '', $fileformat = '') {1 A, s6 w6 b% b/ A/ i* d

" m4 a4 X. B2 `$ H& S

" r# J' h- d/ y( h         global $DT, $_userid;/ { h8 P, C" y9 K8 `% z

& A; ~2 K2 N: e) g/ n. S

# m9 b" T3 Q _6 F         foreach($_file as $file) { & y5 b# S* F$ }) v: h3 n* f

& U& }& G: f) l+ G+ w$ T

; t8 p; p# n) M1 w3 r1 n             $this->file = $file['tmp_name']; $ C/ w" @% G K8 F3 {- l

) V0 D( H; z% \: E

, b2 U3 k2 A ^- ^) x5 W2 v- {8 n! D             $this->file_name = $file['name'];% Q0 L0 \3 ]) v) K/ L7 @" }9 A& Y

0 h* l! F$ ^# j: S

6 ^# z2 \; B: {% j/ r, d% K             $this->file_size = $file['size'];; b/ J' b, g' Q1 z4 J3 S$ _

3 V8 Y& G& s1 Y3 C: e: ] z1 K

% ?5 M; F# z7 D1 X, i/ ~3 }& t* ^3 }             $this->file_type = $file['type'];+ ^+ L2 W9 t+ G$ y8 \2 t

9 { L1 m, E+ I) [. a6 X, ^. {

5 {% o$ R& F7 }! B/ O( Y             $this->file_error = $file['error'];- \8 L% I3 @: M( X1 L7 r- F2 c

3 K5 J. D9 N6 P. ~- g, ~' x0 w

% c/ p @( x: F$ h X   ! {8 H0 X3 w( c4 |; _3 f

3 K6 X7 Y3 [4 g# I6 X1 M, q

3 M. d. S! U4 I% M$ Z         } o7 b' u6 \) d$ r: k; Q

1 i; x- U1 N, M# Y

9 y) y: [9 [' g$ T4 p0 n0 N) c         $this->userid = $_userid; ' L0 V4 `' d+ b: A; ^) S. t. m

3 i3 {% p1 Y0 D) O

' r0 x3 l( y4 F+ a% x6 a         $this->ext = file_ext($this->file_name); 1 m1 R! \! [: ]( R+ y

# p7 ^" b+ S# n8 l; _; G

0 T5 k+ K/ \) r: ?: r         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];8 t1 K1 ]8 ^4 n( D: t

- u8 V) n+ B) F" g3 m7 \/ p4 F

) k3 t( R4 j& F. t) T% Y# {         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; 1 Y* u' N9 }! {

1 `0 E4 U3 i* S* G

/ i" @$ V: i+ {6 k* X         $this->savepath = $savepath;! T! x) W, K8 T3 m2 M

' H! I1 C- [3 k2 E- K# [1 L

, C4 C' M/ H7 ~2 j/ w+ F3 d         $this->savename = $savename;7 B# l% c5 i( b3 }5 G

& f5 k0 O$ ?* J; P- d. j- {9 e

. Q' g5 Y! h+ x0 A     }} " r, |2 ?( a' u; l

7 t+ R* k o0 S5 M5 p8 ^

& T- T9 @" T7 S) O: g- N# E 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 6 B6 ^' C* `1 G2 W2 W/ `" K

8 f! y" f5 v4 q' P, A- ~

1 Q8 V( J* [4 q3 P. f; G3 f 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 ! f1 {6 | d! F: n7 O, V

: I' T* t% k1 R Z& W8 @/ `/ ]

# y6 [, a1 }: h/ a( `) o $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 # }1 m2 C4 W6 U5 D z" t: S

* u& [ m3 \) i+ w

' W. e# u7 {3 Y) y2 P7 ?6 | 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:1 n9 i4 u `, l) o+ s

# B) Z1 l: O8 R' s1 W; v

6 M) G$ U! T# }7 U  - n- t$ L/ N# B# e D& `4 A( m

/ `& g, {; _9 W& I

+ G5 z6 A, t5 ~5 _ 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:" N* p; n$ b' c: Y

, x" q, w; J; M; Q( [ Z

- ~ h& H- L- y) `" M3 H/ w <?phpclass upload {3 ^. K: g. K# D" @8 k8 k- n, x

8 U, c# e) X) H( M6 ~) x! G

0 R# z( q/ y0 b# O2 S, S b     function save() {$ k: b9 c# ~- N4 o9 c) u

: Y e( k, M9 Y+ v3 @7 B

, F1 B( J. W( x) s# q, M# D3 S         include load('include.lang');4 S3 |5 G6 Z! Y/ _" U0 f) a8 w

% m- L7 L3 ]# `% {

4 d7 y9 J2 Q. K9 P# e         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');1 c( _+ W# g; z& s5 K5 J Z% \( k9 M9 ~, C

: s8 W4 I/ [4 {: x2 m5 P

) n: w/ }" z! d& ]+ v   - f @/ K# x8 r% {9 H

2 {; o, D# ~/ m

! m1 J1 c+ f: H# s+ y         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); 5 e7 m& b7 G {5 n* F( @4 J

0 B6 V, P, u) T1 s, o9 U

/ ^- }. H+ V, a4 H q w   5 t3 ~' O+ S) o) h d; }

+ l) C: l9 b; I5 p4 ^

' a* g( J: A/ m i' d0 i, U         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); % q6 U' c2 a+ A+ n

! X# t. v0 \9 n" ^8 v* |6 X

1 r1 |) H4 ~4 k   % b. e# Q! y( Z6 v

9 K1 A9 B6 g; M0 |! }9 }- e4 b

4 p8 E; T+ F4 |7 ?) [) d( D1 m         $this->set_savepath($this->savepath);) k7 G" n: t& G1 Y: t4 o0 e

9 {* k1 I+ T! o0 @; p' _

9 r0 u5 L# o' c# V# j7 }: c E$ s/ @         $this->set_savename($this->savename);1 m% e7 e6 O7 g0 r+ b. @

0 H3 B- n! d( b$ s7 |

% x+ U+ x3 @7 f+ V$ n   # L1 z" d% ^6 U" y

V! G2 x1 |1 }1 Q0 N5 o

" O" v0 h- Y* J/ k* X: \& `         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); 8 Y9 e0 p7 [6 C' B, \2 k6 N

; l# B# |; s& f. N% G5 ` o

* `" Z0 z" @3 O0 q" a. x6 a         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);) W* d6 d9 _$ t1 Q( o

8 O/ w* ^! L$ a3 r

- m; I0 i9 Z; R: P         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);% {7 y$ D# X% a2 t$ H7 O" U$ \

6 p1 }4 ^* Q# }# ?3 N

6 Z+ Z+ b( x$ `0 |# v" H   ; s/ F( g9 K# D9 v% M

0 X. O/ S1 X: C R, o; ]

9 B9 [) I/ G C' `         $this->image = $this->is_image(); 9 H: ^' [% f6 \0 ?

% V# H/ N' J0 g6 V, u

" [( p* X; t7 Z4 c7 [; n         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);" ^0 h0 b3 t8 m, r! y% v( M+ E

* H! `- `# [1 l1 u5 E

; }) e4 c. }' H; f         return true; - A# b. ?. V' ~4 ?8 b

( y% L; |' _& z' q7 Y

( U5 a: T$ `( G3 c3 X# Q     }} . ]/ l" U3 B2 t5 W

5 b, N9 S. h& ~2 y( i" N* w( ^

2 [4 w3 m+ k. y6 W& C$ _ 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:0 g& t' p9 h& u6 z; B

) P7 e4 m, A( {

+ M O9 o* H& \" |; [& R/ @, J; W. { <?php X* x7 z4 N$ y& U# v) M

@2 Z$ ~4 s! |' N- I: g; b5 n

2 V7 L4 }) u- h$ y3 ?, z     function is_allow() { $ m: [( X" k8 h O+ O' l- r% h

5 R8 p: i$ F: }* @

! k# k( k* s z         if(!$this->fileformat) return false; 9 Q5 W# Y v* q3 ` |

+ E0 l6 Z8 ?. I3 U6 Z

3 g9 |) [, J. h' b6 k7 \         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; . \- e( M$ n' \! h- s

' K+ x$ w: Z8 H) g% [" R% L% i1 O

6 F" C. y0 |' b# j: D+ r) `         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;; ~$ o$ g2 c2 w. t; X9 I

; r9 `3 J0 ]7 F2 b) r/ m7 [7 b

4 ~6 p$ C% Q8 Z* U9 l5 L         return true;( _5 J [6 K8 k6 T, b

+ ]" ^! S. {( P! h% X) [$ T

5 [6 @. E$ s0 Z/ o1 K- G- S     } S% T# g' w7 h$ {7 B

$ ]9 P+ V' I4 _& R) ^% e

6 V$ m# e' o! X 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 . W4 p$ z) i# X& ~! k# X" \

* }( O. B# W# j8 h

: q* Y1 Y5 {, Y- f2 N; L. ]" p4 _ 接着会进行真正的保存。通过$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文件。 7 u$ q0 d/ j6 [& z4 ^

! L0 i4 f0 s# h. t# W( ]5 w- e8 E

- m' s+ t: F6 S. Q- B9 N8 b 漏洞利用 r, n$ A6 V2 W: N9 Y, C9 F

U2 J' t7 k9 d) `, J2 L

a' ~4 a2 V; A. ]9 N% [/ R/ G 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 4 R% X) S. T; a( o& ~7 i7 D

, f' i8 [, x/ x# u: F

% @+ I% |7 ]3 x3 P   5 D5 l0 m2 @( D# B7 v! h

+ W* s4 K" m$ n% U, H+ e

9 [# x3 q: ~) |2 b0 R 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid: B& y4 R5 s! Y$ k/ \

& A Z( W2 _, T( `- A* N$ X

( g# \- ~% z5 x" j 不过实际利用上会有一定的限制。 , d* g) \, k! _( h7 z6 T7 t7 y6 L- g

2 A1 U4 x5 \1 \$ W. C0 m

: r# A7 S# w, }0 E+ W& T. Q 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。& M, D1 e( i/ T( w

* W$ e7 ^! X* f2 i

- k: u) [9 O6 ]) d   , i _" {6 t; C" Y* S7 R- r

% F( _; K6 L5 V: Y w+ b( @) W' d

* V3 a6 @5 F0 u9 O/ K/ t7 Y3 [* n 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: + w+ }! B- E! f% v

6 L. z2 k! K! J/ C

8 }# R7 ~( N. L6 g- S# @! K, ^9 g$ t 省略...$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]);省略...2 @" b& m7 ]) p8 z" M9 c& R/ d. ^6 \

2 {6 r [, o; r- j

# ~4 s0 a2 T/ a2 V$ { 因此要利用成功就需要条件竞争了。8 Y, w# n) I1 I. Z/ E% s

3 M8 V1 h) v' a z

! @7 k: l$ q% M: }+ v4 L+ g) p 补丁分析 ( D% J- {. w5 b5 H" s$ ~

! w( _; @ \+ {3 w$ m O( L9 J

) N r. u8 Q" E. ]% w: k. D( M  3 J( A# a# z7 C5 D1 r# c

! F; D$ B% A @

* h2 v/ Y/ U" J9 Y. h0 n" q$ E4 S 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:* m1 C# m- z1 g" e

) X, T/ n: @+ O$ H# o

8 w" r4 L H( V8 X7 y. U function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}; M) x1 c% y& q. B

* x; {# u: G3 t) F6 q% T8 T1 I

$ `/ p5 z4 Z% `& w( ]( m   $ _! t1 h1 }5 I9 v" u0 `: f

: q& U2 V" d: d9 L! @. y# M

0 j U6 @# r0 \1 q' e 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。- a9 ?, V2 {1 y, U7 D% Q

3 J" p: H( y' O! W: p" }: w$ J

; n3 O5 x+ G% }* E! j" v6 |( b: o! c 在is_allow()中增加对$this->savename的二次检查。 + x& }3 S h4 B \

% ^! ]: ~" k5 J

+ ~3 H0 I8 i- b+ d9 @; H2 A 最后: h, p1 j: B/ K( X. ?; V0 j

! k0 r; S4 o, h0 R! M" L8 A

" b2 Q1 g* i7 i0 I 嘛,祝各位大师傅中秋快乐! 7 ~+ w% Y# o6 w

# v* }; p" ^( {: Y5 V. a

5 n4 _" m3 T: v6 H  ! `) t1 U$ c7 \3 n: A/ O7 |2 u

4 S1 F- J. M0 p% ^& X7 s
回复

使用道具 举报

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

本版积分规则

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