0x00 Introduction

---

The previous article 'vSphere Development Guide 1 – vSphere Automation API' introduced methods for interacting with vCenter Server and virtual machines through the vSphere Automation API. However, some operations in the vSphere Automation API are not supported by older versions of vCenter (< vSphere 7.0U2), limiting its universality. This article will introduce a more universal implementation method – the vSphere Web Services API.

0x01 Overview

---

This article will cover the following topics:

  • Development details of the vSphere Web Services API
  • Analysis of the open-source tool SharpSphere
  • Open-source code vSphereWebServicesAPI_Manage.py

0x02 Development Details of the vSphere Web Services API

---

Reference documents:

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

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

References for Python implementation code:

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

To improve efficiency, we implement this based on the Python SDK pyvmomi

Specific details are as follows:

(1) Login operation

Call SmartConnect, passing in the username and plaintext password

Specific details can be viewed in the file /lib/site-packages/pyVim/connect.py after installing pyvmomi

(2) View virtual machine configuration

Query by creating a ContainerView managed object

Compared to the vSphere Automation API, the obtained content is more comprehensive

For example, vsphere-automation-sdk-python does not support obtaining the UUID corresponding to each virtual machine, but it can be obtained through pyvmomi

(3) Send files to a virtual machine

Use the method InitiateFileTransferToGuest, which requires passing in the following six parameters:

  • vm, specifying the virtual machine to operate on
  • auth, credentials for logging into the virtual machine
  • guestFilePath, the save path for the file sent to the virtual machine
  • fileAttributes, the file attributes sent to the virtual machine
  • fileSize, file size
  • overwrite, specifies whether to overwrite

Upon successful execution, returns the URI corresponding to the file

Use the PUT method to access the URI, with the data field containing the file content to be sent, which must be transmitted in binary format

The specific implementation code is as follows:

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) Download files from virtual machine

Using the InitiateFileTransferFromGuest method, the following three parameters must be passed:

  • vm, specifies the virtual machine to operate on
  • auth, credentials for logging into the virtual machine
  • guestFilePath, the path of the virtual machine file to be downloaded

Upon successful execution, returns the uri corresponding to the specified file

When accessing the uri using the GET method, distinguish between text format and binary format when retrieving file content. Text format can be read using r.text, while binary format can be read using r.content

The specific implementation code is as follows:

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)

Analysis of the Open-Source Tool SharpSphere 0x03

---

https://github.com/JamesCooteUK/SharpSphere

Developed in C#, compatible with Cobalt Strike

Supports the following features:

  • Acts as a C2 server
  • Code execution
  • File upload
  • File download
  • View virtual machine configuration
  • Dump memory

The implementation process for dumping memory is as follows:

  • Obtain a virtual machine snapshot; if none exists, create a snapshot file (.vmem)
  • Download the snapshot locally via file URI creation
  • Parse the snapshot file using WinDbg and Mimikatz to extract credentials from the lsass process

Currently, operations on Linux virtual machines are not supported.

During actual use, if you encounter the following error:

Error: An error occurred while making the HTTP request to https:///. This could be due to the fact that the server certificate is not configured properly 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.

You can try adding the following code to resolve it:

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

0x04 Open Source Code

---

The complete open source code has been uploaded to GitHub, with the address as follows:

An Open Source Project

Code Applicable Version: No restrictions

Supports the following features:

  • Read virtual machine configurations
  • View virtual machine files
  • Delete virtual machine files
  • Upload files to the virtual machine
  • Download files from the virtual machine
  • Execute commands in the virtual machine

The specific commands are as follows:

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

For operations on virtual machines, both Windows and Linux systems are supported

0x05 Summary

---

This article introduces the method of interacting with vCenter Server and virtual machines through the vSphere Web Services API, including the open-source implementation code vSphereWebServicesAPI_Manage.py, and documents the development details.

Regarding the vSphere Web Services API, it offers greater versatility; however, due to being developed based on the SDK, the resulting tools tend to have a larger file size.