Initial commit

This commit is contained in:
dash
2026-01-23 20:47:54 +01:00
commit 0014f8210b
14 changed files with 1430 additions and 0 deletions

105
README.md Normal file
View File

@@ -0,0 +1,105 @@
# slpscan - service location protocol scanner
## intro
This is a research tool, created for looking deeper into SLP at the internet.
Due current events we decided to publish a version of it, to support defenders and researchers tackle the current ESXi exploitation scheme.
## usage
Show all supported functions
```
./slpscan.py -m ?
----------------------------------------------------------------------
SLPv1 Modes Operation Description
----------------------------------------------------------------------
svc_req_v1 1
svc_reply_v1 2
svc_attr_req_v1 6
svc_attr_reply_v1 7
svc_type_req_v1 9
svc_type_reply_v1 10
----------------------------------------------------------------------
SLPv2 Modes Operation Description
----------------------------------------------------------------------
svc_req_v2 1
svc_reply_v2 2
svc_attr_req_v2 6
svc_attr_reply_v2 7
svc_type_req_v2 9
svc_type_reply_v2 10
```
Do slp svc req v2
`./slpscan.py -l 192.168.170.50 -m svc_req_v2`
Do slp svc type req v1
`./slpscan.py -l 192.168.170.50 -m svc_type_req_v1`
Do slp attribute req for vmware v2
`./slpscan.py -l 192.168.170.50 -m svc_attr_req_v2`
Show supported probes
```
./slpscan.py -P?
SLP Request | Brief | Devices
-------------------------------------------------------------------------------
svc_type_req_holder_v1 | example pkt, svc_type_req_v1 |
svc_attr_req_holder_v1 | example request, svc_attr_req_v1 |
svc_req_holder_v2 | example pkt, svc_req_v2 |
svc_type_req_holder_v2 | example pkt, svc_type_req_v2 |
svc_attr_req_holder_v2 | example pkt, svc_attr_req_v2 |
VMWARE_SVC_Request_https | service:https |
```
For SLP identification against ESXi Hosts use the probe published within the release:
```
./slpscan.py -l <ip> -P VMWARE_SVC_Request_https
```
If you have a datacenter and need to check a big list of hosts use the -L option.
If you have a specific probe you can easily add it to the probe json file in the libs directory. There are already several examples to do so.
General help:
```
usage: slpscan.py [-h] [-l HOST] [-L HOSTLIST] [-p PORT] [-t THRCNT] [-m SLP_MODE]
[-P PROBE_MODE] [-d PKT_DELAY] [-T TIMEOUT] [-o OUTFILE] [-oj OUTFILE_JSON]
[-r UNRANDOM] [-R RANDOMIP]
service location protocol 0.3.7 by dash in published 2023
options:
-h, --help show this help message and exit
-l HOST, --host HOST host to check version
-L HOSTLIST, --hostlist HOSTLIST
hostlist to check
-p PORT, --port PORT slp port (default:427)
-t THRCNT, --threads THRCNT
how many threads
-m SLP_MODE, --slp-mode SLP_MODE
what attack mode to choose, ? for list
-P PROBE_MODE, --probe-mode PROBE_MODE
what probe to send, ? for list
-d PKT_DELAY, --packet-delay PKT_DELAY
set the delay(in seconds) a packet is sent, delay is per thread (1s and
10 threads, each second 10 threads are working)
-T TIMEOUT, --timeout TIMEOUT
timeout of socket recv
-o OUTFILE, --outfile OUTFILE
outfile in txt format
-oj OUTFILE_JSON, --outfile-json OUTFILE_JSON
outfile in json format
-r UNRANDOM, --unrandom UNRANDOM
disable random targetlist
-R RANDOMIP, --randomIP RANDOMIP
generate random ips on the fly
```
# outro
This tool is part of an ongoing research conducted by Marco Lux (ping@curesec.com) and Pedro Umbelino (pedro.umbelino@bitsight.com).

0
libs/__init__.py Normal file
View File

16
libs/srvloc_fortune.py Normal file
View File

@@ -0,0 +1,16 @@
# fortune cookies
import random
fck = ['2021 will **** less...',
'"Youre not allowed to say this, but...", is usually the preamble for something pretty pretty stupid which is coming next.',
'...',
'the plot thickens',
'just grabbing some b33rs'
'it just had to happen in 2023'
]
def rnd_fck():
nxt_fck = random.choice(fck)
return nxt_fck

42
libs/srvloc_globals.py Normal file
View File

@@ -0,0 +1,42 @@
import queue
global jout_Queue
jout_Queue = queue.Queue()
global rQ
rQ = queue.Queue()
global q
q = queue.Queue()
__tool_version__ = '0.3.7'
__tool_author__ = 'Marco Lux'
__tool_date__ = 'published 2023'
SLP_SVC_REQ = 0x1
SLP_SVC_REPLY = 0x2
SLP_ATTR_REQ = 0x6
SLP_ATTR_REPLY = 0x7
SLP_SVC_TYPE_REQ = 0x9
SLP_SVC_TYPE_REPLY = 0xa
# basic v1 pkt
req_dict_v1 = {'slp_ver': 1,
'slp_func': SLP_ATTR_REQ,
'slp_pkt_len': 0,
'slp_flags': 0,
'slp_dialect': 0,
'slp_lang': 0x656e,
'slp_enc': 3,
'slp_transx': 0x29A}
# basic v2 pkt
req_dict_v2 = {'slp_ver': 2,
'slp_func': SLP_SVC_TYPE_REQ,
'slp_pkt_len': 0,
'slp_flags': 0,
'slp_next_offset': 0,
'slp_xid': 0x666,
'slpintroduction_lang_tag_len': 2,
'slp_lang_tag': 0x656e}

