BC-SECURITY / Empire

Empire is a post-exploitation and adversary emulation framework that is used to aid Red Teams and Penetration Testers.
https://bc-security.gitbook.io/empire-wiki/
BSD 3-Clause "New" or "Revised" License
4.1k stars 566 forks source link

Broken osx/application stager after the changes to launcherBase #86

Closed hypnoticpattern closed 4 years ago

hypnoticpattern commented 4 years ago

Empire Version

3.0.5

OS Information (Linux flavor, Python version)

Server: Ubuntu 18.04.3 LTS (Bionic Beaver) |=> Python 3.6.9 Agent: Run on OSX Catalina 10.15.2 |=> Python 2.7.16 (the one used by the stager) |=> Python 2.7.17 (shipped with MacOS) |=> Python 3.7.3 (I manually installed it)

Expected behavior and description of the error, including any actions taken immediately prior to the error. The more detail the better.

(Empire) > usestager osx/application
(Empire: stager/osx/application) > set Listener http
(Empire: stager/osx/application) > set SafeChecks False
(Empire: stager/osx/application) > generate

[*] Stager output written out to: /tmp/out.zip

After generating the stager for OSx and run the application bundle in MacOS the code doesn't do anything. To troubleshot the issue I run the binary within the app bundle and I get the following error:

$ ./launcher.app/Contents/MacOS/launcher
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "<string>", line 3, in <module>
ImportError: No module named request

I added a couple of lines within the launcherBase to verify what Python interpreter was used and I got this:

2.7.16 (default, Nov  9 2019, 05:55:08)
[GCC 4.2.1 Compatible Apple LLVM 11.0.0 (clang-1100.0.32.4) (-macos10.15-objc-s)

The reason of the error is because the launcher code was modified to be compatible with Python 2/3 (standard version) and the launcher moved from urllib2 (original Empire) to urllib (new version). The Python version used by the template in Empire/data/misc/apptemplateResources/ is probably the Apple flavor and that doesn't have the request module in urllib:

/usr/local/opt/python@2/bin/python2.7
>>> import urllib.request
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named request

Is there a way to have a different Python version running with the templates in Empire/data/misc/apptemplateResources/?

vinnybod commented 4 years ago

I can verify I have the same issue. The binary launcher file references /System/Library/Frameworks/Python.framework/Versions/2.7/Python. I am unsure how those files were created to begin with, but I think having one that targets 2.7 and another to target 3.x might be the solution, what do you think?

I floated a similar idea here https://github.com/BC-SECURITY/Empire/issues/81#issuecomment-578002801

hypnoticpattern commented 4 years ago

It would be great to have the templates source code, either creating new ones or retrieving the source for the the ones embedded in the project, since it can be useful to make sure nothing funky is happening when you run the binary.

To keep things simple I would stick with the current version of the app because Python 2.7 ships with MacOS and we can rely on the fact that it is on all MacOS systems. At least until Apple decides to migrate to Python 3.x. That being said for my current situation would be perfect to have both apps: one that references to Python 2.7 and another one to Python 3.7.

I moved one step forward using this tip importing standard libraries before line 443

launcherBase += "from future.standard_library import install_aliases;\n"
launcherBase += "install_aliases();\n"

Unfortunately that wouldn't make the code work with Python 3 out of the box (same as issue 81)

Digging into the problem I found another porting bug related to the stage1 sent to the MacOS:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 22, in <module>
  File "<string>", line 441, in <module>
  File "<string>", line 264, in aes_encrypt_then_hmac
TypeError: str() takes at most 1 argument (2 given)

That is caused by the bytes methods (1 - 2) in aes_encrypt_then_hmac which is just an alias for str() in Python 2.7 and doesn't accept two arguments.

Cx01N commented 4 years ago

@hypnoticpattern We have spent some time trying to build either dual launchers or a single launcher to do both 2.7/3.x and the fundamental differences between the two languages have made it a non-trivial task. The way bytes are handled in Python 2 makes communicating with Empire problematic since we are moving to be solely Python 3.X for the server.

I think the ideal situation would be for us to be able to use 2.7 and 3 agents, and we are happy to include a pull request if you would like to develop this, but we would like to focus on updating some of the Powershell capability before tackling this problem, so it is not currently on our radar.

Invoke-Mimikatz commented 4 years ago

@Cx01N From an operator standpoint, the only place Python2 compatibility needs to be provided is in the agents. Since Python2 is EOL, I see no reason Empire should support it on the server side. An operator can always update their Empire server to the latest Python release, but Python2 agent compatibility would still matter for Linux/Unix or embedded devices that might still be running legacy Python2 only. I know going forward I will be running the Empire server using Python3 only.

vinnybod commented 4 years ago

@Invoke-Mimikatz I think we all agree that we would like to have both, it's just not at the top of the priorities.

Here's what we thought about when making this decision

So looking at the future, the python3 agent was more important to have. We'd love to have the 2.7 agents for older Linux machines and MacOS, but its just not at the top of our priorities. This is not a hard stance against support, we're just depending on those who need it to provide it at this time, and we are open to discussing how it could be implemented with anybody who wants to.

hypnoticpattern commented 4 years ago

@Cx01N I can take a look and see if I can write a Python 2.7 agent that can talk to the Python 3 server. As you mentioned it won't be easy since the agent itself inherits pieces of code that are also used on the server side(e.g. crypto).

At the moment the MacOS stager is broken because the launcher uses the python 2.7 interpreter but is Python 3 code. Would be possible to update the application bundle and the launcher stagers to point to python3 (e.g. /usr/bin/python3)? I can work on that if you can point me to documentation on how the application bundle was developed.

Cx01N commented 4 years ago

@hypnoticpattern Unfortunately, we do not have a lot of documentation on how things were developed in the original Empire. The osx/application launcher is something that was preconfigured and is relatively foreign to us. @vinnybod tends to work on mac a lot more than the rest of us and he may be able to point you in the right direction.

hypnoticpattern commented 4 years ago

I had a deeper look at project files and noticed macho.m. That file should be the one used to create the Macho executable that is also included in the app bundle. Empire injects the Python code directly inside the __cstring section of the compiled file replacing the hardcoded base64 string PAYLOAD. macho.m uses <Python/Python.h> and that leverage the MacOS version of Python (i.e. 2.7.16). I wonder if there is a way to call python3 instead. For example the one shipped with XCode.

Cx01N commented 4 years ago

@hypnoticpattern Thanks! I've been checking it out and tried making some changes. It looks like it is leveraging the python files in /system/Library/Frameworks/Python.frameworks/Versions/2.7/. Since 3.7 isn't getting installed in this directory it is causing some issues. You can change the directory in the machotemplate to /usr/bin/python3 but you'll get that dylib isn't found. Hopefully, that helps.

hypnoticpattern commented 4 years ago

@Cx01N The make it work the machotemplate needs Python3.framework shipped with XCode and it's in /Application/XCode.app/Contents/Developer/Library/Framework. At this point I see two options: 1) Use the .app bundle approach and include the Python3.framework there changing the linker option; 2) Use the Macho binary alone and change the dylib path to /Application/XCode.app/Contents/Developer/Library/Framework/Python3.framework using install_name_tool and "hope" XCode is installed.

Cx01N commented 4 years ago

@hypnoticpattern I am not sure which direction is the best, so I will leave it up to you since I rarely use osx as it is.

Cx01N commented 4 years ago

@hypnoticpattern Closing the issue for now. Please feel free to open a PR or new issue if you get the launcher updated.