Skip to main content Home Creators wgpsec aboutsecurity browser-xterm-interaction
browser-xterm-interaction Playwright Browser MCP 与 xterm.js 终端交互方法论。当需要通过浏览器操作网页内嵌终端、CTF 靶场伪终端、Cloud Shell、在线 IDE 中的终端时使用。覆盖终端内容读取(5 种方法)、命令执行、输出捕获、screenshot 降级策略。只要目标页面中有任何形式的 Web 终端(xterm.js/hterm/jQuery Terminal),就应使用此技能
Jump to install Skills Marketplace Discover and explore AI skills built by the community.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Copy promptShow prompt details A direct command skips the review prompt. Inspect the source before running it.
npx skills add https://github.com/wgpsec/AboutSecurity --skill browser-xterm-interactionThe command stays on one line. Scroll horizontally to inspect it before copying.
Prefer a local copy? Download the files currently available to SkillsMP.
Download Zip Downloading... More from this repository Related occupations SOC
Based on SOC occupation classification
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 方法才能可靠地读取终端输出。
核心原则
一次定型 — 找到有效的读取方法后坚持用它,不要每次都换方法,因为来回切换会浪费大量 turn
3 次法则 — 一种方法最多试 3 次就切换下一种,避免在死路上消耗 turn
screenshot 兜底 — JS 方法全部失败时,截图+视觉分析是 100% 可靠的最终手段
marker 包裹 — 用 echo "===START==="; cmd; echo "===END===" 包裹命令输出,方便精确提取
Phase 1: 识别终端类型
先用一次 browser_snapshot() 或 browser_evaluate 探测终端类型:
() => {
const xterm = document .querySelector ('.xterm' );
const jquery_term = window .jQuery && window .jQuery .fn . ;
hterm = . ( );
{
: !!xterm,
: !!jquery_term,
: !!hterm,
: xterm ? xterm. :
};
}
terminal
const
document
querySelector
'[id*="hterm"]'
return
xterm
jquery_terminal
hterm
terminal_classes
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 写入 () => {
const textarea = document .querySelector ('.xterm-helper-textarea' );
if (textarea) {
textarea.focus ();
}
}
⚠️ 重要 :执行命令后,必须等待足够时间让输出完成:
快速命令(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)
输出只有提示符,没有任何命令结果
输出看起来是中间片段(没有起始行)
修复方法 command > /tmp/verify.txt 2>&1 && cat /tmp/verify.txt
Phase 3: 输出读取(核心难点)
⭐ 方法 1: xterm-rows innerText(最可靠) () => {
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 () => {
const screen = document .querySelector ('.xterm-screen' );
return screen ? screen.innerText : 'not found' ;
}
方法 3: 通过 Terminal API(如果暴露) () => {
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 包裹法(精确提取命令输出) echo "===START===" ; your_command_here; echo "===END==="
然后在 JS 中提取 ===START=== 和 ===END=== 之间的内容。
方法 5: 重定向到文件 + cat your_command > /tmp/out.txt 2>&1; cat /tmp/out.txt
Phase 4: 降级策略(JS 方法失败时)
⭐ Screenshot + 视觉分析(最终兜底) 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 脚本会导致格式错误。
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 )
echo 'import boto3; print(boto3.client("s3").list_buckets())' | base64
browser_type (ref, "echo 'aW1wb3J0IGJvdG8z...' | base64 -d > /tmp/s.py && python3 /tmp/s.py" , submit=True )
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):
browser_type (ref, "AWS_ACCESS_KEY_ID=AKIAXXXX AWS_SECRET_ACCESS_KEY=YYYY aws s3 ls" , submit=True )
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 终端输出缓冲区有限,长输出会丢失开头部分:
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 )