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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
% O( u7 K; C& y, L& W

' V$ R* q5 `) P& S% G |( J' P

9 O3 V6 {7 ]9 `0 k

4 k$ A) n9 B7 ` 前言 8 R7 ?6 G% ]. m" n ^7 ]/ Z

; `* b; Y& N' W8 f# T1 \ I$ Q

# U) {# _8 }) K: C4 h 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 % g- }" ]4 d( a, W9 M+ e% g+ G

5 K" P/ p2 U/ K, I8 ]; z6 W

3 M3 z1 [# c: x2 w- }   + V% _: n/ a! p `+ \

- J9 k9 f; i' J7 U' X' C+ B, E

5 N$ B3 p) h9 g1 N5 r 漏洞分析 1 |1 o7 T9 l5 Z }8 k7 i( Z

3 W+ E ~: J. C9 _ x& L

3 s! \+ o Y7 ]0 s( r 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: , r! V# ?" ]2 Z" X. O8 A1 j; J8 d$ w+ \0 q

. o# y' w$ ^; D+ L* Z- a% f. q

i1 U7 ^! L M3 P  " g$ K& `) V$ o; M' z3 o

( C0 H- h: V. b7 J, T7 B) a

1 R' E" }, ^! _; ^- q2 b 对应着avatar.inc.php代码如下: , p) |9 ]3 E- h; |0 L# x0 l

: m* V, |& H1 O3 E0 v

' C& @) F6 C$ Z' n7 f <?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% z+ `( @( W& o2 W

2 `" B- j+ F( J- U7 i

# W6 S4 G3 {3 s* Z6 B. v     case 'upload':* _- ]# t' G. C Y6 L, m% z

/ F Z2 o) \+ P L7 R: `; a

4 y2 K, O2 I; ?' _2 X( W         if(!$_FILES['file']['size']) {* V+ w: \$ u s4 u& d$ {' ~2 d

; @9 O, S+ @! X- D

3 l/ ?2 Z/ B+ z             if($DT_PC) dheader('?action=html&reload='.$DT_TIME); " U7 y. p" M: P. u

b. b5 X8 `" k

1 S" B( T% T; `; @             exit('{"error":1,"message":"Error FILE"}'); ' {3 C- D! P6 V; B7 D

4 ^$ v0 R! z" y. S# q. I' o

4 \1 r0 p! I- w+ q         }) n7 r0 ^* S3 h7 F# z2 l7 q

/ A4 g, ]3 ~+ o9 F8 p5 B& w

& p- D6 ?9 S& N6 y) @, J         require DT_ROOT.'/include/upload.class.php'; 0 Y% k2 Y% h! a- ?& W' N3 ^+ T

4 t; y! {) W7 c5 k

4 b! F' d) X5 k5 k0 h1 l6 d( q   ) B* e9 y& q9 x! V+ q

9 \, A1 r% K: q+ L$ @8 K

6 A0 t; u$ a/ B# s" G9 E         $ext = file_ext($_FILES['file']['name']);( W4 L% D! \+ p+ d* g2 m/ K0 z

) {% d( w& K1 e

: C4 Q7 `8 r9 A& a7 S# ?         $name = 'avatar'.$_userid.'.'.$ext;7 ?4 y2 X5 ~3 I4 f

/ D0 ]" A! ?9 I0 d

6 `6 Y; d R. b9 e         $file = DT_ROOT.'/file/temp/'.$name; 8 h# k& N: V) q! |4 G

- }3 ~5 m* M$ U

* b: P$ I s. R& z  * T) K# d" |: z- x

9 {' F/ |. }4 E& s

5 X+ D+ L" ^3 a/ `         if(is_file($file)) file_del($file); ?/ q, `$ C! c

( o2 o# |5 H) }5 x) e& P

% w& @# t4 W( k6 M         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');2 g$ b: r8 d+ }; n& X

6 P6 s& ~, E: j4 o/ e8 Z

* B- @8 `6 X& I/ J: m" W2 Y$ X, B   ) `' a! A! D+ I+ g8 Y

1 }( x* L1 O7 f6 C2 ^

# C7 j0 S# m; ?3 Z, A( J( t+ M4 i9 r         $upload->adduserid = false; / S) |- b7 x: R1 x, a4 S

7 h$ F- f/ c1 J2 n1 O4 C

/ u5 _0 K% _' k+ t  ( G2 H& d- B1 [9 q" y" x

( Y; R* ~: K2 h

6 z" U0 N0 W& y1 u/ T         if($upload->save()) {2 H; l( P9 R! m2 n8 ?

1 _- [( c1 b5 ?

0 \% W& o! x1 d- n/ A6 R# P             ... 9 n8 b! y4 E7 F. z

8 V- M4 D) E# [

* w2 I3 T6 p9 J- _         } else {2 ~+ I& h4 r+ Y

& l6 |1 |: u. N9 b9 ?) i

) M& x/ g% x7 `: _$ z' m             ... $ H: c. s {* h2 p, s) a

J- k1 U, u- m5 ]5 u8 p

, \7 Z* [/ c; V. F, r( r0 D9 s         }: N7 o! s$ H) z3 V% A0 D

- I; f! E8 v, y0 G1 W Z/ k

+ W/ x5 s% Q( N( m     break; 2 q, w) }: D: X4 V, f$ D

2 [ a5 ?4 z$ A' x

8 x- R6 G- \' r- i' x4 C 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 3 v% e) G) k! ]) {' [9 Q! k1 {9 |

6 W6 o3 q5 K' h- `, s! r- z

1 z/ [; \" a" g; ^ upload对象构造函数如下,include/upload.class.php:25:( _+ X" n4 ]& n

9 ]( ]& G" y# ^* K

9 v- j& G' m- `0 D4 W: O <?phpclass upload {" H" {! X' d) W# H: s7 O

3 U& \. @ T9 K, N6 V% Y

$ m! ~1 C9 e5 @     function __construct($_file, $savepath, $savename = '', $fileformat = '') { % P- Q4 E7 r8 y4 ^3 U/ X5 ]

# A8 |$ J; S. \7 w$ W( D# ]" z

4 _- S/ ~) n) l" G6 O. ^, i, W         global $DT, $_userid;! W* J- d- M) T0 }

5 }3 a* T7 s( f$ ]- A5 y& e

" q$ b5 Q. o7 w$ E         foreach($_file as $file) { ; ]/ G3 B, N z9 x. c

! s) }+ s% j( r4 g6 n

7 k6 j) T1 T+ u! W6 @. |             $this->file = $file['tmp_name'];/ U% W& T6 E) w" l9 p: ~0 q1 b

$ Q, s. G4 l& ~ E8 ^" n

/ P5 b3 d* V" a" l             $this->file_name = $file['name'];# w, {, _1 s8 m

1 \7 P- ~7 v c7 m/ p! {% ^1 ^

6 Q6 k8 v3 c- S! ]2 n& b6 F5 Q             $this->file_size = $file['size'];- O4 A: I; k; l- O" x- [

" M, v }4 D% N* v3 n+ N% @

/ I9 H N' U( n0 T             $this->file_type = $file['type'];- m& ~( o8 O- s. b; s8 ^5 a5 A

m" k' _ s7 W! _

" W! j2 y$ J& u/ m2 x5 y/ t             $this->file_error = $file['error']; 3 K3 k) D) H# r3 t, f

9 d" s. l; |4 r% I# B

- Q. p8 J1 X4 q) t% |   + E3 B; }; C; E

0 ?% Y% T9 A. W* @

/ u9 l! M) Z! x7 Q         }0 ^! G# A; x) B7 L& X

: c& D3 S7 T9 l& W0 w

`7 q( S2 x! }6 q* q1 i         $this->userid = $_userid;4 C! @# ^' ~% q, m4 T

- x8 h8 M# @7 d! {" `! W" C

! ^9 J6 {" \- v1 O H! @         $this->ext = file_ext($this->file_name);; `7 R6 h2 ]: V

d5 g; l! `* ~8 l7 W

( m$ ^, [4 F% D' U# J         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];* v' F9 E4 S. `3 ]# _+ _

" t5 ~& u2 G- @; R

7 A* |( R3 \& q" T. s' q         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; 5 H8 V- F+ W$ [. P3 ~

5 _) _4 y: M0 }! a- y4 S9 l3 ~& n7 h

k$ M! \5 Z9 Z" T' ~# V9 t$ Y* Z         $this->savepath = $savepath; * r. W% t! y" l# N9 ^1 o, ?0 i

' p) u7 G2 a; `+ ^

