在 RISC-V 板子(哪吒 D1)上安装 Arch Linux

为什么是这块板子

一个月前,在操作 MirrorZ 项目时,我了解到肥猫建的镜像站——武昌首义学院开源软件镜像站,在其中发现一个申必的项目:archriscv,我当时表示震撼猫猫。

过了一段时间,也就是一周前,我心血来潮想找块 RISC-V 板子玩玩,试一下 archriscv(别问我为啥不用 qemu 玩),于是问肥猫有没有消费级板子,肥猫表示可以看看这块。

为了论证这块板子能装上 Arch Linux,我看了一些教程和文档,不过发现结果不尽人意,不仅需要用 Windows 特有工具来烧录固件(我手头甚至没有一台 Windows 机器),其内核也较难 hack(源码需要注册后下载,编译需要奇妙虚拟机,生成的镜像是特殊的),这对于我来说非常难受。

不过好在,在 Icenowy 的介绍下,我找到了 https://linux-sunxi.org/Allwinner_Nezha 这个页面,其中给出了一个比较舒畅的折腾方式(主线内核!),至少我能自己折腾分区表和内核了。

所以有两条路,要么采用厂商附带的分区表、内核,要么自己编译,我更倾向于后者,不过保险起见,我先尝试了前者。

厂商附带

由于厂商镜像必须得使用 Windows 特有工具来烧录(Icenowy 并不推荐 hack 这个分区表),我只能找一个 dd 出来的镜像,由于我买的时候没有附带 SD 卡,我前往相关论坛找资源,可惜下载下来均不能用。幸好,我找到了二老师,二老师刚好有一份 dd 出来的备份,于是我拿到了一个能工作的镜像!

该镜像的基础版本是 sipeed debian hdmi,是二老师用 Windows 烧录后 dd 出来用于备份的,避免每次需要重新用 Windows 烧写。

我在 dd 后,成功启动!

可惜折腾了一阵后,发现内核缺了不少东西,自己也编译不了相关模块,我准备随便弄一弄就交差了。按照 sipeed 的说法,只需要自己换一个 rootfs 就行,于是我直接把 sipeed 镜像的 debian rootfs 删了换成 archriscv 的,指令如下

1
2
3
4
mount /dev/mmcblk0p7 /mnt
cd /mnt
rm -rf *
tar xf ~/archriscv-20210601.tar.zst

然后对该 rootfs 做些调整,如何调整参见后文。

之后就是上电启动了,我没去验证图形界面,只试了串口和 ssh。

注意其中内核版本。

主线内核

https://linux-sunxi.org/Allwinner_Nezha 中已经非常详细了,在做完这个页面的步骤后即按照 Manual Build Howto 页面的步骤走即可,其中一些参数随着情况改变就行。

一些构建小建议:

加速内核代码下载

1
2
3
4
5
git clone https://mirrors.tuna.tsinghua.edu.cn/git/linux.git
cd linux
git remote add smaeul git@github.com:smaeul/linux.git
git fetch smaeul riscv/d1-wip
git checkout smaeul/riscv/d1-wip

交叉编译环境构建:由于我用 NixOS,所以……

1
2
3
4
5
6
7
8
with import <nixpkgs> {
crossSystem = {
config = "riscv64-unknown-linux-gnu";
};
};

mkShell {
}

内核编译时还有一些 HOST 依赖,我是这样用的 nix-shell -p gcc flex bison pkg-config ncurses openssl bc swig libelf gmp zlib libmpc pahole

不过在这里要提醒一点,现在该内核、glibc、工具链等的相性不是很好,一些服务(乃至 systemd 本体)在启动时容易 SEGV,最终导致 kernel panic 然后不能启动,我的解决方案是,将所有 SEGV 的服务都 systemctl mask 掉!

同时,其他不少软件(基本在涉及网络的地方,比如 curl,比如 pacman)都会 SEGV,导致我装软件包只能按照后文中所提的那样,拆下 sd 卡后 chroot 进去装。当然,我们发现 ssh 是能工作的,所以也可以把包 scp 上去然后 pacman -U 安装。

SEGV/panic 的具体情况可以参考 https://gist.github.com/heitbaum/e4dceeb7b236560b94cc66fce91cdd11

注意其中内核版本,既有 patch 也有我本地修改,按过不表。

rootfs 调整

首先修改 /etc/fstab,使得根目录是 rw 的(这能避免一些 SEGV)

1
/dev/mmcblk0p2  /       ext4    user_xattr,errors=remount-ro    0       1

之后替换镜像源

1
sed -i 's_archriscv.felixc.at_mirrors.wsyu.edu.cn/archriscv_g' /etc/pacman.conf

顺便,据肥猫介绍,archriscv-20210601.tar.zst 的 root 密码是 sifive,而比这个更早的镜像没有密码(不过有些小坑在里面)

顺便, archlinuxcn/binfmt-qemu-static 相当好用,arch-chroot 进去以后直接调整各种配置也比较清爽。

镜像下载

镜像下载估计会托管到 archriscv 那边,不过按照目前的情况看,使用厂商内核的镜像可以直接下载 sipeed 的 debian 镜像然后更换 rootfs,没啥技术含量;而折腾主线内核的同学应该有精力自己编译,该内核目前也还不稳定,下载后发现基本不能用反而影响不好,所以没必要提供下载。

Bluetooth PAN Setup

缘起

由于我有部分设备没有 RJ45 接口,我也不想买扩展坞或交换机,于是准备在无线上下个功夫,可惜清华的无线网络过于拉跨,除了巨大的延迟与延迟方差,还有认证和下线等问题,实在难受。

我日常会将我的键盘和鼠标「ssh」 到别的机器上(类似于要聚餐了我 ssh 过去吃饭),所以对延迟非常敏感。如下图,我用左边的上古机器(上古机器的键盘真的舒服啊)ssh 到右边的平板(没有 RJ45 接口)聊 Telegram。

这时我注意到了我的机器上基本都有蓝牙。由于我的内网机器都是 Linux 机器,所以自由度还是很大的,于是开始了解相关概念。

Fugoes 以前弄过 ethernet over bluetooth,具体内容如下

1
2
3
4
5
6
7
# for server:
sudo ip link add br-blue type bridge
sudo ip link set br-blue up
sudo bt-network -s nap br-blue

# for client:
sudo bt-network -c <server's MAC address> nap

在现在的我看来,这个教程已经很精髓了,足够囊括所需,但对于小白来说太不友好了,所以我这篇博文意在扩充以上的命令,给出一些先决条件和准备。

PAN (Personal Area Network)

PAN 和 LAN、WAN 等概念是一系列的,它更注重个人身边区域设备的连接情况,这个情况下用蓝牙是非常不错的选择。

为了构建 PAN,蓝牙实现了一种协议 BNEP,能够搭建一个二层隧道。在此基础上,由于程序的实现,我们使用一个 bridge 来连接这些隧道,构建一个完整的二层。

在目前的实现(bt-network)中,这个 bridge 是实现在服务端的;由于蓝牙的限制,一个服务端只能连接 7 个客户端,也就是说整个 PAN 内最多只能有 8 台设备。经典的网络拓扑是这样的

1
2
3
4
5
6
7
8
server:       bnep0--bridge--bnep2
server: / | \
server: / bnep1 \
/ | |
/ | |
client0: bnep0 | |
client1: bnep0 |
client2: bnep0

当然我们可以做一些更加高级的组网方式,绕开蓝牙的数量限制,比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server:       bnep0--bridge--bnep2
server: / | \
server: / bnep1 \
/ | |
/ | |
client0: bnep0 | |
client1: bnep0 |
client2: bnep0
client2: |
client2: bridge
client2: |
client2: bnep1
|
|
|
client8: bnep0

不过确实一般人没有这么多蓝牙设备,按下不表。

配对——预处理

为了建立 BNEP 隧道,我们需要先配对两个设备,一般而言两边设备都会走以下流程

1
2
3
4
5
6
7
# start bluetoothd daemon
systemctl start bluetooth
bluetoothctl
[bluetoothctl] power on
[bluetoothctl] pairable on
[bluetoothctl] discoverable on
[bluetoothctl] scan on

之后便是配对,只需一方发起,不过需要双方确认

1
[bluetoothctl] pair <MAC of peer device>

这里的 MAC 地址需要根据自己的设备情况填入。在 pair 成功后,即可进行以下组网操作。

组网

服务端

按照程序的一贯特点,bridge 是需要我们手动启动的,所以我有如下脚本。

1
2
3
ip l add pan0 type bridge
ip l set pan0 up
ip a a 10.0.111.1/24 dev pan0

之后分别在两个控制台中启用两个服务

