[Перевод] Подробный разбор цепочки эксплойтов Playstation 4 и 5

qwbhd_8-k8dyh0y_phusxowynhe.jpeg


В статье представлена цепочка из пяти уязвимостей, позволяющая нападающему получить возможности JIT и исполнять произвольные полезные нагрузки. Передаваемая полезная нагрузка вызывает переполнение буфера, приводящее к kernel panic.

Уязвимости


[Уровень серьёзности: средний][PS4] [PS5] Уязвимость 1


Класс com.sony.gemstack.org.dvb.user.UserPreferenceManagerImpl при помощи readObject() десериализует файл userprefs в привилегированном контексте, что небезопасно:

    private void initPreferences() {
        try {
            UserPreferenceManagerImpl.preferences = AccessController.doPrivileged((PrivilegedExceptionAction)new ReadPreferenceAction());
        }
        catch (PrivilegedActionException ex) {}
        if (UserPreferenceManagerImpl.preferences == null) {
            UserPreferenceManagerImpl.preferences = new String[UserPreferenceManagerImpl.PREFERENCES.length][];
        }
        if (UserPreferenceManagerImpl.preferences[3] == null) {
            UserPreferenceManagerImpl.preferences[3] = new String[] { "26" };
            this.savePreferences();
        }
    }
    private static class ReadPreferenceAction implements PrivilegedExceptionAction
    {
        public Object run() throws Exception {
            String[][] array = null;
            ObjectInputStream objectInputStream = null;
            try {
                objectInputStream = new ObjectInputStream(new BufferedInputStream(new FileInputStream(RootCertManager.getOriginalPersistentRoot() + "/userprefs")));
                array = (String[][])objectInputStream.readObject();
            }
            finally {
                if (objectInputStream != null) {
                    objectInputStream.close();
                }
            }
            return array;
        }
    }


Злоумышленник может заменить файл userprefs зловредным сериализированным объектом для создания экземпляров классов в привилегированном контексте. На старых прошивках, например, 5.05, в которых отсутствует коммит https://github.com/openjdk/jdk/commit/020204a972d9be8a3b2b9e75c2e8abea36d787e9#diff-2c19943dd71743c3de69aa065025e753ca2e1f3b7ebc798e0d954de75d995de5, эксплуатировать эту уязвимость легко: нападающий может создать экземпляр подкласса ClassLoader для вызова defineClass с любыми разрешениями, а в конечном итоге обойти диспетчер безопасности.

[Уровень серьёзности: средний][PS4] Уязвимость 2


Класс com.oracle.security.Service содержит метод newInstance, вызывающий Class.forName для произвольного имени класса. Это позволяет создавать экземпляры произвольных классов, даже ограниченных (например, в sun.). Уязвимость работает для всех классов с публичными конструкторами, которые имеют единственные аргументы. Проверку в newInstance можно обойти, вызвав com.oracle.ProviderAdapter.setProviderAccessor для специальной созданной реализации ProviderAccessor.

        if (!this.registered) {
            if (ProviderAdapter.getService(this.provider, this.type, this.algorithm) != this) {
                throw new NoSuchAlgorithmException("Service not registered with Provider " + this.provider.getName() + ": " + this);
            }
            this.registered = true;
        }


[Уровень серьёзности: средний][PS4] [PS5] Уязвимость 3


Класс com.sony.gemstack.org.dvb.io.ixc.IxcProxy содержит защищённый метод invokeMethod, способный вызывать методы в привилегированном контексте. Проверки разрешений в методах можно обойти при соблюдении следующих условий:

  • Метод публичный и нестатический
  • Класс метода публичный, не final и его экземпляры можно создавать.


В такой ситуации нападающий может написать подкласс целевого класса, реализующий интерфейс, в котором нужный метод выбрасывает RemoteException.
Например, в File.list() присутствуют проверки разрешений. Злоумышленник может обойти их при помощи следующих классов:

class FileImpl extends File implements FileInterface {
  FileImpl(String pathname) {
    super(pathname);
  }
}
Code 91 Bytes
interface FileInterface extends Remote {
  public String[] list() throws RemoteException;
}


Эту уязвимость можно использовать для обеспечения утечки структуры файловой системы, а также для дампинга файлов (например, из /app0/).

[Уровень серьёзности: высокий][PS4] Уязвимость 4


«compiler receiver thread» получает от рабочего процесса структуру размером 0×58 байтов:

typedef struct {
  uint8_t cmd; // 0x00
  uint64_t arg0; // 0x08
  uint64_t arg1; // 0x10
  uint64_t arg2; // 0x18
  uint64_t arg3; // 0x20
  uint64_t arg4; // 0x28
  uintptr_t runtime_data; // 0x30
  uintptr_t compiler_data; // 0x38
  uint64_t data1; // 0x40
  uint64_t data2; // 0x48
  uint64_t unk; // 0x50
} CompilerAgentRequest; // 0x58

CompilerAgentRequest req;
while (CompilerAgent::readn(s, &req, sizeof(req)) > 0) {
  uint8_t ack = 0xAA;
  CompilerAgent::writen(s, &ack, sizeof(ack));
  if (req.compiler_data != 0) {
    memcpy(req.compiler_data + 0x28, &req, sizeof(req));
    ...
  }
  ...
}


Эта структура содержит указатель на смещение 0×38 (мы называем его compiler_data) от процесса компилятора, который используется для создания резервной копии структуры запроса. Нападающий может просто отправить ненадёжный указатель, а compiler receiver thread скопирует данные из запроса в его память. Иными словами, это примитив write-what-where. Нападающий может эксплуатировать эту уязвимость, передавая указатель на память JIT и сохраняя содержимое, которое должно быть записано в запрос. Компилятор запишет эти данных в память JIT, давая нам возможность исполнять произвольные полезные нагрузки. Последствия могут быть очень серьёзными:

  • Можно написать загрузчик ELF для загрузки и исполнения пиратских игр
  • Эксплойт ядра становится тривиальной задачей, поскольку отсутствует SMEP и можно просто перейти к пользователю при помощи повреждённого указателя функции.


[Уровень серьёзности: высокий][PS4] [PS5] Уязвимость 5


В PS4 и PS5 используется драйвер UDF https://github.com/williamdevries/UDF, содержащий переполнение буфера. Злоумышленник может сделать размер inf_len больше, чем sector_size (при внутреннем распределении предполагается, что данные меньше, чем размер сектора) и вызвать переполнение при помощи memcpy().

int
udf_read_internal(struct udf_node *node, uint8_t *blob)
{
	struct file_entry *fe = node->fe;
	struct extfile_entry *efe = node->efe;
	struct udf_mount *ump;
	uint64_t inflen;
	int addr_type, icbflags;
	uint32_t sector_size;
	uint8_t *pos;

	/* получаем диапазон и выполняем параноидальные проверки */
	ump = node->ump;
	sector_size = ump->sector_size;

	if (fe != NULL) {
		inflen = le64toh(fe->inf_len);
		pos = &fe->data[0] + le32toh(fe->l_ea);
		icbflags = le16toh(fe->icbtag.flags);
	} else {
		inflen = le64toh(efe->inf_len);
		pos = &efe->data[0] + le32toh(efe->l_ea);
		icbflags = le16toh(efe->icbtag.flags);
	}
	addr_type = icbflags & UDF_ICB_TAG_FLAGS_ALLOC_MASK;

	/* копируем информацию */
	memset(blob, 0, sector_size);
	memcpy(blob, pos, inflen);

	return (0);
}


Proof-of-concept


Была создана цепочка эксплойтов bd-jb в виде файла .iso, демонстрирующая использование уязвимостей 1–4, что доказывает возможность запуска произвольных полезных нагрузок. Необходимо записать образ iso с файловой системой UDF 2.5. Полезную нагрузку можно отправить при помощи nc $PS4IP 1337 < payload.bin. Переданная полезная нагрузка приводит к kernel panic, вызывая уязвимость 5 (файл /PWN/0 был модифицирован, чтобы использовать внутреннее распределение, он имеет размер 4 МБ и заполнен символами A). Протестировано на самой свежей прошивке 9.00.

Влияние


Благодаря этим уязвимостям можно выпускать пиратские игры на дисках Bluray. Это возможно даже без эксплойта ядра, поскольку у нас есть возможности JIT.

Хроника устранения уязвимостей


25 октября 2021 года — theflow0 отправил отчёт PlayStation.

5 ноября 2021 года — shoshin_cup (представитель PlayStation) изменил статус на Triaged.

12 ноября 2021 года — theflow0 выплачено вознаграждение $20000.

5 апреля 2022 года — shoshin_cup закрыл отчёт и изменил статус на Resolved.

5 апреля 2022 года — theflow0 попросил опубликовать этот отчёт.

10 июня 2022 года — sazerac (представитель HackerOne) согласился опубликовать отчёт.

10 июня 2022 года — отчёт был опубликован.

© Habrahabr.ru