首页
论坛
专栏
课程

如何只输入少量用户密码而使用大量的密码

2020-1-3 23:18 3310

如何只输入少量用户密码而使用大量的密码

2020-1-3 23:18
3310
  穷举攻击最怕密码的位数多,位数一多从时间考虑它也玩不起。但用户密码太长是不好记忆的,下面的方法让你只需要输入少量的用户密码而使用大量的密码数组,你可以这样做:从你接受到的少量的用户密码算出几个参数,其中之一作为某随机函数的种子,然后从此种子开始的1000个字节作为密码的原始数组,这么多的密码也许是太多了,然后你用前面算出的参数处理一下这个数组(例如倒序等),然后从这1000个字节中随机选择几个作为加密之用即可。此种方法是从1000的候选者中选择的,如此定义要想穷举攻击必须考虑这个范围。
  也可以这样,指定一个随机函数和一个数值,以此数值为种子其后生成的一千个字节的数组作为默认密码数组S,当程序接受到用户输入的少量密码后,程序将对数组 S进行随机排序,生成新的数组,而程序使用这个新数组进行加密或解密工作,S数组和用户输入数据可以少量使用或不用。这样就达到了用户只需少量输入就能使用大量用户密码的目的。



2020安全开发者峰会(2020 SDC)议题征集 中国.北京 7月!

最后于 4天前 被sjdkx编辑 ,原因: 丰富内容
最新回复 (22)
白菜大哥 2020-1-4 05:41
2
0
不懂,我的理解是,假设你一个软件,登陆密码是123456,不论你如何加密,但是你下次输入123456的时候都必须能够登陆上去。只要有了这点,人家一直穷举,只要穷举到了123456还是可以登陆进去,你的加密就算在复杂,也失去了意义。至于你说的硬盘id和随机数之类的,这些还是可以伪造的,硬盘id可以hook,随机数可以int3 hook单字节指令以后模拟运行,也可以hook住random函数。
sjdkx 2020-1-5 00:08
3
0
密码字符多了,穷举需要近乎无穷大的时间,所以穷举攻击失效,本帖的方法就是讨论如何输入有限的一点点密码,而实际使用大量的数组做用户密码。仔细看看道理很简单的。也不需要伪造什么东西。
wsy 2020-1-6 10:04
4
0
现有的多数都比你的扯淡话完善的多。
继续围观装疯卖傻。
sjdkx 2020-1-6 14:42
5
0
  我说的方法不能用吗?什么叫装疯卖傻?你要是理解不了我可以说得更详细些。
  例如得到几个用户输入的密码字符,当然这些都是数据了,用这几个数算出几个参数不难吧,然后算出随机函数的种子值,以此做种子取例如1000个随机函数的值放到字节数组里,这也不难吧,我们就用1000个数据为基础做出用户密码,但实际上我们用不了那么多的数据作为用户密码,刚才不是算出了几个参数吗?随便取一个算出个大于1000的数,用此数除以1000得到一个余数N,原来的数组是 f[0] , f[1],...f[999],现在取用f[N]及其后面的几个数据作为加密用的用户密码即可。
  为什么这样做,主要为了克制穷举攻击而已,诸位有什么好办法请不吝赐教。

yimingqpa 1 2020-1-8 14:51
6
0
按网上大多数人做法是 MD5/SHA 加几次密码再发服务器, 服务器判断对错。
防穷举可以像银行一样,错误几次后禁止登陆。
sjdkx 2020-1-8 15:18
7
0
错误几次后禁止登陆,确实是个办法。但必须考虑最坏的情况,即窃密方能够使用穷举攻击,也能有相应的软件,在此条件下能抵抗的住才能实现安全加密。
sjdkx 2020-2-3 17:38
8
0
使用长密码对分组密码加密用处不大,因为分组密码密钥就那几位,找到关键部位穷举攻击就不错。
但对于流密码加密使用长密码就很厉害了,因为密码长度没有约束。而穷举长密码就是个梦。
可以这样做:设定一个长密码作为默认密码,得到用户密码后,对默认密码进行改造,得到加密使用的密码。如果加密无需那么多密码,可以拣选其中部分使用。
Boring勇哥 2020-3-20 19:44
9
0
额,楼主我想问一下,你这个长密码也是依据用户提供的短密码生成的吧
Boring勇哥 2020-3-25 00:04
10
1

楼主,还记得你写的功能强大的自动密码文件加密软件吗?那个帖子不能回复就在此回复一下吧。提前说一下,我是个初学者。我粗略的分析了一下,你的算法加密强度非常低,所有自动生成的密钥都是基于GetTickCount()返回得32位值,无论自动密码和用户密码长度多少,破解最多需要尝试 pow(2,32)*1000*20 次就能得到结果。(其实根本不需要这么多次,因为GetTickCount()返回开机到现在经过的毫秒数,不会太大)
上式的1000是最大自动密钥长度(字节为单位),20是最长用户密码长度(字节单位)。
所谓不可破解就是假定破解者不知道算法和被加密文件格式,现实中需要严格保护加密算法的情况,使用的加密算法都是特供的,你我都没有能力开发。
下面提供我逆出来的加解密算法,并提供解密pe格式文件的简单例子。
ps:我在伪代码里发现了很多类似 (a ^ (a ^ b)) 的异或代码,不知道是反编译器的原因还是楼主真的不知道  (a ^ (a ^ b)) == b
ps:一切避开数论讨论加密原理的行为都是耍流氓
#include <cstdio>
#include <ctime>
#include <Windows.h>
#include <algorithm>
#pragma warning(disable:28159)
#pragma warning(disable:4996)

DWORD pKeySink[623];
DWORD dwKeyLengthInDword = 623;
DWORD dwGenerateKeyCount;

BYTE cbDataSink[1000];
bool cbKeyGenerated;

void GenerateKeySinkWithRandom(_In_ DWORD Random) {
    DWORD dwCurrentValue;
    dwGenerateKeyCount = 0;
    cbKeyGenerated = true;
    pKeySink[0] = Random;
    for (DWORD index = 0; index < 622; ++index) {
        dwCurrentValue = pKeySink[index];
        pKeySink[index + 1] = index + 0x6C078965 * (dwCurrentValue ^ (dwCurrentValue >> 30));
    }
}

void updateKeySink() {
    bool v4; // zf

    for (auto i = 0; i < dwKeyLengthInDword; ++i) {
        v4 = (LOBYTE(pKeySink[i + 1] % 624) & 1) == 0;
        pKeySink[i] = ((pKeySink[(i + 1) % 624] & 0x7fffffffu) >> 1) ^ pKeySink[i % 624];
        if (!v4)
            pKeySink[i] ^= 0x9908B0DF;
    }
    return;
}

DWORD GenerateRandomInKeySink() {
    int v1; // eax
    DWORD v2; // ecx
    DWORD v3; // edx
    DWORD result; // eax

    if (!cbKeyGenerated) GenerateKeySinkWithRandom(time(nullptr));
    
    v1 = dwGenerateKeyCount;
    if (!dwGenerateKeyCount)
    {
        updateKeySink();
        v1 = dwGenerateKeyCount;
    }
    v2 = ((((pKeySink[v1] >> 11) ^ pKeySink[v1]) & 0xFF3A58AD) << 7) ^ (pKeySink[v1] >> 11) ^ pKeySink[v1];
    v3 = (v1 + 1) % 0x270u;
    result = ((v2 & 0xFFFFDF8C) << 15) ^ v2 ^ ((((v2 & 0xFFFFDF8C) << 15) ^ v2) >> 18);
    dwGenerateKeyCount = v3;
    return result;
}

__forceinline BOOLEAN NTAPI RtlIsValidImageHeader(_In_ LPCVOID lpBufferToImageHeader) {
    auto dos = PIMAGE_DOS_HEADER(lpBufferToImageHeader);
    return (dos->e_magic == IMAGE_DOS_SIGNATURE) &&
        (dos->e_lfanew >= sizeof(IMAGE_DOS_HEADER)) &&
        (dos->e_lfanew < 0x400) &&
        (PIMAGE_NT_HEADERS(SIZE_T(lpBufferToImageHeader) + dos->e_lfanew)->Signature == IMAGE_NT_SIGNATURE);
}

VOID NTAPI RtlUpdateBuffer(
    _In_ DWORD dwKey,
    _In_ BOOLEAN Encrypt,
    _In_ DWORD dwUserKeyLength,
    _In_ DWORD dwAutoKeyLength,
    _In_reads_bytes_(dwCiphertextLength) LPCVOID lpCiphertext,
    _In_ DWORD dwCiphertextLength,
    _Out_writes_bytes_all_(dwCiphertextLength) LPVOID lpResult,
    _Out_writes_opt_(dwUserKeyLength) LPDWORD lpKey) {
    LPDWORD pAutoKey = new DWORD[1ull + dwAutoKeyLength];
    DWORD dwTemp = 0;

    RtlMoveMemory(lpResult, lpCiphertext, dwCiphertextLength);

    GenerateKeySinkWithRandom(dwKey);
    for (auto i = 0; i < dwUserKeyLength; ++i)
        cbDataSink[i] = GenerateRandomInKeySink();
    for (auto i = 10; i; --i)
        for (auto j = 0; j < dwUserKeyLength; ++j)
            std::swap(cbDataSink[j], cbDataSink[(j + GenerateRandomInKeySink()) % dwUserKeyLength]);

    cbDataSink[dwUserKeyLength] = 0;

    if (dwUserKeyLength > 0)
        for (auto i = 0; i < dwUserKeyLength; ++i)
            dwTemp += cbDataSink[i];
    GenerateKeySinkWithRandom(dwTemp);
    if (dwAutoKeyLength > 0) {
        for (auto i = 0; i < dwAutoKeyLength; ++i)
            pAutoKey[i] = GenerateRandomInKeySink();
        for (auto i = 0; i < dwAutoKeyLength; ++i)
            std::swap(pAutoKey[i], pAutoKey[(i + GenerateRandomInKeySink()) % dwAutoKeyLength]);
    }
    GenerateKeySinkWithRandom(dwTemp * dwTemp);
    if (dwCiphertextLength > 0) {
        dwTemp = dwCiphertextLength % dwAutoKeyLength;
        for (auto i = 0; i < dwCiphertextLength; ++i) {
            if (i == dwTemp) GenerateKeySinkWithRandom(pAutoKey[1]);
            LPBYTE(lpResult)[i] += (Encrypt ? 1 : -1) * GenerateRandomInKeySink();
        }
    }
    if (lpKey) {
        for (auto i = 0; i < dwUserKeyLength; ++i)
            lpKey[i] = cbDataSink[i];
    }
    delete[]pAutoKey;
}

#define RtlEncryptBuffer(_dwKey_, _dwUserKeyLength_, _dwAutoKeyLength, _lpCiphertext_, _dwCiphertextLength_, _lpResult_, _lpKey_)\
    RtlUpdateBuffer(_dwKey_, TRUE, _dwUserKeyLength_, _dwAutoKeyLength, _lpCiphertext_, _dwCiphertextLength_, _lpResult_, _lpKey_)
#define RtlDecryptBuffer(_dwKey_, _dwUserKeyLength_, _dwAutoKeyLength, _lpCiphertext_, _dwCiphertextLength_, _lpResult_, _lpKey_)\
    RtlUpdateBuffer(_dwKey_, FALSE, _dwUserKeyLength_, _dwAutoKeyLength, _lpCiphertext_, _dwCiphertextLength_, _lpResult_, _lpKey_)

BOOLEAN NTAPI RtlCrackPeFileEncryption(
    _Out_writes_bytes_(21*2) LPSTR lpUserPassword,
    _In_opt_ DWORD dwBeginRandom,
    _In_ LPCVOID lpBufferToCrack,
    _In_ DWORD dwBufferSize) {
    lpUserPassword[0] = '\0';

    BOOLEAN result = FALSE;
    LPVOID lpData = nullptr;
    const auto Length = sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS32);
    auto BeginRandom = dwBeginRandom;
    DWORD dwUserKey[2];

    if (dwBufferSize < Length)
        return FALSE;

    lpData = new char[dwBufferSize];
    while (BeginRandom & 0xffffffff) {
        RtlDecryptBuffer(BeginRandom, 2, 567, lpBufferToCrack, dwBufferSize, lpData, LPDWORD(&dwUserKey));
        if (RtlIsValidImageHeader(lpData)) {
            //found
            result = TRUE;
            sprintf(lpUserPassword, "%02X%02X", BYTE(dwUserKey[0]), BYTE(dwUserKey[1]));
            break;
        }
        --BeginRandom;
    }

    return result;
}

