pwnable.kr-input

难点感觉是linux下的父进程和子进程间的通道搭建,和搭建socket客户端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>

int main(int argc, char* argv[], char* envp[]){
	printf("Welcome to pwnable.kr\n");
	printf("Let's see if you know how to give input to program\n");
	printf("Just give me correct inputs then you will get the flag :)\n");

	// argv
	if(argc != 100) return 0;
	if(strcmp(argv['A'],"\x00")) return 0;
	if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0;
	printf("Stage 1 clear!\n");	

	// stdio
	char buf[4];
	read(0, buf, 4);
	if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0;
	read(2, buf, 4);
        if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0;
	printf("Stage 2 clear!\n");
	
	// env
	if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
	printf("Stage 3 clear!\n");

	// file
	FILE* fp = fopen("\x0a", "r");
	if(!fp) return 0;
	if( fread(buf, 4, 1, fp)!=1 ) return 0;
	if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0;
	fclose(fp);
	printf("Stage 4 clear!\n");	

	// network
	int sd, cd;
	struct sockaddr_in saddr, caddr;
	sd = socket(AF_INET, SOCK_STREAM, 0);
	if(sd == -1){
		printf("socket error, tell admin\n");
		return 0;
	}
	saddr.sin_family = AF_INET;
	saddr.sin_addr.s_addr = INADDR_ANY;
	saddr.sin_port = htons( atoi(argv['C']) );
	if(bind(sd, (struct sockaddr*)&saddr, sizeof(saddr)) < 0){
		printf("bind error, use another port\n");
    		return 1;
	}
	listen(sd, 1);
	int c = sizeof(struct sockaddr_in);
	cd = accept(sd, (struct sockaddr *)&caddr, (socklen_t*)&c);
	if(cd < 0){
		printf("accept error, tell admin\n");
		return 0;
	}
	if( recv(cd, buf, 4, 0) != 4 ) return 0;
	if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0;
	printf("Stage 5 clear!\n");

	// here's your flag
	system("/bin/cat flag");	
	return 0;
}

argv:

strcmp比较两个字符串,直接构造函数的时候让argv['A']='\x00'就行,argv['B']同理

 

stdio:

read中的0、2分别是标准输入(也就是我们的键盘输入)和标准错误(命令错误信息的输出)

2是我们没办法输入的,这时候就用到dup2()了,具体看https://blog.csdn.net/u012058778/article/details/78705536

然而我们的输入还是只有一个(键盘),看到别人写的用linux的pipe()可以在父进程和子进程间创建两个通道,从子程序读取

(因为子进程结尾要执行execve()从而转而执行input程序)并把两个读取的端口的文件描述符用dup2改成0和2,然后从父程序写入(关于execve可以试着照着https://blog.csdn.net/sl1248/article/details/50916400 写一写就能大致了解其作用了,我觉得很多人说要在子程序执行execve是因为这个函数会终止子程序去执行别的程序,但看各种教程似乎父进程和子进程谁执行execve都没关系。。。)

 

env:

getenv()获得对应环境变量的值,所以在环境变量envp中添加"\xde\xad\xbe\xef=\xca\xfe\xba\xbe"

 

file:

FILE* fp = fopen("\x0a", "r");的意思就是让fp成为指向文件名为 \x0a 的指针,所以为了让fopen不返回null,就需要创建名为 \x0a的文件,然后给文件写入\x00\x00\x00\x00

 

network:

各种函数的意义在https://blog.csdn.net/qq_20307987/article/details/51337179 有总结

端口设置不能超过65535

https://blog.csdn.net/u012763794/article/details/51992512对整个函数的注解非常详细了

最后精简的程序如下

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>

int main(){
	char *argv[101]={"/home/input2/input",[1 ... 99]="a",NULL};
    //1 ... 99代表1-99,别忘记空格
	argv['A']="\x00";
	argv['B']="\x20\x0a\x0d";
	argv['C']="123456";
	char* env[2]={"\xde\xad\xbe\xef=\xca\xfe\xba\xbe",NULL};
	
	FILE* fp=fopen("\x0a","w");
	fwrite("\x00\x00\x00\x00",4,1,fp);
	fclose(fp);
	
	int pipestdin[2]={-1,-1};
	int pipestderr[2]={-1,-1};
	pid_t pid;
	pipe(pipestdin);
	pipe(pipestderr);
	pid=fork();
	if(pid==0){
		//子进程
		close(pipestdin[1]);close(pipestderr[1]);//关闭写入端
		dup2(pipestdin[0],0);dup2(pipestderr[0],2);//把写入端的文字描述符换成0和2
		execve("/home/input2/input",argv,env);
	}
	else{
		//父进程
		close(pipestdin[0]);close(pipestderr[0]);//关闭读入端
		write(pipestdin[1],"\x00\x0a\x00\xff",4);//写入数据
		write(pipestderr[1],"\x00\x0a\x02\xff",4);
		
	}
	//感觉子进程和父进程除了返回的pid不同之外并没有差别,父子进程的内容可以对调
	sleep(1);//等待前面程序执行完成
	int sockfd;
	struct sockaddr_in server;
	sockfd = 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(12345);
	connect(sockfd,(struct sockaddr*)&server,sizeof(server));
	char buf[4]="\xde\xad\xbe\xef";
	write(sockfd,buf,4);
	close(sockfd);
 	
}

最后想要执行程序,就需要把文件上传到pwnable.kr的服务器,看别人都存在tmp下我也存在tmp下了,另外程序最后执行的是

system("/bin/cat flag");所以需要在当前目录创建一个硬链接到/home/input2下的flag文件,但是现在已经不允许创建硬链接了,

但是在/tmp/input2有flag文件,估计是前人留下的,所以把程序放到/tmp/input2然后编译运行就好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值