176
libs/srvloc_helper.py Normal file
View File

@@ -0,0 +1,176 @@
import os
import sys
import pytz
import base64
import datetime
import random
from random import randrange
from libs.srvloc_globals import *
from libs.srvloc_log import printd, printe
def randomizeIP(iplist):
''' function to randomize ips to scan'''
# orig_list = iplist
# run 1
random.shuffle(iplist)
# run 2
random.shuffle(iplist)
return iplist
def generateIP():
blockOne = randrange(0, 255, 1)
blockTwo = randrange(0, 255, 1)
blockThree = randrange(0, 255, 1)
blockFour = randrange(0, 255, 1)
if blockOne == 10:
return generateIP()
elif blockOne == 172:
return generateIP()
elif blockOne == 192:
return generateIP()
else:
d = str(blockOne) + '.' + str(blockTwo) + '.' + \
str(blockThree) + '.' + str(blockFour)
return d
def console_size():
height, witdh = os.popen('stty size', 'r').read().split()
return (int(witdh), int(height))
def generate_randomIP(q, count):
i = 0
count = int(count)
# print 'cnt', count
while i != count:
ip = generateIP()
check_ip = [ip]
ip = check_blacklist(check_ip)
if len(ip) > 0:
q.put(ip[0])
i += 1
else:
print('{0} BLACKLISTED.'.format(ip))
print('gen %d' % (q.qsize()))
return
def timefield_rfc3339():
'''
implementing timestamp like rfc3339
'''
d = datetime.datetime.utcnow()
d_with_timezone = d.replace(tzinfo=pytz.UTC)
timestamp = d_with_timezone.isoformat()
return timestamp
def timedict_rfc3339():
'''
implementing timestamp like rfc3339
'''
d = datetime.datetime.utcnow()
d_with_timezone = d.replace(tzinfo=pytz.UTC)
timestamp = d_with_timezone.isoformat()
return {'timestamp': timestamp}
def ascii_check(rec):
'''check if banner has non-ascii values
'''
# yes, this is a bool now
ascii_bool = False
data = rec
# print(rec)
# poor mans clause for checking if ascii or not
try:
if type(rec) != int:
ascii_test = data.decode('ascii')
ascii_bool = True
else:
return rec
except UnicodeDecodeError as e:
ascii_bool = False
# its not ascii, so base64 encoding
if ascii_bool == False:
rec = base64.b64encode(data)
return(rec)
def clean_line(line):
line = line.rstrip('\r')
line = line.rstrip('\n')
return line
def check_file_exists(fname):
try:
stat = os.stat(fname)
except FileNotFoundError as e:
print(repr(e))
return False
return True
def check_file_readable(fname):
try:
fr = open(fname, 'r', 1)
except PermissionError as e:
print(repr(e))
return False
except Exception as e:
print(repr(e))
return False
return True
def create_hostlist(args):
#ret = check_file_exists(args.hostlist)
# if not ret:
# return False
fr = open(args.hostlist, 'r', 1)
for line in fr.readlines():
cline = clean_line(line)
rQ.put(cline)
rQ.put('')
# req_queue.put('EOF')
def check_blacklist(whites):
# def check_blacklist(blacks, whites):
'''
'''
fr = open('supply/blacklist.txt', 'r')
bips = fr.readlines()
white_len = len(whites)
blacklisted = []
for black in bips:
black = clean_line(black)
try:
whites.index(black)
whites.remove(black)
blacklisted.append(black)
print('BLACKLISTED {0}'.format(black))
except ValueError as e:
pass
white_len_after = len(whites)
#print('Whitelist: {0}/{1}'.format(white_len, white_len_after))
return whites

9
libs/srvloc_log.py Normal file
View File

@@ -0,0 +1,9 @@
def printd(data):
# if DEBUG_PROTO:
# print(data)
pass
def printe(data):
pass

267
libs/srvloc_main.py Normal file
View File

