Intro
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!
Kihei
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
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
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
Done.
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(VerySad.java:4)
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
yv66vgAAAD0APgoAAgADBwAEDAAFAAYBABBqYXZhL2xhbmcvT2JqZWN0AQAGPGluaXQ+AQADKClWAxkAAAAIAAkBABdWZXJ5U2FkamF2YS9sYW5nL09iamVjdAoACwAMBwANDAAOAA8BABBqYXZhL2xhbmcvU3RyaW5nAQAJc3Vic3RyaW5nAQAWKElJKUxqYXZhL2xhbmcvU3RyaW5nOwoACwARDAASABMBAAdyZXBsYWNlAQAWKENDKUxqYXZhL2xhbmcvU3RyaW5nOwoACwAVDAAWABcBAAt0b1VwcGVyQ2FzZQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7CQAZABoHABsMABwAHQEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsKAB8AIAcAIQwAIgAjAQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYFAAAAAAAAA+gKACcAKAcAKQwAKgArAQAQamF2YS9sYW5nL1RocmVhZAEABXNsZWVwAQAEKEopVgcALQEAHmphdmEvbGFuZy9JbnRlcnJ1cHRlZEV4Y2VwdGlvbgoALAAvDAAwAAYBAA9wcmludFN0YWNrVHJhY2UHADIBAAdWZXJ5U2FkAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABG1haW4BABYoW0xqYXZhL2xhbmcvU3RyaW5nOylWAQANU3RhY2tNYXBUYWJsZQcAOQEAE1tMamF2YS9sYW5nL1N0cmluZzsHADsBAAJbQgEAClNvdXJjZUZpbGUBAAxWZXJ5U2FkLmphdmEAIQAxAAIAAAAAAAIAAQAFAAYAAQAzAAAAHQABAAEAAAAFKrcAAbEAAAABADQAAAAGAAEAAAABAAkANQA2AAEAMwAAALIAAwAIAAAAQBIHPBu8CE0SCE4tBhAJtgAKOgQZBBBTEHi2ABA6BRkFtgAUOgayABgZBrYAHhQAJLgAJqf/+joHGQe2AC6n//AAAQAtADMANgAsAAIANAAAADIADAAAAAMAAwAEAAcABQAKAAYAEwAHAB4ACAAlAAkALQAMADMAEAA2AA4AOAAPAD0AEAA3AAAAIAAC/wAtAAcHADgBBwA6BwALBwALBwALBwALAABIBwAsAAEAPAAAAAIAPQ==
Just running it on my desktop reveals the solution:
java VerySad
YXADJA
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();
System.out.println(var6);
while(true) {
while(true) {
try {
Thread.sleep(1000L);
} catch (InterruptedException var8) {
var8.printStackTrace();
}
}
}
}
}
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"