oiweiwei / go-msrpc

The DCE/RPC / MS-RPC Codegen/Client for Go
MIT License
42 stars 1 forks source link

[Issues] Auth user with member of `Domain users` issues #2

Closed XiaoliChan closed 4 months ago

XiaoliChan commented 4 months ago

I am trying to use it with goroutine in some cases, and I make a function like this

func WMIConn(info *common.HostInfo, user string, pass string, hash string, clientName string) (bool, error) {
    ctx := gssapi.NewSecurityContext(context.Background())
    if hash != "" {
        gssapi.AddCredential(credential.NewFromNTHash(user, hash))
    } else {
        gssapi.AddCredential(credential.NewFromPassword(user, pass))
    }
    gssapi.AddMechanism(ssp.NTLM)

    // ObjectExporter uses well-known endpoint 135.
    cc, err := dcerpc.Dial(ctx, fmt.Sprintf("%v:%v", info.Host, info.Ports), dcerpc.WithTimeout(time.Duration(common.Timeout)*time.Second))
    if err != nil {
        return false, err
    }
       ......

But I will get this error

panic: mechanism 1.3.6.1.4.1.311.2.2.10 already exist

goroutine 23 [running]:
wmi-brutreforce/Plugins/libs/go-msrpc/ssp/gssapi.(*MechanismStore).AddMechanism(0xc0003c1800, {0x1852290, 0x0})
        /home/dev/test/Plugins/libs/go-msrpc/ssp/gssapi/mechanism_store.go:22 +0x2a7
wmi-brutreforce/Plugins/libs/go-msrpc/ssp/gssapi.AddMechanism(...)
        /home/dev/test/Plugins/libs/go-msrpc/ssp/gssapi/mechanism_store.go:68
wmi-brutreforce/Plugins.WMIConn(0xc0013ae0c0, {0x7ffcf8076ebd, 0xd}, {0x1362248, 0xb}, {0x0, 0x0}, {0xc000b92db0?, 0x12?})
        /home/dev/test/Plugins/wmi.go:219 +0x1cb
wmi-brutreforce/Plugins.WMI_PlainText_login.func1()
        /home/dev/test/Plugins/wmi.go:119 +0xff
created by wmi-brutreforce/Plugins.WMI_PlainText_login
        /home/dev/test/Plugins/wmi.go:116 +0x165
oiweiwei commented 4 months ago

The Call AddMechanism is intended to be used in global initialization, just set it once in main package, or using once.Do

oiweiwei commented 4 months ago

If want to use it on per-thread level, use dcerpc.WithMechanism and dcerpc.WithCredentials to pass it on client level.

See doc: https://pkg.go.dev/github.com/oiweiwei/go-msrpc/dcerpc#SecurityContextOption

XiaoliChan commented 4 months ago

I follow the example and I got this error

cannot use creds (variable of type credential.Password) as gssapi.Credential value in argument to dcerpc.WithCredentials: credential.Password does not implement gssapi.Credential (missing method MechanismTypes)compilerInvalidIfaceAssign

Code

creds := credential.NewFromPassword(username, password)
cli, err := iobjectexporter.NewObjectExporterClient(ctx, cc, dcerpc.WithSeal(), dcerpc.WithCredentials(creds))

The data type is wrong in your example by following the doc.

oiweiwei commented 4 months ago

Yeah, it seems to be inconvenient (I'll think on how to make it easeier), but wrap through NewFromPassword with gssapi.NewCredential("", nil, gssapi.InitiateAndAccept, credential.NewFromPassword(...))

XiaoliChan commented 4 months ago

Okay, but after I add dcerpc.WithCredentials(gssapi.NewCredential("", nil, gssapi.InitiateAndAccept, creds)), dcerpc.WithMechanism(&ssp.NTLM), it did authentication, but it failed in

// start services client.
    svcs, err := iwbemservices.NewServicesClient(ctx, wcc, dcom.WithIPID(ns.InterfacePointer().IPID()))
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        return
    }

Log

bind: parse options: security context is empty
oiweiwei commented 4 months ago

ctx := gssapi.NewSecurityContext(context.Background())

This line is still needed. And just to make it nicer pass just ssp.NTLM (not pointer)

oiweiwei commented 4 months ago

Oh, and I think you need to initialize context and pass creds to all New*Client calls.

XiaoliChan commented 4 months ago

ctx := gssapi.NewSecurityContext(context.Background())

This line is still needed. And just to make it nicer pass just ssp.NTLM (not pointer)

Yes, I use it, but I still got error

oiweiwei commented 4 months ago

Oh now I see, since you're defining security on local level, iwbemservices.NewServicesClient must also have all this WithSeal WithCredentials, WithMechanism

XiaoliChan commented 4 months ago

Code

svcs, err := iwbemservices.NewServicesClient(ctx, wcc, dcom.WithIPID(ns.InterfacePointer().IPID()), dcerpc.WithCredentials(gssapi.NewCredential("", nil, gssapi.InitiateAndAccept, creds)), dcerpc.WithMechanism(&ssp.NTLM))

And now got a new error

bind: could not bind the selected transport: alter context: read packet: decode packet: fault: nca_proto_error (0x1c01000b): The RPC client or server protocol has been violated.
XiaoliChan commented 4 months ago

Oh, I miss the dcerpc.withseal(), now is works!

XiaoliChan commented 4 months ago

Another bug I found, is when I check the RPC endpoint filter function, if I provide an IP address in the dcerpc.WithTargetName(), it always returns a "ncacn_ip_tcp:HOSTNAME[port]", this does not make sense, the endpoint should return which contains dcerpc.WithTargetName()

Like

for _, i := range act.OXIDBindings.GetStringBindings() {
        if strings.Contains(i.String(), target) {
            stringbinding = i.String()
        }
    }

In impacket, it always did it.

oiweiwei commented 4 months ago

Oh, I miss the dcerpc.withseal(), now is works!

By design, it should reuse the context established with NewLevel1LoginClient, so that NewServices must have only IPID option... Let me check first if custom context level options are working with this.

XiaoliChan commented 4 months ago

BTW, how to set the timeout in this part?

login, err := l1login.NTLMLogin(
        ctx,
        &iwbemlevel1login.NTLMLoginRequest{
            This:            &dcom.ORPCThis{Version: srv.COMVersion},
            NetworkResource: "//./root/cimv2",
        },
    )

In AD environment, if provide a low-priv user, it will be hanging in this part.

oiweiwei commented 4 months ago

BTW, how to set the timeout in this part?

login, err := l1login.NTLMLogin(
      ctx,
      &iwbemlevel1login.NTLMLoginRequest{
          This:            &dcom.ORPCThis{Version: srv.COMVersion},
          NetworkResource: "//./root/cimv2",
      },
  )

In AD environment, if provide a low-priv user, it will be hanging in this part.

As of now it's controlled by dcerpc.WithTimeout option that must be passed to Dial() function. This would be the global timeout for all operations (receive fragment, send fragment)

oiweiwei commented 4 months ago

Oh, I miss the dcerpc.withseal(), now is works!

I've added few use-cases in the doc with last commit, so basically in such cases (when you have a single TCP/IP connection) it makes sense to use exactly the same security context (and not re-establish new one every time).

So to achive this, use gssapi.NewSecurityContext(ctx, ssp.NTLM, yourCreds)

For first client (iwbemlogin) you should use WithSign() / WithSeal()

For second client (iwbemservices) you should just specify IPID and all the reset will be inherited from the client above.

oiweiwei commented 4 months ago

Another bug I found, is when I check the RPC endpoint filter function, if I provide an IP address in the dcerpc.WithTargetName(), it always returns a "ncacn_ip_tcp:HOSTNAME[port]", this does not make sense, the endpoint should return which contains dcerpc.WithTargetName()

Like

for _, i := range act.OXIDBindings.GetStringBindings() {
      if strings.Contains(i.String(), target) {
          stringbinding = i.String()
      }
  }

In impacket, it always did it.

well, that makes sense to pick up the best binding. will add it.

XiaoliChan commented 4 months ago

I will test it tomorrow :)

XiaoliChan commented 4 months ago

As of now it's controlled by dcerpc.WithTimeout option that must be passed to Dial() function. This would be the global timeout for all operations (receive fragment, send fragment)

Weird, sometimes it takes no effect in go routine

oiweiwei commented 4 months ago

Weird, sometimes it takes no effect in go routine

can we nail it down somehow, I mean, at which stage exactly it got stuck (maybe logs would be helpful, if you can add WithLogger to all Dial statements), by idea I see there is always sort of context cancel thing, but frankly saying I cannot be 100% sure that it works.

XiaoliChan commented 4 months ago

can we nail it down somehow, I mean, at which stage exactly it got stuck (maybe logs would be helpful, if you can add WithLogger to all Dial statements), by idea I see there is always sort of context cancel thing, but frankly saying I cannot be 100% sure that it works.

Ah, yes, I will provide the log tomorrow.

XiaoliChan commented 4 months ago

Env:

Here is the log

{"level":"debug","message":"etablishing new transport for binding ncacn_ip_tcp:192.168.85.201[49667]"}
{"level":"debug","message":"dialing tcp 192.168.85.201:49667"}
{"level":"debug","message":"dialing tcp 192.168.85.201:49667 done"}
{"level":"debug","message":"new transport for binding ncacn_ip_tcp:192.168.85.201[49667] has been successfully established"}
{"level":"debug","message":"binding the selected transport"}
{"level":"debug","packet_type":"bind","flags":"first|last|support_header_sign_or_cancel|multiplexing","frag_length":176,"auth_length":52,"call_id":1,"message":"write_packet"}
{"level":"debug","packet_type":"bind_ack","flags":"first|last|support_header_sign_or_cancel|multiplexing","frag_length":314,"auth_length":222,"call_id":1,"message":"read packet done"}
{"level":"debug","max_xmit_frag":4096,"max_recv_frag":4096,"group_id":21100,"secondary_addr":"49667","multiplexing":true,"security_context_multiplexing":true,"keep_conn_on_orphaned":true,"network_timeout":5000,"programmable_deadline":3000,"message":"negotiated_features"}
{"level":"debug","packet_type":"auth3","flags":"first|last|support_header_sign_or_cancel","frag_length":424,"auth_length":396,"call_id":2,"message":"write_packet"}
{"level":"debug","message":"selected transport has been successfully binded"}
{"level":"debug","message":"started sender routine"}
{"level":"debug","call_id":3,"message":"serving call"}
{"level":"debug","call_id":3,"in":{"this":{"version":{"major_version":5,"minor_version":7},"flags":0,"cid":null,"extensions":null},"that":null,"locale_version":0,"return":0},"message":"operation input"}
{"level":"debug","packet_type":"request","flags":"first|object","frag_length":0,"auth_length":0,"call_id":3,"alloc_hint":0,"context_id":6,"op_num":3,"object":"0002742d-0154-0000-da99-6f8c55b71f27","message":"writing packet"}
{"level":"debug","call_id":3,"message":"send is done"}
{"level":"debug","packet_type":"request","flags":"first|last|object","frag_length":112,"auth_length":16,"call_id":3,"alloc_hint":0,"context_id":6,"op_num":3,"object":"0002742d-0154-0000-da99-6f8c55b71f27","message":"writing packet done"}
{"level":"debug","message":"started receiver routine"}
{"level":"debug","call_id":3,"message":"serving response"}
{"level":"debug","packet_type":"response","flags":"first|last","frag_length":64,"auth_length":16,"call_id":3,"message":"reading packet"}
{"level":"debug","packet_type":"response","flags":"first|last","frag_length":64,"auth_length":16,"call_id":3,"alloc_hint":16,"context_id":6,"cancel_count":0,"message":"reading packet done"}
{"level":"debug","call_id":3,"out":{"this":{"version":{"major_version":5,"minor_version":7},"flags":0,"cid":null,"extensions":null},"that":{"flags":0,"extensions":null},"locale_version":1,"return":0},"message":"operation output"}
{
  "that": {
    "flags": 0,
    "extensions": null
  },
  "locale_version": 1,
  "return": 0
}
{"level":"debug","call_id":4,"message":"serving call"}
{"level":"debug","call_id":4,"in":{"this":{"version":{"major_version":5,"minor_version":7},"flags":0,"cid":null,"extensions":null},"that":null,"network_resource":"//./root/cimv2","preferred_locale":"","flags":0,"context":null,"namespace":null,"return":0},"message":"operation input"}
{"level":"debug","packet_type":"request","flags":"first|object","frag_length":0,"auth_length":0,"call_id":4,"alloc_hint":0,"context_id":6,"op_num":6,"object":"0002742d-0154-0000-da99-6f8c55b71f27","message":"writing packet"}
{"level":"debug","call_id":4,"message":"send is done"}
{"level":"debug","call_id":4,"message":"serving response"}
{"level":"debug","packet_type":"request","flags":"first|last|object","frag_length":160,"auth_length":16,"call_id":4,"alloc_hint":0,"context_id":6,"op_num":6,"object":"0002742d-0154-0000-da99-6f8c55b71f27","message":"writing packet done"}
{"level":"error","error":"context deadline exceeded","message":"receiver: read buffer error"}
{"level":"error","call_id":4,"error":"context deadline exceeded","message":"serving response error"}

The code is stuck in this part and didn't return error (not goroutine issues)

    // login to WMI.
    login, err := l1login.NTLMLogin(ctx, &iwbemlevel1login.NTLMLoginRequest{
        This:            &dcom.ORPCThis{Version: srv.COMVersion},
        NetworkResource: "//./root/cimv2",
    })

    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        return
    }
oiweiwei commented 4 months ago

Oh, this is something. So deadlines are being fired but it fires to the call before the NTLMLogin (EstablishPosition). Next deadline causes deadlock in code. Will check on that.

UPD no it just deadlocks from the call itself as you mentioned.

XiaoliChan commented 4 months ago

UPD no it just deadlocks from the call itself as you mentioned.

Yes, and others work well in goroutine!

oiweiwei commented 4 months ago

@XiaoliChan let me know if this (https://github.com/oiweiwei/go-msrpc/commit/76443ca6b7c0debc738f840f01a8f1aa150de2f8) fixes the issue

XiaoliChan commented 4 months ago

@oiweiwei Sure, thanks for your work, I will test it tomorrow, it's close to 12 PM here.

XiaoliChan commented 4 months ago

@oiweiwei Confirm the bug is fixed, now it can receive the context error, issue closed image