Closed Q1984 closed 4 years ago
Can you check with the latest version? Heard some earlier reports of that but was before I put more refined exception handling in place
Closing this one, I can confirm this is not the case.
Latest version of python requests library does not play well with this. It will normalize/canonicalize URLs before sending the request, eliminating the directory traversal from the request, and also follows redirects making it less clear what happened.
eg. The 'req.url' at the end here shows a redirect was followed:
$ python3
Python 3.7.4 (default, Dec 13 2019, 01:02:18)
[GCC 7.3.1 20180712 (Red Hat 7.3.1-6)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import requests
>>> req = requests.get('https://x.x.x.x/vpn/../vpns/etc/smb.conf')
>>> req.content
b'<!DOCTYPE html PUBLIC "-//W3C//DTD XDEV_HTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
... snipped ...
</body>\r\n</html>\r\n'
>>> req.status_code
200
>>> req.url
'https://x.x.x.x/vpn/tmindex.html'
Latest requests library allows not following redirects, but will still normalize the request URL, which hides the vulnerability:
$ python3
Python 3.7.4 (default, Dec 13 2019, 01:02:18)
[GCC 7.3.1 20180712 (Red Hat 7.3.1-6)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import requests
>>> req = requests.get('https://x.x.x.x/vpn/../vpns/etc/smb.conf', allow_redirects=False)
>>> req.content
b'<html><head><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8"><script type="text/javascript" src="/vpn/resources.js"></script><script type="text/javascript" language="javascript">var Resources = new ResourceManager("/logon/themes/Default/resources/{lang}", "REDIRECTION_BODY");</script></head><body><span id="This object may be found "></span><a href="/vpn/tmindex.html"><span id="here"></span></a><span id="Trailing phrase after here"></span><script type="text/javascript" language="javascript">Resources.Load();</script></body></html>'
>>> req.status_code
302
>>> req.url
'https://x.x.x.x/vpns/etc/smb.conf'
>>>
Correction: the problematic library is actually urllib3 (which was also downgraded when I downgraded requests). The new behaviour appears to have been introduced in version 1.25.0.
Just was looking for a solution to this myself. I was left scratching my head, as I don't see an option to prevent urllib from preprocessing the URL. Also tried a rewrite to use pycurl but the ../ is also removed from the URL using that library.
Verified with tcpdump the GET request being made is: GET /vpns/cfg/smb.conf HTTP/1.1
A real hacky workaround is to execute the curl binary via os.system.
As the same as in the exploit script. Gives a false negative and exit.
Also tried a rewrite to use pycurl but the ../ is also removed from the URL using that library.
You can use the "path as is" option in PyCurl:
curl.setopt(pycurl.PATH_AS_IS, 1)
A real hacky workaround is to execute the curl binary via os.system.
Ewwwww
I'm using requests 2.18.4 and is working fine:
root@stronghold-nix:/home/relik/Desktop/git/cve-2019-19781# pip3 freeze | grep requests requests==2.18.4
Digging into this
I've updated the requirements section to reflect that the version of requests needs to be 2.18.4 or above (most recent version in pip repositories is 2.22). This does appear to be a bug in older version of python requests.
Running pip3 install -r requirements should alleviate any issues.
requests isn't actually the issue, urllib3 is - versions since 1.25.0 use a new url parser that rewrites/normalizes URLs and eliminates the directory traversal. the problem is with new versions, not old.
Thanks for the info, for the time being I put a check in for requirements to be <=urllib3==1.24.3.
Looks to be this one here:
Upgraded urllib3.utils.parse_url() to be RFC 3986 compliant. (Pull #1487)
I downloaded this script on Saturday, and again today. The version from Saturday works as expected and gives me 3 positives. The version from today has a different output (no separate line per server scanned), and gives me 0 positives. I have tested this on two different Linux servers, one on the inside of our firewall, and one on the outside. Both servers have the same versions of both "requests" and "liburl3".
I do not appear to be able to reproduce this - I've scanned entire class B's, and C's without any issues and shows them properly. I would ensure you have the proper outbound filtering rules for the ports you are scanning. The only thing that has substantially changed since Saturday is a more defined response from the smb.conf but wouldn't have any impact on anything else related to detect/non detect.
root@stronghold-nix:/home/relik/Desktop/git/cve-2019-19781# ./cve-2019-19781_scanner.py
/ \ \ / / | | \ / \/ |/ \ / |/ ____ / \/ | | | \ \ / /| | ____ ) | | | || | () |____| | () | / / () || | | | \ \/ / | |____/ /| | | || |_, |__| |__, | / / > < | | | | \ / | | / /| || || | / / | | / / / / | () || | _| \/ |__| |__|\/ || // || // /_/ __/ ||
CVE-2019-19781-Scanner Company: TrustedSec Written by: Dave Kennedy This will look to see if the remote system is still vulnerable to CVE-2019-19781. This will only scan one host at a time. You can use CIDR notations as well for example: 192.168.1.1/24 You can use hostnames instead of IP addresses also. Example: python3 cve-2019-19781_scanner.py 192.168.1.1/24 443 Example2: python3 cve-2019-19781_scanner.py 192.168.1.1 443 Example3: python3 cve-2019-19781_scanner.py fakewebsiteaddress.com 443 Example4: python3 cve-2019-19781_scanner.py as15169 443 Usage: python3 cve-2019-19781_scanner.py targetip targetport
Since I am scanning from the same two servers (one at work, one at home), I find it weird that outbound rules would impact this. The script from Saturday gives me three positives both from at home and work, while the script from today gives me zero positives.
The older version has three example commands in the header, the newer version has four. The file sizes differ with approx 2000 bytes.
Mentioned in previously response, there were changes on Saturday and through Sunday which would indicate the file size difference. The main changes on Saturday were to ensure and validate it was a Citrix server. Are the three you are scanning actually Citrix systems.
Some of them are, and the output from the older version of the script indicates the different responses while it traverses through the subnet:
[-] Server xxx.yyy.zzz.1 does not appear to be a Citrix server. [-] ConnectionError: Server xxx.yyy.zzz.2 did not respond to a web request or the port (80) is not open. [!] This Citrix ADC Server: xxx.yyy.zzz.3 is still vulnerable to CVE-2019-19781. [-] ReadTimeout: Server xxx.yyy.zzz.4 timed out and didn't respond on port: 80. [*] Awesome! The server xxx.yyy.zzz.5 is not vulnerable.
The newer version of the script gives me no output per server, just a summary line at the end.
Yes, the readout from the servers was removed/minimized and just gives a summary report out at the end. Sorry to hear it's not reporting out, I have validated that it is properly looking for the servers and is finding them. I would check around to see if you can actually get the responses back. It is working as intended.
I just did a pip3 install -r requirements.txt
which resulted in Successfully installed urllib3-1.24.3 whois-0.9.5
Seems to be working on my end:
x.x.x.116
If you want the output similarly to Saturday, you can type:
python3 cve-2019-19781.py ipaddr port verbose
Will need to git pull the latest version, just added verbose option.
Going to close this one out since it appears fixed in the requirements for now. I submitted for newer versions of urllib3 and will see if they fix. If you follow the installation of versions in the requirements it works fine.
Opened a new issue for urllib3 here: https://github.com/urllib3/urllib3/issues/1781
This has been addressed in a better way by using prepare. It is now working in all versions of urllib3.
with requests.Session() as s: r = requests.Request(method='POST', url=url, data=data, headers=headers) prep = r.prepare() prep.url = url req = s.send(prep, verify=False)
Scanner gives false negative results, but manually curling gives the smb.