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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
$ d1 p$ U: }: ~9 g( a3 {

. U- j! ^# b" O' b+ Y$ ~

* I1 B1 H6 v1 G" e) \8 K% S) q

1 ?9 J% A) \0 ~4 {, `. y 前言 5 g( x- P5 |$ D. H( R

4 ~# n* r' F) _( [* d- ?2 Q" x

% J% ]: q2 j. X) {% M6 W% q1 x2 ~ 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。1 R, J7 R& q) X& G* q

5 E5 V7 g% v% c% Q9 L2 q) c4 u

# X6 P% v: I0 v' V3 {3 J   Y0 I0 A( }) B1 G& i5 N4 C d

' j- T& c0 ~3 E) c0 D( D

, v, F( d% l \6 z& _' ^ 漏洞分析 7 Z; t5 O4 |! _" N( j, o7 Z& R) J

# ^$ H4 y( _/ f1 s4 R; J" M k2 h

- ~% R& K0 H# f, H0 E 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: - K& R9 K: D; U3 I; v% A: ?4 y! Q% k

6 _5 |0 Z; M; U: f5 y

# ~ s2 B2 o! U; B! t  ' G- i8 F0 n+ ^) f

8 o# O7 B0 y' v7 ?7 `; l5 ~

1 o( l& b! a. a P7 O( L- v 对应着avatar.inc.php代码如下: ; U3 I+ H9 B4 K s/ _9 J0 ?

4 {# F1 G, ]& w5 m" `$ T$ f. g

9 l; L4 k% Z; g1 U <?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 M* ?' _6 x7 o+ j: [% m

& K6 o: I x$ j4 V; p

( k0 \- U7 {5 Q0 h( \; v     case 'upload': 5 O( w* |: [+ W& B* w

! x0 b, c) [! z7 G& p

( H) r5 Q& Z, W# j; Y% I         if(!$_FILES['file']['size']) { " e( A% J/ y9 m$ L) T

5 s* u% v7 |7 ]) J

( j+ K4 c4 ~$ ~             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); 9 \3 o) ?2 w; b7 r

9 N7 o& ~6 [) k. W! d/ c9 o3 l

/ B5 m& Z2 y3 f0 U) l( I             exit('{"error":1,"message":"Error FILE"}'); # Y3 S0 Y. ]/ r

" d& |* z$ Y: D6 a

* ] V5 X; E; \/ v& _4 d         } o; u1 n* y6 u+ U( g# _

4 N$ w1 T1 j6 a2 \+ n! `. I

: L6 u4 }3 E5 o         require DT_ROOT.'/include/upload.class.php'; 0 f0 A3 p9 E8 h7 ]0 m* Y

2 R' `5 P1 m* H

/ G" a; k) q0 k U2 g   8 k3 F4 H$ e2 H% H& z4 d1 E

) ^. V) [! w _' K) ?2 P

! h) w+ l+ Q9 b$ k2 K7 y8 x         $ext = file_ext($_FILES['file']['name']);, Z6 H8 @& X- h) F

0 [! ^6 p$ d( ^

- g) t6 c% G' A# J4 D7 o6 k         $name = 'avatar'.$_userid.'.'.$ext; / Z$ f/ z9 X+ l9 ~

7 ?0 n6 _, m" T) R

. J- G( P; @. Z6 J         $file = DT_ROOT.'/file/temp/'.$name; 4 c1 Z7 `% i: p' U! n E. f. p P

- ^5 r, c1 R$ ?/ j7 P2 @8 t

, E1 j- Z9 B2 E) k5 _8 A  1 P) B: y# e& q8 e5 M

0 B/ U8 ]! u$ h, X* r* |% B, z

" l7 ^/ C0 l3 p( ]" P) ~# k         if(is_file($file)) file_del($file);' f& @3 d% y0 q3 m8 _: _5 }6 U

8 d, i4 |9 h$ H3 d) Z

! F5 O2 L6 y; l* U- r2 Z         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');4 z6 y$ D2 G( W% v

6 F( o, A7 \) g) H. `

5 y# k& G* ^" l- @' t# Q: Y/ y7 k   & }0 h. @3 S W1 d3 k

7 Q! f6 @7 N$ n1 x: ~1 R7 Z

! S/ B6 c2 v: P9 D: C3 Y         $upload->adduserid = false; 7 r: u9 R2 }, r e

$ k9 P7 g, C" ^/ f& L. ~; T3 a

3 S: }3 L; X1 w0 j  + u# w1 _. I0 @

( {/ N) R* G7 B; _6 _9 S

5 r* w$ V! W! h/ u5 J, L. f         if($upload->save()) { : E/ C3 }: k# Q7 f S

. c: [+ {6 V7 C- `0 U. ?, P; M" X

0 R; X% d2 C/ j5 V             ...) w' u7 B" O" e& N$ K; v

6 C, J- |, Q0 T u

; l M( P( v7 a" r5 D5 L         } else {6 p9 R9 c7 S5 d

# ^ N. I9 T$ \% C7 R

" `) R9 D/ l+ }             ...2 x# u" u! `. X( D3 m) C5 ]

+ \9 B% V: u& C0 t& }

+ @2 o1 c& L: Q' k         }0 s. K& I v- K) ?

+ j' T+ `0 Z6 G4 z

3 `, g1 l1 R5 i6 M     break; 6 c/ @* M+ R s8 N1 F

5 i; l0 b8 L, b3 Y+ n3 E

; _' V3 u. N5 e8 B; l7 c" ] 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 + t! T/ J$ d2 K; x4 Y( J

% @! R, n3 c, i4 r# m6 B, n A9 I

5 @( p8 Z; {" d' v' A: ? upload对象构造函数如下,include/upload.class.php:25: 4 L- M' S9 L" ^( u7 [

" s, w: {7 M% M/ W

2 U5 W: F- q( b, P/ U6 Y <?phpclass upload { " k" _# L+ y- L$ r4 z

' T+ d x ?" a3 F/ k+ U, e

, `$ Z" r0 @$ z! v* ?! n     function __construct($_file, $savepath, $savename = '', $fileformat = '') { 6 K) L8 c. F! S

0 |3 P( x7 Z9 _" I" B

* c- `; F% r. X. G         global $DT, $_userid;# `2 ~) j1 I! m0 W; J4 T) D I

! D7 R! @- I; g; l+ k9 w- b+ `

/ u5 l* h1 B4 A! q         foreach($_file as $file) { 9 T8 c; }8 |( \ V0 @ o* G4 M& E

9 v& M6 e+ e; l0 Y" D8 V/ y

' J! V( V+ S4 G/ e& R. f+ s6 j             $this->file = $file['tmp_name']; ) t$ F# x1 |4 z) ?

6 P: D5 e! \0 W" |

' N9 b* }# M5 v i: D             $this->file_name = $file['name']; 9 W' a" t+ j; u( m

1 U1 m' Z! Y; S. z! w% b

7 O) G/ Y( ^- S- z             $this->file_size = $file['size'];: d* K$ D2 ]6 C% R3 E

( t- g) e3 T/ L! Y: q/ e& J

/ E/ Q5 J( O9 K( A- s1 F' M             $this->file_type = $file['type']; 6 Q) a4 m' H* V' r

4 t; E! ]# p5 d9 q' S, d- p+ U

" g7 V% v- @ f8 B' ^/ B7 {: G             $this->file_error = $file['error'];: F7 M1 }+ v- `3 H

. i( f% T& p- k5 W+ ?& I4 V6 k' u7 E/ |

5 p! [" A" s2 i, [. ?" K* Y" p   4 w" j0 n- l" C3 b

6 u$ |; f8 R2 f; h; ?" V. [, A

$ i! n1 e1 F2 q9 e         }6 P7 ~) \' ~2 x8 v b* D

2 S. T# A* ? Q5 r8 D& k' Y2 a

# N+ `" T8 }1 d7 `. _         $this->userid = $_userid;% U, y. P5 _7 I. N8 \

) U( H q/ p8 D8 S( v) L5 c

: f/ V1 Q& K z- A$ k9 b# }         $this->ext = file_ext($this->file_name); / e' w- u6 v T2 g

5 D! u4 u' p: u

/ _2 J W; L4 G         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];; T7 }. [: y/ Q% J& P% X

% G3 s$ n2 \* y: j, @

% r( K* c% |1 b3 @& m% @         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;0 R3 W( ], P$ n: K

, G; W" ~+ v f t

" {4 R. m1 o" B5 F i0 j, o3 m         $this->savepath = $savepath;: y7 J+ P; R( k7 g& N& F; I J

8 q1 T: l4 [& ], j& W

' g) q2 T' m* G: a0 P. B9 p$ W         $this->savename = $savename; 7 Y& H2 [" ^% Z. t* P' f( ?0 }

, J3 v3 i& p" Y

, ^/ @4 b+ i" T7 \! n F# q     }}# [% J' n7 B( q* I9 U$ a$ \/ {/ b

- L9 ]8 X" M4 v' Z

/ D. K' @' T5 o* b; H 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 5 b. y6 O/ ~+ P& \8 g& N( ]

/ E5 z3 o' H" E# ]

* B( X" ], v/ w' C* O% k7 @ 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 $ |: `* s' |, ^# L! `6 d7 Y

3 S ~( E4 L7 W2 }$ F D! `, k+ b( d

# i4 C' M' h4 t. J% u$ w1 } $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.php6 \9 K0 @$ c4 K# K2 }) c

; u e2 p; }+ T$ r& e4 K! e

, L( Q, B8 M* M( k: T+ K 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:# J/ M) W5 j. l+ C

4 ]) `* k6 V- u& I% |7 ~. {& u/ q

B; a4 z4 u }2 m" I8 Y! l  1 ?' \; l1 e! X P4 H3 Z

4 ?, ]# X1 }7 `( {5 U) a+ W

& S& B4 O! y. `7 o$ o 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: 6 K) A2 {( J8 E& H

/ P3 X! s, [4 ^6 R6 L; J

: ~) Z9 N0 x1 ^3 i! C <?phpclass upload {, n2 q/ A5 R1 w- J+ W) b! P4 I

: T2 m. U" T- f% D5 \% m* Z

# D2 W c1 ~$ ]+ c5 y; v, A7 V     function save() { $ U. K# O0 i3 q' P

: k) V1 ^+ O& n/ t

2 I8 S8 [: l8 q         include load('include.lang');4 P3 z6 P1 }5 K2 w9 s

7 ~- j5 `: D+ k9 h. [7 X0 r

# T% G4 ?$ w, R+ Y: z' i3 h; o         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');3 g: I8 |: ^5 p3 W1 ^9 _' W

. C5 E4 g; H+ s3 d( j

" X) q7 i" \0 d; v; y  8 E4 ]' z; o$ S h

$ x7 t' T# j3 r2 M

; k5 \/ j. N3 j9 v) l; K, l; P         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); 3 J5 G" n: S K5 K5 t

4 X h; A) F7 q1 T/ r

8 A0 n+ T! u! m) q0 J6 f7 C  ! a7 Z) i0 Q) K4 t2 g2 m H1 U

