利用Win2K下的空會話(null session)入侵服務器
i、 introduction
ii、 about tcp port 445
iii、the null session
iv、 break through "restricanonymous=1"
v、 conclusion
終于有時間可以寫自己的東西了。呵呵,直入主題,不廢話了。
i、 introduction
關于win2k下面的空會話,已經是一個很老的話題了。當然,也一直被認為是win2
k自帶的一個后門。當建立一個空會話之后,對于一臺配置不到位的win2k服務器來說,
那么將能夠得到非常多的信息,比如枚舉帳號等等。
ii、 about tcp port 445, win2000的tcp 445端口
smb(server message block)協議在nt/2000中用來作文件共享,在nt中,smb運行
于nbt(netbios over tcp/ip)上,使用137,139(udp),139(tcp)端口。在2000中
,smb可以直接運行在tcp/ip上,而沒有額外的nbt層,使用tcp 445端口。因此在2000上
應該比nt稍微變化多一些。
可以在“網絡連接/屬性/tcpip協議/屬性/高級/wins中設置啟用或者禁用nbt(net
bios over tcp/ip)。
當2000使用網絡共享的時候,就面臨著選擇139或者445端口了。下面的情況確定會
話使用的端口:
1、如果客戶端啟用了nbt,那么連接的時候將同時訪問139和445端口,如果從445端口得
到回應,那么客戶端將發送rst到139端口,終止這個端口的連接,接著就從445端口進行
smb的會話了;如果沒有從445端口而是從139得到回應,那么
就從139端口進行會話;如果沒有得到任何回應,那么smb會話失敗。
2、如果客戶端禁用了nbt,他就將只從445端口進行連接。當然如果服務器(開共享端)
沒有445端口進行smb會話的話,那么就會訪問失敗了,所以禁用445端口后,對訪問nt機
器的共享會失敗。
3、如果服務器端啟用nbt,那么就同時監聽udp 137、138端口和tcp139,445。如果禁用
nbt,那么就只監聽445端口了。
所以對于2000來說,共享問題就不僅僅是139端口,445端口同樣能夠完成。
iii、the null session,關于空會話
null會話(空會話)使用端口也同樣遵循上面的規則。null會話是同服務器建立的
無信任支持的會話。一個會話含用戶的認證信息,而null會話是沒有用戶的認證信息
,也就好比是一個匿名的一樣。
沒有認證就不可能為系統建立安全通道,而建立安全通道也是雙重的,第一,就是
建立身份標志,第二就是建立一個臨時會話密匙,雙方才能用這個會話進行加密數據交
換(比如rpc和com的認證等級是pkt_privacy)。不管是經過ntlm還是經過kerberos認證
的票據,終究是為會話創建一個含用戶信息的令牌。(這段來自joe finamore)
根據win2000的訪問控制模型,對于空會話同樣需要提供一個令牌。但是空會話由于
是沒有經過認證的會話,所以令牌中不含用戶信息,因此,建立會話雙方沒有密匙的
交換,這也不能讓系統間發送加密信息。這并不表示空會話的令牌中不含sid,對于一
個空會話,lsa提供的令牌的sid是s-1-5-7,這就是空會話建立的sid,用戶名是 anony
mous logon。這個用戶名是可以在用戶列表中看到的。但是是不能在sam數據庫中找到,
屬于系統內置的帳號。
(關于這部分對null session的分析,可以參照:《null sessions in nt/2000》http
://rr.sans.org/win/null.php)
null會話幾乎成為了微軟自己安置的后門,但是微軟為什么要來設置這樣一個“后
門”呢?我也一直在想這個問題,如果null會話沒有什么重要的用途,那么微軟也應該
不會來設置這樣一個東西。好不容易才在微軟上找到這個:
當在多域環境中,要在多域中建立信任關系,首先需要找到域中的pdc來通過安全通
道的密碼驗證,使用空會話能夠非常容易地找到pdc,還有就是關于一些系統服務的問題
。而且lmhosts的#include就需要空會話的支持,可以參考文章:
http://support.microsoft.com/default.aspx?scid=kben-us;q121281
還有http://support.microsoft.com/default.aspx?scid=kben-us;q124184
其實建立一個空會話的條件也非常嚴格。首先要能夠滿足上面的,也就是打開tcp
139和tcp 445端口。我們可以從一次關閉這兩個端口的情況中看得出來。服務器關閉44
5和139端口,然后我們來進行空會話的連接。首先,客戶端打算
連接的是445端口,然后再試圖連接139端口。當然 后還是失敗了。
僅僅開放這兩個端口還不行,服務器還必須得打開ipc$共享。如果沒有ipc共享,即
使共享一個文件,有權限為anonymous logon,也不能建立會話,即使權限設置為完全控
制,出現的連接錯誤依然是權限不夠。這和其他帳號是不一樣的。如果要允許一個文件
夾共享能夠類似ipc$(命名管道而非共享)能夠使用空會話,那么需要修改注冊表:
hkey_local_machine/system/currentcontrolset/services/lanmanserver/parameters
/中的:nullsessionshares,
添加新的共享名,這樣才能建立一個共享的空會話。這時,將不依賴ipc的存在了。(即
使這樣的空會話對于后面的突破也是一點沒可取之處的,因為沒有了ipc$命名管道,rp
c不可取了,這下知道ipc這個命名管道的具體實現了。呵呵)
雖然空會話建立的要求很嚴格,但是那都是默認建立的。既然是默認的,對于使用
win2k系統的服務器來說,就還是有利用的價值。 明顯的就是空會話可以很方便地連接
到其他的域,枚舉用戶、機器等。這也就是掃描軟件進行探測的原理。我舉個簡單的程序
例子來說明這個用途,并枚舉用戶組和用戶。
#include <windows.h>
#include <stdio.h>
#include <lm.h>
#pragma comment (lib, "mpr.lib")
#pragma comment (lib, "netapi32.lib")
void explorer_groups(char *);
void main( int argc, char *argv[ ] )
{
dword ret;
char username[100] = "", password[100] = "";
char server[100] = "", ipc[100] = "";
netresource net;
if (argc == 1) {
exit(1);
}
strncpy(server,argv[1],100);
printf("server: %s/n", server);
sprintf(ipc,"////%s//ipc$",server);
net.lplocalname = null;
net.lpprovider = null;
net.dwtype = resourcetype_any;
net.lpremotename = (char*)&ipc;
printf("setting up session... ");
ret = wnetaddconnection2(&net,(const char *)&password,(const char *)&use
rname,0);
if (ret != error_success)
{
printf("ipc$ connect fail./n");
exit(1);
}
else printf("ipc$ connect success./n");
explorer_groups((char*)&server);
printf("disconnect server... ");
ret = wnetcancelconnection2((char*)&ipc,0,true);
if (ret != error_success)
{
printf("fail./n");
exit(1);
}
else printf("success./n");
exit (0);
}
void explorer_groups(char *server)
{
dword ret, read, total, resume = 0;
int i;
lpvoid buff;
char comment[255];
wchar_t wserver[100];
do {
ret = netlocalgroupenum(wserver, 1, (unsigned char **)&buff, max_pre
ferred_length, &read, &total, &resume);
if (ret != nerr_success && ret != error_more_data)
{
printf("fail/n");
break;
}
plocalgroup_info_1 info = (plocalgroup_info_1) buff;
for (i=0; i<read; i++) {
printf("group: %s/n",info[i].lgrpi1_name);
widechartomultibyte(cp_acp, 0, info[i].lgrpi1_comment , -1, comm
ent,255,null,null);
printf("/tcomment: %s/n",comment);
dword ret, read, total, resume = 0;
ret = netlocalgroupgetmembers((const unsigned short*)&wserver, i
nfo[i].lgrpi1_name, 2, (unsigned char **)&buff, 1024, &read, &total, &resume
);
if (ret != nerr_success && ret != error_more_data) {
printf("fail/n");en
break;
}
plocalgroup_members_info_2 info = (plocalgroup_members_info_2) b
uff;
for (unsigned i=0; i<read; i++) {
printf("/t/t%s/n", info[i].lgrmi2_domainandname);
printf("/t/t/tsid:%d/n", info[i].lgrmi2_sid);
printf("/t/t/tsidusage:%d/n",info[i].lgrmi2_sidusage);
}
netapibufferfree (buff);
}
netapibufferfree (buff);
} while (ret == error_more_data );
}
這是一個簡單的例子。當然可以查詢更多的東西。類似,就不再重復了。枚舉用戶
名是很重要的用途,因為接下來可以做的就是進行密碼的猜解,這對系統安全構成的威
脅是非常大的。
想到有威脅,就需要知道怎么防范。防范很簡單。不過這里需要提醒的是關于注冊
表(或者安全策略中)中可以設置restrictanonymous為1,這樣可以禁止空連接進行枚
舉。因為很多安全配置介紹中都是這樣做的,因為如果設置為2的
話,有一些問題會發生。比如一些win的服務出現問題等等。但是,restricanonymous設
置為1并不會組織用戶帳號的枚舉。因為空連接是一樣能夠建立的,并不是說阻止了空連
接的建立。但是我們這里需要來突破!!
iv、 break through "restricanonymous=1",突破restricanonymous=1的限制進行用戶
枚舉
雖然我們還是能夠建立空連接,但是卻沒有那些net函數的訪問權限了。這個可以看
msdn上關于對網絡管理函數的安全問題。但是,有一個突破點,呵呵,那就是猜測。這
個需要聯系到訪問控制模型上了。就象前面對null會話說到的那樣,null會話獲得anon
ymous logon 用戶名的sid(安全標志符),系統用sid來標志這個用戶,同時創建令牌
。而這個令牌才是系統對用戶的訪問權限標志,并不是用帳戶名來確定。現在我們需要
的是sid,因為令牌中含了帳戶的sid,對于系統來說,sid是不變的。sid是在帳戶或
者組創建的時候就建立好了。可是每個win2k系統為相同用戶名的帳戶創建的sid并不相
同。
我們來先分析這個重要的sid。sid的格式是:s-r-x-y(1)-y(2)-……-y(n)。其中s
表示該字符串是sid,r是sid的版本號,對于2000來說,這個就是1,然后x是標志符的頒
發機構 (identifier authority)。對于2000內的帳戶,考試合格頒發機構就是nt,值是5。然
后y表示一系列的子考試合格頒發機構,前面幾項是標志域的, 后一個y(n)標志著域內的帳戶和
組。也就是說,對于多數帳戶來說,區別在與這個y(n)。
現在需要分析一下sam結構中對帳戶的管理。展開注冊表的sam之后,
hkey_local_machine/sam/sam/domains/builtin/aliases/members,存儲著重要的帳戶
內容。找到本地的域,然后展開,得到的就是本地帳號的所有sid列表。分析其規律,因
為有些是系統規定的y(n),所以就有規律了。其中的000001f4,十進制的500,這一定是
一個固定的,標志系統建立時候的內置管理員帳號administrator,000001f5就是guest
帳號了。然后看到了一個跳躍到了000003e8,這是其他帳號的開始,比如 tsinternetu
ser帳戶以及自定義的帳戶了。
到了這里,我們還需要函數的支持。lookupaccountname()和lookupaccountsid(),
這兩個函數是允許空會話使用的。上面通過空會話來建立連接然后使用net*函數枚舉帳
戶已經被restrictanonymous禁止了。因此,sid成為我們的突破口。不過sid對于一臺機
器來說是固定的,但是并不是說跟其他的機器的sid是一樣的,我們需要進行一定的猜測
,用來獲得sid中不同的部分,就是幾個子考試合格頒發機構。哈哈,我們猜測的東西就是用戶名
。假象系統存在的用戶名,比如tsinternetuser,guest帳戶等。然后通過這些帳戶名獲
得sid的前面部分,然后根據前面部分進行枚舉,將 后一個子考試合格頒發機構進行遞加,從而
獲得每個帳戶的sid,然后又回過來,利用函數lookupaccountsid()進行用戶名的查詢。
這樣就達到了枚舉帳戶的目的了,而且是在 restrictanonymous=1 的情況下。
我寫了一個程序,我猜測的帳號是guest。大致思路在這里介紹了,把代碼貼在這里
太占篇幅,原代碼和執行程序可以在我的主頁(www.opengram.com)下載。
v、 conclusion
不過,即便我們花費這么大精力能夠突破restricanoymous=1的限制,但是防范起來
簡直太容易了。取消掉ipc$命名管道,就讓null session完全沒有用了。
-----------------------
reference:
1、msdn
2、http://www.microsoft.com/msj/defaultframe.asp?page=/msj/0299/security/sec
urity0299.htm&nav=/msj/0299/newnav.htm
3、《the use of tcp port 445 in windows 2000》 arne vidstrom