PT-2026-59311 · Pypi · Mobsf

Published

2026-07-13

·

Updated

2026-07-13

CVSS v3.1

5.3

Medium

VectorAV:N/AC:H/PR:N/UI:R/S:U/C:N/I:N/A:H

Description

MobSF's read sqlite() function in mobsf/MobSF/utils.py (lines 542-566) uses Python string formatting (%) to construct SQL queries with table names read from a SQLite database's sqlite master table. When a security analyst uses MobSF to analyze a malicious mobile application containing a crafted SQLite database, attacker-controlled table names are interpolated directly into SQL queries without parameterization or escaping.
This allows an attacker to:
  1. Cause Denial of Service -- A malicious table name causes the database viewer to crash, preventing the analyst from viewing ANY data in the SQLite database. A malicious app can use this to hide sensitive data (C2 server URLs, stolen credentials, API keys) from MobSF's analysis.
  2. Achieve SQL Injection -- The SELECT * FROM query on line 557 is provably injectable via UNION SELECT, allowing attacker-controlled data to be returned in query results. The current code structure (a PRAGMA statement that runs first on line 553) limits the full exploitation chain, but the underlying code is verifiably injectable.

Root Cause

The vulnerable code in mobsf/MobSF/utils.py:542-566:
python
def read sqlite(sqlite file):
  """Sqlite Dump - Readable Text."""
  table dict = {}
  try:
    con = sqlite3.connect(sqlite file)
    cur = con.cursor()
    cur.execute('SELECT name FROM sqlite master WHERE type='table';')
    tables = cur.fetchall()
    for table in tables:
      table dict[table[0]] = {'head': [], 'data': []}
      cur.execute('PRAGMA table info('%s')' % table)  # <-- INJECTION POINT 1
      rows = cur.fetchall()
      for sq row in rows:
        table dict[table[0]]['head'].append(sq row[1])
      cur.execute('SELECT * FROM '%s'' % table)     # <-- INJECTION POINT 2
      rows = cur.fetchall()
      for sq row in rows:
        tmp row = []
        for each row in sq row:
          tmp row.append(str(each row))
        table dict[table[0]]['data'].append(tmp row)
  except Exception:
    logger.exception('Reading SQLite db')
  return table dict
Lines 553 and 557 use % string formatting to interpolate table (a tuple from sqlite master) directly into SQL strings. The table value is attacker-controlled when the SQLite database originates from a malicious application being analyzed.

Attack Vector

The read sqlite() function is called from two locations:
  1. Dynamic Analysis File Viewer (mobsf/DynamicAnalyzer/views/common/device.py:64):
  • Triggered when an analyst clicks to view a .db file in device data
  • Applies to both Android and iOS dynamic analysis
  1. iOS Static Analysis File Viewer (mobsf/StaticAnalyzer/views/ios/views/view source.py:123):
  • Triggered when an analyst clicks to view a .db file during iOS static analysis

Attack Scenario

  1. Attacker creates a malicious Android APK (or iOS IPA) containing a SQLite database with a crafted table name in the assets/ directory
  2. The SQLite database contains a table created with:
sql
CREATE TABLE "x' UNION SELECT 'SQL INJECTION PROOF'--" (id INTEGER);
  1. Security analyst uploads the application to MobSF for analysis
  2. Analyst browses the extracted files and clicks to view the SQLite database
  3. MobSF's read sqlite() reads table names from sqlite master, including the malicious name x' UNION SELECT 'SQL INJECTION PROOF'--
  4. The table name is interpolated into SQL queries via string formatting:
  • PRAGMA table info('x' UNION SELECT 'SQL INJECTION PROOF'--') -- causes syntax error (DoS)
  • SELECT * FROM 'x' UNION SELECT 'SQL INJECTION PROOF'--' -- SQL injection (UNION SELECT returns attacker data)

Impact

Denial of Service (Confirmed)

When the malicious table name is the first table in sqlite master (i.e., created first in the database), the PRAGMA statement on line 553 raises a sqlite3.OperationalError, which is caught by the outer try/except. This causes read sqlite() to return an empty or partial result, preventing the analyst from viewing any database content.
Security impact: A malicious app author can use this technique to hide incriminating data stored in SQLite databases from MobSF's analysis. This directly undermines MobSF's core purpose as a security analysis tool.

SQL Injection (Confirmed in Isolation)

The SELECT * FROM query on line 557 is demonstrably injectable. When the malicious table name x' UNION SELECT 'SQL INJECTION PROOF'-- is interpolated, the resulting query:
sql
SELECT * FROM 'x' UNION SELECT 'SQL INJECTION PROOF'--'
Successfully executes and returns attacker-controlled data via UNION SELECT. The -- comments out the trailing single quote. This is verified by the PoC script.
Note: In the current code structure, the PRAGMA table info() statement on line 553 runs before the SELECT * FROM on line 557. The PRAGMA fails with a syntax error for injected payloads, which triggers the exception handler before the SELECT can execute. This limits the full exploitation chain. However, the code flaw is real and any future refactoring that changes the execution order or removes the PRAGMA would immediately expose the full SQL injection.

Proof of Concept

Files Provided(Gdrive)

FileDescription
poc sqlite injection.pyStandalone PoC demonstrating the vulnerability
malicious.dbCrafted SQLite database (generated by PoC)
create malicious apk.shScript to package the malicious DB into an APK
malicious sqli.apkPre-built APK for testing against MobSF

Running the PoC

bash
# Run the standalone PoC (no MobSF required)
python3 poc sqlite injection.py

# Build the malicious APK (requires Android SDK)
./create malicious apk.sh

# Test against MobSF
# 1. Start MobSF
# 2. Upload malicious sqli.apk
# 3. Browse extracted files -> click app data.db
# 4. Observe: database viewer fails (DoS)

PoC Output (Abbreviated)

[STEP 2] Running MobSF's read sqlite() against malicious database...
  [!] EXCEPTION CAUGHT: OperationalError: near "UNION": syntax error
  [!] DoS CONFIRMED: read sqlite() crashed

[STEP 3] Demonstrating SELECT * FROM injection in isolation...
  Query: SELECT * FROM 'x' UNION SELECT 'SQL INJECTION PROOF'--'
  [+] Query executed successfully!
  [+] Results: [('SQL INJECTION PROOF',), ('normal data',)]
  [+] SQL INJECTION CONFIRMED

[STEP 4] Complete DoS (malicious table created first):
  Tables with data: NONE
  [!] COMPLETE DoS CONFIRMED

Suggested Fix

Replace string formatting with properly quoted identifiers. SQLite uses double quotes for identifiers:
python
def read sqlite(sqlite file):
  """Sqlite Dump - Readable Text."""
  table dict = {}
  try:
    con = sqlite3.connect(sqlite file)
    cur = con.cursor()
    cur.execute('SELECT name FROM sqlite master WHERE type='table';')
    tables = cur.fetchall()
    for table in tables:
      table name = table[0]
      # Properly escape table name as a double-quoted identifier
      safe name = table name.replace('"', '""')
      table dict[table name] = {'head': [], 'data': []}
      cur.execute(f'PRAGMA table info("{safe name}")')
      rows = cur.fetchall()
      for sq row in rows:
        table dict[table name]['head'].append(sq row[1])
      cur.execute(f'SELECT * FROM "{safe name}"')
      rows = cur.fetchall()
      for sq row in rows:
        tmp row = []
        for each row in sq row:
          tmp row.append(str(each row))
        table dict[table name]['data'].append(tmp row)
  except Exception:
    logger.exception('Reading SQLite db')
  return table dict
This escapes any double quotes within table names by doubling them ("""), which is the standard SQL mechanism for identifier quoting. This prevents breakout from the double-quoted identifier context.

Resources

  • CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
  • OWASP SQL Injection: https://owasp.org/www-community/attacks/SQL Injection
  • Affected File: mobsf/MobSF/utils.py, lines 542-566

Fix

Found an issue in the description? Have something to add? Feel free to write us 👾

Related Identifiers

PYSEC-2026-2662

Affected Products

Mobsf