DEF CON Quals 2026 - mapllvm (WIP)

It takes the finest maple syrup to make a compiler like this

Challenge Overview

Category: Binary Exploitation | Points: 290 | Solves: 33

mapllvm 是一个用 Racket 编写的简单函数式语言编译器,将类 Scheme 源码编译为 x86-64 汇编,链接自定义 GC 后执行。服务端接受 <=2000 字符的源码,编译运行在 nsjail 中。flag 位于 /flag。

编译器架构

1
2
3
源码 (read) → validate → uniquify → closure-conversion → datatype-encoding
→ imperative-transform → function-calls → control-flow → emit (nasm)
→ nasm + ld + gc.o → 执行

类型系统

  • 3-bit tagged pointers: INTEGER(000) / PTR(001) / OTHER(010)
  • 堆对象: vector(tag=2), pair(tag=3), closure(tag=4)
  • 整数为 61-bit signed,tag 后为 n << 3
  • 算术运算无溢出检查(直接使用 x86 add/sub/imul

内置操作

1
2
3
* + - < > <= >= = != not cons car cdr
integer? boolean? empty? unit? vector? function? cons?
make-vec vec-len vec-set! vec-get panic!

已发现漏洞

1. panic! 可绕过 Validator 调用

validator 试图通过 guard 阻止 panic!

1
2
3
4
5
[`(,op ,es ...)
#:when (and (member op source-lang-ops)
(not (equal? op 'panic!)))
(andmap validate-e es)]
[`(,es ...) (andmap validate-e es)] ; ← fallback 允许 (panic! ...)

(panic! 0) 会被 fallback pattern 捕获并验证通过,后续 pass 中 panic! 作为 source-lang-op 被正确处理。实战验证:(panic! 42) 编译运行,exit code = 42。

危害:仅能调用 exit(n),无可控代码执行。

2. 算术溢出导致 make-vec OOB 写

整数范围为 [-2^60, 2^60-1]。两个最大值相加:

1
2
3
4
(+ 1152921504606846975 1152921504606846975)
; untagged: (2^60-1) + (2^60-1) = 2^61-2
; tagged: (2^61-2) * 8 mod 2^64 = -16
; bit-rshift(-16, 3) = -2

(make-vec overflow_value) 中: 1. gc_alloc(-2+2) = gc_alloc(0) → 最小分配 8 bytes 2. 写 VECTOR-OBJ-TAG 到 offset 0,写 length(-2) 到 offset 1 (OOB) 3. vec-zero-initptr+16 开始以 len=-2 递归写 0 到负偏移

zero-init 是一个 tail-recursive 循环:

1
2
3
(if (= len 0) UNIT
(begin (mem-write addr (- len 1) 0)
(call vec-zero-init addr (- len 1))))

len=-2 进入后永远无法达到 len=0,持续向低地址写 0: - offset -3: 写 block header mark - offset -4: 写 block header allocated flag (设为 0 = free) - offset -5: 写 block header size (设为 0) - 继续向下直到 segfault

服务端验证:(make-vec (- 0 1)) → exit code -11 (SIGSEGV)

利用障碍

  1. zero-init 无限循环无法停止(堆地址空间与栈分离,无法改写栈上循环计数器)
  2. 写入值恒为 0,无法注入任意地址
  3. 堆不可执行(NX),即使 corrupt closure code label 也无法执行 shellcode
  4. 无 seccomp(nsjail 配置无线程限制外的 syscall 过滤),但缺少可利用的代码执行原语
  5. 内部操作 (closure-env-set 等) 无法从源码调用(uniquify pass 产生 match error)

待探索方向

  • GC metadata 破坏后能否通过大量分配触发 GC → UAF/double-alloc
  • nsjail mount namespace 逃逸(/flag 为 bind mount,/dev 可读写)
  • Racket read 的 reader extension 注入(#reader 等)
  • 编译器 pass 间的类型混淆(如 closure 与 vector 的 tag 互换)