browser-xterm-interaction
$
npx mdskill add wgpsec/AboutSecurity/browser-xterm-interactionExecute commands in browser web terminals using Playwright
- Interacts with xterm.js and hterm interfaces inside web pages
- Depends on Playwright MCP and browser_snapshot APIs
- Selects input methods by detecting terminal rendering techniques
- Delivers results via command output or fallback screenshots
SKILL.md
.github/skills/browser-xterm-interactionView on GitHub ↗
---
name: browser-xterm-interaction
description: "Playwright Browser MCP 与 xterm.js 终端交互方法论。当需要通过浏览器操作网页内嵌终端、CTF 靶场伪终端、Cloud Shell、在线 IDE 中的终端时使用。覆盖终端内容读取(5 种方法)、命令执行、输出捕获、screenshot 降级策略。只要目标页面中有任何形式的 Web 终端(xterm.js/hterm/jQuery Terminal),就应使用此技能"
metadata:
tags: "browser,playwright,xterm,web-terminal,xtermjs,浏览器终端"
category: "tool"
---
# Playwright Browser MCP × Web 终端交互
Web 终端(xterm.js、hterm 等)在浏览器中渲染终端界面,但其输出通常用 canvas 或自定义 DOM 渲染,标准的 `browser_snapshot()` 只能看到 accessibility tree 中的最后一行 prompt。这是与 Web 终端交互时最大的痛点——需要用特定的 JS 方法才能可靠地读取终端输出。
## 核心原则
1. **一次定型** — 找到有效的读取方法后坚持用它,不要每次都换方法,因为来回切换会浪费大量 turn
2. **3 次法则** — 一种方法最多试 3 次就切换下一种,避免在死路上消耗 turn
3. **screenshot 兜底** — JS 方法全部失败时,截图+视觉分析是 100% 可靠的最终手段
4. **marker 包裹** — 用 `echo "===START==="; cmd; echo "===END==="` 包裹命令输出,方便精确提取
---
## Phase 1: 识别终端类型
先用一次 `browser_snapshot()` 或 `browser_evaluate` 探测终端类型:
```javascript
// 探测终端类型
() => {
const xterm = document.querySelector('.xterm');
const jquery_term = window.jQuery && window.jQuery.fn.terminal;
const hterm = document.querySelector('[id*="hterm"]');
return {
xterm: !!xterm,
jquery_terminal: !!jquery_term,
hterm: !!hterm,
terminal_classes: xterm ? xterm.className : 'not found'
};
}
```
---
## Phase 2: 命令执行(输入)
### 方法 A: 通过 textbox ref 输入(推荐)
```
browser_type(ref='<terminal_input_ref>', text='your command', submit=True)
```
### 方法 B: 通过 keyboard 直接输入
```
browser_click(element='terminal area', ref='<terminal_ref>')
browser_press_key(key='your command text') // 逐字符
browser_press_key(key='Enter')
```
### 方法 C: 通过 evaluate 写入
```javascript
() => {
const textarea = document.querySelector('.xterm-helper-textarea');
if (textarea) {
textarea.focus();
// 通过 InputEvent 模拟输入
}
}
```
**⚠️ 重要**:执行命令后,必须等待足够时间让输出完成:
- 快速命令(echo, cat): `browser_wait_for(time=2)`
- 中速命令(nslookup, curl): `browser_wait_for(time=5)`
- 慢速命令(扫描、编译): `browser_wait_for(time=15-30)`
---
## Phase 2.5: 输出完整性验证
读取终端输出后,**必须检查输出是否完整**:
### 判断不完整的信号
- 配置文件只显示了最后 1-2 行(如 resolv.conf 只有 options 行,缺少 nameserver/search)
- 输出只有提示符,没有任何命令结果
- 输出看起来是中间片段(没有起始行)
### 修复方法
发现输出可能不完整时,**立即用重定向法重试**:
```bash
command > /tmp/verify.txt 2>&1 && cat /tmp/verify.txt
```
不要继续基于不完整输出做决策。
---
## Phase 3: 输出读取(核心难点)
### ⭐ 方法 1: xterm-rows innerText(最可靠)
```javascript
() => {
const rows = document.querySelectorAll('.xterm-rows > div');
let lines = [];
for (let row of rows) {
const text = row.innerText || row.textContent || '';
if (text.trim()) lines.push(text);
}
return lines.join('\n');
}
```
### 方法 2: xterm-screen innerText
```javascript
() => {
const screen = document.querySelector('.xterm-screen');
return screen ? screen.innerText : 'not found';
}
```
### 方法 3: 通过 Terminal API(如果暴露)
```javascript
() => {
// 尝试通过 xterm.js Terminal 实例的 buffer 读取
const term = document.querySelector('.xterm');
if (term && term._core) {
const buffer = term._core.buffer.active;
let lines = [];
for (let i = 0; i < buffer.length; i++) {
const line = buffer.getLine(i);
if (line) lines.push(line.translateToString(true));
}
return lines.join('\n');
}
return 'Terminal API not accessible';
}
```
### 方法 4: Marker 包裹法(精确提取命令输出)
执行命令时用 marker 包裹,便于精确提取:
```bash
echo "===START==="; your_command_here; echo "===END==="
```
然后在 JS 中提取 `===START===` 和 `===END===` 之间的内容。
### 方法 5: 重定向到文件 + cat
```bash
your_command > /tmp/out.txt 2>&1; cat /tmp/out.txt
```
适用于输出很长或滚动导致内容丢失的情况。
---
## Phase 4: 降级策略(JS 方法失败时)
### ⭐ Screenshot + 视觉分析(最终兜底)
当 JS 方法都无法可靠读取终端输出时:
```
browser_take_screenshot(type='png', filename='term_output.png')
Read(file_path='<output_dir>/term_output.png')
```
然后通过图片内容视觉分析终端输出。
**⚠️ 禁止使用 `fullPage=True`** — 全页截图体积极大(>1MB),会导致 SDK JSON buffer 溢出崩溃。只用默认的 viewport 截图。
**优点**:100% 可靠,不依赖 DOM 结构
**缺点**:消耗更多 tokens、只能看到可视区域
**技巧**:
- 在截图前先滚动终端到底部
- 长输出分多次截图
- 用 `fullPage=True` 尝试全页截图
- 配合 marker 法定位关键输出区域
---
## 常见问题与解决
### Q: `browser_snapshot()` 只返回最后一行 prompt
xterm.js 用 canvas 或 DOM renderer 渲染,snapshot 只能看到 accessibility tree。
**解决**: 用 `browser_evaluate` + Phase 3 的 JS 方法。
### Q: `browser_evaluate` 返回空字符串
可能终端用了 canvas renderer,DOM 中没有文本节点。
**解决**: 用 screenshot 兜底。
### Q: 终端输出太长被截断
xterm.js 有 scrollback buffer 限制(默认 1000 行)。
**解决**: 用重定向到文件法,或分段查看。
### Q: 命令执行后看不到输出变化
可能等待时间不够,或终端没有刷新。
**解决**: 增加 `browser_wait_for` 时间,或按 Enter 触发刷新。
---
## 决策流程图
```
开始
↓
识别终端类型 (Phase 1)
↓
执行命令 (Phase 2, 方法 A 优先)
↓
等待输出 (browser_wait_for)
↓
尝试 JS 读取 (Phase 3, 方法 1)
├── 成功 → 锁定此方法,后续复用
├── 失败 → 尝试方法 2
│ ├── 成功 → 锁定
│ ├── 失败 → 尝试方法 3
│ │ ├── 成功 → 锁定
│ │ └── 失败 → Screenshot 兜底 (Phase 4)
└── 3 次后仍不稳定 → Screenshot 兜底
```
**⚠️ 禁止**: 在 JS 读取方法之间反复来回切换超过 3 次。确定一种方法后坚持使用。
---
## CTF / Cloud Shell 专区
### 多行脚本写入最佳实践
Web 终端的输入框通常是**单行**的,直接粘贴多行 Python/Bash 脚本会导致格式错误。
**方法 A: heredoc 写文件(推荐)**
```javascript
// 逐行写入脚本文件
browser_type(ref, "cat > /tmp/exploit.py << 'PYEOF'", submit=True)
browser_wait_for(time=1)
browser_type(ref, "import boto3, json", submit=True)
browser_type(ref, "s3 = boto3.client('s3')", submit=True)
browser_type(ref, "print(s3.list_buckets())", submit=True)
browser_type(ref, "PYEOF", submit=True)
browser_wait_for(time=1)
browser_type(ref, "python3 /tmp/exploit.py", submit=True)
```
**方法 B: base64 编码(最可靠)**
```javascript
// 先在本地构造脚本,base64 编码后一行写入
// 本地 bash:
echo 'import boto3; print(boto3.client("s3").list_buckets())' | base64
// → aW1wb3J0IGJvdG8z...
// 在 Web 终端:
browser_type(ref, "echo 'aW1wb3J0IGJvdG8z...' | base64 -d > /tmp/s.py && python3 /tmp/s.py", submit=True)
```
**方法 C: echo 追加(简单脚本)**
```javascript
browser_type(ref, "echo 'import boto3' > /tmp/s.py", submit=True)
browser_type(ref, "echo 's3=boto3.client(\"s3\")' >> /tmp/s.py", submit=True)
browser_type(ref, "echo 'print(s3.list_buckets())' >> /tmp/s.py", submit=True)
browser_type(ref, "python3 /tmp/s.py", submit=True)
```
**⚠️ 禁止**: 在 `browser_type` 的 text 参数中包含 `\n` 换行符 — Web 终端输入框不支持多行输入。
### 环境变量设置
Web 终端可能限制写入 `~/.aws/credentials`(Permission denied):
```javascript
// 方法 A: export 在同一行(仅当前命令有效)
browser_type(ref, "AWS_ACCESS_KEY_ID=AKIAXXXX AWS_SECRET_ACCESS_KEY=YYYY aws s3 ls", submit=True)
// 方法 B: 在 Python 脚本内设置
browser_type(ref, "cat > /tmp/s.py << 'EOF'", submit=True)
browser_type(ref, "import os, boto3", submit=True)
browser_type(ref, "os.environ['AWS_ACCESS_KEY_ID']='AKIAXXXX'", submit=True)
browser_type(ref, "os.environ['AWS_SECRET_ACCESS_KEY']='YYYY'", submit=True)
browser_type(ref, "s3=boto3.client('s3',region_name='us-east-1')", submit=True)
browser_type(ref, "print(s3.list_buckets())", submit=True)
browser_type(ref, "EOF", submit=True)
browser_type(ref, "python3 /tmp/s.py", submit=True)
```
### 长输出截取
Web 终端输出缓冲区有限,长输出会丢失开头部分:
```javascript
// 使用 marker 包裹 + tail 截取
browser_type(ref, "echo '===START==='; aws s3 ls 2>&1 | tail -50; echo '===END==='", submit=True)
// 输出写文件再分段读取
browser_type(ref, "python3 /tmp/exploit.py > /tmp/out.txt 2>&1", submit=True)
browser_type(ref, "head -20 /tmp/out.txt", submit=True)
browser_type(ref, "tail -20 /tmp/out.txt", submit=True)
```
More from wgpsec/AboutSecurity
- 401-403-bypass401/403 访问拒绝绕过方法论。当遇到管理后台、API 端点返回 401/403 Forbidden 时使用。覆盖路径操纵、HTTP 方法篡改、Header 注入、协议降级、组合攻击
- ad-acl-abuseActive Directory ACL 滥用攻击方法论。当 BloodHound 发现 GenericAll/WriteDACL/WriteOwner/GenericWrite/ForceChangePassword 等危险 ACE 时使用。覆盖 ACE 枚举、权限滥用链、Shadow Credentials、RBCD 攻击
- ad-delegation-attackKerberos 委派攻击(非约束/约束/RBCD)。当 BloodHound 发现委派配置、或已获取有 SPN 的服务账号/机器账号控制权时使用。通过 S4U 协议滥用可实现跨服务模拟任意用户,常用于域内权限提升和横向移动。
- ad-domain-attackActive Directory 域环境攻击全链路。当目标主机在域环境中(systeminfo 显示 Domain 非 WORKGROUP)、发现 88/389/636 端口、或获取到域用户凭据时使用。覆盖域信息收集、用户枚举、Kerberoasting、AS-REP Roasting、委派攻击、ACL 滥用、DCSync、Golden/Silver Ticket
- ad-persistenceAD 域环境持久化技术。当已获取域管/本地管理员权限、需要建立持久访问以确保重启或密码更改后仍能回到目标环境时使用。覆盖主机级持久化(计划任务/注册表Run/COM劫持/WMI事件订阅/Windows服务/启动文件夹)、域级持久化(Golden Ticket/Silver Ticket/Skeleton Key/DSRM/AdminSDHolder)、DCShadow/GoldenGMSA高级技术、清理命令与检测规避
- ad-trust-attack域信任关系攻击。当目标存在多域/多林环境时使用。包含父子域提权(Golden Ticket + ExtraSid)、跨林攻击(SID History/MSSQL Trust Links)、单向信任利用。已获取子域 Domain Admin 或发现信任关系时优先加载。
- adcs-certipy-attackActive Directory Certificate Services (ADCS) 证书攻击。当发现域内有 CA 服务器、ADCS Web Enrollment、证书模板配置错误时使用。覆盖 ESC1-ESC11 所有证书滥用路径、Certipy 工具链、证书伪造、NTLM 中继到 ADCS。发现 ADCS/CA/证书/certsrv 相关内容时一定要使用此技能
- adinfo-enum使用 Adinfo 进行 Active Directory 信息收集。当获得域用户凭据后需要快速收集域环境信息时使用。Adinfo 是一个快速 AD 信息收集工具,一条命令输出域控列表、域管用户、信任关系、GPO、SPN、委派配置等关键信息——比手动 LDAP 查询快得多。发现域环境后第一步信息收集使用此技能
- agent-security|
- ai-data-security|