
#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");
setregid(getegid(), getegid());
system("/bin/cat flag");
exit(0);
}
printf("learn about Linux file IO\n");
return 0;
}
source code는 다음과 같다.
프로그램을 실행하면서 입력할 수 있는 2번째 인자에 0x1234를 빼서 해당 파일 디스크립터를 바탕으read 함수를 호출한다.

flag 파일은 read 권한이 없어 읽지 못하는 것 같으므로 코드 중 setregid로 현재 프로세스의 권한을 상승시켜주는 부분이 존재한다.
#define STDIN_FILENO 0 // 표준 입력 (keyboard 등)
즉 적절한 decimal 숫자를 입력하여 read 함수의 인자로 적절한 인자를 전달해야 한다. 키보드 입력을 읽게 하기 위해 0을 넣어야 하니
0x1234 = 4660 을 ./fd의 인자로 넣어주면,
read 함수에 fd=0 이 호출되어 키보드 입력 값을 넣어줄 수 있게 된다.
이후 LEMTWIN\n 과 문자열을 비교하는데, 이를 입력 값으로 넣어주면 flag를 읽어낼 수 있다.
flag :
Mama! Now_I_understand_what_file_descriptors_are!
terminal도 하나의 device이다. terminal에서 보통 읽기, 쓰기 모드로 파일을 열 때, 그 열린 디바이스 파일 기술자를 dup()해서 0, 1, 2로 전부 열기 때문에 실제로 같은 터미널 디바이스를 가리키게 된다. 이는 이미 열려 있는 파일(또는 장치)에 대한 식별자(fd)를 복사해서, 똑같이 그 파일을 가리키는 또 다른 숫자(fd)를 하나 더 만드는 것이다.
터미널(예: /dev/pts/N)은 보통 읽기 · 쓰기 모드(O_RDWR)로 열리고, 그 한 개의 열린 디바이스 파일 기술자를 그대로 0, 1, 2로 dup()하여 모두 O_RDWR로 열어두는 것이 일반적이다. 그래서 0이 아닌 1, 2로 fd를 넘겨주어 read해도 터미널 입력을 받는 것이 일반적이다.