Pages

Friday, July 22, 2016

Python: Client-Server Socket Programming I

Either as a penetration tester or incident handler, who hasn't been in the situation where third party tools were not allowed in the network and transferring files, executing commands remotely, or even sniffing data became a huge challenge?

This post will be the start of a series of posts which will narrate how understanding Python language is really useful whether if you are part of a blue or red team. As any other topic, it is always good to start from the basics and then work up to more advanced scripts. I have read several other posts about this specific topic (Client-Server Socket programming), and I always had to go back and check other references in order to understand every single line. Here, I will be providing definitions for every method or class being used in the Python code and as many details as I can in order to make it easy for you, and also develop a strong understanding of the basics of socket programming. 

Socket Module

Described as a"Low-Level Networking Interface" by Python documentation, provides access to the BSD Socket Interface (API) which allows you to develop applications that could transfer data between processes on the same host or on separate ones over the network without writing several lines of code to deal with the many layers of networking protocols. The way how this is possible is by using system calls and Python's object-oriented style. The socket() function inside of the socket module returns a socket object whose methods implement various socket system calls.

***Remember: Function socket() creates a Socket Object. Socket Object have Methods 


Basic Server Socket Methods

Method
Description
s.bind()   Binds address (Hostname, Port Number) to socket object  
s.listen()  Sets up and starts a TCP Listener  
s.accept()   Accepts TCP connections. Blocking call since it waits for connection request 


Basic Client Socket Methods

Method
Description
s.connect()   Connects to TCP server. Initiates the connection with the server


Basic General Socket Methods

Method
Description
s.recv()  Receives TCP  message
s.send() Sends TCP  message  
s.recvfrom()  Receives UDP message
s.sendto()  Sends UDP  message
s.close()   Closes Socket


BSD Sockets generally relies upon client-server architecture. For TCP communications, one host(server) starts listening on a specific port for incoming connection request. When a request arrives, the server host accepts the connection request, which at this point data can be transferred between the hosts (Server-Client).


Simple TCP Socket Server

Quick Overview:
  • The server creates a socket object
  • Binds and IP and Port number to the socket object
  • Server socket starts listening on the IP and Port provided
  • Server socket starts accepting connection requests
    • Blocking Call: Until the time a client connects to the server socket, it will just wait and no return. 
  • Server accepts connection and creates a new socket object for the specific client's connection.
  • Server starts receiving data from the client through the new client socket object
  • Once the client is done sending requests, the server closes the client socket object and the main server socket used to listen and accept connections.


Code:

1
2
3
4
5
6
#!/usr/bin/env python

import socket

socketserver = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socketserver.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)


  • Line #3: Import socket module/library
  • Line #5: Create the server socket object by using the socket() function
    • Socket() Syntax
    • socket.socket(socket.<Address Family>, socket.<Socket Type>)
      • Address Family : 
        • AF_UNIX = Used for Unix Domain Sockets
        • AF_INET = Used for IPv4 Internet Based Apps.
        • AF_INET6 = Used for IPv6 Internet Based Aapps
      • Socket Type:
        • SOCK_STREAM = TCP (Default)
        • SOCK_DGRAM = UDP
  • Line #6: Set the option SO_REUSEADDR and value 1 to the server socket in order reuse a local socket in TIME_WAIT state, without waiting for its natural timeout to expire.
    • If you dont have this option in your code, you will get the error message "Address already in use" every time you run your script after the first attempt.
    • Setsockopt() manipulates the options associated with the socket
    • Setsockopt() Syntaxsocket.setsockopt(socket.<Level>, socket.<Option Name>, Value)
      • Level: Level at which option residesSOL_SOCKET = Options at the socket level
      • Option Name:SO_DEBUG = Enables recording of debugging informationSO_REUSEADDR = Enables local address/socket reuseSO_BROADCAST = Enables permission to transmit broadcast messagesSO_KEEPALIVE = Enables Keep connection alive
      • Value: Argument should be non-zero to enable a Boolean option




1
2
3
4
5
6
7
8
bind_ip = ""
bind_port = 8000

socketserver.bind((bind_ip, bind_port))

socketserver.listen(1)

(client, (ip, port)) = socketserver.accept()


  • Line #1-2 : Set two variables (IP & Port) with their respective values
    • "" = (Empty string) - INADDR_ANY 
    • 8000 = Port that our server will use to start listening
  • Line #4: Use method bind() in order bind the IP and port to the socket object
    • bind() Syntax:
      bind(
      Address) =  bind((IP, port))
      • I know you have noticed that the values (IP & Port) are inside of another parenthesis. This is because the method bind expects a Tuple.
      • Tuple: Sequence of immutable Python objects. Tuples are sequences, just like lists. The differences between tuples and lists are, the tuples cannot be changed unlike lists, and tuples use parentheses, whereas lists use square brackets.
  • Line #6: Start listening by using the listen() method
    • This method takes an argument which sets the number of concurrent clients that the socket can handle.
    • i.e Listen(5)
    • listen() Syntax:listen(<Integer>)
  • Line #8: After the server socket is bound to an address & port number and listening, it has to start accepting client connections/requests.
    • Use the Accept() method to accept connections
    • This method returns values in form of a Tuple
    • The return value is a pair (client, (ip, port)) 
      •  (Client Socket) which will be used by the server to send and receive data to that specific connection. 
      •  (Client Socket) which will be used by the server to send and receive data to that specific connection.
      • Client is a new Socket object (Client Socket) which will be used by the server to send and receive data to that specific connection.
      • IP is the client's address. Where the connection is coming from
      • Port used on the client's computer to establish this connection
    • Once again, by default, the Accept() method is a blocking call which means that until the time a client connects to the socket server, it will just wait and not return.




1
2
3
4
5
6
7
8
9
while 1:
  data = client.recv(2096)
  if not data:
    break
  print "client sent: ", data
  client.send(data)

client.close()
socketserver.close()


  • In order to receive and send data we will have to create a while loop
  • Line #1: create a standard loop (infinite loop unless the connection breaks)
  • Line #2: use the client's socket object and use the method recv() to receive data from the client connected to the server socket.
    • The maximum amount of data that can be transferred at once per cycle in the loop is determined by the Buffer size which is defined inside of the recv() method.
  • Line #3-4: simple condition which breaks the loop if the connection drops and not data is transferred by the client.
  • Line #5-6: It displays the value of Data which is the string sent by the client connected.
    • We can now use the client's socket object and use the method send() to send the same Data back.
    • This basically turns into an ECHO Server
  • Line #8: Once the loop stops, the Server automatically closes the Client Socket object.
  • Line #9: Finally, the server also closes its own Server socket



Simple TCP Client Socket

Quick Overview:


  • Clients create a socket object
  • Client connects to Server
  • Once connection is established, the client can start sending data (strings) to the server.
  • After sending strings over, the client also receives the server's response

Code:



1
2
3
4
5
6
#!/usr/bin/env python
import socket
import sys

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((sys.argv[1], 8000))


  • Line #3: Besides the Socket module, we will need to import the sys module as well.
    • this module will allow us to provide a parameter to our python script before running it 
    • The script will capture the parameter and append it to sys.argv[1] it will make sense in a few.
  • Line #5: Create the client socket the same way how we created the Server socket
    • As you can see, I use the same options for the socket objects
  • Line #6: The client object now can use the connect() method and define the specific IP of the server and the port where it is listening on.
    • The connect() method defines its values in a form of a Tuple.
    • Sys.argv[1] : Points to the first parameter (In this case an IP) provided while running the script after the script's name
    • Example: python client_socket.py 192.168.2.222
      • Port:  Port where the server that you are trying to connect to is listening on



1
2
3
4
5
6
while 1:
  userinput = raw_input()
  client.send(userinput)
  print client.recv(2096)

client.close()


  • In order to send and receive data, we also have to create a a while loop
  • Line #1: Start the standard while loop
  • Line #2: using the Raw_input() function the program can capture input from my keyboard (STDIN). I append my input to variable Userinput 
  • Line #3: Use the client object and the method send() in order to send data (strings) to the server.
    • In this case, we are sending the contents of variable Userinput
  • Line #4: After sending input to the server, the client  can also receive the server responses by using the client socket  and the method recv().
    • The same way how we set a buffer to our connection in the server, we have to do it with the client. Buffer size 2096
    • Responses are printed on the clients screen.
  • Line #6: once the loop terminates, the client closes its client socket



Testing the Server and client



  • Test was done locally ( localhost )
  • Started running Socketserver.py first
  • Then, ran basic_client.py with a parameter 127.0.0.1 (IP of server)
  • Server identified and accepted the connection request from the client
  • Client now can start typing anything and sending it to the server
  • The server Echo everything that gets sent to it
  • Finally, the client interrupts the connection and the server shuts down itself successfully

Whats Next?
  • Server executes strings/commands being sent by the client
  • Multi-threading to handle several sessions at once


I hope you enjoyed this first simple python socket programming tutorial. This was really easy to code, but the value here was to understand how socket programming paired with python's object-oriented style works from scratch. Next time I will take this basic script to the next level so that you can see how easy and powerful python is.

Feedback is always appreciated ! Thank you !


References:



No comments:

Post a Comment