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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
3 u) B- `6 r" s( J: H o. j

" O2 u' @ \9 y# j8 a3 z

! M6 T/ y; S/ d$ F

0 H" {7 l9 c5 q) f) d. m3 e, h; X* S 前言! w; z* L, W0 B% v! ~) y

) N/ R- H0 y& n: C

: {5 M5 H% q2 d; B 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。. I" g2 g" g p( N4 r: z

4 c5 i' |9 n. B. j$ g9 X

& V- J: e6 i/ W9 x2 H   - ^7 ]/ _8 N2 [! u

( {' h" P- v4 Y) o, l' O0 t

2 X% j2 v3 z( X& K. F$ t7 B; ?' a 漏洞分析 6 {, W+ N$ p- O& M. p4 E

6 v% C- X, e- }; { g

1 p' G1 q& B6 D; O3 p; \) c& Y1 v, R 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:# }* n! H1 d$ r% v! X2 f6 I& a

( v. T- d- d- w: ]: U I" l: y& f! P! T

# B+ t- i' Z* b6 C! |+ c- t   2 o7 B4 K7 c4 P8 }$ [# L

9 q) P. h& E J8 ^

0 ^( V' n, w+ x" f7 ^+ H$ S1 u 对应着avatar.inc.php代码如下: 4 G/ ^1 s6 u" S, e+ w8 P+ }

- v' c: g3 `& }2 x% {" R' ?- W, g

! a. Q3 K, ]+ ^ <?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) { H$ `. u0 S7 }8 w. _% u4 Z, X- G

# l L, }% F/ G# b1 {- V' x P

# C, V6 }) s) F9 u& e1 S     case 'upload':$ d4 c; F! E" i0 ^

* o, a& I8 M7 y. `6 A) l; l2 M

+ O( W! Y! x' C2 a! D# [         if(!$_FILES['file']['size']) { 9 S; i( B4 R* T* G% ^9 r- R& V9 j+ ~

. ^, @: s8 e! w7 {' Q

* l1 H) c) m2 T% n% l' A8 h$ H* ]             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);2 h8 K( {' G$ D8 t/ B8 X

) l1 Z( n g o0 T4 \/ E

8 z3 Z0 m6 b" ^& v7 W' v             exit('{"error":1,"message":"Error FILE"}'); " w# e* y; k; a

) G) S' b: \8 x

. Z2 w. [9 U) M$ _/ a5 d" d         } - R8 S1 W+ i0 B1 r

/ y! Y8 @4 J' H F! r" j8 Z# G2 y4 ~/ j

9 ?- E7 k" y- K         require DT_ROOT.'/include/upload.class.php'; $ e1 D6 L4 V% O G4 ^' o

p+ k$ l# R9 r

6 R R! N5 |/ d) L8 t   : I% p, l8 [2 n

: u3 V# |4 ^$ K% p

7 V) X- [# ~$ z; m2 G0 i4 _7 ]) I# F. h         $ext = file_ext($_FILES['file']['name']); & c* x" c0 j) v0 o1 A" G- s

( h4 A* U& p d; ]; s7 c

3 W$ G* W& Z5 P9 ^! L9 w/ U4 d2 m. O$ n         $name = 'avatar'.$_userid.'.'.$ext;& L3 V9 ~/ s7 {* W

D7 Q3 \: `; Y% D

- s6 |7 p4 F5 A( S         $file = DT_ROOT.'/file/temp/'.$name; . b1 @6 u8 P0 H+ P: S" U6 h2 w; j

+ X1 u. X1 _8 z7 ^4 a+ M( J

* i. B" N" C1 n+ w7 ^% |0 R T" m  $ B# w8 S- T. \- j* ]$ _$ R

0 [; `$ { A) m" D

2 G- C: m+ ^( k, l4 i) y. \         if(is_file($file)) file_del($file);& E7 ^# a4 ^7 |; W/ J1 g9 i& A

* Z9 J% A6 o/ V2 W4 N9 Z8 R- ]) M

. s9 | k+ ?' a) A0 _3 r         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');& X5 b; O) B; M# i

3 m. H, s& A' @# d- z R: o

% } u& q& G* c' \8 q   i: F1 G3 @7 D

5 d! a, ~7 h6 T

+ |: Y) a1 W5 R' b# v& J- j         $upload->adduserid = false; : _9 d8 y) ?! V+ Y1 @

; F8 _* `5 |; S- ], C

6 [2 ~+ ?! M# j- h! V   6 H3 r3 @7 ^3 V* g6 {4 Q

& e- ^$ |" K# N+ P* X/ T2 S

, o6 T# W8 W+ w" H" i         if($upload->save()) {' K) L9 N1 g" n3 N

5 `2 j4 E; T# V

