Роутеры ZyXEL, например, модель Keenetic Lite II, имеют вкладку System → Configuration на которой можно скачать файл running-config (running configuration). Этот файл содержит текущие настройки, в том числе пароль для подключению к Интернет-провайдеру и пароль для Wi-Fi сети.
Пароли хранятся в закодированном виде, например:
authentication wpa-psk ns3 HfNV/Uq8ubal+UXhXERgHRdO authentication password ns3 9IpS3eYkkEN3Vld3CXRZtnBz
Строка с wpa-psk — это закодированный пароль от точки доступа Wi-Fi, а строка с password это пароль для Интернет-подключения к провайдеру.
Этот метод кодирования называют по-разному: NS3 / NDMS V2 / ZyXEL config.
Пользователь Felis-Sapiens с помощью обратного инженеринга раскрыл метод кодирования и даже написал скрипт на Python 3 для кодирования и декодирования.
А пользователь VasiliyP сделал ещё одну реализацию декодирования на JavaScript, благодаря чему пользователи могут декодировать NS3 (NDMS V2) прямо в окне веб-браузера.
Декодирование NS3 (NDMS V2) в веб-браузере
Создайте файл zyxel_scramble.htm:
gedit zyxel_scramble.htm
И скопируйте в него
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="robots" content="noindex"> <title>ns3 decoder</title> <script> var Base64 = { _keyStr: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', decode: function (f_input) { var result = ''; var v1, v2, v3; var v4, v5, v6, v7; var v8 = 0; f_input = f_input.replace(/[^A-Za-z0-9+/=]/g, ''); while (v8 < f_input.length) { v4 = this._keyStr.indexOf(f_input.charAt(v8++)); v5 = this._keyStr.indexOf(f_input.charAt(v8++)); v6 = this._keyStr.indexOf(f_input.charAt(v8++)); v7 = this._keyStr.indexOf(f_input.charAt(v8++)); v1 = v4 << 2 | v5 >> 4; v2 = (v5 & 15) << 4 | v6 >> 2; v3 = (v6 & 3) << 6 | v7; result = result + String.fromCharCode(v1); if (v6 != 64) { result = result + String.fromCharCode(v2) }; if (v7 != 64) { result = result + String.fromCharCode(v3) } }; return result } } function xor1(a0, buffer){ var a1 = 0; var a3 = 0; var v1 = 1; for (;;) { v1++; if (v1 == a0) break; a3 ^= buffer[a1 + v1 - 2]; } var a2 = 1; var t0 = 8; for (;;) { var v0 = 0xFF ^ a3; v0 = v0 << a2; var v1 = a3 >> a2; v1 ^= v0; v1 &= 0xFF; v0 = 0xFF ^ v1; v0 ^= a3; v1 = v1 << a2; v1 ^= v0; a2++; a3 = v1 & 0xFF; if (a2 == t0) break; } var v1 = 1; for (;;) { v1++; a1++; if (v1 == a0) return; buffer[a1 - 1] ^= a3; } } function xor2(a0, buffer, a2, a3){ var a1 = 0; a2 &= 0xFF; a3 &= 0xFF; var v1 = 1; for (;;) { v1 += 1; a1++; if (v1 == a0) return; var v0 = buffer[a1 - 1]; v0 ^= a3; a3 += a2; buffer[a1 - 1] = v0 & 0xFF; a2 = a3 - a2; } } function ns3decode(ns3){ //var buffer = Base64.decode(ns3).split('').map(v => v.charCodeAt(0)); var buffer = Base64.decode(ns3).split(''); for (var i = 0; i < buffer.length; i++) buffer[i] = buffer[i].charCodeAt(0); var s4_size = buffer.length; var s2_size = s4_size - 2; var size24924925 = s4_size * 0x24924925; var hi_size24924925 = size24924925 / 0x100000000; var tmp = s4_size - hi_size24924925; var addr_move_to = (hi_size24924925 + (tmp / 2 )) / 4; var s0 = buffer.splice(addr_move_to, 1); buffer.push(buffer[buffer.length - 1]); var mod = s0 % (s4_size - 1); var addr_move_to = mod; var s2 = buffer.splice(addr_move_to, 1); buffer.push(buffer[buffer.length - 1]); xor1(s4_size, buffer); xor2(s4_size, buffer, s2, s0); //var str = buffer.map(v => String.fromCharCode(v)).join(''); var arr = []; for (var i = 0; i < buffer.length; i++) arr[i] = String.fromCharCode(buffer[i]); var str = arr.join(''); for(var i = 0; i < buffer.length; i++){ if (!buffer[i]) { str = str.substr(0, i); break; } } return str; } function calculate(){ var ns3 = document.getElementById("input").value; if(ns3.length == 0){ document.getElementById("output").hidden = true; return; } var decoded = ns3decode(ns3); var box = document.getElementById("output"); box.value = decoded; box.hidden = false; box.select(); } function init() { var box = document.getElementById("input"); box.ondragenter = function() { this.value = ''; document.getElementById("output").value = ''; }; box.ondrop = function() { calculate(); }; box.onclick = function() { this.select(); }; box.select(); var box = document.getElementById("output"); box.onclick = function() { this.select(); }; } </script> </head> <body onload="init()" > <h1>ns3 decoder</h1> <input type="text" id="input" style="border: solid 1px #EEEEEE" value="BiEjZE6GOPmOmTtDIO0Gtcw+"/> <input type="submit" id="button" onClick="calculate()" value="decode"/> <br> <input type="text" id="output" hidden="true" style="border: solid 1px #EEEEEE" /> </body> </html>
Теперь просто откройте файл в любом веб-браузере.
Введите в окно данные для декодирования и нажмите кнопку «decode».
Кодирование и декодирование NS3 (NDMS V2) в командной строке
Создайте файл zyxel_scramble.py:
gedit zyxel_scramble.py
И скопируйте в него:
#!/usr/bin/env python3 # # zyxel_scramble.py # # author: Felis-Sapiens # # Decode password from ZyXEL config (NDMS V2) import sys from base64 import b64decode, b64encode from hashlib import md5 def first_step(password, x1, x2): x1 &= 0xff x2 &= 0xff for i in range(len(password)): password[i] = (password[i] ^ x2) & 0xff x2, x1 = x1 + x2, x2 return password def second_step(password): x = 0 for b in password: x ^= b for i in range(1,8): a = ((x >> i) ^ (~x << i)) & 0xff x = ((a << i) ^ (~a ^ x)) & 0xff for i in range(len(password)): password[i] ^= x return password def scramble_decode(password): if len(password) % 4 != 0: password += '=' * (4 - len(password) % 4) password = list(b64decode(password)) length = len(password) if length not in [0x12, 0x24, 0x48]: print('ERROR: invalid input length') return a2 = length // 7 x2 = password[a2] del password[a2] a1 = x2 % (length - 1) x1 = password[a1] del password[a1] password = second_step(password) password = first_step(password, x1, x2) zero_pos = password.index(0) if zero_pos != -1: length = zero_pos return bytes(password[:length]).decode() def scramble_encode(password): old_length = len(password) length = len(password) + 3 if length < 0x13: length = 0x12 elif length < 0x25: length = 0x24 elif length < 0x49: length = 0x48 else: print('ERROR: password is too long') return password = password.encode() md5_digest = md5(password).digest() password = list(password) if length != old_length: password.append(0) for i in range(old_length+1, length-2): password.append((password[i-old_length-1] * 2 + md5_digest[2 + i%14]) & 0xff) x1 = md5_digest[0] x2 = md5_digest[1] password = first_step(password, x1, x2) password = second_step(password) password.insert(x2 % (length - 1), x1) password.insert(length // 7, x2) return b64encode(bytes(password)) def main(): if len(sys.argv) < 3: print('Usage: zyxel_scramble.py decode|encode password') return if sys.argv[1] == 'decode': password = scramble_decode(sys.argv[2]) elif sys.argv[1] == 'encode': password = scramble_encode(sys.argv[2]) else: print('ERROR: unknown command', sys.argv[1]) return if password is not None: print(password) if __name__ == '__main__': main()
Для декодирования используйте команду вида:
python3 zyxel_scramble.py decode 'СТРОКА'
Например:
python3 zyxel_scramble.py decode '9IpS3eYkkEN3Vld3CXRZtnBz'
Для кодирования в NS3 (NDMS V2) используйте команду вида:
python3 zyxel_scramble.py encode 'СТРОКА'
Например:
python3 zyxel_scramble.py encode 'HackWare'
Будет выведена строка вида «b'HfNV/Uq8ubal+UXhXERgHRdO'» - вам нужно взять из неё то, что помещено в одинарные кавычки.
Заключение
Таким образом вы сможете восстановить пароль для подключения к Интернет-провайдеру если вы случайно сбросили ваш роутер до заводских настроек (либо у вас возникли проблемы при обновлении прошивки), но у вас сохранился конфигурационный файл роутера.
Связанные статьи:
- KeePass в Linux: как русифицировать и использовать, полезные плагины (100%)
- Hashcat не видит центральный процессор AMD (РЕШЕНО) (88%)
- Как зашифровать файлы в OpenSSL (62.1%)
- Как шифровать строки в OpenSSL (62.1%)
- Как удалить метаданные файла на Linux (50%)
- Скрипт для подключения и отключения от OpenVPN в зависимости от доступности сервера (RANDOM - 12.1%)