Monitoring Changes on Your Network over Time Using PBNJ

Joshua D. Abraham

Using Nmap to scan your network has become a security standard. With the addition of OS detection and service version detection, Nmap's features have expanded to the point where it is one of the most useful security tools available today. Nevertheless, it is widely understood that Nmap has several limitations, the chief one of which is the manner in which Nmap processes information about changes. To be frank, it simply doesn't. When you perform an Nmap scan of your network, what Nmap does do is present you with a "snapshot in time "of your network.

The brilliance of Nmap is its capability to perform this task quickly, reliably, and with precision. But as we all know, the only constant in life is change. So when there are changes on your network and you run your next scan, as far as Nmap is concerned, there is no history; there is only the current scan.

Nmap does not provide a convenient method of monitoring changes on the network so, until recently, the only way to actually determine whether anything had changed was to perform another scan and compare the results by hand. This is a very time-consuming process that is tedious and prone to all the typical errors of such tasks. Sure, the *nix wizards would just whip up a quick shell or Perl script to show the changes between two scans, but again, this is still only comparing two snapshots in time.

A better solution is to store a history of the changes in a common location, a database, as opposed to dealing with flat files created on an ad hoc basis. This is exactly what PBNJ does: it parses, correlates, and stores in a database all the information it harvests from Nmap about the changes taking place on your network.


PBNJ is a suite of tools written in Perl. PBNJ calls Nmap to perform a scan and then PBNJ correlates the information about the targets using Nmap's result and the PBNJ database. PBNJ uses Nmap::Parser to extract the data from Nmap's XML output. The default database of PBNJ is Sqlite3; however, PBNJ also supports MySQL and Postgres.

Running Nmap

Here is an example of running Nmap with root privilege by using sudo. The output of the file will be stored in the file.xml so that it can be parsed later.

$ sudo ./nmap -A -oX file.xml
Nmap has many different options and settings. If you want more information about various options use the manpage, or just run "nmap -h".

Installing PBNJ and Its Prerequisites

Besides requiring Nmap::Parser, PBNJ also requires several other Perl modules. The full requirements are:

All of these modules can be installed using CPAN interactive shell or by using the package management system. The source for PBNJ can be downloaded from the PBNJ site at:
Simply download the latest version and extract the files to the desired location:

$ tar -jxvf pbnj-2.04.tar.bz2
Now you can proceed with the installation of PBNJ.

$ perl Makefile.PL
$ make
# make install (as root)
One quick method to make sure everything is set up properly is to parse an Nmap output file. This is most useful for *nix administrators who know exactly which arguments they want to pass Nmap -- those which are optimal in their environment. Therefore, use PBNJ to simply extract the information from the Nmap XML file.

$ scanpbnj -x nmap.xml

Starting Scan of
Inserting Machine
Inserting Service on 22:tcp ssh
Inserting Service on 25:tcp smtp
Scan Complete for
$ rm data.dbl
This shows that PBNJ is working properly. Here I removed the database file to continue with explanation and examples.

PBNJ Files and Features

Once you have installed PBNJ, you will notice that there are a few programs included. Each of these programs has a manpage, if you are curious about learning more about the various options and configurations. The following programs are included:

For this article, I will only talk about ScanPBNJ and OutputPBNJ. The main program of PBNJ is ScanPBNJ, which performs the scanning, parsing, and storing of the Nmap scans in the PBNJ database. OutputPBNJ provides an easy method of accessing the data stored in the PBNJ database.

Single PBNJ Scan

PBNJ can be used to perform a scan of a target.

$ sudo scanpbnj
Starting Scan of
Inserting Machine
Inserting Service on 22:tcp ssh
Inserting Service on 25:tcp smtp
Scan Complete for
It is clear that this machine is running SSH and SMTP mail server on ports 22 and 25. I will now stop SSH and then rescan the machine.

$ sudo /etc/init.d/ssh stop
 * Stopping OpenBSD Secure Shell server...              [ ok ]

$ sudo scanpbnj
Starting Scan of
Machine is already in the database
Checking Current Services
        ! Service 22:tcp ssh is down
        = smtp:25 is (unknown version) Postfix smtpd
Scan Complete for
PBNJ shows that SSH on port 22 is now down. The various types of changes that are tracked are either state changes or version changes. State changes can be either "up" or "down". Version changes are a bit more complicated. For example, when you first scan a machine, all the services that are running on the target are inserted into the database.

Now, when the second scan occurs, ScanPBNJ will take the new scan results and look up the latest information about each of the services for that machine. If there is no service found in the database on a given port, then that service is then inserted into the database. However, if there is a different version running on the machine, ScanPBNJ extracts the information about the service and inserts a new entry into the database with the state set to "down", and then uses the new results and inserts it into the database with a state of "up".

