2013.07.25 10:41 [Exploit]

<BufferOver Flow 소개>


버퍼 오버플로(buffer overflow) 또는 버퍼 오버런(buffer overrun)은 메모리를 다루는 데에 오류가 발생하여 잘못된 동작을 하는 프로그램 취약점이다. 컴퓨터 보안 프로그래밍에서  프로세스 데이터 버퍼에 저장할 때 프로그래머가 지정한 곳 바깥에 저장하는 것이다. 벗어난 데이터는 인접 메모리를 덮어 쓰게 되는데 다른 데이터가 포함되어 있을 수도 있는데, 손상을 받을 수 있는 데이터는 프로그램 변수와 프로그램 흐름 제어 데이터도 포함된다. 이로 인해 잘못된 프로그램 거동이 나타날 수 있으며, 메모리 접근 오류, 잘못된 결과, 프로그램 종료, 또는 시스템 보안 누설이 발생할 수 있다.

버퍼 오버플로가 코드를 실행시키도록 설계되거나 프로그램 작동을 변경시키도록 설계된 입력에 의해 촉발될 수 있다. 따라서 이는 많은 소프트웨어 취약점의 근간이 되며 악의적으로 이용될 수 있다. 경계 검사로 버퍼 오버플로를 방지할 수 있다.

버퍼 오버플로는 보통 데이터를 저장하는 과정에서 그 데이터를 저장할 메모리 위치가 유효한지를 검사하지 않아 발생한다. 이러한 경우 데이터가 담긴 위치 근처에 있는 값이 손상되고 그 손상이 프로그램 실행에 영향을 미칠 수도 있다. 특히, 악의적인 공격으로 인해 프로그램에 취약점이 발생할 수도 있다.

흔히 버퍼 오버플로와 관련되는 프로그래밍 언어는 C와 C++로, 어떤 영역의 메모리에서도 내장된 데이터 접근 또는 덮어쓰기 보호 기능을 제공하지 않으며 어떤 배열에 기록되는 데이터가 그 배열의 범위 안에 포함되는지 자동으로 검사하지 않는다.

간단히 요약하면 프로그램내의 한정되어있는 버퍼공간을 초과하게 되면 프로그램은 비정상 종료가 됩니다. 비정상 종료가 될때 EIP레지스터(다음실행할 명령주소 값을 나타냄)는 리턴 주소를 가르키게 되는데 이때 EIP 레지스터값까지 공격자가 원하는 값으로 변경하여 조작할 수 있다면 버퍼 오버플로우 취약점이 있다고 볼 수 있습니다.


<취약점 확인>

RM MP3 DOWN : http://www.rm-to-mp3.net/download.html

OS :  Windows XP Service Pack3

사용된 도구 및 언어 : Windbg, ,OllyDbg, C언어, Perl, 자작개발프로그램 등


[그림.1]


취약점이 존재하는 3Easy RM to MP3 Converter를 다운받습니다.

그리고 Load버튼으로 m3u 확장자의 파일을 불러오는 기능을 합니다. 

이 어플리케이션에서 파일의 용량이 몇만바이트 이상이면 CRASH가 발생하게 되는데요 하나씩 살펴 보겠습니다.


[그림.2]


보통 Perl이나 Metasploit에서 지원하는 패턴생성하는 스크립트를 사용하면 쉽게 패턴을 만들수 있습니다.

하지만 저의 테스트 환경의 컴퓨터 상에서는 Metasploit이 없다는점, 그리고 Metasploit을 구동하기위한 운영체제 linux가 깔려있지 않다는점 이걸 다 설치하고 하려면 엄청난 시간낭비가 예상이 되어 그냥 패턴생성 하는거 만들어보자 해서 만들었는데 개인적으로 만족합니다. 

만든프로그램은 그림.2와 같이 "16진수를 영문으로 변환", "영문을 16진수로 변환", "ABC패턴 생성기", "BOF_Aa1패턴생성기", "그리고 offset 계산기" 등의 프로그램입니다.

최종적으로 사용되는 프로그램은 "ABC패턴 생성기", "BOF_Aa1패턴생성기", "그리고 offset 계산기" 3개인데요 하나씩 설명드리겠습니다.

우선 ABC패턴생성기로 a문자 1000개를 aaaa.m3u파일로 생성합니다.


[그림.3]


그리고 파일을 Load하면 로딩에 실패했다고 나오면서 입력한 그대로 파일에 읽힙니다. 

뭔가 의심스러운 행동을 하지만 아직 CRASH가 발생하지 않았습니다.


[그림.4]


windbg.exe -I 명령을 내려놓으면 프로그램이 실행하다 오류가 발생하면 오류가 발생된 지점부터 디버깅을 할 수있게 실행이 됩니다.

[그림.5]


ABC패턴 생성기로 abc 각각 1만개씩 생성합니다.


[그림.6]


그리고 다시 3만개의 문자열이 들어있는 aaaa.m3u파일을 Load합니다.

Load하는순간 windbg가 실행이 되고 EIP레지스터(다음실행할명령의 주소를 가르킴)가 63636363으로 채워집니다. 63은 16진수이며 아스키코드로 "c" 입니다.

그리고 ESP레지스터도 확인 한 결과 전부 "c"로 채워져 있습니다.


[그림.7]


저 EIP가 가르키는 곳의 위치를 찾기위해 Aa0등과 같은 패턴을 생성해서 확인을 해야합니다.

그림.7 과 같이 Metasploit Framework에서 패턴 스크립트를 지원을 해줍니다. 

저 스크립트를 사용하면 좋지만 저는 저만의 도구로 사용하기로 합니다.


[그림.8]


만들때 급하게 만드느라 저는 시작패턴이 Aa1부터시작되게 했습니다. 그리고 9다음 Aa0가 되게끔 구현을 했구요.

저 5000개의 패턴중에 잘못 구현된 부분이 있는데 그 부분까지도 위치로 파악이 됩니다. 그리고 지금 목적은 BOF를 구현하는것이지 개발이 목적이 아니므로 수정하지 않고 그냥 사용합니다.

이렇게 생성한 5000개의 패턴을 그림.5에서 생성한 메모장의 1만개의c문자 대신 5000개의 패턴을 입력합니다.

그럼 이 파일은 총 25000개의 문자열을 갖게 됩니다.


[그림.9]


다시 프로그램에서 Load하여 확인 결과 EIP값이 5000개패턴의 값중 일부로 변경된것을 확인할 수 있습니다.

변경된 주소는 0x36764835 입니다.


[그림.10]


이제 offset 계산기로 EIP=0x36764835 값을 입력합니다.

프로그램을 처음 만들때 Little Endian을 생각지 않고 만들었다가 조금 헤맸었는데요. 금방해결했습니다.

EIP주소값을 프로그램에 입력하면 10진수, 그리고 해당 값을 영문으로 변환하고 파일이 있는 위치의 값을 계산해 주는데요.

여기서도 개발 오류가있습니다. 원래 aaaa.m3u 파일을 읽고 총 2만5천개의 문자열중에서의 위치를 찾게 만들어야되는데 급하게 만들다보니 생성된 패턴 5000개중에 위치를 찾도록 만들어버린 오류를 범하게 됬습니다. 대신 영문으로 변환해주는 기능이 있기떄문에 변환된 영문 : 5 H v 6 로 메모장에서 패턴을 검색하면 그림.10처럼 쉽게 찾을 수 있습니다.

저 부분을 "cccc"문자열로 변경해서 다시 Load합니다.


[그림.11]


다시 windbg가 실행이 되고 확인결과 정확히 EIP=43434343으로 변경이 됬습니다. 43은 ascii code로 "c" 입니다.

[그림.12]


그림.6에서 ESP레지스터까지 "c" 문자열로 채워진것을 확인 했었습니다.

ESP 레지스터가 가르키는 공간까지 공격자가 원하는 코드로 채울수 있는지 확인하기 위하여 CCCC다음에 1abcdefghijklmnopqrstuvwxyz.. 와 같은 패턴을 입력하여 확인합니다.


[그림.13]


