Showing posts with label Perl. Show all posts
Showing posts with label Perl. Show all posts

Wednesday, May 10, 2017

DNS PII UH-OH

A seemingly normal DNS packet:

ETH: dst:98:76:54:32:10:ab  src:ab:cd:ef:12:34:56  type:0x0800
IPv4: version:4  hlen:5  tos:0x00  length:190  id:44821
IPv4: flags:0x00  offset:0  ttl:128  protocol:0x11  checksum:0xb060
IPv4: src:8.8.8.8  dst:192.168.10.1
UDP: src:53  dst:13245  length:170  checksum:0x8756
DNS: id:21817  qr:1  opcode:0  flags:0x18  rcode:0
DNS: qdCount:1  anCount:4
DNS: nsCount:0  arCount:0
DNS::Question: name:www.www.com
DNS::Question: type:1  class:1
DNS::RR: name:www.www.com
DNS::RR: type:5  class:1  ttl:64  rdlength:16
DNS::RR::CNAME: cname:www.Johnny.com
DNS::RR: name:www.www.com
DNS::RR: type:5  class:1  ttl:64  rdlength:17
DNS::RR::CNAME: cname:www.Fingers.com
DNS::RR: name:www.www.com
DNS::RR: type:1  class:1  ttl:64  rdlength:4
DNS::RR::A: address:25.7.42.197
DNS::RR: name:www.www.com
DNS::RR: type:1  class:1  ttl:64  rdlength:4
DNS::RR::A: address:73.156.82.200

But upon some decoding ...

Thursday, May 04, 2017

Net::SSH2 Upgrade Access to Cisco

I upgraded my Strawberry Perl to 5.24 and my GNS3 test lab for SSH access via CRAPPS suddenly didn't work.

Knowing the Perl version changed, I checked and the Net::SSH2 version changed (0.58 to 0.63). Troubleshooting was pretty easy - just a quick Perl script to open a connection and die on failure with error:

#!perl
use strict;
use warnings;
use Net::SSH2;

my $ssh2 = Net::SSH2->new();
$ssh2->connect('10.254.254.1') or $ssh2->die_with_error;
print "Success\n";

The error was: Unable to exchange encryption keys (-8 LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE). OK, so I said troubleshooting was easy ... the solution ... maybe not so much?

Monday, March 21, 2016

Remote Send from the Comfort of Your Packet Crafting Shell

I've been spending a lot of time updating the Perl Packet Crafter to a new version - a complete rewrite with lots of new features and plugins.

During the course of testing, I thought about remote sending and remote capture, that is, using the shell to create the packets and passing them to an agent on a remote machine for sending. Like a the old Network General / Associates distributed Sniffer concept, but distributed sender in this case.

Low and behold, WinPcap and Wireshark have the remote capture functionality, but remote sending? That required some research.

Tuesday, August 25, 2015

Perl Automation Saves the Day - 6 of them in fact!

How does one analyze 182,000+ lines of Excel looking for correlations? I suppose there may be some Excel magic, but when IP addresses are involved - and I know Excel hasn't had an out-of-the-box IP network sort order, methinks I'm in for a long slog.

The background: I have an export from a customer IP address management tool and we needed to verify the block type assignments were correct. To do this, I needed to understand the top-level aggregates (IP network and mask) for each top-level container (site). There were around 50 aggregates in each of 6 containers. I then needed to verify if the 30,000+ subnetworks in each container did or didn't fall within the 50 associated aggregate blocks.

Where to begin? Looking at, analyzing and deciding on 1 line per second would still take almost 6 and a half working days (8 hours / per day); and 1 per second is a gross underestimate of the actual time required. I needed to automate this somehow.

Wednesday, August 05, 2015

Debugging Perl Debugger: Part 3 - Automation

We have Perl debugging integrated with Notepad++. We fixed variable values so they show up in watch lists. Now we want to automate the use of Perl debugging directly from Notepad++ while editing a Perl script.

NppExec is a plugin with powerful automation features for Notepad++. The documentation showed it had all I needed to get this to work.

Tuesday, August 04, 2015

