Pittenum – Remote Linux enumeration script using PXSSH

Remote Linux Enumeration Script Using PXSSH



Once again, I’ve decided to do another blog on something that has come across my path and that I find rather interesting… I hope whoever reads this can gain something from it, if you have any questions please leave a comment below. I mainly write these blogs for personal development as I feel it helps solidify what I am trying to learn as I journey through this massive industry and if someone enjoys what I have to say along the way that is a huge bonus to me, so let’s get started!

I’ve decided to do a write up on creating a remote Linux enumeration script using PXSSH. I’ve decided to do this because I was recently put in an environment where we did not have host-based agents running on the remote hosts that we needed to protect. So, we needed a way to be able to enumerate the remote hosts and store that information to then compare at later dates. Essentially we needed a baseline script.

The following is me explaining how I approached creating a baseline script and some of my thoughts and considerations before getting into the skeleton of the program itself.

My Setup

My environment is the new Windows Linux Subsystem, personally, I think it is great! I have been using X-Server and sublime IDE to edit and view my scripts on the Linux Subsystem.

The module you will need for this script to run is PEXPECT, this comes natively on Kali but if you have another distro you may be required to install it. If you are missing the module you can use Pythons standard package-management installation tool “PIP”


Ultimately, it would be best practice to set up some clean Linux machines to practice making baselines of before making some changes and seeing how well you can spot the differences.

I’m writing this script in python 2.7 however I see no issues in this being compatible with 3.7 as long as the correct formats are followed.


What do we want in our remote enumeration script, what is important? If we create a baseline of a machine on our network what information should we be gathering in that baseline that we know will change if an attacker was to infiltrate the remote host.

I’m going to be using the Linux Triage chapter in the Blue Team Field Manual as a good guide to what information we should be enumerating.

Some of the other considerations with this information are how we will store it and prevent collisions with information. What I mean by that is if I start scanning a remote host, what happens if I have done this host before – I need to effectively manage how my information is stored to prevent it from overwriting information that I want to keep or it leaving me with duplicate information appended to the original file.

The other important piece is how we will interact with the host. This script utilises SSH and just executes commands remotely using PXSSH from the PXEXPECT module, the script is simple in how it each function just sends the commands one by one to the chosen remote host and stores the output into a variable.


– Remote Enumeration using SSH
o The script will perform all the command execution remotely using SSH

– Data storage and Data Deduplication
o Store returned output into a variable
o Create a file on the localhost
o Separate data via a naming convention of logged data (data, time, function type and hostname)

– Secure storage of credentials
o Using a pre-existing module such as getpass() to securely store the password in the script without echoing it

– Custom command execution & log
o Command via user input
o Optional logging of commands and output

What to enumerate

The script has each of these information categories separated into functions as well as how the information is logged. Each function logs the information enumerated from that category into a separate log file.

– System Information
o Hostname
o Operating System
o Kernel-Version
o Uptime
o Time and Date Settings

– User information
o View logged in users
o Show if a user has ever logged in remotely
o View failed logins
o View local user accounts
o View local groups
o View sudo access
o View accounts with UID 0
o View root authorized SSH key authentications
o List of files opened by the user
o View the root user bash history

– Network Information
o View the network interfaces,
o View the network connections,
o View the listening ports,
o View the routes,
o View the ARP table
o List the processes listening on ports.

– Service Information
o View Processes
o List of load modules
o List of open files
o List of open files, using the network
o List services

– Policy, Patch and Settings Information
o View pam.d files
o Configuration files in /etc

– Autorun and Autoload Information
o List cron jobs
o List cron jobs by root and other UID 0 accounts
o Review for unusual cron jobs

– Logs
o View root user command history
o View last logins

– Files, Drives and Shares Information
o View disk space
o View directory listing for /etc/init.d
o Get more info for a file
o Identify file types
o Look for immutable files
o View directory listing for /root
o Look for files recently modified
o Look for world-writable files
o Look for recently created files

Program Skeleton


Initially executing the script, it will run the main() function which will ask you to store the remote hostname, username & password into a variable and then will pass these variables throughout the functions to use them to authenticate via SSH to execute the relative commands of the function you have selected to enumerate. The password is stored securely using the getpass() module and does not display upon entry.


Example output:

Logo() & Options()

