中国网络渗透测试联盟

标题: Destoon cms前台getwebshell [打印本页]

作者: admin    时间: 2018-10-20 20:13
标题: Destoon cms前台getwebshell
2 K: e) N& y% E. q& x

/ u0 v. R3 s- }- ]2 f

" d0 C. ^# a% D6 X" S3 u

: ?+ P0 o; q* [! P( d ] 前言 1 L$ N4 B' x; V# O9 T

! ~4 y% A& u2 b8 y! M

% B: n3 `$ N! ?' d, ]* f1 G, b 2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 1 E* q R+ N" l8 z3 h* A

; B1 j6 H4 z0 [* P7 t& w$ a

' \* c, E5 ?9 Z! F* k  7 G5 Z/ V) L6 h9 c ]% I9 X5 J% ]

) A' z( w, N: x1 r6 `8 j1 I# K4 |! {

( Y+ ?- |. Q7 Z2 Q. S 漏洞分析4 {5 q+ u- Q5 ]* M' k

) w' x5 x* X4 e+ L+ C, r2 ?

$ I. D' k- h' J, K 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:2 X* T7 ^8 R1 e4 C9 ]

2 I S& R C+ ^: R' L) T! F

# Q0 W& f9 W& s   8 Z, A# H( Y; L( s$ k

4 }* Z& I$ E$ ?4 l9 v

# P( \: h) C: s, c; r0 p) t& e* G 对应着avatar.inc.php代码如下:& u& K" ?7 R3 A' M7 g

" j1 Q2 d W* G+ J% }/ c" p7 [8 u

/ P( D& F/ u7 Q+ J3 s8 _# @# b <?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) { ) z9 ?2 z+ M; w, W$ f5 J7 G

6 G& q- d+ M7 d. n3 k

& H/ X8 o5 Z' X2 [$ I     case 'upload': ; B7 k# P7 T# |# j* O

" H, W5 n* F7 S' x5 k5 z- y. V

_- \- @; D! T0 ~         if(!$_FILES['file']['size']) {- [0 }5 w3 R# x

/ E9 V0 j- `! K K5 p. M

. b! c- a# x$ Q! j             if($DT_PC) dheader('?action=html&reload='.$DT_TIME);/ X& t5 G' j7 a4 E# a% d1 W

. k' h0 k3 } ?5 ]& P8 }) N

( \ _1 c7 p* ~7 O             exit('{"error":1,"message":"Error FILE"}'); 5 W6 b* g' I. ]4 M; p, @( {

5 G. m; Z* K( r) Q; V

( j) ^# C; A' t) E6 P0 T         } P5 [. `0 q+ R2 s2 ?

1 s. b c( D& ^& M

9 j7 k0 J' J; o" P         require DT_ROOT.'/include/upload.class.php';2 R2 A* R. B8 ?. c1 A& O' F2 k0 i

% F) H2 p2 Q6 y% \

6 b$ Y6 k0 z$ m/ q# O; _8 y   ( {1 Q# N8 j) T3 R% |

! f3 j/ _- U2 H( A

* D7 L% S! m, V. p) ~' t# g! m I; l         $ext = file_ext($_FILES['file']['name']); 4 I" ^" K$ ]. S! K

3 S; y$ L; T4 W

' w, e7 s/ x$ A! A; ]# Y         $name = 'avatar'.$_userid.'.'.$ext;. ]% w5 H0 B! S# X3 x1 v

4 l P5 J3 H: b4 m' \( g

) C# ]; M0 A* k$ p; i         $file = DT_ROOT.'/file/temp/'.$name; 9 _9 a- i% Q) h5 G

' T/ K% q5 W+ D

7 x9 p$ a7 N) `/ u  ' G& X9 g9 P: u$ b6 L, E! g2 C

0 u* v+ C8 l# \

$ r6 C- q8 h. W! @7 h         if(is_file($file)) file_del($file); ' U* e; P; H% h$ c/ M% ?

) K$ j$ P4 k$ g9 h) ]

- q( U$ U6 @1 I1 E; I9 W- k2 {+ I# v         $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png'); 0 c) I# O, l0 q

5 _) r9 h/ u h1 c7 p

' I; k, t% S( i. H: q! K0 S  , W1 v u( _: @/ ~ U

$ M: L5 w. _$ v4 ]2 A! y

' B* T# O" O! U. A0 ?- y         $upload->adduserid = false;, p, K) y) {, c2 u' ^) h, _

" o7 z0 l- r" o

. E3 \- o9 D# M( ^. W   5 P) f: h/ L6 i7 o/ |4 ?" \+ @& K

; A+ }& i; D* \# S$ A

" l( n2 j; p: m2 J. P) X; v         if($upload->save()) { 3 ]' U! e$ V. w5 S( l9 z

( W" d' n( R% D& c; E. Y$ i* z& b2 x2 R( [

% e4 u4 P' O. O5 y1 c             ... 2 {( \ F2 d3 y1 l/ c

9 _+ ?: a6 K: x+ a/ r

$ `9 V9 M$ j, _- K- |2 S         } else {! j, p& I, V; U+ B3 M4 b

8 s& s$ W3 T" d" e2 C: U( Y

4 U5 _8 G; B7 n+ G0 |; `$ L             ...2 k- \+ x6 v+ m1 C1 w

$ Z# w: t! i) I4 O1 d& z

6 ?* q# i& ?8 E& e' V" p6 r, R. n         } % L9 l0 T+ g9 F7 \9 h

% C" [. n. r v# ?

; R9 m! S D: Q0 `# ^" `" y     break; u0 R( ?' r+ U5 S$ ^/ `& C

6 p( ]8 M8 U: h: C. }# X0 Z

8 x( w# a# `2 \! W* @' E 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。 8 i L# y1 i7 m$ d

) b7 h9 b1 W( W" v w! ~1 U1 l* z

5 L# x! R/ t4 K+ @4 f$ a6 f upload对象构造函数如下,include/upload.class.php:25:6 ^2 X* \$ E8 U: `) K% q+ q

+ S% u9 J" [2 W# c. ]

3 X5 x# D1 u3 t7 s ?! l) H( a" Z d0 | <?phpclass upload { 2 p# F9 t0 B: e2 Z, w$ d# A* e

; }) A: g8 j! ]' _' q0 o4 o

