제 개인사이트에 작성한 문서라 문서 말투가 일기체입니다.
모두 수정할 수 없기에 이해해주시고 봐주시면 감사하겠습니다
-------------------------------------------------------------------------------------------
SmashTheStack 워게임은 제목을 봐서는 Stack 을 공격하는 스택 버퍼 오버플로우와 관련이 많다고 생각하겠지만
여러 분류로 나뉘어져 있는것 같다. 내가 풀고있는 IO 이 서버에서는 FTZ 초기레벨과 같이 기본 해킹 문제를
다루고 있는것 같다.
나의 숙적, 버퍼 오버플로우와 대면할때는 달라져 있겠지.
이미 문제를 풀어 팀에서는 과제로 제출했지만 좀 더 디테일하게 분석을 해 올릴 생각이며,
내가 민망하게끔 기초스러운것이라도 알게된게 있다면 기재할 생각이다. 그래서 아마 하나의 문서를 작성할때
오랜 시간이 걸릴것으로 예정되며 또한 이 문서의 길이도 엄청나게 길어질 것이라 생각된다.
SSH - level1@io.smashthestack.org -p2224
처음 서버를 접속한 자라면 먼저 영문으로 작성된 긴 가이드를 읽을 수 있다.
문제를 푸는 방법에 대해 나와있으며 요약하자면
level1@io:/levels$ cd /levels
// 문제폴더
level1@io:/levels$ ls -al
// 문제확인
level1@io:/levels$ level01
// 문제확인
level1@io:/levels$ cat /home/level2/.pass
// 권한 획득 시 키값 얻기 (쉘 권한에서만 가능)
level1@io:/levels$ ./level01
Usage: ./level01 <password>
level1@io:/levels$ ./level01 aaaa
Fail.
프로그램을 실행하면 패스워드를 입력하라는 메세지가 나오고 아무런 문자나 입력하면 Fail. 이라는 문자열이 뜬다.
소스는 당연히 없을터이니 GDB 를 이용해 프로그램을 분석해서 문제를 풀라는 말이었다.
문제를 푸는 방법은 쉽게 다가가기 -> 어렵게 다가가기 이다.
먼저 트릭을 사용해 보았다. 리눅스에서는 strings 명령어를 통해 바이너리 파일의 문자열을 확인할 수 없다.
대게 vi, cat 를 사용할 수 없을 경우 많이 사용된다.
level1@io:/levels$ strings level01
/lib/ld-linux.so.2
__gmon_start__
libc.so.6
printf
execl
puts
strncmp
_IO_stdin_used
__libc_start_main
GLIBC_2.0
PTRh
0Y_]
[^_]
[^_]
omgpassword
Usage: %s <password>
Win.
/bin/sh
Fail.
답은 omgpassword 이다.
level1@io:/levels$ ./level01 omgpassword
Win.
sh-3.2$ id
uid=1001(level1) gid=1001(level1) euid=1002(level2) groups=1001(level1),1029(nosu)
sh-3.2$ cat /home/level2/.pass
WE5aVWRwYPhX
쉘을 획득했지만 얻는게 있어야 재미도 있기에 GDB 로 문제를 열고 분석해보겠다.
level1@io:/levels$ gdb -q level01
(gdb) disas main
Dump of assembler code for function main:
0x080483f4 <main+0>: lea 0x4(%esp),%ecx // ESP+4 => argc 주소값을 ECX 에 복사 - 0xbfffdd10 주소가 들어가있음
0x080483f8 <main+4>: and $0xfffffff0,%esp
0x080483fb <main+7>: pushl -0x4(%ecx)
0x080483fe <main+10>: push %ebp
0x080483ff <main+11>: mov %esp,%ebp // 프롤로그
0x08048401 <main+13>: push %edi
0x08048402 <main+14>: push %ecx // EDI, ECX 차례대로 푸시
/*
EDI 는 ESI 레지스터가 가리키는 주소의 데이터가 복사되며
ECX 는 반복 명령어 사용시 반복 카운트로 사용 됨
*/
0x08048403 <main+15>: sub $0x30,%esp // 0x30 => 48 바이트 변수 선언, 스택의 길이 0x30
0x08048406 <main+18>: mov %ecx,-0x20(%ebp)
// (EBP-32) 에 ECX 의 값을 복사 - (EBP-32) 에도 argc 주소 0xbfffdd10 가 들어가있음.
0x08048409 <main+21>: movl $0x80485c8,-0xc(%ebp) // (EBP-12) 에 0x80485c8 주소값을 복사
/*
(gdb) x/s 0x80485c8
0x80485c8: "omgpassword"
정답이 (EBP-12) 안에 들어가있다. 과연 프로그램이
어떻게 갖고노는지 아직 알 수 없으니, 찬찬히 알아가보자.
*/
0x08048410 <main+28>: mov -0x20(%ebp),%eax // EAX 에 (EBP-32) 의 값을 복사 => 0xbfffdd10 들어감
0x08048413 <main+31>: cmpl $0x2,(%eax) // EAX 와 2 와 비교한다.
0x08048416 <main+34>: je 0x8048439 <main+69>
// 참이면 0x8048439 이 곳으로 점프 - 아마도 인자의 개수를 비교하는것같다.
/*
그래서 무조건 ./level01 argv[1] 이렇게 입력해주지 않으면 오류가 뜨는것이라고 추측 가능하다.
참이 아니라면 밑에서 계속 된다.
*/
0x08048418 <main+36>: mov -0x20(%ebp),%edx // EDX 에 (EBP-32) 값 복사 - EDX 에는 0xbfffdd10 들어감
0x0804841b <main+39>: mov 0x4(%edx),%eax // EAX 에 (EDX+4) 값 복사 - argv[0] 의 주소 복사
0x0804841e <main+42>: mov (%eax),%eax // EAX 에 EAX 복사 - 별 의미없다.
0x08048420 <main+44>: mov %eax,0x4(%esp) // (ESP+4) 에 EAX 값 복사 - argv[0] 의 주소 복사
0x08048424 <main+48>: movl $0x80485d4,(%esp) // ESP 에 0x80485d4 값 복사 - 0x80485d4: "Usage: %s <password>\n" 출력
0x0804842b <main+55>: call 0x804832c <printf@plt> // printf 함수 호출
0x08048430 <main+60>: movl $0x1,-0x1c(%ebp) // (EBP-28) 에 1 복사
0x08048437 <main+67>: jmp 0x80484b2 <main+190> // 0x80484b2 이곳으로 점프~
0x08048439 <main+69>: mov -0xc(%ebp),%eax
// EAX 에 (EBP-12) 값 복사 - (EBP-12) 는 정답이 들어가있으므로 EAX 에 정답이 들어감
0x0804843c <main+72>: mov $0xffffffff,%ecx // ECX 에 -1 복사 - NOT 으로 플러스로 변하기때문에 카운터용으로
0x08048441 <main+77>: mov %eax,-0x24(%ebp) // (EBP-36) 에 EAX 값 복사 - (EBP-36) 에 정답 복사
0x08048444 <main+80>: mov $0x0,%al // AL 에 0 복사
0x08048446 <main+82>: cld
0x08048447 <main+83>: mov -0x24(%ebp),%edi
// EDI 에 (EBP-36) 값 복사 - 이제 EDI 에는 EAX 가 들어가있음 - EDI 에 정답이 들어가있는거나 마찬가지
0x0804844a <main+86>: repnz scas %es:(%edi),%al
0x0804844c <main+88>: mov %ecx,%eax // EAX 에 ECX 복사
0x0804844e <main+90>: not %eax // -1 을 부호바꿔서 +1 로 - 12
0x08048450 <main+92>: lea -0x1(%eax),%edx // EDX 에 (EAX-1) 주소값 복사
0x08048453 <main+95>: mov -0x20(%ebp),%ecx
// ECX 에 (EBP-32) 값 복사 - ECX 에 argc 주소 0xbfffdd10 가 들어가있음.
0x08048456 <main+98>: mov 0x4(%ecx),%eax // EAX 에 (ECX+4) 값 복사 - EAX 에 argv[0] 들어감
0x08048459 <main+101>: add $0x4,%eax // EAX + 4 - argv[1] 들어감
0x0804845c <main+104>: mov (%eax),%ecx // ECX 에 EAX 값 복사 - ECX 에 argv[1] 들어감
0x0804845e <main+106>: mov %edx,0x8(%esp) // (ESP+8) 에 EDX 값 복사 - 세번째 인자 전달
0x08048462 <main+110>: mov -0xc(%ebp),%eax // EAX 에 (EBP-12) 복사 - EAX 에 정답 omgpassword 들어감
0x08048465 <main+113>: mov %eax,0x4(%esp)
// (ESP+4) 에 EAX 값 복사 - 두번째 인자 전달 - 바로 위의 과정덕에 정답이 나오게됨
0x08048469 <main+117>: mov %ecx,(%esp) // ESP 에 ECX 값 복사 - 첫번째 인자 argv[1] 들어감
/*
ESP 를 참조한다는것에 생각을 하면
%edx,0x8(%esp) -> %eax,0x4(%esp) -> %ecx,(%esp) 이 것은
세 개의 인자를 넣는것을 알 수 있다. 원래는 차례대로 나와야 되지만
얼핏 들어본바로는 인텔에서는 함수 호출시 인자가 거꾸로 전달된다고 한다는것을 들어본것 같다.
주소값이 4바이트 이니깐 4바이트씩 증가하고 있다.
*/
0x0804846c <main+120>: call 0x804830c <strncmp@plt> // 아마도 이 함수에 세 개의 인자가 전달되어서 이 함수에서 비교하나 보다
// strncmp 는 문자열 비교 함수
/*
(gdb) x/s $edx
0xb: <Address 0xb out of bounds>
(gdb) x/s $eax
0x80485c8: "omgpassword"
(gdb) x/s $ecx
0xbfffdea1: "aaaaa"
중요한 부분이기에 확인해보았더니 eax 에서 정답이 나왔다.
0x08048471 <main+125>: test %eax,%eax // EAX 가 0 인지 확인
0x08048473 <main+127>: jne 0x804849f <main+171> // 0x804849f 로 점-점-점-ㅍ- ㅡ
0x08048475 <main+129>: movl $0x80485ea,(%esp) // ESP 에 0x80485ea 값 복사 - 0x80485ea: "Win."
0x0804847c <main+136>: call 0x80482fc <puts@plt> // Puts 함수 호출
0x08048481 <main+141>: movl $0x0,0x8(%esp) // (ESP+8) 에 0 복사
0x08048489 <main+149>: movl $0x80485ef,0x4(%esp) // (ESP+4) 에 0x80485ef 값 복사
0x08048491 <main+157>: movl $0x80485f2,(%esp)
0x08048498 <main+164>: call 0x80482ec <execl@plt> // 쉘 실행~~
0x0804849d <main+169>: jmp 0x80484ab <main+183> // 종료하는곳으로 점프~
0x0804849f <main+171>: movl $0x80485fa,(%esp)
0x080484a6 <main+178>: call 0x80482fc <puts@plt> // omgpassword 입력을 못해서 틀렸을경우 Fail. 출력
0x080484ab <main+183>: movl $0x0,-0x1c(%ebp) // (EBP-1) 에 0 복사
0x080484b2 <main+190>: mov -0x1c(%ebp),%eax // EAX 에 (EBP-28) 값 복사
0x080484b5 <main+193>: add $0x30,%esp // ESP+30 스택 공간 초기화
0x080484b8 <main+196>: pop %ecx
0x080484b9 <main+197>: pop %edi
0x080484ba <main+198>: pop %ebp
0x080484bb <main+199>: lea -0x4(%ecx),%esp
0x080484be <main+202>: ret // 에필로그 끝!!
End of assembler dump.
이로서 분석이 끝났다. 내가 이렇게 직접 GDB 로 문제를 분석해보는건 오랜만에 처음이라 힘든 작업이었으나
나의 발전을 위한 과정이라 생각해 이렇게 분석을 100% 하고나니 뿌듯하게 느껴진다.
왜 프로그램을 실행하면 저렇게 나오는지, 왜 정답이 나오는지 이제 100% 알겠다.
앞으로 문제풀때 확실히 이 경험이 도움이 될거라 나는 믿는다. 첫 번째 복습차로 이렇게 복습을 했지만
앞으로도 계속 복습을 해 나가면서 발전해가리라 믿는다.
'스터디 > wargames' 카테고리의 다른 글
SmashTheStack : level3@io.smashthestack 문제풀이 (0) | 2011.07.21 |
---|---|
SmashTheStack : level2@io.smashthestack 문제풀이 (0) | 2011.07.20 |
[웹해킹] 와우해커(WowHacker) 문제풀이 - Level3 (2) | 2010.10.06 |
[웹해킹] 와우해커(Wowhacker) 문제풀이 - Level2 (0) | 2010.10.03 |
[웹해킹] 와우해커(Wowhacker) 문제풀이 - Level1 (0) | 2010.10.02 |