Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Python Protocol Simulator By Sergej Šrepfler ([email protected]) Ver 0.3 A quick guide 1. How to build diameter message flow using libDiameter ...................................... 2 1.1 Copying existing diameter packets .............................................................. 2 1.2 Building request with AVPs ......................................................................... 3 1.3 Mixing AVPs and unknown values .............................................................. 4 2. How to build radius message flow using libRadius ............................................. 6 2.1 Copying existing radius packets .................................................................. 6 2.2 Building request with AVPs ......................................................................... 7 2.3 Mixing AVPs and unknown values .............................................................. 8 3. EAP-Payload/EAP-Message ............................................................................ 10 3.1 Real time EAP-SIM calculations (A3/A8 algorithm) ................................... 10 3.2 Real time EAP-AKA calculations (milenage algorithm) .............................. 10 3.3 Real time EAP-AKA' calculations (milenage algorithm) ............................. 11 4. Bug reports ...................................................................................................... 12 5. Appendix 1 - EAP calculations ......................................................................... 13 6. Appendix 2 – Compiling calc ............................................................................ 14 7. Appendix 3 – External sources & Licences....................................................... 15 1. How to build diameter message flow using libDiameter You can use several aproaches: 1. copy/paste whole diameter packet 2. build every request one AVP at the time 3. do the mixture of previous two. It requires a little fiddling and a bit of understanding of Diameter protocol. But the result is fast and flexible. 1.1 Copying existing diameter packets Let's start with copying existing diameter packets. Ladies and gentlemans – start your Wiresharks and open your snoops. Open the diameter message you want to copy/emulate, and do the right-click on Diameter message, and Copy the Bytes as Hex-Stream. In my example, I will copy Capabilities-ExchangeRequest, so the bytes would be 0100008c8000010100000000000000010860000100000108400000167374612e7370 72696e742e636f6d00000000012840000012737072696e742e636f6d000000000101 4000000e00010a1e60c800000000010a4000000c000028af0000010d000000154c61 6e64736c69646548534757000000000001024000000c01000022000001164000000c 4f32a086 So – let's build our CER message to include into python CER='0100008c800001010000000000000001086000010000010840000016737461 2e737072696e742e636f6d00000000012840000012737072696e742e636f6d000000 0001014000000e00010a1e60c800000000010a4000000c000028af0000010d000000 154c616e64736c69646548534757000000000001024000000c010000220000011640 00000c4f32a086' Note that I just copy-pasted the value from wireshark, put them under quotes and gave them appropriate name. Here is simplified and minimalistic example. See example files for more info. Greyed is the python script. #!/usr/bin/env python from libDiameter import * if __name__ == '__main__': HOST='server' PORT=3868 Please modify HOST and PORT to proper values (you'll definitely want to change HOST to your server hostname/IP). Dictionary is not used in this example. Conn=Connect(HOST,PORT) # Let's build CER Please insert newly created CER variable after this. Note that python uses indentation, so please do it right. Examples in this document are NOT aligned properly. Please see examples directory or read some python manual. This variable should be in single line CER='0100008c8000010100000000000000010860000100000108400000167374 612e737072696e742e636f6d00000000012840000012737072696e742e636f6d0 000000001014000000e00010a1e60c800000000010a4000000c000028af000001 0d000000154c616e64736c69646548534757000000000001024000000c0100002 2000001164000000c4f32a086' # send data Conn.send(CER.decode('hex')) # Receive response received = Conn.recv(1024) You now can add in exactly the same way any diameter message. When done, please close connection. Conn.close() And we are done. This is NOT the full message flow, but it showed you how to quickly build the diameter messages using diamClient and existing snoop from Wireshark. 1.2 Building request with AVPs This is rather simple – manually add all AVPs to message. Just be sure that ALL AVPs are in dictionary. Commands and Vendors are defined in dictionary, so make sure that you DID load the diameter dictionary with command LoadDictionary("dictDiameter.xml") Let's build CER (Note: Use your values for ORIGIN_HOST and ORIGIN_REALM): # Let's build CER CER_avps=[ ] CER_avps.append(encodeAVP('Origin-Host', ORIGIN_HOST)) CER_avps.append(encodeAVP('Origin-Realm', ORIGIN_REALM)) CER_avps.append(encodeAVP('Vendor-Id', 11)) CER_avps.append(encodeAVP('Origin-State-Id', 1094807040)) CER_avps.append(encodeAVP('Supported-Vendor-Id', 11)) CER_avps.append(encodeAVP('Acct-Application-Id', 16777265)) # 3GPP SWx=16777265 # Create message header (empty) CER=HDRItem() # Set command code CER.cmd=dictCOMMANDname2code('Capabilities-Exchange') # Set Hop-by-Hop and End-to-End initializeHops(CER) # Add AVPs to header and calculate remaining fields msg=createReq(CER,CER_avps) # msg now contains CER Request as hex string And to send it, we will use exactly the same code as before # send data Conn.send(msg.decode('hex')) # Receive response received = Conn.recv(1024) So just replace single line we copied in previous example with these lines, and... you are done! That way you can quickly start, and replace copied message with one you can change/adapt. 1.3 Mixing AVPs and unknown values Sometimes you'll need something in between. E.g – I want to change one AVP, but do not want to touch others in copied message. Let's show how it can be done using message containing AVP we DO NOT WANT TO CHANGE, and User-Name WE DO WANT TO CHANGE. Note: You can copy the RAW values directly from wireshark snoop. Open AVP you want to copy, right-click on it, and Copy Bytes as Hex-Stream. Remember – always click on TOPMOST level of AVP you want to copy, not on expanded ones. Once we can see values in wireshark we want to copy/change, we can make any mix we want to. Capital letter variables are for clarity. Replace them with expected values # Let's build DER DER_avps=[] DER_avps.append(encodeAVP("Origin-Host", ORIGIN_HOST)) DER_avps.append(encodeAVP("Origin-Realm", ORIGIN_REALM)) DER_avps.append(encodeAVP("Session-Id", SESSION_ID)) DER_avps.append(encodeAVP("Destination-Host", DEST_HOST)) DER_avps.append(encodeAVP("Destination-Realm", DEST_REALM)) DER_avps.append(encodeAVP("Origin-State-Id", 1329853127)) DER_avps.append(encodeAVP("User-Name", "[email protected]")) # And now some values I do NOT want to change or understand DER_avps.append("000001ce4000004002000038013031323131313232323 230303036323340776c616e2e6d6e633032332e6d63633236322e336770706 e6574776f726b2e6f7267") DER_avps.append("00000408c0000010000028af00000000") # Just to ilustrate that you can mix it any way you want DER_avps.append("000005e0c0000010000028af48525044") DER_avps.append(encodeAVP("Auth-Session-State", 0)) DER_avps.append(encodeAVP("Origin-State-Id", 1321976431)) DER_avps.append(encodeAVP("Calling-Station-Id", '310006232157383') DER_avps.append(encodeAVP("Vendor-Id", 10415) And now proceed with adding header fields and making it the proper Diameter message # Create empty message header DER_H=HDRItem() # Set command code DER_H.cmd=getCommandCode('Diameter-EAP') # Set Hop-by-Hop and End-to-End initializeHops(DER_H) # Add AVPs to header and calculate remaining fields msg=createReq(DER_H,DER_avps) # msg now contains Diameter-EAP-Request as hex string Note: If you have to send Enumerated AVP, use numeric value, not enumerated string If you have to send Grouped AVP, see example # Example for ENUMERATED AVP # 0-WLAN 1-UTRAN 2-GERAN 2001-HRPD 2003-EHRPD ... DER_avps.append(encodeAVP("RAT-Type", 0)) # Example for GROUPED AVP # Grouped AVPs are encoded like this DER_avps.append(encodeAVP("Vendor-Specific-ApplicationId",[ encodeAVP("Vendor-Id",dictVENDORid2code('TGPP')), encodeAVP("Auth-Application-Id",APPLICATION_ID)])) That is it. Happy coding. 2. How to build radius message flow using libRadius You can use several aproaches: 1. copy/paste whole radius packet 2. build every request one AVP at the time 3. do the mixture of previous two. It requires a little fiddling and a bit of understanding of Radius protocol. But the result is fast and flexible. 2.1 Copying existing radius packets Let's start with copying existing radius packets. Ladies and gentlemans – start your Wiresharks and open your snoops. Open the radius message you want to copy/emulate, and do the right-click on Radius message, and Copy the Bytes as Hex-Stream. In my example, I will copy the radius message, so the bytes would be 0b03005616af199ad8906acbc1ab69c2eff86a4c4f0e0102000c170500000d01000050 12989f092fca647c2b5973ed1bfc3a180d1822d51ee1f848bcc9a44973282f625303e4 e9023a9562b8ff9c02f6ddc28d5e00eb So – let's build our message to include into python MSG="0b03005616af199ad8906acbc1ab69c2eff86a4c4f0e0102000c170500000d01 00005012989f092fca647c2b5973ed1bfc3a180d1822d51ee1f848bcc9a44973282f62 5303e4e9023a9562b8ff9c02f6ddc28d5e00eb" Note that I just copy-pasted the value from wireshark, put them under quotes and gave them appropriate name. Here is simplified and minimalistic example. See example files for more info. Greyed is the python script. #!/usr/bin/env python from libRadius import * if __name__ == '__main__': HOST='server' PORT=3868 Please modify HOST and PORT to proper values (you'll definitely want to change HOST to your server hostname/IP). Dictionary is not used in this example. Conn=socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Let's build message Please insert newly created variable after this. Note that python uses indentation, so please do it right. Examples in this document are NOT aligned properly. Please see examples directory or read some python manual. This variable should be in single line msg="0b03005616af199ad8906acbc1ab69c2eff86a4c4f0e0102000c17050000 0d0100005012989f092fca647c2b5973ed1bfc3a180d1822d51ee1f848bcc9a44 973282f625303e4e9023a9562b8ff9c02f6ddc28d5e00eb" # send data Conn.sendto(msg.decode("hex"),(HOST,PORT)) # Receive response received = Conn.recv(1024) You now can add in exactly the same way any radius message. When done, please close connection. Conn.close() And we are done. This is NOT the full message flow, but it showed you how to quickly build the radius messages using radClient and existing snoop from Wireshark. 2.2 Building request with AVPs This is rather simple – manually add all AVPs to message. Just be sure that ALL AVPs are in dictionary. Commands and Vendors are defined in dictionary, so make sure that you DID load the radius dictionary with command LoadDictionary("dictRadius.xml") Let's build radius message (Note: Use your values for ORIGIN_HOST and ORIGIN_REALM): # Let's build message RES_avps=[] RES_avps.append(encodeAVP("State", STATE)) RES_avps.append(encodeAVP("Calling-Station-Id", CALLING_ID)) RES_avps.append(encodeAVP("Called-Station-Id", CALLED_ID)) RES_avps.append(encodeAVP("NAS-Identifier", "default")) RES_avps.append(encodeAVP("User-Name", "testuser")) RES_avps.append(encodeAVP("Acct-Session-Id", "a1")) RES_avps.append(encodeAVP("NAS-IP-Address", NAS_IP)) RES_avps.append(encodeAVP("NAS-Port-Id", NAS_PORT)) # Create message header (empty) RES=HDRItem() # Set command code RES.Code=dictCOMMANDname2code("Access-Request") RES.Identifier=RID # Add AVPs to header and calculate remaining fields msg=createReq(RES,RES_avps) # msg now contains Response as hex string And to send it, we will use exactly the same code as before # send data Conn. sendto(msg.decode("hex"),(HOST,PORT)) # Receive response received = Conn.recv(1024) So just replace single line we copied in previous example with these lines, and... you are done! That way you can quickly start, and replace copied message with one you can change/adapt. 2.3 Mixing AVPs and unknown values Sometimes you'll need something in between. E.g – I want to change one AVP, but do not want to touch others in copied message. Let's show how it can be done using message containing AVP we DO NOT WANT TO CHANGE, and User-Name WE DO WANT TO CHANGE. Note: You can copy the RAW values directly from wireshark snoop. Open AVP you want to copy, right-click on it, and Copy Bytes as Hex-Stream. Remember – always click on TOPMOST level of AVP you want to copy, not on expanded ones. Once we can see values in wireshark we want to copy/change, we can make any mix we want to. # Let's build RAD RAD_avps=[] RAD_avps.append(encodeAVP("User-Name", "[email protected]")) # And now some values I do NOT want to change or understand RAD_avps.append("000001ce4000004002000038013031323131313232323 230303036323340776c616e2e6d6e633032332e6d63633236322e336770706 e6574776f726b2e6f7267") RAD_avps.append("00000408c0000010000028af00000000") # Just to ilustrate that you can mix it any way you want RAD_avps.append("000005e0c0000010000028af48525044") RAD_avps.append(encodeAVP("Auth-Session-State", 0)) RAD_avps.append(encodeAVP("Calling-Station-Id", '310006232157383') And now proceed with adding header fields and making it the proper Radius message # Create message header (empty) RES=HDRItem() # Set command code RES.Code=dictCOMMANDname2code("Access-Request") RES.Identifier=RID # Add AVPs to header and calculate remaining fields msg=createReq(RES,RES_avps) # msg now contains Response as hex string Note: If you have to send Enumerated AVP, use numeric value, not enumerated string # Example for ENUMERATED AVP # 1-Login 2-Framed 17-Authorize only... DER_avps.append(encodeAVP("Service-Type", 1)) That is it. Happy coding. 3. EAP-Payload/EAP-Message To be able to create/modify/decode EAP message, import EAP module. Included example is for Diameter. The same logic applies to Radius (change import library and port). #!/usr/bin/env python from libDiameter import * import eap if __name__ == '__main__': HOST='server' PORT=3868 LoadDictionary("dictDiameter.xml") eap.LoadEAPDictionary("dictEAP.xml") And from now on you can access EAP messages in similar manner. See included examples for more details. NOTE: eapcalc executable (compiled for your platform) must be in the same directory where eap.py is. For your convenience, precompiled version is already included in src_eapcalc/bin, but you need to copy proper version to correct location. E.g: on windows platform place eapcalc.exe in the same directory with eap.py If it does not work, you'll need to compile it yourself. 3.1 Real time EAP-SIM calculations (A3/A8 algorithm) There is no "standard" algorithm to calculate EAP-SIM values. 3GPP published COMP128 as private algorithm (available only to members), but there is no guarantee that your provider will use exact algorithm (it changed over the years due to security breach). Still – it is included for completeness. 3.2 Real time EAP-AKA calculations (milenage algorithm) If you know OP and K for your subscriber (Operator-Specific Constant and Subscriber Secret Key), it is possible to dynamically calculate all keys. See example/eap_AKA_calc.py for example how to do it. Here is only interesting part: # Part just to show key calculation # Calculation of EAP Keys OPc="aaaabbbbccccddddaaaabbbbccccdddd" K="77777777777777777777777777777777" Identity="[email protected]" SQN = "000000000001" AMF = "3333" # RAND is copied from Challenge RAND="ce9e2d867cc86dde4cc87899136184d5" XRES,CK,IK,AK,AKS=eap.aka_calc_milenage(OPc,K,RAND) KENCR,KAUT,MSK,EMSK,MK=eap.aka_calc_keys(Identity,CK,IK) # AUTN is actually SQN xor AK + AMF + XMAC params="0x"+OPc+" 0x"+K+" 0x"+RAND+" 0x"+SQN+" 0x"+AMF XMAC,MACS=eap.exec_calc("milenage-f1",params) # 1) From OP,K,RAND calculate XRES,Ck,Ik (milenage-f2345) # 2) Using OP,K,RAND,SQN,AMF calculate XMAC, MAC_S (milenage-f1) to verify AUTN # 3) From Identity,Ck,Ik calculate keys (aka) Remember – this will only calculate EAP-AKA keys. You still need to know what to do with them and place proper values it into message. 3.3 Real time EAP-AKA' calculations (milenage algorithm) If you know OP and K for your subscriber (Operator-Specific Constant and Subscriber Secret Key), it is possible to dynamically calculate all keys. See example/eap_AKAP_calc.py for example how to do it. The only difference between AKA and AKA' is function for key calculation (will it use SHA-1 or SHA-256). Here is only interesting part # Part just to show key calculation # Calculation of EAP Keys OPc="aaaabbbbccccddddaaaabbbbccccdddd" K="77777777777777777777777777777777" Identity="[email protected]" SQN = "000000000001" AMF = "3333" # RAND is copied from Challenge RAND="ce9e2d867cc86dde4cc87899136184d5" XRES,CK,IK,AK,AKS=eap.aka_calc_milenage(OPc,K,RAND) params="0x"+OPc+" 0x"+K+" 0x"+RAND+" 0x"+SQN+" 0x"+AMF XMAC,MACS=eap.exec_calc("milenage-f1",params) KENCR,KAUT,MSK,EMSK,KRE=eap.akap_calc_keys(Identity,CK,IK) Remember – this will only calculate EAP-AKA' keys. You still need to know what to do with them and place proper values it into Diameter message. 4. Bug reports Please send any changes, enhancements, fixes, bug reports and successful examples to my email. If you like this tool, a small donation might encourage me to extend it a bit further. 5. Appendix 1 - EAP calculations For all calculations, external C program is used. If you run it without any parameters, it will show you usage examples. Prefix 0x indicate that value should be HEX encoded with 0x at the beginning. sim <Identity> <0xn*Kc> <0xNONCE_MT> <0xVER_LIST> <selected_ver> aka <Identity> <0xCk> <0xIk> akaprime <Identity> <0xCk> <0xIk> mac-sim <0xK_aut> <0xMSG> [0xDATA] mac-aka <0xK_aut> <0xMSG> [0xDATA] mac-akaprime <0xK_aut> <0xMSG> [0xDATA] computeOPc <0xOP> <0xK> milenage-f1 <0xOPc> <0xK> <0xRAND> <0xSQN> <0xAMF> milenage-f2345 <0xOPc> <0xK> <0xRAND> encrypt <0xIV> <0xK_encr> <0xMSG> decrypt <0xIV> <0xK_encr> <0xMSG> Output of aka: MK, PRF, KENCR, KAUT, MSK, EMSK Output of akaprime: KENCR, KAUT, KRE, MSK, EMSK Output of mac-*: MAC Output of milenage-f2345: XRES, CK, IK, AK, AK* Output of milenage-f1: XMAC, MACS Output of encrypt: encrypted value to be placed in AT_ENCR_DATA Output of decrypt: decrypted AVPs 6. Appendix 2 – Compiling calc The source for all calculations is included. To compile it, use enclosed calc_compile.sh. It should be possible to compile it on other platforms as well (with probably some changes due to endianness). All supporting functions for calc.c were taken from hostapd source (http://w1.fi/hostapd/ ), so if you are wandering about it – take a look at the original sources Compilation was tested: On Linux with gcc On Intel-based Solaris 10 with gcc On Windows XP with MinGW (www.mingw.org) To verify calculations for newly built application, included is calc_test.sh script. It should report PASS or FAIL for all supported calculations. 7. Appendix 3 – External sources & Licences PyPS itself is available under the terms of BSD licence. See README for more details. EAP Calculations are performed using code from hostapd Copyright (c) 2002-2012, Jouni Malinen <[email protected]> and contributors. http://hostap.epitest.fi/hostapd/ This software may be distributed, used, and modified under the terms of BSD license. See README for more details. Dictionary contains data from Wireshark dictionary http://www.wireshark.org Wireshark® is available under the GNU General Public License version 2. COMP128 A3/A8 Algorithm implementation Copyright 1998, Marc Briceno, Ian Goldberg, and David Wagner. http://www.scard.org/gsm