[安洵杯 2019]easy_web 1

前言

这题是一道没出好的题目,导致了后面的正则匹配出现了很大的问题,不过因为我也菜。。我也对这个正则匹配没有理解好。所以我没看出这个问题。。。

WP

前面的题解就尽量省略一些,主要讲一下最后命令执行的正则匹配。看到url的img传参,base64二次解密再加一次hex解密,就可以得到555.png,因此我们反向构造index.php,得到index.php的源码:


error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd']))
    header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));

$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
     
    echo '';
    die("xixi~ no flag");
} else {
     
    $txt = base64_encode(file_get_contents($file));
    echo "";
    echo "
"
; } echo $cmd; echo "
"
; if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) { echo("forbid ~"); echo "
"
; } else { if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) { echo `$cmd`; } else { echo ("md5 is funny ~"); } } ?>

md5强碰撞没啥可说的,网上都有。主要是最后的正则匹配的问题:

preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)

这题的本意是让我们fuzz,看看哪些命令没有被过滤。首先猜测flag在/flag里,因为它这个正则表达式没有对读文件的命令过滤完整,因此参考一下linux中读文件的命令:

(1)more:一页一页的显示档案内容
(2)less:与 more 类似,但是比 more 更好的是,他可以[pg dn][pg up]翻页
(3)head:查看头几行
(4)tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
(5)tail:查看尾几行
(6)nl:显示的时候,顺便输出行号
(7)od:以二进制的方式读取档案内容(不推荐)
(8)vi:一种编辑器,这个也可以查看
(9)vim:一种编辑器,这个也可以查看(靶机测试不能用)
(10)sort:可以查看
(11)uniq:可以查看
(12)file -f和 tailf

sort和uniq在这题都没有过滤,都可以得到flag,这是这题的正解。

这题之所以会出现非预期解的原因是正则匹配那里,没有成功的匹配反斜杠:

|\\|\\\\|

这里引用一位大佬的话:

因为正则匹配中相当于要经过两层解析器解析,一层是php的,一层是正则表达式的。所以此处前面的两个反斜杠经过php解析器处理后应该是表示了一个转义号\,之后又与后面的表示逻辑或的|结合到一起,从而在正则表达式解析器中解析为|。又因为|是正则中的保留符号,所以需要一个转义符来转义。所以最后的实现效果应为对于字符|的过滤。

简单来说,就是PHP中想要正则匹配反斜杠,我们需要四个反斜杠\\\\
因为正则匹配要经过两层解析器过滤。在PHP解析后,第一个反斜杠转义第二个,第三个反斜杠转义了第四个。
在经过正则表达式的解析器解析的时候,实际上解析的是\\,这时候相当于再转义,成功匹配成了反斜杠。

虽然题目中也用了4个反斜杠,但是前面的两个反斜杠最终的结果是把|给转义了,最终导致相当于匹配的是|\,导致了反斜杠的逃逸。
在linux中,反斜杠并不影响命令的执行,因此可以直接执行命令:

l\s /
ca\t /flag

关于四个反斜杠的知识,参考了这位大师傅的文章:
从一道CTF的非预期解看PHP反斜杠匹配问题

你可能感兴趣的:(命令执行,Linux相关知识,shell,php,linux,正则表达式,web)