代码
1 |
|
解题思路
首先定位到1
2
3
4if(hashcode == check_password( argv[1] )){ //这行给出flag,即想办法让这两个相等
system("/bin/cat flag");
return 0;
}
说明要想得到flag就要让 你的输入 传入函数后得到一个与hashcode一样的值
if(strlen(argv[1]) != 20){ 这个if语句也告诉我们输入要为20个字节
int* ip = (int*)p;
这里对p进行了一次强制转化,从原本读出来的char转化为了int
char占一个字节,8个比特。int占4个字节,32个比特。 (十六进制0x01就是一个字节了,那么十六进制下01010101就可以表示一个int数了)
hashcode == check_password( argv[1] )实现了一个累加
这里需要构造5个int类型的数(总共是20字节)相加为0x21DD09EC
实际上就是把5*4=20个字符,每4个分成一组累加。
普遍的一个想法: 0x1DD905E8+0x01010101*4=0x21DD09EC 这里用01010101来构造
那么开始操作,实践
探究数据在内存中的存储方式
1 |
|
1 | 1145258561 |
转为16进制:1
2
3
4
50x44434241
0x48474645
0x4C4B4A49
0x504F4E4D
0x535251
如果我们输入A-T这20个字符,那么会报出一下的错误:
[Error] initializer-string for array of chars is too long [-fpermissive]
说明数组的限制了,那么推理出应是结束符需要占一个的位子
接着测试
发现输出了一个空行,那么应该是结束符了
接着进行测试1
2
3
4
5
6
7
8
9
10
11
12#include<bits/stdc++.h>
using namespace std;
int main()
{
char str[20]={'\x01','\x02','\x03','\x04','\x05','\x06','\x07','\x08','\x09','\x10','\x11','\x12','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01'};
int *p=(int *)str;
for(int i=0; i<5; i++){
cout<<dec<<p[i]<<endl;
cout<<hex<<p[i]<<endl;
}
return 0;
}
得到1
2
3
4
5
6
7
8
9
1067305985
4030201
134678021
8070605
303108105
12111009
16843009
1010101
16843009
1010101
尝试构造payload
0x1DD905E8+0x010101014=0x21DD09EC
我们通过之前的观察发现转换为int时,四个字符是倒过来存储合并的, 0x1DD905E8我们要写成’\xE8’,’\x05’,’\xD9’,’\x1D’才行
那么尝试:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using namespace std;
int main()
{
char str[20]={'\xE8','\x05','\xD9','\x1D','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01','\x01'};
int *p=(int *)str;
for(int i=0; i<5; i++){
cout<<dec<<p[i]<<endl;
cout<<hex<<p[i]<<endl;
}
int tmp=0;
for(int i=0; i<5; i++)
tmp+=p[i];
cout<<dec<<tmp;
printf("\n");
cout<<hex<<tmp;
return 0;
}
输出1
2
3
4
5
6
7
8
9
10
11
12500762088
1dd905e8
16843009
1010101
16843009
1010101
16843009
1010101
16843009
1010101
568134124
21dd09ec
发现构造成功,得到我们想要的0x21DD09EC
终端输入
大佬的爬虫脚本1
2
3
4
5
6
7
8
9
10
11
12#!/usr/bin/python
import subprocess
from pwn import *
hashcode = 0x21DD09EC
a = 0x01020304
b = hashcode - 4 * a
payload = p32(a) * 4 + p32(b)
target = subprocess.Popen(args=['/home/col/col',payload])
另解
python -c “print ‘\xe8\x05\xd9\x1d’+16*’\x01’”|xargs ./col因为是小端机器需要反序,xargs可以把数据当作命令行参数传给制定的程序。
来自https://blog.csdn.net/u010334666/article/details/81193660的python代码1
2
3
4
5
6
7
8
9
10 collision.py+ buffers
1 #!/usr/bin/env python
2 # coding=utf-8
3 from pwn import *
4 pwn_ssh=ssh(host='pwnable.kr',user='col',password='guest',port=2222)
5 print(pwn_ssh.connected())
6 sh=pwn_ssh.process(argv=['collision','\xc9\xce\xc5\x06' * 4 + '\xcc\xce\xc5\x06'],executable='./col')#那些数字是0x21DD09EC/5计算得到,然后用0x21DD09EC-得到的结果*4
7 print(sh.recvall())
~
~
补充
‘\x’
\xhh 表示1到2位十六进制所代表的任意字符
字符型常量所表示的值是字符型变量所能包含的值。我们可以用ASCII表达式来表示一个字符型常量,或者用单引号内加反斜杠表示转义字符。
\x表示后面的字符是十六进制数,\o表示后面的字符是八进制数。例如十进制的17用十六进制表示就是
‘\x11’,用八进制表示就是‘\021’;
所有的ASCII码都可以用“\”加数字(一般是8进制数字)来表示。而C中定义了一些字母前加”\”来表示常见的那些不能显示的ASCII字符,如\0,\t,\n等,就称为转义字符,因为后面的字符,都不是它本来的ASCII字符意思了。
char *p
char *p
char* p和char *p
eg:
char *p=”abc123ABC”;//char p[]=”abc123ABC”
char* p是一个指针,根本没分配内存,他指向的”abc123ABC” 是只读的,不能改变,你在下面给他赋值肯定是错的
而char p[]是一个数组,已经分配内存,是将”abc123ABC” 复制到该内存里面,这个内存是可读写的1
2
3
4
5
6
7
8
9
10
11
12
13
14
15const char *p; //*p是const,p可变:const 后面紧跟的是char,所以*p是一个char字符,不可变
const (char *) p;//p是const,*p可变:const 后面紧跟的是(char *)这个整体,所以p是char*类型,不可变。
char* const p; //p是const,*p可变:const 后面紧跟的是p,所以p不可变
const char* const p; //p和*p都是const:第一个const后面紧跟的是char,所以char类型的字符*p不可变;第二个const后面紧跟的是p,所以p不可变。
char const * p;// *p是const,p可变:const后面紧跟的是*,但是单独的*不能表明修饰的内容,所以将*p看成一个整体,所以const修饰的是*p,*p不可变。
(char*) const p;//p是const,*p可变:const紧跟的是p,所以p不可变。
char* const p;// p是const,*p可变:const紧跟的是p,所以p不可变。
char const* const p;// p和*p都是const:第一个const紧跟的是*,不能表明修饰的内容,将后面整体的(* const p)看成一个整体,那就说明*p不可变,第二个const后面紧跟的是p,所以p不可变。
原文:https://blog.csdn.net/qq_38204481/article/details/79711702
原文:https://blog.csdn.net/jack0201/article/details/75071980
原文:https://blog.csdn.net/u010334666/article/details/81193660