《白帽子讲Web安全》13-14章

发布于:2024-11-29 ⋅ 阅读:(29) ⋅ 点赞:(0)

《白帽子讲Web安全》13-14章

13、应用层拒绝服务攻击

13.1、DDOS简介

分布式拒绝服务(Distributed Denial of Service, DDOS)利用合理的请求造成资源过载,导致服务不可用。
分布式拒绝服务攻击,将正常请求放大了若干倍,通过若干个网络节点同时发起攻击,以达成规模效应。这些网络节点往往是黑客们所控制的“肉鸡”,数量达到一定规模后,就形成了一个“僵尸网络”。大型的僵尸网络,甚至达到了数万、数十万台的规模。如此规模的僵尸网络发起的DDOS攻击,几乎是不可阻挡的。

SYN flood是一种最为经典的DDOS攻击,利用了TCP协议设计中的缺陷。
对抗SYN flood的主要措施有SYN Cookie/SYN Proxy、safereset等算法。SYN Cookie的主要思想是为每一个P地址分配一个“Cookie”,并统计每个IP地址的访问频率。如果在短时间内收到大量的来自同一个IP地址的数据包,则认为受到攻击,之后来自这个IP地址的包将被丢弃。

13.2、应用层DDOS

DDOS攻击更为可怕,因为今天几乎所有的商业Anti-DDOS设备,只在对抗网络层DDOS 时效果较好,而对应用层DDOS攻击却缺乏有效的对抗手段。

13.2.1、CC攻击

CC 攻击的原理非常简单,就是对一些消耗资源较大的应用页面不断发起正常的请求,以达到消耗服务端资源的目的。在 Web应用中,查询数据库、读/写硬盘文件等操作,相对都会消耗比较多的资源。

应用层 DDOS 攻击是针对服务器性能的一种攻击,那么许多优化服务器性能的方法都或多或少地能缓解此种攻击。比如将使用频率高的数据放在memcache 中,相对于查询数据库所消耗的资源来说,查询memcache所消耗的资源可以忽略不计。

13.2.2、限制请求频率

最常见的针对应用层 DDOS攻击的防御措施,是在应用中针对每个“客户端”做一个请求频率的限制。
通过IP地址与Cookie定位一个客户端,如果客户端的请求在一定时间内过于频繁,则对之后来自该客户端的所有请求都重定向到一个出错页面。

13.2.3、道高一尺,魔高一丈

从以下几个方面着手防御应用层DDOS:

  • 首先,应用代码要做好性能优化。合理地使用memcache就是一个很好的优化方案,将数据库的压力尽可能转移到内存中。此外还需要及时地释放资源,比如及时关闭数据库连接,减少空连接等消耗。
  • 其次,在网络架构上做好优化。善于利用负载均衡分流,避免用户流量集中在单台服务器上。同时可以充分利用好CDN和镜像站点的分流作用,缓解主站的压力。
  • 最后,也是最重要的一点,实现一些对抗手段,比如限制每个IP地址的请求频率。

13.3、验证码的那些事儿

验证码是互联网中常用的技术之一,它的英文简称是CAPTCHA (Completely AutomatedPublic Turing Test to Tell Computers and Humans Apart,全自动区分计算机和人类的图灵测试)。

除了直接利用图像相关算法识别验证码外,还可以利用Web实现上可能存在的漏洞破解验证码。

    • 实现原理:验证码的验证过程,是比对用户提交的明文和服务器端Session里保存的验证码明文是否一致。
    • 存在漏洞:验证码消耗掉后SessionID未更新,导致原有的SessionID可以一直重复提交同一个验证码。
    • 实现原理:将所有的验让码图片生成好,以哈希过的字符串作为验证码图片的文件名。在使用验证码时,则直接从图片服务器返回已经生成好的验证码,这种设计原本的想法是为了提高性能。
    • 存在漏洞:攻击者可以事先采用枚举的方式,遍历所有的验证码图片,并建立验证码到明文之间的一一对应关系,从而形成一张“彩虹表”,这也会导致验证码形同虚设。
    • 修补方式:验证码的文件名需要随机化,满足“不可预测性”原则。

13.4、防御应用层DDOS

做好人机识别、在Web Sever这一层做些防御

