基於Python的nessus API簡析——登入&進行一次掃描
前言
最近在開發一個基於Nessus的自動化漏掃工具,來和大家分析一下關於Nessus API的使用心得。
Nessus提供了非常完善的API,可以幫助我們實現很多事情,無論是對接其他運維繫統,還是用來編寫自動化的漏掃工具都十分方便。
Nessus為這些api提供了詳細的文件,你可以在Settings->My Account->API Keys->API documentation裡看到。
一、登入
無論是發起一次掃描,還是獲取掃描的結果,首先都需要進行登入,nessus在這裡提供了兩種登入方式。相關內容可以在API文件的https://localhost:8834/api#/resources/session
1、session create
第一種方式是使用Nessus的使用者名稱密碼,使用/session介面:
POST /session
向/session介面傳送post請求,請求的payload引數為使用者名稱和密碼。程式碼如下:
# coding=utf-8
import requests
import json
def login_create():
# 登入成功則返回token,否則返回空
token = ''
# 呼叫/session介面
url = "https://localhost:8834/session"
# nessus的使用者名稱密碼作為post的payload
data = {
'username': 'admin',
'password': 'admin'
}
# 傳送請求
respon = requests.post(url, data=data, verify=False)
# 如果請求成功則返回token值
if respon.status_code == 200:
# 返回值是一個json字串,用json.loads解析成字典取值
token = json.loads(respon.text)['token']
return token
若使用者名稱和密碼正確,/session介面會返回一個token,將這個token放入請求的頭資訊中,之後的請求中帶上這個頭資訊就可以使用了。舉一個例子:
def get_scans_list():
# 返回結果
result = ''
# 首先獲取一下token
token = login_create()
if token != '':
# 呼叫/folders介面
url = "https://localhost:8834/scans"
# 組裝一個請求的頭,把剛剛拿到的token放入請求頭
header = {'X-Cookie': 'token={token};'.format(token=token),
'Content-type': 'application/json',
'Accept': 'text/plain'}
respon = requests.get(url, headers=header, verify=False)
# 請求成功,則返回結果,否則返回空值
if respon.status_code == 200:
result = json.loads(respon.text)
return result
2、API Key
除了使用使用者名稱密碼的方式,Nessus還提供了API key的方式,只需將Nessus生成的accessKey和secretKey放入請求頭資訊中即可。之後的例子都會使用API key的方式來呼叫。
API key可以通過Settings->My Account->API Keys->Generate獲取
還是用get_scans_list做例子:
def get_scans_list():
# 返回結果
result = ''
# 呼叫/folders介面
url = "https://localhost:8834/scans"
# Nessus生成的API key
accesskey = 'b901cfe7e184801952db08795fa23f48f1f6c50732f0765e229975354aa58f92'
secretkey = '860ad22d0536dd8fd88d3f93901d78cfe4cbc4dbd37bc17ba72a7e989f160739'
# 組裝請求頭
header = {'X-ApiKeys': 'accessKey={accesskey};secretKey={secretkey}'.format(accesskey=accesskey, secretkey=secretkey),
'Content-type': 'application/json',
'Accept': 'text/plain'}
respon = requests.get(url, headers=header, verify=False)
# 請求成功,則返回結果,否則返回空值
if respon.status_code == 200:
result = json.loads(respon.text)
return result
二、開始一次掃描
1、定位掃描任務
發起掃描的API介面為:
POST /scans/{scan_id}/launch
可以看到,進行掃描需要提供一個scan_id。如何獲取scan_id呢?
這裡給大家分享一種利用任務名獲取scan_id的方法。
首先,利用介面/scans介面獲取一個所有任務的列表,可以參看剛才的get_scans_list()函式,這裡就不重複了,讓我們看一下它的返回值,可以看到返回值中有一個sacns的字典列表, 裡面有name和id這兩個欄位
{
"folders": [
folder Resource
],
"scans": [
{
"id": {integer},
…
"name": {string},
…
}
],
"timestamp": {integer}
}
def get_scan_id(scanname):
# 任務ID
scan_id = 0
# 獲取任務列表
scans_list = get_scans_list()['scans']
if scans_list != '':
# 遍歷任務列表
for scan in scans_list:
# 判斷是否是指定的任務
if scan['name'] == scanname:
scan_id = scan['id']
break
# 如果未找到,則返回0
return scan_id
由於不同的資料夾下允許存在同名的任務,所以為了更精確的定位任務,也可以加入folder_id來查詢,呼叫介面/scan時加入查詢字串folder_id=3即可。folder_id也可以用和scan_id相同的方法獲取,大家可以自己試一下。
2、發起任務
有了scan_id,我們就可以使用/scans/{scan_id}/launch介面來發起一個任務了。
另外這個介面需要用POST方法呼叫,掃描的目標需要放在請求的payload裡。程式碼如下:
# iplist為掃描目標IP的列表
def scan_luanch(iplist):
# header資訊在全域性變數中,之後的例項中將不再重複
# 呼叫/scans/{scan_id}/launch
url = 'https://localhost:8834/scans/{scan_id}/launch'.format(scan_id=get_scan_id('default'))
# 掃描目標放在請求的payload裡
data = {
'alt_targets': iplist
}
# 傳送請求
respon = requests.post(url, headers=header, data=data, verify=False)
# 是否請求成功
if respon.status_code == 200:
return True
else:
return False
我們可以呼叫一下scan_launch()看看效果
scan_luanch(['1.1.1.0', '1.1.1.1'])
可以看到我們已經成功的發起了一次任務了。
至此我們就完成了登入和發起任務的工作,之後還有許多內容可以完善,例如監聽任務狀態,獲取任務結果,以及動態建立任務等,將在後續的文章中為大家分享。