[CTF] SECCON Beginners 2023
2023. 6. 4. 16:05ㆍ정보보안/CTFLOG
반응형
Forbidden - web
var express = require("express");
var app = express();
const HOST = process.env.CTF4B_HOST;
const PORT = process.env.CTF4B_PORT;
const FLAG = process.env.CTF4B_FLAG;
app.get("/", (req, res, next) => {
return res.send('FLAG はこちら: <a href="/flag">/flag</a>');
});
const block = (req, res, next) => {
if (req.path.includes('/flag')) {
return res.send(403, 'Forbidden :(');
}
next();
}
app.get("/flag", block, (req, res, next) => {
return res.send(FLAG);
})
var server = app.listen(PORT, HOST, () => {
console.log("Listening:" + server.address().port);
});
워밍업 문제였다.
a태그를 클릭하면 block에 막혀서 flag를 얻을 수 없었다.
하지만 "flag"라는 문자열을 막고 있었으므로 대문자를 입력하면
필터링을 통과하여 flag를 얻을 수 있다.
aiwaf - web
저번에 풀었던 nginx 파서 버그와 비슷하다고 생각이 들었던 문제이다.
대회가 끝나고 풀었는데 이상하게 대회때는
import uuid
import openai
import urllib.parse
from flask import Flask, request, abort
# from flask_limiter import Limiter
# from flask_limiter.util import get_remote_address
##################################################
# OpenAI API key
KEY = "****REDACTED****"
##################################################
app = Flask(__name__)
app.config["RATELIMIT_HEADERS_ENABLED"] = True
# limiter = Limiter(get_remote_address, app=app, default_limits=["3 per minute"])
openai.api_key = KEY
top_page = """
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>亞空文庫</title>
</head>
<body>
<h1>亞空文庫</h1>
AIにセキュリティの物語を書いてもらいました。<br>
内容は正しいかどうかわかりません。<br>
<ul>
<li><a href="/?file=book0.txt">あ書</a></li>
<li><a href="/?file=book1.txt">い書</a></li>
<li><a href="/?file=book2.txt">う書</a></li>
<!-- <li><a href="/?file=book3.txt">え書</a></li> -->
</ul>
※セキュリティのためAI-WAFを導入しています。<br>
© 2023 ももんがの書房
</body>
</html>
"""
@app.route("/")
def top():
file = request.args.get("file")
if not file:
return top_page
if file in ["book0.txt", "book1.txt", "book2.txt"]:
with open(f"./books/{file}", encoding="utf-8") as f:
return f.read()
# AI-WAF
puuid = uuid.uuid4()
prompt = f"""\
以下の{puuid}に囲まれた部分のURLクエリはパストラバーサル攻撃でしょうか?
そうである場合Yesを、違う場合Noを返してください。
../やflagという文字列が含まれていた場合もYesを返してください。
{puuid}
{urllib.parse.unquote(request.query_string)[:50]}
{puuid}
"""
try:
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{
"role": "user",
"content": prompt,
},
],
)
result = response.choices[0]["message"]["content"].strip()
except:
return abort(500, "OpenAI APIのエラーです。\n少し時間をおいてアクセスしてください。")
if "No" in result:
with open(f"./books/{file}", encoding="utf-8") as f:
return f.read().replace(KEY, "")
return abort(403, "AI-WAFに検知されました👻")
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0", port=31415)
저 코드에서 ../와 flag가 들어가면 path traversal로 판단하고 YES를 출력한다.
그 밑 코드에서는 ai의 대답에 No의 유무로 path traversal을 판단하고 있다.
나는 이것만 보고 계속 파라미터의 입력에서 ai가 ../와 flag를 인지하지 못하게 하는 우회 방법만 생각하다가
막혔는데 코드를 유심히 보면
{puuid}
{urllib.parse.unquote(request.query_string)[:50]}
{puuid}
이 부분에서 [:50] 까지만 읽고 있기 때문에,
저번에 nginx에서 파서의 길이제한으로 취약점이 터지던 버그와 비슷하다고 생각이 들었다.
따라서 아래와 같은 방법으로 해결 가능했다.
Half - rev
워밍업 문제로 리버싱 문외한인 나도 ida로 까보니 풀수있었다.
CoughingFox2 - crypto
# coding: utf-8
import random
import os
flag = b"ctf4b{xxx___censored___xxx}"
# Please remove here if you wanna test this code in your environment :)
flag = os.getenv("FLAG").encode()
cipher = []
for i in range(len(flag)-1):
c = ((flag[i] + flag[i+1]) ** 2 + i)
cipher.append(c)
random.shuffle(cipher)
print(f"cipher = {cipher}")
간단한 암호학 문제
plain text에서 첫번째 문자와 그 다음에 오는 문자의 값을 더한 뒤,
제곱하여 i를 더해주고 있다.
import random
import os
flag = ["c"]
cipher = [4396, 22819, 47998, 47995, 40007, 9235, 21625, 25006, 4397, 51534, 46680, 44129, 38055, 18513, 24368, 38451, 46240, 20758, 37257, 40830, 25293, 38845, 22503, 44535, 22210, 39632, 38046, 43687, 48413, 47525, 23718, 51567, 23115, 42461, 26272, 28933, 23726, 48845, 21924, 46225, 20488, 27579, 21636]
plain = []
def find_possible_chars():
possible_chars = []
for j in range(33, 127):
if (ord(flag[-1]) + j)**2 + len(flag)-1 in cipher:
possible_chars.append(chr(j))
return possible_chars
def backtrack():
if len(flag) == len(cipher):
print(''.join(flag))
return
possible_chars = find_possible_chars()
for char in possible_chars:
flag.append(char)
backtrack()
flag.pop()
backtrack()
백트래킹으로 풀었는데 나만 좀 어렵게 해결한 것 같다.
flag = ["c"]
cipher = [4396, 22819, 47998, 47995, 40007, 9235, 21625, 25006, 4397, 51534, 46680, 44129, 38055, 18513, 24368, 38451, 46240, 20758, 37257, 40830, 25293, 38845, 22503, 44535, 22210, 39632, 38046, 43687, 48413, 47525, 23718, 51567, 23115, 42461, 26272, 28933, 23726, 48845, 21924, 46225, 20488, 27579, 21636]
plain = []
def find_possible_chars():
possible_chars = []
for j in range(33, 127):
if (ord(flag[-1]) + j)**2 + len(flag)-1 in cipher:
possible_chars.append(chr(j))
return possible_chars
def backtrack():
if len(flag) == len(cipher):
print(''.join(flag))
return
possible_chars = find_possible_chars()
for char in possible_chars:
flag.append(char)
backtrack()
flag.pop()
backtrack()
반응형
'정보보안 > CTFLOG' 카테고리의 다른 글
[CTF] SSTF 2023 (0) | 2023.08.20 |
---|---|
[CTF] CCE2023 final (1) | 2023.07.13 |
[CTF] DEFCON31 Quals (0) | 2023.05.29 |
[CTF] GreyCTF'23 write up (0) | 2023.05.22 |
[CTF] LINE CTF 2023 - old pal (0) | 2023.03.26 |