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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
9 a# a% F1 p! ~9 r

; n& V+ g$ j$ Q% y9 p; }3 O

* G. @& K& i0 X# O

6 |9 G W# N4 o' j) j2 b 前言1 D- }( m. l: I: Q2 X5 K$ r+ o) ^3 ~

1 ^( K+ n6 A; c7 Q! v: \3 O

- b$ P9 q* J/ `/ h! s 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。5 c! f( S4 P" z9 [& U0 A

/ w- F- s8 D: @/ I" `( H, A- \+ Q

( N- c% F- j4 \. B$ ` W, A( @  ( u( M9 y/ t/ Q0 r; e( a2 }: n

( N4 q5 W. l7 v+ a5 e

1 F9 e0 u; W: x" G 漏洞分析8 P% P( H5 `! M6 Y

3 S6 `" Y4 A9 ^& w w

, g* u' Q! \3 _6 |1 h# o; [3 \ 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: : H6 W, y5 ]3 T" J. s

/ o7 d6 |* ^ L4 i1 V4 X4 B

0 [! J6 L) a2 ]& _4 a  3 F; q) i7 X6 g1 {+ Y& Q+ S3 g

7 Y: T1 ~9 Y" D6 t5 [( k4 `

# q% l" ~7 p* B+ V7 J/ l 对应着avatar.inc.php代码如下: 4 ^% M$ d" S4 T* G5 M

3 e: y- O5 J0 y1 Z* Y5 O6 ~

: ]: P8 m3 D5 f) w4 O# T# c <?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) {- _$ p' S/ O/ ~0 l

; S* E, E/ }& I" C

: J$ _' J) z; H$ `6 Z" R( |     case 'upload': 1 A! A: B% [- s; H2 @" G! g

( _& O: h3 t% M

& l) w2 s+ [- e2 g2 {         if(!$_FILES['file']['size']) { 9 g9 e/ U @' l% ?, T* Z# o

/ w1 a0 g' Y8 e5 ^' v! U

% j* u, ]) a! L* c9 x$ r; L             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); # G) H5 X& V7 \$ u6 D: ~+ s* E/ P+ ?

3 o: |; a$ a$ O2 b' c

; w# F: Q/ g% }* e; t5 {: z             exit('{"error":1,"message":"Error FILE"}'); * e+ D* v' w4 ~

% _3 f- Y7 L* ^ _. O9 Z$ D( w

( R# e$ [; B% }4 `: {- Z         }6 n: |1 \( D/ @. g

6 W9 o0 B Z7 w/ Z% \1 ^

# V- F9 a1 z* V6 L         require DT_ROOT.'/include/upload.class.php'; : b$ E( K) Z9 E& t& ^; }, Z: p* ~7 b4 h

; o" o% M5 O7 t4 ~5 H+ ~/ f

" w4 p( y) F# q  , t& o a5 [0 z2 K

" @* m' G1 M+ G6 P* }: i

9 f( e* W" E( v3 q         $ext = file_ext($_FILES['file']['name']);2 C: e, O0 U! \- l, [: Y9 i

$ X# U8 V' E8 K1 B, v

5 z: c; p8 P3 U7 `7 W         $name = 'avatar'.$_userid.'.'.$ext; 3 @: s% H# H7 M

" L# m1 H9 l9 o. j

6 [4 ]$ r! A b+ P         $file = DT_ROOT.'/file/temp/'.$name; / q0 y1 s0 E" s1 a

3 R4 k7 Y1 }: N5 W

9 @* d! Q K& G   7 B9 h/ W' a! j6 s& c

% g# j* p1 z0 {6 N7 Z

# b8 L2 ^0 |6 A         if(is_file($file)) file_del($file); 0 n: I" I& o4 `0 y3 f$ H

. M2 B9 U- o Z, \8 n) `! Y

5 o" `# L! i" z+ L1 f2 Z9 i         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');# w( w9 `# V' s( r0 j/ V

& Q, j# x' o2 K% f. E- {

2 q/ h$ }' v" ?   + B- x) E& q! j. ^9 c

% @) e6 z" U1 K. L- j1 h

8 m' N6 Q6 S) d/ _4 G( Y% n% e         $upload->adduserid = false; $ f& [. a$ m+ U4 }# ^, K( g! j. O, @

% j# H3 V0 h7 c6 A

& ~2 e N; ?# X" R8 q5 i: \4 d: B  ) k, Q' L- A O X6 _$ |

3 ^- O' [# X2 x6 j# R% ^

1 z- J1 i! G' H6 p& D         if($upload->save()) { - [! t7 W0 X* l/ Q6 v# s

* L2 K% ?( X8 j7 K

5 E4 x! p/ c+ ]) W- I, ~! j% Y             ... 2 z6 l% E$ Y1 V% e% v

, V7 |$ h& K1 Y0 J' {

6 W! N; M- G$ z* F$ V) K         } else { 2 F+ I& F8 p% I' W8 i

; f9 B9 z3 h+ d) M3 V2 y

; R( \( W* W5 D! J( S0 l( \             ... 4 r# _9 ^2 `2 o. F

$ Q& `) N/ w" s8 V3 v

: S# Q; T4 A0 H8 `7 R         }" j+ k Q4 [6 D2 [( S, H) K4 S% ]

3 T0 r7 \1 e* a7 y" w- p

0 j4 Y- a8 w" R* Q3 _     break; 2 \ J7 A1 Y' K: a5 A! p l1 X

v, I' p, r% D% ?1 b# ^0 L9 s3 K7 `! s

) D: T. s# a) j& S! d! F 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。9 u+ l+ }# h% O. G5 o

( {- X& K+ H+ P- B+ Q9 V

: v' g5 p- y# g) f upload对象构造函数如下,include/upload.class.php:25:0 Q: Y4 o/ |+ n4 }' L0 C

D1 A7 m, o+ U

) G8 L0 b" o+ T7 |9 } <?phpclass upload {' m1 r0 e8 @3 |

# c9 `: j2 }6 u. R/ T/ v

7 b4 b. ?7 a# L a$ d9 r' x2 _* m/ V& [     function __construct($_file, $savepath, $savename = '', $fileformat = '') { % X$ Y8 k4 [9 h7 U* ^3 r! ]

4 J1 r& v" p- M- b6 I" l1 Y

( S8 d( x6 I y- h( J' t+ R         global $DT, $_userid; 0 ]) p5 X8 n) q% t5 g0 d

$ @* f4 C! M3 ^! A9 d3 e

9 M) {% v- Z1 D8 v" D: {; f+ @% S         foreach($_file as $file) {' B. T4 C) n+ `

# L+ K1 z+ C* b Y0 `: Z

& v. n3 i6 O: P* Z             $this->file = $file['tmp_name'];4 y0 S# @+ D7 O

* w* D) {. l& i0 P8 D2 F6 _7 O' O

[8 o/ k6 T4 k" y1 k5 z             $this->file_name = $file['name']; ( ~3 C" ^. A, Y \

8 n7 D/ U3 ~# L2 n+ m5 w

& ]/ ?7 ~6 \2 Y3 K/ j7 ?1 z& ?             $this->file_size = $file['size'];4 g- M/ y# Y7 R$ x

6 I! v' Z/ k( o0 F% X& B9 P8 Z

& E% z6 A* g+ z             $this->file_type = $file['type'];; j, l) h0 p! X

& A, c$ O/ I, l, Q! K2 M$ f

& i$ b3 F* `) @1 |9 a             $this->file_error = $file['error']; 8 @: ?5 z1 [' I: I* ?1 p5 J; y

" {" g. \, q6 s" C, g% T5 d# U

" R; B! o- g! W# k7 M3 S: |   + U* }3 U% V2 B8 X |& d

$ L9 {5 |2 u+ a( G, }. G2 ?2 ]

5 g: _+ p4 E2 u. _9 t. R1 T         }2 X7 T; |: ^" v) A# h' A

' k: U1 i; U" c0 G4 w/ f* e- a

$ J. W$ x" t2 j. h2 b* j         $this->userid = $_userid;$ q: k5 v2 x! @6 }' U8 ~8 u: Y6 k

# M/ \, T; S" ?, \7 A& L& K5 B. b

3 `5 V c- K+ i2 T2 P( K3 p         $this->ext = file_ext($this->file_name); # D6 O! B# I- U ]6 H

) T( e0 b" C4 T+ N, F. K

5 Q k, t" S# ~" j. T5 a         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];! Y: c) T: ] b+ W! t# x8 e

8 f: g6 l, M1 D+ ]+ w

5 q, G' k \3 L0 S; L$ [3 x         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;% i1 W' Y* }+ H3 y

. ^3 G; L& J2 h# G1 Q6 ^0 d4 f

0 ]4 _! D1 t% W1 y# ]         $this->savepath = $savepath; 1 \3 A8 B) v# o* r

6 x% p+ O7 Q. z' i