- D. J) Q3 s3 E$ r" S/ _& I8 e     function __construct($_file, $savepath, $savename = '', $fileformat = '') {4 w# `. M0 ?0 P1 \5 P/ G0 }; ]

& H7 Q7 V- \ ?" F7 p" p, k, O

& k: Q9 {, ]2 H         global $DT, $_userid;0 [, A" B9 x! y, t

- a1 Q' u/ Z! k% `# a

8 W0 ~ A( g! I K8 A2 L) d9 U         foreach($_file as $file) { % t" \! `% k5 H* o8 S

' w/ n* [. ~' H O% x

, u1 p4 w9 K& q- j             $this->file = $file['tmp_name']; 8 q7 }, B( T9 v- l" T. k9 @( y

* f* X+ K& `. S7 o% H' {

: y; i! A* I$ p r0 P1 U             $this->file_name = $file['name']; # _: e, W- T% w% B. d

. G( y9 o8 X7 K2 H, N x8 [

, U! R! n$ H# T- d             $this->file_size = $file['size']; 3 |: ^$ W, E' t) [

5 f: o& M$ U! P( Y" v* Y

2 y5 L( W" V8 W: J7 v8 j             $this->file_type = $file['type']; 4 x0 Q- ?6 I$ b" E4 E- d; r: i

. t8 `7 ^5 G5 y# p" |; [& W

+ g) K2 }+ u' m! [6 O. z, s             $this->file_error = $file['error']; . X' L7 j4 W- }0 f, U* F" z

$ ?7 b- A& |+ ]0 Z2 h

4 _7 M1 D/ X+ g2 J, v) s1 K, c. J   + N9 }- n$ V5 ?: V# V

- j' W: U$ ~% ^' w" Z

" F* a# m. t' I. Q         } % l& J% D, t" m2 I% ]) Y! V

& L7 H+ y6 b" E" Z; S. t1 `! ]

w2 |9 c3 M- A6 R         $this->userid = $_userid;. Q5 I9 v; [ @& o

9 @1 Z2 n7 {6 n9 a1 Y% T

. P& J' G* k. W! o         $this->ext = file_ext($this->file_name);+ }* L$ n# u* Z3 e/ b% M4 z

7 }4 L4 h$ f; f" Y' n8 p, E

+ r- P! d. v* g7 s+ z         $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype']; 9 z2 _: k- _4 `1 D/ p

; W# E }" {9 `

: \, @. F+ r6 ^+ I         $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024; 2 M& F x) V% z: o2 a

. T# K4 g, v9 j3 c3 B7 |

% F- P" b; c7 `& S5 _- }8 s' c         $this->savepath = $savepath;) r% X# c2 M {

* @& N; S0 i3 w. n- M% ]

+ L4 D4 ]7 U6 p: o. _! A1 D" ^         $this->savename = $savename; ' ^# k/ s+ P9 ~7 ]& t. q% d4 u

1 X3 U8 a) k9 k6 a

