Skip to content

Instantly share code, notes, and snippets.

@gerry
Last active April 8, 2020 11:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gerry/765797 to your computer and use it in GitHub Desktop.
Save gerry/765797 to your computer and use it in GitHub Desktop.
Extracts the embedded source code from Cisco Security Agent Management Console.
#!/usr/bin/env python
"""htl_extract.py, Extracts the embedded source code from Cisco Security Agent Management Console.
Gerry <gerry@hiredhacker.com>
"""
import os
import sys
import struct
import pefile
from itertools import takewhile
_KEY = 'Copyright material protected by a technological protection measure!'
_KEY_LEN = len(_KEY)
def get_section_by_name(pe, name):
try:
section = filter(lambda s: s.Name == name, pe.sections)[0]
except IndexError, e:
return None
return section
def not_null(c):
return c != '\0'
def decrypt_data(data):
decrypted_data = []
for idx, val in enumerate(data):
decrypted_data.append(chr(ord(val) - ord(_KEY[idx % _KEY_LEN])))
return ''.join(decrypted_data)
def main(output_path="./extracted", filename="webadmin.exe", section_name="htlconst"):
pe = pefile.PE(filename, fast_load=True)
# Create the output path is needed, then chdir to it
output_path = os.path.abspath(output_path)
if not os.path.isdir(output_path):
os.makedirs(output_path)
os.chdir(output_path)
# Filename to search for when locating the file table
search_string = "login.htl"
# The htlconst section contains all the data we need
htlconst = get_section_by_name(pe, section_name)
assert htlconst is not None, "Could not find section: %s" % section_name
image_base = pe.OPTIONAL_HEADER.ImageBase
try:
# Get index of known filename in htlconst section data
filename_offset = htlconst.data.index(search_string)
except ValueError:
sys.exit("Could not find search string: %s" % search_string)
# Get address of known filename in htlconst section
filename_rva = filename_offset + htlconst.VirtualAddress + image_base
# Search for a pointer to the filename
filename_ptr = struct.pack("I", filename_rva)
assert filename_ptr in htlconst.data, "Could not find pointer to filename"
# Search back for 0x00000000 and add 4 to get the start of the struct
table_offset = None
filename_ptr_idx = htlconst.data.index(filename_ptr)
# Is there a better way todo this?
for idx in reversed(xrange(0, filename_ptr_idx, 4)):
if htlconst.data[idx:idx+4] == "\x00\x00\x00\x00":
table_offset = idx + 4
break
assert table_offset is not None, "Could not find start of file table"
cur_offset = table_offset
while True:
# unpack pointers to the filename and its 'encrypted' contents
(ptr_filename, ptr_data) = struct.unpack('II', htlconst.data[cur_offset:cur_offset + 8])
# Check for the end of the structure
if not htlconst.contains_rva(ptr_data - image_base):
break
filename = pe.get_string_at_rva(ptr_filename - image_base)
print '[+] Extracting: %s' % filename
data_offset = (ptr_data - image_base - htlconst.VirtualAddress)
encrypted_data = takewhile(not_null, htlconst.data[data_offset:])
decrypted_data = decrypt_data(encrypted_data)
f = open(filename, 'w')
f.write(decrypted_data)
f.close()
cur_offset += 8
print "[+] Done extracting %d files." % ((cur_offset - table_offset)/8)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment