Skip to content

Commit e80cc63

Browse files
authored
Merge pull request github#3861 from dilanbhalla/privatedata
C++: Private Data File/Buffer Writes
2 parents 0476b97 + dcfbb86 commit e80cc63

File tree

7 files changed

+296
-0
lines changed

7 files changed

+296
-0
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>Private data that is stored in a file or buffer unencrypted is accessible to an attacker who gains access to the
7+
storage.</p>
8+
9+
</overview>
10+
<recommendation>
11+
12+
<p>Ensure that private data is always encrypted before being stored.
13+
It may be wise to encrypt information before it is put into a buffer that may be readable in memory.</p>
14+
15+
<p>In general, decrypt private data only at the point where it is necessary for it to be used in
16+
cleartext.</p>
17+
18+
</recommendation>
19+
20+
<references>
21+
22+
<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>
23+
<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>
24+
<li>M. Howard and D. LeBlanc, <i>Writing Secure Code</i>, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.</li>
25+
26+
27+
28+
<!-- LocalWords: CWE
29+
-->
30+
31+
</references>
32+
</qhelp>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @name Exposure of private information
3+
* @description If private information is written to an external location, it may be accessible by
4+
* unauthorized persons.
5+
* @kind path-problem
6+
* @problem.severity error
7+
* @id cpp/private-cleartext-write
8+
* @tags security
9+
* external/cwe/cwe-359
10+
*/
11+
12+
import cpp
13+
import experimental.semmle.code.cpp.security.PrivateCleartextWrite
14+
import experimental.semmle.code.cpp.security.PrivateCleartextWrite::PrivateCleartextWrite
15+
import DataFlow::PathGraph
16+
17+
from WriteConfig b, DataFlow::PathNode source, DataFlow::PathNode sink
18+
where b.hasFlowPath(source, sink)
19+
select sink.getNode(),
20+
"This write into the external location '" + sink + "' may contain unencrypted data from $@",
21+
source, "this source."
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Provides a taint-tracking configuration for reasoning about private information flowing unencrypted to an external location.
3+
*/
4+
5+
import cpp
6+
import semmle.code.cpp.dataflow.TaintTracking
7+
import experimental.semmle.code.cpp.security.PrivateData
8+
import semmle.code.cpp.security.FileWrite
9+
import semmle.code.cpp.security.BufferWrite
10+
import semmle.code.cpp.dataflow.TaintTracking
11+
12+
module PrivateCleartextWrite {
13+
/**
14+
* A data flow source for private information flowing unencrypted to an external location.
15+
*/
16+
abstract class Source extends DataFlow::ExprNode { }
17+
18+
/**
19+
* A data flow sink for private information flowing unencrypted to an external location.
20+
*/
21+
abstract class Sink extends DataFlow::ExprNode { }
22+
23+
/**
24+
* A sanitizer for private information flowing unencrypted to an external location.
25+
*/
26+
abstract class Sanitizer extends DataFlow::ExprNode { }
27+
28+
/** A call to any method whose name suggests that it encodes or encrypts the parameter. */
29+
class ProtectSanitizer extends Sanitizer {
30+
ProtectSanitizer() {
31+
exists(Function m, string s |
32+
this.getExpr().(FunctionCall).getTarget() = m and
33+
m.getName().regexpMatch("(?i).*" + s + ".*")
34+
|
35+
s = "protect" or s = "encode" or s = "encrypt"
36+
)
37+
}
38+
}
39+
40+
class WriteConfig extends TaintTracking::Configuration {
41+
WriteConfig() { this = "Write configuration" }
42+
43+
override predicate isSource(DataFlow::Node source) { source instanceof Source }
44+
45+
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
46+
47+
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
48+
}
49+
50+
class PrivateDataSource extends Source {
51+
PrivateDataSource() { this.getExpr() instanceof PrivateDataExpr }
52+
}
53+
54+
class WriteSink extends Sink {
55+
WriteSink() {
56+
exists(FileWrite f, BufferWrite b |
57+
this.asExpr() = f.getASource()
58+
or
59+
this.asExpr() = b.getAChild()
60+
)
61+
}
62+
}
63+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* Provides classes and predicates for identifying private data and functions for security.
3+
*
4+
* 'Private' data in general is anything that would compromise user privacy if exposed. This
5+
* library tries to guess where private data may either be stored in a variable or produced by a
6+
* function.
7+
*
8+
* This library is not concerned with credentials. See `SensitiveActions` for expressions related
9+
* to credentials.
10+
*/
11+
12+
import cpp
13+
14+
/** A string for `match` that identifies strings that look like they represent private data. */
15+
private string privateNames() {
16+
// Inspired by the list on https://cwe.mitre.org/data/definitions/359.html
17+
// Government identifiers, such as Social Security Numbers
18+
result = "%social%security%number%" or
19+
// Contact information, such as home addresses and telephone numbers
20+
result = "%postcode%" or
21+
result = "%zipcode%" or
22+
// result = "%telephone%" or
23+
// Geographic location - where the user is (or was)
24+
result = "%latitude%" or
25+
result = "%longitude%" or
26+
// Financial data - such as credit card numbers, salary, bank accounts, and debts
27+
result = "%creditcard%" or
28+
result = "%salary%" or
29+
result = "%bankaccount%" or
30+
// Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc.
31+
// result = "%email%" or
32+
// result = "%mobile%" or
33+
result = "%employer%" or
34+
// Health - medical conditions, insurance status, prescription records
35+
result = "%medical%"
36+
}
37+
38+
/** An expression that might contain private data. */
39+
abstract class PrivateDataExpr extends Expr { }
40+
41+
/** A functiond call that might produce private data. */
42+
class PrivateFunctionCall extends PrivateDataExpr, FunctionCall {
43+
PrivateFunctionCall() {
44+
exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames()))
45+
}
46+
}
47+
48+
/** An access to a variable that might contain private data. */
49+
class PrivateVariableAccess extends PrivateDataExpr, VariableAccess {
50+
PrivateVariableAccess() {
51+
exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames()))
52+
}
53+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
edges
2+
| test.cpp:77:16:77:22 | medical | test.cpp:78:24:78:27 | temp |
3+
| test.cpp:81:17:81:20 | call to func | test.cpp:82:24:82:28 | buff5 |
4+
| test.cpp:81:22:81:28 | medical | test.cpp:81:17:81:20 | call to func |
5+
nodes
6+
| test.cpp:57:9:57:18 | theZipcode | semmle.label | theZipcode |
7+
| test.cpp:74:24:74:30 | medical | semmle.label | medical |
8+
| test.cpp:77:16:77:22 | medical | semmle.label | medical |
9+
| test.cpp:78:24:78:27 | temp | semmle.label | temp |
10+
| test.cpp:81:17:81:20 | call to func | semmle.label | call to func |
11+
| test.cpp:81:22:81:28 | medical | semmle.label | medical |
12+
| test.cpp:82:24:82:28 | buff5 | semmle.label | buff5 |
13+
| test.cpp:96:37:96:46 | theZipcode | semmle.label | theZipcode |
14+
| test.cpp:99:42:99:51 | theZipcode | semmle.label | theZipcode |
15+
#select
16+
| 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. |
17+
| 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. |
18+
| 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. |
19+
| 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. |
20+
| 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. |
21+
| 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. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Security/CWE/CWE-359/PrivateCleartextWrite.ql
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#define FILE int
2+
#define wchar char
3+
#define size_t int
4+
typedef int streamsize;
5+
6+
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
7+
int fputs(const char *s, FILE *stream);
8+
int fputc(int c, FILE *stream);
9+
int fprintf(FILE *stream, const char *format, ...);
10+
int sprintf(char *s, const char *format, ...);
11+
size_t strlen(const char *s);
12+
13+
namespace std
14+
{
15+
template <class charT>
16+
struct char_traits;
17+
18+
template <class charT, class traits = char_traits<charT>>
19+
class basic_ostream /*: virtual public basic_ios<charT,traits> - not needed for this test */
20+
{
21+
public:
22+
typedef charT char_type;
23+
basic_ostream<charT, traits> &write(const char_type *s, streamsize n);
24+
};
25+
26+
template <class charT, class traits = char_traits<charT>>
27+
class basic_ofstream : public basic_ostream<charT, traits>
28+
{
29+
public:
30+
};
31+
32+
template <class charT, class traits>
33+
basic_ostream<charT, traits> &operator<<(basic_ostream<charT, traits> &, const charT *);
34+
35+
typedef basic_ostream<char> ostream;
36+
typedef basic_ofstream<char> ofstream;
37+
}; // namespace std
38+
39+
using namespace std;
40+
41+
char *encrypt(char *buffer)
42+
{
43+
return buffer;
44+
}
45+
char *func(char *buffer)
46+
{
47+
return buffer;
48+
}
49+
50+
// test for CleartextFileWrite
51+
void file()
52+
{
53+
char *theZipcode = "cleartext zipcode!";
54+
FILE *file;
55+
56+
// BAD: write zipcode to file in cleartext
57+
fputs(theZipcode, file);
58+
59+
// GOOD: encrypt first
60+
char *encrypted = encrypt(theZipcode);
61+
fwrite(encrypted, sizeof(encrypted), 1, file);
62+
}
63+
64+
// test for CleartextBufferWrite
65+
int main(int argc, char **argv)
66+
{
67+
char *medical = "medical";
68+
char *buff1;
69+
char *buff2;
70+
char *buff3;
71+
char *buff4;
72+
73+
// BAD: write medical to buffer in cleartext
74+
sprintf(buff1, "%s", medical);
75+
76+
// BAD: write medical to buffer in cleartext
77+
char *temp = medical;
78+
sprintf(buff2, "%s", temp);
79+
80+
// BAD: write medical to buffer in cleartext
81+
char *buff5 = func(medical);
82+
sprintf(buff3, "%s", buff5);
83+
84+
char *buff6 = encrypt(medical);
85+
// GOOD: encrypt first
86+
sprintf(buff4, "%s", buff6);
87+
}
88+
89+
// test for CleartextFileWrite
90+
void stream()
91+
{
92+
char *theZipcode = "cleartext zipcode!";
93+
ofstream mystream;
94+
95+
// BAD: write zipcode to file in cleartext
96+
mystream << "the zipcode is: " << theZipcode;
97+
98+
// BAD: write zipcode to file in cleartext
99+
(mystream << "the zipcode is: ").write(theZipcode, strlen(theZipcode));
100+
101+
// GOOD: encrypt first
102+
char *encrypted = encrypt(theZipcode);
103+
mystream << "the zipcode is: " << encrypted;
104+
(mystream << "the zipcode is: ").write(encrypted, strlen(encrypted));
105+
}

0 commit comments

Comments
 (0)