Turning Unzip into a File Dropper

 

How did this compromise the machine?

If you find yourself/coworkers fearlessly unzipping and untaring 3rd party archives on your linux system, you may want to give this a read. Im going to show a POC on how an infection can occur solely through archive extractions.

The main functionality I targeted is the directory structure creation upon “unzip” or “tar -xvf” command ..If we can get files to be written to other places instead, then we can own the machine via autostart locations.

First attempt was to modify zip file and fool it into dropping files/directories with special path characters (“../” or “/” “). This may get me outside the “current working directory jail”.

  

 

Unzip is wise to this, and carefully parses out these characters before unpacking to avoid files escaping the current working directory. I found unzip has two different sanitizer routines. One pulls out directory traversal patterns, and the other pulls out “out-of-range characters (0x00-0x1f)”. Seeing this, the next step was to attempt paths like “.\x04./” hoping it would pass the traversal sanitizer and then be normalized by the out-of-range sanitizer; finally becoming “../”. Nope, still caught it.

After messing more with this, env vars, etc – I realized instead of going backwards to get out of the directory jail, you should go forward. This means making symlinks.

If an attacker can compress a directory with symlinks, these symlinks can be dropped which point to sensitive paths, and zip artifacts can be configured to write to them, thus traversing outside the current working directory.

Using ZIP with symlinks works simply enough (using the –symlinks flag), but it rats you out when it comes time to unzip, here shows a message from “unzip” claiming it just made a symlink to /etc/init.d/

can we suppress this? I attempted some terminal tricks to try that. If we can get unzip to echo special characters, we can control the terminal via Terminal Control Sequences (ie echo -e “\x1b[0;31m”). Well, we got that pesky sanitizer that unzip uses (filters out chars < 0x1F). Can we bypass the sanitizer?

Above you can see a format string being assembled, (which happens to be the symlink creation message “notScary -> /etc/init.d/”). All the sprint_f messages I could find were preceeded with this sanitizer routine. (alternatively, one could try making a filename field longer than 0x4000 in the zip format, which the sanitizer will fall short on (since 0x4000 = MAX_LENGTH), although, there are careful checks before this that wont allow this long of zip filename fields.

The only sprintf call I found that does not check for sanitization is the filename passed to zip itself, and apparently, you can name files with raw hex in them on Linux. So we make a zip file with an embedded terminal code for blanking out the console ‘\x1bPn=6’

zip -r Pictures[www.freepics.com$(echo -e “\x1bPn=6”)].zip /ResourceFolder).

Which appears as     on the filesystem. Regardless of the social engineering namestyle, running this through unzip, a terminal code will be executed (the ? mark is actually a \x1b character), since the sanitizer isnt ran on the filename itself. This blanks out the console, preventing any information being echoed about the unzip process. This zip can now drop files that wont be shown in the output…kinda cool

 

Animated GIF - Find & Share on GIPHY

Lets shelve this one for a sec. I found tar is the best way to deliver symlinks since they are preserved and there is no message of symlinks being made. The below file structure would exploit this, and cause infection whether unzipped as root or not (.desktop files in the .gnome dir for example). These are all autostart locations, that can get code to be ran upon next login (or even sooner with a cron job – if root).

The events become:

  1. untar Downloaded.tar
  2. unzip Pictures.zip (or Pictures[www.freepics.com?Pn=6].zip)
  3. infected

At unzip, the untared softlinks are hit and malware is written to /etc/init.d or the user directories paths to hit the user auto start. Heres where the console disable comes into play. Unless the targeted user’s name is known, we cant get persistence if not unziped as root, since we dont know the home/user directory name. For this, we need to attempt relative traversals (../) until we get to the $USER dir and hit files like .ssh/.gnome or other interesting files. Some of these may look strange, especially .ssh file being written. With the console hack, output can be supressed

Whats more concerning than the social engineering aspects of this, is the general concept that one can escape CWD jails with unzip or untar. This could be an issue for many platforms which allow submitted zips – trusting that contents will be jailed. For example, Android, which unzips .APKs under a priviledged process (PackageManager)…but dont bother trying this one, already did – doesnt work 😉

Hiding Your Process from Sysinternals

 

Malicious.exe running, but does not show up in Procexp.exe

I was researching ways to not just do anti-analysis, but to rather run executables in spite of analysis, while evading analysis. I found ways to go invisible from a few common analysis tools, but the final boss was Sysinternals Suite, no Administrative nor SEDebugPriveledge allowed for this challenge. I figure I’d share what I found here, which also ended up being a privilege escalation.

ProcExp’s “HiddenProc” Easter Egg

I first started with Procexp. Sifting through Procexp in IDA I thought I instantly found a promising section of code and was going to call it a day.

This routine searches for a MULTI_SZ registry value named “HiddenProcs”. If it exists, it will parse a list of new-line delimited process names and what I assume, later filter them out of the viewing. Unfortunately, the actual routine in charge of hiding these process names doesn’t exist (probably compiled out for release mode?). This is a dead registry key. Moving on.

ProcExp Image Hijack 1 (failed)

If we can hijack procexp, then we can control it to display whatever we want to the user. When you run Procexp32.exe (or other 32-bit Sysinternal tools) on a 64-bit OS, it will often dump a 64-bit version of itself to disk, and then run the 64 version instead. Is there a way we can image hijack this, post drop?

What we are looking at is the drop and execute code from Procmon32. This routine is responsible for writing a 64-bit version of itself and executing if on 64-bit OS. From the top green node (Drop64bitProcExp function) to the bottom green node (CreateProcessW). There is a gap. If we can ensure ProcExp32.exe spends as much time in the red while we repetitively try to write to the dropped exe, can we intercept the image before CreateProcess is called?

Below I try this, with a simple POC to set my thread to TIME_PRIORITY_TIME_CRITICAL, while attempting to write my own code to the dropped exe. The goal is to get my quantum to get executed in between the 2 green nodes.

When running this program, and a user attempts to open ProcExp, we get the error below. Looks like it may have only wrote partial way for the Image Hijack. This is not deterministic enough, and too SCIFI.

 

ProcExp Image Hijack 2 (success)

Investigating the file drop code, we see ProcExp doesn’t instantly exit if wfopen_s(“ProcExp64.exe”, “wb”) fails.

This will be its flaw. It ignores the fopen error as long as “GetFileAttributes” succeeds.

..so the image hijack is very simple. We just write out own “ProcExp64.exe” to the temp directory, and make it “READ ONLY”. That way, fopen(“ProcExp64.exe” ,”wb”) fails, but when trying GetFileAttributes it will succeed, and execution flow will lead us right to CreateProcess.

 

Here my process drops a fake version of ProcessExplorer under the %temp% dir as “PROCEXP64.exe”. Its marked read-only (you can try this at home actually). This is a simple program that just says “Hijacked” to the console.

Now when we try to run Procexp.exe, it hits this flaw and instead runs our fake “PROCEXP64.exe”.

 

This shows a hijack is possible, but I think we can do better, since this is limited to 64-bit OS running 32-bit Sysinternals.

 

The DLL Hijack (final solution)

This is final method I choose for the POC. Looking at Sysinternal RegKeys, we see one called “DbghelpPath”. This Registry key is writable for most applications since its the USER registry hive.

DbghelpPath key just points it to a path that it trusts to hold the “dbghelp.dll”. Well, I’ll just hijack this path then, thanks. My binary sets this key to point to the %TEMP% dir while dropping its own dbghelp.dll in temp (%TEMP%/DbgHelp.dll). When Procexp is runs, it will load the dll in this path. Once i get it to load my dll we hook ProcExp routines to hide my process. You can see the main code for this in

https://github.com/RISCYBusiness/Jadoube/blob/master/AntiSysInternals/AntiSysInternals/Procexp.cpp

This involved reversing ProcExp logic. After doing so, I found the best point to target the hook – OpenProcess API.

We patch OpenProcess Api, since, it is called for each process it shows to the output. Its better if I target a hook here, rather than hardcoded offset. If I do the later, it will be more volatile to different versions of ProcExp (since offsets can change, code added/removed). So instead I patch OpenProcess API, because when ProcExp calls it, theres a dangling r14 register that happens to point to the ProcExp PROCESS linked list. This PROCESS linked list structure is less likely to change between versions.

Here we patch:

Here we hook, to hide our process from ProcExp linked list. This involved reversing the PROCEXP process linked list structure a bit, but once done, we have a filter for filtering processes. Below this POC filters processes named “Malicious.exe”.

End Result

One cool thing about this, is that nearly all SysInternal tools have this DbgHelp path writable key, so you can theoretically do this with all Sysinternal Suite tools.