OK so i have been working on this for a bit, and i cant see how to get any further. I keep running into roadblocks with the DESCryptoServiceProvider and somehow it just doesnt seem to be comming out with the right answers.
the sudo code version of LM_Hash is:
LMHASH = concat(DES(Upper(PWD)[0..7],KGS!##$%),DES(Upper(PWD)[8..13],KGS!##$%))
First issue is the LM Key I keep seeing the atleast two variants its either "KGS!##$%" or "KGS!+#$%" neither gets me the right answers but both dont seem to fit with the origin story (its KGS and SHIFT 12345 assuming a US keyboard
on a UK thats "KGS!"£$%")
I am pretty sure i have the parameters set up correctly now, but my understanding seems to be failing me. here's what i have so far, any help is appreciated I am running Powershell V5.1 on Win 10, the string to encrypt is passed in as $string
$plaintext = "KGS!##$%"
$OEM = [System.Text.Encoding]::GetEncoding($Host.CurrentCulture.TextInfo.OEMCodePage)
$str1 = $OEM.GetBytes($string.substring(0,7)) +[Byte]$null
$str2 = $OEM.GetBytes($string.Substring(7)) +[Byte]$null
$IV = new-object "System.Byte[]" 8
$hasher = New-Object -TypeName System.Security.Cryptography.DESCryptoServiceProvider -Property #{key=$str1; IV = $IV; mode = [System.Security.Cryptography.CipherMode]::ECB; Padding=[System.Security.Cryptography.PaddingMode]::None}
$outbyte = new-object "System.Byte[]" 8
$encrypter1 = $hasher.CreateEncryptor()
$outbyte = $encrypter1.TransformFinalBlock($OEM.GetBytes($plaintext),0,8)
$data1 = [System.BitConverter]::ToString($outbyte).replace("-","")
$encrypter1.Dispose()
In theory this should encrypt the Key (which ever one it is) with DES using the first 7 characters of the string ($str1) as the key (with a null byte on the end) and then we do this to the second half ($str2) and concat them back together to get the LMHASH.
The ASCII-encoded string KGS!##$% is the correct magic constant to use
using the first 7 characters of the string ($str1) as the key (with a null byte on the end)
This, however, is incorrect. The key is not composed by padding the 7 bytes of partial input with a single 0-byte at the end, but by partitioning the input into 8 7-bit chunks and left-shifting them once (resulting in 8 bytes).
The easiest way to implement this in PowerShell is probably with strings, so I'd likely do something like this:
# Convert string to byte array
$inBytes = $OEM.GetBytes($str1)
# Create a binary string from our bytes
$bitString = ''
foreach($byte in $inBytes){
$bitstring += [convert]::ToString($byte, 2).PadLeft(8, '0')
}
# Partition the byte string into 7-bit chunks
[byte[]]$key = $bitString -split '(?<=\G.{7}(?<!$))' |ForEach-Object {
# Insert 0 as the least significant bit in each chunk
# Convert resulting string back to [byte]
[convert]::ToByte("${_}0", 2)
}
try{
# Create the first encryptor from our new key, and an empty IV
[byte[]]$iv = ,0 * 8
$enc = $hasher.GetEncryptor($key, $iv)
# Calculate half of the hash
$block1 = $enc.TransformFinalBlock($plaintext, 0, 8)
}
finally{
# Dispose of the encryptor
$enc.Dispose()
}
Then repeat for $str2 and concatenate the resulting blocks for the full LM hash
anyone having issues, based on #mathias R. Jessen
's answer above, here is a fuction that computes half the LM-Hash takes in 7 character string and outputs the hash as Hex.
Function LM-hash {
Param(
[Parameter(mandatory=$true,ValueFromPipeline=$true,position=0)]
[ValidateLength(7,7)]
[String]$Invalue
)
$plaintext = "KGS!##$%"
$OEM = [System.Text.Encoding]::GetEncoding($Host.CurrentCulture.TextInfo.OEMCodePage)
$inBytes = $OEM.GetBytes($invalue)
$bitString = ''
foreach($byte in $inBytes){
$bitstring += [convert]::ToString($byte, 2).PadLeft(8, '0')
}
[byte[]]$key = $bitString -split '(?<=\G.{7}(?<!$))' |ForEach-Object { [convert]::ToByte("${_}0", 2)}
$iv = new-object "System.Byte[]" 8
$DESCSP = New-Object -TypeName System.Security.Cryptography.DESCryptoServiceProvider -Property #{key=$key; IV = $IV; mode = [System.Security.Cryptography.CipherMode]::ECB; Padding=[System.Security.Cryptography.PaddingMode]::None}
$enc = $DESCSP.CreateEncryptor()
$block1 = $enc.TransformFinalBlock($OEM.GetBytes($plaintext), 0, 8)
return [System.BitConverter]::ToString($block1).replace("-","")
$enc.Dispose()
}
this gives the correct result for half the hash, so feeding each half in seperatley and concatenating the strings gives you a full LM hash
Related
I am trying to replicate the encryption result from here in Ruby using OpenSSL: https://emvlab.org/descalc/?key=18074F7ADD44C903&iv=18074F7ADD44C903&input=4E5A56564F4C563230313641454E5300&mode=cbc&action=Encrypt&output=25C843BA5C043FFFB50F76E43A211F8D
Original string = "NZVVOLV2016AENS"
String converted to hexadecimal = "4e5a56564f4c563230313641454e53"
iv = "18074F7ADD44C903"
key = "18074F7ADD44C903"
Expected result = "9B699B4C59F1444E8D37806FA9D15F81"
Here is my ruby code:
require 'openssl'
require "base64"
include Base64
iv = "08074F7ADD44C903"
cipher = "08074F7ADD44C903"
def encode(string)
puts "Attempting encryption - Input: #{string}"
encrypt = OpenSSL::Cipher.new('DES-CBC')
encrypt.encrypt
encrypt.key = ["18074F7ADD44C903"].pack('H*') #.scan(/../).map{|b|b.hex}.pack('c*')
encrypt.iv = ["18074F7ADD44C903"].pack('H*')
result = encrypt.update(string) + encrypt.final
puts "Raw output: #{result.inspect}"
unpacked = result.unpack('H*')[0]
puts "Encrypted key is: #{unpacked}"
puts "Encrypted Should be: 9B699B4C59F1444E8D37806FA9D15F81"
return unpacked
end
res = encode("NZVVOLV2016AENS")
Output:
Encrypted key is: 9b699b4c59f1444ea723ab91e89c023a
Encrypted Should be: 9B699B4C59F1444E8D37806FA9D15F81
Interestingly, the first half of the result is correct, and the last 16 digits are incorrect.
The web site uses Zero padding by default, while the Ruby code uses PKCS#7 padding by default.
Ruby does not seem to support Zero padding, so disable the default padding and implement Zero padding yourself.
Zero padding pads to the next full block size with 0x00 values. The block size for DES is 8 bytes. If the last block of the plaintext is already filled, no padding is done:
def zeroPad(string, blocksize)
len = string.bytes.length
padLen = (blocksize - len % blocksize) % blocksize
string += "\0" * padLen
return string
end
In the encode() function (which should better be called encrypt() function) the following lines must be added before encryption:
encrypt.padding = 0 # disable PKCS#7 padding
string = zeroPad(string, 8) # enable Zero padding
The modified Ruby code then gives the same ciphertext as the web site.
Note that DES is insecure, also it' s insecure to use the key as IV (as well as a static IV). Furthermore, Zero padding is unreliable in contrast to PKCS#7 padding.
I am implementing an encryption in a project that I has in another java project.
The code in java project is this:
public static String cifraDES(String chave, String dado) throws Exception {
DESKeySpec keySpec = new DESKeySpec(hexStringToByteArray(chave));
SecretKeyFactory kf = SecretKeyFactory.getInstance("DES");
SecretKey passwordKey = kf.generateSecret(keySpec);
Cipher c = Cipher.getInstance("DES");
c = Cipher.getInstance("DES/ECB/NoPadding");
c.init(Cipher.ENCRYPT_MODE, passwordKey);
return bytesToHex(c.doFinal(hexStringToByteArray(dado)));
}
In Ruby project i want implement this encrypt too. But this dont work:
dado = "53495A45303030386E6F7661313031305858585858585858"
chave = "3455189635541968"
des = OpenSSL::Cipher.new('des-ecb').encrypt
des.key = chave
s = des.update(dado) + des.final
Base64.encode64(s).gsub(/\n/, "")
In terminal I recive this message:
'key' be must 8 bytes
And i need this return: b42e3dbfffd4bb5487a27fd702f079e287e6325767bfdd20
View:
http://des.online-domain-tools.com/link/1145159gOjlrPNRkaT/
You haven’t converted the key and data from hex strings, you can do that using pack:
dado = ["53495A45303030386E6F7661313031305858585858585858"].pack('H*')
(When you do this to the key, it is converted from 16 hexidecimal characters to 8 bytes, so not doing this step is causing the error are getting).
You haven’t specified no padding:
des.padding = 0
And you want the result hex encoded, not base 64. You can use unpack:
puts s.unpack('H*')[0]
Putting it all together:
dado = ["53495A45303030386E6F7661313031305858585858585858"].pack('H*')
chave = ["3455189635541968"].pack('H*')
des = OpenSSL::Cipher.new('des-ecb').encrypt
des.key = chave
des.padding = 0
s = des.update(dado) + des.final
puts s.unpack('H*')[0]
Result is b42e3dbfffd4bb5487a27fd702f079e287e6325767bfdd20.
The error seems pretty clear to me. The key you're using chave is 16 bytes. Your key has to be 8 bytes. So reduce the length of the key to 8 chars then try.
Edit - Answer posted below
I have a script that usually uses #ARGV arguments but in some cases it is invoked by another script (which I cannot modify) that instead only passes a config filename which among other things has the command line options that should have been passed directly.
Example:
Args=--test --pdf "C:\testing\my pdf files\test.pdf"
If possible I'd like a way to parse this string into an array that would be identical to #ARGV.
I have a workaround where I setup an external perl script that just echos #ARGV, and I invoke this script like below (standard boilerplate removed).
echo-args.pl
print join ("\n", #ARGV);
test-echo-args.pl
$my_args = '--test --pdf "C:\testing\my pdf files\test.pdf"';
#args = map { chomp ; $_ } `perl echo-args.pl $my_args`;
This seems inelegant but it works. Is there a better way without invoking a new process? I did try splitting and processing but there are some oddities on the command line e.g. -a"b c" becomes '-ab c' and -a"b"" becomes -ab" and I'd rather not worry about edge cases but I know that'll bite me one day if I don't.
Answer - thanks ikegami!
I've posted a working program below that uses Win32::API and CommandLineToArgvW from shell32.dll based on ikegami's advice. It is intentionally verbose in the hopes that it'll be more easy to follow for anyone like myself who is extremely rusty with C and pointer arithmetic.
Any tips are welcome, apart from the obvious simplifications :)
use strict;
use warnings;
use Encode qw( encode decode );
use Win32::API qw( );
use Data::Dumper;
# create a test argument string, with some variations, and pack it
# apparently an empty string returns $^X which is documented so check before calling
my $arg_string = '--test 33 -3-t" "es 33\t2 ';
my $packed_arg_string = encode('UTF-16le', $arg_string."\0");
# create a packed integer buffer for output
my $packed_argc_buf_ptr = pack('L', 0);
# create then call the function and get the result
my $func = Win32::API->new('shell32.dll', 'CommandLineToArgvW', 'PP', 'N')
or die $^E;
my $ret = $func->Call($packed_arg_string, $packed_argc_buf_ptr);
# unpack to get the number of parsed arguments
my $argc = unpack('L', $packed_argc_buf_ptr);
print "We parsed $argc arguments\n";
# parse the return value to get the actual strings
my #argv = decode_LPWSTR_array($ret, $argc);
print Dumper \#argv;
# try not to leak memory
my $local_free = Win32::API->new('kernel32.dll', 'LocalFree', 'N', '')
or die $^E;
$local_free->Call($ret);
exit;
sub decode_LPWSTR_array {
my ($ptr, $num) = #_;
return undef if !$ptr;
# $ptr is the memory location of the array of strings (i.e. more pointers)
# $num is how many we need to get
my #strings = ();
for (1 .. $num) {
# convert $ptr to a long, using that location read 4 bytes - this is the pointer to the next string
my $string_location = unpack('P4', pack('L', $ptr));
# make it human readable
my $readable_string_location = unpack('L', $string_location);
# decode the string and save it for later
push(#strings, decode_LPCWSTR($readable_string_location));
# our pointers are 32-bit
$ptr += 4;
}
return #strings;
}
# Copied from http://stackoverflow.com/questions/5529928/perl-win32api-and-pointers
sub decode_LPCWSTR {
my ($ptr) = #_;
return undef if !$ptr;
my $sW = '';
for (;;) {
my $chW = unpack('P2', pack('L', $ptr));
last if $chW eq "\0\0";
$sW .= $chW;
$ptr += 2;
}
return decode('UTF-16le', $sW);
}
In unix systems, it's the shell that parses that shell command into strings. But in Windows, it's up to each application. I think this is normally done using the CommandLineToArgv system call (which you could call with the help of Win32::API), but the spec is documented here if you want to reimplement it yourself.
I am trying to convert some C# code into ruby. Here is a snippet of the C# code:
string Encrypt(string toEncrypt, string key) {
byte[] keyArray;
byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);
keyArray = UTF8Encoding.UTF8.GetBytes(key);
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
tdes.Key = keyArray;
tdes.Mode = CipherMode.ECB;
tdes.Padding = PaddingMode.PKCS7;
ICryptoTransform cTransform = tdes.CreateEncryptor();
byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
return Convert.ToBase64String(resultArray, 0, resultArray.Length);
My biggest issue seems to be around getting the padding specification right.
Here is what I have so far...
des = OpenSSL::Cipher::Cipher.new('des-ecb')
des.encrypt # OpenSSL::PKCS7 has to passed in somewhere
des.key = '--The Key--'
update_value = des.update(val)
After running through all of the available OpenSSL ciphers and testing to see if any of the outputs resulted in the same encrypted string with no success, I then did the same thing, but this time passing in a padding integer (from 0 - 20), and iterated over all of the ciphers again.
This resulted in a success!
The final code:
def encrypt val
des = OpenSSL::Cipher::Cipher.new 'DES-EDE3'
des.encrypt
des.padding = 1
des.key = '--SecretKey--'
update_value = des.update(val)
up_final = update_value + des.final
Base64.encode64(up_final).gsub(/\n/, "")
end
The biggest thing to note is the I had to remove the newline characters, and had to put in a padding of 1.
I'm still confused on the padding...but, wanted to update everyone on what I found in case someone runs into this in the future
:Update: The padding didn't matter after all...if you take out that line it still encrypts the same as if you had any number in there...the big difference I was missing was taking out the newlines
Try 'des-ede3-ecb' or just '3des' as names instead. 'des-ecb' is unlikely to return a triple DES cipher.
PKCS#7 is normally the default for OpenSSL, so you may not have to specify it.
Make sure that your character-encoding (UTF-8, compatible with ASCII for values up to 7F) and encoding (base 64) matches as well.
Below I have some code to get the values of instances of performance counters (which are instantiated once a page is visited) and send them to Graphite to display graphs in the following format:
[Path in Graphite (e.g., metric.pages.Counter1)] [value of counter] [epoch time]
To do this I made the following code where the writer is configured correctly to work:
# Get all paths to MultipleInstance counters and averages that start with "BLABLA" and
# put them into an array and get the epoch time
$pathsWithInstances = (get-counter -ListSet BLABLA*) | select -ExpandProperty PathsWithInstances
$epochtime = [int][double]::Parse((Get-Date -UFormat %s))
# This functions splits the path (e.g., \BLABLA Web(welcome)\Page Requests) into three
# parts: the part before the
# opening brace (the CounterCategory, e.g., "\BLABLA Web"), the part in between the braces
# (the page or
# service, e.g., "welcome"), and the part after the closing brace (the name of the test,
# e.g.,
# "\Page Requests"). We obtain the metric out of this information and send it to
# Graphite.
enter code here
foreach ($pathWithInstance in $pathsWithInstances)
{
$instanceProperties = $pathWithInstance.Split('()')
$counterCategory = $instanceProperties[0]
if ($counterCategory -eq ("\BLABLA Web") )
{
# Replace the * with nothing so that counters that are used to display the
# average (e.g., \BLABLAWeb(*)\Page Requests) are displayed on top in the
# Graphite directory.
$pagePath = $instanceProperties[1].Replace('*','')
$nameOfTheTest = $instanceProperties[2]
# Countername which is used in Graphite path gets whitespace and backslash
# removed in the name used for the path in Graphite (naming conventions)
$counterName = $nameOfTheTest.Replace(' ','').Replace('\','')
$pathToPerfCounter = $pathWithInstance
$pathInGraphite = "metrics.Pages." + $pagePath + $counterName
#Invoked like this since otherwise the get-counter [path] does not seem to work
$metricValue = [int] ((Get-Counter "$pathToPerfCounter").countersamples | select -
property cookedvalue).cookedvalue
$metric = ($pathInGraphite + " " + $metricValue + " " + $epochTime)
$writer.WriteLine($metric)
$writer.Flush()
}
}
Unfortunately this code is very slow. It takes about one second for every counter to send a value. Does someone see why it is so slow and how it can be improved?
You're getting one counter at a time, and it takes a second for Get-Counter to get and "Cook" the values. Get-Counter will accept an array of counters, and will sample, "cook" and return them all in that same second. You can speed it up by sampling them all at once, and then parsing the values from the array of results:
$CounterPaths = (
'\\Server1\Memory\Page Faults/sec',
'\\Server1\Memory\Available Bytes'
)
(Measure-Command {
foreach ($CounterPath in $CounterPaths)
{Get-Counter -counter $counterpath}
}).TotalMilliseconds
(Measure-Command {
Get-Counter $CounterPaths
}).TotalMilliseconds
2017.4693
1012.3012
Example:
foreach ($CounterSample in (Get-Counter $CounterPaths).Countersamples)
{
"Path = $($CounterSample.path)"
"Metric = $([int]$CounterSample.CookedValue)"
}
Path = \\Server1\memory\page faults/sec
Metric = 193
Path = \\Server1\memory\available bytes
Metric = 1603678208
Use the Start-Job cmdlet, to create separate threads for each counter.
Here is a simple example of how to take the Counter Paths and pass them into an asynchronous ScriptBlock:
$CounterPathList = (Get-Counter -ListSet Processor).PathsWithInstances.Where({ $PSItem -like '*% Processor Time' });
foreach ($CounterPath in $CounterPathList) {
Start-Job -ScriptBlock { (Get-Counter -Counter $args[0]).CounterSamples.CookedValue; } -ArgumentList $CounterPath;
}
# Call Receive-Job down here, once all jobs are finished
IMPORTANT: The above example uses PowerShell version 4.0's "method syntax" for filtering objects. Please make sure you're running PowerShell version 4.0, or change the Where method to use the traditional Where-Object instead.