虽然一直在打比赛,但是很少自己亲自尝试写相关的漏洞,这里从头到尾的具体到代码的学习一次,记录一下。
SQLI:
本次WEB实验采用kali2的环境。由Linux的一些原因数据库并未采用MySQL,而是采用了其分支MariaDB,因此经过简单的MariaDB的学习,开始此实验。
首先将环境配置好,并简单的写了一个用于测试SQLI的网页。
毫无WAF的代码如下:
然后我们先在数据库内添加我们能需要查询的东西
添加完成后
整型注入
字符注入
稍微修改代码
再进行字符注入
布尔盲注
接下来按照课本要求简单复现:
布尔型注入查询数据库版本
此处没有回显,说明语句判断结果为false,则查询语句会等价为?x=20 and 0,则不执行查询操作。
没有回显,继续尝试。
有回显,此时语句等价为?x=20
and 1,则执行查询,并回显。确认数据库版本号为1。
关于盲注,我们再进行测试的时候一般辅助以脚本,因为其操作过程重复度太高。这里简单写了一个脚本来进行上面的盲注测试。脚本如下:
延时注入
回车之后,查询会先查询x=20,之后因为and存在会将后面的语句也执行,延时三秒回显,确实可以达到延时效果。将and改为or也可以实现延时,但是会有结果回显。
报错型注入
这类型的注入效果实际得看回显内容的多少,像是我们为了实验而搭建的代码一般不会加过滤或是除了查询目标值回显之外,根本就不会去写回显语句。为了实现报错注入,使用了一个mysqli_error()函数。效果如下:
在?x=20后面加一个单引号会导致引号不能成对而无法匹配,导致数据库报错并通过函数回显错误信息。上面仅仅是一个函数,我们就能通过其报错知道两个重要信息:1、数据库类型为MariaDB。2、具体过滤了什么字符。(WAF测试)
综合注入攻击
结合书上的方法,简单模拟一次攻击。
由以上报错、布尔、延时注入我们已经知道数据库为MariaDB,基本没有字符过滤(这是当然的了),语句可以通过关键词组合查询语句,那我们可以使用union进行联合查询。
那我们开始尝试爆库
我搭建的sqli,所以我们爆sqli表
继续爆字段
过程没什么好解释的,就是一些MariaDB的基本查询语句。
修复以上漏洞
接下来进行以上漏洞修复。
首先,对于报错注入,我们可以将代码中的回显语句完全去掉。
或者是限制其回显,这样可以极大提高盲注的难度。
对于有用户需求的客户端或者是站点来说,回显过于具体会方便入侵,但是完全没有回显对于用户来说也不是好事,这样会使用户体验很差。因此我们一般采取限制其回显内容来防范这类问题。
之后,对于用户能控制的语句和变量,我们需要对其进行过滤来防止恶意语句的传递(WAF)。
首先我们知道攻击者可能会通过关键词组合攻击语句。那么我们可以对关键词进行过滤,并过滤部分函数。再严格一些,限制输入类型(过滤大小写)。加入简单正则后的代码如下:
效果如下:
会将非法字符进行匹配从而阻止非法语句进入数据库。但是这并不是过滤,而是在发现非法字符之后直接阻止了程序的执行。
将代码逻辑整理一下,最终代码如下:
除了上述方法之外,我们还可采用MySQL降权与预编译语句的方法。我上面的代码中的dvwau是拥有所有权限的一个角色。将这样的角色交给用户并不是很安全,因此我们在数据库中操作,将dvwau的权限拿掉,只留下部分即可。预编译的话,是预先定义好sql的查询语句,先用?符号占位,待用户传入参数之后,即可执行。这与一般的查询不一样,预编译是先将语句进入
数据库中,之后再传入参数。而现在的做法大多数为等查询语句拼接好之后,再传入数据库执行。形式如下:
不同数据库有不同的查询语句,那么随之而来的问题肯定也不一样,mysql是这样的,其他的不一定是这样。网上搜索了一番,还发现了一种防御方法也就是现在认为比较安全的方法PDO。
PHP 数据对象(PDO) 扩展为PHP访问数据库定义了一个轻量级的一致接口。实现 PDO 接口的每个数据库驱动可以公开具体数据库的特性作为标准扩展功能。 注意利用
PDO 扩展自身并不能实现任何数据库功能;必须使用一个具体数据库的 PDO 驱动来访问数据库服务。
PDO 提供了一个数据访问抽象层,这意味着,不管使用哪种数据库,都可以用相同的函数(方法)来查询和获取数据。 PDO不提供数据库抽象层;它不会重写 SQL,也不会模拟缺失的特性。如果需要的话,应该使用一个成熟的抽象层。
从 PHP 5.1开始附带了 PDO,在 PHP 5.0 中是作为一个 PECL 扩展使用。PDO 需要PHP
5核心的新OO特性,因此不能在较早版本的 PHP 上运行。其实PDO也才采用了预编译的方法来增强查询语句的安全。
PDO划清了用户输入与代码的界限,不会将用户输入与查询语句进行拼接。关于PDO的使用方法,如下:
Xss:
先写个简单的前端和后端的代码
Xss_Reflected
效果如下:
简单测试一下<script>alert(1)</script>
成功弹窗,此类行为反射型XSS漏洞。
Xss_Stored:
我们稍微修改一下代码,进行存储型XSS漏洞测试。我拿前面自己写的SQLI页面来用一用。
效果如下:
来测试一下。
成功弹窗,在Stored型XSS中,我们一旦注入恶意代码成功,那么每次我们访问此页面时服务器都会将我们注入数据库的恶意代码拿出来当作script再解析并执行一遍,这就是Stored型的特点。
Xss_DOM:
我们再稍微修改一下代码,尝试实现DOM型漏洞
效果如下:
测试一下
成功弹窗,利用成功。到这里基础XSS代码都复现完了。
漏洞修复:
我们开始考虑如何修复这些漏洞。
反射型与DOM型漏洞都为非持久化漏洞,而Stored型持久化漏洞。
DOM
& Reflected
首先针对反射型漏洞与DOM型漏洞,其利用原理是在用户进行数据提交的时候,服务器端将传入的数据当作html元素进行了解析,html元素可包括js脚本,也就意味着可以执行恶意代码。如果需要对其进行修复的话,在当我们需要向html标签之间插入不可信数据的时候,我们可以对其进行HTML Entity 编码以及特殊字符的过滤,比如将空格转换为  ;、<转换为< ;等,在进行转义时,不能单纯的将“转为\”而是要用/\”这种方式,或者将除了字母和数字以外的东西全部使用十六进制的编码方式进行编码。Php提供了htmlspecialchars()函数将输入进行转义变为HTML实体编码。
DOM:
REFLECT:
效果如下:
DOM:
REFLECTED:
更进一步,因为XSS操作大多数为盗取用户cookie,我们可以设置一个HttpOnly,避免脚本对cookie的访问。
Stored:
对于STORED型,我们采取方法也可以是加上HTML实体转义,也要在进行数据库操作的时候进行防范,我可以加入上面的SQLI的代码。
代码如下:
效果如下:
不过总的来说,没有绝对安全的防御,当我们引入HTML实体的时候又会带来新的XXE、SSRF等问题,不过这不属于课程作业的内容,有机会再做XXE攻击和防御测试。
DVWA漏洞攻击实现:
由于是闯关,我就当作写WP来写了。
LOW:
Brute Force:
用Burpsuit截包,然后发送至intruder:
锁定爆破点
选择字典
或是进行爆破
Start Attack即可。
都可以得到密码:password。
SQLI:
这里先随手一试1’ or 1=1 –+’
没有任何过滤,简单的闭合单引号并且注释掉后面的语句即可。
SQLBI:
先输入1‘ and substring(version(),1,1)>5 –’,回显
再输入1‘ and substring(version(),1,1)<5 –’,
逐次尝试<4,3,2,1,到1的时候回显
则再尝试1‘ and substring(version(),1,1)=1 –’,回显
则可确定数据库版本为1。
用同样的方法则可以逐个确定表名,列名,字段名。贴上脚本
Xss_Reflected:
输入<script>alert(‘1’)</script>
Xss_Stored:
先输入
提交之后可以看到
Medium:
Brute Force:
提交密码之后发现页面回显很慢,猜测可能有延迟,一看代码果不其然。
可以修改burp suit里intruder的option设置进行调整。
SQLI:
看到页面变成固定列表了,不能自己输入值了
看源码
发现传参方式变为了POST,那么我们抓包修改id的值再提交。
成功,接下来的步骤与之前的一致,可以使用联合注入,不再重复。
SQLBI:
和SQLI很像,估计也是需要抓包,那我们用burp尝试。
发现可以进行盲注,基本没有过滤。
用脚本不好实现,我们可以用利用burp的intruder功能,先进行如下设置
标记爆破点
对payload进行设置,上面我们需要爆破数据库版本,所以设置payload类型为数字, 范围为1~128
Start attack的结果
会发现有个响应的长度明显比其他的短,那是因为长度为4996的那个响应里包含了exists而长度为5002的里面包含了MISSING所以有长度差别。我们需要的是正确判断的句子,也就是exists那个响应的判断语句是对的,也就是说payload为1时,回显exists,即数据库版本为1。
可利用此特性进行列名表名字段名的爆破。步骤仍然相同,不再重复。
Xss_Reflected:
随手尝试<script>alert(‘1’)</script>
看回显可能过滤了script头
尝试下大小写<Script>alert(‘1’)</Script>
成功回显,只过滤了script,没有用正则匹配。
XSS_Stored:
由上可知先<Script>alert(1)</Script>
发现不行,看来不是大小写问题
再尝试<Scr<Script>ipt>alert(1)</Sc<Script>ript>
还是不行
看看源代码
发现对name只做了简单的过滤处理,将<script>置换为空。而message进行了html 实体转义的操作,很难进行XSS
但是Name可以进行XSS
但是发现输入有长度限制,我们将限制去掉
再输入<scr<script>ipt>alert(1)</sc<script>ript>
成功弹窗
High
Brute Force:
提交密码发现又有延时,感觉还加了别的什么,但是一看源码,随机延时0~3秒……
2333
我们依旧可以修改burp suit里intruder的option设置进行调整,使其等待最大延时时间即可。
SQLI:
这题还给了单独的输入界面
测试了一下感觉你输入什么当前页面就会回显什么,并且在主页面输出回显结果
但是只有一行
看一下源代码
发现没有加什么东西,只加了一个limit来限制输出而已。
我们可以用#或者–+注释掉,再用‘闭合掉单引号这里可以用联合注入配合group_concat多行回显。
1’or 1=1 group_concat(database()),1 from users #
接下来的步骤与前面难度的大致一样,不再重复。
SQLBI:
怎么回事,怎么又是似曾相识的样子
按照High里的SQLI的过滤来尝试
果然又是用#将后面的语句注释掉,再用单引号闭合limit再配合group_concat即可。
发现注入点,我们可以利用。本来以为它是将data加密后存进cookie中再进行提交的,这样抓包就无法修改,我们进行盲注的时候还需要猜测cookie的结构,很麻烦。但是…
哦豁,他没有这么做,那我们继续用intruder来尝试盲注。
锁定爆破点
设置payload
Start attack
发现长度都一致,很奇怪,仔细看发现因为
HTTP头指向的是
这样不管怎么回显都是
我们修改下请求头使其指向回显的页面
再试试,发现成功了
同样的方法可以爆库爆列名。感觉手动测试的时候有延迟,可能还存在一些东西,看看源代码。
发现了sleep,那就意味着基于时间的盲注会不准确,因此我一开始尝试布尔注入避开了弯路。
XSS_Reflected:
随手尝试<Script>alert(1)</Script>
回显有点奇怪,猜测应该是正则掉了script,试试别的标签iframe、img之类的
例如<img src=1 onerror=alert(1)>
成功,查看下源代码,发现循环正则匹配script以及/i,/i参数的意思是无视大小写,这样使得script头很难通过正则,但是我们可以用别的标签头。
XSS_Stored:
随手在name和messagebox都尝试<Scr<script>ipt>alert(1)</Sc<script>ript>
这回显似曾相识,name试试别的标签头
……,和上一题很像嘛。
看看源代码
果不其然。
简单RE
此次实验分别采用了WINDOWS10和XP系统。因为我一直以来写的都是web不怎么会逆向,作业的完成寻求了队友的帮助,非常感谢。做个记录。
Crackeme
首先将文件拖入IDA
找到跳转点
跟进
发现这里的函数有个逻辑,条件不满足就跳转(JZ),即密码不正确就跳转
我们将jz改为jnz
这时候逻辑会反过来,输入错误密码就不会跳转,输入正确的密码就会跳转。我们运行运行一下exe。
输入错误密码
输入正确密码
完成。
Overflow1
先拖进IDA,设置断点,随便输入几个数,密码错误。
找到断口,发现一个函数
跟进一下,大致逻辑为将a1与1234567进行比较,返回一个值给v3,之后将a1复制给v2,然后将逻辑值返回给v3。
我们发现v2与v3差了8个字,那么此处可以拿来作为溢出点利用,有如下操作:
覆盖前
输入大于1234567的8位字符或数字都可以
覆盖成功
可以看到exe
成功
Overflow2
与上面一样操作,先设置断点,找断口
发现一个函数
变成了文件输入,需要一个叫password的文件
写个文件
运行
找到我们刚才输入的东西
回过来看上面的函数,好像这里可以用来溢出
我们可以写一个文件用8个字符将其填充覆盖,之后再使用4个字符来覆盖main
运行结果
完成。
Overflow3
拖进IDA发现buffer由8字节变成44字节,所以我们可以用44个字节来覆盖authenticated 低字节中的 1在调试的过程中得到buffer的起始地址为OX0019FB2C.
让程序弹出一个消息框只需要调用
Windows 的 API 函数 MessageBox,而调用API函数就需要将之翻译成机器代码,用十六进制填入password.txt中。用Dependency Walker工具来获取函数MessageBoxA的入口地址。可以通过动态链接库user32.dll在系统中加载的基址和MessageBoxA在库中的偏移相加得到。于是可得user32.dll 的基地址为
0x69E00000,MessageBoxA 的偏移地址为 0x00080CD0。基地址加上偏移地址就得到了 MessageBoxA 在内存中
的入口地址 0x69E80CD0。第 53~56 字节填入 buffer 的起址
0x0019FAA0,其余的字节用 0x90填充.
运行exe
成功。
作业到此结束。