2016년 8월 12일 금요일

정보보안기사 정리 22 - 시스템 공격

시스템 공격  - 버퍼오버플로우(스택 버퍼오버플로우 공격, 힙 오버플로우 공격) 공격 및 대응, 포맷스트링 공격 및 대응, 레이스 컨디션 공격 및 대응
백도어, 시스템 자원 고갈 공격, 리버스엔지니어링, 루트킷, 메모리 재사용 개념


1. Buffer Overflow 공격
- 종류: 스택 버퍼 오버플로우 공격, 힙 오버플로우 공격
- 프로세스 메모리 구조는 Text, Data, Bss, Stack, Heap으로 구성되어 있음.
- C/C++ 을 사용하는 프로그램에서 많이 발생하며 정해진 크기의 버퍼를 넘어서는 입력값으로 인해 이웃한 메모리 위치에 입력값을 기록함으로써 공격하는 방법.
- 공격원리 예
  void main(int argc, char * argv[]){
     char buffer[10];
     strcpy(buffer, argv[1]);
     printf("%s", buffer);
  }
  실제 공격은 strcpy에서 발생함. strcpy 함수는 입력된 인수의 경계를 체크하지 않음.
  즉, 인수의 크기가 10바이트를 넘지 말아야하지만 넘어서는 입력이 들어와도 Stack에 쓰는 것을 이용한 것.
- 오버플로우 공격의 핵심은 오버플로우가 발생하는 버퍼에 저장되있는 공격자의 코드로 실행제어를 이동시키는 것임. 이 코드를 쉘코드(Shellcode) 라고 함.
  이를 통해 사용자 명령어 라인의 해석기인 쉘로 제어를 넘기고 공격당한 프로그램의 권한으로 시스템의 다른 프로그램에 접근함.


2. 스택 버퍼 오버플로우 공격
- 스택은 함수처리를 위해 사용되는데 함수를 호출할 때 함수의 지역변수, 매개변수, 함수 처리 종료 후 돌아갈(반환될) 주소 등이 스택에 저장됨.
  스택 버퍼 오버플로우 공격은 스택에 정해진 버퍼보다 크기가 큰 공격코드를 삽입하여 함수의 반환 주소를 변경하여 공격하는 방법.
  보통 SetUID가 설정된 루트 권한의 프로그램을 공격대상으로 하여 공격 코드를 루트권한으로 실행하도록 함.

* 참고
- http://inhack.org/wordpress/?p=2932 (스택 버퍼 오버플로우로 쉘 따내는 예)


3. 힙 오버플로우
- 힙에 할당된 버퍼들에 문자열 등이 저장되면서 할당된 메모리 사이즈를 초과하여 문자열 등이 저장되는 것.
- 힙에 요청되는 메모리는 연결리스트같은 동적 데이터 구조를 위해 사용됨.
  스택과 다르게 실행 제어를 쉽게 이동시킬 반환주소는 없지만 힙에 할당된 공간이 함수에 대한 포인터를 포함한다면 공격자는 이 주소를 변경하여
  버퍼에 있는 공격자의 쉘코드를 가리키도록 할 수 있음.


4. 버퍼 오버플로 공격의 대응
- 컴파일 시간 방어: 프로그램 내에서 공격 저지하도록 강화함.
  ① 오버플로우 허용안하는 고급언어 사용(요즘 것들은 범위 검사 코드를 컴파일러가 넣어줌)
  ② 안전한 표준 라이브러리 사용(Libsafe 라이브러리 같은 것)
  ③ 스택 프레임 손상 탐지 추가 코드 넣기(함수의 진입과 종료 코드 조사하여 스택 공격 여부 파악)
  ④ 안전한 함수 사용: strcpy, strcat 등의 함수들은 입력크기에 대한 제한이 없음. 따라서 strncat, strncpy 같은 함수를 사용해야 함.

- 실행시간 방어: 컴파일 시간 방어 기법들은 기존 프로그램을 다시 컴파일 해야 하기 때문에 OS 업데이트로 배포되는 실행 시간 방어법이 좋음.
  ① 가상 주소 공간의 메모리에 변경을 수행하여 타깃 버퍼의 위치를 예측하기 어렵게 함.
  ② 실행 가능 주소 공간 보호: 스택과 힙을 실행 불능으로 만들어 공격을 방어함. 자바 런타임 시스템 같은 컴파일러의 경우 스택에
      실행코드를 두기에 문제가 되지만 시스템 보호에 좋기때문에 가장 좋은 대응 방법 중 하나로 인식됨.
  ③ 가드 페이지: 프로세스 주소 공간 내의 메모리 임계 구역 사이에 가드 페이지 삽입. MMU가 체크하여 가드페이지 접근하면 프로세스 종료시킴.


5. 포맷스트링 공격
- C언어의 printf() 등의 함수에서 사용되는 문자열의 입출력 형태를 정의하는 문자열을 포맷스트링이라고 함.
  포맷 스트링의 약점은 입력된 값을 검증하지 않고 포맷스트링의 입출력으로 사용하는 것.
- 이를 통해 프로세스 공격, 메모리 내용 읽기 및 수정, 프로세스 권한 획득을 통한 코드 실행이 가능.
  printf, fprintf, sprintf에서 %d, %f, %c 같은 포맷 스트링 식별자를 사용하는데, 입력되는 스트링을 검사안하고 바로 출력 한다.
  (%lf: 실수형, %u: 양의 정수, %x:16 진수, %n: *int(사용된 총 바이트 수), %hn: 2바이트 단위)
- 정적 포맷 스트링: printf(“%s”, &sad); 에서는 포맷 스트링 공격 안됨.
  하지만 printf(sad) 같이 된 경우에는 데이터의 형태가 모호해져 공격이 가능해짐.
- 공격 예
  int main(int argc, char **argv) {
    int var1=10; int var2=11;
    printf(argv[1]);
    return 0;
  }
  파일이름이 fm. 실행: ./fm “%8x %8x %8x %8x“  으로 실행하면 출력은 a b sdf423 sdfsdf4 같이 나옴.
  의미: 이것은 포맷스트링을 외부 입력으로 넣은 것으로 %x(%x는 16진수 출력하는 것.)를 통해 스택의 메모리 정보를 읽게 한 것.
  스택에 10, 11이 쌓여 있고, %x로 16진수를 출력하는데 16진수 10은 a 이므로, a b가 출력된 후 스택에 있는 메모리 정보인 sdf423 sdfsdf4 가 이어서 출력됨.
- 왜 위험한가: %x를 통해 메모리 내용 참조 또는 원하는 위치(RET 영역)로 이동한 후 %n으로 Return Address를 악성코드가 위치한 주소로 변조해 실행가능.
  * RET 영역: 스택에서 함수의 return address가 저장된 메모리 공간.
- 대응
  ① fprintf, sprintf, snprintf, vfprint 등과 같은 것 쓰지 않기. ‘f’가 붙는 것은 사용자가 좀더 유용한 제어를 하기 위한 것. 이런 함수들이 동적 포맷스트링 함수.
  ② printf(argv[1]) 이 아닌 printf(“%s”, argv[1]) 과 같이 형태를 지정.


6. 레이스 컨디션 공격 (Race Condition Attack)
- Race condition은 둘 이상의 프로세스나 스레드가 공유자원에 동시에 접근할 때 발생하는 상황을 말함.
- 실행되는 프로세스가 임시파일을 만들 경우 악의적인 프로그램을 통해 그 프로세스 실행 중에 끼어들어 임시파일을 목적파일로 연결(심볼릭링크)하여
  악의적인 행위를 하게 하는 것을 Race Condition Attack 이라고 함.
  만약 프로세스가 setuid 설정으로 root 권한으로 실행되면 중요자원도 쉽게 접근하여 문제가 될수 있음.(심볼릭 링크에 의한 쓰기를 발생시키는 것)
- 레스트 컨디션 공격의 대상은 소유자가 root 이고, SetUID 비트를 가지며, 임시 파일을 생성하는 파일임.
  이러한 파일의 실행 절차: 일반 권한으로 프로그램 실행 → SetUID로 프로세스 권한 상승 → 관리자권한으로 전환 → 임시파일 생성 → 프로그램 동작 및 처리 → 프로그램 종료
- 위와 같은 프로그램에 레이스 컨디션 공격을 하기 위해선 반드시 임시 파일의 이름을 알고있어야 한다는 것이다.
  리눅스에는 lsof(list open files) 명령어가 있어 특정 파일에 접근하는 프로세스 목록을 알 수 있으며, 특정 프로세스가 사용하는 파일 목록도 알아낼 수 있음.
- 공격 과정 예
  파일에 데이터를 쓰는 실행파일이 있고, 이 파일은 SetUID 비트를 갖고, 임시파일(예: temp.dat)을 생성한다고 했을 때,
  공격자는 "ln -s /etc/shadow /tmp.dat" 으로 shadow 파일을 조작하기 위해 임시파일 tmp.dat에 심볼릭 링크를 생성함.
  임시파일로 출력하면 실제로는 심볼릭 링크를 통해 shadow 파일에 출력이 되게 됨.
