Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DNS Issues with resolvers using DNS-0x20 encoding #1587

Open
miszr opened this issue Mar 6, 2024 · 12 comments
Open

DNS Issues with resolvers using DNS-0x20 encoding #1587

miszr opened this issue Mar 6, 2024 · 12 comments

Comments

@miszr
Copy link

miszr commented Mar 6, 2024

Describe the bug
If a host, windows was used during testing, is configured to use a DNS resolver which employs what is known as DNS-0x20 encoding, the DNS C2 communication channel is never established.

The encoding known as DNS-0x20, will basically randomly change the case of each character in a DNS query. This will garble the DNS queries sent to the sliver server which result in error messages in the logs.

Here is an example (cleaned for privacy) of what will be seen in the sliver.log file:

INFO[2024-03-06T09:40:52Z] [sliver/server/c2/dns.go:361] 'bAAKbH898rE8.sUbdoMAIn.eXAmpLe.coM.' is subdomain of 'subdomain.example.com.' 
ERRO[2024-03-06T09:40:52Z] [sliver/server/c2/dns.go:377] [dns] error decoding subdata: invalid dns message

To Reproduce
Configure a host to use the Google public DNS resolver (8.8.8.8) and attempt to deploy a DNS beacon.

Expected behavior
The beacon is successfully deployed and can be used.

Desktop (please complete the following information):

  • OS: Windows
  • Version 1.5.42

Additional context
Found this article as well as the linked whitepaper

@moloch--
Copy link
Member

moloch-- commented Mar 6, 2024

I think the real bug is in the detection of case sensitive vs. case insensitive encoding, the implant should* be able to detect this manipulation and fallback to base32 but doesn't.

@miszr
Copy link
Author

miszr commented Mar 6, 2024

There are multiple issues here:

First Issue

An example of a message which failes to decode is the initial TOTP message, which is always base32 encoded.

func (s *SliverDNSClient) otpMsg() (string, error) {
otpMsg := &dnspb.DNSMessage{
Type: dnspb.DNSMessageType_TOTP,
ID: uint32(0), // Take advantage of the variable length encoding
}
data, err := proto.Marshal(otpMsg)
if err != nil {
return "", err
}
msg, _ := s.base32.Encode(data)
return string(msg), nil
}

The problem is that the DNS-0x20 encoding will alter the case of the subdata, which then fails decoding in the base32 encoder, as there are characters which are uppercase.

const base32Alphabet = "ab1c2d3e4f5g6h7j8k9m0npqrtuvwxyz"
var sliverBase32 = base32.NewEncoding(base32Alphabet).WithPadding(base32.NoPadding)
// Encode - Base32 Encode
func (e Base32) Encode(data []byte) ([]byte, error) {
return []byte(sliverBase32.EncodeToString(data)), nil
}
// Decode - Base32 Decode
func (e Base32) Decode(data []byte) ([]byte, error) {
return sliverBase32.DecodeString(string(data))
}

Possible Solution

A solution to this is doing strings.ToLower on the input data.

return sliverBase32.DecodeString(strings.ToLower(string(data)))

This however is partially problematic(programming wise) as, it transfers the responsibility from server/c2/dns.go to the base32 encoder.

Second Issue

Sometimes, a base32 encoded value mangled by DNS-0x20(for example) is interpreted as a base58 encoded value. I have seen you mention this issue here: #1354 (comment)

This can become a problem for example here:

sliver/server/c2/dns.go

Lines 382 to 394 in c8a7948

msg, checksum, err := s.decodeSubdata(subdomain)
if err != nil {
dnsLog.Errorf("[dns] error decoding subdata: %v", err)
return s.nameErrorResp(req)
}
// TOTP Handler can be called without dns session ID
if msg.Type == dnspb.DNSMessageType_TOTP {
return s.handleHello(domain, msg, req)
}
// All other handlers require a valid dns session ID
_, ok := s.sessions.Load(msg.ID & sessionIDBitMask)

When the data gets interpreted as base58 and produces semi-valid protobuf data, the decodeSubdata functions returns a message and no error. This becomes a problem on line 394 as we use the ID field, which is incorrectly interpreted.

This issue will produce a log warning:

dnsLog.Warnf("[dns] session not found for id %v (%v)", msg.ID, msg.ID&sessionIDBitMask)

Possible Solution

A possible solution could be that if we attempt to decode the subdata using the current method and we are unable to find a valid session id we could attempt to decode the lowercase value of the subdata.

