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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
: |; f7 c$ {) @6 q

" K. }& \! f2 ~4 Z' f# V- A

3 e# I% \5 L5 }3 R6 W* P

; n L. u: t/ H, [ 前言 $ c* h! s& z7 r7 X- `3 d

2 ]- g9 P5 s, Y- Z

; } U& ^& D9 K+ x 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。1 j& E; L, q# w

, M9 I: O+ j' g9 m. N+ V' l

1 d8 |$ t/ L. j* m' _5 ~   m9 T5 c- { T% }) F

/ C' i8 v, ^5 u7 v( z0 B% v# u

- r$ {5 U' V. S# t( S8 R 漏洞分析 , q& z% D2 F$ n) y# ^

' L: |9 \( F# _- z" k0 K

& @+ x3 O5 O' o& q1 H E 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: 1 H+ |% X& b, F( M- P& J, b

3 v- K+ I5 W* J A9 J

3 j" k3 B( B1 V4 {3 C2 U& q  5 W$ H) ]8 r4 r: i0 _( @

4 j9 ~' N* p$ g' t$ t7 k

6 T9 U+ G8 Y8 m: V 对应着avatar.inc.php代码如下:4 v7 H S, X! `6 f5 U

) N& G. h) s4 ?0 l3 U+ ]

) V: f8 }( h7 _2 ]- ?; s% J <?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) {1 ?/ u( j: u3 m2 Y* g

8 D4 V7 x* Z8 X

- j3 H" o K$ `: r3 l! R, O     case 'upload': , M7 |+ n0 _& {+ f; S

' D$ @+ P8 l/ P4 G* X

/ Z- V$ z" L0 Q' x# v8 ?         if(!$_FILES['file']['size']) { * E! e0 b/ ]; c2 m% j0 {

3 D8 y1 Y4 ?4 |! ]

; {5 a" Y. E! R8 \             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);$ _" E8 t3 X3 d i5 P/ z8 B2 H

& a7 j9 i5 W6 s+ ^& ~# [# H

0 n4 b: Q( O$ B: {- H2 U             exit('{"error":1,"message":"Error FILE"}'); 0 ^4 R- D. B5 m5 O0 M& @4 e

! v$ T* f1 q1 d! @6 O4 o

2 s, P" e3 J$ G* h: \) `         } 8 l6 S$ u7 o5 X$ y4 j2 \8 Y3 a% W

/ x4 X( L3 B9 m2 N+ q( I

# S7 C0 ?+ a/ y! Z i+ J         require DT_ROOT.'/include/upload.class.php';2 V+ O3 p0 P$ p6 `

$ x# ?' S1 u( d+ N! R" Z( J& S

+ @3 `9 }$ A0 ^4 E   9 T2 y& j/ t7 T7 A

1 n! [* Y/ g% k

0 v, d% k! i* ]" n& U% ?4 x/ O+ `         $ext = file_ext($_FILES['file']['name']);/ B' b, o2 X8 i% V

/ f2 D0 I' P' Y. d

* M) f! z9 }0 O( Q6 D         $name = 'avatar'.$_userid.'.'.$ext;" `1 _ z" E! x4 `

% A1 ]0 J' B) e

9 |) e0 m0 D1 {         $file = DT_ROOT.'/file/temp/'.$name;! ? z% r$ z! I4 J. F% z

2 |( |8 A$ G* b( n

' Q4 H1 D* q- ~+ t( Y  5 a; C1 o( l# N- _

( ?/ ?* ^, y* T" Y- [

! k- d. h) P8 W/ ~0 b" J; _ G         if(is_file($file)) file_del($file);) _1 w) C/ n7 V3 c

. q6 E2 \$ {3 e$ B

2 C) W+ P/ [, N6 j* N0 \2 \8 D/ C         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); 7 J+ }* o3 P, f7 r2 a- {

8 j/ z- n( W) W( M/ o$ H$ A

- u2 M' U/ |* j; H5 A   ( M, A- n8 H$ E% C

+ t% I% p; E4 o$ }; q

- G6 r% F2 _: a' k2 W# t( C9 j         $upload->adduserid = false;0 p3 T5 p/ \. A2 ]; K8 ~

8 U+ a! R0 U$ \# \

; ~, V2 T, B( V" k# U0 T: s8 L   1 X! [9 l* s& [" ~' A9 `

. u) Z8 b7 P6 _( [3 G7 l1 R( O- U& _; k

3 b' V/ o4 ^' P5 `: N         if($upload->save()) { 7 ?6 y8 R$ p) v2 K

O5 v- V1 }1 d' J+ e4 I3 ^+ e- Q" f

7 M. u% ~) N( o1 }             .../ Z, {* c! P; n- R! I, ~

' M$ d% }0 g6 t9 {

, W2 I% Q% D0 k7 o         } else {$ W4 y- d" D1 G7 M- g* s- E( f6 g5 E

/ t0 f1 [* ]* f! t4 i2 _& }- r

- W, Z. r, y6 ?' p. P) M+ j             ... n: r2 t; Q# V- T; D9 A

! B( N/ G& c# ~1 |2 t4 y

! t7 o& q+ t) y% M ~! W& x         }% B k2 Q- m+ k0 D0 f9 ?

A9 x: L2 S" h! e

4 J4 t8 O- q q3 V9 d. U& W" h$ d     break;/ l% Z% F$ i( x/ @ t' K

1 O Q6 H3 L0 Y; ]

5 z3 F$ U8 M" M. H; @. m* @ 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。. ]! Z3 {$ J8 p8 K) U# q& ?/ K

1 m" E% I" Q* M% {

5 P' V8 K+ [* s+ d; V* z upload对象构造函数如下,include/upload.class.php:25:2 j o& T+ b$ J

" ^. R& \1 N7 Y8 K# u

5 E+ E5 u5 t6 h" [" i3 m r! R <?phpclass upload {( {" e" A# {# ?" V$ P" u

. m- o% \" o4 u7 P- [( c

- z' V1 z7 G) A     function __construct($_file, $savepath, $savename = '', $fileformat = '') { - S9 G+ s' T" X, N" _5 y1 t$ P) P9 O

4 K0 G9 v+ ~% F5 X8 j. j# `

- c+ {9 L1 x: Y p/ z         global $DT, $_userid; & q3 \$ O/ @' ?4 S# G8 o( F! J f& q$ ~

. D. [. d/ ?3 k. b( K& V

9 X3 X4 a& x5 S L' g* T& O         foreach($_file as $file) {) U# W9 a, r4 r9 S7 d1 ~5 y# s$ ]

" z1 |% W: n0 X/ a, G, i& \- p

3 l9 a$ V6 B0 g, T7 P1 G6 ]             $this->file = $file['tmp_name'];+ p+ a b; Z/ h/ ?

; M# Z- r3 S6 K# z

% e; Q0 H, \7 P: Q             $this->file_name = $file['name']; ) f2 i4 ^% |$ y1 u

& Q. E3 G, T( D+ A8 K

* Y! N8 }) U4 G) S" h0 B3 \             $this->file_size = $file['size'];$ m4 J: s4 E# R( F6 S; G

* {3 i& w& e9 v4 O( e

( H- y- G, n/ i. ?, b$ U             $this->file_type = $file['type']; . Y/ i- h1 f/ c3 J$ p6 `

; S$ ]+ _/ h3 I# [

( N; q9 t. v% D5 _$ J- K3 u& q             $this->file_error = $file['error'];: F4 i( ^* N1 e9 U+ p- j2 p/ N1 m

^7 J* _5 H: S8 c8 J' Z

2 y; B$ x# u6 T! }  / r4 z, z: F0 `9 g* L' r: I9 h- ~! |

5 k8 K- Y" j. p, f+ ?

/ t0 V9 S9 e* `! ^ T, L9 c4 X: C         } * [7 V& s- k. o, `' [0 E% u3 t7 L

5 d, Z* K/ G0 J$ a# x

6 E! U5 j, r* V0 h" Q         $this->userid = $_userid; * d/ i$ i2 t( \

" Q% d9 o: P, f) Z1 f. _6 s

4 q. N, r9 Y) d         $this->ext = file_ext($this->file_name);% @4 f$ t# X5 }; X

8 s9 B4 s, r* W( l1 ?% v2 I s

) T8 L" l; ?( n q         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];% Q9 P/ e; r9 g0 u9 z- P6 b

$ Z% t v( X1 [5 y9 e4 P% N% C

5 U/ a1 V% J, j' }         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;4 z( c3 D2 {& ?* V7 P, J

7 X# {7 \: r% y# h

$ u& j( n. e$ u" Z2 W9 N         $this->savepath = $savepath;& x" w5 z H' ~! f) [

$ J8 V3 ~4 m7 [# ?$ C

# Z. j, |( I+ M+ G& D         $this->savename = $savename; 1 C7 O$ Q2 B4 w+ z5 y& ~; r+ r. e

+ i7 D, K* V8 u: \3 ?* w5 e/ @

2 P+ Z9 t0 U& H3 R     }}! k! B3 i9 p. b" c# O/ R

5 ~1 W- Q. ?( I) G2 r7 B

( b$ v b# k0 Z: T4 q) L. ~2 D 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 7 ]2 b: O8 @! u8 w

9 ?. f8 v: r) q% P

" F0 L, g. q, Q$ w; n6 t 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 7 z0 h- [/ Z) E; K; H' o! I

- ~: A+ R2 w2 }. \$ x4 t! O3 \

2 J- [) [6 P! j $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 S$ d$ `: \5 O

# ~: P# b4 z5 n7 D( s7 J2 F3 n9 y

) O. J5 s& v a- a# c( A" R5 o1 B 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:. |8 V4 n% z! F! u3 c9 S

4 v6 z* p# V# @& z

5 u4 [ Q! e) ^1 u" M6 a) \( j& x  " W$ Z) D& T7 k2 \1 p

; s" }4 Q: Y3 ~1 S' b+ F' c$ M, Z( {

! Y* s1 {$ o3 H 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: 3 P0 G" S$ z6 v: }* ?0 _+ T. P! u

" T! ?% Y& n+ Z, @' P" S

4 Y; n$ J, q B7 g8 J <?phpclass upload {7 a' L2 i& L* i& U' L* N" R0 e) f

5 t- z& V/ ]4 U7 x2 e; q1 x; G; G

% ?1 Z; V; p& h$ Q" j     function save() {4 M6 I/ f0 g0 i% v1 A9 a

* n3 J' v6 q2 x; L" _( v& t

) A$ a$ W% B( z, y- a2 U         include load('include.lang'); 4 q9 Z, n) |4 |- b9 M

$ B2 m2 p3 K9 k) t3 ?

; a8 E7 y/ _8 W' q4 c' r) G$ J+ o         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); 5 ]3 g& W& L5 m B

$ X: |1 d) K& V8 P5 S( `/ C

) Z1 O) S/ o) f% c9 ~2 H   * i3 D0 i2 ?- E# Z6 i) l

+ q5 x" j b. M% d2 [

. Y$ V `2 x4 F2 ^" _& [         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); : ]" v1 Q/ [. ^6 m" i `7 }: H( g0 [

% ~4 y, q3 D$ P

" H: D/ I: U0 c4 Z/ H- F& [* ?  4 _) p5 \1 J" h' Q7 F5 T+ V9 p* R( g# i

4 ?7 q! ^& U0 m6 a2 p5 v

2 A8 n# i/ C' ~! H4 ~         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);/ ~+ V7 G+ ^' [4 e

& t: ?. D' F! e+ l7 j' Q

- p! r( z1 P8 p* a. d& @( J0 Z   / A2 k! A# m" [& V, \" C

2 y* a8 X b4 y% u+ l( B

% T) `: a" f. M( }3 t         $this->set_savepath($this->savepath);& ?2 _* y L" i" Y0 H2 f' t

: m e. E5 j4 p6 R8 ^

+ Q* d n; l4 ^. |- `         $this->set_savename($this->savename); S& q! e# z/ n8 k" L( P, J u, Y

- I3 P% n5 |: c) n4 z8 M+ e1 o4 B

" f4 R# x2 E. m) i% u& n   6 u( _, W% l: C& g4 R/ N

# P9 U7 M- E2 x" k+ |

7 N2 G/ `) e4 b. W9 v2 e% X         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);: I9 N, h- j& l* M

( i$ A9 t' x2 f- l! \- Z# ]

4 R, f6 v$ p* Y& Z; W2 O         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); ' O4 `" i2 S) C, Y2 m+ g/ R

. z6 C- V: @5 a5 D

$ S! R4 b5 w! t- `7 N- {. \( t         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);" B0 g$ G' h- \$ {" d

# I2 ^% a, u. p% ? s

0 ~% J# X! Y# m1 `  % l. X" {/ g$ z- V! w& S

. {0 \4 @7 o/ Q t. R

+ o. f, e9 r' S% V i         $this->image = $this->is_image(); & b/ i" J; E2 U

4 _9 i( q9 d+ S

, x/ j6 u6 Z8 d0 z$ Y L         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);/ j2 M, G5 W1 s" T6 x' P

7 f: T. o% b/ i. {. _

' S: @- M/ A2 d0 R6 p         return true; & F) X5 d7 s5 I6 B2 y- }. b) L

% H/ a% [* P; ] m: |2 |

4 e. I8 }$ W! |' P     }} * I$ \( _$ | g5 E

8 I% ^$ f) q* _. l& K B8 G0 W; S4 ?

, C1 \# i0 j6 b+ ]5 b 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: - C0 @9 }, h: W0 X; G

- z- `- K$ Y4 X" v; v

- _! J, C6 E, z" M <?php, |& ?1 \. |$ F5 X3 B' K

- U& u. e6 L2 T$ q

" R- e( f' U0 V. }" F2 Q     function is_allow() { ' Z2 i3 {* o8 O3 \6 D

0 H# D" A' k% |# z- e4 T, @

0 A! ]' W8 n( |) Z1 y: e         if(!$this->fileformat) return false; ; \' R! |" t% q5 C/ K6 r( j

8 k' j& F/ `' S) L) V6 K/ [

* K: \; J$ b) j) V+ \5 g8 [; F         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;( K3 g( F) v6 e# B. e2 \" s9 G# W

' }: u( F0 [! w" i8 p! V" |% U

0 z% b! M' A- ]$ W         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; ! w3 C. u/ x* |9 }3 C" t0 T& z

6 e8 Y/ t5 ~1 i

/ D, v- |9 J) x9 s' F$ L8 m         return true; + G3 X) [0 a% ^6 C' I6 v2 ?0 f, }

+ Y C8 @- ^5 }" y

6 p+ ]& m! [/ d) |5 h     } 1 [ k7 X3 g, _0 X

1 x, U5 V( A# w2 o4 W

& u8 K% s9 h9 @1 l* f; G& K6 m 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。9 K# G, Y# V# m3 o

% Y; ]( E$ `( l7 ?

0 P, D: {$ r" a& @: L: Q* u4 ~. U0 h: q 接着会进行真正的保存。通过$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文件。 9 S0 d/ H7 s1 c; C4 g; z

1 B* b* |/ ~5 E! x$ Z$ v7 m

5 c. [ ]: B; {6 x6 M. x6 i/ I 漏洞利用, h4 P: P/ x9 P4 X9 B" ^

u& |2 W! ?, P3 m0 l ?

7 }) O! X5 t- }( }$ F, M; L 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 7 o$ y H" C2 v9 L0 P

; f# o5 N% X& e

4 l0 e( P* T: u: r   . Z1 r" G0 \9 F* o/ z1 m

: m- C4 X/ m" b6 [* P

2 X6 f4 x7 F, v) V 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid% n. l9 N3 ~# e1 V" h

3 A& d% i8 S2 k

+ w; P5 C) l) U% A7 b: C: D1 F 不过实际利用上会有一定的限制。 5 e, L& p/ c+ R

) W. _ k& ~% C, `% p, i

2 @7 q) ]+ ]1 R 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 - T3 _' P }0 o e

7 Q) g4 X; ^- P1 _2 @

7 i+ W) c* @6 P9 e2 g; k   ) z* U, H+ j3 K7 ^

w( d: k, O% P- N4 W7 ]

$ ?, w$ v# R( z0 v 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:0 h V$ P9 s0 q" [+ `$ [

% _$ g; `3 m& ^; x

6 a/ T- m5 m: n J u 省略...$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]);省略...( A) X& X5 Y& |, J: A8 t+ x

0 T N! b5 Q2 i. Y* ^

J4 ]/ ]3 a, R L1 k" T" }# s+ t0 a 因此要利用成功就需要条件竞争了。# a( [7 y6 p3 H

' `8 N {; H$ A' d! V& c9 J1 R

/ L; N( Y! r, K- X9 ~% A a4 o 补丁分析 ' ^! V. n3 T- [4 ?9 x: V

5 M2 v0 ?( g- H7 N9 ^5 n

M/ w) P* t7 s6 l  4 S7 F; W# @' ]( Z2 L$ J- l

) F2 J9 r* }/ l8 d; Z# V

! a5 L- T8 U L8 |' M1 ] 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:" j+ C- H+ c6 W p6 r! P8 u

x, ~9 h# J3 [1 [5 |

' R, V, n% M; w% b+ `; ~ function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} ; p! q; i: z: P% W. k$ [

. s5 ]7 b( m7 Z" S

# C1 s# |* ~ I x( B5 p1 U5 r# ^( j   L$ i( F* {7 S1 E# a5 h4 }

3 {: `7 I5 S' A2 s1 l0 T- p; L. _1 p* H

n6 s6 ?: V& r 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。3 T7 s+ d8 B/ U& z$ f, A

! M8 I6 g. c8 Q$ d9 m- X* f9 t

8 [5 m' [5 h! S 在is_allow()中增加对$this->savename的二次检查。 3 I" A" n5 M8 R! q

, B1 y6 W' z* Q# r1 u) d8 h' p

7 \; W: h ?; u" b 最后 + P6 X0 M' o+ @3 `+ Q+ O4 _8 F

3 E+ U3 b+ _* R8 E. C) v

9 A8 i: a1 n/ A 嘛,祝各位大师傅中秋快乐! 7 Q7 K" S+ u2 t8 ]2 g. S* K `* q

9 m2 @" B) t3 T+ K P

- u3 H0 ^- F7 H+ `7 s   " L8 l! P) K; q7 u, N. X

" I* k2 E6 ?/ B4 Q2 i r q$ H# N+ Y
回复

使用道具 举报

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

本版积分规则

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