Debugging Perl Debugger: Part 2 - Variable Values Vindicated

In yesterday's post, I described how I got Perl debugging integrated with Notepad++. I had issues with watch variables only showing the variable name and type, not the value. I had source code from both the DBGp plugin and the Komodo Perl debugger. My only real option was to change the Perl debugger source as the plugin was written in a language I'm unfamiliar with and didn't have a compiler for.

The DBGp plugin offers a cool button labeled "DBG" which pops up a window with the raw XML messages sent between the debugger and the plugin. Like a packet capture trace file, this was a great place to start. Adding a watch variable and querying it's state showed the XML exchange and specifically where the value was returned:

...
&ltproperty name="$VERSION" fullname="$VERSION" encoding="base64" type="scalar" constant="0" children="0" size="25" &gt
    &ltvalue encoding="base64"&gt
        &lt![CDATA[MS4wIC0gMjkgSlVMIDIwMTU=]]&gt
    &lt/value&gt
&lt/property&gt
...

So it was getting sent. Why couldn't the plugin read and display it?

Monday, August 03, 2015

Debugging Perl Debugger: Part 1 - Notepad++ Integration

I use Notepad++ for most of my Windows text-based editing including Perl scripts. Every once in a while I'm stuck and need debugging, but I was too lazy to learn the Perl debugger command line. I had a simple Perl-specific IDE laying around solely to use for the integrated debugging in its Graphical User Interface (GUI). The ideal solution would be Perl debugger integration with Notepad++.

Google searches revealed there was no Perl-specific debugger integration in Notepad++; however, there was a debugger plugin - DBGP Plugin listed on the Notepad++ Plugins list. It was originally written for PHP and Xdebug integration. Further searches and the README lead me to believe it may support other programming languages as long as they supported the DBGP - common debug protocol. Could Perl work this way?

Monday, June 23, 2014

Perl and SQLite - Follow Through, On and Up

As follow through to my previous post, I did some investigation into SQLite and got that working in my little database proof of concept script.

As follow-on to my digression in that post, SQLite adds yet another wrinkle in the AUTO_INCREMENT saga I used as a primary key in each of my tables. These are the strings I found to work:

Microsoft Access
ID AUTOINCREMENT NOT NULL PRIMARY KEY
MySQL
ID INT AUTO_INCREMENT NOT NULL PRIMARY KEY
SQLite
ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL

As follow-up to my statement about not finding a way to programmatically instantiate a MySQL database, I did some searching and found the following in the 'perldoc' for DBD::mysql - I have yet to try it.

$rc = $drh->func('createdb', $database, $host, $user, $password, 'admin');

...

createdb
    Creates the database $dbname. Equivalent to "m(y)sqladmin create $dbname".

And finally, the topic of this post. I found this neat little Perl extension for SQLite and thought I might give it a try.

Learning from my last post, I checked my SQLite version and it was 32-bit, so I switched my Perl to Strawberry 5.18.1 32-bit on Windows 7 x64 and followed the steps outlined in that post. It worked a treat first time! So I ported the executable extension to my other machine and tried it and it failed.

Knowing my other machine was 64-bit for everything I switched Perl back to Strawberry 5.18.1 64-bit on my original machine and failure there too. It makes sense; I compiled a 32-bit Perl extension for a 32-bit version of SQLite - it stands to reason it would need a 32-bit version of Perl to run it. But I use 64-bit Perl normally.

Easy enough solution, I grabbed the sources for SQLite from their download page and compiled myself with the 64-bit gcc from Strawberry Perl 5.18.1 64-bit. This incidentally was the easiest build I've done - no warnings, no errors, built first time.

gcc shell.c sqlite3.c -o sqlite3.exe -lpthread

I redid the extension compile with 64-bit Perl and all worked again - this time all with 64-bit.

Since I did this a few times (due to my own mistake), I created a Perl package with a Makefile.PL to easily rebuild the SQLite Perl extension whenever / wherever needed.

Tuesday, June 17, 2014

64-bit Solution for "2-bit" Perl Problem

I'm not a Perl programming by trade, I just know enough to help me in my day to day activities. That said, database development / interaction with Perl is not a day to day activity, but it's something I feel I should know at a base level for the few times I've needed it in the past. To that end, I have a simple script that illustrates database instantiation, creation, population and some data manipulations with Perl.

I mainly use Windows, so the database for my little experiment was Microsoft Access as that's the most common database found on Windows; usually part of an enterprise Microsoft Office installation. I also have a view to cross platform capabilities, so I made my script check the operating system ($^O eq "MSWin32") and if not Windows, use MySQL as the database.

<digression>This presents some issues with database creation - creating tables has a different SQL syntax between Microsoft Access and MySQL so again an operating system check is required for some activites. For example, both have the concept of auto-increment, but MySQL is AUTO_INCREMENT, Access is AUTOINCREMENT. And while MySQL needs a type definition (INT AUTO_INCREMENT), Access believes AUTOINCREMENT is explicitly an integer type of its own, so will fail with INT AUTOINCREMENT.</digression>

My script:

  1. Instantiates - make a database (if it doesn't already exist)
  2. Creates - create the tables / schema in the database
  3. Populates - adds some data to the database
  4. Manipulates - does some SQL INSERT, UPDATE, DELETE, SELECT queries

I couldn't find a way to programmatically instantiate a database in MySQL - the database needs to exist for my script to use it. It's easy enough to do with some simple MySQL commands (with operations for a test example emphasized over secure best practices):

$ mysql -u root -p

mysql> create database PerlTest;
mysql> grant usage on PerlTest.* to perluser@localhost identified by 'password';
mysql> grant all privileges on PerlTest.* to perluser@localhost;

I did find a way to programmatically instantiate a Microsoft Access database in Perl. It relies on Win32::OLE, standard with Strawberry Perl - my Perl of choice for Windows.

use Cwd;
use Win32::OLE;
use Win32::OLE::Const;

my $dbname = getcwd . '/' . 'PerlTest.accdb';
my $props = Win32::OLE::Const->Load('Microsoft DAO')
my $dbLangGeneral = $props->{dbLangGeneral};

my $Access = Win32::OLE->new('Access.Application', 'Quit');
my $Workspace = $Access->DBEngine->CreateWorkspace('', 'Admin', '');
my $Database = $Workspace->CreateDatabase($dbname, $dbLangGeneral);

It's at this point I should mention I did this years ago on Windows XP (32-bit) with Strawberry Perl 5.10.1 (32-bit). I've since upgraded to Windows 7 (64-bit) and Strawberry Perl 5.18.1 (64-bit) and along the way, the script "broke".

I decided for no particular reason the other day to see what was wrong. I was sure it was a 32/64 bit issue with the instantiate code. On Windows 7 64-bit, I could instantiate the database with Strawberry 32-bit, but not connect to it for steps 2 through 4. With Strawberry 64-bit, I couldn't instantiate the database, but could do steps 2 through 4 on an existing database.

It turns out, there isn't a 64-bit version of "Microsoft DAO" and instead, you should rely on "Microsoft Office 14.0 Access Database Engine Object" for similar functionality. Changing the line to read:

my $props = Win32::OLE::Const->Load('Microsoft Office 14.0 Access Database Engine Object')

did the trick!

Next step - testing this all with SQLite.

Tuesday, April 02, 2013

Open Source and Closed Protocols

There isn't a better way to learn a protocol for me than reading the RFC and watching Wireshark captures and decodes. However, it becomes difficult when we're talking about a closed proprietary protocol.

Recently, I've been creating new protocol modules for the Net::Frame Perl module suite. Hot Standby Router Protocol (HSRP) is a Cisco proprietary protocol but is detailed in RFC 2281. However, that's only version 1. Version 2 isn't in an open RFC. Another example is Cisco Discovery Protocol (CDP) which has no RFC for version 1 or version 2. Luckily, there is plenty of information on the Internet.

I couldn't get all the information for all the CDP message types, but I'm about 90% there. A more interesting note was in the CDP header itself. There is a checksum that's described as the "normal Internet checksum". However, when I implemented that in a Net::Frame::Layer::CDP module, it didn't work. What was the problem?

A Google search lead me to the source code for Wireshark and a file called packet-cdp.c. A comment beginning on line 230 explains:

"CDP doesn't adhere to RFC 1071 section 2. (B). It incorrectly assumes checksums are calculated on a big endian platform, therefore i.s.o. padding odd sized data with a zero byte _at the end_ it sets the last big endian _word_ to contain the last network _octet_. This byteswap has to be done on the last octet of network data before feeding it to the Internet checksum routine. CDP checksumming code has a bug in the addition of this last _word_ as a signed number into the long word intermediate checksum. When reducing this long to word size checksum an off-by-one error can be made. This off-by-one error is compensated for in the last _word_ of the network data."

That meant I needed some "massaging" of my payload before sending to my Internet checksum routine. A quick proof of concept proved correct and then some solicitation for simplification produced this:

   [...]

   if (length( $payload )%2) {
      if (substr($payload, -1) ge "\x80") {
         substr $payload, -1, 1, chr(ord(substr $payload, -1) - 1);
         substr $payload, -1, 0, "\xff";
      } else {
         substr $payload, -1, 0, "\0";
      }
   }

   [...]

   $self->checksum(inetChecksum($phpkt));

I also found a very long thread in a mailing list archive that describes how some smart guys found this problem and came up with the Wireshark solution. Thanks for open source - even on closed protocols!

Tuesday, March 05, 2013

Thinking Outside the "Package" for Packets

I responded to a query on a community forum for Perl about creating a DNS update with a spoofed source address. I was ASSURED it was for a contest and the code was what was essential. The contest site recommended scapy, but the poster was looking for a Perl solution. I had the easy answer: Perl Packet Crafter.

The poster went on to detail some code that created the DNS update but when he sent the packet, he couldn't change the source IP address. Obviously. He used Net::DNS as follows:

use strict;
use warnings;
use Net::DNS;

my $update = Net::DNS::Update->new('evil.zz');
$update->push(prerequisite => nxrrset('hacker11.evil.zz. A'));
$update->push(update => rr_add('hacker11.evil.zz. 86400 A 127.0.0.1'));

my $res = Net::DNS::Resolver->new;
$res->nameservers('192.168.200.113');

my $reply = $res->send($update);

The last line sends the update by taking the created DNS data and letting standard socket routines create the Layer 3 header. Perl Packet Crafter (PPC) can create the custom Layer 3 header and with Net::Frame::Layer::DNS (which I wrote), one can easily create the required packet. However, with most of the work done, is it necessary to recode the original script using Net::Frame::Layer::DNS? Turns out ... no.

Because of the layered nature of the Net::Frame suite of modules on which PPC is based, one can easily create any or all layers of a frame with the objects or simply by hand crafting an octet stream. Or ... even using another Perl module that can output the required stream.

For Net::DNS, there is an undocumented sub called make_query_packet() in the Net::DNS::Resolver::Base code that creates a Net::DNS::Packet object on which the data() method can be called to create the necessary octet stream. It may sound complicated, but all it means is replace the last line of code above with:

$dnsdata = $res->make_query_packet($update);

Now, in PPC, you can use the $dnsdata->data call to create the DNS payload in a UDP packet. It looks like the following:

VinsWorldcom@C:\tmp\> ppc.pl -i "Wireless Network Connection"
Welcome to Perl Packet Crafter (PPC)
Copyright (C) Michael Vincent 2012

Wireless Network Connection

ppc> use Net::DNS;
ppc> $update = Net::DNS::Update->new('evil.zz');
ppc> $update->push(prerequisite => nxrrset('hacker11.evil.zz. A'));
ppc> $update->push(update => rr_add('hacker11.evil.zz. 86400 A 127.0.0.1'));
ppc> $res = Net::DNS::Resolver->new;
ppc> $res->nameservers('192.168.200.113');
ppc> $dnsdata = $res->make_query_packet($update);

We've used PPC to enter the original code with the modified last line - instead of send, use the make_query_packet() routine to create the Net::DNS::Packet object. Continuing, we create the packet in PPC:

ppc> $ether = ETHER;
ppc> $ipv4 = IPv4(src=>'1.1.1.1',dst=>'192.168.200.113',protocol=>NF_I+Pv4_PROTOCOL_UDP);
ppc> $udp = UDP(dst=>53,payload=>$dnsdata->data);
ppc> $packet = packet $ether,$ipv4,$udp;

And to be sure before we send it, we can use Net::Frame::Layer::DNS for a nice decode:

ppc> use Net::Frame::Layer::DNS qw(:consts);
ppc> decode $packet;
ETH: dst:55:66:88:78:aa:30  src:c0:c1:c2:08:46:56  type:0x0800
IPv4: version:4  hlen:5  tos:0x00  length:90  id:23417
IPv4: flags:0x00  offset:0  ttl:128  protocol:0x11  checksum:0x53fe
IPv4: src:1.1.1.1  dst:192.168.200.113
UDP: src:50281  dst:53  length:70  checksum:0x5661
DNS: id:5329  qr:0  opcode:5  flags:0x00  rcode:0
DNS: qdCount:1  anCount:1
DNS: nsCount:1  arCount:0
DNS::Question: name:evil.zz
DNS::Question: type:6  class:1
DNS::RR: name:hacker11.[@12(evil.zz)]
DNS::RR: type:1  class:254  ttl:0  rdlength:0
DNS::RR: name:[@25(hacker11.[@12(evil.zz)])]
DNS::RR: type:1  class:1  ttl:86400  rdlength:4
DNS::RR::A: address:127.0.0.1

Job done!

Note this can also be done with other Perl modules like Net::DHCP::Packet with the new() and serialize() calls, for example.

Thursday, October 25, 2012

More on IPv6 in Perl Modules

Recently, I went on a tear updating my Perl modules on CPAN to support IPv6. Since my modules (and scripts) rely on other core modules like Socket, IO::Socket and some non core modules like Net::SNMP and Net::Telnet(::Cisco), I had a task to make sure the underlying modules supported IPv6.

I already dealt with IPv6 in the core Socket module for Windows in a previous post. So now to deal with a higher level of code written by other developers. Would they be responsive?

Turns out ... YES! I worked with the author of Net::TFTPd a while back to get an ASCII / BIN mode bug fix submitted and he was again very responsive with my IPv6 ask and patch. So IPv6 capable TFTP server - Perl has one!

I also rely on Net::Telnet::Cisco which uses Net::Telnet to do all the heavy lifting. Net::Telnet looked like it hadn't been updated since 2002 but I submitted the query / patch and contacted the author. In a few days he got back to me and we had a pretty detailed dialogue about adding IPv6 support. There may be a coming version which does this "natively", but imagine my surprise when I found Net::Telnet was already IPv6-capable!

The "workaround" - and as workarounds go, this is the least kludgey I've seen - goes like this:

Open a socket using an IPv6 capable module - like IO::Socket::IP:

use IO::Socket::IP -register;

my $socket = IO::Socket::IP->new(
    PeerHost => '192.168.10.1' # or 2001:db8:192:168::10:1
    PeerPort => 23,
    Family   => AF_INET # or AF_INET6
) or die "not connected\n";

Then, use the $socket handle as an argument to the 'fhopen' parameter in the Net::Telnet new() constructor:

my $session = Net::Telnet->new(
    fhopen => $socket
);

Jay Rogers (Net::Telnet author) is obviously a genius providing IPv6 support in Net::Telnet before Perl core even supported IPv6! Ok, maybe that's a stretch, but certainly the openness of the Net::Telnet API allows for this kind of "plug-and-play" with IPv4/v6 sockets and enables IPv6 right now, while in the background "native" support may be coming.

Thanks Jay!

Monday, August 20, 2012

IPv6 in Perl on Windows

The state of IPv6 support in Perl has long been lamented but there are those trying to change this including Paul Evans who's updating many of the networking modules - including the Socket core module - to support IPv6. It's a shame the C compiler shipping with Strawberry Perl still doesn't accommodate IPv6 on Windows.

I've been using Strawberry Perl after switching from ActiveState since version 5.10. I found myself needing more and more XS modules and the ActiveState PPM process was always lagging behind. I had a copy of MinGW gcc - the free Windows port of the 'gcc' compiler - and with 'dmake', I could build my own modules directly from CPAN. However, I had some issues and when I learned about Strawberry Perl including the gcc compiler in the distribution, I switched. I've used Strawberry Perl on Windows XP 32-bit and now Windows 7 64-bit.

I've also recently been doing a lot of IPv6 work and using Perl as a quick development platform to test the IPv6 protocol proved easy with the Net::Frame modules detailed in an early series of blog posts. However, when looking to create sockets with Perl, I found errors, most notably:

Socket::inet_ntop not implemented on this architecture

The lack of this function prevented the display of IPv6 addresses and it's missing complement - inet_pton - prevents the resolution of IPv6. These functions are certainly present in Windows 7.

The issue lies in the header and library files distributed with MinGW gcc compiler in the Strawberry Perl release. They simply don't identify the functions in the headers, nor provide linkage to them in the libraries. Fortunately, tools shipped with the MinGW gcc compiler in Strawberry Perl can be used to remedy the issue and get full IPv6 functionality. Here's how.

First off, we need to update the header file with the function prototypes. I did this by creating a new header file called 'ws2tcpip-win.h':


#ifndef WS2TCPIP_WIN_H
#define WS2TCPIP_WIN_H

typedef USHORT ADDRESS_FAMILY;

#if (NTDDI_VERSION >= NTDDI_VISTA)
WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT, PCSTR, PVOID);
WINSOCK_API_LINKAGE INT WSAAPI InetPtonW(INT, PCWSTR, PVOID);