在人机识别方面:

  1. 判断HTTP头中的User-Agent字段来识别客户端。
    • 存在的缺陷:可能会被客户端篡改。
  2. 让客户端解析一段JavaScript,并给出正确的运行结果。因为大部分的自动化脚本都是直接构造HTTP包完成的,并非在一个浏览器环境中发起的请求。因此一段需要计算的JavaScript,可以判断出 客户端到底是不是浏览器。类似的,发送一个flash让客户端解析,也起到同样的作用。
    • 存在的缺陷:有的自动化脚本是内嵌在浏览器中的“内挂”,就无法检测出来了。(“内挂”指的是嵌入在浏览器中的自动化脚本或插件。这些脚本或插件直接在浏览器环境中运行,因此能够模拟正常用户的行为)

在Web Server层做防御,其好处是请求尚未到达后端的应用程序里,因此可以起到一个保护的作用。

  1. 在Apache的配置文件中,有一些参数可以缓解DDOS攻击。比如调小Timeout、KeepAliveTimeout值,增加MaxClients值。
    • 存在的缺陷:这些参数的调整可能会影响到正常应用,因此需要视实际情况而定。
  2. “mod_qos”是Apache的一个Module,原理是限制单个IP地址的访问频率。
    • 存在的缺陷:面对单个IP地址或者IP地址较少的情况下,比较有用。
    1. 应用层DDoS攻击使用真实IP地址,攻击者的IP地址数量有限。2. 攻击者的IP地址在一定范围内轮询,单个IP地址的请求频率会增加。基于以上两点,Yahoo设计了一套算法,根据IP地址和Cookie信息计算客户端的请求频率并进行拦截。该系统在整体架构上有一台master服务器,集中计算所有IP地址的请求频率,并将策略同步到每台Web服务器上。
    • 存在的缺陷:未开源

13.5、资源耗尽攻击

除CC攻击外,还有一些利用Web Sever的漏洞或设计缺陷,直接造成拒绝服务。

13.5.1、Slowloris攻击

Slowloris攻击原理是以极低的速度往服务器发送HTTP请求。由于WebSever对于并发的连接数都有一定的上限,因此若是恶意地占用住这些连接不释放,那么Web Sever的所有连接都将被恶意连接占用,从而无法接受新的请求,导致拒绝服务。

在正常的HTTP包头中,是以两个CLRF表示HTTP Headers部分结束的。

Content-Length: 42\r\n\r\n

由于Web Server只收到了一个\r\n,因此将认为HTTP Headers部分没有结束,并保持此连接不释放,继续等待完整的请求。此时客户端再发送任意HTTP头,保持住连接即可。

当构造多个连接后,服务器的连接数很快就会达到上限。此类拒绝服务攻击的本质,实际上是对有限资源的无限制滥用。

13.5.2、HTTP POST DOS

HTTP POST DOS的原理是在发送 HTTP POST包时,指定一个非常大的Content-Length值,然后以很低的速度发包,比如10~100s 发一个字节,保持住这个连接不断开。这样当客户端连接数多了以后,占用住了Web Server 的所有可用连接,从而导致DOS。本质也是针对Apache的MaxClients限制的。

要解决此类问题,可以使用Web应用防火墙,或者一个定制的Web Server安全模块。

13.5.3、Server Limit DOS

web Server对 HTTP包头都有长度限制,以Apache举例,默认是8192字节。也就是说,Apache所能接受的最大HTTP包头大小为8192字节(这里指的是 Request Header,如果是 Request Body,则默认的大小限制是2GB)。如果客户端发送的HTTP包头超过这个大小,服务器就会返回一个4xx错误,提示信息为:

Your browser sent a request that this server could not understand.Size of a request header field exceeds server limit.

假如攻击者通过XSS攻击,恶意地往客户端写入了一个超长的 Cookie,则该客户端在清空Cookie之前,将无法再访问该Cookie所在域的任何页面。这是因为Cookie也是放在HTTP包头里发送的,而 Web Server 默认会认为这是一个超长的非正常请求,从而导致“客户端”的拒绝服务。

要解决此问题,需要调整Apache配置参数LimitRequestFieldSize,这个参数设置为0时,对HTTP包头的大小没有限制。

