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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
, L# F: S, \) ^5 m( o2 p; b

+ c7 w) Y' L8 R# y. ^

' }& ~. X0 q4 s( j) B& O* _

) P' r) I, m: x- \- J2 s7 ~ 前言- p4 T- s8 Q6 m$ Q0 }7 Z5 U8 D4 ^

5 u3 D/ p l+ }; M# g" j

* P1 C- z( i' m* n6 v7 u+ ` 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 ; m0 e4 a* ^7 k5 }

' g h3 [) A: N' d0 A% P- Q* D

7 O9 X( [- X- a$ S9 Y1 _; d   # p1 S& N" H, @/ t Q1 H

+ e2 }5 x) j4 Y# A- y9 ?) k0 B

9 D" M. B# l0 t; v 漏洞分析 . ]5 N1 Q$ d/ }7 ?1 {

' }; C5 ?, L9 Z/ J$ n

5 o0 P H2 l2 C: C! k* { 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: ( W& K: m8 x7 T' o0 N' E

: l$ N% h* K! V) v1 r4 C# o8 v4 _

: \+ v2 R5 Y; e7 L) v$ X   ' W3 K/ u: T) N

7 K) L: D: ^) a0 l

) _& f+ o" }, ]6 v! t* z5 G% z, O 对应着avatar.inc.php代码如下: , x1 R5 T% M& o" Z; p5 e

9 V7 ^: H' O% h% C" t$ z( l

& a) y* q" m* 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) { ( G5 m+ M3 V. M+ r- ] T4 o

& S- C; s) m3 c2 X; O

8 B) T4 I) J$ c1 f& J2 I% o     case 'upload': 6 E7 _- b. ]! K7 j" j/ K j6 A

, ?" u% I3 }9 R7 l3 y

( c) }+ r( [, j! f+ y( A7 e% k3 S         if(!$_FILES['file']['size']) {/ q* a/ u' C0 Y2 `

7 Y1 H/ m& I7 S0 `1 }

' h2 C% ?" v4 n+ y             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); 8 h3 [# K1 a' u8 p0 x

e' ]/ L. u7 C$ K" z' i# r2 _

0 R4 _8 {" Z2 x             exit('{"error":1,"message":"Error FILE"}'); * \0 d; o, F: `

1 c3 U6 P( W9 \, W1 a* J/ P

! _& x! ^, a9 x7 [# D6 u8 k         }$ K7 O4 {9 C4 ^+ C5 U( \6 d7 J. [! a

2 _0 r& W" }; I

" |, ^) @; ]+ C [- p& H9 w         require DT_ROOT.'/include/upload.class.php'; 3 D5 r' _0 W* K7 s* }) e

' b, G: O6 T3 J% M1 n8 R* J* J4 g

/ [5 H: Q& D) {4 x5 d  7 L" z9 E6 n+ b

' _0 Z8 Y) R6 Y4 N

, W4 I9 u: }, t, h, N! W1 c         $ext = file_ext($_FILES['file']['name']); ( c+ q6 l z) X! o" t

2 C" l! B! i R5 `- [% t! S' D

- I% p7 G5 m: y0 A( g4 s+ g         $name = 'avatar'.$_userid.'.'.$ext;4 |& a* g* I4 o

. z! M4 Y% t7 @* _

9 |! F& q! _# P2 M. w/ X         $file = DT_ROOT.'/file/temp/'.$name;/ D0 \7 v- N; z5 }

- }9 x7 T' T0 a0 f& Q

% Q# M0 l' e4 E' ~" `& C  ( p2 ^& b. q- J: U

# F5 @( i" S2 E

! T1 D/ ?) S8 ]" g/ S         if(is_file($file)) file_del($file);1 H0 n$ q! A/ W. ^* m, `' H, u8 o! y

# T8 u, O: j) M. [; c

& X) w' t. R9 f' Z: n         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');' H }2 _( ]) k. |

, s+ T+ R+ p5 z3 C) `

4 ?' D9 m! \$ p0 U' V8 w: f9 {' B   5 q0 b) l1 |: `4 Q# ~

/ q$ Q6 L5 a7 [

, [1 W x0 ^, t5 \ l+ B- K/ O         $upload->adduserid = false; : Y3 z& u$ Q8 Q3 [8 X7 w

& [3 m& P- {- \; _/ m

/ _+ H' Y+ `. A9 ?1 ]0 N4 E   ( W; @, t+ M r; E

4 d3 M) L! i$ l7 m. M7 m1 K# e |9 i

& B7 j% @" @- m/ f         if($upload->save()) {3 z# f3 r; l5 e; J6 u

9 D3 `* s' N. m5 N7 h' S3 Q# {5 T

* y1 W1 d& v, I4 W+ P1 R z& X             ... / G6 ~; L& F: C, |

0 W6 g. d/ i' N9 D L' ?- }

) D, i# d$ d! I2 \/ I; B         } else { 9 S7 \2 W$ h7 @8 S

0 G- s1 m- i* H. H

& T, x( B" ?: g- _1 c1 \( p             ...9 o; s, V) F8 O& Y x7 F

+ z% J7 j7 P0 h+ g' g: h/ [$ y

2 H% S. N3 n/ Q/ d         }% ?0 V- y% W7 h1 s5 Z: O* K7 i' y

5 @: e# s; r! |& W. ?

% U* |" @( x; B! P. R     break;& H% z- w" ]; z3 L! x: r; C9 a

2 O; S `7 U' h

! L) c) `: }0 z! V 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。# H# `& \ a; V7 S3 C) y j( X1 L

0 e$ v3 L( Y" |. q4 { D

: T+ q/ g$ q# i. Z2 R" t7 s upload对象构造函数如下,include/upload.class.php:25:4 t$ Z6 a- o: e/ r9 O! v: p) \

2 m% _: e2 u( d8 T, u# S4 ~% o

! d# a6 p' |# G+ k/ F3 g* A* ` <?phpclass upload {" m7 ?7 h: l# S* K @& w

2 g3 n5 Y# P# _

; ?2 y8 P( S. }" X% ~* d( n     function __construct($_file, $savepath, $savename = '', $fileformat = '') { # `8 S; ^2 e- x, t

7 f% G: o0 R3 a! ?# T

, x% B* b' ~, r7 F0 u         global $DT, $_userid;$ S3 h5 Y' z. ]$ m1 s

5 ^8 f/ ?( \$ ~3 {6 L" C

o5 a. ^& X3 I% ^5 ]9 k         foreach($_file as $file) { / O6 h% x9 ^& }8 K

9 v" N0 [6 Q0 C# m. s

4 `$ T A5 ^ A; V5 l+ n0 Y: y5 E0 l             $this->file = $file['tmp_name']; ( o$ E% }8 ?$ t2 _

9 g+ t' v7 a9 L0 h" h

- M% w, H/ _2 s' x I             $this->file_name = $file['name']; 6 [ g. Q% Q4 @! S

4 G6 B1 F7 _: Q3 f6 D

( ]0 s1 s( X: I, {6 H$ s3 r% S             $this->file_size = $file['size'];, t* Y* P* |2 [! t

6 {2 V8 t0 h% `9 f

1 B! |5 c# u i; ~) r- T- R& I             $this->file_type = $file['type']; 1 { z0 ]$ p3 U0 D

! O! @% ~- T/ E7 _" i- B6 n

$ a9 _* a( A) ~( J             $this->file_error = $file['error']; 0 d% `/ _; A" j

; H8 c" R+ P& }- b" ^9 ]

" s* E6 s) D8 S   # g- n9 ?! O I- B/ w9 ~" @8 S* d; j

9 ?4 ]: j7 U" f n7 ~

4 _( M6 A3 V* ` d$ u( O9 h. ]* C ?         }0 }6 A/ r5 f" v, @+ s! y

2 A4 G0 a" P9 o; K2 X0 A2 u

* r: s/ ?8 @+ ~         $this->userid = $_userid;0 s- i" l( V0 R

+ k0 O5 E0 m, J# c q" Q. R' Y

' }" P6 @9 k; G7 ?         $this->ext = file_ext($this->file_name); + v: G# k% E9 o9 ?& E6 X1 W; ?7 f

+ r) J& p' }, u) E

9 c! b, h$ |1 I; [: z" _4 ?& ?         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];8 ^ A: i7 |: Z+ p

5 W! c" D; Y. b( k/ K8 I

6 J2 K9 `1 u; [2 W' ~. k7 v- _7 m8 }+ i         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; ! Q2 X2 x5 G8 I9 h1 P& S" m

0 @& Y* t$ ?) N( z K5 C. B0 B

+ o# s; K) R" S         $this->savepath = $savepath;8 H2 n+ Y6 R- m1 F: N

- S- P$ R2 G2 \1 V. o3 b/ Q% ~$ g# [# L

+ v8 k2 T( @. U: w- K' t0 e* t9 C* ~         $this->savename = $savename; 5 j4 Q3 X( b9 I9 r* D2 [

7 k( U) R- Y% U- L

% j3 w1 \1 ?0 Z9 Q     }} D' A; C1 ?4 e. L4 d; |

, z9 \$ s; F9 k# N& T) u

2 b: [ W6 d6 G6 d5 k) a 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。8 e; H4 `7 r& A4 b- ?- l; Y

5 N* }$ Y# f" c; A7 s! S

# |% c' C! h" J9 t. W! [ 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 7 _$ @! Q6 r; |0 L& H. b' m8 ]

4 z8 U* U5 ?( l/ w. i, \0 f

z7 @2 M6 C$ C# ` $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. k: u, N; _( p0 l

6 D/ Q- ]7 A6 o; K; j" S

( W/ Q$ u% ^" ]0 v/ Q3 w: l5 Y 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: 3 B4 g1 |) D) y r

" V7 v, F' p" L+ ~# T

* k" d! N) P& ?$ t. n5 A$ w  " I0 j/ e8 s3 }. G! @( P

, {' r3 N: }( s$ Q

. \7 ]* u. f( K( N' @ 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:% s; R- U- B/ s5 s' j3 c o

3 {: s5 t9 J* B. g

& l0 U+ }! ~' U) } <?phpclass upload { 1 K5 P+ t* T9 X3 d& }

* Z' m, B8 O' O" d5 c/ C1 q

2 H- A1 S) r2 M: k* `     function save() {' x0 }# E- F- O& W# P

6 {" D2 i2 K- a) |0 {) X. J- H3 G

6 {- Y( b/ u1 t, m! E: y% d3 \( u9 H         include load('include.lang');( d" O! w4 v8 j- |+ j; _

+ K& {+ {. W4 @% O% S

& o, A. d/ Q) Y% L7 Y$ _         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); + R7 g1 Z6 h( G& s

# T4 O1 L) H" N9 B; J0 n

5 y) |. }+ E- ~1 u. E; @4 h3 I) ~  ( c: C* F3 o. i; L: a6 c) l

l6 h( X8 C- s. A& D

+ J3 |+ V B" I* E' T         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');5 N; d A4 }, q) Z9 u( S

: r: k. ~% I& y/ Y: t

% D( D5 x0 g' a. O! X/ g7 ^8 ]   T# q3 y W# r( a) d8 y0 v

@6 G( R. M3 @4 U" T/ e) n

1 s: A1 t2 [7 W         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); ! \$ U& ?( `+ c' }3 g2 z' `; c

4 W* j7 N$ W( T H

1 G8 B+ ?; A' T- ?3 G b4 d8 l+ Z. p   4 u8 v5 N0 Z* u8 k; c0 M" |

0 E% c( H4 F! |( Q

) u& S9 H% U) C' m& U; l* Y$ d2 n         $this->set_savepath($this->savepath);5 q! O& J& ^. l

! g3 u$ l6 G" r. o& k5 U4 j

0 b, N8 y0 Y" M. B         $this->set_savename($this->savename); $ _( Y; V$ b, |% j

' l$ g# y: G6 q" ]* ^1 b2 v3 s- L2 M

6 W# r3 O: N3 D: I  # n+ g$ @) Q. ]* m. f A" U

1 i2 s0 @2 a6 B

( f `! P( e/ D2 m         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); . {/ Z! l8 e8 i: X8 k1 }

- P; y. {) d+ t" q1 V! h

4 P; H1 n0 q5 U2 e! ~" v         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);8 K4 I* t3 K' ~# \5 U

9 s0 I8 \( T: \: Y# s3 m

1 n% O7 O2 i9 I1 \& B: N6 I         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); * J* [0 S4 n, G ]7 [! j

$ {+ W7 }/ v8 ^

9 n6 F+ w$ w* h) O4 v: g  / _2 v4 V6 `/ K* Z0 [4 p

5 D j0 D- A7 T) g& O4 k3 N! ?% R

0 O1 k5 I, P& K- d         $this->image = $this->is_image();# Z8 F5 U: |4 O1 f& D+ T

# p# r! o( r/ Z, o0 r3 g

+ r: ?8 C9 I+ g         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);7 z& `/ Q z3 I0 B% A% m+ R

, ~- b2 q# ?6 p, Y8 _: [- K

) x& H$ M0 i# t! b/ N/ {; z2 [# N         return true;( C# `' R+ ]0 K* G& k- Q

/ X5 d5 d( ]! b! {; s* ~" o

& u1 \3 T9 e3 x' V; g! C     }} . o% r% Y V1 ?! L$ I8 x7 a( j

8 Y+ c0 K6 _. n

% P; W# A: q$ B1 L: t8 o, t: A$ k 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:8 R/ j" A% Z$ W( ?: M& T: r

: P* f' W2 `: } d- o

% C' \! i1 e' B0 r <?php$ l, Q( n7 t( \- D9 C8 F# b

& u, h% B; C# x" `8 W& X% A% k

9 k' f; _ n/ X+ `( |: t     function is_allow() { ( {$ W0 m9 a2 J6 T+ U0 q

1 F6 T G; {/ V j

, ]0 j! r8 k' w& W         if(!$this->fileformat) return false;" B' q: Z: b" p8 R" a$ I

+ X! |; b, @) n' Q- P

4 g. V* ?' E7 B         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;% L9 x7 z0 z# s/ X) h7 ~) z7 |

& S+ u0 Z3 r8 w) S% T% [$ s6 c

) `; R: o, l. E9 Q g F         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;: j1 ]. m6 s# e

0 I. M1 n, b) I, |. e; r

$ Z2 ?3 o1 E, }         return true;: `( g" W( n! n- m7 Z+ ?: t3 z7 k

: E! s y; T+ I8 Z) j& E

0 k) b1 J Q" ~7 f# G4 T6 v/ J5 @7 \     }) Z& E d% D0 f* f4 r8 M- I2 X. R. f

c7 Y# n% r. `7 D I. U

$ r7 x* h7 P5 z 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。* P) k) c: ]% u: @

