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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
/ c" t: u- n# f, ~# \

, |, C* O, j* [* {

0 ?. d4 c8 Y# u. W7 [2 o, n

) u: _, x5 V1 Z 前言 ; ]2 n2 q& R) K4 F: G& a

$ y# ?4 t2 E7 a; H

6 E5 w/ d7 M5 v8 u2 W7 t3 q) v: \ 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。0 c. m8 D# e4 l. R$ e0 m7 O/ r$ ?

- Q8 Z! U, Y& P) n/ B6 s

n2 m) k! B9 }  : g: r7 A2 d/ n, M4 F; A* R

8 H5 {; x$ Y$ M, y2 L7 f

( J, O8 `3 u9 g 漏洞分析 2 E5 [3 l* B' T- w) Y

: Y+ i/ [8 i+ M _% y2 `" W

/ ^( `3 k; V$ i. R 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:* H1 y5 Q+ \) l

6 N& r6 w* d, B6 W

5 K* y) l0 v6 q  3 I* Q \, f) f4 [1 \

& d* c) [$ m) c+ R* y2 j* f

5 z: h' H" k, }2 \' v7 Z 对应着avatar.inc.php代码如下:4 w( s- i- K0 I% }% u s

8 ]! [$ [: [' l

9 X1 L2 b8 A" O# l1 }" p6 Z; L <?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) {" `( g- ?# e& `- \

2 d! Y- h) L: I B! D G# g# L

0 z4 |, H @0 i; b# i     case 'upload': 9 P* X: ]* Z$ A3 @. K& O2 S: I

9 W5 f$ h- B0 \$ g1 F

; Q" j' t: f, g         if(!$_FILES['file']['size']) {) G! P/ c. n4 g3 |0 |" _

3 y5 y6 p# d( w* y: T# t

- M& W' K; X! } H             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); ; I- F+ ^# ^' X( h7 G) d! O

9 Q& ]" F5 [/ S, [. I

- q+ g/ c- X- `% Y" u' m             exit('{"error":1,"message":"Error FILE"}'); ) P3 e3 T- G, T7 {( ]8 r& M

6 l9 N6 o& Y0 ~* s

" X$ z9 U2 r( n# ?5 I: k# P         } 0 J+ h/ o* n0 |# ^5 l. h- r

* h, y' u" ?6 t7 A, X

5 t- x6 h/ h: b! w* d! W         require DT_ROOT.'/include/upload.class.php';# t8 e7 Z% T# j! P: ^7 ]

6 A' g: S# Z* V4 O; l

6 o* R/ B% v n5 P4 r; J   # t* l* O/ A/ K1 N/ X7 w* T

, d# u7 [ Z. s) [

) Z4 \. n: W; l         $ext = file_ext($_FILES['file']['name']);1 h+ e! u. C2 Y, q, q! S9 G4 Z# ], f

& [# ~+ W5 S1 \ @% }& n7 V

4 d; Y3 k5 }# N. o$ k+ j2 A6 X         $name = 'avatar'.$_userid.'.'.$ext;, v- A8 O7 |# X3 O: c9 `

7 ^: `; e, o- z

% ]# @+ R' o+ C         $file = DT_ROOT.'/file/temp/'.$name;. ` h4 {! F0 a

0 ?; I( D. x# G/ D

" P9 m, o4 V+ q7 k$ S% w8 F   / G- K6 d3 [+ M4 b1 f' r, R

# p% ^$ H. m6 ?( P t A% u

4 t( ~" Z* K' I' D5 q4 K& n* X! }         if(is_file($file)) file_del($file); " k5 M/ U7 f- t! s6 F: b

7 A$ m0 G8 V; ^, `/ R5 [

/ [( R. f T$ P H         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');: U( ~- ^5 m" ~! X: I- A9 F

8 ? o% U/ P$ J- p% a

v5 h2 b& X2 U4 W* b4 ]/ e9 a  9 A/ o, }, I8 m, L3 G" L

+ z1 Q9 {% Q1 V9 F2 q' D& [

; J7 H" {: f& E' g         $upload->adduserid = false; + Q) S/ Z; ~3 B

( p, n! f$ F6 D/ [1 l& \

R$ L9 w$ W& W, V) W- E   . n8 x. p( W/ j4 \9 n

$ p) K' X3 h U# v# z+ x- @

. o+ Q7 C3 L: W' ~1 J7 o         if($upload->save()) { + \" f! d( J& ~) T( V6 @4 m, C

- ~5 ]( r! z5 r& k/ c! z

4 `) b7 a. z- s8 B+ p             ... D+ F/ Z" |; ^# z( ~

: R( d7 w+ n3 ?+ f# A6 f

- E2 b4 m( L& Y& s3 j3 j         } else {6 {! c& A% I% l0 f

0 s+ ?6 s# c* ]% E) A4 I

8 N' g& N# D. J% ~7 S, O9 {             ... 7 d5 t/ t" Q0 ~3 r: G$ {0 D

8 ]5 b2 } ~/ p

# p$ H- b/ p( m% t) \         }% _+ `( {* J" D

& E7 W/ y" m3 ~% p0 l* S- v2 `& Y

8 ]* }2 K9 c+ O6 p2 T     break; 7 s' \& }4 l2 E y

' }+ N0 O* R5 z" ~

/ f* K" x) f7 Y 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 p+ J6 W' e0 t* J/ w7 d: I

# {2 m6 m! O* x6 Z; H

# z, d8 o* e* h d! z upload对象构造函数如下,include/upload.class.php:25:7 ~; a# b9 P8 L) ]

8 H& V& _$ {* t+ U! Q% l% ]% I

" m7 E" j0 K& G <?phpclass upload {0 D: v f* n8 C1 Z; k

- }' l, h- n- J3 h1 Z, K( i

0 J, L2 W9 b! q" N H     function __construct($_file, $savepath, $savename = '', $fileformat = '') {8 W# b8 o3 m/ U! Z1 H: C

4 C3 K% V! b8 z. i0 G' C

; h0 X% a+ u H, u& w$ D3 R8 m* d         global $DT, $_userid;. W$ m9 o) l" l- e( F1 d8 K

6 [! @0 ^+ }6 Z8 k, L" ~, G

3 ^+ B/ E: U, t V         foreach($_file as $file) {1 D. e% H7 c7 a6 g8 ^

7 N0 a5 t. s( Y) q) L' e

I e: F8 ]+ e% @* y             $this->file = $file['tmp_name'];8 O+ g" L x5 ~: z( ^6 \! n

" J$ B& X! }% ~7 |

2 D+ I# z1 @) t& z* {* Z             $this->file_name = $file['name'];5 X0 |0 j# j. H/ }/ ^$ v" o N

( Z! t' a' K& [+ k R, Z' e) {

! i3 T' A4 [5 {- W% s6 T9 f             $this->file_size = $file['size'];* a" [: F, f& F* s$ _/ v

0 k- E1 e* z6 Z( k% E' I! Z' {- \# P# K, j

8 e% o% M6 o/ ?4 D             $this->file_type = $file['type'];; P1 S7 F2 A1 h( T% s" [' k- o

4 ^ G' t, ?, k& F5 T- k, h; |

# e3 V, g! K# C8 o             $this->file_error = $file['error'];7 W& ]; m$ N" T$ {, C. j

1 ^) f5 o9 ?: s# t3 ^) \8 [

2 j. X) r, i. I G( b: V  . {7 q0 S+ n5 |0 w

3 ^5 r4 H# X! d E: e: e

. J2 s2 \' F3 |& L/ S1 q+ a         }$ V& `, }; w3 `8 {4 o3 J9 j9 \4 _! g% f

2 Q- S" t+ O) @( i

( \1 }- d: _7 n/ u) u, O" k         $this->userid = $_userid;1 a4 Q1 Y% V( A3 _+ J) P

5 {# ]% Y6 g2 u$ l4 l3 g5 ~; @

6 |$ p# t/ `4 @3 }/ o+ [" L         $this->ext = file_ext($this->file_name); 1 U7 x" \- E4 z5 f0 f; G

- c+ w8 |$ ]" I3 w% Y

- ?* @' m+ }: T3 {         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; ) ?9 ~" N& f$ D+ ?6 N

& J7 k- X1 j) k: [8 z$ R. Z

6 ~' z% [" S% K         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; : D7 ~1 |" x4 C5 }+ A- L

+ \' _& |, p" R

" a) T6 x! O. q6 l$ Z9 F         $this->savepath = $savepath;+ B& V, H3 w: I0 Q' R( f

c; D& Y5 {, k0 H) C1 ?

k) p z5 Z+ o, o' Z) X3 Y) }         $this->savename = $savename; + c8 u5 _& Y& ^$ g T

9 I! ~0 {- T6 z

3 R& L( N1 T1 k& t8 J' z     }}9 c% f. a D: f

( n# t C- s: e0 I6 R. N( m

4 P& [ ?' z9 w% ]" A 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。* O! {9 E0 \5 [5 \" Q' l1 @

% N' D9 _" R, f% N

& x2 u6 c0 o1 M+ _$ @# g8 h; C g 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 / |* D. E$ Z$ S6 b

- I3 g( Y0 H+ K+ d- s; i9 j

. w! B4 ~0 w% B% d7 h1 R* o* [ $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' ] G* @# B

+ q) G, r& C4 O6 \5 S

1 M0 [# ]- u4 n0 A 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: # X( |7 f7 w4 S5 e+ \

+ P7 Q3 @2 D& @& R

- o( c0 }# w2 J7 e4 {" w8 x   c) \& _3 `1 m! V6 N$ Z, d' q

+ _4 ^' g2 Q! E( y: y

3 \& q l% L4 ~5 q" Y# E, [ 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: 6 _5 f. A* z. |! ]; @, r: [

7 J- e: H: K) g: _

8 [3 y. s6 k- @ U) z5 @ <?phpclass upload {4 P. x& s0 P2 `1 O# R3 b. T" p

# _0 ?. }5 [! t, U9 A& {3 ]. @

. F: w, p& y$ ~2 n! x4 o1 O; V! l k4 z     function save() { 7 ~& s+ b% `9 \4 K& w' d

- @, F/ ?& w8 [$ L' O; s( {% y/ C0 o

9 `: ?/ E+ } K5 b# g         include load('include.lang');" U6 F( W# q% ]& [2 {" d2 j& j

! b Y' b5 v. F3 _

9 f. U _6 L1 ]/ \, j" d         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');- n0 x: J3 v+ Y( j# e5 n( ~

- N7 ]+ j9 f' j! Z7 o9 Q9 Y

- Q0 W/ ?$ _8 D! q( E  7 j0 a! u9 g& b T' s

% q+ f; R& R0 {& g

6 C' p4 s: p8 g, K7 K2 R         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); . q+ S" g1 B" A7 K5 C

! b/ h) E: K9 h0 \

8 }- g9 P3 q; F3 M) ]8 A1 }+ p  , ^! ^/ D& S/ n! F0 n F* ?9 y8 P9 p

; a# D d. C1 B1 N8 m# F9 H, S% J

8 m$ _1 E+ d! m         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); 0 b1 d! g: H/ f) k5 H: R. X

3 w4 R9 f9 k* g

5 Y. D$ t s9 x4 v Y) C; J   7 [( j3 J/ S% k2 o! M5 ]

! q! n5 |* D* U1 P7 ~

- r& z& H8 r3 T! l. L         $this->set_savepath($this->savepath);* ~1 J: y$ H P1 v$ n# j( K3 R

0 `$ q3 J; n8 h" p: f# \

~7 w r) ?2 \/ D0 j( w         $this->set_savename($this->savename); 0 j, o& O: ^2 Y6 u* }! y

) N- B7 i" _* |9 o

! b! l/ l0 e0 E" L' u  ' {, v1 O, I8 X! B

6 G( h8 m F, o+ {! g& F, j8 ]% l' T

$ G; o: B/ t: ]* \* c3 D         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);: F. C6 K- j, D6 h

5 ^% i- k' g4 s4 x1 l3 o g

9 e6 K; l" W I( G6 }! K5 g         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); : g: G9 t: M; i$ H, q

* s6 w/ o! o9 C/ N* j: k/ d, i, \

$ n6 _6 Y: l# m! x( h- {6 t         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); . x3 a |+ I! V6 N# s. p7 ~

6 Q9 H/ s1 }: o2 Z& A" T

4 P: F0 b5 T7 U0 f! Q  2 V3 _5 X" g" J

! m: A+ ^* e X' J2 b

5 [) m5 | E# ]7 ^         $this->image = $this->is_image(); 7 @: n6 M% _! f" {

3 [5 g8 b \, C

( }' l, E8 R5 H" O+ c8 g         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); - ? g( ~1 C' p6 A

! V) g [8 J9 E/ k( E; e1 y6 e

1 G$ S/ c1 S* U6 Z! A         return true;2 I, {" f6 L2 K/ f7 w" W7 N

' b9 h: I; ~$ r% l: {

6 G. r( ~) E1 {+ K, y$ X+ ?! ]     }}6 P* O" r+ X/ L% p1 {- D

! Z" Q, Q" A# o" ]

$ d. W9 T7 `1 ] 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: + W' R" n' P2 D, C* p+ z% j0 G

" o, ^: K* b9 h* T, a; R$ I2 O

- c3 y4 K0 H4 Y' K* q0 O9 Y$ H <?php 5 G! }: ?/ @4 @6 T& B$ j4 i& r3 M) W& }

% m: h7 R4 |; |7 C

+ \4 a& {/ f; h9 [) y* ?' L% O% m     function is_allow() {5 K8 _1 g$ g6 X; G# n8 i

) B4 `9 c S% I. P0 f8 R0 S$ y/ y5 T

, }0 l" j; J+ J3 F$ n9 |         if(!$this->fileformat) return false;3 ~( W. c' Z- I1 N; {

" N5 e! q; I0 v7 c8 f; _

v% ?- o) [( X% L/ d' X2 m3 j8 L         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; ) B- t* }4 p( l2 x, E

, w* W" R# g7 [- P

* \$ |" R: r6 v2 W( H, g- Q         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; . j0 P9 N; O5 q* E- z" O

% ]4 W3 E6 O6 ~+ F, O+ z

# u4 U& ~& [& U$ ^" c, w. Y: }         return true; B" @) D' @) t; o+ u

6 w0 ?4 v/ e$ O& e8 I1 c- _) z

8 K# n. ?5 o+ W. A @     } 6 O& R3 C# c a* K$ T

% L& C' O0 @ `: ]; w

* h3 D" y, @6 K- o* h 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 0 V# R* s# m1 B. g8 m

' G! H, d3 P* r# W) W4 g% w$ {

7 J% k8 @4 m( a0 P& N% D 接着会进行真正的保存。通过$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文件。 + C m! ^& e3 O9 D3 `

' q# m! d5 d {; v* ?# L1 {8 t

2 z8 q0 q. D) N, h) L' ` 漏洞利用9 B7 p' n, S5 M% k- A( F1 _

5 ^* ], N1 z: U

: A7 n; V# O1 M9 c# C& r 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 ^3 J$ }; H5 F5 ~$ w8 L$ g

" M) P; M) i0 T$ \) n, O' i$ R

1 b2 y* K: U# k1 o! D   , N4 [' J3 M, {9 X% A

3 }/ K3 V! ]5 R1 S( H; v% K3 q2 I$ R9 ~

. B Q# {* h' z) g( x$ y0 j6 Z 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid( S* o6 J1 ]5 d0 Y9 b% C2 }

% x4 F; u9 j1 c

# A: {1 e' w3 D9 [# @9 @( `! u 不过实际利用上会有一定的限制。 ( I; @* X! d( t

4 ~+ S- T! g& e. S. u% [: l4 f

+ K( p% G) j4 O1 y) q 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 ) T, [& h2 { k. H' \

4 V4 b$ {& u3 K3 d' m! d

) c& K1 J1 G) w' a  ; J" Z- H3 e- z `0 X

3 @/ M8 K2 x/ m8 w

1 _# O A2 s. q- |7 G7 {6 ], W 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: 9 W3 u/ ~4 O/ c/ l

5 @. P- G9 a ~0 Z1 n3 ^: F

/ q1 {7 h$ J2 z 省略...$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]);省略...2 N3 X! v9 ?& `( e% i) g' Y: z

+ t' O) w: t- w6 H! i& o8 w4 b

2 b: W. [, Y5 B8 d: u 因此要利用成功就需要条件竞争了。 B: E" U8 e# h. C* `! G( j) s9 ~6 o

$ ^2 s9 N+ `* n% y/ V8 N0 P- H

* n- ?, X- B7 M1 e 补丁分析 2 |/ V4 D4 \7 J% @# @6 t% a

% t$ d+ _* d, Y( \) ]

9 `9 W- z% v* V   % z% c2 i; Y, D2 R6 k

& o# j0 T) E {; F$ _+ t8 I

# w& [1 t& D2 O# D- z 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:6 r, P+ z# B) {. H" b4 k- `6 f9 T

5 q" k% _9 Z7 u2 H

: X; L0 D: L$ ^, J/ K: p! r2 A function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}7 `' e! D; p; p; S6 t* A! ~1 H& ?2 ?

% k. R' t2 l8 K0 |- D

- k, @* v. Z4 f9 @# b  ' i2 ]. n+ j9 D

) |. F# ?- {% D; l h3 l

/ u6 D; ~; X3 b$ V 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 # O7 i: d6 C3 i6 V3 S

' a {: W" J6 \7 E

+ f) Q! N& |4 x( i9 h+ K: t 在is_allow()中增加对$this->savename的二次检查。 E8 i, u+ S j$ P8 ?

0 H: Q$ f8 {$ X7 g8 y9 L

4 ~& U! r/ t1 [. o5 [ 最后 9 a) V" g0 x8 }8 k) r

* o' R/ c8 L) P

5 [6 Q* C! S( g* c1 D 嘛,祝各位大师傅中秋快乐!9 I$ {9 _, x3 F6 R2 { @) Y$ z A

1 Z9 A' N: _/ w @. J1 q( c) y0 k

6 R0 T& l4 F& E2 p; t, |: i  / W0 e. @" x. T# S( w3 d3 r Q

* l) R& K6 S. f( B% m2 D, B) m& @( `
回复

使用道具 举报

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

本版积分规则

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