Friday, May 15, 2009

Episode #36: File Linking

Paul pops off:

Creating links between files is a handy feature in UNIX/Linux systems. There are many instances where you need to have a copy of the file (or dirctory) in a particular location, but only want to maintain one original. For example, I was running a program to check the security of my Apache configuration file. It expected the file to exist in "/usr/local/apache2/conf/httpd.conf", but the original file was located at "/etc/httpd/conf/httpd.conf". To solve this problem I created a "soft" link as follows:

$ ln -s /etc/httpd/conf/httpd.conf /usr/local/apache2/conf/httpd.conf


The above "ln" command takes the "-s" flag to indicate a soft link, which creates a pointer to the original file. Next you specify the original file, followed by the file that will point to the original. Many will forget which one comes first (the original or the pointer), so don't forget that the original file always comes first :) Oh, and you can view the links by using the ls -l command:

$ ls -l /usr/local/apache2/conf/httpd.conf
lrwxrwxrwx 1 root root 26 Apr 21 13:57 /usr/local/apache2/conf/httpd.conf -> /etc/httpd/conf/httpd.conf


Hal chimes in:

Let me show you one more useful trick with the "ln" command. You can actually create symlinks to an entire directory of files with a single "ln" command:

# cd /usr/local/bin
# ln -s ../depot/clamav/current/bin/* .

First we "cd" to /usr/local/bin. The "ln" command creates a link to every object under /usr/local/depot/clamav/current/bin. The names of the links in /usr/local/bin will have the same name as the files under .../clamav/current/bin.

This is how I manage software that I've built from source on my systems. In fact, .../clamav/current is itself a symlink to a directory like .../clamav/0.95.1. Whenever I build the latest version of ClamAV, I install it in its own .../clamav/<vers> directory and just change the .../clamav/current symlink to point to the latest and greatest version. Since all the symlinks under /usr/local/{bin,sbin,etc,lib,include} are expressed using the .../clamav/current link, every other link in the hierarchy automatically starts pointing at the right version as soon as I change the .../clamav/current link. And it's easy to revert too, just in case the new version isn't working for some reason. Slick.

Ed responds:

Sadly, Microsoft never got around to implementing a pure-play shortcut-creating feature inside of cmd.exe. Because of that, several folks have released third-party tools that do so. Some nice ones include the NT resource kit tool simply called shortcut.exe, Pixelab's xxcopy, and NirSoft's NirCmd.

But, downloading a third-party tool isn't our way at this here blog. So, we must explore other options.

While cmd.exe itself doesn't have a feature for creating shortcuts, wscript, which is built in, does. There are many examples out on the Internet for creating shortcuts with wscript, but I've boiled them down to their bare minimum:

set WshShell = WScript.CreateObject("WScript.Shell" )
set oShellLink = WshShell.CreateShortcut( Wscript.Arguments.Named("shortcut") & ".lnk" )
oShellLink.TargetPath = Wscript.Arguments.Named("target")
oShellLink.Save

The above script takes two arguments: the name of the target you want to create a shortcut to (/target:) and the shortcut name itself (/shortcut:). Note that the target could be a file or a directory. To create a shortcut using this script, we could dump all of that stuff above into a file called shortcutter.vbs, and then run it with the wscript interpreter.

"Ah... but that would be a scripting solution and not a command line," you might say. "You need to create a single command line that addresses the challenge."

Thanks for the delightful reminder. What, are you on Hal's payroll? Don't you have anything better to do with your time than taunt me? ;)

OK... I'll take your input and respond with this for a command line:

C:\> echo set WshShell = WScript.CreateObject("WScript.Shell" ) > shortcutter.vbs &
echo set oShellLink = WshShell.CreateShortcut( Wscript.Arguments.Named("shortcut") ^& ".lnk" )
>> shortcutter.vbs & echo oShellLink.TargetPath = Wscript.Arguments.Named("target")
>> shortcutter.vbs & echo oShellLink.Save >> shortcutter.vbs &
wscript shortcutter.vbs /target:[source] /shortcut:[shortcut]


It pretty much types itself, doesn't it? Easy!

Uh.... or not.

I'm simply creating the vbs script, which I'm naming shortcutter.vbs, and then invoking it to create the shortcut. I don't delete it at the end, because I want to keep it around for future uses. These things come in handy, you know.

Wednesday, May 13, 2009

Episode #35: Remotely Locking Out User While Preserving Session

Ed kicks it off:

We received a request the other day from Mr. Fordm via the Pauldotcom IRC channel. He was wondering if there was a way to lock out a user engaged in an active session on a machine. This kind of thing comes up from time to time, often during abrupt employee termination. Here's the scenario: User John Doe gets canned. He's sitting at his computer logged on in his cubicle and the IT or security staff is instructed to just get him off the machine immediately. Any delay, and there is a chance he'd launch the missiles against friendly targets or something.

The security guy suggests just remotely shutting the system down. But, no... management wants more. They want to preserve the currently logged on session so they can see if John had started to launch the missiles by typing:

C:\> wmic missiles call launch target=...*

So, how can we lock the user out while preserving the GUI session which might hold some juicy info?

First off, we want to change the user's password. Otherwise, he or she would log right back in once we lock the session. Let's assume the user is logged in via a local account, and change the password by using remote command execution via WMIC. We covered remote command execution in Episode #31, which we'll use to invoke the "net user" command to change the password:

C:\> wmic /node:[IPaddr] /user:[Admin] /password:[password] process call
create "net user [user] [NewPassword]"

You can go further, disabling the account so that no one can login with it until you re-enable it, by running:

C:\> wmic /node:[IPaddr] /user:[Admin] /password:[password] process call
create "net user [user] /active:no"

Remember, if you want to get back into this user's session later, you'll have to re-enable that user by running:

C:\> wmic /node:[IPaddr] /user:[Admin] /password:[password] process call
create "net user [user] /active:yes"

Next, we've got to lock the session. On first blush, you might think to use the following command, wrapped up inside of WMIC for remote execution:

C:\> rundll32.exe user32.dll,LockWorkStation


When executed by a local user currently logged on to a Windows box, this will lock the workstation. Nice... but... executed remotely, using WMIC as shown above, won't do the trick on most versions of Windows. You see, this command against a remote target won't be able to get access to the user's currently logged on console GUI session, so nothing happens.

You might think that we can get a little more intricate by running the logoff command against the user, again wrapped up inside of WMIC:

C:\> logoff


Nope... same problem. Works great locally, but remotely, it can't interact with that console session. And, worse... if it did work, it would eliminate the session with the juicy information we want to preserve when it logs off the user.

So, what to do? There's a great command for doing just this kind of thing: tsdiscon.

You can run it as follows:

C:\> wmic /node:[IPaddr] /user:[Admin] /password:[password] process call
create "tsdiscon"
Alternatively, the tsdiscon command has an option to run remotely:

C:\> tsdiscon console /server:[IPaddr] /v

This works like a champ on XP, locking the user at the console out, while preserving the session.

Note that tsdiscon, when run remotely, will pass through your current user's authentication credentials to the target IPaddr machine. Thus, make sure you are logged in with a user and password combination that are also in the admin group of the target machine, or that have domain admin privileges.

Unfortunately, while this works great on XP, the tsdiscon command doesn't allow you to disconnect the console session for Windows Vista or 2008 Server. I've confirmed this in my lab, and have found references to that limitation in Microsoft documentation. On Vista and 2008, you can use tsdiscon to disconnect RDP/Terminal Services sessions other than the console session (you can get a list of sessions on Vista and 2008 by running "query session" or by running "qwinsta" on XP). Sadly, I haven't found a remote command-line method for closing the console session on Vista or 2008 server while preserving that session. The rwinsta command in XP and Vista resets a session on a Vista or XP box, when used as follows:

C:\> wmic /node:[IPaddr] /user:[Admin] /password:[password] process call
create "rwinsta console"

...but you'll lose all of the current session information and running programs when rwinsta kills the session. Still, that'll let you lock out the user so he can't launch the missiles... but at the cost of losing the cmd.exe session history showing that he tried to launch them. For most purposes, that'll suffice. And, I guess it provides yet another reason to stay on XP (as if you needed any more of them).

If you know of a way to remotely disconnect a console user session on Vista using built-in command-line tools, please do send in a suggestion to suggestions@commandlinekungfu.com, and I'll gladly add it to this article.

*Current versions of Windows do not expose the missiles alias within wmic. In Windows 9 (code name: "We miss Bill"), though, it will be built-in, along with the callable method "launch". Just wait.

Hal reports from the bunker:

As Ed points out, the first trick is to lock the user's account. I'm going to assume that the system is using local password files, rather than a networked authentication database such as LDAP or Kerberos. These latter systems have their own command-line interfaces which allow you to lock user accounts, but they're outside of the scope of this blog.

So we need to SSH into the user's workstation and gain root privileges via su or sudo. Note that this assumes you have an SSH server running for remote maintenance tasks. A lot of Linux workstation builds don't automatically configure an SSH server by default. You're "Seriously Out of Luck" in these cases, and the best that you can do is try to seize the workstation before the user has a chance to launch their missiles. If you have an intelligent switch fabric, you might want to move the user's workstation onto an isolated VLAN before seizing the workstation. That way, the user might have a chance to trash their own system, but less opportunity to launch missiles at other targets.

Once you're into the system, use "passwd -l" to lock the user's account ("passwd -u" will unlock the account again, btw). Let's use Paul as our example fall guy again:


# passwd -l paul

"passwd -l" can have different effects, depending on what flavor of Unix you're using. On Linux systems, the usual practice is to introduce a "!" character at the front of the user's password hash. This renders the hash invalid so users can't log in, but it's easy to undo the change if you decide you later want to let the user into the system. Some Linux systems go further and set the "account disabled as of ..." field in the /etc/shadow file (it's the second-to-last field for each entry) to a date in the past so that even just resetting the password hash is insufficient to unlock the account.

On older, proprietary Unix systems like Solaris, "passwd -l" usually changes the user's password hash to an invalid string like "*LK*", which unfortunately loses the user's original password hash. However, at least on Solaris systems, the cron daemon will actually refuse to execute jobs for users whose password entry is "*LK*". This means clever users can't set up automated tasks to re-open access to their systems (or launch missiles). When locking accounts on Linux systems, you should also make sure to disable any cron jobs that user may have set up:

# crontab -l -u paul > /root/paul.crontab
# crontab -r -u paul

Here we're making a backup copy of Paul's crontab under /root and then removing all cron jobs. You could later restore Paul's crontab with "crontab -u paul /root/paul.crontab".

If you're worried about the user logging into the workstation remotely after you've turned on the screen locker, then you also need to be careful that the user has no "authorized_keys" files, or even ".[sr]hosts" and hosts.equiv files if you're allowing "HostBasedAuthentication":

# mkdir /root/paul-trustfiles
# mv ~paul/.ssh/authorized_keys ~paul/.[rs]hosts /etc/*hosts.equiv /root/paul-trust

OK, that should be sufficient for keeping that naughty Paul out of the machine. As far as turning on the screen locker, there are a lot of different options on different Unix systems, but let's just stick with the popular (and widely available) "xlock" program. Whatever program you choose to use, the biggest trick to remotely enabling the screen locker is to first acquire the necessary credentials to access the user's X display:

# export DISPLAY=:0.0
# cp ~paul/.Xauthority /root
# su -c paul 'xlock -mode blank -info "This workstation administratively locked"'

On the first line, we set our "DISPLAY" environment variable to match the user's display-- normally ":0.0", you can validate this with the "who" command if you're not sure. On the second line, we grab the user's "magic cookie" file, which allows us access to the X server on the specified "DISPLAY". Finally, we turn on the xlock program with just a blank, black screen. Also, the above example demonstrates that you can also specify an informational message that the user sees when they try to unlock their workstation.

Note that our example starts the xlock program as user "paul", which means the password for the "paul" account-- rendered invalid with the "passwd -l" command earlier-- will be required to unlock the screen. You could actually dispense with the "su paul -c" and start the xlock program as root, thus forcing somebody to enter the root password to unlock the screen. Of course, if Paul happens to know the root password for his workstation, this is not a good idea (you certainly don't want to lock the root account on the system)! However, another possibility would be to actually "su" to some other user account when starting up the screen locker, just to make things more difficult for Paul. But I think you're probably better off using Paul's account, since we know that user has an invalid password. To unlock the screen again, once Paul has been safely escorted out of the building, just kill the xlock process.

Monday, May 11, 2009

Episode #34: Suspicious Password Entries

Hal Says:

Older, proprietary Unix systems tend to include the "logins" command, which is a handy little command for searching your passwd and shadow files for different sorts of information. In particular, "logins -p" (find accounts with null passwords) and "logins -d" (find accounts with duplicate UIDs) are useful when auditing a large user database. Unfortunately, the "logins" command doesn't exist in Linux. But of course you can emulate some of its functionality using other command-line primitives.

Finding accounts with null passwords is just a simple awk expression:

# awk -F: '($2 == "") {print $1}' /etc/shadow

We use "-F:" to tell awk to split on the colon delimiters in the file and then look for entries where the password hash in the second field is empty. When we get a match, we print the username in the first field.

Finding duplicate UIDs is a little more complicated:

# cut -f3 -d: /etc/passwd | sort -n | uniq -c | awk '!/ 1 / {print $2}'

Here we're using "cut" to pull the UIDs out of the third field of /etc/passwd, then passing them into "sort -n" to put them in numeric order. "uniq -c" counts the number of occurrences of each UID, creating one line of output for each UID with the count in the first column. Our awk expression looks for lines where this count is not 1 and prints the UID from each matching line.

Another useful password auditing command is to look for all accounts with UID 0. Normally there should only be a single UID 0 account in your password file ("root"), but sometimes you'll see attackers hiding UID 0 accounts in the middle of the password file. The following awk snippet will display the usernames of all UID 0 accounts:

# awk -F: '($3 == 0) {print $1}' /etc/passwd

More generally, you can use the "sort" command to sort the entire password file numerically by UID:

# sort -t: -k3 -n /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
...

"-t" is used to specify the column delimiter, "-k" specifies which column(s) to sort on, and "-n" means do a numeric (as opposed to alphabetic) sort. The advantage to viewing the password file this way is that all the UID 0 accounts bubble right up to the top of the output, plus it's easier to spot accounts with duplicate UIDs this way.

Ed responds:

OK, sports fans... Hal really threw down the gauntlet here. Brace yourselves, because this is gonna get ugly. You've been warned.

Unlike Linux with its /etc/passwd and /etc/shadow files, Windows doesn't make it easy to access user account password hashes. Thus, it's much harder for us to determine if a password is blank... but we do have some options.

For starters, we could blatantly violate our ground rules here and use third-party tools. One option that pops into mind is fgdump, my favorite tool for dumping Windows hashes. We could run:

C:\> fgdump -c & find "NO PASSWORD" 127.0.0.1.pwdump & del 127.0.0.1.pwdump


This command invokes fgdump, which runs against localhost by default, with the -c option to turn off the dumping of cached credentials, making it give us only the local SAM database. Unfortunately, fgdump doesn't have the option of displaying the hashes on standard output, but instead stores its results in a file called [IPaddr].pwdump. So, we then run the find command to look for output that contains the words "NO PASSWORD" in this file, and then delete the file.

Now, keep in mind that if a given user has a password that is 15 or more characters in length, that user will authenticate using only the NT Hash, and Windows will set the LANMAN to a value of a hash of padding, that old AAD3B4... stuff. In its output for such accounts, fgdump will display "NO PASSWORD" for the LANMAN hash of such accounts, even though they do have an NT hash with an NT password. Thus, to avoid false positives with accounts that have passwords greater than 14 characters, we should tweak our command to:

C:\> fgdump -c & find "NO PASSWORD*********************:NO PASSWORD" 127.0.0.1.pwdump
& del 127.0.0.1.pwdump

Easy.

Yeah, it's easy, if you throw away our treasured rule of using only built-in tools.

But, there's another way, which violates a completely different ground rule we've got around here. Instead of using a third-party tool, we could rely on built-in functionality via a Visual Basic Script. There's a great script from the
awesome Scripting Guys, available here, which attempts to change each user's password from blank to blank. If it is successful, you've got a user with a blank password. Nice and easy!

But, this one also throws another precious ground rule under the bus. That is, we aren't supposed to be using scripts, but instead we rely on single (albeit at times complex) commands.

For a third option, why don't we try to mimic the operation of this VB script at the cmd.exe command line? We could change a user password to blank by running:

C:\> net user [user] ""


This command tells the system to change the password of [user] to blank. If the password policy allows such passwords, it will succeed. Ummm... that's no good for us, because it succeeds regardless of the current user's password. So, this command violates a third coveted rule around here: Commands have to actually work.

Oooookay then. Is there a fourth option? Turns out there is, but it gets pretty ugly. The basis of this command is to rely on "net use" to make an SMB connection locally, thusly:

C:\> net use \\[hostname] "" /u:[user]


If the guest account is disabled, and you otherwise have a default security policy, the system displays the following text if [user] has a blank password:

System error 1327 has occurred.

Logon failure: user account restriction.
Possible reasons are blank passwords not allowed, logon hour restrictions,
or a policy restriction has been enforced.

Note that first possible reason -- blank passwords.

Also, note that this same message comes up if there are policies defined that restrict the account from logging on during certain times of day or other policy restrictions. But, still, in most environments, this is an indication that the password is blank. Not perfect, but good enough for most cases.

Building on this, here ya go, a "single" command that checks to see if local accounts have blank passwords, without using any third-party tools or scripts:


C:\> FOR /F "tokens=2 skip=1" %i in ('wmic useraccount list brief') do @echo.
& echo Checking %i & net use \\[hostname] "" /u:%i 2>&1 | find /i "blank" >nul
&& echo %i MAY HAVE A BLANK PASSWORD & net use * /del /y > nul

Wow! There's a mess, huh? Here's what I'm doing...

I'm setting up a FOR /F loop to iterate on the output of the command 'wmic useraccount list brief', which will show all of the locally defined accounts on the box. I'm parsing the output of that command by skipping the first line (which is column headers) and setting the value of my iterator variable to the second item in each line (the first is the Account Type, the second is the SYSTEM\username).

I'm then echoing a blank line to our output (echo.) to make things prettier followed by displaying a message that I'm checking a given account (echo Checking %i). Then, I try to make an SMB connection to our local hostname (you really should put in your own system's hostname... using \\127.0.0.1 isn't reliable on every version of Windows). The attempted SMB connection has a password of blank ("") and a user name of our current iterator variable (/u:%i).

Now, if the account has a blank password, I'll get an error message that says: "Logon failure: user account restrictions. Possible reasons are blank passwords...". Remember that if you have any of those other restrictions defined on the box, our little one-liner will give you false positives.

Then, I take our error message and dump it into a replicated copy of our standard output (2>&1) so that I can scrape through what was Standard Error with the find command to look for the word "blank". I dump the output of that to nul so we don't see it's ugliness. Then, if the find command is successful (&&), I print out a message saying that %i MAY HAVE A BLANK PASSWORD. Note the weasel word "MAY". That's because there may be other account restrictions applied to the account or system.

Finally, I drop any residual SMB connections we've made (net use * /del /y), dumping its output to nul.

Whew! I tried several other methods for doing this at the command line, but they got even uglier, believe it or not.

Also, note that the above command depends on the Guest account being disabled. If you have that account enabled, it'll show that no accounts have blank passwords, as you'll never get the requisite error message. But, for most production environments, you really should disable that Guest account, you know. You can do that at the command line with:

C:\> net user guest active:no


Be careful, though, to make sure that you don't have any apps that actually rely on the Guest account being enabled.

Now, let's see what else Hal has in store for us in his initial challenge...

Ahhh... userID numbers, better known as SIDs in Windows. Well, Windows assigns those at account creation, attempting to make sure that they are all unique. Therefore, we should never have the same value for two different accounts at the same time... right? Just to make sure, we can dump them using:

C:\> wmic useraccount get sid, name


Unfortunately, the output shows name first followed by sid. That's a crummy aspect of wmic... it shows you attributes in its output alphabetically by attribute name. "Name" comes before "Sid" alphabetically, so we get name, sid even though we asked for sid, name. We can reverse them using a FOR /F loop to parse, and then sort them, using the following command:

C:\> (for /F "tokens=1,2 skip=1" %i in ('"wmic useraccount get sid, name"')
do @echo %j %i) | sort



So, here, I'm running the wmic command inside a FOR /F loop. I've embedded the command in single-quote followed by double-quote at the beginning, and double-quote followed by single-quote at the end. The reason for this is two fold... Normally, we require just the single quotes at the beginning and end to run a command inside the parens of a FOR /F loop. But, if the command has a comma or quote in it, we must either use a ^ before the comma or quote, or put the whole thing inside of single-quote double-quotes as I have here. I used the latter because of the parens around the entire FOR /F loop, which I used so I could pipe it through the sort command. I've found that the ^, or ^" in FOR /F loops have problems when you put parens around the entire FOR /F loop, so I've taken to using the ' " and " ' as they work regardless of the ( ) around the FOR /F loop. It's a little trick I figured out on a flight to Defcon years ago.

So, where was I? Oh yeah... we've now got a list of SIDs and usernames, sorted. Our sort is alphabetic, not numeric, which kinda stinks. Still, you could eyeball the resulting list and see if any of them are identical. Sadly, there is no built-in "uniq" command in Windows. Man, Hal and Paul have it easy, don't they?

If you really want a uniq, you could download a "uniq" command for Windows. Or, you could simulate one. Are you ready for a sick little trick to detect whether a file has all unique lines using built-in tools in Windows?

For this stunt, we'll rely on the built-in Windows fc command, which compares two files (fc stands for "file compare". We can use it as follows:

C:\> (for /F "tokens=1,2 skip=1" %i in ('"wmic useraccount get sid, name"')
do @echo %j %i) | sort > accounts.txt & sort /r accounts.txt > accountsr.txt
& fc accounts.txt accountsr.txt & del accounts.txt & del accountsr.txt


The idea here is to use the sort /r command to create a list of accounts in reverse order, and then compare it to the original list of accounts. If there are no duplicate SIDs, your output will simply show the list of accounts forward, followed by the list of accounts backward. If there are one or more duplicate SIDs, you will see a blank line in the middle of your output as fc tries to show you the differences. Let me illustrate with an example.

Here is the output when we have all unique SIDs:

Comparing files accounts.txt and ACCOUNTSR.TXT
***** accounts.txt
S-1-5-21-2574636452-2948509063-3462863534-1002 SUPPORT_388945a0
S-1-5-21-2574636452-2948509063-3462863534-1003 ASPNET
S-1-5-21-2574636452-2948509063-3462863534-1004 HelpAssistant
S-1-5-21-2574636452-2948509063-3462863534-1005 skodo
S-1-5-21-2574636452-2948509063-3462863534-1006 nonadmin
S-1-5-21-2574636452-2948509063-3462863534-1064 __vmware_user__
S-1-5-21-2574636452-2948509063-3462863534-1072 frank
S-1-5-21-2574636452-2948509063-3462863534-1073 dog
S-1-5-21-2574636452-2948509063-3462863534-1074 fred
S-1-5-21-2574636452-2948509063-3462863534-500 Administrator
S-1-5-21-2574636452-2948509063-3462863534-501 Guest
***** ACCOUNTSR.TXT
S-1-5-21-2574636452-2948509063-3462863534-501 Guest
S-1-5-21-2574636452-2948509063-3462863534-500 Administrator
S-1-5-21-2574636452-2948509063-3462863534-1074 fred
S-1-5-21-2574636452-2948509063-3462863534-1073 dog
S-1-5-21-2574636452-2948509063-3462863534-1072 frank
S-1-5-21-2574636452-2948509063-3462863534-1064 __vmware_user__
S-1-5-21-2574636452-2948509063-3462863534-1006 nonadmin
S-1-5-21-2574636452-2948509063-3462863534-1005 skodo
S-1-5-21-2574636452-2948509063-3462863534-1004 HelpAssistant
S-1-5-21-2574636452-2948509063-3462863534-1003 ASPNET
S-1-5-21-2574636452-2948509063-3462863534-1002 SUPPORT_388945a0
*****


And, here is the output when we have a dupe:


Comparing files accounts.txt and ACCOUNTSR.TXT
***** accounts.txt
S-1-5-21-2574636452-2948509063-3462863534-1002 SUPPORT_388945a0
S-1-5-21-2574636452-2948509063-3462863534-1003 ASPNET
S-1-5-21-2574636452-2948509063-3462863534-1004 HelpAssistant
S-1-5-21-2574636452-2948509063-3462863534-1005 skodo
S-1-5-21-2574636452-2948509063-3462863534-1006 nonadmin
S-1-5-21-2574636452-2948509063-3462863534-1064 __vmware_user__
S-1-5-21-2574636452-2948509063-3462863534-1072 frank
***** ACCOUNTSR.TXT
S-1-5-21-2574636452-2948509063-3462863534-501 Guest
S-1-5-21-2574636452-2948509063-3462863534-500 Administrator
S-1-5-21-2574636452-2948509063-3462863534-1074 fred
S-1-5-21-2574636452-2948509063-3462863534-1072 frank
*****

***** accounts.txt
S-1-5-21-2574636452-2948509063-3462863534-1072 dog
S-1-5-21-2574636452-2948509063-3462863534-1074 fred
S-1-5-21-2574636452-2948509063-3462863534-500 Administrator
S-1-5-21-2574636452-2948509063-3462863534-501 Guest
***** ACCOUNTSR.TXT
S-1-5-21-2574636452-2948509063-3462863534-1072 dog
S-1-5-21-2574636452-2948509063-3462863534-1064 __vmware_user__
S-1-5-21-2574636452-2948509063-3462863534-1006 nonadmin
S-1-5-21-2574636452-2948509063-3462863534-1005 skodo
S-1-5-21-2574636452-2948509063-3462863534-1004 HelpAssistant
S-1-5-21-2574636452-2948509063-3462863534-1003 ASPNET
S-1-5-21-2574636452-2948509063-3462863534-1002 SUPPORT_388945a0


See, the frank and dog account have the same SID, as indicated on either side of that blank line in the middle there. If there are any dupe SIDs, you'll see that tell-tale blank line in the middle of the output. Sure, it's a kluge, but it is a quick and dirty way of determining whether there is a duplicate in a stream of information.

And, we end on a much simpler note. Hal wants to find accounts with superuser privileges (UID 0 on Linux). We can simply look for accounts in the administrators group using:

C:\> net localgroup administrators


So, there you have it. I warned you that it would get ugly, and I am to deliver on my promises. In the end, we were able to achieve nearly all of what Hal did in Linux, making certain assumptions.

Friday, May 8, 2009

Episode #33: Recognizing Sub-Directories

Hal takes requests:

Loyal reader Lloyd Alvarez writes in with a little problem. He's writing Javascript that needs to get a directory listing and be able to easily discriminate sub-directories from other objects in the directory. The trick is that he needs code for both Unix/Linux and Windows. Well where else would you come for that kind of service than Command Line Kung Fu blog?

Turns out that Lloyd had doped out a Unix solution on his own:

$ ls -l | gawk '{printf("%s,",$1);for(i=9;i<=NF;i++) printf("%s ",$i);printf(":")}'
total,:drwxr-xr-x,dir1 :drwxr-xr-x,dir2 :-rw-r--r--,file1 :-rw-r--r--,file2 ...

Yow! That's some pretty ugly awk and even uglier output, but Lloyd was able to parse the resulting stream in his Javascript and easily pick out the directories.

But let me make that even easier for you, Lloyd old buddy:

# ls -F
dir1/ dir2/ file1 file2 otherdir1/ otherdir2/ otherfile1 otherfile2

Yep, the "-F" option causes ls to append a special character to each object in the directory to let you know what that object is. As you can see, directories have a "/" appended-- should be easy to pick that out of the output! Other suffix characters you might see include "@" for symbolic links, "*" for executables, and so on. Regular files get no special suffix (see the above output).

Maybe Lloyd would prefer to just get a list of the sub-directories without any other directory entries:

$ find * -maxdepth 0 -type d
dir1
dir2
otherdir1
otherdir2

Or if you're not into the find command:

$ for i in *; do [ -d $i ] && echo $i; done
dir1
dir2
otherdir1
otherdir2

Too many choices, but then that's Unix for you! I'll step out of the way now and let Ed shock Lloyd with the Windows madness.

Ed jumps in:

Good stuff, Lloyd! Thanks for writing in.

In Windows, the easiest way to do this is to use the dir command, with the /d option. That option is supposed to simply list things in columns, but it adds a nice little touch -- directory names now have square brackets [ ] on either side of them. Check it out:

C:\> dir /d
[.] [dir1] file1 [otherdir1] otherfile1
[..] [dir2] file2 [otherdir2] otherfile2

So, just find and parse out those brackets, Lloyd, and you should be good to go. Oh, and remove the . and .. if you don't want then. Unfortunately, we cannot eliminate them with a /b (for bare), because that removes the brackets.

For Hal's additional fu for looking for just directories, we can rely on the fact that Windows considers "directoriness" (is that a word?) as an attribute. So, we can list only the directories using:

C:\> dir /ad /b
dir1
dir2
otherdir1
otherdir2

Or, if you want only files (i.e., NOT directories):
C:\> dir /a-d /b
file1
file2
otherfile1
otherfile2


You could do this kinda stuff with FOR /D loops as well, which give you even more flexibility. For example, if you just want directory names with a slash after them, to give you similar output to Hal's "ls -F", you could run:

C:\> FOR /D %i in (*) do @echo %i/
dir1/
dir2/
otherdir1/
otherdir2/

Or, if you really like the colons that your current scripts parse, you could do:

C:\> FOR /D %i in (*) do @echo :%i

By altering that echo statement, you can roll the output however you'd like.

Fun, fun, fun!

Wednesday, May 6, 2009

Episode #32: Wiping Securely

Ed gets his groove on:

The Encrypted File System on Windows stinks. It's something of an embarrassment for me that it is called EFS, because those are my initials too. My biggest beef with EFS is that it leaves cleartext copies of files around in unallocated space if you simply drag and drop a file into an EFS-protected directory. No, seriously... isn't that awful? Doh!

But, included with all the stinkatude of EFS is one gem: the cipher command, when used with the /w: option. I don't use EFS to encrypt, but often rely on cipher to wipe. I used it just last week to help a buddy who was trying to recondition his computer so he could give it to his son for school. He had some work files he needed to clear out, and was planning on simply deleting them through the recycle bin. Ouch! That won't work very well, as those files would still be recoverable. I broke into a mini-lesson about file systems and secure deletion options.

For wiping files, there are many good options available for download, such as sdelete from Microsoft SysInternals or DBAN to completely blow away a file system. But, what if you are stranded on a desert island and need to securely delete something using only built-in tools? Windows 2000 and better (not XP Home... Microsoft purposely cripples that version) have the cipher command, which includes the /w: option to wipe the unallocated space on a volume that you specify like this:

C:\> cipher /w:c:\folder

This command will cause Windows to overwrite all the unallocated space on the volume with c:\folder three times. First, it overwrites with zeros, then with ones, and then random numbers. Unfortunately, there's no option to specify overwriting any number of times other than three. Well, unless you want to... uh... do the obvious:

C:\> for /L %i in (1,1,9) do @cipher /w:c:\folder

This command will overwrite your unallocated space 27 times. Oh, and it'll take a long time on any reasonably sized partition with a lot of open space.

Whenever using cipher to wipe files, there are some hugely important notes to keep in mind.

First off, you have to include the colon between the /w and the folder name. Do people at Microsoft stay up late at night thinking of ways to make their syntax more horrible and inconsistent than ever, or does it just happen?

Second, and this one is huge.... note that cipher won't actually delete any current files in c:\folder or that folder itself! A lot of people think cipher will securely delete the folder (c:\folder) you specify, and that's not right. It securely deletes all unallocated (already deleted) files and folders on the entire partition that c:\folder inhabits. That's a much more thorough (and likely time consuming) process, but realize that it will leave behind c:\folder and its contents. If you want to get rid of them, delete them, and then do a cipher /w:c:\ to wipe the whole partition.

Now, there are major debates as to whether overwriting three times is enough for a good wipe. I've read the debates, and am comfortable that, for modern media, three times overwriting is good enough for most uses. If I need stronger destruction of data, it's best to simply take a hammer to the hard drive.

Hal wipes out:

Most Linux distros these days ship with the "shred" command, which overwrites files and then optionally deletes them:

# shred -n 3 -z -u myfile

Here "-n 3" specifies three overwrite passes, "-z" means to do a final overwrite with zeroes (nulls) to make it less obvious you've been shredding your files, and "-u" means to remove the file once the overwrites are performed.

But you should be aware that using "shred" on an individual file like we do in the above example may still leave traces of the file on disk. That's because most Linux systems these days use the ext3 file system, which has a file system transaction journal. Even after using "shred" on the file, the contents of the file may be recoverable from the journal using a tool like "ext3grep".

So the most secure option is to "shred" the entire disk (which overwrites the journal as well):

# shred -n 3 -z /dev/sdb

In these cases, you don't want to remove the disk device file itself once you're done with the overwriting so we leave off the "-u" option. This is also why "-u" is a separate option that must be explicitly set-- overwriting entire disks is the more common use case.

What if you're on a non-Linux system and don't have "shred" installed? Well, you could certainly download the "shred" source code (or "srm", another popular file deletion tool). But don't forget that you also have "dd", which I often use to wipe disks:

# dd if=/dev/urandom of=/dev/sdb bs=4096
# dd if=/dev/zero of=/dev/sdb bs=4096

The first command overwrites /dev/sdb with pseudo-random data-- use /dev/urandom instead of /dev/random for this because /dev/random can block waiting for additional entropy. The second overwrites your disk with zeroes. Run the commands multiple times depending on the number of overwrites you're most comfortable with.

Loyal reader Jeff McJunkin also points out that you can use "dd" to wipe the unallocated space in a partition, just like Ed is doing with "cipher":

# dd if=/dev/urandom of=junk bs=4096; rm junk

This will consume all remaining disk space in the partition with a file called junk-- the "dd" command will stop when the partition fills-- and then removes it immediately. Be sure to do this command as root, because the last 5% of the space in the file system is normally reserved for root-owned processes and not accessible to normal users.

Monday, May 4, 2009

Episode #31: Remote Command Execution

Ed starts out:

One of the most frequent questions I get regarding the Windows command line involves how to run commands on a remote Windows machine and get access to the standard output of the command. Sure, Microsoft SysInternals psexec rocks, but it's not built in. On Linux and Unix, ssh offers some great possibilities here, but neither ssh nor sshd are built-in to Windows (and what's with that? I mean... we need that. Call Microsoft right now and demand that they build in an ssh and sshd into Windows. Installing a third-party version is certainly doable, but we need it built in... starting about 5 years ago, thank you very much.)

Anyway, while there are many options for running a command on a remote Windows machine using built in tools (such as using at, schtasks, or sc), one of my faves is good old WMIC:

C:\> wmic /node:[targetIPaddr] /user:[admin] process call create "cmd.exe /c [command]"


That'll run [command] on the target, after prompting you for the given admin's password.

You won't see the standard output, though.

To get that, change it to:


C:\> wmic /node:[targetIPaddr] /user:[admin] process call create "cmd.exe /c [command] >> 
\\[YourIPaddr]\[YourShare]\results.txt"


Make sure you have [YourShare] open on your box so the target machine and [admin] user can write to your share. The results.txt file will have your standard output of the command once it is finished.

Oh, and to execute a command en mass on a bunch of targets, you could use /node:@[filename.txt], in which the filename has one line per machine name or IP address on which you want to run the given command.

Not nearly as elegant as what I'm sure my sparring partners will come up with for Linux, but it is workable.

Hal Replies:

Thanks for throwing us a bone here, Ed. With SSH built into every modern Unix-like operating system, remote commands are straightforward:

$ ssh remotehost df -h

Sometimes, however, you need to SSH as a different user-- maybe you're root on the local machine, but the remote system doesn't allow you to SSH directly as root, so you have to use your normal user account. There's always the "-l" option:

$ ssh -l pomeranz remotehost df -h

But what if you want to scp files as an alternate user? The scp command doesn't have a command line option like "-l" to specify an alternate user.

One little-known trick is that both ssh and scp support the old "user@host" syntax that's been around since the rlogin days. So these commands are equivalent:

$ ssh -l pomeranz remotehost df -h
$ ssh pomeranz@remotehost df -h

Personally, I never use "-l"-- I find "user@host" more natural to type and it works consistently across a large number of SSH-based utilities, including rsync.

Unlike wmic, SSH does not have built-in support for running the same command on several targets. The "Unix design religion" is that you're supposed to do this with other shell primatives:

$ for h in $(< targets); do echo ===== $h; ssh $h df -h; done

By the way, note the "$(< targets)" syntax in the above loop, which is just a convenient alternate form of "`cat targets`".

Unfortunately, the above loop is kind of slow if you have a lot of targets, because the commands are run in serial fashion. You could add some shell fu to background each ssh command so that they run in parallel:

$ for h in $(< targets); do (echo ===== $h; ssh $h df -h) & done

Unfortunately, this causes the output to be all garbled because different commands return at different speeds.

Frankly, you're better off using any of the many available Open Source utilities for parallelizing SSH commands. Some examples include sshmux, clusterssh, and fanout (which was written by our friend and fellow SANS Instructor, Bill Stearns). Please bear in mind, however, that while remote SSH commands allow you to easily shoot yourself in the foot, these parallelized SSH tools allow you to simultaneously shoot yourself in both feet, both hands, the head, and every major internal organ all at the same time. Take care when doing these sorts of things as root.

Friday, May 1, 2009

Episode #30: Twiddling with the Firewall

Ed kicks it off:

One of the real gems of the Windows command line is netsh. I use it all the time. In Episode #2, about a thousand years ago (gosh... can it be only 2 months?), we talked about using netsh (yeah, and iptables) to display firewall config information. But, you know, there are some commands I run even more often that alter the firewall.

In particular, if I'm in my lab doing an analysis that requires me to shut off the built-in Windows firewall, or when I'm conducting a pen test and want to hack naked without a firewall, I drop the firewall with:

C:\> netsh firewall set opmode disable


That's soooo much easier than digging through the blasted GUI to find where Microsoft buried the firewall configuration in the given version of Windows.

Simple command, but I use it all the time.

To turn it back on, you'd run:

C:\> netsh firewall set opmode enable


If you want to poke holes through the firewall based on port, you could run:

C:\> netsh firewall add portopening protocol = [TCP|UDP] port = [portnum] name = [NameOfRule]
mode = enable scope = custom addresses = [allowedIPaddr]


This syntax would configure the firewall to allow traffic in for the port you specify only if it comes from allowedIPaddr, to make your rule a little safer.

And, to remove the rule, just change "add" to "del", and only type in the command up to the port number.

Finally, as we mentioned in Episode #2, to show the overall config of the firewall, you can run:

C:\> netsh firewall show config


Hal chimes in:

Firewall software is another one of those areas where there are a number of competing options on different Unix/Linux platforms. For example you've got ipfilter on Solaris and {Free,Net}BSD, pf on OpenBSD, and IP Tables on Linux. I'm going to stick with IP Tables for purposes of this discussion, since it's probably what most of you all deal with the most.

Unfortunately, IP Tables is rather cranky to work with from the command line if you stick with the basic "iptables" command. The developers tried to smash all of the possible functionality configuration options into a single command, and IP Tables is capable of some complicated stuff. The result, however, is that there's a pretty steep learning curve for doing even basic operations. This is why simplified firewall configuration GUIs are provided by the major Linux distributions.

But let's try and cover some of the same command-line territory that Ed does in his Windows examples. First, as far as starting and stopping the firewall goes, your best bet is to just run "/etc/init.d/iptables [start|stop]" as root. The init script hides a lot of complexity around loading and unloading kernel modules, firewall rules, and default packet handling policies. For example, here are the manual command equivalents for "/etc/init.d/iptables stop":


# iptables -P INPUT ACCEPT
# iptables -P OUTPUT ACCEPT
# iptables -P FORWARD ACCEPT
# iptables -F

Yep, four commands-- and that's without even showing you the commands that some init scripts use to unload the IP Tables kernel modules. The first three commands above set the default permit ("ACCEPT") policy ("-P") for inbound ("INPUT") and outbound ("OUTPUT") packets, as well as for packets being passed through the system ("FORWARD"). It's possible that your particular firewall configuration doesn't change the default policies to block packets ("DROP"), but it's best to be sure. The final "iptables -F" command flushes all filtering rules, which means all packets will now simply be handled by the default "ACCEPT" policies.

The simplest possible example of adding a rule to allowing traffic into your system on a particular port would be something like:

# iptables -A INPUT -p tcp --dport 80 -j ACCEPT

This allows ("-j ACCEPT") inbound traffic ("-A INPUT") on 80/tcp ("-p tcp --dport 80"). Deleting the rule is as simple as running the same command but with a "-D INPUT" option (delete from the "INPUT" chain) instead of "-A INPUT" (add to the "INPUT" chain).

However, depending on how your particular Linux vendor sets up their firewall, adding rules directly to the "INPUT" chain may not be the right thing to do. Many vendors set up their own rule chains that pre-empt the default chains. Also, you may have to add rules to the "OUTPUT" chain (or vendor-specific equivalent) to allow the return packets to escape your host, unless you have a "default permit" configuration in the outgoing direction. For these reasons, it's best to inspect your current rule sets ("iptables -L -v") before making changes.

I should mention that a simplified command-line interface is now becoming available in some Linux distributions, notably Ubuntu. If all you're interested in is a host-based firewall, the "ufw" command makes configuration and maintenance much easier. Here are some sample "ufw" commands:

# ufw enable                # enable filtering
# ufw disable # turn off firewall
# ufw status # show current rules
# ufw allow 80/tcp # allow 80/tcp to any IP on this host
# ufw delete allow 80/tcp # delete above rule

You can also do more complex rules like:

# ufw allow proto tcp from 1.2.3.4 to any port 25

If you're more used to Cisco extended ACL syntax, or use ipfilter on other Unix systems, the "ufw" command-line syntax will be pretty natural for you.