I'm trying to get the user's last logon in some Windows machines using WMI, but for some reason, this information is different for different commands when I think they should be the same.
The first command that I'm using is : PATH Win32_NetworkLoginProfile WHERE "Name='DOMAIN\\fakeuser'" GET LastLogon. The result for it is like below:
LastLogon
20181206093540.000000-480
The second command is: PATH Win32_NTLogEvent WHERE "(EventIdentifier =4648 OR EventIdentifier = 4647 OR EventIdentifier = 4634)" GET CategoryString, TimeGenerated, InsertionStrings
The result is like below (after some processing to find the last entry of category "Logon" linked to the "fakeuser", since the command returns a lot of information):
CategoryString
Logon
InsertionStrings
{"S-1-5-21-3457937927-2839227994-823803824-1104","DOMAIN$","DOMAIN","0x3e6","{00000000-0000-0000-0000-000000000000}","fakeuser","DOMAIN","{00000000-0000-0000-0000-000000000000}","localhost","localhost","0x64c","C:\Windows\System32\svchost.exe","999.999.99.999","0"}
TimeGenerated
20181206173540.580545-000
For what I understand for these two commands, the LastLogon in the first result should be the same of TimeGenerated in the second one. Am I misunderstood something?
In my preliminary research, I found a possible bug in the WMI Timestamps, but I don't know if it is the same problem.
Some additional information:
These commands are executed using a script that make a remote connection using WinRM connection (ports 5985 and 5986) and then executes the commands to get the info, but I also tried to connect in the machine using RDP and execute it in Powershell with wmic PATH.... The result is the same.
I tested it in Windows 10 and also in Windows Server 2012, but the scripted will be used in some other Windows versions.
To get the Event numbers for the log class, I used this link
After first comment, I noticed that the problem is in time zones. Are there any way to set timezone direct in these commands or convert timezones between them?
Imagine that we have a program or script that can take a password (or other sensitive information) argument:
> program.exe /password:secret
For Linux, best practice generally recommends against specifying the password directly on the command-line because of potential security concerns (the password can appear in the shell's history file and the system's process table):
$ ./program.sh --password 'secret' &
[1] 4152
$ cat /proc/4152/cmdline
/bin/sh./program.sh--passwordsecret
However, when searching around, I don't see the same vigorous recommendation for Windows.
When writing programs and scripts for Windows, should we provide an alternate means to input a password besides an argument to a command-line option to avoid unintentionally exposing the password?
The answer to this question suggests that a dedicated password input prompt improves security by preventing shoulder-surfing. Are there any other ways to exploit a visible password in a command string that justifies writing alternate input methods, both in the context of a shell and in the way Windows manages processes?
Do the security implications change when starting a program from a batch file or PowerShell script that passes a password as an argument to the program?
$password = # prompt for password
program.exe /password:$password
Edit - I understand that we can misinterpret this question as opinion-based because I mention "best practices" and "recommendations" to provide background. However, I seek specific evidence that demonstrates how a password might be vulnerable in a command argument and—if this assumption is valid—concrete examples or references that depict secure alternatives.
Windows historically didn't save command history between sessions, only within the session. This was true for the command prompt and for PowerShell.
As Bill Stewart pointed out, Windows PowerShell on Windows 10 and Windows 2016 includes PSReadline by default, which does save your command history between sessions. You can see this by looking at the file here: (Get-PSReadLineOption).HistorySavePath.
But even if it's set to off, or on an OS version that didn't offer the option, that doesn't mean entering a plaintext password as an argument is a good idea.
If you must offer that, you should also have a way to have the program prompt at run time.
For PowerShell and other .Net applications, you have another issue with accepting plaintext passwords: they linger in memory and there's no good way to explicitly clear them.
This issue is two-fold: strings are immutable in .Net, which means you cannot just modify the string with nulls or random characters to clear it in memory (you will actually be creating a brand new string), and on top of that you cannot control when a specific object will handled by garbage collection, so you can't explicitly remove it.
This is why the SecureString class exists, but not everything can use this.
In PowerShell, there is a PSCredential object which stores a user name in plain text and a password as a SecureString. This should always be used in PowerShell, and should be the preferred argument type (in lieu of a separate user name and password).
Most commands in PowerShell that require a credential take it as this type of object.
You can retrieve a plaintext version of the password easily with this object as well. Doing so then puts that into a managed string and you get the risks I mentioned above.
In my opinion though, it is still preferable to use a PSCredential object in these situations, right up until the point you need the plaintext version. It helps to maintain the standardization of this type in both a built-in/'official' capacity, as well as in user-defined commands.
This type is also easily serializable with Export-Clixml into a form that is encrypted. This can give you a really nice way of providing an automated option to use stored credentials in scripts, with nothing in plaintext, and no prompting or user intervention required.
To more directly answer my question, here's a small supplement to briantist's excellent answer:
When security is a concern, do not input or request passwords or other sensitive data as command-line arguments. Other users and programs can snoop on command-line arguments fairly trivially.
We can use Windows Management Instrumentation to pull command-line arguments in PowerShell and through other programs like those built on .NET. Here's an example using PowerShell:
PS> Get-WmiObject Win32_Process -Filter 'Name = "program.exe"' | Select-Object CommandLine
C:\program.exe /password:secret
The command-line password argument is also visible in the Task Manager for any users on the same system that can see processes from other users:
As briantist wrote in his answer, programs and scripts that we write should provide alternate means to input sensitive information besides command-line arguments. PowerShell scripts can securely pass this information between scripts using PSCredential or SecureString objects.
Is there a way to create a process with wmic on localhost as a different user.Using something like this:
wmic /node:localhost /user:user process call create "cmd"
I know I can use runas but I'm curious if this is possoble.
May be some of the global switches could help? (/ROLE,/IMPLEVEL,/AUTHLEVEL,/AGGREGATE,/AUTHORITY ) .Or changing some configuration?
At the moment I get this error:
ERROR:
Description = User credentials cannot be used for local connections
EDIT: this is the best solution that I've found for such cases
The WMI engine doesn't let you use credentials to connect locally so hence WMIC won't either. RunAs is your best bet.
Assume I have access to a SMB server at IP 1.2.3.4, how can I determine the list of available shares?
Windows Explorer can do it when I enter a UNC path \\1.2.3.4\ - but command prompt "dir \\1.2.3.4\" fails!
I've tried the usual FindFirstFile/FindNext calls - which I use successfully to read the files and directories on each share, but they don't work directly on the server root.
Ideally, I need something that works for XP onwards.
Edit: I want to do this programatically, rather than from command line. Redirecting and parsing the output from 'net view 1.2.3.4' would work, but I'm ideally looking for an API for this job.
According to the docs there's a NetShareEnum and a WNetEnumResource function.
net view \\1.2.3.4
Check out http://www.ss64.com/nt/net_share.html
Edit: If you want to do this programmatically, it looks like NetShareEnum would work.
It appears you can use WMI to get this information. Check this post for some neat PowerShell tutorials that show now to use the Win32_Share WMI object.
Never use WMI (not installed natively)
Use Win32 api to enumerate shares, posted millions of times on google groups( win32)
Suppose some Windows service uses code that wants mapped network drives and no UNC paths. How can I make the drive mapping available to the service's session when the service is started? Logging in as the service user and creating a persistent mapping will not establish the mapping in the context of the actual service.
Use this at your own risk. (I have tested it on XP and Server 2008 x64 R2)
For this hack you will need SysinternalsSuite by Mark Russinovich:
Step one:
Open an elevated cmd.exe prompt (Run as administrator)
Step two:
Elevate again to root using PSExec.exe:
Navigate to the folder containing SysinternalsSuite and execute the following command
psexec -i -s cmd.exe
you are now inside of a prompt that is nt authority\system and you can prove this by typing whoami. The -i is needed because drive mappings need to interact with the user
Step Three:
Create the persistent mapped drive as the SYSTEM account with the following command
net use z: \\servername\sharedfolder /persistent:yes
It's that easy!
WARNING: You can only remove this mapping the same way you created it, from the SYSTEM account. If you need to remove it, follow steps 1 and 2 but change the command on step 3 to net use z: /delete.
NOTE: The newly created mapped drive will now appear for ALL users of this system but they will see it displayed as "Disconnected Network Drive (Z:)". Do not let the name fool you. It may claim to be disconnected but it will work for everyone. That's how you can tell this hack is not supported by M$.
I found a solution that is similar to the one with psexec but works without additional tools and survives a reboot.
Just add a sheduled task, insert "system" in the "run as" field and point the task to a batch file with the simple command
net use z: \servername\sharedfolder /persistent:yes
Then select "run at system startup" (or similar, I do not have an English version) and you are done.
You'll either need to modify the service, or wrap it inside a helper process: apart from session/drive access issues, persistent drive mappings are only restored on an interactive logon, which services typically don't perform.
The helper process approach can be pretty simple: just create a new service that maps the drive and starts the 'real' service. The only things that are not entirely trivial about this are:
The helper service will need to pass on all appropriate SCM commands (start/stop, etc.) to the real service. If the real service accepts custom SCM commands, remember to pass those on as well (I don't expect a service that considers UNC paths exotic to use such commands, though...)
Things may get a bit tricky credential-wise. If the real service runs under a normal user account, you can run the helper service under that account as well, and all should be OK as long as the account has appropriate access to the network share. If the real service will only work when run as LOCALSYSTEM or somesuch, things get more interesting, as it either won't be able to 'see' the network drive at all, or require some credential juggling to get things to work.
A better way would be to use a symbolic link using mklink.exe. You can just create a link in the file system that any app can use. See http://en.wikipedia.org/wiki/NTFS_symbolic_link.
There is a good answer here:
https://superuser.com/a/651015/299678
I.e. You can use a symbolic link, e.g.
mklink /D C:\myLink \\127.0.0.1\c$
You could us the 'net use' command:
var p = System.Diagnostics.Process.Start("net.exe", "use K: \\\\Server\\path");
var isCompleted = p.WaitForExit(5000);
If that does not work in a service, try the Winapi and PInvoke WNetAddConnection2
Edit: Obviously I misunderstood you - you can not change the sourcecode of the service, right? In that case I would follow the suggestion by mdb, but with a little twist: Create your own service (lets call it mapping service) that maps the drive and add this mapping service to the dependencies for the first (the actual working) service. That way the working service will not start before the mapping service has started (and mapped the drive).
ForcePush,
NOTE: The newly created mapped drive will now appear for ALL users of this system but they will see it displayed as "Disconnected Network Drive (Z:)". Do not let the name fool you. It may claim to be disconnected but it will work for everyone. That's how you can tell this hack is not supported by M$...
It all depends on the share permissions. If you have Everyone in the share permissions, this mapped drive will be accessible by other users. But if you have only some particular user whose credentials you used in your batch script and this batch script was added to the Startup scripts, only System account will have access to that share not even Administrator.
So if you use, for example, a scheduled ntbackuo job, System account must be used in 'Run as'.
If your service's 'Log on as: Local System account' it should work.
What I did, I didn't map any drive letter in my startup script, just used net use \\\server\share ... and used UNC path in my scheduled jobs. Added a logon script (or just add a batch file to the startup folder) with the mapping to the same share with some drive letter: net use Z: \\\... with the same credentials. Now the logged user can see and access that mapped drive. There are 2 connections to the same share. In this case the user doesn't see that annoying "Disconnected network drive ...". But if you really need access to that share by the drive letter not just UNC, map that share with the different drive letters, e.g. Y for System and Z for users.
Found a way to grant Windows Service access to Network Drive.
Take Windows Server 2012 with NFS Disk for example:
Step 1: Write a Batch File to Mount.
Write a batch file, ex: C:\mount_nfs.bat
echo %time% >> c:\mount_nfs_log.txt
net use Z: \\{your ip}\{netdisk folder}\ >> C:\mount_nfs_log.txt 2>&1
Step 2: Mount Disk as NT AUTHORITY/SYSTEM.
Open "Task Scheduler", create a new task:
Run as "SYSTEM", at "System Startup".
Create action: Run "C:\mount_nfs.bat".
After these two simple steps, my Windows ActiveMQ Service run under "Local System" priviledge, perform perfectly without login.
The reason why you are able to access the drive in when you normally run the executable from command prompt is that when u are executing it as normal exe you are running that application in the User account from which you have logged on . And that user has the privileges to access the network. But , when you install the executable as a service , by default if you see in the task manage it runs under 'SYSTEM' account . And you might be knowing that the 'SYSTEM' doesn't have rights to access network resources.
There can be two solutions to this problem.
To map the drive as persistent as already pointed above.
There is one more approach that can be followed. If you open the service manager by typing in the 'services.msc'you can go to your service and in the properties of your service there is a logOn tab where you can specify the account as any other account than 'System' you can either start service from your own logged on user account or through 'Network Service'. When you do this .. the service can access any network component and drive even if they are not persistent also.
To achieve this programmatically you can look into 'CreateService' function at
http://msdn.microsoft.com/en-us/library/ms682450(v=vs.85).aspx and can set the parameter 'lpServiceStartName ' to 'NT AUTHORITY\NetworkService'. This will start your service under 'Network Service' account and then you are done.
You can also try by making the service as interactive by specifying SERVICE_INTERACTIVE_PROCESS in the servicetype parameter flag of your CreateService() function but this will be limited only till XP as Vista and 7 donot support this feature.
Hope the solutions help you.. Let me know if this worked for you .
I find a very simple method: using command "New-SmbGlobalMapping" of powershell, which will mount drive globally:
$User = "usernmae"
$PWord = ConvertTo-SecureString -String "password" -AsPlainText -Force
$creds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord
New-SmbGlobalMapping -RemotePath \\192.168.88.11\shares -Credential $creds -LocalPath S:
You wan't to either change the user that the Service runs under from "System" or find a sneaky way to run your mapping as System.
The funny thing is that this is possible by using the "at" command, simply schedule your drive mapping one minute into the future and it will be run under the System account making the drive visible to your service.
I can't comment yet (working on reputation) but created an account just to answer #Tech Jerk #spankmaster79 (nice name lol) and #NMC issues they reported in reply to the "I found a solution that is similar to the one with psexec but works without additional tools and survives a reboot." post #Larry had made.
The solution to this is to just browse to that folder from within the logged in account, ie:
\\servername\share
and let it prompt to login, and enter the same credentials you used for the UNC in psexec. After that it starts working. In my case, I think this is because the server with the service isn't a member of the same domain as the server I'm mapping to. I'm thinking if the UNC and the scheduled task both refer to the IP instead of hostname
\\123.456.789.012\share
it may avoid the problem altogether.
If I ever get enough rep points on here i'll add this as a reply instead.
Instead of relying on a persistent drive, you could set the script to map/unmap the drive each time you use it:
net use Q: \\share.domain.com\share
forfiles /p Q:\myfolder /s /m *.txt /d -0 /c "cmd /c del #path"
net use Q: /delete
This works for me.