문제 확인
file descriptor와 관련한 문제인 것 같다.
ssh fd@pwnable.kr -p2222로 접속해 fd문제를 풀 수 있다. 비밀번호는 아래 문제에서 알 수 있듯 guest이다.
pwnable.kr에 접속하여 ls 명령어로 어떤 파일이 있는지 확인한다.
현재 id는 fd인데 fd 실행파일의 경우 setuid가 걸려있어 fd_pwn계정 소유의 fd 실행파일을 fd계정이 실행할 수 있다. 따라서 fd 실행파일을 이용해 쉘을 따면 fd_pwn권한의 쉘로 인해 flag실행이 가능해 진다.
이제 fd의 소스코드인 fd.c를 분석해 쉘을 획득하면 될 것이다.
소스코드 분석
fd@pwnable:~$ nl fd.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 char buf[32];
5 int main(int argc, char* argv[], char* envp[]){
6 if(argc<2){
7 printf("pass argv[1] a number\n");
8 return 0;
9 }
10 int fd = atoi( argv[1] ) - 0x1234;
11 int len = 0;
12 len = read(fd, buf, 32);
13 if(!strcmp("LETMEWIN\n", buf)){
14 printf("good job :)\n");
15 system("/bin/cat flag");
16 exit(0);
17 }
18 printf("learn about Linux file IO\n");
19 return 0;
20 }
9번째 줄까지의 내용으로 보아 fd파일은 실행할 때 1개의 인자를 받는다.
10번째 줄은 인자(argv[1])를 정수타입으로 바꾼 후 0x1234에서 뺀 값을 fd에 저장한다.
12번째 줄은 fd 파일에서 32bytes 만큼 읽어와 buf에 저장한다.
이후 buf와 LETMEWIN가 같으면 flag를 출력해준다.
따라서 인자값을 통해 fd를 어떻게 설정해서 어떻게 buf에 LETMEWIN을 넣을지 고민해야된다.
fd는 read()에서 파일 디스크립터로 이용되고 있으므로 파일 디스크립터와 read 함수에 대해 봐보자.
파일 디스크립터(File Descriptor)
파일 디스크립터란 파일이나 입출력 리소스에 액세스할 때 사용되는 추상표현으로, 시스템으로부터 할당받은 파일이나 소켓을 대표하는 정수이다. 쉽게 말해 파일에 번호를 붙여 지칭하는 것이라고 보면 된다.
fd의 0,1,2번은 고정된 역할을 한다. 이는 프로세스가 메모리에서 실행을 시작할 때 기본적으로 할당된다.
File Descriptor | file stream | 이름 |
0 | stdin | 표준 입력 |
1 | stdout | 표준 출력 |
2 | stderr | 표준 에러 |
read(int fd, void *buf, size_t bytes)
int fd - 읽을 파일의 파일 디스크립터
void *buf - 읽은 데이터를 저장할 버퍼(배열)
size_t bytes - 읽을 데이터의 최대 길이
10번째 줄의 read(fd, buf, 32)의 경우 fd가 가리키는 파일에서 32만큼 읽어 buf에 저장한다.
이 때,fd가 0이면 표준입력에서 읽어오므로 fd를 0으로 설정하면 직접 입력한 값을 저장할 수 있다!
Flag
fd를 0으로 맞춰주어 LETMEWIN을 입력하면 flag를 출력할 수 있다.
fd값은 9번째 줄을 통해 인자로 받은 값에서 0x1234(4660)을 빼준다는 것을 알고 있다. 따라서 fd를 0으로 해주려면 4660을 인자로 전달하면 된다.
fd를 0으로 주면 read함수에서 표준입력에서 값을 읽어오므로 fd에 4660을 주고 LETMEWIN\n을 입력하면 된다.