본문 바로가기

보안

보안기사 - 시스템 해킹

버퍼 오버플로우 공격 (Buffer Overflow Attack)

연속된 메모리 공간을 사용하는 프로그램에 할당된 메모리의 범위를 넘어선 위치에 자료를 읽거나 쓰려고 할 때 발생

프로그램의 오작동 유발, 악의적 코드 실행시켜 공격자 프로그램을 통제할 수 있는 권한 획득하게 됨

  1. 스택 버퍼 오버플로우
    스택에 할당된 버퍼들이 문자열 계산 등에 의해 정의된 버퍼의 한계치를 넘는 경우 버퍼 오버플로우가 발생하여 복귀 주소(return address)를 변경하고 공격자가 원하는 임의 코드를 실행

  2. 힙 버퍼 오버플로우
    힙에 할당된 버퍼들에 문자열 등이 저장되어 질 때, 최초 정의된 힙의 메모리 사이즈를 초과하여 문자열 등이 저장되는 경우 버퍼 오버플로우가 발생하여 데이터와 함수 주소 등을 변경하여 공격자가 원하는 임의 코드를 실행
  • 스택
    함수 처리를 위해 지역변수 및 매개변수가 위치하는 메모리 영역

  • 사용자가 동적으로 할당하는 메모리 영역 (malloc)

스택 버퍼 오버플로우

배경지식



  • stack frame
    모든 함수는 호출이 되면 자신만의 스택 공간이 할당 (stack frame)
    스택 프레임 공간 내에서 SFP(Stack Frame Pointer)를 기준점으로 하여 스택 포인터에 상대주소(offset)를 저장하여 메모리 접근을 하게 된다



  • epilogue 과정 이전에 RET 조작하여 악성코드 실행되게 하는 것
대응 방안

  1. 안전한 코드
    - strcpy 함수는 오버플로우에 취약! -> strncpy 함수로 교체
    - 입력 값 사전 검증을 통한 버퍼 오버플로우 방지 (입력 문자열 길이 검사)

  2. 스택 가드 (stack guard)
    메모리상에서 프로그램의 복귀주소와 변수 사이에 특정 값을 저장해두었다가 그 값이 변경되었을 경우를 오버플로우로 가정하여 프로그램 실행을 중단

  3. 스택 쉴드 (stack shield)
    함수 시작 시 복귀 주소를 Global RET라는 특수 스택에 저장해 두었다가 함수 종료 시 저장된 값과 스택의 RET값을 비교해 다를 경우 오버플로우로 가정하여 프로그램 실행을 중단

  4. ASLR (Address Space Layout Randomization)
    메모리 공격을 방어하기 위해 주소 공간 배치를 난수화하는 기법
    실행 시 마다 메모리 주소를 변경시켜 악성코드에 의한 특정주소 호출 방지

레이스 컨디션 공격 (Race Condition Attack)

  • 레이스 컨디션
    둘 이상의 프로세스나 스레드가 공유자원에 동시에 접근할 때 접근하는 순서에 따라 비정상적인 (원하지 않는) 결과가 발생하는 조건/상황
공격 원리

실행되는 프로세스가 임시파일을 만드는 경우 악의적 프로그램을 통해 그 프로세스의 실행 중에 끼어들어 임시파일을 목적파일로 연결(심볼릭 링크)하여 악의적 행위를 하는 것
만약 프로세스가 setuid 설정이 되어 root 권한으로 실행된다면 권한 상승을 통한 중요 자원(파일)에 접근하는 심각한 문제 발생

대응 방안

  1. 가능하면 임시파일 생성X (메모리 상에서 처리)
  2. 파일 생성시 이미 동일한 파일 존재하는 경우 파일 생성 또는 쓰기 금지
  3. 사용하고자 하는 파일에 링크가 걸려있으면 실행 중단
  4. umask를 최하 022 정도로 유지하여 임시로 생성한 파일이 공격자에 의해 악의적으로 삭제되지 않도록 한다.

포맷 스트링 공격 (Format String Attack)

  • 포맷 스트링
    C 언어의 printf, fprintf, sprintf 등의 함수에서 사용되는 문자열의 입출력 형태를 정의하는 문자열로 서식문자열이라 표현
    식별자 출력형식
    %d 10진수 정수 출력
    %f 실수 출력
    %c 문자 출력
    %s 문자열 출력
    %x 16진수 정수 출력
    %n 이전까지 출력한 총 바이트 수를 지정한 변수에 저장

포맷 스트링을 인자로 하는 함수의 취약점을 이용한 공격
외부로부터 입력된 값을 검증하지 않고 입출력 함수의 포맷 스트링을 그대로 사용하는 경우 발생

공격자는 포맷 스트링을 이용해 취약한 프로세스를 공격하거나 메모리 내용을 읽거나 쓸 수 있다
그 결과 공격자는 취약한 프로세스의 권한을 획득하여 임의의 코드를 실행할 수 있다


포맷 스트링(서식 문자열)의 취약점

포맷 스트링을 인자로 하는 함수 사용 시 포맷 스트링을 지정하지 않고 사용자 입력을 통해서 포맷 스트링이 결정된다면 공격자는 이를 조작하여 메모리 내용을 참조하고 특정 영역의 값을 변경할 수 있다
가령, 공격자는 스택 프레임 구조를 고려하면서 "%x"를 통해 메모리 내용 참조 및 원하는 위치(RET영역)로 이동한 후 "%n"을 통해 Return Address를 악성코드가 위치한 주소로 변조하여 악성코드 실행

포맷 스트링 대응방안

포맷 스트링을 함수의 입력 파라미터로 직접 사용하지 않는다

취약한 사용 예

#include <stdio.h>
int main(int argc, char **argv) {
     printf(argv[1]);
}




안전한 사용 예

#include <stdio.h>
int main(int argc, char **argv) {
     printf("%s", argv[1]);
}




안전한 코드의 예처럼 함수 사용 시 포맷 스트링을 지정해 간접적으로 참조 되도록