Nikos Paraskevopoulos Nikos Paraskevopoulos - 1 month ago 17
Java Question

Avoid formula injection, keeping cell value (quote prefix in HSSF/*.xls)

The application I am working on creates Excel exports using Apache POI. It was brought to our attention, through a security audit, that cells containing malicious values can spawn arbitrary processes if the user is not careful enough.

To reproduce, run the following:

import java.io.FileOutputStream;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

public class BadWorkbookCreator {
public static void main(String[] args) throws Exception {
try(
Workbook wb = new HSSFWorkbook();
FileOutputStream fos = new FileOutputStream("C:/workbook-bad.xls")
) {
Sheet sheet = wb.createSheet("Sheet");
Row row = sheet.createRow(0);
row.createCell(0).setCellValue("Aaaaaaaaaa");
row.createCell(1).setCellValue("-2+3 +cmd|'/C calc'!G20");
wb.write(fos);
}
}
}


Then open the resulting file:

Screenshot to reproduce problem

And follow these steps:


  1. Click on (A) to select the cell with malicious content

  2. Click on (B) so that the cursor is in the formula editor

  3. Press ENTER

  4. You will be asked if you allow Excel to run an external application; if you answer yes, Calc is launched (or any malicious code)



One may say that the user is responsible for letting Excel run arbitrary things and the user was warned. But still, the Excel is downloaded from a trusted source and someone may fall into the trap.

Using Excel, you can place a single quote in front of the text in the formula editor to escape it. Placing the single quote in the cell content programmatically (e.g. code as below) makes the single quote visible!

String cellValue = cell.getStringCellValue();
if( cellValue != null && "=-+@".indexOf(cellValue.charAt(0)) >= 0 ) {
cell.setCellValue("'" + cellValue);
}


The question: Is there a way to keep the value escaped in the formula editor, but show the correct value, without the leading single quote, in the cell?

Desired outcome, when hand-edited with Excel

Answer

Thanks to the hard work investigating of Axel Richter here and Nikos Paraskevopoulos here....

From Apache POI 3.16 beta 1 onwards (or for those who live dangerously, any nightly build after 20161105), there are handy methods on CellStyle for getQuotePrefixed and setQuotePrefixed(boolean)

Your code could then become:

// Do this once for the workbook
CellStyle safeFormulaStyle = workbook.createCellStyle();
safeFormulaStyle.setQuotePrefixed(true);


// Per cell
String cellValue = cell.getStringCellValue();
if( cellValue != null && "=-+@".indexOf(cellValue.charAt(0)) >= 0 ) {
    cell.setCellStyle(safeFormulaStyle);
}