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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
- b& I/ V+ Y9 l( O

W# P' F: _* P6 T0 V1 p3 E( \! U

$ C8 L6 P: Z. Q/ ~& E

* n9 v/ o1 } b F' A. _ 前言$ M+ d: Q, A$ Q% u9 m0 E, q

8 y4 f/ @2 \9 u) w7 r

$ \: |5 l2 l/ ]) ]$ b. | 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 ) r8 Y; \) C4 o

% v4 |2 D" H8 k0 G

" u; I3 P5 j5 f! ^8 Z2 c, A6 n' S   ( {4 ] [% k2 g- Y, T

! j9 M1 \9 r, ~9 Y+ R0 b9 A0 w: a

0 q- R. z% V$ ?( H; c 漏洞分析 9 F. l) x3 X; X9 t

1 ]" o9 G5 t2 H7 }3 t

7 H! H3 X, b" j K" E! a7 Z 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: F" K A, e* o( n: N

5 w- L7 b4 d# c# f

5 N5 B( i/ c' G   # e1 }* Z4 V& @( u2 Z+ ]7 i

, u6 M: `- `& `

$ Q# J2 y7 A/ _" V 对应着avatar.inc.php代码如下:: K8 A4 O* j6 c; [

6 E3 }) p8 \1 p. Z+ k! ^

) m3 X( }) T0 ]0 V; d <?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! J0 c/ l: F* f$ n1 u8 S6 J8 b

# E$ K. `2 E; k( t* b+ w

8 H, `. q! J; L) l4 u; J6 M     case 'upload': / g5 `" f+ b. W3 M

8 m( ?# ?2 H8 s# y' |: n2 x' A

" Q- ~5 H6 q4 D' h& j         if(!$_FILES['file']['size']) {& F! `; x! _! s F' K

1 X: U/ v1 B; M* P3 c/ K' |+ B/ m

+ G3 u$ T, |4 q2 F             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);+ e1 U* |/ x0 w) Y) c: `/ x$ J

1 P, Y7 u3 b/ V. J' l0 W8 u2 r

, @! I+ ~8 T& b f# K             exit('{"error":1,"message":"Error FILE"}');5 i; i. {/ X4 x1 T4 f& a( w

* E" A# F# G) l5 A, m6 `) n