WINSOCK_API_LINKAGE PCSTR WSAAPI inet_ntop(INT, PVOID, PSTR, size_t);
WINSOCK_API_LINKAGE PCWSTR WSAAPI InetNtopW(INT, PVOID, PWSTR, size_t);

#define InetPtonA       inet_pton
#define InetNtopA       inet_ntop

#ifdef UNICODE
#define InetPton        InetPtonW
#define InetNtop        InetNtopW
#else
#define InetPton        InetPtonA
#define InetNtop        InetNtopA
#endif

#if INCL_WINSOCK_API_TYPEDEFS
typedef INT (WSAAPI * LPFN_INET_PTONA)(INT Family, PCSTR pszAddrString, PVOID pAddrBuf);
typedef INT (WSAAPI * LPFN_INET_PTONW)(INT Family, PCWSTR pszAddrString, PVOID pAddrBuf);

typedef PCSTR (WSAAPI * LPFN_INET_NTOPA)(INT Family, PVOID pAddr, PSTR pStringBuf, size_t StringBufSize);
typedef PCWSTR (WSAAPI * LPFN_INET_NTOPW)(INT Family, PVOID pAddr, PWSTR pStringBuf, size_t StringBufSize);

#ifdef UNICODE
#define LPFN_INET_PTON          LPFN_INET_PTONW
#define LPFN_INET_NTOP          LPFN_INET_NTOPW
#else
#define LPFN_INET_PTON          LPFN_INET_PTONA
#define LPFN_INET_NTOP          LPFN_INET_NTOPA
#endif

