- 打开题目审题
- 找到突破口
- 相关c语言知识
- 源代码分析
- 找到FLAG
打开题目审题
Mommy! what is a file descriptor in Linux? * try to play the wargame your self but if you are ABSOLUTE beginner, follow this tutorial link: https://youtu.be/971eZhMHQQw ssh fd@pwnable.kr -p2222 (pw:guest)
Linux中的文件描述符是什么。如果你是完全的初学者,可以去youtube链接看视频。
解释下 SSH:
ssh fd@pwnable.kr -p2222 (pw:guest)
SSH 是Linux系统的登录工具,现广泛用于服务器登录和各种加密通信。其实在这里就是一个终端连接工具。
所以,打开CMD
输入命令回车,输入密码:guest 。即可登录服务器。
登录上就可以开始找flag了。
找到突破口
照例先 ls -la
fd@pwnable:~$ ls -la total 40 drwxr-x--- 5 root fd 4096 Oct 26 2016 . drwxr-xr-x 115 root root 4096 Dec 22 2020 .. d--------- 2 root root 4096 Jun 12 2014 .bash_history -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 -rw------- 1 root root 128 Oct 26 2016 .gdb_history dr-xr-xr-x 2 root root 4096 Dec 19 2016 .irssi drwxr-xr-x 2 root root 4096 Oct 23 2016 .pwntools-cache
发现flag文件,尝试查看(肯定看不了
fd@pwnable:~$ cat flag cat: flag: Permission denied fd@pwnable:~$
果然不能看。
看下别的,文件flag肯定是最后的答案,所以需要看看其他文件找寻突破口。
还有一个c语言源文件fd.c和可执行文件fd。
那没什么说的,看看fd.c。
fd@pwnable:~$ cat fd.c #include#include #include char buf[32]; int main(int argc, char* argv[], char* envp[]){ if(argc<2){ printf("pass argv[1] a numbern"); return 0; } int fd = atoi( argv[1] ) - 0x1234; int len = 0; len = read(fd, buf, 32); if(!strcmp("LETMEWINn", buf)){ printf("good job :)n"); system("/bin/cat flag"); exit(0); } printf("learn about Linux file IOn"); return 0; }
找到重点 system("/bin/cat flag");
看到源码大概已经明白了,我们没有查看文件的权限,但是fd有,所以只要分析如何让代码执行system("/bin/cat flag");即可。
相关c语言知识
int main( int argc , char * argv[ ] ,char * envp[ ])
main函数的参数列表保存了输入参数的信息:
第一个参数argc记录了输入参数的个数。且argc是包括程序本身在内的参数个数。如本题目,直接./fd运行,该参数实际上是1,而不是0。
第二个参数是字符串数组的,字符串数组的每个单元是char*类型的,指向一个c风格字符串,arg[ ]指向的数组中至少有一个字符指针,即arg[0].他通常指向程序中的可执行文件的文件名。
第三个参数是用来取得系统的环境变量,如:在DOS下,有一个PATH变量。当你在DOS提示符下输入一个命令的时候,DOS会首先在当前目录下找这个命令的执行文件。如果找不到,则到PATH定义的路径下去找,找到则执行,找不到返回Bad command or file name 。在DOS命令提示符下键入set可查看系统的环境变量。
int atoi(const char * str)
**函数说明:**C 库函数 int atoi(const char *str) 把参数 str 所指向的字符串转换为一个整数(类型为 int 型)。
ssize_t read(int fd, void * buf, size_t count);
函数说明:read()会把参数fd 所指的文件传送count 个字节到buf 指针所指的内存中. 若参数count 为0, 则read()不会有作用并返回0. 返回值为实际读取到的字节数, 如果返回0, 表示已到达文件尾或是无可读取的数据,此外文件读写位置会随读取到的字节移动。
fd所指文件指的是什么
每一个进程在进程控制块中都保存着一分文件描述符表,文件描述符就是这个表的索引,文件描述符表中每个表项都有一个指向已打开文件的指针。
fd: 为打开文件的文件描述符,而每个进程都有一张文件描述符表,fd文件描述符就是这张表的索引。
Linux下 文件描述符(fd)与 文件指针(FILE*) 想了解更多可以看该链接。
简言之,在linux中,值为0、1、2的fd,分别代表标准输入、标准输出、标准错误输出。
源代码分析
char buf[32];
int main(int argc, char* argv[], char* envp[]){
// 输入参数不能少于2 接一个参数就可以
if(argc<2){
printf("pass argv[1] a numbern");
return 0;
}
// 将 argv[1] - 0x1234 赋值给fd
int fd = atoi( argv[1] ) - 0x1234;
int len = 0;
// 读取32字节到buf中,那么fd 必须为标准输入 即为0
len = read(fd, buf, 32);
// buf 中32字节一定是 LETMEWIN 这样就能得到flag
if(!strcmp("LETMEWINn", buf)){
printf("good job :)n");
system("/bin/cat flag");
exit(0);
}
printf("learn about Linux file IOn");
return 0;
}
代码看完了,想必如何做出来也都心里有数了。
参数一定要满足 fd=0 那么 参数 argv[1] = 0x1234 = 4660。
最后在输入“LETMEWIN” 即可。
找到FLAG
执行可执行文件fd。./fd 4660 回车。
fd@pwnable:~$ ./fd 4660
此时开始执行len = read(fd, buf, 32); 输入“LETMEWIN” 回车即可得到flag。
fd@pwnable:~$ ./fd 4660 LETMEWIN good job :) mommy! I think I know what a file descriptor is!!
flag:mommy! I think I know what a file descriptor is!!
输入验证即可。
参考:
https://www.cnblogs.com/ftl1012/p/ssh.html
https://blog.csdn.net/qq_40657299/article/details/78332875
http://c.biancheng.net/cpp/html/239.html
https://blog.csdn.net/qq_31967569/article/details/81145309


![【pwnable.kr】Toddler‘s Bottle-[fd] 【pwnable.kr】Toddler‘s Bottle-[fd]](http://www.mshxw.com/aiimages/31/456951.png)
