Skip to content

Instantly share code, notes, and snippets.

@shreve

shreve/tp.md

Last active Apr 29, 2021
Embed
What would you like to do?
TP-Link Router Config Decrypt

TP-Link Router Config

Update 2021-04-29: This may still work for you if you've got an old TP-Link router, but this is not maintained and doesn't work with newer models. If you've got a newer router, other projects like tpconf_bin_xml will likely work better for you.

TP-Link allows you to backup and restore your router's config file. For some reason, they decided to encrypt these backups so you cannot modify them. I think this is dumb. This script lets you decrypt and re-encrypt your config files so you can modify them as you see fit.

I use this to modify my reserved addresses list because editing them through the web interface is terribly slow and cumbersome.

  1. Go to the router and download the config file from the "Backup & Restore" section of "System Tools".
  2. Run ruby tp.rb config.bin
  3. Edit the created config file config.cfg as you please.
  4. Run ruby tp.rb config.cfg
  5. Return to the "Backup & Restore" section and upload your modified config.bin file.

This work is based on the hack by Matteo Croce here. Thank you for doing the hard part.

#!/usr/bin/env ruby
require 'openssl'
infile = ARGV[0]
content = File.read(infile)
mode = infile.end_with?('.bin') ? :decrypt : :encrypt
# Configure the cipher with what we know about the TP-Link encryption
cipher = OpenSSL::Cipher::DES.new(:ECB)
cipher.key = ["478DA50BF9E3D2CF"].pack("H*")
cipher.padding = 0
case mode
when :decrypt
cipher.decrypt
plaintext = cipher.update(content) + cipher.final
# First 16 bytes are checksum of content
reported_checksum = plaintext[0...16]
plaintext = plaintext[16..-1]
# Trim empty bytes used to pad out the plaintext to be multiple of 8
plaintext = plaintext[0...-1] while plaintext.end_with?("\x0")
checksum = OpenSSL::Digest::MD5.digest(plaintext)
if reported_checksum != checksum
$stderr.puts "Checksums do not match"
$stderr.puts "reported: " << reported_checksum.bytes.map { |b| b.to_s(16) }.join
$stderr.puts "actual: " << checksum.bytes.map { |b| b.to_s(16) }.join
end
File.write(infile.sub('.bin', '.cfg'), plaintext)
when :encrypt
# The body is prefixed with the 16 byte md5 checksum of the contents
checksum = OpenSSL::Digest::MD5.digest(content)
body = checksum + content
# The body needs to be padded out with zero bytes until the byte
# length is a multiple of 8.
remaining = 8 - (body.length % 8)
pad = remaining == 8 ? '' : ("\x0" * remaining)
# Set the cipher to encryption mode and perform the action
cipher.encrypt
ciphertext = cipher.update(body + pad) + cipher.final
File.write(infile.sub('.cfg', '.bin'), ciphertext)
end
@thatoo-apple

This comment has been minimized.

Copy link

@thatoo-apple thatoo-apple commented Dec 1, 2020

Hi. First, thank you. It's really great what you have done. I'm trying to decrypt my WR840N, but when I will edit the config.cfg file I got "incompatible character encodings: ASCII-8BIT and UTF-8".

@Nikhil1O1

This comment has been minimized.

Copy link

@Nikhil1O1 Nikhil1O1 commented Apr 14, 2021

$ ruby tp.rb config.bin
tp.rb:18:in update': key not set (OpenSSL::Cipher::CipherError) from tp.rb:18:in

'

I'm getting this error. Any idea?

@sengiv

This comment has been minimized.

Copy link

@sengiv sengiv commented Apr 29, 2021

This script appears out of date, and no longer functional for newer TL-LINK devices.
tpconf_bin, a similar project is a working alternative to this

@shreve

This comment has been minimized.

Copy link
Owner Author

@shreve shreve commented Apr 29, 2021

@sengiv thanks. I'll add that and a deprecation notice to this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment