2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。
: x, I2 `% I+ ?2 u! ~2 r9 R- @ ) p/ C8 O" I0 z* i8 H% m2 K/ [* }
; M( I9 q% I7 R; ^! q/ G8 P: ]( A; T8 s- V6 ^- k& ^5 L* C 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下: X" i5 f/ X/ O' a: t
对应着avatar.inc.php代码如下:; k# F& Y/ ?) ^4 r7 g/ j
<?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) {, |! u$ J' ~: ?. U$ Y! l' t
case 'upload':: N0 k7 Z0 p: I# y! Z0 J
if(!$_FILES['file']['size']) {; X+ W0 X# k7 g& Q8 u/ ~
0 y+ E6 V! w/ u0 K2 k+ X% w if($DT_PC) dheader('?action=html&reload='.$DT_TIME);' k% E/ M& I+ G3 S) U
" B, Z$ N1 H8 } h) p2 dexit('{"error":1,"message":"Error FILE"}');5 S" C* E* u2 g& g# z- a! a
}$ B6 M5 t( ^+ C! q- i% \% i( T( Z
- y7 k% f4 [) h6 ?: @9 E* p5 g require DT_ROOT.'/include/upload.class.php';
5 f, l4 u4 m1 U8 x3 b9 L! ~! V6 W, F l9 q, E$ S5 c
$ext = file_ext($_FILES['file']['name']);; V' _8 P+ c0 ?! r
$name = 'avatar'.$_userid.'.'.$ext;9 t8 r# T/ x5 Q7 P$ `5 x
( S! Z: J! K5 x8 {* F! d$ }# K $file = DT_ROOT.'/file/temp/'.$name;8 t" M1 B+ R; w$ b1 F1 R" d
9 N* a+ ?6 a* d% n' o% U) S6 j+ M7 u0 o* g2 v) n+ D& Q
if(is_file($file)) file_del($file);
% Y( W( W7 p2 f, J$ T $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');2 @ V& |2 @1 T; z
: r6 Y, ~/ U$ [& I/ P9 L5 M! y- q. V$ M $upload->adduserid = false;8 F: R. J# T7 J& u3 `/ R6 ^
; O$ J7 W( N/ t1 v5 p* V9 ?; N% ^ s. T7 C: X
' c0 @0 e7 [. S3 P2 d: N6 j! o3 X7 |1 Q if($upload->save()) {( Y7 Q9 T" y: Q( \
/ L1 k% R1 m9 @5 w- z( z) ^4 _* d2 y* I! W" ^+ u+ v4 e; a- R" Q* a ..., |0 i5 ~2 |. H: x
} else { {% l) b2 A [1 l
: Y) S% g, s: S* |+ {- d ...5 f) J: i# Q, T/ v2 J9 V2 i
: f+ |* d; Z6 Q1 o8 X! d4 E }
: h% [: }0 i* F0 s break;
0 O G9 j" p' I! c. N% r# `+ K& {# h0 P# X0 s# q 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。( @) \+ n! ?$ B# J: c: f* ~
upload对象构造函数如下,include/upload.class.php:25:
?, A, l. ?& X; o <?phpclass upload {
1 V* U7 C; w% P function __construct($_file, $savepath, $savename = '', $fileformat = '') {) h4 N% [3 u( h: L4 D9 ]. w
. }7 f5 J; v) t global $DT, $_userid;
9 f: R) R, f6 S+ K4 ~" Z5 j& F5 o9 s$ ?' @" E6 o foreach($_file as $file) {6 `/ j+ P8 N8 L: C4 `! `1 I$ ?* i
, o) e) s l$ R7 Q6 G+ C/ A% a$ Z Q- A+ C: ] $this->file = $file['tmp_name'];
/ Z2 \( l5 n" [2 |& m4 b $this->file_name = $file['name'];
) _9 q; ?. K, p$ E8 }1 J: { $this->file_size = $file['size'];
$this->file_type = $file['type'];0 Y( r: H, o5 h; T
$ g5 S! F5 d2 o; }6 Q $this->file_error = $file['error'];2 Y& ^% x4 }: S: P+ f1 ~
7 j1 y' z! w& B. x
; m" Y7 _) Y* ]* O }
' N: W3 E0 r3 y$this->userid = $_userid;" _6 a2 C0 v6 W1 N. _. B
$this->ext = file_ext($this->file_name);
}" d( R$ U7 z" |6 a" [+ t $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];
/ }( G# \3 T) d* i! z$ J( f$this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;
$this->savepath = $savepath;
# r& r- @1 f6 }. G( _& _ $this->savename = $savename;
/ l% I% J6 j! N0 l% I6 X7 B }}
9 T, P8 H! a) b' u9 K2 G! \# w- p6 W% D6 k% d4 A3 N 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。
" }. C: z5 B- a7 o6 \% K, B g, v 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中
1 z3 ~' c/ d' u1 [: y+ l: u6 R/ c/ {* m' ?; e $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
6 S* V9 |5 ]3 k5 V而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:, A- W+ O e: z/ K$ ~3 t: @" x
; \. P& `2 a8 V2 r( F* ~: @
3 X2 f4 U. M6 X0 C 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:
+ M; I/ \, Q* a/ O9 k' |2 t9 M. t6 k( g T0 {. t <?phpclass upload {" V/ J/ F P6 X. z% m
+ [7 h _" r% o1 D3 s- Q% W' u+ V7 g# Z7 X function save() {3 o) P/ W: [0 E; G2 v1 s/ r
. Q. H f7 B: n: Q. }1 X1 w$ K. t. S3 |/ l% g5 n1 h* r include load('include.lang');
7 H& \* g7 @) _* [# i if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');+ d. n7 w; A7 W3 a3 d. M6 e( i( R$ z$ y* J
0 @! k4 b. i: n( ?' d
if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');2 n+ |: c3 m, ]* [
* T9 Q1 Y. X5 u8 v
8 `8 T$ G4 G! d# z if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);2 F7 g3 e; b$ h5 T3 S0 r3 v
! }3 o" e6 I5 n. ^ $this->set_savepath($this->savepath);3 a; J; L9 G# L( q" x# W
6 ?; ^& B: s3 X4 H# A9 [" u* O8 @9 K6 w& E7 r# Y8 ]& h $this->set_savename($this->savename);; ` b+ A$ V( H, j5 o9 V
* Z- B4 W4 g% R& Q" j; g9 o/ D
. Z1 T; V2 ?0 w3 {6 S1 z0 D- h; v if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);" M2 u |9 Z! d2 ]5 k9 X; M. y' ~
! l- z e% n, p* y if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);
; Q2 @2 g3 v! K. t* `& F if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);
) m5 L4 F) c3 r9 c h2 X
$this->image = $this->is_image();
; k' k/ G! c3 D5 @, B% ?; G& Y4 v& \ if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);. ~7 [8 U; E9 h3 }* x% ~4 Q0 V
7 T0 k a" B) p* B* S) D8 H0 H. `0 c: S R- c; d return true;
/ [1 l0 M' h6 ?9 ^+ u' Z# Z}}/ D( a# ^; Q# O. o8 m
先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:
<?php" @' E0 {( ?0 s
) J8 R( j. P, \ function is_allow() {
1 F9 a. P- j/ O: [ V- B if(!$this->fileformat) return false;
" g" l& ?8 a/ G if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;
6 Y) R& y1 O0 _* sy1 T, s6 L( N2 A. H8 \: f. i6 N( L 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;
return true;) X1 N$ a( H. U6 ?2 z, b, e
8 [$ ]# j* K6 m1 }" ~7 G }9 ]; d1 W$ ~. Q6 m: m. r5 e$ y p
/ ^: y9 S9 b5 j( N% l, _3 ] 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。
1 y: w9 ^" ?, Y8 i* y8 Y/ T/ |1 M' \& r1 J4 @" H1 J( o: m 接着会进行真正的保存。通过$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 Z- j% T8 m4 i" ]- } 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。5 `& I! K: m, M, W' X0 R' T
# `2 H. c) q( i/ H7 a: z! s6 J
- g7 E V2 c2 ?0 A# Z 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid
% d" a$ Y( B- @6 T2 v不过实际利用上会有一定的限制。
. j' P# \% n) K) J f [/ A/ e+ O 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。
( u% p# M2 u, Z0 S
8 z' N$ l- H3 [4 U0 n4 r1 |2 g2 s 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:5 `3 R6 z, {' k5 O, K
+ _6 c- @* W& K7 K 省略...$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]);省略...
因此要利用成功就需要条件竞争了。% v3 O$ {+ t# }( M6 ]8 r
; s$ t) V" o# k8 A! D/ y
9 D$ ~, x( B5 ] 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:
. \) X! w' U0 Q# [/ l$ d& G* ^/ r: P$ X' B* U5 _8 P6 @# L function is_image($file) { return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}. e0 N; o& R5 L
9 N% z4 y l- `7 [* U, \' r1 l5 s 9 W+ V, @2 }4 i% G- ~
$ F; \# K8 e( d% K7 T- s/ @ 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。8 s5 c0 ^) Z/ X) {' g' c: K5 Q
" T6 @& L4 S+ n; w0 M% ]! z9 v% v ?' ^ x, N3 @ 在is_allow()中增加对$this->savename的二次检查。
4 b; s) W# k. \' G4 |# ^4 a- M$ x u) t' d; \ 嘛,祝各位大师傅中秋快乐!4 y [# ~' ~, n! R0 e
: I' t. _& t: n; a9 z, ^1 t: t# Q
欢迎光临 中国网络渗透测试联盟 (https://www.cobjon.com/) | Powered by Discuz! X3.2 |