@@ -0,0 +1,267 @@
import os
import sys
import json
import time
import socket
import threading
import argparse
from libs.srvloc_globals import q, rQ, __tool_author__, __tool_version__, __tool_date__, jout_Queue
from libs.srvloc_proto_v1 import *
from libs.srvloc_proto_v2 import *
from libs.srvloc_helper import randomizeIP, generate_randomIP, clean_line, check_blacklist
from libs.srvloc_log import printd, printe
from libs.srvloc_fortune import rnd_fck
def parser_main():
parser_desc = 'service location protocol {0} by {1} in {2}'.format(
__tool_version__, __tool_author__, __tool_date__)
prog_desc = 'slpscan.py'
parser = argparse.ArgumentParser(prog=prog_desc, description=parser_desc)
parser.add_argument("-l", "--host", action="store",
required=False, help='host to check version', dest='host')
parser.add_argument("-L", "--hostlist", action="store",
required=False, help='hostlist to check', dest='hostlist')
parser.add_argument("-p", "--port", action="store", required=False,
default=427, help='slp port (default:427)', dest='port')
parser.add_argument("-t", "--threads", action="store", required=False,
default=50, help='how many threads', dest='thrCnt', type=int)
parser.add_argument("-m", "--slp-mode", action="store", required=False, default='',
help='what attack mode to choose, ? for list', dest='slp_mode')
parser.add_argument("-P", "--probe-mode", action="store", required=False, default=False,
help='what probe to send, ? for list', dest='probe_mode')
parser.add_argument("-d", "--packet-delay", action="store", required=False, type=float,
help='set the delay(in seconds) a packet is sent, delay is per thread (1s and 10 threads, each second 10 threads are working)',
dest='pkt_delay')
parser.add_argument("-T", "--timeout", action="store", required=False,
default=5, help='timeout of socket recv', dest='timeout')
parser.add_argument("-o", "--outfile", action="store",
required=False, help='outfile in txt format', dest='outfile')
parser.add_argument("-oj", "--outfile-json", action="store",
required=False, help='outfile in json format', dest='outfile_json')
parser.add_argument("-r", "--unrandom", action="store", required=False,
help='disable random targetlist', dest='unrandom')
parser.add_argument("-R", "--randomIP", action="store", required=False,
help='generate random ips on the fly', dest='randomip')
args = parser.parse_args()
return args
SLP_SVC_REQ = 0x1
SLP_SVC_REPLY = 0x2
SLP_ATTR_REQ = 0x6
SLP_ATTR_REPLY = 0x7
SLP_SVC_TYPE_REQ = 0x9
SLP_SVC_TYPE_REPLY = 0xa
# FIXME build other structure for modes
def choose_slp_mode(args):
lsize = 70
modes = {1: {'name': '{0:<30} {1:<15} {2:<30}'.format('SLPv1 Modes', 'Operation', 'Description'), 'Operation': '', 'Description': '', 'Method': ''},
# 2: {'name': '-'*lsize, 'Operation': '', 'Description': '', 'Method': ''},
# !!!!!!
21: {'name': 'svc_req_v1', 'Operation': SLP_SVC_REQ, 'Description': '', 'Method': build_slp_svc_req_v1()},
22: {'name': 'svc_reply_v1', 'Operation': SLP_SVC_REPLY, 'Description': '', 'Method': build_slp_reply_v1()},
26: {'name': 'svc_attr_req_v1', 'Operation': SLP_ATTR_REQ, 'Description': '', 'Method': build_svc_attr_req_v1()},
27: {'name': 'svc_attr_reply_v1', 'Operation': SLP_ATTR_REPLY, 'Description': '', 'Method': build_slp_attr_reply_v1()},
29: {'name': 'svc_type_req_v1', 'Operation': SLP_SVC_TYPE_REQ, 'Description': '', 'Method': build_slp_svc_type_req_v1()},
30: {'name': 'svc_type_reply_v1', 'Operation': SLP_SVC_TYPE_REPLY, 'Description': '', 'Method': build_slp_type_reply_v1()},
38: {'name': '{0:<30} {1:<15} {2:<30}'.format('SLPv2 Modes', 'Operation', 'Description'), 'Operation': '', 'Description': '', 'Method': ''},
40: {'name': 'svc_req_v2', 'Operation': SLP_SVC_REQ, 'Description': '', 'Method': build_slp_svc_req_v2()},
41: {'name': 'svc_reply_v2', 'Operation': SLP_SVC_REPLY, 'Description': '', 'Method': build_slp_reply_v2()},
45: {'name': 'svc_attr_req_v2', 'Operation': SLP_ATTR_REQ, 'Description': '', 'Method': build_slp_attr_req_v2()},
46: {'name': 'svc_attr_reply_v2', 'Operation': SLP_ATTR_REPLY, 'Description': '', 'Method': build_slp_attr_reply_v2()},
48: {'name': 'svc_type_req_v2', 'Operation': SLP_SVC_TYPE_REQ, 'Description': '', 'Method': build_slp_svc_type_req_v2()},
49: {'name': 'svc_type_reply_v2', 'Operation': SLP_SVC_TYPE_REPLY, 'Description': '', 'Method': build_slp_type_reply_v2()},
}
slp_mode = args.slp_mode
if slp_mode == '?':
print()
for k in modes.keys():
# print(modes[k])
name = modes[k]['name']
operation = modes[k]['Operation']
desc = modes[k]['Description']
if name.startswith('SLPv'):
print('-'*lsize)
print('{0:<30} {1:<15} {2:<30}'.format(name, operation, desc))
if name.startswith('SLPv'):
print('-'*lsize)
print()
sys.exit()
else:
for k in modes.keys():
if slp_mode == modes[k]['name']:
pkt = modes[k]['Method']
return pkt
print('Unknown mode use -m? for showing supported modes')
sys.exit()
def make_request(host, port, args, pkt):
human = []
timeout = args.timeout
slp_mode = args.slp_mode
probe_mode = args.probe_mode
pkt_delay = args.pkt_delay
if pkt_delay:
time.sleep(pkt_delay)
try:
# build up socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# we want to have a timeout
s.settimeout(timeout)
s.connect((host, port))
# send the discovery packet
s.send(pkt)
# getting the data of the connection
rec = s.recv(4096)
data_dict = {'target': host+':' +
str(port), 'reply_pkt': rec, 'pkt': pkt}
hdata = '%s:%d' % (host, port)
hdump = '%s' % (repr(rec))
# place stuff in one of the queues
human.append(hdata)
human.append(hdump)
# human.append(reply_dict)
rQ.put(human)
except socket.timeout:
printe('%s timeout' % host)
except socket.error:
printe('%s refused' % host)
def run_mainthreads(args, pkt):
# wanna have a c00kie?!
fck = rnd_fck()
printd(fck)
if args.outfile:
fw = open(args.outfile, 'w')
if args.outfile_json:
fwj = open(args.outfile_json, 'w')
if args.host:
host = args.host
print('Hostmode: %s' % host)
line = clean_line(host)
bl = [host]
wh = check_blacklist(bl)
if len(wh) == 1:
q.put(line)
else:
# print('{0} Blacklisted!!!'.format(host))
sys.exit()
elif args.hostlist:
ipL = []
hostlist = args.hostlist
print('Hostlistmode')
fr = open(hostlist, 'r')
rBuf = fr.readlines()
for l in rBuf:
l = clean_line(l)
ipL.append(l)
if not args.unrandom:
iplist = randomizeIP(ipL)
else:
iplist = ipL
iplist = check_blacklist(iplist)
list = [q.put(query) for query in iplist]
elif args.randomip:
randIP = int(args.randomip)
print('RandomIPs: %d' % (randIP))
else:
print('Unknown or no mode choosen. cya')
sys.exit()
if not args.randomip:
print('Targets: %d' % (q.qsize()))
else:
# lets start the thread for generating randomIPs
printd('Starting random thread:')
randIPThread = threading.Thread(
target=generate_randomIP, args=(q, args.randomip))
randIPThread.daemon = True
randIPThread.start()
# FIXME
# quick fix so we do not miss the threading loop
# better would be a counter in the loop itself
time.sleep(5)
port = int(args.port)
thrCnt = args.thrCnt
thrList = []
printd('Starting loop')
while True:
if len(thrList) < thrCnt and q.qsize() > 0:
newthread = threading.Thread(target=make_request, args=(
q.get(), port, args, pkt))
newthread.daemon = True
newthread.start()
thrList.append(newthread)
for entry in thrList:
if entry.is_alive() == False:
entry.join()
thrList.remove(entry)
time.sleep(0.1)
if rQ.qsize() > 0:
pout = rQ.get()
pp = '%s' % (pout)
print('[RAW] ', pp)
print()
if args.outfile:
fw.write(pp + '\n')
if jout_Queue.qsize() > 0:
jdata = jout_Queue.get()
print(jdata)
if args.outfile_json:
fwj.write(jdata + '\n')
if q.qsize() == 0 and len(thrList) == 0:
break
def print_slp_modes():
data = '''
Supported modes:
* slp_type_request
requesting the supported ressources at the remote device
'''
print(data)

71
libs/srvloc_parse.py Normal file
View File

@@ -0,0 +1,71 @@
import struct
def parse_slp_reply(data_dict):
'''
Attribute Reply
Version | Function | PktLen | Flags | Dialect | Lang | Encoding | TransactionID | Error Code | Attribute List Len | Attribdata
1b 1b 2b 1b 1b 2b 2b 2b 2b 2b
'''
reply_pkt = data_dict['reply_pkt']
slp_ver, slp_func, slp_pkt_len, slp_flags, slp_dialect, slp_lang, slp_enc, slp_transx,\
slp_err, slp_attr_list = struct.unpack(
'>BBHBBHHHHH', reply_pkt[:16])
reply_dict = {'slp_ver': slp_ver,
'slp_func': slp_func,
'slp_pkt_len': slp_pkt_len,
'slp_flags': slp_flags,
'slp_dialect': slp_dialect,
'slp_lang': slp_lang,
'slp_enc': slp_enc,
'slp_transx': slp_transx,
'slp_err': slp_err,
'slp_attr_list': slp_attr_list}
# FIXME FIXME
# ... disabling parsing for now
# print(reply_dict)
attr_data = reply_pkt[16:]
# print(attr_data)
s_attr_data = attr_data.split(b'(')
print(s_attr_data)
s_attr_data.remove(b'')
hwdata = s_attr_data
hw_dict = {}
for item in hwdata:
item = item.decode()
item = item.rstrip(')')
key_a, val_a = item.split('=')
hw_dict[key_a] = val_a
#hwdata = hwdata.decode()
shwdata = hwdata.split('(')
#hw_dict = {}
# shwdata.remove('')
print('-'*80)
print(hwdata)
print(hw_dict)
print('-'*80)
for b in shwdata:
b = b.rstrip(')')
b = b.split('=')
key_b = b[0]
val_b = b[1]
hw_dict[key_b] = val_b
print(b)
if key_b == 'x-hp-p1':
hw_dict[key_b] = {}
['x-hp-p1', 'MFG:Hewlett-Packard']
s_attr_data.remove(b')')
print(s_attr_data)
for entry in s_attr_data:
ee = entry.decode()
ee = ee.split(':')
# print(ee)
hw_dict[ee[0]] = ee[1]
return hw_dict

151
libs/srvloc_probes.json Normal file
View File

@@ -0,0 +1,151 @@
{
"1": {
"name": "svc_type_req_holder_v1",
"brief": "example pkt, svc_type_req_v1",
"devices": [],
"description": "example pkt, svc_type_req_v1",
"probe": {
"base": {
"slp_ver": 1,
"slp_func": 9,
"slp_pkt_len": 0,
"slp_flags": 0,
"slp_dialect": 0,
"slp_lang": 25966,
"slp_enc": 3,
"slp_transx": 666
},
"data": {
"slp_prev_res_list": 0,
"slp_all": 65535,
"slp_scope": "default"
}
}
},
"2": {
"name": "svc_attr_req_holder_v1",
"brief": "example request, svc_attr_req_v1",
"devices": [],
"description": "example request, svc_attr_req_v1",
"probe": {
"base": {
"slp_ver": 1,
"slp_func": 6,
"slp_pkt_len": 0,
"slp_flags": 0,
"slp_dialect": 0,
"slp_lang": 25966,
"slp_enc": 3,
"slp_transx": 667
},
"data": {
"slp_prev_res_list": 0,
"slp_svc_len": 8,
"slp_svc_url": "service:",
"slp_scope_len": 0,
"slp_attr_len": 0
}
}
},
"3": {
"name": "svc_req_holder_v2",
"brief": "example pkt, svc_req_v2",
"devices": [],
"description": "example pkt, svc_req_v2",
"probe": {
"base": {
"slp_ver": 2,
"slp_func": 1,
"slp_pkt_len": 0,
"slp_flags": 0,
"slp_next_offset": 0,
"slp_xid": 12345,
"slp_ltag_len": 2,
"slp_ltag": 25966
},
"data": {
"slp_prev_res_list": 0,
"slp_svc_type_len": 0,
"slp_svc_type": "service:https",
"slp_scope_len": 13,
"slp_scope": "default"
}
}
},
"4": {
"name": "svc_type_req_holder_v2",
"brief": "example pkt, svc_type_req_v2",
"devices": [],
"description": "example pkt, svc_type_req_v2",
"probe": {
"base": {
"slp_ver": 2,
"slp_func": 9,
"slp_pkt_len": 0,
"slp_flags": 0,
"slp_next_offset": 0,
"slp_xid": 45267,
"slp_ltag_len": 2,
"slp_ltag": 25966
},
"data": {
"slp_prev_res_list": 0,
"slp_all": 65535,
"slp_scope": "default",
"slp_scope_len": 7
}
}
},
"5": {
"name": "svc_attr_req_holder_v2",
"brief": "example pkt, svc_attr_req_v2",
"devices": [],
"description": "example pkt, svc_attr_req_v2",
"probe": {
"base": {
"slp_ver": 2,
"slp_func": 6,
"slp_pkt_len": 0,
"slp_flags": 0,
"slp_next_offset": 0,
"slp_xid": 19121,
"slp_ltag_len": 2,
"slp_ltag": 25966
},
"data": {
"slp_prev_res_list": 0,
"slp_svc_url_len": 13,
"slp_svc_url": "service:https",
"slp_scope_len": 0,
"slp_scope": "",
"slp_tag_len": 0,
"slp_tag": ""
}
}
},
"6": {
"name": "VMWARE_SVC_Request_https",
"brief": "service:https",
"devices": [],
"description": "",
"probe": {
"base": {
"slp_ver": 2,
"slp_func": 1,
"slp_pkt_len": 0,
"slp_flags": 0,
"slp_next_offset": 0,
"slp_xid": 23152,
"slp_ltag_len": 2,
"slp_ltag": 25966
},
"data": {
"slp_prev_res_list": 0,
"slp_svc_type_len": 13,
"slp_svc_type": "service:https",
"slp_scope_len": 7,
"slp_scope": "default"
}
}
}
}

184
libs/srvloc_probes.py Normal file
View File

