Skip to content

Commit

Permalink
devicetrust: don't invoke powershell when reading system information (#…
Browse files Browse the repository at this point in the history
…50372)

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.
  • Loading branch information
zmb3 authored Dec 19, 2024
1 parent 860a9d4 commit b1d8c3b
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 74 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,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.114.0
github.com/yusufpapurcu/wmi v1.2.4
go.etcd.io/etcd/api/v3 v3.5.17
go.etcd.io/etcd/client/v3 v3.5.17
go.mongodb.org/mongo-driver v1.14.0
Expand Down Expand Up @@ -521,7 +522,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
Expand Down
131 changes: 58 additions & 73 deletions lib/devicetrust/native/device_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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) {
Expand All @@ -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"},
Expand All @@ -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()

Expand All @@ -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,
Expand Down

0 comments on commit b1d8c3b

Please sign in to comment.