无法覆盖 EIP 寄存器

信息安全 开发 缓冲区溢出
2021-09-05 11:51:31

两个程序的源代码在帖子末尾

所以,我一直在研究黑客:剥削的艺术,到目前为止一切都很好。我已经设法在易受攻击的程序 notesearch.c 中控制 EIP。

gdb-peda$ run $(perl -e 'print "a"x112 . "bbbb"')
Starting program: /root/hacking/booksrc/notesearch $(perl -e 'print "a"x112 . "bbbb"')
[DEBUG] found a 5 byte note for user id 0
[DEBUG] found a 7 byte note for user id 0
-------[ end of note data ]-------

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers----------------------------------    -]
EAX: 0x0 
EBX: 0x0 
ECX: 0xbffff300 ('a' <repeats 36 times>, "\003")
EDX: 0x0 
ESI: 0x2 
EDI: 0xb7fb5000 --> 0x1b3db0 
EBP: 0x0 
ESP: 0xbffff300 ('a' <repeats 36 times>, "\003")
EIP: 0x61616161 ('aaaa')
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction     overflow)
[-------------------------------------code------------------------------------    -]
Invalid $PC address: 0x61616161
[------------------------------------stack------------------------------------    -]
0000| 0xbffff300 ('a' <repeats 36 times>, "\003")
0004| 0xbffff304 ('a' <repeats 32 times>, "\003")
0008| 0xbffff308 ('a' <repeats 28 times>, "\003")
0012| 0xbffff30c ('a' <repeats 24 times>, "\003")
0016| 0xbffff310 ('a' <repeats 20 times>, "\003")
0020| 0xbffff314 ('a' <repeats 16 times>, "\003")
0024| 0xbffff318 ('a' <repeats 12 times>, "\003")
0028| 0xbffff31c ("aaaaaaaa\003")
[-----------------------------------------------------------------------------    -]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x61616161 in ?? ()
gdb-peda$ 

但是,一旦我编写了自己的非常简单的错误代码并尝试控制 EIP,就会发生这种情况

gdb-peda$ run
Starting program: /root/vulnerable 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbbbb

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0x0 
ECX: 0x41414141 ('AAAA')
EDX: 0xb7fb687c --> 0x0 
ESI: 0x1 
EDI: 0xb7fb5000 --> 0x1b3db0 
EBP: 0x41414141 ('AAAA')
ESP: 0x4141413d ('=AAA')
EIP: 0x804841d (<main+50>:  ret)
EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8048416 <main+43>: mov    ecx,DWORD PTR [ebp-0x4]
   0x8048419 <main+46>: leave  
   0x804841a <main+47>: lea    esp,[ecx-0x4]
=> 0x804841d <main+50>: ret    
   0x804841e:   xchg   ax,ax
   0x8048420 <__libc_csu_init>: push   ebp
   0x8048421 <__libc_csu_init+1>:   push   edi
   0x8048422 <__libc_csu_init+2>:   push   esi
[------------------------------------stack-------------------------------------]
Invalid $SP address: 0x4141413d
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0804841d in main ()
gdb-peda$ p/x $eip
$1 = 0x804841d
gdb-peda$ 

我什么都没有,ESP不应该保持不变(因为它没有存储在堆栈中)并且EIP被覆盖了吗?

你可以找到 notesearch.c @ https://github.com/intere/hacking 下面是我所谓的“可利用”程序。

不用说,我禁用了 ASLR,并且程序是使用 -fno-stack-protector 和 -zexecstack 标志编译的。如果您需要更多信息,请发表评论。

#include <stdio.h>

int main(){
    char *buffer[64];
    gets(buffer);
    return 0;
}
1个回答

你的代码是错误的:

#include <stdio.h>

int main(){
    char *buffer[64];
    gets(buffer);
    return 0;
}

创建一个指向字符的char *buffer[64]指针数组。

再试一次:

#include <stdio.h>

int main(){
    char buffer[64];
    gets(buffer);
    return 0;
}

在 IDEone 运行它


实际上,我可以看到很多为什么这不起作用的原因。让我们通过您的程序集:

   0x8048416 <main+43>: mov    ecx,DWORD PTR [ebp-0x4]

由于ebp-0x4是输入缓冲区中的一个位置,ecx因此加载了0x41414141.

   0x8048419 <main+46>: leave  

这将清除当前堆栈帧,基本上设置espebp,然后将堆栈顶部的值弹出到ebp以恢复前一个帧指针。您可以再次看到它ebp被设置为0x41414141,因为堆栈中的顶部项目仍然是缓冲区的一部分。

   0x804841a <main+47>: lea    esp,[ecx-0x4]

这会加载ecx-0x4into的有效地址esp想像lea一个方便的指针数学指令。在这种情况下,它实际上只是在做esp = ecx - 0x4. 这就是为什么你的esp价值是0x4141413d.

=> 0x804841d <main+50>: ret    

一条ret指令设置eip到堆栈中的顶部值。这里的问题是您的堆栈指针设置为无效内存。你用0x4141413d.

为什么会这样?首先,您的有效负载几乎可以肯定是太长了,但是您的代码所做的与示例代码所做的在汇编中存在差异。但是,有一线希望:您可以在ret指令之前立即控制堆栈指针。这意味着您只需指向esp您控制的一些内存,您就可以eip控制。

您要做的是运行代码直到到达<main+43>,然后计算出有效负载中的偏移量已放置在ebp+0x4. 您可以使用诸如 Metasploit 的 pattern_create.rb 之类的工具使这更容易一些,但只需观察堆栈中的值就足以手动解决。

因此,让我们假设有效负载中的 80 个字节是应用程序从中读取该值的地方。您的有效负载现在想要以'A' * 80. 在此之后,将堆栈地址减去 8 个字节编码到有效负载中。因此,如果此时的堆栈指针为 0x4120EC40,则减 8 得到 0x4120EC38,然后将所有\x41\x20\xEC\x38放入您的有效负载中。A

当你下次运行程序时,应该发生的事情是这样的:

0x8048416 <main+43>: mov    ecx,DWORD PTR [ebp-0x4]
; [ebp-0x4] contains an address slightly further up the stack (0x4120EC38)
; all the memory around this address contains 0x41414141
0x8048419 <main+46>: leave  
; leave the stack frame, setting esp and popping ebp
; both registers are trashed but it doesn't matter
0x804841a <main+47>: lea    esp,[ecx-0x4]
; subtract 4 from 0x4120EC38, giving us 0x4120EC34
; then set esp to this value - [esp] is now 0x41414141
0x804841d <main+50>: ret    
; pop the top value off the stack and set eip to it
; eip is now 0x41414141

你赢了。

作为附录,您可能会遇到无法将堆栈地址编码到有效负载中的问题,因为它包含空字节。您可以通过缩短有效负载来解决此问题,以便它仅部分覆盖堆栈帧指针,因此您可以更改最低有效字节(或两个字节),允许部分控制esp以将其指向堆栈的更上方,而不是完全覆盖它.