설명
void echo()
{
char buf[8];
// 대충 buf에 사용자 입력 값 삽입하는 코드
}
void caller()
{
echo();
}
char buf[8] 선언 시 여러 상태정보와 함께 스택에 24바이트 할당한다.
buf와 저장된 리턴 포인터 사이의 16바이트는 사용되지 않는다.
echo()의 buf에 8문자(널 문자 포함)를 입력하면 buf에 할당된 공간에 알맞게 저장한다.
하지만 보다 긴 문자열을 입력하면 스택에 저장된 정보의 일부를 덮어쓰게 된다.
23개 까지는 심각한 결과가 발생하지 않지만, 이 이상부터는 리턴 포인터의 값과 저장 상태까지도 손상된다.
리턴 주소가 손상되면 ret instruction이 프로그램을 전혀 예상하지 못한 곳으로 점프하게 한다.
저장 공간을 오버플로우하게 되는 함수(strcpy, strcat, sprintf …)를 사용하는 것은 나쁜 프로그래밍 습관이다.
버퍼 오버플로우 악용
버퍼 오버플로우의 보다 치명적인 사용은 프로그램이 하지 않을 기능들을 실행하도록 하는 것이다.
이는 컴퓨터 네트워크 상의 시스템 보안성을 공격하는 일반적인 방법이다.
ex) 바이트로 인코딩한 탐색코드(exploit code) 포인터로 리턴 주소를 덮어쓰는 문자열을 추가 입력
→ 탐색코드로 점프
→ 시스템 콜을 이용해서 쉘을 시작하고 공격자에게 여러가지 운영체제 기능 제공
→ 이후 ret을 한 번 더 실행시켜 정상적인 리턴이 발생한 것처럼 하기도 한다.
버퍼 오버플로우 공격 대응 기법
GCC 최신 버전에서 제공하는 일반적인 기법 3가지 소개한다.
이들은 프로그래머에게 특별히 요구되는 것이 없고, 성능을 거의 감소시키지 않는 공통점이 있다.
함께 사용하면 보다 효과적이나 그래도 모든 공격을 막을 수는 없다.
스택 랜덤화
: 예시로 설명한 탐색코드를 삽입하려면 문자열이 위치하게 될 스택의 주소를 알아야 하지만,
역사적으로 프로그램의 스택 주소를 쉽게 예측할 수 있었다.
스택의 위치를 프로그램의 매 실행마다 다르게 해주는 것이 포인트
프로그램의 시작 시 랜덤 크기 공간을 할당해서 스택 위치가 변경되도록 해준다.
주소 공간 배치 랜덤화 (ASLR) 라고 하는 리눅스 시스템 표준 기법을 통해 코드, 라이브러리 코드, 스택, 전역변수, 힙 데이터를 포함하는 여러 부분들이 매번 실행할 때마다 메모리의 다른 지역에 로딩한다.
스택 손상
: C에서 배열의 경계를 넘는 쓰기 작업을 방지하는 안정적인 방법은 존재하지 않지만,
검출해로운 효과가 발생하기 전에 이러한 쓰기 작업이 발생하는 것을 감지할 수는 있다.
→ 지역 버퍼와 나머지 스택 상태 값 사이에 특별한 카나리(Canary) 값을 저장하는 것
함수로부터 리턴하기 전에 프로그램은 카나리 값이 변경되었는지를 체크한다.
프로그램의 매 실행마다 랜덤으로 생성되어 공격자가 값을 쉽게 추정하기 어렵다.
실행코드 영역 제한하기
일반적인 프로그램에서 컴파일러가 만든 코드를 저장하는 메모리 부분만이 실행 가능하면 된다.
나머지 부분들은 읽기와 쓰기만 허용하도록 제한한다.
『Computer Systems: A Programmer's Perspective』 책을 읽고 정리한 글입니다.
검색을 허용하지 않고, 수익을 창출하지 않습니다.