보안 위협과 방어
버퍼 오버플로와 악성 코드의 공격 경로를 살펴보고 메모리 보호·SELinux·AppArmor·방화벽의 계층적 방어를 구성합니다.
보호 메커니즘이 완벽하더라도 공격자는 소프트웨어의 취약점을 파고듭니다.
OS 수준의 보안 위협과 방어 기법을 이해하면, 안전한 코드를 작성하고 시스템을 견고하게 구성하는 능력이 생깁니다.
보안은 단일 기술이 아니라 계층적 방어(Defense in Depth)입니다.
버퍼 오버플로우
버퍼 오버플로우(Buffer Overflow)는 오래되었지만 여전히 대표적인 취약점 중 하나입니다.
1988년 모리스 웜부터 현재까지 계속 발견됩니다.
배열의 범위를 넘어서 데이터를 쓰면, 인접한 메모리 영역(리턴 주소, 함수 포인터 등)을 덮어쓸 수 있습니다.
#include <string.h>
#include <stdio.h>
/* 취약한 코드 — 절대 이렇게 작성하지 마세요 */
void vulnerable(char *input) {
char buffer[64];
strcpy(buffer, input); /* 입력이 64바이트를 초과하면 오버플로우! */
printf("Buffer: %s\n", buffer);
}
/* 경계 검사를 추가한 코드 */
void safe(const char *input) {
char buffer[64];
snprintf(buffer, sizeof(buffer), "%s", input); /* 길이를 넘으면 잘라서 널 종단 */
printf("Buffer: %s\n", buffer);
}스택 스매싱 공격
스택 버퍼를 넘은 바이트는 인접 데이터를 훼손하고, 제어 데이터까지 닿으면 흐름 탈취로 이어질 수 있습니다.
- 1개념적 프레임
낮은 주소 → 높은 주소 buffer[64] 외부 입력의 목적지 canary 함수 종료 전 변조 검사 saved state 프레임·레지스터 저장 값 return target 반환 제어 지점의 한 예 입력 길이 > 목적지 용량 → 인접 영역으로 손상 확산
- 2결함에서 악용까지
각 단계에서 차단 1 범위 검사 누락 고정 버퍼에 제한 없는 쓰기 2 메모리 손상 카나리·포인터·제어 데이터 변조 3 제어 전환 시도 코드 주입 또는 ROP/JOP 재사용
- 31
범위 검사 누락 고정 버퍼에 제한 없는 쓰기
- 42
메모리 손상 카나리·포인터·제어 데이터 변조
- 53
제어 전환 시도 코드 주입 또는 ROP/JOP 재사용
- 6경계 인지 설계
· PREVENT 경계 인지 설계 메모리 안전 언어, 길이 검증, 크기 제한 API
- 7빌드·테스트 탐지
· FIND 빌드·테스트 탐지 경고·SAST·퍼징·AddressSanitizer
- 8런타임 변조 감지
· DETECT 런타임 변조 감지 FORTIFY_SOURCE · stack-protector-strong
- 9악용 표면 축소
· CONTAIN 악용 표면 축소 NX · ASLR+PIE · RELRO · CFI/CET
공격자가 리턴 주소를 조작하면 함수 반환 시 제어 흐름이 공격자가 의도한 위치로 이동할 수 있습니다.
현대 시스템에서는 직접 shellcode 실행뿐 아니라 ROP 같은 코드 재사용 공격도 함께 고려해야 합니다.
다계층 방어
#include <stdio.h>
#include <stdlib.h>
int main() {
/* 1. 안전한 함수 사용 */
char buf[32];
/* strcpy → strncpy, sprintf → snprintf, gets → fgets */
if (fgets(buf, sizeof(buf), stdin) == NULL) return 1;
/* 2. 컴파일러 방어 (-fstack-protector) */
/* 스택 카나리: 리턴 주소 앞에 랜덤 값 삽입 */
/* 함수 반환 시 카나리가 변조되었으면 __stack_chk_fail() 호출 */
/* 3. OS 방어 (ASLR) */
printf("스택 변수 주소: %p\n", (void *)buf);
printf("힙 주소: %p\n", (void *)malloc(1));
/* 매 실행마다 주소가 달라짐 → 공격자의 주소 예측을 어렵게 함 */
return 0;
}# 주요 보안 옵션을 켜고 컴파일
gcc -O2 \
-fstack-protector-all \
-D_FORTIFY_SOURCE=2 \
-Wformat -Wformat-security \
-pie -fPIE \
-Wl,-z,relro,-z,now \
-Wl,-z,noexecstack \
-o program program.c
# -fstack-protector-all: 모든 함수에 스택 카나리
# -D_FORTIFY_SOURCE=2: 최적화 옵션과 함께 버퍼 크기 검사 강화
# -pie -fPIE: 위치 독립 실행 파일 (ASLR 효과 극대화)
# -Wl,-z,relro,-z,now: GOT 보호 (Full RELRO)
# -Wl,-z,noexecstack: 스택 실행 금지 표시| 방어 기법 | 대상 | 동작 |
|---|---|---|
| 스택 카나리 | 스택 오버플로우 | 리턴 주소 변조 탐지 |
| ASLR | 주소 예측 | 메모리 레이아웃 무작위화 |
| DEP/NX | 코드 주입 | 데이터 영역 실행 금지 |
| PIE | 코드 영역 예측 | 코드 섹션도 무작위 배치 |
| RELRO | GOT 변조 | GOT 영역 읽기 전용화 |
| CFI | 간접 호출 변조 | 제어 흐름 무결성 검증 |
취약한 함수 하나를 고치는 것만으로는 부족합니다. 스택 카나리, ASLR, NX, PIE, RELRO, CFI가 각기 다른 공격 단계를 막습니다.
- 1경계 검사
Safe API 경계 검사 strcpy 대신 snprintf , fgets 를 사용합니다.
- 2리턴 주소 변조 탐지
Canary 리턴 주소 변조 탐지 반환 전 랜덤 값이 바뀌었는지 확인합니다.
- 3주소 예측 방해
ASLR 주소 예측 방해 스택, 힙, 라이브러리 위치를 실행마다 바꿉니다.
- 4데이터 실행 차단
NX 데이터 실행 차단 스택과 힙에 넣은 코드를 바로 실행하지 못하게 합니다.
- 5GOT 보호
RELRO GOT 보호 동적 링크 테이블 변조를 어렵게 만듭니다.
- 6간접 호출 검증
CFI 간접 호출 검증 허용된 제어 흐름 밖으로 점프하지 못하게 합니다.
악성 코드 분류
| 유형 | 전파 방식 | 특징 | 예시 |
|---|---|---|---|
| 바이러스 | 숙주 프로그램에 삽입 | 실행 시 활성화, 자기 복제 | CIH, Melissa |
| 웜 | 네트워크 자동 전파 | 독립 실행, 취약점 이용 | Morris, WannaCry |
| 트로이 목마 | 유용한 프로그램 위장 | 백도어 설치 | Zeus, Emotet |
| 랜섬웨어 | 다양 (이메일, 웹) | 파일 암호화 후 몸값 요구 | WannaCry, Ryuk |
| 루트킷 | 기존 침입 후 설치 | 커널 수준 은닉 | Sony Rootkit |
| 키로거 | 트로이 목마/물리적 | 키 입력 기록 전송 | - |
바이러스와 웜은 퍼지는 방식이 다르고, 루트킷과 랜섬웨어는 시스템에 남기는 흔적과 우선 조치가 다르다.
- 침투와 전파
웜 네트워크 취약점을 이용해 독립적으로 이동하므로 패치와 방화벽 범위가 중요하다. 트로이 목마 정상 도구처럼 보이는 실행 흐름을 만들기 때문에 출처와 서명 검증이 필요하다.
- 은닉과 피해
루트킷 프로세스와 파일 표시를 숨기므로 해시 비교와 오프라인 검사가 효과적이다. 랜섬웨어 파일을 빠르게 암호화하므로 쓰기 권한 최소화와 복구 가능한 백업이 핵심이다.
실무 방어
import hashlib
import os
def file_hash(path):
"""파일의 SHA-256 해시 계산"""
h = hashlib.sha256()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
return h.hexdigest()
# 중요 시스템 파일의 해시를 저장하고 주기적으로 비교
critical_files = ["/usr/bin/sudo", "/usr/bin/ssh", "/usr/sbin/sshd"]
for f in critical_files:
if os.path.exists(f):
print(f"{f}: {file_hash(f)}")
# 해시가 변경되었으면 → 파일이 변조된 것 (루트킷 의심)
# 실무에서는 AIDE, Tripwire, OSSEC 같은 무결성 검사 도구 사용강제 접근 제어 (MAC)
일반적인 Unix 권한(DAC, Discretionary Access Control)은 파일 소유자가 권한을 마음대로 설정할 수 있습니다.
root가 탈취되면 모든 보호가 무력화됩니다.
MAC(Mandatory Access Control)은 시스템 관리자가 정의한 정책에 따라 접근을 제어합니다.
정책이 enforcing 상태라면, root 권한 프로세스도 허용되지 않은 접근은 제한됩니다.
SELinux
NSA가 개발한 MAC 구현입니다.
프로세스마다 보안 컨텍스트(타입)를 부여하고, 정책에 명시적으로 허용된 접근만 가능합니다.
# SELinux 상태 확인
getenforce # Enforcing, Permissive, Disabled
sestatus # 상세 상태
# 파일의 보안 컨텍스트 확인
ls -Z /var/www/html/index.html
# -rw-r--r--. root root system_u:object_r:httpd_sys_content_t:s0
# httpd 프로세스는 httpd_sys_content_t 타입의 파일만 읽을 수 있음
# /home/user/ 디렉토리는 user_home_t → httpd가 접근 불가
# 컨텍스트 복원
restorecon -Rv /var/www/html/웹 서버 프로세스가 침해되더라도 confined 도메인에 갇혀 있다면, SELinux 정책에 의해 /etc/shadow 읽기나 임의 포트 변경 같은 행동이 차단될 수 있습니다.
DAC 권한이 허용하더라도 enforcing 상태의 MAC 정책이 막으면 접근은 실패한다. root 프로세스도 confined 도메인에서는 정책 안에 갇힌다.
- 주체
httpd 프로세스 httpd_t
- 객체
/var/www/html/index.html httpd_sys_content_t
- 연산
파일 read, 디렉토리 search, 소켓 bind 같은 권한 단위
- 정책
allow httpd_t httpd_sys_content_t:file read
- AVC denial 또는 접근 허용
허용 규칙이 있으면 접근, 없으면 AVC denial 기록
AppArmor
프로그램별로 접근 가능한 파일, 네트워크, 커패빌리티를 프로파일로 정의합니다.
SELinux보다 설정이 간단합니다.
# /etc/apparmor.d/usr.sbin.nginx 프로파일 예시
# /usr/sbin/nginx {
# /var/www/** r, # 웹 콘텐츠 읽기만
# /var/log/nginx/** w, # 로그 쓰기만
# /run/nginx.pid rw, # PID 파일
# network inet tcp, # TCP 네트워크 허용
# deny /etc/shadow r, # shadow 파일 읽기 명시적 거부
# }
# AppArmor 상태 확인
aa-status
# 프로파일을 enforce 모드로 설정
aa-enforce /etc/apparmor.d/usr.sbin.nginx네트워크 보안 기초
방화벽 (iptables/nftables)
# iptables 기본 규칙: 필요한 것만 허용, 나머지 차단
iptables -P INPUT DROP # 기본 정책: 모든 입력 차단
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT # 나가는 트래픽은 허용
# 기존 연결 유지
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# SSH (포트 22) 허용
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# HTTP/HTTPS 허용
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 로컬 루프백 허용
iptables -A INPUT -i lo -j ACCEPT보안 모니터링
# 실패한 로그인 시도 확인
grep "Failed password" /var/log/auth.log | tail -20
# 현재 열린 포트 확인
ss -tlnp
# 최근 sudo 사용 기록
grep sudo /var/log/auth.log | tail -10
# 비정상 프로세스 확인
ps aux --sort=-pcpu | head -20보안 계층 정리
| 계층 | 방어 수단 |
|---|---|
| 네트워크 | 방화벽, IDS/IPS, VPN |
| 운영체제 | MAC(SELinux/AppArmor), 패치 관리, 로깅 |
| 애플리케이션 | 입력 검증, 안전한 함수, 코드 분석 |
| 데이터 | 암호화, 해싱, 백업 |
| 물리 | 잠금장치, BIOS 패스워드, TPM |
버퍼 오버플로우, 악성 코드, 권한 탈취는 서로 다른 경로로 들어오지만 입력 검증, 실행 방지, 강제 접근 제어, 감시가 함께 작동해야 한다.
- 경계 검사메모리 공격 방어
경계 검사 문자열 복사는 버퍼 크기를 기준으로 제한하고, 널 종료 여부까지 확인한다. 스택 카나리 반환 주소 앞의 임의 값을 확인해 스택 변조를 함수 반환 전에 탐지한다. 주소 무작위화 스택, 힙, 코드 영역 위치를 예측하기 어렵게 만들어 코드 재사용 공격 비용을 높인다.
- 강제 접근침해 이후 확산 제한
강제 접근 보안 컨텍스트와 프로파일이 루트 권한 프로세스의 허용되지 않은 파일 접근도 차단한다. 방화벽 기본 입력 차단 뒤 기존 연결, 관리 포트, 서비스 포트만 명시적으로 허용한다. 무결성 검사 중요 실행 파일의 해시 변화를 감시해 루트킷이나 파일 변조 징후를 찾는다.
보안은 OS의 한 기능이 아니라, 물리적 접근부터 애플리케이션 코드까지 전 계층에 걸친 노력입니다.
다음 장에서는 현대 인프라의 핵심인 가상화와 컨테이너를 다루겠습니다.
침해는 진입 한 번으로 끝나지 않는다. 실행 주체의 자격 증명과 허용 범위가 바뀌고, 그 권한으로 지속성·유출·자원 고갈이 이어질 때 비로소 피해가 커진다.
- 1실행 경로 확보
ENTRY 실행 경로 확보 노출 서비스, 악성 파일, 탈취 계정으로 user process가 시작된다. auth·service·exec 기록
- 2권한 전이 시도
BOUNDARY 권한 전이 시도 setuid, file capability, sudo 정책 또는 커널 결함을 노린다. UID·capability 변화
- 3고권한 동작
CONTROL 고권한 동작 보호 파일·프로세스·네트워크 설정에 접근 범위를 넓힌다. syscall·LSM 거부/허용
- 4지속·유출·고갈
IMPACT 지속·유출·고갈 unit·cron 변경, secret 읽기, CPU·메모리 독점으로 피해를 유지한다. 파일 변경·자원 압력