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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
3 b- U d* z' [; [

% H* r# L6 C* s* F9 C9 s0 Q) |- A) w

* I9 t4 m8 c5 |

. T/ f7 [4 D* K 前言) W/ z# w+ n; A" j

: Q9 K1 q/ m8 Z: Z6 N

. q+ N& B3 F- X) W 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 ( f& U' e9 w: A t( X/ h

( s" B& y& i' Y

* w. V2 y) N- p K/ _  , T' Q ]1 d8 u+ o4 x

- f5 d s0 |% z$ _

& m1 A8 }3 {% B$ I 漏洞分析 8 s3 X# [$ P8 @, A

( P4 H; \- p3 T; g# G

: P) O: Y% ?" o7 }% W3 x% W 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:) J# }' T! I6 c) D6 }' c4 T; I

% K4 k, D7 t4 [( G

, o5 g2 N; `, A: A% m$ m( q  3 F1 @7 J' g5 }- G3 ?+ [

1 K) Y) z% h+ f* j5 U

" Q" E9 o: c$ w3 F/ } 对应着avatar.inc.php代码如下:$ {6 O* L: Z+ J4 G

7 R; \7 P- @( k1 y. F

7 V9 {" _7 G+ ] <?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) {# k& G( x& V" e: q- ]

9 f. X( Z! h; s( e" t

- }0 Q9 v+ `2 R     case 'upload': 1 |# ?2 C4 v, {) {

% D3 D; |1 ?6 ` i9 q' s

4 M4 U7 t# {' l$ R         if(!$_FILES['file']['size']) { % Y/ m l1 d7 F' Q( H/ h( `) V

' ^! P. e% P2 ]; F# ]! J8 j

3 |) |7 l% {* u             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);0 O6 e3 a- }) H! U

