2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。
# P7 n+ }+ _" w* N: @" }
" m% f8 J E# E0 N* D* D: U1 ^
" z ?& d2 E% v 根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:
. ~" n6 C: H3 T1 |- ^' M3 R% z# |) V' ^1 `2 ?& G. _
0 I3 p. _) W2 @. o0 B 对应着avatar.inc.php代码如下:
9 k3 p: T( v+ {+ j' ~. o; G ^7 s) @* n0 G5 H <?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) {$ j# t- L1 J+ X$ w( ?' m2 m8 |0 w
case 'upload':
if(!$_FILES['file']['size']) {& V* y3 Q. [# L
1 s7 d$ r6 t! ]- c0 z, W: V* @ if($DT_PC) dheader('?action=html&reload='.$DT_TIME);# q( H/ i$ K% ~3 b4 u, d* t
3 _8 V; E( `0 E' t+ l: Z9 w7 P* M* K8 f1 l3 K( c exit('{"error":1,"message":"Error FILE"}');, z0 B7 e4 T0 n; r" y
6 W3 k( z: ^3 g) \" |3 c }$ e7 B% h1 U# ]6 m. J5 c
. }1 v3 d- y1 Y' Nrequire DT_ROOT.'/include/upload.class.php';
; u- v2 d- e7 h9 [, ~
0 r9 J9 M; S x- A) b" Y5 C8 \$ d5 Q+ d% K: l, f+ O $ext = file_ext($_FILES['file']['name']);' [/ y a( X1 b t
& c8 s' B* {5 N% y$name = 'avatar'.$_userid.'.'.$ext;
' q& b# D7 ~% l: J' Y, d; N3 Z4 z7 _7 L+ H c. ]& r. ^2 e $file = DT_ROOT.'/file/temp/'.$name;
- m( [8 _: i" p9 v! X8 t* D5 e6 Z/ g% k- x' ]0 i4 ?; {" e
: L1 ]: a* c8 s" y/ c7 ?: X+ d: Y0 L6 T% e' Y if(is_file($file)) file_del($file);
$upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');% w8 l: H# k. ?0 Z; R0 v
- {, Y- `) d& _3 b% W! G/ b. S2 k- Y1 A9 C* r) o" {/ e- T+ K1 h 7 T. Z2 Z7 G2 }$ L
7 m0 R+ f, @% X/ g: D $upload->adduserid = false;! z! l$ f2 K3 Q$ c$ t$ _0 W6 p
" [0 a! l! s8 z A) Q
if($upload->save()) {
' W# g0 P3 [# u" p; r4 m2 l}6 v8 K& u# a! b4 t/ B0 n( M ...( r5 t& Y$ J5 J. y# P9 k
} else {4 X, g8 C' r5 h
...
3 }0 a2 _- o9 e" W5 p7 q! T% ^5 h2 K}8 h- P2 ]2 C- b9 h; d& m, N
break;; X0 B7 w9 K# j2 }9 }6 R* N% j
0 o( C3 O3 [, z) x0 S* Q" ^$ T! _0 l9 G3 T S1 M9 k0 n 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。8 B( C# O' Y+ u" D2 n! Q/ y3 O
upload对象构造函数如下,include/upload.class.php:25:3 s- Y. i, j- B W& N
$ `" W* Z6 S! n4 T, @. U<?phpclass upload {$ q W$ K% {5 h( W
# Y! p# c; O# b) ?" d$ I. [4 {function __construct($_file, $savepath, $savename = '', $fileformat = '') {
4 ~4 z' j+ [: x* k+ t! k2 J0 ]1 s; q4 P global $DT, $_userid;
, ~" y) z+ R+ F- S8 t2 Oforeach($_file as $file) {7 H0 T* r, e& }" j0 n8 q5 N
8 E! }5 L; r' d/ J; w" c7 E $this->file = $file['tmp_name'];+ M- J) Z" C1 W, [
$this->file_name = $file['name'];
: f. l F% k) l6 ? $this->file_size = $file['size'];
. W6 x/ |5 A; J9 Z5 V' p0 U R) s. \6 a $this->file_type = $file['type'];. K2 w' b7 {7 K0 H
2 L0 P6 [) f/ F3 |8 a, s. E$this->file_error = $file['error'];
J0 D/ H* q! a& x) C+ i' r: m: Y3 P0 N( P
1 }0 E) C4 s4 M* p h }
6 o: H& E/ C7 _, W) B7 l: q4 g2 L$ G( t; { U, ^ $this->userid = $_userid;
$this->ext = file_ext($this->file_name);
. o1 ]4 {7 Y, s% N0 Z $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];
0 a7 r# H: [6 e* a1 R5 {& |4 b& c6 J $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;; |. `: y# E) [+ r3 K# W
3 G1 u* P& o% _8 W2 s M$this->savepath = $savepath;2 o7 k; J0 }5 ~/ a: W! z
8 p: X2 k/ _) [" L/ D/ K/ q) U& R# u* H& x $this->savename = $savename; z0 T# u3 z8 h2 p+ l/ Q
( b- Z* z( X+ b% H5 k1 L }}* L) I% l9 X. a6 o ]) u1 }
4 F O& N, y1 }* p 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。2 n5 _( J. s$ s$ n9 k7 Y; h
! \4 J& n! X, p1 n) `因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中 6 T' U" M j3 G5 i+ c" Z
$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
k; ^1 N* H: R! Y7 O% x1 l8 u+ r9 I! x! C 而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:8 L" B: s: V8 O) Q
# t' d# D8 K) M2 A0 d
" Y. A1 V3 ~" q) ]) `! J- b 回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:2 d. j' n5 ?! }& ^
& z) @3 Z+ U6 U) J <?phpclass upload {- N; A' C2 K, }' S0 g# U
* ^4 C2 n/ B: r1 g* Yfunction save() {
4 h5 U, Q6 {/ }' \* Einclude load('include.lang');
9 N8 |# m: Y- u) J. |# [% X6 [if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');
& ]; [4 N0 U4 I' i! Y1 _ ) I0 ?4 k3 q6 e' q' s* b+ n
if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');
& }" t! P+ `; y. z0 t. Q 0 Z1 _2 K) N! ]$ u0 z1 `
2 [* }+ |, [% e' g+ n6 ]* j! S6 W( } if(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);- [! R4 l4 Z+ |! V
& S/ I) f7 v9 M, M, H+ {7 A) y: r( a9 ]
. g1 N" l1 e; y* ?, J) q6 E $this->set_savepath($this->savepath);7 G/ y8 ~9 R# q& Q5 q
% `9 t7 ? o: ?$ v $this->set_savename($this->savename);
5 x5 t u/ U/ y( `8 W|/ n P6 L! {! d" v1 B" V: Y
5 f& ]' [& F* E' U5 C E6 Qif(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);( ?# h5 C8 G, M* \9 r) R
% I" M; a+ v; c, @- X" f# I if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);
" j: o& O$ ]' \ if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);
2 P5 P8 C9 V; @( e& W* j; c! j- w/ j7 s
3 q9 ^; R0 o% h3 N# D/ V$this->image = $this->is_image();2 X% \. o; v1 k* R A( C
) A, T* ~! m1 h+ @if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);
8 F' X. h& q( Ereturn true;+ q, F0 I( ?/ W1 ?7 m" C7 u q
$ ^, p# ], W P- e# d f }}& U+ ?" U: A! m3 X* L4 T
- J; \$ X1 O$ }, _, L: W- g' x 先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:
; Z9 D7 x4 u+ l! ~8 y, G<?php% f+ d6 d% e( M1 M' y- j9 r
function is_allow() {4 t+ k0 f) X$ j9 g3 X: C
5 t- J% Q- A/ Q3 K( A" H- _+ y, p. a1 E$ I/ I; G& X7 t if(!$this->fileformat) return false;
5 |! x& Z+ L, E2 o! S+ f- d 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;
- w3 P+ @3 `9 c8 jreturn true;9 h; i3 U$ z1 x5 J
6 @% \- g/ {" G8 V$ ` }5 `7 o6 `2 d3 M
可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。* _3 A4 X* E% X. Y1 E+ {
接着会进行真正的保存。通过$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文件。. M+ W+ A0 s# l
7 }! N. _& f, |/ {! k) d 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。) B \, H- z) \, C
Z% H2 {7 ] x2 t
* _5 s# T# I P j9 ^ 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid
* B5 a9 S. G/ [! B0 s 不过实际利用上会有一定的限制。
- `5 N- X& t) n) r% Z5 z( H5 |& |# D g 第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。
/ V: z) t4 b' X0 z' I B) [
8 p& J3 y9 a1 F0 s2 u2 ^
第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:
$ g5 ^/ U/ g, n6 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]);省略...
% X& a( U8 j' T因此要利用成功就需要条件竞争了。
; x2 g) x; U! z9 n
; h) w/ _1 k, j9 P
/ C" Z0 H; h9 s5 n% H0 [0 Q% e 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:
! R+ a Q9 ] ^. r3 A' }4 p, l3 c k4 B* X% H% o$ u1 h8 v function is_image($file) { return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}( q: X6 o2 O: y5 L, H
% H0 ?* W. z" {+ q
在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。
, G/ M6 O" z0 e在is_allow()中增加对$this->savename的二次检查。
' l; F/ z! p; Z. A5 g9 O. [- b- E* I7 s* u 嘛,祝各位大师傅中秋快乐!0 h7 \! W' f3 O6 n
+ Q1 J, w2 I( v; z$ N/ ?' U7 n 3 q* y5 Q4 W( Y4 o. z
| 欢迎光临 中国网络渗透测试联盟 (https://www.cobjon.com/) | Powered by Discuz! X3.2 |