I wrote this in C# to encrypt and decrypt a password, which could be stored in a file, or perhaps the registry. It converts the password into a byte string, that byte string is converted using base64, and then is encrypted using AES. Take note, I have added permutations for the byte streams. These should be changed for every password you encrypt. One final thought, the overall security of this process depends on the protection of your C# code. So…compile this, and keep any source code in a secure location. This is not meant to be used as a secure app for distribution, just as a method to generate the encrypted password. If you’re trying to create a standalone app, look into AppendChar.
Screenshot
Code
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace SecurePassword
{
class Encode_Decode
{
public static class Global
{
// set password
public const string strPassword = "LetMeIn99$";
// set permutations
public const String strPermutation = "ouiveyxaqtd";
public const Int32 bytePermutation1 = 0x19;
public const Int32 bytePermutation2 = 0x59;
public const Int32 bytePermutation3 = 0x17;
public const Int32 bytePermutation4 = 0x41;
}
// The console window
public static void Main(String[] args)
{
Console.Title = "Secure Password v2";
Console.WriteLine("Output---");
Console.WriteLine("");
Console.WriteLine("Password: " + Global.strPassword);
string strEncrypted = (Encrypt(Global.strPassword));
Console.WriteLine("Encrypted: " + strEncrypted);
string strDecrypted = (Decrypt(strEncrypted));
Console.WriteLine("Decrypted: " + strDecrypted);
Console.ReadKey();
}
// encoding
public static string Encrypt(string strData)
{
return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(strData)));
// reference https://msdn.microsoft.com/en-us/library/ds4kkd55(v=vs.110).aspx
}
// decoding
public static string Decrypt(string strData)
{
return Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(strData)));
// reference https://msdn.microsoft.com/en-us/library/system.convert.frombase64string(v=vs.110).aspx
}
// encrypt
public static byte[] Encrypt(byte[] strData)
{
PasswordDeriveBytes passbytes =
new PasswordDeriveBytes(Global.strPermutation,
new byte[] { Global.bytePermutation1,
Global.bytePermutation2,
Global.bytePermutation3,
Global.bytePermutation4
});
MemoryStream memstream = new MemoryStream();
Aes aes = new AesManaged();
aes.Key = passbytes.GetBytes(aes.KeySize/8);
aes.IV = passbytes.GetBytes(aes.BlockSize/8);
CryptoStream cryptostream = new CryptoStream(memstream,
aes.CreateEncryptor(), CryptoStreamMode.Write);
cryptostream.Write(strData, 0, strData.Length);
cryptostream.Close();
return memstream.ToArray();
}
// decrypt
public static byte[] Decrypt(byte[] strData)
{
PasswordDeriveBytes passbytes =
new PasswordDeriveBytes(Global.strPermutation,
new byte[] { Global.bytePermutation1,
Global.bytePermutation2,
Global.bytePermutation3,
Global.bytePermutation4
});
MemoryStream memstream = new MemoryStream();
Aes aes = new AesManaged();
aes.Key = passbytes.GetBytes(aes.KeySize/8);
aes.IV = passbytes.GetBytes(aes.BlockSize/8);
CryptoStream cryptostream = new CryptoStream(memstream,
aes.CreateDecryptor(), CryptoStreamMode.Write);
cryptostream.Write(strData, 0, strData.Length);
cryptostream.Close();
return memstream.ToArray();
}
// reference
// https://msdn.microsoft.com/en-us/library/system.security.cryptography(v=vs.110).aspx
// https://msdn.microsoft.com/en-us/library/system.security.cryptography.cryptostream%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
// https://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes(v=vs.110).aspx
// https://msdn.microsoft.com/en-us/library/system.security.cryptography.aesmanaged%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
}
}
This is hashing I’ve done in C#. I have included MD5, SHA1, SHA256, SHA384, and SHA512, the hashes, and then validation of a working versus non-working password.
Screenshot
The code…just copy/paste into Visual Studio Console Project and compile.
using System;
using System.Text;
using System.Security.Cryptography;
public class ReturnHash
{
public static string ComputeHash(string plainText, string hashAlgorithm, byte[] saltBytes)
{
// Salt size
saltBytes = new byte[8];
// Convert Text to Array
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
// Create Array
byte[] plainTextSaltBytes =
new byte[plainTextBytes.Length + saltBytes.Length];
// Copy Text into Array
for (int i = 0; i < plainTextBytes.Length; i++)
plainTextSaltBytes[i] = plainTextBytes[i];
// Append Salt
for (int i = 0; i < saltBytes.Length; i++)
plainTextSaltBytes[plainTextBytes.Length + i] = saltBytes[i];
// Algorithm
HashAlgorithm hash;
// Initialize Class
switch (hashAlgorithm.ToUpper())
{
case "SHA1":
hash = new SHA1Managed();
break;
case "SHA256":
hash = new SHA256Managed();
break;
case "SHA384":
hash = new SHA384Managed();
break;
case "SHA512":
hash = new SHA512Managed();
break;
default:
hash = new MD5CryptoServiceProvider();
break;
}
byte[] hashBytes = hash.ComputeHash(plainTextSaltBytes);
byte[] hashSaltBytes = new byte[hashBytes.Length +
saltBytes.Length];
// Hash to Array
for (int i = 0; i < hashBytes.Length; i++)
hashSaltBytes[i] = hashBytes[i];
// Append Salt
for (int i = 0; i < saltBytes.Length; i++)
hashSaltBytes[hashBytes.Length + i] = saltBytes[i];
// Convert result into a base64-encoded string.
string hashValue = Convert.ToBase64String(hashSaltBytes);
// Return Result
return hashValue;
}
public static bool VerifyHash(string plainText,
string hashAlgorithm,
string hashValue)
{
// Base64-encoded hash
byte[] hashWithSaltBytes = Convert.FromBase64String(hashValue);
// Hash without Salt
int hashSizeInBits, hashSizeInBytes;
// Size of hash is based on the specified algorithm.
switch (hashAlgorithm.ToUpper())
{
case "SHA1":
hashSizeInBits = 160;
break;
case "SHA256":
hashSizeInBits = 256;
break;
case "SHA384":
hashSizeInBits = 384;
break;
case "SHA512":
hashSizeInBits = 512;
break;
default: // MD5
hashSizeInBits = 128;
break;
}
// Convert to bytes.
hashSizeInBytes = hashSizeInBits / 8;
// Verify Hash Length
if (hashWithSaltBytes.Length < hashSizeInBytes)
return false;
// Array to hold Salt
byte[] saltBytes = new byte[hashWithSaltBytes.Length -
hashSizeInBytes];
// Salt to New Array
for (int i = 0; i < saltBytes.Length; i++)
saltBytes[i] = hashWithSaltBytes[hashSizeInBytes + i];
string expectedHashString =
ComputeHash(plainText, hashAlgorithm, saltBytes);
return (hashValue == expectedHashString);
}
}
public class PasswordEncodingTest1
{
[STAThread]
static void Main(string[] args)
{
// Test Passwords
string rightPassword = "TRUEPassword$";
string wrongPassword = "FALSEPassword$";
string passwordHashMD5 =
ReturnHash.ComputeHash(rightPassword, "MD5", null);
string passwordHashSha1 =
ReturnHash.ComputeHash(rightPassword, "SHA1", null);
string passwordHashSha256 =
ReturnHash.ComputeHash(rightPassword, "SHA256", null);
string passwordHashSha384 =
ReturnHash.ComputeHash(rightPassword, "SHA384", null);
string passwordHashSha512 =
ReturnHash.ComputeHash(rightPassword, "SHA512", null);
Console.WriteLine("Hash and Comparison--- \r\n");
// md5
Console.WriteLine("MD5 {0}", passwordHashMD5);
Console.WriteLine("Right PW MD5 {0}",
ReturnHash.VerifyHash(
rightPassword, "MD5",
passwordHashMD5).ToString());
Console.WriteLine("Wrong PW MD5 {0}",
ReturnHash.VerifyHash(
wrongPassword, "MD5",
passwordHashMD5).ToString());
Console.WriteLine("");
// reference https://en.wikipedia.org/wiki/MD5
// https://msdn.microsoft.com/en-us/library/system.security.cryptography.md5(v=vs.110).aspx
// sha1
Console.WriteLine("SHA1 {0}", passwordHashSha1);
Console.WriteLine("Right PW SHA1 {0}",
ReturnHash.VerifyHash(
rightPassword, "SHA1",
passwordHashSha1).ToString());
Console.WriteLine("Wrong PW SHA1 {0}",
ReturnHash.VerifyHash(
wrongPassword, "SHA1",
passwordHashSha1).ToString());
Console.WriteLine("");
// reference https://en.wikipedia.org/wiki/SHA-1
// https://msdn.microsoft.com/en-us/library/system.security.cryptography.sha1(v=vs.110).aspx
// sha256
Console.WriteLine("SHA256 {0}", passwordHashSha256);
Console.WriteLine("Right PW SHA256 {0}",
ReturnHash.VerifyHash(
rightPassword, "SHA256",
passwordHashSha256).ToString());
Console.WriteLine("Wrong PW SHA256 {0}",
ReturnHash.VerifyHash(
wrongPassword, "SHA256",
passwordHashSha256).ToString());
Console.WriteLine("");
// reference https://en.wikipedia.org/wiki/SHA-2
// https://msdn.microsoft.com/en-us/library/system.security.cryptography.sha256(v=vs.110).aspx
// sha384
Console.WriteLine("SHA384 {0}", passwordHashSha384);
Console.WriteLine("Right PW SHA384 {0}",
ReturnHash.VerifyHash(
rightPassword, "SHA384",
passwordHashSha384).ToString());
Console.WriteLine("Wrong PW SHA384 {0}",
ReturnHash.VerifyHash(
wrongPassword, "SHA384",
passwordHashSha384).ToString());
Console.WriteLine("");
// reference https://msdn.microsoft.com/en-us/library/system.security.cryptography.sha384(v=vs.110).aspx
// sha512
Console.WriteLine("SHA512 {0}", passwordHashSha512);
Console.WriteLine("Right PW SHA512 {0}",
ReturnHash.VerifyHash(
rightPassword, "SHA512",
passwordHashSha512).ToString());
Console.WriteLine("Wrong PW SHA512 {0}",
ReturnHash.VerifyHash(
wrongPassword, "SHA512",
passwordHashSha512).ToString());
// reference https://msdn.microsoft.com/en-us/library/system.security.cryptography.sha512(v=vs.110).aspx
Console.WriteLine("");
Console.WriteLine("");
Console.ReadKey();
}
}
I created this to store passwords in a more secure manner. It could be used to encode a password, and also to retrieve and decode a password. I do permutations based upon a specified number to perform the shift in ASCII (polyalphabetic substitution cipher), but…I’d also like to add a more advanced method, which would increase the complexity of encoding using multiple formulas based upon positioning. As this will be considered v1, I’ll post v2 when complete.
In v2 (to provide better security for passwords, specifically), what I’m going to do is create unique formulas for each position. It may be written in VB or C#.
So, a potential password, let’s say LetMeIn99$, could look like this once encoded: AAAAAAAAAA.
The idea is that there is zero correlation between one position, to the next, as seen from the onlooker.
From Bruce Schneier’s Applied Cryptography, 2nd edition…
Substitution Ciphers
A substitution cipher is one in which each character in the plaintext is substituted for another character in the ciphertext. The receiver inverts the substitution on the ciphertext to recover the plaintext. In classical cryptography, there are four types of substitution ciphers:
— A simple substitution cipher, or monoalphabetic cipher, is one in which each character of the plaintext is replaced with a corresponding character of ciphertext. The cryptograms in newspapers are simple substitution ciphers.
— A homophonic substitution cipher is like a simple substitution cryptosystem, except a single character of plaintext can map to one of several characters of ciphertext. For example, “A” could correspond to either 5, 13, 25, or 56, “B” could correspond to either 7, 19, 31, or 42, and so on.
— A polygram substitution cipher is one in which blocks of characters are encrypted in groups. For example, “ABA” could correspond to “RTQ,” “ABB” could correspond to “SLL,” and so on.
— A polyalphabetic substitution cipher is made up of multiple simple substitution ciphers. For example, there might be five different simple substitution ciphers used; the particular one used changes with the position of each character of the plaintext.
The famous Caesar Cipher, in which each plaintext character is replaced by the character three to the right modulo 26 (“A” is replaced by “D,” “B” is replaced by “E,”…, “W” is replaced by “Z,” “X” is replaced by “A,” “Y” is
replaced by “B,” and “Z” is replaced by “C”) is a simple substitution cipher. It’s actually even simpler, because the ciphertext alphabet is a rotation of the plaintext alphabet and not an arbitrary permutation.
ROT13 is a simple encryption program commonly found on UNIX systems; it is also a simple substitution cipher. In this cipher, “A” is replaced by “N,” “B” is replaced by “O,” and so on. Every letter is rotated 13 places. Encrypting a file twice with ROT13 restores the original file.
P = ROT13 (ROT13 (P))
ROT13 is not intended for security; it is often used in Usenet posts to hide potentially offensive text, to avoid giving away the solution to a puzzle, and so forth.
Simple substitution ciphers can be easily broken because the cipher does not hide the underlying frequencies of the different letters of the plaintext. All it takes is about 25 English characters before a good cryptanalyst can reconstruct the plaintext [1434]. An algorithm for solving these sorts of ciphers can be found in [578,587,1600,78,1475,1236,880]. A good computer algorithm is [703].
Homophonic substitution ciphers were used as early as 1401 by the Duchy of Mantua [794]. They are much more complicated to break than simple substitution ciphers, but still do not obscure all of the statistical properties of the plaintext language. With a known-plaintext attack, the ciphers are trivial to break. A ciphertext-only attack is harder, but only takes a few seconds on a computer. Details are in [1261].
Polygram substitution ciphers are ciphers in which groups of letters are encrypted together. The Playfair cipher, invented in 1854, was used by the British during World War I [794]. It encrypts pairs of letters together. Its cryptanalysis is discussed in [587,1475,880]. The Hill cipher is another example of a polygram substitution cipher [732]. Sometimes you see Huffman coding used as a cipher; this is an insecure polygram substitution cipher.
Polyalphabetic substitution ciphers were invented by Leon Battista in 1568 [794]. They were used by the Union army during the American Civil War. Despite the fact that they can be broken easily [819,577,587,794] (especially with the help of computers), many commercial computer security products use ciphers of this form [1387,1390,1502]. (Details on how to break this encryption scheme, as used in WordPerfect, can be found in [135,139].) The Vigenère cipher, first published in 1586, and the Beaufort cipher are also examples of polyalphabetic substitution ciphers.
Polyalphabetic substitution ciphers have multiple one-letter keys, each of which is used to encrypt one letter of the plaintext. The first key encrypts the first letter of the plaintext, the second key encrypts the second letter of the plaintext, and so on. After all the keys are used, the keys are recycled. If there were 20 one-letter keys, then every twentieth letter would be encrypted with the same key. This is called the period of the cipher. In classical cryptography, ciphers with longer periods were significantly harder to break than ciphers with short periods. There are computer techniques that can easily break substitution ciphers with very long periods.
A running-key cipher—sometimes called a book cipher—in which one text is used to encrypt another text, is another example of this sort of cipher. Even though this cipher has a period the length of the text, it can also be broken easily [576,794].
I wrote this to install a ‘prepared’ package to a single computer, or to a list of computers. It’s low tech to get around the numerous problems associated with remote installation using PowerShell, VBScript, or desktop management applications.
I do plan on adding a form to collect the necessary variables, and a GUI layout to add a little pizzazz to the overall look and functionality. I’ll post that when complete.
* be careful with this, especially if deploying packages to multiple computers
@echooff
Title Script Engine v1 – by Eddie Jackson
color 0a
::PACKAGE PATH, WITHOUT C:\ – will come from formSetAppPath=Progra~2\CompanyFolder
::APP NAME – will come from formsetAppName=FooApp
::PACKAGE/SETUP FILE NAME – will come from formSetFileName=FooSetup.exe
::USE, IF ONLY NEEDED FOR ONE COMPUTER::otherwise, set OnePC=NO – will come from formSetOnePC=NO
::——————————————————-SetCurDir=C:\_ENGINE
SetPCList=computers.txt
setLocal EnableDelayedExpansion
setFilePath=c:\%AppPath%\%FileName%If Not Exist “%CurDir%\logs” (
md%CurDir%\logs
)
If Not Exist “%CurDir%\paexec.exe” (
clsecho Missing PAExec.exe file.
echo.
pause
exit
)
::GENERATES REMOTE SEQUENCE FILEecho @echooff>remoteCommand.cmd
echo title %AppName%>>remoteCommand.cmd
echocls>>remoteCommand.cmd
echoEcho Installing %AppName%…please wait>>remoteCommand.cmd
echo “%FilePath%”>>remoteCommand.cmd
::CREATES DATE AND TIME TIMESTAMP::sets a static timestampfor /F “tokens=2-4 delims=/- ” %%p in (‘date/T’) dosetmdate=%%r%%p%%qfor /F “tokens=1-2 delims=:- ” %%p in (‘time/T’) dosetmtime=%%p%%qSetReportN=%mdate%_%mtime%.txt
if%OnePC%NEQ NO gotoSINGLEecho Multiple PC mode
echo.
for /f “tokens=* delims= ” %%a in (%PCList%) do (
::sets dynamic timestampFOR /F “TOKENS=*” %%B IN (‘DATE/T’) DOSETNowD=%%BFOR /F “TOKENS=*” %%A IN (‘TIME/T’) DOSETNowT=%%Aclsecho Accessing: %%a
c:\windows\System32\ping.exe %%a | find “Reply” > nul
iferrorlevel1 (echo !NowD! !NowT!, %%a, OFFLINE >> “logs\%ReportN%” & echo%%a OFFLINE & echo.
) else (
echo.
echo Copying files to %%a…
if not exist “\\%%a\c$\%AppPath%” md “\\%%a\c$\%AppPath%”
copy /y /v “%CurDir%\remoteCommand.cmd” “\\%%a\c$\%AppPath%”
copy /y /v “%CurDir%\%FileName%” “\\%%a\c$\%AppPath%”
c:\windows\System32\ping.exe -n 4127.0.0.1>nul
clsecho Launching %AppName%on%%a…
“%CurDir%\PAExec.exe” \\%%a -s -i -w “c:\%AppPath%” “c:\%AppPath%\remoteCommand.cmd”
echo !NowD! !NowT!, %%a, ONLINE >> “logs\%ReportN%” & echo%%a ONLINE & echo.
)
)
gotoEND:SINGLEFOR /F “TOKENS=*” %%B IN (‘DATE/T’) DOSETNowD=%%BFOR /F “TOKENS=*” %%A IN (‘TIME/T’) DOSETNowT=%%AsetReportN=_SingleReport.txt
clsecho Single PC mode
echo.
echo Accessing: %OnePC%
c:\windows\System32\ping.exe %OnePC% | find “Reply” > nul
iferrorlevel1 (echo%NowD%%NowT%, %OnePC%, OFFLINE >> “logs\%ReportN%” & echo%OnePC% OFFLINE & echo.
) else (
echo.
echo Copying files to %OnePC%…
if not exist “\\%OnePC%\c$\%AppPath%” md “\\%OnePC%\c$\%AppPath%”
copy /y /v “%CurDir%\remoteCommand.cmd” “\\%OnePC%\c$\%AppPath%”
copy /y /v “%CurDir%\%FileName%” “\\%OnePC%\c$\%AppPath%”
c:\windows\System32\ping.exe -n 4127.0.0.1>nul
echo Launching %AppName%on%OnePC%…
%CurDir%\PAExec.exe \\%OnePC% -s -i -w c:\%AppPath% c:\%AppPath%\remoteCommand.cmd
echo%NowD%%NowT%, %OnePC%, ONLINE >> “logs\%ReportN%” & echo%OnePC% ONLINE & echo.
)
gotoEND:END
@echoonpause
exit /b 0
Notes
FOR /F “tokens=*” %%A IN (C:\script\servers.txt) DO WMIC /Node:%%A Process call create “cmd.exe /c gpupdate”
$List = ‘server1′,’server2′,’server3′,’server4’
foreach ($server in $List) {psexec \\$server gpupdate}
This script will add or remove a domain user from the local admin group based upon their membership to an Active Directory group. You basically just drop the user into the specified group, and the script does the rest. If the user is removed from the AD group, they will be removed from the local admin group.
I tested this in the System Account, so it can be deployed using desktop management software (preferably as a regular policy), or even as part of a login script.
Things to note:
Was tested in W7 and W10.
Was tested in the system account security context.
Tested locally and in desktop management software.
If user is not logged in, the script will exit.
To acquire the current user from the system account, I query the explorer.exe process and return the user.
It does work with nested AD groups.
Compile this script, if used in production.
'I've left some simple logging in place,
'which I used for testing in the SYSTEM ACCOUNT
'you can remove it if not needed
Option Explicit
'on error resume next
Dim objRootDSE, objDBConnection, objRecord, objList, strDomain, strADGroup, strDB
Dim StrGroupDN, strSpace, strCurrentUser, MembershipStatus, strComputer, Return
Dim strNameOfUser, ReturnedUserName, objShell, strUserDomain, colProcesses, objProcess
Set objShell = CreateObject("Wscript.Shell")
'IS USER LOGGED IN?
UserLoggedInStatus()
'AD GROUP - AD GROUP TO QUERY - GROUPS CAN BE NESTED
'SYSTEM ACCOUNT: NO ISSUES
strADGroup = "AD_Group_Name_Here"
'COMPUTERNAME
'SYSTEM ACCOUNT: NO ISSUES
'set this computer name
strComputer = objShell.ExpandEnvironmentStrings("%COMPUTERNAME%")
strComputer = trim(strComputer)
strComputer = lcase(strComputer)
'DOMAIN OR WORKGROUP
'SYSTEM ACCOUNT: NO ISSUES
strUserDomain = objShell.ExpandEnvironmentStrings("%USERDOMAIN%")
strUserDomain = trim(strUserDomain)
strUserDomain = lcase(strUserDomain)
'used for logging - can be removed if not needed
objShell.run "cmd / md c:\setup",0,true
'WORKGROUP STATUS
'IF WORKGROUP IS FOUND, EXIT
'return in user context
if strUserDomain = strComputer then
objShell.run "cmd /c echo user context workgroup>C:\setup\_ComputerType.txt",0,false
Leave()
'return in SYSTEM ACCOUNT
elseif strUserDomain = "workgroup" then
objShell.run "cmd /c echo system account workgroup>C:\setup\_ComputerType.txt",0,false
Leave()
elseif strUserDomain = "OtherWorkgroupName" then
objShell.run "cmd /c echo system account workgroup>C:\setup\_ComputerType.txt",0,false
Leave()
else
objShell.run "cmd /c echo " & strUserDomain & ">C:\setup\_ComputerType.txt",0,false
end if
'DOMAIN
'SYSTEM ACCOUNT: NO ISSUES
Set objRootDSE = GetObject("LDAP://RootDSE")
strDomain = Trim(objRootDSE.Get("DefaultNamingContext"))
'USERNAME
'SYSTEM ACCOUNT: NO ISSUES
'Returns username from explorer.exe process
GetUserName()
strCurrentUser = ReturnedUserName
strCurrentUser = trim(strCurrentUser)
strCurrentUser = lcase(strCurrentUser)
objShell.run "cmd /c echo " & strCurrentUser & ">C:\setup\_CurrentUser.txt",0,false
'some simple validation
if strCurrentUser = strComputer & "$" then Leave()
if strCurrentUser = "" then Leave()
if strCurrentUser = "system" then Leave()
'Group membership is false(disabled) by default
MembershipStatus = "False"
'SET AD DB
strDB = "Select ADsPath From 'LDAP://" & strDomain & "' Where ObjectCategory = 'Group' AND Name = '" & strADGroup & "'"
Set objDBConnection = CreateObject("ADODB.Connection")
objDBConnection.Provider = "ADsDSOObject": objDBConnection.Open "Active Directory Provider"
Set objRecord = CreateObject("ADODB.Recordset")
'CONNECT TO AD
objRecord.Open strDB, objDBConnection
'TEST CONNECTION
'Can't find group - exiting now
if objRecord.EOF = True then
Leave()
end if
'SCAN THROUGH TO END OF GROUPS
'if at end, leave
If objRecord.EOF Then
'Group not found
on error resume next
'If not at end, access next group
Elseif Not objRecord.EOF Then
on error resume next
objRecord.MoveLast: objRecord.MoveFirst
While Not objRecord.EOF
StrGroupDN = Trim(objRecord.Fields("ADsPath").Value)
Set objList = CreateObject("Scripting.Dictionary")
strSpace = " "
NestedADMembers StrGroupDN, strSpace, objList, strCurrentUser
Set objList = Nothing
objRecord.MoveNext
Wend
End If
'ONCE COMPLETE, CHECK MEMBERSHIP STATUS
If MembershipStatus = "True" then
AccessGranted()
Else
RemoveAccess()
end if
'Exit
Leave()
'MY SUBs AND FUNCTIONS
'Grant admin access
Sub AccessGranted()
on error resume next
'SYSTEM ACCOUNT: NO ISSUES
objShell.run "cmd /c echo GRANT ACCESS>C:\setup\_AccessStatus.txt",0,false
objShell.run "net localgroup administrators " & strUserDomain & "\" & strCurrentUser & " /add",0,false
WScript.Sleep 2000
end sub
'Remove admin access
Sub RemoveAccess()
on error resume next
'SYSTEM ACCOUNT: NO ISSUES
objShell.run "cmd /c echo REMOVE ACCESS>C:\setup\_AccessStatus.txt",0,false
objShell.run "net localgroup administrators " & strUserDomain & "\" & strCurrentUser & " /delete",0,false
WScript.Sleep 2000
end sub
'Exit
Sub Leave()
on error resume next
objRecord.Close: Set objRecord = Nothing
objDBConnection.Close: Set objDBConnection = Nothing
Set objRootDSE = Nothing
WScript.Sleep 5000
WScript.Quit
end sub
'Cycles through main Group
Sub MainADGroup(ADPath, strSpace, objList, strCurrentUser)
on error resume next
Dim objADGroup, objADMember, strADMember
Set objADGroup = GetObject(ADPath)
For Each objADMember In objADGroup.Members
strADMember = trim(objADMember.sAMAccountName)
strADMember = lcase(strADMember)
'SYSTEM ACCOUNT: NO ISSUES
objShell.run "cmd /c echo " & strADMember & ">>C:\setup\_GroupMembers.txt",0,false
if strADMember = strCurrentUser then
MembershipStatus = "True"
end if
Next
End Sub
'Cycle through Nested Groups
Function NestedADMembers (ADPath, strSpace, objList, strCurrentUser)
on error resume next
Dim objGroup, objMember, strGroupMember
Set objGroup = GetObject(ADPath)
Set objShell = CreateObject("Wscript.Shell")
For Each objMember In objGroup.Members
strGroupMember = trim(objMember.sAMAccountName)
strGroupMember = lcase(strGroupMember)
'SYSTEM ACCOUNT: NO ISSUES
objShell.run "cmd /c echo " & strGroupMember & ">>C:\setup\_GroupMembers.txt",0,false
if strGroupMember = strCurrentUser then
MembershipStatus = "True"
end if
If Strcomp(Trim(objMember.Class), "Group", vbTextCompare) = 0 Then
If objList.Exists(objMember.ADsPath) Then
'do nothing
Else
objList.Add objMember.ADsPath, 1
MainADGroup objMember.ADsPath, strSpace & " ", objList, strCurrentUser
End If
End If
Next
End Function
'Returns Username from explorer.exe
Function GetUserName()
on error resume next
strComputer = "."
Set objShell = CreateObject("Wscript.Shell")
Set colProcesses = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2").ExecQuery("Select * from Win32_Process Where Name = 'explorer.exe'")
For Each objProcess in colProcesses
Return = objProcess.GetOwner(strNameOfUser)
'SYSTEM ACCOUNT: NO ISSUES
objShell.run "cmd /c echo " & strNameOfUser & ">C:\setup\_ExplorerProcessUser.txt",0,false
If Return <> 0 Then
'no owner
Else
ReturnedUserName = strNameOfUser
End If
Next
End function
'Returns if user is logged on
Function UserLoggedInStatus()
on error resume next
Dim LoggedInStatus, objWMIService, colItems, strComputer, objshell, objItem
Set objShell = CreateObject("Wscript.Shell")
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * from Win32_ComputerSystem",,48)
For Each objItem in colItems
LoggedInStatus = trim(objItem.UserName) & ""
next
if LoggedInStatus = "" then
'SYSTEM ACCOUNT: NO ISSUES
UserLoggedInStatus = FALSE
objShell.run "cmd /c echo " & UserLoggedInStatus & ">C:\setup\_LogonStatus.txt",0,false
WScript.Quit(0)
end if
if LoggedInStatus <> "" then
'SYSTEM ACCOUNT: NO ISSUES
UserLoggedInStatus = TRUE
objShell.run "cmd /c echo " & UserLoggedInStatus & ">C:\setup\_LogonStatus.txt",0,false
end if
Set objItem = Nothing
Set colItems = Nothing
Set objWMIService = Nothing
End function
This can be used to return Last Boot Time on remote computers. Just add computer names into computers.txt, and run script. A report will be generated, as well as each computer’s info displayed in a pop up. You can disable the pop up if not needed.
VBScript
Option Explicit
Dim objFSO, txtComputers, txtReport, RemoteComputer, objWMI, varBootup
Dim varLastBootTime, varSystemUptime, colOS, objOS, txtCName, txtRName
'computers text file
txtCName = "_Computers.txt"
'report text file
txtRName = "_Boot_Report.txt"
Const ForReading = 1
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set txtComputers = objFSO.OpenTextFile(txtCName, ForReading)
Set txtReport = objFSO.CreateTextFile(txtRName)
Do Until txtComputers.AtEndOfStream
RemoteComputer = txtComputers.Readline
Set objWMI = GetObject ("winmgmts:\\" & RemoteComputer & "\root\cimv2")
Set colOS = objWMI.ExecQuery ("Select * from Win32_OperatingSystem")
For Each objOS in colOS
varBootup = objOS.LastBootUpTime
varLastBootTime = DateTranslation(varBootup)
'Writes to report
txtReport.WriteLine "Remote Computer: " & RemoteComputer
txtReport.WriteLine "Last Reboot: " & varLastBootTime
varSystemUptime = DateDiff("h", varLastBootTime, Now)
txtReport.WriteLine "System online since " & varSystemUptime & " hours" & vbCrLf
'Display pop up
msgbox RemoteComputer & ": " & varLastBootTime,,"Last Boot"
Next
Loop
'close write to text file
txtComputers.Close
'done
MsgBox "Done! Report name " & chr(34) & txtRName & chr(34) & " was created.",64,"Report"
WScript.Quit(0)
'clear session
set objFSO = Nothing
set txtComputers = Nothing
set txtReport = Nothing
set objWMI = Nothing
set colOS = Nothing
varBootup = ""
RemoteComputer = ""
varLastBootTime = ""
varSystemUptime = ""
objOS = ""
'translates boot time
Function DateTranslation(varBootup)
DateTranslation = CDate(Mid(varBootup, 5, 2) & "/" & Mid(varBootup, 7, 2) & "/" & Left(varBootup, 4) _
& " " & Mid (varBootup, 9, 2) & ":" & Mid(varBootup, 11, 2) & ":" & Mid(varBootup, 13, 2))
End Function
PowerShell
Clear-Host
$txtComputers = "C:\Report\_Computers.txt"
$txtReport = "C:\Report\_Boot_Report.txt"
#Returns computer names
$computers = Get-Content $txtComputers
#Cycles through
foreach ($RemoteComputer in $computers)
{
#Is computer online
If (Test-Connection -ComputerName $RemoteComputer -ErrorAction SilentlyContinue -Quiet -Count 2) {
#Returns boot time
Get-WmiObject Win32_OperatingSystem -ComputerName $RemoteComputer).LastBootUpTime
#Convert the last boot time to a reader friendly date time format
$time = [System.Management.ManagementDateTimeConverter]::ToDateTime($objWMI)
#output to console
$RemoteComputer + "`n" + $time + "`n"
#output to file
"$RemoteComputer" + " " + "$time" | Out-file -FilePath $txtReport -Append
}
}
Start-Sleep 5
# Clear session
$txtComputers = ""
$txtReport = ""
$computers = ""
$computer = ""
$objWMI = ""
Batch
cls
@Echooff
Title Last Reboot
color 0a
Setlocal EnableDelayedExpansion
:: Report nameSetreport=Boot_Report.txt
:: Enter name of computer text fileSetPCList=Computers.txt
clsEcho Searching for boot times...
:: PROGRAM ROUTINE:CYCLEfor /f "tokens=* delims= "%%a in (%PCList%) do (
ping %%a | find "Reply" > nul
iferrorlevel1 (
echo%%a>> "_offline.txt"
echo.
%windir%\system32\ping.exe -n 2127.0.0.1>nul
) else (
echo.
for /f "tokens=1,2,3,4" %%g in ('dir /a:h \\%%a\c$\pagefile.sys ^|find "pagefile.sys"') do (
setbTime=%%a%%g%%h%%iecho !bTime!
echo !bTime!>>%report%echo.>>%report%echo.
)
)
)
pauseendlocal
exit /b 0:: NOTES
:: you could also script network statistics workstation
:: command: net statistics workstation
:: output: Statistics since 2/21/2017 7:49:38 PM
:::: or use WMIC for /f %%A in ('WMIC OS GET LASTBOOTUPTIME ^| find "."') DO set DTS=%%A
These scripts would be a great addition to any form, where you’re passing AD credentials. These allow you to verify the credentials are correct before continuing.
VBScript
'These values will come from a form
strDomain = "YourDomain"
strUsername = "YourUserName"
strUserPassword = InputBox("","Password")
'Returns True or False
msgbox CheckAccess(strDomain,strUsername,strUserPassword)
Function CheckAccess(strDomain, strUsername, strUserPassword)
Dim objAD, objADUser, strADPath
Const ADS_SECURE_AUTHENTICATION = &h0001
Const ADS_CHASE_REFERRALS_ALWAYS = &H60
strADPath = "LDAP://" & strDomain & "/OU=Users,DC=" & strDomain
On Error Resume Next
set objAD = GetObject("LDAP:")
set objADUser = objAD.OpenDSObject (strADPath, strUsername, strUserPassword, ADS_SECURE_AUTHENTICATION OR ADS_CHASE_REFERRALS_ALWAYS)
if Err.Number <> 0 then
CheckAccess = False
else
CheckAccess = True
end if
Err.Clear
On Error Goto 0
set objAD = Nothing
set objADUser = Nothing
set strADPath = Nothing
strUserPassword = ""
strUsername = ""
strDomain = ""
End Function
PowerShell
$strUserName = "YourUsername"
$strUserPassword = "UserPassword"
CheckAccess $strUserName $strUserPassword
Function CheckAccess {
if (!($strUserName) -or !($strUserPassword)) {
Write-Warning 'Please verify your username and password!'
} else {
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$objAD = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('domain')
$objAD.ValidateCredentials($strUserName, $strUserPassword)
}
}
Here is a weird one for you. We’re upgrading to the latest version of LANDesk, and for some reason, numerous LD profile folders are being generated in C:\users (I’m thinking is has to do with the ALS service/LANDesk account). The folders either start with ALS_SVC or TEMP.%computername%.
Two things you’ll need to do
#1 Remove LD profiles from C:\users
#2 Remove LD profiles from the registry path:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
Initially, I wrote scripts in PowerShell and VB that looked at folder size and removed 0 byte sizes, the problem was…while they worked in other folders just fine, they could not read all of the C:\users folders—-no access (certain vb and ps objects just could not access the folders). So, I went low tech with this one. And, guess what? It worked. I wrote this batch and compiled it into an EXE. Once launched, it found the 0 byte size folders and removed them.
@echo off title LANDesk Cleanup setlocal enabledelayedexpansion
set DName= set FSize=0
C: cd\users
:: return objects in directory for /f “tokens=*” %%A in (‘dir c:\users /a /b’) do ( set DName=c:\users\%%A echo Checking !DName!…
:: scan through all folders :: set total size of folder to cumulative file sizes for /f “tokens=3-4” %%B in (‘dir “!DName!” /s ^| find /i “file(s)”‘) do ( set FSize=%%B ) :: if no files were returned, this directory has a size 0 :: if size 0, then remove folder and all subfolders :: log the action if !FSize! EQU 0 rd /q /s “!DName!” && echo %Date% !DName! !FSize!>>C:\users\removal_log.txt echo. ) endlocal
* Update 02/20/2017
So, come to find out, NOT all the LANDesk profile folders were 0 byte. So, I ended up writing a new script to just delete ALS_SVC* and TEMP.* folders
@echo off title LANDesk Cleanup setlocal enabledelayedexpansion
c: cd\users cls
set DName= set FSize= set CName=%computername% set str1=ALS_SVC set str2=TEMP. set found=false
:: GET FOLDER For /d %%A in (*) Do ( set found=false set DName=c:\users\%%A
:: UPDATE TIMESTAMP
for /F “TOKENS=*” %%T in (‘Time /t’) do set NowT=%%T
:: FIND FOLDER SIZE
for /f “tokens=3-4” %%S in (‘dir “!DName!” /s ^| find /i “file(s)”‘) do set FSize=%%S
IF NOT ALS OR TEMP, LOG
if !found! EQU false echo NOT_DELETED %Date% !NowT! “!DName!” !FSize!>>c:\users\removal_log.txt )
set DName= set FSize set CName= set str1= set str2=
endlocal exit /b 0
* Update 02/21/2017
Yet another update. It was requested that the amount of folders being deleted, a timestamp of the last run date of the deployment package, and total times the package has run, all be tracked. So, I modified the script above to add counters for the package and for the folders being deleted. These variables are added to the registry, which are then scanned via our desktop management software.
The reg keys that are added
In LANDesk, these are published to clients
The final result, an updated inventory record with tracking info
The modified script – this was compiled
@echo off title LANDesk Cleanup setlocal enabledelayedexpansion
c: cd\users cls
:: folders to look for set str1=ALS_SVC set str2=TEMP.
:: directory name set DName=
:: directory size set FSize=
:: bool: if folder matches logic set found=false
set fCount= set rCount1=0 set rCount2=0
:: reg key set AppName=ProfileCleanup set RegKey1=”HKLM\SOFTWARE\LANDesk\Tracking\%AppName%” /v “Counter” set RegKey2=”HKLM\SOFTWARE\LANDesk\Tracking\%AppName%” /v “Last_Run” set RegKey3=”HKLM\SOFTWARE\LANDesk\Tracking\%AppName%” /v “Folders_Deleted”
:: REMOVE ALL ALS_SVC* FOLDERS For /d %%A in (*) Do ( set found=false set DName=c:\users\%%A for /F “TOKENS=*” %%T in (‘Time /t’) do set NowT=%%T
for /f “tokens=3-4” %%S in (‘dir “!DName!” /s ^| find /i “file(s)”‘) do set FSize=%%S
:: retrieve current count and add 1 :: this is how many times the script has run :Main cls echo Retrieving Reg Count… FOR /f “tokens=1,2,3” %%a In (‘%windir%\sysnative\REG QUERY %RegKey1% /reg:64’) do ( set rCount1=%%c set /a rCount1+=1 && %windir%\sysnative\REG ADD %RegKey1% /d !rCount1! /reg:64 /f ping -n 4 127.0.0.1>nul )
:: this is how many folders have been deleted cls echo Retrieving Folder Count… FOR /f “tokens=1,2,3” %%a In (‘%windir%\sysnative\REG QUERY %RegKey3% /reg:64’) do ( set rCount2=%%c set /a “rCount2=!rCount2!+!fCount!” && %windir%\sysnative\REG ADD %RegKey3% /d !rCount2! /reg:64 /f ) :: add timestamp to registry %windir%\sysnative\REG ADD %RegKey2% /d “%date% %time%” /reg:64 /f
:: clear session set RegKey1= set RegKey2= set RegKey3= set fCount= set rCount1= set rCount2= set AppName=
set DName= set FSize set str1= set str2= set NowT=
:: clean up registry keys cls echo Removing LANDesk ALS profiles… for /f “tokens=1,2 delims==” %%s IN (‘WMIC path win32_useraccount where name^=’administrator’ get sid /value ^| find /i “SID”‘) do set SID=%%t set RootSID=%SID:~0,-5% set RegPath=HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
set /a counter=3 :loop1 if %counter% lss 10 ( reg delete “%regPath%\%RootSID%-100%counter%” /f /reg:64 set /a counter+=1 goto :loop1 )
set /a counter=10 :loop2 if %counter% lss 100 ( reg delete “%regPath%\%RootSID%-10%counter%” /f /reg:64 set /a counter+=1 goto :loop2 )
set /a counter=100 :loop3 if %counter% lss 999 ( reg delete “%regPath%\%RootSID%-1%counter%” /f /reg:64 set /a counter+=1 goto :loop3 )
endlocal exit /b 0
* Note how %windir%\sysnative was used. You’ll use %windir%\system32 if using a 64bit compiler or running script not compiled
Notes
As for what’s causing the issue, beyond the ALS account, LANDesk is still trying to figure it out.
Some C# code I was working on to do the task above.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
class LANDeskProfileCleanup
{
public static void Main(string[] args)
{
try
{
string dirPath = @"c:\users\";
List<string> dirs List<string>(Directory.EnumerateDirectories(dirPath));
foreach (var dir in dirs)
{
long length = Directory.GetFiles(dir, "*", SearchOption.AllDirectories).Sum(t =&amp;gt; (new FileInfo(t).Length));
Console.WriteLine("{0}", dir.Substring(dir.LastIndexOf("\\") + 1));
}
//Console.WriteLine("{0} Folders found.", dirs.Count);
}
catch (UnauthorizedAccessException UAEx)
{
Console.WriteLine(UAEx.Message);
}
catch (PathTooLongException PathEx)
{
Console.WriteLine(PathEx.Message);
}
Console.ReadLine();
}
public static long DirSize(DirectoryInfo dir)
{
return dir.GetFiles().Sum(fi => fi.Length) +
dir.GetDirectories().Sum(di => DirSize(di));
}
}