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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
2 R, ?1 f9 y1 X5 a3 Z

% K% s) V9 R. e" S( ?

3 U7 r2 ?6 ^) Z# L

# R7 d |$ z( l6 I7 F 前言" B# x2 V2 F' O8 `3 U0 T$ w4 v

& Y+ [7 C5 |1 n9 `5 ^5 C/ K( J

0 y' b# w; w0 Y X% L 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 - C- p& @ j- |. V

7 O' m$ C: o$ V2 D' Z

+ M$ |* L* w D5 P" D/ n   0 R+ Q+ B% P1 m0 B" g% Z- q+ y6 _

" ?3 ]5 ]3 |+ r" _ f9 W, y, @' y

+ h0 F6 `! j1 Z9 E& D 漏洞分析 : R u+ }) ~/ ?9 E$ Z( d4 q

, m/ ?; @; [- S ?! }. [$ C

0 N# z1 _" }: W! q5 n3 O+ _ 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: & D- b! [3 @% y% Y, \- T4 h

$ r6 G( t) U/ N1 P4 m

, ]& a3 W5 D4 h- |7 J9 I   6 t, A" y& |: T- H: |

- n* W$ P+ q" q4 u8 L6 f

" Z+ a1 x( O6 m7 y% i& h 对应着avatar.inc.php代码如下:7 |# N' i! u1 y2 |

" K, W4 g) r c Z

+ B& Z0 R8 M7 P: g- O. u+ w4 Y- 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) { ' Y7 C( `0 J' o% I- e

) B7 h6 U4 f# j5 L" X1 Z

2 T# |5 j7 h0 |& @     case 'upload':, d; C7 U$ c/ |6 |" S+ C+ r

6 i7 F' E' X# O# r

7 [1 O7 U; c) o* q2 b3 _         if(!$_FILES['file']['size']) { * P4 O' Y# @+ v

. e) ?: k7 C3 g7 S# Q2 m* o

- z2 u5 F3 A; c5 j             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); " ?) `3 V1 r" p: o$ e

- i( M, ?1 k0 H( U

% w# L: D! e4 c7 B             exit('{"error":1,"message":"Error FILE"}');% N1 c. ?: P4 X( U; B2 |3 i) M

, V Z) J5 }+ b# n

: B b) s! P0 T0 O" T/ Q; ?         } / a7 Y7 c* N1 i

& R" C. ]' V9 \" r9 H

# _! _6 \ _3 ~4 j         require DT_ROOT.'/include/upload.class.php';" k3 x1 `7 c( z9 V6 o# q

, E1 o/ x3 h1 h8 M. e" q+ x

" K% u! E6 H5 Y @& R   ! K7 V0 U; l) k. ^, X' B

8 M, G, a0 H* |! O

0 e7 s4 i3 W( y         $ext = file_ext($_FILES['file']['name']);& o5 Q2 A- N6 w [ H

. q7 U" r/ R2 v2 P; m% l

+ s+ T) b+ D% y; k4 h         $name = 'avatar'.$_userid.'.'.$ext;( C( u4 k. O9 |, E" g9 z2 U

" Z2 z$ m. `7 N L1 S) w: n$ t

1 U4 i3 }4 q/ k         $file = DT_ROOT.'/file/temp/'.$name;4 b6 u# {0 H4 C, t5 F) u1 T! [

h E3 z8 e+ L& n6 @9 V) `, Y

p! R8 [! J% e" I( _0 W$ z1 ~' N. j" \$ \   - @6 f6 e) u4 T7 x) V

; C8 c( v8 Q7 w6 P

# F9 v) B7 {, T) r3 n         if(is_file($file)) file_del($file);7 g0 U! i+ ^8 P {

+ S( w4 `( A, z

& x5 O8 `: q5 F* u( d9 D2 x         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');+ }2 g# l$ _: W# E2 S5 ^' e

3 R: ^, {/ b; i7 F3 l# u" k4 g" x3 Y

: Q, O7 U3 O9 l; c* r6 [  - @$ t5 d9 E K% F; k* `8 `

/ @ U) R, G6 E3 I

- Q7 ]. N4 `1 Q$ Z# {2 P         $upload->adduserid = false; ' U) f9 I, G. }* l0 }, h( m+ e

, m$ ?) F. ?) j2 Z5 L E, v3 r: L# G

+ Q8 f# k# _/ z4 D( P   ' M! Y( D ~& ~' V: h$ } U

6 |3 {% [1 g& W2 q

4 i/ E( p% @( {# B* F& i         if($upload->save()) {& X; C i, I) m9 \3 ?

2 b1 g, a" G# A1 ^. u6 t' G: p" m

' l( c$ w( T7 i, r             ...8 _6 p: d0 A: N( h5 I

* C5 V, \- U f& s9 d

: c! d/ E" G, y3 p5 Z& j& J         } else {) H# L7 u4 e, z. @$ m

: C* v4 T$ ~: a2 e+ m$ h" Q; k& v. D: W

; o1 z* y1 ?; K7 T* v; c |             ..." R7 p. E% K7 w/ u- R

* Y2 U# C7 j6 y u K. Y2 G1 o; k

/ ^0 W1 ]: G3 x5 G+ g) ?! Y         }! |& D* m: `1 v" z" t/ Y

4 O# @5 U/ n4 z( H

$ C( q }, C) Q/ d! K# C6 Z4 ]     break;9 m* ]- [2 |) x" L

3 ^* P7 H/ b! Y2 a9 k

! Z1 q9 U; q5 [6 }+ G9 y2 Q 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 ) \% q# l6 ?: N- t' I

% D8 t( w! @; P5 x* n7 {3 q

7 K2 T& i# j( O! W9 V6 H4 }" y* H upload对象构造函数如下,include/upload.class.php:25:4 m/ q8 H6 m1 N/ W/ K% f; q

( H' U$ Y5 s3 N/ V

. c& ? B; O7 |: F. r6 S: h9 Q <?phpclass upload {; [/ k- ~( u1 ] U2 M, [

6 z) t4 {- |8 w( \) D

# o7 E7 `. I) M     function __construct($_file, $savepath, $savename = '', $fileformat = '') {5 E# u- ~) j5 n7 {! j

4 f5 V5 E8 }' ?

1 F7 \8 ^# F8 R# ^- v         global $DT, $_userid; 8 g* C9 ?3 c7 l2 d& J/ G

, a. x5 s( u: L/ C+ D

* m! ]8 o! I+ {. |1 U9 M' q/ ]         foreach($_file as $file) {4 r$ G6 P" I: [6 s

) |3 S8 v% q( u K7 Z

. x' m" z0 t" R/ X: X0 {1 ]7 R4 I             $this->file = $file['tmp_name'];7 d, g+ e, x$ e6 @4 l# F( O% o) g$ x

+ C% d% D7 S3 `7 i

( [* s% o* x' C/ W1 \             $this->file_name = $file['name']; ' w3 K- K, w+ {) m: k

1 r2 `0 }: e$ Z1 ~% k

& j6 w) W" E) u0 z4 E             $this->file_size = $file['size'];# T4 U7 E: l8 N4 ]- b

2 Z, |; y: p7 c

1 O7 n4 a2 S, X4 Q5 d$ @+ V. E! x             $this->file_type = $file['type']; ! v; ^& E' {- B, I, x

/ {: b6 ?+ P( V

3 T( D# X0 v! w5 r6 e4 _: _8 t             $this->file_error = $file['error']; 8 s. I9 H$ }( p& @3 j% R6 L& n5 w) T1 j

& r; w! `, q# o a

+ m" n/ z5 v" j2 D: y   U% p% x% Z M. A) F% A6 j

0 Z3 p! S& `4 v& A1 ^9 a

4 i' W( g. u. k2 O" ]2 F         } 6 Q4 n5 W! h. q- Z7 K& c1 n

3 }5 i& Y8 t- I: p! d* c( J* _

& q9 p8 H( u7 ], Z7 w3 P" W3 T {         $this->userid = $_userid;4 Z3 V% F7 B% A& V+ T3 p

9 ~/ D! K& N E: H

2 C' S8 k; x. f7 T5 V% x. f/ E         $this->ext = file_ext($this->file_name); : @6 C* _" p) X# ~# y" n( Q

* |2 R, z; F- l8 ]# @+ L! P

- _& Q5 E X" w5 V; ?; G         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];: W6 l0 }: }9 X; S4 @! b, R5 O

) @' M' ?# p: n5 z

5 |9 ]7 n( ~ k7 F% Q5 A2 P         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;/ M/ V5 K2 d4 s. k

1 {7 Q5 \* ~& M

/ _6 Z& M8 W2 T+ c0 o% \, n2 k, C         $this->savepath = $savepath;! j1 Q+ }0 V L) [

" ?5 u) F. I$ y+ e' w1 n

, ^; z0 B* @5 q         $this->savename = $savename;" E' R& u k/ q) U$ o' y/ l! Q% \

p+ y- ?# ]8 t

8 E) C* E- y i3 l5 |     }}/ [/ Y4 I: q0 m4 e" n! @

$ e6 y9 q# \7 E. q- @$ y

: k n# M# ]+ F8 H7 i2 a 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 * E9 ?8 c0 J' ^7 N

1 S/ L/ Q' q( [* i

' c5 H3 M( C& D+ ]8 J; J 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 , v1 j, ~- p4 o1 c5 p4 i0 _

/ c5 D" d! H' O6 p5 G, F

- |# S2 d$ q3 d. Z) B' J( I) z6 n! M- M $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 + a- |- i1 b: \

7 J& w9 [4 a: f; M7 d3 ^

# `# n. g! K' ]2 p; e) D 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:2 |5 E! s; N6 f( F* C( Q# Q0 d

+ {1 x8 F! n5 h& E; E' x" C$ ~+ c

/ I6 @9 S- H/ C. d  + F! T% ^' Q( ]% u

* T! x* H% f5 L

0 f; c S4 V2 h: m% i 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: 1 C: E7 Y" O3 R$ X( t0 I* {

- ]7 r& J7 y" _3 v6 S" o9 U% j

1 V& N& n( j; n+ l; Z <?phpclass upload {) m/ G; ?: v3 b4 Q

7 J$ @+ k, `) a. d1 [

7 O1 V5 c7 @) R% r     function save() {# {- H/ O T F- v+ R' B5 [5 v

2 K' S2 W& W, A

$ A4 z' O) n0 v$ W! m2 n6 P         include load('include.lang');9 O2 Z4 }: Z1 @$ K0 L# l: W6 A

# `, j7 I/ u) m1 i6 b7 _

# H' A" @/ `( t7 Y7 b0 V/ ]         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');' P. [+ b5 @8 S$ A+ j5 D

, R1 x! g2 k5 ?

9 I2 V! Q2 z6 P   & e4 ?' ], H! P. y* e+ }1 Y

% L& E: P/ T! J

) o/ M3 s- v5 L8 T* `9 H         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');& v$ F' }# u4 @# v; k2 ~& [' Z; u$ ?

4 Q( Q: ]* u1 M; b5 c9 S

2 l4 d7 n+ u7 |2 Y8 }) z' Y7 m  ; g- R# S. E3 m

' |7 _% q; s" Q. P& [0 ]' J

7 S4 c! M1 g+ h         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);8 G# K$ E1 D0 t! e) B

: N1 L& ` M( H$ F* P

, k9 h) x9 t6 _& p8 a. j' Q5 d  . M, ^& \. g. R8 H- B

) v" x6 D, r0 ?) Z

8 t, o* [+ |6 s         $this->set_savepath($this->savepath); , P8 c# p; v' D/ L

w5 Q# `) x% M$ x& B2 j

) n8 k3 i& D$ P6 r# B# G) W         $this->set_savename($this->savename);- E$ I5 Z6 P0 ]

' }. e# n+ J& f

: ?1 l' `8 K" j( k4 e   & l' v. J& U( g3 e+ J

/ x0 M: D, m* a0 B* u; X

; r O3 F" s& ]" g# d" @         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);" o+ ?9 e8 l( a B$ p. \: e3 h

$ @; |3 I _1 M: Z! e

% H! r& k, M0 W Q1 v7 e         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); c Z9 B, Y# k+ w1 K) j* O6 p

- S- W6 g; F$ v( G* T+ V

/ a/ N, c$ E. O' |( ]/ c, e         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);3 \! ~6 C$ [* I6 `0 i

5 p* |4 o! N+ _7 x, q

0 J' o0 p, k1 i# B   " e+ m. r7 o% g0 J, S. l2 f

: c" k# c5 p7 @0 F

. Q) r* d8 R7 q, {$ f         $this->image = $this->is_image(); 2 c4 o) ^1 X8 K0 L z

2 d+ Y7 s" q' N% t- ?! B T) T

# t0 I0 V6 L4 ?0 Q         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); " N, Y \; D. j

