Documentation Index
Fetch the complete documentation index at: https://hackbook.dudji.com/llms.txt
Use this file to discover all available pages before exploring further.
Basic Script
#!/bin/bash
if [ $# -eq 0 ]
then
echo -e "You need to specify the target domain.\n"
echo -e "Usage:"
echo -e "\t$0 <domain>"
exit 1
else
domain=$1
fi
<SNIP>
#!/bin/bash selects the interpreter.
if-else-fi handles conditional execution.
echo prints output.
$#, $0, and $1 are special variables.
domain=$1 stores the first user argument.
Shebang
The shebang is the first line of a script and starts with #!. It tells the
system which interpreter should run the file.
Other common examples:
Conditionals
Use if, elif, else, and fi to branch based on conditions.
#!/bin/bash
value=$1
if [ "$value" -gt "10" ]
then
echo "Given argument is greater than 10."
elif [ "$value" -lt "10" ]
then
echo "Given argument is less than 10."
else
echo "Given argument is not a number."
fi
Dudji@htb[/htb]$ bash if-elif-else.sh HTB
if-elif-else.sh: line 5: [: HTB: integer expression expected
if-elif-else.sh: line 8: [: HTB: integer expression expected
Given argument is not a number.
Arguments and Variables
Bash supports positional arguments directly.
$0 is the script name.
$1 to $9 are the first nine positional arguments.
$# is the number of arguments.
Dudji@htb[/htb]$ ./script.sh ARG1 ARG2 ARG3 ... ARG9
ASSIGNMENTS:
$0 $1 $2 $3 ... $9
Special Variables
Special variables use the Internal Field Separator
(IFS) to split arguments.
| Special Variable | Description |
|---|
$# | Number of arguments passed to the script. |
$@ | Full list of command-line arguments. |
$n | Argument by position, for example $1. |
$$ | PID of the current process. |
$? | Exit status of the last command. 0 means success. |
Variables
Assignments do not use $, and there must be no spaces around =.
Bash treats variable content mostly as strings unless used in arithmetic.
Variables are global by default unless declared with local.
Dudji@htb[/htb]$ variable = "this will result with an error."
command not found: variable
Dudji@htb[/htb]$ variable="Declared without an error."
Dudji@htb[/htb]$ echo $variable
Declared without an error.
Arrays store multiple values under one variable name, indexed from 0.
#!/bin/bash
domains=(www.inlanefreight.com ftp.inlanefreight.com vpn.inlanefreight.com www2.inlanefreight.com)
echo ${domains[0]}
Quoted values stay together as one array element:
#!/bin/bash
domains=("www.inlanefreight.com ftp.inlanefreight.com vpn.inlanefreight.com" www2.inlanefreight.com)
echo ${domains[0]}
Arithmetic
Use arithmetic expansion $((...)) and increment/decrement operators for math.
| Operator | Description |
|---|
+ | Addition |
- | Subtraction |
* | Multiplication |
/ | Division |
% | Modulus |
variable++ | Increase by 1 |
variable-- | Decrease by 1 |
#!/bin/bash
increase=1
decrease=1
echo "Addition: 10 + 10 = $((10 + 10))"
echo "Subtraction: 10 - 10 = $((10 - 10))"
echo "Multiplication: 10 * 10 = $((10 * 10))"
echo "Division: 10 / 10 = $((10 / 10))"
echo "Modulus: 10 % 4 = $((10 % 4))"
((increase++))
echo "Increase Variable: $increase"
((decrease--))
echo "Decrease Variable: $decrease"
${#variable} returns string length:
#!/bin/bash
htb="HackTheBox"
echo ${#htb}
Use read when the script should pause for user input.
echo -e "Additional options available:"
echo -e "\t1) Identify the corresponding network range of target domain."
echo -e "\t2) Ping discovered hosts."
echo -e "\t3) All checks."
echo -e "\t*) Exit.\n"
read -p "Select your option: " opt
Output Control
Use tee when you want to both display output and save it to a file.
netrange=$(whois $ip | grep "NetRange\|CIDR" | tee -a CIDR.txt)
hosts=$(host $domain | grep "has address" | cut -d" " -f4 | tee discovered_hosts.txt)
-a appends instead of overwriting.
Dudji@htb[/htb]$ cat discovered_hosts.txt CIDR.txt
165.22.119.202
NetRange: 165.22.0.0 - 165.22.255.255
CIDR: 165.22.0.0/16
Flow Control - Branches
Branches let the script choose one path over another. In Bash, the main branch
constructs are if-else and case.
Case Statements
case compares one expression against exact patterns.
case <expression> in
pattern_1 ) statements ;;
pattern_2 ) statements ;;
pattern_3 ) statements ;;
esac
Example:
case $opt in
"1") network_range ;;
"2") ping_host ;;
"3") network_range && ping_host ;;
"*") exit 0 ;;
esac
Functions
Functions keep scripts shorter and easier to reuse. Define them before the
first call because Bash reads from top to bottom.
function name {
<commands>
}
Example:
function network_range {
for ip in $ipaddr
do
netrange=$(whois $ip | grep "NetRange\|CIDR" | tee -a CIDR.txt)
cidr=$(whois $ip | grep "CIDR" | awk '{print $2}')
cidr_ips=$(prips $cidr)
echo -e "\nNetRange for $ip:"
echo -e "$netrange"
done
}
Parameter Passing
Functions use their own positional parameters, just like scripts.
#!/bin/bash
function print_pars {
echo $1 $2 $3
}
one="First parameter"
two="Second parameter"
three="Third parameter"
print_pars "$one" "$two" "$three"
Return Values
Functions return status codes, and $? reads the last one.
| Return Code | Description |
|---|
1 | General errors |
2 | Misuse of shell builtins |
126 | Command invoked cannot execute |
127 | Command not found |
128 | Invalid argument to exit |
128+n | Fatal error signal n |
130 | Script terminated by Control-C |
255 | Exit status out of range |
#!/bin/bash
function given_args {
if [ $# -lt 1 ]
then
echo -e "Number of arguments: $#"
return 1
else
echo -e "Number of arguments: $#"
return 0
fi
}
given_args
echo -e "Function status code: $?\n"
given_args "argument"
echo -e "Function status code: $?\n"
content=$(given_args "argument")
echo -e "Content of the variable: \n\t$content"
Flow Control - Loops
Loops repeat work until input is exhausted or a condition changes.
for loops iterate over items.
while loops run while a condition is true.
until loops run while a condition is false.
For Loops
for variable in 1 2 3 4
do
echo $variable
done
for variable in file1 file2 file3
do
echo $variable
done
for ip in 10.10.10.170 10.10.10.174
do
ping -c 1 $ip
done
One-line form:
Dudji@htb[/htb]$ for ip in 10.10.10.170 10.10.10.174;do ping -c 1 $ip;done
While Loops
while loops need a counter or changing condition so they do not run forever.
stat=1
while [ $stat -eq 1 ]
do
ping -c 2 $host > /dev/null 2>&1
if [ $? -eq 0 ]
then
echo "$host is up."
((stat--))
((hosts_up++))
((hosts_total++))
else
echo "$host is down."
((stat--))
((hosts_total++))
fi
done
continue skips to the next iteration and break exits the loop.
#!/bin/bash
counter=0
while [ $counter -lt 10 ]
do
((counter++))
echo "Counter: $counter"
if [ $counter == 2 ]
then
continue
elif [ $counter == 4 ]
then
break
fi
done
Until Loops
until is the inverse of while: it runs until the condition becomes true.
#!/bin/bash
counter=0
until [ $counter -eq 10 ]
do
((counter++))
echo "Counter: $counter"
done
Comparison Operators
Bash operators are usually grouped into string, integer, file, and logical
checks.
String Operators
| Operator | Description |
|---|
== | is equal to |
!= | is not equal to |
< | is less than in ASCII order |
> | is greater than in ASCII order |
-z | string is empty |
-n | string is not empty |
Quote strings like "$1" to avoid parsing issues.
if [ "$1" != "HackTheBox" ]
then
echo -e "You need to give 'HackTheBox' as argument."
exit 1
elif [ $# -gt 1 ]
then
echo -e "Too many arguments given."
exit 1
else
echo -e "Success!"
fi
< and > string comparisons work inside [[ ... ]].
Dudji@htb[/htb]$ man ascii
Integer Operators
| Operator | Description |
|---|
-eq | is equal to |
-ne | is not equal to |
-lt | is less than |
-le | is less than or equal to |
-gt | is greater than |
-ge | is greater than or equal to |
if [ $# -lt 1 ]
then
echo -e "Number of given arguments is less than 1"
exit 1
elif [ $# -gt 1 ]
then
echo -e "Number of given arguments is greater than 1"
exit 1
else
echo -e "Number of given arguments equals 1"
fi
File Operators
| Operator | Description |
|---|
-e | file exists |
-f | regular file |
-d | directory |
-L | symbolic link |
-N | modified since last read |
-O | owned by current user |
-G | group matches current user |
-s | size greater than 0 |
-r | readable |
-w | writable |
-x | executable |
if [ -e "$1" ]
then
echo -e "The file exists."
exit 0
else
echo -e "The file does not exist."
exit 2
fi
Boolean and Logical Operators
Use [[ ... ]] for boolean-style checks.
if [[ -z $1 ]]
then
echo -e "Boolean value: True (is null)"
exit 1
elif [[ $# > 1 ]]
then
echo -e "Boolean value: True (is greater than)"
exit 1
else
echo -e "Boolean value: False (is equal to)"
fi
| Operator | Description |
|---|
! | logical NOT |
&& | logical AND |
|| | logical OR |
if [[ -e "$1" && -r "$1" ]]
then
echo -e "We can read the file that has been specified."
exit 0
elif [[ ! -e "$1" ]]
then
echo -e "The specified file does not exist."
exit 2
elif [[ -e "$1" && ! -r "$1" ]]
then
echo -e "We don't have read permission for this file."
exit 1
else
echo -e "Error occurred."
exit 5
fi
Debugging
Bash debugging is usually done with -x and -v.
bash -x script.sh shows each command as it executes.
bash -x -v script.sh also shows the code as Bash reads it.
Dudji@htb[/htb]$ bash -x CIDR.sh
+ '[' 0 -eq 0 ']'
+ echo -e 'You need to specify the target domain.\n'
You need to specify the target domain.
Dudji@htb[/htb]$ bash -x -v CIDR.sh
#!/bin/bash
if [ $# -eq 0 ]
then
echo -e "You need to specify the target domain.\n"
exit 1
fi