commit 3233091d325669ff96d03792623e494ef37b8756 Author: Alex Gustafsson Date: Tue Oct 27 17:36:11 2020 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..21dbce2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +firmware +dump diff --git a/README.md b/README.md new file mode 100644 index 0000000..2d80c22 --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +# D-Link DCS reversing +### An effort to simplify firmware analysis and firmware updating on D-Link's DCS line of products +*** + +## Project goal + +The short-term goal of this project is to provide the necessary tools to easily analyze and modify D-Link's firmware for its DCS line of products (such as their network cameras). + +A long-term goal is to be able to provide an alternative firmware with only the bare essentials to use the camera in order to fully disable all cloud aspects of the camera and to potentially make it HomeKit-compatible. + +## What's in the box + +### Scripts + +#### Firmware + +The `./scripts/download-firmware.sh` script downloads the firmware for a specific product. For example, one can download the latest firmware for the DCS-936L like so: + +```sh +./scripts/download-firmware.sh [firmware index] +./scripts/download-firmware.sh dcs-936l firmware 1 +``` + +The `./scripts/unpack-firmware.sh` script decrypts, unpacks and carves date from the firmware file. It can be used like so: + +```sh +./scripts/unpack-firmware.sh +./scripts/unpack-firmware.sh firmware/DCS-936L_fw_revA1_1-07-04_eu_multi_20180918.zip dump +``` + +Example (shallow) directory structure for the unpacking script: + +``` +❯ tree -L 4 -d dump/ +dump/ +├── carved +│   └── _update.extracted +│   ├── squashfs-root +│   └── squashfs-root-0 +│   ├── empty -> /tmp +│   ├── lib -> /tmp +│   ├── lock -> /tmp +│   ├── log -> /tmp +│   ├── run -> /tmp +│   ├── shares +│   ├── tmp -> /tmp +│   └── www +└── extracted +``` + +### Tools + +The `./tools` directory contains dockerized tools such as Binwalk. The tools can be built and used using the following script: + +```sh +./tools/build.sh +docker run -it binwalk +``` + +## Previous art + +https://github.com/bmork/defogger + +## Contributing + +Any contribution is welcome. If you're not able to code it yourself, perhaps someone else is - so post an issue if there's anything on your mind. + +### Development + +Clone the repository: +``` +git clone https://github.com/AlexGustafsson/dlink-dcs-reversing && cd dlink-dcs-reversing +``` diff --git a/scripts/download-firmware.sh b/scripts/download-firmware.sh new file mode 100755 index 0000000..488389d --- /dev/null +++ b/scripts/download-firmware.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +model="$1" +output_directory="$2" +firmware_index="$3" + +function search() { + query="$1" + curl -s "https://eu.dlink.com/uk/en/search/suggestions?mode=ajax&q=$query" | grep -e 'item-listing.*href' | sed 's/.*href="\([^"]\+\)".*/\1/' +} + +function find_firmware() { + page="$1" + curl -s "https://eu.dlink.com$page" | grep "href" | sed 's/.*href="\([^"]\+\)".*/\1/' | grep ".zip" +} + +function find_all_firmware() { + while read -r page; do + find_firmware "$page" + done +} + +search_result="$(search "$model")" +if [[ -z "$search_result" ]]; then + echo "Unable to find any matching model" + exit 1 +fi + +all_firmware="$(echo "$search_result" | find_all_firmware)" +if [[ -z "$all_firmware" ]]; then + echo "Unable to find any firmware for that model" + exit 1 +fi + +if [[ -z "$firmware_index" ]]; then + echo "Found firmware the following firmware" + echo "$all_firmware" | nl -w1 -s' ' + echo -ne "\nWhich firmware do you want to download? " + read firmware_index +fi + +firmware="$(echo "$all_firmware" | sed -n "${firmware_index}p")" +if [[ -z "$firmware" ]]; then + echo "No such firmware" + exit 1 +fi + +wget --directory-prefix="$output_directory" "$firmware" diff --git a/scripts/unpack-firmware.sh b/scripts/unpack-firmware.sh new file mode 100755 index 0000000..70ef3a0 --- /dev/null +++ b/scripts/unpack-firmware.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash + +USE_DOCKER="${USE_DOCKER:-1}" + +# The input firmware zip file as downloaded from D-Link's website +firmware_zip="$1" + +# Output directory +output="$2" + +# The RSA private key dumped from the device using the following command: +# pibinfo PriKey +private_key="$(cat << EOF +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQC87QYtji6c0odxDs5SON+lpP0WE52WMK4OuRo8mHL0HWi0bZe0 +dyDcJ6IO6EwKuIn0sJi5oY8/P0g3PAp4TTlZm6WBiwZ8XaPwnGY8Kd2lrcRvPR+0 +UJ07hQ4RvhfFmanN8KEhe/myKkMldO/reyW8nwRRJwc2OHEvZ81KfAppWwIDAQAB +AoGAKGeIqrV9woxD6yn/dhYzvnlKpy4KxdQjZYKw2cTA0PR5MB1AFJhlrq/LOOT1 +XlWZK3uZLhofSKeAClAM7S2W1fTbxmM8y/3g6c5Z3LbpNsvwVTWNp5ErbKMMeR+q +CnUA+NQ47f65EwUUyTIWhMcgINLjZT33Eg0DOPUVIoiDEgECQQDta5ehRLQQN8Tr +5OROwFrJA3qL4g+qmcnUrVfgDE/ro1zxcYluleg8ZCAJbhBgju7Y75voRlrZITkF +2dGFZq4PAkEAy7Xowx+az4ZU6Iw/AGmbv801digR9345UKZZNRLffLh0Hda2mPM+ +25whWWTMiPcY0ty9/MZovZvVyuYuxzHb9QJBAJlbEgpdMmH3Y/9rTf2ASiPlV1bb +onrz82aowUY7LbRrRTG/wKHpuqSnl/n/WhzEtorx2qbiKvRtfUPGOowMkwkCQGgi +n9BPcbYwd2tBdlthoUrVPkUeisC399iwkN2+vhxltoYiYsmhXzqof6vRCXXiyv/P +9Bcp3hU/enT0YmlVpZkCQQDmsui42t0uup3hj6ITZa2JRkCCUI7qyU0HrE65lj88 +s4IoUXr0RWUtnEbeDUbw2GtQVHNoldXVXSh30SDb6El7 +-----END RSA PRIVATE KEY----- +EOF +)" + +# Temporary directory used throughtout the unpacking +temp_directory="$(mktemp -d)" + +# First unzip the file which contains a single tar file +unzip -p "$firmware_zip" > "$temp_directory/firmware.tar" + +# Untar the file +tar --extract --directory="$temp_directory" --file="$temp_directory/firmware.tar" +rm "$temp_directory/firmware.tar" + +# Decrypt the AES encryption key +echo "$private_key" > "$temp_directory/rsa.key" +openssl rsautl -decrypt -in "$temp_directory/aes.key.rsa" -out "$temp_directory/aes.key" -inkey "$temp_directory/rsa.key" + +# Decrypt the update itself +openssl aes-128-cbc -v -nosalt -md md5 -d -in "$temp_directory/update.aes" -out "$temp_directory/update" -kfile "$temp_directory/aes.key" +openssl aes-128-cbc -v -nosalt -md md5 -d -in "$temp_directory/update.bin.aes" -out "$temp_directory/update.bin" -kfile "$temp_directory/aes.key" + +# Move the firmware to the target directory +mkdir -p "$output/extracted" +mv "$temp_directory/update" "$output/extracted" +mv "$temp_directory/update.bin" "$output/extracted" + +# Remove the temporary directory +rm -r "$temp_directory" + +# Unpack the base64 encoded binary within update.bin +mkdir -p "$output/carved" +cat "$output/extracted/update.bin" | tr -d '\n' | sed 's!.*=== ddPack Boundary ===begin-base64 755 /dev/stdout!!' | rev | cut -c 5- | rev | base64 -d > "$output/carved/payload" + +# Extract files from the update payload +if [[ "$USE_DOCKER" -eq 1 ]]; then + docker run --rm -it -v "$(realpath "$output"):/output" binwalk --extract --directory "/output/carved" "/output/extracted/update" +else + binwalk --extract --directory "$output/carved" "$output/extracted/update" +fi diff --git a/tools/Dockerfile.binwalk b/tools/Dockerfile.binwalk new file mode 100644 index 0000000..f5e3334 --- /dev/null +++ b/tools/Dockerfile.binwalk @@ -0,0 +1,11 @@ +FROM debian:buster-slim + +RUN apt-get update && \ + apt-get install -y python3 git squashfs-tools python3-pip && \ + git clone https://github.com/ReFirmLabs/binwalk.git && \ + cd binwalk && \ + python3 setup.py install + +VOLUME /output + +ENTRYPOINT ["binwalk"] diff --git a/tools/build.sh b/tools/build.sh new file mode 100755 index 0000000..17ed1cb --- /dev/null +++ b/tools/build.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +docker build -t binwalk -f tools/Dockerfile.binwalk .