#!/usr/bin/python3
# Original script by frutis, mods by Leo-PL
# Usage:
# 1. uncomment proper section for your router (MF283+, MF286, MF286A, MF286D).
# 2. set correct IME if you have one
# 3. Set correct version ID (the integrate_version from NVRAM) for your device if not already found here
# 4. Run the script - it shall return the URL of data update, if available for the set base version.
from base64 import b64decode, b64encode
from hashlib import md5
from secrets import choice
from string import printable, digits
import requests
from xml.dom.minidom import parseString
#import logging
#import http.client as http_client
#http_client.HTTPConnection.debuglevel = 1
#logging.basicConfig()
#logging.getLogger().setLevel(logging.DEBUG)
#requests_log = logging.getLogger('requests.packages.urllib3')
#requests_log.setLevel(logging.DEBUG)
#requests_log.propagate = True
# MF286D
#model = 'MF286D'
#TELIA
#firmware = 'TELIA_MF286DV1.0.0B10'
#ELISA
#firmware = 'ELISA_FI_MF286DV1.0.0B03'
#TIM
#firmware = 'TIM_IT_MF286DV1.0.0B07'
#PLAY
#firmware = 'NTT_PL_MF286DV1.0.0B02'
#imei_start = '86675404'
#URI = 'https://dmeu.ztems.com/zxmdmp/dm'
# MF286A
#model = 'MF286'
# Tele2 LT
#firmware = 'TELE2_LT_QMF286AV1.0.0B01'
#imei_start = '86821902'
#URI = 'https://dmeu.ztems.com/zxmdmp/dm'
# MF286
#model = 'MF286'
# EN_EEU (Orange PL)
#firmware = 'EN_EEU_MF286V1.0.1B01'
#imei_start = '86821902'
#URI = 'http://dms.ztems.com/zxmdmp/dm'
# MF283+
model = 'MF283PLUS'
firmware = 'EN_ORA_PL_MF283+V1.0.0B12'
#firmware = 'EN_ORA_PL_MF283+V1.0.0B06'
#firmware = 'EN_PL_MF283PLUSV1.0.0B02'
imei_start = '864780021'
URI = 'http://dms.ztems.com/zxmdmp/dm'
#request_type = 'devicerequest'
request_type = 'userrequest'
username = 'ZTEDM'
password = 'ZTEDM'
def imei_get(number):
while len(number) < 14:
number += choice(digits)
alphabet='0123456789'
imei = number + alphabet[0]
n = len(alphabet)
imei = tuple(alphabet.index(i)
for i in reversed(str(imei)))
check_digit = (sum(imei[::2]) +
sum(sum(divmod(i * 2, n))
for i in imei[1::2])) % n
number += alphabet[-check_digit]
return number
def nonce_get():
nonce = ''.join((choice(printable) for x in range(16)))
return b64encode(nonce.encode('utf-8')).decode('utf-8')
def hmac_get(nonce, body, username, password, mark=':'):
auth = username + mark + password
mark_enc = mark.encode('utf-8')
auth_b64 = b64encode(md5(auth.encode('utf-8')).digest())
nonce_md5 = b64decode(nonce)
body_b64 = b64encode(md5(body.encode('utf-8')).digest())
hmac = auth_b64 + mark_enc + nonce_md5 + mark_enc + body_b64
return b64encode(md5(hmac).digest()).decode('utf-8')
def body_get(body):
xml = ''
for line in body.splitlines():
xml += line.strip()
return xml
imei = imei_get(imei_start)
print(f'Model: {model}')
print(f'Firmware: {firmware}')
print(f'IMEI: {imei}')
try:
body1 = f'''
1.2
DM/1.2
3E8
1
{URI}
IMEI:{imei}
5000
1
1226
-
int
org.openmobilealliance.dm.firmwareupdate.{request_type}
indeterminate
0
2
1201
3
-
./DevInfo/Ext/Correlator
chr
0
-
./DevInfo/Ext/ErrCode
int
0
-
./DevInfo/Ext/InnerV
chr
-
./DevInfo/Ext/CompileT
chr
-
./DevInfo/Ext
node
CompileT/InnerV/ErrCode/Correlator
-
./DevInfo/DmV
chr
1.2
-
./DevInfo/Lang
chr
en
-
./DevInfo/DevId
chr
IMEI:{imei}
-
./DevInfo/Man
chr
ZTE
-
./DevInfo/Mod
chr
{model}
'''
body = body_get(body1)
hmac = hmac_get(nonce_get(), body, username, password)
headers = {'Accept-Charset': 'utf-8',
'Accept-Language': 'en',
'Connection': 'close',
'Cache-Control': 'no-cache',
'Content-Type': 'application/vnd.syncml.dm+xml',
'x-syncml-hmac': f'algorithm=MD5, username="{username}", mac={hmac}',
'User-Agent': None,
'Accept-Encoding': None}
response = requests.post(URI, data=body, headers=headers)
response.raise_for_status()
resp = parseString(response.text)
resp_uri = resp.getElementsByTagName('RespURI')[0].firstChild.nodeValue.replace('&', '&')
nonce = resp.getElementsByTagName('NextNonce')[0].firstChild.nodeValue
body2 = f'''
1.2
DM/1.2
3E8
2
IMEI:{imei}
5000
1
1
0
SyncHdr
IMEI:{imei}
{URI}
b64
syncml:auth-MAC
{nonce_get()}
401
2
1226
-
int
org.openmobilealliance.dm.firmwareupdate.{request_type}
indeterminate
0
3
1201
4
-
./DevInfo/Ext/Correlator
chr
0
-
./DevInfo/Ext/ErrCode
int
0
-
./DevInfo/Ext/InnerV
chr
-
./DevInfo/Ext/CompileT
chr
-
./DevInfo/Ext
node
CompileT/InnerV/ErrCode/Correlator
-
./DevInfo/DmV
chr
1.2
-
./DevInfo/Lang
chr
en
-
./DevInfo/DevId
chr
IMEI:{imei}
-
./DevInfo/Man
chr
ZTE
-
./DevInfo/Mod
chr
{model}
'''
body = body_get(body2)
hmac = hmac_get(nonce, body, username, password)
headers['x-syncml-hmac'] = f'algorithm=MD5, username="{username}", mac={hmac}'
response = requests.post(resp_uri, data=body, headers=headers)
response.raise_for_status()
resp = parseString(response.text)
nonce = resp.getElementsByTagName('NextNonce')[0].firstChild.nodeValue
body3 = f'''
1.2
DM/1.2
3E8
3
IMEI:{imei}
5000
1
2
0
SyncHdr
IMEI:{imei}
b64
syncml:auth-MAC
{nonce_get()}
200
2
2
5
Get
./DevDetail/SwV
200
3
2
5
-
./DevDetail/SwV
chr
{firmware}
'''
body = body_get(body3)
hmac = hmac_get(nonce, body, username, password)
headers['x-syncml-hmac'] = f'algorithm=MD5, username="{username}", mac={hmac}'
response = requests.post(resp_uri, data=body, headers=headers)
response.raise_for_status()
resp = parseString(response.text)
resp_replace = resp.getElementsByTagName('Replace')
if resp_replace:
data_uri = resp_replace[0].getElementsByTagName('Data')[1].firstChild.nodeValue.replace('&', '&')
response = requests.get(data_uri, headers=headers)
response.raise_for_status()
resp = parseString(response.text)
firm_desc = resp.getElementsByTagName('description')[0].firstChild.nodeValue
firm_uri = resp.getElementsByTagName('objectURI')[0].firstChild.nodeValue.replace('&', '&')
print(f'\nDescription: {firm_desc}')
print(f'URI: {firm_uri}')
else:
print('\nNothing found')
except requests.exceptions.HTTPError as http_error:
print(f'HTTP error: {http_error}')
except Exception as error:
print(f'Error: {error}')