|# Nokia/Alcatel-Lucent router backup configuration tool|
|# - 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)|
|big_endian = True|
|encrypted_cfg = False|
|return struct.unpack('>I' if big_endian else '<I', val)|
|return struct.pack('>I' if big_endian else '<I', val)|
|if (cfg[0:4] == b'\x00\x12\x31\x23'):|
|elif (cfg[0:4] == b'\x23\x31\x12\x00'):|
|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|
|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|
|# unpack xml from cfg|
|if (len(sys.argv) == 3 and sys.argv == '-u'):|
|# line feed|
|# read the cfg file|
|cf = open(sys.argv, '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|
|# decrypt and check validity|
|decrypted = RouterCrypto().decrypt(cfg_data)|
|big_endian = checkendian(decrypted)|
|# if decryption failed, or still invalid, bail out|
|if big_endian == None:|
|print('invalid cfg file/magic :(\n')|
|# set decrypted cfg buffer and encryption flag|
|print('-> encrypted cfg detected')|
|cfg_data = decrypted|
|encrypted_cfg = True|
|# log endianness|
|print('-> big endian CPU detected')|
|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')|
|# 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')|
|print('\nunpacked as: ' + out_filename)|
|print('\n# repack with:')|
|print('%s %s %s %s\n' % (sys.argv, ('-pb' if big_endian else '-pl') + ('e' if encrypted_cfg else ''), out_filename, hex(fw_magic)))|
|# generate cfg from xml|
|elif (len(sys.argv) == 4 and (sys.argv[:3] == '-pb' or sys.argv[:3] == '-pl')):|
|fw_magic = 0|
|# parse hex string|
|fw_magic = int(sys.argv, 16)|
|# 32-bit check|
|print('\ninvalid magic value specified (32-bit hex)\n')|
|big_endian = sys.argv[:3] == '-pb'|
|encrypted_cfg = sys.argv[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, 'rb')|
|xml_data = xf.read()|
|# compress using default zlib compression|
|compressed = zlib.compress(xml_data)|
|## construct the header ##|
|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)|
|cfg_data += p32(fw_magic)|
|# add the compressed xml|
|cfg_data += compressed|
|# encrypt if necessary|
|cfg_data = RouterCrypto().encrypt(cfg_data)|
|# write the cfg file|
|of = open(out_filename, 'wb')|
|print('\npacked as: ' + out_filename + '\n')|
|# decrypt/encrypt secret value|
|elif (len(sys.argv) == 3 and (sys.argv == '-d' or sys.argv == '-e')):|
|decrypt_mode = sys.argv == '-d'|
|# base64 decode + AES decrypt|
|print('\ndecrypted: ' + RouterCrypto().decrypt(base64.b64decode(sys.argv)).decode('UTF-8') + '\n')|
|# AES encrypt + base64 encode|
|print('\nencrypted: ' + base64.b64encode(RouterCrypto().encrypt(sys.argv.encode())).decode('UTF-8') + '\n')|
|print('\n#\n# Nokia/Alcatel-Lucent router backup configuration tool\n#\n')|
|print('# unpack (cfg to xml)\n')|
|print(sys.argv + ' -u config.cfg\n')|
|print('# pack (xml to cfg)\n')|
|print(sys.argv + ' -pb config.xml 0x13377331 # big endian, no encryption, fw_magic = 0x13377331')|
|print(sys.argv + ' -pl config.xml 0x13377331 # little endian, ...')|
|print(sys.argv + ' -pbe config.xml 0x13377331 # big endian, with encryption, ...')|
|print(sys.argv + ' -ple config.xml 0x13377331 # ...\n')|
|print('# decrypt/encrypt secret values within xml (ealgo="ab")\n')|
|print(sys.argv + ' -d OYdLWUVDdKQTPaCIeTqniA==')|
|print(sys.argv + ' -e admin\n')|
@espetoet - Hello, I've been contacted last year by someone who had a Nokia router with an ARM (little endian) CPU and a different value for 'magic2', and I've since fixed the script for him in private, but never got around to rewriting this one and making it universal.
Thank you very much the extraction was successful.
Now I have another problem, I followed the tutorial as a reference https://0x41.cf/reversing/2019/10/08/unlocking-nokia-g240wa.html
I was able to unpack/pack successfully but when uploading the firmware I get this:
One thing odd is when I re-pack, the size of the firmware is quite different:
I want to have a root access to try to put the device (Nokia G-240W-A) into wan bridge (do the vlan and media conversion on it, and only have to do the pppoe on a second router, a Mikrotik. Right now I'm on a full bridge mode and my Mikrotik struggles in terms of speed to do the vlan and pppoe on the cpu). I don't know if I will be able to do that :P
Salam, I'm pretty confused as to why you're referring to the cfg file as firmware - I think you were meant to upload it via the same page you got it from.
For the size, it's normal, there's garbage bytes that are included when generating the cfg from the router (I think, didn't bother to check).
For your 'WAN bridge' setup, I'm not sure if you'll be able to achieve it - I'd advise you just get a decent router that can handle VLAN and PPPoE and leave the Nokia as a simple ONT modem - I personally got an Archer C60 v2 3 months ago (~500MAD) that I've flashed with OpenWrt (can easily NAT 100mbps even without software flow offloading, and has decent 5GHz range) and I never had to power cycle anything ever again.
If you knew what I meant then it's ok.
I already have a Mikrotik and using the Nokia in bridge mode and handling the VLAN and pppoe on the Mikrotik.
Just playing with it to see if I can do a wan bridge. I saw in the HTML than the drop down has been hidden to put in in "pppoe bridge". I was able to save the wan in bridge mode by enabling the previously hidden HTML element but I don't know if the hardware behind is supporting it. It does save correctly and says it's connected to the ISP in status but it doesn't seem to work, I will have to test again.
Still, the script don't work for me. And yes I was referring to a backup/restore of the config. A lot of companies are calling the bin a firmware.
Hello @thedroidgeek Thankyou for the detailed instruction , although this is the first time ive used python and still able get to root user succesfully on G-140w-F & G-140w-C . now what i want is to Modify the default configuration of the ONT ( that means if we hard reset the ONT it will restore our modified configuration). Thanks Again for the Guide
@thedroidgeek your script is awesome I have a Nokia G-140W-h ont and that looks similar. but when I use your script it returns me a message (Invalid magic) Would you help me
Got the root shell. Thank you so much.
But my router is configured for airtel and hard-coded in tr-069 page. I want to use it for bsnl, but I think I can change in .XML if needed. And I don't know whether some other settings will mitigate usage.
Can you help, is there any way to reset the router without vendor detail ?
At first I got same
Than I tried This. It seems the behavior is different across devices/fw versions - perhaps try setting LimitAccount_ONTUSER to false and then login with ONTUSER, if that doesn't work, I'd suggest you look for other settings that might look interesting in the xml.
Thanks for your work, it's awesome!!
I've got two devices: an I-240W-A, and a G-240W-B.
I got ssh access on the G-one! But it logs me out immediately:
`[tulio@TulainasV5 G-240W-B]$ ssh -vvv email@example.com
debug3: receive packet: type 52
debug3: send packet: type 1
Do you have an idea how to get around it?
I'm still working on the I-240 one, which has a firewall blocking ssh input traffic. I'm stilll working on it.
I got a G-140W-H here, hardware version 3FE48054BDAA, software version 3FE48077CGCB30, boot version U-Boot Mar-31-2020--23:07:20. This is for a brazilian ISP called "Oi". They block A LOT of ports, and don't give the option to bridge the router. They provide a very limited "userAdmin" user, and they have changed the AdminGPON user default password. No one has it. I was trying an easier way to get AdminGPON access, other than dumping the firmware with JTAG cable, wich I don't have. One way would be backing up the firmware from web interface, but this userAdmin user does not have access to firmware page. Does anyone here know of a hardcoded password for this model, or knows a way to dump the firmware from web interface with this "userAdmin" unprivilleged user? BTW, changing html at runtime vi browser debug console does not work to change configurations at this model. Web interface complains about user privilleges for any changes at the "WAN" tab.
i have G-140W-B from telmex, not telnet/ssh enable, download config using your excellent tool to unpack and only modify Telnet Enable from False to True, then pack with magic, when upload via page Import Config, it upload OK and then restart router, but when it restart no any changes, again download config and check and no changes appear done, do you have similar problem?, share some device daa
@thedroidgeek Thank you for the script. I was able to login to telnet and try to access shell which is asking for password that I try to provide same password as telnet but it says invalid. can you guys help to identify in the xml where is the shell password @shailparmar me having G-140w-F & G-140w-C both able to login to telnet but not getting shell prompt like you shared screenshot in this thread. Help to identify where is the configuration in the xml. I tried my best last 2 weeks. Thank you again
Awesome work and findings. Much to learn from your knowledge, thanks for sharing.
If someone have any details on my questions please help.
It is possible to do anything from ssh.
how did you managed to get the root pass from config.xml file?
Hello how did you do to access ONTUSER my model G-140W-H and I believe it is different in some configuration. I already set LimitAccount_ONTUSER to false, but without success when logging via telnet. Can you help me. I can send the configuration file.
Thanks for the answer. already done so much by telnet or ssh but not login.
If I send you the configuration file, you could see if I'm doing something wrong. or forgetting to activate something.
@joaodalvi did you find the password for AdminGPON ? I have a G-140W-C and ALC#FGU doesn't work for me either
Hello bro did you managed to get the SSH access it g2425g?
@thedroidgeek Thanks man. Great work!