Brady Bunch Boondoggle - Answer Resources

The Brady Bunch Boondoggle hacking challenge is located here. The answers and winners are available here.

I submitted the following answer. I spent more time on the bonus question by decoding the SSH traffic and examining the steps taken to compromise the server.


ANSWERS

1) What SSID is used for the kids' rogue AP?

The SSID of the kid's rogue access point is: boondoggle.

2) How were the kids able to access Greg's rogue access point even
   though it was not detected during Mr. Phillips PCI compliance
   assessment?

The rogue AP was not detected during the PCI audit because the auditor
was using Kismet.  Kismet is a passive wireless monitoring tool and
wouldn't detect a rogue access point that wasn't broadcasting its SSID
unless the AP was actively being used.

3) How did Peter compromise the target system?

Peter was able to compromise the Human Resources' payroll system by
first gaining access to the 'mphillips' account on 172.16.0.99.  The
172.16.0.99 machine was running an outdated version of Ubuntu 7.10
with an SSH server that was affected by the Debian OpenSSL predictable
PRNG vulnerability.

Once Peter had terminal access on 172.16.0.99, he was able to directly
access the Human Resources' MySQL database containing the payroll
information; the firewall rules permitted connections on port 3306
(the default MySQL port) from 172.16.0.99.

BONUS QUESTION: What steps did Peter take to change Mike's salary
                after compromising the target?  What is Mike's new salary?

Once Peter had gained access to the Human Resources' database, he
enumerated the available database instances.  He then listed the
tables in the 'payroll' database and examined the contents of each.
After determining the appropriate database table, columns and
identifier values, Peter directly updated the salary for Mike Brady.

Mike's new salary is 185000 -- ten times his previous salary of 18500.

ANALYSIS

1) The SSID.

The SSID can be determined by analyzing the 'bbb.dump' wireless
packet capture file that cousin Oliver recorded.  A recent version of
Wireshark can be used to examine the packet capture.  By carefully
following the exchange of Probe Requests, Probe Responses and Beacon
Frames, the Authentication and Association Requests can be determined.

But perhaps a simpler method of examining the wireless packet capture
is by using Kismet with the 'pcapfile' source type.  In Kismet, the
'pcapfile' source type can be use to read from an existing capture
file.

Examining the 'bbb.dump' file is Kismet reveals two SSIDs of interest:

 Name                      T W Ch  Packts Flags IP Range
 Probe networks            G N ---     78       0.0.0.0
 <no ssid>                 P N ---     62       0.0.0.0
 somethingclever           P N ---     16       0.0.0.0
 boondoggle                A O 011 223155  T4   172.16.0.6


The 'somethingclever' SSID has only a handful of packets associated
with it and no IP address range.

The 'boondoggle' SSID is more interesting and had over 220,000 packets
exchanged and IP address range of 172.16.0.6.

Listing the clients for the 'boondoggle' SSID shows the following:

 T MAC               Manuf      Data Crypt  Size IP Range
 I 00:1A:70:FC:C0:6F Unknown       0     0    0B 0.0.0.0
 F 00:10:C6:CE:CA:71 Usi           4     0  426B 172.16.0.98
 S FF:FF:FF:FF:FF:FF Unknown       0     0    0B 0.0.0.0
 F 00:0C:29:FD:A6:66 Unknown  119653     0   14M 172.16.0.99
 E 00:19:7D:1B:03:FA Unknown   94131     0   10M 172.16.0.6


2) Why didn't 'boondoggle' show up during the audit?

Kismet is a passive wireless monitoring tool which means that it
requires the presence of SSID values in beacon frames or probe
response/requests.

An active testing tool such as Netstumbler would most likely have
detected the 'boondoggle' SSID when it probed the network.  Based on
the packet capture, the rogue AP responded to broadcast probe
requests:

14:55:24.422780 Probe Request () [1.0 2.0 5.5 11.0 6.0 9.0 12.0 18.0 Mbit]
14:55:24.629709 Probe Request () [1.0 2.0 5.5 11.0 6.0 9.0 12.0 18.0 Mbit]
14:55:35.585406 Probe Response (boondoggle) [1.0* 2.0* 5.5* 11.0* 18.0 24.0 36.0 54.0 Mbit] CH: 11
14:55:35.654382 Probe Response (boondoggle) [1.0* 2.0* 5.5* 11.0* 18.0 24.0 36.0 54.0 Mbit] CH: 11

3) Compromising the target.

The suggested wlan2eth utility can be used to convert 'bbb.dump' to a
packet capture that contains Ethernet types.  The converted packet
capture can then be analyzed using any number of network utilities.

