├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── bug_report.md_dis └── workflows │ ├── antinoobs.yml │ ├── kvm.yml │ └── shellcheck.yml ├── Databases ├── Mongo.sh └── MongoGetDups.md ├── LICENSE.txt ├── Mac ├── VPN_split.sh └── yara.sh ├── Networking ├── fix_iptables.sh └── visc2ovpn.py ├── README.md ├── Reversing ├── FullColor.idc ├── FullColor.py ├── README.md └── flare-emu-string-deobfuscation.py ├── Virtualization ├── README.md ├── kvm-qemu.sh ├── libguestfs.sh └── libguestfs │ └── Dockerfile ├── Vol3 ├── pony.py └── zbotscan.py └── Windows ├── choco.bat └── disable_win7noise.bat /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | custom: ["https://paypal.me/brukhovetskyy"] 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md_dis: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | ## This is opensource and you getting __free__ support so be friendly! 8 | * Wants to say thank you -> [buy me a beer](https://opencollective.com/virustotalapi) 9 | 10 | # Prerequisites 11 | 12 | Please answer the following questions for yourself before submitting an issue. 13 | 14 | - [ ] I do understand that this repo is not a proper place for CAPE issues 15 | - [ ] I checked to make sure that this issue has not already been filed 16 | - [ ] I'm reporting the issue to the correct repository (for multi-repository projects) 17 | - [ ] I read my log of instalation, all issues will be closed if you don't do your part of work 18 | - [ ] I understand that reporting issue related to any instalation script without instalation log is useless and will be closed 19 | - [ ] Without proper details and remove template your issue will be IGNORED! 20 | 21 | # Expected Behavior 22 | 23 | Please describe the behavior you are expecting 24 | 25 | # Current Behavior 26 | 27 | What is the current behavior? 28 | 29 | # Failure Information (for bugs) 30 | 31 | Please help provide information about the failure if this is a bug. If it is not a bug. 32 | 33 | ## Steps to Reproduce 34 | 35 | Please provide detailed steps for reproducing the issue. 36 | 37 | 1. step 1 38 | 2. step 2 39 | 3. you get it... 40 | 41 | ## Context 42 | 43 | Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions. 44 | 45 | | Question | Answer 46 | |------------------|-------------------- 47 | | OS version | Ubuntu 20.04, Windows 10, macOS 10.15, etc 48 | | Software version | QEMU 5, virsh 6.2, etc 49 | 50 | ## Failure Logs 51 | 52 | Please include any relevant log snippets or files here. 53 | -------------------------------------------------------------------------------- /.github/workflows/antinoobs.yml: -------------------------------------------------------------------------------- 1 | on: 2 | issues: 3 | types: [opened, edited] 4 | 5 | jobs: 6 | auto_close_issues: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v1 11 | - name: Automatically close issues that don't follow the issue template 12 | uses: lucasbento/auto-close-issues@v1.0.2 13 | with: 14 | github-token: ${{ secrets.GITHUB_TOKEN }} 15 | issue-close-message: "@${issue.user.login}: hello! :wave:\n\nThis issue is being automatically closed because it does not follow the issue template. If you don't care to provide proper details. I won't waste my time here" # optional property 16 | closed-issues-label: "🙁 Not following issue template" # optional property 17 | -------------------------------------------------------------------------------- /.github/workflows/kvm.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branch: 4 | - master 5 | 6 | name: Test build of KVM-QEMU 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Run script file 14 | run: | 15 | sudo apt install libaio-dev libaio1 libxml2-utils -y 16 | chmod a+x "${GITHUB_WORKSPACE}/Virtualization/kvm-qemu.sh" 17 | BUILD_ENV=1 "${GITHUB_WORKSPACE}/Virtualization/kvm-qemu.sh" all 18 | 19 | shell: bash 20 | -------------------------------------------------------------------------------- /.github/workflows/shellcheck.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branch: 4 | - master 5 | 6 | name: 'Trigger: Push action' 7 | 8 | jobs: 9 | shellcheck: 10 | name: Shellcheck 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Run ShellCheck 15 | uses: ludeeus/action-shellcheck@master 16 | -------------------------------------------------------------------------------- /Databases/Mongo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #show how to upgrade old mongo to newer on Debian 4 | 5 | function update_install { 6 | sudo apt-get update 7 | sudo apt-get install -y mongodb-org 8 | } 9 | 10 | function wipe_mongo(){ 11 | dpkg -l|grep mongo| cut -f 3 -d " "|xargs dpkg --remove 12 | } 13 | 14 | # mongo 3.4 debian 9 - https://docs.mongodb.com/v3.4/tutorial/install-mongodb-on-debian/ 15 | # https://blog.m157q.tw/posts/2018/07/24/upgrade-mongodb-from-3-2-to-3-4-on-debian-9/ 16 | # http://repo.mongodb.org/apt/debian/dists/jessie/mongodb-org/3.4/main/binary-amd64/ 17 | wget http://security.debian.org/debian-security/pool/updates/main/o/openssl/libssl1.0.0_1.0.1t-1+deb8u11_amd64.deb && dpkg -i libssl1.0.0_1.0.1t-1+deb8u11_amd64.deb 18 | wget http://repo.mongodb.org/apt/debian/dists/jessie/mongodb-org/3.4/main/binary-amd64/mongodb-org-server_3.4.18_amd64.deb && dpkg -i mongodb-org-server_3.4.18_amd64.deb 19 | wget http://repo.mongodb.org/apt/debian/dists/jessie/mongodb-org/3.4/main/binary-amd64/mongodb-org-shell_3.4.20_amd64.deb && dpkg -i mongodb-org-shell_3.4.20_amd64.deb 20 | mongo --eval 'db.adminCommand({setFeatureCompatibilityVersion: "3.4"})' 21 | 22 | # mongo3.6 23 | wipe_mongo 24 | sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2930ADAE8CAF5059EE73BB4B58712A2291FA4AD5 25 | echo "deb http://repo.mongodb.org/apt/debian stretch/mongodb-org/3.6 main" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.6.list 26 | update_install 27 | mongo --eval 'db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )' 28 | mongo --eval 'db.adminCommand({setFeatureCompatibilityVersion: "3.6"})' 29 | 30 | # mongo 4 31 | wipe_mongo 32 | rm /etc/apt/sources.list.d/mongodb-org-3.6.list 33 | sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4 34 | echo "deb http://repo.mongodb.org/apt/debian stretch/mongodb-org/4.0 main" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.0.list 35 | update_install 36 | sudo systemctl unmask mongodb 37 | systemctl start mongo 38 | -------------------------------------------------------------------------------- /Databases/MongoGetDups.md: -------------------------------------------------------------------------------- 1 | # https://www.compose.com/articles/finding-duplicate-documents-in-mongodb/ 2 | 3 | * get duplicated values 4 | ``` 5 | use yeti 6 | db.observable.aggregate([ 7 | {$group: { _id: {value: "$value"}, count: {$sum: 1}}}, 8 | {$match: {count: {"$gt": 1}}}, 9 | {$sort: {count: -1}}], 10 | {allowDiskUse:true, cursor:{}} 11 | ); 12 | ``` 13 | 14 | * remove duplicated 15 | ``` 16 | var duplicates = []; 17 | db.observable.aggregate([ 18 | {$group: { _id: {value: "$value"}, count: {$sum: 1}, dups: { "$addToSet": "$_id" },}}, 19 | {$match: {count: {"$gt": 1}}}, 20 | {$sort: {count: -1}}], 21 | {allowDiskUse:true, cursor:{}} 22 | ).forEach(function(doc) { 23 | doc.dups.shift(); // First element skipped for deleting 24 | doc.dups.forEach( function(dupId){ 25 | duplicates.push(dupId); // Getting all duplicate ids 26 | } 27 | ) 28 | }); 29 | 30 | db.observable.remove({_id:{$in:duplicates}}) 31 | ``` 32 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2018-2019 doomedraven 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /Mac/VPN_split.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # replace "yourcomapy.com." with your real root domain 3 | # replace "VPN_IP" with real VPN public ip 4 | # replace dns1 and dns2 with your real dns 5 | # To get real dns, add --dump --verbose to vpn-slice when starts with openconnect 6 | 7 | function _check_brew() { 8 | if [ ! -f /usr/local/bin/brew ]; then 9 | /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 10 | fi 11 | } 12 | 13 | 14 | _check_brew 15 | brew install openconnect git unbound 16 | pip3 install git+https://github.com/dlenski/vpn-slice.git 17 | 18 | 19 | # how to configure local dns server 20 | # https://calomel.org/unbound_dns.html  21 | # https://sizeof.cat/post/unbound-on-macos/ 22 | # https://nlnetlabs.nl/documentation/unbound/unbound.conf/ 23 | #(curl --silent https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/fakenews-gambling-porn-social/hosts | grep '^0\.0\.0\.0' | sort) | awk '{print "local-zone: \""$2"\" refuse"}' > /usr/local/etc/unbound/zone-block-general.conf 24 | sudo dscl . -create /Groups/_unbound 25 | sudo dscl . -create /Groups/_unbound PrimaryGroupID 444 26 | sudo dscl . -create /Users/_unbound 27 | sudo dscl . -create /Users/_unbound RecordName _unbound unbound 28 | sudo dscl . -create /Users/_unbound RealName "Unbound DNS server" 29 | sudo dscl . -create /Users/_unbound UniqueID 444 30 | sudo dscl . -create /Users/_unbound PrimaryGroupID 444 31 | sudo dscl . -create /Users/_unbound UserShell /usr/bin/false 32 | sudo dscl . -create /Users/_unbound Password '*' 33 | sudo dscl . -create /Groups/_unbound GroupMembership _unbound 34 | 35 | sudo /usr/local/opt/unbound/sbin/unbound-anchor -a /usr/local/etc/unbound/root.key 36 | sudo /usr/local/opt/unbound/sbin/unbound-control-setup -d /usr/local/etc/unbound 37 | sudo cp /usr/local/etc/unbound/unbound.conf /usr/local/etc/unbound/unbound.conf_original 38 | sudo curl --silent -o /usr/local/etc/unbound/root.hints https://www.internic.net/domain/named.cache 39 | 40 | cat >> /usr/local/etc/unbound/unbound.conf << EOL 41 | server: 42 | # log verbosity 43 | verbosity: 3 44 | # domain-insecure: * 45 | # logfile: "/tmp/unbound.log" 46 | # log-queries: yes 47 | # log-time-ascii: yes 48 | interface: 127.0.0.1 49 | access-control: 127.0.0.1/8 allow 50 | chroot: "" 51 | username: "_unbound" 52 | # auto-trust-anchor-file: "/usr/local/etc/unbound/root.key" 53 | # answer DNS queries on this port 54 | port: 53 55 | # enable IPV4 56 | do-ip4: yes 57 | # disable IPV6 58 | do-ip6: no 59 | # enable UDP 60 | do-udp: yes 61 | # enable TCP, you could disable this if not needed, UDP is quicker 62 | do-tcp: yes 63 | # which client IPs are allowed to make (recursive) queries to this server 64 | access-control: 10.0.0.0/8 allow 65 | access-control: 127.0.0.0/8 allow 66 | access-control: 192.168.0.0/16 allow 67 | root-hints: "/usr/local/etc/unbound/root.hints" 68 | # do not answer id.server and hostname.bind queries 69 | hide-identity: yes 70 | # do not answer version.server and version.bind queries 71 | hide-version: yes 72 | # will trust glue only if it is within the servers authority 73 | harden-glue: yes 74 | # require DNSSEC data for trust-anchored zones, if such data 75 | # is absent, the zone becomes bogus 76 | harden-dnssec-stripped: yes 77 | # use 0x20-encoded random bits in the query to foil spoof attempts 78 | use-caps-for-id: yes 79 | # the time to live (TTL) value lower bound, in seconds 80 | cache-min-ttl: 3600 81 | # the time to live (TTL) value cap for RRsets and messages in the cache 82 | cache-max-ttl: 86400 83 | # perform prefetching of close to expired message cache entries 84 | prefetch: yes 85 | num-threads: 4 86 | msg-cache-slabs: 8 87 | rrset-cache-slabs: 8 88 | infra-cache-slabs: 8 89 | key-cache-slabs: 8 90 | rrset-cache-size: 256m 91 | msg-cache-size: 128m 92 | so-rcvbuf: 1m 93 | private-address: 192.168.0.0/16 94 | private-address: 172.16.0.0/12 95 | private-address: 10.0.0.0/8 96 | private-domain: "home.lan" 97 | unwanted-reply-threshold: 10000 98 | val-clean-additional: yes 99 | # additional blocklist (Steven Black hosts file, read above) 100 | # include: /usr/local/etc/unbound/zone-block-general.conf 101 | private-domain: "yourcomapy.com." 102 | local-data: "vpn.yourcomapy.com. IN A VPN_IP" 103 | remote-control: 104 | control-enable: yes 105 | control-interface: 127.0.0.1 106 | server-key-file: "/usr/local/etc/unbound/unbound_server.key" 107 | server-cert-file: "/usr/local/etc/unbound/unbound_server.pem" 108 | control-key-file: "/usr/local/etc/unbound/unbound_control.key" 109 | control-cert-file: "/usr/local/etc/unbound/unbound_control.pem" 110 | 111 | forward-zone: 112 | name: "yourcomapy.com." 113 | #forward-ssl-upstream:yes 114 | forward-addr: dns1 115 | forward-addr: dns2 116 | 117 | forward-zone: 118 | name: "." 119 | # forward-ssl-upstream: yes 120 | forward-addr: 1.1.1.1@53#one.one.one.one 121 | forward-addr: 8.8.8.8@53#dns.google 122 | forward-addr: 9.9.9.9@53#dns.quad9.net 123 | forward-addr: 1.0.0.1@53#one.one.one.one 124 | forward-addr: 8.8.4.4@53#dns.google 125 | forward-addr: 149.112.112.112@53#dns.quad9.net 126 | EOL 127 | 128 | sudo chown -R _unbound:staff /usr/local/etc/unbound 129 | sudo chmod 640 /usr/local/etc/unbound/* 130 | 131 | sudo brew services start unbound 132 | 133 | networksetup -setdnsservers Wi-Fi 127.0.0.1 134 | networksetup -getdnsservers Wi-Fi 135 | 136 | -------------------------------------------------------------------------------- /Mac/yara.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -f "/usr/local/bin/brew" ]; then 4 | /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 5 | fi 6 | 7 | brew install yara 8 | ARCHFLAGS=-Wno-error=unused-command-line-argument-hard-error-in-future python3 -m pip install yara-python 9 | -------------------------------------------------------------------------------- /Networking/fix_iptables.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Fix when docker breaks your iptables 3 | if [ $# -eq 0 ] || [ $# -lt 2 ]; then 4 | echo "$0 " 5 | echo " example: $0 192.168.1.0 virbr0 eno0" 6 | exit 1 7 | fi 8 | 9 | echo "[+] Setting iptables" 10 | iptables -t nat -A POSTROUTING -o "$2" -j MASQUERADE 11 | iptables -A FORWARD -i "$2" -o "$2" -m state --state RELATED,ESTABLISHED -j ACCEPT 12 | iptables -A FORWARD -i "$2" -o "$2" -j ACCEPT 13 | iptables -I FORWARD -m physdev --physdev-is-bridged -j ACCEPT 14 | iptables -I FORWARD -o "$2" -d "$1"/24 -j ACCEPT 15 | iptables -t nat -A POSTROUTING -s "$1"/24 -j MASQUERADE 16 | iptables -A FORWARD -o "$2" -m state --state RELATED,ESTABLISHED -j ACCEPT 17 | iptables -A FORWARD -i "$2" -o "$3" -j ACCEPT 18 | iptables -A FORWARD -i "$2" -o lo -j ACCEPT 19 | 20 | echo "[+] Setting network options" 21 | # https://forums.fedoraforum.org/showthread.php?312824-Bridge-broken-after-docker-install&s=ffc1c60cccc19e46c01b9a8e0fcd0c35&p=1804899#post1804899 22 | { 23 | echo "net.bridge.bridge-nf-call-ip6tables=0"; 24 | echo "net.bridge.bridge-nf-call-iptables=0"; 25 | echo "net.bridge.bridge-nf-call-arptables=0"; 26 | echo "net.ipv4.conf.all.forwarding=1"; 27 | echo "net.ipv4.ip_forward=1"; 28 | } >> /etc/sysctl.conf 29 | sysctl -p 30 | echo "iptables -A FORWARD -i $2 -o $2 -j ACCEPT" >> /etc/network/if-pre-up.d/kvm_bridge_iptables 31 | 32 | virsh nwfilter-list 33 | -------------------------------------------------------------------------------- /Networking/visc2ovpn.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """Converts viscosity export files into an open vpn package 4 | Usage: viscosity-to-openvpn.py 5 | # source https://gist.github.com/brianbruggeman/f032f5b8e4b7fc1c63c8691071be5946 6 | """ 7 | import io 8 | import os 9 | import sys 10 | import tarfile 11 | 12 | import click 13 | 14 | if sys.version.startswith('3'): 15 | unicode = str 16 | 17 | 18 | # ---------------------------------------------------------------------- 19 | # Exceptions 20 | # ---------------------------------------------------------------------- 21 | class ConversionError(Exception): 22 | """Base conversion error""" 23 | pass 24 | 25 | 26 | class NoConnectionName(ConversionError): 27 | """No connection name was available""" 28 | pass 29 | 30 | 31 | class NoCertificateData(ConversionError): 32 | """No certificate data was found within certificate file""" 33 | pass 34 | 35 | 36 | class NoCertificateFile(ConversionError): 37 | """File was not available within archive""" 38 | pass 39 | 40 | # ---------------------------------------------------------------------- 41 | # Command-line Interface 42 | # ---------------------------------------------------------------------- 43 | @click.command() 44 | @click.argument('input-path', type=click.Path(exists=True)) 45 | @click.argument('output', required=False, type=click.Path(), default=None) 46 | def convert(input_path, output=None): 47 | '''Converts Viscosity package 48 | Args: 49 | input (str): path to folder or file input 50 | output (str): path to folder output [default: None] 51 | ''' 52 | if input_path.endswith('.visc'): 53 | output = input_path if output is None else output 54 | if output and not os.path.exists(output): 55 | output = input_path 56 | files = [os.path.join(input_path, filename) for filename in os.listdir(input_path)] 57 | for config_fp in files: 58 | new_config = [] 59 | if config_fp.endswith('.conf'): 60 | with io.open(config_fp, encoding='utf-8') as stream: 61 | connection_name = extract(stream, new_config, input_path=input_path) 62 | 63 | new_config.insert(0, '# OpenVPN Config for {}'.format(connection_name)) 64 | new_config = '\n'.join(new_config) + '\n' 65 | output_filepath = os.path.join(output, '{}.ovpn'.format(connection_name)) 66 | with io.open(output_filepath, 'w', encoding='utf-8') as out: 67 | out.write(unicode(new_config)) 68 | 69 | print('Wrote: {}'.format(output_filepath)) 70 | 71 | elif input_path.endswith('.visz'): 72 | if output is None: 73 | output = os.path.dirname(input_path) 74 | 75 | data = {} 76 | with tarfile.open(input_path) as zipped: 77 | for filepath, fileinfo in zip(zipped.getnames(), zipped.getmembers()): 78 | if not fileinfo.isfile(): 79 | continue 80 | filename = filepath.split(os.path.sep)[-1] 81 | data[filename] = zipped.extractfile(filepath).read() 82 | 83 | for key in data: 84 | if not key.endswith('.conf') or key.startswith('.'): 85 | continue 86 | 87 | new_config = [] 88 | lines = data[key].split('\n') 89 | connection_name = extract(lines, new_config, file_data=data) 90 | 91 | new_config.insert(0, '# OpenVPN Config for {}'.format(connection_name)) 92 | new_config = '\n'.join(new_config) + '\n' 93 | output_filepath = os.path.join(output, '{}.ovpn'.format(connection_name)) 94 | with io.open(output_filepath, 'w', encoding='utf-8') as out: 95 | out.write(unicode(new_config)) 96 | 97 | print('Wrote: {}'.format(output_filepath)) 98 | 99 | 100 | # ---------------------------------------------------------------------- 101 | # CLI Support 102 | # ---------------------------------------------------------------------- 103 | def extract(data, new_config, input_path=None, file_data={}): 104 | certificate_files = ['ca', 'cert', 'key', 'tls-auth'] 105 | connection_name = '' 106 | for line in data: 107 | line = line.rstrip() 108 | 109 | if not line.strip(): 110 | continue 111 | 112 | # This was an invalid configuration, for some reason 113 | elif line == 'compress lzo': 114 | continue 115 | 116 | elif line.startswith('#'): 117 | if line.startswith('#viscosity name'): 118 | connection_name = line.split('#viscosity name ', 1)[-1] 119 | connection_name = connection_name.strip() 120 | continue 121 | 122 | try: 123 | key, value = line.split(' ', 1) 124 | value = value.strip() 125 | except ValueError: 126 | key, value = line, '' 127 | 128 | if key in certificate_files: 129 | if key == 'tls-auth': 130 | try: 131 | value, direction = value.split(' ', 1) 132 | new_config.append('key-direction {}'.format(direction)) 133 | except ValueError: 134 | pass 135 | 136 | if input_path: 137 | cert_filepath = os.path.join(input_path, value) 138 | with io.open(cert_filepath, encoding='utf-8') as cf: 139 | certificate = cf.read() 140 | else: 141 | if value not in file_data: 142 | raise NoCertificateFile('Could not find certificate file in archive') 143 | certificate = file_data.get(value) 144 | 145 | if not certificate: 146 | raise NoCertificateData('Could not find certificate data') 147 | 148 | new_config.append('<%s>' % key) 149 | new_config.append(certificate) 150 | new_config.append('' % key) 151 | continue 152 | 153 | new_config.append(line) 154 | 155 | if not connection_name.strip(): 156 | raise NoConnectionName('Could not find connection name in file. Aborting') 157 | 158 | return connection_name 159 | 160 | 161 | if __name__ == '__main__': 162 | convert() 163 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tools 2 | Combination of different utilities, have fun! 3 | 4 | ## License 5 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fdoomedraven%2FTools.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fdoomedraven%2FTools?ref=badge_large) 6 | -------------------------------------------------------------------------------- /Reversing/FullColor.idc: -------------------------------------------------------------------------------- 1 | #include 2 | static main(void) { 3 | auto currentEA; 4 | auto currentMnem; 5 | auto prevMnem; 6 | auto currentOp; 7 | prevMnem = ""; 8 | currentOp; 9 | currentEA = FirstSeg(); 10 | currentEA = NextHead(currentEA, 0xFFFFFFFF); 11 | while (currentEA != BADADDR) { 12 | currentMnem = GetMnem(currentEA); 13 | 14 | //Highlight call functions 15 | if (currentMnem == "call") 16 | SetColor(currentEA, CIC_ITEM, 0xc7c7ff); 17 | //Non-zeroing XORs are often signs of data encoding 18 | if (currentMnem == "xor") { 19 | if (GetOpnd(currentEA, 0) != GetOpnd(currentEA, 1)) { 20 | SetColor(currentEA, CIC_ITEM, 0xFFFF00); 21 | } 22 | } 23 | //Instructions used for Anti-VM, sidt, sgdt, sldt, smsw, str, in, cpuid 24 | if (currentMnem == "sidt" || currentMnem == "sgdt" || currentMnem == "sldt" 25 | || currentMnem == "smsw" || currentMnem == "str" 26 | || currentMnem == "in" || currentMnem == "cpuid") 27 | SetColor(currentEA, CIC_ITEM, 0xFFFF00); 28 | 29 | //Highlight interrupts in code as an anti-debugging measure 30 | if (currentMnem == "int" && 31 | (GetOpnd(currentEA, 0) == "3" || GetOpnd(currentEA, 0) == "2D")) { 32 | SetColor(currentEA, CIC_ITEM, 0xFFFF00); 33 | } 34 | 35 | //Highlight other instructions sometimes used for anti-debugging 36 | if (currentMnem == "rdtsc" || currentMnem == "icebp") { 37 | SetColor(currentEA, CIC_ITEM, 0xFFFF00); 38 | } 39 | //Highlight push/ret combinations as a shellcode 40 | if (currentMnem == "ret" && prevMnem == "push") 41 | SetColor(currentEA, CIC_ITEM, 0xFFFF00); 42 | 43 | currentEA = NextHead(currentEA, 0xFFFFFFFF); 44 | prevMnem = currentMnem; 45 | } 46 | } -------------------------------------------------------------------------------- /Reversing/FullColor.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | # http://www.hexblog.com/?p=120 3 | # Default IDA Pro Paths: 4 | # MAC /Applications/IDA\ Pro\ X/idaq.app/Contents/MacOS/plugins/ 5 | # Windows C:\Program Files (x86)\IDA X\plugins 6 | 7 | # to make it autoexec on openfile 8 | # add this to plugins.cfg 9 | # ; Other plugins 10 | #FullColor FullColor.py 0 0 SILENT 11 | 12 | # thanks @JR0driguezB for help :) 13 | 14 | from __future__ import print_function 15 | from idautils import Heads 16 | from idc import get_segm_start, get_segm_end, print_insn_mnem, get_screen_ea, print_operand, set_color, CIC_ITEM 17 | import idaapi 18 | 19 | #idaapi.auto_wait() 20 | PLUGIN_TEST = 1 21 | 22 | class FullColor_t(idaapi.plugin_t): 23 | flags = idaapi.PLUGIN_UNL 24 | comment = "Set colors :)" 25 | help = "No help needed" 26 | wanted_name = "FullColor" 27 | wanted_hotkey = "" 28 | 29 | def init(self): 30 | #idaapi.msg("init() called!\n") 31 | #self.run(0) 32 | return idaapi.PLUGIN_OK 33 | 34 | def run(self, arg=0): 35 | print("hell2") 36 | idaapi.msg("run() called with %d!\n" % arg) 37 | heads = Heads(get_segm_start(get_screen_ea()), get_segm_end(get_screen_ea())) 38 | funcCalls = [] 39 | xor = [] 40 | antiVM = [] 41 | for i in heads: 42 | # Color the Calls off-white 43 | if print_insn_mnem(i) == "call": 44 | funcCalls.append(i) 45 | # Color Anti-VM instructions Red and print their location 46 | elif print_insn_mnem(i) in ("sidt", "sgdt", "sldt", "smsw", "str", "in", "cpuid"): 47 | antiVM.append(i) 48 | # Color non-zeroing out xor instructions Orange 49 | elif print_insn_mnem(i) == "xor" and (print_operand(i,0) != print_operand(i,1)): 50 | xor.append(i) 51 | 52 | print("Number of calls: %d" % (len(funcCalls))) 53 | for i in funcCalls: 54 | set_color(i, CIC_ITEM, 0xc7fdff) 55 | 56 | print("Number of potential Anti-VM instructions: %d" % (len(antiVM))) 57 | for i in antiVM: 58 | print("Anti-VM potential at %x" % i) 59 | set_color(i, CIC_ITEM, 0x0000ff) 60 | 61 | print("Number of xor: %d" % (len(xor))) 62 | for i in xor: 63 | set_color(i, CIC_ITEM, 0x00a5ff) 64 | 65 | def term(self): 66 | idaapi.msg("term() called!\n") 67 | 68 | def PLUGIN_ENTRY(): 69 | return FullColor_t() 70 | 71 | if PLUGIN_TEST: 72 | # Create form 73 | f = PLUGIN_ENTRY() 74 | f.init() 75 | f.run() 76 | f.term() 77 | -------------------------------------------------------------------------------- /Reversing/README.md: -------------------------------------------------------------------------------- 1 | Reversing related 2 | =================== 3 | * __FullColor.(idc|py)__ - Ida highlight Call/Non-zeroing XORs/AntiDebug/Interruptss/etc [script origin](https://practicalmalwareanalysis.com/colorida-idc-2/) 4 | -------------------------------------------------------------------------------- /Reversing/flare-emu-string-deobfuscation.py: -------------------------------------------------------------------------------- 1 | # https://unit42.paloaltonetworks.com/using-idapython-to-make-your-life-easier-part-1/ 2 | # https://www.hex-rays.com/products/ida/support/ida74_idapython_no_bc695_porting_guide.shtml 3 | # https://gitlab.com/krabsonsecurity/buer-loader-analysis/blob/master/stringdec.py 4 | # https://hex-rays.com/products/ida/support/idapython_docs/ 5 | 6 | from __future__ import print_function 7 | from datetime import datetime 8 | import flare_emu 9 | 10 | def find_function_arg_key(addr): 11 | while True: 12 | addr = idc.prev_head(addr) 13 | if idc.print_insn_mnem(addr) == "push": 14 | return idc.get_operand_value(addr, 0) 15 | return False 16 | 17 | def deobfuscate(argv): 18 | myEH = flare_emu.EmuHelper() 19 | allocated = myEH.allocEmuMem(1024) 20 | # we do must disable skip calls due to internal calls 21 | # stack for x86, first arg is ret addr so 0 22 | # registers for x64 23 | myEH.emulateRange(myEH.analysisHelper.getNameAddr("stringDecrypt"), skipCalls=False, stack = [0, argv[0], "useless", allocated]) 24 | return myEH.getEmuString(allocated).decode("latin-1") 25 | 26 | if __name__ == '__main__': 27 | now = datetime.now() 28 | eh = flare_emu.EmuHelper() 29 | # you must rename func inside of the IDA 30 | deobf_func_addr = eh.analysisHelper.getNameAddr("stringDecrypt") 31 | for x in XrefsTo(deobf_func_addr): 32 | indexed = find_function_arg_key(x.frm) 33 | s = deobfuscate([indexed]) 34 | print(f"{hex(x.frm)}: {s}") 35 | eh.analysisHelper.setComment(x.frm, s, False) 36 | print(f"It took {datetime.now()-now} seconds") 37 | -------------------------------------------------------------------------------- /Virtualization/README.md: -------------------------------------------------------------------------------- 1 | QEMU/KVM/libvirt related 2 | =================== 3 | * [KVM bible](https://doc.opensuse.org/documentation/leap/virtualization/html/book.virt/) 4 | * [Virtualization Best Practices](https://documentation.suse.com/sles/15-SP1/pdf/article-vt-best-practices_color_en.pdf) 5 | * [Comprehensive guide to performance optimizations for gaming on virtual machines with KVM/QEMU and PCI passthrough](https://mathiashueber.com/performance-tweaks-gaming-on-virtual-machines/) 6 | * __kvm-qemu.sh__ - ___PUBLIC VERSION___ - Deploy they all! 7 | -------------------------------------------------------------------------------- /Virtualization/kvm-qemu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (C) 2011-2023 DoomedRaven. 4 | # This file is part of Tools - https://github.com/doomedraven/Tools 5 | # See the file 'LICENSE.md' for copying permission. 6 | # https://www.doomedraven.com/2016/05/kvm.html 7 | # https://www.doomedraven.com/2020/04/how-to-create-virtual-machine-with-virt.html 8 | # Use Ubuntu 22.04 LTS 9 | # Update date: 22.02.2023 10 | 11 | # Glory to Ukraine! 12 | 13 | : ' 14 | Huge thanks to: 15 | * @SamRSA8 16 | * @http_error_418 17 | * @2sec4you 18 | * @seifreed 19 | * @Fire9 20 | * @abuse_ch 21 | * @wmetcalf 22 | * @ClaudioWayne 23 | * @CplNathan 24 | ' 25 | 26 | # ToDo investigate 27 | #https://www.jamescoyle.net/how-to/1810-qcow2-disk-images-and-performance 28 | #when backing storage is attached to virtio_blk (vda, vdb, etc.) storage controller - performance from iSCSI client connecting to the iSCSI target was in my environment ~ 20 IOPS, with throughput (depending on IO size) ~ 2-3 MiB/s. I changed virtual disk controller within virtual machine to SCSI and I'm able to get 1000+ IOPS and throughput 100+ MiB/s from my iSCSI clients. 29 | 30 | #https://linux.die.net/man/1/qemu-img 31 | #"cluster_size" 32 | #Changes the qcow2 cluster size (must be between 512 and 2M). Smaller cluster sizes can improve the image file size whereas larger cluster sizes generally provide better performance. 33 | 34 | # https://github.com/dylanaraps/pure-bash-bible 35 | # https://www.shellcheck.net/ 36 | 37 | # ACPI tables related 38 | # https://wiki.archlinux.org/index.php/DSDT 39 | # Dump on linux 40 | # acpidump > acpidump.out 41 | # Dump on Windows 42 | # https://acpica.org/downloads/binary-tools 43 | # acpixtract -a acpi/4/acpi.dump 44 | 45 | # acpixtract -a acpidump.out 46 | # iasl -d DSDT.dat 47 | # Decompile: iasl -d dsdt.dat 48 | # Recompile: iasl -tc dsdt.dsl 49 | 50 | # if you want all arches support in QEMU, just set QTARGETS to empty 51 | QTARGETS="--target-list=i386-softmmu,x86_64-softmmu,i386-linux-user,x86_64-linux-user" 52 | 53 | 54 | #https://www.qemu.org/download/#source or https://download.qemu.org/ 55 | qemu_version=8.0.0 56 | # libvirt - https://libvirt.org/sources/ 57 | # changelog - https://libvirt.org/news.html 58 | libvirt_version=9.2.0 59 | # virt-manager - https://github.com/virt-manager/virt-manager/releases 60 | # autofilled 61 | OS="" 62 | username=$SUDO_USER 63 | MAINTAINER="" 64 | # Skip last octet it will be auto populated 65 | VM_NETWORK_RANGE="192.168.1" 66 | DNS_PRIMARY="8.8.8.8" 67 | DNS_SECONDARY="8.8.4.4" 68 | 69 | systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target 70 | 71 | #replace all occurances of CPU's in qemu with our fake one 72 | cpuid="Intel(R) Core(TM) i3-4130 CPU" 73 | #cpuid="AMD FX(tm)-4300 Quad-Core Processor" 74 | 75 | #KVMKVMKVM\\0\\0\\0 replacement 76 | hypervisor_string_replacemnt="GenuineIntel" 77 | #hypervisor_string_replacemnt="AuthenticAMD" 78 | 79 | #QEMU HARDDISK 80 | #qemu_hd_replacement="SanDisk SDSSD" 81 | qemu_hd_replacement="SAMSUNG MZ76E120" 82 | #QEMU DVD-ROM 83 | #qemu_dvd_replacement="HL-DT-ST WH1" 84 | #qemu_dvd_replacement="HL-PV-SG WB4" 85 | qemu_dvd_replacement="HL-PQ-SV WB8" 86 | 87 | #BOCHSCPU 88 | bochs_cpu_replacement="INTELCPU" 89 | #bochs_cpu_replacement="AMDCPU" 90 | 91 | #QEMU\/Bochs 92 | qemu_bochs_cpu='INTEL\/INTEL' 93 | #qemu_bochs_cpu='AMD\/AMD' 94 | 95 | #qemu 96 | qemu_space_replacement="intel " 97 | #qemu_space_replacement="amd " 98 | 99 | #06\/23\/99 100 | src_misc_bios_table="07\/02\/18" 101 | 102 | #04\/01\/2014 103 | src_bios_table_date2="11\/03\/2018" 104 | 105 | #01\/01\/2011 106 | src_fw_smbios_date="11\/03\/2018" 107 | 108 | # ToDO add to see if cpu supports VTx 109 | # egrep '(vmx|svm)' --color=always /proc/cpuinfo 110 | #* If your CPU is Intel, you need activate in __BIOS__ VT-x 111 | # * (last letter can change, you can activate [TxT ](https://software.intel.com/en-us/blogs/2012/09/25/how-to-enable-an-intel-trusted-execution-technology-capable-server) too, and any other feature, but VT-* is very important) 112 | 113 | # ToDo check if aptitude is installed if no refresh and install 114 | sudo apt update 2>/dev/null 115 | sudo apt install aptitude -y 2>/dev/null 116 | 117 | NC='\033[0m' 118 | RED='\033[0;31m' 119 | echo -e "${RED}[!] ONLY for UBUNTU 20.04 and 22.04${NC}" 120 | echo -e "${RED}\t[!] NEVER install packages from APT that installed by this script${NC}" 121 | echo -e "${RED}\t[!] NEVER use 'make install' - it poison system and no easy way to upgrade/uninstall/cleanup, use dpkg-deb${NC}" 122 | echo -e "${RED}\t[!] NEVER run 'python setup.py install' DO USE 'pip intall .' the same as APT poisoning/upgrading${NC}\n" 123 | echo -e "${RED}\t[!] NEVER FORCE system upgrade, it will ignore blacklist and mess with packages installed by APT and this scritp!${NC}\n" 124 | 125 | function usage() { 126 | cat << EndOfHelp 127 | Usage: $0 | tee $0.log 128 | Commands - are case insensitive: 129 | All - - Execs QEMU/SeaBios/KVM, username is optional 130 | QEMU - Install QEMU from source, 131 | DEFAULT support are x86 and x64, set ENV var QEMU_TARGERS=all to install for all arches 132 | SeaBios - Install SeaBios and repalce QEMU bios file 133 | Libvirt - install libvirt, username is optional 134 | Apparmor - Install apparmor parsers 135 | KVM - <3 136 | GRUB - add IOMMU to grub command line 137 | tcp_bbr - Enable TCP BBR congestion control 138 | * https://www.cyberciti.biz/cloud-computing/increase-your-linux-server-internet-speed-with-tcp-bbr-congestion-control/ 139 | Mosh - mobile shell - https://mosh.org/ 140 | Clone - <#vm_to_create> 141 | * Example Win7x64 /VMs/Win7x64.qcow2 0 5 /var/lib/libvirt/images/ 192.168.1 linked 142 | https://wiki.qemu.org/Documentation/CreateSnapshot 143 | Libvmi - install LibVMI 144 | Virtmanager - install virt-manager 145 | Libguestfs - install libguestfs 146 | Replace_qemu - only fix antivms in QEMU source 147 | Replace_seabios - only fix antivms in SeaBios source 148 | Issues - will give you error - solution list 149 | noip - Install No-ip deamon and enable on boot 150 | SysRQ - enable SysRQ - https://sites.google.com/site/syscookbook/rhel/rhel-sysrq-key 151 | 152 | Tips: 153 | * Latest kernels having some KVM features :) 154 | * apt search linux-image 155 | * QCOW2 allocations types performance 156 | * https://www.jamescoyle.net/how-to/1810-qcow2-disk-images-and-performance 157 | * https://www.jamescoyle.net/how-to/2060-qcow2-physical-size-with-different-preallocation-settings 158 | EndOfHelp 159 | } 160 | 161 | function grub_iommu(){ 162 | # ToDo make a sed with regex which works on all cases 163 | echo "[+] Updating GRUB for IOMMU support" 164 | if ! sed -i 's/GRUB_CMDLINE_LINUX=""/GRUB_CMDLINE_LINUX="intel_iommu=on"/g' /etc/default/grub; then 165 | echo "[-] GRUB patching failed, add intel_iommu=on manually" 166 | return 1 167 | fi 168 | sudo update-grub 169 | echo "[+] Please reboot" 170 | } 171 | 172 | function _sed_aux(){ 173 | # pattern path error_msg 174 | if [ -f "$2" ] && ! sed -i "$1" "$2"; then 175 | echo "$3" 176 | fi 177 | } 178 | 179 | function _enable_tcp_bbr() { 180 | # https://www.cyberciti.biz/cloud-computing/increase-your-linux-server-internet-speed-with-tcp-bbr-congestion-control/ 181 | # grep 'CONFIG_TCP_CONG_BBR' /boot/config-$(uname -r) 182 | # grep 'CONFIG_NET_SCH_FQ' /boot/config-$(uname -r) 183 | # egrep 'CONFIG_TCP_CONG_BBR|CONFIG_NET_SCH_FQ' /boot/config-$(uname -r) 184 | if ! grep -q -E '^net.core.default_qdisc=fq' /etc/sysctl.conf; then 185 | echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf 186 | echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf 187 | fi 188 | 189 | modprobe br_netfilter 190 | echo "br_netfilter" >> /etc/modules 191 | { 192 | echo "net.bridge.bridge-nf-call-arptables = 1"; 193 | echo "net.bridge.bridge-nf-call-ip6tables = 1"; 194 | echo "net.bridge.bridge-nf-call-iptables = 1"; 195 | echo "net.core.rmem_max = 16777216"; 196 | echo "net.core.wmem_max = 16777216"; 197 | echo "net.ipv4.tcp_rmem = 4096 87380 16777216"; 198 | echo "net.ipv4.tcp_wmem = 4096 65536 16777216"; 199 | echo "net.ipv4.tcp_syncookies = 0" ; 200 | echo "net.ipv4.tcp_mem = 50576 64768 98152" ; 201 | echo "net.core.netdev_max_backlog = 2500" ; 202 | echo "vm.swappiness = 1" ; 203 | echo "vm.dirty_ratio = 15"; 204 | } >> /etc/sysctl.conf 205 | sudo sysctl -p 206 | 207 | sudo sysctl --system 208 | } 209 | 210 | function install_apparmor() { 211 | aptitude install -f bison linux-generic-hwe-22.04 -y 212 | aptitude install -f apparmor apparmor-profiles apparmor-profiles-extra apparmor-utils libapparmor-dev libapparmor1 python3-apparmor python3-libapparmor libapparmor-perl -y 213 | } 214 | 215 | 216 | function install_libguestfs() { 217 | # https://libguestfs.org/guestfs-building.1.html 218 | cd /opt || return 219 | echo "[+] Check for previous version of LibGuestFS" 220 | sudo dpkg --purge --force-all "libguestfs-*" 2>/dev/null 221 | 222 | wget -O- https://packages.erlang-solutions.com/ubuntu/erlang_solutions.asc | sudo apt-key add - 223 | sudo add-apt-repository -y "deb https://packages.erlang-solutions.com/ubuntu $(lsb_release -sc) contrib" 224 | sudo aptitude install -f parted libyara3 erlang-dev gperf flex bison libaugeas-dev libhivex-dev supermin ocaml-nox libhivex-ocaml genisoimage libhivex-ocaml-dev libmagic-dev libjansson-dev gnulib jq ocaml-findlib -y 2>/dev/null 225 | sudo apt update 226 | sudo aptitude install -f erlang -y 227 | 228 | if [ ! -d libguestfs ]; then 229 | #ToDo move to latest release not latest code 230 | #_info=$(curl -s https://api.github.com/repos/libguestfs/libguestfs/releases/latest) 231 | #_version=$(echo $_info |jq .tag_name|sed "s/\"//g") 232 | #_repo_url=$(echo $_info | jq ".zipball_url" | sed "s/\"//g") 233 | #wget -q $_repo_url 234 | #unzip $_version 235 | git clone --recursive https://github.com/libguestfs/libguestfs 236 | fi 237 | cd libguestfs || return 238 | git submodule update --init 239 | autoreconf -i 240 | ./configure CFLAGS=-fPIC 241 | make -j"$(nproc)" 242 | 243 | # Install virt tools that are in a diff repo since LIBGUESTFS 1.46 split 244 | # More Info: https://listman.redhat.com/archives/libguestfs/2021-September/msg00153.html 245 | cd /opt || return 246 | if [ ! -d guestfs-tools ]; then 247 | git clone --recursive https://github.com/rwmjones/guestfs-tools.git 248 | fi 249 | cd guestfs-tools || return 250 | # Following tips to compile the guestfs-tools as depicted in https://www.mail-archive.com/libguestfs@redhat.com/msg22408.html 251 | git submodule update --init --force 252 | autoreconf -i 253 | ../libguestfs/run ./configure CFLAGS=-fPIC 254 | ../libguestfs/run make -j $(getconf _NPROCESSORS_ONLN) 255 | 256 | echo "[+] /opt/libguestfs/run --help" 257 | echo "[+] /opt/libguestfs/run /opt/guestfs-tools/sparsify/virt-sparsify -h" 258 | } 259 | 260 | 261 | function install_libvmi() { 262 | # IMPORTANT: 263 | # 1) LibVMI will have KVM support if libvirt is available during compile time. 264 | # 265 | # 2 )Enable GDB access to your KVM VM. This is done by adding '-s' to the VM creation line or 266 | # by modifying the VM XML definition used by libvirt as follows: 267 | # Change: 268 | # 269 | # to: 270 | # 271 | # 272 | # Add: 273 | # 274 | # 275 | # 276 | # under the level of the XML. 277 | 278 | # The -s switch is a shorthand for -gdb tcp::1234 279 | 280 | # LibVMI 281 | cd /tmp || return 282 | 283 | if [ ! -d "libvmi" ]; then 284 | # git clone https://github.com/libvmi/libvmi.git 285 | wget -q https://github.com/libvmi/libvmi/archive/refs/tags/v0.14.0.zip -O libvmi-v0.14.0.zip 286 | unzip libvmi-v0.14.0.zip 287 | echo "[+] Cloned LibVMI repo" 288 | fi 289 | mkdir -p /tmp/libvmi_builded/DEBIAN 290 | echo -e "Package: libvmi\nVersion: 1.0-0\nArchitecture: $ARCH\nMaintainer: $MAINTAINER\nDescription: libvmi" > /tmp/libvmi_builded/DEBIAN/control 291 | cd "libvmi-v0.14.0" || return 292 | 293 | # install deps 294 | aptitude install -f -y cmake flex bison libglib2.0-dev libjson-c-dev libyajl-dev doxygen 295 | # other deps 296 | aptitude install -f -y pkg-config 297 | mkdir build 298 | cd build || return 299 | cmake -DENABLE_XEN=OFF -DENABLE_KVM=ON -DENABLE_XENSTORE=OFF -DENABLE_BAREFLANK=OFF .. 300 | 301 | make -j"$(nproc)" install DESTDIR=/tmp/libvmi_builded 302 | dpkg-deb --build --root-owner-group /tmp/libvmi_builded 303 | apt -y -o Dpkg::Options::="--force-overwrite" install /tmp/libvmi_builded.deb 304 | 305 | /sbin/ldconfig 306 | 307 | # LibVMI Python 308 | cd /tmp || return 309 | 310 | if [ ! -d "python" ]; then 311 | # actual 312 | # https://github.com/libvmi/python/tree/76d9ea85eefa0d77f6ad4d6089e757e844763917 313 | # git checkout add_vmi_request_page_fault 314 | # git pull 315 | #git clone https://github.com/libvmi/python.git libvmi-python 316 | pip3 install libvmi 317 | echo "[+] Cloned LibVMI Python repo" 318 | fi 319 | cd "libvmi-python" || return 320 | 321 | # install deps 322 | aptitude install -f -y python3-pkgconfig python3-cffi python3-future 323 | #pip3 install . 324 | python3 setup.py build 325 | pip3 install . 326 | 327 | # Rekall 328 | cd /tmp || return 329 | 330 | if [ ! -d "rekall" ]; then 331 | git clone https://github.com/google/rekall.git 332 | echo "[+] Cloned Rekall repo" 333 | fi 334 | 335 | virtualenv /tmp/MyEnv 336 | source /tmp/MyEnv/bin/activate 337 | pip3 install --upgrade testresources setuptools pip wheel 338 | pip3 install capstone 339 | pip3 install --editable rekall/rekall-lib 340 | # ERROR: rekall-efilter 1.6.0 has requirement future==0.16.0 341 | pip3 install future==0.16.0 342 | # TypeError: Set() missing 1 required positional argument: 'value' 343 | pip3 install pyaff4==0.26.post6 344 | pip3 install --editable rekall/rekall-core 345 | pip3 install --editable rekall/rekall-agent 346 | pip3 install --editable rekall 347 | pip3 install --upgrade pyasn1 348 | deactivate 349 | } 350 | 351 | # In progress... 352 | # 353 | # Errors: "The selected hypervisor has no events support!" - only Xen supported unfortunately 354 | # 355 | function install_pyvmidbg() { 356 | # deps 357 | aptitude install -f python3-docopt python3-lxml cabextract 358 | 359 | # libvmi config entry 360 | # /etc/libvmi.conf: 361 | # win10 { 362 | # ostype = "Windows"; 363 | # rekall_profile = "/etc/libvmi/rekall-profile.json"; 364 | # } 365 | 366 | # Make Windows 10 profile 367 | # Copy from Guest OS file "C:\Windows\System32\ntoskrnl.exe" 368 | # rekall peinfo -f 369 | # 370 | # Once the PDB filename and GUID is known, creating the Rekall profile is done in two steps: 371 | # rekall fetch_pdb 372 | # rekall parse_pdb > rekall-profile.json 373 | # 374 | # In case of Windows 10: 375 | # rekall fetch_pdb ntkrnlmp 376 | # May cause error like "ERROR:rekall.1:Unrecognized type T_64PUINT4" (not dangerous) 377 | # rekall parse_pdb ntkrnlmp > rekall-profile.json 378 | 379 | # install rekall profile 380 | # /etc/libvmi/rekall-profile.json 381 | 382 | # git clone https://github.com/Wenzel/pyvmidbg.git 383 | # virtualenv -p python3 venv 384 | # source venv/bin/activate 385 | # python3 setup.py build 386 | # pip3 install . 387 | 388 | # sudo python3 -m vmidbg 5000 --address 0.0.0.0 cmd -d 389 | 390 | # git clone https://github.com/radare/radare2.git 391 | # sys/install.sh 392 | # r2 -d gdb://127.0.0.1:5000 -b 64 393 | } 394 | 395 | function install_libvirt() { 396 | # http://ask.xmodulo.com/compile-virt-manager-debian-ubuntu.html 397 | #rm -r /usr/local/lib/python2.7/dist-packages/libvirt* 398 | 399 | if [ ! -f /etc/apt/preferences.d/doomedraven ]; then 400 | # set to hold to avoid side problems 401 | cat >> /etc/apt/preferences.d/doomedraven << EOH 402 | Package: libvirt-bin 403 | Pin: release * 404 | Pin-Priority: -1 405 | Package: libvirt0 406 | Pin: release * 407 | Pin-Priority: -1 408 | Package: qemu 409 | Pin: release * 410 | Pin-Priority: -1 411 | Package: qemu 412 | Pin: release * 413 | Pin-Priority: -1 414 | Package: gir1.2-libvirt-glib-1.0 415 | Pin: release * 416 | Pin-Priority: -1 417 | Package: libvirt-glib-1.0-0 418 | Pin: release * 419 | Pin-Priority: -1 420 | Package: libvirt-glib-1.0-data 421 | Pin: release * 422 | Pin-Priority: -1 423 | EOH 424 | fi 425 | 426 | # preferences.d doesnt work for me with qemu 7.0.0 and Ubuntu 22.04, to be sure, handle via dpkg 427 | echo "qemu hold" | sudo dpkg --set-selections 2>/dev/null 428 | echo "[+] Checking/deleting old versions of Libvirt" 429 | apt purge libvirt0 libvirt-bin libvirt-$libvirt_version 2>/dev/null 430 | dpkg -l|grep "libvirt-[0-9]\{1,2\}\.[0-9]\{1,2\}\.[0-9]\{1,2\}"|cut -d " " -f 3|sudo xargs dpkg --purge --force-all 2>/dev/null 431 | sudo apt install mlocate libxml2-utils gnutls-bin gnutls-dev libxml2-dev bash-completion libreadline-dev numactl libnuma-dev python3-docutils flex -y 432 | # Remove old links 433 | updatedb 434 | temp_libvirt_so_path=$(locate libvirt-qemu.so | head -n1 | awk '{print $1;}') 435 | libvirt_so_path="${temp_libvirt_so_path%/*}/" 436 | 437 | if [[ -n "$libvirt_so_path" ]]; then 438 | for so_path in $(ls "${libvirt_so_path}"libvirt*.so.0); do 439 | dest_path=/lib/$(uname -m)-linux-gnu/$(basename "$so_path") 440 | if [ -f "$dest_path" ]; then 441 | rm "$dest_path" 442 | fi 443 | done 444 | fi 445 | 446 | cd /tmp || return 447 | if [ -f libvirt-$libvirt_version.tar.xz ]; then 448 | rm -r libvirt-$libvirt_version 449 | else 450 | wget -q https://libvirt.org/sources/libvirt-$libvirt_version.tar.xz 451 | wget -q https://libvirt.org/sources/libvirt-$libvirt_version.tar.xz.asc 452 | gpg --verify "libvirt-$libvirt_version.tar.xz.asc" 453 | fi 454 | tar xf libvirt-$libvirt_version.tar.xz 455 | cd libvirt-$libvirt_version || return 456 | if [ "$OS" = "Linux" ]; then 457 | aptitude install -f mlocate iptables python3-dev unzip numad libglib2.0-dev libsdl1.2-dev lvm2 python3-pip ebtables libosinfo-1.0-dev libnl-3-dev libnl-route-3-dev libyajl-dev xsltproc libdevmapper-dev libpciaccess-dev dnsmasq dmidecode librbd-dev libtirpc-dev -y 2>/dev/null 458 | 459 | # see https://github.com/doomedraven/Tools/issues/100 460 | install_apparmor 461 | 462 | pip3 install ipaddr ninja meson flake8 -U 463 | # --prefix=/usr --localstatedir=/var --sysconfdir=/etc 464 | #git init 465 | #git remote add doomedraven https://github.com/libvirt/libvirt 466 | # To see whole config sudo meson configure 467 | # true now is enabled 468 | cd /tmp/libvirt-$libvirt_version || return 469 | sudo meson build -D system=true -D driver_remote=enabled -D driver_qemu=enabled -D driver_libvirtd=enabled -D qemu_group=libvirt -D qemu_user=root -D secdriver_apparmor=enabled -D apparmor_profiles=enabled -D bash_completion=auto 470 | 471 | sudo ninja -C build 472 | sudo ninja -C build install 473 | if [ $? -ne 0 ]; then 474 | echo "${RED}Failed. Read the instalation log for details${NC}" 475 | exit 1 476 | fi 477 | 478 | cd .. 479 | 480 | updatedb 481 | # ToDo fix bad destiny on some systems, example, first arg should be destiny to link not source 482 | # /usr/lib/x86_64-linux-gnu/libvirt-qemu.so.0 -> /usr/lib64/libvirt-qemu.so 483 | temp_libvirt_so_path=$(locate libvirt-qemu.so | head -n1 | awk '{print $1;}') 484 | temp_export_path=$(locate libvirt.pc | head -n1 | awk '{print $1;}') 485 | libvirt_so_path="${temp_libvirt_so_path%/*}/" 486 | if [[ $libvirt_so_path == "/usr/lib/x86_64-linux-gnu/" ]]; then 487 | temp_libvirt_so_path=$(locate libvirt-qemu.so | tail -1 | awk '{print $1;}') 488 | libvirt_so_path="${temp_libvirt_so_path%/*}/" 489 | fi 490 | export_path="${temp_export_path%/*}/" 491 | export PKG_CONFIG_PATH=$export_path 492 | 493 | if [[ -n "$libvirt_so_path" ]]; then 494 | # #ln -s /usr/lib64/libvirt-qemu.so /lib/x86_64-linux-gnu/libvirt-qemu.so.0 495 | for so_path in $(ls "${libvirt_so_path}"libvirt*.so.0); do ln -sf "$so_path" /lib/$(uname -m)-linux-gnu/$(basename "$so_path"); done 496 | ldconfig 497 | else 498 | echo "${RED}[!] Problem to create symlink, unknown libvirt_so_path path${NC}" 499 | exit 1 500 | fi 501 | fi 502 | 503 | # https://wiki.archlinux.org/index.php/Libvirt#Using_polkit 504 | if [ -f /etc/libvirt/libvirtd.conf ]; then 505 | path="/etc/libvirt/libvirtd.conf" 506 | elif [ -f /usr/local/etc/libvirt/libvirtd.conf ]; then 507 | path="/usr/local/etc/libvirt/libvirtd.conf" 508 | fi 509 | 510 | sed -i 's/#unix_sock_group/unix_sock_group/g' /etc/libvirt/*.conf 511 | sed -i 's/#unix_sock_ro_perms = "0777"/unix_sock_ro_perms = "0770"/g' /etc/libvirt/*.conf 512 | sed -i 's/#unix_sock_rw_perms = "0770"/unix_sock_rw_perms = "0770"/g' /etc/libvirt/*.conf 513 | sed -i 's/#auth_unix_ro = "none"/auth_unix_ro = "none"/g' /etc/libvirt/*.conf 514 | sed -i 's/#auth_unix_rw = "none"/auth_unix_rw = "none"/g' /etc/libvirt/*.conf 515 | sed -i 's/#auth_unix_ro = "polkit"/auth_unix_ro = "none"/g' /etc/libvirt/*.conf 516 | sed -i 's/#auth_unix_rw = "polkit"/auth_unix_rw = "none"/g' /etc/libvirt/*.conf 517 | 518 | #echo "[+] Setting AppArmor for libvirt/kvm/qemu" 519 | sed -i 's/#security_driver = "selinux"/security_driver = "apparmor"/g' /etc/libvirt/qemu.conf 520 | # https://gitlab.com/apparmor/apparmor/wikis/Libvirt 521 | FILES=( 522 | /etc/apparmor.d/usr.sbin.libvirtd 523 | /usr/sbin/libvirtd 524 | /usr/libexec/virt-aa-helper 525 | ) 526 | for file in "${FILES[@]}"; do 527 | if [ -f "$file" ]; then 528 | sudo aa-complain "$file" 529 | fi 530 | done 531 | 532 | cd /tmp || return 533 | 534 | if [ ! -f v$libvirt_version.zip ]; then 535 | wget -q https://github.com/libvirt/libvirt-python/archive/v$libvirt_version.zip 536 | fi 537 | if [ -d "libvirt-python-$libvirt_version" ]; then 538 | rm -r "libvirt-python-$libvirt_version" 539 | fi 540 | unzip v$libvirt_version.zip 541 | cd "libvirt-python-$libvirt_version" || return 542 | python3 setup.py build 543 | pip3 install . 544 | if [ "$OS" = "Linux" ]; then 545 | # https://github.com/libvirt/libvirt/commit/e94979e901517af9fdde358d7b7c92cc055dd50c 546 | groupname="" 547 | if grep -q -E '^libvirtd:' /etc/group; then 548 | groupname="libvirtd" 549 | elif grep -q -E '^libvirt:' /etc/group; then 550 | groupname="libvirt" 551 | else 552 | # create group if missed 553 | groupname="libvirt" 554 | groupadd libvirt 555 | fi 556 | usermod -G $groupname -a "$(whoami)" 557 | if [[ -n "$username" ]]; then 558 | usermod -G $groupname -a "$username" 559 | fi 560 | 561 | #check links 562 | # sudo ln -s /usr/lib64/libvirt-qemu.so /lib/x86_64-linux-gnu/libvirt-qemu.so.0 563 | # sudo ln -s /usr/lib64/libvirt.so.0 /lib/x86_64-linux-gnu/libvirt.so.0 564 | systemctl enable virtqemud.service virtnetworkd.service virtstoraged.service virtqemud.socket 565 | echo "[+] You should logout and login " 566 | fi 567 | 568 | } 569 | 570 | function install_virt_manager() { 571 | # pm-utils 572 | # from build-dep 573 | aptitude install -f libgirepository1.0-dev gtk-doc-tools python3 python3-pip gir1.2-govirt-1.0 libgovirt-dev \ 574 | libgovirt-common libgovirt2 gir1.2-rest-0.7 unzip intltool augeas-doc ifupdown wodim cdrkit-doc indicator-application \ 575 | augeas-tools radvd auditd systemtap nfs-common zfsutils python-openssl-doc samba \ 576 | debootstrap sharutils-doc ssh-askpass gnome-keyring\ 577 | sharutils spice-client-glib-usb-acl-helper ubuntu-mono x11-common python3-gi \ 578 | python3-gi-cairo python3-pkg-resources \ 579 | python3-libxml2 libxml2-utils libxrandr2 libxrender1 libxshmfence1 libxtst6 libxv1 libyajl2 msr-tools osinfo-db \ 580 | python3-cairo python3-cffi-backend libxcb-present0 libxcb-render0 libxcb-shm0 libxcb-sync1 \ 581 | libxcb-xfixes0 libxcomposite1 libxcursor1 libxdamage1 libxfixes3 libxft2 libxi6 libxinerama1 \ 582 | libxkbcommon0 libusbredirhost1 libusbredirparser1 libv4l-0 libv4lconvert0 libvisual-0.4-0 libvorbis0a libvorbisenc2 \ 583 | libvte-2.91-0 libvte-2.91-common libwavpack1 libwayland-client0 libwayland-cursor0 libwayland-egl1-mesa libwayland-server0 \ 584 | libx11-xcb1 libxcb-dri2-0 libxcb-dri3-0 libsoup-gnome2.4-1 libsoup2.4-1 libspeex1 libspice-client-glib-2.0-8 \ 585 | libspice-client-gtk-3.0-5 libspice-server1 libtag1v5 libtag1v5-vanilla libthai-data libthai0 libtheora0 libtiff5 \ 586 | libtwolame0 libpython3-dev librados2 libraw1394-11 librbd1 librdmacm1 librest-0.7-0 \ 587 | librsvg2-2 librsvg2-common libsamplerate0 libsdl1.2debian libshout3 libsndfile1 libpango-1.0-0 libpangocairo-1.0-0 \ 588 | libpangoft2-1.0-0 libpangoxft-1.0-0 libpciaccess0 libphodav-2.0-0 libphodav-2.0-common libpixman-1-0 libproxy1v5 \ 589 | libpulse-mainloop-glib0 libpulse0 libgstreamer1.0-0 libgtk-3-0 libgtk-3-bin libgtk-3-common libgtk-vnc-2.0-0 \ 590 | libgudev-1.0-0 libgvnc-1.0-0 libharfbuzz0b libibverbs1 libiec61883-0 libindicator3-7 libiscsi7 libjack-jackd2-0 libjbig0 \ 591 | libjpeg-turbo8 libjpeg8 libjson-glib-1.0-0 libjson-glib-1.0-common liblcms2-2 libmp3lame0 libmpg123-0 libnl-route-3-200 \ 592 | libnspr4 libnss3 libogg0 libopus0 liborc-0.4-0 libosinfo-1.0-0 libcairo-gobject2 libcairo2 libcdparanoia0 libcolord2 \ 593 | libcups2 libdatrie1 libdbusmenu-glib4 libdbusmenu-gtk3-4 libdconf1 libdv4 libegl-mesa0 libegl1 libepoxy0 libfdt1 libflac8 \ 594 | libfontconfig1 libgbm1 libgdk-pixbuf2.0-0 libgdk-pixbuf2.0-bin libgdk-pixbuf2.0-common libglapi-mesa libglvnd0 libgraphite2-3 \ 595 | libgstreamer-plugins-base1.0-0 libgstreamer-plugins-good1.0-0 gtk-update-icon-cache hicolor-icon-theme humanity-icon-theme \ 596 | ibverbs-providers libaa1 libaio1 libappindicator3-1 libasound2 libasound2-data libasyncns0 libatk-bridge2.0-0 libatk1.0-0 \ 597 | libatk1.0-data libatspi2.0-0 libaugeas0 libavahi-client3 libavahi-common-data libavahi-common3 libavc1394-0 libbluetooth3 \ 598 | libcaca0 libcacard0 gir1.2-atk-1.0 gir1.2-freedesktop gir1.2-gdkpixbuf-2.0 gir1.2-gtk-3.0 gir1.2-gtk-vnc-2.0 \ 599 | gir1.2-libosinfo-1.0 gir1.2-pango-1.0 gir1.2-spiceclientglib-2.0 gir1.2-spiceclientgtk-3.0 gir1.2-vte-2.91 glib-networking \ 600 | glib-networking-common glib-networking-services gsettings-desktop-schemas gstreamer1.0-plugins-base gstreamer1.0-plugins-good \ 601 | gstreamer1.0-x adwaita-icon-theme at-spi2-core augeas-lenses cpu-checker dconf-gsettings-backend dconf-service \ 602 | fontconfig fontconfig-config fonts-dejavu-core genisoimage gir1.2-appindicator3-0.1 gir1.2-secret-1 \ 603 | gobject-introspection intltool pkg-config libxml2-dev libxslt-dev python3-dev gir1.2-gtk-vnc-2.0 gir1.2-spiceclientgtk-3.0 libgtk-3-dev \ 604 | mlocate gir1.2-gtksource-4 libgtksourceview-4-0 libgtksourceview-4-common checkinstall -y 605 | # should be installed first 606 | # moved out as some 20.04 doesn't have this libs %) 607 | aptitude install -f -y python3-ntlm-auth libpython3-stdlib libbrlapi-dev libgirepository1.0-dev python3-testresources 608 | apt-get -y -o Dpkg::Options::="--force-overwrite" install ovmf 609 | pip3 install tqdm requests six urllib3 ipaddr ipaddress idna dbus-python certifi lxml cryptography pyOpenSSL chardet asn1crypto pycairo PySocks PyGObject 610 | 611 | # not available in 22.04 612 | if [ $(lsb_release -sc) != "jammy" ]; then 613 | aptitude -f install python-enum34 libxenstore3.0 libnetcf1 libcroco3 -y 614 | fi 615 | 616 | updatedb 617 | 618 | temp_libvirt_so_path=$(locate libvirt-qemu.so | head -n1 | awk '{print $1;}') 619 | temp_export_path=$(locate libvirt.pc | head -n1 | awk '{print $1;}') 620 | libvirt_so_path="${temp_libvirt_so_path%/*}/" 621 | export_path="${temp_export_path%/*}/" 622 | 623 | export PKG_CONFIG_PATH=$export_path 624 | 625 | cd /tmp || return 626 | if [ ! -f libvirt-glib-3.0.0.tar.gz ]; then 627 | wget -q https://libvirt.org/sources/glib/libvirt-glib-3.0.0.tar.gz 628 | wget -q https://libvirt.org/sources/glib/libvirt-glib-3.0.0.tar.gz.asc 629 | gpg --verify "libvirt-glib-3.0.0.tar.gz.asc" 630 | 631 | fi 632 | tar xf libvirt-glib-3.0.0.tar.gz 633 | cd libvirt-glib-3.0.0 || return 634 | aclocal && libtoolize --force 635 | automake --add-missing 636 | ./configure 637 | # mkdir -p /tmp/libvirt-glib_builded/DEBIAN 638 | # echo -e "Package: libvirt-glib-1.0-0\nVersion: 1.0-0\nArchitecture: $ARCH\nMaintainer: $MAINTAINER\nDescription: libvirt-glib-1.0-0" > /tmp/libvirt-glib_builded/DEBIAN/control 639 | # make -j"$(nproc)" install DESTDIR=/tmp/libvirt-glib_builded 640 | # dpkg-deb --build --root-owner-group /tmp/libvirt-glib_builded 641 | # apt -y -o Dpkg::Options::="--force-overwrite" install /tmp/libvirt-glib_builded.deb 642 | 643 | make -j"$(nproc)" 644 | # ToDo add blacklist 645 | checkinstall --pkgname=libvirt-glib-1.0-0 --default 646 | # v4 is meson based 647 | # sudo meson build -D system=true 648 | cd /tmp || return 649 | if [ ! -f gir1.2-libvirt-glib-1.0_1.0.0-1_amd64.deb ]; then 650 | wget -q http://launchpadlibrarian.net/297448356/gir1.2-libvirt-glib-1.0_1.0.0-1_amd64.deb 651 | fi 652 | dpkg --force-confold -i gir1.2-libvirt-glib-1.0_1.0.0-1_amd64.deb 653 | 654 | /sbin/ldconfig 655 | 656 | if [ ! -d "virt-manager" ]; then 657 | git clone https://github.com/virt-manager/virt-manager.git 658 | echo "[+] Cloned Virt Manager repo" 659 | fi 660 | cd "virt-manager" || return 661 | # py3 662 | #pip3 install . 663 | python3 setup.py build 664 | python3 setup.py install 665 | if [ "$SHELL" = "/bin/zsh" ] || [ "$SHELL" = "/usr/bin/zsh" ] ; then 666 | echo "export LIBVIRT_DEFAULT_URI=qemu:///system" >> "$HOME/.zsh" 667 | else 668 | echo "export LIBVIRT_DEFAULT_URI=qemu:///system" >> "$HOME/.bashrc" 669 | fi 670 | sudo glib-compile-schemas --strict /usr/share/glib-2.0/schemas/ 671 | systemctl enable virtstoraged.service 672 | systemctl start virtstoraged.service 673 | 674 | # i440FX-Issue Win7: Unable to complete install: 'XML error: The PCI controller with index='0' must be model='pci-root' for this machine type, but model='pcie-root' was found instead' 675 | # Workaround: Edit Overiew in XML view and delete all controller entries with type="pci" 676 | # Example: 677 | # 678 | # 679 | } 680 | 681 | function install_kvm_linux() { 682 | sed -i 's/# deb-src/deb-src/g' /etc/apt/sources.list 683 | apt update 2>/dev/null 684 | aptitude install -f build-essential locate python3-pip gcc pkg-config cpu-checker intltool libtirpc-dev -y 2>/dev/null 685 | aptitude install -f gtk-update-icon-cache -y 2>/dev/null 686 | 687 | # WSL support 688 | aptitude install -f gcc make gnutls-bin -y 689 | # remove old 690 | apt purge libvirt0 libvirt-bin -y 691 | apt-mark hold libvirt0 libvirt-bin 692 | install_libvirt 693 | 694 | systemctl enable libvirtd.service virtlogd.socket 695 | systemctl restart libvirtd.service virtlogd.socket 696 | 697 | kvm-ok 698 | 699 | if ! grep -q -E '^net.bridge.bridge-nf-call-ip6tables' /etc/sysctl.conf; then 700 | cat >> /etc/sysctl.conf << EOF 701 | net.bridge.bridge-nf-call-ip6tables = 0 702 | net.bridge.bridge-nf-call-iptables = 0 703 | net.bridge.bridge-nf-call-arptables = 0 704 | EOF 705 | fi 706 | # Ubuntu 18.04: 707 | # /dev/kvm permissions always changed to root after reboot 708 | # "chown root:libvirt /dev/kvm" doesnt help 709 | addgroup kvm 710 | usermod -a -G kvm "$(whoami)" 711 | if [[ -n "$username" ]]; then 712 | usermod -a -G kvm "$username" 713 | fi 714 | chgrp kvm /dev/kvm 715 | if [ ! -f /etc/udev/rules.d/50-qemu-kvm.rules ]; then 716 | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0660"' >> /etc/udev/rules.d/50-qemu-kvm.rules 717 | fi 718 | 719 | echo 1 > /sys/module/kvm/parameters/ignore_msrs 720 | echo 0 > /sys/module/kvm/parameters/report_ignored_msrs 721 | 722 | if [ ! -f /etc/modprobe.d/kvm.conf ]; then 723 | cat >> /etc/modprobe.d/kvm.conf << EOF 724 | options kvm ignore_msrs=Y 725 | options kvm report_ignored_msrs=N 726 | EOF 727 | fi 728 | } 729 | 730 | 731 | function replace_qemu_clues_public() { 732 | echo '[+] Patching QEMU clues' 733 | _sed_aux "s/QEMU HARDDISK/$qemu_hd_replacement/g" qemu*/hw/ide/core.c 'QEMU HARDDISK was not replaced in core.c' 734 | _sed_aux "s/QEMU HARDDISK/$qemu_hd_replacement/g" qemu*/hw/scsi/scsi-disk.c 'QEMU HARDDISK was not replaced in scsi-disk.c' 735 | _sed_aux "s/QEMU DVD-ROM/$qemu_dvd_replacement/g" qemu*/hw/ide/core.c 'QEMU DVD-ROM was not replaced in core.c' 736 | _sed_aux "s/QEMU DVD-ROM/$qemu_dvd_replacement/g" qemu*/hw/ide/atapi.c 'QEMU DVD-ROM was not replaced in atapi.c' 737 | _sed_aux "s/QEMU PenPartner tablet/ PenPartner tablet/g" qemu*/hw/usb/dev-wacom.c 'QEMU PenPartner tablet' 738 | _sed_aux 's/s->vendor = g_strdup("QEMU");/s->vendor = g_strdup("");/g' qemu*/hw/scsi/scsi-disk.c 'Vendor string was not replaced in scsi-disk.c' 739 | _sed_aux "s/QEMU CD-ROM/$qemu_dvd_replacement/g" qemu*/hw/scsi/scsi-disk.c 'Vendor string was not replaced in scsi-disk.c' 740 | _sed_aux 's/padstr8(buf + 8, 8, "QEMU");/padstr8(buf + 8, 8, "");/g' qemu*/hw/ide/atapi.c 'padstr was not replaced in atapi.c' 741 | _sed_aux 's/QEMU MICRODRIVE/ MICRODRIVE/g' qemu*/hw/ide/core.c 'QEMU MICRODRIVE was not replaced in core.c' 742 | _sed_aux "s/KVMKVMKVM\\0\\0\\0/$hypervisor_string_replacemnt/g" qemu*/target/i386/kvm.c 'KVMKVMKVM was not replaced in kvm.c' 743 | _sed_aux 's/"bochs"/""/g' qemu*/block/bochs.c 'BOCHS was not replaced in block/bochs.c' 744 | _sed_aux 's/"BOCHS "/"ALASKA"/g' qemu*/include/hw/acpi/aml-build.h 'BOCHS was not replaced in block/bochs.c' 745 | _sed_aux 's/Bochs Pseudo/Intel RealTime/g' qemu*/roms/ipxe/src/drivers/net/pnic.c 'Bochs Pseudo was not replaced in roms/ipxe/src/drivers/net/pnic.c' 746 | } 747 | 748 | function replace_seabios_clues_public() { 749 | echo "[+] Generating SeaBios Kconfig" 750 | echo "[+] Fixing SeaBios antivms" 751 | _sed_aux 's/Bochs/DELL/g' src/config.h 'Bochs was not replaced in src/config.h' 752 | _sed_aux "s/BOCHSCPU/$bochs_cpu_replacement/g" src/config.h 'BOCHSCPU was not replaced in src/config.h' 753 | _sed_aux 's/"BOCHS "/"DELL"/g' src/config.h 'BOCHS was not replaced in src/config.h' 754 | _sed_aux 's/BXPC/DELL/g' src/config.h 'BXPC was not replaced in src/config.h' 755 | _sed_aux "s/QEMU\/Bochs/$qemu_bochs_cpu/g" vgasrc/Kconfig 'QEMU\/Bochs was not replaced in vgasrc/Kconfig' 756 | _sed_aux "s/qemu /$qemu_space_replacement/g" vgasrc/Kconfig 'qemu was not replaced in vgasrc/Kconfig' 757 | _sed_aux "s/06\/23\/99/$src_misc_bios_table/g" src/misc.c 'change seabios date 1' 758 | _sed_aux "s/04\/01\/2014/$src_bios_table_date2/g" src/fw/biostables.c 'change seabios date 2' 759 | _sed_aux "s/01\/01\/2011/$src_fw_smbios_date/g" src/fw/smbios.c 'change seabios date 3' 760 | _sed_aux 's/"SeaBios"/"AMIBios"/g' src/fw/biostables.c 'change seabios to amibios' 761 | 762 | FILES=( 763 | src/hw/blockcmd.c 764 | src/fw/paravirt.c 765 | ) 766 | for file in "${FILES[@]}"; do 767 | _sed_aux 's/"QEMU/"/g' "$file" "QEMU was not replaced in $file" 768 | done 769 | 770 | _sed_aux 's/"QEMU"/""/g' src/hw/blockcmd.c '"QEMU" was not replaced in src/hw/blockcmd.c' 771 | 772 | FILES=( 773 | "src/fw/acpi-dsdt.dsl" 774 | "src/fw/q35-acpi-dsdt.dsl" 775 | ) 776 | for file in "${FILES[@]}"; do 777 | _sed_aux 's/"BXPC"/""/g' "$file" "BXPC was not replaced in $file" 778 | done 779 | _sed_aux 's/"BXPC"/"AMPC"/g' "src/fw/ssdt-pcihp.dsl" 'BXPC was not replaced in src/fw/ssdt-pcihp.dsl' 780 | _sed_aux 's/"BXDSDT"/"AMDSDT"/g' "src/fw/ssdt-pcihp.dsl" 'BXDSDT was not replaced in src/fw/ssdt-pcihp.dsl' 781 | _sed_aux 's/"BXPC"/"AMPC"/g' "src/fw/ssdt-proc.dsl" 'BXPC was not replaced in "src/fw/ssdt-proc.dsl"' 782 | _sed_aux 's/"BXSSDT"/"AMSSDT"/g' "src/fw/ssdt-proc.dsl" 'BXSSDT was not replaced in src/fw/ssdt-proc.dsl' 783 | _sed_aux 's/"BXPC"/"AMPC"/g' "src/fw/ssdt-misc.dsl" 'BXPC was not replaced in src/fw/ssdt-misc.dsl' 784 | _sed_aux 's/"BXSSDTSU"/"AMSSDTSU"/g' "src/fw/ssdt-misc.dsl" 'BXDSDT was not replaced in src/fw/ssdt-misc.dsl' 785 | _sed_aux 's/"BXSSDTSUSP"/"AMSSDTSUSP"/g' src/fw/ssdt-misc.dsl 'BXSSDTSUSP was not replaced in src/fw/ssdt-misc.dsl' 786 | _sed_aux 's/"BXSSDT"/"AMSSDT"/g' src/fw/ssdt-proc.dsl 'BXSSDT was not replaced in src/fw/ssdt-proc.dsl' 787 | _sed_aux 's/"BXSSDTPCIHP"/"AMSSDTPCIHP"/g' src/fw/ssdt-pcihp.dsl 'BXPC was not replaced in src/fw/ssdt-pcihp.dsl' 788 | 789 | FILES=( 790 | src/fw/q35-acpi-dsdt.dsl 791 | src/fw/acpi-dsdt.dsl 792 | src/fw/ssdt-misc.dsl 793 | src/fw/ssdt-proc.dsl 794 | src/fw/ssdt-pcihp.dsl 795 | src/config.h 796 | ) 797 | for file in "${FILES[@]}"; do 798 | _sed_aux 's/"BXPC"/"A M I"/g' "$file" "BXPC was not replaced in $file" 799 | done 800 | } 801 | 802 | function install_qemu() { 803 | cd /tmp || return 804 | 805 | echo '[+] Cleaning QEMU old install if exists' 806 | rm -r /usr/share/qemu >/dev/null 2>&1 807 | dpkg -r ubuntu-vm-builder python-vm-builder >/dev/null 2>&1 808 | dpkg -l |grep qemu |cut -d " " -f 3|xargs dpkg --purge --force-all >/dev/null 2>&1 809 | 810 | echo '[+] Downloading QEMU source code' 811 | if [ ! -f qemu-$qemu_version.tar.xz ]; then 812 | wget -q "https://download.qemu.org/qemu-$qemu_version.tar.xz" 813 | wget -q "https://download.qemu.org/qemu-$qemu_version.tar.xz.sig" 814 | gpg --verify "qemu-$qemu_version.tar.xz.sig" 815 | fi 816 | 817 | if [ ! -f qemu-$qemu_version.tar.xz ]; then 818 | echo "[-] Download qemu-$qemu_version failed" 819 | exit 820 | fi 821 | 822 | if ! tar xf "qemu-$qemu_version.tar.xz" ; then 823 | echo "[-] Failed to extract, check if download was correct" 824 | exit 1 825 | fi 826 | 827 | if [ "$OS" = "Linux" ]; then 828 | aptitude install -f software-properties-common -y 829 | add-apt-repository universe -y 830 | apt update 2>/dev/null 831 | aptitude install -f python3-pip openbios-sparc openbios-ppc libssh2-1-dev vde2 liblzo2-dev libghc-gtk3-dev libsnappy-dev libbz2-dev libxml2-dev google-perftools libgoogle-perftools-dev libvde-dev python3-sphinx-rtd-theme -y 832 | aptitude install -f debhelper libusb-1.0-0-dev libxen-dev uuid-dev xfslibs-dev libjpeg-dev libusbredirparser-dev device-tree-compiler texinfo libbluetooth-dev libbrlapi-dev libcap-ng-dev libcurl4-gnutls-dev libfdt-dev gnutls-dev libiscsi-dev libncurses5-dev libnuma-dev libcacard-dev librados-dev librbd-dev libsasl2-dev libseccomp-dev libspice-server-dev libaio-dev libcap-dev libattr1-dev libpixman-1-dev libgtk2.0-bin libxml2-utils systemtap-sdt-dev uml-utilities libcapstone-dev -y 833 | # qemu docs required 834 | PERL_MM_USE_DEFAULT=1 perl -MCPAN -e install "Perl/perl-podlators" 835 | pip3 install sphinx ninja 836 | fi 837 | # WOOT 838 | # some checks may be depricated, but keeping them for compatibility with old versions 839 | #if [ $? -eq 0 ]; then 840 | if declare -f -F "replace_qemu_clues"; then 841 | # Private version 842 | replace_qemu_clues 843 | else 844 | # Public version 845 | replace_qemu_clues_public 846 | fi 847 | # ToDo reintroduce it? 848 | #if [ $fail -eq 0 ]; then 849 | echo '[+] Starting compile it' 850 | cd qemu-$qemu_version || return 851 | # add in future --enable-netmap https://sgros-students.blogspot.com/2016/05/installing-and-testing-netmap.html 852 | # remove --target-list=i386-softmmu,x86_64-softmmu,i386-linux-user,x86_64-linux-user if you want all targets 853 | ./configure $QTARGETS --prefix=/usr --libexecdir=/usr/lib/qemu --localstatedir=/var --bindir=/usr/bin/ --enable-gnutls --enable-docs --enable-gtk --enable-vnc --enable-vnc-sasl --enable-curl --enable-kvm --enable-linux-aio --enable-cap-ng --enable-vhost-net --enable-vhost-crypto --enable-spice --enable-usb-redir --enable-lzo --enable-snappy --enable-bzip2 --enable-coroutine-pool --enable-replication --enable-tools 854 | # --enable-capstone 855 | if [ $? -eq 0 ]; then 856 | echo '[+] Starting Install it' 857 | if [ -f /usr/share/qemu/qemu_logo_no_text.svg ]; then 858 | rm /usr/share/qemu/qemu_logo_no_text.svg 859 | fi 860 | mkdir -p /tmp/qemu-"$qemu_version"_builded/DEBIAN 861 | echo -e "Package: qemu\nVersion: $qemu_version\nArchitecture: $ARCH\nMaintainer: $MAINTAINER\nDescription: Custom antivm qemu" > /tmp/qemu-"$qemu_version"_builded/DEBIAN/control 862 | make -j"$(nproc)" install DESTDIR=/tmp/qemu-"$qemu_version"_builded 863 | if [ "$OS" = "Linux" ]; then 864 | dpkg-deb --build --root-owner-group /tmp/qemu-"$qemu_version"_builded 865 | apt -y -o Dpkg::Options::="--force-overwrite" install /tmp/qemu-"$qemu_version"_builded.deb 866 | elif [ "$OS" = "Darwin" ]; then 867 | make -j"$(nproc)" install 868 | fi 869 | # hack for libvirt/virt-manager 870 | if [ ! -f /usr/bin/qemu-system-x86_64-spice ]; then 871 | ln -s /usr/bin/qemu-system-x86_64 /usr/bin/qemu-system-x86_64-spice 872 | fi 873 | if [ ! -f /usr/bin/kvm-spice ]; then 874 | ln -s /usr/bin/qemu-system-x86_64 /usr/bin/kvm-spice 875 | fi 876 | if [ ! -f /usr/bin/kvm ]; then 877 | ln -s /usr/bin/qemu-system-x86_64 /usr/bin/kvm 878 | fi 879 | if [ $? -eq 0 ]; then 880 | echo '[+] Patched, compiled and installed' 881 | else 882 | echo '[-] Install failed' 883 | fi 884 | else 885 | echo '[-] Compilling failed' 886 | fi 887 | #else 888 | # echo '[-] Check previous output' 889 | # exit 890 | #fi 891 | 892 | #else 893 | # echo '[-] Download QEMU source was not possible' 894 | #fi 895 | if [ "$OS" = "linux" ]; then 896 | dpkg --get-selections | grep "qemu" | xargs apt-mark hold 897 | dpkg --get-selections | grep "libvirt" | xargs apt-mark hold 898 | apt-mark unhold qemu libvirt 899 | fi 900 | 901 | } 902 | 903 | function install_seabios() { 904 | cd /tmp || return 905 | echo '[+] Installing SeaBios dependencies' 906 | aptitude install -f git acpica-tools -y 907 | if [ -d seabios ]; then 908 | rm -r seabios 909 | fi 910 | if git clone https://github.com/coreboot/seabios.git; then 911 | cd seabios || return 912 | if declare -f -F "replace_seabios_clues"; then 913 | replace_seabios_clues 914 | else 915 | replace_seabios_clues_public 916 | fi 917 | # make help 918 | # make menuconfig -> BIOS tables -> disable Include default ACPI DSDT 919 | # get rid of this hack 920 | make -j"$(nproc)" 2>/dev/null 921 | # Windows 10(latest rev.) is uninstallable without ACPI_DSDT 922 | # sed -i 's/CONFIG_ACPI_DSDT=y/CONFIG_ACPI_DSDT=n/g' .config 923 | sed -i 's/CONFIG_XEN=y/CONFIG_XEN=n/g' .config 924 | sed -i 's/PYTHON=python/PYTHON=python3/g' Makefile 925 | if make -j "$(nproc)"; then 926 | echo '[+] Replacing old bios.bin to new out/bios.bin' 927 | bios=0 928 | SHA256_BIOS=$(shasum -a 256 out/bios.bin|awk '{print $1}') 929 | 930 | #if [ ! -f /usr/share/qemu/bios.bin_back ]; then 931 | # cp /usr/share/qemu/bios.bin /usr/share/qemu/bios.bin_back 932 | # cp /usr/share/qemu/bios-256k.bin /usr/share/qemu/bios-256k.bin_back 933 | #fi 934 | 935 | FILES=( 936 | "/usr/share/qemu/bios.bin" 937 | "/usr/share/qemu/bios-256k.bin" 938 | ) 939 | for file in "${FILES[@]}"; do 940 | cp -vf out/bios.bin "$file" 941 | SHA256_BIOS_TMP=$(shasum -a 256 $file|awk '{print $1}') 942 | if [[ $SHA256_BIOS_TMP != $SHA256_BIOS ]]; then 943 | echo "[-] BIOS hashes doesn't match: $SHA256_BIOS - $SHA256_BIOS_TMP" 944 | bios=0 945 | else 946 | bios=1 947 | fi 948 | done 949 | 950 | if grep -q -E 'prebuild.qemu.org' /usr/share/qemu/bios.bin; then 951 | echo 'YOUR BIOS /usr/share/qemu/bios.bin is default, you might have max RAM limit inside of the VM, replace with latest compiled' 952 | bios=0 953 | fi 954 | 955 | if [ $bios -eq 1 ]; then 956 | echo '[+] Patched bios.bin placed correctly' 957 | else 958 | echo '[-] Bios patching failed' 959 | fi 960 | else 961 | echo '[-] Bios compilation failed' 962 | fi 963 | cd - || return 964 | else 965 | echo '[-] Check if git installed or network connection is OK' 966 | fi 967 | } 968 | 969 | function enable_sysrq(){ 970 | if ! grep -q -E '^kernel.sysrq=1' /etc/sysctl.conf; then 971 | echo "kernel.sysrq=1" >> /etc/sysctl.conf 972 | fi 973 | } 974 | 975 | function issues(){ 976 | cat << EndOfHelp 977 | ### Links: 978 | * https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/virtualization_deployment_and_administration_guide/sect-troubleshooting-common_libvirt_errors_and_troubleshooting 979 | * https://wiki.libvirt.org/page/Failed_to_connect_to_the_hypervisor 980 | 981 | ### Errors and Solutions 982 | 983 | * Error: 984 | * VM can't use more than 2-3Gb of ram for x64 VM 985 | * Solution: 986 | * Ensure that you not using default QEMU bios.bin, use next command to check, it shouldn't find coincidences 987 | * grep "prebuild.qemu.org" /usr/share/qemu/bios.bin 988 | * Error: 989 | * GLib-GIO-ERROR **: 09:05:35.162: Settings schema 'org.virt-manager.virt-manager' is not installed 990 | * Solution: 991 | * sudo glib-compile-schemas --strict /usr/share/glib-2.0/schemas/ 992 | 993 | * Error: 994 | * error: internal error: cannot load AppArmor profile 995 | * Solution: 996 | * Any apparmor error try to run: /usr/libexec/virt-aa-helper or journalctl -u libvirtd | cat 997 | * most of the issues with AppArmor is related to libvirt problems 998 | 999 | * Error: 1000 | * /usr/libexec/virt-aa-helper: error while loading shared libraries: libvirt.so.0: cannot open shared object file: No such file or directory 1001 | * Solution: 1002 | strace -Tfe trace=openat /usr/libexec/virt-aa-helper 1003 | 1004 | * Error 1005 | /usr/libexec/virt-aa-helper: error while loading shared libraries: libvirt.so.0: cannot open shared object file: Permission denied 1006 | * Solution: 1007 | aa-complain /usr/libexec/virt-aa-helper 1008 | 1009 | * Error: 1010 | * If you getting an apparmor error 1011 | * Solution 1012 | * sed -i 's/#security_driver = "apparmor"/security_driver = "apparmor"/g' /etc/libvirt/qemu.conf 1013 | 1014 | * Error: 1015 | required by /usr/lib/libvirt/storage-file/libvirt_storage_file_fs.so 1016 | * Solution: 1017 | systemctl daemon-reload 1018 | systemctl restart libvirtd libvirt-guests.service 1019 | 1020 | * Error: 1021 | /libvirt.so.0: version LIBVIRT_PRIVATE_x.x.0' not found (required by /usr/sbin/libvirtd) 1022 | * Solutions: 1023 | 1. apt purge libvirt0 libvirt-bin 1024 | 2. reboot 1025 | 3. $0 libvirt 1026 | 1027 | Can be extra help, but normally solved with first3 steps 1028 | 1. ldd /usr/sbin/libvirtd 1029 | 2. ls -lah /usr/lib/libvirt* 1030 | * Make sure what all symlinks pointing to last version 1031 | * Error: 1032 | * Libvirt sometimes causes access denied errors with access the locations different from "/var/lib/libvirt/images" 1033 | * Solution: 1034 | * sed -i 's/user = "root"/user = "$(whoami)"/g' /etc/libvirt/qemu.conf 1035 | * sed -i 's/user = "root"/group = "libvirt"/g' /etc/libvirt/qemu.conf 1036 | 1037 | * Error: 1038 | libvirt: Polkit error : authentication unavailable: no polkit agent available to authenticate action 'org.libvirt.unix.manage' 1039 | * Solutions: 1040 | 1. 1041 | sed -i 's/#unix_sock_group/unix_sock_group/g' /etc/libvirt/libvirtd.conf 1042 | sed -i 's/#unix_sock_ro_perms = "0777"/unix_sock_ro_perms = "0770"/g' /etc/libvirt/libvirtd.conf 1043 | sed -i 's/#unix_sock_rw_perms = "0770"/unix_sock_rw_perms = "0770"/g' /etc/libvirt/libvirtd.conf 1044 | sed -i 's/#auth_unix_ro = "none"/auth_unix_ro = "none"/g' /etc/libvirt/libvirtd.conf 1045 | sed -i 's/#auth_unix_rw = "none"/auth_unix_rw = "none"/g' /etc/libvirt/libvirtd.conf 1046 | 2. Add ssh key to $HOME/.ssh/authorized_keys 1047 | virt-manager -c "qemu+ssh://user@host/system?socket=/var/run/libvirt/libvirt-sock" 1048 | 1049 | * Error: 1050 | unable to execute QEMU command 'getfd' 1051 | * Solution: 1052 | Compile without apparmor 1053 | 1054 | * Slow HDD/Snapshot taking performance? 1055 | Modify 1056 | 1057 | To 1058 | 1059 | * Error: 1060 | error : virPidFileAcquirePath:422 : Failed to acquire pid file '/var/run/libvirtd.pid': Resource temporarily unavailable 1061 | * Solution 1062 | ps aux | grep libvirtd 1063 | * Error: 1064 | Failed to connect socket to '/var/run/libvirt/libvirt-sock': Permission denied 1065 | * Solution: 1066 | * usermod -G libvirt -a username 1067 | * log out and log in 1068 | 1069 | * Error: 1070 | yara: error while loading shared libraries: libyara.so.3: cannot open shared object file: No such file or directory 1071 | 1072 | Solution 1: 1073 | aptitude install -f libyara3 1074 | Solution 2: 1075 | sudo echo "/usr/local/lib" >> /etc/ld.so.conf 1076 | sudo ldconfig 1077 | 1078 | # Fixes from http://ask.xmodulo.com/compile-virt-manager-debian-ubuntu.html 1079 | 1. ImportError: No module named libvirt 1080 | $ ./kvm-qemu.sh libvirt 1081 | 1082 | 2. ImportError: No module named libxml2 1083 | $ pip3 install libxml2-python3 1084 | 1085 | 3. ImportError: No module named requests 1086 | $ aptitude install -f python-requests 1087 | 1088 | 4. Error launching details: Namespace GtkVnc not available 1089 | $ ./kvm-qemu.sh libvirt 1090 | 1091 | 5. ValueError: Namespace LibvirtGLib not available 1092 | $ ./kvm-qemu.sh libvirt 1093 | 1094 | 6. ValueError: Namespace Libosinfo not available 1095 | $ aptitude install -f libosinfo-1.0 1096 | 1097 | 7. ImportError: No module named ipaddr 1098 | $ aptitude install -f python-ipaddr 1099 | 1100 | 8. Namespace Gtk not available: Could not open display: localhost:10.0 1101 | 8 ValueError: Namespace GtkSource not available 1102 | $ aptitude install -f gir1.2-gtksource-4 libgtksourceview-4-0 libgtksourceview-4-common 1103 | * Error will specify version, example gi.require_version("GtkSource", "4"), if that version is not available for your distro 1104 | * you will need downgrade your virt-manager with $ sudo rm -r /usr/share/virt-manager and install older version 1105 | 1106 | 9. ImportError: cannot import name Vte 1107 | $ aptitude install -f gir1.2-vte-2.90 1108 | 1109 | 10. TypeError: Couldn't find foreign struct converter for 'cairo.Context' 1110 | $ aptitude install -f python3-gi-cairo 1111 | 1112 | 1113 | EndOfHelp 1114 | } 1115 | 1116 | 1117 | function cloning() { 1118 | if [ $# -lt 6 ]; then 1119 | echo '[-] You must provide <#vm_to_create> ' 1120 | exit 1 1121 | fi 1122 | 1123 | which virt-manager 1124 | if [ $? -eq 1 ]; then 1125 | echo "You need to install virt-manager. Run sudo $0 virtmanager" 1126 | exit 1 1127 | fi 1128 | 1129 | virsh net-list --all|grep hostonly 1130 | if [ $? -eq 1 ]; then 1131 | cat > /tmp/hostonly.xml << EOF 1132 | 1133 | hostonly 1134 | 9385b182-075b-429e-a089-4b05374e87c2 1135 | 1136 | 1137 | 1138 | 1139 | 1140 | 1141 | 1142 | 1143 | 1144 | 1145 | 1146 | 1147 | 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | 1154 | 1155 | 1156 | 1157 | 1158 | 1159 | EOF 1160 | 1161 | virsh net-define /tmp/hostonly.xml 1162 | virsh net-autostart hostonly 1163 | virsh net-start hostonly 1164 | fi 1165 | for i in $(seq "$3" "$4"); do 1166 | worked=1 1167 | # bad macaddress can be generated 1168 | while [ $worked -eq 1 ]; do 1169 | macaddr=$(hexdump -n 6 -ve '1/1 "%.2x "' /dev/random | awk -v a="2,6,a,e" -v r="$RANDOM" 'BEGIN{srand(r);}NR==1{split(a,b,",");r=int(rand()*4+1);printf "%s%s:%s:%s:%s:%s:%s\n",substr($1,0,1),b[r],$2,$3,$4,$5,$6}') 2>/dev/null 1170 | if virt-clone --print-xml -n "$1_$i" -o "$1" -m "$macaddr" -f "${5}/${1}_${i}.qcow2" |sed "s|||g" > "$5/$1_$i.xml"; then 1171 | if [ ! -f "${5}/${1}_${i}.qcow2" ]; then 1172 | echo "Creating $5/$1_$i.qcow2" 1173 | if [ "$7" == "linked" ]; then 1174 | qemu-img create -f qcow2 -F qcow2 -b "$2" "$5/$1_$i.qcow2" 1175 | else 1176 | # full clone 1177 | cp "$2" "$5/$1_$i.qcow2" 1178 | fi 1179 | fi 1180 | #2>/dev/null 1181 | virsh net-update hostonly add-last ip-dhcp-host "" --live --config 1182 | sed -i "s|||g" "$5/$1_$i.xml" 1183 | virsh define "$5/$1_$i.xml" 1184 | worked=0 1185 | fi 1186 | done 1187 | echo "" 1188 | done 1189 | 1190 | echo "[+] Enjoy" 1191 | } 1192 | 1193 | # Doesn't work ${$1,,} 1194 | COMMAND=$(echo "$1"|tr "[:upper:]" "[:lower:]") 1195 | 1196 | case $COMMAND in 1197 | '-h') 1198 | usage 1199 | exit 0;; 1200 | 'issues') 1201 | issues 1202 | exit 0;; 1203 | esac 1204 | 1205 | #if ([ "$COMMAND" = "all" ] || [ "$COMMAND" = "libvirt" ]) && [ $# -eq 2 ]; then 1206 | # if [ id -u "$2" ]; then 1207 | # username="$2" 1208 | # else 1209 | # echo "[-] username $2 doesn't exist" 1210 | # exit 1 1211 | # fi 1212 | #fi 1213 | 1214 | #check if start with root 1215 | if [ "$EUID" -ne 0 ]; then 1216 | echo 'This script must be run as root' 1217 | exit 1 1218 | fi 1219 | 1220 | OS="$(uname -s)" 1221 | MAINTAINER="$(whoami)"_"$(hostname)" 1222 | ARCH="$(dpkg --print-architecture)" 1223 | #add-apt-repository universe 1224 | #apt update && apt upgrade 1225 | #make 1226 | 1227 | case "$COMMAND" in 1228 | 'issues') 1229 | issues;; 1230 | 'all') 1231 | aptitude install -f language-pack-UTF-8 -y 1232 | install_qemu 1233 | install_seabios 1234 | install_kvm_linux 1235 | # add check if server or desktop 1236 | # install_virt_manager 1237 | # install_libguestfs 1238 | # check if all features enabled 1239 | virt-host-validate qemu 1240 | systemctl daemon-reload 1241 | systemctl restart libvirtd libvirt-guests.service 1242 | _enable_tcp_bbr 1243 | grub_iommu 1244 | enable_sysrq 1245 | ;; 1246 | 'apparmor') 1247 | install_apparmor;; 1248 | 'qemu') 1249 | install_qemu;; 1250 | 'seabios') 1251 | install_seabios;; 1252 | 'kvm') 1253 | install_kvm_linux;; 1254 | 'libguestfs') 1255 | install_libguestfs;; 1256 | 'tcp_bbr') 1257 | _enable_tcp_bbr;; 1258 | 'replace_qemu') 1259 | if declare -f -F "replace_qemu_clues"; then 1260 | replace_qemu_clues 1261 | else 1262 | replace_qemu_clues_public 1263 | fi 1264 | ;; 1265 | 'sysrq') 1266 | enable_sysrq;; 1267 | 'libvirt') 1268 | install_libvirt;; 1269 | 'libvmi') 1270 | install_libvmi;; 1271 | 'virtmanager') 1272 | install_virt_manager;; 1273 | 'clone') 1274 | cloning "$2" "$3" "$4" "$5" "$6" "$7" "$8";; 1275 | 'noip') 1276 | if [ "$OS" = "Linux" ]; then 1277 | cd /tmp || return 1278 | if [ ! -f noip-duc-linux.tar.gz ]; then 1279 | wget -q http://www.no-ip.com/client/linux/noip-duc-linux.tar.gz 1280 | fi 1281 | tar xf noip-duc-linux.tar.gz 1282 | rm noip-duc-linux.tar.gz 1283 | cd "noip-*" || return 1284 | make install 1285 | crontab -l | { cat; echo "@reboot sleep 10 && /usr/local/bin/noip2 -c /usr/local/etc/no-ip2.conf"; } | crontab - 1286 | fi 1287 | ;; 1288 | 'replace_seabios') 1289 | if [ ! -d "$2" ]; then 1290 | echo "[-] Pass the path to SeaBios folder" 1291 | exit 1 1292 | fi 1293 | cd "$2" || exit 1 1294 | if declare -f -F "replace_seabios_clues"; then 1295 | replace_seabios_clues 1296 | else 1297 | replace_seabios_clues_public 1298 | fi 1299 | ;; 1300 | 'grub') 1301 | grub_iommu;; 1302 | 'mosh') 1303 | if [ "$OS" = "Linux" ]; then 1304 | sudo aptitude install -f mosh -y 1305 | else 1306 | echo "https://mosh.org/#getting" 1307 | fi 1308 | ;; 1309 | *) 1310 | usage;; 1311 | esac 1312 | -------------------------------------------------------------------------------- /Virtualization/libguestfs.sh: -------------------------------------------------------------------------------- 1 | # Dead code - keeping just in case a copy 2 | # use docker or another VM for sparsify VMs 3 | function install_libguestfs() { 4 | # https://libguestfs.org/guestfs-building.1.html 5 | cd /opt || return 6 | echo "[+] Check for previous version of LibGuestFS" 7 | sudo dpkg --purge --force-all "libguestfs-*" 2>/dev/null 8 | 9 | # deprecated 10 | wget -O- https://packages.erlang-solutions.com/ubuntu/erlang_solutions.asc | sudo apt-key add - 11 | sudo add-apt-repository -y "deb https://packages.erlang-solutions.com/ubuntu $(lsb_release -sc) contrib" 12 | sudo aptitude install -f default-jdk parted libyara3 erlang-dev gperf flex bison libaugeas-dev libhivex-dev supermin ocaml-nox libhivex-ocaml genisoimage libhivex-ocaml-dev libmagic-dev libjansson-dev gnulib jq ocaml-findlib opam -y 2>/dev/null 13 | sudo apt update 14 | sudo aptitude install -f erlang -y 15 | 16 | if [ ! -d libguestfs ]; then 17 | # ToDo move to latest release not latest code 18 | #_info=$(curl -H "Accept: application/vnd.github+json" -s https://api.github.com/repos/libguestfs/libguestfs/tags) 19 | #_version=$(echo $_info |jq .[0].name|sed "s/\"//g") 20 | #_repo_url=$(echo $_info |jq ".[0].zipball_url" | sed "s/\"//g") 21 | #wget -q $_repo_url 22 | #unzip $_version 23 | git clone --recursive https://github.com/libguestfs/libguestfs 24 | fi 25 | 26 | 27 | HIVEX_VERSION=1.3.23 28 | # install hivex 29 | wget https://github.com/libguestfs/hivex/archive/refs/tags/v${HIVEX_VERSION}.zip 30 | unzip v${HIVEX_VERSION} 31 | cd hivex-${HIVEX_VERSION} 32 | autoreconf -i 33 | ./generator/generator.ml 34 | ./configure 35 | make -j"$(nproc)" 36 | # make check 37 | cd .. || return 38 | 39 | cd libguestfs || return 40 | # cd $(ls | grep "libguestfs-libguestfs*") || return 41 | git submodule update --init 42 | autoreconf -i 43 | eval $(opam env) 44 | opam init 45 | opam install augeas -y 46 | eval $(opam env) 47 | OCAMLPATH=$HOME/.opam/default/lib/ocaml::/usr/lib/ocaml ./configure CFLAGS=-fPIC --disable-ocaml --disable-perl --disable-python --disable-ruby --disable-haskell --disable-php --disable-erlang --disable-lua --disable-golang --disable-gobject 48 | OCAMLPATH=$HOME/.opam/default/lib/ocaml::/usr/lib/ocaml:/opt/hivex-{HIVEX_VERSION} make clean -j"$(nproc)" 49 | 50 | # Install virt tools that are in a diff repo since LIBGUESTFS 1.46 split 51 | # More Info: https://listman.redhat.com/archives/libguestfs/2021-September/msg00153.html 52 | cd /opt || return 53 | if [ ! -d guestfs-tools ]; then 54 | git clone --recursive https://github.com/rwmjones/guestfs-tools.git 55 | fi 56 | cd guestfs-tools || return 57 | # Following tips to compile the guestfs-tools as depicted in https://www.mail-archive.com/libguestfs@redhat.com/msg22408.html 58 | git config --global --add safe.directory /opt/guestfs-tools 59 | git submodule update --init --force 60 | autoreconf -i 61 | ../libguestfs/run ./configure CFLAGS=-fPIC 62 | ../libguestfs/run make -j"$(nproc)" 63 | 64 | echo "[+] /opt/libguestfs/run --help" 65 | echo "[+] /opt/libguestfs/run /opt/guestfs-tools/sparsify/virt-sparsify -h" 66 | } 67 | -------------------------------------------------------------------------------- /Virtualization/libguestfs/Dockerfile: -------------------------------------------------------------------------------- 1 | # Based on https://anthony-f-tannous.medium.com/use-docker-and-libguestfs-tools-to-shrink-virtual-machine-disks-vmdks-cae0aae17da6 2 | # https://lucascavalare.github.io/2020-03-15-AppArmor_Docker/ 3 | 4 | # DO NOT INSTALL Docker from SNAP, it will give problems with permissions on mounting folders, as readonly blablabla 5 | # https://docs.docker.com/engine/install/ubuntu/ 6 | 7 | # sudo apt-get update 8 | # sudo apt-get install ca-certificates curl 9 | # sudo install -m 0755 -d /etc/apt/keyrings 10 | # sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc 11 | # sudo chmod a+r /etc/apt/keyrings/docker.asc 12 | 13 | # Add the repository to Apt sources: 14 | # echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 15 | # sudo apt-get update 16 | # sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 17 | 18 | # To enable internet 19 | # echo 'DOCKER_OPTS="--dns 8.8.8.8 --dns 8.8.4.4"' >> /etc/default/docker 20 | # sudo docker build --network=host -t guest-fs-tools . 21 | 22 | # Running 23 | # sudo docker run -it --rm --privileged -v /opt/VMs:/mnt/vm-disks guest-fs-tools 24 | # docker exec -it $(docker container ls | grep 'guest-fs-tools' | awk '{print $1}') sudo /usr/bin/virt-sparsify path_to_vm path_to_out_vm --tmp /mnt/vm-disks 25 | # Might need another volume mount which contains more space 26 | # last command inside docker might require sudo 27 | 28 | FROM ubuntu:22.04 29 | RUN echo "deb http://us.archive.ubuntu.com/ubuntu/ jammy universe" >> /etc/apt/sources.list && \ 30 | apt-get update && apt-get install -y --no-install-recommends libguestfs-tools software-properties-common sudo apt-utils linux-image-generic 31 | RUN useradd -ms /bin/bash -d /home/docker docker && usermod -aG sudo docker && echo "docker ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers 32 | ENV HOME=/home/docker 33 | USER docker 34 | 35 | -------------------------------------------------------------------------------- /Vol3/pony.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ''' 3 | Created on 23 Oct 2019 4 | Updated on 02 Feb 2021 for Vol3 1.0.1 5 | 6 | # Copyright (C) 2011-2021 DoomedRaven. 7 | # This file is part of Tools - https://github.com/doomedraven/Tools 8 | # See the file 'LICENSE.md' for copying permission. 9 | 10 | This is demo plugin of volatility3 to show community how to easilly upgrade/make an vol3 plugin 11 | Special huge thanks to @ikelos and @xabiugarte for help/fixes 12 | 13 | https://github.com/doomedraven/Tools/Vol3/pony.py 14 | ''' 15 | import os 16 | import re 17 | import io 18 | import sys 19 | import json 20 | 21 | import logging 22 | from typing import Any, List, Tuple, Dict, Optional, Union, Iterable 23 | from urllib.request import pathname2url 24 | import volatility3.plugins 25 | import volatility3.symbols 26 | from volatility3 import framework 27 | from volatility3.framework import interfaces, renderers, exceptions 28 | from volatility3.framework.configuration import requirements 29 | from volatility3.framework.layers import resources 30 | from volatility3.framework.renderers import format_hints 31 | from volatility3.plugins import yarascan 32 | from volatility3.plugins.windows import pslist, vadyarascan, vadinfo 33 | 34 | log = logging.getLogger(__name__) 35 | 36 | try: 37 | import yara 38 | has_yara=True 39 | except ImportError: 40 | log.info("Python Yara module not found, plugin (and dependent plugins) not available") 41 | has_yara=False 42 | 43 | def standalone_extractor(data): 44 | return Pony.get_config(data) 45 | 46 | class Pony(interfaces.plugins.PluginInterface): 47 | """ Extracts Pony config """ 48 | _version=(1, 0, 0) 49 | _required_framework_version = (2, 0, 0) 50 | 51 | @classmethod 52 | def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: 53 | return [ 54 | requirements.TranslationLayerRequirement(name='primary', 55 | description="Memory layer for the kernel", 56 | architectures=["Intel32", "Intel64"]), 57 | requirements.SymbolTableRequirement(name="nt_symbols", description="Windows kernel symbols"), 58 | requirements.IntRequirement(name="max_size", 59 | default=0x40000000, 60 | description="Set the maximum size (default is 1GB)", 61 | optional=True), 62 | requirements.VersionRequirement(name = 'pslist', component = pslist.PsList, version = (2, 0, 0)), 63 | requirements.IntRequirement(name='pid', 64 | description="Process ID to include (all other processes are excluded)", 65 | optional=True), 66 | requirements.URIRequirement(name="yara_file", description="Yara rules (as a file)", optional=True), 67 | requirements.PluginRequirement(name = 'vadyarascan', plugin = vadyarascan.VadYaraScan, version = (1, 0, 0)), 68 | requirements.VersionRequirement(name = 'vadinfo', component = vadinfo.VadInfo, version = (2, 0, 0)), 69 | ] 70 | 71 | @staticmethod 72 | def get_config(pe): 73 | # https://github.com/Xyl2k/Pony-gate-extractor/blob/master/PonyExtractor.py 74 | config = { 75 | "cncs": [], 76 | "downloads": [], 77 | } 78 | 79 | start = pe.find(b"YUIPWDFILE0YUIPKDFILE0YUICRYPTED0YUI1.0") 80 | if not start: 81 | return dict() 82 | 83 | pe = pe[start-600:start+500] 84 | gate_url = re.compile(b".*\.php$") 85 | exe_url = re.compile(b".*\.exe$") 86 | dll_url = re.compile(b".*\.dll$") 87 | output = re.findall(b"(https?:\/\/.[A-Za-z0-9-\.\_\~\:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\\=]+(?:\.php|\.exe|\.dll))", pe) 88 | if not output: 89 | return config 90 | 91 | for url in output: 92 | try: 93 | if b'\x00' not in url: 94 | if url is None: 95 | continue 96 | if gate_url.match(url): 97 | config['cncs'].append(url.lower()) 98 | elif exe_url.match(url): 99 | config['downloads'].append(url.lower()) 100 | elif dll_url.match(url): 101 | config['downloads'].append(url.lower()) 102 | except Exception as e: 103 | print(e) 104 | 105 | config["cncs"] = list(set(config["cncs"])) 106 | config["downloads"] = list(set(config["downloads"])) 107 | return config 108 | 109 | def _generator(self): 110 | if not has_yara: 111 | log.error("You must install yara") 112 | return 113 | 114 | config = dict() 115 | 116 | if self.config.get('yara_file', None) is not None: 117 | RULES = yara.compile(file=resources.ResourceAccessor().open(self.config['yara_file'], "rb")) 118 | else: 119 | # https://github.com/Yara-Rules/rules/blob/master/malware/MALW_Pony.yar 120 | SIGS = { 121 | 'pony': ''' 122 | 123 | rule pony { 124 | meta: 125 | author = "Brian Wallace @botnet_hunter" 126 | author_email = "bwall@ballastsecurity.net" 127 | date = "2014-08-16" 128 | description = "Identify Pony" 129 | strings: 130 | $ = "YUIPWDFILE0YUIPKDFILE0YUICRYPTED0YUI1.0" 131 | condition: 132 | all of them 133 | } 134 | ''' 135 | } 136 | 137 | RULES = yara.compile(sources=SIGS) 138 | 139 | #filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)]) 140 | filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)]) 141 | 142 | for task in pslist.PsList.list_processes( 143 | context=self.context, 144 | layer_name=self.config['primary'], 145 | symbol_table=self.config['nt_symbols'], 146 | filter_func=filter_func): 147 | try: 148 | proc_layer_name = task.add_process_layer() 149 | except exceptions.InvalidAddressException: 150 | continue 151 | 152 | proc_layer = self.context.layers[proc_layer_name] 153 | 154 | for offset, rule_name, name, value in proc_layer.scan( 155 | context=self.context, 156 | scanner=yarascan.YaraScanner(rules=RULES), 157 | sections=vadyarascan.VadYaraScan.get_vad_maps(task)): 158 | log.debug("Got a Yara match!") 159 | 160 | vad, vad_start, vad_end = self.get_vad(task, offset) 161 | if vad is None: 162 | log.debug("VAD not found") 163 | return 164 | 165 | full_pe = io.BytesIO() 166 | chunk_size = 1024 * 1024 * 10 167 | #vadinfo.VadInfo.vad_dump(self.context, task, vad, full_pe) 168 | offset = vad_start 169 | while offset < vad_end: 170 | to_read = min(chunk_size, vad_end - offset) 171 | data = proc_layer.read(offset, to_read, pad = True) 172 | if not data: 173 | break 174 | full_pe.write(data) 175 | offset += to_read 176 | if not full_pe: 177 | continue 178 | config = self.get_config(full_pe.getvalue()) 179 | if not config: 180 | log.debug("Config extraction failed") 181 | continue 182 | 183 | yield (0, (format_hints.Hex(offset), task.UniqueProcessId, str(config))) 184 | 185 | #replace with list_vads and write correct filter func 186 | @staticmethod 187 | def get_vad(task: interfaces.objects.ObjectInterface, address: int):# vad 188 | """Creates a map of start/end addresses within a virtual address 189 | descriptor tree. 190 | Args: 191 | task: The EPROCESS object of which to traverse the vad tree 192 | Returns: 193 | An iterable of tuples containing start and end addresses for each descriptor 194 | """ 195 | vad_root = task.get_vad_root() 196 | for vad in vad_root.traverse(): 197 | end = vad.get_end() 198 | start = vad.get_start() 199 | if end > address >= start: 200 | return vad, start, end 201 | return None, None, None 202 | 203 | def run(self): 204 | return renderers.TreeGrid([('Offset', format_hints.Hex), ('PID', int), ('Config', str)], self._generator()) 205 | 206 | if __name__ == '__main__': 207 | with open(sys.argv[1], 'rb') as f: 208 | data = f.read() 209 | config = standalone_extractor(data) 210 | print(json.dumps(config, indent=4)) 211 | -------------------------------------------------------------------------------- /Vol3/zbotscan.py: -------------------------------------------------------------------------------- 1 | # Volatility 2 | # 3 | # Zeus support: 4 | # Michael Hale Ligh 5 | # 6 | # Citadel 1.3.4.5 support: 7 | # Santiago Vicente 8 | # 9 | # Generic detection, Citadel 1.3.5.1 and ICE IX support: 10 | # Juan C. Montes 11 | # 12 | # Port to volatility3 by @doomedraven 13 | # https://github.com/doomedraven 14 | # 15 | # This program is free software; you can redistribute it and/or modify 16 | # it under the terms of the GNU General Public License as published by 17 | # the Free Software Foundation; either version 2 of the License, or (at 18 | # your option) any later version. 19 | # 20 | # This program is distributed in the hope that it will be useful, but 21 | # WITHOUT ANY WARRANTY; without even the implied warranty of 22 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23 | # General Public License for more details. 24 | # 25 | # You should have received a copy of the GNU General Public License 26 | # along with this program; if not, write to the Free Software 27 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 28 | # 29 | 30 | import re 31 | import io 32 | import sys 33 | import time 34 | import logging 35 | import binascii 36 | import struct 37 | import hashlib 38 | 39 | # VOLATILITY IMPORTS 40 | from typing import Iterable, List, Tuple 41 | from volatility3.framework.objects import utility 42 | from volatility3.framework import interfaces, renderers, exceptions, constants 43 | from volatility3.framework.configuration import requirements 44 | from volatility3.framework.layers import resources 45 | from volatility3.framework.symbols import intermed 46 | from volatility3.framework.renderers import format_hints 47 | from volatility3.plugins import yarascan 48 | from volatility3.plugins.windows import pslist, vadyarascan # , procdump, dlllist 49 | from volatility3.plugins.windows.vadinfo import VadInfo, winnt_protections 50 | 51 | 52 | try: 53 | import pefile 54 | except ImportError: 55 | print("Missed pefile library -> pip3 install pefile") 56 | 57 | try: 58 | import yara 59 | has_yara = True 60 | except ImportError: 61 | has_yara = False 62 | 63 | log = logging.getLogger(__name__) 64 | 65 | # CONSTANTS 66 | RC4_KEYSIZE = 0x102 67 | 68 | ZEUS_STURCTURE = { 69 | "_ZEUS2_CONFIG": 0x1E6, 70 | "_CITADEL1345_CONFIG": 0x11C, 71 | "_CITADEL1351_CONFIG": 0x130, 72 | } 73 | 74 | ZEUS_STURCTURE_size = { 75 | 0x1E6: "_ZEUS2_CONFIG", 76 | 0x11C: "_CITADEL1345_CONFIG", 77 | 0x130: "_CITADEL1351_CONFIG", 78 | } 79 | 80 | def _parsed_struct_read_str(data, start, length): 81 | return data[start: start+length].split(b"\x00")[0].decode("utf-8") 82 | 83 | def parsed_struct(decoded_config, decoded_magic, zbotversion): 84 | # https://en.wikipedia.org/wiki/C_data_types 85 | parsed = dict() 86 | if zbotversion == "_ZEUS2_CONFIG": 87 | """ 88 | "struct_size": [0x0, ["unsigned int"]], 89 | "guid": [0x4, ["array", 0x30, ["unsigned short"]]], 90 | "guid2": [0x7C, ["array", 0x10, ["unsigned char"]]], 91 | "rc4key": [0x8C, ["array", 0x100, ["unsigned char"]]], 92 | "exefile": [0x18E, ["String", dict(length=0x14)]], 93 | "datfile": [0x1A2, ["String", dict(length=0x14)]], 94 | "keyname": [0x1B6, ["String", dict(length=0xA)]], 95 | "value1": [0x1C0, ["String", dict(length=0xA)]], 96 | "value2": [0x1CA, ["String", dict(length=0xA)]], 97 | "value3": [0x1D4, ["String", dict(length=0xA)]], 98 | "guid_xor_key": [0x1DE, ["unsigned int"]], 99 | "xorkey": [0x1E2, ["unsigned int"]], 100 | """ 101 | parsed = { 102 | "struct_size": struct.unpack("=I", decoded_magic[:4])[0], 103 | "guid": decoded_magic[4:0x40].decode("utf-16le").split("\x00")[0], 104 | "guid2": decoded_magic[0x7c:0x7c+0x10], 105 | "rc4key": decoded_magic[0x8C: 0x8C+0x100], 106 | "exefile": _parsed_struct_read_str(decoded_magic, 0x18E, 0x14), 107 | "datfile": _parsed_struct_read_str(decoded_magic, 0x1A2, 0x14), 108 | "keyname": _parsed_struct_read_str(decoded_magic, 0x1B6, 0xa), 109 | "value1": _parsed_struct_read_str(decoded_magic, 0x1C0, 0xa), 110 | "value2": _parsed_struct_read_str(decoded_magic, 0x1CA, 0xa), 111 | "value3": _parsed_struct_read_str(decoded_magic, 0x1D4, 0xa), 112 | "guid_xor_key": struct.unpack("=I", decoded_magic[0x1de:0x1de+4])[0], 113 | "xorkey": struct.unpack("=I", decoded_magic[0x1e2:0x1e2+4])[0], 114 | } 115 | 116 | elif zbotversion in ("_CITADEL1345_CONFIG", "_CITADEL1351_CONFIG"): 117 | """ 118 | "struct_size": [0x0, ["unsigned int"]], 119 | "guid": [0x4, ["array", 0x30, ["unsigned short"]]], 120 | "guid2": [0x7C, ["array", 0x10, ["unsigned char"]]], 121 | "exefile": [0x9C, ["String", dict(length=0x14)]], 122 | "datfile": [0xB0, ["String", dict(length=0x14)]], 123 | "keyname": [0xEC, ["String", dict(length=0xA)]], 124 | "value1": [0xF6, ["String", dict(length=0xA)]], 125 | "value2": [0x100, ["String", dict(length=0xA)]], 126 | "value3": [0x10A, ["String", dict(length=0xA)]], 127 | "guid_xor_key": [0x114, ["unsigned int"]], 128 | "xorkey": [0x118, ["unsigned int"]], 129 | 130 | _CITADEL1351_CONFIG has 4 extra fields 131 | "value4": [0x11C, ["unsigned int"]], 132 | "value5": [0x120, ["unsigned int"]], 133 | "value6": [0x124, ["unsigned int"]], 134 | "value7": [0x128, ["unsigned int"]], 135 | "value8": [0x12C, ["unsigned int"]], 136 | """ 137 | parsed = { 138 | "struct_size": struct.unpack("=I", decoded_magic[:4])[0], 139 | "guid": decoded_magic[4:0x40].decode("utf-16le").split("\x00")[0], 140 | "guid2": decoded_magic[0x7c:0x7c+0x10], 141 | "exefile": _parsed_struct_read_str(decoded_magic, 0x9c, 0x14), 142 | "datfile": _parsed_struct_read_str(decoded_magic, 0xb0, 0x14), 143 | "keyname": _parsed_struct_read_str(decoded_magic, 0xec, 0xa), 144 | "value1": _parsed_struct_read_str(decoded_magic, 0xF6, 0xa), 145 | "value2": _parsed_struct_read_str(decoded_magic, 0x100, 0xa), 146 | "value3": _parsed_struct_read_str(decoded_magic, 0x10A, 0xa), 147 | "guid_xor_key": struct.unpack("=I", decoded_magic[0x114:0x114+4])[0], 148 | "xorkey": struct.unpack("=I", decoded_magic[0x118:0x118+4])[0], 149 | } 150 | 151 | if zbotversion == "_CITADEL1351_CONFIG": 152 | parsed.update({ 153 | "value4": struct.unpack("=I", decoded_magic[0x11c:0x11c+4])[0], 154 | "value5": struct.unpack("=I", decoded_magic[0x120:0x120+4])[0], 155 | "value6": struct.unpack("=I", decoded_magic[0x124:0x124+4])[0], 156 | "value7": struct.unpack("=I", decoded_magic[0x128:0x128+4])[0], 157 | "value8": struct.unpack("=I", decoded_magic[0x12c:0x12c+4])[0], 158 | }) 159 | 160 | 161 | return parsed 162 | 163 | class ZBOTScan(interfaces.plugins.PluginInterface): 164 | _version = (1, 0, 0) 165 | _required_framework_version = (2, 0, 0) 166 | 167 | """ Locate and Decrypt Configs for: ZeuS v2, Citadel 168 | * ZeuS 2.0.8.9 (z4 & z5) 169 | * ZeuS 2.1.0.1 (z3 & z5) 170 | * Ice IX (ZeuS 2.1.0.1 + mod RC4) 171 | Citadel 1.3.4.5 172 | * Citadel 1.3.5.1 173 | """ 174 | 175 | # Internal vars 176 | signatures = { 177 | # ZeuS v2 178 | 'namespace01':'rule zeus2_1 {strings: $a = {56 BA ?? ?? 00 00 52 68 ?? ?? ?? ?? 50 E8 ?? ?? ?? ?? 8B 35 ?? ?? ?? ?? 8B 0D ?? ?? ?? ??} condition: $a}', 179 | 'namespace02':'rule zeus2_2 {strings: $a = {55 8B EC 51 A1 ?? ?? ?? ?? 8B 0D ?? ?? ?? ?? 56 8D 34 01 A1 ?? ?? ?? ?? 8B 0D ?? ?? ?? ??} condition: $a}', 180 | 'namespace03':'rule zeus2_3 {strings: $a = {68 02 01 00 00 8D 84 24 ?? ?? ?? ?? 50 8D 44 24 ?? 50 E8 ?? ?? ?? ?? B8 E6 01 00 00 50 68 ?? ?? ?? ??} condition: $a}', 181 | 'namespace04':'rule zeus2_4 {strings: $a = {68 02 01 00 00 8D 85 ?? ?? ?? ?? 50 8D 85 ?? ?? ?? ?? 50 E8 ?? ?? ?? ?? B8 E6 01 00 00 50 68 ?? ?? ?? ??} condition: $a}', 182 | 'namespace05':'rule zeus2_5 {strings: $a = {56 BA ?? ?? 00 00 52 68 ?? ?? ?? ?? 50 E8 ?? ?? ?? ?? 8B 0D ?? ?? ?? ?? 03 0D ?? ?? ?? ??} condition: $a}', 183 | # Citadel 184 | 'namespace06':'rule citadel_1 {strings: $a = {8B EC 83 EC 0C 8A 82 ?? ?? ?? ?? 88 45 FE 8A 82 01 01 00 00 88 45 FD 8A 82 02 01 00 00 B9 ?? ?? ?? ?? 88 45 FF E8 ?? ?? ?? ??} condition: $a}', 185 | 'namespace07':'rule citadel_2 {strings: $a = {56 BA ?? ?? 00 00 52 68 ?? ?? ?? ?? 50 E8 ?? ?? ?? ?? 8B 0D ?? ?? ?? ?? 03 0D ?? ?? ?? ?? 8B F2 2B C8} condition: $a}', 186 | 'namespace08':'rule citadel_3 {strings: $a = {68 ?? ?? 00 00 8D 85 ?? ?? ?? ?? 50 8D 85 ?? ?? ?? ?? 50 E8 ?? ?? ?? ?? B8 ?? ?? 00 00 50 68 ?? ?? ?? ??} condition: $a}', 187 | } 188 | 189 | zbot = "" 190 | 191 | @classmethod 192 | def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: 193 | return [ 194 | requirements.TranslationLayerRequirement( 195 | name="primary", description="Memory layer for the kernel", architectures=["Intel32", "Intel64"] 196 | ), 197 | requirements.SymbolTableRequirement(name="nt_symbols", description="Windows kernel symbols"), 198 | requirements.IntRequirement( 199 | name="max_size", default=0x40000000, description="Set the maximum size (default is 1GB)", optional=True 200 | ), 201 | requirements.PluginRequirement(name="pslist", plugin=pslist.PsList, version=(2, 0, 1)), 202 | requirements.IntRequirement( 203 | name="pid", description="Process ID to include (all other processes are excluded)", optional=True 204 | ), 205 | requirements.URIRequirement(name="yara_file", description="Yara rules (as a file)", optional=True), 206 | requirements.PluginRequirement(name="vadyarascan", plugin=vadyarascan.VadYaraScan, version=(1, 0, 0)), 207 | ] 208 | 209 | @staticmethod 210 | def get_vad(task: interfaces.objects.ObjectInterface, address: int): # vad 211 | """Creates a map of start/end addresses within a virtual address 212 | descriptor tree. 213 | Args: 214 | task: The EPROCESS object of which to traverse the vad tree 215 | Returns: 216 | An iterable of tuples containing start and end addresses for each descriptor 217 | """ 218 | vad_root = task.get_vad_root() 219 | for vad in vad_root.traverse(): 220 | end = vad.get_end() 221 | start = vad.get_start() 222 | 223 | # put a max size as 500mb 224 | if end - start > 0x500000: 225 | continue 226 | 227 | if start <= address < end: 228 | return vad, start, end 229 | return None, None, None 230 | 231 | @staticmethod 232 | def carve_data(vad_start, vad_end, proc_layer): 233 | chunk_size = 1024 * 1024 * 10 234 | full_vad = io.BytesIO() 235 | tmp_offset = vad_start 236 | while tmp_offset < vad_end: 237 | to_read = min(chunk_size, vad_end - tmp_offset) 238 | data = proc_layer.read(tmp_offset, to_read, pad=True) 239 | if not data: 240 | break 241 | full_vad.write(data) 242 | tmp_offset += to_read 243 | 244 | return full_vad.getvalue() 245 | 246 | def injection_filter(self, vad): 247 | """ 248 | This is a callback that's executed by get_vads() 249 | when searching for injected code / hidden DLLs. 250 | This looks for private allocations that are committed, 251 | memory-resident, non-empty (not all zeros) and with an 252 | original protection that includes write and execute. 253 | It is important to note that protections are applied at 254 | the allocation granularity (page level). Thus the original 255 | protection might not be the current protection, and it 256 | also might not apply to all pages in the VAD range. 257 | @param vad: an MMVAD object. 258 | @returns: True if the MMVAD looks like it might 259 | contain injected code. 260 | """ 261 | 262 | protect = vad.get_protection(VadInfo.protect_values(self.context, self.config['primary'], self.config['nt_symbols']), winnt_protections) 263 | write_exec = "EXECUTE" in protect and "WRITE" in protect 264 | 265 | # The Write/Execute check applies to everything 266 | if not write_exec: 267 | return False 268 | 269 | # This is a typical VirtualAlloc'd injection 270 | try: 271 | if vad.get_private_memory() == 1 and vad.vad.get_tag() == "VadS": 272 | return True 273 | except Exception as e: 274 | print(e) 275 | # This is a stuxnet-style injection 276 | if vad.get_private_memory() == 0 and protect != "PAGE_EXECUTE_WRITECOPY": # noqa: W504 277 | return True 278 | 279 | return False 280 | 281 | def check_zbot(self): 282 | """ Detect the zbot version """ 283 | 284 | rules = yara.compile(sources=self.signatures) 285 | 286 | p_round = self.context.config.get("sandbox_round", 1) 287 | if self.context.config.get('sandbox_pids', None): 288 | pids = self.context.config.get("sandbox_pids") 289 | else: 290 | pids = [self.config.get('pid', None)] 291 | 292 | filter_func = pslist.PsList.create_pid_filter(pids, True if p_round == 2 else False) 293 | list_tasks = pslist.PsList.list_processes( 294 | context=self.context, 295 | layer_name=self.config["primary"], 296 | symbol_table=self.config["nt_symbols"], 297 | filter_func = filter_func, 298 | ) 299 | 300 | for task in list_tasks: 301 | try: 302 | proc_layer_name = task.add_process_layer() 303 | except exceptions.InvalidAddressException: 304 | continue 305 | 306 | proc_layer = self.context.layers[proc_layer_name] 307 | 308 | for vad_start, vad_len in vadyarascan.VadYaraScan.get_vad_maps(task): 309 | vad, vad_start, vad_end = self.get_vad(task, vad_start) 310 | if not vad_start or not vad_end: 311 | continue 312 | 313 | if vad_end - vad_start == 0xFFFF or vad_end - vad_start >= 1000000000: 314 | continue 315 | 316 | data = self.carve_data(vad_start, vad_end, proc_layer) 317 | if not data.startswith(b"MZ") and not self.injection_filter(vad): 318 | continue 319 | 320 | # check for the signature with YARA, both hits must be present 321 | matches = rules.match(data=data) 322 | if not matches: 323 | continue 324 | 325 | 326 | hits = dict((m.rule, m.strings[0][0]) for m in matches) 327 | log.debug("yara rules") 328 | log.debug(hits) 329 | 330 | # Rules for CITADEL 331 | if ('citadel_1' in hits) & ('citadel_2' in hits) & ('citadel_3' in hits): 332 | self.zbot = 'CITADEL' 333 | log.debug('CITADEL DETECTED') 334 | return task 335 | 336 | # Rules for ZEUS2 337 | if ( (('zeus2_1' in hits) | ('zeus2_2' in hits) | ('zeus2_5' in hits)) & 338 | (('zeus2_3' in hits) | ('zeus2_4' in hits)) ): 339 | self.zbot = 'ZEUS' 340 | log.debug('ZEUS v2 DETECTED') 341 | return task 342 | 343 | def run(self): 344 | return renderers.TreeGrid([("PID", int), ("Config", str)], self._generator()) 345 | 346 | def _generator(self): 347 | """ Check the zbot version and analyze it """ 348 | 349 | task = self.check_zbot() 350 | malware = None 351 | 352 | if self.zbot == 'CITADEL': 353 | malware = Citadel(self.config, self.context) 354 | elif self.zbot == 'ZEUS': 355 | malware = ZeuS2(self.config, self.context) 356 | elif malware.zbot == 'ICEIX': 357 | malware = ICEIX(self.config, self.context) 358 | 359 | if malware: 360 | config = malware.calculate(task) 361 | # malware.render_text(sys.stdout, data) 362 | yield (0, (task.UniqueProcessId, str(config))) 363 | 364 | 365 | class ZbotCommon(): 366 | """ Common functions for all zbot versions """ 367 | 368 | params = dict( 369 | # This contains the C2 URL, RC4 key for decoding 370 | # local.ds and the magic buffer 371 | decoded_config = None, 372 | # This contains the hardware lock info, the user.ds 373 | # RC4 key, and XOR key 374 | encoded_magic = None, 375 | # The decoded version of the magic structure 376 | decoded_magic = None, 377 | # The key for decoding the configuration 378 | config_key = None, 379 | # The login key (citadel only) 380 | login_key = None, 381 | # The AES key (citadel only) 382 | aes_key = None, 383 | 384 | ) 385 | 386 | # Depricated 387 | def get_hex(self, buf): 388 | return "\n".join(["{0:#010x} {1:<48} {2}".format(o, h, ''.join(c)) for o, h, c in utils.Hexdump(buf)]) 389 | 390 | def decode_config(self, encoded_config, last_sec_data): 391 | """Decode the config with data from the last PE section. 392 | 393 | @param encoded_config: the encoded configuration 394 | @param last_sec_data: last PE section data. 395 | """ 396 | 397 | return bytes([(last_sec_data[i] ^ encoded_config[i]) for i in range(len(encoded_config))]) 398 | 399 | 400 | def decode_magic(self, config_key): 401 | """Decode the magic structure using the configuration key. 402 | 403 | @param config_key: the config RC4 key. 404 | """ 405 | 406 | return self.rc4(config_key, self.params['encoded_magic']) 407 | 408 | def rc4(self, key, encoded, login_key=0): 409 | """Perform a basic RC4 operation""" 410 | # Turn the buffers into lists so the elements are mutable 411 | key_copy = [c for c in key] 412 | enc_copy = [c for c in encoded] 413 | 414 | # Start with the last two bytes in the key 415 | var1 = key_copy[0x100] 416 | var2 = key_copy[0x101] 417 | var3 = 0 418 | # ICE IX MOD 419 | mod1 = 0 420 | mod2 = 0 421 | if self.zbot == "ICEIX": 422 | mod1 = 3 423 | mod2 = 7 424 | 425 | # Do the RC4 algorithm 426 | for i in range(0, len(enc_copy)): 427 | var1 += 1 + mod1 428 | a = var1 & 0xFF 429 | b = key_copy[a] 430 | var2 += b 431 | var2 &= 0xFF 432 | key_copy[a] = key_copy[var2] 433 | key_copy[var2] = b 434 | enc_copy[i] ^= key_copy[(key_copy[a] + b + mod2) & 0xFF] 435 | 436 | # CITADEL MOD 437 | if self.zbot == "CITADEL": 438 | if not login_key: 439 | login_key = self.params["login_key"] 440 | enc_copy[i] ^= login_key[var3] 441 | var3 += 1 442 | if var3 == len(login_key): 443 | var3 = 0 444 | 445 | # Return the decoded bytes as a string 446 | decoded = [c for c in enc_copy] 447 | return bytes(decoded) 448 | 449 | def get_only_hex(self, buf, start=0, length=16): 450 | """Hexdump formula seen at http://code.activestate.com/recipes/142812-hex-dumper""" 451 | result = "" 452 | for i in range(0, len(buf), length): 453 | s = buf[i : i + length] 454 | result = result + "".join(["%02x" % x for x in s]) 455 | return result 456 | 457 | def rc4_init(self, data): 458 | """Initialize the RC4 keystate""" 459 | # The key starts off as a mutable list 460 | key = list() 461 | for i in range(0, 256): 462 | key.append(i) 463 | # Add the trailing two bytes 464 | key.append(0) 465 | key.append(0) 466 | # Make a copy of the data so its mutable also 467 | data_copy = [ord(c) for c in data] 468 | var1 = 0 469 | var2 = 0 470 | for i in range(0, 256): 471 | a = key[i] 472 | var2 += data_copy[var1] + a 473 | var2 &= 0xFF 474 | var1 += 1 475 | key[i] = key[var2] 476 | key[var2] = a 477 | # Return a copy of the key as a string 478 | return "".join([chr(c) for c in key]) 479 | 480 | 481 | class ZeuS2(ZbotCommon): 482 | """ Scanner for ZeuS v2 """ 483 | 484 | def __init__(self, config, context): 485 | self.zbot = "ZEUS" 486 | self.zbotversion = "" 487 | self.config = config 488 | self.context = context 489 | 490 | self.signatures = { 491 | "namespace1": r"rule z1 {strings: $a = {56 BA ?? ?? 00 00 52 68 ?? ?? ?? ?? 50 E8 ?? ?? ?? ?? 8B 35 ?? ?? ?? ?? 8B 0D ?? ?? ?? ??} condition: $a}", 492 | "namespace5": r"rule z5 {strings: $a = {56 BA ?? ?? 00 00 52 68 ?? ?? ?? ?? 50 E8 ?? ?? ?? ?? 8B 0D ?? ?? ?? ?? 03 0D ?? ?? ?? ??} condition: $a}", 493 | "namespace2": r"rule z2 {strings: $a = {55 8B EC 51 A1 ?? ?? ?? ?? 8B 0D ?? ?? ?? ?? 56 8D 34 01 A1 ?? ?? ?? ?? 8B 0D ?? ?? ?? ??} condition: $a}", 494 | "namespace3": r"rule z3 {strings: $a = {68 02 01 00 00 8D 84 24 ?? ?? ?? ?? 50 8D 44 24 ?? 50 E8 ?? ?? ?? ?? B8 E6 01 00 00 50 68 ?? ?? ?? ??} condition: $a}", 495 | "namespace4": r"rule z4 {strings: $a = {68 02 01 00 00 8D 85 ?? ?? ?? ?? 50 8D 85 ?? ?? ?? ?? 50 E8 ?? ?? ?? ?? B8 E6 01 00 00 50 68 ?? ?? ?? ??} condition: $a}", 496 | } 497 | 498 | self.magic_struct = "_ZEUS2_CONFIG" 499 | self.magic_struct_size = ZEUS_STURCTURE[self.magic_struct] 500 | 501 | def check_matches_zeus2(self, proc_layer, vad_start, matches, last_sec_data): 502 | """Check the Yara matches and derive the encoded/decoded 503 | config objects and magic structures. 504 | 505 | @param task_space: the process AS 506 | @param vad: the containing MMVAD 507 | @param matches: list of YARA hits 508 | @param last_sec_data: buffer of the last PE section's data 509 | """ 510 | 511 | hits = dict((m.rule, m.strings[0][0] + vad_start) for m in matches) 512 | # Check version 513 | if ("z3" in hits) & ("z5" in hits): 514 | self.zbotversion = " 2.1.0.1" 515 | elif ("z4" in hits) & ("z5" in hits): 516 | self.zbotversion = " 2.0.8.9" 517 | 518 | if "z3" in hits: 519 | addr = struct.unpack("=I", proc_layer.read(hits["z3"] + 30, 0x4))[0] 520 | self.params["encoded_magic"] = proc_layer.read(addr, self.magic_struct_size) 521 | elif "z4" in hits: 522 | addr = struct.unpack("=I", proc_layer.read(hits["z4"] + 31, 0x4))[0] 523 | self.params["encoded_magic"] = proc_layer.read(addr, self.magic_struct_size) 524 | else: 525 | return False 526 | 527 | if "z1" in hits: 528 | addr = struct.unpack("=I", proc_layer.read(hits["z1"] + 8, 0x4))[0] 529 | size = struct.unpack("=I", proc_layer.read(hits["z1"] + 2, 0x4))[0] 530 | encoded_config = proc_layer.read(addr, size) 531 | self.params["decoded_config"] = self.decode_config(encoded_config, last_sec_data) 532 | elif "z2" in hits: 533 | addr = struct.unpack("=I", proc_layer.read(hits["z2"] + 26, 0x4))[0] 534 | encoded_config = proc_layer.read(addr, 0x3C8) 535 | rc4_init = self.rc4_init(encoded_config) 536 | self.params["decoded_config"] = self.rc4(rc4_init, last_sec_data[2:]) 537 | elif "z5" in hits: 538 | addr = struct.unpack("=I", proc_layer.read(hits["z5"] + 8, 0x4))[0] 539 | size = struct.unpack("=I", proc_layer.read(hits["z5"] + 2, 0x4))[0] 540 | encoded_config = proc_layer.read(addr, size) 541 | self.params["decoded_config"] = self.decode_config(encoded_config, last_sec_data) 542 | else: 543 | return False 544 | 545 | # We found at least one of each category 546 | return True 547 | 548 | def scan_key_zeus2(self, task_space): 549 | """Find the offset of the RC4 key and use it to 550 | decode the magic buffer. 551 | 552 | @param task_space: the process AS 553 | """ 554 | 555 | offset = 0 556 | found = False 557 | 558 | while offset < len(self.params["decoded_config"]) - RC4_KEYSIZE: 559 | config_key = self.params["decoded_config"][offset : offset + RC4_KEYSIZE] 560 | decoded_magic = self.decode_magic(config_key) 561 | 562 | # When the first four bytes of the decoded magic buffer 563 | # equal the size of the magic buffer, then we've found 564 | # a winning RC4 key 565 | (struct_size,) = struct.unpack("=I", decoded_magic[0:4]) 566 | 567 | if self.magic_struct_size != struct_size & struct_size < 1500: 568 | log.debug("size error") 569 | log.debug(struct_size) 570 | log.debug(self.magic_struct_size) 571 | 572 | if struct_size == self.magic_struct_size: 573 | found = True 574 | self.params["config_key"] = config_key 575 | self.params["decoded_magic"] = decoded_magic 576 | break 577 | 578 | offset += 1 579 | 580 | return found 581 | 582 | def calculate(self, task): # noqa: C901 583 | """ Analyze zbot process """ 584 | rules = yara.compile(sources=self.signatures) 585 | config = dict() 586 | 587 | try: 588 | proc_layer_name = task.add_process_layer() 589 | except exceptions.InvalidAddressException: 590 | return 591 | 592 | proc_layer = self.context.layers[proc_layer_name] 593 | for vad_start, vad_len in vadyarascan.VadYaraScan.get_vad_maps(task): 594 | 595 | vad, vad_start, vad_end = ZBOTScan.get_vad(task, vad_start) 596 | # check for the signature with YARA, both hits must be present 597 | if not vad_start or not vad_end: 598 | log.debug("missed VAD details") 599 | continue 600 | 601 | if vad_end - vad_start == 0xFFFF or vad_end - vad_start >= 1000000000 : 602 | log.debug("VAD is too big") 603 | continue 604 | 605 | data = ZBOTScan.carve_data(vad_start, vad_end, proc_layer) 606 | # check for the signature with YARA, both hits must be present 607 | matches = rules.match(data=data) 608 | if not matches: 609 | continue 610 | 611 | if not data.startswith(b"MZ"): 612 | log.debug("NOT MZ") 613 | continue 614 | 615 | if len(matches) < 2: 616 | log.debug("don't have 2 matches") 617 | continue 618 | try: 619 | # There must be more than 2 sections 620 | pe = pefile.PE(data=data, fast_load=True) 621 | if len(pe.sections) < 2: 622 | log.debug("less than 2 sections") 623 | continue 624 | except Exception as e: 625 | print(e) 626 | continue 627 | 628 | # Get the last PE section's data 629 | last_sec = pe.sections[-1] 630 | last_sec_data = proc_layer.read((last_sec.VirtualAddress + vad_start), last_sec.Misc_VirtualSize) 631 | if len(last_sec_data) == 0: 632 | log.debug("empty section") 633 | continue 634 | 635 | # CITADEL 636 | if self.zbot == "CITADEL": 637 | success = self.check_matches_citadel(proc_layer, vad_start, matches, last_sec_data) 638 | if not success: 639 | continue 640 | success = self.scan_key_citadel(proc_layer) 641 | if not success: 642 | continue 643 | # ZEUS v2 or ICE IX 644 | elif self.zbot == "ZEUS": 645 | success = self.check_matches_zeus2(proc_layer, vad_start, matches, last_sec_data) 646 | if not success: 647 | log.debug("check_matches_zeus2 false") 648 | continue 649 | success = self.scan_key_zeus2(proc_layer) 650 | if not success: 651 | # Check ICEIX 652 | if self.zbotversion == " 2.1.0.1": 653 | self.zbot = "ICEIX" 654 | self.zbotversion = "" 655 | log.debug("Checking ICE IX") 656 | malware = ICEIX(self.config, self.context) 657 | config = malware.calculate(task, vad_start, data, proc_layer) 658 | else: 659 | continue 660 | else: 661 | # Parse zbotv2 here 662 | parsed = parsed_struct(self.params["decoded_config"], self.params["decoded_magic"], self.magic_struct) 663 | registry_dict = { 664 | "key_path": "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\%s" % "{0}".format(parsed["keyname"]), 665 | # .v() Do the actual reading and decoding of this member 666 | "Value1": "{0}".format(parsed["value1"]), 667 | "Value2": "{0}".format(parsed["value2"]), 668 | "Value3": "{0}".format(parsed["value3"]), 669 | } 670 | 671 | urls = [] 672 | conf_blob = self.params["decoded_config"] 673 | while b"http" in conf_blob: 674 | url = conf_blob[conf_blob.find(b"http") :] 675 | urls.append(url[:url.find(b"\x00")].decode("utf-8")) 676 | conf_blob = url[url.find(b"\x00") :] 677 | 678 | config_rc4_key_hex = self.params["config_key"] 679 | # quitamos el padding de volatility ..:76:a:d9:bf:0:0 -> 76:a:d9:bf 680 | if config_rc4_key_hex[-2:] == b"\x00\x00": 681 | config_rc4_key_hex = config_rc4_key_hex[:-2] 682 | 683 | creds_key = self.params["decoded_magic"][0x8C : 0x8C + RC4_KEYSIZE] 684 | 685 | config = { 686 | "urls": urls, 687 | "malware_zbot": "ZEUS", 688 | "zbot_version": self.zbotversion, 689 | "process_name": utility.array_to_string(task.ImageFileName), 690 | "process_id": str(task.UniqueProcessId), 691 | "process_address": str(vad_start), 692 | "computer_identifier": parsed["guid"], 693 | "mutant_key": str(parsed["guid_xor_key"]), 694 | "xor_key": str(parsed["xorkey"]), 695 | "registry": registry_dict, 696 | "executable": parsed["exefile"], 697 | "data_file": parsed["datfile"], 698 | "creds_key": binascii.hexlify(creds_key).decode("utf-8"), 699 | "config_rc4_keystream_plaintext": binascii.hexlify(config_rc4_key_hex).decode("utf-8"), 700 | } 701 | 702 | return config 703 | 704 | def render_text(self, outfd, config): 705 | """Render the plugin's default text output""" 706 | 707 | # Check for data 708 | if config: 709 | # Get a magic object from the buffer 710 | outfd.write("*" * 50 + "\n") 711 | outfd.write("{0:<30} : {1}\n".format("ZBot", self.zbot + self.zbotversion)) 712 | # outfd.write("{0:<30} : {1}\n".format("Process", utility.array_to_string(task.ImageFileName))) 713 | # outfd.write("{0:<30} : {1}\n".format("Pid", task.UniqueProcessId)) 714 | # outfd.write("{0:<30} : {1}\n".format("Address", vad_start)) 715 | 716 | for i, url in enumerate(config["urls"]): 717 | outfd.write("{0:<30} : {1}\n".format("URL {0}".format(i), url)) 718 | 719 | outfd.write("{0:<30} : {1}\n".format("Identifier", config["guid"])) 720 | outfd.write("{0:<30} : {1}\n".format("Mutant key", config["guid_xor_key"])) 721 | outfd.write("{0:<30} : {1}\n".format("XOR key", config["xorkey"])) 722 | outfd.write( 723 | "{0:<30} : {1}\n".format( 724 | "Registry", 725 | "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\{0}".format(config["keyname"]), 726 | ) 727 | ) 728 | outfd.write("{0:<30} : {1}\n".format(" Value 1", config["value1"])) 729 | outfd.write("{0:<30} : {1}\n".format(" Value 2", config["value2"])) 730 | outfd.write("{0:<30} : {1}\n".format(" Value 3", config["value3"])) 731 | outfd.write("{0:<30} : {1}\n".format("Executable", config["exefile"])) 732 | outfd.write("{0:<30} : {1}\n".format("Data file", config["datfile"])) 733 | """ 734 | outfd.write( 735 | "{0:<30} : \n{1}\n".format( 736 | "Config RC4 key", 737 | "\n".join( 738 | [ 739 | "{0:#010x} {1:<48} {2}".format(vad.Start + o, h, "".join(c)) 740 | for o, h, c in utils.Hexdump(params["config_key"]) 741 | ] 742 | ), 743 | ) 744 | ) 745 | 746 | outfd.write( 747 | "{0:<30} : \n{1}\n".format( 748 | "Credential RC4 key", 749 | "\n".join( 750 | ["{0:#010x} {1:<48} {2}".format(vad.Start + o, h, "".join(c)) for o, h, c in utils.Hexdump(config["creds_key"])] 751 | ), 752 | ) 753 | ) 754 | """ 755 | 756 | 757 | class Citadel(ZbotCommon): 758 | """ Scanner for Citadel version """ 759 | 760 | def __init__(self, config, context): 761 | self.zbot = "CITADEL" 762 | self.zbotversion = " 1.3.5.1" 763 | self.magic_struct = "" 764 | self.config = config 765 | self.context = context 766 | 767 | self.signatures = { 768 | "namespace1": r"rule z1 {strings: $a = {8B EC 83 EC 0C 8A 82 ?? ?? ?? ?? 88 45 FE 8A 82 01 01 00 00 88 45 FD 8A 82 02 01 00 00 B9 ?? ?? ?? ?? 88 45 FF E8 ?? ?? ?? ??} condition: $a}", 769 | "namespace2": r"rule z2 {strings: $a = {56 BA ?? ?? 00 00 52 68 ?? ?? ?? ?? 50 E8 ?? ?? ?? ?? 8B 0D ?? ?? ?? ?? 03 0D ?? ?? ?? ?? 8B F2 2B C8} condition: $a}", 770 | "namespace3": r"rule z3 {strings: $a = {68 ?? ?? 00 00 8D 85 ?? ?? ?? ?? 50 8D 85 ?? ?? ?? ?? 50 E8 ?? ?? ?? ?? B8 ?? ?? 00 00 50 68 ?? ?? ?? ??} condition: $a}", 771 | "namespace4": r"rule z4 {strings: $a = {68 ?? ?? 00 00 8D 84 24 ?? ?? ?? ?? 50 8D 44 24 ?? 50 E8 ?? ?? ?? ?? B8 ?? ?? 00 00 50 68 ?? ?? ?? ??} condition: $a}", 772 | "namespace5": r"rule z5 {strings: $a = {81 30 ?? ?? ?? ?? 0F B6 50 03 0F B6 78 02 81 70 04 ?? ?? ?? ?? 81 70 08 ?? ?? ?? ?? 81 70 0c ?? ?? ?? ?? C1 E2 08 0B D7} condition: $a}", 773 | "namespace6": r"rule z6 {strings: $a = {33 F6 C7 45 ?? ?? ?? ?? ?? 5B 8A 4C 3D ?? 8A D1 80 E2 07 C0 E9 03 47 83 FF 04} condition: $a}", 774 | } 775 | """ 776 | botnet offset: 777 | EB 11 jmp short loc_3C9FDBA 778 | 8D 85 48 FA FF FF lea eax, [ebp+var_5B8] 779 | E8 FD FD FF FF call XorDecryptionIntoEAX ; No APIs 780 | 8D 8D 64 FD FF FF lea ecx, [ebp+var_29C] 781 | 8B 55 08 mov edx, [ebp+arg_0] 782 | 83 C8 FF or eax, 0FFFFFFFFh 783 | E8 86 16 01 00 call CopyUnicodeStringECX2EDX ; No APIs 784 | """ 785 | self.CITADEL_GET_BOTNET_PATTERNS = [ 786 | re.compile( 787 | br".*\xeb.\x8d\x85(....)\xe8....\x8d\x8d(....)\x8b.\x08\x83\xc8\xff\xe8.*", 788 | re.DOTALL, 789 | ) 790 | ] 791 | 792 | def rc4_init_cit(self, key, magicKey): # noqa: C901 793 | """ Initialize the RC4 keystate """ 794 | 795 | hash = [] 796 | box = [] 797 | keyLength = len(key) 798 | magicKeyLen = len(magicKey) 799 | 800 | for i in range(0, 256): 801 | hash.append(key[i % keyLength]) 802 | box.append(i) 803 | 804 | y = 0 805 | for i in range(0, 256): 806 | y = (y + box[i] + hash[i]) % 256 807 | tmp = box[i] 808 | box[i] = box[y] 809 | box[y] = tmp 810 | 811 | y = 0 812 | for i in range(0, 256): 813 | magicKeyPart1 = magicKey[y] & 0x07 814 | magicKeyPart2 = magicKey[y] >> 0x03 815 | y += 1 816 | if y == magicKeyLen: 817 | y = 0 818 | 819 | if magicKeyPart1 == 0: 820 | box[i] = ~box[i] 821 | elif magicKeyPart1 == 1: 822 | box[i] ^= magicKeyPart2 823 | elif magicKeyPart1 == 2: 824 | box[i] += magicKeyPart2 825 | elif magicKeyPart1 == 3: 826 | box[i] -= magicKeyPart2 827 | elif magicKeyPart1 == 4: 828 | box[i] = box[i] >> (magicKeyPart2 % 8) | (box[i] << (8 - (magicKeyPart2 % 8))) 829 | elif magicKeyPart1 == 5: 830 | box[i] = box[i] << (magicKeyPart2 % 8) | (box[i] >> (8 - (magicKeyPart2 % 8))) 831 | elif magicKeyPart1 == 6: 832 | box[i] += 1 833 | elif magicKeyPart1 == 7: 834 | box[i] -= 1 835 | 836 | box[i] = box[i] & 0xFF 837 | 838 | return bytes([c for c in box]) 839 | 840 | def get_urls(self, base_config, data): 841 | 842 | urls = [] 843 | 844 | """ 845 | 8D 84 24 50 01 00 00 lea eax, [esp+668h+var_518] 846 | C6 44 24 12 00 mov [esp+668h+var_656], 0 847 | C6 44 24 13 01 mov [esp+668h+var_655], 1 848 | E8 2D 95 00 00 call XorDecryptionIntoEAX ; No APIs 849 | 8B 9C 24 58 05 00 00 mov ebx, [esp+668h+var_110] 850 | 8D 84 24 A8 03 00 00 lea eax, [esp+668h+var_2C0] ; url1 851 | 89 44 24 38 mov [esp+668h+var_630], eax 852 | C1 EB 0C shr ebx, 0Ch 853 | 8D 84 24 8B 01 00 00 lea eax, [esp+668h+var_4DD] ; url2 854 | 83 E3 01 and ebx, 1 855 | 89 44 24 3C mov [esp+668h+var_62C], eax 856 | 8D 84 24 CD 05 00 00 lea eax, [esp+668h+var_9B] ; url3 857 | """ 858 | URL_SEARCH_PATTERNS = [ 859 | re.compile( 860 | br".*\x8d\x84\x24(....)\xc6\x44\x24.\x00\xc6\x44\x24.\x01\xe8....\x8b.\x24....\x8d.\x24(....).{2,10}\x8d\x84\x24(....).{2,10}\x8d\x84\x24(....).*", 861 | re.DOTALL, 862 | ) 863 | ] 864 | 865 | for pattern in URL_SEARCH_PATTERNS: 866 | m = re.match(pattern, data) 867 | if m: 868 | base = struct.unpack("I", m.group(1))[0] 869 | for x in range(2, 5): 870 | offset = struct.unpack("I", m.group(x))[0] - base 871 | url = self.get_string_from_data(base_config, offset=offset) 872 | log.debug("got url: %s" % url) 873 | if url != "" and url not in urls: 874 | urls.append(url.split("\x00")[0]) 875 | 876 | # blunt tool way in case something broke 877 | while b"http" in base_config: 878 | url = base_config[base_config.find(b"http") :] 879 | url_trim = url[: url.find(b"\x00")].decode("utf-8") 880 | log.debug("found through dumb way: %s" % url_trim) 881 | if url_trim not in urls: 882 | urls.append(url_trim) 883 | base_config = url[url.find(b"\x00") :] 884 | 885 | return list(filter(None, urls)) 886 | 887 | def get_string_from_data(self, data, offset=0, widechar=False): 888 | 889 | out = "" 890 | count = offset 891 | while count < len(data): 892 | char = data[count] 893 | if char == 0: 894 | break 895 | out += chr(char) 896 | if widechar: 897 | count += 2 898 | else: 899 | count += 1 900 | 901 | return out 902 | 903 | def search_botnet(self, base_config, data): 904 | 905 | botnet = "" 906 | for pattern in self.CITADEL_GET_BOTNET_PATTERNS: 907 | m = re.match(pattern, data) 908 | if m: 909 | offset = struct.unpack("I", m.group(2))[0] - struct.unpack("I", m.group(1))[0] 910 | log.debug("botnet offset: %x" % offset) 911 | botnet = "" 912 | count = 0 913 | while count < 20: # BOTNET_MAX_CHARS - 20 914 | char = base_config[offset + count] 915 | if char == 0: 916 | break 917 | botnet += chr(char) 918 | count += 2 # widechar 919 | log.debug("found botnet: %s" % botnet) 920 | 921 | return botnet 922 | 923 | def calculate(self, task): # noqa: C901 924 | 925 | p = task 926 | rules = yara.compile(sources=self.signatures) 927 | 928 | try: 929 | proc_layer_name = task.add_process_layer() 930 | except exceptions.InvalidAddressException: 931 | return 932 | 933 | proc_layer = self.context.layers[proc_layer_name] 934 | 935 | for vad_start, vad_len in vadyarascan.VadYaraScan.get_vad_maps(task): 936 | 937 | vad, vad_start, vad_end = ZBOTScan.get_vad(task, vad_start) 938 | 939 | start = vad.StartingVpn << 12 940 | # check for the signature with YARA, both hits must be present 941 | if not vad_start or not vad_end: 942 | continue 943 | 944 | if vad_end - vad_start == 0xFFFF or vad_end - vad_start >= 1000000000 : 945 | continue 946 | 947 | data = ZBOTScan.carve_data(vad_start, vad_end, proc_layer) 948 | matches = rules.match(data=data) 949 | if not matches or len(matches) != 5: 950 | continue 951 | 952 | if not data.startswith(b"MZ"): 953 | continue 954 | 955 | 956 | try: 957 | # There must be more than 2 sections 958 | pe = pefile.PE(data=data, fast_load=True) 959 | if len(pe.sections) < 2: 960 | continue 961 | except Exception as e: 962 | print(e) 963 | continue 964 | 965 | last_sec = pe.sections[-1] 966 | last_sec_data = proc_layer.read((last_sec.VirtualAddress + start), last_sec.Misc_VirtualSize) 967 | if len(last_sec_data) == 0: 968 | continue 969 | 970 | # contains C2 URL, RC4 key for decoding local.ds and the magic buffer 971 | decoded_config = "" 972 | # contains hw lock info, the user.ds RC4 key, and XOR key 973 | encoded_magic = "" 974 | # contains BO_LOGIN_KEY 975 | # contains de AES XOR key 976 | aes_xor_key = "" 977 | # Length of the Zeus Magic Object 978 | zeus_magic = "" 979 | # contains Salt RC4 Init key 980 | salt_rc4_initKey = "" 981 | 982 | for match in matches: 983 | sigaddr = match.strings[0][0] + start 984 | log.debug("Found {0} at {1:#x}".format(match.rule, sigaddr)) 985 | if match.rule == "z1": 986 | addr = struct.unpack("=I", proc_layer.read(sigaddr+30, 4))[0] 987 | loginKey = proc_layer.read(addr, 0x20) 988 | elif match.rule == "z2": 989 | address = struct.unpack("=I", proc_layer.read(sigaddr + 8, 0x4))[0] 990 | size = struct.unpack("=I", proc_layer.read(sigaddr + 2, 0x4))[0] 991 | encoded_config = proc_layer.read(address, size) 992 | decoded_config = self.decode_config(encoded_config, last_sec_data) 993 | elif match.rule == "z3": 994 | zeus_magic = proc_layer.read(sigaddr + 25, 0x4) 995 | (zeus_magic,) = struct.unpack("=I", zeus_magic[0:4]) 996 | addr = struct.unpack("=I", proc_layer.read(sigaddr+31, 4))[0] 997 | encoded_magic = proc_layer.read(addr, zeus_magic) 998 | elif match.rule == "z4": 999 | zeus_magic = proc_layer.read(sigaddr + 24, 0x4) 1000 | (zeus_magic,) = struct.unpack("=I", zeus_magic[0:4]) 1001 | addr = struct.unpack("=I", proc_layer.read(sigaddr+30, 4))[0] 1002 | encoded_magic = proc_layer.read(sigaddr + 30, zeus_magic,) 1003 | elif match.rule == "z5": 1004 | aes_xor_key = proc_layer.read(sigaddr + 2, 0x4) 1005 | aes_xor_key += proc_layer.read(sigaddr + 17, 0x4) 1006 | aes_xor_key += proc_layer.read(sigaddr + 24, 0x4) 1007 | aes_xor_key += proc_layer.read(sigaddr + 31, 0x4) 1008 | elif match.rule == "z6": 1009 | salt_rc4_initKey = proc_layer.read(sigaddr + 5, 0x4) 1010 | salt_rc4_initKey_hex = self.get_only_hex(salt_rc4_initKey).upper() 1011 | 1012 | if not decoded_config or not encoded_magic: 1013 | continue 1014 | 1015 | offset = 0 1016 | 1017 | decoded_magic = "" 1018 | config_key = "" 1019 | aes_key = "" 1020 | rc4_comKey = "" 1021 | 1022 | found = False 1023 | 1024 | while offset < len(decoded_config) - RC4_KEYSIZE: 1025 | 1026 | config_key = decoded_config[offset : offset + RC4_KEYSIZE] 1027 | decoded_magic = self.rc4(config_key, encoded_magic, loginKey) 1028 | # when the first four bytes of the decoded magic buffer equal the size 1029 | # of the magic buffer, then we've found a winning RC4 key 1030 | (struct_size,) = struct.unpack("=I", decoded_magic[0:4]) 1031 | if struct_size in ZEUS_STURCTURE_size and ZEUS_STURCTURE_size[struct_size].startswith("_CITADEL"): 1032 | if ZEUS_STURCTURE_size[struct_size] == "_CITADEL1345_CONFIG": 1033 | self.magic_struct = "_CITADEL1345_CONFIG" 1034 | self.zbotversion = " 1.3.4.5" 1035 | elif ZEUS_STURCTURE_size[struct_size] == "_CITADEL1351_CONFIG": 1036 | self.magic_struct = "_CITADEL1351_CONFIG" 1037 | self.zbotversion = " 1.3.5.1" 1038 | found = True 1039 | if found: 1040 | aes_key = self.rc4(config_key, hashlib.md5(loginKey).digest(), loginKey) 1041 | rc4_comKey = self.rc4_init_cit(aes_key, salt_rc4_initKey) 1042 | break 1043 | 1044 | offset += 1 1045 | 1046 | if not found: 1047 | log.debug("Error, cannot decode magic") 1048 | continue 1049 | 1050 | # grab the URLs from the decoded buffer 1051 | urls = [] 1052 | urls = self.get_urls(decoded_config, data) 1053 | botnet = self.search_botnet(decoded_config, data) 1054 | clean_urls = [] 1055 | config_file_paths = [] 1056 | for u in urls: 1057 | f_path = "" 1058 | clean_u = u 1059 | if "|" in u: 1060 | clean_u = u[: u.find("|")] 1061 | f_path = u[u.find("|") + 1 :] 1062 | clean_urls.append(clean_u) 1063 | config_file_paths.append(f_path) 1064 | 1065 | parsed = parsed_struct(decoded_config, decoded_magic, self.magic_struct) 1066 | 1067 | registry_dict = { 1068 | "key_path": "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\%s" % "{0}".format(parsed["keyname"]), 1069 | # .v() Do the actual reading and decoding of this member 1070 | "Value1": "{0}".format(parsed["value1"]), 1071 | "Value2": "{0}".format(parsed["value2"]), 1072 | "Value3": "{0}".format(parsed["value3"]), 1073 | } 1074 | config = { 1075 | "urls": clean_urls, 1076 | "botnet": botnet, 1077 | "malware_zbot": "CITADEL", 1078 | "zbot_version": self.zbotversion, 1079 | "process_name": utility.array_to_string(task.ImageFileName), 1080 | "process_id": str(p.UniqueProcessId), 1081 | "process_address": str(start), 1082 | "computer_identifier": parsed["guid"], 1083 | "mutant_key": str(parsed["guid_xor_key"]), 1084 | "xor_key": str(parsed["xorkey"]), 1085 | "config_rc4_keystream_plaintext": binascii.hexlify(config_key[:0x100]).decode("utf-8"), 1086 | "comm_rc4_key_plaintext": binascii.hexlify(rc4_comKey).decode("utf-8"), 1087 | "registry": registry_dict, 1088 | "executable": parsed["exefile"], 1089 | "login_key": loginKey.decode("utf-8").upper(), 1090 | "aes_key": binascii.hexlify(aes_key).decode("utf-8").upper(), 1091 | "aes_xor_key": binascii.hexlify(aes_xor_key).decode("utf-8").upper(), 1092 | "config_file_paths": config_file_paths, 1093 | "salt_rc4_initKey_hex": loginKey.decode("utf-8").upper(), 1094 | } 1095 | 1096 | return config 1097 | 1098 | 1099 | class ICEIX(ZbotCommon): 1100 | """ Scanner for ICE IX """ 1101 | 1102 | def __init__(self, config, context): 1103 | self.zbot = "ICEIX" 1104 | self.zbotversion = "" 1105 | self.config = config 1106 | self.context = context 1107 | 1108 | self.signatures = { 1109 | "namespace1": r"rule z1 {strings: $a = {56 BA ?? ?? 00 00 52 68 ?? ?? ?? ?? 50 E8 ?? ?? ?? ?? 8B 35 ?? ?? ?? ?? 8B 0D ?? ?? ?? ??} condition: $a}", 1110 | "namespace5": r"rule z5 {strings: $a = {56 BA ?? ?? 00 00 52 68 ?? ?? ?? ?? 50 E8 ?? ?? ?? ?? 8B 0D ?? ?? ?? ?? 03 0D ?? ?? ?? ??} condition: $a}", 1111 | "namespace2": r"rule z2 {strings: $a = {55 8B EC 51 A1 ?? ?? ?? ?? 8B 0D ?? ?? ?? ?? 56 8D 34 01 A1 ?? ?? ?? ?? 8B 0D ?? ?? ?? ??} condition: $a}", 1112 | "namespace3": r"rule z3 {strings: $a = {68 02 01 00 00 8D 84 24 ?? ?? ?? ?? 50 8D 44 24 ?? 50 E8 ?? ?? ?? ?? B8 E6 01 00 00 50 68 ?? ?? ?? ??} condition: $a}", 1113 | "namespace4": r"rule z4 {strings: $a = {68 02 01 00 00 8D 85 ?? ?? ?? ?? 50 8D 85 ?? ?? ?? ?? 50 E8 ?? ?? ?? ?? B8 E6 01 00 00 50 68 ?? ?? ?? ??} condition: $a}", 1114 | } 1115 | 1116 | self.magic_struct = "_ZEUS2_CONFIG" 1117 | self.magic_struct_size = ZEUS_STURCTURE[self.magic_struct] 1118 | 1119 | def rc4(self, key, data, offset1=3, offset2=7): 1120 | """ Perform a basic RC4 operation """ 1121 | state = list(range(256)) 1122 | x = 0 1123 | y = 0 1124 | 1125 | for i in list(range(256)): 1126 | state[i] = key[i] 1127 | 1128 | out = [None] * len(data) 1129 | 1130 | for i in range(len(data)): 1131 | x = (x + offset1) & 0xFF 1132 | y = (state[x] + y + offset2) & 0xFF 1133 | state[x], state[y] = state[y], state[x] 1134 | out[i] = (data[i] ^ state[(state[x] + state[y]) & 0xFF]) 1135 | 1136 | return bytes(out) 1137 | 1138 | def calculate(self, p, start, data, ps_ad): # noqa: C901 1139 | 1140 | # check for the signature with YARA, both hits must be present 1141 | rules = yara.compile(sources=self.signatures) 1142 | matches = rules.match(data=data) 1143 | try: 1144 | # There must be more than 2 sections 1145 | pe = pefile.PE(data=data, fast_load=True) 1146 | except Exception as e: 1147 | print(e) 1148 | return 1149 | 1150 | # Get the last PE section's data 1151 | last_sec = pe.sections[-1] 1152 | last_sec_data = ps_ad.read((last_sec.VirtualAddress + start), last_sec.Misc_VirtualSize) 1153 | if len(last_sec_data) == 0: 1154 | log.debug("Last section is empty") 1155 | return 1156 | 1157 | # contains C2 URL, RC4 key for decoding local.ds and the magic buffer 1158 | decoded_config = "" 1159 | # contains hw lock info, the user.ds RC4 key, and XOR key 1160 | encoded_magic = "" 1161 | 1162 | for match in matches: 1163 | sigaddr = match.strings[0][0] + start 1164 | log.debug("Found {0} at {1:#x}".format(match.rule, sigaddr)) 1165 | 1166 | if match.rule == "z1": 1167 | address = struct.unpack("=I", ps_ad.read(sigaddr + 8, 0x4))[0] 1168 | size = struct.unpack("=I", ps_ad.read(sigaddr + 2, 0x4))[0] 1169 | encoded_config = ps_ad.read(address, size) 1170 | decoded_config = self.decode_config(encoded_config, last_sec_data) 1171 | elif match.rule == "z2": 1172 | config_ptr = struct.unpack("=I", ps_ad.read(sigaddr + 26, 0x4))[0] 1173 | config_ptr = struct.unpack("=I", ps_ad.read(config_ptr, 0x4))[0] 1174 | encoded_config = ps_ad.read(config_ptr, 0x3C8) 1175 | decoded_config = self.rc4(self.rc4_init(encoded_config), last_sec_data[2:]) 1176 | elif match.rule == "z5": 1177 | address = struct.unpack("=I", ps_ad.read(sigaddr + 8, 0x4))[0] 1178 | size = struct.unpack("=I", ps_ad.read(sigaddr + 2, 0x4))[0] 1179 | encoded_config = ps_ad.read(address, size) 1180 | decoded_config = self.decode_config(encoded_config, last_sec_data) 1181 | elif match.rule == "z3": 1182 | address = struct.unpack("=I", ps_ad.read(sigaddr + 30, 0x4))[0] 1183 | encoded_magic = ps_ad.read(address, ZEUS_STURCTURE[self.magic_struct]) 1184 | elif match.rule == "z4": 1185 | address = struct.unpack("=I", ps_ad.read(sigaddr + 31, 0x4))[0] 1186 | encoded_magic = ps_ad.read(address, ZEUS_STURCTURE[self.magic_struct]) 1187 | 1188 | if not decoded_config or not encoded_magic: 1189 | log.debug("ICEIX not decoded_config or not encoded_magic") 1190 | return None 1191 | 1192 | offset = 0 1193 | 1194 | decoded_magic = "" 1195 | config_key = "" 1196 | 1197 | found = False 1198 | while offset < len(decoded_config) - RC4_KEYSIZE: 1199 | 1200 | config_key = decoded_config[offset : offset + RC4_KEYSIZE] 1201 | decoded_magic = self.rc4(config_key, encoded_magic) 1202 | # when the first four bytes of the decoded magic buffer equal the size 1203 | # of the magic buffer, then we've found a winning RC4 key 1204 | (struct_size,) = struct.unpack("=I", decoded_magic[0:4]) 1205 | 1206 | if struct_size == ZEUS_STURCTURE[self.magic_struct]: 1207 | found = True 1208 | break 1209 | 1210 | offset += 1 1211 | 1212 | if not found: 1213 | log.debug("Error, cannot decode magic") 1214 | return None 1215 | 1216 | # grab the URL from the decoded buffer 1217 | url = decoded_config[decoded_config.find(b"http") :] 1218 | url = url[:url.find(b"\x00")].decode("utf-8") 1219 | 1220 | # use list for url (sames as others families) 1221 | urls = [url] 1222 | creds_key = decoded_magic[0x8C : 0x8C + RC4_KEYSIZE] 1223 | # add parsing here 1224 | 1225 | parsed = parsed_struct(decoded_config, decoded_magic, self.magic_struct) 1226 | parsed["urls"] = urls 1227 | parsed["guid2"] = binascii.hexlify(parsed["guid2"]).decode("utf-8") 1228 | parsed["rc4key"] = binascii.hexlify(parsed["rc4key"]).decode("utf-8") 1229 | 1230 | 1231 | registry_dict = { 1232 | "key_path": "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\%s" % "{0}".format(parsed["keyname"]), 1233 | # .v() Do the actual reading and decoding of this member 1234 | "Value1": "{0}".format(parsed["value1"]), 1235 | "Value2": "{0}".format(parsed["value2"]), 1236 | "Value3": "{0}".format(parsed["value3"]), 1237 | } 1238 | 1239 | config = { 1240 | "urls": urls, 1241 | "malware_zbot": self.zbot, 1242 | "zbot_version": self.zbotversion, 1243 | "process_name": utility.array_to_string(p.ImageFileName), 1244 | "process_id": str(p.UniqueProcessId), 1245 | "process_address": str(start), 1246 | "computer_identifier": parsed["guid"], 1247 | "mutant_key": str(parsed["guid_xor_key"]), 1248 | "xor_key": str(parsed["xorkey"]), 1249 | "registry": registry_dict, 1250 | "executable": parsed["exefile"], 1251 | "data_file": parsed["datfile"], 1252 | "urls": urls, 1253 | "config_rc4_keystream_plaintext": binascii.hexlify(config_key[:0x100]).decode("utf-8"), 1254 | "cred_rc4_key_plaintext": binascii.hexlify(creds_key[:0x100]).decode("utf-8"), 1255 | } 1256 | return p, start, config 1257 | 1258 | -------------------------------------------------------------------------------- /Windows/choco.bat: -------------------------------------------------------------------------------- 1 | rem Chocolatey now requires PowerShell v3 (or higher) and .NET 4.0 (or higher) due to recent upgrades to TLS 1.2. 2 | rem Please ensure .NET 4+ and PowerShell v3+ are installed prior to attempting FLARE VM installation. 3 | rem Below are links to download .NET 4.5 and WMF 5.1 (PowerShell 5.1). 4 | rem .NET 4.5 https://www.microsoft.com/en-us/download/details.aspx?id=30653 5 | rem WMF 5.1 https://www.microsoft.com/en-us/download/details.aspx?id=54616 6 | 7 | 8 | powershell -Command "if ($PSVersionTable.PSVersion.Major -ge 5 -and $PSVersionTable.PSVersion.Minor -ge 1 ){Set-ExecutionPolicy Bypass -Scope Process -Force; iwr https://community.chocolatey.org/install.ps1 -UseBasicParsing | iex } else {Write-Output "'Chocolatey now requires PowerShell v3 (or higher) and .NET 4.0 (or higher) due to recent upgrades to TLS 1.2. Please ensure .NET 4+ and PowerShell v3+ are installed prior to attempting FLARE VM installation. `nBelow are links to download .NET 4.5 and WMF 5.1 (PowerShell 5.1). .NET 4.5 https://www.microsoft.com/en-us/download/details.aspx?id=30653 WMF 5.1 https://www.microsoft.com/en-us/download/details.aspx?id=54616'"}" 9 | choco upgrade chocolatey 10 | choco install -y dotnetfx dotnet4.7.2 dotnet vcredist-all wixtoolset msxml4.sp3 msxml6.sp1 11 | 12 | pip3 install pillow pywintrace 13 | -------------------------------------------------------------------------------- /Windows/disable_win7noise.bat: -------------------------------------------------------------------------------- 1 | REM NTP 2 | reg add "HKLM\SYSTEM\CurrentControlSet\Services\W32Time\Parameters" /v LocalNTP /t REG_DWORD /d 0 /f 3 | REM HELP 4 | REM http://www.windows-commandline.com/start-stop-service-command-line/ 5 | REM disable UAC 6 | reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v EnableLUA /t REG_DWORD /d 0 /f 7 | REM disable Windows defender 8 | sc config WinDefend start= disabled 9 | REM disable windows update 10 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v AUOptions /t REG_DWORD /d 1 /f 11 | REM disable aero 12 | net stop uxsms 13 | REM disable the firewall 14 | netsh firewall set opmode mode=DISABLE 15 | REM disable IPv6 16 | netsh interface teredo set state disabled 17 | netsh interface ipv6 6to4 set state state=disabled undoonstop=disabled 18 | netsh interface ipv6 isatap set state state=disabled 19 | REM disable active probing 20 | reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NlaSvc\Parameters\Internet" /v EnableActiveProbing /t REG_DWORD /d 0 /f 21 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\NetworkConnectivityStatusIndicator" /v EnableActiveProbing /t REG_DWORD /d 0 /f 22 | REM disable passive probing 23 | reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NlaSvc\Parameters\Internet" /v PassivePollPeriod /t REG_DWORD /d 0 /f 24 | REM disable SSDP 25 | sc config SSDPSRV start= disabled 26 | net stop SSDPSRV 27 | REM disable computer browsing 28 | sc stop Browser 29 | sc config Browser start= disabled 30 | REM disable WinHTTP Web Proxy Auto-Discovery 31 | reg add "HKLM\SYSTEM\CurrentControlSet\services\WinHttpAutoProxySvc" /v Start /t REG_DWORD /d 4 /f 32 | REM disable Function Discovery Resource Publication service 33 | reg add "HKLM\SYSTEM\CurrentControlSet\services\FDResPup" /v Start /t REG_DWORD /d 4 /f 34 | REM IE blank page 35 | reg add "HKCU\Software\Microsoft\Internet Explorer\Main" /V "Start Page" /D "" /F 36 | REM disable IExplorer Proxy 37 | reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyEnable /t REG_DWORD /d 00000000 /f 38 | REM disable netbios in TCP/IP 39 | wmic nicconfig where index=8 call SetTcpipNetbios 2 40 | REM disable netbios service 41 | reg add "HKLM\SYSTEM\CurrentControlSet\services\Lmhosts" /v Start /t REG_DWORD /d 4 /f 42 | REM disable LLMNR 43 | reg add "HKLM\Software\policies\Microsoft\Windows NT\DNSClient" /v "EnableMulticast" /t REG_DWORD /d 0 /f 44 | REMdisable SQM 45 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\FlexGo\FGNotify\Prechecks" /v Sqm /t REG_DWORD /d 00000002 /f 46 | REM Disable cert check 47 | reg add "HKLM\SYSTEM\CurrentControlSet\Services\HTTP\Parameters\SslBindingInfo" /v DefaultSslCertCheckMode /t REG_DWORD /d 1 /f 48 | REM disable ClickToRunSvc 49 | sc stop "ClickToRunSvc" 50 | sc config "ClickToRunSvc" start= disabled 51 | 52 | REM dr.watson 53 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug" /v AUTO /t REG_SZ /d 0 /f 54 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug" /v AutoExclusionList /t REG_SZ /d 0 /f 55 | 56 | REM curtain 57 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging\ModuleNames" /v * /t REG_SZ /d * /f /reg:64 58 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging" /v EnableScriptBlockLogging /t REG_DWORD /d 00000001 /f /reg:64 59 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" /v EnableTranscripting /t REG_DWORD /d 00000001 /f /reg:64 60 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" /v OutputDirectory /t REG_SZ /d C:\PSTranscipts /f /reg:64 61 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription" /v EnableInvocationHeader /t REG_DWORD /d 00000001 /f /reg:64 62 | 63 | REM disable windows defender 64 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows Defender" /v DisableAntiSpyware /t REG_DWORD /d 0 /f 65 | 66 | REM https://superuser.com/questions/972501/how-to-stop-microsoft-from-gathering-telemetry-data-from-windows-7-8-and-8-1 67 | sc stop DiagTrack 68 | sc stop dmwappushservice 69 | sc delete DiagTrack 70 | sc delete dmwappushservice 71 | echo "" > C:\ProgramData\Microsoft\Diagnosis\ETLLogs\AutoLogger\AutoLogger-Diagtrack-Listener.etl 72 | reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\DataCollection" /v AllowTelemetry /t REG_DWORD /d 0 /f 73 | reg add "HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\WMI\AutoLogger" /v AutoLogger-Diagtrack-Listener /t DWORD /d 0 /f 74 | 75 | REM Win10/11 telemetry 76 | sc config DiagTrack start= disabled 77 | sc config dmwappushservice start= disabled 78 | 79 | REM schtasks 80 | schtasks.exe /Change /TN "\Microsoft\Office\Office Automatic Updates 2.0" /Disable /ru "" 81 | schtasks.exe /Change /TN "\Microsoft\Office\Office ClickToRun Service Monitor" /Disable /ru "" 82 | schtasks.exe /Change /TN "\Microsoft\Office\Office Feature Updates" /Disable /ru "" 83 | schtasks.exe /Change /TN "\Microsoft\Office\Office Feature Updates Logon" /Disable /ru "" 84 | schtasks.exe /Change /TN "\Microsoft\Office\OfficeTelemetryAgentFallBack2016" /Disable /ru "" 85 | schtasks.exe /Change /TN "\Microsoft\Office\OfficeTelemetryAgentLogOn2016" /Disable /ru "" 86 | 87 | schtasks.exe /Change /TN "\Microsoft\Windows\Application Experience\AitAgent" /Disable /ru "" 88 | schtasks.exe /Change /TN "\Microsoft\Windows\Application Experience\Microsoft Compatibility Appraiser" /Disable /ru "" 89 | schtasks.exe /Change /TN "\Microsoft\Windows\Application Experience\ProgramData\Updater" /Disable /ru "" 90 | schtasks.exe /Change /TN "\Microsoft\Windows\Customer Experience Improvement Program\KernelCeipTask" /Disable /ru "" 91 | schtasks.exe /Change /TN "\Microsoft\Windows\Customer Experience Improvement Program\UsbCeip" /Disable /ru "" 92 | schtasks.exe /Change /TN "\Microsoft\Windows\Customer Experience Improvement Program\Consolidator" /Disable /ru "" 93 | schtasks.exe /Change /TN "\Microsoft\Windows\Autochk\Proxy" /Disable /ru "" 94 | schtasks.exe /Change /TN "\Microsoft\Windows\DiskDiagnostic\Microsoft-Windows-DiskDiagnosticDataCollector" /Disable /ru "" 95 | schtasks.exe /Change /TN "\Microsoft\Windows\Windows Media Sharing\UpdateLibrary" /Disable /ru "" 96 | 97 | schtasks.exe /Change /TN "\CCleaner Update" /Disable /ru "" 98 | schtasks.exe /Change /TN "\CCleaner UpdateSkipUAC" /Disable /ru "" 99 | schtasks.exe /Change /TN "\Microsoft\Windows\Windows Media Sharing\UpdateLibrary" /Disable /ru "" 100 | schtasks.exe /Change /TN "\Microsoft\Windows\Windows Media Sharing\UpdateLibrary" /Disable /ru "" 101 | 102 | 103 | REM Uninstall telemetry updates 104 | wusa /uninstall /kb:3065988 /quiet /norestart 105 | wusa /uninstall /kb:3083325 /quiet /norestart 106 | wusa /uninstall /kb:3083324 /quiet /norestart 107 | wusa /uninstall /kb:2976978 /quiet /norestart 108 | wusa /uninstall /kb:3075853 /quiet /norestart 109 | wusa /uninstall /kb:3065987 /quiet /norestart 110 | wusa /uninstall /kb:3050265 /quiet /norestart 111 | wusa /uninstall /kb:3050267 /quiet /norestart 112 | wusa /uninstall /kb:3075851 /quiet /norestart 113 | wusa /uninstall /kb:2902907 /quiet /norestart 114 | wusa /uninstall /kb:3068708 /quiet /norestart 115 | wusa /uninstall /kb:3022345 /quiet /norestart 116 | wusa /uninstall /kb:2952664 /quiet /norestart 117 | wusa /uninstall /kb:2990214 /quiet /norestart 118 | wusa /uninstall /kb:3035583 /quiet /norestart 119 | wusa /uninstall /kb:971033 /quiet /norestart 120 | wusa /uninstall /kb:3021917 /quiet /norestart 121 | wusa /uninstall /kb:3044374 /quiet /norestart 122 | wusa /uninstall /kb:3046480 /quiet /norestart 123 | wusa /uninstall /kb:3075249 /quiet /norestart 124 | wusa /uninstall /kb:3080149 /quiet /norestart 125 | wusa /uninstall /kb:2977759 /quiet /norestart 126 | wusa /uninstall /kb:3083710 /quiet /norestart 127 | wusa /uninstall /kb:3083711 /quiet /norestart 128 | wusa /uninstall /kb:3112336 /quiet /norestart 129 | wusa /uninstall /kb:3123862 /quiet /norestart 130 | 131 | REM office 2010 132 | reg add "HKEY_CURRENT_USER\Software\Microsoft\Office\Common\Security" /v DisableAllActiveX /t REG_DWORD /d 0 /f 133 | reg add "HKEY_CURRENT_USER\Software\Microsoft\Office\Common\Security" /v UFIControls /t REG_DWORD /d 1 /f 134 | for %%x in (Word Excel PowerPoint Publisher Outlook) do ( 135 | reg add "HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\%%x\Common\General" /v ShownOptIn /t REG_DWORD /d 1 /f 136 | reg add "HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\%%x\Security" /v VBAWarnings /t REG_DWORD /d 1 /f 137 | reg add "HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\%%x\Security" /v AccessVBOM /t REG_DWORD /d 1 /f 138 | reg add "HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\%%x\Security" /v DisableDDEServerLaunch /t REG_DWORD /d 0 /f 139 | reg add "HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\%%x\Security" /v ExtensionHardening /t REG_DWORD /d 0 /f 140 | reg add "HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\%%x\Security" /v ShownOptIn /t REG_DWORD /d 1 /f 141 | reg add "HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\%%x\Security" /v ShownOptIn /t REG_DWORD /d 1 /f 142 | reg add "HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\%%x\Security" /v ShownOptIn /t REG_DWORD /d 1 /f 143 | reg add "HKEY_CURRENT_USER\Software\Policies\Microsoft\Office\14.0\%%x\Security" /v MarkInternalAsUnsafe /t REG_DWORD /d 0 /f 144 | reg add "HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\%%x\Security\ProtectedView" /v DisableAttachmentsInPV /t REG_DWORD /d 1 /f 145 | reg add "HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\%%x\Security\ProtectedView" /v DisableInternetFilesInPV /t REG_DWORD /d 1 /f 146 | reg add "HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\%%x\Security\ProtectedView" /v DisableUnsafeLocationsInPV /t REG_DWORD /d 1 /f 147 | reg add "HKEY_CURRENT_USER\Software\Microsoft\Office\14.0\%%x\Security" /v EnableDEP /t REG_DWORD /d 1 /f 148 | ) 149 | --------------------------------------------------------------------------------