0x01 引言
哈希扩展攻击是个老漏洞了,但很少碰到。研究漏洞时忽然发现了一个有意思的问题,如下代码是掌阅某站的古老代码,存在SQL注入,但是需要绕过签名后才能注入,大佬们说可以利用哈希扩展攻击绕过,决定研究一番。
#该函数存在sql注入,需要绕过签名才能注入
def get_chapter_list(self):
"""
获取章节列表
:return:
"""
book_id = self.get_argument("book_id")
client_id = self.get_argument("client_id")
sign = self.get_argument("sign")
if not book_id:
self.api_json({"status": Status.ERROR, "msg": "参数缺失"})
return
md5 = hashlib.md5(client_id+SIGN_SECERT+book_id).hexdigest()
if md5 != sign:
self.api_json({"status": Status.ERROR, "msg": "非法请求"})
return
chapter_service = Service.inst().chapter
try:
chapter_list = chapter_service.get_upload_chapter_list(book_id)
self.api_json(chapter_list)
except Exception, e:
logging.error("查询章节列表=%s", str(e), exc_info=True)
self.api_json({"status": Status.ERROR, "msg": "查询失败"})
#跟进get_upload_chapter_list函数
def get_upload_chapter_list(self, book_id):
"""
获取同步给客户端的章节列表
:param book_id:
:return:
"""
query = Query()
query.set_table("book_zhang")
query.set_sql("select id, chaptername name")
query.where(" book_id = %s " % book_id)
query.where("app_ifup = 1")
query.orderby("chapterid", "asc")
return query.data()
0x02 哈希扩展攻击
什么是哈希扩展攻击?
在未知secret并且知道一段由包含secret加密的结果的情况下通过控制可控变量得到md5(secret+可控)的值。
极端一点描述:
已知:X + 4 = 9
问题:填空使得等号两边相等 X + ()=()
0x03 MD5算法流程
MD5算法主要分这几步:
1.把消息分为n个消息块。(补位)
补位:把原始消息化为bit字节单位的数据,然后后面补上10000....(n个0)使得最后len(message) % 512 == 448 ,这里的len测的是message的bit位数, 然后补100
2.对最后一个消息块进行消息填充。(补长度)
把刚刚得到的数据补到64 字节,根据进制转换,64bytes = 512bits 刚刚是448bits,也就是说还少了64bits也就是8bytes,补上八个字符
3.每个消息块和一个输入量做运算,把运算结果作为下一个输入量(计算消息摘要)
对消息进行分组以及padding后,MD5算法开始依次对每组消息(填充好的消息按照64字节分组)进行压缩,经过64轮数学变换。这个过程中,一开始会有定义好的初始化向量ABCD,为4个中间值,初始化向量不是随机生成的,是标准里定义死的硬编码。
在我们得到这段密文之后,如果下次加密还是用的上次加密相同的密文并且密文后接了可控数据,我们就可以利用可控量进行手动填充原始数据使其达到正常加密前两步padding后的结果(给出密文的那次加密),又因为是分组加密,所以每组的加密结果互不干涉,这也就是ECB的诟病,所以我们可以手动添加最后一组明文,并根据上一次加密的结果逆向再和库中固定的加密轮密钥来演算出我们新添加的一组明文的加密结果,进而推出最后的新密文
0x04 长度扩展攻击
看一hash扩展攻击的题目,hashTest.php内容如下:
show_source(__file__);
$secret = "XXXXXXXXXXXXXXX"; //15 length
if(!isset($_COOKIE["user"])){ //setcookie user,hash firsttime
setcookie("user", "admin");
setcookie("hash", md5($secret."admin"));
}
if(isset($_COOKIE["user"]) && isset($_COOKIE["hash"])){ // hash === md5(secret + user)
if($_COOKIE["user"] === "admin"){
die("can not admin");
}
$user = $_COOKIE["user"];
$hash = $_COOKIE["hash"];
if($hash === md5($secret.$user)){
die("hash attack success!");
}else{
die("try again!");
}
}
?>
题目分析:
1.md5($secret."admin")的值是39ab35f1f08e1bb112b49fc9879ce092
2.$secret是密文,admin是数据,原始数据总长度:15+5=20
3.数据是admin
怎么才能get flag?
修改cookie中user和hash,传入不为admin的user和hash值,使得
$hash === md5($secret.$user),就会get flag!
0x05 HashPump安装
手算是不可能手算的,只能借助工具来计算!
HashPump是一个借助于OpenSSL实现了针对多种散列函数的攻击工具,支持针对MD5、CRC32、SHA1、SHA256和SHA512等长度扩展攻击。而MD2、SHA224和SHA384算法不受此攻击的影响,因其部分避免了对状态变量的输出,并不输出全部的状态变量。
下载及安装:
git clone https://github.com/bwall/HashPump.git
apt-get install g++ libssl-dev
cd HashPump
make
make install
攻击目的:在未知secret并且知道一段由包含secret加密的结果的情况下通过控制可控变量得到md5(secret+可控)的值
0x06 开始解题
这时候我们使用HashPump,附加数据至少1位以上:
# hashpump
Input Signature: 8867c97050ca95d6b74a70232d6394a6
Input Data: admin
Input Key Length: 20
Input Data to Add: 9ba13a
或者直接
hashpump -s 8867c97050ca95d6b74a70232d6394a6 -d admin -k 20 -a 9ba13a
就会得到
c284277d5a447a0b554704c0490b5150
adminx80x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00xc8x00x00x00x00x00x00x009ba13a
这两个结果就是上边的填空题答案:
第一个是新的签名,把它设置到cookies的hash里。
第二个把payload设置到cookies的user里,将x换成%,
然后刷新
hash=c284277d5a447a0b554704c0490b5150
user=admin%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%009ba13a
就可以get flag。
0x07 总结
1.HashPump工具可以帮助我们进行哈希扩展攻击
2.攻击场景是可以绕过签名,达到某种攻击
3.md5和sha1尽量不要用来做签名,会被绕过
参考文献:
https://www.bilibili.com/read/cv8322710/
https://www.leavesongs.com/PENETRATION/zhangyue-python-web-code-execute.html
https://www.jianshu.com/p/43f0d7bf8750
原文始发于微信公众号(98KSec):哈希扩展攻击及复现
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论