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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
6 \+ U' e5 [/ V& {( ]8 L4 v6 y! b5 m* [

& R) C8 k4 {" `% c+ f [0 H

, G- ^ k4 w' s% \1 g3 }

8 E! ]; s$ F8 k, Q& N 前言 2 H4 N1 r) H) A m

) G: \/ c3 l/ w! A9 } {

6 z" M. Y0 t" m 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 . ?! g+ i3 Z7 @) I Q/ f

- w, K+ T* h" x- Y% v8 Y+ t( Q' [

( N6 i) C; _0 v# v3 J) A   % e! F! R- z" i3 S

4 ^ O* b) Q8 U' z

. ]7 m5 O7 Z( d* F# D/ e 漏洞分析 ! y2 i! T4 F3 e! v4 c

; W* ~2 U) T' v* m

3 w. q q$ |+ j( n3 C; o 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: ' o1 O, y d# i3 }& ^

' Y* Y7 |5 ^2 D. `8 q A

6 f0 L+ R: s6 l1 d4 o% j3 A  9 m3 [! }' S9 k, u/ Y

7 U1 X# j, `) M- [( T% a- G6 G

( {" ?! ]/ f* b/ c 对应着avatar.inc.php代码如下:0 l; o0 P J+ L- M4 Y/ K

8 o' A! h6 c: ]

6 j; h$ m, v: N <?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) { + j3 d& R: l+ r6 c

/ B7 a5 r3 |2 d: c% d1 t5 x

& P: p; \3 r* F- z     case 'upload': / o; i5 T3 S7 A7 {, R

. ]. j& j. J; }# E5 c$ T8 o ^

: S2 _; k$ \, w% B         if(!$_FILES['file']['size']) { Y$ \; f# H& O$ {

1 |4 ~, K, s6 R0 T

# N+ f; K ?& _             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);: q7 Y: X7 Y% o& {4 E E6 k" s

0 ~$ m& a. F$ i+ k6 b

' W/ W& q8 p8 E) @             exit('{"error":1,"message":"Error FILE"}');. c: \4 e. e& c; C( C& }' ^' F

' x0 ` L+ W2 f& s5 F. ~1 `

H, h4 m5 l% o$ M; f: F, T0 s& P         }/ B; u) E& U6 ]& d5 U* G

( H3 \* ~' p& E0 Q. E* i" l

+ J4 W2 q" e( b% d! ?, |* i         require DT_ROOT.'/include/upload.class.php'; / ?/ x2 N- R" K2 p' s- V

- ?3 m$ n) y& ?4 N% x

: s4 F9 w0 I, G" S, A   ; f/ T6 n5 J3 W8 C. F; O+ @9 P# m5 K

