SHA to test passwords

Started by stanl, October 09, 2019, 05:00:27 AM

Previous topic - Next topic

stanl

Tony;


Thanks to your assistance with my earlier post regarding creating an SHA hash, I have another use for this. Trying to help out a friend who found a website: https://api.pwnedpasswords.com


to test whether a password could have been potentially used or hacked previously. I found that the url will accept a 5 byte SHA1 has and return results. Although the code you supplied me was SHA256 the same methods work with SHA1.  So, in the script below, a hash for a suggested password:
Code (WINBATCH) Select


ObjectClrOption('useany', 'System.Core')
objCrypt = ObjectClrNew('System.Security.Cryptography.SHA1Cng')
pw = 'W1Batc$e'
pw = ChrStringToUnicode(pw)
aBytes = ObjectType('array|ui1', pw)
aHash = objCrypt.ComputeHash(aBytes)
hHash = BinaryAllocArray(aHash)
strHash = BinaryPeekHex(hHash, 0, BinaryEODGet(hHash))
BinaryFree(hHash)
Message('Hash Result', 'Hash string: ':strHash:@lf:'Length: ':StrLen(strHash))
objCrypt.Clear()
objCrypt = 0
Exit


But similar code in powershell returns a completely different hash



$pw = 'W1Batc$e'
$bytes = [Text.Encoding]::UTF8. GetBytes($pw)
$stream = [IO.MemoryStream]::new($bytes )
$hash = Get-FileHash -Algorithm 'SHA1' -InputStream $stream
$stream.Close()
$stream.Dispose()
$hash



My less than knowledgeable assumption is the PS text encoding is the difference but would appreciate your expertise

td

I tend to stay away from SHA1 because the algorithm is eminently hackable and has been deprecated in the context of security usage.  That said, it doesn't really matter when you are using SHA1 to generate a thumbprint key for lookups or something similar.

Powsershell is mostly just an interpreter.  The real work is done by the dotNet framework classes that it calls underneath the hood. If you poke around in the FCL security classes you may find serval different iterations of SHA1 available.  Those different iterations may explain the difference in hash output.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl


EDIT: forget what I posted below. When I do things for friends, I don't charge. This looks like it would take a lot of code and not a simple script, or just use PS. Also, not something for Tech db.  I just recommended some google finds.



Thanks. I read the warnings about SHA1 also. Changed the PS to SHA256 as well as WB. Still get 2 different hashes. Regardless, the url will accept the first 5 chars of the hash but seems to return a SHA1 40-char with what I assume after the colon is the number of hits and I guess they would need to be decrypted?


https://api.pwnedpasswords.com/range/51194






004593A12B86479B246D18AD747412CEBCA:2
00A133BA395B042F69B02F498ABD97B2BC1:1
00A2C96D2041337C6D143601DD1D606BED0:3
011F4F3F78B696288AF0E584000C2499894:2
0160C7A2F3FD5A1F3022BDB2D374D47D102:1
028BFE37B7DD5EA0C0592E56515F3BAB42D:2
02AA8FCEA1E7D9C5D4FE1A1962F2E3579F3:4
0380C95E775593F087B1BFEDBC728823C22:1
04294C1B377D08C04803CCD49F303DF1E5B:2
04B756AE8A2740056A0EF3C84748C443238:15
04BC1B76C6DCF4996DF9D69B97758F0C671:2
0593BA8ADBEBB8361BE2B8BACBB4539E52C:2
05CDB44FCDEB396EF1D1036609344F237B0:1
07190468B69531DC846D67B5A9D4330AE93:2
09215EE3642F44ABB45ACF1149D964EB2B1:2

td

Our first encounter with the SHA1 issue dates back ~8-10 years.  WindowWare had to get all new codesigning and SSL certificates.  The codesigning certificates were a problem because MSFT didn't add support for 256-bit hashing to Windows Vista until very late in its life cycle.  And then the patch was only for the 64-bit version and it wasn't initially part of a monthly update.  The user had to find and download it from MSFT's site.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

I introduced my friend to a url that has 1/2 billion pawned passwords and you can submit text and it will return if it appears in the lookup, or you can download the data [about 11 gig].....


