非常规手段上传下载二进制文件

文中演示了3种数据映射方案,有更多其他编解码方案,这3种够用了。前面介绍的都是bin与txt的相互转换,各种编码、解码。假设数据传输通道只有一个弱shell,有回显,可以通过copy/paste无损传输可打印字符。为了将不可打印字节传输过去,只能通过编解码进行数据映射。

☆ od+xxd

2000年时我和tt在一台远程主机上想把其中一个ELF弄回本地来逆向工程,目标只在23/TCP上开了服务,其他端口不可达。远程主机上可用命令少得可怜,xxd、base64、uuencode之类的都没有,但意外发现有个od。后来靠od把这个ELF从远程弄回了本地。

为了便于演示说明,生造一个二进制文件:

这是bash语法,ash不支持。

xxd在Linux上很常见,但在其他非Linux的*nix环境中,od可能更常见。

some.txt形如:

在远程主机上显示some.txt,设法把其中的内容原封不动地弄回本地来,比如录屏、开启终端日志等等。然后在本地处理some.txt,恢复出some。

如果远程主机上有sed,上面这步可以在远程主机进行,减少通过网络传输的文本数据量。

some.tmp内容形如:

some.tmp的格式就是”xxx -p”的输出格式。

od本身只有数据转储功能,没有数据恢复功能。上面用”xxd -r”恢复出binary。

有人Ctrl-U断在U-Boot中,进行hexdump,然后恢复binary,本质是一样的。

☆ xxd

如果远程主机有xxd,整个过程类似。

1) 方案1

some.txt形如:

xxd生成的some.txt已经是最精简形式,不需要sed再处理。

2) 方案2

方案2演示xxd的其他参数,性价比不如方案1。

some.txt形如:

$ xxd -r -s 0 some.txt some

☆ base64

https://en.wikipedia.org/wiki/Base64

原始数据

二进制表示

从8-bits一组变成6-bits一组

16进制表示

查表后转成:

上面是base64编码基本原理,没有考虑需要填充的情形。

如果远程主机可以对binary进行base64编码,就没什么好说的了。

some.txt形如:

本文假设针对*nix环境,不考虑vbscript、jscript这些存在。

base64编码比”xxd -p”省空间,前者一个字符代表6-bits,后者一个字符代表4-bits。

☆ uuencode/uudecode

https://en.wikipedia.org/wiki/Uuencoding



 

is a character indicating the number of data bytes which have been encoded on that line. This is an ASCII character determined by adding 32 to the actual byte count, with the sole exception of a grave accent “`” (ASCII code 96) signifying zero bytes. All data lines except the last (if the data was not divisible by 45), have 45 bytes of encoded data (60 characters after encoding). Therefore, the vast
majority of length values is ‘M’, (32 + 45 = ASCII code 77 or “M”).

If the source is not divisible by 3 then the last 4-byte section will contain padding bytes to make it cleanly divisible. These bytes are subtracted from the line’s so that the decoder does not append unwanted characters to the file.

uu编码如今已不多见。

1) uu编码

some.txt形如:

这是传统的uuencode编码

2) base64编码

某些uuencode命令支持base64

some.txt形如:

====

解码时不需要额外参数,靠第一行识别base64编码。”uuencode -m”产生的内容相比base64产生的内容,多了第一行及最后一行:

====

把这两行删除后,就可以用”base64 -d”解码。

☆ awk

我们并不只考虑从远程主机下载binary,也考虑向远程主机上传binary。

如果目标环境有gcc,就弄个C代码实现base64编解码。本文不考虑宽松环境,像perl、python、gcc之类的都不考虑。考虑目标环境存在awk。

1) base64decode.awk

https://github.com/shane-kerr/AWK-base64decode/blob/master/base64decode.awk


 


 

busybox不一定有nc,如果有awk就可以用前面这招。awk脚本执行效率很低,极端情况下聊胜于无。base64decode.awk在一个很弱的busybox环境下成功解码。

2) base64.awk

https://sites.google.com/site/dannychouinard/Home/unix-linux-trinkets/little-utilities/base64-and-base85-encoding-awk-scripts

Danny Chouinard的原实现在做base64编码时没有正确处理结尾的=,他固定添加”==”。
这个问题不大,原脚本产生的编码输出可以被原脚本有效解码,但用其他工具解码原脚本产生的编码输出时可能容错度不够。比如”scz@nsfocus”经原脚本编码产生”c2N6QG5zZm9jdXM==”,结尾多了一个=。

如果上下文都只用Danny Chouinard的原脚本,它的实现是最精简的。

下面是改过的版本,确保base64编码输出符合规范,以便与其他工具混合使用。其base64编码功能无法直接处理binary,只能处理”xxd -p”这类输入,允许出现空格。
暂时没有找到用awk直接处理binary的办法。



这两个输出完全相同。

base64解码时,必须关闭%c的UTF-8支持,下面两种办法均可:

base64.awk直接使用awk的printf()。如果这个awk实际是由busybox提供的,此时可能无法输出0x00,这点需要在目标环境实测:

可以调用shell的printf输出0x00,UTF-8困挠一并被规避,参看uudecode_ash.awk。

3) uudecode.awk

busybox提供的awk可能无法输出0x00,本脚本不适用于busybox环境。


 


 

4) uudecode_ash.awk


 


 

此处不需要LANG=C,并且可以输出0x00,适用于busybox环境。

5) base64_ash.awk

从base64.awk移植出一个可以在busybox(ash+awk)中使用的版本。



此处不需要LANG=C,并且可以输出0x00,适用于busybox环境。

☆ bash

1) xxd.sh

这个脚本要求bash 4.3或更高版本,充斥着bash的各种奇技淫巧,如果读不懂,请看bash(1)。


 


 

脚本中的-d ”很重要,否则读取\n时,\n被自动转成\0。

这两个输出完全相同

这两个输出完全相同

“xxd.sh -r”的输入允许出现空格、换行等一切非16进制数字的字符,它们将被丢弃。
16进制数字大小写不敏感。

2) xxd_mini.sh


 


 

这个脚本不支持带ascii区的hexdump,即不支持”xxd -g 1″的效果,但支持”xxd -p”、”xxd -r”的效果,作为上传、下载工具,足矣。

相比xxd.sh,xxd_mini.sh的语法有些陈旧,这是为了兼容ash,参看xxd.ash的说明。

这两个输出完全相同

这两个输出完全相同

3) base64.sh


 

☆ ash

bash很强大,而我们面临的很可能是busybox提供的ash,ash要比bash弱不少。

1) xxd.ash


 


 

xxd.ash实际就是xxd_mini.sh,编写后者时已经充分考虑了ash与bash的兼容性。

为了进行递增操作,使用了let关键字,ash很可能不支持(())。

不要写function关键字,busybox v1.19.3不认,v1.27.2才认。

ash不支持-d、-N,因此xxd.ash中read时删除了-d ”,这导致脚本无法正确读取\n,读进来时被自动转换成\0,在ash中找不到规避办法。

ash不支持${parameter,,pattern},无法将输入自动转换成小写,xxd.ash只能处理全小写的some.txt。

xxd.ash的revert()可用,hexdump()不能正确转储\n。如果some中不包含\n,则可使用xxd.ash的hexdump()。如果非要在弱环境中进行hexdump(),可以先用revert()上传一个静态链接的ELF,此处不展开讨论。

2) echohelper.c

busybox的ash支持”echo -n -e”,这可能是最笨的上传binary方案。写个辅助C程序将指定binary转换成一系列echo命令。


 

3) base64.ash

base64编码时不直接处理binary,处理”xxd -p”、”od -An -tx1 -v –width=30″这类输入,允许出现空格,只支持小写[a-f]。如果busybox没有提供od,base64.ash无法进行base64编码。base64.ash不直接处理binary,主要因为busybox的ash不支持-d,
无法有效读取\n。

base64.ash进行base64解码时仅依赖busybox的ash,但效率极其低下。


 


 

 

☆ openssl

openssl可以进行base64编解码。一般不考虑目标环境存在openssl,列于此处只是出
于完备性考虑。

☆ 小结

至此为止,前面介绍的都是bin与txt的相互转换,各种编码、解码。假设数据传输通道只有一个弱shell,有回显,可以通过copy/paste无损传输可打印字符。为了将不可打印字节传输过去,只能通过编解码进行数据映射。前文只演示了3种数据映射方
案,有更多其他编解码方案,但没必要,这3种够用了。

弱环境使得无法用C代码完成编解码,只能用一些受限的现有工具完成,为此上场了各种奇技淫巧。

后面的内容是一些相关发散。

☆ perl

1) nc.pl

nc.pl实现nc部分功能。


 


 

本文最初没打算把perl牵扯进来,一般有perl的环境都不算弱环境。事实上前面主要考虑没有网络的串口登录shell,而且优先考虑恶劣的弱busybox环境。

后来想起曾经处理过一台x64/Solaris,当时需要取证,不允许在上面额外安装二进制工具,系统中没有nc,但有perl解释器。虽然这个场景不够恶劣,也算有所限制。

用这个办法把硬盘dd走了。

☆ SecureCRT

这里介绍ZMODEM/YMODEM/XMODEM/KERMIT方案,某些场景用得上,包括U-Boot,但举例时用了Windows和Linux。

1) 从Windows向Linux上传文件

1.1) ZMODEM(推荐)

在Linux中安装lrzsz包:

假设在Windows中用SecureCRT SSH登录Linux,在Linux的当前shell中切换到用于存放上传文件的目录,比如:

在Windows中操作SecureCRT:

之后在/tmp/modem中将出现被上传文件。

整个过程会在Linux中隐式执行rz:

第二种操作方式,SecureCRT SSH登录Linux,在Linux中切换目录,在Windows中用鼠标拖放待上传文件到SecureCRT SSH会话窗口,此时会弹出一个小窗口,在其中选择”Send Zmodem”。

第三种操作方式,SecureCRT SSH登录Linux,在Linux中切换目录,在Linux中执行rz命令,在SecureCRT中弹出界面让你选择文件,确定后完成上传。

1.2) YMODEM

相比ZMODEM,YMODEM、XMODEM没有优势,这里只是演示,并不推荐。

在Linux中执行:

在Windows中操作SecureCRT:

或者用鼠标拖放文件到相应SecureCRT会话窗口。YMODEM比ZMODEM慢,在Debian中居然需要用Ctrl-C结束,不过不影响上传数据。

1.3) XMODEM(不推荐)

在Linux中执行:

rx一次只能接收一个文件。

在Windows中操作SecureCRT:

待上传文件在Windows中的名字不要求是some,但到了Linux中将被重命名为some。在
Debian中同样可能需要用Ctrl-C结束,但不影响上传数据。

相比ZMODEM、YMODEM,XMODEM有个大问题,在man中写道:

Up to 1023 garbage characters may be added to the received file.

尾部填充导致不宜用XMODEM上传binary,尽管可以用dd切掉尾部填充。ZMODEM、
YMODEM无此问题。

1.4) KERMIT

介绍ZMODEM的文章很多,介绍KERMIT的较少,看到过标题说是介绍KERMIT内容实际是ZMODEM的文章,真扯淡。

在Linux中安装ckermit包:

在Linux中执行:

在Windows中操作SecureCRT:

或者用鼠标拖放文件到相应SecureCRT会话窗口,在弹出窗口中选择”Send Kermit”。

2) 从Linux向Windows下载文件

2.1) ZMODEM(推荐)

假设在Windows中用SecureCRT SSH登录Linux

在Windows中操作SecureCRT:

不必理会Upload的设置

在Linux中执行:

在Windows中检查Download目录,已经出现被下载文件。

SecureCRT对sz的支持比较智能,没有想像中的:

这带来一些兼容性问题。某远程主机是一台嵌入式ARM/Linux,上面有个3.48版sz,远程执行”sz -b <file>”后,SecureCRT这边没反应,但用YMODEM下载成功。后来把源自Debian 9的lrzsz 0.12.21-10交叉编译出静态链接版本弄到前述ARM/Linux上,
用ZMODEM下载成功。

2.2) YMODEM

在Linux中执行:

在Windows中操作SecureCRT:

在Windows中检查Download目录,已经出现被下载文件。

2.3) XMODEM(不推荐)

在Linux中执行:

sx一次只能传送一个文件。

在Windows中操作SecureCRT:

与2.2小节不同,此处弹出文件对话框,让你选择输出目录,还可以指定输出文件名。

1.3小节提到的尾部填充(0x1a)并不是Linux版rx命令的独有表现,应该是XMODEM规范。
SecureCRT通过XMODEM接收文件时,同样会进行尾部填充。填充什么数据,填充多少
字节,可以看rx源码,我已经打定主意不用XMODEM,不深究。

2.4) KERMIT

在Linux中执行:

指定-P,否则文件下载到Windows后文件名变成全大写。

在Windows中操作SecureCRT:

在Windows中检查Download目录,已经出现被下载文件。SecureCRT没有单独为KERMIT
配置下载目录的地方,KERMIT与ZMODEM共用同一个下载目录。

☆ zssh

若A、B都是Linux,也可以用rz/sz上传下载,此时需要zssh。zssh是”Zmodem SSH”
的缩写,Debian有这个包,直接装就是。

man手册里有:

zssh is an interactive wrapper for ssh used to switch the ssh connection between the remote shell and file transfers. This is achieved by using another tty/pty pair between the user and the local ssh process to plug either the user’s tty (remote shell mode) or another process (file transfer mode) on the ssh connection.

ztelnet behaves similarly to zssh, except telnet is used instead of ssh.It is equivalent to ‘zssh -s “telnet -8 -E”‘

$ zssh <user>@<ip>

登录后,在远程shell里执行:

按下zssh的”escape squence”,缺省是Ctrl-@(或Ctrl-2)。这将进入另一个提示符,
在其中输入rz

即可完成下载。此处有坑,假设是在C中用SecureCRT远程登录A,该会话启用ZMODEM,前述操作原始意图是从B向A提供文件,实际效果是从B向C提供文件;这种场景下,为了达成原始意图,必须先禁用C与A之间的ZMODEM。

上传更简单,在”zssh >”提示符下执行sz:

上传时跟SecureCRT一样”智能”,不需要在远程shell里显式执行rz来配合。

 

发表评论