首页
AndyYan's DN42 Network
友链
关于
Search
1
MaiBot+AstrBot+Napcat分离部署,实用、稳定且拟人化的QQBot部署方案
301 阅读
2
Linux环境下的Silly Tavern 云酒馆 AI 搭建与美化完善(几乎0基础?)
148 阅读
3
IOS系统解压.lz4格式压缩包
111 阅读
4
解决OMV登陆WebUI时400 Bad Request错误
77 阅读
5
Typecho + JOE 主题下 Mermaid 渲染的解决方案
36 阅读
所有文章
NAS
DN42
开源项目
AI
调优
运维
网络安全
登录
Search
AndyYan
累计撰写
12
篇文章
累计收到
1
条评论
首页
栏目
所有文章
NAS
DN42
开源项目
AI
调优
运维
网络安全
页面
AndyYan's DN42 Network
友链
关于
搜索到
12
篇与
的结果
2026-07-12
通过Asterisk自建PBX以接入Telephony42
提示:本人对于电话网络的了解停留在初步阶段,本博客仅仅是我初步的学习总结,不仅十分不完善,还可能出现部分知识性错误,如遇错误与意见可联系
[email protected]
,感谢您的理解和指正!另:本文默认您已经加入了DN42并至少和一位peer进行了对等连接。本文部分基础概念和图示基于 Yukari Chiba 的《telephony42 完全指南》整理和改写,原文采用 CC BY-SA 4.0,本文相应部分亦按 CC BY-SA 4.0 发布。前言本人在DN42网络中扮演 AS4242422921 并拥有一个四节点的小集群,集群间建立了 FullMesh 的 IBGP 组网,通过 OSPF 作为 IGP 协议建立内部动态路由,并申请了 andy.dn42 作为我的域名,搭建了 1主+1从 的权威服务器(由knot驱动)。最近在DN42的群里看到了 Yukari 大佬提到的telephony42,即一套实验性质的去中心化电话网络。我觉得自建一个电话网络很酷,于是打算跟着Yukari的 这篇博客 搓一个 PBX(Private Branch Exchange - 私有交换机) ,接入telephony42, 打电话骚扰群友 。前期准备{x} 一个DN42域名 {x} 一台 已接入DN42 的,配置不低于1C1G的服务器 {x} 自建的DN42内部权威DNS(这里以knot演示)概念须知PS:本篇博客仅是“最小加入指南”,后面譬如SRTP的进阶操作本人也在学习,还请自行了解,本部分参考了0x7f的博客内容。组件简要解释PBX (Private Branch Exchange - 私有交换机)视作 VoIP 网络中的路由器,负责处理信令和路由,有时也处理语音数据。B2BUA (Back-to-Back User Agent - 背靠背用户代理)Asterisk 之类的 PBX 就是一个典型的 B2BUA。与仅转发信令的 SIP 代理不同,当 A 通过 PBX 呼叫 B 时,PBX 实际上是先以被叫的身份接听了 A 的电话,然后再以主叫的身份向 B 发起一通全新的电话,最后在内部把这两个独立的通道桥接起来。这使得 PBX 能随时介入通话过程,进行录音、转码、甚至强制掐线。Endpoint (终端设备)任何可以发起或接收呼叫的设备。它可以是电脑上的软件电话应用、桌面的实体 IP 电话机、ATA、网页上的 WebRTC 电话,甚至是另一个网络的 PBX。协议简要解释会话发起协议 (SIP - RFC 3261)作为控制平面。采用纯文本格式,长得有点像 HTTP,包含 URI、头字段和正文。主要负责信令传输,例如传递呼叫的来源和目标、接通、挂断或拒接等,不负责传输具体的语音流。SIP 默认监听于 UDP/TCP 5060 端口(在报文较大时通常优先使用 TCP),TLS 加密状态下监听 TCP 5061 端口。实时传输协议 (RTP - RFC 3550)作为数据平面。当 SIP 握手与 SDP 协商完成后,双方会在协商好的高位动态端口(通常在 UDP 10000 至 20000 之间)互相传递 RTP 数据包。这些包对延迟和抖动极其敏感。如果防火墙只放行了 SIP (5060) 但没放行 RTP,就会遇到 VoIP 史上最著名的故障: 单通(接通了但听不到声音)。路由简要解释ENUM (E.164 Number Mapping)是一种自动发现路由的机制。当拨打目标是未知目的地的电话时,PBX 会根据号码查询 DNS 的 NAPTR 记录,查找到该号码对应的 SIP 目标地址,在DN42通过一个特殊的DNS查询实现Context (上下文)是 Asterisk 拨号计划的关键组件。可以把它理解为 VoIP 系统中的 VRF。它可以把不信任的外部呼叫丢进一个没有外拨权限的隔离上下文中,并且能够通过 Goto 等语句实现在不同上下文间链式跳转,在后面的拦截伪造来电等功能上很有用对于跨网的SIP呼叫,可以分为 三个阶段 :第一部分:SIP 呼叫建立PBX将充当一个类似“ 中转站 ”的角色,先接听主叫设备的 INVITE ,使用 ENUM DNS 查询( 后面会展开解释 )与信令路由,查询到对方的PBX之后向其转发INVITE,由对方的PBX再呼叫被叫设备,接听后层层转发回200 OKsequenceDiagram participant Caller as 主叫设备 (A) participant PBXA as 本地 PBX participant PBXB as 对端 PBX participant Callee as 被叫设备 (B) Note over Callee, Caller: 控制平面交互 (SIP) Caller->>PBXA: INVITE sip:B@PBXA_IP (含 SDP 提议) PBXA->>Caller: 100 Trying Note over PBXA, PBXB: ENUM DNS 查询与信令路由 PBXA->>PBXB: INVITE sip:B@PBXB_IP (含 SDP 提议) PBXB->>PBXA: 100 Trying PBXB->>Callee: INVITE (含 SDP 提议) Callee->>PBXB: 180 Ringing (振铃) PBXB->>PBXA: 180 Ringing PBXA->>Caller: 180 Ringing Note over Callee: 用户接听电话 Callee->>PBXB: 200 OK (含 SDP 应答) PBXB->>PBXA: 200 OK (含 SDP 应答) PBXA->>Caller: 200 OK (含 SDP 应答) Caller->>PBXA: ACK PBXA->>PBXB: ACK PBXB->>Callee: ACK第二部分:RTP 语音传输在接通之后,通过两台PBX作为桥梁,两台终端即可在10000端口附近(可手动指定范围)互传udp数据包,即RTP语音流, 在本文关闭 direct_media 、由 PBX 锚定媒体的配置下 ,所有数据在逻辑上仍通过PBX转发sequenceDiagram participant Caller as 主叫设备 (A) participant PBXA as 本地 PBX participant PBXB as 对端 PBX participant Callee as 被叫设备 (B) Note over Caller, Callee: 数据平面交互 (RTP) Caller-->>PBXA: RTP 语音流 PBXA-->>PBXB: RTP 语音流 PBXB-->>Callee: RTP 语音流 Callee-->>PBXB: RTP 语音流 PBXB-->>PBXA: RTP 语音流 PBXA-->>Caller: RTP 语音流第三部分:SIP 挂断在挂断阶段,仍是通过PBX转发BYE,回传200 OK进行挂断操作sequenceDiagram participant Caller as 主叫设备 (A) participant PBXA as 本地 PBX participant PBXB as 对端 PBX participant Callee as 被叫设备 (B) Note over Caller, Callee: 控制平面交互 (SIP) Caller->>PBXA: BYE PBXA->>PBXB: BYE PBXB->>Callee: BYE Callee->>PBXB: 200 OK PBXB->>PBXA: 200 OK PBXA->>Caller: 200 OK通过以上内容,我们可以发现,主要的过程都离不开PBX交换机,故只要我们搭建好PBX即可初步建起自己的电话网络。而对于PBX的 软交换平台 ,对于新手玩家Asterisk是比较好的选择。PS:Yukari把PBX比作 电话网络的自治系统 ,把Asterisk比作 电话网络的 BIRD开始部署1.SIP 域名创建在 yourdomain.dn42 Zone 中添加:sip.andy.dn42. 300 IN A 172.21.118.162 #ip对应你的PBX服务器 sip.andy.dn42. 300 IN AAAA fdd2:e3e2:c922::2 #ip对应你的PBX服务器 pbx.andy.dn42. 300 IN CNAME sip.andy.dn42.2.ENUM (E.164 Number Mapping)建立由上面的内容我们可以知道,要想呼叫其他PBX下的设备,必须 知道对方的地址 ,故我们需要一种机制将电话号码翻译成 PBX 的 IP 地址,这就是 ENUM ENUM基于 DNS ,它很像 rDNS 的解析方式,即先将电话号码翻转,加入点号分隔,再加上一个特定的域名后缀最后,利用 DNS 的 NAPTR (Naming Authority Pointer) 记录,一个包含正则表达式的记录,就可以可以将这串倒写的号码解析成 SIP URI。下面是demo,此正则可以匹配该前缀下的所有号码$ORIGIN 4.3.2.1.0.4.2.4.tel.dn42. * IN NAPTR 10 100 "u" "E2U+sip" "!^(.*)$!sip:\\
[email protected]
!" .我的权威DNS使用的是kont,下面是我创建ENUM的步骤:2.1按号码计算出自己的ENUM Zone我的号码是 +042429211001去掉 +: 042429211001逐位反转并加点: 1.0.0.1.1.2.9.2.4.2.4.0.tel.dn42.号码前缀: +04242921对应的ENUM Zone: 1.2.9.2.4.2.4.0.tel.dn42.2.2创建对应 ENUM Zone创建Zone文件:/var/lib/knot/zones/1.2.9.2.4.2.4.0.tel.dn42.zone内容:$ORIGIN 1.2.9.2.4.2.4.0.tel.dn42. $TTL 300 @ IN SOA ns1.andy.dn42. admin.andy.dn42. ( 2026071001 3600 900 604800 300 ) @ IN NS ns1.andy.dn42. @ IN NS ns2.andy.dn42. ; +042429211001 #我准备创建两个分机 1.0.0.1 300 IN NAPTR 100 10 "u" "E2U+sip" \ "!^.*$!sip:
[email protected]
!" . ; +042429211002 2.0.0.1 300 IN NAPTR 100 10 "u" "E2U+sip" \ "!^.*$!sip:
[email protected]
!" .2.3使用 kzonecheck 检查 ENUM Zone 文件的合法性。kzonecheck -o "$ZONE" "$ZONEFILE"退出码 0 即表示PASS。2.4创建主NS对应的Knot DNS服务配置文件示例:key: - id: telephony42-xfr algorithm: hmac-sha256 secret: "[数据删除]" remote: - id: telephony42-secondary address: 172.21.118.164 key: telephony42-xfr acl: - id: telephony42-transfer key: telephony42-xfr action: transfer zone: - domain: 1.2.9.2.4.2.4.0.tel.dn42. file: /var/lib/knot/zones/1.2.9.2.4.2.4.0.tel.dn42.zone notify: telephony42-secondary acl: telephony42-transfer在主配置中加入include:include: "/etc/knot/conf.d/*.conf"检查合法性:knotc conf-check设置文件权限:chown root:knot /etc/knot/conf.d chmod 750 /etc/knot/conf.d chown root:knot /etc/knot/conf.d/telephony42.conf chmod 640 /etc/knot/conf.d/telephony42.conf chown knot:knot /var/lib/knot/zones/1.2.9.2.4.2.4.0.tel.dn42.zone chmod 640 /var/lib/knot/zones/1.2.9.2.4.2.4.0.tel.dn42.zone最后重载 kont2.5同步到从服务器首先生成密钥:umask 077 openssl rand -base64 32 > /root/telephony42-xfr.secret chmod 600 /root/telephony42-xfr.secret chown root:root /root/telephony42-xfr.secret stat -c '%A %U:%G %s bytes %n' /root/telephony42-xfr.secret把密钥文件存放到ns2上再到 ns2 执行:chmod 600 /root/telephony42-xfr.secret chown root:root /root/telephony42-xfr.secret stat -c '%A %U:%G %s bytes %n' /root/telephony42-xfr.secret #查看权限是否正确ns2通过AXFR从ns1同步。示例:key: - id: telephony42-key algorithm: hmac-sha256 secret: "[刚刚生成的密钥]" remote: - id: ns1 address: 172.21.118.161 key: telephony42-key zone: - domain: 1.2.9.2.4.2.4.0.tel.dn42 master: ns1重载kont。3.安装并配置Asterisk我的系统是Debian13,其默认 trixie 仓库中没有完整的 Asterisk 主程序包,故打算自己编译安装。在询问ai之后,得知:教程中说可以 APT 安装,是因为它隐含使用了 Debian sid 仓库。对于已经运行 BIRD 和其他 DN42 服务的核心节点,不建议为了 Asterisk 混入 sid,可能导致基础库被大规模升级。3.1源码编译 Asterisk安装编译依赖:apt install -y \ build-essential pkg-config autoconf-archive bison flex \ wget ca-certificates patch bzip2 python3-dev \ libedit-dev libjansson-dev libsqlite3-dev uuid-dev libxml2-dev \ libssl-dev libcurl4-openssl-dev liburiparser-dev libxslt1-dev \ libcap-dev libnewt-dev libncurses-dev \ libsrtp2-dev libgsm1-dev libspeexdsp-dev \ libogg-dev libvorbis-dev \ bind9-dnsutils下载并解压 Asterisk 22.10.1:cd /usr/src wget https://downloads.asterisk.org/pub/telephony/asterisk/releases/asterisk-22.10.1.tar.gz tar -xzf asterisk-22.10.1.tar.gz cd asterisk-22.10.1配置时使用 Asterisk 自带的 pjproject:./configure --with-pjproject-bundled make menuselect.makeopts启用所需模块:for MODULE in \ res_pjsip \ chan_pjsip \ res_pjsip_authenticator_digest \ res_pjsip_endpoint_identifier_ip \ res_pjsip_registrar \ res_pjsip_session \ res_pjsip_sdp_rtp \ res_rtp_asterisk \ func_enum \ app_dial \ app_echo \ app_playback \ pbx_config \ codec_alaw \ codec_ulaw \ codec_g722 do menuselect/menuselect \ --enable "$MODULE" \ menuselect.makeopts done由于我的vps只有1c1g,故采用单线程编译:make -j1完成后安装:make install make samples make config make install-logrotate ldconfig3.2创建Asterisk用户官方建议Asterisk以低权限运行,故创建专用用户:groupadd --system asterisk useradd \ --system \ --gid asterisk \ --home-dir /var/lib/asterisk \ --shell /usr/sbin/nologin \ asterisk准备目录:install -d -o asterisk -g asterisk /run/asterisk install -d -o asterisk -g asterisk /var/log/asterisk install -d -o asterisk -g asterisk /var/spool/asterisk install -d -o asterisk -g asterisk /var/lib/asterisk最后注册系统服务并保持自启3.3配置Asterisk首先是最重要的: Asterisk只应监听DN42内部网段!防止公网 SIP 扫描和暴力注册!172.21.118.162:5060/UDP [fdd2:e3e2:c922::2]:5060/UDP3.4创建PJSIP 分机我打算创建两个分机:1001:Windows Blink 1002:iOS SIP 客户端 1001 → +042429211001 1002 → +042429211002核心配置:[transport-dn42-ipv4] type=transport protocol=udp bind=172.21.118.162:5060 local_net=172.20.0.0/14 [transport-dn42-ipv6] type=transport protocol=udp bind=[fdd2:e3e2:c922::2]:5060 local_net=fd00::/8分机模板:[local-endpoint](!) type=endpoint context=from-local disallow=all allow=g722 allow=alaw allow=ulaw direct_media=no rtp_symmetric=yes force_rport=yes rewrite_contact=yes dtmf_mode=rfc4733注:DNS NAPTR 定义的是外部号码如何找到本地分机;分机外呼时使用的 Telephony42 主叫号码则需要通过 endpoint 的 callerid 或拨号计划另行设置。3.5缩小RTP范围我的内部网络目前只有两个分机,所以决定将 RTP 端口缩小到: UDP 10000–10100[general] rtpstart=10000 rtpend=10100 strictrtp=yes3.6拨号计划我的内部号码: 1001 1002 我打算使用一个号码进行本地ECHO测试: 5000 我的核心拨号计划:[from-local] exten => _XXXX,1,NoOp(Local call) same => n,Goto(local-extensions,${EXTEN},1) [local-extensions] exten => 1001,1,Dial(PJSIP/1001,30) same => n,Hangup() exten => 1002,1,Dial(PJSIP/1002,30) same => n,Hangup() exten => 5000,1,Answer() same => n,Wait(1) same => n,Echo() same => n,Hangup()最后验证模块是否正常:asterisk -rx 'module show like chan_pjsip' asterisk -rx 'module show like res_pjsip' asterisk -rx 'module show like res_rtp_asterisk' asterisk -rx 'module show like func_enum' asterisk -rx 'core show application Echo' asterisk -rx 'core show function ENUMLOOKUP'重载Asterisk:asterisk -rx "pjsip reload"3.7在PBX上部署分流 DNS由于PBX的默认DNS并不能解析tel.dn42,故应该部署dnsmasq对以下域名进行分流:<yourdomain>.dn42 tel.dn42安装dnsmasq:apt install -y dnsmasq我的配置文件示例: /etc/dnsmasq.d/dn42.confserver=/andy.dn42/172.21.118.161 server=/andy.dn42/172.21.118.164 server=/tel.dn42/172.21.118.161 server=/tel.dn42/172.21.118.164 #注:Knot DNS 只实现权威 DNS 服务,不是递归解析器,上面两行请设置成自己的权威DNS! listen-address=127.0.0.1 bind-interfaces重启dnsmasq(由于 监听地址 变化,无法进行热重载):systemctl restart dnsmasq在Asterisk 中测试:asterisk -rx \ 'dialplan eval function ENUMLOOKUP(+042429211001,sip,u,1,tel.dn42)'如果正常,应该返回:sip:
[email protected]
配置 Telephony42 外呼拨号计划由于ENUM只负责告诉Asterisk“打给谁”,但后续Asterisk需要决定用哪个PJSIP endpoint 发起呼叫等等,故我们需要一个通用出站模板peer-enum-outbound 于是在 pjsip.conf 中配置一个无需注册的出站 endpoint。示例:[peer-enum-outbound] type=endpoint transport=transport-udp context=from-enum disallow=all allow=ulaw allow=alaw aors=peer-enum-outbound direct_media=no [peer-enum-outbound] type=aor实际目标由 ENUM 返回的 SIP URI 决定3.9配置 Telephony42 外呼拨号计划在 extensions.conf 中添加:[internal] exten => _+X.,1,NoOp(Telephony42 outbound: ${EXTEN}) same => n,Set(ENUMURI=${ENUMLOOKUP(${EXTEN},sip,u,1,tel.dn42)}) same => n,Set(ENUMURI=${ENUMLOOKUP(${ENUMNUM},sip,,1,tel.dn42)}) same => n,NoOp(ENUM result: ${ENUMURI}) same => n,GotoIf($["${ENUMURI}"=""]?enum-failed) same => n,Dial(PJSIP/${ENUMURI}@peer-enum-outbound,60) same => n,Hangup() same => n(enum-failed),Playback(invalid) same => n,Hangup()重载Asterisk:asterisk -rx "pjsip reload" asterisk -rx "dialplan reload"此时通过 1001 拨打 +042429211002应该已经能通了。 推荐使用 sngrep 进行通话流程分析,其是一个专用于 SIP 的 TUI 抓包与可视化工具(附有颜色标识)。3.10配置 Telephony42 入站 SIP为了接收其他 Telephony42 节点的呼叫,需要设置入站 endpoint。示例:[peer-enum-inbound] type=endpoint transport=transport-udp context=context-enum disallow=all allow=ulaw allow=alaw direct_media=no然后添加identify,让来自DN42的SIP包匹配到该endpoint。[peer-enum-inbound-identify-v4] type=identify endpoint=peer-enum-inbound match=10.0.0.0/8 match=172.20.0.0/14 match=172.31.0.0/16 [peer-enum-inbound-identify-v6] type=identify endpoint=peer-enum-inbound match=fd00::/8再次强调: 千万不要把公网 0.0.0.0/0 放进去!!3.11配置独立入站 Context在入站时,通话会先被引入到一个独立的Context中。[context-enum] exten => 1001,1,NoOp(Telephony42 inbound to 1001) same => n,Dial(PJSIP/1001,30) same => n,Hangup() exten => 1002,1,NoOp(Telephony42 inbound to 1002) same => n,Dial(PJSIP/1002,30) same => n,Hangup()重新加载Asterisk 需要注意,独立的入站 Context 只能限制外部来电的拨号权限,不能证明 CallerID 真实有效。4.提交 Telephony42 Registrytelephony: +04242921 nserver: ns1.andy.dn42 nserver: ns2.andy.dn42 admin-c: ANDY-DN42 tech-c: ANDY-DN42 mnt-by: ANDY-MNT source: DN425.PR合并后验证父区委派生效后,你可以使用 DN42 递归 DNS 查询。ZONE="1.2.9.2.4.2.4.0.tel.dn42." #检查 NS: dig @172.20.0.53 "$ZONE" NS \ +noall +answer +authority #正常应显示: #ns1.andy.dn42. #ns2.andy.dn42.至此,大公告成!快访问yp.dn42拨打 奇奇怪怪 的电话吧!参考文章:telephony42 完全指南:从零开始在 dn42 构建电话网络在 Telephony42 中配置 E.164 ENUM
2026年07月12日
33 阅读
0 评论
1 点赞
2026-07-02
一次 Looking Glass 参数注入事件的复盘:从 /etc/shadow 泄露到全集群加固
本文隐去了真实主机名、域名与 IP,但保留了完整的攻击 payload 与处置命令,方便复现与自查。TL;DR我的 DN42 集群跑着一个公网可访问的 bird-lg-go Looking Glass,四台节点共用一个前端。其中 bird-lgproxy ≤ v1.4.6 存在参数注入漏洞(CVE-2026-26514,GHSA-3qm5-22pm-wqg9):攻击者通过 traceroute 功能的 q 参数注入 mtr 的 -F 选项,把任意文件当作“报告文件”读出来,最终读到了每台机器的 /etc/shadow——也就是 root 密码哈希。注意 v1.4.6 的修复不完整、可被绕过,真正修好是在 v1.4.7。处置结论:四台节点全部升级到 v1.4.7、轮换全部 root 密码、审计 SSH 密钥与计划任务、确认无后门与异常登录。 漏洞在公网边缘已验证彻底关闭。漏洞原理Looking Glass 的 traceroute 功能,本质上是把用户输入的目标丢给系统的 mtr / traceroute 去跑。问题出在旧版 bird-lgproxy 直接把用户输入拼进 exec.Command,既没有把它当成单一目标、也没有拒绝以 - 开头的输入。于是用户输入里可以夹带选项。攻击者只要构造:-F /etc/shadow test.invalid它就被当成了 mtr -F /etc/shadow test.invalid。而 mtr 的 -F 选项恰好是“从文件读取主机列表(report file)”——mtr 会尝试打开 /etc/shadow,并在报错信息里带出文件内容;而 proxy 用 CombinedOutput() 把 stdout + stderr 一起回显给前端,于是文件内容就顺着错误输出泄露了出去。test.invalid 只是凑一个“看起来像目标”的占位。于是一条 HTTP GET 就能读任意文件:# 攻击者视角:通过公网 LG 前端读取 /etc/shadow curl 'https://<lg-frontend>/traceroute/<node>/-F%20%2Fetc%2Fshadow%20test.invalid'这类问题的根源是老生常谈的一句话:永远不要把用户输入拆成命令行参数。目标就该当作一个不可拆分的整体字符串,任何以 - 开头的东西都不该被解释成选项。下面这张图把整条注入链路画出来了——注意红色路径是漏洞如何从一个 HTTP 参数一路变成任意文件读取的:flowchart LR A[公网攻击者] -->|"GET /traceroute/node/<br/>-F /etc/shadow test.invalid"| B[LG 前端<br/>bird-lg] B -->|转发请求<br/>源 IP = 前端| C[节点 bird-lgproxy<br/>:8000] C -->|"输入直接进 exec.Command<br/>未拒绝 - 开头参数"| D{"mtr -F /etc/shadow<br/>test.invalid"} D -->|-F 被解释为<br/>read report file| E[["读取 /etc/shadow"]] E -->|回显文件内容| A style A fill:#ffd5d5,stroke:#c0392b style D fill:#ffe6cc,stroke:#e67e22 style E fill:#ffd5d5,stroke:#c0392b,stroke-width:2px攻击时间线从被攻击节点的 bird-lgproxy 日志里,能看到一段非常“教科书”的探测→提权信息收集过程(时间为东八区,2026 年 7 月 1 日晚):时间(UTC+8)谁干了什么07-01 21:47攻击者(经前端转发)bird-lgproxy 读 /etc/passwd(枚举用户、确认可读文件)07-01 21:53攻击者读 /root/.bash_history(翻历史命令,找线索/凭据)07-01 21:57攻击者读 /etc/shadow(拿到 root 密码哈希)用时序图看这十分钟的动作更直观,也能看清“攻击者 → 前端 → proxy → 文件系统”这条链路上,日志分别记在了哪一层:sequenceDiagram autonumber actor Atk as 攻击者(公网) participant FE as LG 前端 bird-lg participant PX as 节点 bird-lgproxy participant FS as 节点文件系统 Note over Atk,FS: 2026-07-01 晚(UTC+8) Atk->>FE: 21:47 GET -F /etc/passwd FE->>PX: 转发(源 IP = 前端) PX->>FS: mtr -F /etc/passwd FS-->>Atk: 用户列表回显 Note right of PX: proxy 日志只记到<br/>前端 IP,非攻击者 Atk->>FE: 21:53 GET -F /root/.bash_history FE->>PX: 转发 PX->>FS: 读取历史命令 FS-->>Atk: 命令历史回显 Atk->>FE: 21:57 GET -F /etc/shadow FE->>PX: 转发 PX->>FS: mtr -F /etc/shadow FS-->>Atk: root 密码哈希泄露日志里的 payload 长这样(URL 编码前):GET /traceroute?q=-F+/etc/passwd+<占位主机> GET /traceroute?q=-F+/etc/shadow+<占位主机>从 passwd → bash_history → shadow 的顺序很典型:先确认漏洞可用、再翻可能藏凭据的地方、最后直取密码哈希准备离线爆破。整个过程只花了十分钟。取证的一个坑:日志里的“攻击者 IP”是我自己的前端机这里有个值得单独拎出来讲的教训。bird-lg 是前端 + 后端 proxy 分离的架构:公网用户访问前端,前端再把 traceroute 请求转发给各节点的 bird-lgproxy。所以当我去被攻击节点的 proxy 日志里查“攻击者 IP”时,看到的源地址全是前端机器自己的 IP——因为对 proxy 来说,请求确实是前端发来的。真正的攻击者公网 IP,只存在于前端(反向代理 / 前端容器)的访问日志里。而等我开始做取证时:proxy 容器在升级时被重建,日志只剩当天;前端所在机器的反代访问日志已经轮转,最早的记录已经晚于攻击发生的时间;各节点 journald 的留存窗口也已经滚过了原始攻击时刻。结果就是:现存日志里所有 -F /etc/shadow 的请求,溯源后都指向我自己后来做的 PoC 验证和加固测试(payload 里还带着我自己打的 poc.invalid、codex-audit 之类的标记)。原始攻击者的真实 IP,随日志轮转丢失了。溯源时对每一条 -F /etc/shadow 请求的判定逻辑大致是这样,最后能落到“真实攻击者 IP”的路径全被日志留存挡死了:flowchart TD Q["现存日志里的<br/>-F /etc/shadow 请求"] --> M{"payload 带<br/>poc.invalid / codex-audit ?"} M -->|是| SELF["自己的 PoC / 加固验证"] M -->|否| T{"时间早于<br/>已知攻击时刻 ?"} T -->|否| SELF2["事后测试<br/>(仍是自己)"] T -->|是| L{"能查到<br/>前端访问日志 ?"} L -->|否:已轮转/重建| LOST["真实攻击者 IP<br/>已丢失 ✗"] L -->|是| REAL["定位攻击者 IP ✓"] style SELF fill:#d5f5e3,stroke:#27ae60 style SELF2 fill:#d5f5e3,stroke:#27ae60 style LOST fill:#ffd5d5,stroke:#c0392b,stroke-width:2px style REAL fill:#d6eaf8,stroke:#2980b9也做了兜底核查——确认没有异常后果:登录审计:所有成功的 root 登录源 IP 要么是我自己的出口,要么是我常用宽带 IP 段(带密钥登录用的是我自己的部署密钥),没有陌生境外 IP 成功登录,无横向移动迹象。authorized_keys:逐个核对指纹,只有我自己的密钥,无陌生 key。计划任务 / 进程:无后门 cron,无可疑常驻进程。教训很直接:面向公网的服务,一定要把访问日志的留存时间调长、并集中收走。等出事了再回头翻,日志往往已经不在了——尤其是这种“真实来源只记录在前端一层”的架构,取证窗口比你想的更短。漏洞报告与致谢发现异常后,我第一时间意识到这是个真实的安全问题,但我本身对安全研究并不在行。于是我把情况同步给了朋友 @KaguraiYoRoy,最终由他完成了漏洞分析、复现与规范的安全报告,一起负责任地披露给了上游作者。上游据此发布了官方安全公告并修复:GHSA-3qm5-22pm-wqg9 — Argument Injection / Exposure of Sensitive InformationCVE-2026-26514 · CWE-88(Argument Injection)+ CWE-200(Sensitive Info Exposure)CVSS 3.1: 7.5 High(AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N)影响版本:bird-lgproxy ≤ 1.4.6 · 修复版本:1.4.7 · 发布:2026-07-02致谢:KaguraiYoRoy(Analyst)、AndyYJF(Reporter)公告原文:https://github.com/xddxdd/bird-lg-go/security/advisories/GHSA-3qm5-22pm-wqg9借这次事,也想说一句:不懂安全不丢人,但发现问题别憋着。找个懂行的朋友一起走负责任披露的流程,比自己瞎折腾或者装作没看见都强得多。修复:升级到 v1.4.7需要强调的是:v1.4.6 的第一次修复并不彻底——它可以被无空格变体绕过,比如 -F/etc/shadow(选项和路径连在一起,绕过了当时基于空格的判断)。真正修好是在 v1.4.7:它对 traceroute 目标增加了 strings.HasPrefix(query, "-") 检查,任何以 - 开头的输入直接拒绝,从根上断掉了“把输入当选项”的可能。这样 -F /etc/shadow 会被直接拦下返回 Invalid target.,CVE 到这里彻底堵死。我把四台节点全部拉到了 v1.4.7。集群里有两种部署方式,处理方式不同:systemd 直接跑二进制的节点——换二进制 + 重启:cd /tmp curl -sLO https://github.com/xddxdd/bird-lg-go/releases/download/v1.4.7/bird-lgproxy-go-v1.4.7-linux-amd64.tar.gz tar xzf bird-lgproxy-go-v1.4.7-linux-amd64.tar.gz cp -a /usr/local/bin/bird-lgproxy-go /usr/local/bin/bird-lgproxy-go.pre-v1.4.7.bak # 留回滚点 cp -f bird-lgproxy-go /usr/local/bin/bird-lgproxy-go chmod +x /usr/local/bin/bird-lgproxy-go systemctl restart bird-lgproxyDocker 部署的节点——用 FROM scratch 的本地镜像,替换其中的 proxy 二进制后重建:# 构建上下文里就一个 Dockerfile + proxy 二进制 + 自带的 traceroute # Dockerfile: # FROM scratch # COPY proxy /proxy # COPY traceroute /traceroute # ENTRYPOINT ["/proxy"] cp -f proxy.v147 ./bird-lgproxy-v1.4.7/proxy docker compose up -d --build bird-lgproxy验证:用攻击者的原始 payload 打自己修复不能只看版本号,得用攻击者的真实路径去验证。我把原始 payload 通过公网前端重放到每一台节点:for node in <node1> <node2> <node3> <node4>; do curl -s "https://<lg-frontend>/traceroute/$node/-F%20%2Fetc%2Fshadow%20test.invalid" \ | grep -c 'root:' # 期望:0 done四台全部返回 Invalid target.,/etc/shadow 内容零泄露。直接对本地 :8000 打也是同样结果:curl -s 'http://127.0.0.1:8000/traceroute?q=-F+/etc/shadow+8.8.8.8' # 旧版:吐出 /etc/shadow 内容 # v1.4.7:HTTP 400 / Invalid target.收尾清单{x} 四台节点升级到 v1.4.7,保留回滚二进制/compose{x} 用原始 payload 经公网前端重放验证,四台 /etc/shadow 零泄露{x} 轮换全部 root 密码(≥16 位混合){x} 审计 authorized_keys 指纹、cron、常驻进程——无异常{x} 核查登录历史——无陌生 IP 成功登录几点反思别把用户输入拆成命令行参数。 这不是 LG 独有的坑,任何把外部输入拼进 exec 的地方都要警惕以 - 开头的注入。目标当整体字符串处理是底线。公网服务的 proxy 别裸奔在 0.0.0.0。 我这次四台的 bird-lgproxy :8000 都是公网直接可达的——下一步准备把它收敛到只允许前端访问(iptables / 只监听内网)。少一个公网入口,就少一层攻击面。日志留存要提前配好、集中收走。 出事后才发现真正的攻击者 IP 只记录在前端一层、而且已经轮转丢了。取证的黄金窗口比想象中短得多。本文用于记录与分享,已隐去全部真实主机名、域名与 IP。如果你也在跑公网 Looking Glass,建议立刻确认 bird-lgproxy 版本 ≥ v1.4.7。
2026年07月02日
17 阅读
0 评论
0 点赞
2026-06-26
RP Hub——更适合新手的类Silly Tavern项目
声明:此项目二改自 RP-Hub ,根据原项目作者要求,本人已得到原作者关于二改的授权。二改过程中使用了AI,若您对此反对可停止阅读前言众所周知,原版的Silly Tavern,动画是 没有 的,界面是 臃肿 的,对新手是 地狱 的。我一直想找到一个易用且现代的类Silly Tavern项目,某次偶然在B站看到了这个视频:{bilibili bvid="BV1yKSSBKERq" page=""/}可谓是正中下怀啊!但是仔细看了看项目发现,由于偏向仅前端开发,项目没有原版Silly Tavern的多用户功能,也没有多端同步能力,和我的预期相差较多,于是 秉承着Plus订阅不用白不用的态度 我决定手搓出多用户和同步功能。博弈于是我便把项目克隆,打开了Codex,开始了和AI博弈的两天。过程就不多说了,本次二改项目使用了GLM-5.2、GPT-5.5,Claude-Opus-4.6模型共同开发,由GPT主写代码,GLM进行代码审批, Claude负责摸鱼 。跌跌撞撞花了不少token,反正是做出来了。 成果二改项目我已经放在了 Github 在原纯前端项目基础上,新增了以下功能(详见 DEPLOY.md):多用户体系JWT 认证(用户名 + 密码 + 刷新令牌轮转)用户数据云端同步(基于时间戳的智能合并,非整体覆盖)本地模式 / 服务端模式可切换(未登录时与原项目完全一致)公共角色库(自建)用户提交角色卡 → 管理员审核 → 公共展示 → 其他用户下载与原作者的「万相广场」并存,侧边栏明确区分后台管理(admin.html)系统统计(用户/角色卡/登录趋势)用户管理(增删改查、封禁/解封、API Key 绑定、配额)角色卡审核(预览、通过、拒绝带理由、下架)公告管理(创建/编辑/置顶,前端主页自动展示)API 用量统计、审计日志站点公告系统管理员发布公告 → 前端主页弹窗展示(区别于原版本更新日志)
2026年06月26日
4 阅读
0 评论
0 点赞
2026-06-20
StableDiffusion学习札记_P1
前言之前就一直想玩StableDiffusion奈何没有合适的显卡。高考完终于买了个5080的笔电想着是时候开玩了~初步了解鉴于StableDiffusion的webui有段时间没更新了,我选择使用comfyUI作为可视化界面----更加现代化,并且拓展性更强开整1部署comfyUI对于新手来说,使用StabilityMatrix部署comfyUI是个不错的选择1.1建一个固定目录在一个空间比较大的盘,新建一个文件夹用于存放后续文件,比如:D:\AI1.2下载StabilityMatrix与安装前往github,在 这里 下载最新版release安装到:D:\AI\StabilityMatrix{alert type="warning"}注意:下载下来的压缩文件必须解压后运行,否则安装程序受限{/alert}第一次打开 Stability Matrix,通常会让你设置一个数据目录 / Library 路径。填写:D:\AI\AIData以后模型、包、缓存都存这个目录。1.3安装comfyUI打开 Stability Matrix 后,找到它里面的包 / Packages / Install入口。找到 ComfyUI安装 最新版其它设置都保持默认装完后,点击 Launch / 启动{alert type="warning"}注意,下载过程需要从github拉取release,若使用代理,请开启tun模式。{/alert}若一切正常,应该会出现这个提示:[INFO] To see the GUI go to: http://127.0.0.1:8188访问即可。2.加入模型,创建工作流2.1模型获取新手可以直接使用我在网上找到的资源。只有度盘链接还请见谅 {cloud title="SD" type="bd" url="https://pan.baidu.com/s/1v2g_4hKDafaS9tcYEouYxA?pwd=1ajk" password="1ajk"/}或者前往 C站 寻找模型{collapse}{collapse-item label="附C站Content Moderation设置指南" close}代理 访问https://civitai.red/登录账号点击右上角个人图标,选择最下方的齿轮图标进入账户设置找到Content Moderation修改注:一定要使用代理,也不要访问.com域名{/collapse-item}{/collapse}2.2模型识别模型的识别比较重要,使用错误的模型+Lora会导致报错首先是 模型文件类型 建议直接识别后缀1..safetensors / .ckpt 这个属于Checkpoint(大模型主体),可以单独出图模型代数识别:sd15 / 1-5 / v1-5 → SD1.5sd21 / 2-1 → SD2.1xl / SDXL → SDXL2..safetensors 这个属于LoRA(风格/角色插件)不能单独出图,必须 + base model3..vae.safetensors这个是VAE,是用来改善颜色/对比度的4..pt / .bin这个属于Embedding / Textual Inversion,是用于用于触发关键词的2.3创建工作流打开ComfyUI,你应该能看见一个空白画布双击左键,搜索 Load Checkpoint ,添加节点双击左键,搜索 CLIP Text Encode ,添加节点(要添加两个,这是正向和反向提示词的输入处)双击左键,搜索 KSampler ,添加节点双击左键,搜索 VAE Decode ,添加节点双击左键,搜索 Save Image ,添加节点连线规则: 从 Checkpoint加载器(简易)连出去:模型 → KSampler 的 modelCLIP → 两个 CLIP文本编码 节点的 clipVAE → VAE Decode 的 vae 文本节点:正向 CLIP文本编码 → KSampler 的 positive负向 CLIP文本编码 → KSampler 的 negative Empty Latent Image:输出 → KSampler 的 latent_image KSampler:输出 → VAE Decode 的 samples VAE Decode:输出 → Save Image 的 images 3.开始作画3.1填入提示词正向提示词填到第一个 CLIP 文本编码里:masterpiece, best quality, ultra detailed, 1girl, portrait, soft lighting, detailed eyes负向提示词填到第二个 CLIP 文本编码里:low quality, blurry, bad anatomy, extra fingers, deformed hands, watermark, textEmpty Latent Image 设置:width: 1024 height: 1024 batch_size: 1KSampler 设置:steps: 28 cfg: 6 sampler_name: dpmpp_2m //自行选择,推荐这个 scheduler: karras最后点击 运行 成图:
2026年06月20日
11 阅读
1 评论
0 点赞
2026-06-11
博客封面是怎么来的?
这个博客上的封面是怎么来的?其实是用 GPT-Image-2生成的随手让它写了个prompt可以保持创作风格:我会给你一篇技术博客原文,请你根据文章内容生成一张博客封面图。 要求: 1. 先理解文章主题,不要逐字照搬正文。 2. 封面用于博客首页卡片展示,显示区域较小,所以画面必须简洁、清晰、缩略图可读。 3. 图片比例为 3:2,横向构图,例如 1200x800。 4. 风格为深色科技风、蓝紫霓虹、现代极客风,适合技术博客。 5. 不要做复杂信息图,不要大量小字,不要密集代码,不要完整教程步骤。 6. 画面只保留一个大标题、一个短副标题,以及 1 到 3 个能表达主题的简洁图标或抽象元素。 7. 中文标题要大、粗、清晰,距离边缘留足安全边距,避免被博客卡片裁切。 8. 右侧可以放终端、服务器、网络连接、代码窗口、流程节点、工具图标等抽象元素,但不要太复杂。 9. 不要水印,不要真实品牌 Logo,不要生成过多装饰。 10. 如果文章主题偏运维、Linux、网络、自建服务,请优先使用终端、服务器、连接线、状态图标等视觉元素。 请根据下面的文章原文,自动提炼: - 封面主标题 - 封面副标题 - 画面主体元素 - 最终生成一张 3:2 技术博客封面图 文章原文如下: 【在这里粘贴文章全文】
2026年06月11日
14 阅读
0 评论
1 点赞
1
2
3