pwnable.kr input

思路

  • 首先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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容

  • 一、Linux系统概述 不加引号可理解为宏,直接替换,单引号中特殊字符会被解释为普通字符,双引号中$,,'还是特殊...
    赤果_b4a7阅读 1,507评论 0 2
  • ### main函数执行之前做了什么?(iOS) & dyld 是Apple 的动态链接器;在 xnu 内核为程...
    天使君阅读 688评论 0 1
  • 1. 硬链接和软连接区别 硬连接-------指通过索引节点来进行连接。在Linux的文件系统中,保存在磁盘分区...
    杰伦哎呦哎呦阅读 2,256评论 0 2
  • Linux 进程管理与程序开发 进程是Linux事务管理的基本单元,所有的进程均拥有自己独立的处理环境和系统资源,...
    JamesPeng阅读 2,467评论 1 14
  • UNIX-like 操作系统,有一个强劲的功能:可以同时运行多个进程,并且让进程们共享 CPU,内存,和其他的资源...
    Tulayang阅读 3,253评论 1 36