Keep Calm and Carry On

pwnable-kr-input

0x01 分析

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

在这个程序中如果要执行到system("/bin/cat flag")需要突破5重关卡:

  1. 必须是100个参数,其中第'A'"\x00"(空字符串),第'B'是”\x20\x0a\x0d”;
  2. 从标准输入中读4个字节"\x00\x0a\x00\xff",从标准错误中读4个字节"\x00\x0a\x02\xff"
  3. 设置环境变量"\xde\xad\xbe\xef = \xca\xfe\xba\xbe";
  4. 打开名为\x0a的文件,并从中读4个字符"\x00\x00\x00\x00"
  5. 建立socket,并向服务端发送"\xde\xad\xbe\xef"

0x02 利用代码

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import time
import socket
import subprocess

# stage1
args = list("a" * 100)
args[0] = "./input"
args[ord('A')] = ""
args[ord('B')] = "\x20\x0a\x0d"
args[ord('C')] = "8888"

# stage2
stdin_r, stdin_w = os.pipe()
stderr_r, stderr_w = os.pipe()
time.sleep(2)
os.write(stdin_w, "\x00\x0a\x00\xff")
os.write(stderr_w, "\x00\x0a\x02\xff")

# stage3
environment = {"\xde\xad\xbe\xef" : "\xca\xfe\xba\xbe"}

#stage4
f = open("\x0a", "wb")
f.write("\x00\x00\x00\x00")
f.close()

# stage5
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

pro = subprocess.Popen(args, stdin = stdin_r, stderr = stderr_r, env = environment)

time.sleep(2)
s.connect(("127.0.0.1", 8888))
s.send("\xde\xad\xbe\xef")
s.close()

0x03 执行

  1. 由于文件无法复制到/home/input2(没有写的权限),因此将文件复制到pwnable.kr的/tmp目录(有写的权限)

    1
    $ scp -P 2222 input_exp.py input2@pwnable.kr:/tmp
  2. 由于input.csystem("/bin/cat flag")使用的是相对目录,因此要找个办法让在/tmp执行的程序也能找到/home/input2/flag文件,使用建立软链接的方法:

    1
    $ ln /home/input/flag flag # 在这个目录中访问flag就相当于访问/home/input2/flag
  3. 执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    input2@prowl:/tmp/drinkwater$ ls                                                               
    input_exp.py
    input2@prowl:/tmp/drinkwater$ ln -s /home/input2/flag flag
    input2@prowl:/tmp/drinkwater$ ls -l
    total 4 lrwxrwxrwx 1 input2 input2 17 Nov 18 07:48 flag -> /home/input2/flag
    -rw-rw-r-- 1 input2 input2 772 Nov 18 07:36 input_exp.py
    input2@prowl:/tmp/drinkwater$ python input_exp.py
    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!
    Stage 2 clear!
    Stage 3 clear!
    Stage 4 clear!
    input2@prowl:/tmp/drinkwater$ Stage 5 clear!
    Mommy! I learned how to pass various input in Linux :)
    input2@prowl:/tmp/drinkwater$