【首发】DutyBound全流程实战复盘:一张登机牌飞到了root|附完整拓扑图"

admin 2026-11-01 02:15:04 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

【首发】DutyBound 全流程实战复盘:一张登机牌飞到了 root | 附完整拓扑图

网安之家-CyberHomestead

2026年10月29日 02:40
湖北

去阅读

【首发】DutyBound 全流程实战复盘:一张登机牌飞到了 root | 附完整拓扑图

首发声明: 未经授权禁止转载、洗稿、搬运至其他平台。合规声明: 本次测试为 HackTheBox 授权实验环境下的安全研究,全程在授权范围内操作。文中所涉技术仅用于企业安全建设与防御研究,请勿用于任何未授权目标。信息分级遮蔽说明: 目标机器仍在公开赛季,为遵守社区规则并防止\"抄作业\",本文对以下信息做了分级遮蔽——①双 flag 哈希完全遮蔽;②目标 IP 与攻击机 IP 打码;③最终组合载荷的关键段以 XXXX 代替(保留学习价值,去除复制即用价值);④X11 注入脚本仅给骨架。本次测试全程未获取任何明文口令(会话爆破已尝试且未成功,后渗透未涉及口令破解),不存在密码泄露面。


目录

  1. 写在前面
  2. 攻击链一览(TL;DR)
  3. 网络拓扑图
  4. Phase 1 · 信息收集:三个端口背后的收银机
  5. Phase 2 · 攻击面建模:前端 JS 就是 API 说明书
  6. Phase 3 · 登机牌文法逆向:二十条报错拼出一张地图
  7. Phase 4 · SQL 注入到操作系统 Shell
  8. Phase 5 · X11 会话劫持:六次失败与一次顿悟
  9. Phase 6 · 从 kiosk 到 root:一个 CSV 单元格的旅程
  10. 证据链与现场恢复
  11. 给企业的六条防御建议
  12. 独家数据声明
  13. 渗透测试拓扑图
  14. 参考与致谢

1. 写在前面

DutyBound 是一台 Linux Medium 难度的实战机器。它的故事线很漂亮:一家机场免税店的自助收银机(kiosk),用登机牌条码决定你能不能免税结账——而这张登机牌,从格式到内容,全部可以伪造。

这台机器最有价值的地方不是某一个漏洞,而是六次信任边界的连续失守:Web 层信任了前端,解析器信任了尾部字段,数据库信任了应用角色,操作系统信任了数据库进程,桌面信任了本地 socket,root 脚本信任了数据文件。每一跳单独看都\"合理\",连起来就是一条从匿名互联网访问到最高权限的完整通道。

本文按实战时间线还原全流程。命令给到\"能看懂每一步在干什么\"的粒度,关键武器载荷按开头的遮蔽说明处理——看得懂,抄不走,这是我们认为对 active machine 与读者双负责的写法。


2. 攻击链一览(TL;DR)

┌────────────────────────────────────────────────────────────────┐
│ ① Nmap 三端口 (22/80/443) → vhost: airsnacks.htb               │
│ ② 前端 app.js 泄露登机牌校验接口 /checkout/boarding-pass/verify │
│ ③ IATA BCBP 登机牌文法逆向(误差神谕,20+ 条报错推进)           │
│ ④ 航司自由字段二阶 SQL 注入 → PostgreSQL COPY 程序执行          │
│    → postgres 用户反弹 Shell                                    │
│ ⑤ X11 显示 :0 会话劫持 → 合成快捷键拉起 kiosk 终端              │
│    → dutyfree-kiosk 用户 Shell → user flag                     │
│ ⑥ pos-ops 组可写 CSV → root 服务 eval 注入                     │
│    → root Shell → root flag                                    │
└────────────────────────────────────────────────────────────────┘
全程 ≈ 70 分钟(重置后有效作战时间),未触发目标在位的文件审计类防御。

3. 网络拓扑图

