scanner.provider.traversal returned exception error.
thesinghsec opened this issue · 4 comments
- It was working well in previous version of Drozer, upon updating I got this error again and again.
dz> run scanner.provider.traversal -a com.mwr.example.sieve
Attempting to run shell module
Scanning com.mwr.example.sieve...
Exception occured: No files supported by provider at content://com.mwr.example.sieve.DBContentProvider/Passwords//../../../../../../../../../../../../../../../../etc/hosts
root@d7feae1123d5:/# drozer console connect --server host.docker.internal --debug
Selecting ebe9fcc0c47b28da (Google sdk_gphone64_x86_64 12)
.. ..:.
..o.. .r..
..a.. . ....... . ..nd
ro..idsnemesisand..pr
.otectorandroidsneme.
.,sisandprotectorandroids+.
..nemesisandprotectorandroidsn:.
.emesisandprotectorandroidsnemes..
..isandp,..,rotecyayandro,..,idsnem.
.isisandp..rotectorandroid..snemisis.
,andprotectorandroidsnemisisandprotec.
.torandroidsnemesisandprotectorandroid.
.snemisisandprotectorandroidsnemesisan:
.dprotectorandroidsnemesisandprotector.
drozer Console (v3.0.1 debug mode)
dz> run scanner.provider.traversal --uri content://com.withsecure.example.sieve.provider.DBContentProvider
Attempting to run shell module
exception in module: ReflectionException: No files supported by provider at content://com.withsecure.example.sieve.provider.DBContentProvider/../../../../../../../../../../../../../../../../etc/hosts
Traceback (most recent call last):
File "/usr/local/lib/python3.10/dist-packages/drozer/console/session.py", line 402, in do_run
module.run(argv[1:])
File "/usr/local/lib/python3.10/dist-packages/drozer/modules/base.py", line 183, in run
result = self.execute(arguments)
File "/usr/local/lib/python3.10/dist-packages/drozer/modules/scanner/provider/traversal.py", line 26, in execute
self.__test_uri(arguments.package_or_uri, vulnerable)
File "/usr/local/lib/python3.10/dist-packages/drozer/modules/scanner/provider/traversal.py", line 53, in __test_uri
data = self.contentResolver().read(uri + "/../../../../../../../../../../../../../../../../etc/hosts")
File "/usr/local/lib/python3.10/dist-packages/drozer/modules/common/provider.py", line 127, in read
fd = client.openFile(self.parseUri(uri), "r")
File "/usr/local/lib/python3.10/dist-packages/pysolar/reflection/types/reflected_object.py", line 64, in _invoker
result = self._reflector.invoke(self, method_name,
File "/usr/local/lib/python3.10/dist-packages/pysolar/reflection/reflector.py", line 83, in invoke
raise ReflectionException(response.reflection_response.errormessage)
pysolar.reflection.exceptions.ReflectionException: No files supported by provider at content://com.withsecure.example.sieve.provider.DBContentProvider/../../../../../../../../../../../../../../../../etc/hosts
yup legit. we'll look into it
So, this is a fun one.
At its core, the problem boils down to the logic of the __test_uri()
function of the scanner.provider.traversal
module. In drozer 2, this was implemented as follows:
def __test_uri(self, uri, vulnerable):
try:
data = self.contentResolver().read(uri + "/../../../../../../../../../../../../../../../../etc/hosts")
except ReflectionException as e:
if e.message.find("java.io.FileNotFoundException") >= 0 or \
e.message.find("java.lang.IllegalArgumentException") >= 0 or \
e.message.find("java.lang.SecurityException") >= 0 or \
e.message.find("No content provider") >= 0 or \
e.message.find("RuntimeException"):
data = ""
else:
raise
if data != None and len(data) > 0:
vulnerable.add(uri)
A cursory read of the try
/except
block makes it seem like an exception should be suppressed if it contains one of five strings, and raised otherwise. So, it got ported to drozer 3 as:
def __test_uri(self, uri, vulnerable):
try:
data = self.contentResolver().read(uri + "/../../../../../../../../../../../../../../../../etc/hosts")
except ReflectionException as e:
if "java.io.FileNotFoundException" in str(e) or \
"java.lang.IllegalArgumentException" in str(e) or \
"java.lang.SecurityException" in str(e) or \
"No content provider" in str(e) or \
"RuntimeException" in str(e):
data = ""
else:
raise
if data != None and len(data) > 0:
vulnerable.add(uri)
This is all sensible at a glance, but the drozer 2 logic was actually flawed due to a missing >= 0
- if the string RuntimeException
was NOT found, string.find()
would return -1
, causing the entire if
statement to evaluate as True
. In practice, this means that it set data
to ""
if e.message
contained one of the first four strings OR didn't start with RuntimeException
. The only way for it to really go False
would be to start with RuntimeException
and not contain any of the other four strings. This probably always evaluated as True
, and so the exception was likely never raised.
Now, drozer 3's logic attempts to implement the same intention, but without the same error. As a result it ends up with a statement that throws the exception much more often. My suspicion is that drozer 2 worked by accident.
The obvious solution here appears to be to remove the if
statement entirely, and to gracefully handle all exceptions that come from the content provider. In the future, we may add handlers for scenarios like a non-existent provider (to give a meaningful message to the user), but for now this will make the module work as it did before.
Once pull request #427 is merged, please try building drozer 3 and see if this works for you. I strongly suspect it will.
We'll keep the issue open, because ideally we should add a few exception type checks here and there.
Keeping this open as a reminder to do further work on those exception checks
Okay, so, having done a bit more work on that, realistically speaking better error checking here is gonna be difficult. Different versions of Android throw different exceptions, and Catching 'Em All™ is likely not practical. Closing this for the time being - it currently works as well as drozer 2 ever did