niteCTF-2023-Image-Gallery-wirteup

考点

SQL注入绕过,js反混淆

解题

题目给了源代码如下
main.py

from flask import Flask, render_template, request, redirect, url_for, session
import sqlite3
import uuid

app = Flask(__name__)
app.secret_key = str(uuid.uuid4())


@app.route("/login", methods=["POST", "GET"])
def login():
    if "logged_in" in session and session["logged_in"]:
        session.pop("logged_in", None)
        return redirect(url_for("login"))

    if request.method == "POST":
        username = request.form["username"]
        password = request.form["password"]
        loweruser = username.lower()
        lowerpass = password.lower()
        invalid_entries = invalid_entries = [
            "=",
            "<",
            ">",
            "+",
            "//",
            "|",
            ";",
            " ",
            " ",
            "'1",
            " 1",
            " true",
            "'true",
            " or",
            "'or",
            "/or",
            " and",
            "'and",
            "/and",
            "'like",
            " like",
            "/like",
            "'where",
            " where",
            "/where",
            "%00",
            "null",
            "admin'",
        ]
        matching_value = next(
            (
                value
                for value in invalid_entries
                if value in loweruser or value in lowerpass
            ),
            None,
        )
        if matching_value:
            error = (
                f"Invalid entry in username and/or password fields. Please try again."
            )
            return render_template("login.html", error=error)

        conn = sqlite3.connect("chal.db")
        cursor = conn.cursor()

        query = f"SELECT secret FROM login_details WHERE username = '{username}' AND password = '{password}';"

        result = cursor.execute(query)
        user = result.fetchone()

        conn.close()

        if user:
            session["logged_in"] = True
            session["username"] = username
            session["secret"] = user[0]
            return redirect(url_for("profile"))
        else:
            error = "Invalid login credentials. Please try again."
            return render_template("login.html", error=error)

    return render_template("login.html")


@app.route("/")
def index():
    if "logged_in" in session and session["logged_in"]:
        session.pop("logged_in", None)
        return redirect(url_for("index"))
    return render_template("landing.html")


@app.route("/profile")
def profile():
    if "logged_in" in session and session["logged_in"]:
        username = session["username"]
        secret = session.get("secret")
        key = "0"
        if "secret" not in str(secret):
            key = "1"
        return render_template(
            "profile.html", username=username, secret=secret, key=key
        )
    else:
        error = "You are not logged in. Please log in first."
        return render_template("login.html", error=error)


@app.route("/logout")
def logout():
    session.pop("logged_in", None)
    return render_template("login.html")


@app.after_request
def add_cache_control(response):
    response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0"
    response.headers["Pragma"] = "no-cache"
    response.headers["Expires"] = "-1"
    return response


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=1337)

通过对代码进行审计可以看到再login路由下存在SQL注入,也就是登录处,但是对字符进行了白名单限制

@app.route("/login", methods=["POST", "GET"])
def login():
    if "logged_in" in session and session["logged_in"]:
        session.pop("logged_in", None)
        return redirect(url_for("login"))

    if request.method == "POST":
        username = request.form["username"]
        password = request.form["password"]
        loweruser = username.lower()
        lowerpass = password.lower()
        invalid_entries = invalid_entries = [
            "=", "<", ">", "+", "//", "|", ";", " ", " ", "'1", " 1", " true", "'true", " or", "'or", "/or", " and", "'and", "/and", "'like", " like", "/like", "'where", " where", "/where", "%00", "null", "admin'",
        ]

尝试使用tab键来绕过空格(space)
123
发现能够成功登录
123
也没有发现什么,结合源码还给了一个SQL文件

schema_sql

CREATE TABLE login_details (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    username TEXT NOT NULL,
    password TEXT NOT NULL
, secret TEXT);

