소프트웨어 개발보안 가이드 분석(2021) : 메모리 버퍼 오버플로우

메모리 버퍼 오버플로우 보안약점은 연속된 메모리 공간을 사용하는 프로그램에서

할당된 메모리의 범위를 넘어선 위치에 자료를 읽거나 쓰려고 할 때 발생한다.

메모리 버퍼 오버플로우는 프로그램의 오동작을 유발시키거나 악의적인 코드를 실행시킴으로써

공격자 프로그램을 통제할 수 있는 권한을 획득하게 한다.

소프트웨어 개발보안가이드(2021), 한국인터넷진흥원

메모리 버퍼 오버플로우는 프로그램이 할당된 메모리 영역을 벗어나는 데이터를 쓰는 공격으로, 일반적으로 악의적인 코드를 실행시키는 데 사용됩니다. 이러한 공격은 주로 C 및 C++과 같은 저수준 언어로 작성된 프로그램에서 발생하지만, 다른 언어에서도 발생할 수 있습니다. 메모리 버퍼 오버플로우는 주로 스택이나 힙과 같은 메모리 영역에 대한 잘못된 접근으로 인해 발생하며, 공격자는 프로그램의 흐름을 조작하여 시스템에 해를 끼칩니다.

이번 포스팅에선 해당 취약점에 대하여 취약한 코드 예제와 이를 방지하기 위한 안전한 코드 작성 방법을 살펴보려고 합니다.

공격 메커니즘

메모리 버퍼 오버플로우 공격 과정
정상 버퍼와 버퍼 오버플로우가 발생된 메모리 차이
  1. 프로그램에서 사용자 입력을 처리하는 과정에서 메모리 할당이 발생합니다.
  2. 공격자는 이 메모리 할당 과정에서 프로그램의 버퍼(message)를 넘어서는 큰 데이터(0xAABB)를 입력합니다.
  3. 이러한 입력으로 인해 버퍼의 경계를 넘어서는 데이터가 쓰입니다.(정상적이라면 num은 10이지만 너무 큰 사이즈가 들어와 num 자리까지 0xAABB가 입력됩니다.
  4. 쓰여진 데이터는 메모리의 다른 영역을 덮어쓰거나 악의적인 코드를 실행하기 위한 데이터로 사용될 수 있습니다.(정상 수행의 경우 정상적인 주소로 복귀하는 반면, 오버플로우가 일어난 메모리의 경우 0xAABB가 공격 코드의 시작 주소이므로 결국 공격 코드를 수행하게 됩니다.)

 

취약한 웹 애플리케이션의 예

메모리 버퍼 오버플로우는 일반적으로 C나 C++와 같은 저수준 언어에서 발생하는 취약점이지만, 자바에서도 유사한 문제가 발생할 수 있습니다. 아래의 자바 코드는 메모리 버퍼 오버플로우 취약점을 갖고 있습니다.

public class BufferOverflowExample {
    public static void main(String[] args) {
  1     String secretPassword = "topsecret"; // 비밀번호

  2     Scanner scanner = new Scanner(System.in);
        System.out.print("Password: ");
  3     String inputPassword = scanner.next();

    }
}
  • 1 : 비밀번호를 저장하는 변수를 선언하고, topsecret으로 초기화합니다.
  • 2, 3 : 사용자에게 비밀번호를 입력하도록 요청합니다.
    • 이 코드에서 메모리 버퍼 오버플로우가 발생하는 이유는 사용자가 입력한 비밀번호의 길이를 제한하지 않기 때문입니다.
    • 만약 사용자가 매우 긴 문자열로 비밀번호를 입력하면 프로그램이 할당한 메모리 공간을 초과하여 다른 메모리 영역을 침범할 수 있습니다.

 

시큐어 코딩 적용 방법

    public static void main(String[] args) {
        String secretPassword = "topsecret"; 

        Scanner scanner = new Scanner(System.in);
        System.out.print("Password: ");
        String inputPassword = scanner.nextLine(); 

  1     if (inputPassword.length() <= 20 && inputPassword.matches("[a-zA-Z0-9]+")) {
  2         if (inputPassword.equals(secretPassword)) {
                System.out.println("Access granted!");
            } else {
                System.out.println("Access denied!");
            }
        } else {
            System.out.println("Invalid password!");
        }
    }
  • 1 : 사용자가 입력한 비밀번호의 길이를 검증하여 최대 길이를 넘지 않도록 하며 정규 표현식을 이용해 검증까지 적용합니다.
  • 2 : 입력한 비밀번호가 유효한 경우에만 비밀번호를 비교하며 그렇지 않을 경우엔 Invalid password 메시지를 통해 오류를 사용자에게 전달합니다.

메모리 버퍼 오버플로우를 방지하기 위한 시큐어코딩 적용 방법으로는 다음과 같은 방법들도 있습니다.

  1. 입력 검증 : 모든 사용자 입력에 대해 적절한 길이 제한 및 형식 검증을 수행하여 오버플로우를 방지합니다.
  2. 보안 강화된 함수 및 라이브러리 사용 : 안전한 함수와 라이브러리를 사용하여 메모리 조작을 수행합니다.
  3. 스택 보호 기법 : 스택 보호 기법을 사용하여 스택 오버플로우를 방지합니다.
  4. ASLR(주소 공간 무작위화) : 메모리 주소 공간을 무작위로 배치하여 공격자가 취약점을 이용하기 어렵게 만듭니다.

  • 이전 소프트웨어 개발보안 가이드 분석(2021) : 보안기능 결정에 사용되는 부적절한 입력값
  • 다음 소프트웨어 개발보안 가이드 분석(2021) : 포맷 스트링 삽입