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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
* V3 q( g2 @% c( Y2 g* K# e

& n6 b1 ~ L: c: g

) M+ G+ X+ _, ?6 ?4 l4 n

3 T; ^/ d3 N' S 前言# _- O+ W0 m q1 ]$ c

( T( P: x2 A' @ g" `

/ v( V! b# |9 j- S 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 1 _' V+ o- S1 f8 \

( \. k- C$ p3 q9 Q

! b& W/ {# c0 K   / f/ f5 z7 ]8 m1 d j( Z* d, t

; H& e. l8 o, p o$ q+ m

, B- c! a' o8 k9 G% Z 漏洞分析 ! g" e- ~( m7 q9 |! s# E+ a

- u' m+ q: D9 l U& ]: D! B) O* D0 c

& m5 o* Y; o- T& F# n' a' V 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: / T% [( |; O; P9 u

9 r2 \1 }# Q/ K- J$ v

J: s, y* { v! }4 M2 r/ `   7 t; s" R8 p& O4 \) w% s* O# J+ E

& `! Q. y' J S/ c# k n% P

$ J6 g6 l/ J. J( k 对应着avatar.inc.php代码如下: 6 b% u; I# ^! m6 g* I' Z+ F

, D! C$ z3 W/ h2 n/ S$ L: h. c

/ g) v+ O% S; j8 F* w, M <?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) {5 o9 [# D, @+ I0 k3 {# `! @# P2 T

( Y$ ?: Z3 H3 ]0 V/ S

/ Q4 C9 X8 ]9 }7 d" p7 {     case 'upload': 6 r5 {7 k8 ?5 y; t' a

, h- r; Q9 h" a2 F% b/ T

V( q0 ^& ] ~& D8 a0 q         if(!$_FILES['file']['size']) { , d+ `' y2 W" v$ k

* Z! e# Z3 @- f( C; t

+ f i6 N8 \( ~: x/ H) v$ _& i             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); 8 d# \% V1 J a! K2 W, D

0 c( Z- n/ m, f1 V N

2 }+ Y/ k5 `5 A: y! E, Y) A             exit('{"error":1,"message":"Error FILE"}');% d4 _: Y0 S ?

; [3 N2 p, u1 j# \- q. X

; I0 Y; }$ ^! i& Q& z! e         }. l* D. M: v, x8 h3 w9 K

$ c+ T8 w0 @- X/ \3 l ]) m5 [

+ y/ F3 [3 q- q: d# @) L         require DT_ROOT.'/include/upload.class.php'; x+ `9 G9 J: \: x4 \+ E

% s4 R% a W6 K" U O1 ?: `

7 G6 I h' w5 o9 P   # b. ?% g- G+ Q

" V/ k: m7 }7 y$ N& l. V2 ~8 D; U& Y( c

% u2 n E4 |# N         $ext = file_ext($_FILES['file']['name']);+ O' a: O0 |( `8 H# H! W

8 J# T# i: c* b* M1 M) ?& ^% _. j

: y2 M+ \, g2 X. k: y2 o7 O. Y8 g/ K0 X         $name = 'avatar'.$_userid.'.'.$ext; 5 P; P( x) p- i: {$ T$ T

5 b0 `1 f1 h2 B! E

: e9 T9 p2 e( n% @+ i         $file = DT_ROOT.'/file/temp/'.$name; 2 w% @8 b8 l8 A A% k5 Y; {* E

9 B% B+ X! H: r9 B; A/ O

: r2 q: i3 `1 z; \! m$ Y  6 [: Q2 U! e, d% [/ O

0 @+ T2 w9 }* O' b

) z3 q1 D X( O; Z         if(is_file($file)) file_del($file); + N8 W; u2 k$ z

0 H3 x; r$ d$ J! B% \: M) J

& t7 {! V/ a7 Y, K/ N         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');6 L* U2 z$ }& O7 _" O! t

% ]; q5 _: p7 ^% D. e, H* m) A

3 y' |- p Y! W! i  & ~- \5 w5 I0 C1 U" T

k2 v# w4 q5 a* u$ v

0 U _# K- ]% [+ ~- h$ z1 t; \         $upload->adduserid = false;5 g# h/ m( Z, N

7 ]! U& p( l; O' j) @

! d* n% I/ f7 G; _- Q  ) n6 m! n1 h5 q% Y6 Z4 w

7 m3 \0 s8 X9 Z8 j: ^

) U1 e( q7 }$ t& F* w( k         if($upload->save()) {: Q4 m R- k7 I! M# c5 z) A

8 s) _! @8 }& z& J4 i# p

* s* p0 ]2 }+ O8 S7 J3 t             ...* v& `. k6 Y5 E( D1 A4 }9 D

# b, m+ Z" z" c F) |, c! s

+ F3 c$ }7 g5 J) K1 K# y         } else {" G' ^! \% D7 A: b; u. K% M3 B

4 d, [& x. L" A, d% X1 a9 x5 L( [

( s! \' [3 V1 j$ H             ... , r3 u* y* E' ?( |. B0 w

2 i5 `4 D0 g8 k' ^

