BGGP4: PleaseMom, QUANTUM, Rat?

Mar 31, 2024

15 mins read

For this last years Binary Golf Grand Prix the goal was to:

Create the smallest self-replicating file.

Requirements:

  • Produce exactly 1 copy of itself
  • Name the copy “4”
  • Not execute the copied file
  • Print, return, or display the number 4

I sat down with some beer, as one does, and resolved that my personal twist on the challenge would be to not just do a single filetype, but a chain of obscure filetypes. I scripted out enumeration of all of the default file handlers for Windows 7 in a blog I titled Obscure Windows File Type.

After mapping out a insanely complex chain of filetypes in Windows I intended to use, I started in! And if you can’t already tell, I totally missed the deadline which was ~ a year ago.

fingerguns

  • Side Note: I’m _mattata on Twitter, you should give me a follow. I do stuff like this often.

Instead, I kept getting distracted turning over rocks that largely haven’t been touched in years. I found:

  • Windows easter eggs
  • Top-Score Irony
  • A digital signature bypass for an executable format

And then I promptly forgot to write any of it down and it has sat in a folder on my machine for many months.

So here goes: PleaseMom, QUANTUM, Rat.

This mindmap is available in full resolution here.

plan

For code samples provided in this blog, I have “pretty-printed” them where possible to aid the reader. In practice, assume that all whitespace is stripped at a minimum.

4.gadget

This file extension in Windows is the file extension for Windows Sidebar, which was last officially supported in Windows 7.

Per Microsoft Security Advisory 2719662:

An attacker could create a malicious Gadget and then trick a user into installing the malicious Gadget. Once installed, the malicious Gadget could run arbitrary code in the context of the current user. If the current user is logged on with administrative user rights, an attacker could take complete control of the affected system.

Fun fact: To this day, most commercial malware analysis sandboxes still can’t accurately analyze .gadget.

This seemed like a good file format to start with at the top level because ultimately it’s an executable archive. .gadget is actually a renamed .zip (or .cab file, as I discovered), which means that anything I golfed, or tried to make small, would ultimately be compressed as the final wrapper. Nice!

When a user executes a .gadget file C:\Program Files\Windows Sidebar\Sidebar.exe is called to load the file and execute it. As part of that workflow, the archive is extracted/exploded to %LOCALAPPDATA%\Microsoft\Windows Sidebar\Gadgets\.

First the gadget.xml is read from the archive:

<?xml version="1.0" encoding="utf-8" ?>
<gadget>
	<name>""</name>
	<version>0</version>
	<hosts>
		<host name="sidebar">
			<base type="HTML" apiVersion="1.0.0" src="4.html" />
			<permissions>Full</permissions>
			<platform minPlatformVersion="1.0" />
		</host>
	</hosts>
</gadget>

Above, I instruct the renderer to load 4.html, which has the following contents.

<html>
	<head>
		<script>
		System.Shell.execute("powershell", "-file \""+System.Gadget.path+"\\4.ps1\"");
	</script>
	</head>

4


</html>

This leverages the Windows Sidebar System Object reference to call System.Shell.execute while also looking up the exploded gadget’s path on disk with System.Gadget.path.

Ultimately, we are now running an arbitrary .ps1 powershell script along with any other bundled assets we manage to cram in our 4.gadget.

4.ps1

Powershell! I’ve written a lot of this in my lifetime, it should be fairly straightforward. I originally intended to use this powershell step as my “easily orchestrate everything else that’s complicated” freebie. Easy right?

I got things working. I started exploring all of the .vbs–>.wsh–>.js–>.vbe–>.wsf–>.jse chains and got them working too! Magic was HAPPENING!

Then I reached the convoluted end of the big mindmap above and found the wonderful .rat file format. Knowing that since I use a rat as my profile picture, I absolutely had to use this file format, I realized that I now had an additional nesting of files. While I’d already reasonably golfed them, sitting right at the bottom of the mindmap is the Diagnostic.Cabinet AKA .cab.

.cab files are compressed archives that when executed launch C:\Windows\system32\msdt.exe /cab "%1". A compressed archive format for all of the weird chains I put together? Sure, I’ll take free size savings.

Well, this is precisely when things sprialed entirely out of control. Suddenly all of the .gadget files I was building started throwing errors related to the .cab file. After researching the [MS-CAB]: Cabinet File Format trying to figure out what I’d somehow messed up.

It turns out that when a .gadget file is launched, sidebar.exe will attempt to validate the archive format. Not only are .zip archives valid .gadget, but .cab are as well. Additionally, the .cab check happens first, so if a .gadget that is actually a .zip contains a .cab within in the .zip, the .cab is exploded first.

long edi = Package::VerifyGadgetExtension(ecx, ((char*)ecx + 0x20e));
if (edi >= 0)
{
    bool eax_3 = CabPackage::IsCabinet(((char*)ecx + 6));
    long eax_8;
    long eax_10;
    if (eax_3 == 0)
    {
        ZipPackage::ZipPackage(((char*)ebp - 0x6a8));
        *(int8_t*)((char*)ebp - 4) = 2;
        int80_t st0_1;
        eax_8 = ZipPackage::Load(((char*)ebp - 0x6a8), ((char*)ecx + 6));
        if (eax_8 >= 0)
        {
            eax_10 = ZipPackage::ExplodeToFolder(((char*)ebp - 0x6a8), *(int32_t*)((char*)ebp - 0x630));
            if (eax_10 >= 0)
            {
                *(int8_t*)((char*)ebp - 4) = 0;
                ZipPackage::~ZipPackage(((char*)ebp - 0x6a8));
            }
        }
        if ((eax_8 < 0 || (eax_8 >= 0 && eax_10 < 0)))
        {
            StockAtlLib::ReportError(0x80004005, false, 0x7a512);
            StockAtlLib::DisplayError(0x80004005, nullptr, data_1098320, 0x1770, 0x1771, 0x1772);
            *(int8_t*)((char*)ebp - 4) = 0;
            ZipPackage::~ZipPackage(((char*)ebp - 0x6a8));
        }
    }

This really upset me and I wanted to find a workaround. From my work on BGGP2 which focused on polyglots I knew I could probably just offset the magic header of the .cab so that the CabPackage::IsCabinet would fall through to the default ZipPackage::ZipPackage handling.

Somewhere along the lines, I mistakenly deleted my chain of .vbs–>.wsh–>.js–>.vbe–>.wsf–>.jse while trying to golf the self-extraction. Regardless, here’s the Powershell stub I came up with:

$a=$false;
$b=gc $MyInvocation.MyCommand.Path -en byte;
foreach($c in $b){
    if($a -eq $true){
        ac "4.ex_" -va ([byte[]]$c) -en byte
    }
    if($c -eq 0xff){
        $a = $true
    }
}
cmd /c copy /b "%windir%\system32\extrac32.exe"+"4.ex_" "4.exe";
.\4.exe /Y;
start-sleep 2;
.\4.cpl;

The the letters gc, -eq, ac, -va, -en are shorthands. In Powershell, many longer flags/function names can be abreviated as long as their shortened version is unambiguous against other options/flags on the same function. However, this also isn’t always true. As best as I can tell this is a “try it and find out” feature, super useful for golfing!

The above script reads itself as bytes until it encounters a byte with value 0xff, which is a value that will never occur in a typical powershell script. At this point the $a variable is set to $true, and all of the following bytes are written to a new file as 4.ex_, where 4.ex_ is actually a .cab file.

The last 4 lines of the script:

  • Prepend 4.ex_ ( which is actually a .cab) with the contents extrac32.exe (A signed binary distributed with Windows) as 4.exe to form an executable self-extracting .cab archive.
  • Run 4.exe to extract the contents of the .cab, which in this case is the Windows Control Panel item 4.cpl
  • Run the 4.cpl Windows Control Panel item

The reason for the .cab file being named 4.ex_ is clearly something I did deliberately, but I can’t for the life of me remember why. I think I picked it up from the old DIAMOND.EXE docs I found, probably not needed.

But where’s the .cab?

The stub .ps1 seen above is used with the following python builder script to sandwich the entire binary contents of the .cab inside of powershell’s

<#
multi-
    line
        comments
#>
import sys
import getopt

def main(argv):
    inputstub = ''
    inputcab = ''
    outputfile = ''
    opts, args = getopt.getopt(argv,"hs:c:o:",["stub=","cab=","out="])
    for opt, arg in opts:
        if opt == '-h':
            print ('stubgen.py -s <powershell stub> -c <cab arc> -o <output file>')
            sys.exit()
        elif opt in ("-s", "--stub"):
            inputstub = arg
        elif opt in ("-c", "--cab"):
            inputcab = arg
        elif opt in ("-o", "--out"):
            outputfile = arg
    print ('Stub file is ', inputstub)
    print ('Cab file is ', inputcab)
    print ('Output file is ', outputfile)

    with open(outputfile, 'wb') as fo:
        with open(inputstub, 'rb') as fi:
            fo.write(fi.read())
        fo.write(b'<#')
        fo.write(b'\xFF')
        with open(inputcab, 'rb') as fi:
            fo.write(fi.read())
        fo.write(b'#>')
    
if __name__ == "__main__":
    main(sys.argv[1:])

The resulting 4.ps1 is valid powershell, but also contains a 4.cab which contains a 4.cpl.

00000000: 2461 3d24 6661 6c73 653b 2462 3d67 6320  $a=$false;$b=gc 
00000010: 244d 7949 6e76 6f63 6174 696f 6e2e 4d79  $MyInvocation.My
00000020: 436f 6d6d 616e 642e 5061 7468 202d 656e  Command.Path -en
00000030: 2062 7974 653b 666f 7265 6163 6828 2463   byte;foreach($c
00000040: 2069 6e20 2462 297b 6966 2824 6120 2d65   in $b){if($a -e
00000050: 7120 2474 7275 6529 7b61 6320 2234 2e65  q $true){ac "4.e
00000060: 785f 2220 2d76 6120 285b 6279 7465 5b5d  x_" -va ([byte[]
00000070: 5d24 6329 202d 656e 2062 7974 657d 6966  ]$c) -en byte}if
00000080: 2824 6320 2d65 7120 3078 6666 297b 2461  ($c -eq 0xff){$a
00000090: 203d 2024 7472 7565 7d7d 636d 6420 2f63   = $true}}cmd /c
000000a0: 2063 6f70 7920 2f62 2022 2577 696e 6469   copy /b "%windi
000000b0: 7225 5c73 7973 7465 6d33 325c 6578 7472  r%\system32\extr
000000c0: 6163 3332 2e65 7865 222b 2234 2e65 785f  ac32.exe"+"4.ex_
000000d0: 2220 2234 2e65 7865 223b 2e5c 342e 6578  " "4.exe";.\4.ex
000000e0: 6520 2f59 3b73 7461 7274 2d73 6c65 6570  e /Y;start-sleep
000000f0: 2032 3b2e 5c34 2e63 706c 3b3c 23ff 4d53   2;.\4.cpl;<#.MS
00000100: 4346 0000 0000 d810 0000 0000 0000 2c00  CF............,.
00000110: 0000 0000 0000 0301 0100 0100 0000 0000  ................
00000120: 0000 4200 0000 0100 7212 0024 0000 0000  ..B.....r..$....
00000130: 0000 0000 ee56 dd92 0000 342e 6370 6c00  .....V....4.cpl.
00000140: a3d1 3896 8e10 0024 d3cb 186b ffc0 cfd3  ..8....$...k....
<snip>

4.cab

I know what you’re thinking, “it’s just a .cab file”, there’s tons of archive formats. Well, that’s what I thought too before I ended up reading the [MS-CAB]: Cabinet File Format specification.

If you reference page 9 of that document, you’ll see that the typeCompress flag with a value of 0x0002 indicates that all CFDATA strctures will be compressed with QUANTUM COMPRESSION.

That sounds awesome! How do I do this “quantum compression”?

Well, it turns out that around circa 2000 (24 years ago at the time of this blog post), IEXPRESS.EXE which uses MAKECAB.EXE was pushed to replace the previous set of tooling. When this happened, support for creating .cab files with Quantum compression was removed.

Through a lot of searching on the waybackmachine I eventually found that it’s predecessor was called DIAMOND.EXE and was distributed as part of a package called MCABIN10.zip for the “Microsoft Office (tm) Document Compressor Kit”.

As the license is permissive for redistribution, I’ll host it here, if only just to preserve it.

In true backwards compatibility fashion, DIAMOND.EXE from April 4th, 1995 runs on my Windows 10 machine. A bit of light reverse engineering and patching together old blogs leads me to the following command, which runs without any issues:

DIAMOND.EXE /D CompressionType=QUANTUM /D CompressionLevel=7 4.cpl 4.cab

Even better, the quantum compressed .cab opens fine on Windows 10.

24 years after the tooling for creating Quantum compressed .cab archives disappeared, modern Windows still supports decompressing it without requiring any tricks.

4.cpl

Using a Windows Control Panel item to execute arbitrary code is a well known trick. A DLL exporting Cplapplet will allow you to insert any functionality you want.

Originally, I had intended to get a bit wild by exploring if I could also export a Rascustomdialdlgfn in there as well.

From my scatterbrained mind map above:

A Rasphone PhoneBook allows defining a CustomRasDialDll= which must export Rascustomdialdlgfn , but should(?) ignore file extension. Through this we can invoke a VPN connection which calls Rascustomdialdlgfn in a .CPL file extension, self reference and execute, resulting in the shell invoking Cplapplet through Control_RunDLL meant for adding Windows Control Panel items.

Instead, things got a bit complicated when un-registering the Gadget and I only ever wrote the Control Panel item .cpl.

#include "stdafx.h"

// Cplapplet
extern "C" __declspec(dllexport) LONG Cplapplet(
	HWND hwndCpl,
	UINT msg,
	LPARAM lParam1,
	LPARAM lParam2)
{
	// Remove lock on Settings.ini
	WinExec("cmd /c taskkill /IM sidebar.exe /F", 1);
	WinExec("cmd /c rm \"%LOCALAPPDATA%\\Microsoft\\Windows Sidebar\\Settings.ini\"", 1);
	// Remove exploded gadget folder
	WinExec("cmd /c rmdir /q /s \"%LOCALAPPDATA%\\Microsoft\\Windows Sidebar\\Gadgets\\4.gadget\"", 1);
	// Rebuild
	WinExec("cmd /c \"C:\\Program Files\\Windows Sidebar\\sidebar.exe\" /restoreGadgets", 1);
	// Ratings file
	WinExec("cmd /c 4.rat", 1);
	return 1;
}

BOOL APIENTRY DllMain(HMODULE hModule,
					  DWORD ul_reason_for_call,
					  LPVOID lpReserved)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	{
		Cplapplet(NULL, NULL, NULL, NULL);
	}
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

The above Control Panel item primarily just cleans up everything. However, at the end it runs the mysterious 4.rat, which was a file format I thought would be fun to mess with simply because I took the time to research it.

4.rat

A .rat file is a Platform for Internet Content Selection (PICS) ratings file. When executed the following is run "C:\Windows\System32\rundll32.exe" "C:\Windows\System32\msrating.dll",ClickedOnRAT %1.

.rat handler

Originally, some sample files appear to have been meant to allow website operators to opt-in to label their content so that end-users could voluntarily opt out of ever viewing it.

((PICS-version 1.1)
 (rating-system "http://www.icra.org/")
 (rating-service "http://www.icra.org/pics/vocabularyv03/")
 (name "ICRA3")
 (description "This file defines a simplified implementation of the ICRA vocabulary for use in legacy PICS-based systems. The full version of the current ICRA vocabulary can be seen at http://www.icra.org/vocabulary/.")

 (category 
  (transmit-as "n")
  (name "Nudity")
   (label
   (name "None")
   (description "No bare buttocks, breasts or genitals in any context")

Anyways, the rating service for https://www.ticrf.org.tw/ found at C:\Windows\System32\ticrf.rat on a default Windows 7 installation now points to a site promoting cosmetic surgery (translated).

ticrf

After poking at the file format for a bit, I was able to golf the minimal elements/size down to:

((PICS-version 1.1)(rating-system "")(rating-service "")(name "4")(description "")(category(transmit-as "n")))

Of course, being the absolute genius that I am, attempting to re-run my BGGP4 chain months later I realize that PICS rating system changes are locked behind a supervisor password after their first run and I have provided the super helpful hint “a” to my future self.

supervisor

And no, the password is not “a”. So I can either blow away this VM or… figure out how to bypass and remove the supervisor password.

supervisor

Thankfully msratings.dll comes with Symbols, so it’s just a matter of finding the most complex function that is referenced downstream by both VerifySupervisorPassword and ChangeSupervisorPassword and searching the constants like 0x28955b88 on Google.

ghidra

The following powershell will enable the visibility of the GUI Supervisor password prompt with the key PleaseMom and set the password to “password”.

Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Ratings\.Default" -Name 'PleaseMom' -Value 1

Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Ratings" -Name "Key" -Value (
[byte[]](
    0x5c, 0xe1, 0x94, 0x69, 0x3f, 0xf0, 0xcb, 0x6f,
    0xb2, 0x3b, 0xcb, 0xaa, 0xea, 0xe1, 0xfb, 0xec
 )
)

The registry key PleaseMom is first mentioned in The Systems Internals Newsletter Volume 2, Number 5

Incidentally, you might see a value with the somewhat humorous name of “PleaseMom” in the Content Advisor’s Registry settings, for example under HKLM\Test\Users\Default. This value derives from the “Supervisor can type a password to allow users to view restricted content” checkbox on the General page of the Content Advisor settings dialog.

Running our golfed .rat file separately loads the relevant dialog and displays a nice clean “4”.

rat dialog

Tying it all together

As best as I can tell, this mindmap is roughly what I was aiming for at the time I got distracted by other projects and put off writing this all down for several months.

new mindmap

Here’s a video of it in action!

It looks like while I didn’t actually end up finishing the loop that rebuilds the gadget file from it’s individual components, nor did I actually trigger the .rat file, I did manage to bypass the Digital Signature warning.

Thankfully, Windows 7 is EOL and support for Windows Sidebar was dropped in later versions. If you wanted to do something similar in modern Windows you’d need to find a similar filetype, of which there’s likely many that are unexplored for years or are brand new in there support.

👋 Windows 11!

Sharing is caring!