100 lines
3.5 KiB
Python
100 lines
3.5 KiB
Python
#!/bin/env python3
|
|
|
|
import os
|
|
import sys
|
|
import time
|
|
import stat
|
|
import pwd
|
|
import subprocess
|
|
|
|
|
|
def change_user(username):
|
|
try:
|
|
user_info = pwd.getpwnam(username)
|
|
except KeyError:
|
|
print("user %s does not exist" % (username,))
|
|
raise
|
|
os.setregid(user_info.pw_gid, user_info.pw_gid)
|
|
os.setreuid(user_info.pw_uid, user_info.pw_uid)
|
|
|
|
|
|
def main(argv):
|
|
dolphin_path = './Dolphin.app/Contents/MacOS/Dolphin'
|
|
try:
|
|
username = os.environ['SUDO_USER']
|
|
print(username)
|
|
except KeyError:
|
|
print('$SUDO_USER not set; use sudo -E')
|
|
|
|
# 1. open tap0 and configure it
|
|
print("starting and configuring tap0")
|
|
tap0_fd = os.open('/dev/tap0', os.O_RDWR)
|
|
os.set_inheritable(tap0_fd, True)
|
|
subprocess.check_call(['ifconfig', 'tap0', argv[1]], stderr=subprocess.DEVNULL)
|
|
subprocess.check_call(['ifconfig', 'tap0', 'up'], stderr=subprocess.DEVNULL)
|
|
|
|
# 2. fork a Dolphin process, dropping privileges first
|
|
print("starting dolphin")
|
|
dolphin_proc = subprocess.Popen([dolphin_path],
|
|
preexec_fn=lambda: change_user(username), pass_fds=(tap0_fd,))
|
|
time.sleep(1)
|
|
|
|
# 3. create a temp file for dolphin to open instead of /dev/tap0
|
|
print("creating temp file")
|
|
tmpfile_fd = os.open("/tmp/dnet", os.O_CREAT | os.O_RDWR, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
|
|
os.close(tmpfile_fd)
|
|
os.chmod("/tmp/dnet", stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
|
|
|
|
# 4. modify dolphin's memory to make it upen the temp file
|
|
print("redirecting /dev/tap0 for dolphin")
|
|
addresses_str = subprocess.check_output(['memwatch', str(dolphin_proc.pid), 'find', '\"/dev/tap0\"'], stderr=subprocess.DEVNULL)
|
|
for line in addresses_str.splitlines():
|
|
tokens = line.split()
|
|
if len(tokens) != 3:
|
|
continue
|
|
print("redirecting /dev/tap0 to /tmp/dnet at %s in dolphin" % (tokens[1],))
|
|
subprocess.check_call(["memwatch", str(dolphin_proc.pid), "access", tokens[1], "rwx"], stderr=subprocess.DEVNULL)
|
|
subprocess.check_call(["memwatch", str(dolphin_proc.pid), "write", tokens[1], "\"/tmp/dnet\""], stderr=subprocess.DEVNULL)
|
|
subprocess.check_call(["memwatch", str(dolphin_proc.pid), "access", tokens[1], "r-x"], stderr=subprocess.DEVNULL)
|
|
|
|
# step 5: use lsof to find out when dolphin opens /tmp/dnet
|
|
print("waiting for temp file to open")
|
|
dolphin_tap0_fd = -1
|
|
while dolphin_tap0_fd < 0:
|
|
time.sleep(1)
|
|
result = subprocess.check_output(["lsof", "-p", str(dolphin_proc.pid)], stderr=subprocess.DEVNULL)
|
|
for line in result.splitlines():
|
|
if b'/tmp/dnet' not in line:
|
|
continue
|
|
|
|
fd_str = line.split()[3]
|
|
dolphin_tap0_fd = int(fd_str[0:-len(fd_str.lstrip(b'0123456789'))])
|
|
print("found open tap fd %d in dolphin" % (dolphin_tap0_fd,))
|
|
|
|
# step 6: use memwatch to move the tap0 fd into place
|
|
print("replacing temp fd %d with tap fd %d in dolphin" % (dolphin_tap0_fd,
|
|
tap0_fd))
|
|
assembly_contents = b"""start:
|
|
mov rax, 0x000000000200005A # dup2(from_fd, to_fd)
|
|
mov rdi, %d
|
|
mov rsi, %d
|
|
syscall
|
|
|
|
# close the original fd. if dup2 failed, this will break the connection and
|
|
# dolnet will notice. note that rdi is preserved during the syscall so we
|
|
# don't need to reload it
|
|
mov rax, 0x0000000002000006 # close(fd)
|
|
syscall
|
|
|
|
ret
|
|
""" % (tap0_fd, dolphin_tap0_fd)
|
|
subprocess.run(["memwatch", str(dolphin_proc.pid), "--", "run", "-"], input=assembly_contents, stderr=subprocess.DEVNULL)
|
|
|
|
# ok we're done - dolphin is running as a non-privileged user with tap0 open
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main(sys.argv))
|