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