Corelan Exploit Tutorial : Corelan 팀에서 작성한 문서로, 공격 코드를 작성하는 방법에 대하여 서술되어있다.(문서 제목이 공격 코드 작성하기 튜토리얼이다.) Crelan 팀은 유명한 외국의 해킹 팀이다.


해당 문서에서 배운 내용을 좀더 쉽게 풀어써보고자 이 글을 쓴다.

Wargame에서는 Linux BOF 밖에 공부할 수가 없으니, Windows BOF를 공부하기에는 이 문서가 최고인 것 같다.


필요한 프로그램 : Easy RM to MP3 Converter, Immunity Debugger, WinDbg, python 

- 접기 WinDbg : WDK(Windows Driver Kit)에 포함되어 있는 것으로, MS에서 제작한 윈도우 디버거이다.WDK를 꼭 다운받아야 하는 건 아니고, standalone으로 다운 받을 수 있다.python : 스크립트 언어 중 하나. 간단한 문법으로 금방금방 코드를 짤 수 있는 것이 특징.





Easy RM to MP3 Converter 프로그램은 오버플로우 취약점을 가지고 있다.

오버플로우를 일으킬 파일을 파이썬으로 만들어보자.


실습 환경 : VMware - Windows XP


Python IDLE을 열고, New File로 새 파일을 만들어 다음 코드를 입력하고 저장 후 실행.


f=open('exploit1.m3u','w')

buf="A"*30000

f.write(buf)

f.close()


exploit.m3u라는 파일을 열어(없으면 생성하여) 파일에 A를 50,000개 입력 후 저장하는 코드다.


이제 프로그램을 실행하고 exploit1.m3u 파일을 열어보자.

Drag&Drop으로 열 수도 있고, Load 버튼을 통해 열 수도 있다.

Drag&Drop 시에는 가끔 Load가 아니라 Purchase 버튼이 눌리기도 하니 주의.


exploit1.m3u 파일을 Load하면 아무 응답 없이 파일이 꺼지는 것을 확인할 수 있다!


프로그램에서 crash가 발생한 모습 자세한 정보를 보려면 여기를 클릭하십시오를 클릭했을 때


2번째 캡처에서 Offset: 41414141을 확인할 수 있다.

이는 EIP를 의미하는 것으로, 이 EIP가 41414141(AAAA)로 Overwrite 되었기 때문에 위와 같은 Crash가 발생한 것이다.


- 접기

이 크래쉬가 발생되는 원인을 이해하기 위해서 우리는 레지스터와 메모리에 대해 알아야한다. EIP는 레지스터 중 하나이다.

BOF가 뭐지? <-- Security(포스팅 예정)

EIP가 뭐지? <-- Computer Science - Operating System - Register(포스팅 예정)

Memory 구조 - Stack Segment <-- Computer Science(포스팅 예정)



WinDbg로 메모리 덤프를 확인하기 위해 WInDbg를 포스트모템 디버거로 설정해보자.

포스트모템 디버거 : 프로세스가 크래시crash 날 때마다 윈도우가 디버거를 이용해 덤프 파일을 생성하게 하는 메커니즘

Reference : http://www.slideshare.net/ohyecloudy/13-2247006

콘솔창을 띄우고 다음 명령어를 입력해보자.


cd C:\Program Files\Debugging Tools for Windows (x86)

windbg -i


windbg 디렉토리로 이동하여 windbg를 포스트모템 디버거로 변경하는 명령어이다.


Crash 발생 시 Windbg가 실행된다.


이제 EIP를 우리가 원하는대로 조종해보자.

그러려먼 정확히 dummy 값이 얼만큼 들어가야 하는 지 정확히 알 필요가 있다.(dummy=쓰레기 값)

그것을 위하여 해당 문서에서는 메타스플로잇Metasploit의 pattern_create.rb 툴을 사용했다.

칼리 리눅스에서 pattern_create.rb를 사용해보자.

필자는 칼리 리눅스 2를 사용했다.


- 접기

칼리 리눅스가 뭐지?

메타스플로잇이 뭐지?




/usr/share/metasploit-framework/tools/exploit/pattern_create.rb 5000


5000 바이트짜리 패턴 문자열을 생성한 것이다.

이제 XP로 돌아가서 파일에 A를 20,000개와 패턴 5000바이트를 입력하고 컨버터 프로그램에서 실행해보자.

사실 파이썬이 아니라, 텍스트 파일에 A 20000개와 패턴 문자열을 입력 후 m3u로 확장자를 변경해도 같은 파일이 생성된다.



파이썬 코드로 입력.


Crash가 발생하지 않은 모습. 프로그램 내에서 오류 처리.


A를 25,000개 입력하고 패턴 문자열을 넣어보자.



Crash 발생! 패턴 문자열 중 일부로 EIP가 Overwrite 되었다.


이제 EIP에 덮어씌워진 문자열을 메타스플로잇에 입력해보자.