@@ -0,0 +1,184 @@
import json
from libs.srvloc_proto_v1 import build_slp_base_v1, compute_len_v1, _slp_attr_req_v1, _slp_svc_req_v1, \
_slp_svc_reply_v1, _slp_attr_reply_v1, _slp_type_reply_v1, _slp_svc_type_req_v1
from libs.srvloc_proto_v2 import _slp_svc_req_v2, _slp_svc_reply_v2, _slp_svc_type_req_v2, _slp_attr_req_v2,_slp_attr_reply_v2, _slp_type_reply_v2, compute_len_v2, build_slp_base_v2
from libs.srvloc_helper import console_size
fname = 'srvloc_probes.json'
SLP_SVC_REQ = 0x1
SLP_SVC_REPLY = 0x2
SLP_ATTR_REQ = 0x6
SLP_ATTR_REPLY = 0x7
SLP_SVC_TYPE_REQ = 0x9
SLP_SVC_TYPE_REPLY = 0xa
def open_probe_file(fname):
fr = open(fname, 'r')
jprobes = json.loads(fr.read())
fr.close()
return jprobes
def print_probes(jprobes):
width, heigth = console_size()
# 30% name
# 40% brief
# 15% devices
# yeah i need to substract max width len as well ...
name_res = 0.30
brief_res = 0.40
dev_res = 0.15
tab_vert_res = 0.90
width = width * 0.90
name_space = int(width * name_res)
brief_space = int(width * brief_res)
dev_space = int(width * dev_res)
max_table_vert = int(width * tab_vert_res)
print('{0: <{1}}| {2: <{3}}| {4: <{5}}'.format(
'SLP Request', name_space, 'Brief', brief_space, 'Devices', dev_space))
print('-'*(max_table_vert))
for k in jprobes.keys():
name = jprobes[k]['name']
desc = jprobes[k]['brief']
devices = jprobes[k]['devices']
str_devices = ",".join(devices)
print('{0: <{1}}| {2: <{3}}| {4: <{5}}'.format(
name, name_space, desc, brief_space, str_devices, dev_space))
def probe_packet(jprobes, pname):
# print('123')
for k in jprobes.keys():
name = jprobes[k]['name']
if pname == name:
# print('Found probe')
base = jprobes[k]['probe']['base']
data = jprobes[k]['probe']['data']
slp_ver = jprobes[k]['probe']['base']['slp_ver']
slp_func = jprobes[k]['probe']['base']['slp_func']
if slp_ver == 1:
slp_ver, slp_func, slp_pkt_len, slp_flags, slp_dialect, slp_lang, slp_enc, slp_transx = jprobes[k]['probe']['base'].values(
)
pkt1 = build_slp_base_v1(
slp_ver, slp_func, slp_pkt_len, slp_flags, slp_dialect, slp_lang, slp_enc, slp_transx)
if slp_func == SLP_SVC_REQ:
slp_prev_res_list_len, slp_rest_list, slp_pred_len, slp_pred = jprobes[k]['probe']['data'].values(
)
pkt2 = _slp_svc_req_v1(
slp_prev_res_list_len, slp_rest_list.encode(), slp_pred_len, slp_pred.encode())
elif slp_func == SLP_SVC_REPLY:
err_code, num_urls, url_lifetime, url_len, urls, num_auths = jprobes[k]['probe']['data'].values(
)
pkt2 = _slp_svc_reply_v1(
err_code, num_urls, url_lifetime, url_len, urls.encode(), num_auths)
elif slp_func == SLP_ATTR_REQ:
slp_prev_res_list, slp_svc_len, slp_svc_url, slp_scope_len, slp_attr_len = jprobes[k]['probe']['data'].values(
)
pkt2 = _slp_attr_req_v1(
slp_prev_res_list, slp_svc_len, slp_svc_url.encode(), slp_scope_len, slp_attr_len)
elif slp_func == SLP_ATTR_REPLY:
err_code, attr_list_len, attr_list, attr_auths = jprobes[k]['probe']['data'].values(
)
pkt2 = _slp_attr_reply_v1(
err_code, attr_list_len, attr_list.encode(), attr_auths)
elif slp_func == SLP_SVC_TYPE_REQ:
slp_prev_res_list, slp_all, slp_scope = jprobes[k]['probe']['data'].values(
)
pkt2 = _slp_svc_type_req_v1(
slp_prev_res_list, slp_all, slp_scope.encode())
elif slp_func == SLP_SVC_TYPE_REPLY:
err_code, svc_type_count, svc_type_list_len, svc_type_list = jprobes[k]['probe']['data'].values(
)
pkt2 = _slp_type_reply_v1(
err_code, svc_type_count, svc_type_list_len, svc_type_list.encode())
else:
print('{0} function not yet supported.'.format(slp_func))
return False
pkt = pkt1 + pkt2
pkt_rdy = compute_len_v1(pkt)
elif slp_ver == 2:
slp_ver, slp_func, slp_pkt_len, slp_flags, slp_next_offset, slp_xid, slp_ltag_len, slp_ltag = jprobes[k]['probe']['base'].values(
)
pkt1 = build_slp_base_v2(
slp_ver, slp_func, slp_pkt_len, slp_flags, slp_next_offset, slp_xid, slp_ltag_len, slp_ltag)
if slp_func == SLP_SVC_REQ:
slp_prev_res_list, slp_svc_type_len, slp_svc_type, slp_scope_len, slp_scope = jprobes[k]['probe']['data'].values(
)
pkt2 = _slp_svc_req_v2(
slp_prev_res_list, slp_svc_type_len, slp_svc_type.encode(), slp_scope_len, slp_scope.encode())
elif slp_func == SLP_SVC_REPLY:
err_code, num_urls, reserved, url_lifetime, url_len, urls, num_auths = jprobes[k]['probe']['data'].values(
)
pkt2 = _slp_svc_reply_v2(
err_code, num_urls, reserved, url_lifetime, url_len, urls.encode(), num_auths)
elif slp_func == SLP_SVC_ACK:
err_code = jprobes[k]['probe']['data'].values(
)
pkt2 = _slp_svc_ack_v2(
err_code)
elif slp_func == SLP_ATTR_REQ:
slp_prev_res_list, slp_svc_url_len, slp_svc_url, slp_scope_len, slp_scope, slp_tag_len, slp_tag = jprobes[k]['probe']['data'].values(
)
pkt2 = _slp_attr_req_v2(
slp_prev_res_list, slp_svc_url_len, slp_svc_url.encode(), slp_scope_len, slp_scope.encode(), slp_tag_len, slp_tag.encode())
elif slp_func == SLP_ATTR_REPLY:
err_code, attr_list_len, attr_list, attr_auths = jprobes[k]['probe']['data'].values(
)
pkt2 = _slp_attr_reply_v2(
err_code, attr_list_len, attr_list.encode(), attr_auths)
elif slp_func == SLP_SVC_TYPE_REQ:
slp_prev_res_list, slp_all, slp_scope, slp_scope_len = jprobes[k]['probe']['data'].values(
)
pkt2 = _slp_svc_type_req_v2(
slp_prev_res_list, slp_all, slp_scope.encode(), slp_scope_len)
elif slp_func == SLP_SVC_TYPE_REPLY:
err_code, svc_type_list_len, svc_type_list = jprobes[k]['probe']['data'].values(
)
pkt2 = _slp_type_reply_v2(
err_code, svc_type_list_len, svc_type_list.encode())
else:
print('{0} function not yet supported.'.format(slp_func))
pkt = pkt1 + pkt2
pkt_rdy = compute_len_v2(pkt)
# print('* SLP_SVC_REQ implemented')
else:
print('Not supported version {0}'.format(slp_ver))
return False
print(base)
print(data)
return pkt_rdy

195
libs/srvloc_proto_v1.py Normal file
View File

