#!/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}')