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

Destoon cms前台getwebshell

[复制链接]
跳转到指定楼层
楼主
发表于 2018-10-20 20:13:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
5 {5 y$ t* W0 g8 N, V

/ c7 A$ a' A$ x4 q) \7 V

# H. j0 s h/ _9 n+ B- x2 d

3 Z& T' I! v, ?3 {9 _7 _; i0 } 前言 " L3 {% M; z' _0 m" {8 J+ E' M

/ L3 g3 A6 p M( ~2 u1 w$ W

% k! ^4 ?" h6 ?8 G D 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。# M* z6 g A! l

) d& K n; ~: G+ C$ Z: R

" |- ~$ M/ S7 T" T  8 j+ f6 ~1 w3 y ~6 W% l1 Y& @# k

# @; U$ B: j5 n8 S' k0 a

$ w" M+ ?* }. n) o5 A" @ 漏洞分析 , k; I7 M i* y, @) D! p0 X. v6 s

8 J& Z) c* |& W! R- V* b

/ Y' N/ J0 q+ S# d+ `6 _+ f 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:( @6 i* [8 S ], V7 B3 _

1 F1 ^* q B% Y1 Q, S0 i

3 b+ X; V F/ P! B  - K9 T) o5 O7 F/ g4 N" y

6 M6 g+ a3 f; n, D4 t2 n

q6 ? V9 o9 c 对应着avatar.inc.php代码如下: ) Z+ L: E( L9 z; x$ z, ]. a/ Y* a

) y1 B3 i* X: }$ [8 X2 r

( E0 l! i- `" L& I! c1 @' _ <?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) { 0 m1 Z! F6 A3 V, R( o2 x

2 Q: C4 P9 d: L

# ^* o+ K9 H( D4 f     case 'upload':0 |6 M* t5 X2 l# a

( r7 M9 s2 b" `

" p5 _( O& }9 _9 L( G* |* w0 u& T% a         if(!$_FILES['file']['size']) {4 v ?; Q# w) P# b' B5 w- ?$ W

; ?7 q5 O) n( X' A: V

# n5 A/ `" t% g- L3 U6 w             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);' T( k; Q) i1 |! R

3 o3 l( m; L5 K+ O% v" S5 S A7 t' U

3 a9 l$ f$ @ `5 }- K& S5 T             exit('{"error":1,"message":"Error FILE"}');( \% q u) Z& H/ Q

" w) b7 V4 L- ?; C5 y) g6 \

( _0 U2 ?( g. Y# n5 ]         }2 ]- w; \, I( f' o5 I2 U

; W8 T! @9 d5 F/ K" g5 @

3 k5 U3 l. d: q         require DT_ROOT.'/include/upload.class.php';0 Y8 `1 w1 R5 i" e) [

/ T4 M8 c! N/ j) _0 ~/ w1 p

, l5 y1 P( G& C) S, s3 K' e  : G9 B+ U' d- a$ N1 @: B8 Y

& `& m6 J# c& f0 L# B

, N( q) k5 i; H S4 u+ M         $ext = file_ext($_FILES['file']['name']); ' E8 E9 A0 T) C8 o

$ V- N: q" S* m0 b/ r' c

" V8 P$ h v" `5 |% A         $name = 'avatar'.$_userid.'.'.$ext; 0 A8 y2 h* F* `; ?% R1 `

# i1 h; }5 ? U1 @% ]3 c. L4 \

3 z. b' O+ ?5 K5 B: C2 m         $file = DT_ROOT.'/file/temp/'.$name; 6 d! W) f' w, H1 j# ?

$ R: n, v0 m4 H

0 E1 d: A6 V8 O; j- h   * S D! D* |0 i1 i

# [) r+ Z$ \- ^* u1 D1 b1 `" E4 ]

. L6 k& u% C J" t; v) H* }, ^         if(is_file($file)) file_del($file);. ?) V$ i+ I, [ x: V$ U4 P

; m% T+ y& T; T4 _

8 a% V7 \& L( C% E9 H0 S9 z         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');* q" z, @8 L v( R, |. r

9 q3 ] Q0 q* p% h4 V, ~4 }2 P8 t3 T1 u

?" U9 z+ S' @$ ~3 W2 H6 q  0 ?3 H1 a4 i6 n+ A6 l

' S0 x+ G& j4 | l' V7 ~- y

- H; c% L* U2 h2 |0 y* F! R3 u/ c         $upload->adduserid = false; H1 r H* q9 ?8 {

6 g( B' N, m2 w" M' @# W0 t

+ b, H: J& c+ A8 S  ( B; r: V* r+ }4 x

0 W) n7 Z8 L, ^9 @1 X/ ]

$ t# p) v2 Y! O( D: x         if($upload->save()) { " _4 c* R: c. R1 p: Q) c/ z

Y2 |( f( i: q+ J0 G6 D; Q; y

9 Y, r0 I6 S8 M2 M             ..., g- d$ y+ x) n. W+ C

2 \3 o& g5 Q2 N. c4 w

' G* b C2 K2 }5 ?8 t         } else {+ f, V$ j8 T. v4 h, f

. C/ P' c; ^; \+ @- l

d$ ]) W+ O. M, }% W             ... & X) E7 A% T. Q$ L. ^

