[ZJCTF 2019]NiZhuanSiWei 1
进入环境,得到源代码
观察源代码,发现:
我们需要绕过两个点
首先是 get传参 ,让text=welcome to the zjctf
在这里我们需要用到 php的伪协议 data://
data:// — 数据流读写
- 条件:allow_url_fopen=on;allow_url_include=on
- 作用:作为一个封装器对进行数据流读写
- 使用格式:data://text/plain,[code]
eg: data://text/plain,base64,[code]
构造payload前半部分:
?text=data://text/plain;welcome to the zjctf
然后在源代码中 过滤掉了flag,但在下文中注释到了useless.php
所以我们可以尝试再次使用php伪协议 filter
,对该文件进行读取
php://filter
- 条件:allow_url_fopen=off/on;allow_url_include=off/on
- 作用:用过滤器读写数据流 读写文件
- 用法:格式为php://filter/[write] or [read]=[过滤器]/[resource]=[文件路径]
至此我们就可以构造 payload的后半部分
file=php://filter/read=convert.base64-encode/resource=useless.php
将payload组合起来 我们进行访问,
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=php://filter/read=convert.base64-encode/resource=useless.php
访问获得一串baase64 编码 解码查看情况
发现两个重点:
一个是 php 中使用了 魔术手法
一个是 代码中使用了反序列化
本来这两个也没什么 但在php代码中 同时使用了两者 而且 反序列化中参数 用户可控 这就造成了反序列化漏洞
我们在编写代码的时候 储存的数据类型(对象)往往随着程序的终止而终止,而有些情乱下需要对象的状态保存下来
这时候就用到了序列化,将对象以二进制的状态保存下来
序列化后对象的格式:
对象类型:对象名长度:“对象名”:对象成员变量个数:{变量1类型:变量名1长度:变量名1; 参数1类型:参数1长度:参数1; 变量2类型:变量名2长度:“变量名2”; 参数2类型:参数2长度:参数2;… …}
对象类型:Class:用O表示,Array:用a表示。
变量和参数类型:string:用s表示,Int:用i表示,Array:用a表示。
序列符号:参数与变量之间用分号(;)隔开,同一变量和同一参数之间的数据用冒号(:)隔开。
读取序列化对象的操作就是反序列化 (unserialize())
作用:把序列化后的字符串转化为对象,恢复原本对象后用于程序或代码的各种操作。
.
如果 反序列化对象中存在魔术方法 而且魔术方法中的代码或者变量用户可控,就可能产生反序列化漏洞,
根据反序列化后不同的代码可以导致各种攻击,如代码注入、SQL注入、目录遍历等等。
.
__toString(): 直接输出对象引用时自动被调用;
了解上面之后,我们也就能够构造payload了
password=O:4:"flag":1:{s:4"file";s:8:"flag.php";}
将我们新建的payload跟在后面即可获得flag
注:在这时候,我们就不需要访问useless.php文件 所以在构造payload访问的时候,记得要将php的filter的伪协议删去,仅仅让file=useless.php 来达到绕过的目的
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}