美文网首页
pwnable.kr input

pwnable.kr input

作者: chenmuxin | 来源:发表于2018-12-23 14:42 被阅读0次

    思路

    • 首先ssh连接后查看c代码是什么,发现这是一个考验linux基础输入知识的题,意味着没接触过的人要大量恶补linux的知识(说的就是自己)
    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;
    }
    
    • 首先由代码可见,我们一共需要过五关,每一次都要成功,最后才能cat flag

    首先介绍一个函数int execve(const char *filename, char *const argv[],char *const envp[])作用是启动新的进程,而进程的文件有filename指定,传入的参数为argv,并且有环境变量envp,这个函数将在下面中用到,同时需要注意argv和envp都需要以NULL结尾

    第一关

        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"); 
    
    • 首先我们的argc要等于100,既要有100个参数,要在第“A”(即65)的位置为\x00,在第“B”(66)的位置是“\x20\x0a\x0d”,那么就简单的写入即可
    #include<stdio.h>
    int main()
    {
        /*1*/
        char *argv[101]={0};
        for(int i=1;i<100;i++)
            argv[i]="a";
        argv[0]="/home/input2/input";
        argv['A']="\x00";
        argv['B']="\x20\x0a\x0d";
        
    }
    

    第二关

    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");
    
    • 这一次由之前做过的fd题可以知道read()的第一个参数表示文件描述符,0代表标准输入,2代表标准错误输出,输入容易控制,但要如何使得错误输出也被控制呢?就需要学会pipe管道和I/O重定向

    pipe是为了在两个进程之间通信设置的,单方向的通信,一方面读,一方面写。以下是pipe的定义http://man7.org/linux/man-pages/man2/pipe.2.html

    pipe通道是为两个进程通信服务的,但此时只有一个进程,因此我们要fork一个子进程,实现子进程和父进程的通信,以下是fork的原理
    https://blog.csdn.net/jason314/article/details/5640969

    I/O重定向指的是将已创建的文件描述符指向其他文件,以下是具体原理
    http://www.cnblogs.com/weidagang2046/p/io-redirection.html

    以下是一个很好的利用pipe通道实现I/O重定向的说明
    http://unixwiz.net/techtips/remap-pipe-fds.html

    • 因此我们创建两个pie通道,表示两个进程之间的通信,然后fork处子进程,在子进程中写入,在父进程处进行重定向,将从子进程读取的内容重定向到标准输入和标准错误输出
        /*2*/
        int pipe_stdin[2] = {-1, -1};
        int pipe_stderr[2] = {-1, -1};
        pid_t pid_child;
        if ( pipe(pipe_stdin) < 0 || pipe(pipe_stderr) < 0 )
        {
            perror("Cannot create the pipe.");
        }
    
        #define STDIN_READ   pipe_stdin[0]
        #define STDIN_WRITE  pipe_stdin[1]
        #define STDERR_READ  pipe_stderr[0]
        #define STDERR_WRITE pipe_stderr[1]
    
        if ( ( pid_child = fork() ) < 0 )   // do not forget the ()!
        {
            perror("Cannot create fork child.");
        }
        if(pid_child == 0)  //in child
        {
            close(STDIN_READ);
            close(STDERR_READ);//关闭输入
            write(STDIN_WRITE,"\x00\x0a\x00\xff",4);
            write(STDERR_WRITE,"\x00\x0a\x02\xff",4);
        }
        else    //in father
        {
            close(STDERR_WRITE);
            close(STDIN_WRITE);//关闭输出
            dup2(STDIN_READ,0);
            dup2(STDERR_READ,2);
        }
        printf("link\n");
       
    }
    

    第三关

     // env
        if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
        printf("Stage 3 clear!\n");
    
    • 首先是函数getenv,目得是使两个值相等

    char *getenv(const char *name)是查找程序环境列表中参数name的值

    • 而环境列表我们一般用不到,但是,作用是将一些源程序所在系统的位置等信息传入,那我们只需要传入环境变量时,将这一等式当作环境变量之一传入即可。
    char *envp[2] = {"\xde\xad\xbe\xef=\xca\xfe\xba\xbe", NULL};//第三关用到的环境变量
    execve("/home/input2/input", argv, envp);
    

    第四关

        // 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"); 
    
    • 简单的打开“\x0a”这个文件,写入信息"\x00\x00\x00\x00"即可
    /*4*/
        FILE *fp=fopen("\x0a","wb");
        if(!fp)
        {
            perror("Can not open file.");
    
        }
        printf("Open file success.\n");
        fwrite("\x00\x00\x00\x00",4,1,fp);
        fclose(fp);
    }
    

    第五关

     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;
    }
    
    • 由代码可知把这个input作为一个服务端,绑定的端口值是argv[‘C’]里面存的数值,当然代码用了一下atoi转化字符串为数字,比如存的是char 0,那么绑定的端口就是0号端口。然后验证的是传进来的某连接发送的内容是"\xde\xad\xbe\xef".

    • 所以我们自己写的时候,可以给input指定一个端口,然后我们的程序再连接这个端口,发送"\xde\xad\xbe\xef"就好了。

    argv['C'] = "9999"; 
    sleep(2); // wait the server start
        int sockfd;
        char buf[10] = {0}; // buf to be sent
        int len;            // len of avail buf
        struct sockaddr_in servaddr;
        servaddr.sin_family = AF_INET;  
        servaddr.sin_port = htons(9999);  // port in argv['C'] 
        servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //local
        if( (sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0 )  
        {  
            perror("socket error.");  
            exit(1);  
        }  
        if ( connect(sockfd, (struct sockaddr*) &servaddr, sizeof(servaddr)) < 0 )
        {
            perror("connect error.");
            exit(1);
            }
        printf("socket connect.\n");
        strcpy(buf, "\xde\xad\xbe\xef");
        len = strlen(buf);
        send(sockfd, buf, len, 0);
        close(sockfd);  
    
        return 0;
    

    总结

    • 做这道题时会感觉好多都不认识,但坚持下来就能学到很多的东西。

    tips:在这道题提交的时候需要将自己的文件用scp上传到/tmp或者在服务器的tmp文件夹中新建文件夹,用vim写,使用“gcc 文件 -o 输出文件名称”来编译,同时还需要软连接,“ln -s /home/input2/flag flag”

    #include<stdio.h>//fopen perror
    #include <stdlib.h>
    #include <unistd.h>//pipe execve
    #include <string.h>//strcmp  
    #include <sys/types.h>//bind
    #include <sys/socket.h>// linux socket
    #include <netinet/in.h>   
    #include <netdb.h>   
    #include <arpa/inet.h>  
    
    int main()
    {
        /*1*/
        char *argv[101]={0};
        for(int i=1;i<100;i++)
            argv[i]="a";
        argv[0]="/home/input2/input";
        argv['A']="\x00";
        argv['B']="\x20\x0a\x0d";
        argv['C'] = "9999"; 
        argv[100] = NULL;
    
    
        char *envp[2] = {"\xde\xad\xbe\xef=\xca\xfe\xba\xbe", NULL};//第三关用到的环境变量
    
        /*2*/
        int pipe_stdin[2] = {-1, -1};
        int pipe_stderr[2] = {-1, -1};
        pid_t pid_child;
        if ( pipe(pipe_stdin) < 0 || pipe(pipe_stderr) < 0 )
        {
            perror("Cannot create the pipe.");
        }
    
        #define STDIN_READ   pipe_stdin[0]
        #define STDIN_WRITE  pipe_stdin[1]
        #define STDERR_READ  pipe_stderr[0]
        #define STDERR_WRITE pipe_stderr[1]
    
        if ( ( pid_child = fork() ) < 0 )   // do not forget the ()!
        {
            perror("Cannot create fork child.");
        }
        if(pid_child == 0)  //in child
        {
            close(STDIN_READ);
            close(STDERR_READ);//关闭输入
            write(STDIN_WRITE,"\x00\x0a\x00\xff",4);
            write(STDERR_WRITE,"\x00\x0a\x02\xff",4);
        }
        else    //in father
        {
            close(STDERR_WRITE);
            close(STDIN_WRITE);
            dup2(STDIN_READ,0);
            dup2(STDERR_READ,2);
            execve("/home/input2/input", argv, envp);  
        }
        printf("link\n");
    
        /*4*/
        FILE *fp=fopen("\x0a","wb");
        if(!fp)
        {
            perror("Can not open file.");
    
        }
        printf("Open file success.\n");
        fwrite("\x00\x00\x00\x00",4,1,fp);
        fclose(fp);
    
        /*5*/
        sleep(2); // wait the server start
        int sockfd;
        char buf[10] = {0}; // buf to be sent
        int len;            // len of avail buf
        struct sockaddr_in servaddr;
        servaddr.sin_family = AF_INET;  
        servaddr.sin_port = htons(9999);  // port in argv['C'] 
        servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //local
        if( (sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0 )  
        {  
            perror("socket error.");  
            exit(1);  
        }  
        if ( connect(sockfd, (struct sockaddr*) &servaddr, sizeof(servaddr)) < 0 )
        {
            perror("connect error.");
            exit(1);
            }
        printf("socket connect.\n");
        strcpy(buf, "\xde\xad\xbe\xef");
        len = strlen(buf);
        send(sockfd, buf, len, 0);
        close(sockfd);  
    
        return 0;
    }
    
    success.png

    相关文章

      网友评论

          本文标题:pwnable.kr input

          本文链接:https://www.haomeiwen.com/subject/nrvicqtx.html