尝试查看一下secret字段
465
这时候发现session内容变多了,结合前面登陆成功所给的secret,不免会想到session相关题目,将此处的session进行解码使用flask-unsign
456
将得到的密文进行JWT解码
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmbGFnIjoibml0ZXtpc190aGlzX3RoZV9mbGFnP30iLCJuYW1lIjoiVHJ5IGFuZCBnZXQgdGhlIGZsYWchIiwiZGVzYyI6IihmdW5jdGlvbihfMHhkOGZiZGIsXzB4MjQyNDgzKXt2YXIgXzB4NTVkYzdjPV8weDNlN2QsXzB4M2FlZmExPV8weGQ4ZmJkYigpO3doaWxlKCEhW10pe3RyeXt2YXIgXzB4M2Q3ODQ3PS1wYXJzZUludChfMHg1NWRjN2MoMHg5MikpLzB4MSoocGFyc2VJbnQoXzB4NTVkYzdjKDB4OTYpKS8weDIpK3BhcnNlSW50KF8weDU1ZGM3YygweDhlKSkvMHgzKigtcGFyc2VJbnQoXzB4NTVkYzdjKDB4OTMpKS8weDQpK3BhcnNlSW50KF8weDU1ZGM3YygweDk0KSkvMHg1K3BhcnNlSW50KF8weDU1ZGM3YygweDhmKSkvMHg2KigtcGFyc2VJbnQoXzB4NTVkYzdjKDB4OGIpKS8weDcpK3BhcnNlSW50KF8weDU1ZGM3YygweDkxKSkvMHg4Ky1wYXJzZUludChfMHg1NWRjN2MoMHg4YykpLzB4OSoocGFyc2VJbnQoXzB4NTVkYzdjKDB4OTUpKS8weGEpK3BhcnNlSW50KF8weDU1ZGM3YygweDkwKSkvMHhiO2lmKF8weDNkNzg0Nz09PV8weDI0MjQ4MylicmVhaztlbHNlIF8weDNhZWZhMVsncHVzaCddKF8weDNhZWZhMVsnc2hpZnQnXSgpKTt9Y2F0Y2goXzB4NGIyODljKXtfMHgzYWVmYTFbJ3B1c2gnXShfMHgzYWVmYTFbJ3NoaWZ0J10oKSk7fX19KF8weDQ0ZTcsMHhiNGJmMSkpO2Z1bmN0aW9uIF8weDNlN2QoXzB4M2JjYTMzLF8weDIxZjY0OCl7dmFyIF8weDQ0ZTc3Yz1fMHg0NGU3KCk7cmV0dXJuIF8weDNlN2Q9ZnVuY3Rpb24oXzB4M2U3ZGU2LF8weDMxZmViYyl7XzB4M2U3ZGU2PV8weDNlN2RlNi0weDhiO3ZhciBfMHg1ZTU4NTA9XzB4NDRlNzdjW18weDNlN2RlNl07cmV0dXJuIF8weDVlNTg1MDt9LF8weDNlN2QoXzB4M2JjYTMzLF8weDIxZjY0OCk7fWZ1bmN0aW9uIHd1dF9pc190aGlzX25vdygpe3ZhciBfMHg1OTQ5NTY9XzB4M2U3ZDtjb25zb2xlWydsb2cnXShfMHg1OTQ5NTYoMHg4ZCkpO31mdW5jdGlvbiBfMHg0NGU3KCl7dmFyIF8weDQ1ZDA4ZT1bJzlsbENnTm8nLCdhSFIwY0hNNkx5OW5hWFJvZFdJdVkyOXRMMmx6YUdGdUxYTjFjbUZ1WVM5amFHRnNiR1Z1WjJVdicsJzkyNG9mb3BKQicsJzE1NzA5OHR0REd2YScsJzI0MjU3Mjc3YW1kc2RZJywnNDY1NzY4MFdhaWNKRCcsJzh4SHRxc2QnLCcxNjI1Mld6cWZjcycsJzExOTg3OTVlVUFmSUQnLCc0MDI1MTEwR1hveWJNJywnNzk2ODJRU254c1MnLCc4NHhCSnNzdyddO18weDQ0ZTc9ZnVuY3Rpb24oKXtyZXR1cm4gXzB4NDVkMDhlO307cmV0dXJuIF8weDQ0ZTcoKTt9IiwiaWF0IjoxNTE2MjM5MDIyfQ.bTHY_Rrkc2mn2xwJe1tP_PeGUftqNoWZFLO2yBH2B5Q

{
  "header": {
    "alg": "HS256",
    "typ": "JWT"
  },
  "payload": {
    "flag": "nite{is_this_the_flag?}",
    "name": "Try and get the flag!",
    "desc": "(function(_0xd8fbdb,_0x242483){var _0x55dc7c=_0x3e7d,_0x3aefa1=_0xd8fbdb();while(!![]){try{var _0x3d7847=-parseInt(_0x55dc7c(0x92))/0x1*(parseInt(_0x55dc7c(0x96))/0x2)+parseInt(_0x55dc7c(0x8e))/0x3*(-parseInt(_0x55dc7c(0x93))/0x4)+parseInt(_0x55dc7c(0x94))/0x5+parseInt(_0x55dc7c(0x8f))/0x6*(-parseInt(_0x55dc7c(0x8b))/0x7)+parseInt(_0x55dc7c(0x91))/0x8+-parseInt(_0x55dc7c(0x8c))/0x9*(parseInt(_0x55dc7c(0x95))/0xa)+parseInt(_0x55dc7c(0x90))/0xb;if(_0x3d7847===_0x242483)break;else _0x3aefa1['push'](_0x3aefa1['shift']());}catch(_0x4b289c){_0x3aefa1['push'](_0x3aefa1['shift']());}}}(_0x44e7,0xb4bf1));function _0x3e7d(_0x3bca33,_0x21f648){var _0x44e77c=_0x44e7();return _0x3e7d=function(_0x3e7de6,_0x31febc){_0x3e7de6=_0x3e7de6-0x8b;var _0x5e5850=_0x44e77c[_0x3e7de6];return _0x5e5850;},_0x3e7d(_0x3bca33,_0x21f648);}function wut_is_this_now(){var _0x594956=_0x3e7d;console['log'](_0x594956(0x8d));}function _0x44e7(){var _0x45d08e=['9llCgNo','aHR0cHM6Ly9naXRodWIuY29tL2lzaGFuLXN1cmFuYS9jaGFsbGVuZ2Uv','924ofopJB','157098ttDGva','24257277amdsdY','4657680WaicJD','8xHtqsd','16252Wzqfcs','1198795eUAfID','4025110GXoybM','79682QSnxsS','84xBJssw'];_0x44e7=function(){return _0x45d08e;};return _0x44e7();}",
    "iat": 1516239022
  }
}

