深度調查CVE-2015-5477&CloudFlare Virtual DNS如何保護其使用者
上週,ISC 釋出補丁,修復了BIND9 DNS伺服器中的一個遠端可利用漏洞。這個漏洞會導致伺服器在處理某個資料包時發生崩潰。
公告中指出道,伺服器在處理TKEY型別的查詢時出現了一個錯誤,這個錯誤導致assertion fail,而這個fail又造成了伺服器的崩潰。因為assertion是在查詢解析的過程出現的,所以這個問題無法避免:伺服器在接收到資料包時,首先要做的就是解析這個查詢,然後再根據需要做出相應的決定。
TSIG是DNS伺服器使用的一個驗證彼此的協議。在這個協議的上下文中用到了TKEY 查詢。不同於常規的DNS查詢,在TKEY查詢的資訊中,有一個EXTRA/ADDITIONAL節,在這個節中包含有關於TKEY型別的“meta”記錄。
因為現在利用資料包已經公開了,所以我覺著我們可以研究一下這個漏洞程式碼。那我們就看看這個崩潰例項的輸出結果:
03-Aug-2015 16:38:55.509 message.c:2352: REQUIRE(*name == ((void*)0)) failed, back trace
03-Aug-2015 16:38:55.510 #0 0x10001510d in assertion_failed()+0x5d
03-Aug-2015 16:38:55.510 #1 0x1001ee56a in isc_assertion_failed()+0xa
03-Aug-2015 16:38:55.510 #2 0x1000bc31d in dns_message_findname()+0x1ad
03-Aug-2015 16:38:55.510 #3 0x10017279c in dns_tkey_processquery()+0xfc
03-Aug-2015 16:38:55.510 #4 0x100016945 in ns_query_start()+0x695
03-Aug-2015 16:38:55.510 #5 0x100008673 in client_request()+0x18d3
03-Aug-2015 16:38:55.510 #6 0x1002125fe in run()+0x3ce
03-Aug-2015 16:38:55.510 exiting (due to assertion failure)
[1] 37363 abort (core dumped) ./bin/named/named -f -c named.conf
上面的崩潰程式碼對我們啟示很大,它告訴我們這是由assertion失敗導致的崩潰,並且告訴我們出現問題的地方在message.c:2352. 下面是漏洞程式碼摘要:
// https://source.isc.org/git/bind9.git -- faa3b61 -- lib/dns/message.c
isc_result_t
dns_message_findname(dns_message_t *msg, dns_section_t section,
dns_name_t *target, dns_rdatatype_t type,
dns_rdatatype_t covers, dns_name_t **name,
dns_rdataset_t **rdataset)
{
dns_name_t *foundname;
isc_result_t result;
/*
* XXX These requirements are probably too intensive, especially
* where things can be NULL, but as they are they ensure that if
* something is NON-NULL, indicating that the caller expects it
* to be filled in, that we can in fact fill it in.
*/
REQUIRE(msg != NULL);
REQUIRE(VALID_SECTION(section));
REQUIRE(target != NULL);
if (name != NULL)
==> REQUIRE(*name == NULL);
[...]
這裡,我們找到了一個函式"dns_message_findname
",這個函式的作用是根據message section中給定的名稱和型別,查詢具有相同名稱和型別的RRset。這個函式應用了一個很常見的C API:來獲取結果,在結果中填充著caller傳遞的指標 (dns_name_t **name, dns_rdataset_t **rdataset
)。
很諷刺的是,這些指標的驗證過程真的非常嚴格:如果這些指標沒有指向(dns_name_t *)NULL,REQUIRE assertion
就會fail並且伺服器就會崩潰,也不會嘗試恢復。呼叫這個函式的程式碼必須要格外小心地把指標傳遞到NULL dns_name_t *
,函式會填充到程式碼中返回找到的名稱。
在非記憶體安全語言中,當assertion是無效的時候,崩潰就經常出現。因為當出現了異常時,程式很可能就沒辦法來清理自身的記憶體了。
所以,在繼續調查中,我們透過棧來查詢非法呼叫。接下來就是dns_tkey_processquery
,下面是簡化摘要:
// https://source.isc.org/git/bind9.git -- faa3b61 -- lib/dns/tkey.c
isc_result_t
dns_tkey_processquery(dns_message_t *msg, dns_tkeyctx_t *tctx,
dns_tsig_keyring_t *ring)
{
isc_result_t result = ISC_R_SUCCESS;
dns_name_t *qname, *name;
dns_rdataset_t *tkeyset;
/*
* Interpret the question section.
*/
result = dns_message_firstname(msg, DNS_SECTION_QUESTION);
if (result != ISC_R_SUCCESS)
return (DNS_R_FORMERR);
qname = NULL;
dns_message_currentname(msg, DNS_SECTION_QUESTION, &qname);
/*
* Look for a TKEY record that matches the question.
*/
tkeyset = NULL;
name = NULL;
result = dns_message_findname(msg, DNS_SECTION_ADDITIONAL, qname,
dns_rdatatype_tkey, 0, &name, &tkeyset);
if (result != ISC_R_SUCCESS) {
/*
* Try the answer section, since that's where Win2000
* puts it.
*/
if (dns_message_findname(msg, DNS_SECTION_ANSWER, qname,
dns_rdatatype_tkey, 0, &name,
&tkeyset) != ISC_R_SUCCESS) {
result = DNS_R_FORMERR;
tkey_log("dns_tkey_processquery: couldn't find a TKEY "
"matching the question");
goto failure;
}
}
[...]
這裡有兩個dns_message_findname
呼叫,因為我們尋找的是傳遞惡意name的一個呼叫,所以我們可以忽略掉第一個呼叫了,因為前面寫著name = NULL
;
第二個呼叫就比較有意思了。在先呼叫了dns_message_findname
之後,呼叫又重新使用了相同的dns_name_t *name
,而且也沒有把它設定成NULL。這可能就是bug出現的地方了。
現在的問題是,什麼時候dns_message_findname
會設定name
,而不返回ISC_R_SUCCESS呢(這樣的話if條件就能滿足了)?現在,我們一起看一看完整的函式主體。
// https://source.isc.org/git/bind9.git -- faa3b61 -- lib/dns/message.c
isc_result_t
dns_message_findname(dns_message_t *msg, dns_section_t section,
dns_name_t *target, dns_rdatatype_t type,
dns_rdatatype_t covers, dns_name_t **name,
dns_rdataset_t **rdataset)
{
dns_name_t *foundname;
isc_result_t result;
/*
* XXX These requirements are probably too intensive, especially
* where things can be NULL, but as they are they ensure that if
* something is NON-NULL, indicating that the caller expects it
* to be filled in, that we can in fact fill it in.
*/
REQUIRE(msg != NULL);
REQUIRE(VALID_SECTION(section));
REQUIRE(target != NULL);
if (name != NULL)
REQUIRE(*name == NULL);
if (type == dns_rdatatype_any) {
REQUIRE(rdataset == NULL);
} else {
if (rdataset != NULL)
REQUIRE(*rdataset == NULL);
}
result = findname(&foundname, target,
&msg->sections[section]);
if (result == ISC_R_NOTFOUND)
return (DNS_R_NXDOMAIN);
else if (result != ISC_R_SUCCESS)
return (result);
if (name != NULL)
*name = foundname;
/*
* And now look for the type.
*/
if (type == dns_rdatatype_any)
return (ISC_R_SUCCESS);
result = dns_message_findtype(foundname, type, covers, rdataset);
if (result == ISC_R_NOTFOUND)
return (DNS_R_NXRRSET);
return (result);
}
你能發現,dns_message_findname
首先使用了findnamet
來匹配與目標名稱一致的記錄,然後用dns_message_findtype
來匹配目標型別。在這兩個呼叫之間... *name = foundname
!即使dns_message_findname
在 DNS_SECTION_ADDITIONAL
中找到了name == qname
的一條記錄,但是型別不是dns_rdatatype_tkey
這個name
也會被填充並返回失敗。 第二個dns_message_findname
呼叫會觸發惡意 name
,然後就一發不可收拾了。
的確,補丁只是在第二個呼叫前新增了 name = NULL
(不,我們的出發點不是補丁程式,不然還有什麼意思)
diff --git a/lib/dns/tkey.c b/lib/dns/tkey.c
index 66210d5..34ad90b 100644
--- a/lib/dns/tkey.c
+++ b/lib/dns/tkey.c
@@ -654,6 +654,7 @@ dns_tkey_processquery(dns_message_t *msg, dns_tkeyctx_t *tctx,
* Try the answer section, since that's where Win2000
* puts it.
*/
+ name = NULL;
if (dns_message_findname(msg, DNS_SECTION_ANSWER, qname,
dns_rdatatype_tkey, 0, &name,
&tkeyset) != ISC_R_SUCCESS) {
讓我們再看一下bug觸發的流程:
- 收到一個TKEY型別查詢,呼叫dns_tkey_processquery來解析這個查詢
- 在EXTRA節中找到與查詢名稱相同的記錄,導致填充了
name
,但是這條記錄並不是一個TKEY記錄,導致result != ISC_R_SUCCESS
- 再次呼叫
dns_message_findname
在ANS節中查詢,現在是透過惡意的name
參考 - assertion *name != NULL fail,
BIND崩潰
@jfoote_透過 american fuzzy lop 模糊測試工具發現了這個bug。模糊測試工具是一個自動工具,能自動向目標程式不斷地提交異常輸入,直至程式崩潰。你可以透過TKEY 查詢+ 非TKEY EXTRA RR的組合來看看伺服器最終是怎樣崩潰的,並找到這個bug。
Virtual DNS使用者是安全的
好訊息! CloudFlare Virtual DNS使用者的BIND伺服器不會受到這次攻擊的影響。如果需要的話,我們的自定義Go DNS伺服器-PRDNS會首先解析所有的查詢,並“消毒”,然後才會把查詢轉發回原來的伺服器。
因為Virtual DNS並不支援TSIG和TKEY(用於認證伺服器到伺服器之間的流量,並不是遞迴查詢),所以沒有必要在查詢中轉發EXTRA節的記錄,Virtual DNS也沒有這樣做。這樣面臨的攻擊風險就小了很多,而且也無法透過Virtual DNS來利用這個漏洞。
現在還沒有什麼辦法能防禦這個漏洞:PRDNS總是會驗證進入的資料包是不是良性的,確保查詢是正常的,並且簡化成最簡單的形式,接著才會轉發。
相關文章
- DNS伺服器保護方法:幾點保護DNS伺服器的有效方法小結2019-01-22DNS伺服器
- 【系列筆記】保護小區的調查、監測和巡護?(二)2016-03-22筆記
- 皮尤研究中心:調查發現智慧手機使用者更希望得到隱私保護2012-09-07
- http代理是如何保護使用者隱私的?2021-12-29HTTP
- 深度理解 Virtual DOM2017-04-18
- 微軟調查顯示:約40%的受訪者不知如何在網路上保護個人隱私2013-01-24微軟
- 解讀保護DNS伺服器的幾種方法2011-09-05DNS伺服器
- 怎樣做好保護網站並對其進行修改2020-11-25網站
- 調查顯示80%的Python使用者將其作為主要語言2018-03-30Python
- Electron-如何保護原始碼?2019-02-18原始碼
- SnellAdvancedMedia選擇金雅拓保護其智慧財產權2018-05-18
- 如何刪除word文件密碼保護 解除word文件保護密碼2022-03-28密碼
- 如何檢查Mac上是否啟用了SIP系統完整性保護2020-12-29Mac
- labview密碼保護方式及如何保護labview密碼不被破解2016-06-11View密碼
- 如何保護電子郵件安全2019-03-25
- 如何保護物聯網裝置2022-03-30
- 如何破解Word文件密碼保護2017-01-16密碼
- 【深度思考】如何優雅告知使用者,網站正在升級維護?2020-08-06網站
- 保護模式下的特權級檢查 (轉)2007-12-14模式
- 基於隱私保護技術的DNS通訊協議介紹2019-07-07DNS協議
- Strategy Analytics:調查顯示88%的英國使用者對其智慧音響表示滿意2019-10-17
- 如何保護PostgreSQL資料庫安全? | goteleport2021-04-03SQL資料庫Go
- 如何保護好資料伺服器2022-07-15伺服器
- 如何使用代理保護企業網路?2022-06-20
- 如何保護企業免受黑客攻擊?2022-02-10黑客
- CRM如何保護企業資料安全?2022-02-24
- CRM如何保護客戶資料安全?2022-02-15
- 如何保護SSL證書私鑰安全2017-12-14
- 如何在保護使用者隱私的同時實現精準廣告投放?2022-09-01
- 保護模式2024-03-11模式
- 保護期限2024-03-20
- win10如何禁用增強保護_win10怎麼禁用系統保護2020-08-30Win10
- DNS查詢2016-07-23DNS
- 新發現DNS安全漏洞影響巨大,政企如何做好DNS安全防護?2021-08-12DNS
- 【等級保護】等級保護共分為幾級?保護物件是指什麼?2022-03-03物件
- 如何用 AI 技術保護隱私安全?2020-03-15AI
- Uber如何安全保護Kafka基礎設施?2022-04-22Kafka
- 程式設計師如何保護自己的眼睛2018-12-06程式設計師