엄마~!~!어떻게 하면 프로그램에 내 입력이 통과할 수 있는거야? 라고 물어본다. 살짝 리버싱문제 같은 느낌도 든다. 일단 접속해보자.



input파일을 실행해 보자.



인자를 직접 전달해 줘야 하는 것 같다. input.c파일을 읽어보면


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
68
#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 != 100return 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, 41, 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, 40!= 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;
}
 
cs


이렇게 꽤 긴 코드가 있다.

처음부터 차례대로 분석해보자.


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


1단계는 argv에 관한 코드이다. 


argc와 argv는 main함수의 인수인데 argc에는 인자의 개수가 저장되고 argv에는 인자가 각각 들어있다.

그리고 여기서 argv의 첫 번째 배열에는 실행 명령어가 들어가있다.


예를들어서 ./input a b와 같이 인자를 전달해준다면

argc에는 3이 들어가고 argv[0]에 ./input, argv[1]에 a, argv[2]에 b가 들어가게 되는 것이다.


이제 저 코드를 해석해보면 인자의 개수를 100개로 하고 argv['A'] 즉 argv[65]에는 \x00이 들어가고 argv['B'] 즉 argv[66]에는 \x20\x0a\x0d가 들어가게 하면 되는것이다.


이제 2단계를 확인해보자.


1
2
3
4
5
6
7
// 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");
cs


2단계는 파일 디스크립터 관련된 문제이다.

0 : 표준입력

1 : 표준출력

2 : 표준에러

이것만 이용하면 바로 풀 수 있을 것이다.

read(0, buf, 4)는 표준 입력의 4바이트를 읽어와서 \x00\x0a\x00\xff와 비교하고

read(2, buf, 4)는 표준 에러의 4바이트를 읽어와서 \x00\x0a\x02\xff와 비교한다.


여기서 표준 입력은 그냥 \x00\x0a\x00\xff를 넣어주면 되는데 표준에러에 어떻게 넣어야 할까에 대해서 고민을 좀 했다.

그냥 간단하게 어떤 파일을 하나 열어서 거기에 \x00\x0a\x02\xff를 넣고 표준에러의 fd값을 저 파일로 바꿔주면 될 것 같아서 해보니까 성공했다~!



쓰기 권한이 있는 /tmp에 가서 test파일을 만들고 실행시켜보았다.



2단계까지의 페이로드는 다음과 같다.


from pwn import *


context.log_level = 'debug'


k = open("ryu", "w")

k.write("\x00\x0a\x02\xff")

#ryu라는 파일을 열어서 해당 값을 입력해준다.


k = open("ryu", "r")


argv = [str(i) for i in range(100)]

argv[ord('A')] = '\x00'

argv[ord('B')] = '\x20\x0a\x0d'

#전달할 인자를 생성해준다.


p = process(argv, stderr=k, executable='/home/input2/input')


p.recv(1000)


p.send('\x00\x0a\x00\xff')

#표준입력으로 값을 전달해준다.

p.recv(1000)



이제 3단계를 확인해보자.


1
2
3
 // env
    if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
    printf("Stage 3 clear!\n");
cs


getenv함수는 해당 환경변수의 값을 가져오는 함수니까 "\xde\xad\xbe\ef"라는 이름의 환경변수 값이 "\xca\xfe\xba\xbe"이면 된다.


간단하게


e = {'\xde\xad\xbe\xef' : '\xca\xfe\xba\be'}


라는 코드를 추가하고 환경변수로 e를 넣어주면 된다.




성공이다~!~!


이제 4단계를 확인해보자.


1
2
3
4
5
6
7
// file
    FILE* fp = fopen("\x0a""r");
    if(!fp) return 0;
    if( fread(buf, 41, fp)!=1 ) return 0;
    if( memcmp(buf, "\x00\x00\x00\x00"4) ) return 0;
    fclose(fp);
    printf("Stage 4 clear!\n");  
cs


\x0a라는 파일을 열어서 처음 4바이트가 \x00\x00\x00\x00이면 성공이다.

이번 단계도 간단하게 파일을 열어서 저 데이터를 넣어주기만 하면 된다.

l = open("\x0a", "w")

l.write("\x00\x00\x00\x00")

l.close()



성공이다~!~!


이제 마지막 단계를 확인해보자.


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
// 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, 40!= 4 ) return 0;
    if(memcmp(buf, "\xde\xad\xbe\xef"4)) return 0;
    printf("Stage 5 clear!\n");
cs


argv['C']에 있는 값을 포트번호로 소켓을 생성하고 listen하고 있다가 데이터가 들어오면 그 데이터를 buf에 저장하고 \xde\xad\xbe\xef와 비교하는 코드이다.


이번단계를 해결하려면 argv['C']에 포트번호를 넣어놓고 remote를 통해 lacalhost를 호출하고 데이터를 전달해주면 될 것 같다.


from pwn import *


context.log_level = 'debug'


k = open("ryu", "w")

k.write('\x00\x0a\x02\xff')

#ryu라는 파일을 열어서 해당 값을 입력해준다.


l = open("\x0a", "w")

l.write("\x00\x00\x00\x00")

l.close()

#4단계 파일에 값을 넣어준다.


k = open("ryu", "r")


argv = [str(i) for i in range(100)]

argv[ord('A')] = '\x00'

argv[ord('B')] = '\x20\x0a\x0d'

argv[ord('C')] = '1010'

#전달할 인자를 생성해준다.


e = {'\xde\xad\xbe\xef' :'\xca\xfe\xba\xbe'}

#환경변수를 만들어준다.


p = process(argv, stderr=k, env=e, executable='/home/input2/input')


p.recv(1000)


p.send('\x00\x0a\x00\xff')

#표준입력으로 값을 전달해준다.

p.recv(1000)


sleep(3)

#소켓 열리는 시간도 생각해준다.


m = remote('lacalhost', 1010)

m.send('\xde\xad\xbe\xef')


p.recv(1000)

p.recv(1000)




성공은 했는데 나오지가 플래그가 뜨지 않았다ㅠㅠ

이 파일이 실행될 때의 경로가 /tmp안이니까 플래그 파일이 해당경로 안에 없어서 그런 것이였다.

중간에 파일을 만들기도 해야하니까 옮기는건 힘들고 심볼릭 링크를 걸어줬다.



이런식으로 해주면



플래그가 나온 것을 확인할 수 있다~!~!

'pwnable > pwnable.kr' 카테고리의 다른 글

[pwnable.kr] mistake  (0) 2019.02.24
[pwnable.kr] leg  (0) 2019.02.20
[pwnable.kr] random  (0) 2019.01.15
[pwnable.kr] passcode  (0) 2019.01.13
[pwnable.kr] flag  (0) 2019.01.12

+ Recent posts