From 2e97a7e57323f99e1870c1f5c9779e6fe5f54586 Mon Sep 17 00:00:00 2001
From: magnum <magnum>
Date: Sun, 11 Dec 2011 14:25:03 +0100
Subject: [PATCH] Didier Arenzana's radius2john.pl and dynamic formats

---
 run/dictionary.rfc2865 |  137 +++++++++++++++++++++++++++++++++
 run/dynamic.conf       |   30 +++++--
 run/radius2john.pl     |  200 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 359 insertions(+), 8 deletions(-)
 create mode 100644 run/dictionary.rfc2865
 create mode 100755 run/radius2john.pl

diff --git a/run/dictionary.rfc2865 b/run/dictionary.rfc2865
new file mode 100644
index 0000000..a3fbc6b
--- /dev/null
+++ b/run/dictionary.rfc2865
@@ -0,0 +1,137 @@
+# -*- text -*-
+#
+#	Attributes and values defined in RFC 2865.
+#	http://www.ietf.org/rfc/rfc2865.txt
+#
+#	$Id$
+#
+ATTRIBUTE	User-Name				1	string
+ATTRIBUTE	User-Password				2	string 
+ATTRIBUTE	CHAP-Password				3	octets
+ATTRIBUTE	NAS-IP-Address				4	ipaddr
+ATTRIBUTE	NAS-Port				5	integer
+ATTRIBUTE	Service-Type				6	integer
+ATTRIBUTE	Framed-Protocol				7	integer
+ATTRIBUTE	Framed-IP-Address			8	ipaddr
+ATTRIBUTE	Framed-IP-Netmask			9	ipaddr
+ATTRIBUTE	Framed-Routing				10	integer
+ATTRIBUTE	Filter-Id				11	string
+ATTRIBUTE	Framed-MTU				12	integer
+ATTRIBUTE	Framed-Compression			13	integer
+ATTRIBUTE	Login-IP-Host				14	ipaddr
+ATTRIBUTE	Login-Service				15	integer
+ATTRIBUTE	Login-TCP-Port				16	integer
+# Attribute 17 is undefined
+ATTRIBUTE	Reply-Message				18	string
+ATTRIBUTE	Callback-Number				19	string
+ATTRIBUTE	Callback-Id				20	string
+# Attribute 21 is undefined
+ATTRIBUTE	Framed-Route				22	string
+ATTRIBUTE	Framed-IPX-Network			23	ipaddr
+ATTRIBUTE	State					24	octets
+ATTRIBUTE	Class					25	octets
+ATTRIBUTE	Vendor-Specific				26	octets
+ATTRIBUTE	Session-Timeout				27	integer
+ATTRIBUTE	Idle-Timeout				28	integer
+ATTRIBUTE	Termination-Action			29	integer
+ATTRIBUTE	Called-Station-Id			30	string
+ATTRIBUTE	Calling-Station-Id			31	string
+ATTRIBUTE	NAS-Identifier				32	string
+ATTRIBUTE	Proxy-State				33	octets
+ATTRIBUTE	Login-LAT-Service			34	string
+ATTRIBUTE	Login-LAT-Node				35	string
+ATTRIBUTE	Login-LAT-Group				36	octets
+ATTRIBUTE	Framed-AppleTalk-Link			37	integer
+ATTRIBUTE	Framed-AppleTalk-Network		38	integer
+ATTRIBUTE	Framed-AppleTalk-Zone			39	string
+
+ATTRIBUTE	CHAP-Challenge				60	octets
+ATTRIBUTE	NAS-Port-Type				61	integer
+ATTRIBUTE	Port-Limit				62	integer
+ATTRIBUTE	Login-LAT-Port				63	string
+
+#
+#	Integer Translations
+#
+
+#	Service types
+
+VALUE	Service-Type			Login-User		1
+VALUE	Service-Type			Framed-User		2
+VALUE	Service-Type			Callback-Login-User	3
+VALUE	Service-Type			Callback-Framed-User	4
+VALUE	Service-Type			Outbound-User		5
+VALUE	Service-Type			Administrative-User	6
+VALUE	Service-Type			NAS-Prompt-User		7
+VALUE	Service-Type			Authenticate-Only	8
+VALUE	Service-Type			Callback-NAS-Prompt	9
+VALUE	Service-Type			Call-Check		10
+VALUE	Service-Type			Callback-Administrative	11
+
+#	Framed Protocols
+
+VALUE	Framed-Protocol			PPP			1
+VALUE	Framed-Protocol			SLIP			2
+VALUE	Framed-Protocol			ARAP			3
+VALUE	Framed-Protocol			Gandalf-SLML		4
+VALUE	Framed-Protocol			Xylogics-IPX-SLIP	5
+VALUE	Framed-Protocol			X.75-Synchronous	6
+
+#	Framed Routing Values
+
+VALUE	Framed-Routing			None			0
+VALUE	Framed-Routing			Broadcast		1
+VALUE	Framed-Routing			Listen			2
+VALUE	Framed-Routing			Broadcast-Listen	3
+
+#	Framed Compression Types
+
+VALUE	Framed-Compression		None			0
+VALUE	Framed-Compression		Van-Jacobson-TCP-IP	1
+VALUE	Framed-Compression		IPX-Header-Compression	2
+VALUE	Framed-Compression		Stac-LZS		3
+
+#	Login Services
+
+VALUE	Login-Service			Telnet			0
+VALUE	Login-Service			Rlogin			1
+VALUE	Login-Service			TCP-Clear		2
+VALUE	Login-Service			PortMaster		3
+VALUE	Login-Service			LAT			4
+VALUE	Login-Service			X25-PAD			5
+VALUE	Login-Service			X25-T3POS		6
+VALUE	Login-Service			TCP-Clear-Quiet		8
+
+#	Login-TCP-Port		(see /etc/services for more examples)
+
+VALUE	Login-TCP-Port			Telnet			23
+VALUE	Login-TCP-Port			Rlogin			513
+VALUE	Login-TCP-Port			Rsh			514
+
+#	Termination Options
+
+VALUE	Termination-Action		Default			0
+VALUE	Termination-Action		RADIUS-Request		1
+
+#	NAS Port Types
+
+VALUE	NAS-Port-Type			Async			0
+VALUE	NAS-Port-Type			Sync			1
+VALUE	NAS-Port-Type			ISDN			2
+VALUE	NAS-Port-Type			ISDN-V120		3
+VALUE	NAS-Port-Type			ISDN-V110		4
+VALUE	NAS-Port-Type			Virtual			5
+VALUE	NAS-Port-Type			PIAFS			6
+VALUE	NAS-Port-Type			HDLC-Clear-Channel	7
+VALUE	NAS-Port-Type			X.25			8
+VALUE	NAS-Port-Type			X.75			9
+VALUE	NAS-Port-Type			G.3-Fax			10
+VALUE	NAS-Port-Type			SDSL			11
+VALUE	NAS-Port-Type			ADSL-CAP		12
+VALUE	NAS-Port-Type			ADSL-DMT		13
+VALUE	NAS-Port-Type			IDSL			14
+VALUE	NAS-Port-Type			Ethernet		15
+VALUE	NAS-Port-Type			xDSL			16
+VALUE	NAS-Port-Type			Cable			17
+VALUE	NAS-Port-Type			Wireless-Other		18
+VALUE	NAS-Port-Type			Wireless-802.11		19
diff --git a/run/dynamic.conf b/run/dynamic.conf
index eca468d..da9e7d7 100644
--- a/run/dynamic.conf
+++ b/run/dynamic.conf
@@ -196,19 +196,33 @@ Test=$dynamic_1007$de56b00bb15d6db79204bd44383469bc$T &;thatsworking
 Test=$dynamic_1007$fb685c6f469f6e549c85e4c1fb5a65a6$\\H:;test3
 
 ####################################################################
-# Simple DYNAMIC type for joomla md5($p.$s)   Included here to 'exercise' the script parser
+# Dynamic type for algorithm used in RADIUS User-Password attrinute md5($p.$s)
 ####################################################################
 [List.Generic:dynamic_1008]
-# expression shown will be the string:   dynamic_1008 md5($p.$s) [joomla]
-Expression=md5($p.$s) [joomla]
-# Flag needed here, is Salt.  There is no 'fixed' saltlen.
+# expression shown will be this string:
+Expression=md5($p.$s) [RADIUS User-Password]
+# Flag needed here, is Salt
 Flag=MGF_SALTED
-# here is the optimized 'script' to perform the md5 8 times on itself.
+# The salt has a fixed length of 16 bytes
+Saltlen=16
 Func=DynamicFunc__clean_input
 Func=DynamicFunc__append_keys
 Func=DynamicFunc__append_salt
 Func=DynamicFunc__crypt
-Test=$dynamic_1008$ed52af63d8ecf0c682442dfef5f36391$1aDNNojYGSc7pSzcdxKxhbqvLtEe4deG:test1
-Test=$dynamic_1008$4fa1e9d54d89bfbe48b4c0f0ca0a3756$laxcaXPjgcdKdKEbkX1SIjHKm0gfYt1c:thatsworking
-Test=$dynamic_1008$82568eeaa1fcf299662ccd59d8a12f54$BdWwFsbGtXPGc0H1TBxCrn0GasyAlJBJ:test3
+Test=$dynamic_1008$b962b0d40fc9111ce5f8efab424bad73$NormalSaltNormal:secret
+Test=$dynamic_1008$8bfccd9d67ec0bcdc38e9ae3c19a2903$FinishingwitHEX$:secret
+Test=$dynamic_1008$bf239357f3aa95508a53fe41b7e5f2e3$inthem$HEXiddle6:secret
+Test=$dynamic_1008$7fe3c4d1bf2ac68e94ee9f2bf75b9601$HEX$00000000000000000000000000000000:secret
+Test=$dynamic_1008$658bbf9f04538d6bede09a4a52a77504$HEX$626c6168003637383930313233343536:secret
 
+######################################################################
+# Dynamic Type for algorithm used in RADIUS Responses md5($s.$p)
+######################################################################
+[List.Generic:dynamic_1009]
+Expression=md5($s.$p) [RADIUS Responses]
+Flag=MGF_SALTED
+Func=DynamicFunc__clean_input
+Func=DynamicFunc__append_salt
+Func=DynamicFunc__append_keys
+Func=DynamicFunc__crypt
+Test=$dynamic_1009$0b9b9fdf75fc79d85c5b69aa1de26288$Salt:test1
diff --git a/run/radius2john.pl b/run/radius2john.pl
new file mode 100755
index 0000000..f5b5678
--- /dev/null
+++ b/run/radius2john.pl
@@ -0,0 +1,200 @@
+#!/opt/local/bin/perl
+
+# This software is Copyright © 2011 Didier ARENZANA <darenzana-at-gmail.com>, 
+# and it is hereby released to the general public under the following terms:
+# Redistribution and use in source and binary forms, with or without modification, are permitted, as long as the original
+# author is referenced. 
+
+# Utility to bruteforce RADIUS shared-secret
+# Usage: perl rad2john.pl <pcap files>
+#
+# application of two  methods described in http://www.untruth.org/~josh/security/radius/radius-auth.html :
+# "3.3 User-Password Attribute Based Shared Secret Attack" and
+# "3.1 "Response Authenticator Based Shared Secret Attack"
+
+# For attack 3.3 : 
+# we try authentications using a known password, and sniff the radius packets to a pcpap file.
+# This script reads access-request in the pcap file, and dumps the md5(RA+secret) and RA, in a john-friendly format.
+# The password must be always the same, be less then 16 bytes long, and entered in the $PASSWORD variable below.
+# The user names used during this attack must be entered in @LOGINS below.
+
+# For attack 3.1:
+# we don't need to try authentications. Just sniff the radius packets in a pcap file.
+# This script reads the pcap file, matches radius responses with the corresponding requests, 
+# and dumps md5 and salt as needed.
+
+# This script assumes there is one radius secret per client IP, that does not change during the whole time of packet dump.
+# so it will only dump one couple of matching (salt, md5) for each distinct client IP adress.
+# attack 3.3 takes precedence to attack 3.1, because the salt will be shorter.
+# set variable $UNIQUE to 0 to disable this behavior.
+
+use warnings ;
+use strict ;
+
+use Net::Pcap ;
+use NetPacket::IP qw(:protos);
+use NetPacket::UDP;
+use NetPacket::Ethernet;
+
+use Net::Radius::Dictionary ;
+use Net::Radius::Packet ;
+
+use Data::Dumper ;
+$Data::Dumper::Useqq = 1 ;
+
+# The password used during the attack
+my $PASSWORD = '1' ;
+# The user logins used during the attack
+my @LOGINS = ( 'crack', 'toto') ;
+# Set to 0 to disable unicity of client IPs in the output file
+my $UNIQUE = 1 ;
+
+my %VALID_LOGIN ;
+$VALID_LOGIN{$_} = 1 foreach (@LOGINS) ;
+
+# storage for Access-Requests. keys: srcip-ID, values: Request Auth.
+my %requests ;
+my %dumped_ips ;
+
+my $dict  = new Net::Radius::Dictionary "dictionary.rfc2865" ;
+
+foreach my $filename ( @ARGV ) {
+    %requests = () ; #comment this line if request and matching responses can be in different files
+    read_file($filename) ;
+}
+
+sub read_file {
+    my ($filename) = @_ ;
+    my ($err, $object,$filter) ;
+    
+    $object = Net::Pcap::open_offline($filename, \$err) ;
+    if (defined $object ) { 
+        print STDERR "Processing $filename\n" ;        
+    } else {
+        print STDERR "unable to read file $filename - $err\n" ;
+        return ;
+    }
+    
+    Net::Pcap::compile( $object, 
+        \$filter,'udp port 1812',
+        0, 0 
+        ) && die 'Unable to compile packet capture filter';
+    
+    Net::Pcap::setfilter($object, $filter) &&
+        die 'Unable to set packet capture filter';
+    
+    Net::Pcap::loop($object, -1, \&process_packet, Net::Pcap::datalink($object)) ; # || die "Unable to read packet : " . Net::Pcap::geterr($object) ;
+    
+    Net::Pcap::close($object) ;
+}
+
+sub process_packet {
+    my ($linktype, $header, $packet) = @_ ;
+    my ($iner_data, $protocol) ;
+    
+    #print join (" ", $header->{len}, $header->{tv_sec}, $header->{tv_usec}) . "\n" ;
+    #print Dumper($header, $packet) ;
+
+    if ($linktype==0) {
+        # loopback - check if it's IP protocol
+        $protocol = unpack("V", substr($packet, 0, 4)) ;
+        if ($protocol != 2) {
+            print STDERR "loopback protocol $protocol not supported.\n" ;
+            return ;
+        }
+        $iner_data=substr($packet, 4) ;
+    } elsif ($linktype==1) {
+        # ethernet
+        my $ether = NetPacket::Ethernet->decode($packet) ;
+        $protocol = $ether->{'type'} ;
+        if ( $protocol != 0x800 ) {
+            print STDERR "ethernet protocol $protocol not supported.\n" ;
+            return ;
+        }
+
+        $iner_data = NetPacket::Ethernet::strip($packet);
+    } else {
+        print STDERR "Link type $linktype not supported.\n" ;
+        return ;
+    }
+    
+    # we should have an IP packet in $iner_data
+    my $ip  = NetPacket::IP->decode($iner_data);
+    if ($ip->{proto} != 17) {
+        # the filter should have sent only UDP packets... what is going on?
+        print STDERR "IP protocol field is not 17! file format error?\n" ;
+        print STDERR Dumper(\$ip) ;
+        die ;
+    }
+    
+    # We now have an UDP packet in $ip->{data}
+    my $udp = NetPacket::UDP->decode($ip->{'data'});
+    
+    my $radius= new Net::Radius::Packet($dict, $udp->{'data'});
+    $radius->show_unknown_entries(0) ;
+
+    process_radius($ip, $radius) ;
+}
+
+
+sub process_radius {
+    my ($ip, $rad) = @_ ;
+    
+    local $_= $rad-> code ;
+        
+    if ( /Access-Request/ ) {
+        dump_access_request(
+            $ip->{'src_ip'},
+            $rad->attr('User-Name'), 
+            $rad->authenticator(), 
+            $rad->attr('User-Password')
+        ) if defined($VALID_LOGIN{$rad->attr('User-Name')}) ;
+        
+        $requests{$ip->{'src_ip'}. '-' . $rad->identifier()} = $rad->authenticator() ;
+    }
+    elsif (/Access-Accept/ || /Access-Challenge/ || /Access-Reject/) {
+        my $key=$ip->{'dest_ip'}. '-' . $rad->identifier() ;
+        return unless defined($requests{$key}) ;
+        dump_response($ip->{'dest_ip'}, $requests{$key}, $rad) ;        
+    }
+}
+
+sub dump_response {
+    # Extract md5 hash from the response packet,
+    # and build salt from the response packet and the corresponding request authenticator
+    my ($ip, $req_ra, $rad) = @_ ;
+        
+    return if ($UNIQUE && defined ($dumped_ips{$ip})) ;
+    
+    # extract the hash
+    my $hash = $rad->authenticator() ;
+    
+    #extract the packet raw data to get the salt
+    my $salt= $rad->pack() ;
+    #replace Response Authenticator with the Request Authenticator
+    substr($salt, 4, 16)=$req_ra ;
+    
+    print $ip . ':$dynamic_1009$' . 
+        unpack('H*', $hash) . 
+        '$HEX$' . unpack('H*', $salt) .
+        "\n" ;
+    
+    $dumped_ips{$ip} = 'reply' ;
+}
+
+sub dump_access_request { 
+    # Extract the md5 hash and salt from the packet
+    # and dump them in 'joomla' form.
+    my ($ip, $login, $ra, $hashed) = @_ ;
+    
+    return if ($UNIQUE && defined ($dumped_ips{$ip}) && ($dumped_ips{$ip} eq 'request')) ;
+    
+    print $ip . ':$dynamic_1008$' . 
+        # the RADIUS User-Password attribute contains MD5(RA+secret) XOR password
+        # we need to xor it to get back MD5(RA+secret)
+        unpack("H*", $hashed ^ $PASSWORD) .
+        '$HEX$' . unpack("H*", $ra) . 
+        "\n" ;
+    
+    $dumped_ips{$ip} = 'request' ;
+}
-- 
1.7.5.4

