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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
! n2 `- t/ J" K! E0 y. k6 e

9 }" V/ h/ l8 m0 X% @+ S

" Q! O6 ~7 M ~

: l9 t N2 Y* g9 O: T 前言 ! Q) y3 L, v4 f5 O# J

- }- u" ^/ K) U. s* L

. @4 l, d3 n* R# C& ]8 N A 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。, m0 ^ {4 H$ }; u

0 F9 G" ^% Q7 } Z2 T

+ f6 ]- X* g+ T0 R. e1 V( ?   3 m$ a) t8 S. }6 V, m4 }5 m( |: _

3 `8 Q9 ]. K" d% A! S5 c

4 }7 @; g; h7 N' o 漏洞分析 7 ~% r6 `8 u9 V7 P

1 h) k6 Z5 }4 Z0 U0 s9 n

, e! Q& j" I+ H: a4 k 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: & ?% P6 {( R4 r; ^0 e7 W. ?( b+ Y

0 N+ w, I' i* _* u+ J

2 b& _% p% G% ^, v, r3 [& \) k  $ ^0 u# ?; [. [: j

4 u# [3 j% S0 Z0 T4 ?) e

# P# |( g8 g, m' G7 t4 L& @ 对应着avatar.inc.php代码如下:# U" R. Y* h) K: V3 [

) g9 ]6 [& n/ c e1 z5 {

( o% R ^, x; J$ s2 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) { 3 ?1 ~. e$ p6 C

/ z: u: }5 o$ |7 J8 e" q

9 A: y D0 ], e     case 'upload':% \% i# Y( o/ F

& n' v5 g3 L$ C

! z: T' ?: k Y& k; J! D- O         if(!$_FILES['file']['size']) {; g* k* o4 ~1 f7 H8 M

' q% S) @: W3 W) f& G

( z8 j! A C, j0 N4 Q             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); % W2 K2 K. \! [# O- f5 a9 P- H5 h3 u1 f

8 x- B$ f5 l) d9 E2 S

" q+ a5 A* @: a% I             exit('{"error":1,"message":"Error FILE"}');$ L6 Z9 y1 ]5 r0 j9 {! `( U1 {

" X& i6 {: ]; c1 d" @0 f

* e( {, W# r. e$ \6 X         } ) x- z* I% U- ^& {% c

% p0 s* X, C% p6 R) t* X% n5 e

* i# I8 m+ B, B7 H         require DT_ROOT.'/include/upload.class.php';% [' C: ^+ d7 k- [& @( S9 |7 F

# s0 i5 h, {0 H u) ^4 }

c3 Y& V0 h2 f9 E6 T# L   % T7 V# S2 I. D% |- s9 w, \9 j/ U

3 t r, J3 E; g* ]5 \

" {) B) _: T8 \0 g2 r4 A" G6 q         $ext = file_ext($_FILES['file']['name']); 6 A4 ?$ ~' L6 m9 W& D8 ~, ~

a) _+ _, B2 T Q2 c

* d$ I' `& _' {         $name = 'avatar'.$_userid.'.'.$ext;+ d! C Q6 \7 w5 ^. q0 C) D e

( |" h% T. I! ]

! l/ L" E( K7 a- B+ g, w/ ]1 i9 q         $file = DT_ROOT.'/file/temp/'.$name; 8 @. W) M; y. H+ U* M4 f$ G8 l( d

' y* H( w- n9 q- J z: ]

7 I% \) {) A# ^2 l  $ }5 Y' O% F( l5 ?

9 P) }5 q8 A E: F6 u

# Z: T8 x! S5 |; e" j- Y         if(is_file($file)) file_del($file);" V2 @. M# g2 T4 |7 {& V

+ j; n8 l4 u! C

, w0 l2 X0 ?+ b% F8 D5 j# K9 b         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); 8 t8 b% e0 W" R- d" F

& ^$ \, S1 a4 ]- f# p w+ s, G6 }- Q9 L$ B

7 h4 @' o c9 q5 }1 k0 r   ' u& F$ F+ m8 C- n0 t( f

% s1 i) _" Z. Z9 w4 B8 ~

( f0 a9 b$ z: U Y# P2 Z& } S         $upload->adduserid = false; * V1 t" I: t- r+ ~* _

3 n# t& b1 i, K: A

$ n9 ?- z& ]! ?8 O   ! R2 V/ p# ]1 Y8 z* I. E& w

) W K d" [' c U8 a4 g

: e; J: W8 {. ?- R# {' C/ g+ l j2 L         if($upload->save()) {' `8 J7 m7 q& |: R# ~( A

1 y& h0 M2 }5 z& {

& u$ L P! Z4 |& ~& J, \& Q             ... . _2 K4 Z0 d L R

( `! w/ _+ `' f. ]9 T

: M- g4 G8 D: P) o" [7 v: r         } else {' {3 J" L' M p! a3 R0 X$ G2 {$ |

( T8 S, I% U' A

" S8 A5 u" f7 M8 p) l; U1 r3 ?             ...$ P$ c8 m& H7 s L3 B2 _4 _

7 t7 K+ D }0 {/ C5 }' L+ A

G+ j! U& R( X         }/ o# l) o2 f% H6 a" L( b

2 q7 e' q2 T$ _+ j" U% B' |

1 g5 X- w& h! i     break; # C8 f- |9 x# Q) f6 p7 |

, c; ^) Q7 o' N) b3 [: [: g& T' G

3 R" Z5 o% E7 A A- _# r 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。! }7 c/ I4 t5 a( _; I" d. E

5 q" w4 K! X: F6 N+ H ^

7 ?- U' _, {4 g! s1 ]" F1 B upload对象构造函数如下,include/upload.class.php:25: , y# `6 [) i& Z4 q

* q6 U! ` _8 o5 s

! F3 D8 n" v+ |2 I& Z: Q0 y' U <?phpclass upload {4 c8 M' T9 l$ E; E

; n* u0 L' S0 ]) w

7 M( m |1 K, U" M8 z: r. h     function __construct($_file, $savepath, $savename = '', $fileformat = '') { 4 n: L X% h+ d: E

) [% Z9 `' ?+ s0 O i

# N( S( @+ z! W0 O# b+ k         global $DT, $_userid; / }# C* T+ t' X" w2 s

7 `9 A9 N/ s: [: p" h' a

1 ]; s3 U7 M0 U$ ~: N6 t         foreach($_file as $file) { 6 c. N% V; l5 [# ~- \/ p

: S; D2 z5 ~+ A" e: h# e2 X

8 r0 W, E/ B3 G0 N* U, e" F             $this->file = $file['tmp_name'];) o; @4 `, R" Y

) Q9 w; I7 l, S) V. t! L1 I3 C

M4 Q: O7 l5 V G2 R1 q             $this->file_name = $file['name']; ' G1 X- R5 {+ e- c

2 K7 S2 e) Z" S% m

9 b% k6 \; B1 M- b; i! H6 }             $this->file_size = $file['size']; 9 \# U2 k% \4 f9 i* c

5 F0 ]2 v- ? W$ g

8 l5 G1 m# a7 Y2 L; |             $this->file_type = $file['type'];9 d7 D% ?5 ~9 U8 H8 I! h7 F7 H

) {0 O9 U! \" M( m" S

: Q E4 B3 j+ \1 ^             $this->file_error = $file['error']; * ~. l0 M) c; x9 g* c! b

) u) R3 ~# p( O; z# X3 F$ |' a

* z3 H0 Y: C$ [) ?2 m4 z   ( { L" ]4 f* Z3 }- R h) m

3 o1 \+ ?$ \* i

+ S" w* t) V0 i) Q         }$ V y$ T7 ]8 w5 h4 F

8 n# N4 I& t( `7 w8 E+ r; j0 n

, C9 y% w/ }% z- j: ]/ ]         $this->userid = $_userid;. w2 v* d" B; h6 W0 d W5 s0 o

1 R, C8 q, E3 p+ E

. w. c! e( C. y. T- N$ ^+ ^4 }         $this->ext = file_ext($this->file_name); / ]# b' H! L4 X

& D9 [4 E" P- l9 q" a8 s% `

5 ?# p) j4 I- R, H2 z, E         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; & K: S3 X2 |( h1 V1 w; p

" p5 g; T, D ^4 _( V- E

" ~. X) i/ r& P         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;# D; z2 P! E6 n+ e, |" {

|9 x* O, r; p, n

S& m8 W: n5 ^4 _" Z         $this->savepath = $savepath;8 u `) e) n% H0 n6 g

3 O) @' K d+ Q9 M' k: @$ v

1 v! W t+ I1 a2 Q* A* {         $this->savename = $savename;/ j9 R" _4 c( o& E' @3 [

( B/ b4 U% D! A8 P/ e$ h

/ m. s+ g N; v/ f) G& d' ^1 f     }} . O" e; u: v2 Q. q1 k5 ?! F

! W+ R6 j0 z" _) ?; T

, g% o7 E' G @* h4 Y/ y 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。8 n0 D, U7 C4 y+ ~ k

1 o, T7 C# X; ]5 v8 Y' Z4 n# V

* z: Z# S6 \+ l; Z6 J) S* x 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 % D' x* @( l6 [6 e2 N3 a I/ q

3 e: f% E- ~' e! U! |

, T: D8 N# `1 \4 ^ T5 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+ R& z1 K- \# t4 J9 M6 }

" H. s$ G: t% _% y8 I

/ e4 |; }) v: [: I/ P 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: / ^2 [+ O# C' _$ u$ |# b

% ^% d5 U1 I3 [$ z4 s) k1 X

3 D2 Q$ B3 [0 e   , G5 X* s" f) G- L& J, p% A

4 Y& [ G; R _+ e: g

& ~: B$ g4 R& I$ c( D. U$ _) o 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: 4 M, `# G: Q0 ~

( n; y2 @" B; L- A9 t

0 a0 Q2 i/ h2 _6 X3 H9 Z ? <?phpclass upload { 7 k, p+ S' s6 O. B& [

9 f( O$ ^$ i6 L# ?

4 _% Q, _' {- b3 m     function save() { 9 d2 S1 C# O# z! s2 q

+ u* U+ j2 F3 i4 W

1 {8 A. \. `- P2 ]5 C* F3 P& I8 R% R         include load('include.lang');0 `4 s0 c* f& b& ?8 S, l5 g

- t* S& Q( t; g$ ^( o

$ t4 u) L$ w9 o2 G         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); % s5 N$ p7 B6 K7 ?; K

, @: I5 f; ]/ ]5 @/ ]9 h! S

F) g3 e+ Z- I$ m, J% p' Y- l  $ j. F; B" b$ |

0 F9 m% V8 \9 [: L' j$ G6 Z8 p& E# i H

; w5 e, @% t9 @0 l# H         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');2 Q$ ], N, C' v7 | a

8 \, W! m) @8 I3 }2 e2 A- V

- e# ^ Z0 K! {2 t. f   5 T+ ~5 p7 D+ G2 q- U/ S7 \7 u& u

9 f! H/ f" ]* \

' O' c9 [% W3 y: ~9 q& z# ^3 V' j         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); ' U2 w% A! b9 x6 v) [

5 a; R7 g' f# j E! D

3 X" \( \! `+ a  ! ]$ n; F* z* Z8 R. B& T, R

, w- T; j5 c( t" B; S! O1 H

0 ]3 V# s9 W6 G0 b# `3 x" H         $this->set_savepath($this->savepath);( O7 t/ ]" h, D) s4 Y

# s# }5 M" a# T, _, J

$ F2 M& t3 U, z) x4 z         $this->set_savename($this->savename);; G2 e7 R- Z0 p+ {0 d

8 v% m2 x- \6 q& O% N2 s: g

4 E% n6 l4 a* }- o$ ?* L1 x  . W+ q( {) Z7 f: \

& t% e# n; x& D/ S- p; W- F4 E

: @0 S; \6 ^1 M3 V- _# G9 r         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);+ o9 Z2 g% T' C: B' r, x' I

, K" [- h8 `9 U' p

' n( A9 r5 G3 N) M% B         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);0 L* ]$ m0 U( w# P* n

% B/ R! Q9 w4 B) W$ x* j, `8 _

4 R# B- r7 H+ S3 g         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); P K) t' G" G& B

) [0 V. S& ^2 [ H/ s) C

' D, B, r2 c7 k$ e0 M  + Q$ j s1 f8 K3 y# ^- N

2 M' b) e% P0 ^. `$ b

$ @/ {$ G4 c1 f8 M& o* D         $this->image = $this->is_image(); ! `. `5 c' h* S1 \1 f5 O4 O4 e% p t7 w

6 i5 A: w+ ~) Y+ }+ u: @

) r! K% y( X' E% y& i         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);, y [$ q' M) v5 D

0 g. A |$ k1 B) X# B/ X

2 R1 k1 y+ A( p2 _         return true; * e% j) Z8 ~# Z( h! n" q( r, K- t

% U/ j5 K& r5 j3 f. T0 I

; R. |! H3 ?3 m$ p/ _     }} : ~- Q- |3 a) o* N& R# z! B; L# u

) [: ]; m( v! Z; C, |4 f

) M% @4 q: K. N0 p( S- \- c& n3 s 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: 8 k5 ]' D3 V. b8 k* U7 y A

& b1 {6 n- |- H7 b L; q3 P

0 L% z+ L% P) g9 d+ o <?php+ x9 ~) L& D6 v0 {; I6 F3 v

! z5 N2 C7 m9 f$ J/ W/ b3 N

6 K. M; v# C- E3 u& {3 e     function is_allow() { 6 Y0 i+ W5 W1 R! n* N# a

7 V0 u! u# U6 `# {' q& T

3 K' s& t7 ]7 c5 r- X; e1 `         if(!$this->fileformat) return false; 3 I9 P1 l1 ^0 B' ~' y6 @& h

6 y4 b/ R8 F7 H9 a1 J6 }