通过以上几种攻击的介绍,我们了解到“拒绝服务攻击”的本质实际上就是一种“资源耗尽攻击”,因此在设计系统时,需要考虑到各种可能出现的场景,避免出现“有限资源”被恶意滥用的情况,这对安全设计提出了更高的要求。

13.6、一个正则引发的血案:ReDOS

ReDOS是一种代码实现上的缺陷

ReDOS (Regular Expression Denial of Service) 是一种通过精心设计的恶意输入,利用正则表达式的匹配过程来导致服务拒绝的攻击。ReDOS 攻击通常利用正则表达式引擎在处理复杂、低效正则表达式时的性能瓶颈,使得服务器需要大量时间和资源进行匹配,从而导致系统崩溃或变慢。

  • 原理
    ReDOS 攻击主要发生在使用“回溯算法”的正则表达式引擎中。当正则表达式中包含大量的“重复结构”(如嵌套的分组、多重或模式等)时,匹配输入时可能会触发指数级的回溯。例如,带有重复的 (a|aa)+(a+)+ 结构的正则表达式在匹配特定长字符串时,会因为回溯而导致计算时间急剧上升。


  • 考虑一个简单的正则表达式 (a|aa)+b,用来匹配一个由 aaa 组成的序列,最后以 b 结尾。如果用户传入 aaaaaaaaaaaaaaaaaaaaaaaaaaaa 这样没有结尾的 b,引擎会进行大量的回溯尝试,导致系统资源被耗尽。

  • 防御 ReDOS 的方法

    1. 限制正则表达式的复杂性:避免使用容易导致回溯的重复结构,比如 (a+)+(a|aa)+ 等。
    2. 使用非回溯正则表达式引擎:一些正则引擎(如 RE2)不使用回溯算法,因而不会受到 ReDOS 攻击的影响。
    3. 检测和过滤恶意输入:提前过滤或限制输入的长度和复杂度,降低攻击可能性。
    4. 设置超时:为正则匹配操作设置合理的超时时间,避免因为长时间计算导致的系统资源耗尽。

13.7、小结

  • 在本章中讲述了应用层拒绝服务攻击的原理和解决方案。应用层拒绝服务攻击是传统的网络拒绝服务攻击的一种延伸,其本质也是对有限资源的无限制滥用所造成的。所以,解决这个问题的核心思路就是限制每个不可信任的资源使用者的配额。
  • 在解决应用层拒绝服务攻击时,可以采用验证码,但验证码并不是最好的解决方案。Yahoo的专利为我们提供了更宽广的思路。
  • 在本章最后介绍了ReDOS这种比较特殊的拒绝服务攻击,在应用安全中需要注意这个问题。

14、PHP安全

14.1、文件包含漏洞

其原理就是注入一段用户能控制的脚本或代码,并让服务器端执行。“代码注入”的典型代表就是文件包含(File Inclusion)。文件包含可能会出现在JSP、PHP、ASP等语言中,常见的导致文件包含的函数如下。

PHP: include(), include_once(), require(), require_once(), fopen(), readfile(), ...
JSP/Servlet: ava.io.File(), java.io.FileReader(), ...
ASP: include file, include virtual,...

文件包含是PHP的一种常见用法,主要有四个函数完成

include()
require()
include_once()
require_once()

当使用这4个函数包含一个新的文件时,该文件将作为PHP代码执行,PHP内核并不会在意该被包含的文件是什么类型。所以如果被包含的是txt文件、图片文件、远程URL,也都将作为PHP代码执行。这一特性,在实施攻击时将非常有用。

要想成功利用文件包含漏洞,需要满足下面两个条件:
(1)include()等函数通过动态变量的方式引入需要包含的文件;
(2)用户能够控制该动态变量。

14.1.1、本地文件包含

能够打开并包含本地文件的漏洞,被称为本地文件包含漏洞(Local File Inclusion,简称LFI)。

include '/home/wwwrun/'.$file.'.php';

