- The Guide to Finding and Reporting Web Vulnerabilities
- About the Author
- About the Tech Reviewer
- Foreword
- Introduction
- Who This Book Is For
- What Is In This Book
- Happy Hacking!
- 1 Picking a Bug Bounty Program
- 2 Sustaining Your Success
- 3 How the Internet Works
- 4 Environmental Setup and Traffic Interception
- 5 Web Hacking Reconnaissance
- 6 Cross-Site Scripting
- 7 Open Redirects
- 8 Clickjacking
- 9 Cross-Site Request Forgery
- 10 Insecure Direct Object References
- 11 SQL Injection
- 12 Race Conditions
- 13 Server-Side Request Forgery
- 14 Insecure Deserialization
- 15 XML External Entity
- 16 Template Injection
- 17 Application Logic Errors and Broken Access Control
- 18 Remote Code Execution
- 19 Same-Origin Policy Vulnerabilities
- 20 Single-Sign-On Security Issues
- 21 Information Disclosure
- 22 Conducting Code Reviews
- 23 Hacking Android Apps
- 24 API Hacking
- 25 Automatic Vulnerability Discovery Using Fuzzers
Writing Your Own Recon Scripts
You’ve probably realized by now that good recon is an extensive process. But it doesn’t have to be time-consuming or hard to manage. We’ve already discussed several tools that use the power of automation to make the process easier.
你可能已经意识到了,良好的侦察是一个复杂的过程。但是它并不一定需要耗费大量的时间或难以管理。我们已经讨论了几种利用自动化技术来简化这一过程的工具。
Sometimes you may find it handy to write your own scripts. A script is a list of commands designed to be executed by a program. They’re used to automate tasks such as data analysis, web-page generation, and system administration. For us bug bounty hunters, scripting is a way of quickly and efficiently performing recon, testing, and exploitation. For example, you could write a script to scan a target for new subdomains, or enumerate potentially sensitive files and directories on a server. Once you’ve learned how to script, the possibilities are endless.
有时候你可能会发现编写自己的脚本很方便。脚本是一组命令的列表,旨在通过程序执行。它们用于自动化数据分析、网页生成和系统管理等任务。对于我们这些漏洞赏金猎人来说,脚本是一种快速高效地执行侦察、测试和利用的方式。例如,你可以编写一个脚本来扫描目标找出新的子域名,或者在服务器上枚举潜在的敏感文件和目录。一旦你学会了如何编写脚本,可能性就是无限的。
This section covers bash scripts in particular—what they are and why you should use them. You’ll learn how to use bash to simplify your recon process and even write your own tools. I’ll assume that you have basic knowledge of how programming languages work, including variables, conditionals, loops, and functions, so if you’re not familiar with these concepts, please take an introduction to coding class at Codecademy ( https://www.codecademy.com/ ) or read a programming book.
本节特别介绍 bash 脚本,包括其定义和使用的原因。您将学习如何使用 bash 简化您的侦察过程,甚至编写自己的工具。我假定您对编程语言的基本知识有所了解,包括变量、条件、循环和函数,如果您对这些概念不熟悉,请参加 Codecademy(https://www.codecademy.com/)的编程入门课程或阅读编程书籍。
Bash scripts, or any type of shell script, are useful for managing complexities and automating recurrent tasks. If your commands involve multiple input parameters, or if the input of one command depends on the output of another, entering it all manually could get complicated quickly and increase the chance of a programming mistake. On the other hand, you might have a list of commands that you want to execute many, many times. Scripts are useful here, as they save you the trouble of typing the same commands over and over again. Just run the script each time and be done with it.
Bash 脚本或任何类型的 shell 脚本,都有助于管理复杂性并自动化重复任务。如果您的命令涉及多个输入参数,或者一个命令的输入取决于另一个命令的输出,手动输入这些内容可能会很复杂,增加编程错误的机会。另一方面,您可能有一系列命令需要执行很多次。在这种情况下,脚本非常有用,因为它们可以让您省去反复输入相同命令的麻烦。只需每次运行脚本即可完成任务。
Understanding Bash Scripting Basics
Let’s write our first script. Open any text editor to follow along. The first line of every shell script you write should be the shebang line . It starts with a hash mark ( #
) and an exclamation mark ( !
), and it declares the interpreter to use for the script. This allows the plaintext file to be executed like a binary. We’ll use it to indicate that we’re using bash.
让我们写我们的第一个脚本。打开任何文本编辑器进行跟随。你写的每个 shell 脚本的第一行应该是 shebang 行。它以井号 (#) 和感叹号 (!) 开头,并声明要用于脚本的解释器。这允许纯文本文件像二进制文件一样被执行。我们将使用它来表示我们正在使用 bash。
Let’s say we want to write a script that executes two commands; it should run Nmap and then Dirsearch on a target. We can put the commands in the script like this:
假设我们想编写一个脚本,执行两个命令;它应该在目标上运行 Nmap,然后在其中运行 Dirsearch。我们可以像这样将命令放入脚本中:
#!/bin/bash
nmap scanme.nmap.org
/PATH/TO/dirsearch.py -u scanme.nmap.org -e php
This script isn’t very useful; it can scan only one site, scanme.nmap.org . Instead, we should let users provide input arguments to the bash script so they can choose the site to scan. In bash syntax, $1
represents the first argument passed in, $2
is the second argument, and so on. Also, $@
represents all arguments passed in, while $#
represents the total number of arguments. Let’s allow users to specify their targets with the first input argument, assigned to the variable $1
:
这个脚本并不是非常有用,它只能扫描一个网站,scanme.nmap.org。相反,我们应该让用户提供输入参数来控制 bash 脚本,这样他们就可以选择要扫描的网站。在 bash 语法中,$1 表示传递的第一个参数,$2 为第二个参数,以此类推。同时,$@ 表示所有传入的参数,而 $# 表示参数的总数。让我们允许用户使用第一个输入参数来指定他们的目标,并将其分配给变量 $1:
#!/bin/bash
nmap $1
/PATH/TO/dirsearch.py -u $1 -e php
Now the commands will execute for whatever domain the user passes in as the first argument.
现在,命令将针对用户作为第一个参数传递的任何域执行。
Notice that the third line of the script includes /PATH/TO/dirsearch.py . You should replace /PATH/TO/ with the absolute path of the directory where you stored the Dirsearch script. If you don’t specify its location, your computer will try to look for it in the current directory, and unless you stored the Dirsearch file in the same directory as your shell script, bash won’t find it.
请注意脚本的第三行包括 /PATH/TO/dirsearch.py。你应该用 Dirsearch 脚本所在的目录的绝对路径替换 /PATH/TO/。如果你不指定其位置,你的计算机将尝试在当前目录中寻找它,除非你将 Dirsearch 文件存储在与你的 shell 脚本相同的目录中,否则 bash 将找不到它。
Another way of making sure that your script can find the commands to use is through the PATH
variable, an environmental variable in Unix systems that specifies where executable binaries are found. If you run this command to add Dirsearch’s directory to your PATH
, you can run the tool from anywhere without needing to specify its absolute path:
你可以通过 PATH 变量来确保你的脚本可以找到要使用的命令。在 Unix 系统中,它是一个环境变量,指定了可执行二进制文件的位置。如果你运行这个命令将 Dirsearch 的目录添加到 PATH 中,你就可以在任何地方运行该工具,而无需指定其绝对路径。
export PATH="PATH_TO_DIRSEARCH:$PATH"
After executing this command, you should be able to use Dirsearch directly:
执行此命令后,您应该能够直接使用 Dirsearch:
#!/bin/bash
nmap $1
dirsearch.py -u $1 -e php
Note that you will have to run the export
command again after you restart your terminal for your PATH
to contain the path to Dirsearch. If you don’t want to export PATH
over and over again, you can add the export
command to your ~/ .bash_profile file, a file that stores your bash preferences and configuration. You can do this by opening ~/.bash_profile with your favorite text editor and adding the export
command to the bottom of the file.
请注意,重启终端后,您必须再次运行导出命令,以便您的 PATH 包含 Dirsearch 路径。如果您不想一遍又一遍地导出 PATH,您可以将导出命令添加到~/.bash_profile 文件中,这是一个存储 bash 偏好和配置的文件。您可以通过用您喜欢的文本编辑器打开~/.bash_profile,并将导出命令添加到文件底部来实现这一点。
The script is complete! Save it in your current directory with the filename recon.sh . The .sh extension is the conventional extension for shell scripts. Make sure your terminal’s working directory is the same as the one where you’ve stored your script by running the command cd /
location /
of /
your /
script . Execute the script in the terminal with this command:
脚本完成了!请将其保存在当前目录中,文件名为 recon.sh。 .sh 扩展名是 shell 脚本的传统扩展名。确保您终端的工作目录与存储脚本的目录相同,方法是运行命令 cd /location/of/your/script。执行此命令在终端中运行脚本:
$ ./recon.sh
You might see a message like this:
“您可能会看到这样的消息:” (Note: This is the translated content in Simplified Chinese)
permission denied: ./recon.sh
This is because the current user doesn’t have permission to execute the script. For security purposes, most files aren’t executable by default. You can correct this behavior by adding executing rights for everyone by running this command in the terminal:
这是因为当前用户没有执行脚本的权限。出于安全考虑,大多数文件默认情况下都不可执行。您可以通过在终端中运行此命令为所有人添加执行权限来更正此行为:
$ chmod +x recon.sh
The chmod
command edits the permissions for a file, and +x
indicates that we want to add the permission to execute for all users. If you’d like to grant executing rights for the owner of the script only, use this command instead:
"chmod 命令用于编辑文件的权限,+x 表示我们希望为所有用户添加执行权限。如果您只想授予脚本所有者执行权限,请改用此命令:" “chmod 命令编辑文件权限,+x 表示我们想要为所有用户添加执行权限。如果您只想授予脚本所有者执行权限,请使用此命令:”
$ chmod 700 recon.sh
Now run the script as we did before. Try passing in scanme.nmap.org as the first argument. You should see the output of the Nmap and Dirsearch printed out:
现在像之前一样运行脚本。尝试将 scanme.nmap.org 作为第一个参数传递。你应该会看到 Nmap 和 Dirsearch 的输出被打印出来:
$ ./recon.sh scanme.nmap.org
Starting Nmap 7.60 ( https://nmap.org )
Nmap scan report for scanme.nmap.org (45.33.32.156)
Host is up (0.062s latency).
Other addresses for scanme.nmap.org (not scanned): 2600:3c01::f03c:91ff:fe18:bb2f
Not shown: 992 closed ports
PORT STATE SERVICE
22/tcp open ssh
25/tcp filtered smtp
80/tcp open http
135/tcp filtered msrpc
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds
9929/tcp open nping-echo
31337/tcp open Elite
Nmap done: 1 IP address (1 host up) scanned in 2.16 seconds
Extensions: php | HTTP method: get | Threads: 10 | Wordlist size: 6023
Error Log: /Users/vickieli/tools/dirsearch/logs/errors.log
Target: scanme.nmap.org
[11:14:30] Starting:
[11:14:32] 403 - 295B - /.htaccessOLD2
[11:14:32] 403 - 294B - /.htaccessOLD
[11:14:33] 301 - 316B - /.svn -> http://scanme.nmap.org/.svn/
[11:14:33] 403 - 298B - /.svn/all-wcprops
[11:14:33] 403 - 294B - /.svn/entries
[11:14:33] 403 - 297B - /.svn/prop-base/
[11:14:33] 403 - 296B - /.svn/pristine/
[11:14:33] 403 - 315B - /.svn/text-base/index.php.svn-base
[11:14:33] 403 - 297B - /.svn/text-base/
[11:14:33] 403 - 293B - /.svn/props/
[11:14:33] 403 - 291B - /.svn/tmp/
[11:14:55] 301 - 318B - /images -> http://scanme.nmap.org/images/
[11:14:56] 200 - 7KB - /index
[11:14:56] 200 - 7KB - /index.html
[11:15:08] 403 - 296B - /server-status/
[11:15:08] 403 - 295B - /server-status
[11:15:08] 301 - 318B - /shared -> http://scanme.nmap.org/shared/
Task Completed
Saving Tool Output to a File
To analyze the recon results later, you may want to save your scripts’ output in a separate file. This is where input and output redirection come into play. Input redirection is using the content of a file, or the output of another program, as the input to your script. Output redirection is redirecting the output of a program to another location, such as to a file or another program. Here are some of the most useful redirection operators:
为了以后分析重新构建的结果,您可能希望将脚本输出保存在单独的文件中。这就是输入和输出重定向发挥作用的地方。输入重定向是使用文件的内容或另一个程序的输出作为脚本的输入。输出重定向是将程序的输出重定向到另一个位置,如文件或另一个程序。以下是一些最有用的重定向运算符:
- PROGRAM
>
FILENAME Writes the program’s output into the file with that name. (It will clear any content from the file first. It will also create the file if the file does not already exist.) - PROGRAM
>>
FILENAME Appends the output of the program to the end of the file, without clearing the file’s original content. - PROGRAM
<
FILENAME Reads from the file and uses its content as the program input. - PROGRAM1
|
PROGRAM2 Uses the output of PROGRAM1 as the input to PROGRAM2 .
We could, for example, write the results of the Nmap and Dirsearch scans into different files:
例如,我们可以将 Nmap 和 Dirsearch 扫描的结果写入不同的文件中:
#!/bin/bash
echo "Creating directory $1_recon." 1
mkdir $1_recon 2
nmap $1 > $1_recon/nmap 3
echo "The results of nmap scan are stored in $1_recon/nmap."
/PATH/TO/dirsearch.py -u $1 -e php 4 --simple-report=$1_recon/dirsearch
echo "The results of dirsearch scan are stored in $1_recon/dirsearch."
The echo
command 1 prints a message to the terminal. Next, mkdir
creates a directory with the name DOMAIN_recon 2 . We store the results of nmap
into a file named nmap in the newly created directory 3 . Dirsearch’s simple-report
flag 4 generates a report in the designated location. We store the results of Dirsearch to a file named dirsearch in the new directory.
echo 命令 1 将消息打印到终端。接下来,mkdir 创建名为 DOMAIN_recon 2 的目录。我们将 nmap 的结果存储到新创建的目录中的名为 nmap 的文件中 3。Dirsearch 的 simple-report 标志 4 在指定位置生成报告。我们将 Dirsearch 的结果存储到新目录中名为 dirsearch 的文件中。
You can make your script more manageable by introducing variables to reference files, names, and values. Variables in bash can be assigned using the following syntax: VARIABLE_NAME =
VARIABLE_VALUE . Note that there should be no spaces around the equal sign. The syntax for referencing variables is $
VARIABLE_NAME . Let’s implement these into the script:
你可以通过引入变量来使你的脚本更易于管理,以便引用文件、名称和值。Bash 中的变量可以使用以下语法进行分配:VARIABLE_NAME=VARIABLE_VALUE。请注意,等号周围不应该有空格。引用变量的语法是$VARIABLE_NAME。让我们将这些引入脚本中:
#!/bin/bash
PATH_TO_DIRSEARCH="/Users/vickieli/tools/dirsearch"
DOMAIN=$1
DIRECTORY=${DOMAIN}_recon 1
echo "Creating directory $DIRECTORY."
mkdir $DIRECTORY
nmap $DOMAIN > $DIRECTORY/nmap
echo "The results of nmap scan are stored in $DIRECTORY/nmap."
$PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php –simple-report=$DIRECTORY/dirsearch 2
echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
We use ${DOMAIN}_recon
instead of $DOMAIN_recon
1 because, otherwise, bash would recognize the entirety of DOMAIN_recon
as the variable name. The curly brackets tell bash that DOMAIN
is the variable name, and _recon
is the plaintext we’re appending to it. Notice that we also stored the path to Dirsearch in a variable to make it easy to change in the future 2 .
我们使用${DOMAIN}_recon 代替$DOMAIN_recon 1,否则,bash 会将 DOMAIN_recon 的全部识别为变量名。花括号告诉 bashDOMAIN 是变量名,_recon 是我们要添加的明文。请注意,我们还将 Dirsearch 的路径存储在变量中,以便将来轻松更改。
Using redirection, you can now write shell scripts that run many tools in a single command and save their outputs in separate files.
使用重定向,你现在可以编写 Shell 脚本,在一个命令中执行多个工具,并将它们的输出保存在不同的文件中。
Adding the Date of the Scan to the Output
Let’s say you want to add the current date to your script’s output, or select which scans to run, instead of always running both Nmap and Dirsearch. If you want to write tools with more functionalities like this, you have to understand some advanced shell scripting concepts.
假设你想要将当前日期添加到你的脚本输出中,或者选择运行哪个扫描工具,而不是总是运行 Nmap 和 Dirsearch。如果你想要编写更多功能的工具,就必须理解一些高级的 shell 脚本概念。
For example, a useful one is command substitution , or operating on the output of a command. Using $()
tells Unix to execute the command surrounded by the parentheses and assign its output to the value of a variable. Let’s practice using this syntax:
例如,一个有用的语法是命令替换,或者操作命令的输出。使用$() 告诉 Unix 执行括号内的命令并将其输出赋值给一个变量的值。让我们练习使用这种语法:
#!/bin/bash
PATH_TO_DIRSEARCH="/Users/vickieli/tools/dirsearch"
TODAY=$(date) 1
echo "This scan was created on $TODAY" 2
DOMAIN=$1
DIRECTORY=${DOMAIN}_recon
echo "Creating directory $DIRECTORY."
mkdir $DIRECTORY
nmap $DOMAIN > $DIRECTORY/nmap
echo "The results of nmap scan are stored in $DIRECTORY/nmap."
$PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php --simple-report=$DIRECTORY/dirsearch
echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
At 1 , we assign the output of the date
command to the variable TODAY
. The date
command displays the current date and time. This lets us output a message indicating the day on which we performed the scan 2 .
在第 1 步,我们为变量 TODAY 分配 date 命令的输出。date 命令显示当前日期和时间。这样,我们可以输出一个消息,指示执行扫描的日期。
Adding Options to Choose the Tools to Run
Now, to selectively run only certain tools, you need to use conditionals. In bash, the syntax of an if
statement is as follows. Note that the conditional statement ends with the fi
keyword, which is if
backward:
现在,要选择性地运行某些工具,您需要使用条件语句。在 Bash 中,if 语句的语法如下所示。请注意,条件语句以 if 关键字结尾,该关键字是 if 的反向形式:fi。
if [ condition 1 ]
then
# Do if condition 1 is satisfied
elif [ condition 2 ]
then
# Do if condition 2 is satisfied, and condition 1 is not satisfied
else
# Do something else if neither condition is satisfied
fi
Let’s say that we want users to be able to specify the scan MODE
, as such:
让我们假设我们希望用户能够指定扫描模式:
$ ./recon.sh scanmme.nmap.org MODE
We can implement this functionality like this:
我们可以这样实现此功能:
#!/bin/bash
PATH_TO_DIRSEARCH="/Users/vickieli/tools/dirsearch"
TODAY=$(date)
echo "This scan was created on $TODAY"
DIRECTORY=${DOMAIN}_recon
echo "Creating directory $DIRECTORY."
mkdir $DIRECTORY
if [ $2 == "nmap-only" ] 1
then
nmap $DOMAIN > $DIRECTORY/nmap 2
echo "The results of nmap scan are stored in $DIRECTORY/nmap."
elif [ $2 == "dirsearch-only" ] 3
then
$PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php –simple-report=$DIRECTORY/dirsearch 4
echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
else 5
nmap $DOMAIN > $DIRECTORY/nmap 6
echo "The results of nmap scan are stored in $DIRECTORY/nmap."
$PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php --simple-report=$DIRECTORY/dirsearch
echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
fi
If the user specifies nmap-only
1 , we run nmap
only and store the results to a file named nmap 2 . If the user specifies dirsearch-only
3 , we execute and store the results of Dirsearch only 4 . If the user specifies neither 5 , we run both scans 6 .
如果用户指定了只运行 nmap (nmap-only 1),我们只运行 nmap 并将结果存入名为 nmap 的文件中 (nmap 2)。如果用户指定了只运行 Dirsearch (dirsearch-only 3),我们只执行并存储 Dirsearch 的结果 (4)。如果用户没有指定 (5),我们将运行两个扫描 (6)。
Now you can make your tool run only the Nmap or Dirsearch commands by specifying one of these in the command:
现在您可以通过在命令中指定其中之一来仅运行 Nmap 或 Dirsearch 命令:
$ ./recon.sh scanme.nmap.org nmap-only
$ ./recon.sh scanme.nmap.org dirsearch-only
Running Additional Tools
What if you want the option of retrieving information from the crt.sh tool, as well? For example, you want to switch between these three modes or run all three recon tools at once:
如果您想要从 crt.sh 工具中检索信息,该怎么办?例如,您想在这三种模式之间切换或同时运行所有这三个 recon 工具。
$ ./recon.sh scanme.nmap.org nmap-only
$ ./recon.sh scanme.nmap.org dirsearch-only
$ ./recon.sh scanme.nmap.org crt-only
We could rewrite the if-else
statements to work with three options: first, we check if MODE
is nmap-only
. Then we check if MODE
is dirsearch-only
, and finally if MODE
is crt-only
. But that’s a lot of if-else
statements, making the code complicated.
我们可以将 if-else 语句重写为三个选项:首先,我们检查 MODE 是否仅限于 nmap。然后,我们检查 MODE 是否仅限于 dirsearch,最后检查 MODE 是否仅限于 crt。但这会造成很多 if-else 语句,让代码变得复杂。
Instead, let’s use bash’s case
statements, which allow you to match several values against one variable without going through a long list of if-else
statements. The syntax of case
statements looks like this. Note that the statement ends with esac
, or case
backward:
使用 bash 的 case 语句,而不是通过长长的 if-else 语句列表来匹配多个值与一个变量。case 语句的语法如下。请注意,语句最后以 esac 或 case 后退结束。
case $VARIABLE_NAME in
case1)
Do something
;;
case2)
Do something
;;
caseN)
Do something
;;
*)
Default case, this case is executed if no other case matches.
;;
esac
We can improve our script by implementing the functionality with case
statements instead of multiple if-else
statements:
我们可以通过使用 case 语句而不是多个 if-else 语句来实现功能,从而改进我们的脚本。
#!/bin/bash
PATH_TO_DIRSEARCH="/Users/vickieli/tools/dirsearch"
TODAY=$(date)
echo "This scan was created on $TODAY"
DOMAIN=$1
DIRECTORY=${DOMAIN}_recon
echo "Creating directory $DIRECTORY."
mkdir $DIRECTORY
case $2 in
nmap-only)
nmap $DOMAIN > $DIRECTORY/nmap
echo "The results of nmap scan are stored in $DIRECTORY/nmap."
;;
dirsearch-only)
$PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php --simple-report=$DIRECTORY/dirsearch
echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
;;
crt-only)
curl "https://crt.sh/?q=$DOMAIN&output=json" -o $DIRECTORY/crt 1
echo "The results of cert parsing is stored in $DIRECTORY/crt."
;;
*)
nmap $DOMAIN > $DIRECTORY/nmap
echo "The results of nmap scan are stored in $DIRECTORY/nmap."
$PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php --simple-report=$DIRECTORY/dirsearch
echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
curl "https://crt.sh/?q=$DOMAIN&output=json" -o $DIRECTORY/crt
echo "The results of cert parsing is stored in $DIRECTORY/crt."
;;
esac
The curl
command 1 downloads the content of a page. We use it here to download data from crt.sh. And curl
’s -o
option lets you specify an output file. But notice that our code has a lot of repetition! The sections of code that run each type of scan repeat twice. Let’s try to reduce the repetition by using functions. The syntax of a bash function looks like this:
curl 命令 1 下载页面的内容。我们在这里使用它来从 crt.sh 下载数据。curl 的-o 选项可以让您指定输出文件。但请注意我们的代码有很多重复!运行每种扫描类型的代码段重复两次。让我们尝试通过使用函数来减少重复。Bash 函数的语法如下:
FUNCTION_NAME()
{
DO_SOMETHING
}
After you’ve declared a function, you can call it like any other shell command within the script. Let’s add functions to the script:
在声明函数之后,你可以像脚本中的任何其他 shell 命令一样调用它。让我们向脚本中添加函数: 在脚本中添加函数后,你可以像普通的 shell 命令一样调用它。
#!/bin/bash
PATH_TO_DIRSEARCH="/Users/vickieli/tools/dirsearch"
TODAY=$(date)
echo "This scan was created on $TODAY"
DOMAIN=$1
DIRECTORY=${DOMAIN}_recon
echo "Creating directory $DIRECTORY."
mkdir $DIRECTORY
nmap_scan() 1
{
nmap $DOMAIN > $DIRECTORY/nmap
echo "The results of nmap scan are stored in $DIRECTORY/nmap."
}
dirsearch_scan() 2
{
$PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php --simple-report=$DIRECTORY/dirsearch
echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
}
crt_scan() 3
{
curl "https://crt.sh/?q=$DOMAIN&output=json" -o $DIRECTORY/crt
echo "The results of cert parsing is stored in $DIRECTORY/crt."
}
case $2 in 4
nmap-only)
nmap_scan
;;
dirsearch-only)
dirsearch_scan
;;
crt-only)
crt_scan
;;
*)
nmap_scan
dirsearch_scan
crt_scan
;;
esac
You can see that we’ve simplified our code. We created three functions, nmap_scan
1 , dirsearch_scan
2 , and crt_scan
3 . We put the scan
and echo
commands in these functions so we can call them repeatedly without writing the same code over and over 4 . This simplification might not seem like much here, but reusing code with functions will save you a lot of headaches when you write more complex programs.
你可以看到我们已经简化了我们的代码。我们创建了三个函数,nmap_scan 1,dirsearch_scan 2 和 crt_scan 3。我们将扫描和回显命令放入这些函数中,因此我们可以重复调用它们,而无需一遍又一遍地编写相同的代码 4。尽管在这里简化可能不会有太大影响,但在编写更复杂的程序时,重用代码并使用函数将为您节省很多头疼。
Keep in mind that all bash variables are global except for input parameters like $1
, $2
, and $3
. This means that variables like $DOMAIN
, $DIRECTORY
, and $PATH_TO_DIRSEARCH
become available throughout the script after we’ve declared them, even if they’re declared within functions. On the other hand, parameter values like $1
, $2
, and $3
can refer only to the values the function is called with, so you can’t use a script’s input arguments within a function, like this:
请记住,除了输入参数 $1、$2 和$3,所有 Bash 变量都是全局的。这意味着,在声明它们之后,例如 $DOMAIN、$DIRECTORY 和 $PATH_TO_DIRSEARCH 这样的变量在整个脚本中都可以使用,即使它们在函数内声明。另一方面,像 $1、$2 和 $3 这样的参数值只能引用函数被调用时的值,所以你不能在函数内部使用脚本的输入参数,例如:
nmap_scan()
{
nmap $1 > $DIRECTORY/nmap
echo "The results of nmap scan are stored in $DIRECTORY/nmap."
}
nmap_scan
Here, the $1
in the function refers to the first argument that nmap_scan
was called with, not the argument our recon.sh script was called with. Since nmap_scan
wasn’t called with any arguments, $1
is blank.
这里的函数$1 指的是 nmap_scan 被调用时的第一个参数,而不是我们的 recon.sh 脚本被调用时的参数。由于 nmap_scan 没有被任何参数调用,$1 为空。
Parsing the Results
Now we have a tool that performs three types of scans and stores the results into files. But after the scans, we’d still have to manually read and make sense of complex output files. Is there a way to speed up this process too?
现在我们有一个可以执行三种扫描并将结果存储到文件中的工具。但是,在扫描之后,我们仍然需要手动阅读和理解复杂的输出文件。有没有办法也加快这个过程呢?
Let’s say you want to search for a certain piece of information in the output files. You can use Global Regular Expression Print ( grep ) to do that. This command line utility is used to perform searches in text, files, and command outputs. A simple grep
command looks like this:
假设你想在输出文件中搜索某个信息片段。你可以使用全球正则表达式打印(grep)来实现。这个命令行实用程序用于在文本、文件和命令输出中执行搜索。一个简单的 grep 命令如下:
grep password file.txt
This tells grep
to search for the string password
in the file file.txt , then print the matching lines in standard output. For example, we can quickly search the Nmap output file to see if the target has port 80 open:
这告诉 grep 在 file.txt 文件中搜索字符串"password",然后在标准输出中打印匹配的行。例如,我们可以快速搜索 Nmap 输出文件,看看目标是否开放了 80 端口:
$ grep 80 TARGET_DIRECTORY/nmap
80/tcp open http
You can also make your search more flexible by using regular expressions in your search string. A regular expression , or regex , is a special string that describes a search pattern. It can help you display only specific parts of the output. For example, you may have noticed that the output of the Nmap command looks like this:
你也可以使用正则表达式来让搜索更加灵活。正则表达式(又称为 regex)是一种描述搜索模式的特殊字符串。它可以帮助你仅显示输出中的特定部分。例如,你可能已经注意到 Nmap 命令输出的结果具有如下的格式:
Starting Nmap 7.60 ( https://nmap.org )
Nmap scan report for scanme.nmap.org (45.33.32.156)
Host is up (0.065s latency).
Other addresses for scanme.nmap.org (not scanned): 2600:3c01::f03c:91ff:fe18:bb2f
Not shown: 992 closed ports
PORT STATE SERVICE
22/tcp open ssh
25/tcp filtered smtp
80/tcp open http
135/tcp filtered msrpc
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds
9929/tcp open nping-echo
31337/tcp open Elite
Nmap done: 1 IP address (1 host up) scanned in 2.43 seconds
You might want to trim the irrelevant messages from the file so it looks more like this:
你可能需要从文件中修剪不相关的消息,使其看起来更像这样:
PORT STATE SERVICE
22/tcp open ssh
25/tcp filtered smtp
80/tcp open http
135/tcp filtered msrpc
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds
9929/tcp open nping-echo
31337/tcp open Elite
Use this command to filter out the messages at the start and end of Nmap’s output and keep only the essential part of the report:
使用此命令过滤掉 Nmap 输出的开头和结尾的消息,只保留报告的关键部分:
grep -E "^\S+\s+\S+\s+\S+$" DIRECTORY/nmap > DIRECTORY/nmap_cleaned
The -E
flag tells grep
you’re using a regex. A regex consists of two parts: constants and operators. Constants are sets of strings, while operators are symbols that denote operations over these strings. These two elements together make regex a powerful tool of pattern matching. Here’s a quick overview of regex operators that represent characters:
-E 标志告诉 grep 你正在使用正则表达式。正则表达式由常量和运算符两部分组成。常量是一组字符串,而运算符是表示对这些字符串进行操作的符号。这两个元素共同使正则表达式成为强大的模式匹配工具。以下是表示字符的正则表达式运算符的快速概述:
\d
matches any digit.\w
matches any character.\s
matches any whitespace, and\S
matches any non-whitespace..
matches with any single character.\
escapes a special character.^
matches the start of the string or line.$
matches the end of the string or line.
Several operators also specify the number of characters to match:
一些运算符还指定要匹配的字符数:
*
matches the preceding character zero or more times.+
matches the preceding character one or more times.{3}
matches the preceding character three times.{1, 3}
matches the preceding character one to three times.{1, }
matches the preceding character one or more times.[
abc]
matches one of the characters within the brackets.[
a-
z]
matches one of the characters within the range of a to z .(
a|
b|
c)
matches either a or b or c .
Let’s take another look at our regex expression here. Remember how \s
matches any whitespace, and \S
matches any non-whitespace? This means \s+
would match any whitespace one or more characters long, and \S+
would match any non-whitespace one or more characters long. This regex pattern specifies that we should extract lines that contain three strings separated by two whitespaces:
让我们再看一下我们的正则表达式。记得 \s 匹配任何空格,而 \S 匹配任何非空白字符吗?这意味着 \s+ 将匹配任何一个或多个字符长的空格,而 \S+ 将匹配任何一个或多个字符长的非空白。此正则表达式模式指定我们应该提取包含由两个空格分隔的三个字符串的行:
"^\S+\s+\S+\s+\S+$"
The filtered output will look like this:
筛选后的输出将如下所示:
PORT STATE SERVICE
22/tcp open ssh
25/tcp filtered smtp
80/tcp open http
135/tcp filtered msrpc
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds
9929/tcp open nping-echo
31337/tcp open Elite
To account for extra whitespaces that might be in the command output, let’s add two more optional spaces around our search string:
为了考虑命令输出中可能存在的额外空格,请在我们的搜索字符串周围再添加两个可选的空格:
"^\s*\S+\s+\S+\s+\S+\s*$"
You can use many more advanced regex features to perform more sophisticated matching. However, this simple set of operators serves well for our purposes. For a complete guide to regex syntax, read RexEgg’s cheat sheet ( https://www.rexegg.com/regex-quickstart.html ).
你可以使用许多更高级的正则表达式功能来执行更复杂的匹配。但是,这个简单的操作符集对我们的目的非常有用。有关正则表达式语法的完整指南,请阅读 RexEgg 的备忘单(https://www.rexegg.com/regex-quickstart.html)。
Building a Master Report
What if you want to produce a master report from all three output files? You need to parse the JSON file from crt.sh. You can do this with jq
, a command line utility that processes JSON. If we examine the JSON output file from crt.sh, we can see that we need to extract the name_value
field of each certificate item to extract domain names. This command does just that:
如果您想从所有三个输出文件中生成主报告怎么办?您需要解析 crt.sh 中的 JSON 文件。您可以使用 jq,这是一种处理 JSON 的命令行实用程序。如果我们检查 crt.sh 的 JSON 输出文件,我们可以看到我们需要提取每个证书项目的 name_value 字段以提取域名。这个命令就是这样做的:
$ jq -r ".[] | .name_value" $DOMAIN/crt
The -r
flag tells jq
to write the output directly to standard output rather than format it as JSON strings. The .[]
iterates through the array within the JSON file, and .name_value
extracts the name_value
field of each item. Finally, $DOMAIN/crt
is the input file to the jq
command. To learn more about how jq
works, read its manual ( https://stedolan.github.io/jq/manual/ ).
-r 标志告诉 jq 直接将输出写入标准输出,而不是将其格式化为 JSON 字符串。 . []遍历 JSON 文件中的数组,.name_value 提取每个项目的 name_value 字段。最后,$ DOMAIN / crt 是传递给 jq 命令的输入文件。要了解有关 jq 如何工作的更多信息,请阅读其手册(https://stedolan.github.io/jq/manual/)。
To combine all output files into a master report, write a script like this:
将所有输出文件组合成主报告,请编写以下脚本: 将所有输出文件组合成主报告,编写类似于此的脚本:
#!/bin/bash
PATH_TO_DIRSEARCH="/Users/vickieli/tools/dirsearch"
DOMAIN=$1
DIRECTORY=${DOMAIN}_recon
echo "Creating directory $DIRECTORY."
mkdir $DIRECTORY
nmap_scan()
{
nmap $DOMAIN > $DIRECTORY/nmap
echo "The results of nmap scan are stored in $DIRECTORY/nmap."
}
dirsearch_scan()
{
$PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php --simple-report=$DIRECTORY/dirsearch
echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
}
crt_scan()
{
curl "https://crt.sh/?q=$DOMAIN&output=json" -o $DIRECTORY/crt
echo "The results of cert parsing is stored in $DIRECTORY/crt."
}
case $2 in
nmap-only)
nmap_scan
;;
dirsearch-only)
dirsearch_scan
;;
crt-only)
crt_scan
;;
*)
nmap_scan
dirsearch_scan
crt_scan
;;
esac
echo "Generating recon report from output files..."
TODAY=$(date)
echo "This scan was created on $TODAY" > $DIRECTORY/report 1
echo "Results for Nmap:" >> $DIRECTORY/report
grep -E "^\s*\S+\s+\S+\s+\S+\s*$" $DIRECTORY/nmap >> $DIRECTORY/report 2
echo "Results for Dirsearch:" >> $DIRECTORY/report
cat $DIRECTORY/dirsearch >> $DIRECTORY/report 3
echo "Results for crt.sh:" >> $DIRECTORY/report
jq -r ".[] | .name_value" $DIRECTORY/crt >> $DIRECTORY/report 4
First, we create a new file named report and write today’s date into it 1 to keep track of when the report was generated. We then append the results of the nmap
and dirsearch
commands into the report file 2 . The cat
command prints the contents of a file to standard output, but we can also use it to redirect the content of the file into another file 3 . Finally, we extract domain names from the crt.sh report and append it to the end of the report file 4 .
首先,我们创建一个名为 report 的新文件,并将今天的日期写入其中 1,以跟踪报告生成的时间。然后,将 nmap 和 dirsearch 命令的结果追加到报告文件中 2。cat 命令可以将文件内容打印到标准输出,但我们也可以使用它将文件内容重定向到另一个文件 3。最后,我们从 crt.sh 报告中提取域名,并将其追加到报告文件的末尾 4。
Scanning Multiple Domains
What if we want to scan multiple domains at once? When reconning a target, we might start with several of the organization’s domain names. For example, we know that Facebook owns both facebook.com and fbcdn.net . But our current script allows us to scan only one domain at a time. We need to write a tool that can scan multiple domains with a single command, like this:
如果我们想要一次扫描多个域名怎么办?在对目标进行侦察时,我们可能会从组织的几个域名开始。例如,我们知道 Facebook 拥有 facebook.com 和 fbcdn.net。但是我们当前的脚本只允许我们一次只扫描一个域名。我们需要编写一个工具,可以用一个命令扫描多个域名,就像这样:
./recon.sh facebook.com fbcdn.net nmap-only
When we scan multiple domains like this, we need a way to distinguish which arguments specify the scan MODE
and which specify target domains. As you’ve already seen from the tools I introduced, most tools allow users to modify the behavior of a tool by using command line option s or flags , such as -u
and --simple-report
.
当我们像这样扫描多个域时,我们需要一种方法来区分哪些参数指定扫描模式,哪些参数指定目标域。正如我介绍的工具已经表明的那样,大多数工具允许用户使用命令行选项或标志(例如 -u 和--简单报告)来修改工具的行为。
The getopts
tool parses options from the command line by using single-character flags. Its syntax is as follows, where OPTSTRING specifies the option letters that getopts
should recognize. For example, if it should recognize the options -m
and -i
, you should specify mi
. If you want an option to contain argument values, the letter should be followed by a colon, like this: m:i
. The NAME argument specifies the variable name that stores the option letter.
getopts 工具使用单字母标志从命令行解析选项。它的语法如下,其中 OPTSTRING 指定 getopts 应该识别的选项字母。例如,如果它应该识别选项-m 和-i,则应该指定 mi。如果您希望选项包含参数值,则字母后面应跟冒号,如此 m:i。NAME 参数指定存储选项字母的变量名称。
getopts OPTSTRING NAME
To implement our multiple-domain scan functionality, we can let users use an -m
flag to specify the scan mode and assume that all other arguments are domains. Here, we tell getopts
to recognize an option if the option flag is -m
and that this option should contain an input value. The getopts
tool also automatically stores the value of any options into the $OPTARG
variable. We can store that value into a variable named MODE:
为实现多域名扫描功能,我们可以让用户使用“-m”标志指定扫描模式,并假设所有其他参数都是域名。这里,我们告诉 getopts,如果选项标志为“-m”,则应识别该选项,并且该选项应包含一个输入值。 getopts 工具还会自动将任何选项的值存储到$OPTARG 变量中。我们可以将该值存储到名为 MODE 的变量中:
getopts "m:" OPTION
MODE=$OPTARG
Now if you run the shell script with an -m
flag, the script will know that you’re specifying a scan MODE
! Note that getopts
stops parsing arguments when it encounters an argument that doesn’t start with the -
character, so you’ll need to place the scan mode before the domain arguments when you run the script:
现在,如果你在运行 shell 脚本时使用-m 标志,脚本将会知道你要指定扫描模式!请注意,当 getopts 遇到一个不以-字符开头的参数时,它会停止解析参数,因此在运行脚本时,你需要将扫描模式放在域参数之前:
./recon.sh -m nmap-only facebook.com fbcdn.net
Next, we’ll need a way to read every domain argument and perform scans on them. Let’s use loops! Bash has two types of loops: the for
loop and the while
loop. The for
loop works better for our purposes, as we already know the number of values we are looping through. In general, you should use for
loops when you already have a list of values to iterate through. You should use while
loops when you’re not sure how many values to loop through but want to specify the condition in which the execution should stop.
接下来,我们需要一种方法来读取每个域参数并对它们进行扫描。让我们使用循环!Bash 有两种类型的循环:for 循环和 while 循环。为了我们的目的,for 循环更加适用,因为我们已经知道了要循环遍历的值的数量。通常情况下,当你已经拥有一个值列表时,应该使用 for 循环进行迭代。如果您不确定要循环遍历多少个值,但想要指定执行应该停止的条件,则应该使用 while 循环。
Here’s the syntax of a for
loop in bash. For every item in LIST_OF_VALUES , bash will execute the code between do
and done
once:
以下是 bash 中 for 循环的语法。对于 LIST_OF_VALUES 中的每个项,bash 将执行 do 和 done 之间的代码一次:
for i in LIST_OF_VALUES
do
DO SOMETHING
done
Now let’s implement our functionality by using a for
loop:
现在,让我们使用 for 循环来实现我们的功能:
1 for i in "${@:$OPTIND:$#}"
do
# Do the scans for $i
done
We create an array 1 that contains every command line argument, besides the ones that are already parsed by getopts
, which stores the index of the first argument after the options it parses into a variable named $OPTIND
. The characters $@
represent the array containing all input arguments, while $#
is the number of command line arguments passed in. "${@:OPTIND:}"
slices the array so that it removes the MODE
argument, like nmap-only
, making sure that we iterate through only the domains part of our input. Array slicing is a way of extracting a subset of items from an array. In bash, you can slice arrays by using this syntax (note that the quotes around the command are necessary):
我们创建一个数组 1,其中包含每个命令行参数,除了那些已经被 getopts 解析的参数。getopts 将解析选项后的第一个参数的索引存储在一个名为$ OPTIND 的变量中。$@代表包含所有输入参数的数组,而$#是传递的命令行参数的数量。“$ { @: OPTIND:}”对数组进行切片,以便删除 MODE 参数(如 nmap-only),确保我们只迭代输入的域部分。数组切片是一种从数组中提取子集的方法。在 bash 中,可以使用以下语法切片数组(注意,必须使用命令周围的引号):
"${INPUT_ARRAY:START_INDEX:END_INDEX}"
The $i
variable represents the current item in the argument array. We can then wrap the loop around the code:
$i 变量代表参数数组中的当前项。然后我们可以在代码周围包装循环:
#!/bin/bash
PATH_TO_DIRSEARCH="/Users/vickieli/tools/dirsearch"
nmap_scan()
{
nmap $DOMAIN > $DIRECTORY/nmap
echo "The results of nmap scan are stored in $DIRECTORY/nmap."
}
dirsearch_scan()
{
$PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php --simple-report=$DIRECTORY/dirsearch
echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
}
crt_scan()
{
curl "https://crt.sh/?q=$DOMAIN&output=json" -o $DIRECTORY/crt
echo "The results of cert parsing is stored in $DIRECTORY/crt."
}
getopts "m:" OPTION
MODE=$OPTARG
for i in "${@:$OPTIND:$#}" 1
do
DOMAIN=$i
DIRECTORY=${DOMAIN}_recon
echo "Creating directory $DIRECTORY."
mkdir $DIRECTORY
case $MODE in
nmap-only)
nmap_scan
;;
dirsearch-only)
dirsearch_scan
;;
crt-only)
crt_scan
;;
*)
nmap_scan
dirsearch_scan
crt_scan
;;
esac
echo "Generating recon report for $DOMAIN..."
TODAY=$(date)
echo "This scan was created on $TODAY" > $DIRECTORY/report
if [ -f $DIRECTORY/nmap ];then 2
echo "Results for Nmap:" >> $DIRECTORY/report
grep -E "^\s*\S+\s+\S+\s+\S+\s*$" $DIRECTORY/nmap >> $DIRECTORY/report
fi
if [ -f $DIRECTORY/dirsearch ];then 3
echo "Results for Dirsearch:" >> $DIRECTORY/report
cat $DIRECTORY/dirsearch >> $DIRECTORY/report
fi
if [ -f $DIRECTORY/crt ];then 4
echo "Results for crt.sh:" >> $DIRECTORY/report
jq -r ".[] | .name_value" $DIRECTORY/crt >> $DIRECTORY/report
fi
done 5
The for
loop starts with the for
keyword 1 and ends with the done
keyword 5 . Notice that we also added a few lines in the report section to see if we need to generate each type of report. We check whether the output file of an Nmap scan, a Dirsearch scan, or a crt.sh scan exist so we can determine if we need to generate a report for that scan type 2 3 4 .
for 循环从 for 关键字 1 开始,到 done 关键字 5 结束。请注意,我们还在报告部分添加了一些行,以查看是否需要生成每种类型的报告。我们检查 Nmap 扫描,Dirsearch 扫描或 crt.sh 扫描的输出文件是否存在,以便确定我们是否需要为该扫描类型生成报告。
The brackets around a condition mean that we’re passing the contents into a
test
command: [ -f $DIRECTORY/nmap ]
is equivalent to test -f $DIRECTORY/nmap
.
条件周围的括号表示我们将内容传递到测试命令中:[ -f $DIRECTORY/nmap ] 等同于 test -f $DIRECTORY/nmap。
The test
command evaluates a conditional and outputs either true
or false
. The -f
flag tests whether a file exists. But you can test for more conditions! Let’s go through some useful test conditions. The -eq
and - ne
flags test for equality and inequality, respectively. This returns true
if $3
is equal to 1
:
测试命令评估一个条件并输出真或假。-f 标志测试文件是否存在。但您可以测试更多的条件!让我们来看看一些有用的测试条件。-eq 和 -ne 标志分别测试相等和不相等。如果 $3 等于 1,则返回 true:
if [ $3 -eq 1 ]
This returns true
if $3
is not equal to 1
:
这个语句返回 true 如果 3 不等于 1。
if [ $3 -ne 1 ]
The -gt
, -ge
, -lt
, and le
flags test for greater than, greater than or equal to, less than, and less than or equal to, respectively:
'-gt'表示大于,'-ge'表示大于或等于,'-lt'表示小于,'le'表示小于或等于。
if [ $3 -gt 1 ]
if [ $3 -ge 1 ]
if [ $3 -lt 1 ]
if [ $3 -le 1 ]
The -z
and -n
flags test whether a string is empty. These conditions are both true:
"-z 和-n 标志测试字符串是否为空。这些条件都是真的:"
if [ -z "" ]
if [ -n "abc" ]
The -d
, -f
, -r
, -w
, and -x
flags check for directory and file statuses. You can use them to check the existence and permissions of a file before your shell script operates on them. For instance, this command returns true
if /bin is a directory that exists:
-d、-f、-r、-w 和 -x 标记检查目录和文件状态。您可以使用它们在 shell 脚本操作文件之前检查文件的存在和权限。例如,如果 /bin 是一个存在的目录,这个命令返回 true:
if [ -d /bin]
This one returns true
if /bin/bash is a file that exists:
如果/bin/bash 是存在的文件,则返回 true。
if [ -f /bin/bash ]
And this one returns true
if /bin/bash is a readable file:
如果/bin/bash 是可读文件,则此命令将返回 true。
if [ -r /bin/bash ]
or a writable file:
或者可写入的文件:
if [ -w /bin/bash ]
or an executable file:
或一个可执行文件:
if [ -x /bin/bash ]
You can also use &&
and ||
to combine test expressions. This command returns true
if both expressions are true:
您也可以使用&&和||来组合测试表达式。如果两个表达式都为真,则此命令将返回 true。
if [ $3 -gt 1 ] && [ $3 -lt 3 ]
And this one returns true
if at least one of them is true:
如果其中至少有一个为真,则返回 true。
if [ $3 -gt 1 ] || [ $3 -lt 0 ]
You can find more comparison flags in the test
command’s manual by running man test
. (If you aren’t sure about the commands you’re using, you can always enter man
followed by the command name in the terminal to access the command’s manual file.)
如果您对正在使用的命令不确定,可以在终端输入 man 跟随命令名称来访问命令的说明文件,您可以在测试命令的手册中找到更多的比较标志,通过运行 man test。
Writing a Function Library
As your codebase gets larger, you should consider writing a function library to reuse code. We can store all the commonly used functions in a separate file called scan.lib . That way, we can call these functions as needed for future recon tasks:
随着代码库越来越庞大,您应该考虑编写函数库以重用代码。我们可以将所有常用的函数存储在一个名为 scan.lib 的单独文件中。这样,我们就可以根据未来的勘察任务需要随时调用这些函数。
#!/bin/bash
nmap_scan()
{
nmap $DOMAIN > $DIRECTORY/nmap
echo "The results of nmap scan are stored in $DIRECTORY/nmap."
}
dirsearch_scan()
{
$PATH_TO_DIRSEARCH/dirsearch.py -u $DOMAIN -e php --simple-report=$DIRECTORY/dirsearch
echo "The results of dirsearch scan are stored in $DIRECTORY/dirsearch."
}
crt_scan()
{
curl "https://crt.sh/?q=$DOMAIN&output=json" -o $DIRECTORY/crt
echo "The results of cert parsing is stored in $DIRECTORY/crt."
}
In another file, we can source the library file in order to use all of its functions and variables. We source a script via the source
command, followed by the path to the script:
在另一个文件中,我们可以通过调用库文件来使用它的所有函数和变量。我们可以通过 source 命令,后面跟着脚本的路径,来调用脚本文件。
#!/bin/bash
source ./scan.lib
PATH_TO_DIRSEARCH="/Users/vickieli/tools/dirsearch"
getopts "m:" OPTION
MODE=$OPTARG
for i in "${@:$OPTIND:$#}"
do
DOMAIN=$i
DIRECTORY=${DOMAIN}_recon
echo "Creating directory $DIRECTORY."
mkdir $DIRECTORY
case $MODE in
nmap-only)
nmap_scan
;;
dirsearch-only)
dirsearch_scan
;;
crt-only)
crt_scan
;;
*)
nmap_scan
dirsearch_scan
crt_scan
;;
esac
echo "Generating recon report for $DOMAIN..."
TODAY=$(date)
echo "This scan was created on $TODAY" > $DIRECTORY/report
if [ -f $DIRECTORY/nmap ];then
echo "Results for Nmap:" >> $DIRECTORY/report
grep -E "^\s*\S+\s+\S+\s+\S+\s*$" $DIRECTORY/nmap >> $DIRECTORY/report
fi
if [ -f $DIRECTORY/dirsearch ];then
echo "Results for Dirsearch:" >> $DIRECTORY/report
cat $DIRECTORY/dirsearch >> $DIRECTORY/report
fi
if [ -f $DIRECTORY/crt ];then
echo "Results for crt.sh:" >> $DIRECTORY/report
jq -r ".[] | .name_value" $DIRECTORY/crt >> $DIRECTORY/report
fi
done
Using a library can be super useful when you’re building multiple tools that require the same functionalities. For example, you might build multiple networking tools that all require DNS resolution. In this case, you can simply write the functionality once and use it in all of your tools.
使用库可以在构建多个需要相同功能的工具时非常有用。例如,您可能会构建多个网络工具,它们都需要 DNS 解析。在这种情况下,您只需编写一次功能,然后在所有工具中使用它。
Building Interactive Programs
What if you want to build an interactive program that takes user input during execution? Let’s say that if users enter the command line option, -i
, you want the program to enter an interactive mode that allows you to specify domains to scan as you go:
如果您想建立一个交互式程序,在执行期间接受用户输入怎么办?假设用户输入命令行选项 -i,则希望该程序进入交互模式,允许您按需指定要扫描的域:
./recon.sh -i -m nmap-only
For that, you can use read
. This command reads user input and stores the input string into a variable:
为此,您可以使用 read 命令。该命令读取用户输入并将输入字符串存储到变量中:
echo "Please enter a domain!"
read $DOMAIN
These commands will prompt the user to enter a domain, then store the input inside a variable named $DOMAIN
.
这些命令会提示用户输入域名,并将输入存储在名为$DOMAIN 的变量中。
To prompt a user repeatedly, we need to use a while
loop, which will keep printing the prompt asking for an input domain until the user exits the program. Here’s the syntax of a while
loop. As long as the CONDITION is true, the while
loop will execute the code between do
and done
repeatedly:
要重复提示用户,我们需要使用 while 循环,它将不断打印提示以要求输入域,直到用户退出程序。以下是 while 循环的语法。只要条件为真,则 while 循环将在 do 和 done 之间重复执行代码。
while CONDITION
do
DO SOMETHING
done
We can use a while
loop to repeatedly prompt the user for domains until the user enters quit
:
我们可以使用 while 循环来重复提示用户输入域名,直到用户输入 quit 为止。
while [ $INPUT != "quit" ];do
echo "Please enter a domain!"
read INPUT
if [ $INPUT != "quit" ];then
scan_domain $INPUT
report_domain $INPUT
fi
done
We also need a way for users to actually invoke the -i
option, and our getopts
command isn’t currently doing that. We can use a while
loop to parse options by using getopts
repeatedly:
我们还需要一种让用户实际调用“-i”选项的方法,而目前我们的 getopts 命令还没有实现这一点。我们可以使用 while 循环,通过重复使用 getopts 来解析选项。
while getopts "m:i" OPTION; do
case $OPTION in
m)
MODE=$OPTARG
;;
i)
INTERACTIVE=true
;;
esac
done
Here, we specify a while
loop that gets command line options repeatedly. If the option flag is -m
, we set the MODE
variable to the scan mode that the user has specified. If the option flag is -i
, we set the $INTERACTIVE
variable to true
. Then, later in the script, we can decide whether to invoke the interactive mode by checking the value of the $INTERACTIVE
variable. Putting it all together, we get our final script:
在这里,我们指定一个 while 循环,重复获取命令行选项。如果选项标志是“-m”,我们将 MODE 变量设置为用户指定的扫描模式。如果选项标志是“-i”,我们将$INTERACTIVE 变量设置为 true。然后,在脚本的后面,我们可以通过检查$INTERACTIVE 变量的值来决定是否调用交互模式。把它全部结合起来,我们得到了最终的脚本:
#!/bin/bash
source ./scan.lib
while getopts "m:i" OPTION; do
case $OPTION in
m)
MODE=$OPTARG
;;
i)
INTERACTIVE=true
;;
esac
done
scan_domain(){
DOMAIN=$1
DIRECTORY=${DOMAIN}_recon
echo "Creating directory $DIRECTORY."
mkdir $DIRECTORY
case $MODE in
nmap-only)
nmap_scan
;;
dirsearch-only)
dirsearch_scan
;;
crt-only)
crt_scan
;;
*)
nmap_scan
dirsearch_scan
crt_scan
;;
esac
}
report_domain(){
DOMAIN=$1
DIRECTORY=${DOMAIN}_recon
echo "Generating recon report for $DOMAIN..."
TODAY=$(date)
echo "This scan was created on $TODAY" > $DIRECTORY/report
if [ -f $DIRECTORY/nmap ];then
echo "Results for Nmap:" >> $DIRECTORY/report
grep -E "^\s*\S+\s+\S+\s+\S+\s*$" $DIRECTORY/nmap >> $DIRECTORY/report
fi
if [ -f $DIRECTORY/dirsearch ];then
echo "Results for Dirsearch:" >> $DIRECTORY/report
cat $DIRECTORY/dirsearch >> $DIRECTORY/report
fi
if [ -f $DIRECTORY/crt ];then
echo "Results for crt.sh:" >> $DIRECTORY/report
jq -r ".[] | .name_value" $DIRECTORY/crt >> $DIRECTORY/report
fi
}
if [ $INTERACTIVE ];then 1
INPUT="BLANK"
while [ $INPUT != "quit" ];do 2
echo "Please enter a domain!"
read INPUT
if [ $INPUT != "quit" ];then 3
scan_domain $INPUT
report_domain $INPUT
fi
done
else
for i in "${@:$OPTIND:$#}";do
scan_domain $i
report_domain $i
done
fi
In this program, we first check if the user has selected the interactive mode by specifying the -i
option 1 . We then repeatedly prompt the user for a domain by using a while
loop 2 . If the user input is not the keyword quit
, we assume that they entered a target domain, so we scan and produce a report for that domain. The while
loop will continue to run and ask the user for domains until the user enters quit
, which will cause the while
loop to exit and the program to terminate 3 .
在此程序中,我们首先检查用户是否通过指定 -i 选项选择交互模式。接着,我们使用 while 循环重复提示用户输入域名。如果用户输入不是 quit 关键字,则假定他们输入了目标域名,因此我们会扫描并为该域名生成报告。while 循环将继续运行并要求用户输入域名,直到用户输入 quit,这将导致 while 循环退出并终止程序。
Interactive tools can help your workflow operate more smoothly. For example, you can build testing tools that will let you choose how to proceed based on preliminary results.
交互工具可以帮助你的工作流程更加顺畅。例如,你可以构建测试工具,根据初步结果选择如何继续操作。
Using Special Variables and Characters
You’re now equipped with enough bash knowledge to build many versatile tools. This section offers more tips that concern the particularities of shell scripts.
你现在掌握了足够的 Bash 知识,可以构建许多多功能工具。本节提供更多有关 shell 脚本的特殊提示。
In Unix, commands return 0
on success and a positive integer on failure. The variable $?
contains the exit value of the last command executed. You can use these to test for execution successes and failures:
在 Unix 中,命令成功执行会返回 0,失 败则返回正整数。变量$?包含最后一个执行的命令的退出值。您可以使用这些值来测试执行的成功或失败情况:
#!/bin/sh
chmod 777 script.sh
if [ "$?" -ne "0" ]; then
echo "Chmod failed. You might not have permissions to do that!"
fi
Another special variable is $$
, which contains the current process’s ID. This is useful when you need to create temporary files for the script. If you have multiple instances of the same script or program running at the same time, each might need its own temporary files. In this case, you can create temporary files named /tmp/script_name_$$ for every one of them.
另一个特殊的变量是$$,它包含当前进程的 ID。当您需要为脚本创建临时文件时,这非常有用。如果您同时运行同一脚本或程序的多个实例,每个实例可能都需要自己的临时文件。在这种情况下,您可以为它们中的每一个创建名为/tmp/script_name_$$的临时文件。
Remember that we talked about variable scopes in shell scripts earlier in this chapter? Variables that aren’t input parameters are global to the entire script. If you want other programs to use the variable as well, you need to export the variable:
在本章早些时候我们讨论过 shell 脚本中的变量作用域。如果变量不是输入参数,则对整个脚本都是全局的。如果您希望其他程序也使用该变量,您需要导出该变量:
export VARIABLE_NAME=VARIABLE_VALUE
Let’s say that in one of your scripts you set the variable VAR
:
假设在您的脚本中设置了变量 VAR:
VAR="hello!"
If you don’t export it or source it in another script, the value gets destroyed after the script exits. But if you export VAR
in the first script and run that script before running a second script, the second script will be able to read VAR
’s value.
如果您在脚本中不会导出或在另一个脚本中引用它,值将在脚本退出后被销毁。但是,如果您在第一个脚本中导出 VAR 并在运行第二个脚本之前运行该脚本,则第二个脚本将能够读取 VAR 的值。
You should also be aware of special characters in bash. In Unix, the wildcard character *
stands for all . For example, this command will print out all the filenames in the current directory that have the file extension .txt :
你也需要注意 bash 中的特殊字符。在 Unix 中,通配符*代表所有。例如,这个命令将打印当前目录中所有具有文件扩展名.txt 的文件名:
$ ls *.txt
Backticks ( `
) indicate command substitution. You can use both backticks and the $()
command substitution syntax mentioned earlier for the same purpose. This echo
command will print the output of the whoami
command:
反引号(`)表示命令替换。您可以同时使用反引号和$()命令替换语法来达到相同的目的。这个 echo 命令将打印出 whoami 命令的输出:
echo `whoami`
Most special characters, like the wildcard character or the single quote, aren’t interpreted as special when they are placed in double quotes. Instead, they’re treated as part of a string. For example, this command will echo the string "abc '*' 123"
:
大多数特殊字符(例如通配符或单引号)在双引号中使用时不会被解释为特殊字符,而是被视为字符串的一部分。例如,这个命令将回显字符串 "abc '*' 123"。
$ echo "abc '*' 123"
Another important special character is the backslash ( \
), the escape character in bash. It tells bash that a certain character should be interpreted literally, and not as a special character.
另一个重要的特殊字符是反斜杠(\),是 bash 中的转义字符。它告诉 bash 应该将某些字符解释为字面意思,而不是特殊字符。
Certain special characters, like double quotes, dollar sign, backticks, and backslashes remain special even within double quotes, so if you want bash to treat them literally, you have to escape them by using a backslash:
特定的特殊字符,比如双引号、美元符号、反引号和反斜杠,即使在双引号内部仍然保持特殊,所以如果想要让 bash 将它们按字面含义处理,你需要使用反斜杠来转义它们:
$ echo "\" is a double quote. \$ is a dollar sign. \` is a backtick. \\ is a backslash."
This command will echo:
"这个指令将会回显:"
" is a double quote. $ is a dollar sign. ` is a backtick. \ is a backslash.
You can also use a backslash before a newline to indicate that the line of code has not ended. For example, this command
你也可以在换行符之前使用反斜杠,以表示该行代码未结束。例如,这个命令。
chmod 777 \
script.sh
is the same as this one:
与这个相同:
chmod 777 script.sh
Congratulations! You can now write bash scripts. Bash scripting may seem scary at first, but once you’ve mastered it, it will be a powerful addition to your hacking arsenal. You’ll be able to perform better recon, conduct more efficient testing, and have a more structured hacking workflow.
恭喜您!现在您已经可以编写 Bash 脚本了。Bash 脚本在一开始可能会让人感到害怕,但一旦掌握它,它将成为您黑客工具箱中强大的工具。您将能够执行更好的侦察,进行更有效的测试,并拥有更有结构的黑客工作流程。
If you plan on implementing a lot of automation, it’s a good idea to start organizing your scripts from the start. Set up a directory of scripts and sort your scripts by their functionality. This will become the start of developing your own hacking methodology. When you’ve collected a handful of scripts that you use on a regular basis, you can use scripts to run them automatically. For example, you might categorize your scripts into recon scripts, fuzzing scripts, automated reporting, and so on. This way, every time you find a script or tool you like, you can quickly incorporate it into your workflow in an organized fashion.
如果您计划实施大量自动化,最好从一开始就开始组织您的脚本。建立一个脚本目录,并根据功能对脚本进行分类。这将成为开发自己的黑客方法论的开始。当您收集了一些经常使用的脚本时,可以使用脚本自动运行它们。例如,您可以将脚本分类为侦察脚本,模糊测试脚本,自动报告等。这样,每当您发现自己喜欢的脚本或工具时,都可以以有组织的方式快速将其纳入您的工作流程中。
Scheduling Automatic Scans
Now let’s take your automation to the next level by building an alert system that will let us know if something interesting turns up in our scans. This saves us from having to run the commands manually and comb through the results over and over again.
现在,让我们把你的自动化技术提升到新的水平,构建一个警报系统,让我们知道如果在我们的扫描中出现有趣的内容,这将使我们免于手动运行命令和一遍又一遍地浏览结果的麻烦。
We can use cron jobs to schedule our scans. Cron is a job scheduler on Unix-based operating systems. It allows you to schedule jobs to run periodically. For example, you can run a script that checks for new endpoints on a particular site every day at the same time. Or you can run a scanner that checks for vulnerabilities on the same target every day. This way, you can monitor for changes in an application’s behavior and find ways to exploit it.
我们可以使用 cron 作业来计划我们的扫描。Cron 是 Unix 基础操作系统上的作业调度程序。它允许您定期运行作业。例如,您可以每天在同一时间运行检查特定站点上新端点的脚本。或者您可以每天运行检查同一目标漏洞的扫描程序。通过这种方式,您可以监视应用行为的变化并找到对其进行利用的方法。
You can configure Cron’s behavior by editing files called crontabs . Unix keeps different copies of crontabs for each user. Edit your own user’s crontab by running the following:
你可以通过编辑名为 crontabs 的文件来配置 Cron 的行为。Unix 为每个用户保留不同的 crontabs 副本。 运行以下命令来编辑你自己用户的 crontab:
crontab -e
All crontabs follow this same syntax:
所有的 cron 表达式都遵循相同的语法:
A B C D E command_to_be_executed
A: Minute (0 – 59)
B: Hour (0 – 23)
C: Day (1 – 31)
D: Month (1 – 12)
E: Weekday (0 – 7) (Sunday is 0 or 7, Monday is 1...)
Each line specifies a command to be run and the time at which it should run, using five numbers. The first number, from 0 to 59, specifies the minute when the command should run. The second number specifies the hour, and ranges from 0 to 23. The third and fourth numbers are the day and month the command should run. And the last number is the weekday when the command should run, which ranges from 0 to 7. Both 0 and 7 mean that the command should run on Sundays; 1 means the command should run on Mondays; and so on.
每行都指定要运行的命令以及它应该运行的时间,使用五个数字。第一个数字从 0 到 59,指定命令应该运行的分钟。第二个数字指定小时数,范围从 0 到 23。第三和第四个数字是命令应该运行的日期和月份。最后一个数字是命令应该运行的星期几,范围从 0 到 7。0 和 7 都表示命令应该在星期天运行。1 表示命令应该在星期一运行,依此类推。
For example, you can add this line to your crontab to run your recon script every day at 9:30 PM :
例如,您可以将以下行添加到您的 crontab 中,每天晚上 9 点 30 分运行您的 recon 脚本:
30 21 * * * ./scan.sh
You can also batch-run the scripts within directories. The run-parts
command in crontabs tells Cron to run all the scripts stored in a directory. For example, you can store all your recon tools in a directory and scan your targets periodically. The following line tells Cron to run all scripts in my security directory every day at 9:30 PM :
您还可以批量运行目录中的脚本。crontab 中的 run-parts 命令告诉 Cron 运行目录中存储的所有脚本。例如,您可以将所有的侦察工具存储在一个目录中,并定期扫描您的目标。下面的命令告诉 Cron 每天晚上 9:30 运行我安全目录中的所有脚本:
30 21 * * * run-parts /Users/vickie/scripts/security
Next, git diff
is a command that outputs the difference between two files. You need to install the Git program to use it. You can use git diff
to compare scan results at different times, which quickly lets you see if the target has changed since you last scanned it:
`git diff`是一个命令,用于输出两个文件之间的差异。要使用它,您需要安装 Git 程序。您可以使用`git diff`比较不同时间的扫描结果,这将快速让您了解目标自上次扫描以来是否有更改:
git diff SCAN_1 SCAN_2
This will help you identify any new domains, subdomains, endpoints, and other new assets of a target. You could write a script like this to notify you of new changes on a target every day:
这将帮助您识别目标的任何新域名、子域名、终端点和其他新资产。您可以编写如下脚本,每天通知您目标的新变化:
#!/bin/bash
DOMAIN=$1
DIRECTORY=${DOMAIN}_recon
echo "Checking for new changes about the target: $DOMAIN.\n Found these new things."
git diff <SCAN AT TIME 1> <SCAN AT TIME 2>
And schedule it with Cron:
"使用 Cron 安排它:"
30 21 * * * ./scan_diff.sh facebook.com
These automation techniques have helped me quickly find new JavaScript files, endpoints, and functionalities on targets. I especially like to use this technique to discover subdomain takeover vulnerabilities automatically. We’ll talk about subdomain takeovers in Chapter 20 .
这些自动化技巧帮助我快速在目标上找到新的 JavaScript 文件、端点和功能。我特别喜欢使用这种技术自动发现子域接管漏洞。我们将在第 20 章讨论子域接管问题。
Alternatively, you can use GitHub to track changes. Set up a repository to store your scan results at https://github.com/new/ . GitHub has a Notification feature that will tell you when significant events on a repository occur. It’s located at Settings ▶ Notifications on each repository’s page. Provide GitHub with an email address that it will use to notify you about changes. Then, in the directory where you store scan results, run these commands to initiate git
inside the directory:
或者,您可以使用 GitHub 来跟踪更改。 在 https://github.com/new/上设置一个存储您的扫描结果的存储库。 GitHub 具有通知功能,可以在存储库上发生重要事件时告诉您。 它位于每个存储库页面的“设置▶Notifications”下。 向 GitHub 提供一个电子邮件地址,以便它会使用它来通知您有关更改的信息。 然后,在存储扫描结果的目录中运行以下命令以初始化目录内的 git:
git init
git remote add origin https://PATH_TO_THE_REPOSITORY
Lastly, use Cron to scan the target and upload the files to GitHub periodically:
最后,使用 Cron 定期扫描目标并上传文件到 GitHub。
30 21 * * * ./recon.sh facebook.com
40 21 * * * git add *; git commit -m "new scan"; git push -u origin master
GitHub will then send you an email about the files that changed during the new scan.
GitHub 随后会向您发送一封有关新扫描中更改的文件的电子邮件。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论