Stack Overflow에 대한 대응과 안전한 프로그램 개발
스택 Overflow에 대한 대응
일반적인 방어 방법
방어 기술은 fix를 대체하기 어려움
- 계층적인 방어(Layered Defense) 기법 : 악성코드 실행, 취약한 코드의 악용
- 코드의 변경이 가능한지에 대한 고려
Overflow defense
공격에 대한 성공 확률 낮춤
- Tamper detection: 누가 건드렸는지 확인
- 운영체제와 하드웨어에서의 Memory protection: 실행할 수 없는 곳에서는 실행이 안되도록 함
- Diversification methods: 메모리의 위치를 랜덤하게 해라. (특히 return address, ebp)
Canaries on the stack
각 스택 프레임이 갖고 있는 취약한 포인터 값들에 대한 보호
Idea
- 프레임을 보호계층으로 감싸기 "canary" - unhealthy condition을 알아내서 먼저 알려줌
- Canary는 return address 아래 위치
- 공격자가 리턴 주소를 위변조 하고자 할 때
- 프로그램 실행 중 Canary의 갱신 여부를 확인
Stack without canaries
Stack with canaries
GCC's Stack Smashing Protector
#include <stdio.h>
#include <string.h>
int fun1(char *arg){
char buffer[1024];
strcpy(buffer,arg);
}
void main(int argc, char *argv[]){
fun1(argv[1]);
}
위의 코드를 어셈블리로 표현하면 다음과 같다.
SSP 없는 코드
fun1
- pushl, movl을 통해 새로운 스택을 만든다.
- 스택이 가리키는 위치가 1048만큼 증가한다.
- ebp에서 8만큼 떨어진 값을 eax에 넣는다.
- eax의 값을 esp에서 4만큼 떨어진 값에 넣는다.
- eax값을 esp로 이동하고 strcpy를 호출한다.
SSP 있는 코드
- 스택을 생성한 후, 스택이 가리키는 위치를 1064만큼 올린다.
- ebp에서 8만큼 떨어진 값을 eax에 넣고, ebp에서 -1052만큼 떨어진 값에 eax를 넣는다.
- gs:20은 일종의 난수이다. EAX에 canary 값을 세팅한다.
- 리턴 주소 근처에 canary 값을 저장한 후, strcpy를 호출한다.
- edx에 canary 값을 저장하고, canary 값이 바뀌었는지 확인한다. 변하지 않았다면 L3로 점프해 정상 종료하고, 변했다면 abort 한다.
Detecting Stack Overflow
$ gcc -m32 overflow.c -o overflow.out
$ ./overflow.out xxxx
$ ./overflow.out 'perl -e 'print "x"x1025''
***stack smashing detected ***: ./overflow.out terminated Aborted (core dumped)
경쟁 조건
경쟁 : 공격자는 canary 매커니즘을 분석하여 이의 취약점을 발견하고 공격을 수행
- 상수형 canary를 찾아냄
- 만일 canary가 0x0af237ab6이면 리턴 주소 근처에 해당 값을 넣어서 우회함
- 난수형 canary
- 난수 값의 변화를 확인하거나 SEED 값을 찾아냄
- 컴퓨터는 보통 SEED 값을 사용해 난수값을 생성하는데, 보통 SEED는 날짜,시간 값을 쓴다.
- 암호 기술 기반의 난수 생성
- 값이 저장된 위치 찾기
- 이를 복사하기 위한 코드 작성
Temper detection 효과
지역 변수는 보호하지 않음
- 지역 변수의 재배치를 방어기법으로 사용
파라미터에 대한 overwriting 공격
- 연속적으로 쓰기가 발생한 경우의 변경을 확인
- 리턴 주소에 쓰기를 해도 리턴하지 않음 -> 종료, abort
Heap 보호 기능 개발
- glibc와 Windows XP SP2부터 heap canaries를 갖고 있음
리턴을 기반으로 하는 프로그래밍
- state-of-the-art: 존재하는 실행 코드의 활용
- Canaries 우회, NX까지 우회
Operating system 분리
고립(Isolation) : 다른 프로세스들이 다른 자원을 사용하도록
공유(Sharing) : 프로세스간 자원이 공유되어야 함, 일부 고립 허용
- 공유 대상: All or nothing, 접근 통제를 위한 중재, 사용량 제한을 통한 중재
Concern: 보호의 단위
운영체제는 다중 사용자 환경을 기반으로 분리 매커니즘을 중요하게 생각함
NX(실행불가-Non-executable) Memory
CPU가 메모리 페이지에 대해 R, RW, X 보호 기능을 갖게 함
- x86 series CPU들은 페이지 레벨 XD/NX을 갖게 함
non-executable regions의 정의를 통해
- code와 데이터가 나누어져 있는 상황을 고려
- 쉘 코드의 실행이 데이터영역에서 이루어지지 않도록 할 수 있음
ASLR (Address Space Layout Randomization)
개념
- 같은 프로그램의 여러 버전을 만들어서 다양화
- 고정된 구조를 가질 것이라고 가장한 공격의 발생을 막을 수 있음
Randomization을 통해
- 데이터 혹은 코드의 위치를 찾기 어렵게 함
효과
- 좋지만, 주된 취약점을 제거하지는 못함
- 오히려 ASLR구현 내용이 악용 대상이 되기도 함
- 적은 주소 공간을 가지고 random화 할 경우 (e.g. 256 addresses), 공격자가 brute force(무차별 대입 공격) 기법으로 취약한 위치를 찾을 수 있음
안전한 프로그램 개발
방어적 프로그래밍 - 경계 점검
오버 플로우에 대한 방어적 프로그래밍 방법은 경계의 검사임
- 쓰기 전 데이터 길이 검사 (Check data lengths before writing)
- 입력의 길이를 제한 (Constrain size of inputs)