这道题看了wp后也还是一脸懵逼的状态,但大致过程是了解了,知道了如何利用这个点去进行rce,至于里面的perl的exp,还有异或生成shell的python脚本,还得好好研究下,我太菜了!
这次主要参考了Y1ng师傅的wp和w4nder师傅的wp,把链接直接贴在下面吧,有些东西师傅们的博客里写的很清楚了,我也不再赘述
首先进题目是个计算页面,直接查看一下源码,发现有calc.php,访问后得到calc.php的源码
1 |
|
可以看到是过滤了a-z,[\x7f-\xff]以及一些符号,但是没有过滤位运算符|和&
所以根据这一点,就知道了这题的是通过php中位运算来进行rce达到命令执行的目的
关于如何取得字符
这里主要用的是php的一些特性,可以获得几个字符
这里就直接引用Y1ng师傅的解释,讲的非常明白
获得数字字符
我们可以得到任意数字,
(1)
仍是int,但是如果((1).(2))
(注意需要套一个括号否则出错)就会得到字符串“12”
之后再通过字符串截取即可得到单字符,PHP中可以使用大括号来完成,也是按照惯例,第一个字符编号是0,第二个是1,以此类推
获得部分字符
通过
NAN INF
以及科学计数法可以获得INAFE这5个字母,这样得到:但是得到的是float类型,同样使用大括号截取并不能得到对应的单字符,反而会报错并返回NULL
那我们还可以通过刚刚的方法,让两个数字做点运算然后加上括号包裹,再用
{}
截取,即可:
php中的位运算
php 位运算符 & (按为与)
将&两边的数值二进制化进行比较 两边分别8位数相互对应 都为1则为1否则为0
列: 1 & 2
1的二进制为 00000001
2的二进制为 00000010
00000000 16进制转回来是0
所以 1 & 2 打印就是 0;例: 15 & 8
15的二进制为 00001111
8的二进制为 00001000
00001000 16进制转回来是 8
所以 15 & 8 打印就是 8;php 位运算符 | (按位或)
将|两边的数值二进制化进行比较 两边分别8位数相互对应 有1就为1否则为0
列: 1 | 8
1的二进制为 00000001
8的二进制为 00001000
00001001 16进制转回来是9
所以 1 | 8 打印就是 9;
使用位运算获得更多字符
经过上面的运算,我们仅仅只获得了几个字符,那么如何获取更多字符呢?相信大家也都想到了,那就是套娃,用获得的字符进行位运算|和&
,然后再用位运算得到的字符再进行位运算,一直套娃,就可以获得几乎全部字符(不能说全部,根据大师傅说的还是有几个获取不了的),也就达到了rce的目的。这里也是贴一个cjm00n师傅的脚本
,可以直接把命令转化为位运算后的结果,以后遇到类似的也可以直接用
1 | #!/usr/bin/env python3 |
构造system(next(getallheaders()))执行命令
最后的命令执行我在ctfhub上打过去复现失败了,直接给命令显示出来了,不知道为啥,可能题目有改过吧,我太菜了,这里就直接用Y1ng师傅和w4nder师傅的结果吧
正常来讲,使用上面脚本将system(‘ls /‘)变为位运算的结果后打过去应该可以看到根目录下有一个/readflag文件
使用上面的脚本,再次构造system(‘/readflag’)的位运算形式,(打过去的时候记得要url编码一次,不然无法识别)会发现得到了一个需要计算结果才能得到flag的页面
再次引用Y1ng师傅的解释
这个之前在2019 *CTF包括前几天的De1CTF等都有出现,这东西实际上是运行在系统上,有几种解决办法,比如trap等等,但是前提是先获得一个交互式shell,于是我又构造了反弹shell结果没成功,去问了管理员说是靶机不能出网
还可以用php或者perl的exp一键打,但是Payload比较长,所以我们必须要构造一个webshell,弄一个可控参数,然后把我们的Payload放上去,于是很容易想到了这样的格式:
1 system(next(getallheaders()))
由于get请求有长度限制,所以我们无法将所有payload全部写在请求里,于是可以用system(next(getallheaders()))
来读取请求头中的内容作为命令执行
至于payload,这里我选择的的是w4nder师傅的的perl文件的形式
可以将payload写入/tmp下然后用perl执行,编码一下防止数据丢失
echo ‘IyEvdXNyL2Jpbi9lbnYgcGVybAogICAgICAgIHVzZSB3YXJuaW5nczsKICAgICAgICB1c2Ugc3RyaWN0OwogICAgICAgIHVzZSBJUEM6Ok9wZW4yOwogICAgICAgICR8ID0gMTsKICAgICAgICBteSAkcGlkID0gb3BlbjIoXCpvdXQyLCBcKmluMiwgIi9yZWFkZmxhZyIpIG9yIGRpZTsKICAgICAgICBteSAkcmVwbHkgPSA8b3V0Mj47CiAgICAgICAgcHJpbnQgU1RET1VUICRyZXBseTsKICAgICAgICAkcmVwbHkgPSA8b3V0Mj47CiAgICAgICAgcHJpbnQgU1RET1VUICRyZXBseTsKICAgICAgICBteSAkYW5zd2VyID0gZXZhbCgkcmVwbHkpOwogICAgICAgIHByaW50IFNURE9VVCAiYW5zd2VyOiAkYW5zd2VyXFxuIjsKICAgICAgICBwcmludCBpbjIgIiAkYW5zd2VyICI7CiAgICAgICAgaW4yLT5mbHVzaCgpOwogICAgICAgICRyZXBseSA9IDxvdXQyPjsKICAgICAgICBwcmludCBTVERPVVQgJHJlcGx5OwogICAgICAgICRyZXBseSA9IDxvdXQyPjsKICAgICAgICBwcmludCBTVERPVVQgJHJlcGx5Ow==’|base64 -d >/tmp/a.pl
可以看到上面是将写入文件的过程放在了Cache—Control这个请求头中,再利用system(next(getallheaders()))
来达到命令执行的目的
至于上面那串perl代码,base64解码后是这样的,perl代码我也不太懂,有空得研究下
1 | #!/usr/bin/env perl |
大概的意思就是执行/readflag,然后将他的输出放到$reply变量里,然后用eval计算返回给程序,最后再次得到的$reply就是flag,虽然没学过但我觉得大致就是这个意思emmm,收藏了能用就行
然后执行perl /tmp/a.pl即可
总结
匆匆忙忙赶了篇博客,大多数地方都是照搬大师傅们的博客,这个rctf/xctf的题目真的好难呀,这貌似是web里最简单的一道了,然而还是好多都没看懂,不禁感叹我太菜了呜呜呜呜/(ㄒoㄒ)/~~。自己还有好多不懂的地方,得再去仔细研究,不过也有所收获,知道了如何去构造异或型的shell,对php中的一些运算规则也有了更多理解,还收获了两个exp,总之好好记录是最重要的,加油💪