Runaway Check Tutorial
TheCAT
11/26/2014
Objective: automate the process of examining all running processes on a list of linux/unix based servers and obtaining a list of processes that fall outside guidelines. These guidelines can be found on internal documentation like the DCM, but basically indicate there are cutoffs for CPU use, memory use and so on. The results should indicate which user and what process and machine are involved. At that point further consideration can be given on a case by case basis.
The goal of this lecture is to give you some legos to build things with. The legos you get here today will be most of the legos you need to build a runaway script with, but these legos can be used for other things. Cause who doesn't like legos; except unseen legos in the middle of the night with barefeet.
Minimum Command Requirements:
ssh
ps
Optional Command Requirements:
netgrouplist
ssh-keygen
ssh-agent
ssh-add
uptime
grep
echo
awk
sed
cut
>>
>
Possible Syntax:
for do done
if then else
` `
$
Tools:
vim/emacs/nano
Likely the end results of this script will be more complicated than the previous lesson's results. Again, when considering functionality examine the guidelines for runaway scripts in our in-house documentation.
Overview:
One way to tackle this is to use an ssh for-loop. Basically we set up a for-loop with every computer involved and ssh to that machine get the desired output, parse it to our satisfaction, and dump the results into a RESULTS.txt. From there as previously indicated we would examine things on a case by case basis.
Recall we will need some starting syntax at the top of our file:
#! /bin/bash
and we will ultimately need to chmod our script to be executable:
me@machine$ chmod 700 my_script.bash
That being said let's talk about keys.
Keys:
If you haven't got an ssh-key you will need one. Currently there are over 50 machines that need to be examined and you can't be entering in your password every time you run the script ... for each machine.
So, ssh to your favorite machine, I recommend a cs compute powerhouse not an irc box or lab box. Once there, generate an ssh-key if you don't have one.
yourname@machine$ ssh-keygen -t rsa -b 4096
and when prompted for a passphrase, give it a decent one that you can remember. Accept the default file name given.
Now let's get your key onto machines where it has to go. The thing is though your key has a passphrase associated with it, so every time you use your key you now have to answer the passphrase - so how is that different than just entering the password to the machine?
We can use an agent to bypass that. The agent will store our passphrase for us so we don't have to enter it over and over.
yourname@machine$ ssh-agent bash
yourname@machine$ ssh-add -t 3600
Enter passphrase for /u/yourname/.ssh/id_rsa:
yourname@machine$
First we started an agent with a new bash shell. Go ahead and up-arrow. All your previous commands are gone. If you type exit (don't do this now) it will appear that nothing happened, but actually it closes the agent shell. Then we allowed the agent to have access to our key for 3,600 seconds. The clock is ticking.
So now let's transfer our public key to the machines we want to get to. We are going to do this in a for-loop on the command line to practice our syntax.
yourname@machine$ for machine in aaa bbb ccc; do ssh-copy-id yourname@$machine; done
We will still have to enter our password to ssh to those machines, but only one last time. Why did I pick those three machines? Hint: NFS mounted directories.
Since our directories are NFS mounted, did we actually have to use ssh-copy-id at all?
Now we can get around without passwords as long as we have an agent running. You need to remember the agent when running/ testing your script.
But what machines to use? This is in the DCM but we'll go over it a bit more here.
From your favorite machine:
yourname@machine$ netgrouplist -l
This gives you a listing of all our machines grouped by various categories. We are concerned with the following three: linux-login-sys, ece-secure-sys and cs-secure-sys.
yourname@machine$ netgrouplist cs-secure-sys
Because our inventory and the names of machines changes constantly, we want to make certain we use whatever is most current. As such we are either going to (A) dynamically import this list of machines from these three lists straight into our script or (B) put them all in one list and use the contents of that list in our
script. Remember the name of each machine is part of a for-loop just like it was in our command line example.
Here is how you could get a list into the script, you use backticks:
#! /bin/bash
for list in `netgrouplist -l`; do
echo $list
done
Go ahead make this script, chmod it accordingly and run it. How would you change it to get a list of machines instead of a list of lists? How would you change it to get three lists of machines?
What if you want to put the list results in a file? There are many commands that put their results at stdout (the screen). But what if you want those results in a file?
Change the above script as follows:
#! /bin/bash
for machine in `netgrouplist cs-secure-sys`; do
echo $machine >> RESULTS.out
done
Now run it
yourname@machine$ ./your_script.bash
yourname@machine$ ls
RESULTS.out
We could have gotten the same thing as follows:
yourname@machine$ netgrouplist cs-secure-sys >> MYRESULTS.out
The >> is called a redirection operator and it redirects stdout to a file of your choosing, specifically it appends data to that file - it doesn't over write it. If no file by that name exists, it is created.
The > operator is very similar EXCEPT that it overwrites data. Any old data in the file will be lost.
Now that we can get to machines let's get that data about those processes.
yourname@machine$ ps
yourname@machine$ ps -ef
yourname@machine$ man ps
As you can see ps is a very powerful tool. Once you have a chance to read the man pagea bit, take a look at the examples portion. You should be able to get some ideas about how to construct your ps.
I am specifically NOT going to tell you what the flags are for ps that you will want. You should read the man page and get some ideas, then experiment with the results locally, and incorporate that into your script.
Look at the requirements document (DCM) and see what matches in the man page. Once you have built your script and come up with ideas that work (on your own) I will be happy to answer any questions about how to make it better.
For now we will practice the fundamentals with ps -ef
But ps gives us some out put and we want to be able to separate that output based on results. One thing we can do to eliminate some out put is with grep. Grep stands for Grab REgular exPressions. No, I am lying, but it should.
So we can use grep to exclude things - like maybe anything that is a daemon or root (if we want), with -v ie
yourname@machine$ ps -ef | grep -v root > test.out
In reality, if we are looking for runaway processes, do we really want to reject a process because it is being run by root? What if that process owned by root is busted or worse - shouldn't be running as a root process. I would want to know about a process consuming 100% cpu even if it is run by root.
Now we know how to get some results to play with, let's do that.
#! /bin/bash
for machine in `netgrouplist cs-secure-sys`; do
ssh $machine ps -ef >> temp.out
done
awk '($1 !~/USER/) && ($2 > 32000)' temp.out >> RESULTS.out
rm temp.out
What did we get?
yourname@machine$ cat RESULTS.out
You now have most of the tools you need to construct a runaway script. You will need to spend more time with ps and awk (or sed) to actually get the info you need and parse it. Consider adding uptime as well.
Wednesday, November 26, 2014
Wednesday, November 19, 2014
Bash Scripting: Printer Test Script
Print Script
Tutorial Lecture
TheCAT
11/19/2014
TheCAT
11/19/2014
Objective: Send a
printer test page to a group of networked printers. Ideally, the test
page has a date and user name. Additionally it should be in color and
span the depth and breadth of the page. This script can then be used
in conjunction with lab checks to ensure that the printers in the
labs are in good working order and so that an individual that
encounters the page can understand its purpose.
Minimum Command
Requirements:
man
lp or lpr
chmod
Optional Commands:
date
cat
convert
lpoptions
& many more
possible
Possible Syntax:
for, do, done
if then else fi
case esac
exit
shift
Tools:
nano/vim/emacs
a .jpg or two
Side note: A print script
can range from 5 lines to 150 or more. You can use any language and
there are no hard and fast rules beyond the objectives – which are
more of a guideline.
First let's look at
what lp and lpr do.
user@machine$ man lp
user@machine$ man
lpr
While I personally
think that lp is a little more versatile offering a lot of options I
actually used lpr for my script – no real rhyme or reason there so
consider either one a viable alternative.
So you can see that
you are ultimately going to have to provide some data to the command
like the name of the printer and the file to print.
But what to do when
there is more than one printer?
Well, we could just
run the command multiple times, each time updating the name of the
printer to reflect the next printer on our list of printers. In fact
that is exactly what we are going to do, but we aren't going to do it
by hand, we are going to put it in a script.
One method for doing
something a lot of times is to put it in a for-loop. In this case each
iteration of the loop corresponds to printing the same file to a new
printer.
You now have the
foundation for the entire script; the rest is just refining the bits
of what we discussed so far. And for that we will work
individually... more on this at the very end. Other questions to ask
yourself
* What are the
printers?
- do these live in
the script?
- do they live in a
separate file?
* What file to use?
- do I create my
own image?
- Text or picture?
* How to get the
date on it?
* How to get a name
on it?
* Do I test all the
printers at once?
But before we start
coding (and after we finish solving the problem) let's start with
something we will need at the top of any script: the magic cookie. No
not that chocolate yummy thing. This is a line at the top of our file
that reads as follows:
#! /bin/bash
This tells kernel to
run the script in a bash shell. So when you run a command from the
command line, there is a fork command that creates and entirely new
shell which is an exact copy of the shell currently running. We want
to make certain that when the script is run, that the new shell is
also a bash shell. This falls under the execve command that follows
(but we now we are way out of scope)...
Lastly let's discuss
chmod. Change file mode allows us to set permissions for a file. On
our systems when you create a new file the permissions default to:
-rw------- meaning only we can read and write to it, but cannot
execute. For a text file this isn't terrible, but since we are
building a script we will want to make it executable. Additionally
there is the consideration of whether we want other people to run our
script. Do you want to let other cats run it? Or anyone at all? For
now let's assume that we want to make it so that only we can run it.
When we are done, we will want to:
chmod 700 my_script
Now let's get to work and start thinking about how to set up what we need based on the previous bullet point questions....
For Loop Syntax
for item in apple banana cherry; do
echo $item
done
for number in {1..9}; do
echo $number
done
for f in `ls`; do
For Loop Syntax
for item in apple banana cherry; do
echo $item
done
for number in {1..9}; do
echo $number
done
for f in `ls`; do
echo $f
done
Monday, November 10, 2014
Microcorruption - Montevideo
Again, they have the same strange add
0x10 to the stack pointer at the very end of the execution of login.
This allows us, as before, to overflow the buffer and inject a return
address that we want. If this were a software version the address we
could put in there would be “unlock door” but since this is a
hardware version we have to again get 0x7F into the SR and call 0x10
<_trap_interrupt> to open the lock. Pretty much the same as
before, so let's try the same code.
Of course it doesn't work, and as we
can see they have added strcpy into the mix which effectively copies
all information up to and including the null terminator – but it
stops there. So we only get one occurrence of 00 and it has to be at
the end.
But other than that our methodology
should be intact, we just have to adjust the code. We can still have
the filler up front, then the re-deirect to our code as before, but
we have to be a little more creative to work around the 00 entries in
our password.
Ideas:
So we have some filler, the address to
our shell code, another bit of filler and then our code:
3f40
fffe mv #xfffe, r15 ← load fffe into register 15
1f53 increment
r15 ← increment to ff00
024f mv
r15, sr ← move to status register
b012
1000 call 0x10 ← has to be the last thing
So
putting it together we have the following parts:
41414242434344444545464647474848
filler
0244 address to our
4141 filler
3f40fffe1f53024fb0121000 injected
shell code
41414242434344444545464647474848024441413f40fffe1f53024fb0121000
Wow, that's a really long password.
Perhaps we can think of something shorter?
So rather than inject our shell code
after the password, why not make our shell code the password itself?
At the end we can redirect the program counter (instruction pointer)
to the start of our shell code. Of course we will need to watch out
for null terminators which means we can't call 0x10 from inside, but
the provided code already does for us at address 455c. All we have
to do is jump there.
So, instead of starting our code with
4141 (AA) let's start with the injected code and as the filler. We
also change the jump to address (to an address within the code that
does it for itself). The last portion is the address where the
beginning of the password is stored. Here's an entry that works and
is much shorter.
3f40fffe1f53024fb0125c45 41414141 ee43
← shell code →
← filler → ← starting address of shell code →
Tuesday, November 4, 2014
Microcorruption - Whitehorse
I probably should have read the manual
(right, who does that?) before now. But better late than never. The
manual on pages on 4 and 5 talks about how 0x7F is the code to unlock
the door – not a function call but basically a hardware interrupt
that accomplishes the same thing. Additionally there is mention of
0x7D and 0x7E which set a byte in memory if the entered password
matches. Look for those to come into play at some point. We actually
already saw these in Cusco, although that knowledge wasn't fully
necessary at that point.
As in a Cusco, there is still an
instruction (after the password has been checked and after the “wrong
password” message is displayed) that adds 10 to the stack pointer.
So the stack pointer jumps forward x10 to address 3cc6, and the next
instruction is to return to the value held at the address 3cc6. The
assumption is apparently that you have not entered a password longer
than x10 bytes and put your own value in that address. But as the
saying goes: when you assume you make an 'ass' out of 'u' and 'me'..
So we can put what we want in 3cc6
because nothing prevents us from entering a longer password than 16
characters – that is nothing prevents us from overflowing the
buffer. Last time we did this we put the starting address of the
“unlock door” function. This time however there is no unlock door
function, but there is ample room to put code that we write in
assembly.
So first we want to get control of the
instruction pointer. Put the address at that point to the area of the
buffer that we have overflowed, that is point the instruction pointer
at the code we are overflowing into the buffer. This is called
injecting code.
So what do we want to do? As you may
recall from Cusco, hardware interrupt x7e when placed in the SR
(stack register) and called with the interrupt trap causes the
password to be checked. Hardware interrupt x7d sets a password in
memory indicating that the password was correct, and x7f straight up
unlocks the door.
Let's do the following:
Point the instruction pointer at memory
we control.
Get x7f into the SR **
Call the interrupt trap.
We can look at the existing
instructions in the code to cobble the bits and pieces together –
or if you want a project you can learn this version of assembly and
roll your own. But the basic
structure of the code we can use is as follows
3e40
007f mv #x7f00, r14
0f4e mv
r14, r15
024f mv
r15, sr
32d0
0080 bis (highbit), sr **
b012
1000 call 0x10
**Note: the manual says x7F opens the
door, but on my version of the game they had an extra part in there
that set the high bit to 1 yielding xFF as the value required to
unlock the door. The only way you figured this out was by looking at
the code within <INT>
453e: 32d0 0080 bis #0x8000, sr
so while you were examining
<conditional_unlock> where it loads x7e onto the stack and
calls <INT> you can see as you step through it that x7e gets
loaded into the SR (from r15) and they flip the high bit on and
then call the trap.
You can trim down the code above if you
work through it – I don't want to take all the fun out – but a
successful entry based on the code above can look like:
41414242434344444545464647474848c83c3e40007f0f4e024f32d00080b0121000
Microcorruption - REYKEYAVIC
In the end, one line of code shows you how to open the lock. Security through obscurity ... isn't. Sure don't be the tallest nail near the hammer, but that shouldn't be your entire game plan.
What follows is my thought process while analyzing the code, scroll to the end for the facepalm.
do_clear – writes 00 to x100 bytes
starting at x247c to initialize that portion of memory to x0 values.
While I personally feel that initializing things like variables and
pointers is good, safe practice in coding, it does serve to highlight
that this area of memory may be important to the encryption/
decryption/ password recognition process.
Main – puts x4520 into r14 and then
into r15. If this is meant as an address it puts you halfway into
“do_nothing”. Then promptly overwrites r14 with xf8 and r15 with
2400. Then calls ENC.
ENC – begins by writing values 0 to
x100 in sequential order to bytes starting at x247C and rising. Given
that we just spent all that time writing zeros to them, only to then
re-initialize them all again with specific values is … uh … part
of the game I guess. Moving along then....
44a4: R8 = 0
… an algorithm?
44b0: references troll text:
“ThisIsSecureRight?” and puts “T” (x54) in R10
44b4: sign extend R10 … this
shouldn't produce any result on an ascii character as the highest is
x7f. However if a non ascii character is entered there may be things.
44b6: R10+R13->R13 (54, 54)
44b8: Set high order bits to zero,
resulting in new byte. So if we sign extend, then add, then overflow
the register and set the high order bits back to zero, we can get a
new ascii character. Adding xffff to x53, then anding with xff yields
x52.
x247c: There appears to be something
being put starting here again...
444a: call 2400 program is now
executing code that it appears to have generated in the previous
portions.
Then I see it, the snippet of code that says: Compare d7c5 <---- this is the
password for opening the door.
Face, palm.
Subscribe to:
Posts (Atom)