writeUp for input2

pwnable

ssh [email protected] -p 2222 (password: guest)

po上源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#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;
}

这条题目主要是用来练习Linux编程的几个常见函数

  • execv()
  • fork()
  • pipe()
  • dup2()
  • socket编程
  1. argv

    argc用来保持程序运行时的参数数目(包括程序完整路径),argv用来具体保存参数(argv[0]是程序运行路径),envp用来具体保存程序运行时的环境变量。

    1
    2
    3
    4
    5
    6
    7
    char *argv[101] = {0};
    for(int i = 0; i < 101; i++)argv[i] = "a";
    argv[0] = "/home/input2/input";
    argv['A'] = "\x00";
    argv['B'] = "\x20\x0a\x0d";
    argv[100] = NULL;
    argv['C'] = "2333"//端口
  2. stdio

    可见程序比较了标准输入流(0),标准错误流(2)中的各四个字节,可以考虑用管道重定向这两个流,利用fork()函数产生子进程,子进程将字节写入管道,父进程从管道重定向到标准流中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    int myin[2], myerr[2];
    pipe(myin);//建立一个用于输入流的管道
    pipe(myerr);//建立一个用于错误流的管道
    pid_t pid = fork();//产生子进程,共享这两个管道
    if(pid == 0){//如果是子进程
    close(myin[0]);
    close(myerr[0]);//关闭读取的一端
    write(myin[1], "\x00\x0a\x00\xff", 4);
    write(myerr[1], "\x00\x0a\x02\xff", 4);//向管道写数据
    }else if(pid > 0){//如果是父进程
    close(myin[1]);
    close(myerr[1]);//关闭输入的一端
    dup2(myin[0], 0);//重定向到标准输入流
    dup2(myerr[0], 2);//重定向到标准错误流
    execve(argv[0], argv, envp);//父进程启动目标程序
    }
  3. env

    直接设置环境变量

    1
    char *envp[2] = {"\xde\xad\xbe\xef=\xca\xfe\xba\xbe", NULL};
  4. file

    直接写文件

    1
    2
    3
    FILE *fp = fopen("\x0a", "wb");
    fwrite("\x00\x00\x00\x00", 4, 1, fp);
    fclose(fp);
  5. network

    socket编程,建立套接字然后连接,发送数据,注意端口设置在argv['C']

    1
    2
    3
    4
    5
    6
    7
    8
    9
    struct sockaddr_in serv;
    serv.sin_family = AF_INET;
    serv.sin_port = htons(2333);
    serv.sin_addr.s_addr = inet_addr("127.0.0.1");
    int sock = socket(PF_INET, SOCK_STREAM, 0);
    connect(sock, &serv, sizeof(serv));
    char *buf = "\xde\xad\xbe\xef\n";
    send(sock, buf, strlen(buf), 0);
    close(sock);

完整代码:input2.c

注意需要在运行程序的目录tmp底下建立flag的软连接ln /home/input2/flag flag