#endif  //  TYPEDEFS
#endif  //  (NTDDI_VERSION >= NTDDI_VISTA)

#endif

Next, copy that file to the appropriate location:

copy ws2tcpip-win.h c:\strawberry\c\x86_64-w64-mingw32\include

and update the existing 'ws2tcpip.h' file in that location with the following:

#include <ws2tcpip-win.h>

Now we need to create the library. This is done by first exporting the symbols from the appropriate DLL file - in this case, ws2_32.dll - and then creating the link library. The version of the MinGW gcc compiler with Strawberry Perl has 'pexports' included, so simply create the DEF file:

pexports c:\windows\system32\ws2_32.dll > ws2_32.def

Now create the library:

dlltool --as-flags=--64 -m i386:x86-64 -k --output-lib libws2_32-new.a --input-def WS2_32.def

Then put the new library in the appropriate location:

copy libws2_32-new.a c:\strawberry\c\x86_64-w64-mingw32\lib

Now we're ready to go. Get the latest release of Socket from CPAN, unzip and extract the tar file. Change into the directory and start the build process:

perl Makefile.PL

Now, before doing the 'dmake', edit the generated Makefile. Find the "DEFINE =" and "LDLOADLIBS =" lines and update them as per below:

DEFINE = -DHAS_GETADDRINFO -DHAS_GETNAMEINFO -DHAS_SOCKADDR_IN6 -DHAS_IP_MREQ -DHAS_IP_MREQ_SOURCE -DHAS_IPV6_MREQ -DHAS_INETNTOP -DHAS_INETPTON -DAF_INET6

LDLOADLIBS = -lmoldname -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -lnetapi32 -luuid -lws2_32 -lmpr -lwinmm -lversion -lodbc32 -lodbccp32 -lcomctl32 -lws2_32-new

Finally, 'dmake', 'dmake test' and 'dmake install' and you're good to go with IPv6!

Wednesday, February 08, 2012

Penultimate Perl Packets

Previous posts pontificated Perl packet crafting. This entry encapsulates every earlier evidence on the issue.

The Windows Packet Crafter is back - newly imagined as a Perl script that creates an interactive shell around the Net::Frame modules for creating and capturing custom packets from a Windows computer.

The misery of making packets on a Windows computer is of course the Windows TCP/IP stack's scarcity of support for raw socket options like IP_HDRINCL and normal setsockopt() calls. The way around this is literally around the Windows TCP/IP stack with a driver that allows access to the network hardware without the Windows API aggravation. The WinPcap library is a useful tool in this regard and there exists a Net::Pcap Perl module to fully utilize this functional utility.

I've already sung the praises in previous posts of the Net::Frame suite. Windows Packet Crafter creates a wrapper script to present a shell with commands and macros for creation and manipulation of packets using the Net::Frame suite.

Since Windows Packet Crafter is written in Perl as a Perl module with a separate shell script, one can easily create their own scripts utilizing the abstracted methods, macros and commands of the module. Or, for the lazy, just use the included scripts to create ARP requests, IPv4 and IPv6 traceroutes, TCP connections and many other common and useful tasks.

