Skip to content

Instantly share code, notes, and snippets.

@thedroidgeek
Last active November 20, 2025 20:55
Show Gist options
  • Select an option

  • Save thedroidgeek/80c379aa43b71015d71da130f85a435a to your computer and use it in GitHub Desktop.

Select an option

Save thedroidgeek/80c379aa43b71015d71da130f85a435a to your computer and use it in GitHub Desktop.
Nokia/Alcatel-Lucent router backup configuration tool
#!/usr/bin/env python3
#
# Nokia/Alcatel-Lucent router backup configuration tool
#
# Features:
# - Unpack/repack .cfg files generated from the backup and restore functionnality
# in order to modify the full router configuration
# - Decrypt/encrypt the passwords/secret values present in the configuration
#
# Blog post: https://0x41.cf/reversing/2019/10/08/unlocking-nokia-g240wa.html
#
# Released under the MIT License (http://opensource.org/licenses/MIT)
# Copyright (c) Sami Alaoui Kendil (thedroidgeek)
#
import sys
import zlib
import struct
import base64
import binascii
import datetime
big_endian = True
encrypted_cfg = False
def u32(val):
return struct.unpack('>I' if big_endian else '<I', val)[0]
def p32(val):
return struct.pack('>I' if big_endian else '<I', val)
def checkendian(cfg):
if (cfg[0:4] == b'\x00\x12\x31\x23'):
return True
elif (cfg[0:4] == b'\x23\x31\x12\x00'):
return False
else:
return None
class RouterCrypto:
def __init__(self):
from Crypto.Cipher import AES
# key and IV for AES
key = '3D A3 73 D7 DC 82 2E 2A 47 0D EC 37 89 6E 80 D7 2C 49 B3 16 29 DD C9 97 35 4B 84 03 91 77 9E A4'
iv = 'D0 E6 DC CD A7 4A 00 DF 76 0F C0 85 11 CB 05 EA'
# create AES-128-CBC cipher
self.cipher = AES.new(bytes(bytearray.fromhex(key)), AES.MODE_CBC, bytes(bytearray.fromhex(iv)))
def decrypt(self, data):
output = self.cipher.decrypt(data)
# remove PKCS#7 padding
return output[:-ord(output[-1:])]
def encrypt(self, data):
# add PKCS#7 padding for 128-bit AES
pad_num = (16 - (len(data) % 16))
data += chr(pad_num).encode() * pad_num
return self.cipher.encrypt(data)
#
# unpack xml from cfg
#
if (len(sys.argv) == 3 and sys.argv[1] == '-u'):
# line feed
print('')
# read the cfg file
cf = open(sys.argv[2], 'rb')
cfg_data = cf.read()
# check cfg file magic (0x123123) and determine endianness
big_endian = checkendian(cfg_data)
if big_endian == None:
# check if config is encrypted
decrypted = None
try:
# decrypt and check validity
decrypted = RouterCrypto().decrypt(cfg_data)
big_endian = checkendian(decrypted)
except ValueError:
pass
# if decryption failed, or still invalid, bail out
if big_endian == None:
print('invalid cfg file/magic :(\n')
exit()
# set decrypted cfg buffer and encryption flag
print('-> encrypted cfg detected')
cfg_data = decrypted
encrypted_cfg = True
# log endianness
if big_endian:
print('-> big endian CPU detected')
else:
print('-> little endian CPU detected')
# get fw_magic (unknown, could be fw version/compile time, hw serial number, etc.)
fw_magic = u32(cfg_data[0x10:0x14])
print('-> fw_magic = ' + hex(fw_magic))
# get the size of the compressed data
data_size = u32(cfg_data[4:8])
# get the compressed data
compressed = cfg_data[0x14 : 0x14 + data_size]
# get the checksum of the compressed data
checksum = u32(cfg_data[8:12])
# verify the checksum
if (binascii.crc32(compressed) & 0xFFFFFFFF != checksum):
print('\nCRC32 checksum failed :(\n')
exit()
# unpack the config
xml_data = zlib.decompress(compressed)
# output the xml file
out_filename = 'config-%s.xml' % datetime.datetime.now().strftime('%d%m%Y-%H%M%S')
of = open(out_filename, 'wb')
of.write(xml_data)
print('\nunpacked as: ' + out_filename)
print('\n# repack with:')
print('%s %s %s %s\n' % (sys.argv[0], ('-pb' if big_endian else '-pl') + ('e' if encrypted_cfg else ''), out_filename, hex(fw_magic)))
cf.close()
of.close()
#
# generate cfg from xml
#
elif (len(sys.argv) == 4 and (sys.argv[1][:3] == '-pb' or sys.argv[1][:3] == '-pl')):
fw_magic = 0
try:
# parse hex string
fw_magic = int(sys.argv[3], 16)
# 32-bit check
p32(fw_magic)
except:
print('\ninvalid magic value specified (32-bit hex)\n')
exit()
big_endian = sys.argv[1][:3] == '-pb'
encrypted_cfg = sys.argv[1][3:] == 'e'
out_filename = 'config-%s.cfg' % datetime.datetime.now().strftime('%d%m%Y-%H%M%S')
# read the xml file
xf = open(sys.argv[2], 'rb')
xml_data = xf.read()
xf.close()
# compress using default zlib compression
compressed = zlib.compress(xml_data)
## construct the header ##
# magic
cfg_data = p32(0x123123)
# size of compressed data
cfg_data += p32(len(compressed))
# crc32 checksum
cfg_data += p32(binascii.crc32(compressed) & 0xFFFFFFFF)
# size of xml file
cfg_data += p32(len(xml_data) + 1)
# fw_magic
cfg_data += p32(fw_magic)
# add the compressed xml
cfg_data += compressed
# encrypt if necessary
if encrypted_cfg:
cfg_data = RouterCrypto().encrypt(cfg_data)
# write the cfg file
of = open(out_filename, 'wb')
of.write(cfg_data)
of.close()
print('\npacked as: ' + out_filename + '\n')
#
# decrypt/encrypt secret value
#
elif (len(sys.argv) == 3 and (sys.argv[1] == '-d' or sys.argv[1] == '-e')):
decrypt_mode = sys.argv[1] == '-d'
if decrypt_mode:
# base64 decode + AES decrypt
print('\ndecrypted: ' + RouterCrypto().decrypt(base64.b64decode(sys.argv[2])).decode('UTF-8') + '\n')
else:
# AES encrypt + base64 encode
print('\nencrypted: ' + base64.b64encode(RouterCrypto().encrypt(sys.argv[2].encode())).decode('UTF-8') + '\n')
else:
print('\n#\n# Nokia/Alcatel-Lucent router backup configuration tool\n#\n')
print('# unpack (cfg to xml)\n')
print(sys.argv[0] + ' -u config.cfg\n')
print('# pack (xml to cfg)\n')
print(sys.argv[0] + ' -pb config.xml 0x13377331 # big endian, no encryption, fw_magic = 0x13377331')
print(sys.argv[0] + ' -pl config.xml 0x13377331 # little endian, ...')
print(sys.argv[0] + ' -pbe config.xml 0x13377331 # big endian, with encryption, ...')
print(sys.argv[0] + ' -ple config.xml 0x13377331 # ...\n')
print('# decrypt/encrypt secret values within xml (ealgo="ab")\n')
print(sys.argv[0] + ' -d OYdLWUVDdKQTPaCIeTqniA==')
print(sys.argv[0] + ' -e admin\n')
@alexceltare2
Copy link

alexceltare2 commented Apr 3, 2025

PSA: You need pycryptodome library installed from pip for this script to work! Remove any previously installed crypto libraries.

@enineboyuna06
Copy link

Do anyone has a full flash dump of G-2426G-A?

@NimdaR00t
Copy link

Hello, do anyone has an idea about Orange fiberbox ? ALCL code
when you request for a configuration backup, a .fiberbox file extension is downloaded not a .cfg

@Heaven-Is-Myth-Nepal-Is-Real
Copy link

@NimdaR00t
Copy link

kindly email me justsad[at]gmail.c0m

@NimdaR00t
Copy link

Can you share the file and model number of your router

On Wed, 16 Jul 2025 at 3:51 AM NimdaR00t @.> wrote: @.* commented on this gist. ------------------------------ Hello, do anyone has an idea about Orange fiberbox ? ALCL code when you request for a configuration backup, a .fiberbox file extension is downloaded not a .cfg — Reply to this email directly, view it on GitHub https://gist.github.com/thedroidgeek/80c379aa43b71015d71da130f85a435a#gistcomment-5677556 or unsubscribe https://github.com/notifications/unsubscribe-auth/BIN23NPBEZUOVEXRI3M6VDT3IV3NRBFKMF2HI4TJMJ2XIZLTSKBKK5TBNR2WLJDUOJ2WLJDOMFWWLO3UNBZGKYLEL5YGC4TUNFRWS4DBNZ2F6YLDORUXM2LUPGBKK5TBNR2WLJDHNFZXJJDOMFWWLK3UNBZGKYLEL52HS4DFVRZXKYTKMVRXIX3UPFYGLK2HNFZXIQ3PNVWWK3TUUZ2G64DJMNZZDAVEOR4XAZNEM5UXG5FFOZQWY5LFVA4TQNZVGAZTENNHORZGSZ3HMVZKMY3SMVQXIZI . You are receiving this email because you commented on the thread. Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub .

kindly email me justsad[at]gmail.com

@kumarvivek1752
Copy link

i have no confing file backup and restore option how can i unlock it

router details
broadband provider airtel

Device name                                       G-2425G-A
Vendor                                                Nokia
Hardware Version:                            3TN00562AABA
Boot Version                                      Bootbase1.1-Nov-17-2023--19:50:10
Software Version:                             3TN00795HJLI94(1.2401.194)

@m7mdcc
Copy link

m7mdcc commented Sep 27, 2025

What is the backup and restore option CGI URL ? Like device status is device_status.cgi . what is the backup one ?

@creited
Copy link

creited commented Oct 8, 2025

@m7mdcc were you able to get this url? I could get 200 response from upgrade.cgi, but the page is blank. BTW, my device is Nokia G1425G-B.

@m7mdcc
Copy link

m7mdcc commented Oct 20, 2025

@m7mdcc were you able to get this url? I could get 200 response from upgrade.cgi, but the page is blank. BTW, my device is Nokia G1425G-B.

Same as you , i will try to attach serial cable to router

@yunez
Copy link

yunez commented Oct 21, 2025

Hi there! I have a Nokia G1425 from Telemex (Mexican ISP): Chipset: MTK7528 Hardware: 3FE77771BEAA Software: 3FE49568IJLJ07(1.2402.307) Made in Nov 1, 2024. This one does not let me decode, I get the dreaded: -> little endian CPU detected -> fw_magic = 0xfffffffe Traceback (most recent call last): File "/home/alisi/Downloads/nokia.py", line 137, in xml_data = zlib.decompress(compressed) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ zlib.error: Error -3 while decompressing data: incorrect header check

I understand this is a decoding problem (I use Python and these libraries myself!) - The question here is, is there anything I can do to help the project/community to get this version of the firmware cracked?

I am more than willing to share my config.cfg with someone if needed; the credentials are non-relevant outside Telmex's network anyway: https://drive.google.com/file/d/1NEp0mSZUdCpTH5w9QxykT00Mxmmgn1aj/view?usp=sharing

Thanks in advance! -Alisi

Stuck in here as well, same situation same Telmex ont device.

@borchacalas
Copy link

borchacalas commented Oct 23, 2025 via email

@gekkehenkie11
Copy link

@yunez and others with that decompression error:

  1. make sure you have installed
    pip install pycryptodomex
    and
    pip install pycryptodome
  2. If it still doesnt work, change your python version. I had the same problem with version 3.13.7 but it worked with version 3.11.9

@yunez
Copy link

yunez commented Oct 31, 2025

  1. pip install pycryptodome

Tried all of the above you mentioned, even did a clean uninstall and other older versions from 2021 and onwards but same error message. :(

nokia ont fw version is: 3TN00225LJLJ63 (if anyone wants to chime in and had success)

@gekkehenkie11
Copy link

gekkehenkie11 commented Oct 31, 2025

It doesnt have much to do with the nokia ont version. This really is a problem with the program code itself, so between the python code, the python version and the libraries. If you're tech savy you can even do everything yourself, so without this tool. There's a youtube video out there describing the file format of the config file and how to decrypt it yourself. I was about to go that route when I found out that it did work on a different python version, like I described in the post above yours.

@yunez
Copy link

yunez commented Nov 2, 2025

It doesnt have much to do with the nokia ont version. This really is a problem with the program code itself, so between the python code, the python version and the libraries. If you're tech savy you can even do everything yourself, so without this tool. There's a youtube video out there describing the file format of the config file and how to decrypt it yourself. I was about to go that route when I found out that it did work on a different python version, like I described in the post above yours.

I think I know which video that one is. I tried that but somehow, I keep getting stuck when trying to view the "enc_block_with_salt", salted part is different from the salt numbers and under "Salted__" part to the right, I see the Salt numbers were it should be "Salted__ n", don't know what I'm doing different, literally followed the same instructions as in the video.

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