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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
0 ?2 J$ k0 y! b' e' {+ l% V

3 K+ i/ J4 ^: r' t

+ O6 F' C) i1 Q

W& C$ l7 R* H 前言& E$ W* @, P/ L; ?$ J3 G

" q$ H$ i2 e9 }% G3 x. i" A E

; A: z9 L* q! u- J) x$ e1 J 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。- q8 g4 b+ k" E# J! R

3 Y/ c6 n$ d1 S0 B

7 E5 o' W" Q7 i1 i' @   2 A- h" P& q: F7 z9 I/ q

7 U3 o' X/ { m0 O7 [4 u

+ ^6 r. W' L# d 漏洞分析 / {1 R% V4 P3 N8 {& S

$ a! N$ [. c a4 \

o) _5 a. J5 Z 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:- d2 f* Z* l( W) C& w3 Q! t4 }1 L T7 {

/ T& Y9 {# O: U$ I5 Q

# z( m) n, v" S' F   # x9 Q) V6 j2 Q& l; t. D

7 C6 V% i0 F+ X& C+ W

" g {0 D7 Z# e; }. f3 C 对应着avatar.inc.php代码如下: 7 w }! R% t+ G/ D( G: [

, s, m7 K% w1 ?* m

( C, |! f/ j5 f. M# 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) {1 R ]5 ?) J& A( d/ X

" `0 D* c2 J8 E, y& J' J) b/ X" e

+ F5 z. K( w# r2 J$ Q     case 'upload': ! o' p1 f, Q7 n2 b9 ^, I, |

+ f0 m2 e) t9 o! n) p e

y$ k. `3 ]# t$ [! e         if(!$_FILES['file']['size']) { 8 }) a$ n; K0 \/ i, b

% J8 n! `* a; b& R8 e g& o9 S

- n5 G- c% @* J8 R" [* T% L( U             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);4 l( W- n8 b4 F# Y! a9 v. P8 j# q9 v( P

/ i7 k+ \, D$ t, f: Z8 o7 Q$ Z7 J( z

+ `7 L# z. z" f; o4 C4 G" s             exit('{"error":1,"message":"Error FILE"}');% b1 i B+ ?7 O

4 d2 @- D; i2 Q0 Q( X

# a) u, O! e1 X5 x1 V( c" A8 Q         }+ E) H+ H1 {/ C( O9 g& z ]

6 G) W0 F8 J1 G+ H& t2 a, |( \9 k

* D4 ]. G1 H" X% D( S( @6 E/ w) C2 ~7 r         require DT_ROOT.'/include/upload.class.php';2 r/ ~4 W$ E2 A5 i3 s

4 Y5 _% H' H' w

, {1 n( M/ j8 N( c; M) a; {  8 M6 ~* e8 [% k# N

' [& v+ q# ?" v

* d9 J8 B$ m# k8 Y& s         $ext = file_ext($_FILES['file']['name']); 1 w3 G0 _0 a5 {8 G8 c' E

/ b F8 n/ [; T4 c

2 d9 j4 ~: |) d( o7 |' Z& ?7 z         $name = 'avatar'.$_userid.'.'.$ext; n: N: ]" h) o" r* X

. Y8 ^* f- B: J# ^9 F7 e

8 ~% C% M' Q4 O5 P- }9 B         $file = DT_ROOT.'/file/temp/'.$name; ( {9 n$ a# [5 a

( z- l0 G! H3 H3 R! I$ t

+ j5 x, }3 Q; y) J   T$ M9 t3 V( e- j" u

: A* Q8 I: y S0 c3 | o

6 A% b$ y! T& h' v! Z: N         if(is_file($file)) file_del($file);, L/ N+ U6 @! V' ~ q$ O6 i. `7 t' s$ l

( @ |" v0 n9 V2 q0 m/ \6 i

& O- i: ]2 N, D         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');' X7 l% G% o0 f4 [! p+ c4 e

8 r6 D6 K% Y* ~

( W9 @, Q" M/ `   ! F( U, E% `" ?7 ~% h* g$ ?7 v5 ]

/ k' w5 [2 i% P0 m% @

/ M2 ~6 i; L! i; `6 y* b, v8 {         $upload->adduserid = false;7 C1 p7 u1 X' q' w0 N2 a

9 [) K$ x' A2 ^3 w. Z' \

# i, g1 W+ |& \4 ? v   ; K, n6 c; K3 S/ H0 r3 v' [0 n

2 M' E( p, C3 q& j

' l2 r, L! X& {! d         if($upload->save()) { 9 W1 i5 y3 f, n# O1 m* r

! e; Y% v$ @5 v9 `6 d

2 w$ i2 H! s. l4 F; n             ... v4 E$ E6 r$ B1 s7 R1 ~

5 h5 ?3 r2 A/ S+ |5 l- e0 p' q' ?* Z3 r

5 G5 D# Y o. ?7 ]; g. J& {         } else { ( }9 C$ s6 v d4 a. d- H) n

0 D; H, k" M8 c2 e

( O# R# P; Y+ W             ...( {% k7 ?( H' T" I# J5 ^: `0 n

) H; U. X( H8 H3 a5 y

* ?! p, j# [3 C3 J4 T. S2 H         }5 m$ S# I0 t; N6 H* C

: `3 t; a0 W3 `' c

/ G: P6 e# K4 I     break;+ t& g( j3 K7 S3 j$ }, a

% [& U% w3 u' v& L+ V2 X0 G1 l

4 ^! R3 R6 @( r) m3 K3 S/ { 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 % E9 G1 z( f4 }6 _1 c8 S

4 J9 F% S0 s% _/ l

' T- d# o9 q/ ], p4 j* x upload对象构造函数如下,include/upload.class.php:25: . X |7 b. d* a1 _' n% L

X# S% [% q9 X+ B% O9 i N

7 q ]# R4 o( {+ U+ \& t' I+ v <?phpclass upload {4 e, @% Y5 P4 x; c

) ^) t2 |1 S- T" s- L+ \

& w: C% R( Z' x3 ~8 w4 F( ]4 t4 {7 P     function __construct($_file, $savepath, $savename = '', $fileformat = '') { " ~ k( z: D+ w

+ C3 x1 ~2 A+ D8 {. j

% @6 F* f. y( E+ n0 X* ]         global $DT, $_userid; M6 n( k2 F0 R* X4 G6 l5 J

`5 Z( K$ }" U- j5 i

5 Z1 r) Q+ }1 m         foreach($_file as $file) {* T2 D$ R, B4 [% M0 E+ | z

4 |& A/ j9 g5 Q8 W

3 h7 U0 z H% e) }6 {             $this->file = $file['tmp_name']; @! |0 L/ s" J9 k, v/ j3 N

. f$ F( Z5 Z3 D7 [+ |

" K% [$ e v0 m. q             $this->file_name = $file['name'];% C9 W7 i9 o9 _' e

& F! S* v/ v" q8 q0 D' _

& e0 p& a Q% m* Q             $this->file_size = $file['size']; : Q$ x3 j) M( f9 C& k

7 V" e3 f: h4 b! J3 x7 W/ W8 a

: T* Y# W' o4 V8 j; N/ M$ g6 F             $this->file_type = $file['type'];2 C6 k, i F, k F6 j- `

- E; n8 R* w* v7 e% g# \' |" g+ y! p

5 c) i# | x- l8 G. E+ R             $this->file_error = $file['error']; 9 ?( P0 S6 n0 g- t% \, O

- z- J; ]' S1 y; C+ G2 `

) _! Q5 P& O! c   $ ~6 W' ?6 O/ t( D/ R+ a) @. [

8 l& m$ k" K" U* a, j/ x' r/ N

# k: _: b) w0 T L         }0 N/ K5 ]; U+ W

+ u7 \/ s @0 ]

& F6 }; \4 a6 N& s# j- v2 ]* O3 Z         $this->userid = $_userid;2 h0 r4 v# N4 K) V& j c

2 l3 l2 T C. I; E5 U

6 a W/ W9 v8 h2 V         $this->ext = file_ext($this->file_name); * I G0 V) b1 d- l4 i7 _

# V3 A$ |2 p _/ `( A7 t) e# h! U

1 N( \5 B a9 B         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];) Y* m1 }$ R+ q( c" N' z% R) j: o

2 E4 t% d# N- ^' t8 G9 O. e1 I4 B

' L; x9 J! _3 U: Y( p1 e3 b         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; $ S/ c4 X2 f. a1 U: L/ e% }3 z) G

! o' B1 F# X! Q7 t _

5 \6 r$ b' _7 j/ d, Z6 g/ [) ^         $this->savepath = $savepath; 7 `' V: f/ [0 d: H' \/ G

. c+ \; f+ m4 C

8 _+ P) `' o5 x; c4 G/ T         $this->savename = $savename; + d3 I3 z, I2 K( s

- o4 i) k+ o2 \0 V

4 d* {6 L1 `; B     }}& I8 V4 H+ x, U

1 ]4 r7 ?) d' b

7 c; Q1 J6 I' j0 m" {7 E( h$ f$ r 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 7 l) ~% b* f( N" G; r( } j" {

5 \$ y/ T- W* o4 s) |4 H

; [7 v5 N% O8 x& ^; k5 s& | 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 , O0 W2 h* ^$ |; k

# |% U( _1 c3 F

6 Q' O6 Q$ ]+ W8 k8 k $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 1 k% X+ d0 G) h4 K D

+ Z. Z$ f1 f! ~5 n. ?

9 D' p) F1 {: c+ P0 h& n/ U0 I( ? 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: # m3 F7 e; f/ [; R0 D2 B* {

& ^ y, e5 c4 `/ w- E2 r8 {

# d; }. G/ a: i9 t: [   " K! a- S! }5 s) U- c

& o2 B+ Z/ O* m) B2 l9 A. L

7 z7 Z/ ]& q! d7 z% N, p5 b# o 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:' p% X3 F' t5 [, u! W2 k

" B; X7 L7 U8 \# }

1 K/ l* e7 X2 r- W8 M <?phpclass upload { ; O- l& ?- D9 w

8 B: l4 h; i, ~* ~5 y

% B1 O2 Z) \# x. {3 P     function save() {& Y2 P+ r% Z$ u, P7 a: Z# Z

6 n+ Q f+ M ?; {$ M, r

) Q. p7 u8 J$ _9 ]' B4 j0 [1 U         include load('include.lang'); `& `* C. H- t

6 b2 N- L5 g# ]7 |5 @. ]

+ d. y/ u8 y- b% r3 M! v         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); ) E$ t; M/ n5 z: q

( \( W6 [2 ^6 t* d# t

5 e. l( k* @) h4 u* N2 N- x2 |   ! X" j l* i. L

* M& F. K) g" l

7 Y+ r( L6 `8 ~2 ^+ b( |8 y, X         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); $ v9 F9 \7 o4 }# w0 o% {+ K

& o. t- {. P! S6 T w

6 d+ S1 Q7 u" u8 ]/ h( ]  8 Y. @- _6 g. W/ K- _* ?- V

, k6 s9 ]9 a3 B" ` J7 q# u- d

2 S5 ]& m4 ?+ x) K2 ?/ H7 N# u         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);" b9 s$ C5 @ Y, \: x( c( W- j' x

/ ^4 S {7 l) _9 \ K( K

& q* f5 v# k+ ~" \* l   + J+ M3 T& w- s5 `6 s7 l7 L L

7 j; @8 W4 M8 O; W9 X

: P7 k% g0 i' E) X5 D, X         $this->set_savepath($this->savepath);- f7 z& }: S, w% B) ~' L$ ~" x

5 ?+ A- |1 q' s6 o

~2 i( L# i0 P# n# u6 b" b; Y         $this->set_savename($this->savename);1 G8 p( W" L0 Q) c$ N0 w

4 s4 o7 g1 p0 o

4 F8 V' o- h, I3 t4 C1 @  - @; f3 K2 G! T( D2 }# K7 O& ~' C" G) a) y

. {0 p! F, l4 { {/ g2 V. S

9 M2 h2 v3 g: t" A& W8 F         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);3 K; M E8 M" V* I3 r) q2 J" Y4 o

+ p: n; r2 u5 b* V8 H% Y

F' w' j+ h0 K& G3 P: `* d+ Q         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); 3 Q' a/ e" ?- L: N3 y

: s' M4 F# K2 U9 w0 S* s

4 E, R& j! u" g' n7 Q6 @1 N( }5 h' u         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); 6 R! n4 N3 [8 [$ {

! k- V; S8 ^, @( E+ P2 V. b2 C

6 J2 R8 [, }+ p: J* P. e   - ~' F% B+ f7 o- ^3 t3 l5 `

' D- P8 K0 d+ p7 M5 l

' m( I9 `( H% d( [         $this->image = $this->is_image(); 6 E% F5 a9 i- {+ k. k" B

5 g1 x9 C/ k o2 n( N9 Z

6 f- v- D+ g7 @- Y: i         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); 7 U& I0 I9 b9 J3 Q" A

" T, s& f- D% w1 Z

! @6 r3 B) s( E, K9 r6 U( T         return true;. T" W Y3 t7 j" x7 e# N8 Y- `$ ~

' S% Z R; w. b' Z" e6 l

* j. b/ _# R* x     }}1 L( L6 a n) Z& G

0 e$ Q- i9 h1 x

+ d H' f' C1 A1 ~+ P1 T9 U, { 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: 3 J+ w3 o5 y9 C& k/ G" o

* e7 }" m9 p3 Q) J

/ u6 O) u' I6 f& R( G, E! o) x <?php. e* D$ P# Z7 r8 r% i7 p% b+ G

# @0 \# n- m6 ~- C' o

: X1 }% L$ A/ [+ Q, h' B5 F9 q     function is_allow() {2 \, a+ E( k9 l+ e4 R3 [$ _

9 p: T8 v3 _: p* X" O7 L

- _+ {2 H/ D! P( V" i! M5 k, i         if(!$this->fileformat) return false;# f! w( s- z$ {% G; J# z

( y; |$ F! I& _* i

9 g" i- v$ @" a         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; - T$ N; y& L+ @, |5 p8 g% d4 W

% w( w6 D5 Z' o6 L) i$ q0 f

5 v# r9 L# f9 V1 h! X d1 S; t4 h         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;: K I2 v5 ?5 K+ Y, e% ~

- E9 p5 n: c+ R( k ?) O% w: s

- K, K& P' J( }$ M         return true;2 T( Y4 I- h) k5 E

2 u( A& I3 t. [' D$ P

, C8 d3 U! |" n5 |     }) \$ [' G* n* f" M* i

7 }5 ?# K% g! K6 R9 w+ Q0 J

4 E/ |6 S u& x6 {0 H) U 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 : _# K! j0 Y- `2 m

) {6 s9 ?; O% _3 m- r, K

" [( ?$ |! L8 u9 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文件。 3 L' Q8 u+ N! ?" j/ c

" w" ~% O* [3 |5 J6 A" g- \7 M3 |

- [) g9 B: C. o7 n; _ 漏洞利用, Q: y- H6 p8 o& n; N0 Q1 F

6 i* m1 R- ?; {- A

( g2 \7 c) K; M) x2 d 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。" f- s' _0 o: y1 E4 l: m5 Z

, B% Y& P9 p. j4 f, {; L3 v

, G, }0 G2 \+ q9 B   [# a7 V1 q9 b- j& T3 v/ v

% [* b0 A; L( J* ?/ v

( o4 Z2 d& `# b' C 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid / l( M2 J2 h' l: l% z

3 f# G$ r1 y2 a$ R/ g2 f, n

8 g/ n; C! K( P 不过实际利用上会有一定的限制。# Q2 E v% z, q" `0 @/ H0 |; L

' C; ~+ L/ u2 I: \' o8 I

6 W$ O: M6 W3 y6 J% a 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 ! b& C/ Y& D, i' ]

1 u( w. v8 b, `5 m# P

[/ S* d' I3 D5 O% B   ( }% B* x3 D8 f$ l+ L

k; R6 A; x& A$ c" S, n

$ u7 h' N, P' T- F1 N 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: 7 S' C9 j, R3 u4 B5 G0 k

7 l# b. @# ^& M+ S& k0 e$ s- [

l2 | ~/ p: u5 V# Y1 s 省略...$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]);省略... 7 d8 q* O# L& I/ L5 [

! i0 Q9 | D5 x$ z( u6 _# ?

! f) q' z( m G 因此要利用成功就需要条件竞争了。4 j( a2 M! h7 O' L }

. Z/ Q; T4 b" u

1 d* {, O6 i' S 补丁分析; E4 z% A/ l7 c* x0 r0 d; i5 [

8 ]+ Y( q, R' q4 A+ Y! h( c4 N

2 y( J8 P& z0 c  9 }: i7 p9 X. ?4 W

& @! C& e! }4 C+ c

0 r5 H, ]$ E* Z1 h- m9 F7 |6 a 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:7 r5 }4 \1 w1 u& D; X" p+ S

* n4 C: n& i5 V

; u. j# V# V' [7 H9 A function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}" W: g* i' k$ \9 S6 S0 t

7 b; Z- `/ F! K- @* J5 S- q

2 T3 a- C7 q, H  0 _, v) n7 R' i7 M: A0 C% {+ u3 M( @

( C" J3 U2 f, v x. m2 ^

0 h+ }' @6 A+ t: X0 P% E- L9 E 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。! O8 G- {7 Y5 K( a* h8 ^8 v

( `0 E8 }; L. Z) O, @4 C5 d

6 k* Q$ m1 ^5 ? J, n1 {" O3 I 在is_allow()中增加对$this->savename的二次检查。* \+ N: O1 [- N4 W

* P2 i4 j( g$ a6 X5 J

. d' r0 P8 D0 c 最后 3 r* Y8 C+ \$ [7 g5 l

' j. ^5 r+ t8 Z

n( [/ w1 u j( |# ?' X+ L 嘛,祝各位大师傅中秋快乐! 9 ], @1 j9 C4 B

. m: J0 F, U. c4 z/ r# i& N

' F# l; g& J/ F ~) ~   % n, A" _3 q* J

; c) q. b$ m3 ]2 j- h5 v& o+ ?4 G
回复

使用道具 举报

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

本版积分规则

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