Skip to content

Java: Check whether there are internal files in the App that can be read and written by any other App #11016

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

// BAD:openFileOutput() uses Context.MODE_WORLD_READABLE mode
void test_file_read() {
try {
FileOutputStream outputStream = this.openFileOutput("test_read.txt", Context.MODE_WORLD_READABLE);
outputStream.write("123".getBytes(StandardCharsets.UTF_8));
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

// BAD:openFileOutput() uses Context.MODE_WORLD_READABLE mode
void test_file_read2() {
try {
openFileOutput("", 0);
FileOutputStream outputStream = this.openFileOutput("test_all222.txt", 1);
outputStream.write("123".getBytes(StandardCharsets.UTF_8));
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}


// BAD:openFileOutput() uses Context.MODE_WORLD_WRITEABLE mode
void test_file_write() {
try {
FileOutputStream outputStream = this.openFileOutput("test_write.txt", Context.MODE_WORLD_WRITEABLE);
outputStream.write("123".getBytes(StandardCharsets.UTF_8));
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

// BAD:openFileOutput() uses the Context.MODE_WORLD_READABLE mode and Context.MODE_WORLD_WRITEABLE mode
void test_file_read_write() {
try {
FileOutputStream outputStream = this.openFileOutput("test_read_write.txt", Context.MODE_WORLD_READABLE | Context.MODE_WORLD_WRITEABLE);
outputStream.write("123".getBytes(StandardCharsets.UTF_8));
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

// BAD:openFileOutput() uses the Context.MODE_WORLD_READABLE mode and Context.MODE_WORLD_WRITEABLE mode
void test_file_all() {
try {
FileOutputStream outputStream = this.openFileOutput("test_all.txt", Context.MODE_PRIVATE | Context.MODE_WORLD_READABLE | Context.MODE_WORLD_WRITEABLE);
outputStream.write("123".getBytes(StandardCharsets.UTF_8));
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}


//GOOD:openFileOutput() uses the Context.MODE_PRIVATE mode
void test_file_private() {
try {
FileOutputStream outputStream = this.openFileOutput("test_private.txt", Context.MODE_PRIVATE);
outputStream.write("123".getBytes(StandardCharsets.UTF_8));
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>

<overview>
<p>Check whether there are internal files in the App that can be read and written by any other App.</p>
</overview>

<recommendation>
<p>To share data between different software, you need to set an internal file as globally readable or globally writable so that other applications can read and modify the file. If such files contain sensitive information such as key configuration information and account information, they may be stolen or maliciously tampered with, causing problems such as program failure and service logic modification.</p>
</recommendation>

<example>
<p>The following example shows the pattern used by openFileOutput(). In the 'BAD' case, the file can be read and modified by other applications. In the 'GOOD' case, no other application can read and modify the file.</p>
<sample src="FileReadWrite.java" />
</example>

<references>
<li>
Android Developers:
<a href="https://developer.android.google.cn/reference/android/content/Context#openFileOutput(java.lang.String,%20int)">openFileOutput</a>
<a href="https://developer.android.google.cn/reference/android/content/Context#MODE_PRIVATE">Context.MODE_PRIVATE mode</a>
<a href="https://developer.android.google.cn/reference/android/content/Context#MODE_WORLD_READABLE">Context.Android MODE_WORLD_READABLE mode</a>
<a href="https://developer.android.google.cn/reference/android/content/Context#MODE_WORLD_WRITEABLE">Context.Android MODE_WORLD_WRITEABLE mode</a>
</li>
</references>
</qhelp>
45 changes: 45 additions & 0 deletions java/ql/src/experimental/Security/CWE/CWE-1025/FileReadWrite.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* @name Globally readable internal file vulnerability
* @description Check whether there are internal files in the App that can be read and written by any other App.
* @kind problem
* @id globally-readable-internal-file-vulnerability
* @problem.severity error
* @security-severity 8.2
* @precision high
* @tags security
* external/cwe/cwe-1025
*/

import java
import semmle.code.java.dataflow.FlowSources


/** The class `android.context.ContextWrapper`. */
class AndroidContextWrapper extends RefType{
AndroidContextWrapper(){
this.hasQualifiedName("android.content", "ContextWrapper")
}
}

/** A method call to `context.openFileOutput`. */
class OpenFileOutputMethodAccess extends Method {
OpenFileOutputMethodAccess() {
this.hasName("openFileOutput") and
this.getDeclaringType() instanceof AndroidContextWrapper
}
}

class FileReadAndWriteNode extends DataFlow::Node{
FileReadAndWriteNode(){
exists(OpenFileOutputMethodAccess m |
m.getParameter(1).getAnArgument()=this.asExpr() and
not (this.asExpr().toString()="0" or
this.asExpr().toString()="Context.MODE_PRIVATE" or
this.asExpr().toString()="MODE_PRIVATE")
)
}
}


from FileReadAndWriteNode parameter
select parameter, "Wrong mode parameter of openFileOutput() function "