Building your malware analysis lab is a key point before playing with malware. Today, thanks to virtualization, it's convenient to play with snapshots in your lab. Start a new investigation from a clean snapshot and take some more when you reached a critical step. But, if you need to revert to a previous snapshot, all changes applied to the VM are... lost! Therefore, it's important to store important data outside the VM.
If you do malware analysis with Ghidra[1], your work lives in "projects". A project is a ".gpr" file and a ".rep" directory sitting on the disk of your analysis machine. Every renamed function, comment, structure and bookmark you added is stored there. And,as said before, hours of work can be lost if your VM has issues or you restart to a clean snapshot. When you create a new project in Ghidra, it always asks you if you want a "shared" or "non-shared" project:

Ghidra ships with a component that many people ignore: Ghidra Server. It is included in the standard distribution (no extra download, no licence) and turns your local projects into "shared projects" backed by a central repository. Here are some reasons why it's useful to deploy one in yout lab:
From a requirements perspective, the server is lightweight. I'm running Ghidra 12.1.4 in an Ubuntu container on my Proxmox host, but any small Linux VM will do. Recommended specs:
To avoid incompatibility issues, use the same version of Ghida between the server and clients. Keep versions aligned!
Important remarks about networking:
Don't forget that this server will host malicious pieces of code! Treat it as hosting live malware and keep it in your malware analysis lab network! Nothing is executed there, but the repositories contain the raw bytes of every sample you imported. Sounds legit but never expose it to the Internet. Ghidra Server is a Java RMI service.
The basic setup is pretty easy. There is no official container image, but the distribution contains everything needed to run the server as a regular service. First, create a dedicated account, unpack Ghidra and create the repositories directory:
# apt install openjdk-25-jre-headless unzip # useradd -r -m -d /opt/ghidra -s /usr/sbin/nologin ghidra # cd /opt && unzip ghidra_12.1.4_PUBLIC_*.zip # ln -s ghidra_12.1.4_PUBLIC ghidra-current # mkdir -p /opt/ghidra-repos && chown -R ghidra: /opt/ghidra-repos
The configuration is in "server/server.conf". Set the repositories directory, the account used to run the JVM, and the server parameters:
ghidra.repositories.dir=/opt/ghidra-repos
???????wrapper.app.account=ghidra
# Authentication: local Ghidra passwords, expire after 90 days
wrapper.app.parameter.1=-a0
wrapper.app.parameter.2=-e90
# Hostname announced to clients
wrapper.app.parameter.3=-ip
wrapper.app.parameter.4=ghidra.lab.local
# Repositories directory (must be the last parameter)
wrapper.app.parameter.5=${ghidra.repositories.dir}
The main authentication modes are "-a0" (passwords managed by the server), "-a2" (PKI with client certificates) and "-a4" (JAAS, to plug into LDAP or Active Directory). Leave anonymous access disabled. I'm using the local authentication scheme (-a0).
The "-ip" parameter is important: RMI returns this name to the clients, which reconnect to it on ports 13101 and 13102. If clients can't resolve it, they reach the server on the first port and then fail. If you want to bind the service to a single interface (your VPN interface, for example), add -i <address>.
Install the service, check it and create the users:
# /opt/ghidra-current/server/svrInstall # systemctl status ghidraSvr # ss -tlnp | grep -E '1310[0-2]' # cd /opt/ghidra-current/server # ./svrAdmin -add xavier # ./svrAdmin -list -users
New users get the initial password "changeme" (yes, unsecure...) and must change it at first login. The svrAdmin commands are queued and processed by the running server within a few seconds.
Now, you are ready to use your brand now server! Repositories are created from the Ghidra client:
Access for other analysts is managed from the client (Project > Edit Access List) or from the server, once the repository exists:
# ./svrAdmin -grant john +w MalwareLab # +r read-only, +w read/write, +a admin
A shared project still creates a .gpr file and a .rep directory locally! That's expected: they contain the connection settings, a cache and your checked-out copies. Importing a binary does not send it to the server. It remains a private file until you right-click it and select "Add to Version Control...". From then on, the workflow is similar to any source control system:
How to restore your project if your analysis VM has been reverted to a clean snapshot and the local project is gone? To get everything back, create a new Shared Project, connect to the server and select your existing repository (don't create a new one). The project tree is populated with all checked-in programs. If a file is still checked out by the old instance of the VM, right-click it, select View Checkouts and terminate the stale entry.
What the server can't give back:
Before deploying your own server, you can have a look at the ghidra-server project[2] that offers a free server for the RE community. But don't forget that, like any "cloud" service, you'll share your data!
Happy reversing!
[1] https://github.com/nationalsecurityagency/ghidra
[2] https://ghidra-server.org
Xavier Mertens (@xme)
Senior ISC Handler | SANS Principal Instructor | Freelance Consultant
Xameco | PGP Key