Yozzang의 해킹일기 💻
article thumbnail
Published 2022. 7. 5. 00:22
Pwnable (7. input) Wargame/Pwnable
728x90

이번 포스트에서는 Pwnable의 input문제에 대해 다루겠다.

 


문제 코드는 다음과 같다.

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

## 문제 해석 :

해당 문제의 코드가 상당히 길다. 대충 살펴 보니 총 5개의 스테이지가 있고 스테이지를 다 클리어하면 flag를 얻을 수 있는 것으로 보인다.

## 문제 풀이 :

먼저 우리의 공격 코드를 만들어야 하는데, /tmp 폴더에서만 실행 권한이 있으므로 /tmp 폴더에 들어가서 우리만의 폴더를 하나 만들어 보겠다.

공격 폴더 작성

코드 폴더를 만든 다음에 각 스테이지에 대해 풀이해보겠다.

# Stage 1

// 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'] = "\x00", argv['B'] = "\x20\x0a\x0d"이면 해당 스테이지를 클리어할 수 있는 것으로 보인다.

 

## 코드 : 

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

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

파일 스크립트 0 = stdin, 2 = stderr이므로 stdin으로 "\x00\x0a\x00\xff" 입력받고, stderr로 "\x00\x0a\x02\xff" 입력받으면 된다.

 

## 코드 : 

# stage 2
with open('./stderr', 'a') as f:
	f.write('\x00\x0a\x02\xff')
    
target = process(executable = '/home/input2/input', argv=argvs, stderr=open('./stderr'), env=envVal)
target.sendline('\x00\x0a\x00\xff')

 


# Stage 3

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

getenv() 함수는 환경 변수 검색 함수이다. 따라서 환경변수 "\xde\xad\xbe\xef"에 "\xca\xfe\xba\xbe"를 넣으면 된다.


## 코드 : 

# stage 3
env_val = {'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'}

# 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" 파일에 가져온 첫 4바이트의 값이 "\x00\x00\x00\x00"이면 된다.

 

## 코드 : 

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

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

스테이지 5는 소켓에 관한 코드이다. "saddr.sin_port = htons( atoi(argv['C']) );"를 확인해보면 포트번호를 'C'로 해서 서버를 열고, "\xde\xad\xbe\xef"를 보내주면 된다.

 

## 코드 : 

# stage 5
argvs[ord('C')] = '40000'

conn = remote('localhost','40000')
conn.send('\xde\xad\xbe\xef')

 

그러나 현재 우리의 코드 폴더에 flag가 없으므로 flag에 대한 심볼릭을 생성해보겠다.

flag 심볼릭 생성

## 최종 결과 : 

### 공격코드 : 

from pwn import *

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

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

# stage 3
env_val = {'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'}

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

# stage 5
argvs[ord('C')] = '40000'

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

target.sendline('\x00\x0a\x00\xff')

conn = remote('localhost','40000')
conn.send('\xde\xad\xbe\xef')
target.interactive()

### 실행 결과 :

공격 실행

Flag = Mommy! I learned how to pass various input in Linux :)

'Wargame > Pwnable' 카테고리의 다른 글

Pwnable (9. mistake)  (0) 2022.07.07
Pwnable (8. leg)  (0) 2022.07.06
Pwnable (6. random)  (0) 2022.07.04
Pwnable (5. passcode)  (0) 2022.07.03
Pwnable (4. flag)  (0) 2022.07.02
profile

Yozzang의 해킹일기 💻

@요짱

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!