Transcript Document

Part 2. 제2장 시스템에 대한 사이버 공격
(시스템 서비스 거부 공격,그 외 공격 방법)
김민철
ToC
 2.5 시스템 서비스 거부 공격
 2.5.1. 디스크 채우기
 2.5.2 메모리 고갈 기법
 2.5.3 모든 프로세스 죽이기 기법
 2.6 그 외 공격 방법
 2.6.1 레이스 컨디션을 이용한 공격
 2.6.2 포맷 스트링을 이용한 공격
 2.6.3 리버스 엔지니어링을 이용한 공격
2
2.5 시스템 서비스 거부 공격
 서비스 거부 공격(DoS : Denial of Service) :
 한 사용자가 시스템의 리소스를 독점하거나 파괴함으
로써 다른 사용자들이 이 시스템의 서비스를 올바르
게 사용할 수 없도록 만드는 것
 출처 : 네이버 지식백과
3
2.5 시스템 서비스 거부 공격
 시스템이 보유하고 있는 자원을 선점하거나 모두 고
갈하는 방식으로 수행됨
 시스템 서비스 거부 공격은 간단한 C코드나 쉘 스크
립트를 이용하여 가능
 그러나 먼저 시스템 계정을 획득해야 함
 패스워드 공격, 버퍼 오버플로우 공격 등을 통해 시스
템 계정을 획득한 후 시스템 서비스 거부 공격을 수행
하여 서비스를 수행하지 못하도록 함
4
2.5 시스템 서비스 거부 공격
 대표적인 방법
 디스크 채우기, 메모리 고갈, 모든 프로세스 죽이기, 프로세
스 무한 생성의 방법 등
 공격자에 의해 대부분 이루어지나, 사용자들의 프로그래밍
실수로 인해서 발생할 수도 있음
5
2.5.1. 디스크 채우기
 디스크 채우기
 임의의 파일을 만들어 파일 시스템을 가득 차게 하는
방식
 파일 시스템을 가득 차게 하여 시스템을 마비 시김
void main()
{
int ifd;
char buf[8192];
ifd = open("./attckfil", O_WRONLY | O_CREAT, 0777); // 파일 생성
unlink("./attckfil");
// 파일 엔트리삭제
while(1)
write(ifd, buf, sizeof(buf));
// 파일 크기를 무한정 증가
}
[표 2-10] 디스크 채우기의 소스 코드
6
2.5.1. 디스크 채우기
 해결방법
 문제의 프로세스 종료
 문제의 프로세스를 알아내는 것이 어려움
 사용자에게 사용할 수 있는 디스크 공간 제한
 tmp 디렉터리와 같이 모든 사용자가 공통으로 사용하는 디렉
터리에 있는 파일을 대상으로 한 공격에는 무용지물
 시스템 재부팅
 재부팅하는 동안 서비스 불가
7
2.5.2 메모리 고갈 기법
 메모리 고갈 기법
 메모리를 무한정 할당하여 가용 메모리를 없애는 공
격
 메모리 영역에 버퍼를 채우는 점에서 버퍼 오버플로
우의 공격과 유사한 특징을 지님
8
2.5.2 메모리 고갈 기법
 메모리를 고갈시켜 시스템을 마비시킴
 실제 메모리를 모두 사용한 후에 스왑(swap)공간까지
모두 채우다가 결국에는 몇 초 안에 시스템이 제공할
수 있는 모든 메모리를 고갈시킴
void main()
{
char c;
while(1)
c = malloc(1000);
}
// 무한으로 메모리 할당
[표 2-11] 메모리 고갈 방법의 소스 코드
9
2.5.3 모든 프로세스 죽이기 기법
 모든 프로세스 죽이기 기법
 공격자가 루트의 권한을 획득한 상태에서 간단한 스
크립트를 통하여 사용 중인 프로세스를 죽일 수 있음
 FTP 서비스를 제공하는 데몬이 함께 죽으면서 시스템에서
제공하는 모든 서비스가 동작하지 못하게 됨
#!/bin/sh
sync
kill -15 1
// 15 :SIGTERM 종료
// 1 init process
// 파일 시스템의 구조 검사, 파일 시스템 마운트,
// 서버 데몬 실행, 사용자 로그인을 기다림,
// 사용자를 위한 쉘을 띄움
[표 2-12] 모든 프로세스 죽이기 기법의 소스 코드
10
2.5.3 모든 프로세스 죽이기 기법
 프로세스 만들기 기법
 무한의 프로세스를 생성하는 소스 코드를 작성함으로