For example, running the capture through snort [1] reveals a couple
items of interest:

 - an NMAP scan of 172.16.0.99 from 172.16.0.6
 - a potential SSH scan of 172.16.0.99 from 172.16.0.6

The argus utilities [2] can be used to summarize the TCP exchanges
between 172.16.0.6 and 172.16.0.99.  The majority of TCP sessions have
very few bytes exchanged.  This is consistent with both an NMAP scan and
SSH brute-force attempts.

However, there is one TCP session from 172.16.0.6:44388 to
172.16.0.99:22 that involves a large number of packets and bytes.
The contents of this TCP connection can be retrieved from the packet
capture by using the tcpflow utility [3].

A BPF can be used with tcpflow to selectively extract the desired
packets:

 '(src port 44388 and dst port 22) or (dst port 44388 and src port 22)'

This results in two stream files:

 172.016.000.099.00022-172.016.000.006.44388
 172.016.000.006.44388-172.016.000.099.00022

Based on the network diagram, the 172.16.0.99 host is an Ubuntu 7.10
machine which could likely be running a SSH server vulnerable to the
Debian OpenSSL predictable PRNG vulnerability [4].  If so, it may be
possible to recover the contents of the SSH exchange by brute forcing
the DH key values.  To accomplish this task, the 'ssh_kex_keygen' [5]
and 'ssh_decoder.rb' [6] can be used.

Running these tools against the extracted stream files is successful:

 * read handshake
cipher: aes128-cbc, mac: hmac-md5, kex_hash: sha256, compr: none
 * bruteforce DH
DH shared secret : 480a7ee6d32d09fb5a6a931ba0fca33e5adf3b853f3e1f18a49f555c2f693a47c08f5a1aa39c25a66ec3962cd0d2e0d0dd43ef72a62f6f6ed15c731698c0c0171bfd80bc
c53ea2c0d7eefbd0a035ac6afb468a359159a4a4970e602b7710ca599173157709d28ed7649764ac87dc5fc2f90f9f67092a89b7ac04f1bd640ad619
 * derive keys
 * decipher streams
 * successful authentication packet
{:key=>
  {:type=>"ssh-dss",
   :p=>
    "\000\345\353k\271\036j\020\203o\311U\355\335*\261\330zE\337\354\326\002\255$\371\372\246r\252s\202\277\226\370\236m\347\300\257V\315\242\373b\0004\243
\204\260 \t[U\360\254\0211\220&\036\216%\320x9\262o\177~\3523;\225fA\a\021\vZl\315\323\317A\203\304\250\321\365\212\374<\253/v$O0@\351Z\230\307p\262\245\25
1P\031bn\371\263H\b\260\256\204)\024]\002\251R\177~Ui",
   :q=>"\000\3556\300aXn\221\305\244\024\316J\201q\374\277\226`=\317",
   :y=>
    "v\020\206\271s,\t\235k\031\t\210C<\205$9\354@\027^\26453\003\032\3217\333\330\b?\2159\302\245\227XnBQd\217\277N\270\215\263\037/\002\202\226\357\f:\33
5\\\333\351^\264(\032\267$lt6\365oQ\324\203;p\212?\337\316WB\353\303\250\203Qf\036c\310\314,%\236\v\317t\024'\237'j\237l\304\"j\330cB\030CAr\276d\304?\270\
177L\226\203\t\333]\366",
   :g=>
    "\000\273\233\031+m\313\r\337\361\e\212\314=\206/\245r\r\346z/z\2160\022\261\002\302\260^\337-\034\r\021v\e\276\250Q\300*\250\243\217\320o\023\333\000\
202\374+\374\373\220Q\bO\346\225\272\330\253\374\374\310\253&\355\f\203\321s\026Q\312)\223\306\314\275\224\336\264y\305\262]\035\226\203I\333]\206Y~l\360\2
66\366t\204\333b\\\367!\313\252\177\327T\231\331m,\247M\244\273u\0270\001\313\310"},
 :testic=>1,
 :username=>"mphillips",
 :keytype=>"ssh-dss",
 :nextservice=>"ssh-connection",
 :auth_method=>"publickey"}
 * deciphered streams saved to "sshdecrypt.0.client.dat" & "sshdecrypt.0.server.dat"

Examining the contents of the decrypted files gives some indication
that Peter was able to successfully access the 172.16.0.99 machine
using the 'mphillips' account over SSH.  This was most likely
accomplished by using one of the OpenSSH "toys" from Metasploit [7] to
exploit the PRNG weakness.

Bonus) Completing the compromise.