7 P3 D4 u) |2 ^$ o0 j& r0 W! Y     }} ( Q& `9 t: g; H9 Y! ?

: x4 K" u* r# W. J

; _6 s% r W, q( l 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。 ! W/ z5 f, \7 k: U

; {# ~% F2 [+ q1 ^- ?% n

. C5 n* I* o f' ]7 H7 x 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 4 o# I& {, Y9 M& o: _% B

7 L( O* B/ [0 ~

: M2 t0 d! p9 w2 ~0 B% L/ b7 t $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 L8 m( I/ t! `0 |9 H+ t$ M. `& [

1 `; ~0 N/ W2 L. i e

9 I" e! f+ O1 s5 M 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:3 m8 ^( y2 E3 F- {, h1 p* o6 M

8 J# B' n) t/ e, v$ [3 ]- F6 R! O: @

. P6 ^7 ]: d7 U  8 ] x) ^$ _7 u

5 L3 o$ P' L5 @# O; k

, {8 s% a2 c1 k+ X4 d 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:& q% ^! B. _- A) P m- ~. O3 q

, w, p: S) o+ Z) T* r

4 P1 g. b% ~. O' K T, d <?phpclass upload { : @: v) {4 a" b

( X9 w4 Y* F$ W: L

* l# P: m' Z6 o1 _3 k     function save() {8 ?, f* F( _ t) A

' m `5 ]6 d8 W' i2 r9 Q0 u

/ Y# S: `2 L' T; p( X         include load('include.lang'); ( {& d" e; @. L& u( q7 r

3 t; A+ b7 X4 Q1 c4 S4 B

2 A9 b. x1 O5 `- B0 R+ ^9 t* r         if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')'); $ V8 t5 x; U3 F* \( @* i

4 k* f5 c+ w4 d0 r$ a" T

* i# j0 ?" E9 v8 ^" I6 H  9 L4 J; I4 y8 Q4 M ~5 x

* Z& |: K3 e# B- n0 D/ x

/ Z# _1 ~6 K# n/ w* ^* f" R         if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');- R. E% h- X/ H! @' f- c1 {% p

% f- E5 k: p& T: N+ g b

2 z" E; x- J# T+ J; \# m  , ~5 w# ^& e( r, O8 i9 i4 q

' ^6 d9 B' z! a

; H7 T3 I' c* V" R8 m         if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);$ ~% ^2 X& f- W( @& u: v

: |" j3 D, M! }" X9 S8 |. k6 K

+ q5 ~' X+ r5 r* }   8 L$ d9 h$ L5 q v5 G% w3 G, i

+ m" R' \: Q1 K5 `/ Y

: o0 B: A' @( U$ p         $this->set_savepath($this->savepath);6 s3 H3 W: \+ d6 ]: g5 o

) l: K1 C7 n5 X( L

* @; Z8 C _( t( h) i         $this->set_savename($this->savename); - _7 q& c `0 L$ H. `2 M, m f

$ w; U; R5 {1 X- m1 c( j0 ?) C5 y

% k0 ?: s2 ^5 a' H/ D* g  , _ L& i3 Y, O6 V9 T/ l- G' M

3 F7 D& g) H; \: a1 l2 T8 K7 s

+ K* v' _) j& v6 b         if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); \ y z4 ]0 o: }

7 Y) T7 N9 r3 \* i

- `) D; i* Q9 y+ ~         if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']); & L4 ~" t7 p5 a4 z* Y8 a: L) b" K

" [. x; H7 `( c9 b- O6 f5 y5 J

5 s7 ]; g/ i% N         if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);9 K. S; L+ F) q

8 G* o: P$ n& ?1 E% z& `

% {. k7 W- r* r$ b1 y   0 n Q* j: i4 C' [8 G$ J

; D' j8 J* i7 o, y5 D* @

; e- Z% [% S/ z ]3 m8 A* n0 v         $this->image = $this->is_image(); . l3 F2 @% ?# a( t

4 m- \( l9 |2 K- ~. E+ r6 \

6 b7 ]0 k4 x" d1 W9 f# k         if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);% g) x' C1 y! ~6 Z( A* \" Y. [

7 `" F- `5 i' R1 E% B) s

