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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
. D. M8 a. ^& o2 q

: ?# d) U/ v0 Z8 m% e" _9 G

* Z. V2 k# Q2 o1 ~& F# ^

$ h2 s& B& P2 f; g0 N2 h 前言 ; @2 o* i& x" G* h

8 Q s# [& J6 _) g

; Z) u, C' L. K5 M8 E, h, \7 { 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。4 r- |! X0 P2 o ]

7 ^' c1 p; ]' I$ |% N% M; D

& u( V' j% Z% I5 a/ c. D C( G  # y& Z( ~5 C B8 m

8 E9 I0 k! t* A8 ^2 v

8 _/ o1 }. y9 R. ]+ n0 y ^ 漏洞分析: W8 Y" N, l# `! y. K s+ G

. H) k3 U$ l7 h# l1 N

6 x0 o4 Q5 k; ?. d: f 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:1 B' H. D$ P" P

; J& h( `7 n: o- @. K& Q+ X

3 b6 `, M! K0 }9 a8 S1 ?  + l7 p" a O# o8 s; s* y- c: b

" |5 I3 }# t: S3 {4 m$ i1 \9 ^- X

4 [# e* |& ?) g% z 对应着avatar.inc.php代码如下: 0 E% N- g0 z6 K) P$ D6 H

4 h; K) u" s6 W, w6 x/ h1 ]5 [

5 e, _# ], o+ I5 I1 u5 G U% I, } <?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) { ! O8 `( n6 e- m% i5 I E1 f

. r- M6 N. P, @: c. E& l

% O, b! Q8 j+ ~     case 'upload': 0 i+ r* H* ]) G" Z4 D1 ~6 o8 Q

! n/ U+ N" H B

9 A! L" b- i8 g" J( \6 C N# \         if(!$_FILES['file']['size']) {5 e8 r# I2 b7 ~* _- R+ q

8 Y7 r9 ~ T7 Z/ m) x0 e% f

$ T; q3 O: L+ R, D2 a: j" p4 `             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); & r7 g# S% H5 h/ y" x; m/ k

) }' B! h4 c% F, m1 x

: G+ ~$ l- t! h- \             exit('{"error":1,"message":"Error FILE"}'); 9 u- S" U0 D4 I) y9 ~. r7 `

0 r% j9 a4 Z1 m3 e

! c- E; x. Y( T         } ) L- M, n; C# w5 C7 O' M

( X% S( f- G* f) u! f

5 j5 U' Q L- }1 X1 R         require DT_ROOT.'/include/upload.class.php'; $ W3 G9 \4 W6 o9 F/ O8 C- H

- p1 Z0 j8 I" I5 G/ g ~; {, N

- @$ t+ N3 N' Y: |9 U   " Y& k* Q; Y% y5 e' n' E4 b$ Q

6 p x$ _# n _9 l' K

( }% X; \! c" z' H         $ext = file_ext($_FILES['file']['name']); ) ^& k+ L% E* \" X

5 k8 |0 Z1 s) Y" X

J: M' v4 f# L         $name = 'avatar'.$_userid.'.'.$ext;5 D7 S6 v1 S3 C2 a( c4 O- [* P

+ Q4 F; _! U: B3 M

+ O5 L) ^& q/ _3 \8 I$ u; ?( L4 \         $file = DT_ROOT.'/file/temp/'.$name; : x& b- H; J7 Y, Y! M" l: p

& A2 t5 h) j }- J& ^

( j- z3 Y5 y( W t( V0 I) P8 j- p   0 S# q" t# l) A6 |. k* F8 |

4 w; U5 ~+ Z5 ~1 V& W/ v$ `- U% E

+ m/ c' O% A( T! v: j$ u         if(is_file($file)) file_del($file); j; Q: H- ^5 i

! E# f( i: w/ @0 h6 i9 t. Z: U5 b

. V9 q5 d0 l+ Y Y4 N! B         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); " u, n3 d4 t) A7 t, U

N" ~ K) b# o" |: [

; E9 w; j4 L3 c. k9 L8 u: d+ e   ' `9 K1 {9 u: U) B

* q9 A$ ?& R, a* z

' e4 O4 H# m8 }4 l         $upload->adduserid = false; % `7 E( r$ R+ K8 J

# p. P$ h& u% k" D

% l7 z! B7 |4 t# d0 Y: h; s# n   6 P. y: O* i) e

0 E8 `# H8 @8 {

# f3 `) v9 p% U" k j# e. W         if($upload->save()) {: M' G& P' C* E

& d2 `! h' }' o: Y

+ M& ?) R/ |: Q' `$ Q9 t6 u% [             ... r9 v( ~/ A+ I

$ A" U: |0 [/ [) v/ h% h( [) Y

4 ?7 F4 f& w* a: S7 _8 G# ~+ D6 B) T         } else { / w: e! M+ Z, g( p

% M5 [1 a& W* c- k6 ~

- X3 t' ]2 K, i. ^8 a; B) [6 |             .../ P4 v& a; ~8 h* |4 P1 {3 V: E0 i! w

. C! u: u" x( e0 ]$ r

h1 ^. W! Z: T; @         }, T7 ~5 u! u3 |7 R2 N d

) v) }' w I0 u% L! ~

5 G. O* @" ~; P* m+ d     break; $ l0 V! V: j9 f. ^

: N; z" c& _! n" B5 E8 s

& R$ Q, i2 m" q) n0 v0 E 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。3 d" v: N7 N1 n4 q$ s7 Y. ^2 z

a: V: u; P2 M. a# q* ?8 U

e, K( ~% `9 I3 P* _: Q/ v upload对象构造函数如下,include/upload.class.php:25:: z! r/ g8 f+ @7 C

% B' V9 Z* O1 Y5 @% b

9 C, @" R2 ?1 `- q9 Q9 x& { <?phpclass upload { 9 [7 r M. _* G) ^+ L/ ^) F( [

: S- h7 ^. m; ^2 Q$ n% p# K1 N

7 V x# |1 R% o: R- Z: o, }     function __construct($_file, $savepath, $savename = '', $fileformat = '') {3 k0 q& m& o4 i! ^. t1 M5 `

, T) q5 K! e/ b# u4 {% W# j1 S- U

/ U: e( e% k; b- ]( ?6 W         global $DT, $_userid; 6 n6 S$ g* s- y, r, N+ O4 Y

4 w0 |9 R7 h( C, ^ k5 d- ?% X

7 M: ], Z+ r) ?+ ]4 w, N" z         foreach($_file as $file) { 2 o" a9 Q r: S j7 d

) B3 h% S4 ^: O2 p( ]0 l9 R+ |

1 I, e: o3 f# h             $this->file = $file['tmp_name'];, V) N1 F8 j M d

, ~8 M- _9 M/ R9 y; E& T

5 H6 i3 m1 V5 y# ~             $this->file_name = $file['name']; - _' R' Y7 K( H V

) x( m; l9 ^- x; y- M8 G

( u1 s; M/ N3 Y             $this->file_size = $file['size'];# Q3 V: A/ k9 C2 ?" ^

+ a3 w. _: f5 c0 R& ^4 G; h

U+ z5 e7 r+ j             $this->file_type = $file['type']; 0 Z. O z/ j! \0 F3 T Q# r$ Q

! `1 J' s# h* }; V

$ S/ d J1 \9 N* o( H             $this->file_error = $file['error'];8 @, s* t9 m3 x: u' y2 Z

0 G3 W: O: n7 e

5 n6 J4 V- R3 c+ n7 P; K+ V  % y' F4 d: h: g* z* R

3 u/ z& w% K' \4 R/ v

7 L; s% E8 T& G         } # W! Y7 t9 m! b3 w% c A. U

8 _, S( b2 A) d! c" `0 Q9 A/ c

/ w. c. B8 R- W. ~) H/ n# H         $this->userid = $_userid;4 y7 f0 N0 [* y- y

+ P9 k- r% {) k4 y2 i& f7 s

1 J( K' k6 T" p4 [9 v0 T         $this->ext = file_ext($this->file_name); . [+ e* i. P7 T" r

6 q4 c! r( Z2 D( U$ s% {( g

7 i% [( F% i7 U         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];2 I; a/ ~, v4 c: M

; B0 D# P% r7 B/ E) q: D5 [

/ ?$ t$ W7 S# ]- s         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; & E! M5 b* w9 L) i* c

+ E8 ~5 l$ `9 Z, L" X

6 q' E0 {& F) d- g& O d2 Q# ]7 L         $this->savepath = $savepath;- O; X9 ?4 m0 D* R( @ z0 O

1 ~6 L8 y! S r3 [

3 T! I, n8 a; {! L8 D         $this->savename = $savename;% f( b- h) w0 a

6 r1 a& l& |; J6 {2 j7 J( {

. ^% i, w( T8 c1 X; x# j. \! i- `5 v W     }}7 `8 Q" n/ X6 `$ L' y/ ]5 F

/ A# J4 p* I5 z A

) j4 y6 L# B4 {4 Y 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。" O, P4 Q3 c9 d0 x8 W b

; a* F* G: f# Q! C8 F

- g; l/ B1 @# b h 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 " A5 W5 }8 U+ q* c" V' n/ I

. A; g5 ~5 h; u8 P& b I1 [4 }+ A

6 j" M9 j* c1 b( d2 @5 t9 ^% b% H6 } $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 " @7 _7 y% _7 f, x" e* o

0 z) J$ ?1 @- S# t, N! C( }4 N

; S4 x) X6 S. k' V( |! ^0 C 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: ) |% \+ v( ~! v) a6 r) X, Q; |

' I' [- W/ e+ \- A; d9 ?! Q

( T; I& ]* p& ~0 s) v  8 t8 |2 |% R$ }6 h

9 h) ]( N; Z# w+ I# v$ Q7 _* f

4 i: S' r, O. K0 y Y 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:% v! I- |. }9 C4 I" e" {8 ?

, r+ \" ~- R1 E3 B4 ^# k4 L9 x% z# R0 k( @

! r8 Q+ f6 p7 r <?phpclass upload {& y0 ^2 o8 f8 a/ R: U

2 g: Z3 g' r; y8 C0 t: @' _

' ?5 r _9 g; z! t# D6 v     function save() {+ Z3 ]4 t( B. E! y2 {

, m' O/ ] v) `) a

3 i: I" x. `4 }/ _9 P& x. O) ~. d$ x0 T         include load('include.lang');' u* @" V7 \& J

0 h# ^) O2 k; {" O/ U

- j9 X( T* s3 A; e9 V4 s         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); & Z/ V# V* `9 q9 g

, q' i# I" u9 v" D4 l

/ L1 j. k3 X- l( e5 ?. _2 l& O, ^  : \* f3 e1 g3 O! O$ k( i( _

$ R* O+ T) b" Q) p/ k

, g' v+ u, F) I3 \         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');" S7 n, z w' x: O) J

" W7 E8 n3 l5 B9 y

0 f1 T7 }* [6 o+ b  7 Q* N, ~( M! Z* c5 v9 p

" t# j. R, U4 X$ l0 h

# X6 q3 s! p$ m) V         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); " o& v/ n/ F, b D

. A4 ~+ e+ u1 A: ?9 D

7 R0 V! W: u# a7 w- o   7 ] i8 w" p& q( a5 |, ~' a# A# E

8 ]$ f) I& n6 ~1 i8 |% Z8 M

8 J, B& M2 i2 G l$ g7 k         $this->set_savepath($this->savepath);) y9 ]4 @8 U# C/ b; }

