카테고리 없음

[pwnable.kr] input ( write - up )

hellojuntae 2019. 4. 18. 22:48

[1] 코드분석

처음 이문제를 풀때, 나도그랬고, 아마 여러분들도 그랬을 것이다.

솔직히 그냥 문제보고 나갔다.

근데 지금은 toddler 부분은 다풀어야겠다고 생각이 들었다.

그래서 시도해본다.

솔직히 나는 이문제가 pwntools 공부하는데 큰 도움이 되었던것 같다. 나중에 다시 pwntools로 익스를 작성해 봐야겠다.

익스코드는 https://mandu-mandu.tistory.com/76 를 참조했다.

시작해보자.

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
69
#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

진짜 개극혐이다.

코드를 분석해보면, stage1부터 stage5까지 조건에 맞게 값을 입력받으면, 플래그가 뿅 하는 구조이다.




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

일단 stage1의 부분이다. 

첫번째 if문 : 인자 ( argc ) 의 갯수가 100개인지 확인한다.

두번째 if문 : 우리는 두번째 if문을 거짓으로 만들어야 한다. 그리고 strcmp 함수는 두 문자열이 같으면 거짓을 반환한다. 따라서 우리는 argv['A']와 "\x00"를 같게 만들어주면 된다.

세번째 if문 : 마찬가지로 argv['B']와 \x20\x0a\x0d 를 같게 만들어주면 된다.




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

두번째로 stage2 부분이다.

마찬가지로 read함수로 입력을 받은 후 memcmp로 값을 비교한다. buf의 4바이트 만큼과 "\x00\x0a\x00\xff"의 4바이트 만큼을 비교하여 두 값이 같다면 0을 출력해준다. 그러므로 우리는 두 값을 같게 만들어 주면 될것이다.

그리고 read의 fd중 2는 stderr ( 표준에러 ) 이다. 이 값에다가 "\x00\x0a\x02\xff" 를 넣어주면 조건을 만족하고, 스테이지를 클리어 할 수 있을것이다.




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

stage3 이다.

요부분은 쉽다. getenv는 환경변수의 주소를 출력해주는 함수이다. 

환경변수 "\xde\xad\xbe\xef"를 "\xca\xfe\xba\xbe" 로 입력해주면 스테이지가 클리어 될것이다.




1
2
3
4
5
6
7
8
// 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

stage4 이다. 파일을 연 후에, 처음 4바이트가 "\x00\x00\x00\x00" 이면 조건을 만족할것이다.




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
// 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

마지막 stage이다. 다른건 다풀고 여기서 멘탈이 나갔었다.

소켓프로그래밍(?)에 대해 더 많은 정보가 필요하면 https://rotapple.tistory.com/8 여기로!!


중요부분만 설명하면, argc['C'] 포트를 연 후에, 'C' 포트부분에 "\xde\xad\xbe\xef" 를 입력하면 플래그가 뜰것이다.





[2] 익스플로잇

파이썬 익스코드는 위에 써 둔 블로그 주소에서 참고했다.

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
#-*- coding:utf-8 -*-
 
from pwn import *
 
#stage1
argvs = [str(i) for i in range(100)]
argvs[ord('A')] = '\x00'
argvs[ord('B')] = '\x20\x0a\x0d'
 
#stage2
with open('./stderr''a') as f:
    f.write('\x00\x0a\x02\xff')
#stage3
envVal = {'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'}
 
#stage4
with open('./\x0a''a') as f:
    f.write('\x00\x00\x00\x00')
 
#stage5 
argvs[ord('C')] = '40000'
 
#인자전달, stderr 파일 열기, 환경변수 설정등
target = process(executable='/home/input2/input', argv=argvs, stderr=open('./stderr'), env=envVal)
 
#stage2의 stdin
target.sendline('\x00\x0a\x00\xff')
 
#다시 stage5, 포트가 argv['C']와 일치해야겠죠?
conn = remote('localhost'40000)
conn.send('\xde\xad\xbe\xef')
target.interactive()  
 
cs

pwntool을 사용하면 이렇게 편안하게 익스플로잇 할 수 있었다. 

아 그리고 쓰기권한이 있는 폴더는 /tmp 이다. /tmp로 이동한 후 mkdir로 폴더를 하나 만들어서 여기서 익스코드를 작성하자.

근데 그러면 플래그가 없어서 플래그를 읽어오지 못할것이다.


그래서 우리는 심볼릭 링크로 플래그를 연결시킨후에 플래그를 출력시키게 하면 된다. 

ln -s /home/input2/flag flag


이러면 플래그가 출력될것이다.




와! 클리어!