ctfshow Web入门[PHP特性] web111-137 Writeup
变量覆盖, 无回显命令执行, 相关函数的绕过…
web111
hint 提示考察全局变量 (其实是超全局变量 $GLOBALS
)
https://www.php.net/manual/zh/reserved.variables.globals
$GLOBALS — 引用全局作用域中可用的全部变量
在 PHP 中, 被定义在函数外部的变量, 拥有全局作用域, 被称作全局变量
如果要想在函数内部使用全局变量, 必须通过 global $var
来显式引用或者是通过超全局变量数组 $GLOBALS['var']
来访问
$GLOBALS
数组存储着文件中所有的全局变量 (包含 include 进来的全局变量)
看一个例子
|
|
输出
|
|
题目中很明显的有 include("flag.php");
, 而且 eval()
相关操作是在 getFlag()
内进行的
那么我们可以猜测 flag.php 中存在这诸如 $flag="xxx"
的定义, 但我们不知道变量名具体是什么, 而定义的这些变量, 相对于 getFlag()
来说是全局变量
通过变量覆盖输出 $GLOBALS
的内容
|
|
得到 flag
web112
highlight_file()
支持伪协议
|
|
得到 flag
hint 里面除了 php://filter
还有一个 compress.zlib://flag.php
https://www.php.net/manual/zh/wrappers.php
其中的压缩流
本地测试下来只有 compress.zlib://
可以直接读任意文件 (或者文件包含), 不知道为什么…
web113
filter 被过滤了, 测试了 php://input 发现并不能执行命令
不过可以通过上一题学到的 compress.zlib://
读取文件
|
|
hint 给出的 payload
|
|
is_file()
目录溢出漏洞, 大概意思就是通过多级嵌套绕过检测
这里的 /proc/self/root
是一个指向 /
的软连接
参考文章 https://www.anquanke.com/post/id/213235
web114
上一题的目录溢出被过滤了, 但是 php://filter 还能用
|
|
感觉考察点应该是这里的 is_file()
支持读取伪协议
web115
is_numeric()
的相关比较
|
|
可以看到我们在数字开头加入一些特殊字符可以绕过 is_numeric()
返回 true
在数字中间和末尾加就没有这个效果
而源码中的 !==
和 ===
一样是强类型比较
|
|
对于 ===
的强类型比较, 但凡比较的字符串有一点点的不一样 (空格 tab 换行符等), 就返回 false
这样子我们可以绕过前两个比较, 即 is_numeric($num) and $num !== '36'
filter()
没有对上面提到的特殊字符进行处理, 只是过滤了十六进制, 八进制这些利用进制转换和科学计数法的 tricks
但问题是这里的 trim()
会去除空格和 tab 等字符
我们需要找到一个不能够被 trim()
去除的字符
fuzz 一下 ASCII 0-31 字符
|
|
最终的 payload 是 %0c36
, 即 \f36
, 其中 \f
是换页符 (我也不知道具体是啥…)
|
|
web123
考察变量覆盖, 需要通过 eval 构造出 $fl0g
这个变量, 而且不能赋值
没有过滤 ()
_
$
, 猜测是通过函数来实现
变量覆盖的相关函数
|
|
这里使用 extract()
不过一直没有显示 flag…
本地测试了一下, 发现 .
被转义成 _
了
hint 如下
|
|
测试发现 [
被转换成 _
, 而后面的 .
没有被转换
Google 找了好久的参考文章 https://blog.csdn.net/mochu7777777/article/details/115050295
当 PHP 版本小于8时有效
换了 payload 之后得到 flag
其实 eval 里面直接 echo $flag
也行
或者用 var_export(get_defined_vars())
导出已定义变量
web125
直接 echo 不行了
变量覆盖或者导出已定义变量
hint 是 highlight_file($_GET[1])
读取文件, 毕竟 eval 很灵活
web126
过滤了几个字母
常规的变量覆盖使用 $_GET
$_POST
$_COOKIE
都不行了
注意到这里有一个变量 $a=$_SERVER['argv']
$_SERVER['argv']
与 $_SERVER['QUERY_STRING']
不同的是前者是数组, 而后者是一整个字符串
$_SERVER['argv']
数组里面的每一项在 GET 传参中以空格分隔 (url 编码以后就是加号)
注意这两个变量的值都会进行 url 编码
第一种方法是变量覆盖
直接 parse_str($a[0])
肯定不行, 因为存在 !isset($_GET['fl0g'])
这一句
但是因为 $a
是数组, 可以利用 argv 传参和 get 传参的差异得到 flag
第二种方法是代码执行
但是引号被过滤了, 就算能绕过也会被 url 编码, 怎么办?
考察以下代码
虽然爆了 warning 但是能正常返回一个字符串
测试了下直接 fun=$a[0]
不行, 必须得再内嵌一个 eval 或者 assert
assert 不用加分号, eval 必须加分号
web127
变量覆盖, 过滤了一堆字符
根据上文 PHP 不能加引号也能正常赋值字符串以及把变量名/索引名中不合法的字符替换成 _
的特性
payload 如下
|
|
web128
call_user_func()
第一个参数是被调用的回调函数, 其余的参数是回调函数的参数
回调函数名不能含有字母和数字, emmm
hint 提示 _()
是 gettext()
的别名
参考文章
https://www.php.net/manual/zh/function.gettext
https://www.cnblogs.com/lost-1987/articles/3309693.html
gettext()
用于实现输出的国际化, 在不同区域输出不同语言的内容
本地测试时需要开启 php_gettext
扩展
第一层 call_user_func("_", "xx")
返回 xx 字符串, 然后被当作回调函数名被第二个 call_user_func()
调用
也就是说我们最终利用的函数不能含有参数, 而且必须是一步到位的
因为 flag 定义在变量里, 通过 get_defined_vars()
导出变量
|
|
web129
stripos()
查找字符串首次出现的位置 (不区分大小写)
data:// 伪协议用不了, php 相关的没想到怎么构造
换个思路, 类似之前强网杯遇到的目录穿越, 构造一个不存在的目录然后 ..
|
|
注意 ctfshow
不能在第一位, 否则的话 stripos()
的结果是0
hint 的方法如下
|
|
网上搜了一下发现 php://filter 其实能用
|
|
web130
正则可视化 https://jex.im/regulex/#!flags=&re=.+?ctfshow
模式修饰符 https://www.php.net/manual/zh/reference.pcre.pattern.modifiers.php
/s
使 .
匹配所有字符, 包含换行符
post 传递 ctfshow 直接就出来了…
在 wp 上看到了其它几种方法
通过数组绕过
|
|
第一个原因的其实是正则并未匹配 ctfshow 后面有什么字符, 所以类似 ctfshow123
也是能够绕过正则的检测, 后面匹配到了在第一位的 ctfshow, stripos()
返回0, 0 === false
返回 false, 绕过了 die()
第二个的原因是 preg_match()
遇到数组会返回 false, stripos()
遇到数组会返回 NULL, NULL === false
返回 false
web131
正则跟上一题的一模一样
强制转换成 String 类型, 而且 ctfshow 前面加上了 36D, 上一题的方法不管用了
hint 提示是正则表达式溢出
参考文章 https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html
回溯失败时会返回 false
ctrl + v 多按一会
这个漏洞的修复方法是使用 ===
与返回值作比较, 例如
|
|
0 === false
返回 false
web132
打开后是个网页
robots.txt
访问后得到源码
0x36D 是 877
mt_rand(1, 0x36D)
返回 1-877 之间的随机数, 而且是自动播种, 应该没有伪随机数的问题
if 后面有 &&
和 ||
, 猜测考察运算符的优先级
查阅 PHP 手册得知, &&
的优先级高于 ||
所以其实是
|
|
整个条件只要 $username == "admin"
为 true 即可, 不用关心 code 和 password 的值
payload
|
|
直接得到 flag
web133
不会了..
hint 提示 https://blog.csdn.net/qq_46091464/article/details/109095382
类似于变量覆盖, 原理跟 "$var"
一样, 反引号内也能够引用变量
测试 payload
|
|
注意分号后面的空格, 如果没有这个空格, 那么 substr()
截断后的语句就是这样的
|
|
eval 之后会报错
知道这个 tricks 之后, 剩下的就可以当作无回显的命令执行
cp mv 测试失败, 估计是限制了权限
尝试用 dnslog 回显
|
|
解码失败, 有长度限制
用 grep 查找 ctfshow 关键字
|
|
得到 flag
wp 里面是通过 curl -F
+ brup collaborator 的形式
-F
为以带文件的形式发送 post 请求, 可以认为没有长度限制
|
|
web134
变量覆盖
当时想着是通过 $_POST=$_COOKIE
, 然后 Cookie 处写变量
结果发现执行失败
查了手册才知道 =
后面的已经算是字符串了, 例如
换个思路, 根据上面的例子可知, parse_str()
是接受数组传参的
|
|
测试失败, 想了想可能是因为 parse_str()
已经默认将值和索引解析成字符串了, 不需要再加引号
|
|
web135
过滤了 curl wget 等函数, bash sh 也过滤了
想到了利用 .
执行文件的方法 (无需 x 权限)
利用 PHP 上传时保存在 /tmp/php[六位大小写字母]
的缓存文件执行命令
|
|
hint 如下
|
|
后来发现这题直接 mv cp 也能做…
|
|
web136
无回显命令执行, 过滤了很多…
hint 提示如下
|
|
tee 参考文章 https://zhuanlan.zhihu.com/p/34510815
根目录内容
|
|
其它 wp 的另一种 payload
|
|
web137
静态方法
https://www.php.net/manual/zh/language.oop5.static.php
通过范围解析操作符 ::
访问