@@ -0,0 +1,195 @@
import struct
import random
from libs.srvloc_log import printd
SLP_SVC_REQ = 0x1
SLP_SVC_REPLY = 0x2
SLP_ATTR_REQ = 0x6
SLP_ATTR_REPLY = 0x7
SLP_SVC_TYPE_REQ = 0x9
SLP_SVC_TYPE_REPLY = 0xa
SLP_TRANSX_RAND = True
SLP_XID_RAND = True
CRAFT_AUTO_LEN = True
DEBUG_PROTO = True
#####################
###### SLP v1 #######
#####################
def build_slp_base_v1(slp_ver=1, slp_func=0, slp_pkt_len=0, slp_flags=0, slp_dialect=0, slp_lang=0x656e, slp_enc=3, slp_transx=0x29A):
'''
this method is used for building up what i call the *base* part of the SLPv1 specification. Base parameters are all parameters part
of every valid SLPv1 packet.
Returns: pkt - a ready base packet, WARNING this packet is missing the function part and also pkt_len is not computed yet
'''
# print(slp_ver)
if SLP_TRANSX_RAND:
slp_transx = random.randint(1, 65535)
# basic pkt structure v1
pkt = struct.pack('>BBHBBHHH', slp_ver, slp_func, slp_pkt_len,
slp_flags, slp_dialect, slp_lang, slp_enc, slp_transx)
printd(pkt)
return pkt
def compute_len_v1(pkt):
'''
This method is used for computing the overall length of a complete SLPv1 packet. It's default usage is being called at the end of
packet building process.
Params: pkt - a ready pkt without correct pkt_len, usually 0 (zero)
Returns: pkt - a ready pkt with correct pkt_len
'''
pkt_len = len(pkt)
pkt_byte_len = struct.pack('>H', pkt_len)
pkt = pkt[:2]+pkt_byte_len+pkt[4:]
return pkt
########SLP_SVC_REQ = 0x1
def build_slp_svc_req_v1():
pkt1 = build_slp_base_v1(slp_func=SLP_SVC_REQ)
pkt2 = _slp_svc_req_v1()
pkt = pkt1+pkt2
pkt_rdy = compute_len_v1(pkt)
return pkt_rdy
def _slp_svc_req_v1(slp_prev_res_list_len=1, slp_resp_list=b'A', slp_pred_len=1, slp_pred=b'B'):
'''
'''
pkt2 = struct.pack('>H' + str(slp_prev_res_list_len) + 'sH' + str(slp_pred_len) + 's',
slp_prev_res_list_len,
slp_resp_list,
slp_pred_len,
slp_pred
)
return pkt2
########################################################################################################################################
###### SLP_SVC_REPLY = 0x2
def build_slp_reply_v1():
pkt1 = build_slp_base_v1(slp_func=SLP_SVC_REPLY)
pkt2 = _slp_svc_reply_v1()
pkt = pkt1+pkt2
pkt_rdy = compute_len_v1(pkt)
return pkt_rdy
def _slp_svc_reply_v1(err_code=0, num_urls=1, url_lifetime=667, url_len=12, urls=b'service:wbem', num_auths=0):
'''
'''
#url_len = len(urls)
pkt2 = struct.pack('>HHHH'+str(url_len)+'sB', err_code,
num_urls, url_lifetime, url_len, urls, 0)
return pkt2
########################################################################################################################################
#############SLP_ATTR_REQ = 0x6
def build_svc_attr_req_v1():
pkt1 = build_slp_base_v1(slp_func=SLP_ATTR_REQ)
pkt2 = _slp_attr_req_v1()
pkt = pkt1+pkt2
pkt_rdy = compute_len_v1(pkt)
return pkt_rdy
def _slp_attr_req_v1(slp_prev_res_list=0, slp_url_len=8, slp_svc_url=b'service:', slp_scope_len=0, slp_attr_len=0):
# attr request structure
pkt2 = struct.pack('>HH'+str(slp_url_len)+'sHH', slp_prev_res_list,
slp_url_len, slp_svc_url, slp_scope_len, slp_attr_len)
return pkt2
########################################################################################################################################
#########SLP_ATTR_REPLY = 0x7
def build_slp_attr_reply_v1():
pkt1 = build_slp_base_v1(slp_func=SLP_ATTR_REPLY)
pkt2 = _slp_attr_reply_v1()
pkt = pkt1+pkt2
pkt_rdy = compute_len_v1(pkt)
return pkt_rdy
def _slp_attr_reply_v1(err_code=0, attr_list_len=9, attr_list=b'attribute', attr_auths=0):
pkt2 = struct.pack('>HH' + str(attr_list_len) + 'sB',
err_code,
attr_list_len,
attr_list,
attr_auths
)
return pkt2
########################################################################################################################################
##################SLP_SVC_TYPE_REQ = 0x9
def build_slp_svc_type_req_v1():
pkt1 = build_slp_base_v1(slp_func=SLP_SVC_TYPE_REQ)
pkt2 = _slp_svc_type_req_v1()
pkt = pkt1+pkt2
pkt_rdy = compute_len_v1(pkt)
return pkt_rdy
def _slp_svc_type_req_v1(slp_prev_res_list=0, slp_all=65535, slp_scope=b'default'):
slp_scope_len = len(slp_scope)
pkt2 = struct.pack('>HHH'+str(slp_scope_len)+'s', slp_prev_res_list,
slp_all, slp_scope_len, slp_scope)
return pkt2
########################################################################################################################################
########SLP_SVC_TYPE_REPLY = 0xa
def build_slp_type_reply_v1():
pkt1 = build_slp_base_v1(slp_func=SLP_SVC_TYPE_REPLY)
pkt2 = _slp_type_reply_v1()
pkt = pkt1+pkt2
pkt_rdy = compute_len_v1(pkt)
return pkt_rdy
def _slp_type_reply_v1(err_code=0, svc_type_count=1, svc_type_list_len=31, svc_type_list=b'service:Windows:wbem:http:https'):
pkt2 = struct.pack('>HHH' + str(svc_type_list_len) + 's',
err_code,
svc_type_count,
svc_type_list_len,
svc_type_list
)
return pkt2
########################################################################################################################################

172
libs/srvloc_proto_v2.py Normal file
View File

