哈希长度扩展攻击的实现

0x00 引言

针对md5、sha1等哈希加密算法的攻击很多,但网上详细的分析哈希长度扩展攻击的文章却很少。其实这是一个非常简单明了的攻击手段,虽然此类哈希算法具有不可逆的特点,但是这种攻击手段很巧妙的利用了哈希算法的一个弱点,即初始幻数相同(也称初始值或registers)。在这篇文章中,默认读者对相应的算法已经有所了解,所以关于算法的实现在此不再赘述。本文以md5为例对哈希长度扩展攻击进行简要剖析。

md5算法:

sha1算法:

简单分析一下这两种算法的异同。

(回头再写www)

0x01 攻击成立的条件

1、攻击者知道哈希值和其的明文的一部分。

2、明文的另一部分长度已知或可预测(可穷举尝试或爆破以获取长度)。

3、服务器明文加盐的规律必须可预测或固定。

0x02 攻击准备

1、一个网上随处可见的正确的md5算法实现脚本(当然需要判断脚本正确与否的能力)

2、修改脚本

0x03 攻击原理分析

先建立几个概念:

1、这种攻击不是逆向的,而是正向的

2、这种攻击的本质是将服务器返回给你的哈希值作为初始状态还给服务器,让服务器在此基础上继续计算

推荐一篇讲原理讲的比较通俗易懂的文章:传送门

引言中提到过,哈希长度扩展是针对哈希算法的一个弱点展开的攻击。md5算法在一开始有四个初始幻数:

我们将明文信息长度对512bit取模,看看结果是否为448bit。一般来说很少会有信息正好满足这个条件,所以要经过一个补位的过程。补位至满足上述条件后再在信息的最后64bit加上信息的长度,若信息长度大于2的64次方,则只记原长度的低64位。补位完成后的消息应该是若干个512bit的区块。然后将补位完成后的消息以512bit为一组进行4轮运算,这里注意,所谓的运算是以明文信息为基础对幻数进行的运算,改变的是A,B,C,D这四个幻数。每一个512bit的区块对幻数进行运算后就被丢弃,每一个区块运算完毕后得出的新的4个幻数便是下一个区块运算时的初始幻数。一直到所有区块对幻数的计算结束,将最终的幻数小端反序后首尾相接便是最终输出的md5值。

那么什么是扩展攻击?简单来说,就是一种改变初始幻数后对我们可控的输入信息再进行md5的攻击。可想而知,这样计算出的md5值必定是已知的,也就是可控的。那么这种攻击的核心便是改变初始幻数,作为攻击者,我们不可能改变服务端的md5算法,也就是说真正的初始幻数是实际上不可变的。但是攻击者通过提交与服务器返回的哈希值相对应的明文来使服务器端的幻数变为之前服务器返回的哈希值,并在后面衔接自己的内容来使服务器下一次返回的哈希值成为已知。

0x04 攻击实现

我们实现这一攻击就需要一个攻击辅助脚本,下面是作者自己调试好的一个例子(调了一万年)。附有详细使用方法和注释,所以不进行说明。

本文md5实现脚本脚本来自:http://blog.csdn.net/adidala/article/details/28677393(原md5实现脚本有不少问题,会导致输出的md5值错误,这些错误在攻击辅助脚本中已被修复)
攻击辅助脚本(最好理解了原理再来看脚本):

若有需要回头加一个自动提交猜解长度的脚本。

0x05 一次攻击实例

没有找到好的例子,就拿一道ctf题为例,是PCTF的一道web题。现被收录于jarvisoj,题目名是“flag在管理员手里”。

直接从看到哈希值开始。

通过一些手段拿到的网站源码如下

分析一下源码,$role将一个序列化的cookie反序列化后读取,值得一提的是反序列化函数对单一字符串只会进行到第一个分号结束,后面有什么一概不管。也就是说类似于:

会把$role赋值为guest,后面的x就当没看见。要获得flag,这段cookie的开头必定是:

但是我们注意到在计算MD5之前有一个对cookie字符串的反序,所以正确的md5值的明文应该是这样的:

这道题最终猜解出来$salt的长度为12字节,假设现在我们已知其长度为12字节,利用脚本计算扩展:

其中xxx为我们不知道的salt,有效输出为msg,我们现在获得了扩展字符串。

显然原明文补位后的区块数为1,所以我们现在利用脚本计算payload中的MD5:

这里仍要注意反序问题,我们输入的任何内容都会被反序计算,所以这里的extension是反序的。

构造如下payload:

这里还有一个注意点,就是中间的填充字符串的顺序必须是反的,这样一来在整个字符串反序后对于md5来说才会变成正确的填充。

发包,成功获得flag。

纠正说明sql注入中位运算符的使用

//2、管道符的使用

//不清楚。。。现在已知的:

//1、order by之后可以使用|数字使排序错乱,不清楚具体是怎么错乱的

//2、where子句之后跟上|1|0也能出数据,但要是跟上|大于一或小于零的数就出不了数据

(以上注释符?表示内容不作数,同时保留曾经的错误内容以表示这是个坑)

首先感谢cL0und师傅,这个点我终于弄明白了,万万没想到是这种逻辑

纠正之前的两个说法:

第一,符号|在这里不是管道符,是mysql按位或运算

第二,排序并没有错乱,只是看上去错乱了,其本质仍是从小到大或从大到小排列

msql的位运算符有以下三个:

之前提到的排序“错乱”语句的基本构造如下:

这个语句其实mysql是这样理解的:

现在来说明其原理

在我们下达order by num指令时,sql并非对num进行直接排序,而是生成一个类似于字典的键值对进行排序,结构如下所示;

这里就有一个问题,同样都是1234,sql却会对作为key的1234进行排序。由于这里key和value都是1234,因此不会造成任何影响。但是下面那这种情况就不同了:

那么相当于:

我们再将其按key进行从小到大的排序看看,这样就会变成:

好了我们回头看实际的输出:

果然是2134,同理其它位运算符会产生相同的效果。

总结一下:

1、mysql不会直接对结果集中的参数进行排序,而是生成一个键值对后对其中的键进行排序

2、当然这只是一种理解方式,从最根本的层面理解的话可能不是这么回事,但是总得用语言把这种现象描述出来才行(逃)。

一个关于file_get_content()插入数据的小姿势

今天在jarvisoj上做到一道题用到了一个没有见过的姿势,记录一下。

原题链接

0x00 解题过程

点进去看到index.php

有一个参数id,随意试了一下,无果,于是抓包看看(其实这里应该看源码,反正做出来了就无所谓了吧)。

果然在返回中看到提示:

访问之,得到一段源码,下面是魔改后的源码:

只要显示出1 ok,2 ok,3 ok这个就算成功绕过了,$id$b比较好绕,最麻烦的就是这个$a

学习了一波后发现两种绕法,一种是:

另一种如图所示:

(关于这两种绕法的说明见下一节)

然后拿到一个不知道是什么玩意儿的东西。。。很头大,很久才反应出这TM居然是一个目录,于是访问url:

明摆着是注入,尝试后发现:

1、过滤了小写的select、union,于是使用大写的SELECT,UNION绕过

2、过滤了空格和加号、/**/、%0a、%20等等好几个空格的绕过姿势,百密一疏,最后使用%0b成功绕过

开始构造注入语句,先测试出字段数为3时不报错:

网页返回:

测试显示位,测试得显示位为第二行的第三列:

网页返回:

查找表名:

这里表名是content,sql执行失败会返回整个sql语句

网页返回:

尝试后锁定context字段:

获得flag

PCTF{Fin4lly_U_got_i7_C0ngRatulation5}

0x01 小姿势

第一种绕过:利用data协议

参考文章:http://blog.csdn.net/lxgwm2008/article/details/38437875

使用示例里面全部都有

利用url可以直接插入以下几几种类型的小数据:

所以我们可以使用这种操作绕过

第二种绕过:利用php协议读取post数据。

参考文章:官方文档

测试代码:

payload:

可以看到测试成功

总结:

1、在存在file_get_contents()函数时利用data协议可以给变量赋值

2、在存在file_get_contents()函数时利用php://input可以读取post内容给变量赋值

SQL注入中遇到的一些奇技淫巧

投稿文章,所以:

转载自安全客

先上一道简单的ctf注入题:

一道利用order by进行注入的ctf题

很不错的一道利用order by的注入题,之前不知道order by除了爆字段还有这种操作。

原题地址:http://chall.tasteless.eu/level1/index.php?dir=(很可惜原题好像炸了还好存了解题脚本hhhh)

直接进去dir后的参数是ASC,网页上有从1~10编号的10条信息。绕了一大圈反应出是order by后的参数,尝试把参数改为DESC,果然倒序排列了。题目给了提示:hint: table level1_flag column flag给了数据表和字段,于是开始构造payload。