9 ~0 i8 t1 s# J7 ]9 f

& h- P0 o2 p9 r9 E7 C# B& y         }! S5 ?- e0 [8 m, m

3 F/ m! H. H# V( E

) N2 N7 B) U- Q     break;, v$ Q3 N) B; T1 h( R

$ k$ Q4 S. s+ F- A0 V) v7 ^+ I

6 F; w$ ~2 D/ J0 R) q; n 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。; V$ v3 C4 L! Z- J! d. a

* z: S- n+ {* K1 J0 _, j

. r( u7 J* P1 \9 A upload对象构造函数如下,include/upload.class.php:25: ; H# u1 S9 \8 F$ I+ v% Z

& f. i- ~* Q" t/ X

2 [8 _7 M' Y6 A <?phpclass upload { 1 Q9 R3 D$ u9 u2 C

% O G8 c- M$ |' @, k# ?# e+ I

4 A- U. h/ W4 n# ~     function __construct($_file, $savepath, $savename = '', $fileformat = '') { # }) z3 t% }/ x3 q- [6 Y' E

( O; [2 c& ~3 g ~& n. z0 Q

: `' S+ M `+ L1 W         global $DT, $_userid; : c. A2 v3 h" Z$ X

# O3 l7 J, Y% w8 E# m

. C: Q9 A* M9 i# f" @         foreach($_file as $file) {& c! O4 _4 v& Q# {# ?( `: f

$ W1 D* C2 M4 d. u0 G" z

* g" B+ o2 A" w             $this->file = $file['tmp_name']; $ c& n: V( a+ o) R* c+ R

5 g! g; y" q4 y( A- G' l

" R- [; k- d% b8 s5 p4 \! Q             $this->file_name = $file['name']; 9 L! p. c- t; R( Q/ b4 ~( \' t

4 x# t' f( G3 |5 H9 T

/ e+ b; A8 L. [9 t/ F% u, Q             $this->file_size = $file['size']; 8 y) R! Z* D- K+ o- D* q

" s* _* j! k K. _

! A" t$ ^$ d( g' X             $this->file_type = $file['type']; z& k2 [# d& I( I: b: b

2 h. t; Z( z/ \% P8 a4 z

' L$ Y1 S$ |/ S+ s1 e             $this->file_error = $file['error'];4 z# l% g- X# z6 ^% U

; h5 S: s# e: ^! T

) U: X$ a3 p0 _) Q3 t   & K9 V0 B* ^, ^

6 R3 w( v9 T5 [1 P7 u7 r% c8 V

`3 ^4 u+ m0 i' D3 `         }9 h0 L" k8 _% D5 |* j4 a

6 U0 y; W: _+ @5 Q1 N. F) M

. ~8 _' X) M/ `8 U         $this->userid = $_userid;2 `3 i+ `0 {9 s6 V; u0 {" h9 x: C

+ G2 V: h5 n: f; L9 O* Q

4 B. i# V: s* M/ f6 s/ R$ m         $this->ext = file_ext($this->file_name);, f$ ]0 W% Q6 V- L: U' \

5 J3 b8 B: Y4 I4 r7 k' C5 X4 b

0 E: [- q3 T7 ]# r L. i         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];; ]8 D4 D. X* | l

% q$ Q5 U$ z2 ?/ Y

6 _1 d8 a6 J7 M% U( t* I         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; . [5 b7 a0 O4 A' [

7 ]4 C5 p/ E/ N( c# v9 b# S: P

; k/ |# Y' l. `$ u6 q6 i5 I         $this->savepath = $savepath; ' ?" E8 A5 b4 f+ _ `: d6 [6 ^" P* L

& G6 _2 \9 z0 h0 ]- n

3 K r. W& j" y& f( p8 P; }. A         $this->savename = $savename; . H$ }- S5 t4 b$ k+ g1 W3 a: Z. ?/ g

' ] p1 A$ b! P1 O y2 J# y; U

1 x" ^9 I& t* |2 p     }}2 ~( P1 z8 X( L( M$ D

% @9 R) G! c8 ]8 L Z

3 ]4 I0 Y3 r$ t7 r$ }1 M( i% @ 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 * I0 I$ }6 a; X+ g

8 I8 g, k. a2 k( v+ A( o

! }4 q/ `1 [9 h& b 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 ' O7 u+ ]# t" S3 V0 q) b

9 s/ T! z# c4 W1 Y

) V- A: `" M+ O! ]" z3 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.php4 I9 ^$ i+ l% F. {

0 A) T$ s8 W1 `

0 q% b3 w+ Q2 I; |! _/ {! i% l 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下: : S3 K( ?# H3 A/ V5 t* E

# q0 U: P+ I: Z$ O

& p; ~/ n0 H8 _( Y$ P x   1 Y& ^# X8 T( e; l0 ~. N

$ w; o; L9 e& c( q

. E$ T/ R1 q) |( k/ i9 s5 I 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50: " U2 ]1 q" E7 U. T. M8 f

! ~1 `# B# x* C+ b/ b

1 j1 P: t" y( {4 A: L. J1 k* V <?phpclass upload {+ {. |2 i5 m/ K5 H8 i) ^0 M

, o" V6 T1 h5 Q' Q

) V4 K# s7 c E ]     function save() { 3 [0 c) a! n+ F9 V

$ e- L) H$ G* a' L# ^ T

9 i2 Y6 x( }& a         include load('include.lang');/ O& P. ^, R4 ]

E3 q" |# W+ U7 n4 ]( d

8 o- j+ {! m; a5 s1 d% e         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');- I, M- Y+ h- B3 p! H9 K4 R

2 c, R: s2 X" i! M( ^- K6 w* }; q7 G6 W

" H5 N, ^* ?( x7 O   : H% a, K% N) [; K: h1 X g

3 j" m6 ?: H2 Q

0 I. `- ^* z+ S8 Q* A: q0 p2 E         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');% h3 M& ~& L5 T7 h( { D$ g* L

- R% P( i7 X, c( m* m+ S

2 ]0 |; V6 }0 Y2 B. _   $ u" m, c; D# l# M, k+ {

z( T8 \5 }# s& S

?3 K. W% c* v+ d, a         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']); ) W Q( \ e7 G8 k

0 m4 x5 s9 ]9 y' M

0 @: I2 b- C( e8 G3 |# M# i; |   0 }4 O8 k1 j! V5 }

9 H7 O( [7 B7 G% s0 G

% R1 y- R) t! m$ r- x         $this->set_savepath($this->savepath);# P0 d- R% g k! M/ o7 L

( z7 q6 {3 I7 i9 h

8 ` H, f/ p7 M7 f$ x+ S; Z         $this->set_savename($this->savename); 1 S5 K- E* S% x4 j5 k

) P7 K' g6 b6 [2 N, ^2 H& h) Q

& G; r: p0 E" x5 k- e/ P   ( I, T# ^. ]+ A0 W