! V9 ~0 S4 x: a& q+ ?% \             ...5 k9 [4 t/ ? ` u, h( E" Z

1 q' {$ t8 m' c" P/ a7 w

& h4 |& F" g- l7 {; B h% P         } else {, j1 G, h4 b) [0 x* ]

; r/ l/ r( x5 |" h3 p0 J4 k4 W

2 }! m$ \. b+ w: r# e             ... + D; j* q6 Y( G0 e) O

. V2 Q4 L1 S1 z+ o I6 m+ {

8 V9 [, Y! B# G+ Z         }$ S- j: s# F9 P% E( ~& ]! U0 S/ e

& i1 w* `0 y/ Y

* T3 R0 B3 G2 O6 ~$ c     break;$ V3 C5 d& |: [1 C$ O: Q

+ L$ h8 ~ t: v, A

4 w+ l3 p8 y9 k6 c2 y* Q" R& a 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。; [1 S- r; E! t( t# E7 v

/ L) p# e7 Y" t" i; |+ ` ?1 L* s9 p: J

" ?& }: P( h4 _/ i0 A+ v z+ N upload对象构造函数如下,include/upload.class.php:25: , t; S) U* W$ X4 V2 l( @+ R

3 @5 M( h) o2 m

; Q8 K4 C3 U ~! G8 A <?phpclass upload {) Z8 {9 k4 d, L v

+ `5 Z. U- @3 _- K, p

) ?: U4 ]6 L8 C+ }     function __construct($_file, $savepath, $savename = '', $fileformat = '') {1 R# k( [3 v7 J C' s

. s! o( J; C% T G( k: i& c

" @. Q! R% Y% P o7 ^ I4 @         global $DT, $_userid;& }6 N3 W( o1 s$ o

5 y6 I+ {. K- `8 y. I1 T) l4 u' }, z

% P$ B- [! |0 i+ ~* a# v         foreach($_file as $file) {8 A5 [# Z: x1 c. L& r

1 ~! ^- E2 a$ e" g

}' d. y" \9 b* e/ \& d1 N7 \& w, k             $this->file = $file['tmp_name']; % G H# A# b `, k6 p

7 w& s% K$ d6 c

; ^. m. B, v1 \1 Y9 T N             $this->file_name = $file['name']; , O' `7 E: ?$ p' `

9 B+ Z$ C- b6 V7 X/ g9 @

, O- f* h) y" x' ?             $this->file_size = $file['size'];- v( K8 Z7 N: X9 u

. ~ ]0 ^8 U: }! ^1 u6 I' \, e" R

2 \' X6 J0 J- H! q6 `/ J; @7 e: ?             $this->file_type = $file['type']; - U2 x4 l y H! L4 y! `$ {

% x" b1 F) ?" `$ R

6 b" j" ^3 ?8 m5 \             $this->file_error = $file['error']; 3 c) n! g: y3 C9 Q0 L

- j2 F1 ?$ l0 k- h2 \5 H

+ a0 F7 N8 `0 {: q. q# ~5 d  . |7 N: J5 F5 \# L" t4 {

0 x2 E5 V! p3 Q: @2 }8 O

, \9 ^( Y. H) B         }0 _% n/ u* b D3 ?" J1 u

: W4 v' q) _6 ~4 c# R( E! R+ N

6 j ?/ C& p, V8 G3 x$ S* t         $this->userid = $_userid;, ?/ }0 \2 t7 T

8 x5 B! P2 W, g/ R

* D' n) z' r$ L8 y/ O$ ?         $this->ext = file_ext($this->file_name);3 a! u' t) z3 ]3 f- _2 A. z

! Y+ a. Z9 k; y5 v% M) t

* s9 z8 F2 Z( K! i, g2 y/ X$ S& N( g         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];$ t( T" j7 ?7 @9 w

0 k1 y1 E. ?8 H' T1 U" n* y

4 C& g: N2 O7 S) j$ i         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; / e5 F( \" E [5 K0 s

$ j3 x @! X9 z8 T2 E- s

& f/ L! u0 l- \         $this->savepath = $savepath;' E$ `0 w9 |5 ?

/ W; l) C6 y, Q* i- n! [4 q o

; n# [# M, Y, O$ _8 T. O7 c         $this->savename = $savename;; T' f* d$ U5 Z/ s1 r2 g+ m, Y3 g4 A

2 d) v* h2 h0 }) g0 |- l9 i

U5 B2 t& e% ~, ^, o     }} 9 i* ]: k7 D4 z2 C9 h4 M. v

4 W7 m( n+ z! Z, Z' [' | \+ h

1 w( L/ O- n& Y/ d 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 3 H& h$ m! ^3 Z4 [ Q: Y6 N" [1 V4 j5 f3 B

. t) p: a8 w1 F. j& h3 M* O

0 |' l. s, _' B- G+ J% x 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 ( _1 ~9 o+ E7 Z! u7 |) v

9 Q* A1 d) Y3 W u

9 E4 X5 j! c% m5 [ $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 * v* q& {- J4 m6 A4 {" M

5 H/ r% r- l5 R( T

, [1 ?! y: o7 ~1 i 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:( I$ T, M8 q) J+ _0 k9 }! v

% e5 @1 g: p2 Y6 n# U4 r

' Z, R/ O Z7 e: N. C6 t! }  . J! s0 m; a2 ?' h$ |9 j- z! L

; x* j3 U5 j- j

. \3 _' l. t2 d7 Y9 N 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: 3 D. N- u& ]% U# e( _' v0 w* ]

6 X% a8 M( s; G) T6 [9 y. h V* ]% {3 B

& U3 K' D' ]; b- Q. B2 I- e7 @, o7 J <?phpclass upload {! R- p+ g3 J0 [* h; Q

4 B( p; T8 B% q/ h

/ U4 `' S2 b+ x     function save() { / i \$ V2 X" ^# E* i7 g4 |! B/ Z

/ i7 a! g( g$ Y0 w5 \

/ l' K6 d! N; S7 ?+ ]         include load('include.lang');2 s6 \; E* k! C+ J5 z

1 P$ y/ l* I: t/ v0 o+ }& x

6 q4 T; J% e j/ p) t6 B         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');1 D1 [( M$ I2 {: ^$ o/ h1 {

& S$ ^ p$ c3 k, ~/ f" Y) g7 e4 S

! {0 }; [# I$ l" ]  " R7 s- }4 h0 d2 P& g

, m, ^3 x+ G q, `1 s' ]$ e

# i/ e0 U/ \3 F N" d" i; y4 @         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');, E4 v. V. _: C( J

- e) ~( K/ e- A' d; q4 P

! }: t% f( D. x P) S* }$ i   + K9 ]* I/ U$ S$ x9 E

- y7 N }5 X4 H' V. Y2 j

g' g/ A6 f- A4 S& F         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);' K3 x0 s' b, v

5 Z+ A/ F( N4 M

' `, T5 g& W V8 w   $ {% H: w& O$ c

4 v% ?# J$ g* X! g9 {% x1 p

2 W7 d4 V. t. K& ?+ H7 g" x8 X# ^         $this->set_savepath($this->savepath);: Y/ [: A- O" s. R7 F" M. g

' O6 n9 ^7 Q# H6 @4 K( Y

0 m: E4 e$ O( g" t         $this->set_savename($this->savename); : m/ ~" ^) z7 y% U: t$ o8 w

- O" `: T* U k v! J+ V- o

3 I9 ^" c4 ]' q   9 E' X4 K3 X' F( s

* ^: q; N- e4 @% c/ F

% L" x5 Q; @3 I l! {         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); 1 A9 n0 U+ x7 h) x' [7 o$ ]/ s; o

2 S7 L' S1 V4 l% u x: i

: m# Z4 C1 i; U0 J2 o         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);0 L7 O6 ^% {/ R* p9 u/ c9 v

' I5 P- l h. i- C# k# z' T

* N# b/ T& V r( Y8 ^         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']); 6 ^( E8 K" y" W- D0 T

" ?. `/ X0 ]9 l/ D6 L8 c

3 r- H9 ]& B8 p+ B  - T# D* Q+ M0 C, E! F2 D

7 N$ l7 o6 V4 q

" P' A0 f) F; f8 o         $this->image = $this->is_image();1 N) r1 r4 W% H7 B3 H

4 i# n5 N/ T$ |9 b, d

- M! S# L5 P J8 K$ k+ u         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); , A6 J9 a+ Y& Q

! e `3 r! o9 B5 P6 H) a% i

" y# N# g" o1 Y1 X" ?         return true; ( Q- v \7 I) b* s# {( z

: a( K6 }% P/ I8 A8 \

2 M, P: C; A% V% n( L5 V     }} # \& B7 M+ b" f, f8 g! j

/ d) u) Q2 m$ O: C

% ^+ R h" F) G$ E3 l2 d0 t 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: ) o7 I( d" K2 u& w

: D' ^% c; T, V- v/ Q: s

) c* [& R- ^1 P8 V9 L7 I <?php, n! f% x4 H2 M

( h i( K$ |9 k% i! c

$ v1 g& i6 d. [% x. c+ {6 l     function is_allow() {- O- @0 K5 ~# E( D$ V2 k

1 n% L# n' A5 g) N9 G

5 x+ o+ }1 d0 R7 b         if(!$this->fileformat) return false; $ U2 b3 j; A0 T" q" y2 g

" K `& ]; t" w& h3 n; f* B

7 ?; F/ L: s5 l M4 H1 u         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;2 J% m" O6 a: w) i0 k

6 G- l- X6 x4 L: w

& b% n) S4 p, G; |* w. r         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; / z& F% v# a: w! M6 i6 o1 x

$ v5 g& ^" o+ A' m% m) X, i& D# L

( n: C, M+ f% v" F! v# V         return true; 6 h+ `) Z' n$ A: L% a3 t

+ `8 ~! u& q- |7 n) D& Y$ b

, [4 e/ d5 T8 [; k& O% _* H+ H     } , u! I1 t$ V1 z6 ] [% p

& {2 F2 J8 p2 j( E5 _3 t8 v1 j1 ?& r

# E+ D8 v/ _5 k, r* p# T& e 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 / l& D, T' T; H. B0 b; P

/ f( e7 X2 P3 L6 K+ i% h

; i: F8 o5 t" b: Z; N X4 F 接着会进行真正的保存。通过$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文件。 + t7 |1 X+ v- f' ]

1 `* H7 X( Q' |4 |& Q

$ V5 G6 X' t+ L 漏洞利用 ' k( T# E- } ]# [1 F

% B* l: k, g' f9 r

6 F6 g# u% e' ] 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。9 X5 U( B: C' o( x

0 y% v6 ~$ Z. U. K

7 r+ P# n2 M* u2 A) l   - j n) o1 P, G$ h3 \' ]( T

5 G" @) d, P8 j# g% Z8 S8 ?

! K# {# u1 W" T0 U& B- I; J 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid3 ^! t9 C( W; M) v

- t2 J9 U3 k1 U# Y' \' O2 u

$ m4 [- o( S, ~# N0 p9 C 不过实际利用上会有一定的限制。( X* y9 L. K2 a4 H" j$ H2 ~

% F# | d7 w# Y

3 |: ~ Z- h6 U& P+ \# J 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。 ; e' v0 v) w! r! Q8 u3 T% c

( P* a6 a- t$ C% f! j4 f

7 s6 Q$ J( Y# r- j+ h4 U2 l  : d! d1 s# Q: Q$ A; i; m/ V

' ~0 |: I8 N. @: i

( n8 X. N8 ^3 E H 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:; t1 x3 j2 {- S- f) L, p. S

/ m: z1 m _8 Q( k& R

& T( g2 U: s& O* f1 n 省略...$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]);省略... 1 Q+ J1 i' m; G# P1 M

- s, R/ U$ G6 e- b! r$ @( F. W0 t

. a2 ?4 J5 p$ T7 I" D 因此要利用成功就需要条件竞争了。 / p9 X8 }, F J3 R

$ b6 \, S4 {, I) \0 |, O8 v; R

2 d6 ]1 h7 p( J* K+ ]6 F 补丁分析3 D- }% p& C3 a% B3 {

- a- V1 v% ^: d7 j, X6 }' I

) R1 U+ Q% o& J, J: ]   6 ? A3 u h, v' Y6 z# \7 d! P

( V: |8 G9 i8 m/ M# {/ A* U

& N% V/ n( k: _" U: V 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: 4 ~! q# N6 I1 S' R$ m

* B5 ^" I' X& n9 a

9 `& a7 J0 L; d% p5 X' O function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}! {4 X. P1 V, d9 K& F: |/ n

$ b3 @! c+ z1 Q. F! x ?

+ }5 ?" [) T& C6 T: U   2 S1 E/ q* d! c. b, d3 U; d/ ~

% ?: U5 A1 o. k; h4 a/ ]

" r3 B o. b" Y; j0 G/ c, W- ] 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 : O; F/ p7 Z* x: x4 k F: y3 u. h

9 Y+ U: k5 L. H+ _3 H

' `/ `( L. j" e t8 R) k 在is_allow()中增加对$this->savename的二次检查。 $ ?: m% X. [# w9 s+ J- {

0 z' {* `+ t$ v2 h0 Z0 g; [% t, z

1 ? Y' a9 ?2 e; c y* e% l 最后 ( H: h" Q5 K8 y

( G7 A( g4 @8 M

% K& {" U1 r a! d( R 嘛,祝各位大师傅中秋快乐!2 k2 N+ \- c5 M" U% @9 Q

) Y& U7 N0 ^0 Q# D5 x: @2 w

3 W6 ^ |: Q J! V8 a6 H   " S8 [0 i, A" e% F5 o9 A; Z

) O0 j8 c' \% @; x
回复

使用道具 举报

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

本版积分规则

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