███ █████ ░░░ ░░███ ████████ ████ █████ ██████ ░███████ ██████ █████ ████ ░░███░░███ ░░███ ███░░ ███░░███ ░███░░███ ░░░░░███ ░░███ ░███ ░███ ░███ ░███ ░░█████ ░███ ░░░ ░███ ░███ ███████ ░███ ░███ ░███ ░███ ░███ ░░░░███░███ ███ ░███ ░███ ███░░███ ░███ ░███ ████ █████ █████ ██████ ░░██████ ████ █████░░████████ ░░███████ ░░░░ ░░░░░ ░░░░░ ░░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░░░░░ ░░░░░███ ███ ░███ ░░██████ ░░░░░░
I have been procrastinating on the PMAT course for months, and I finally decided to finish it this week somehow. This writeup is for the Backdoor.srvupdat.exe.malz file.
Without further ado, let’s just dive into it.
I’m using IDA for this writeup, but it should be easy enough to follow along with other disassemblers/decompilers that you are familiar with.
AlphaGolang’s categorization script has very nicely come up with folders for us to look into. Here’s a snippet:
One look at https://github.com/kardianos/service and we can immediately figure out that service has very little to do with our actual malware, but a lot to do with how it persists in the system. We can basically ignore all of the github_com_kardianos_service* functions.
service
github_com_kardianos_service*
The main function in golang is usually named main_main in IDA, so here’s a snippet of that:
main_main
srv, srvgosse41sse42ssse3 and gosrv immediately stand out to me as weird, and it’s pretty interesting that it’s passed onto github_com_kardianos_service_New. Thankfully, there’s a simple example on the github page. Here’s the relevant snippet:
srv
srvgosse41sse42ssse3
gosrv
github_com_kardianos_service_New
1 2 3 4 5 6 7 8 9 func main() { svcConfig := &service.Config{ Name: "GoServiceExampleSimple", DisplayName: "Go Service Example", Description: "This is an example Go service.", } prg := &program{} s, err := service.New(prg, svcConfig)
1 2 3 4 5 6 7 8 9
func main() { svcConfig := &service.Config{ Name: "GoServiceExampleSimple", DisplayName: "Go Service Example", Description: "This is an example Go service.", } prg := &program{} s, err := service.New(prg, svcConfig)
Okay, so the last argument is the svcConfig, which is presumably what the service will be called in Task Scheduler. There’s also the main_logger variable, which at first glance looks like it corresponds to the s.Logger(nil) line in the simple example.
svcConfig
main_logger
s.Logger(nil)
If all this is true, there should be three functions named Start, Stop, and run right? As a matter of fact, there are, and they’re called main__program_Start, main__program_Stop, and main__program_run in the screenshots, respectively. So if I’m following the example program given, main__program_Start should simply be just go p.run(), and if you look at the disassembly, that is indeed the case:
Start
Stop
run
main__program_Start
main__program_Stop
main__program_run
go p.run()
So we have shown beyond a shred of doubt that the malware here follows the same basic program structure as the example program for service, which means that p.run() (or rather main__program_run) should have all the juicy details. And it does:
p.run()
The very first line shows a GET request to http://ec2-3-109-20-24-srv3.local/favicon.ico, and then there’s a net.Dial, where the program clearly reads something out of it. If you’ve worked with metasploit or related shells enough, you know that the reverse shell usually connects to a remote IP and then does something with the input that it gets.
GET
http://ec2-3-109-20-24-srv3.local/favicon.ico
net.Dial
metasploit
reverse shell
And there’s the telltale sign that it’s indeed a shell, it executes something based on what I give as input.
So all I really need to do is to figure out what it’s connecting to, where the assembly of the net.Dial call comes in handy:
So all I need to do is to assign my VM the IP 10.10.1.237, start a listener at port 3301, and we should have a reverse shell.
10.10.1.237
3301
And that’s exactly what happens.