./pattern_offset.rb 0x42346a42


그러면 EIP의 위치가 1062라는 것을 알 수 있다.(문서에서는 오프셋이 1072인데, 필자는 1062가 나왔다.)

즉, 우리는 dummy를 26062 bytes 입력하면 다음 4 바이트로 EIP를 마음대로 입력할 수 있다는 것이다.





eip 위치에 ssae를 주었더니 eip가 65617373으로 바뀌었다. ssae를 헥스값으로 바꿨더니 73716165가 나왔다.

즉, eip에 'eass'가 입력된 것이다. 왜 거꾸로 입력 되었을까?

그것은 리틀 엔디안 방식이기 때문이다.

리틀엔디안이 뭐지? <-- 포스팅 예정


그러므로 우리가 'ssae'를 eip에 입력하고 싶으면, 'eass'라고 입력해야함을 알 수 있다.


자, 이제 우리는 eip를 덮어씌웠으니, 또다른 레지스터인 esp를 덮어씌울 차례이다.

esp는 또 뭐여 <-- 포스팅 예정

프로그램의 흐름과 레지스터 <-- 포스팅 예정


eip가 덮어씌워지는 자리 뒤가 바로 esp의 위치임을 우리는 알고 있다.

eip 뒤에 문자열을 추가로 입력해보자.




d esp를 입력하면 esp가 가리키는 메모리 영역을 볼 수 있다.

우리는 esp를 0123456789로 입력하려는 생각이었으나, 실제로 보니 0123을 지나 4를 esp가 가리키고 있다.

이유는 무엇일까? 

- 이유 ret 명령이 수행되기 전의 스택 프레임 아래에 1개의 매개인자가 존재했던 것이다. 매개인자는 아무 값으로 덮어씌워도 상관없으므로, 더미값으로 덮어씌우면 esp에 원하는 값을 입력할 수 있다.



그리하여, 우리는 eip와 esp 사이에 4바이트의 더미를 둬야한다는 것을 알 수 있다.

문서에서 덮어씌워지는 원리에 대해 설명해놓은 것을, 필자는 다른 글에서 쓰도록 하겠다. (링크 걸어놓음)

원리를 이해하면서 넘어가보자.


eip는 프로그램 실행 흐름을 변경하기 위해 덮어씌웠다. 그런데 esp는 왜 덮어씌웠느냐?

esp에 우리가 원하는 코드를 삽입하고, eip를 통해 실행 흐름을 esp로 돌려서, esp에서 원하는 코드를 실행하기 위함이다.

고로, 우리는 esp에 입력할 페이로드가 필요하다. 이제 다시 메타스플로잇을 사용해보자.

우리는 프로그램의 흐름을 변경하여 계산기를 실행하도록 유도할 것이다.


msfvenom -a x86 --platform Windows -p windows/exec CMD=calc.exe -e x86/shikata_ga_nai -b '\x00' -i 3 -f python


무슨 명령어인지 궁금하다면 다음을 참고하자.

메타스플로잇 명령어 정리 <-- 포스팅 예정




다음과 같이 출력되면, 파이썬 코드로 옮겨서 사용하면 된다.

페이로드는 다음과 같이 얻었으니, 이제 eip에 esp의 주소를 입력하면

우리의 예상대로라면 esp에 입력되어있는 계산기를 실행하는 코드가 실행될 것이다.


우리는 windbg에서 esp의 주소를 확인할 수 있었다.(d esp)

esp의 주소는 0x000ffd38이었다.


페이로드가 사라졌다?(계산기를 실행하는)


위 캡처에서 우리는 eip를 esp의 주소로 잘 바꿨다는 것을 알 수 있다.

파이썬 코드에서는 \xff 형식을 통해 바이너리 형태로 입력하였고, 리틀엔디안에 의해 거꾸로 입력해주었다.

헌데, esp에 입력했던 계산기 실행 페이로드가 사라진 것을 확인할 수 있다. 그래서 계산기가 실행되는 것이 아니라 crash가 여전히 발생한다.

사실은, 사라진 것이 아니라, 입력이 안된 것이다. 입력이 왜 안되었느냐?

esp의 주소 중 \x00이 있었는데, 이 바이트가 NULL을 의미하여, 문자열 입력이 해당 부분에서 종료된 것이다.


테스트용 코드를 이용해 확인해보자.


ABCD 중 A 뒤에 \x00를 주었을 때, A까지만 입력이 되는 것을 확인할 수 있다.


자, 그러면 eip를 esp의 주소로 덮어씌우는 것은 불가능하다. 그럼 어떻게 해야할까?

jmp esp를 통해 eip를 esp의 주소로 덮어쓰는 것이다. jmp가 뭐지? 이 부분에 대해서는 다음 장에서 자세하게 다룬다.




우리가 실패한 방법은 위 그림에서 첫 번째 그림이고, 우리가 새롭게 시도하려는 방법은 두 번째 그림이다.

