Created
October 4, 2016 10:28
-
-
Save bAndie91/dca26e544a1b75fcb06c8594c007e1c1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl | |
# | |
# Rouge DHCP traffic detection. | |
# | |
$0 = "RougeDetect"; | |
# Wait for dhcp offer: | |
$WAIT = 10; | |
# Only legal dhcp server and gateway: | |
$Router = "192.168.1.254"; | |
$iface = "eth0"; | |
use IO::Select; | |
use IO::Socket::INET; | |
use Net::DHCP::Packet; | |
use Net::DHCP::Constants; | |
use Data::Dumper; | |
use Net::RawIP; | |
use Net::Pcap qw/pcap_open_live lookupnet compile setfilter pcap_loop/; | |
use NetPacket::Ethernet qw/eth_strip/; | |
use NetPacket::IP; | |
use NetPacket::UDP; | |
use POSIX qw/:sys_wait_h/; | |
if(-t 2) { | |
open DEBUG, ">&STDERR"; | |
} | |
else { | |
open DEBUG, ">", "/dev/null"; | |
} | |
# generate fake mac address | |
@HWaddr = ( int(rand(2**24)) & 0xFCFFFF, int(rand(2**24)) ); | |
$HWaddr = sprintf "%06X%06X", @HWaddr; | |
#$HWaddr = "000802d9b325"; | |
#$HWaddr = "0026181beffe"; | |
$MAC = mac2coloned($HWaddr); | |
$xid = int(rand(0xFFFFFFFF)); | |
$discover = Net::DHCP::Packet->new( | |
Xid => $xid, | |
#Flags => 0x0000, # Unicast | |
Flags => 0x8000, # Broadcast | |
DHO_DHCP_MESSAGE_TYPE() => DHCPDISCOVER(), | |
Chaddr => $HWaddr, | |
Ciaddr => '0.0.0.0', | |
Siaddr => '0.0.0.0', | |
Giaddr => '0.0.0.0', | |
Hops => 0, | |
DHO_DHCP_PARAMETER_REQUEST_LIST() => join(' ', 1..254), | |
DHO_VENDOR_CLASS_IDENTIFIER() => "MSFT", | |
); | |
#print $discover->toString(); | |
$dhcp = $discover->serialize(); | |
$kid[0] = fork; | |
if($kid[0]==0) { | |
$0 = "RougeDetect-Listen"; | |
exit fake_listen_sniff(); | |
} | |
$kid[1] = fork; | |
if($kid[1]==0) { | |
$0 = "RougeDetect-Discover"; | |
exit discover(); | |
} | |
print DEBUG "Waiting $WAIT sec\n", ; | |
$start = time; | |
WAIT: | |
$kid = waitpid -1, WNOHANG; | |
$rouge = ($? >> 8); | |
if($kid == $kid[0]) { | |
kill 'TERM', $kid[1]; | |
exit $rouge; | |
} | |
if($start + $WAIT > time) { | |
goto WAIT; | |
} | |
else { | |
kill 'TERM', @kid; | |
exit 0; | |
} | |
sub discover { | |
$packet = new Net::RawIP({ | |
ip => { | |
saddr => '0.0.0.0', | |
daddr => '255.255.255.255', | |
frag_off => 0, | |
}, | |
udp => { | |
source => 68, | |
dest => 67, | |
data => $dhcp, | |
}, | |
}); | |
#$packet->send; | |
$packet->ethnew($iface); | |
$packet->ethset(dest => "FF:FF:FF:FF:FF:FF"); | |
$packet->ethset(source => $MAC); | |
for(1..5) { | |
print DEBUG "DHCP Discover $MAC\n", ; | |
$packet->ethsend; | |
sleep 1; | |
} | |
} | |
sub fake_listen_socket { | |
# listens for DHCP reply on dgram socket | |
my $start = time; | |
$socket = IO::Socket::INET->new( | |
Proto => 'udp', | |
LocalPort => '68', | |
LocalAddr => '0.0.0.0', | |
) or die "socket: $@\n"; | |
while(1) { | |
@ready = IO::Select->new($socket)->can_read($WAIT); | |
if(not @ready or $start + $WAIT < time) { | |
return 0; | |
} | |
$socket->recv($newmsg, 1024); | |
my $rouge = display_dhcp_reply($newmsg); | |
return 1 if $rouge; | |
} | |
} | |
sub fake_listen_sniff { | |
# listens for DHCP reply on raw interface | |
$filter_str = "udp and dst port 68"; | |
$pcap = pcap_open_live($iface, 1500, 1, $WAIT*1000, \$errstr) | |
or die "Can't open device $iface: $errstr\n"; | |
lookupnet($iface, \$net, \$mask, \$errstr) == 0 | |
or die "Can't get device properties $iface: $errstr\n"; | |
compile($pcap, \$bpf_prog, $filter_str, 0, $mask) == 0 | |
or die "Can't compile filter '$filter_str'\n"; | |
setfilter($pcap, $bpf_prog) == 0 | |
or die "Can't apply filter '$filter_str'\n"; | |
my $rouge; | |
pcap_loop($pcap, -1, sub { | |
my ($rouge, $header, $packet) = @_; | |
$eth = NetPacket::Ethernet->decode($packet); | |
$ip = NetPacket::IP->decode(eth_strip($packet)); | |
$udp = NetPacket::UDP->decode($ip->{'data'}); | |
#print DEBUG Dumper $eth, $ip, $udp; | |
$rouge = display_dhcp_reply($udp->{'data'}, mac2coloned($eth->{'src_mac'})); | |
exit $rouge; # drasztikus | |
}, \$rouge); | |
return $rouge; | |
} | |
sub display_dhcp_reply { | |
my $packet = Net::DHCP::Packet->new($_[0]); | |
if($packet->xid eq $xid) { | |
if($packet->siaddr ne $Router or $packet->getOptionValue(DHO_ROUTERS()) ne $Router) | |
{ | |
printf "src_mac = %s\n", $_[1] || "n/a"; | |
print $packet->toString(); | |
return 1; | |
} | |
} | |
return 0; | |
} | |
sub mac2coloned { | |
return join ':', grep {$_} split/(..)/, $_[0]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment