├── .gitignore ├── LICENSE ├── README.md └── webcache.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Arbaz Hussain 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # web-cache-deception-checker 2 | Simple Tool to check for Cache Deception Attack Both For Authenticated and UnAuthenticated Pages 3 | 4 | 5 | https://medium.com/@arbazhussain/auto-web-cache-deception-tool-2b995c1d1ab2 6 | 7 | python webcache.py urls.txt 8 | 9 | 10 | You Can may also use webbrowser module for it :) for 100% Result. 11 | -------------------------------------------------------------------------------- /webcache.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import sys 3 | 4 | headers = {"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","Upgrade-Insecure-Requests":"1","Connection":"close","User-Agent":"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36","Referer":"https://www.site.com/teams/","Accept-Language":"en-US,en;q=0.8"} 5 | cookies = {"Paste Cookies Here"} 6 | 7 | short_extensions = ['css','png','jpg','gif','txt','js','swf','bmp'] 8 | large_extensions = ['aif','aiff','css','au','avi','bin','bmp','cab','carb','cct','cdf','class','css','doc',' dcr',' dtd',' gcf',' gff',' gif',' grv',' hdml',' hqx',' ico',' ini',' jpeg',' jpg',' js',' mov',' mp3',' nc',' pct',' ppc',' pws',' swa',' swf',' txt',' vbs',' w32',' wav',' wbmp',' wml',' wmlc',' wmls',' wmlsc',' xsd',' zip'] 9 | 10 | auth = requests.Session() 11 | results=[] 12 | possible_result=[] 13 | urls = sys.argv[1] 14 | try: 15 | with open(urls,'r') as f: 16 | for j in f.readlines(): 17 | j=j.strip('\n') 18 | j=j.strip('\r') 19 | url = j 20 | #print url 21 | unsession = requests.get(url) 22 | session = auth.get(url, headers=headers, cookies=cookies) 23 | print '\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n' 24 | print '[+] Authenticated Detail [+] '+'\n' 25 | print 'URL : '+session.url 26 | print 'Status Code : '+str(session.status_code) 27 | print 'Content Length: '+str(len(session.content)) 28 | print '\n' 29 | print '[+] UnAuthenticated Details [+] '+'\n' 30 | print 'URL : '+unsession.url 31 | print 'Status Code : '+str(unsession.status_code) 32 | print 'Content Length : '+str(len(unsession.content))+'\n' 33 | print '\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n' 34 | if unsession.history: 35 | for resp in unsession.history: 36 | print 'Redirected From : '+resp.url 37 | print 'With Status Code : '+str(resp.status_code) 38 | print '\n' 39 | print '\n~~~~~ATTACK~~~~~\n' 40 | for i in short_extensions: 41 | i = i.strip('\n') 42 | i = i.strip('\r') 43 | i = 'testsheet.'+i 44 | newurl=url+i 45 | newsession = auth.get(newurl, headers=headers, cookies=cookies) 46 | print 'Trying ... -> '+str(newurl)+'\n' 47 | conditionContent = str(len(newsession.content)+100) # To Avoid False Positivie 48 | 49 | #print conditionContent 50 | if len(newsession.content) == len(session.content) | (newsession.status_code) == (session.status_code): 51 | print '100% Cache at : '+newurl+str(newsession.status_code)+', Length:'+str(len(newsession.content))+'\n' 52 | results.append(newurl) 53 | elif len(session.content) > len(newsession.content) & (newsession.status_code) == (session.status_code): 54 | if conditionContent >= len(session.content): 55 | print 'Possible Cache at : '+newurl+str(newsession.status_code)+', Length:'+str(len(newsession.content))+'\n' 56 | possible_result.append(newurl) 57 | else: 58 | print 'Not Possible , Status code : '+str(newsession.status_code)+', Length:'+str(len(newsession.content))+'\n' 59 | 60 | except KeyboardInterrupt as e: 61 | print 'Error occured : '+str(e)+'\n' 62 | pass 63 | 64 | print '[+] Results '+str(len(results))+'\n' 65 | print results 66 | 67 | print '[+] Possible Results '+str(len(possible_result))+'\n' 68 | print possible_result 69 | --------------------------------------------------------------------------------