' W- g! h/ Q$ g) C& G

# g' Z' _; w: p: ^- c6 K) o8 p: i         $this->set_savename($this->savename);5 X- _) S: q' k$ X8 X# Z

) x$ |5 s9 A/ o6 K5 M4 o" t

' X. B/ O# X, c# _( s   4 z+ d M& E Y8 {% P' `5 X0 u

" q" t- I* G% t- A' M1 G

w; ^9 e$ h4 P$ r+ h T0 R( ?5 e         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);$ x4 \& `: a% _& F

& Z9 ?4 n; }4 { B% D! L8 K

2 C7 S% ]8 _/ {$ o' I0 Q/ f" X         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);- t2 ? K7 I3 H8 z: i- C

* C. k7 {& O; C

8 a8 y# k' g) F: s         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);& g* h' P7 T) x( H6 ]

4 ] w; r1 G- e, f) g$ B

- T" E3 n f$ H  0 x: Y6 G# K, L6 d/ E

- ?3 k x7 B* o1 c9 `3 z9 \3 z

1 X- @" P6 b$ a8 O         $this->image = $this->is_image();' r$ A/ g" `. Y. G6 X# M: }

8 H: M+ b3 t2 @( V2 P

; \+ Z) ?2 h1 ^" D: S/ W! z         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);& ` f! f3 f' m3 s& y3 C

, o" y f- {. n7 o3 z1 U' P

9 w/ v5 Z$ _: _- s         return true;, w- Z- |5 E8 P5 w: G, {/ i

) B T$ \: [2 Y1 c# {# s

' I" t9 k' a$ v     }}" E8 z2 r3 q, a: R e+ C W+ Z; j4 y