Once Peter has access to the 'mphillips' account, he does some poking
around.  In the process of doing this, he discovers that there is PHP
file that is run at login that displays who is in the office.  Using
the username and password in the PHP file, he attempts to directly
access to the Human Resources MySQL database.  That attempt fails
because the 'mysql' client is not installed.

Instead, the following PHP file (.myclient.php) is uploaded and used:

<?

$hostname = "10.10.10.10";
$username = "root";
$password = "Ph1ll1psD3s1gn";
$dbname = "payroll";

$link = mysql_connect($hostname, $username, $password)
        or die ('Could not connect to MySQL server: ' . mysql_error());

while(true) {
        print "Cmd: ";
        $line = trim(fgets(STDIN));

        $result = mysql_query($line);
        if (!$result) {
                print "ERROR: " . mysql_error() . "\n";
        }
        if ($result != 1) {
                while($row = mysql_fetch_row($result)) {
                        print "\t";
                        foreach($row as $val)
                                print "$val ";
                        print "\n";
                }
        }
}

?>

As a result, Peter is able to work around the missing mysql client by
using the existing MySQL client functionality in PHP.

Once this PHP script is run, he executes the following commands:

 show databases;
 use payroll;
 show tables;
 desc employee;
 select empid, firstname, lastname from employee;
 desc employeetype;
 select * from employeetype;
 select empid, firstname, lastname, typeid from employee;
 desc salary;
 select * from salary where empid = 9;
 update salary set empid='185000' where empid=9;
 select * from salary where empid = 9;
 select * from salary;
 update salary set empid=9, baseyear='185000' where empid='185000';
 select * from salary where empid = 9;
 commit;

Peter narrowly avoids catastrophe.  He almost removed Mike from the
'salary' table due to a simple typo.  He updated Mike's empid value to
'185000' instead of setting the baseyear value.  If Peter hadn't
caught this mistake, it is most likely that Mike wouldn't have
received his next paycheck and would have to sell the house for sure.

REFERENCES

[1] http://www.snort.org/
[2] http://qosient.com/argus/
[3] http://www.circlemud.org/~jelson/software/tcpflow/
[4] http://osvdb.org/show/osvdb/45029
[5] http://www.cr0.org/progs/sshfun/
[6] http://www.willhackforsushi.com/code/ssh_decoder.rb
[7] http://www.metasploit.com/users/hdm/tools/debian-openssl/

The ssh_decoder.rb script was modified to include the following
content at the end of the script.  This addition attempts to reprocess
the packet streams into readable content.  The server stream was
successfully reconstructed into a state that was extremely readable.
The client stream was not.  This may be due to the problems with
decrypting the client stream or the over-simplified method of
rebuilding the terminal session.

---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---

class StreamCleaner
  # clean the stream by removing ctrl and terminal escapes
  def initialize
    @ctrl_prefix=["^"[0],0,0,0,0,0,0].pack("ccccccc")
    @term_escape=/\e[\]\[0-9;]*[a-zA-Z]/
    @normalize_crlf=/\r\n|\r(?!\n)|\n\r/
    @buffer = ""
  end
  def clean(fd, p)
    if (94 == p.type || 'CHANNEL_DATA' == p.type) and p.payload[0,7] == @ctrl_prefix
      content=p.payload[9..-1]
      content.gsub!(@term_escape, '')
      content.gsub!(@normalize_crlf,"\n")
      content.length.times {|i|
        if "\b" == content[i].chr
          @buffer = @buffer[0..-2]
        else
          @buffer << content[i]
        end
      }
      if @buffer.index("\n")
        fd.write @buffer
        @buffer = ""
      end
    else
      # fd.write "\n[#{p.type}]->[#{p.payload}]\n"
    end
  end
  def flush(fd)
    fd.write @buffer
    @buffer = ""
  end
end

stream_cleaner = StreamCleaner.new
cfile = "client-cleaned-#{i}.txt"
sfile = "server-cleaned-#{i}.txt"
File.open(cfile, 'wb') { |fd|
  cs.packets.each { |p| stream_cleaner.clean(fd, p) }
  stream_cleaner.flush(fd)
}
File.open(sfile, 'wb') { |fd|
  ss.packets.each { |p| stream_cleaner.clean(fd, p) }
  stream_cleaner.flush(fd)
}

puts " * cleaned streams saved to #{cfile.inspect} & #{sfile.inspect}"

---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---8<---

Although 'more' or 'less' could be used to read to decoded SSH traffic, I was able to modify the 'ssh_decoder.rb' script to interpret the terminal session commands. This made it easier to analyze manually.