( d# \1 ^8 Y( S3 ~

2 C; K8 P1 }. }) x1 H; V2 Z         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);3 I9 L+ y, J1 D& {3 T) l

9 W: K6 J/ ?+ I; ?3 F1 Z7 t& i' \2 O$ g

1 V0 h' Q; b9 A% Z# d% k, r5 {7 N  - k% M/ s& }+ X9 t4 O

4 R! {$ I) q4 g' m& x- b& h$ v+ s

8 _+ R/ E0 u; _- M6 K+ `6 B         $this->set_savepath($this->savepath);" ? a1 d. o: o5 N1 Z/ n, c4 [

9 h" P/ s2 R; Z

5 f; a4 l- C& L: w' |6 C3 v5 J         $this->set_savename($this->savename);) m! o" M$ I! z0 d, `4 M, j

/ ^: t6 \$ z2 T- Y& i

& m7 {3 g7 V' U" d   ' K3 y( i" A7 y! C

8 M$ m r& Y1 [) I8 M1 M

' Z3 n8 U/ t4 }) v o7 ~: H% _         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);- f9 k5 M* Q! |" L) Q& p8 _ q

8 A" ~: v) @2 l: x9 [7 I) ?, j

1 P1 C, T% _' x& R7 l         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); * f' J( \* @% l+ y

& h- E; z. Q3 T- y; F& D

+ _9 ]$ h9 Y# j0 r; D' x5 Q5 k         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); $ z' T. \) j/ j8 j$ {! [( \: k

% x6 S+ d4 |3 Y: ~+ l0 m

5 l2 t7 D9 J$ q- b  + D+ B8 @. }' C! ]9 g/ e

) I! l$ L. M9 p7 I" q6 Z+ c$ z

