📄 chntpw.c
字号:
/* * chntpw.c - Offline Password Edit Utility for NT 3.51 4.0 5.0 5.1 SAM database. * 1999-feb: Now able to browse registry hives. (write support to come) * 2000-jan: Attempt to detect and disable syskey * 2000-jun: syskey disable works on NT4. Not properly on NT5. * 2000-jun: changing passwords regardless of syskey. * 2001-jan: patched & changed to use OpenSSL. Thanks to Denis Ducamp * 2001-jul: extra blank password logic (when NT or LANMAN hash missing) * 2002-dec: New option: blank the pass (zero hash lengths). * 2002-dec: New option: Specify user using RID * 2003-jan: Support in ntreg for adding keys etc. Editor updated. * 2003-jan: Changed to use more of struct based V + some small stuff * 2004-jan: Changed some of the verbose/debug stuff * 2004-aug: More stuff in regedit. Stringinput bugfixes. * * Copyright (c) 1997-2004 Petter Nordahl-Hagen. * Freely distributable in source or binary for noncommercial purposes, * but I allow some exceptions to this. * Please see the COPYING file for more details on * copyrights & credits. * * Part of some routines, information and ideas taken from * pwdump by Jeremy Allison. * * Some stuff from NTCrack by Jonathan Wilkins. * * * THIS SOFTWARE IS PROVIDED BY PETTER NORDAHL-HAGEN `AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <ctype.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <openssl/des.h>#include <openssl/md4.h>#define uchar u_char#define MD4Init MD4_Init#define MD4Update MD4_Update#define MD4Final MD4_Final#include "ntreg.h"#include "sam.h"const char chntpw_version[] = "chntpw version 0.99.3 040818, (c) Petter N Hagen";extern char *val_types[REG_MAX+1];/* Global verbosity */int gverbose = 0;#define MAX_HIVES 10/* Array of loaded hives */struct hive *hive[MAX_HIVES+1];int no_hives = 0;/* Icky icky... globals used to refer to hives, will be * set when loading, so that hives can be loaded in any order */int H_SAM = -1;int H_SYS = -1;int H_SEC = -1;int H_SOF = -1;int syskeyreset = 0;int dirty = 0;int max_sam_lock = 0;/* * of user with RID 500, because silly MS decided * to localize the bloody admin-username!! AAAGHH! */char admuser[129]="Administrator";/* ============================================================== *//* Crypto-stuff & support for what we'll do in the V-value *//* Zero out string for lanman passwd, then uppercase * the supplied password and put it in here */void make_lanmpw(char *p, char *lm, int len){ int i; for (i=0; i < 15; i++) lm[i] = 0; for (i=0; i < len; i++) lm[i] = toupper(p[i]);}/* * Convert a 7 byte array into an 8 byte des key with odd parity. */void str_to_key(unsigned char *str,unsigned char *key){ int i; key[0] = str[0]>>1; key[1] = ((str[0]&0x01)<<6) | (str[1]>>2); key[2] = ((str[1]&0x03)<<5) | (str[2]>>3); key[3] = ((str[2]&0x07)<<4) | (str[3]>>4); key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5); key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6); key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7); key[7] = str[6]&0x7F; for (i=0;i<8;i++) { key[i] = (key[i]<<1); } DES_set_odd_parity((des_cblock *)key);}/* * Function to convert the RID to the first decrypt key. */void sid_to_key1(unsigned long sid,unsigned char deskey[8]){ unsigned char s[7]; s[0] = (unsigned char)(sid & 0xFF); s[1] = (unsigned char)((sid>>8) & 0xFF); s[2] = (unsigned char)((sid>>16) & 0xFF); s[3] = (unsigned char)((sid>>24) & 0xFF); s[4] = s[0]; s[5] = s[1]; s[6] = s[2]; str_to_key(s,deskey);}/* * Function to convert the RID to the second decrypt key. */void sid_to_key2(unsigned long sid,unsigned char deskey[8]){ unsigned char s[7]; s[0] = (unsigned char)((sid>>24) & 0xFF); s[1] = (unsigned char)(sid & 0xFF); s[2] = (unsigned char)((sid>>8) & 0xFF); s[3] = (unsigned char)((sid>>16) & 0xFF); s[4] = s[0]; s[5] = s[1]; s[6] = s[2]; str_to_key(s,deskey);}/* DES encrypt, for LANMAN */void E1(uchar *k, uchar *d, uchar *out){ des_key_schedule ks; des_cblock deskey; str_to_key(k,(uchar *)deskey);#ifdef __FreeBSD__ des_set_key(&deskey,ks);#else /* __FreeBsd__ */ des_set_key((des_cblock *)deskey,ks);#endif /* __FreeBsd__ */ des_ecb_encrypt((des_cblock *)d,(des_cblock *)out, ks, DES_ENCRYPT);}/* Check if hive is SAM, and if it is, extract some * global policy information from it, like lockout counts etc */void check_get_samdata(void){ struct accountdb_F *f; struct keyval *v; if (H_SAM >= 0) { /* Get users F value */ v = get_val2buf(hive[H_SAM], NULL, 0, ACCOUNTDB_F_PATH, REG_BINARY); if (!v) { printf("Login counts data not found in SAM\n"); return; } printf("\n* SAM policy limits:\n"); f = (struct accountdb_F *)&v->data; max_sam_lock = f->locklimit; printf("Failed logins before lockout is: %d\n",max_sam_lock); printf("Minimum password length : %d\n",f->minpwlen); printf("Password history count : %d\n",f->minpwlen); }} /* Try to decode and possibly change account lockout etc * This is \SAM\Domains\Account\Users\<RID>\F * It's size seems to always be 0x50. * Params: RID - user ID, mode - 0 silent, 1 silent, 2 edit. * Returns: ACB bits with high bit set if lockout count is >0 */short handle_F(int rid, int mode){ struct user_F *f; char s[200]; char yn[10]; struct keyval *v; unsigned short acb; int b; if (H_SAM < 0) return(0); /* Get users F value */ snprintf(s,180,"\\SAM\\Domains\\Account\\Users\\%08X\\F",rid); v = get_val2buf(hive[H_SAM], NULL, 0, s, REG_BINARY); if (!v) { printf("Cannot find value <%s>\n",s); return(0); } if (v->len != 0x50) { printf("handle_F: F value is 0x%x bytes, not 0x50, unable to check account flags!\n",v->len); FREE(v); return(0); } f = (struct user_F *)&v->data; acb = f->ACB_bits; if (mode) { printf("Account bits: 0x%04x =\n",acb); for (b=0; b < 15; b++) { printf("[%s] %-15.15s | ", (acb & (1<<b)) ? "X" : " ", acb_fields[b] ); if (b%3 == 2) printf("\n"); } printf("\nFailed login count: %u, while max tries is: %u\n",f->failedcnt,max_sam_lock); printf("Total login count: %u\n",f->logins); if (mode > 1) { if ( acb & (ACB_DISABLED|ACB_AUTOLOCK) || (f->failedcnt > 0 && f->failedcnt >= max_sam_lock) ) { printf("Account is %s\n",(acb & ACB_DISABLED) ? "disabled" : "probably locked out!"); printf("Do you wish me to reset the failed count, unset disabled and lockout,\n"); fmyinput("and set the \"password never expires\" option? (y/n) [n]",yn,2); if (*yn == 'y') { acb |= ACB_PWNOEXP; acb &= ~ACB_DISABLED; acb &= ~ACB_AUTOLOCK; f->ACB_bits = acb; f->failedcnt = 0; put_buf2val(hive[H_SAM], v, 0, s, REG_BINARY); printf("Unlocked!\n"); } } } } return (acb | ( (f->failedcnt > 0 && f->failedcnt >= max_sam_lock)<<15 ) | (acb & ACB_AUTOLOCK)<<15 | (acb & ACB_DISABLED)<<15);}/* Promote user into administrators group (group ID 0x220) * And remove from all others... * hdesc - hive * rid - users rid * no returns yet * THIS IS VERY HACKISH YET */void promote_user(int rid){ char s[200]; char g[200]; int nk = 0; struct keyval *m = NULL, *c = NULL; struct keyval admember = { 4, 0x220 }; unsigned int *grps, *gcnts; int count = 0; int i, size, grp; if (!rid || (H_SAM < 0)) return; /* Get member list for user. Go for the first full SID, it's usually local computer I hope */ snprintf(s,180,"\\SAM\\Domains\\Builtin\\Aliases\\Members\\S-1-5-21-\\%08X",rid); /* Now, the TYPE field is the number of groups the user is member of */ /* Don't we just love the inconsistent use of fields!! */ nk = trav_path(hive[H_SAM], 0, s, 0); if (!nk) { printf("Cannot find path <%s>\n",s); return; } nk += 4; count = get_val_type(hive[H_SAM],nk,"@"); if (count == -1) { printf("Cannot find value <%s\\@>\n",s); return; } printf("User is member of %d groups.\n",count); /* This is the data size */ size = get_val_len(hive[H_SAM],nk,"@"); /* It should be 4 bytes for each group */ printf("Data size %d bytes.\n",size); if (size != count * 4) { printf("DEBUG: Size is not 4 * count! May not matter anyway. Continuing..\n"); } m = get_val2buf(hive[H_SAM], NULL, nk, "@", 0); if (!m) { printf("Could not get value data! Giving up.\n"); return; } printf("User was member of groups: "); grps = (unsigned int *)&m->data; for (i = 0; i < count; i++) { grp = grps[i]; printf("%08x ",grp); switch (grp) { case 0x220: printf("=Administrators, "); break; case 0x221: printf("=Users, "); break; case 0x222: printf("=Guests, "); break; default: printf(", "); break; } snprintf(g,180,"\\SAM\\Domains\\Builtin\\Aliases\\%08X\\C",grp); c = get_val2buf(hive[H_SAM], NULL, 0, g, 0); if (c) { gcnts = (unsigned int *)&c->data; gcnts[0xc]--; /* Decrease members counter */ put_buf2val(hive[H_SAM], c, 0, g, 0); } else { printf("Group info for %x not found!\n",grp); } }#if 1 printf("\nDeleting user memberships\n"); del_value(hive[H_SAM], nk, "@"); printf("Adding into only administrators:\n"); if (!add_value(hive[H_SAM], nk, "@", 1)) { /* Type is # of groups, here 1 */ printf("Failed to add @ value to key\n"); }#endif put_buf2val(hive[H_SAM], &admember, nk, "@", 0); /* Now bumb up administrator groups count */ c = get_val2buf(hive[H_SAM], NULL, 0, "\\SAM\\Domains\\Builtin\\Aliases\\00000220\\C", 0); if (!c) printf("Group info for 220 (adm) not found!\n"); gcnts = (unsigned int *)&c->data; gcnts[0xc]++; put_buf2val(hive[H_SAM], c, 0, "\\SAM\\Domains\\Builtin\\Aliases\\00000220\\C", 0); printf("Promotion DONE!\n"); }/* Decode the V-struct, and change the password * vofs - offset into SAM buffer, start of V struct * rid - the users RID, required for the DES decrypt stage * * Some of this is ripped & modified from pwdump by Jeremy Allison * */char *change_pw(char *buf, int rid, int vlen, int stat){ uchar x1[] = {0x4B,0x47,0x53,0x21,0x40,0x23,0x24,0x25}; int pl; char *vp; static char username[128],fullname[128]; char comment[128],homedir[128],md4[32],lanman[32]; char newunipw[34], newp[20], despw[20], newlanpw[16], newlandes[20]; char yn[4]; int username_offset,username_len; int fullname_offset,fullname_len; int comment_offset,comment_len; int homedir_offset,homedir_len;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -