feat: improve examples/dcom.go and implement "generic" wmiexec.go script

see: https://github.com/oiweiwei/go-msrpc/issues/3
This commit is contained in:
oiweiwei
2024-06-03 18:59:59 +02:00
parent d497d277ae
commit df779cedc5
5 changed files with 288 additions and 38 deletions
+2 -2
View File
@@ -14,8 +14,8 @@ class MyClass : Base {
};
instance of MyClass {
Array = {1, 2, 3};
Id = 123;
Data1 = "StringField";
Data2 = "defaultValue";
Id = 123;
Array = {1, 2, 3};
};
+85 -28
View File
@@ -1,12 +1,27 @@
//go:build exclude
//go:build ignore
// dcom.go script executes the calc.exe on the remote machine.
// wmiexec.go is a sample for executing arbitrary WMI class method on the remote machine, examples:
// (1) enumerate sub keys on the remote machine, where 2147483650 is HKEY_LOCAL_MACHINE
// (see https://learn.microsoft.com/en-us/previous-versions/windows/desktop/regprov/enumkey-method-in-class-stdregprov)
//
// go run wmiexec.go \
// --debug \
// --class StdRegProv \
// --method EnumKey \
// --args '{"hDefKey": 2147483650, "sSubKeyName": "SYSTEM\\CurrentControlSet\\Services"}'
//
// (2) cerate a process on the remote machine:
//
// go run wmiexec.go --class Win32Process --method Create --args '{"CommandLine": "calc.exe", "CurrentDirectory": "C:\\"}'
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"io"
"net"
"os"
@@ -35,6 +50,32 @@ import (
var j = func(data any) string { b, _ := json.MarshalIndent(data, "", " "); return string(b) }
var (
class string
method string
arg string
resource string
debug bool
args wmio.Values
)
func init() {
flag.StringVar(&class, "class", "Win32_Process", "class name")
flag.StringVar(&method, "method", "Create", "method name")
flag.StringVar(&arg, "args", `{"CommandLine":"calc.exe","CurrentDirectory":"C:\\"}`, "method args")
flag.StringVar(&resource, "resource", "//./root/cimv2", "resource name, ie root/default, or root/cimv2")
flag.BoolVar(&debug, "debug", false, "debug")
flag.Parse()
if err := json.Unmarshal([]byte(arg), &args); err != nil {
fmt.Fprintln(os.Stderr, "parse_args", err)
os.Exit(1)
}
}
func init() {
// add credentials.
gssapi.AddCredential(credential.NewFromPassword(os.Getenv("USERNAME"), os.Getenv("PASSWORD")))
@@ -46,6 +87,14 @@ func init() {
func main() {
log := zerolog.New(os.Stderr)
if !debug {
log = zerolog.New(io.Discard)
}
log.Info().Str("class", class).Str("method", method).Str("args", fmt.Sprintf("%+v", args)).Msg("execute")
ctx := gssapi.NewSecurityContext(context.Background())
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dcom/2be2642e-67a1-4690-883b-642b505ddb1d
@@ -73,17 +122,19 @@ func main() {
return
}
fmt.Println("-----------------------")
fmt.Println("OBJECT RESOLVER STRING BINDINGS")
fmt.Println("-----------------------")
if debug {
fmt.Println("-----------------------")
fmt.Println("OBJECT RESOLVER STRING BINDINGS")
fmt.Println("-----------------------")
fmt.Println(j(srv.ObjectResolverBindings.GetStringBindings()))
fmt.Println(j(srv.ObjectResolverBindings.GetStringBindings()))
fmt.Println("-----------------------")
fmt.Println("OBJECT RESOLVER SECURITY BINDINGS")
fmt.Println("-----------------------")
fmt.Println("-----------------------")
fmt.Println("OBJECT RESOLVER SECURITY BINDINGS")
fmt.Println("-----------------------")
fmt.Println(j(srv.ObjectResolverBindings.GetSecurityBindings()))
fmt.Println(j(srv.ObjectResolverBindings.GetSecurityBindings()))
}
// dial activation client.
// cc, err = dcerpc.Dial(ctx, net.JoinHostPort(os.Getenv("SERVER"), "135"))
@@ -118,17 +169,19 @@ func main() {
return
}
fmt.Println("-----------------------")
fmt.Println("REMOTE ACTIVATION")
fmt.Println("-----------------------")
fmt.Println(j(act))
for i, ifd := range act.InterfaceData {
if debug {
fmt.Println("-----------------------")
fmt.Println(i, "REMOTE ACTIVATION INTERFACE")
fmt.Println("REMOTE ACTIVATION")
fmt.Println("-----------------------")
fmt.Println(j(ifd.GetObjectReference()))
fmt.Println(j(act))
for i, ifd := range act.InterfaceData {
fmt.Println("-----------------------")
fmt.Println(i, "REMOTE ACTIVATION INTERFACE")
fmt.Println("-----------------------")
fmt.Println(j(ifd.GetObjectReference()))
}
}
std := act.InterfaceData[0].GetStandardObjectReference().Std
@@ -149,7 +202,7 @@ func main() {
l1login, err := iwbemlevel1login.NewLevel1LoginClient(ctx, wcc,
dcom.WithIPID(std.IPID),
dcerpc.WithSign(),
dcerpc.WithTargetName(os.Getenv("TARGET")), dcerpc.WithLogger(zerolog.New(os.Stdout)))
dcerpc.WithTargetName(os.Getenv("TARGET")), dcerpc.WithLogger(log))
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
@@ -164,12 +217,14 @@ func main() {
return
}
fmt.Println(j(pos))
if debug {
fmt.Println(j(pos))
}
// login to WMI.
login, err := l1login.NTLMLogin(ctx, &iwbemlevel1login.NTLMLoginRequest{
This: &dcom.ORPCThis{Version: srv.COMVersion},
NetworkResource: "//./root/cimv2",
NetworkResource: resource,
})
if err != nil {
@@ -177,7 +232,9 @@ func main() {
return
}
fmt.Println(j(login))
if debug {
fmt.Println(j(login))
}
ns := login.Namespace
@@ -190,17 +247,17 @@ func main() {
builder := query.NewBuilder(ctx, svcs, srv.COMVersion)
in := wmio.Values{
"CommandLine": "cmd.exe /Q /c calc.exe",
"CurrentDirectory": "C:\\",
if debug {
obj, _ := builder.Spawn(class).Method(method).Values(args, wmio.JSONValueToType).Object()
fmt.Println(j(obj))
}
// use simple query builder to execute the Create static method of the Win32_Process.
out, err := builder.Spawn("Win32_Process").Method("Create").Values(in).Exec().Object()
out, err := builder.Spawn(class).Method(method).Values(args, wmio.JSONValueToType).Exec().Object()
if err != nil {
fmt.Fprintln(os.Stderr, "svcs_exec_method", err)
return
}
fmt.Println(j(out))
fmt.Println(j(out.Values()))
}
+184
View File
@@ -0,0 +1,184 @@
package wmio
import "strconv"
func JSONValueToType(value any, typ CIMType) (any, bool) {
switch value := value.(type) {
case float64:
switch typ {
case Uint8:
return uint8(value), true
case Int8:
return int8(value), true
case Uint16:
return uint16(value), true
case Int16:
return int16(value), true
case Uint32:
return uint32(value), true
case Int32:
return int32(value), true
case Uint64:
return uint64(value), true
case Int64:
return int64(value), true
case Float32:
return float32(value), true
case Float64:
return value, true
}
case []float64:
switch typ {
case Uint8Array:
ret := make([]uint8, len(value))
for i := range value {
ret[i] = uint8(value[i])
}
return ret, true
case Int8Array:
ret := make([]int8, len(value))
for i := range value {
ret[i] = int8(value[i])
}
return ret, true
case Uint16Array:
ret := make([]uint16, len(value))
for i := range value {
ret[i] = uint16(value[i])
}
return ret, true
case Int16Array:
ret := make([]int16, len(value))
for i := range value {
ret[i] = int16(value[i])
}
return ret, true
case Uint32Array:
ret := make([]uint32, len(value))
for i := range value {
ret[i] = uint32(value[i])
}
return ret, true
case Int32Array:
ret := make([]int32, len(value))
for i := range value {
ret[i] = int32(value[i])
}
return ret, true
case Uint64Array:
ret := make([]uint64, len(value))
for i := range value {
ret[i] = uint64(value[i])
}
return ret, true
case Int64Array:
ret := make([]int64, len(value))
for i := range value {
ret[i] = int64(value[i])
}
return ret, true
case Float32Array:
ret := make([]float32, len(value))
for i := range value {
ret[i] = float32(value[i])
}
return value, true
case Float64Array:
return value, true
}
case string:
switch typ {
case Uint8:
ret, _ := strconv.ParseUint(value, 10, 8)
return uint8(ret), true
case Int8:
ret, _ := strconv.ParseInt(value, 10, 8)
return uint8(ret), true
case Uint16:
ret, _ := strconv.ParseUint(value, 10, 16)
return uint16(ret), true
case Int16:
ret, _ := strconv.ParseInt(value, 10, 16)
return uint16(ret), true
case Uint32:
ret, _ := strconv.ParseUint(value, 10, 32)
return uint32(ret), true
case Int32:
ret, _ := strconv.ParseInt(value, 10, 32)
return uint32(ret), true
case Uint64:
ret, _ := strconv.ParseUint(value, 10, 64)
return uint64(ret), true
case Int64:
ret, _ := strconv.ParseInt(value, 10, 64)
return uint64(ret), true
case Float32:
ret, _ := strconv.ParseFloat(value, 32)
return float32(ret), true
case Float64:
ret, _ := strconv.ParseFloat(value, 64)
return float64(ret), true
}
case []string:
switch typ {
case Uint8Array:
ret := make([]uint8, len(value))
for i := range value {
rret, _ := strconv.ParseUint(value[i], 10, 8)
ret[i] = uint8(rret)
}
return ret, true
case Int8Array:
ret := make([]int8, len(value))
for i := range value {
rret, _ := strconv.ParseInt(value[i], 10, 8)
ret[i] = int8(rret)
}
return ret, true
case Uint16Array:
ret := make([]uint16, len(value))
for i := range value {
rret, _ := strconv.ParseUint(value[i], 10, 16)
ret[i] = uint16(rret)
}
return ret, true
case Int16Array:
ret := make([]int16, len(value))
for i := range value {
rret, _ := strconv.ParseInt(value[i], 10, 16)
ret[i] = int16(rret)
}
return ret, true
case Uint32Array:
ret := make([]uint32, len(value))
for i := range value {
rret, _ := strconv.ParseUint(value[i], 10, 32)
ret[i] = uint32(rret)
}
return ret, true
case Int32Array:
ret := make([]int32, len(value))
for i := range value {
rret, _ := strconv.ParseInt(value[i], 10, 32)
ret[i] = int32(rret)
}
return ret, true
case Uint64Array:
ret := make([]uint64, len(value))
for i := range value {
rret, _ := strconv.ParseUint(value[i], 10, 64)
ret[i] = uint64(rret)
}
return ret, true
case Int64Array:
ret := make([]int64, len(value))
for i := range value {
rret, _ := strconv.ParseInt(value[i], 10, 64)
ret[i] = int64(rret)
}
return ret, true
}
}
return value, false
}
+3 -3
View File
@@ -78,7 +78,7 @@ func (b *builder) Spawn(cls string) *builder {
obj, err := b.wmi.GetObject(b.ctx, &iwbemservices.GetObjectRequest{
This: &dcom.ORPCThis{Version: b.ver},
ObjectPath: &oaut.String{Data: "Win32_Process"},
ObjectPath: &oaut.String{Data: cls},
Object: &wmi.ClassObject{},
})
@@ -109,13 +109,13 @@ func (b *builder) Method(m string) *builder {
}
// Values function sets the parameters for the object.
func (b *builder) Values(values wmio.Values) *builder {
func (b *builder) Values(values wmio.Values, convert ...func(any, wmio.CIMType) (any, bool)) *builder {
if b.obj == nil {
return b.withErrf("Values: object is nil")
}
params, err := b.obj.New(values)
params, err := b.obj.New(values, convert...)
if err != nil {
return b.withErrf("Values: %v", params)
}
+14 -5
View File
@@ -147,7 +147,7 @@ func (o *Object) Method(n string) (*Object, *Object, error) {
type Values map[string]any
func (o *Object) New(values Values) (*Object, error) {
func (o *Object) New(values Values, convert ...func(any, CIMType) (any, bool)) (*Object, error) {
if values == nil {
values = make(map[string]any)
@@ -187,12 +187,21 @@ func (o *Object) New(values Values) (*Object, error) {
continue
}
typ, err := ValueType(value)
if err != nil {
return nil, fmt.Errorf("%s: %v", inst.CurrentClass.Properties[i].Name, err)
var typeOk bool
if len(convert) > 0 {
value, typeOk = convert[0](value, inst.CurrentClass.Properties[i].Value.Type)
inst.Properties[i] = &Property{Value: Value{Type: inst.CurrentClass.Properties[i].Value.Type, Value: value}}
}
if !typeOk {
typ, err := ValueType(value)
if err != nil {
return nil, fmt.Errorf("%s: %v", inst.CurrentClass.Properties[i].Name, err)
}
inst.Properties[i] = &Property{Value: Value{Type: typ, Value: value}}
}
inst.Properties[i] = &Property{Value: Value{Type: typ, Value: value}}
}
return &Object{Instance: inst}, nil