Files:
- task.tgz
We found ourselves with a qemu image, which after quick google, can be unpacked with a simple lz4 initramfs.release.img.lz4 | cpio -i
. There we can inspect /init
#! /bin/sh
mount -t devtmpfs dev /dev
mount -t proc proc /proc
mount -t sysfs sysfs /sys
sysctl -w user.max_user_namespaces=0
ip link set up dev lo
adduser -D -s /bin/sh -u 1000 user
mount -o remount,ro /
/bin/server
setsid /bin/cttyhack login -f user
poweroff -d 1 -n -f
Interesting parts being /bin/server
. /bin/cttyhack
turned out to be regular cttyhack. Also, entire file system is read-only mount -o remount,ro /
A quick glance in ghidra showed that binary starts SSL server and binds on 127.0.0.1:4433
:
init_openssl();
ssl_ctx = create_context();
configure_context(ssl_ctx);
socket = create_socket();
while( true ) {
addr = 0x10;
// stack string '\ntset'
local_16 = 0x74736574;
local_12 = 10;
status = accept(socket, &sockaddr, &addr);
if (status < 0)
break;
ssl_con = SSL_new(ssl_ctx);
SSL_set_fd(ssl_con,status);
status = SSL_accept(ssl_con);
if (status < 1) {
ERR_print_errors_fp(stderr);
}
else {
handler(ssl_con);
}
SSL_shutdown(ssl_con);
SSL_free(ssl_con);
close(status);
}
The handler functions looks something like this:
// this value is NOT seeded!
int not_rand_val = rand();
snprintf(buf,0x7f,"%d\n", not_rand_val);
int buf_len = strlen(buf);
SSL_write(ssl,buf, buf_len);
SSL_read(ssl,buf,0x80);
buf_to_int = atoi(buf);
if (not_rand_val == buf_to_int) {
char* flag = get_flag();
int flag_len = strlen(flag);
SSL_write(ssl, flag, flag_len);
}
So, we just need to open SSL connection to the server and we get the flag? Should be easy...
The linux inside image was an arch linux with busybox and almost no tools. That means openssl was not present. We couldn't drop a binary via shell, since entire file system was read only. There wasn't nc
in path, but /bin/busybox nc
did work! It's just a missing symlink. Here we had an idea - what if we use netcat as a proxy?
note: I'm skipping here loooong hours spent on debugging
The idea was to send:
(while true; do base64 -d; done) | /bin/busybox nc 127.0.0.1 4433
to the server, and using quick & dirty pwntools script open 2 sockets.
the server is echo'ing back what we send. This was solved by performing stty -echo
.
Netcat would send our payload until stdin is closed. That's no good. We managed to bypass this with a (while true; do read s; printf "$s"; done) | /bin/busybox nc 127.0.0.1 4433
This did work, but we needed to change our encoding from base64 to \\x41
.
Solution from #2 did work, but now we needed to fix some bytes (\\x10
to \\x16
). This was solved by adding | xxd -c1
at the end.
After a bit of debugging and hammering a very horrible pwntools script we got the flag:
DrgnS{Shellcoding_and_shellscripting_whats_not_to_like}
. Turns out author had a completely different solution to the challenge. Well, unintended solution it is!
The final solving script can be found in solve.py
.