pwnable.krToddler‘s Bottle-[collision]
Posted KUSIA_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pwnable.krToddler‘s Bottle-[collision]相关的知识,希望对你有一定的参考价值。
打开题目审题
Daddy told me about cool MD5 hash collision today.
I wanna do something like that too!
ssh col@pwnable.kr -p2222 (pw:guest)
题目中提到MD5哈希碰撞,可以基本确定方向
输入命令回车,输入密码:guest 即可登录服务器。
找到突破口
照例先
ls -la
col@pwnable:~$ ls -la
total 36
drwxr-x--- 5 root col 4096 Oct 23 2016 .
drwxr-xr-x 115 root root 4096 Dec 22 2020 ..
d--------- 2 root root 4096 Jun 12 2014 .bash_history
-r-sr-x--- 1 col_pwn col 7341 Jun 11 2014 col
-rw-r--r-- 1 root root 555 Jun 12 2014 col.c
-r--r----- 1 col_pwn col_pwn 52 Jun 11 2014 flag
dr-xr-xr-x 2 root root 4096 Aug 20 2014 .irssi
drwxr-xr-x 2 root root 4096 Oct 23 2016 .pwntools-cache
发现flag文件,尝试查看(肯定看不了
fd@pwnable:~$ cat flag
cat: flag: Permission denied
fd@pwnable:~$
果然不能看。看下别的,文件flag肯定是最后的答案,所以需要看看其他文件找寻突破口。
还有一个c语言源文件col.c
和可执行文件col
。
那没什么说的,看看col.c。
col@pwnable:~$ cat col.c
#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
int* ip = (int*)p;
int i;
int res=0;
for(i=0; i<5; i++){
res += ip[i];
}
return res;
}
int main(int argc, char* argv[]){
if(argc<2){
printf("usage : %s [passcode]\\n", argv[0]);
return 0;
}
if(strlen(argv[1]) != 20){
printf("passcode length should be 20 bytes\\n");
return 0;
}
if(hashcode == check_password( argv[1] )){
system("/bin/cat flag");
return 0;
}
else
printf("wrong passcode.\\n");
return 0;
}
找到重点 system("/bin/cat flag");
看到源码大概已经明白了,我们没有查看文件的权限,但是fd有,所以只要分析如何让代码执行system("/bin/cat flag");
即可。
源代码分析
col@pwnable:~$ cat col.c
#include <stdio.h>
#include <string.h>
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
int* ip = (int*)p;
int i;
int res=0;
for(i=0; i<5; i++){
res += ip[i];
}
return res;
}
int main(int argc, char* argv[]){
// 要有参数
if(argc<2){
printf("usage : %s [passcode]\\n", argv[0]);
return 0;
}
// 第一个参数长度必须是20字节
if(strlen(argv[1]) != 20){
printf("passcode length should be 20 bytes\\n");
return 0;
}
// 对argv[1]进行check_password 结果与hashcode 相等
if(hashcode == check_password( argv[1] )){
system("/bin/cat flag");
return 0;
}
else
printf("wrong passcode.\\n");
return 0;
}
单独分析
unsigned long check_password(const char* p)
unsigned long check_password(const char* p){
int* ip = (int*)p;
int i;
int res=0;
for(i=0; i<5; i++){
res += ip[i];
}
return res;
}
res 就是将 参数每四个字节相加后的结果,联系main函数,可知我们输入参数每四个字节相加必须hashcode = 0x21DD09EC
解题步骤
其实就是计算五个数,五个数相加等于 0x21DD09EC 算一下这五个数。 设为 a、a、a、a、a + b
继续计算器计算:0x06C5CEC8 * 5 = 0x21DD09E8 与原数相差 4
五个数就计算出来了
0x06C5CEC8、0x06C5CEC8、0x06C5CEC8、0x06C5CEC8、0x06C5CECC
但是我们如何保证我们输入的数正好以我们想要的形式存在呢。直接作为参数输入的话。
col@pwnable:~$ ./col 06C5CEC806C5CEC806C5CEC806C5CEC806C5CECC
passcode length should be 20 bytes
col@pwnable:~$
明显是不对的,会把每个字符读作一个字节,这样就有40字节了。
我们实际要将这些值直接写到内存里,也即这些值是已经对应好的ascii值。并且是大端模式,对应数据的高字节存放在低地址。【可以使用GDB进行调试或者两种模式都输入尝试】print 从高地址向低地址写,因此综合与我们书写相反。【大小端应该都懂】
那么问题来了,如何将这些值直接写到内存里呢? 使用命令:
"print '\\xCC\\xCE\\xC5\\x06'+'\\xC8\\xCE\\xC5\\x06'*4"
构造输入命令:
./col $(python -c "print '\\xCC\\xCE\\xC5\\x06'+'\\xC8\\xCE\\xC5\\x06'*4")
python -c是指用python解释器执行print ‘\\xCC\\xCE\\xC5\\x06’+’\\xC8\\xCE\\xC5\\x06’*4
$()
指令是括号内的字符串被shell解释为命令行,在执行时,shell首先执行该命令行,并以它的标准输出结果取代整个$()
,整个语句的意思是将参数
‘\\xCC\\xCE\\xC5\\x06\\xC8\\xCE\\xC5\\x06\\xC8\\xCE\\xC5\\x06\\xC8\\xCE\\xC5\\x06\\xC8\\xCE\\xC5\\x06’
直接写入到内存。
col@pwnable:~$ ./col $(python -c "print '\\xCC\\xCE\\xC5\\x06'+'\\xC8\\xCE\\xC5\\x06'*4")
daddy! I just managed to create a hash collision :)
daddy! I just managed to create a hash collision 😃
验证即可
相关知识
介绍与上一篇有重复
MD5哈希碰撞
md5是一种被广泛使用的密码散列函数,可以产生一个128位的(16进制)散列值,2004年,我国中科院院士王小云证实md5算法无法防止碰撞,因此,不适用于安全性认证。
MD5作为文件校验方法已经不可靠了,可以人为制造碰撞。
int main( int argc , char * argv[ ] ,char * envp[ ])
main函数的参数列表保存了输入参数的信息:
第一个参数argc记录了输入参数的个数。且argc是包括程序本身在内的参数个数。如本题目,直接./fd运行,该参数实际上是1,而不是0。
第二个参数是字符串数组的,字符串数组的每个单元是char*类型的,指向一个c风格字符串,arg[ ]指向的数组中至少有一个字符指针,即arg[0].他通常指向程序中的可执行文件的文件名。
第三个参数是用来取得系统的环境变量,如:在DOS下,有一个PATH变量。当你在DOS提示符下输入一个命令的时候,DOS会首先在当前目录下找这个命令的执行文件。如果找不到,则到PATH定义的路径下去找,找到则执行,找不到返回Bad command or file name 。在DOS命令提示符下键入set可查看系统的环境变量。
$(python -c “print ‘\\xCC\\xCE\\xC5\\x06’+’\\xC8\\xCE\\xC5\\x06’ * 4”)详解
$(linux命令)
在执行时,shell首先执行该命令行,并以它的标准输出结果取代整个$()
python -c
可以在命令行中调用 python 代码, 实际上 -c 就是 command 的意思
print str
将字符串写入到内存中
参考:
https://blog.csdn.net/jxz_dz/article/details/102755923
https://zhuanlan.zhihu.com/p/131283811
https://www.pythonheidong.com/blog/article/147292/6142802b1e89b9028188/
以上是关于pwnable.krToddler‘s Bottle-[collision]的主要内容,如果未能解决你的问题,请参考以下文章
pwnable.krToddler‘s Bottle-[fd]
pwnable.krToddler‘s Bottle-[random]