pwnable.kr one
OK, 题目说让我们 ssh 远程服务器,那么咱们 ssh 连接一下。友情提示:现在的 pwn 一般都是基于 Linux 的哦。所以大家在开始玩这么一个游戏的时候建议先安装一个基于 Linux 内核的虚拟机,如 WSL。
```shell
$ ssh fd@pwnable.kr -p2222
fd@pwnable.kr's password: guest # 这个密码是不可见的
```
下面我使用的是 WSL for kali 远程登录的结果:
好了,相信大家都会基本的 Linux 命令。我们查看一下目录文件:
```shell
$ ls
fd fd.c flag
```
发现目录下有三个文件。然后三个文件的类型可以使用‘file filename’查看。
```$ file fd fd.c flag
fd: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=c5ecc1690866b3bb085d59e87aad26a1e386aaeb, not strippedfd.c: C source, ASCII text
flag: regular file, no read permission
我们发现 flag 是没有读权限的,显然咱们不能直接使用 cat 命令查看。所以咱们还是老老实实的做题吧。
既然题目提供了源代码,我们当然是先分析源代码:
$ cat fd.c
fd.c 文件内容如下:
#include
#include
#include
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;
}
分析一下,程序大概的意思就是让我们提供一个参数(从 atoi 还可以看出,参数还必须是一个整数)给程序,然后程序将 buf 里面的内容与字符串“LETMEWIN\n”开始比较,if 语句里面紧接着就跟了咱们想要的 flag 。于是咱们开始分析一下判断条件,发现 buf 只在
len = read(fd, buf, 32);
中出现。于是乎我们开始查询“Linux中read函数的用法”……查到一个 Linux 常用函数手册,但是他个鸡儿的乱码了(查看源码发现网页采用的是big5编码),于是乎在chrome中下载了一个转码插件随后顺利解决了乱码问题。
read(由已打開的文件讀取數據)
相關函數readdir,write,fcntl,close,lseek,readlink,fread
表頭文件#include
定義函數ssize_t read(int fd,void * buf ,size_t count);
函數說明read()會把參數fd 所指的文件傳送count個字節到buf指針所指的內存中。若參數count為0,則read()不會有作用並返回0。返回值為實際讀取到的字節數,如果返回0,表示已到達文件尾或是無可讀取的數據,此外文件讀寫位置會隨讀取到的字節移動。
附加說明如果順利read()會返回實際讀到的字節數,最好能將返回值與參數count 作比較,若返回的字節數比要求讀取的字節數少,則有可能讀到了文件尾、從管道(pipe)或終端機讀取,或者是read()被信號中斷了讀取動作。當有錯誤發生時則返回-1,錯誤代碼存入errno中,而文件讀寫位置則無法預期。
錯誤代碼EINTR 此調用被信號所中斷。
EAGAIN 當使用不可阻斷I/O 時(O_NONBLOCK),若無數據可讀取則返回此值。
EBADF 參數fd 非有效的文件描述詞,或該文件已關閉。
範例參考open()。
当然,如果你完全信任自己的英语水平的话可以在 Linux 中使用下面的命令查看 read 函数的帮助。
$ man read
READ(2) Linux Programmer's Manual READ(2)
NAME
read - read from a file descriptor
SYNOPSIS
#include
ssize_t read(int fd, void *buf, size_t count);
DESCRIPTION
read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.
On files that support seeking, the read operation commences at the current file offset, and the file offset is incremented by the number of bytes read. If
the current file offset is at or past the end of file, no bytes are read, and read() returns zero.
If count is zero, read() may detect the errors described below. In the absence of any errors, or if read() does not check for errors, a read() with a count
of 0 returns zero and has no other effects.
If count is greater than SSIZE_MAX, the result is unspecified.
RETURN VALUE
On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number. It is not an error if this
number is smaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we
were close to end-of-file, or because we are reading from a pipe, or from a terminal), or because read() was interrupted by a signal. See also NOTES.
On error, -1 is returned, and errno is set appropriately. In this case, it is left unspecified whether the file position (if any) changes.
……(此处省略一万个单词)
因为这里 fd 参数才是我们可以控制的,所以我们要知道的是 read 函数中 fd 参数代表的是什么。由 reference 我们知道,int fd 实际上是一个文件描述符。继续搜索“read 函数中的 fd”在百度贴吧中得到了完美的解释。
open打开一个文件,并返回一个整数值,这个整数叫做文件描述符,默认的标准输入是 0, 标准输出是1,标准出错是2(这些可以在unistd.h中找到),所以你再open一个文件的时候返回的是3,如果在你关闭这个文件之前,再open一个文件,那返回的就是4,以前类推。
至于读函数出错,你可以包含errno.h头文件,并打印错误信息,printf( "%d,%s", errno,(char*)strerror(errno) );来显示错误信息
--https://zhidao.baidu.com/question/1430510337268766779.html(来来,给这个完美的解释一个赞!)
Perfect~,我们已经知道要干嘛了,就是使 fd 的值为 0(其实我测试了一下,0,1,2 都是可以的),然后使 read 接收我们的输入。
0x1234 = 4660
最终解题:
fd@ubuntu:~$ ./fd 4660
LETMEWIN
good job :)
mommy! I think I know what a file descriptor is!!
fd@ubuntu:~$
Flag: mommy! I think I know what a file descriptor is!!