아메바시큐리티
Section 17. 각종 시스템 보안 위협 및 대응책 본문
Buffer Overflow 공격
버퍼 오버플로우 공격 개요
1) 프로세스 메모리 구조
- Text 영역, Data 영역 , Heap 영역, Stack 영역

Text 영역
- 프로그램 코드와 상수 저으이
- 읽기만 가능한 메모리 영역
- 데이터를 저장하려고 하면 분할 충돌로 인해 프로세스 중지
Data 영역
- 전역 변수와 정적 변수가 저장
Heap 영역
- 프로그래머의 필요에 따라 동적 메모리 호출에 의해 할당되는 메모리 영역
Stack 영역
- 함수 인자 값, 함수 내의 지역 변수, 함수의 변환 주소 등이 저장되는 영역
- 상위메모리에서 하위 메모리로 주소 데이터가 저장
2) 스택 프레임 구조

3) Buffer Overflow 공격의 기본 개념
- 버퍼 또는 데이터 저장 영역에 할당된 용량보다 더 많은 입력이 위치하면 다른 정보를 변경할 수 있는 조건
- 공격자는 이런 조건을 이용하여 시스템을 중지시키거나 시스템의 제어를 갖기 위한 특별한 코드를 삽입
- 프로세스가 정해진 크기의 버퍼 한계를 벗어나 이웃한 메모리 위치에 데이터를 겹쳐 쓰려고 시도하는 것과 같은 프로그래밍 오류의 결과로 발생
4) 공격 원리
int main(int argc, char *argv[]){
char buff[10];
stcpy(buffer, argv[1]);
printf("%s\n", buffer);
}
이 프로그램은 실행시 인자값을 넣어주어 해당 인자값을 출력해주는 프로그램이다.
해당 코드에서 버퍼 오버플로우는 "strcpy(buffer, argv[1])" 에서 일어난다
Stack buffer overflow
- 보통 SetUID 가 설정된 루트 권한의 프로그램을 대상으로 공격
- 스택에 정해진 버퍼보다 큰 공격 코드를 삽입하여 반환주소를 변경함으로써 임의의 공격 코드를 root 권한으로 실행하도록 하는 방법
1) Shellcode
- 많은 버퍼 오플로우 공격의 핵심 요소는 오버플로우가 발생하는 버퍼에 저장되는 공격자의 코드로 실행제어를 이동시키는 것
- 이 코드를 shell 코드라고 한다.
- 사용자 명령어 라인의 해석기인 셸로 제어를 넘기고 공격당한 프로그램의 권한으로 시스템의 다른 프로그램이 접근하기 때문
2) stack buffer overflow 공격 절차
1단계 : 공격 셸 코드를 버퍼에 저장
2단계 : 루트 권한으로 실행되는 프로그램의 특정 함수의 스택 반환주소 버퍼를 오버플로우시켜서 공격 셸 코드가 저장되어 있는 버퍼의 주소로 덮어 씌우기
3단계 : 특정 함수의 호출이 완료되면 조작된 반환 주소로 셸코드의 주소가 반환되어 셸 코드가 실행되고, 루트 권한을 획득
Heap overflow
- 힙은 전역 데이터 위에 위치하며 메모리 위 방향으로 커짐
- 힙에 요청되는 메모리는 레코드의 연결리스트와 같은 동적 데이터 구조를 위해 사용
- 레코드가 오버플로우에 취약한 버퍼를 가지고 있다면 연속된 메모리가 손상 가능
- 실행제어를 쉽게 이동시킬 수 있는 반환주소는 없음.
- 할당된 공간이 함수에 대한 포인터를 포함하고 있다면.... 공격자는이 주소를 변경하여 겹쳐 쓴 버퍼에 있는 셸코드를 가리키도록 할 수 있음.
Buffer overflow 공격 대응책
컴파일 시간 방어
- 새 프로그램 내에서 공격을 저지하도록 프로그램을 강화하는 것을 목표로 함
- 프로그램을 컴파일할 때 검사하여 버퍼 오버플로우를 방지하거나 발견하는 것
- 버퍼 오버플로우를 허용하지 않는 Java, ADA, Python과 같은 현대화된 고급 프로그래밍 언어를 사용하여 소프트웨어를 작성하면 방지 가능
- C나 C++ 과 같이 메모리 접근에 높은 자유도를 가진 프로그래밍 언어를 사용하는 경우, 반드시 할당된 버퍼 크기를 초과하지 않도록 로직을 설정해야함
- 기존의 프로그램을 다시 컴파일 해야하는 단점이 있음.
1) 프로그래밍 언어의 선택
- 현대의 고급 수준의 프로그래밍 언어를 사용하여 프로그램을 작성하면 방지 가능
- 컴파일러가 범위 검사를 강제로 수행하는 코드를 자동으로 추가했기 때문
2) 안전한 코딩 기법들
- 사용을 자제해야하는 함수 : strcat, strcpy, gets, scanf, sscanf, vscanf, vsscanf, sprintf, vsprintf, gethosbyname, realpath
- 사용을 권장하는 함수 : strncat, strncpy, fgets, fscanf , vfscanf, snprintf, vsnprintf
3) 언어 확장과 안전한 라이브러리 사용
- 표준 문자열 라이브러리를 안전한것으로 교체하는 방법
- Libsafe. 표준 의미를 구현하면서 추가적으로 복사 연산이 스택 프레임 내부의 지역 변수 공간을 넘어가지 않도록 검사하는 기능을 구현
4) 스택 보호 메커니즘
- 함수의 진입과 종료 코드를 조사하고 함수의 스택 프레임에 대해 손상이 있는지를 검사하는 것.
- 만약 검사에 이상이 있다면 프로그램 종료
- 컴파일러가 프로그램의 함수 호출 시에 ret 앞에 canary 값을 주입하고 종료 시에 canary 값이 변조되었는지 여부를 확인하여 버퍼 오버 플로우를 탐지
실행 시간 방어
- 운영체제의 업데이트로 배포할 수 있음
- 실행 가능 주소 공간 보호(Executable Address Space Protection) 기법과 주소 공간 임의화(Address Space Randomization)기법 사용
Executable Address space Protection
- 실행 코드가 프로세스 메모리상의 특정위치에서만 실행될 수 있게 함으로서 공격자가 스택 버퍼에 주입한 실행코드를 원천적으로 실행될 수 없게 만드는 방법
- 대부분의 공격은 스택 버퍼에 실행 코드를 주입하므로 재컴파일 없이 기존 프로그램들을 보호하는데 효과적
Address Space Randomization
- 스택 버퍼가 위치하는 주소 공간을 메모리 내에서 임의적으로 배치함으로써, 공격자가 스택 버퍼 속에 주입한 실행코드의 주소를 예측할 수 없게 만드는 방법
- 실행코드로의 반환 주소 변경이 매우 어렵게 되어 스택버퍼오버플로우 공격이 어려워짐
1) ASLR과 NOP
Address Space Layout Randomization
- 프로세스 주소 공간에 있는 중요 데이터 구조의 위치를 조작하는 방법
- 공격자는 셸 코드로 제어를 넘기기 위해 공격에 사용할 적당한 반환주소를 결정하는데 예측된 주소를 사용
- 이러한 예측을 어렵게 하는 방법으로, 각 프로세스 안의 스택이 임의의 다른 곳에 위치하도록 변경하는 방법
NOP sled
- NOP는 아무 기능도 수행하지 않는 명령어를 의미
- 기계어 코드 핵사값이 0x90
- 빈공간을 채우기 위한 명령어로, 해당 명령어를 만나면 아무런 동작 없이 다음 명령어로 넘어감
- 셸 코드 실행을 위해 NOP 명령을 삽입하는 이유는 Return Address가 셸코드 주소를 정확히 가리켜야 하는데 프로그램 실행시마다 셸 코드 주소가 조금씩 바뀔 수 있기 때문에 정확한 주소를알기 어려움
- 실행 확률을 높이기 위해 다수의 NOP 명령을 셸 코드 앞에 삽입하여 해당 영역을 가리키기만 하면 NOP을 타고 최종적으로 셸 코드가 실행되도록 하기 위해 공격시 NOP을 삽입한 셸 코드를 사용
2) 실행 가능 주소 공간의 보호
- 스택과 힙을 Non-Executable Stack으로 만듦으로써 기존 프로그램을 위한 여러가지 유형의 버퍼 오버플로우에 대한 방어를 제공
- 최신 운영체제는 이 기법을 포함
- 프로그램이 실행 가능 스택을 사용안함으로 설정하는 방법은 /etc/system 파일 설정을 통해 가능
set noexec_user_stack = 1
set noexec_user_stack_log = 1
* 우회 방법
return to libc 공격사용.
- 스택에 있는 ret 주소를 실행 가능한 임의의 주소 (libc 영역 주소)로 돌려 원하는 함수를 수행하게 만드는 기법.
- 메모리에 적재된 공유 라이브러리는 스택에 존재하는 것이 아니므로, 우회 가능
Format String Attack
- printf, fprintf, sprintf와 같이 포맷스트링을 사용하는 함수를 사용할 때, 외부로 입력된 값을 검증하지 않고, 입출력 함수의 포맷 문자열을 그대로 사용하는 경우 발생할 수 있는 취약점
- 포맷문자열을 이용하여 취약한 프로세스를 공격하거나 메모리 내용을 읽거나 쓰기 가능
- 공격자는 취약한 프로세스의 권한을 획득하여 임의의 코드를 실행 가능
공격 원리
- 데이터의 형태에 대한 불명확한 정의로 인해 발생
#include <stdio.h>
int main(int argc, char **argv[]){
printf("%s", argv[1]);
}
- 여기서 포맷 스트링 공격은 %s를 변경하는 것
- 포맷 스트링을 지정하지 않고, 사용자 입력을 통해서 포맷 스트링이 결정된다면, 이를 조작하여 메모리 내용을 참조하고 특정 영역의 값을 변경할 수 있음.
예시)
공격자가 스택 프레임 구조를 고려하면서 %x를 통해 메모리 내용 참조 및 원하는 위치로 이동한 후 %n을 통해 Return Address를 악성코드가 위치한 주소로 변조하여 악성코드를 실행할 수 있음.
취약점 위협 요소
1) 프로그램의 파괴
- 가장 간단한 공격
- core를 덤프하는 데몬을 죽이는데 유용하게 사용 가능
- DNS 스푸핑을 할 때 어떤 서비스가 반응하지 않도록 하는데도 유용
2) 프로세스 메모리 보기
- 포맷 함수의 응답을 볼 수 있다면 그것으로부터 유용한 정보를 수집 가능
- 공격자는 결과를 통해 포맷 스트링이 무엇을 하며, 프로세스 배치가 어떻게 생겼는지 대략적인 내용 획득 가능
3) 메모리 덮어 쓰기
- 어떤 프로세스의 명령 통제권 장악 가능
보안 대책
- printf(), snprintf() 등 포맷 문자열을 사용하는 함수를 사용할 때는, 사용자 입력값을 직접적으로 포맷 문자열로 사용하거나, 포맷 문자열 생성에 포함하지 않는다.
- 포맷 문자열을 사용하는 함수에서 입력값을 사용할 때는 포맷 스트링을 변경할 수 있는 구조로 쓰지 않는다.
- 특히, %n, %hn은 공격자가 이를 이용해 특정 메모리 위치에 값을 변경할 수 있으므로, 포맷 스트링 매개체로 사용하지 않는다.
- 사용자 입력값을 포맷 문자열을 사용하는 함수에 사용할 때는 가능한 %s 포맷 문자열을 지정한다.
Race Condition Attack
- 둘 이상의 프로세스나 스레드가 공유자원에 동시에 접근할 때 접근하는 순서에 따라 비정상적인 결과가 발생하는 조건상황
- 프로세스가 임시 파일을 만드는 경우, 악의적인 프로그램을 통해 그 프로세스의 실행중에 끼어들어 임시파일을 목적파일로 연결(심볼릭 링크)하여 악의적인 행위를 하는 것
- 프로세스가 setuid 설정이 되어, root 권한으로 실행된다면 권한 상승을 통한 중요 자원에 접근 가능
파일 링크
1) 하드링크
- 하드 링크를 간단히 말하면 똑같이 복사된 파일을 만드는 것.
- 하드 링크 파일을 수정하면 원래 파일도 수정됨
- 두 파일 중 하나를 삭제하면 파일의 내용은 바뀌지 않고 링크만 줄어듬
- 두 파일이 각각 동일한 수준의 데이터를 가지며 서로 그 데이터를 동기화
- 링크하고자 하는 파일이 다른 파티션에 존재하면 안됨
2) 심볼릭 링크
- 데이터가 있는 파일이 처음부터 하나뿐이고, 심볼릭 링크는 단지 원본 파일 데이터를 가리키는 링크 정보만 가져옴
- 원본 파일과 심볼릭 링크는 원본 파일이 상제되더라도, 원본 파일의 이름과 위치를 기억하고 계속 그 파일을 바라보는 상태로 남음
- 삭제된 원본 파일 대신 처음 원본 파일과는 다르지만 같은 경로에 같은 파일명으로 생성하면, 심볼릭 링크 파일은 새로 생성된 파일에 여전히 링크 파일로 존재하게됨
심볼릭 링크와 레이스 컨디션 공격
- 대상 : 소유자가 root이고, SetUID 비트를 가지며, 임시파일을 생성하는 파일
- 조건 : 소유자 root, SetUID, 생성되는 임시 파일의 이름을 알고 있어야 함
- losf 명령어로 특정파일에 접근하는 프로세스 목록을 확인할 수 있으며, 특정 프로세스가 사용하는 파일 목록을 뽑을 수 있음. 이를 통해 임시파일이름을 확보
- 임시 파일이름으로 프로그램이 실행되기 전의 심볼릭 링크 파일을 생성
- 해당 심볼릭 링크 파일은 관리자 권한으로만 접근 가능한 /etc/passwd 파일 등을 가르키고 있으면 됨
레이스 컨디션 공격에 대한 대응책
- 프로그램 로직 중에 임시파일을 생성한 후, 임시파일에 대한 심볼릭 리크 설정 여부와 권한에 대한 검사 과정을 추가
- 가능하면 임시파일을 생성하지 않는 것이 좋음
- umask를 최하(022) 정도로 유지하여 임시로 생성한 파일이 공격자에 의해 악의적으로 삭제되지 않도록 해야함
Backdoor
- 시스템의보안이 제거된 비밀 통로
- 서비스 기술자나 유지보수 프로그래머의 접근 편의를 위해 시스템 설계자가 고의적으로 만들어 놓은 통로
- 현장에서 서비스 기술자가 공급사 유지보수 프로그래머의 업무 편의성을 향상 시켜줌
- 악의적으로 만는 통로의 대표 예시 : Back orifice
- 해킹 프로그램의 일종으로 PC에 내장되어 사용자 몰래 사용자의 정보를 저장, 유출하기 위한 프로그램
리눅스/유닉스의 백도어
- root 권한으로 운영되는 경우는, httpd 데몬이 시작 프로그램으로 자동 실행되거나 관리자가 보안에 관심 없는 경우
- root 권한으로 운영되는 http 데몬은 보안에 매우 취약
- 인터넷을 사용하는 동안에는 차이는 못느끼지만, root 계정으로 운영되는 웹서버에 접속하고 있을 때는, root ㅜ건한으로, nobody 계정으로 웹에 접속하고 있을 때는 nobody 권한으로 시스템에 자료를 요청하는 것
백도어 탐지와 대응책
1) 현재 동작중인 프로세스 확인
- 백도어가 아닌 정상 프로세스를 아는 것도 중요
- 윈도우나 유닉스 시스템의 정상 프로세스를 알아두는 것이 좋음
- 윈도우 프로세스의 경우 이름을 인지해두면 웜/바이러스나 백도어에 대응하는데 큰 도움이 됨.
웜/바이러스나 백도어가 가장 애용하는 것은 Csrss와 Svchost 프로세스
Csrss.exe(Client/Server Runtime Subsystem:Win32)
- 윈도우 콘솔을 관장하고, 스레드를 생성/삭제하며 32 비트 가장 MS_DOS 모드를 지원하는 프로세스
Explorer.exe
- 작업 표시줄, 바탕화면 같은 사용자 셸을 지원하는 프로세스
Lsass.exe(Local security Authentication Server)
- Winlogon 서비스에 필요한 인증 프로세스를 담당, 인증 성공시 초기 셸을 실행
- 사용자별 엑세스 토큰을 생성하여 다른 프로세스들이 해당 토큰을 상속받게 됨
Mstask.exe(Windows Task Scheduler)
- 시스템에 대한 백업이나 업데이트 등에 관련된 작업의 스케줄러 프로세서
Smss.exe(Session Manager SubSystem)
- 사용자 섹션 시작 기능을 담당하는 프로세스
- Winlogon, Win32을 구동하고, 시스템 변수 설정
- Smss는 Winlogon이나 Csrss가 끝나기를 기다려 정상적인 Winlogon, Csrss 종료 시 시스템 종료
Spoolsv.exe(Printer Spoller Service)
- 프린터와 팩스의 스풀링 기능을 담당하는 프로세스
Svchost.exe(Service Host Process)
- DLL에 의해 실행되는 프로세스의 기본 프로세스
- 따라서 한 시스템에서 scvhost 프로세스를 여러개 볼 수 있음
Services.exe(Service Control Manager)
- 시스템 서비스를 시작/정지하여 그들간의 상호 작용하는 기능을 수행하는 프로세스
Taskmgr.exe(Task Manager)
- Window 작업관리자 자신의 프로세스
Winlogon.exe (Windows Logon Process)
- 사용자 로그인/로그오프를 담당하는 프로세스
시스템 자원 고갈 공격(시스템 서비스 거부 공격)
- DoS 공격
- 시스템이 보유하고 있는 자원을 선점하거나 모두 고갈하는 방식으로 사용
- 디스크 채우기, 메모리 고갈, 모든 프로세스 죽이기, 프로세스 무한 생성 등...
System resource Exhaustion Attack 종류
1) 가용 디스크 자원 고갈 공격
#include <unistd.h>
#include <sys/file.h>
void main(){
int fd;
char buf[1000];
fd = create("/root/temfile", 0777)
while(1) write(fd,buf,sizeof(buf));
}
위와 같은 코드를 사용하여 공격하는 방식.
파일을 생성하고 이 파일에 1,000 바이트씩 계속 써 나가는 프로그램
2) 가용 메모리 자원 고갈 공격
- 다른 특별한 함수 없이, malloc 함수를 통해 메모리 할당만 계속 수행하는 프로그램
- 디스크 고갈 공격보다 시스템 자원을 더 많이 차지하여 터미널 창 하나만 띄우는데도 몇분을 소모하게 하는 공격
#include <stdio.h>
void main(){
char *m;
while(1)
m = malloc(1000);
}
3) 가용 프로세스 자원 고갈 공격
- fork() 라는 프로세스 할당 함수만 무한대로 사용하는 방법
- 당장 시스템 자원을 많이 차지하지 않지만, 가용 프로세스가 가득 차면 문제가 발생
- 심한 경우 운영체제를 재 설치 해야할 수도 있음.
#include <unistd.h>
void main(){
while(1)
fork();
return 0;
}
4) 프로세스 죽이기 공격
- 공격자가 루트 권한을 획득한 상태에서 스크립트를 통해 사용중인 프로세스를 죽이는 기법
- 웹 또는 FTP 서비스를 제공하는 데몬이 함께 죽으면서 시스템에서 제공하는 모든 서비스가 동작하지 못하게됨
#!/bin/sh
sync
kill -15 1
리버스 엔지니어링
- 장치나 시스템의 구조를 분석하여 원리를 발견하는 과정
- 상업적이나 군사적으로 하드웨어를 분석하는것에서 시작
- 어떤 대상을 생산하기 위한 지식이나 절차가 거의 없는 상태에서 최종 제품을 통해 설계하는 과정을 역추론하는 것이 목적
리버스 엔지니어링 공격
- 공격 대상 시스템 또는 응용프로그램에 대한 분석 수행
- 분석 이후, 해당 시스템이나 응용 프로그램이 갖고 있는 취약점을 찾아 공격하는 방식으로 주로 사용
리버스 엔지니어링에 대한 대응책
- 프로그램 코드를 어렵게 하는 난독화 기술
- 코드의 난독화를 통해 가독성을 낮추어, 분석을 어렵게 하거나 패턴/시그니처 기반 보안 장비의 탐지를 위회하기 위해 사용
소스코드 난독화
- C/C++/JAVA 등의 프로그램 소스코드를 알아보기 힘들게 만드는 기술.
바이너리 난독화
- 생성된 바이너리를 역공하을 통해 분석하기 어렵게 변조하는 기술
기타 시스템 보안위협 및 대응책
Rootkit
- 시스템에 설치되어 그 존재의흔적을 최대 한도로 숨기면서 공격자가 언제든지 시스템에 관리자 권한으로 접근할 수 있도록 비밀통로를 지속적으로 유지시켜주는 일련의 프로그램 집합
- 공격자는 운영체제의 모든 기능과 서비스를 제어할 수 있게 됨.
- 자신의 존재를 숨기기 위해 시스템을 변경하여, 루트킷의 탐지와 행위를 어렵게 한다.
1) Rootkit의 확인
- 공격자는 자신의 행동을 숨기기 위해 정상적인 프로그램을 대신하도록 바이너리 파일을 변조시키는 경우가 많음
- ls 명령어를 수정하여 자신의 프로세스가 보이지 않도록 하는 등의 해우이
- rootkit을 찾기 위해서는 시스템 프로그램의 파일 크기, 생성시간 등 메타데이터를 확인
- /bin/usr/bin에서 다른 프로그램이 기본적으로 설치된 시간과 다르게 변경된 파일이 있는지 확인
- 트로이목마로 자주 변조되는 ls, ps 등과 같은 파일의 경우 똑같은 OS 의 다른 시스템 파일 사이즈를 비교하여 변조 여부 확인
2) 논리폭탄(logic bomb)
- 특정한 사건이 발생할 때 프로그램이나 일련의 코드를 실행
예시) 네트워크 관리자는 그가 해고되었다면 기업의 전체 데이터 베이스를 삭제하도록 프로그램된 논리폭탄 설치 가능
- 사용자가 특정한 행위를 수행할 때, 그것의 페이로드 실행을 활성화하는 많은 유형의 트리거를 가질 수 있음
- 공격자가 시스템에 포렌식활동일 실행되면 다 지우도록 논리폭탄을 심어둔다면 이것으로 수사를 어렵게 할 수 있음
'정보보안기사(필기)' 카테고리의 다른 글
| Section 19. 네트워크 개요 (0) | 2021.03.03 |
|---|---|
| Section 18. 최신 보안 주제들 (0) | 2021.03.02 |
| Section 16. 서버 보안 관리 (0) | 2021.03.01 |
| Section 15. 유닉스/리눅스 서버 보안 (0) | 2021.03.01 |
| Section 14. 윈도우 서버 보안 (0) | 2021.02.26 |