본문 바로가기
[ ★ ]Study/War Game

[Toddler's Bottle] pwnable.kr input 풀이

by nroses-taek 2017. 9. 13.
아래는 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

argv['A'] = argv[65] = NULL 부터 시작해서 조건문을 맞추어 나가면 된다.

문제를 풀기위해서 exec함수 시리즈를 공부해봐야 합니다.

input파일을 실행하기 위한 경로와 전달해줄 환경변수가 있어야 하므로 execve를 선택했습니다.
조금더 자세히 쓰고 싶지만 그러면 문제 풀이 글이 어지러워질 것 같아서
알고 싶다면 구글 검색 !

Answer

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
70
71
72
73
74
75
76
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
 
 
int main()
{
    //stage1
    int i = 0;
    char *argv[100];
    char *env[2= {"\xde\xad\xbe\xef=\xca\xfe\xba\xbe"NULL};
    for(i = 0; i < 99; i ++){
        argv[i] = "A";
    }
    argv[100= NULL;
    argv['A'= "\x00";
    argv['B'= "\x20\x0a\x0d";
    argv['C'= "8689";    
    
    //stage2
    int fd1[2], fd2[2];
    pipe(fd1);
    pipe(fd2);
 
 
    pid_t child;
 
    if((child = fork()) == -1){
        puts("fork() error");
        return 1;
    }
    else if( child == 0 ){
        close(fd1[1]);
        close(fd2[1]);
        dup2(fd1[0], 0);
        dup2(fd2[0], 2);
        close(fd1[0]);
        close(fd2[0]);
        
        FILE *fp = fopen("\x0a""w");
        fwrite("\x00\x00\x00\x00",4,1,fp);
        fclose(fp);
    
        execve("/home/input2/input", argv, env);    
    }
    else{
        close(fd1[0]);
        close(fd2[0]);
        write(fd1[1], "\x00\x0a\x00\xff"4);
        write(fd2[1], "\x00\x0a\x02\xff"4);
 
        int sockfd, port;
        struct sockaddr_in server_addr;
        
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
        memset(&server_addr, 0x00sizeof(server_addr));
 
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        server_addr.sin_port = htons(atoi(argv['C']));
 
        if(connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr))){
            printf("connect() error");
            return 1;
        }
        write(sockfd, "\xde\xad\xbe\xef"4);
        close(sockfd);
 
    }
}
cs

------stage1------
1
2
3
4
5
6
for(i = 0; i < 99; i ++){
argv[i] = "A";
}
argv[100= NULL;
argv['A'= "\x00";
argv['B'= "\x20\x0a\x0d";

cs


argv 버퍼에 A로 다 채우고 마지막에는 NULL바이트를 넣었다.
C언어에서 배열의 끝은 NULL이어야 한다. @_@
그리고 argv['A'], argv['B']에 알맞게 넣어준다.

------stage2------
이 부분을 따로 공부하게 됐네요. 파이프라는 것을 통해서 통신함으로 문제를 푸는 것입니다.
http://nroses-taek.tistory.com/139 pipe에 대한 설명입니다. 공부해보세요.
위 참고포스팅은 양방향 통신이 아닙니다. pipe에 감을 잡기 위한 것일뿐...
이 stage2를 Clear하시기 위해서는 양방향 통신이 필요합니다. 때문에 pipe() 함수가 2개 되어있죠.
그리고 나서 문제의 read와 memcmp를 맞추어 주기 위해서 write로 조건을 맞추어 주면 됩니다.

stage2를 클리어하기 위해서 순서도를 계획해봅시다.
  1. 각 파일 디스크립터에 대한 2개의 파이프를 만들어줍니다.
  2. child 스레드를 생성합니다. 이 스레드는 input파일을 우리의 목적을 달성하는데  있어서 중요한 역할을 할 것입니다.
    값들을 input파일에 보내야하니깐요 !
  3. 부모 프로세스에서 값을 보내줍니다.
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
int fd1[2], fd2[2];
pipe(fd1);
pipe(fd2);
 
 
pid_t child;
 
if((child = fork()) == -1){
puts("fork() error");
return 1;
}
else if( child == 0 ){
//input파일의 write부분은 필요 없겠죠 ?
close(fd1[1]);
close(fd2[1]);
//이 부분이 값을 redirect 하는 부분입니다.
dup2(fd1[0], 0);
dup2(fd2[0], 2);
close(fd1[0]);
close(fd2[0]);
 
 //프로그램을 실행합니다 !
execve("/home/input2/input", argv, env);        
}
else{
//부모 프로세스의 read end는 필요가 없습니다.
close(fd1[0]);
close(fd2[0]);
write(fd1[1], "\x00\x0a\x00\xff"4);
write(fd2[1], "\x00\x0a\x02\xff"4);
}
 
cs


------stage3------
1
char *env[2= {"\xde\xad\xbe\xef=\xca\xfe\xba\xbe"NULL};
cs

------stage4------
1
2
3
FILE *fp = fopen("\x0a""w");
fwrite("\x00\x00\x00\x00",4,1,fp);
fclose(fp);
cs

이번에는 File입니다. \x0a 라는 파일을 읽어들여 4바이트 값을 memcmp로 비교합니다.
이것 또한 위와같이 만족시켜주면 됩니다.


------stage5------
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int sockfd, port;
struct sockaddr_in server_addr;
 
sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
memset(&server_addr, 0x00sizeof(server_addr));
 
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(atoi(argv['C']));
 
if(connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr))){
printf("connect() error");
return 1;
}
write(sockfd, "\xde\xad\xbe\xef"4);
close(sockfd);
cs
argv['C']로 포트번호를 받아 bind합니다. 또한 recv한 데이터가 \xde\xad\xbe\xef 4바이트랑 같냐라는
조건을 달았습니다. 그러면 우리는 argv['C] 에다가 임의의 포트번호를 넣어주고, 로컬로 연결시켜준 다음에 write로 조건문을 만족시켜 줍니다.



댓글