一、不足:
真·什么也不会。。
- 代码审计吧,,不会代码审计,
- 对python很不了解,,,刚看代码的时候,真的没有一句能够看懂的。。不行就以后看看少用burp,多用pyt脚本练练python看看,
- 代码审计拉得很,,,
二、注意事项
- 盐的长度,通常不会是题目所给的盐的长度,这点要注意。
三、看WP:
1. 哈希拓展长度攻击
- 啥啊这是,,是个python的flask。但是怎么看啊这,,,
#! /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):#初始化是吧,
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: # 注意这里是 in 哦 in 还是比较安全的
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:# 如果read这个字符串再 action 这个参数里面的话
f = open("./%s/result.txt" % self.sandbox, 'r')#这个sandbox是ip的一个md5加密。
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)://下面这个setsign获取的是一个加盐的md5的值。
if (getSign(self.action, self.param) == self.sign)://self.sign是从cookie中来的,
return True//这就和md5长度拓展攻击很想了啊
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"
/*这两个怼一块是不是就是md5加盐加密啊,*/
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')
- 解释一下部分函数:
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字符串
- 分析一下三个路由
#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)//返回getSign的返回值
@app.route('/De1ta',methods=['GET','POST'])
def challenge()://python方式获取get的传参,
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()
/geneSign
:获取get方法传入的param的值,和 已经定义的 action="scan"调用getSign函数来生成签名。
/De1ta
:从cookies中获取action,param,sign参数和 用户的 ip。然后构造一个Task类对象并 调用Exec方法 ,做种以json形式返回结果。
/
:首页获取源码。
- 函数
接下里看 getSign 函数
def getSign(action, param)://返回一个md5加密签名。
return hashlib.md5(secert_key + param + action).hexdigest()
将secert_key,param和action拼接在一起。 返回MD5加密后的结果 。这个不要理解错了,这可是引导解题思路的关键啊。
waf 函数
def waf(param)://不允许有gopher或者file 关键字
check=param.strip().lower()
if check.startswith("gopher") or check.startswith("file"):
return True
else:
return False
上面的路由,如果waf为true的话为hacker。所以这里是过滤gopher和 file
exec方法:
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
如果scan在action中,则将scan读取到的内容写进沙盒里的result.txt;
如果,read在action中,则将沙河中的result.txt的内容读取出来。
py文件读取的芝士1 。。。py文件读取的芝士2
-
分析完后我们发现 getSign 默认只生成action为 scan 的签名,我们可以利用将 flag.txt 的内容督导 result.txt 里,如果我们想要将 result.txt 的内容读出来,则需要伪造一个 action 为 read 的签名。
-
要伪造一个 action 为 read 的签名,同时 getSign 的 action 又只能够是 scan 。那么靠它来生成应该是不行了,那就只能够自己直接伪造一个 action 为 read 的签名了
-
想要伪造签名我们呢再来看一下getSign函数
def getSign(action, param):
return hashlib.md5(secert_key + param + action).hexdigest()
这里生成签名的时候,是将 secert_key , param , action参数拼接到一起来的。而且action在最后,这样我们就可以用 哈希长度拓展攻击 来伪造我们需要的签名了, 只需要在scan后面拼上read即可。
原理的话,就是 他后面需补位,而补位的话,它默认是800000来补位。对于具体什么值没有要求,这样我们就可以在后面加上 read 。从而达到 read in action 中了。就可以执行 read 那块了
使用哈希长度拓展攻击需要 知道三个东西: 明文(scan),盐的长度(16为的随机数),明文的md5值(action的值:scan)。而且用户能够提交修改md5的值。然后我们要随意添加的内容是 read 。
这里就有问题了哦
注意事项是:盐的长度,通常不是题目所给的盐的长度。
明文只是action而已。所以前面的param的值,也应该算在盐的长度里面的。,所以param的值是多少呢?resp = scan(self.param)//
找到了。resp读取了param的内容,也就是param就是个文件名,就是flag.txt。
所以盐的长度就是16+8=24了。所以
使用哈希长度拓展攻击需要 知道三个东西: 明文(scan),盐的长度(24为),明文的md5值(action的值:scan)。而且用户能够提交修改md5的值。然后我们要随意添加的内容是 read
伪造的话,方法有二,一个是python的脚本,另一个是 hashpump 这个工具,
key+flag.txt+action
分为两种方法,
一个就是 盐是前两个,16+8= 24 。明文就是 scan 。添加数据是read
第二个就是 盐就是 key 16为。 然后 明文 是 flag.txtscan 。添加的数据是 flag.txtread
这个是第一种的
盐长为 24
加密之后的是 99e0ef1c300c7b19d66dea842bee2370
明文是 scan
要添加的值是 read
015ae5058d54a33a611e2d955e53193d
scan\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x00\x00\x00\x00read
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
然后到这个 De1ta 路由中提交数据
结果不行,那就用python来提交数据
OK
这个是第二种的
这个方法不行,因为在开始没有传递param = flag.txt的时候,明文 是 scan 不是 flag.txtscan 。我们在构造的时候可以构造上 flag.txtread。从而有了 flag.txt。然是 没有传入 param参数的时候,getsign设定了action只能够是scan,没有flag.txt所以明文不对,
所以param=flag.txt 。这里不用param,他归到了 action里面了
盐长为16。
加密之后的 6c0432a0c9eaaa4705de585a430085ad
明文是 flag.txtscan
要添加的是 flag.txtread
12e4543f1babc9be6836416c53e4b42a
flag.txtscan\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x00\x00\x00\x00\x00\x00\x00flag.txtread
flag.txtscan%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%00flag.txtread
分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线分割线
修改之后,把明文 修改为了 scan之后呢
12e4543f1babc9be6836416c53e4b42a
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%00%00%00%00%00%00%00%00%a0%00%00%00%00%00%00%00flag.txtread
还是不对,不想了吧,可能是第一次 没有读入 flag.txt的内容吧,
连data数据都没有
2.字符串拼接
截图来自:先知社区;
和那个哈希拓展攻击很像啊。都是 action中有了read。
不过这个更轻巧。===========要用这个方法,还是 代码审计 要好啊,,,