在阅读此article时 绕过一些保护
如果您开始阅读该文章,您会发现作者做了一个非常奇怪的计算,我不理解它:
好吧,让我们开始吧:
RET2RET
演示代码(包含易受攻击的函数strcpy
)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char **argv) {
char buf[256];
strcpy(buf, argv[1]);
return 1;
}
在使用GDB分解上面的代码后,您将得到如下所示(反汇编主函数):
(gdb) disassemble main
Dump of assembler code for function main:
0x08048384 <main+0>: push %ebp
0x08048385 <main+1>: mov %esp,%ebp
0x08048387 <main+3>: sub $0x108,%esp
0x0804838d <main+9>: and $0xfffffff0,%esp
0x08048390 <main+12>: mov $0x0,%eax
0x08048395 <main+17>: sub %eax,%esp
0x08048397 <main+19>: sub $0x8,%esp
0x0804839a <main+22>: mov 0xc(%ebp),%eax
0x0804839d <main+25>: add $0x4,%eax
0x080483a0 <main+28>: pushl (%eax)
0x080483a2 <main+30>: lea 0xfffffef8(%ebp),%eax
0x080483a8 <main+36>: push %eax
0x080483a9 <main+37>: call 0x80482b0 <_init+56>
0x080483ae <main+42>: add $0x10,%esp
0x080483b1 <main+45>: mov $0x1,%eax
0x080483b6 <main+50>: leave
0x080483b7 <main+51>: ret
End of assembler dump.
(gdb)
之后,我们在strcpy上设置一个断点,因为它是一个易受攻击的函数:
(gdb) break *main+37
Breakpoint 1 at 0x80483a9
现在我们运行程序,并给它一大堆A
char来使缓冲区溢出
(gdb) run `perl -e 'print "A"x272'`
Starting program: /tmp/vuln `perl -e 'print "A"x272'`
之后,我们打印eax
寄存器的内容(我认为eax
持有buf
地址,如果我错了,请纠正我)
(gdb) print (void *) $eax
$1 = (void *) 0xbffff5d0
好吧,文章的作者在下面说了这句话
简单的计算即可得出'buf'变量范围[
0xbffff6d8
-0xbffff5d0
] /(264 bytes
;0x108h
)
我不知道这是什么意思,以及他想出那个0xbffff6d8
地址的地方
答案 0 :(得分:1)
如果我错了请纠正我
首先,您对eax
是正确的,它包含buf
的地址(有很多方法)。要确保这一点:首先,最简单的是将args
个函数以相反的顺序压入堆栈,因此最后一次压入将是第一个args
,如您所见反汇编窗口,调用该函数之前的最后一个窗口是push eax
,因此eax
中的值必须为buf
地址。
我不知道这是什么意思,也不清楚他在哪里提出该
0xbffff6d8
地址
0xbffff6d8
是ebp
的值,0xbffff6d8 - 0xbffff5d0
背后的想法是在开始抛出段错误之前了解最大buf大小的多种方法之一
无需执行此计算即可知道此信息的另一种方法是,从上方的反汇编窗口中看到0x08048387 <main+3>: sub $0x108,%esp
,它为本地buf
数组分配了一个空间,但并非在所有情况下都是如此:取决于局部变量的数量(如果每次都有一个局部变量/数组,那么它将每次都起作用,否则您必须聪明地使用此方法),但是作者的方法每次都会起作用。
答案 1 :(得分:1)
ret2ret漏洞利用的基本思想是在栈上找到一个可以稍作更改的值,以便它指向一个可能溢出的缓冲区。通过执行所需数量的ret
指令以达到该值,您将最终能够在缓冲区(漏洞利用)中执行代码。
在执行call 0x80482b0 <_init+56>
之前,示例中的堆栈如下所示:
--------------------
| 0xbffff6f0 | ebp+28
--------------------
| 0xb7fdb000 | ebp+24
--------------------
| 0xb800167c | ebp+20
--------------------
| 0xbffff770 | ebp+16 (previous stack frame)
--------------------
| 0xbffff764 | ebp+12 (points to the argv array)
--------------------
| 0x2 | ebp+8 (holds the value of argc)
--------------------
| main ret addr | ebp+4
--------------------
|previous ebp value| ebp
--------------------
| appears to be |
| unused | ebp-8
--------------------
| |
| 256-byte |
| buf |
| |
| |
| | ebp-264
--------------------
| | ebp-268 (holds the second argument to strcpy)
--------------------
| | ebp-272 = esp (holds the first argument to strcpy)
--------------------
那么如何在此堆栈上执行成功的ret2ret攻击?我们知道main
中的最后一条指令是ret
。我们需要找到一种方法来多次执行该指令,以便最终执行我们决定放入buf
中的任何代码。请记住,每次ret
在32位x86模式下执行时,它将从堆栈中弹出4字节的值。因此,我们需要弄清的第一件事是需要从堆栈中弹出多少个4字节值才能得到一个恰好表示指向buf
的指针的值。本文的作者显示,第一个这样的值在ebp+28
。范围0xbffff6d8-0xbffff5d0包含256字节缓冲区。但是,ebp+28
处的值为0xbffff6f0,该值实际上在缓冲区外部。但是,由于strcpy
将在末尾附加一个NULL字节,因此我们可以使它用NULL覆盖ebp+28
的第一个字节,使其包含指向缓冲区内部的0xbffff600。从ebp+4
开始,有7个4字节值。因此,我们需要执行ret
7次,然后才能从buf
中的某个位置继续执行。
要实现此目的,需要将ebp+4
处的值设置为ret
中main
的地址,即0x080483b7。同样,所有ebp+24
之前(包括ret
)的所有4字节位置都必须设置为0x080483b7。这样,ret
的前6次执行将跳转到ret
,一次又一次地执行。但是最后一次执行evilbuf
时,控制权将转移到缓冲区将被覆盖的0xbffff6f0。
现在考虑当利用代码中的argv[1]
作为ebp-264
传递时会发生什么。该缓冲区包含261个单字节NOP指令。接下来的7个字节是执行出口系统调用的指令(尽管真正的利用会做更多有趣的事情)。这268个字节将覆盖从ebp+3
到ret
的所有字节。然后有strcpy
的地址的6个副本。最后,ebp+28
将为我们慷慨地附加一个NULL,从而覆盖strcpy
处的字节。
在ret
返回并且main
中的ret
首次执行之后,它将再次执行6次,然后将执行0xbffff600处的指令。这是我们的NOP之一。然后NOP sled的其余部分将被执行,直到到达设计的指令序列(有效负载)为止,在这种情况下,该序列仅执行出口系统调用。
Ret2ret不需要先验缓冲区地址。因此,即使OS将堆栈的基地址随机化,它也可以工作。但这需要知道 pdfView1 = findViewById(R.id.pdfViewer1);
pdfView1.fromAsset("cpgTest1.pdf")
.scrollHandle(new DefaultScrollHandle(this))
.defaultPage(currentPage)
.onPageChange(this)
.load();
}
@Override
public void onPageChanged(int page, int pageCount)
{
currentPage = page;
}
@Override
public void loadComplete(int nbPages)
{
if (currentPage >= 0)
{
pdfView1.jumpTo(currentPage);
}}
@Override
public void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putInt(KEY_CURRENT_PAGE, currentPage);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
currentPage = savedInstanceState.getInt(KEY_CURRENT_PAGE);
}
}
指令的地址。