Tags: netcat ash python 

Rating: 5.0

# babyshell [242 points] (38 solves)


- task.tgz

## Part 0: research

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 /

setsid /bin/cttyhack login -f user

poweroff -d 1 -n -f

Interesting parts being `/bin/server`. `/bin/cttyhack` turned out to be regular [cttyhack](https://git.busybox.net/busybox/tree/shell/cttyhack.c). Also, entire file system is read-only `mount -o remount,ro /`

## Part 1: inspecting the binary

A quick glance in ghidra showed that binary starts SSL server and binds on ``:

ssl_ctx = create_context();
socket = create_socket();
while( true ) {
addr = 0x10;

// stack string '\ntset'
local_16 = 0x74736574;
local_12 = 10;

status = accept(socket, &sockaddr, &addr);
if (status < 0)

ssl_con = SSL_new(ssl_ctx);
status = SSL_accept(ssl_con);
if (status < 1) {
else {

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);
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...

## Part 2: The struggle

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 4433`

to the server, and using quick & dirty pwntools script open 2 sockets.

### problem #1

![The echo problem](echo.png)

the server is echo'ing back what we send. This was solved by performing `stty -echo`.

### problem #2

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 4433`

This did work, but we needed to change our encoding from base64 to `\\x41`.

### problem #3

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`.

Original writeup (https://github.com/p4-team/ctf/tree/master/2020-11-20-dragonctf/babyshell).