문제에 접속하여 flag 파일을 읽으려하면 권한 문제로 읽히지 않는다.

input파일을 실행해보면 다음과 같이 출력이 나온다.

또한 input.c 소스코드를 보면 다음과 같다.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69
#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; // 인자가 100이 아니면

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;

// port 정보

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;

}

Colored by Color Scripter
cs

 

//argv로 주석이 되어 있는 부분의 코드를 보면 인자의 크기가 100이 아니면, 인자의 'A'인덱스 즉 65번째 인덱스가 "\x00"가 아니고, 인자의 'B' 인덱스 즉 66번째 인덱스가 "\x20\x0a\x0d"가 아니면 프로그램을 종료하고, 위 조건들을 만족시키는 경우 Stage 1 clear을 출력한다.

 

//stdio로 주석이 되어 있는 부분의 코드를 보면 read 함수로 buf를 읽어오는데, 0(stdin)이 "\x00\x0a\x00\xff"이 아닐 경우, 2(stderr)이 "\x00\x0a\x02\xff"가 아닐 경우 조건을 프로그램을 종료하고, 조건들을 만족시킬 경우 Stage 2 clear을 출력한다.

 

//env로 주석이 되어 있는 부분의 코드를 보면 getenv()함수로 환경변수 "\xde\xad\xbe\xef"의 값이 "\xca\xfe\xba\xbe"가 아닐 경우 프로그램을 종료하고, 조건을 만족하면 Stage 3 clear을 출력한다.

 

//file로 주석이 되어 있는 부분의 코드를 보면 fopen()함수로 "\x0a"라는 이름의 파일을 열고 파일을 읽어 파일의 내용이 "\x00\x00\x00\x00"가 아니면 프로그램을 종료하고, 조건을 만족하면 Stage 4 clear을 출력한다.

 

//network로 주석이 되어 있는 부분의 코드를 보면 sockaddr_in 구조체를 사용하고 있고, 소켓 생성에 실패(socket 함수 -1 반환)하면 socket error을 출력하고 프로그램을 종료한다. saddr의 포트 정보에 인자의 'C' 인덱스 즉 67번째 인덱스의 값을 넣고, bind 함수로 소켓 통신할 준비를 해준다. 이 과정에서 에러가 발생할 경우 bind error use another port를 출력하고 프로그램을 종료한다. accept()함수로 소켓을 연결하고, 에러가 발생할 경우(return -1) accept error tell admin을 출력하고 프로그램을 종료한다. 이후 소켓에서 4바이트를 받아와서 받아온 값이 "\xde\xad\xbe\xef"가 맞다면 Stage 5 clear을 출력한다.

 

위 5가지의 조건을 모두 만족시키면 플래그를 읽을 수 있다.

 

문제를 해결하기 위해 pwntools를 이용하여 파이썬 코드로 작성하였다.

첫번째 조건을 만족시키기 위해 인자값을 조건에서 주어진 대로 설정하고, 인자를 전달하려면 process 함수의 argv 옵션을 이용하여 전달할 수 있다.

 

두 번째 조건을 만족시키기 위해 stdin으로는 "\x00\x0a\x00\xff"를 전달하였고, stderr일 경우 전달하는 파일을 만들어 "\x00\x0a\x02\xff"을 저장하여 process함수의 stderr옵션을 이용하여 전달하였다.

 

세 번째 조건을 만족시키기 위해 process함수의 env 옵션을 이용하여 환경변수를 설정하였다.

 

네 번째 조건을 만족시키기 위해 조건에 주어진 이름의 파일을 만들고, 파일 내용도 조건에 주어진 대로 지정해 주었다.

 

다섯 번째 조건을 만족시키기 위해 인자의 67번째('C')인덱스에 포트를 지정하고 지정한 포트로 nc 연결을 하여 조건에서 주어진 값을 전달하였다.

따라서 코드로 작성하면 다음과 같다.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22
from pwn import \\\*



argvs = [str(i) for i in range(100)]

argvs[65] = '\x00'

argvs[66] = '\x20\x0a\x0d'

argvs[67] = '3000'



p = process(executable='/home/input2/input', argv=argvs, stderr=open('./stderr'), env={'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'})



pay = '\x00\x0a\x00\xff'

with open('./stderr', 'w') as f:

f.write('\x00\x0a\x02\xff')



with open('\x0a', 'w') as f:

f.write('\x00\x00\x00\x00')



p.sendline(pay)



r = remote('localhost',3000)

r.send('\xde\xad\xbe\xef')



p.interactive()

Colored by Color Scripter
cs

또한 flag파일은 현재 경로에 없기 때문에 ln \\-s /home/input2/flag flag 명령으로 현재 경로에 심볼릭 링크를 걸어준 후 파이썬 코드를 실행하면 플래그를 얻을 수 있다.

'Security & Hacking > Wargame' 카테고리의 다른 글

[HackCTF] RTL_World  (0) 2021.07.24
[pwnable.kr] shellshock  (0) 2021.06.16
[HackCTF] BOF_PIE  (0) 2021.06.01
[LoS] golem  (0) 2021.05.19
[HackCTF] Offset  (0) 2021.05.13

+ Recent posts