int main() {
    //BYTE test[4] = { 0x11,0x22,0x33,0x44 };
    //PDWORD key = nullptr;
    //calc(test, 4, key, &key, 2, 567);
    //calc(test, 4, key, nullptr, 2, 567);

    typedef struct _PE_HEADERS{
        IMAGE_DOS_HEADER dos;
        IMAGE_NT_HEADERS32 nt;
        _PE_HEADERS() {
            dos.e_magic = IMAGE_DOS_SIGNATURE;
            dos.e_lfanew = sizeof(dos);
            nt.Signature = IMAGE_NT_SIGNATURE;
        }
    }PE_HEADERS,*PPE_HEADERS;

    auto pImage = new PE_HEADERS;
    DWORD key[2];
    char szKey[42];
    RtlEncryptBuffer(GetTickCount(), 2, 567, pImage, sizeof(PE_HEADERS), pImage, LPDWORD(&key));
    if (!RtlCrackPeFileEncryption(szKey, GetTickCount(), pImage, sizeof(PE_HEADERS))) {
        //failed
        abort();
    }

    //check key[] and szKey

    //success
    delete pImage;
    return 0;
}

alphc 2020-3-25 08:08
11
0
楼上说的对,如果要证明你的密码强度大一定要给出严谨的数学证明
Lightal 2020-3-25 09:01
12
0
有种民科既视感
sjdkx 2020-3-25 16:24
13
0
@Boring勇哥 自动密码没有位数的限制,你那个20位是怎么来的?想让程序生成多少位字节密码是使用者自己设定的。 由于密码是程序随机生成的,所以加密强度极高,我是自夸了。这些密码信息不是保存在密文里(像一般加盐技术那样)而是由用户自行保存,所以相当安全,破解者想要得到比登天还难。
@Lightal 业余爱好,数学功底很差。
重申长密码的使用:
程序内部有一个较长的固定数组F,而 用户输入的密码 到达以后,程序将首先使用 用户输入的密码进行计算并用数据调整前面那个数组F,使其发生彻底改变生成F1,程序将使用这些改变后的数组F1作为用户密码来加密明文等。
Boring勇哥 2020-3-25 22:15
14
0
截图为证。即使没有这些限制,复杂度也只是线性增长,并非指数增长,因此安全性不会提高太多。
Boring勇哥 2020-3-25 22:17
15
0
只要程序第一次调用的GetTickCount()或者time()返回值确定了,后面所有的随机数数组的内容都确定了。
sjdkx 2020-3-26 00:15
16
0
确定了又如何,能得到数据才是真章,否则还是0?
Boring勇哥 2020-3-26 00:41
17
0
?????那不是你加密用的自动生成的密钥?有了密钥不就能解出明文了?看来是我水平太低,无法理解你的高级算法,楼主加油吧
sjdkx 2020-3-27 00:14
18
0
您需要将自动密码的那个程序了解清楚,那个程序是这样工作的,你用它加密文件时:
给它你要加密的文件,它将自动生成用户密码(密码位数是事先设置的),它用这些用户密码完成加密,并给你显示用户密码让你保存,以便解密时用。特殊之处是不用您自己提供密码,程序代劳了。
Boring勇哥 2020-3-27 10:05
19
0
我知道,程序算密码用的第一个随机数是一个32位的整数,只要确定了这个数,你后面所有的密钥都可以推算出来,加密还有什么意义?我又没说用户提供密码吧?不要偷换概念
Boring勇哥 2020-3-27 10:08
20
0
你一直在回避算法的安全性,回答的无关紧要,不要回复我了,节约大家的时间,谢谢。
wx_ggMM 2020-3-30 10:12
21
0
输入少量的用户密码 =》 我爆破少量密码就好了,省心!感谢楼主让我们吃上饭
Boring勇哥 2020-3-30 16:45
22
0
wx_ggMM 输入少量的用户密码 =》 我爆破少量密码就好了,省心!感谢楼主让我们吃上饭
哈哈哈
sjdkx 2020-3-30 19:21
23
0
别做梦了,程序设计就是使用大量密码的,请注意其默认的密码数组是预先设定好的,用户输入的少量密码,将对那个固定数组的数值进行全面改造,改造后的数组才是真正要使用的用户密码数组。
游客
登录 | 注册 方可回帖
返回