OutputPBNJ Querying

Having direct output from a scanning tool is one thing, but having a system that actually uses queries to extract the information that is most useful to the user, is quite another. OutputPBNJ provides this functionality. It does this by using a YAML configuration file that contains queries for the PBNJ database. It is able to execute any of the queries in the configuration file based on their name and print the result in various output formats. Here is an example of a single query in the configuration file:

- name: latestinfo
  desc: latest host and services info
  sql: |-
    select S.updated_on,, S.service,S.state,S.version,S.protocol
    from services as S,machines as M where updated_on = (select
    updated_on from services ORDER BY updated_on DESC limit 1) and 
    M.mid = S.mid
This contains the name, the description, and the SQL for the query. For now, the SQL isn't important. The only thing you need to know is that it will return the latest changes that have occurred in the database. Therefore, we can use this query to determine what is currently going on by calling OutputPBNJ and simply passing the name of the query and the output type. For this example, we will be using CSV, comma delimited, as the output type for formatting reasons.

$ sudo ./outputpbnj --query latestinfo --type csv
Thu Oct 19 00:08:43 2006,localhost,ssh,down,4.2p1 Debian 7ubuntu3.1,tcp
Automating PBNJ Scans

Now that we have a brief understanding of how to use ScanPBNJ and OutputPBNJ, we actually have the pieces in place to build an automated alerting system. We will use ScanPBNJ to perform an automated scan and then use OutputPBNJ to send the latest changes to an email address.

For this example, I will use a standard 192.168.10.x network layout, which is a class C network for which I am the administrator. To begin, open your favorite text editor and create /root/bin/scan.

outputpbnj -q latestinfo -t csv | mail -s "PBNJ LatestInfo `date`" root
The first line simply specifies that we are doing shell programming. The second line performs a scan of the target IP range using the default arguments. Note that we are not using ping for this scan. This will make the scan more accurate because all IP addresses will be scanned regardless of whether they respond to a ping.

The third line is a bit more complicated. The first part is calling the "latestinfo" query, as we have seen, and the second part is simply piping the output from OutputPBNJ into mail. This uses the output from OutputPBNJ as the input to mail. We specify the subject line that contains the program name, query name and the date data. Finally, we specify the user to whom the email will be sent, which is the local root user.

Make sure the file is executable:

$ sudo chmod +x /root/bin/scan
We then add the script to the Cron scheduler. Add the following to your crontab:

# scan of 10 network every 2 hours and email the latest changes to root
#m    h      dom    mon    dow    user    command
16    */2    *      *      *      root    /root/bin/scan
Bam! Now you will have an automated scanning system with alerting in place. It almost seems too good to be true!

Addition Features of PBNJ

PBNJ has several additional features that could not be covered in this article:

Here are various topics that may be included in a follow-up article:

PBNJ's Default Sqlite3 Schema

For the people who would like to build their own queries, the SQL schema is built to be flexible. The three databases that are supported are Sqlite3, MySQL, and Postgres. Sqlite3 is the default database, so I have included the Sqlite3 schema below for reference.

CREATE TABLE machines (
            ip TEXT,
            host TEXT,
            localh INTEGER,
            os TEXT,
            machine_created TEXT,
            created_on TEXT);
CREATE TABLE services (
            mid INTEGER,
            service TEXT,
            state TEXT,
            port INTEGER,
            protocol TEXT,
            version TEXT,
            banner TEXT,
            machine_updated TEXT,
            updated_on TEXT);
Recent Work

I gave a presentation about PBNJ at LinuxWorld August 2006 San Francisco. Also, PBNJ is included in Backtrack, a well-known Security LiveCD. BackTrack has been dubbed as the best Security Live CD today, and rated 1st in its category, and 32nd overall in Using a LiveCD is one of the easiest methods to try out PBNJ before you install it. In addition, PBNJ is officially packaged for Debian, FreeBSD and Gentoo. I recommend using package management regardless of whether you have a great deal of *nix experience or not.

Installation on Debian (as root):

# apt-get install pbnj
Installation on FreeBSD (as root):

# cd /usr/ports/security/pbnj ; make install clean
Installation on Gentoo (as root):

# emerge pbnj

I hope that you have gained an understanding of the features that PBNJ can provide when monitoring a network. By using PBNJ, you will be letting Nmap focus on what it does best, which is providing a snapshot in time. You will simultaneously be harnessing PBNJ to correlate the changes that occur, to provide a flexible querying/alerting system -- one that you can customize to fit in any environment.



Nmap --

Author's Web site --

Backtrack --

Joshua D. Abraham is a 4th-year Computer Science student at Northeastern University. He has focused on various network security-related projects such as PBNJ, IDS, and Backtrack. He also works with the Volunteer Systems Group at his college, in addition to working as a Security Professional.