써 시스템의 프로세스 테이블을 고갈 시킬 수 있음
 일반적으로 커널이 생성될 때 가능한 프로세스의 수를 한정
시켜 놓게 되는데, 이를 넘게 되면 프로세스 테이블이 고갈되
면서 시스템의 모든 서비스가 중단됨
void main()
{
while(1) fork(); // 무한으로 자식 프로세스 생성
}
[표 2-13] 프로세스 만들기 기법의 소스 코드
11
2.6 그 외 공격 방법
 앞에서 설명한 공격들은 공격 도구나 응용프로
그램들을 이용하여 손쉽게 시스템에 침입
 시스템 프로세스 구조를 이용하는 레이스 컨디
션 공격, 컴파일러 종류에 따른 포맷 스트링 공격,
응용프로그램의 코드나 실행파일 자체의 구조를
분석하는 리버스 엔지니어링은 공격자의 특성에
따라 공격 방법이 달라짐
 공격자와 시스템 특성에 따라 변화가 심함
 특정한 보안규칙에 의한 시스템을 보호 어려움
 시스템을 복구하는데 오랜시간이 걸리며 이 시간동안
시스템에 심각한 영향을 끼치게 됨
12
2.6.1 레이스 컨디션을 이용한 공격
 레이스 컨디션(Race Condition)
 여러개의 프로세스나 프로그램을 실행하는 도중에
CPU를 사용하려는 충돌 상황
 두개 이상의 프로세스가 동시에 돌아갈 경우에 서로 CPU를
선점하기 위해서 발생하는 경쟁관계를 말함
 레이스 컨디션을 이용한 공격
 운영체제에서 CPU는 여러 개의 프로세스를 동시에
실행하는 것처럼 시간을 배분하여 실행
 이런 특성으로 인해 시스템을 공격할 수 있는 기회가 발생
