Skip to main content

Tales From The Red Team: Building Winexe

During one of our Red Team Engagements we were able to gain a foothold on our client’s perimeter via default credentials for an uncommon application server. Once we had this foothold we needed to expand access: privilege escalate and move laterally.

Unfortunately for us, their RHEL 6.8 server was properly configured and patched, so opportunities to escalate to root were fairly minimal. With the webshell we had uploaded we could perform internal reconnaissance and determined that we could definitely reach systems outside the client’s private cloud, so moving laterally to their core network seemed possible. With our external reconnaissance we had identified user accounts, and likely candidates for passwords for their Active Directory deployment. Without root access, installing additional toolsets such as Impacket for Linux -> Windows command execution seemed out of reach. In fact, we tried to deploy Impacket and its dependencies, but it was a painful process and we gave up once we realized it was likely going to be unsuccessful.

But we were familiar with an older tool: winexe. This tool is provided for you on Kali Linux, but unfortunately you can’t simply copy it to your target system and run it. It’s essentially the Linux version of psexec and works great. Also, if you build your own copy of winexe the likelihood of antivirus software catching it is rather low.

Like many binary tools in Linux it’s linked against shared libraries (like DLLs, but for Linux), and Linux binaries very commonly require exact share library version matches to run on a different system than it was built. Since RHEL 6.8 is a pretty old distribution it’s pretty unlikely that it has the correct shared library versions to run winexe from Kali. We could’ve tried to copy all of the shared library dependencies from Kali to our target, but without trying we assumed we might ultimately run into issues with the binaries (winexe and shared libraries) being built against newer Kernel headers.

So, we took it upon ourselves to build a static copy of winexe to upload to the compromised host. The process was a bit painful, since this is a fairly old tool and there’s no nice, clean GitHub page explaining how to build it. In fact, the copy of winexe source that we started with came from Sourceforge.

The first attempt at a static build completed after quite a bit of research and futzing, but once we transferred it to the target host we received an error that the version of GLIBC (GNU C Library) on the target system was incompatible. Similar to the Kali binary, we could’ve attempted to move the necessary GLIBC shared libraries to the target system, but decided the better solution would be to simply build winexe against the correct GLIBC version. Enter our buddy Docker!

Build Assistance

After a lot of research we found the Reevert Winexe source and patches ( worked the best for our build process, however the instructions were all for Debian. Our target version of GLIBC was 2.12, which wasn’t readily satisfied by any older supported Linux distributions other than CentOS 6.8. Since many enterprises we assess make use of RHEL, it made the most sense for us to come up with build instructions for CentOS.

Prepping the Environment

The first thing we do is to prep our build environment. We’ll start by pulling the CentOS 6.8 Docker image.

Download CentOS 6.8 Docker Image:

docker pull centos:6.8

Once we have the Docker image, we’ll download the necessary source code referenced in the Reevert Winexe build process to our Docker host system.

Acquire Source Code:

1)      Clone the Reevert Winexe repo:

git clone

2)      Download Samba 4.3.13 source tarball


Since we’ll want to copy our statically linked winexe binary somewhere other than our Docker container we map the host’s /home/ubuntu/tmp directory to the Docker container’s /tmp directory. The resulting winexe binary can then be copied from the Docker hosts’s filesystem anywhere we want.

Creating a CentOS 6.8 Container:

docker run -t -i --name centos -v ~/tmp:/tmp centos:6.8

Installing Libraries and Dependencies

The first thing we chose to do was install an external repository containing the MinGW Windows cross-compiler. There are two Windows service binaries (32-bit and 64-bit) that are built as part of building winexe. In the statically-linked version of winexe they’re incorporated into the binary.

Installing EPEL Repo:

yum install

During our initial builds in CentOS 6.8 we found ourselves repeatedly returning to installing missing dependencies. Although we’d like to keep our package deployment as minimal as possible we gave into time pressure and installed two large package groups to make our lives easier.

Installing Dev Tools and Libraries

Install “Development Tools” and “Development Libraries” package groups

yum groupinstall "Development Tools" "Development Libraries"

Install Additional Dev Libraries:

yum install glibc-static \
libacl-devel \
libcom_err-devel \
mlocate \
openldap-devel \
popt-devel \
python-devel \
zlib-devel \

Install the MinGW Dependencies

yum install mingw-binutils-generic \
mingw-filesystem-base \
mingw32-binutils \
mingw32-cpp \
mingw32-crt \
mingw32-filesystem \
mingw32-gcc \
mingw32-headers \
mingw64-binutils \
mingw64-cpp \
mingw64-crt \
mingw64-filesystem \
mingw64-gcc \

Building Winexe

The next few steps of this process require applying patches to the samba sources, and making a manual edit to one of the build scripts. For these next few steps we follow the Reevert Winexe build steps, but choose to build against SMBv2 because we’re less confident about finding SMBv1 in our target environment.

Patching Source Code:

cd reevert-winexe-waf
tar -xf ../samba-4.3.13.tar.gz && mv samba-4.3.13 samba
rm -r source/smb_static
cat patches/fix_smb_static.patch | patch -p1
cat patches/smb2_nognutls_noaddc.patch | patch -p1
cat patches/smb2_add_public_includes.patch | patch -p1
cd source && ln -s ../samba/bin/default/smb_static

Manually Editing wscript_build

We found that there’s an issue with the build process not detecting a Samba include directory. After all of the package installation issues we had encountered earlier, this was a pretty tedious issue to resolve. We’re not sure if this was caused by our build environment, or if it’s an intentionally missing step in the Reevert Winexe build steps. Regardless, the fix is pretty easy: edit the /tmp/reevert-winexe-waf/wscript_build file.


includes=bld.env.SAMBA_DIR + '/bin/default/include/public',


includes=bld.env.SAMBA_DIR + '/bin/default/include/public ' + bld.env.SAMBA_DIR + '/lib/tevent',

Now it’s time to rock and roll. We run the following build command, and grab a coffee (it might take up to 5 minutes depending on your hardware):

./waf --samba-dir=../samba configure build

If everything went well, your terminal should look like the following:


And you should have winexe-static in the build subdirectory:


You probably see the winexesvc32.exe and winexesvc64.exe binaries above. Those are the service binaries, which are deployed on remote systems to perform your command execution. The winexe-static binary contains a copy of them internally, which in combination with statically linking other source code is why it’s so much larger than Kali’s winexe binary:


Testing Winexe Against a Simulated Target

Since we’re strong proponents of testing everything in a development environment before our client, we spun up and prepped a Windows 7 VM for this next step. Since our Win7 VM isn’t connected to a domain, and our victim account isn’t a RID-500 account we needed to make a quick adjustment. Provided that you’re still running UAC on your non-domain test systems, due to the “pass the hash” patch from several years ago we needed to add a registry key LocalAccountTokenFilterPolicy ( to allow us to make use of a non-RID-500 account for remote administration:


Good deal, now we can move back to our Docker container and test against this system. A brief look at the help tells us how to make use of winexe:


In this case, the user’s name is “victim” and his password is the incredibly strong “Password123!”. We’ll do something really simple like run tasklist on the remote Windows host:

./winexe-static -U victim%Password123! // tasklist


As you can see, it’s really not too hard to get a static build of an older tool like winexe, you just need to use some elbow grease. When we finally had this version built we deployed it against our target, and it worked like a charm! Unfortunately for us, the systems we were targeting had been incorporated into the client’s egress filtering initiative. We spent additional time to construct customized Meterpreter Reverse HTTPS payloads that were getting automatically deleted by their Antivirus, which we were NOT expecting would happen. Due to the severity of the entry point we unfortunately lost our foothold when the client chose to correctly revoke our access. Despite the win and subsequent loss of access we still learned a lot, and now have the ability to build a lateral movement tool for Enterprise Linux distributions that can be used by a low-privileged Linux.