congdong007

Penetration Test、Software Developer

0%

While loops are also fairly common and execute code while an expression is true. While loops have
a simple format and, like if, use the square brackets ([]) for the test:

1
2
3
4
while [ <some test> ]
do
<perform an action>
done

Let’s re-create the previous example with a while loop:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
kali@kali:~$ cat ./while.sh
#!/bin/bash
# while loop example
counter=1
while [ $counter -lt 10 ]
do
echo "10.11.1.$counter"
((counter++))
done
kali@kali:~$ chmod +x ./while.sh
kali@kali:~$ ./while.sh
10.11.1.1
10.11.1.2
10.11.1.3
10.11.1.4
10.11.1.5
10.11.1.6
10.11.1.7
10.11.1.8
10.11.1.9

This is not the output we expected. This is a common mistake called an “off by one” error. In the
example above, we used -lt (less than) instead of -le (less than or equal to), so our counter only got
to nine, not ten as originally intended.

The ((counter++)) line uses the double-parenthesis (( )) construct to perform arithmetic
expansion and evaluation at the same time. In this particular case, we use it to increase our counter
variable by one. Let’s re-write the while loop and try the example again:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
kali@kali:~$ cat ./while2.sh
#!/bin/bash
# while loop example 2
counter=1
while [ $counter -le 10 ]
do
echo "10.11.1.$counter"
((counter++))
done
kali@kali:~$ chmod +x ./while2.sh
kali@kali:~$ ./while2.sh
10.11.1.1
10.11.1.2
10.11.1.3
10.11.1.4
10.11.1.5
10.11.1.6
10.11.1.7
10.11.1.8
10.11.1.9
10.11.1.10

Our while loop is looking much better now.

In computer programming, loops help us with repetitive tasks that we need to run until a certain
criteria is met. Iteration is particularly useful for penetration testers, so we recommend paying very
close attention to this section.
In Bash, the two most predominant loop commands are for and while. We will take a look at both.

For loops are very practical and work very well in Bash one-liners. This type of loop is used to
perform a given set of commands for each of the items in a list. Let’s briefly look at its general
syntax:

1
2
3
4
for var-name in <list>
do
<action to perform>
done

The for loop will take each item in the list (in order), assign that item as the value of the variable varname, perform the given action between do and done, and then go back to the top, grab the next
item in the list, and repeat the steps until the list is exhausted.

Let’s take a look at a more practical example that will quickly print the first 10 IP addresses in the
10.11.1.0/24 subnet:

1
2
3
4
5
6
7
8
9
10
11
kali@kali:~$ for ip in $(seq 1 10); do echo 10.11.1.$ip; done
10.11.1.1
10.11.1.2
10.11.1.3
10.11.1.4
10.11.1.5
10.11.1.6
10.11.1.7
10.11.1.8
10.11.1.9
10.11.1.10

In this Bash one-liner, we used the seq command to print a sequence of numbers, in
this case the numbers one through ten. Each number is then assigned to the ip variable, and then
each IP address is displayed to the screen as the for loop runs multiple times, exiting at the end of
the sequence.
Another way of re-writing the previous for loop involves brace expansion using ranges. Brace
expansion using ranges is written giving the first and last values of the range and can be a sequence
of numbers or characters. This is known as a “sequence expression”:

1
2
3
4
5
6
7
8
9
10
11
kali@kali:~$ for i in {1..10}; do echo 10.11.1.$i;done
10.11.1.1
10.11.1.2
10.11.1.3
10.11.1.4
10.11.1.5
10.11.1.6
10.11.1.7
10.11.1.8
10.11.1.9
10.11.1.10

There is a lot of potential for this type of loop. Displaying IP addresses to the screen may not seem
very useful, but we can use the same loop to run a port scan using nmap (which we discuss in
detail in another module). We can also attempt to use the ping command to see if any of the IP
addresses respond to ICMP echo requests, etc.

Boolean logical operators, like AND (&&) and OR (||) are somewhat mysterious because Bash
uses them in a variety of ways.
One common use is in command lists, which are chains of commands whose flow is controlled by
operators. The “|” (pipe) symbol is a commonly-used operator in a command list and passes the
output of one command to the input of another. Similarly, boolean logical operators execute
commands based on whether a previous command succeeded (or returned True or 0) or failed
(returned False or non-zero).
Let’s take a look at the AND (&&) boolean operator first, which executes a command only if the
previous command succeeds (or returns True or 0):

1
2
3
4
5
6
kali@kali:~$ user2=kali
kali@kali:~$ grep $user2 /etc/passwd && echo "$user2 found!"
kali:x:1000:1000:,,,:/home/kali:/bin/bash
kali found!
kali@kali:~$ user2=bob
kali@kali:~$ grep $user2 /etc/passwd && echo "$user2 found!"

In this example, we first assigned the username we are searching for to the user2 variable. Next,
we use the grep command to check if a certain user is listed in the /etc/passwd file, and if it is,
grep returns True and the echo command is executed. However, when we try searching for a user
that we know does not exist in the /etc/passwd file, our echo command is not executed.

When used in a command list, the OR (||) operator is the opposite of AND (&&); it executes the next
command only if the previous command failed (returned False or non-zero):

1
2
3
4
5
kali@kali:~$ echo $user2
bob
kali@kali:~$ grep $user2 /etc/passwd && echo "$user2 found!" || echo "$user2 not found
!"
bob not found!

In the above example, we took our previous command a step further and added the OR (||) operator
followed by a second echo command. Now, when grep does not find a matching line and returns
False, the second echo command after the OR (||) operator is executed instead.
These operators can also be used in a test to compare variables or the results of other tests. When
used this way, AND (&&) combines two simple conditions, and if they are both true, the combined
result is success (or True or 0).
Consider this example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
kali@kali:~$ cat ./and.sh
#/bin/bash
# and example
if [ $USER == 'kali' ] && [ $HOSTNAME == 'kali' ]
then
echo "Multiple statements are true!"
else
echo "Not much to see here..."
fi
kali@kali:~$ chmod +x ./and.sh
kali@kali:~$ ./and.sh
Multiple statements are true!
kali@kali:~$ echo $USER && echo $HOSTNAME
kali
kali

In this example, we used AND (&&) to test multiple conditions and since both variable comparisons
were true, the whole if line succeeded, so the then branch executed.
When used in a test, the OR (||) boolean operator is used to test one or more conditions, but only
one of them has to be true to count as success.
Let’s take a look at an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
kali@kali:~$ cat ./or.sh
#!/bin/bash
# or example
if [ $USER == 'kali' ] || [ $HOSTNAME == 'pwn' ]
then
echo "One condition is true, this line is printed"
else
echo "You are out of luck!"
fi
kali@kali:~$ chmod +x ./or.sh
kali@kali:~$ ./or.sh
One condition is true, this line is printed
kali@kali:~$ echo $USER && echo $HOSTNAME
kali
kali

In this example, we used OR (||) to test multiple conditions and since one of the variable
comparisons was true, the whole if line succeeded, so the then branch executed.

Conditional statements allow us to perform different actions based on different conditions. The
most common conditional Bash statements include if, else, and elif.
The if statement is relatively simple–it checks to see if a condition is true–but it requires a very
specific syntax. Pay careful attention to this syntax, especially the use of required spaces:

1
2
3
4
if [ <some test> ]
then
<perform an action>
fi

In this listing, if “some test” evaluates as true, the script will “perform an action”, or any commands
between then and fi. Let’s look at an actual example:

1
2
3
4
5
6
7
8
9
10
11
12
kali@kali:~$ cat ./if.sh
#!/bin/bash
# if statement example
read -p "What is your age: " age
if [ $age -lt 16 ]
then
echo "You might need parental permission to take this course!"
fi
kali@kali:~$ chmod +x ./if.sh
kali@kali:~$ ./if.sh
What is your age: 15
You might need parental permission to take this course!

In this example, we used an if statement to check the age entered by a user. If the entered age was
less than (-lt) 16, the script would output a warning message.
The square brackets (“[“ and “]”) in the if statement above are actually a reference to the test
command. This simply means we can use all of the operators that are allowed by the test
command. Some of the most common operators include:

Operator Description: Expression True if…
!EXPRESSION The EXPRESSION is false.
-n STRING STRING length is greater than zero
-z STRING The length of STRING is zero (empty)
STRING1 != STRING2 STRING1 is not equal to STRING2
STRING1 = STRING2 STRING1 is equal to STRING2
INTEGER1 -eq INTEGER2 INTEGER1 is equal to INTEGER2
INTEGER1 -ne INTEGER2 INTEGER1 is not equal to INTEGER2
INTEGER1 -gt INTEGER2 INTEGER1 is greater than INTEGER2
INTEGER1 -lt INTEGER2 INTEGER1 is less than INTEGER2
INTEGER1 -ge INTEGER2 INTEGER1 is greater than or equal to INTEGER 2
INTEGER1 -le INTEGER2 INTEGER1 is less than or equal to INTEGER 2
-d FILE FILE exists and is a directory
-e FILE FILE exists
-r FILE FILE exists and has read permission
-s FILE FILE exists and it is not empty
-w FILE FILE exists and has write permission
-x FILE FILE exists and has execute permission

With the above in mind, our previous example using if can be rewritten without square brackets as
follows:

1
2
3
4
5
6
7
8
9
10
11
12
kali@kali:~$ cat ./if2.sh
#!/bin/bash
# if statement example 2
read -p "What is your age: " age
if test $age -lt 16
then
echo "You might need parental permission to take this course!"
fi
kali@kali:~$ chmod +x ./if2.sh
kali@kali:~$ ./if2.sh
What is your age: 15
You might need parental permission to take this course!

Even though this example is functionally equivalent to the example using square brackets, using
square brackets makes the code slightly easier to read.
We can also perform a certain set of actions if a statement is true and another set if it is false. To
do this, we can use the else statement, which has the following syntax:

1
2
3
4
5
6
if [ <some test> ]
then
<perform action>
else
<perform another action>
fi

Let’s extend our previous “age” example to include the else statement:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
kali@kali:~$ cat ./else.sh
#!/bin/bash
# else statement example
read -p "What is your age: " age
if [ $age -lt 16 ]
then
echo "You might need parental permission to take this course!"
else
echo "Welcome to the course!"
fi
kali@kali:~$ chmod +x ./else.sh
kali@kali:~$ ./else.sh
What is your age: 21
Welcome to the course!

Notice that the else statement was executed when the entered age was greater than (or more
specifically “not less than”) sixteen.
The if and else statements only allow two code execution branches. We can add additional
branches with the elif statement which uses the following pattern:

1
2
3
4
5
6
7
8
9
if [ <some test> ]
then
<perform action>
elif [ <some test> ]
then
<perform different action>
else
<perform yet another different action>
fi

Let’s again extend our “age” example to include the elif statement:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
kali@kali:~$ cat ./elif.sh
#!/bin/bash
# elif example
read -p "What is your age: " age
if [ $age -lt 16 ]
then
echo "You might need parental permission to take this course!"
elif [ $age -gt 60 ]
then
echo "Hats off to you, respect!"
else
echo "Welcome to the course!"
fi
kali@kali:~$ chmod +x ./elif.sh
kali@kali:~$ ./elif.sh
What is your age: 65
Hats off to you, respect!

In this example, the code execution flow was slightly more complex. In order of operation, the then
branch executes if the entered age is less than sixteen, the elif branch is entered (and the “Hats
off..” message displayed) if the age is greater than sixty, and the else branch executes only if the
age is greater than sixteen but less than sixty.

Command-line arguments are a form of user input, but we can also capture interactive user input
while a script is running with the read command. In this example, we will use read to capture user
input and assign it to a variable:

1
2
3
4
5
6
7
8
9
10
kali@kali:~$ cat ./input.sh
#!/bin/bash
echo "Hello there, would you like to learn how to hack: Y/N?"
read answer
echo "Your answer was $answer"
kali@kali:~$ chmod +x ./input.sh
kali@kali:~$ ./input.sh
Hello there, would you like to learn how to hack: Y/N?
Y
Your answer was Y

We can alter the behavior of the read command with various command line options. Two of the
most commonly used options include -p, which allows us to specify a prompt, and -s, which makes
the user input silent. The latter is ideal for capturing user credentials:

1
2
3
4
5
6
7
8
9
10
11
kali@kali:~$ cat ./input2.sh
#!/bin/bash
# Prompt the user for credentials
read -p 'Username: ' username
read -sp 'Password: ' password
echo "Thanks, your creds are as follows: " $username " and " $password
kali@kali:~$ chmod +x ./input2.sh
kali@kali:~$ ./input2.sh
Username: kali
Password:
Thanks, your creds are as follows: kali and nothing2see!

Not all Bash scripts require arguments. However, it is extremely important to understand how
they are interpreted by Bash and how to use them. We have already executed Linux commands
with arguments. For example, when we run the command ls -l /var/log, both -l and /var/log
are arguments to the ls command.
Bash scripts are no different; we can supply command-line arguments and use them in our scripts:

1
2
3
4
5
6
kali@kali:~$ cat ./arg.sh
#!/bin/bash
echo "The first two arguments are $1 and $2"
kali@kali:~$ chmod +x ./arg.sh
kali@kali:~$ ./arg.sh hello there
The first two arguments are hello and there

Here we created a simple Bash script, set executable permissions on it, and then ran it
with two arguments. The $1 and $2 variables represent the first and second arguments passed to
the script. Let’s explore a few special Bash variables:

Variable Name Description
$0 The name of the Bash script
$1 - $9 The first 9 arguments to the Bash script
$# Number of arguments passed to the Bash script
$@ All arguments passed to the Bash script
$? The exit status of the most recently run process
$$ The process ID of the current script
$USER The username of the user running the script
$HOSTNAME The hostname of the machine
$RANDOM A random number
$LINENO The current line number in the script

Some of these special variables can be very useful when debugging a script. For example, we might
be able to obtain the exit status of a command to determine whether it was successfully executed
or not.

Variables are named places to temporarily store data. We can set (or “declare”) a variable, which
assigns a value to it, or read a variable, which will “expand” or “resolve” it to its stored value.

We can declare variable values in a number of ways. The easiest method is to set the value directly
with a simple name=value declaration. Notice that there are no spaces before or after the “=” sign:

1
kali@kali:~$ first_param=Super

Declaring a variable is pointless unless we can reference it. To do this, we precede the variable with
the “$” character. Whenever Bash encounters this syntax in a command, it replaces the variable
name with its value (“expands” the variable) before execution:

1
2
3
4
kali@kali:~$ first_param=Super
kali@kali:~$ last_param=Hero
kali@kali:~$ echo $first_param $last_param
Super Hero

Variable names may be uppercase, lowercase, or a mixture of both. However, Bash is casesensitive so we must be consistent when declaring and expanding variables. In addition, it’s good
practice to use descriptive variable names, which make our scripts much easier to read and
maintain.

Be advised that Bash interprets certain characters in specific ways. For example, this declaration
demonstrates an improper multi-value variable declaration:

1
2
kali@kali:~$ greeting=Hello World
bash: World: command not found