可看出desc处对代码进行了混淆,尝试反混淆

(function(topic, b) {
  /** @type {function (number, ?): ?} */
  var getter = _0x3e7d;
  var out = topic();
  for (;!![];) {
    try {
      /** @type {number} */
      var a = -parseInt(getter(146)) / 1 * (parseInt(getter(150)) / 2) + parseInt(getter(142)) / 3 * (-parseInt(getter(147)) / 4) + parseInt(getter(148)) / 5 + parseInt(getter(143)) / 6 * (-parseInt(getter(139)) / 7) + parseInt(getter(145)) / 8 + -parseInt(getter(140)) / 9 * (parseInt(getter(149)) / 10) + parseInt(getter(144)) / 11;
      if (a === b) {
        break;
      } else {
        out["push"](out["shift"]());
      }
    } catch (_0x4b289c) {
      out["push"](out["shift"]());
    }
  }
})(_0x44e7, 740337);
/**
 * @param {number} opt_attributes
 * @param {?} deepDataAndEvents
 * @return {?}
 */
function _0x3e7d(opt_attributes, deepDataAndEvents) {
  var args = _0x44e7();
  return _0x3e7d = function(opt_attributes, deepDataAndEvents) {
    /** @type {number} */
    opt_attributes = opt_attributes - 139;
    var pageY = args[opt_attributes];
    return pageY;
  }, _0x3e7d(opt_attributes, deepDataAndEvents);
}
/**
 * @return {undefined}
 */
function wut_is_this_now() {
  /** @type {function (number, ?): ?} */
  var prepArgs = _0x3e7d;
  console["log"](prepArgs(141));
}
/**
 * @return {?}
 */
function _0x44e7() {
  /** @type {Array} */
  var aHR0cHM6Ly9naXRodWIuY29tL2lzaGFuLXN1cmFuYS9jaGFsbGVuZ2Uv = ["9llCgNo", "aHR0cHM6Ly9naXRodWIuY29tL2lzaGFuLXN1cmFuYS9jaGFsbGVuZ2Uv", "924ofopJB", "157098ttDGva", "24257277amdsdY", "4657680WaicJD", "8xHtqsd", "16252Wzqfcs", "1198795eUAfID", "4025110GXoybM", "79682QSnxsS", "84xBJssw"];
  /**
   * @return {?}
   */
  _0x44e7 = function() {
    return aHR0cHM6Ly9naXRodWIuY29tL2lzaGFuLXN1cmFuYS9jaGFsbGVuZ2Uv;//base64解码为https://github.com/ishan-surana/challenge/
  };
  return _0x44e7();
}
;

flag就在里面,但是由于比赛结束,flag已经删除。。。

js混淆与反混淆

JS混淆

JavaScript混淆是一种通过修改源代码,使其难以阅读和理解的技术。这种技术的目的是增加代码的复杂性,从而防止逆向工程、减缓恶意攻击和防止未经授权的复制。混淆通常包括以下几个方面:

  1. 变量和函数名重命名: 将代码中的变量名和函数名替换为无意义、短小或难以理解的名称。
  2. 控制流混淆: 修改代码的控制流程,使其变得复杂和难以分析。这可能包括添加无用的分支、条件语句重组等。
  3. 字符串和常量加密: 将字符串和常量进行加密或编码,使其在运行时动态解码,增加代码分析的难度。
  4. 添加噪音和无用代码: 在代码中添加无用的语句、注释或函数,使其更加混乱,增加理解的难度。

JavaScript反混淆

JavaScript反混淆是一种尝试还原混淆代码的过程,以便更容易地理解和分析代码。反混淆的目标是还原代码的可读性,以便于审查、维护或逆向工程。反混淆通常包括以下一些方法:

  1. 还原变量和函数名: 将混淆过的变量名和函数名还原为更有意义的名称,以提高代码的可读性。
  2. 简化控制流: 通过移除无用的分支或还原原始的控制流程,使代码更易于理解。
  3. 解密字符串和常量: 解密加密的字符串和常量,还原它们的原始值。
  4. 去除噪音和无用代码: 删除添加的无用语句、注释或函数,以简化代码。
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

请我喝杯咖啡吧~

支付宝
微信