Skip to content

Instantly share code, notes, and snippets.

@crackerboy
Forked from shreve/tp.md
Created December 11, 2020 00:23
Show Gist options
  • Select an option

  • Save crackerboy/5a224d91bc87d20c3541f3cca26413ee to your computer and use it in GitHub Desktop.

Select an option

Save crackerboy/5a224d91bc87d20c3541f3cca26413ee to your computer and use it in GitHub Desktop.
TP-Link Router Config Decrypt

TP-Link Router Config

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment