Merge pull request #3861 from dilanbhalla/privatedata

C++: Private Data File/Buffer Writes
This commit is contained in:
Robert Marsh 2020-08-11 15:49:31 -04:00 коммит произвёл GitHub
Родитель 0476b97f63 dcfbb86674
Коммит e80cc63219
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 296 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,32 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Private data that is stored in a file or buffer unencrypted is accessible to an attacker who gains access to the
storage.</p>
</overview>
<recommendation>
<p>Ensure that private data is always encrypted before being stored.
It may be wise to encrypt information before it is put into a buffer that may be readable in memory.</p>
<p>In general, decrypt private data only at the point where it is necessary for it to be used in
cleartext.</p>
</recommendation>
<references>
<li><a href="https://owasp.org/www-project-top-ten/OWASP_Top_Ten_2017/Top_10-2017_A3-Sensitive_Data_Exposure">OWASP Sensitive_Data_Exposure</a></li>
<li>M. Dowd, J. McDonald and J. Schuhm, <i>The Art of Software Security Assessment</i>, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.</li>
<li>M. Howard and D. LeBlanc, <i>Writing Secure Code</i>, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.</li>
<!-- LocalWords: CWE
-->
</references>
</qhelp>

Просмотреть файл

@ -0,0 +1,21 @@
/**
* @name Exposure of private information
* @description If private information is written to an external location, it may be accessible by
* unauthorized persons.
* @kind path-problem
* @problem.severity error
* @id cpp/private-cleartext-write
* @tags security
* external/cwe/cwe-359
*/
import cpp
import experimental.semmle.code.cpp.security.PrivateCleartextWrite
import experimental.semmle.code.cpp.security.PrivateCleartextWrite::PrivateCleartextWrite
import DataFlow::PathGraph
from WriteConfig b, DataFlow::PathNode source, DataFlow::PathNode sink
where b.hasFlowPath(source, sink)
select sink.getNode(),
"This write into the external location '" + sink + "' may contain unencrypted data from $@",
source, "this source."

Просмотреть файл

@ -0,0 +1,63 @@
/**
* Provides a taint-tracking configuration for reasoning about private information flowing unencrypted to an external location.
*/
import cpp
import semmle.code.cpp.dataflow.TaintTracking
import experimental.semmle.code.cpp.security.PrivateData
import semmle.code.cpp.security.FileWrite
import semmle.code.cpp.security.BufferWrite
import semmle.code.cpp.dataflow.TaintTracking
module PrivateCleartextWrite {
/**
* A data flow source for private information flowing unencrypted to an external location.
*/
abstract class Source extends DataFlow::ExprNode { }
/**
* A data flow sink for private information flowing unencrypted to an external location.
*/
abstract class Sink extends DataFlow::ExprNode { }
/**
* A sanitizer for private information flowing unencrypted to an external location.
*/
abstract class Sanitizer extends DataFlow::ExprNode { }
/** A call to any method whose name suggests that it encodes or encrypts the parameter. */
class ProtectSanitizer extends Sanitizer {
ProtectSanitizer() {
exists(Function m, string s |
this.getExpr().(FunctionCall).getTarget() = m and
m.getName().regexpMatch("(?i).*" + s + ".*")
|
s = "protect" or s = "encode" or s = "encrypt"
)
}
}
class WriteConfig extends TaintTracking::Configuration {
WriteConfig() { this = "Write configuration" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
}
class PrivateDataSource extends Source {
PrivateDataSource() { this.getExpr() instanceof PrivateDataExpr }
}
class WriteSink extends Sink {
WriteSink() {
exists(FileWrite f, BufferWrite b |
this.asExpr() = f.getASource()
or
this.asExpr() = b.getAChild()
)
}
}
}

Просмотреть файл

@ -0,0 +1,53 @@
/**
* Provides classes and predicates for identifying private data and functions for security.
*
* 'Private' data in general is anything that would compromise user privacy if exposed. This
* library tries to guess where private data may either be stored in a variable or produced by a
* function.
*
* This library is not concerned with credentials. See `SensitiveActions` for expressions related
* to credentials.
*/
import cpp
/** A string for `match` that identifies strings that look like they represent private data. */
private string privateNames() {
// Inspired by the list on https://cwe.mitre.org/data/definitions/359.html
// Government identifiers, such as Social Security Numbers
result = "%social%security%number%" or
// Contact information, such as home addresses and telephone numbers
result = "%postcode%" or
result = "%zipcode%" or
// result = "%telephone%" or
// Geographic location - where the user is (or was)
result = "%latitude%" or
result = "%longitude%" or
// Financial data - such as credit card numbers, salary, bank accounts, and debts
result = "%creditcard%" or
result = "%salary%" or
result = "%bankaccount%" or
// Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc.
// result = "%email%" or
// result = "%mobile%" or
result = "%employer%" or
// Health - medical conditions, insurance status, prescription records
result = "%medical%"
}
/** An expression that might contain private data. */
abstract class PrivateDataExpr extends Expr { }
/** A functiond call that might produce private data. */
class PrivateFunctionCall extends PrivateDataExpr, FunctionCall {
PrivateFunctionCall() {
exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames()))
}
}
/** An access to a variable that might contain private data. */
class PrivateVariableAccess extends PrivateDataExpr, VariableAccess {
PrivateVariableAccess() {
exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames()))
}
}

