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