SadServers is a LeetCode style puzzle for Sysadmins/Site Reliability Engineers/DevOps Engineers or whatever Ops people in IT are called nowadays. The following is a writeup of walking through the challenges given.

New challenges have been added since our last look at the project, so let’s dive in!


There is a /home/admin/kihei program. Make the changes necessary so it runs succesfully, without deleting the /home/admin/datafile file.

df shows that the disk isn’t full:

/dev/nvme0n1p1   7.7G  6.0G  1.3G  83% /

We run the application with strace

cd ~admin
strace -o xxx -f ./kihei

In the output, we see that the application tries to create a file with fallocate

616   openat(AT_FDCWD, "/home/admin/data/newdatafile", O_RDWR|O_CREAT, 0666) = 3
616   fallocate(3, 0, 0, 1500000000)    = -1 ENOSPC (No space left on device)

So it tries to create a 1.5G file - exceeding the free disk space which is just 1.3G. so… what disks do we have, and what filesystems?

$ lsblk
nvme1n1      259:0    0    1G  0 disk
nvme2n1      259:1    0    1G  0 disk
nvme0n1      259:2    0    8G  0 disk
├─nvme0n1p1  259:3    0  7.9G  0 part /
├─nvme0n1p14 259:4    0    3M  0 part
└─nvme0n1p15 259:5    0  124M  0 part /boot/efi
$ mount | grep 'on / '
/dev/nvme0n1p1 on / type ext4 (rw,relatime,discard,errors=remount-ro)

Aha! Ext4 - it (per default) reserves 5% of blocks for root

$ sudo dumpe2fs /dev/nvme0n1p1 | grep -i reserved
dumpe2fs 1.46.2 (28-Feb-2021)
Reserved block count:     103218
Reserved GDT blocks:      1007
Reserved blocks uid:      0 (user root)
Reserved blocks gid:      0 (group root)
  Reserved GDT blocks at 2-1008
  Reserved GDT blocks at 32770-33776
  Reserved GDT blocks at 98306-99312
  Reserved GDT blocks at 163842-164848
  Reserved GDT blocks at 229378-230384
  Reserved GDT blocks at 294914-295920
  Reserved GDT blocks at 819202-820208
  Reserved GDT blocks at 884738-885744
  Reserved GDT blocks at 1605634-1606640

Jup - let’s reset it to 1%:

$ sudo tune2fs -m1 /dev/nvme0n1p1
tune2fs 1.46.2 (28-Feb-2021)
Setting reserved blocks percentage to 1% (20643 blocks)
$ df -h /
Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p1  7.7G  6.0G  1.6G  79% /

After inspecting datafile, we see it’s just

tune2fs -m0 /dev/nvme0n1p1

And we are

$ ./kihei

Belo Horizonte

There is a one-class Java application in your /home/admin directory. Running the program will print out a secret code, or you may be able to extract the secret from the class file without executing it but I’m not providing any special tools for that.

admin@ip-172-31-36-227:~$ ls
Sad.class  agent
admin@ip-172-31-36-227:~$ java Sad
Error: LinkageError occurred while loading main class Sad
        java.lang.UnsupportedClassVersionError: Sad has been compiled by a more recent version of the Java Runtime (class file version 61.0), this version of the Java Runtime only recognizes class file versions up to 55.0

Ok, so we have to try with another JDK. There’s another one around, try it:

admin@ip-172-31-36-227:~$ /usr/lib/jvm/
java-1.11.0-openjdk-amd64/ java-11-openjdk-amd64/     openjdk-11/
java-1.17.0-openjdk-amd64/ java-17-openjdk-amd64/     openjdk-17/
admin@ip-172-31-36-227:~$ /usr/lib/jvm/java-17-openjdk-amd64/bin/java Sad
Error: Could not find or load main class Sad
Caused by: java.lang.NoClassDefFoundError: VerySad (wrong name: Sad)

Rename the file, try again:

admin@ip-172-31-36-227:~$ mv Sad.class VerySad.class
admin@ip-172-31-36-227:~$ /usr/lib/jvm/java-17-openjdk-amd64/bin/java VerySad
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
        at VerySad.main(

The machine only has 512 MB memory and we have no network access. So we extract the class file by base64 encoding it:

admin@ip-172-31-36-227:~$ cat VerySad.class | base64 -w0

Just running it on my desktop reveals the solution:

java VerySad

Using a decompiler, we see that we could have also removed the first 2 lines from the main method:

public class VerySad {
    public VerySad() {

    public static void main(String[] var0) {
        int var1 = 419430400;
        byte[] var2 = new byte[var1];
        String var3 = "VerySadjava/lang/Object";
        String var4 = var3.substring(3, 9);
        String var5 = var4.replace('S', 'x');
        String var6 = var5.toUpperCase();

        while(true) {
            while(true) {
                try {
                } catch (InterruptedException var8) {

Also extracting the code generating var6 and putting it in JShell works:

$ jshell
|  Welcome to JShell -- Version 20.0.1
|  For an introduction type: /help intro

jshell> String var3 = "VerySadjava/lang/Object";
   ...>         String var4 = var3.substring(3, 9);
   ...>         String var5 = var4.replace('S', 'x');
   ...>         String var6 = var5.toUpperCase();
var3 ==> "VerySadjava/lang/Object"
var4 ==> "ySadja"
var5 ==> "yxadja"
var6 ==> "YXADJA"