WP026——CTF赛题解析-No one knows regex better than me

63 阅读3分钟

时效性

2025年8月15日

题目来源

ctf.bugku.com/challenges/…

题目描述

image.png

<?php 
error_reporting(0);
# 从请求中获取了两个参数:'zero'和'first',然后,代码将这两个参数拼接在一起并赋值给变量$second。
$zero=$_REQUEST['zero']; 
$first=$_REQUEST['first'];
$second=$zero.$first; 

if(preg_match_all("/Yeedo|wants|a|girl|friend|or|a|flag/i",$second)){ # 代码使用正则表达式检查$second变量中是否包含字符串"Yeedo"、"wants"、"a"、"girl"、"friend"、"or"、"a"、"flag"
    $key=$second; # 如果匹配成功,则将$key变量设置为$second的值。
    if(preg_match("/\.\.|flag/",$key)){ # 代码使用正则表达式检查$key变量是否包含".."或"flag"字符串。
        die("Noooood hacker!"); # 如果匹配成功,则输出"Noooood hacker!"并停止脚本的执行。
    }else{
        $third=$first;  # 如果不成功,代码则将$first变量赋值给$third。所以$key不能有flag和..。
        if(preg_match("/\\|\056\160\150\x70/i",$third)){ 
         # 代码使用正则表达式检查$third变量是否包含"|\056\160\150\x70"(即"|.php")字符串。 此处使用的是八进制和16进制的混合匹配,\056 \160 \150为八进制 \x70 为16机制
         # 如果匹配成功,则从$third字符串的第5个字符开始截取,并将结果赋值给$end变量。
            $end=substr($third,5);
            highlight_file(base64_decode($zero).$end); //maybe flag in flag.php
            # 代码调用highlight_file()函数来展示源代码文件。
              # $zero变量经过base64解码后的字符串与$end变量拼接后得到的字符串就是会展示的源代码文件。
        }
    }
}
else{
# 如果以上的匹配条件都不满足,则会展示当前文件的源代码。
    highlight_file(__FILE__);
}

Write up

目前题目的要求如下

  1. 传入两个参数 zero ,first 将两个字符串进行拼接 成second
  2. 要求second字符传中存在 "Yeedo"、"wants"、"a"、"girl"、"friend"、"or"、"a"、"flag"字符
  3. second字符串中不得存在 ..和‘flag’
  4. firsit参数中需要存在 |.php
  5. 从first 第六位字符开始取出 end
  6. 将base64 编码后的zero和end进行拼接 读出文件

end = .php zero base64解码以后为 flag zero 为 ZmxhZw==

first 从第五位开始是 .php first = girl|.php

second=ZmxhZw==girl.php

url = /?zero=ZmxhZw==&first=girl|.php

image.png

下面提供一个简单的将各种进制编码转换成ASCLL码

def convert_to_ascii(input_str, delimiter=' '):
    """
    将分隔符分隔的进制字符串转换为ASCII字符串
    :param input_str: 包含进制字符串的输入(支持0b/0o/0x前缀或自动识别)
    :param delimiter: 分隔符,默认为空格
    :return: ASCII解码后的字符串
    """
    parts = input_str.split(delimiter)
    ascii_chars = []
    
    for part in parts:
        part = part.strip()  # 移除首尾空格
        if not part:
            continue
            
        try:
            # 识别带前缀的进制
            if part.startswith('0b'):
                num = int(part, 2)
            elif part.startswith('0o'):
                num = int(part, 8)
            elif part.startswith('0x'):
                num = int(part, 16)
            else:
                # 无前缀时自动推断进制
                if all(c in '01' for c in part):
                    num = int(part, 2)  # 二进制
                elif all(c in '01234567' for c in part):
                    num = int(part, 8)  # 八进制
                elif all(c in '0123456789ABCDEFabcdef' for c in part):
                    num = int(part, 16)  # 十六进制
                else:
                    raise ValueError(f"无法识别进制: {part}")
            
            # 转换为ASCII字符(确保数值在0-255范围内)
            if 0 <= num <= 255:
                ascii_chars.append(chr(num))
            else:
                raise ValueError(f"数值 {num} 超出ASCII范围(0-255)")
                
        except Exception as e:
            ascii_chars.append(f"[错误:{part}({e})]")
    
    return ''.join(ascii_chars)

# 示例使用
if __name__ == "__main__":
    input_str = "0b1000001 0o101 41 0x41"  # 不同进制的"A"
    result = convert_to_ascii(input_str)
    print(f"输入: {input_str}\n输出: {result}")  # 输出: AAAA
    
    # 测试自定义分隔符
    custom_delim = " 056 160 150 0x70"
    print(convert_to_ascii(custom_delim, " "))  # 输出: .php