【pwnable.kr】input

题目描述

Mom? how can I pass my input to a computer program?
源代码如下:

#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;
}


思路

这道题就是五个输入的方式,一个一个来就可以了,由于无法通过命令行用python传递不可见字符,所以只能用c或python的对应的执行程序的函数通过参数数组传递进去。这里我采用的是c语言。

第一个部分直接采用execve函数即可,第二个部分稍稍复杂,需要开一个子进程,通过pipe向子进程的stdin和stderr传递相应的字符而不是直接在子进程里输出。第三个部分也直接通过设置execve的参数即可。第四部分向文件写入空字符,注意一些输出/赋值的函数会遇到空字节截断。第五部分使用socket发送,在发送之前需要稍等一会儿,等服务器端开启之后再连接,不然服务器接收不到。
最后,程序编译好之后scp到服务器的/tmp/子文件夹/中运行,拿到flag的时候由于flag不在当前文件夹,所以需要创建一个文件链接。文件链接有两种方式,通过命令行ln和直接写link函数,后者不能创建软链接,而在本题的服务器下只有添加软连接的权限。tmp文件夹下存在flag文件无法再链接,所以创建子文件夹。

exp如下:

#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 
#include <stdlib.h>
#include <errno.h>

static void
parent(int fd1[2],int fd2[2],char* argv[]){
//----stage 2----
	close(fd1[0]);
	close(fd2[0]);
	ssize_t len;
	char buf[64];
	len = strlen(buf);
	sprintf(buf,"lllll\x00jkkjk");
	//printf(buf);
	len = strlen(buf);
	write(fd1[1],"\x00\x0a\x00\xff",4);
	//write(fd1[1],buf,4);
	sprintf(buf,"\x00\x0a\x02\xff");
	//write(fd2[1],buf,4);
	write(fd2[1],"\x00\x0a\x02\xff",4);
	
//----stage 5----
	sleep(1);	
	int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;

    char buffer[256];
    portno = atoi(argv[1]);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) 
        printf("ERROR opening socket");
    server = gethostbyname("localhost");
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host\n");
        exit(0);
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, 
         (char *)&serv_addr.sin_addr.s_addr,
         server->h_length);
    serv_addr.sin_port = htons(portno);
    if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) 
        printf("ERROR connecting");
    bzero(buffer,256);
    strcpy(buffer,"\xde\xad\xbe\xef");
    
    n = write(sockfd,buffer,strlen(buffer));
    if (n < 0) 
         printf("ERROR writing to socket");

    close(sockfd);
    printf("message sent!\n");
}

static void
child(int fd1[2],int fd2[2],char* argv[]){
//----stage 2----
	close(fd1[1]);
	close(fd2[1]);
	dup2(fd1[0],STDIN_FILENO);
	dup2(fd2[0],STDERR_FILENO);	
//----stage 1----
	char* p[101]={};
    for(int i=0;i<100;i++){
        p[i]="a";
    }
    p['A']="\x00";
    p['B']="\x20\x0a\x0d";
    p['C']=argv[1];
    p[100]=NULL;
//----stage 3----
    char * const envp[] = {"\xde\xad\xbe\xef=\xca\xfe\xba\xbe",NULL};
//----stage 4----
    char buff[4];
    FILE * fd = fopen("\x0a","w");
    memset(buff,0x00,4);
    fwrite(buff,4,1,fd);
    fclose(fd);
    printf("staring input\n");
    execve("/home/input2/input",p,envp);
    //execlp("wc", "wc", "-l", NULL);
}

int main(int argc,char *argv[]){
	pid_t pid;
	int fd1[2];
	int fd2[2];
	
	pipe(fd1);
	pipe(fd2);
	
	//----link flag file----
	//char *path1 = "/home/input2/flag";
	//char *path2 = "/tmp/veronica/flag";
	//int   status;

	pid = fork();
	if (pid > 0) {
                parent(fd1, fd2,argv);
        } else {
                child(fd1, fd2,argv);
        }
 
        return 0;
}



小tip

  • scp一定要记得写源和目标
  • 如果程序没有输入可能会运行过快导致还没有attach上的时候就终止了
  • C语言中要注意双引号和单引号有区别
  • sprintf遇到\x00直接截断了所以导致出问题
  • 如果想向字符数组写入\x00的内容可以用memset函数
  • write函数和fwrite函数遇到空字符不会截断
  • 对于参数是字符数组地址的函数,可以直接传进去一个字符串,不一定要传变量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值