#author: fl4nk3r
from pwn import *
import string
import time

# --- CONFIGURATION ---
HOST = '192.168.48.40'
PORT = 1337
BLOCK_SIZE = 16
# ---------------------

# Reduce console noise so you only see the flag updates
context.log_level = 'info'

def get_hex_response(io, payload):
    io.sendline(payload)
    io.recvuntil(b"Ciphertext: ")
    return io.recvline().strip().decode()

def solve():
    # The flag variable is defined OUTSIDE the connection loop
    # so we don't lose progress when we reconnect.
    known_flag = b""

    print(f"[*] Starting Resilient Attack on {HOST}:{PORT}...")

    # --- OUTER LOOP: HANDLES RECONNECTION ---
    while True:
        try:
            # 1. Connect (or Reconnect)
            io = remote(HOST, PORT, level='error')
            io.recvuntil(b"> ")

            # If we are reconnecting, tell the user where we are resuming
            if len(known_flag) > 0:
                print(f"[*] Resuming with flag: {known_flag.decode()}")

            # --- INNER LOOP: THE ATTACK ---
            while True:
                # We use "Safe Padding" (Block+1) to ensure we NEVER send an empty line.
                # This prevents the specific crash you were seeing.
                # Logic: Instead of pad 0, we send pad 16.
                pad_len = (BLOCK_SIZE - 1) - (len(known_flag) % BLOCK_SIZE) + BLOCK_SIZE
                padding = b"A" * pad_len

                # Determine which block holds our target byte
                # Since we added 16 bytes of padding, we shift target by +1 block
                target_block_index = (len(known_flag) // BLOCK_SIZE) + 1

                # Step 1: Get Target Block
                full_ciphertext = get_hex_response(io, padding)

                start = target_block_index * 32
                end = start + 32
                target_block_sig = full_ciphertext[start:end]

                # Step 2: Brute-Force
                found_byte = False

                for char in string.printable:
                    guess_char = char.encode()

                    payload = padding + known_flag + guess_char
                    guess_ciphertext = get_hex_response(io, payload)

                    if guess_ciphertext[start:end] == target_block_sig:
                        known_flag += guess_char
                        print(f"[+] Found: '{char}' | Flag: {known_flag.decode()}")
                        found_byte = True
                        break

                if not found_byte:
                    print(f"[-] No match found (or end of flag).")
                    print(f"[FINAL FLAG] {known_flag.decode()}")
                    return # Exit completely

                if known_flag.endswith(b"}"):
                    print("\n[SUCCESS] Flag Captured!")
                    return # Exit completely

        except EOFError:
            print("\n[!] Server disconnected. Reconnecting in 1 second...")
            io.close()
            time.sleep(1)
            continue # Jumps back to "OUTER LOOP" to reconnect

        except KeyboardInterrupt:
            print("\n[*] Attack stopped by user.")
            break
        except Exception as e:
            print(f"\n[!] Unexpected error: {e}")
            break

if __name__ == "__main__":
    solve()
from pwn import *
import string
import time

# --- CONFIGURATION ---
#HOST = '192.168.48.40'
#PORT = 1337
#BLOCK_SIZE = 16
# ---------------------

# Reduce console noise so you only see the flag updates
context.log_level = 'info'

def get_hex_response(io, payload):
    io.sendline(payload)
    io.recvuntil(b"Ciphertext: ")
    return io.recvline().strip().decode()

def solve():
    # The flag variable is defined OUTSIDE the connection loop
    # so we don't lose progress when we reconnect.
    known_flag = b""

    print(f"[*] Starting Resilient Attack on {HOST}:{PORT}...")

    # --- OUTER LOOP: HANDLES RECONNECTION ---
    while True:
        try:
            # 1. Connect (or Reconnect)
            io = remote(HOST, PORT, level='error')
            io.recvuntil(b"> ")

            # If we are reconnecting, tell the user where we are resuming
            if len(known_flag) > 0:
                print(f"[*] Resuming with flag: {known_flag.decode()}")

            # --- INNER LOOP: THE ATTACK ---
            while True:
                # We use "Safe Padding" (Block+1) to ensure we NEVER send an empty line.
                # This prevents the specific crash you were seeing.
                # Logic: Instead of pad 0, we send pad 16.
                pad_len = (BLOCK_SIZE - 1) - (len(known_flag) % BLOCK_SIZE) + BLOCK_SIZE
                padding = b"A" * pad_len

                # Determine which block holds our target byte
                # Since we added 16 bytes of padding, we shift target by +1 block
                target_block_index = (len(known_flag) // BLOCK_SIZE) + 1

                # Step 1: Get Target Block
                full_ciphertext = get_hex_response(io, padding)

                start = target_block_index * 32
                end = start + 32
                target_block_sig = full_ciphertext[start:end]

                # Step 2: Brute-Force
                found_byte = False

                for char in string.printable:
                    guess_char = char.encode()

                    payload = padding + known_flag + guess_char
                    guess_ciphertext = get_hex_response(io, payload)

                    if guess_ciphertext[start:end] == target_block_sig:
                        known_flag += guess_char
                        print(f"[+] Found: '{char}' | Flag: {known_flag.decode()}")
                        found_byte = True
                        break

                if not found_byte:
                    print(f"[-] No match found (or end of flag).")
                    print(f"[FINAL FLAG] {known_flag.decode()}")
                    return # Exit completely

                if known_flag.endswith(b"}"):
                    print("\n[SUCCESS] Flag Captured!")
                    return # Exit completely

        except EOFError:
            print("\n[!] Server disconnected. Reconnecting in 1 second...")
            io.close()
            time.sleep(1)
            continue # Jumps back to "OUTER LOOP" to reconnect

        except KeyboardInterrupt:
            print("\n[*] Attack stopped by user.")
            break
        except Exception as e:
            print(f"\n[!] Unexpected error: {e}")
            break

if __name__ == "__main__":
    solve()
