pwnable
input2
本题要求对目标文件进行正确的输入。查看源代码,该程序需要5次不同的输入,只有全部输入正确才能得到flag。
第一次是命令行参数,源代码如下:
if(argc != 100) return 0;
if(strcmp(argv['A'],"x00")) return 0;
if(strcmp(argv['B'],"x20x0ax0d")) return 0;
printf("Stage 1 clear!
");
可以看到,命令行参数个数必须为100,'A'的ascii码为67,'B'为68,这两个位置的命令行参数必须为"x00"和"x20x0ax0d",这里我先试着用python直接打印参数的方式输入,结果由于x00的存在执行不成功。还是得写一个程序来运行,且后面其余输入也一并写进去。我们可以用execv()函数来执行input,并以命令行参数作为其参数
char *argv[101] = {"~/input", [1 ... 99] = "a", NULL};
argv['A'] = "x00";
argv['B'] = "x20x0ax0d";
然后使用execve("/home/input2/input", argv, NULL);
执行这个程序即可完成第一阶段的输入
第二阶段要求从标准输入流和标准错误流读取数据
char buf[4];
read(0, buf, 4);
if(memcmp(buf, "x00x0ax00xff", 4)) return 0;
read(2, buf, 4);
if(memcmp(buf, "x00x0ax02xff", 4)) return 0;
printf("Stage 2 clear!
");
正常情况下我们是不能直接向标准错误流输入数据的,可以通过管道来实现对标准输入流与标准错误流的输入。
使用pipe可以将一个二元整形数组通过系统调用变为输入/输出文件标识符,[0]为输入,[1]为输出。所谓管道是用来给父进程与子进程通信的,若子进程要向父进程传输数据时,关闭[0]并将数据写入[1]中,然后父进程关闭[1]即可从[0]读取数据。我们要做的就是创建一个子进程,将需要的数据传入子进程的[1]中,然后父进程关闭[1],将[0]转换位标准输入流或标准错误流,这样标准输入流与标准错误流中就有我们需要的数据了,代码如下:
int stdout_pipe[2] = {-1, -1}; int stderr_pipe[2] = {-1, -1}; pid_t child_pid; pipe(stdout_pipe); pipe(stderr_pipe); child_pid = fork(); if (child_pid == 0) { close(stdout_pipe[0]); close(stderr_pipe[0]); write(stdout_pipe[1], "x00x0ax00xff", 4); write(stderr_pipe[1], "x00x0ax02xff", 4); } else { close(stdout_pipe[1]); close(stderr_pipe[1]); dup2(stdout_pipe[0], 0); dup2(stderr_pipe[0], 2); close(stdout_pipe[0]); close(stderr_pipe[0]); char* env[2] = {"xdexadxbexef=xcaxfexbaxbe", NULL}; execve("/home/input2/input", argv, NULL); }
第三阶段和第四阶段分别是环境变量读取和文件读取,环境变量可直接作为execv函数的参数直接传入,而文件直接创建并写入数据即可
FILE* fp = fopen("x0a", "w");
fwrite("x00x00x00x00", 4, 1, fp);
fclose(fp);
char* env[2] = {"xdexadxbexef=xcaxfexbaxbe", NULL};
execve("/home/input2/input", argv, env);
第五阶段是网络的通信,input程序会将命令行参数的第67个作为侦听端口,从中接收要求的数据,所以第五阶段的本质就是网络编程,先设置好端口,再向该端口传输要求的数据即可,代码如下
argv['C'] = "666666";
int csock;
struct sockaddr_in server;
csock = socket(AF_INET, SOCK_STREAM, 0);
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_port = htons(666666);
connect(csock, (struct sockaddr*)&server, sizeof(server));
char buf[4] = "xdexadxbexef";
write(csock, buf, 4);
close(csock);
输入全部正确后flag就被打印出来了。