2018年9月21日,Destoon官方发布安全更新,修复了由用户“索马里的海贼”反馈的一个漏洞。! v5 D% D& z0 b! u! b. I+ p
; ]& {$ p- ]6 E4 H: \1 { J
根据更新消息可知漏洞发生在头像上传处。Destoon中处理头像上传的是 module/member/avatar.inc.php 文件。在会员中心处上传头像时抓包,部分内容如下:; D/ I4 K- B5 U5 V9 w
对应着avatar.inc.php代码如下:' m& z1 E" _6 j! ~$ W* U
1 O$ _ {2 g1 U2 ]% f! J2 G% ]& z8 Q# ~5 S/ z# N <?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) {
case 'upload':5 D4 ?9 V7 W. i. I
if(!$_FILES['file']['size']) {
8 h; N* n9 ]4 c: E2 E+ s0 Pj7 H0 @0 y M: B6 D; H$ b- Y if($DT_PC) dheader('?action=html&reload='.$DT_TIME);) q8 ^( ~ d6 O' E( }/ G; H
2 K/ x/ ]% G% ]) h) ?1 `' i. q# y1 M exit('{"error":1,"message":"Error FILE"}');% c- g$ r! A' c
1 u+ T B& u4 K+ w3 O( a" X3 K9 k: N) L# r5 \; Y# h }
require DT_ROOT.'/include/upload.class.php';
. l; e0 N! F- [0 ?, o2 S" h# `5 U- |0 ~- D
$ext = file_ext($_FILES['file']['name']);
. V C& |0 s+ P $name = 'avatar'.$_userid.'.'.$ext;
' q; O2 F9 v4 P0 d$file = DT_ROOT.'/file/temp/'.$name;! A/ _/ n" b3 r: H/ R/ y
6 @3 M) `) y. i9 l3 t# [
+ V3 G3 q6 m; Y |3 V if(is_file($file)) file_del($file);7 L G$ b( o- G) p& H6 [* a
* H0 P# i' _; a( C. g# @6 c$ k$upload = new upload($_FILES, 'file/temp/', $name, 'jpg|jpeg|gif|png');
2 f: W/ N. j/ b* l. ], j) n& H$ A
/ h7 l9 Q/ I2 X+ w E $upload->adduserid = false;( x) a* Y. F; i, c" w( V B
" k$ b9 L0 c5 P+ b7 V ]# X: D6 R Y, ?4 E) p( \
if($upload->save()) {
( J" v x r5 o& Y, o8 B! V...8 ?: e5 P; V! p n
( h6 N5 z, \/ ]; ^" A# e2 k& z } else {# X) s8 G& a$ `) i+ U. [
& Y8 N, V; X. G" {$ J ..." P; p: j# l% {. ~- p+ H
6 q: S- {! s. e9 ]0 A6 U w# H* q% O J }
2 X0 X) W3 j6 c: N% p/ f break;
8 `" d; Q, x) E- ^: z/ _9 ^+ d 这里通过$_FILES['file']依次获取了上传文件扩展名$ext、保存临时文件名$name、保存临时文件完整路径$file变量。之后通过new upload();创立一个upload对象,等到$upload->save()时再将文件真正写入。% j4 W/ S t1 Q6 s J
upload对象构造函数如下,include/upload.class.php:25:. K! c) c# ~/ w7 f; i: U" k
$ [) q8 L/ q$ L( A" y <?phpclass upload {# O/ Z2 `2 e- s
, S1 \: D1 h9 x, n function __construct($_file, $savepath, $savename = '', $fileformat = '') {& I6 l4 J6 H9 |/ Z+ a- O
0 R5 _7 J, F% z) Q+ H' b' ^ q" _7 G5 K8 M4 o2 ~ global $DT, $_userid; r9 ^- P" Z9 `2 S
% i& h, r: D# p( `- s Rforeach($_file as $file) {
" d$ n. J T4 r) {+ c7 O% J. z/ ?1 i8 F $this->file = $file['tmp_name'];
$ N! b0 @. U4 U: C; m/ ]' z4 M# u' E. p $this->file_name = $file['name'];# y. k; Y2 s- ]' E" ~! n
4 r; _" z$ g3 K/ M. q$ `: L0 t$this->file_size = $file['size'];- V" B" v, G& J1 x; k0 u
# d2 F" l3 \6 E, o+ N4 G, g $this->file_type = $file['type'];+ f7 c0 c' n* J( i, y! C- l/ V* M
, a" E# S, f( j! ^0 r- @! H $this->file_error = $file['error'];
}
$this->userid = $_userid;
$this->ext = file_ext($this->file_name);3 ]# V$ ] f' R/ m; H; D* R
- X: U1 m) N( T. Z3 y* A* g% f/ L! c' _2 ~0 w9 F7 _ $this->fileformat = $fileformat ? $fileformat : $DT['uploadtype'];% \+ w7 x8 i" Q2 x/ [
+ ?% r0 `7 q+ N) i( y1 k% T" r $this->maxsize = $DT['uploadsize'] ? $DT['uploadsize']*1024 : 2048*1024;% n j( z; m' S. G* Q
$this->savepath = $savepath;
8 t4 e( W1 t2 i- n1 O, a$this->savename = $savename;- t* w& o& A% ~6 X
0 V3 M" A; [( w4 @9 S- w' a+ {}}
4 e( C' G1 W( Q. T( C: Z' k 这里通过foreach($_file as $file)来遍历初始化各项参数。而savepath、savename则是通过__construct($_file, $savepath, $savename = '', $fileformat = '')直接传入参数指定。& A: T( b! ~ [' b; D1 S# J
* P, D* m* g: c7 v# L; ]) N* v9 T6 n8 r* z0 z8 k" ]$ O% k 因此考虑上传了两个文件,第一个文件名是1.php,第二个文件是1.jpg,只要构造合理的表单上传(参考:https://www.cnblogs.com/DeanChopper/p/4673577.html),则在avatar.inc.php中
! r6 x, U' u4 q2 `' u; V( b0 g5 ]+ ^4 j) X# A" K `. G $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) Y% N; j7 b8 _; f# f; Q: N
. c1 A# w/ A0 t' o" D) H' S而在upload类中,由于多个文件上传,$this->file、$this->file_name、$this->file_type将foreach在第二次循环中被置为jpg文件。测试如下:5 b% I) b# `7 W: f
! S3 n/ M( N4 K; e
回到avatar.inc.php,当进行文件保存时调用$upload->save(),include/upload.class.php:50:7 D3 d, Y3 M2 [( f
9 J: [: Z& o- H0 Y8 f) T) S+ ?0 ]) W% Z, I: @$ D <?phpclass upload {7 u0 A5 o" M0 E+ g5 {9 f; d! r
function save() {
include load('include.lang');
8 g8 h% x4 w$ H0 B9 l if($this->file_error) return $this->_('Error(21)'.$L['upload_failed'].' ('.$L['upload_error_'.$this->file_error].')');
6 Q! k, l0 z& Q1 j
% _+ A' \# C+ t' e3 A+ b8 m if($this->maxsize > 0 && $this->file_size > $this->maxsize) return $this->_('Error(22)'.$L['upload_size_limit'].' ('.intval($this->maxsize/1024).'Kb)');
3 s& V# M8 S# U- O" {! Z0 \, w9 W( t# |& Y7 S. o1 U3 ?- E
W: V& X9 ^% B3 e. qif(!$this->is_allow()) return $this->_('Error(23)'.$L['upload_not_allow']);
8 u8 ^+ x# O# v/ K5 o& M; b4 [$ f . F4 d5 U3 Y6 `
7 J# J; K/ Y- b( M& r0 I' u6 W, ]! V5 c6 C% V2 ]/ W $this->set_savepath($this->savepath);
& g' o1 v0 w: A% i/ R5 O$this->set_savename($this->savename);
3 h+ C) z. \; D* Q1 L: _
9 H0 Y4 B8 C$ s( Yif(!is_writable(DT_ROOT.'/'.$this->savepath)) return $this->_('Error(24)'.$L['upload_unwritable']);' S7 ~/ @' G' `
2 p# G9 t- j% h3 M4 [7 q9 l: y, P7 a if(!is_uploaded_file($this->file)) return $this->_('Error(25)'.$L['upload_failed']);
- l( K5 t3 F% D$ [- P( f8 c. R8 i* H9 a: L6 s* ]6 c: X* Q2 X7 l6 _' g if(!move_uploaded_file($this->file, DT_ROOT.'/'.$this->saveto)) return $this->_('Error(26)'.$L['upload_failed']);
6 c4 y7 e/ n3 ?6 O6 e e9 F2 |9 a( c $this->image = $this->is_image();
if(DT_CHMOD) @chmod(DT_ROOT.'/'.$this->saveto, DT_CHMOD);
7 z; I8 t0 w; Y return true;- |! I9 q i: q& B" H. t
* T; a4 D: s, J8 ga" [/ }! a5 Y7 M" u }}/ E. b( g3 I2 M4 N- P, W q5 u
& n. z1 h2 X3 u- j: K* @" {! P先经过几个基本参数的检查,然后调用$this->is_allow()来进行安全检查 include/upload.class.php:72:- m' h7 [8 I% V
5 D& R( c3 O9 B; f8 l4 S9 K) ?8 N$ N$ Y <?php9 G. S: K( n9 F1 A) B5 m# }
1 C2 R1 h2 P& G/ M! `7 F function is_allow() {
if(!$this->fileformat) return false;
( C) R& Q6 ]" m8 z' Z if(!preg_match("/^(".$this->fileformat.")$/i", $this->ext)) return false;' F1 k R7 C4 b
& Y$ a( t( H: N U0 ]4 J% d3 ?+ g# ] p7 m3 h 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;
; H0 ^4 |* ^- | return true;) ^" Z2 N" {) k
5 { _. R+ k, _) [2 N, L]6 T8 I# @8 T3 h, b }" B3 n1 r5 r( e
4 K: t" r( @' D6 ? 可以看到这里仅仅对$this->ext进行了检查,如前此时$this->ext为jpg,检查通过。
接着会进行真正的保存。通过$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 @6 U9 I. h3 R4 p2 T 综上,上传两个文件,其中第一个文件以php为结尾如1.php,用于设置后缀名为php;第二个文件为1.jpg,jpg用于绕过检测,其内容为php一句话木马(图片马)。6 w# `, V- S P x7 W5 l
7 B" U8 Q8 ~( C. U
6 \% x4 T7 \* T1 `/ g5 t% b5 o 然后访问http://127.0.0.1/file/temp/avatar1.php 即可。其中1是自己的_userid& H7 ?- q: ?) N1 B/ e D8 _
. [* U& T) V& F. Q8 m0 n& {# G/ k4 _( o: O: C. s) g2 n) _: i: I 不过实际利用上会有一定的限制。3 s, p, S8 p6 l& t1 t8 H& y
第一点是destoon使用了伪静态规则,限制了file目录下php文件的执行。
& R6 L% y$ q/ b! q, M9 M* w2 u0 s- v! s& X: n2 ^
第二点是avatar.inc.php中在$upload->save()后,会再次对文件进行检查,然后重命名为xx.jpg:
^- s# g5 g% E& W; 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]);省略...7 a. {+ M C& H. P
$ l) ?% a' M* b3 b$ @ 因此要利用成功就需要条件竞争了。4 j. b6 E. v+ o/ S0 b
( b& \; _& g9 _- r! T1 Q. g3 [. n1 U. J& x% H, t2 v
9 b; i. n' h1 r1 V$ G- {1 w
4 [% ^, k& h Z. U! j 在upload的一开始,就进行一次后缀名的检查。其中is_image如下:
1 J& |; c- G0 s+ d( j; Jfunction is_image($file) { return preg_match("/^(jpg|jpeg|gif|png|bmp)$/i", file_ext($file));}7 g6 I7 D- U" c# z" B
3 _: N/ @" q3 L3 S: J
* U/ \7 b1 T1 P D7 d2 y: ? 在__construct()的foreach中使用了break,获取了第一个文件后就跳出循环。! g x- _: D* B1 N; ~4 \
在is_allow()中增加对$this->savename的二次检查。
) D% [" Z; K% d$ B% E& K 嘛,祝各位大师傅中秋快乐!
5 l/ r9 i2 F2 T: i4 S+ j/ D9 G3 z: ^$ g
| 欢迎光临 中国网络渗透测试联盟 (https://www.cobjon.com/) | Powered by Discuz! X3.2 |