5 {$ q: x6 u# x5 d! f6 j         $this->savename = $savename;/ ]: B1 m# s* S% |: r/ @; o

+ g. x4 a, X1 ^8 P3 G/ y" D

' G. j5 N( X! s% B     }} 7 F3 V- \( H( A/ I

' |: e% T' e: {' P4 b# K

+ V( P2 Y) F- o' n7 j3 a' B- _ 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。1 Y5 ~, m2 f# S6 _& a

& t! @* q# R2 g, X; M8 i- n

3 o% a4 x& i: M7 [7 } 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 . U1 a; I$ F8 n( u9 r3 T. y

f9 m; B% {' |& h3 c' ]- ?- X

' A6 a3 Y# W$ T- ~ $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.php5 R# T* A5 N2 ^

% L: d) r$ Z- y! B4 u

3 Y0 P) ~$ z2 J 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:- m) S2 a8 b& A* U

; G t7 }+ }, }& h/ y/ D

, ]) O, v/ Y3 H e5 z  " r1 w+ S) i y7 E& p2 k

8 S- ~3 U6 L; j( a* t0 W

& u' U+ n+ b& F, }& a2 | z) [- z 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:: e: |5 J. r8 N5 h: U

* K$ a/ \, N; z1 D1 A

) |, L1 a8 E4 {5 M0 A <?phpclass upload {9 ^* t H2 n$ F% w: H

- U0 b0 ?9 U6 e8 P$ @0 y4 ^% \

6 S6 L2 n+ ^& ~" {7 I     function save() {- c1 O) h) L+ j: M

* y. l1 i( b7 S0 o5 q7 Q6 ^, @

/ J6 L0 B# d" V) e/ n3 Q         include load('include.lang'); , z, |3 H( c; \- T, N

# g/ i, c9 n/ ?* K; k

: f0 d* R- T2 \2 v1 \& F         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');- s- N1 N) t7 C. ]% l

3 [( g2 l2 ]+ h3 P6 ]( X

& C. m+ V2 m4 o5 F$ X   + n* z; ^- c m' X5 d

. [: o9 \; Y: l/ `

1 I/ X0 \7 g1 h) q0 f         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); 3 |8 C% u, @( ^" `

; y* u# R# `, l& o, q7 F4 [5 O: S

- p6 R m6 u+ [2 D4 j# @' y; S  " K" L s: n. ~/ h4 k& r F: c

: X4 h9 A7 w7 T* o) L( \) z( P* W

& x& Y: _/ O0 J$ b* _. ]         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);2 H$ T+ Q: }. y0 K5 k& W7 M" _

% X& l6 k+ {/ H n: w+ g" j

* y \0 E9 r. v O$ U/ O  2 u3 t _4 z4 E4 _* v4 f

2 w7 N0 c$ P, R' X' a5 N: ^4 P- B8 |

- q9 z: x V8 j- E         $this->set_savepath($this->savepath); * ?; j' s$ B0 u& ?. B

, p( O; _4 T; n1 R9 S5 V# u; f

/ |: b. o) N8 ]! \' H$ O         $this->set_savename($this->savename); / u' s$ Z1 z& {; K

! n8 g" X9 T& u8 W" l! ?

+ Y- \$ }5 B+ f5 H& r  3 i+ i! O: i) ^. T. u; f

+ E k5 W& [$ {7 r

3 H- C' w' Z" O$ V$ T         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);/ v( i" x+ U0 o0 \

) j0 l* Z; }/ h- _) m, [

. L3 S% r* b" E* v( E- h3 x         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);% ]" ]2 A! ^# ?4 i+ [

$ D v( d; }6 A2 P

, h4 h @! q( U5 ~0 O- h4 l         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); $ }7 i! S' @) D# s

) T! c0 E- B0 g* ?& Y+ A

0 w& t f# _3 o. J$ K( t) p6 g4 w   , G! m& ~2 ^! S9 k; Q

; s0 z3 T. Y# t( l; m I. S

