SHOW:
|
|
- or go back to the newest paste.
| 1 | - | /* |
| 1 | + | /** |
| 2 | * Source: https://stackoverflow.com/questions/17946282/whats-the-hash-in-hkcu-software-microsoft-windows-currentversion-explorer-filee | |
| 3 | * | |
| 4 | * AssocHashGen - now you can set default browser in one click, just like on XP | |
| 5 | * | |
| 6 | * This is a crappy proof of concept tool to generate User Association hashes | |
| 7 | * in Windows 10 and 8.1. | |
| 8 | * | |
| 9 | * This should be all that's needed to solve the association problems that | |
| 10 | * Mozilla and Google have been complaining about. If microsoft still claims | |
| 11 | * this is a "security" measure and not just anti-competitive nonsense, it | |
| 12 | * defeats that "security" too. | |
| 13 | * | |
| 14 | * http://www.theverge.com/2015/7/30/9076445/mozilla-microsoft-windows-10-browser-default-apps-complaint | |
| 15 | * http://www.theverge.com/2015/10/18/9563927/microsoft-windows-10-default-apps-browser-prompt | |
| 16 | * | |
| 17 | * Technical details: | |
| 18 | * User choice hashes are pretty much just | |
| 19 | * MD5((protocol + sid + progid + exepath).toLower()) passed through some | |
| 20 | * custom scrambling functions. Dead simple. Not sure why no one else beat me | |
| 21 | * to it yet. | |
| 22 | * | |
| 23 | * Using Qt because I had it around and they have MD5 built in as well as | |
| 24 | * nice string manipulation functions. | |
| 25 | * | |
| 26 | * Sample Usage: | |
| 27 | * For URLs of http and https, provide SID to calculate this for any user on | |
| 28 | * any machine: | |
| 29 | * AssocHashGen -s "<SID>" -p "c:\program files (x86)\mozilla firefox\firefox.exe" http FirefoxURL | |
| 30 | * For all others (file extensions, non-http(s) URLs, etc) do NOT provide exe path: | |
| 31 | * AssocHashGen -s "<SID>" .htm ChromeHTML | |
| 32 | * | |
| 33 | * Tested on Windows 10 Enterprise and 8.1 Professional. | |
| 34 | * Algorithm has remained the same. | |
| 35 | * | |
| 36 | * WARNING: | |
| 37 | * This code lacks basic things like error handling, memory management and | |
| 38 | * buffer length checks. This is done "for clarity" but also "because lazy". | |
| 39 | * Do _not_ use it directly except for testing. | |
| 40 | */ | |
| 41 | ||
| 42 | #include <QCoreApplication> | |
| 43 | ||
| 44 | #include <QCommandLineParser> | |
| 45 | #include <iostream> | |
| 46 | #include <Windows.h> | |
| 47 | #include <sddl.h> | |
| 48 | #include <vector> | |
| 49 | #include <QDebug> | |
| 50 | #include <QCryptographicHash> | |
| 51 | ||
| 52 | void CS64_WordSwap(const unsigned int *data, unsigned int dataLength, | |
| 53 | const unsigned int *md5Start, unsigned int *output) | |
| 54 | {
| |
| 55 | unsigned int v5; // er10@1 | |
| 56 | const unsigned int *v6; // rdi@1 | |
| 57 | unsigned int v7; // er11@3 | |
| 58 | unsigned int v8; // ebx@3 | |
| 59 | int v9; // er9@3 | |
| 60 | int v10; // er11@3 | |
| 61 | int v11; // ebx@3 | |
| 62 | unsigned int v12; // er8@3 | |
| 63 | __int64 v13; // rsi@3 | |
| 64 | unsigned int v14; // er8@4 | |
| 65 | unsigned int v15; // edx@4 | |
| 66 | int v16; // er8@4 | |
| 67 | int v17; // er9@4 | |
| 68 | unsigned int v18; // edx@4 | |
| 69 | char result; // al@6 | |
| 70 | unsigned int v20; // er11@9 | |
| 71 | unsigned int v21; // er8@9 | |
| 72 | int v22; // er9@9 | |
| 73 | ||
| 74 | v5 = dataLength; | |
| 75 | v6 = data; | |
| 76 | if (dataLength < 2 || dataLength & 1) | |
| 77 | {
| |
| 78 | result = 0; | |
| 79 | } | |
| 80 | else | |
| 81 | {
| |
| 82 | v7 = *md5Start | 1; | |
| 83 | v8 = md5Start[1] | 1; | |
| 84 | v9 = 0; | |
| 85 | v10 = v7 + 0x69FB0000; | |
| 86 | v11 = v8 + 0x13DB0000; | |
| 87 | v12 = 0; | |
| 88 | v13 = ((v5 - 2) >> 1) + 1; | |
| 89 | do | |
| 90 | {
| |
| 91 | v14 = *v6 + v12; | |
| 92 | v6 += 2; | |
| 93 | v5 -= 2; | |
| 94 | v15 = 0x79F8A395 * (v14 * v10 - 0x10FA9605 * (v14 >> 16)) | |
| 95 | + 0x689B6B9F * ((v14 * v10 - 0x10FA9605 * (v14 >> 16)) >> 16); | |
| 96 | v16 = 0xEA970001 * v15 - 0x3C101569 * (v15 >> 16); | |
| 97 | v17 = v16 + v9; | |
| 98 | v18 = (*(v6 - 1) + v16) * v11 - 0x3CE8EC25 * ((*(v6 - 1) + v16) >> 16); | |
| 99 | v12 = 0x1EC90001 * (0x59C3AF2D * v18 - 0x2232E0F1 * (v18 >> 16)) | |
| 100 | + 0x35BD1EC9 * ((0x59C3AF2D * v18 - 0x2232E0F1 * (v18 >> 16)) >> 16); | |
| 101 | v9 = v12 + v17; | |
| 102 | --v13; | |
| 103 | } while (v13); | |
| 104 | if (v5 == 1) | |
| 105 | {
| |
| 106 | v20 = (*v6 + v12) * v10 - 0x10FA9605 * ((*v6 + v12) >> 16); | |
| 107 | v21 = 0xEA970001 * (0x79F8A395 * v20 + 0x689B6B9F * (v20 >> 16)) | |
| 108 | - 0x3C101569 * ((0x79F8A395 * v20 + 0x689B6B9F * (v20 >> 16)) >> 16); | |
| 109 | v22 = v21 + v9; | |
| 110 | v12 = 0x1EC90001 | |
| 111 | * (0x59C3AF2D * (v21 * v11 - 0x3CE8EC25 * (v21 >> 16)) | |
| 112 | - 0x2232E0F1 * ((v21 * v11 - 0x3CE8EC25 * (v21 >> 16)) >> 16)) | |
| 113 | + 901586633 | |
| 114 | * ((0x59C3AF2D * (v21 * v11 - 0x3CE8EC25 * (v21 >> 16)) | |
| 115 | - 0x2232E0F1 * ((v21 * v11 - 0x3CE8EC25 * (v21 >> 16)) >> 16)) >> 16); | |
| 116 | v9 = v12 + v22; | |
| 117 | } | |
| 118 | result = 1; | |
| 119 | *output = v12; | |
| 120 | output[1] = v9; | |
| 121 | } | |
| 122 | } | |
| 123 | ||
| 124 | void CS64_Reversible(const unsigned int *data, unsigned int dataLength, | |
| 125 | const unsigned int *md5, unsigned int *output) | |
| 126 | {
| |
| 127 | unsigned int v5; // er10@1 | |
| 128 | const unsigned int *v6; // rdi@1 | |
| 129 | unsigned int v7; // ebx@3 | |
| 130 | int v8; // er11@3 | |
| 131 | unsigned int v9; // er9@3 | |
| 132 | int v10; // ebx@3 | |
| 133 | unsigned int v11; // er8@3 | |
| 134 | __int64 v12; // rsi@3 | |
| 135 | unsigned int v13; // er8@4 | |
| 136 | unsigned int v14; // er8@4 | |
| 137 | unsigned int v15; // edx@4 | |
| 138 | int v16; // er11@4 | |
| 139 | unsigned int v17; // edx@4 | |
| 140 | unsigned int v18; // edx@4 | |
| 141 | char result; // al@6 | |
| 142 | unsigned int v20; // edx@9 | |
| 143 | unsigned int v21; // edx@9 | |
| 144 | int v22; // er11@9 | |
| 145 | unsigned int v23; // edx@9 | |
| 146 | unsigned int v24; // eax@9 | |
| 147 | ||
| 148 | v5 = dataLength; | |
| 149 | v6 = data; | |
| 150 | if (dataLength < 2 || dataLength & 1) | |
| 151 | {
| |
| 152 | result = 0; | |
| 153 | } | |
| 154 | else | |
| 155 | {
| |
| 156 | v7 = *md5; | |
| 157 | v8 = 0; | |
| 158 | v9 = md5[1] | 1; | |
| 159 | v10 = v7 | 1; | |
| 160 | v11 = 0; | |
| 161 | v12 = ((v5 - 2) >> 1) + 1; | |
| 162 | do | |
| 163 | {
| |
| 164 | v5 -= 2; | |
| 165 | v13 = v10 * (*v6 + v11); | |
| 166 | v6 += 2; | |
| 167 | v14 = 0x5B9F0000 * (0xB1110000 * v13 - 0x30674EEF * (v13 >> 16)) | |
| 168 | - 0x78F7A461 | |
| 169 | * ((0xB1110000 * v13 - 0x30674EEF * (v13 >> 16)) >> 16); | |
| 170 | v15 = 0x1D830000 * (0x12CEB96D * (v14 >> 16) - 0x46930000 * v14) | |
| 171 | + 0x257E1D83 | |
| 172 | * ((0x12CEB96D * (v14 >> 16) - 0x46930000 * v14) >> 16); | |
| 173 | v16 = v15 + v8; | |
| 174 | v17 = 0x16F50000 * v9 * (*(v6 - 1) + v15) - 0x5D8BE90B | |
| 175 | * (v9 * (*(v6 - 1) + v15) >> 16); | |
| 176 | v18 = 0x2B890000 * (0x96FF0000 * v17 - 0x2C7C6901 * (v17 >> 16)) | |
| 177 | + 0x7C932B89 | |
| 178 | * ((0x96FF0000 * v17 - 0x2C7C6901 * (v17 >> 16)) >> 16); | |
| 179 | v11 = 0x9F690000 * v18 - 0x405B6097 * (v18 >> 16); | |
| 180 | v8 = v11 + v16; | |
| 181 | --v12; | |
| 182 | } while (v12); | |
| 183 | if (v5 == 1) | |
| 184 | {
| |
| 185 | v20 = 0xB1110000 * v10 * (v11 + *v6) - 0x30674EEF | |
| 186 | * (v10 * (v11 + *v6) >> 16); | |
| 187 | v21 = 0x1D830000 | |
| 188 | * (0x12CEB96D * ((0x5B9F0000 * v20 - 0x78F7A461 * (v20 >> 16)) >> 16) | |
| 189 | - 0x46930000 * (0x5B9F0000 * v20 - 0x78F7A461 * (v20 >> 16))) | |
| 190 | + 0x257E1D83 | |
| 191 | * ((0x12CEB96D * ((0x5B9F0000 * v20 - 0x78F7A461 * (v20 >> 16)) >> 16) | |
| 192 | - 0x46930000 * (0x5B9F0000 * v20 - 0x78F7A461 * (v20 >> 16))) >> 16); | |
| 193 | v22 = v21 + v8; | |
| 194 | v23 = 0x16F50000 * v9 * v21 - 0x5D8BE90B * (v9 * v21 >> 16); | |
| 195 | v24 = (0x96FF0000 * v23 - 0x2C7C6901 * (v23 >> 16)) >> 16; | |
| 196 | v11 = 0x9F690000 * (0x2B890000 * (0x96FF0000 * v23 - 0x2C7C6901 * (v23 >> 16)) + 0x7C932B89 * v24) | |
| 197 | - 0x405B6097 * ((0x2B890000 * (0x96FF0000 * v23 - 0x2C7C6901 * (v23 >> 16)) + 0x7C932B89 * v24) >> 16); | |
| 198 | v8 = v11 + v22; | |
| 199 | } | |
| 200 | result = 1; | |
| 201 | *output = v11; | |
| 202 | output[1] = v8; | |
| 203 | } | |
| 204 | } | |
| 205 | ||
| 206 | QString getSid() | |
| 207 | {
| |
| 208 | // shit code with no error handling. if we can't get the sid, just tank, lazy PoC | |
| 209 | HANDLE hToken = NULL; | |
| 210 | OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken); | |
| 211 | DWORD dwBufferSize = 0; | |
| 212 | GetTokenInformation(hToken, TokenUser, NULL, 0, &dwBufferSize); | |
| 213 | std::vector<BYTE> buffer; | |
| 214 | buffer.resize(dwBufferSize); | |
| 215 | PTOKEN_USER pTokenUser = reinterpret_cast<PTOKEN_USER>(&buffer[0]); | |
| 216 | ||
| 217 | GetTokenInformation( | |
| 218 | hToken, | |
| 219 | TokenUser, | |
| 220 | pTokenUser, | |
| 221 | dwBufferSize, | |
| 222 | &dwBufferSize); | |
| 223 | ||
| 224 | LPWSTR output; | |
| 225 | ConvertSidToStringSidW(pTokenUser->User.Sid, &output); | |
| 226 | ||
| 227 | CloseHandle(hToken); | |
| 228 | hToken = NULL; | |
| 229 | ||
| 230 | return QString::fromWCharArray(output); | |
| 231 | } | |
| 232 | ||
| 233 | ||
| 234 | QString genHash(QString protocol, QString exepath, QString sid, QString progid) | |
| 235 | {
| |
| 236 | // start out zero'd because towchararray doesn't append 0s and laziness | |
| 237 | wchar_t* data = (wchar_t*)calloc(1024, 1); | |
| 238 | QString((protocol + sid + progid + exepath).toLower()).toWCharArray(data); | |
| 239 | QCryptographicHash hash(QCryptographicHash::Md5); | |
| 240 | int dataLength = wcslen(data)*2+2; | |
| 241 | hash.addData((char *)data, dataLength); | |
| 242 | int v6 = dataLength >> 2; | |
| 243 | if ((dataLength >> 2) & 1) | |
| 244 | --v6; | |
| 245 | ||
| 246 | // result of the aforementioned md5 operation | |
| 247 | unsigned int* md5 = (unsigned int*)hash.result().data(); | |
| 248 | unsigned int out[2]; | |
| 249 | unsigned int out2[2]; | |
| 250 | CS64_WordSwap((unsigned int *)data, v6, md5, out); | |
| 251 | CS64_Reversible((unsigned int *)data, v6, md5, out2); | |
| 252 | ||
| 253 | unsigned int finalResult[2] = { out[0] ^ out2[0], out[1] ^ out2[1] };
| |
| 254 | return QByteArray((char*)finalResult, sizeof(finalResult)).toBase64(); | |
| 255 | } | |
| 256 | ||
| 257 | int main(int argc, char *argv[]) | |
| 258 | {
| |
| 259 | ||
| 260 | QCoreApplication a(argc, argv); | |
| 261 | QCommandLineParser parser; | |
| 262 | parser.setApplicationDescription("Crack windows user association hashes");
| |
| 263 | parser.addHelpOption(); | |
| 264 | parser.addVersionOption(); | |
| 265 | parser.addPositionalArgument("protocol", "Protocol string");
| |
| 266 | parser.addPositionalArgument("progid", "Program ID to associate");
| |
| 267 | parser.addOption(QCommandLineOption("s",
| |
| 268 | "Security Identifier (SID) token", | |
| 269 | "sid", getSid())); | |
| 270 | parser.addOption(QCommandLineOption("p",
| |
| 271 | "Path to executable for ProgId (only " | |
| 272 | "needed for browsers http/https " | |
| 273 | "protocol)", "path")); | |
| 274 | parser.process(a); | |
| 275 | ||
| 276 | if (parser.positionalArguments().size() < 2) | |
| 277 | parser.showHelp(); | |
| 278 | QTextStream ts( stdout ); | |
| 279 | ts << genHash(parser.positionalArguments().first(), parser.value("p"),
| |
| 280 | parser.value("s"), parser.positionalArguments().last())
| |
| 281 | << endl; | |
| 282 | } |