* x e* u+ [7 S% `" f         }2 O# u: K# D( A( X- M

8 o) _$ r- Q# j2 o

# e+ C2 w+ E1 Z2 b }$ v         require DT_ROOT.'/include/upload.class.php';1 E9 ^/ ~7 Z$ W8 j7 {7 y

" {6 f# p4 B3 w% Q0 ^: T0 e

3 z* T1 }. I- j) o- q j  : C$ F6 D$ `, k' P6 f

. Y/ ^, H7 P; X6 D3 j9 k

K7 H( E' @1 p; _' B* y5 o         $ext = file_ext($_FILES['file']['name']); * G1 i; D6 B w J

& S' ?4 i9 }3 d$ e( Y1 X

; u7 M, r; M; J5 S; @+ a         $name = 'avatar'.$_userid.'.'.$ext; ; M+ a" y |- t2 P

9 W, y2 b! u( g, M- x" T

8 @# z* F+ }4 D         $file = DT_ROOT.'/file/temp/'.$name;, u# F& N6 J# P) Q# R% U8 V% v" ~1 s

9 Z2 \$ ~& H6 {

7 |: Y' M& u2 }0 F( m5 \  . V5 D, ?* C6 z. m% O

' d1 |5 l0 C# O5 |

% l1 C& x8 }# Y# ?         if(is_file($file)) file_del($file); ' @* d$ e; m2 D4 c, }. N

1 i& g1 o' M2 Z v9 e) ]* _! N

( C- o. d, Y( `6 K) v9 \, s         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); : I" j9 S( X/ ?* g

6 e" D& ^. D+ m; R8 b' {2 ?

( D1 L% O1 C7 k  ; e% P: V h- f# t7 r

# ]* c' V$ u6 o$ x1 a! I2 B; D

9 ^2 B, R" o- ^" }         $upload->adduserid = false; 4 U2 p+ q; b, M8 \- Y

* n* v" b1 k2 }) X. [

% ~9 \* h9 X' j8 Y; ^- k5 j3 k   * ]1 W; r8 i( D/ ^

1 T: R# m8 ?; J- q

5 C: x$ J! p0 x" d* ^         if($upload->save()) { 9 h8 ]. ]- z5 K2 r. Q

4 h4 p+ o$ H9 T

) v8 Q: }3 ?* i- @             ...1 u. l8 ]. _/ q m+ a3 T

# D& j' o- O+ u/ Q3 ]

" Y. F! _2 S; N! }. ?         } else {$ p1 u& Z2 p7 { P2 ]

# W$ o$ d% y6 }

2 {: v9 n8 u: |* q$ i             ...1 ~% | Y% n0 F! c7 J

. \7 B0 b. G( B! v

6 W; k& G# i8 F+ D         } o1 j, i8 `, |( d: T

f- D, K' s- A8 [

$ z+ Z% r) t- y4 q, h% x( a% k/ g Q3 Z5 i     break;# t1 C0 ]- j; n6 V* H4 K

& e2 N7 \7 K' Z& j( V

& U* `/ J; x' M4 j2 j. R 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。7 R, B; L0 t% z8 F+ B+ [

/ T+ L* Q4 x2 ~) Q# C

' ^+ [& [& H. C9 r9 i! Z4 M upload对象构造函数如下,include/upload.class.php:25: " F* D8 i! ~* F0 d

0 y ?8 R6 f V z* e2 F5 D9 M

3 O; h& e+ N$ w9 S% }5 N <?phpclass upload { 0 Z. S" \9 G8 J& [) @

3 t6 a T5 @8 Z+ R7 M' G

/ M- X5 I* |/ u* X7 m, [, ?     function __construct($_file, $savepath, $savename = '', $fileformat = '') { ; |( ]( s; U' G: B

+ Q3 t4 S8 N6 O5 p: ~- L

' T K# u3 C' n2 c+ Q( k4 g: G7 ]$ v         global $DT, $_userid;6 ?- \) x; M2 m/ j: {2 q6 o' l

3 N2 t+ a) \ A! ]$ r

9 G! N: Q! g4 x) K0 z/ g: p0 t         foreach($_file as $file) { 7 Q* |, K+ g0 V5 P; m+ \* h. Y: J

1 u0 {" r8 h2 u! }& ^! L7 O

$ Z8 V2 N: h5 Z' N6 u. D6 _0 z3 V             $this->file = $file['tmp_name']; % k, R! P0 w( ^" d

9 l+ }9 R, I- V3 L( e: y. c! i

+ l7 }8 B( S5 g9 | u             $this->file_name = $file['name'];1 A( v" q# N& M" U& Q3 |% \

8 e3 ^/ T- h- P. G, [0 ^" A

6 p0 q. f8 u/ [$ B5 M             $this->file_size = $file['size']; $ M- C6 w+ @, C5 C

! }2 B6 H' C( P2 ?' o

" Y4 F6 R2 H; q             $this->file_type = $file['type']; 5 M3 h j3 O5 }9 m5 Z0 {

4 b, L& n0 t; x! |

: e5 j% N5 w8 l( l" Z9 R# [# l             $this->file_error = $file['error']; 5 S( `$ T+ c N* K* L5 t# H

- V q* M, M6 d* d/ Y9 A

4 J. ]2 M& O; y   # |6 j: X* v' t5 W$ x9 D

; Y5 h2 ^& E5 u1 Q! W" @0 B

" `+ m% z8 a, H! ]         } " v& A9 z+ M8 q

( C* @' X" o( u: e7 b( f }$ x" u

2 ^' O8 l+ p3 }: y! i6 z2 N- Q         $this->userid = $_userid;- H8 Y/ }3 @& d, f: H3 q

& o Q; }- t0 C p( W

/ r$ ?2 {! {0 n5 \; B         $this->ext = file_ext($this->file_name);8 N) b3 g0 g( x' J5 i" |4 O9 Q

5 w8 }* j8 y1 _9 ?/ P4 V

c2 D& T2 ?" @         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];4 v* n6 I) y, s f* _ V: X0 m/ ^

: c0 x6 o" a$ y

6 @' D2 f9 |( r" V: e& N# j5 B& Y) [         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;* m7 g( k* s/ H

) E' @8 D8 ]5 `

8 m9 x3 X3 ]9 @, |; `         $this->savepath = $savepath;- q5 r x7 {: K! ~) H

5 f2 a( c- w/ x s; g7 [5 A7 H- y

+ n* g5 A: J) n& M- r- Z0 U         $this->savename = $savename; 0 b, g* ~+ t2 I( {+ B

1 i" N6 e7 V- L% [

! |8 E; D8 f6 |     }} }& b: a2 A+ s8 {( i2 Q

X% t! m$ v) X, W! {

3 F' _: O* Z0 w8 P2 {: a! A1 W 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 ! n! H$ q4 c) | o! N& l/ K2 Z9 B

% ]/ C7 l8 E3 L

/ @4 F6 K8 ?" J' f2 Z0 Z& k 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 $ o" N3 z6 H' ~. D9 T0 x

7 U0 \/ w1 `: l/ G4 n

0 p$ B6 ]2 g) O" H0 v, I, I. v f $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 1 K7 f% x' a p

! d1 ]8 p+ r. b+ O8 }! {

" c+ B+ |& A! |" B 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:/ B' u3 n" D" }. Z( U3 h

" M) f5 V1 n4 L5 S b" v' x3 W

8 V% K3 z2 P" t7 y' @  , j& \8 [* [3 F0 {

" y. u8 B0 i- {* U: f! b

, `) n# P8 g( i6 N! b- K 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:2 D4 y) M1 D$ H% }! ]) j

, f8 Q0 i, o& T3 O! T

7 }! B# c& d4 j$ V3 ?- Q0 _ <?phpclass upload {8 [ z& x6 u" m) `& k

) a, F! o: x6 O ?6 ]0 k

