- 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
Hunting for SQL Injections
Let’s start hunting for SQL injections! Earlier in this chapter, I mentioned that we can classify SQL injections as either first order or second order. But there’s another way of classifying SQL injections that is useful when exploiting them: classic SQL injections, and blind SQL. The approach to detecting and exploiting these differs.
让我们开始寻找 SQL 注入!在本章早些时候,我提到过我们可以将 SQL 注入分类为一级注入或二级注入。但是,在利用它们时,还有另一种有用的分类方法:经典 SQL 注入和盲注 SQL。检测和利用这些的方法是不同的。
Before we dive into each type, a common technique for detecting any SQL injection is to insert a single quote character ( '
) into every user input and look for errors or other anomalies. The single quote is a special character in SQL statements that denotes the end of a query string. If the application is protected against SQL injections, it should treat the single quote as plain data, and inserting a single quote into the input field should not trigger database errors or change the logic of the database query.
在我们深入讨论每种类型之前,检测 SQL 注入的常见技术是将单引号字符(')插入每个用户输入,并查找错误或其他异常。单引号是 SQL 语句中表示查询字符串结束的特殊字符。如果应用程序受到 SQL 注入的保护,它应该将单引号视为普通数据,将单引号插入输入字段不应触发数据库错误或更改数据库查询的逻辑。
Another general way of finding SQL injections is fuzzing , which is the practice of submitting specifically designed SQL injection payloads to the application and monitoring the server’s response. We will talk about this in Chapter 25 .
发现 SQL 注入的另一种常规方法是“Fuzzing”,即将特别设计的 SQL 注入有效载荷提交给应用程序并监视服务器的响应。我们将在第 25 章中讨论此问题。
Otherwise, you can submit payloads designed for the target’s database intended to trigger a difference in database response, a time delay, or a database error. Remember, you’re looking for clues that the SQL code you injected can be executed.
否则,您可以提交专为目标数据库设计的有效负载,以触发数据库响应差异,时间延迟或数据库错误。请记住,您正在寻找 SQL 代码注入可以执行的线索。
Step 1: Look for Classic SQL Injections
Classic SQL injections are the easiest to find and exploit. In classic SQL injections, the results of the SQL query are returned directly to the attacker in an HTTP response. There are two subtypes: UNION based and error based.
经典的 SQL 注入是最容易发现和利用的。在经典的 SQL 注入中,SQL 查询的结果直接返回给攻击者的 HTTP 响应。它有两种子类型:基于联合和基于错误的注入。
Our email example earlier is a case of the UNION-based approach: an attacker uses the UNION
operator to concatenate the results of another query onto the web application’s response:
我们之前提到的电子邮件示例是基于 UNION 的攻击方法:攻击者使用 UNION 运算符将另一个查询的结果连接到 Web 应用程序的响应中:
SELECT Title, Body FROM Emails
WHERE Username='vickie' AND AccessKey='ZB6w0YLjzvAVmp6zvr'
UNION SELECT Username, Password FROM Users;-- ;
In this case, the server would return all usernames and passwords along with the user vickie ’s emails in the HTTP response ( Table 11-2 ).
在这种情况下,服务器将在 HTTP 响应中返回所有用户名和密码,以及用户 Vickie 的电子邮件(表 11-2)。
Table 11-2 : Emails That Result from Our Malicious Query
表 11-2:由我们恶意查询引起的电子邮件。
Title | Body |
Finish setting up your account! | Please finish setting up your example.com account by submitting a recovery email address. |
Welcome | Welcome to example.com ’s email service |
admin | t5dJ12rp$fMDEbSWz |
vickie | password123 |
jennifer | letmein! |
On the other hand, error-based SQL injection attacks trigger an error in the database to collect information from the returned error message. For example, we can induce an error by using the CONVERT()
function in MySQL:
另一方面,基于错误的 SQL 注入攻击会在数据库中触发错误,以从返回的错误消息中收集信息。例如,我们可以使用 MySQL 中的 CONVERT() 函数来引发错误:
SELECT Title, Body FROM Emails
WHERE Username='vickie' AND AccessKey='ZB6w0YLjzvAVmp6zvr'
UNION SELECT 1,
CONVERT((SELECT Password FROM Users WHERE Username="admin"), DATE); –-
The CONVERT(
VALUE ,
FORMAT )
function attempts to convert VALUE to the format specified by FORMAT . Therefore, this query will force the database to convert the admin’s password to a date
format, which can sometimes cause the database to throw a descriptive error like this one:
CONVERT(VALUE,FORMAT)函数尝试将 VALUE 转换为 FORMAT 指定的格式。因此,这个查询将强制数据库将管理员密码转换为日期格式,这有时会导致数据库抛出如下所示的描述性错误:
Conversion failed when trying to convert "t5dJ12rp$fMDEbSWz" to data type "date".
The database throws descriptive errors to help developers pinpoint problems, but can also accidentally reveal information to outsiders if error messages are shown to regular users as well. In this example, the database points out that it has failed to convert a string value, "t5dJ12rp$fMDEbSWz"
, to the date
format. But t5dJ12rp$fMDEbSWz
is the password of the admin account! By displaying a descriptive error message, the database has accidentally revealed a sensitive piece of information to outsiders.
数据库提供详细的错误信息来帮助开发人员找到问题,但如果错误信息也显示给普通用户,就可能会意外地向外泄露信息。在这个例子中,数据库指出无法将“t5dJ12rp$fMDEbSWz”这个字符串值转换成日期格式。但是,“t5dJ12rp$fMDEbSWz”是管理员账户的密码!通过显示详细的错误信息,数据库意外地向外界透露了一条敏感信息。
Step 2: Look for Blind SQL Injections
Also called inferential SQL injections , blind SQL injections are a little harder to detect and exploit. They happen when attackers cannot directly extract information from the database because the application doesn’t return SQL data or descriptive error messages. In this case, attackers can infer information by sending SQL injection payloads to the server and observing its subsequent behavior. Blind SQL injections have two subtypes as well: Boolean based and time based.
也称为推理性 SQL 注入,盲注攻击比较难以检测和利用。这种攻击发生在攻击者不能直接从数据库中提取信息的情况下,因为应用程序不返回 SQL 数据或描述性错误消息。在这种情况下,攻击者可以通过向服务器发送 SQL 注入负载并观察其随后的行为来推断信息。盲注攻击也分为两个子类型:基于布尔值和基于时间的情况。
Boolean-based SQL injection occurs when attackers infer the structure of the database by injecting test conditions into the SQL query that will return either true
or false
. Using those responses, attackers could slowly infer the contents of the database. For example, let’s say that example.com maintains a separate table to keep track of the premium members on the platform. Premium members have access to advanced features, and their home pages display a Welcome, premium member!
banner. The site determines who is premium by using a cookie that contains the user’s ID and matching it against a table of registered premium members. The GET request containing such a cookie might look like this:
基于布尔值的 SQL 注入是指攻击者通过向 SQL 查询注入测试条件来推断数据库结构,以返回 true 或 false。攻击者可以利用这些响应,逐步推断出数据库的内容。例如,假设 example.com 维护一个单独的表来跟踪平台上的高级会员。高级会员可以访问高级功能,并在其主页上显示“欢迎,高级会员!”的横幅。该网站通过使用包含用户 ID 的 cookie 并将其与注册的高级会员表进行匹配来确定谁是高级会员。包含此类 cookie 的 GET 请求可能如下所示:
GET /
Host: example.com
Cookie: user_id=2
The application uses this request to produce the following SQL query:
该应用程序使用此请求来生成以下 SQL 查询:
SELECT * FROM PremiumUsers WHERE Id='2';
If this query returns data, the user is a premium member, and the Welcome, premium member!
banner will be displayed. Otherwise, the banner won’t be displayed. Let’s say your account isn’t premium. What would happen if you submit this user ID instead?
如果此查询返回数据,则用户是高级会员,并将显示“欢迎,高级会员!”横幅。否则,该横幅将不会显示。假设您的帐户不是高级帐户。如果您提交此用户 ID,会发生什么?
2' UNION SELECT Id FROM Users
WHERE Username = 'admin'
and SUBSTR(Password, 1, 1) ='a';--
Well, the query would become the following:
好的,查询将变为以下内容:
SELECT * FROM PremiumUsers WHERE Id='2'
UNION SELECT Id FROM Users
WHERE Username = 'admin'
and 1SUBSTR(Password, 1, 1) = 'a';--
The SUBSTR(
STRING ,
POSITION ,
LENGTH )
function extracts a substring from the STRING , of a specified LENGTH , at the specified POSITION in that string. Therefore, SUBSTR(Password, 1, 1)
1 returns the first character of each user’s password. Since user 2 isn’t a premium member, whether this query returns data will depend on the second SELECT
statement, which returns data if the admin account’s password starts with an a
. This means you can brute-force the admin’s password; if you submit this user ID as a cookie, the web application would display the premium banner if the admin account’s password starts with an a
. You could try this query with the letters b
, c
, and so on, until it works.
SUBSTR(字符串,位置,长度)函数从指定位置开始,提取字符串中指定长度的子字符串。因此,SUBSTR(密码,1,1)返回每个用户密码的第一个字符。由于用户 2 不是高级成员,此查询返回数据取决于第二个 SELECT 语句,如果管理员帐户的密码以 a 开头,则返回数据。这意味着您可以强制破解管理员的密码;如果您将此用户 ID 提交为 cookie,则 Web 应用程序将显示高级横幅,如果管理员帐户的密码以 a 开头。您可以尝试使用字母 b,c 等进行此查询,直到成功。
You can use this technique to extract key pieces of information from the database, such as the database version, table names, column names, and credentials. I talk more about this in “Escalating the Attack” on page 201 .
您可以使用此技术从数据库中提取关键信息,如数据库版本、表名、列名和凭据。我在第 201 页的“升级攻击”中将更详细地讨论此技术。
A time-based SQL injection is similar, but instead of relying on a visual cue in the web application, the attacker relies on the response-time difference caused by different SQL injection payloads. For example, what might happen if the injection point from our preceding example doesn’t return any visual clues about the query’s results? Let’s say premium members don’t get a special banner, and their user interfaces don’t look any different. How do you exploit this SQL injection then?
时间型 SQL 注入类似,但攻击者不依赖于 Web 应用程序中的视觉提示,而是依赖于不同 SQL 注入有效负载所引起的响应时间差异。例如,如果我们上一个示例中的注入点没有返回任何关于查询结果的视觉提示会发生什么?假设高级会员没有获得特殊横幅,他们的用户界面也没有任何不同。那么您如何利用此 SQL 注入呢?
In many databases, you can trigger a time delay by using a SQL query. If the time delay occurs, you’ll know the query worked correctly. Try using an IF
statement in the SQL query:
在许多数据库中,您可以通过使用 SQL 查询触发时间延迟。如果时间延迟发生,您将知道查询已正确工作。尝试在 SQL 查询中使用 IF 语句。 在许多数据库中,您可以使用 SQL 查询触发时间延迟。如果时间延迟发生,您将知道查询已正确工作。尝试在 SQL 查询中使用 IF 语句。
IF(CONDITION, IF-TRUE, IF-FALSE)
For example, say you submit the following ID:
例如,假设您提交以下身份证:
2' UNION SELECT
IF(SUBSTR(Password, 1, 1) = 'a', SLEEP(10), 0)
Password FROM Users
WHERE Username = 'admin';
The SQL query would become the following:
SQL 查询将变为以下内容:
SELECT * FROM PremiumUsers WHERE Id='2'
UNION SELECT
IF(SUBSTR(Password, 1, 1) = 'a', SLEEP(10), 0)
Password FROM Users
WHERE Username = 'admin';
The SLEEP(
SECONDS )
function in MySQL will create a time delay in the response for the specified number of seconds. This query will instruct the database to sleep for 10 seconds if the admin’s password starts with an a
character. Using this technique, you can slowly figure out the admin’s password.
MySQL 中的 SLEEP(SECONDS)函数将在指定的秒数内创建响应延迟。 如果管理员密码以 “a” 字符开头,则此查询将指示数据库等待 10 秒钟。 使用此技术,您可以逐渐找出管理员的密码。
Step 3: Exfiltrate Information by Using SQL Injections
Imagine that the web application you’re attacking doesn’t use your input in a SQL query right away. Instead, it uses the input unsafely in a SQL query during a backend operation, so you have no way to retrieve the results of injection via an HTTP response, or infer the query’s results by observing server behavior. Sometimes there’s even a time delay between when you submitted the payload and when the payload gets used in an unsafe query, so you won’t immediately be able to observe differences in the application’s behavior.
请想象一下,您正在攻击的 Web 应用程序不会立即在 SQL 查询中使用您的输入。相反,在后端操作期间不安全地使用输入作为 SQL 查询,因此您无法通过 HTTP 响应检索注入的结果,也无法通过观察服务器行为推断查询的结果。有时,提交有效载荷和有效载荷在不安全的查询中使用之间会存在时间延迟,因此您可能无法立即观察到应用程序行为上的差异。
In this case, you’ll need to make the database store information somewhere when it does run the unsafe SQL query. In MySQL, the SELECT. . .INTO
statement tells the database to store the results of a query in an output file on the local machine. For example, the following query will cause the database to write the admin’s password into /var/www/html/output.txt , a file located on the web root of the target web server:
在这种情况下,当数据库运行不安全的 SQL 查询时,您需要将信息存储在某个地方。在 MySQL 中,SELECT...INTO 语句告诉数据库将查询结果存储在本地计算机上的输出文件中。例如,下面的查询将导致数据库将管理员密码写入到位于目标 Web 服务器的 Web 根目录下的/var/www/html/output.txt 文件中:
SELECT Password FROM Users WHERE Username='admin'
INTO OUTFILE '/var/www/html/output.txt'
We upload to the /var/www/html directory because it’s the default web directory for many Linux web servers. Then you can simply access the information by navigating to the /output.txt page on the target: https://example.com/output.txt . This technique is also a good way to detect second-order SQL injections, since in second-order SQL injections, there is often a time delay between the malicious input and the SQL query being executed.
我们上传到/var/www/html 目录,因为它是许多 Linux Web 服务器的默认 Web 目录。然后,您可以通过导航到目标上的/output.txt 页面(https://example.com/output.txt)简单地访问信息。这种技术还是检测二阶 SQL 注入的好方法,因为在二阶 SQL 注入中,恶意输入和执行 SQL 查询之间经常存在时间延迟。
Let’s put this information in context. Say that when you browse example.com , the application adds you to a database table to keep track of currently active users. Accessing a page with a cookie, like this
让我们将这个信息放入背景中。假设当您浏览 example.com 时,该应用程序会将您添加到数据库表中以跟踪当前活跃用户。访问一个带有 cookie 的页面,就像这样。
GET /
Host: example.com
Cookie: user_id=2, username=vickie
will cause the application to add you to a table of active users. In this example, the ActiveUsers table contains only two columns: one for the user ID and one for the username of the logged-in user. The application uses an INSERT
statement to add you to the ActiveUsers table. INSERT
statements add a row into the specified table with the specified values:
将会使应用程序将您添加到活跃用户表中。在此示例中,ActiveUsers 表仅包含两列:一个用于用户 ID,一个用于已登录用户的用户名。应用程序使用 INSERT 语句将您添加到 ActiveUsers 表中。INSERT 语句将指定的值添加到指定表中的一行:
INSERT INTO ActiveUsers
VALUES ('2', 'vickie');
In this case, an attacker can craft a malicious cookie to inject into the INSERT
statement:
在这种情况下,攻击者可以制作恶意的 cookie,并注入到 INSERT 语句中。
GET /
Host: example.com
Cookie: 1user_id="2', (SELECT Password FROM Users
WHERE Username='admin'
INTO OUTFILE '/var/www/html/output.txt'));-- ", username=vickie
This cookie 1 will, in turn, cause the INSERT
statement to save the admin’s password into the output.txt file on the victim server:
这个 Cookie 1 会触发 INSERT 语句将管理员密码保存在受害服务器的 output.txt 文件中。
INSERT INTO ActiveUsers
VALUES ('2', (SELECT Password FROM Users
WHERE Username='admin'
INTO OUTFILE '/var/www/html/output.txt'));-- ', 'vickie');
Finally, you will find the password of the admin account stored into the output.txt file on the target server.
最后,你会在目标服务器的 output.txt 文件中找到管理员账户的密码。
Step 4: Look for NoSQL Injections
Databases don’t always use SQL. NoSQL , or Not Only SQL , databases are those that don’t use the SQL language. Unlike SQL databases, which store data in tables, NoSQL databases store data in other structures, such as key-value pairs and graphs. NoSQL query syntax is database-specific, and queries are often written in the programming language of the application. Modern NoSQL databases, such as MongoDB, Apache CouchDB, and Apache Cassandra, are also vulnerable to injection attacks. These vulnerabilities are becoming more common as NoSQL rises in popularity.
数据库并不一定使用 SQL 语言。NoSQL(非关系型数据库)是指不使用 SQL 语言的数据库。和 SQL 数据库不同的是,NoSQL 数据库把数据存储在其他结构中,例如键值对和图形数据。NoSQL 查询语法是特定于数据库的,并且查询通常是在应用程序的编程语言中编写的。现代 NoSQL 数据库(如 MongoDB,Apache CouchDB 和 Apache Cassandra)也容易受到注入攻击的影响。随着 NoSQL 日益流行,这些漏洞变得越来越普遍。
Take MongoDB, for example. In MongoDB syntax, Users.find()
returns users that meet a certain criteria. For example, the following query returns users with the username vickie
and the password password123
:
以 MongoDB 为例。在 MongoDB 语法中,Users.find() 返回满足某个条件的用户。例如,以下查询返回用户名为 vickie,密码为 password123 的用户:
Users.find({username: 'vickie', password: 'password123'});
If the application uses this functionality to log in users and populates the database query directly with user input, like this:
如果应用程序使用此功能来登录用户并直接使用用户输入填充数据库查询语句,例如:
Users.find({username: $username, password: $password});
attackers can submit the password {$ne: ""}
to log in as anyone. For example, let’s say that the attacker submits a username of admin
and a password of {$ne: ""}
. The database query would become as follows:
攻击者可以提交密码{$ne: ""}以登录任何人。例如,假设攻击者提交 admin 用户名和密码{$ne: ""}。数据库查询将变为以下形式:
Users.find({username: 'admin', password: {$ne: ""}});
In MongoDB, $ne
selects objects whose value is not equal to the specified value. Here, the query would return users whose username is admin
and password isn’t equal to an empty string, which is true unless the admin has a blank password! The attacker can thus bypass authentication and gain access to the admin account.
在 MongoDB 中,$ne 选择值不等于指定值的对象。因此,查询将返回用户名为 admin 且密码不等于空字符串的用户,这是正确的,除非管理员有空白密码!攻击者因此可以绕过身份验证并访问管理员帐户。
Injecting into MongoDB queries can also allow attackers to execute arbitrary JavaScript code on the server. In MongoDB, the $where
, mapReduce
, $accumulator
, and $function
operations allow developers to run arbitrary JavaScript. For example, you can define a function within the $where
operator to find users named vickie
:
注入到 MongoDB 查询中也可以允许攻击者在服务器上执行任意 JavaScript 代码。在 MongoDB 中,$where、mapReduce、$accumulator 和 $function 操作允许开发者运行任意 JavaScript。例如,您可以在 $where 运算符中定义一个函数来查找名为 vickie 的用户。
Users.find( { $where: function() {
return (this.username == 'vickie') } } );
Say the developer allows unvalidated user input in this function and uses that to fetch account data, like this:
假设开发人员在此函数中允许未验证的用户输入,并使用该输入来获取账户数据,就像这样:
Users.find( { $where: function() {
return (this.username == $user_input) } } );
In that case, an attacker can execute arbitrary JavaScript code by injecting it into the $where
operation. For example, the following piece of malicious code will launch a denial-of-service (DoS) attack by triggering a never-ending while
loop:
在这种情况下,攻击者可以通过将恶意代码注入到$where 操作中来执行任意的 JavaScript 代码。例如,下面这段恶意代码将触发一个无限循环,从而发起拒绝服务(DoS)攻击:
Users.find( { $where: function() {
return (this.username == 'vickie'; while(true){};) } } );
The process of looking for NoSQL injections is similar to detecting SQL injections. You can insert special characters such as quotes ( ' "
), semicolons ( ;
), and backslashes ( \
), as well as parentheses ( ()
), brackets( []
), and braces ( {}
) into user-input fields and look for errors or other anomalies. You can also automate the hunting process by using the tool NoSQLMap ( https://github.com/codingo/NoSQLMap/ ).
寻找 NoSQL 注入的过程类似于检测 SQL 注入。您可以在用户输入字段中插入特殊字符,例如引号('“),分号(;)和反斜杠(\),以及括号(()),方括号([])和大括号({}),并查找错误或其他异常。您还可以使用 NoSQLMap 工具(https://github.com/codingo/NoSQLMap/)自动化搜索过程。
Developers can prevent NoSQL injection attacks by validating user input and avoiding dangerous database functionalities. In MongoDB, you can disable the running of server-side JavaScript by using the --noscripting
option in the command line or setting the security.javascriptEnabled
flag in the configuration file to false
. Find more information at https://docs.mongodb.com/manual/faq/fundamentals/index.html .
开发人员可以通过验证用户输入和避免使用危险的数据库功能来防止 NoSQL 注入攻击。在 MongoDB 中,您可以通过在命令行中使用--noscripting 选项或将配置文件中的 security.javascriptEnabled 标志设置为 false 来禁用服务器端 JavaScript 的运行。更多信息请访问 https://docs.mongodb.com/manual/faq/fundamentals/index.html。
Additionally, you should follow the principle of least privilege when assigning rights to applications. This means that applications should run with only the privileges they require to operate. For example, when an application requires only read access to a file, it should not be granted any write or execute permissions. This will lower your risk of complete system compromise during an attack.
此外,在分配应用程序权限时,应遵循最小特权原则。这意味着应用程序应仅以其操作所需的权限运行。例如,当应用程序仅需要读取文件的访问权限时,不应授予任何写入或执行权限。这将降低您在攻击期间受到完全系统妥协的风险。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论