. L% I4 C( Z$ _% n         $this->savename = $savename;# w7 a7 r/ V, F

1 f0 g* S; t" V# g5 v/ T" U# p

1 a" L6 X- j u1 B% e' E. G4 m     }}5 J9 l( y8 f/ _7 L I/ U

( t3 U: }' x3 y5 t, o0 f

3 ~; q6 ~: q6 N* z2 @" V9 t' H# H 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。( B8 G" x# @- g, v

: {6 E4 Y7 D: h" m$ P) y+ ^

! q) S2 Q! A( Q7 i2 j7 K 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 / b$ ^$ u) ~$ G; t" i

" P" T& ^1 Y. _0 y; Q& T. `

9 ]: q1 S. d+ r+ [0 G7 p $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 $ R0 Q, W: z. d- L$ ]+ x; a

i' a/ J/ u# N6 N3 z

/ }2 x* k- x9 b- K8 q+ b0 }1 N 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: : B- x) m3 Z# Z# j2 J, e7 P9 e

; P- M: E1 a3 E5 h

& p% Y p% V6 k# }6 s3 F1 ]8 i  6 _+ [$ S3 V% @6 n: `

5 g9 \# b; b$ B, S

0 P9 ?( [$ T' q5 |9 G+ E! A/ U 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:1 [" Z2 g8 J0 o3 W7 T# c

6 k$ K5 p" X6 h; K/ ] G

" S# W; K9 P/ w- v/ M5 r2 G <?phpclass upload { ( L9 Y& R3 o2 _) w' }* C3 G# w! v

( G9 L: q+ `) H1 A

) f, C9 _4 R! w! ?     function save() { }" @: T2 P8 W- W8 p

+ K/ |! N& E0 d. B0 _1 ~

- O8 L5 v$ r, ~         include load('include.lang'); ! [' D% B8 v, ~9 A( C$ w+ a

7 _+ W) M @' w# S4 i) A; Z

$ U# l7 P( N) {. X" W! w0 _9 Q8 i         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); 8 a1 Y5 j: _, m% D9 s

x: H6 _7 Q n0 s% ?& J

# x, E9 Z2 E( n/ C/ ]7 j" ]% [   / `* C! R4 }! ^8 b. u, o: e+ o

& T1 k& d4 b0 n N

4 l( _2 B$ u1 L         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); 5 [, F' W# J Q1 Y, M+ s: D

. E y& P" J2 o. |3 O

5 Y( h6 `! |' ]  3 Y+ T8 }) a6 w% }+ D

- H; a4 H i: |' L

L7 N, E# R h: d5 {         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);1 R: m4 e1 g. Y0 Y& c& ^' q) K

1 s1 I$ b3 [ p+ g4 P+ F& n$ }

. W9 D6 X2 i4 u6 u' k3 d  # B* s2 m: G" Q$ [/ i+ L/ i

1 H' L3 \$ z" m6 F. O0 d2 t

9 y% L2 |1 J2 _7 s         $this->set_savepath($this->savepath);2 w" @7 X/ a' P! z

2 t) E6 R+ X% C5 _0 ^

# v3 {3 h5 g0 S         $this->set_savename($this->savename);5 c# F8 E* ]) W. h& K

, X% M! V0 L. x D0 W4 g

s0 T# z. {" ?3 s  - `; [. n+ C- K$ p' ?+ ^9 q# b* C5 e

) \9 n% r5 y' ?. l0 u

, s6 C& k! z8 @, F; f' s1 o" |1 U+ O         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); % j6 v0 q) n7 L8 |: e9 w. p5 A8 G

& Y/ I8 |" |( K0 ]2 Y3 K

: ?& S1 O4 u4 [: A; h/ i         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); F' n' N9 s- a8 O+ B: K7 |0 E

D% B; d7 o. X

+ k+ ~2 k; K+ z         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);! z: J1 j+ x0 \1 m7 |

g& Z. v, {) y0 Z' S5 a5 b

7 _3 t; m3 K; }. W# |5 w& e- h   " L8 J9 r0 ]4 \* {( ^# @1 f

$ m; w2 _" L" Q- C6 v# x1 R

( s+ I. t: |% G' @) ~" Z% V. i         $this->image = $this->is_image();2 t& G3 A3 V4 U. F* D# E

1 s: U; b/ n# Q3 Q& f

! k: z$ C) d. p+ ?; n+ N; L0 b         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);! M/ Y$ ~4 j: X* o, Y

6 Z2 G2 p( H, E$ @. ^: F4 ^2 J

5 ]/ V& \& n& C         return true; 7 W/ X" X: z2 }" m6 ?" {0 s

- p. R; F7 F$ A9 D

' u0 d; [# x1 ~; P     }} y6 h# }6 k. L. d

$ g4 b( `! f8 `! S0 M# g9 ~7 W3 V F

2 q' z0 N4 e/ ~# n8 Q 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:/ Y7 P b1 F" v4 R( O( j

) e# A3 H% d% z/ T5 S! T

9 P4 k; U: ?( U <?php ; }- ?$ p% ]) @0 C) v( A6 O

) D- d8 G3 i0 K' @' \% ~

2 h2 N( C& W0 L. k3 z2 @     function is_allow() {8 F4 k0 R$ T+ U/ w/ Q- H

* k, V( h% c% _1 }+ }4 ]

