windows 添加管理员--Rust版本

作者:Sec-Labs | 发布时间:

0x00 前言

偶然得知微软官方发布了Rust for Windows(https://github.com/microsoft/windows-rs),Rust可以直接原生调用任何Windows API,极大的扩展了 rust 在 windows 上的开发范围和能力。 因此尝试使用 Rust 编写一个添加管理员的 Rust 应用。

项目地址

https://github.com/Ryze-T/rust-addUser

核心代码

main.rs

use windows::{
    Win32::NetworkManagement::NetManagement::USER_INFO_1,
    Win32::NetworkManagement::NetManagement::NetUserAdd,
    Win32::NetworkManagement::NetManagement::UF_SCRIPT,
    Win32::NetworkManagement::NetManagement::USER_PRIV_USER,
    Win32::NetworkManagement::NetManagement::NetLocalGroupAddMembers,
    core::PWSTR,
    core::PCWSTR
};
use windows::Win32::NetworkManagement::NetManagement::LOCALGROUP_MEMBERS_INFO_3;

fn main()  {
    let servename = PCWSTR::default();
    let level:u32 = 1;
    let parm_error= &mut 0;

    let mut username: Vec<u16> = "test".encode_utf16().collect();
    username.push(0);
    let p_username = username.as_ptr() as *mut u16;
    let mut password: Vec<u16> = "1q@W3e$r".encode_utf16().collect();
    password.push(0);
    let p_password = password.as_ptr() as *mut u16;

    let mut groupname: Vec<u16> = "administrators".encode_utf16().collect();
    groupname.push(0);
    let p_groupname = groupname.as_ptr() as *mut u16;

    let ui1 = &mut USER_INFO_1{
        usri1_name:PWSTR(p_username),
        usri1_password:PWSTR(p_password),
        usri1_password_age: 0,
        usri1_priv: USER_PRIV_USER,
        usri1_home_dir: PWSTR(std::ptr::null_mut()),
        usri1_comment: PWSTR(std::ptr::null_mut()),
        usri1_flags: UF_SCRIPT,
        usri1_script_path: PWSTR(std::ptr::null_mut()),
    };

    let lmi3  = &mut LOCALGROUP_MEMBERS_INFO_3{
        lgrmi3_domainandname: PWSTR(p_username),
    };

    unsafe{
        let result = NetUserAdd(servename,level,ui1 as *const _ as _,parm_error );
        if result == 0 {
            println!("[+] User added successfully");
        }
        else {
            println!("[-] Failed to add user:{}", result);
        }
        let result2 = NetLocalGroupAddMembers(servename,PCWSTR(p_groupname),3,lmi3 as *const _ as _,1);
        if result2 == 0 {
            println!("[+] Administrators group added successfully");
        }
        else{
            println!("[-] Failed to add Administrators group:{}",result2);
        }
    }

}

 

0x01 构建

之前用C写过,因此知道添加管理员需要使用到两个api:NetUserAdd 和 NetLocalGroupAddMembers,同时需要两个结构体:USER_INFO_1 和 LOCALGROUP_MEMBERS_INFO_3。

1.1 NetUserAdd

函数定义:

pub unsafe fn NetUserAdd<'a, Param0: IntoParam<'a, PCWSTR>>(    servername: Param0,     level: u32,     buf: *const u8,     parm_err: *mut u32 ) -> u32 

具体参数含义还需要去微软官方搜索对应 API 查看:

  • servername 为 NULL 时代表本地计算机。
  • level 为 1 时,指定有关用户参数的信息,此时 buf 参数指向 USER_INFO_1结构。
  • parm_error 指向一个值的指针,为 NULL 时 在出错时不返回索引。

servername 的类型为 Param0,即 IntoParam<’a, PCWSTR>,而且 rust 中并没有 NULL,因此这里定义为:

let servename = PCWSTR::default(); 

USER_INFO_1 结构体定义:

pub struct USER_INFO_1 {    pub usri1_name: PWSTR,    pub usri1_password: PWSTR,    pub usri1_password_age: u32,    pub usri1_priv: USER_PRIV,    pub usri1_home_dir: PWSTR,    pub usri1_comment: PWSTR,    pub usri1_flags: USER_ACCOUNT_FLAGS,    pub usri1_script_path: PWSTR, } 

不一一解释,重点关注 usri1_name 和 usri1_password,这两个参数指明添加用户的账号密码。但是其类型是 PWSTR,查看该类型定义:

pub struct PWSTR(pub *mut u16); 

因此获取字符串的指针后,强制类型转换为 u16,再转为 PWSTR。

这里还涉及到 rust 与 Windows 编码的问题,rust 本机字符串默认是 UTF-8,而许多 Windows 函数都以 UTF-16 作为字符串编码,因此如果 username 或 password 在定义时没有转为 utf-16,实际添加的用户在 Windows 上就会出现乱码。因此可以使用 encode_utf16(),将字符串转为 utf16 值的迭代器,通过 collect() 在该迭代器上构造一个 Vec,再 push(0) 作为字符串结尾。

因此代码为:

let mut username: Vec<u16> = "test".encode_utf16().collect(); username.push(0); let p_username = username.as_ptr() as *mut u16; let mut password: Vec<u16> = "1q@W3e$r".encode_utf16().collect(); password.push(0); let p_password = password.as_ptr() as *mut u16; let ui1 = &mut USER_INFO_1{    usri1_name:PWSTR(p_username),    usri1_password:PWSTR(p_password), usri1_password_age: 0, usri1_priv: USER_PRIV_USER, usri1_home_dir: PWSTR(std::ptr::null_mut()), usri1_comment: PWSTR(std::ptr::null_mut()), usri1_flags: UF_SCRIPT, usri1_script_path: PWSTR(std::ptr::null_mut()), }; 

由于 buf 的参数类型为 *const u8,因此在调用时要使用 as 作为强制类型转换。

最后进行 NetUserAdd 调用:

NetUserAdd(servename,level,ui1 as *const _ as _,parm_error ); 

1.2 NetLocalGroupAddMembers

NetLocalGroupAddMembers 是用来将用户加入到特定组的 Windows API,其函数定义为:

pub unsafe fn NetLocalGroupAddMembers<'a, Param0: IntoParam<'a, PCWSTR>, Param1: IntoParam<'a, PCWSTR>>(    servername: Param0,     groupname: Param1,     level: u32,     buf: *const u8,     totalentries: u32 ) -> u32 

了解了上一个 api 在 rust 中的表现形式之后,这个 api 也就不难了,要注意的两个参数:groupname 和 buf,buf指向的是 LOCALGROUP_MEMBERS_INFO_3。

groupname 和 servername 一样,但是这里需要赋值,因此也要和 USER_INFO_1 中的 usri1_username 一样处理:

let mut groupname: Vec<u16> = "administrators".encode_utf16().collect(); groupname.push(0); let p_groupname = groupname.as_ptr() as *mut u16; 

LOCALGROUP_MEMBERS_INFO_3 结构体定义:

pub struct LOCALGROUP_MEMBERS_INFO_3 {    pub lgrmi3_domainandname: PWSTR, } 

这里的 lgrmi3_domainandname 指用户名,因此用 p_username 即可。

最后调用为:

NetLocalGroupAddMembers(servename,PCWSTR(p_groupname),3,lmi3 as *const _ as _,1); 

0x02 结果

68666675c7170156

 

 

标签:工具分享, 渗透技巧