@@ -0,0 +1,172 @@
import struct
import random
from libs.srvloc_log import printd
SLP_SVC_REQ = 0x1
SLP_SVC_REPLY = 0x2
SLP_ATTR_REQ = 0x6
SLP_ATTR_REPLY = 0x7
SLP_SVC_TYPE_REQ = 0x9
SLP_SVC_TYPE_REPLY = 0xa
SLP_TRANSX_RAND = True
SLP_XID_RAND = True
CRAFT_AUTO_LEN = True
DEBUG_PROTO = True
#####################
###### SLP v2 #######
#####################
def build_slp_base_v2(slp_ver=2, slp_func=0, slp_pkt_len=0, slp_flags=0, slp_next_offset=0, slp_xid=0x299, slp_ltag_len=2, slp_ltag=0x656e):
if SLP_XID_RAND:
slp_xid = random.randint(1, 65535)
# basic pkt structure v2
pkt = struct.pack('>BBBHHBHHHH', slp_ver, slp_func, 0, slp_pkt_len,
slp_flags, 0, slp_next_offset, slp_xid, slp_ltag_len, slp_ltag)
return pkt
def compute_len_v2(pkt):
pkt_len = len(pkt)
pkt_byte_len = struct.pack('>bH', 0, pkt_len)
pkt = pkt[:2] + pkt_byte_len + pkt[5:]
return pkt
#########SLP_SVC_REQ = 0x1
def build_slp_svc_req_v2():
pkt1 = build_slp_base_v2(slp_func=SLP_SVC_REQ)
pkt2 = _slp_svc_req_v2()
pkt = pkt1+pkt2
pkt_rdy = compute_len_v2(pkt)
return pkt_rdy
def _slp_svc_req_v2(slp_prev_res_list=0, slp_svc_type_len=0, slp_svc_type=b'service:wbem', slp_scope_len=7, slp_scope=b'default'):
'''
'''
pkt2 = struct.pack('>HH'+str(slp_svc_type_len)+'sH'+str(slp_scope_len)+'sHH', slp_prev_res_list,
slp_svc_type_len, slp_svc_type, slp_scope_len, slp_scope, 0, 0)
return pkt2
##########
#######SLP_SVC_REPLY = 0x2
def build_slp_reply_v2():
pkt1 = build_slp_base_v2(slp_func=SLP_SVC_REPLY)
pkt2 = _slp_svc_reply_v2()
pkt = pkt1+pkt2
pkt_rdy = compute_len_v2(pkt)
return pkt_rdy
def _slp_svc_reply_v2(err_code=0, num_urls=1, reserved=0, url_lifetime=667, url_len=12, urls=b'service:wbem', num_auths=0):
'''
'''
pkt2 = struct.pack('>HHBHH'+str(url_len)+'sB', err_code,
num_urls, reserved, url_lifetime, url_len, urls, 0)
return pkt2
######SLP_ATTR_REQ = 0x6
def build_slp_attr_req_v2():
pkt1 = build_slp_base_v2(slp_func=SLP_ATTR_REQ)
pkt2 = _slp_attr_req_v2()
pkt = pkt1+pkt2
pkt_rdy = compute_len_v2(pkt)
return pkt_rdy
def _slp_attr_req_v2(slp_prev_res_list=0, slp_svc_url_len=12, slp_svc_url=b'service:wbem', slp_scope_len=0, slp_scope=b'', slp_tag_len=0, slp_tag=b''):
pkt2 = struct.pack('>HH'+str(slp_svc_url_len)+'sH'+str(slp_scope_len)+'sH'+str(slp_tag_len)+'sH', slp_prev_res_list,
slp_svc_url_len, slp_svc_url, slp_scope_len, slp_scope, slp_tag_len, slp_tag, 0)
return pkt2
#######################
######SLP_ATTR_REPLY = 0x7
def build_slp_attr_reply_v2():
pkt1 = build_slp_base_v2(slp_func=SLP_ATTR_REPLY)
pkt2 = _slp_attr_reply_v2()
pkt = pkt1+pkt2
pkt_rdy = compute_len_v2(pkt)
return pkt_rdy
def _slp_attr_reply_v2(err_code=4, attr_list_len=0, attr_list=b'', attr_auths=4):
pkt2 = struct.pack('>HH' + str(attr_list_len) + 'sB',
err_code,
attr_list_len,
attr_list,
attr_auths
)
return pkt2
##########
#####SLP_SVC_TYPE_REQ = 0x9
def build_slp_svc_type_req_v2():
pkt1 = build_slp_base_v2(slp_func=SLP_SVC_TYPE_REQ)
pkt2 = _slp_svc_type_req_v2()
pkt = pkt1+pkt2
pkt_rdy = compute_len_v2(pkt)
return pkt_rdy
def _slp_svc_type_req_v2(slp_prev_res_list=0, slp_all=65535, slp_scope=b'default', slp_scope_len=7):
pkt2 = struct.pack('>HHH'+str(slp_scope_len)+'s', slp_prev_res_list,
slp_all, slp_scope_len, slp_scope)
return pkt2
########################
#####SLP_SVC_TYPE_REPLY = 0xa
def build_slp_type_reply_v2():
pkt1 = build_slp_base_v2(slp_func=SLP_SVC_TYPE_REPLY)
pkt2 = _slp_type_reply_v2()
pkt = pkt1+pkt2
pkt_rdy = compute_len_v2(pkt)
return pkt_rdy
def _slp_type_reply_v2(err_code=0, svc_type_list_len=31, svc_type_list=b'service:Windows:wbem:http:https'):
pkt2 = struct.pack('>HH' + str(svc_type_list_len) + 's',
err_code,
svc_type_list_len,
svc_type_list
)
return pkt2
###########################

41
slpscan.py Executable file
View File

@@ -0,0 +1,41 @@
#!/usr/bin/env python
import os
import sys
import time
from libs.srvloc_proto_v1 import CRAFT_AUTO_LEN
from libs.srvloc_main import print_slp_modes, parser_main, run_mainthreads, choose_slp_mode
from libs.srvloc_globals import *
from libs.srvloc_log import printe, printd
from libs.srvloc_probes import open_probe_file, print_probes, probe_packet
def run(args):
fname = 'libs/srvloc_probes.json'
slp_mode = args.slp_mode
global CRAFT_AUTO_LEN
if args.probe_mode:
jprobes = open_probe_file(fname)
if args.probe_mode == '?' or args.probe_mode == 'help':
print_probes(jprobes)
sys.exit()
else:
pkt = probe_packet(jprobes, args.probe_mode)
else:
pkt = choose_slp_mode(args)
print('PKT: ', pkt)
run_mainthreads(args, pkt)
def main():
args = parser_main()
run(args)
if __name__ == "__main__":
main()

1
supply/blacklist.txt Normal file
View File

@@ -0,0 +1 @@
127.0.0.2