. ]% l# b6 n! s. c

) p! x( C2 G" J8 u0 E; ?3 ?         return true; ; @" e3 {" [& h

& ~8 a' K# R {* r- ?2 `

j2 z$ D; S( ?# C7 `     }}% C7 J. z. \ j8 J

2 ~. C. O& D5 E* f# q+ _" n

. }8 C4 P9 q8 H+ g/ V 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: * ]& T8 D; V1 [. o

( t* Q Z. M6 p

1 w9 c+ A8 n- ~ m1 b <?php( v, c, z! \. p3 W! n. @ m+ I; q1 d

, l( r& ]# \- X x

' {; U7 x& K8 t$ k     function is_allow() {* ~3 S! D6 Z/ a5 H7 t) h2 b

H3 w/ ~1 W0 a6 f2 C* L! a1 k

% J' O: l+ U. a6 z- u& {         if(!$this->fileformat) return false; + }5 _, m4 {: t# q! q

7 j x5 ]; N* v( n3 C: S

" Z W+ ?$ i3 `- N0 M         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;" w; B" d/ b; C) t( Q

, K2 ^9 D h; g7 p

+ R0 U$ l$ [6 @4 r- |* R. i' a         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;1 Q+ Q: {* H }; H, Q- D: ~9 B

. P8 m/ m W. n+ C8 U+ _

- w! E( E4 L6 u# D         return true; ; J# r3 R( `1 w/ q

6 O x% A5 t7 r9 {1 C8 @

$ ?+ j2 X$ E6 f5 c6 w: e     } + ^" @' l$ Y) f. W

( z% [2 { W! P( [

1 ^ l" ]6 S& b& C% R" [ 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。& L$ |0 } I% N1 G3 w+ c/ C

2 V( i- u/ h; ?* Y( Y; ?/ g

0 c) C; M+ ~9 p; U; x2 O' ~" { 接着会进行真正的保存。通过$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 h" r( `! T9 s' _

4 b; {( H$ G( }' C% L, F( U4 @

( R2 X8 ?8 T8 J/ G9 [ 漏洞利用 ) A9 x2 C( t: m) Y' L

! I$ s6 |# R& w0 M5 x5 G

. U0 @# t$ K( [3 ^ 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。' S5 @' w0 @( O

& _6 D/ l. h3 u9 g8 T$ K) z

: E" f. V4 i* O   ' R5 Q8 \& s) {" X

) O3 l+ J! b7 C2 n! _7 u1 t4 Z1 c o

2 |4 X; q6 z4 J- E 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid' F7 K% J8 h. e: K& D

/ V- C5 i7 |5 Y' j3 b K

$ l8 }# F: u3 }- e$ _" _0 b 不过实际利用上会有一定的限制。' L# ^$ A" Z& g0 M& t' X- C# ]+ x

/ E- c' S7 b+ F/ ~5 W: g

\9 L. J- J; h2 c9 K- J 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 7 Q# a6 d- M+ G9 ~1 S, s

h1 ?5 c2 q; D

4 Y- _3 R& j" M$ {( A8 T/ m) O  " n b" V1 r. A$ e' A) P

' e4 U( C; e7 A+ a4 C

9 W; k: I6 c3 l6 i0 E3 c7 ~ 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: c: Z, ]9 j% n

8 Z, c: o% w/ s5 x" \; |# }

% o% n# k' c; _2 I- n 省略...$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]);省略...( z+ k2 e0 f" O8 b _2 @

2 G4 z" K4 n" J# P

Z8 T5 @7 e; ]/ b7 R* a0 q 因此要利用成功就需要条件竞争了。6 G' R! j l3 `$ u

. b, l3 t7 c. [ I

) R. E% i+ E% i6 I) g 补丁分析 $ p; _& ]: t. A$ z+ _

9 i* b' ]: }# `3 o0 E1 U! e0 l

" H% V" ?; B% ~3 g# h; }: ^. a  $ ~$ y+ N" ^! b1 i# T7 O1 ~8 X

9 F. E* Z' N( x8 q& I" X

& E, f4 \% m% u" w* \. C& M 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: 8 U# h" [, I0 ^& k1 ]* t; P; F4 d

! \1 C' p, o: T1 c

! H" G% E3 g! u. X1 J' \9 ]6 n, V function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} - B* ~0 I5 G2 J6 m* \

X" @* o2 |) I4 y z

4 R4 g* u) R t* W# I# h  + |! }+ q1 m/ ~

- ]/ M2 M/ q+ D

* B0 }9 B% V& x* x7 J- y1 S# ? 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。8 v: r( a* e1 `) _3 K

. V3 L" J2 o2 I

8 e5 e L7 ^6 L( P3 b8 P 在is_allow()中增加对$this->savename的二次检查。 8 r' m1 W; r1 q- m; a, G# D

/ c# O" f2 n& ~" W% b5 D

# M: s* j/ ]3 R: c$ j 最后 * l7 m6 _% {+ v1 D8 N

" |0 \2 S q1 R* I/ J( Y+ Q, P: t

. v- l f% m/ g2 p. @& h 嘛,祝各位大师傅中秋快乐! 0 Z" J, C9 z A8 w* q

0 i5 [: y. }6 c& ], d$ p

; E5 x$ Y* h. E  8 O+ [5 B4 c1 V: h1 Q0 | G

! S" o3 z% r( R$ m1 b$ Q7 I
回复

使用道具 举报

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

本版积分规则

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