Protostar - Stack 6 (쉘 코드 + 버퍼 오버플로우 + RTL)
oolongeya
·2021. 9. 1. 13:42
Stack6.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void getpath()
{
char buffer[64];
unsigned int ret;
printf("input path please: "); fflush(stdout);
gets(buffer);
ret = __builtin_return_address(0);
if((ret & 0xbf000000) == 0xbf000000) {
printf("bzzzt (%p)\n", ret);
_exit(1);
}
printf("got path %s\n", buffer);
}
int main(int argc, char **argv)
{
getpath();
}
|
cs |
ret = __builtin_return_address 라는 함수가 있다.
이 함수는 현재 실행중인 함수의 주소를 ret으로 가져와서 ret에 뭐가 있는지 점검하는 함수이다.
if((ret & 0xbf000000) 이라는 조건문은 그렇게 가져온 주소가 bf 주소를 포함한다면, 무력하게 만드는 의미로 보인다.
이 조건문이 참이라면 printf 함수가 실행되고 exit로 프로그램이 종료되어버리니 이 함수가 실행되면 안된다.
간단하게 생각하면 주소에 0xbf 가 들어가면 안된다!!
문제를 풀기위해 이러한 중간 부분을 피해서 ret 으로 진입해야 한다는 것이다.
아래의 게시글은 return to library 기법을 다룬다. 읽고 오면 이해하기 쉽다.
https://kkomii22.tistory.com/73
자 그럼 필요한 정보는
1. system() 의 주소
2. /bin/sh의 주소
3. 스택이 첫번째 요소까지의 거리
3가지를 확인해야 한다.
먼저 system 함수부터 찾자
프로그램의 메인부분에서 get부분에 브레이크 포인트를 걸어두고 한번 run을 해야 system 주소를 확인할 수 있다.
disas system, print system 편한 방식으로 찾자.
system 함수의 주소를 먼저 확인한다. 0x7ffff7e37e50
/bin/sh의 주소도 찾을 수 있다. 0x7ffff7f79152
패턴을 만들어서 스택의 첫번째 요소까지 얼마나 떨어져있는지 확인을 해보자.
88 바이트 이후에 rip로 옮겨질 스택의 첫 요소값이 들어가는 것을 확인할 수 있다.
그 다음 값이 이제 ret 주소로 들어가는 것이다.
그렇다면
1. system() 의 주소 = 0x7ffff7e37e50
2. /bin/sh의 주소 = 0x7ffff7f79152
3. 스택이 첫번째 요소까지의 거리 = 88
ret2lib 기법 설명 게시글에서 작성한 stack5.py 코드이다.
큰 변화 없이 아래 코드와 유사하게 작성할 것이다.
그런데 문제가 있다 위 stack5.py 파일은 32비트 기반의 Protostar 이미지 파일에서 사용한 것이다.
Stack6.c 파일은 64비트 환경 리눅스에서 우리가 새롭게 코드를 작성하고 컴파일한 파일이므로
64비트 기반이므로 위와 같은 코드로 작성하면 안된다.
그렇다면 어떻게 해야할까?
우선 RTL(Return to Library) 기법은 NX bit라는 메모리 보호 기법을 우회하기 위한 공격이다.
NX bit는 스택영역에 쉘 코드를 삽입해서 이 쉘 코드를 실행할 수 없도록 스택 상의 실행 권한을 제거해서 프로세스 명령어나 데이터 저장을 위한 메모리 영역을 분리 시키는 기술이다.
RTL(Return to Library) 기법은 x86, 64 각각 다른 비트수에서 다른 방식으로 페이로드를 짜야한다.
각 비트별 함수 호출 규약을 살펴봐야 한다는 것이다.
x86에서는 cdecl 방식을 규약으로 사용한다.
1 2 3 4 순서대로 작성해서 컴파일을 한다면, push를 이용해서 스택에 0x4, 0x3, 0x2, 0x1로 집어 넣는다.
x64에서는 fastcall 방식을 규약으로 사용한다.
1 2 3 4 순서대로 작성해서 컴파일을 한다면, 레지스터(rdi, rsi, rdx, rcx, r8, r9)를 이용한다.
rdi(1), rsi(2), rdx(3), rcx(4) 를 이용해서 인자를 전달한다.
즉 함수 호출 규약에서의 차이가 존재하기 때문에 페이로드도 달라지는데,
x86 방식에서 페이로드는 스택에서 첫번째로 꺼내야 하는 인자값이 ret에 들어가는 것이다.
(패딩) + ret(system 함수의 주소) + 더미(system 함수의 ret값을 더미로 덮음) + system 함수의 인자(/bin/sh 의 주소) 방식으로 페이로드를 작성한다.
x64 방식에서는 ret에 "pop rdi, ret" 코드가 저장된 주소값이 들어가야 한다.
ropsearch 'pop rdi; ret' 으로 코드의 주소를 확인할 수 있다. (0x0040125b)
(패딩) + ret("pop rdi, ret" 코드 주소) + 레지스터 인자값("/bin/sh") + system 함수의 주소 방식으로
페이로드를 작성해야 한다.
위와 같은 페이로드를 작성해서 공격파일을 만들겠다.
성공적으로 쉘을 얻을 수 있다.
'시스템 해킹 (Pwnable) > Wargame' 카테고리의 다른 글
Protostar - Stack 7 (쉘 코드 + 버퍼 오버플로우 + RTL) (0) | 2021.09.12 |
---|---|
Dreamhack(pwnable) - sint (0) | 2021.09.01 |
Protostar - Stack 5 (쉘 코드 + 버퍼 오버플로우 + attach) (0) | 2021.08.28 |
Protostar - Stack 4 (버퍼 오버플로우 공격 + 메모리 참조) (0) | 2021.08.25 |
Protostar - Stack 3 (버퍼 오버플로우 공격 + 메모리 참조) (0) | 2021.08.24 |