Windows Packet Crafter can easily be expanded with additional Perl modules - like Convert::ASN1 to decode SNMP packets (example is included in the distribution) or new 'plugins' to extend the features / commands in the Windows Packet Crafter shell environment.

Give it a test run and let me know what can be improved!

Friday, December 23, 2011

Perl Packet Playback Part 2

To follow on from yesterday's post, I did some more searching today and found a whole suite of Perl modules to assist in creating custom packets.

Net::Frame

Patrice Auffret is a genius. This expandable module framework assists in crafting just about any type of packet you want simply by building the layers from the modules used. If a protocol doesn't exist, not to worry, you can create a new module that fits easily into the framework and continue testing.

Some of the more advance routines allow for device identification and automate the sending and receiving, but they also rely on Net::Libdnet which won't compile in Strawberry Perl for Windows for me. Not to worry, I simply use the Net::Pcap module to handle the sending and receiving of the frames I create with the Net::Frame library of modules.

The only thing that's missing is the nice wrapper script like Scapy (or Scaperl) that makes it an interactive tool. But with an interface as easy as:


#!/usr/bin/perl

use strict;
use warnings;

# load modules we'll need for ICMPv6 ping
use Net::Frame::Simple;
use Net::Frame::Layer::ETH qw(:consts);
use Net::Frame::Layer::IPv6 qw(:consts);
use Net::Frame::Layer::ICMPv6 qw(:consts);
use Net::Frame::Layer::ICMPv6::Echo;

# create ethernet header
my $ether  = Net::Frame::Layer::ETH->new(
                 src  => '00:00:00:00:00:11',
                 dst  => 'ab:cd:ef:00:11:22', # Router MAC
                 type => NF_ETH_TYPE_IPv6
             );
