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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
1 o- W9 C) U9 s1 ~( ]; p7 C

9 G' f' I/ y5 z) [- a& T; }3 A

$ D! J% u A# N( g

# T" n; c* V. w( y$ Z 前言 4 Z8 ?+ I7 {% R1 K+ L3 ]

4 L1 m& P" |( @ E

+ W, u% O& Y8 R# S 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。7 [7 Y3 p, K1 w& m& a

2 k' j9 z" v! ^8 S* e3 w: M

( P: o. }: ?- L% N   & T# K8 T9 g8 ~4 N, l8 h6 J* X4 i

0 Y, `# p. B( A' O# Y0 M

& a; w+ N2 H0 q q6 ^2 P 漏洞分析0 y% F9 O+ ` _5 A o, E+ _4 ?

7 I; L3 k1 c- m. B

/ O" ?2 T( W. T 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:0 G# R$ c0 ]- A- {

! f2 A% Y" Q3 R) c1 X2 V0 g

) t2 U5 n1 o/ H0 ^. P0 l/ ~  7 \, M( l3 T( W

+ y/ Y5 W) u/ c3 h

& s% M2 ^% Q( ]4 M6 o 对应着avatar.inc.php代码如下: # ~. l5 W. [( Y$ X+ s6 R" L

( ^9 {' v; D8 h

: ]5 }9 d5 A+ n1 } <?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) { " V) I! l! B- X- w- b3 C( Q7 x+ y

, z5 [# j2 B4 e Z: S* W

( r" H+ M7 g9 O+ ?     case 'upload': ! @6 n" V- v9 k% w0 w$ W( O

9 z9 {0 y. Y( h9 {, b5 }

1 W& J* S) X7 |2 F% e         if(!$_FILES['file']['size']) {' z0 g1 R9 [4 F* @+ l# Z, x+ _

7 z+ e+ T) R* S! ?$ A& ]$ S0 U

9 v x8 S+ g+ e3 d: @             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); 8 G0 W( I" k) N! k

; A- n: r* v0 \$ c+ \9 M

. J( g/ {. p. a* M, [             exit('{"error":1,"message":"Error FILE"}'); - @% X# R2 n" n1 U, [% i) p% K

0 k0 k& ^4 E/ ^/ A0 c3 E

0 p' E" n, p' U2 h+ r- {         } ; z; O* e% R7 M: e8 ]( S

0 c* y+ k/ C5 h" v

2 l- ^# m) {+ h+ u% `; t$ o         require DT_ROOT.'/include/upload.class.php';# Z& E* P' D$ \3 `: X

7 f( x9 a2 P/ j6 @

! g3 y ^; s2 U$ [   3 N& U* k: V! ]$ A" y( j- s

& T4 N7 e# l! f! ]& C. L

& e+ z$ y. u, G         $ext = file_ext($_FILES['file']['name']);8 F; e, U0 Y5 s: R: @1 \2 S

% y8 G8 P- Q! b5 a; h( W

! Q- K& P# S; H0 p6 P3 ^, U         $name = 'avatar'.$_userid.'.'.$ext; $ m) F* U4 |. v8 ~- U2 d4 O

3 b' b; u; f7 g: F, o) b

8 U0 [! F$ H( X; ?         $file = DT_ROOT.'/file/temp/'.$name;( w0 a6 d& H* g

# v5 p3 ?/ R2 L& r* p

" ]- e7 j2 [; Y+ c1 y  7 Z4 n3 u" a' {/ M* \# }% Y

* ^0 ]* s4 Y1 p3 n

6 S) `7 @% @; {/ L# C% [' g         if(is_file($file)) file_del($file); - \! Z0 N" }; w2 y5 E

\4 G- g$ Q* Q( | X

6 H) `" _' k, H* M3 N, K         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');2 A) c. H$ \2 a4 @' o/ R

- m/ R% r# {; P* A4 q. e

; J9 c, u9 [: Y/ p   u3 G2 u% C3 r, w; i8 D

1 d* E, O. K4 w: |5 m7 W

; ?: s8 Q4 M) U! f         $upload->adduserid = false; # M! q0 i' j' j3 s" [0 D4 ^

% k2 d i7 P* H; F; e! S5 {2 d: E

) u- J! d' p( M7 T   2 B' g( S3 y4 b; p" A" V2 ^% W+ R1 Q

1 J% _7 D) A9 e1 r0 V7 d! I3 E$ Q

2 \) w$ b( l W8 G2 ]8 n9 J( P& H         if($upload->save()) {5 k& J# z# ` ^& O

0 X# i5 e# D' x, x+ m- ^

) q# [) ]3 q9 n             ... % h* H* }, k; A; m; k

4 D& g8 y+ Z9 x0 u/ a1 U% k

6 ^& C1 Z$ e, o5 t         } else {6 l$ b8 n3 V0 Y; O( {- M

. |/ a8 k/ {4 \2 W$ y- D

9 u" Y+ v# `% q( C7 p j             ...2 W& ^8 D" m7 V/ I

0 M2 r7 W; t" d9 T

& z9 S$ K( C+ J0 t8 M! ?1 g7 |         } E$ \% }/ K" g

9 A: } m3 H: N. p: q

" d1 b G2 S- d6 r8 f     break;8 d8 k4 U5 g! C2 L9 d) ~6 n9 e

G2 [4 L& N% s" [( G" e' {

) h# Q! X+ v2 r! ~ 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 - t3 ?4 s/ x6 H1 M; p

6 c( e' M- V8 [+ @' O+ v7 F- H

+ ]; [2 T7 a* J) J" F upload对象构造函数如下,include/upload.class.php:25:) t3 e: R; o: ? D s& o& g7 A: w

7 Q! j& ?+ ]' A* [" e

* ?. `& w m6 @; r+ D2 ^9 D1 c/ o% M8 s <?phpclass upload { @: O* w; l, ]$ D

' g+ r6 v2 f% j0 n, @1 r; p

5 |, C% F0 R% v! y4 `: o1 v     function __construct($_file, $savepath, $savename = '', $fileformat = '') {; B) f$ S, d" t: @

1 m% A2 \. J( t' g+ R

! k0 X6 T- o$ g5 Q1 l# n         global $DT, $_userid;1 ?& C7 T8 } F6 u0 G$ V/ w' w

6 v* w. l& m9 I' u$ v0 F U

3 u! M# o* r: @         foreach($_file as $file) {' ~+ e7 E9 R* p+ H% ?' Y8 ^

% b/ N G* Z2 H3 E+ y: M

' ?2 g* c5 o" T [5 h W             $this->file = $file['tmp_name']; : k4 `/ D" ?$ t i4 b

7 w6 t* _# B: e! t6 U

$ v! k& q: g. ~5 }. n! u! x' U             $this->file_name = $file['name']; , M5 k* x9 h# U$ p* ?$ A$ u/ p: r

3 u) P' V! i) O) Q* @3 Q, E: q

" h1 t9 o. f8 ~, O, ]! I             $this->file_size = $file['size']; ( K$ y/ ~9 a0 s

/ T- A6 X! C2 e

9 h4 {5 ]+ B' O0 i. s             $this->file_type = $file['type'];+ x3 P0 W4 M+ U8 v6 t

2 V6 ^5 Z; S3 L0 M

/ Y, d. L! S% m             $this->file_error = $file['error'];% g3 R5 N N4 V" i

: S' M% {5 C6 y; U4 O

! z8 H. j: c3 `   ; N$ f4 h- E% x3 x/ G

# w0 E9 Z. `) [0 v3 A- L) F2 M; U

) ~3 p" p2 x3 P8 Q         }: T/ J* ^0 g6 i& C* F. c

. i9 }4 E9 O) A* O0 U+ d

, L, C, l9 G8 M) f         $this->userid = $_userid; 7 f" r: l6 T: v8 b; Y G; d

- u1 z/ ^5 C. v& E6 N4 s

) {0 C- O( x* ]. \& V         $this->ext = file_ext($this->file_name); ) ~' b# ~% D7 H+ C4 p/ l6 J

7 [# k6 b4 P/ c3 x* K

$ V% t, u' j9 {" Q; c! _1 l( K         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];+ W& i0 {0 n. B B

& _* n7 i& E: {4 t

3 | p2 j6 a1 U4 ^         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; # C( s- L3 d5 i. m/ O6 e3 }

+ d& M6 v1 j# v* V; H) J$ X; K

}: d; l$ X0 ^+ P. d1 D         $this->savepath = $savepath;. i8 c0 Z' X8 u

8 A; a5 Y' ] q, y" q5 V& q; G& O# R" u; [

/ x' ]. }: C Q, W4 w/ r         $this->savename = $savename; & y8 H; l) R# ]) j' o: _

: V2 y& p% t' `' G& a

" k _' ~, ]4 @; n% c5 t     }}* k$ }* ?2 N3 k

c( U4 M: a/ U/ @: e. a

6 U8 |; q$ }/ A/ T6 z/ V! l 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 # u2 M1 _& @: @. J M2 O; |

4 j! [9 D2 U: V) Z' I! e

" I# O. ]- V2 k% O) ~" Z 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 3 H2 y( ^! J, G) @! K) Q$ `

! w# W) h+ [" C3 D! h8 U4 ]! N+ W

; S, N0 D- e6 [* j- ] $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 # q7 [5 a2 b7 ?/ L! j

& v% w9 ` x4 F2 L4 e

3 k. w) e/ U3 f- U0 L 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: G( c( H' R! y( w8 `1 [

2 n9 G6 d+ K# X: b

) P4 q2 Y, F4 K2 d   N3 P" k2 ?5 J8 P0 c: O$ B

/ p2 V/ A; u! }2 ?' c

% P" l8 w( v0 G% X7 S 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: : E2 A( O5 P) X% |! _

Q; C# ? T" i; r3 u. b5 R7 @5 I

1 G! f9 Y# t7 u7 Q& b# K& H <?phpclass upload {2 c7 s0 S3 `. l! r3 x

& A; K0 p+ @2 ^2 ~4 n- B" C) z, I% c

f4 g, O' V% Q2 ]8 v     function save() { 6 F2 Q/ Q& H! G* R" |# j" e( }

0 o3 y" ^8 ^! A Y/ [1 @

- _; {, m: V0 ]: E9 B1 [" y0 o         include load('include.lang'); 1 W" w& g% V8 N. Z

4 o3 S( v; L. u! I

+ b4 L2 t8 V9 V0 [         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');2 w( R7 n: `( g

' c, s2 F% P1 w( x o. M5 P

' E M' Q, u4 l8 u8 x   , _. ]$ G/ f3 ?3 i9 r

" h$ x: V2 `: ~9 y

1 e e; `) P; F. N C6 {         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); z3 i# `/ Y( e" Q% F: }9 D' `

7 n; }* m9 ^) w% i

' Y; g X; t3 K6 h p( w$ G( e9 M   ; W; g4 Q: w2 }. R& R

3 v# t. z; S. ~9 n. X3 Z

7 c- o( D- i+ b5 U) T! l, x! s ]( _         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);/ V; z! v1 Z# N0 M. Z

9 g- E* ?4 m( I1 u5 G

& }3 X0 v6 j$ `3 N; Q   ) C0 T0 F% q& o

7 s" V7 |2 c" ]$ n& o7 N6 g

, g5 U8 u: [" @9 S         $this->set_savepath($this->savepath);" f3 P2 {% q! u- s+ X$ V

! a# J" t3 z' E7 u8 P0 T; Q# y: a

8 A) D5 S' y% z( M. u         $this->set_savename($this->savename);9 n' v N) w5 @) G

1 `. }0 x0 H/ w

# O8 x$ d \0 W   . x4 I% i& f2 }6 P* n- I" G

9 Z$ Z5 M* }/ }1 t$ }: H& e

