Skip to content

Latest commit

 

History

History
34 lines (17 loc) · 3.02 KB

看不见的彼方.md

File metadata and controls

34 lines (17 loc) · 3.02 KB

看不见的彼方

这题不难,chroot 漏信息的方法太多了。但做法比较多,看看不同人的方法就比较有意思。

进程数量做侧信道(失败但理论上能成功)

我一开始是考虑 chroot 后进程能获取/影响哪些系统全局属性,这些属性就能用来当侧信道。我选的是系统进程数量,因为这个比较容易精确控制。

我的实现在 看不见的彼方/side_channel/ 目录下,具体来讲:

  • Alice 一开始就 fork 出 256 个进程。每隔 10ms,Alice 通过杀死一个进程传递一个 bit 0,通过 fork 新进程传递一个 bit 1,这样传题目要求的 32 byte 也就不到 3s,时间是够的。

  • 而 Bob 这边轮询 sysinfo(&info); info.procs 读系统进程数量,一旦发生变化就记录一个比特。

假设系统进程数量短时间内保持稳定,这个方法应当是有效的。实际上我本地测试有超过一半的时间都能正确传递信息。而评测环境用的 Docker,开了 PID namespace 后读取到的进程数量是局限在容器内的,理论上应当更加稳定。但实际上这个方法失败了,评测环境中 Bob 要么读不到进程变化,要么读到的全是 bit 1。我本地 Docker 调试也没法复现问题,就毫无头绪。

赛后跟 zzh 交流了一下,他说 “容器默认都限制 16 个 pid” …… 🤷🏽‍♀️

System V IPC

考虑常规 IPC 方法。executor.c 禁用了一些常见 IPC 机制,但漏网之鱼总是有的。以 Linux IPC 为关键词搜索其他的 IPC 机制,System V IPC 就是一例。System V IPC 是在 Linux 内核实现的,executor.c 里面并没有禁用相关 syscall,所以是可用的。

正常的 System V IPC 使用流程是:

  1. 两边进程选择同一个路径,调用 key_t ftok(const char *pathname, int proj_id) 函数,生成同一个 key;
  2. 两边进程调用 int msgget(key_t key, int msg_flags),从 key 获得 message queue 的 ID;
  3. 然后两边就可以用 msgsndmsgrcv 函数发消息了,体验跟管道差不多。

第一步 ftok 给人一种两边进程需要能访问同一个文件的错觉。但简单测试就会发现,跳过这一步直接自己选择一个整数作为 key(key_t 就是个整数类型)完全没有问题。至于标准中法中为什么 key 非要用个文件路径生成(甚至这个路径必须可访问),而不是任意字符串都 OK,我就不清楚了。man ftok 甚至还写了 Of course, no guarantee can be given that the resulting key_t is unique. 感觉就很离谱,可能是 everything is a file 哲学用力过猛吧。

总之,跳过 ftok,Alice 和 Bob 直接用同一个 key 做 System V IPC 就好了。代码在 看不见的彼方/sysvipc/ 目录下,是基于这篇文章代码实现的 System V IPC,Alice 相当于 client,Bob 相当于 server。