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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
! A- _ N" K0 C- ~; n' h1 I

/ `: y7 Q* V4 b

- R3 ^' c) w& L$ b

( [" ?% l4 e8 u/ c* H% d 前言 " F6 |3 i, V9 Q& J% H; f! m

: \9 U8 Z5 `, h9 P' k: D

# j2 y& ?: j, }: \- ~. p 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。8 u0 r, f3 }$ K4 U0 f

w$ o8 p$ \& A; C. F

c) T( }" V% _3 F/ @9 i: k0 k G# O  8 L: F) h3 T4 _3 t E& L. Z J! H' p

3 ?! ^: D9 M* Q: C: q

* E G( J, R Z3 i! Y 漏洞分析 V& a, U' \: q

" T+ n6 j5 y4 k/ Q

0 O6 ~8 d8 {* I! K* u! M; T, Q! D 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: ! J- k6 ]/ ]% X2 N

- G v! X- _7 E, u

) N2 ~/ l- \) ]5 b1 [0 G4 Q   % \0 F V3 F% h

2 T- d8 l( h. q* O4 f5 P- O

j- w2 d' e4 g) u6 u! K 对应着avatar.inc.php代码如下: # `- q) O/ H4 L

# O1 s( Z! `( I& X

/ J; O; H; H- Y# Y4 N9 D0 e <?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) {+ e1 H6 f) C" V. ~3 Q7 [

7 U1 o; M& ]! X. G8 U

) x2 r* ]7 M( K7 m) ?     case 'upload': " m, d+ r n7 Z" t

& U3 O- |. Q" X7 f, N

8 K+ T+ Y. c" ^! O# j) @+ w         if(!$_FILES['file']['size']) { * X l! v( I" Z) o" |7 R5 U

: ~8 d( ^- a( B5 w

5 y. S# g1 p: A. Z$ z0 T: c             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);! d& w9 I: i/ s, b5 Q

+ C1 t4 _6 L$ G; @- O# l

6 B9 \" ^1 m8 J- }! b. B% i6 x# t3 o             exit('{"error":1,"message":"Error FILE"}');9 a5 }5 T/ C# D" W' @5 n

, B. Q/ b+ H$ Y- G9 J- N0 [# d* ~

# W/ u4 Y( N2 m M9 H: D         }3 o9 ^0 z7 H+ n) c& ~/ Y1 P2 S# H

) B. K3 D- Q& ?+ r; R$ t( Q

& @0 B1 s; a k3 L6 N         require DT_ROOT.'/include/upload.class.php';6 G! c; ^( ^7 Z0 H" E

3 N/ Y! }. `0 z h- C' J: a) W

6 V, D3 m; I m3 R' \1 ]1 e2 Y; d   0 E0 o& S+ ~: x# P* M$ P$ c* I

5 f1 n- [% i* X1 o) j8 Y9 r) I

, _, \" z! P$ q; n6 H( x         $ext = file_ext($_FILES['file']['name']); $ L8 ]2 K3 e& }" L

/ ^4 ~2 v3 K* z# w" j+ Q

( ?2 v: @: Q# r4 k: Y7 E* p& Q         $name = 'avatar'.$_userid.'.'.$ext;& w1 p$ n) u% E! ^

3 o% I4 s7 l$ q. e* n2 v3 o& \

$ o) ~/ P4 r; V1 r         $file = DT_ROOT.'/file/temp/'.$name;4 f+ F) E ^1 u2 a

1 e/ z: ^# g4 a6 R1 X6 _3 D

; r5 p/ B I9 q- i% ] p  ) p7 x7 M! w8 ^" E4 @/ e% r8 D

) V* C8 Y& V/ G) b

& y' [6 H0 e3 V# ~         if(is_file($file)) file_del($file);' f- B6 q( O! L5 ^* T6 w

7 d6 W: e; Q3 U3 @0 w% ?1 s

\( ], B4 K! Y. N         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');6 h" A4 S6 \$ G0 C W9 l' J( p: a

5 K; g; ?& }) t$ N* W2 W& X( I( j1 f

$ U' p0 o1 H0 ^; w4 Z' f5 |7 A1 I5 S   & C" b {2 Y" y2 X r; t @

/ O+ D, X/ \6 M% t% Q9 c6 I

; \( I$ V# S9 C4 Q         $upload->adduserid = false; 5 J9 k9 a7 H/ H* I" F: N' O! v

. p' a. p: R) o; p! @

l2 v$ T l" g. g* S2 G' b   & ~( k5 N$ g+ j9 ^1 W: @

7 `) j9 w3 Y4 r( M

7 R2 A+ X1 L% N+ z* q+ I         if($upload->save()) {+ D' e9 y3 G6 Z- a) R D K+ o1 ?

+ s3 F& S! h5 n

* e' P% d2 Y( H6 S             .... L* _& V( @- T. T

8 t8 X$ z: O. ?! t# m4 n2 M

+ N! X6 k6 O/ Q j8 m- \* K3 d         } else { 5 |- R& R; u5 U8 z; q, S; [; }: R

2 K5 B# r) Q; ^ f

1 A! @/ X7 U. A" \7 }- P, F             ...- e; X7 U) |9 a& g5 g0 J4 W

& G. t0 A/ a! @! W: J4 \- Z

' J; O# ~$ ?! `' S) ~ c* C         } # |8 a; n+ s. Y7 ?% u" g' I+ F

9 o5 `/ i9 o: q6 G/ i

0 P0 D5 Y2 i" [" M2 M6 ?     break; 1 C, `& y+ ~3 A, E, x& {

- G; {2 i' g4 ]# S/ V! L3 O" I4 f

: N7 }6 n' ?4 o# _# v- q 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 $ S" ^4 o' T% U E7 ^) Z

7 c- P& {' o+ ~1 K+ G

L$ O; _0 \7 n0 o) R+ @, j5 K9 m upload对象构造函数如下,include/upload.class.php:25:' ?% ~& {2 o* X8 W3 m' @/ r

! b; k1 q1 R* Z# W# l1 B

9 f5 K9 y2 ^, H9 W2 l <?phpclass upload { ' Z+ [' i; C- h$ X, q" o1 T

1 n8 k( r' X4 x- @

* P; D4 K; B7 f' M" A- I/ f     function __construct($_file, $savepath, $savename = '', $fileformat = '') {/ o3 ]0 f: n, v) M; x

4 |9 S# r% ^. p3 ^- W( m

3 @2 P. n6 \4 A* Y9 z7 E) S, x+ F         global $DT, $_userid; 0 n* o/ R. Y7 Y( a, n

4 G$ Y- D* V1 s4 J# s

( f! e0 [" O, u! s2 j0 `         foreach($_file as $file) { ; N' M* k _& M! S

" ?; d) G8 h* E' P3 M

1 k+ t. ^' C# d& b( i6 a1 Y" f' S* Z             $this->file = $file['tmp_name'];# o' _6 b0 E4 h# A/ B

# P1 f7 r: O' b; n7 Y- Q2 |) k# o

4 o b% y$ m; ?. u             $this->file_name = $file['name'];9 I5 C- c4 | n% D5 u

, u+ [+ q7 B, R% E3 A) c6 c

) R- p0 G1 O9 B8 G/ S: A5 m4 y' ]- a             $this->file_size = $file['size']; # g! c8 J# t9 o# U$ P* p

- \, W' J( [: O I' t

- \! Y+ M+ s4 W4 X0 w             $this->file_type = $file['type'];2 H: [ E2 w: m* q

2 ]( S$ a, a6 y n

& p/ }: N+ K) a- M9 c, i: W) b             $this->file_error = $file['error']; 0 `0 ~2 g) O( g& P0 i2 c; K

# n) k* s& H* F

: j7 ?" t3 _3 `   _# K% q% _1 T* u+ l, G4 r. ~- v1 `

* y& j: \% f. o1 \

% F, r* H% X3 {) Z- h         } - |$ x4 {; v+ |1 J

) k) f) ^& x. [" f

1 Q- O F7 B+ Q0 L. ^2 E. Z8 Y9 y" O, H         $this->userid = $_userid;! M6 `6 }* I) U5 x' i" x: J

1 Q1 t8 `1 R: M, R- K6 q) H' k" G

8 x$ I& Y/ G- Z8 }3 U: Z( V9 s u. Z) P         $this->ext = file_ext($this->file_name);1 K2 h( c8 z0 v% g p/ p

. u/ d& P9 y/ `1 N

c+ Z; B6 s2 }! u% {* j         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];5 W+ S q* z7 U

$ r6 ?' ?# W+ T2 a

, I% g3 T" g+ }- n         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; 5 c7 W. q/ {, I6 w

4 c5 @. h6 }) i8 l6 g8 `

r( F% p# ^# L" k. h         $this->savepath = $savepath; 2 s4 `- L) |+ W0 x% |

5 }& [/ v' q, Q Y+ f' U) L

# D X& r$ A& I         $this->savename = $savename; ) m% `4 ~ c6 F0 x2 ?

% l8 y4 A; K a% w! l$ w D) E$ e

7 v$ E& h9 N/ i! d. K     }} - B' h5 @5 e9 S4 ~

8 H6 ]+ K6 h ^9 U% B) [

, k, r1 `. ]; \) H" o 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 , w# c3 w& a" P \ A3 l

- t1 @- _8 n3 @* I, U. j2 J

8 M2 z6 t: Y } 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 ) ]' b) W+ ~* ?. I2 t

9 y, r7 n% Z5 w1 n$ t7 h9 |7 ?

' K: d( j0 n$ D7 ?: G5 ]* m% h $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 i5 n2 @7 S2 \$ H+ E

6 b5 E9 g+ ]( P- D+ j! m

% m9 K4 I! I/ A 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:. T, B( l/ a; ]8 ]8 k7 f

7 v1 f0 U# J' L# T

5 k o- J- V% @) }" C   1 h$ @5 @- @+ ~6 Z* c

! y+ o- [" S* L6 q

0 U* w6 o, o/ Y" m. N0 }4 { 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: 0 _$ F4 Q$ o( B4 J' Q* Q

8 M$ U$ _% w( I1 r6 [8 L/ |0 G

+ G* K$ O+ f* [, J( {: n C- q <?phpclass upload { 3 H# X: I/ T1 s8 v8 S$ E) R

& t; k) h) z1 r9 }

* x/ ]" c1 `7 ]8 i     function save() { " S' l3 A, d( ?" T/ [5 M3 ^

9 Y9 @1 l5 i0 C" ?* s( K

* x4 E3 |: s2 A; x: }" A2 a. |4 K         include load('include.lang');+ s4 [3 V( b! \ n- u4 s

& }* S Q: g% T7 d4 L8 ^# d

. V! S9 z P' s2 F' c         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); : P3 R: Y$ Q7 |* G8 e+ Z0 L

5 n3 i) W5 q! b0 b

d9 v* f8 y4 l' \ A; z1 A   : v( Q: D- _" X

# A6 R: O" p7 o) @

" g& k7 {* V1 s/ q6 H; L2 p         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');5 V7 O( m6 K$ Z- C* \9 s1 F

7 `/ Q. O+ _3 j: t. x

7 C& A1 q5 N' J8 L6 I1 E  $ o0 i4 m' X: e( l/ o

# m' j u: G9 A( d

- |* Q4 {# g% y1 P8 A% V         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);$ V n. C4 r. S4 t* r9 D

- N8 x4 Z4 E5 l" } a

8 R& @) U4 e$ ^   0 e; l1 r' U* M& K

r) W; l4 K8 b( h6 H8 U

@2 [0 `5 _4 R( ?, q4 a         $this->set_savepath($this->savepath);7 Z$ Q: }. Y3 U: Q

" ~. w1 L+ Z* l7 M Y4 W

$ H) f2 W% o C6 m8 p8 p         $this->set_savename($this->savename); . z$ r- R8 ~1 K9 h# c& U; Q

% } X* r1 Y3 B1 a" ]

- X8 \; S, F1 M  ' _9 |, @# i! P' P9 I- _

) s# X6 D5 \2 [1 _' m

* ]( o: ?2 D6 C- L& h         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);/ x& r5 r; y) U t6 d, l

5 m" }# G8 B# x2 U3 Z9 _

" `) i5 B$ f" H& V         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);) |% ?* q+ A4 s6 ]! \8 B/ @

/ x6 |8 G' r- L: D4 N

3 X: L& Y& W1 x3 `; ^         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);: Z% r9 u% o! ?

& N% T: l' a1 y5 g5 q/ b4 o$ m

8 \. O) y1 S4 P% B( M3 v  0 v" d7 X& C( w0 h! g) l

0 |4 t6 \4 K& S7 A

1 _, C+ f0 q6 k         $this->image = $this->is_image(); 4 [& [9 L) r" {% F

8 e* k' i3 B6 j1 A" k' k+ t

3 T, ]) ]( u% u+ r0 k6 F         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); 0 `) G: {9 v$ `: N

; `0 O3 {$ ? P3 X

/ S, M$ k( f+ W: s4 h8 D         return true; 0 f) v- M: l; @6 O5 _

, _* m z" L. r9 R0 D% I( d% `

- [4 ?' Q( x1 \$ ?" P: \7 X- j9 l     }}# D7 A B% c! S* @/ v0 q( g

, z/ M8 H" O$ R u% S

. e8 J" C. q# g 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:0 @5 n8 s/ x( ~+ @

( e0 d* b- n: q' g; C6 {8 ]

1 x+ k- S1 n% u- m! m, ] <?php 0 }' \* a: F# r; J! R* X+ x" G

+ G: O! V" { ?+ W/ e

( s" \& @. t5 {7 f9 h     function is_allow() {2 ?' Z2 Q- B4 r' k! E1 d2 ~0 `5 n1 w

! W7 S$ [, w5 K) B# Z/ g- ?/ g8 }

- Q% Q* @* J# {8 b' K; c4 j         if(!$this->fileformat) return false;3 Z8 M& N0 [' d

; U- o7 a- V2 N3 [! X- W( p

% d- Z2 I4 y% I! U' j9 `         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; $ I3 g. F4 W2 a( c( j0 {3 z

0 {# R" J. V1 l8 J- H. j, O

8 ^4 O# j7 o; `         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;! E- ?: J1 l4 G% U' f! j

) R( y3 ^; P5 V0 | l

4 }: X# s, C. z; ?         return true; * N) b3 U( [$ Q3 Y. y) M0 j

6 _2 p& Y+ T+ ]0 ^( ^# R

: w' X- _4 A' B8 `1 h4 f4 X: d- {     } , J8 ~5 x8 z" T; H+ X/ H/ _5 W

9 t3 ]* t: Y! Z7 W0 _: G/ ]. N

4 v; \) F) `. `2 k$ ]0 z' Z+ j 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。3 m1 |0 l4 f- n7 b/ Z* E* R

# i( s5 K! K& {0 o( b7 l

( c: L8 ?2 ]# } 接着会进行真正的保存。通过$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文件。 + [& T2 ]7 t( Y. n% h& E$ a

- w0 ~, j: g K0 _# r# s$ s( O

M8 t5 v' l0 C3 r4 X: h) k( q 漏洞利用 ( }+ W; y( I) {* x. q

# U# \$ _; O3 b2 y8 `- K: S0 R9 K1 m

2 U) [1 q. N0 m. C2 X 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。4 w& L0 j2 o0 d( b* [/ V; G

* V+ O a5 k. z/ o& O

7 O" _) T3 N" k2 i   5 v& L* d+ o, J" K# f0 x

" \4 ^" w% H6 P, K: g

. w( c5 u0 r/ Y- z8 L& g 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid" g7 ^2 q" n& e9 f

4 P+ @% v; Y9 Y) S3 a

# u: }1 T/ ^0 V9 r7 o- C. w 不过实际利用上会有一定的限制。# x8 E- _1 C. q

1 I! U" S4 ~/ n7 l% s. P

& e }6 P: ] V {* `5 `* F# U 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 G: D7 }+ k1 h5 d4 i; u: X& E

r# f# H7 V; W9 b7 m* c* q

' L+ C7 {6 `/ {- v. S' t   ( M, H! u Q( j, H. E! Y8 ?

% q; M( m' c2 s( [

& A# ?( U! j9 M& E* b0 S. R, l 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg: % S6 ^' g$ c5 o6 \# z. i$ K1 E

. l8 F" U! E3 v8 |" r

' E& G' R- g# b4 `: F 省略...$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]);省略.../ q: h5 [2 l0 j) A1 \ P

$ M( O, O) l- S8 O8 Y& |: ~

) h7 l6 a5 a+ d5 w9 z 因此要利用成功就需要条件竞争了。4 U" N+ P N8 l6 y+ R a2 u

) [$ W8 G; Z' x* r) v$ n

- Q3 N" m6 x4 {- f" B4 X 补丁分析: G$ w* ^% f* s( _. Y4 G

P/ Z2 h* z. [- l1 @4 k$ D+ U# u) V

7 J5 o: X4 c; l" n% f   1 n% }) Z/ }% j2 { e

! @0 x! F- D5 [) n. r9 [2 n( V

& Y$ t. D* p- q4 T 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:2 e- k. `" k0 R" W: y) F* |5 e( r

! ?7 G6 C! d1 ~; x t) q# k

. N2 V. B8 ~- B# {) X4 i! [ function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}4 w2 p3 j4 A- @: h! O

' [6 E( X5 j5 U5 T# S

N" q m9 X0 W! n   ! ?' |$ L! B$ @' T& }0 J e

P: |' c8 m; R+ Y# f1 ?$ R

; t' c! x) u# s p( g, C8 o 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。- V* C) ]2 m" c3 L, }

% Y! O2 V* C8 s! M2 n3 V9 ^

% M% c# i6 O& M0 v 在is_allow()中增加对$this->savename的二次检查。" V" c6 }( k4 ~0 J

% @3 b1 g, L- \- O8 ?8 ^

. o- h' j0 ]8 k5 Q; {, T# V 最后 0 r2 i8 |3 K( I y

2 C8 E# V6 }+ Q# T2 h U8 \

% @% C7 P {% a" ]* k8 O 嘛,祝各位大师傅中秋快乐!! ?7 w( C6 P- G5 C% p. Z

: r+ `$ N" f* \; o* W

# _) E4 a f' q. ]2 f# w+ _+ ^   + Y3 Q4 f% x, T1 ^

3 z% L$ B2 V+ v0 x5 k
回复

使用道具 举报

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

本版积分规则

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