- 대응 방안
  ① 가능하면 임시파일 생성하지 않아야 함.
  ② 파일 생성시 이미 동일한 파일이 있으면 파일 생성또는 쓰기를 금지해야 함.
  ③ 사용하고자 하는 파일에 링크가 있으면 실행을 중단함.
  ④ 중간에 심볼릭 링크 설정 여부와 권한 검사 과정 추가함.

* 파일 링크 종류
- 하드 링크: 원본의 복사된 파일을 만들고 동기화 시킴. 같은 파티션 내의 파일만 가능. 하나를 수정하면 다른 하나도 똑같이 수정 됨.
- 심볼릭 링크: 원본 파일과 이를 가리키는 링크 정보만 가지는 심볼릭 링크 파일 두개가 존재(윈도우의 바로가기 같은 것).


7. 백도어(관리자 훅, 트랩도어)
- 업무의 편의성이나 유지보수를 위해 보안이 제거된 비밀통로이지만 악의적인 목적으로 만들어 놓는 경우도 있음.
- 리눅스/유닉스에서 백도어는 웹 서비스를 제공하는 http 데몬은 80포트를 이용하는데 데몬의 권한이 root 또는 일반 사용자로 운영됨
  어떤 계정이냐에 따라 공격자가 할 수 있는 일이 달라짐.
- 대응
  ① 열린 포트 확인, 유닉스/리눅스의 경우 SetUID 파일 검사를 하여 파일들의 무결성 검사를 수행한다.
  ② 각 파일들의 무결성 검사(대게 MD5 같은 해시 함수 사용)로 변경 내역 확인.
  ③ 로그 분석


8. 시스템 자원 고갈 공격(DoS)
- 간단한 C코드나 쉘 스크립트로 공격하며, 시스템의 계정을 획득해야 할 수 있음.
- 디스크 채우기, 메모리 고갈, 프로세스 죽이기, 프로세스 무한 생성 등의 공격이 있음.
- 가용 디스크 자원 고갈 공격
  void main(){ int fd; char buf[1000]; fd = create("/root/tempfile", 0777); while(1) write(fd, buf, sizeof(buf)); }
  대응: 재부팅, 디스크 공간 제한
- 가용 메모리 자원 고갈 공격
  while(1) m = malloc(1000);  같이 메모리를 할당함.
- 가용 프로세스 자원 고갈 공격
  while(1) fork();  같이 하면 일반적으로 커널이 생성될 때 설정한 가능한 프로세스의 수를 넘어서게 만들어 프로세스 테이블이 모두 고갈되어 서비스가 중단됨.
- 대응책: 시스템에 대한 무결성 체크, 자원에 대해 요청가능한 할당량 설정


9. 리버스 엔지니어링
- 이미 만들어진 프로그램의 원리를 이해하고 구조를 분석하여 유사한 프로그램을 만드는 것.
  오류 제거, 동작 문제 파악 등을 위해서도 하지만 바이러스, 웜, 백신을 만드는데 사용하기도 함.
- 대응책
  ① Packing: 원본을 PE 파일(Potable Executable, 실행 파일 형식)로 압축하고 암호화하여 저장.
  ② 안티 디버깅: 리버스 엔지니어링에 사용되는 디버거를 탐지함.
  ③ 타이밍 체크: 프로세스는 디버깅 중일 때 CPU 연산시간이 오래 걸림. 따라서 RDTSC 명령어로 두 구간 사이의 타임스탬프가 길면
      디버깅 중으로 판단하고 프로그램이나 디버거 종료.
  ④ 쓰레기 코드 삽입 및 코드 치환


10. 루트킷(rootkit)
- 설치된 흔적을 숨기고 비밀통로를 지속적으로 유지시켜주는 일련의 프로그램들의 집합
- 파일 조작, 타켓 디바이스 모니터링, 네트워크 트래픽 송수신 등 가능
- 시스템 프로세스, 파일 레지스트리를 모니터 하고 보고하는 메커니즘을 조작해 자신을 숨김
- 참고: http://proneer.tistory.com/entry/%EB%A3%A8%ED%8A%B8%ED%82%B7Rootkit%EC%9D%B4%EB%9E%80


11. 메모리 재사용
- 프로세스에게 메모리 할당 후 해제하는데, 메모리에는 사용한 프로세스 정보가 남아 있음.
  이것에 접근하여 읽으면 데이터가 읽힘
- 대응책: 메모리를 ‘0’으로 초기화, 페이징 또는 스왑 파일들도 초기화.

댓글 없음:

댓글 쓰기