8 h0 T0 d2 k, m

) F) }3 A8 P' L2 O/ u8 V" R$ O         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); : j D% W Z' T( L& o& n

' n% ], h, |$ L. i8 G# f3 ]

1 F: o6 a& l3 m) @3 C% p         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); ) M- O, }9 k( |# a: _

. `0 A+ M t( G

& L6 Z; u2 d3 |; j: [! J" g         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);6 p# [5 k, u% |) x$ I5 c) T

" e7 k+ A3 P8 ^0 V; C% J& P

( r. Z3 A+ d4 M/ ^  2 y& ]8 h' }! m5 Q3 k

. e! Y7 I! N. S4 }5 B N

$ R( @3 v, Q' P. }7 Z, i         $this->image = $this->is_image(); , \9 {! [2 r z. M

" L( w, O8 {& k; i2 e# R

5 h! i) B/ N9 k+ g: Z         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD); ) d1 l9 \: o/ o. Y

: [1 o" C8 _# n$ j; r' G1 B9 m

: G: Z. [, c0 \6 }# P Y5 m5 k         return true; * f3 |4 q: Y9 s8 T

) X2 u- h$ u% h: E

, o3 |" ^) u" j     }}) m/ x2 p) ~& _. }2 k3 g5 ]

( R( C: o8 X$ z7 f) J# |/ a! @1 C

- q5 l5 j* q, b$ a/ F) y9 Y: A 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72: , F. |! T, v; F8 s0 L8 B: i3 u

6 y8 t0 k2 W$ Z# w/ `6 s, F

7 q# b0 O$ {# x0 ?0 i <?php7 b. ?. @* Q' a3 r6 S

8 H/ S, z8 J n/ O% x( p( y

9 i& k1 p/ ~$ r$ I" g     function is_allow() {) r, L5 Y% Z5 u3 x( ^9 T# n

7 i* a% P( W+ { r& v5 z' Y! w

4 b9 Q7 P6 F9 z4 c% W0 ^# s% G2 V         if(!$this->fileformat) return false;' J, B# R# [- y) e0 ^7 r

/ w- E2 N' z. Z2 A! t

' \- D1 w$ A; h) x. G9 g: c: w4 ]4 ~         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;4 Z7 V, B/ ?8 x8 s' M# y( |. g

0 e* L1 ]- m% C7 |1 m1 ~' I/ [

8 d& d3 g' d# H$ ?/ K0 _- a; z         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;( c: r3 _5 a0 X4 u! h+ o

" d1 O: L7 y* }3 k

# d# x1 J/ ~3 P# ~ f         return true;8 |: ^# z* A) m# P8 K

+ S$ e1 U; t) t F

3 H" R; F+ e2 U- b" \4 j     } 5 V( g# K* N/ L3 O0 F( ]

' x& M9 F( k3 c2 g T6 m0 b

; j7 E* O/ H' y 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。 * w# s9 n' p- [, I. H2 p% a% `

9 G8 @% m9 m* _( o8 i0 r$ e

/ V0 d9 I8 Q: f: i+ r, c! O% b( w 接着会进行真正的保存。通过$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文件。. z2 n& U5 f9 U, d6 O( W- f4 B

8 a" q+ W& M8 o2 F' Z% X& Z

$ b$ ~, m5 x, e( [- L. @ ^ 漏洞利用4 Q4 _3 |! Q8 V" K9 U

5 S3 A0 U1 D! g. c; R2 R X. f

4 g/ ~0 x) C, S 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。" Q" C/ M: L6 }- P3 D' M# b

" x3 G. F3 ]0 D. ~; U# a

& r& ?+ c& s) L* Q4 B f   & r+ x' ~, G! V; G7 q8 p$ m

" }8 P) I+ }# d& S& @, U, J; y/ v

6 v N8 q2 W% o8 F, i: N# W0 d: p 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid5 s1 S6 h- Y( m1 \! `+ t% U

. v9 O' f7 _( I* c# w

* {) K' ]+ N8 u( t3 R% } 不过实际利用上会有一定的限制。! ?# t3 g4 |4 v: b" u) A( Z

2 X& G% f6 _" o! I' \) |' M/ p" f

8 s3 F0 A. U( m: L 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。6 d8 \$ |! }3 P! z; j3 ^

$ `4 E) }" i' j9 J

0 @5 b) u1 T! i/ p; B! X2 @; ?  4 s2 f. H/ I' m& G1 } E6 a ?

y& F6 r2 C x) P- w4 z9 P

, v) |7 `/ i) {8 A 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:. I: v/ k) @9 G& ?7 Q# L8 d2 O7 q I

( F2 f+ z, A* ^" z7 w( K! p

+ }4 s- e$ K, T( A6 @8 b& ~ 省略...$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]);省略... - p7 n. j; T6 \- f1 f3 v, Z8 `1 ^

2 V: o( K# H3 Z2 S: {' X

5 b/ u" q; ^0 ^( e. V 因此要利用成功就需要条件竞争了。 9 ^; U2 x; A' g- Q" D; w1 a1 |

. [' t$ d7 g: [& z) C! g3 p

% e$ n4 ^& i K, V 补丁分析! I& `* r: w* o4 l- Q* L

) w) ]9 u! E0 e

7 a4 C8 M; G0 s& A# X' I  $ m* w' X( C: B9 ]

4 u6 r! }! U- Y3 n

. Q9 ^, t5 z7 P" V8 J# H6 m 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:. C. J0 M& W! }7 P7 a0 ^& c& [

& v! T2 ^4 G. x+ |

" J( f9 f% I& G7 R' n function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}8 l4 O: `0 K. } ]+ P% [

( B0 a2 v. d" p {5 ~

2 E/ P; Z+ ~5 \: u. N  + e6 K+ |/ Y& t, F9 r9 K% v7 s, z

/ _3 q1 z, ?3 Z( Q) d. @ m# a) D

4 W5 j& ~$ u! t. }# ^2 @% Q 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。( J, v( Y6 c9 v& z. k9 F' ?2 a

8 v' s' ]1 C4 T$ }! Q5 b

9 O/ m; s8 p8 M$ A 在is_allow()中增加对$this->savename的二次检查。9 Y9 m8 C8 K- [6 M; T' L

2 B( R2 N0 O5 a; U$ s

6 Z2 P7 T" }; [- c- u 最后3 v% ^6 ?# y% \( V

3 i! b' U' c [' V( z; B. ?( A

4 Y" u" x2 M+ K- d6 Z 嘛,祝各位大师傅中秋快乐! 6 t* A, x. n0 f ~: r5 v

" S, Z; [" w! m7 [! K1 e

" H3 i0 ~' Z4 ~$ H1 t$ t) M& n  9 E3 r, t: q: w' \) `' q

# I7 s' L, N& f, L$ k$ a& W0 [
回复

使用道具 举报

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

本版积分规则

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