对于以上代码就存在LFI漏洞,用户能够控制参数file,当file值为"../../etc/passwd"时,PHP将访问/etc/passwd.php文件,为了使其正确的访问到/etc/passwd,还需要做一个截断操作。

  1. 00截断
    由于PHP内核是由C语言实现的,因此使用了C语言中的一些字符串处理函数。在连接字符串时,0字节(\x00)将作为字符串结束符。攻击者在最后加入一个0字节,就能截断file变量之后的字符串,即:

    l

    ../../etc/passwd\0
    

    通过Web输入时,只需UrlEncode,变成:

    ../../etc/passwd%00
    
  2. 构造长目录
    在一般的Web应用中,0字节用户其实是不需要使用的,因此完全可以禁用0字节,但目录字符串,在Windows下256字节、Linux下4096字节时会达到最大值,最大值长度之后的字符将被丢弃。可以通过./来构造:

    ./././././././././././././abc
    

    或者

    /abc
    

    或者

    ../1/abc/../1/abc/../1/abc
    

使用../../../来返回到上层目录中,这种方式又被称为“目录遍历”(Path Traversal),常见的目录遍历漏洞,还可以通过不同的编码方式来绕过一些服务器端逻辑。

防御方法

  1. PHP配置open_basedir
    open_basedir 的作用是限制在某个特定目录下PHP能打开的文件,其作用与safe_mode是否开启无关。
  2. 限定用户可以控制的变量值
    尽量避免包含动态的变量,尤其是用户可以控制的变量。一种变通方式,则是使用枚举,将$file值枚举出来,只处理这些值。
14.1.2、远程文件包含

如果PHP的配置选项allow_url_include为ON的话,则include/require函数是可以加载远程文件的,这种漏洞被称为远程文件包含漏洞(Remote File Inclusion,简称RFI)。

14.1.3、本地文件包含的利用技巧

远程文件包含漏洞之所以能够执行命令,就是因为攻击者能够自定义被包含的文件内容。因此本地文件包含漏洞想要执行命令,也需要找到一个攻击者能够控制内容的本地文件。有以下技巧:
(1)包含用户上传的文件。
(2)包含data:// 或 php://input等伪协议。
(3)包含Session文件。
(4)包含日志文件,比如Web Sever的access log。
(5)包含/proc/self/environ文件。
(6)包含上传的临时文件(RFC1867)
(7)包含其他应用创建的文件,比如数据库文件、缓存文件、应用日志等,需要具体情况具体分析。

(1)包含用户上传文件能否攻击成功,取决于文件上传功能的设计,比如要求知道用户上传后文件所在的物理路径,有时这个路径很难猜到。在本书“文件上传漏洞”一章中给出了很多设计安全文件上传功能的建议。

(2)伪协议如php://input等需要服务器支持,同时要求allow_url_include设置为ON。在 PHP5.2.0之后的版本中支持 data: 伪协议,可以很方便地执行代码,它同样要求allow_url_include为ON。

(3)包含Session文件的条件也较为苛刻,它需要攻击者能控制部分Session文件的内容。比如:

php ×|s:19: "<?php phpinfo(); ?>" 

PHP默认生成的Session文件往往存放在/tmp目录下,比如:

bash /tmp/ sess_SESSIONID 

(4)包含日志文件是一种比较通用的技巧。因为服务器一般都会往 Web Server 的access_log里记录客户端的请求信息,在 error_log里记录出错请求。因此攻击者可以间接地将PHP 代码写入到日志文件中,在文件包含时,只需要包含日志文件即可。
但需要注意的是,如果网站访问量大的话,日志文件有可能会很大(比如一个日志文件有2GB),当包含一个这么大的文件时,PHP进程可能会僵死。但 Web Server往往会滚动日志,或每天生成一个新的日志文件。因此在凌晨时包含日志文件,将提高攻击的成功性,因为此时的日志文件可能非常小。

如果 httpd 的配置文件和日志目录完全猜不到怎么办?如果PHP的错误回显没有关闭,那么构造一些异常也许能够暴露出 Web目录所在位置。此外,还可以利用下面的方法。

(5)包含/proc/self/environ是一种更为通用的方法,因为它根本不需要猜测被包含文件的路径,同时用户也能控制它的内容。

http://www.website.com/view. php?page=..../../../../proc/self/environ

包含/proc/self/environ文件,可能看到Web进程运行时的环境变量,在用户可以控制的地方注入代码以完成攻击。

以上这些方法,都要求PHP能够包含这些文件,而这些文件往往都处于Web目录之外,如果PHP配置了open_basedir,则很可能会使得攻击失效。