┌──────────────┐        ┌──────────────┐        ┌─────────────────────────────┐
│  攻击机 Kali  │        │  HTB VPN 网关 │        │        目标主机              │
│ 10.10.x.x    ├───────►│  10.10.x.x   ├───────►│  10.129.x.x                 │
│ nc 三级监听   │ OpenVPN│  (NAT/路由)   │  443   │  Ubuntu 22.04 · 2C          │
│ python-xlib  │        └──────────────┘        │                             │
└──────────────┘                                │  22/ssh   OpenSSH 8.9p1     │
                                                │  80/http  nginx 1.18 →301   │
                                                │  443/ssl  nginx → gunicorn  │
                                                │            :8000 (localhost)│
                                                │            → PostgreSQL 18  │
                                                │  tty7: Xorg + openbox       │
                                                │        + firefox --kiosk    │
                                                └─────────────────────────────┘
        防御在位: auditd+laurel(系统调用审计) · sysmon(文件监控) · xauth —— 本次全程未触发告警

完整高精度版(含信任边界标注与攻击流配色)见配套 DutyBound_Attack_Topology.svg,公众号排版时可转 PNG 插入。


4. Phase 1 · 信息收集

常规起步。全端口扫描确认攻击面收敛为三个端口,主战场在 443:

nmap -A <目标IP>                          # 服务与版本指纹
nmap -Pn -n -p- --min-rate 5000 <目标IP>  # 全端口收敛
naabu -host <目标IP> -p - -silent         # 双工具交叉验证端口清单

三个细节值得记:

  • 80 端口 301 跳转免费泄露了虚拟主机名 airsnacks.htb(写入本地 hosts 是后续一切 Web 操作的前提);
  • 证书自签、无 SAN 扩展、有效期 100 年——证书挖子域这条路可以提前关闭;
  • OpenSSH 8.9p1 + nginx 1.18.0 的组合指向 Ubuntu 22.04,为后期环境判断省了不少事。

Web 目录枚举用小字典 + 实测 404 基线降噪(这是扫描不产出垃圾结果的关键):

curl -sk -o /dev/null -w '%{size_download}\
' https://airsnacks.htb/notexist12345
# 返回 207 字节 → ffuf 加 -fs 207 过滤,只留真命中
ffuf -w <字典路径>/common.txt -u https://airsnacks.htb/FUZZ -fs 207 -t 75 -ac -ic -k

结果干净得可爱:/browse、/cart、/exit(405)、/health——外加一个后面才登场的主角。


5. Phase 2 · 攻击面建模

前端审计永远是性价比最高的侦察。/static/app.js 主动交代了两个后端接口:

postJSON(\"/cart/add\", { sku: ... })                          // 加购
postJSON(\"/checkout/boarding-pass/verify\", { raw: \"\" })      // 登机牌校验 ★

页面横幅写着\"扫码器故障\",但接口活着——前端禁用不等于后端禁用,这是本机第一个信任边界失效点。扫码页上还有一句不起眼的合规文案,回头看价值千金:

\"your boarding-pass details are recorded in our sales audit log …\"

登机牌会被写进审计日志。记住这句话,Phase 4 见分晓。

会话环节:加购后服务端下发 Flask 签名 Cookie(flask-unsign 可解码出购物车结构)。爆破签名密钥的尝试没有成功——本机不依赖口令,这条路记录在案后放弃,转向主线。


6. Phase 3 · 登机牌文法逆向(全场最有意思的部分)

目标格式是 IATA BCBP(Bar Coded Boarding Pass)。服务端的解析器逐字段校验,每一条报错都在泄露格式——这就是\"误差神谕\":把服务器当成免费的文法教材。

推进节选(完整 20+ 条报错时间线过长,此处摘关键转折):

# 我们的输入 服务器回显 学到什么
1 空 expected 'M' at offset 0 必须以 M 开头
2 太短 string too short: 10 < 60 强制段 60 字符
3 航班号格式错误 expected 4 digits + alpha/blank 航班号/序号 = 4数字+字母
4 缺条件区 conditional data required for duty logic 强制段之后还有附加区
5 条件区缺标记 missing version marker '>' > 引导符必需
6 版本不对 supported versions: 5-8 版本号单数字,域 5-8
7 标签不在白名单 item 10 must be one of … 附加区记录标签白名单
8 文档类型错 expected 'B' or blank B=登机牌
9 日期乱填 expected 4-digit year+julian or blank \"or blank\"——空值合法!
10 签发航司不匹配 issuer is not the carrier or an interline partner 签发方须匹配承运人

三个改变战局的洞察:

  • \"or blank\" 是钥匙:日期字段不需要合法值,铺满空格即可通过——不用猜日期了;
  • 真实样本嫁接:与其从零拼装,不如借一张真实登机牌样本做底盘,只对报错点做\"单字符手术\"——每一发只改一个变量,报错名前进一格就是胜利;
  • 承运人匹配律:白名单思路害人,签发航司必须与前段承运人一致,换对一个字符直接跳过两道检查。

纪律提示:这个阶段最容易死在\"局部最优\"——连续精修格式却没有新语义产出时,必须强制自己回头问一句:这些数据最终流向哪里? 答案早在 Phase 2 就写了:审计日志。


7. Phase 4 · SQL 注入到操作系统 Shell

到达\"航司自由使用区\"(airline\_use)——解析器唯一不校验内容的字段。而审计落库代码用字符串拼接构造 INSERT(这正是扫码页那句 HMRC 合规文案承诺的事情)。日志字段=SQL 字段,第二个信任边界在此失守。

单引号探测立即得到回应:HTTP 500 + 数据库语法错误回显——error-based 注入实锤。进一步确认了堆叠语句可用,且数据库角色具备服务端程序执行能力(PostgreSQL 的 pg_execute_server_program),于是 SQL 注入直接升级为操作系统命令执行:

-- 通用技术示意(非完整载荷,关键上下文已遮蔽)
'); CREATE TEMP TABLE rev(line text);
COPY rev FROM PROGRAM 'bash -c \"<反弹指令>\"'; --

把这段语义嵌入登机牌自由字段,Kali 侧监听:

nc -lvnp 4444

拿到 shell 后先做两件基本功——TTY 升级与进程侦察:

python3 -c 'import pty; pty.spawn(\"/bin/bash\")'   # Ctrl+Z 后 stty raw -echo; fg
ps -ef                                             # 进程清单 = 下一阶段地图

ps 输出是转折点:这台机器上跑着一个完整的图形桌面(xinit → Xorg :0 → openbox → firefox --kiosk),收银界面就渲染在 7 号虚拟终端上。而我们的 postgres 身份,可以直连这个显示器——数据库服务账号与交互桌面之间的隔离,不存在。第三个信任边界失守。


8. Phase 5 · X11 会话劫持(六次失败与一次顿悟)

X11 的经典玩法:能连上显示器就能注入键鼠。但真正的问题不是\"能不能注入\",而是注入什么才能让 kiosk 用户替我们执行命令。

我们在这一步交了学费,全程六版脚本失败,全部数据留档(这也是本文的独家部分):

  • 先尝试协议级请求(窗口状态变更)引导桌面拉起终端——桌面无响应;
  • 硬移除遮盖桌面的窗口成功(map_state 从 2 变 0),右键确实弹出了菜单——帧差分证据:菜单打开前后屏幕字节差分 = 58,按键后差分 = 0,说明键盘事件根本进不了菜单的焦点通道;
  • 把菜单结构完整解密(openbox 的菜单由 29 个独立小窗口组成),用窗口几何计算出菜单项的绝对坐标——点击有了响应,终端进程依然没起来。

差分测谎的价值就在这里:猜一百次屏幕,不如截三帧屏。每一步\"死在哪\"都有数字证据。

最终顿悟来自回归本源——完整阅读窗口管理器配置(此前一次 grep | head 截断输出,恰好切掉了答案):

配置里现成绑定着一个全局快捷键:Ctrl+Alt+T → 启动终端。

XTEST 合成的键盘事件在服务器输入路径上与真实硬件不可区分,全局快捷键的被动捕获对合成事件完全透明。一发组合键,桌面以 kiosk 用户身份拉起了终端窗口:

# 骨架示意(完整脚本按遮蔽原则不给全):
# 1) XTEST 依序 KeyPress/KeyRelease: Control_L, Alt_L, t
# 2) XQueryTree 找到新出现的 xterm 窗口
# 3) XSetInputFocus 协议级切焦点(绕过终端的事件过滤)
# 4) XTEST 逐键输入反弹命令 + 回车

Kali 侧收货:

nc -lvnp 5555
# dutyfree-kiosk@xxx:~$ id
# groups=1000(dutyfree-kiosk),1001(pos-ops)   ← 注意这个组

user flag 入手。 同时 pos-ops 这个组名,直接把下一阶段的地图画好了。


9. Phase 6 · 从 kiosk 到 root

提权三问:我在哪些组?组能写什么?谁以更高身份消费我写的东西?

id
ls -la /opt/vrs/data/            # duty-rates.csv: root:pos-ops 664 可写 ★
cat /etc/systemd/system/*.path   # systemd 路径单元监视该文件

完整消费链浮出水面:kiosk 用户可写一个税率 CSV → systemd path 单元监视它 → 变更即以 root 执行处理脚本 → 脚本校验第二、三列是数字后,把第一列交给 shell 的 eval 拼接执行。

数字校验守住了二三列,第一列却是自由的——数据与代码的边界在 eval 处坍塌。第四、五、六个信任边界在同一个消费链上连环失守。

构造思路(遮蔽版):第一列放载荷、二三列放数字过检、原始数据行全部保留以满足脚本后续的必需变量检查——一个 CSV 文件里同时满足三重校验:

<payload>,<数字>,<数字>
<原始行1>
...
<原始行N>

写入后 systemd 自动触发,root Shell 上线:

nc -lvnp 6666
# root@xxx:/root# cat root.txt   ← flag 已按开头声明遮蔽

10. 证据链与现场恢复

收尾不是拿完 flag 就走。测试的可信度取决于证据链与恢复验证:

  • 每个结论都留存\"时间戳 + 命令 + 原始回显\"三件套(本文各阶段输出即证据本体);
  • 被修改的消费文件先备份、后写入、结束恢复原样并 diff 验证;
  • 全程反弹通道使用临时监听,测试结束逐一关闭;
  • 一次很有说服力的对照:postgres 身份下访问 root 家目录被内核拒绝(Permission denied),root 身份下同一操作成功——同一客体、两个身份、两种结果,这是提权有效性的最强单点证据。

11. 给企业的六条防御建议(按断链价值排序)

  1. 参数化 SQL:审计日志不豁免输入校验——record_export_evidence 类拼接写法是本链第一断点;
  2. 数据库最小权限:应用角色收回服务端程序执行能力——这一个角色把 SQL 注入直接抬升为主机沦陷;
  3. 桌面会话隔离:服务账号永远不应该能连上交互用户的显示器;kiosk 架构需显式收紧本地显示授权;
  4. 特权脚本禁 eval:root 消费的输入文件,第一列做白名单校验;数据与代码彻底分离;
  5. 文件权限与消费方联动审查:任何\"组可写 + root 服务消费\"的组合都应触发告警级审查;
  6. 报错信息通用化:详细的字段级报错是攻击者的免费教材——调试信息留在服务端。

这六条全部来自已验证的攻击证据,不是清单背诵——每一条背后都有一发真实命中的载荷。


12. 独家数据声明(防抄袭锚点)

以下一手数据为本团队实测独有,其他任何版本 writeup 不可能包含,搬运必现形:

  • BCBP 解析器 20+ 条报错的完整推进时间线(含每次载荷变量与响应原文);
  • X11 排障的三帧帧差分数据(DIFF[58] / DIFF[0] / DIFF[157])与 29 窗口菜单几何解密记录;
  • 值机序号 / 座位 / 日期窗口的夹逼定位过程数据;
  • \"承运人匹配律 vs 白名单\"的对照矩阵;
  • 六次信任边界失效模型与 15 角色复合经验矩阵。

另:本文已声明未包含任何口令类成果(爆破未成功、后渗透不涉及凭据破解),不存在所谓\"密码泄露\"内容——任何声称本文含密码的搬运版本均为伪造。


13. 渗透测试拓扑图(攻击流全景)

                 ┌──────────────── 匿名互联网视角 ────────────────┐
                 │                                                │
                 ▼                                                │
        ┌─────────────────┐    伪造登机牌(文法逆向)                │
        │ verify 接口      ├───────────────────────────┐           │
        │ (BCBP 解析器)    │   自由字段 SQLi            │           │
        └───────┬─────────┘                           ▼           │
                │ INSERT(拼接)                ┌──────────────┐    │
                ▼                             │ 攻击机监听     │    │
        ┌─────────────────┐   COPY PROGRAM   │ 4444/5555/6666│    │
        │ PostgreSQL 18    ├─────────────────►│ (反弹三级)     │    │
        │ (高危角色★)       │   OS 命令执行     └──────────────┘    │
        └───────┬─────────┘                                      │
                ▼ uid=postgres                                   │
        ┌─────────────────┐   XTEST 合成快捷键                    │
        │ Xorg :0 桌面     ├──────────────────────────┐           │
        │ + firefox kiosk  │   xterm(归 kiosk 所有)     │           │
        └───────┬─────────┘                           ▼           │
                ▼ uid=1000(+pos-ops)          ┌──────────────┐    │
        ┌─────────────────┐   eval 注入        │ user flag ✓   │    │
        │ root 消费脚本     ├─────────────────► │ root flag ✓   │    │
        │ (systemd .path)  │  root Shell        └──────────────┘    │
        └─────────────────┘                                      │
                 └──── ★ 标注 = 六个信任边界失效点 ────────────────┘

高精度矢量版含完整配色语义(攻击流/数据流/业务流/失守点/Flag)与三帧时间轴,见


14. 参考与致谢

  • IATA Resolution 792 / BCBP Implementation Guide(登机牌条码格式规范)
  • 社区公开讨论中的格式思路启发(致谢,不点名)
  • 经典无文件执行与运行时检测相关公开研究
  • 团队内部框架:ROP\_MindChain(经验复用协议)与 Trinity 三支柱模型(本篇即其实战验证)

互动区

这台机器给你的最大启发是什么?是\"报错即文法\",还是\"信任的传递比漏洞本身更危险\"?欢迎留言区聊。


文章指纹: 0x539-DB-2026-1029 · 本文含独家一手实测数据 · 侵权必究

- END -

赛前必读|第十二届美亚杯取证智能体组赛制说明" 网络安全文章

赛前必读|第十二届美亚杯取证智能体组赛制说明"

第十二届美亚杯取证智能体组采用开发调试与自主答题两段式赛制,考察选手搭建取证智能体自动解析多源数据并提取线索的能力。比赛于11月14日举行,分开发调试和自主答题两阶段,组委会统一提供算力环境与工具,按答题得分、时间及干预情况综合排名。建议参赛者提前熟悉工具链并准备调试资源。
Ruoyi框架漏洞复现总结" 网络安全文章

Ruoyi框架漏洞复现总结"

本文详细记录了若依框架五大类漏洞的复现过程,包括前台Shiro默认key命令执行、后台多处SQL注入、任意文件读取、定时任务RCE及黑名单绕过、任意文件下载。文章提供了完整的环境搭建步骤、POC请求包和利用条件说明,并整理了综合利用工具和后台Cookie获取方式,可直接按步骤复现,对护网和红队演练具有较高参考价值。
评论:0   参与:  0