As a system administrator or security practitioner, it is useful to have a tool that allows you to verify the current configuration of a system, such as files that exist, registry values, or user accounts. In addition to verifying a configuration, this technique can be used as a lightweight host intrusion-detection system by recording a baseline configuration and then monitoring for variations from that baseline. You can also use it to look for specific indicators of compromise.
In this chapter, we develop a tool to read in a text file that consists of a series of configurations to validate, such as the existence of a file or user, and verify that the condition exists on the system. This tool is targeted at the Windows operating system but could easily be modified to support Linux.
The validateconfig.sh tool validates the following:
The existence or nonexistence of a file
The SHA-1 hash of a file
A Windows Registry value
The existence or nonexistence of a user or group
Table 21-1 shows the syntax for the configuration file the script will read.
Purpose | Format |
---|---|
Existence of a file |
|
Nonexistence of a file |
|
File hash |
|
Registry key value |
|
Existence of a user |
|
Nonexistence of a user |
|
Existence of a group |
|
Nonexistence of a group |
|
Example 21-1 shows a sample configuration file.
user jsmith file "c:windowssystem32calc.exe" !file "c:windowssystem32ad.exe"
The script in Example 21-2 reads in a previously created configuration file and confirms that the configuration exists on the system.
#!/bin/bash -
#
# Cybersecurity Ops with bash
# validateconfig.sh
#
# Description:
# Validate a specified configuration exists
#
# Usage:
# validateconfig.sh < configfile
#
# configuration specification looks like:
# [[!]file|hash|reg|[!]user|[!]group] [args]
# examples:
# file /usr/local/bin/sfx - file exists
# hash 12384970347 /usr/local/bin/sfx - file has this hash
# !user bono - no user "bono" allowed
# group students - must have a students group
#
# errexit - show correct usage and exit
function
errexit
(
)
{
echo
"
invalid syntax at line
$ln
"
echo
"usage: [!]file|hash|reg|[!]user|[!]group [args]"
exit
2
}
# errexit
# vfile - vaildate the [non]existance of filename
# args: 1: the "not" flag - value:1/0
# 2: filename
#
function
vfile
(
)
{
local
isThere
=
0
[
[
-e
$2
]
]
&&
isThere
=
1
(
(
$1
)
)
&&
let
isThere
=
1-
$isThere
return
$isThere
}
# vfile
# verify the user id
function
vuser
(
)
{
local
isUser
$UCMD
$2
&
>/dev/null
isUser
=
$?
if
(
(
$1
)
)
then
let
isUser
=
1-
$isUser
fi
return
$isUser
}
# vuser
# verify the group id
function
vgroup
(
)
{
local
isGroup
id
$2
&
>/dev/null
isGroup
=
$?
if
(
(
$1
)
)
then
let
isGroup
=
1-
$isGroup
fi
return
$isGroup
}
# vgroup
# verify the hash on the file
function
vhash
(
)
{
local
res
=
0
local
X
=
$(
sha1sum
$2
)
if
[
[
${
X
%% *
}
=
=
$1
]
]
then
res
=
1
fi
return
$res
}
# vhash
# a windows system registry check
function
vreg
(
)
{
local
res
=
0
local
keypath
=
$1
local
value
=
$2
local
expected
=
$3
local
REGVAL
=
$(
query
$keypath
//v
$value
)
if
[
[
$REGVAL
=
=
$expected
]
]
then
res
=
1
fi
return
$res
}
# vreg
#
# main
#
# do this once, for use in verifying user ids
UCMD
=
"net user"
type
-t
net
&
>/dev/null
||
UCMD
=
"id"
ln
=
0
while
read
cmd
args
do
let
ln++
donot
=
0
if
[
[
${
cmd
:
0
:
1
}
=
=
'!'
]
]
then
donot
=
1
basecmd
=
${
cmd
#
!
}
fi
case
"
$basecmd
"
in
file
)
OK
=
1
vfile
$donot
"
$args
"
res
=
$?
;
;
hash
)
OK
=
1
# split args into 1st word , remainder
vhash
"
${
args
%% *
}
"
"
${
args
#*
}
"
res
=
$?
;
;
reg
)
# Windows Only!
OK
=
1
vreg
$args
res
=
$?
;
;
user
)
OK
=
0
vuser
$args
res
=
$?
;
;
group
)
OK
=
0
vgroup
$args
res
=
$?
;
;
*
)
errexit
;
;
esac
if
(
(
res
!
=
OK
)
)
then
echo
"
FAIL: [
$ln
]
$cmd
$args
"
fi
done
The errexit
function is a handy helper function to have, to give the user some helpful information on the correct use of the script—and then exiting with an error value. The syntax used in the usage
message is typical *nix syntax: items separated by a vertical bar are choices; items inside square brackets are optional.
This uses the if-less f
statement to check on the file’s existence.
This is a simple way to toggle a 1 to a 0, or a 0 to a 1, conditional on the first argument being nonzero.
This uses the more readable, but bulkier, if
statement to do the toggle.
Running the sha1sum
command, the output will be saved in the X
variable. The output consists of two “words”: the hash value and the filename.
To check whether the hash values match, we need to remove the filename, the second word, from the output of the sha1sum
command. The %%
indicates the longest match possible, and the pattern specifies starting with a blank and then any characters (*
).
The type
command will tell us whether the net
command exists; if it fails to find it, then we’ll use the id
command instead.
Reminder: This takes a substring of cmd
beginning at position 0 and taking only one character; i.e., it’s the first character of cmd
. Is it an exclamation mark (aka bang)? That is often used in programming to mean “not.”
We need to take off the bang from the command name.
As the comment says, it splits the args in two—taking the first word and then the remainder, as it calls our vhash
function.
The case
statement in bash allows for pattern matching in the separate cases. A common pattern is the asterisk to match any string, placed as the last case, to act as a default. If no other pattern was matched, this one will match and will be executed. Since the input didn’t match any supported choice, it must be bad input, so we call errexit
to fail out.
The validateconfig.sh tool enables you to verify that a specific configuration exists on a system. This is useful for compliance checks and can also be used to identify the existence of malware or an intrusion by looking for specific indicators of compromise.
YARA is a great source for host-based indicators of compromise. To learn more, visit the YARA website.
In the next chapter, we look at auditing user accounts and credentials to determine whether they have been involved in a known compromise.
Try expanding and customizing the features of validateconfig.sh by adding the following functionality:
Check whether a specific file permission exists.
Check whether a particular network port is open or closed.
Check whether a particular process is running.
Support comments in the input stream. If the first character of a line read is a hashtag, discard the line (i.e., nothing to process).
Visit the Cybersecurity Ops website for additional resources and the answers to these questions.