bash - 从从标准输入接收到的 shell 脚本中提取二进制有效负载
问题描述
如果您是使用以下技术的 shell 脚本文件,则可以从其中提取任何有效负载(请参阅this):
#!/bin/sh
tail -n +4 > package.tgz
exec tar zxvf package.tgz
# payload comes here...
这需要一个文件,因此tail
可以将文件查找到正确的位置。
在我的特殊情况下,为了进一步自动化,我使用了该| sh -
模式,但它破坏了有效负载提取,因为管道不可搜索。
我还尝试将二进制有效负载嵌入到heredoc中,这样我就可以制作如下内容:
cat >package.tgz <<END
# payload comes here
END
tar zxvf package.tgz
但它使 shell(bash 和 NetBSD 的 /bin/sh)感到困惑,而且它不起作用。
我可以在 heredoc 中使用 uuencode 或 base64,但我只是想知道是否有一些 shell 魔法可用于从标准输入接收脚本和二进制数据,并从标准输入接收的数据中提取二进制数据。
编辑:
当我的意思是外壳变得混乱时,我的意思是它可以忽略空字节或具有未定义的行为,即使在 heredoc 中也是如此。尝试:
cat > /tmp/out <<EOF
$(echo 410041 | xxd -p -r)
EOF
xxd -p /tmp/out
巴什抱怨:line 2: warning: command substitution: ignored null byte in input
。
如果我从字面上将十六进制字节嵌入410041
到 shell 脚本中并使用引用的 heredoc,结果会有所不同,但 bash 只会删除空字节。
echo '#!/bin/sh' > foo.sh
echo "cat > /tmp/out <<'EOF'" >> foo.sh
echo 410041 | xxd -p -r >> foo.sh
echo >> foo.sh
echo EOF >> foo.sh
echo 'xxd -p /tmp/out' >> foo.sh
bash /tmp/foo.sh
41410a
解决方案
bash(和其他 shell)倾向于在 C 字符串中“思考”,这些字符串以空字符结尾,因此不能包含空字符(这就是字符串结尾的意思)。要产生空值,您几乎必须运行一些程序/命令,这些程序/命令采用一些安全编码的内容并产生空值,并将其输出直接发送到文件或管道,而不需要外壳在两者之间查看。
最简单的方法是使用 base64 之类的东西对文件进行编码,然后将输出从base64 -D
. 像这样的东西:
base64 -D <<'EOF' | tar xzv
H4sIAOzIHV8AA+y9DVxVVbowvs/hgAc8sY+Jhvl1VCoJBVQsETVgOIgViin2pSkq
....
EOF
如果您不想使用 base64,另一种选择是使用 bash 的printf
内置函数将包含 null 或其他奇怪的输出打印到管道。它可能看起来像这样:
LC_ALL=C
printf '\037\213\010\000\354\310\035_\000\003\354\275\015\\UU....' | tar xzv
在上面的示例中,我将所有不可打印的 ASCII 转换为\octal
代码。实际上应该可以将几乎所有内容都包含为文字字符,除了 null、单引号(不能包含在单引号字符串中,可能最简单的八进制编码)、反斜杠(只需加倍)和百分号 (也加倍)。我不认为这会是一个问题,但首先设置可能是最安全的LC_ALL=C
,所以它不会对输入字符串中的 non-valid-UTF-8 感到害怕。
这是一个快速而肮脏的 C 程序来进行编码。请注意,它将输出发送到标准输出,并且可能包含会弄乱您的终端的垃圾;所以一定要在某处直接输出。
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char *argv[] ) {
int ch;
FILE *fp;
if ( argc != 2 ) {
fprintf(stderr, "Usage: %s infile\n", argv[0]);
return 1;
}
fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "Error opening %s", argv[1]);
return 1;
}
printf("#!/bin/bash\nLC_ALL=C\nprintf '");
while((ch = fgetc(fp)) != EOF) {
switch(ch) {
case '\000':
printf("\\000");
break;
case '\047':
printf("\\047");
break;
case '%':
case '\\':
printf("%c%c", ch, ch);
break;
default:
printf("%c", ch);
}
}
fclose(fp);
printf("' | tar xzv\n");
return 0;
}
推荐阅读
- javascript - 当 csv 列的名称在 Angular 中包含空格时,有没有办法读取它?
- node.js - 未从 mongo 数据库中获取数据。收到一个空错误。尽管 mongo 云中存在集合
- java - 使用 Gson 反序列化 LocalDate 的问题
- apache-spark - Apache Spark 中的自动批处理
- python - 具有默认属性工厂的 Python 类
- javascript - 带有小数的数字的角度指令和键盘复制粘贴支持
- java - 无法从抽屉菜单上的链接打开本地 HTML 页面
- python - python - 当我在各种长度的元组中进行选择时,如何计算元组的长度而不是python中字符串的长度
- python - 如何在 python 中使用 API 更新 Strava 活动?
- javascript - 向安全网站发送 AJAX 请求