于是玄学来了,在order by后面插入管道符|之后再跟一个偶数(?这里我真的不清楚)会导致排序错乱。尝试以下url:

http://chall.tasteless.eu/level1/index.php?dir=|2

果然排序错乱,那么想要查出flag必定要使用以下语句:

select flag from level1_flag

(结果证明确实这是一个一行一列的玩意儿,不然就要使用到limit或group_concat)

但是网页上没有显示这个的输出框,于是我们这样利用这个查询的结果集:

|(select(select flag from level1_flag)regexp '正则')+1

解释一下,括号里的正则匹配成功返回1,所以再加1变成2

所以如果匹配成功,网页的排序就会错乱,如果不成功排序则不会错乱,于是最终脚本:

总结一下:

1、管道符的使用(见正文)

2、regexp的使用(见正文)

其实还有一个group by后面的注入,觉得没什么好说的,where后面的都能用(大概)

0x00 union、intersect和minus的使用

union基本语法:

intersect(交集)和minus(差集)也一样,但是mysql不支持交集和差集,所以这也是一个判断数据库的方法。

就说说union:

基本法:前后两个select语句的字段数要相同,不然sql必定报错,所以可以用union指令判断数据表的字段数,基本构造方法:

...where...union select 1,2,3,4,...,x limit y,z

其中where子句可以没有,limit视情况而定,中间输入进去的1,2,3,4,…,x他们中的任何一个都可以用函数代替,最终他们在默认排序的情况下会被拼接到结果集的最后一行。例:

union查询强大而灵活,因为他可以查询两个不同的表的信息,哪怕这两个表字段数不同,只要这样做:

而test_table内的数据结构是这样的:

很明显与learning_test表的字段数不同,但是我们使用了group_concat()函数拼接了我们需要的内容。

0x01 管道符的使用

1、order by之后可以使用|数字使排序错乱,不清楚具体是怎么错乱的

2、where子句之后跟上|1|0也能出数据,但要是跟上|大于一或小于零的数就出不了数据

0x02 regexp的使用

很简单,正则匹配,匹配对象必须是单行单列,或者说是字符串。基本语法:

意思是将括号内的查询的结果集尝试与给出的正则匹配,如果配对成功则返回1,配对失败返回0

0x03 group_concat()的使用

将一列数据进行拼接,非常便利的函数,一般与union一起使用,就像本节的第一小节给出的最后一个例子一样。

0x04 order by爆字段

0x05 利用虚拟表在不知道字段名的情况下出数据

本小节原文参见:人生赢家师傅博客

——手段很阴险wwww

先上payload进行分析(强行换行缩进):

正常版:

这本是一道ctf题,前面估计是where后面的子句。这道题过滤了三样东西:1、空格,2、逗号,3、字段名

这里不详细说绕过,方法很多,空格利用%0a绕过,union指令中的逗号利用join绕过,limit指令中的逗号利用offset绕过。

这里因为payload中不能出现字段名,因此我们创建了一个与所查表字段数相同的虚拟表并对其并将其查询结果与前面的查询union起来。具体来说是这样:

正常版:

payload中的join换成逗号亦可。

我们平时使用union时都是将union select 1,2,3,4…写在后面以填充不存在的数据并测试字段数。在这种操作中我们把union select 1,2,3,4…写在了前面来充当虚拟表的字段名。本质上来说并不是不知道字段名,而是把不知道字段名的表的查询结果和我们创建的字段名为1,2,3,4…的虚拟表的交集作为一个结果集返回。

这里有一个点,方括号内的limit子句需要特别注意,要取下面这个子查询↓

结果集的最后一行,因为我们需要的数据被union拼到了最后一行(在我们需要的数据只有一行的情况下)。

如果我们需要的东西不止一行会怎么样呢?一段简单的测试:

并不会报错,我们需要的查询结果就是第5,6行第2字段的2和3。

下面是对虚拟表的简单测试:

可以看到我们创建的确实是字段名为1和2的虚拟表,此表的结构为一行两列。

用虚拟表去union其他表的数据:

可行,新姿势GET

0x06 substring()和ascii()的联合使用

用于猜解数据库名、表名、字段名和查询结果等

具体使用:

看到返回了1,也就是说select bin_column from learning_test where num=2这个查询语句返回的结果集的第一个字符的ascii码确实是大于10的。当然这个过程是繁琐的,可以使用脚本进行自动化猜解,或使用sqlmap中集成的类似的自动化注入功能。

0x07 利用floor()报错注入

直接给出payload:

报错机制有待研究,这两个payload是可以直接使用的