栈溢出

缓冲区溢出:简单来说,缓冲区溢出就是超长的数据向小缓冲区复制,导致数据超出了小缓冲区,致使缓冲区其他的数据遭到破坏
栈溢出是缓冲区溢出的一种,也是最常见的。只不过栈溢出发生在栈,堆溢出发生在堆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>
#include <string.h>

#define PASSWORD "12345"

int test(char *password)
{
    int ret_num;
    char buffer[8];
    ret_num = strcmp(password, PASSWORD);
    strcpy(buffer, password);
    return ret_num;
}

int main()
{
    int flag = 0;
    char password[88];
    while (1)
    {
        printf("plz input your password:\n");
        scanf("%s", password);
        flag = test(password);
        if (flag)
        {
            printf("incorrect password!\n\n");
        }
        else
        {
            printf("Success!You are right!\n");
        }
    }
    return 0;
}

当我们输入aaaaaaaa时,8个字节的“a”刚好覆盖了buffer,然后字符串结尾的截断符“\0”正好覆盖掉了紧挨着的ret_num的最低字节,使得ret_num变为0x00000000,满足条件

[!bug]
但是当我们输入01234567呢,虽然现在也是8个字节,但是在strcmp时,0-1为-1,而负数是以补码的形式存储的,且返回类型为int,即0xFFFFFFFF。当截断符“\0”覆盖时,仅能覆盖最低字节,也就是变为0xFFFFFF00,依旧是非0,无法通过

基本ROP

ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过现代操作系统的各种通用防御(比如内存不可执行和代码签名等)。通过上面的栈溢出漏洞原理,我们可以发现栈溢出的控制点是ret处那么ROP的核心思想就是利用以ret结尾的指令序列把栈中的应该返回EIP的地址更改成我们需要的值,实现任意跳转,从而控制程序的执行流程

ROP,其主要思想是在栈缓冲区溢出的基础上,利用程序中已有的小片段(gadgets)来改变某些寄存器或者变量的值,从而控制程序的执行流程。所谓gadgets就是以ret结尾的指令序列,通过这些指令序列,我们可以修改某些地址的内容,方便控制程序的执行流程。
之所以称之为ROP,是因为核心在于利用了指令集中的ret指令,改变了指令流的执行顺序。

ROP攻击一般需要满足以下条件

  • 程序存在溢出,并且可以控制返回地址
  • 可以找到满足条件的gadgets以及相应的gadgets地址
    如果gadgets每次的地址不同,我们就需要考虑动态地获取对应的地址

基本的ROP有:ret2text、ret2shellcode、ret2syscall、ret2libc