본문 바로가기

시스템

쉘코드를 이용한 Buffer Overflow (NOP Sled)

쉘코드를 이용해 버퍼 오버플로우 공격을 시험해보자.

시험을 위해서는 먼저 버퍼 오버플로우 취약점을 가진 프로그램이 필요하다.

 

위의 프로그램에서는 strcpy()를 사용하고 있다.

strcpy()는 NULL Byte(문자열의 끝)를 만나기 전까지의 문자열를 복사하는 함수로 문자열의 길이를 검사하지 않는다.

따라서 256바이트에 해당하는 버퍼의 크기를 넘어서는 값을 입력하더라도 그대로 버퍼에 복사가 되므로 해당 프로그램은 버퍼 오버플로우 취약점을 가지게 된다.

 

위의 프로그램에서 버퍼 오버플로우가 일어나는 과정을 확인하기에 앞서 편의를 위해 메모리 보호기법 중 하나인 ASLR을 비활성하겠다.

ASLR(Address Space Layout Randomization)이란 메모리상의 공격을 어렵게 하기 위해 프로그램을 실행할 때 마다 스택, 힙, 라이브러리의 주소를 랜덤으로 배정하는 기법이다.

 

ASLR옵션을 끄는 명령어는 다음과 같다.

echo 0 > /proc/sys/kernel/randomize_va_space

 

이어서 위 프로그램에 버퍼 오버플로우를 발생시켜보자.

빨간 박스의 명령어를 살펴보자.

16진수로 100은 10진수로 256을 의미하므로 해당 명령어는 버퍼의 크기를 할당한 것임을 알 수 있다.

 

다음으로 디스어셈블된 위 그림을 참고해 스택의 구조를 생각해보자.

스택의 구조는 RET위에 SFP위에 EBX레지스터의 값 위에 버퍼가 차례대로 들어간 형태가 될 것이다.

이를 그림으로 표현해보면 다음과 같다.

버퍼 오버플로우는 RET을 우리가 삽입한 코드(정확히 말하자면 주소)로 덮어 해당코드가 동작하도록 만드는 것이다.

따라서 버퍼의 크기(256), EBX(4), SFP(4)를 포함해 대략 264byte 이상을 입력한다면 RET을 덮을 수 있다.

 

그럼 오버플로우를 발생시켜 임의의값으로 RET을 덮어보자.

ret가 eip가 되도록 breakpoint를 잡아준다.

 

r $(python -c 'print("A"*268)')을 입력해 argv[1]에 "A"문자 268개를 넣어주면서 실행한다.

 

step into명령어를 통해 ret명령어 안으로 들어온 결과이다. eip의 값이 0x41414141이라는 것과 Invalid $PC address: 0x41414141이라는 메세지를 통해 RET의 return address가 AAAA로 값이 바뀐 사실을 알 수 있다.

 

이를 통해 우리는 어떻게 버퍼 오버플로우를 일으켜 쉘코드를 실행시킬지에 대한 개념을 알게되었다. 

argv[1]에 쉘코드를 입력한 후 버퍼 오버플로우를 일으켜 RET을 버퍼에 존재하는 쉘코드의 시작주소로 덮어버린다면 RET가 실행되면서 쉘코드가 존재하는 위치로 이동한 후 쉘코드가 실행될 것이다.

하지만 정확한 쉘코드의 시작주소를 알기는 어렵다. 따라서 NOP SLED 기법을 사용할 것이다.

NOP SLED를 사용하게 되면 쉘코드의 정확한 시작주소를 알지 못하더라도 쉘코드를 실행시킬 수 있다.

 

NOP SLED를 직역하면 NOP 썰매로 이는 NOP를 타고 쭉 미끄러져 쉘코드까지 도달하는 것을 말한다.

NOP(0x90)란 No Operation의 약자로 아무런 실행을 하지 않는것을 뜻한다. 원래의 NOP역할은 기계어 코드가 다른 기계어 코드와 섞이지 않도록 하는 것이다. 따라서 쉘코드의 앞과 뒤를 NOP로 감싼 후 return address를 NOP의 한 지점으로 도달하도록 설정한다면 NOP의 흐름을 타고 쉘코드에 도착하게 된다.

 

그렇다면 이제 쉘코드를 이용해 버퍼 오버플로우를 일으켜 보자.

사용할 쉘코드는 25byte로 다음과 같다.

\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80

 

payload의 형태는 NOP(100)+SHELLCODE(25)+NOP(135)+SFP(4)+RET(4)로 구성했다.

이제 payload를 작성해 버퍼 오버플로우가 일어나는지 살펴보도록 하겠다.

버퍼 오버플로우에 성공한 것을 확인할 수 있다.

반응형