. u; Y- {* E' D8 ]         return true; 0 i2 S% z8 l7 }. k

1 G5 B4 G5 | E/ F: r4 q: ^$ j2 H

5 g# h u( Z3 n0 `     }}, m; D. k) {! |. A& @

9 C8 n( T$ V; z* H( V

# v; M- X$ @8 [7 n- i0 ~3 Z 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:, _ Q+ ?* D* G

1 N$ o& r- E4 x7 O* A+ ^+ o3 c# c

4 u' J9 y X8 y <?php ; J4 E0 H; q1 R* h9 {5 H. e

; E% t: [6 z( m, x0 G! T# b

$ f3 s9 B, b; A T O     function is_allow() { ! B( @' B$ h5 r& c

; V9 P- m& l6 M3 W, ?

! G4 B5 n3 G: e* y         if(!$this->fileformat) return false;8 O3 S: C F2 Y: a& Y8 v2 N

# Z' f9 I5 @9 Q

/ }7 H$ b! X- x* P0 K' D         if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false; ; j6 M$ L5 X! O1 f; y# @' l4 c

6 n+ y9 Z7 k' u. u& b5 k9 n% e

' Z9 K. ~) R O) d& M* W. w         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; * Y$ \. d4 i3 A/ c2 B j% f& A

4 c |6 b1 N, }; l# F

: M- Z; P: F7 I, P3 v' ]0 z, M         return true; $ O7 u6 {1 @ V4 c8 P5 l$ L @

9 } L* K& n" i, c# W/ s

2 e; m) z* G6 O1 S2 C     } # @* \- u: D1 f6 z& t6 s

; f' L' A5 z) B. H0 ^

5 ~3 v$ n! y0 k3 ?1 e* k; [; y 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。( c4 ^. s' @$ n- B1 z( y! r( G

` S2 R5 ?8 _( A) J3 f

" n4 \) i3 k4 o0 p 接着会进行真正的保存。通过$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文件。6 |/ w( ?$ }- I1 @" {. w

2 A2 l J0 s+ V$ e

- ~ w& B: d6 t 漏洞利用( i# N1 g q" y! X/ _1 s2 n7 J

: S1 B) D# Z: Q, b

$ r+ e0 e# v; K! f$ m1 W, ^ p 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。 0 P' Q. i2 `5 A8 S

~ K7 z& r0 }. G, c" d% _

4 z7 T& D# ?; ]2 J2 J* w; d   9 a o0 K- {6 a" J

; [, _( Q6 g& I9 S0 k ^% K1 W- u

7 T8 @% U& _1 G% t7 F 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid% t' k4 n0 Y2 e# C u5 Q

% S( b! u! ~ M* Z

2 r$ ]1 y+ l3 b7 X 不过实际利用上会有一定的限制。0 J7 n1 I0 u+ M) k2 a) F7 e

4 b$ I4 s; M! p' g- l' Q

: m/ E7 m( R$ }9 W* u& U! h1 ^ 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。. h5 z" T# A/ {; o6 p- Q

8 ~# m& A! R' u0 K

# C+ w. |/ A/ b; j8 M4 `- _   z3 s1 h5 M7 T- ~- `0 f# @

/ Y& ?$ H E: V2 J

# J' A( K. f" Z1 | 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:9 a0 H' }1 ^8 Z5 V8 y: l

# r! c% u" o' n3 b2 c' {

/ O" y9 D/ E R$ @- s; ^& d 省略...$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' |: P# a( k4 e- U7 u6 {) ?9 j& B

% v/ }5 [3 o% w, E! R3 W0 K# a

9 g5 B8 J! D L) z2 \! p9 U 因此要利用成功就需要条件竞争了。$ W% ~, Q5 E( ] L' y) A+ z

1 m8 y. T! x' n7 e( @5 L5 ~) N3 ?( }1 q

9 L4 N9 W* g% }5 U0 M& T3 A 补丁分析 . n" m4 h- \) Z2 W, r/ p

6 }8 _) L2 ~, a Y) |; Q4 k$ S

5 a" D% G5 `# l3 g- | q  8 }3 \" Y- K) h, C9 O" T1 e

$ v! j! a* f9 |- f( Z

, l! R1 |2 G8 i- ^0 Q9 k7 P( W 在upload的一开始,就进行一次后缀名的检查。其中is_image如下: 7 U7 `7 W7 l- j B' o3 L5 k! ^

/ X. E3 p# \, S0 A

) M8 E& T/ j& F function is_image($file) {    return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));} $ R# E3 M) T. _$ w6 j% u$ O( N7 m+ H6 X

( E' q8 a/ t2 Y) y' O w

- g! S0 P- F" j! o  : J; n" W9 g" l8 ^" F, I7 Z" v, l

) {4 ]3 z8 Y" ~+ ~

# f0 T5 t0 \: h; |1 ] 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。 6 R3 ?+ \. R' x3 O& ]

" N+ y- y" U1 b2 m5 P6 w- u

0 a- P @' v( g6 Z2 P t/ b 在is_allow()中增加对$this->savename的二次检查。9 @9 f4 J6 S' j

% e1 {1 x2 Z& n+ J

0 Q" i7 [# R, d 最后& {- b6 q( J* [! S& h' g- Q& g

8 @4 |2 x/ H. D4 o* r' o/ f( H4 e

7 w" a5 x( F- f! t5 g, Y 嘛,祝各位大师傅中秋快乐! + l7 W7 x* n$ G. Z

1 o. e# R/ {% V8 w! S" S' l- @

1 ^) g& C% f6 Y   & V/ Q: _5 u9 b; Q4 S

% @; @# z& Z, R0 p" j





欢迎光临 中国网络渗透测试联盟 (https://www.cobjon.com/) Powered by Discuz! X3.2