1
2
bt-agent
bt-network -s nap pan0

客户端

1
2
bluetoothctl
[bluetoothctl] connect <MAC of server device>

由于 connect 后如果没有服务与活动,该连接会自动断开,所以我们需要在链接刚好启动的时候,在另一窗口运行以下命令

1
bt-network -c <MAC of server device> nap

如果命令成功,我们就可以 ip l 看到一个新的设备啦

1
2
3
4
5
$ ip a show dev bnep0
229: bnep0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000
link/ether 28:3a:4d:7e:ec:0e brd ff:ff:ff:ff:ff:ff
inet6 fe80::2a3a:4dff:fe7e:ec0e/64 scope link
valid_lft forever preferred_lft forever

这时我们就可以配上地址并测试联通性了

1
2
ip a a 10.0.111.2/24 dev bnep0
ping -c1 10.0.111.1

一些日志

服务端

连接成功后会有以下日志

  • bt-agent
    1
    2
    3
    4
    $ bt-agent
    Agent registered
    Default agent requested
    Device: client (MAC) for UUID xxxxxx
  • bt-network
    1
    2
    $ bt-network -s nap pan0
    NAP server registered

客户端

1
2
$ bt-network -c <server MAC> nap
Network service is connected

延迟

1
2
3
4
5
6
7
8
9
10
$ ping -c4 10.0.111.1
PING 10.0.111.1 (10.0.111.1) 56(84) bytes of data.
64 bytes from 10.0.111.1: icmp_seq=1 ttl=64 time=28.6 ms
64 bytes from 10.0.111.1: icmp_seq=2 ttl=64 time=22.9 ms
64 bytes from 10.0.111.1: icmp_seq=3 ttl=64 time=22.8 ms
64 bytes from 10.0.111.1: icmp_seq=4 ttl=64 time=34.7 ms

--- 10.0.111.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 22.836/27.244/34.665/4.871 ms

带宽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[server] $ iperf -s
[client] $ iperf -c 10.0.111.1 -t 10 -i 2
------------------------------------------------------------
Client connecting to 10.0.111.1, TCP port 5001
TCP window size: 85.0 KByte (default)
------------------------------------------------------------
[ 3] local 10.0.111.2 port 51094 connected with 10.0.111.1 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0- 2.0 sec 640 KBytes 2.62 Mbits/sec
[ 3] 2.0- 4.0 sec 512 KBytes 2.10 Mbits/sec
[ 3] 4.0- 6.0 sec 768 KBytes 3.15 Mbits/sec
[ 3] 6.0- 8.0 sec 785 KBytes 3.22 Mbits/sec
[ 3] 8.0-10.0 sec 829 KBytes 3.39 Mbits/sec
[ 3] 0.0-10.3 sec 3.45 MBytes 2.82 Mbits/sec

问题与解决

bluetoothctl waiting to connect to bluetoothd

要么没开 bluetooth.service(这是Arch Linux有的,别的发行版请自行寻找等价服务),要么就是蓝牙设备还没有转换过来(常见与Dell的一些蓝牙和Wifi在一张板子上的),这时候需要 hid2hci 这个命令。

bt-network -c Network service is not supported by this device

这时候你还未连接已经配对好的设备,所以说 Network service 不支持啦。这时候需要在 bluetoothctl 中 connect 服务端,并在连接还未断开时运行命令

bt-network -c segmentation fault (core dumped)

服务端没有启动 bt-agent

闲话

  • 网上的教程都巨大古老了,然后最坑人的是,bluez 这群人非常折腾,许多工具都在版本迭代中没有了,弄得到处碰壁
  • 蓝牙栈确实没有什么很好的工具,要不是真的想折腾与利用,建议不要折腾。尤其是,组了蓝牙网络以后,其可能并不好纳入已有的网络(比如地址段)。对我来说,我是搭建 overlay network 作为内网的,所以下层越丰富越好
  • 目前我的内网里有Ethernet,有Wifi,也有蓝牙;有x86,也有arm;有教育网,也有移动网,真是包罗万象了。最后附上一张我内网的拓扑图吧(由于是 full mesh 内网,其拓扑会时常变动,这只是一个截面)。

将 Kindle 作为状态屏

前言

最近又开始摸鱼。很久以前有个通知中心的想法(目前这个想法在另一个项目中实现),主要是我注意到我拥有的屏幕并不少(手机平板小电脑Kindle),那么怎么显示这些通知呢;在想这个想法的时候,突然发现,我的 Kindle 还没法自由掌控呢,于是我开始折腾。

然后有幸越狱(最近刚出了一个利用方式,影响范围还很广,很久没开机的 Kindle 可能都可以利用),有幸拿到 ssh(指中途被自动更新打断差点吓死),有幸摸清了一些服务,有幸找到网络上一系列 边边角角 的技术讨论,结合自己一些 Linux 使用经验,整理了以下路径。

此处是博文,本项目地址在 https://github.com/ZenithalHourlyRate/kindle

kindle 越狱/ssh

这个参考 https://bookfere.com/post/tag/kindle%E8%B6%8A%E7%8B%B1 即可。

做完这步之后,你能熟练地通过 usb 来 ssh 上你的机器。通过 wifi 的方式需要在更改 iptables 之后做到(清华校园网特性)

我的体验就几点

  • 在拿出吃灰的 Kindle 查看固件时以及走流程之后直到 Hotfix,都要开启飞行模式!不然自动更新了都没法哭;
  • 我在 Hotfix 并装了几个插件后,立即被自动更新了,KUAL 就打开失败了,然后只能重装了一次插件(应该是自动更新覆盖了一些文件);
  • 要装这个插件:usbnet。首先要使它开机自启「Enable SSH at boot」,这很重要!这是我们之后长期的调试方式!注意到 KUAL 中 usbnet 的「Toggle USBNetwork 」,这个是用来切换模式,USBMS 和 USBNET,前者就是插上以后能挂载盘(USB Mass Storage),后者插上去以后 ip l 多出一个新的 interface(具体名称请自己替换),然后可以这样访问机器
    1
    2
    3
    ip l set enp0s20f0u2 up
    ip a a 192.168.15.1/24 dev enp0s20f0u2
    ssh root@192.168.15.244
  • 这种模式切换是跨启动的,所以说在切换了一次以后,查询一下状态,之后便不用调整了;不过建议多切换一下模式,体验程序的工作方式
  • KUAL USBNET 中显示的状态是当前的状态,而不是点击以后进入的状态
  • 此时先不要开换用 openssh,继续使用 dropbear,等到换了端口以后再换服务器
  • 装一个禁止休眠的 helper,在休眠后 usb网卡会掉线,从而 ssh 掉线

然后一些评论:

  • 网上有说弹出但不物理拔出 Kindle 来启动 usbnet 的方法,这个方法在 Linux 下似乎不是很好操作
  • 有很多同学热衷于装很多插件,但实际上在 kindle 自己的服务框架内跑服务,是非常吃内存的,在 kindle 完全开机后,我只剩余100M以下的内存,但在关掉 kindle 自己的框架以后,我能剩余接近 300M 的内存。举个例子,自定义屏保插件在每次屏保时会消耗 30~80M 的内存,还不会自动 GC(当时我拿着 top 监控,就怕 oom 了)
  • 作为 Linux 用户,我们的最终目的就是要干掉 Kindle 的主服务,然后跑自己的服务。网上有一些 chroot 装 Arch Linux/Debian 的帖子,我为内存而担心啊。
  • 不过我们不要完全抛弃 Kindle 的主服务,比如其提供的亮度调整,Wifi 连接,还是能帮助我们很多的。
  • 经过探索,发现这个内核不支持 IPv6,也不支持 tun/tap ,对于不能进入我的内网,我表示遗憾。

调整 IP 桌子

注意到 /etc/upstart/network.conf 中有 iptables-restore < /etc/sysconfig/iptables,我们查看文件可见几个关键规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]

# TCP handling. Allow incoming TCP TPH on WAN
-A INPUT -i wlan0 -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT

# Usbnet. All traffic allowed.
-A INPUT -i usb0 -j ACCEPT

# ICMP. Allow only responses to local connections
-A INPUT -p icmp -m state --state RELATED,ESTABLISHED -j ACCEPT

COMMIT

我们需要调整一些规则,比如放行我的 ssh 端口(usbnet 这个插件在启动时会自己加规则,所以不需要给 22 加规则),比如放行 icmp。注意,在调整前建议备份。

1
2
-A INPUT -i wlan0 -p tcp -m tcp --dport <my_ssh_port> -j ACCEPT
-A INPUT -p icmp -j ACCEPT

更改 ssh 端口

重启或手动调整 iptables 规则,确保这时候我们的桌子是正确的。

这时候修改 /mnt/us/usbnet/etc/sshd_config (该配置不影响 dropbear) 中的端口为 <my_ssh_port>,然后在命令行中手动启动 /usr/sbin/sshd ,尝试是否能连接,如果能连接,则在 KUAL USBNET 中将默认启用的 ssh 设置为 openssh。

调整后建议重启以验证并再次尝试 ssh,确保服务如预期进行。

DDNS

创建 /etc/upstart/ddns 的脚本(记得 chmod +x)(或者可以放在其他位置,比如 /usr/local/bin/,但不能放在 /tmp/root 中),内容为你的 DDNS 提供商的脚本,之后在 /etc/crontab/root 创建 */5 * * * * /etc/upstart/ddns 的项目。

建议先手动运行相关脚本,测试脚本的正确性。

swapfile

由于内存捉急,我开了一个 swapfile。注意,该文件不能在根 / 下!根下空间不多。

1
2
3
dd if=/dev/zero of=/mnt/base-us/swapfile bs=1M count=200
mkswap /mnt/base-us/swapfile
swapon /mnt/base-us/swapfile

然后在 /etc/fstab 的最后加上

1
/mnt/base-us/swapfile none swap defaults 0 0

但是观测发现 Kindle 并不会自己挂载 swap,所以我们需要一个 helper 在开机后执行 swapon -a,这个参考 zhelper 文件夹(还没写)。安装方法就是将 zhelper 目录放在 /mnt/base-us/extensions 目录下。

状态屏

这个的主要灵感是 https://github.com/mpetroff/kindle-weather-display/blob/master/kindle/display-weather.sh,发现了其中有展示图片的方法。

目前的架构是,Kindle的主服务框架依然开机自启,以便我们进行必要的调整(比如连接 Wifi,调整亮度),我们在 KUAL 中通过 Helper 启用该模式(会关闭电源管理),而这个文件的更新是通过服务器 scp 到 Kindle上实现的 (约定位置为 /tmp/screen.png),并触发相关渲染任务(当然也可以 Kindle 轮询实现,请 PR)。由于主服务未关,我们可以随时切换到主服务中进行调整。

这样在 Kindle 闲置时能自动 播放 屏保,非常有意思(不过也相对耗电了)

当然我们也提供了关闭主服务(Close Framework)的方法,如果想要在重启主服务,需要重启或 ssh kindle initctl start framework

该服务的安装方法是将 screen/extensions/screen 文件夹放在 /mnt/us/extensions 文件夹下;将 screen/screen 文件夹放在 /mnt/us/ 文件夹下。

由于图片的收集和渲染是服务端实现的,我们只需要保证传输到 Kindle 上的格式符合其需求,我们提供了一个帮助脚本 scripts/png8.sh ,方便转换图片。需要自行修改脚本中的分辨率。

同时提个了一个样例部署脚本 scripts/server.sh

杂项

  • 在翻 /etc/network/interfaces 发现 bnep0,注意到被闲置的蓝牙
  • /var/log/messages 是整个系统启动的 log,可以用这个查看 Kindle 的服务启动顺序。
  • 服务的错误信息都在 /tmp/some\_app.err
  • 类似于 systemctl,Kindle 有 initctl

未来可能的方向

  • 安装 Debian/Arch Linux
  • 更换内核,开启 IPv6 / TUNTAP 的支持
  • 给 Kindle 上蓝牙组网

从Ant Design GitHub失窃案谈开去:国内有可用的2FA吗

TL;DR

本文是 https://canokeys.org 或(Telegram: @canokeys)的硬广,但本文并非 Canokeys 官方发布。

事件本身

2021-02-15,UTC+8,早晨的群友发现 https://ant.design 404,后官方推特向GitHub反馈表明帐号失窃,相关项目被转移。

群友讨论

不少群友在感叹相关人员水平之后(延伸材料),灵魂发问:为啥该失窃员工没有对帐号开启2FA(多重因素验证)呢。我的简单回答是:很难,或者没法合法开启。本文不探讨「相关人员水平」或「相关组织管理(指组织强制所有成员开启2FA)」的问题,我们来探讨一下「国内的2FA可用性」。

GitHub 2FA

我们知道,现代的登录系统,除了密码,还需要通过其他的验证方式来验证「登录者是帐号所有者」,例如「SMS验证码」,「验证应用(Authenticator App)」,「安全密钥」等,这些验证方式自然也被GitHub所使用,但是,国内的GitHub用户真的能「合法、廉价、易用」地使用相关方式吗。

  • SMS验证码:在GitHub该界面中,没有+86用户的选项,而要获取其他区号,除了少部分人(相对于GitHub国内用户基数),基本无法「合法」做到。
  • 验证应用(Authenticator App):前有Google Authenticator后有Microsoft Authenticator,更别说开源的Authy,andOTP等工具,但是,除了Google Play商店或者F-droid,我们基本无法在国内的应用商店中找到这些软件,就算找到了,相关软件也需要Google的服务框架(例如小米应用商店中的Microsoft Authenticator),所以这不「易用」,甚至不「合法」,而有些软件是需要搭配硬件密钥使用的(Yubikey,Nitrokey,Google Titan),这些并不「廉价」,甚至进口这些硬件密钥似乎也不「合法」
  • 安全密钥:也就上文提到的那些硬件密钥,同时还有 Solokeys,U2F-zero 等相关密钥,但这些即不「廉价」(一个用户需要多个密钥用作备份),也不能「大批量购买」(对企业用户来说,这是最大的阻碍,而Ant Design是企业用户)

糟糕透顶的国内密码学实践

上述问题,可以推广到大量实践了2FA的应用。

登录认证,作为基础设施中的基础设施,是应该有一套完整的实践的,但现在各个环节缺失,帐号的安全性如何保障?

登录认证,作为基础设施中的基础设施,是应该有一套「自主可控」的软件与硬件系统的(这里是严肃的「自主可控」,而不是只是外国软件套皮)。比如说,国内的企业会信任Yubikey吗,会信任Nitrokey吗,依赖于Google Play的「验证应用」能被国内用户、国内企业使用吗。

国内目前的相关2FA实践是手机验证码,但似乎这成了必选项,而不是可选项,理想中的2FA应该是其他的验证因素也足够验证,这样大家为「手机不在身边」,「手机号被盗以至于银行卡被盗刷」而烦恼。另外值得一提的是「网银盾」这种2FA,需要更现代、开源的实现,比如WebAuthn,而不是国产不知名,无法审核的插件。

国内的2FA解决方案

CanoKey由清华大学的一些老师/学生(同时也是开源社区的大佬)所写,有软硬件(包括PCB设计)皆开源的stm32版本,也有使用密码学芯片的发售版本(其核心功能一致,只是速度较大差别),可供国内广大用户、企业选用,满足其硬件密钥乃至安全性需求。

这是「合法」的(参考《密码法》),「廉价」的(比市场同类商品价格低,功能更加丰富),「易用」的(相关软件已经发布)。

同时,用户可以体验虚拟硬件密钥,进行构建/购买前的测试与游玩!同时也欢迎用户参与核心代码库的贡献,您的贡献可能出现在下一版的发售中!

Link Preview May Reset Your Password

TL;DR

Link preview provided as a feature by webmail/clients such as Outlook or IM like Telegram may cause unexpected behavior like unwanted account register confirming, automatic mailing list subscribing or even infinite password resetting through links with special functionality.

The Story and Other Stories

Official Account Used For Registering

One morning, I opened OUR mailbox, and found an address validation letter with a confirming link lying there. Spam, I thought, as this email address is used as a official account, no one in our organizaiton would use it for personal account registration, but right before I close this mailbox, I was suddenly reminded of something.

The day before this morning, we happily tested a new functionality provided by the webmail of Outlook – Link Preview. Well since we are already modern now, with modern front-end and modern webpage, what’s wrong with link preview? This is what we all expected! This is how modern front-end should be like!

But wait, would link preview visit every link in this email? If it does, right after I opened this email, the validation would be done!

Even worse, I cannot check whether the validation is over since the registered account is not in my hand and I dare not click on that confirming link.

After some discussion within our org, we think that there may be some allow lists that prevent the preview of certain sites, but if the list is not a list of REGEX, when I register the account of popular platforms, the link would still be previewed.

So the registered account still hangs there, we do not know its status, and bad thing can be done with that account.

After I discovered this issue, I come to a broader concern on similar issues. And after posting this issue among my friends, more stories are collected and more exploits can be done.

One story is that one user of Telegram gmailbot received a validation letter one day, and after they opened that bot, the message of confirmation success was received right away. The mechanism of preview of Telegram is to preview every first link of one message unless the user cancels that.

Another story is that one user of one email provider used that email for some account registering, and one day they forgot their password. For that site, password reset are done by a special link sent to the email address associated with that account, but for the sake of security, the email provider checks every link in the email(common behavior of big providers, maybe), hence the password reset link would have been accessed once when the user sees the email, and the reset cannot be done successfully. Since this security check is a black box and unrevealed to that user, the user tried almost infinitely and asked the admin of that site to debug.

Spamming, DoS, Fake Identity, Misbehavior and Even More

  1. When subscribing a mailing list on web, a confirmation letter would be sent, in some forms it asks you to reply to that email containing certain string, but mostly it shows you a validation link. This may be used for spamming hence DoS of one account; it can also be used for DoS of one mailing list as all subscribers are ‘valid’.
  2. When you upload a PGP public key to a keyserver like OpenPGP, it sends validation letter. Hence fake public key of one identity can be made with confirmation, and without revocation.
  3. As the last story above indicates, password reset can never be done, or done by the provider.
  4. When there are one-time special functional links(common behavior), and the provider/client automatically access that link in any form, there are unexpected behaviors.

Related Report

Ref to this post https://www.macrumors.com/2020/10/26/link-previews-may-lead-to-security-vulnerabilities/

It talks about information leaking and malicious payload, while I am from the perspective of the protocol itself.

Conclusion

Providers, don’t touch my thing.

Automation/Rules without turn-off options are bad.

赶/敢PRE

背景

我们组作为搞系统架构的,总是要看一些系统架构的文章;MICRO 这个会议作为搞微架构的会议,当然是入了老师法眼的,于是一段时间内的组会内容变成了 MICRO 20 文章选读。

这个选读,自然是同学们来做,当时我去得晚了一些,发现和我相关的文章都被选走了,然后其他的讲具体加速器设计的也没啥意思,做内存的和我也没啥关系,正愁选啥呢,然后就看到了最佳论文候选,一篇讲量子内存微架构的文章,看起来很有意思,于是我选了。

摸鱼

但是啊,选了以后,我一直想看,但一直没空看,或者说没有找到可用的空来看,于是直接摸鱼到了PRE那天的前一天晚上。

慌张

前一天晚上了,怎么办!

只能硬上呗。可惜我不懂量子计算机,这种东西看着头痛。还好学过一点量子计算,应该是能看的吧?

万幸的是,这篇文章的 Background 讲得相当足,然后图片相当有表现力,然后想法也非常直观,于是看的差不多了。

这时候已经是 PRE 那天早上4点,我想,slides 先不急,先出去吃个饭嘛。

睡觉

于是便利蜂走起!然后吃完以后觉得浑身舒服,于是滚回寝室睡觉,这时候是早上6点。

一个小时!

然后起床,惊吓,中午12:20了!而组会在13:30,我 slide 还没动手呢!

于是我花了 10min 赶到地方,开始疯狂铺垫相关背景。

到 13:25 的时候,背景差不多了,该讲文章的主要贡献了,但没时间了!

于是我把两张图直接丢上去,各成一页,然后匆匆赶往目的地。

坑蒙拐骗

幸好组里没什么人懂量子计算机,蒙混过关!

从 Signing Party 到年轻人的第一次 Revoke

前情提要

开源软件供应链2020峰会的邀请下,以及TUNA的派遣下,我与来自UOJ的两位同学billchenchina以及Ciel面基,并举行了一次奇怪的signing party。

举办 Signing Party

首先我们交换了公钥,对照了公钥指纹。

然后我们验证了ID,无非是常规的身份证(如果 UID 中有实名的话),学生卡,以及其他ID。

以下步骤可以非面对面进行。

然后我们商议决定,除了要验证邮箱,我们还需要验证私钥的持有性,即验证各个私钥的功能性。

于是我们采取了以下握手方案:

  1. A 向 B 发送用 A 签名用 B 加密的「信息」,「信息」中包含发方与接收方邮箱,以及一些字符。(B 收到后即可验证 A 的 S 能力与邮箱的持有性)
  2. B 将 「信息」附在其邮件中,以「—BEGIN RAW MESSAGE—」包裹,并增加B相关的一些字符,形成「信息二」,签名并加密,发送给 A 。(A 收到后可验证 B 的S,E以及邮箱的持有)
  3. A 将 「信息二」附在邮件中,附上对 B 公钥的签名,并将加上字符形成「信息三」,签名加密后发给 B。(B 收到后可验证 A 的E,C,并收获签名)
  4. B 将 「信息三」附在邮件中,附上对 A 公钥的签名,形成「信息四」,签名加密后发给 A。(A 收到后可验证B的C,并收获签名)

上述方案对于一个 UID ,一组ES密钥的用户较为友好,对于多个 UID,多组ES密钥的用户,需要重复其中一些步骤才能进行下一步。

对于 A 密钥,我们并未验证,不过可以给一个中心服务器,上传各个人的公钥,只要验证其能登录上去,即可。

按签名的性质来说,我们只需要验证UID,也就是验证 ID 与邮箱与指纹即可完成 Signing Party 的前半段,后半段只需要交换签名。我们这次进行了一定的加强。

出锅

在第三步时,我应该对 A 的公钥进行签名,然后发现我找不到我的主私钥了。

我导出的私钥显示为(使用 pgpdump

1
2
3
4
5
6
7
8
Old: Secret Key Packet(tag 5)(277 bytes)
Ver 4 - new
Public key creation time - Wed May 1 17:54:39 CST 2019
Pub alg - RSA Encrypt or Sign(pub 1)
RSA n(2048 bits) - ...
RSA e(17 bits) - ...
Sym alg - Plaintext or unencrypted data(sym 0)
GnuPG gnu-dummy (s2k 1001)

注意到,其只有 n 与 e,而没有其余参数,例如

1
2
3
4
Encrypted RSA d
Encrypted RSA p
Encrypted RSA q
Encrypted RSA u

回溯历史,发现当时我导出「主」私钥的参数为

1
2
9603  gpg --armor --export-secret-subkeys '23470D4FD461AA84!'
9610 gpg --delete-secret-keys 23470D4FD461AA84

其中 23470D4FD461AA84 为主钥的keyid,但由于命令是 export-secret-subkeys,在此keyid下找不到相应子密钥,所以导出来的私钥是空的,只有公钥与UID与一些签名。

所以在导出时应该注意!并查验导出私有钥匙的文件!可以通过 pgpdumpgpg --show-keys filegpg --list-packets file 来查验。

Revoke

找不到私钥,但有预先生成的吊销证书,我于是将其挂载在公钥上后上传,成功吊销。

年轻人的第一次 Revoke。

新公钥

目前新公钥在 https://blog.zenithal.me/key 中,其指纹为 1127F188280AE3123619332987E17EEF9B18B6C9

由于进行过 UID 验证,重启握手后完成了 Signing Party。

Mutt 配置:一种实践的部署方式

万事在命令行中完成,这是个美好的愿景,也是许多人的实践。

但当我们发现,连日常处理邮件都需要开一个网页或开一个图形化客户端才能完成,而没有可用的命令行客户端时,我们是失望的。

尤其是当我们需要对邮件进行自动化与控制,普通的图形化界面已经满足不了时,我们渴求着脚本,我们期望着管道。

而对一些对安全有需求的客户来说,加密与签名也必不可少,但这些工具的集成与支持程度做得非常糟糕。

为了解决该难题,一些人可能已经查过,找到了救命工具:Mutt!

但是,Mutt 的配置难度立即击败了他们,他们受挫了,又缩回了图形化邮件客户端。

为什么会这么难�这一方面有工具选项繁杂的问题,另一方面,邮件系统本身也是难的!而现代的信息系统中,邮件已经逐渐式微,大家对其的掌握已经不像以前的黑客那么熟练了,从而相关的介绍文章也少之甚少。

我们发现,互联网上的 Mutt 教程要么对 Mutt 特性大书特书,然后根据特性列举一些未组织的代码片段;要么就是简单示例,给些参数;要么就是大量枚举选项(Mutt自己的文档也是如此)。这些教程,要么让人不知如何下手,要么让人迷失在大量选项中,从而难以构建其一个最小的工作样例。

是的,我们所需要的就是一个最小工作样例,以此为基础,逐渐调参到我们喜欢的模样。这种反馈过程不必多言。

所以,我们从实践角度出发,给大家提供一个最小工作样例,先让整个流程部署起来、跑起来!然后再谈其他的配置。

配置文件与说明在这个地址中:https://github.com/ZenithalHourlyRate/muttrc