Diafuzzer
Diameter interfaces fuzzer. Its fuzzing process is based on concrete network traces, and it uses 3gpp and ETSI specifications to fuzz efficiently.
Install / Use
/learn @Orange-OpenSource/DiafuzzerREADME
diafuzzer
Diameter fuzzer, based on specifications of Diameter applications following rfc 3588 / 6733
Overview
Diafuzzer is composed of several different tools:
- simple and accurate Diameter callflows, based on pcap traces
- script language to perform additional functions such as logging, database lookup or others
- detailed description of Diameter applications defined at 3GPP and ETSI
- runtime helpers to perform unit testing and fuzz testing
It is developped in Python, with four major components:
- Diameter.py: contains Python classes named Msg and Avp which mirror wire. Both classes implement default values for unspecified fields, and compute fields which value depends on other fields.
- Dia.py: parses dia files. It contains Python classes to model message structure and avp cardinality, plus their datatype.
- pcap2scn.py and pcap2pdu.py: shall be given a pcap file as first argument. They will both produce a Python form of Diameter PDUs contained in trace, based on Diameter.py module above. pcap2scn.py will produce a client and/or server scenario, made of interleaved send and receive operations, whereas pcap2pdu.py will only dump PDUs as objects.
- unit.py and fuzz.py: shall be given four arguments, amongst which a scenario, and a role. unit.py will replay the given scenario, and fuzz.py will use scenario as a baseline to produce fuzzing operations.
It is not compatible with Python3.
Diameter.py
Avp usage
Make sure to import Avp class from Diameter module
>>> from Diameter import Avp
To create an Avp instance:
>>> a = Avp()
>>> a
Avp(code=0,vendor=0)
The following parameters can be given when building a new instance:
Parameter | Default value when not specified | Meaning --------- | ------------------- | ----- code | 0 | AVP code V | False | AVP Vendor bit M | False | AVP Mandatory bit P | False | AVP Protected bit reserved | None | reserved bits (5 bits) vendor | 0 | vendor id (32 bits) avps | [] | inner AVPs data | None | value length | None, will be computed during encoding | length (32 bits)
For example, to create an Origin-Host Avp instance:
>>> Avp(code=264, data='hss.openims.test')
Avp(code=264, vendor=0, data='hss.openims.test')
Several ways are provided to supply values instead of raw bytes:
>>> Avp(code=266, u32=323)
Avp(code=266, vendor=0, data='\x00\x00\x01C')
The table below gives parameter names that can be used, and their format:
Parameter | Format | Argument type --------- | ------ | ------------- u32 | !L | Integer s32 | !I | Integer u64 | !Q | Integer f32 | !f | Float f64 | !d | Float v4 | NA | Dotted IPv4 address as a string, such as '0.0.0.0' v6 | NA | Colon separated IPv6 as a string, such as '::1'
To create a wire-ready version of the instance:
>>> Avp(code=266, u32=323).encode()
'\x00\x00\x01\n\x00\x00\x00\x0c\x00\x00\x01C'
To create an instance from raw bytes:
>>> Avp.decode('\x00\x00\x01\n\x00\x00\x00\x0c\x00\x00\x01C')
Avp(code=266, vendor=0, data='\x00\x00\x01C')
Msg usage
Make sure to import Msg and Avp classes from Diameter module
>>> from Diameter import Msg, Avp
To create a Msg instance:
>>> m = Msg()
>>> m
Msg(code=0, app_id=0x0, avps=[
])
Parameter | Default value when not specified | Meaning --------- | ------------------- | ------- version | 1 | version (8 bits) length | None, will be computed during encoding | length (32 bits) R | False | Request bit P | False | Proxyable bit E | False | Error bit T | False | reTransmitted bit reserved | None, will be set to zeros | reserved bits (4 bits) code | 0 | code (32 bits) app_id | 0 | application ID (32 bits) e2e_id | None, will be randomly generated during encoding | end-to-end ID (32 bits) h2h_id | None, will be randomly generated during encoding | hop-by-hop ID (32 bits) avps | [] | inner AVPs
For example, to create a Device-Watchdog Request Msg instance:
>>> m = Msg(code=280, R=True)
>>> m
Msg(R=True, code=280, app_id=0x0, avps=[
])
In order to create a real-world Capabilities-Exchange Request Msg instance:
>>> m = Msg(R=True, code=257, app_id=0x0, avps=[
... Avp(code=264, M=True, vendor=0, data='127.0.0.1'),
... Avp(code=296, M=True, vendor=0, data='org.domain.com'),
... Avp(code=257, M=True, vendor=0, data='\x00\x01\x7f\x00\x00\x01'),
... Avp(code=266, M=True, vendor=0, data='\x00\x00\x00\x00'),
... Avp(code=269, M=True, vendor=0, data='Mu Service Analyzer Diameter Implementation'),
... Avp(code=299, M=True, vendor=0, data='\x00\x00\x00\x00'),
... Avp(code=260, M=True, vendor=0, avps=[
... Avp(code=266, M=True, vendor=0, data='\x00\x00(\xaf'),
... Avp(code=258, M=True, vendor=0, data='\x01\x00\x00\x00'),
... ]),
... ])
>>> m
Msg(R=True, code=257, app_id=0x0, avps=[
Avp(code=264, M=True, vendor=0, data='127.0.0.1'),
Avp(code=296, M=True, vendor=0, data='org.domain.com'),
Avp(code=257, M=True, vendor=0, data='\x00\x01\x7f\x00\x00\x01'),
Avp(code=266, M=True, vendor=0, data='\x00\x00\x00\x00'),
Avp(code=269, M=True, vendor=0, data='Mu Service Analyzer Diameter Implementation'),
Avp(code=299, M=True, vendor=0, data='\x00\x00\x00\x00'),
Avp(code=260, M=True, vendor=0, avps=[
Avp(code=266, M=True, vendor=0, data='\x00\x00(\xaf'),
Avp(code=258, M=True, vendor=0, data='\x01\x00\x00\x00'),
]),
])
Encoding and decoding to/from raw bytes work the same as for Avp:
>>> m = Msg.decode('\x01\x00\x00\xbc\x80\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x08@\x00\x00\x11127.0.0.1\x00\x00\x00\x00\x00\x01(@\x00\x00\x16org.domain.com\x00\x00\x00\x00\x01\x01@\x00\x00\x0e\x00\x01\x7f\x00\x00\x01\x00\x00\x00\x00\x01\n@\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x01\r@\x00\x003Mu Service Analyzer Diameter Implementation\x00\x00\x00\x01+@\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x01\x04@\x00\x00 \x00\x00\x01\n@\x00\x00\x0c\x00\x00(\xaf\x00\x00\x01\x02@\x00\x00\x0c\x01\x00\x00\x00')
>>> m
Msg(R=True, code=257, app_id=0x0, avps=[
Avp(code=264, M=True, vendor=0, data='127.0.0.1'),
Avp(code=296, M=True, vendor=0, data='org.domain.com'),
Avp(code=257, M=True, vendor=0, data='\x00\x01\x7f\x00\x00\x01'),
Avp(code=266, M=True, vendor=0, data='\x00\x00\x00\x00'),
Avp(code=269, M=True, vendor=0, data='Mu Service Analyzer Diameter Implementation'),
Avp(code=299, M=True, vendor=0, data='\x00\x00\x00\x00'),
Avp(code=260, M=True, vendor=0, avps=[
Avp(code=266, M=True, vendor=0, data='\x00\x00(\xaf'),
Avp(code=258, M=True, vendor=0, data='\x01\x00\x00\x00'),
]),
])
>>> m.encode()
'\x01\x00\x00\xbc\x80\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x08@\x00\x00\x11127.0.0.1\x00\x00\x00\x00\x00\x01(@\x00\x00\x16org.domain.com\x00\x00\x00\x00\x01\x01@\x00\x00\x0e\x00\x01\x7f\x00\x00\x01\x00\x00\x00\x00\x01\n@\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x01\r@\x00\x003Mu Service Analyzer Diameter Implementation\x00\x00\x00\x01+@\x00\x00\x0c\x00\x00\x00\x00\x00\x00\x01\x04@\x00\x00 \x00\x00\x01\n@\x00\x00\x0c\x00\x00(\xaf\x00\x00\x01\x02@\x00\x00\x0c\x01\x00\x00\x00'
Dia.py
Dia file format
The file format is based on a sequence of sections, each section beginning with
@<keyword> ...
...
Depending on the keyword, arguments may follow, and content of section may not be empty.
The example below defines one of the S13 application message:
@id 16777252
@name S13
@inherits ietf-avps
@inherits 3gpp-avps
@messages
ME-Identity-Check-Request ::= <Diameter Header: 324, REQ, PXY, 16777252>
< Session-Id >
[ Vendor-Specific-Application-Id ]
{ Auth-Session-State }
{ Origin-Host }
{ Origin-Realm }
[ Destination-Host ]
{ Destination-Realm }
{ Terminal-Information }
[ User-Name ]
* [ AVP ]
* [ Proxy-Info ]
* [ Route-Record ]
Application are defined using an id and a name:
@id 16777252
@name S13
Most applications will use AVPs inherited from both IETF and 3GPP:
@inherits ietf-avps
@inherits 3gpp-avps
Messages are defined using their Command Code format:
@messages
ME-Identity-Check-Request ::= <Diameter Header: 324, REQ, PXY, 16777252>
< Session-Id >
[ Vendor-Specific-Application-Id ]
{ Auth-Session-State }
{ Origin-Host }
{ Origin-Realm }
[ Destination-Host ]
{ Destination-Realm }
{ Terminal-Information }
[ User-Name ]
* [ AVP ]
* [ Proxy-Info ]
* [ Route-Record ]
AVPs are defined by their code, datatype and flags:
@avp_types
User-Name 1 UTF8String M
User-Password 2 OctetString M
NAS-IP-Address 4 OctetString M
NAS-Port 5 Unsigned32 M
For AVP of type Grouped, the corresponding Command Code format must be defined in a grouped section:
@grouped
Vendor-Specific-Application-Id ::= <AVP Header: 260>
{ Vendor-Id }
[ Auth-Application-Id ]
[ Acct-Application-Id ]
Failed-AVP ::= <AVP Header: 279>
1* { AVP }
For AVP of type Enumerated, the corresponding values must defined in an enum section, with AVP name in argument:
@enum Timezone-Flag
UTC 0
LOCAL 1
OFFSET 2
@enum QoS-Semantics
QOS_DESIRED
