前言

本項(xiàng)目已開源:快去 Star & Fork 吧?。?!
GitHub:https://github.com/jwt1399/Sec-Tools
Gitee:https://gitee.com/jwt1399/Sec-Tools
項(xiàng)目介紹
系統(tǒng)簡介
本項(xiàng)目命名為Sec-Tools,是一款基于 Python-Django 的在線多功能 Web 應(yīng)用滲透測試系統(tǒng),包含漏洞檢測、目錄識別、端口掃描、指紋識別、域名探測、旁站探測、信息泄露檢測等功能。本系統(tǒng)通過旁站探測和域名探測功能對待檢測網(wǎng)站進(jìn)行資產(chǎn)收集,通過端口掃描、指紋識別、目錄識別和信息泄露檢測功能對待檢測網(wǎng)站進(jìn)行信息收集,通過收集的信息分析評估網(wǎng)站存在哪些安全隱患,然后使用漏洞檢測功能揭示網(wǎng)站存在的漏洞以及危害等級并給出修復(fù)建議。通過這一系列的步驟,可以對Web應(yīng)用進(jìn)行全面檢測,從而發(fā)現(xiàn)網(wǎng)站存在的安全隱患,因此用戶可以針對相應(yīng)的網(wǎng)絡(luò)威脅做出應(yīng)急響應(yīng),進(jìn)而提升站點(diǎn)的安全性。
相關(guān)技術(shù)
| 名稱 | 版本 |
|---|---|
| Python | 3.7.0 |
| Django | 3.1.4 |
| SQLite | 3.35.2 |
| ECharts | 5.0.1 |
| Tabler | 1.0.0-beta2 |
| SimpleUI | 2021.1.1 |
| Docsify | 4.11.6 |
| Layer | 3.2.0 |
| Boostrap Table | 1.18.2 |
項(xiàng)目功能

項(xiàng)目首頁
首頁采用 ECharts 對漏洞掃描的漏洞等級、指紋識別組件、安全導(dǎo)航數(shù)據(jù)做了可視化圖表展示,圖表的風(fēng)格沒有統(tǒng)一,湊合看吧??

身份驗(yàn)證
新用戶想要使用系統(tǒng)功能必須要注冊登錄,游客只能訪問部分頁面。本系統(tǒng)有普通用戶和超級用戶。普通用戶可以使用本系統(tǒng)的所有功能,但是不能登錄后臺管理系統(tǒng)。超級用戶不僅可以使用所用功能還可以登錄后臺管理系統(tǒng)中所有的用戶權(quán)限和數(shù)據(jù)。
設(shè)計(jì)思路:登錄和注冊模塊在 Django 自帶的認(rèn)證模塊的基礎(chǔ)上進(jìn)行實(shí)現(xiàn),因此在后臺-->用戶與授權(quán)就可對注冊用戶進(jìn)行權(quán)限分配和相應(yīng)管理。我們使用 Django 自帶的數(shù)據(jù)庫 SQLite 來存放賬戶信息,重構(gòu)了數(shù)據(jù)庫表auth_user表,增加了用戶郵箱字段,auth_user 中密碼字段是加了 salt 的 sha256 值再經(jīng)過 base64 編碼之后的值,保障了用戶的信息安全。
[圖片上傳失敗...(image-100e46-1636383910442)]
| 登錄頁 | [圖片上傳失敗...(image-676a14-1636383910442)] |
|---|---|
| 注冊頁 | [圖片上傳失敗...(image-ed7783-1636383910442)] |
重設(shè)密碼功能調(diào)用第三方包 django-password-reset 進(jìn)行實(shí)現(xiàn)
| 步驟一 | [圖片上傳失敗...(image-1ced47-1636383910442)] |
|---|---|
| 步驟二 | [圖片上傳失敗...(image-70e9df-1636383910442)] |
漏洞檢測
該模塊主要是對目標(biāo)Web系統(tǒng)進(jìn)行安全漏洞掃描,包括SQL注入、跨站腳本攻擊(XSS)、弱密碼、中間件漏洞。中間件漏洞掃描包括對Weblogic、Struts2、Tomcat 、Jboss、Drupal、Nexus的已知漏洞進(jìn)行檢測,用戶提供目標(biāo)URL并選擇CVE漏洞編號。
設(shè)計(jì)思路
該模塊的全掃描、SQL注入漏洞掃描、XSS漏洞掃描、弱口令掃描、僅爬取是調(diào)用 AWVS API 進(jìn)行實(shí)現(xiàn)。中間件漏洞掃描是基于腳本模擬網(wǎng)絡(luò)請求實(shí)現(xiàn)。根據(jù)漏洞形成的原因,生成一些測試 payload 發(fā)送到目標(biāo)系統(tǒng),再由返回的狀態(tài)碼和數(shù)據(jù)來判斷payload是否有效。
實(shí)現(xiàn)效果
[圖片上傳失敗...(image-f2e56f-1636383910442)]
點(diǎn)擊掃描目標(biāo)跳轉(zhuǎn)到漏洞結(jié)果頁:
[圖片上傳失敗...(image-21473f-1636383910442)]
再點(diǎn)擊掃描目標(biāo)的跳轉(zhuǎn)到漏洞詳情頁:
[圖片上傳失敗...(image-120af9-1636383910442)]
詳細(xì)實(shí)現(xiàn)
添加掃描目標(biāo)
漏洞掃描最開始的工作是添加掃描目標(biāo)到 AWVS 的掃描隊(duì)列中。AWVS 提供了一個(gè) API 接口: /api/v1/targets,使用 POST 請求, POST 請求參數(shù)為:{"address":"XXXX.XXXX.XXXX","description":"xxxx","criticality":"10"}。
當(dāng)目標(biāo)添加成功后會(huì)返回一個(gè) target_id ,這個(gè)值在所有掃描中是唯一的。通過 target_id 判斷目標(biāo)是否添加成功。添加完目標(biāo)后并沒有開始掃描,需要使用另一個(gè) API 接口:/api/v1/scans,使用 POST 請求,傳入剛剛添加目標(biāo)生成的 target_id 和用戶選擇的掃描類型,POST 請求參數(shù)為:{"target_id":"xxxxxxx","profile_id":"xxxxxxx"}。開始掃描將會(huì)返回狀態(tài)碼200。
使用 Python 的第三方庫 requests 來實(shí)現(xiàn) API 接口訪問。核心代碼如下:
#Target: POST請求/api/v1/targets
try:
#data包含目標(biāo)URL和類型,auth_headers包含API_KEY
response = requests.post(targets_api, auth_headers, data, False)
result = response.json()
target_id = result.get('target_id')
return target_id
except Exception:
return None
#Scan: POST請求/api/v1/scans
try:
response = requests.post(scan_api, data, auth_headers, False)
status_code = 200
except Exception:
status_code = 404
return status_code
API 接口已經(jīng)實(shí)現(xiàn),還需要獲取用戶輸入的數(shù)據(jù)。由于本系統(tǒng)是基于 Django 實(shí)現(xiàn)的,所以使用 HTML+JavaScript 提供用戶界面和接受和發(fā)送數(shù)據(jù)到后端,后端使用 Python 實(shí)現(xiàn)。首先在 urls.py 里面加入添加訪問路徑
path('vuln_scan', views.vuln_scan, name='vuln_scan')
在 views.py 中定義 vuln_scan() 函數(shù)接收前端的用戶輸入,并調(diào)用已經(jīng)寫好的 API 函數(shù)。用戶輸入的 url 為掃描的目標(biāo),掃描類型包括SQL注入、XSS漏洞、弱口令和全掃描,其中全掃描就是掃描所有類型的漏洞,如果添加成功后返回的 target_id 不是 None,說明添加成功,就可以開始調(diào)用開始掃描的 API,開始掃描后返回狀態(tài)碼,為200則開始掃描,返回成功否則返回失敗。核心代碼如下:
@csrf_exempt
def vuln_scan(request):
#通過POST請求獲取用戶輸入的URL和掃描類型
url = request.POST.get('ip')
scan_type = request.POST.get('scan_type')
t = Target(API_URL, API_KEY)
#將目標(biāo)URL添加到掃描隊(duì)列中
target_id = t.add(url)
#如果target_id不為None,則開始掃描
if target_id is not None:
s = Scan(API_URL, API_KEY)
status_code = s.add(target_id, scan_type)
if status_code == 200:
return success()
return error()
最后使用 JavaScript 來實(shí)現(xiàn)發(fā)送用戶輸入的數(shù)據(jù),選擇通過 POST 方法發(fā)送數(shù)據(jù),并在發(fā)送之前判斷用戶輸入的合法性,核心代碼如下:
function get_scan_info(ip , scan_type) {
#使用POST請求發(fā)送用戶輸入
$.post('/vuln_scan', {
ip: ip ,
scan_type: scan_type
}, function (data) {
if (data.code !== 200) {
......
} else {
......
}
......});
}
var domain = $('input[name=scan_url]').val();
#使用循環(huán)判斷用戶選擇的掃描類型
for(var i=0; i<document.getElementsByName("scan_type").length; i++) {
if (document.getElementsByName("scan_type")[i].checked) {
var scan_type=document.getElementsByName("scan_type")[i].value;
}
}
if(domain){
get_scan_info(domain,scan_type)
}else{
......
}
總體來說,通過上述的代碼實(shí)現(xiàn),實(shí)現(xiàn)了將用戶輸入通過 JavaScript 傳輸給后臺,后臺接收數(shù)據(jù)后將調(diào)用 AWVS API,然后 AWVS 開始根據(jù)用戶輸入開始掃描目標(biāo) URL,掃描結(jié)束后將結(jié)果保存在數(shù)據(jù)庫中。實(shí)現(xiàn)效果如下:
[圖片上傳失敗...(image-dcd297-1636383910442)]
獲取掃描結(jié)果
在上一小節(jié)中,將目標(biāo)掃描的結(jié)果保存到數(shù)據(jù)庫中,我們需要得到所有的掃描目標(biāo),‘/api/v1/scans‘,請求方式為 GET,請求成功后會(huì)返回所有掃描目標(biāo)的信息,利用這個(gè) API 可以實(shí)現(xiàn)展示所有掃描目標(biāo)。要實(shí)現(xiàn)展示每個(gè)掃描目標(biāo)的所有漏洞的功能,需要按照 target_id 來在所有掃描目標(biāo)中搜索。AWVS 也提供了相應(yīng)的 API,我們需要用到的 API 為:/api/v1/vulnerabilities
?q=severity:{int};criticality:{int};status:{string};cvss_score:{logicexpression};cvss_score:{logicexpression};target_id:{target_id};group_id:{group_id}。請求方式為 GET。利用 target_id 搜索每個(gè)掃描目標(biāo)。這也解決了漏洞細(xì)節(jié)頁面的 URL 問題。當(dāng)使用 target_id 搜索掃描目標(biāo)成功時(shí)將會(huì)返回這個(gè)目標(biāo)的所搜漏洞信息,包括這個(gè)目標(biāo)包含的漏洞個(gè)數(shù)、每個(gè)漏洞的危險(xiǎn)等級、掃描時(shí)間、掃描類型、掃描狀態(tài)等信息。
具體實(shí)現(xiàn)步驟和添加掃描目標(biāo)大體相似,首先第一步使用 requests 來實(shí)現(xiàn) API 請求。核心代碼如下:
#獲取所有掃描目標(biāo)
response=requests.get(scan_api, self.auth_headers, False)
scan_response=response.json().get('scans')
for scan in scan_response:
scan['request_url'] = request_url
scan_list.append(scan)
return scan_list
#搜索狀態(tài)為“open“,對應(yīng)target_id的掃描目標(biāo)
vuln_search_api=f'{vuln_api}?q=status:{status};target_id:{target_id}'
try:
#使用get方式請求
response = requests.get(vuln_search_api, auth_headers, False)
#返回搜索結(jié)果目標(biāo)的所有漏洞信息
return response.text
except Exception:
return None
在 urls.py 中加入用戶訪問的 url ,這個(gè)需要提供一個(gè) target_id 方便后續(xù)功能的實(shí)現(xiàn),先獲取所有目標(biāo)的target_id,然后使用循環(huán)將所有 target_id 加入到 urlpatterns 列表中。因?yàn)樵?Django 中 views 函數(shù)通常只能使用一個(gè) request 參數(shù),由于這里需要將 target_id 傳入到 views 函數(shù)中,使用正則匹配的 “(?P<target_id>.*)$” 接收傳入的 target_id,在 views 里對應(yīng)函數(shù)的第二個(gè)形參名必須和 <> 里的值一致才有效。核心代碼如下:
path('vulnscan', views.vulnscan, name="vulnscan"),
for target_id in target_ids:
#使用正則匹配獲取第二個(gè)參數(shù):taget_id
urlpatterns.append(url(r'^vuln_result/(?P<target_id>.*)$', views.vuln_result, name='vuln_result/'+target_id))
在 views.py 里定義函數(shù) vulnscan(request) 獲取所有對應(yīng)的目標(biāo)漏洞信息。使用 API 得到返回的漏洞危險(xiǎn)等級、掃描目標(biāo)URL、每個(gè)漏洞唯一標(biāo)識的 vuln_id、掃描類型、掃描處理時(shí)間,API 返回的掃描處理時(shí)間不是標(biāo)準(zhǔn)的時(shí)間格式,使用正則匹配的方式,將其轉(zhuǎn)換為 “%Y-%m-%d %H:%M:%S“ 的格式,再定義函數(shù) vuln_result(request,target_id),根據(jù) target_id 獲取掃描目標(biāo)中所有漏洞信息,包括存在漏洞的URL、漏洞類型、狀態(tài)和處理時(shí)間等信息。核心代碼如下:
@login_required
def vuln_result(request, target_id):
d = Vuln(API_URL, API_KEY)
data = []
vuln_details = json.loads(d.search(None,None, "open", target_id=str(target_id)))
id = 1
for target in vuln_details['vulnerabilities']:
item={
'id': id,
'severity': target['severity'],
'target': target['affects_url'],
'vuln_id':target['vuln_id'],
'vuln_name': target['vt_name'],
'time': re.sub(r'T|\..*$', " ", target['last_seen'])
}
id += 1
data.append(item)
return render(request,'vuln-reslut.html',{'data': data})
在這個(gè)子功能中,前端的數(shù)據(jù)展示使用的是 Bootstrap Table。這個(gè)模板有很多實(shí)用的功能,比如表格的搜索功能、分頁展示功能等等,增加了用戶體驗(yàn)。表格的數(shù)據(jù)在 HTML 中使用雙花括號來接收,在 views.py 函數(shù)中返回的到相應(yīng)的 HTML 頁面時(shí),將 data 字典一起返回。這樣的返回方式可以將使用字典中的 key 值獲取對應(yīng)的 values 值。還可以是使用 if-else、for 等語句來分類展示數(shù)據(jù)。核心代碼如下:
{% for item in data %}
……………
# 這個(gè)只展示了掃描目標(biāo)列,其他列類似
<a href="/vuln_detail/{{ item.vuln_id }}"> {{ item.target }}</a>
……………
{% endfor %}
最后實(shí)現(xiàn)的效果如下圖所示,根據(jù)每個(gè)掃描狀態(tài)不同有不同的顯示,使用紅黃藍(lán)綠來分類高危、中危、低危、info等級的漏洞。最后展示了掃描的處理時(shí)間。
[圖片上傳失敗...(image-978c12-1636383910442)]
表格中掃描目標(biāo)列可以點(diǎn)擊進(jìn)入查看目標(biāo)的所有漏洞信息,如下圖所示,展示了特定的掃描目標(biāo)每個(gè)漏洞的危險(xiǎn)等級、存在漏洞的URL、漏洞的類型。
[圖片上傳失敗...(image-71ed88-1636383910442)]
獲取漏洞細(xì)節(jié)
在實(shí)現(xiàn)漏洞掃描和結(jié)果展示后,還需要獲取每個(gè)漏洞的細(xì)節(jié)。包括導(dǎo)致漏洞的請求參數(shù)、測試的 payload、數(shù)據(jù)請求包、簡要的修復(fù)建議等等。因?yàn)槊總€(gè)漏洞也存在一個(gè)唯一的標(biāo)識 vuln_id,可以根據(jù)這個(gè)值查詢指定漏洞的所有信息。使用的 API 為:/api/v1/vulnerabilities/{vuln_id},請求方式為 GET。
同樣地,首先使用 requests 來實(shí)現(xiàn) API 的調(diào)用,傳入 vuln_id 來查詢指定漏洞的所有信息,代碼如下:
#獲取指定漏洞的相關(guān)信息
def get(self, vuln_id):
vuln_get_api = f'{self.vuln_api}/{vuln_id}'
try:
#使用GET請求將vuln_id傳給API,結(jié)果以json格式返回
response = requests.get(vuln_get_api, auth_headers, False)
return response.json()
except Exception:
return None
在 urls.py 中添加漏洞細(xì)節(jié)的 url,這里與上一節(jié)展示掃描目標(biāo)的所有漏洞類似,都用正則匹配的形式接收 views.py里函數(shù)的第二個(gè)形參,但是這里不在使用 target_id 而是使用 vuln_id。代碼如下:
for vuln_id in vuln_ids:
urlpatterns.append(url(r'^vuln_detail/(?P<vuln_id>.*)$', views.vuln_detail, name='vuln_detail/' + vuln_id))
在 views.py 里面定義 vuln_details(request,vuln_id) 函數(shù),根據(jù) vuln_id 查詢指定漏洞的相關(guān)信息。該函數(shù)將 API 返回的值中有用的信息提取出來到字典 data 里,返回給 vuln-details.html 頁面,使用 雙花括號 來接收該漏洞的受影響的URL、處理時(shí)間、漏洞類型、漏洞測試參數(shù)、數(shù)據(jù)請求包、簡要的修復(fù)建議等信息。實(shí)現(xiàn)效果如下圖所示。
@login_required
def vuln_detail(request,vuln_id):
d = Vuln(API_URL,API_KEY)
data = d.get(vuln_id)
print(data)
parameter_list = BeautifulSoup(data['details'], features="html.parser").findAll('span')
request_list = BeautifulSoup(data['details'], features="html.parser").findAll('li')
data_dict = {
'affects_url': data['affects_url'],
'last_seen': re.sub(r'T|\..*$', " ", data['last_seen']),
'vt_name': data['vt_name'],
'details': data['details'].replace(" ",'').replace('</p>',''),
'request': data['request'],
'recommendation': data['recommendation'].replace('<br/>','\n')
}
try:
data_dict['parameter_name'] = parameter_list[0].contents[0]
data_dict['parameter_data'] = parameter_list[1].contents[0]
except:
pass
num = 1
try:
Str = ''
for i in range(len(request_list)):
Str += str(request_list[i].contents[0])+str(request_list[i].contents[1]).replace('<strong>', '').replace('</strong>', '')+'\n'
num += 1
except:
pass
data_dict['Tests_performed'] = Str
data_dict['num'] = num
data_dict['details'] = data_dict['details'].replace('class="bb-dark"','style="color: #ff0000"')
return render(request, "vuln-detail.html", {'data': data_dict})
[圖片上傳失敗...(image-6f7311-1636383910442)]
基于POC驗(yàn)證的中間件漏洞掃描
本系統(tǒng)使用POC腳本實(shí)現(xiàn)對一些中間件的漏洞掃描[7],包括Weblogic、Tomcat、Drupal、JBoss、Nexus、Struts2等等。通過每個(gè)漏洞的特點(diǎn),使用Python編寫不同的POC腳本驗(yàn)證目標(biāo)是否存在該漏洞。
首先這里的用戶界面和基于AWVS的漏洞掃描共用,單獨(dú)加入了中間件漏洞CVE編號的選擇。使用JavaScript發(fā)送用戶輸入的數(shù)據(jù)到后端。核心代碼如下:
#使用POST請求提交用戶的輸入
function get_Middleware_scan(ip , CVE_id) {
$.post('/Middleware_scan', {
ip: ip , #目標(biāo)URL
CVE_id: CVE_id #選擇的CVE編號
}, function (data) {
#處理返回結(jié)果
………
………});
}
將目標(biāo)添加到數(shù)據(jù)庫后,再查詢數(shù)據(jù)庫開始掃描,通過 ajax 來訪問 start_Middleware_scan 調(diào)用開始掃描的函數(shù),由于掃描時(shí)間可能會(huì)很長,需要設(shè)置足夠的 timeout 來等待掃描的結(jié)果返回。核心代碼如下:
$.ajax({
#使用POST請求發(fā)送目標(biāo)URL和CVE編號,設(shè)置超時(shí)為1秒
type: "POST",
url: '/start_Middleware_scan',
timeout: 10000,
data: {
ip: ip,
CVE_id: CVE_id
}
});
在 urls.py 里加入中間件漏洞掃描的訪問路徑,需要加入兩個(gè)路徑:’Middleware_scan‘,‘start_Middleware_scan’。前者是用戶添加掃描目標(biāo)時(shí)的路徑,用于接收用戶輸入的目標(biāo)和CVE編號之后將其插入數(shù)據(jù)庫。后者是將目標(biāo)插入數(shù)據(jù)庫之后,通過時(shí)間戳、狀態(tài)、目標(biāo) URL 以及 CVE 編號查詢出來開始掃描。當(dāng)掃描結(jié)束時(shí)更新數(shù)據(jù)庫中對應(yīng)掃描目標(biāo)的狀態(tài)。這樣的設(shè)計(jì)可以實(shí)時(shí)的看到掃描的狀態(tài)。
數(shù)據(jù)庫使用的是 Sqlite,在 models.py 里創(chuàng)建一個(gè)數(shù)據(jù)庫表 Middleware_vuln ,字段包括ID、目標(biāo)URL、狀態(tài)、結(jié)果、CVE編號、時(shí)間戳。在 Django 里定義這個(gè)類表示我們需要?jiǎng)?chuàng)建的數(shù)據(jù)庫,在 modles.py 里創(chuàng)建好之后,使用命令python manage.py makemigrations來記錄 modles.py 的所有改動(dòng),并且將這個(gè)改動(dòng)遷移到 migrations 這個(gè)文件下生成一個(gè)文件例如:0001文件,如果你接下來還要進(jìn)行改動(dòng)的話可能生成就是另外一個(gè)文件不一定都是0001文件,但是這個(gè)命令并沒有作用到數(shù)據(jù)庫,再使用命令 python manage.py migrate 將根據(jù)剛剛檢測到的改動(dòng)來創(chuàng)建數(shù)據(jù)庫表和字段屬性。核心代碼如下:
class Middleware_vuln(models.Model):
#類名為數(shù)據(jù)庫表名,變量名為字段名,字段屬性定義如下
id = models.AutoField(primary_key=True)
url = models.CharField(max_length=100, null=True)
status = models.CharField(max_length=20, null=True)
result = models.CharField(max_length=100, null=True)
CVE_id = models.CharField(max_length=100, null=True)
time = models.CharField(max_length=100, null=True, unique=True)
在添加目標(biāo)和開始檢測的時(shí)候我們需要插入數(shù)據(jù)庫和查詢數(shù)據(jù)庫,這里使用 Django 的函數(shù)來處理數(shù)據(jù)庫的增刪查改。對于 Middleware_vule 的插入使用 Middleware_vuln.objects.create(url, status, result, CVE_id, time),更新使用 Middleware_vuln.objects.filter(time).update(status, result)。還需要使用 try-except 來處理異常情況并打印出錯(cuò)信息。
def insert_Middleware_data(url, CVE_id, Time, result=None, status="runing"):
try:
Middleware_vuln.objects.create(url=url, status=status, result=result, CVE_id=CVE_id, time=Time)
print("insert success")
return True
except:
print("data insert error")
return False
def update_Middleware_data(url, CVE_id, Time, result):
try:
Middleware_vuln.objects.filter(url=url, status='runing', CVE_id=CVE_id, time=Time).update(status="completed", result=result)
print("update success")
except:
print("data updata error")
在views.py里定義 Middleware_scan() 獲取用戶輸入,并插入到數(shù)據(jù)庫中,其中時(shí)間戳 Time 為全局變量,作為后面開始掃描部分查詢數(shù)據(jù)庫的條件,在插入數(shù)據(jù)成功就返回 success(),否側(cè)返回 error(),這里返回的函數(shù)時(shí)返回的狀態(tài)碼,success()返回的是200,error()返回404,通過上面 JavaScrip t接收后做出判斷,并彈出相應(yīng)的提示框,核心代碼如下:
Time = 0.0
@csrf_exempt
@login_required
def Middleware_scan(request):
#使用POST請求獲取用戶輸入,并將其插入數(shù)據(jù)庫中。
#Time作為全局變量插入到數(shù)據(jù)庫中,作為查詢目標(biāo)信息的條件。
global Time
try:
url= request.POST.get('ip')
CVE_id = request.POST.get('CVE_id').replace('-',"_")
Time = time.time() # time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(t))時(shí)間戳轉(zhuǎn)日期格式
if insert_Middleware_data(url, CVE_id, Time):
return success()
except:
return error()
又定義函數(shù) start_Middleware_scan(),實(shí)現(xiàn)將數(shù)據(jù)庫中時(shí)間戳為 Time,狀態(tài)為 run 的目標(biāo)查詢出來,根據(jù) CVE編號調(diào)用對應(yīng)的 POC 腳本。最后更新數(shù)據(jù)庫的掃描結(jié)果和掃描狀態(tài),由于在上一步中將數(shù)據(jù)插入數(shù)據(jù)庫中可能會(huì)花費(fèi)一點(diǎn)時(shí)間,所以需要使用 sleep() 等待數(shù)據(jù)插入后再進(jìn)行查詢工作和掃描工作,保證不遺漏掃描目標(biāo)。
@csrf_exempt
@login_required
def start_Middleware_scan(request):
try:
url = request.POST.get('ip')
ip, port = urlparse(url).netloc.split(':')
CVE_id = request.POST.get('CVE_id').replace('-', "_")
time.sleep(5) #等待數(shù)據(jù)插入成功后在查詢出來掃描
msg = Middleware_vuln.objects.filter(url=url, status='runing', CVE_id=CVE_id, time=Time)
print(msg)
#掃描條目可能不止一條,需要使用循環(huán)來掃描
for target in msg:
result = POC_Check(target.url, target.CVE_id)
#將掃描結(jié)果和狀態(tài)更新
update_Middleware_data(target.url, target.CVE_id, Time, result)
return success()
except:
return error()
端口掃描
本系統(tǒng)端口掃描當(dāng)用戶指定了目標(biāo)IP地址后,系統(tǒng)正式工作,IP傳入后臺對目標(biāo)進(jìn)行掃描,掃描完成后將開放端口和對應(yīng)服務(wù)顯示到前端界面上。在“按端口分布劃分”和“按協(xié)議類型劃分”兩個(gè)欄目中對端口劃分進(jìn)行講解,使用戶免于查詢的繁瑣。同時(shí)該模塊還將內(nèi)置常見端口查詢表,在此可進(jìn)行端口及其對應(yīng)服務(wù)和功能的相關(guān)查詢和篩選,通過這一系列功能用戶能夠更加清晰的認(rèn)識到目標(biāo)主機(jī)開放了哪些服務(wù),以此來分析可能存在漏洞的服務(wù)。
設(shè)計(jì)思路
本系統(tǒng)端口掃描的實(shí)現(xiàn)方法是利用Python提供的庫函數(shù)Socket進(jìn)行調(diào)用,通過TCP三次握手與目標(biāo)計(jì)算機(jī)的端口建立連接。當(dāng)完成一次完整的三次握手時(shí),則可以推斷出端口和對應(yīng)服務(wù)是開放的,反之則沒有開放,為了提高了掃描的效率,本系統(tǒng)引入多線程掃描機(jī)制。
實(shí)現(xiàn)效果
[圖片上傳失敗...(image-594aa8-1636383910442)]
詳細(xì)實(shí)現(xiàn)
端口掃描
通過 Python 直接定義 socket,嘗試與目標(biāo)端口進(jìn)行連接。本程序中使用sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM);的方式進(jìn)行 TCP 連接,調(diào)用sock.connect_ex((ip,port)),來嘗試連接端口,如果端口開放則返回0,否則返回錯(cuò)誤代碼。使用try語句來捕獲異常,如果 socket 連接超時(shí),則返回異常處理信息。核心代碼如下:
def socket_scan(self, hosts):
'''端口掃描核心代碼'''
global PROBE
socket.setdefaulttimeout(1)
ip, port = hosts.split(':')
try:
if len(self.port) < 25:
# 創(chuàng)建套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# TCP/IP三次握手建立連接
result = sock.connect_ex((ip, int(port)))
# 調(diào)用socket.connect_ex((ip, port)),端口開放返回0,否則返回錯(cuò)誤代碼
# 實(shí)現(xiàn)和nmap的全連接掃描類似的功能。
if result == 0: # 成功建立TCP鏈接
self.port.append(port) # 結(jié)果集中增加端口
for i in PROBE: # 通過HTTP1.1刺探
sock.sendall(i.encode()) # 發(fā)送完整的TCP數(shù)據(jù)包
response = sock.recv(256) # 接受最大256byte
sock.close()
if response:
break
if response:
for pattern in SIGNS:
pattern = pattern.split(b'|')
if re.search(pattern[-1],response, re.IGNORECASE):
# 正則匹配banner信息與字典中的服務(wù)
proto = '{}:{}'.format(pattern[1].decode(), port)
self.out.append(proto) # 添加至輸出結(jié)果
break
else:
self.num = 1
except (socket.timeout, ConnectionResetError): # 異常處理
pass
except:
pass
如果這樣單線程(串行)阻塞運(yùn)行,會(huì)耗費(fèi)大量時(shí)間,因此,通過并發(fā)的方式,并發(fā)請求,提升掃描速度,通過對比掃描300個(gè)端口單線程需要30s左右,多線程僅需10s左右。
本端口掃描功能中采用了并發(fā)64條線程來進(jìn)行掃描,因此,在定義run方法時(shí),每個(gè)線程掃描的兩個(gè)端口號間差數(shù)為64,在程序中使用 concurrent.futures 來實(shí)現(xiàn)。concurrent.futures 模塊提供了一個(gè)高水平的接口用于異步執(zhí)行調(diào)用。異步執(zhí)行可以使用線程實(shí)現(xiàn),使用 ThreadPoolExecutor,或者獨(dú)立的進(jìn)程,使用 ProcessPoolExecutor 實(shí)現(xiàn)。兩者都實(shí)現(xiàn)相同接口,都是由抽象 Executor 類定義的。
THREADNUM = 64 # 線程數(shù)
def run(self, ip): #多線程掃描
hosts = []
global PORTS, THREADNUM
for i in PORTS:
hosts.append('{}:{}'.format(ip, i))
try:
with concurrent.futures.ThreadPoolExecutor(
max_workers=THREADNUM) as executor:
executor.map(self.socket_scan, hosts)
except EOFError:
pass
端口查詢表
端口查詢表功能通過收集網(wǎng)上端口信息建立端口查詢庫,涉及到的端口數(shù)據(jù)是存儲在數(shù)據(jù)庫中,包括端口ID、端口號、端口對應(yīng)服務(wù)、端口對應(yīng)協(xié)議和端口所屬狀態(tài)。端口查詢表結(jié)構(gòu)如下表所示。
| 字段名 | 字段類型 | 允許空 | 是否主鍵 | 備注 |
|---|---|---|---|---|
| id | integer | Not null | True | 端口ID |
| num | bigint | Not null | False | 端口號 |
| service | text | Not null | False | 端口對應(yīng)服務(wù) |
| protocol | Varchar(20) | Not null | False | 端口對應(yīng)協(xié)議 |
| status | Varchar(10) | Not null | False | 端口所屬狀態(tài) |
端口查詢數(shù)據(jù)庫采用Django Model進(jìn)行建立,字段包含端口號、服務(wù)、協(xié)議、和狀態(tài)。實(shí)現(xiàn)代碼如下:
class PortList(models.Model):
'''端口查詢表'''
num=models.BigIntegerField(verbose_name='端口號')
service=models.TextField(max_length=100,verbose_name='服務(wù)')
protocol=models.CharField(max_length=20,verbose_name='
協(xié)議',blank=True,default='未知')
status=models.CharField(max_length=10,verbose_name='
狀態(tài)',blank=True,default='未知')
class Meta:
# 后臺表頭設(shè)置
verbose_name=verbose_name_plural='端口列表'
數(shù)據(jù)庫建立后需要進(jìn)行后臺注冊,這樣可以進(jìn)入后臺對數(shù)據(jù)進(jìn)行管理,實(shí)現(xiàn)代碼如下:
@admin.register(PortList)
class PortListAdmin(ImportExportModelAdmin):
# 設(shè)置哪些字段顯示在后臺
list_display = ('num', 'service', 'protocol', 'status',)
# 設(shè)置num字段進(jìn)入編輯界面
list_display_links = ('num',)
search_fields = ('num', 'service',)
# 過濾器,按字段進(jìn)行篩選
list_filter = ('protocol','status')
# 設(shè)置num為默認(rèn)排序字段
ordering = ('num', )
list_per_page = 15 #設(shè)置每頁顯示數(shù)據(jù)條數(shù)
指紋識別
該模塊采用提取指紋特征碼特征信息來識別Web指紋,系統(tǒng)通過構(gòu)造大量特殊的HTTP請求與Web服務(wù)器交互,從其響應(yīng)數(shù)據(jù)包信息中提取提取指紋特征信息,然后通過與指紋數(shù)據(jù)庫進(jìn)行比對,從而獲取到Web服務(wù)器及應(yīng)用的組件信息和版本信息。通過發(fā)現(xiàn)這些特征信息并對它進(jìn)行識別可以幫助我們快速地制定滲透策略,是滲透環(huán)節(jié)中關(guān)鍵的一步。
設(shè)計(jì)思路
國內(nèi)外對Web服務(wù)器及應(yīng)用指紋的研究,主要都是通過構(gòu)造大量特殊的HTTP請求與Web服務(wù)器交互,從其響應(yīng)數(shù)據(jù)包信息中提取提取指紋特征信息,然后通過與指紋數(shù)據(jù)庫進(jìn)行比對,從而獲取到Web服務(wù)器及應(yīng)用的組件信息和版本信息。本文采用基于關(guān)鍵字特征匹配的方法實(shí)現(xiàn)指紋識別功能,為了使檢測結(jié)果更加準(zhǔn)確,對比網(wǎng)上一些主流的指紋數(shù)據(jù)庫,對本系統(tǒng)的數(shù)據(jù)庫進(jìn)行了一系列優(yōu)化。
實(shí)現(xiàn)效果
[圖片上傳失敗...(image-360594-1636383910442)]
詳細(xì)實(shí)現(xiàn)
指紋識別流程中最關(guān)鍵的就是提取指紋特征這一步驟。提取指紋特征首先需要確定應(yīng)該提取響應(yīng)數(shù)據(jù)報(bào)文中的哪些數(shù)據(jù)。因此需要設(shè)計(jì)特征提取算法對響應(yīng)數(shù)據(jù)報(bào)文進(jìn)行分析,響應(yīng)數(shù)據(jù)包是由響應(yīng)行、響應(yīng)頭、響應(yīng)體三部分構(gòu)成。響應(yīng)行由HTTP版本、狀態(tài)碼、狀態(tài)碼描述構(gòu)成。響應(yīng)頭用于指示客戶端如何處理響應(yīng)體,響應(yīng)頭里面包含很多的組件信息,用于告訴瀏覽器響應(yīng)的類型、字符編碼服務(wù)器類型和字節(jié)大小等信息。響應(yīng)體則是服務(wù)器根據(jù)客戶端的請求返回給客戶端的具體數(shù)據(jù)。響應(yīng)頭和響應(yīng)體中包含了能夠識別Web指紋組件的字段內(nèi)容,因此,對響應(yīng)頭和響應(yīng)體中關(guān)鍵字段的提取,是實(shí)現(xiàn)指紋識別技術(shù)的核心。
指紋識別技術(shù)分為信息收集階段和Web指紋識別階段。
(I)信息收集階段:通過用戶輸入的URL,收集Web應(yīng)用的特定字段信息,返回頁面關(guān)鍵字或者特殊文件和路徑等這些特征。收集的關(guān)鍵數(shù)據(jù)越多對接下來的指紋識別結(jié)果越準(zhǔn)確。
(2)Web指紋識別階段:該階段包含兩部分,一部分是指紋庫的建立,該部分主要負(fù)責(zé)從已知的Web應(yīng)用程序中收集特征信息,并且建立指紋特征庫;本文通過分析HTTP響應(yīng)數(shù)據(jù)報(bào)文,設(shè)計(jì)了網(wǎng)站指紋的提取規(guī)則,通過分析響應(yīng)頭字段和響應(yīng)體內(nèi)容構(gòu)建了一個(gè)指紋組件信息庫,信息庫采用JSON格式進(jìn)行存儲。指紋信息是從Wappalyzer和FOFA等平臺上進(jìn)行收集歸納的。另一部分從待測的Web應(yīng)用程序中收集特征信息,并與指紋特征庫中的數(shù)據(jù)進(jìn)行比較,從而識別出待測的web應(yīng)用程序。
目錄掃描
目錄識別參照dirsearch實(shí)現(xiàn),包含php、asp、jsp等網(wǎng)站類型進(jìn)行掃描,還設(shè)置了遞歸掃描和自定義掃描選項(xiàng)。支持自定義前后綴或者子目錄。
設(shè)計(jì)思路
Dirsearch 掃描的結(jié)果通過 JSON的格式保存在對應(yīng)的路徑下,因此我們可以減輕對數(shù)據(jù)庫的依賴。獲取的數(shù)據(jù)被分成 URL 和 TIMR,URL下又分為 content-length、path、redirect、starus四個(gè)部分。因?yàn)樵?JSON 格式中被不同類型括號的數(shù)據(jù)會(huì)被 Django 解析為列表、字典等格式,因此我們需要對獲得的 JSON 數(shù)據(jù)進(jìn)行處理,將其轉(zhuǎn)換為 Django 可以識別的數(shù)據(jù),使之在前端進(jìn)行讀取。
要正確解析數(shù)據(jù),需要先理解 Python 數(shù)據(jù)結(jié)構(gòu)和 JSON 數(shù)據(jù)的轉(zhuǎn)換方法。我們基于當(dāng)前的固定的 JSON 格式去解析取得的數(shù)據(jù)。
實(shí)現(xiàn)效果
[圖片上傳失敗...(image-12bb38-1636383910442)]
[圖片上傳失敗...(image-53a734-1636383910442)]
信息泄露
該模塊主要為用戶提供常見的信息泄露檢查。在前端直觀的展示后臺地址、配置文件等可能存在泄露的信息,在結(jié)果列表中,用戶可以非常清晰的知道某個(gè)Web服務(wù)存在的信息泄露問題。
[圖片上傳失敗...(image-64c550-1636383910442)]
旁站探測
該模塊主要對通過 IP 地址,直接獲取與當(dāng)前 IP 所在服務(wù)器上的其它網(wǎng)站, 本模塊直接調(diào)用 api 實(shí)現(xiàn)
[圖片上傳失敗...(image-419b60-1636383910442)]
域名探測
該模塊主要通過調(diào)用 api 來掃描網(wǎng)站的子域名
[圖片上傳失敗...(image-938d6b-1636383910443)]
安全導(dǎo)航
安全導(dǎo)航頁面的靈感來自于Viggo大佬開發(fā)的Webstack項(xiàng)目,該項(xiàng)目是基于Bootstrap開發(fā)的純前端頁面,因此前端我沿用了Webstack 的風(fēng)格并融合了Tabler UI風(fēng)格,并用 Django寫了后臺管理,可在線對分類和條目進(jìn)行管理。
前端頁面
[圖片上傳失敗...(image-89ad32-1636383910443)]
后臺管理頁面
[圖片上傳失敗...(image-e10e11-1636383910443)]
數(shù)據(jù)庫設(shè)計(jì)
[圖片上傳失敗...(image-7cee4a-1636383910443)]
導(dǎo)航條目-Item
標(biāo)題title 描述desc 網(wǎng)址url 分類category(外鍵) 圖片img 圖片寬度img_width
class Item(models.Model):
'''導(dǎo)航條目'''
title = models.CharField(max_length=50,verbose_name='名稱')
desc = models.TextField(max_length=100,verbose_name='描述')
url = models.URLField(verbose_name='網(wǎng)址',blank=True)
img = models.URLField(default='https://jwt1399.top/favicon.png',verbose_name='logo')
img_width = models.IntegerField(default=45, verbose_name='圖片寬度')
category = models.ForeignKey(Category, blank=True, null=True, verbose_name='分類', on_delete=models.CASCADE)
class Meta:
verbose_name=verbose_name_plural='導(dǎo)航條目'
#后臺條目圖片預(yù)覽
def img_admin(self):
return format_html( '<img src="{}" width="50px" height="50px" style="border-radius: 50%;" />',self.img,)
img_admin.short_description = 'logo預(yù)覽'
def __str__(self):
return self.title
條目分類-Category
名稱name 排序 sort 是否添加到導(dǎo)航欄add_menu 圖標(biāo)icon
class Category(models.Model):
"""條目分類"""
name = models.CharField(max_length=20, verbose_name='名稱')
sort = models.IntegerField(default=1, verbose_name='顯示順序')
add_menu = models.BooleanField(default=True, verbose_name='添加到導(dǎo)航欄')
icon = models.CharField(max_length=30, default='fas fa-home',verbose_name='圖標(biāo)')
class Meta:
verbose_name_plural=verbose_name = '分類'
#統(tǒng)計(jì)分類對應(yīng)條目數(shù),并放入后臺
def get_items(self):
return len(self.item_set.all())
get_items.short_description = '條目數(shù)' # 設(shè)置后臺顯示表頭
#后臺圖標(biāo)預(yù)覽
def icon_data(self):#引入Font Awesome Free 5.11.1
return format_html('<h1><i class="{}"></i></h1>',self.icon) #轉(zhuǎn)化為<i class="{self.icon}"></i>
icon_data.short_description = '圖標(biāo)預(yù)覽'
def __str__(self):
return self.name
文檔頁
將 Docsify 直接嵌入了 Django 中構(gòu)造了文檔頁。
[圖片上傳失敗...(image-962fc8-1636383910443)]
兼容性
Phone端
| 豎屏 | 橫屏 |
|---|---|
| [圖片上傳失敗...(image-8b2350-1636383910443)] | [圖片上傳失敗...(image-7d931a-1636383910443)] |
Pad端
| 豎屏 | 橫屏 |
|---|---|
| [圖片上傳失敗...(image-cc8da8-1636383910443)] | [圖片上傳失敗...(image-d09548-1636383910443)] |
版本變更記錄
v2.7(2021-04-18)
- 新增
域名探測功能; - 新增
中間件漏洞掃描; - 修復(fù)
忘記密碼功能; - 優(yōu)化AWVS未啟動(dòng)報(bào)錯(cuò)信息;
- 優(yōu)化
用戶登錄邏輯; - 優(yōu)化
漏掃詳情頁UI; - 優(yōu)化導(dǎo)航欄布局;
- 優(yōu)化若干小細(xì)節(jié);
v2.6(2021-03-31)
- 新增漏洞
掃描詳情功能; - 新增首頁
儀表盤; - 安全導(dǎo)航頁導(dǎo)航欄
移動(dòng)端優(yōu)化; - 安全導(dǎo)航頁目錄欄
縮放優(yōu)化; - 注冊&登錄界面優(yōu)化;
- 文檔頁
導(dǎo)航欄優(yōu)化; - 新增
UI夜間模式; - 修復(fù)若干
UI顯示Bug;
v2.5(2021-03-02)
- 新增了
漏洞掃描功能; - 端口掃描頁新增
常見端口查詢表; - 信息泄露頁新增
常見信息泄露列表; - 指紋識別頁新增
數(shù)據(jù)分析圖表; - 漏洞掃描頁
界面優(yōu)化;
v2.4(2021-02-22)
- 新增了
目錄識別功能; - 重寫
歡迎頁; - 安全導(dǎo)航頁
移動(dòng)端界面適配; - 安全導(dǎo)航頁
UI優(yōu)化; - 目錄識別頁
界面優(yōu)化; - 指紋識別頁新增
常見指紋顯示與搜索; - 引入
Boostrap Table實(shí)現(xiàn)分頁; - 淘汰
LayUI改用Layer進(jìn)行彈窗; - 文檔頁增加
導(dǎo)航欄;
v2.3(2021-02-08)
- 全新的
頁面布局; - UI適配
移動(dòng)端; - 優(yōu)化
導(dǎo)航頁布局; - 優(yōu)化一系列
UI顯示問題;- 優(yōu)化了
手機(jī)端頁腳顯示 - 優(yōu)化了
平板端導(dǎo)航條顯示 - 頁面底部增加
回到頂部按鈕 - 按鈕
觸發(fā)跳轉(zhuǎn)頁面相對位置 -
回車鍵觸發(fā)查詢按鈕 - 優(yōu)化
導(dǎo)航頁頁腳顯示
- 優(yōu)化了
v2.2 (2021-02-03)
- 新增了
信息泄露探測功能; - 新增了
旁站探測功能; - 新增了導(dǎo)航頁
數(shù)據(jù)分析功能; - 新增了基于
Docsify的文檔頁; - 重構(gòu)了
靜態(tài)文件static文件結(jié)構(gòu) - 優(yōu)化了項(xiàng)目
文件結(jié)構(gòu); - 美化了
注冊頁面; - 引入了
動(dòng)態(tài)粒子背景效果; - 修復(fù)了一些
UI顯示問題;
v2.1 (2021-01-13)
- 新增了
指紋識別功能; - 新增了
登錄和注冊功能功能; - 新增了
歡迎頁; 新增了文檔頁;- 修復(fù)了一些
UI顯示問題;
v2.0(2021-01-04)
- 新增了
端口掃描功能; - 新增了
安全導(dǎo)航功能; - 連入了
SQLite數(shù)據(jù)庫,后續(xù)考慮改為MySQL; - 修復(fù)了一些
UI顯示問題; - 修復(fù)了后臺頭部小圖標(biāo)無法顯示問題;
- 新增了后臺數(shù)據(jù)導(dǎo)入導(dǎo)出功能;
v1.0(2020-12-20)
- 基于
Tabler框架構(gòu)造了前端頁面; - 采用基于
Python的 Django 框架編寫后端; - 引入了
SimpleUi美化 Django 后臺框架; - 引入了
Font-Awsome 5.15.1圖標(biāo);
項(xiàng)目部署
本地部署
1.使用 Pycharm 打開本項(xiàng)目,在 Pycharm 的 setting--->Project Interpreter中 add 一個(gè)新的虛擬環(huán)境。
2.在該虛擬環(huán)下打開 Pycharm 自帶 Terminal 運(yùn)行 pip install -r requirements.txt 下載項(xiàng)目所需的第三方包。
3.現(xiàn)在項(xiàng)目已經(jīng)可以正常運(yùn)行了,但是想要使用漏洞掃描功能,需要安裝AWVS,并在項(xiàng)目的setting.py 中配置 AWVS的 API URL和API KEY
4.忘記密碼功能相關(guān)配置在項(xiàng)目的setting.py 中修改
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25 # 發(fā)件箱的smtp服務(wù)器端口
EMAIL_HOST_USER = 'xxx' # 你的郵箱賬號
EMAIL_HOST_PASSWORD ="xxx" # 郵箱授權(quán)碼
EMAIL_USE_TLS = True # 這里必須是 True,否則發(fā)送不成功
EMAIL_FROM = 'xxx' # 你的郵箱賬號
DEFAULT_FROM_EMAIL = 'xxx' # 你的郵箱賬號
5.創(chuàng)建超級管理員 python manage.py createsuperuser
服務(wù)器部署
TO DO
不論是開發(fā)還是安全感覺都有很長的路要走,路漫漫其修遠(yuǎn)兮,吾將上下而求索,共勉 !
- 安全工具頁
- 安全圖書頁
- 引入MySQL數(shù)據(jù)庫
- 掃描算法優(yōu)化
- 代碼變量、數(shù)據(jù)庫結(jié)構(gòu)優(yōu)化
- 漏洞報(bào)告導(dǎo)出功能
- 頁面異步刷新