버퍼 오버플로(Buffer Overflow)란?

배열 등의 영역(버퍼)에 그 크기를 넘는 데이터를 써서 이웃한 메모리를 망가뜨리는 것. C 언어에서는 쓰기 대상의 크기를 모르는 strcpy나 gets, 범위를 확인하지 않고 인덱스를 사용하는 루프 등에서 일어나기 쉬우며, 미정의 동작이 된다. 스택 위의 버퍼가 넘치면 지역 변수나 함수의 복귀 주소가 고쳐 쓰여, 외부에서 준 입력으로 임의의 코드를 실행시키는 공격에 악용되기도 한다. 크기를 지정할 수 있는 함수나 범위 확인으로 막는다.

일본어 명칭: バッファオーバーフロー

버퍼 오버플로(Buffer Overflow)은(는) 링커, 로더, 실행 파일, C 언어 기초 등 2개 분야의 연습 문제 3개에 나와요.

분야: C 언어 기초

흔한 원인

종단의 '\0' 1바이트를 세는 것을 잊고 char name[8]에 8문자를 strcpy하거나, 입력의 길이를 확인하지 않고 gets나 scanf("%s")로 읽거나, for (i = 0; i <= N; i++)처럼 하나 더 많이 쓰는 실수가 전형적입니다. 쓰기 대상의 크기를 받지 않는 함수에서는 얼마나 쓸지를 입력 데이터가 정해 버립니다.

공격에 대한 악용과 대책 구조

스택 위의 버퍼 가까이에는 함수의 복귀 주소가 저장되어 있습니다. 공격자는 긴 입력으로 그곳을 고쳐 써서, 준비한 코드나 라이브러리 함수로 처리를 옮깁니다. OS나 컴파일러는 복귀 주소 바로 앞에 둔 값의 변화를 조사하는 스택 보호(카나리), 쓰기와 실행의 분리(W^X), 주소의 무작위화(ASLR) 등으로 악용을 어렵게 하고 있습니다.

프로그램에서 막는 방법

strcpy 대신 snprintf처럼 쓰는 크기를 지정할 수 있는 함수를 사용하고, 입력의 길이와 인덱스의 범위를 반드시 확인합니다. 개발 중에는 -fsanitize=address(AddressSanitizer)를 붙이면 범위 밖 쓰기를 실행 시에 검출할 수 있습니다. MMU를 갖지 않는 마이컴에서는 넘쳐도 바로 멈추지 않고 이웃한 데이터를 망가뜨리므로 특히 주의가 필요합니다.

도전해 보기: 버퍼 오버플로(Buffer Overflow) 관련 문제

ASLR(주소 공간 배치 무작위화)의 목적으로 올바른 것은 무엇입니까?

  1. A스택, 힙, 라이브러리 등의 주소를 실행할 때마다 바꾸어, 공격자가 노리는 주소를 예측하기 어렵게 한다
  2. B메모리 주소를 무작위로 골라 캐시 히트율을 높인다
  3. C사용하지 않는 메모리를 무작위로 해제하여 빈 용량을 늘린다
정답과 해설 보기

정답: A. 스택, 힙, 라이브러리 등의 주소를 실행할 때마다 바꾸어, 공격자가 노리는 주소를 예측하기 어렵게 한다

버퍼 오버플로 등을 악용하는 공격에서는 라이브러리 함수나 스택 위 데이터의 주소를 아는 것이 중요합니다. ASLR은 프로세스를 기동할 때마다 스택, 힙, 공유 라이브러리, 실행 파일을 놓는 주소를 바꾸어 공격자가 주소를 단정할 수 없게 합니다. 단독으로 완전히 막을 수 있는 것은 아니며, 쓰기와 실행의 분리 등 다른 대책과 조합하여 사용합니다.

메모리 페이지에 '쓰기 가능'과 '실행 가능'을 동시에 허용하지 않는 것(W^X, 쓰기와 실행의 분리)으로 막기 쉬워지는 공격은 무엇입니까?

  1. A비밀번호를 무차별 대입하는 공격
  2. B대량의 통신을 보내 서버를 멈추는 공격
  3. C스택 등 데이터 영역에 공격용 기계어를 써 넣고 그곳으로 점프시켜 실행하게 하는 공격
정답과 해설 보기

정답: C. 스택 등 데이터 영역에 공격용 기계어를 써 넣고 그곳으로 점프시켜 실행하게 하는 공격

예전 공격에서는 버퍼 오버플로로 스택에 기계어를 써 넣고, 복귀 주소를 그곳으로 향하게 하여 실행시키는 방법이 자주 사용되었습니다. CPU의 페이지 테이블에는 실행 금지 비트(NX 비트 등)가 있어, OS가 스택, 힙, .data를 실행 금지로, .text를 쓰기 금지로 하면 이 방법은 쓸 수 없게 됩니다. JIT 컴파일러처럼 실행 중에 코드를 만드는 경우에는 쓰기와 실행 권한을 전환하여 사용합니다.

자주 묻는 질문

버퍼 오버플로는 반드시 프로그램을 멈추게 하나요?

멈춘다고는 할 수 없습니다. 넘친 쓰기가 사용되지 않는 영역에 닿으면 아무 일도 일어나지 않는 것처럼 보이고, 다른 변수를 망가뜨리면 떨어진 곳에서 잘못된 결과가 됩니다. 복귀 주소를 망가뜨렸을 때나 스택 보호가 작동했을 때 비로소 비정상 종료하기도 합니다.

strncpy를 사용하면 안전한가요?

strncpy는 지정한 수의 문자까지만 복사하지만, 원래 문자열이 길면 종단의 '\0'을 붙이지 않으므로 그 후의 처리에서 범위 밖을 읽을 우려가 있습니다. 크기를 지정하고 반드시 종단을 붙이는 snprintf를 사용하거나, strncpy 뒤에 직접 종단을 씁니다.

해설 속 버퍼 오버플로(Buffer Overflow)

버퍼 오버플로(Buffer Overflow)이(가) 나오는 문제 (3)

  1. ASLR(주소 공간 배치 무작위화)의 목적으로 올바른 것은 무엇입니까?
  2. 메모리 페이지에 '쓰기 가능'과 '실행 가능'을 동시에 허용하지 않는 것(W^X, 쓰기와 실행의 분리)으로 막기 쉬워지는 공격은 무엇입니까?
  3. 위의 함수 save에 8문자의 문자열 "ABCDEFGH"를 전달했다. 일어나는 일로 올바른 것은?

관련 용어

스택(Stack) 복귀 주소(return address) ASLR 공유 라이브러리(Shared Library) 페이지 테이블(Page Table) JIT 컴파일 변수(Variable) 공용체(union) 구조체(struct) 댕글링 포인터(Dangling Pointer)

용어집으로 돌아가기