前言
由于最近在打ctf因此遇到了一个python库的漏洞因此总结一下。
0x01 python urllib(urllib2)库的漏洞分析
0x001 python 2.7.16
测试代码如下:
1 | import urllib2 |
结果如下:
1 | root@kali:~# nc -lvp 8080 |
我们可以看到已经成功的进行了CRLF注入。
0x002 python 3.7.3
测试代码如下:
1 | import urllib |
结果如下:
1 | listening on [any] 8080 ... |
0x003 已有漏洞的版本
2.7.16~3.7.3
https://nvd.nist.gov/vuln/detail/CVE-2019-9947
0x004 源码分析:
0x02 hash长度扩展攻击
0x001 md5简介
MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。
MD5算法具有以下特点:
1、压缩性:任意长度的数据,算出的MD5值长度都是固定的。
2、容易计算:从原数据计算出MD5值很容易。
3、抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
4、强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
MD5的作用是让大容量信息在用数字签名软件签署私人密钥前被”压缩”成一种保密的格式(就是把一个任意长度的字节串变换成一定长的16进制数字串)。
大家都知道,地球上任何人都有自己独一无二的指纹,这常常成为司法机关鉴别罪犯身份最值得信赖的方法;与之类似,MD5就可以为任何文件(不管其大小、格式、数量)产生一个同样独一无二的MD5“数字指纹”,如果任何人对文件做了任何改动,其MD5也就是对应的“数字指纹”都会发生变化。
0x002 md5加密原理步骤
1.补充
在MD5算法中,首先需要对信息进行填充,使其位长对512求余的结果等于448,并且填充必须进行,即使其位长对512求余的结果等于448
。因此,信息的位长(Bits Length)将被扩展至N*512+448,N为一个非负整数,N可以是零。
填充的方法如下:
1)在信息的后面填充一个1和无数个0,直到满足上面的条件时才停止用0对信息的填充。
2)在这个结果后面附加一个64位二进制表示的填充前信息长度(单位位Bit),如果二进制表示的填充前信息长度超过64位,则取低位64位。
经过这两步的处理,信息的位长=N512+448+64=(N+1)512,即长度恰好是512的整数倍。这样做的原因是为满足后面处理中对信息长度的要求。
2.初始化变量
初始的128位值为初试链接变量,这些参数用于第一轮的运算,以大端字节序来表示,他们分别为: A=0x01234567,B=0x89ABCDEF,C=0xFEDCBA98,D=0x76543210。
(每一个变量给出的数值是高字节存于内存低地址,低字节存于内存高地址,即大端字节序。在程序中变量A、B、C、D的值分别为0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476)
3.处理分组数据
每一分组的算法流程如下:
第一分组需要将上面四个链接变量复制到另外四个变量中:A到a,B到b,C到c,D到d。从第二分组开始的变量为上一分组的运算结果,即A = a, B = b, C = c, D = d。
主循环有四轮(MD4只有三轮),每轮循环都很相似。第一轮进行16次操作。每次操作对a、b、c和d中的其中三个作一次非线性函数运算,然后将所得结果加上第四个变量,文本的一个子分组和一个常数。再将所得结果向左环移一个不定的数,并加上a、b、c或d中之一。最后用该结果取代a、b、c或d中之一。
以下是每次操作中用到的四个非线性函数(每轮一个)。
1 | F( X ,Y ,Z ) = ( X & Y ) | ( (~X) & Z ) |
(&是与(And),|是或(Or),~是非(Not),^是异或(Xor))
这四个函数的说明:如果X、Y和Z的对应位是独立和均匀的,那么结果的每一位也应是独立和均匀的。
F是一个逐位运算的函数。即,如果X,那么Y,否则Z。函数H是逐位奇偶操作符。
假设Mj表示消息的第j个子分组(从0到15),常数ti是4294967296*abs(sin(i))的整数部分,i取值从1到64,单位是弧度。(4294967296=2的32次方)
现定义:
1 | FF(a ,b ,c ,d ,Mj ,s ,ti ) 操作为 a = b + ( (a + F(b,c,d) + Mj + ti) << s) |
注意:<<
表示循环左移位,不是左移位。
这四轮(64步)是:
第一轮
1 | FF(a ,b ,c ,d ,M0 ,7 ,0xd76aa478 ) |
第二轮
1 | GG(a ,b ,c ,d ,M1 ,5 ,0xf61e2562 ) |
第三轮
1 | HH(a ,b ,c ,d ,M5 ,4 ,0xfffa3942 ) |
第四轮
1 | II(a ,b ,c ,d ,M0 ,6 ,0xf4292244 ) |
所有这些完成之后,将a、b、c、d分别在原来基础上再加上A、B、C、D。
即a = a + A,b = b + B,c = c + C,d = d + D
然后用下一分组数据继续运行以上算法。
4.输出
最后的输出是a、b、c和d的级联。
当你按照我上面所说的方法实现MD5算法以后,你可以用以下几个信息对你做出来的程序作一个简单的测试,看看程序有没有错误。
MD5(“”) = d41d8cd98f00b204e9800998ecf8427e
MD5(“a”) = 0cc175b9c0f1b6a831c399e269772661
MD5(“abc”) = 900150983cd24fb0d6963f7d28e17f72
MD5(“message digest”) = f96b697d7cb7938d525a2f31aaf161d0
MD5(“abcdefghijklmnopqrstuvwxyz”) = c3fcd3d76192e4007dfb496cca67e13b
MD5(“ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz”) =
f29939a25efabaef3b87e2cbfe641315
MD5(“8a683566bcc7801226b3d8b0cf35fd97”) =cf2cb5c89c5e5eeebef4a76becddfcfd
MD5加密字符串实例
现以字符串jklmn
为例。
该字符串在内存中表示为:6A 6B 6C 6D 6E
(从左到右为低地址到高地址,后同),信息长度为40 bits,即0x28。
对其填充,填充至448位,即56字节。结果为:
1 | 6A 6B 6C 6D 6E 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 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |
剩下64位,即8字节填充填充前的信息拉长,按小段字节序填充剩下的8字节,结果为。
1 | 6A 6B 6C 6D 6E 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 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 28 00 00 00 00 00 00 00 |
(64字节,512 bits)
初始化A,B,C,D四个变量。
将这64字节填充后数据分成16个小组(程序中对应为16个数组),即:
M0:6A 6B 6C 6D(这是内存中的顺序,按照小端字节序原则,对应数组M(0)的值为0x6D6C6B6A,下同)
M1:6E 80 00 00
M2:00 00 00 00
……
M14:28 00 00 00
M15:00 00 00 00
经过3.分组数据处理
后,a,b,c,d值分别为0xD8523F60,0x837E0144,0x517726CA,0x1BB6E5FE
在内存中为:
a: 60 3F 52 D8
b:44 01 7E 83
c:CA 26 77 51
d:FE E5 B6 1B
a,b,c,d按内存顺序输出即为最终结果:603F52D844017E83CA267751FEE5B61B。就是字符串jklmn
的md5值。
0x003 代码实现MD5转换器
java实现MD5:
1 | package com.liangjie.test; |
0x004 哈希长度扩展攻击
下面结合一个例子来描述其过程:
1 | <html lang="en"> |
这个核心的判断在第二个if判断
1 | if (urldecode($username) === "admin" && urldecode($password) != "admin") |
flag获取的要求是:传进一个cookie使getmein
等于md5($secret.urldecode($username.$password))
且后面部分不能为adminadmin
,也就是说需要构造getmein
的cookie
和他那串字符串相同就可以了。
我们已知$secret
长度为15,先进行消息的填充,前面的A随便写的,为了占15个字符,填充如下:
1 | AAAAAAAAAAAAAAAadminadmin\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\x00\x00\x00\xc8\x00\x00\x00\x00\x00\x00\x00dawn |
my_md5.py
1 | #!/usr/bin/env python |
exp.py
1 | #!/usr/bin/env python |