BCACTF 6.0: nips writeup

Contents
Note

This writeup primarily uses a tool called Binary Ninja, it is a free/paid tool but since this challenge uses the MIPS architecture, decompilation requires the paid version.

Foreword

12:00 AM ET

Discord Ping announcing nips being dropped

Just when I was about to go to sleep, a rev challenge dropped. I couldn't go to sleep without trying it!

12:19 AM ET

Message showing I got the first blood
blooded in 19 minutes, for my king mph

This writeup walks through how I approached nips, one of the least solved rev challenges at BCACTF 6.0, and I managed to first blood the challenge (somehow) through only static analysis. Let’s get into it.

Challenge Overview

nips

Hard
Author:Colin (cold10)
Category:Rev
Points:225
Solves:17
Attachment:Download
Flag:
bcactf{S3lf_M0d1fy1ng_MIPS}

nips yips nips yips this was going to be more complex but i got lazy yips yips yips mips mips mips

Initial Triage

A typical first move is running file to see what we're working with.

file nips
nips: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), statically linked, BuildID[sha1]=98990fa2683a1eae86ad8639507965ac7eee11a9, for GNU/Linux 3.2.0, stripped

Interesting, we are going to be working with the MIPS architecture for this challenge and the binary is stripped.

Info

MIPS is another CPU architecture, like x86 and ARM. MIPS is specifically a RISC (Reduced Instruction Set Computing) architecture meaning it uses a smaller, simpler set of instructions to achieve a higher processing speeds and more efficient execution

The next tool I would typically open is Binary Ninja, using its decompiler and strings view, we can get some more information about this binary.

Image showing the strings view on Binja with a string about it being UPX packed
Strings View: UPX Packed !?

This ends our triage and gives us a trail to hike on. The binary is UPX-packed, so our next step is to unpack it and then analyze the binary.

Info

Binary packing is a technique used to compress or obfuscate an executable binary. UPX is a very popular tool for binary packing.

Unpacking

To unpack UPX binaries, we will use the upx command along with -d to decompress the binary.

upx -d nips
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2024
UPX 4.2.2       Markus Oberhumer, Laszlo Molnar & John Reiser    Jan 3rd 2024
 
        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
    573171 <-    262812   45.85%  linux/mipsel   nips

And if we run it (again), it will still execute perfectly fine after unpacking.

./nips
Usage: ./nips <flag>
Tip

To execute MIPS binaries, look into installing QEMU user-mode emulation.

And now that we can confirm everything runs and was unpacked smoothly, thus begins our analysis.

Analysis

The goal for our analysis here is to analyze the logic of the binary, then reverse its logic to get the flag.

Binary Ninja Decompilation & Analysis

Finding main()

Image showing the symbols view on Binja
Symbols View

The binary is stripped leaving us with no named functions to analyze, not even a main function.

We do know that executing the binary outputs binary usage. With this knowledge, we can search for the usage text in the binary to find what may potentially be the main function.

Image showing usage searched for in binary ninja
CTRL + F with the usage text

A neat thing you can do with Binary Ninja is search for references after clicking on data you want it to map to.

Image showing cross references in binary ninja
Cross References

and that leads us to this...

Image showing the identified main function
Main Function found
Tip

Rename your functions in Binary Ninja by selecting the function and pressing N. Afterwards, you can enter the name you want to rename the symbol to.

Understanding main's logic

As you can see, I have renamed a few symbols for post-solve analysis, but this will be irrelevent as I want to focus on how I solved it, which wasn't spending 20 minutes analyzing the binary.

Let's divert our attention towards this section.

Image showing a key section in the decompilation which will help us figure out what encryption this binary is using.
Key Section

The C snippet loops through 32 bytes (0x20) in 8-byte chunks and runs each chunk through sub_40082c and stores the result of it in var_54.

I put my focus on this section specifically because there is some data being passed to a function which might suggest this is an important element in the challenge. Let's look in the function.

Image showing a function in the decompilation which will is getting data passed to him.
Function with data passed to it

Looking at the function, it looks like a some sort of encryption they made until I noticed the constant 0x61c88647 for var_18 while you can assume this may be an author chosen constant, I decided to google it.

Image showing a google search about the `0x61c88647` constant to see if it is part of any encryption.
Google search for 0x61c88647

Aha! This is a constant used for the TEA encryption and the rest of the function logic seems to be for TEA encryption.

Info

TEA or Tiny Encryption Algorithm is a symmetric-key block cipher made to be simple and efficient in environments where there may not be as many resources.

This is a good lead for us and might be the direction we need to focus on for this challenge. But what is the data that's being passed through the function?

Image showing what the data is
data_493030

Nice! These are the keys the TEA encryption function uses. Let's make it more prettier with Binary Ninja.

Tip

Change data types by selecting the data and pressing Y, afterwards you can enter the type you want to change the data to.

Image showing the change type menu
Changing the type of the data

We can change it into an unsigned 32-bit integer with an array of 4 elements and here is what it looks like after.

Image showing the data after having its type changed
After data type changed

Boom! That looks so much prettier and we are able to see the keys better, thanks to Binary Ninja.

Recap from analysis

Now from what I understand:

  1. Input is provided through args (e.g. ./nips bcactf{test})

  2. arg2 and strlen of arg2 (named in Binary Ninja) is passed to a function which then stores arg2 in var_34 (see below)

Image showing a line from the main function which transforms arg2 to
Sub function which parses and pads arg2
  1. var_34 is then encrypted (essentially) with the keys then compares this to the ciphertext (see below) which is also 0x20 (32) bytes
Image showing a line from the main function which checks against the encrypted text if it matches the ciphertext
memcmp check to see if it matches
  1. If the ciphertext matches var_54 then "yips yips yips!" otherwise "nips..."

Solving the challenge

We have the ciphertext, keys, and know that it is TEA. From a simple Google search for a Python implementation and I found this.

Image showing the usage for decryption with the Tea implementation
usage for decryption

Final Solution

# decrypted: bcactf{S3lf_M0d1fy1ng_MIPS}
 
import tea # the python tea implementation gist
import base64
 
ciphertext = "45a8d67f79b2e728e8c51b244fc5d7f7549ba908909f81ec0f841fc39715f955"
 
# base64'ed because the python implementation expects it to be in base64
ct_b64ed = base64.b64encode(bytes.fromhex(ciphertext)).decode()
 
keys = (0xdeadbeef, 0xcafebabe, 0xfeedface, 0xc0defeed)
 
# joining the key into one 16 byte key like the tea implementation expects
key_bytes = b''.join(k.to_bytes(4, 'little') for k in keys)
key_str = key_bytes.decode('latin-1') # decode from latin-1
 
plaintext = tea.decrypt(ct_b64ed, key_str)
print(f"decrypted: {plaintext}")

Above is what my final solve looks looks like, though I had to change the line below in tea.py specifically the decrypt() function because UTF-8 would blow up bytes ≥ 0x80 unlike latin-1.

tea.py fixes
- k = _str2vec(key.encode()[:16])
+ k = _str2vec(key.encode("latin-1")[:16])
v = _str2vec(base64.b64decode(ciphertext.encode()))

Afterword

Thanks to the BCA CTF 6.0 admin team for hosting such a fun CTF. I had a great time solving some of the challenges and learned a few things along the way. Hope this writeup helped in some way to anyone reading it. Peace ✌️

Previous
qangr Examples
Next
Latest blog