2 K+ ^5 Y" g$ S1 B) S         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); 1 o4 s% H: {" V( w' i l! r

, E" W3 T1 z8 @+ ]* V

) ]& \1 O# d) N         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);, I) e$ m! j- B' f6 k3 \( O3 F

! A; l# P2 d' X' F' ]

1 o/ v6 Z5 _, }7 E         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);2 f; ^; j! h2 @7 T( J" s

( [, K; U3 |9 o- W: c

) ]+ F% F# o: h! t  + O1 ]6 t3 m; |; x5 L8 x

' Z1 c8 N' l3 J. G* E: k' N! r

* S1 b& F6 w5 Y8 ^: ]6 J# V- Q( C; Z         $this->image = $this->is_image(); 4 S6 Q4 t& j0 T# l/ Z$ q9 A

6 `0 u: o4 ?' x N# ~% u3 E

; J( N$ d- w& i4 i- B P L         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);8 Z ^- o8 b- r% x6 ~7 a3 e! C8 ?

/ ]9 [; Y6 ]$ x/ ]! z1 G P |

! Z/ g$ U% L! C; G         return true; ( Q1 R; X; \( }5 |, V9 @2 {2 V

3 U: t- g' M$ [& `, ]1 K3 x1 p3 q

) I; [6 B) k9 [ l% G. k0 r     }}0 y/ B' Q1 `; i! v) M1 g

% y5 @4 l7 D2 |1 q! |% g! K

3 V4 h9 U# g( ? t- F/ R: J 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: $ s) M( j9 V: L& _

/ v/ `2 {. ~$ b' Y$ X, @- W

: ~; G( r- n$ g+ w- t <?php }7 H' A7 w& W4 R/ R1 M& b F

/ ~8 c2 j, H. C. k4 |

! p0 y. g' a, ?! [     function is_allow() { 5 @4 G; h; g: B! I* y- B

( H- Q4 h( ~2 g+ [; e

F* x; v9 E; P4 O+ M         if(!$this->fileformat) return false;, K3 h$ x8 [: k- {6 e) W

/ B( |" D, d) v/ g* Q1 Y y! d

' q9 M$ q4 Y( P- |; A* C- Y, q         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;: ~% @2 J$ Y/ ~! O7 F

+ {9 `3 ^4 Y1 U; e

. {) P( E+ _8 C% j4 \         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;, _5 e, M, E7 H P

( k9 N9 ~/ d. p6 g7 L, }

( X( A. `4 C$ X6 d6 |7 O' B         return true;! @) V: Q5 u# C' a) O- a

* F9 @' @1 R8 ^# z2 Q2 Z

' n8 X: k9 \9 ?+ E! y3 \) w     } & T' g3 R1 o" K; l# c$ C# {8 o0 U

7 j6 e5 I, M* G, h% j( R, s' t

5 j, N2 W. Z r9 l. e 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 ( l; U1 P$ V9 t: }* m) m8 p

7 m: }! k. H' j4 r9 S, `1 ~& b

+ a5 Z" W5 c8 \. o( f: t 接着会进行真正的保存。通过$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文件。: Q( B8 v' B7 O% v- l( f

6 E Y# U# O; S2 g; ~

' R2 b; m- K& n! P7 m) [( O6 E7 T 漏洞利用. {: \ j6 \/ Q1 @) k) E

/ `8 o0 g1 r, o9 b

1 S8 `# A( L. l8 {( c 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 6 I# y) {+ z8 I9 c( Q/ B+ g0 `: I2 r

4 C# ^$ L- w' Q, b5 t4 w F, `

: w4 q% z' { j; z' l   , D$ L) K6 d, |$ G6 o

" q: Y9 h$ b: J/ a

& `$ m5 ^& W; o7 Y5 W2 }" ` 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid % `) t. |: S. l Q1 q

, k# P/ A8 |) W# p$ E+ N1 j% m

3 W2 Q' v: K* O 不过实际利用上会有一定的限制。! K3 M8 o1 w3 n. Z1 _" W4 R

( |; s- a1 a" D0 D% J+ E. o$ U( q! `

h$ F; h# J4 n- c# r 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。2 A$ @/ ]3 ~' g: q9 _6 ~+ }

! C3 z7 |; a2 \" H' Y1 p0 z

8 i. ]+ |9 Z; [8 a9 `   0 r) [% b& r- ~) T; l" l7 z$ \

$ r; Y+ `6 p1 L& Y3 N

) _* x R0 b3 z, C5 P8 w 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: + A# Z5 P7 @! m' I

2 k4 \+ W+ | e/ g- N* t/ o' K

2 T4 E! h( a" r' s5 _ 省略...$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]);省略... 9 t: u0 v% B% G( v8 \1 A+ g

3 j; ?- L; ?2 X: o

q2 \& |/ F! f5 o. T$ ^7 G( s 因此要利用成功就需要条件竞争了。, ]+ \% ?, i+ ~0 e

& A# A7 [1 g. g

/ W* _ M. z; O& t6 @" [9 b5 I 补丁分析8 E1 {. w }3 K! A) W$ j

1 x$ a! W; t! J0 B# A& I1 Q

0 G7 [ Z3 a1 A" F! M7 c   6 [$ T2 ~; c1 v; ?6 ^! O# {+ q3 k9 @

5 P" J9 k) N2 a

% O U0 e5 t) p- z5 | 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:5 {) ?- _ z" \& A7 q) i

8 F5 @) _& G$ Q2 j) ?+ o$ q

' G6 D2 _6 a# X9 r( [+ g function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} - M& m4 Z: X% @/ g- S3 p) U

6 v$ i8 e" h( |% H: F

; `7 N; i: \% }7 k1 W   % O) v/ V, k$ J6 p# H% C

( O0 \ g& r1 [

7 @$ V& W+ r6 N/ W9 H: M' ^" T8 Q& X 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 ' a1 [3 |6 P) [7 `

6 H8 O1 \( R6 m) k* V

4 _& ~& G8 b! J: X' a, q 在is_allow()中增加对$this->savename的二次检查。3 D4 ]3 B- Z1 |0 l/ m

1 Q; |6 C* q5 p3 ]" e i- Y$ A# d

# h& J' x' [% G6 k7 O% |) |% a, B 最后, m* k7 A0 \; t, u

@) ?" [+ F4 A5 w* Q

2 o; H6 o. l. A. ~: R1 Q" W 嘛,祝各位大师傅中秋快乐! - {" R i- B0 z

, [1 O/ S, ~5 `2 k

3 I/ n9 V+ ^9 t4 m' L* D3 Y; u  0 I3 \* T! {( `5 P- H; A# P( m

- v# B; @& R: b5 m3 n- L
回复

使用道具 举报

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

本版积分规则

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