+ [2 g6 p2 l( q7 o5 {

2 r1 G3 j ^0 J$ M             exit('{"error":1,"message":"Error FILE"}'); " W- F6 l) c+ M6 p4 ]0 t! n5 g

4 G, l7 M% w- ?7 H: E

9 K' ?* j% F2 T& o/ T         }+ r8 Y8 t3 h& O- `4 `; o4 N8 Z

7 i: O3 w/ K; n/ p8 g! K; ?4 ^' W

: n9 ?7 g) t- ?2 X         require DT_ROOT.'/include/upload.class.php'; " U* q1 Y4 V0 k0 Z: P

& X8 D" D( o( E2 Z+ X. c0 V

! I" R/ d# I, Y1 B2 ]. C  6 e9 ~6 w, _" p# n/ R9 s$ r# f8 L

# ?' c* R0 q) R3 }* p; y6 L. Q

# ^% A. A8 d* B4 Z- F9 S         $ext = file_ext($_FILES['file']['name']);7 ?; ^- H7 m2 r8 ^

$ m1 p; ^& r- r/ ^2 [6 C; S% |. c |

: e) j$ @1 s$ `4 j         $name = 'avatar'.$_userid.'.'.$ext;5 m+ U5 @: Y$ g

- J p) c2 ]+ P+ \

+ _% z! R" a0 [8 V) L4 L/ z         $file = DT_ROOT.'/file/temp/'.$name;: V# I2 f: q7 z: ^/ e/ ]

. F! B' L3 ~# Z& B: g, I

h4 `# C( t/ E) C- u1 R6 I7 R   - K3 s' X) o$ Q/ C8 ?" Q

1 B1 {6 b0 U$ h* A$ o+ c2 l. m9 p

8 u( h8 L- u( j         if(is_file($file)) file_del($file); $ k* T3 R# W& b

8 t' C p) [& J

4 }2 O. `; A! Q6 U1 }         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); / P+ x! u5 L" ^. g7 D# o. [/ }; @

3 R# o/ }( Z) i; l

/ D" X9 s; Y. a$ w0 `- t2 g" S   ' q7 {) i$ U5 e( \4 y

7 c1 [6 d# U: w; k8 T$ t% L' n8 z

1 L1 g3 i" K' Z: }         $upload->adduserid = false;$ h1 U( u- Q, Q7 H6 y( g, Q H

7 D) ~7 g: e) l0 D3 i) T9 y ~

7 e6 @! l7 i( m7 C' P" u  4 u n/ h1 d# e( V

# s5 j5 F1 p7 U. e J3 Q

- G$ Q* L5 l8 m" \8 {% {9 N% o4 X+ D6 z         if($upload->save()) {( v/ r7 X, w. c, {

# ~9 A$ v$ Z* S7 ^

- ]3 G( ]( z9 g( z$ s1 F8 @             ...( v9 k, s: g- a; j. U

3 d2 O4 L. }$ q9 J3 r9 K9 z

, L3 a7 s5 [! }4 ]         } else { , ]0 }$ C! |$ E

9 y" q; n, a. P

4 _1 b( P7 Z9 L z9 f             ...( A4 k- {$ f8 N& P3 X/ ^3 c" G

( R% y. L* X( E( p5 S

/ Q% X' [; P* Q( C# \& a) W         }# y, |7 v2 V: m0 {% v

! m6 u9 V1 b7 O/ i$ @8 S c

% ?" B, \, F4 ~' W. g) s- x( b4 M& Y     break; 8 N, B2 C- }/ ]0 t* x

3 [; L, m6 M( G3 x" n* ?- u7 ~

8 t3 |9 Z. Q& }. V* y; C0 M 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。$ l" H* ~/ G/ S! m: \2 H

# f7 G' Q8 ^. U T2 M; E* k" B

% x/ ^8 z6 b4 o: Y1 y: H+ s upload对象构造函数如下,include/upload.class.php:25:6 Z' q. f) z @" n, ?! @1 k/ X

% w1 g# i2 E; I. {# L" ]

" l+ d: ~# ~0 w' D2 H <?phpclass upload {& ]& L& _9 g5 i; w8 Z# ]

( g, W# y7 C4 O4 D3 H

' m' T: ^6 e' P0 U     function __construct($_file, $savepath, $savename = '', $fileformat = '') {5 ?. C7 W1 E) w

/ Q7 q N5 A2 J$ z+ U+ t

9 {# b6 e6 r- K2 Z! h+ `         global $DT, $_userid; * D9 c! i+ ^- f* \

; {. x. a# A7 m: V* M! U8 [

6 k1 h, n+ S0 e) \; S: C) }9 m- d         foreach($_file as $file) { + t0 ~6 O1 f0 s5 N% Z" @

2 z6 | @+ A& W9 c

; }: D& t- r3 d- z7 ~# V2 r             $this->file = $file['tmp_name']; 8 |" y. M6 d) k# g' z

4 y3 U6 Q) B6 {. E6 x- Q1 x) t4 d4 a

: P; X' M% ?5 C. [5 K( E" i y             $this->file_name = $file['name']; h& v8 o' @! p& P T8 k

# d6 K6 l ^2 [0 O# j, q# V' L9 }4 _# p

- ]- I# l# y4 |5 `, V             $this->file_size = $file['size'];: ]% h! \( d9 e5 w, {5 k

9 n O( a& }7 g" g; Y- A

4 k" h% H! O7 R0 a% t             $this->file_type = $file['type']; $ Q9 ^, g1 }2 d/ C

. P- N2 S- O2 f" K% X5 n( `

1 {9 N4 i/ d. O2 N2 O: N1 J             $this->file_error = $file['error'];9 V! q2 n9 h( D/ [! Z

8 `# | V8 N( P$ `# C o- L$ |

, ?$ {" W- R8 f3 R7 [  + b F, C8 r( B N- g3 h. T: U

% q3 [7 L5 A, M5 ^2 D- @2 g7 m

1 N! A+ @- x0 Y$ ?6 X         } ' r) I% a, ?$ c) ?

3 X: N1 ?) f8 d q7 l

9 O/ B: k6 u# z0 x1 V6 E! w         $this->userid = $_userid;- t) }( u5 T6 A. A

" e8 q. ^9 t' t1 h7 e2 v( l

9 w! m( s. Q3 d+ W+ u3 x7 w         $this->ext = file_ext($this->file_name);6 o- E. P) g0 q' \: b7 _

0 ^6 J# O2 d% R. r& y' a4 N6 ~% N

5 d0 L# e" [$ @. T! ?         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; ) v8 l. U" G1 J+ a! |$ ?

5 d) M. D$ I* g! b

2 \/ }0 C- M' D5 K" C         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;' ]5 j7 _! a) h b. G7 S/ r* m; y

2 C- N; J6 a7 ^/ K* q

4 J# N+ ^1 R/ i. z2 C' T5 T         $this->savepath = $savepath;* m; ?. z. S& ~! B- W6 Q- n0 j/ S

0 x& U# b( c) j9 M

" I! W/ G" Y+ b0 ^* y+ t5 x! ~         $this->savename = $savename;* H, G" _. y: H- N* b

2 o9 Q# p2 I. O

' k W4 J2 i6 Z* K6 O: l) q4 ]7 ~     }}9 z3 Q3 }) }) n) \

, ~4 m" p) j: X3 b; V

1 T' O" c* }/ t 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 ! S, t3 h! q; h# Z' V7 S

7 M9 p! A4 m# E" p$ Z

( f. I7 M0 C& L1 U5 O, z 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 * p& W3 _1 Y& p2 ^3 E$ \& R

' p4 g0 }& b, N

4 F9 H3 L5 [) C& ?* D6 {4 F! I $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 . ]- B s( {# s( {: u2 Q

5 d( ~% |3 X3 X( |

' Y2 X/ r4 u/ f" s# ` 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:2 i# b& k9 C0 t, a+ d1 A

, K+ @0 A$ H0 X" p0 K

Q' m h9 [+ E  6 K8 j3 q4 l9 k8 R6 L

( V, O" j& Y7 o0 }

7 O& j# f: T3 w2 K+ g0 p7 | 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: * Y0 {; k% ]: K

% h0 k1 ?! f" `3 T7 |" b1 r# b

1 C% Z' [% z3 J2 a ? <?phpclass upload {" h; M9 i9 S* \ {

* Q9 N: q' b$ k/ _4 s5 I6 T) ~

8 L( H4 s7 P8 G2 Q/ k1 D: B     function save() { V+ {) Y" D4 S: r/ f: Z; ~

+ L* V9 B2 V8 R

) Y, s' x k3 C8 V. p6 Q! ^         include load('include.lang'); ! _+ N. D8 j! C. K

6 A, w8 {& U! h% z9 A

0 ^; h# V! {3 ^, m( O2 A. @0 K         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');. c w2 l1 V G6 p$ ~ G: x

' U) Y% ]; N" R6 q

' U* n8 t# z j, @6 }9 P& ^% z5 L  % A! W2 m W; i) E% a

& j5 n4 b# b5 T# ~ a

# G D0 b$ ?$ n0 o* |- @" A         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)'); + R$ X7 r, r+ D6 m. ]8 `! A

! G$ Q8 [2 l6 T' C4 Q* u* {

) |0 g9 n/ T' @& b   ) q l# @; D8 @4 ~4 F! B' R; m

% Q& z- Y# Z5 E/ }" E; |! V9 E ?! o

9 C0 q. ]% A( T: ]& {+ K% e         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); + O: J; m) e. w4 V

' C5 b! @ ]$ y# V6 U

6 {/ Q7 V6 o- L& @( |! _   # e$ g$ U. u M3 K% V

2 [2 \) s, E/ U0 f0 f( K

$ D8 a, Q9 J) \( `. r. X/ U6 F         $this->set_savepath($this->savepath); & A9 R& j* |6 O$ {9 w* s3 o4 [$ L

3 j+ D/ Q1 d+ ?6 A

( c% Y7 G% u4 a4 @$ A0 [         $this->set_savename($this->savename); : g5 F1 D7 v7 K$ i) n4 y" e

) g5 m3 H" P( h2 f4 r- _

1 X2 A0 g) R% g( n( j   : i1 L& M- q* \

1 e( b9 X+ b" f4 ?: j

) k! F* F0 h, x7 j. l1 x         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);" [- M# l7 }- n5 Z, c+ Q4 ]

$ s) k1 K5 E8 k1 S. a! y6 P# K* W$ O

( \2 D1 w4 L3 S& I         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);5 E! d0 C0 ?3 {

$ f+ T& X* }7 P. _, w7 y6 ^

5 R; y$ w$ D0 |% |: ]+ `* F7 O         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); : U8 [6 `) K: b0 T6 Z$ u

& v0 Q( X) J6 Q' S6 M( s

% a7 x: j* h+ u! Y! ]: G   0 o& y7 R" m7 W% _4 v

8 i: @ m3 D' r. H

2 z' f- o8 k- @) N" [1 P         $this->image = $this->is_image(); # ?6 ^7 V* l. L, q. F

( x6 s$ R$ q: o6 }3 @

3 f8 P; n9 Y9 P7 g- T8 Q8 j         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); " w( ~0 U1 e# R$ s/ B" r

+ ]; H6 g( X% W" D

& T) M0 P+ r) X; R$ B5 L         return true; . S C. k9 _* o

5 q' C w5 n0 i7 B: ?. b

! }2 z3 W, }! \( X     }}! B* B+ k8 [6 }+ `+ S. Q- E

) e2 G" f- j5 C c8 u: N

! I3 r6 g1 J2 u: r 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: ; f; x: p, W, I! l* [

3 r( N9 S4 I0 E0 s$ G4 q" }

c! s2 P- i0 k' J2 M* c6 c <?php( j1 P5 d1 v- J" ]3 R2 y, N

0 S6 Q0 ~) O- W* [- Z3 w

' I2 Z% J! Z6 i6 }4 @' h+ k& e- Q     function is_allow() {# R) B8 S, Q" S l

+ e; a6 C9 N% @+ h! v, ^

, X* P0 b2 C, {3 p( z3 @         if(!$this->fileformat) return false;! K' b, X, v4 k# g$ g! w

" w. L: F5 k, z G0 u) S9 C+ H' {; F

! @9 c, D0 S0 V0 a; w         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; 5 Q+ d) E" q$ ~) w, N0 H1 K U

& }4 S, a" f+ A- F+ X! L

0 n4 a u ^3 f3 n! l; r3 H         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;, ]& D. ]3 x/ k1 a. s

4 U7 t6 R% l% |9 f* w! z0 S

* @# A# C2 ~ L8 W% V6 I& E         return true;- p+ e- v/ X+ D# Z7 y

/ f& ^/ N t/ _ v1 L

- v l6 h# r- h/ h9 ] w     }. F- j# _% Q( g% A8 F W

, W `" l$ z4 B+ d- L5 L8 R1 T' m# ]

; A* T5 L7 Z% R( C! Q, I+ w 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。; L$ e$ H% h. R2 c& P Z

! o3 v8 d! W/ M; V+ A- _8 w

/ C8 L8 `$ q7 B3 G 接着会进行真正的保存。通过$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文件。 - U2 e# s: J" j& F6 E$ t5 r

. I% {( d' ]6 g1 p& ~4 ?

4 _, r! k* t* m5 V. w/ y& q 漏洞利用: c0 t/ q6 q: F9 x' ~

' z* y7 m& c. J3 n

2 c2 l/ z; e/ x- q6 o2 o 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。; ~; E, F! A4 j) k) u' S

9 {% ^* T/ g; @

6 g3 N" L2 } w, m/ G' j) Z   3 {4 _2 g) S' M- r

( ~/ n; W' X) H# `" f/ z8 A0 f; a" }

0 w# I( d% z z 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid 8 }" O6 E. A8 |# t

! O/ G; C" {& m% h, J+ y5 I

3 S1 w0 y8 n8 x 不过实际利用上会有一定的限制。 ) u+ g( f; h+ j6 l. t5 i

- V4 H4 Q; g: q, {1 B

& ?( b" Q. e$ ]& T% ` n. A- v 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 0 s7 M0 J6 x2 v9 t" [& H- z

/ g' v9 n. s7 }7 Z* d6 a

! [( N& r9 \$ c! ^) a   + ?0 B6 B2 _: G7 |

2 {& d7 ?6 q$ X$ s: u8 X

- ~* s0 F; _* u1 d9 } 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: , X1 ^3 j% B, D2 A* r- @" z$ l. B2 h* U

6 }, {5 E# m; M6 d' i K

# W$ S o. h3 R 省略...$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]);省略... + v' G% N( R0 P) ~4 C+ X$ t& C

4 D: x5 O8 G( d. ?% U4 q

( N* ^& O) c) p/ ?/ M6 A' }/ q- f 因此要利用成功就需要条件竞争了。 8 U% u2 L/ [/ \; c9 }& K0 z P

+ e+ w+ p7 I, X5 T! g4 G8 s: D

. `5 U/ _0 v- o+ N% o" N 补丁分析* z8 D c t, ~6 k$ b

4 x3 ?* l4 K* U! q% W8 S/ O

- P) k% b( r7 J) Q   & L* t. T$ Q* ]& A4 N) I

4 N# }& R/ G% [3 b

: W" i* t- G9 e( I 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:( X5 `. u; ]3 W

% Z* z+ k/ I! m) y J# [( ]* F

! C" ^9 x( H$ I s1 x. g* w function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}/ A1 z3 l% ?6 @. J8 _9 Y) {

! U- L( Z h0 M, O( O4 H

1 ~# h8 \# R' ^; d% ~   3 g5 s' C. W3 Y# A) M

5 r% p6 m, U7 m9 ^

2 b6 O. H& v4 ^+ u5 n 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。, h4 I0 F# N& f' t

8 J! l5 R$ w2 Q4 r; N

( I7 L- a4 l7 C ~) i1 } 在is_allow()中增加对$this->savename的二次检查。9 p; x0 u$ g. ]6 d

9 K# J' H2 P2 }; W# T4 W

5 \# n/ s& H0 X# U4 Y' X 最后$ ?' M0 A: k6 S4 f7 e

- a: @# S J: a

# q6 x, R# l+ d" p! l: J 嘛,祝各位大师傅中秋快乐! V7 O! e3 o3 P+ e8 {

8 n/ v; K2 k# N: p& x7 B5 t6 \

0 t6 x/ P, M, }% i   ! z* [4 i( `# Q1 L7 ^3 g& @

. `0 x8 X/ r0 {1 o- _5 b
回复

使用道具 举报

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

本版积分规则

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