但PHP创建的上传临时文件,往往处于PHP 允许访问的目录范围内。包含这个临时文件的方法,其理论意义大于实际意义。根据RFC1867,PHP处理上传文件的过程是这样的:
在这里插入图片描述

(6)PHP会为上传文件创建临时文件,其目录在 php.ini的 upload_tmp _dir 中定义。但该值默认为空,此时在Linux下会使用/tmp目录,在 Windows下会使用C:\windows\temp目录。

该临时文件的文件名是随机的,攻击者必须准确猜测出该文件名才能成功利用漏洞。PHP在此处并没有使用安全的随机函数,因此使得暴力猜解文件名成为可能。在 Windows下,仅有65535种不同的文件名。

14.2、变量覆盖漏洞

14.2.1、全局变量覆盖

变量如果未被初始化,且能被用户所控制,那么很可能会导致安全问题。而在 PHP中,这种情况在register_globals为ON时尤其严重。

PHP中使用变量并不需要初始化,因此register_globals=ON时,变量来源可能是各个不同的地方,比如页面的表单、Cookie等。类似的,通过$GLOBALS获取的变量,也可能导致变量覆盖。

防范措施:

销毁全局变量必须使用$GLOBALS,如果实现代码关闭register_globals,则一定要覆盖superglobals。

14.2.2、extract()变量覆盖

extract()函数能将变量从数组导入当前的符号表,其函数定义如下:

int extract ( array $var_array [, int $extract_type [, string $prefix ]] )
  • $extract_type (可选):用于指定 extract() 如何处理数组中的键与当前作用域已有变量名冲突的情况。常用的选项包括:
    • EXTR_OVERWRITE:覆盖已有的同名变量(默认)。
    • EXTR_SKIP:跳过已有的同名变量,不覆盖。
    • EXTR_PREFIX_SAME:给同名变量加上指定的前缀。
    • EXTR_PREFIX_ALL:给所有变量加上指定的前缀。
    • EXTR_PREFIX_INVALID:只给无效或非法的变量名加前缀。

当用户可以控制传入 extract() 的数组时,他们可以通过构造特定的键名来覆盖已有的关键变量。比如,如果 PHP 程序在使用 extract() 时没有合理地限制输入,攻击者可以覆盖诸如 $is_admin$username 等变量,从而改变程序逻辑,进而进行攻击操作。

一种较为安全的做法是确定register_globals = OFF后,在调用extract()时使用EXTR_SKIP保证已有变量不会被覆盖。但extract()的来源如果能被用户控制,则仍然是一种非常糟糕的使用习惯。同时还要留意变量获取的顺序,在 PHP中是由php.ini中的variables_order所定义的顺序来获取变量的。

14.2.3、遍历初始化变量

常见的一些以遍历的方式释放变量的代码,可能会导致变量覆盖。

14.2.4、import_request_variables变量覆盖

import_request_variables()GET、POST、Cookie中的变量导入到全局,使用这个函数只需要简单地指定类型即可。其中第二个参数是为导入的变量添加的前缀,如果没有指定,则将覆盖全局变量。

14.2.5、parse_str()变量覆盖

parse_str()函数往往被用于解析URL的query string,但是当参数值能被用户控制时,很可能导致变量覆盖。

还有一些变量覆盖的方法,难以一次列全,但有以下安全建议:

  • 首先,确保register_globals =OFF。若不能自定义php.ini,则应该在代码中控制。
  • 其次,熟悉可能造成变量覆盖的函数和方法,检查用户是否能控制变量的来源。
  • 最后,养成初始化变量的好习惯。

14.3、代码执行漏洞

PHP 中的代码执行情况非常灵活,但究其原因仍然离不开两个关键条件:第一是用户能够控制的函数输入;第二是存在可以执行代码的危险函数。

14.3.1、“危险函数”执行代码

在 PHP中,能够执行代码的方.式远不止文件包含漏洞一种,比如危险函数 popen()system()passthru()exec()等都可以直接执行系统命令。

14.3.1.1、phpMyAdmin 3.4.3.1远程代码执行漏洞
14.3.1.2、MyBB 1.4 远程代码执行漏洞

从“找到敏感函数”到“成为一个代码执行漏洞”。

14.3.2、“文件写入”执行代码