확인결과 cccc다음의 1abc 총4바이트를 제외한 defg...문자열부터 시작하여 채워집니다.

그리고 하단에 "aaaa" 문자열로 나타나게 되는데 이것은 처음 입력한 a패턴의 문자열입니다.

이 뜻은 또 다른 쉘코드를 넣을 수 있는 공간으로 볼 수 있습니다.


[그림.14]


다시 코드를 "cccc"다음에 "bbbb" 4바이트로 채워넣고 1abc... 문자열로 채우고 확인하면 정확한 위치가 나옵니다.

이제 이 부분에다 쉘코드를 작성해서 EIP레지스터를 ESP레지스터 주소로 세팅 후에 쉘코드가 실행 될 수 있도록 만들겠습니다.

자! 이제 EIP 주소를 0x000ff730 으로 변경해야합니다. 

이 부분을 C로 짜려고 테스트도 하고 했는데 C로짜서 16진수로 만들고 그 만든 부분을 메모장에 복사하여 붙여넣으면 실행되지 않습니다. 인식을 못하게 됩니다. 그래서 25000개의 문자중에 "5Hv6" 문자열을 찾고 그 위치에 16진수로 000ff730으로 변경하게 끔 만들면 될것같긴 하지만 그냥 perl의 도움을 받기로 합니다.


[그림.15]


perl은 스크립트 언어이고 C보다는 쉽습니다. 그림.15의 주석내용 그대로입니다. 변수를 선언하고 "\x41" x 26104 라고 하면 변수에 a를 26104만큼 생성해 놓습니다.

그림.15는 간략하게 프로그램의 흐름이 0x000ff730으로 이동 후 NOP 25개를 거처 \xcc(break) 를 만나면 멈추는 코드입니다. 

\xcc코드가 굉장히 유용한데 올리디버거나 이뮤니티 디버거로 디버깅시에 프로그램을 실행시키고 저 코드를 만나게되면 그 지점에서 멈추게 됩니다.

Perl 스크립트를 저장하고 F5를 눌러 실행시킵니다.


[그림.16]


그럼 위와같이 문자열이 파일에 생성이 됩니다. 

저 이상한 문자도 16진수로 변환해보면 정확히 90과 CC로 나옵니다.

 NOP과 Break코드도 생성이 됬습니다.


[그림.17]


프로그램에서 파일을 Load하여 프로그램을 실행합니다. 

그러나 ESP레지스터를 확인한 결과 000ff730에 NULL 코드가 들어가 있습니다.

이럴경우 프로그램은 중단되어 더이상 실행이 되지 않기 때문입니다.

그러나 방법은 있습니다.

EIP레지스터를 "JMP ESP" 코드가 있는 주소값으로 세팅하게되면 프로그램은 자동으로 JMP ESP를 실행하여 ESP 쉘코드가 실행이 될 것입니다.

우선 JMP ESP코드의 HEX CODE를 알아야 합니다. 어셈과 1:1 매칭되는 기계어 입니다.

[그림.18]


windbg를 실행하고 RM2MP3를 Attach 합니다.


[그림.19]


그럼 Command Line창이 나오는데 여기다가 그림.19와 같이 입력합니다.

주의 사항은 JMP ESP를 입력하고 한번 더 엔터를 입려한 후에 JMP ESP를 엔터치기 전에 있던 주소 7c931210으로 입력을 해야 됩니다.

확인해보니 JMP ESP의 HEX CODE는 ff e4입니다.

자! 이제 JMP ESP의 헥사 코드도 찾았고 ff e4가 들어있는 주소값을 구하기만 하면됩니다.

구하는 방법은 프로그램에서 사용하는 dll에서 ff e4(JMP ESP)가 있는 코드를 찾는것입니다.

이렇게 하는 이유는 프로그램은 실행이 될때 1개이상의 dll을 호출하여 사용하게 됩니다. dll도 실행파일이며 프로그램이 실행될때 필요한 함수를 제공하기도합니다.

프로그램이 실행이되고 dll이 호출이되면 메모리상에 프로그램과 dll은 서로 공유를 할 수 있습니다. 즉 메모리에 서로 녹아 하나가 된다고 보면됩니다.

하지만 메모리 공유는 프로세스와 프로세스는 서로 같은 메모리를 공유할 수 없습니다.  단 프로세스 안에는 스레드가 있지요 스레드는 같은 프로세스 내에 있는 스레드끼리 공유가 가능합니다. 

자세한 내용은 시스템 프로그래밍관련 서적을 참고하시기 바랍니다.

아무튼 이러한 이유에서 프로그램에서 사용하는 dll내의 주소값을 이용합니다.


[그림.20]


windbg가 실행될때 맨 윗라인은 프로그램이 실행되면서 로딩된 DLL 리스트를 보여줍니다.

그 리스트에서 Esay RM MP3관련 DLL을 찾습니다.

몇개의 주소가 나왔습니다. 0x01770000 ~ 0x3182000 까지입니다.


[그림.21]


windbg의 Command lin에서 s 0x01770000 l 0x3182000 ff e4 를 입력하면 저 주소 범위내에서 ff e4 즉 JMP ESP코드를 갖고있는 코드만 리스트화 해서 보여줍니다.

그 중에서 주의사항이 역시 NULL바이트(00)이 있는 주소값을 사용하게 되면 안됩니다.

그 이유는 전에 설명했기 떄문에 생략하겠습니다.

저는 라인의 맨첫번쨰 주소 0x01834f65 주소를 선택합니다.


[그림.22]


Perl에서 0x000ff730 값을 0x01834f65값으로 변경합니다.

그리고 실행하면 정상적으로 cc(break)있는 부분까지만 실행이 된 것을 확인할 수 있습니다.


[그림.23]


이제 마지막으로 쉘코드를 작성합니다.

쉘코드 작성법은 http://dklee85.tistory.com/69에서 확인 하시면 됩니다.


[그림.23]


프로그램을 실행시키고 OllyDbg에서 프로그램을 Attach 한 후 Ctrl+F2하여 리스타트하고 F9를 눌러 자동실행합니다.

그렇게 되면 아까 CC(Break)있는 곳까지만 실행이 됩니다.

이후 F8(Step over)로 NOP을 쭉 타고 내려옵니다.

[그림.24]


NOP을 미끄러지듯 쭉 내려오면 제가 작성한 쉘코드를 만날 수 있습니다.

하지만 실행이 되지 않습니다. 

왜 실행이 되지 않을까요?

이유는 문자열(명령어)이 정확하지 않기 때문에 실행이 되지않습니다.

분명 "cmd /c /calc" 와 같이 정확한 명령이 실행이 되어야 프로그램이 실행이 되는데 여기서는 calc 뒤에 쓰레기 값이 붙습니다.

즉 "cmd /c /calcsdoi" 와같은 이상한 명령어가 완성이 되기때문에 실행이 되지않습니다.

해결법을 위해 00를 넣어봤습니다. 하지만 디버깅 상태에서는 실행이 되나 cc break포인터를 해제한후 디버깅모드가아닌 상태로 실행했을땐 실행이 되지 않았습니다.

그 이유는 00 NULL 바이트 이기때문에 프로그램실행이 중단되는 이유때문입니다.

그래서 생각한것이 저 부분을 한칸띄우기로 하고 \x20으로 변경해서 쉘코드를 추가해줍니다.


[그림.25]


결과는 정상적으로 실행이 됩니다.


[그림.26]


마지막으로 Perl 스크립트에서 #$shellcode = $shellcode."\xcc" 부분을 주석처리 해줍니다. 

결과는 역시 정상적으로 계산기가 실행이 됩니다.















<참조>

http://ko.wikipedia.org/wiki/버퍼_오버플로

https://www.corelan.be/index.php/2009/07/19/exploit-writing-tutorial-part-1-stack-based-overflows/

http://sinun.tistory.com/156

'[Exploit]' 카테고리의 다른 글

[Exploit] BufferOver Flow - Easy Rm to MP3 Converter  (2) 2013.07.25
[Exploit] ShellCode 작성하기  (0) 2013.07.25
posted by 미스터리 DKL