Good in study, attitude and health

vSphere开发指南2——vSphere Web Services API

0x00 前言


在上篇文章《vSphere开发指南1——vSphere Automation API》介绍了通过vSphere Automation API实现vCenter Server同虚拟机交互的方法,但是vSphere Automation API有些操作不支持低版本的vCenter(<vSphere7.0U2),导致通用性不够,本文将要介绍更为通用的实现方法——vSphere Web Services API

0x01 简介


本文将要介绍以下内容:

  • vSphere Web Services API开发细节
  • 已开源工具SharpSphere的分析
  • 开源代码vSphereWebServicesAPI_Manage.py

0x02 vSphere Web Services API开发细节


参考文档:

https://code.vmware.com/apis/968

https://code.vmware.com/docs/11721/vmware-vsphere-web-services-sdk-programming-guide

Python实现代码的参考资料:

https://github.com/vmware/pyvmomi-community-samples

为了提高效率,这里我们基于Python SDK pyvmomi进行实现

具体细节如下:

(1)登录操作

调用SmartConnect,传入用户名和明文口令

具体细节可在安装pyvmomi后,从文件/lib/site-packages/pyVim/connect.py中查看

(2)查看虚拟机配置

通过创建ContainerView托管对象进行查询

相比于vSphere Automation API,获得的内容更加全面

例如,vsphere-automation-sdk-python不支持获得每个虚拟机对应的UUID,但可以通过pyvmomi获得

####(3)向虚拟机发送文件

使用方法InitiateFileTransferToGuest,需要传入以下六个参数:

  • vm,指定要操作的虚拟机
  • auth,登录虚拟机的凭据
  • guestFilePath,向虚拟机发送的文件保存路径
  • fileAttributes,向虚拟机发送的文件属性
  • fileSize,文件大小
  • overwrite,指定是否覆盖

执行成功后,返回文件对应的uri

使用PUT方法访问uri,data字段为发送的文件内容,这里的文件内容需要使用二进制格式进行发送

具体实现代码如下:

def UploadFileToVM(api_host, username, password, vm_name, guest_username, guest_user_password, local_path, guest_path):
    service_instance = SmartConnect(host=api_host, user=username, pwd=password, port=443, disableSslCertValidation=True)
    if not service_instance:
        raise SystemExit("[!] Unable to connect to host with supplied credentials.")

    creds = vim.vm.guest.NamePasswordAuthentication(username = guest_username, password = guest_user_password)

    with open(local_path, 'rb') as file_obj:
        data_to_send = file_obj.read()

    try:

        content = service_instance.RetrieveContent()
        vm = get_obj(content, [vim.VirtualMachine], vm_name)
        if not vm:
            raise SystemExit("Unable to locate the virtual machine.")

        file_attribute = vim.vm.guest.FileManager.FileAttributes()    
        profile_manager = content.guestOperationsManager.fileManager
        res = profile_manager.InitiateFileTransferToGuest(vm, creds, guest_path, file_attribute, len(data_to_send), True)      
        print("[+] transfer uri: " + res)

        headers = {
            "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0",
        } 
        r = requests.put(res, headers = headers, data = data_to_send, verify = False)
        if r.status_code ==200:
            print("[+] " + r.text)
        else:         
            print("[!]" + str(r.status_code))
            print(r.text)
            exit(0)

    except vmodl.MethodFault as error:
        print("[!] Caught vmodl fault : " + error.msg)

(4)从虚拟机下载文件

使用方法InitiateFileTransferFromGuest,必须传入以下三个参数:

  • vm,指定要操作的虚拟机
  • auth,登录虚拟机的凭据
  • guestFilePath,需要下载的虚拟机文件路径

执行成功后,返回指定文件对应的uri

使用GET方法访问uri,在获取文件内容时需要区分文本格式和二进制格式,文本格式可以使用r.text读取,二进制格式可以使用r.content读取

具体实现代码如下:

def DownloadFileFromVM(api_host, username, password, vm_name, guest_username, guest_user_password, guest_path, type):
    service_instance = SmartConnect(host=api_host, user=username, pwd=password, port=443, disableSslCertValidation=True)
    if not service_instance:
        raise SystemExit("[!] Unable to connect to host with supplied credentials.")

    creds = vim.vm.guest.NamePasswordAuthentication(username = guest_username, password = guest_user_password)

    try:

        content = service_instance.RetrieveContent()
        vm = get_obj(content, [vim.VirtualMachine], vm_name)
        if not vm:
            raise SystemExit("Unable to locate the virtual machine.")
   
        profile_manager = content.guestOperationsManager.fileManager
        res = profile_manager.InitiateFileTransferFromGuest(vm, creds, guest_path)      
        print("[+] transfer uri: " + res.url)
        print("    size: " + str(res.size))
        headers = {
            "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0",
        } 
        r = requests.get(res.url, headers = headers, verify = False)
        if r.status_code ==200:
            if type == "text":
                print("[+] result: ")
                print(r.text)
            else:
                print("[+] save the result as temp.bin")
                with open("temp.bin", "wb") as file_obj:
                    file_obj.write(r.content)
  
        else:         
            print("[!]" + str(r.status_code))
            print(r.text)
            exit(0)

    except vmodl.MethodFault as error:
        print("[!] Caught vmodl fault : " + error.msg)

0x03 已开源工具SharpSphere的分析


https://github.com/JamesCooteUK/SharpSphere

c#开发,与Cobalt Strike兼容

支持以下功能:

  • 作为C2服务器
  • 代码执行
  • 文件上传
  • 文件下载
  • 查看虚拟机配置
  • Dump内存

其中,Dump内存的实现流程如下:

  • 获得虚拟机快照,如果没有就创建快照文件(.vmem)
  • 将快照下载到本地,通过创建文件uri的方式进行下载
  • 通过WinDbg和Mimikatz解析快照文件,导出lsass进程中的凭据

目前暂不支持对Linux虚拟机的操作

在实际使用过程中,如果遇到以下错误:

Error: An error occurred while making the HTTP request to https://<ip>/. This could be due to the fact that the server certificate is not configuredproperly with HTTP.SYS in the HTTPS case. This could also be caused by a mismatch of the security binding between the client and the server.

可以尝试添加以下代码解决:

System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;

0x04 开源代码


完整的开源代码已上传至github,地址如下:

https://github.com/3gstudent/Homework-of-Python/blob/master/vSphereWebServicesAPI_Manage.py

代码适用版本:没有限制

支持以下功能:

  • 读取虚拟机的配置
  • 查看虚拟机文件
  • 删除虚拟机文件
  • 向虚拟机上传文件
  • 从虚拟机下载文件
  • 在虚拟机中执行命令

具体命令如下:

  • ListVM
  • GetVMConfig
  • ListHost
  • ListVMProcess
  • CreateVMProcess
  • KillVMProcess
  • ListVMFolder
  • DeleteVMFile
  • DownloadFileFromVM
  • UploadFileToVM

其中,对于虚拟机的操作,支持Windows和Linux系统

0x05 小结


本文介绍了通过vSphere Web Services API实现vCenter Server同虚拟机交互的方法,开源实现代码vSphereWebServicesAPI_Manage.py,记录开发细节。

对于vSphere Web Services API,通用性更强,但是由于基于SDK进行开发,导致编译出来的工具体积较大。


LEAVE A REPLY