이번에는 input 문제를 풀어보았다.
그냥 컴퓨터 프로그램에 input을 어떻게 전달할 수 있냐고 물어보고 있다.
별 의미는 없는 것 같다.
아래에 있는 명령어를 통해 접속해준다.
접속 완료했다.
파일 리스트는 이 정도 있다는 것을 확인한 뒤 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;
}
코드를 보니 단계별로 진행하는 것 같아 단계별로 쪼개보았다.
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");
명령인수에 대한 내용이다.
요약하자면, 인자의 개수가 100개이고, 65번째 인자가 "\x00", 66번째 인자가 "\x20\x0a\x0d" 이면 된다.
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");
표준 입출력에 대한 내용이다.
stdin의 4btye에 "\x00\x0a\x00\xff", stderr의 4btye에 "\x00\x0a\x02\xff"를 줘야한다.
stdin은 send로 보내도 되는데, stderr은 파일을 따로 만들어줘야한다.
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"와 같아야 한다.
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"라는 파일을 여는데, 이 파일의 처음 4byte가 "\x00\x00\x00\x00"이면 된다.
이것도 파일을 따로 만들어줘야한다.
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");
네트워크에 대한 내용이다.
요약하자면, argv[67]에 해당하는 포트를 열고, 여기서 4btyet를 받아, 이 값이 "\xde\xad\xbe\xef"가 되면 된다.
위의 내용들을 토대로 익스플로잇 코드를 작성하면 아래와 같다.
from pwn import *
# stage 1
argvs = [str(i) for i in range(100)]
argvs[65] = "\x00"
argvs[66] = "\x20\x0a\x0d"
# stage 2
with open("./stderr", "a") as f:
f.write("\x00\x0a\x02\xff")
# stage 3
envVal = {"\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[67] = "11111"
p = process(executable = "/home/input2/input", argv=argvs, stderr=open("./stderr"), env=envVal)
p.sendline("\x00\x0a\x00\xff")
local = remote("localhost","11111")
local.send("\xde\xad\xbe\xef")
p.interactive()
이 익스플로잇 코드를 아무데나 두면 안되고
tmp 디렉터리 아래에 아무 폴더나 만든 뒤에 거기에 익스플로잇 파일을 생성해줘야 한다.
생성한 뒤 그냥 실행하면 flag와 연결이 안되서 제대로 작동하지 않기 때문에 심볼릭 링크를 걸어줘야 한다.
이렇게 걸어준다.
작성한 익스플로잇코드를 실행시키면
성공적으로 flag를 획득할 수 있다.
획득한 flag를 입력해야하는데, 현재 pwnable.kr 페이지에 접속이 불가능해서 나중에 입력하고 수정하겠다.
'Security > pwnable.kr' 카테고리의 다른 글
[pwnable.kr] Toddler's Bottle - cmd1 (0) | 2022.05.07 |
---|---|
[pwnable.kr] Toddler's Bottle - bof (0) | 2022.05.07 |
[pwnable.kr] Toddler's Bottle - blackjack (0) | 2021.12.05 |
[pwnable.kr] Toddler's Bottle - lotto (0) | 2021.11.28 |
[pwnable.kr] Toddler's Bottle - mistake (0) | 2021.11.21 |
댓글