13
2.6.1 레이스 컨디션을 이용한 공격
#include <stdio.h>
#include <stdlib.h>
int main()
{
int childpid;
int a, b;
if((childpid = fork()) > 0 { /* Parent process */
for(a = 0; a < 100; a++) printf("O");
exit(0);
}
else { /* child process */
for(b = 0; b < 100; b++) printf("X");
exit(0);
}
}
[결과]
OOOOOXXXXOOXXXOXXOXOXOOOOOXXXXXOXXOO ....
[그림 2-32] 레이스 컨디션 예제
14
2.6.1 레이스 컨디션을 이용한 공격
 유닉스 운영체제에서의 레이스 컨디션을 이용한
공격방법
 유닉스 프로그램이 실행되어 프로세스가 되면 두 개
의 UID(User ID), RUID(Real UID)와
EUID(Effective UID)를 가짐
 RUID : 프로그램을 실행시킨 사용자의 ID로 보통 로그인 후
에 갖게 되는 UID
 EUID : 프로세스 실행 중 파일에 접근하기 위해 갖게 되는
UID로 보통 RUID와 동일하게 설정됨
 그러나 실행파일에 SETUID가 설정되어 있다면
EUID는 프로그램 파일의 소유자 UID로 설정되게 된
다.
15
2.6.1 레이스 컨디션을 이용한 공격
 사용자가 SETUID가 설정된 루트의 프로그램 P를 실행
 사용자는 P를 실행하는 동안 루트의 권한을 갖게됨
 P가 임시 파일을 만든다면, 그 파일은 루트 권한의 UID를 가지는 파
일을 생성함
 임시 파일을 P가 접근하기 전에 다른 시스템 파일로 변경
 덮어쓰기가 가능해지고 루트권한을 획득할 수 있음
16
2.6.1 레이스 컨디션을 이용한 공격
17
2.6.1 레이스 컨디션을 이용한 공격
 레이스 컨디션을 이용한 공격에서 반드시 두 개의 시스
템 콜이 있을 필요는 없음
 SETUID 권한을 갖는 프로그램이 임시파일을 중요한 시
스템 파일로 링크시킨다면 시스템 파일을 수정할 수 있
음
 현재 대부분의 유닉스 계열 운영체제에서는 레이스 컨디
션에 의한 공격이 통하지 않음
 임시 파일을 생성한 이후 임시 파일에 대한 링크의 유무를 검사
함으로써 링크가 설정되어 있는 경우 실행할 수 없게 되어있음
 그러나 이것은 레이스 컨디션에 대한 원천적인 해결 방
법이 아니기 때문에 여전히 다른 영역에서 사용 됨
18
2.6.2 포맷 스트링을 이용한 공격
 포맷 스트링(Format String) 공격
 데이터를 덮어써서 프로그램의 실행 흐름을 변경하는
것
 버퍼 오버플로우 공격과 매우 유사
 포맷 스트링은 printf()와 같은 포맷 함수에서 사용
 포맷 함수는 스트링을 첫 번째 인자로 받은 다음, 포맷
스트링에 따라 여러 인자를 받아들임
19
2.6.2 포맷 스트링을 이용한 공격
 printf(), fprintf(), vprintf(), sprintf()등은 일정한 형
식을 만들 수 있음
 값이나 데이터의 포맷 문자열 지정자(Format specifier)에
의해 메모리 영역을 덮어씌움으로써 버퍼 오버플로우 공격으
로 인한 메모리 영역 변형과 같은 효과를 갖게 됨
printf("출력될 수 : %d\n", sum);
[표 2-14] 포맷 스트링의 예제
20
2.6.2 포맷 스트링을 이용한 공격


포맷 스트링 공격은 포맷 스트링 버그를 갖는 코드를 이용하여 이루어짐
[표 2-16]을 포함한 코드를 실행할 때 명령형 인자로 정상적인 스트링을
입력할 경우에는 그대로 출력
 '%x%x'와 같이 포맷 스트링을 포함한 인자와 함께 프로그램을 실행하면 문자열
을 포맷 스트링이 있는 문자열로 인식하고 16진수 값 두 개를 출력
 공격자는 이 버그를 이용하여 프로그램의 리턴주소를 임의로 선택한 주소로 덮
어쓰게 할 수 있음
 결과적으로 공격을 위한 쉘코드를 실행할 수 있음

실행코드의 정확한 주소와 리턴주소를 구하기 위한 사전작업이 필요
char buffer[512]="";
strncpy(buffer, argv[1], 500);
printf(buffer);
[표 2-16] 포맷 스트링 공격을 위한 코드 예
21
2.6.2 포맷 스트링을 이용한 공격
프로그램에 명령행 인자로서 스택에 있는 리턴주소에 맞게 바이트 개수를
포맷형식에 맞추어 리턴 주소 영역에 쉘 코드의 주소를 포함시킨 스트링을
넣게 되면 그림과 같이 공격 코드가 실행 됨
22
2.6.3 리버스 엔지니어링을 이용한 공격
 리버스 엔지니어링(Reverse Engineering)
 프로그램의 동작과정을 역추적하여 프로그램의 설계
구조를 알아내는 것을 말함
 리버스 엔지니어링을 통해 프로그램의 취약성과 버그
를 새로운 코드로 덮어씌우는 패치를 수행
 복사방지를 제거하거나 패스워드 허가를 무력화 시키
는 크랙으로 이용
 리버스 엔지니어링은 시스템의 보안성을 향상시키기
도 하고 공격도구로도 사용될 수 있음
23
2.6.3 리버스 엔지니어링을 이용한 공격
 리버스 엔지니어링의 대표적인 도구
 디버거
 사용자 모드 디버거
– 응용프로그램 디버깅 가능
 커널모드 디버거
– 디바이스를 비롯하여 운영체제까지 디버깅 가능
– 시스템의 취약성을 공격할 수 있는 툴로 이용될 수 있음
 디컴파일러
 실행 프로그램을 소스코드로 변환해 주는 툴
– Java, .net(C# 등)의 Dynamic한 언어들의 경우 디컴파일러가
일반 개발업무에서도 흔히 사용됨
24
2.6.3 리버스 엔지니어링을 이용한 공격
 리버스 엔지니어링을 이용한 공격 절차
1. 공격자는 이러한 리버스 엔지니어링을 통해 공격대
상 시스템 또는 응용프로그램에 대한 분석을 수행
2. 분석 후 해당 시스템이나 응용프로그램이 갖고 있는
취약점 발견
3. 취약점을 공격할 수 있는 코드를 생성
4. 공격코드를 이용하여 동일한 구조의 시스템이나 같
은 응용프로그램을 사용하는 시스템에 대한 공격 수
행
25
2.6.3 리버스 엔지니어링을 이용한 공격
 단점
 리버스 엔지니어링 기술은 상당한 수준의 공격자의
분석 능력을 필요로 하고 취약점을 찾기까지 많은 시
간이 걸림
 장점
 이로 인해 발견된 취약점은 시스테에 심각한 위험을
끼칠 수 있음
 많은 공격자들에 의해 시도되는 공격방법
26