- 접기

eip를 esp로 덮어쓰는 것과 eip를 jmp esp의 주소로 덮어쓰는 것(jmp esp 명령을 수행하는 것)은 결국 같은 효과이다.

jmp esp라는 기계어를 한 번 거쳐간다는 차이 뿐이다. 그러므로 우리는 이 방법을 사용하려 한다.



이를 위해서, 우리는 JMP ESP의 주소를 알아야한다.

그러려면 JMP ESP가 어디있는 지 찾아야한다. 이것은 windbg 또는 findjmp라는 프로그램에서 가능하다.

여기서는 windbg를 통해서 찾아보겠다.


eip를 esp로 덮어쓴 파일을 실행하여 crash가 발생시킨 후 windbg가 실행되었던 지점까지 가보자.

eip는 esp와 같은 곳을 가리키고 있는 상태이어야 한다. 아니면 아래 명령어가 조금 바뀌어야 할 것이다.

그리고 다음과 같이 입력하자.


a

jmp esp

엔터

u esp


a는 어셈블 모드를 시작

jmp esp는 기계어 입력

엔터는 어셈블 모드 종료

u esp는 esp부터 언어셈블(디스어셈블)하는 명령이다.




만약, a를 입력했을 때, 현재 eip가 출력된다. 출력된 주소가 esp와 다르다면, u 다음에 esp가 아니라 해당 주소를 입력해줘야 한다.

위와 같이 jmp esp의 기계어가 ff e4라는 것을 확인할 수 있다.

그렇다면, jmp esp의 바이너리 즉 ff e4를 dll 파일에서 찾아보자.

dll이 뭐지? <-- 포스팅 예정

windbg가 실행되었을 때 나오는 dll 파일들 중

ModLoad: 019c0000 01e8d000   C:\Program Files\Easy RM to MP3 Converter\MSRMCcodec02.dll

라는 줄이 있다. 필자는 MSRMCcodec02.dll이라는 dll 파일에서 ff e4라는 기계어를 찾아볼 것이다.

다른 dll파일에서 찾아도 상관 없을 것이다.

단, 이 때 페이로드를 넣어야한다. 파일의 내용 길이에 따라 주소가 변경되기 때문이다.


s 019c0000 01e8d000 ff e4




windbg 명령어 정리 <-- 포스팅 예정


다음과 같이 ff e4들을 찾아낸 것을 알 수 있다.

(캡처는 문서상에서 나와있는 명령어 s 019c0000 l 01e8d000 ff e4를 그대로 친 것인데, 01e8d000을 넘어간 영역이 검색되어버린다.  가운데 l(L)을 빼준 s 019c0000 01e8d000 ff e4는 해당 범위만 정확히 검색이 된다. l을 빼고 사용하도록 하자.)

좌측에는 ff e4의 주소들이 있는데, esp와 마찬가지로 \x00을 포함하고 있다면 사용이 불가능하다.

필자는 01b7f23a라는 주소를 사용할 것이다.

리틀엔디안으로 하면 \x3a\xf2\xb7\x01이다.

이때, 페이로드 앞에 NOP을 적당히 넣어줘야 한다. POP/PUSH 연산에 의해 페이로드가 덮어씌워지는 것을 방지하기 위해서다.

이 부분에 대해 잘 설명된 글이 있어서 링크를 걸어둔다.

링크 글의 내용에 대해 나중에 개인적으로 포스팅 할 예정이다.

Reference : http://orang.tistory.com/2

ESP와 Payload(Shellcode) <-- 포스팅 예정



성공적으로 계산기가 실행됐다!

- 접기

사실 이 부분에서 삽질을 많이 했다. JMP ESP의 주소가 01c7f23a로 바뀌는 경우도 있었다.



여기까지가 1장의 내용이다. NULL BYTE 때문에(ESP의 주소 중 \x00) JMP ESP의 가젯을 찾아 EIP를 해당 가젯의 주소로 변조하고, NOP SLED를 사용하는 등의 고생을 했다.

워게임을 통해 리눅스 BOF를 먼저 공부하고 이 문서를 보는 게 좋을 것 같다는 생각이 든다.

그리고 쉬운 시스템 워게임은 해커스쿨 것이 많이 있고 매우 좋다.

(FTZ->LOB Redhat->이 문서 읽기 순)


아무튼 이 문서에서 얻어야할 것은 '계산기를 띄우는 방법'이 아니라 '계산기가 띄워지는 원리'이므로, 링크되어 있는 포스트들의 내용을 모두 이해하는 것이 좋을 것이다. 구글신님과 함께.(추후 링크&포스팅 예정..)


링크걸 글들을 모두 포스팅하고 2장 쉘코드로 점프를 포스팅하겠다!


WRITTEN BY
hojongs
블로그 옮겼습니다 https://hojongs.github.io/