3 `+ U$ f4 W( t; O( U5 z/ _" Y

5 v+ q2 L, |7 t 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: l9 Z6 e7 ]0 e

9 ]$ d; j. y) N" c/ M

0 U4 [8 I; I& t/ t2 z0 u <?php ( t, p: U5 u/ W# k Y! A

# ^( _( G$ H1 v3 E9 X* @* k, v

& ]8 a$ S/ O0 o& ?& I1 u     function is_allow() {1 `3 X q0 z( P/ g

9 x/ |. E8 A8 {( v# S

' c3 o1 a& q2 g$ A: g7 V( |         if(!$this->fileformat) return false; 6 L, e# i: t& a! h/ k/ R$ F, \

5 E# a x( p3 m! g! y) O/ M/ Y1 N

1 b1 y' |+ ]; D' @2 A         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; ' ]2 m3 D# j) f6 e& a: A

& Z( m! p) A' s" J* k; `2 b

: W7 q3 X* d0 S' ]. o. 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; 4 a+ q" Z4 S: S; E: f0 j5 X2 _

$ O! e. F/ k( `- v7 A) L

, @3 z+ ^- r7 S& \; d3 [         return true;# s) |1 t+ ?: ?

}+ E% |4 \* H3 H. r# D8 x2 {' t" b7 C

6 t* B- x4 R* q; V     }2 a. ~9 N: m9 E

8 O4 o) e- }2 n# Q

, ?" ?+ r- l' \6 s/ A 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 ' K+ D& u# M2 i# C. W4 H4 f

" T1 L( H8 u$ B7 ^* V

7 c9 Q4 a8 v+ e 接着会进行真正的保存。通过$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文件。/ m* r2 e6 V" D V; j- s

8 H( T2 Z6 G1 R" O2 _

& K% m$ n" I: ~5 P3 ^ 漏洞利用 # Y& P8 K4 k9 r7 g9 k( P8 ?% w4 Q

6 x m$ F) x6 [/ e* z, U$ d9 ]5 n' g

$ M, W: R3 ?5 p( Y' V. I9 q 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。9 @9 C/ A4 e% }4 d

1 |1 I. @, V! i; t# x6 g

4 ^5 F5 ?( _! G6 x- A4 _3 l   & p; s* ~/ f& @) n. }) n. e

4 N4 M' u+ W. s

- C; o% p& P0 d& T 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid8 {# y3 N4 j5 z7 e: q+ S' E

2 T6 i) D) G. y% J- p- b- F* Z

! d' b& R. I6 h w 不过实际利用上会有一定的限制。9 [9 g6 n! _! H

/ f! K, D! \: m! Q

& @4 f& ~; \/ j' h: Z, @1 g$ u3 s 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 $ s7 r0 B4 I5 {" n0 M; T" M8 W, y

# G$ q3 t1 E' F6 ~/ I: T( ~8 F, ^

! o% J+ k. U: R9 o  ! R8 J: H4 F5 g, ?9 ^

) ~; H1 A: {/ Y! l2 w' Y& F: j7 d

! R! M. ?% z! G, `# B3 i0 a 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:2 \5 n f$ m) y1 j9 y5 Z

. G" k1 @4 o- Z

9 ~5 [9 o4 {" y$ E2 J7 D! Q+ R6 B$ B 省略...$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]);省略... % H5 \- J3 S$ D6 s1 b

- r0 V2 s6 D1 [

6 V9 X" L6 J# G' x 因此要利用成功就需要条件竞争了。; j- V! i! |5 p, ^2 z0 }: d& Y

) P/ v/ n4 U$ e

5 s% w: |3 ~# C: |8 C 补丁分析 9 f# `* z) {* o# i0 z1 @

1 \' ~( w4 I$ B1 q

/ o4 z) x7 ?; W. y. s" z0 u& m   6 d% o* ]+ J* [8 _# {7 {- z3 X

Q" p' p0 b2 {& ?

! B6 u) o- Z' w8 c1 W4 c 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: , |1 }/ s2 ~' X6 _4 ~ e3 [0 A

" @. D# M9 Y. Q1 W; a

3 J M- U+ W, b3 G6 e; d0 r; b1 B function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}" Z7 d0 N+ O/ \4 G% S0 K/ C/ Y

R4 e! q: B. \7 D: J% k

! s1 x$ B" z' r' |   2 l7 f5 i) B2 k6 P) o7 {! z

' t* J7 r" Z `# h1 ] \

# J$ D- B$ f: X) m) M 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。8 g0 W$ r* s5 O5 T" J5 _. g2 |% e

8 `; }$ t, N3 L# s& j

: B0 V" t8 S4 L b3 V+ w- @; I 在is_allow()中增加对$this->savename的二次检查。/ ^1 C }) j. J/ p9 g1 x

1 P5 @9 K6 |5 Z4 o: h) \- m7 W2 }

) T) F! c2 ?) s) u$ \' N p 最后 , Q2 `& ?* q$ z( ^' V; ?1 D$ q4 c$ `

+ {+ X3 i4 j' C u5 s! \

' r) ~) e/ T- c/ e% B+ @8 a2 E 嘛,祝各位大师傅中秋快乐! 9 ?. b3 k5 \7 r% e, s E, K

& C; i' I. b- x

6 i4 z1 w0 @% `/ K% I0 h7 b  0 E, G/ T: J8 v& q/ I* n% J

# A* ^2 U1 [+ V0 W% I. X0 Q/ ?
回复

使用道具 举报

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

本版积分规则

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