pwnable.kr
[Toddler's Bottle]
# input2

여러가지 input을 알아보는 문제다.....
nc를 해서 cat input.c을 해보면
//input2@prowl:~$ 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; //인자 100개
if(strcmp(argv['A'],"\x00")) return 0; //인자[65] = \x00
if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0; //인자[66] = \x20\x0a\x0d
printf("Stage 1 clear!\n");
// stdio
char buf[4];
read(0, buf, 4); //stdin 4바이트를 buf에 넣음
if(memcmp(buf, "\x00\x0a\x00\xff", 4)) return 0; //buf의 4바이트 = \x00\x0a\x00\xff
read(2, buf, 4); //stderr 4바이트를 buf에 넣음
if(memcmp(buf, "\x00\x0a\x02\xff", 4)) return 0; //buf의 4바이트 = \x00\x0a\x02\xff
printf("Stage 2 clear!\n");
// env
if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0; //환경변수 \xde\xad\xbe\xef = \xca\xfe\xba\xbe
printf("Stage 3 clear!\n");
// file
FILE* fp = fopen("\x0a", "r"); //\x0a파일 열기, readonly
if(!fp) return 0;
if( fread(buf, 4, 1, fp)!=1 ) return 0; //파일의 1개 4바이트가 1이 아니어야 함
if( memcmp(buf, "\x00\x00\x00\x00", 4) ) return 0; //파일 4바이트 = \x00\x00\x00\x00
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']) ); //인자[66]을 포트로
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; //cd에서 4바이트를 읽어서 buf에 buf는 4가되면 안됨
if(memcmp(buf, "\xde\xad\xbe\xef", 4)) return 0; //buf = \xde\xad\xbe\xef
printf("Stage 5 clear!\n");
// here's your flag
system("/bin/cat flag");
return 0;
}
이렇게 나온다. 주석을 달아놓음..!
#include <stdio.h>
void main()
{
char *args[101];
int i;
for(i=0;i<100;i++) args[i]="a";
args['A']="\x00";
args['B']="\x20\x0a\x0d";
args[100]=NULL
.
.
.
.
.
stage1을 깨기위한 코드를 작성해보면 이렇게 된다. 그냥 이렇게 해버리면 된다~~
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void main()
{
char *args[101];
int i;
for(i=0;i<100;i++) { args[i]="a"; }
args['A']="\x00";
args['B']="\x20\x0a\x0d";
args[100]=NULL
int fd0[2];
int fd2[2];
pipe(fd0);
pipe(fd2);
pid_t pid;
if((pid=fork())<0) { printf("Error\n"); }
else if(pid==0) {
close(fd0[1]);
close(fd2[1]);
dup2(fd0[0],0);
dup2(fd2[0],2);
close(fd0[0]);
close(fd2[0]);
execve("/home/input2/input",args); }
else {
close(fd0[0]);
close(fd2[0]);
write(fd0[1],"\x00\x0a\x00\xff",4);
write(fd2[1],"\x00\x0a\x02\xff",4);
}
c언어에서는 pipe() 라는 함수를 사용하여 서로 독립된 프로세스들이 데이터를 주고 받을 수 있도록 할 수 있다. 하나의 파이프 및 파이프에 대한 두 개의 파일 디스크립터가 생성되고, 하나의 파이프를 프로세스들이 공유하는 것인데, 크기가 2인 int 배열이다. pipe(fd)의 리턴값이 -1이면 실패한 것이고 성공시 0을 반환한다. fd[0]에는 함수 호출 후 입력을 받는 역할을 하며, fd[1]에는 함수 호출 후 출력을 하는 역할을 한다.
따라서 stage2를 깨기 위해서는 크기가 2인 int형 배열 fd0과 fd2를 선언하고 pipe함수를 호출한다. fork()로 pid를 넘겨주어서 -1이면 에러를 출력한다.
또한 pipe함수에서 일반적으로 통신할 때, fork생성 후에 부모-자식 프로세스 관계를 이용하여 통신을 한다. pid=0이면 자식 프로세스이므로, 자식 프로세스로 /home/input2/input을 실행하여 통신을 하려고 한다.
열려진 파일의 디스크립터를 복제하는 dup와 dup2 함수 중에서, dup2 함수는 정규 파일을 표준 출력으로 사용할 때 사용된다고 한다. dup2 함수의 첫 번째 인자는 열려진 파일 디스크립터이고, 두 번째 인자는 파일 디스크립터이다. 첫 번째 인자로 열려진 파일 디스크립터가 참조하는 파일 테이블 엔트리를 두 번째 전달한 파일 디스크립터도 참조하게되므로, pu2(fd0[0], 0)을 하게되면, stdin가 fd0을 참조하게 된다. stderr도 마찬가지이다.
그리고 부모 프로세스는 현재 이 파일이므로(/tmp/amy2/input.c), \x00\x0a\x00\xff와 \x00\x0a\x02\xff를 fd0[0]과 fd2[0]로 전달한다.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void main()
{
char *args[101];
int i;
for(i=0;i<100;i++) { args[i]="a"; }
args['A']="\x00";
args['B']="\x20\x0a\x0d";
args[100]=NULL
char *env[2]={"\xde\xad\xbe\xef=\xca\xfe\xba\xbe", NULL};
int fd0[2];
int fd2[2];
pipe(fd0);
pipe(fd2);
pid_t pid;
if((pid=fork())<0) { printf("Error\n"); }
else if(pid==0) {
close(fd0[1]);
close(fd2[1]);
dup2(fd0[0],0);
dup2(fd2[0],2);
close(fd0[0]);
close(fd2[0]);
execve("/home/input2/input",args, env);
}
else {
close(fd0[0]);
close(fd2[0]);
write(fd0[1],"\x00\x0a\x00\xff",4);
write(fd2[1],"\x00\x0a\x02\xff",4);
}
stage3을 위해서는 환경 변수를 설정해야 한다. 그냥 하면 된다.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void main()
{
char *args[101];
int i;
for(i=0;i<100;i++) { args[i]="a"; }
args['A']="\x00";
args['B']="\x20\x0a\x0d";
args[100]=NULL
char *env[2]={"\xde\xad\xbe\xef=\xca\xfe\xba\xbe", NULL};
int fd0[2];
int fd2[2];
pipe(fd0);
pipe(fd2);
pid_t pid;
FILE *fp= fopen("\x0a", "ab+");
fwrite("\x00\x00\x00\x00", 4, 1, fp);
fclose(fp);
if((pid=fork())<0) { printf("Error\n"); }
else if(pid==0) {
close(fd0[1]);
close(fd2[1]);
dup2(fd0[0],0);
dup2(fd2[0],2);
close(fd0[0]);
close(fd2[0]);
execve("/home/input2/input",args, env);
}
else {
close(fd0[0]);
close(fd2[0]);
write(fd0[1],"\x00\x0a\x00\xff",4);
write(fd2[1],"\x00\x0a\x02\xff",4);
}
stage4를 깨기 위해서는 파일을 하나 열어야 한다. fopen("파일명", "ab+")는 파일을 읽고 쓰기위해 개방할 때, 파일이 존재해도 파일 끝에서부터 데이터를 추가하여 binary mode로 한다는 옵션이다. 이 파일에 \x00\x00\x00\x00을 쓰면 된다.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
void main()
{
char *args[101];
int i;
for(i=0;i<100;i++) { args[i]="a"; }
args['A']="\x00";
args['B']="\x20\x0a\x0d";
args['C']="8888"
args[100]=NULL
char *env[2]={"\xde\xad\xbe\xef=\xca\xfe\xba\xbe", NULL};
int fd0[2];
int fd2[2];
pipe(fd0);
pipe(fd2);
pid_t pid;
FILE *fp= fopen("\x0a", "ab+");
fwrite("\x00\x00\x00\x00", 4, 1, fp);
fclose(fp);
if((pid=fork())<0) { printf("Error\n"); }
else if(pid==0) {
close(fd0[1]);
close(fd2[1]);
dup2(fd0[0],0);
dup2(fd2[0],2);
close(fd0[0]);
close(fd2[0]);
execve("/home/input2/input",args, env);
}
else {
close(fd0[0]);
close(fd2[0]);
write(fd0[1],"\x00\x0a\x00\xff",4);
write(fd2[1],"\x00\x0a\x02\xff",4);
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
portno = atoi(args['C']);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
server = gethostbyname("127.0.0.1");
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
serv_addr.sin_port = htons(portno);
sleep(5);
connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr));
write(sockfd,"\xde\xad\xbe\xef",4);
close(sockfd);
}
stage5를 깨기 위해서는 소켓을 해야 한다. 대충 포트는 8888으로 잡고, 연결 시킨 후에 \xde\xad\xbe\xef를 써준다.
이제 실행을 해보자!

템프 디렉토리를 만들어주고,

지금까지 완성시킨 코드를 쓰고,

컴파일을 했다!

clear는 되는데 flag가 안보인다....?
// here's your flag
system("/bin/cat flag");
return 0;
사실 /home/input2/input.c의 마지막 부분 코드의 /bin/cat flag의 flag는 상대경로이므로, 현재 디렉토리(/tmp/amy2)에서는 flag가 존재하지 않는다ㅠㅠ..
심볼릭 링크를 써보자!

ㅎㅎ

쨔안
'Wargame > pwnable.kr' 카테고리의 다른 글
[pwnable.kr] TB #memcpy (0) | 2019.08.05 |
---|---|
[pwnable.kr] TB #leg (0) | 2019.08.05 |
[pwnable.kr] TB #blackjack (0) | 2019.08.03 |
[pwnable.kr] TB #passcode (0) | 2019.08.03 |
[pwnable.kr] TB #flag (0) | 2019.08.02 |