Fork me on GitHub

fd_WriteUp(pwnable.kr_fd)Linux文件描述符

本题目来自:http://pwnable.kr/play.php

PKn8Q1.png

首先根据题目提示使用

1
ssh fd@pwnable.kr -p2222

来连接到目标服务器,密码为guest

PKn1zR.png

ls一下发现当前目录下有三个文件,一看到flag字样的文件名就下意识的cat flag但是发现没有读取权限,而除了flag文件外还有一个源码和一个可执行文件,显然作者的意图是让玩家分析源代码再通过可执行文件读取flag里面的内容。

PKnGsx.png

fd.c内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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;
}

现在我们来分析一下代码

首先main函数使用了带参数的形式:int main(int argc, char* argv[], char* envp[])

argc: 参数的个数,不给main()函数传递参数时默认值为1,即至少有一个参数为该可执行文件的文件名(含目录)。

argv: 为指针数组,分别指向各个字符串参数的首地址,其中argv[0]存储的是可执行文件的文件名的首地址 。

envp:存放系统的环境变量 。

假设有一个文件名为1.sh的文件中主函数声明为int main(int argc , char* argv[] )的形式,如果调用时使用root@kali-linux:~# ./1.sh hahaha xixixi hehehe的形式,则此时argc的值为4,argv[0]为“1.sh” ,argv[1]为“hahaha”,argv[2]为“xixixi”,argv[3]为“hehehe” 。

那么这个代码块:

1
2
3
4
if(argc<2){
printf("pass argv[1] a number\n");
return 0;
}

表示的意思是运行fd的时候需要加参数。

1
2
3
4
5
6
7
8
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);
}

以上代码将argv[1]即第一个参数做了转整型的处理,使用的是atoi()函数:

int atoi(const char *str) 把参数 str 所指向的字符串转换为一个整数(类型为 int 型)

而后面又使用read(fd, buf, 32)把fd(文件描述)所指的文件传送32个字节到buf所指的内存中,read()函数返回值为实际读取到的字节数, 如果返回0, 表示已到达文件尾或是无可读取的数据。若参数count 为0, 则read()不会有作用并返回0。

关于文件描述符:

文件描述符的值 意义
0 标准输入
1 标准输出
2 错误

strcmp("LETMEWIN\n", buf)将用户输入的字符串与“LETMEWIN”进行比较,如果一致则输出flag的内容,因此我们需要找到输入的点,因此本题的关键点在于通过控制argv[1] (第一个参数)来修改文件的描述符为0从而修改文件状态为标准输入,再输入“LETMEWIN”来得到flag,此时我们需要考虑如何时文件描述符为0,int fd = atoi( argv[1] ) - 0x1234文件描述符在程序中的变量名为fd(file descriptor),因此我们需要将atoi( argv[1] ) - 0x1234的值设为0,0x1234为16进制数,如果我们在参数输入时输入0x1234,atoi()函数会将0x1234中的x转为整型数字,因此数值会发生变化,此时应该使用进制转换将0x1234转换为十进制的4660再输入“LETMEWIN”即可获得flag。

1
2
3
4
fd@ubuntu:~$ ./fd 4660
LETMEWIN
good job :)
mommy! I think I know what a file descriptor is!!
您的支持是我最大的动力🍉