6 I6 Y0 K# L1 c" q% X: ]7 l         }% v+ D7 H$ }1 v) m' U- ]: ^7 D

* Q" c! b; U' v+ P

5 z# q, b! q; D9 T     break; ; n) ]( t7 k! ^8 R1 Z; E

! R' j& y N. E' q" q

( C- m, k! y" ]$ T 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。" K4 m2 r: p/ m# f

7 S8 m: l- s9 j" a) t- s

9 x9 G E; w6 X' H2 J upload对象构造函数如下,include/upload.class.php:25: ) u) j' d. W$ O z: L- t

3 R" M2 ]9 |% ~, @2 x. ~

( }3 i8 k. R1 w7 M+ h, X ^ <?phpclass upload {' d) I; X% r9 U

! a, ~' Y. o) l. o! j& J

" s" [; w0 f Z, Z [     function __construct($_file, $savepath, $savename = '', $fileformat = '') {- l5 t+ M) e1 Z/ N

6 `! Q2 D7 i0 |: H8 N- q

% E" I8 p6 B% i5 ]+ X. {         global $DT, $_userid;8 d! U! c" [3 a. q( U* u0 o2 q

; |) q% C d* p) o' T" A

/ I* h9 Y5 S: q( S7 u8 K" @7 @7 y         foreach($_file as $file) {1 K" J4 }! F4 k7 m D

1 _, P' L1 P! [+ ~( d# N

+ K' N$ v7 j8 G: F, [; R             $this->file = $file['tmp_name']; ( R: x1 x# u0 f

) a. [; O" C6 s: k5 T! V

; P% D- t% C* i9 Q& [+ W8 j             $this->file_name = $file['name'];: ?0 R' W$ Q* R/ U

/ J5 V! m N# Z

2 f2 q4 v' T8 x0 t             $this->file_size = $file['size']; 1 \% o6 X O, K9 v/ ?

* w, T0 R* ~# y# _; i5 ?! H* i

, @; z' n2 e4 |: w             $this->file_type = $file['type']; % C+ R6 K- L N" Y0 j9 u5 u. `

7 ]8 C8 S% R/ K. c5 I) E3 f

- @: y7 W/ h$ c* Z% p$ R9 G* |             $this->file_error = $file['error']; 2 A8 H a! d* H1 s

f* L( [8 J' X1 R# ], D

7 X6 q* G- E- t: \5 N$ R   N% e& x8 Y1 D) [

9 t; y0 J+ l" ~8 F

0 B* R" o$ o- R- H4 ?, h9 ^3 r7 a         } ' @. Y* S! A1 C; G) u

+ V" n/ d/ r( B% \8 ?6 {; M4 R1 S

9 u0 S3 K# `! p         $this->userid = $_userid; ( w9 F4 U4 \$ \* }# o0 V

1 T/ [ z+ t4 a. M+ t: w, Q

6 e- ~1 p8 L# P' A8 C K         $this->ext = file_ext($this->file_name);" g( c( R \2 ?& Y

% z- ]1 _9 b2 T! {; h1 [

# C$ o! D) |" n) E' Z% ]         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; 9 m2 ^, x) I: _! j0 O0 S+ f

9 l: s0 ~# S- _- a4 S1 M

; v& J7 G. U/ c4 ~: F& @         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;9 s* E/ F$ [2 A' d0 @+ o$ E1 f0 ]

! S" B) ]7 L7 c* v" e: s, D& a

9 q+ n2 l+ N: X9 c         $this->savepath = $savepath;3 ^% I- Y3 u3 y' _9 q, C; S

6 f: m x' d, {5 o: o) `0 U: k

q5 h X8 r% H) h4 o- I         $this->savename = $savename;$ G& N' x! {1 a7 d8 p

2 v5 [1 H& L# a' Z! D- u, Q1 V; k0 b1 p

# S4 `% J$ m3 S$ L5 v     }} * J9 m/ k5 w: ?" i. l. v

4 l) r1 Y& V' A7 W/ r+ f

& y' C; `2 d: J; Y: A 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 8 z2 z- }( {( j. T; L8 T7 U

7 i1 W) ~9 c, z( p# O3 S; s& `8 I0 c

. E- c& r8 C- S" M& L c 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 ) L! ?) K& D; H, N k8 P6 n

# b+ q) T9 y( s, F4 F9 v" G: x

- C# {4 L4 m8 d8 S! X- ? $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 * B7 M) P5 \; f$ S

9 X! C3 i6 @$ c2 V7 z# Z$ g2 j

# c- f5 P' q' M6 z% |. g 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: . u7 h5 ^7 J6 R2 F2 G

, @5 }& W$ S3 L6 n! [! \2 N

* v9 y9 ]' R$ e, s* O* y; X: h1 s   % R8 {; @; n' Z) e

: |3 h* x; p0 |! K: l6 V, C

! ~. u% l7 v2 G1 W$ a+ W2 o2 b 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:/ K+ z c9 k, U2 j6 D! R

( [) U' U" J4 ~, m

9 X; a* ?' f# p+ ^% {1 z& x, ? <?phpclass upload {8 o" M2 p" H0 O+ Y. Q

1 P8 V0 r- c2 |2 d

( B. S h8 H/ u9 p     function save() { 7 }) [% [4 a' S5 V6 L

; Z3 f3 F0 v) {' B/ Z

5 }' H7 e9 r$ A2 w7 _9 Z( Q         include load('include.lang'); , _; w+ ]+ f3 r

7 S0 x5 h7 q+ L

# a/ J* k j* Z" q# c8 ~) ~         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');4 f2 v3 G# U/ L

* K; d, v( P7 H

. f. R! t) A5 ~. Y$ Q' q   ) p6 R: { l( i0 X; f7 x

- d' ^3 o* B& h4 V) d) I

1 |. C. @+ _ w3 T: z/ C8 N         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');+ }" W" k% [, ], q

1 j. d2 g2 b7 Z) I. F/ O }1 N

- g& R. ~/ E% q% ]+ a3 n  % i, a R/ Z' u4 s: v

8 _3 v7 y9 C# S. j7 D! K7 E6 a$ b V

. E: R9 k2 C) d6 u+ f         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); ; \ r4 P. E) J4 _

; q" E: W, m7 W r; o5 j" q. q. ~

3 I9 W& u1 W$ K" K' ]3 _! [9 w   : q4 `$ [( u, c! \8 t! }: O! u

' ~# d/ Z5 j* c0 d8 Y

0 y+ n' p( c5 E9 k         $this->set_savepath($this->savepath);( M( C; N. [1 K* A2 }

9 _/ I( t9 `, n+ x8 j( h

8 l J* O, Z4 P# g8 ?- [8 x         $this->set_savename($this->savename);1 X* q A+ ^; J

, [9 L* |" `( n0 b6 e6 {5 A

) q, ~6 H, t6 F6 A   ! T4 Z$ I' ?% r% s; `" O

# U# H+ B" J" E% c3 E

& B" q) W1 }2 @; T, y; K# t0 b2 J         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); & D% {2 R6 z; m# j' ~* A

4 D# Z0 X9 z8 z- [% M/ {* W! l

4 C, _4 D* d/ x8 y Q! o' [" P9 s         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);8 ^ m+ n! m4 K) I

0 Z5 |8 T1 y9 _0 H. c/ B

3 _) ^- M' D1 F' \0 W; g4 H         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); ' S2 r) z$ T+ j) w

8 a5 p7 V; {6 n1 I

/ S6 M% n% `0 h& {- i; i, n   3 {2 C3 W. E2 c" A/ ]# j& S

6 e6 y" R+ ?6 Z

$ ~9 X% O2 v1 A: ^         $this->image = $this->is_image(); 3 Z1 A) m5 o+ |

z& L3 Z! \( C

! R" b3 {7 ~% ]0 n6 z+ S         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);: n1 X9 `# T0 P3 N/ a

! i; A2 L+ S+ Y4 M$ [# n

! |) t9 w: B: M0 ?- i+ G         return true;& V8 i0 \( X% Y" P w& S

; [9 }% D! C K8 \7 |

! ?- ?" O) ~( D2 ^" P- }     }}; U* \$ [6 W' o9 C3 Y* o

7 `2 v* X2 K& I( [

& t1 n. A4 [4 Q! L" O7 O* p$ W 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: ; i+ T* J( p0 \3 y" S9 m2 S

' s( p0 B! }: d5 t8 N* Y. ~

7 j; Y" x9 {$ c <?php9 p* L1 Q4 L7 o& ~! i- v9 c0 J9 I3 y

9 N r z+ a" U9 a

% f/ |# T) a, W2 f     function is_allow() {: Q4 D% \! v. \, }" s9 k3 u# Y

/ A$ o" n7 i! y% O% y( W% C: P9 b

N: E! X: e, L; ^         if(!$this->fileformat) return false; 3 w+ Z- b' k8 r

; z& u' i7 a2 {1 o0 e p# J2 O

3 b( g7 L8 W: C) z* h# r" d: `         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;, b+ [ t# o! F) r! g3 }

1 T0 u! c: [" @# e ~6 R) H( n

z. t2 ^0 W5 q1 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;( \: a6 l B) S4 j; ]

% [. f( V; _. b- u/ _

8 X! c5 W0 z) l! E: A5 S         return true; - R6 g7 c) w6 P6 m

3 V, o0 O& }4 P% I* J9 l5 M

; j+ F1 K+ T" W# M     } , e' T, }8 _+ ]" p

o; l9 ~+ A( u* Y2 R8 m

- ~; D/ \# j! D3 j# y, b4 s 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。& J8 |* c2 l3 K) k3 ?7 Q

5 x A/ z& j+ o' J8 ^! C

: |3 i% T1 B8 x; w$ G7 _7 k7 \! { 接着会进行真正的保存。通过$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文件。% y" `9 t. Z3 v2 }& L# Y

0 O+ x6 o& O# p6 }- k) x

* j' v: U. C" m" g- m2 M# N$ ? 漏洞利用% B! w8 w# y l7 n, `7 D8 {* T N

9 _% x) J8 \. t8 P

2 J7 f- V' T' C 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 7 p0 j6 x+ R' @* w$ ~# ~

* n1 b. i( W* b% y

$ i) q2 r, A. n- x   7 E! H% m ^: B

, K5 i, e( e! [$ L* A; @: ?& [, n8 D

3 u. r/ o( b0 f& {% U% [) Z 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid9 |6 d u( M, f% t

^" \* h7 _8 J

' C j, L4 N3 u 不过实际利用上会有一定的限制。 + D/ [4 S+ A+ K# n: F; z% _* q

: [. b; z8 T' `4 h0 P, ?7 X ~

! d# O; W* r& P: F, S: f6 j- l- | 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。6 W* | \- S4 a+ X# q, I6 p: l

, F7 M. a9 A7 s! C9 i( v! l

' n, s8 W( t0 F6 w( h) _- ~/ Q! F8 w  2 L2 ^! P# u9 {8 A# }% \/ _1 x

" \ Y% W' O8 t$ R

' n9 @. n9 B/ t/ ]/ l3 w+ j 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: 3 f0 i$ s9 E5 M

9 V+ s& J1 x$ R- A: q7 s9 g

5 s5 G L" u) ` g 省略...$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]);省略...: ^6 e3 u: A& I( q" D5 }

* e' S+ h8 O6 b/ a+ Y

" B- `+ m$ X' F$ I( K 因此要利用成功就需要条件竞争了。 * G( |: H+ O' @# O& ^3 H6 D

/ d3 ^& X6 |/ s3 h! Y' t, G

1 A% U6 i- H+ }6 z7 K 补丁分析 : ~+ P2 F" h# N8 |$ Z8 v+ p" x6 v% H

* r/ j7 e2 D$ r+ i6 O+ C9 O

; N% c4 Q3 }6 x' i+ K4 f   # g, s# N$ b5 L6 N' S! k

' V1 y) Q6 A( u4 ]& E

1 I+ y8 J3 N; W0 @+ z+ b/ q& } 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: : N' c) \0 u- S* f2 C2 {+ E

+ y7 \0 Z9 t5 ?5 X3 Q3 y( C

7 M$ n* a" P5 V: N1 c/ E5 [ function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} ) |+ y' X A6 j& `

* X: \! c: V/ n* {* k

, ~4 B* Z) m# x# X2 U o! p   t! m* N4 Y" X; @5 b

& y, [$ [. L2 H3 b

% V4 O4 J' Q' Q! P 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 2 Q: a4 Z" I2 R! n; Q

* }! d6 t6 l# W, A6 I+ W. _

' a( }, H( w6 L 在is_allow()中增加对$this->savename的二次检查。 9 V, j2 Z2 T' Q4 z1 ]

Z" c+ J# X, N% \7 V

5 H# s9 _& i0 j3 u 最后 `- V6 P! V' K- b7 c( K

d. ?" I% v5 V. K2 Q/ L; t

' k4 ]0 c5 |+ l0 x 嘛,祝各位大师傅中秋快乐! $ ~7 f7 f: |' R6 g6 d. o T& p% W

, Q; s5 ^& S7 G8 l

, s; @$ e7 s% K0 x3 m  5 x# S( `/ Q5 Q3 P% A; c

4 R& @8 `9 w/ y& Z3 y! @
回复

使用道具 举报

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

本版积分规则

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