前提
pikachu平台搭建
BurpSuite 抓本地包的方法
参考)
- 核心是设置浏览器允许本地(localhost 127.0.0.1)使用代理
代理服务器
- 我这里用 SwitchyOmega 插件来管理代理设置,原先设置的Burpsuite代理,默认是不代理本地主机的
- 需要将不代理地址列表清空
允许劫持localhost
火狐浏览器搜索
about:config
搜索首选项名称
network.proxy.allow_hijacking_localhost
- 将 false 改为 true
- 注意:当需要Bp获取请求包时,再将 config 改为 true(如点击登录按钮等)
1 暴力破解
1.1 基于表单的暴力破解
- 打开Bp,先进入登录页,任意输入用户名及口令,更换为本地代理,然后将 network.proxy.allow_hijacking_localhost 改为 true,点击 Login
- 获取请求,CTRL + I 发送至 Intruder 模块,添加变量,攻击类型改为 Cluster bomb。并分别为用户名密码设置 payload,设置线程(这里设为 50)
- start attack,根据响应包长度找到两个成功登录的用户名及口令,Done!
1.2 验证码绕过(on server)
- 打开 Bp,在输入框中填写任意用户名和口令,然后输入相应的验证码,选择本地代理并将 network.proxy.allow_hijacking_localhost 改为 true,用 Bp 截获请求。CTRL + R 发送到 Repeater,Go,试试验证码
- 当我们输入页面显示的验证码会提示,用户名或密码不存在
- 任意修改用户名及密码,都会得到这个提示,而修改验证码则会提示
- 因此得到结论:后台并不会刷新验证,我们得到的验证码持续有效,因此 CTRL + I 发送到 Intruder 模块进行爆破。
- 与上文同理可得两个账号
1.3 验证码绕过(on client)
- 操作步骤同上一节服务端绕过
- 与之不同之处,从 Repeater 开始,本次实验即使输错验证码,甚至删除验证码参数都不会提示验证码错误或验证码为空,均提示
在响应中可以看到,验证码生成与验证是通过前端 JS 实现,只有在前端通过网页登录才会进行验证。用 Bp 直接 POST 参数,是不会进行验证的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37<script language="javascript" type="text/javascript">
var code; //在全局 定义验证码
function createCode() {
code = "";
var codeLength = 5;//验证码的长度
var checkCode = document.getElementById("checkCode");
var selectChar = new Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');//所有候选组成验证码的字符,当然也可以用中文的
for (var i = 0; i < codeLength; i++) {
var charIndex = Math.floor(Math.random() * 36);
code += selectChar[charIndex];
}
//alert(code);
if (checkCode) {
checkCode.className = "code";
checkCode.value = code;
}
}
function validate() {
var inputCode = document.querySelector('#bf_client .vcode').value;
if (inputCode.length <= 0) {
alert("请输入验证码!");
return false;
} else if (inputCode != code) {
alert("验证码输入错误!");
createCode();//刷新验证码
return false;
}
else {
return true;
}
}
createCode();
</script>删除验证码参数,或使用任意验证码按上一节同样的方式进行爆破即可,Done!
1.4 token防爆破?
先验知识
什么是 Token: 第一次登录后,服务器生成一个 Token 并返回给客户端,客户端需要带着这个 Token请求数据
使用相同的 token POST 数据会提示 csrf token error
- 重新获取一个新的 token (重来一次)
- Sniper:(一个 payload)根据字典中数据,依次对标记的变量进行替换。相当于将标记的 n 个变量当作一整块,每次从字典中拿出 n 个数据依次分给这 n 个变量,直到字典用尽。
- Battering Ram: (一个 payload)被标记的 n 个变量的变化是同步的。从字典中取出的一个数据会被同步给到这 n 个变量。
- Pitchfork: (多个 payload)交叉替换,比如 n 个变量,会对应有 n 个 payload,每个变量的取值只能从它自己的 payload 中获得,当最少 payload 被遍历完,整个攻击过程就结束。
- Cluster bomb: (多个 payload)n 个变量就是O(N^n) 的穷举。
具体步骤
- 前面的步骤跟上文同理,直到发送到 Intruder
- 由于 Token 需要从上一次响应中取值,所以攻击类型和线程是需要注意的。攻击类型需为 Pitchfork 交叉替换,线程数只能为 1
- 因为交叉替换会同步遍历 payload,所以如果标记用户名、口令、token 三个变量,那有可能可登录的账号密码不能被遍历到,所以这里的实验只标记了口令和 token,用户名直接猜测是 admin
- payload 1 选择口令字典;payload 2 由于需要从上一个响应中获取,所以选择 Recursive grep 通过递归搜索
- Options 中需要添加数据提取
- 点击 Refetch response,搜索 token 选中后点击 ok
- start attack, Done!
2 XSS
先验知识
分类
反射型,存储型,DOM 型
如何避免
- 输入过滤
- 输出转义
2.1 反射型xss(get)
- 任意输入,发现 GET 参数 message 在变。也可 F12 查看元素得到这个信息
- 由于有长度限制,所以直接在 URL 中进行修改使 message 为
<script>alert(1)</script>
1 | http://localhost:8081/pikachu/vul/xss/xss_reflected_get.php?message=%3Cscript%3Ealert(1)%3C/script%3E&submit=submit# |
- 页面弹窗,DONE!
2.2 反射性xss(post)
需要登录,所以先进行弱口令爆破
易得
admin/123456
登录进去,直接在输入框中输入<script>alert(1)</script>
,即刻弹窗
- 在 BurpSutie 中修改 POST 请求体的内容再 Foward 道理是一样的。Done!
2.3 存储型xss
与反射型 XSS 不同,存储型 XSS 会被存储到服务器,是持久型的。也就是说只要成功注入 XSS,用户每次访问都会有效(XSS)
- 在留言框,留言
<script>alert(1)</script>
即可
2.4 DOM型xss
2.4.1 什么是 DOM
Document Object Model(文档对象模型)将 HTML 文档表达为树结构。它定义了所有 HTML 元素的对象和属性,以及访问它们的方法。通过 HTML DOM,树中的所有节点均可通过 JavaScript 的一些 API 进行访问,所有 HTML 元素均可被修改,也可以创建或删除节点。
2.4.2 具体步骤
- 点击按钮,显示 “what do you see?” 的链接,这个链接指向的就是当前当前页
- F12 找到这个元素
- 所以,我们在输入框输入的数据会通过 DOM 方法
getElementById()
获取到并初始化 str,第二个getElementById("dom")
则会找到 dom 元素,并通过 innerHTML 属性获取元素内容。这样的话,我们可以通过对输出值的修改闭合前面的单引号然后注入。
1 | '><img src="#" onmouseover="alert('xss')"> |
- 原来的 dom 节点就会被修改为
- 只要鼠标在图片上放移动,就会触发 XSS
2.4.3 拓展
- 为了加深理解,我们可以自己闭合标签后,在页面新增输入框等元素
1 | '><input id="hh" name="hh" type="text" value> |
- 新增按钮
1 | '><input id="hh" name="hh" type="button" value="hh"> |
2.5 DOM型xss-x
- 点击按钮后出现一个超链接
- 点击该链接,发现 dom 节点
- 再往前看看源码,所以首先会从输入框获取字符串,然后以 “text=” 进行分割后,选第二个元素;再将这个元素中的加号换成空格,然后再送给
getElementById
处理。
- 可以输入字符串后,直接在浏览器控制台跑一下代码,看看其变化
1 | '><img src="#" onmouseover="alert('xss')"> |
- 所以道理是一样的,只不过受影响的节点是点击按钮后的第二条链接处
- DONE!
2.6 xss盲打
核心就是尝试
- 弱口令爆破可以得到 admin/123456,进入后台可以看到已有的留言
- 尝试在留言框写入
1 | <script>alert(1)</script> |
- 管理员重新进入后台时,就会触发 xss
当然姓名框也可以进行 xss
2.7 xss之过滤
- 大小写绕过
1 | <Script>alert(1)</Script> |
2.8 xss之htmlspecialchars
2.8.1 htmlspecialchars
PHP: htmlspecialchars - Manual
&
(& 符号)&
"
(双引号)"
,除非设置了 ENT_NOQUOTES'
(单引号) 设置了 ENT_QUOTES 后进行转换:#039;
(如果是 ENT_HTML401) ,或者'
(如果是 ENT_XML1、 ENT_XHTML 或 ENT_HTML5)。
<
(小于)成为<
>
(大于)成为>
2.8.2 具体步骤
- 输入测试代码
<script>alert(1)</script>
后,查看源码,发现<>
被替换掉了
1 | <p class='notice'>你的输入已经被记录:</p><a href='<script>alert(1)</script>'><script>alert(1)</script></a> </div> |
- 输入单引号进行测试
1 | p class='notice'>你的输入已经被记录:</p><a href='''>'</a> |
- 所以可以通过只是用单引号的方法注入,成功
1 | ' onclick='alert(1)' |
- 查看源码,这段代码是完成注入的
2.9 xss之href输出
- 输入测试
1 | &"'<> |
- 查看网页源码
1 | <a href='&"'<>'> 阁下自己输入的url还请自己点一下吧</a> |
- 都被转换过了,所以尝试 JavaScript 伪协议 参考
1 | javascript:alert(1) |
- 查看一下源码,成功注入
- 点击链接,即可触发 XSS
2.10 xss之js输出
- 随便输入个 123 查看源码
1 | <script> |
- 发现输入会直接插入到 JavaScript 中,所以
'</script>
闭合前面的<script>
,然后就可以写入 XSS 了
1 | </script><script>alert(1)</script> |
3 CSRF
先验知识
XSS与CSRF的区别 - 抖音2020 - 博客园 (cnblogs.com)
浅谈CSRF攻击方式 - hyddd - 博客园 (cnblogs.com)
- CSRF(跨站请求伪造)与 XSS 的区别:
- CSRF 需要用户先登录网站A,获取 cookie。XSS 不需要登录
- CSRF 是利用网站A本身的漏洞,去请求网站A的api。XSS 是向网站 A 注入 JS代码,然后执行 JS 里的代码,篡改网站A的内容。
总的来说就是:攻击者盗用用户的身份,假装用户发起恶意请求
3.1 CSRF(get) login
- 输入 lili/123456 点击登录并截获请求
1 | GET /pikachu/vul/csrf/csrfget/csrf_get_login.php?username=lili&password=123456&submit=Login |
- 登陆后信息
- 修改个人信息并抓包(修改手机号为 123)
1 | GET /pikachu/vul/csrf/csrfget/csrf_get_edit.php?sex=&phonenum=123&add=&email=&submit=submit |
- 这时,只要 lili 不退出登录,且点击链接
1 | http://localhost:8081/pikachu/vul/csrf/csrfget/csrf_get_edit.php?sex=boy&phonenum=0000&add=wakanda&email=hh@hh.com&submit=submit |
就可以成功修改信息
3.2 CSRF(post) login
- 登录 lili/123456,修改地址并抓包
1 | POST /pikachu/vul/csrf/csrfpost/csrf_post_edit.php |
- 构造表单提交 POST 请求。参考 https://blog.csdn.net/weixin_44940180/article/details/107140456
1 |
|
- 将此 HTML 文件放到
..\xampp\htdocs
目录,在不退出 lili 账户的情况下,访问http://localhost:8081/csrf_post.html
- 点击按钮,信息被成功修改
3.3 CSRF Token login
- 首先登录 lili/123456,然后修改个人信息,用 BP 截获
1 | GET /pikachu/vul/csrf/csrftoken/token_get_edit.php?sex=0&phonenum=0&add=0&email=0&token=7161660c8beacb171d715542422&submit=submit |
- 这里与上文 CSRF(get) login 区别在于,这里是带 token 的,由于 token 是随机的,所以这里我们无法和上文一样构造 URL 让用户点击完成信息修改了。
4 SQL 注入
先验知识
形成的原因
前端数据传到后台处理时,未做严格过滤,导致传入的数据拼接到 SQL 语句后,被执行
常见注入方法
- UNION 注入
- 方法:使用 UNION 语句把数据展示到页面
- 报错注入
- 方法:利用 MySQL 执行后的报错信息
- 布尔盲注
- 方法:看不到直接的数据,但是可以通过注入,看页面是否有变化来推测出数据。
- 案例:比如在参数后添加类似
and 1=1
这种,如果页面正常返回,说明and
是成功执行的,那这个参数就存在 SQL 注入漏洞
- 时间盲注
- 方法:通过 sleep() 函数,利用 IF 函数或 AND OR 的短路特性和执行时间判断 SQL 攻击的结果
- 案例:
id='1' or sleep(1);
如何避免
- 使用预编译语句,绑定变量
- 检查数据类型(比如邮箱,日期等按严格的格式输入)
- 使用安全的编码函数(比如 OWASP ESAPI 中实现的encodeForSQL)
4.1 数字型注入
- 选择任一数字,点击查询,发现 URL 并未出现对应的参数,所以应该是通过 POST 传参
- 用 BurpSuite 截获数据包
1 | POST /pikachu/vul/sqli/sqli_id.php |
CTRL + R
发送到 Repeater 发送,得到响应
- 改参数
id=2
重发一次,结果为
id=2-1
重发,结果为
- 所以,是进行数值运算的,因此存在数字型注入
- 通过联合查询,查询数据库所有表名
1 | id=-1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() |
- 查询
users
对应字段名
1 | id=-1 union select 1,group_concat(column_name) from information_schema.columns where table_name='users' |
4.2 字符型注入
- 输入
1'
报错
1 | You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1''' at line 1 |
- 猜测查询语句为
1 | SELECT * FROM 表名 WHERE username='输入的用户名'; |
- 所以需要先闭合前面的单引号,然后注释后面的单引号,输入以下内容
1 | 1' or 1=1# |
关于 or 1=1
的解释
- 如上述输入后,原本的查询语句就会变成
1 | SELECT * FROM 表名 WHERE username='1' or 1=1#'; |
- 这条语句条件判断会被后面的
or 1=1
影响,因为一直为真,所以任意 username 都会被查询
4.3 搜索型注入
- 只要输入用户名的一部分,就可以找到该用户并输出其信息
- 所以可以想到 MYSQL 中 用通配符过滤 ,也即使用
%
来表示任意字符出现任意次数,所以查询语句的后半段大致应该是username = '%用户名的部分字符%'
- 因此,与上一例子同理,可以先输入任意字符然后闭合前面的单引号,并注释后面的单引号
1 | 1' or 1=1# |
4.4 xx型注入
- 看一下源码
\xampp\htdocs\pikachu\vul\sqli
1 | //这里没有做任何处理,直接拼到select里面去了 |
- 所以需要闭合前面的小括号
1 | 1') or 1=1# |
拓展1 联合查询
UNION
如何查询字段数?
- 利用
order by 1
测试,以字符型注入为例,可以输入
1 | 1' order by 1# |
1 | 1' order by 2# |
返回结果均为
- 当输入
1 | 1' order by 3# |
报错
因此可以确定有两个字段
可以通过 UNION 查询,获取数据库名
database()
,版本version()
,用户user()
。当然如果只需要查询其中某个信息,可以用任意数值代替其他字段,如
1 | 1' union select database(),2# |
information_schema
a. 获取表名 table_schema
- 根据上文,我们已经可以知道数据库名称为 pikachu ,可以构造查询语句,查询 table_schema
1 | 1'union select 1,table_name from information_schema.tables where table_schema=database()# |
b. 获取字段名 table_name
- 由上文可知,有一个表为 user,所以里面极可能存放用户名及密码等重要信息
- 同理可以构造查询语句,查询表 users 中的字段
1 | 1' union select 1,column_name from information_schema.columns where table_name='users'# |
c. 获取内容
1 | 1' union select username,password from users# |