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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
7 W3 B, u* S5 x! i

1 P0 p- p% S5 w1 @% S

3 w( r7 B H4 P- V3 p, w

" J O/ I# g: s2 R7 I ~* b7 ]' l2 O5 ? 前言' `6 g2 u+ D% U9 ~9 u3 ]" v

1 w# T* r+ Z; a

2 y5 v; {5 S5 h5 B5 }7 R 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 4 W4 w" ^, N$ B

3 ^" E7 w7 f4 E, v% ~5 Y$ Q

7 R& H/ f0 o# V6 h6 V4 M+ M; R# Q   0 X8 D* p' Q5 b

# z; i! C* M- c% M- R% V

6 g9 W4 [) y' Z8 }+ M 漏洞分析: X2 [+ \ c7 A- K! `$ u; y

: F5 \' M2 \) o2 k/ H

. z- f2 [. v1 X8 a5 s 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: & s2 \# h' J" n2 X

" Z* _% P" t. ]

: \; f8 t- R! m' P& n1 y, p   , K$ p$ ]/ q5 ~7 J5 B; J

' L" A; R; V% S, K; ?* }

( o8 d+ n+ r5 h# l( `* K 对应着avatar.inc.php代码如下: ' J# k1 X8 p3 B6 K4 F( Q

9 D2 f' m! P! ^1 r

8 i! r- \7 d$ 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) {3 v' @) v7 D7 p

: \4 O, Q4 W# m! ]$ S3 q+ i

* L: m9 Y6 Y! N" Q( j2 W2 ]# R     case 'upload':7 U; N/ b" m% Y q" e5 O9 F

, c2 F) _* n( U, ~ G! m

. `: W9 |$ O* `3 a5 D$ j: F3 G) s$ B         if(!$_FILES['file']['size']) { . z5 b, H4 B: @0 F( z

1 ^. Z1 l# y3 o' c, } V( x

8 t j6 O% {% |4 s5 E2 a6 i             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);1 v' N n4 V8 v

6 N5 C0 \& O3 X0 ]8 Z0 G' k* h4 n

' P8 p" s: A8 }: y9 g, s             exit('{"error":1,"message":"Error FILE"}');4 F/ b q: _- f) Z# Q

* t" \1 u3 B: E2 E7 S, D7 z# r- b

. E8 w" d& s& `4 b3 x" c         } 4 M( O; D0 }* G% X/ A! G; Y

% r8 ^) }" \4 I. e

. n" A% b! |8 [/ X: q' Z L9 ]9 m         require DT_ROOT.'/include/upload.class.php'; ) L# v, e8 e4 N5 R1 N/ t- H

: d- q5 [/ r, @ W, i7 f6 K

0 U4 R8 K# \' Q; x+ o! P! {8 z/ e  ! o3 W: A. B* D, |" K- P

3 ~) [7 v% X9 o- }' @

2 A7 w; H. h: w9 V5 Z1 J         $ext = file_ext($_FILES['file']['name']); E6 R# [/ D3 s* s. J" ?4 {

. S; f5 G. I- a

3 I6 S9 @' a& N7 v) E# M         $name = 'avatar'.$_userid.'.'.$ext;& I8 N: ~5 I0 Y

( L/ S/ O5 c$ E+ d2 m7 c& B) M

8 O+ O( A0 f: b: m" b+ q* W6 Q1 ?         $file = DT_ROOT.'/file/temp/'.$name;6 S7 z5 k1 s: h

m/ M5 K5 U3 f

, {; a% S& i- s c3 X7 Z$ p   ; \' c/ Q" ~! D% j# b

+ f, @' p- p$ {3 R* f

a& c5 _/ Q }/ F+ J+ P( o         if(is_file($file)) file_del($file); + e2 E" U) {" ]8 F/ r( r0 F# W2 p4 F

! j2 J+ b) U+ A& F* v3 w7 O& M

, t. F" P" U" l4 a         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); $ Y: \, X3 ]: n5 A$ b9 W

* w! P* K0 c$ L: R: a: o1 w' p# p

" j: `0 H/ T, b( M @   4 B3 S: R2 I: ^; ]' h4 W$ s

$ x3 n* Q0 F0 {

$ }% @( ]! h8 J+ J0 I% @7 W0 |         $upload->adduserid = false;7 g( P/ I- W" ^' j3 d+ ?

, ^) s3 o/ L+ ^

5 t. |& k) l! e. Y! o2 `# a/ Y  % ^( x8 T, O8 @0 F. W! D

* c& K9 o3 p# s$ i2 u! y

+ a' z! u6 G2 b/ n: {         if($upload->save()) { * c( O* S5 t# T( }# }

' P+ y2 E/ }! ^ g/ f7 F0 z u0 T; \

/ G6 q) X: }/ P             ... ! [1 l+ E* I$ Q9 m5 u, y8 ]8 G5 X7 y

& \/ v8 {6 ^; m1 f

/ ` z \/ ?- g/ L         } else { ) ^6 ?! O- @" l- ?& j) j s- B( G

