1 /*
2 * chntpw.c - Offline Password Edit Utility for Windows SAM database
3 *
4 * This program uses the "ntreg" library to load and access the registry,
5 * it's main purpose is to reset password based information.
6 * It can also call the registry editor etc
7
8 * 2011-apr: Command line options added for hive expansion safe mode
9 * 2010-jun: Syskey not visible in menu, but is selectable (2)
10 * 2010-apr: Interactive menu adapts to show most relevant
11 * selections based on what is loaded
12 * 2008-mar: Minor other tweaks
13 * 2008-mar: Interactive reg ed moved out of this file, into edlib.c
14 * 2008-mar: 64 bit compatible patch by Mike Doty, via Alon Bar-Lev
15 * http://bugs.gentoo.org/show_bug.cgi?id=185411
16 * 2007-sep: Group handling extended, promotion now public
17 * 2007-sep: User edit menu, some changes to user info edit
18 * 2007-apr-may: Get and display users group memberships
19 * 2007-apr: GNU license. Some bugfixes. Cleaned up some output.
20 * 2004-aug: More stuff in regedit. Stringinput bugfixes.
21 * 2004-jan: Changed some of the verbose/debug stuff
22 * 2003-jan: Changed to use more of struct based V + some small stuff
23 * 2003-jan: Support in ntreg for adding keys etc. Editor updated.
24 * 2002-dec: New option: Specify user using RID
25 * 2002-dec: New option: blank the pass (zero hash lengths).
26 * 2001-jul: extra blank password logic (when NT or LANMAN hash missing)
27 * 2001-jan: patched & changed to use OpenSSL. Thanks to Denis Ducamp
28 * 2000-jun: changing passwords regardless of syskey.
29 * 2000-jun: syskey disable works on NT4. Not properly on NT5.
30 * 2000-jan: Attempt to detect and disable syskey
31 * 1999-feb: Now able to browse registry hives. (write support to come)
32 * See HISTORY.txt for more detailed info on history.
33 *
34 *****
35 *
36 * Copyright (c) 1997-2012 Petter Nordahl-Hagen.
37 *
38 * This program is free software; you can redistribute it and/or modify
39 * it under the terms of the GNU General Public License as published by
40 * the Free Software Foundation; version 2 of the License.
41 *
42 * This program is distributed in the hope that it will be useful,
43 * but WITHOUT ANY WARRANTY; without even the implied warranty of
44 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45 * GNU General Public License for more details.
46 *
47 * See file GPL.txt for the full license.
48 *
49 *****
50 *
51 * Information and ideas taken from pwdump by Jeremy Allison.
52 *
53 * More info from NTCrack by Jonathan Wilkins.
54 *
55 */
56
57 /* TODO: This is getting ugly. Most likely best to split up into different programs
58 * and put commun stuff in library
59 */
60
61
62 #include <stdio.h>
63 #include <sys/types.h>
64 #include <sys/stat.h>
65 #include <fcntl.h>
66 #include <ctype.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <unistd.h>
70 #include <sys/types.h>
71 #include <inttypes.h>
72
73 /* Define DOCRYPTO in makefile to include cryptostuff to be able to change passwords to
74 * a new one.
75 * Changing passwords is seems not to be working reliably on XP and newer anyway.
76 * When not defined, only reset (nulling) of passwords available.
77 */
78
79 #ifdef DOCRYPTO
80 #include <openssl/des.h>
81 #include <openssl/md4.h>
82 #endif
83
84 #define uchar u_char
85 #define MD4Init MD4_Init
86 #define MD4Update MD4_Update
87 #define MD4Final MD4_Final
88
89 #include "ntreg.h"
90 #include "sam.h"
91
92 const char chntpw_version[] = "chntpw version 0.99.6 110511 , (c) Petter N Hagen";
93
94 extern char *val_types[REG_MAX+1];
95
96 /* Global verbosity */
97 int gverbose = 0;
98
99
100 #define MAX_HIVES 10
101
102 /* Array of loaded hives */
103 struct hive *hive[MAX_HIVES+1];
104 int no_hives = 0;
105
106 /* Icky icky... globals used to refer to hives, will be
107 * set when loading, so that hives can be loaded in any order
108 */
109
110 int H_SAM = -1;
111 int H_SYS = -1;
112 int H_SEC = -1;
113 int H_SOF = -1;
114
115 int syskeyreset = 0;
116 int dirty = 0;
117 int max_sam_lock = 0;
118
119 /*
120 * of user with RID 500, because silly MS decided
121 * to localize the bloody admin-username!! AAAGHH!
122 */
123 char admuser[129]="Administrator";
124
125 /* ============================================================== */
126
127
128 #ifdef DOCRYPTO
129
130 /* Crypto-stuff & support for what we'll do in the V-value */
131
132 /* Zero out string for lanman passwd, then uppercase
133 * the supplied password and put it in here */
134
make_lanmpw(char * p,char * lm,int len)135 void make_lanmpw(char *p, char *lm, int len)
136 {
137 int i;
138
139 for (i=0; i < 15; i++) lm[i] = 0;
140 for (i=0; i < len; i++) lm[i] = toupper(p[i]);
141 }
142
143 /*
144 * Convert a 7 byte array into an 8 byte des key with odd parity.
145 */
146
str_to_key(unsigned char * str,unsigned char * key)147 void str_to_key(unsigned char *str,unsigned char *key)
148 {
149 int i;
150
151 key[0] = str[0]>>1;
152 key[1] = ((str[0]&0x01)<<6) | (str[1]>>2);
153 key[2] = ((str[1]&0x03)<<5) | (str[2]>>3);
154 key[3] = ((str[2]&0x07)<<4) | (str[3]>>4);
155 key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5);
156 key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6);
157 key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7);
158 key[7] = str[6]&0x7F;
159 for (i=0;i<8;i++) {
160 key[i] = (key[i]<<1);
161 }
162 DES_set_odd_parity((des_cblock *)key);
163 }
164
165 /*
166 * Function to convert the RID to the first decrypt key.
167 */
168
sid_to_key1(uint32_t sid,unsigned char deskey[8])169 void sid_to_key1(uint32_t sid,unsigned char deskey[8])
170 {
171 unsigned char s[7];
172
173 s[0] = (unsigned char)(sid & 0xFF);
174 s[1] = (unsigned char)((sid>>8) & 0xFF);
175 s[2] = (unsigned char)((sid>>16) & 0xFF);
176 s[3] = (unsigned char)((sid>>24) & 0xFF);
177 s[4] = s[0];
178 s[5] = s[1];
179 s[6] = s[2];
180
181 str_to_key(s,deskey);
182 }
183
184 /*
185 * Function to convert the RID to the second decrypt key.
186 */
187
sid_to_key2(uint32_t sid,unsigned char deskey[8])188 void sid_to_key2(uint32_t sid,unsigned char deskey[8])
189 {
190 unsigned char s[7];
191
192 s[0] = (unsigned char)((sid>>24) & 0xFF);
193 s[1] = (unsigned char)(sid & 0xFF);
194 s[2] = (unsigned char)((sid>>8) & 0xFF);
195 s[3] = (unsigned char)((sid>>16) & 0xFF);
196 s[4] = s[0];
197 s[5] = s[1];
198 s[6] = s[2];
199
200 str_to_key(s,deskey);
201 }
202
203 /* DES encrypt, for LANMAN */
204
E1(uchar * k,uchar * d,uchar * out)205 void E1(uchar *k, uchar *d, uchar *out)
206 {
207 des_key_schedule ks;
208 des_cblock deskey;
209
210 str_to_key(k,(uchar *)deskey);
211 #ifdef __FreeBSD__
212 des_set_key(&deskey,ks);
213 #else /* __FreeBsd__ */
214 des_set_key((des_cblock *)deskey,ks);
215 #endif /* __FreeBsd__ */
216 des_ecb_encrypt((des_cblock *)d,(des_cblock *)out, ks, DES_ENCRYPT);
217 }
218
219 #endif /* DOCRYPTO */
220
221
222 /* Get machines SID as binary (raw data)
223 * str = pointer to buffer, first 20 bytes will be filled in
224 * returns true if found, else 0
225 */
226
get_machine_sid(char * sidbuf)227 int get_machine_sid(char *sidbuf)
228 {
229
230 struct accountdb_V *v;
231 struct keyval *kv;
232 uint32_t ofs;
233 uint32_t len;
234
235 if (H_SAM >= 0) {
236
237 /* Get accoundb V value */
238 kv = get_val2buf(hive[H_SAM], NULL, 0, ACCOUNTDB_V_PATH, REG_BINARY, TPF_VK);
239 if (!kv) {
240 fprintf(stderr,"get_machine_sid: Machine SID not found in SAM\n");
241 return(0);
242 }
243
244 // hexdump(&(kv->data), 0, kv->len,1);
245
246 v = (struct accountdb_V *)&kv->data;
247 ofs = v->sid_ofs;
248 len = v->sid_len + 4;
249 ofs += 0x40;
250
251 if (len != SID_BIN_LEN) {
252 fprintf(stderr,"get_machine_sid: WARNING: SID found, but it has len=%d instead of expected %d bytes\n",len,SID_BIN_LEN);
253 }
254
255 // printf("get_machine_sid: adjusted ofs = %x, len = %x (%d)\n",ofs,len,len);
256
257
258 memcpy(sidbuf, (char *)v+ofs, len);
259
260 // hexdump(sidbuf, 0, len, 1);
261
262 return(1);
263 }
264 return(0);
265 }
266
267 /* Make string out of SID, in S-1-5 authority (NT authority)
268 * like S-1-5-21-516312364-151943033-2698651
269 * Will allocate return string (which can be of variable lenght)
270 * NOTE: caller must free it
271 * sidbuf = the SID binary data structure with it's type+counter first
272 *
273 * str = string buffer to fill, be sure to have at least space:
274 * 6 chars athority prefix (S-1-5-)
275 * 4 * 10 digits (the 4 32 bit groups)
276 * 3 for the - between the groups
277 * 1 for null termination
278 * 50 chars
279 */
sid_to_string(struct sid_binary * sidbuf)280 char *sid_to_string(struct sid_binary *sidbuf)
281 {
282
283 int cnt, i;
284 int *array;
285 char *str = NULL;
286
287 // hexdump(sidbuf, 0, 24, 1);
288
289
290 array = (int *)&sidbuf->array;
291
292 if (sidbuf->unknown0 != 1) {
293 fprintf(stderr,"sid_to_string: DEBUG: first byte unexpected: %d\n",sidbuf->unknown0);
294 }
295
296 cnt = sidbuf->sections;
297
298 // printf("sid_to_string: DEBUG: sections = %d\n",cnt);
299
300 str = str_dup("S-1-5");
301
302 for (i = 0; i < cnt; i++) {
303 str = str_catf(str,"-%u",sidbuf->array[i]);
304 }
305
306 // printf("sid_to_string: returning <%s>\n",str);
307
308
309 return(str);
310 }
311
312
313
314
315 /* Check if hive is SAM, and if it is, extract some
316 * global policy information from it, like lockout counts etc
317 */
318
check_get_samdata(int show)319 void check_get_samdata(int show)
320 {
321 struct accountdb_F *f;
322 struct keyval *v;
323
324 if (H_SAM >= 0) {
325
326 /* Get accoundb F value */
327 v = get_val2buf(hive[H_SAM], NULL, 0, ACCOUNTDB_F_PATH, REG_BINARY, TPF_VK);
328 if (!v) {
329 fprintf(stderr,"WARNING: Login counts data not found in SAM\n");
330 return;
331 }
332
333 f = (struct accountdb_F *)&v->data;
334 max_sam_lock = f->locklimit;
335
336 if (show) {
337 printf("\n* SAM policy limits:\n");
338 printf("Failed logins before lockout is: %d\n",max_sam_lock);
339 printf("Minimum password length : %d\n",f->minpwlen);
340 printf("Password history count : %d\n",f->minpwlen);
341 }
342 }
343 }
344
345
346 /* Try to decode and possibly change account lockout etc
347 * This is \SAM\Domains\Account\Users\<RID>\F
348 * It's size seems to always be 0x50.
349 * Params: RID - user ID, mode - 0 silent, 1 silent, 2 edit.
350 * Returns: ACB bits with high bit set if lockout count is >0
351 */
352
handle_F(int rid,int mode)353 short handle_F(int rid, int mode)
354 {
355
356 struct user_F *f;
357 char s[200];
358 struct keyval *v;
359 unsigned short acb;
360 int b;
361
362 if (H_SAM < 0) return(0);
363
364 /* Get users F value */
365 snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\F",rid);
366 v = get_val2buf(hive[H_SAM], NULL, 0, s, REG_BINARY, TPF_VK_EXACT);
367 if (!v) {
368 printf("Cannot find value <%s>\n",s);
369 return(0);
370 }
371
372 if (v->len < 0x48) {
373 printf("handle_F: F value is 0x%x bytes, need >= 0x48, unable to check account flags!\n",v->len);
374 FREE(v);
375 return(0);
376 }
377
378 f = (struct user_F *)&v->data;
379 acb = f->ACB_bits;
380
381 if (mode == 1) {
382 printf("Account bits: 0x%04x =\n",acb);
383
384
385 for (b=0; b < 15; b++) {
386 printf("[%s] %-15.15s | ",
387 (acb & (1<<b)) ? "X" : " ", acb_fields[b] );
388 if (b%3 == 2) printf("\n");
389 }
390
391 printf("\nFailed login count: %u, while max tries is: %u\n",f->failedcnt,max_sam_lock);
392 printf("Total login count: %u\n",f->logins);
393 }
394
395 if (mode == 2) {
396 acb |= ACB_PWNOEXP;
397 acb &= ~ACB_DISABLED;
398 acb &= ~ACB_AUTOLOCK;
399 f->ACB_bits = acb;
400 f->failedcnt = 0;
401 put_buf2val(hive[H_SAM], v, 0, s, REG_BINARY,TPF_VK_EXACT);
402 printf("Unlocked!\n");
403 }
404 return (acb | ( (f->failedcnt > 0 && f->failedcnt >= max_sam_lock)<<15 ) | (acb & ACB_AUTOLOCK)<<15 | (acb & ACB_DISABLED)<<15);
405 }
406
407
408 /* Stuff SID binary list into more easily handled arrays
409 * sidbuf = binary list buffer (not changed, may point into value structure)
410 * size = number of bytes of raw data
411 * returns pointer to array, terminated with NULL pointer.
412 * Keeps full binary data from each SID
413 * All array space is allocated, call free_sid_array() to free it.
414 */
415
make_sid_array(struct sid_binary * sidbuf,int size)416 struct sid_array *make_sid_array(struct sid_binary *sidbuf, int size)
417 {
418
419 int num = 0;
420 int sidlen;
421 struct sid_binary *sb;
422 struct sid_array *array;
423
424 CREATE(array, struct sid_array, 1);
425 array[0].len = 0;
426 array[0].sidptr = NULL;
427
428 while (size > 0) {
429
430 sidlen = sidbuf->sections * 4 + 8;
431
432 // printf("make_sid_array: sidlen = %d\n",sidlen);
433
434 ALLOC(sb, 1, sidlen);
435 memcpy(sb, sidbuf, sidlen);
436 array[num].len = sidlen;
437 array[num].sidptr = sb;
438 sidbuf = (void *)sidbuf + sidlen;
439 size -= sidlen;
440 num++;
441
442 array = realloc(array, (num + 1) * sizeof(struct sid_array));
443 array[num].len = 0;
444 array[num].sidptr = NULL;
445
446 }
447
448
449 return(array);
450
451 }
452
453 /* Free the sid array (from the function above) */
454
free_sid_array(struct sid_array * array)455 void free_sid_array(struct sid_array *array)
456 {
457
458 int num = 0;
459
460 while (array[num].sidptr) {
461 free(array[num].sidptr);
462 num++;
463 }
464
465 free(array);
466 }
467
468 /* Compare two SIDs, and return like strcmp */
sid_cmp(struct sid_binary * s1,struct sid_binary * s2)469 int sid_cmp(struct sid_binary *s1, struct sid_binary *s2)
470 {
471 int p;
472
473 if (!s1 && !s2) return(0);
474 if (!s1) return(-1);
475 if (!s2) return(1);
476
477 if (s1->sections < s2->sections) return(-1); /* s1 has shorter len, always smaller */
478 if (s1->sections > s2->sections) return(1); /* s1 has longer len, always larger */
479 /* Run compare since same length */
480 for (p = 0; p < s1->sections; p++) {
481 if (s1->array[p] < s2->array[p]) return (-1);
482 if (s1->array[p] > s2->array[p]) return (1);
483 }
484 /* At end. Thus equal */
485 return(0);
486 }
487
488
489
490 /* Get list of group members for a group
491 * Will get the SID list (as binary) into a buffer that will be allocated
492 * according to the neccessary size (based on member count)
493 * NOTE: Caller must free the buffer when not needed any more
494 * grp = group ID
495 * sidarray = pointer to pointer to sid array which will be allocated
496 * Returns number of members in the group
497 */
498
get_grp_members_sid(int grp,struct sid_array ** sarray)499 int get_grp_members_sid(int grp, struct sid_array **sarray)
500 {
501 char g[200];
502 // char groupname[128];
503
504 struct sid_array *marray;
505 struct keyval *c = NULL;
506 struct group_C *cd;
507 // int grpnamoffs, grpnamlen;
508 int mofs, mlen;
509
510 snprintf(g,180,"\\SAM\\Domains\\Builtin\\Aliases\\%08X\\C",grp);
511 c = get_val2buf(hive[H_SAM], NULL, 0, g, 0, TPF_VK_EXACT);
512 if (c) {
513 cd = (struct group_C *)&c->data;
514
515 // grpnamoffs = cd->grpname_ofs + 0x34;
516 // grpnamlen = cd->grpname_len;
517
518 // cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
519
520 // printf("get_grp_members_sid: group %x named %s has %d members\n",grp,groupname,cd->grp_members);
521
522 mofs = cd->members_ofs;
523 mlen = cd->members_len;
524
525 // printf("get_grp_members_sid: mofs = %x, mlen = %x (%d)\n", mofs,mlen,mlen);
526 // printf("get_grp_members_sid: ajusted: mofs = %x, mlen = %x (%d)\n", mofs + 0x34 ,mlen,mlen);
527
528 // hexdump(&c->data, 0, c->len, 1);
529 // hexdump(&cd->data[mofs], 0, mlen, 1);
530
531 marray = make_sid_array((struct sid_binary *)&cd->data[mofs], mlen);
532
533 *sarray = marray;
534 // free_sid_array(marray);
535
536 free(c);
537
538 } else {
539 printf("Group info for %x not found!\n",grp);
540 *sarray = NULL;
541 return(0);
542 }
543
544 return(cd->grp_members);
545
546 }
547
548 /* Put list of group members back into group C structure
549 * grp = group ID
550 * sidarray = pointer to sid array
551 * Returns true if success
552 */
553
put_grp_members_sid(int grp,struct sid_array * sarray)554 int put_grp_members_sid(int grp, struct sid_array *sarray)
555 {
556 char g[200];
557 char groupname[128];
558
559 struct keyval *c = NULL;
560 struct group_C *cd;
561 int grpnamoffs, grpnamlen;
562 int mofs, mlen;
563 int sidlen = 0;
564 void *sidptr;
565 int i;
566 char *str;
567
568 snprintf(g,180,"\\SAM\\Domains\\Builtin\\Aliases\\%08X\\C",grp);
569 c = get_val2buf(hive[H_SAM], NULL, 0, g, 0, TPF_VK_EXACT);
570 if (c) {
571 cd = (struct group_C *)&c->data;
572
573 grpnamoffs = cd->grpname_ofs + 0x34;
574 grpnamlen = cd->grpname_len;
575
576 cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
577
578 if (gverbose) printf("put_grp_members_sid: group %x named %s has %d members\n",grp,groupname,cd->grp_members);
579
580 mofs = cd->members_ofs;
581 mlen = cd->members_len;
582
583 if (gverbose) printf("put_grp_members_sid: ajusted: mofs = %x, mlen = %x (%d)\n", mofs + 0x34 ,mlen,mlen);
584
585 if (gverbose) hexdump(&c->data, 0, c->len, 1);
586
587 /* Get total size of new SID data */
588
589 for (i = 0; sarray[i].sidptr; i++) sidlen += sarray[i].len;
590
591 if (gverbose) printf("put_grp_members_sid: new count : %d, new sidlen: %x\n",i,sidlen);
592
593 /* Resize buffer with C structure */
594 c = realloc(c, 4 + mofs + sidlen + 0x34); /* offset of SIDs + sids lenght + pointer list at start */
595 c->len = 0x34 + mofs + sidlen;
596
597 cd = (struct group_C *)&c->data;
598 mofs = cd->members_ofs;
599 sidptr = &cd->data[mofs];
600
601 for (i = 0; sarray[i].sidptr; i++) {
602 if (gverbose) printf(" copying : %d len %x, at %x\n",i,sarray[i].len, sidptr);
603 str = sid_to_string(sarray[i].sidptr);
604 if (gverbose) printf(" Member # %d = <%s>\n", i, str);
605 FREE(str);
606 memcpy(sidptr, sarray[i].sidptr, sarray[i].len);
607 sidptr += sarray[i].len;
608 }
609
610 cd->members_len = sidlen; /* Update member count in C struct */
611 cd->grp_members = i;
612
613 if (gverbose) hexdump(&c->data, 0, c->len, 1);
614
615 if (!put_buf2val(hive[H_SAM], c, 0, g, 0, TPF_VK_EXACT)) {
616 fprintf(stderr,"put_grp_members_sid: could not write back group info in value %s\n",g);
617 free(c);
618 return(0);
619 }
620
621
622 free(c);
623
624 } else {
625 printf("Group info for %x not found!\n",grp);
626 return(0);
627 }
628
629 return(1);
630
631 }
632
633
634 /* List groups, optionally with members */
635
list_groups(int listmembers)636 void list_groups(int listmembers) {
637
638 struct ex_data ex;
639 struct sid_array *sids = NULL;
640 int nkofs;
641 unsigned int grp;
642 int count = 0, countri = 0;
643 struct keyval *c = NULL;
644 struct group_C *cd;
645 int grpnamoffs, grpnamlen, i;
646 char groupname[200];
647 char *str;
648
649
650 if (H_SAM < 0) return;
651
652
653 nkofs = trav_path(hive[H_SAM], 0,"\\SAM\\Domains\\Builtin\\Aliases",0);
654 if (!nkofs) {
655 printf("list_groups: Cannot find group list in registry! (is this a SAM-hive?)\n");
656 return;
657 }
658
659 /* Pick up all subkeys here, they are local groups */
660 while ((ex_next_n(hive[H_SAM], nkofs+4, &count, &countri, &ex) > 0)) {
661
662 // printf("Group ID %s\n",ex.name);
663 sscanf(ex.name,"%x",&grp);
664
665 /* Groups keys have a C value, get it and pick up the name etc */
666 /* Some other keys also exists (Members, Names at least), but we skip them */
667
668 c = get_val2buf(hive[H_SAM], NULL, ex.nkoffs+4, "C", 0, TPF_VK_EXACT);
669 if (c) {
670 cd = (struct group_C *)&c->data;
671 grpnamoffs = cd->grpname_ofs + 0x34;
672 grpnamlen = cd->grpname_len;
673
674 cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
675
676 printf("Group #%x named <%s> has %d members\n",grp,groupname,cd->grp_members);
677
678 if (listmembers) {
679 get_grp_members_sid(grp, &sids);
680
681 for (i = 0; sids[i].sidptr; i++) {
682 str = sid_to_string(sids[i].sidptr);
683 printf(" Member # %d = <%s>\n", i, str);
684 FREE(str);
685 }
686 free_sid_array(sids);
687 }
688 }
689
690 }
691
692 }
693
694 /* Get group IDs a user is member of
695 * rid = user ID
696 * returns: since value data is just an array of grp ids (4 bytes each),
697 * just return the keyval structure (size + data)
698 * caller must free() keyval
699 */
700
get_user_grpids(int rid)701 struct keyval *get_user_grpids(int rid)
702 {
703 char s[200];
704 struct sid_binary sid;
705 char *sidstr;
706
707 int nk = 0;
708 struct keyval *m = NULL;
709 int count = 0;
710 int size;
711
712 if (!rid || (H_SAM < 0)) return(NULL);
713
714 if (!get_machine_sid((char *)&sid)) {
715 fprintf(stderr,"get_user_grpids: Could not find machine SID\n");
716 return(0);
717 }
718
719 sidstr = sid_to_string(&sid);
720
721 /* Get member list for user on this machine */
722 snprintf(s,180,"\\SAM\\Domains\\Builtin\\Aliases\\Members\\%s\\%08X",sidstr,rid);
723
724 free(sidstr);
725
726 /* Now, the TYPE field is the number of groups the user is member of */
727 /* Don't we just love the inconsistent use of fields!! */
728 nk = trav_path(hive[H_SAM], 0, s, 0);
729 if (!nk) {
730 /* This probably means user is not in any group. Seems to be the case
731 for a couple of XPs built in support / guest users. So just return */
732 if (gverbose) printf("get_user_grpids: Cannot find RID under computer SID <%s>\n",s);
733 return(NULL);
734 }
735 nk += 4;
736 count = get_val_type(hive[H_SAM],nk,"@",TPF_VK_EXACT);
737 if (count == -1) {
738 printf("get_user_grpids: Cannot find value <%s\\@>\n",s);
739 return(NULL);
740 }
741
742 // printf("get_user_grpids: User is member of %d groups:\n",count);
743
744 /* This is the data size */
745 size = get_val_len(hive[H_SAM],nk,"@",TPF_VK_EXACT);
746
747 /* It should be 4 bytes for each group */
748 if (gverbose) printf("Data size %d bytes.\n",size);
749 if (size != count * 4) {
750 printf("get_user_grpids: DEBUG: Size is not 4 * count! May not matter anyway. Continuing..\n");
751 }
752
753 m = get_val2buf(hive[H_SAM], NULL, nk, "@", 0, TPF_VK_EXACT);
754 if (!m) {
755 printf("get_user_grpids: Could not get value data! Giving up.\n");
756 return(NULL);
757 }
758
759 return(m);
760 }
761
762 /* Put/set group IDs a user is member of
763 * rid = user ID
764 * val = keyval structure of data, actual value data is a list
765 * of ints, one per group
766 * returns true if successful setting the value
767 */
768
put_user_grpids(int rid,struct keyval * val)769 int put_user_grpids(int rid, struct keyval *val)
770 {
771 char s[200];
772 struct sid_binary sid;
773 char *sidstr;
774
775 int newcount = 0;
776 int nk = 0;
777 int count = 0;
778
779 if (!rid || (H_SAM < 0)) return(0);
780
781 if (!val || !val->len) return(0);
782
783 if (!get_machine_sid((char *)&sid)) {
784 fprintf(stderr,"put_user_grpids: Could not find machine SID\n");
785 return(0);
786 }
787
788 sidstr = sid_to_string(&sid);
789
790 /* Get member list for user on this machine */
791 snprintf(s,180,"\\SAM\\Domains\\Builtin\\Aliases\\Members\\%s\\%08X",sidstr,rid);
792
793 free(sidstr);
794
795 /* Now, the TYPE field is the number of groups the user is member of */
796
797 nk = trav_path(hive[H_SAM], 0, s, 0);
798 if (!nk) {
799 /* This probably means user is not in any group. Seems to be the case
800 for a couple of XPs built in support / guest users. So just return */
801 if (gverbose) printf("put_user_grpids: Cannot find RID under computer SID <%s>\n",s);
802 return(0);
803 }
804
805 nk += 4;
806
807 count = get_val_type(hive[H_SAM],nk,"@",TPF_VK_EXACT);
808 if (count == -1) {
809 printf("put_user_grpids: Cannot find value <%s\\@>\n",s);
810 return(1);
811 }
812
813 if (gverbose) printf("put_user_grpids: User was member of %d groups:\n",count);
814
815 /* This is the data size */
816 /* It should be 4 bytes for each group */
817
818 newcount = val->len >> 2;
819
820 if (gverbose) printf("Data size %d bytes.\n",val->len);
821 if (val->len != newcount << 2) {
822 printf("set_user_grpids: DEBUG: Size is not 4 * count! May not matter anyway. Continuing..\n");
823 }
824
825 if (gverbose) printf("put_user_grpids: User is NOW member of %d groups:\n",newcount);
826
827 set_val_type(hive[H_SAM],nk,"@",TPF_VK_EXACT,newcount);
828
829 if (!put_buf2val(hive[H_SAM], val, nk, "@", 0, TPF_VK_EXACT) ) {
830 printf("put_user_grpids: Could not set reg value data!\n");
831 return(0);
832 }
833
834 return(1);
835 }
836
837
838
839
840
841 /* List users membership or check if admin (is in admin group)
842 * rid - users rid
843 * check - if 1 just check if admin, do not list
844 * returns true if user is admin
845 */
846
list_user_groups(int rid,int check)847 int list_user_groups(int rid, int check)
848 {
849 char g[200];
850
851 char groupname[128];
852 struct keyval *m = NULL, *c = NULL;
853 struct group_C *cd;
854 unsigned int *grps;
855 int count = 0, isadmin = 0;
856 int i, grp, grpnamoffs, grpnamlen;
857
858 if (!rid || (H_SAM < 0)) return(0);
859
860 m = get_user_grpids(rid);
861
862 if (!m) return(0);
863
864 grps = (unsigned int *)&m->data;
865 count = m->len >> 2;
866
867 for (i = 0; i < count; i++) {
868 grp = grps[i];
869 if (!check) printf("%08x ",grp);
870
871 if (grp == 0x220) isadmin = 1;
872
873 if (!check) {
874 snprintf(g,180,"\\SAM\\Domains\\Builtin\\Aliases\\%08X\\C",grp);
875 c = get_val2buf(hive[H_SAM], NULL, 0, g, 0, TPF_VK_EXACT);
876 if (c) {
877 cd = (struct group_C *)&c->data;
878 grpnamoffs = cd->grpname_ofs + 0x34;
879 grpnamlen = cd->grpname_len;
880
881 cheap_uni2ascii((char *)cd + grpnamoffs, groupname, grpnamlen);
882
883 printf("= %s (which has %d members)\n",groupname,cd->grp_members);
884
885 // get_grp_members_sid(grp, &sidbuf);
886
887 } else {
888 printf("Group info for %x not found!\n",grp);
889 }
890 }
891 }
892
893 free(m);
894
895 return(isadmin);
896 }
897
898
899
900
901 /* Add user to a group
902 * rid = user RID
903 * grp = group ID
904 * return true if success
905 */
906
add_user_to_grp(int rid,int grp)907 int add_user_to_grp(int rid, int grp)
908 {
909 struct keyvala *usrgrplist, *newusrgrplist;
910 struct sid_array *sarray, *narray;
911 struct sid_binary *usid;
912 struct sid_binary msid;
913 int members, newmembers;
914 char *str;
915 int ugcnt;
916 int o,n,hit,c;
917 unsigned int *og, *ng;
918
919
920
921 if (!rid || !grp || (H_SAM < 0)) return(0);
922
923 /* Build user SID (add RID to machine SID) */
924
925 if (!get_machine_sid((char *)&msid)) {
926 fprintf(stderr,"get_user_grpids: Could not find machine SID\n");
927 return(0);
928 }
929
930 /* well, and hope that machine SID is always same size here too */
931 ALLOC(usid, sizeof(struct sid_binary) +4, 1);
932
933 memcpy(usid, &msid, sizeof(struct sid_binary));
934
935 usid->array[4] = rid; /* Tack RID on at end */
936 usid->sections = 5;
937
938 str = sid_to_string(usid);
939
940 if (gverbose) printf("add_user_to_grp: user SID is <%s>\n", str);
941
942 free(str);
943
944 /* With all of the above functions, it should now just be to get
945 * the list of groups the user account has listed under it
946 * and the list of users the group has listed under it
947 */
948
949 usrgrplist = (struct keyvala *)get_user_grpids(rid);
950
951 if (!usrgrplist) {
952 printf("add_user_to_grp: user # %x not found!\n",rid);
953 return(0);
954 }
955
956
957 members = get_grp_members_sid(grp, &sarray);
958
959 if (!sarray) {
960 printf("add_user_to_grp: group # %x not found!\n",grp);
961 FREE(usrgrplist);
962 return(0);
963 }
964
965
966 /* Add the group to the users list of groups it is member of */
967
968 ugcnt = usrgrplist->len >> 2; /* Count of groups already on user */
969
970 /* Allocate new larger usrgrplist for one more entry */
971
972 ALLOC(newusrgrplist, usrgrplist->len + 4 + 4, 1);
973 bzero(newusrgrplist, usrgrplist->len + 4 + 4); /* for sanity.. */
974 newusrgrplist->len = usrgrplist->len + 4;
975
976 og = (unsigned int *)&usrgrplist->data;
977 ng = (unsigned int *)&newusrgrplist->data;
978
979 if (gverbose) printf("usrgrplist-len = %d\n", usrgrplist->len);
980
981
982 #if 0 /* If list should be sorted, but seems windows does not do that? */
983
984 /* Copy over users group list, adding in where needed */
985
986 hit = 0;
987 for (o = 0, n = 0; o < ugcnt; o++, n++) {
988 printf(":: %d %d : %x\n",o,n,og[o]);
989 if (og[o] == grp) { /* Was already in there, so just don't increase size.. */
990 newusrgrplist->len = usrgrplist->len;
991 hit = 1;
992 }
993 if (og[o] > grp && !hit) {
994 ng[n++] = grp; /* Next is higher, so insert out rid */
995 hit = 1;
996 printf(" -- insert\n");
997 }
998 ng[n] = og[o];
999 }
1000 printf("n = %d\n",n);
1001 if (!hit) ng[n] = grp; /* Insert at end if we run down */
1002
1003 #endif
1004
1005 /* Copy over users group list, checking if already there */
1006
1007 hit = 0;
1008 for (o = 0; o < ugcnt; o++) {
1009 if (gverbose) printf(":: %d : %x\n",o,og[o]);
1010 if (og[o] == grp) { /* Was already in there, so just don't increase size.. */
1011 newusrgrplist->len = usrgrplist->len;
1012 hit = 1;
1013 if (gverbose) printf(" -- match\n");
1014 }
1015 ng[o] = og[o];
1016 }
1017 if (gverbose) printf(" - end of list at o = %d\n",o);
1018
1019 if (!hit) ng[o] = grp; /* Just stuff new group in at end if not already in list */
1020
1021 if (gverbose) {
1022 for (o = 0; o < (newusrgrplist->len >> 2); o++) {
1023 printf("grp index %d = %08x\n", o, ng[o]);
1024 }
1025 }
1026 /* And then we add the user SID into the groups list of members */
1027
1028 if (gverbose) {
1029 printf("add_user_to_grp: grp memberlist BEFORE:\n");
1030
1031 for (o = 0; sarray[o].sidptr; o++) {
1032 str = sid_to_string(sarray[o].sidptr);
1033 printf(" Member # %d = <%s>\n", o, str);
1034 FREE(str);
1035 }
1036 }
1037
1038 newmembers = members + 1;
1039 ALLOC(narray, sizeof(struct sid_array) * (newmembers + 2), 1); /* Add one entry size */
1040
1041 if (gverbose) printf("members = %d\n", members);
1042
1043 hit = 0;
1044 for (o = 0, n = 0; o <= members; o++, n++) {
1045 c = sid_cmp(sarray[o].sidptr, usid); /* Compare slot with new SID */
1046 if (gverbose) printf("sid_cmp returns %d\n",c);
1047 if (c == 0) {
1048 newmembers--; /* Already there, don't change anything */
1049 hit = 1;
1050 }
1051 if (!hit && ((c > 0) || !sarray[o].sidptr)) { /* Next is higher, insert new SID */
1052 if (gverbose) printf(" -- add\n");
1053 narray[n].len = usid->sections * 4 + 8; /* Hmm */
1054 narray[n].sidptr = usid;
1055 n++;
1056 hit = 1;
1057 }
1058 narray[n].len = sarray[o].len;
1059 narray[n].sidptr = sarray[o].sidptr;
1060 }
1061
1062 if (gverbose) {
1063 printf("add_user_to_grp: grp memberlist AFTER:\n");
1064
1065
1066 for (o = 0; narray[o].sidptr; o++) {
1067 str = sid_to_string(narray[o].sidptr);
1068 printf(" Member # %d = <%s>\n", o, str);
1069 FREE(str);
1070 }
1071 }
1072
1073 /* Write new lists back to registry */
1074 if (!put_user_grpids(rid, (struct keyval *)newusrgrplist)) {
1075 fprintf(stderr, "add_user_to_grp: failed storing users group list\n");
1076 } else if (!put_grp_members_sid(grp, narray)) {
1077 fprintf(stderr,"add_user_to_grp: failed storing groups user list\n");
1078 put_user_grpids(rid, (struct keyval *)usrgrplist); /* Try to roll back */
1079 }
1080
1081 FREE(usrgrplist);
1082 FREE(newusrgrplist);
1083 free_sid_array(narray);
1084 FREE(sarray); /* Pointers was copied to narray, and freed above, just free the array here */
1085
1086 return(1);
1087
1088 }
1089
1090 /* Remove user from a group
1091 * rid = user RID
1092 * grp = group ID
1093 * return true if success
1094 */
1095
remove_user_from_grp(int rid,int grp)1096 int remove_user_from_grp(int rid, int grp)
1097 {
1098 struct keyvala *usrgrplist, *newusrgrplist;
1099 struct sid_array *sarray, *narray;
1100 struct sid_binary *usid;
1101 struct sid_binary msid;
1102 int members, newmembers;
1103 char *str;
1104 int ugcnt;
1105 int o,n,hit,c;
1106 unsigned int *og, *ng;
1107
1108
1109
1110 if (!rid || !grp || (H_SAM < 0)) return(0);
1111
1112 /* Build user SID (add RID to machine SID) */
1113
1114 if (!get_machine_sid((char *)&msid)) {
1115 fprintf(stderr,"get_user_grpids: Could not find machine SID\n");
1116 return(0);
1117 }
1118
1119 /* well, and hope that machine SID is always same size here too */
1120 ALLOC(usid, sizeof(struct sid_binary) +4, 1);
1121
1122 memcpy(usid, &msid, sizeof(struct sid_binary));
1123
1124 usid->array[4] = rid; /* Tack RID on at end */
1125 usid->sections = 5;
1126
1127 if (gverbose) {
1128 str = sid_to_string(usid);
1129 printf("remove_user_from_grp: user SID is <%s>\n", str);
1130 free(str);
1131 }
1132
1133 /* With all of the above functions, it should now just be to get
1134 * the list of groups the user account has listed under it
1135 * and the list of users the group has listed under it
1136 */
1137
1138 usrgrplist = (struct keyvala *)get_user_grpids(rid);
1139
1140 if (!usrgrplist) {
1141 printf("remove_user_from_grp: user # %x not found!\n",rid);
1142 return(0);
1143 }
1144
1145
1146 members = get_grp_members_sid(grp, &sarray);
1147
1148 if (!sarray) {
1149 printf("remove_user_from_grp: group # %x not found!\n",grp);
1150 FREE(usrgrplist);
1151 return(0);
1152 }
1153
1154
1155 /* Add the group to the users list of groups it is member of */
1156
1157 ugcnt = usrgrplist->len >> 2; /* Count of groups already on user */
1158
1159 /* Allocate same size usrgrplist, since we don't know if we are in there and need to be removed */
1160
1161 ALLOC(newusrgrplist, usrgrplist->len + 4, 1);
1162 bzero(newusrgrplist, usrgrplist->len + 4); /* for sanity.. */
1163 newusrgrplist->len = usrgrplist->len;
1164
1165 og = (unsigned int *)&usrgrplist->data;
1166 ng = (unsigned int *)&newusrgrplist->data;
1167
1168 if (gverbose) printf("usrgrplist-len = %d\n", usrgrplist->len);
1169
1170
1171 /* Copy over users group list, if relevant group found, don't copy it over */
1172
1173 hit = 0;
1174 for (o = 0; o < ugcnt; o++) {
1175 if (gverbose) printf(":: %d : %x\n",o,og[o]);
1176 if (og[o] == grp) { /* Group found */
1177 hit = 1;
1178 if (gverbose) printf(" -- match\n");
1179 } else {
1180 ng[o-hit] = og[o];
1181 }
1182 }
1183 if (gverbose) printf(" - end of list at o = %d\n",o);
1184 if (hit) {
1185 newusrgrplist->len -= 4; /* Decrease size if found */
1186 } else {
1187 fprintf(stderr, "remove_user_from_grp: NOTE: group not in users list of groups, may mean user not member at all. Safe. Continuing.\n");
1188 }
1189
1190
1191 if (gverbose) {
1192 for (o = 0; o < (newusrgrplist->len >> 2); o++) {
1193 printf("grp index %d = %08x\n", o, ng[o]);
1194 }
1195
1196 /* Remove the user SID from the groups list of members */
1197
1198 printf("remove_user_from_grp: grp memberlist BEFORE:\n");
1199
1200 for (o = 0; sarray[o].sidptr; o++) {
1201 str = sid_to_string(sarray[o].sidptr);
1202 printf(" Member # %d = <%s>\n", o, str);
1203 FREE(str);
1204 }
1205 }
1206
1207 newmembers = members;
1208 ALLOC(narray, sizeof(struct sid_array) * (newmembers + 2), 1);
1209
1210 if (gverbose) printf("members = %d\n", members);
1211
1212
1213 hit = 0;
1214 for (o = 0, n = 0; o <= members; o++, n++) {
1215 c = sid_cmp(sarray[o].sidptr, usid); /* Compare slot with new SID */
1216 if (gverbose) printf("sid_cmp returns %d\n",c);
1217 if (c == 0) {
1218 newmembers--; /* Found, skip copy and decrease list size */
1219 hit = 1;
1220 n--;
1221 } else {
1222 narray[n].len = sarray[o].len; /* Copy entry */
1223 narray[n].sidptr = sarray[o].sidptr;
1224 }
1225 }
1226 if (!hit) fprintf(stderr, "remove_user_from_grp: NOTE: user not in groups list of users, may mean user was not member at all. Does not matter, continuing.\n");
1227
1228 if (gverbose) {
1229 printf("remove_user_from_grp: grp memberlist AFTER:\n");
1230 for (o = 0; narray[o].sidptr; o++) {
1231 str = sid_to_string(narray[o].sidptr);
1232 printf(" Member # %d = <%s>\n", o, str);
1233 FREE(str);
1234 }
1235 }
1236
1237 /* Write new lists back to registry */
1238 if (!put_user_grpids(rid, (struct keyval *)newusrgrplist)) {
1239 fprintf(stderr, "remove_user_from_grp: failed storing users group list\n");
1240 } else if (!put_grp_members_sid(grp, narray)) {
1241 fprintf(stderr,"remvoe_user_from_grp: failed storing groups user list\n");
1242 put_user_grpids(rid, (struct keyval *)usrgrplist); /* Try to roll back */
1243 }
1244
1245 FREE(usrgrplist);
1246 FREE(newusrgrplist);
1247 free_sid_array(narray);
1248 FREE(sarray); /* Pointers was copied to narray, and freed above, just free the array here */
1249
1250 return(1);
1251
1252 }
1253
1254
1255
1256
1257 /* Promote user into administrators group (group ID 0x220)
1258 * And remove from all others...
1259 * rid - users rid
1260 * no returns yet
1261 */
1262
promote_user(int rid)1263 void promote_user(int rid)
1264 {
1265
1266 char yn[5];
1267
1268 if (!rid || (H_SAM < 0)) return;
1269
1270 printf("\n=== PROMOTE USER\n\n");
1271 printf("Will add the user to the administrator group (0x220)\n"
1272 "and to the users group (0x221). That should usually be\n"
1273 "what is needed to log in and get administrator rights.\n"
1274 "Also, remove the user from the guest group (0x222), since\n"
1275 "it may forbid logins.\n\n");
1276 printf("(To add or remove user from other groups, please other menu selections)\n\n");
1277 printf("Note: You may get some errors if the user is already member of some\n"
1278 "of these groups, but that is no problem.\n\n");
1279
1280 fmyinput("Do it? (y/n) [n] : ", yn, 3);
1281
1282 if (*yn == 'y') {
1283
1284 printf("* Adding to 0x220 (Administrators) ...\n");
1285 add_user_to_grp(rid, 0x220);
1286 printf("* Adding to 0x221 (Users) ...\n");
1287 add_user_to_grp(rid, 0x221);
1288
1289 printf("* Removing from 0x222 (Guests) ...\n");
1290 remove_user_from_grp(rid, 0x222);
1291
1292 printf("\nPromotion DONE!\n");
1293
1294 } else {
1295 printf("Nothing done, going back..\n");
1296 }
1297
1298 }
1299
1300
interactive_remusrgrp(int rid)1301 void interactive_remusrgrp(int rid)
1302 {
1303 char inp[20];
1304 int grp, l;
1305
1306 printf("\n=== REMOVE USER FROM A GROUP\n");
1307
1308 list_user_groups(rid,0);
1309
1310 printf("\nPlease enter group number (for example 220), or 0 to go back\n");
1311 l = fmyinput("Group number? : ",inp,16);
1312 sscanf(inp, "%x", &grp);
1313
1314 if (!grp) {
1315 printf("Going back..\n");
1316 return;
1317 }
1318
1319 printf("Removing user from group 0x%x (%d)\n",grp,grp);
1320 printf("Error messages if the user was not member of the group are harmless\n\n");
1321
1322 remove_user_from_grp(rid, grp);
1323
1324 printf("\nFinished removing user from group\n\n");
1325
1326 }
1327
1328
interactive_addusrgrp(int rid)1329 void interactive_addusrgrp(int rid)
1330 {
1331 char inp[20];
1332 int grp, l;
1333
1334 printf("\n == ADD USER TO A GROUP\n");
1335
1336 list_groups(0);
1337
1338 printf("\nPlease enter group number (for example 220), or 0 to go back\n");
1339 l = fmyinput("Group number? : ",inp,16);
1340 sscanf(inp, "%x", &grp);
1341
1342 if (!grp) {
1343 printf("Going back..\n");
1344 return;
1345 }
1346
1347 printf("Adding user to group 0x%x (%d)\n",grp,grp);
1348 printf("Error messages if the user was already member of the group are harmless\n\n");
1349
1350 add_user_to_grp(rid, grp);
1351
1352 printf("\nFinished adding user to group\n\n");
1353
1354
1355 }
1356
1357
1358 /* Decode the V-struct, and change the password
1359 * vofs - offset into SAM buffer, start of V struct
1360 * rid - the users RID, required for the DES decrypt stage
1361 *
1362 * Some of this is ripped & modified from pwdump by Jeremy Allison
1363 *
1364 */
change_pw(char * buf,int rid,int vlen,int stat)1365 char *change_pw(char *buf, int rid, int vlen, int stat)
1366 {
1367
1368 int pl;
1369 char *vp;
1370 static char username[128],fullname[128];
1371 char comment[128], homedir[128], newp[20];
1372 int username_offset,username_len;
1373 int fullname_offset,fullname_len;
1374 int comment_offset,comment_len;
1375 int homedir_offset,homedir_len;
1376 int ntpw_len,lmpw_len,ntpw_offs,lmpw_offs;
1377 int dontchange = 0;
1378 unsigned short acb;
1379 struct user_V *v;
1380
1381 #ifdef DOCRYPT
1382 int i;
1383 char md4[32],lanman[32];
1384 char newunipw[34], despw[20], newlanpw[16], newlandes[20];
1385 des_key_schedule ks1, ks2;
1386 des_cblock deskey1, deskey2;
1387 MD4_CTX context;
1388 unsigned char digest[16];
1389 uchar x1[] = {0x4B,0x47,0x53,0x21,0x40,0x23,0x24,0x25};
1390 #endif
1391
1392
1393 v = (struct user_V *)buf;
1394 vp = buf;
1395
1396 username_offset = v->username_ofs;
1397 username_len = v->username_len;
1398 fullname_offset = v->fullname_ofs;
1399 fullname_len = v->fullname_len;
1400 comment_offset = v->comment_ofs;
1401 comment_len = v->comment_len;
1402 homedir_offset = v->homedir_ofs;
1403 homedir_len = v->homedir_len;
1404 lmpw_offs = v->lmpw_ofs;
1405 lmpw_len = v->lmpw_len;
1406 ntpw_offs = v->ntpw_ofs;
1407 ntpw_len = v->ntpw_len;
1408
1409 if (!rid) {
1410 printf("No RID given. Unable to change passwords..\n");
1411 return(0);
1412 }
1413
1414 if (gverbose) {
1415 printf("lmpw_offs: 0x%x, lmpw_len: %d (0x%x)\n",lmpw_offs,lmpw_len,lmpw_len);
1416 printf("ntpw_offs: 0x%x, ntpw_len: %d (0x%x)\n",ntpw_offs,ntpw_len,ntpw_len);
1417 }
1418
1419 *username = 0;
1420 *fullname = 0;
1421 *comment = 0;
1422 *homedir = 0;
1423
1424 if(username_len <= 0 || username_len > vlen ||
1425 username_offset <= 0 || username_offset >= vlen ||
1426 comment_len < 0 || comment_len > vlen ||
1427 fullname_len < 0 || fullname_len > vlen ||
1428 homedir_offset < 0 || homedir_offset >= vlen ||
1429 comment_offset < 0 || comment_offset >= vlen ||
1430 lmpw_offs < 0 || lmpw_offs >= vlen)
1431 {
1432 if (stat != 1) printf("change_pw: Not a legal V struct? (negative struct lengths)\n");
1433 return(NULL);
1434 }
1435
1436 /* Offsets in top of struct is relative to end of pointers, adjust */
1437 username_offset += 0xCC;
1438 fullname_offset += 0xCC;
1439 comment_offset += 0xCC;
1440 homedir_offset += 0xCC;
1441 ntpw_offs += 0xCC;
1442 lmpw_offs += 0xCC;
1443
1444 cheap_uni2ascii(vp + username_offset,username,username_len);
1445 cheap_uni2ascii(vp + fullname_offset,fullname,fullname_len);
1446 cheap_uni2ascii(vp + comment_offset,comment,comment_len);
1447 cheap_uni2ascii(vp + homedir_offset,homedir,homedir_len);
1448
1449 #if 0
1450 /* Reset hash-lengths to 16 if syskey has been reset */
1451 if (syskeyreset && ntpw_len > 16 && !stat) {
1452 ntpw_len = 16;
1453 lmpw_len = 16;
1454 ntpw_offs -= 4;
1455 (unsigned int)*(vp+0xa8) = ntpw_offs - 0xcc;
1456 *(vp + 0xa0) = 16;
1457 *(vp + 0xac) = 16;
1458 }
1459 #endif
1460
1461 printf("\nRID : %04d [%04x]\n",rid,rid);
1462 printf("Username: %s\n",username);
1463 printf("fullname: %s\n",fullname);
1464 printf("comment : %s\n",comment);
1465 printf("homedir : %s\n\n",homedir);
1466
1467 list_user_groups(rid,0);
1468 printf("\n");
1469
1470 acb = handle_F(rid,1);
1471
1472 if (lmpw_len < 16 && gverbose) {
1473 printf("** LANMAN password not set. User MAY have a blank password.\n** Usually safe to continue. Normal in Vista\n");
1474 }
1475
1476 if (ntpw_len < 16) {
1477 printf("** No NT MD4 hash found. This user probably has a BLANK password!\n");
1478 if (lmpw_len < 16) {
1479 printf("** No LANMAN hash found either. Try login with no password!\n");
1480 dontchange = 1;
1481 } else {
1482 printf("** LANMAN password IS however set. Will now install new password as NT pass instead.\n");
1483 printf("** NOTE: Continue at own risk!\n");
1484 ntpw_offs = lmpw_offs;
1485 *(vp+0xa8) = ntpw_offs - 0xcc;
1486 ntpw_len = 16;
1487 lmpw_len = 0;
1488 }
1489 }
1490
1491 if (gverbose) {
1492 hexprnt("Crypted NT pw: ",(unsigned char *)(vp+ntpw_offs),16);
1493 hexprnt("Crypted LM pw: ",(unsigned char *)(vp+lmpw_offs),16);
1494 }
1495
1496 #ifdef DOCRYPTO
1497 /* Get the two decrpt keys. */
1498 sid_to_key1(rid,(unsigned char *)deskey1);
1499 des_set_key((des_cblock *)deskey1,ks1);
1500 sid_to_key2(rid,(unsigned char *)deskey2);
1501 des_set_key((des_cblock *)deskey2,ks2);
1502
1503 /* Decrypt the NT md4 password hash as two 8 byte blocks. */
1504 des_ecb_encrypt((des_cblock *)(vp+ntpw_offs ),
1505 (des_cblock *)md4, ks1, DES_DECRYPT);
1506 des_ecb_encrypt((des_cblock *)(vp+ntpw_offs + 8),
1507 (des_cblock *)&md4[8], ks2, DES_DECRYPT);
1508
1509 /* Decrypt the lanman password hash as two 8 byte blocks. */
1510 des_ecb_encrypt((des_cblock *)(vp+lmpw_offs),
1511 (des_cblock *)lanman, ks1, DES_DECRYPT);
1512 des_ecb_encrypt((des_cblock *)(vp+lmpw_offs + 8),
1513 (des_cblock *)&lanman[8], ks2, DES_DECRYPT);
1514
1515 if (gverbose) {
1516 hexprnt("MD4 hash : ",(unsigned char *)md4,16);
1517 hexprnt("LANMAN hash : ",(unsigned char *)lanman,16);
1518 }
1519 #endif /* DOCRYPTO */
1520
1521
1522 printf("\n- - - - User Edit Menu:\n");
1523 printf(" 1 - Clear (blank) user password\n");
1524 printf("%s2 - Unlock and enable user account%s\n", (acb & 0x8000) ? " " : "(",
1525 (acb & 0x8000) ? " [probably locked now]" : ") [seems unlocked already]");
1526 printf(" 3 - Promote user (make user an administrator)\n");
1527 printf(" 4 - Add user to a group\n");
1528 printf(" 5 - Remove user from a group\n");
1529 #ifdef DOCRYPTO
1530 printf(" 9 - Edit (set new) user password (careful with this on XP or Vista)\n");
1531 #endif
1532 printf(" q - Quit editing user, back to user select\n");
1533
1534 pl = fmyinput("Select: [q] > ",newp,16);
1535
1536 if ( (pl < 1) || (*newp == 'q') || (*newp == 'Q')) return(0);
1537
1538
1539 if (*newp == '2') {
1540 acb = handle_F(rid,2);
1541 return(username);
1542 }
1543
1544 if (*newp == '3') {
1545 promote_user(rid);
1546 return(username);
1547 }
1548
1549 if (*newp == '4') {
1550 interactive_addusrgrp(rid);
1551 return(username);
1552 }
1553
1554 if (*newp == '5') {
1555 interactive_remusrgrp(rid);
1556 return(username);
1557 }
1558
1559
1560 #ifdef DOCRYPT
1561 if (*newp == '9') { /* Set new password */
1562
1563 if (dontchange) {
1564 printf("Sorry, unable to edit since password seems blank already (thus no space for it)\n");
1565 return(0);
1566 }
1567
1568 pl = fmyinput("New Password: ",newp,16);
1569
1570 if (pl < 1) {
1571 printf("No change.\n");
1572 return(0);
1573 }
1574
1575 cheap_ascii2uni(newp,newunipw,pl);
1576
1577 make_lanmpw(newp,newlanpw,pl);
1578
1579 /* printf("Ucase Lanman: %s\n",newlanpw); */
1580
1581 MD4Init (&context);
1582 MD4Update (&context, newunipw, pl<<1);
1583 MD4Final (digest, &context);
1584
1585 if (gverbose) hexprnt("\nNEW MD4 hash : ",digest,16);
1586
1587 E1((uchar *)newlanpw, x1, (uchar *)lanman);
1588 E1((uchar *)newlanpw+7, x1, (uchar *)lanman+8);
1589
1590 if (gverbose) hexprnt("NEW LANMAN hash : ",(unsigned char *)lanman,16);
1591
1592 /* Encrypt the NT md4 password hash as two 8 byte blocks. */
1593 des_ecb_encrypt((des_cblock *)digest,
1594 (des_cblock *)despw, ks1, DES_ENCRYPT);
1595 des_ecb_encrypt((des_cblock *)(digest+8),
1596 (des_cblock *)&despw[8], ks2, DES_ENCRYPT);
1597
1598 des_ecb_encrypt((des_cblock *)lanman,
1599 (des_cblock *)newlandes, ks1, DES_ENCRYPT);
1600 des_ecb_encrypt((des_cblock *)(lanman+8),
1601 (des_cblock *)&newlandes[8], ks2, DES_ENCRYPT);
1602
1603 if (gverbose) {
1604 hexprnt("NEW DES crypt : ",(unsigned char *)despw,16);
1605 hexprnt("NEW LANMAN crypt: ",(unsigned char *)newlandes,16);
1606 }
1607
1608 /* Reset hash length to 16 if syskey enabled, this will cause
1609 * a conversion to syskey-hashes upon next boot */
1610 if (syskeyreset && ntpw_len > 16) {
1611 ntpw_len = 16;
1612 lmpw_len = 16;
1613 ntpw_offs -= 4;
1614 *(vp+0xa8) = (unsigned int)(ntpw_offs - 0xcc);
1615 *(vp + 0xa0) = 16;
1616 *(vp + 0xac) = 16;
1617 }
1618
1619 for (i = 0; i < 16; i++) {
1620 *(vp+ntpw_offs+i) = (unsigned char)despw[i];
1621 if (lmpw_len >= 16) *(vp+lmpw_offs+i) = (unsigned char)newlandes[i];
1622 }
1623
1624 printf("Password changed!\n");
1625
1626
1627 } /* new password */
1628 #endif /* DOCRYPT */
1629
1630 if (pl == 1 && *newp == '1') {
1631 /* Setting hash lengths to zero seems to make NT think it is blank
1632 * However, since we cant cut the previous hash bytes out of the V value
1633 * due to missing resize-support of values, it may leak about 40 bytes
1634 * each time we do this.
1635 */
1636 v->ntpw_len = 0;
1637 v->lmpw_len = 0;
1638
1639 printf("Password cleared!\n");
1640 }
1641
1642 #if 0
1643 hexprnt("Pw in buffer: ",(vp+ntpw_offs),16);
1644 hexprnt("Lm in buffer: ",(vp+lmpw_offs),16);
1645 #endif
1646 dirty = 1;
1647 return(username);
1648 }
1649
1650
1651 /* Registry edit wrapper */
1652
mainloop(void)1653 void mainloop(void)
1654 {
1655 regedit_interactive(hive, no_hives);
1656 }
1657
1658
1659 /* Iterate over users in SAM file, and do things with it
1660 * automode - if null, just list, else handle auto change
1661 * f = reset first user that is in admin group
1662 * a = reset all users in admin group
1663 * 0x1f4 (built-in administrator account) will only be reset
1664 * if no other users are found to be admin group
1665 */
1666
list_users(int readable)1667 int list_users(int readable)
1668 {
1669 char s[200];
1670 struct keyval *v;
1671 int nkofs /* ,vkofs */ ;
1672 int rid;
1673 int count = 0, countri = 0;
1674 int ntpw_len;
1675
1676 unsigned short acb;
1677
1678 struct user_V *vpwd;
1679 struct ex_data ex;
1680
1681 if (H_SAM < 0) return(1);
1682 nkofs = trav_path(hive[H_SAM], 0,"\\SAM\\Domains\\Account\\Users\\Names\\",0);
1683 if (!nkofs) {
1684 printf("list_users: Cannot find usernames in registry! (is this a SAM-hive?)\n");
1685 return(1);
1686 }
1687
1688 if (readable) printf("| RID -|---------- Username ------------| Admin? |- Lock? --|\n");
1689
1690 while ((ex_next_n(hive[H_SAM], nkofs+4, &count, &countri, &ex) > 0)) {
1691
1692 /* Extract the value out of the username-key, value is RID */
1693 snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\Names\\%s\\@",ex.name);
1694 rid = get_dword(hive[H_SAM], 0, s, TPF_VK_EXACT|TPF_VK_SHORT);
1695 if (rid == 500) strncpy(admuser,ex.name,128); /* Copy out admin-name */
1696
1697 /* Now that we have the RID, build the path to, and get the V-value */
1698 snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid);
1699 v = get_val2buf(hive[H_SAM], NULL, 0, s, REG_BINARY, TPF_VK_EXACT);
1700 if (!v) {
1701 printf("Cannot find value <%s>\n",s);
1702 return(1);
1703 }
1704
1705 if (v->len < 0xcc) {
1706 printf("list_users: Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n",
1707 s, v->len);
1708 } else {
1709
1710 vpwd = (struct user_V *)&(v->data);
1711 ntpw_len = vpwd->ntpw_len;
1712
1713 acb = handle_F(rid,0);
1714 if (readable) {
1715 printf("| %04x | %-30.30s | %-6s | %-8s |\n",
1716 rid, ex.name, (list_user_groups(rid,1) ? "ADMIN" : "") , ( acb & 0x8000 ? "dis/lock" : (ntpw_len < 16) ? "*BLANK*" : "") );
1717 } else {
1718 printf("%04x:%s:%d:%x:%x\n",
1719 rid, ex.name, list_user_groups(rid,1) , acb, ntpw_len );
1720 }
1721
1722
1723 // change_pw( (char *)&v->data , rid, v->len, (*automode == 'l') ? 2 : 1);
1724
1725 }
1726 FREE(v);
1727 FREE(ex.name);
1728 }
1729 return(0);
1730 }
1731
1732
1733 /* Find a username in the SAM registry, then get it's V-value,
1734 * and feed it to the password changer.
1735 */
1736
find_n_change(char * username)1737 void find_n_change(char *username)
1738 {
1739 char s[200];
1740 struct keyval *v;
1741 int rid = 0;
1742
1743 if ((H_SAM < 0) || (!username)) return;
1744 if (*username == '0' && *(username+1) == 'x') sscanf(username,"%i",&rid);
1745
1746 if (!rid) { /* Look up username */
1747 /* Extract the unnamed value out of the username-key, value is RID */
1748 snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\Names\\%s\\@",username);
1749 rid = get_dword(hive[H_SAM],0,s, TPF_VK_EXACT|TPF_VK_SHORT);
1750 if (rid == -1) {
1751 printf("Cannot find value <%s>\n",s);
1752 return;
1753 }
1754 }
1755
1756 /*
1757 printf("Username: %s, RID = %d (0x%0x)\n",username,rid,rid);
1758 */
1759
1760 /* Now that we have the RID, build the path to, and get the V-value */
1761 snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\V",rid);
1762 v = get_val2buf(hive[H_SAM], NULL, 0, s, REG_BINARY, TPF_VK_EXACT);
1763 if (!v) {
1764 printf("Cannot find value <%s>\n",s);
1765 return;
1766 }
1767
1768 if (v->len < 0xcc) {
1769 printf("Value <%s> is too short (only %d bytes) to be a SAM user V-struct!\n",
1770 s, v->len);
1771 } else {
1772 change_pw( (char *)&v->data , rid, v->len, 0);
1773 if (dirty) {
1774 if (!(put_buf2val(hive[H_SAM], v, 0, s, REG_BINARY, TPF_VK_EXACT))) {
1775 printf("Failed to write updated <%s> to registry! Password change not completed!\n",s);
1776 }
1777 }
1778 }
1779 FREE(v);
1780 }
1781
1782 /* Check for presence of syskey and possibly disable it if
1783 * user wants it.
1784 * This is tricky, and extremely undocumented!
1785 * See docs for more info on what's going on when syskey is installed
1786 */
1787
1788 #undef LSADATA
1789
handle_syskey(void)1790 void handle_syskey(void)
1791 {
1792
1793 /* This is \SAM\Domains\Account\F */
1794 struct samkeyf {
1795 char unknown[0x50]; /* 0x0000 - Unknown. May be machine SID */
1796 char unknown2[0x14];
1797 char syskeymode; /* 0x0064 - Type/mode of syskey in use */
1798 char syskeyflags1[0xb]; /* 0x0065 - More flags/settings */
1799 char syskeyobf[0x30]; /* 0x0070 - This may very well be the obfuscated syskey */
1800 }; /* There may be more, usually 8 null-bytes? */
1801
1802 /* Security\Policy\SecretEncryptionKey\@, only on NT5 */
1803 /* Probably contains some keyinfo for syskey. Second DWORD seems to be syskeymode */
1804 struct secpoldata {
1805 int unknown1; /* Some kind of flag? usually 1 */
1806 int syskeymode; /* Is this what we're looking for? */
1807 int unknown2; /* Usually 0? */
1808 char keydata[0x40]; /* Some kind of scrambled keydata? */
1809 };
1810
1811 #ifdef LSADATA
1812 /* SYSTEM\CurrentControlSet\Control\Lsa\Data, only on NT5?? */
1813 /* Probably contains some keyinfo for syskey. Byte 0x34 seems to be mode */
1814 struct lsadata {
1815 char keydata[0x34]; /* Key information */
1816 int syskeymode; /* Is this what we're looking for? */
1817 };
1818 #endif
1819
1820 /* void *fdata; */
1821 struct samkeyf *ff = NULL;
1822 struct secpoldata *sf = NULL;
1823 /* struct lsadata *ld = NULL; */
1824 int /* len, */ i,secboot, samfmode, secmode /* , ldmode */ ;
1825 struct keyval *samf, *secpol /* , *lsad */ ;
1826 char *syskeytypes[4] = { "off", "key-in-registry", "enter-passphrase", "key-on-floppy" };
1827 char yn[5];
1828
1829 printf("\n---------------------> SYSKEY CHECK <-----------------------\n");
1830
1831
1832 if (H_SAM < 0) {
1833 printf("ERROR: SAM hive not loaded!\n");
1834 return;
1835 }
1836 samf = get_val2buf(hive[H_SAM], NULL, 0, "\\SAM\\Domains\\Account\\F", REG_BINARY, TPF_VK_EXACT);
1837
1838 if (samf && samf->len > 0x70 ) {
1839 ff = (struct samkeyf *)&samf->data;
1840 samfmode = ff->syskeymode;
1841 } else {
1842 samfmode = -1;
1843 }
1844
1845 secboot = -1;
1846 if (H_SYS >= 0) {
1847 secboot = get_dword(hive[H_SYS], 0, "\\ControlSet001\\Control\\Lsa\\SecureBoot", TPF_VK_EXACT );
1848 }
1849
1850 secmode = -1;
1851 if (H_SEC >=0) {
1852 secpol = get_val2buf(hive[H_SEC], NULL, 0, "\\Policy\\PolSecretEncryptionKey\\@", REG_NONE, TPF_VK_EXACT);
1853 if (secpol) { /* Will not be found in NT 4, take care of that */
1854 sf = (struct secpoldata *)&secpol->data;
1855 secmode = sf->syskeymode;
1856 }
1857 }
1858
1859 #ifdef LSADATA
1860 lsad = get_val2buf(hive[H_SYS], NULL, 0, "\\ControlSet001\\Control\\Lsa\\Data\\Pattern", REG_BINARY, TPF_VK_EXACT);
1861
1862 if (lsad && lsad->len >= 0x38) {
1863 ld = (struct lsadata *)&lsad->data;
1864 ldmode = ld->syskeymode;
1865 } else {
1866 ldmode = -1;
1867 }
1868 #endif
1869
1870 printf("SYSTEM SecureBoot : %d -> %s\n", secboot,
1871 (secboot < 0 || secboot > 3) ? "Not Set (not installed, good!)" : syskeytypes[secboot]);
1872 printf("SAM Account\\F : %d -> %s\n", samfmode,
1873 (samfmode < 0 || samfmode > 3) ? "Not Set" : syskeytypes[samfmode]);
1874 printf("SECURITY PolSecretEncryptionKey: %d -> %s\n", secmode,
1875 (secmode < 0 || secmode > 3) ? "Not Set (OK if this is NT4)" : syskeytypes[secmode]);
1876
1877 #ifdef LSADATA
1878 printf("SYSTEM LsaData : %d -> %s\n\n", ldmode,
1879 (ldmode < 0 || ldmode > 3) ? "Not Set (strange?)" : syskeytypes[ldmode]);
1880 #endif
1881
1882 if (secboot != samfmode && secboot != -1) {
1883 printf("WARNING: Mismatch in syskey settings in SAM and SYSTEM!\n");
1884 printf("WARNING: It may be dangerous to continue (however, resetting syskey\n");
1885 printf(" may very well fix the problem)\n");
1886 }
1887
1888 if (secboot > 0 || samfmode > 0) {
1889 printf("\n***************** SYSKEY IS ENABLED! **************\n");
1890 printf("This installation very likely has the syskey passwordhash-obfuscator installed\n");
1891 printf("It's currently in mode = %d, %s-mode\n",secboot,
1892 (secboot < 0 || secboot > 3) ? "Unknown" : syskeytypes[secboot]);
1893
1894 if (no_hives < 2) {
1895 printf("\nSYSTEM (and possibly SECURITY) hives not loaded, unable to disable syskey!\n");
1896 printf("Please start the program with at least SAM & SYSTEM-hive filenames as arguments!\n\n");
1897 return;
1898 }
1899 printf("SYSKEY is on! However, DO NOT DISABLE IT UNLESS YOU HAVE TO!\n");
1900 printf("This program can change passwords even if syskey is on, however\n");
1901 printf("if you have lost the key-floppy or passphrase you can turn it off,\n");
1902 printf("but please read the docs first!!!\n");
1903 printf("\n** IF YOU DON'T KNOW WHAT SYSKEY IS YOU DO NOT NEED TO SWITCH IT OFF!**\n");
1904 printf("NOTE: On WINDOWS 2000 and XP it will not be possible\n");
1905 printf("to turn it on again! (and other problems may also show..)\n\n");
1906 printf("NOTE: Disabling syskey will invalidate ALL\n");
1907 printf("passwords, requiring them to be reset. You should at least reset the\n");
1908 printf("administrator password using this program, then the rest ought to be\n");
1909 printf("done from NT.\n");
1910 printf("\nEXTREME WARNING: Do not try this on Vista or Win 7, it will go into endless re-boots\n\n");
1911
1912 fmyinput("\nDo you really wish to disable SYSKEY? (y/n) [n] ",yn,2);
1913 if (*yn == 'y') {
1914 /* Reset SAM syskey infostruct, fill with zeroes */
1915 if (ff) {
1916 ff->syskeymode = 0;
1917
1918 for (i = 0; i < 0x3b; i++) {
1919 ff->syskeyflags1[i] = 0;
1920 }
1921
1922 put_buf2val(hive[H_SAM], samf, 0, "\\SAM\\Domains\\Account\\F", REG_BINARY, TPF_VK_EXACT);
1923
1924 }
1925 /* Reset SECURITY infostruct (if any) */
1926 if (sf) {
1927 memset(sf, 0, secpol->len);
1928 sf->syskeymode = 0;
1929
1930 put_buf2val(hive[H_SEC], secpol, 0, "\\Policy\\PolSecretEncryptionKey\\@", REG_BINARY, TPF_VK_EXACT);
1931
1932 }
1933
1934 #if LSADATA
1935 if (ld) {
1936
1937 ld->syskeymode = 0;
1938
1939 put_buf2val(hive[H_SYS], lsad, 0, "\\ControlSet001\\Control\\Lsa\\Data\\Pattern", REG_BINARY, TPF_VK_EXACT);
1940
1941 }
1942 #endif
1943
1944 /* And SYSTEM SecureBoot parameter */
1945
1946 put_dword(hive[H_SYS], 0, "\\ControlSet001\\Control\\Lsa\\SecureBoot", TPF_VK_EXACT, 0);
1947
1948 dirty = 1;
1949 syskeyreset = 1;
1950 printf("Updating passwordhash-lengths..\n");
1951 list_users(1);
1952 printf("* SYSKEY RESET!\nNow please set new administrator password!\n");
1953 } else {
1954
1955 syskeyreset = 1;
1956 }
1957 } else {
1958 printf("Syskey not installed!\n");
1959 return;
1960 }
1961
1962 }
1963
1964
1965 /* Interactive user edit */
useredit(void)1966 void useredit(void)
1967 {
1968 char iwho[100];
1969 int il;
1970
1971 printf("\n\n===== chntpw Edit User Info & Passwords ====\n\n");
1972
1973 if (H_SAM < 0) {
1974 printf("ERROR: SAM registry file (which contains user data) is not loaded!\n\n");
1975 return;
1976 }
1977
1978
1979 list_users(1);
1980
1981 while (1) {
1982 printf("\nSelect: ! - quit, . - list users, 0x<RID> - User with RID (hex)\n");
1983 printf("or simply enter the username to change: [%s] ",admuser);
1984 il = fmyinput("",iwho,32);
1985 if (il == 1 && *iwho == '.') { printf("\n"); list_users(1); continue; }
1986 if (il == 1 && *iwho == '!') return;
1987 if (il == 0) strcpy(iwho,admuser);
1988 find_n_change(iwho);
1989 }
1990
1991 }
1992
1993
recoveryconsole()1994 void recoveryconsole()
1995 {
1996
1997 int cmd = 0;
1998 int sec = 0;
1999 static char *scpath = "\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\RecoveryConsole\\SetCommand";
2000 static char *slpath = "\\Microsoft\\Windows NT\\CurrentVersion\\Setup\\RecoveryConsole\\SecurityLevel";
2001 char yn[5];
2002
2003 if (H_SOF < 0) {
2004 printf("\nSOFTWARE-hive not loaded, and there's where RecoveryConsole settings are..\n");
2005 return;
2006 }
2007
2008 cmd = get_dword(hive[H_SOF],0,scpath,TPF_VK_EXACT);
2009 sec = get_dword(hive[H_SOF],0,slpath,TPF_VK_EXACT);
2010
2011 if (cmd == -1 && sec == -1) {
2012 printf("\nDid not find registry entries for RecoveryConsole.\n(RecoveryConsole is only in Windows 2000 and XP)\n");
2013 return;
2014 }
2015
2016 printf("\nRecoveryConsole:\n- Extended SET command is \t%s\n", cmd>0 ? "ENABLED (1)" : "DISABLED (0)");
2017 printf("- Administrator password login: %s\n", sec>0 ? "SKIPPED (1)" : "ENFORCED (0)");
2018
2019 fmyinput("\nDo you want to change it? (y/n) [n] ",yn,2);
2020 if (*yn == 'y') {
2021 cmd ^= 1;
2022 sec ^= 1;
2023 if (!put_dword(hive[0], 0, scpath, TPF_VK_EXACT, cmd)) printf("Update of SET level failed registry edit\n");
2024 if (!put_dword(hive[0], 0, slpath, TPF_VK_EXACT, sec)) printf("Update of login level failed registry edit\n");
2025 printf("Done!\n");
2026 }
2027
2028 }
2029
2030
2031 /* Interactive menu system */
2032
interactive(void)2033 void interactive(void)
2034 {
2035 int il;
2036 char inbuf[20];
2037
2038 while(1) {
2039 printf("\n\n<>========<> chntpw Main Interactive Menu <>========<>\n\n"
2040 "Loaded hives:");
2041 for (il = 0; il < no_hives; il++) {
2042 printf(" <%s>",hive[il]->filename);
2043 }
2044
2045 printf("\n\n");
2046
2047 /* Make menu selection depending on what is loaded
2048 but it is still possible to select even if not shown */
2049
2050 if (H_SAM >= 0) {
2051 printf(" 1 - Edit user data and passwords\n");
2052 printf(" 2 - List groups\n");
2053 }
2054 if (H_SOF >= 0) {
2055 printf(" 3 - RecoveryConsole settings\n");
2056 printf(" 4 - Show product key (DigitalProductID)\n");
2057 }
2058 #if 0
2059 if (H_SAM >= 0 && H_SYS >= 0 && H_SEC >= 0) {
2060 printf(" 8 - Syskey status & change\n");
2061 }
2062 #endif
2063
2064 printf(" - - -\n"
2065 " 9 - Registry editor, now with full write support!\n"
2066 " q - Quit (you will be asked if there is something to save)\n"
2067 "\n\n");
2068
2069 il = fmyinput("What to do? [1] -> ", inbuf, 10);
2070
2071 if (!il) useredit();
2072 if (il) {
2073 switch(inbuf[0]) {
2074 case '1': useredit(); break;
2075 case '2': list_groups(0); break;
2076 case '3': recoveryconsole(); break;
2077 case '4': cat_dpi(hive[H_SOF],0,"\\Microsoft\\Windows NT\\CurrentVersion\\DigitalProductId"); break;
2078 case '8': handle_syskey(); break;
2079 case '9': mainloop(); break;
2080 case 'q': return; break;
2081 }
2082 }
2083 }
2084 }
2085
2086
cmd_usrgrp(char * user,char * grp,int what)2087 int cmd_usrgrp(char *user, char *grp, int what)
2088 {
2089 int numgrp;
2090 int rid = 0;
2091 char s[200];
2092
2093 numgrp = strtol(grp, NULL, 0);
2094
2095 printf("numgrp = %d (0x%x)\n", numgrp, numgrp);
2096
2097 if ((H_SAM < 0) || (!user)) return(1);
2098 if (*user == '0' && *(user+1) == 'x') sscanf(user,"%i",&rid);
2099
2100 if (!rid) { /* Look up username */
2101 /* Extract the unnamed value out of the username-key, value is RID */
2102 snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\Names\\%s\\@",user);
2103 rid = get_dword(hive[H_SAM],0,s, TPF_VK_EXACT|TPF_VK_SHORT);
2104 if (rid == -1) {
2105 printf("User <%s> not found\n",user);
2106 return(1);
2107 }
2108 }
2109
2110 printf("Username: %s, RID = %d (0x%0x)\n",user,rid,rid);
2111
2112 switch (what) {
2113 case 0: return(add_user_to_grp(rid, numgrp)); break;
2114 case 1: return(remove_user_from_grp(rid, numgrp)); break;
2115 }
2116
2117
2118 return(0);
2119
2120 }
2121
2122
do_automode(char * automode,char * who,char * grp)2123 void do_automode(char *automode, char *who, char *grp)
2124 {
2125 struct sid_binary sid;
2126 char *sidstr;
2127
2128 #if 1
2129 printf("DEBUG: do_automode start\n");
2130 printf("automode = %s\n",automode);
2131 printf("who = %s\n",who);
2132 printf("grp = %s\n",grp);
2133 #endif
2134
2135 switch (*automode) {
2136 case 'l': list_users(0); break;
2137 case 's': if(get_machine_sid((char *)&sid)) { sidstr = sid_to_string(&sid); puts(sidstr); FREE(sidstr);} break;
2138 case 'g': list_groups(1); break;
2139 case 'a': cmd_usrgrp(who, grp, 0); break; /* Add user to group */
2140 case 'r': cmd_usrgrp(who, grp, 1); break; /* Remove user from group */
2141
2142 }
2143
2144
2145 // printf("DEBUG: do_automode end\n");
2146
2147 }
2148
2149
usage(void)2150 void usage(void) {
2151 printf("chntpw: change password of a user in a Windows SAM file,\n"
2152 "or invoke registry editor. Should handle both 32 and 64 bit windows and\n"
2153 "all version from NT3.x to Win7\n"
2154 "chntpw [OPTIONS] <samfile> [systemfile] [securityfile] [otherreghive] [...]\n"
2155 " -h This message\n"
2156 " -u <user> Username or RID to change, Administrator is default\n"
2157 " -l list all users in SAM file\n"
2158 " -i Interactive Menu system\n"
2159 " -e Registry editor. Now with full write support!\n"
2160 " -d Enter buffer debugger instead (hex editor), \n"
2161 " -v Be a little more verbose (for debuging)\n"
2162 " -L For scripts, write names of changed files to /tmp/changed\n"
2163 " -N No allocation mode. Only same length overwrites possible (very safe mode)\n"
2164 " -E No expand mode, do not expand hive file (safe mode)\n"
2165 " -A <subcommand> Auto / Noninteractive. Do stuff without asking\n"
2166 " -A F reset on first admin user (lowest RID)\n"
2167 " -A A reset all admins\n"
2168 " -A l list all users, output it parseable\n"
2169 " -A g list groups\n"
2170 " -A s show machine SID\n"
2171 " -A a -u <username|RID> -u <grpid> Add user to group\n"
2172 " -A r -u <username|RID> -g <grpid> Remove user from group\n"
2173
2174 "\nUsernames can be given as name or RID (in hex with 0x first)\n"
2175 "<grpid> group IDs must be given as number, can be hex with 0x first\n"
2176 "Example: chntpw -A a -u 0x3aa -g 1000 # Adds user with RID hex 0x3aa to group decimal 1000\n"
2177 "\nSee readme file on how to get to the registry files, and what they are.\n"
2178 "Source/binary freely distributable under GPL v2 license. See README for details.\n"
2179 "NOTE: This program is somewhat hackish! You are on your own!\n"
2180 );
2181 }
2182
2183
main(int argc,char ** argv)2184 int main(int argc, char **argv)
2185 {
2186
2187 int dodebug = 0, list = 0, inter = 0,edit = 0,il,d = 0, dd = 0, logchange = 0;
2188 int mode = HMODE_INFO;
2189 extern int /* opterr, */ optind;
2190 extern char* optarg;
2191 char *filename,c;
2192 char *who = "Administrator";
2193 char *grp = NULL;
2194 char iwho[100];
2195 char *automode = "";
2196 FILE *ch; /* Write out names of touched files to this */
2197
2198 char *options = "A:LENidehlvu:g:";
2199
2200 while((c=getopt(argc,argv,options)) > 0) {
2201 switch(c) {
2202 case 'd': dodebug = 1; break;
2203 case 'e': edit = 1; break;
2204 case 'L': logchange = 1; break;
2205 case 'N': mode |= HMODE_NOALLOC; break;
2206 case 'E': mode |= HMODE_NOEXPAND; break;
2207 case 'l': list = 1; break;
2208 case 'v': mode |= HMODE_VERBOSE; gverbose = 1; break;
2209 case 'i': inter = 1; break;
2210 case 'u': who = optarg; break;
2211 case 'g': grp = optarg; break;
2212 case 'A': automode = optarg; mode &= ~HMODE_INFO; break;
2213 case 'h': usage(); exit(0); break;
2214 default: usage(); exit(1); break;
2215 }
2216 }
2217
2218 if (!*automode) printf("%s\n",chntpw_version);
2219
2220 filename=argv[optind];
2221 if (!filename || !*filename) {
2222 usage(); exit(1);
2223 }
2224 do {
2225 if (!(hive[no_hives] = openHive(filename,
2226 HMODE_RW|mode))) {
2227 fprintf(stderr,"%s: Unable to open/read a hive, exiting..\n",argv[0]);
2228 exit(1);
2229 }
2230 switch(hive[no_hives]->type) {
2231 case HTYPE_SAM: H_SAM = no_hives; break;
2232 case HTYPE_SOFTWARE: H_SOF = no_hives; break;
2233 case HTYPE_SYSTEM: H_SYS = no_hives; break;
2234 case HTYPE_SECURITY: H_SEC = no_hives; break;
2235 }
2236 no_hives++;
2237 filename = argv[optind+no_hives];
2238 } while (filename && *filename && no_hives < MAX_HIVES);
2239
2240 #if 0
2241 printf("user = %s\n",who);
2242 printf("automode = %s\n",automode);
2243 #endif
2244
2245 if (dodebug) {
2246 debugit(hive[0]->buffer,hive[0]->size);
2247 } else if (*automode) {
2248 check_get_samdata(0);
2249 do_automode(automode, who, grp);
2250
2251 } else if (inter) {
2252 check_get_samdata(1);
2253 interactive();
2254 } else if (edit) {
2255 check_get_samdata(1);
2256 mainloop();
2257 } else if (list) {
2258 check_get_samdata(1);
2259 list_users(1);
2260 } else if (who) {
2261 check_get_samdata(1);
2262 find_n_change(who);
2263 }
2264
2265
2266
2267
2268
2269 if (list != 1) {
2270 if (!*automode) printf("\nHives that have changed:\n # Name\n");
2271 for (il = 0; il < no_hives; il++) {
2272 if (hive[il]->state & HMODE_DIRTY) {
2273 if (!logchange && !*automode) printf("%2d <%s>",il,hive[il]->filename);
2274 if (hive[il]->state & HMODE_DIDEXPAND) printf(" WARNING: File was expanded! Expermental! Use at own risk!\n");
2275 if (!*automode) printf("\n");
2276
2277 d = 1;
2278 }
2279 }
2280 if (d) {
2281 /* Only prompt user if logging of changed files has not been set */
2282 /* Thus we assume confirmations are done externally if they ask for a list of changes */
2283 if (!logchange && !*automode) fmyinput("Write hive files? (y/n) [n] : ",iwho,3);
2284 if (*iwho == 'y' || logchange || *automode) {
2285 if (logchange) {
2286 ch = fopen("/tmp/changed","w");
2287 }
2288 for (il = 0; il < no_hives; il++) {
2289 if (hive[il]->state & HMODE_DIRTY) {
2290 if (!*automode) printf("%2d <%s> - ",il,hive[il]->filename);
2291 if (!writeHive(hive[il])) {
2292 if (!*automode) printf("OK");
2293 if (hive[il]->state & HMODE_DIDEXPAND) printf(" WARNING: File was expanded! Expermental! Use at own risk!\n");
2294 if (!*automode) printf("\n");
2295 if (logchange) fprintf(ch,"%s ",hive[il]->filename);
2296 dd = 2;
2297 }
2298 }
2299 }
2300 if (logchange) {
2301 fprintf(ch,"\n");
2302 fclose(ch);
2303 }
2304 } else {
2305 printf("Not written!\n\n");
2306 }
2307 } else {
2308 if (!*automode) printf("None!\n\n");
2309 }
2310 } /* list only check */
2311 return(dd);
2312 }
2313