[exploit writing] 2_쉘코드로 점프 (3)

2.pop/ret

위에서 설명했듯이 Easy RM to MP3 예제에서 우리는 buffer를 임의로 변조했고, ESP가 직접 우리가 작성한 쉘코드를 가리키도록 만들었다. 그렇다면 쉘코드를 가리키는 레지스터가 단 하나도 없다면 어떻게 될까?

이러한 상황에서, 쉘코드를 가리키는 주소는 스택의 어딘가에 담겨 있을 것이다. ESP를 덤프할 때, 처음으로 나오는 주소들을 유의해서 보길 바란다. 만약 이 주소들이 공격자의 쉘코드(또는 제어 가능한 버퍼)를 가리키고 있다면, pop/ret 또는 pop/pop/ret 명령을 통해 다음과 같은 결과를 확인할 수 있을 것이다.

- 스택에서 주소를 가져옴 (또는 무시)
- 쉘코드로 연결되는 주소로 점프

'pop/ret' 기술은 ESP+offset이 이미 쉘코드를 가리키는 주소를 담고 있을 때만 사용이 가능하다. ESP를 덤프한 다음 쉘코드를 가리키는 녀석이 처음으로 보이는 주소들 중에 존재하는지 확인한 후, EIP로 향하도록 pop/ret (pop pop ... ret) 참조를 입력하도록 한다. 이를 통해 스택에서 주소를 가져오고, EIP 안으로 다음에 수행해야 할 주소를 입력시킬 수 있다. 만일 처음으로 보이는 주소들 중 하나라도 쉘코드를 가리키는 것이 있다면, 공격은 성공적으로 이루어질 수 있다.

'pop/ret' 기술을 사용하는 두 번째 방법도 존재한다. 만약 공격자가 EIP를 제어하려 하는데, 쉘코드를 가리키는 어떠한 레지스터도 존재하지 않지만 때마침 쉘코드가 ESP+8 의 위치에 존재한다고 가정해 보자. 이런 상황에서, 공격자는 ESP+8로 흐름이 가도록 EIP에 pop/pop/ret 명령을 주입 함으로써 공격을 성공시킬 수 있다. 만약 해당 위치에 JMP ESP로 가는 포인터를 삽입한다면, JMP ESP 포인터 바로 오른쪽에 위치한 쉘코드로 점프하게 될 것이다.

실습을 통해 알아보도록 하자. 우리는 EIP를 덮어 쓰기 이전에 26064 바이트를 채워 넣어야 한다는 것을 알고 있다. 또한 ESP가 우리가 의도한 정확한 위치를 가리키도록 하기 위해 4개의 추가 바이트가 필요하다는 사실도 알고 있다.

우리는 ESP+8 위치에서 테스트를 해 보겠다. 우리는 쉘코드를 가리키는 주소를 가지고 있다.

26064개의 'A' + 4개의 'XXXX' + break + 7개의 NOP + break + 추가 NOP 로 코드를 구성한다. 그리고 쉘코드를 두 번째 브레이크에서 시작한다고 가정해 보자. 우리의 목표는 첫 번째 브레이크를 뛰어 넘는 점프를 통해 두 번째 브레이크(ESP+8의 위치 = 0x000ff738)로 프로그램의 흐름을 제어하는 것이다.

/*esp+8을 이용하기 위해 사용하는 공격 코드*/

my $file= "test2.m3u";
my $junk= "A" x 26064;
my $eip = "BBBB"; # EIP를 'BBBB'로 채워 넣음
my $prependesp = "XXXX"; # ESP가 쉘코드의 시작을 가리키게 하기 위해 4바이트 채움
my $shellcode = "\xcc"; # 첫 번째 브레이크
$shellcode = $shellcode . "\x90" x 7; # 7 바이트를 NOP를 채움
$shellcode = $shellcode . "\xcc"; # 두 번째 브레이크
$shellcode = $shellcode . "\x90" x 500; # 실제 쉘코드
open($FILE,">$file");
print $FILE $junk.$eip.$prependesp.$shellcode;
close($FILE);
print "m3u File Created successfully\n";

위 공격 코드로 생성한 m3u 파일을 실행하면 다음과 같은 결과가 나온다. 먼저 스택 내용을 살펴보도록 하자. 애플리케이션은 버퍼 오버플로우로 인해 충돌이 발생한다. 우리는 그림4에서 EIP를 'BBBB'로 채워 넣었다. ESP는 0x000ff730을 가리키고 있고, 그 다음 7개의 NOP가 채워 진 다음 우리가 만든 쉘코드의 실제 시작 부분인 두 번째 브레이크 명령이 있다.


우리의 목적은 'ESP+8' 값을 EIP로 주입하는 것이다(현재는 '두 번째 브레이크=cc' 값을 가지고 있지만 이 값을 수정함으로써 쉘코드로 점프할 수 있게 된다). 우리는 'pop/ret' 기술과 'JMP ESP'를 혼합하여 쉘코드로 이동할 것이다. 하나의 pop 명령은 스택의 꼭대기에서 4 바이트를 꺼내는 일을 한다. 이렇게 되면 스택 포인터는 000ff734를 가리키게 된다. 추가로 pop 명령을 하나 더 수행하게 되면 4 바이트를 더 스택에서 꺼내게 된다. 결국 ESP가 000ff738을 가리키게 되는 것이다!

 'RET' 명령이 수행될 때, 현재 ESP에 들어 있는 값이 EIP로 주입되게 된다. 그래서 만약 000ff738에 위치한 명령이 JMP ESP 명령을 포함하고 있다면, EIP도 그 일을 수행하게 되는 것이다.

위에서 설명했듯이 우리에게 필요한 명령어 덩어리는 'pop/pop/ret' 이다. 그리고 명령어 덩어리의 첫 부분으로 EIP를 덮어쓰고, ESP+8부분을 쉘코드가 뒤따라 오도록 'JMP ESP'의 주소로 설정해야 한다.

댓글

이 블로그의 인기 게시물

윈도우 설치에서 파티션 설정 오류(NTFS)

[exploit writing] 1_스택 기반 오버플로우 (1) First

하둡 설치 오류 정리