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