3 ^# l9 {* G3 \" `; H& O- t         $this->image = $this->is_image(); - H: {. J: R0 z* {9 @

$ {- g# x( }' H8 @) P7 n* V- y

5 x1 n! |1 k5 Y2 J5 @+ t2 ^         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); 9 h+ e& J) b5 q( f! M: W) n% [ ]

2 t; |% v# y5 K; l! U2 e

$ q" Q$ z# `1 z0 t4 @         return true;" x" P v4 j' f( [3 e

, p- M- M# \# \% c' ~# F. o# X

" S, }5 E" j1 I: a$ X$ W$ v8 Q8 {     }}+ v! D* F$ n I- Y5 C

B. `* V# E0 [/ J0 O

) i4 l+ E- W" E0 Z 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:6 @& q8 b$ }& ]2 U* g w

. Q: m& E, D/ h9 o; P7 w9 `2 _

, H. B/ N1 A8 L+ }. y <?php( x3 M+ l( m; {. {

4 s) a4 t2 P1 P9 Q2 r

5 x% Q/ |: f0 b4 }/ p9 m2 V/ f$ W$ U0 D     function is_allow() {3 l! |- h3 C' _3 o) c

0 T' y* v, l* [8 O" F+ D$ z

' `, O" V6 q# i! I" P5 X, V         if(!$this->fileformat) return false; ( \. \5 B. A7 u! {

+ Z$ y1 C5 I1 p: l$ r- H

+ l& n5 a7 D- u9 E         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;/ S% E2 |5 E9 c* o) H9 Q

) E% h/ x4 h3 P! e v- _

! g7 H. y5 Y7 x         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;/ F) @# P( F. m7 r2 O3 ]% c

1 w; N5 S/ B. x6 N* T

/ K4 Q5 |' Q, X/ v! _5 ^: M' I! S! m         return true; ) z/ ?6 w2 d7 X8 q1 n- W

% _. O- l9 x7 ~* k0 C

5 O% d; m, M, y- e, Z& @& e5 [     }, v, [. d9 p0 o4 N

; P: }3 J% L) g* O; _

, Z9 c9 g1 _9 Y 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。: F. S2 N+ z5 K

8 g; X0 R* u, l. I4 T! C# Q/ @

Z4 h9 R) g4 T) Z. c 接着会进行真正的保存。通过$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 H/ \1 F, A' F

) j$ m& F [% m& W4 M

0 e# h4 h: `( |& a 漏洞利用/ g8 S* k6 x" a1 H' l

/ [+ ^; ^8 d Q( Z ?* _3 x% f

. L# k: @; u, r* a2 }2 ? 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。: _, c6 ]+ U( ~. `7 [

( d% p" L# h" |3 z, a

5 n% b2 v/ j% y8 {' F   - t. d, Y! j% ]/ A( Q, S" F

% P6 ]: P3 |0 S* [0 l

" P; m1 Y N. g W8 u 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid) p% g" |) X% w2 z

1 z! y. v6 {! ?

# s+ p+ F, W3 L" w6 s: J 不过实际利用上会有一定的限制。0 r1 i4 \% m6 t, `8 ]1 v( L' E

, D, i) Y6 F! q: A' k7 x

/ Q3 O' p, { J, A9 k9 n. u: h 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 / Z$ q6 A0 [$ S6 M3 B$ n" L

5 t6 b$ q' Y( J& l

1 e) x F$ I& w' C$ Z6 Z) F2 |# f4 x   4 Z0 r+ n# ?9 v' L8 T9 |. E

+ x2 J, ?% p1 b$ c* y# Z

* w- q& F2 ^! M/ w, F/ c/ E% D- s, d 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: ; ?+ p3 H2 n( u! H, u

4 ]% U9 J! ]3 T% {" U4 Z

, \& a4 V( o$ ]: N; U1 T0 c 省略...$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]);省略...5 P/ L Q8 b& ]1 j

$ r$ N. n0 i# N$ N* [: d& v

1 S: ?0 _8 u2 @ 因此要利用成功就需要条件竞争了。 8 W' [: Y4 A# I) M0 R1 c0 Q

* c# P) S, a1 c' u! |' z4 d% Z

$ y# x& S* @, Y5 K* i4 V 补丁分析 & `7 A& z/ F: z2 E1 |

" ~. O, [1 {( p- u: B& c

4 U8 t6 J& a0 T" L! C' `3 H3 |   # n- M9 W. Y9 b3 O i$ C2 ? e$ H9 E

/ L* Z% s" C2 k) G$ `

$ E& C+ T0 T! @9 P! k 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: - j. _* x/ v3 i

) i. a! K# n+ v# D+ E* N$ f) R

( W0 l: c4 _/ Q function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}% h. J/ q) R% J: p- W- P

5 p) K1 l/ K& h* @8 q, [9 M

! ?( g# V7 F$ R) e1 T% ?  $ x. A6 p4 H! h

6 h9 b8 s. I% q- ` z5 o$ Y' W1 m

# u1 ]# c: u. ^% V! q4 x 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 $ o6 }* U& k5 ~4 w. t7 G/ v5 K4 A: l/ q

7 O# [( U, O' J0 h8 \# j" ^1 f

9 z4 W( B% ^+ f0 O! H2 { 在is_allow()中增加对$this->savename的二次检查。% K7 _4 n9 e- D& ]$ e2 j

/ n4 N; b) Y& R1 f( |9 o

& }' G* E& Z) r8 m$ o) j) n, L/ V 最后5 T; D; z8 Y7 g$ {. V/ e0 }

E4 B+ g1 |2 ^, u

% R. X9 K2 o& I% R/ b 嘛,祝各位大师傅中秋快乐! % q- |6 n$ p" H8 V% G

- j6 {6 E x* J

. `" E7 _+ O$ p/ X  . S% i# r" M& N" v2 F+ b: m

0 o& ^" Y8 A) X% k/ ^9 [4 w
回复

使用道具 举报

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

本版积分规则

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