pwnable.kr之input

文章最后更新时间为:2019年03月05日 15:51:25

题目:

ssh连接之后,发现目录有三个文件inputinput.cflag

熟悉的配方,先查看一下input.c:

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

很多条件需要满足,那么一个一个来解决。

1.argv

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

从以上可以知道:

  1. 这里需要100个参数。(程序名属于第一个参数)
  2. 第66个参数为空字符(A的ascci码为65,下标从0开始)
  3. 第67个参数为\x20\x0a\x0d(B的ascci码为66)

这里我们先用python来生成参数试试:

root@kali:~/桌面/pwn# ./input `python -c 'print "A"*64 + "\x00" + "\x20\x0a\x0d" + "A"*33'`
bash: 警告:命令替换:忽略输入中的 null 字节
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 :)

因为参数中存在\x00,也就是null,所以当程序识别参数时到这里的时候就断了,用python当输入是不行的,这里直接用C语言写:

#include <unistd.h>
int main(){
char *argv[101] = {"/home/input/input", [1 ... 99] = "A", NULL};
argv['A'] = "\x00";
argv['B'] = "\x20\x0a\x0d";
 
execve("/home/input/input",argv,NULL);
}

编译运行输出: Stage 1 clear!

2.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");

程序必须从stdin读取\x00\x0a\x00\xff,从stderr读取\x00\x0a\x02\xff。对于这可以使用管道完成。可以参考http://unixwiz.net/techtips/remap-pipe-fds.html,我们将使用两个管道:一个用于stdin,一个用于stderr。具体做法如下:

  • 创建两个管道pipe2stdin和pipe2stderr
  • 创建子进程
  • 子进程

    • write \x00\x0a\x00\xff to pipe2stdin
    • write \x00\x0a\x02\xff to pipe2stderr
  • 父进程

    • 将stdin映射到pipe2stdin
    • 将stderr映射到pipe2stderr
    • 执行input程序

增加以下代码:

int pipe2stdin[2] = {-1,-1};
int pipe2stderr[2] = {-1,-1};
pid_t childpid;
 
if ( pipe(pipe2stdin) < 0 || pipe(pipe2stderr) < 0){ // 创建管道
    perror("Cannot create the pipe");
    exit(1);
}
 
if ( ( childpid = fork() ) < 0 ){     // 创建子进程
    perror("Cannot fork");
    exit(1);
}
 
if ( childpid == 0 ){
    close(pipe2stdin[0]); close(pipe2stderr[0]);  // 子进程关闭读管道
    write(pipe2stdin[1],"\x00\x0a\x00\xff",4);    
    write(pipe2stderr[1],"\x00\x0a\x02\xff",4);
}
else {
    close(pipe2stdin[1]); close(pipe2stderr[1]);   // 关闭写管道
    dup2(pipe2stdin[0],0); dup2(pipe2stderr[0],2); // 映射stdin和stderr
    close(pipe2stdin[0]); close(pipe2stderr[1]);   
    execve("/home/input/input",argv,NULL);  // 执行程序
}

3.env

if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;

getenv("\xde\xad\xbe\xef")意为查看名为"\xde\xad\xbe\xef"的环境变量的值,这里我们只需传递一个环境变量,名为"\xde\xad\xbe\xef",值为"\xca\xfe\xba\xbe"即可。

char *env[2] = {"\xde\xad\xbe\xef=\xca\xfe\xba\xbe", NULL};
execve("/home/input/input",argv,env); 

4.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");    

We need to open a file and write down 4 null bytes. Once we are done we can close it.
我们需要打开一个文件,并且在文件中写4个null。对于文件的操作需要在execve函数被调用之前完成。

FILE* fp = fopen("\x0a","w");
fwrite("\x00\x00\x00\x00",4,1,fp);
fclose(fp);

5.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");

程序变成了一个socket服务器,我们选择端口为55555:

argv['C'] = "55555";

现在,当我们执行程序时,它将侦听端口55555并等待一个传入连接。我们发送给程序的数据的前4个字节等于\xde\xad\xbe\xef即可。

我们在程序中构建自己的客户端:

int sockfd;
struct sockaddr_in server;
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);
}
char buf[4] = "\xde\xad\xbe\xef";
write(sockfd,buf,4);
close(sockfd);

6.GOGOGO

最后创建c文件时,在/home/input2目录下是无法创建文件的,只有在/tmp目录下才有权限创建文件,而flag路径为/home/input2/flag,这意味着input将无法找到文件flag。为了解决这个问题,我们可以在工作目录中创建一个flag链接,并将其命名为flag。

ln -s /home/input2/flag flag 

最终的完成程序为:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
 
int main (){
    //Stage 1
    char *argv[101] = {"/home/input/input", [1 ... 99] = "A", NULL};
    argv['A'] = "\x00";
    argv['B'] = "\x20\x0a\x0d";
    argv['C'] = "55555";
     
    //Stage 2
    int pipe2stdin[2] = {-1,-1};
    int pipe2stderr[2] = {-1,-1};
    pid_t childpid;
 
    if ( pipe(pipe2stdin) < 0 || pipe(pipe2stderr) < 0){
        perror("Cannot create the pipe");
        exit(1);
    }
     
    //Stage 4
    FILE* fp = fopen("\x0a","w");
    fwrite("\x00\x00\x00\x00",4,1,fp);
    fclose(fp);
 
    if ( ( childpid = fork() ) < 0 ){
        perror("Cannot fork");
        exit(1);
    }
     
    if ( childpid == 0 ){
                /* Child process */
        close(pipe2stdin[0]); close(pipe2stderr[0]); // Close pipes for reading 
        write(pipe2stdin[1],"\x00\x0a\x00\xff",4);
        write(pipe2stderr[1],"\x00\x0a\x02\xff",4);
         
    } 
    else {
        /* Parent process */
        close(pipe2stdin[1]); close(pipe2stderr[1]);   // Close pipes for writing
        dup2(pipe2stdin[0],0); dup2(pipe2stderr[0],2); // Map to stdin and stderr 
        close(pipe2stdin[0]); close(pipe2stderr[1]);   // Close write end (the fd has been copied before)
        // Stage 3
        char *env[2] = {"\xde\xad\xbe\xef=\xca\xfe\xba\xbe", NULL};
        execve("/home/input/input",argv,env);   // Execute the program  
        perror("Fail to execute the program");
        exit(1);
    }
 
        // Stage 5
        sleep(5);
        int sockfd;
        struct sockaddr_in server;
        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\n");
        char buf[4] = "\xde\xad\xbe\xef";
        write(sockfd,buf,4);
        close(sockfd);
        return 0;
}

上传文件

scp -P2222 /root/test.c input2@pwnable.kr:/tmp/what

flag

Mommy! I learned how to pass various input in Linux :)

参考:https://werewblog.wordpress.com/2016/01/11/pwnable-kr-input/

1 + 6 =
1 评论
    免费节点 Firefox Browser 47 Windows 7
    2019年03月16日 回复

    学习了,涨姿势啊!!