1 ]5 l: m! C8 c9 h/ @7 ~0 R) x K

5 [3 w* L X; X' E$ N 接着会进行真正的保存。通过$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文件。 ; M7 m- N6 W" c0 E0 `0 F! M6 \

2 b; W6 ^/ Z7 v' o7 l

% [4 r: g M) y- l 漏洞利用; P0 Y" ?2 r( |% u2 J6 h

2 T7 y- |" u* I3 X

8 ^) G. O; Y+ U 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。/ u5 P" |. c. y" P" l: [, i2 Z1 R- b

0 l- z6 a) L9 Q" K) d7 u

' Z5 d2 B6 `. i9 k% f# P" s   p% a X3 r8 |2 C8 F' ~

$ u6 d1 c {8 C5 h, d. h* S

7 N+ w' _! l* T- K 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid . x: I B& C1 l% @6 w. E+ I1 i

* r3 j3 [8 E: B G; G7 v0 K

" p* ]1 `8 Q, Y" X: w 不过实际利用上会有一定的限制。 0 a8 i; c' r% x1 _4 @! k' ?

$ D& {, f' ^ R: O; X* h6 \

) U ^! w# ~8 O& ]( } 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。7 X& u+ X+ u# E+ j- O5 y

0 G/ B5 i" T$ o1 m( c

. B& e3 F2 h6 K5 E% Q. M8 B  * G' G6 k6 B. D* M* K

0 q* v, A8 Q7 M

0 G0 v9 P2 c& n6 N 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: . Z1 g/ D3 _0 @

: A8 K% n. Q3 H& ]

$ M# g- Q. k4 h 省略...$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]);省略...& c# \0 ?& y" g

1 p. d0 B0 D- X" |" |7 v" O

( ? [4 j2 E' j 因此要利用成功就需要条件竞争了。! Q' v( S3 X- @

?& S0 t1 x8 t: O6 u4 H

/ s" k( w* \& F$ O7 z7 `1 f, z Q1 Z 补丁分析 . |0 M, ?7 L8 ~9 p" _8 a

' M* V9 Z, z5 L- v8 F* Z% @

* s! B1 q9 P9 D, ~/ O7 z. K   2 @8 Y4 j0 y' H* B0 _

3 w6 T" Y! _# m9 a4 l- y; x. m: [

! N6 c! G4 h7 J; v. P3 H% F# E% l 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:$ }& k% Q& N* ^1 `& c/ d3 j

$ d7 p0 z6 g/ d* a6 p

% e; J3 A) Y7 m* { function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}. q7 K+ B+ J* [2 V9 x$ Y

# d0 Z; d9 A0 K N2 l! P) {

6 W. ~2 b% `1 p2 s" ~. [4 w   0 z# b% C3 {3 U) E- M4 P

% q" q/ D; g" u

$ p' J1 u/ ]' K0 q* o0 @7 F 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 8 K$ j& i2 h L

_* K: X( x" U5 F

9 k) n7 V9 C, u* J3 J# N3 g 在is_allow()中增加对$this->savename的二次检查。% J4 X3 g6 L# {. V5 s& a7 b

7 B4 r. [- J, A

* O9 [7 |! T6 |0 b: J* A 最后: R o) \/ o) R6 f' N

1 V( ?( N& c' F$ @

8 z' c( L+ U3 L8 O 嘛,祝各位大师傅中秋快乐!0 I: H* |' y' A9 g4 d4 A

4 u! r& X$ B# _

1 |7 Z2 y! }8 E' K( ^6 \  : M# y1 Y, V/ ?8 A4 a+ C

) e d* x1 v S. z' w
回复

使用道具 举报

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

本版积分规则

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