From c4cc91cba2977e5ed2a4bb30e56399ce47d65b9f Mon Sep 17 00:00:00 2001 From: Zac Bergquist Date: Thu, 19 Dec 2024 13:33:35 -0700 Subject: [PATCH] devicetrust: don't invoke powershell when reading system information (#50453) The device trust web flow can result in a web browser launching Teleport Connect (which launches tsh, which in turn launches powershell). Some antivirus solutions flag cases where a powershell process is a descendent of a web browser process. In order to avoid being blocked by the antivirus software, we want to read system information directly instead of via powershell. --- go.mod | 2 +- lib/devicetrust/native/device_windows.go | 131 ++++++++++------------- 2 files changed, 59 insertions(+), 74 deletions(-) diff --git a/go.mod b/go.mod index fdb545c375dd9..7a06f6ad87300 100644 --- a/go.mod +++ b/go.mod @@ -184,6 +184,7 @@ require ( github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb github.com/vulcand/predicate v1.2.0 // replaced github.com/xanzy/go-gitlab v0.109.0 + github.com/yusufpapurcu/wmi v1.2.4 go.etcd.io/etcd/api/v3 v3.5.16 go.etcd.io/etcd/client/v3 v3.5.16 go.mongodb.org/mongo-driver v1.14.0 @@ -520,7 +521,6 @@ require ( github.com/xlab/treeprint v1.2.0 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect github.com/yuin/gopher-lua v1.1.1 // indirect - github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/zeebo/errs v1.3.0 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect github.com/zmap/zcrypto v0.0.0-20231219022726-a1f61fb1661c // indirect diff --git a/lib/devicetrust/native/device_windows.go b/lib/devicetrust/native/device_windows.go index 036f1e48e514d..b0a871c18a994 100644 --- a/lib/devicetrust/native/device_windows.go +++ b/lib/devicetrust/native/device_windows.go @@ -19,17 +19,18 @@ package native import ( - "bytes" "encoding/base64" "errors" + "fmt" "os" - "os/exec" "os/user" + "strconv" "time" "github.com/google/go-attestation/attest" "github.com/gravitational/trace" log "github.com/sirupsen/logrus" + "github.com/yusufpapurcu/wmi" "golang.org/x/sync/errgroup" "golang.org/x/sys/windows" "google.golang.org/protobuf/types/known/timestamppb" @@ -78,106 +79,90 @@ func handleTPMActivateCredential(encryptedCredential, encryptedCredentialSecret return windowsDevice.handleTPMActivateCredential(encryptedCredential, encryptedCredentialSecret) } -// getDeviceSerial returns the serial number of the device using PowerShell to -// grab the correct WMI objects. Getting it without calling into PS is possible, -// but requires interfacing with the ancient Win32 COM APIs. func getDeviceSerial() (string, error) { - cmd := exec.Command( - "powershell", - "-NoProfile", - "Get-WmiObject Win32_BIOS | Select -ExpandProperty SerialNumber", - ) // ThinkPad P P14s: // PS > Get-WmiObject Win32_BIOS | Select -ExpandProperty SerialNumber // PF47WND6 - out, err := cmd.Output() - if err != nil { + + type Win32_BIOS struct { + SerialNumber string + } + + var bios []Win32_BIOS + query := wmi.CreateQuery(&bios, "") + if err := wmi.Query(query, &bios); err != nil { return "", trace.Wrap(err) } - return string(bytes.TrimSpace(out)), nil + + if len(bios) == 0 { + return "", trace.BadParameter("could not read serial number from Win32_BIOS") + } + + return bios[0].SerialNumber, nil } func getReportedAssetTag() (string, error) { - cmd := exec.Command( - "powershell", - "-NoProfile", - "Get-WmiObject Win32_SystemEnclosure | Select -ExpandProperty SMBIOSAssetTag", - ) // ThinkPad P P14s: // PS > Get-WmiObject Win32_SystemEnclosure | Select -ExpandProperty SMBIOSAssetTag // winaia_1337 - out, err := cmd.Output() - if err != nil { + + type Win32_SystemEnclosure struct { + SMBIOSAssetTag string + } + + var system []Win32_SystemEnclosure + query := wmi.CreateQuery(&system, "") + if err := wmi.Query(query, &system); err != nil { return "", trace.Wrap(err) } - return string(bytes.TrimSpace(out)), nil + + if len(system) == 0 { + return "", trace.BadParameter("could not read asset tag from Win32_SystemEnclosure") + } + + return system[0].SMBIOSAssetTag, nil } func getDeviceModel() (string, error) { - cmd := exec.Command( - "powershell", - "-NoProfile", - "Get-WmiObject Win32_ComputerSystem | Select -ExpandProperty Model", - ) // ThinkPad P P14s: // PS> Get-WmiObject Win32_ComputerSystem | Select -ExpandProperty Model // 21J50013US - out, err := cmd.Output() - if err != nil { + + type Win32_ComputerSystem struct { + Model string + } + var cs []Win32_ComputerSystem + query := wmi.CreateQuery(&cs, "") + if err := wmi.Query(query, &cs); err != nil { return "", trace.Wrap(err) } - return string(bytes.TrimSpace(out)), nil + + if len(cs) == 0 { + return "", trace.BadParameter("could not read model from Win32_ComputerSystem") + } + + return cs[0].Model, nil } func getDeviceBaseBoardSerial() (string, error) { - cmd := exec.Command( - "powershell", - "-NoProfile", - "Get-WmiObject Win32_BaseBoard | Select -ExpandProperty SerialNumber", - ) // ThinkPad P P14s: // PS> Get-WmiObject Win32_BaseBoard | Select -ExpandProperty SerialNumber // L1HF2CM03ZT - out, err := cmd.Output() - if err != nil { - return "", trace.Wrap(err) - } - return string(bytes.TrimSpace(out)), nil -} - -func getOSVersion() (string, error) { - cmd := exec.Command( - "powershell", - "-NoProfile", - "Get-WmiObject Win32_OperatingSystem | Select -ExpandProperty Version", - ) - // ThinkPad P P14s: - // PS> Get-WmiObject Win32_OperatingSystem | Select -ExpandProperty Version - // 10.0.22621 - out, err := cmd.Output() - if err != nil { + type Win32_BaseBoard struct { + SerialNumber string + } + var bb []Win32_BaseBoard + query := wmi.CreateQuery(&bb, "") + if err := wmi.Query(query, &bb); err != nil { return "", trace.Wrap(err) } - return string(bytes.TrimSpace(out)), nil -} - -func getOSBuildNumber() (string, error) { - cmd := exec.Command( - "powershell", - "-NoProfile", - "Get-WmiObject Win32_OperatingSystem | Select -ExpandProperty BuildNumber", - ) - // ThinkPad P P14s: - // PS> Get-WmiObject Win32_OperatingSystem | Select -ExpandProperty BuildNumber - // 22621 - out, err := cmd.Output() - if err != nil { - return "", trace.Wrap(err) + if len(bb) == 0 { + return "", trace.BadParameter("could not read serial from Win32_BaseBoard") } - return string(bytes.TrimSpace(out)), nil + return bb[0].SerialNumber, nil } func collectDeviceData(_ CollectDataMode) (*devicepb.DeviceCollectedData, error) { @@ -188,15 +173,13 @@ func collectDeviceData(_ CollectDataMode) (*devicepb.DeviceCollectedData, error) g.SetLimit(groupLimit) // Run exec-ed commands concurrently. - var systemSerial, baseBoardSerial, reportedAssetTag, model, osVersion, osBuildNumber string + var systemSerial, baseBoardSerial, reportedAssetTag, model string for _, spec := range []struct { fn func() (string, error) out *string desc string }{ {fn: getDeviceModel, out: &model, desc: "device model"}, - {fn: getOSVersion, out: &osVersion, desc: "os version"}, - {fn: getOSBuildNumber, out: &osBuildNumber, desc: "os build number"}, {fn: getDeviceSerial, out: &systemSerial, desc: "system serial"}, {fn: getDeviceBaseBoardSerial, out: &baseBoardSerial, desc: "base board serial"}, {fn: getReportedAssetTag, out: &reportedAssetTag, desc: "reported asset tag"}, @@ -214,6 +197,8 @@ func collectDeviceData(_ CollectDataMode) (*devicepb.DeviceCollectedData, error) }) } + ver := windows.RtlGetVersion() + // We want to fetch as much info as possible, so errors are ignored. _ = g.Wait() @@ -232,8 +217,8 @@ func collectDeviceData(_ CollectDataMode) (*devicepb.DeviceCollectedData, error) OsType: devicepb.OSType_OS_TYPE_WINDOWS, SerialNumber: serial, ModelIdentifier: model, - OsVersion: osVersion, - OsBuild: osBuildNumber, + OsVersion: fmt.Sprintf("%v.%v.%v", ver.MajorVersion, ver.MinorVersion, ver.BuildNumber), + OsBuild: strconv.FormatInt(int64(ver.BuildNumber), 10), OsUsername: u.Username, SystemSerialNumber: systemSerial, BaseBoardSerialNumber: baseBoardSerial,