Просмотреть файл

@ -0,0 +1,21 @@
edges
| test.cpp:77:16:77:22 | medical | test.cpp:78:24:78:27 | temp |
| test.cpp:81:17:81:20 | call to func | test.cpp:82:24:82:28 | buff5 |
| test.cpp:81:22:81:28 | medical | test.cpp:81:17:81:20 | call to func |
nodes
| test.cpp:57:9:57:18 | theZipcode | semmle.label | theZipcode |
| test.cpp:74:24:74:30 | medical | semmle.label | medical |
| test.cpp:77:16:77:22 | medical | semmle.label | medical |
| test.cpp:78:24:78:27 | temp | semmle.label | temp |
| test.cpp:81:17:81:20 | call to func | semmle.label | call to func |
| test.cpp:81:22:81:28 | medical | semmle.label | medical |
| test.cpp:82:24:82:28 | buff5 | semmle.label | buff5 |
| test.cpp:96:37:96:46 | theZipcode | semmle.label | theZipcode |
| test.cpp:99:42:99:51 | theZipcode | semmle.label | theZipcode |
#select
| test.cpp:57:9:57:18 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:57:9:57:18 | theZipcode | this source. |
| test.cpp:74:24:74:30 | medical | This write into the external location 'medical' may contain unencrypted data from $@ | test.cpp:74:24:74:30 | medical | this source. |
| test.cpp:78:24:78:27 | temp | This write into the external location 'temp' may contain unencrypted data from $@ | test.cpp:77:16:77:22 | medical | this source. |
| test.cpp:82:24:82:28 | buff5 | This write into the external location 'buff5' may contain unencrypted data from $@ | test.cpp:81:22:81:28 | medical | this source. |
| test.cpp:96:37:96:46 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:96:37:96:46 | theZipcode | this source. |
| test.cpp:99:42:99:51 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:99:42:99:51 | theZipcode | this source. |

Просмотреть файл

@ -0,0 +1 @@
experimental/Security/CWE/CWE-359/PrivateCleartextWrite.ql

Просмотреть файл

@ -0,0 +1,105 @@
#define FILE int
#define wchar char
#define size_t int
typedef int streamsize;
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
int fputs(const char *s, FILE *stream);
int fputc(int c, FILE *stream);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *s, const char *format, ...);
size_t strlen(const char *s);
namespace std
{
template <class charT>
struct char_traits;
template <class charT, class traits = char_traits<charT>>
class basic_ostream /*: virtual public basic_ios<charT,traits> - not needed for this test */
{
public:
typedef charT char_type;
basic_ostream<charT, traits> &write(const char_type *s, streamsize n);
};
template <class charT, class traits = char_traits<charT>>
class basic_ofstream : public basic_ostream<charT, traits>
{
public:
};
template <class charT, class traits>
basic_ostream<charT, traits> &operator<<(basic_ostream<charT, traits> &, const charT *);
typedef basic_ostream<char> ostream;
typedef basic_ofstream<char> ofstream;
}; // namespace std
using namespace std;
char *encrypt(char *buffer)
{
return buffer;
}
char *func(char *buffer)
{
return buffer;
}
// test for CleartextFileWrite
void file()
{
char *theZipcode = "cleartext zipcode!";
FILE *file;
// BAD: write zipcode to file in cleartext
fputs(theZipcode, file);
// GOOD: encrypt first
char *encrypted = encrypt(theZipcode);
fwrite(encrypted, sizeof(encrypted), 1, file);
}
// test for CleartextBufferWrite
int main(int argc, char **argv)
{
char *medical = "medical";
char *buff1;
char *buff2;
char *buff3;
char *buff4;
// BAD: write medical to buffer in cleartext
sprintf(buff1, "%s", medical);
// BAD: write medical to buffer in cleartext
char *temp = medical;
sprintf(buff2, "%s", temp);
// BAD: write medical to buffer in cleartext
char *buff5 = func(medical);
sprintf(buff3, "%s", buff5);
char *buff6 = encrypt(medical);
// GOOD: encrypt first
sprintf(buff4, "%s", buff6);
}
// test for CleartextFileWrite
void stream()
{
char *theZipcode = "cleartext zipcode!";
ofstream mystream;
// BAD: write zipcode to file in cleartext
mystream << "the zipcode is: " << theZipcode;
// BAD: write zipcode to file in cleartext
(mystream << "the zipcode is: ").write(theZipcode, strlen(theZipcode));
// GOOD: encrypt first
char *encrypted = encrypt(theZipcode);
mystream << "the zipcode is: " << encrypted;
(mystream << "the zipcode is: ").write(encrypted, strlen(encrypted));
}