# create IPv6 header
my $ipv6   = Net::Frame::Layer::IPv6->new(
                 src => '2001::1',
                 dst => '2001:4860:800e::68',
                 nextHeader => NF_IPv6_PROTOCOL_ICMPv6
             );
# create ICMPv6 header
my $icmpv6 = Net::Frame::Layer::ICMPv6->new(
                 type => NF_ICMPv6_TYPE_ECHO_REQUEST,
                 code => NF_ICMPv6_CODE_ZERO
             );
# create ICMPv6 echo request
my $echo   = Net::Frame::Layer::ICMPv6::Echo->new(
                 payload => 'echo'
             );

# put it all together, this automatically
# calculates and populates length and 
# checksum fields in all headers - SWEET!
my $packet = Net::Frame::Simple->new(
    layers => [ $ether, $ipv6, $icmpv6, $echo]
);

####################
# send with Net::Pcap
use Net::Pcap qw(:functions);

my %devinfo;
my $err;

my $interface = pcap_open($ARGV[0], 100, 0, 1000, \%devinfo, \$err);

if (!defined($interface)) {
    printf "Unable to open adapter '%s'\n", $ARGV[0];
    exit 1;
}

if (pcap_sendpacket($interface, $packet->raw) != 0) {
    printf "Error sending packet: %s\n", pcap_geterr($interface);
    exit 1;
}

I think I'm good to go!

Even tcpdump thinks so:

tcpdump: listening on \Device\NPF_{12345678-ABCD}
19:36:29.686987
00:00:00:00:00:11 > ab:cd:ef:00:11:22, ethertype IPv6 (0x86dd), 
length 66: (hlim 255, next-header: ICMPv6 (58), length: 12) 
2001::1 > 2001:4860:800E::68: 
[icmp6 sum ok] ICMP6, echo request, length 12, seq 10299
    0x0000:  abcd ef00 1122 0000 0000 0011 86dd 6000  ....."........`.
    0x0010:  0000 000c 3aff 2001 0000 0000 0000 0000  ....:...........
    0x0020:  0000 0000 0001 2001 4860 800e 0000 0000  ........H`......
    0x0030:  0000 0000 0068 8000 0084 804d 283b 6563  .....h.....M(;ec
    0x0040:  686f                                     ho

Thursday, December 22, 2011

IPv6 Packet Crafting in Perl

I refuse to learn a new programming language just so I can use a tool developed in it. Scapy - a packet crafting tool written in Python - seems like a pretty cool utility when doing testing, as I have been with IPv6 recently. On a Windows machine, options are severely limited when it comes to crafting packets unless you use the WinPcap libraries, and Scapy does this.

However, I don't know Python and have no desire to learn or install it just to get the use of Scapy for the limited testing I'm doing. I do however know Perl and when I found Scaperl I was delighted. It's a port of Scapy from Python to Perl but with very limited feature set and a release date of 2006. At least enough for what I wanted - until I read:

  • there is no support for IPv6.

in the "Known limitations" section of the site. A little more searching lead me to a revived Scaperl site so I grabbed that copy and started testing.

The first step was the install of a few required modules that I didn't yet have - no problems. You will need Net::Pcap installed and there were some issues with the latest version for Windows (cleared up with available patches) back when I installed this long ago. I got Scaperl running and sent a test packet.

The code is a little 'buggy' that is to say not so much incorrect, but it doesn't quite perform the way one would expect. Checksum calculation is one such example. If I don't set the checksum (say in an IP header) I expect Scaperl to calculate for me - which it does. However, if I make some modifications to the packet and then resend, the checksum is incorrect. Scaperl uses the previously calculated checksum. This could be fixed by checking for a "calcualte_checksum" flag versus testing if the checksum value is 0 (which it is originally, but not so much after a first calculation). Minor annoyances which have their workarounds.

More importantly, this Scaperl version has some IPv6 support. I was quickly able to complete the code needed and was sending IPv6 test packets soon after. There isn't yet support for IPv6 Extension Headers, but I was able to cobble together some code that lets me send the routing extension header (IPv6 loose source routing). More work on this when time allows.

 

Copyright © VinsWorld. All Rights Reserved.