zoukankan      html  css  js  c++  java
  • pwnable.kr之input

    连接到远程服务器:ssh input2@pwnable.kr -p2222

    查看题目所给的代码,根据题目的要求我们要给出所有符合条件的输入才能拿到flag,本来想在输入上动点歪脑筋,结果输入有字节数的限制,然后再查看一下程序是否有保护

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

    发现有栈溢出和数据执行保护,就绝了走捷径的心思。

    查看代码知道有五步要走,为了检测每步是否正确,使用命令:scp -P 2222 input2@pwnable.kr:/home/input2/input /home/countfatcode/Code 把input文件下载到本地。

    scp命令详解请看:https://blog.csdn.net/sanbingyutuoniao123/article/details/72420637

    第一部分:argv

    1     // argv
    2     if(argc != 100) return 0;
    3     if(strcmp(argv['A'],"x00")) return 0;
    4     if(strcmp(argv['B'],"x20x0ax0d")) return 0;
    5     printf("Stage 1 clear!
    ");

    因为要传入的参数过多,可以用 execve() 函数来实现。

    函数详解请看:https://www.cnblogs.com/jxhd1/p/6706701.html

    于是可以构造c语言代码:

    #include <stdio.h>
    #include <stdlib.h>
    int main()
    {
        char *argv[]={"input",[1 ... 99]="A",NULL};
        argv['A']="x00";
        argv['B']="x20x0ax0d";
        execve("input",argv,NULL);
        return 0;
    }

    编译运行,得到结果:

    Welcome to pwnable.kr
    Let's see if you know how to give input to program
    Just give me correct inputs then you will get the flag :)
    Stage 1 clear!

    第二部分:stdio

    查看代码:

        // stdio
        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! ");

    第一个 read() 函数的文件描述符是0,表示从终端读入字符,第二个 read() 函数的文件描述符是 2,表示从标准错误输出读入字符,此时就要用到 pipe管道来解决。

    pipe详解:https://blog.csdn.net/skyroben/article/details/71513385

    因为有两个read,所以要创建两个管道,代码如下:

     int fd1[2],fd2[2];
     int ret1,ret2; //用来接受pipe函数的返回值
     ret1=pipe(fd1);
     if(ret1==-1)
     {
           perror("pipe fd1 error:");
           exit(1);
     }
     ret2=pipe(fd2);
     if(ret2==-1)
     {
           perror("pipe fd2 error:");
           exit(1);
     }

    再用 fork() 创建子进程,代码如下:

    pid_t id=fork()

     第二部分完整代码如下:

        //stdio
        int fd1[2],fd2[2];
        int ret1,ret2; //用来接受pipe函数的返回值
        ret1=pipe(fd1);
        if(ret1==-1)
        {
            perror("pipe fd1 error:");
            exit(1);
        }
        ret2=pipe(fd2);
        if(ret2==-1)
        {
            perror("pipe fd2 error:");
            exit(1);
        }
        pid_t id=fork();
        if(id==0) //子进程
        {
            close(fd1[0]);
            close(fd2[0]); //关闭子进程读端
            write(fd1[1],"x00x0ax00xff",4);
            write(fd2[1],"x00x0ax02xff",4);
        }
        else //父进程
        {
            close(fd1[1]);
            close(fd2[1]);
            dup2(fd1[0],0);
            dup2(fd2[0],2);//文件描述符重定向
            close(fd1[0]);
            close(fd2[0]);
    
            //env
            char *env[2]={"xdexadxbexef=xcaxfexbaxbe",NULL};
    
            //file
            FILE *fp=fopen("x0a","w");
            if(fp==NULL)
                printf("文件打开失败
    ");
            else
            {
                fwrite("x00x00x00x00",4,1,fp);
                fclose(fp);
                execve("input",argv,env);
            }
        }

    将一二部分的代码结合起来运行一下,发现正确。

    第三部分:env

    查看代码:

        // env
        if(strcmp("xcaxfexbaxbe", getenv("xdexadxbexef"))) return 0;
        printf("Stage 3 clear!
    ");

    getenv() 函数的作用是获取环境变量名为 xdexadxbexef 的变量值,这时就要用 execve() 的第三个参数来构造该环境变量,具体代码如下:

            //env
            char *env[2]={"xdexadxbexef=xcaxfexbaxbe",NULL};

    结合一二部分的代码运行,发现正确。

     第四部分:file

    查看代码:

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

    这部分是关于文件操作,具体代码如下:

            //file
            FILE *fp=fopen("x0a","w");
            if(fp==NULL)
                printf("文件打开失败
    ");
            else
            {
                fwrite("x00x00x00x00",4,1,fp);
                fclose(fp);
                execve("input",argv,env);
            }

    再结合第一二三部分的代码运行,正确。

    第五部分:network

    第五题主要涉及的是socket编程,题目给的代码是:

        // network
        int sd, cd;
        struct sockaddr_in saddr, caddr;
        sd = socket(AF_INET, SOCK_STREAM, 0);
        if(sd == -1){
            printf("socket error, tell admin
    ");
            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
    ");
                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
    ");
            return 0;
        }
        if( recv(cd, buf, 4, 0) != 4 ) return 0;
        if(memcmp(buf, "xdexadxbexef", 4)) return 0;
        printf("Stage 5 clear!
    ");
    
        // here's your flag
        system("/bin/cat flag");    

    观察知题目提供的是服务器端的代码,所以我们要通过编程实现客户端的代码

            sleep(5);
            struct sockaddr_in server; 
            int sockfd = socket(AF_INET,SOCK_STREAM,0); //创造一个套字节,客户端只有一个套字节
            if(sockfd<0) //检查是否创造成功
            {
                perror("Cannot create the socket:");
                exit(1);
            }
            server.sin_family=AF_INET; //使用IPv4地址
            server.sin_addr.s_addr=inet_addr("127.0.0.1"); //具体的IP地址
            server.sin_port=htons(55555); //端口
            if(connect(sockfd,(struct sockaddr*)&server,sizeof(server))<0)
            {
                perror("Problem connecting");
                exit(1);
            }
            printf("Connected
    ");
            char buf[4]="xdexadxbexef";
            write(sockfd,buf,4);
            close(sockfd);
            return 0;

    最后这题完整的代码如下:

    #include <stdio.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <stdlib.h>
    #include <unistd.h>
    int main()
    {
        //argv
        char *argv[]={"./input",[1 ... 99]="A",''};
        argv['A']="x00";
        argv['B']="x20x0ax0d";
        argv['C']="55555";
    
        //stdio
        int fd1[2],fd2[2];
        int ret1,ret2; //用来接受pipe函数的返回值
        ret1=pipe(fd1);
        if(ret1==-1)
        {
            perror("pipe fd1 error:");
            exit(1);
        }
        ret2=pipe(fd2);
        if(ret2==-1)
        {
            perror("pipe fd2 error:");
            exit(1);
        }
        pid_t id=fork();
        if(id==0) //子进程
        {
            close(fd1[0]);
            close(fd2[0]); //关闭子进程读端
            write(fd1[1],"x00x0ax00xff",4);
            write(fd2[1],"x00x0ax02xff",4);
        }
        else //父进程
        {
            close(fd1[1]);
            close(fd2[1]);
            dup2(fd1[0],0);
            dup2(fd2[0],2);//文件描述符重定向
            close(fd1[0]);
            close(fd2[0]);
    
            //env
            char *env[2]={"xdexadxbexef=xcaxfexbaxbe",NULL};
    
            //file
            FILE *fp=fopen("x0a","w");
            if(fp==NULL)
                printf("文件打开失败
    ");
            else
            {
                fwrite("x00x00x00x00",4,1,fp);
                fclose(fp);
                execve("input",argv,env);
            }
        }
            //network
            sleep(5);
            struct sockaddr_in server;
            int sockfd = socket(AF_INET,SOCK_STREAM,0);
            if(sockfd<0)
            {
                perror("Cannot create the socket:");
                exit(1);
            }
            server.sin_family=AF_INET;
            server.sin_addr.s_addr=inet_addr("127.0.0.1");
            server.sin_port=htons(55555);
            if(connect(sockfd,(struct sockaddr*)&server,sizeof(server))<0)
            {
                perror("Problem connecting");
                exit(1);
            }
            printf("Connected
    ");
            char buf[4]="xdexadxbexef";
            write(sockfd,buf,4);
            close(sockfd);
            return 0;
    }

    注意:我们需要用scp命令把代码文件上传到服务器的tmp文件夹,由于tmp文件夹里没有flag文件,还需要用ln命令进行连接,最后编译运行就可以得到flag。

  • 相关阅读:
    8. Java 异常
    9. GUI编程
    生成验证码
    使用response实现文件的下载
    关于小程序的几个坑
    关于redux应用
    关于修改react的启动端口
    react 坑总结
    关于小程序的一些坑的总结
    微信开发者工具 跨域问题
  • 原文地址:https://www.cnblogs.com/countfatcode/p/11181197.html
Copyright © 2011-2022 走看看