소스코드
#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;
}
코드를 살펴 보면 다 입력값을 받는 것이다.
첫 번째 조건
// 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']
즉, argv[65]
값이 \x00
이여야 한다.
argv['B']
는 argv[66]
이고 값이 \x20\x0a\x0d
여야한다.
두 번째 조건
// 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");
buf 함수에 입력을 받는데, 입력 받는 값이 \x00\x0a\x00\xff
여야 한다.
그리고 stderr를 통해 입력을 받는데, stderr는 \x00\x0a\x02\xff
여야 한다.
stdin = 0
stdout = 1
stderr = 2
세 번째 조건
// env
if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
printf("Stage 3 clear!\n");
환경변수 이름이 \xde\xad\xbe\xef
인 변수안에 \xca\xfe\xba\xbe
값이 존재해야 한다.
네 번째 조건
// 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
인 파일을 읽고, 그 파일의 데이터에 \x00\x00\x00\x00
값인지 확인한다.
파일도 있어야하고, 데이터도 존재해야 한다.
마지막 조건
// 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");
이번에는 서버를 열고, 서버에 접속을 기다리고, 접속을 하면 사용자로 부터 데이터를 받는다.
이때 입력 값이 \xde\xadxbe\xef
인지 확인한다.
서버를 열때 포트번호를 argv['C']의 값으로 사용한다.
이렇게 조건들을 다 만족하면 flag 값을 출력해 성공하게 된다.
이제 이 조건들을 하나하나 만족해주는 코드를 작성하자.
from pwn import *
target = ['/home/input2/input']
for i in range(1, 100):
if i == ord('A'):
target.append('\x00')
elif i == ord('B'):
target.append('\x20\x0a\x0d')
elif i == ord('C'):
target.append('12346')
else:
target.append('data')
env_data = {'\xde\xad\xbe\xef': '\xca\xfe\xba\xbe'}
with open('./stderr', 'w') as fp:
fp.write('\x00\x0a\x02\xff')
p = process(target, env=env_data, stderr=open('./stderr'))
print p.recvline()
print p.recvline()
print p.recvline()
print p.recvuntil('Stage 1 clear!\n')
p.send('\x00\x0a\x00\xff')
print p.recvuntil('Stage 2 clear!\n')
print p.recvuntil('Stage 3 clear!\n')
fp = open('./\x0a', 'w')
fp.write('\x00' * 4)
fp.close()
print p.recvuntil('Stage 4 clear!\n')
sleep(2)
conn = remote("127.0.0.1", 12346)
conn.send('\xde\xad\xbe\xef')
print p.recvuntil('Stage 5 clear!\n')
p.interactive()
이 부분은 문제 서버에 접속해 풀어야한다. 서버에 접속하면 input2 홈 경로에는 쓰기 권한이 존재하지 않는다. 그렇다고 원격에서 해도 뭐 의미가 없다.
서버에 /tmp 경로가 쓰기권한이 존재한다.
이 부분에 파일을 만들어 해당 코드를 작성해서 풀면된다.
경로가 /tmp 안에 있어서 flag 파일이 존재하지 않아 마지막 cat flag하는 명령어가 안된다.
되게 하기위해 심볼릭 링크를 설정해 파일을 출력할 수 있게 하면된다.
'WarGame > pwnable.kr' 카테고리의 다른 글
leg (0) | 2021.01.05 |
---|---|
coin1 (0) | 2020.12.23 |
mistake (0) | 2020.12.02 |
shellshock (0) | 2020.12.02 |