tmxklab
[FTZ] Level18 풀이 본문
1. 문제확인
1) 사용자 및 패스워드 : level18 / why did you do it
2) 파일확인
3) hint 코드 설명
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
void shellout(void);
int main()
{
char string[100]; // 사용자의 입력받은 문자열을 저장하는 배열
int check;
int x=0; // 사용자의 입력을 1byte씩 저장하는 변수
int count=0; // string배열에서 인덱스 역할
fd_set fds; // 파일 디스크립터(fd)를 저장하는 구조체 변수 선언(배열 형태)
printf("Enter your command: ");
fflush(stdout); // 출력 버퍼를 비움
while(1)
{
// 밑에 switch문의 default문이 실행되면 count값 증가
if(count >= 100)
printf("what are you tyring to do?\n");
// check가 0xdeadbeef면 shellout()호출(공격해야하는 부분)
if(check == 0xdeadbeef)
shellout();
else // check가 0xdeadbeef가 아닌 경우 실행
{
FD_ZERO(&fds); // fds구조체 변수 초기화
// STDIN_FILENO의 값은 0 표준 입력 파일 디스크립터 값
// STDIN_FILENO의 값이 0이므로 fds배열의 0번째에 1로 세팅
FD_SET(STDIN_FILENO, &fds);
// select함수는 fds에 할당된 fd의 이벤트가 발생하면 어떤 fd이벤트가 발생했는지 알려줌
// return값은 -1: 오류발생, 0: 타임아웃, 0보다 큰 수: 변화 발생 파일 디스크립터 수
if(select(FD_SETSIZE, &fds, NULL, NULL, NULL) >= 1)
{
// fileno함수는 stream과 현재 연관된 파일 핸들을 판별
// stdin은 0이므로 fileno(stdin)의 리턴 값은 0을 리턴함
// fds구조체 변수에 0으로 세팅되면 0이 아닌 값 return
if(FD_ISSET(fileno(stdin), &fds))
{
// 표준 입력으로 1byte만큼 x에 저장(사용자 입력 가능)
read(fileno(stdin), &x, 1);
// x의 값에 따라 switch문 실행
switch(x)
{
case '\r':
case '\n':
printf("\a");
break;
case 0x08:
count--;
printf("\b \b");
break;
default:
string[count] = x;
count++;
break;
}
}
}
}
}
}
// level19의 쉘 권한을 얻는 함수
void shellout(void)
{
setreuid(3099, 3099); // level19의 ruid와 euid값으로 변경
// execl()는 다른 프로그램을 실행하고 자신은 종료하는 함수
// 첫 번째 인자는 path, 두 번째 인자는 실행 파일명
// 즉, path에 지정한 경로명의 파일을 실행하며 argv0~argvn을 인자로 전달
execl("/bin/sh", "sh", NULL);
}
- select()와 관련된 매크로 및 함수들은 여기서 다루지 않음(소켓 프로그래밍 관련)
- while문이 무한으로 돌면서 1byte씩 사용자의 입력을 받아 x의 값에 따라 switch문 실행
- 또한, while문 처음부분에 2개의 if문이 실행되면서 계속 check와 count변수의 값 검사
2. 접근방법
1) attackme 디버깅
- <main+12, 19> : 처음 hint 코드에서 0으로 초기화 시키는 값은 x와 count변수, 따라서 x먼저 0으로 초기화 시키므로 [ebp-108]에 x변수가 존재하고 [ebp-112]에 count변수 존재
- <main+72, 76> : [ebp-112]와 0x63(99)와 비교하므로 [ebp-112]는 count변수 재확인
- <main+91> : [ebp-104]와 0xdeadbeaf를 비교하므로 [ebp-104]에 check변수 존재
- <main+417, 420> : [ebp-108]위치의 값을 edi에 그리고 다시 [ebp-252]위치에 넣음, 그리고 밑에 case문에서 지정된 값과 비교하는 것을 통해 switch문의 시작임을 알 수 있음
- <main+499 ~ 538> : <main+499>에서 [ebp-100]의 주소 값을 eax로 이동, [ebp-100]에 string의 주소 값 존재하며 밑에 행들을 보면 switch문의 default부분임을 알 수 있음
- <main+532> : x의 값이 들어있는 al을 count의 주소 값이 들어있는 edx와 string의 주소 값이 들어있는 ecx를 더한 [edx+ecx]에 넣는 것을 알 수 있음 → string[count] = x;
2) 스택구조
3) 공격방법 생각
- 현재 사용자의 입력 값을 받을 수 있는 부분은 1byte의 x값임
- check부분을 조작해야 하지만 스택구조를 보면 check가 string보다 더 낮은 주소에 위치
- 생각해 볼 수 있는 방법은 [ebp-100]에 위치한 string변수를 [ebp-104]로 옮겨야 함
- 따라서, string의 주소 값을 조작할 수 있는 부분은 switch문의 default 즉, stirng[count]=x;
- 초기 count값은 0이므로 string값을 읽어들일 때 [ebp-100]부터 읽게 됨, 하지만 string의 인덱스가 음수인 -값을 가질 때 어디서부터 읽는지 시도해봐야함
4) 0x08을 넣어 string 인덱스 값 조작
① 0x08의 개수 1~4까지 넣은 경우
- 0x08의 개수가 증가될수록 string의 인덱스인 count값이 처음 초기 값 0x00000000에서 0xffffffff, 0xfffffffe, 0xfffffffd, 0xfffffffc로 변함
- count의 값과 string의 주소 값을 더한 [edx+ecx]가 결국 x가 들어가는 위치이며 0x08의 개수가 증가될수록 [ebp-100]부터 [ebp-104]로 낮은주소로 향하는 것을 알 수 있음
② 0x08 4개와 쓰레기 값(0x99)의 개수 1~4까지 넣은 경우
- 0x08의 개수가 4개인 경우 check의 주소 값인 [ebp-104]를 참조하는 것을 알 수 있으므로 쓰레기 값을 증가 시켜 [ebp-104]의 위치에 어떤 값이 채워지는지 확인
※ 참고
gdb 실행 중 입력 값 넣는 방법
1) (gdb) r <<< $(python -c 'print "A"*3')
2) (gdb) r <<< "AAAAAAAAA"
3) (gdb) r <<< python -c 'print "A"*3'
5) 결론
- 0x08의 개수가 4개인 경우 string의 변수가 check의 주소 값인 [ebp-104]부터 참조하는 것을 알 수 있음
- 또한, 4개의 0x08을 넣고 추가적으로 쓰레기 값을 넣었을 때 check의 값이 쓰레기 값들로 채워지는 것을 알 수 있음
- 따라서, 4개의 0x08과 0xdeadbeef를 채우면 check의 값이 deadbeef로 채울 수 있음
3. 풀이
1) 공격 코드 작성 및 bof 수행
'War Game > FTZ' 카테고리의 다른 글
[FTZ] Level19 풀이 (0) | 2020.01.12 |
---|---|
[FTZ] Level17 풀이 (0) | 2020.01.12 |
[FTZ] Level16 풀이 (0) | 2020.01.12 |
[FTZ] Level15 풀이 (0) | 2020.01.12 |
[FTZ] Level14 풀이 (0) | 2020.01.12 |
Comments