Ghede's hackme
My hackme is now no longer online. I had over 200MB of pcap files files with
over 500 IP addresses for what I had planned to publish a report,
something that has been on my todo list for way to long. It's a tedious
job to do by hand, so I've removed it from my todo list for now.
I've anonomized the pcap files and will give them to anyone who is
interested.
For those of you that participated in the hackme challenge here follows a description of the setup,
I will publish the interesting parts of the successful and pathetic unsuccessful attempts here shortly.
Goal
The goal of this hackme was to provide a challenge for people that are interested in 'real' hacking,
what in my definition means people that know how to read code, write and secure code and have some
additional system and security knowledge.
The way to accomplish this, and to keep off the 'exploit collector' type of dumb-ass hackers I first had
to set up a completely secure environment with no vulnerabilities that would be entries to the exploit
collectors.
In order not to scare off junior hackers, and in order for the challenge to be also worthy for the
hacking gods I tried to make the hackme consist of a chain of holes that were supposed to be getting
more difficult with each step (or at least should request an additional skill from the attacker.
I divided the hackme challenge up into 4 different challenges meant for different (cumulative) skill levels:
- Step 1: Junior Hacker
- Step 2: Senior Hacker/Junior Specialist
- Step 3: Hacking Guru/Senior Specialist
- Step 4: Hacking god/B.O.F.H.'s worst nightmare
As it later turned out the 4th challenge was taken in almost no time by most people who
got trough step 3, and some of those people tolled me it was the easiest step, so I kinda
got the difficulty of the 4th step wrong.
Secure environment
In order to set up the environment for the hackme there had to be a way in trough the internet,
using a webserver seemed like a good choice as many security problems are with webservers
and putting in a bit of pseudo-reality seemed like a good idea.
Next to a way in, there had to be a way out of the secure environment, a place to punch some
holes in, and the tools for the contenders to open these holes wide up. For these reasons I set up
a chrooted environment with the following software installed in it:
- A bare-bones apache webserver.
- sudo
- scripting languages
- A bare-bones perl
- tcl/expect
- pike
Finally in an attempt to prevent people from not using the tools provided and compiling their
own I put the hackme on an old sun solaris box, I later did find some binaries in the
pcap files, but mostly it has been effective a.f.a.i.c.s.
First challenge
The first challenge was the 'exploit collector filter', it exist of a set of 2 cgi scripts
from what the second one had a hole so big that I was surprised at how many people that I assumed
until now to be reasonably competent did not get trough.
The hole actually existed out of 3 different security hazards:
- Insecure parameter passing between scripts
- Direct access to the data area
- Execute access to files in the data area
The first script generated html output containing a line like:
<IMG SRC="logit.pl?hour=2001_05_27_21&line=993672416:192.168.1.3:Mozilla/4.76%20[en]%20(X11;%20U;%20Linux%202.2.17-21mdksecure%20i686)">
This html made a image be loaded that was generated by the script 'logit.pl' . This script also used the info being
fed by the first script by doing some simple decoding.
As you can see the parameters are divided in a 'hour' and a 'line' parameter. The hour actually was also the filename used for
the logfile that the script logit.pl was writing to. The 'line' parameter was an 'url encoded' line that was decoded by the
script and than written to the log file.
Next to writing to a definable file the file that was written to actually had
its execute bits all set to one, thus the file would be executable if the webserver allowed it.
The url encoding is a quite simple encoding, basically any non alphanumeric character is replaced by its hex-code preceded by the
'%' sign.
So it was possible to 'log' an executable file, the next question was what to log. The answer was a 'perl' or 'tcl' script,
both perl and tcl were installed in the /usr/local/bin/, so the way to get trough the first challenge was basically by
logging a perl of tcl script.
Second challenge
The first challenge weeded out all the lame-brains (and some of the people with just to little time, I've seen some people
getting real close with #!/usr/bin/perl instead of the needed #!/usr/local/bin/perl), now the second challenge was there
to see if you were in the big-league.
The second challenge existed out of a broken sgid binary with its source available, and a renamed parser.
The program run_parser had the following source that had to be interpreted (I've made the important parts Bold:
/* run_parser.c */
/* This program spawns a logfile parser after doing some basic checks */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
int main(int argc, char *argv[])
{
char logfilename[256];
char confline[4096];
char parserfilename[256];
char command[768];
char buf[1025];
char hd[256];
char hcf[512];
struct stat parser_stat;
int i,c;
FILE *conf,*process1;
if (argc != 2)
{
printf("usage: %s <logfile>\n\n",argv[0]);
exit(0);
}
sprintf(hcf,"%s/.parser_config",getenv("HOME"));
strncpy(logfilename,argv[1],255);
if ((conf=fopen(hcf,"r"))||(conf=fopen("/challenge2/etc/parser_config","r")))
{
printf("Reading conf\n");
while ((fscanf(conf,"%[a-zA-Z0-9/. ]\n",confline)>0))
{
printf("%s\n",confline);
if (strlen(confline) < 256)
{
if (!(sscanf(confline,"parser %s",parserfilename)))
{
parserfilename[0]=0;
}
}
}
fclose(conf);
printf("Checking for parser\n");
if (parserfilename[0]==0)
{
printf("Error: no parser configured\n");
return(1);
}
printf("getting file stats for parser %s\n",parserfilename);
if (stat(parserfilename,&parser_stat)==0)
{
printf("checking staff\n");
if (parser_stat.st_gid == 10)
{
sprintf(command,"%s %s",parserfilename,logfilename);
printf("spawning %s\n",command);
fflush(stdout);
execl(parserfilename, parserfilename, logfilename, (char *)0);
fflush(stdout);
return(0);
}
else
{
printf("No you don't ;-)\n");
return(1);
}
}
else
{
printf("Error: can't stat parser %s\n",parserfilename);
return(1);
}
}
else
{
printf("ERR\n");
return(1);
}
}
The way to get truegh this was basically simple, the attacker had to create a config file with the
name .parser_config in a directory writable to him, and next had to put a entry parser
in this file to point to a parser. However this parser would have to have a groupid 10 of the
staff group. With this config file the attacker could call the run_parser program when the environment
variable HOME was set to the attackers home directory.
The finding of a suitable parser was added so that both system analysis skills and code reading skills
would be needed, further the kinda exotic parser was chosen to force the attacker to do some background research.
The directory /challenge2/tmp contained the parser, this parser was actually a pike binary renamed to ps.
Summarizing the challenge could be taken in the following way:
- Analyze the source file.
- Identify the /challenge2/tmp/ps file as being a pike interpreter and creating a pike script to gain access to the 3th
challenge
- Creating a .parser_config file with a parser pointing to /challenge2/tmp/ps
- Calling the run_parser with the HOME environment variable set to the right dir, using the pike script as logfile parameter.
Third challenge
The second challenge was meant to separate the junior hackers from the Senior-Hacker/Junior-Specialist,
after this the th challenge would be there for the Hacking-Guru/Senior-Specialist to proof themselves.
(i>There initial were some minor migration bugs in this setup that I did not test after migrating from the test box
to the hackme box, I would like to thank Dvorak for pointing them out and helping me to fix them).
This challenge consisted out of a sudo configuration that allowed the attacker to run a simple interactive
script as root. The script had a hole in it that would only be accessible after 8 simple multiple choice questions.
The problem however would be that the attacker would not have an interactive access to the system, thus the attacker would either
need to find a way to gain interactive access, or write a script that would be able to fake being an interactive user.
I have tried (and somewhat failed looking at the pcap files that do have interactive sessions in them) to prevent
the way of interactive access to the system, forcing the attacker to bend himself into narrow holes in order to write
a script that could simulate an interactive user. For making this attack possible and not to easy the following action had been
taken:
- Expect had been installed in order to make writing interactive scripts easier
- The pseudo tty devices were not added to the chrooted environment in order to make the use of 'interact' not possible.
The script that had to be analyzed was the following:
#!/usr/local/bin/perl
$|=1;
@belong=("horse,cow,chicken","dog,cat,goldfish","lion,monkey,zebra",
"shark,seal,dolphin","fly,spider,butterfly","vacuum-cleaner,sheep,rubber-doll",
"virus,bacteria,infection","tree,bush,plant","bike,car,bus",
"lake,sea,river","mountain,hill,bump","lamer,gamer,mcse-er",
"food,sex,perl","argv,argc,getenv");
print "This script will allow staff members to edit a configuration script\n";
print "\n\n\n\n\nTo establish you are human, and not a script, you need to answer\n";
print "8 stupid questions that only a human or a real bad-ass script\n";
print "could answer\n\n";
$anoy=int(rand(6))+1;
foreach $x (1 .. 8)
{
if ($x == $anoy)
{
$line="";
print "We interrupt this program to anoy you and make things generally irritating\n";
sleep(10);
}
$rand=int(rand(14-$x));
$question=splice(@belong,$rand,1);
@nobel=(@belong,@old);
$nobel2=$nobel[rand(13)];
@nobel3=split(/\,/,$nobel2);
$nobel4=$nobel3[int(rand(3))];
push(@old,$question);
@nobel5=split(/\,/,$question);
$nobel6[0]=splice(@nobel5,int(rand(3)),1);
$nobel6[1]=splice(@nobel5,int(rand(2)),1);
$nobel6[2]=$nobel5[0];
$answer=int(rand(4));
$alist.=$answer;
splice(@nobel6,$answer,0,$nobel4);
print "Which does not belong?\n";
foreach $index (0 .. 3)
{
$cnt=$index+1;
print " $cnt) $nobel6[$index]\n";
}
print "\n:";
$starttime=time();
$userline=<>;
$speed=time()-$starttime;
if ($speed < 2)
{
print "Piss off stupid script, no human reads+chooses+types that fast\n";
exit;
}
$speed{$speed}++;
if ($speed{$speed} > 6)
{
print "Ok, you're a smart script, but I'm smarter, you're not human, no human reads+chooses+types with such a constant
speed\n"; exit;
}
chomp($userline);
print "Ok I'll do a complex computation to see if you're right\n";
sleep(1);
if ($userline == ($answer+1))
{
print "Briljant\n\n\n";
}
else
{
print "Duh\n";
print "You're a script or a lamer, that is WRONG\n";
exit;
}
}
print "OK, Your human (or a badd-ass script)\n";
open(CONF,"/challenge3/conf/dummy.conf");
$count=0;
while(<CONF>)
{
if (/^(\w+)\s+(\w+)/)
{
$conf[$count]="$1 $2";
$count++;
}
}
close(CONF);
print "What parameter needs changing ?\n";
foreach $count (0 .. $count-1)
{
($p,$v)=split(/\s+/,$conf[$count]);
print " ${count}) $p\n";
}
print ":";
$parnr=<>;
chomp($parnr);
print "New value:";
$nv=<>;
chomp($nv);
$aa=$conf[$parnr];
($p,$v)=split(/\s+/,$conf[$parnr]);
$es="\$aa =~ s/$v/$nv/;1;";
$ok=eval($es);
if ($ok)
{
$conf[$parnr]=$aa;
open(CONF,">/challenge3/conf/dummy.conf");
foreach $count (0 .. $count-1)
{
print CONF "$conf[$count]\n";
}
}
else
{
print "Eval err $ok $!\n";
}
Basically this script would test if you are a human by asking 8 questions like:
Which does not belong?
1) virus
2) bacteria
3) infection
4) zebra
:
That would all have to be answered correctly. To make simulating being human more difficult the script
took the following measures:
- Test if the time to answer was always at least 2 seconds.
- Test if the time to answer was not to constant.
- Interrupt at a random time with one of my favorite Monthy Python lines
After successful fooling the script in believing your script to be human the part of the script
that is shown bold would be accessible, allowing for exploitation of a simple unchecked eval bug.
Unfortunately my closing of the system from interactive sessions proofed insufficient, and some people
came truegh by getting some kind of interactive shell (what is also kinda impressive taking into account
that there were no pty's available, and the platform was Sparc-Solaris, but requires a bit less skill
than the path I designed).
The last challenge
The 4th and last challenge was a challenge that proofed to be not much of a challenge for the people. After completing the 3th
challenge the attacker would be in a chrooted environment as the user root. I was under the impression that having the
chrooted environment on an other file system would make this rather difficult (The only technique I knew to break out of a
chrooted environment only works when the real root is on the same partition), I knew it would be theoretically possible to
break out, but I thought it wasn't widespread knowledge. Anyway it was meant as a last challenge for the people who broke
truegh the first 3 challenges with their coding and analysis skills, and the first challenge that did not need coding skills
but rather needed system knowledge, the last challenge on itself may not have been so hard on itself, but I believe that
having the knowledge for completing this tasks combined with the analysis and coding skills needed for the first 3 tasks
proofs the people who complete all the tasks to be part of the league of Hacking gods.