[HNCTF 2022 WEEK2]Canyource
暑假在家是真的容易颓废,刚开学先写一道简单的rce找回一下之前的状态。
<?php
highlight_file(__FILE__);
if(isset($_GET['code'])&&!preg_match('/url|show|high|na|info|dec|oct|pi|log|data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['code'])){
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);}
else
die('nonono');}
else
echo('please input code');
?>
进去直接给出了我们源代码,接下来我会对代码进行一个比较详细的解析。
首先给出了我们两个条件选项,全部绕过才能在eval进行一个漏洞利用。
if(isset($_GET['code'])&&!preg_match('/url|show|high|na|info|dec|oct|pi|log|data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['code']))
第一个条件isset()函数检测我们是否使用get的方式提交code参数,并且!在PHP中用于取反布尔值。所以使用!preg_match()函数来过滤掉部分参数。
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code']))
第二部分则是使用preg_replace()函数进行一个内容匹配并且替换。
原型是preg_replace($pattern, $replacement, $input);
在 $input中匹配$pattern的内容,匹配到利用$replacement内容替换$pattern内容。
[^\W]+
:匹配一个或多个字母、数字或下划线。即,匹配字母、数字和下划线的连续序列
\(和\):匹配了()
(?R)?
:匹配一个递归的子表达式。(?R)
表示递归地应用整个正则表达式。?
表示这是可选的。这意味着模式可以匹配一次或多次,或完全不匹配。
我将在这个例子中,让大家对其有一个初步的了解:
例子:abc(def(ghi))
1.先是匹配 abc
作为 [^\W]+
2.匹配 ()作为\(和\)
3.匹配 def(ghi)
作为递归的内容 ,当匹配完外层的abc(def(ghi))就会再次对def(ghi)再次进行一个关于[^\W]+和
\(以及\)进行一个匹配。我理解的意思就是对外面进行匹配后在对()内进行匹配直到完全匹配结束。
两种方法:
一:使用随机文件读取
payload:
?code=readfile(array_rand(array_flip(scandir(pos(localeconv())))));
localeconv()返回一个包含与本地化设置相关的信息的关联数组,例如货币符号、小数点符号等。
pos() 函数用于返回数组中的第一个元素 。
scandir()函数返回指定目录中的文件和目录列表 。
array_flip() 函数将数组的键和值交换 。
array_rand()函数从数组中随机选择一个键。
readfile()函数读取并输出指定文件的内容。
总结的内容就是这样。
localeconv()
:
返回一个包含与本地化设置相关的信息的关联数组,例如货币符号、小数点符号等。
pos(localeconv())
:
pos()
函数用于返回数组中的第一个元素。由于localeconv()
返回一个数组,pos(localeconv())
会返回这个数组中的第一个元素。
scandir(pos(localeconv()))
:
scandir()
函数返回指定目录中的文件和目录列表。这里的参数是pos(localeconv())
的返回值,也就是localeconv()
返回数组的第一个元素,因此这个元素应该是一个目录的路径。
array_flip(scandir(pos(localeconv())))
:
array_flip()
函数将数组的键和值交换。因此,如果scandir(pos(localeconv()))
返回一个文件和目录的列表,那么array_flip()
会将这些文件和目录名作为键,原来的索引作为值。
array_rand(array_flip(scandir(pos(localeconv()))))
:
array_rand()
函数从数组中随机选择一个键。这个键是随机选择的文件或目录名,因为我们先将scandir()
的结果进行了键值翻转。
readfile(array_rand(array_flip(scandir(pos(localeconv())))))
:
readfile()
函数读取并输出指定文件的内容。由于array_rand()
随机选择了一个文件名,这个文件名会被传递给readfile()
,所以readfile()
会输出这个随机选择文件的内容。
多几次要出现以下情况就读取到flag了
flag在源代码中
二: 利用变量
payload:
?code=eval(end(current(get_defined_vars())));&wllm=system(%27cat%20f*%27);
get_defined_vars():来查看当前作用域中所有变量的名称和它们的值
current()
是 PHP 的一个内置函数,用于获取数组中当前元素的值。
end()
函数用于将数组的内部指针移动到数组的最后一个元素,并返回该元素的值end(current(get_defined_vars()))应该一直返回false。对于这次试题我猜测是因为后台只有一个文件目录,所以才可以使用。