* l1 p! }& e4 g5 `4 F% d6 k     function save() {0 W4 j: S( Z, R" G

6 K* n2 x' ?' J

* w+ R. s! A+ b& m8 K5 u         include load('include.lang');6 ~- w3 {: Z$ J* s/ ~+ q

8 u! y( O b$ L6 R

$ V5 S4 u* J4 K, k% o         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); ! C$ j4 z! O: c! i" |/ b. X5 x

" Z+ g+ m8 Z$ d. f, `" \1 Y! ^

; }1 Y2 U: _0 f& J5 T6 y   : y: v4 R1 C- G% i( p

" ~% J1 `, Q# a! q% [

9 Y; y+ h! M4 V7 ]7 l         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');( z1 @3 e' F4 a6 ]0 k

7 p% }$ y! R& ~2 c# f

7 Z! c5 [. _% R" j6 Y' N. |  + x- D9 Q7 T$ @6 ?

" L& o. b- j N6 x3 X

- X' ]7 R; K) L+ F         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);2 X3 _7 m+ |8 T. L5 v$ X

# d' k) P. E1 W7 Z

( y* f, c5 T; q& E7 r! l3 M: C# Q, c  3 ~+ q Z9 L- `" B, u6 z F* G

6 r! I3 c, j: I& O3 e3 M0 r7 H

& q t5 N$ g$ \# S         $this->set_savepath($this->savepath); # s5 @; P4 R% \4 @. K# v8 U: k

& l+ V7 S% [* w1 {

8 I6 V5 C: [4 e7 o8 e/ d         $this->set_savename($this->savename); , @# Y, o( l. [! Z

& u1 {% e4 O6 ~2 v H- H# a

* q9 m% O- H* m, B   o' X$ O+ A1 v c8 U5 o) c1 W

* I6 q3 M! Q! s

" j b/ C# s8 t9 K. i6 ?2 u6 I         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); / ]9 I. p1 B5 m+ i! l( i

2 J x. f( T8 c2 G

1 h0 n% _7 R5 y' L) J; h$ i: x         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); 9 D; P# p. H5 s' J

' B8 E3 i' Y/ E* Z" t3 @

% w6 [( `) F8 f# g! ?7 O, \         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); / V9 X- D3 d- Z5 v% e

9 |2 o6 H3 M {/ Z; s4 A1 k

7 M3 r2 V2 P+ u% }4 ?9 r   " t) N1 d8 y- H) x" S

) y- c. @! g. Y

6 E( n" G5 T2 y: _: e* i5 B         $this->image = $this->is_image();- q1 f' F+ b& B' s" w" b6 k' c9 J

* k2 h# }* o1 l8 m* ~

- v7 [2 e' {' m6 o+ p+ j         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); / V6 D, z l9 x- L

! y) i9 E6 U% P t3 K7 j5 l/ G5 u

1 y1 T) G: E+ ]$ q         return true; 7 B, W* {: l; g0 w

1 K' i' s5 N* H8 y" d! u. M h# k

4 d) Z0 o; j* j1 {     }}, |! B( y+ R) B% k0 }$ C4 W2 w9 g

& U- d0 Y. h/ ~: H& f4 ?) K

& d$ J+ A& a* ]# w/ _ 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:7 ^- `3 f- ]7 ?0 N2 i" m

" }' G2 q( l6 G

! ?- {5 k% W: p. n <?php " H& [ v4 S) W+ |

+ A8 J5 m+ R9 f. G. X

7 H6 ]* z) P7 ?2 L1 Y! V* M, e     function is_allow() {; T. g2 v+ R, @. L4 N9 {

7 k( i8 J& B# {/ A; s& b

6 a% [; N$ h9 v/ {( w% O         if(!$this->fileformat) return false; 3 L7 e- G1 g: R! X- U

# R2 z- x2 X7 D" I7 Q2 d8 g) k

+ g( u8 v+ v# n: w6 u         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; 0 _) x: J1 S2 `( y) t

7 ^5 Y: e" T! p! C" x/ W1 k

0 P0 g. _3 R7 O% p: i         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; X9 J9 l ]0 p8 Q' {' ^2 g3 J- O

! l% M$ U4 b3 q5 x/ [2 p1 R

6 O2 v0 i7 ^5 a. F1 s" Q         return true; g, a, k2 v. i0 s3 B8 Z- X) n

8 k8 R) D- {# o' P7 ^: I; \; m

! X( j& `% X8 ~2 s7 k, |* |     } , x- S7 v1 x+ J% x

2 R3 }* U- P+ O" Q* q

0 T- Q. [* d! G+ U% N& ^1 X0 e2 S) \ 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 0 G+ M6 P) `+ b1 c: \/ G

$ i% g- \5 @+ ]6 D# k

! _ S2 W! P0 L, o: a% ] 接着会进行真正的保存。通过$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文件。; n3 \- F( V( r5 i9 w

. n; O# Q& W ?9 k8 k/ _

9 i1 D G9 M. E# c i2 Q( M 漏洞利用 2 w6 C+ U3 P8 e7 i# J

% a* ?8 h1 @) G& ^4 e# M t" r; x

; D" k+ x/ r' n$ T' u3 K$ F 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 5 W/ h! i7 u4 F9 i$ A

3 n% o; ?, E/ O

" t D0 h4 e/ M# m5 _# s   ! [" o. }( l8 h( {" l' z

3 J' K& `. D" a1 B

j- w( G; X# w 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid N: }6 T3 j3 `2 S2 i/ Y( ^

t# Q$ J4 B- m; [" k

2 N8 {! P, l8 x" W 不过实际利用上会有一定的限制。 8 {; O! l, a, Z4 H

0 u# m; j, j7 l1 s3 V/ [

4 {8 ]7 X: f, \) s* F+ ~# w 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 ' T$ s ~& j1 q4 s

6 _9 @; c8 W; J6 s" I4 W

4 ?7 ^" p4 Z2 e* \9 P4 e  $ X8 H, I+ Y5 n3 b; | ~# d

0 {/ |' K2 P f

, u+ K# h4 A! Y! K, b 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:& ~& J4 U0 S3 x* p- y5 [+ m4 T

/ k! }" n& c0 Y v- Q

/ @2 H1 \) x, l" J4 ]2 J 省略...$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]);省略...* H/ \, V( p6 m; P! D. G

% h. f- c! ^0 c& b4 O/ S2 Y, T0 F4 y

/ P2 T) W+ o8 f+ _+ q9 z' { 因此要利用成功就需要条件竞争了。3 V$ f& s) ^: |2 ?

: s. ^/ {1 S9 T" Q' [- t+ x

- m/ ^; [$ f$ t$ [8 U0 e+ c 补丁分析 , {- E x G; E V

' ?, [1 |% c: F0 l- q

" y6 ], R3 B0 g% r8 o  * y7 }) q3 A$ ~9 u8 ?- H

7 n; G: @; ~6 |0 s6 K- a8 f

, G5 n' t6 v4 c" B: u4 r 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:& W7 U3 @ e+ J+ W

" v) h8 |5 Q7 H! ]; C3 A. y

7 ]: p- `+ [' T4 z- M/ `( T function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}# C2 p {& c+ _4 g9 g8 z* h( b

6 \9 P" F; w. Z: V( W4 z% j4 I2 c: b

4 r7 Q" i2 p9 i, a) H6 R, o  , N. g& v; o+ |5 `: t$ i

! k; \2 E2 ]$ R% x- S

, n9 y8 E; a7 ], ~" j- s 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 2 m- v7 l4 ?( @* D- [4 S6 s0 ~: o

% A! X( b) y7 N1 Q, a) K

* [' |/ ^2 [3 j) @% ^6 s5 \ 在is_allow()中增加对$this->savename的二次检查。 6 j: t/ X9 v& `. [6 z$ W/ y

, K% E+ u0 m: q- @; M

5 }, O$ S3 b# ~ 最后" p/ q' r3 \1 x

- ?; o8 S8 P& Q5 L. |

Y: A: z: e: l) x' f0 _7 s. d 嘛,祝各位大师傅中秋快乐!, x7 ^9 q; Z$ h% ]

/ P. ]3 r! r ~0 _7 G \

2 A, l8 c" u5 {4 ]2 x+ ]& V9 H   " a! D$ d2 I7 x O

7 o/ D H3 n4 G7 y6 Y6 V2 ^
回复

使用道具 举报

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

本版积分规则

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