[pwnable.kr] fd
pwnable.kr을 정리 하는 이유
대학원에 입학하기 전 게으름을 방지하고자 기초 지식을 다시 잡기 위해 진행하려고 합니다.
하루의 한문제를 클리어 하는 것을 목표로 가지고 있으며, 당일 풀지 못한 문제는 시간이 걸려도 정답을 절대 보지 않고 클리어 하는 것을 목표로 두고 있습니다.
문제 설명
Mommy! what is a file descriptor in Linux?
엄마! Linux에서 file descriptor가 뭔가요?
아직 소스코드를 확인해 보지 않았으나, 질문에서 Linux File descriptor에 대한 의문을 가지고 있기 때문에, Linux File descriptor와 관련된 문제인 것을 유추할 수 있습니다.
주어진 접속 정보(ssh fd@pwnable.kr -p2222 (pw:guest))를 가지고 접속 시 아래와 같은 파일 목록을 확인할 수 있습니다.
1
2
3
4
5
fd@pwnable:~$ ls -l
total 16
-r-sr-x--- 1 fd_pwn fd 7322 Jun 11 2014 fd
-rw-r--r-- 1 root root 418 Jun 11 2014 fd.c
-r--r----- 1 fd_pwn root 50 Jun 11 2014 flag
힌트로 주어진 fd.c 파일을 확인해 보고 적절한 페이로드를 삽입해 문제 해결을 시도하게 됩니다.
문제 풀이
코드 분석
fd.c를 열어보면 다음과 같은 소스 코드를 확인할 수 있습니다.
중요한 코드들을 해석해 보면서 문제 풀이에 접근해 보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
if(argc<2){
printf("pass argv[1] a number\\n");
return 0;
}
int fd = atoi( argv[1] ) - 0x1234;
int len = 0;
len = read(fd, buf, 32);
if(!strcmp("LETMEWIN\\n", buf)){
printf("good job :)\\n");
system("/bin/cat flag");
exit(0);
}
printf("learn about Linux file IO\\n");
return 0;
}
4 행 : 32byte 크기의 char형 배열을 선언합니다.
10 행 : main()함수에 argument로 전달받은 argc[1]을 atoi() 함수의 argument로 전달한 뒤, 0x1234를 빼주는 작업을 합니다.
12 행 : read()함수에 10 행의 결과 값인 fd를 첫 번째 인자로 주어 함수를 실행하는 것을 확인할 수 있습니다.
- 인자는 총 3개(fd, buf, 32)가 들어가는 것을 확인할 수 있습니다.
14 행 : if(!strcmp("LETMEWIN\\n", buf))의 코드를 확인했을 때, buf에 들어가야하는 값이 LETMEWIN\\n가 되어야 하는 것을 알 수 있습니다.
- buf는 4행에서 선언한 것을 확인하였고 어떤 값으로도 초기화가 되어 있지 않습니다.
해당 코드를 분석 했을 때, buf에
LETMEWIN\\n"값을 넣어야 하는 것을 알 수 있습니다.
read()
read()에는 코드 4행에서 선언한 buf변수가 들어가는 것을 확인할 수 있고, 함수가 어떻게 작동되는지 확인하여 14행의 if문을 어떻게 통과할지 생각해 보아야 합니다.
- 첫 번째 인자 : fd, (File Descriptor,
int fd)- 파일 디스크립터(File Descriptor)는 Linux or Unix쪽의 시스템에서 Process -> File을 핸들링 시 사용하는 개념으로, Process -> File에 접근시 사용하는 추상적인이다. 즉, 특정한 파일에 접근하기 위한 추상적인 키이다.
다음은 파일 디스크립터에 사용될 수 있는 장표입니다.
| 파일 디스크립터 | 목적 | POSIX 이름 | stdio 스트림 |
|---|---|---|---|
| 0 | 표준입력 | STDIN_FILENO | stdin |
| 1 | 표준출력 | STDOUT_FILENO | stdout |
| 2 | 표준에러 | STDERR_FILENO | stderr |
- 두 번째 인자 : buf, (
void *buf)- 변수 buf가 들어오는 곳으로 파일을 읽을 버퍼 공간이 오게 됩니다.
- 세 번째 인자 : 32, (
size_t nbytes)- 버퍼의 크기를 알려주는 인자가 오게 됩니다.
- 반환 값 :
ssize_t- 정상 적으로 실행되었다면 읽어들인 바이트 수를 반환합니다.
결론
분석을 토대로 다음과 같이 확인할 수 있습니다.
- read() 함수에 대해 알았을 때, fd를 0으로 만들어 줘야 원하는 값을 입력 가능하다.
- fd 의 값은
인자 값 - 0x1234으로 만들어 진다. 0x1234는 10 진수로 4660을 초기 인자로 넘겨 준다.- fd의 값을 0으로 만들어 입력을 받을 수 있는 형태가 되면
LETMEWIN와 엔터를 입력한다.
결과
위에서 구한 결론을 순서대로 입력해주면 플래그를 획득할 수 있습니다.
1
2
3
4
fd@pwnable:~$ ./fd 4660
LETMEWIN
good job :)
mommy! I think I know what a file descriptor is!!