在database.inc.php导入 zip文件时,存在写文件操作,但其对安全的判断过于简单,导致用户可以将此文件内容修改为PHP代码。

14.3.3、其他执行代码方式
  1. 直接执行代码的函数。eval()assert()system()exec()shell_exec()passthru()escapeshellcmd()pcntl_exec()等。
  2. 文件包含函数include()include_once()require()require_once()
  3. 本地文件写入。常见的有file_put_contents()fwrite()fputs()等。
    需要注意的是,写入文件的功能可以和文件包含、危险函数执行等漏洞结合,最终使得原本用户无法控制的输入变成可控。在代码审计时要注意这种“组合类”漏洞。
    • preg_replace的第一个参数如果存在/e模式修饰符,则允许代码执行。
    • 需要注意的是,即便第一个参数中并没有e模式修饰符,也是有可能执行代码的。这要求第一个参数中包含变量,并且用户可控,有可能通过注入/e%00 的方式截断文本,注入一个“/e”。
  4. 动态函数执行。用户自定义的动态函数可以导致代码执行。
  5. Curly Syntax。PHP的Curly Syntax 也能导致代码执行,它将执行花括号间的代码,并将结果替换回去。
  6. 回调函数执行代码。很多函数都可以执行回调函数,当回调函数用户可控时,将导致代码执行。
  7. unserialize()导致代码执行。unserialize()这个函数也很常见,它能将序列化的数据重新映射为PHP变量。但是unserialize()在执行时如果定义了_destruct()函数,或者是_wakeup()函数,则这两个函数将执行。unserialize()代码执行有两个条件:
    • 一是unserialize()的参数用户可以控制,这样可以构造出需要反序列化的数据结构;
    • 二是存在_destruct()函数或者_wakeup()函数,这两个函数实现的逻辑决定了能执行什么样的代码。

14.4、定制安全的PHP环境

除了熟悉各种 PHP漏洞外,还可以通过配置php.ini来加固PHP的运行环境。
PHP官方也曾经多次修改 php.ini的默认设置。在本书中,推荐 php.ini中一些安全相关参数的配置。

  1. register_globals
    register_globals=ON时,PHP不知道变量从何而来,也容易出现一些变量覆盖的问题。因此从最佳实践的角度,强烈建议设置register_globals=OFF ,这也是PHP新版本中的默认设置。

  2. open_basedir
    open_basedir可以限制PHP只能操作指定目录下的文件。这在对抗文件包含、目录遍历等攻击时非常有用。我们应该为此选项设置一个值。需要注意的是,如果设置的值是一个指定的目录,则需要在目录最后加上一个“/”,否则会被认为是目录的前缀。

    open basedir = / home /web/html/
    
  3. allow_url_include
    为了对抗远程文件包含,请关闭此选项,一般应用也用不到此选项。同时推荐关闭的还有allow_url_fopen

  4. display errors
    错误回显,一般常用于开发模式,但是很多应用在正式环境中也忘记了关闭此选项。

  5. log_errors
    在正式环境下用这个就行了,把错误信息记录在日志里。

  6. magic_quotes_gpc
    推荐关闭,它并不值得依赖(请参考“注入攻击”一章),已知已经有若干种方法可以绕过它,甚至由于它的存在反而衍生出一些新的安全问题。

  7. cgi.fix_pathinfo
    若 PHP 以CGI的方式安装,则需要关闭此项,以避免出现文件解析问题。

  8. session.cookie_httponly
    开启 HttpOnly。

  9. session.cookie_secure
    若是全站HTTPS则请开启此项。

  10. safe_mode
    PHP的安全模式是否应该开启的争议一直比较大。一方面,它会影响很多函数;另一方面,它又不停地被黑客们绕过,因此很难取舍。如果是共享环境(比如App Engine),则建议开启safe_mode,可以和 disable_functions 配合使用;如果是单独的应用环境,则可以考虑关闭它,更多地依赖于disable_functions控制运行环境安全。

  11. disable_functions
    disable_functions能够在PHP中禁用函数。这是把双刃剑,禁用函数可能会为开发带来不便,但禁用的函数太少又可能增加开发写出不安全代码的几率,同时为黑客获取 webshell提供便利。

14.5、小结


网站公告

今日签到

点亮在社区的每一天
去签到