" B$ x, w: m h! S8 P0 [3 F s8 G         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;+ ?, a0 O+ W7 R& Z/ S& f

- ]8 z+ e1 M6 h* H

2 t# d) q2 L- F! h3 _         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; , B: u0 N0 j; ^" _ [; e

# R s; B) Y8 _) ^. E& j

P9 z3 T6 h, Y2 T0 B; N         return true;- j% A( A/ t+ P/ ]4 r0 g4 e

3 v$ U# E+ ?. U

`; r8 W9 b; E* ]% e     }3 f! s7 P& _0 I% o. `

0 e3 D5 L4 s% ^* w: S$ y. @

. z! `% R" X( n8 o, \2 @ 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。/ O& l8 G+ b% t2 A. j

* j; F! p7 O6 S, y1 D P

, S2 Y. H1 r; D5 ^: B$ `" d5 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文件。 5 A8 k5 g4 B9 _$ T% _

- U) w! B6 w& f* m3 w& b

8 `1 O/ \, [( h. Y- [1 ? 漏洞利用 * E X ~, m2 e

6 k i* V5 s/ J* ?! y1 U: S

& }4 l Q- Y9 }/ D% v% k6 q L' e) k 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 W4 `$ \+ ~' @

) G: {0 V8 M& J, t- d% t

; v w2 z q7 X* n- i% V) c   / {" l, V2 [& Q# M, W

0 G* C4 X3 V1 l) d. ]( [

+ t, V5 r1 z: T9 `7 F" A3 F 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid( w# Q) w% a3 C z9 y1 W V

4 o# B* @* R( C# M; u6 {8 K

: @" K0 Y. H( }! Z+ y 不过实际利用上会有一定的限制。# X; K& E2 z0 @" D0 F- k8 ~3 v

- O( n) c- K6 \: d; N: k) N, U! Q3 D

% ]! Z# L5 A$ \5 }) T 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 . u! V* X4 u" P

& j6 \' `% y' C. R0 d( H( O3 ?

7 ~% T9 W/ J- X/ V   ' ^* `. G1 B4 d4 H, U q/ n9 k

8 w: c; L- ], e- ?

: y$ `# ^: X3 a6 k, W9 e: n 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:- L/ [7 C0 F$ @8 t) T' Q! j

5 U2 N, `4 c& W; s5 q4 }7 W$ c

( z* ~: ?6 j$ z2 w" E$ U; S; F1 A 省略...$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]);省略... , m4 M9 J3 Q2 E1 \, N$ z

6 k$ l1 b/ Z2 B3 y0 f

2 N6 ^7 q- M1 B" v( X 因此要利用成功就需要条件竞争了。 , K& a) q- h" d4 _6 W ^

5 z3 H8 U, L' T: E8 C

- ]8 Q- Y% [3 y7 J3 O; e$ j 补丁分析/ L6 D6 t9 Z6 n- a

8 r0 D& P, x% {, h6 T! w

. `* s5 B8 `5 q) }% D7 m   " Q& G0 L% l% q+ ~, _5 |2 U

5 J- O, k4 r/ L# Q) g

( t \, G- [2 ~4 o! Y4 Q 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:8 D/ m5 d; D0 I

w4 ^! h" j( z" C9 {

! A- E+ {7 ?) d- d$ q function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}2 O1 F( M+ m) |0 Z3 n% \. k9 m: G

' q# l* G$ }: j

: m( w! z T$ ?' P. t  . I" D. h" p/ _& V

) C' L7 O; x& P D8 M! |

3 f: f) b" }% Z n 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。9 ^: h9 X; N7 E5 m! J

\1 \. w/ }& O: [0 R0 q

& E0 R M8 a4 d% |2 T9 q 在is_allow()中增加对$this->savename的二次检查。, U, i6 a% K9 f2 D

5 I5 T, [& i& m( ]; `" a6 n6 m

- A! S4 K4 M5 s* j% `9 z. H6 t# G 最后7 \$ e% y( B5 C k

9 N3 x: X! s* q% C# y! w

* M/ D3 X/ w5 c3 v2 O1 J6 b6 I 嘛,祝各位大师傅中秋快乐!; O! s2 W: M. y5 [

/ {. M, K3 \3 f7 e# A

+ g; ~( Z) K8 u$ Q# M U   ; N: ~9 h4 }6 w; `9 V% H4 }- B

6 {* \) G/ C( w5 i- T0 u
回复

使用道具 举报

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

本版积分规则

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