To the point: he supports computer training in retirement homes... and you know old people like [me] who set their passwords to their favorite dog or other easy things to hack. So part of the ask was if I could help with a compiled exe to show the risk.


The lookup data [above] is all SHA1. The curious aspect of the api lookup I was going to replicate was it accepted the first 5 bytes of the SHA1 has, then looked up the remaining 35 bytes in the return adding up the number(s) after the :


For fun I might just try a script with some basic passwords like 'Lassie' just to work out parsing the results.[/me]

stanl

below is a working test script for the SHA1 lookup for a password of 'password' and returns over 3 million hits. I used a call to PS as the same query using the WB CLR
Code (WINBATCH) Select


ObjectClrOption('useany', 'System.Core')
objCrypt = ObjectClrNew('System.Security.Cryptography.SHA1Cng')



returns nothing found. As Tony noted earlier: If you poke around in the FCL security classes you may find serval different iterations of SHA1 available.  Those different iterations may explain the difference in hash output.
so I picked what appears to work.

Code (WINBATCH) Select


;Winbatch 2019B - Use CLR/Powershell to check for pwned paswords
;This should work with versions after 2013                 
;Stan Littlefield, October 14,2019
;/////////////////////////////////////////////////////////////////////////////////////////////////////////


GoSub udfs
IntControl(73,1,0,0,0)
cFile="c:\temp\hash.txt"
If fileexist(cFile) Then Filedelete(cFile)
;this can easily be a dialog
pw = 'password'


BoxOpen("Please Wait","Looking for Pawned Passwords for: ":pw)
pwscript()
ObjectClrOption("useany", "System.Management.Automation")
objAutoPs = ObjectClrNew("System.Management.Automation.PowerShell")
oPshell = objAutoPs.Create()
oScope = ObjectType("BOOL",@TRUE)
oPshell.AddScript(cScript,oScope)
objAsync = oPshell.BeginInvoke()
oPShell.EndInvoke(objAsync)     


oPshell.Dispose()
oPshell=0


found=0


If FileExist(cFile)
   f=FileOpen(cFile,"READ")


   While @TRUE
      line = FileRead(f)
      If line == "*EOF*" Then Break
      found=found+Int(ItemExtract(2,line,":"))
   EndWhile
   FileClose(f)
Endif
BoxShut()
Message("Password Check: ":pw,"found ":found)


Exit


:WBERRORHANDLER
geterror()
Message("Error Encountered",errmsg)
Exit


:udfs
#DefineSubRoutine pwscript()
cScript="$pw = '%pw%'":@LF
cScript=cScript:"$bytes = [Text.Encoding]::UTF8. GetBytes($pw)":@LF
cScript=cScript:"$stream = [IO.MemoryStream]::new($bytes )":@LF
cScript=cScript:"$hash = Get-FileHash -Algorithm 'SHA1' -InputStream $stream":@LF
cScript=cScript:"$stream.Close()":@LF
cScript=cScript:"$stream.Dispose()":@LF
cScript=cScript:"$hash":@LF
cScript=cScript:"$first5hashChars,$remainingHashChars = $hash.Hash -split '(?<=^.{5})'":@LF
cScript=cScript:'$url = "https://api.pwnedpasswords.com/range/$first5hashChars"':@LF
cScript=cScript:"[Net.ServicePointManager]::SecurityProtocol = 'Tls12'":@LF
cScript=cScript:"$response = Invoke-RestMethod -Uri $url -UseBasicParsing":@LF
cScript=cScript:"$lines = $response -split '\r\n'":@LF
cScript=cScript:'$filteredLines = $lines -like "$remainingHashChars*"':@LF
cScript=cScript:"$filteredLines | Out-File %cFile%":@LF


Return cScript


#EndSubRoutine


#DefineSubRoutine geterror()
   wberroradditionalinfo = wberrorarray[6]
   lasterr = wberrorarray[0]
   handlerline = wberrorarray[1]
   textstring = wberrorarray[5]
   linenumber = wberrorarray[8]
   errmsg = "Error: ":lasterr:@LF:textstring:@LF:"Line (":linenumber:")":@LF:wberroradditionalinfo
   Return(errmsg)
