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 | } |