From 22d493ae7fd6ec601441bc52397d795a5d46ffe3 Mon Sep 17 00:00:00 2001 From: Francois Contat <francois.contat@ssi.gouv.fr> Date: Wed, 7 Jun 2017 16:55:24 +0200 Subject: [PATCH] Tacacs+ support --- scapy/contrib/tacacs.py | 432 +++++++++++++++++++++++++++++++++++++++ scapy/contrib/tacacs.uts | 190 +++++++++++++++++ 2 files changed, 622 insertions(+) create mode 100755 scapy/contrib/tacacs.py create mode 100644 scapy/contrib/tacacs.uts diff --git a/scapy/contrib/tacacs.py b/scapy/contrib/tacacs.py new file mode 100755 index 00000000..dd348042 --- /dev/null +++ b/scapy/contrib/tacacs.py @@ -0,0 +1,432 @@ +#!/usr/bin/env python + +''' +Copyright (C) 2017 Francois Contat <francois.contat@ssi.gouv.fr> +This program is published under a GPLv2 license + +Based on tacacs+ v6 draft https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06 +''' + +import struct +import hashlib + +from scapy.packet import Packet, bind_layers +from scapy.fields import ByteEnumField, ByteField, IntField +from scapy.fields import FieldListField +from scapy.fields import FieldLenField, ConditionalField, StrLenField +from scapy.layers.inet import TCP +from scapy.config import conf + + +SECRET = 'test' + +def obfuscate(pay, secret, session_id, version, seq): + + ''' + + Obfuscation methodology from section 3.7 + https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-3.7 + + ''' + + pad = '' + curr_pad = '' + + # pad length must equal the payload to obfuscate. + # pad = {MD5_1 [,MD5_2 [ ... ,MD5_n]]} + + while len(pad) < len(pay): + + msg = hashlib.md5() + msg.update(struct.pack('!I', session_id)) + msg.update(secret) + msg.update(struct.pack('!BB', version, seq)) + msg.update(curr_pad) + curr_pad = msg.digest() + pad += curr_pad + + # Obf/Unobfuscation via XOR operation between plaintext and pad + + new_payload = (struct.pack('B', ord(pad[i]) ^ ord(pay[i])) for i in xrange(len(pay))) + return "".join(new_payload) + +TACACSPRIVLEVEL = {15:'Root', + 1:'User', + 0:'Minimum'} + +########################## +# Authentication Packets # +########################## + +TACACSVERSION = {1:'Tacacs', + 192:'Tacacs+'} + +TACACSTYPE = {1:'Authentication', + 2:'Authorization', + 3:'Accounting'} + +TACACSFLAGS = {1:'Unencrypted', + 4:'Single Connection'} + +TACACSAUTHENACTION = {1:'Login', + 2:'Change Pass', + 4:'Send Authentication'} + +TACACSAUTHENTYPE = {1:'ASCII', + 2:'PAP', + 3:'CHAP', + 4:'ARAP', #Deprecated + 5:'MSCHAP', + 6:'MSCHAPv2'} + +TACACSAUTHENSERVICE = {0:'None', + 1:'Login', + 2:'Enable', + 3:'PPP', + 4:'ARAP', + 5:'PT', + 6:'RCMD', + 7:'X25', + 8:'NASI', + 9:'FwProxy'} + +TACACSREPLYPASS = {1:'PASS', + 2:'FAIL', + 3:'GETDATA', + 4:'GETUSER', + 5:'GETPASS', + 6:'RESTART', + 7:'ERROR', + 21:'FOLLOW'} + +TACACSREPLYFLAGS = {1:'NOECHO'} + +TACACSCONTINUEFLAGS = {1:'ABORT'} + + +class TacacsAuthenticationStart(Packet): + + ''' + + Tacacs authentication start body from section 4.1 + https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-4.1 + + ''' + + name = 'Tacacs Authentication Start Body' + fields_desc = [ByteEnumField('action', 1, TACACSAUTHENACTION), + ByteEnumField('priv_lvl', 1, TACACSPRIVLEVEL), + ByteEnumField('authen_type', 1, TACACSAUTHENTYPE), + ByteEnumField('authen_service', 1, TACACSAUTHENSERVICE), + FieldLenField('user_len', None, fmt='!B', length_of='user'), + FieldLenField('port_len', None, fmt='!B', length_of='port'), + FieldLenField('rem_addr_len', None, fmt='!B', length_of='rem_addr'), + FieldLenField('data_len', None, fmt='!B', length_of='data'), + ConditionalField(StrLenField('user', '', length_from=lambda x: x.user_len), + lambda x: x != ''), + StrLenField('port', '', length_from=lambda x: x.port_len), + StrLenField('rem_addr', '', length_from=lambda x: x.rem_addr_len), + StrLenField('data', '', length_from=lambda x: x.data_len)] + +class TacacsAuthenticationReply(Packet): + + ''' + + Tacacs authentication reply body from section 4.2 + https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-4.2 + + ''' + + name = 'Tacacs Authentication Reply Body' + fields_desc = [ByteEnumField('status', 1, TACACSREPLYPASS), + ByteEnumField('flags', 0, TACACSREPLYFLAGS), + FieldLenField('server_msg_len', None, fmt='!H', length_of='server_msg'), + FieldLenField('data_len', None, fmt='!H', length_of='data'), + StrLenField('server_msg', '', length_from=lambda x: x.server_msg_len), + StrLenField('data', '', length_from=lambda x: x.data_len)] + +class TacacsAuthenticationContinue(Packet): + + ''' + + Tacacs authentication continue body from section 4.3 + https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-4.3 + + ''' + + name = 'Tacacs Authentication Continue Body' + fields_desc = [FieldLenField('user_msg_len', None, fmt='!H', length_of='user_msg'), + FieldLenField('data_len', None, fmt='!H', length_of='data'), + ByteEnumField('flags', 1, TACACSCONTINUEFLAGS), + StrLenField('user_msg', '', length_from=lambda x: x.user_msg_len), + StrLenField('data', '', length_from=lambda x: x.data_len)] + +######################### +# Authorization Packets # +######################### + +TACACSAUTHORTYPE = {0:'Not Set', + 1:'None', + 2:'Kerberos 5', + 3:'Line', + 4:'Enable', + 5:'Local', + 6:'Tacacs+', + 8:'Guest', + 16:'Radius', + 17:'Kerberos 4', + 32:'RCMD'} + +TACACSAUTHORSTATUS = {1:'Pass Add', + 2:'Pass repl', + 16:'Fail', + 17:'Error', + 33:'Follow'} + +class TacacsAuthorizationRequest(Packet): + + ''' + + Tacacs authorization request body from section 5.1 + https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-5.1 + + ''' + + name = 'Tacacs Authorization Request Body' + fields_desc = [ByteEnumField('authen_method', 0, TACACSAUTHORTYPE), + ByteEnumField('priv_lvl', 1, TACACSPRIVLEVEL), + ByteEnumField('authen_type', 1, TACACSAUTHENTYPE), + ByteEnumField('authen_service', 1, TACACSAUTHENSERVICE), + FieldLenField('user_len', None, fmt='!B', length_of='user'), + FieldLenField('port_len', None, fmt='!B', length_of='port'), + FieldLenField('rem_addr_len', None, fmt='!B', length_of='rem_addr'), + FieldLenField('arg_cnt', None, fmt='!B', count_of='arg_len_list'), + FieldListField('arg_len_list', [], ByteField('', 0), + length_from=lambda pkt: pkt.arg_cnt), + StrLenField('user', '', length_from=lambda x: x.user_len), + StrLenField('port', '', length_from=lambda x: x.port_len), + StrLenField('rem_addr', '', length_from=lambda x: x.rem_addr_len)] + + def guess_payload_class(self, pay): + if self.arg_cnt > 0: + return TacacsPacketArguments + return conf.padding_layer + +class TacacsAuthorizationReply(Packet): + + ''' + + Tacacs authorization reply body from section 5.2 + https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-5.2 + + ''' + + name = 'Tacacs Authorization Reply Body' + fields_desc = [ByteEnumField('status', 0, TACACSAUTHORSTATUS), + FieldLenField('arg_cnt', None, fmt='!B', count_of='arg_len_list'), + FieldLenField('server_msg_len', None, fmt='!H', length_of='server_msg'), + FieldLenField('data_len', None, fmt='!H', length_of='data'), + FieldListField('arg_len_list', [], ByteField('', 0), + length_from=lambda pkt: pkt.arg_cnt), + StrLenField('server_msg', '', length_from=lambda x: x.server_msg_len), + StrLenField('data', '', length_from=lambda x: x.data_len)] + + def guess_payload_class(self, pay): + if self.arg_cnt > 0: + return TacacsPacketArguments + return conf.padding_layer + + +###################### +# Accounting Packets # +###################### + +TACACSACNTFLAGS = {2:'Start', + 4:'Stop', + 8:'Watchdog'} + +TACACSACNTSTATUS = {1:'Success', + 2:'Error', + 33:'Follow'} + +class TacacsAccountingRequest(Packet): + + ''' + + Tacacs accounting request body from section 6.1 + https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-6.1 + + ''' + + name = 'Tacacs Accounting Request Body' + fields_desc = [ByteEnumField('flags', 0, TACACSACNTFLAGS), + ByteEnumField('authen_method', 0, TACACSAUTHORTYPE), + ByteEnumField('priv_lvl', 1, TACACSPRIVLEVEL), + ByteEnumField('authen_type', 1, TACACSAUTHENTYPE), + ByteEnumField('authen_service', 1, TACACSAUTHENSERVICE), + FieldLenField('user_len', None, fmt='!B', length_of='user'), + FieldLenField('port_len', None, fmt='!B', length_of='port'), + FieldLenField('rem_addr_len', None, fmt='!B', length_of='rem_addr'), + FieldLenField('arg_cnt', None, fmt='!B', count_of='arg_len_list'), + FieldListField('arg_len_list', [], ByteField('', 0), + length_from=lambda pkt: pkt.arg_cnt), + StrLenField('user', '', length_from=lambda x: x.user_len), + StrLenField('port', '', length_from=lambda x: x.port_len), + StrLenField('rem_addr', '', length_from=lambda x: x.rem_addr_len)] + + def guess_payload_class(self, pay): + if self.arg_cnt > 0: + return TacacsPacketArguments + return conf.padding_layer + +class TacacsAccountingReply(Packet): + + ''' + + Tacacs accounting reply body from section 6.2 + https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-6.2 + + ''' + + name = 'Tacacs Accounting Reply Body' + fields_desc = [FieldLenField('server_msg_len', None, fmt='!H', length_of='server_msg'), + FieldLenField('data_len', None, fmt='!H', length_of='data'), + ByteEnumField('status', None, TACACSACNTSTATUS), + StrLenField('server_msg', '', length_from=lambda x: x.server_msg_len), + StrLenField('data', '', length_from=lambda x: x.data_len)] + +class TacacsPacketArguments(Packet): + + ''' + + Class defined to handle the arguments listed at the end of tacacs+ + Authorization and Accounting packets. + + ''' + + __slots__ = ['_len'] + name = 'Arguments in Tacacs+ packet' + fields_desc = [StrLenField('data', '', length_from=lambda pkt: pkt._len)] + + def pre_dissect(self, s): + cur = self.underlayer + i = 0 + + # Searching the position in layer in order to get its length + + while isinstance(cur, TacacsPacketArguments): + cur = cur.underlayer + i += 1 + self._len = cur.arg_len_list[i] + return s + + def guess_payload_class(self, pay): + cur = self.underlayer + i = 0 + + # Guessing if Argument packet. Nothing in encapsulated via tacacs+ + + while isinstance(cur, TacacsPacketArguments): + cur = cur.underlayer + i += 1 + if i+1 < cur.arg_cnt: + return TacacsPacketArguments + return conf.padding_layer + + + +class TacacsClientPacket(Packet): + + ''' + + Super class for tacacs packet in order to get them uncrypted + Obfuscation methodology from section 3.7 + https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-3.7 + + ''' + def post_dissect(self, pay): + + if self.flags == 0: + pay = obfuscate(pay, SECRET, self.session_id, self.version, self.seq) + return pay + +class TacacsHeader(TacacsClientPacket): + + ''' + + Tacacs Header packet from section 3.8 + https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-3.8 + + ''' + + name = 'Tacacs Header' + fields_desc = [ByteEnumField('version', 192, TACACSVERSION), + ByteEnumField('type', 1, TACACSTYPE), + ByteField('seq', 1), + ByteEnumField('flags', 0, TACACSFLAGS), + IntField('session_id', 0), + IntField('length', None)] + + def guess_payload_class(self, payload): + + # Guessing packet type from type and seq values + + # Authentication packet - type 1 + + if self.type == 1: + if self.seq % 2 == 0: + return TacacsAuthenticationReply + if sum(struct.unpack('bbbb', payload[4:8])) == len(payload[8:]): + return TacacsAuthenticationStart + elif sum(struct.unpack('!hh', payload[:4])) == len(payload[5:]): + return TacacsAuthenticationContinue + + # Authorization packet - type 2 + + if self.type == 2: + if self.seq % 2 == 0: + return TacacsAuthorizationReply + return TacacsAuthorizationRequest + + # Accounting packet - type 3 + + if self.type == 3: + if self.seq % 2 == 0: + return TacacsAccountingReply + return TacacsAccountingRequest + + return conf.raw_layer + + def post_build(self, p, pay): + + # Setting length of packet to obfuscate if not filled by user + + if self.length is None and pay: + p = p[:-4] + struct.pack('!I', len(pay)) + + + if self.flags == 0: + + pay = obfuscate(pay, SECRET, self.session_id, self.version, self.seq) + return p + pay + + return p + + def hashret(self): + return struct.pack('I', self.session_id) + + def answers(self, other): + return (isinstance(other, TacacsHeader) and + self.seq == other.seq + 1 and + self.type == other.type and + self.session_id == other.session_id) + + +bind_layers(TCP, TacacsHeader, dport=49) +bind_layers(TCP, TacacsHeader, sport=49) +bind_layers(TacacsHeader, TacacsAuthenticationStart, type=1, dport=49) +bind_layers(TacacsHeader, TacacsAuthenticationReply, type=1, sport=49) + +if __name__ == '__main__': + from scapy.main import interact + interact(mydict=globals(), mybanner='tacacs+') diff --git a/scapy/contrib/tacacs.uts b/scapy/contrib/tacacs.uts new file mode 100644 index 00000000..3f82ad6a --- /dev/null +++ b/scapy/contrib/tacacs.uts @@ -0,0 +1,190 @@ ++ Tacacs+ header + += default instanciation + +pkt = IP()/TCP(dport=49)/TacacsHeader() +str(pkt) == b'E\x00\x004\x00\x01\x00\x00@\x06|\xc1\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xd0p\x00\x00\xc0\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00' + += default values build + +pkt = IP()/TCP(dport=49)/TacacsHeader() +pkt.session_id == 0 and TacacsHeader in pkt + += filled values build + +pkt = IP()/TCP(dport=49)/TacacsHeader(seq=5, session_id=165) +str(pkt) == b'E\x00\x004\x00\x01\x00\x00@\x06|\xc1\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xcb\xcb\x00\x00\xc0\x01\x05\x00\x00\x00\x00\xa5\x00\x00\x00\x00' + += dissection + +pkt = Ether('\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x004\x00\x01\x00\x00@\x06|\xc1\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x1c4\x00\x00\xc0\x01\x01\x00\x00Y\xb3\xe3\x00\x00\x00\x00') +pkt.session_id == 5878755 + ++ Tacacs+ Authentication Start + += default instanciation + +pkt = IP()/TCP(dport=49)/TacacsHeader()/TacacsAuthenticationStart() +str(pkt) == b'E\x00\x00<\x00\x01\x00\x00@\x06|\xb9\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xce\xfb\x00\x00\xc0\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x08R\x0f\x9e\xe7\xe0\xd1/\x9c' + += default values build + +pkt = IP()/TCP(dport=49)/TacacsHeader()/TacacsAuthenticationStart() +TacacsAuthenticationStart in pkt and pkt.action == 1 and pkt.priv_lvl == 1 and pkt.authen_type == 1 and pkt.authen_service == 1 + += filled values build -- SSH connection sample use scapy, secret = test + +pkt = IP()/TCP(dport=49)/TacacsHeader(seq=1, flags=0, session_id=2424164486, length=28)/TacacsAuthenticationStart(user_len=5, port_len=4, rem_addr_len=11, data_len=0, user='scapy', port='tty2', rem_addr='172.10.10.1') +str(pkt) == b"E\x00\x00P\x00\x01\x00\x00@\x06|\xa5\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xd4t\x00\x00\xc0\x01\x01\x00\x90}\xd0\x86\x00\x00\x00\x1c\x05\x00POza\xed\xee}\xa5R\xd3Vu+\xbb'\xae\x98\xaa\x1a\x9d\x17=\x90\xd2\x07q" + += dissection + +pkt = Ether('\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00P\x00\x01\x00\x00@\x06|\xa5\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xd5t\x00\x00\xc0\x01\x01\x00\x90}\xd0\x86\x00\x00\x00\x1c\x05\x00POza\xed\xee}\xa5R\xd3Vu+\xbb&\xae\x98\xaa\x1a\x9d\x17=\x90\xd2\x07q') +pkt.user == 'scapy' and pkt.port == 'tty3' + ++ Tacacs+ Authentication Reply + += default instanciation + +pkt = IP()/TCP(dport=49)/TacacsHeader()/TacacsAuthenticationReply() +str(pkt) == b'E\x00\x00:\x00\x01\x00\x00@\x06|\xbb\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xfd\x9d\x00\x00\xc0\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x06R\x0e\x9f\xe6\xe0\xd1' + += default values build + +pkt = IP()/TCP(dport=49)/TacacsHeader(seq=2)/TacacsAuthenticationReply() +TacacsAuthenticationReply in pkt and pkt.status == 1 + += filled values build -- SSH connection sample use scapy, secret = test + +pkt = IP()/TCP(dport=49)/TacacsHeader(seq=2, flags=0, session_id=2424164486, length=16)/TacacsAuthenticationReply(status=5, flags=1, server_msg_len=10, data_len=0, server_msg='Password: ') +str(pkt) == b'E\x00\x00D\x00\x01\x00\x00@\x06|\xb1\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x0f\x87\x00\x00\xc0\x01\x02\x00\x90}\xd0\x86\x00\x00\x00\x10*\x0c\x1d\xa0\xa2[xE\x0b\t/s\xee\x8b\xd3o' + += dissection + +pkt = Ether('\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00D\x00\x01\x00\x00@\x06|\xb1\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x0f\x87\x00\x00\xc0\x01\x02\x00\x90}\xd0\x86\x00\x00\x00\x10*\x0c\x1d\xa0\xa2[xE\x0b\t/s\xee\x8b\xd3o') +pkt.server_msg == 'Password: ' + ++ Tacacs+ Authentication Continue + += default instanciation + +pkt = IP()/TCP(dport=49)/TacacsHeader()/TacacsAuthenticationContinue() +str(pkt) == b'E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xfcp\x00\x00\xc0\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x05S\x0e\x9f\xe6\xe1' + += default values build + +pkt = TacacsAuthenticationContinue() +TacacsAuthenticationContinue in pkt and pkt.data == '' and pkt.user_msg == '' and pkt.data_len is None and pkt.user_msg_len is None + += filled values build -- SSH connection sample secret = test, password = pass + +pkt = IP()/TCP(dport=49)/TacacsHeader(seq=3, flags=0, session_id=2424164486, length=9)/TacacsAuthenticationContinue(flags=0, user_msg_len=4, data_len=0, user_msg='pass') +str(pkt) == b'E\x00\x00=\x00\x01\x00\x00@\x06|\xb8\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00u\xfa\x00\x00\xc0\x01\x03\x00\x90}\xd0\x86\x00\x00\x00\t\xec8\xc1\x8d\xcc\xec(\xacT' + += dissection + +pkt = Ether('\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00A\x00\x01\x00\x00@\x06|\xb4\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xb5\xfd\x00\x00\xc0\x01\x03\x00\x90}\xd0\x86\x00\x00\x00\r\xec4\xc1\x8d\xcc\xec(\xacT\xd2k&T') +pkt.user_msg == 'password' + ++ Tacacs+ Authorization Request + += default instanciation + +pkt = IP()/TCP()/TacacsHeader(type=2)/TacacsAuthorizationRequest() +str(pkt) == b'E\x00\x00<\x00\x01\x00\x00@\x06|\xb9\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xcd\xdb\x00\x00\xc0\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x08S\x0f\x9e\xe7\xe0\xd1/\x9c' + += default values build + +pkt = IP()/TCP(dport=49)/TacacsHeader(type=2)/TacacsAuthorizationRequest() +TacacsAuthorizationRequest in pkt and pkt.priv_lvl == 1 and pkt.authen_type == 1 and pkt.authen_service == 1 + += filled values build -- SSH connection sample secret = test + +pkt = IP()/TCP(dport=49)/TacacsHeader(seq=1, type=2 , flags=0, session_id=135252, length=47)/TacacsAuthorizationRequest(authen_method=6, user_len=5, port_len=4, rem_addr_len=11, arg_cnt=2, arg_len_list=[13, 4], user='scapy', port='tty2', rem_addr='172.10.10.1')/TacacsPacketArguments(data='service=shell')/TacacsPacketArguments(data='cmd*') +str(pkt) == b'E\x00\x00c\x00\x01\x00\x00@\x06|\x92\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xb28\x00\x00\xc0\x02\x01\x00\x00\x02\x10T\x00\x00\x00/\xdd\xe0\xe8\xea\xba\xca$Y\xf7\x00\xc2Hh\xed\x03\x1eK84\x10\xb9h\xd7@\x0e\xd5\x13\xf0\xfaA\xfa\xbe;\x01\x82\xecl\xf9\xc6\xa0Z6\x98j\xfd\\9' + += dissection + +pkt = Ether('\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00c\x00\x01\x00\x00@\x06|\x92\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xc2D\x00\x00\xc0\x02\x01\x00R\xf2\x0e\xf4\x00\x00\x00/\xe6\x01\x03 \xd7\xa9\x91\x7fv\xf2\x15-\x88a\xac$\x14\x9f\xc0\xbb\xb8a\xe0\x86e\xf9\xd9\xa2\xc4\xe7\x0bRI\xc8\xdd\x97\xd5\x80\xcf\xce\x81*"\xbc\x15E\x95') +pkt.port == 'tty9' + ++ Tacacs+ Authorization Reply + += default instanciation + +pkt = IP()/TCP()/TacacsHeader(type=2)/TacacsAuthorizationReply() +str(pkt) == b'E\x00\x00:\x00\x01\x00\x00@\x06|\xbb\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xfc}\x00\x00\xc0\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x06S\x0e\x9f\xe6\xe0\xd1' + += default values build + +pkt = TacacsHeader()/TacacsAuthorizationReply() +TacacsAuthorizationReply in pkt and pkt.status == 0 and pkt.arg_cnt is None and pkt.data_len is None + += filled values build -- SSH connection sample secret = test + +pkt = Ether()/IP()/TCP(dport=49)/TacacsHeader(seq=2, type=2 , flags=0, session_id=1391595252, length=20)/TacacsAuthorizationReply(status=1, arg_cnt=2, server_msg_len=0, data_len=0, arg_len_list=[11, 1])/TacacsPacketArguments(data='priv-lvl=15')/TacacsPacketArguments(data='a') +str(pkt) == b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00H\x00\x01\x00\x00@\x06|\xad\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00|G\x00\x00\xc0\x02\x02\x00R\xf2\x0e\xf4\x00\x00\x00\x14\xce^Xp~Z\x9b^\xd8Y\xc9"\xf5\xb0&\xe5Ji\xa8\x15' + += dissection + +pkt = Ether('\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00H\x00\x01\x00\x00@\x06|\xad\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00|G\x00\x00\xc0\x02\x02\x00R\xf2\x0e\xf4\x00\x00\x00\x14\xce^Xp~Z\x9b^\xd8Y\xc9"\xf5\xb0&\xe5Ji\xa8\x15') +pkt.status == 1 + ++ Tacacs+ Accounting Request + += default instanciation + +pkt = IP()/TCP()/TacacsHeader(type=3)/TacacsAccountingRequest() +str(pkt) == b'E\x00\x00=\x00\x01\x00\x00@\x06|\xb8\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xb3\xd9\x00\x00\xc0\x03\x01\x00\x00\x00\x00\x00\x00\x00\x00\tS\x0e\x9e\xe7\xe1\xd1/\x9c\x19' + += default value build + +pkt = IP()/TCP()/TacacsHeader(type=3)/TacacsAccountingRequest() +TacacsAccountingRequest in pkt and pkt.authen_method == 0 and pkt.priv_lvl == 1 and pkt.authen_type == 1 + += filled values build -- SSH connection sample secret = test + +pkt = IP()/TCP(dport=49)/TacacsHeader(seq=1, type=3 , flags=0, session_id=3059434316, length=67)/TacacsAccountingRequest(flags=2, authen_method=6, priv_lvl=15, authen_type=1, authen_service=1, user_len=5, port_len=4, rem_addr_len=11, arg_cnt=3, arg_len_list=[10, 12, 13], user='scapy', port='tty2', rem_addr='172.10.10.1')/TacacsPacketArguments(data='task_id=24')/TacacsPacketArguments(data='timezone=CET')/TacacsPacketArguments(data='service=shell') +str(pkt) == b'E\x00\x00w\x00\x01\x00\x00@\x06|~\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00sk\x00\x00\xc0\x03\x01\x00\xb6[CL\x00\x00\x00C\x1d\xfb\x81\xd52\xfb\x06\x8b\t\xb9\x0c87\xd3 i\x05\xb5|\x9f\x01l\xbf/\xd3\rc\x0f\nDr\xc0\xc9.\x88@*(S\xfeA\xd4\x19wFj=\xc3\x9f\x00!D\xbe$E\x04\x0e\xe75\x99\xd6\r\x0f\x16\xc7\x1d\xc2' + += dissection + +pkt = Ether('\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00w\x00\x01\x00\x00@\x06|~\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00|j\x00\x00\xc0\x03\x01\x00\xb6[CL\x00\x00\x00C\x1d\xfb\x81\xd52\xfb\x06\x8b\t\xb9\x0c87\xd3 i\x05\xb5|\x9f\x01l\xb1/\xd3\rc\x0f\nDr\xc0\xc9.\x88@*(S\xfeF\xd5\x19wFj=\xc3\x9f\x00!D\xbe$E\x04\x0e\xe75\x99\xd6\r\x0f\x16\xc7\x1d\xc2') +pkt.rem_addr == '192.10.10.1' + ++ Tacacs+ Accounting Reply + += default instanciation + +pkt = IP()/TCP(dport=49)/TacacsHeader(seq=2, type=3)/TacacsAccountingReply() +str(pkt) == b'E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00,\x07\x00\x00\xc0\x03\x02\x00\x00\x00\x00\x00\x00\x00\x00\x05B\xd2A\x8b\x1f' + += default values build + +pkt = IP()/TCP(dport=49)/TacacsHeader(seq=2, type=3)/TacacsAccountingReply() +TacacsAccountingReply in pkt and pkt.server_msg == '' and pkt.server_msg_len is None and pkt.status is None + += filled values build -- SSH connection sample secret = test + +pkt = Ether()/IP()/TCP(dport=49)/TacacsHeader(seq=2, type=3 , flags=0, session_id=3059434316, length=5)/TacacsAccountingReply(status=1, server_msg_len=0, data_len=0) +str(pkt) == b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xc5\x7f\x00\x00\xc0\x03\x02\x00\xb6[CL\x00\x00\x00\x05\xf4\x0f\xad,o' + += dissection +pkt = Ether('\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xc5\x7f\x00\x00\xc0\x03\x02\x00\xb6[CL\x00\x00\x00\x05\xf4\x0f\xad,o') +pkt.status == 1 + ++ Changing secret to foobar + += instanciation + +scapy.contrib.tacacs.SECRET = 'foobar' +pkt = Ether()/IP()/TCP(dport=49)/TacacsHeader(session_id=441255181, type=3, seq=2, length=5)/TacacsAccountingReply(status=1, server_msg_len=0, data_len=0) +str(pkt) == b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00/,\x00\x00\xc0\x03\x02\x00\x1aM\x05\r\x00\x00\x00\x05S)\x9b\xb4\x92' + += dissection + +scapy.contrib.tacacs.SECRET = 'foobar' + +pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00/,\x00\x00\xc0\x03\x02\x00\x1aM\x05\r\x00\x00\x00\x05S)\x9b\xb4\x92') +pkt.status == 1 + -- GitLab