A nice 'n' easy introduction into the bash shell

_________________________________________________________ Guide to (mostly) Harmless Hacking Vol. 5 Programmers' Series No. 4: A nice 'n' easy introduction into the bash shell _________________________________________________________ By idle (idle@mailexcite.com) with a little help from his friends (Meino :) (Carolyn Meinel) The first in the Programmers' Series introduced you to some of the very basics of writing shell scripts. Creating files, executing commands and so on. This Guide will introduce you to additional important concepts, but this time using the bash shell. Why is shell programming important for hacking? Let's say you are trying to get root control of a Hacker Wargame computer (see http://www.happyhacker.org for latest information on what computers are legal to break into). You find an exploit shell script at some place such as http://www.rootshell.com that looks ideal for doing the job -- but it doesn't work! Maybe your problem was not with the exploit script, but with what shell you are using or how your shell is set up. This Guide will help you understand more about what makes the bash shell work and how to make it do what you want it to do. ****************************************************************** In this Guide you will learn about: * The shell "environment" * variables * processes (parent and child processes) * the bash prompt -- how to customize it * tilde expansion * aliases * creating your bash startup (profile) file * how to write scripts in bash ****************************************************************** ****************************************************************** Newbie note: Bash is a Unix shell program. It takes all your commands and turns them into something a Unix type operating system can understand. I (Carolyn Meinel) recommend the tcsh shell, but if you can't use tcsh right now, bash is my second favorite shell. Bash stands for "Bourne Again Shell," a word play on the Bourne Shell, from which the bash shell was adapted. To find out whether you are currently using the bash shell, in your shell account give the command "env". It will include an entry "SHELL:bash" if you are already using it. If it shows a different shell, give the command "bash". If it gives an error message, you don't have bash. ****************************************************************** You'll soon see that even your most basic shell in any kind of Unix is more powerful than DOS, and if you are short of ideas for scripts to write, hopefully this will open your mind a little. Environment Variables If you have any programming experience at all, or you have been reading the Guides like a good little hacker, then you will know about Variables. Think of these as named pieces of your computer's memory which hold values. Depending upon the variables in question, you may or may not have immediate control of their contents. Another concept is your environment, which is what you find when you give the "env" command. Much like the real world you live in, your environment in your shell stores information such as the operating system you are running, the version, your home directory, a list of directories to look at when you want to run a program, and so on. Some of the more common bash environment variables that you will want to know a bit about are: PATH this is a colon-separated list of directories which bash uses to search for commands. eg: /bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/newstuff/progs HOME the current users home directory, eg: /home/bob CDPATH a colon-seperated list of directories to search when you issue the cd command, eg: /home:/usr/local/etc:/usr/local/games if you had a directory under /usr/local/etc called myGreatProg, you could issue a cd myGreatProg on the command line and you would be taken to the first occurrence of myGreatProg in CDPATH. In this case, /usr/local/etc/myGreatProg. When it comes down to it, this is not necessary, it's just a nicety which can confuse you at times. Use with care! MAILPATH a colon-separated list of spool files which bash checks periodically for new mail, eg: /usr/spool/mail/bob:/var/spool/bob PS1 & PS2 primary and secondary prompt strings. The primary prompt is used at your standard interactive shell. ie; when you log in or you run bash. The secondary prompt is used when bash needs more input to complete a command. for example, at the prompt, type: cd \ bash will change the prompt to ">" or whatever your PS2 is set to, indicating that you need to type more before it can execute your instruction. Try typing: ~ and after you press ENTER, the complete command will have been "cd ~". This takes you to your home directory. The backslash "\" indicated that the command continues on the next line. This can be used in your scripts as well for long lines that you don't want to get too messy. RANDOM This is a nice variable which you can call upon to give you a random number. Assigning a number to this will allow you to generate a seed. This is a nifty idea, but unfortunately, it is not entirely random. After executing this a few times, I noticed a pattern, so if you are going to use It, take care. Mind you, if you were going to write some really flash crypto routine, then you'd probably be using a programming language as opposed to a scripting language :) To set variables, you can use the set command, eg: set TEMP_VAR=cheese But this will not export it into the environment in bash. to do that, you must use export, eg: xport TEMP_VAR You can, however, just use export, thus: export TEMP_VAR=cheese Processes The concept of the environment now comes into full effect. When you are in your bash shell and you execute a command or script, that command INHERITS it's parent process's environment. The shell in this case is the parent process, and the command or script you run is the child process. This child process can in turn start a child process of it's own. The concept of child and parent is relative to which process you are talking about. Any process that starts a child becomes that child's parent. Much like in real life. You are a child, but may eventually become a parent as well. Init is the Mother Of All Processes. Think of it as god if you are religious. If you're not religious, think of it as a The Big Bang. Or anything else that pleases you. To see how your children and their parents are doing, use the command 'pstree', or if you don't have that, 'ps -f' to get a list of every process running and how it relates to init. Are we going off on a tangent? Probably...anyway, when you type a command, you will notice a variable called $_ is being set. We'll see soon what this is actually doing. Let's try an example. Type this: set hi_temp="Hi, this is TEMP" set | less Note: if you don't have less, use more. ;) You will see at this point, no variable called $hi_temp. but at the bottom, you should see =hi_temp=Hi, This is TEMP Then type: set hi_test="Hi, this is TEST" set | less Take note there is now no reference to "hi_temp". But one of the last lines should read "_=hi_test=Hi, this is TEST." The variable $_ is actually just a reminder of the last command you typed, not a place to remember set commands. You will find very quickly that unless you explicitly use export in the first instance in the shell (not in a script file), you will not be able to export to the environment. This is because the set command runs as a child process which eats it's own variables. You can prove this to yourself by doing the following: set hi_test="Hi, this is TEST" ls set| grep "hi_test" You will have nothing returned back. Whereas if you had done: export hi_test="Hi, this is TEST" ls set | grep "hi_test" Bash will return: hi_test=Hi, this is TEST Once the variable is in the environment, it can be accessed by the child processes. Note that the parent process will not inherit any variables from the children. This is very dangerous. In scripts, if you are setting variables to be used locally in the script, you won't have to export. But if you wish for those processes your script calls, ie, another script, then you will have to export. More on Variables To reference variables on the command line or in your scripts, you will have to prepend the variable name with the String symbol, or the dollar sign ($). Take the following simple example entered at the command line export HI=hello echo "$HI" The output after the echo command is: hello If you had just typed 'echo "HI"' as your last command, what do you think would have been displayed? All those who replied 'HI' get to buy themselves a chocolate fish. Note that after the echo command, no matter whether we are using variables or not, I use quote marks (") before and after the string to be echoed. This is best shown by example. Type the following at the prompt: export HI="*" echo $HI You will get the equivalent of the current directory's listing dumped out. But if you go: echo "$HI" Bash will display * Which is what you wanted. IE; the CONTENTS of the variable. bash likes to substitute commands before executing. Remember when to use quotes and you will be fine. To display a variable already set in place, you just use echo, eg: echo "$PS1" Will probably give you: \h:\w\$ Don't worry if yours is slightly different. Now you can create and reference variables. The Bashg Prompt This is the equivalent to using the DOS PROMPT command or the tcsh set prompt= command. In bash, the variable that stores what your prompt is to display is $PS1. To change this you simply need to do something basic like: export PS1="this is my computer: " And next time you look, your prompt will read: this is my computer: This prompt will wait for you to enter a command. You will notice that this is now set. Press ENTER, type "ls", it stays. Great! But that is a little boring. What you want it to do is display your user name and/or the current directory you are in so that you don't have to use pwd (the command to show your directory) constantly. Bash allows you to use switches (as seen when we did an echo $PS1 earlier) to display lots of useful info. Here is a list of the main ones: \d date (format: ddd mmm dd - eg: Sat Mar 12) \h display your computers hostname \n newline \s shell name (ie: bash) \t time (24 hour format: hh:mm:ss - eg: 16:22:08) \u current user \w working directory (this changes on the fly to the directory that you change to). \$ display $ character \\ display \ character There are others, but these will be the most useful to you. Play around and see what others you can find. Tilde Expansion Another neat thing that bash provides is what is called Tilde Expansion. The tilde key (~) on it's own is expanded out to the user's home directory, for example. You can see this by issuing a 'cd ~' on your command line. Here is a list of expansions that bash will give you: ~ expands to $HOME ~bob expands to bob's home directory ~+ $PWD ~- $OLDPWD These expansions basically replace themselves on the command line with the contents of the second column above. Once the command has been expanded, bash executes it. Aliases One of the things I could not do without is the alias command. If you enter any aliases, bash will fill out the text of the alias name with whatever the alias contents is before executing the command. For example, when I use ls, I like to see my directory listing in a certain way. Colors, for example, are good. I also like slashes (/) at the end of directory names just to show they are directories, and I also like to view all files starting with a full stop (.) in my listings. So instead of typing 'ls -Aop' every single time I want a dir listing, I just use an alias so that -Aop becomes my default. I enter the command: alias ls='ls -Aop' And from then on, bash uses that alias as my default command. Whenever I enter the alias name (in this case 'ls'), bash expands that out to 'ls -Aop'automatically for me. If I want to remove the alias, I use 'unalias ls'. Alternatively, I can opt to type 'unalias -a' to remove all aliases I have set. Bash Startup File If you have read the previous guides, you will know that bash provides you with some startup files which get run whenever you log into a bash shell. The file I am talking about is your profile. This can be called .bash_profile, .bash_login, or .profile and is located in your home directory. If you don't have this file, then just create it in your favorite text editor. Then you can insert any alias commands you want, and next time you log in, they will all be active. Or, you can write the file now and use the 'source' command to execute your profile without logging out: source .profile (or substitute for ".profile" whatever your profile is called.) ********************************************************************** You can kill your account -- warning! Adding things to .profile should be tested ONLY with the "source .profile" command!!! And only if you are satisfied with the result, log out and login again!!! Reason: Imagine you wrote an "exit" at the wrong place (i.e. not preceding it with a comment-sign), you will never be able to login! A solution is to login twice, one shell is good for testing the other one for recovering from errors!!! You can login twice with the command "telnet localhost". Then if you mess up in this second shell you will wind up back in your first shell and can delete the profile file before it kills your shell again. This saves the embarrassment of having to call tech support and confess to having made a big mess of your account. ********************************************************************** Let's Script! So now that we've got all that basic stuff out of the way, let's get into some serious shell scripting. You know how to string commands together by now; you just create a text file, start listing any commands you want to execute, save the file, change its permissions (chmod +x ) and viola, you have a shell script. But what if you want to do something more? What if you want to pass parameters on the command line? What if you want to check for files existence before executing a command? What if you want to compare the values of two variables? What indeed? You will need to know a bit about Loop Constructs and Conditional Constructs. Loop constructs are: until while for Conditional constructs are: if case I will list the syntaxes of each and give basic real life examples to help you along your way. None of these are exhaustive, but they will give you a good foothold to allow you to further research bash and learn how to kick serious butt. One thing to remember with the following constructs is that they are not like most other languages you may be used to. When you do a test, you are actually executing commands. Whether these are commands built-in to bash, or whether they are other commands it doesn't matter, but you can't get away with direct comparisons of variables. You'll see in the examples. Also, all semi-colons (;) can be replaced with newlines. ******************************************************************** IMPORTANT NOTE: bash is VERY space sensitive. Unless you copy any example scripts EXACTLY, you will most likely encounter problems. ******************************************************************** UNTIL Syntax: until ; do ; done Description: will continue to be executed until has an exit status which is not zero (ie; until it's not false) Example script: -------- #!/bin/bash counter=1 until [ "$counter" = "10" ] do echo $counter let counter+=1 done -------- This script is very simple. It sets a variable called "counter" and initializes it to a value of 1. (We don't need to export it as it doesn't need to be in the environment. Only this script will be using it.) Then we want to echo a list from 1 to 10, so we do the comparison. We put the test arguments into square brackets so that bash knows we are doing a comparison. It will keep looping until the variable counter reaches a value of "10". The line "let counter+=1" is saying "take the contents of the variable counter, add 1 to it, and save the value back into counter. We could have also typed: let counter=$counter+1 Note where you use the $ preceding the variable names and when you don't. When we want to display the actual value in the variable we use $variable. When we want to reference the variable for the reasons of changing it, we just use variable. The 'let' command allows you to do mathematical equations. Alternatively, you can wrap the equation in $[]. For the example above, it would be: $[counter+=1] This is know as Arithmetic Expansion, and I have found it to be less reliable than using the let command. Also, using let makes your scripts that much more readable in the future. Mathematical Operators: So you know what sort of mathematical operators you can use, here is a list: - + unary minus and plus ! ~ logical and bitwise negation * / % multiplication, division, remainder + - addition, subtraction << >> left and right bitwise shifts <= >= < > comparison == != equality and inequality & bitwise AND ^ bitwise exclusive OR | bitwise OR && logical AND || logical OR = *= /= %= += -= <<= >>= &= ^= |= assignment operators WHILE Syntax: while ; do ; done Description: do while has an exit status of zero. (ie; true) Example Script: -------- #!/bin/bash counter=1 while [ "$counter" != "11" ] do echo $counter let counter+=1 done -------- We use the same script here using a different method of testing. This script will continue to loop while the variable $counter is not 11. This means that it will happily process the commandlist until $counter equals 11. Note that this is a string comparison, not an integer (numerical) comparison. FOR Syntax: for [in ]; do ; done Description: This will execute for each item in . If the optional [in ] is left out, bash will replace this with $@. Example Script: -------- #!/bin/bash for i in cheese comics "computers which don't suck" motorbikes do echo "I like $i" done -------- This will display: I like cheese I like comics I like computers which don't suck I like motorbikes As you can see, each word is treated as a separate parameter unless you use quotes to block a group of words. IF Syntax: if ; then elif ; then elif ; then else fi Description: This command is rather more complex in nature. Using elif, you can include as many tests as you like. If the script fails to find a match in any of the tests, it will default to executing the commandlist under else. The has an extensive list of parameters you can pass which allow you to test for various things including whether files exist on your hard drive, comparing 2 variables, etc. Example script: -------- #!/bin/bash if [ -z $1 ] then echo "enter your favorite band name" exit fi if [ "$1" = "manowar" ] then echo "the greatest band on the planet!" elif [ "$1" = "DIO" ] then echo "awesome! they rock!" elif [ "$1" = "KISS" ] then echo "ah. that's taking me back..." else echo "never heard of $1" fi -------- First, if you don't know it already, $1 indicated the first parameter on a command line. For example, if you enter 'cd /home/me', "/home/me" is the first parameter that you have entered. Inside the script, or the cd command, it could be referred to as $1. $2 is second, $3 is the third, etc. One nice ability is to use $0, which refers to the calling command - in this case "cd." Even batter is $* which stores all command line parameters, except $0. OK, next we introduce two kinds of tests in the if construct. -z checks to see if the user has entered any parameters, and promptly informs them when they forget to do so. The next one which is used in the rest of the script compares two variables. Both are wrapped in quote marks to avoid bash trying to execute them as commands. Another point to note is make sure to separate the square brackets, otherwise bash will get confused. Other useful if lines: if [ -f ~/.profile ] (checks to see if you have a .profile file in you current home dir. If so, the test will pass TRUE) if [ ! -f ~/.profile ] (checks to see if ~/.profile is NOT there and return TRUE if the file is NOT there.) Take note, this only checks for the existence of files, not directories. Can you figure out how to make it check for the existence of directories? CASE Syntax: case in pattern [| pattern]) ;; pattern [| pattern]) ;; esac Description: The case command firstly expands whatever is in and compares it to all the patterns listed. If it finds a match, it will execute the appropriate . More than one pattern can be included per line by separating them with a pipe (|) symbol. Also, you can use the pattern * to include everything else. Example script: -------- #!/bin/bash case $1 in cheese | bread | meat) echo "food!" ;; viper | stingray | transam) echo "car!" ;; *) echo "I don't know what it is!" ;; esac --------- This is fairly straight forward once you get to grips with it. It pulls in a parameter that you enter on the command line and tries to figure out what it is. If you enter "cheese", "bread" or "meat", our clever script will recognize this as a type of food and will tell you so. If you enter "viper", "stingray" or "transam", it will recognize it as a car! Anything else leaves our poor program clueless. There is also a command called select which was stolen from the korn shell. The syntax for this is very similar to case. See if you can figure it out and get it working. HINT - read "man bash". There are many more features of bash, I have not covered a great portion, but that is part of the fun. Now go read the man page (this text should help you make sense out of it!) and see what other nifty little features bash has. I wrote this tutorial using bash version 1.14.7(1). I am about to upgrade to 2.02.01 or whatever the latest is to see what differences that gives me. Get the latest bash version via ftp at prep.ai.mit.edu or one of its mirror sites. This is a good time to brush up your search engine skills ;) _______________________________________________________________________ Where are those back issues of GTMHHs and Happy Hacker Digests? Check out the official Happy Hacker Web page at http://www.happyhacker.org. We are against computer crime. We support good, old-fashioned hacking of the kind that led to the creation of the Internet and a new era of freedom of information. So don't email us about any crimes you may have committed! To subscribe to Happy Hacker and receive the Guides to (mostly) Harmless Hacking, please email hacker@techbroker.com with message "subscribe happy-hacker" in the body of your message. Copyright 1998 idle (idle@mailexcite.com). You may forward, print out or post this GUIDE TO (mostly) HARMLESS HACKING on your Web site as long as you leave this notice at the end. _________________________________________________________ Carolyn Meinel M/B Research -- The Technology Brokers http://techbroker.com

Share this article

Receive all the latest articles by email!

Get all articles delivered directly to your mailbox as and when they are released on WindowSecurity.com! Choose between receiving instant updates with the Real-Time Article Update, or a monthly summary with the Monthly Article Update.



Receive all the latest articles by email!

Receive Real-Time & Monthly WindowSecurity.com article updates in your mailbox. Enter your email below!
Click for Real-Time sample & Monthly sample

Become a WindowSecurity.com member!

Discuss your security issues with thousands of other network security experts. Click here to join!

Community Area

Log in | Register

Solution Center

Readers' Choice

Which is your preferred Email Anti Virus solution?