[Перевод] Подробный разбор цепочки эксплойтов Playstation 4 и 5
В статье представлена цепочка из пяти уязвимостей, позволяющая нападающему получить возможности 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 года — отчёт был опубликован.