实验吧-让我进去【salt加密 哈希长度拓展攻击】
来源:互联网 发布:淘宝水果供应商 编辑:程序博客网 时间:2024/05/16 23:47
原题内容:
相信你一定能拿到想要的
Hint:你可能希望知道服务器端发生了什么。。
格式:CTF{}
解题链接:点这里
首先拿到题目,查看源代码,bp准备。源代码没问题,直接开始bp尝试key,两行直接admin,admin测试,通过bp可以看到以下内容:
自习看了一下bp得到的东西,set-cookies无疑是最容易注意到的东西,一般bp很少看到这个东西,这题有这个,可能与题目有关,对bp的两个key(“sample-hash”,"source")的value分别进行修改。good,当source值改变时,我们得到了源代码,图片如下:
<<?php<html><body><pre>$flag = "XXXXXXXXXXXXXXXXXXXXXXX";$secret = "XXXXXXXXXXXXXXX"; // This secret is 15 characters long for security!$username = $_POST["username"];$password = $_POST["password"];if (!empty($_COOKIE["getmein"])) { if (urldecode($username) === "admin" && urldecode($password) != "admin") {# ===俩边不管值还是类型都要一致 if ($COOKIE["getmein"] === md5($secret . urldecode($username . $password))) { echo "Congratulations! You are a registered user.\n"; die ("The flag is ". $flag);#exit()/die() 函数输出一条消息,并退出当前脚本 } else { die ("Your cookies don't match up! STOP HACKING THIS SITE."); } } else { die ("You are not an admin! LEAVE."); }}setcookie("sample-hash", md5($secret . urldecode("admin" . "admin")), time() + (60 * 60 * 24 * 7));if (empty($_COOKIE["source"])) { setcookie("source", 0, time() + (60 * 60 * 24 * 7));}else { if ($_COOKIE["source"] != 0) { echo ""; // This source code is outputted here }} </pre><h1>Admins Only!</h1><p>If you have the correct credentials, log in below. If not, please LEAVE.</p><form method="POST"> Username: <input type="text" name="username"> <br> Password: <input type="password" name="password"> <br> <button type="submit">Submit</button></form></body></html>?>
稍微分析下,常规思路得到flag为,username为admin,password不可为admin,添加cookies,变量名为getmein,要求为:
$COOKIE["getmein"] === md5($secret . urldecode($username . $password))
很有意思,再看一下sample-hash的值
setcookie("sample-hash", md5($secret . urldecode("admin" . "admin")), time() + (60 * 60 * 24 * 7));
似乎明白了限制password不能为admin的意思,就是坑你的,哈哈哈
研究了很久,才知道这题应该用到hash拓展攻击,在这里和大家分享一下,萌新如何理解hash拓展攻击
话不多说,直入正题,从题目开始,首先理一理我们手上拥有的数据:
1.源码 不解释了,上面分析很多了
2.sample-hash=571580b26c65f306376d4f64e53cb5c7 这个是未知的15位secret+adminadmin经过md5形成的加密串
有用的就是这两点信息,在这里,先介绍下salt加密的概念
salt加密
所谓加Salt,就是加点“佐料”。当用户首次提供密码时(通常是注册时),由系统自动往这个密码里加一些“Salt值”,这个值是由系统随机生成的,并且只有系统知道。然后再散列。而当用户登录时,系统为用户提供的代码撒上同样的“Salt值”,然后散列,再比较散列值,已确定密码是否正确。
有没有感觉很熟悉,对了,这里的secert就很像一个未知的salt,对于这种未知salt,但过关MD5/sha1加密,目前有一个很好的办法,就是hash长度拓展攻击。下面以这题为例详细介绍一下:
基本思路
我们知道secert+adminadmin经过md5形成的加密串,
flag获取的要求是:传进一个cookie使其等于secret+urldecode($username . $password)MD5加密后的结果且后面部分不能为adminadmin
所以我们需要的就是构造cookie使其相等
md5 算法实现(补全、计算)
要实现25位串xxxxxxxxxxxxxxxadminadmin的MD5加密,首先要将其转变成16进制(x代表未知的15位secret),
消息必须进行补位,即使得其长度在对512取模后的值为448。当消息长度不满448 bit 时(注意是位,而不是字符串长度),消息长度达到448 bit 即可。当然,如果消息长度已经达到448 bit,也要进行补位。补位是必的。
补位的方式的二进制表示是在消息的后面加上一个1,后面跟着无限个0,直到消息长度为448。在16进制下,我们需要在消息后补80,就是2进制的10000000。我们把消息abc进行补位到448 bit,也就是56 byte。
如图这一段即为补全部分:
再之后,进行最初消息位数的补位(除去补位部分的,住是位数的个数不是字节的个数),此题的长度为25 byte = 25*8 bit = 200 bit,换算成16进制,即为0xc8。故上图第57位为c8,再之后继续补00到64 byte。
补位到此结束,之后进行消息摘要的计算,具体原理这里不做介绍了,博主语言表达能力不够强,不敢乱写,有兴趣的可在文章末尾找到我推荐的几个博客进行学习,从容易理解的角度来说就是将初始的四个渐变变量,
A = 0x67452301;B = 0xEFCDAB89;C = 0x98BADCFE;D = 0x10325476;
用补位已经补长度完成之后的消息来一起进行运算,每次拿出 512 bit的消息(即64字节),多次变化(每次变化这四个渐变变量都会变化)到最后一轮,这四个渐变变量接在一起,再高低位互换得到最终加密结果。具体运算过程,再这题中其实不需要过多理解(不必强求,我理解这个变化花了好久好久,其实对这题并无太多影响)
哈希拓展长度攻击
问题就出在覆盖上。我们在不知道具体 $SECRET 的情况下,得知了secert+adminadmin 的hash 值(md5加密结果),以及我们得知sercret的位数。而我们得到的 hash 值正是最后一轮摘要后的经过高地位互换的链变量。我们可以想像一下,如果还有下一轮运算,我们现在的这个hash值就是其需要用到的渐变变量,我们唯一需要做的就是将渐变变量反过来(高低位互换,这里还有个概念,小端规则,自己查一下,不做说明了),然后利用这个计算得到下一轮的hash值,在这题理解为构造一个前面为xxxxxxxxxxxxxxxadminadmin的大于64位的字符串,得到其hash值,最终完成解题
qwq,可能理解起来有点难,我自己写这段话的时候都感觉别扭,但尽力理解吧,不懂得可以在博客下留言,我尽力解答
下面直接上本题的解题思路了
首先我选择构造的是:
xxxxxxxxxxadminadmin\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\x00admin
最后的admin是我随意加的,大家也可以换
那么我们最终的目的就是,获取这个的hash值通过bp将值通过cookie传递,与secret+urldecode($username . $password)的hash值进行判等,大家明显对比可知,
这里,我的username为admin,password为
admin\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\x00admin
,也不对,还要反urldecode一下,很简单的,16进制urlencode即将\x换成%,所以password为:
adminadmin%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%c8%00%00%00%00%00%00%00admin
接下来是重头戏,如何获取其的hash值,代码如下:
#include <cmath>#include <cstdio>#include <vector>#include <string>#include <cstring>#include <iostream>using namespace std;typedef unsigned int uint;typedef long long LL;const int MAXN = 1e6 + 5;const int mod = 1e9 + 7;struct MD5 { typedef void (MD5::*deal_fun)(uint&, uint, uint, uint, uint, uint, uint);//用于定义函数指针数组 string init_str;//数据字符串 uint init_arr[1000];//最终的数据数组{进行扩充处理后的数据} const static int MAXN = 1e2; static uint s_state[4];//最开始的默认静态渐变变量 uint state[4];//这个也是默认渐变变量,但是会改变 static uint rolarray[4][4];//位移数组 static uint mN[4][16];//对M数组的处理 uint curM;//当前处理的直接在整个数据中的位置 uint lenZ;//数据的总长{进行扩充处理后的数据总长,这个数是64的倍数} uint offset;//需要从第几组开始处理 uint Tarr[64];//当前保存的T数组数据 uint Memory[64 + 5];//当前要处理的64个字节数据 uint M[16];//将64个字节数据分为16个数 MD5(); MD5(string str, int noffset); //数据处理函数 inline uint F(uint X, uint Y, uint Z); inline uint G(uint X, uint Y, uint Z); inline uint H(uint X, uint Y, uint Z); inline uint I(uint X, uint Y, uint Z); //循环左移函数 uint ROL(uint s, uint ws); //过程处理函数 inline void FF(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac); inline void GG(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac); inline void HH(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac); inline void II(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac); //生成T数组单个数据的函数 inline uint T(uint i); //将总数据中的64个字节移到Memory数组中 void data_Init(); //建立M数组 void create_M_arr(); //移动a,b,c,d,规则在前面介绍了 void l_data_change(uint *buf); //产生T数组 void create_T_arr(); //得到最终MD5值 string get_MD5(); //过程处理 void processing();};uint MD5::rolarray[4][4] = { { 7, 12, 17, 22 }, { 5, 9, 14, 20 }, { 4, 11, 16, 23 }, { 6, 10, 15, 21 }};uint MD5::mN[4][16] = { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, { 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12 }, { 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2 }, { 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 }};/*传统渐变变量0x67452301,0xefcdab89,0x98badcfe,0x10325476这四个东西是可以根据要求更改的,如果取上述几个数则和经常用的MD5算出的结果是一样的对了,由于有些数据是静态的,改变之后不会进行需要重新进行复制*/uint MD5::s_state[4] = { 0xb2801557, 0x06f3656c, 0x644f6d37, 0xc7b53ce5};//已经按小端规则反处理哈希值了MD5::MD5() {}MD5::MD5(string str, int noffset = 1) { offset = noffset; curM = (noffset - 1) * 64;//从0位置处开始处理 init_str = str;//对数据字符串进行处理 lenZ = init_str.length(); memset(init_arr, 0, sizeof(init_arr)); for(int i = 0; i < lenZ; i ++) { init_arr[i] = str[i];//最终的数据数组进行赋值 } /* 将数据扩充到取模64个字节等于56个字节 第一个填充0x80,然后就是0x00了 */ if(lenZ % 64 != 56) init_arr[lenZ ++] = 0x80; while(lenZ % 64 != 56) { init_arr[lenZ ++] = 0x00; } /* 最后8个字节保存了没扩充钱位数的多少,记住是位数的个数不是字节的个数,同时是按照小端规则 */ uint lengthbits = init_str.length() * 8; init_arr[lenZ ++] = lengthbits & 0xff; init_arr[lenZ ++] = lengthbits >> 8 & 0xff; init_arr[lenZ ++] = lengthbits >> 16 & 0xff; init_arr[lenZ ++] = lengthbits >> 24 & 0xff; //因为uint最多32位所以我们只要考虑四个字节就可以了,虽然实际上要考虑64位,嘿 lenZ += 4;//这步我没读懂!!! for(int i = 0;i < 4;i ++){ state[i] = s_state[i];//将最开始的默认静态渐变变量赋值给静态渐变变量 }}inline uint MD5::F(uint X, uint Y, uint Z) { return (X & Y) | ((~X) & Z);}inline uint MD5::G(uint X, uint Y, uint Z) { return (X & Z) | (Y & (~Z));}inline uint MD5::H(uint X, uint Y, uint Z) { return X ^ Y ^ Z;}inline uint MD5::I(uint X, uint Y, uint Z) { return Y ^ (X | (~Z));}uint MD5::ROL(uint s, uint ws) { return (s << ws) | (s >> (32 - ws));}inline void MD5::FF(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac) { a = ROL(a + F(b, c, d) + x + ac, s) + b; //printf("ff\n");}inline void MD5::GG(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac) { a = ROL(a + G(b, c, d) + x + ac, s) + b; //printf("gg\n");}inline void MD5::HH(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac) { a = ROL(a + H(b, c, d) + x + ac, s) + b; //printf("hh\n");}inline void MD5::II(uint &a, uint b, uint c, uint d, uint x, uint s, uint ac) { a = ROL(a + I(b, c, d) + x + ac, s) + b; //printf("ii\n");}//这里前面讲了inline uint MD5::T(uint i) { return (uint)((0xffffffff + 1LL) * abs(sin(i)));}//取64个字节放在Memory数组中void MD5::data_Init() { uint tmp = 0; for(int i = 0; i < 64; i ++) { Memory[i] = init_arr[curM + i]; } curM += 64;//变化位置}void MD5::create_T_arr() { for(int i = 1; i <= 64; i ++) { Tarr[i - 1] = T(i); }}/*这里使用了小端将数据存在M数组中,可以稍微思考一下*/void MD5::create_M_arr() { uint tmp = 0; int cnt = 0; for(int i = 0; i < 64; i += 4) { tmp = 0; for(int j = 3; j >= 0; j --) { tmp |= Memory[i + j]; if(j == 0) break; tmp <<= 8; } M[cnt ++] = tmp; }}//移动a,b,c,d,最后一个移到第一个void MD5::l_data_change(uint *buf) { uint buftmp[4] = {buf[3], buf[0], buf[1], buf[2]}; for(int i = 0; i < 4; i ++) { buf[i] = buftmp[i]; }}void MD5::processing() { uint statetmp[4]; for(int i = 0; i < 4; i ++) { statetmp[i] = state[i]; } /* 这里的处理只是为了更方便的循环 */ uint * a = &statetmp[0]; uint * b = &statetmp[1]; uint * c = &statetmp[2]; uint * d = &statetmp[3]; /* 产生M数组和T数组 */ create_M_arr(); create_T_arr(); /* 建立函数指针数组 循环处理 */ deal_fun d_fun[4] = { &MD5::FF, &MD5::GG, &MD5::HH, &MD5::II }; for(int i = 0; i < 4; i ++) { for(int j = 0; j < 16; j ++) { (this ->* d_fun[i])(*a, *b, *c, *d, M[mN[i][j]], rolarray[i][j % 4], Tarr[i * 16 + j]); l_data_change(statetmp);//交换a,b,c,d } } for(int i = 0; i < 4; i ++) { state[i] += statetmp[i]; }}string MD5::get_MD5() { string result; char tmp[15]; for(int i = 0;i < (lenZ - (offset - 1) * 64) / 64;i ++){ data_Init(); processing(); } /* 最终显示也是用小端 */ for(int i = 0; i < 4; i ++) { sprintf(tmp, "%02x", state[i] & 0xff); result += tmp; sprintf(tmp, "%02x", state[i] >> 8 & 0xff); result += tmp; sprintf(tmp, "%02x", state[i] >> 16 & 0xff); result += tmp; sprintf(tmp, "%02x", state[i] >> 24 & 0xff); result += tmp; } return result;}int main() { MD5 md1("123456789123456adminadmin123456789123456789123456789123456789123admin",2); cout << md1.get_MD5() << endl; return 0;}
代码不懂得的可以去原博主去深究,博客末尾处会贴上链接
运行代码可得hash值:
bp得答案:
不行了不行了,写博客头痛了就,理解和写博客完全是两种感觉,有问题在博客下面留言吧,我会尽力解答
写在末尾,特别感谢,三篇好博文帮助我有了很好的理解:
http://www.freebuf.com/articles/web/31756.html
科普哈希长度扩展攻击(Hash Length Extension Attacks)
http://blog.csdn.net/qq_18661257/article/details/53767761
MD5加密算法详解(c和c++环境下)
http://blog.csdn.net/syh_486_007/article/details/51228628
hash哈希长度扩展攻击解析(记录一下,保证不忘)
- 实验吧-让我进去【salt加密 哈希长度拓展攻击】
- 让我进去--实验吧
- 浅析哈希长度拓展攻击
- SALT 加密
- salt加密
- 哈希长度扩展攻击解析
- 哈希长度扩展攻击以及HashPump
- Hashpump实现哈希长度扩展攻击
- 随机salt二次加密
- MD5+Salt加密机制
- 随机salt二次加密
- Shiro salt 加密
- MD5+Salt加密
- MD5+salt加密
- 加密中的“Salt”
- C# salt+hash 加密
- MD5+Salt加密
- 谈撒盐(salt)加密
- Android中Jni入门常见异常
- HDU 1028 Ignatius and the Princess III(母函数)
- securefx连接linux后文件夹中文乱码问题解决
- js中退出语句break,continue和return 比较
- 双链表的创建、遍历、测长、插入、删除、销毁
- 实验吧-让我进去【salt加密 哈希长度拓展攻击】
- 判断一棵树是否是完全二叉树【每日一题】
- Apache Spark 内存管理详解
- Java中String和byte[]互转问题
- ES6 module export和import
- strcpy的参数
- 注册表
- SSAS对称维度与非对称维度
- windiows 下 WSAEventSelect模型