Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

executable file 201 lines (157 sloc) 6.66 kb
#!/usr/bin/perl
# This software is Copyright (c) 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' ;
}
Jump to Line
Something went wrong with that request. Please try again.