The logo() & Option function just holds a print() list of the name, logo, and options to trigger the various different conditional statements within the menu() function. It displays two variables within the option() function being the hostname and the username to display to the operator so they are aware of what host they are executing against. These are both run within the menu() function in the While loop to consistently display the logo & options throughout the scripts use.



I’ve personally gotten into the habit of developing terminal menus within a lot of my scripts. I generally have done this because I find having a menu with all my scripts functions laid out easy to understand and follow rather than just passing parameters and switches to a script. Obviously, this complicates things a little more but for the purpose of this script, this will be relatively simple.

The switch menu utilizes a while True loop, this helps in the returning to the menu function after the execution of one of the listed functions has been completed. This is because pythons while True loop will loop forever until a break condition. If the IF condition is met for the selected function the hostname, user and password data will be passed to the selected function.



How data is logged

As each command is executed on the remote host, the output of the command is then appended to a variable called (output). After the final command has been executed and appended to the variable you will be prompted asking if you wish to append the data to a file, If you accept the data collected by the function stored in the variable will be appended to a log file using the category and the hostname as a naming convention to separate the data collected into individual log files. To prevent data de-duplication, I’ve imported the time module and store the current date/time into a variable that I append to the start of the log file name.

Global variable declared to append the date-time to the start of every log file name

Example output:

How the command execution occurs

Each function generally opens into a try & except handling exception. Python executes code following the try statement as a “normal” part of the program. The code that follows the except statement is the program’s response to any exceptions in the preceding try clause.

The code following the try clause is the establishing of the SSH connection after the creation of the pxssh.pxssh() object.

Once the SSH connection has been established the commands are executed via the pxssh sendline() function. Each command has been categorized as best as possible. The description of the data being enumerated is appended to the output variable. A title is appended before the command is executed to help make the log file easier to read and understand.



Custom Command Execution

The custom command execution is a function I built just in case the operator wants to check something manually on the host, either a log file that is not listed or a configuration file. There is an if_elif statement written into the function when it is first executed giving the operator the choice of logging the commands and output of the commands in the session. If the operator selects to log the session it will enter a While loop where the condition is if start is not equal to stop, to continue to pass the user input down into the sessions s.sendline() command execution function. If logging is enabled the output is appended to the variable and written to a log file when the operator triggers the if statement with “x”. If the operator does not select to log a similar while loop is executed however no data is appended to a variable or to a log file at the end of the session.



Thanks for reading

This is by no means the most efficient way of writing a remote enumeration framework for Linux and I’m very keen to have feedback on ways I could improve this or do it differently. Thanks – Pitticus

3 thoughts on “Pittenum – Remote Linux enumeration script using PXSSH”

  1. Great script. Keep it going.
    As extra have you thought about execution of multiple menu items.
    Output to JSON for later parsing.

    1. Hey Adam!

      It’s certainly a great idea, definitely will be implementing that.

      I have another script I’m making on the side mirrored to this that utilizes parameters and arguments.

      My idea is this will output everything at once without requiring any user input and I’ll implement a switch for JSON output.

  2. The Windows Linux subsystem! Get linux on Windows so you’ll never need Linux again!

    Just kidding!

    Actually… I’m not sure how I feel about linux contaminating my nice Windows environment =P

    I have two small suggestions about your python script.

    Firstly, are you familiar with context managers? In your code, you open a file, write to it, then you close it. With a context manager, you can do something like this:

    with open(“myfile.txt”, ‘w’) as myfilehandle:

    Once execution leaves the ‘with’ block, the file will be closed. This happen no matter how you exit the ‘with’ block (e.g. even if the write function leaks an exception, the cleanup will still happen). This is great because it means you don’t have to worry about the cleanup yourself. The way this works ‘under the hood’ is that the ‘open’ function returns a ‘file’ object. All ‘file’ objects have some cleanup code that will trigger when it leaves a context manager, this cleanup code closes the file handle. There’s a good in depth explaination here: https://dbader.org/blog/python-context-managers-and-with-statement

    Secondly, I find it a lot easier to read other people’s code if they have globals (like constants or global variables) named differently. Personally, I prefix my global variables with “g_” (so in your case, it would be “g_date” instead of “date”). This way, when reading through the code, it’s super obvious whether a variable is a local variable or a global one.

Leave a Reply

Your email address will not be published. Required fields are marked *