# 第六章 WEB偵查
本章內容:
1. 使用`Mechanize`匿名瀏覽互聯網
2. Python使用`Beautiful Soup`映射WEB元素
3. 使用Python與Google交互
4. 使用Python和Twitter交互
5. 自動釣魚
> 在我生命的八十七年中,我親眼目睹了技術革命的演替。但卻沒有人完成了人的思考和需要這一問題。
> —Bernard M. Baruch 美國第28到第32任總統的顧問
## 簡介:今天的社會工程學
2010年,兩個大規模的網絡攻擊改變了我們對網絡戰的理解。先前我們在第四章討論過極光行動。在極光行動攻擊中,瞄準了多個跨國的公司,雅虎,賽門鐵克,Adobe等還有一些Google賬戶。華盛頓郵報報道這是一個新的有著先進水平的攻擊。Stuxnet,第二次攻擊,針對SCADA系統,特別是那些在伊朗的。網絡維護者應該關注該蠕蟲的發展,這是一個比極光行動更加先進和成熟的蠕蟲攻擊。盡管這兩個網絡攻擊非常復雜,但他們有一個共同的關鍵點:他們的傳播,至少部分是通過社會工程學傳播的。不管多么復雜的和致命的網絡攻擊增加有效的社會工程學會增加攻擊的有效性。在下面的章節中,我們將研究如何使用使用Python來實現自動化的社會工程學攻擊。
在進行任何操作之前,攻擊者應該有目標的詳細信息,信息越多攻擊的成功的機會越大。概念延伸到信息戰爭的世界。在這個鄰域和當今時代,大部分所需的信息可以在互聯網上找到,由于互聯網龐大的規模,遺漏重要信息的可能性很高。為了防止信息丟失,計算機程序可以自動完成整個過程。Python是一個很好的執行自動化任務的工具,大量的第三方庫允許我們輕松的和互聯網,網站進行交互。
## 攻擊之前的偵查
在本章中,我們通過程序對目標進行偵查。在這個發面關鍵是確保我們收集更多的信息量,而不被警惕性極高,能干的公司總部的網絡管理員檢測到。最后我們將看看如何匯總數據允許我們發動高度復雜的個性化的社會工程學攻擊。確保在應用任何這些技術之前詢問了執法官員和法律的意見。我們在這展示攻擊和用過的工具是為了更好的理解他們的做法和知道如何在我們的生活中如何防范這種攻擊。
## 使用Mechanize庫瀏覽互聯網
典型的計算機用戶依賴WEB瀏覽器瀏覽網站和導航互聯網。每一個站點都是不同的,可以包含圖片,音樂和視頻中的各種各樣的組合。然而,瀏覽器實際上讀取一個文本類型的文檔,理解它,然后將他顯示給用戶,類似于一個Python程序的源文件和Python解釋器的互動。用戶可以使用瀏覽器訪問站點或者使用不同的方法瀏覽他們的源代碼。Linux下的我`wget`程序是個很受歡迎的方法。在Python中,瀏覽互聯網的唯一途徑是取回并下載一個網站的HTML源代碼。有許多不同的庫已經已經完成了處理WEB內容的任務。我們特別喜歡`Mechanize`,你在前幾章已經用過。`Mechanize`: http://wwwsearch.sourceforge.net/mechanize/ 。
`Mechanize`主要的類Browser,允許任何可以在瀏覽器是上進行的操作。這個類也有其他的有用的方法是程序變得更簡單。下面腳本演示了`Mechanize`最基本的使用:取回一個站點的源代碼。這需要創建一個瀏覽器對象,然后調用`open()`函數。
```
import mechanize
def viewPage(url):
browser = mechanize.Browser()
page = browser.open(url)
source_code = page.read()
print(source_code)
viewPage('http://www.syngress.com/')
```
運行這個腳本,我們看到它打印出 `www.syngress.com` 首頁的HTML代碼。
```
recon:~# python viewPage.py
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>
Syngress.com - Syngress is a premier publisher of content in
the Information Security field. We cover Digital Forensics, Hacking
and Penetration Testing, Certification, IT Security and Administration, and
more.
</title>
<meta name="description" content="" /><meta name="keywords"
content="" />
<..SNIPPED..>
```
我們將使用`mechanize.Browser`類來構建腳本,在本章中瀏覽互聯網。但是你不會受它的約束,Python提供了幾個不同的方法瀏覽。這章使用`Mechanize`由于他提供了特殊的功能。John J. Lee設計的`Mechanize`提供可狀態編程,簡單的HTML表格和方便的解析和處理,例如`HTTP-Equiv`這樣的命令和刷新。此外,它提供給你的內在對象是匿名的。這一切都會在下面的章節中用到。
## 匿名---增加代理,用戶代理和Cookies
現在我們有從互聯網獲取網頁內容的能力,退一步想想接下來的處理很有必要。我們的程序和在瀏覽器中打開一個網站沒有什么不同,因此,我們應該采取同樣的步驟在正常的瀏覽網頁時建立匿名。網站查找唯一標識符來識別網頁游客有幾種不同的方法。第一種方法是通過記錄請求的IP來確認用戶。這可以通過使用虛擬專用網絡(VPN)或者tor網絡來緩和。一旦一個客戶連接到VPN,然后,所有的將通過VPN自動處理。Python可以連接到代理服務器,給程序添加匿名功能。`Mechanize`的Browser類可以指定一個代理服務器屬性。簡單的設置瀏覽器代理是不夠巧妙的。有很多的免費的代理網絡,所以用戶可以進去選擇它們,通過它們的功能瀏覽。在這個例子中,我們選擇 http://www.hidemyass.com/ 的HTTP代理。在你讀到這里的時候這個代理很有可能已經不工作了。所以去這個網站得到使用不同HTTP代理的細節。此外,McCurdy維護了一個很好的代理列表在網站: http://rmccurdy.com/scripts/proxy/good.txt 。我們將測試我們的代理訪問NOAA網站,它會友好的告訴你訪問該網站時你的IP地址。
```
import mechanize
def testProxy(url, proxy):
browser = mechanize.Browser()
browser.set_proxies(proxy)
page = browser.open(url)
source_code = page.read()
print source_code
url = 'http://ip.nefsc.noaa.gov/'
hideMeProxy = {'http': '216.155.139.115:3128'}
testProxy(url, hideMeProxy)
```
雖然識別HTML源代碼有一點困難,我們看到該網站人為我們的IP地址是`216.155.139.115`,我們的代理,成功!我們繼續構建腳本。
```
recon:~# python proxyTest.py
<html><head><title>What's My IP Address?</title></head>
<..SNIPPED..>
<b>Your IP address is...</b></font><br><font size=+2 face=arial
color=red> 216.155.139.115</font><br><br><br><center> <font size=+2face=arial color=white> <b>Your hostname appears to be...</b></
font><br><font size=+2 face=arial color=red> 216.155.139.115.
choopa.net</font></font><font color=white
<..SNIPPED..>
```
我們現在有一個簡單的匿名瀏覽器。站點使用瀏覽器的`user-agent`字符串來識別唯一用戶另一種方法。在正常情況下,`user-agent`字符串讓網站知道關于瀏覽器的重要信息能制作HTML代碼給用戶更好的體驗。然而,這些信息柏包含內核版本,瀏覽器版本,和其他關于用戶的詳細信息。惡意網站利用這些信息針對特定的瀏覽器進行精密的滲透利用,而其他網站利用這些信息來區分電腦是位與NAT網絡還是私有網絡。最近,一個丑聞被爆出,一個旅游網站利用`user-agent`字符串來檢測MacBook用戶并提供更昂貴的選擇。
幸運的是,`Mechanize`改變`user-agent`字符串和改變代理一樣簡單。網站: http://www.useragentstring.com/pages/useragentstring.php 為我們展示了一個巨大的有效的`user-agent`字符串名單供我們選擇。我們將編寫一個腳本來測試改變我們的`user-agent`字符串訪問 http://whatismyuseragent.dotdoh.com/ 來打印出我們的`user-agent`字符。
```
import mechanize
def testUserAgent(url, userAgent):
browser = mechanize.Browser()
browser.addheaders = userAgent
page = browser.open(url)
source_code = page.read()
print(source_code)
url = 'http://whatismyuseragent.dotdoh.com/'
userAgent = [('User-agent','Mozilla/5.0 (X11; U; Linux 2.4.2-2 i586; en-US; m18) Gecko/20010131 Netscape6/6.01')]
testUserAgent(url, userAgent)
```
運行這個腳本,我們看到我們可以用虛假的`user-agent`字符串來訪問頁面。
```
recon:~# python userAgentTest.py
<html>
<head>
<title>Browser UserAgent Test</title>
<style type="text/css">
<..SNIPPED..>
<p><a href="http://www.dotdoh.com" target="_blank"><img src="logo.
gif" alt="Logo" width="646" height="111" border="0"></a></p>
<p><h4>Your browser's UserAgent string is: <span
class="style1"><em>Mozilla/5.0 (X11; U; Linux 2.4.2-2 i586; en-US;
m18) Gecko/20010131 Netscape6/6.01</em></span></h4>
</p>
<..SNIPPED..>
```
最后,網站會返回一些包含獨特標識的cookie給WEB瀏覽器允許網站識別重復的重復的訪客。為了防止這一點,我們將執行其他函數從我們的WEB瀏覽器中清除cookie。另外一個Python標準庫`cookielib`包含幾個處理不同類型cookie的容器。這里使用的cookie類型包含儲存各種不同的cookie到硬盤的功能。這個功能允許用戶查看cookies而不必在初始化后返回給網站。讓我們建立一個簡單的腳本使用`CookieJar`來測試。我們將打開 http://www.syngress.com 頁面作為我們的第一個例子。但現在我們打印瀏覽會話存儲的cookie。
```
import mechanize
import cookielib
def printCookies(url):
browser = mechanize.Browser()
cookie_jar = cookielib.LWPCookieJar()
browser.set_cookiejar(cookie_jar)
page = browser.open(url)
for cookie in cookie_jar:
print(cookie)
url = 'http://www.syngress.com/'
printCookies(url)
```
運行這個腳本,我們可以看到來自網站的session id的cookie。
```
recon:~# python printCookies.py
<Cookie _syngress_session=BAh7CToNY3VydmVudHkiCHVzZDoJbGFzdCIAOg9zZYNzaW9uX2lkIiU1ZWFmNmIxMTQ5ZTQxMzUxZmE2ZDI1MSBlYTA4ZDUxOSIKZmxhc2hJQzonQWN0aW8uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNoSGFzaHsABjoKQHVzZWR7AA%3D%3D--f80f741456f6c0dc82382bd8441b75a7a39f76c8 forwww.syngress.com/>
```
## 最終封裝我們的代碼為Python類
已經有了幾個功能,將瀏覽器作為參數,修改它,偶爾添加一個額外的參數。如果將這些添加到一個類里面將很有用,這些功能可以歸結為一個瀏覽器對象簡單的調用,而不是導入我們的函數到某個文件使用笨拙的語法調用。我們我們這么做可以擴展`Browser`類,我們的新`Browser`類將會有我們已經創建過的函數,以及初始化的附加功能。這將有利于提高代碼的可讀性,并封裝所有的功能在`Browser`類中直接處理。
```
import mechanize, cookielib, random, time
class anonBrowser(mechanize.Browser):
def __init__(self, proxies = [], user_agents = []):
mechanize.Browser.__init__(self)
self.set_handle_robots(False)
self.proxies = proxies
self.user_agents = user_agents + ['Mozilla/4.0 ', 'FireFox/6.01','ExactSearch', 'Nokia7110/1.0']
self.cookie_jar = cookielib.LWPCookieJar()
self.set_cookiejar(self.cookie_jar)
self.anonymize()
def clear_cookies(self):
self.cookie_jar = cookielib.LWPCookieJar()
self.set_cookiejar(self.cookie_jar)
def change_user_agent(self):
index = random.randrange(0, len(self.user_agents))
self.addheaders = [('User-agent', (self.user_agents[index]))]
def change_proxy(self):
if self.proxies:
index = random.randrange(0, len(self.proxies))
self.set_proxies({'http': self.proxies[index]})
def anonymize(self, sleep = False):
self.clear_cookies()
self.change_user_agent()
self.change_proxy()
if sleep:
time.sleep(60)
```
我們的新類有一個默認的`user-agents`列表,接受列表添加進去,以及用戶想使用的代理服務器列表。它還具有我們先前創建的三個功能,可以單獨也可以同時使用匿名函數。最后,`anonymize`提供等待60秒的選項,增加在服務器日志請求訪問之間的時間。同時也不改變提供的信息,該額外的步驟減小了被識別為相同的源地址的機會。增加時間和模糊的通過安全是一個道理,但是額外的措施是有幫助的,時間通常不是一個問題。另一個程序可以以相同的方式使用這個新類。文件`anonBrowser.py`包含新類,如果想在導入調用是看到它,我們必須將它保存在腳本的目錄。
讓我們編寫我們的腳本,導入我們的新類。我有一個教授曾將幫助他四歲的女兒在線投票競爭小貓冠軍。由于投票是在會話的基礎上的,每個游客的票需要是唯一的。我們來看看是否我們能欺騙這個網站給予我們每次訪問唯一的cookie。我們將匿名訪問該網站四次。
```
from anonBrowser import *
ab = anonBrowser(proxies=[],user_agents=[('User-agent','superSecretBroswer')])
for attempt in range(1, 5):
ab.anonymize()
print('[*] Fetching page')
response = ab.open('http://kittenwar.com')
for cookie in ab.cookie_jar:
print(cookie)
```
運行該腳本,我們看到頁面獲得五次不同時間不同cookie的訪問。成功!隨著我們匿名訪問類的建立,讓我們抹去我們訪問網站上的私人信息。
```
recon:~# python kittenTest.py
[*] Fetching page
<Cookie PHPSESSID=qg3fbia0t7ue3dnen5i8brem61 for kittenwar.com/>
[*] Fetching page
<Cookie PHPSESSID=25s8apnvejkakdjtd67ctonfl0 for kittenwar.com/>
[*] Fetching page
<Cookie PHPSESSID=16srf8kscgb2l2e2fknoqf4nh2 for kittenwar.com/>
[*] Fetching page
<Cookie PHPSESSID=73uhg6glqge9p2vpk0gt3d4ju3 for kittenwar.com/>
```
## 用匿名類抹去WEB頁面
現在我們可以用Python取回WEB內容。可以開始偵查目標了。我們可以通過檢索大型的網站來開始我們的研究了。攻擊者可以深入的調查目標的主頁面尋找隱藏的和有價值的數據。然而這種搜索行動會產生大量的頁面瀏覽器。移動網站的內容到本地能減少頁面的瀏覽數。我們可以只訪問頁面一次,然后研究無數次。有一些框架可以這樣做,但是我們將建立我們自己的,利用先前的`anonBrowser`類。讓我們利用`anonBrowser`類檢索目標網站所有的鏈接吧。
## 用Beautiful Soup解析Href鏈接
為了從目標網站解析鏈接,我們有兩個選擇:(1)利用正則表達式來搜索和替換HTML代碼。(2)使用強大的第三方庫`BeautifulSoup`,可以在下面網站下載安裝: http://www.crummy.com/software/BeautifulSoup/ 。`BeautifulSoup`的創造者構建了這個極好的庫來處理和解析HTML代碼和XML。首先,我們看看怎樣使用兩種方法找到鏈接,然后解釋為什么大多數情況下`BeautifulSoup`是很好的選擇。
```
# coding=UTF-8
from anonBrowser import *
from BeautifulSoup import BeautifulSoup
import optparse
import re
def printLinks(url):
ab = anonBrowser()
ab.anonymize()
page = ab.open(url)
html = page.read()
try:
print '[+] Printing Links From Regex.'
link_finder = re.compile('href="(.*?)"')
links = link_finder.findall(html)
for link in links:
print link
except:
pass
try:
print '\n[+] Printing Links From BeautifulSoup.'
soup = BeautifulSoup(html)
links = soup.findAll(name='a')
for link in links:
if link.has_key('href'):
print link['href']
except:
pass
def main():
parser = optparse.OptionParser('usage%prog -u <target url>')
parser.add_option('-u', dest='tgtURL', type='string', help='specify target url')
(options, args) = parser.parse_args()
url = options.tgtURL
if url == None:
print parser.usage
exit(0)
else:
printLinks(url)
if __name__ == '__main__':
main()
```
運行我們的腳本,讓我們來解析來自流行網站的鏈接,我們的腳本產生鏈接的結果通過正則表達式和`BeautifulSoup`解析。
```
recon:# python linkParser.py -uhttp://www.hampsterdance.com/
[+] Printing Links From Regex.
styles.css
http://Kunaki.com/Sales.asp?PID=PX00ZBMUHD
http://Kunaki.com/Sales.asp?PID=PX00ZBMUHD
Freshhampstertracks.htm
freshhampstertracks.htm
freshhampstertracks.htm
http://twitter.com/hampsterrific
http://twitter.com/hampsterrific
https://app.expressemailmarketing.com/Survey.aspx?SFID=32244
funnfree.htm
https://app.expressemailmarketing.com/Survey.aspx?SFID=32244
https://app.expressemailmarketing.com/Survey.aspx?SFID=32244
meetngreet.htm
http://www.asburyarts.com
index.htm
meetngreet.htm
musicmerch.htm
funnfree.htm
freshhampstertracks.htm
hampsterclassics.htm
http://www.statcounter.com/joomla/
[+] Printing Links From BeautifulSoup.
http://Kunaki.com/Sales.asp?PID=PX00ZBMUHD
http://Kunaki.com/Sales.asp?PID=PX00ZBMUHD
freshhampstertracks.htm
freshhampstertracks.htm
freshhampstertracks.htm
http://twitter.com/hampsterrific
http://twitter.com/hampsterrific
https://app.expressemailmarketing.com/Survey.aspx?SFID=32244
funnfree.htm
https://app.expressemailmarketing.com/Survey.aspx?SFID=32244
https://app.expressemailmarketing.com/Survey.aspx?SFID=32244
meetngreet.htm
http://www.asburyarts.com
http://www.statcounter.com/joomla/
```
乍一看兩個似乎差不多。然而,使用正則表達式和`BeautifulSoup`產生了不同的結果,與一個特定的數據塊相關聯的標簽變化不大,造成程序更加頑固的是網站管理員的念頭。比如,我們的正則表達式包含CSS作為一個`link`,顯然,這不是一個鏈接,但他被正則表達式匹配了。`BeautifulSoup`解析時知道忽略它,不包含。
## 用Beautiful Soup下載圖片
除了網頁上面的鏈接,它上面的圖片可能會有用。在第三章,我們展示了如何從圖像中提取元數據。再一次,`BeautifulSoup`成為了關鍵,允許在任何HTML中搜索`img`標簽。瀏覽器對象下載圖片保存在本地硬盤,代碼的變化只是將鏈接變為圖像。隨著這些變化,我們基本的檢索器已經變得足夠強大到找到網頁的鏈接和下載圖像。
```
# coding=UTF-8
from anonBrowser import *
from BeautifulSoup import BeautifulSoup
import os
import optparse
def mirrorImages(url, dir):
ab = anonBrowser()
ab.anonymize()
html = ab.open(url)
soup = BeautifulSoup(html)
image_tags = soup.findAll('img')
for image in image_tags:
filename = image['src'].lstrip('http://')
filename = os.path.join(dir, filename.replace('/', '_'))
print('[+] Saving ' + str(filename))
data = ab.open(image['src']).read()
ab.back()
save = open(filename, 'wb')
save.write(data)
save.close()
def main():
parser = optparse.OptionParser('usage%prog -u <target url> -d <destination directory>')
parser.add_option('-u', dest='tgtURL', type='string', help='specify target url')
parser.add_option('-d', dest='dir', type='string', help='specify destination directory')
(options, args) = parser.parse_args()
url = options.tgtURL
dir = options.dir
if url == None or dir == None:
print parser.usage
exit(0)
else:
try:
mirrorImages(url, dir)
except Exception, e:
print('[-] Error Mirroring Images.')
print('[-] ' + str(e))
if __name__ == '__main__':
main()
```
運行這個腳本,我們看到它成功的下載了網站的所有圖像。
```
econ:~# python imageMirror.py -u http://xkcd.com -d /tmp/
[+] Saving /tmp/imgs.xkcd.com_static_terrible_small_logo.png
[+] Saving /tmp/imgs.xkcd.com_comics_moon_landing.png
[+] Saving /tmp/imgs.xkcd.com_s_a899e84.jpg
```
## 研究,調查,發現
在大多數現代社會工程學的嘗試中,攻擊者的目標從公司或者企業開始。對于Stuxnet的肇事者,是一個有權限進入SCADA系統的伊朗人。極光行動背后的人是通過調查公司的人員而獲取對重要地點的訪問權的。讓我們假設,我們有一個有趣的公司并知道背后一個主要人物,一個通常的攻擊者可能會有比這個更少的信息。攻擊者往往只有攻擊者更宏觀的知識,他們需要利用互聯網和其他資源深入了解個人。從Oracle,Google等所有的,我們利用接下來的一系列的腳本。
## 用Python和Google API交互
想象一下,一個朋友問你一個隱晦的問題,他們錯誤的以為你知道些什么。你怎么回答?Google一下。所以,我們如何了解目標公司的更多信息了?好的,答案再次是Google。Google提供了應用程序接口API允許程序員進行查詢并得到結果,而不必嘗試破解正常的Google界面。目前有兩套API,老舊的API和API,這些需要開發者密鑰。要求獨一無二的開發者密鑰讓匿名變得不可能,一些我們以努力獲得成功的腳本將不能用。幸運的是老舊的版本任然允許一天之中進行一系列的查詢,大約每天30次搜索結果。用于收集信息的話30次結果足夠了解一個組織網站的信息了。我們將建立我們的查詢功能,返回攻擊者感興趣的信息。
```
import urllib
from anonBrowser import *
def google(search_term):
ab = anonBrowser()
search_term = urllib.quote_plus(search_term)
response = ab.open('http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=' + search_term)
print(response.read())
google('Boondock Saint')
```
從Google返回的內容和下面的類似。
```
{"responseData": {"results":[{"GsearchResultClass":"GwebSearch",
"unescapedUrl":"http://www.boondocksaints.com/","url":"http://
www.boondocksaints.com/","visibleUrl":"www.boondocksaints.
com","cacheUrl":"http://www.google.com/search?q\
u003dcache:J3XW0wgXgn4J:www.boondocksaints.com","title":"The \
u003cb\u003eBoondock Saints\u003c/b\u003e","titleNoFormatting":"The
Boondock
<..SNIPPED..>
\u003cb\u003e...\u003c/b\u003e"}],"cursor":{"resultCount":"62,800",
"pages":[{"start":"0","label":1},{"start":"4","label":2},{"start
":"8","label":3},{"start":"12","label":4},{"start":"16","label":
5},{"start":"20","label":6},{"start":"24","label":7},{"start":"2
8","label":8}],"estimatedResultCount":"62800","currentPageIndex"
:0,"moreResultsUrl":"http://www.google.com/search?oe\u003dutf8\
u0026ie\u003dutf8\u0026source\u003duds\u0026start\u003d0\u0026hl\
u003den\u0026q\u003dBoondock+Saint","searchResultTime":"0.16"}},
"responseDetails": null, "responseStatus": 200}
```
`quote_plus()`函數是這個腳本中的新的代碼塊。URL編碼是指非字母數字的字符被轉換然后發送到服務器。雖然不是完美的URL編碼,但是適合我們的目的。最后打印Google的響應顯示:一個長字符串的括號和引號。如果你仔細觀察,會發現響應的內容看起來很像字典。這些響應是json格式的,和字典非常相似,不出所料,Python有庫可以構建和處理json字符串。讓我們添加這個功能重新審視這個響應。
```
import urllib, json
from anonBrowser import *
def google(search_term):
ab = anonBrowser()
search_term = urllib.quote_plus(search_term)
response = ab.open('http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=' + search_term)
objects = json.load(response
print(response.read())
google('Boondock Saint')
```
當我們打印對象時,看起來非常像第一次函數的響應。json庫加載響應到一個字典,讓這些字段更容易理解,而不需要手動的解析字符串。
```
{u'responseData': {u'cursor': {u'moreResultsUrl': u'http://www.google.
com/search?oe=utf8&ie=utf8&source=uds&start=0&hl=en&q=Boondock
+Saint', u'estimatedResultCount': u'62800', u'searchResultTime':
u'0.16', u'resultCount': u'62,800', u'pages': [{u'start': u'0',
u'label': 1}, {u'start': u'4', u'label': 2}, {u'start': u'8',
u'label': 3}, {u'start': u'12', u'label': 4}, {u'start': u'16',
u'label': 5}, {u'start': u'20', u'label': 6}, {u'start': u'24',
u'label': 7}, {u'start': u'28', u..SNIPPED..>
Saints</b> - Wikipedia, the free encyclopedia', u'url': u'http://
en.wikipedia.org/wiki/The_Boondock_Saints', u'cacheUrl': u'http://
www.google.com/search?q=cache:BKaGPxznRLYJ:en.wikipedia.org',
u'unescapedUrl': u'http://en.wikipedia.org/wiki/The_Boondock_
Saints', u'content': u'The <b>Boondock Saints</b> is a 1999 American
action film written and directed by Troy Duffy. The film stars Sean
Patrick Flanery and Norman Reedus as Irish fraternal <b>...</b>'}]},
u'responseDetails': None, u'responseStatus': 200}
```
現在我們可以考慮在一個給定的Google搜索的結果里什么事是重要的。顯然,頁面返回的鏈接很重要。此外,頁面的標題和Google用的小的文本斷對理解鏈接指向哪里也很重要。為了組織這些結果,我們創建了一個類來保存結果。這將是訪問不同的信息更容易。
```
# coding=UTF-8
import json
import urllib
import optparse
from anonBrowser import *
class Google_Result:
def __init__(self,title,text,url):
self.title = title
self.text = text
self.url = url
def __repr__(self):
return self.title
def google(search_term):
ab = anonBrowser()
search_term = urllib.quote_plus(search_term)
response = ab.open('http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=' + search_term)
objects = json.load(response)
results = []
for result in objects['responseData']['results']:
url = result['url']
title = result['titleNoFormatting']
text = result['content']
new_gr = Google_Result(title, text, url)
results.append(new_gr)
return result
def main():
parser = optparse.OptionParser('usage%prog -k <keywords>')
parser.add_option('-k', dest='keyword', type='string', help='specify google keyword')
(options, args) = parser.parse_args()
keyword = options.keyword
if options.keyword == None:
print(parser.usage)
exit(0)
else:
results = google(keyword)
print(results)
if __name__ == '__main__':
main()
```
這種更簡潔的呈現數據的方式產生以下輸出:
```
recon:~# python anonGoogle.py -k 'Boondock Saint'
[The Boondock Saints, The Boondock Saints (1999) - IMDb, The Boondock
Saints II: All Saints Day (2009) - IMDb, The Boondock Saints -
Wikipedia, the free encyclopedia]
```
## 用Python解析Tweets
在這一點上,我們的腳本已經自動的收集了一些我們的目標的信息。在我們的下一系列的步驟中,我們將撤離域和組織,開始在網上尋找可用的個人信息。
像Google,Twitter給開發者提供了API,該文檔在 https://dev.twitter.com/docs ,非常深入,提供了更多特點的訪問,但在本程序中用不到。
讓我們探究以下如何從Twitter檢索數據。具體來說,我們要轉發美國愛國者黑客th3j35t3r的微博,他把Boondock Saint作為自己的昵稱。我們將構建`reconPerson()`類然后輸入th3j35t3r作為Twitter的搜索關鍵字。
```
# coding=UTF-8
import json
import urllib
from anonBrowser import *
class reconPerson:
def __init__(self,first_name,last_name, job='',social_media={}):
self.first_name = first_name
self.last_name = last_name
self.job = job
self.social_media = social_media
def __repr__(self):
return self.first_name + ' ' + self.last_name + ' has job ' + self.job
def get_social(self, media_name):
if self.social_media.has_key(media_name):
return self.social_media[media_name]
return None
def query_twitter(self, query):
query = urllib.quote_plus(query)
results = []
browser = anonBrowser()
response = browser.open('http://search.twitter.com/search.json?q='+ query)
json_objects = json.load(response)
for result in json_objects['results']:
new_result = {}
new_result['from_user'] = result['from_user_name']
new_result['geo'] = result['geo']
new_result['tweet'] = result['text']
results.append(new_result)
return results
ap = reconPerson('Boondock', 'Saint')
print ap.query_twitter('from:th3j35t3r since:2010-01-01 include:retweets')
```
當進一步的繼續檢索Twitter,我們已經看到了打來那個的信息,這可能對愛國者黑客有用。他正在和一些黑客團體UGNazi的支持者起沖突。好奇心驅使我們想知道為什么會變成這樣。
```
recon:~# python twitterRecon.py
[{'tweet': u'RT @XNineDesigns: @th3j35t3r Do NOT give up. You are
the bastion so many of us need. Stay Frosty!!!!!!!!', 'geo':
None, 'from_user': u'p\u01ddz\u0131uod\u0250\u01dd\u028d \u029e\
u0254opuooq'}, {'tweet': u'RT @droogie1xp: "Do you expect me to
talk?" - #UGNazi "No #UGNazi I expect you to die." @th3j35t3r
#ticktock', 'geo': None, 'from_user': u'p\u01ddz\u0131uod\u0250\
u01dd\u028d \u029e\u0254opuooq'}, {'tweet': u'RT @Tehvar: @th3j35t3r
my thesis paper for my masters will now be focused on supporting the
#wwp, while I can not donate money I can give intelligence.'
<..SNIPPED..>
```
我希望,你看到這個代碼的時候在想“我現在知道該怎么做了!”確實是這樣,從互聯網上檢索一些特定模式的信息之后。顯然,使用Twitter的結果沒有用,使用他們尋找目標的信息。當談論獲取個人信息時社交平臺是一個金礦。個人的生日,家鄉甚至家庭地址,電話號碼等隱秘的信息都會被給予不懷好意的人。人們往往沒有意識到這個問題,使用社交網站是不安全的習慣。讓我們進一步的探究從Twitter的提交里面提取位置數據。
## 獲取Twitter的位置數據
很多Twitter用戶遵守一個不成文的規定,當有創作時就與全世界分享。一般來說,計算公式是:【其他Twitter用戶的消息是針對】+【文本的消息加上段連接】+【Hash標簽】。其他的信息可能也包括,但是不在消息體內,就像圖像或者位置。然而,退后一步,以攻擊者的眼光審視一下這個公式,對于惡意用戶這個公式變成了:【用戶感興趣的人,增加某人真正交流的機會】+【某人感興趣的鏈接或者主題,他們會對這個主題的消息很感興趣】+【某人可能會對這個主題有更多的了解】。圖片或者地理標簽不在有用或者是朋友的有趣的花邊新聞。他們會成為配置中的額外的細節,例如某人經常去哪吃早餐。雖然這可能是一個偏執的觀點,我們將自動化的收集從Twitter檢索的每一條信息。
```
# coding=UTF-8
import json
import urllib
import optparse
from anonBrowser import *
def get_tweets(handle):
query = urllib.quote_plus('from:' + handle+ ' since:2009-01-01 include:retweets')
tweets = []
browser = anonBrowser()
browser.anonymize()
response = browser.open('http://search.twitter.com/search.json?q='+ query)
json_objects = json.load(response)
for result in json_objects['results']:
new_result = {}
new_result['from_user'] = result['from_user_name']
new_result['geo'] = result['geo']
new_result['tweet'] = result['text']
tweets.append(new_result)
return tweets
def load_cities(cityFile):
cities = []
for line in open(cityFile).readlines():
city=line.strip('\n').strip('\r').lower()
cities.append(city)
return cities
def twitter_locate(tweets,cities):
locations = []
locCnt = 0
cityCnt = 0
tweetsText = ""
for tweet in tweets:
if tweet['geo'] != None:
locations.append(tweet['geo'])
locCnt += 1
tweetsText += tweet['tweet'].lower()
for city in cities:
if city in tweetsText:
locations.append(city)
cityCnt+=1
print("[+] Found "+str(locCnt)+" locations via Twitter API and "+str(cityCnt)+" locations from text search.")
return locations
def main():
parser = optparse.OptionParser('usage%prog -u <twitter handle> [-c <list of cities>]')
parser.add_option('-u', dest='handle', type='string', help='specify twitter handle')
parser.add_option('-c', dest='cityFile', type='string', help='specify file containing cities to search')
(options, args) = parser.parse_args()
handle = options.handle
cityFile = options.cityFile
if (handle==None):
print parser.usage
exit(0)
cities = []
if (cityFile!=None):
cities = load_cities(cityFile)
tweets = get_tweets(handle)
locations = twitter_locate(tweets,cities)
print("[+] Locations: "+str(locations))
if __name__ == '__main__':
main()
```
我了測試我們的腳本,我們建立了城市的列表。
```
recon:~# cat mlb-cities.txt | more
baltimore
boston
chicago
cleveland
detroit
<..SNIPPED..>
recon:~# python twitterGeo.py -u redsox -c mlb-cities.txt
[+] Found 0 locations via Twitter API and 1 locations from text search.
[+] Locations: ['toronto'] recon:~# python twitterGeo.py -u nationals -c mlb- cities.txt
[+] Found 0 locations via Twitter API and 1 locations from text search.
[+] Locations: ['denver']
```
## 用正則表達式解析Twitter的關注
接下來我們將收集目標的興趣,這包括其他用戶或者是網路內容。任何時候網站都提供了能力知道用戶對什么感興趣,跳過去,這些數據將成為成功的社會工程攻擊的基礎。如我們前面討論的,Twitter的興趣點包含任何鏈接,Hash標簽或者是其他用戶提到的內容。用正則表達式找到這些很容易。
```
# coding=UTF-8
import json
import re
import urllib
import urllib2
import optparse
from anonBrowser import *
def get_tweets(handle):
query = urllib.quote_plus('from:' + handle+ ' since:2009-01-01 include:retweets')
tweets = []
browser = anonBrowser()
browser.anonymize()
response = browser.open('http://search.twitter.com/search.json?q=' + query)
json_objects = json.load(response)
for result in json_objects['results']:
new_result = {}
new_result['from_user'] = result['from_user_name']
new_result['geo'] = result['geo']
new_result['tweet'] = result['text']
tweets.append(new_result)
return tweets
def find_interests(tweets):
interests = {}
interests['links'] = []
interests['users'] = []
interests['hashtags'] = []
for tweet in tweets:
text = tweet['tweet']
links = re.compile('(http.*?)\Z|(http.*?) ').findall(text)
for link in links:
if link[0]:
link = link[0]
elif link[1]:
link = link[1]
else:
continue
try:
response = urllib2.urlopen(link)
full_link = response.url
interests['links'].append(full_link)
except:
pass
interests['users'] += re.compile('(@\w+)').findall(text)
interests['hashtags'] +=re.compile('(#\w+)').findall(text)
interests['users'].sort()
interests['hashtags'].sort()
interests['links'].sort()
return interests
def main():
parser = optparse.OptionParser('usage%prog -u <twitter handle>')
parser.add_option('-u', dest='handle', type='sring', help='specify twitter handle')
(options, args) = parser.parse_args()
handle = options.handle
if handle == None:
print(parser.usage)
exit(0)
tweets = get_tweets(handle)
interests = find_interests(tweets)
print('\n[+] Links.')
for link in set(interests['links']):
print(' [+] ' + str(link))
print('\n[+] Users.')
for user in set(interests['users']):
print(' [+] ' + str(user))
print('\n[+] HashTags.')
for hashtag in set(interests['hashtags']):
print('\n[+] ' + str(hashtag))
if __name__ == '__main__':
main()
```
運行我們的興趣分析腳本,我們看到它解析出針對目標的鏈接,用戶名,Hash標簽。請注意,它返回了一個Youtube的視頻,一些用戶名和當前即將到來的比賽的Hash標簽。好奇心再一次讓我們知道該怎么做。
```
recon:~# python twitterInterests.py -u sonnench
[+] Links.
[+]http://www.youtube.com/watch?v=K-BIuZtlC7k&feature=plcp
[+] Users.
[+] @tomasseeger
[+] @sonnench
[+] @Benaskren
[+] @AirFrayer
[+] @NEXERSYS
[+] HashTags.
[+] #UFC148
```
這里使用正則表達式不是尋找信息的合適方法。正則表達式抓住包含鏈接的文本將會錯過某一特定的URL,因為用正則表達式很難匹配所有格式的URL。然而,對我們而言正則表達式99%的情況下會工作。此外,使用`urllib2`庫里的函數打開鏈接而不是我們的匿名類。
再一次,我們將使用使用一個字典排序信息到一個更加易于管理的數據結構中,所以我們不需要創造一個類。由于Twitter字符的限制,許多URL使用某種服務把URL變短了。這些鏈接并不非常有用,因為他們能指向任何地方。為了擴展他們,我們將使用`urllib2`打開。腳本打開頁面后,`urllib`能取回整個URL。其他用戶和Hash標簽將使用類似的正則表達式來檢索。并返回給主要的方法`twitter()`。位置和關注最后會被調用得到。
可以做其他事情擴展處理Twitter的能力。互聯網上有無限的資源,無數中分析數據的方法要求擴大自動化收集信息程序的能力。
將我們整個系列的偵查包裝在一起,我們做了一個類來檢索位置,興趣和Twitter。這些在下一節中將會看到用處的。
```
# coding=UTF-8
import urllib
from anonBrowser import *
import json
import re
import urllib2
class reconPerson:
def __init__(self, handle):
self.handle = handle
self.tweets = self.get_tweets()
def get_tweets(self):
query = urllib.quote_plus('from:' + self.handle+' since:2009-01-01 include:retweets')
tweets = []
browser = anonBrowser()
browser.anonymize()
response = browser.open('http://search.twitter.com/search.json?q=' + query)
json_objects = json.load(response)
for result in json_objects['results']:
new_result = {}
new_result['from_user'] = result['from_user_name']
new_result['geo'] = result['geo']
new_result['tweet'] = result['text']
tweets.append(new_result)
return tweets
def find_interests(self):
interests = {}
interests['links'] = []
interests['users'] = []
interests['hashtags'] = []
for tweet in self.tweets:
text = tweet['tweet']
links = re.compile('(http.*?)\Z|(http.*?) ').findall(text)
for link in links:
if link[0]:
link = link[0]
elif link[1]:
link = link[1]
else:continue
try:
response = urllib2.urlopen(link)
full_link = response.url
interests['links'].append(full_link)
except:
pass
interests['users'] +=re.compile('(@\w+)').findall(text)
interests['hashtags'] +=re.compile('(#\w+)').findall(text)
interests['users'].sort()
interests['hashtags'].sort()
interests['links'].sort()
return interests
def twitter_locate(self, cityFile):
cities = []
if cityFile != None:
for line in open(cityFile).readlines():
city = line.strip('\n').strip('\r').lower()
cities.append(city)
locations = []
locCnt = 0
cityCnt = 0
tweetsText = ''
for tweet in self.tweets:
if tweet['geo'] != None:
locations.append(tweet['geo'])
locCnt += 1
tweetsText += tweet['tweet'].lower()
for city in cities:
if city in tweetsText:
locations.append(city)
cityCnt += 1
return locations
```
## 匿名郵件
越來約頻繁的,網站要求用戶創建并登陸賬戶,如果他們想訪問網站的最佳資源的話。這顯然會出現一個問題,對于傳統的瀏覽用戶,瀏覽互聯網的瀏覽器是不同的,登陸顯然破壞了匿名瀏覽,登陸后的任何行為取決于賬戶。大多數網站只需要一個有效的郵件地址并不檢查其他的私人信息。像雅虎,Google提供的郵箱服務是免費的,很容易申請。然而,他們有一些服務和條款你必須接受和理解。
一個很好的選擇是使用一個一次性的郵箱賬戶獲得一個永久性的郵箱。十分鐘郵箱 http://10minutemail.com/10MinuteMail/index.html 提供一個一次性的郵箱。攻擊者可以利用很難追查的電子郵件創建不依賴他們的賬戶。大多數網站最起碼的使用條款是不允許收集其他用戶的信息。雖然實際的攻擊者不遵守這些規定,對賬戶使用這種技術完全可以做到。記住,雖然這一技術可以被用來保護你,你應該采取行動,確保你的賬戶的行為安全。
## 大規模的社會工程
在這一點上,我們已經收集了目標的大量的有價值的信息。利用這些信息自動的生成郵件是一個復雜的事,尤其是添加了足夠的細節讓他變得可行。在這一點上一個選項可能會讓目前的程序停止:這也允許攻擊者利用所有的有用的信息構造一個郵件。然而,手動發郵件給一個大組織的每一個人是不可行的。Python的能力允許我們的這個過程自動化并快速獲得結果。為了這個目的,我們將使用收集到的信息建立一個非常簡單的郵件并發送給目標。
## 使用Smtplib發送郵件給目標
發送電子郵件的過程中通常需要開發客戶的選擇,點擊新建,然后點擊發送。在這背后,客戶端連接到服務器,可能記錄了日志,交換信息的發送人,收件人和其他必要的資料。Python的`Smtplib`庫將在程序中處理這些過程。我們將通過建立一個Python的電子郵件客戶端發送我們的惡意郵件給目標。這個客戶端很基本,但讓我們在程序中發送郵件很簡單。我們這次的目的,我們將使用Google的郵件SMTP服務,你需要創建一個Google郵件賬戶,在我們的腳本中使用,或者使用自己的SMTP服務器。
```
import smtplib
from email.mime.text import MIMEText
def sendMail(user,pwd,to,subject,text):
msg = MIMEText(text)
msg['From'] = user
msg['To'] = to
msg['Subject'] = subject
try:
smtpServer = smtplib.SMTP('smtp.gmail.com', 587)
print("[+] Connecting To Mail Server.")
smtpServer.ehlo()
print("[+] Starting Encrypted Session.")
smtpServer.starttls()
smtpServer.ehlo()
print("[+] Logging Into Mail Server.")
smtpServer.login(user, pwd)
print("[+] Sending Mail.")
smtpServer.sendmail(user, to, msg.as_string())
smtpServer.close()
print("[+] Mail Sent Successfully.")
except:
print("[-] Sending Mail Failed.")
user = 'username'
pwd = 'password'
sendMail(user, pwd, 'target@tgt.tgt', 'Re: Important', 'Test Message')
```
運行腳本,檢查我們的郵箱,我們可以看到成功的發送了郵件。
```
recon:# python sendMail.py
[+] Connecting To Mail Server.
[+] Starting Encrypted Session.
[+] Logging Into Mail Server.
[+] Sending Mail.
[+] Mail Sent Successfully.
```
提供了有效的郵件服務器和參數,客戶端將正確的發送郵件給目標。有許多的郵件服務器,然而,不帶開轉發,我們只能發送郵件到就特定的地址。在本地的郵件服務中設置轉發,或者在互聯網上打開轉發。將能發送郵件從任何地址到任何地址,發送方的地址甚至可以是無效的。
垃圾郵件的發送者使用相同的技術發送郵件來自`Potus@whitehouse.gov`:他們簡單的偽造了發送地址。很少
有人會打開來自可疑地址郵件,我們可以偽造郵件的發送地址是關鍵。使用客
戶端打開,打開轉發功能,是攻擊者從一個看起來值得信奈的地址發送郵件,
增加用戶點開郵件的可能性
## 用Smtplib進行魚叉式網絡釣魚
將我們所有的研究放在一起是我們最后的階段。在這里,我們的腳本創建了一個看起來像目標朋友發來的電子郵件,目標發現一些有趣的事情,郵件看起來是真人寫的。大量的研究投入到幫助電腦的的交流看起來更像人,各種各樣的方法任然在完善。為了減少這種可能性,我們將創建一個包含攻擊荷載的的簡單的信息郵件。程序的幾個部分之一將涉及選擇包含這條信息。我們的程序將按數據隨機的選擇。采取地步驟是:選擇虛假的發件人地址,制作一個主題,創建一個信息,然后發送電子郵件。幸運的是創建發送人和主題是相當的簡單。
代碼的if語句仔細的處理和如何將短信息連接在一起是很重要的問題。當處理數量巨大的可能性時,在我們的偵查中將使用更多情況的代碼,每一個可能性會被分為獨立的函數。每一個方法將以特定的的方法承擔一塊的開始和結束,然后獨立與其他代碼的操作。這樣,收集到某人的信息就越多,唯一改變的是方法。最后一步是通過我們的電子郵件客戶端,相信它的人愚蠢的做剩下的活。這個過程的沒一部分在這一章中都討論過,這是任何被用來獲取權限的釣魚網站的產物。在我們的例子中,我們簡單的發送一個名不副實的鏈接,有效荷載可以是附件或者是詐騙網站,或者任何其他的攻擊方法。這個過程將對每一個成員重復,只要一個人上當攻擊者就能獲取權限。
我們特定的腳本將攻擊一個用戶基于他公開的信息。基于他的地點,用戶,Hash標簽,鏈接,腳本將創建一個附帶惡意鏈接的郵件等待用戶點擊。
```
# coding=UTF-8
import smtplib
import optparse
from email.mime.text import MIMEText
from twitterClass import *
from random import choice
def sendMail(user,pwd,to,subject,text):
msg = MIMEText(text)
msg['From'] = user
msg['To'] = to
msg['Subject'] = subject
try:
smtpServer = smtplib.SMTP('smtp.gmail.com', 587)
print("[+] Connecting To Mail Server.")
smtpServer.ehlo()
print("[+] Starting Encrypted Session.")
smtpServer.starttls()
smtpServer.ehlo()
print("[+] Logging Into Mail Server.")
smtpServer.login(user, pwd)
print("[+] Sending Mail.")
smtpServer.sendmail(user, to, msg.as_string())
smtpServer.close()
print("[+] Mail Sent Successfully.")
except:
print("[-] Sending Mail Failed.")
def main():
parser = optparse.OptionParser('usage%prog -u <twitter target> -t <target email> -l <gmail login> -p <gmail password>')
parser.add_option('-u', dest='handle', type='string', help='specify twitter handle')
parser.add_option('-t', dest='tgt', type='string', help='specify target email')
parser.add_option('-l', dest='user', type='string', help='specify gmail login')
parser.add_option('-p', dest='pwd', type='string', help='specify gmail password')
(options, args) = parser.parse_args()
handle = options.handle
tgt = options.tgt
user = options.user
pwd = options.pwd
if handle == None or tgt == None or user ==None or pwd==None:
print(parser.usage)
exit(0)
print("[+] Fetching tweets from: "+str(handle))
spamTgt = reconPerson(handle)
spamTgt.get_tweets()
print("[+] Fetching interests from: "+str(handle))
interests = spamTgt.find_interests()
print("[+] Fetching location information from: "+ str(handle))
location = spamTgt.twitter_locate('mlb-cities.txt')
spamMsg = "Dear "+tgt+","
if (location!=None):
randLoc=choice(location)
spamMsg += " Its me from "+randLoc+"."
if (interests['users']!=None):
randUser=choice(interests['users'])
spamMsg += " "+randUser+" said to say hello."
if (interests['hashtags']!=None):
randHash=choice(interests['hashtags'])
spamMsg += " Did you see all the fuss about "+ randHash+"?"
if (interests['links']!=None):
randLink=choice(interests['links'])
spamMsg += " I really liked your link to: "+randLink+"."
spamMsg += " Check out my link to http://evil.tgt/malware"
print("[+] Sending Msg: "+spamMsg)
sendMail(user, pwd, tgt, 'Re: Important', spamMsg)
if __name__ == '__main__':
main()
```
測試我們的腳本,我們可以獲得一些關于Boston Red Sox的信息,從他的Twitter賬戶上,為了發送一個惡意的垃圾郵件。
```
recon# python sendSpam.py -u redsox -t target@tgt -l username -p password
[+] Fetching tweets from: redsox
[+] Fetching interests from: redsox
[+] Fetching location information from: redsox
[+] Sending Msg: Dear redsox, Its me from toronto. @davidortiz said to say hello. Did you see all the fuss about #SoxAllStars? I really liked your link to:http://mlb.mlb.com. Check out my link to http://evil.tgt/malware
[+] Connecting To Mail Server.
[+] Starting Encrypted Session.
[+] Logging Into Mail Server.
[+] Sending Mail.
[+] Mail Sent Successfully.
```
## 本章總結
雖然這個方法不是用于另一個人或者組織,但它對認識其可行性和組織的脆弱性很重要。Python和其他腳本語言允許程序員快速的創建一個方法,使用從互聯網上找到的廣闊的資源,來獲取潛在的利益。在我們的代碼中,我創建了一個類來模擬瀏覽器同時增加了匿名訪問,檢索網站,使用強大的Google,利用Twitter來了解目標的更多信息功能,然后把所有的細節發送一個特殊的電子郵件給目標用戶。互聯網的連接速度限制了程序,線程的某些函數將大大的減少執行時間。此外,一旦我們學會了如何從數據源中檢索信息,對其他網站做同樣的信息是很簡單的。個人美譽訪問和處理互聯網上大量的信息的能力,但是強大的Python和它的庫允許訪問每一個資源的能力遠遠高于幾個熟練的人員。知道這一切,攻擊不是你想象中的那么復雜,你的組織是如何的脆弱?什么公開的信息可以被攻擊者使用?你會成為一個Python檢索信息和惡意郵件的受害者嗎?