--- tags: devtools2022 --- # 02B-Shell Scripting ## Your current shell You can find your current OS shell with the command `which $SHELL`. Few of the most widely used shells are bash and zsh. Since both zsh and bash are derived from the same **[Bourne](https://en.wikipedia.org/wiki/Bourne_shell)** shell family as bash does, most commands, syntax, and control structures will work just the **same**. ## Writing a shell script Most users think that shell is equivalent to a *prompt* and a command line. That is in fact shell in **interactive mode**. A shell can also run in non-interactive mode, as when **executing** scripts. We can use scripts to **automate** certain logic, for example creating and deleting several files, renaming files, etc. Scripts are basically **lists** of **commands** (just like the ones we can type on the command line), but **stored in a file**. When a script is executed, all these commands are (generally) executed **sequentially**, one after another For example, here's a shell script that helps you to create a directory (if it doesn't exist already): ```bash= #!/bin/bash echo "Enter directory name" read ndir if [ -d "$ndir" ] then echo "Directory exist" else `mkdir $ndir` echo "Directory created" fi ``` Create a file called `createdir.sh` with the content above. Run it with the command `bash createdir.sh`. ![](https://i.imgur.com/pmBXP5s.png) You can also make the script directly executable because of the presence of the **shebang** line. Change its permission to be executable with `chmod` and then run it like an executable with `./<execname>`: ![](https://i.imgur.com/vUCmtC4.png) ## Shebang In computing, a shebang is the **character** **sequence** consisting of the characters number sign and exclamation mark (#!) at the beginning of a script. When we try to run an executable file, the **[execve](https://man7.org/linux/man-pages/man2/execve.2.html)** program is called to **replace** the current process (bash shell if we are using terminal) with a **new** one and **decide** how exactly that should be done. **If we try to run a text file** (scripts), execve **expects** the first two characters of the file to be “#!” (read “shebang” or “hashbang”) followed by a **path to the interpreter** that will be used to **interpret** the rest of the script. The most common need for using shebang appears when we’re writing shell scripts. Below is a simple example of a shell script that will simply greet a user: ```shell= #!/bin/sh echo "Hello, ${USER}" ``` > where `sh` is a command language interpreter that executes commands read from a command line string and `/bin/sh`is actually the path (**symbolic link**) to sh-compatible implementation of sh (Shell Command Language). In most cases, it’ll be **bash** (Bourne-Again SHell), but if we want to secure portability, we should use the symlink. Also, remember that the script **must** be executable for this to work. When we create a new script file, we can make it **executable** using the `chmod` command: ``` chmod +x name_of_the_file ``` If we want to use different interpreters, such as `python`, we can change the shebang to any [ELF executable](https://linuxhint.com/understanding_elf_file_format/). For example, here we use `python` as the interpreter of the script: ```python= #!/usr/bin/python print("hello world") x = 3 y = 4 print(x+y) ``` ![](https://i.imgur.com/OETKhyK.png) At first, the file pythonscript is not executable since we cant execute it (running `./pythonscript` results in error). After we changed the mode, we can now execute it using `./pythonscript`. This is functionally **equivalent** to running the command `python pythonscript`. ## Background knowledge ### File Permission File permission can be viewed when you type `ls -l` command. The output can be something like this: ``` 24120112 -rw-r--r--@ 1 natalie_agus staff 5492569 Sep 1 10:05 Develop and Collaborate (Day1 - Morning) - HackMD.pdf 23676798 drwxr-xr-x@ 9 natalie_agus staff 288 Sep 1 12:18 DevTools ``` We particularly want to bring your attention to 10-letter section of the output such as `-rw-r--r--` and `drwxr-xr-x`. What do you think they mean? *Hint: Every file and directory on your Unix/Linux system is assigned 3 types of owner (User/creator, Group, Other). Each owner can have the right to execute, read, or write into the file.* ### File Links ![](https://i.imgur.com/3NQKpKQ.png) In UNIX-like file systems: * An inode is a data structure which purpose is to contain attributes about **all** files in the system. * It also contains references to files (which otherwise is just an anonymous chunks of data) each given an **inode number**. You can try it by typing the command `ls -il` in your current directory to display the inode number of all your files in that path: ![](https://i.imgur.com/W0SeJM9.png) * A file in its entirety is **not** an inode, a file has an associated inode with it. inode contains only the file attributes (metadata). It is pretty difficult for users to find their files based on inode numbers. Therefore, we have directories which allows us to traverse the file system using named paths. * A directory is a **special** file whose content is some kind of **mapping** of names to files (more specifically inodes). * A directory can have another directory as its entry (so we can search for files via recursion). * We can have **different names** for the **same** file; in this case, they have no difference (in content) at all, like a person with different nicknames. These “extra names” mapped to the same inode entry are called **hard links**. * There’s another type of link called **symbolic links**, also known as "shortcuts". A symbolic link is simply a file whose **content** is a **text string**(i.e: reference to another file or directory) that is **automatically** interpreted and followed by the operating system as a path to another file or directory. ### Example of File Link: Hardlink * A file containing “helloworld” is created, with inode 24247396. We call it “input”. * At this point, a directory entry named “input” pointing to this file containing “helloworld” is created within the directory named Links. We call this entry the **original** filename. * We create a hardlink to “input”: `ln <inputfile> <outputfile>` * Upon inspection, notice that **both** input and input_hardlink points to the same file: 24247396. There’s virtually **no difference** between the original name-file mapping “input” and “input_hardlink”. ![](https://i.imgur.com/UFzVlRc.png) Like regular files, path have path names in filesystem namespace, e.g: /Users/natalie_agus/Desktop/Links/input_hardlink ### Example of File Link: Symbolic link You can create a symbolic link using the following command: `ln -s <inputfile> <outputfile> ` ![](https://i.imgur.com/XqQDVxS.png) Note that a symbolic link has a new inode id (24247669 as shown). This means that symbolic link is created as a **new file** unlike hardlinks. If we delete the original reference `input.txt`, the symbolic link will be **broken**. However the hardlink will still work because the file is **not deleted on disk**. ![](https://i.imgur.com/bD2y6xh.png) ## Bash script quick-tips ### Special characters ![](https://i.imgur.com/ln00dU0.png) ### Parameters ![](https://i.imgur.com/M1oVNBV.png) ### Important control operators The easiest way of performing a certain action depending on the **success** of a previous command is through the use of control operators. These operators are && and ||, which respectively represent a logical AND and a logical OR. These operators are used *between* two commands, and they are used to control whether the second command should be executed depending on the success of the first. **This concept is called conditional execution.** ![](https://i.imgur.com/dyR6vEk.png) **Yes, that space after the \[\[ and before the \]\] is intentional**. Maybe you wonder why it is \[\[ instead of \[? [Read this](http://mywiki.wooledge.org/BashGuide/TestsAndConditionals). ## Sourcing and Aliasing The `source` command reads and executes commands from the file specified as its argument in the **current** shell environment. It is useful to **load** functions, variables, and configuration files into shell scripts (kind of like `import` in Python) or to the current shell environment. You can source a file in two different ways: ```bash= source FILENAME [ARGUMENTS] . FILENAME [ARGUMENTS] ``` Bash **aliases** are essentially shortcuts that can save you from having to remember long commands and eliminate a great deal of typing when you are working on the command line. For example, you could set the alias `gpom` to be a shortcut for the `git pull origin master` command. Creating aliases in bash is very straight forward. The syntax is as follows: ``` alias alias_name="command_to_run" ``` To test what it means as loading **into the current shell environment**, create a file called `config.sh``: ```bash= VAR1="foo" VAR2="bar" alias gpom="git pull origin master" ``` Here we defined two variables, which otherwise doesn't exist in our original shell **session**. After we type the command `source config.sh`, the two variables are "registered" in our current session as shown: ![](https://i.imgur.com/h1RubSO.png) However when we close our shell window and reopen a new one, we need to **reload** this `config.sh` script to maintain the loading of the two variables and the alias in the new **session**. It can be a tedious job having to repeatedly `source <FILE>` each time you want to load important stuffs like `$PATH` configuration, prompt customisations, aliases, etc. ## Shell Profiles The shell profile is a file on your computer that your shell runs every time a new a session is created. For bash, this will be `.bashrc` and for zsh this will be `.zshrc`. They must be located in common places, like your `$HOME` directory. This is useful because we need to run certain code every time before starting to work. It has to be located in your home directory, with the name `.bash_profile` exactly. ### Test profile Create a new file called `.bash_profile` in your EC2 home directory. You can use the GUI in Cloud9 or the following command: ``` touch /home/ubuntu/.bash_profile ``` Then paste the following content in `.bash_profile`: ```bash= export VAR1="foo" export VAR2="bar" alias gpom="git pull origin master" export PATH=/FundamentalDevTools:$PATH ``` Before having the `.bash_profile`, the shell doesn't know what `VAR1` and `VAR2`, or `gpom` are. The `PATH` also does not contain the new directory `/FundamentalDevTools`. ![](https://i.imgur.com/NYxywtK.png) After having `.bash_profile`, we can successfully test for these new variables, alias, and `PATH` update each time the terminal starts: ![](https://i.imgur.com/ms6HwB2.png) ## Quick Setup Everytime you setup a new system, it is useful to use some tools that will install all programs that you might need in the system for development. One excellent example is [Ansible](https://github.com/ansible/ansible) which lets you automate the setup of a network of computers at once. For now, let's use this simple script as example to configure a fresh EC2 Ubuntu instance for our purpose. First, let's change it's default shell to `zsh` ```bash #!/bin/bash # zsh and ssh sudo apt update sudo apt install zsh sudo chsh $USER -s $(which zsh) sudo apt install openssh-server ``` Then, setup zsh at `~/.zsh`. We install some plugins to allow syntax highlighting, autosuggestions, etc. ```bash # zsh setup at ~/.zsh sudo apt install git mkdir -p ~/.zsh/plugins ~/.zsh/themes touch ~/.zsh/.zshrc touch ~/.zsh/.zsh_history git clone https://github.com/spaceship-prompt/spaceship-prompt.git ~/.zsh/themes/spaceship-prompt git clone https://github.com/zdharma-zmirror/fast-syntax-highlighting.git ~/.zsh/plugins/fast-syntax-highlighting git clone https://github.com/zsh-users/zsh-completions.git ~/.zsh/plugins/zsh-completions git clone https://github.com/zsh-users/zsh-autosuggestions.git ~/.zsh/plugins/zsh-autosuggestions git clone https://github.com/unixorn/fzf-zsh-plugin.git ~/.zsh/plugins/fzf-zsh-plugin git clone https://github.com/Aloxaf/fzf-tab ~/.zsh/plugins/fzf-tab git clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf ~/.fzf/install ``` Then populate `.zshrc`: ```bash ln -s -T ~/.zsh/.zshrc ~/.zshrc echo "### ZSH HOME export ZSH=\$HOME/.zsh ### ---- history config ------------------------------------- export HISTFILE=\$ZSH/.zsh_history # How many commands zsh will load to memory. export HISTSIZE=10000 # How many commands history will save on file. export SAVEHIST=10000 # History won't save duplicates. setopt HIST_IGNORE_ALL_DUPS # History won't show duplicates on search. setopt HIST_FIND_NO_DUPS # Immediate Append setopt INC_APPEND_HISTORY # Add timestamp export HISTTIMEFORMAT=\"[%F %T] \" setopt EXTENDED_HISTORY ### PATH export PATH=\$HOME/bin:\$HOME/.local/bin:/usr/local/bin:/snap/bin:/opt/bin:\$PATH ### ---- PLUGINS & THEMES ----------------------------------- source \$ZSH/themes/spaceship-prompt/spaceship.zsh-theme source \$ZSH/plugins/fast-syntax-highlighting/fast-syntax-highlighting.plugin.zsh source \$ZSH/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh [ -f ~/.fzf.zsh ] && source ~/.fzf.zsh source \$ZSH/plugins/fzf-zsh-plugin/fzf-zsh-plugin.plugin.zsh source \$ZSH/plugins/fzf-tab/fzf-tab.plugin.zsh fpath=(\$ZSH/plugins/zsh-completions/src \$fpath) alias zr=\"source ~/.zshrc\" ### --- Spaceship Config ------------------------------------ SPACESHIP_PROMPT_ORDER=( user # Username section dir # Current directory section host # Hostname section git # Git section (git_branch + git_status) hg # Mercurial section (hg_branch + hg_status) exec_time # Execution time line_sep # Line break jobs # Background jobs indicator exit_code # Exit code section char # Prompt character ) SPACESHIP_USER_SHOW=always SPACESHIP_PROMPT_ADD_NEWLINE=false SPACESHIP_CHAR_SYMBOL=\"❯\" SPACESHIP_CHAR_SUFFIX=\" \" source \$ZSH/plugins/fzf-zsh-plugin/fzf-zsh-plugin.plugin.zsh source \$ZSH/plugins/fzf-tab/fzf-tab.plugin.zsh" >> ~/.zshrc ``` Finally, install some other useful tools: ```bash ################ some other tools ################# # encryption sudo apt install whois # exa sudo apt install exa echo "alias ls=\"exa --long\"" >> ~/.zshrc echo "alias zr=\"source ~/.zshrc\"" >> ~/.zshrc # the fuck sudo apt-get install python3-dev sudo apt-get install python3-pip pip3 install thefuck --user echo export PATH="$PATH:/home/ubuntu/.local/bin" >> ~/.zshrc echo "eval $(thefuck --alias)" >> ~/.zshrc # bat sudo apt-get install bat echo "alias cat=\"batcat\"" >> ~/.zshrc ``` Once done, save the file and make it executable, then run it. ```bash chmod a+x setup.sh ./setup.sh ``` Logout, then log back in to observe the change: ![](https://i.imgur.com/zYUemwI.png) > Note: using shell plugin manager like [zinit](https://github.com/zdharma-continuum/zinit) is a better choice once we know how things work under the hood. > ## Running a Script on Startup with cron The idea of a startup script is to help us get our system up and running again in the event of unexpected reboot, such as to re-run our node `server.js` upon startup. Prepare a simple startup script called `startup.sh`. Take note of the path where you store it: ```bash= #!/bin/sh echo "Last reboot time: $(date). Running node server now." >> <absolute your path to log file>/logfile.txt node <absolute your path to server file>/html.js ``` Remember to use **absolute path** rather than relative path. Then change it to be executable: ``` chmod +x startup.sh ``` The idea is to add a log each time the machine starts up into a file called `logfile.txt`, and then start the node server. The logfile is for us to know that the startup script is executed properly once the machine boots up. ### Cron The software utility `cron` (also known as cron job) is a time-based job scheduler in Unix-like computer operating systems. We can tell it to do some stuffs like **running startup scripts** when the machine boots up. It is typically used by users who **set up** and **maintain** software environments. With cron, users can schedule jobs to run periodically at fixed times, dates, or intervals. > If you want to learn more on how to write **cron schedule expression**, visit [this site](https://www.netiq.com/documentation/cloud-manager-2-5/ncm-reference/data/bexyssf.html) and [this site](https://crontab.guru) to get started. Go to [here](https://www.tecmint.com/11-cron-scheduling-task-examples-in-linux/) if you'd like to get started quickly without depth in understanding instead. To add jobs to cron, type the command `sudo crontab -e`. This will bring up an editor where you can type the following command: ``` @reboot sh <path to your startup script>/startup.sh ``` ![](https://i.imgur.com/EsfEWCU.png) Then you can reboot your system using the command: ``` sudo reboot ``` Check at start time that there's an entry in your `logfile.txt`: ``` Last reboot time: Thu Sep 2 12:22:52 UTC 2021. Running node server now. ``` Check that `node` is running `server.js` using the command: ``` ps aux | grep node ``` ![](https://i.imgur.com/tCjoggo.png) Using cron is quick and clean since we don’t have to deal with additional configurations, **but not every version of cron supports @reboot**. To list out cron tasks for the current user, use the command: ``` crontab -l ``` To delete existing tasks, simply open the crontab editor again with `crontab -e` and delete the lines you no longer need. ## Exercise: Useful Tools We now know how to write bash scripts, how to schedule jobs with cron, and how to read syntaxes of bash script. We also have learned a little bit about file links and file permission. The one thing left to do now is *practice* and familiarise ourselves with **useful commands** so we have a better idea on how to write a shell script that best suit our needs. Checkout the following commands by typing `man <command>` on your terminal and trying them out. **Text-manipulation scripting tool:** * awk * sed * grep * sort * uniq * cat * cut * echo * egrep * fgrep * wc **Process monitoring commands:** * ps * top * htop * atop * lsof **Network diagnostics tools:** * nmap * tcpdump * ping * mtr * traceroute * dig * iptables * netstat It takes more than a day to be able to write your own bash script, so take it easy! Start by reading great examples first, which can be found [here](https://github.com/awesome-lists/awesome-bash). They're useful tools that can make your development experience so much faster and useful. For instance, [this tool](https://github.com/sebglazebrook/aliases) creates useful aliases such as `gc` for `git commit`, `ll` for `ls -la`, and so on. To put the cherry on the cake, checkout `[Ohmyzsh](https://github.com/ohmyzsh/ohmyzsh)` to customise your `zsh` shell. P.S.: checkout this awesome tool [Codegrepper](https://www.codegrepper.com). You can install it as a Chrome extension, then search for any dev-related queries, e.g: *"convert react to typescript"*, and **boom**! it shows the command for you: ![](https://i.imgur.com/uSCTwqU.png) Happy copy pasting! *Just ensure you know how they work*. # 02C-The TOCTOU Bug Clone [this](https://github.com/natalieagus/devtools-toctou) repository, and notice you have the following files: ![](https://i.imgur.com/8EXFFKy.png) `vulnerable_root_proc.c` contains instructions that checks for `access` before opening a file on behalf of the user: ```c if (!access(fileName, W_OK)) { printf("Access Granted \n"); /*Simulating the Delay*/ sleep(DELAY); // sleep for 1 secs ... ``` What this program does is to **reset** the password of any user in the system to `00000`. Of course, only `root` account should be able to do this. It receives two arguments: the file containing the password to replace, and the target username. - The `sleep` is deliberately added to introduce a delay, hence exacerbating the vulnerability of this program. The idea is to write a bash script to **exploit** this vulnerability. ### Compile Vulnerable Program as Root Set the `root` password, and login as `root`. Afterwards, `make` the program, and add a few users of your choice with some difficult password like `LDcwzD&#6JKr`. ``` sudo passwd root su root cd [PATH_TO_ROOTFILES/RootFiles] && make adduser [USERNAME] && adduser [USERNAME] sudo # repeat 2-3 times ``` You can **test** `vulnerable_root_prog` as `root`, for eg: ``` ./Root/vulnerable_root_prog /etc/shadow [TARGET_USERNAME] ``` This will successfully change `[TARGET_USERNAME]` password to `00000`. ### The Attack Script Now switch back to your normal user, e.g `su ubuntu`. Our idea is to run the `vulnerable_root_prog` to open some regular user owned file so that `access` is granted, then quickly swapping it (using symlink) to `root` file `/etc/passwd` where all our user passwords are stored as hash values. We want to run the script as such `./exploit.sh [TARGET_USERNAME]`. The script must: 1. Check if the first argument is given, else `exit` 2. Run `./Root/vulnerable_root_prog userfile.txt [TARGET_USERNAME]` as regular user, which will obviously be granted since `userfile.txt` are indeed just a text file belonging to the user, and **quickly** create symlink `userfile.txt` pointing to `/etc/shadow` 3. We need to keep track if `/etc/shadow` file has successfully be **changed** (use `ls`). If yes, exit. Otherwise repeat step (2). ### Cleanup Once successful, you can delete the toy users and its home directory using the command: ``` userdel -r [USERNAME] ``` # Summary - [x] Shebang line, basics of shell scripting language - [x] File permissions and links - [x] Control operators in bash scripts - [x] Startup, setup scripts, and cron jobs - [x] Useful system programs - [x] TOCTOU Vulnerability and exploitation