High severity7.8NVD Advisory· Published Apr 11, 2016· Updated May 6, 2026
CVE-2015-5349
CVE-2015-5349
Description
The CSV export in Apache LDAP Studio and Apache Directory Studio before 2.0.0-M10 does not properly escape field values, which might allow attackers to execute arbitrary commands by leveraging a crafted LDAP entry that is interpreted as a formula when imported into a spreadsheet.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.apache.directory.studio:org.apache.directory.studio.ldapbrowser.coreMaven | < 2.0.0.v20151221-M10 | 2.0.0.v20151221-M10 |
Affected products
27cpe:2.3:a:apache:ldap_studio:0.6.0:*:*:*:*:*:*:*+ 3 more
- cpe:2.3:a:apache:ldap_studio:0.6.0:*:*:*:*:*:*:*
- cpe:2.3:a:apache:ldap_studio:0.7.0:*:*:*:*:*:*:*
- cpe:2.3:a:apache:ldap_studio:0.8.0:*:*:*:*:*:*:*
- cpe:2.3:a:apache:ldap_studio:0.8.1:*:*:*:*:*:*:*
cpe:2.3:a:apache:directory_studio:1.0.0:*:*:*:*:*:*:*+ 22 more
- cpe:2.3:a:apache:directory_studio:1.0.0:*:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:1.0.1:*:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:1.4.0:*:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:1.5.0:*:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:1.5.1:*:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:1.5.2:*:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:1.5.3:*:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:2.0.0:milestone1:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:2.0.0:milestone2:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:2.0.0:milestone3:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:1.1.0:*:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:1.1.0:rc1:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:1.1.0:rc2:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:1.2.0:*:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:1.2.0:rc1:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:1.3.0:*:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:1.3.0:rc1:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:2.0.0:milestone4:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:2.0.0:milestone5:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:2.0.0:milestone6:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:2.0.0:milestone7:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:2.0.0:milestone8:*:*:*:*:*:*
- cpe:2.3:a:apache:directory_studio:2.0.0:milestone9:*:*:*:*:*:*
Patches
1ac57a26fcb98Escape spreadsheet formula in CSV export
6 files changed · +80 −15
plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportCsvRunnable.java+21 −11 modified@@ -279,9 +279,8 @@ private static String recordToCsv( IBrowserConnection browserConnection, LdifCon StringBuffer sb = new StringBuffer(); if ( exportDn ) { - sb.append( quoteCharacter ); - sb.append( record.getDnLine().getValueAsString() ); - sb.append( quoteCharacter ); + String value = record.getDnLine().getValueAsString(); + appendValue( quoteCharacter, sb, value ); if ( attributes == null || attributes.length > 0 ) sb.append( attributeDelimiter ); @@ -295,14 +294,7 @@ private static String recordToCsv( IBrowserConnection browserConnection, LdifCon if ( attributeMap.containsKey( oidString ) ) { String value = attributeMap.get( oidString ); - - // escape - value = value.replaceAll( quoteCharacter, quoteCharacter + quoteCharacter ); - - // always quote - sb.append( quoteCharacter ); - sb.append( value ); - sb.append( quoteCharacter ); + appendValue( quoteCharacter, sb, value ); } // delimiter @@ -318,6 +310,24 @@ private static String recordToCsv( IBrowserConnection browserConnection, LdifCon } + private static void appendValue( String quoteCharacter, StringBuffer sb, String value ) + { + // escape quote character + value = value.replaceAll( quoteCharacter, quoteCharacter + quoteCharacter ); + + // prefix values starting with '=' with a single quote to avoid interpretation as formula + if ( value.startsWith( "=" ) ) + { + value = "'" + value; + } + + // always quote + sb.append( quoteCharacter ); + sb.append( value ); + sb.append( quoteCharacter ); + } + + /** * Gets the attribute map. *
plugins/ldapbrowser.core/src/main/java/org/apache/directory/studio/ldapbrowser/core/jobs/ExportXlsRunnable.java+14 −4 modified@@ -46,6 +46,7 @@ import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.Cell; import org.eclipse.core.runtime.Preferences; @@ -151,7 +152,7 @@ public void run( StudioProgressMonitor monitor ) { int cellNum = 0; attributeNameMap.put( "dn", cellNum ); //$NON-NLS-1$ - headerRow.createCell( cellNum ).setCellValue( "dn" ); //$NON-NLS-1$ + createStringCell( headerRow, cellNum ).setCellValue( "dn" ); //$NON-NLS-1$ } // max export @@ -289,7 +290,7 @@ private static void recordToHSSFRow( IBrowserConnection browserConnection, LdifC HSSFRow row = sheet.createRow( sheet.getLastRowNum() + 1 ); if ( exportDn ) { - HSSFCell cell = row.createCell( 0 ); + HSSFCell cell = createStringCell(row, 0 ); cell.setCellValue( record.getDnLine().getValueAsString() ); } for ( String attributeName : attributeMap.keySet() ) @@ -300,17 +301,26 @@ private static void recordToHSSFRow( IBrowserConnection browserConnection, LdifC { int cellNum = headerRowAttributeNameMap.size(); headerRowAttributeNameMap.put( attributeName, new Integer( cellNum ) ); - HSSFCell cell = headerRow.createCell( cellNum ); + HSSFCell cell = createStringCell( headerRow, cellNum ); cell.setCellValue( attributeName ); } if ( headerRowAttributeNameMap.containsKey( attributeName ) ) { int cellNum = headerRowAttributeNameMap.get( attributeName ).shortValue(); - HSSFCell cell = row.createCell( cellNum ); + HSSFCell cell = createStringCell( row, cellNum ); cell.setCellValue( value ); } } } + + + private static HSSFCell createStringCell( HSSFRow row, int cellNum ) + { + HSSFCell cell = row.createCell( cellNum ); + cell.setCellType( Cell.CELL_TYPE_STRING ); + return cell; + } + }
tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/BrowserViewBot.java+7 −0 modified@@ -137,6 +137,13 @@ public ExportWizardBot openExportDsmlWizard() } + public ExportWizardBot openExportCsvWizard() + { + ContextMenuHelper.clickContextMenu( browserBot.getTree(), "Export", "CSV Export..." ); + return new ExportWizardBot( ExportWizardBot.EXPORT_CSV_TITLE ); + } + + public ImportWizardBot openImportLdifWizard() { ContextMenuHelper.clickContextMenu( browserBot.getTree(), "Import", "LDIF Import..." );
tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/bots/ExportWizardBot.java+7 −0 modified@@ -31,6 +31,7 @@ public class ExportWizardBot extends WizardBot { public static final String EXPORT_LDIF_TITLE = "LDIF Export"; public static final String EXPORT_DSML_TITLE = "DSML Export"; + public static final String EXPORT_CSV_TITLE = "CSV Export"; private String title; @@ -53,6 +54,12 @@ public boolean isVisible() } + public void typeReturningAttributes( String returningAttributes ) + { + bot.comboBoxWithLabel( "Returning Attributes:" ).setText( returningAttributes ); + } + + public void typeFile( String file ) { bot.comboBox().setText( file );
tests/test.integration.ui/src/main/java/org/apache/directory/studio/test/integration/ui/ImportExportTest.java+30 −0 modified@@ -266,4 +266,34 @@ public void testImportDontUptateUI() throws Exception assertEquals( "Only 2 event firings expected when importing LDIF.", 2, fireCount ); } + + /** + * Export to CSV and checks that spreadsheet formulas are prefixed with an apostrophe. + */ + @Test + public void testExportCsvShouldPrefixFormulaWithApostrophe() throws Exception + { + URL url = Platform.getInstanceLocation().getURL(); + final String file = url.getFile() + "ImportExportTest.csv"; + System.out.println( file ); + + browserViewBot.selectEntry( "DIT", "Root DSE", "ou=system", "ou=users", "cn=Wolfgang K\u00f6lbel" ); + + // export LDIF + ExportWizardBot wizardBot = browserViewBot.openExportCsvWizard(); + assertTrue( wizardBot.isVisible() ); + wizardBot.typeReturningAttributes( "cn, description" ); + wizardBot.clickNextButton(); + wizardBot.typeFile( file ); + wizardBot.clickFinishButton(); + wizardBot.waitTillExportFinished( file, 80 ); // is actually 86 bytes + + List<String> lines = FileUtils.readLines( new File( file ) ); + // verify that the first line is header + assertEquals( "dn,cn,description", lines.get( 0 ) ); + // verify that the second line is actual content and the formula is prefixed with an apostrophe + assertEquals( "\"cn=Wolfgang K\u00f6lbel,ou=users,ou=system\",\"Wolfgang K\u00f6lbel\",\"'=1+1\"", + lines.get( 1 ) ); + } + }
tests/test.integration.ui/src/main/resources/org/apache/directory/studio/test/integration/ui/ImportExportTest.ldif+1 −0 modified@@ -22,4 +22,5 @@ objectClass: inetOrgPerson objectClass: top cn:: V29sZmdhbmcgS8O2bGJlbA== sn:: S8O2bGJlbA== +description: =1+1
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- directory.apache.org/studio/news.htmlnvdVendor AdvisoryWEB
- github.com/advisories/GHSA-p9qj-4rjp-j3w9ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2015-5349ghsaADVISORY
- github.com/apache/directory-studio/commit/ac57a26fcb98aa17fe9534575cf5fdad00a1c839ghsaWEB
- lists.apache.org/thread.html/reb5443aaf781b364896ee9d7cf6e97fdc4f5a5174132c319252963b6@%3Ccommits.directory.apache.org%3EghsaWEB
- web.archive.org/web/20201209040832/http://www.securityfocus.com/archive/1/537225/100/0/threadedghsaWEB
- www.securityfocus.com/archive/1/537225/100/0/threadednvd
- lists.apache.org/thread.html/reb5443aaf781b364896ee9d7cf6e97fdc4f5a5174132c319252963b6%40%3Ccommits.directory.apache.org%3Envd
News mentions
0No linked articles in our index yet.