This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
howto:dji_ftpd_aes_unscramble [2017/07/23 13:03] czokie [Fixing 16 bytes] |
howto:dji_ftpd_aes_unscramble [2019/01/15 23:00] (current) czokie |
||
---|---|---|---|
Line 3: | Line 3: | ||
When you get FTP access to your DJI, you can push files to the aircraft using normal FTP commands (to a limited subset of directories due to /system being read only). | When you get FTP access to your DJI, you can push files to the aircraft using normal FTP commands (to a limited subset of directories due to /system being read only). | ||
- | But, downloading is another matter. DJI modified the FTPD that is built into their firmware, to stop people getting access to the files. (and hide the fact that their aircraft is using GPL software | + | But, downloading is another matter. DJI modified the FTPD that is built into their firmware, to stop people getting access to files, in an attempt to hide the fact that their aircraft is using GPL software |
- | A number of people have already solved how to upload/ | + | A number of people have already solved how to upload/ |
+ | |||
+ | Through earlier work, we have the decryption key. Before encrypting, DJI also do some XOR operations on the first 16 bytes of the input document, to try and prevent people from reverse engineering their encryption. In the python approach below, this is taken care of. The initial decryption code was created by Hostile (aka MavProxyUser). Subsequent patches were submitted by Czokie to deal with the XOR tweaks, as well as stripping PKCS7 padding at the end of the decrypted file. | ||
+ | |||
+ | This method is published [[https:// | ||
===== 1. Toolchain ===== | ===== 1. Toolchain ===== | ||
Line 11: | Line 15: | ||
===== 2. Get the DJI_ftpd_aes_unscramble magic ===== | ===== 2. Get the DJI_ftpd_aes_unscramble magic ===== | ||
- | If this is your first time using duml, you will need to checkout the code from git. | + | If this is your first time using DJI_ftpd_aes_unscramble, you will need to checkout the code from git. |
cd ~/ | cd ~/ | ||
Line 24: | Line 28: | ||
===== 3. Now What? ====== | ===== 3. Now What? ====== | ||
+ | Below are a set of instructions that come from the GIT repository where this code is located. The details are included here for ease of use. | ||
+ | |||
+ | |||
+ | # DJI_ftpd_aes_unscramble | ||
+ | DJI has modified the GPL Busybox ftpd on Mavic, Spark, & Inspire 2 to include scrambling of downloaded files... | ||
+ | |||
+ | Windows executable release created via: | ||
+ | < | ||
+ | c: | ||
+ | </ | ||
+ | If not using packaged release for Windows, make sure you have pip, and that pycrypto is installed | ||
+ | |||
+ | Usage: | ||
+ | Make sure you have a DJI Mavic, Inspire 2, or Phantom 4, or Spark connected. | ||
+ | |||
+ | Mirror the FTPD via the script, OR manually pull down a target file. | ||
+ | < | ||
+ | $ python dji_ftpd_descrambler.py 192.168.42.2 | ||
+ | --2017-05-25 23: | ||
+ | => ‘DJI_aes_ftp_dump/ | ||
+ | Connecting to 192.168.42.2: | ||
+ | Check the contents of the folder DJI_aes_ftp_dump | ||
+ | ... | ||
+ | |||
+ | Verify the file is AES encrytped aka " | ||
+ | $ xxd DJI_aes_ftp_dump/ | ||
+ | 00000000: dee9 a171 7fad 24e2 a2ad fe52 f2a9 43e4 ...q..$....R..C. | ||
+ | 00000010: cdf3 ab35 4ec3 82a8 f491 f3e5 40a8 c92c ...5N.......@.., | ||
+ | 00000020: b80a 8c8e 0bef 6bf5 5505 b71c d819 9bde ......k.U....... | ||
+ | 00000030: cf23 f181 68b1 ae23 6305 1c8b 4d1a 986c .# | ||
+ | 00000040: 4d3e 569a 97e1 33b0 7a05 4ff1 92c2 d88d M> | ||
+ | 00000050: 20b7 d872 5ef4 a288 f25d dc06 a8e7 6b0d | ||
+ | 00000060: dc14 85c1 45eb bc59 36d8 1c63 b17f d35b ....E..Y6..c...[ | ||
+ | 00000070: 07c0 1499 ff5b 4c0f 7cc7 df67 d09b a2ea .....[L.|..g.... | ||
+ | 00000080: 0dfc fcb3 8aab 5f06 aace 0f41 a6c6 fb89 ......_....A.... | ||
+ | 00000090: 5d13 a609 c74a 7318 4734 2d95 d5bc b975 ]....Js.G4-....u | ||
+ | </ | ||
+ | |||
+ | Descramble the file... profit! | ||
+ | < | ||
+ | $ python dji_ftpd_descrambler.py DJI_aes_ftp_dump/ | ||
+ | PBS^U\5] [0x0] state=0, reset phy | ||
+ | [1980/00/01 0:0:5] [0x0]========= machine=1, state=0, runtime=72 ========= | ||
+ | [1980/00/01 0:0:5] [0x0] state=0, [0] reset mac to idle | ||
+ | [1980/00/01 0:0:7] [0x1866] state=0, recv shakehand req | ||
+ | [1980/00/01 0:0:7] [0x187c] state from 0 to connect | ||
+ | [2017/04/14 14:42:30] [0x3d27d0] state=3, connect to out_of_sync | ||
+ | [2017/04/14 14:42:30] [0x3d27d0] state=3, [1] reset mac to idle | ||
+ | [2017/04/14 14:44:8] [0x47d] state=3, recv shakehand req | ||
+ | [2017/04/14 14:44:8] [0x4c3] state=3, recv shakehand req | ||
+ | [2017/04/14 14:44:8] [0x530] state from 3 to connect | ||
+ | </ | ||
+ | On Windows the process works the same, with alternate synatx on the command line. | ||
+ | |||
+ | You can use the new bash interface: | ||
+ | < | ||
+ | MavproxyUser@DESKTOP-QPUF664 MINGW64 ~/ | ||
+ | $ python dji_ftpd_descrambler.py kernel00.log | ||
+ | oOZTPTP7] c0 1 (init) init: untracked pid 621 exited | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | Or make use of the standard cmd.exe interface: | ||
+ | < | ||
+ | C: | ||
+ | !!!New kernel log start!!! | ||
+ | |||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | -- More -- | ||
+ | </ | ||
+ | Alternatively on windows you can use the precompied .exe (see the Releases tab) | ||
+ | < | ||
+ | C: | ||
+ | h | ||
+ | VTVSPW] c1 11916 (kworker/ | ||
+ | <7>[ 1380.255734] c1 11916 (kworker/ | ||
+ | <7>[ 1382.825736] c0 26031 (kworker/ | ||
+ | </ | ||
+ | |||
+ | Description: | ||
+ | |||
+ | |||
+ | I miss the good ole days of public tar & feathering over GPL violations! | ||
+ | |||
+ | < | ||
+ | "The following products and/or projects appear to use BusyBox, but do not appear to release source code as required by the BusyBox license. This is a violation of the law! The distributors of these products are invited to contact Erik Andersen if they have any confusion as to what is needed to bring their products into compliance, or if they have already brought their product into compliance and wish to be removed from the Hall of Shame." | ||
+ | </ | ||
+ | |||
+ | < | ||
+ | This page is no longer updated, these days, BusyBox handles enforcement of our license via our fiscal sponsor, Software Freedom Conservancy instead. Please email < | ||
+ | |||
+ | Previously, this page listed products that included BusyBox but included neither source code nor offer for one. The BusyBox project has decided to not publicly shame companies until Conservancy has an opportunity to talk privately with companies who violate the GPL to convince them to comply with BusyBox' | ||
+ | </ | ||
+ | |||
+ | https:// | ||
+ | |||
+ | In an attempt to obfuscate the files downloaded from several DJI products, an AES function was added to the ftpd download routines of busybox. | ||
+ | |||
+ | It is extremely trivial to extract the binary using binwalk from one of the available firmware downloads. From here it can be Reverse Engineered. | ||
+ | |||
- | OK. What we know so far... We already know the AES key, and this is used in both of the methods below | + | On OSX you can navigate to: / |
+ | On Windows to: C:\Program Files (x86)\DJI Product\DJI Assistant 2\ Assistant\Data\firm_cache | ||
- | ==== Method One ==== | + | Run binwalk with the extraction flag against any appropriate firmware file. |
+ | < | ||
+ | $ grep busybox wm* -r | ||
+ | Binary file wm220_0100_v02.05.04.34_20170209_ca02.pro.fw.sig matches | ||
+ | Binary file wm220_0100_v02.06.04.84_20170324_ca02.pro.fw.sig matches | ||
+ | Binary file wm220_0801_v01.04.17.03_20170120.pro.fw.sig matches | ||
+ | Binary file wm220_0801_v01.05.00.20_20170331.pro.fw.sig matches | ||
+ | Binary file wm220_0805_v01.01.00.71_20161227.pro.fw.sig matches | ||
+ | Binary file wm220_0805_v01.01.00.87_20170427.pro.fw.sig matches | ||
+ | Binary file wm220_1301_v01.04.17.03_20170120.pro.fw.sig matches | ||
+ | Binary file wm220_1301_v01.05.00.23_20170418.pro.fw.sig matches | ||
+ | Binary file wm220_2801_v01.02.21.01_20170421.pro.fw.sig matches | ||
+ | </ | ||
- | Hostile | + | Pick one... just make sure it doesn' |
- | ==== Method Two ==== | + | < |
+ | $ binwalk -e wm220_0801_v01.04.17.03_20170120.pro.fw.sig | ||
+ | </ | ||
+ | Launch | ||
+ | https://wiki.ubuntu.com/ARM/BuildEABIChroot | ||
- | I was talking to Jezzab in #general about this - He gave me an alternate command | + | < |
+ | # ./busybox tcpsvd | ||
+ | tcpsvd: listening on 0.0.0.0:21, starting | ||
+ | tcpsvd: status 1/30 | ||
+ | tcpsvd: start 9062 127.0.0.1: | ||
+ | </ | ||
- | openssl enc -d -nosalt -in *.fw.sig -aes-128-cbc -K 746869732d6165732d6b657900000000 -iv 00000000000000000000000000000000 > output.fw.sig.decrypt | + | Download, compile, and run aes-finder against the ftp binary. Extract the AES key by running against the PID. |
+ | https:// | ||
+ | < | ||
+ | $ sudo ./a.out -9062 | ||
+ | Searching PID 9062 ... | ||
+ | [0x7f4e6c17b93c] Found AES-128 encryption key: 746869732d6165732d6b657900000000 | ||
+ | [0x7f4e6c17ba30] Found AES-128 decryption key: 746869732d6165732d6b657900000000 | ||
- | But, the first 16 bytes in the resulting decrypted file are still not correct. We have to assume that DJI are meddling with the original file content, to try and screw with us. (Shock horror). | + | $ echo -e " |
+ | this-aes-key | ||
+ | </ | ||
+ | This oddly enough was the string that made me look for the routine in the first place. It shows up in clear text in the binary. | ||
+ | Busybox does not normally have an AES function, so this was immediately a red flag. | ||
- | ==== Fixing 16 bytes ==== | + | I had a hunch that DJI reused their encryption choices... and they did, the same routines used on the NAZA firmware |
- | This is where jezzab has done some great work | + | |
- | < | + | https:// |
- | then on the first 16 bytes you need to do this: | + | https://cdn.hackaday.io/ |
- | // Descramble first 16 bytes | + | |
- | for (int i = 0x00; i < 0x0A; i++) | + | |
- | { | + | |
- | array[i] ^= (byte)(0x30 + i); | + | |
- | } | + | |
- | for (int i = 0x0A; i < 0x10; i++) | + | |
- | { | + | |
- | array[i] ^= (byte)(0x57 + i); | + | |
- | } | + | |
- | </file> | + | |
+ | Simply replace the AES key with the one above in the tool provided by seasonalvegetables3. | ||
- | So. Part one of this solution - some quick python | + | <code> |
- | < | + | #Key = "7F0B9A5026674ADA0BB64F27E6D8C8A6" |
- | #!/usr/bin/env python | + | Key = " |
+ | IV = " | ||
+ | </code> | ||
- | import array | + | This project will recurvively download the contents of the ftp server, and decrypt them for you in a local plaintext mirror. |
- | input = open(' | + | In essence using code in this repo would be the same as running: |
- | input.seek(0) | + | < |
- | arr = array.array(' | + | $ wget -m ftp:// |
- | arr.fromfile(input, | + | </ |
- | for i in range(10): | + | Followed by: |
- | arr[i] ^= 0x30 + i | + | < |
+ | python djicrypt.py -d -i downloadedfile -o outputfile | ||
+ | </ | ||
- | for i in range(10, | + | Alternately you can just use openssl: |
- | arr[i] ^= 0x57 + i | + | < |
+ | openssl enc -d -nosalt -in downloadedfile -aes-128-cbc -K 746869732d6165732d6b657900000000 -iv 00000000000000000000000000000000 | ||
+ | </ | ||
- | output = open(' | + | And of course *our* script as detailed above in Usage: |
- | arr.tofile(output) | + | |
- | output.write(input.read()) | + | |
- | </ | + | In this example the ever so valuable DAAK is extracted... as is the WAEK (wireless password) |
+ | $ python dji_ftpd_descrambler.py | ||
+ | < | ||
+ | < | ||
+ | initrd=0x07400000, | ||
+ | recovery: | ||
+ | chip_sn=31337000 board_sn=01EAT2D111XXXX daak=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA daek=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA drak=6f707f2962351d75bc089ac34da119fa | ||
+ | saak=6f402fb8625205ce9bdd580217d218d8 waek=WIFIPASS production quiet board_id=0xe2200026 | ||
+ | </ | ||
- | FIXME: Either update | + | ===== Credit ===== |
+ | * This python module was created by Hostile | ||
+ | * Subsequent bug fixes created | ||
+ | * Earlier work was based on https:// |