The solution would look something like this:

	dnsLog.Debugf("[dns] processing req for subdomain = %s", subdomain)
	msg, checksum, err := s.decodeSubdata(subdomain)
	if err != nil {
		dnsLog.Errorf("[dns] error decoding subdata: %v", err)
		return s.nameErrorResp(req)
	}

	// TOTP Handler can be called without dns session ID
	if msg.Type == dnspb.DNSMessageType_TOTP {
		return s.handleHello(domain, msg, req)
	}

	// All other handlers require a valid dns session ID
	_, ok := s.sessions.Load(msg.ID & sessionIDBitMask)
	if !ok {
		subdomain = strings.ToLower(subdomain)

		dnsLog.Debugf("[dns] reprocessing req for subdomain = %s", subdomain)
		msg, checksum, err = s.decodeSubdata(subdomain)
		if err != nil {
			dnsLog.Errorf("[dns] error decoding subdata: %v", err)
			return s.nameErrorResp(req)
		}
		_, ok := s.sessions.Load(msg.ID & sessionIDBitMask)
		if !ok {
			dnsLog.Warnf("[dns] session not found for id %v (%v)", msg.ID, msg.ID&sessionIDBitMask)
			return s.nameErrorResp(req)
		}
	}

This is a bit of a hacky solution, but should work.

Better Solution

Would be nice to be able to determine whether the resolver manipulates the DNS queries using for example DNS-0x20 encoding.

@moloch--
Copy link
Member

moloch-- commented Mar 7, 2024

We've removed TOTP in v1.6, it would be good to address all these issues in that branch. Perhaps we should just use a single bit to indicate the Base32 vs. Base58 instead of trying to detect it.

@miszr
Copy link
Author

miszr commented Mar 7, 2024

Sorry, but I do not understand. The code I referenced in the previous comment is from the master branch and not to the 1.5.x/master branch.

Is there a more updated version of v1.6?

@moloch--
Copy link
Member

moloch-- commented Mar 7, 2024

Oh yes, you're right. We did remove the TOTP auth, but the message is still called TOTP.

@mragusa
Copy link

mragusa commented Apr 11, 2024

I am using BIND9 with a custom TLD in a lab environment and encountering the same issue in v1.5.42:

"file":"github.com/bishopfox/sliver/server/c2/dns.go:377","func":"github.com/bishopfox/sliver/server/c2.(*SliverDNSServer).handleC2","level":"eror","msg":"[dns] error decoding subdata: invalid dns message","pkg":"c2","stream":"dns","time":"2024-04-11T13:51:09-04:00"}

{"file":"github.com/bishopfox/sliver/server/c2/dns.go:361","func":"github.com/bishopfox/sliver/server/c2.(*SliverDNSServer).isC2SubDomain","level":"info","msg":"'Y1utwLtf8cjUiQzG79EKeAhKMrKCtAqxd4WPt3nrrEW5DAa4B4hBqGMxXcjHRtR.6EhPZHPu9Mfr3FLgNZTrNm1kvfVGUbJfM9r6y3kTav5TE1kQZDETnZ7MngmqA2m.xskXt92QdYcAwqYEf1SPDr8ncU8nzMKUNUJK8bSkSKqZyqHPkKwKY7ePHJdzuiP.xEgdNGHeWVmhfKB64ro9RZKBK2Ded4Y2iAq8Y.beacon.example.com.' is subdomain of 'beacon.example.com.'","pkg":"c2","stream":"dns","time":"2024-04-11T13:51:09-04:00"}

The client binary is running on debian linux 12.4

@derekkddj
Copy link

i am havking the same error

@GeneralBison
Copy link

I'm having this issue even with force-base32 enabled. Should this work or is it a problem with my setup?

@miszr
Copy link
Author

miszr commented May 3, 2024

I'm having this issue even with force-base32 enabled. Should this work or is it a problem with my setup?

Unfortunately no. The base32 encoder does not force lowercase characters, which will mean that decoding will not be possible using base32.

@derekkddj
Copy link

for now i can make it work using DNS server 1.1.1.1, but i can not use this is production if the "victim" is using google DNS resolvers.

@try-catch-try
Copy link

Our team is also experiencing the same problem when using 8.8.8.8 resolver as well as some others. While using server version 1.5.42 I attempted to start the dns listener with the -D option to disable TOTP auth as suggested but this did not appear to fix the problem. I also tried to compile and run version 1.6 of the server using 9510a5f and it appears that the DNS command has been removed so I was unable to test.

@moloch--
Copy link
Member

I'm working on a fix for v1.6 and hoping it have it done soon, it's the last major bug before the v1.6 release!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants