시스템 해킹/pwnable

[pwnable.kr] input

Lhaaan 2024. 5. 9. 13:40

이번건 꽤나 어려웠고, pwntools에서 다양한 기능을 사용할 수 있는 것을 알았음

뭔가 파이썬으로 커맨드라인 쳐서 실행시킬 수 있지 않을까 싶었는데, 전혀 안됨...

앵간하면 pwntools 쓰자

 

전체 코드를 보자.

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

 

argv 개수가 100개여야 하고, argv['A']가 "\x00", argv['B']가 "\x20\x0a\x0d"여야 한다. 처음에 멍청했던게 A,B를 hex로 변환하면 10,11 이니까 10,11번째라고 생각했음..진짜 멍청 ascii table 보면 A는 65, B는 66이다. pwntools에서는 argv를 지정할 수 있는 기능을 주기 때문에 해당 기능을 사용하였다.

argvs = [str(i) for i in range(100)]
argvs[ord('A')] = '\x00'
argvs[ord('B')] = '\x20\x0a\x0d'

p = process(executable = '/home/input2/input', argv=argvs, stderr=open('./stderr'), env = _env)

 

 

2. stage 2

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

 

이건 뭐냐. read(0, buf, 4); → 4바이트 입력받음 → "\x00\x0a\x00\xff" 입력 여기까지는 순탄했음

read(2, buf, 4); → 여기서 에러 출력시 "\x00\x0a\x02\xff" 이게 나와야 하는데 이걸 어케 바꾸노?

여기서 stderr을 어떻게 내 마음대로 출력하게 하는지를 몰랐음.. 근데 다른 블로그를 뒤져보니 이런 글이 있었다. stderr를 제어할 수 있는거구나..

 

파이썬 강력하다.. pwntools에서도 그 기능을 제공해주고 있었다.

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

 

3. stage 3

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

 

이 문제는 비교적 간단했다. 환경 변수 설정 문제로 간단.

 

4. stage 4

 // 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" 넣어주면 끝이다.

 

5. stage 5

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

 

이 문제는 좀 애맥었다. saddr.sin_port = htons( atoi(argv['C']) ); argv['C'] 번째 인자를 포트로 사용하여 소켓을 연다는 것은 알았는데, recv한 값이 “\xde\xad\xbe\xef”면 통과되는 것으로 추정했다. 근데 진짜 멍청했던게 내가 localhost로 열어서 연결한 다음 값을 보내줘야하는데 열지도 않고 계속 send보내서 삽질함..ㅋ..재밌

 

# stage 5
conn = remote('localhost', 50000)
conn.send('\xde\xad\xbe\xef')

 

6. 결론

tmp 디렉토리에서 poc 코드를 만들었기 때문에 심볼릭 링크 설정도 필요했다.

ln -s /home/input2/flag flag 필수다!

from pwn import *

context.log_level = 'debug'
# stage 1
argvs = [str(i) for i in range(100)]
argvs[ord('A')] = '\x00'
argvs[ord('B')] = '\x20\x0a\x0d'
argvs[ord('C')] = '50000'

# stage 2
with open("./stderr", "w") as f:
        f.write("\x00\x0a\x02\xff")


# stage 3
_env = {}
_env["\xde\xad\xbe\xef"] = "\xca\xfe\xba\xbe"

# stage 4
with open("./\x0a", "wb") as f:
        f.write("\x00\x00\x00\x00")


p = process(executable = '/home/input2/input', argv=argvs, stderr=open('./stderr'), env = _env)
p.recvuntil("clear!")

p.send("\x00\x0a\x00\xff")
#p.recvuntil("clear!")

# stage 5
conn = remote('localhost', 50000)
conn.send('\xde\xad\xbe\xef')

p.interactive()

'시스템 해킹 > pwnable' 카테고리의 다른 글

[pwnable.kr] passcode  (0) 2024.05.09
[pwnable.kr] dragon  (0) 2023.07.07
[pwnable.kr] Collision  (0) 2023.05.21
[pwnable.kr] fd  (0) 2023.05.21
[DreamHack] oob1  (0) 2023.05.21