ctf

正则表达式练习®ex101

Posted by Kody Black on April 22, 2024

做一道正则表达式的题目,顺便复习一下相关知识

题目:No one knows regex better than me - Bugku CTF

<?php 
error_reporting(0);
$zero=$_REQUEST['zero'];
$first=$_REQUEST['first'];
$second=$zero.$first;
if(preg_match_all("/Yeedo|wants|a|girl|friend|or|a|flag/i",$second)){
    $key=$second;
    if(preg_match("/\.\.|flag/",$key)){
        die("Noooood hacker!");
    }else{
        $third=$first;
        if(preg_match("/\\|\056\160\150\x70/i",$third)){
            $end=substr($third,5);
            highlight_file(base64_decode($zero).$end);//maybe flag in flag.php
        }
    }
}
else{
    highlight_file(__FILE__);
}

可以用gpt修改如下:方便直接在一些php在线工具中测试:

<?php 
error_reporting(E_ALL); // Enable all errors for debugging
echo "<p>Debugging Output:</p>";

$zero = 'ZmxhZw==';
$first = 'aaaa|.php';
$second = $zero . $first;
echo "<p>Zero: " . $zero . "</p>";
echo "<p>First: " . $first . "</p>";
echo "<p>Second (concatenated zero and first): " . $second . "</p>";

if (preg_match_all("/Yeedo|wants|a|girl|friend|or|a|flag/i", $second)) {
    $key = $second;
    echo "<p>Passed first regex check with Key: " . $key . "</p>";
    if (preg_match("/\.\.|flag/", $key)) {
        die("Noooood hacker!");
    } else {
        $third = $first;
        echo "<p>Third: " . $third . "</p>";
        if (preg_match("/\\|\056\160\150\x70/i", $third)) {
            $end = substr($third, 5);
            echo "<p>End (substr of third): " . $end . "</p>";
            $file_to_highlight = base64_decode($zero) . $end;
            echo "<p>File to highlight: " . htmlspecialchars($file_to_highlight) . "</p>";
            echo $file_to_highlight; // maybe flag in flag.php
        }
    }
} else {
    echo "<p>Did not pass the initial regex check, showing current file.</p>";
    
}
?>

但是其实对于这个答案,我还是很疑惑,主要问题出在/\\|\056\160\150\x70/i这个正则表达式上,按照一般的理解,肯定是匹配\或着.php,但是实际情况应该是需要匹配|.php,很迷惑php中对于正则表达式的解析,就答案来看,就是只把后面的|进行了转义,也就是说将两个\作为和一个\等效的形式进行了处理。经过进一步的测试,发现php对于一开始读取的字符串就直接进行了处理,测试在之前preg_match("/\\|\056\160\150\x70/i", $third)里面的表达式:

<?php 
$preg = "/\\|\056\160\150\x70/i";
echo $preg;
?>

结果就是/\|.php/i,php先把\\转意成了一个\,然后再按照正则表达式的语法进行匹配。成功破案!

练习regex101

task 3

# 参考答案
(?![EIOU])[B-Z]

# 主要是我没想到这个能这么用,一般我觉得?!的用法只是
# \d(?!nd) 用来匹配 1st 2nd 3pc 中除了2的其他两个数字
# 但是现在有了更深的理解,匹配的过程是先匹配后面的B-Z,然后把其中以EIOU其中的某一个开头的结果排除掉。

task 7

# 我的答案
^(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)$
# hhh确实有点蠢这个方法,但是当时确实没想到可以用\b

# 好一点的答案
^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|\d{2}|\d)(?:\.|$)){4}\b$

# 抄作业改进自己的
^(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)(?:\.|$)){4}\b$

# 再改进一点点,尽力了44个字符,最优解是39
^(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.?\b){4}$

task 8

# 我的答案
<[^>]*>|^[^<]*>|<.*[^>]$
# 我的想法就是分情况,因为需要额外处理跨行情况下一行中没有<和没有>的两种情况

# 好一点的答案
(?:^|<)[^<>]*>|<[^>]*
# 对其稍加优化(当然也可以去掉?: 不过按照普遍规范,不重复使用时用?:更好)
(?:^|<)[^<]*>|<[^>]*

# 更好的答案
<[^>]*|[^<]*>
# 其实个人觉得有一点投机取巧了,因为这样虽然会得到正确结果 ,但实际上是拼接起来的,只有特定的情况下才能这么用吧

答案参考:我的 Regex quiz 答案 - 丁丁の店 (butanediol.me)

另一个不错的学习入门网站:Regex Learn

task 15

首先思路就是匹配到任意字符.任意字符的模式然后除掉数字.数字的情况

任意字符我想到的表示方法就是:\s|.|^|$

所以得到正则表达式为:(?!\d\.\d)(\s|.|^)\.(\s|.|$)

匹配没有太大的问题(所以也是有问题的),但是这道题要求是替换,就不太行了,因为这样默认的单次替换无法处理类似...的情况。如果将替换目标为$1-$2,那么...就会替换为.-.,按题目要求应该替换为---

所以说,还是应该只匹配到那个点,再对其直接替换。方法就是把之前的表达式拆成两半:

(?<!\d)\.|\.(?!\d)