4 t, H% s' |' g9 B4 F6 q         $this->image = $this->is_image(); 5 L% f U, [5 s' O6 A- |4 [* N

9 ?, c9 \' q( {. p f2 K

* q5 w4 e5 P4 y; B( m5 T         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); 8 o9 I: f' a! Z* K0 g& ^' b

6 f1 I' z. C* N; g- x! F

4 v. q( a6 ]% T7 L* n% Q         return true;! i( U1 G$ O9 s3 x+ c! N

: f3 `0 s. f4 f' U' W: `

% ~) E( i# C: T0 S, q- |     }} $ a/ q, |! x# ]8 Q

6 T# ]% |$ G5 }9 f) x ]

& j- h0 c( w( | 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: 5 n' B" O4 b- ]; J5 [

& y5 l* i% K4 {, a

. i" h: U/ @6 u7 o/ p1 U- q' n <?php 5 h( |7 x( a$ T: A4 [

8 j: D4 y+ x, _2 q- x

9 N6 v8 v' }0 \, g v     function is_allow() {1 Z( R4 @0 p. f3 x! Q

7 U0 M3 R8 t- t

3 E3 m5 e* C+ W7 F$ h         if(!$this->fileformat) return false;$ ~6 @- c# ? r6 R4 ~

: V' R8 T1 ^1 j) s* ~" _

4 @4 T9 j& S. c5 D         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;* I4 K% Y: `, @% m

6 P/ ]0 T! e3 v0 ?! p

: N: k& S2 n( V. a7 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; 8 o6 z- i: A& R% @& L8 T( Z& g2 k2 P

; w- J- S9 c4 e5 m# g0 N, X, n

) o* C0 i0 G+ o% o$ A w         return true;- t% H$ @# x, Q4 x

) n9 \8 i& M2 [" B& T

! r' C6 ]* x" \ d     } 7 Z+ o& \" o% t- \. B

: C" }8 @$ t8 G$ y/ b) |5 @- p( Y( r

2 h4 y' h" _ g0 ~2 [ 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。+ y. [6 a1 u8 U; F7 U( D

M9 Q4 L% J' C! I5 e& [* C

8 |9 R& ?; K5 Y8 K% i9 l- l! \" ? 接着会进行真正的保存。通过$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文件。2 c& C; L; F( @* Z6 K

& l" z) }1 r- C E: n

- c( P2 \; A) P! g1 w 漏洞利用 4 k4 y! P5 S& z, x; `6 p/ D

: Z7 f3 z, `4 Q+ {0 K" {$ N4 @3 S% G

$ t5 C8 T! x+ O3 q$ R, C# H 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。: p& q. y# n g! |

; m4 i( f) [, v. e

" q! Q, z1 v3 q   3 z7 t& c F; P9 G

5 b2 b# Q6 n3 M& }! ^" Y. c7 u* b, w

: k, o6 C; P0 m) } a5 P 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid ) m# O; T6 F0 v: D$ D/ ]

7 S' p: O! E2 ^5 V. `6 P. V/ j L; S/ ~

" i' y0 K$ j2 P, `' j/ x 不过实际利用上会有一定的限制。' u% w% l5 S+ Y0 u- |' O, N

3 V2 C, J+ m3 Y1 ]6 D: E7 `

, M9 q! d# M( H 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 / a) H4 n, S+ T8 ~' z/ i$ o

; Z* }+ D9 c0 X) ?

- {8 F! R/ F* B3 q. L   ( x& J2 g; C6 F* e5 U6 e; ~

/ i% @5 c ^: f) G6 Q, F, _, s

" P/ `' M) A/ z4 V3 @, r) {7 o i 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: 7 n/ m3 n! J6 B- b0 P; T @4 h7 X

, u* e8 I) ^. T" f+ m ?( w

8 V' R* t, A: 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]);省略... 7 P x W% o9 x( n1 e6 e4 Q

" q) L2 }+ L! ~* ~( N' U

3 ^8 B: j5 I% [- q( h0 W9 L. C5 ? 因此要利用成功就需要条件竞争了。- V8 x* D k, X% X

2 ?% y8 W3 G1 L6 ~4 M, l

5 W/ g: Z: T) [0 l! ~5 g0 b" M# e 补丁分析 . W- ], E; d5 J, b5 p

8 C6 n- p0 h6 R0 r

* @- G. L6 |* b q/ t  4 r/ i: x9 ~0 {5 E

9 k" _1 [+ q+ H5 T' g( B& R# R

0 S' m' z0 @9 @+ Z9 p' W9 \- l V 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: ( u: B. Y9 Z2 F

) S. `4 w/ e/ Z4 V ]

" R& i2 J( h( o function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}1 \# ?7 w6 `# n7 d& i J$ U

% M; k' q8 }% n2 j, q

4 g+ Q7 c$ z& J  4 i* {* G. L! j/ I

$ T) n7 p' m+ H8 D A( @0 n

5 q6 E, l% m* X( | 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。2 l" X1 r( n; M9 z9 ?) h

5 @, f6 X" ]8 d1 }# T

1 t* h& V4 ~ s* p( u( \ 在is_allow()中增加对$this->savename的二次检查。 Y! x1 B) z. a( |

/ C* y# {( N5 X. v6 j

* Y! z0 y5 Y6 g% n; x 最后' F' J, ]$ z0 y

+ [( b" C* H0 O0 ^) L* g

& P' ]; J( a4 Z3 A' A1 v% w3 {: X; y 嘛,祝各位大师傅中秋快乐! ( L# S, ^* F; w* Q( d% d

# g0 M% p/ r6 K9 h/ e

/ a/ r2 u8 e* k! G1 y( F9 Z, N3 p$ _   . Z9 ~3 Q, z7 M

* N& _4 T3 Z/ g% D& M( d$ s& T0 Q
回复

使用道具 举报

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

本版积分规则

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