This was not necessarily what we expected. To fix this, we can use either single quotes () or double
quotes () to enclose our text. However, Bash treats single and double quotes differently. When
encountering single quotes, Bash interprets every enclosed character literally. When enclosed in
double quotes, all characters are viewed literally except “$”, “`”, and “" meaning variables will be
expanded in an initial substitution pass on the enclosed text.

A simple example will help clarify this:

1
2
3
4
5
6
kali@kali:~$ greeting='Hello World'
kali@kali:~$ echo $greeting
Hello World
kali@kali:~$ greeting2="New $greeting"
kali@kali:~$ echo $greeting2
New Hello World

In this example, the single-quote-enclosed declaration of greeting preserved the value of our text
exactly and did not interpret the space as a command delimiter. However, in the double-quoteenclosed declaration of greeting2, Bash expanded $greeting to its value (“Hello World”), honoringthe special meaning of the “$” character.
We can also set the value of the variable to the result of a command or program. This is known as
command substitution, which allows us to take the output of a command or program (what
would normally be printed to the screen) and have it saved as the value of a variable.
To do this, place the variable name in parentheses “()”, preceded by a “$” character:

1
2
3
kali@kali:~$ user=$(whoami)
kali@kali:~$ echo $user
kali

Here we assigned the output of the whoami command to the user variable. We then
displayed its value. An alternative syntax for command substitution using the backtick, or grave,
character (`) is shown below:

1
2
3
kali@kali:~$ user2=`whoami`
kali@kali:~$ echo $user2
kali

The backtick method is older and typically discouraged as there are differences in how the two
methods of command substitution behave. It is also important to note that command
substitution happens in a subshell and changes to variables in the subshell will not alter variables
from the master process. This is demonstrated in the following example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
kali@kali:~$ cat ./subshell.sh
#!/bin/bash -x
var1=value1
echo $var1
var2=value2
echo $var2
$(var1=newvar1)
echo $var1
`var2=newvar2`
echo $var2
kali@kali:~$ ./subshell.sh
+ var1=value1
+ echo value1
value1
+ var2=value2
+ echo value2
value2
++ var1=newvar1
+ echo value1
value1
++ var2=newvar2
+ echo value2
value2
kali@kali:~$

In this example, first note that we changed the shebang, adding in the -x flag. This instructed Bash
to print additional debug output, so we could more easily see the commands that were executed
and their results. As we view this output, notice that commands preceded with a single “+” character
were executed in the current shell and commands preceded with a double “++” were executed in a
subshell.
This allows us to clearly see that the second declarations of var1 and var2 happened inside a
subshell and did not change the values in the current shell as the initial declarations did.

The GNU Bourne-Again Shell (Bash) is a powerful work environment and scripting engine. A
competent security professional skillfully leverages Bash scripting to streamline and automate
many Linux tasks and procedures. In this module, we will introduce Bash scripting and explore
several practical scenarios.

A Bash script is a plain-text file that contains a series of commands that are executed as if they had
been typed at a terminal prompt. Generally speaking, Bash scripts have an optional extension of
.sh (for ease of identification), begin with #!/bin/bash and must have executable permissions set
before they can be executed. Let’s begin with a simple “Hello World” Bash script, file named hello-world.sh:

1
2
3
#!/bin/bash
# Hello World Bash Script
echo "Hello World!"

This script has several components worth explaining:
• Line 1: #! is commonly known as the shebang, and is ignored by the Bash interpreter. The
second part, /bin/bash, is the absolute path to the interpreter, which is used to run the
script. This is what makes this a “Bash script” as opposed to another type of shell script, like
a “C Shell script”, for example.
• Line 2: # is used to add a comment, so all text that follows it is ignored.
• Line 3: echo “Hello World!” uses the echo Linux command utility to print a given string to the
terminal, which in this case is “Hello World!”.

Next, let’s make the script executable and run it:

1
2
3
kali@kali:~$ chmod +x hello-world.sh
kali@kali:~$ ./hello-world.sh
Hello World!

The chmod command, along with the +x option is used to make the script executable.
./helloworld.sh is used to actually run it.
The ./ notation may seem confusing but this is simply a path
notation indicating that this script is in the current directory. Whenever we type a command, Bash
tries to find it in a series of directories stored in a variable called PATH. Since our home directory
is not included in that variable, we must use the relative path to our Bash script in order for Bash
to “find it” and run it.

Now that we have created our first Bash script,next we will explore Bash in a bit more detail.

Due to the requirements of my work, I needed to design an open software system, and immediately thought of the Python language. As a result, I designed a simple and easily extensible plugin system.

Here’s the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#main.py


import importlib

class PluginManager:
def __init__(self):
self.plugins = []
self.func_name = "MyPlugin"

def load_plugin(self, plugin_name):
try:
plugin_module = importlib.import_module(f'plugins.{plugin_name}')
plugin_instance = getattr(plugin_module, self.func_name)()
self.plugins.append(plugin_instance)
print(f"plugin {plugin_name} loaded.")
except ImportError:
print(f"Load plugin {plugin_name} failed.")

def run_plugins(self):
for plugin in self.plugins:
plugin.run()


if __name__ == "__main__":
manager = PluginManager()

while True:
print("1. Add Plugin")
print("2. Run Plugin")
print("3. Quit")
choice = input("Your Select: ")

if choice == "1":
plugin_name = input("Please input the name of plugin: ")
manager.load_plugin(plugin_name)

elif choice == "2":
manager.run_plugins()

elif choice == "3":
break

Then, you create a directory named ‘plugins.’ In this directory, you can add your plugin, which must define a class named ‘MyPlugin’ and must have a function named ‘run’. Here is the simple code:

1
2
3
4
5
6
7
#my_plugin

class MyPlugin:
def __init__(self):
pass
def run(self):
print("MyPlugin is running")

The following is the running process:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1. Add Plugin
2. Run Plugin
3. Quit
Your Select: 1
Please input the name of plugin: my_plugin
plugin my_plugin loaded.
1. Add Plugin
2. Run Plugin
3. Quit
Your Select: 2
MyPlugin is running
1. Add Plugin
2. Run Plugin
3. Quit
Your Select: 3
PS C:\Users\xxx\Desktop\yyy\plugin-system>

You can use this model to design a big and an opened software system.

Before you start learning shellcode development, please install NASM on your Linux system. Here’s the shellcode code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
section .text

global _start

_start:

jmp short callShellcode

shellcode:

pop esi ; Pop '/bin/sh' from the stack into esi
xor eax, eax ; Set eax to NULL
mov byte [esi + 7], al ; Null-terminate '/bin/sh' using the low byte of eax
lea ebx, [esi] ; Load the address of '/bin/sh' into ebx
mov dword [esi + 8], ebx
lea ecx, [esi + 8] ; Load the address of the argv array into ecx
mov dword [esi + 12], eax
lea edx, [esi + 12] ; Load the address of the NULL terminator into edx
mov al, 0x0b ; Set al to 0x0b, the system call number for execve
int 0x80 ; Trigger the syscall

callShellcode:

call shellcode
db '/bin/sh'

After saving this code to a file named “shellx.asm,” you need to compile it using NASM to obtain the hexadecimal representation of the code. Use the following commands:

1
2
nasm -f elf shellx.asm
ld -o shellx shellx.o -m elf_i386

This will generate a “shellx” file. However, it’s not executable yet. You’ll need to use the “objdump” command to extract the hexadecimal code. You can use a Bash script like this:

1
2
3
4
for i in $(objdump -d "$1" | tr '\t' ' ' | tr ' ' '\n' | egrep '^[0-9a-f]{2}$'); do
echo -n "\x$i"
done
echo -e "\n"

To validate your assembly code, you’ll need a C program as follows:

1
2
3
4
5
6
7
char code[] = "\xeb\x18\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x8d\x4e\x08\x89\x46\x0c\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68";

int main(int argc, char **argv) {
int (*func)();
func = (int (*)()) code;
(int)(*func)();
}

Compile this code with the following command:

1
gcc -g -o shellcodetest shellcodetest.c -m32 -z execstack

This will produce an executable, “shellcodetest.” When you run it, you’ll get a shell with root privileges:

1
2
3
4
5
6
7
8
root@kali:/home/kali/shellCode_train# ./shellcodetest
# ls
exit.asm hello shellcodetest shellx.asm
exit.o hello.asm shellcodetest.c shellx.o
getshellcode.sh hello.o shellx
# whoami
root
#