PHP 无参函数实现 RCE

前言

今天做了一道 RCE 的题目 , 里面有一个 preg_replace() 过滤

其中 , [^\W] 包含所有的数字 , 字母 以及 下划线 , 而 (?R) 则是 DEELX 正则表达式扩展语法 中的递归表达式 , 表示递归引用整个模式 .

这代表着我们可以输入递归的字符串( 比如 abc(); , abc(def()); , abc(def(ghi())); , ... ) , 但若我们输入一个带参数的字符串 , 正则匹配将会失败 . 简单的来说 , 就是我们可以递归输入无参函数 , 但不能输入有参函数 .

想了很久都没有什么绕过思路 , 没有参数怎么传输我要执行的代码? 于是去 Google , 然后找到了 一叶飘零师傅的 PHP Parametric Function RCE 这篇文章 .

过去没有接触过 PHP 无参数实现RCE 的题目 , 这里学习记录一下 , 供以后参考~


getenv()

PHP 中有众多的超全局变量

注意 $_ENV 这个超全局变量 , 我们可以使用 getenv() 函数来获取一个包含当前环境变量的数组 .

可以在 phpinfo() 中查看能获取到的环境变量

可以看到 , 很多时候服务器的环境变量不止一个 , 多个环境变量放在一个数组中 , 那么如何从这个数组中取出一个指定的值呢 ?

array_rand()

array_rand() 方法用于从一个数组中随机( 伪随机 )取出一个或者多个单元 , 并返回随机条目的一个或者多个

但这样只能获得数组的 , 可以通过 array_flip() 函数获取数组的

array_flip()

该函数用于交换数组中的值和键 , 返回一个 相互交换后的数组

getenv() , array_rand() , array_flip() 这三个函数配套使用 , 可以爆破出数组中需要的内容 . 比如下面就拿到了 " PATH " 变量对应的值 .

但在实际环境中 , $_ENV都是空数组,主要是为了安全起见,不让它获取操作系统信息 !


getallheaders()

getenv() 可以获取到所有环境变量的列表 , 但我们有更好利用的点 ,比如获取到 HTTP Header 的信息 .

在 Apache 环境中 , 可以使用 getallheaders() 函数来返回 HTTP Header 信息

这里我们可以自定义一些 HTTP Header

可以使用 end() 函数将数组的最后一项提取出来

这样我们就拿到了自定义的 HTTP Header 字段, 并且该字段可以被我们自由使用 , 比如通过 eval() 函数执行 .

也可以利用该函数完成 RCE ~


get_defined_vars()

上述的 getallheaders() 函数仅能在 Apache 环境下完成 RCE , 那么如果目标中间件不是 Apache( 比如说 Nginx ) , 该如何完成 RCE 呢?

get_defined_vars() 函数返回由所有已定义变量所组成的多维数组 . 这些变量包括环境变量 , 服务器变量 , 用户定义的变量 .

该函数会回显全局变量

$_GET

因此我们可以利用全局变量( 比如 $_GET ) 来实现 RCE ,但这里 $_GET 不在数组最后一位 , 因此无法用 end() 函数 , 但我们还可以使用 current() 函数获取数组的第一个单元 .

和之前的思路一样 , 在 URL 中添加想要执行的参数即可

由于 Payload 在 URL 中 , 所以别忘了对其中某些特殊字符进行编码后再提交

但大多数网站都会对 $_GPC( $_GET , $_POST , $_COOKIE ) 做全局过滤 , 因此大部分时候我们要从 $_FILES 入手 .

$_FILES

构造如下 Python 脚本

我们将文件名作为将要执行的 Payload , 这样就不需要再使用 array_flip() 函数了 . 查看输出 , 发现我们的 Payload 位于 $_FILES变量中 .

接下来我们的任务就是从中提取出写好的 Payload 了 , 比如这里可以使用 current(current(end())) 这组函数来拿到指令

然后通过 eval() 函数执行 , 就可以拿到 RCE 了~


$_COOKIE

$_COOKIE 的利用方式与 $_FILE 非常类似 , 我们常使用 session_id() 函数来设置或获取当前会话的 SessionID . 这里 SessionID 是可以利用的

虽然 SessionID 仅支持字母和数字 , 不支持一些符号( 比如 " ; " 或者 " ' " ) . 但这都不是事 , 可以利用编码( 比如 HEX )来绕过这个限制

构造如下 Python 脚本

这里发现 $_COOKIE 字段不在数组的开头或者末尾 , 因此 current()函数 和 end()函数是不能用了 , 我去 PHP 官网上看了下 PHP数组函数 , 找到半天也没有找到适合的函数 .

其实方法还是有的 , 比如你可以参考 boring_code 这道题 , 其中给出了很多思路 , 这里就不深入下去了

但大佬给出了另外一种方式 : session_start() 函数

 session_start() 函数常常用于创建新会话 . 但如果通过 GET , POST  , 或者 COOKIE 提交了 Session ID, 则会重用现有 Session . 该函数返回值为 True / False

在重用了现有会话后 , 可以使用 session_id() 来获取该会话的Session_ID

在获取了 Session_id 后 , 仅需要对其进行 hex2bin 解码 , 就可以拿到之前构造的 Payload 了

通过 eval() 即可拿到 RCE !


Read Directly

可以尝试直接读取本地文件 , 其中最大的问题就是如何切换目录来遍历目录 .

  1. getcwd() : 获取当前工作目录

  2. scandir() : 查看当前目录下的内容

  3. dirname() : 跳转上级目录

  4. readfile() : 读取文件

当然 , 使用该方法读取文件的局限性很大 , 比如只能从当前目录递归遍历到根目录 , 而无法读取其他的目录的内容 . 基本上用处不太大~


总结

这种无参数完成 RCE 的方式还是蛮有意思的 , 需要在多研究下~

并且这里可以做一个总结

getchwd() : 返回当前工作目录

scandir() : 返回指定目录中的文件和目录的数组

dirname() : 返回路径中的目录部分

chdir() : 改变当前的目录

readfile() : 输出一个文件

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

pos() : current() 的别名

next() : 将内部指针指向数组中的下一个元素,并输出

end() : 将内部指针指向数组中的最后一个元素,并输出

array_rand() : 返回数组中的随机键名,或者如果您规定函数返回不只一个键名,则返回包含随机键名的数组

array_flip() : 用于反转/交换数组中所有的键名以及它们关联的键值

chr() : 从指定的 ASCII 值返回字符

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

getenv() : 获取一个环境变量的值(在7.1之后可以不给予参数)
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