def recv(self): """Receive a packet at the Enqueue layer, performing reassemble of fragmented packets if necessary. :return: received :class:`SAPEnqueue` packet :rtype: :class:`SAPEnqueue` :raise socket.error: if the connection was close """ # Receive the NI packet packet = SAPNIStreamSocket.recv(self) if SAPEnqueue in packet and packet[SAPEnqueue].more_frags: log_sapenqueue.debug("Received Enqueue fragmented packet") head = str(packet[SAPEnqueue])[:20] data = str(packet[SAPEnqueue])[20:] total_length = packet[SAPEnqueue].len - 20 recvd_length = len(packet[SAPEnqueue]) - 20 log_sapenqueue.debug("Received %d up to %d bytes", recvd_length, total_length) while recvd_length < total_length and packet[SAPEnqueue].more_frags == 1: response = SAPNIStreamSocket.recv(self)[SAPEnqueue] data += str(response)[20:] recvd_length += len(response) - 20 log_sapenqueue.debug("Received %d up to %d bytes", recvd_length, total_length) packet = SAPEnqueue(head + data) return packet
def main(): options = parse_options() if not has_fau_timer: print ("[-] Required library not found. Please install it from https://github.com/seecurity/mona-timing-lib") return if options.verbose: logging.basicConfig(level=logging.DEBUG) # Initiate the connection conn = SAPNIStreamSocket.get_nisocket(options.remote_host, options.remote_port) print("[*] Connected to the SAP Router %s:%d" % (options.remote_host, options.remote_port)) # Retrieve the router version used by the server if not specified if options.router_version is None: options.router_version = get_router_version(conn) print("[*] Using SAP Router version %d" % options.router_version) print("[*] Checking if the server is vulnerable to a timing attack (CVE-2014-0984) ...") with open(options.output, "w") as f: c = 0 for i in range(0, len(options.password) + 1): password = options.password[:i] + "X" * (len(options.password) - i) print("[*] Trying with password (%s) len %d" % (password, len(password))) for _ in range(0, options.tries): try_password(options, password, f, c) c += 1
def main(): options = parse_options() if options.verbose: logging.basicConfig(level=logging.DEBUG) # Initiate the connection conn = SAPNIStreamSocket.get_nisocket(options.remote_host, options.remote_port) print "[*] Connected to the message server %s:%d" % (options.remote_host, options.remote_port) client_string = options.client # Send MS_LOGIN_2 packet p = SAPMS(flag=0x00, iflag=0x08, toname=client_string, fromname=client_string) print "[*] Sending login packet" response = conn.sr(p)[SAPMS] print "[*] Login performed, server string: %s" % response.fromname print "[*] Listening to server messages" try: while (True): # Send MS_SERVER_LST packet response = conn.recv()[SAPMS] print "[*] Message received !" response.show() except SocketError: print "[*] Connection error" except KeyboardInterrupt: print "[*] Cancelled by the user"
def main(): options = parse_options() if options.verbose: logging.basicConfig(level=logging.DEBUG) # Initiate the connection conn = SAPNIStreamSocket.get_nisocket(options.remote_host, options.remote_port) print "[*] Connected to the message server %s:%d" % (options.remote_host, options.remote_port) client_string = options.client # Send MS_LOGIN_2 packet p = SAPMS(flag=0x00, iflag=0x08, toname=client_string, fromname=client_string) print "[*] Sending login packet" response = conn.sr(p)[SAPMS] print "[*] Login performed, server string: %s" % response.fromname # Sends a message to another client p = SAPMS(flag=0x02, iflag=0x01, toname=options.target, fromname=client_string, opcode=1) p /= Raw(options.message) print "[*] Sending packet to: %s" % options.target conn.send(p)
def main(): options = parse_options() if options.verbose: logging.basicConfig(level=logging.DEBUG) # Initiate the connection conn = SAPNIStreamSocket.get_nisocket(options.remote_host, options.remote_port) print "[*] Connected to the SAP Router %s:%d" % (options.remote_host, options.remote_port) # Retrieve the router version used by the server if not specified if options.router_version is None: options.router_version = get_router_version(conn) print "[*] Using SAP Router version %d" % options.router_version print "[*] Checking if the server is vulnerable to a timing attack (CVE-2014-0984) ..." with open(options.output, "w") as f: c = 0 for i in range(0, len(options.password) + 1): password = options.password[:i] + "X" * (len(options.password) - i) print "[*] Trying with password (%s) len %d" % (password, len(password)) for _ in range(0, options.tries): try_password(options, password, f, c) c += 1
def do_connect(self, args): """ Initiate the connection to the Message Server service. The connection is registered using the client_string runtime option. """ # Create the socket connection try: self.connection = SAPNIStreamSocket.get_nisocket(self.options.remote_host, self.options.remote_port) except SocketError as e: self._error("Error connecting with the Message Server") self._error(str(e)) return self._print("Attached to %s / %d" % (self.options.remote_host, self.options.remote_port)) # Send MS_LOGIN_2 packet p = SAPMS(flag=0x00, iflag=0x08, toname=self.runtimeoptions["client_string"], fromname=self.runtimeoptions["client_string"]) self._debug("Sending login packet") response = self.connection.sr(p)[SAPMS] if response.errorno == 0: self.runtimeoptions["server_string"] = response.fromname self._debug("Login performed, server string: %s" % response.fromname) self._print("pysap's Message Server monitor, connected to %s / %d" % (self.options.remote_host, self.options.remote_port)) self.connected = True else: if response.errorno in ms_errorno_values: self._error("Error performing login: %s" % ms_errorno_values[response.errorno]) else: self._error("Unknown error performing login: %d" % response.errorno)
def route_test(rhost, rport, thost, tport): print "[*] Routing connections to %s:%s" % (thost, tport) # Initiate the connection. We don't want the NI Stream Socket to handle # keep-alive messages, as the response to connect requests are NI_PONG conn = SAPNIStreamSocket.get_nisocket(rhost, rport, keep_alive=False) router_string = [SAPRouterRouteHop(hostname=rhost, port=rport), SAPRouterRouteHop(hostname=thost, port=tport)] router_string_lens = map(len, map(str, router_string)) p = SAPRouter(type=SAPRouter.SAPROUTER_ROUTE, route_entries=len(router_string), route_talk_mode=1, route_rest_nodes=1, route_length=sum(router_string_lens), route_offset=router_string_lens[0], route_string=router_string, ) response = conn.sr(p) if router_is_error(response): status = 'error' elif router_is_pong(response): status = 'open' conn.close() return status
def main(): options = parse_options() if options.verbose: logging.basicConfig(level=logging.DEBUG) print("[*] Connecting to SAP Router %s:%d (talk mode %s)" % (options.remote_host, options.remote_port, options.talk_mode)) # Retrieve the router version used by the server if not specified if options.router_version is None: conn = SAPNIStreamSocket.get_nisocket(options.remote_host, options.remote_port, keep_alive=False) options.router_version = get_router_version(conn) conn.close() print("[*] Using SAP Router version %d" % options.router_version) options.talk_mode = {"raw": 1, "ni": 0}[options.talk_mode] results = [] for (host, port) in parse_target_hosts(options.target_hosts, options.target_ports): status = route_test(options.remote_host, options.remote_port, host, port, options.talk_mode, options.router_version) if options.verbose: print("[*] Status of %s:%s: %s" % (host, port, status)) if status == "open": results.append((host, port)) print("[*] Host/Ports found open:") for (host, port) in results: print("\tHost: %s\tPort:%s" % (host, port))
def get_nisocket(cls, host=None, port=None, route=None, password=None, talk_mode=None, router_version=None, **kwargs): """Helper function to obtain a :class:`SAPRoutedStreamSocket`. :param host: target host to connect to if not specified in the route :type host: C{string} :param port: target port to connect to if not specified in the route :type port: ``int`` :param route: route to use for determining the SAP Router to connect :type route: C{string} or ``list`` of :class:`SAPRouterRouteHop` :param password: target password if not specified in the route :type password: C{string} :param talk_mode: the talk mode to use for requesting the route :type talk_mode: ``int`` :param router_version: the router version to use for requesting the route :type router_version: ``int`` :keyword kwargs: arguments to pass to :class:`SAPRoutedStreamSocket` constructor :return: connected socket through the specified route :rtype: :class:`SAPRoutedStreamSocket` :raise SAPRouteException: if the route request to the target host/port was not accepted by the SAP Router :raise socket.error: if the connection to the target host/port failed or the SAP Router returned an error """ # If no route was provided, use the standard SAPNIStreamSocket # get_nisocket method if route is None: return SAPNIStreamSocket.get_nisocket(host, port, **kwargs) # If the route was provided using a route string, convert it to a # list of hops if isinstance(route, str): route = SAPRouterRouteHop.from_string(route) # If the host and port were specified, we need to add a new hop to # the route if host is not None and port is not None: route.append(SAPRouterRouteHop(hostname=host, port=port, password=password)) # Connect to the first hop in the route (it should be the SAP Router) sock = socket.create_connection((route[0].hostname, int(route[0].port))) # Create a SAPRoutedStreamSocket instance specifying the route return cls(sock, route, talk_mode, router_version, **kwargs)
def __init__(self, sock, route, talk_mode=None, router_version=None, keep_alive=True, base_cls=None): """Initialize the routed stream socket. It should receive a socket connected with the SAP Router, and a route to specify to it. After initialization and if the route is accepted all calls to send() and recv() would be made to the target host/service trough the SAP Router. :param sock: a socket connected to the SAP Router :type sock: C{socket} :param route: a route to specify to the SAP Router :type route: ``list`` of :class:`SAPRouterRouteHop` :param talk_mode: the talk mode to use when routing :type talk_mode: ``int`` :param router_version: the router version to use for requesting the route. If no router version is provided, it will be obtained from the SAP Router by means of a control packet. :type router_version: ``int`` :param keep_alive: if true, the socket will automatically respond to keep-alive request messages. Otherwise, the keep-alive messages are passed to the caller in :class:`recv` and :class:`sr` calls. :type keep_alive: ``bool`` :param base_cls: the base class to use when receiving packets, it uses SAPNI as default if no class specified :type base_cls: :class:`Packet` class """ self.routed = False self.talk_mode = talk_mode self.router_version = router_version # Connect to the SAP Router SAPNIStreamSocket.__init__(self, sock, keep_alive=keep_alive, base_cls=base_cls) # Now that we've a NIStreamSocket, retrieve the router version if # was not specified if self.router_version is None: self.router_version = get_router_version(self) self.route_to(route, talk_mode)
def route(self): """Requests a route to forward the traffic through the remote SAP Router. @raise SAPRouteException: if the route request is denied @raise Exception: if an error occurred when requesting the route """ log_saprouter.debug("Routing to %s:%d" % (self.target_address, self.target_port)) # Creates the connection with the SAP Router (remote_address, remote_port) = self.remote_host router = SAPNIStreamSocket.get_nisocket(remote_address, remote_port, keep_alive=self.keep_alive) # Build the Route request packet router_string = [SAPRouterRouteHop(hostname=remote_address, port=remote_port), SAPRouterRouteHop(hostname=self.target_address, port=self.target_port, password=self.target_pass)] router_string_lens = list(map(len, list(map(str, router_string)))) p = SAPRouter(type=SAPRouter.SAPROUTER_ROUTE, route_entries=len(router_string), route_talk_mode=self.talk_mode, route_rest_nodes=1, route_length=sum(router_string_lens), route_offset=router_string_lens[0], route_string=router_string) # Send the request and grab the response response = router.sr(p) if SAPRouter in response: response = response[SAPRouter] if router_is_pong(response): log_saprouter.debug("Route request to %s:%d accepted by %s:%d" % (self.target_address, self.target_port, remote_address, remote_port)) self.routed = True elif router_is_error(response) and response.return_code == -94: log_saprouter.debug("Route request to %s:%d not accepted by %s:%d" % (self.target_address, self.target_port, remote_address, remote_port)) raise SAPRouteException("Route request not accepted") else: log_saprouter.error("Router send error: %s" % response.err_text_value) raise Exception("Router error: %s", response.err_text_value) else: log_saprouter.error("Wrong response received") raise Exception("Wrong response received") return router
def test_sapnistreamsocket_close(self): """Test SAPNIStreamSocket with a server that closes the connection""" self.start_server(SAPNITestHandlerClose) sock = socket.socket() sock.connect((self.test_address, self.test_port)) self.client = SAPNIStreamSocket(sock, keep_alive=False) with self.assertRaises(socket.error): self.client.sr(self.test_string) self.stop_server()
def recv(self): """Receive a packet from the target host. If the talk mode in use is native and we've already set the route, the packet received is a raw packet. Otherwise, the packet received is a NI layer packet in the same way the :class:`SAPNIStreamSocket` works. """ # If we're working on native mode and the route was accepted, we don't # need the NI layer anymore. Just use the plain socket inside the # NIStreamSockets. if self.routed and self.talk_mode == 1: return StreamSocket.recv(self) # If the route was not accepted yet or we're working on non-native talk # mode, we need the NI layer. return SAPNIStreamSocket.recv(self)
def send(self, packet): """Send a packet. If the talk mode in use is native the packet sent is a raw packet. Otherwise, the packet is a NI layer packet in the same way the :class:`SAPNIStreamSocket` works. :param packet: packet to send :type packet: Packet """ # If we're working on native mode and the route was accepted, we don't # need the NI layer anymore. Just use the plain socket inside the # NIStreamSockets. if self.routed and self.talk_mode == 1: return StreamSocket.send(self, packet) # If the route was not accepted yet or we're working on non-native talk # mode, we need the NI layer. return SAPNIStreamSocket.send(self, packet)
def test_sapnistreamsocket_getnisocket(self): """Test SAPNIStreamSocket get nisocket class method""" self.start_server(SAPNITestHandler) self.client = SAPNIStreamSocket.get_nisocket(self.test_address, self.test_port) packet = self.client.sr(self.test_string) packet.decode_payload_as(Raw) self.client.close() self.assertIn(SAPNI, packet) self.assertEqual(packet[SAPNI].length, len(self.test_string)) self.assertEqual(packet.payload.load, self.test_string) self.stop_server()
def test_sapnistreamsocket_without_keep_alive(self): """Test SAPNIStreamSocket without keep alive""" self.start_server(SAPNITestHandlerKeepAlive) sock = socket.socket() sock.connect((self.test_address, self.test_port)) self.client = SAPNIStreamSocket(sock, keep_alive=False) packet = self.client.sr(self.test_string) packet.decode_payload_as(Raw) self.client.close() # We should receive a PING instead of our packet self.assertIn(SAPNI, packet) self.assertEqual(packet[SAPNI].length, len(SAPNI.SAPNI_PING)) self.assertEqual(packet.payload.load, SAPNI.SAPNI_PING) self.stop_server()
def test_sapnistreamsocket_with_keep_alive(self): """Test SAPNIStreamSocket with keep alive""" self.start_server(SAPNITestHandlerKeepAlive) sock = socket.socket() sock.connect((self.test_address, self.test_port)) self.client = SAPNIStreamSocket(sock, keep_alive=True) packet = self.client.sr(self.test_string) packet.decode_payload_as(Raw) self.client.close() # We should receive our packet, the PING should be handled by the # stream socket self.assertIn(SAPNI, packet) self.assertEqual(packet[SAPNI].length, len(self.test_string)) self.assertEqual(packet.payload.load, self.test_string) self.stop_server()
def test_sapnistreamsocket_base_cls(self): """Test SAPNIStreamSocket handling of custom base packet classes""" self.start_server(SAPNITestHandler) class SomeClass(Packet): fields_desc = [StrField("text", None)] sock = socket.socket() sock.connect((self.test_address, self.test_port)) self.client = SAPNIStreamSocket(sock, base_cls=SomeClass) packet = self.client.sr(self.test_string) self.client.close() self.assertIn(SAPNI, packet) self.assertIn(SomeClass, packet) self.assertEqual(packet[SAPNI].length, len(self.test_string)) self.assertEqual(packet[SomeClass].text, self.test_string) self.stop_server()
def server_mode(options): """"Implements the niping server running mode :param options: option set from the command line :type options: Values """ if not options.host: options.host = "0.0.0.0" sock = socket() try: sock.bind((options.host, options.port)) sock.listen(0) print("") print(datetime.today().ctime()) print("ready for connect from client ...") while True: sc, sockname = sock.accept() client = SAPNIStreamSocket(sc) print("") print(datetime.today().ctime()) print("connect from host '{}', client hdl {} o.k.".format(sockname[0], client.fileno())) try: while True: r = client.recv() client.send(r.payload) except SocketError: pass finally: print("") print(datetime.today().ctime()) print("client hdl {} disconnected ...".format(client.fileno())) except SocketError: print("[*] Connection error") except KeyboardInterrupt: print("[*] Cancelled by the user") finally: sock.shutdown(SHUT_RDWR) sock.close()
def main(): options = parse_options() if options.verbose: logging.basicConfig(level=logging.DEBUG) # Initiate the connection conn = SAPNIStreamSocket.get_nisocket(options.remote_host, options.remote_port) print "[*] Connected to the message server %s:%d" % (options.remote_host, options.remote_port) client_string = options.client # Send MS_LOGIN_2 packet p = SAPMS(flag=0x00, iflag=0x08, toname=client_string, fromname=client_string) print "[*] Sending login packet:" response = conn.sr(p)[SAPMS] print "[*] Login OK, Server string: %s" % response.fromname server_string = response.fromname # Send a Dump Info packet for each possible Dump for i in ms_dump_command_values.keys(): # Skip MS_DUMP_MSADM and MS_DUMP_COUNTER commands as the info # is included in other dump commands if i in [1, 12]: continue p = SAPMS(flag=0x02, iflag=0x01, toname=server_string, fromname=client_string, opcode=0x1e, dump_dest=0x02, dump_command=i) print "[*] Sending dump info", ms_dump_command_values[i] response = conn.sr(p)[SAPMS] if (response.opcode_error != 0): print "Error:", ms_opcode_error_values[response.opcode_error] print response.opcode_value
def main(): options = parse_options() if options.verbose: logging.basicConfig(level=logging.DEBUG) response = False p = SAPRouter(type=SAPRouter.SAPROUTER_ADMIN) if options.stop: p.adm_command = 5 print "[*] Requesting stop of the remote SAP Router" elif options.soft: p.adm_command = 9 print "[*] Requesting a soft shutdown of the remote SAP Router" response = True elif options.info: p.adm_command = 2 if options.info_password: if len(options.info_password) > 19: print "[*] Password too long, truncated at 19 characters" p.adm_password = options.info_password print "[*] Requesting info using password", p.adm_password else: print "[*] Requesting info" response = True elif options.new_route: p.adm_command = 3 print "[*] Requesting a refresh of the router table" elif options.trace: p.adm_command = 4 print "[*] Requesting a toggle on the trace settings" elif options.cancel: p.adm_command = 6 p.adm_client_ids = map(int, options.cancel.split(",")) print "[*] Requesting a cancel of the route(s) with client id(s) %s" % p.adm_client_ids response = True elif options.dump: p.adm_command = 7 print "[*] Requesting a dump of the buffers" elif options.flush: p.adm_command = 8 print "[*] Requesting a flush of the buffers" elif options.hide: p.adm_command = 14 print "[*] Requesting a hide on the errors to clients" response = True elif options.set_peer: p.adm_command = 10 p.adm_address_mask = options.set_peer print "[*] Request a set peer trace for the address mask %s" % p.adm_address_mask response = True elif options.clear_peer: p.adm_command = 11 p.adm_address_mask = options.clear_peer print "[*] Request a clear peer trace for the address mask %s" % p.adm_address_mask response = True elif options.trace_conn: p.adm_command = 12 p.adm_client_ids = map(int, options.trace_conn.split(",")) print "[*] Requesting a connection trace with client id(s) %s" % p.adm_client_ids response = True else: print "[*] No command specified !" return # Initiate the connection conn = SAPNIStreamSocket.get_nisocket(options.remote_host, options.remote_port) print "[*] Connected to the SAP Router %s:%d" % (options.remote_host, options.remote_port) # Retrieve the router version used by the server if not specified if options.router_version: p.version = options.router_version else: p.version = get_router_version(conn) or p.version print "[*] Using SAP Router version %d" % p.version # Send the router admin request print "[*] Sending Router Admin packet" if options.verbose: p.show2() conn.send(p) # Grab the response if required if response: # Some responses has no SAPRouter's packet format and are raw strings, # we need to get the SAP NI layer first and then check if we could go # down to the SAPRouter layer. response = conn.recv()[SAPNI] if SAPRouter in response and response[SAPRouter].payload: response = response[SAPRouter] # If the response is an error, print and exit if router_is_error(response): print "[*] Error requesting info:" if options.verbose: response.show2() else: print response.err_text_value # Otherwise, print all the packets sent by the SAP Router else: print "[*] Response:" try: while (response): print response.payload response = conn.recv() except: pass
class PySAPNIStreamSocketTest(unittest.TestCase): test_port = 8005 test_address = "127.0.0.1" test_string = "TEST" * 10 def start_server(self, handler_cls): self.server = ThreadingTCPServer((self.test_address, self.test_port), handler_cls, bind_and_activate=False) self.server.allow_reuse_address = True self.server.server_bind() self.server.server_activate() self.server_thread = Thread(target=self.server.serve_forever) self.server_thread.start() def stop_server(self): self.server.shutdown() self.server.server_close() self.server_thread.join() def test_sapnistreamsocket(self): """Test SAPNIStreamSocket""" self.start_server(SAPNITestHandler) sock = socket.socket() sock.connect((self.test_address, self.test_port)) self.client = SAPNIStreamSocket(sock) packet = self.client.sr(self.test_string) packet.decode_payload_as(Raw) self.client.close() self.assertIn(SAPNI, packet) self.assertEqual(packet[SAPNI].length, len(self.test_string)) self.assertEqual(packet.payload.load, self.test_string) self.stop_server() def test_sapnistreamsocket_base_cls(self): """Test SAPNIStreamSocket handling of custom base packet classes""" self.start_server(SAPNITestHandler) class SomeClass(Packet): fields_desc = [StrField("text", None)] sock = socket.socket() sock.connect((self.test_address, self.test_port)) self.client = SAPNIStreamSocket(sock, base_cls=SomeClass) packet = self.client.sr(self.test_string) self.client.close() self.assertIn(SAPNI, packet) self.assertIn(SomeClass, packet) self.assertEqual(packet[SAPNI].length, len(self.test_string)) self.assertEqual(packet[SomeClass].text, self.test_string) self.stop_server() def test_sapnistreamsocket_getnisocket(self): """Test SAPNIStreamSocket get nisocket class method""" self.start_server(SAPNITestHandler) self.client = SAPNIStreamSocket.get_nisocket(self.test_address, self.test_port) packet = self.client.sr(self.test_string) packet.decode_payload_as(Raw) self.client.close() self.assertIn(SAPNI, packet) self.assertEqual(packet[SAPNI].length, len(self.test_string)) self.assertEqual(packet.payload.load, self.test_string) self.stop_server() def test_sapnistreamsocket_without_keep_alive(self): """Test SAPNIStreamSocket without keep alive""" self.start_server(SAPNITestHandlerKeepAlive) sock = socket.socket() sock.connect((self.test_address, self.test_port)) self.client = SAPNIStreamSocket(sock, keep_alive=False) packet = self.client.sr(self.test_string) packet.decode_payload_as(Raw) self.client.close() # We should receive a PING instead of our packet self.assertIn(SAPNI, packet) self.assertEqual(packet[SAPNI].length, len(SAPNI.SAPNI_PING)) self.assertEqual(packet.payload.load, SAPNI.SAPNI_PING) self.stop_server() def test_sapnistreamsocket_with_keep_alive(self): """Test SAPNIStreamSocket with keep alive""" self.start_server(SAPNITestHandlerKeepAlive) sock = socket.socket() sock.connect((self.test_address, self.test_port)) self.client = SAPNIStreamSocket(sock, keep_alive=True) packet = self.client.sr(self.test_string) packet.decode_payload_as(Raw) self.client.close() # We should receive our packet, the PING should be handled by the # stream socket self.assertIn(SAPNI, packet) self.assertEqual(packet[SAPNI].length, len(self.test_string)) self.assertEqual(packet.payload.load, self.test_string) self.stop_server() def test_sapnistreamsocket_close(self): """Test SAPNIStreamSocket with a server that closes the connection""" self.start_server(SAPNITestHandlerClose) sock = socket.socket() sock.connect((self.test_address, self.test_port)) self.client = SAPNIStreamSocket(sock, keep_alive=False) with self.assertRaises(socket.error): self.client.sr(self.test_string) self.stop_server()
def main(): options = parse_options() level = logging.INFO if options.verbose: level = logging.DEBUG logging.basicConfig(level=level, format='%(message)s') logging.info("[*] Loading fingerprint database") fingerprint_db = FingerprintDB(options.fingerprints) # Check if we were asked to add a new fingerprint if options.add_fingerprint: if not options.version_info: logging.info( "[-] You must provide version info to add new entries to the fingerprint database !" ) return logging.info("[*] Adding a new entry to the fingerprint database") fingerprint_db.add_fingerprint(options.new_fingerprint_file, options.version_info) return misses = [] matches = [] logging.info("[*] Trying to fingerprint version using %d packets" % (len(fingerprint_targets))) # Trigger some errors and check with fingerprint db l = len(fingerprint_targets) i = 1 for (target, packet) in list(fingerprint_targets.items()): logging.info("[*] (%d/%d) Fingerprint for packet '%s'" % (i, l, target)) # Initiate the connection and send the packet conn = SAPNIStreamSocket.get_nisocket(options.remote_host, options.remote_port, keep_alive=False) if packet is None: # Timeout error error_text = conn.recv().err_text_value else: error_text = conn.sr(packet).err_text_value matched = fingerprint_db.match_fingerprint(target, error_text) if matched: logging.info("[*] (%d/%d) Fingerprint for packet '%s' matched !" % (i, l, target)) matches.append((target, matched)) else: logging.info( "[*] (%d/%d) Fingerprint for packet '%s' not matched" % (i, l, target)) misses.append((target, error_text)) i += 1 if matches: versions = [] counts = {} logging.info("\n[*] Matched fingerprints (%d/%d):" % (len(matches), l)) for (target, fingerprints) in matches: logging.info("[+] Request: %s" % target) for fingerprint in fingerprints: match = {} for field in version_info_fields: if field in fingerprint: match[field] = fingerprint[field] if match not in versions: counts[str(match)] = 0 versions.append(match) counts[str(match)] += 1 logging.info("\n[*] Probable versions (%d):" % len(versions)) for version in versions: msg = " ".join([ "%s: \"%s\"" % (field, version[field]) for field in version_info_fields if version[field] != "" ]) logging.info("[*]\tHits: %d Version: %s" % (counts[str(version)], msg)) if misses: logging.info("\n[*] Non matched fingerprints (%d/%d):" % (len(misses), l)) for (target, _) in misses: logging.info("[-] Request: %s" % target) logging.info( "\n[-] Some error values where not found in the fingerprint database. " "If you want to contribute submit a issue to https://github.com/SecureAuthCorp/pysap " "or write an email to [email protected] with the following information along " "with the SAP Router file information and how it was configured.\n" ) options.new_entries = True # Build new entries for the fingerprint database if options.new_entries: new_fingerprint = {} for (target, error_text) in misses: new_fingerprint[target] = [{}] for field in fingerprint_fields: new_fingerprint[target][0][field] = getattr(error_text, field) # Expand with matched targets also for (target, fingerprint) in matches: new_fingerprint[target] = fingerprint logging.info("\nNew fingerprint saved to: %s" % options.new_fingerprint_file) with open(options.new_fingerprint_file, 'w') as f: json.dump(new_fingerprint, f) version_info = { "patch_number": "", "source_id": "", "update_level": "", "file_version": "", "platform": "", "submitted_by": "", "comment": "", } logging.info("\n\nVersion information to complete and submit:") logging.info("%s" % json.dumps(version_info, indent=4))
def main(): options = parse_options() if options.verbose: logging.basicConfig(level=logging.DEBUG) # Initiate the connection conn = SAPNIStreamSocket.get_nisocket(options.remote_host, options.remote_port) print "[*] Connected to the message server %s:%d" % (options.remote_host, options.remote_port) # Set release information prop = SAPMSProperty(id=7, release="720", patchno=70, supplvl=0, platform=0) p = SAPMS(flag=0x01, iflag=0x01, toname="MSG_SERVER", fromname=options.client, opcode=0x43, property=prop) print "[*] Setting release information" conn.send(p) # Perform the login enabling the DIA+BTC+ICM services p = SAPMS(flag=0x08, iflag=0x08, msgtype=0x89, toname="-", fromname=options.client) print "[*] Sending login packet" conn.sr(p)[SAPMS] print "[*] Login performed" # Changing the status to starting p = SAPMS(flag=0x01, iflag=0x09, msgtype=0x05, toname="-", fromname=options.client) print "[*] Changing server's status to starting" conn.send(p) # Set IP address p = SAPMS( flag=0x01, iflag=0x01, toname="MSG_SERVER", fromname=options.client, opcode=0x06, opcode_version=0x01, change_ip_addressv4=options.logon_address, ) print "[*] Setting IP address" response = conn.sr(p)[SAPMS] print "[*] IP address set" response.show() # Set logon information l = SAPMSLogon(type=2, port=3200, address=options.logon_address, host=options.client, misc="LB=3") p = SAPMS(flag=0x01, iflag=0x01, msgtype=0x01, toname="MSG_SERVER", fromname=options.client, opcode=0x2B, logon=l) print "[*] Setting logon information" response = conn.sr(p)[SAPMS] print "[*] Logon information set" response.show() # Set the IP Address property prop = SAPMSProperty(client=options.client, id=0x03, address=options.logon_address) p = SAPMS(flag=0x02, iflag=0x01, toname="-", fromname=options.client, opcode=0x43, property=prop) print "[*] Setting IP address property" response = conn.sr(p)[SAPMS] print "[*] IP Address property set" response.show() # Changing the status to active p = SAPMS(flag=0x01, iflag=0x09, msgtype=0x01, toname="-", fromname=options.client) print "[*] Changing server's status to active" conn.send(p) # Wait for connections try: while True: response = conn.recv()[SAPMS] response.show() except KeyboardInterrupt: print "[*] Cancelled by the user !" # Send MS_LOGOUT packet p = SAPMS(flag=0x00, iflag=0x04, toname="MSG_SERVER", fromname=options.client) print "[*] Sending logout packet" conn.send(p)
def main(): options = parse_options() if options.verbose: logging.basicConfig(level=logging.DEBUG) print("[*] Loading fingerprint database") fingerprint_db = FingerprintDB(options.fingerprints) # Check if we were asked to add a new fingerprint if options.add_fingerprint: if not options.version_info: print("[-] You must provide version info to add new entries to the fingerprint database !") return print("[*] Adding a new entry to the fingerprint database") fingerprint_db.add_fingerprint(options.new_fingerprint_file, options.version_info) return misses = [] matches = [] print("[*] Trying to fingerprint version using %d packets" % (len(fingerprint_targets))) # Trigger some errors and check with fingerprint db l = len(fingerprint_targets) i = 1 for (target, packet) in list(fingerprint_targets.items()): print("[*] (%d/%d) Fingerprint for packet '%s'" % (i, l, target)) # Initiate the connection and send the packet conn = SAPNIStreamSocket.get_nisocket(options.remote_host, options.remote_port, keep_alive=False) if packet is None: # Timeout error error_text = conn.recv().err_text_value else: error_text = conn.sr(packet).err_text_value matched = fingerprint_db.match_fingerprint(target, error_text) if matched: print("[*] (%d/%d) Fingerprint for packet '%s' matched !" % (i, l, target)) matches.append((target, matched)) else: print("[*] (%d/%d) Fingerprint for packet '%s' not matched" % (i, l, target)) misses.append((target, error_text)) i += 1 if matches: versions = [] counts = {} print("\n[*] Matched fingerprints (%d/%d):" % (len(matches), l)) for (target, fingerprints) in matches: print("[+] Request: %s" % target) for fingerprint in fingerprints: match = {} for field in version_info_fields: if field in fingerprint: match[field] = fingerprint[field] if match not in versions: counts[str(match)] = 0 versions.append(match) counts[str(match)] += 1 print("\n[*] Probable versions (%d):" % len(versions)) for version in versions: msg = " ".join(["%s: \"%s\"" % (field, version[field]) for field in version_info_fields if version[field] != ""]) print("[*]\tHits: %d Version: %s" % (counts[str(version)], msg)) if misses: print("\n[*] Non matched fingerprints (%d/%d):" % (len(misses), l)) for (target, _) in misses: print("[-] Request: %s" % target) print("\n[-] Some error values where not found in the fingerprint database. " "If you want to contribute submit a issue to https://github.com/SecureAuthCorp/pysap " "or write an email to [email protected] with the following information along " "with the SAP Router file information and how it was configured.\n") options.new_entries = True # Build new entries for the fingerprint database if options.new_entries: new_fingerprint = {} for (target, error_text) in misses: new_fingerprint[target] = [{}] for field in fingerprint_fields: new_fingerprint[target][0][field] = getattr(error_text, field) # Expand with matched targets also for (target, fingerprint) in matches: new_fingerprint[target] = fingerprint print("\nNew fingerprint saved to: %s" % options.new_fingerprint_file) with open(options.new_fingerprint_file, 'w') as f: json.dump(new_fingerprint, f) version_info = {"patch_number": "", "source_id": "", "update_level": "", "file_version": "", "platform": "", "submitted_by": "", "comment": "", } print("\n\nVersion information to complete and submit:") print("%s" % json.dumps(version_info, indent=4))
def main(): options = parse_options() if options.verbose: logging.basicConfig(level=logging.DEBUG) response = False p = SAPRouter(type=SAPRouter.SAPROUTER_ADMIN) if options.stop: p.adm_command = 5 print("[*] Requesting stop of the remote SAP Router") elif options.soft: p.adm_command = 9 print("[*] Requesting a soft shutdown of the remote SAP Router") response = True elif options.info: p.adm_command = 2 if options.info_password: if len(options.info_password) > 19: print("[*] Password too long, truncated at 19 characters") p.adm_password = options.info_password print("[*] Requesting info using password %s" % p.adm_password) else: print("[*] Requesting info") response = True elif options.new_route: p.adm_command = 3 print("[*] Requesting a refresh of the router table") elif options.trace: p.adm_command = 4 print("[*] Requesting a toggle on the trace settings") elif options.cancel: p.adm_command = 6 p.adm_client_ids = list(map(int, options.cancel.split(","))) print("[*] Requesting a cancel of the route(s) with client id(s) %s" % p.adm_client_ids) response = True elif options.dump: p.adm_command = 7 print("[*] Requesting a dump of the buffers") elif options.flush: p.adm_command = 8 print("[*] Requesting a flush of the buffers") elif options.hide: p.adm_command = 14 print("[*] Requesting a hide on the errors to clients") response = True elif options.set_peer: p.adm_command = 10 p.adm_address_mask = options.set_peer print("[*] Request a set peer trace for the address mask %s" % p.adm_address_mask) response = True elif options.clear_peer: p.adm_command = 11 p.adm_address_mask = options.clear_peer print("[*] Request a clear peer trace for the address mask %s" % p.adm_address_mask) response = True elif options.trace_conn: p.adm_command = 12 p.adm_client_ids = list(map(int, options.trace_conn.split(","))) print("[*] Requesting a connection trace with client id(s) %s" % p.adm_client_ids) response = True else: print("[*] No command specified !") return # Initiate the connection conn = SAPNIStreamSocket.get_nisocket(options.remote_host, options.remote_port) print("[*] Connected to the SAP Router %s:%d" % (options.remote_host, options.remote_port)) # Retrieve the router version used by the server if not specified if options.router_version: p.version = options.router_version else: p.version = get_router_version(conn) or p.version print("[*] Using SAP Router version %d" % p.version) # Send the router admin request print("[*] Sending Router Admin packet") if options.verbose: p.show2() conn.send(p) # Grab the response if required if response: # Some responses has no SAPRouter's packet format and are raw strings, # we need to get the SAP NI layer first and then check if we could go # down to the SAPRouter layer. raw_response = conn.recv()[SAPNI] if SAPRouter in raw_response: router_response = raw_response[SAPRouter] # If the response was null, just return elif raw_response.length == 0: return # If the response is an error, print and exit if router_is_error(router_response): print("[*] Error requesting info:") if options.verbose: router_response.show2() else: print(router_response.err_text_value.error) # Otherwise, print the packets sent by the SAP Router else: print("[*] Response:\n") if options.info: # Decode the first packet as a list of info client raw_response.decode_payload_as(SAPRouterInfoClients) clients = [] clients.append("\t".join(["ID", "Client", "Partner", "Service", "Connected on"])) clients.append("-" * 60) for client in raw_response.clients: # If the trace flag is set, add a mark flag = "(*)" if client.flag_traced else "(+)" if client.flag_routed else "" fields = [str(client.id), client.address, "%s%s" % (flag, client.partner) if client.flag_routed else "(no partner)", client.service if client.flag_routed else "", datetime.fromtimestamp(client.connected_on).ctime()] clients.append("\t".join(fields)) # Decode the second packet as server info raw_response = conn.recv() raw_response.decode_payload_as(SAPRouterInfoServer) print("SAP Network Interface Router running on port %d (PID = %d)\n" "Started on: %s\n" "Parent process: PID = %d, port = %d\n" % (raw_response.port, raw_response.pid, datetime.fromtimestamp(raw_response.started_on).ctime(), raw_response.ppid, raw_response.pport)) print("\n".join(clients)) print("(*) Connections being traced") # Show the plain packets returned try: raw_response = conn.recv() while raw_response: print(raw_response.payload) raw_response = conn.recv() except error: pass
def get_nisocket(cls, host=None, port=None, route=None, password=None, talk_mode=None, router_version=None, **kwargs): """Helper function to obtain a :class:`SAPRoutedStreamSocket`. If no route is specified, it returns a plain `SAPNIStreamSocket`. If no route is specified and the talk mode is raw, it returns a plain `StreamSocket` as it's assumed that the NI layer is not desired. :param host: target host to connect to if not specified in the route :type host: C{string} :param port: target port to connect to if not specified in the route :type port: ``int`` :param route: route to use for determining the SAP Router to connect :type route: C{string} or ``list`` of :class:`SAPRouterRouteHop` :param password: target password if not specified in the route :type password: C{string} :param talk_mode: the talk mode to use for requesting the route :type talk_mode: ``int`` :param router_version: the router version to use for requesting the route :type router_version: ``int`` :keyword kwargs: arguments to pass to :class:`SAPRoutedStreamSocket` constructor :return: connected socket through the specified route :rtype: :class:`SAPRoutedStreamSocket` :raise SAPRouteException: if the route request to the target host/port was not accepted by the SAP Router :raise socket.error: if the connection to the target host/port failed or the SAP Router returned an error """ # If no route was provided, check the talk mode if route is None: # If talk mode is raw, create a new StreamSocket and get rid of the # NI layer completely and force the base class to Raw. if talk_mode == 1: sock = socket.create_connection((host, port)) if "base_cls" in kwargs: kwargs["base_cls"] = Raw return StreamSocket(sock, **kwargs) # Otherwise use the standard SAPNIStreamSocket get_nisocket method else: return SAPNIStreamSocket.get_nisocket(host, port, **kwargs) # If the route was provided using a route string, convert it to a # list of hops if isinstance(route, str): route = SAPRouterRouteHop.from_string(route) # If the host and port were specified, we need to add a new hop to # the route if host is not None and port is not None: route.append( SAPRouterRouteHop(hostname=host, port=port, password=password)) # Connect to the first hop in the route (it should be the SAP Router) sock = socket.create_connection( (route[0].hostname, int(route[0].port))) # Create a SAPRoutedStreamSocket instance specifying the route return cls(sock, route, talk_mode, router_version, **kwargs)
def main(): options = parse_options() if options.verbose: logging.basicConfig(level=logging.DEBUG) response = False p = SAPRouter(type=SAPRouter.SAPROUTER_ADMIN) if options.stop: p.adm_command = 5 print("[*] Requesting stop of the remote SAP Router") elif options.soft: p.adm_command = 9 print("[*] Requesting a soft shutdown of the remote SAP Router") response = True elif options.info: p.adm_command = 2 if options.info_password: if len(options.info_password) > 19: print("[*] Password too long, truncated at 19 characters") p.adm_password = options.info_password print("[*] Requesting info using password %s" % p.adm_password) else: print("[*] Requesting info") response = True elif options.new_route: p.adm_command = 3 print("[*] Requesting a refresh of the router table") elif options.trace: p.adm_command = 4 print("[*] Requesting a toggle on the trace settings") elif options.cancel: p.adm_command = 6 p.adm_client_ids = list(map(int, options.cancel.split(","))) print("[*] Requesting a cancel of the route(s) with client id(s) %s" % p.adm_client_ids) response = True elif options.dump: p.adm_command = 7 print("[*] Requesting a dump of the buffers") elif options.flush: p.adm_command = 8 print("[*] Requesting a flush of the buffers") elif options.hide: p.adm_command = 14 print("[*] Requesting a hide on the errors to clients") response = True elif options.set_peer: p.adm_command = 10 p.adm_address_mask = options.set_peer print("[*] Request a set peer trace for the address mask %s" % p.adm_address_mask) response = True elif options.clear_peer: p.adm_command = 11 p.adm_address_mask = options.clear_peer print("[*] Request a clear peer trace for the address mask %s" % p.adm_address_mask) response = True elif options.trace_conn: p.adm_command = 12 p.adm_client_ids = list(map(int, options.trace_conn.split(","))) print("[*] Requesting a connection trace with client id(s) %s" % p.adm_client_ids) response = True else: print("[*] No command specified !") return # Initiate the connection conn = SAPNIStreamSocket.get_nisocket(options.remote_host, options.remote_port) print("[*] Connected to the SAP Router %s:%d" % (options.remote_host, options.remote_port)) # Retrieve the router version used by the server if not specified if options.router_version: p.version = options.router_version else: p.version = get_router_version(conn) or p.version print("[*] Using SAP Router version %d" % p.version) # Send the router admin request print("[*] Sending Router Admin packet") if options.verbose: p.show2() conn.send(p) # Grab the response if required if response: # Some responses has no SAPRouter's packet format and are raw strings, # we need to get the SAP NI layer first and then check if we could go # down to the SAPRouter layer. raw_response = conn.recv()[SAPNI] if SAPRouter in raw_response: router_response = raw_response[SAPRouter] # If the response was null, just return elif raw_response.length == 0: return # If the response is an error, print and exit if router_is_error(router_response): print("[*] Error requesting info:") if options.verbose: router_response.show2() else: print(router_response.err_text_value.error) # Otherwise, print the packets sent by the SAP Router else: print("[*] Response:\n") if options.info: # Decode the first packet as a list of info client raw_response.decode_payload_as(SAPRouterInfoClients) clients = [] clients.append("\t".join( ["ID", "Client", "Partner", "Service", "Connected on"])) clients.append("-" * 60) for client in raw_response.clients: # If the trace flag is set, add a mark flag = "(*)" if client.flag_traced else "(+)" if client.flag_routed else "" fields = [ str(client.id), client.address, "%s%s" % (flag, client.partner) if client.flag_routed else "(no partner)", client.service if client.flag_routed else "", datetime.fromtimestamp(client.connected_on).ctime() ] clients.append("\t".join(fields)) # Decode the second packet as server info raw_response = conn.recv() raw_response.decode_payload_as(SAPRouterInfoServer) print( "SAP Network Interface Router running on port %d (PID = %d)\n" "Started on: %s\n" "Parent process: PID = %d, port = %d\n" % (raw_response.port, raw_response.pid, datetime.fromtimestamp(raw_response.started_on).ctime(), raw_response.ppid, raw_response.pport)) print("\n".join(clients)) print("(*) Connections being traced") # Show the plain packets returned try: raw_response = conn.recv() while (raw_response): print(raw_response.payload) raw_response = conn.recv() except error: pass
def main(): options = parse_options() if options.verbose: logging.basicConfig(level=logging.DEBUG) # Initiate the connection conn = SAPNIStreamSocket.get_nisocket(options.remote_host, options.remote_port) print "[*] Connected to the message server %s:%d" % (options.remote_host, options.remote_port) client_string = options.client # Build MS_LOGIN_2 packet p = SAPMS(flag=0x00, iflag=0x08, toname=client_string, fromname=client_string) # Send MS_LOGIN_2 packet print "[*] Sending login packet" response = conn.sr(p)[SAPMS] print "[*] Login performed, server string: %s" % response.fromname server_string = response.fromname print "[*] Retrieving current value of parameter: %s" % options.param_name # Send ADM AD_PROFILE request adm = SAPMSAdmRecord(opcode=0x1, parameter=options.param_name) p = SAPMS(toname=server_string, fromname=client_string, version=4, flag=0x04, iflag=0x05, adm_records=[adm]) print "[*] Sending packet" response = conn.sr(p)[SAPMS] if options.verbose: print "[*] Response:" response.show() param_old_value = response.adm_records[0].parameter print "[*] Parameter %s" % param_old_value # If a parameter change was requested, send an ADM AD_SHARED_PARAMETER request if options.param_value: print "[*] Changing parameter value from: %s to: %s" % (param_old_value, options.param_value) # Build the packet adm = SAPMSAdmRecord(opcode=0x2e, parameter="%s=%s" % (options.param_name, options.param_value)) p = SAPMS(toname=server_string, fromname=client_string, version=4, iflag=5, flag=4, adm_records=[adm]) # Send the packet print "[*] Sending packet" response = conn.sr(p)[SAPMS] if options.verbose: print "[*] Response:" response.show() if response.adm_records[0].errorno != 0: print "[*] Error requesting parameter change (error number %d)" % response.adm_records[0].errorno else: print "[*] Parameter changed for the current session !"
def __init__(self, sock, keep_alive=True, base_cls=None): """Initialization defaults to SAPEnqueue as base class""" if base_cls is None: base_cls = SAPEnqueue SAPNIStreamSocket.__init__(self, sock, keep_alive=keep_alive, base_cls=base_cls)
def connect(self): """ Creates a L{SAPNIStreamSocket} connection to the host/port """ self._connection = SAPNIStreamSocket.get_nisocket(self.host, self.port)