Closed XiaoliChan closed 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
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
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.
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(...))
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
ctx := gssapi.NewSecurityContext(context.Background())
This line is still needed. And just to make it nicer pass just ssp.NTLM (not pointer)
Oh, and I think you need to initialize context and pass creds to all New*Client calls.
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
Oh now I see, since you're defining security on local level, iwbemservices.NewServicesClient must also have all this WithSeal WithCredentials, WithMechanism
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.
Oh, I miss the dcerpc.withseal()
, now is works!
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.
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.
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.
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)
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.
Another bug I found, is when I check the
RPC endpoint filter
function, if I provide an IP address in thedcerpc.WithTargetName()
, it always returns a "ncacn_ip_tcp:HOSTNAME[port]", this does not make sense, the endpoint should return which containsdcerpc.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.
I will test it tomorrow :)
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
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.
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.
Env:
Only Domain Users group
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
}
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.
UPD no it just deadlocks from the call itself as you mentioned.
Yes, and others work well in goroutine!
@XiaoliChan let me know if this (https://github.com/oiweiwei/go-msrpc/commit/76443ca6b7c0debc738f840f01a8f1aa150de2f8) fixes the issue
@oiweiwei Sure, thanks for your work, I will test it tomorrow, it's close to 12 PM here.
@oiweiwei Confirm the bug is fixed, now it can receive the context error, issue closed
I am trying to use it with goroutine in some cases, and I make a function like this
But I will get this error