2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。 `) W4 r' |) S X
, g5 H& J; o9 X8 G6 _4 }, D' s
& i) e. n: z) p2 f/ f 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:6 z# F$ j% a c; e
& K9 r0 Y4 j0 h$ L4 d$ U
" U, x5 {& E; E# e& |
5 \5 I5 R1 n% Q0 q 对应着avatar.inc.php代码如下:
0 f3 n O9 a7 I; [* ?! y# z <?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) {# M; c. o! B% d- b2 P& M) Y
/ ^6 \# o2 Q* D2 z1 k. b8 P9 \5 \8 r0 z/ r; L; D case 'upload':
+ s7 n/ u7 Q% e% K' Q% X if(!$_FILES['file']['size']) {
if($DT_PC) dheader('?action=html&reload='.$DT_TIME);, b9 W4 D6 l) x0 `: p, t: t
" [3 }+ Z" j7 { exit('{"error":1,"message":"Error FILE"}');' b; C/ `8 W2 j6 Y
$ ?9 L( {7 i2 m+ b( ~* l8 R } j: ^% j5 v1 [9 @. ~/ p% w
X4 f+ p" `9 W% W. B5 U1 nrequire DT_ROOT.'/include/upload.class.php';
. z6 c4 K3 U, E - n0 g7 n* d2 Y, ?) i& [
4 O+ b7 j4 Z+ v4 ~ $ext = file_ext($_FILES['file']['name']);
: k( [) N$ `1 n$ W $name = 'avatar'.$_userid.'.'.$ext;
5 {& u: k. b5 l& |# x& m) B$file = DT_ROOT.'/file/temp/'.$name;+ E0 F5 M8 [7 g3 E4 B: _- f
5 t% ~" l) j$ {, Q1 ^8 R if(is_file($file)) file_del($file);% T" X3 J4 E# O, t
" z0 d) b& v( M1 f6 { i0 b $upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');2 h8 ~% I/ w" O' J; L, ~( e
3 L$ {3 H/ A* X; m4 S* \5 d/ J. {* L , z L0 x/ @ P' ?/ q9 k/ c+ y
6 Z1 `0 m d3 ]# Z3 R$ j. d9 t- f; Z $upload->adduserid = false;
0 a$ l+ [2 _/ o$ b% B/ ?2 y1 B2 w 9 k5 u' \# f5 f3 R6 w& o2 m: ?
! v# f2 H/ x- A; [7 l3 r$ [! w( | if($upload->save()) {
: e. q& A2 k ~& r+ }2 N- ?, G3 A. o( I$ M! b9 A8 e& {" j ...& s& `3 y/ e7 ]5 ^
7 P& O- G9 ], K( l, u9 { } else {; i5 p0 @ j9 E
* J5 q( @7 O, o9 \p4 w; I2 L1 n! w9 ^ ...( J, @, w+ h# G/ H( e/ k$ |
7 ]% f/ x: b% _" x: d, \- ? }
" Z# W% R# M, m" L$ e6 [* G: o \* b break;, x$ ~* Y6 Z& P6 {# s; O6 P3 P) V
# C6 X8 p2 {- A7 H8 j# E$ _: V0 J6 J* u: l- O' B: @; e; G e 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。
upload对象构造函数如下,include/upload.class.php:25:6 z! p. E7 n. ]% O; y
<?phpclass upload {
& b" U) K! g3 {! H function __construct($_file, $savepath, $savename = '', $fileformat = '') {3 {: f% O, c3 s& D
, x4 u3 \+ v- T; ] \8 Q: ] global $DT, $_userid;
foreach($_file as $file) {
% V$ q" s; F/ e' ]7 h* q% ?' C $this->file = $file['tmp_name'];' L8 l, h& H2 t3 X
" V- u3 E! }2 S6 d $this->file_name = $file['name'];
* g8 G" E3 ?& T7 j. w $this->file_size = $file['size'];7 x9 q; C e# d5 D
$this->file_type = $file['type'];
5 f; `) f9 w: P4 _% d6 \ $this->file_error = $file['error'];
6 |$ k% s5 O# u5 B; F H& |9 H, N3 k# [2 {- k4 P) v1 h
}0 _7 ^+ e |* s, @* L4 ]$ W
- }8 o8 c* l H: ]) ? $this->userid = $_userid;0 r# X3 t4 o! v8 F$ w5 B
- H; z( ?6 Z0 e0 L; l$ `9 p% r $this->ext = file_ext($this->file_name);
2 V( }$ A# f; ]; o! e# O $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];7 ^# {" _" b& u W- m
0 D5 h3 N' w; WP; ~6 \+ C$ ^& h' T $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;0 ^+ g. q2 T) u' o/ s$ Z# M
$this->savepath = $savepath;$ c( S# n) K! ^" Z3 m- o
' C+ G# \$ h0 _# y1 D9 } $this->savename = $savename;/ E! e# Q% `# g
8 o' {: w4 y6 N: J! y, k" l9 r1 j( r ]* n+ w1 } }}
; g* \4 z/ T3 V. c |1 @6 R% `" T1 b, ]# \+ r4 u 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。
" N8 o/ X* ~7 @1 @+ I1 s2 q$ \5 k 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中
& F+ [* Y" ]; V7 ^2 [5 a; T. W& X( ]9 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.php2 l+ c4 ^5 a" h u" v, f
! Z- L2 f% a. Q5 ?! ~3 B# o 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:$ c2 Q8 `% n1 [- U+ F+ [7 y
5 Y- Z( }/ a4 q& o: J
" e, T8 o: }) h* N- Q l 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:' t7 E3 L1 p- r6 e. f
. j5 ?" d# s3 v& z9 C <?phpclass upload { G. b! w+ s2 D; A) @
1 q4 ~8 L% |5 ^( P* ` function save() {& p7 H! R* A( s. J' r* f
' b% `. Y. ?9 \5 v' U9 Y: q. Q include load('include.lang');* b: x# _3 c8 K8 `1 C
2 J' i J& k4 k/ s6 `* Z1 u$ {: b& c# M, d* L. d/ M if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');# X, T$ z) a1 {& F
6 l/ n/ m2 o f3 m5 `/ Q
& e0 E5 i. q' }9 R. j7 h- R: Z; I% T3 f* h* [1 ]' u if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');: J$ p4 Q- A r& h
7 q5 w* R5 {. y! b0 p- P
: Z8 B+ i" @8 R2 F( f6 Q* o if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);
$this->set_savepath($this->savepath);
M' X, l/ E4 J $this->set_savename($this->savename);
; I& z" t9 v4 G4 A$ S9 r- D# u; ^0 i 9 n- @; _2 y: G& i
/ `# y$ ?/ K; F. v2 o+ d- V* w8 n8 N$ _* P9 ]' l9 Y/ C if(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']); E6 Y. X0 v8 Q$ B2 {
. o) f& k8 Z2 b# H, U4 @ if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);' y: N# T4 ~* U
if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);+ @2 K6 K- w3 c8 N! `4 f
% {$ C) F: w( n; Q( u4 ^; l + x; O7 w. ?$ c" V$ H
' K: Z0 O0 f) v) ]' y# \ $this->image = $this->is_image();; O4 H% F# h1 z/ _+ _3 |$ X$ c
) o- ~& \0 l8 r9 n! Y5 D if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);5 k0 b8 [( K* f0 l7 V5 W
return true;" K, M! w i* Q* z
6 A4 k. o- M0 L9 S5 f }}
9 H i! c% b5 M" l O 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:
1 f" |7 A8 q& X9 U0 |<?php
4 ^, K/ j! L7 h+ {7 J3 L' n function is_allow() {* B! z% w' G# c2 ~! u
: ^2 Q k, H6 j# E3 f; B6 e9 e2 l U* Q E8 O5 M7 k if(!$this->fileformat) return false;, b4 w; P3 l6 T/ b# L
* _' I0 e6 x$ C+ I7 z2 o9 ~' I if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;
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;
0 L' Z" [0 i8 B$ R' _ return true;
}
& H" N9 @2 }2 V. }; {$ q" E u" E 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。& o% o' ^: c. ^
! y3 ~4 z& G: D" 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文件。( O# R {/ H7 l; }5 n
$ {7 n1 l3 f g; A3 h; m7 n& D i7 x3 E8 a 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。
8 E7 `) R' A& v; @3 B; W& e2 ]
# J# \# C4 t7 h
) m1 g+ o7 [" ^9 `4 v 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid
6 J2 r8 [8 B4 D1 p. ` 不过实际利用上会有一定的限制。
2 \$ q9 S# \( u# x 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。6 f7 c# o* B: N: k! d$ j- t9 N5 ^5 S
" F3 r. `& A* R
! Y: f$ m$ J a. I7 u7 J 第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:! k& @9 L+ J7 T: g1 _* o2 W
: S- M, D8 G; Z$ }2 \- t 省略...$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]);省略...
因此要利用成功就需要条件竞争了。
8 @0 J) D1 z6 x6 H, x# w
- h' O* B! l8 Z7 @# ~; K- ~ 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:3 M9 S+ ~3 p# U# g: d f; o
6 w" l- b, o* M2 p& w8 q# ]1 `( ?3 ^$ h function is_image($file) { return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}
6 y5 E8 k% p3 ~+ i1 q) k# ?, W( f) @# L0 K
在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。
在is_allow()中增加对$this->savename的二次检查。$ s$ M, j1 g- N: S
7 q& x2 a) t' g3 F4 L5 [8 y 嘛,祝各位大师傅中秋快乐!# w+ Y# l! R# w
- i5 Q1 A- Y+ G # A5 X9 w; o/ p) V. P% o8 C
欢迎光临 中国网络渗透测试联盟 (https://cobjon.com/) | Powered by Discuz! X3.2 |