WarGame/pwnable.kr

input

cyanhe_wh 2021. 1. 5. 22:41
반응형

소스코드

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

코드를 살펴 보면 다 입력값을 받는 것이다.

첫 번째 조건

        // 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");

메인 함수의 argc 개수가 100개여야하고, argv['A'] 즉, argv[65] 값이 \x00 이여야 한다.

argv['B']argv[66] 이고 값이 \x20\x0a\x0d 여야한다.

두 번째 조건

        // 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");

buf 함수에 입력을 받는데, 입력 받는 값이 \x00\x0a\x00\xff 여야 한다.

그리고 stderr를 통해 입력을 받는데, stderr는 \x00\x0a\x02\xff 여야 한다.

stdin = 0

stdout = 1

stderr = 2

세 번째 조건

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

환경변수 이름이 \xde\xad\xbe\xef 인 변수안에 \xca\xfe\xba\xbe 값이 존재해야 한다.

네 번째 조건

        // 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"); 

파일의 이름이 \x0a 인 파일을 읽고, 그 파일의 데이터에 \x00\x00\x00\x00 값인지 확인한다.

파일도 있어야하고, 데이터도 존재해야 한다.

마지막 조건

        // 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");

이번에는 서버를 열고, 서버에 접속을 기다리고, 접속을 하면 사용자로 부터 데이터를 받는다.

이때 입력 값이 \xde\xadxbe\xef 인지 확인한다.

서버를 열때 포트번호를 argv['C']의 값으로 사용한다.

이렇게 조건들을 다 만족하면 flag 값을 출력해 성공하게 된다.

이제 이 조건들을 하나하나 만족해주는 코드를 작성하자.

from pwn import *

target = ['/home/input2/input']
for i in range(1, 100):
        if i == ord('A'):
                target.append('\x00')
        elif i == ord('B'):
                target.append('\x20\x0a\x0d')
        elif i == ord('C'):
                target.append('12346')
        else:
                target.append('data')

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

with open('./stderr', 'w') as fp:
        fp.write('\x00\x0a\x02\xff')

p = process(target, env=env_data, stderr=open('./stderr'))

print p.recvline()
print p.recvline()
print p.recvline()

print p.recvuntil('Stage 1 clear!\n')

p.send('\x00\x0a\x00\xff')
print p.recvuntil('Stage 2 clear!\n')
print p.recvuntil('Stage 3 clear!\n')

fp = open('./\x0a', 'w')
fp.write('\x00' * 4)
fp.close()
print p.recvuntil('Stage 4 clear!\n')

sleep(2)

conn = remote("127.0.0.1", 12346)
conn.send('\xde\xad\xbe\xef')
print p.recvuntil('Stage 5 clear!\n')

p.interactive()

이 부분은 문제 서버에 접속해 풀어야한다. 서버에 접속하면 input2 홈 경로에는 쓰기 권한이 존재하지 않는다. 그렇다고 원격에서 해도 뭐 의미가 없다.

서버에 /tmp 경로가 쓰기권한이 존재한다.

이 부분에 파일을 만들어 해당 코드를 작성해서 풀면된다.

경로가 /tmp 안에 있어서 flag 파일이 존재하지 않아 마지막 cat flag하는 명령어가 안된다.
되게 하기위해 심볼릭 링크를 설정해 파일을 출력할 수 있게 하면된다.

반응형

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

leg  (0) 2021.01.05
coin1  (0) 2020.12.23
mistake  (0) 2020.12.02
shellshock  (0) 2020.12.02