Blog | Tag | Local | Guest | Login | Write |  RSS


이전글 [UNIX 보안 기초 -2-] UNIX 파일시스템 초간략




  • DoS, DDoS - local DoS, remote DoS  (서비스를 불가능하게 하는 공격)
  • 시스템 오류 - 환경변수의 취약성, ptrace, race condition, 잘못된 퍼미션
  • 프로그램의 오류 - setuid, daemon 의 오류 이용, BOF(Buffer Overflow), FSB(Format String Bug)
  • Network의 취약성 - Spoofing, Sniffing
  • 기타 - passwd cracking

다음은 간단한 해킹시도의 원리들이다.

1. DoS, DDoS

DoS (Denial of Service) 공격이란 다중작업을 지원하는 운영체제에서 발생할 수 있는 공격 방법. 구체적으로 한 프로세스가 시스템의 리소스를 독점하거나, 모두 사용해 버리거나 또는 파괴하여서 올바른 서비스를 제공하지 못하도록 하는 공격. 공격 장소에 따라 나누면 local DoS, remote DoS 가 있다.

local DoS - 공격자가 시스템에 들어와서 시행하는 방법. 실제 이를 위해서는 간단한 C코드나 쉘 스크립트를 이용하면 된다. 하지만 이러한 공격법의 문제점은 반드시 시스템에 계정이 있어야 한다는 점.
ex) 디스크 채우기, 메모리 고갈, 프로세스 만들기. Explorer 4.0 자기참조프레임

remote DoS - 일반적으로 이들은 특정 포트를 listen하고 있는 프로세스를 마비시키거나 운영체제의 네트워크 기능 자체를 오동작하게 하거나 LAN 자체를 마비시키는 것으로 분류할 수가 있다. 
ex) ping attack, ICMP(Internet Control Message Protocol)공격, 메일폭탄 등.


2. 환경변수 이용


다중 사용자를 지원하는 서버용 시스템은 각 사용자에 맞는 환경변수를 지정하여서 지정한 환경 하에서 사용자가 좀더 편리하게 작업을 할 수 있도록 도와주고 있다.

PATH - PATH 환경변수는 실행파일이 위치한 디렉토리들을 값으로 가지고 있다. 프로그램 내부에서 절대 경로를 주지 않고 외부 프로그램을 호출한다면 PATH 변수에 포함된 디렉토리들을 순서대로 찾아가면서 호출한다. 프로그래머가 만약 다음과 같은 코드를 사용한다면

...
    exec("ls -l | grep xxx");
...
PATH 변수를 "." (현재 디렉토리)가 가장 앞에 오도록 수정하고, 현재 디렉토리에 'ls'라는 SETUID 실행 파일을 만들어 둔다.

IFS - IFS (Internal Field Seperator)는 프로그램이 exec()나 popen() 등을 이용하여 외부 프로그램을 실행할 때 입력되는 문자열을 여러 필드로 나눌 때 기준이 되는 문자를 정의하는 변수이다. 기본적으로 IFS는 ' ' (space)로 정의된다.

프로그래머가 코드상에서 exec("/usr/lib/sendmail"); 이라고 코드를 넣었다고 가정하자. 만약 이 프로그램을 IFS를 '/' 로 바꾸고 실행하였다면 어떤 효과가 나타날까? exec(" usr lib sendmail"); 을 수행한것이 되어버린다. 즉, exec 의 실행 명령어는 'usr'이 되며 'lib sendmail'은 'usr' 이란 프로그램에 대한 인자로 쓰이게 된다. 만약 path 내에 SETUID 프로그램이라면 보안상의 문제가 된다.

hankyung@ubuntu:~$ IFS="/"; export IFS
IFS 를 "/" 로 바꾸었다.

set | less 명령어로 확인하여 보자.


IFS 가 "/"로 바뀐 것을 확인할 수 있다.

동적 라이브러리 이용 - 유닉스 시스템에서 사용하는 동적 라이브러리(Dynamic Library)를 이용하기 위한 환경변수를 이용해도 프로그램의 오동작을 유도할 수도 있다. 동적라이브러리를 이용하기 위한 환경변수에는 LD_LIBRARY_PATH와 LD_PRELOAD가 있다.

- LD_LIBRARY_PATH : 동적 라이브러리가 들어있는 디렉토리들을 값으로 갖는다.
- LD_PRELOAD : 먼저 loading 해야 하는 동적 라이브러리가 들어있는 디렉토리를 값으로 갖는다.

fgetc(char *buf, int n, FILE *fp) {
    exec("/bin/sh", "-sh", 0);
}
이 코드를 동적 라이브러리로 만든다.
hankyung@ubuntu:~$ cc -c -pic fget.c
hankyung@ubuntu:~$ ld -o libme.so fget.o
만들어진 동적 라이브러리를 다음과 같이 다른 동적라이브러리보다 먼저 링크되도록 한다.
hankyung@ubuntu:~$ setenv LD_PRELOAD .:$LD_PRELOAD

fgetc()를 사용하는 수퍼유저 소유의 SETUID 프로그램을 실행시키면 파일로부터 문자를 받아들이는 원래의 fgetc() 대신에 크래커가 작성한 위의 fgetc()가 실행되어 쉽게 수퍼유저 권한으로 실행된 쉘을 얻을 수 있다.


3. Race Condition(경쟁조건) #1. 임시파일을 생성하는 SETUID가 걸린 프로그램

만약 수퍼유저 소유의 SETUID 프로그램이 임시파일을 만든다면 수퍼유저의 권한으로 파일이 생성될 것이고 이 임시 파일을 프로세스가 접근하기 전에 다른 시스템 파일로 바꿔버린다면 어떤 시스템 파일이든 덮어쓰기가 가능해진다.


root권한의 setuid가 걸린 "good"이라는 프로그램이 있다고 하자.
그런데 이 프로그램은 실행한 후에 /tmp 디렉토리안에 byebye라는 임시파일을 만든다. 이러한 경우, 즉 프로그램이 실행 과정에 임시파일을 생성하는 경우에도 심볼릭링크를 이용하여 해킹을 할 수 있다.
일단 good이라는 프로그램을 실행하면 /tmp/byebye라는 임시파일이 생길 것이다. 일반 사용자가 이 임시파일(/tmp/byebye)을 삭제하고, /etc/passwd의 심볼릭 링크를 만드는데 이름을 /tmp/byebye라고 하여 만들었다.

그럼 이제 다시 good을 실행시키면 어떻게 되겠는가?

good이라는 프로그램엔 root권한의 setuid가 걸려있으므로 실행되는 동안에는 root의 권한이 유지된다. 프로그램이 byebye라는 임시파일을 만드는데 byebye는 /etc/passwd의 심볼릭링크 파일이다. /etc/passwd는 root만이 쓰기가 가능한데, 프로그램 실행 중에는 root권한이기 때문에 결국 /etc/passwd파일에 쓰기가 가능해진다. 따라서 심볼릭링크 파일을 수정하면 원본파일(/etc/passwd)도 수정이 되는 것이다.

이러한 사실을 기초로 하면, 결국 /tmp/byebye의 내용이 /etc/passwd로 들어갈 것이고 /etc/passwd파일은 손상을 입게된다고 예상할 수 있을 것이다.

ex)
-> 문제가 되었던 SunOS의 /bin/mail은 편지를 저장할 때 먼저 그 사용자의 계정을 파일 이름으로 하는 파일의 상태를 검사한다. (lstat()을 이용). 그리고 나서 편지를 저장하기 위해 open()을 수행한다. 즉, 두 시스템 콜 사이에 아무런 변화가 없었다고 가정하고 프로그램이 작성되어 있다. 하지만 lstat()를 사용한 바로 다음에 다른 프로세스가 그 파일을 /.rhosts로 바꿔버리면 편지의 내용은 /.rhosts에 저장하게 된다. 만약 편지의 내용 중에 "+ +"가 들어 있다면 모든 시스템에서 수퍼유저의 권한으로 아무런 제약 없이 로긴할 수 있게 되는 것이다.

hankyung@ubuntu:~$ cd /var/spool/mail; ln -s ~root/.rhosts daemon
hankyung@ubuntu:~$ echo "+ +" | mail daemon
hankyung@ubuntu:~$ rlogin localhost -l root
* /bin/mail 프로그램이 /var/spool/mail/user_id에 해당하는 파일이 symbolic 링크인지 아닌지를 확인하지 않은 채 root의 권한으로 /var/spool/mail/user_id에 해당되는 파일에 수신된 E-mail의 내용을 덮어쓰기 때문에 발생.

4. Race Condition(경쟁조건) #2.

만약 위와 같다면 왜 race condition이 등장하게 되었을까? 그냥 링크 시켜서 하면 되는 것인데, 왜 프로세스 간의 resource 경쟁이 나타나는가? 그것은 일종의 방어자와 공격자의 싸움에서 등장한 결과라고도 볼 수 있다.

프로그래머는 이런 문제점을 인식하고 lstat()을 이용한 방법을 택하였다. lstat()을 이용하여 먼저 modify 하고자 하는 파일이 Symbolic Link인지를 먼저 파악, 그 후 그 파일을 open() 시켜서 처리하도록 한 것이다. 아무런 문제가 없는 듯이 보였다. 그러나, 여기에서 진정한 race condition이 등장한다.

lstat()과 open()사이에는 분명히 갭이 존재한다. 그 갭을 적절히 이용한 것이 바로 race condition인데, 먼저 race 프로그램을 background로 돌린다. race 프로그램은 돌면서, 공격하고자 하는 프로그램이 modify하는 파일을 연속해서 지우고, 링크를 만들고 하는 작업을 반복한다. 그런 후에 공격하고자 하는 프로그램을 돌린다. 그러면 어떻게 될까?

race condition example.

#include <stdio.h>

int main(void)
{
      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);
      }
}
fork()라는 함수는 동일한 작업을 하는 프로세스를 하나 더 띄우는 함수이다.
그냥 생각하기로는 결과가 OXOXOX...이런 식으로 나오리라 생각할 수 있지만, 실행해보면 OOOXXOXOOOOXXOXXXXOXOOX 이런식으로 얽혀서 주기성이 없이 나타난다. 이것이 바로 race condition의 기본 개념.

(글이 길어저 자세한 내용은 나중에 다룰 "race condition 시도" 글에서 다루도록 하겠습니다.)

5. PTRACE

ptrace는 생성된 프로세스가 어떻게 움직이며, 어떤 식으로 데이터를 읽고 쓰는지, 어떤 에러를 내는지 추적을 하기 위해 마련된 시스템 콜이다. 이것은 주로 디버그를 위해 사용되며, 따라서 디버거는 일종의 ptrace 명령어 묶음 유틸리티라고 보면 옳겠다. 프로그래머는 디버거를 통해 ptrace를 손쉽게 사용할 수 있으며, 자신이 만든 프로그램이 어떻게 수행되는지 총괄적으로 관제할 수 있다.

부모프로세스가 invoke된 자식 프로세스를 통제할 수 있다는 사실. 조작의 대상이 되는 것은 그 프로세스가 가진 variables등의 메모리 core와 registers 들이다. ptrace 명령어들은, 이러한 것들을 읽고 쓰고 할 수 있음으로 해서 그 프로세스에 대한 전체적인 control flow도 조작해낼 수 있다는 것이다.


* Reference
- [book] Security PLUS for UNIX
- BIT 교육센터 유닉스 보안 강의자료
- wikipedia.org
- http://www.hackerslab.org 레벨3


이번주 완전 바빴네요. 과제심사에 중간고사에...ㄷㄷㄷㅠ_ㅠ

다음글 부터는 본격적으로 해킹시도에 대해 공부 및 실습과정을 쓰도록 하겠습니다.
예고- BOF (Buffer Over Flow)