Temat: Atak MITM zastosowanie praktyczne - ZTE MF286D
Od jakiegoś czasu ZTE nie daje możliwośći aktualizacji oprogramowania dla MF286D Telia B02 oraz B06. Kolejne aktualizacje od wersji B07 do B12 działają prawidłowo.
Jako, że oprogramowanie routera nie weryfikuje certyfikatów można łatwo oszukać mechanizm aktualizacji. Przygotowałem skrypt w postaci dodatku do mitmproxy który pozwala wykonać aktualizację z wersji B02 oraz B06.
Do przeprowadzenia ataku użyłem routera oraz komputera z mitmproxy na którym był uruchomiony mitmproxy.
1. Na routerze ustawiamy adres ip komputera z mitmproxy dla hosta dmeu.ztems.com (np. w /etc/hosts dla dnsmasq)
2. Na komputerze z mitmproxy ustawiamy rzeczywisty adres dla dmeu.ztems.com (może to być również w /etc/hosts) jeśli korzystamy z serwera dns na routerze
3. Uruchamiamy mitmproxy udając serwer ZTE:
mitmproxy --mode reverse:https://dmeu.ztems.com@443 -s ztedm.py4. Ustawiamy tryb "Ethernet" na MF286D
5. Podłączamy port WAN MF286D do routera i wyszukujemy aktualizację
A to skrypt ztedm.py
from base64 import b64decode, b64encode
from hashlib import md5
from xml.dom.minidom import parseString
class ZteDm:
def __init__(self):
self.nonce = None
self.version = None
self.fake_version = 'TELIA_MF286DV1.0.0B07'
self.firmware = {
'TELIA_MF286DV1.0.0B02': '/firmwarepackages/DE/ZTE/MF286D/214204/TELIA_MF286DV1.0.0B02-TELIA_MF286DV1.0.0B06-normal_CDN.dd',
'TELIA_MF286DV1.0.0B06': '/firmwarepackages/DE/ZTE/MF286D/224366/TELIA_MF286DV1.0.0B06-TELIA_MF286DV1.0.0B07_CDN.dd'
}
def hmac_get(self, 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 request(self, flow):
username = 'ZTEDM'
password = 'ZTEDM'
if 'Content-Type' in flow.request.headers and flow.request.headers['Content-Type'] == 'application/vnd.syncml.dm+xml':
xml_request = parseString(flow.request.text.strip())
results = xml_request.getElementsByTagName('Results')
if results:
data = results[0].getElementsByTagName('Data')[0].firstChild.nodeValue
for key in self.firmware:
if data == key:
self.version = data
results[0].getElementsByTagName('Data')[0].firstChild.nodeValue = self.fake_version
body = xml_request.toxml()
hmac = self.hmac_get(self.nonce, body, username, password)
flow.request.headers['x-syncml-hmac'] = f'algorithm=MD5, username="{username}", mac={hmac}'
flow.request.text = body
next_nonce = xml_request.getElementsByTagName('NextNonce')
if next_nonce:
self.nonce = next_nonce[0].firstChild.nodeValue
def response(self, flow):
username = 'zxmdmp'
password = 'ZTEPWD'
if 'Content-Type' in flow.response.headers and flow.response.headers['Content-Type'] == 'application/vnd.syncml.dm+xml':
xml_response = parseString(flow.response.text.strip())
if self.version:
targetref = xml_response.getElementsByTagName('TargetRef')[0].firstChild.nodeValue
sid = targetref.split('keyid=')[1]
replace_data = f'https://dleu.ztems.com:443/zxmdmp/download.do?doWhat=getDD&filename={self.firmware[self.version]}&sid={sid}'
replace = xml_response.getElementsByTagName('Replace')
if replace:
item = replace[0].getElementsByTagName('Item')
item[1].getElementsByTagName('Data')[0].firstChild.nodeValue = replace_data
body = xml_response.toxml()
hmac = self.hmac_get(self.nonce, body, username, password)
flow.response.headers['x-syncml-hmac'] = f'algorithm=MD5, username="{username}", mac={hmac}'
flow.response.text = body
self.version = None
next_nonce = xml_response.getElementsByTagName('NextNonce')
if next_nonce:
self.nonce = next_nonce[0].firstChild.nodeValue
addons = [ZteDm()]@Cezary jeśli uważasz, że to powinno się znaleźć w wątku o MF286D to przenieś proszę.