#EndSubRoutine




Return

td

Another example of failed multitasking on my part.  The reason you were getting different hash results wasn't because of the algorithm.  It was because you were not hashing the same values.  Your PS script hashes a UTF-8 string while your WinBatch script is hashing a UTF-16 string.  Two different byte arrays will produce two different hashes.  If they did not, the hash algorithm is flawed.  To hash the correct string using WIL and dotNet sans PS requires a bit of additional processing because COM Automation works with UTF-16 text strings.

So you end up with something like the following:
Code (winbatch) Select
ObjectClrOption('useany', 'System.Core')
objCrypt = ObjectClrNew('System.Security.Cryptography.SHA1Cng')
pw = 'W1Batc$e'

;; Converts password to UTF-8 based byte array.
Encoding = ObjectClrNew('System.Text.Encoding')
aBytes = Encoding.UTF8.GetBytes(pw)

aHash = objCrypt.ComputeHash(aBytes)
hHash = BinaryAllocArray(aHash)
strHash = BinaryPeekHex(hHash, 0, BinaryEODGet(hHash))
BinaryFree(hHash)
Message('Hash Result', 'Hash string: ':strHash:@lf:'Length: ':StrLen(strHash))
objCrypt.Clear()
objCrypt = 0
Exit


Nothing quite like seeing what you are looking at...
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

Most of the password lookup process could be done without even using dotNet's FCL.  However, here is a rough (no error handling included) and a very lightly tested script approach using just WIL and dotNet's FCL.
   
Code (winbatch) Select
;; Most of this could be in a couple of UDFs.
gosub def_udfs

pw = 'passsword'  ;; The infamous password.

strHash = GetStrHash(pw)
strFirst5 = StrSub(strHash, 1, 5)
ObjectClrOption('useany', 'System')
objWebUtil = ObjectClrNew('System.Net.WebUtility')
strF5Encoded = objWebUtil.UrlEncode(strFirst5)

strUrl = 'https://api.pwnedpasswords.com/range/':strF5Encoded

;  Need TLS 1.2
objUri = ObjectClrNew('System.Uri', strUrl)
objSvcManager = ObjectClrNew('System.Net.ServicePointManager')
protocols = ObjectClrType("System.Net.SecurityProtocolType",3072|768) 
objSvcManager.SecurityProtocol = protocols
objSvcPoint = objSvcManager.FindServicePoint(objUri)

objWebRequest = ObjectClrNew('System.Net.WebRequest')
objWebRequest = objWebRequest.Create(objUri)
objWebRequest.Timeout = objWebRequest.Timeout * 6
objResponse = objWebRequest.GetResponse()
objStream = objResponse.GetResponseStream

;  Check for a match.
Encoding = ObjectClrNew( 'System.Text.Encoding' )
objStreamReader = ObjectClrNew("System.IO.StreamReader", objStream, Encoding.UTF8)
strRest = StrSub(strHash, 6, -1)
bFound = StrIndexNC(objStreamReader.ReadToEnd(), strRest, 1, @fwdscan)
objStreamReader.Close()
objStream.Close()

if bFound then strText = 'You have been pwnded!'
else strText = 'You haven''t been pwnded yet.'

Pause('Password Check', strText)

exit

;; User define procedures here.
:def_udfs

#DefineFunction GetStrHash(_Str)

   ObjectClrOption('useany', 'System.Core')
   objCrypt = ObjectClrNew('System.Security.Cryptography.SHA1Cng')

   ;; Converts password to UTF-8 based byte array.
   Encoding = ObjectClrNew('System.Text.Encoding')
   aBytes = Encoding.UTF8.GetBytes(_Str)

   aHash = objCrypt.ComputeHash(aBytes)
   hHash = BinaryAllocArray(aHash)
   strHash = BinaryPeekHex(hHash, 0, BinaryEODGet(hHash))
   BinaryFree(hHash)
   objCrypt.Clear()
   objCrypt = 0

   return strHash
#EndFunction

return
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

Thanks again. Lot's of new CLR snippets to add to my library, especially setting the protocol prior to a web request.