( s, w4 d# s! l5 K/ R8 c& e

$ b4 ~4 R- a3 [ H/ Q; }         $ext = file_ext($_FILES['file']['name']);( \! z E9 u+ z: P2 x; U8 p) c

6 a" A V9 | A; q

# c* J5 D) l: X! [         $name = 'avatar'.$_userid.'.'.$ext; ! z9 y4 ^) Z3 V/ i. {4 F

& N0 |: E; b1 n' K

/ y( W5 q6 s5 h5 i& o' e/ u; ~: @         $file = DT_ROOT.'/file/temp/'.$name;1 {" L3 q$ o X8 [+ p4 Y8 u1 L

' _8 g- T6 N6 d0 V* k

- b4 _9 @; c1 {( ^; F   % K7 J x/ `- Y! T/ U/ N* e! f p

- v' V1 v6 E- D! I( M- N

: O2 m* k) X/ q4 n! g9 ?) ?         if(is_file($file)) file_del($file);, V2 z: B! \% \; f; y# G

0 ?6 f; X8 d- c7 t/ a! j4 ?# J

% m" T# m$ v7 [2 ^7 N2 Q# p         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');3 w+ ]2 \( ]! {+ U; y/ D5 s# C

& B- M) j7 g# p" V* Z; \

0 ^: k9 S$ H) L# a# g7 m  - o9 o# [" B! D3 q, A0 |

. \1 Y% ^( I* U% K

1 l* d0 `' i. u         $upload->adduserid = false;, h( q5 l; Z2 y! ~, p$ ~: H0 @( \

& Y3 j! ]8 p2 e# W) q. H/ j3 m& _# V

- h) F" `) ]$ Z3 ]0 s( v8 ?5 \  - l" m$ I, z& O+ l1 y) T1 Y( o

9 ^! l; Q* F9 B. P3 H

$ B1 i- @3 \% M% y5 C& X         if($upload->save()) { ' k& o' |! \+ l: Y

8 {5 a& C P3 l9 I/ t; s1 I+ ]

" x7 U8 s( c, u. g0 C             ...& v# t/ J/ Q0 O# I

4 n8 s0 B' E' V( B

* {3 C, n- n( _( x         } else { ; h% `+ h* t& o) u( p( f4 t- t

. v& b* U4 q) _! b7 Y, f; W

2 T2 d, t k) h* v             ...5 t; e. D( R ]! b# z! P! l

5 D* s7 h- F9 x A- |3 r+ q; @

) z1 B: s! C, z$ B         }% q3 {0 `3 N% I3 ? N

3 E, u4 F- g6 o% i' ]

, X' @% B& ^. p3 {7 v     break;$ R. s5 ~% K+ T

' H, C1 q( |4 `. ` t2 s

7 L7 j J2 w" E" M3 I3 R 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 7 o9 h& M/ u" H j' K

: L+ e' N- f0 N7 q

5 s, J0 Z$ v* f9 X, w upload对象构造函数如下,include/upload.class.php:25:+ Z; z6 e: c& X' f$ F

- Z* X' L8 t% I: W# X

8 M( m: {6 t0 W* p. b( @3 ^ <?phpclass upload { . U1 D( \: i& v5 o

( o* s8 c0 i8 P$ [! D) U# G4 W

5 F/ D6 I# f' N% r6 S( q) X     function __construct($_file, $savepath, $savename = '', $fileformat = '') { 3 O9 z; R1 M3 g* _3 V, W7 F8 g

* e+ ?, ^! V; p2 n/ T

) V8 v* q9 {* L3 b+ l3 [         global $DT, $_userid; ; V$ k' O8 c$ \' D

% Z) c3 `- y: C* ~

( I y' _) J9 J) w+ C: R {         foreach($_file as $file) {- b0 {$ [5 Q' I( v

. l2 \ R$ |* {7 ~8 F

- B4 P% F6 `5 ]5 H* O5 T5 F6 p             $this->file = $file['tmp_name']; + v1 U) d8 C+ Q! `9 h& p

9 b. Z& O, }: g. _& |8 e

/ V# H) N0 o5 h& Z             $this->file_name = $file['name'];) |5 N; [1 Q0 R

" z: u' U. ~+ u0 i

& v4 p7 b0 N0 ^9 g! l6 Q             $this->file_size = $file['size']; : |- _' Z- s6 ?+ Q% Q5 w4 s# r. W/ g7 ?

! f7 q) h; {0 Z5 A& r2 E& [ L

* N+ e$ n5 m+ }( ~5 T             $this->file_type = $file['type']; * l0 w' [/ b' H. S, ]

) n+ C9 \0 ]. A" t

% z# [9 \! P9 G$ s) F; l, ~, Y             $this->file_error = $file['error'];+ z# I9 f" @/ m, m

2 d! s( l7 ^+ g) n7 E

- b. Q% l3 X* M6 D+ l( u9 ]  + n$ d5 ~9 ?; G0 Y" K( w8 E

9 Y, h5 v& J$ i; f7 q; q, p

. U& o! f8 I8 h. B$ S: I0 [         } 8 [$ Y( \! r/ R

' @0 e4 l! J5 M6 b8 D

6 s" k$ t4 q. q( i3 _ \3 P         $this->userid = $_userid;4 V& Q- o$ Z: c% i

7 Z( b, T. H' G1 l4 t, n

: k* S# G& w- D: n% o+ H8 i         $this->ext = file_ext($this->file_name);# G9 x1 o4 ?% u

% ^6 r; G2 S$ J$ V" h9 n8 N1 D

2 G2 b8 C2 S# U8 q: u0 O, \* s         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; 5 Z0 g- }8 l# C2 T3 z W" D7 [

% }8 W( E6 W+ D) Y8 A2 @/ `

- P' y8 O9 L* Z         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; ' e& P$ b2 p% D4 I

8 ~6 C0 M0 s8 H1 ^

0 j/ f' F, N7 d# T         $this->savepath = $savepath; 8 b3 S1 z# U+ T, P$ k

3 c/ }. _% {! \! m

. w+ D$ o. j& |         $this->savename = $savename; u+ e; C( \$ S/ O6 p+ F

, P% b- Y! z; G9 |' y

' `$ N/ _+ B, ^3 g     }}0 C- q2 t- I9 h& L" c) ~

- s4 l! [* ]1 }( [. U( V- z

8 @% E$ `1 u* _& X, h' x' H 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。, l2 t, Y: g1 P* T4 V3 a9 B

4 R& p* {* J% o

# @% ]: N( [1 X3 K& L. e2 t 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 + Q$ x. d( Y- ^3 Q1 F

2 R9 d3 ~7 @* O0 J8 |3 t9 |

N5 L9 y& v2 T% u. n2 E& _ $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 h0 a+ y- O3 C3 @1 A2 r3 X- p

' @$ ^! Q' L8 }9 a

1 U" j. H" g" U 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: " @4 C+ a! Z8 r& Q

6 \% _) D* _; A$ B, u2 f5 `

: p% |% q% p! G+ @5 A   / w }& l& R5 I# o, l: C$ h/ J' q

5 }/ G% i9 |% ?

: \3 x" b. o# y2 n3 A: K% P$ H f 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:+ b& q; L% {" W5 K

% }7 b& F; `- }. O) {( r6 t6 {$ F

4 F: f. @* B) \4 I4 o( J <?phpclass upload {. U3 Z; ?& l! v# k- l: J+ u& h( T

6 n% @7 z& i; T. v: X% x

. z; @4 N: I/ J5 r0 R/ w% Z     function save() { a8 |7 o" V: Y# C& W/ ^- J+ T: c

- P2 `3 X- Q" T8 @

- z# w& T& m2 g9 Y4 [7 w3 J         include load('include.lang');9 x& u2 x+ \% k/ {( C

" D9 G: d% z9 o! Q

D1 O1 b% s I4 P+ i- b! U         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');) X/ R5 G2 }2 A9 N# N8 S

q" h3 X5 y5 a9 B% ~6 B3 p* v

0 L% N* y8 f% z \, l- D   `; P0 E9 g6 }3 j7 q' l/ M

: r% t+ m M5 A/ R% R

u: Q% T+ k7 a5 X         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); E- k( g( J! A

0 }' x7 j& L. V, \

% P' ^9 D6 _. q/ L1 a   / Q! Q% d& R# a5 V; E# t

8 k \& T* Z# P0 c4 V

. |+ P0 N) r) J: F# f0 X6 |2 L' D         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); * v1 W7 F# Y9 ?9 I+ ~

3 }: s7 r- P; w- d' I7 o0 @2 Z! _

" ~0 }& Y6 Z" m* p* y u6 E8 h* X   " d/ Q- B, D. T' [7 `/ E G; [

3 o0 Z+ n% w, k

% r/ v& c0 `: u$ ~         $this->set_savepath($this->savepath); % h+ W& q7 T) }" ?2 d' z

% A6 ?/ @# F. [6 Z% g6 C

. z0 w- `: q/ Y* T0 t& o+ O7 @# b         $this->set_savename($this->savename);: _( h$ x/ y8 {' r

0 V5 _* S W m7 x$ t8 M, e1 g

5 p5 J8 _: b9 [( r( h p   * L0 e9 O( \; S: e( V

$ n- m7 T# w% u# d( g0 k

Z% ~' W+ R" Y% ]         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); 4 A- v$ m/ k( o8 I

2 Y% a* I0 x# ^7 g

# n3 t( K9 E* v         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); ! A8 ?- V9 f4 E4 D; e

" V& k: Z6 ?& N N5 n0 I5 F: X

$ T% L9 ~( D* M, b( g         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);2 X: \# {% m' H* j0 F5 {6 T

4 Z' [, \! K7 H& R i( I6 \

! |! M" |- y7 E% }7 D7 @2 m  7 \7 y5 v6 Z, M1 I

) K: _3 d0 C6 P4 _. G1 y& D# l

% t! B% K g5 K6 g         $this->image = $this->is_image();5 ~5 _' v' X" S# l& M

0 X& I$ B4 T/ D& p1 J

3 ~0 H- |+ ^' ~0 P4 S         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);6 F" U7 ^* E9 `' H

7 D% a+ B; i+ N# l

! B2 y- H# E$ v' V4 e \( W8 G7 O6 }         return true;* Q& P8 H7 \7 y- ~" d/ r

7 G+ L2 c+ Z @$ h1 X! c% q/ Y: S

* c! \ x8 T T0 Q$ f% O     }}6 ?7 h u) g0 S: s" Q$ w

" W& I1 R, h. B" P9 \

3 L; k6 U1 V" p# P7 F 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:" L: _. `: M& v' L8 j( N: I' k) P

6 ?% ? i; B8 f6 J; i

: H: x4 G2 s1 B) m+ I% o3 {# }& m <?php 0 ~6 K; o! M+ I! M4 e

5 y0 B) n- A2 j* `# o

e) }( D- ^7 V. R7 R1 L4 D     function is_allow() { 6 b9 A" v! J$ @( K2 D

/ O5 E8 n n# Y; p

: A! D0 z+ P4 C0 J; ~4 j A         if(!$this->fileformat) return false; 1 ^6 @3 ~3 Q4 V2 `7 o

) M8 b% @0 t1 B2 A

s* `3 J1 C6 U% ~         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;. L4 M( {7 E7 v5 Q0 u* X5 j

$ L/ v* e8 ?3 f0 k1 ?

% j% l4 s9 P4 I" i1 i& b' Z/ U         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; 9 b. ^3 Y. |; [. _8 R) I' a6 v$ g

6 d! }/ J/ W) B$ K3 C9 z9 `7 a n

" n# w# ?/ |! i0 n$ b* Z         return true;9 G! c2 F A3 d/ v5 S. A2 O* X

( d! p7 m* Q! N) q& W( \ I

! d0 q1 P( Y6 _4 i; O$ a0 o/ j     }9 Y( _' p$ A/ z

1 d# U c; q! s' l

. H% [: @2 D( B+ D- r# x. g- x 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。2 D+ k5 ?! Y% r7 H) [5 ]

( n7 h7 {; n: ]$ z E5 n

4 X3 v7 ~7 y7 g% W! X! n% B 接着会进行真正的保存。通过$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 ^* I; M: V5 w) z

2 F+ Z# _* p- Q" j

& C: ^. h3 Z9 G 漏洞利用! c; u+ \/ B0 g6 |

; l% _* [7 E' X1 @7 K. Q

5 O: ]( `: z4 L2 B! G, i- ? i# ^3 ^ 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。! m2 Z: y e. N, h; }, e

7 y, v ]2 k# {" I4 E, v/ E7 r3 c) Q

, K' h5 a9 X' e   ! ~: Y) `- \: n, }/ b& k

' w1 O0 |3 p; k

/ m& Z; t$ U f 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid : t5 c# N' m/ n; B5 g" K

$ Y, z" V, d: R0 `

! I% a" q! R2 K. u; S 不过实际利用上会有一定的限制。! d# U+ b/ a. M$ X

+ k% H4 L; e) l" u/ V$ @ g, }8 h) Q

: X) p8 ~1 n2 r1 J+ h 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 $ u/ w, a; u% F

8 I- P8 C0 w2 u, s& ~% Y9 J5 \

( o' x9 q! F5 E! O: i9 ^$ ^   / s. ?- E7 Y$ Q$ O$ x8 h9 k

: R# b; ?. s: O

7 V( Q2 R4 L/ ]- _, ~+ K, O 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: , ]- L$ \" U. I3 T, f

3 k; C5 t8 [3 i/ }

& ~+ h% Q8 H+ @: K$ i8 v3 o 省略...$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]);省略...4 X7 W R& s8 }$ b( M9 v h

8 M9 b# `6 y) n( \+ ~9 l& b/ Q

& a7 T* {$ M! \ 因此要利用成功就需要条件竞争了。 , h) l5 W# T- m4 Q q- d

3 M# h6 O, C4 ] @, B9 \; B n

( G3 Q! C9 N0 a6 f3 [) I9 K4 b9 n% B 补丁分析# o* r8 E. {" Y: T4 @/ S. M) w

% x3 R r! F6 F# A' U

1 h9 i4 L' c& n t7 k0 Q  * t1 b7 M) q* r+ v# _- n

+ K4 K' M" ~$ Q. t; i

& R, ?" f( y' [ 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: 3 |5 F) b1 @, Y; | R

8 A2 p' T& v. q+ e

3 _. d- W, D) x9 ^. F function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}$ K8 B& e! p7 D* i: O

/ c3 Q% e- ?* ^6 k4 u7 ?2 i

. |; s! d$ h- v: A, M9 C6 G! k# S   8 G2 h: X# f; Z5 [* M+ P* X$ x, }

1 ^- I( [2 @, J% _ l6 j0 U

( P6 a7 F+ R! Q' b1 N, ^/ j8 ] 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。' d7 H$ F7 U. |2 C

) {) q2 G2 l- G. e; {# ^4 j6 {+ P# v

. [9 Q$ H9 Y% e0 n 在is_allow()中增加对$this->savename的二次检查。, Z! b6 F0 X) v1 t' }) {

7 L# s3 p+ I% I. u* M: {9 j

2 ^3 \+ F- ?1 V- h: m% a- ?& y 最后 $ Q6 r* \ k3 Q; O6 s( n. R

0 E4 J, z. C$ v/ L5 V( V( j6 u

$ e2 m, l- s0 O# K4 R9 G$ b 嘛,祝各位大师傅中秋快乐! f& I7 b0 N* }/ V: Q- h$ A

! G, L. i8 \5 N

- Y; Z% }7 e; L- X: n# z+ A  4 s5 t9 R+ b% x5 r- [+ s

* K$ p1 z& F* b" |' i+ S" `
回复

使用道具 举报

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

本版积分规则

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