+ w7 L3 x/ C k9 v, ]2 z

6 H0 m# n- w& W8 C& j v' y6 n0 T* T             ... 9 o* h7 E" N0 X) a

* Q5 H, k! e9 ?. i* B" v

5 |0 G \* b# @7 p7 w q' V         } ! c1 \8 e$ Q& t, q( W7 N6 P, `9 ]+ v

0 ~$ ^3 G8 E- i& e4 G' G) Z0 h5 p

# C. Z1 ]. a1 H& D     break; . d7 c; q& E: E( g

8 O$ H8 Y( e& m- X

2 m4 \7 y! S" d0 Q 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 % f4 L$ H2 K, s+ E8 b

' b! S$ @/ F- }4 T `

+ P4 l; Y8 P% L3 i+ o$ J upload对象构造函数如下,include/upload.class.php:25: 7 Q* H- O' k" P" c, Q* E

. P1 p$ V M$ G

3 B) S& _5 g' L <?phpclass upload {6 s7 H. U4 A3 B" X

# u# l+ j) g3 a

9 b6 N! U8 J$ z0 {5 J+ }     function __construct($_file, $savepath, $savename = '', $fileformat = '') {" u" }0 T5 _1 F+ s0 x

4 x/ s3 _% j2 G/ M- e) n4 R

J: N4 Y8 h4 D; A4 D3 m& V$ G" a) S         global $DT, $_userid;+ D. L1 Y2 _' q) `% H

: w4 j# K/ b, a t% V" ^& D( T

* P: S. Y: [( |( p0 Z         foreach($_file as $file) {' _6 N6 B8 K; I# P/ S q# ?

+ M g' o: ^3 x" \4 k( _5 r

; n4 ]4 ~4 ?+ |$ u ^ f' L             $this->file = $file['tmp_name']; # i% V/ w8 W7 |. p \

0 t- P4 A. H& T F5 J

/ t Q$ p& y, z& @: l5 y7 s             $this->file_name = $file['name']; 1 r# o! G: } e( M

& E! _7 F+ c% c

+ g* a- N. O5 g* M3 ~+ `             $this->file_size = $file['size']; 2 }8 X, q; |/ l# A. X$ e

$ J3 a3 \$ Q: }

, r, [8 N3 N1 h- `             $this->file_type = $file['type'];/ r! ^1 Q( T5 }; C5 J9 O9 q$ D

" G' A- ]4 ?, M0 l: X- o+ P6 |

$ H* G) u2 J. `% M4 ]             $this->file_error = $file['error'];' m3 }3 L: Z& y3 e: p3 c+ `

9 ~, R6 N4 d( }7 ~* M- r3 w1 \

5 d7 ]: v( a/ s$ `$ b   " J, j" }# {# ~9 g8 l+ n

! W& [& b t% \6 ?! s1 L

. H0 F7 \& u/ ^. f         } 9 n/ h6 d! L* x: K6 V* g

/ u: P) S' _+ ]

: @7 i! S$ G/ Q% d4 f- i% m         $this->userid = $_userid;7 h5 B3 ^$ H- T. a0 J& E" j* d0 L

3 w" f. u- e l. R+ z

1 H# [/ ~5 P9 r# V$ F! F' [         $this->ext = file_ext($this->file_name); ~+ u3 M. m8 b I

& u. c& m; l6 y5 w

. s) O, P, @3 ^         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; / b' ~+ y @/ t/ ^7 O! f# o! G- @

( Z0 K0 o' m, Q; F" E, [

1 n7 E" y" p2 {6 H* m         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;4 }- `7 D8 X1 L+ W2 s! b8 Y: q

6 z; v, R. P: q( s# V9 t# m: l

) S ~- R F% j& B         $this->savepath = $savepath; % I5 w4 E6 K$ I$ W3 ~6 ]7 R

. I' \, G! i7 w; c9 z8 J

" P/ C+ k$ ]8 |: h         $this->savename = $savename;' g. l6 g# V; ^

" i0 P( R$ E- M. b2 D3 @

7 K2 g& e9 U. y% j4 Z4 Q9 l     }}) q; m ]/ L. E4 w7 C

7 D5 c+ h/ O5 X

3 p; C, D, N0 o! P# C 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。" p( b0 m( t: ?

/ {- D& v' P- F2 p

0 w; ~) V7 G/ v 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 - F( g4 {: O! F

) d6 J4 z) S1 P* V; W

/ S1 G1 O% k4 D& s $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 3 |5 t5 z$ x5 V, X2 q

5 _ Y6 ]# H$ T; z+ X- G

; K: V' o* [2 O' x 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: 6 p7 ?8 ^0 a: v' [

% E# c3 e7 s/ t4 l# I4 E J

# a0 v) E; A1 | l1 A; w  5 h% x5 d: x: @- F, C/ z6 H

' x4 s5 s. n/ H

8 G6 b* S X0 ?0 b$ v4 F 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:8 H+ r' ^* U# v3 K

) [0 d% i, [7 B$ c

. \( k, U! z4 [1 p <?phpclass upload {$ K, g) {' K4 j: ~1 V0 f

* f. m; l) ~( o3 G- \( D: [

3 K4 S" ^# H, j2 }     function save() { , b( t9 _0 G- v

, P& O. |$ c/ \1 v+ a

2 X9 Z& O1 j, d) D! a/ B         include load('include.lang');+ s: X: v, y1 ]' i* s

# a! V! t; @4 N6 t

2 j2 R- O+ e+ A1 c" B+ p         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');& m/ U% W& O1 c4 _6 O

- S, W$ R7 K) z" h0 v+ F! K" n+ m

% v T/ F) F3 b# Z3 D   : D2 ]' m/ d9 M6 D

& Z9 |. U4 u1 J5 @

$ B0 j2 H7 W& j3 M5 ^         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');( J5 F+ C8 x/ p0 j

5 A. ~5 f H- L+ L

" z+ v @) L# U# i7 \   0 b% j" s4 {2 I1 ~4 [9 D5 t

& {6 `' k- A( J

/ u7 s; O7 |7 c" o0 b( J         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);+ ]! ?' I& R. k. A# _8 S5 w

/ H% @/ v: M3 B

% x2 i# D+ u' A% ~" \  $ l2 `3 G* H, f. @' k& X0 q

6 ?* w' [6 F7 V4 b! D, m

- `, `* a* U% Y9 y. P. u: r3 Y         $this->set_savepath($this->savepath);5 y( X+ C F* e r) x3 I

7 w: `& K k" C

8 h9 }$ }. ^) Z- o) I' O. d4 P         $this->set_savename($this->savename);) U6 W& r. Z x- |. W

, v2 ~6 j# `) F# _- M

0 G' e/ \2 l, @* H8 V, l1 q* r   $ B1 F+ M* e% x& \

- Z! S- u3 P- w

) v i( Z, D9 f# S. J# p         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);6 m$ U8 u; b$ a W, @5 f

# r: m) l3 G' g( X# p/ i

4 ]: s" Y$ Z4 s" }         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);% X) }1 U* I, ~

9 M( d$ p8 @+ o

! \- ^) L6 Z3 u4 T- g. D. f         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); F- h) X! R A/ w5 y

" ]4 z9 z' z) W9 Q% x6 _

9 r3 q# `& f3 M7 j6 r. E8 B+ m% M6 \$ @   5 ]! r# _2 {3 ]+ B4 |, f

: i5 Q2 o' t1 p

5 R( o, p2 K8 E, s {         $this->image = $this->is_image();/ B/ V o. V$ Q

2 w% A/ _2 ]! b2 b7 ]0 D X

7 X+ i, u, P3 n5 M1 |$ _1 ` L: G         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); 9 p( m$ u& A/ C# m7 }

& l/ m7 _* Y( C: w8 O

5 b4 L/ k. e+ X7 _8 r# t         return true;0 I& }2 f8 q3 y! W) ~' q7 X, o

* s% H# d# t7 r8 F2 U4 Q) z

; Z' V( B5 \& ~" `- d2 @     }}% r: F8 Y" Z( l1 x5 j

: k9 S+ g- k* q) e4 Q

7 ~7 G9 S% O6 m$ S+ p/ U 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: L5 X) A7 J$ ~. H. F' x% V

" e y% o0 D3 @4 W' O

$ _7 x9 I6 ?8 ~. ]4 w7 c L% b% y2 N <?php y' a. X. X' t

' B; [6 |% l2 U

3 G2 p `6 ~6 v4 c, _$ }     function is_allow() { ; D( y N: c# G2 W

7 O. o( @- O% W/ B

" r# h% M8 r3 z/ b" D         if(!$this->fileformat) return false;5 \: w7 b4 U; f8 w- Y( X/ b1 w

9 f$ U6 `# m3 ^5 F% _

3 i4 e" y3 l7 P. x, s0 i# Z         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; % k1 e9 a, h. t3 ]) s

/ ~% s# n+ K5 _) g5 H7 Y2 k

. Z* Q! G8 p! e$ R. L2 ?         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 m( U% o% W! _! P8 A% |; ]" ?

6 g$ |3 O( _* \( O% S- X

3 ]' ]7 ^% `4 B! M ]4 i, |0 P8 q         return true; ' U3 p6 ~% W& c9 _8 r# H

( x$ i3 U; q6 D2 y: x2 [6 d" t

, g) R" T$ V6 f5 W K5 N4 ~; j     } 6 {" B* w9 F! V! E( V# J: P

7 a1 o; W: q5 B

$ _& |5 Q4 R4 S% L" ~* j. } 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。6 Y2 X$ P" a4 N" H# |; ^' m5 a/ S

. X$ b$ H6 @# a# @& D# }' }

0 o& G. G! b% Q) c 接着会进行真正的保存。通过$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文件。 1 g4 n y1 |0 H- \

% d. S4 v5 b) t% v4 k( b

( o* u3 v) t( q. c( W 漏洞利用 R* q. g) {9 }' H/ o+ v; v

& H+ v' @6 Y; K" e8 G8 _7 m+ t: j$ O

, w* F% m0 q/ [5 ? 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 , Q+ A: V. n; z/ \8 s5 O+ \

" K: f6 `" T9 x

/ F) y# A5 b' N, u   9 I- p+ I% s" d; L

5 J& K) R. j# T7 C

" j& |: z+ n1 m9 `- O; g 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid * D- Q, b* ~5 V: L0 k, I! n

7 D3 d G' ]/ p2 O

" q t" m! d* R- |8 M2 F% S 不过实际利用上会有一定的限制。 5 v% p5 q9 o! b2 K K

/ J1 T& K4 `6 E, N

! M5 X2 f- `! ]' l 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 ) z; e5 ` P$ Z; [9 v3 H

# ~+ ~0 H4 v& e# c l3 C

9 F7 q* g% e" f! ~$ } E  # s* X" ^9 q7 r3 K# Z

9 A. d+ a9 c) P$ z2 l) F: d

+ o% n$ b$ e+ t, {+ n. Z. Q 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: 4 w8 A$ F, ^- Q3 b

$ ?3 i- [) B3 b1 ?

0 O3 J$ H* b2 q* J0 x; _ 省略...$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]);省略... ; _1 F6 t9 W$ E' d5 S6 D5 `

# R9 w' u: l! z& q

/ {# V; q) S; d# ] r7 r; u2 e 因此要利用成功就需要条件竞争了。! s5 `1 V% i4 b

; L8 K6 f- `. y

$ C6 [8 v4 p2 S0 o7 ? 补丁分析3 a& a. P1 U, ?: P( l9 M

4 a- @; ]/ `# z2 J3 s8 `

, F1 @( J8 p, B/ ~6 _/ L/ I2 F! D  , {% K K1 i" Y- F6 }: V

% ?! \2 x5 E% k7 R4 }; P

: n' V* o4 U& H5 s 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:4 d4 E* N* R s+ H

$ ^9 l y$ a, Y5 G+ t8 x

& p. P. q( I6 O) H+ c* G function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}* |5 y7 J q; a ^6 r5 V

+ H( i! H3 `( d! E+ Z1 s0 f

N! M+ {, m3 b* L) w- T `4 G+ b   # m) u5 z3 P$ U. Y

1 y1 R1 o4 X* p' g1 o( ^

K" @0 K1 P" n! \ 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。" m9 r0 W+ \. O6 m/ ]' @9 v" i

7 @/ h- a& ?9 n5 [' ^

! m9 K, E- A( t' u) @8 H* | 在is_allow()中增加对$this->savename的二次检查。# K" a, T; @1 [5 S) V( Q, R# T; [' S

5 B0 s3 w+ D6 F$ ?

! S$ Q* K& `* t& E6 }- J% v 最后* x& o$ {" e- H/ E( s$ b) V

$ [, L& E" K) O9 ~) O$ @

) t7 [, h& I- D7 M 嘛,祝各位大师傅中秋快乐! . I$ m% P8 e! J; i% E

/ x* R* g) |# ~$ P* l6 ?7 v; f8 F

/ R/ d! w' ]8 K1 N   # y2 R. r' J/ ?5 I, Q+ [

( s$ a' V2 f7 O& \) i8 _
回复

使用道具 举报

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

本版积分规则

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