7 v- Q* D$ `5 y! o2 ~1 @         if(!$this->fileformat) return false; & G) ^7 z( I1 Y- ~1 q

- E; P2 j; J, i9 y

" g2 B0 ?9 S4 |2 H& a) q1 J8 z( y         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;9 b E. v, t! \

8 m5 P B# Z, R' b9 x7 F

7 q, m7 n) C9 S! ] F         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; 4 ^1 i8 @0 ]7 [8 W/ d$ B

. f7 I4 J, N9 H' ~/ U

4 d- ~$ y$ y) k, ^+ D' H1 I1 S2 t         return true; # t/ W, m5 L# [/ I& Q

, i* ]2 ?$ w$ h6 c. a. l/ i- w5 B

% O6 @) T! Y- a/ ^. H; W# a" P     } 1 u" r5 W; u# u& H& p' q g

. d3 s% r# k. Y$ P5 C

) ~7 l1 ^( d1 X9 d8 \ 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 4 V- [4 x5 f3 U4 W# w

% u; H, O: d" e( @5 H" o4 a4 x

2 V0 j( _5 f5 J( X 接着会进行真正的保存。通过$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文件。& }! M' N+ V d% |; d* X$ Q

3 Y! W6 d' v0 S" d

9 T- J0 N) P- R) E/ ~9 { 漏洞利用3 _/ i% Y0 O3 x3 j: e9 K' I( `

) m, M8 y# }1 B3 X' e W0 X

/ m1 q6 K# O( s 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。& [& M9 \ T- \

$ N7 c8 @; `7 L/ P0 X

W" N( v- y" q! U- r2 \/ k   3 @$ U4 e5 T) T; C: c

9 e/ v3 @8 I$ ?2 ^. z: Z

! b) M; \' k8 L1 B% `1 W) P7 M 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid + p; ^* w% ^- Y3 c* v

* w3 @7 l5 s7 d

0 `* d. \! u/ t. H& `: K 不过实际利用上会有一定的限制。7 [* N/ e. \( p. K( P6 Q

$ h- c/ ~$ t6 ^( X: g" O* x1 n+ c

t# b3 ^& ` W# Z2 { 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。+ {# f) w a2 G! A4 K0 {

! E- l. G& I& K" D. i+ j

- Y1 F; X1 M& p9 U   . R' _4 Q5 Z; s7 {! V. s

( x- r- z/ i6 j4 Q

' f) {, v7 J9 f0 ^% o 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:" @4 ?# u5 n% R7 e

* J$ ]& [! x7 b4 n3 d, u

" D7 ~% j5 O& _3 Z5 X4 }9 h; E 省略...$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]);省略...4 k3 T* |' Q7 p

( E! Z6 E3 I% a+ E+ b

- q! u) Z! ?9 y- k 因此要利用成功就需要条件竞争了。. c; M1 H3 ^" u

+ d3 z1 U1 P/ b: U% d1 ]. ?

. f7 `3 R9 J: t7 F" d$ V. d 补丁分析 / h; T* c8 z6 K3 e

, Y1 S+ Y9 o* e0 v* u3 c% c! y& L+ `

6 a" I" Q9 K1 B$ }9 d4 S7 d   / _2 B+ m3 X. a$ _+ z( _6 n1 t, p2 ~- b

" P6 h' ?! B$ V

4 D$ y8 o3 V7 v1 M' o: D' N7 C 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:" t$ `9 _- S: C# f

8 y {$ X& N% M( u

7 J* ]! M/ S8 p8 J# E0 [) m function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} ( _. m F" Y3 C# N2 {# I

) m$ |' v( H! L2 q9 ]. a. M+ c

- `/ E/ f' M3 y. [- M7 a   0 F& M8 d! ]7 D- z h m5 `8 Q

& g+ a4 j/ C! Q* ~" v) c

( q$ n+ @* i% W1 X# | 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 2 u7 n8 y- L# }6 z" `, o

2 c8 Z$ ]: o" y' ~* D! W( S

/ ?# D9 F6 C6 }' c- Y, m8 N9 K 在is_allow()中增加对$this->savename的二次检查。4 U2 z! \. R+ m O+ J$ L( U

! F, Z6 z; O2 p7 Y

0 \7 T+ F. N, K& k4 g" s 最后3 x4 n" k3 j# B7 P

: h" u& l; P% n6 J; J! |7 p5 S# ]0 _

. d- v1 @! J& A) O$ M8 T& H 嘛,祝各位大师傅中秋快乐! 6 r- _( C" V) @% l, O. _

( a* y$ f9 [( Z2 D4 G6 |2 g

}; u7 y4 ^* z' I  , s0 o/ f% P. h; M2 P( M' n

# h$ U. q' I) o6 N- s& b6 y% s( D
回复

使用道具 举报

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

本版积分规则

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