本篇文章针对于DVWA靶场的impossible级别进行代码审计,基于DVWA的每一个漏洞的impossible级别,从整体思路上出发,进行代码审计,最后对其总结。从中可以学习到一些漏洞的防御手法,并且对漏洞有更深入的理解。
文章目录
- 暴力破解
- 整体思路
- 代码审计
- 总结
- 命令注入
- 整体思路
- 代码审计
- 总结
- CSRF
- 整体思路
- 代码审计
- 总结
- 文件包含
- 整体思路
- 代码审计
- 总结
- 文件上传
- 整体思路
- 代码审计
- 总结
- SQL注入
- 整体思路
- 代码审计
- 总结
- SQL盲注
- 整体思路
- 代码审计
- 总结
- 弱会话IDs
- 整体思路
- 代码审计
- 总结
- XSS-DOM
- 整体思路
- 代码审计
- 总结
- XSS-Reflect
- 整体思路
- 代码审计
- 总结
- XSS-Store
- 整体思路
- 代码审计
- 总结
- JavaScript Attacks
- Insecure CAPTCHA
- 整体思路
- 代码审计
- 总结
- DVWA代码审计总结
暴力破解
整体思路
注意:一切sql执行都使用预编译。
- 首先通过token令牌校验,防止CSRF攻击
- 对用户传入的数据做好消毒处理
- 定义防御规则:登录失败次数,用户锁定时间,用户是否锁定
- 先查询用户登录信息,判断用户是否被锁定
- 用户没被锁定,才使用预编译sql校验用户与密码
- 如果登录成功,就更新用户登录失败次数为0,并且继续登录成功的逻辑
- 如果登录失败,就增加用户登录失败次数,只能在过了锁定时间之后才能再次登录
- 最后都要更新用户最后登录的时间
代码审计
-
token校验
使用checkToken()函数进行校验防止CSRF
-
数据消毒:对username与password进行过滤
使用stripslashes()函数删除数据的反斜杠,mysqli_real_escape_string()函数对输入的数据进行过滤,如果遇到特殊符号就进行转义,防止sql注入
-
定义登录失败限制次数,上锁时间,账户锁定状态
-
预编译获取用户登录信息
-
检查登录用户,判断失败登录次数是否大于规则定义的失败次数,如果大于,并且锁定时间还没到,那就上锁,否则就不锁定。
-
预编译查询,匹配用户名密码
-
判断用户是否符合登录条件
-
登录失败,添加登录失败次数
总结
暴力破解的impossible级别,
在代码语法上,主要使用了token校验防止CSRF攻击,并且对于一起sql语句都使用预编译的方式执行。
在代码逻辑上添加了登录规则,失败登录次数,用户锁定规则。
命令注入
整体思路
- token校验防止CSRF攻击
- stripslashes()函数对数据消毒
- 通过点分割,在程序中对IP进行拼接
- 通过php_uname来区分操作系统
- 通过shell_exec()对不同的操作系统执行不同的ping命令
代码审计
-
token校验,防止CSRF攻击
-
数据消毒,使用stripslashes()函数将用户输入的数据消除反斜杠,使用explode()函数,以点作为分隔符分割数组
-
筛选数据,使用shell_exec()函数进行ping命令执行
通过is_numeric()函数来判断用户输入的数据是否是IP
php_uname( ‘s’ )函数来获取操作系统名称
stristr() 函数搜索字符串在另一字符串中的第一次出现,判断
windows NT
是否出现在操作系统的名称描述中shell_exec()函数,用户执行命令,并返回执行结果,如果命令执行错误,将会返回NULL,与exec()函数相比,shell_exec()返回值更详细,因为exec()返回值是执行结果的最后一行,不过他和shell_exec()函数一样,如果命令执行错误,也返回NULL。
总结
命令注入的impossible级别:
在代码语法上,加入token校验。
在逻辑上对数据进行消毒,然后借助程序来拼接有效IP,从而防止用户输入的非法数据被执行。
CSRF
整体思路
- token校验
- 数据消毒
- 二次认证
代码审计
-
token校验,防止CSRF攻击
-
获取用户输入的数据,其中会让用户输入当前密码,进行二次校验,有效提高了CSRF利用难度
-
数据消毒。通过stripslashes()函数将数据的反斜杠删除,通过mysqli_real_escape_string()函数,对数据的特殊符号进行转义。
-
校验当前旧密码
-
对确认密码进行校验
总结
CSRF的impossible级别
防御分两步:
添加token校验:让客户端每次发起修改密码请求的时候都要携带服务端返回的token,这样就能有效避免攻击者伪造这个修改密码的请求,以为攻击者是无法获取服务端响应的token值的
二次校验:想要修改密码,就必须先输入旧密码,这样能加大攻击者伪造请求的难度,如果攻击者不知道密码,就无法完成请求伪造。
文件包含
整体思路
- 文件包含可能导致信息泄露,恶意文件执行
- 所以在符合业务的场景下,通过白名单的手段,规定只能包含服务器特定文件
代码审计
- 通过if来限制包含文件,只能包含服务器中特定的文件,类似于白名单限制,如果包含的文件不是规定的文件,则程序会输出错误
总结
文件包含的impossible级别。
通过指定可以包含的文件,达到白名单的限制效果
防止了攻击者使用file协议,相对路径来包含服务器敏感文件,比如日志等信息,也能防止攻击者使用http协议,https协议让服务器包含恶意的文件。
文件上传
整体思路
- 文件上传需要确定上传的路径,因此要定义文件的上传路径
- 文件的名字使用随机的唯一id,从而让攻击者无法获取到图片位置信息,无法访问
- 使用白名单的方式判断文件是否合法
- 通过getimagesize()函数判断是否是真的图片,底层是利用文件头来判断的
- 二次渲染,避免图片马,防止了程序的其他漏洞利用导致图片马被解析执行,比如文件解析漏洞,文件包含漏洞:通过imagecreatefromjpeg()等函数重新生成新的图片
代码审计
核心函数解释:
-
strrpos() 函数查找字符串在另一字符串中最后一次出现的位置。此函数是区分大小写的,与strripos()函数相反,strripos()函数不区分大小写
-
sys_get_temp_dir() :返回临时文件路径
-
ini_get(): 获取一个配置选项的值,成功是返回配置选项值的字符串,null 的值则返回空字符串。如果配置选项不存在,将会返回 FALSE
-
uniqid() :基于以微秒计的当前时间,生成一个唯一的 ID
-
getimagesize():是PHP中用于获取图像的大小和格式的函数,底层利用文件头来判断文件类型。它可以返回一个包含图像的宽度、高度、类型和MIME类型的数组。可以从本地获取,也可以从远程获取。如果目标地址是图片,就会返回数组,否则返回FALSE并产生一条 E_WARNING 级的错误信息。
-
imagecreatefromjpeg() 函数用于从 JPEG 文件或 URL 创建一个新图象,将新图像放入内存中,后面需要通过imagedestroy()函数释放内存中的图像资源
-
imagejpeg():
1、imagejpeg()函数是php图像处理中的一个重要函数,用于将图像保存为jpeg格式文件。
2、imagejpeg()函数的语法如下:
bool imagejpeg ( resource $image [, string $filename [, int $quality ]] )
其中, i m a g e 是图像资源, image是图像资源, image是图像资源,filename是保存的文件名,$quality是保存质量,取值范围从0-100,默认为75。
-
imagedestroy():释放内存中的图像资源
-
getcwd():获取当前目录
-
rename():函数用于重命名文件或目录,可以用于移动文件
语法:
bool rename ( string $source , string $target )
参数:
- $source:必需,表示源文件或目录的名称。
- $target:必需,表示目标文件或目录的名称。
返回值:
在成功时返回TRUE,失败时返回FALSE。 -
file_exists():检查文件或目录是否存在
-
unlink():删除文件
-
token校验
-
获取上传的文件对象信息
其中通过strrpos()函数获取最后一个点的位置,并使用substr()函数截取,最终获得上传的文件后缀
-
定义文件上传目标路径
sys_get_temp_dir()返回临时文件路径,ini_get()从php的配置文件中获得临时文件路径
-
判断文件是否合法,并二次生成图片到内存中,使用imagejpeg()将图片保存到临时文件中
-
移动临时文件到目标路径
总结
文件上传impossible级别。
代码逻辑:
通过白名单的形式来限制文件上传的类型,然后利用getimagesize()函数判断上传的文件是否是真的图片,并且使用imagecreatefromjpeg()函数基于原来的图像创建一个新图像存到内存中,从而防止图片马的上传,然后用imagejpeg()将内存中的图片保存到临时文件。
总的来说通过白名单严格限制了上传文件类型,然后通过二次渲染防止了图片马,避免程序中出现的文件包含漏洞或者文件解析漏洞对图片马进行解析执行。
SQL注入
整体思路
- 通过
is_numeric()
函数,判断用户传参是否为数字,是的话才进行查询 - 使用预编译的方式,执行sql
- 确保执行sql返回的结果为1才会返回数据
代码审计
总结
sql注入的防御,主要是对参数进行过滤,在这个靶场中,id为数值,所以使用了is_numeric()
函数对传的参数进行了校验。
sql语句的执行使用prepare函数,进行预编译处理,能有效的防止sql注入
最后取数据的时候,确保sql查询结果为1条数据,才返回数据,如果多条数据,就不返回。
SQL盲注
整体思路
- token校验
- 参数校验,只有数字才会进行数据库查询逻辑
- sql查询时使用预编译
代码审计
-
token校验,防止CSRF攻击
-
参数校验,确保参数是数字类型才会执行,才会进入sql语句执行的逻辑,sql执行需要经过预编译处理
总结
SQL盲注的impossible级别。
SQL盲注和普通的SQL注入的防御差不多。
都是先token校验,然后进行参数校验,对sql语句执行前进行预编译处理,从而能完全避免sql注入。
弱会话IDs
整体思路
cookie是用户登录凭证,cookie泄露之后,可以让攻击者越权访问程序,然而cookie是由服务端设置给客户端的,如果设置的cookie有规律,就会被攻击者猜测cookie,从而绕过登录
所以生成cookie的方式需要复杂,不易猜测。
这里就使用了mt_rand()函数,随机生成字符串并且使用time()函数生成时间戳,与字符串 impossible
进行拼接,拼接好之后使用sha1()函数进行加密,从而让攻击者无法猜测到session值,从而无法越权访问。
代码审计
$cookie_value采用随机数+时间戳+固定字符串"Impossible",再进行sha1运算,完全不能猜测到dvwaSession的值
总结
弱会话IDs 的 impossible级别。
为了防止攻击者获得cookie用户凭证。
所以服务器需要设置足够复杂的cookie。
可以使用随机数,加上sha1加密,让攻击者无法猜测cookie,从而无法越权访问应用程序。
XSS-DOM
整体思路
XSS-DOM类型的特点就是客户端调用了document对象,能够动态更新网页。DOM型从效果上来说也属于反射型XSS,是通过修改页面的DOM节点形成的XSS。在 HTML DOM (Document Object Model) 中 , 每一个元素都是 节点
只要对用户在url中输入的内容不做解码,让内容直接显示在网页中,就不会被JS解析。
代码审计
服务端没有代码,主要是在客户端处理,看到如下源代码,由于我们在url中输入的任何符号,都会被url编码,以下源代码在渲染我们输入的值的时候,因为没有进行解码,所以即使我们输入了xss注入的攻击代码,也无法被客户端执行。
总结
DOM型XSS从效果上看属于反射型。
在这个靶场中,我们是通过url注入xss代码的,所以我们的特殊符号会自动编码为url编码,
如果客户端对用户的输入不做解码,将会以url编码的形式展示在网页中,从而不会被JS执行导致XSS攻击。
XSS-Reflect
整体思路
使用htmlspecialchars()函数,将预定义字符转为HTML实体,防止浏览器将特殊符号作为HTML元素,而仅仅是将它作为字符串,从而不会影响标签结构,就不会被JS解析。
代码审计
htmlspecialchars()这个函数的的功能:是把预定义的字符&、"、'、<、>转换为 HTML 实体,防止浏览器将其作为HTML元素。这种做法可以直接避免XSS攻击
总结
htmlspecialchars()函数可以完全避免XSS注入。
XSS-Store
整体思路
- 数据消毒
- 将输入的数据变为HTML实体
- 数据库操作使用预编译处理
代码审计
-
token校验,防止CSRF攻击
-
数据消毒
使用htmlspecialchars()将输入转为HTML实体
使用stripslashes()函数过滤反斜杠,mysqli_real_escape_string()函数对特殊符号进行转义
-
对数据库操作进行预编译处理
通过prepare与占位符,进行预编译处理数据库操作
总结
对于存储型XSS,和其他两种类型的相同点就是使用htmlspecialchars()函数将输入的内容转为HTML实体,将一些预定义的符号当做字符串处理。
由于存储型XSS涉及到了数据库操作,为了保证数据库安全,防止SQL注入,所以需要对数据库的操作进行预编译处理。
JavaScript Attacks
JS攻击级别
在前几个级别中,都是对前端暴露出的JS代码进行分析,在hign级别中出现了JS混淆,可以通过这个网站:http://deobfuscatejavascript.com/#将混淆的js代码进行处理,还原为正常的js代码。
由于JS是在客户端的,如果攻击者看了JS源码之后,理解了其中的代码逻辑,如果具有漏洞的话,就可以通过控制台去执行JS代码中的函数,从而达到利用目的。因此对于JS攻击,理论上是没有impossible级别的。
impossible级别
参考链接:https://blog.csdn.net/Sheng_GuoFu/article/details/128651797
大致意思就是:你永远不能相信来自用户的任何输入,而且必须对此做出防备,但你又不能阻止用户的输入,因为这样可能会干扰网站的正常使用,所以压根就不存在Impossible级别。
Insecure CAPTCHA
整体思路
- token校验,预防CSRF攻击
- 验证逻辑不进行分步验证,一步验证到位,防止直接跳步绕过验证
- 要求输入旧密码,进行二次校验,预防CSRF等攻击
代码审计
-
token校验,防止CSRF攻击
-
数据消毒
stripslashes()去除反斜杠,mysqli_real_escape_string()给预定义符号转义
-
检查验证码
-
判断验证码是否失败
-
旧密码校验,更新密码
总结
不安全的验证码impossible级别。
主要是防止攻击者绕过校验,一般出现的都是逻辑漏洞。
在这个级别中,取消了分步校验的逻辑。
通过二次校验,检查旧密码输入的是否正确,防止CSRF,近源等攻击。
校验要修改的密码与确认密码。
DVWA代码审计总结
通过对DVWA靶场impossible级别的代码审计,发现大多数防御措施有很多共性。都使用了token校验,从根本上防止了CSRF攻击。
在逻辑处理之前,都会对数据进行消毒,其中的常用函数是stripslashes(),删除字符串中的反斜杠,mysqli_real_escape_string()函数对特殊符号进行转义。或者做白名单限制,遇到需要与数据库交互的逻辑,都会使用预编译处理。
如果在业务中,具有将用户的数据回显到浏览器的场景,大概率会使用到htmlspecialchars()函数,将输入转为HTML实体,从根源上阻止了XSS攻击。