Aller au contenu
  1. Posts/

Writeups Braeker CTF 2024 partie 1

·852 mots·4 mins
Lacroix Raphaël (Chepycou)
Auteur
Lacroix Raphaël (Chepycou)
Bonjour, bienvenue sur mon blog. Je suis Raphaël LACROIX, je développe diverses applications pendant mon temps libre, allant du très inutile au parfois utile. Je fais aussi beaucoup de Capture The Flag et de défis de cybersécurité.
2024 Braeker CTF - Cet article fait partie d'une série.
Partie 1: Cet article

Bonjour à tous 👋, dans cet article de blog, je vais passer en revue quelques défis du Braeker CTF.

Microservices
#

Manigances de réseau OOB

Ce défi était assez intéressant, chaque bit du drapeau était envoyé en utilisant un autre port que nous devions deviner dans un jeu « plus ou moins ». C’était amusant d’expérimenter un peu plus que ce à quoi je suis habitué avec scappy que je recommande à tous ceux qui s’intéressent à la cybersécurité des réseaux de maîtriser un peu !

from scapy.all import *
load_layer("http")
from Crypto.Util.number import bytes_to_long, long_to_bytes
import random


def dropTheFlag(flag):

    # Flag falls into bits
    pieces = bytes_to_long(flag.encode())
    pieces = bin(pieces)
    pieces = pieces[2:]
    pieces = [int(x) for x in pieces]

    # Bits get scattered about
    d = {}
    for i,x in enumerate(pieces):
        d[i]=x
    l = list(d.items())
    random.shuffle(l)
    pieces = dict(l)

    return pieces


# It was right here
flag = "brck{not_the_flag}"

# Oh dang I dropped the flag
pieces = dropTheFlag(flag)

# Let's pick it back up
pickMapping = {}
for i,v in enumerate(pieces):
    pickMapping[i] = (v, pieces[v])


# Neat TCP things
FIN = 0x01
SYN = 0x02
PSH = 0x08
ACK = 0x10

# Server IP for filter
serverIP = "server_ip"

# Totally valid HTTP response
body = b"HTTP/1.1 404 OK\r\nConnection: close\r\nContent-Type: text/html\r\n\r\nKeep guessing!"


def processWebRequest(payload, dport):
    
    # Secret and piece of flag
    secret, b = pickMapping[dport-1000]

    # Extract guess from request
    filterString = b'GET /guess?guess='
    guess = 0
    try:
        guess = payload[len(filterString)+2:payload.find(' ', len(filterString)+2)]
        guess = int(guess)
    except:
        guess = 0

    # Return based on guess
    body_ret = body + b' guess = ' + str(guess).encode() + b'\r\n'

    if guess > secret:
        return 1, body_ret
    elif guess == secret:
        return 2+b, body_ret
    else:
        return 0, body_ret


def packet_callback(pkt):

    # User packet
    ip = pkt[IP]
    tcp = pkt[TCP]
    payload = str(bytes(tcp.payload))

    # Init response packet
    ip_response = IP(src=ip.dst, dst=ip.src)

    # No response by default
    pkt_resp = False

    # ACK some SYNS
    if tcp.flags == SYN:

        syn_ack_tcp = TCP(sport=tcp.dport, dport=tcp.sport, flags="SA", ack=tcp.seq + 1, seq=1337)
        pkt_resp = ip_response / syn_ack_tcp
    
    # Respond to the PSH
    elif tcp.flags & PSH == PSH:

        if len(payload) > 10:
            ret, lbody = processWebRequest(payload, tcp.dport)
        
        # Reply OOB
        pkt_resp = (ip_response / 
                    TCP(sport=tcp.dport, dport=tcp.sport, flags="PAF", seq=1338, ack=tcp.seq+len(tcp.payload)) / 
                    Raw(lbody))
        send(pkt_resp)
        pkt_resp = (ip_response / 
                    TCP(sport=tcp.dport, dport=tcp.sport, flags="PAF", seq=7331+ret, ack=tcp.seq+len(tcp.payload)) / 
                    Raw(lbody))

    # ACK them FINs
    elif tcp.flags & FIN == FIN:
        pkt_resp = ip_response / TCP(sport=tcp.dport, dport=tcp.sport, flags="RA", seq=tcp.ack, ack=tcp.seq+len(tcp.payload))

    # Respond if applicable
    if pkt_resp:
        send(pkt_resp)


# Filter packets for required ports
def packet_filter(pkt):
    return (TCP in pkt and
            pkt[IP].dst == serverIP and
            pkt[TCP].dport >= 1000 and pkt[TCP].dport <= 1000+len(pickMapping)-1)


# Spin up hundreds of webservices just like in the cloud
sniff(filter="tcp", prn=packet_callback, lfilter=packet_filter)

’e’
#

Bidouilles bizarres de nombres et représentation de nombres

Dans ce défi, vous rencontrez un robot pas très sympathique, apparemment appelé epsilon, qui sait très bien compter et votre objectif est de le faire échouer à compter 3 fois de suite.

alt text

Première étape
#

Le code du premier défi est le suivant :

bool flow_start() {

	// Get user input
	float a = get_user_input("Number that is equal to two: ");

	// Can't be two
	if (a <= 2)
		return false;

	// Check if equal to 2
	return (unsigned short)a == 2;
}

Voici un exemple de Conversion de type et de ses risques. Ici, nous pouvons saisir la valeur maximale d’un unsigned short + 2 qui, en débordant, sera tronqué à 2 comme par exemple 65538.

Deuxième étape
#

Voici le code pour le deuxième défi :

bool round_2() {

	float total = 0;

	// Sum these numbers to 0.9
	for (int i = 0; i < 9; i++)
		total += 0.1;

	// Add user input
	total += get_user_input("Number to add to 0.9 to make 1: ");

	// Check if equal to one
	return total == 1.0;
}

Si nous ajoutons 0.099999880791 (bienvenue en [floating point hell] territory) (https://youtu.be/jxi0ETwDvws?t=135) et l’arrondi fait qu’il s’agit en fait de 1.

Troisième étape
#

bool level_3() {

	float total = 0;

	unsigned int *seed;
	vector<float> n_arr;

	// Random seed
	seed = (unsigned int *)getauxval(AT_RANDOM);
	srand(*seed);

	// Add user input
	add_user_input(&n_arr, "Number to add to array to equal zero: ");

	// Add many random integers
	for (int i = 0; i < 1024 * (8 + rand() % 1024); i++)
		n_arr.push_back((rand() % 1024) + 1);

	// Add user input
	add_user_input(&n_arr, "Number to add to array to equal zero: ");

	// Get sum
	for (int i = 0; i < n_arr.size(); i++)
		total += n_arr[i];

	// Check if equal to zero
	return total == 0;
}

Le plus intéressant des trois défis : Si nous entrons des valeurs à tester, nous voyons qu’il est impossible de deviner combien il faut enlever pour atteindre 0 exactement. On se rend alors compte que l’on peut tirer parti du fait qu’ajouter à MAX_FLOAT des valeurs telles que celles ajoutées ligne 17 ne le changera pas.

Par conséquent, nous pouvons plafonner la valeur à une grande valeur (j’ai choisi 340282000000000014192072600942972764160.0), laisser les petits nombres aléatoires la remplir sans impact et ensuite supprimer la grande valeur ajoutée avec un -340282000000000014192072600942972764160.0

brck{Th3_3pS1l0n_w0rkS_In_M15t3riOuS_W4yS}

2024 Braeker CTF - Cet article fait partie d'une série.
Partie 1: Cet article